From 9e7a9101b0474d7f5c455e6914b34fa34721266e Mon Sep 17 00:00:00 2001 From: zanostro Date: Sun, 7 Dec 2025 11:33:03 +0100 Subject: [PATCH] saved snapshot for naloga2 --- ass2/simulator_SIC_XE/.gitignore | 95 ++ ass2/simulator_SIC_XE/CMakeLists.txt | 60 + ass2/simulator_SIC_XE/Makefile | 68 ++ ass2/simulator_SIC_XE/README.md | 55 + ass2/simulator_SIC_XE/gui/qt/CMakeLists.txt | 54 + .../gui/qt/MachineController.cpp | 76 ++ .../gui/qt/MachineController.h | 34 + ass2/simulator_SIC_XE/gui/qt/main.cpp | 14 + ass2/simulator_SIC_XE/gui/qt/mainwindow.cpp | 1030 +++++++++++++++++ ass2/simulator_SIC_XE/gui/qt/mainwindow.h | 85 ++ ass2/simulator_SIC_XE/gui/qt/mainwindow.ui | 930 +++++++++++++++ ass2/simulator_SIC_XE/include/constants.h | 52 + ass2/simulator_SIC_XE/include/device.h | 16 + ass2/simulator_SIC_XE/include/file_device.h | 23 + ass2/simulator_SIC_XE/include/file_reader.h | 25 + ass2/simulator_SIC_XE/include/input_device.h | 18 + ass2/simulator_SIC_XE/include/instructions.h | 101 ++ ass2/simulator_SIC_XE/include/loader.h | 68 ++ ass2/simulator_SIC_XE/include/machine.h | 149 +++ ass2/simulator_SIC_XE/include/opcode.h | 116 ++ ass2/simulator_SIC_XE/include/output_device.h | 18 + ass2/simulator_SIC_XE/include/reader.h | 21 + ass2/simulator_SIC_XE/include/string_reader.h | 22 + ass2/simulator_SIC_XE/include/utils.h | 95 ++ ass2/simulator_SIC_XE/src/device.cpp | 19 + ass2/simulator_SIC_XE/src/file_device.cpp | 68 ++ ass2/simulator_SIC_XE/src/file_reader.cpp | 36 + ass2/simulator_SIC_XE/src/input_device.cpp | 20 + ass2/simulator_SIC_XE/src/instructions.cpp | 603 ++++++++++ ass2/simulator_SIC_XE/src/loader.cpp | 134 +++ ass2/simulator_SIC_XE/src/machine.cpp | 525 +++++++++ ass2/simulator_SIC_XE/src/main.cpp | 140 +++ ass2/simulator_SIC_XE/src/opcode.cpp | 109 ++ ass2/simulator_SIC_XE/src/output_device.cpp | 16 + ass2/simulator_SIC_XE/src/string_reader.cpp | 33 + 35 files changed, 4928 insertions(+) create mode 100644 ass2/simulator_SIC_XE/.gitignore create mode 100644 ass2/simulator_SIC_XE/CMakeLists.txt create mode 100644 ass2/simulator_SIC_XE/Makefile create mode 100644 ass2/simulator_SIC_XE/README.md create mode 100644 ass2/simulator_SIC_XE/gui/qt/CMakeLists.txt create mode 100644 ass2/simulator_SIC_XE/gui/qt/MachineController.cpp create mode 100644 ass2/simulator_SIC_XE/gui/qt/MachineController.h create mode 100644 ass2/simulator_SIC_XE/gui/qt/main.cpp create mode 100644 ass2/simulator_SIC_XE/gui/qt/mainwindow.cpp create mode 100644 ass2/simulator_SIC_XE/gui/qt/mainwindow.h create mode 100644 ass2/simulator_SIC_XE/gui/qt/mainwindow.ui create mode 100644 ass2/simulator_SIC_XE/include/constants.h create mode 100644 ass2/simulator_SIC_XE/include/device.h create mode 100644 ass2/simulator_SIC_XE/include/file_device.h create mode 100644 ass2/simulator_SIC_XE/include/file_reader.h create mode 100644 ass2/simulator_SIC_XE/include/input_device.h create mode 100644 ass2/simulator_SIC_XE/include/instructions.h create mode 100644 ass2/simulator_SIC_XE/include/loader.h create mode 100644 ass2/simulator_SIC_XE/include/machine.h create mode 100644 ass2/simulator_SIC_XE/include/opcode.h create mode 100644 ass2/simulator_SIC_XE/include/output_device.h create mode 100644 ass2/simulator_SIC_XE/include/reader.h create mode 100644 ass2/simulator_SIC_XE/include/string_reader.h create mode 100644 ass2/simulator_SIC_XE/include/utils.h create mode 100644 ass2/simulator_SIC_XE/src/device.cpp create mode 100644 ass2/simulator_SIC_XE/src/file_device.cpp create mode 100644 ass2/simulator_SIC_XE/src/file_reader.cpp create mode 100644 ass2/simulator_SIC_XE/src/input_device.cpp create mode 100644 ass2/simulator_SIC_XE/src/instructions.cpp create mode 100644 ass2/simulator_SIC_XE/src/loader.cpp create mode 100644 ass2/simulator_SIC_XE/src/machine.cpp create mode 100644 ass2/simulator_SIC_XE/src/main.cpp create mode 100644 ass2/simulator_SIC_XE/src/opcode.cpp create mode 100644 ass2/simulator_SIC_XE/src/output_device.cpp create mode 100644 ass2/simulator_SIC_XE/src/string_reader.cpp diff --git a/ass2/simulator_SIC_XE/.gitignore b/ass2/simulator_SIC_XE/.gitignore new file mode 100644 index 0000000..4cbe372 --- /dev/null +++ b/ass2/simulator_SIC_XE/.gitignore @@ -0,0 +1,95 @@ +# Build directories +build/ +target/ + +# CMake generated files +CMakeCache.txt +CMakeFiles/ +CMakeScripts/ +cmake_install.cmake +*.cmake +!CMakeLists.txt + +# Compiled Object files +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# VS Code +.vscode/ +*.code-workspace + +# CLion +.idea/ +cmake-build-*/ + +# Xcode +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 +*.xcuserstate +project.xcworkspace/ +xcuserdata/ + +# Qt Creator +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +*.moc.cpp +*.qm +*.prl +CMakeLists.txt.user + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Temporary files +*~ +*.swp +*.swo +*.tmp +*.bak + +# Log files +*.log + +# Core dumps +core \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/CMakeLists.txt b/ass2/simulator_SIC_XE/CMakeLists.txt new file mode 100644 index 0000000..c6c37e1 --- /dev/null +++ b/ass2/simulator_SIC_XE/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.10) +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 +set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + +# Collect all .cpp sources under src/ +file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp") + +if(NOT SOURCES) + message(WARNING "No source files found in ${PROJECT_SOURCE_DIR}/src — the build will create an empty library") +endif() + +# Build a static library from all sources +add_library(simulator_lib STATIC ${SOURCES}) +target_include_directories(simulator_lib PUBLIC ${PROJECT_SOURCE_DIR}/include) +set_target_properties(simulator_lib PROPERTIES OUTPUT_NAME "simulator") + +# If a main.cpp exists, create an executable that links the library. +if(EXISTS "${PROJECT_SOURCE_DIR}/src/main.cpp") + add_executable(simulator_exec "${PROJECT_SOURCE_DIR}/src/main.cpp") + target_link_libraries(simulator_exec PRIVATE simulator_lib) +endif() + + +if(TARGET simulator_exec) + add_custom_target(run + DEPENDS simulator_exec + COMMAND ${CMAKE_COMMAND} -E echo "Running simulator_exec..." + COMMAND $ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Builds and runs simulator_exec" + ) +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/ass2/simulator_SIC_XE/Makefile b/ass2/simulator_SIC_XE/Makefile new file mode 100644 index 0000000..93bc1f6 --- /dev/null +++ b/ass2/simulator_SIC_XE/Makefile @@ -0,0 +1,68 @@ +# Simple Makefile wrapper to configure, build and run the CMake project. +# Usage: +# make # configure + build with all cores +# make build # configure + build with all cores +# make all # clean + configure + build + run +# make run # just run the executable (no build) +# 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 +NPROC := $(shell nproc) + +.PHONY: all configure build run clean distclean + +# Default target: just build +default: build + +# make all: clean, build, then run +all: clean build run + +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 with $(NPROC) cores..." + $(CMAKE) --build $(BUILD_DIR) -j$(NPROC) + +# make run: just launch the executable (no build) +run: + @echo "Running primary target..." + # Prefer GUI if available, otherwise fall back to console executable + @if [ -x "$(GUI_TARGET)" ]; then \ + echo "Launching GUI: $(GUI_TARGET)"; \ + ./$(GUI_TARGET); \ + 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 + @echo "Removing target directory..." + -rm -rf target/ + +distclean: + @echo "Removing build artifacts and generated files..." + -rm -rf $(BUILD_DIR) CMakeFiles CMakeCache.txt cmake_install.cmake target/ diff --git a/ass2/simulator_SIC_XE/README.md b/ass2/simulator_SIC_XE/README.md new file mode 100644 index 0000000..aac6331 --- /dev/null +++ b/ass2/simulator_SIC_XE/README.md @@ -0,0 +1,55 @@ +# SIC/XE Simulator + +A complete SIC/XE architecture simulator with instruction execution, device I/O, and memory management. + +## Quick Start + +The easiest way to build and run the simulator: + +```bash +make run +``` + +This single command will: +- Configure the build system (if needed) +- Compile all source files +- Link the executable +- Run the simulator + +## Build Commands + +| Command | Description | +|--------------|----------------------------------------------------| +| `make` | Build the project | +| `make build` | Build the project | +| `make run` | Build run the simulator | +| `make clean` | Clean build artifacts | +| `make run` | Clean build artifacts, build and run the simulator | + + +## Project Structure + +``` +simulator_SIC_XE/ +├── include/ # Header files (.h) +├── src/ # Source files (.cpp) +├── target/bin/ # Build output (executables, libraries) +└── build/ # CMake build directory +``` + +## Features + +- **SIC/XE Architecture**: Complete register set (A, X, L, B, S, T, F, PC, SW) +- **Instruction Execution**: Format 1, 2, and 3/4 instruction support +- **Device I/O**: Input, output, and file device management +- **Memory Management**: 24-bit address space with proper bounds checking + +## Development + +The project uses CMake with a convenient Makefile wrapper. All build artifacts are placed in `target/bin/` for easy access. + +For manual CMake usage: +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j +``` diff --git a/ass2/simulator_SIC_XE/gui/qt/CMakeLists.txt b/ass2/simulator_SIC_XE/gui/qt/CMakeLists.txt new file mode 100644 index 0000000..cf8072a --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/gui/qt/MachineController.cpp b/ass2/simulator_SIC_XE/gui/qt/MachineController.cpp new file mode 100644 index 0000000..881e97d --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/MachineController.cpp @@ -0,0 +1,76 @@ +#include "MachineController.h" +#include "../../include/machine.h" +#include +#include + +using namespace std::chrono; + +MachineController::MachineController(std::shared_ptr machine, QObject *parent) + : QObject(parent), m_machine(std::move(machine)) +{ + if (!m_machine) { + m_machine = std::make_shared(); + } + m_lastUpdateTime = steady_clock::now(); +} + +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() { + const auto minUpdateInterval = milliseconds(16); + + while (m_running.load()) { + try { + if (m_machine) { + m_machine->execute(); + m_machine->tick(); + m_ticksSinceLastUpdate++; + + // Throttle GUI updates to 60 Hz + auto now = steady_clock::now(); + auto elapsed = duration_cast(now - m_lastUpdateTime); + + if (elapsed >= minUpdateInterval) { + emit tick(); + m_lastUpdateTime = now; + m_ticksSinceLastUpdate = 0; + } + + if (m_machine->isStopped()) { + emit tick(); + m_running.store(false); + break; + } + } + } catch (const std::exception &e) { + emit error(QString::fromStdString(e.what())); + // Stop on fatal error + m_running.store(false); + break; + } + } +} diff --git a/ass2/simulator_SIC_XE/gui/qt/MachineController.h b/ass2/simulator_SIC_XE/gui/qt/MachineController.h new file mode 100644 index 0000000..801b5f4 --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/MachineController.h @@ -0,0 +1,34 @@ +#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; + std::atomic m_ticksSinceLastUpdate{0}; + std::chrono::steady_clock::time_point m_lastUpdateTime; +}; + +#endif // MACHINECONTROLLER_H diff --git a/ass2/simulator_SIC_XE/gui/qt/main.cpp b/ass2/simulator_SIC_XE/gui/qt/main.cpp new file mode 100644 index 0000000..707d916 --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/main.cpp @@ -0,0 +1,14 @@ +#include +#include "mainwindow.h" +#include "../../include/opcode.h" + +int main(int argc, char **argv) { + loadInstructionSet(); + + qputenv("QT_QPA_PLATFORM", "xcb"); + + QApplication app(argc, argv); + MainWindow w; + w.show(); + return app.exec(); +} \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/gui/qt/mainwindow.cpp b/ass2/simulator_SIC_XE/gui/qt/mainwindow.cpp new file mode 100644 index 0000000..b53aa72 --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -0,0 +1,1030 @@ +#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 +#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, Qt::QueuedConnection); + connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay, Qt::QueuedConnection); + + 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, Qt::QueuedConnection); + + // Connect menu actions + connect(ui->actionLoad_Object_File, &QAction::triggered, this, &MainWindow::loadObjectFile); + connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::showAboutDialog); + connect(ui->actionFrequency, &QAction::triggered, this, &MainWindow::showFrequencyDialog); + + setupMemoryDisplay(); + setupDisassemblyDisplay(); + + loadInstructionSet(); + // Don't load any program by default - user will load via File menu + + 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] : "?"; + + // Check if this is a single-operand Format 2 instruction + QString mnem = result.mnemonic.toUpper(); + if (mnem == "CLEAR" || mnem == "TIXR") { + result.operand = reg1Str; + } else if (mnem == "SVC") { + result.operand = QString::number(r1); + } else if (mnem == "SHIFTL" || mnem == "SHIFTR") { + result.operand = QString("%1, %2").arg(reg1Str).arg(r2); + } else { + // Two register operands (ADDR, SUBR, COMPR, etc.) + 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 += (address + 3); + 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); + + // Save scroll position before updating + int scrollPos = ui->DisasemblyScrollArea->verticalScrollBar()->value(); + + ui->DisasemblyScrollArea->setWidget(container); + + // Restore scroll position after updating + ui->DisasemblyScrollArea->verticalScrollBar()->setValue(scrollPos); +} + +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); + + int scrollPos = ui->MemoryScrollArea->verticalScrollBar()->value(); + + ui->MemoryScrollArea->setWidget(container); + + ui->MemoryScrollArea->verticalScrollBar()->setValue(scrollPos); +} + +void MainWindow::loadObjectFile() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Load Object File"), + QString(), + tr("Object Files (*.obj);;All Files (*)")); + + if (fileName.isEmpty()) { + return; + } + + try { + // Stop execution if running + m_controller->stop(); + + // Reset machine state + m_machine->reset(); + + // Load the object file + Loader loader(m_machine, fileName.toStdString()); + loader.load(); + + // Update displays + updateRegisterDisplays(); + updateMemoryDisplay(); + updateDisassemblyDisplay(); + + QMessageBox::information(this, tr("Success"), + tr("Object file loaded successfully")); + } catch (const std::exception &e) { + QMessageBox::critical(this, tr("Error"), + tr("Failed to load object file: %1").arg(e.what())); + } +} + +void MainWindow::showAboutDialog() +{ + QMessageBox::about(this, tr("About SIC/XE Simulator"), + tr("SIC/XE Simulator\nby Zan Skvarca\n2025")); +} + +void MainWindow::showFrequencyDialog() +{ + if (!m_machine) return; + + QDialog dialog(this); + dialog.setWindowTitle(tr("Set Frequency")); + dialog.setModal(true); + + QVBoxLayout *layout = new QVBoxLayout(&dialog); + + QLabel *currentLabel = new QLabel(tr("Current frequency: %1 Hz").arg(m_machine->getSpeed()), &dialog); + layout->addWidget(currentLabel); + + QLabel *newLabel = new QLabel(tr("New frequency (Hz):"), &dialog); + layout->addWidget(newLabel); + + QLineEdit *freqInput = new QLineEdit(&dialog); + freqInput->setValidator(new QIntValidator(1, 1000000000, &dialog)); + freqInput->setText(QString::number(m_machine->getSpeed())); + layout->addWidget(freqInput); + + QHBoxLayout *buttonLayout = new QHBoxLayout(); + QPushButton *submitBtn = new QPushButton(tr("Submit"), &dialog); + QPushButton *cancelBtn = new QPushButton(tr("Cancel"), &dialog); + buttonLayout->addWidget(submitBtn); + buttonLayout->addWidget(cancelBtn); + layout->addLayout(buttonLayout); + + connect(submitBtn, &QPushButton::clicked, &dialog, &QDialog::accept); + connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject); + + if (dialog.exec() == QDialog::Accepted) { + bool ok; + int newFreq = freqInput->text().toInt(&ok); + if (ok && newFreq > 0) { + m_machine->setSpeed(newFreq); + QMessageBox::information(this, tr("Success"), + tr("Frequency set to %1 Hz").arg(newFreq)); + } + } +} diff --git a/ass2/simulator_SIC_XE/gui/qt/mainwindow.h b/ass2/simulator_SIC_XE/gui/qt/mainwindow.h new file mode 100644 index 0000000..2dba671 --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/mainwindow.h @@ -0,0 +1,85 @@ +#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(); + void loadObjectFile(); + void showAboutDialog(); + void showFrequencyDialog(); + +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/ass2/simulator_SIC_XE/gui/qt/mainwindow.ui b/ass2/simulator_SIC_XE/gui/qt/mainwindow.ui new file mode 100644 index 0000000..e9084bd --- /dev/null +++ b/ass2/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -0,0 +1,930 @@ + + + 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 + + + + | + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + 0 + 0 + 1172 + 20 + + + + + File + + + + + + Machine + + + + + + Help + + + + + + + + + + Load Object File + + + + + Frequency + + + + + About + + + + + + diff --git a/ass2/simulator_SIC_XE/include/constants.h b/ass2/simulator_SIC_XE/include/constants.h new file mode 100644 index 0000000..6f2195a --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/include/device.h b/ass2/simulator_SIC_XE/include/device.h new file mode 100644 index 0000000..6b5acb7 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/device.h @@ -0,0 +1,16 @@ +#ifndef DEVICE_H +#define DEVICE_H + + +class Device { +public: + Device(); + + bool test(); + virtual unsigned char read(); + virtual void write(unsigned char value); +}; + + + +#endif // DEVICE_H \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/include/file_device.h b/ass2/simulator_SIC_XE/include/file_device.h new file mode 100644 index 0000000..01b433c --- /dev/null +++ b/ass2/simulator_SIC_XE/include/file_device.h @@ -0,0 +1,23 @@ +#ifndef FILE_DEVICE_H +#define FILE_DEVICE_H + +#include "device.h" +#include +#include + +class FileDevice : public Device { +public: + explicit FileDevice(const std::string &filename); + ~FileDevice(); + unsigned char read() override; + void write(unsigned char value) override; + +private: + void ensureFileOpen(); + std::fstream fileStream; + std::string filename; + bool fileCreated; + std::streampos readPosition; +}; + +#endif // FILE_DEVICE_H \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/include/file_reader.h b/ass2/simulator_SIC_XE/include/file_reader.h new file mode 100644 index 0000000..87b8a17 --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/include/input_device.h b/ass2/simulator_SIC_XE/include/input_device.h new file mode 100644 index 0000000..40b01d3 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/input_device.h @@ -0,0 +1,18 @@ +#ifndef INPUT_DEVICE_H +#define INPUT_DEVICE_H + +#include "device.h" +#include + +class InputDevice : public Device { +public: + explicit InputDevice(std::istream &in); + ~InputDevice(); + + unsigned char read(); + +private: + std::istream &inStream; +}; + +#endif // INPUT_DEVICE_H \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/include/instructions.h b/ass2/simulator_SIC_XE/include/instructions.h new file mode 100644 index 0000000..5bef303 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/instructions.h @@ -0,0 +1,101 @@ +#ifndef INSTRUCTIONS_H +#define INSTRUCTIONS_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); +void shiftl_handler(Machine& m, int r1, int n); +void shiftr_handler(Machine& m, int r1, int n); +void subr_handler(Machine& m, int r1, int r2); +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/ass2/simulator_SIC_XE/include/loader.h b/ass2/simulator_SIC_XE/include/loader.h new file mode 100644 index 0000000..41899c2 --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/include/machine.h b/ass2/simulator_SIC_XE/include/machine.h new file mode 100644 index 0000000..cd14f74 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/machine.h @@ -0,0 +1,149 @@ +#ifndef MACHINE_H +#define MACHINE_H + +#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 "utils.h" + +using std::string; +using std::cerr; +using std::endl; +using std::cout; + + +class Machine { +public: + Machine(); + Machine(int speedHz) : Machine() { this->speedHz = speedHz; _instructionsTable = instructions; } + ~Machine(); + + int getA() const { return A; } + void setA(int value) { A = toSIC24(value); } + + int getB() const { return B; } + void setB(int value) { B = toSIC24(value); } + + int getX() const { return X; } + void setX(int value) { X = toSIC24(value); } + + int getL() const { return L; } + void setL(int value) { L = toSIC24(value); } + + int getS() const { return S; } + void setS(int value) { S = toSIC24(value); } + + int getT() const { return T; } + 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; } + + double getF() const { return F; } + void setF(double value) { F = value; } + + int getReg(int regNum) const; + void setReg(int regNum, int value); + + // Memory access methods + int getByte(int address); + void setByte(int address, int value); + + int getWord(int address); + void setWord(int address, int value); + + double getFloat(int address); + void setFloat(int address, double value); + + + // Device access methods + Device& getDevice(int num); + void setDevice(int num, std::shared_ptr device); + // 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(); + + // Execution and speed control + int getSpeed() const; + void setSpeed(int Hz); + void start(); + void stop(); + void tick(); + void halt(); + bool isStopped() const { return _stopped; } + void reset(); + + // error handling methods + void notImplemented(string mnemonic); + void invalidOpcode(int opcode); + void invalidAddressing(); + 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; + double F; + + // memory + unsigned char memory[MEMORY_SIZE]; + + // devices + std::vector> devices; + // fallback device returned when device slot is empty/invalid + Device fallbackDevice; + + // Execution control + std::atomic running{false}; + std::atomic speedHz{10}; // Default 10 Hz + + 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 +}; + + + +#endif // MACHINE_H \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/include/opcode.h b/ass2/simulator_SIC_XE/include/opcode.h new file mode 100644 index 0000000..a467488 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/opcode.h @@ -0,0 +1,116 @@ +#ifndef OPCODE_H +#define OPCODE_H + +#include "utils.h" + +// ============================== +// Opcode definitions (SIC/XE) +// ============================== +#define ADD 0x18 +#define ADDF 0x58 +#define ADDR 0x90 +#define AND 0x40 +#define CLEAR 0xB4 +#define COMP 0x28 +#define COMPF 0x88 +#define COMPR 0xA0 +#define DIV 0x24 +#define DIVF 0x64 +#define DIVR 0x9C +#define FIX 0xC4 +#define FLOAT 0xC0 +#define HIO 0xF4 +#define J 0x3C +#define JEQ 0x30 +#define JGT 0x34 +#define JLT 0x38 +#define JSUB 0x48 +#define LDA 0x00 +#define LDB 0x68 +#define LDCH 0x50 +#define LDF 0x70 +#define LDL 0x08 +#define LDS 0x6C +#define LDT 0x74 +#define LDX 0x04 +#define LPS 0xD0 +#define MUL 0x20 +#define MULF 0x60 +#define MULR 0x98 +#define NORM 0xC8 +#define OR 0x44 +#define RD 0xD8 +#define RMO 0xAC +#define RSUB 0x4C +#define SHIFTL 0xA4 +#define SHIFTR 0xA8 +#define SIO 0xF0 +#define SSK 0xEC +#define STA 0x0C +#define STB 0x78 +#define STCH 0x54 +#define STF 0x80 +#define STI 0xD4 +#define STL 0x14 +#define STS 0x7C +#define STSW 0xE8 +#define STT 0x84 +#define STX 0x10 +#define SUB 0x1C +#define SUBF 0x5C +#define SUBR 0x94 +#define SVC 0xB0 +#define TD 0xE0 +#define TIO 0xF8 +#define TIX 0x2C +#define TIXR 0xB8 +#define WD 0xDC + +// ============================== +// 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 + + + +enum class InstructionType { + TYPE1, + TYPE2, + TYPE3_4, + INVALID +}; + +class Machine; // forward + +// Store raw function pointer (void*) to allow different handler signatures +using RawHandler = void*; + +struct InstructionInfo { + const char* name; + InstructionType type; + RawHandler handler; +}; + +extern InstructionInfo instructions[]; +extern InstructionInfo instructionsEXEX[]; + +// Initialize the instruction table +void loadInstructionSet(); + +#endif // OPCODE_H diff --git a/ass2/simulator_SIC_XE/include/output_device.h b/ass2/simulator_SIC_XE/include/output_device.h new file mode 100644 index 0000000..94ba8b8 --- /dev/null +++ b/ass2/simulator_SIC_XE/include/output_device.h @@ -0,0 +1,18 @@ +#ifndef OUTPUT_DEVICE_H +#define OUTPUT_DEVICE_H + +#include "device.h" +#include + +class OutputDevice : public Device { +public: + explicit OutputDevice(std::ostream &out); + ~OutputDevice(); + + void write(unsigned char value) override; + +private: + std::ostream &outStream; +}; + +#endif // OUTPUT_DEVICE_H \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/include/reader.h b/ass2/simulator_SIC_XE/include/reader.h new file mode 100644 index 0000000..e582161 --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/include/string_reader.h b/ass2/simulator_SIC_XE/include/string_reader.h new file mode 100644 index 0000000..71c3da9 --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/include/utils.h b/ass2/simulator_SIC_XE/include/utils.h new file mode 100644 index 0000000..3197a95 --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/src/device.cpp b/ass2/simulator_SIC_XE/src/device.cpp new file mode 100644 index 0000000..87ab0d7 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/device.cpp @@ -0,0 +1,19 @@ +#include "device.h" + +Device::Device() +{ +} + +bool Device::test() +{ + return true; +} + +unsigned char Device::read() +{ + return 0; +} + +void Device::write(unsigned char value) +{ +} diff --git a/ass2/simulator_SIC_XE/src/file_device.cpp b/ass2/simulator_SIC_XE/src/file_device.cpp new file mode 100644 index 0000000..781a9b6 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/file_device.cpp @@ -0,0 +1,68 @@ +#include "file_device.h" +#include +#include + +FileDevice::FileDevice(const std::string &filename) + : filename(filename), fileCreated(false), readPosition(0) +{ +} + +FileDevice::~FileDevice() +{ + if (fileStream.is_open()) { + fileStream.close(); + } +} + +void FileDevice::ensureFileOpen() +{ + if (!fileStream.is_open()) { + // Check if file exists + std::ifstream checkFile(filename); + bool fileExists = checkFile.good(); + checkFile.close(); + + if (fileExists) { + fileStream.open(filename, std::ios::in | std::ios::out | std::ios::ate); + fileCreated = true; + } else { + // Create new file + std::ofstream create(filename); + if (!create) { + throw std::runtime_error("Failed to create file: " + filename); + } + create.close(); + fileCreated = true; + + fileStream.open(filename, std::ios::in | std::ios::out); + if (!fileStream.is_open()) { + throw std::runtime_error("Failed to open file after creating: " + filename); + } + } + } +} + +unsigned char FileDevice::read() +{ + unsigned char value = 0; + ensureFileOpen(); + if (fileStream.is_open()) { + fileStream.seekg(readPosition); + char ch; + if (fileStream.get(ch)) { + value = static_cast(ch); + readPosition = fileStream.tellg(); + } + } + return value; +} + +void FileDevice::write(unsigned char value) +{ + ensureFileOpen(); + if (fileStream.is_open()) { + fileStream.seekp(0, std::ios::end); + fileStream.put(static_cast(value)); + fileStream.flush(); + } +} \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/src/file_reader.cpp b/ass2/simulator_SIC_XE/src/file_reader.cpp new file mode 100644 index 0000000..2a800cc --- /dev/null +++ b/ass2/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/ass2/simulator_SIC_XE/src/input_device.cpp b/ass2/simulator_SIC_XE/src/input_device.cpp new file mode 100644 index 0000000..340c721 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/input_device.cpp @@ -0,0 +1,20 @@ +#include "input_device.h" + +InputDevice::InputDevice(std::istream &in) + : inStream(in) +{ +} + +InputDevice::~InputDevice() +{ +} + +unsigned char InputDevice::read() +{ + char c; + if (!inStream.get(c)) { + // If stream is at EOF or error, return 0 + return 0; + } + return static_cast(c); +} \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/src/instructions.cpp b/ass2/simulator_SIC_XE/src/instructions.cpp new file mode 100644 index 0000000..a1c4e55 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/instructions.cpp @@ -0,0 +1,603 @@ +#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; + } +} + + +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)); +} + +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) { + + if (m.getReg(r2) == 0) { + m.invalidOpcode(DIVR); + return; + } + m.setReg(r2, m.getReg(r2) / m.getReg(r1)); +} + +void mulr_handler(Machine &m, int r1, int r2) +{ + m.setReg(r2, m.getReg(r1) * m.getReg(r2)); +} + +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) +{ + 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) +{ + 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)); +} + +// TODO: implement SVC functionality +void svc_handler(Machine &m, int n, int unused) +{ + m.notImplemented("SVC"); +} + +void tixr_handler(Machine &m, int r1, int unused) +{ + m.setX(m.getX() + 1); + int valX = m.getX(); + 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); + + int instrSize = 3; + int instrAddr = m.getPC() - instrSize; + // Check if jumping to itself (halt pattern) + if (target == instrAddr) { + m.halt(); + } + + 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; + if (mode == AddressingMode::IMMEDIATE) { + val = ea & 0xFF; + } else if (mode == AddressingMode::INDIRECT) { + val = m.getByte(m.getWord(ea)); + } else { + val = m.getByte(ea); + } + 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/ass2/simulator_SIC_XE/src/loader.cpp b/ass2/simulator_SIC_XE/src/loader.cpp new file mode 100644 index 0000000..8d3aa17 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/loader.cpp @@ -0,0 +1,134 @@ +#include "loader.h" +#include "file_reader.h" +#include "string_reader.h" +#include "machine.h" +#include "constants.h" +#include +#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) + std::string lengthStr = _file_reader->readString(2); + int length = std::stoi(lengthStr, nullptr, 16); + + // Read the rest of the line (data bytes with spaces) + std::string dataLine = _file_reader->readLine(); + + // Remove all whitespace from the data line + dataLine.erase(std::remove_if(dataLine.begin(), dataLine.end(), ::isspace), dataLine.end()); + + // Now use StringReader to parse the hex bytes + StringReader stringReader(dataLine); + record.data.resize(length); + + for (int i = 0; i < length; ++i) { + std::string byteHex = stringReader.readString(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/ass2/simulator_SIC_XE/src/machine.cpp b/ass2/simulator_SIC_XE/src/machine.cpp new file mode 100644 index 0000000..d8d0356 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/machine.cpp @@ -0,0 +1,525 @@ +#include "machine.h" + +#include + +#include "opcode.h" +#include "instructions.h" +#include +#include + +using std::make_shared; + +string prefix = "Machine error: "; + + +Machine::Machine() +{ + // Initialize registers and memory to zero + A = B = X = L = S = T = PC = SW = 0; + F = 0.0; + for (int i = 0; i < MEMORY_SIZE; i++) { + memory[i] = 0; + } + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + VA[i] = VS[i] = VT[i] = 0; + } + _stopped = false; + + devices.resize(NUM_DEVICES); + // device 0: standard input + devices[0] = make_shared(std::cin); + // device 1: standard output + devices[1] = make_shared(std::cout); + + // Initialize devices >= 2 as FileDevice with hex names in devices directory + for (int i = 2; i < NUM_DEVICES; i++) { + char hex[3]; + snprintf(hex, sizeof(hex), "%02X", i); + std::string filename = "devices/" + std::string(hex) + ".dev"; + try { + devices[i] = std::make_shared(filename); + } catch (const std::exception &e) { + cerr << prefix << "Warning: Failed to initialize FileDevice for device " << i << ": " << e.what() << endl; + } + } + + _exex_mode = false; + _instructionsTable = instructions; +} + +Machine::~Machine() +{ + for (auto& device : devices) { + device.reset(); + } +} + +int Machine::getSpeed() const +{ + return speedHz.load(); +} + +void Machine::setSpeed(int Hz) +{ + speedHz.store(Hz); +} + +// TODO: implement errors +void Machine::notImplemented(string mnemonic) +{ + cout << prefix << "Not implemented: " << mnemonic << endl; +} + +void Machine::invalidOpcode(int opcode) +{ + cout << prefix << "Invalid opcode: " << opcode << endl; +} + + +void Machine::invalidAddressing() +{ + cout << prefix << "Invalid addressing mode" << endl; +} + +void Machine::divisionByZero(int opcode) +{ + cout << prefix << "Division by zero error in opcode: " << opcode << endl; +} + +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 = speedHz.load(); + if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick"); + + const auto delay = std::chrono::milliseconds(1000 / speed); + std::this_thread::sleep_for(delay); +} + +void Machine::halt() +{ + _stopped = true; +} + +void Machine::reset() +{ + // Reset all registers + A = B = X = L = S = T = PC = SW = 0; + F = 0.0; + + // Clear memory + for (int i = 0; i < MEMORY_SIZE; i++) { + memory[i] = 0; + } + + // Reset execution state + _stopped = false; + running.store(false); + + // Reset vector registers + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + VA[i] = VS[i] = VT[i] = 0; + } +} + +int Machine::getReg(int regNum) const +{ + switch (regNum) { + case 0: return A; + case 1: return X; + case 2: return L; + case 3: return B; + case 4: return S; + case 5: return T; + case 6: return F; + case 8: return PC; + case 9: return SW; + default: + cerr << prefix << "Invalid register number: " << regNum << endl; + return -1; + } +} + +// TODO: handle double for F register +void Machine::setReg(int regNum, int value) +{ + value = toSIC24(value); + switch (regNum) { + case 0: A = value; break; + case 1: X = value; break; + case 2: L = value; break; + case 3: B = value; break; + case 4: S = value; break; + case 5: T = value; break; + case 6: F = value; break; + case 8: PC = value; break; + case 9: SW = value; break; + default: + cerr << prefix << "Invalid register number: " << regNum << endl; + break; + } +} + +int Machine::getByte(int address) +{ + if (address < 0 || address >= MEMORY_SIZE) { + cerr << prefix << "Invalid memory address: " << address << endl; + return -1; + } + return static_cast(memory[address]); +} + +void Machine::setByte(int address, int value) +{ + if(address < 0 || address >= MEMORY_SIZE) { + cerr << prefix << "Invalid memory address: " << address << endl; + return; + } + + memory[address] = static_cast(value); +} + +// Assuming word is 3 bytes + +int Machine::getWord(int address) +{ + if (address < 0 || address + 2 >= MEMORY_SIZE) { + cerr << prefix << "Invalid memory address: " << address << endl; + return -1; + } + // Big-endian: high byte first + return (static_cast(memory[address]) << 16) | (static_cast(memory[address + 1]) << 8) | static_cast(memory[address + 2]); +} + +// Assuming word is 3 bytes +void Machine::setWord(int address, int value) +{ + if(address < 0 || address + 2 >= MEMORY_SIZE) { + cerr << prefix << "Invalid memory address: " << address << endl; + return; + } + value &= 0xFFFFFF; + + // Big-endian: high byte first + memory[address] = static_cast((value >> 16) & 0xFF); + memory[address + 1] = static_cast((value >> 8) & 0xFF); + memory[address + 2] = static_cast(value & 0xFF); +} + +double Machine::getFloat(int address) +{ + if (address < 0 || address + 5 >= MEMORY_SIZE) { + cerr << prefix << "Invalid float address: " << address << endl; + return 0.0; + } + + // load 6 bytes, big-endian → 48-bit word + unsigned long long raw = + ((unsigned long long)memory[address] << 40) | + ((unsigned long long)memory[address+1] << 32) | + ((unsigned long long)memory[address+2] << 24) | + ((unsigned long long)memory[address+3] << 16) | + ((unsigned long long)memory[address+4] << 8) | + (unsigned long long)memory[address+5]; + + 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) +{ + 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 big-endian + memory[address] = (unsigned char)((raw >> 40) & 0xFF); + memory[address+1] = (unsigned char)((raw >> 32) & 0xFF); + memory[address+2] = (unsigned char)((raw >> 24) & 0xFF); + memory[address+3] = (unsigned char)((raw >> 16) & 0xFF); + memory[address+4] = (unsigned char)((raw >> 8) & 0xFF); + memory[address+5] = (unsigned char)( raw & 0xFF); +} + + + +Device &Machine::getDevice(int num) +{ + if(num < 0 || num >= static_cast(devices.size()) || !devices[num]) { + cerr << prefix << "Invalid device number: " << num << endl; + return fallbackDevice; + } + return *devices[num]; +} + +void Machine::setDevice(int num, std::shared_ptr device) +{ + if(num < 0 || num >= NUM_DEVICES) { + cerr << prefix << "Invalid device number: " << num << endl; + return; + } + if(static_cast(devices.size()) != NUM_DEVICES) { + devices.resize(NUM_DEVICES); + } + // Enforce: devices with index >= 2 must be FileDevice instances + if (num >= 2) { + // try dynamic cast + if (std::dynamic_pointer_cast(device) == nullptr) { + cerr << prefix << "Device at index " << num << " must be a FileDevice." << endl; + return; + } + } + devices[num] = device; +} + +void Machine::setFileDevice(int num, const std::string &filename) +{ + if(num < 0 || num >= NUM_DEVICES) { + cerr << prefix << "Invalid device number: " << num << endl; + return; + } + if(static_cast(devices.size()) != NUM_DEVICES) { + devices.resize(NUM_DEVICES); + } + try { + devices[num] = std::make_shared(filename); + } catch (const std::exception &e) { + cerr << prefix << "Failed to create FileDevice for index " << num << ": " << e.what() << endl; + } +} + +int Machine::fetch() +{ + return getByte(PC++); +} + +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) +{ + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); + handler(*this); + return true; + } + undefinedHandler(opcode); + return false; +} + +bool Machine::execF2(int opcode, int operand) +{ + int r1 = (operand >> 4) & 0xF; + int r2 = operand & 0xF; + + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); + handler(*this, r1, r2); + return true; + } + undefinedHandler(opcode); + return false; +} + + +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/ass2/simulator_SIC_XE/src/main.cpp b/ass2/simulator_SIC_XE/src/main.cpp new file mode 100644 index 0000000..433a3f1 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/main.cpp @@ -0,0 +1,140 @@ +#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; + + + 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) + "rec.obj"); + loader.load(); + + cout << "=== Starting execution of rec.obj ===" << endl; + cout << "Initial PC: 0x" << std::hex << machine->getPC() << std::dec << endl; + + int maxSteps = 10000; + for (int step = 0; step < maxSteps; step++) { + int pc = machine->getPC(); + int opcode = machine->getByte(pc) & 0xFC; + const char* instName = (opcode < 256 && instructions[opcode].type != InstructionType::INVALID) + ? instructions[opcode].name : "???"; + + int regA = machine->getA(); + int regB = machine->getB(); + int regX = machine->getX(); + int regL = machine->getL(); + int sw = machine->getSW(); + + printf("Step %4d: PC=0x%05X %-6s A=0x%06X B=0x%06X X=0x%06X L=0x%06X SW=0x%06X\n", + step, pc, instName, regA, regB, regX, regL, sw); + + machine->execute(); + + if (machine->isStopped()) { + cout << "Machine halted at step " << step << endl; + break; + } + + if (step > 100 && machine->getPC() == pc) { + cout << "ERROR: Infinite loop detected at PC=0x" << std::hex << pc << std::dec << endl; + break; + } + } + + cout << "\n=== Final state ===" << endl; + cout << "A: " << machine->getA() << endl; + cout << "B: " << machine->getB() << endl; + cout << "X: " << machine->getX() << endl; + + + return 0; +} \ No newline at end of file diff --git a/ass2/simulator_SIC_XE/src/opcode.cpp b/ass2/simulator_SIC_XE/src/opcode.cpp new file mode 100644 index 0000000..0b63ce8 --- /dev/null +++ b/ass2/simulator_SIC_XE/src/opcode.cpp @@ -0,0 +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, 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)}; + + // 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)}; + } + // 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/ass2/simulator_SIC_XE/src/output_device.cpp b/ass2/simulator_SIC_XE/src/output_device.cpp new file mode 100644 index 0000000..ce7b4ec --- /dev/null +++ b/ass2/simulator_SIC_XE/src/output_device.cpp @@ -0,0 +1,16 @@ +#include "output_device.h" + +OutputDevice::OutputDevice(std::ostream &out) + : outStream(out) +{ +} + +OutputDevice::~OutputDevice() +{ +} + +void OutputDevice::write(unsigned char value) +{ + outStream.put(static_cast(value)); + outStream.flush(); +} diff --git a/ass2/simulator_SIC_XE/src/string_reader.cpp b/ass2/simulator_SIC_XE/src/string_reader.cpp new file mode 100644 index 0000000..b87cc02 --- /dev/null +++ b/ass2/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; +}