From d4754a048de64824f03ca92eaf61254d9dbe9402 Mon Sep 17 00:00:00 2001 From: zanostro Date: Tue, 11 Nov 2025 21:51:15 +0100 Subject: [PATCH 01/15] added instruction functionality --- simulator_SIC_XE/include/constants.h | 38 +++ simulator_SIC_XE/include/instructions.h | 52 +++- simulator_SIC_XE/include/machine.h | 60 +---- simulator_SIC_XE/include/opcode.h | 11 +- simulator_SIC_XE/include/utils.h | 80 ++++++ simulator_SIC_XE/src/instructions.cpp | 317 +++++++++++++++++++++++- simulator_SIC_XE/src/machine.cpp | 194 +++++++++++---- simulator_SIC_XE/src/main.cpp | 40 ++- simulator_SIC_XE/src/opcode.cpp | 132 +++++----- 9 files changed, 754 insertions(+), 170 deletions(-) create mode 100644 simulator_SIC_XE/include/constants.h create mode 100644 simulator_SIC_XE/include/utils.h diff --git a/simulator_SIC_XE/include/constants.h b/simulator_SIC_XE/include/constants.h new file mode 100644 index 0000000..0034171 --- /dev/null +++ b/simulator_SIC_XE/include/constants.h @@ -0,0 +1,38 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +// ============================== +// SIC/XE Architecture Constants +// ============================== + +// Memory and system constants +constexpr int MEMORY_SIZE = 65536; +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 + +#endif // CONSTANTS_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/instructions.h b/simulator_SIC_XE/include/instructions.h index ff2e532..e085a9d 100644 --- a/simulator_SIC_XE/include/instructions.h +++ b/simulator_SIC_XE/include/instructions.h @@ -1,11 +1,17 @@ #ifndef INSTRUCTIONS_H #define INSTRUCTIONS_H -#include "opcode.h" +#include "utils.h" + +class Machine; // forward declaration + + + // 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 +22,49 @@ 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); + #endif // INSTRUCTIONS_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/machine.h b/simulator_SIC_XE/include/machine.h index e0920da..9ee4fca 100644 --- a/simulator_SIC_XE/include/machine.h +++ b/simulator_SIC_XE/include/machine.h @@ -4,18 +4,15 @@ #include #include #include +#include +#include "constants.h" #include "device.h" #include "input_device.h" #include "output_device.h" #include "file_device.h" #include "opcode.h" - -#include - -#define MEMORY_SIZE 65536 -#define NUM_DEVICES 256 - +#include "utils.h" using std::string; using std::cerr; @@ -28,28 +25,29 @@ public: Machine(); ~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; } @@ -83,7 +81,7 @@ public: bool execF1(int opcode); bool execF2(int opcode, int operand); - bool execSICF3F4(int opcode, int ni, int operand); + bool execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand); // error handling methods void notImplemented(string mnemonic); @@ -106,40 +104,6 @@ private: Device fallbackDevice; }; - // Convert integer to 24-bit signed SIC representation -inline int toSIC24(int value) { - value &= 0xFFFFFF; - if (value & 0x800000) { - value -= 0x1000000; - } - return value; -} - -inline int setCC(int sw, int cc) { - sw &= ~CC_MASK; - sw |= (cc & CC_MASK); - return sw; -} - -inline int sic_comp(int a, int b, int sw) { - int sa = toSIC24(a); - int sb = toSIC24(b); - - int cc; - if (sa < sb) { - cc = CC_LT; - } else if (sa == sb) { - cc = CC_EQ; - } else { - cc = CC_GT; - } - - return setCC(sw, cc); -} - -inline int getCC(int sw) { - return sw & CC_MASK; -} #endif // MACHINE_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/opcode.h b/simulator_SIC_XE/include/opcode.h index 93df629..071e1bf 100644 --- a/simulator_SIC_XE/include/opcode.h +++ b/simulator_SIC_XE/include/opcode.h @@ -1,6 +1,8 @@ #ifndef OPCODE_H #define OPCODE_H +#include "utils.h" + // ============================== // Opcode definitions (SIC/XE) // ============================== @@ -65,15 +67,6 @@ #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 - - - enum class InstructionType { TYPE1, TYPE2, diff --git a/simulator_SIC_XE/include/utils.h b/simulator_SIC_XE/include/utils.h new file mode 100644 index 0000000..754b256 --- /dev/null +++ b/simulator_SIC_XE/include/utils.h @@ -0,0 +1,80 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "constants.h" + +// ============================== +// 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; +} + +#endif // UTILS_H \ No newline at end of file diff --git a/simulator_SIC_XE/src/instructions.cpp b/simulator_SIC_XE/src/instructions.cpp index 364fed1..a0a96dd 100644 --- a/simulator_SIC_XE/src/instructions.cpp +++ b/simulator_SIC_XE/src/instructions.cpp @@ -1,16 +1,81 @@ #include "instructions.h" #include "machine.h" +#include "utils.h" + +inline int resolveWordOperand(Machine& m, int ea, AddressingMode mode) +{ + switch (mode) + { + case AddressingMode::IMMEDIATE: return ea; + case AddressingMode::SIMPLE: + case AddressingMode::SIC_DIRECT: return m.getWord(ea); + case AddressingMode::INDIRECT: return m.getWord(m.getWord(ea)); + default: m.invalidAddressing(); return 0; + } +} + +inline double resolveFloatOperand(Machine& m, int ea, AddressingMode mode) +{ + switch (mode) + { + case AddressingMode::IMMEDIATE: return static_cast(ea); + case AddressingMode::SIMPLE: + case AddressingMode::SIC_DIRECT: return m.getFloat(ea); + case AddressingMode::INDIRECT: return m.getFloat(m.getWord(ea)); + default: m.invalidAddressing(); return 0.0; + } +} + + +inline void writeWordOperand(Machine& m, int ea, AddressingMode mode, int value) +{ + switch (mode) + { + case AddressingMode::SIMPLE: + case AddressingMode::SIC_DIRECT: m.setWord(ea, value); break; // direct store + case AddressingMode::INDIRECT: m.setWord(m.getWord(ea), value); break; // store via pointer + default: m.invalidAddressing(); break; + } +} + +inline void writeFloatOperand(Machine& m, int ea, AddressingMode mode, double value) +{ + switch (mode) + { + case AddressingMode::SIMPLE: + case AddressingMode::SIC_DIRECT: m.setFloat(ea, value); break; + case AddressingMode::INDIRECT: m.setFloat(m.getWord(ea), value); break; + default: m.invalidAddressing(); break; + } +} + + +// For jump-like ops: what PC should become? +inline int resolveJumpTarget(Machine& m, int ea, AddressingMode mode) +{ + switch (mode) + { + case AddressingMode::IMMEDIATE: + case AddressingMode::SIMPLE: + case AddressingMode::SIC_DIRECT: return ea; // jump to EA (normal case) + case AddressingMode::INDIRECT: return m.getWord(ea); // jump via pointer + default: m.invalidAddressing(); return m.getPC(); + } +} void 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 +96,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 +141,231 @@ void tixr_handler(Machine &m, int r1, int unused) int valR1 = m.getReg(r1); m.setSW(sic_comp(valX, valR1, m.getSW())); } + +void add_handler(Machine &m, int ea, AddressingMode mode) +{ + int val = resolveWordOperand(m, ea, mode); + m.setA(m.getA() + val); +} + +void addf_handler(Machine &m, int ea, AddressingMode mode) +{ + double val = resolveFloatOperand(m, ea, mode); + m.setA(m.getA() + val); +} + +void and_handler(Machine &m, int ea, AddressingMode mode) +{ + int val = resolveWordOperand(m, ea, mode); + m.setA(m.getA() & val); +} + +void comp_handler(Machine &m, int ea, AddressingMode mode) +{ + int operand = resolveWordOperand(m, ea, mode); + m.setSW(sic_comp(m.getA(), operand, m.getSW())); +} + +void compf_handler(Machine &m, int ea, AddressingMode mode) +{ + double operand = resolveFloatOperand(m, ea, mode); + m.setSW(sic_comp(m.getF(), operand, m.getSW())); +} + +void div_handler(Machine &m, int ea, AddressingMode mode) +{ + int divisor = resolveWordOperand(m, ea, mode); + if (divisor == 0) { + m.divisionByZero(DIV); + return; + } + m.setA(m.getA() / divisor); +} + +void divf_handler(Machine &m, int ea, AddressingMode mode) +{ + double divisor = resolveFloatOperand(m, ea, mode); + if (divisor == 0.0) { + m.divisionByZero(DIVF); + return; + } + m.setF(m.getF() / divisor); +} + +void j_handler(Machine &m, int ea, AddressingMode mode) +{ + int target = resolveJumpTarget(m, ea, mode); + m.setPC(target); +} + +void jeq_handler(Machine &m, int ea, AddressingMode mode) +{ + int sw = m.getSW(); + int cc = getCC(sw); + if (cc == CC_EQ) { + int target = resolveJumpTarget(m, ea, mode); + m.setPC(target); + } +} + +void jgt_handler(Machine &m, int ea, AddressingMode mode) +{ + int sw = m.getSW(); + int cc = getCC(sw); + if (cc == CC_GT) { + int target = resolveJumpTarget(m, ea, mode); + m.setPC(target); + } +} + +void jlt_handler(Machine &m, int ea, AddressingMode mode) +{ + int sw = m.getSW(); + int cc = getCC(sw); + if (cc == CC_LT) { + int target = resolveJumpTarget(m, ea, mode); + m.setPC(target); + } +} + +void jsub_handler(Machine &m, int ea, AddressingMode mode) +{ + int target = resolveJumpTarget(m, ea, mode); + m.setL(m.getPC()); + m.setPC(target); +} + +void lda_handler(Machine& m, int ea, AddressingMode mode) +{ + m.setA(resolveWordOperand(m, ea, mode)); +} + +void ldb_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setB(resolveWordOperand(m, ea, mode)); +} + +void ldch_handler(Machine &m, int ea, AddressingMode mode) +{ + int val = resolveWordOperand(m, ea, mode); + m.setA((m.getA() & 0xFFFF00) | (val & 0xFF)); +} + +void ldf_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setF(resolveFloatOperand(m, ea, mode)); +} + +void ldl_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setL(resolveWordOperand(m, ea, mode)); +} + +void lds_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setS(resolveWordOperand(m, ea, mode)); +} + +void ldt_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setT(resolveWordOperand(m, ea, mode)); +} + +void ldx_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setX(resolveWordOperand(m, ea, mode)); +} + +void mul_handler(Machine &m, int ea, AddressingMode mode) +{ + int val = resolveWordOperand(m, ea, mode); + m.setA(m.getA() * val); +} + +void mulf_handler(Machine &m, int ea, AddressingMode mode) +{ + m.setF(m.getF() * resolveFloatOperand(m, ea, mode)); +} + +void or_handler(Machine &m, int ea, AddressingMode mode) +{ + int val = resolveWordOperand(m, ea, mode); + m.setA(m.getA() | val); +} + +void 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 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())); +} diff --git a/simulator_SIC_XE/src/machine.cpp b/simulator_SIC_XE/src/machine.cpp index d190f6d..8180727 100644 --- a/simulator_SIC_XE/src/machine.cpp +++ b/simulator_SIC_XE/src/machine.cpp @@ -4,6 +4,7 @@ #include "opcode.h" #include "instructions.h" +#include using std::make_shared; @@ -128,23 +129,93 @@ void Machine::setWord(int address, int value) cerr << prefix << "Invalid memory address: " << address << endl; return; } + value &= 0xFFFFFF; memory[address] = static_cast(value & 0xFF); memory[address + 1] = static_cast((value >> 8) & 0xFF); memory[address + 2] = static_cast((value >> 16) & 0xFF); } -// TODO: implement proper float storage and retrieval double Machine::getFloat(int address) { - return 0.0; + if (address < 0 || address + 5 >= MEMORY_SIZE) { + cerr << prefix << "Invalid float address: " << address << endl; + return 0.0; + } + + // load 6 bytes, little-endian → 48-bit word + unsigned long long raw = + (unsigned long long)memory[address] | + ((unsigned long long)memory[address+1] << 8) | + ((unsigned long long)memory[address+2] << 16) | + ((unsigned long long)memory[address+3] << 24) | + ((unsigned long long)memory[address+4] << 32) | + ((unsigned long long)memory[address+5] << 40); + + int sign = (raw >> 47) & 0x1; + int exponent = (raw >> 40) & 0x7F; + unsigned long long frac = raw & SICF_FRAC_MASK; // 40 bits + + if (raw == 0) return 0.0; + + // value = (1 + frac/2^40) * 2^(exp - 64) + double mant = 1.0 + (double)frac / (double)(1ULL << SICF_FRAC_BITS); + int e = exponent - SICF_EXP_BIAS; + double val = std::ldexp(mant, e); // ldexp is fast enough here + return sign ? -val : val; } void Machine::setFloat(int address, double value) { - // TODO: implement proper float storage + if (address < 0 || address + 5 >= MEMORY_SIZE) { + cerr << prefix << "Invalid float address: " << address << endl; + return; + } + + if (value == 0.0) { + memory[address] = 0; + memory[address+1] = 0; + memory[address+2] = 0; + memory[address+3] = 0; + memory[address+4] = 0; + memory[address+5] = 0; + return; + } + + int sign = value < 0; + double x = sign ? -value : value; + + // normalize x to [1, 2) + int exp2 = 0; + x = std::frexp(x, &exp2); + x *= 2.0; + exp2 -= 1; + + int exp_field = exp2 + SICF_EXP_BIAS; + if (exp_field < 0) exp_field = 0; + if (exp_field > 127) exp_field = 127; + + // mantissa = (x - 1) * 2^40 + double frac_d = (x - 1.0) * (double)(1ULL << SICF_FRAC_BITS); + unsigned long long frac = (unsigned long long)(frac_d + 0.5); // round + frac &= SICF_FRAC_MASK; + + unsigned long long raw = + ((unsigned long long)sign << 47) | + ((unsigned long long)exp_field << 40) | + frac; + + // store 6 bytes little-endian + memory[address] = (unsigned char)( raw & 0xFF); + memory[address+1] = (unsigned char)((raw >> 8) & 0xFF); + memory[address+2] = (unsigned char)((raw >> 16) & 0xFF); + memory[address+3] = (unsigned char)((raw >> 24) & 0xFF); + memory[address+4] = (unsigned char)((raw >> 32) & 0xFF); + memory[address+5] = (unsigned char)((raw >> 40) & 0xFF); } + + Device &Machine::getDevice(int num) { if(num < 0 || num >= static_cast(devices.size()) || !devices[num]) { @@ -195,49 +266,48 @@ int Machine::fetch() return getByte(PC++); } -void Machine::execute() -{ - int opcode = fetch(); - InstructionType type = instructions[opcode].type; - switch (type) { - case InstructionType::TYPE1: execF1(opcode);break; - case InstructionType::TYPE2: execF2(opcode, fetch());break; - case InstructionType::TYPE3_4: // extract n and i bits - { - int ni = opcode & 0x3; - int operand = fetch(); - execSICF3F4(opcode, ni, operand); - } - break; - default: invalidOpcode(opcode); break; +void Machine::execute() { + int b1 = fetch(); + InstructionInfo &info = instructions[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 = instructions[opcode]; + int ni = b1 & NI_MASK; + + if (info34.type == InstructionType::TYPE3_4) { + int b2 = fetch(), b3 = fetch(); + int x = (b2 & 0x80) ? 1 : 0; + int b = (b2 & 0x40) ? 1 : 0; + int p = (b2 & 0x20) ? 1 : 0; + int e = (b2 & 0x10) ? 1 : 0; + + int operand; + if (ni == NI_SIC) { + // PURE SIC + operand = ((b2 & 0x7F) << 8) | b3; + } else { + // SIC/XE + operand = e + ? (((b2 & 0x0F) << 16) | (b3 << 8) | fetch()) // F4: 20-bit + : (((b2 & 0x0F) << 8) | b3); // F3: 12-bit + } + + execSICF3F4(opcode, ni, x, b, p, e, operand); + return; } + + invalidOpcode(b1); } + + + bool Machine::execF1(int opcode) { - switch (opcode) - { - case FIX: - setA(static_cast(getF())); - return true; - case FLOAT: - setF(static_cast(getA())); - return true; - case HIO: - notImplemented("HIO"); - return true; - case NORM: - notImplemented("NORM"); - return true; - case SIO: - notImplemented("SIO"); - return true; - case TIO: - notImplemented("TIO"); - return true; - default: - break; - } + undefinedHandler(opcode); return false; } @@ -255,7 +325,47 @@ 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 (instructions[opcode].handler) { + auto h = reinterpret_cast(instructions[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 (instructions[opcode].handler) { + auto h = reinterpret_cast(instructions[opcode].handler); + h(*this, ea, mode); + return true; + } + + undefinedHandler(opcode); return false; } + diff --git a/simulator_SIC_XE/src/main.cpp b/simulator_SIC_XE/src/main.cpp index 1c285b9..4171396 100644 --- a/simulator_SIC_XE/src/main.cpp +++ b/simulator_SIC_XE/src/main.cpp @@ -13,15 +13,41 @@ int main() Machine machine; cout << "Machine initialized successfully." << endl; - - // COMPUTE A + B and store result in B + + // Test 1: ADD immediate 0x0030 (ADD #48) 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; + cout << "Test 1 - Immediate ADD:" << endl; + cout << " A before: " << machine.getA() << endl; + machine.setByte(0x0, 0x19); // ADD with ni=01 (immediate addressing) + machine.setByte(0x1, 0x00); + machine.setByte(0x2, 0x30); // Immediate value 48 (0x30) machine.execute(); - cout << "After ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl; + cout << " A after ADD #48: " << machine.getA() << endl; + + // Test 2: ADD direct 0x0020 (ADD 0x20) + machine.setA(5); + machine.setPC(0x10); // Move PC for next instruction + cout << "\nTest 2 - Direct ADD:" << endl; + cout << " A before: " << machine.getA() << endl; + machine.setByte(0x20, 25); // Store value 25 at address 0x20 + machine.setByte(0x10, 0x1B); // ADD with ni=11 (simple/direct addressing) + machine.setByte(0x11, 0x00); + machine.setByte(0x12, 0x20); // Address 0x20 + machine.execute(); + cout << " A after ADD [0x20]: " << machine.getA() << endl; + + // Test 3: ADD indirect @0x0030 (ADD @0x30) + machine.setA(15); + machine.setPC(0x20); // Move PC for next instruction + cout << "\nTest 3 - Indirect ADD:" << endl; + cout << " A before: " << machine.getA() << endl; + machine.setWord(0x30, 0x40); // Address 0x30 contains address 0x40 + machine.setByte(0x40, 35); // Store value 35 at address 0x40 + machine.setByte(0x20, 0x1A); // ADD with ni=10 (indirect addressing) + machine.setByte(0x21, 0x00); + machine.setByte(0x22, 0x30); // Address 0x30 (which points to 0x40) + machine.execute(); + cout << " A after ADD @0x30: " << machine.getA() << endl; return 0; } \ No newline at end of file diff --git a/simulator_SIC_XE/src/opcode.cpp b/simulator_SIC_XE/src/opcode.cpp index febc489..04fa3b8 100644 --- a/simulator_SIC_XE/src/opcode.cpp +++ b/simulator_SIC_XE/src/opcode.cpp @@ -1,70 +1,71 @@ #include "opcode.h" #include "instructions.h" +#include "utils.h" #include InstructionInfo instructions[0xff]; void loadInstructionSet() { - instructions[ADD] = {"ADD", InstructionType::TYPE3_4, nullptr}; - instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, nullptr}; - instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; - instructions[AND] = {"AND", InstructionType::TYPE3_4, nullptr}; - instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; - instructions[COMP] = {"COMP", InstructionType::TYPE3_4, nullptr}; - instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, nullptr}; - instructions[COMPR] = {"COMPR", InstructionType::TYPE2, nullptr}; - instructions[DIV] = {"DIV", InstructionType::TYPE3_4, nullptr}; - instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, nullptr}; - instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; - instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr}; - instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr}; - instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; - instructions[J] = {"J", InstructionType::TYPE3_4, nullptr}; - instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, nullptr}; - instructions[JGT] = {"JGT", InstructionType::TYPE3_4, nullptr}; - instructions[JLT] = {"JLT", InstructionType::TYPE3_4, nullptr}; - instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, nullptr}; - instructions[LDA] = {"LDA", InstructionType::TYPE3_4, nullptr}; - instructions[LDB] = {"LDB", InstructionType::TYPE3_4, nullptr}; - instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, nullptr}; - instructions[LDF] = {"LDF", InstructionType::TYPE3_4, nullptr}; - instructions[LDL] = {"LDL", InstructionType::TYPE3_4, nullptr}; - instructions[LDS] = {"LDS", InstructionType::TYPE3_4, nullptr}; - instructions[LDT] = {"LDT", InstructionType::TYPE3_4, nullptr}; - instructions[LDX] = {"LDX", InstructionType::TYPE3_4, nullptr}; - instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; - instructions[MUL] = {"MUL", InstructionType::TYPE3_4, nullptr}; - instructions[MULF] = {"MULF", InstructionType::TYPE3_4, nullptr}; - instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; - instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr}; - instructions[OR] = {"OR", InstructionType::TYPE3_4, nullptr}; - instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr}; - instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; - instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, nullptr}; - instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; - instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; - instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; - instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; - instructions[STA] = {"STA", InstructionType::TYPE3_4, nullptr}; - instructions[STB] = {"STB", InstructionType::TYPE3_4, nullptr}; - instructions[STCH] = {"STCH", InstructionType::TYPE3_4, nullptr}; - instructions[STF] = {"STF", InstructionType::TYPE3_4, nullptr}; - instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; - instructions[STL] = {"STL", InstructionType::TYPE3_4, nullptr}; - instructions[STS] = {"STS", InstructionType::TYPE3_4, nullptr}; - instructions[STSW] = {"STSW", InstructionType::TYPE3_4, nullptr}; - instructions[STT] = {"STT", InstructionType::TYPE3_4, nullptr}; - instructions[STX] = {"STX", InstructionType::TYPE3_4, nullptr}; - instructions[SUB] = {"SUB", InstructionType::TYPE3_4, nullptr}; - instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, nullptr}; - instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; - instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; - instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; - instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr}; - instructions[TIX] = {"TIX", InstructionType::TYPE3_4, nullptr}; - instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; - instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr}; + instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast(add_handler)}; + instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast(addf_handler)}; + instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; + instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast(and_handler)}; + instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; + instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast(comp_handler)}; + instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast(compf_handler)}; + instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast(compr_handler)}; + instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast(div_handler)}; + instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast(divf_handler)}; + instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; + instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr}; + instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr}; + instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; + instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast(j_handler)}; + instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast(jeq_handler)}; + instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast(jgt_handler)}; + instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast(jlt_handler)}; + instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast(jsub_handler)}; + instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast(lda_handler)}; + instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast(ldb_handler)}; + instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast(ldch_handler)}; + instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast(ldf_handler)}; + instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast(ldl_handler)}; + instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast(lds_handler)}; + instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast(ldt_handler)}; + instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast(ldx_handler)}; + instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; + instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast(mul_handler)}; + instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast(mulf_handler)}; + instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; + instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr}; + instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast(or_handler)}; + instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr}; + instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; + instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast(rsub_handler)}; + instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; + instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; + instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; + instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; + instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast(sta_handler)}; + instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast(stb_handler)}; + instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast(stch_handler)}; + instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast(stf_handler)}; + instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; + instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast(stl_handler)}; + instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast(sts_handler)}; + instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast(stsw_handler)}; + instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast(stt_handler)}; + instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast(stx_handler)}; + instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast(sub_handler)}; + instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast(subf_handler)}; + instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; + instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; + instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; + instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr}; + instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast(tix_handler)}; + instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; + instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr}; // Mark uninitialized opcodes as INVALID for (int i = 0; i < 0xff; ++i) { @@ -72,4 +73,15 @@ void loadInstructionSet() instructions[i] = {"INVALID", InstructionType::INVALID, nullptr}; } } -} \ No newline at end of file +} + +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 + } +} From 6b3f4989ae63e5fff9502b457d0748bbe6bc33c8 Mon Sep 17 00:00:00 2001 From: zanostro Date: Wed, 12 Nov 2025 12:13:26 +0100 Subject: [PATCH 02/15] added new instructions and new test --- simulator_SIC_XE/include/instructions.h | 10 ++- simulator_SIC_XE/include/machine.h | 16 ++++- simulator_SIC_XE/src/instructions.cpp | 12 +++- simulator_SIC_XE/src/machine.cpp | 43 +++++++++++ simulator_SIC_XE/src/main.cpp | 95 ++++++++++++++++--------- simulator_SIC_XE/src/opcode.cpp | 4 +- 6 files changed, 142 insertions(+), 38 deletions(-) diff --git a/simulator_SIC_XE/include/instructions.h b/simulator_SIC_XE/include/instructions.h index e085a9d..61cc452 100644 --- a/simulator_SIC_XE/include/instructions.h +++ b/simulator_SIC_XE/include/instructions.h @@ -5,8 +5,16 @@ 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); - +/* IDEJE ZA SIC_XE_XE :)*/ +// void nop(Machine& m); // Type 2 instruction handlers void addr_handler(Machine& m, int r1, int r2); diff --git a/simulator_SIC_XE/include/machine.h b/simulator_SIC_XE/include/machine.h index 9ee4fca..0bcb448 100644 --- a/simulator_SIC_XE/include/machine.h +++ b/simulator_SIC_XE/include/machine.h @@ -5,6 +5,9 @@ #include #include #include +#include +#include + #include "constants.h" #include "device.h" @@ -23,6 +26,7 @@ using std::cout; class Machine { public: Machine(); + Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; } ~Machine(); int getA() const { return A; } @@ -74,7 +78,6 @@ 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(); @@ -83,6 +86,12 @@ public: bool execF2(int opcode, int operand); bool execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand); + // Execution and speed control + int getSpeed() const; + void setSpeed(int kHz); + void start(); + void stop(); + // error handling methods void notImplemented(string mnemonic); void invalidOpcode(int opcode); @@ -102,6 +111,11 @@ private: std::vector> devices; // fallback device returned when device slot is empty/invalid Device fallbackDevice; + + // Execution control + std::atomic running{false}; + std::atomic speedkHz{1}; // Default 1 kHz + void tick(); // simulate a clock tick }; diff --git a/simulator_SIC_XE/src/instructions.cpp b/simulator_SIC_XE/src/instructions.cpp index a0a96dd..85e3bd9 100644 --- a/simulator_SIC_XE/src/instructions.cpp +++ b/simulator_SIC_XE/src/instructions.cpp @@ -63,8 +63,18 @@ inline int resolveJumpTarget(Machine& m, int ea, AddressingMode mode) } } +void fix_handler(Machine &m) +{ + m.setA(static_cast(m.getF())); +} -void addr_handler(Machine& m, int r1, int r2) { +void float_handler(Machine &m) +{ + m.setF(static_cast(m.getA())); +} + +void addr_handler(Machine &m, int r1, int r2) +{ m.setReg(r2, m.getReg(r1) + m.getReg(r2)); } diff --git a/simulator_SIC_XE/src/machine.cpp b/simulator_SIC_XE/src/machine.cpp index 8180727..e4ee364 100644 --- a/simulator_SIC_XE/src/machine.cpp +++ b/simulator_SIC_XE/src/machine.cpp @@ -5,6 +5,7 @@ #include "opcode.h" #include "instructions.h" #include +#include using std::make_shared; @@ -29,6 +30,17 @@ Machine::~Machine() } } +int Machine::getSpeed() const +{ + return speedkHz.load(); +} + +void Machine::setSpeed(int kHz) +{ + speedkHz.store(kHz); +} + +// TODO: implement errors void Machine::notImplemented(string mnemonic) { cout << prefix << "Not implemented: " << mnemonic << endl; @@ -39,6 +51,7 @@ void Machine::invalidOpcode(int opcode) cout << prefix << "Invalid opcode: " << opcode << endl; } + void Machine::invalidAddressing() { cout << prefix << "Invalid addressing mode" << endl; @@ -54,6 +67,15 @@ void Machine::undefinedHandler(int opcode) cout << prefix << "Undefined handler for opcode: " << opcode << endl; } +void Machine::tick() +{ + const int speed = speedkHz.load(); + if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick"); + + const auto delay = std::chrono::microseconds(1000 / speed); + std::this_thread::sleep_for(delay); +} + int Machine::getReg(int regNum) const { switch (regNum) { @@ -307,6 +329,11 @@ void Machine::execute() { bool Machine::execF1(int opcode) { + if (instructions[opcode].handler) { + auto handler = reinterpret_cast(instructions[opcode].handler); + handler(*this); + return true; + } undefinedHandler(opcode); return false; } @@ -369,3 +396,19 @@ bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int op return false; } +void Machine::start() +{ + running.store(true); + + // Main execution loop + // TODO: consider running in separate thread + while (running.load()) { + execute(); + tick(); + } +} + +void Machine::stop() +{ + running.store(false); +} diff --git a/simulator_SIC_XE/src/main.cpp b/simulator_SIC_XE/src/main.cpp index 4171396..16dcd0c 100644 --- a/simulator_SIC_XE/src/main.cpp +++ b/simulator_SIC_XE/src/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "machine.h" #include "file_device.h" #include "opcode.h" @@ -12,42 +14,69 @@ int main() loadInstructionSet(); Machine machine; - cout << "Machine initialized successfully." << endl; + cout << "SIC/XE Program: Accumulator Loop" << endl; - // Test 1: ADD immediate 0x0030 (ADD #48) - machine.setA(10); - cout << "Test 1 - Immediate ADD:" << endl; - cout << " A before: " << machine.getA() << endl; - machine.setByte(0x0, 0x19); // ADD with ni=01 (immediate addressing) - machine.setByte(0x1, 0x00); - machine.setByte(0x2, 0x30); // Immediate value 48 (0x30) - machine.execute(); - cout << " A after ADD #48: " << machine.getA() << endl; + const int TEMP_ADDR = 0x50; + const int LOOP_ADDR = 0x03; - // Test 2: ADD direct 0x0020 (ADD 0x20) - machine.setA(5); - machine.setPC(0x10); // Move PC for next instruction - cout << "\nTest 2 - Direct ADD:" << endl; - cout << " A before: " << machine.getA() << endl; - machine.setByte(0x20, 25); // Store value 25 at address 0x20 - machine.setByte(0x10, 0x1B); // ADD with ni=11 (simple/direct addressing) - machine.setByte(0x11, 0x00); - machine.setByte(0x12, 0x20); // Address 0x20 - machine.execute(); - cout << " A after ADD [0x20]: " << machine.getA() << endl; + // clear TEMP + machine.setByte(TEMP_ADDR, 0); - // Test 3: ADD indirect @0x0030 (ADD @0x30) - machine.setA(15); - machine.setPC(0x20); // Move PC for next instruction - cout << "\nTest 3 - Indirect ADD:" << endl; - cout << " A before: " << machine.getA() << endl; - machine.setWord(0x30, 0x40); // Address 0x30 contains address 0x40 - machine.setByte(0x40, 35); // Store value 35 at address 0x40 - machine.setByte(0x20, 0x1A); // ADD with ni=10 (indirect addressing) - machine.setByte(0x21, 0x00); - machine.setByte(0x22, 0x30); // Address 0x30 (which points to 0x40) - machine.execute(); - cout << " A after ADD @0x30: " << machine.getA() << endl; + // Program (addresses): + // 0x00 LDA #1 + // 0x03 LDB TEMP + // 0x06 ADDR A,B + // 0x08 RMO B,A + // 0x0A STA TEMP + // 0x0D J LOOP + + // LDA #1 + machine.setByte(0x00, 0x01); + machine.setByte(0x01, 0x00); + machine.setByte(0x02, 0x01); + + // LDB TEMP + machine.setByte(0x03, 0x6B); + machine.setByte(0x04, 0x00); + machine.setByte(0x05, TEMP_ADDR); + + // ADDR A,B + machine.setByte(0x06, 0x90); + machine.setByte(0x07, 0x03); + + // RMO B,A + machine.setByte(0x08, 0xAC); + machine.setByte(0x09, 0x30); + + // STA TEMP + machine.setByte(0x0A, 0x0F); + machine.setByte(0x0B, 0x00); + machine.setByte(0x0C, TEMP_ADDR); + + // J LOOP + machine.setByte(0x0D, 0x3F); + machine.setByte(0x0E, 0x00); + machine.setByte(0x0F, LOOP_ADDR); + + machine.setPC(0x00); + + cout << "Program loaded. TEMP at 0x" << std::hex << TEMP_ADDR << std::dec << endl; + + // run a few iterations + for (int i = 0; i < 10; ++i) { + cout << "Iter " << (i + 1) << ": A=" << machine.getA() + << " B=" << machine.getB() + << " TEMP=" << machine.getByte(TEMP_ADDR); + + // advance the program by executing the next 6 instructions + for (int k = 0; k < 6; ++k) machine.execute(); + + cout << " -> A=" << machine.getA() + << " B=" << machine.getB() + << " TEMP=" << machine.getByte(TEMP_ADDR) << "\n"; + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } return 0; } \ No newline at end of file diff --git a/simulator_SIC_XE/src/opcode.cpp b/simulator_SIC_XE/src/opcode.cpp index 04fa3b8..6095307 100644 --- a/simulator_SIC_XE/src/opcode.cpp +++ b/simulator_SIC_XE/src/opcode.cpp @@ -18,8 +18,8 @@ void loadInstructionSet() instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast(div_handler)}; instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast(divf_handler)}; instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; - instructions[FIX] = {"FIX", InstructionType::TYPE1, nullptr}; - instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, nullptr}; + instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast(fix_handler)}; + instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast(float_handler)}; instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast(j_handler)}; instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast(jeq_handler)}; From 42e884acedeb557adb43bb214e3165801fad7ace Mon Sep 17 00:00:00 2001 From: zanostro Date: Wed, 12 Nov 2025 19:04:16 +0100 Subject: [PATCH 03/15] Ignore Qt Creator user file CMakeLists.txt.user --- simulator_SIC_XE/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/simulator_SIC_XE/.gitignore b/simulator_SIC_XE/.gitignore index 6b70294..70b91f4 100644 --- a/simulator_SIC_XE/.gitignore +++ b/simulator_SIC_XE/.gitignore @@ -71,6 +71,7 @@ xcuserdata/ *.moc.cpp *.qm *.prl +CMakeLists.txt.user # OS generated files .DS_Store From 8a6e916876428953c15b971a05439def3c7ca2a4 Mon Sep 17 00:00:00 2001 From: zanostro Date: Wed, 12 Nov 2025 19:05:16 +0100 Subject: [PATCH 04/15] connected to gui --- simulator_SIC_XE/CMakeLists.txt | 9 ++-- simulator_SIC_XE/gui/qt/CMakeLists.txt | 53 +++++++++++++++++++ simulator_SIC_XE/gui/qt/MachineController.cpp | 32 +++++++++++ simulator_SIC_XE/gui/qt/MachineController.h | 27 ++++++++++ simulator_SIC_XE/gui/qt/MainWindow.cpp | 20 +++++++ simulator_SIC_XE/gui/qt/MainWindow.h | 18 +++++++ simulator_SIC_XE/gui/qt/main.cpp | 9 ++++ 7 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 simulator_SIC_XE/gui/qt/CMakeLists.txt create mode 100644 simulator_SIC_XE/gui/qt/MachineController.cpp create mode 100644 simulator_SIC_XE/gui/qt/MachineController.h create mode 100644 simulator_SIC_XE/gui/qt/MainWindow.cpp create mode 100644 simulator_SIC_XE/gui/qt/MainWindow.h create mode 100644 simulator_SIC_XE/gui/qt/main.cpp diff --git a/simulator_SIC_XE/CMakeLists.txt b/simulator_SIC_XE/CMakeLists.txt index 242ebd3..fe0a86a 100644 --- a/simulator_SIC_XE/CMakeLists.txt +++ b/simulator_SIC_XE/CMakeLists.txt @@ -4,7 +4,7 @@ project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Put all build outputs under target/bin as requested +# Put all build outputs under target/bin set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR}) @@ -28,8 +28,7 @@ if(EXISTS "${PROJECT_SOURCE_DIR}/src/main.cpp") target_link_libraries(simulator_exec PRIVATE simulator_lib) endif() -# Convenience target: `cmake --build build --target run` -# This target will build `simulator_exec` (if present) and then execute it. + if(TARGET simulator_exec) add_custom_target(run DEPENDS simulator_exec @@ -43,3 +42,7 @@ 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() diff --git a/simulator_SIC_XE/gui/qt/CMakeLists.txt b/simulator_SIC_XE/gui/qt/CMakeLists.txt new file mode 100644 index 0000000..0b42058 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/CMakeLists.txt @@ -0,0 +1,53 @@ +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}) + +# Use top-level include folder (works when added with add_subdirectory) +target_include_directories(simulator_qt PRIVATE ${CMAKE_SOURCE_DIR}/include) + +# Link to core library target (must be defined by top-level CMake) +target_link_libraries(simulator_qt PRIVATE simulator_lib ${QT_LIB}) + +# Place runtime binary under repo/target/bin to match project layout +set_target_properties(simulator_qt PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/target/bin +) \ No newline at end of file diff --git a/simulator_SIC_XE/gui/qt/MachineController.cpp b/simulator_SIC_XE/gui/qt/MachineController.cpp new file mode 100644 index 0000000..f066e23 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MachineController.cpp @@ -0,0 +1,32 @@ +#include "MachineController.h" +#include + +MachineController::MachineController(QObject *parent) + : QObject(parent) +{ +} + +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() { + emit tick(); +} + +void MachineController::runLoop() { + while (m_running.load()) { + emit tick(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} diff --git a/simulator_SIC_XE/gui/qt/MachineController.h b/simulator_SIC_XE/gui/qt/MachineController.h new file mode 100644 index 0000000..bc29329 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MachineController.h @@ -0,0 +1,27 @@ +#ifndef MACHINECONTROLLER_H +#define MACHINECONTROLLER_H + +#include +#include +#include + +class MachineController : public QObject { + Q_OBJECT +public: + explicit MachineController(QObject *parent = nullptr); + ~MachineController() override; + + void start(); + void stop(); + void step(); + +signals: + void tick(); + +private: + void runLoop(); + std::atomic m_running{false}; + std::thread m_thread; +}; + +#endif // MACHINECONTROLLER_H diff --git a/simulator_SIC_XE/gui/qt/MainWindow.cpp b/simulator_SIC_XE/gui/qt/MainWindow.cpp new file mode 100644 index 0000000..09a31c2 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MainWindow.cpp @@ -0,0 +1,20 @@ +#include "MainWindow.h" +#include "MachineController.h" +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + QWidget *central = new QWidget(this); + auto *layout = new QVBoxLayout(central); + auto *label = new QLabel("SIC/XE Simulator — GUI stub", central); + label->setAlignment(Qt::AlignCenter); + layout->addWidget(label); + setCentralWidget(central); + + m_controller = new MachineController(this); + setWindowTitle("SIC/XE Simulator"); +} + +MainWindow::~MainWindow() = default; diff --git a/simulator_SIC_XE/gui/qt/MainWindow.h b/simulator_SIC_XE/gui/qt/MainWindow.h new file mode 100644 index 0000000..a67c8d1 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/MainWindow.h @@ -0,0 +1,18 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +class MachineController; + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow() override; + +private: + MachineController *m_controller = nullptr; +}; + +#endif // MAINWINDOW_H diff --git a/simulator_SIC_XE/gui/qt/main.cpp b/simulator_SIC_XE/gui/qt/main.cpp new file mode 100644 index 0000000..74143ee --- /dev/null +++ b/simulator_SIC_XE/gui/qt/main.cpp @@ -0,0 +1,9 @@ +#include +#include "MainWindow.h" + +int main(int argc, char **argv) { + QApplication app(argc, argv); + MainWindow w; + w.show(); + return app.exec(); +} \ No newline at end of file From d77a32e6e61811f57c40280dcd154ae41c48e716 Mon Sep 17 00:00:00 2001 From: zanostro Date: Wed, 12 Nov 2025 19:42:59 +0100 Subject: [PATCH 05/15] connected to gui --- simulator_SIC_XE/.gitignore | 1 - simulator_SIC_XE/Makefile | 59 ++++++++++++++++++++++++++ simulator_SIC_XE/gui/qt/CMakeLists.txt | 9 ++-- simulator_SIC_XE/gui/qt/MainWindow.cpp | 20 --------- simulator_SIC_XE/gui/qt/main.cpp | 2 +- simulator_SIC_XE/gui/qt/mainwindow.cpp | 14 ++++++ simulator_SIC_XE/gui/qt/mainwindow.h | 22 ++++++++++ simulator_SIC_XE/gui/qt/mainwindow.ui | 25 +++++++++++ 8 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 simulator_SIC_XE/Makefile delete mode 100644 simulator_SIC_XE/gui/qt/MainWindow.cpp create mode 100644 simulator_SIC_XE/gui/qt/mainwindow.cpp create mode 100644 simulator_SIC_XE/gui/qt/mainwindow.h create mode 100644 simulator_SIC_XE/gui/qt/mainwindow.ui diff --git a/simulator_SIC_XE/.gitignore b/simulator_SIC_XE/.gitignore index 70b91f4..4cbe372 100644 --- a/simulator_SIC_XE/.gitignore +++ b/simulator_SIC_XE/.gitignore @@ -7,7 +7,6 @@ CMakeCache.txt CMakeFiles/ CMakeScripts/ cmake_install.cmake -Makefile *.cmake !CMakeLists.txt diff --git a/simulator_SIC_XE/Makefile b/simulator_SIC_XE/Makefile new file mode 100644 index 0000000..6d5d9ea --- /dev/null +++ b/simulator_SIC_XE/Makefile @@ -0,0 +1,59 @@ +# Simple Makefile wrapper to configure, build and run the CMake project. +# Usage: +# make # builds (default) +# make build # configure + build +# make run # build (if needed) and run the executable +# make clean # run CMake clean (or remove build files) +# make distclean # remove build dir and generated targets + +CMAKE ?= cmake +BUILD_DIR := build +CMAKE_BUILD_TYPE ?= Release +TARGET := target/bin/simulator_exec +GUI_TARGET := target/bin/simulator_qt + +.PHONY: all configure build run clean distclean + +all: build + +configure: + @echo "Configuring (build dir: $(BUILD_DIR), type: $(CMAKE_BUILD_TYPE))" + $(CMAKE) -S . -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) + +build: configure + @echo "Building..." + $(CMAKE) --build $(BUILD_DIR) -j$(shell nproc) + +run: build + @echo "Running primary target..." + # Prefer GUI if available, otherwise fall back to console executable + @if [ -x "$(GUI_TARGET)" ]; then \ + echo "Launching GUI: $(GUI_TARGET)"; \ + sh -c 'nohup env QT_QPA_PLATFORM=xcb ./$(GUI_TARGET) >/dev/null 2>&1 & echo $! > "$(BUILD_DIR)/simulator_qt.pid"'; \ + elif [ -x "$(TARGET)" ]; then \ + @./$(TARGET); \ + else \ + echo "No runnable target found (tried $(GUI_TARGET) and $(TARGET))."; exit 1; \ + fi + +.PHONY: run-gui +run-gui: build + @echo "Running GUI target ($(GUI_TARGET))" + @if [ -x "$(GUI_TARGET)" ]; then \ + echo "Starting GUI..."; ./$(GUI_TARGET) -platform xcb; \ + else \ + echo "GUI executable not found: $(GUI_TARGET)"; exit 1; \ + fi + +.PHONY: build-gui +build-gui: configure + @echo "Building GUI (and core)..." + $(CMAKE) --build $(BUILD_DIR) -j$(shell nproc) --target simulator_qt || true + +clean: + @echo "Cleaning build (CMake clean)..." + -$(CMAKE) --build $(BUILD_DIR) --target clean || true + +distclean: + @echo "Removing build artifacts and generated files..." + -rm -rf $(BUILD_DIR) CMakeFiles CMakeCache.txt cmake_install.cmake target/bin/* diff --git a/simulator_SIC_XE/gui/qt/CMakeLists.txt b/simulator_SIC_XE/gui/qt/CMakeLists.txt index 0b42058..cf8072a 100644 --- a/simulator_SIC_XE/gui/qt/CMakeLists.txt +++ b/simulator_SIC_XE/gui/qt/CMakeLists.txt @@ -30,19 +30,20 @@ endif() set(GUI_SRCS main.cpp - MainWindow.cpp + mainwindow.cpp MachineController.cpp ) set(GUI_HDRS - MainWindow.h + mainwindow.h MachineController.h ) add_executable(simulator_qt ${GUI_SRCS} ${GUI_HDRS}) -# Use top-level include folder (works when added with add_subdirectory) -target_include_directories(simulator_qt PRIVATE ${CMAKE_SOURCE_DIR}/include) +# 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}) diff --git a/simulator_SIC_XE/gui/qt/MainWindow.cpp b/simulator_SIC_XE/gui/qt/MainWindow.cpp deleted file mode 100644 index 09a31c2..0000000 --- a/simulator_SIC_XE/gui/qt/MainWindow.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "MainWindow.h" -#include "MachineController.h" -#include -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) -{ - QWidget *central = new QWidget(this); - auto *layout = new QVBoxLayout(central); - auto *label = new QLabel("SIC/XE Simulator — GUI stub", central); - label->setAlignment(Qt::AlignCenter); - layout->addWidget(label); - setCentralWidget(central); - - m_controller = new MachineController(this); - setWindowTitle("SIC/XE Simulator"); -} - -MainWindow::~MainWindow() = default; diff --git a/simulator_SIC_XE/gui/qt/main.cpp b/simulator_SIC_XE/gui/qt/main.cpp index 74143ee..877769b 100644 --- a/simulator_SIC_XE/gui/qt/main.cpp +++ b/simulator_SIC_XE/gui/qt/main.cpp @@ -1,5 +1,5 @@ #include -#include "MainWindow.h" +#include "mainwindow.h" int main(int argc, char **argv) { QApplication app(argc, argv); diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp new file mode 100644 index 0000000..49d64fc --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -0,0 +1,14 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/simulator_SIC_XE/gui/qt/mainwindow.h b/simulator_SIC_XE/gui/qt/mainwindow.h new file mode 100644 index 0000000..9353441 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui new file mode 100644 index 0000000..1a59260 --- /dev/null +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -0,0 +1,25 @@ + + + + + + MainWindow + + + + 0 + 0 + 640 + 480 + + + + MainWindow + + + + + + + + From 43c8fb2cce798bfd17aa713bec61c59123f0d8c8 Mon Sep 17 00:00:00 2001 From: zanostro Date: Wed, 12 Nov 2025 19:43:12 +0100 Subject: [PATCH 06/15] removed old files --- simulator_SIC_XE/gui/qt/MainWindow.h | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 simulator_SIC_XE/gui/qt/MainWindow.h diff --git a/simulator_SIC_XE/gui/qt/MainWindow.h b/simulator_SIC_XE/gui/qt/MainWindow.h deleted file mode 100644 index a67c8d1..0000000 --- a/simulator_SIC_XE/gui/qt/MainWindow.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include - -class MachineController; - -class MainWindow : public QMainWindow { - Q_OBJECT -public: - explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow() override; - -private: - MachineController *m_controller = nullptr; -}; - -#endif // MAINWINDOW_H From 42737c0a6680b2bfb27a6197df5b41a124169983 Mon Sep 17 00:00:00 2001 From: zanostro Date: Thu, 13 Nov 2025 20:22:50 +0100 Subject: [PATCH 07/15] qol --- simulator_SIC_XE/gui/qt/MachineController.cpp | 33 ++++++-- simulator_SIC_XE/gui/qt/MachineController.h | 7 +- simulator_SIC_XE/gui/qt/main.cpp | 2 + simulator_SIC_XE/gui/qt/mainwindow.ui | 81 ++++++++++++++++--- simulator_SIC_XE/include/machine.h | 2 +- 5 files changed, 107 insertions(+), 18 deletions(-) diff --git a/simulator_SIC_XE/gui/qt/MachineController.cpp b/simulator_SIC_XE/gui/qt/MachineController.cpp index f066e23..0ea4178 100644 --- a/simulator_SIC_XE/gui/qt/MachineController.cpp +++ b/simulator_SIC_XE/gui/qt/MachineController.cpp @@ -1,9 +1,14 @@ #include "MachineController.h" +#include "../../include/machine.h" #include +#include -MachineController::MachineController(QObject *parent) - : QObject(parent) +MachineController::MachineController(std::shared_ptr machine, QObject *parent) + : QObject(parent), m_machine(std::move(machine)) { + if (!m_machine) { + m_machine = std::make_shared(); + } } MachineController::~MachineController() { @@ -21,12 +26,30 @@ void MachineController::stop() { } void MachineController::step() { - emit tick(); + try { + if (m_machine) { + m_machine->execute(); + m_machine->tick(); + emit tick(); + } + } catch (const std::exception &e) { + emit error(QString::fromStdString(e.what())); + } } void MachineController::runLoop() { while (m_running.load()) { - emit tick(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + try { + if (m_machine) { + m_machine->execute(); + m_machine->tick(); + emit tick(); + } + } catch (const std::exception &e) { + emit error(QString::fromStdString(e.what())); + // Stop on fatal error + m_running.store(false); + break; + } } } diff --git a/simulator_SIC_XE/gui/qt/MachineController.h b/simulator_SIC_XE/gui/qt/MachineController.h index bc29329..fa904a3 100644 --- a/simulator_SIC_XE/gui/qt/MachineController.h +++ b/simulator_SIC_XE/gui/qt/MachineController.h @@ -4,11 +4,14 @@ #include #include #include +#include + +class Machine; class MachineController : public QObject { Q_OBJECT public: - explicit MachineController(QObject *parent = nullptr); + explicit MachineController(std::shared_ptr machine = nullptr, QObject *parent = nullptr); ~MachineController() override; void start(); @@ -17,11 +20,13 @@ public: signals: void tick(); + void error(const QString &msg); private: void runLoop(); std::atomic m_running{false}; std::thread m_thread; + std::shared_ptr m_machine; }; #endif // MACHINECONTROLLER_H diff --git a/simulator_SIC_XE/gui/qt/main.cpp b/simulator_SIC_XE/gui/qt/main.cpp index 877769b..f9b6517 100644 --- a/simulator_SIC_XE/gui/qt/main.cpp +++ b/simulator_SIC_XE/gui/qt/main.cpp @@ -1,7 +1,9 @@ #include #include "mainwindow.h" +#include "../../include/opcode.h" int main(int argc, char **argv) { + loadInstructionSet(); QApplication app(argc, argv); MainWindow w; w.show(); diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui index 1a59260..9f505ab 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.ui +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -1,25 +1,84 @@ - + - - - MainWindow - + 0 0 - 640 - 480 + 1137 + 649 MainWindow - - - + + + + + 0 + 0 + 231 + 601 + + + + + + + 230 + 0 + 891 + 601 + + + + + + 0 + 0 + 881 + 181 + + + + Register values + + + + + 0 + 20 + 881 + 161 + + + + + + + TextLabel + + + + + + + + + + + + 0 + 0 + 1137 + 20 + + + + - + diff --git a/simulator_SIC_XE/include/machine.h b/simulator_SIC_XE/include/machine.h index 0bcb448..b92934e 100644 --- a/simulator_SIC_XE/include/machine.h +++ b/simulator_SIC_XE/include/machine.h @@ -91,6 +91,7 @@ public: void setSpeed(int kHz); void start(); void stop(); + void tick(); // error handling methods void notImplemented(string mnemonic); @@ -115,7 +116,6 @@ private: // Execution control std::atomic running{false}; std::atomic speedkHz{1}; // Default 1 kHz - void tick(); // simulate a clock tick }; From c91899306035904e99f19a6a837c5e190f0004cf Mon Sep 17 00:00:00 2001 From: zanostro Date: Thu, 13 Nov 2025 22:44:11 +0100 Subject: [PATCH 08/15] first gui --- simulator_SIC_XE/gui/qt/mainwindow.cpp | 402 ++++++++++++++++- simulator_SIC_XE/gui/qt/mainwindow.h | 27 ++ simulator_SIC_XE/gui/qt/mainwindow.ui | 575 +++++++++++++++++++++++-- simulator_SIC_XE/include/utils.h | 15 + simulator_SIC_XE/src/instructions.cpp | 33 ++ simulator_SIC_XE/src/opcode.cpp | 8 +- 6 files changed, 1027 insertions(+), 33 deletions(-) diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp index 49d64fc..e024b67 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.cpp +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -1,14 +1,414 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "MachineController.h" +#include "../../include/machine.h" +#include "../../include/instructions.h" + +#include +#include +#include +#include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + m_machine(std::make_shared()), + m_controller(std::make_unique(m_machine, this)) { ui->setupUi(this); + + ui->regA_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this)); + ui->regB_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this)); + ui->regS_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this)); + ui->regT_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this)); + ui->regX_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this)); + // unsigned 24 bit + ui->regL_dec_field->setValidator(new QIntValidator(0, 16777215, this)); + ui->regPC_dec_field->setValidator(new QIntValidator(0, 16777215, this)); + ui->regSW_dec_field->setValidator(new QIntValidator(0, 16777215, this)); + // float + ui->regF_dec_field->setValidator(new QDoubleValidator(-3.402823e38, 3.402823e38, 6, this)); + + QRegularExpressionValidator* hexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,6}$"), this); + ui->regA_hex_field->setValidator(hexValidator); + ui->regB_hex_field->setValidator(hexValidator); + ui->regX_hex_field->setValidator(hexValidator); + ui->regS_hex_field->setValidator(hexValidator); + ui->regT_hex_field->setValidator(hexValidator); + ui->regL_hex_field->setValidator(hexValidator); + ui->regPC_hex_field->setValidator(hexValidator); + ui->regSW_hex_field->setValidator(hexValidator); + + QRegularExpressionValidator* binValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,24}$"), this); + ui->regA_bin_field->setValidator(binValidator); + ui->regB_bin_field->setValidator(binValidator); + ui->regX_bin_field->setValidator(binValidator); + ui->regS_bin_field->setValidator(binValidator); + ui->regT_bin_field->setValidator(binValidator); + ui->regL_bin_field->setValidator(binValidator); + ui->regPC_bin_field->setValidator(binValidator); + ui->regSW_bin_field->setValidator(binValidator); + + QRegularExpressionValidator* floatHexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,12}$"), this); + ui->regF_hex_field->setValidator(floatHexValidator); + + QRegularExpressionValidator* floatBinValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,48}$"), this); + ui->regF_bin_field->setValidator(floatBinValidator); + + + connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays); + + connectRegisterFields(); + + connect(ui->StartBtn, &QPushButton::clicked, this, &MainWindow::startExecution); + connect(ui->StopBtn, &QPushButton::clicked, this, &MainWindow::stopExecution); + connect(ui->StepBtn, &QPushButton::clicked, this, &MainWindow::stepExecution); + + loadDemoProgram(); + + updateRegisterDisplays(); } MainWindow::~MainWindow() { delete ui; } + +void MainWindow::updateRegisterDisplays() +{ + if (!m_machine) return; + + // Update all register display formats (decimal, hex, binary) + updateAllFormatsForRegister("regA", m_machine->getA()); + updateAllFormatsForRegister("regB", m_machine->getB()); + updateAllFormatsForRegister("regX", m_machine->getX()); + updateAllFormatsForRegister("regS", m_machine->getS()); + updateAllFormatsForRegister("regT", m_machine->getT()); + updateAllFormatsForRegister("regL", m_machine->getL()); + updateAllFormatsForRegister("regPC", m_machine->getPC()); + updateAllFormatsForRegister("regSW", m_machine->getSW()); + updateFloatRegisterFormats("regF", m_machine->getF()); +} + +void MainWindow::updateSingleRegisterDisplay(const QString& fieldName, int value) +{ + QLineEdit* field = findChild(fieldName); + if (field) { + // Only update if the field doesn't have focus (to avoid interfering with user input) + if (!field->hasFocus()) { + field->setText(QString::number(value)); + } + } +} + +void MainWindow::updateAllFormatsForRegister(const QString& regPrefix, int value) +{ + // Update decimal field + QLineEdit* decField = findChild(regPrefix + "_dec_field"); + if (decField && !decField->hasFocus()) { + decField->setText(QString::number(value)); + } + + // Update hex field + QLineEdit* hexField = findChild(regPrefix + "_hex_field"); + if (hexField && !hexField->hasFocus()) { + // Convert to 24-bit representation, handle negative numbers + unsigned int unsignedValue = static_cast(value) & 0xFFFFFF; + hexField->setText(QString("0x%1").arg(unsignedValue, 6, 16, QChar('0')).toUpper()); + } + + // Update binary field + QLineEdit* binField = findChild(regPrefix + "_bin_field"); + if (binField && !binField->hasFocus()) { + // Convert to 24-bit binary representation + unsigned int unsignedValue = static_cast(value) & 0xFFFFFF; + QString binaryStr = QString::number(unsignedValue, 2); + // Pad to 24 bits + binaryStr = binaryStr.rightJustified(24, '0'); + binField->setText(binaryStr); + } +} + +void MainWindow::updateFloatRegisterFormats(const QString& regPrefix, double value) +{ + // Update decimal field + QLineEdit* decField = findChild(regPrefix + "_dec_field"); + if (decField && !decField->hasFocus()) { + decField->setText(QString::number(value, 'g', 10)); + } + + // Update hex field (48-bit float representation) + QLineEdit* hexField = findChild(regPrefix + "_hex_field"); + if (hexField && !hexField->hasFocus()) { + // Convert double to 48-bit hex representation + // For SIC/XE, we need to convert to the 48-bit float format + uint64_t* intPtr = reinterpret_cast(&value); + uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits + hexField->setText(QString("0x%1").arg(bits48, 12, 16, QChar('0')).toUpper()); + } + + // Update binary field (48-bit float representation) + QLineEdit* binField = findChild(regPrefix + "_bin_field"); + if (binField && !binField->hasFocus()) { + // Convert double to 48-bit binary representation + uint64_t* intPtr = reinterpret_cast(&value); + uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits + QString binaryStr = QString::number(bits48, 2); + // Pad to 48 bits + binaryStr = binaryStr.rightJustified(48, '0'); + binField->setText(binaryStr); + } +} + +void MainWindow::connectRegisterFields() +{ + // Connect decimal register fields to update machine registers when changed + connect(ui->regA_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regB_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regX_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regS_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regT_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regL_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regPC_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regSW_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + connect(ui->regF_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + + // Connect hex register fields + QLineEdit* hexFields[] = { + ui->regA_hex_field, ui->regB_hex_field, ui->regX_hex_field, + ui->regS_hex_field, ui->regT_hex_field, ui->regL_hex_field, + ui->regPC_hex_field, ui->regSW_hex_field, ui->regF_hex_field + }; + for (auto* field : hexFields) { + if (field) { + connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + } + } + + // Connect binary register fields + QLineEdit* binFields[] = { + ui->regA_bin_field, ui->regB_bin_field, ui->regX_bin_field, + ui->regS_bin_field, ui->regT_bin_field, ui->regL_bin_field, + ui->regPC_bin_field, ui->regSW_bin_field, ui->regF_bin_field + }; + for (auto* field : binFields) { + if (field) { + connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged); + } + } +} + +void MainWindow::onRegisterFieldChanged() +{ + if (!m_machine) return; + + QLineEdit* field = qobject_cast(sender()); + if (!field) return; + + QString objectName = field->objectName(); + QString regName = objectName.split('_')[0]; + + + if (regName == "regF") { + handleFloatRegisterFieldChanged(field, objectName); + return; + } + + // Handle integer registers + int value = 0; + bool ok = false; + + // Parse value based on field type + if (objectName.contains("_dec_field")) { + value = field->text().toInt(&ok); + } else if (objectName.contains("_hex_field")) { + QString hexText = field->text(); + // Remove 0x prefix if present + if (hexText.startsWith("0x", Qt::CaseInsensitive)) { + hexText = hexText.mid(2); + } + value = hexText.toInt(&ok, 16); + + if (ok && (regName == "regA" || regName == "regB" || regName == "regX" || + regName == "regS" || regName == "regT")) { + if (value > 0x7FFFFF) { + value = value - 0x1000000; + } + } + } else if (objectName.contains("_bin_field")) { + value = field->text().toInt(&ok, 2); + + if (ok && (regName == "regA" || regName == "regB" || regName == "regX" || + regName == "regS" || regName == "regT")) { + if (value > 0x7FFFFF) { + value = value - 0x1000000; + } + } + } + + if (!ok) { + + updateRegisterDisplays(); + return; + } + + if (regName == "regA") { + m_machine->setA(value); + updateAllFormatsForRegister("regA", m_machine->getA()); + } else if (regName == "regB") { + m_machine->setB(value); + updateAllFormatsForRegister("regB", m_machine->getB()); + } else if (regName == "regX") { + m_machine->setX(value); + updateAllFormatsForRegister("regX", m_machine->getX()); + } else if (regName == "regS") { + m_machine->setS(value); + updateAllFormatsForRegister("regS", m_machine->getS()); + } else if (regName == "regT") { + m_machine->setT(value); + updateAllFormatsForRegister("regT", m_machine->getT()); + } else if (regName == "regL") { + m_machine->setL(value); + updateAllFormatsForRegister("regL", m_machine->getL()); + } else if (regName == "regPC") { + m_machine->setPC(value); + updateAllFormatsForRegister("regPC", m_machine->getPC()); + } else if (regName == "regSW") { + m_machine->setSW(value); + updateAllFormatsForRegister("regSW", m_machine->getSW()); + } +} + +void MainWindow::handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName) +{ + double value = 0.0; + bool ok = false; + + if (objectName.contains("_dec_field")) { + value = field->text().toDouble(&ok); + } else if (objectName.contains("_hex_field")) { + QString hexText = field->text(); + if (hexText.startsWith("0x", Qt::CaseInsensitive)) { + hexText = hexText.mid(2); + } + + uint64_t intValue = hexText.toULongLong(&ok, 16); + if (ok) { + intValue &= 0xFFFFFFFFFFFFULL; + double* floatPtr = reinterpret_cast(&intValue); + value = *floatPtr; + } + } else if (objectName.contains("_bin_field")) { + uint64_t intValue = field->text().toULongLong(&ok, 2); + if (ok) { + intValue &= 0xFFFFFFFFFFFFULL; + double* floatPtr = reinterpret_cast(&intValue); + value = *floatPtr; + } + } + + if (!ok) { + updateRegisterDisplays(); + return; + } + + m_machine->setF(value); + updateFloatRegisterFormats("regF", m_machine->getF()); +} + +void MainWindow::setTestRegisterValues() +{ + if (!m_machine) return; + + // Set some test values to demonstrate the register updating + m_machine->setA(12345); // Decimal: 12345, Hex: 0x003039, Binary: 000000011000000111001 + m_machine->setB(-1000); // Negative value to test signed representation + m_machine->setX(0xABCDEF); // Hex value to test various formats + m_machine->setS(255); // Simple power of 2 minus 1 + m_machine->setT(0x7FFFFF); // Maximum positive 24-bit value + + // Update all displays + updateRegisterDisplays(); +} + +void MainWindow::startExecution() +{ + if (m_controller) { + m_controller->start(); + } +} + +void MainWindow::stopExecution() +{ + if (m_controller) { + m_controller->stop(); + } +} + +void MainWindow::stepExecution() +{ + if (m_controller) { + m_controller->step(); + } +} + +void MainWindow::loadDemoProgram() +{ + if (!m_machine) return; + + // Load the instruction set first + loadInstructionSet(); + + qDebug() << "Loading SIC/XE Demo Program: Accumulator Loop"; + + const int TEMP_ADDR = 0x50; + const int LOOP_ADDR = 0x03; + + // clear TEMP + m_machine->setByte(TEMP_ADDR, 0); + + // Program (addresses): + // 0x00 LDA #1 + // 0x03 LDB TEMP + // 0x06 ADDR A,B + // 0x08 RMO B,A + // 0x0A STA TEMP + // 0x0D J LOOP + + // LDA #1 + m_machine->setByte(0x00, 0x01); + m_machine->setByte(0x01, 0x00); + m_machine->setByte(0x02, 0x01); + + // LDB TEMP + m_machine->setByte(0x03, 0x6B); + m_machine->setByte(0x04, 0x00); + m_machine->setByte(0x05, TEMP_ADDR); + + // ADDR A,B + m_machine->setByte(0x06, 0x90); + m_machine->setByte(0x07, 0x03); + + // RMO B,A + m_machine->setByte(0x08, 0xAC); + m_machine->setByte(0x09, 0x30); + + // STA TEMP + m_machine->setByte(0x0A, 0x0F); + m_machine->setByte(0x0B, 0x00); + m_machine->setByte(0x0C, TEMP_ADDR); + + // J LOOP + m_machine->setByte(0x0D, 0x3F); + m_machine->setByte(0x0E, 0x00); + m_machine->setByte(0x0F, LOOP_ADDR); + + // Set PC to start of program + m_machine->setPC(0x00); + + qDebug() << "Program loaded. TEMP at 0x" << QString::number(TEMP_ADDR, 16).toUpper(); + qDebug() << "PC set to 0x00. Ready to execute."; +} diff --git a/simulator_SIC_XE/gui/qt/mainwindow.h b/simulator_SIC_XE/gui/qt/mainwindow.h index 9353441..e6c6c46 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.h +++ b/simulator_SIC_XE/gui/qt/mainwindow.h @@ -2,6 +2,11 @@ #define MAINWINDOW_H #include +#include + +class MachineController; +class Machine; +class QLineEdit; namespace Ui { class MainWindow; @@ -14,9 +19,31 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); + + std::shared_ptr machine() const { return m_machine; } + MachineController* controller() const { return m_controller.get(); } + + void startExecution(); + void stopExecution(); + void stepExecution(); + + void setTestRegisterValues(); + +private slots: + void updateRegisterDisplays(); + void onRegisterFieldChanged(); private: Ui::MainWindow *ui; + std::shared_ptr m_machine; + std::unique_ptr m_controller; + + 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(); }; #endif // MAINWINDOW_H diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui index 9f505ab..3de39ab 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.ui +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -19,17 +19,7 @@ 0 0 - 231 - 601 - - - - - - - 230 - 0 - 891 + 431 601 @@ -37,34 +27,563 @@ 0 - 0 - 881 - 181 + 280 + 431 + 321 Register values - + - 0 - 20 - 881 - 161 + 70 + 40 + 113 + 23 + + + + + + + 20 + 41 + 57 + 20 + + + + + 10 + + + + Reg A + + + + + + 190 + 40 + 113 + 23 + + + + + + + 310 + 40 + 113 + 23 + + + + + + + 110 + 20 + 57 + 20 + + + + + 10 + + + + Bin + + + + + + 230 + 20 + 57 + 20 + + + + + 10 + + + + Hex + + + + + + 350 + 20 + 57 + 20 + + + + + 10 + + + + Dec + + + + + + 310 + 70 + 113 + 23 + + + + + + + 20 + 71 + 57 + 20 + + + + + 10 + + + + Reg B + + + + + + 190 + 70 + 113 + 23 + + + + + + + 70 + 70 + 113 + 23 + + + + + + + 310 + 100 + 113 + 23 + + + + + + + 20 + 101 + 57 + 20 + + + + + 10 + + + + Reg X + + + + + + 190 + 100 + 113 + 23 + + + + + + + 70 + 100 + 113 + 23 + + + + + + + 310 + 130 + 113 + 23 + + + + + + + 20 + 131 + 57 + 20 + + + + + 10 + + + + Reg S + + + + + + 190 + 130 + 113 + 23 + + + + + + + 70 + 130 + 113 + 23 + + + + + + + 310 + 160 + 113 + 23 + + + + + + + 20 + 161 + 57 + 20 + + + + + 10 + + + + Reg T + + + + + + 190 + 160 + 113 + 23 + + + + + + + 70 + 160 + 113 + 23 + + + + + + + 190 + 190 + 113 + 23 + + + + + + + 70 + 190 + 113 + 23 + + + + + + + 20 + 191 + 57 + 20 + + + + + 10 + + + + Reg L + + + + + + 310 + 190 + 113 + 23 + + + + + + + 20 + 221 + 57 + 20 + + + + + 10 + + + + Reg PC + + + + + + 310 + 220 + 113 + 23 + + + + + + + 70 + 220 + 113 + 23 + + + + + + + 190 + 220 + 113 + 23 + + + + + + + 20 + 250 + 57 + 20 + + + + + 10 + + + + Reg SW + + + + + + 310 + 249 + 113 + 23 + + + + + + + 70 + 249 + 113 + 23 + + + + + + + 190 + 249 + 113 + 23 + + + + + + + 20 + 281 + 57 + 20 + + + + + 10 + + + + Reg L + + + + + + 310 + 280 + 113 + 23 + + + + + + + 70 + 280 + 113 + 23 + + + + + + + 190 + 280 + 113 + 23 - - - - - TextLabel - - - - + + + + 0 + 0 + 431 + 161 + + + + Control + + + + + 40 + 70 + 80 + 23 + + + + Start + + + + + + 180 + 70 + 80 + 23 + + + + Stop + + + + + + 320 + 70 + 80 + 23 + + + + Step + + + + + + + + 430 + 0 + 711 + 601 + + diff --git a/simulator_SIC_XE/include/utils.h b/simulator_SIC_XE/include/utils.h index 754b256..3197a95 100644 --- a/simulator_SIC_XE/include/utils.h +++ b/simulator_SIC_XE/include/utils.h @@ -3,6 +3,8 @@ #include "constants.h" +#include + // ============================== // SIC/XE Utility Functions // ============================== @@ -77,4 +79,17 @@ inline int getCC(int sw) { return sw & CC_MASK; } +inline double normaliseFloat(double value) +{ + if (value == 0.0 )return 0.0; + if (!std::isfinite(value)) return value; + double mantissa = value; + while (std::fabs(mantissa) >= 10.0) mantissa /= 10.0; + while (std::fabs(mantissa) < 1.0) mantissa *= 10.0; + return mantissa; +} + + + + #endif // UTILS_H \ No newline at end of file diff --git a/simulator_SIC_XE/src/instructions.cpp b/simulator_SIC_XE/src/instructions.cpp index 85e3bd9..5286311 100644 --- a/simulator_SIC_XE/src/instructions.cpp +++ b/simulator_SIC_XE/src/instructions.cpp @@ -73,6 +73,11 @@ void float_handler(Machine &m) m.setF(static_cast(m.getA())); } +void norm_handler(Machine &m) +{ + m.setF(normaliseFloat(m.getF())); +} + void addr_handler(Machine &m, int r1, int r2) { m.setReg(r2, m.getReg(r1) + m.getReg(r2)); @@ -303,6 +308,14 @@ void or_handler(Machine &m, int ea, AddressingMode 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()); @@ -372,6 +385,18 @@ void subf_handler(Machine &m, int ea, AddressingMode 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); @@ -379,3 +404,11 @@ void tix_handler(Machine &m, int ea, AddressingMode mode) int memVal = resolveWordOperand(m, ea, mode); m.setSW(sic_comp(valX, memVal, m.getSW())); } + +void wd_handler(Machine &m, int ea, AddressingMode mode) +{ + int deviceNum = resolveWordOperand(m, ea, mode); + Device& device = m.getDevice(deviceNum); + // Write rightmost byte of A register to device + device.write(static_cast(m.getA() & 0xFF)); +} diff --git a/simulator_SIC_XE/src/opcode.cpp b/simulator_SIC_XE/src/opcode.cpp index 6095307..f5d63cf 100644 --- a/simulator_SIC_XE/src/opcode.cpp +++ b/simulator_SIC_XE/src/opcode.cpp @@ -38,9 +38,9 @@ void loadInstructionSet() instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast(mul_handler)}; instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast(mulf_handler)}; instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; - instructions[NORM] = {"NORM", InstructionType::TYPE1, nullptr}; + instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast(norm_handler)}; instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast(or_handler)}; - instructions[RD] = {"RD", InstructionType::TYPE3_4, nullptr}; + instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast(rd_handler)}; instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast(rsub_handler)}; instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; @@ -62,10 +62,10 @@ void loadInstructionSet() instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; - instructions[TD] = {"TD", InstructionType::TYPE3_4, nullptr}; + instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast(td_handler)}; instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast(tix_handler)}; instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; - instructions[WD] = {"WD", InstructionType::TYPE3_4, nullptr}; + instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast(wd_handler)}; // Mark uninitialized opcodes as INVALID for (int i = 0; i < 0xff; ++i) { From 280a3b62fc16815fb526341e3a8c0181485bd0a3 Mon Sep 17 00:00:00 2001 From: zanostro Date: Sat, 15 Nov 2025 13:17:43 +0100 Subject: [PATCH 09/15] added visible memory --- simulator_SIC_XE/gui/qt/mainwindow.cpp | 142 ++++++++++++++++++++++++- simulator_SIC_XE/gui/qt/mainwindow.h | 9 ++ simulator_SIC_XE/gui/qt/mainwindow.ui | 114 ++++++++++++++++++++ simulator_SIC_XE/include/constants.h | 2 +- 4 files changed, 265 insertions(+), 2 deletions(-) diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp index e024b67..f49990a 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.cpp +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -10,7 +10,10 @@ #include #include #include -#include +#include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), @@ -60,6 +63,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays); + connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay); connectRegisterFields(); @@ -67,9 +71,18 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->StopBtn, &QPushButton::clicked, this, &MainWindow::stopExecution); connect(ui->StepBtn, &QPushButton::clicked, this, &MainWindow::stepExecution); + connect(ui->MemoryInc256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc256); + connect(ui->MemoryInc4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc4096); + connect(ui->MemoryInc65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc65536); + connect(ui->MemoryDec256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec256); + connect(ui->MemoryDec4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec4096); + connect(ui->MemoryDec65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec65536); + + setupMemoryDisplay(); loadDemoProgram(); updateRegisterDisplays(); + updateMemoryDisplay(); } MainWindow::~MainWindow() @@ -412,3 +425,130 @@ void MainWindow::loadDemoProgram() qDebug() << "Program loaded. TEMP at 0x" << QString::number(TEMP_ADDR, 16).toUpper(); qDebug() << "PC set to 0x00. Ready to execute."; } + +void MainWindow::setupMemoryDisplay() +{ + // Set the title + ui->MemorygroupBox->setTitle("Memory (RAM)"); +} + +void MainWindow::onMemoryInc256() +{ + m_memoryOffset += 256; + if (m_memoryOffset > 1048576 - 256) { + m_memoryOffset = 1048576 - 256; + } + updateMemoryDisplay(); +} + +void MainWindow::onMemoryInc4096() +{ + m_memoryOffset += 4096; + if (m_memoryOffset > 1048576 - 256) { + m_memoryOffset = 1048576 - 256; + } + updateMemoryDisplay(); +} + +void MainWindow::onMemoryInc65536() +{ + m_memoryOffset += 65536; + if (m_memoryOffset > 1048576 - 256) { + m_memoryOffset = 1048576 - 256; + } + updateMemoryDisplay(); +} + +void MainWindow::onMemoryDec256() +{ + m_memoryOffset -= 256; + if (m_memoryOffset < 0) { + m_memoryOffset = 0; + } + updateMemoryDisplay(); +} + +void MainWindow::onMemoryDec4096() +{ + m_memoryOffset -= 4096; + if (m_memoryOffset < 0) { + m_memoryOffset = 0; + } + updateMemoryDisplay(); +} + +void MainWindow::onMemoryDec65536() +{ + m_memoryOffset -= 65536; + if (m_memoryOffset < 0) { + m_memoryOffset = 0; + } + updateMemoryDisplay(); +} + +void MainWindow::updateMemoryDisplay() +{ + if (!m_machine) return; + + // Create a widget to hold the memory display + QWidget* container = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setSpacing(1); + layout->setContentsMargins(5, 5, 5, 5); + + // Create monospace font for memory display + QFont monoFont("Courier New"); + monoFont.setPointSize(9); + + // Header with current offset range + QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]") + .arg(m_memoryOffset, 5, 16, QChar('0')).toUpper() + .arg(m_memoryOffset + 255, 5, 16, QChar('0')).toUpper(); + QLabel* header = new QLabel(headerText); + QFont headerFont = monoFont; + headerFont.setBold(true); + header->setFont(headerFont); + layout->addWidget(header); + + // Get PC for highlighting + int pc = m_machine->getPC(); + + // Display memory byte by byte - ONLY 256 bytes from current offset + for (int i = 0; i < 256; i++) { + int addr = m_memoryOffset + i; + int byte = m_machine->getByte(addr); + + QString addressStr = QString("0x%1").arg(addr, 5, 16, QChar('0')).toUpper(); + + // Hex value column + QString hexStr = QString("0x%1").arg(byte, 2, 16, QChar('0')).toUpper(); + + // Char representation + QString charStr; + if (byte >= 32 && byte <= 126) { + charStr = QChar(byte); + } else { + charStr = '.'; + } + + QString line = QString("%1 %2 %3") + .arg(addressStr, -12) + .arg(hexStr, -12) + .arg(charStr); + + QLabel* memLine = new QLabel(line); + memLine->setFont(monoFont); + + // Highlight the current PC address + if (pc == addr) { + memLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;"); + } + + layout->addWidget(memLine); + } + + layout->addStretch(); + container->setLayout(layout); + + ui->MemoryScrollArea->setWidget(container); +} diff --git a/simulator_SIC_XE/gui/qt/mainwindow.h b/simulator_SIC_XE/gui/qt/mainwindow.h index e6c6c46..0507803 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.h +++ b/simulator_SIC_XE/gui/qt/mainwindow.h @@ -31,12 +31,20 @@ public: private slots: void updateRegisterDisplays(); + void updateMemoryDisplay(); void onRegisterFieldChanged(); + void onMemoryInc256(); + void onMemoryInc4096(); + void onMemoryInc65536(); + void onMemoryDec256(); + void onMemoryDec4096(); + void onMemoryDec65536(); private: Ui::MainWindow *ui; std::shared_ptr m_machine; std::unique_ptr m_controller; + int m_memoryOffset = 0; void connectRegisterFields(); void updateSingleRegisterDisplay(const QString& fieldName, int value); @@ -44,6 +52,7 @@ private: void updateFloatRegisterFormats(const QString& regPrefix, double value); void handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName); void loadDemoProgram(); + void setupMemoryDisplay(); }; #endif // MAINWINDOW_H diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui index 3de39ab..7722091 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.ui +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -584,6 +584,120 @@ 601 + + + + 0 + 0 + 711 + 251 + + + + GroupBox + + + + + 0 + 20 + 711 + 171 + + + + true + + + + + 0 + 0 + 709 + 169 + + + + + + + + 460 + 200 + 121 + 23 + + + + >> M[+0x01000] + + + + + + 360 + 200 + 101 + 23 + + + + > M[+0x00100] + + + + + + 580 + 200 + 121 + 23 + + + + >>> M[+0x10000] + + + + + + 250 + 200 + 101 + 23 + + + + < M[-0x00100] + + + + + + 130 + 200 + 121 + 23 + + + + << M[-0x01000] + + + + + + 10 + 200 + 121 + 21 + + + + <<< M[-0x10000] + + + diff --git a/simulator_SIC_XE/include/constants.h b/simulator_SIC_XE/include/constants.h index 0034171..9c8988c 100644 --- a/simulator_SIC_XE/include/constants.h +++ b/simulator_SIC_XE/include/constants.h @@ -6,7 +6,7 @@ // ============================== // Memory and system constants -constexpr int MEMORY_SIZE = 65536; +constexpr int MEMORY_SIZE = 1 << 20; // 1 MB memory constexpr int NUM_DEVICES = 256; constexpr int WORD_SIZE = 24; constexpr int WORD_MASK = 0xFFFFFF; From ad3078ba481c656be39c882b78699db4f9acf680 Mon Sep 17 00:00:00 2001 From: zanostro Date: Sat, 15 Nov 2025 17:00:57 +0100 Subject: [PATCH 10/15] added disasembly and better demo program --- simulator_SIC_XE/gui/qt/mainwindow.cpp | 448 ++++++++++++++++++++++--- simulator_SIC_XE/gui/qt/mainwindow.h | 24 ++ simulator_SIC_XE/gui/qt/mainwindow.ui | 284 ++++++++++++---- 3 files changed, 651 insertions(+), 105 deletions(-) diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp index f49990a..aba288f 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.cpp +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -3,6 +3,7 @@ #include "MachineController.h" #include "../../include/machine.h" #include "../../include/instructions.h" +#include "../../include/opcode.h" #include #include @@ -77,12 +78,26 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->MemoryDec256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec256); connect(ui->MemoryDec4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec4096); connect(ui->MemoryDec65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec65536); + connect(ui->MemoryGoToStart_2, &QPushButton::clicked, this, &MainWindow::onMemoryGoToStart); + connect(ui->MemoryGoToEnd, &QPushButton::clicked, this, &MainWindow::onMemoryGoToEnd); + + connect(ui->DisasmInc256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc); + connect(ui->DisasmInc4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc16); + connect(ui->DisasmInc65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc256); + connect(ui->DisasmDec256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec); + connect(ui->DisasmDec4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec16); + connect(ui->DisasmDec65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec256); + connect(ui->DisasmGoToStart, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToStart); + connect(ui->DisasmGoToEnd, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToEnd); + connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateDisassemblyDisplay); setupMemoryDisplay(); + setupDisassemblyDisplay(); loadDemoProgram(); updateRegisterDisplays(); updateMemoryDisplay(); + updateDisassemblyDisplay(); } MainWindow::~MainWindow() @@ -375,55 +390,117 @@ void MainWindow::loadDemoProgram() // Load the instruction set first loadInstructionSet(); - qDebug() << "Loading SIC/XE Demo Program: Accumulator Loop"; - - const int TEMP_ADDR = 0x50; - const int LOOP_ADDR = 0x03; - - // clear TEMP - m_machine->setByte(TEMP_ADDR, 0); - - // Program (addresses): - // 0x00 LDA #1 - // 0x03 LDB TEMP - // 0x06 ADDR A,B - // 0x08 RMO B,A - // 0x0A STA TEMP - // 0x0D J LOOP - - // LDA #1 - m_machine->setByte(0x00, 0x01); - m_machine->setByte(0x01, 0x00); - m_machine->setByte(0x02, 0x01); - - // LDB TEMP - m_machine->setByte(0x03, 0x6B); - m_machine->setByte(0x04, 0x00); - m_machine->setByte(0x05, TEMP_ADDR); - - // ADDR A,B - m_machine->setByte(0x06, 0x90); - m_machine->setByte(0x07, 0x03); - - // RMO B,A - m_machine->setByte(0x08, 0xAC); - m_machine->setByte(0x09, 0x30); - - // STA TEMP - m_machine->setByte(0x0A, 0x0F); - m_machine->setByte(0x0B, 0x00); - m_machine->setByte(0x0C, TEMP_ADDR); - - // J LOOP - m_machine->setByte(0x0D, 0x3F); - m_machine->setByte(0x0E, 0x00); - m_machine->setByte(0x0F, LOOP_ADDR); + qDebug() << "Loading SIC/XE Demo Program: Array Sum with Indirect Addressing"; + // Memory layout + const int ARRAY_ADDR = 0x100; // Array of 3 numbers + const int PTR_ADDR = 0x200; // Pointer to array + const int SUM_ADDR = 0x300; // Result storage + const int COUNTER_ADDR = 0x310; // Loop counter + + // Initialize array with values: 10, 20, 30 + m_machine->setWord(ARRAY_ADDR, 10); + m_machine->setWord(ARRAY_ADDR + 3, 20); + m_machine->setWord(ARRAY_ADDR + 6, 30); + + // Initialize pointer to point to array + m_machine->setWord(PTR_ADDR, ARRAY_ADDR); + + // Initialize counter to 3 + m_machine->setWord(COUNTER_ADDR, 3); + + // Initialize sum to 0 + m_machine->setWord(SUM_ADDR, 0); + + int addr = 0x00; + + // Program: Sum array elements using indirect addressing + // 0x00: LDA #0 ; Initialize accumulator to 0 + m_machine->setByte(addr++, 0x01); // LDA with immediate (n=0,i=1) + m_machine->setByte(addr++, 0x00); + m_machine->setByte(addr++, 0x00); + + // 0x03: STA SUM_ADDR ; Store 0 in SUM + m_machine->setByte(addr++, 0x0F); // STA + m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, SUM_ADDR & 0xFF); + + // 0x06: LDX #0 ; Initialize index to 0 + m_machine->setByte(addr++, 0x05); // LDX with immediate + m_machine->setByte(addr++, 0x00); + m_machine->setByte(addr++, 0x00); + + // LOOP (0x09): + const int LOOP_START = addr; + + // 0x09: LDA @PTR_ADDR ; Load value indirectly through pointer + m_machine->setByte(addr++, 0x02); // LDA with indirect (n=1,i=0) + m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, PTR_ADDR & 0xFF); + + // 0x0C: ADD SUM_ADDR ; Add to sum + m_machine->setByte(addr++, 0x1B); // ADD + m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, SUM_ADDR & 0xFF); + + // 0x0F: STA SUM_ADDR ; Store result back + m_machine->setByte(addr++, 0x0F); // STA + m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, SUM_ADDR & 0xFF); + + // 0x12: LDA PTR_ADDR ; Load current pointer value + m_machine->setByte(addr++, 0x03); // LDA + m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, PTR_ADDR & 0xFF); + + // 0x15: ADD #3 ; Add 3 to move to next array element + m_machine->setByte(addr++, 0x19); // ADD with immediate + m_machine->setByte(addr++, 0x00); + m_machine->setByte(addr++, 0x03); + + // 0x18: STA PTR_ADDR ; Store updated pointer + m_machine->setByte(addr++, 0x0F); // STA + m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, PTR_ADDR & 0xFF); + + // 0x1B: LDA COUNTER_ADDR ; Load counter + m_machine->setByte(addr++, 0x03); // LDA + m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, COUNTER_ADDR & 0xFF); + + // 0x1E: ADD #-1 ; Decrement counter (add -1) + m_machine->setByte(addr++, 0x19); // ADD with immediate + m_machine->setByte(addr++, 0x0F); // -1 in 12-bit two's complement + m_machine->setByte(addr++, 0xFF); + + // 0x21: STA COUNTER_ADDR ; Store counter + m_machine->setByte(addr++, 0x0F); // STA + m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF); + m_machine->setByte(addr++, COUNTER_ADDR & 0xFF); + + // 0x24: COMP #0 ; Compare with 0 + m_machine->setByte(addr++, 0x29); // COMP with immediate + m_machine->setByte(addr++, 0x00); + m_machine->setByte(addr++, 0x00); + + // 0x27: JGT LOOP ; Jump if greater than 0 + m_machine->setByte(addr++, 0x37); // JGT + m_machine->setByte(addr++, (LOOP_START >> 8) & 0xFF); + m_machine->setByte(addr++, LOOP_START & 0xFF); + + // 0x2A: J 0x2A ; Infinite loop (halt) + m_machine->setByte(addr++, 0x3F); // J + m_machine->setByte(addr++, 0x00); + m_machine->setByte(addr++, 0x2A); + // Set PC to start of program m_machine->setPC(0x00); - qDebug() << "Program loaded. TEMP at 0x" << QString::number(TEMP_ADDR, 16).toUpper(); - qDebug() << "PC set to 0x00. Ready to execute."; + qDebug() << "Program loaded:"; + qDebug() << " Array at 0x" << QString::number(ARRAY_ADDR, 16).toUpper() << " = [10, 20, 30]"; + qDebug() << " Pointer at 0x" << QString::number(PTR_ADDR, 16).toUpper(); + qDebug() << " Sum will be stored at 0x" << QString::number(SUM_ADDR, 16).toUpper(); + qDebug() << " Expected result: 60 (0x3C)"; } void MainWindow::setupMemoryDisplay() @@ -486,6 +563,285 @@ void MainWindow::onMemoryDec65536() updateMemoryDisplay(); } +void MainWindow::onMemoryGoToStart() +{ + m_memoryOffset = 0; + updateMemoryDisplay(); +} + +void MainWindow::onMemoryGoToEnd() +{ + m_memoryOffset = 1048576 - 256; + updateMemoryDisplay(); +} + +void MainWindow::setupDisassemblyDisplay() +{ + ui->MemorygroupBox_3->setTitle("Disassembly"); +} + +void MainWindow::onDisassemblyInc() +{ + // Move forward by 1 instruction + auto instr = disassembleAt(m_disassemblyOffset); + m_disassemblyOffset += instr.size; + if (m_disassemblyOffset > 1048576 - 16) { + m_disassemblyOffset = 1048576 - 16; + } + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyInc16() +{ + // Move forward by 16 instructions + for (int i = 0; i < 16 && m_disassemblyOffset < 1048576 - 16; i++) { + auto instr = disassembleAt(m_disassemblyOffset); + m_disassemblyOffset += instr.size; + } + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyInc256() +{ + // Move forward by 256 instructions + for (int i = 0; i < 256 && m_disassemblyOffset < 1048576 - 16; i++) { + auto instr = disassembleAt(m_disassemblyOffset); + m_disassemblyOffset += instr.size; + } + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyDec() +{ + // Move back by trying to find previous instruction (assume max 4 bytes) + m_disassemblyOffset = std::max(0, m_disassemblyOffset - 4); + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyDec16() +{ + // Move back by approximately 16 instructions (16*3 = 48 bytes avg) + m_disassemblyOffset = std::max(0, m_disassemblyOffset - 48); + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyDec256() +{ + // Move back by approximately 256 instructions (256*3 = 768 bytes avg) + m_disassemblyOffset = std::max(0, m_disassemblyOffset - 768); + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyGoToStart() +{ + m_disassemblyOffset = 0; + updateDisassemblyDisplay(); +} + +void MainWindow::onDisassemblyGoToEnd() +{ + m_disassemblyOffset = std::max(0, 1048576 - 1024); + updateDisassemblyDisplay(); +} + +MainWindow::DisassembledInstruction MainWindow::disassembleAt(int address) +{ + DisassembledInstruction result; + result.address = address; + result.size = 1; + result.mnemonic = "???"; + result.operand = ""; + result.effectiveAddr = -1; + result.isImmediate = false; + result.isIndirect = false; + + if (address >= 1048576) { + return result; + } + + int byte1 = m_machine->getByte(address); + int opcode = byte1 & 0xFC; // Mask off lower 2 bits (n, i flags) + + if (opcode >= 0xff || instructions[opcode].type == InstructionType::INVALID) { + result.mnemonic = QString("BYTE 0x%1").arg(byte1, 2, 16, QChar('0')).toUpper(); + return result; + } + + result.mnemonic = QString(instructions[opcode].name); + + switch (instructions[opcode].type) { + case InstructionType::TYPE1: + result.size = 1; + break; + + case InstructionType::TYPE2: { + result.size = 2; + if (address + 1 < 1048576) { + int byte2 = m_machine->getByte(address + 1); + int r1 = (byte2 >> 4) & 0xF; + int r2 = byte2 & 0xF; + + const char* regNames[] = {"A", "X", "L", "B", "S", "T", "F", "?", "PC", "SW"}; + QString reg1Str = (r1 < 10) ? regNames[r1] : "?"; + QString reg2Str = (r2 < 10) ? regNames[r2] : "?"; + + result.operand = QString("%1, %2").arg(reg1Str).arg(reg2Str); + } + break; + } + + case InstructionType::TYPE3_4: { + if (address + 2 >= 1048576) { + result.size = 3; + break; + } + + int byte2 = m_machine->getByte(address + 1); + int byte3 = m_machine->getByte(address + 2); + + int ni = (byte1 >> 0) & 0x3; + int x = (byte2 >> 7) & 0x1; + int b = (byte2 >> 6) & 0x1; + int p = (byte2 >> 5) & 0x1; + int e = (byte2 >> 4) & 0x1; + + if (e) { + // Format 4 - add + prefix to mnemonic + result.mnemonic = "+" + result.mnemonic; + result.size = 4; + if (address + 3 < 1048576) { + int byte4 = m_machine->getByte(address + 3); + int addr = ((byte2 & 0xF) << 16) | (byte3 << 8) | byte4; + + result.isImmediate = (ni == 0x1); + result.isIndirect = (ni == 0x2); + + if (!result.isImmediate) { + result.effectiveAddr = addr; + } + + QString prefix = ""; + if (ni == 0x1) prefix = "#"; // Immediate + else if (ni == 0x2) prefix = "@"; // Indirect + + result.operand = QString("%1%2").arg(prefix).arg(addr, 5, 16, QChar('0')).toUpper(); + if (x) result.operand += ",X"; + } + } else { + result.size = 3; + int disp = ((byte2 & 0xF) << 8) | byte3; + + if (disp & 0x800) { + disp |= 0xFFFFF000; + } + + result.isImmediate = (ni == 0x1); + result.isIndirect = (ni == 0x2); + + QString prefix = ""; + if (ni == 0x1) prefix = "#"; // Immediate + else if (ni == 0x2) prefix = "@"; // Indirect + + if (ni == 0x1 && !p && !b) { + result.operand = QString("#%1").arg(disp & 0xFFF); + } else { + // Calculate effective address for display + int ea = disp; + QString addrMode = ""; + + if (p) { + ea += m_machine->getPC(); + addrMode = " (PC)"; + } else if (b) { + ea += m_machine->getB(); + addrMode = " (B)"; + } + + if (!result.isImmediate && !x) { + result.effectiveAddr = ea & 0xFFFFF; + } + + result.operand = QString("%1%2%3").arg(prefix).arg(ea & 0xFFFFF, 4, 16, QChar('0')).toUpper().arg(addrMode); + if (x) result.operand += ",X"; + } + } + break; + } + + default: + break; + } + + return result; +} + +void MainWindow::updateDisassemblyDisplay() +{ + if (!m_machine) return; + + QWidget* container = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setSpacing(1); + layout->setContentsMargins(5, 5, 5, 5); + + QFont monoFont("Courier New"); + monoFont.setPointSize(9); + + // Header + QString headerText = QString("Address Mnemonic Operand *var **var"); + QLabel* header = new QLabel(headerText); + QFont headerFont = monoFont; + headerFont.setBold(true); + header->setFont(headerFont); + layout->addWidget(header); + + int pc = m_machine->getPC(); + int currentAddr = m_disassemblyOffset; + + // Disassemble up to 255 instructions + for (int i = 0; i < 255 && currentAddr < 1048576; i++) { + auto instr = disassembleAt(currentAddr); + + QString varCol = ""; + QString varVar = ""; + + // *var column - show value at effective address (if not immediate) + if (instr.effectiveAddr >= 0 && instr.effectiveAddr < 1048576) { + int value = m_machine->getWord(instr.effectiveAddr); + varCol = QString("0x%1").arg(value & 0xFFFFFF, 6, 16, QChar('0')).toUpper(); + + // **var column - if indirect (@), dereference again + if (instr.isIndirect && value >= 0 && value < 1048576) { + int derefValue = m_machine->getWord(value); + varVar = QString("0x%1").arg(derefValue & 0xFFFFFF, 6, 16, QChar('0')).toUpper(); + } + } + + QString line = QString("0x%1 %2 %3 %4 %5") + .arg(instr.address, 5, 16, QChar('0')).toUpper() + .arg(instr.mnemonic, -9) + .arg(instr.operand, -14) + .arg(varCol, -9) + .arg(varVar, -9); + + QLabel* instrLine = new QLabel(line); + instrLine->setFont(monoFont); + + // Highlight current PC + if (pc == instr.address) { + instrLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;"); + } + + layout->addWidget(instrLine); + currentAddr += instr.size; + } + + layout->addStretch(); + container->setLayout(layout); + + ui->DisasemblyScrollArea->setWidget(container); +} + void MainWindow::updateMemoryDisplay() { if (!m_machine) return; @@ -501,7 +857,7 @@ void MainWindow::updateMemoryDisplay() monoFont.setPointSize(9); // Header with current offset range - QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]") + QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]") .arg(m_memoryOffset, 5, 16, QChar('0')).toUpper() .arg(m_memoryOffset + 255, 5, 16, QChar('0')).toUpper(); QLabel* header = new QLabel(headerText); diff --git a/simulator_SIC_XE/gui/qt/mainwindow.h b/simulator_SIC_XE/gui/qt/mainwindow.h index 0507803..1e39a7d 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.h +++ b/simulator_SIC_XE/gui/qt/mainwindow.h @@ -32,6 +32,7 @@ public: private slots: void updateRegisterDisplays(); void updateMemoryDisplay(); + void updateDisassemblyDisplay(); void onRegisterFieldChanged(); void onMemoryInc256(); void onMemoryInc4096(); @@ -39,12 +40,23 @@ private slots: void onMemoryDec256(); void onMemoryDec4096(); void onMemoryDec65536(); + void onMemoryGoToStart(); + void onMemoryGoToEnd(); + void onDisassemblyInc(); + void onDisassemblyInc16(); + void onDisassemblyInc256(); + void onDisassemblyDec(); + void onDisassemblyDec16(); + void onDisassemblyDec256(); + void onDisassemblyGoToStart(); + void onDisassemblyGoToEnd(); private: Ui::MainWindow *ui; std::shared_ptr m_machine; std::unique_ptr m_controller; int m_memoryOffset = 0; + int m_disassemblyOffset = 0; void connectRegisterFields(); void updateSingleRegisterDisplay(const QString& fieldName, int value); @@ -53,6 +65,18 @@ private: void handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName); void loadDemoProgram(); void setupMemoryDisplay(); + void setupDisassemblyDisplay(); + + struct DisassembledInstruction { + int address; + int size; + QString mnemonic; + QString operand; + int effectiveAddr; + bool isImmediate; + bool isIndirect; + }; + DisassembledInstruction disassembleAt(int address); }; #endif // MAINWINDOW_H diff --git a/simulator_SIC_XE/gui/qt/mainwindow.ui b/simulator_SIC_XE/gui/qt/mainwindow.ui index 7722091..2fad622 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.ui +++ b/simulator_SIC_XE/gui/qt/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 1137 + 1172 649 @@ -17,7 +17,7 @@ - 0 + 10 0 431 601 @@ -27,7 +27,7 @@ 0 - 280 + 80 431 321 @@ -48,8 +48,8 @@ - 20 - 41 + 10 + 40 57 20 @@ -150,8 +150,8 @@ - 20 - 71 + 10 + 70 57 20 @@ -198,8 +198,8 @@ - 20 - 101 + 10 + 100 57 20 @@ -246,8 +246,8 @@ - 20 - 131 + 10 + 130 57 20 @@ -294,8 +294,8 @@ - 20 - 161 + 10 + 160 57 20 @@ -352,8 +352,8 @@ - 20 - 191 + 10 + 190 57 20 @@ -380,8 +380,8 @@ - 20 - 221 + 10 + 220 57 20 @@ -428,8 +428,8 @@ - 20 - 250 + 10 + 249 57 20 @@ -476,8 +476,8 @@ - 20 - 281 + 10 + 280 57 20 @@ -528,7 +528,7 @@ 0 0 431 - 161 + 81 @@ -537,8 +537,8 @@ - 40 - 70 + 30 + 40 80 23 @@ -550,8 +550,8 @@ - 180 - 70 + 170 + 40 80 23 @@ -563,8 +563,8 @@ - 320 - 70 + 310 + 40 80 23 @@ -578,9 +578,9 @@ - 430 + 450 0 - 711 + 721 601 @@ -590,11 +590,11 @@ 0 0 711 - 251 + 291 - GroupBox + Memory @@ -602,7 +602,7 @@ 0 20 711 - 171 + 221 @@ -614,7 +614,7 @@ 0 0 709 - 169 + 219 @@ -622,79 +622,245 @@ - 460 - 200 - 121 + 450 + 250 + 71 23 - >> M[+0x01000] + >> - 360 - 200 - 101 + 370 + 250 + 71 23 - > M[+0x00100] + > - 580 - 200 - 121 + 530 + 250 + 71 23 - >>> M[+0x10000] + >>> - 250 - 200 - 101 + 290 + 250 + 71 23 - < M[-0x00100] + < - 130 - 200 - 121 + 210 + 250 + 71 23 - << M[-0x01000] + << - 10 - 200 - 121 + 130 + 250 + 71 21 - <<< M[-0x10000] + <<< + + + + + + 50 + 250 + 71 + 21 + + + + O + + + + + + 610 + 250 + 71 + 21 + + + + | + + + + + + + 0 + 300 + 711 + 301 + + + + Disasembly + + + + + 0 + 20 + 711 + 221 + + + + true + + + + + 0 + 0 + 709 + 219 + + + + + + + + 440 + 250 + 71 + 23 + + + + >> + + + + + + 360 + 250 + 71 + 23 + + + + > + + + + + + 520 + 250 + 71 + 23 + + + + >>> + + + + + + 280 + 250 + 71 + 23 + + + + < + + + + + + 200 + 250 + 71 + 23 + + + + << + + + + + + 120 + 250 + 71 + 21 + + + + <<< + + + + + + 40 + 250 + 71 + 21 + + + + O + + + + + + 600 + 250 + 71 + 21 + + + + | @@ -705,7 +871,7 @@ 0 0 - 1137 + 1172 20 From ba18b9211658ac239f3ba0d30590a1cc795f331b Mon Sep 17 00:00:00 2001 From: zanostro Date: Sun, 16 Nov 2025 02:10:14 +0100 Subject: [PATCH 11/15] added extra istructions --- simulator_SIC_XE/include/constants.h | 5 + simulator_SIC_XE/include/instructions.h | 23 ++++ simulator_SIC_XE/include/machine.h | 34 ++++- simulator_SIC_XE/include/opcode.h | 23 ++++ simulator_SIC_XE/src/instructions.cpp | 174 ++++++++++++++++++++++++ simulator_SIC_XE/src/machine.cpp | 85 ++++++++++-- simulator_SIC_XE/src/main.cpp | 101 ++++++++------ simulator_SIC_XE/src/opcode.cpp | 148 +++++++++++--------- 8 files changed, 470 insertions(+), 123 deletions(-) diff --git a/simulator_SIC_XE/include/constants.h b/simulator_SIC_XE/include/constants.h index 9c8988c..3698665 100644 --- a/simulator_SIC_XE/include/constants.h +++ b/simulator_SIC_XE/include/constants.h @@ -35,4 +35,9 @@ 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; + + #endif // CONSTANTS_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/instructions.h b/simulator_SIC_XE/include/instructions.h index 61cc452..5bef303 100644 --- a/simulator_SIC_XE/include/instructions.h +++ b/simulator_SIC_XE/include/instructions.h @@ -12,6 +12,7 @@ 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); @@ -75,4 +76,26 @@ void tix_handler(Machine& m, int ea, AddressingMode mode); void wd_handler(Machine& m, int ea, AddressingMode mode); +// SIC/XE/XE Extended instruction handlers +void xexe_handler(Machine& m); +void halt_handler(Machine& m); +void nop_handler(Machine& m); + +void vaddr_handler(Machine& m, int r1, int r2); +void vsubr_handler(Machine& m, int r1, int r2); +void vmulr_handler(Machine& m, int r1, int r2); +void vdivr_handler(Machine& m, int r1, int r2); + +void vadd_handler(Machine& m, int ea, AddressingMode mode); +void vsub_handler(Machine& m, int ea, AddressingMode mode); +void vmul_handler(Machine& m, int ea, AddressingMode mode); +void vdiv_handler(Machine& m, int ea, AddressingMode mode); +void stva_handler(Machine& m, int ea, AddressingMode mode); +void stvs_handler(Machine& m, int ea, AddressingMode mode); +void stvt_handler(Machine& m, int ea, AddressingMode mode); +void ldva_handler(Machine& m, int ea, AddressingMode mode); +void ldvs_handler(Machine& m, int ea, AddressingMode mode); +void ldvt_handler(Machine& m, int ea, AddressingMode mode); + + #endif // INSTRUCTIONS_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/machine.h b/simulator_SIC_XE/include/machine.h index b92934e..8e80669 100644 --- a/simulator_SIC_XE/include/machine.h +++ b/simulator_SIC_XE/include/machine.h @@ -26,7 +26,7 @@ using std::cout; class Machine { public: Machine(); - Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; } + Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; _instructionsTable = instructions; } ~Machine(); int getA() const { return A; } @@ -82,16 +82,13 @@ public: int fetch(); void execute(); - 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); - // Execution and speed control int getSpeed() const; void setSpeed(int kHz); void start(); void stop(); void tick(); + void halt(); // error handling methods void notImplemented(string mnemonic); @@ -100,6 +97,22 @@ public: void divisionByZero(int opcode); void undefinedHandler(int opcode); + bool getExtendedMode() const { return _exex_mode; } + void enableExtendedMode(); + void disableExtendedMode(); + + + int* getVectorRegister(int regNum); + void setVectorRegister(int regNum, const int* values); + + const int* getVA() const { return VA; } + const int* getVS() const { return VS; } + const int* getVT() const { return VT; } + void setVA(const int* values); + void setVS(const int* values); + void setVT(const int* values); + + private: // registers int A, B, X, L, S, T, PC, SW; @@ -116,6 +129,17 @@ private: // Execution control std::atomic running{false}; std::atomic speedkHz{1}; // Default 1 kHz + + bool execF1(int opcode); + bool execF2(int opcode, int operand); + bool execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand); + + + // Extended mode + bool _stopped{false}; + bool _exex_mode{false}; + InstructionInfo* _instructionsTable; + int VA[VECTOR_REG_SIZE], VS[VECTOR_REG_SIZE], VT[VECTOR_REG_SIZE]; // vector operation registers }; diff --git a/simulator_SIC_XE/include/opcode.h b/simulator_SIC_XE/include/opcode.h index 071e1bf..a467488 100644 --- a/simulator_SIC_XE/include/opcode.h +++ b/simulator_SIC_XE/include/opcode.h @@ -66,6 +66,28 @@ #define TIXR 0xB8 #define WD 0xDC +// ============================== +// Extended opcodes (SIC/XE/XE) +// ============================== +#define NOP 0xF1 +#define HALT 0xF2 +#define XEXE 0xEE // Enable extended mode +#define VADD 0x18 +#define VADDR 0x90 +#define VSUB 0x1C +#define VSUBR 0x94 +#define VMUL 0x20 +#define VMULR 0x98 +#define VDIV 0x24 +#define VDIVR 0x9C +#define STVA 0x0C +#define STVS 0x7C +#define STVT 0x84 +#define LDVA 0x00 +#define LDVS 0x68 +#define LDVT 0x04 + + enum class InstructionType { TYPE1, @@ -86,6 +108,7 @@ struct InstructionInfo { }; extern InstructionInfo instructions[]; +extern InstructionInfo instructionsEXEX[]; // Initialize the instruction table void loadInstructionSet(); diff --git a/simulator_SIC_XE/src/instructions.cpp b/simulator_SIC_XE/src/instructions.cpp index 5286311..9a88a68 100644 --- a/simulator_SIC_XE/src/instructions.cpp +++ b/simulator_SIC_XE/src/instructions.cpp @@ -412,3 +412,177 @@ void wd_handler(Machine &m, int ea, AddressingMode mode) // Write rightmost byte of A register to device device.write(static_cast(m.getA() & 0xFF)); } + +void xexe_handler(Machine &m) +{ + m.enableExtendedMode(); + m.execute(); + m.disableExtendedMode(); +} + +void halt_handler(Machine &m) +{ + m.halt(); +} + +void nop_handler(Machine &m) +{ + // Do nothing +} + +void vaddr_handler(Machine &m, int r1, int r2) +{ + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVectorRegister(r1)[i] + m.getVectorRegister(r2)[i]; + } + m.setVectorRegister(r2, result); +} + +void vsubr_handler(Machine &m, int r1, int r2) +{ + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVectorRegister(r2)[i] - m.getVectorRegister(r1)[i]; + } + m.setVectorRegister(r2, result); +} + +void vmulr_handler(Machine &m, int r1, int r2) +{ + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVectorRegister(r1)[i] * m.getVectorRegister(r2)[i]; + } + m.setVectorRegister(r2, result); +} + +void vdivr_handler(Machine &m, int r1, int r2) +{ + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + if (m.getVectorRegister(r1)[i] == 0) { + m.divisionByZero(VDIVR); + return; + } + result[i] = m.getVectorRegister(r2)[i] / m.getVectorRegister(r1)[i]; + } + m.setVT(result); +} + +void vadd_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVA()[i] + vec[i]; + } + m.setVA(result); +} + +void vsub_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVA()[i] - vec[i]; + } + m.setVA(result); +} + +void vmul_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + result[i] = m.getVA()[i] * vec[i]; + } + m.setVA(result); +} + +void vdiv_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + int result[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + if (vec[i] == 0) { + m.divisionByZero(VDIV); + return; + } + result[i] = m.getVA()[i] / vec[i]; + } + m.setVA(result); +} + +void stva_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + const int* vec = m.getVA(); + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + m.setWord(baseAddr + i * 3, vec[i]); + } +} + +void stvs_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + const int* vec = m.getVS(); + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + m.setWord(baseAddr + i * 3, vec[i]); + } +} + +void stvt_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + const int* vec = m.getVT(); + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + m.setWord(baseAddr + i * 3, vec[i]); + } +} + +void ldva_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + m.setVA(vec); +} + +void ldvs_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + m.setVS(vec); +} + +void ldvt_handler(Machine &m, int ea, AddressingMode mode) +{ + int baseAddr = resolveWordOperand(m, ea, mode); + int vec[VECTOR_REG_SIZE]; + for (int i = 0; i < VECTOR_REG_SIZE; ++i) { + vec[i] = m.getWord(baseAddr + i * 3); + } + m.setVT(vec); +} diff --git a/simulator_SIC_XE/src/machine.cpp b/simulator_SIC_XE/src/machine.cpp index e4ee364..9570054 100644 --- a/simulator_SIC_XE/src/machine.cpp +++ b/simulator_SIC_XE/src/machine.cpp @@ -21,6 +21,8 @@ Machine::Machine() devices[1] = make_shared(std::cout); // device 2: standard error devices[2] = make_shared(std::cerr); + _exex_mode = false; + _instructionsTable = instructions; } Machine::~Machine() @@ -67,6 +69,62 @@ void Machine::undefinedHandler(int opcode) cout << prefix << "Undefined handler for opcode: " << opcode << endl; } +void Machine::enableExtendedMode() +{ + if(!USE_EXTENDED_MODE) return; + _exex_mode = true; + _instructionsTable = instructionsEXEX; +} + +void Machine::disableExtendedMode() +{ + if(!USE_EXTENDED_MODE) return; + _exex_mode = false; + _instructionsTable = instructions; +} + +int *Machine::getVectorRegister(int regNum) +{ + switch (regNum) { + case 0: return VA; + case 4: return VS; + case 5: return VT; + default: + cerr << prefix << "Invalid register number: " << regNum << endl; + return nullptr; + } +} + +void Machine::setVectorRegister(int regNum, const int *values) +{ + int* targetReg = getVectorRegister(regNum); + if (targetReg == nullptr) return; + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + targetReg[i] = toSIC24(values[i]); + } +} + +void Machine::setVA(const int *values) +{ + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + VA[i] = toSIC24(values[i]); + } +} + +void Machine::setVS(const int *values) +{ + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + VS[i] = toSIC24(values[i]); + } +} + +void Machine::setVT(const int *values) +{ + for (int i = 0; i < VECTOR_REG_SIZE; i++) { + VT[i] = toSIC24(values[i]); + } +} + void Machine::tick() { const int speed = speedkHz.load(); @@ -76,6 +134,11 @@ void Machine::tick() std::this_thread::sleep_for(delay); } +void Machine::halt() +{ + _stopped = true; +} + int Machine::getReg(int regNum) const { switch (regNum) { @@ -289,14 +352,16 @@ int Machine::fetch() } void Machine::execute() { + if (_stopped) return; int b1 = fetch(); - InstructionInfo &info = instructions[b1]; + + 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 = instructions[opcode]; + InstructionInfo &info34 = _instructionsTable[opcode]; int ni = b1 & NI_MASK; if (info34.type == InstructionType::TYPE3_4) { @@ -329,8 +394,8 @@ void Machine::execute() { bool Machine::execF1(int opcode) { - if (instructions[opcode].handler) { - auto handler = reinterpret_cast(instructions[opcode].handler); + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); handler(*this); return true; } @@ -343,8 +408,8 @@ bool Machine::execF2(int opcode, int operand) int r1 = (operand >> 4) & 0xF; int r2 = operand & 0xF; - if (instructions[opcode].handler) { - auto handler = reinterpret_cast(instructions[opcode].handler); + if (_instructionsTable[opcode].handler) { + auto handler = reinterpret_cast(_instructionsTable[opcode].handler); handler(*this, r1, r2); return true; } @@ -362,8 +427,8 @@ bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int op // --- PURE SIC --- if (mode == AddressingMode::SIC_DIRECT) { int ea = ea_part + (x ? getX() : 0); - if (instructions[opcode].handler) { - auto h = reinterpret_cast(instructions[opcode].handler); + if (_instructionsTable[opcode].handler) { + auto h = reinterpret_cast(_instructionsTable[opcode].handler); h(*this, ea, mode); return true; } @@ -386,8 +451,8 @@ bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int op // format 4 (e=1): b/p ignored, ea_part is 20-bit absolute int ea = base + ea_part + (x ? getX() : 0); - if (instructions[opcode].handler) { - auto h = reinterpret_cast(instructions[opcode].handler); + if (_instructionsTable[opcode].handler) { + auto h = reinterpret_cast(_instructionsTable[opcode].handler); h(*this, ea, mode); return true; } diff --git a/simulator_SIC_XE/src/main.cpp b/simulator_SIC_XE/src/main.cpp index 16dcd0c..2d9662a 100644 --- a/simulator_SIC_XE/src/main.cpp +++ b/simulator_SIC_XE/src/main.cpp @@ -9,11 +9,16 @@ using std::cout; using std::endl; +struct VectorAddProgram { + int x, y; +}; + + + int main() { loadInstructionSet(); Machine machine; - cout << "SIC/XE Program: Accumulator Loop" << endl; const int TEMP_ADDR = 0x50; @@ -21,61 +26,67 @@ int main() // clear TEMP machine.setByte(TEMP_ADDR, 0); + loadInstructionSet(); - // Program (addresses): - // 0x00 LDA #1 - // 0x03 LDB TEMP - // 0x06 ADDR A,B - // 0x08 RMO B,A - // 0x0A STA TEMP - // 0x0D J LOOP + cout << "SIC/XE Program: Vector add test" << endl; - // LDA #1 - machine.setByte(0x00, 0x01); - machine.setByte(0x01, 0x00); - machine.setByte(0x02, 0x01); + const int VA_ADDR = 0x100; // source vector A + const int VB_ADDR = 0x200; // source vector B + const int VR_ADDR = 0x300; // result store (STVA) - // LDB TEMP - machine.setByte(0x03, 0x6B); - machine.setByte(0x04, 0x00); - machine.setByte(0x05, TEMP_ADDR); + // 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}; - // ADDR A,B - machine.setByte(0x06, 0x90); - machine.setByte(0x07, 0x03); + 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]); + } - // RMO B,A - machine.setByte(0x08, 0xAC); - machine.setByte(0x09, 0x30); + // 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 - // STA TEMP - machine.setByte(0x0A, 0x0F); - machine.setByte(0x0B, 0x00); - machine.setByte(0x0C, TEMP_ADDR); + 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 + }; - // J LOOP - machine.setByte(0x0D, 0x3F); - machine.setByte(0x0E, 0x00); - machine.setByte(0x0F, LOOP_ADDR); + const int PROG_START = 0x00; + for (size_t i = 0; i < sizeof(prog); ++i) { + machine.setByte(PROG_START + static_cast(i), prog[i]); + } - machine.setPC(0x00); + machine.setPC(PROG_START); - cout << "Program loaded. TEMP at 0x" << std::hex << TEMP_ADDR << std::dec << endl; + cout << "Program loaded. VA@0x" << std::hex << VA_ADDR << " VB@0x" << VB_ADDR << " -> store@0x" << VR_ADDR << std::dec << endl; - // run a few iterations - for (int i = 0; i < 10; ++i) { - cout << "Iter " << (i + 1) << ": A=" << machine.getA() - << " B=" << machine.getB() - << " TEMP=" << machine.getByte(TEMP_ADDR); + + const int MAX_STEPS = 100; + for (int i = 0; i < MAX_STEPS; ++i) { + machine.execute(); + } - // advance the program by executing the next 6 instructions - for (int k = 0; k < 6; ++k) machine.execute(); - - cout << " -> A=" << machine.getA() - << " B=" << machine.getB() - << " TEMP=" << machine.getByte(TEMP_ADDR) << "\n"; - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // 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"); } return 0; diff --git a/simulator_SIC_XE/src/opcode.cpp b/simulator_SIC_XE/src/opcode.cpp index f5d63cf..0b63ce8 100644 --- a/simulator_SIC_XE/src/opcode.cpp +++ b/simulator_SIC_XE/src/opcode.cpp @@ -4,74 +4,96 @@ #include InstructionInfo instructions[0xff]; +InstructionInfo instructionsEXEX[0xff]; void loadInstructionSet() { - instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast(add_handler)}; - instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast(addf_handler)}; - instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; - instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast(and_handler)}; - instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; - instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast(comp_handler)}; - instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast(compf_handler)}; - instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast(compr_handler)}; - instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast(div_handler)}; - instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast(divf_handler)}; - instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; - instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast(fix_handler)}; - instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast(float_handler)}; - instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; - instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast(j_handler)}; - instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast(jeq_handler)}; - instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast(jgt_handler)}; - instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast(jlt_handler)}; - instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast(jsub_handler)}; - instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast(lda_handler)}; - instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast(ldb_handler)}; - instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast(ldch_handler)}; - instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast(ldf_handler)}; - instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast(ldl_handler)}; - instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast(lds_handler)}; - instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast(ldt_handler)}; - instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast(ldx_handler)}; - instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; - instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast(mul_handler)}; - instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast(mulf_handler)}; - instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; - instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast(norm_handler)}; - instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast(or_handler)}; - instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast(rd_handler)}; - instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; - instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast(rsub_handler)}; - instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; - instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; - instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; - instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; - instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast(sta_handler)}; - instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast(stb_handler)}; - instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast(stch_handler)}; - instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast(stf_handler)}; - instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; - instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast(stl_handler)}; - instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast(sts_handler)}; - instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast(stsw_handler)}; - instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast(stt_handler)}; - instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast(stx_handler)}; - instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast(sub_handler)}; - instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast(subf_handler)}; - instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; - instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; - instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; - instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast(td_handler)}; - instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast(tix_handler)}; - instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; - instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast(wd_handler)}; + instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast(add_handler)}; + instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast(addf_handler)}; + instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast(addr_handler)}; + instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast(and_handler)}; + instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast(clear_handler)}; + instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast(comp_handler)}; + instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast(compf_handler)}; + instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast(compr_handler)}; + instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast(div_handler)}; + instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast(divf_handler)}; + instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast(divr_handler)}; + instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast(fix_handler)}; + instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast(float_handler)}; + instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr}; + instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast(j_handler)}; + instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast(jeq_handler)}; + instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast(jgt_handler)}; + instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast(jlt_handler)}; + instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast(jsub_handler)}; + instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast(lda_handler)}; + instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast(ldb_handler)}; + instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast(ldch_handler)}; + instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast(ldf_handler)}; + instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast(ldl_handler)}; + instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast(lds_handler)}; + instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast(ldt_handler)}; + instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast(ldx_handler)}; + instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr}; + instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast(mul_handler)}; + instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast(mulf_handler)}; + instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast(mulr_handler)}; + instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast(norm_handler)}; + instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast(or_handler)}; + instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast(rd_handler)}; + instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast(rmo_handler)}; + instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast(rsub_handler)}; + instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast(shiftl_handler)}; + instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast(shiftr_handler)}; + instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr}; + instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr}; + instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast(sta_handler)}; + instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast(stb_handler)}; + instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast(stch_handler)}; + instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast(stf_handler)}; + instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr}; + instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast(stl_handler)}; + instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast(sts_handler)}; + instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast(stsw_handler)}; + instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast(stt_handler)}; + instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast(stx_handler)}; + instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast(sub_handler)}; + instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast(subf_handler)}; + instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast(subr_handler)}; + instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast(svc_handler)}; + instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast(tixr_handler)}; + instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast(td_handler)}; + instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast(tix_handler)}; + instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr}; + instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast(wd_handler)}; - // Mark uninitialized opcodes as INVALID + // Load SIC/XE/XE extended instructions + if (USE_EXTENDED_MODE) { + // Still in main table + instructions[NOP] = {"NOP", InstructionType::TYPE1, reinterpret_cast(nop_handler)}; + instructions[HALT] = {"HALT", InstructionType::TYPE1, reinterpret_cast(halt_handler)}; + instructions[XEXE] = {"XEXE", InstructionType::TYPE1, reinterpret_cast(xexe_handler)}; + + instructionsEXEX[VADD] = {"VADD", InstructionType::TYPE3_4, reinterpret_cast(vadd_handler)}; + instructionsEXEX[VADDR] = {"VADDR", InstructionType::TYPE2, reinterpret_cast(vaddr_handler)}; + instructionsEXEX[VSUB] = {"VSUB", InstructionType::TYPE3_4, reinterpret_cast(vsub_handler)}; + instructionsEXEX[VSUBR] = {"VSUBR", InstructionType::TYPE2, reinterpret_cast(vsubr_handler)}; + instructionsEXEX[VMUL] = {"VMUL", InstructionType::TYPE3_4, reinterpret_cast(vmul_handler)}; + instructionsEXEX[VMULR] = {"VMULR", InstructionType::TYPE2, reinterpret_cast(vmulr_handler)}; + instructionsEXEX[VDIV] = {"VDIV", InstructionType::TYPE3_4, reinterpret_cast(vdiv_handler)}; + instructionsEXEX[VDIVR] = {"VDIVR", InstructionType::TYPE2, reinterpret_cast(vdivr_handler)}; + instructionsEXEX[STVA] = {"STVA", InstructionType::TYPE3_4, reinterpret_cast(stva_handler)}; + instructionsEXEX[STVS] = {"STVS", InstructionType::TYPE3_4, reinterpret_cast(stvs_handler)}; + instructionsEXEX[STVT] = {"STVT", InstructionType::TYPE3_4, reinterpret_cast(stvt_handler)}; + instructionsEXEX[LDVA] = {"LDVA", InstructionType::TYPE3_4, reinterpret_cast(ldva_handler)}; + instructionsEXEX[LDVS] = {"LDVS", InstructionType::TYPE3_4, reinterpret_cast(ldvs_handler)}; + instructionsEXEX[LDVT] = {"LDVT", InstructionType::TYPE3_4, reinterpret_cast(ldvt_handler)}; + } + // Mark uninitialized opcodes as INVALID for (int i = 0; i < 0xff; ++i) { - if (instructions[i].name == nullptr) { - instructions[i] = {"INVALID", InstructionType::INVALID, nullptr}; - } + if (instructions[i].name == nullptr) instructions[i] = {"INVALID", InstructionType::INVALID, nullptr}; + if (instructionsEXEX[i].name == nullptr) instructionsEXEX[i] = {"INVALID", InstructionType::INVALID, nullptr}; } } From 5d2a0f867caead9fd86ee138a6009a39516e7ecf Mon Sep 17 00:00:00 2001 From: zanostro Date: Mon, 17 Nov 2025 12:33:06 +0100 Subject: [PATCH 12/15] implemented rfile reading --- simulator_SIC_XE/include/file_reader.h | 19 ++++++++++++++++ simulator_SIC_XE/include/reader.h | 15 ++++++++++++ simulator_SIC_XE/include/string_reader.h | 17 ++++++++++++++ simulator_SIC_XE/src/file_reader.cpp | 29 ++++++++++++++++++++++++ simulator_SIC_XE/src/string_reader.cpp | 27 ++++++++++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 simulator_SIC_XE/include/file_reader.h create mode 100644 simulator_SIC_XE/include/reader.h create mode 100644 simulator_SIC_XE/include/string_reader.h create mode 100644 simulator_SIC_XE/src/file_reader.cpp create mode 100644 simulator_SIC_XE/src/string_reader.cpp diff --git a/simulator_SIC_XE/include/file_reader.h b/simulator_SIC_XE/include/file_reader.h new file mode 100644 index 0000000..2ee9fcd --- /dev/null +++ b/simulator_SIC_XE/include/file_reader.h @@ -0,0 +1,19 @@ +#pragma once +#include "reader.h" +#include +#include + +class FileReader : public Reader { +public: + explicit FileReader(const std::string &path, std::ios::openmode m = std::ios::binary); + ~FileReader() override; + + int readByte() override; + bool readBytes(uint8_t* buf, size_t len) override; + std::string readString(size_t len) override; + + bool good() const; + +private: + std::ifstream in; +}; diff --git a/simulator_SIC_XE/include/reader.h b/simulator_SIC_XE/include/reader.h new file mode 100644 index 0000000..ff64e77 --- /dev/null +++ b/simulator_SIC_XE/include/reader.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +// Abstract Reader class: read bytes/strings from a source (file, string, etc.) +class Reader { +public: + virtual ~Reader() = default; + // return 0..255 on success, -1 on EOF/error + virtual int readByte() = 0; + // read exactly len bytes into buf; return true on success + virtual bool readBytes(uint8_t* buf, size_t len) = 0; + // read up to len bytes into a std::string; may return shorter string on EOF + virtual std::string readString(size_t len) = 0; +}; \ No newline at end of file diff --git a/simulator_SIC_XE/include/string_reader.h b/simulator_SIC_XE/include/string_reader.h new file mode 100644 index 0000000..6b9187e --- /dev/null +++ b/simulator_SIC_XE/include/string_reader.h @@ -0,0 +1,17 @@ +#pragma once +#include "reader.h" +#include +#include + +class StringReader : public Reader { +public: + explicit StringReader(const std::string &s); + ~StringReader() override; + + int readByte() override; + bool readBytes(uint8_t* buf, size_t len) override; + std::string readString(size_t len) override; + +private: + std::istringstream in; +}; diff --git a/simulator_SIC_XE/src/file_reader.cpp b/simulator_SIC_XE/src/file_reader.cpp new file mode 100644 index 0000000..5785f06 --- /dev/null +++ b/simulator_SIC_XE/src/file_reader.cpp @@ -0,0 +1,29 @@ +#include "file_reader.h" + +FileReader::FileReader(const std::string &path, std::ios::openmode m) + : in(path, m) +{} + +FileReader::~FileReader() = default; + +int FileReader::readByte() { + char c; + if (!in.get(c)) return -1; + return static_cast(c); +} + +bool FileReader::readBytes(uint8_t* buf, size_t len) { + in.read(reinterpret_cast(buf), static_cast(len)); + return static_cast(in.gcount()) == len; +} + +std::string FileReader::readString(size_t len) { + std::string s; + s.resize(len); + in.read(reinterpret_cast(&s[0]), static_cast(len)); + std::streamsize got = in.gcount(); + if (static_cast(got) < len) s.resize(static_cast(got)); + return s; +} + +bool FileReader::good() const { return static_cast(in); } diff --git a/simulator_SIC_XE/src/string_reader.cpp b/simulator_SIC_XE/src/string_reader.cpp new file mode 100644 index 0000000..3a24cb9 --- /dev/null +++ b/simulator_SIC_XE/src/string_reader.cpp @@ -0,0 +1,27 @@ +#include "string_reader.h" + +StringReader::StringReader(const std::string &s) + : in(s) +{} + +StringReader::~StringReader() = default; + +int StringReader::readByte() { + char c; + if (!in.get(c)) return -1; + return static_cast(c); +} + +bool StringReader::readBytes(uint8_t* buf, size_t len) { + in.read(reinterpret_cast(buf), static_cast(len)); + return static_cast(in.gcount()) == len; +} + +std::string StringReader::readString(size_t len) { + std::string s; + s.resize(len); + in.read(reinterpret_cast(&s[0]), static_cast(len)); + std::streamsize got = in.gcount(); + if (static_cast(got) < len) s.resize(static_cast(got)); + return s; +} From d438feb9eeb909f91dc7f7d6b99df6e76bb17b1a Mon Sep 17 00:00:00 2001 From: zanostro Date: Mon, 17 Nov 2025 13:07:24 +0100 Subject: [PATCH 13/15] added reading start --- simulator_SIC_XE/include/file_reader.h | 7 ++- simulator_SIC_XE/include/loader.h | 61 ++++++++++++++++++++++++ simulator_SIC_XE/include/reader.h | 10 +++- simulator_SIC_XE/include/string_reader.h | 7 ++- simulator_SIC_XE/src/file_reader.cpp | 6 +++ simulator_SIC_XE/src/loader.cpp | 35 ++++++++++++++ simulator_SIC_XE/src/string_reader.cpp | 6 +++ 7 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 simulator_SIC_XE/include/loader.h create mode 100644 simulator_SIC_XE/src/loader.cpp diff --git a/simulator_SIC_XE/include/file_reader.h b/simulator_SIC_XE/include/file_reader.h index 2ee9fcd..e2da87a 100644 --- a/simulator_SIC_XE/include/file_reader.h +++ b/simulator_SIC_XE/include/file_reader.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef FILE_READER_H +#define FILE_READER_H + #include "reader.h" #include #include @@ -11,9 +13,12 @@ public: int readByte() override; bool readBytes(uint8_t* buf, size_t len) override; std::string readString(size_t len) override; + std::string readLine() override; bool good() const; private: std::ifstream in; }; + +#endif // FILE_READER_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/loader.h b/simulator_SIC_XE/include/loader.h new file mode 100644 index 0000000..c4d75e3 --- /dev/null +++ b/simulator_SIC_XE/include/loader.h @@ -0,0 +1,61 @@ +#ifndef LOADER_H +#define LOADER_H + +#include +#include +#include +#include "file_reader.h" + +class Machine; + +using std::shared_ptr; +using std::string; + + +class Loader { +public: + Loader( shared_ptr machine, string filename) : _machine(machine), _filename(filename) { + _file_reader = std::make_shared(filename, std::ios::in); + } + ~Loader(); + + + enum class RecordType { + HEADER, + TEXT, + END, + UNKNOWN + }; + + struct HeaderMetadata { + string program_name; + int start_address; + int length; + }; + struct TextRecord { + int start_address; + std::vector data; + }; + struct EndRecord { + int execution_start_address; + }; + + void load(); + +private : + + static RecordType parseRecordType(char c); + + + shared_ptr _machine; + string _filename; + shared_ptr _file_reader; + HeaderMetadata readHeader(); + +}; + + + + + +#endif // LOADER_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/reader.h b/simulator_SIC_XE/include/reader.h index ff64e77..e582161 100644 --- a/simulator_SIC_XE/include/reader.h +++ b/simulator_SIC_XE/include/reader.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef READER_H +#define READER_H + #include #include @@ -12,4 +14,8 @@ public: 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; -}; \ No newline at end of file + // read a line (up to newline), return empty string on EOF + virtual std::string readLine() = 0; +}; + +#endif // READER_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/string_reader.h b/simulator_SIC_XE/include/string_reader.h index 6b9187e..71c3da9 100644 --- a/simulator_SIC_XE/include/string_reader.h +++ b/simulator_SIC_XE/include/string_reader.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef STRING_READER_H +#define STRING_READER_H + #include "reader.h" #include #include @@ -11,7 +13,10 @@ public: int readByte() override; bool readBytes(uint8_t* buf, size_t len) override; std::string readString(size_t len) override; + std::string readLine() override; private: std::istringstream in; }; + +#endif // STRING_READER_H \ No newline at end of file diff --git a/simulator_SIC_XE/src/file_reader.cpp b/simulator_SIC_XE/src/file_reader.cpp index 5785f06..0ace74b 100644 --- a/simulator_SIC_XE/src/file_reader.cpp +++ b/simulator_SIC_XE/src/file_reader.cpp @@ -27,3 +27,9 @@ std::string FileReader::readString(size_t len) { } bool FileReader::good() const { return static_cast(in); } + +std::string FileReader::readLine() { + std::string s; + if (!std::getline(in, s)) return std::string(); + return s; +} diff --git a/simulator_SIC_XE/src/loader.cpp b/simulator_SIC_XE/src/loader.cpp new file mode 100644 index 0000000..0626ac6 --- /dev/null +++ b/simulator_SIC_XE/src/loader.cpp @@ -0,0 +1,35 @@ +#include "loader.h" +#include "file_reader.h" + +Loader::~Loader() +{ + _machine.reset(); +} + + + + +void Loader::load() +{ + + +} + +Loader::RecordType Loader::parseRecordType(char c) +{ + switch (c) { + case 'H': return RecordType::HEADER; + case 'T': return RecordType::TEXT; + case 'E': return RecordType::END; + default: return RecordType::UNKNOWN; // fallback; adjust as needed + } +} + +Loader::HeaderMetadata Loader::readHeader() +{ + RecordType type = parseRecordType(static_cast(_file_reader->readByte())); + if (type != RecordType::HEADER) { + throw std::runtime_error("Expected HEADER record"); + } + HeaderMetadata header; +} diff --git a/simulator_SIC_XE/src/string_reader.cpp b/simulator_SIC_XE/src/string_reader.cpp index 3a24cb9..b87cc02 100644 --- a/simulator_SIC_XE/src/string_reader.cpp +++ b/simulator_SIC_XE/src/string_reader.cpp @@ -25,3 +25,9 @@ std::string StringReader::readString(size_t len) { if (static_cast(got) < len) s.resize(static_cast(got)); return s; } + +std::string StringReader::readLine() { + std::string s; + if (!std::getline(in, s)) return std::string(); + return s; +} From 598865d216158b4f9c37a3836297081e07cea547 Mon Sep 17 00:00:00 2001 From: zanostro Date: Mon, 17 Nov 2025 13:45:03 +0100 Subject: [PATCH 14/15] added loading --- simulator_SIC_XE/include/file_reader.h | 1 + simulator_SIC_XE/include/loader.h | 3 + simulator_SIC_XE/src/file_reader.cpp | 1 + simulator_SIC_XE/src/loader.cpp | 82 +++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/simulator_SIC_XE/include/file_reader.h b/simulator_SIC_XE/include/file_reader.h index e2da87a..87b8a17 100644 --- a/simulator_SIC_XE/include/file_reader.h +++ b/simulator_SIC_XE/include/file_reader.h @@ -11,6 +11,7 @@ public: ~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; diff --git a/simulator_SIC_XE/include/loader.h b/simulator_SIC_XE/include/loader.h index c4d75e3..16ffa45 100644 --- a/simulator_SIC_XE/include/loader.h +++ b/simulator_SIC_XE/include/loader.h @@ -51,6 +51,9 @@ private : string _filename; shared_ptr _file_reader; HeaderMetadata readHeader(); + TextRecord readTextRecord(); + EndRecord readEndRecord(); + bool load_into_memory(int start_address, const std::vector& data); }; diff --git a/simulator_SIC_XE/src/file_reader.cpp b/simulator_SIC_XE/src/file_reader.cpp index 0ace74b..2a800cc 100644 --- a/simulator_SIC_XE/src/file_reader.cpp +++ b/simulator_SIC_XE/src/file_reader.cpp @@ -12,6 +12,7 @@ int FileReader::readByte() { return static_cast(c); } + bool FileReader::readBytes(uint8_t* buf, size_t len) { in.read(reinterpret_cast(buf), static_cast(len)); return static_cast(in.gcount()) == len; diff --git a/simulator_SIC_XE/src/loader.cpp b/simulator_SIC_XE/src/loader.cpp index 0626ac6..df459fa 100644 --- a/simulator_SIC_XE/src/loader.cpp +++ b/simulator_SIC_XE/src/loader.cpp @@ -1,5 +1,7 @@ #include "loader.h" #include "file_reader.h" +#include "machine.h" +#include "constants.h" Loader::~Loader() { @@ -11,8 +13,28 @@ Loader::~Loader() void Loader::load() { + HeaderMetadata header = readHeader(); - + while(true) { + RecordType type = parseRecordType(static_cast(_file_reader->readByte())); + switch (type) { + case RecordType::TEXT: { + TextRecord textRecord = readTextRecord(); + if (!load_into_memory(textRecord.start_address, textRecord.data)) { + throw std::runtime_error("Failed to load text record into memory"); + } + break; + } + case RecordType::END: { + EndRecord endRecord = readEndRecord(); + _machine->setPC(endRecord.execution_start_address); + return; // Loading complete + } + case RecordType::UNKNOWN: + default: + throw std::runtime_error("Unknown record type encountered"); + } + } } Loader::RecordType Loader::parseRecordType(char c) @@ -32,4 +54,62 @@ Loader::HeaderMetadata Loader::readHeader() throw std::runtime_error("Expected HEADER record"); } HeaderMetadata header; + // Read program name (6 bytes) + header.program_name = _file_reader->readString(6); + // Read start address (6 hex digits) + header.start_address = std::stoi(_file_reader->readString(6), nullptr, 16); + // 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() +{ + TextRecord record; + // Assume 'T' has already been read + record.start_address = std::stoi(_file_reader->readString(6), nullptr, 16); + // Read length (1 byte, 2 hex digits) + int length = std::stoi(_file_reader->readString(2), nullptr, 16); + + // Read data bytes + record.data.resize(length); + + + for(int i = 0; i < length; ++i) { + unsigned char buffer[2]; + _file_reader->readBytes(buffer, 2); + record.data[i] = static_cast(std::stoi(std::string(reinterpret_cast(buffer), 2), nullptr, 16)); + } + // consume newline + _file_reader->readLine(); + +} + +Loader::EndRecord Loader::readEndRecord() +{ + EndRecord record; + // 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; // default start address + } + // consume newline + _file_reader->readLine(); + return record; +} + +bool Loader::load_into_memory(int start_address, const std::vector &data) +{ + for(size_t i = 0; i < data.size(); ++i) { + int addr = start_address + static_cast(i); + if (addr < 0 || addr >= MEMORY_SIZE) { // 24-bit address space + return false; // Address out of bounds + } + _machine->setByte(addr, data[i]); + } + return true; } From 7b79c35f639b6d29fca114e29d13bbeac7d3c33b Mon Sep 17 00:00:00 2001 From: zanostro Date: Mon, 17 Nov 2025 15:21:25 +0100 Subject: [PATCH 15/15] added loading --- simulator_SIC_XE/CMakeLists.txt | 12 +++++++++ simulator_SIC_XE/Makefile | 2 +- simulator_SIC_XE/gui/qt/mainwindow.cpp | 10 ++++++- simulator_SIC_XE/include/constants.h | 9 +++++++ simulator_SIC_XE/include/loader.h | 4 +++ simulator_SIC_XE/src/loader.cpp | 37 +++++++++++++++++--------- simulator_SIC_XE/src/main.cpp | 17 ++++++++++++ test.asm | 7 +++++ 8 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 test.asm diff --git a/simulator_SIC_XE/CMakeLists.txt b/simulator_SIC_XE/CMakeLists.txt index fe0a86a..c6c37e1 100644 --- a/simulator_SIC_XE/CMakeLists.txt +++ b/simulator_SIC_XE/CMakeLists.txt @@ -46,3 +46,15 @@ message(STATUS "Output directory: ${OUTPUT_DIR}") if(EXISTS "${CMAKE_SOURCE_DIR}/gui/qt/CMakeLists.txt") add_subdirectory(gui/qt) endif() + +# Copy resources directory (if present) to target/res so build output includes them +if(EXISTS "${CMAKE_SOURCE_DIR}/res") + add_custom_target(copy_resources + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/target/res + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res ${CMAKE_SOURCE_DIR}/target/res + COMMENT "Copying resources from res/ to target/res/" + ) + if(TARGET simulator_exec) + add_dependencies(simulator_exec copy_resources) + endif() +endif() diff --git a/simulator_SIC_XE/Makefile b/simulator_SIC_XE/Makefile index 6d5d9ea..92ab3cc 100644 --- a/simulator_SIC_XE/Makefile +++ b/simulator_SIC_XE/Makefile @@ -26,7 +26,7 @@ build: configure run: build @echo "Running primary target..." - # Prefer GUI if available, otherwise fall back to console executable + # Prefer GUI if avail able, otherwise fall back to console executable @if [ -x "$(GUI_TARGET)" ]; then \ echo "Launching GUI: $(GUI_TARGET)"; \ sh -c 'nohup env QT_QPA_PLATFORM=xcb ./$(GUI_TARGET) >/dev/null 2>&1 & echo $! > "$(BUILD_DIR)/simulator_qt.pid"'; \ diff --git a/simulator_SIC_XE/gui/qt/mainwindow.cpp b/simulator_SIC_XE/gui/qt/mainwindow.cpp index aba288f..afc464c 100644 --- a/simulator_SIC_XE/gui/qt/mainwindow.cpp +++ b/simulator_SIC_XE/gui/qt/mainwindow.cpp @@ -4,6 +4,8 @@ #include "../../include/machine.h" #include "../../include/instructions.h" #include "../../include/opcode.h" +#include "../../include/constants.h" +#include "../../../include/loader.h" #include #include @@ -16,6 +18,10 @@ #include #include +class Loader; + +std::shared_ptr g_loader; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -93,7 +99,9 @@ MainWindow::MainWindow(QWidget *parent) : setupMemoryDisplay(); setupDisassemblyDisplay(); - loadDemoProgram(); + //loadDemoProgram(); + g_loader = std::make_shared(machine(), std::string(PATH_RESOURCES) + "demo_program.obj"); + g_loader->load(); updateRegisterDisplays(); updateMemoryDisplay(); diff --git a/simulator_SIC_XE/include/constants.h b/simulator_SIC_XE/include/constants.h index 3698665..6f2195a 100644 --- a/simulator_SIC_XE/include/constants.h +++ b/simulator_SIC_XE/include/constants.h @@ -1,6 +1,7 @@ #ifndef CONSTANTS_H #define CONSTANTS_H +#include // ============================== // SIC/XE Architecture Constants // ============================== @@ -39,5 +40,13 @@ constexpr int BIT_E_MASK = 0x10; // mask for e bit in F4 and F3 instructions constexpr bool USE_EXTENDED_MODE = true; constexpr int VECTOR_REG_SIZE = 4; +/* if structure is +/target/ + |-> bin/simulator_exec + |-> res/ +*/ +// When running from project root (./target/bin/simulator_exec), resources are in ./target/res/ +constexpr char PATH_RESOURCES[] = "./target/res/"; +constexpr bool FILE_CONTAINS_WHITE_SPACES = true; #endif // CONSTANTS_H \ No newline at end of file diff --git a/simulator_SIC_XE/include/loader.h b/simulator_SIC_XE/include/loader.h index 16ffa45..41899c2 100644 --- a/simulator_SIC_XE/include/loader.h +++ b/simulator_SIC_XE/include/loader.h @@ -5,6 +5,7 @@ #include #include #include "file_reader.h" +#include class Machine; @@ -16,6 +17,9 @@ class Loader { public: Loader( shared_ptr machine, string filename) : _machine(machine), _filename(filename) { _file_reader = std::make_shared(filename, std::ios::in); + if (!_file_reader->good()) { + throw std::runtime_error("Loader: failed to open file: " + filename); + } } ~Loader(); diff --git a/simulator_SIC_XE/src/loader.cpp b/simulator_SIC_XE/src/loader.cpp index df459fa..9b3d293 100644 --- a/simulator_SIC_XE/src/loader.cpp +++ b/simulator_SIC_XE/src/loader.cpp @@ -2,6 +2,7 @@ #include "file_reader.h" #include "machine.h" #include "constants.h" +#include Loader::~Loader() { @@ -49,15 +50,22 @@ Loader::RecordType Loader::parseRecordType(char c) Loader::HeaderMetadata Loader::readHeader() { + RecordType type = parseRecordType(static_cast(_file_reader->readByte())); if (type != RecordType::HEADER) { throw std::runtime_error("Expected HEADER record"); } + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + HeaderMetadata header; // Read program name (6 bytes) header.program_name = _file_reader->readString(6); + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + // Read start address (6 hex digits) header.start_address = std::stoi(_file_reader->readString(6), nullptr, 16); + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + // Read length (6 hex digits) header.length = std::stoi(_file_reader->readString(6), nullptr, 16); // consume newline @@ -67,35 +75,40 @@ Loader::HeaderMetadata Loader::readHeader() Loader::TextRecord Loader::readTextRecord() { + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); TextRecord record; // Assume 'T' has already been read record.start_address = std::stoi(_file_reader->readString(6), nullptr, 16); + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); + // Read length (1 byte, 2 hex digits) int length = std::stoi(_file_reader->readString(2), nullptr, 16); + if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); - // Read data bytes record.data.resize(length); - - for(int i = 0; i < length; ++i) { - unsigned char buffer[2]; - _file_reader->readBytes(buffer, 2); - record.data[i] = static_cast(std::stoi(std::string(reinterpret_cast(buffer), 2), nullptr, 16)); + int index = 0; + string byteStr = _file_reader->readLine(); + // Remove spaces, newlines, and other whitespace characters + byteStr.erase(std::remove_if(byteStr.begin(), byteStr.end(), ::isspace), byteStr.end()); + + for (int i = 0; i < length; ++i) { + std::string byteHex = byteStr.substr(i * 2, 2); + record.data[i] = static_cast(std::stoi(byteHex, nullptr, 16)); } - // consume newline - _file_reader->readLine(); - + 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; // default start address + record.execution_start_address = 0; } // consume newline _file_reader->readLine(); @@ -106,8 +119,8 @@ bool Loader::load_into_memory(int start_address, const std::vector &dat { for(size_t i = 0; i < data.size(); ++i) { int addr = start_address + static_cast(i); - if (addr < 0 || addr >= MEMORY_SIZE) { // 24-bit address space - return false; // Address out of bounds + if (addr < 0 || addr >= MEMORY_SIZE) { + return false; } _machine->setByte(addr, data[i]); } diff --git a/simulator_SIC_XE/src/main.cpp b/simulator_SIC_XE/src/main.cpp index 2d9662a..bd7a9e7 100644 --- a/simulator_SIC_XE/src/main.cpp +++ b/simulator_SIC_XE/src/main.cpp @@ -5,6 +5,8 @@ #include "file_device.h" #include "opcode.h" #include "instructions.h" +#include "constants.h" +#include "loader.h" using std::cout; using std::endl; @@ -17,6 +19,7 @@ struct VectorAddProgram { int main() { + /* loadInstructionSet(); Machine machine; cout << "SIC/XE Program: Accumulator Loop" << endl; @@ -88,6 +91,20 @@ int main() int val = machine.getWord(VR_ADDR + i * 3); cout << val << (i + 1 < VECTOR_REG_SIZE ? ", " : "\n"); } + */ + + loadInstructionSet(); + std::shared_ptr machine = std::make_shared(); + Loader loader(machine, std::string(PATH_RESOURCES) + "test.obj"); + loader.load(); + machine->execute(); + machine->execute(); + machine->execute(); + machine->execute(); + machine->execute(); + machine->execute(); + cout << "Register A after execution: " << machine->getA() << endl; + return 0; } \ No newline at end of file diff --git a/test.asm b/test.asm new file mode 100644 index 0000000..e25da30 --- /dev/null +++ b/test.asm @@ -0,0 +1,7 @@ +test START 0 + LDA #1 + LDB #2 + ADDR B, A + +halt J halt + END test \ No newline at end of file