diff --git a/simulator_SIC_XE/.gitignore b/simulator_SIC_XE/.gitignore index 6b70294..4cbe372 100644 --- a/simulator_SIC_XE/.gitignore +++ b/simulator_SIC_XE/.gitignore @@ -7,7 +7,6 @@ CMakeCache.txt CMakeFiles/ CMakeScripts/ cmake_install.cmake -Makefile *.cmake !CMakeLists.txt @@ -71,6 +70,7 @@ xcuserdata/ *.moc.cpp *.qm *.prl +CMakeLists.txt.user # OS generated files .DS_Store diff --git a/simulator_SIC_XE/CMakeLists.txt b/simulator_SIC_XE/CMakeLists.txt index 242ebd3..c6c37e1 100644 --- a/simulator_SIC_XE/CMakeLists.txt +++ b/simulator_SIC_XE/CMakeLists.txt @@ -4,7 +4,7 @@ project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) 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(CMAKE_RUNTIME_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) endif() -# Convenience target: `cmake --build build --target run` -# This target will build `simulator_exec` (if present) and then execute it. + if(TARGET simulator_exec) add_custom_target(run DEPENDS simulator_exec @@ -43,3 +42,19 @@ endif() message(STATUS "Project: ${PROJECT_NAME}") message(STATUS "Sources found: ${SOURCES}") 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() diff --git a/simulator_SIC_XE/Makefile b/simulator_SIC_XE/Makefile new file mode 100644 index 0000000..92ab3cc --- /dev/null +++ b/simulator_SIC_XE/Makefile @@ -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/* diff --git a/simulator_SIC_XE/gui/qt/CMakeLists.txt b/simulator_SIC_XE/gui/qt/CMakeLists.txt new file mode 100644 index 0000000..cf8072a --- /dev/null +++ b/simulator_SIC_XE/gui/qt/CMakeLists.txt @@ -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 +) \ No newline at end of file diff --git a/simulator_SIC_XE/gui/qt/MachineController.cpp b/simulator_SIC_XE/gui/qt/MachineController.cpp new file mode 100644 index 0000000..0ea4178 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MachineController.cpp @@ -0,0 +1,55 @@ +#include "MachineController.h" +#include "../../include/machine.h" +#include +#include + +MachineController::MachineController(std::shared_ptr machine, QObject *parent) + : QObject(parent), m_machine(std::move(machine)) +{ + if (!m_machine) { + m_machine = std::make_shared(); + } +} + +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; + } + } +} diff --git a/simulator_SIC_XE/gui/qt/MachineController.h b/simulator_SIC_XE/gui/qt/MachineController.h new file mode 100644 index 0000000..fa904a3 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MachineController.h @@ -0,0 +1,32 @@ +#ifndef MACHINECONTROLLER_H +#define MACHINECONTROLLER_H + +#include +#include +#include +#include + +class Machine; + +class MachineController : public QObject { + Q_OBJECT +public: + explicit MachineController(std::shared_ptr 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 m_running{false}; + std::thread m_thread; + std::shared_ptr m_machine; +}; + +#endif // MACHINECONTROLLER_H diff --git a/simulator_SIC_XE/gui/qt/main.cpp b/simulator_SIC_XE/gui/qt/main.cpp new file mode 100644 index 0000000..f9b6517 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/main.cpp @@ -0,0 +1,11 @@ +#include +#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(); +} \ No newline at end of file diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp new file mode 100644 index 0000000..afc464c --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Loader; + +std::shared_ptr g_loader; + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + m_machine(std::make_shared()), + m_controller(std::make_unique(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(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(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(regPrefix + "_dec_field"); + if (decField && !decField->hasFocus()) { + decField->setText(QString::number(value)); + } + + // Update hex field + QLineEdit* hexField = findChild(regPrefix + "_hex_field"); + if (hexField && !hexField->hasFocus()) { + // Convert to 24-bit representation, handle negative numbers + unsigned int unsignedValue = static_cast(value) & 0xFFFFFF; + hexField->setText(QString("0x%1").arg(unsignedValue, 6, 16, QChar('0')).toUpper()); + } + + // Update binary field + QLineEdit* binField = findChild(regPrefix + "_bin_field"); + if (binField && !binField->hasFocus()) { + // Convert to 24-bit binary representation + unsigned int unsignedValue = static_cast(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(regPrefix + "_dec_field"); + if (decField && !decField->hasFocus()) { + decField->setText(QString::number(value, 'g', 10)); + } + + // Update hex field (48-bit float representation) + QLineEdit* hexField = findChild(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(&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(regPrefix + "_bin_field"); + if (binField && !binField->hasFocus()) { + // Convert double to 48-bit binary representation + uint64_t* intPtr = reinterpret_cast(&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(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(&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(&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); +} diff --git a/simulator_SIC_XE/gui/qt/mainwindow.h b/simulator_SIC_XE/gui/qt/mainwindow.h new file mode 100644 index 0000000..1e39a7d --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.h @@ -0,0 +1,82 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +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() 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 m_machine; + std::unique_ptr 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 diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui new file mode 100644 index 0000000..2fad622 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -0,0 +1,883 @@ + + + MainWindow + + + + 0 + 0 + 1172 + 649 + + + + MainWindow + + + + + + 10 + 0 + 431 + 601 + + + + + + 0 + 80 + 431 + 321 + + + + Register values + + + + + 70 + 40 + 113 + 23 + + + + + + + 10 + 40 + 57 + 20 + + + + + 10 + + + + Reg A + + + + + + 190 + 40 + 113 + 23 + + + + + + + 310 + 40 + 113 + 23 + + + + + + + 110 + 20 + 57 + 20 + + + + + 10 + + + + Bin + + + + + + 230 + 20 + 57 + 20 + + + + + 10 + + + + Hex + + + + + + 350 + 20 + 57 + 20 + + + + + 10 + + + + Dec + + + + + + 310 + 70 + 113 + 23 + + + + + + + 10 + 70 + 57 + 20 + + + + + 10 + + + + Reg B + + + + + + 190 + 70 + 113 + 23 + + + + + + + 70 + 70 + 113 + 23 + + + + + + + 310 + 100 + 113 + 23 + + + + + + + 10 + 100 + 57 + 20 + + + + + 10 + + + + Reg X + + + + + + 190 + 100 + 113 + 23 + + + + + + + 70 + 100 + 113 + 23 + + + + + + + 310 + 130 + 113 + 23 + + + + + + + 10 + 130 + 57 + 20 + + + + + 10 + + + + Reg S + + + + + + 190 + 130 + 113 + 23 + + + + + + + 70 + 130 + 113 + 23 + + + + + + + 310 + 160 + 113 + 23 + + + + + + + 10 + 160 + 57 + 20 + + + + + 10 + + + + Reg T + + + + + + 190 + 160 + 113 + 23 + + + + + + + 70 + 160 + 113 + 23 + + + + + + + 190 + 190 + 113 + 23 + + + + + + + 70 + 190 + 113 + 23 + + + + + + + 10 + 190 + 57 + 20 + + + + + 10 + + + + Reg L + + + + + + 310 + 190 + 113 + 23 + + + + + + + 10 + 220 + 57 + 20 + + + + + 10 + + + + Reg PC + + + + + + 310 + 220 + 113 + 23 + + + + + + + 70 + 220 + 113 + 23 + + + + + + + 190 + 220 + 113 + 23 + + + + + + + 10 + 249 + 57 + 20 + + + + + 10 + + + + Reg SW + + + + + + 310 + 249 + 113 + 23 + + + + + + + 70 + 249 + 113 + 23 + + + + + + + 190 + 249 + 113 + 23 + + + + + + + 10 + 280 + 57 + 20 + + + + + 10 + + + + Reg L + + + + + + 310 + 280 + 113 + 23 + + + + + + + 70 + 280 + 113 + 23 + + + + + + + 190 + 280 + 113 + 23 + + + + + + + + 0 + 0 + 431 + 81 + + + + Control + + + + + 30 + 40 + 80 + 23 + + + + Start + + + + + + 170 + 40 + 80 + 23 + + + + Stop + + + + + + 310 + 40 + 80 + 23 + + + + Step + + + + + + + + 450 + 0 + 721 + 601 + + + + + + 0 + 0 + 711 + 291 + + + + Memory + + + + + 0 + 20 + 711 + 221 + + + + true + + + + + 0 + 0 + 709 + 219 + + + + + + + + 450 + 250 + 71 + 23 + + + + >> + + + + + + 370 + 250 + 71 + 23 + + + + > + + + + + + 530 + 250 + 71 + 23 + + + + >>> + + + + + + 290 + 250 + 71 + 23 + + + + < + + + + + + 210 + 250 + 71 + 23 + + + + << + + + + + + 130 + 250 + 71 + 21 + + + + <<< + + + + + + 50 + 250 + 71 + 21 + + + + O + + + + + + 610 + 250 + 71 + 21 + + + + | + + + + + + + 0 + 300 + 711 + 301 + + + + Disasembly + + + + + 0 + 20 + 711 + 221 + + + + true + + + + + 0 + 0 + 709 + 219 + + + + + + + + 440 + 250 + 71 + 23 + + + + >> + + + + + + 360 + 250 + 71 + 23 + + + + > + + + + + + 520 + 250 + 71 + 23 + + + + >>> + + + + + + 280 + 250 + 71 + 23 + + + + < + + + + + + 200 + 250 + 71 + 23 + + + + << + + + + + + 120 + 250 + 71 + 21 + + + + <<< + + + + + + 40 + 250 + 71 + 21 + + + + O + + + + + + 600 + 250 + 71 + 21 + + + + | + + + + + + + + + 0 + 0 + 1172 + 20 + + + + + + + + diff --git a/simulator_SIC_XE/include/constants.h b/simulator_SIC_XE/include/constants.h new file mode 100644 index 0000000..6f2195a --- /dev/null +++ b/simulator_SIC_XE/include/constants.h @@ -0,0 +1,52 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include +// ============================== +// 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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/file_reader.h b/simulator_SIC_XE/include/file_reader.h new file mode 100644 index 0000000..87b8a17 --- /dev/null +++ b/simulator_SIC_XE/include/file_reader.h @@ -0,0 +1,25 @@ +#ifndef FILE_READER_H +#define FILE_READER_H + +#include "reader.h" +#include +#include + +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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/instructions.h b/simulator_SIC_XE/include/instructions.h index ff2e532..5bef303 100644 --- a/simulator_SIC_XE/include/instructions.h +++ b/simulator_SIC_XE/include/instructions.h @@ -1,11 +1,26 @@ #ifndef 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 void addr_handler(Machine& m, int r1, int r2); 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 mulr_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); +// 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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/loader.h b/simulator_SIC_XE/include/loader.h new file mode 100644 index 0000000..41899c2 --- /dev/null +++ b/simulator_SIC_XE/include/loader.h @@ -0,0 +1,68 @@ +#ifndef LOADER_H +#define LOADER_H + +#include +#include +#include +#include "file_reader.h" +#include + +class Machine; + +using std::shared_ptr; +using std::string; + + +class Loader { +public: + Loader( shared_ptr machine, string filename) : _machine(machine), _filename(filename) { + _file_reader = std::make_shared(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 data; + }; + struct EndRecord { + int execution_start_address; + }; + + void load(); + +private : + + static RecordType parseRecordType(char c); + + + shared_ptr _machine; + string _filename; + shared_ptr _file_reader; + HeaderMetadata readHeader(); + TextRecord readTextRecord(); + EndRecord readEndRecord(); + bool load_into_memory(int start_address, const std::vector& data); + +}; + + + + + +#endif // LOADER_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/machine.h b/simulator_SIC_XE/include/machine.h index e0920da..8e80669 100644 --- a/simulator_SIC_XE/include/machine.h +++ b/simulator_SIC_XE/include/machine.h @@ -4,18 +4,18 @@ #include #include #include +#include +#include +#include + +#include "constants.h" #include "device.h" #include "input_device.h" #include "output_device.h" #include "file_device.h" #include "opcode.h" - -#include - -#define MEMORY_SIZE 65536 -#define NUM_DEVICES 256 - +#include "utils.h" using std::string; using std::cerr; @@ -26,30 +26,32 @@ using std::cout; class Machine { public: Machine(); + Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; _instructionsTable = instructions; } ~Machine(); - // Accessor methods for registers int getA() const { return A; } - void setA(int value) { A = value; } + void setA(int value) { A = toSIC24(value); } int getB() const { return B; } - void setB(int value) { B = value; } + void setB(int value) { B = toSIC24(value); } int getX() const { return X; } - void setX(int value) { X = value; } + void setX(int value) { X = toSIC24(value); } int getL() const { return L; } - void setL(int value) { L = value; } + void setL(int value) { L = toSIC24(value); } int getS() const { return S; } - void setS(int value) { S = value; } + void setS(int value) { S = toSIC24(value); } 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; } void setPC(int value) { PC = value; } + // status word: keep as-is int getSW() const { return SW; } void setSW(int value) { SW = value; } @@ -76,14 +78,17 @@ public: // Set a file device at index `num` using the provided filename. void setFileDevice(int num, const std::string &filename); - // Fetch and execute instructions int fetch(); void execute(); - bool execF1(int opcode); - bool execF2(int opcode, int operand); - bool execSICF3F4(int opcode, int ni, int operand); + // Execution and speed control + int getSpeed() const; + void setSpeed(int kHz); + void start(); + void stop(); + void tick(); + void halt(); // error handling methods void notImplemented(string mnemonic); @@ -92,6 +97,22 @@ public: void divisionByZero(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: // registers int A, B, X, L, S, T, PC, SW; @@ -104,42 +125,23 @@ private: std::vector> devices; // fallback device returned when device slot is empty/invalid Device fallbackDevice; + + // Execution control + std::atomic running{false}; + std::atomic 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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/opcode.h b/simulator_SIC_XE/include/opcode.h index 93df629..a467488 100644 --- a/simulator_SIC_XE/include/opcode.h +++ b/simulator_SIC_XE/include/opcode.h @@ -1,6 +1,8 @@ #ifndef OPCODE_H #define OPCODE_H +#include "utils.h" + // ============================== // Opcode definitions (SIC/XE) // ============================== @@ -64,13 +66,26 @@ #define TIXR 0xB8 #define WD 0xDC - - -// 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 +// ============================== +// Extended opcodes (SIC/XE/XE) +// ============================== +#define NOP 0xF1 +#define HALT 0xF2 +#define XEXE 0xEE // Enable extended mode +#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 instructionsEXEX[]; // Initialize the instruction table void loadInstructionSet(); diff --git a/simulator_SIC_XE/include/reader.h b/simulator_SIC_XE/include/reader.h new file mode 100644 index 0000000..e582161 --- /dev/null +++ b/simulator_SIC_XE/include/reader.h @@ -0,0 +1,21 @@ +#ifndef READER_H +#define READER_H + +#include +#include + +// 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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/string_reader.h b/simulator_SIC_XE/include/string_reader.h new file mode 100644 index 0000000..71c3da9 --- /dev/null +++ b/simulator_SIC_XE/include/string_reader.h @@ -0,0 +1,22 @@ +#ifndef STRING_READER_H +#define STRING_READER_H + +#include "reader.h" +#include +#include + +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 \ No newline at end of file diff --git a/simulator_SIC_XE/include/utils.h b/simulator_SIC_XE/include/utils.h new file mode 100644 index 0000000..3197a95 --- /dev/null +++ b/simulator_SIC_XE/include/utils.h @@ -0,0 +1,95 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "constants.h" + +#include + +// ============================== +// 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 \ No newline at end of file diff --git a/simulator_SIC_XE/src/file_reader.cpp b/simulator_SIC_XE/src/file_reader.cpp new file mode 100644 index 0000000..2a800cc --- /dev/null +++ b/simulator_SIC_XE/src/file_reader.cpp @@ -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(c); +} + + +bool FileReader::readBytes(uint8_t* buf, size_t len) { + in.read(reinterpret_cast(buf), static_cast(len)); + return static_cast(in.gcount()) == len; +} + +std::string FileReader::readString(size_t len) { + std::string s; + s.resize(len); + in.read(reinterpret_cast(&s[0]), static_cast(len)); + std::streamsize got = in.gcount(); + if (static_cast(got) < len) s.resize(static_cast(got)); + return s; +} + +bool FileReader::good() const { return static_cast(in); } + +std::string FileReader::readLine() { + std::string s; + if (!std::getline(in, s)) return std::string(); + return s; +} diff --git a/simulator_SIC_XE/src/instructions.cpp b/simulator_SIC_XE/src/instructions.cpp index 364fed1..9a88a68 100644 --- a/simulator_SIC_XE/src/instructions.cpp +++ b/simulator_SIC_XE/src/instructions.cpp @@ -1,16 +1,96 @@ #include "instructions.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(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(m.getF())); +} + +void float_handler(Machine &m) +{ + m.setF(static_cast(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)); } -// CLEAR instruction: clears register r (first nibble), second nibble unused void clear_handler(Machine& m, int r, int unused) { 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) { @@ -31,15 +111,33 @@ void rmo_handler(Machine &m, int r1, int r2) m.setReg(r2, m.getReg(r1)); } + +// SHIFTL r1, n → left *circular* shift n bits 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) { - 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) { 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); 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(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); +} diff --git a/simulator_SIC_XE/src/loader.cpp b/simulator_SIC_XE/src/loader.cpp new file mode 100644 index 0000000..9b3d293 --- /dev/null +++ b/simulator_SIC_XE/src/loader.cpp @@ -0,0 +1,128 @@ +#include "loader.h" +#include "file_reader.h" +#include "machine.h" +#include "constants.h" +#include + +Loader::~Loader() +{ + _machine.reset(); +} + + + + +void Loader::load() +{ + HeaderMetadata header = readHeader(); + + while(true) { + RecordType type = parseRecordType(static_cast(_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(_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(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 &data) +{ + for(size_t i = 0; i < data.size(); ++i) { + int addr = start_address + static_cast(i); + if (addr < 0 || addr >= MEMORY_SIZE) { + return false; + } + _machine->setByte(addr, data[i]); + } + return true; +} diff --git a/simulator_SIC_XE/src/machine.cpp b/simulator_SIC_XE/src/machine.cpp index d190f6d..9570054 100644 --- a/simulator_SIC_XE/src/machine.cpp +++ b/simulator_SIC_XE/src/machine.cpp @@ -4,6 +4,8 @@ #include "opcode.h" #include "instructions.h" +#include +#include using std::make_shared; @@ -19,6 +21,8 @@ Machine::Machine() devices[1] = make_shared(std::cout); // device 2: standard error devices[2] = make_shared(std::cerr); + _exex_mode = false; + _instructionsTable = instructions; } 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) { cout << prefix << "Not implemented: " << mnemonic << endl; @@ -38,6 +53,7 @@ void Machine::invalidOpcode(int opcode) cout << prefix << "Invalid opcode: " << opcode << endl; } + void Machine::invalidAddressing() { cout << prefix << "Invalid addressing mode" << endl; @@ -53,6 +69,76 @@ void Machine::undefinedHandler(int opcode) 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 { switch (regNum) { @@ -128,23 +214,93 @@ void Machine::setWord(int address, int value) cerr << prefix << "Invalid memory address: " << address << endl; return; } + value &= 0xFFFFFF; memory[address] = static_cast(value & 0xFF); memory[address + 1] = static_cast((value >> 8) & 0xFF); memory[address + 2] = static_cast((value >> 16) & 0xFF); } -// TODO: implement proper float storage and retrieval 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) { - // 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) { if(num < 0 || num >= static_cast(devices.size()) || !devices[num]) { @@ -195,49 +351,55 @@ int Machine::fetch() return getByte(PC++); } -void Machine::execute() -{ - int opcode = fetch(); - InstructionType type = instructions[opcode].type; - switch (type) { - case InstructionType::TYPE1: execF1(opcode);break; - case InstructionType::TYPE2: execF2(opcode, fetch());break; - case InstructionType::TYPE3_4: // extract n and i bits - { - int ni = opcode & 0x3; - int operand = fetch(); - execSICF3F4(opcode, ni, operand); - } - break; - default: invalidOpcode(opcode); break; +void Machine::execute() { + if (_stopped) return; + int b1 = fetch(); + + InstructionInfo &info = _instructionsTable[b1]; + + if (info.type == InstructionType::TYPE1) { execF1(b1); return; } + if (info.type == InstructionType::TYPE2) { execF2(b1, fetch()); return; } + + int opcode = b1 & TYPE3_4_SIC_MASK; + InstructionInfo &info34 = _instructionsTable[opcode]; + int ni = b1 & NI_MASK; + + if (info34.type == InstructionType::TYPE3_4) { + 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) { - switch (opcode) - { - case FIX: - setA(static_cast(getF())); + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); + handler(*this); return true; - case FLOAT: - setF(static_cast(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; } @@ -246,8 +408,8 @@ bool Machine::execF2(int opcode, int operand) int r1 = (operand >> 4) & 0xF; int r2 = operand & 0xF; - if (instructions[opcode].handler) { - auto handler = reinterpret_cast(instructions[opcode].handler); + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); handler(*this, r1, r2); return true; } @@ -255,7 +417,63 @@ bool Machine::execF2(int opcode, int operand) 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(_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(_instructionsTable[opcode].handler); + h(*this, ea, mode); + return true; + } + + undefinedHandler(opcode); 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); +} diff --git a/simulator_SIC_XE/src/main.cpp b/simulator_SIC_XE/src/main.cpp index 1c285b9..bd7a9e7 100644 --- a/simulator_SIC_XE/src/main.cpp +++ b/simulator_SIC_XE/src/main.cpp @@ -1,27 +1,110 @@ #include +#include +#include #include "machine.h" #include "file_device.h" #include "opcode.h" #include "instructions.h" +#include "constants.h" +#include "loader.h" using std::cout; using std::endl; +struct VectorAddProgram { + int x, y; +}; + + + int main() { + /* loadInstructionSet(); 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(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 - machine.setA(10); - machine.setB(20); - machine.setByte(0, ADDR); - machine.setByte(1, 0x03); // r1 = 0 (A), r2 = 3 (B) - cout << "Before ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl; - machine.execute(); - cout << "After ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl; + const int MAX_STEPS = 100; + for (int i = 0; i < MAX_STEPS; ++i) { + machine.execute(); + } + + // Read back result vector stored at VR_ADDR + cout << "Result vector at 0x" << std::hex << VR_ADDR << std::dec << ": "; + 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 = std::make_shared(); + 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; } \ No newline at end of file diff --git a/simulator_SIC_XE/src/opcode.cpp b/simulator_SIC_XE/src/opcode.cpp index febc489..0b63ce8 100644 --- a/simulator_SIC_XE/src/opcode.cpp +++ b/simulator_SIC_XE/src/opcode.cpp @@ -1,75 +1,109 @@ #include "opcode.h" #include "instructions.h" +#include "utils.h" #include InstructionInfo instructions[0xff]; +InstructionInfo instructionsEXEX[0xff]; void loadInstructionSet() { - instructions[ADD] = {"ADD", InstructionType::TYPE3_4, nullptr}; - instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, nullptr}; - instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; - instructions[AND] = {"AND", InstructionType::TYPE3_4, nullptr}; - instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; - instructions[COMP] = {"COMP", InstructionType::TYPE3_4, nullptr}; - instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, nullptr}; - instructions[COMPR] = {"COMPR", InstructionType::TYPE2, nullptr}; - instructions[DIV] = {"DIV", InstructionType::TYPE3_4, nullptr}; - instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, nullptr}; - instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; - instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr}; - instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr}; - instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; - instructions[J] = {"J", InstructionType::TYPE3_4, nullptr}; - instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, nullptr}; - instructions[JGT] = {"JGT", InstructionType::TYPE3_4, nullptr}; - instructions[JLT] = {"JLT", InstructionType::TYPE3_4, nullptr}; - instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, nullptr}; - instructions[LDA] = {"LDA", InstructionType::TYPE3_4, nullptr}; - instructions[LDB] = {"LDB", InstructionType::TYPE3_4, nullptr}; - instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, nullptr}; - instructions[LDF] = {"LDF", InstructionType::TYPE3_4, nullptr}; - instructions[LDL] = {"LDL", InstructionType::TYPE3_4, nullptr}; - instructions[LDS] = {"LDS", InstructionType::TYPE3_4, nullptr}; - instructions[LDT] = {"LDT", InstructionType::TYPE3_4, nullptr}; - instructions[LDX] = {"LDX", InstructionType::TYPE3_4, nullptr}; - instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; - instructions[MUL] = {"MUL", InstructionType::TYPE3_4, nullptr}; - instructions[MULF] = {"MULF", InstructionType::TYPE3_4, nullptr}; - instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; - instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr}; - instructions[OR] = {"OR", InstructionType::TYPE3_4, nullptr}; - instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr}; - instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; - instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, nullptr}; - instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; - instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; - instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; - instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; - instructions[STA] = {"STA", InstructionType::TYPE3_4, nullptr}; - instructions[STB] = {"STB", InstructionType::TYPE3_4, nullptr}; - instructions[STCH] = {"STCH", InstructionType::TYPE3_4, nullptr}; - instructions[STF] = {"STF", InstructionType::TYPE3_4, nullptr}; - instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; - instructions[STL] = {"STL", InstructionType::TYPE3_4, nullptr}; - instructions[STS] = {"STS", InstructionType::TYPE3_4, nullptr}; - instructions[STSW] = {"STSW", InstructionType::TYPE3_4, nullptr}; - instructions[STT] = {"STT", InstructionType::TYPE3_4, nullptr}; - instructions[STX] = {"STX", InstructionType::TYPE3_4, nullptr}; - instructions[SUB] = {"SUB", InstructionType::TYPE3_4, nullptr}; - instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, nullptr}; - instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; - instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; - instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; - instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr}; - instructions[TIX] = {"TIX", InstructionType::TYPE3_4, nullptr}; - instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; - instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr}; + instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast(add_handler)}; + instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast(addf_handler)}; + instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; + instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast(and_handler)}; + instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; + instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast(comp_handler)}; + instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast(compf_handler)}; + instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast(compr_handler)}; + instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast(div_handler)}; + instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast(divf_handler)}; + instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; + instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast(fix_handler)}; + instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast(float_handler)}; + instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; + instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast(j_handler)}; + instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast(jeq_handler)}; + instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast(jgt_handler)}; + instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast(jlt_handler)}; + instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast(jsub_handler)}; + instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast(lda_handler)}; + instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast(ldb_handler)}; + instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast(ldch_handler)}; + instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast(ldf_handler)}; + instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast(ldl_handler)}; + instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast(lds_handler)}; + instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast(ldt_handler)}; + instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast(ldx_handler)}; + instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; + instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast(mul_handler)}; + instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast(mulf_handler)}; + instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; + instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast(norm_handler)}; + instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast(or_handler)}; + instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast(rd_handler)}; + instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; + instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast(rsub_handler)}; + instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; + instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; + instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; + instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; + instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast(sta_handler)}; + instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast(stb_handler)}; + instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast(stch_handler)}; + instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast(stf_handler)}; + instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; + instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast(stl_handler)}; + instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast(sts_handler)}; + instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast(stsw_handler)}; + instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast(stt_handler)}; + instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast(stx_handler)}; + instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast(sub_handler)}; + instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast(subf_handler)}; + instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; + instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; + instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; + instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast(td_handler)}; + instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast(tix_handler)}; + instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; + instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast(wd_handler)}; - // Mark uninitialized opcodes as INVALID - for (int i = 0; i < 0xff; ++i) { - if (instructions[i].name == nullptr) { - instructions[i] = {"INVALID", InstructionType::INVALID, nullptr}; - } + // Load SIC/XE/XE extended instructions + if (USE_EXTENDED_MODE) { + // Still in main table + instructions[NOP] = {"NOP", InstructionType::TYPE1, reinterpret_cast(nop_handler)}; + instructions[HALT] = {"HALT", InstructionType::TYPE1, reinterpret_cast(halt_handler)}; + instructions[XEXE] = {"XEXE", InstructionType::TYPE1, reinterpret_cast(xexe_handler)}; + + instructionsEXEX[VADD] = {"VADD", InstructionType::TYPE3_4, reinterpret_cast(vadd_handler)}; + instructionsEXEX[VADDR] = {"VADDR", InstructionType::TYPE2, reinterpret_cast(vaddr_handler)}; + instructionsEXEX[VSUB] = {"VSUB", InstructionType::TYPE3_4, reinterpret_cast(vsub_handler)}; + instructionsEXEX[VSUBR] = {"VSUBR", InstructionType::TYPE2, reinterpret_cast(vsubr_handler)}; + instructionsEXEX[VMUL] = {"VMUL", InstructionType::TYPE3_4, reinterpret_cast(vmul_handler)}; + instructionsEXEX[VMULR] = {"VMULR", InstructionType::TYPE2, reinterpret_cast(vmulr_handler)}; + instructionsEXEX[VDIV] = {"VDIV", InstructionType::TYPE3_4, reinterpret_cast(vdiv_handler)}; + instructionsEXEX[VDIVR] = {"VDIVR", InstructionType::TYPE2, reinterpret_cast(vdivr_handler)}; + instructionsEXEX[STVA] = {"STVA", InstructionType::TYPE3_4, reinterpret_cast(stva_handler)}; + instructionsEXEX[STVS] = {"STVS", InstructionType::TYPE3_4, reinterpret_cast(stvs_handler)}; + instructionsEXEX[STVT] = {"STVT", InstructionType::TYPE3_4, reinterpret_cast(stvt_handler)}; + instructionsEXEX[LDVA] = {"LDVA", InstructionType::TYPE3_4, reinterpret_cast(ldva_handler)}; + instructionsEXEX[LDVS] = {"LDVS", InstructionType::TYPE3_4, reinterpret_cast(ldvs_handler)}; + instructionsEXEX[LDVT] = {"LDVT", InstructionType::TYPE3_4, reinterpret_cast(ldvt_handler)}; } -} \ No newline at end of file + // 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 + } +} diff --git a/simulator_SIC_XE/src/string_reader.cpp b/simulator_SIC_XE/src/string_reader.cpp new file mode 100644 index 0000000..b87cc02 --- /dev/null +++ b/simulator_SIC_XE/src/string_reader.cpp @@ -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(c); +} + +bool StringReader::readBytes(uint8_t* buf, size_t len) { + in.read(reinterpret_cast(buf), static_cast(len)); + return static_cast(in.gcount()) == len; +} + +std::string StringReader::readString(size_t len) { + std::string s; + s.resize(len); + in.read(reinterpret_cast(&s[0]), static_cast(len)); + std::streamsize got = in.gcount(); + if (static_cast(got) < len) s.resize(static_cast(got)); + return s; +} + +std::string StringReader::readLine() { + std::string s; + if (!std::getline(in, s)) return std::string(); + return s; +} diff --git a/test.asm b/test.asm new file mode 100644 index 0000000..e25da30 --- /dev/null +++ b/test.asm @@ -0,0 +1,7 @@ +test START 0 + LDA #1 + LDB #2 + ADDR B, A + +halt J halt + END test \ No newline at end of file