Compare commits
15 commits
master
...
sic_sim_de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b79c35f63 | ||
|
|
598865d216 | ||
|
|
d438feb9ee | ||
|
|
5d2a0f867c | ||
|
|
ba18b92116 | ||
|
|
ad3078ba48 | ||
|
|
280a3b62fc | ||
|
|
c918993060 | ||
|
|
42737c0a66 | ||
|
|
43c8fb2cce | ||
|
|
d77a32e6e6 | ||
|
|
8a6e916876 | ||
|
|
42e884aced | ||
|
|
6b3f4989ae | ||
|
|
d4754a048d |
27 changed files with 3741 additions and 183 deletions
2
simulator_SIC_XE/.gitignore
vendored
2
simulator_SIC_XE/.gitignore
vendored
|
|
@ -7,7 +7,6 @@ CMakeCache.txt
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
CMakeScripts/
|
CMakeScripts/
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
Makefile
|
|
||||||
*.cmake
|
*.cmake
|
||||||
!CMakeLists.txt
|
!CMakeLists.txt
|
||||||
|
|
||||||
|
|
@ -71,6 +70,7 @@ xcuserdata/
|
||||||
*.moc.cpp
|
*.moc.cpp
|
||||||
*.qm
|
*.qm
|
||||||
*.prl
|
*.prl
|
||||||
|
CMakeLists.txt.user
|
||||||
|
|
||||||
# OS generated files
|
# OS generated files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Put all build outputs under target/bin as requested
|
# Put all build outputs under target/bin
|
||||||
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin)
|
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||||
|
|
@ -28,8 +28,7 @@ if(EXISTS "${PROJECT_SOURCE_DIR}/src/main.cpp")
|
||||||
target_link_libraries(simulator_exec PRIVATE simulator_lib)
|
target_link_libraries(simulator_exec PRIVATE simulator_lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Convenience target: `cmake --build build --target run`
|
|
||||||
# This target will build `simulator_exec` (if present) and then execute it.
|
|
||||||
if(TARGET simulator_exec)
|
if(TARGET simulator_exec)
|
||||||
add_custom_target(run
|
add_custom_target(run
|
||||||
DEPENDS simulator_exec
|
DEPENDS simulator_exec
|
||||||
|
|
@ -43,3 +42,19 @@ endif()
|
||||||
message(STATUS "Project: ${PROJECT_NAME}")
|
message(STATUS "Project: ${PROJECT_NAME}")
|
||||||
message(STATUS "Sources found: ${SOURCES}")
|
message(STATUS "Sources found: ${SOURCES}")
|
||||||
message(STATUS "Output directory: ${OUTPUT_DIR}")
|
message(STATUS "Output directory: ${OUTPUT_DIR}")
|
||||||
|
|
||||||
|
if(EXISTS "${CMAKE_SOURCE_DIR}/gui/qt/CMakeLists.txt")
|
||||||
|
add_subdirectory(gui/qt)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Copy resources directory (if present) to target/res so build output includes them
|
||||||
|
if(EXISTS "${CMAKE_SOURCE_DIR}/res")
|
||||||
|
add_custom_target(copy_resources
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/target/res
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res ${CMAKE_SOURCE_DIR}/target/res
|
||||||
|
COMMENT "Copying resources from res/ to target/res/"
|
||||||
|
)
|
||||||
|
if(TARGET simulator_exec)
|
||||||
|
add_dependencies(simulator_exec copy_resources)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
|
||||||
59
simulator_SIC_XE/Makefile
Normal file
59
simulator_SIC_XE/Makefile
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Simple Makefile wrapper to configure, build and run the CMake project.
|
||||||
|
# Usage:
|
||||||
|
# make # builds (default)
|
||||||
|
# make build # configure + build
|
||||||
|
# make run # build (if needed) and run the executable
|
||||||
|
# make clean # run CMake clean (or remove build files)
|
||||||
|
# make distclean # remove build dir and generated targets
|
||||||
|
|
||||||
|
CMAKE ?= cmake
|
||||||
|
BUILD_DIR := build
|
||||||
|
CMAKE_BUILD_TYPE ?= Release
|
||||||
|
TARGET := target/bin/simulator_exec
|
||||||
|
GUI_TARGET := target/bin/simulator_qt
|
||||||
|
|
||||||
|
.PHONY: all configure build run clean distclean
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
configure:
|
||||||
|
@echo "Configuring (build dir: $(BUILD_DIR), type: $(CMAKE_BUILD_TYPE))"
|
||||||
|
$(CMAKE) -S . -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
|
||||||
|
|
||||||
|
build: configure
|
||||||
|
@echo "Building..."
|
||||||
|
$(CMAKE) --build $(BUILD_DIR) -j$(shell nproc)
|
||||||
|
|
||||||
|
run: build
|
||||||
|
@echo "Running primary target..."
|
||||||
|
# Prefer GUI if avail able, otherwise fall back to console executable
|
||||||
|
@if [ -x "$(GUI_TARGET)" ]; then \
|
||||||
|
echo "Launching GUI: $(GUI_TARGET)"; \
|
||||||
|
sh -c 'nohup env QT_QPA_PLATFORM=xcb ./$(GUI_TARGET) >/dev/null 2>&1 & echo $! > "$(BUILD_DIR)/simulator_qt.pid"'; \
|
||||||
|
elif [ -x "$(TARGET)" ]; then \
|
||||||
|
@./$(TARGET); \
|
||||||
|
else \
|
||||||
|
echo "No runnable target found (tried $(GUI_TARGET) and $(TARGET))."; exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: run-gui
|
||||||
|
run-gui: build
|
||||||
|
@echo "Running GUI target ($(GUI_TARGET))"
|
||||||
|
@if [ -x "$(GUI_TARGET)" ]; then \
|
||||||
|
echo "Starting GUI..."; ./$(GUI_TARGET) -platform xcb; \
|
||||||
|
else \
|
||||||
|
echo "GUI executable not found: $(GUI_TARGET)"; exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: build-gui
|
||||||
|
build-gui: configure
|
||||||
|
@echo "Building GUI (and core)..."
|
||||||
|
$(CMAKE) --build $(BUILD_DIR) -j$(shell nproc) --target simulator_qt || true
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning build (CMake clean)..."
|
||||||
|
-$(CMAKE) --build $(BUILD_DIR) --target clean || true
|
||||||
|
|
||||||
|
distclean:
|
||||||
|
@echo "Removing build artifacts and generated files..."
|
||||||
|
-rm -rf $(BUILD_DIR) CMakeFiles CMakeCache.txt cmake_install.cmake target/bin/*
|
||||||
54
simulator_SIC_XE/gui/qt/CMakeLists.txt
Normal file
54
simulator_SIC_XE/gui/qt/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(simulator_qt LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
# Prefer Qt6, fall back to Qt5
|
||||||
|
find_package(Qt6 COMPONENTS Widgets QUIET)
|
||||||
|
if(NOT Qt6_FOUND)
|
||||||
|
# Try explicitly the system Qt6 cmake prefix on Debian/Ubuntu
|
||||||
|
find_package(Qt6 COMPONENTS Widgets QUIET PATHS /usr/lib/x86_64-linux-gnu)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT Qt6_FOUND)
|
||||||
|
# Fallback: try Qt5 if Qt6 is unavailable
|
||||||
|
find_package(Qt5 COMPONENTS Widgets QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(Qt6_FOUND)
|
||||||
|
set(QT_LIB Qt6::Widgets)
|
||||||
|
elseif(Qt5_FOUND)
|
||||||
|
set(QT_LIB Qt5::Widgets)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Qt6 or Qt5 not found. Install Qt development packages or set CMAKE_PREFIX_PATH to your Qt installation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GUI_SRCS
|
||||||
|
main.cpp
|
||||||
|
mainwindow.cpp
|
||||||
|
MachineController.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(GUI_HDRS
|
||||||
|
mainwindow.h
|
||||||
|
MachineController.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(simulator_qt ${GUI_SRCS} ${GUI_HDRS})
|
||||||
|
|
||||||
|
# Allow the generated UI headers (from AUTOUIC) to be found in the build dir
|
||||||
|
# and also include the top-level include folder (works when added with add_subdirectory)
|
||||||
|
target_include_directories(simulator_qt PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
# Link to core library target (must be defined by top-level CMake)
|
||||||
|
target_link_libraries(simulator_qt PRIVATE simulator_lib ${QT_LIB})
|
||||||
|
|
||||||
|
# Place runtime binary under repo/target/bin to match project layout
|
||||||
|
set_target_properties(simulator_qt PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/target/bin
|
||||||
|
)
|
||||||
55
simulator_SIC_XE/gui/qt/MachineController.cpp
Normal file
55
simulator_SIC_XE/gui/qt/MachineController.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include "MachineController.h"
|
||||||
|
#include "../../include/machine.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
MachineController::MachineController(std::shared_ptr<Machine> machine, QObject *parent)
|
||||||
|
: QObject(parent), m_machine(std::move(machine))
|
||||||
|
{
|
||||||
|
if (!m_machine) {
|
||||||
|
m_machine = std::make_shared<Machine>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineController::~MachineController() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineController::start() {
|
||||||
|
if (m_running.exchange(true)) return;
|
||||||
|
m_thread = std::thread([this]{ runLoop(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineController::stop() {
|
||||||
|
if (!m_running.exchange(false)) return;
|
||||||
|
if (m_thread.joinable()) m_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineController::step() {
|
||||||
|
try {
|
||||||
|
if (m_machine) {
|
||||||
|
m_machine->execute();
|
||||||
|
m_machine->tick();
|
||||||
|
emit tick();
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
emit error(QString::fromStdString(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineController::runLoop() {
|
||||||
|
while (m_running.load()) {
|
||||||
|
try {
|
||||||
|
if (m_machine) {
|
||||||
|
m_machine->execute();
|
||||||
|
m_machine->tick();
|
||||||
|
emit tick();
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
emit error(QString::fromStdString(e.what()));
|
||||||
|
// Stop on fatal error
|
||||||
|
m_running.store(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
simulator_SIC_XE/gui/qt/MachineController.h
Normal file
32
simulator_SIC_XE/gui/qt/MachineController.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef MACHINECONTROLLER_H
|
||||||
|
#define MACHINECONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Machine;
|
||||||
|
|
||||||
|
class MachineController : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit MachineController(std::shared_ptr<Machine> machine = nullptr, QObject *parent = nullptr);
|
||||||
|
~MachineController() override;
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void step();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void tick();
|
||||||
|
void error(const QString &msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void runLoop();
|
||||||
|
std::atomic<bool> m_running{false};
|
||||||
|
std::thread m_thread;
|
||||||
|
std::shared_ptr<Machine> m_machine;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MACHINECONTROLLER_H
|
||||||
11
simulator_SIC_XE/gui/qt/main.cpp
Normal file
11
simulator_SIC_XE/gui/qt/main.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <QApplication>
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "../../include/opcode.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
loadInstructionSet();
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
918
simulator_SIC_XE/gui/qt/mainwindow.cpp
Normal file
918
simulator_SIC_XE/gui/qt/mainwindow.cpp
Normal file
|
|
@ -0,0 +1,918 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include "MachineController.h"
|
||||||
|
#include "../../include/machine.h"
|
||||||
|
#include "../../include/instructions.h"
|
||||||
|
#include "../../include/opcode.h"
|
||||||
|
#include "../../include/constants.h"
|
||||||
|
#include "../../../include/loader.h"
|
||||||
|
|
||||||
|
#include <QIntValidator>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
#include <QDoubleValidator>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QFont>
|
||||||
|
|
||||||
|
class Loader;
|
||||||
|
|
||||||
|
std::shared_ptr<Loader> g_loader;
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow),
|
||||||
|
m_machine(std::make_shared<Machine>()),
|
||||||
|
m_controller(std::make_unique<MachineController>(m_machine, this))
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->regA_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
||||||
|
ui->regB_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
||||||
|
ui->regS_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
||||||
|
ui->regT_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
||||||
|
ui->regX_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
||||||
|
// unsigned 24 bit
|
||||||
|
ui->regL_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
||||||
|
ui->regPC_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
||||||
|
ui->regSW_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
||||||
|
// float
|
||||||
|
ui->regF_dec_field->setValidator(new QDoubleValidator(-3.402823e38, 3.402823e38, 6, this));
|
||||||
|
|
||||||
|
QRegularExpressionValidator* hexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,6}$"), this);
|
||||||
|
ui->regA_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regB_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regX_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regS_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regT_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regL_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regPC_hex_field->setValidator(hexValidator);
|
||||||
|
ui->regSW_hex_field->setValidator(hexValidator);
|
||||||
|
|
||||||
|
QRegularExpressionValidator* binValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,24}$"), this);
|
||||||
|
ui->regA_bin_field->setValidator(binValidator);
|
||||||
|
ui->regB_bin_field->setValidator(binValidator);
|
||||||
|
ui->regX_bin_field->setValidator(binValidator);
|
||||||
|
ui->regS_bin_field->setValidator(binValidator);
|
||||||
|
ui->regT_bin_field->setValidator(binValidator);
|
||||||
|
ui->regL_bin_field->setValidator(binValidator);
|
||||||
|
ui->regPC_bin_field->setValidator(binValidator);
|
||||||
|
ui->regSW_bin_field->setValidator(binValidator);
|
||||||
|
|
||||||
|
QRegularExpressionValidator* floatHexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,12}$"), this);
|
||||||
|
ui->regF_hex_field->setValidator(floatHexValidator);
|
||||||
|
|
||||||
|
QRegularExpressionValidator* floatBinValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,48}$"), this);
|
||||||
|
ui->regF_bin_field->setValidator(floatBinValidator);
|
||||||
|
|
||||||
|
|
||||||
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays);
|
||||||
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay);
|
||||||
|
|
||||||
|
connectRegisterFields();
|
||||||
|
|
||||||
|
connect(ui->StartBtn, &QPushButton::clicked, this, &MainWindow::startExecution);
|
||||||
|
connect(ui->StopBtn, &QPushButton::clicked, this, &MainWindow::stopExecution);
|
||||||
|
connect(ui->StepBtn, &QPushButton::clicked, this, &MainWindow::stepExecution);
|
||||||
|
|
||||||
|
connect(ui->MemoryInc256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc256);
|
||||||
|
connect(ui->MemoryInc4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc4096);
|
||||||
|
connect(ui->MemoryInc65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc65536);
|
||||||
|
connect(ui->MemoryDec256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec256);
|
||||||
|
connect(ui->MemoryDec4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec4096);
|
||||||
|
connect(ui->MemoryDec65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec65536);
|
||||||
|
connect(ui->MemoryGoToStart_2, &QPushButton::clicked, this, &MainWindow::onMemoryGoToStart);
|
||||||
|
connect(ui->MemoryGoToEnd, &QPushButton::clicked, this, &MainWindow::onMemoryGoToEnd);
|
||||||
|
|
||||||
|
connect(ui->DisasmInc256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc);
|
||||||
|
connect(ui->DisasmInc4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc16);
|
||||||
|
connect(ui->DisasmInc65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc256);
|
||||||
|
connect(ui->DisasmDec256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec);
|
||||||
|
connect(ui->DisasmDec4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec16);
|
||||||
|
connect(ui->DisasmDec65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec256);
|
||||||
|
connect(ui->DisasmGoToStart, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToStart);
|
||||||
|
connect(ui->DisasmGoToEnd, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToEnd);
|
||||||
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateDisassemblyDisplay);
|
||||||
|
|
||||||
|
setupMemoryDisplay();
|
||||||
|
setupDisassemblyDisplay();
|
||||||
|
//loadDemoProgram();
|
||||||
|
g_loader = std::make_shared<Loader>(machine(), std::string(PATH_RESOURCES) + "demo_program.obj");
|
||||||
|
g_loader->load();
|
||||||
|
|
||||||
|
updateRegisterDisplays();
|
||||||
|
updateMemoryDisplay();
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateRegisterDisplays()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
// Update all register display formats (decimal, hex, binary)
|
||||||
|
updateAllFormatsForRegister("regA", m_machine->getA());
|
||||||
|
updateAllFormatsForRegister("regB", m_machine->getB());
|
||||||
|
updateAllFormatsForRegister("regX", m_machine->getX());
|
||||||
|
updateAllFormatsForRegister("regS", m_machine->getS());
|
||||||
|
updateAllFormatsForRegister("regT", m_machine->getT());
|
||||||
|
updateAllFormatsForRegister("regL", m_machine->getL());
|
||||||
|
updateAllFormatsForRegister("regPC", m_machine->getPC());
|
||||||
|
updateAllFormatsForRegister("regSW", m_machine->getSW());
|
||||||
|
updateFloatRegisterFormats("regF", m_machine->getF());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateSingleRegisterDisplay(const QString& fieldName, int value)
|
||||||
|
{
|
||||||
|
QLineEdit* field = findChild<QLineEdit*>(fieldName);
|
||||||
|
if (field) {
|
||||||
|
// Only update if the field doesn't have focus (to avoid interfering with user input)
|
||||||
|
if (!field->hasFocus()) {
|
||||||
|
field->setText(QString::number(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateAllFormatsForRegister(const QString& regPrefix, int value)
|
||||||
|
{
|
||||||
|
// Update decimal field
|
||||||
|
QLineEdit* decField = findChild<QLineEdit*>(regPrefix + "_dec_field");
|
||||||
|
if (decField && !decField->hasFocus()) {
|
||||||
|
decField->setText(QString::number(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hex field
|
||||||
|
QLineEdit* hexField = findChild<QLineEdit*>(regPrefix + "_hex_field");
|
||||||
|
if (hexField && !hexField->hasFocus()) {
|
||||||
|
// Convert to 24-bit representation, handle negative numbers
|
||||||
|
unsigned int unsignedValue = static_cast<unsigned int>(value) & 0xFFFFFF;
|
||||||
|
hexField->setText(QString("0x%1").arg(unsignedValue, 6, 16, QChar('0')).toUpper());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update binary field
|
||||||
|
QLineEdit* binField = findChild<QLineEdit*>(regPrefix + "_bin_field");
|
||||||
|
if (binField && !binField->hasFocus()) {
|
||||||
|
// Convert to 24-bit binary representation
|
||||||
|
unsigned int unsignedValue = static_cast<unsigned int>(value) & 0xFFFFFF;
|
||||||
|
QString binaryStr = QString::number(unsignedValue, 2);
|
||||||
|
// Pad to 24 bits
|
||||||
|
binaryStr = binaryStr.rightJustified(24, '0');
|
||||||
|
binField->setText(binaryStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateFloatRegisterFormats(const QString& regPrefix, double value)
|
||||||
|
{
|
||||||
|
// Update decimal field
|
||||||
|
QLineEdit* decField = findChild<QLineEdit*>(regPrefix + "_dec_field");
|
||||||
|
if (decField && !decField->hasFocus()) {
|
||||||
|
decField->setText(QString::number(value, 'g', 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hex field (48-bit float representation)
|
||||||
|
QLineEdit* hexField = findChild<QLineEdit*>(regPrefix + "_hex_field");
|
||||||
|
if (hexField && !hexField->hasFocus()) {
|
||||||
|
// Convert double to 48-bit hex representation
|
||||||
|
// For SIC/XE, we need to convert to the 48-bit float format
|
||||||
|
uint64_t* intPtr = reinterpret_cast<uint64_t*>(&value);
|
||||||
|
uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits
|
||||||
|
hexField->setText(QString("0x%1").arg(bits48, 12, 16, QChar('0')).toUpper());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update binary field (48-bit float representation)
|
||||||
|
QLineEdit* binField = findChild<QLineEdit*>(regPrefix + "_bin_field");
|
||||||
|
if (binField && !binField->hasFocus()) {
|
||||||
|
// Convert double to 48-bit binary representation
|
||||||
|
uint64_t* intPtr = reinterpret_cast<uint64_t*>(&value);
|
||||||
|
uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits
|
||||||
|
QString binaryStr = QString::number(bits48, 2);
|
||||||
|
// Pad to 48 bits
|
||||||
|
binaryStr = binaryStr.rightJustified(48, '0');
|
||||||
|
binField->setText(binaryStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::connectRegisterFields()
|
||||||
|
{
|
||||||
|
// Connect decimal register fields to update machine registers when changed
|
||||||
|
connect(ui->regA_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regB_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regX_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regS_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regT_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regL_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regPC_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regSW_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
connect(ui->regF_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
|
||||||
|
// Connect hex register fields
|
||||||
|
QLineEdit* hexFields[] = {
|
||||||
|
ui->regA_hex_field, ui->regB_hex_field, ui->regX_hex_field,
|
||||||
|
ui->regS_hex_field, ui->regT_hex_field, ui->regL_hex_field,
|
||||||
|
ui->regPC_hex_field, ui->regSW_hex_field, ui->regF_hex_field
|
||||||
|
};
|
||||||
|
for (auto* field : hexFields) {
|
||||||
|
if (field) {
|
||||||
|
connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect binary register fields
|
||||||
|
QLineEdit* binFields[] = {
|
||||||
|
ui->regA_bin_field, ui->regB_bin_field, ui->regX_bin_field,
|
||||||
|
ui->regS_bin_field, ui->regT_bin_field, ui->regL_bin_field,
|
||||||
|
ui->regPC_bin_field, ui->regSW_bin_field, ui->regF_bin_field
|
||||||
|
};
|
||||||
|
for (auto* field : binFields) {
|
||||||
|
if (field) {
|
||||||
|
connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onRegisterFieldChanged()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
QLineEdit* field = qobject_cast<QLineEdit*>(sender());
|
||||||
|
if (!field) return;
|
||||||
|
|
||||||
|
QString objectName = field->objectName();
|
||||||
|
QString regName = objectName.split('_')[0];
|
||||||
|
|
||||||
|
|
||||||
|
if (regName == "regF") {
|
||||||
|
handleFloatRegisterFieldChanged(field, objectName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle integer registers
|
||||||
|
int value = 0;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
// Parse value based on field type
|
||||||
|
if (objectName.contains("_dec_field")) {
|
||||||
|
value = field->text().toInt(&ok);
|
||||||
|
} else if (objectName.contains("_hex_field")) {
|
||||||
|
QString hexText = field->text();
|
||||||
|
// Remove 0x prefix if present
|
||||||
|
if (hexText.startsWith("0x", Qt::CaseInsensitive)) {
|
||||||
|
hexText = hexText.mid(2);
|
||||||
|
}
|
||||||
|
value = hexText.toInt(&ok, 16);
|
||||||
|
|
||||||
|
if (ok && (regName == "regA" || regName == "regB" || regName == "regX" ||
|
||||||
|
regName == "regS" || regName == "regT")) {
|
||||||
|
if (value > 0x7FFFFF) {
|
||||||
|
value = value - 0x1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (objectName.contains("_bin_field")) {
|
||||||
|
value = field->text().toInt(&ok, 2);
|
||||||
|
|
||||||
|
if (ok && (regName == "regA" || regName == "regB" || regName == "regX" ||
|
||||||
|
regName == "regS" || regName == "regT")) {
|
||||||
|
if (value > 0x7FFFFF) {
|
||||||
|
value = value - 0x1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
|
||||||
|
updateRegisterDisplays();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regName == "regA") {
|
||||||
|
m_machine->setA(value);
|
||||||
|
updateAllFormatsForRegister("regA", m_machine->getA());
|
||||||
|
} else if (regName == "regB") {
|
||||||
|
m_machine->setB(value);
|
||||||
|
updateAllFormatsForRegister("regB", m_machine->getB());
|
||||||
|
} else if (regName == "regX") {
|
||||||
|
m_machine->setX(value);
|
||||||
|
updateAllFormatsForRegister("regX", m_machine->getX());
|
||||||
|
} else if (regName == "regS") {
|
||||||
|
m_machine->setS(value);
|
||||||
|
updateAllFormatsForRegister("regS", m_machine->getS());
|
||||||
|
} else if (regName == "regT") {
|
||||||
|
m_machine->setT(value);
|
||||||
|
updateAllFormatsForRegister("regT", m_machine->getT());
|
||||||
|
} else if (regName == "regL") {
|
||||||
|
m_machine->setL(value);
|
||||||
|
updateAllFormatsForRegister("regL", m_machine->getL());
|
||||||
|
} else if (regName == "regPC") {
|
||||||
|
m_machine->setPC(value);
|
||||||
|
updateAllFormatsForRegister("regPC", m_machine->getPC());
|
||||||
|
} else if (regName == "regSW") {
|
||||||
|
m_machine->setSW(value);
|
||||||
|
updateAllFormatsForRegister("regSW", m_machine->getSW());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName)
|
||||||
|
{
|
||||||
|
double value = 0.0;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (objectName.contains("_dec_field")) {
|
||||||
|
value = field->text().toDouble(&ok);
|
||||||
|
} else if (objectName.contains("_hex_field")) {
|
||||||
|
QString hexText = field->text();
|
||||||
|
if (hexText.startsWith("0x", Qt::CaseInsensitive)) {
|
||||||
|
hexText = hexText.mid(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t intValue = hexText.toULongLong(&ok, 16);
|
||||||
|
if (ok) {
|
||||||
|
intValue &= 0xFFFFFFFFFFFFULL;
|
||||||
|
double* floatPtr = reinterpret_cast<double*>(&intValue);
|
||||||
|
value = *floatPtr;
|
||||||
|
}
|
||||||
|
} else if (objectName.contains("_bin_field")) {
|
||||||
|
uint64_t intValue = field->text().toULongLong(&ok, 2);
|
||||||
|
if (ok) {
|
||||||
|
intValue &= 0xFFFFFFFFFFFFULL;
|
||||||
|
double* floatPtr = reinterpret_cast<double*>(&intValue);
|
||||||
|
value = *floatPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
updateRegisterDisplays();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_machine->setF(value);
|
||||||
|
updateFloatRegisterFormats("regF", m_machine->getF());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setTestRegisterValues()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
// Set some test values to demonstrate the register updating
|
||||||
|
m_machine->setA(12345); // Decimal: 12345, Hex: 0x003039, Binary: 000000011000000111001
|
||||||
|
m_machine->setB(-1000); // Negative value to test signed representation
|
||||||
|
m_machine->setX(0xABCDEF); // Hex value to test various formats
|
||||||
|
m_machine->setS(255); // Simple power of 2 minus 1
|
||||||
|
m_machine->setT(0x7FFFFF); // Maximum positive 24-bit value
|
||||||
|
|
||||||
|
// Update all displays
|
||||||
|
updateRegisterDisplays();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::startExecution()
|
||||||
|
{
|
||||||
|
if (m_controller) {
|
||||||
|
m_controller->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::stopExecution()
|
||||||
|
{
|
||||||
|
if (m_controller) {
|
||||||
|
m_controller->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::stepExecution()
|
||||||
|
{
|
||||||
|
if (m_controller) {
|
||||||
|
m_controller->step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::loadDemoProgram()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
// Load the instruction set first
|
||||||
|
loadInstructionSet();
|
||||||
|
|
||||||
|
qDebug() << "Loading SIC/XE Demo Program: Array Sum with Indirect Addressing";
|
||||||
|
|
||||||
|
// Memory layout
|
||||||
|
const int ARRAY_ADDR = 0x100; // Array of 3 numbers
|
||||||
|
const int PTR_ADDR = 0x200; // Pointer to array
|
||||||
|
const int SUM_ADDR = 0x300; // Result storage
|
||||||
|
const int COUNTER_ADDR = 0x310; // Loop counter
|
||||||
|
|
||||||
|
// Initialize array with values: 10, 20, 30
|
||||||
|
m_machine->setWord(ARRAY_ADDR, 10);
|
||||||
|
m_machine->setWord(ARRAY_ADDR + 3, 20);
|
||||||
|
m_machine->setWord(ARRAY_ADDR + 6, 30);
|
||||||
|
|
||||||
|
// Initialize pointer to point to array
|
||||||
|
m_machine->setWord(PTR_ADDR, ARRAY_ADDR);
|
||||||
|
|
||||||
|
// Initialize counter to 3
|
||||||
|
m_machine->setWord(COUNTER_ADDR, 3);
|
||||||
|
|
||||||
|
// Initialize sum to 0
|
||||||
|
m_machine->setWord(SUM_ADDR, 0);
|
||||||
|
|
||||||
|
int addr = 0x00;
|
||||||
|
|
||||||
|
// Program: Sum array elements using indirect addressing
|
||||||
|
// 0x00: LDA #0 ; Initialize accumulator to 0
|
||||||
|
m_machine->setByte(addr++, 0x01); // LDA with immediate (n=0,i=1)
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
|
||||||
|
// 0x03: STA SUM_ADDR ; Store 0 in SUM
|
||||||
|
m_machine->setByte(addr++, 0x0F); // STA
|
||||||
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x06: LDX #0 ; Initialize index to 0
|
||||||
|
m_machine->setByte(addr++, 0x05); // LDX with immediate
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
|
||||||
|
// LOOP (0x09):
|
||||||
|
const int LOOP_START = addr;
|
||||||
|
|
||||||
|
// 0x09: LDA @PTR_ADDR ; Load value indirectly through pointer
|
||||||
|
m_machine->setByte(addr++, 0x02); // LDA with indirect (n=1,i=0)
|
||||||
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x0C: ADD SUM_ADDR ; Add to sum
|
||||||
|
m_machine->setByte(addr++, 0x1B); // ADD
|
||||||
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x0F: STA SUM_ADDR ; Store result back
|
||||||
|
m_machine->setByte(addr++, 0x0F); // STA
|
||||||
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x12: LDA PTR_ADDR ; Load current pointer value
|
||||||
|
m_machine->setByte(addr++, 0x03); // LDA
|
||||||
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x15: ADD #3 ; Add 3 to move to next array element
|
||||||
|
m_machine->setByte(addr++, 0x19); // ADD with immediate
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
m_machine->setByte(addr++, 0x03);
|
||||||
|
|
||||||
|
// 0x18: STA PTR_ADDR ; Store updated pointer
|
||||||
|
m_machine->setByte(addr++, 0x0F); // STA
|
||||||
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x1B: LDA COUNTER_ADDR ; Load counter
|
||||||
|
m_machine->setByte(addr++, 0x03); // LDA
|
||||||
|
m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, COUNTER_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x1E: ADD #-1 ; Decrement counter (add -1)
|
||||||
|
m_machine->setByte(addr++, 0x19); // ADD with immediate
|
||||||
|
m_machine->setByte(addr++, 0x0F); // -1 in 12-bit two's complement
|
||||||
|
m_machine->setByte(addr++, 0xFF);
|
||||||
|
|
||||||
|
// 0x21: STA COUNTER_ADDR ; Store counter
|
||||||
|
m_machine->setByte(addr++, 0x0F); // STA
|
||||||
|
m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, COUNTER_ADDR & 0xFF);
|
||||||
|
|
||||||
|
// 0x24: COMP #0 ; Compare with 0
|
||||||
|
m_machine->setByte(addr++, 0x29); // COMP with immediate
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
|
||||||
|
// 0x27: JGT LOOP ; Jump if greater than 0
|
||||||
|
m_machine->setByte(addr++, 0x37); // JGT
|
||||||
|
m_machine->setByte(addr++, (LOOP_START >> 8) & 0xFF);
|
||||||
|
m_machine->setByte(addr++, LOOP_START & 0xFF);
|
||||||
|
|
||||||
|
// 0x2A: J 0x2A ; Infinite loop (halt)
|
||||||
|
m_machine->setByte(addr++, 0x3F); // J
|
||||||
|
m_machine->setByte(addr++, 0x00);
|
||||||
|
m_machine->setByte(addr++, 0x2A);
|
||||||
|
|
||||||
|
// Set PC to start of program
|
||||||
|
m_machine->setPC(0x00);
|
||||||
|
|
||||||
|
qDebug() << "Program loaded:";
|
||||||
|
qDebug() << " Array at 0x" << QString::number(ARRAY_ADDR, 16).toUpper() << " = [10, 20, 30]";
|
||||||
|
qDebug() << " Pointer at 0x" << QString::number(PTR_ADDR, 16).toUpper();
|
||||||
|
qDebug() << " Sum will be stored at 0x" << QString::number(SUM_ADDR, 16).toUpper();
|
||||||
|
qDebug() << " Expected result: 60 (0x3C)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupMemoryDisplay()
|
||||||
|
{
|
||||||
|
// Set the title
|
||||||
|
ui->MemorygroupBox->setTitle("Memory (RAM)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryInc256()
|
||||||
|
{
|
||||||
|
m_memoryOffset += 256;
|
||||||
|
if (m_memoryOffset > 1048576 - 256) {
|
||||||
|
m_memoryOffset = 1048576 - 256;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryInc4096()
|
||||||
|
{
|
||||||
|
m_memoryOffset += 4096;
|
||||||
|
if (m_memoryOffset > 1048576 - 256) {
|
||||||
|
m_memoryOffset = 1048576 - 256;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryInc65536()
|
||||||
|
{
|
||||||
|
m_memoryOffset += 65536;
|
||||||
|
if (m_memoryOffset > 1048576 - 256) {
|
||||||
|
m_memoryOffset = 1048576 - 256;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryDec256()
|
||||||
|
{
|
||||||
|
m_memoryOffset -= 256;
|
||||||
|
if (m_memoryOffset < 0) {
|
||||||
|
m_memoryOffset = 0;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryDec4096()
|
||||||
|
{
|
||||||
|
m_memoryOffset -= 4096;
|
||||||
|
if (m_memoryOffset < 0) {
|
||||||
|
m_memoryOffset = 0;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryDec65536()
|
||||||
|
{
|
||||||
|
m_memoryOffset -= 65536;
|
||||||
|
if (m_memoryOffset < 0) {
|
||||||
|
m_memoryOffset = 0;
|
||||||
|
}
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryGoToStart()
|
||||||
|
{
|
||||||
|
m_memoryOffset = 0;
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMemoryGoToEnd()
|
||||||
|
{
|
||||||
|
m_memoryOffset = 1048576 - 256;
|
||||||
|
updateMemoryDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupDisassemblyDisplay()
|
||||||
|
{
|
||||||
|
ui->MemorygroupBox_3->setTitle("Disassembly");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyInc()
|
||||||
|
{
|
||||||
|
// Move forward by 1 instruction
|
||||||
|
auto instr = disassembleAt(m_disassemblyOffset);
|
||||||
|
m_disassemblyOffset += instr.size;
|
||||||
|
if (m_disassemblyOffset > 1048576 - 16) {
|
||||||
|
m_disassemblyOffset = 1048576 - 16;
|
||||||
|
}
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyInc16()
|
||||||
|
{
|
||||||
|
// Move forward by 16 instructions
|
||||||
|
for (int i = 0; i < 16 && m_disassemblyOffset < 1048576 - 16; i++) {
|
||||||
|
auto instr = disassembleAt(m_disassemblyOffset);
|
||||||
|
m_disassemblyOffset += instr.size;
|
||||||
|
}
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyInc256()
|
||||||
|
{
|
||||||
|
// Move forward by 256 instructions
|
||||||
|
for (int i = 0; i < 256 && m_disassemblyOffset < 1048576 - 16; i++) {
|
||||||
|
auto instr = disassembleAt(m_disassemblyOffset);
|
||||||
|
m_disassemblyOffset += instr.size;
|
||||||
|
}
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyDec()
|
||||||
|
{
|
||||||
|
// Move back by trying to find previous instruction (assume max 4 bytes)
|
||||||
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 4);
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyDec16()
|
||||||
|
{
|
||||||
|
// Move back by approximately 16 instructions (16*3 = 48 bytes avg)
|
||||||
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 48);
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyDec256()
|
||||||
|
{
|
||||||
|
// Move back by approximately 256 instructions (256*3 = 768 bytes avg)
|
||||||
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 768);
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyGoToStart()
|
||||||
|
{
|
||||||
|
m_disassemblyOffset = 0;
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onDisassemblyGoToEnd()
|
||||||
|
{
|
||||||
|
m_disassemblyOffset = std::max(0, 1048576 - 1024);
|
||||||
|
updateDisassemblyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::DisassembledInstruction MainWindow::disassembleAt(int address)
|
||||||
|
{
|
||||||
|
DisassembledInstruction result;
|
||||||
|
result.address = address;
|
||||||
|
result.size = 1;
|
||||||
|
result.mnemonic = "???";
|
||||||
|
result.operand = "";
|
||||||
|
result.effectiveAddr = -1;
|
||||||
|
result.isImmediate = false;
|
||||||
|
result.isIndirect = false;
|
||||||
|
|
||||||
|
if (address >= 1048576) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int byte1 = m_machine->getByte(address);
|
||||||
|
int opcode = byte1 & 0xFC; // Mask off lower 2 bits (n, i flags)
|
||||||
|
|
||||||
|
if (opcode >= 0xff || instructions[opcode].type == InstructionType::INVALID) {
|
||||||
|
result.mnemonic = QString("BYTE 0x%1").arg(byte1, 2, 16, QChar('0')).toUpper();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mnemonic = QString(instructions[opcode].name);
|
||||||
|
|
||||||
|
switch (instructions[opcode].type) {
|
||||||
|
case InstructionType::TYPE1:
|
||||||
|
result.size = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionType::TYPE2: {
|
||||||
|
result.size = 2;
|
||||||
|
if (address + 1 < 1048576) {
|
||||||
|
int byte2 = m_machine->getByte(address + 1);
|
||||||
|
int r1 = (byte2 >> 4) & 0xF;
|
||||||
|
int r2 = byte2 & 0xF;
|
||||||
|
|
||||||
|
const char* regNames[] = {"A", "X", "L", "B", "S", "T", "F", "?", "PC", "SW"};
|
||||||
|
QString reg1Str = (r1 < 10) ? regNames[r1] : "?";
|
||||||
|
QString reg2Str = (r2 < 10) ? regNames[r2] : "?";
|
||||||
|
|
||||||
|
result.operand = QString("%1, %2").arg(reg1Str).arg(reg2Str);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case InstructionType::TYPE3_4: {
|
||||||
|
if (address + 2 >= 1048576) {
|
||||||
|
result.size = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int byte2 = m_machine->getByte(address + 1);
|
||||||
|
int byte3 = m_machine->getByte(address + 2);
|
||||||
|
|
||||||
|
int ni = (byte1 >> 0) & 0x3;
|
||||||
|
int x = (byte2 >> 7) & 0x1;
|
||||||
|
int b = (byte2 >> 6) & 0x1;
|
||||||
|
int p = (byte2 >> 5) & 0x1;
|
||||||
|
int e = (byte2 >> 4) & 0x1;
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
// Format 4 - add + prefix to mnemonic
|
||||||
|
result.mnemonic = "+" + result.mnemonic;
|
||||||
|
result.size = 4;
|
||||||
|
if (address + 3 < 1048576) {
|
||||||
|
int byte4 = m_machine->getByte(address + 3);
|
||||||
|
int addr = ((byte2 & 0xF) << 16) | (byte3 << 8) | byte4;
|
||||||
|
|
||||||
|
result.isImmediate = (ni == 0x1);
|
||||||
|
result.isIndirect = (ni == 0x2);
|
||||||
|
|
||||||
|
if (!result.isImmediate) {
|
||||||
|
result.effectiveAddr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString prefix = "";
|
||||||
|
if (ni == 0x1) prefix = "#"; // Immediate
|
||||||
|
else if (ni == 0x2) prefix = "@"; // Indirect
|
||||||
|
|
||||||
|
result.operand = QString("%1%2").arg(prefix).arg(addr, 5, 16, QChar('0')).toUpper();
|
||||||
|
if (x) result.operand += ",X";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.size = 3;
|
||||||
|
int disp = ((byte2 & 0xF) << 8) | byte3;
|
||||||
|
|
||||||
|
if (disp & 0x800) {
|
||||||
|
disp |= 0xFFFFF000;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.isImmediate = (ni == 0x1);
|
||||||
|
result.isIndirect = (ni == 0x2);
|
||||||
|
|
||||||
|
QString prefix = "";
|
||||||
|
if (ni == 0x1) prefix = "#"; // Immediate
|
||||||
|
else if (ni == 0x2) prefix = "@"; // Indirect
|
||||||
|
|
||||||
|
if (ni == 0x1 && !p && !b) {
|
||||||
|
result.operand = QString("#%1").arg(disp & 0xFFF);
|
||||||
|
} else {
|
||||||
|
// Calculate effective address for display
|
||||||
|
int ea = disp;
|
||||||
|
QString addrMode = "";
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
ea += m_machine->getPC();
|
||||||
|
addrMode = " (PC)";
|
||||||
|
} else if (b) {
|
||||||
|
ea += m_machine->getB();
|
||||||
|
addrMode = " (B)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.isImmediate && !x) {
|
||||||
|
result.effectiveAddr = ea & 0xFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.operand = QString("%1%2%3").arg(prefix).arg(ea & 0xFFFFF, 4, 16, QChar('0')).toUpper().arg(addrMode);
|
||||||
|
if (x) result.operand += ",X";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateDisassemblyDisplay()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
QWidget* container = new QWidget();
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(container);
|
||||||
|
layout->setSpacing(1);
|
||||||
|
layout->setContentsMargins(5, 5, 5, 5);
|
||||||
|
|
||||||
|
QFont monoFont("Courier New");
|
||||||
|
monoFont.setPointSize(9);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
QString headerText = QString("Address Mnemonic Operand *var **var");
|
||||||
|
QLabel* header = new QLabel(headerText);
|
||||||
|
QFont headerFont = monoFont;
|
||||||
|
headerFont.setBold(true);
|
||||||
|
header->setFont(headerFont);
|
||||||
|
layout->addWidget(header);
|
||||||
|
|
||||||
|
int pc = m_machine->getPC();
|
||||||
|
int currentAddr = m_disassemblyOffset;
|
||||||
|
|
||||||
|
// Disassemble up to 255 instructions
|
||||||
|
for (int i = 0; i < 255 && currentAddr < 1048576; i++) {
|
||||||
|
auto instr = disassembleAt(currentAddr);
|
||||||
|
|
||||||
|
QString varCol = "";
|
||||||
|
QString varVar = "";
|
||||||
|
|
||||||
|
// *var column - show value at effective address (if not immediate)
|
||||||
|
if (instr.effectiveAddr >= 0 && instr.effectiveAddr < 1048576) {
|
||||||
|
int value = m_machine->getWord(instr.effectiveAddr);
|
||||||
|
varCol = QString("0x%1").arg(value & 0xFFFFFF, 6, 16, QChar('0')).toUpper();
|
||||||
|
|
||||||
|
// **var column - if indirect (@), dereference again
|
||||||
|
if (instr.isIndirect && value >= 0 && value < 1048576) {
|
||||||
|
int derefValue = m_machine->getWord(value);
|
||||||
|
varVar = QString("0x%1").arg(derefValue & 0xFFFFFF, 6, 16, QChar('0')).toUpper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString line = QString("0x%1 %2 %3 %4 %5")
|
||||||
|
.arg(instr.address, 5, 16, QChar('0')).toUpper()
|
||||||
|
.arg(instr.mnemonic, -9)
|
||||||
|
.arg(instr.operand, -14)
|
||||||
|
.arg(varCol, -9)
|
||||||
|
.arg(varVar, -9);
|
||||||
|
|
||||||
|
QLabel* instrLine = new QLabel(line);
|
||||||
|
instrLine->setFont(monoFont);
|
||||||
|
|
||||||
|
// Highlight current PC
|
||||||
|
if (pc == instr.address) {
|
||||||
|
instrLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;");
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addWidget(instrLine);
|
||||||
|
currentAddr += instr.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addStretch();
|
||||||
|
container->setLayout(layout);
|
||||||
|
|
||||||
|
ui->DisasemblyScrollArea->setWidget(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateMemoryDisplay()
|
||||||
|
{
|
||||||
|
if (!m_machine) return;
|
||||||
|
|
||||||
|
// Create a widget to hold the memory display
|
||||||
|
QWidget* container = new QWidget();
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(container);
|
||||||
|
layout->setSpacing(1);
|
||||||
|
layout->setContentsMargins(5, 5, 5, 5);
|
||||||
|
|
||||||
|
// Create monospace font for memory display
|
||||||
|
QFont monoFont("Courier New");
|
||||||
|
monoFont.setPointSize(9);
|
||||||
|
|
||||||
|
// Header with current offset range
|
||||||
|
QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]")
|
||||||
|
.arg(m_memoryOffset, 5, 16, QChar('0')).toUpper()
|
||||||
|
.arg(m_memoryOffset + 255, 5, 16, QChar('0')).toUpper();
|
||||||
|
QLabel* header = new QLabel(headerText);
|
||||||
|
QFont headerFont = monoFont;
|
||||||
|
headerFont.setBold(true);
|
||||||
|
header->setFont(headerFont);
|
||||||
|
layout->addWidget(header);
|
||||||
|
|
||||||
|
// Get PC for highlighting
|
||||||
|
int pc = m_machine->getPC();
|
||||||
|
|
||||||
|
// Display memory byte by byte - ONLY 256 bytes from current offset
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
int addr = m_memoryOffset + i;
|
||||||
|
int byte = m_machine->getByte(addr);
|
||||||
|
|
||||||
|
QString addressStr = QString("0x%1").arg(addr, 5, 16, QChar('0')).toUpper();
|
||||||
|
|
||||||
|
// Hex value column
|
||||||
|
QString hexStr = QString("0x%1").arg(byte, 2, 16, QChar('0')).toUpper();
|
||||||
|
|
||||||
|
// Char representation
|
||||||
|
QString charStr;
|
||||||
|
if (byte >= 32 && byte <= 126) {
|
||||||
|
charStr = QChar(byte);
|
||||||
|
} else {
|
||||||
|
charStr = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
QString line = QString("%1 %2 %3")
|
||||||
|
.arg(addressStr, -12)
|
||||||
|
.arg(hexStr, -12)
|
||||||
|
.arg(charStr);
|
||||||
|
|
||||||
|
QLabel* memLine = new QLabel(line);
|
||||||
|
memLine->setFont(monoFont);
|
||||||
|
|
||||||
|
// Highlight the current PC address
|
||||||
|
if (pc == addr) {
|
||||||
|
memLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;");
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addWidget(memLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addStretch();
|
||||||
|
container->setLayout(layout);
|
||||||
|
|
||||||
|
ui->MemoryScrollArea->setWidget(container);
|
||||||
|
}
|
||||||
82
simulator_SIC_XE/gui/qt/mainwindow.h
Normal file
82
simulator_SIC_XE/gui/qt/mainwindow.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class MachineController;
|
||||||
|
class Machine;
|
||||||
|
class QLineEdit;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
std::shared_ptr<Machine> machine() const { return m_machine; }
|
||||||
|
MachineController* controller() const { return m_controller.get(); }
|
||||||
|
|
||||||
|
void startExecution();
|
||||||
|
void stopExecution();
|
||||||
|
void stepExecution();
|
||||||
|
|
||||||
|
void setTestRegisterValues();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateRegisterDisplays();
|
||||||
|
void updateMemoryDisplay();
|
||||||
|
void updateDisassemblyDisplay();
|
||||||
|
void onRegisterFieldChanged();
|
||||||
|
void onMemoryInc256();
|
||||||
|
void onMemoryInc4096();
|
||||||
|
void onMemoryInc65536();
|
||||||
|
void onMemoryDec256();
|
||||||
|
void onMemoryDec4096();
|
||||||
|
void onMemoryDec65536();
|
||||||
|
void onMemoryGoToStart();
|
||||||
|
void onMemoryGoToEnd();
|
||||||
|
void onDisassemblyInc();
|
||||||
|
void onDisassemblyInc16();
|
||||||
|
void onDisassemblyInc256();
|
||||||
|
void onDisassemblyDec();
|
||||||
|
void onDisassemblyDec16();
|
||||||
|
void onDisassemblyDec256();
|
||||||
|
void onDisassemblyGoToStart();
|
||||||
|
void onDisassemblyGoToEnd();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
std::shared_ptr<Machine> m_machine;
|
||||||
|
std::unique_ptr<MachineController> m_controller;
|
||||||
|
int m_memoryOffset = 0;
|
||||||
|
int m_disassemblyOffset = 0;
|
||||||
|
|
||||||
|
void connectRegisterFields();
|
||||||
|
void updateSingleRegisterDisplay(const QString& fieldName, int value);
|
||||||
|
void updateAllFormatsForRegister(const QString& regPrefix, int value);
|
||||||
|
void updateFloatRegisterFormats(const QString& regPrefix, double value);
|
||||||
|
void handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName);
|
||||||
|
void loadDemoProgram();
|
||||||
|
void setupMemoryDisplay();
|
||||||
|
void setupDisassemblyDisplay();
|
||||||
|
|
||||||
|
struct DisassembledInstruction {
|
||||||
|
int address;
|
||||||
|
int size;
|
||||||
|
QString mnemonic;
|
||||||
|
QString operand;
|
||||||
|
int effectiveAddr;
|
||||||
|
bool isImmediate;
|
||||||
|
bool isIndirect;
|
||||||
|
};
|
||||||
|
DisassembledInstruction disassembleAt(int address);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
883
simulator_SIC_XE/gui/qt/mainwindow.ui
Normal file
883
simulator_SIC_XE/gui/qt/mainwindow.ui
Normal file
|
|
@ -0,0 +1,883 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1172</width>
|
||||||
|
<height>649</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>431</width>
|
||||||
|
<height>601</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>80</y>
|
||||||
|
<width>431</width>
|
||||||
|
<height>321</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Register values</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QLineEdit" name="regA_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regA_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regA_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>110</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bin</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>230</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hex</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>350</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dec</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regB_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>70</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>70</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg B</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regB_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>70</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regB_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>70</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regX_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg X</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regX_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regX_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regS_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>130</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>130</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg S</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regS_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>130</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regS_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>130</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regT_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>160</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>160</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg T</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regT_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>160</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regT_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>160</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regL_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>190</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regL_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>190</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>190</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg L</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regL_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>190</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>220</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg PC</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regPC_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>220</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regPC_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>220</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regPC_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>220</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>249</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg SW</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regSW_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>249</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regSW_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>249</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regSW_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>249</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>280</y>
|
||||||
|
<width>57</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reg L</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regF_dec_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>280</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regF_bin_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>70</x>
|
||||||
|
<y>280</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="regF_hex_field">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>280</y>
|
||||||
|
<width>113</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>431</width>
|
||||||
|
<height>81</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Control</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QPushButton" name="StartBtn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>80</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="StopBtn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>170</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>80</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="StepBtn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>310</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>80</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Step</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>450</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>721</width>
|
||||||
|
<height>601</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QGroupBox" name="MemorygroupBox">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>711</width>
|
||||||
|
<height>291</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Memory</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QScrollArea" name="MemoryScrollArea">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>711</width>
|
||||||
|
<height>221</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>709</width>
|
||||||
|
<height>219</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryInc4096Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>450</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryInc256Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryInc65536Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>530</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>>>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryDec256Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>290</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryDec4096Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>210</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><<</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryDec65536Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>130</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><<<</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryGoToStart_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>50</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>O</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="MemoryGoToEnd">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>610</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>|</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QGroupBox" name="MemorygroupBox_3">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>300</y>
|
||||||
|
<width>711</width>
|
||||||
|
<height>301</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Disasembly</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QScrollArea" name="DisasemblyScrollArea">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>711</width>
|
||||||
|
<height>221</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents_3">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>709</width>
|
||||||
|
<height>219</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmInc4096Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>440</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmInc256Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>360</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmInc65536Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>520</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>>>></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmDec256Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>280</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmDec4096Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>200</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><<</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmDec65536Btn">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><<<</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmGoToStart">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>40</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>O</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="DisasmGoToEnd">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>600</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>|</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1172</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
52
simulator_SIC_XE/include/constants.h
Normal file
52
simulator_SIC_XE/include/constants.h
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef CONSTANTS_H
|
||||||
|
#define CONSTANTS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
// ==============================
|
||||||
|
// SIC/XE Architecture Constants
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
// Memory and system constants
|
||||||
|
constexpr int MEMORY_SIZE = 1 << 20; // 1 MB memory
|
||||||
|
constexpr int NUM_DEVICES = 256;
|
||||||
|
constexpr int WORD_SIZE = 24;
|
||||||
|
constexpr int WORD_MASK = 0xFFFFFF;
|
||||||
|
|
||||||
|
// SIC/XE floating point constants
|
||||||
|
constexpr int SICF_BITS = 48;
|
||||||
|
constexpr int SICF_FRAC_BITS = 40;
|
||||||
|
constexpr int SICF_EXP_BITS = 7;
|
||||||
|
constexpr int SICF_EXP_BIAS = 64;
|
||||||
|
constexpr unsigned long long SICF_FRAC_MASK = (1ULL << SICF_FRAC_BITS) - 1;
|
||||||
|
|
||||||
|
// SW register condition codes
|
||||||
|
constexpr int CC_LT = 0x0; // 00
|
||||||
|
constexpr int CC_EQ = 0x1; // 01
|
||||||
|
constexpr int CC_GT = 0x2; // 10
|
||||||
|
constexpr int CC_MASK = 0x3; // mask for 2 bits
|
||||||
|
|
||||||
|
// Instruction format bit masks
|
||||||
|
constexpr int TYPE3_4_SIC_MASK = 0xFC;
|
||||||
|
constexpr int NI_MASK = 0x03; // mask for n and i bits
|
||||||
|
constexpr int NI_SIC = 0x0;
|
||||||
|
|
||||||
|
constexpr int BP_BASE_REL_MASK = 0b10;
|
||||||
|
constexpr int BP_PC_REL_MASK = 0b01;
|
||||||
|
constexpr int BP_DIRECT_MASK = 0b00;
|
||||||
|
|
||||||
|
constexpr int BIT_E_MASK = 0x10; // mask for e bit in F4 and F3 instructions
|
||||||
|
|
||||||
|
//SIC/XE/XE
|
||||||
|
constexpr bool USE_EXTENDED_MODE = true;
|
||||||
|
constexpr int VECTOR_REG_SIZE = 4;
|
||||||
|
|
||||||
|
/* if structure is
|
||||||
|
/target/
|
||||||
|
|-> bin/simulator_exec
|
||||||
|
|-> res/
|
||||||
|
*/
|
||||||
|
// When running from project root (./target/bin/simulator_exec), resources are in ./target/res/
|
||||||
|
constexpr char PATH_RESOURCES[] = "./target/res/";
|
||||||
|
constexpr bool FILE_CONTAINS_WHITE_SPACES = true;
|
||||||
|
|
||||||
|
#endif // CONSTANTS_H
|
||||||
25
simulator_SIC_XE/include/file_reader.h
Normal file
25
simulator_SIC_XE/include/file_reader.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef FILE_READER_H
|
||||||
|
#define FILE_READER_H
|
||||||
|
|
||||||
|
#include "reader.h"
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
class FileReader : public Reader {
|
||||||
|
public:
|
||||||
|
explicit FileReader(const std::string &path, std::ios::openmode m = std::ios::binary);
|
||||||
|
~FileReader() override;
|
||||||
|
|
||||||
|
int readByte() override;
|
||||||
|
|
||||||
|
bool readBytes(uint8_t* buf, size_t len) override;
|
||||||
|
std::string readString(size_t len) override;
|
||||||
|
std::string readLine() override;
|
||||||
|
|
||||||
|
bool good() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ifstream in;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILE_READER_H
|
||||||
|
|
@ -1,11 +1,26 @@
|
||||||
#ifndef INSTRUCTIONS_H
|
#ifndef INSTRUCTIONS_H
|
||||||
#define INSTRUCTIONS_H
|
#define INSTRUCTIONS_H
|
||||||
|
|
||||||
#include "opcode.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
class Machine; // forward declaration
|
||||||
|
|
||||||
|
// Type 1 instruction handlers
|
||||||
|
void fix_handler(Machine& m);
|
||||||
|
void float_handler(Machine& m);
|
||||||
|
void hio_handler(Machine& m);
|
||||||
|
void norm_handler(Machine& m);
|
||||||
|
void sio_handler(Machine& m);
|
||||||
|
void tio_handler(Machine& m);
|
||||||
|
void nop_handler(Machine& m);
|
||||||
|
|
||||||
|
/* IDEJE ZA SIC_XE_XE :)*/
|
||||||
|
// void nop(Machine& m);
|
||||||
|
|
||||||
// Type 2 instruction handlers
|
// Type 2 instruction handlers
|
||||||
void addr_handler(Machine& m, int r1, int r2);
|
void addr_handler(Machine& m, int r1, int r2);
|
||||||
void clear_handler(Machine& m, int r, int unused);
|
void clear_handler(Machine& m, int r, int unused);
|
||||||
|
void compr_handler(Machine& m, int r1, int r2);
|
||||||
void divr_handler(Machine& m, int r1, int r2);
|
void divr_handler(Machine& m, int r1, int r2);
|
||||||
void mulr_handler(Machine& m, int r1, int r2);
|
void mulr_handler(Machine& m, int r1, int r2);
|
||||||
void rmo_handler(Machine& m, int r1, int r2);
|
void rmo_handler(Machine& m, int r1, int r2);
|
||||||
|
|
@ -16,5 +31,71 @@ void svc_handler(Machine& m, int n, int unused);
|
||||||
void tixr_handler(Machine& m, int r1, int unused);
|
void tixr_handler(Machine& m, int r1, int unused);
|
||||||
|
|
||||||
|
|
||||||
|
// Type 3/4 instruction handlers
|
||||||
|
void add_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void addf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void and_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void comp_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void compf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void div_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void divf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void j_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void jeq_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void jgt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void jlt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void jsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void lda_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldb_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldch_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldl_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void lds_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldx_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void lps_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void mul_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void mulf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void or_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void rd_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void rsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ssk_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void sta_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stb_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stch_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void sti_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stl_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void sts_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stsw_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stx_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void sub_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void subf_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void td_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void tix_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void wd_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
|
||||||
|
|
||||||
|
// SIC/XE/XE Extended instruction handlers
|
||||||
|
void xexe_handler(Machine& m);
|
||||||
|
void halt_handler(Machine& m);
|
||||||
|
void nop_handler(Machine& m);
|
||||||
|
|
||||||
|
void vaddr_handler(Machine& m, int r1, int r2);
|
||||||
|
void vsubr_handler(Machine& m, int r1, int r2);
|
||||||
|
void vmulr_handler(Machine& m, int r1, int r2);
|
||||||
|
void vdivr_handler(Machine& m, int r1, int r2);
|
||||||
|
|
||||||
|
void vadd_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void vsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void vmul_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void vdiv_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stva_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stvs_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void stvt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldva_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldvs_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
void ldvt_handler(Machine& m, int ea, AddressingMode mode);
|
||||||
|
|
||||||
|
|
||||||
#endif // INSTRUCTIONS_H
|
#endif // INSTRUCTIONS_H
|
||||||
68
simulator_SIC_XE/include/loader.h
Normal file
68
simulator_SIC_XE/include/loader.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef LOADER_H
|
||||||
|
#define LOADER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "file_reader.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
class Machine;
|
||||||
|
|
||||||
|
using std::shared_ptr;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
|
||||||
|
class Loader {
|
||||||
|
public:
|
||||||
|
Loader( shared_ptr<Machine> machine, string filename) : _machine(machine), _filename(filename) {
|
||||||
|
_file_reader = std::make_shared<FileReader>(filename, std::ios::in);
|
||||||
|
if (!_file_reader->good()) {
|
||||||
|
throw std::runtime_error("Loader: failed to open file: " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~Loader();
|
||||||
|
|
||||||
|
|
||||||
|
enum class RecordType {
|
||||||
|
HEADER,
|
||||||
|
TEXT,
|
||||||
|
END,
|
||||||
|
UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HeaderMetadata {
|
||||||
|
string program_name;
|
||||||
|
int start_address;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
struct TextRecord {
|
||||||
|
int start_address;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
};
|
||||||
|
struct EndRecord {
|
||||||
|
int execution_start_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
void load();
|
||||||
|
|
||||||
|
private :
|
||||||
|
|
||||||
|
static RecordType parseRecordType(char c);
|
||||||
|
|
||||||
|
|
||||||
|
shared_ptr<Machine> _machine;
|
||||||
|
string _filename;
|
||||||
|
shared_ptr<FileReader> _file_reader;
|
||||||
|
HeaderMetadata readHeader();
|
||||||
|
TextRecord readTextRecord();
|
||||||
|
EndRecord readEndRecord();
|
||||||
|
bool load_into_memory(int start_address, const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LOADER_H
|
||||||
|
|
@ -4,18 +4,18 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "input_device.h"
|
#include "input_device.h"
|
||||||
#include "output_device.h"
|
#include "output_device.h"
|
||||||
#include "file_device.h"
|
#include "file_device.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
|
#include "utils.h"
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#define MEMORY_SIZE 65536
|
|
||||||
#define NUM_DEVICES 256
|
|
||||||
|
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
|
|
@ -26,30 +26,32 @@ using std::cout;
|
||||||
class Machine {
|
class Machine {
|
||||||
public:
|
public:
|
||||||
Machine();
|
Machine();
|
||||||
|
Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; _instructionsTable = instructions; }
|
||||||
~Machine();
|
~Machine();
|
||||||
|
|
||||||
// Accessor methods for registers
|
|
||||||
int getA() const { return A; }
|
int getA() const { return A; }
|
||||||
void setA(int value) { A = value; }
|
void setA(int value) { A = toSIC24(value); }
|
||||||
|
|
||||||
int getB() const { return B; }
|
int getB() const { return B; }
|
||||||
void setB(int value) { B = value; }
|
void setB(int value) { B = toSIC24(value); }
|
||||||
|
|
||||||
int getX() const { return X; }
|
int getX() const { return X; }
|
||||||
void setX(int value) { X = value; }
|
void setX(int value) { X = toSIC24(value); }
|
||||||
|
|
||||||
int getL() const { return L; }
|
int getL() const { return L; }
|
||||||
void setL(int value) { L = value; }
|
void setL(int value) { L = toSIC24(value); }
|
||||||
|
|
||||||
int getS() const { return S; }
|
int getS() const { return S; }
|
||||||
void setS(int value) { S = value; }
|
void setS(int value) { S = toSIC24(value); }
|
||||||
|
|
||||||
int getT() const { return T; }
|
int getT() const { return T; }
|
||||||
void setT(int value) { T = value; }
|
void setT(int value) { T = toSIC24(value); }
|
||||||
|
|
||||||
|
// PC is an address → don't mask to 24 unless you want 24-bit addressing
|
||||||
int getPC() const { return PC; }
|
int getPC() const { return PC; }
|
||||||
void setPC(int value) { PC = value; }
|
void setPC(int value) { PC = value; }
|
||||||
|
|
||||||
|
// status word: keep as-is
|
||||||
int getSW() const { return SW; }
|
int getSW() const { return SW; }
|
||||||
void setSW(int value) { SW = value; }
|
void setSW(int value) { SW = value; }
|
||||||
|
|
||||||
|
|
@ -76,14 +78,17 @@ public:
|
||||||
// Set a file device at index `num` using the provided filename.
|
// Set a file device at index `num` using the provided filename.
|
||||||
void setFileDevice(int num, const std::string &filename);
|
void setFileDevice(int num, const std::string &filename);
|
||||||
|
|
||||||
|
|
||||||
// Fetch and execute instructions
|
// Fetch and execute instructions
|
||||||
int fetch();
|
int fetch();
|
||||||
void execute();
|
void execute();
|
||||||
|
|
||||||
bool execF1(int opcode);
|
// Execution and speed control
|
||||||
bool execF2(int opcode, int operand);
|
int getSpeed() const;
|
||||||
bool execSICF3F4(int opcode, int ni, int operand);
|
void setSpeed(int kHz);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void tick();
|
||||||
|
void halt();
|
||||||
|
|
||||||
// error handling methods
|
// error handling methods
|
||||||
void notImplemented(string mnemonic);
|
void notImplemented(string mnemonic);
|
||||||
|
|
@ -92,6 +97,22 @@ public:
|
||||||
void divisionByZero(int opcode);
|
void divisionByZero(int opcode);
|
||||||
void undefinedHandler(int opcode);
|
void undefinedHandler(int opcode);
|
||||||
|
|
||||||
|
bool getExtendedMode() const { return _exex_mode; }
|
||||||
|
void enableExtendedMode();
|
||||||
|
void disableExtendedMode();
|
||||||
|
|
||||||
|
|
||||||
|
int* getVectorRegister(int regNum);
|
||||||
|
void setVectorRegister(int regNum, const int* values);
|
||||||
|
|
||||||
|
const int* getVA() const { return VA; }
|
||||||
|
const int* getVS() const { return VS; }
|
||||||
|
const int* getVT() const { return VT; }
|
||||||
|
void setVA(const int* values);
|
||||||
|
void setVS(const int* values);
|
||||||
|
void setVT(const int* values);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// registers
|
// registers
|
||||||
int A, B, X, L, S, T, PC, SW;
|
int A, B, X, L, S, T, PC, SW;
|
||||||
|
|
@ -104,42 +125,23 @@ private:
|
||||||
std::vector<std::shared_ptr<Device>> devices;
|
std::vector<std::shared_ptr<Device>> devices;
|
||||||
// fallback device returned when device slot is empty/invalid
|
// fallback device returned when device slot is empty/invalid
|
||||||
Device fallbackDevice;
|
Device fallbackDevice;
|
||||||
|
|
||||||
|
// Execution control
|
||||||
|
std::atomic<bool> running{false};
|
||||||
|
std::atomic<int> speedkHz{1}; // Default 1 kHz
|
||||||
|
|
||||||
|
bool execF1(int opcode);
|
||||||
|
bool execF2(int opcode, int operand);
|
||||||
|
bool execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand);
|
||||||
|
|
||||||
|
|
||||||
|
// Extended mode
|
||||||
|
bool _stopped{false};
|
||||||
|
bool _exex_mode{false};
|
||||||
|
InstructionInfo* _instructionsTable;
|
||||||
|
int VA[VECTOR_REG_SIZE], VS[VECTOR_REG_SIZE], VT[VECTOR_REG_SIZE]; // vector operation registers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert integer to 24-bit signed SIC representation
|
|
||||||
inline int toSIC24(int value) {
|
|
||||||
value &= 0xFFFFFF;
|
|
||||||
if (value & 0x800000) {
|
|
||||||
value -= 0x1000000;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int setCC(int sw, int cc) {
|
|
||||||
sw &= ~CC_MASK;
|
|
||||||
sw |= (cc & CC_MASK);
|
|
||||||
return sw;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int sic_comp(int a, int b, int sw) {
|
|
||||||
int sa = toSIC24(a);
|
|
||||||
int sb = toSIC24(b);
|
|
||||||
|
|
||||||
int cc;
|
|
||||||
if (sa < sb) {
|
|
||||||
cc = CC_LT;
|
|
||||||
} else if (sa == sb) {
|
|
||||||
cc = CC_EQ;
|
|
||||||
} else {
|
|
||||||
cc = CC_GT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return setCC(sw, cc);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getCC(int sw) {
|
|
||||||
return sw & CC_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif // MACHINE_H
|
#endif // MACHINE_H
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef OPCODE_H
|
#ifndef OPCODE_H
|
||||||
#define OPCODE_H
|
#define OPCODE_H
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
// ==============================
|
// ==============================
|
||||||
// Opcode definitions (SIC/XE)
|
// Opcode definitions (SIC/XE)
|
||||||
// ==============================
|
// ==============================
|
||||||
|
|
@ -64,13 +66,26 @@
|
||||||
#define TIXR 0xB8
|
#define TIXR 0xB8
|
||||||
#define WD 0xDC
|
#define WD 0xDC
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// Extended opcodes (SIC/XE/XE)
|
||||||
// SW register condition codes
|
// ==============================
|
||||||
constexpr int CC_LT = 0x0; // 00
|
#define NOP 0xF1
|
||||||
constexpr int CC_EQ = 0x1; // 01
|
#define HALT 0xF2
|
||||||
constexpr int CC_GT = 0x2; // 10
|
#define XEXE 0xEE // Enable extended mode
|
||||||
constexpr int CC_MASK = 0x3; // mask for 2 bits
|
#define VADD 0x18
|
||||||
|
#define VADDR 0x90
|
||||||
|
#define VSUB 0x1C
|
||||||
|
#define VSUBR 0x94
|
||||||
|
#define VMUL 0x20
|
||||||
|
#define VMULR 0x98
|
||||||
|
#define VDIV 0x24
|
||||||
|
#define VDIVR 0x9C
|
||||||
|
#define STVA 0x0C
|
||||||
|
#define STVS 0x7C
|
||||||
|
#define STVT 0x84
|
||||||
|
#define LDVA 0x00
|
||||||
|
#define LDVS 0x68
|
||||||
|
#define LDVT 0x04
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -93,6 +108,7 @@ struct InstructionInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern InstructionInfo instructions[];
|
extern InstructionInfo instructions[];
|
||||||
|
extern InstructionInfo instructionsEXEX[];
|
||||||
|
|
||||||
// Initialize the instruction table
|
// Initialize the instruction table
|
||||||
void loadInstructionSet();
|
void loadInstructionSet();
|
||||||
|
|
|
||||||
21
simulator_SIC_XE/include/reader.h
Normal file
21
simulator_SIC_XE/include/reader.h
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef READER_H
|
||||||
|
#define READER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Abstract Reader class: read bytes/strings from a source (file, string, etc.)
|
||||||
|
class Reader {
|
||||||
|
public:
|
||||||
|
virtual ~Reader() = default;
|
||||||
|
// return 0..255 on success, -1 on EOF/error
|
||||||
|
virtual int readByte() = 0;
|
||||||
|
// read exactly len bytes into buf; return true on success
|
||||||
|
virtual bool readBytes(uint8_t* buf, size_t len) = 0;
|
||||||
|
// read up to len bytes into a std::string; may return shorter string on EOF
|
||||||
|
virtual std::string readString(size_t len) = 0;
|
||||||
|
// read a line (up to newline), return empty string on EOF
|
||||||
|
virtual std::string readLine() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // READER_H
|
||||||
22
simulator_SIC_XE/include/string_reader.h
Normal file
22
simulator_SIC_XE/include/string_reader.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef STRING_READER_H
|
||||||
|
#define STRING_READER_H
|
||||||
|
|
||||||
|
#include "reader.h"
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
class StringReader : public Reader {
|
||||||
|
public:
|
||||||
|
explicit StringReader(const std::string &s);
|
||||||
|
~StringReader() override;
|
||||||
|
|
||||||
|
int readByte() override;
|
||||||
|
bool readBytes(uint8_t* buf, size_t len) override;
|
||||||
|
std::string readString(size_t len) override;
|
||||||
|
std::string readLine() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::istringstream in;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STRING_READER_H
|
||||||
95
simulator_SIC_XE/include/utils.h
Normal file
95
simulator_SIC_XE/include/utils.h
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// SIC/XE Utility Functions
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
// Instruction bit extraction utilities
|
||||||
|
inline int getXBit(int b2) {
|
||||||
|
return (b2 & 0x80) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getBPBits(int b2) {
|
||||||
|
return (b2 >> 5) & 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AddressingMode {
|
||||||
|
IMMEDIATE,
|
||||||
|
INDIRECT,
|
||||||
|
SIMPLE,
|
||||||
|
SIC_DIRECT,
|
||||||
|
INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get addressing mode from ni bits
|
||||||
|
AddressingMode getAddressingMode(int ni);
|
||||||
|
|
||||||
|
|
||||||
|
// convert to signed 24-bit integer
|
||||||
|
inline int toSIC24(int value) {
|
||||||
|
value &= 0xFFFFFF;
|
||||||
|
if (value & 0x800000) {
|
||||||
|
value -= 0x1000000;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int setCC(int sw, int cc) {
|
||||||
|
sw &= ~CC_MASK;
|
||||||
|
sw |= (cc & CC_MASK);
|
||||||
|
return sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int sic_comp(int a, int b, int sw) {
|
||||||
|
int sa = toSIC24(a);
|
||||||
|
int sb = toSIC24(b);
|
||||||
|
|
||||||
|
int cc;
|
||||||
|
if (sa < sb) {
|
||||||
|
cc = CC_LT;
|
||||||
|
} else if (sa == sb) {
|
||||||
|
cc = CC_EQ;
|
||||||
|
} else {
|
||||||
|
cc = CC_GT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setCC(sw, cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int sic_comp(double a, double b, int sw) {
|
||||||
|
int cc;
|
||||||
|
if (a < b) {
|
||||||
|
cc = CC_LT;
|
||||||
|
} else if (a == b) {
|
||||||
|
cc = CC_EQ;
|
||||||
|
} else {
|
||||||
|
cc = CC_GT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setCC(sw, cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline int getCC(int sw) {
|
||||||
|
return sw & CC_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double normaliseFloat(double value)
|
||||||
|
{
|
||||||
|
if (value == 0.0 )return 0.0;
|
||||||
|
if (!std::isfinite(value)) return value;
|
||||||
|
double mantissa = value;
|
||||||
|
while (std::fabs(mantissa) >= 10.0) mantissa /= 10.0;
|
||||||
|
while (std::fabs(mantissa) < 1.0) mantissa *= 10.0;
|
||||||
|
return mantissa;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // UTILS_H
|
||||||
36
simulator_SIC_XE/src/file_reader.cpp
Normal file
36
simulator_SIC_XE/src/file_reader.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "file_reader.h"
|
||||||
|
|
||||||
|
FileReader::FileReader(const std::string &path, std::ios::openmode m)
|
||||||
|
: in(path, m)
|
||||||
|
{}
|
||||||
|
|
||||||
|
FileReader::~FileReader() = default;
|
||||||
|
|
||||||
|
int FileReader::readByte() {
|
||||||
|
char c;
|
||||||
|
if (!in.get(c)) return -1;
|
||||||
|
return static_cast<unsigned char>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FileReader::readBytes(uint8_t* buf, size_t len) {
|
||||||
|
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
|
||||||
|
return static_cast<size_t>(in.gcount()) == len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileReader::readString(size_t len) {
|
||||||
|
std::string s;
|
||||||
|
s.resize(len);
|
||||||
|
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
|
||||||
|
std::streamsize got = in.gcount();
|
||||||
|
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileReader::good() const { return static_cast<bool>(in); }
|
||||||
|
|
||||||
|
std::string FileReader::readLine() {
|
||||||
|
std::string s;
|
||||||
|
if (!std::getline(in, s)) return std::string();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,96 @@
|
||||||
#include "instructions.h"
|
#include "instructions.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
inline int resolveWordOperand(Machine& m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::IMMEDIATE: return ea;
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: return m.getWord(ea);
|
||||||
|
case AddressingMode::INDIRECT: return m.getWord(m.getWord(ea));
|
||||||
|
default: m.invalidAddressing(); return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double resolveFloatOperand(Machine& m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::IMMEDIATE: return static_cast<double>(ea);
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: return m.getFloat(ea);
|
||||||
|
case AddressingMode::INDIRECT: return m.getFloat(m.getWord(ea));
|
||||||
|
default: m.invalidAddressing(); return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void addr_handler(Machine& m, int r1, int r2) {
|
inline void writeWordOperand(Machine& m, int ea, AddressingMode mode, int value)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: m.setWord(ea, value); break; // direct store
|
||||||
|
case AddressingMode::INDIRECT: m.setWord(m.getWord(ea), value); break; // store via pointer
|
||||||
|
default: m.invalidAddressing(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void writeFloatOperand(Machine& m, int ea, AddressingMode mode, double value)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: m.setFloat(ea, value); break;
|
||||||
|
case AddressingMode::INDIRECT: m.setFloat(m.getWord(ea), value); break;
|
||||||
|
default: m.invalidAddressing(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// For jump-like ops: what PC should become?
|
||||||
|
inline int resolveJumpTarget(Machine& m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::IMMEDIATE:
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: return ea; // jump to EA (normal case)
|
||||||
|
case AddressingMode::INDIRECT: return m.getWord(ea); // jump via pointer
|
||||||
|
default: m.invalidAddressing(); return m.getPC();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fix_handler(Machine &m)
|
||||||
|
{
|
||||||
|
m.setA(static_cast<int>(m.getF()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void float_handler(Machine &m)
|
||||||
|
{
|
||||||
|
m.setF(static_cast<double>(m.getA()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void norm_handler(Machine &m)
|
||||||
|
{
|
||||||
|
m.setF(normaliseFloat(m.getF()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
m.setReg(r2, m.getReg(r1) + m.getReg(r2));
|
m.setReg(r2, m.getReg(r1) + m.getReg(r2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLEAR instruction: clears register r (first nibble), second nibble unused
|
|
||||||
void clear_handler(Machine& m, int r, int unused) {
|
void clear_handler(Machine& m, int r, int unused) {
|
||||||
m.setReg(r, 0);
|
m.setReg(r, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void compr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
|
m.setSW(sic_comp(m.getReg(r1), m.getReg(r2), m.getSW()));
|
||||||
|
}
|
||||||
|
|
||||||
void divr_handler(Machine& m, int r1, int r2) {
|
void divr_handler(Machine& m, int r1, int r2) {
|
||||||
|
|
||||||
|
|
@ -31,15 +111,33 @@ void rmo_handler(Machine &m, int r1, int r2)
|
||||||
m.setReg(r2, m.getReg(r1));
|
m.setReg(r2, m.getReg(r1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// SHIFTL r1, n → left *circular* shift n bits
|
||||||
void shiftl_handler(Machine &m, int r1, int n)
|
void shiftl_handler(Machine &m, int r1, int n)
|
||||||
{
|
{
|
||||||
m.setReg(r1, m.getReg(r1) << n);
|
unsigned int v = m.getReg(r1) & WORD_MASK;
|
||||||
|
n %= WORD_SIZE;
|
||||||
|
unsigned int res = ((v << n) | (v >> (WORD_SIZE - n))) & WORD_MASK;
|
||||||
|
m.setReg(r1, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SHIFTR r1, n → right shift n bits, fill with original leftmost bit
|
||||||
void shiftr_handler(Machine &m, int r1, int n)
|
void shiftr_handler(Machine &m, int r1, int n)
|
||||||
{
|
{
|
||||||
m.setReg(r1, m.getReg(r1) >> n);
|
unsigned int v = m.getReg(r1) & WORD_MASK;
|
||||||
|
n %= WORD_SIZE;
|
||||||
|
unsigned int msb = (v & 0x800000) ? 1u : 0u;
|
||||||
|
unsigned int shifted = v >> n;
|
||||||
|
unsigned int fill = 0;
|
||||||
|
if (msb) {
|
||||||
|
fill = (~0u) << (WORD_SIZE - n);
|
||||||
|
fill &= WORD_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int res = (shifted | fill) & WORD_MASK;
|
||||||
|
m.setReg(r1, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subr_handler(Machine &m, int r1, int r2)
|
void subr_handler(Machine &m, int r1, int r2)
|
||||||
{
|
{
|
||||||
m.setReg(r2, m.getReg(r2) - m.getReg(r1));
|
m.setReg(r2, m.getReg(r2) - m.getReg(r1));
|
||||||
|
|
@ -58,3 +156,433 @@ void tixr_handler(Machine &m, int r1, int unused)
|
||||||
int valR1 = m.getReg(r1);
|
int valR1 = m.getReg(r1);
|
||||||
m.setSW(sic_comp(valX, valR1, m.getSW()));
|
m.setSW(sic_comp(valX, valR1, m.getSW()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() + val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
double val = resolveFloatOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() + val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void and_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() & val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void comp_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int operand = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setSW(sic_comp(m.getA(), operand, m.getSW()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void compf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
double operand = resolveFloatOperand(m, ea, mode);
|
||||||
|
m.setSW(sic_comp(m.getF(), operand, m.getSW()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void div_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int divisor = resolveWordOperand(m, ea, mode);
|
||||||
|
if (divisor == 0) {
|
||||||
|
m.divisionByZero(DIV);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m.setA(m.getA() / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void divf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
double divisor = resolveFloatOperand(m, ea, mode);
|
||||||
|
if (divisor == 0.0) {
|
||||||
|
m.divisionByZero(DIVF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m.setF(m.getF() / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void j_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int target = resolveJumpTarget(m, ea, mode);
|
||||||
|
m.setPC(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jeq_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int sw = m.getSW();
|
||||||
|
int cc = getCC(sw);
|
||||||
|
if (cc == CC_EQ) {
|
||||||
|
int target = resolveJumpTarget(m, ea, mode);
|
||||||
|
m.setPC(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jgt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int sw = m.getSW();
|
||||||
|
int cc = getCC(sw);
|
||||||
|
if (cc == CC_GT) {
|
||||||
|
int target = resolveJumpTarget(m, ea, mode);
|
||||||
|
m.setPC(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jlt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int sw = m.getSW();
|
||||||
|
int cc = getCC(sw);
|
||||||
|
if (cc == CC_LT) {
|
||||||
|
int target = resolveJumpTarget(m, ea, mode);
|
||||||
|
m.setPC(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int target = resolveJumpTarget(m, ea, mode);
|
||||||
|
m.setL(m.getPC());
|
||||||
|
m.setPC(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lda_handler(Machine& m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setA(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldb_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setB(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldch_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA((m.getA() & 0xFFFF00) | (val & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setF(resolveFloatOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldl_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setL(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void lds_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setS(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setT(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldx_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setX(resolveWordOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mul_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() * val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mulf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setF(m.getF() * resolveFloatOperand(m, ea, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void or_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() | val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rd_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||||
|
Device& device = m.getDevice(deviceNum);
|
||||||
|
// Load byte into rightmost byte of A register
|
||||||
|
m.setA((m.getA() & 0xFFFF00) | device.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setPC(m.getL());
|
||||||
|
}
|
||||||
|
|
||||||
|
void sta_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getA());
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getB());
|
||||||
|
}
|
||||||
|
// Rightmost byte of A register is stored
|
||||||
|
void stch_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = m.getA() & 0xFF;
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AddressingMode::SIMPLE:
|
||||||
|
case AddressingMode::SIC_DIRECT: m.setByte(ea, val); break; // direct store
|
||||||
|
case AddressingMode::INDIRECT: m.setByte(m.getWord(ea), val); break; // store via pointer
|
||||||
|
default: m.invalidAddressing(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeFloatOperand(m, ea, mode, m.getF());
|
||||||
|
}
|
||||||
|
|
||||||
|
void stl_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getL());
|
||||||
|
}
|
||||||
|
|
||||||
|
void sts_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getS());
|
||||||
|
}
|
||||||
|
|
||||||
|
void stsw_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getSW());
|
||||||
|
}
|
||||||
|
|
||||||
|
void stt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getT());
|
||||||
|
}
|
||||||
|
|
||||||
|
void stx_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
writeWordOperand(m, ea, mode, m.getX());
|
||||||
|
}
|
||||||
|
|
||||||
|
void sub_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int val = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setA(m.getA() - val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subf_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
double val = resolveFloatOperand(m, ea, mode);
|
||||||
|
m.setF(m.getF() - val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void td_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||||
|
Device& device = m.getDevice(deviceNum);
|
||||||
|
// Test device and set SW accordingly
|
||||||
|
if (device.test()) {
|
||||||
|
m.setSW(setCC(m.getSW(), CC_EQ));
|
||||||
|
} else {
|
||||||
|
m.setSW(setCC(m.getSW(), CC_LT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tix_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
m.setX(m.getX() + 1);
|
||||||
|
int valX = m.getX();
|
||||||
|
int memVal = resolveWordOperand(m, ea, mode);
|
||||||
|
m.setSW(sic_comp(valX, memVal, m.getSW()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void wd_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||||
|
Device& device = m.getDevice(deviceNum);
|
||||||
|
// Write rightmost byte of A register to device
|
||||||
|
device.write(static_cast<unsigned char>(m.getA() & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
void xexe_handler(Machine &m)
|
||||||
|
{
|
||||||
|
m.enableExtendedMode();
|
||||||
|
m.execute();
|
||||||
|
m.disableExtendedMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void halt_handler(Machine &m)
|
||||||
|
{
|
||||||
|
m.halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nop_handler(Machine &m)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void vaddr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVectorRegister(r1)[i] + m.getVectorRegister(r2)[i];
|
||||||
|
}
|
||||||
|
m.setVectorRegister(r2, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vsubr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVectorRegister(r2)[i] - m.getVectorRegister(r1)[i];
|
||||||
|
}
|
||||||
|
m.setVectorRegister(r2, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmulr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVectorRegister(r1)[i] * m.getVectorRegister(r2)[i];
|
||||||
|
}
|
||||||
|
m.setVectorRegister(r2, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdivr_handler(Machine &m, int r1, int r2)
|
||||||
|
{
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
if (m.getVectorRegister(r1)[i] == 0) {
|
||||||
|
m.divisionByZero(VDIVR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result[i] = m.getVectorRegister(r2)[i] / m.getVectorRegister(r1)[i];
|
||||||
|
}
|
||||||
|
m.setVT(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vadd_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVA()[i] + vec[i];
|
||||||
|
}
|
||||||
|
m.setVA(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVA()[i] - vec[i];
|
||||||
|
}
|
||||||
|
m.setVA(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmul_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
result[i] = m.getVA()[i] * vec[i];
|
||||||
|
}
|
||||||
|
m.setVA(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdiv_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
int result[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
if (vec[i] == 0) {
|
||||||
|
m.divisionByZero(VDIV);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result[i] = m.getVA()[i] / vec[i];
|
||||||
|
}
|
||||||
|
m.setVA(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stva_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
const int* vec = m.getVA();
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
m.setWord(baseAddr + i * 3, vec[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stvs_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
const int* vec = m.getVS();
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
m.setWord(baseAddr + i * 3, vec[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stvt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
const int* vec = m.getVT();
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
m.setWord(baseAddr + i * 3, vec[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldva_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
m.setVA(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldvs_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
m.setVS(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ldvt_handler(Machine &m, int ea, AddressingMode mode)
|
||||||
|
{
|
||||||
|
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||||
|
int vec[VECTOR_REG_SIZE];
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
vec[i] = m.getWord(baseAddr + i * 3);
|
||||||
|
}
|
||||||
|
m.setVT(vec);
|
||||||
|
}
|
||||||
|
|
|
||||||
128
simulator_SIC_XE/src/loader.cpp
Normal file
128
simulator_SIC_XE/src/loader.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include "loader.h"
|
||||||
|
#include "file_reader.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
Loader::~Loader()
|
||||||
|
{
|
||||||
|
_machine.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Loader::load()
|
||||||
|
{
|
||||||
|
HeaderMetadata header = readHeader();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
|
||||||
|
switch (type) {
|
||||||
|
case RecordType::TEXT: {
|
||||||
|
TextRecord textRecord = readTextRecord();
|
||||||
|
if (!load_into_memory(textRecord.start_address, textRecord.data)) {
|
||||||
|
throw std::runtime_error("Failed to load text record into memory");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RecordType::END: {
|
||||||
|
EndRecord endRecord = readEndRecord();
|
||||||
|
_machine->setPC(endRecord.execution_start_address);
|
||||||
|
return; // Loading complete
|
||||||
|
}
|
||||||
|
case RecordType::UNKNOWN:
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unknown record type encountered");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::RecordType Loader::parseRecordType(char c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
case 'H': return RecordType::HEADER;
|
||||||
|
case 'T': return RecordType::TEXT;
|
||||||
|
case 'E': return RecordType::END;
|
||||||
|
default: return RecordType::UNKNOWN; // fallback; adjust as needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::HeaderMetadata Loader::readHeader()
|
||||||
|
{
|
||||||
|
|
||||||
|
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
|
||||||
|
if (type != RecordType::HEADER) {
|
||||||
|
throw std::runtime_error("Expected HEADER record");
|
||||||
|
}
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
|
||||||
|
HeaderMetadata header;
|
||||||
|
// Read program name (6 bytes)
|
||||||
|
header.program_name = _file_reader->readString(6);
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
|
||||||
|
// Read start address (6 hex digits)
|
||||||
|
header.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
|
||||||
|
// Read length (6 hex digits)
|
||||||
|
header.length = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||||
|
// consume newline
|
||||||
|
_file_reader->readLine();
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::TextRecord Loader::readTextRecord()
|
||||||
|
{
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
TextRecord record;
|
||||||
|
// Assume 'T' has already been read
|
||||||
|
record.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
|
||||||
|
// Read length (1 byte, 2 hex digits)
|
||||||
|
int length = std::stoi(_file_reader->readString(2), nullptr, 16);
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
|
||||||
|
record.data.resize(length);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
string byteStr = _file_reader->readLine();
|
||||||
|
// Remove spaces, newlines, and other whitespace characters
|
||||||
|
byteStr.erase(std::remove_if(byteStr.begin(), byteStr.end(), ::isspace), byteStr.end());
|
||||||
|
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
std::string byteHex = byteStr.substr(i * 2, 2);
|
||||||
|
record.data[i] = static_cast<uint8_t>(std::stoi(byteHex, nullptr, 16));
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::EndRecord Loader::readEndRecord()
|
||||||
|
{
|
||||||
|
EndRecord record;
|
||||||
|
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||||
|
// Assume 'E' has already been read
|
||||||
|
std::string addrStr = _file_reader->readString(6);
|
||||||
|
if (!addrStr.empty()) {
|
||||||
|
record.execution_start_address = std::stoi(addrStr, nullptr, 16);
|
||||||
|
} else {
|
||||||
|
record.execution_start_address = 0;
|
||||||
|
}
|
||||||
|
// consume newline
|
||||||
|
_file_reader->readLine();
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Loader::load_into_memory(int start_address, const std::vector<uint8_t> &data)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < data.size(); ++i) {
|
||||||
|
int addr = start_address + static_cast<int>(i);
|
||||||
|
if (addr < 0 || addr >= MEMORY_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_machine->setByte(addr, data[i]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "instructions.h"
|
#include "instructions.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
|
||||||
|
|
@ -19,6 +21,8 @@ Machine::Machine()
|
||||||
devices[1] = make_shared<OutputDevice>(std::cout);
|
devices[1] = make_shared<OutputDevice>(std::cout);
|
||||||
// device 2: standard error
|
// device 2: standard error
|
||||||
devices[2] = make_shared<OutputDevice>(std::cerr);
|
devices[2] = make_shared<OutputDevice>(std::cerr);
|
||||||
|
_exex_mode = false;
|
||||||
|
_instructionsTable = instructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::~Machine()
|
Machine::~Machine()
|
||||||
|
|
@ -28,6 +32,17 @@ Machine::~Machine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Machine::getSpeed() const
|
||||||
|
{
|
||||||
|
return speedkHz.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::setSpeed(int kHz)
|
||||||
|
{
|
||||||
|
speedkHz.store(kHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement errors
|
||||||
void Machine::notImplemented(string mnemonic)
|
void Machine::notImplemented(string mnemonic)
|
||||||
{
|
{
|
||||||
cout << prefix << "Not implemented: " << mnemonic << endl;
|
cout << prefix << "Not implemented: " << mnemonic << endl;
|
||||||
|
|
@ -38,6 +53,7 @@ void Machine::invalidOpcode(int opcode)
|
||||||
cout << prefix << "Invalid opcode: " << opcode << endl;
|
cout << prefix << "Invalid opcode: " << opcode << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Machine::invalidAddressing()
|
void Machine::invalidAddressing()
|
||||||
{
|
{
|
||||||
cout << prefix << "Invalid addressing mode" << endl;
|
cout << prefix << "Invalid addressing mode" << endl;
|
||||||
|
|
@ -53,6 +69,76 @@ void Machine::undefinedHandler(int opcode)
|
||||||
cout << prefix << "Undefined handler for opcode: " << opcode << endl;
|
cout << prefix << "Undefined handler for opcode: " << opcode << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Machine::enableExtendedMode()
|
||||||
|
{
|
||||||
|
if(!USE_EXTENDED_MODE) return;
|
||||||
|
_exex_mode = true;
|
||||||
|
_instructionsTable = instructionsEXEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::disableExtendedMode()
|
||||||
|
{
|
||||||
|
if(!USE_EXTENDED_MODE) return;
|
||||||
|
_exex_mode = false;
|
||||||
|
_instructionsTable = instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *Machine::getVectorRegister(int regNum)
|
||||||
|
{
|
||||||
|
switch (regNum) {
|
||||||
|
case 0: return VA;
|
||||||
|
case 4: return VS;
|
||||||
|
case 5: return VT;
|
||||||
|
default:
|
||||||
|
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::setVectorRegister(int regNum, const int *values)
|
||||||
|
{
|
||||||
|
int* targetReg = getVectorRegister(regNum);
|
||||||
|
if (targetReg == nullptr) return;
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||||
|
targetReg[i] = toSIC24(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::setVA(const int *values)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||||
|
VA[i] = toSIC24(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::setVS(const int *values)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||||
|
VS[i] = toSIC24(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::setVT(const int *values)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||||
|
VT[i] = toSIC24(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::tick()
|
||||||
|
{
|
||||||
|
const int speed = speedkHz.load();
|
||||||
|
if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick");
|
||||||
|
|
||||||
|
const auto delay = std::chrono::microseconds(1000 / speed);
|
||||||
|
std::this_thread::sleep_for(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::halt()
|
||||||
|
{
|
||||||
|
_stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
int Machine::getReg(int regNum) const
|
int Machine::getReg(int regNum) const
|
||||||
{
|
{
|
||||||
switch (regNum) {
|
switch (regNum) {
|
||||||
|
|
@ -128,23 +214,93 @@ void Machine::setWord(int address, int value)
|
||||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
value &= 0xFFFFFF;
|
||||||
|
|
||||||
memory[address] = static_cast<unsigned char>(value & 0xFF);
|
memory[address] = static_cast<unsigned char>(value & 0xFF);
|
||||||
memory[address + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
|
memory[address + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
|
||||||
memory[address + 2] = static_cast<unsigned char>((value >> 16) & 0xFF);
|
memory[address + 2] = static_cast<unsigned char>((value >> 16) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement proper float storage and retrieval
|
|
||||||
double Machine::getFloat(int address)
|
double Machine::getFloat(int address)
|
||||||
{
|
{
|
||||||
return 0.0;
|
if (address < 0 || address + 5 >= MEMORY_SIZE) {
|
||||||
|
cerr << prefix << "Invalid float address: " << address << endl;
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load 6 bytes, little-endian → 48-bit word
|
||||||
|
unsigned long long raw =
|
||||||
|
(unsigned long long)memory[address] |
|
||||||
|
((unsigned long long)memory[address+1] << 8) |
|
||||||
|
((unsigned long long)memory[address+2] << 16) |
|
||||||
|
((unsigned long long)memory[address+3] << 24) |
|
||||||
|
((unsigned long long)memory[address+4] << 32) |
|
||||||
|
((unsigned long long)memory[address+5] << 40);
|
||||||
|
|
||||||
|
int sign = (raw >> 47) & 0x1;
|
||||||
|
int exponent = (raw >> 40) & 0x7F;
|
||||||
|
unsigned long long frac = raw & SICF_FRAC_MASK; // 40 bits
|
||||||
|
|
||||||
|
if (raw == 0) return 0.0;
|
||||||
|
|
||||||
|
// value = (1 + frac/2^40) * 2^(exp - 64)
|
||||||
|
double mant = 1.0 + (double)frac / (double)(1ULL << SICF_FRAC_BITS);
|
||||||
|
int e = exponent - SICF_EXP_BIAS;
|
||||||
|
double val = std::ldexp(mant, e); // ldexp is fast enough here
|
||||||
|
return sign ? -val : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::setFloat(int address, double value)
|
void Machine::setFloat(int address, double value)
|
||||||
{
|
{
|
||||||
// TODO: implement proper float storage
|
if (address < 0 || address + 5 >= MEMORY_SIZE) {
|
||||||
|
cerr << prefix << "Invalid float address: " << address << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == 0.0) {
|
||||||
|
memory[address] = 0;
|
||||||
|
memory[address+1] = 0;
|
||||||
|
memory[address+2] = 0;
|
||||||
|
memory[address+3] = 0;
|
||||||
|
memory[address+4] = 0;
|
||||||
|
memory[address+5] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sign = value < 0;
|
||||||
|
double x = sign ? -value : value;
|
||||||
|
|
||||||
|
// normalize x to [1, 2)
|
||||||
|
int exp2 = 0;
|
||||||
|
x = std::frexp(x, &exp2);
|
||||||
|
x *= 2.0;
|
||||||
|
exp2 -= 1;
|
||||||
|
|
||||||
|
int exp_field = exp2 + SICF_EXP_BIAS;
|
||||||
|
if (exp_field < 0) exp_field = 0;
|
||||||
|
if (exp_field > 127) exp_field = 127;
|
||||||
|
|
||||||
|
// mantissa = (x - 1) * 2^40
|
||||||
|
double frac_d = (x - 1.0) * (double)(1ULL << SICF_FRAC_BITS);
|
||||||
|
unsigned long long frac = (unsigned long long)(frac_d + 0.5); // round
|
||||||
|
frac &= SICF_FRAC_MASK;
|
||||||
|
|
||||||
|
unsigned long long raw =
|
||||||
|
((unsigned long long)sign << 47) |
|
||||||
|
((unsigned long long)exp_field << 40) |
|
||||||
|
frac;
|
||||||
|
|
||||||
|
// store 6 bytes little-endian
|
||||||
|
memory[address] = (unsigned char)( raw & 0xFF);
|
||||||
|
memory[address+1] = (unsigned char)((raw >> 8) & 0xFF);
|
||||||
|
memory[address+2] = (unsigned char)((raw >> 16) & 0xFF);
|
||||||
|
memory[address+3] = (unsigned char)((raw >> 24) & 0xFF);
|
||||||
|
memory[address+4] = (unsigned char)((raw >> 32) & 0xFF);
|
||||||
|
memory[address+5] = (unsigned char)((raw >> 40) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Device &Machine::getDevice(int num)
|
Device &Machine::getDevice(int num)
|
||||||
{
|
{
|
||||||
if(num < 0 || num >= static_cast<int>(devices.size()) || !devices[num]) {
|
if(num < 0 || num >= static_cast<int>(devices.size()) || !devices[num]) {
|
||||||
|
|
@ -195,49 +351,55 @@ int Machine::fetch()
|
||||||
return getByte(PC++);
|
return getByte(PC++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::execute()
|
void Machine::execute() {
|
||||||
{
|
if (_stopped) return;
|
||||||
int opcode = fetch();
|
int b1 = fetch();
|
||||||
InstructionType type = instructions[opcode].type;
|
|
||||||
switch (type) {
|
InstructionInfo &info = _instructionsTable[b1];
|
||||||
case InstructionType::TYPE1: execF1(opcode);break;
|
|
||||||
case InstructionType::TYPE2: execF2(opcode, fetch());break;
|
if (info.type == InstructionType::TYPE1) { execF1(b1); return; }
|
||||||
case InstructionType::TYPE3_4: // extract n and i bits
|
if (info.type == InstructionType::TYPE2) { execF2(b1, fetch()); return; }
|
||||||
{
|
|
||||||
int ni = opcode & 0x3;
|
int opcode = b1 & TYPE3_4_SIC_MASK;
|
||||||
int operand = fetch();
|
InstructionInfo &info34 = _instructionsTable[opcode];
|
||||||
execSICF3F4(opcode, ni, operand);
|
int ni = b1 & NI_MASK;
|
||||||
}
|
|
||||||
break;
|
if (info34.type == InstructionType::TYPE3_4) {
|
||||||
default: invalidOpcode(opcode); break;
|
int b2 = fetch(), b3 = fetch();
|
||||||
|
int x = (b2 & 0x80) ? 1 : 0;
|
||||||
|
int b = (b2 & 0x40) ? 1 : 0;
|
||||||
|
int p = (b2 & 0x20) ? 1 : 0;
|
||||||
|
int e = (b2 & 0x10) ? 1 : 0;
|
||||||
|
|
||||||
|
int operand;
|
||||||
|
if (ni == NI_SIC) {
|
||||||
|
// PURE SIC
|
||||||
|
operand = ((b2 & 0x7F) << 8) | b3;
|
||||||
|
} else {
|
||||||
|
// SIC/XE
|
||||||
|
operand = e
|
||||||
|
? (((b2 & 0x0F) << 16) | (b3 << 8) | fetch()) // F4: 20-bit
|
||||||
|
: (((b2 & 0x0F) << 8) | b3); // F3: 12-bit
|
||||||
|
}
|
||||||
|
|
||||||
|
execSICF3F4(opcode, ni, x, b, p, e, operand);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidOpcode(b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool Machine::execF1(int opcode)
|
bool Machine::execF1(int opcode)
|
||||||
{
|
{
|
||||||
switch (opcode)
|
if (_instructionsTable[opcode].handler) {
|
||||||
{
|
auto handler = reinterpret_cast<void(*)(Machine&)>(_instructionsTable[opcode].handler);
|
||||||
case FIX:
|
handler(*this);
|
||||||
setA(static_cast<int>(getF()));
|
|
||||||
return true;
|
return true;
|
||||||
case FLOAT:
|
|
||||||
setF(static_cast<double>(getA()));
|
|
||||||
return true;
|
|
||||||
case HIO:
|
|
||||||
notImplemented("HIO");
|
|
||||||
return true;
|
|
||||||
case NORM:
|
|
||||||
notImplemented("NORM");
|
|
||||||
return true;
|
|
||||||
case SIO:
|
|
||||||
notImplemented("SIO");
|
|
||||||
return true;
|
|
||||||
case TIO:
|
|
||||||
notImplemented("TIO");
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
undefinedHandler(opcode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,8 +408,8 @@ bool Machine::execF2(int opcode, int operand)
|
||||||
int r1 = (operand >> 4) & 0xF;
|
int r1 = (operand >> 4) & 0xF;
|
||||||
int r2 = operand & 0xF;
|
int r2 = operand & 0xF;
|
||||||
|
|
||||||
if (instructions[opcode].handler) {
|
if (_instructionsTable[opcode].handler) {
|
||||||
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(instructions[opcode].handler);
|
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(_instructionsTable[opcode].handler);
|
||||||
handler(*this, r1, r2);
|
handler(*this, r1, r2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +417,63 @@ bool Machine::execF2(int opcode, int operand)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Machine::execSICF3F4(int opcode, int ni, int operand)
|
|
||||||
|
bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand)
|
||||||
{
|
{
|
||||||
|
int ea_part = operand;
|
||||||
|
int base = 0;
|
||||||
|
AddressingMode mode = getAddressingMode(ni);
|
||||||
|
|
||||||
|
// --- PURE SIC ---
|
||||||
|
if (mode == AddressingMode::SIC_DIRECT) {
|
||||||
|
int ea = ea_part + (x ? getX() : 0);
|
||||||
|
if (_instructionsTable[opcode].handler) {
|
||||||
|
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
|
||||||
|
h(*this, ea, mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
undefinedHandler(opcode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- SIC/XE EA calc ---
|
||||||
|
|
||||||
|
if (!e) { // format 3
|
||||||
|
if (b && !p) {
|
||||||
|
base = getB(); // base-relative, unsigned 12-bit
|
||||||
|
} else if (p && !b) {
|
||||||
|
// PC-relative, signed 12-bit
|
||||||
|
if (ea_part & 0x800) // bit 11 set?
|
||||||
|
ea_part |= 0xFFFFF000; // sign-extend
|
||||||
|
base = getPC();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// format 4 (e=1): b/p ignored, ea_part is 20-bit absolute
|
||||||
|
int ea = base + ea_part + (x ? getX() : 0);
|
||||||
|
|
||||||
|
if (_instructionsTable[opcode].handler) {
|
||||||
|
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
|
||||||
|
h(*this, ea, mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
undefinedHandler(opcode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Machine::start()
|
||||||
|
{
|
||||||
|
running.store(true);
|
||||||
|
|
||||||
|
// Main execution loop
|
||||||
|
// TODO: consider running in separate thread
|
||||||
|
while (running.load()) {
|
||||||
|
execute();
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::stop()
|
||||||
|
{
|
||||||
|
running.store(false);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,110 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "file_device.h"
|
#include "file_device.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "instructions.h"
|
#include "instructions.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
|
||||||
|
struct VectorAddProgram {
|
||||||
|
int x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
loadInstructionSet();
|
loadInstructionSet();
|
||||||
Machine machine;
|
Machine machine;
|
||||||
|
cout << "SIC/XE Program: Accumulator Loop" << endl;
|
||||||
|
|
||||||
|
const int TEMP_ADDR = 0x50;
|
||||||
|
const int LOOP_ADDR = 0x03;
|
||||||
|
|
||||||
|
// clear TEMP
|
||||||
|
machine.setByte(TEMP_ADDR, 0);
|
||||||
|
loadInstructionSet();
|
||||||
|
|
||||||
|
cout << "SIC/XE Program: Vector add test" << endl;
|
||||||
|
|
||||||
|
const int VA_ADDR = 0x100; // source vector A
|
||||||
|
const int VB_ADDR = 0x200; // source vector B
|
||||||
|
const int VR_ADDR = 0x300; // result store (STVA)
|
||||||
|
|
||||||
|
// Prepare two 4-element vectors (WORD = 3 bytes) for VA and VB
|
||||||
|
int a_vals[VECTOR_REG_SIZE] = {1,2,3,4};
|
||||||
|
int b_vals[VECTOR_REG_SIZE] = {5,6,7,8};
|
||||||
|
|
||||||
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
machine.setWord(VA_ADDR + i * 3, a_vals[i]);
|
||||||
|
machine.setWord(VB_ADDR + i * 3, b_vals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble program at address 0x000 (we use XEXE before each extended op)
|
||||||
|
// Offsets and bytes (hex):
|
||||||
|
// 0x00: XEXE -> 0xEE
|
||||||
|
// 0x01: LDVA (format 4) -> b1=0x03 (LDVA|ni=0x00|0x03), b2=0x10 (e=1), b3=0x01, b4=0x00 (addr 0x100)
|
||||||
|
// 0x05: XEXE -> 0xEE
|
||||||
|
// 0x06: LDVS (format 4) -> b1=0x6B (0x68|0x03), b2=0x10, b3=0x02, b4=0x00 (addr 0x200)
|
||||||
|
// 0x0A: XEXE -> 0xEE
|
||||||
|
// 0x0B: VADDR B->A (type 2) -> opcode 0x90, operand r1=4 (VS), r2=0 (VA) => operand=(4<<4)|0=0x40
|
||||||
|
// 0x0D: XEXE -> 0xEE
|
||||||
|
// 0x0E: STVA (format4) -> b1=0x0F (0x0C|0x03), b2=0x10, b3=0x03, b4=0x00 (addr 0x300)
|
||||||
|
// 0x12: J (format4) to self -> b1=0x3F (0x3C|0x03), b2=0x10, b3=0x00, b4=0x12
|
||||||
|
|
||||||
|
unsigned char prog[] = {
|
||||||
|
0xEE,
|
||||||
|
0x01, 0x10, 0x01, 0x00, // LDVA (format 4) with ni=IMMEDIATE -> b1=0x01
|
||||||
|
0xEE,
|
||||||
|
0x69, 0x10, 0x02, 0x00, // LDVS (format 4) with ni=IMMEDIATE -> b1=0x69 (0x68|0x01)
|
||||||
|
0xEE,
|
||||||
|
0x90, 0x40, // VADDR VS->VA (type2)
|
||||||
|
0xEE,
|
||||||
|
0x0D, 0x10, 0x03, 0x00, // STVA (format4) with ni=IMMEDIATE -> b1=0x0D
|
||||||
|
0x3F, 0x10, 0x00, 0x12 // J (format4) loop to 0x12
|
||||||
|
};
|
||||||
|
|
||||||
|
const int PROG_START = 0x00;
|
||||||
|
for (size_t i = 0; i < sizeof(prog); ++i) {
|
||||||
|
machine.setByte(PROG_START + static_cast<int>(i), prog[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
machine.setPC(PROG_START);
|
||||||
|
|
||||||
|
cout << "Program loaded. VA@0x" << std::hex << VA_ADDR << " VB@0x" << VB_ADDR << " -> store@0x" << VR_ADDR << std::dec << endl;
|
||||||
|
|
||||||
cout << "Machine initialized successfully." << endl;
|
|
||||||
|
|
||||||
// COMPUTE A + B and store result in B
|
const int MAX_STEPS = 100;
|
||||||
machine.setA(10);
|
for (int i = 0; i < MAX_STEPS; ++i) {
|
||||||
machine.setB(20);
|
machine.execute();
|
||||||
machine.setByte(0, ADDR);
|
}
|
||||||
machine.setByte(1, 0x03); // r1 = 0 (A), r2 = 3 (B)
|
|
||||||
cout << "Before ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl;
|
// Read back result vector stored at VR_ADDR
|
||||||
machine.execute();
|
cout << "Result vector at 0x" << std::hex << VR_ADDR << std::dec << ": ";
|
||||||
cout << "After ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl;
|
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||||
|
int val = machine.getWord(VR_ADDR + i * 3);
|
||||||
|
cout << val << (i + 1 < VECTOR_REG_SIZE ? ", " : "\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadInstructionSet();
|
||||||
|
std::shared_ptr<Machine> machine = std::make_shared<Machine>();
|
||||||
|
Loader loader(machine, std::string(PATH_RESOURCES) + "test.obj");
|
||||||
|
loader.load();
|
||||||
|
machine->execute();
|
||||||
|
machine->execute();
|
||||||
|
machine->execute();
|
||||||
|
machine->execute();
|
||||||
|
machine->execute();
|
||||||
|
machine->execute();
|
||||||
|
cout << "Register A after execution: " << machine->getA() << endl;
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,75 +1,109 @@
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "instructions.h"
|
#include "instructions.h"
|
||||||
|
#include "utils.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
InstructionInfo instructions[0xff];
|
InstructionInfo instructions[0xff];
|
||||||
|
InstructionInfo instructionsEXEX[0xff];
|
||||||
|
|
||||||
void loadInstructionSet()
|
void loadInstructionSet()
|
||||||
{
|
{
|
||||||
instructions[ADD] = {"ADD", InstructionType::TYPE3_4, nullptr};
|
instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(add_handler)};
|
||||||
instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, nullptr};
|
instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(addf_handler)};
|
||||||
instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(addr_handler)};
|
instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(addr_handler)};
|
||||||
instructions[AND] = {"AND", InstructionType::TYPE3_4, nullptr};
|
instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(and_handler)};
|
||||||
instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(clear_handler)};
|
instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(clear_handler)};
|
||||||
instructions[COMP] = {"COMP", InstructionType::TYPE3_4, nullptr};
|
instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(comp_handler)};
|
||||||
instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, nullptr};
|
instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(compf_handler)};
|
||||||
instructions[COMPR] = {"COMPR", InstructionType::TYPE2, nullptr};
|
instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(compr_handler)};
|
||||||
instructions[DIV] = {"DIV", InstructionType::TYPE3_4, nullptr};
|
instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(div_handler)};
|
||||||
instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, nullptr};
|
instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(divf_handler)};
|
||||||
instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(divr_handler)};
|
instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(divr_handler)};
|
||||||
instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr};
|
instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast<RawHandler>(fix_handler)};
|
||||||
instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr};
|
instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(float_handler)};
|
||||||
instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr};
|
instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr};
|
||||||
instructions[J] = {"J", InstructionType::TYPE3_4, nullptr};
|
instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(j_handler)};
|
||||||
instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, nullptr};
|
instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jeq_handler)};
|
||||||
instructions[JGT] = {"JGT", InstructionType::TYPE3_4, nullptr};
|
instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jgt_handler)};
|
||||||
instructions[JLT] = {"JLT", InstructionType::TYPE3_4, nullptr};
|
instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jlt_handler)};
|
||||||
instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, nullptr};
|
instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jsub_handler)};
|
||||||
instructions[LDA] = {"LDA", InstructionType::TYPE3_4, nullptr};
|
instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lda_handler)};
|
||||||
instructions[LDB] = {"LDB", InstructionType::TYPE3_4, nullptr};
|
instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldb_handler)};
|
||||||
instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, nullptr};
|
instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldch_handler)};
|
||||||
instructions[LDF] = {"LDF", InstructionType::TYPE3_4, nullptr};
|
instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldf_handler)};
|
||||||
instructions[LDL] = {"LDL", InstructionType::TYPE3_4, nullptr};
|
instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldl_handler)};
|
||||||
instructions[LDS] = {"LDS", InstructionType::TYPE3_4, nullptr};
|
instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lds_handler)};
|
||||||
instructions[LDT] = {"LDT", InstructionType::TYPE3_4, nullptr};
|
instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldt_handler)};
|
||||||
instructions[LDX] = {"LDX", InstructionType::TYPE3_4, nullptr};
|
instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldx_handler)};
|
||||||
instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr};
|
instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr};
|
||||||
instructions[MUL] = {"MUL", InstructionType::TYPE3_4, nullptr};
|
instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mul_handler)};
|
||||||
instructions[MULF] = {"MULF", InstructionType::TYPE3_4, nullptr};
|
instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mulf_handler)};
|
||||||
instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(mulr_handler)};
|
instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(mulr_handler)};
|
||||||
instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr};
|
instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast<RawHandler>(norm_handler)};
|
||||||
instructions[OR] = {"OR", InstructionType::TYPE3_4, nullptr};
|
instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(or_handler)};
|
||||||
instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr};
|
instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rd_handler)};
|
||||||
instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast<RawHandler>(rmo_handler)};
|
instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast<RawHandler>(rmo_handler)};
|
||||||
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, nullptr};
|
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rsub_handler)};
|
||||||
instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftl_handler)};
|
instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftl_handler)};
|
||||||
instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftr_handler)};
|
instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftr_handler)};
|
||||||
instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr};
|
instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr};
|
||||||
instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr};
|
instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr};
|
||||||
instructions[STA] = {"STA", InstructionType::TYPE3_4, nullptr};
|
instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sta_handler)};
|
||||||
instructions[STB] = {"STB", InstructionType::TYPE3_4, nullptr};
|
instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stb_handler)};
|
||||||
instructions[STCH] = {"STCH", InstructionType::TYPE3_4, nullptr};
|
instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stch_handler)};
|
||||||
instructions[STF] = {"STF", InstructionType::TYPE3_4, nullptr};
|
instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stf_handler)};
|
||||||
instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr};
|
instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr};
|
||||||
instructions[STL] = {"STL", InstructionType::TYPE3_4, nullptr};
|
instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stl_handler)};
|
||||||
instructions[STS] = {"STS", InstructionType::TYPE3_4, nullptr};
|
instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sts_handler)};
|
||||||
instructions[STSW] = {"STSW", InstructionType::TYPE3_4, nullptr};
|
instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stsw_handler)};
|
||||||
instructions[STT] = {"STT", InstructionType::TYPE3_4, nullptr};
|
instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stt_handler)};
|
||||||
instructions[STX] = {"STX", InstructionType::TYPE3_4, nullptr};
|
instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stx_handler)};
|
||||||
instructions[SUB] = {"SUB", InstructionType::TYPE3_4, nullptr};
|
instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sub_handler)};
|
||||||
instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, nullptr};
|
instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(subf_handler)};
|
||||||
instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(subr_handler)};
|
instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(subr_handler)};
|
||||||
instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast<RawHandler>(svc_handler)};
|
instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast<RawHandler>(svc_handler)};
|
||||||
instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(tixr_handler)};
|
instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(tixr_handler)};
|
||||||
instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr};
|
instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(td_handler)};
|
||||||
instructions[TIX] = {"TIX", InstructionType::TYPE3_4, nullptr};
|
instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(tix_handler)};
|
||||||
instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr};
|
instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr};
|
||||||
instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr};
|
instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(wd_handler)};
|
||||||
|
|
||||||
// Mark uninitialized opcodes as INVALID
|
// Load SIC/XE/XE extended instructions
|
||||||
for (int i = 0; i < 0xff; ++i) {
|
if (USE_EXTENDED_MODE) {
|
||||||
if (instructions[i].name == nullptr) {
|
// Still in main table
|
||||||
instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
instructions[NOP] = {"NOP", InstructionType::TYPE1, reinterpret_cast<RawHandler>(nop_handler)};
|
||||||
}
|
instructions[HALT] = {"HALT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(halt_handler)};
|
||||||
|
instructions[XEXE] = {"XEXE", InstructionType::TYPE1, reinterpret_cast<RawHandler>(xexe_handler)};
|
||||||
|
|
||||||
|
instructionsEXEX[VADD] = {"VADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vadd_handler)};
|
||||||
|
instructionsEXEX[VADDR] = {"VADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vaddr_handler)};
|
||||||
|
instructionsEXEX[VSUB] = {"VSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vsub_handler)};
|
||||||
|
instructionsEXEX[VSUBR] = {"VSUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vsubr_handler)};
|
||||||
|
instructionsEXEX[VMUL] = {"VMUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vmul_handler)};
|
||||||
|
instructionsEXEX[VMULR] = {"VMULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vmulr_handler)};
|
||||||
|
instructionsEXEX[VDIV] = {"VDIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vdiv_handler)};
|
||||||
|
instructionsEXEX[VDIVR] = {"VDIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vdivr_handler)};
|
||||||
|
instructionsEXEX[STVA] = {"STVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stva_handler)};
|
||||||
|
instructionsEXEX[STVS] = {"STVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvs_handler)};
|
||||||
|
instructionsEXEX[STVT] = {"STVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvt_handler)};
|
||||||
|
instructionsEXEX[LDVA] = {"LDVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldva_handler)};
|
||||||
|
instructionsEXEX[LDVS] = {"LDVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvs_handler)};
|
||||||
|
instructionsEXEX[LDVT] = {"LDVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvt_handler)};
|
||||||
}
|
}
|
||||||
}
|
// Mark uninitialized opcodes as INVALID
|
||||||
|
for (int i = 0; i < 0xff; ++i) {
|
||||||
|
if (instructions[i].name == nullptr) instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||||
|
if (instructionsEXEX[i].name == nullptr) instructionsEXEX[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressingMode getAddressingMode(int ni)
|
||||||
|
{
|
||||||
|
switch (ni) {
|
||||||
|
case 0x0: return AddressingMode::SIC_DIRECT;
|
||||||
|
case 0x1: return AddressingMode::IMMEDIATE;
|
||||||
|
case 0x2: return AddressingMode::INDIRECT;
|
||||||
|
case 0x3: return AddressingMode::SIMPLE;
|
||||||
|
default: return AddressingMode::INVALID; // Should not happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
33
simulator_SIC_XE/src/string_reader.cpp
Normal file
33
simulator_SIC_XE/src/string_reader.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "string_reader.h"
|
||||||
|
|
||||||
|
StringReader::StringReader(const std::string &s)
|
||||||
|
: in(s)
|
||||||
|
{}
|
||||||
|
|
||||||
|
StringReader::~StringReader() = default;
|
||||||
|
|
||||||
|
int StringReader::readByte() {
|
||||||
|
char c;
|
||||||
|
if (!in.get(c)) return -1;
|
||||||
|
return static_cast<unsigned char>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StringReader::readBytes(uint8_t* buf, size_t len) {
|
||||||
|
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
|
||||||
|
return static_cast<size_t>(in.gcount()) == len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringReader::readString(size_t len) {
|
||||||
|
std::string s;
|
||||||
|
s.resize(len);
|
||||||
|
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
|
||||||
|
std::streamsize got = in.gcount();
|
||||||
|
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringReader::readLine() {
|
||||||
|
std::string s;
|
||||||
|
if (!std::getline(in, s)) return std::string();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
7
test.asm
Normal file
7
test.asm
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
test START 0
|
||||||
|
LDA #1
|
||||||
|
LDB #2
|
||||||
|
ADDR B, A
|
||||||
|
|
||||||
|
halt J halt
|
||||||
|
END test
|
||||||
Loading…
Add table
Add a link
Reference in a new issue