From a711223abf1beb2c4b6f13fe88173b6d403e0551 Mon Sep 17 00:00:00 2001 From: zanostro Date: Sun, 21 Dec 2025 17:17:52 +0100 Subject: [PATCH] added M records --- simulator_SIC_XE/CMakeLists.txt | 15 ++--- simulator_SIC_XE/README.md | 55 ++++++++++++++---- simulator_SIC_XE/include/code.h | 6 ++ simulator_SIC_XE/include/loader.h | 9 +++ simulator_SIC_XE/include/opcode.h | 1 + simulator_SIC_XE/res/simple.asm | 32 +++++++++++ simulator_SIC_XE/res/test_format4.asm | 11 ++++ simulator_SIC_XE/src/assembler.cpp | 77 +++++++++++++++++++++++++ simulator_SIC_XE/src/code.cpp | 29 ++++++++++ simulator_SIC_XE/src/loader.cpp | 81 +++++++++++++++++++++++++++ simulator_SIC_XE/src/node.cpp | 1 + 11 files changed, 297 insertions(+), 20 deletions(-) create mode 100644 simulator_SIC_XE/res/simple.asm create mode 100644 simulator_SIC_XE/res/test_format4.asm create mode 100644 simulator_SIC_XE/src/assembler.cpp diff --git a/simulator_SIC_XE/CMakeLists.txt b/simulator_SIC_XE/CMakeLists.txt index e06a422..cde4cd2 100644 --- a/simulator_SIC_XE/CMakeLists.txt +++ b/simulator_SIC_XE/CMakeLists.txt @@ -15,7 +15,8 @@ file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp") set(MAIN_SRC "${PROJECT_SOURCE_DIR}/src/main.cpp") -list(REMOVE_ITEM SOURCES ${MAIN_SRC}) +set(ASSEMBLER_SRC "${PROJECT_SOURCE_DIR}/src/assembler.cpp") +list(REMOVE_ITEM SOURCES ${MAIN_SRC} ${ASSEMBLER_SRC}) if(NOT SOURCES) message(WARNING "No source files found in ${PROJECT_SOURCE_DIR}/src — the build will create an empty library") @@ -32,15 +33,9 @@ if(EXISTS "${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" - ) +if(EXISTS "${PROJECT_SOURCE_DIR}/src/assembler.cpp") + add_executable(assembler "${PROJECT_SOURCE_DIR}/src/assembler.cpp") + target_link_libraries(assembler PRIVATE simulator_lib) endif() message(STATUS "Project: ${PROJECT_NAME}") diff --git a/simulator_SIC_XE/README.md b/simulator_SIC_XE/README.md index aac6331..ae46366 100644 --- a/simulator_SIC_XE/README.md +++ b/simulator_SIC_XE/README.md @@ -1,30 +1,65 @@ # SIC/XE Simulator -A complete SIC/XE architecture simulator with instruction execution, device I/O, and memory management. +A complete SIC/XE architecture simulator with instruction execution, device I/O, memory management, and assembler. ## Quick Start -The easiest way to build and run the simulator: +### Building the Project + +```bash +make +``` + +This will build: +- `target/bin/simulator_exec` - The main simulator +- `target/bin/assembler` - The SIC/XE assembler +- `target/bin/simulator_qt` - Qt GUI version (if Qt is available) + +### Using the Assembler + +Assemble a SIC/XE assembly file to object code: + +```bash +./target/bin/assembler +``` + +**Example:** +```bash +./target/bin/assembler res/test_format4.asm +``` + +This will: +- Parse and assemble the input file +- Generate modification records (M records) for format 4 instructions +- Create `.obj` with the object code +- Display the object code and symbol table + +**Sample Output:** +``` +H TESTF4 0003E8 00001B +T 0003E8 1B 031003F70F1003FA4B1003FD4F2C090000000000000100004F2BFD +M 0003E9 05 +M 0003ED 05 +M 0003F1 05 +E 0003E8 +``` + +### Running 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 +This will build and run the simulator with the default program. ## Build Commands | Command | Description | |--------------|----------------------------------------------------| -| `make` | Build the project | +| `make` | Build all executables | | `make build` | Build the project | -| `make run` | Build run the simulator | +| `make run` | Build and run the simulator | | `make clean` | Clean build artifacts | -| `make run` | Clean build artifacts, build and run the simulator | ## Project Structure diff --git a/simulator_SIC_XE/include/code.h b/simulator_SIC_XE/include/code.h index 8c65df0..519d5f1 100644 --- a/simulator_SIC_XE/include/code.h +++ b/simulator_SIC_XE/include/code.h @@ -36,6 +36,12 @@ private: int _programLength = 0; std::string _programName; int _baseRegister = -1; // -1 means not set + + struct ModificationRecord { + int address; + int halfBytes; + }; + mutable std::vector _modificationRecords; // Pass 1: build symbol table and assign addresses void firstPass(); diff --git a/simulator_SIC_XE/include/loader.h b/simulator_SIC_XE/include/loader.h index 41899c2..34c31a6 100644 --- a/simulator_SIC_XE/include/loader.h +++ b/simulator_SIC_XE/include/loader.h @@ -27,6 +27,7 @@ public: enum class RecordType { HEADER, TEXT, + MODIFICATION, END, UNKNOWN }; @@ -40,6 +41,11 @@ public: int start_address; std::vector data; }; + struct ModificationRecord { + int address; // Address to be modified + int length; // Length in nibbles + bool add; // true for +, false for - + }; struct EndRecord { int execution_start_address; }; @@ -54,10 +60,13 @@ private : shared_ptr _machine; string _filename; shared_ptr _file_reader; + int _relocation_address; HeaderMetadata readHeader(); TextRecord readTextRecord(); + ModificationRecord readModificationRecord(); EndRecord readEndRecord(); bool load_into_memory(int start_address, const std::vector& data); + void applyModification(const ModificationRecord& mod); }; diff --git a/simulator_SIC_XE/include/opcode.h b/simulator_SIC_XE/include/opcode.h index 1034473..05cb693 100644 --- a/simulator_SIC_XE/include/opcode.h +++ b/simulator_SIC_XE/include/opcode.h @@ -6,6 +6,7 @@ #include #include #include +#include // ============================== // Opcode definitions (SIC/XE) diff --git a/simulator_SIC_XE/res/simple.asm b/simulator_SIC_XE/res/simple.asm new file mode 100644 index 0000000..20beca6 --- /dev/null +++ b/simulator_SIC_XE/res/simple.asm @@ -0,0 +1,32 @@ +SIMPLE START 0 + + +LDA NUM1 + +ADD NUM2 + +STA RESULT + + LDX NUM1 + LDL NUM2 + + LDA #0 + ADDR X,A + ADDR L,A + + +LDA RESULT + ADD #48 + RMO A,S + SHIFTL S,16 + SHIFTR S,16 + RMO S,A + STCH RESULT + LDCH RESULT + WD OUTPUT + +HALT J HALT + +OUTPUT BYTE 1 + +NUM1 WORD 1 +NUM2 WORD 2 +RESULT RESW 1 + + END SIMPLE diff --git a/simulator_SIC_XE/res/test_format4.asm b/simulator_SIC_XE/res/test_format4.asm new file mode 100644 index 0000000..6de136d --- /dev/null +++ b/simulator_SIC_XE/res/test_format4.asm @@ -0,0 +1,11 @@ +TESTF4 START 1000 + +LDA BUFFER + +STA OUTPUT + +JSUB FUNC + RSUB + +BUFFER RESW 1 +OUTPUT RESW 1 +FUNC LDA #0 + RSUB + END TESTF4 diff --git a/simulator_SIC_XE/src/assembler.cpp b/simulator_SIC_XE/src/assembler.cpp new file mode 100644 index 0000000..2ceece9 --- /dev/null +++ b/simulator_SIC_XE/src/assembler.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include "code.h" +#include "parser.h" +#include "opcode.h" + +using std::cout; +using std::endl; +using std::cerr; + +int main(int argc, char* argv[]) { + if (argc != 2) { + cerr << "Usage: " << argv[0] << " " << endl; + return 1; + } + + std::string inputFile = argv[1]; + + // Load instruction set + loadInstructionSet(); + + try { + // Read assembly file + cout << "Assembling: " << inputFile << endl; + std::ifstream file(inputFile); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + inputFile); + } + + std::string input; + std::string line; + while (std::getline(file, line)) { + input += line + "\n"; + } + file.close(); + + // Parse + Parser parser; + Code code = parser.parse(input); + + // Assemble + code.assemble(); + + // Generate object code + std::string objectCode = code.emitText(); + + // Determine output filename + std::string outputFile = inputFile; + size_t lastDot = outputFile.find_last_of('.'); + if (lastDot != std::string::npos) { + outputFile = outputFile.substr(0, lastDot); + } + outputFile += ".obj"; + + // Write to file + std::ofstream out(outputFile); + if (!out.is_open()) { + throw std::runtime_error("Failed to create output file: " + outputFile); + } + out << objectCode; + out.close(); + + // Display results + cout << "\n=== Object Code ===" << endl; + cout << objectCode; + cout << "\n=== Symbol Table ===" << endl; + cout << code.dumpSymbols(); + cout << "\nOutput written to: " << outputFile << endl; + + } catch (const std::exception& e) { + cerr << "ERROR: " << e.what() << endl; + return 1; + } + + return 0; +} diff --git a/simulator_SIC_XE/src/code.cpp b/simulator_SIC_XE/src/code.cpp index 961bf4a..2e87d12 100644 --- a/simulator_SIC_XE/src/code.cpp +++ b/simulator_SIC_XE/src/code.cpp @@ -312,6 +312,24 @@ std::vector Code::generateInstruction(const InstructionNode* inst, int bytes.push_back(byte2); bytes.push_back((displacement >> 8) & 0xFF); bytes.push_back(displacement & 0xFF); + + // Format 4 instructions with symbol references (not immediate values) need M records + bool needsRelocation = false; + if (!operands.empty() && std::holds_alternative(operands[0])) { + auto& sym = std::get(operands[0]); + // If it's not an immediate mode with a constant, it needs relocation + if (!sym.immediate || _symbolTable.find(sym.name) != _symbolTable.end()) { + needsRelocation = true; + } + } + + // Record modification if needed + if (needsRelocation) { + ModificationRecord mod; + mod.address = address + 1; // Skip the opcode+ni byte, start at xbpe+addr + mod.halfBytes = 5; // 5 half-bytes (20 bits) for format 4 address field + _modificationRecords.push_back(mod); + } } else { // Format 3: 12-bit displacement byte2 |= (displacement >> 8) & 0x0F; @@ -443,6 +461,9 @@ std::string Code::emitText() { oss << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << _programLength; oss << "\n"; + // Clear and rebuild modification records + _modificationRecords.clear(); + // T records: text (code/data) std::vector code = emitCode(); int textStart = 0; @@ -462,6 +483,14 @@ std::string Code::emitText() { textStart += textLength; } + // M records: modifications for format 4 instructions + for (const auto& mod : _modificationRecords) { + oss << "M "; + oss << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << mod.address << " "; + oss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase << mod.halfBytes; + oss << "\n"; + } + // E record: execution start address oss << "E "; oss << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << _startAddress; diff --git a/simulator_SIC_XE/src/loader.cpp b/simulator_SIC_XE/src/loader.cpp index 8d3aa17..1d00ac5 100644 --- a/simulator_SIC_XE/src/loader.cpp +++ b/simulator_SIC_XE/src/loader.cpp @@ -17,6 +17,7 @@ Loader::~Loader() void Loader::load() { HeaderMetadata header = readHeader(); + _relocation_address = header.start_address; while(true) { RecordType type = parseRecordType(static_cast(_file_reader->readByte())); @@ -28,6 +29,11 @@ void Loader::load() } break; } + case RecordType::MODIFICATION: { + ModificationRecord modRecord = readModificationRecord(); + applyModification(modRecord); + break; + } case RecordType::END: { EndRecord endRecord = readEndRecord(); _machine->setPC(endRecord.execution_start_address); @@ -45,6 +51,7 @@ Loader::RecordType Loader::parseRecordType(char c) switch (c) { case 'H': return RecordType::HEADER; case 'T': return RecordType::TEXT; + case 'M': return RecordType::MODIFICATION; case 'E': return RecordType::END; default: return RecordType::UNKNOWN; // fallback; adjust as needed } @@ -105,6 +112,29 @@ Loader::TextRecord Loader::readTextRecord() return record; } +Loader::ModificationRecord Loader::readModificationRecord() +{ + ModificationRecord record; + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + + record.address = std::stoi(_file_reader->readString(6), nullptr, 16); + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + + record.length = std::stoi(_file_reader->readString(2), nullptr, 16); + + record.add = true; + std::string rest = _file_reader->readLine(); + // Remove whitespace + rest.erase(std::remove_if(rest.begin(), rest.end(), ::isspace), rest.end()); + if (!rest.empty()) { + if (rest[0] == '-') { + record.add = false; + } + } + + return record; +} + Loader::EndRecord Loader::readEndRecord() { EndRecord record; @@ -132,3 +162,54 @@ bool Loader::load_into_memory(int start_address, const std::vector &dat } return true; } + +void Loader::applyModification(const ModificationRecord& mod) +{ + // M record specifies address and length in half-bytes (nibbles) + // We need to modify the value at that address by adding or subtracting + // the relocation address + + int address = mod.address; + int halfBytes = mod.length; + + // Calculate how many full bytes we need to read + // halfBytes can be odd or even + int numBytes = (halfBytes + 1) / 2; + + if (address < 0 || address + numBytes > MEMORY_SIZE) { + throw std::runtime_error("Modification address out of bounds"); + } + + // Read the current value from memory + int currentValue = 0; + for (int i = 0; i < numBytes; ++i) { + currentValue = (currentValue << 8) | _machine->getByte(address + i); + } + + // If odd number of half-bytes, we only modify the relevant nibbles + // For simplicity, we'll work with the full bytes and mask appropriately + int mask = 0; + for (int i = 0; i < halfBytes; ++i) { + mask = (mask << 4) | 0xF; + } + + // Extract the value to modify + int shift = (numBytes * 2 - halfBytes) * 4; + int valueToModify = (currentValue >> shift) & mask; + + // Apply modification + int newValue = mod.add ? (valueToModify + _relocation_address) + : (valueToModify - _relocation_address); + + // Mask to keep only the relevant bits + newValue &= mask; + + // Reconstruct the full value + int preservedBits = currentValue & ~(mask << shift); + int finalValue = preservedBits | (newValue << shift); + + // Write back to memory (big-endian) + for (int i = 0; i < numBytes; ++i) { + _machine->setByte(address + i, (finalValue >> ((numBytes - 1 - i) * 8)) & 0xFF); + } +} diff --git a/simulator_SIC_XE/src/node.cpp b/simulator_SIC_XE/src/node.cpp index c0b52fd..28c31fe 100644 --- a/simulator_SIC_XE/src/node.cpp +++ b/simulator_SIC_XE/src/node.cpp @@ -1,6 +1,7 @@ #include "node.h" #include #include +#include string Node::toString() const { std::ostringstream oss;