diff --git a/ass2/Machine.cpp b/ass2/Machine.cpp index 6aa9168..7903aea 100644 --- a/ass2/Machine.cpp +++ b/ass2/Machine.cpp @@ -2,12 +2,15 @@ #include #include +#include #include +#include #include "FileDevice.h" #include "InputDevice.h" #include "Opcode.h" #include "OutputDevice.h" +#include "Utils.h" Machine::Machine() { A = 0; @@ -228,10 +231,19 @@ void Machine::setWord(int addr, int val) { } } -Device* Machine::getDevice(int num) const { +Device* Machine::getDevice(int num) { if (num < 0 || num >= MAX_DEVICES) { return nullptr; } + + // lazy init for file devices (3-255) + if (devices[num] == nullptr && num >= 3) { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << num; + std::string filename = ss.str() + ".dev"; + devices[num] = new FileDevice(filename); + } + return devices[num]; } @@ -578,6 +590,10 @@ void Machine::stop() { } } +void Machine::step() { + execute(); +} + bool Machine::isRunning() const { return running; } @@ -589,3 +605,54 @@ int Machine::getSpeed() const { void Machine::setSpeed(int kHz) { speed = kHz; } + +bool Machine::loadSection(std::istream& r) { + int startAddr = 0; + int execAddr = 0; + + char recordType; + while (r.get(recordType)) { + if (recordType == '\n' || recordType == '\r') { + continue; + } + + if (recordType == 'H') { + // header: H^name(6)^startAddr(6)^length(6) + std::string name = Utils::readString(r, 6); + startAddr = Utils::readWord(r); + int length = Utils::readWord(r); + (void)name; + (void)length; + } else if (recordType == 'T') { + // text: T^addr(6)^length(2)^data + int addr = Utils::readWord(r); + int len = Utils::readByte(r); + + for (int i = 0; i < len; i++) { + int byte = Utils::readByte(r); + setByte(addr + i, byte); + } + } else if (recordType == 'M') { + // modification: M^addr(6)^length(2) + // skip for absolute loader + Utils::readWord(r); + Utils::readByte(r); + } else if (recordType == 'E') { + // end: E^execAddr(6) or E alone + execAddr = Utils::readWord(r); + if (execAddr == 0) { + execAddr = startAddr; + } + PC = execAddr; + return true; + } + + // skip to end of line + char c; + while (r.get(c) && c != '\n') { + // skip + } + } + + return false; +} diff --git a/ass2/Machine.h b/ass2/Machine.h index 43477a4..664ee3b 100644 --- a/ass2/Machine.h +++ b/ass2/Machine.h @@ -1,9 +1,10 @@ #ifndef MACHINE_H #define MACHINE_H -#include -#include #include +#include +#include +#include #include "Device.h" @@ -73,7 +74,7 @@ class Machine { int getWord(int addr) const; void setWord(int addr, int val); - Device* getDevice(int num) const; + Device* getDevice(int num); void setDevice(int num, Device* device); // error handling @@ -91,9 +92,13 @@ class Machine { // timer simulation void start(); void stop(); + void step(); bool isRunning() const; int getSpeed() const; void setSpeed(int kHz); + + // loader + bool loadSection(std::istream& r); }; #endif // MACHINE_H diff --git a/ass2/README.md b/ass2/README.md new file mode 100644 index 0000000..4ed5030 --- /dev/null +++ b/ass2/README.md @@ -0,0 +1,76 @@ +# SIC/XE Simulator + +C++ implementacija SIC/XE simulatorja + +## Prevajanje + +```bash +./run.sh +``` + +ali ročno: + +```bash +g++ -std=c++11 -pthread -c *.cpp +g++ -std=c++11 -pthread -o simulator *.o +``` + +## Uporaba + +```bash +./simulator +``` + +```bash +./simulator /obj/poly.obj +``` + +ali z run.sh: +```bash +./run.sh /obj/poly.obj +``` + +## Funkcionalnosti + +### Registri +- **A** - Accumulator (24-bit) +- **X** - Index register (24-bit) +- **L** - Linkage register (24-bit) +- **B** - Base register (24-bit) +- **S, T** - General purpose (24-bit) +- **F** - Floating point (48-bit) +- **PC** - Program Counter (24-bit) +- **SW** - Status Word (24-bit) + +### Pomnilnik +- 1 MB (naslovi 0x00000 - 0xFFFFF) +- Big-endian zapis + +### Naprave +- **0** - standardni vhod (stdin) +- **1** - standardni izhod (stdout) +- **2** - standardni izhod za napake (stderr) +- **3–255** - leno se ustvarijo datoteke `XX.dev` (hex), branje/pisanje prek TD/RD/WD + +### Podprti ukazi + +| Format | Ukazi | +|--------|-------| +| F1 | FIX, FLOAT (float aritmetike ni implementirane) | +| F2 | ADDR, SUBR, MULR, DIVR, COMPR, SHIFTL, SHIFTR, RMO, CLEAR, TIXR | +| F3/F4 | LDA, LDX, LDL, LDB, LDS, LDT, LDCH, STA, STX, STL, STB, STS, STT, STCH, STSW, ADD, SUB, MUL, DIV, AND, OR, COMP, TIX, J, JEQ, JGT, JLT, JSUB, RSUB, TD, RD, WD | + +**Ni** implementirano: ADDF, SUBF, MULF, DIVF, COMPF, LDF, STF, SIO/HIO/TIO, SSK, LPS. + +### Načini naslavljanja +- Neposredno (immediate) +- Posredno (indirect) +- Preprosto (simple) +- Indeksirano (indexed) +- Bazno relativno (base relative) +- PC relativno (PC relative) + +### Izvajanje +- `step()` – izvede en ukaz +- `start()` / `stop()` – samodejno izvajanje s časovnikom (privzeto 1000 kHz) +- Zaščita pred neskončno zanko: ustavi po 100000 ukazih v glavnem programu \ No newline at end of file diff --git a/ass2/Utils.cpp b/ass2/Utils.cpp new file mode 100644 index 0000000..796c5dc --- /dev/null +++ b/ass2/Utils.cpp @@ -0,0 +1,40 @@ +#include "Utils.h" + +std::string Utils::readString(std::istream& r, int len) { + std::string result; + for (int i = 0; i < len; i++) { + char c; + if (r.get(c)) { + result += c; + } + } + return result; +} + +int Utils::readByte(std::istream& r) { + char hex[3]; + hex[0] = r.get(); + hex[1] = r.get(); + hex[2] = '\0'; + + int value = 0; + for (int i = 0; i < 2; i++) { + value <<= 4; + char c = hex[i]; + if (c >= '0' && c <= '9') { + value += c - '0'; + } else if (c >= 'A' && c <= 'F') { + value += c - 'A' + 10; + } else if (c >= 'a' && c <= 'f') { + value += c - 'a' + 10; + } + } + return value; +} + +int Utils::readWord(std::istream& r) { + int b1 = readByte(r); + int b2 = readByte(r); + int b3 = readByte(r); + return (b1 << 16) | (b2 << 8) | b3; +} diff --git a/ass2/Utils.h b/ass2/Utils.h new file mode 100644 index 0000000..3afb7ef --- /dev/null +++ b/ass2/Utils.h @@ -0,0 +1,14 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include + +class Utils { + public: + static std::string readString(std::istream& r, int len); + static int readByte(std::istream& r); + static int readWord(std::istream& r); +}; + +#endif diff --git a/ass2/main.cpp b/ass2/main.cpp index f8db1a1..5501064 100644 --- a/ass2/main.cpp +++ b/ass2/main.cpp @@ -1,16 +1,72 @@ +#include +#include #include + #include "Machine.h" -int main() { +void printReg(const char* name, int value) { + std::cout << name << " = " << std::setw(8) << std::dec << value + << " (0x" << std::hex << std::setw(6) << std::setfill('0') + << value << ")" << std::setfill(' ') << std::endl; +} + +void printRegisters(Machine& machine) { + std::cout << "\n=== Registers ===" << std::endl; + printReg("A ", machine.getA()); + printReg("X ", machine.getX()); + printReg("L ", machine.getL()); + printReg("B ", machine.getB()); + printReg("S ", machine.getS()); + printReg("T ", machine.getT()); + printReg("F ", machine.getF()); + printReg("PC", machine.getPC()); + printReg("SW", machine.getSW()); +} + +int main(int argc, char* argv[]) { Machine machine; - - machine.setA(0x123456); - machine.setX(0xABCDEF); - machine.setPC(0x1000); - - std::cout << "Register A: 0x" << std::hex << machine.getA() << std::endl; - std::cout << "Register X: 0x" << std::hex << machine.getX() << std::endl; - std::cout << "Program Counter: 0x" << std::hex << machine.getPC() << std::endl; - + + if (argc > 1) { + std::ifstream file(argv[1]); + if (file.is_open()) { + std::cout << "=== SIC/XE Simulator ===" << std::endl; + std::cout << "Loading: " << argv[1] << std::endl; + + if (machine.loadSection(file)) { + std::cout << "Start address: 0x" << std::hex << std::setw(6) << std::setfill('0') << machine.getPC() << std::dec << std::setfill(' ') << std::endl; + std::cout << "\nExecuting..." << std::endl; + + int prevPC = -1; + int instrCount = 0; + int maxInstr = 100000; // stop after too many instructions + + while (machine.getPC() != prevPC && instrCount < maxInstr) { + prevPC = machine.getPC(); + machine.execute(); + instrCount++; + } + + if (instrCount >= maxInstr) { + std::cout << "\nwarning: stopped after " << maxInstr << " instructions (infinite loop?)" << std::endl; + } else { + std::cout << "Halted after " << instrCount << " instructions" << std::endl; + } + + printRegisters(machine); + + } else { + std::cerr << "ERROR: Failed to load " << argv[1] << std::endl; + return 1; + } + file.close(); + } else { + std::cerr << "ERROR: Cannot open " << argv[1] << std::endl; + return 1; + } + } else { + std::cout << "SIC/XE Simulator" << std::endl; + std::cout << "Usage: " << argv[0] << " " << std::endl; + } + return 0; } diff --git a/ass2/run.sh b/ass2/run.sh new file mode 100755 index 0000000..f4432d1 --- /dev/null +++ b/ass2/run.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# SIC/XE Simulator - build and run script + +# compile source files +echo "Compiling..." +g++ -std=c++11 -pthread -c Device.cpp InputDevice.cpp OutputDevice.cpp FileDevice.cpp Utils.cpp Machine.cpp main.cpp + +if [ $? -ne 0 ]; then + echo "Compilation failed!" + exit 1 +fi + +# link +g++ -std=c++11 -pthread -o simulator *.o + +if [ $? -ne 0 ]; then + echo "Linking failed!" + exit 1 +fi + +echo "Build successful!" + +# run with argument if provided +if [ $# -gt 0 ]; then + echo "" + ./simulator "$1" +else + echo "Usage: ./run.sh " + echo "" + echo "Available .obj files:" + ls -1 *.obj 2>/dev/null || echo " (none found)" +fi