This commit is contained in:
zanostro 2025-12-07 11:27:01 +01:00
parent 18a14d204c
commit 717568b6d0
30 changed files with 4093 additions and 209 deletions

View file

@ -7,7 +7,6 @@ CMakeCache.txt
CMakeFiles/
CMakeScripts/
cmake_install.cmake
Makefile
*.cmake
!CMakeLists.txt
@ -71,6 +70,7 @@ xcuserdata/
*.moc.cpp
*.qm
*.prl
CMakeLists.txt.user
# OS generated files
.DS_Store

View file

@ -4,7 +4,7 @@ project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Put all build outputs under target/bin as requested
# Put all build outputs under target/bin
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
@ -28,8 +28,7 @@ if(EXISTS "${PROJECT_SOURCE_DIR}/src/main.cpp")
target_link_libraries(simulator_exec PRIVATE simulator_lib)
endif()
# Convenience target: `cmake --build build --target run`
# This target will build `simulator_exec` (if present) and then execute it.
if(TARGET simulator_exec)
add_custom_target(run
DEPENDS simulator_exec
@ -43,3 +42,19 @@ endif()
message(STATUS "Project: ${PROJECT_NAME}")
message(STATUS "Sources found: ${SOURCES}")
message(STATUS "Output directory: ${OUTPUT_DIR}")
if(EXISTS "${CMAKE_SOURCE_DIR}/gui/qt/CMakeLists.txt")
add_subdirectory(gui/qt)
endif()
# Copy resources directory (if present) to target/res so build output includes them
if(EXISTS "${CMAKE_SOURCE_DIR}/res")
add_custom_target(copy_resources
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/target/res
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res ${CMAKE_SOURCE_DIR}/target/res
COMMENT "Copying resources from res/ to target/res/"
)
if(TARGET simulator_exec)
add_dependencies(simulator_exec copy_resources)
endif()
endif()

68
simulator_SIC_XE/Makefile Normal file
View file

@ -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/

View file

@ -19,10 +19,13 @@ This single command will:
## Build Commands
| Command | Description |
|---------|-------------|
|--------------|----------------------------------------------------|
| `make` | Build the project |
| `make run` | Build and run the simulator |
| `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

View file

@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.16)
project(simulator_qt LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# Prefer Qt6, fall back to Qt5
find_package(Qt6 COMPONENTS Widgets QUIET)
if(NOT Qt6_FOUND)
# Try explicitly the system Qt6 cmake prefix on Debian/Ubuntu
find_package(Qt6 COMPONENTS Widgets QUIET PATHS /usr/lib/x86_64-linux-gnu)
endif()
if(NOT Qt6_FOUND)
# Fallback: try Qt5 if Qt6 is unavailable
find_package(Qt5 COMPONENTS Widgets QUIET)
endif()
if(Qt6_FOUND)
set(QT_LIB Qt6::Widgets)
elseif(Qt5_FOUND)
set(QT_LIB Qt5::Widgets)
else()
message(FATAL_ERROR "Qt6 or Qt5 not found. Install Qt development packages or set CMAKE_PREFIX_PATH to your Qt installation.")
endif()
set(GUI_SRCS
main.cpp
mainwindow.cpp
MachineController.cpp
)
set(GUI_HDRS
mainwindow.h
MachineController.h
)
add_executable(simulator_qt ${GUI_SRCS} ${GUI_HDRS})
# Allow the generated UI headers (from AUTOUIC) to be found in the build dir
# and also include the top-level include folder (works when added with add_subdirectory)
target_include_directories(simulator_qt PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/include)
# Link to core library target (must be defined by top-level CMake)
target_link_libraries(simulator_qt PRIVATE simulator_lib ${QT_LIB})
# Place runtime binary under repo/target/bin to match project layout
set_target_properties(simulator_qt PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/target/bin
)

View file

@ -0,0 +1,76 @@
#include "MachineController.h"
#include "../../include/machine.h"
#include <chrono>
#include <QDebug>
using namespace std::chrono;
MachineController::MachineController(std::shared_ptr<Machine> machine, QObject *parent)
: QObject(parent), m_machine(std::move(machine))
{
if (!m_machine) {
m_machine = std::make_shared<Machine>();
}
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<milliseconds>(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;
}
}
}

View file

@ -0,0 +1,34 @@
#ifndef MACHINECONTROLLER_H
#define MACHINECONTROLLER_H
#include <QObject>
#include <atomic>
#include <thread>
#include <memory>
class Machine;
class MachineController : public QObject {
Q_OBJECT
public:
explicit MachineController(std::shared_ptr<Machine> machine = nullptr, QObject *parent = nullptr);
~MachineController() override;
void start();
void stop();
void step();
signals:
void tick();
void error(const QString &msg);
private:
void runLoop();
std::atomic<bool> m_running{false};
std::thread m_thread;
std::shared_ptr<Machine> m_machine;
std::atomic<int> m_ticksSinceLastUpdate{0};
std::chrono::steady_clock::time_point m_lastUpdateTime;
};
#endif // MACHINECONTROLLER_H

View file

@ -0,0 +1,14 @@
#include <QApplication>
#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();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <memory>
class MachineController;
class Machine;
class QLineEdit;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
std::shared_ptr<Machine> machine() const { return m_machine; }
MachineController* controller() const { return m_controller.get(); }
void startExecution();
void stopExecution();
void stepExecution();
void setTestRegisterValues();
private slots:
void updateRegisterDisplays();
void updateMemoryDisplay();
void updateDisassemblyDisplay();
void onRegisterFieldChanged();
void onMemoryInc256();
void onMemoryInc4096();
void onMemoryInc65536();
void onMemoryDec256();
void onMemoryDec4096();
void onMemoryDec65536();
void onMemoryGoToStart();
void onMemoryGoToEnd();
void onDisassemblyInc();
void onDisassemblyInc16();
void onDisassemblyInc256();
void onDisassemblyDec();
void onDisassemblyDec16();
void onDisassemblyDec256();
void onDisassemblyGoToStart();
void onDisassemblyGoToEnd();
void loadObjectFile();
void showAboutDialog();
void showFrequencyDialog();
private:
Ui::MainWindow *ui;
std::shared_ptr<Machine> m_machine;
std::unique_ptr<MachineController> m_controller;
int m_memoryOffset = 0;
int m_disassemblyOffset = 0;
void connectRegisterFields();
void updateSingleRegisterDisplay(const QString& fieldName, int value);
void updateAllFormatsForRegister(const QString& regPrefix, int value);
void updateFloatRegisterFormats(const QString& regPrefix, double value);
void handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName);
void loadDemoProgram();
void setupMemoryDisplay();
void setupDisassemblyDisplay();
struct DisassembledInstruction {
int address;
int size;
QString mnemonic;
QString operand;
int effectiveAddr;
bool isImmediate;
bool isIndirect;
};
DisassembledInstruction disassembleAt(int address);
};
#endif // MAINWINDOW_H

View file

@ -0,0 +1,930 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1172</width>
<height>649</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>431</width>
<height>601</height>
</rect>
</property>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>0</x>
<y>80</y>
<width>431</width>
<height>321</height>
</rect>
</property>
<property name="title">
<string>Register values</string>
</property>
<widget class="QLineEdit" name="regA_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>40</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg A</string>
</property>
</widget>
<widget class="QLineEdit" name="regA_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>40</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regA_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>40</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>110</x>
<y>20</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Bin</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>230</x>
<y>20</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Hex</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>350</x>
<y>20</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Dec</string>
</property>
</widget>
<widget class="QLineEdit" name="regB_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>70</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg B</string>
</property>
</widget>
<widget class="QLineEdit" name="regB_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>70</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regB_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>70</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regX_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>100</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg X</string>
</property>
</widget>
<widget class="QLineEdit" name="regX_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>100</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regX_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>100</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regS_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>130</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg S</string>
</property>
</widget>
<widget class="QLineEdit" name="regS_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>130</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regS_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>130</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regT_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>160</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg T</string>
</property>
</widget>
<widget class="QLineEdit" name="regT_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>160</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regT_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>160</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regL_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>190</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regL_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>190</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_9">
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg L</string>
</property>
</widget>
<widget class="QLineEdit" name="regL_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>190</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>10</x>
<y>220</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg PC</string>
</property>
</widget>
<widget class="QLineEdit" name="regPC_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>220</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regPC_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>220</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regPC_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>220</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_11">
<property name="geometry">
<rect>
<x>10</x>
<y>249</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg SW</string>
</property>
</widget>
<widget class="QLineEdit" name="regSW_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>249</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regSW_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>249</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regSW_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>249</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_12">
<property name="geometry">
<rect>
<x>10</x>
<y>280</y>
<width>57</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Reg L</string>
</property>
</widget>
<widget class="QLineEdit" name="regF_dec_field">
<property name="geometry">
<rect>
<x>310</x>
<y>280</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regF_bin_field">
<property name="geometry">
<rect>
<x>70</x>
<y>280</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="regF_hex_field">
<property name="geometry">
<rect>
<x>190</x>
<y>280</y>
<width>113</width>
<height>23</height>
</rect>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>431</width>
<height>81</height>
</rect>
</property>
<property name="title">
<string>Control</string>
</property>
<widget class="QPushButton" name="StartBtn">
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Start</string>
</property>
</widget>
<widget class="QPushButton" name="StopBtn">
<property name="geometry">
<rect>
<x>170</x>
<y>40</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Stop</string>
</property>
</widget>
<widget class="QPushButton" name="StepBtn">
<property name="geometry">
<rect>
<x>310</x>
<y>40</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Step</string>
</property>
</widget>
</widget>
</widget>
<widget class="QWidget" name="widget_2" native="true">
<property name="geometry">
<rect>
<x>450</x>
<y>0</y>
<width>721</width>
<height>601</height>
</rect>
</property>
<widget class="QGroupBox" name="MemorygroupBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>291</height>
</rect>
</property>
<property name="title">
<string>Memory</string>
</property>
<widget class="QScrollArea" name="MemoryScrollArea">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>711</width>
<height>221</height>
</rect>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>709</width>
<height>219</height>
</rect>
</property>
</widget>
</widget>
<widget class="QPushButton" name="MemoryInc4096Btn">
<property name="geometry">
<rect>
<x>450</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryInc256Btn">
<property name="geometry">
<rect>
<x>370</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryInc65536Btn">
<property name="geometry">
<rect>
<x>530</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;&gt;&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryDec256Btn">
<property name="geometry">
<rect>
<x>290</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryDec4096Btn">
<property name="geometry">
<rect>
<x>210</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryDec65536Btn">
<property name="geometry">
<rect>
<x>130</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>&lt;&lt;&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryGoToStart_2">
<property name="geometry">
<rect>
<x>50</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>O</string>
</property>
</widget>
<widget class="QPushButton" name="MemoryGoToEnd">
<property name="geometry">
<rect>
<x>610</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>|</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="MemorygroupBox_3">
<property name="geometry">
<rect>
<x>0</x>
<y>300</y>
<width>711</width>
<height>301</height>
</rect>
</property>
<property name="title">
<string>Disasembly</string>
</property>
<widget class="QScrollArea" name="DisasemblyScrollArea">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>711</width>
<height>221</height>
</rect>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>709</width>
<height>219</height>
</rect>
</property>
</widget>
</widget>
<widget class="QPushButton" name="DisasmInc4096Btn">
<property name="geometry">
<rect>
<x>440</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmInc256Btn">
<property name="geometry">
<rect>
<x>360</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmInc65536Btn">
<property name="geometry">
<rect>
<x>520</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&gt;&gt;&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmDec256Btn">
<property name="geometry">
<rect>
<x>280</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmDec4096Btn">
<property name="geometry">
<rect>
<x>200</x>
<y>250</y>
<width>71</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmDec65536Btn">
<property name="geometry">
<rect>
<x>120</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>&lt;&lt;&lt;</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmGoToStart">
<property name="geometry">
<rect>
<x>40</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>O</string>
</property>
</widget>
<widget class="QPushButton" name="DisasmGoToEnd">
<property name="geometry">
<rect>
<x>600</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>|</string>
</property>
</widget>
</widget>
</widget>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1172</width>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLoad_Object_File"/>
</widget>
<widget class="QMenu" name="menuMachine">
<property name="title">
<string>Machine</string>
</property>
<addaction name="actionFrequency"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuMachine"/>
<addaction name="menuHelp"/>
</widget>
<action name="actionLoad_Object_File">
<property name="text">
<string>Load Object File</string>
</property>
</action>
<action name="actionFrequency">
<property name="text">
<string>Frequency</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,52 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <string>
// ==============================
// SIC/XE Architecture Constants
// ==============================
// Memory and system constants
constexpr int MEMORY_SIZE = 1 << 20; // 1 MB memory
constexpr int NUM_DEVICES = 256;
constexpr int WORD_SIZE = 24;
constexpr int WORD_MASK = 0xFFFFFF;
// SIC/XE floating point constants
constexpr int SICF_BITS = 48;
constexpr int SICF_FRAC_BITS = 40;
constexpr int SICF_EXP_BITS = 7;
constexpr int SICF_EXP_BIAS = 64;
constexpr unsigned long long SICF_FRAC_MASK = (1ULL << SICF_FRAC_BITS) - 1;
// SW register condition codes
constexpr int CC_LT = 0x0; // 00
constexpr int CC_EQ = 0x1; // 01
constexpr int CC_GT = 0x2; // 10
constexpr int CC_MASK = 0x3; // mask for 2 bits
// Instruction format bit masks
constexpr int TYPE3_4_SIC_MASK = 0xFC;
constexpr int NI_MASK = 0x03; // mask for n and i bits
constexpr int NI_SIC = 0x0;
constexpr int BP_BASE_REL_MASK = 0b10;
constexpr int BP_PC_REL_MASK = 0b01;
constexpr int BP_DIRECT_MASK = 0b00;
constexpr int BIT_E_MASK = 0x10; // mask for e bit in F4 and F3 instructions
//SIC/XE/XE
constexpr bool USE_EXTENDED_MODE = true;
constexpr int VECTOR_REG_SIZE = 4;
/* if structure is
/target/
|-> bin/simulator_exec
|-> res/
*/
// When running from project root (./target/bin/simulator_exec), resources are in ./target/res/
constexpr char PATH_RESOURCES[] = "./target/res/";
constexpr bool FILE_CONTAINS_WHITE_SPACES = true;
#endif // CONSTANTS_H

View file

@ -13,7 +13,11 @@ public:
void write(unsigned char value) override;
private:
void ensureFileOpen();
std::fstream fileStream;
std::string filename;
bool fileCreated;
std::streampos readPosition;
};
#endif // FILE_DEVICE_H

View file

@ -0,0 +1,25 @@
#ifndef FILE_READER_H
#define FILE_READER_H
#include "reader.h"
#include <string>
#include <fstream>
class FileReader : public Reader {
public:
explicit FileReader(const std::string &path, std::ios::openmode m = std::ios::binary);
~FileReader() override;
int readByte() override;
bool readBytes(uint8_t* buf, size_t len) override;
std::string readString(size_t len) override;
std::string readLine() override;
bool good() const;
private:
std::ifstream in;
};
#endif // FILE_READER_H

View file

@ -1,11 +1,26 @@
#ifndef INSTRUCTIONS_H
#define INSTRUCTIONS_H
#include "opcode.h"
#include "utils.h"
class Machine; // forward declaration
// Type 1 instruction handlers
void fix_handler(Machine& m);
void float_handler(Machine& m);
void hio_handler(Machine& m);
void norm_handler(Machine& m);
void sio_handler(Machine& m);
void tio_handler(Machine& m);
void nop_handler(Machine& m);
/* IDEJE ZA SIC_XE_XE :)*/
// void nop(Machine& m);
// Type 2 instruction handlers
void addr_handler(Machine& m, int r1, int r2);
void clear_handler(Machine& m, int r, int unused);
void compr_handler(Machine& m, int r1, int r2);
void divr_handler(Machine& m, int r1, int r2);
void mulr_handler(Machine& m, int r1, int r2);
void rmo_handler(Machine& m, int r1, int r2);
@ -16,5 +31,71 @@ void svc_handler(Machine& m, int n, int unused);
void tixr_handler(Machine& m, int r1, int unused);
// Type 3/4 instruction handlers
void add_handler(Machine& m, int ea, AddressingMode mode);
void addf_handler(Machine& m, int ea, AddressingMode mode);
void and_handler(Machine& m, int ea, AddressingMode mode);
void comp_handler(Machine& m, int ea, AddressingMode mode);
void compf_handler(Machine& m, int ea, AddressingMode mode);
void div_handler(Machine& m, int ea, AddressingMode mode);
void divf_handler(Machine& m, int ea, AddressingMode mode);
void j_handler(Machine& m, int ea, AddressingMode mode);
void jeq_handler(Machine& m, int ea, AddressingMode mode);
void jgt_handler(Machine& m, int ea, AddressingMode mode);
void jlt_handler(Machine& m, int ea, AddressingMode mode);
void jsub_handler(Machine& m, int ea, AddressingMode mode);
void lda_handler(Machine& m, int ea, AddressingMode mode);
void ldb_handler(Machine& m, int ea, AddressingMode mode);
void ldch_handler(Machine& m, int ea, AddressingMode mode);
void ldf_handler(Machine& m, int ea, AddressingMode mode);
void ldl_handler(Machine& m, int ea, AddressingMode mode);
void lds_handler(Machine& m, int ea, AddressingMode mode);
void ldt_handler(Machine& m, int ea, AddressingMode mode);
void ldx_handler(Machine& m, int ea, AddressingMode mode);
void lps_handler(Machine& m, int ea, AddressingMode mode);
void mul_handler(Machine& m, int ea, AddressingMode mode);
void mulf_handler(Machine& m, int ea, AddressingMode mode);
void or_handler(Machine& m, int ea, AddressingMode mode);
void rd_handler(Machine& m, int ea, AddressingMode mode);
void rsub_handler(Machine& m, int ea, AddressingMode mode);
void ssk_handler(Machine& m, int ea, AddressingMode mode);
void sta_handler(Machine& m, int ea, AddressingMode mode);
void stb_handler(Machine& m, int ea, AddressingMode mode);
void stch_handler(Machine& m, int ea, AddressingMode mode);
void stf_handler(Machine& m, int ea, AddressingMode mode);
void sti_handler(Machine& m, int ea, AddressingMode mode);
void stl_handler(Machine& m, int ea, AddressingMode mode);
void sts_handler(Machine& m, int ea, AddressingMode mode);
void stsw_handler(Machine& m, int ea, AddressingMode mode);
void stt_handler(Machine& m, int ea, AddressingMode mode);
void stx_handler(Machine& m, int ea, AddressingMode mode);
void sub_handler(Machine& m, int ea, AddressingMode mode);
void subf_handler(Machine& m, int ea, AddressingMode mode);
void td_handler(Machine& m, int ea, AddressingMode mode);
void tix_handler(Machine& m, int ea, AddressingMode mode);
void wd_handler(Machine& m, int ea, AddressingMode mode);
// SIC/XE/XE Extended instruction handlers
void xexe_handler(Machine& m);
void halt_handler(Machine& m);
void nop_handler(Machine& m);
void vaddr_handler(Machine& m, int r1, int r2);
void vsubr_handler(Machine& m, int r1, int r2);
void vmulr_handler(Machine& m, int r1, int r2);
void vdivr_handler(Machine& m, int r1, int r2);
void vadd_handler(Machine& m, int ea, AddressingMode mode);
void vsub_handler(Machine& m, int ea, AddressingMode mode);
void vmul_handler(Machine& m, int ea, AddressingMode mode);
void vdiv_handler(Machine& m, int ea, AddressingMode mode);
void stva_handler(Machine& m, int ea, AddressingMode mode);
void stvs_handler(Machine& m, int ea, AddressingMode mode);
void stvt_handler(Machine& m, int ea, AddressingMode mode);
void ldva_handler(Machine& m, int ea, AddressingMode mode);
void ldvs_handler(Machine& m, int ea, AddressingMode mode);
void ldvt_handler(Machine& m, int ea, AddressingMode mode);
#endif // INSTRUCTIONS_H

View file

@ -0,0 +1,68 @@
#ifndef LOADER_H
#define LOADER_H
#include <memory>
#include <string>
#include <vector>
#include "file_reader.h"
#include <stdexcept>
class Machine;
using std::shared_ptr;
using std::string;
class Loader {
public:
Loader( shared_ptr<Machine> machine, string filename) : _machine(machine), _filename(filename) {
_file_reader = std::make_shared<FileReader>(filename, std::ios::in);
if (!_file_reader->good()) {
throw std::runtime_error("Loader: failed to open file: " + filename);
}
}
~Loader();
enum class RecordType {
HEADER,
TEXT,
END,
UNKNOWN
};
struct HeaderMetadata {
string program_name;
int start_address;
int length;
};
struct TextRecord {
int start_address;
std::vector<uint8_t> data;
};
struct EndRecord {
int execution_start_address;
};
void load();
private :
static RecordType parseRecordType(char c);
shared_ptr<Machine> _machine;
string _filename;
shared_ptr<FileReader> _file_reader;
HeaderMetadata readHeader();
TextRecord readTextRecord();
EndRecord readEndRecord();
bool load_into_memory(int start_address, const std::vector<uint8_t>& data);
};
#endif // LOADER_H

View file

@ -4,18 +4,18 @@
#include <string>
#include <iostream>
#include <vector>
#include <memory>
#include <atomic>
#include <mutex>
#include "constants.h"
#include "device.h"
#include "input_device.h"
#include "output_device.h"
#include "file_device.h"
#include "opcode.h"
#include <memory>
#define MEMORY_SIZE 65536
#define NUM_DEVICES 256
#include "utils.h"
using std::string;
using std::cerr;
@ -26,30 +26,32 @@ using std::cout;
class Machine {
public:
Machine();
Machine(int speedHz) : Machine() { this->speedHz = speedHz; _instructionsTable = instructions; }
~Machine();
// Accessor methods for registers
int getA() const { return A; }
void setA(int value) { A = value; }
void setA(int value) { A = toSIC24(value); }
int getB() const { return B; }
void setB(int value) { B = value; }
void setB(int value) { B = toSIC24(value); }
int getX() const { return X; }
void setX(int value) { X = value; }
void setX(int value) { X = toSIC24(value); }
int getL() const { return L; }
void setL(int value) { L = value; }
void setL(int value) { L = toSIC24(value); }
int getS() const { return S; }
void setS(int value) { S = value; }
void setS(int value) { S = toSIC24(value); }
int getT() const { return T; }
void setT(int value) { T = value; }
void setT(int value) { T = toSIC24(value); }
// PC is an address → don't mask to 24 unless you want 24-bit addressing
int getPC() const { return PC; }
void setPC(int value) { PC = value; }
// status word: keep as-is
int getSW() const { return SW; }
void setSW(int value) { SW = value; }
@ -76,14 +78,19 @@ public:
// Set a file device at index `num` using the provided filename.
void setFileDevice(int num, const std::string &filename);
// Fetch and execute instructions
int fetch();
void execute();
bool execF1(int opcode);
bool execF2(int opcode, int operand);
bool execSICF3F4(int opcode, int ni, int operand);
// Execution and speed control
int getSpeed() const;
void setSpeed(int Hz);
void start();
void stop();
void tick();
void halt();
bool isStopped() const { return _stopped; }
void reset();
// error handling methods
void notImplemented(string mnemonic);
@ -92,6 +99,22 @@ public:
void divisionByZero(int opcode);
void undefinedHandler(int opcode);
bool getExtendedMode() const { return _exex_mode; }
void enableExtendedMode();
void disableExtendedMode();
int* getVectorRegister(int regNum);
void setVectorRegister(int regNum, const int* values);
const int* getVA() const { return VA; }
const int* getVS() const { return VS; }
const int* getVT() const { return VT; }
void setVA(const int* values);
void setVS(const int* values);
void setVT(const int* values);
private:
// registers
int A, B, X, L, S, T, PC, SW;
@ -104,42 +127,23 @@ private:
std::vector<std::shared_ptr<Device>> devices;
// fallback device returned when device slot is empty/invalid
Device fallbackDevice;
// Execution control
std::atomic<bool> running{false};
std::atomic<int> 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
};
// Convert integer to 24-bit signed SIC representation
inline int toSIC24(int value) {
value &= 0xFFFFFF;
if (value & 0x800000) {
value -= 0x1000000;
}
return value;
}
inline int setCC(int sw, int cc) {
sw &= ~CC_MASK;
sw |= (cc & CC_MASK);
return sw;
}
inline int sic_comp(int a, int b, int sw) {
int sa = toSIC24(a);
int sb = toSIC24(b);
int cc;
if (sa < sb) {
cc = CC_LT;
} else if (sa == sb) {
cc = CC_EQ;
} else {
cc = CC_GT;
}
return setCC(sw, cc);
}
inline int getCC(int sw) {
return sw & CC_MASK;
}
#endif // MACHINE_H

View file

@ -1,6 +1,8 @@
#ifndef OPCODE_H
#define OPCODE_H
#include "utils.h"
// ==============================
// Opcode definitions (SIC/XE)
// ==============================
@ -64,13 +66,26 @@
#define TIXR 0xB8
#define WD 0xDC
// SW register condition codes
constexpr int CC_LT = 0x0; // 00
constexpr int CC_EQ = 0x1; // 01
constexpr int CC_GT = 0x2; // 10
constexpr int CC_MASK = 0x3; // mask for 2 bits
// ==============================
// Extended opcodes (SIC/XE/XE)
// ==============================
#define NOP 0xF1
#define HALT 0xF2
#define XEXE 0xEE // Enable extended mode
#define VADD 0x18
#define VADDR 0x90
#define VSUB 0x1C
#define VSUBR 0x94
#define VMUL 0x20
#define VMULR 0x98
#define VDIV 0x24
#define VDIVR 0x9C
#define STVA 0x0C
#define STVS 0x7C
#define STVT 0x84
#define LDVA 0x00
#define LDVS 0x68
#define LDVT 0x04
@ -93,6 +108,7 @@ struct InstructionInfo {
};
extern InstructionInfo instructions[];
extern InstructionInfo instructionsEXEX[];
// Initialize the instruction table
void loadInstructionSet();

View file

@ -0,0 +1,21 @@
#ifndef READER_H
#define READER_H
#include <string>
#include <cstdint>
// Abstract Reader class: read bytes/strings from a source (file, string, etc.)
class Reader {
public:
virtual ~Reader() = default;
// return 0..255 on success, -1 on EOF/error
virtual int readByte() = 0;
// read exactly len bytes into buf; return true on success
virtual bool readBytes(uint8_t* buf, size_t len) = 0;
// read up to len bytes into a std::string; may return shorter string on EOF
virtual std::string readString(size_t len) = 0;
// read a line (up to newline), return empty string on EOF
virtual std::string readLine() = 0;
};
#endif // READER_H

View file

@ -0,0 +1,22 @@
#ifndef STRING_READER_H
#define STRING_READER_H
#include "reader.h"
#include <string>
#include <sstream>
class StringReader : public Reader {
public:
explicit StringReader(const std::string &s);
~StringReader() override;
int readByte() override;
bool readBytes(uint8_t* buf, size_t len) override;
std::string readString(size_t len) override;
std::string readLine() override;
private:
std::istringstream in;
};
#endif // STRING_READER_H

View file

@ -0,0 +1,95 @@
#ifndef UTILS_H
#define UTILS_H
#include "constants.h"
#include <cmath>
// ==============================
// SIC/XE Utility Functions
// ==============================
// Instruction bit extraction utilities
inline int getXBit(int b2) {
return (b2 & 0x80) ? 1 : 0;
}
inline int getBPBits(int b2) {
return (b2 >> 5) & 0x03;
}
enum class AddressingMode {
IMMEDIATE,
INDIRECT,
SIMPLE,
SIC_DIRECT,
INVALID
};
// Get addressing mode from ni bits
AddressingMode getAddressingMode(int ni);
// convert to signed 24-bit integer
inline int toSIC24(int value) {
value &= 0xFFFFFF;
if (value & 0x800000) {
value -= 0x1000000;
}
return value;
}
inline int setCC(int sw, int cc) {
sw &= ~CC_MASK;
sw |= (cc & CC_MASK);
return sw;
}
inline int sic_comp(int a, int b, int sw) {
int sa = toSIC24(a);
int sb = toSIC24(b);
int cc;
if (sa < sb) {
cc = CC_LT;
} else if (sa == sb) {
cc = CC_EQ;
} else {
cc = CC_GT;
}
return setCC(sw, cc);
}
inline int sic_comp(double a, double b, int sw) {
int cc;
if (a < b) {
cc = CC_LT;
} else if (a == b) {
cc = CC_EQ;
} else {
cc = CC_GT;
}
return setCC(sw, cc);
}
inline int getCC(int sw) {
return sw & CC_MASK;
}
inline double normaliseFloat(double value)
{
if (value == 0.0 )return 0.0;
if (!std::isfinite(value)) return value;
double mantissa = value;
while (std::fabs(mantissa) >= 10.0) mantissa /= 10.0;
while (std::fabs(mantissa) < 1.0) mantissa *= 10.0;
return mantissa;
}
#endif // UTILS_H

View file

@ -3,21 +3,8 @@
#include <fstream>
FileDevice::FileDevice(const std::string &filename)
: filename(filename), fileCreated(false), readPosition(0)
{
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::binary);
if (!fileStream.is_open()) {
std::ofstream create(filename, std::ios::binary);
if (!create) {
throw std::runtime_error("Failed to create file: " + filename);
}
create.close();
fileStream.clear();
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::binary);
if (!fileStream.is_open()) {
throw std::runtime_error("Failed to open file after creating: " + filename);
}
}
}
FileDevice::~FileDevice()
@ -27,19 +14,55 @@ FileDevice::~FileDevice()
}
}
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.read(reinterpret_cast<char*>(&value), sizeof(value));
fileStream.seekg(readPosition);
char ch;
if (fileStream.get(ch)) {
value = static_cast<unsigned char>(ch);
readPosition = fileStream.tellg();
}
}
return value;
}
void FileDevice::write(unsigned char value)
{
ensureFileOpen();
if (fileStream.is_open()) {
fileStream.write(reinterpret_cast<const char*>(&value), sizeof(value));
fileStream.seekp(0, std::ios::end);
fileStream.put(static_cast<char>(value));
fileStream.flush();
}
}

View file

@ -0,0 +1,36 @@
#include "file_reader.h"
FileReader::FileReader(const std::string &path, std::ios::openmode m)
: in(path, m)
{}
FileReader::~FileReader() = default;
int FileReader::readByte() {
char c;
if (!in.get(c)) return -1;
return static_cast<unsigned char>(c);
}
bool FileReader::readBytes(uint8_t* buf, size_t len) {
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
return static_cast<size_t>(in.gcount()) == len;
}
std::string FileReader::readString(size_t len) {
std::string s;
s.resize(len);
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
std::streamsize got = in.gcount();
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
return s;
}
bool FileReader::good() const { return static_cast<bool>(in); }
std::string FileReader::readLine() {
std::string s;
if (!std::getline(in, s)) return std::string();
return s;
}

View file

@ -1,16 +1,96 @@
#include "instructions.h"
#include "machine.h"
#include "utils.h"
inline int resolveWordOperand(Machine& m, int ea, AddressingMode mode)
{
switch (mode)
{
case AddressingMode::IMMEDIATE: return ea;
case AddressingMode::SIMPLE:
case AddressingMode::SIC_DIRECT: return m.getWord(ea);
case AddressingMode::INDIRECT: return m.getWord(m.getWord(ea));
default: m.invalidAddressing(); return 0;
}
}
inline double resolveFloatOperand(Machine& m, int ea, AddressingMode mode)
{
switch (mode)
{
case AddressingMode::IMMEDIATE: return static_cast<double>(ea);
case AddressingMode::SIMPLE:
case AddressingMode::SIC_DIRECT: return m.getFloat(ea);
case AddressingMode::INDIRECT: return m.getFloat(m.getWord(ea));
default: m.invalidAddressing(); return 0.0;
}
}
void addr_handler(Machine& m, int r1, int r2) {
inline void writeWordOperand(Machine& m, int ea, AddressingMode mode, int value)
{
switch (mode)
{
case AddressingMode::SIMPLE:
case AddressingMode::SIC_DIRECT: m.setWord(ea, value); break; // direct store
case AddressingMode::INDIRECT: m.setWord(m.getWord(ea), value); break; // store via pointer
default: m.invalidAddressing(); break;
}
}
inline void writeFloatOperand(Machine& m, int ea, AddressingMode mode, double value)
{
switch (mode)
{
case AddressingMode::SIMPLE:
case AddressingMode::SIC_DIRECT: m.setFloat(ea, value); break;
case AddressingMode::INDIRECT: m.setFloat(m.getWord(ea), value); break;
default: m.invalidAddressing(); break;
}
}
// For jump-like ops: what PC should become?
inline int resolveJumpTarget(Machine& m, int ea, AddressingMode mode)
{
switch (mode)
{
case AddressingMode::IMMEDIATE:
case AddressingMode::SIMPLE:
case AddressingMode::SIC_DIRECT: return ea; // jump to EA (normal case)
case AddressingMode::INDIRECT: return m.getWord(ea); // jump via pointer
default: m.invalidAddressing(); return m.getPC();
}
}
void fix_handler(Machine &m)
{
m.setA(static_cast<int>(m.getF()));
}
void float_handler(Machine &m)
{
m.setF(static_cast<double>(m.getA()));
}
void norm_handler(Machine &m)
{
m.setF(normaliseFloat(m.getF()));
}
void addr_handler(Machine &m, int r1, int r2)
{
m.setReg(r2, m.getReg(r1) + m.getReg(r2));
}
// CLEAR instruction: clears register r (first nibble), second nibble unused
void clear_handler(Machine& m, int r, int unused) {
m.setReg(r, 0);
}
void compr_handler(Machine &m, int r1, int r2)
{
m.setSW(sic_comp(m.getReg(r1), m.getReg(r2), m.getSW()));
}
void divr_handler(Machine& m, int r1, int r2) {
@ -31,15 +111,33 @@ void rmo_handler(Machine &m, int r1, int r2)
m.setReg(r2, m.getReg(r1));
}
// SHIFTL r1, n → left *circular* shift n bits
void shiftl_handler(Machine &m, int r1, int n)
{
m.setReg(r1, m.getReg(r1) << n);
unsigned int v = m.getReg(r1) & WORD_MASK;
n %= WORD_SIZE;
unsigned int res = ((v << n) | (v >> (WORD_SIZE - n))) & WORD_MASK;
m.setReg(r1, res);
}
// SHIFTR r1, n → right shift n bits, fill with original leftmost bit
void shiftr_handler(Machine &m, int r1, int n)
{
m.setReg(r1, m.getReg(r1) >> n);
unsigned int v = m.getReg(r1) & WORD_MASK;
n %= WORD_SIZE;
unsigned int msb = (v & 0x800000) ? 1u : 0u;
unsigned int shifted = v >> n;
unsigned int fill = 0;
if (msb) {
fill = (~0u) << (WORD_SIZE - n);
fill &= WORD_MASK;
}
unsigned int res = (shifted | fill) & WORD_MASK;
m.setReg(r1, res);
}
void subr_handler(Machine &m, int r1, int r2)
{
m.setReg(r2, m.getReg(r2) - m.getReg(r1));
@ -58,3 +156,448 @@ void tixr_handler(Machine &m, int r1, int unused)
int valR1 = m.getReg(r1);
m.setSW(sic_comp(valX, valR1, m.getSW()));
}
void add_handler(Machine &m, int ea, AddressingMode mode)
{
int val = resolveWordOperand(m, ea, mode);
m.setA(m.getA() + val);
}
void addf_handler(Machine &m, int ea, AddressingMode mode)
{
double val = resolveFloatOperand(m, ea, mode);
m.setA(m.getA() + val);
}
void and_handler(Machine &m, int ea, AddressingMode mode)
{
int val = resolveWordOperand(m, ea, mode);
m.setA(m.getA() & val);
}
void comp_handler(Machine &m, int ea, AddressingMode mode)
{
int operand = resolveWordOperand(m, ea, mode);
m.setSW(sic_comp(m.getA(), operand, m.getSW()));
}
void compf_handler(Machine &m, int ea, AddressingMode mode)
{
double operand = resolveFloatOperand(m, ea, mode);
m.setSW(sic_comp(m.getF(), operand, m.getSW()));
}
void div_handler(Machine &m, int ea, AddressingMode mode)
{
int divisor = resolveWordOperand(m, ea, mode);
if (divisor == 0) {
m.divisionByZero(DIV);
return;
}
m.setA(m.getA() / divisor);
}
void divf_handler(Machine &m, int ea, AddressingMode mode)
{
double divisor = resolveFloatOperand(m, ea, mode);
if (divisor == 0.0) {
m.divisionByZero(DIVF);
return;
}
m.setF(m.getF() / divisor);
}
void j_handler(Machine &m, int ea, AddressingMode mode)
{
int target = resolveJumpTarget(m, ea, mode);
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<unsigned char>(m.getA() & 0xFF));
}
void xexe_handler(Machine &m)
{
m.enableExtendedMode();
m.execute();
m.disableExtendedMode();
}
void halt_handler(Machine &m)
{
m.halt();
}
void nop_handler(Machine &m)
{
// Do nothing
}
void vaddr_handler(Machine &m, int r1, int r2)
{
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVectorRegister(r1)[i] + m.getVectorRegister(r2)[i];
}
m.setVectorRegister(r2, result);
}
void vsubr_handler(Machine &m, int r1, int r2)
{
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVectorRegister(r2)[i] - m.getVectorRegister(r1)[i];
}
m.setVectorRegister(r2, result);
}
void vmulr_handler(Machine &m, int r1, int r2)
{
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVectorRegister(r1)[i] * m.getVectorRegister(r2)[i];
}
m.setVectorRegister(r2, result);
}
void vdivr_handler(Machine &m, int r1, int r2)
{
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
if (m.getVectorRegister(r1)[i] == 0) {
m.divisionByZero(VDIVR);
return;
}
result[i] = m.getVectorRegister(r2)[i] / m.getVectorRegister(r1)[i];
}
m.setVT(result);
}
void vadd_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVA()[i] + vec[i];
}
m.setVA(result);
}
void vsub_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVA()[i] - vec[i];
}
m.setVA(result);
}
void vmul_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
result[i] = m.getVA()[i] * vec[i];
}
m.setVA(result);
}
void vdiv_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
int result[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
if (vec[i] == 0) {
m.divisionByZero(VDIV);
return;
}
result[i] = m.getVA()[i] / vec[i];
}
m.setVA(result);
}
void stva_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
const int* vec = m.getVA();
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
m.setWord(baseAddr + i * 3, vec[i]);
}
}
void stvs_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
const int* vec = m.getVS();
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
m.setWord(baseAddr + i * 3, vec[i]);
}
}
void stvt_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
const int* vec = m.getVT();
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
m.setWord(baseAddr + i * 3, vec[i]);
}
}
void ldva_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
m.setVA(vec);
}
void ldvs_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
m.setVS(vec);
}
void ldvt_handler(Machine &m, int ea, AddressingMode mode)
{
int baseAddr = resolveWordOperand(m, ea, mode);
int vec[VECTOR_REG_SIZE];
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
vec[i] = m.getWord(baseAddr + i * 3);
}
m.setVT(vec);
}

View file

@ -0,0 +1,134 @@
#include "loader.h"
#include "file_reader.h"
#include "string_reader.h"
#include "machine.h"
#include "constants.h"
#include <algorithm>
#include <cstdio>
Loader::~Loader()
{
_machine.reset();
}
void Loader::load()
{
HeaderMetadata header = readHeader();
while(true) {
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
switch (type) {
case RecordType::TEXT: {
TextRecord textRecord = readTextRecord();
if (!load_into_memory(textRecord.start_address, textRecord.data)) {
throw std::runtime_error("Failed to load text record into memory");
}
break;
}
case RecordType::END: {
EndRecord endRecord = readEndRecord();
_machine->setPC(endRecord.execution_start_address);
return; // Loading complete
}
case RecordType::UNKNOWN:
default:
throw std::runtime_error("Unknown record type encountered");
}
}
}
Loader::RecordType Loader::parseRecordType(char c)
{
switch (c) {
case 'H': return RecordType::HEADER;
case 'T': return RecordType::TEXT;
case 'E': return RecordType::END;
default: return RecordType::UNKNOWN; // fallback; adjust as needed
}
}
Loader::HeaderMetadata Loader::readHeader()
{
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
if (type != RecordType::HEADER) {
throw std::runtime_error("Expected HEADER record");
}
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
HeaderMetadata header;
// Read program name (6 bytes)
header.program_name = _file_reader->readString(6);
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
// Read start address (6 hex digits)
header.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
// Read length (6 hex digits)
header.length = std::stoi(_file_reader->readString(6), nullptr, 16);
// consume newline
_file_reader->readLine();
return header;
}
Loader::TextRecord Loader::readTextRecord()
{
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
TextRecord record;
// Assume 'T' has already been read
record.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
// Read length (1 byte, 2 hex digits)
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<uint8_t>(std::stoi(byteHex, nullptr, 16));
}
return record;
}
Loader::EndRecord Loader::readEndRecord()
{
EndRecord record;
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
// Assume 'E' has already been read
std::string addrStr = _file_reader->readString(6);
if (!addrStr.empty()) {
record.execution_start_address = std::stoi(addrStr, nullptr, 16);
} else {
record.execution_start_address = 0;
}
// consume newline
_file_reader->readLine();
return record;
}
bool Loader::load_into_memory(int start_address, const std::vector<uint8_t> &data)
{
for(size_t i = 0; i < data.size(); ++i) {
int addr = start_address + static_cast<int>(i);
if (addr < 0 || addr >= MEMORY_SIZE) {
return false;
}
_machine->setByte(addr, data[i]);
}
return true;
}

View file

@ -4,6 +4,8 @@
#include "opcode.h"
#include "instructions.h"
#include <cmath>
#include <thread>
using std::make_shared;
@ -12,13 +14,37 @@ 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<InputDevice>(std::cin);
// device 1: standard output
devices[1] = make_shared<OutputDevice>(std::cout);
// device 2: standard error
devices[2] = make_shared<OutputDevice>(std::cerr);
// 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<FileDevice>(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()
@ -28,6 +54,17 @@ Machine::~Machine()
}
}
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;
@ -38,6 +75,7 @@ void Machine::invalidOpcode(int opcode)
cout << prefix << "Invalid opcode: " << opcode << endl;
}
void Machine::invalidAddressing()
{
cout << prefix << "Invalid addressing mode" << endl;
@ -53,6 +91,97 @@ 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) {
@ -118,7 +247,8 @@ int Machine::getWord(int address)
cerr << prefix << "Invalid memory address: " << address << endl;
return -1;
}
return static_cast<int>(memory[address]) | (static_cast<int>(memory[address + 1]) << 8) | (static_cast<int>(memory[address + 2]) << 16);
// Big-endian: high byte first
return (static_cast<int>(memory[address]) << 16) | (static_cast<int>(memory[address + 1]) << 8) | static_cast<int>(memory[address + 2]);
}
// Assuming word is 3 bytes
@ -128,23 +258,94 @@ void Machine::setWord(int address, int value)
cerr << prefix << "Invalid memory address: " << address << endl;
return;
}
value &= 0xFFFFFF;
memory[address] = static_cast<unsigned char>(value & 0xFF);
// Big-endian: high byte first
memory[address] = static_cast<unsigned char>((value >> 16) & 0xFF);
memory[address + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
memory[address + 2] = static_cast<unsigned char>((value >> 16) & 0xFF);
memory[address + 2] = static_cast<unsigned char>(value & 0xFF);
}
// TODO: implement proper float storage and retrieval
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)
{
// TODO: implement proper float storage
if (address < 0 || address + 5 >= MEMORY_SIZE) {
cerr << prefix << "Invalid float address: " << address << endl;
return;
}
if (value == 0.0) {
memory[address] = 0;
memory[address+1] = 0;
memory[address+2] = 0;
memory[address+3] = 0;
memory[address+4] = 0;
memory[address+5] = 0;
return;
}
int sign = value < 0;
double x = sign ? -value : value;
// normalize x to [1, 2)
int exp2 = 0;
x = std::frexp(x, &exp2);
x *= 2.0;
exp2 -= 1;
int exp_field = exp2 + SICF_EXP_BIAS;
if (exp_field < 0) exp_field = 0;
if (exp_field > 127) exp_field = 127;
// mantissa = (x - 1) * 2^40
double frac_d = (x - 1.0) * (double)(1ULL << SICF_FRAC_BITS);
unsigned long long frac = (unsigned long long)(frac_d + 0.5); // round
frac &= SICF_FRAC_MASK;
unsigned long long raw =
((unsigned long long)sign << 47) |
((unsigned long long)exp_field << 40) |
frac;
// store 6 bytes 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<int>(devices.size()) || !devices[num]) {
@ -195,49 +396,56 @@ int Machine::fetch()
return getByte(PC++);
}
void Machine::execute()
{
int opcode = fetch();
InstructionType type = instructions[opcode].type;
switch (type) {
case InstructionType::TYPE1: execF1(opcode);break;
case InstructionType::TYPE2: execF2(opcode, fetch());break;
case InstructionType::TYPE3_4: // extract n and i bits
{
int ni = opcode & 0x3;
int operand = fetch();
execSICF3F4(opcode, ni, operand);
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
}
break;
default: invalidOpcode(opcode); break;
execSICF3F4(opcode, ni, x, b, p, e, operand);
return;
}
invalidOpcode(b1);
}
bool Machine::execF1(int opcode)
{
switch (opcode)
{
case FIX:
setA(static_cast<int>(getF()));
if (_instructionsTable[opcode].handler) {
auto handler = reinterpret_cast<void(*)(Machine&)>(_instructionsTable[opcode].handler);
handler(*this);
return true;
case FLOAT:
setF(static_cast<double>(getA()));
return true;
case HIO:
notImplemented("HIO");
return true;
case NORM:
notImplemented("NORM");
return true;
case SIO:
notImplemented("SIO");
return true;
case TIO:
notImplemented("TIO");
return true;
default:
break;
}
undefinedHandler(opcode);
return false;
}
@ -246,8 +454,8 @@ bool Machine::execF2(int opcode, int operand)
int r1 = (operand >> 4) & 0xF;
int r2 = operand & 0xF;
if (instructions[opcode].handler) {
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(instructions[opcode].handler);
if (_instructionsTable[opcode].handler) {
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(_instructionsTable[opcode].handler);
handler(*this, r1, r2);
return true;
}
@ -255,7 +463,63 @@ bool Machine::execF2(int opcode, int operand)
return false;
}
bool Machine::execSICF3F4(int opcode, int ni, int operand)
bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand)
{
int ea_part = operand;
int base = 0;
AddressingMode mode = getAddressingMode(ni);
// --- PURE SIC ---
if (mode == AddressingMode::SIC_DIRECT) {
int ea = ea_part + (x ? getX() : 0);
if (_instructionsTable[opcode].handler) {
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
h(*this, ea, mode);
return true;
}
undefinedHandler(opcode);
return false;
}
// --- SIC/XE EA calc ---
if (!e) { // format 3
if (b && !p) {
base = getB(); // base-relative, unsigned 12-bit
} else if (p && !b) {
// PC-relative, signed 12-bit
if (ea_part & 0x800) // bit 11 set?
ea_part |= 0xFFFFF000; // sign-extend
base = getPC();
}
}
// format 4 (e=1): b/p ignored, ea_part is 20-bit absolute
int ea = base + ea_part + (x ? getX() : 0);
if (_instructionsTable[opcode].handler) {
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
h(*this, ea, mode);
return true;
}
undefinedHandler(opcode);
return false;
}
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);
}

View file

@ -1,27 +1,140 @@
#include <iostream>
#include <thread>
#include <chrono>
#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;
cout << "Machine initialized successfully." << endl;
const int TEMP_ADDR = 0x50;
const int LOOP_ADDR = 0x03;
// COMPUTE A + B and store result in B
machine.setA(10);
machine.setB(20);
machine.setByte(0, ADDR);
machine.setByte(1, 0x03); // r1 = 0 (A), r2 = 3 (B)
cout << "Before ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl;
// clear TEMP
machine.setByte(TEMP_ADDR, 0);
loadInstructionSet();
cout << "SIC/XE Program: Vector add test" << endl;
const int VA_ADDR = 0x100; // source vector A
const int VB_ADDR = 0x200; // source vector B
const int VR_ADDR = 0x300; // result store (STVA)
// Prepare two 4-element vectors (WORD = 3 bytes) for VA and VB
int a_vals[VECTOR_REG_SIZE] = {1,2,3,4};
int b_vals[VECTOR_REG_SIZE] = {5,6,7,8};
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
machine.setWord(VA_ADDR + i * 3, a_vals[i]);
machine.setWord(VB_ADDR + i * 3, b_vals[i]);
}
// Assemble program at address 0x000 (we use XEXE before each extended op)
// Offsets and bytes (hex):
// 0x00: XEXE -> 0xEE
// 0x01: LDVA (format 4) -> b1=0x03 (LDVA|ni=0x00|0x03), b2=0x10 (e=1), b3=0x01, b4=0x00 (addr 0x100)
// 0x05: XEXE -> 0xEE
// 0x06: LDVS (format 4) -> b1=0x6B (0x68|0x03), b2=0x10, b3=0x02, b4=0x00 (addr 0x200)
// 0x0A: XEXE -> 0xEE
// 0x0B: VADDR B->A (type 2) -> opcode 0x90, operand r1=4 (VS), r2=0 (VA) => operand=(4<<4)|0=0x40
// 0x0D: XEXE -> 0xEE
// 0x0E: STVA (format4) -> b1=0x0F (0x0C|0x03), b2=0x10, b3=0x03, b4=0x00 (addr 0x300)
// 0x12: J (format4) to self -> b1=0x3F (0x3C|0x03), b2=0x10, b3=0x00, b4=0x12
unsigned char prog[] = {
0xEE,
0x01, 0x10, 0x01, 0x00, // LDVA (format 4) with ni=IMMEDIATE -> b1=0x01
0xEE,
0x69, 0x10, 0x02, 0x00, // LDVS (format 4) with ni=IMMEDIATE -> b1=0x69 (0x68|0x01)
0xEE,
0x90, 0x40, // VADDR VS->VA (type2)
0xEE,
0x0D, 0x10, 0x03, 0x00, // STVA (format4) with ni=IMMEDIATE -> b1=0x0D
0x3F, 0x10, 0x00, 0x12 // J (format4) loop to 0x12
};
const int PROG_START = 0x00;
for (size_t i = 0; i < sizeof(prog); ++i) {
machine.setByte(PROG_START + static_cast<int>(i), prog[i]);
}
machine.setPC(PROG_START);
cout << "Program loaded. VA@0x" << std::hex << VA_ADDR << " VB@0x" << VB_ADDR << " -> store@0x" << VR_ADDR << std::dec << endl;
const int MAX_STEPS = 100;
for (int i = 0; i < MAX_STEPS; ++i) {
machine.execute();
cout << "After ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl;
}
// 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> machine = std::make_shared<Machine>();
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;
}

View file

@ -1,75 +1,109 @@
#include "opcode.h"
#include "instructions.h"
#include "utils.h"
#include <utility>
InstructionInfo instructions[0xff];
InstructionInfo instructionsEXEX[0xff];
void loadInstructionSet()
{
instructions[ADD] = {"ADD", InstructionType::TYPE3_4, nullptr};
instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, nullptr};
instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(add_handler)};
instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(addf_handler)};
instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(addr_handler)};
instructions[AND] = {"AND", InstructionType::TYPE3_4, nullptr};
instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(and_handler)};
instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(clear_handler)};
instructions[COMP] = {"COMP", InstructionType::TYPE3_4, nullptr};
instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, nullptr};
instructions[COMPR] = {"COMPR", InstructionType::TYPE2, nullptr};
instructions[DIV] = {"DIV", InstructionType::TYPE3_4, nullptr};
instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, nullptr};
instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(comp_handler)};
instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(compf_handler)};
instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(compr_handler)};
instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(div_handler)};
instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(divf_handler)};
instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(divr_handler)};
instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr};
instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr};
instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast<RawHandler>(fix_handler)};
instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(float_handler)};
instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr};
instructions[J] = {"J", InstructionType::TYPE3_4, nullptr};
instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, nullptr};
instructions[JGT] = {"JGT", InstructionType::TYPE3_4, nullptr};
instructions[JLT] = {"JLT", InstructionType::TYPE3_4, nullptr};
instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, nullptr};
instructions[LDA] = {"LDA", InstructionType::TYPE3_4, nullptr};
instructions[LDB] = {"LDB", InstructionType::TYPE3_4, nullptr};
instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, nullptr};
instructions[LDF] = {"LDF", InstructionType::TYPE3_4, nullptr};
instructions[LDL] = {"LDL", InstructionType::TYPE3_4, nullptr};
instructions[LDS] = {"LDS", InstructionType::TYPE3_4, nullptr};
instructions[LDT] = {"LDT", InstructionType::TYPE3_4, nullptr};
instructions[LDX] = {"LDX", InstructionType::TYPE3_4, nullptr};
instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(j_handler)};
instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jeq_handler)};
instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jgt_handler)};
instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jlt_handler)};
instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jsub_handler)};
instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lda_handler)};
instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldb_handler)};
instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldch_handler)};
instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldf_handler)};
instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldl_handler)};
instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lds_handler)};
instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldt_handler)};
instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldx_handler)};
instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr};
instructions[MUL] = {"MUL", InstructionType::TYPE3_4, nullptr};
instructions[MULF] = {"MULF", InstructionType::TYPE3_4, nullptr};
instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mul_handler)};
instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mulf_handler)};
instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(mulr_handler)};
instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr};
instructions[OR] = {"OR", InstructionType::TYPE3_4, nullptr};
instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr};
instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast<RawHandler>(norm_handler)};
instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(or_handler)};
instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rd_handler)};
instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast<RawHandler>(rmo_handler)};
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, nullptr};
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rsub_handler)};
instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftl_handler)};
instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftr_handler)};
instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr};
instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr};
instructions[STA] = {"STA", InstructionType::TYPE3_4, nullptr};
instructions[STB] = {"STB", InstructionType::TYPE3_4, nullptr};
instructions[STCH] = {"STCH", InstructionType::TYPE3_4, nullptr};
instructions[STF] = {"STF", InstructionType::TYPE3_4, nullptr};
instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sta_handler)};
instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stb_handler)};
instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stch_handler)};
instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stf_handler)};
instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr};
instructions[STL] = {"STL", InstructionType::TYPE3_4, nullptr};
instructions[STS] = {"STS", InstructionType::TYPE3_4, nullptr};
instructions[STSW] = {"STSW", InstructionType::TYPE3_4, nullptr};
instructions[STT] = {"STT", InstructionType::TYPE3_4, nullptr};
instructions[STX] = {"STX", InstructionType::TYPE3_4, nullptr};
instructions[SUB] = {"SUB", InstructionType::TYPE3_4, nullptr};
instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, nullptr};
instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stl_handler)};
instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sts_handler)};
instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stsw_handler)};
instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stt_handler)};
instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stx_handler)};
instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sub_handler)};
instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(subf_handler)};
instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(subr_handler)};
instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast<RawHandler>(svc_handler)};
instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(tixr_handler)};
instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr};
instructions[TIX] = {"TIX", InstructionType::TYPE3_4, nullptr};
instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(td_handler)};
instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(tix_handler)};
instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr};
instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr};
instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(wd_handler)};
// Load SIC/XE/XE extended instructions
if (USE_EXTENDED_MODE) {
// Still in main table
instructions[NOP] = {"NOP", InstructionType::TYPE1, reinterpret_cast<RawHandler>(nop_handler)};
instructions[HALT] = {"HALT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(halt_handler)};
instructions[XEXE] = {"XEXE", InstructionType::TYPE1, reinterpret_cast<RawHandler>(xexe_handler)};
instructionsEXEX[VADD] = {"VADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vadd_handler)};
instructionsEXEX[VADDR] = {"VADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vaddr_handler)};
instructionsEXEX[VSUB] = {"VSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vsub_handler)};
instructionsEXEX[VSUBR] = {"VSUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vsubr_handler)};
instructionsEXEX[VMUL] = {"VMUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vmul_handler)};
instructionsEXEX[VMULR] = {"VMULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vmulr_handler)};
instructionsEXEX[VDIV] = {"VDIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vdiv_handler)};
instructionsEXEX[VDIVR] = {"VDIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vdivr_handler)};
instructionsEXEX[STVA] = {"STVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stva_handler)};
instructionsEXEX[STVS] = {"STVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvs_handler)};
instructionsEXEX[STVT] = {"STVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvt_handler)};
instructionsEXEX[LDVA] = {"LDVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldva_handler)};
instructionsEXEX[LDVS] = {"LDVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvs_handler)};
instructionsEXEX[LDVT] = {"LDVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvt_handler)};
}
// Mark uninitialized opcodes as INVALID
for (int i = 0; i < 0xff; ++i) {
if (instructions[i].name == nullptr) {
instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
if (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
}
}

View file

@ -0,0 +1,33 @@
#include "string_reader.h"
StringReader::StringReader(const std::string &s)
: in(s)
{}
StringReader::~StringReader() = default;
int StringReader::readByte() {
char c;
if (!in.get(c)) return -1;
return static_cast<unsigned char>(c);
}
bool StringReader::readBytes(uint8_t* buf, size_t len) {
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
return static_cast<size_t>(in.gcount()) == len;
}
std::string StringReader::readString(size_t len) {
std::string s;
s.resize(len);
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
std::streamsize got = in.gcount();
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
return s;
}
std::string StringReader::readLine() {
std::string s;
if (!std::getline(in, s)) return std::string();
return s;
}

7
test.asm Normal file
View file

@ -0,0 +1,7 @@
test START 0
LDA #1
LDB #2
ADDR B, A
halt J halt
END test