spo/ass2/simulator/machine.cpp
2025-12-07 14:45:47 +01:00

467 lines
13 KiB
C++

#include "machine.h"
#include "device.h"
#include "opcode.h"
#include <stdexcept>
using namespace std;
Machine::Machine() {
//nastavi registre
A = B = L = T = S = X = PC = SW = 0;
F = 0.0;
//nastavi naprave
devices[0] = new InputDevice(cin);
devices[1] = new OutputDevice(cout);
devices[2] = new OutputDevice(cerr);
for (int i = 3; i < 256; i++) {
string filename = to_string(i) + ".dev";
devices[i] = new FileDevice(filename);
}
}
int Machine::getReg(int reg)
{
switch (reg) {
case 0: return A;
case 1: return X;
case 2: return L;
case 3: return B;
case 4: return S;
case 5: return T;
case 8: return PC;
case 9: return SW;
default: return -1;
}
}
void Machine::setReg(int reg, int val)
{
switch (reg) {
case 0: A = val; break;
case 1: X = val; break;
case 2: L = val; break;
case 3: B = val; break;
case 4: S = val; break;
case 5: T = val; break;
case 8: PC = val; break;
case 9: SW = val; break;
}
}
unsigned char Machine::readByte(unsigned int address) {
if (address > MAX_ADDRESS || address <0) {
throw std::out_of_range("Memory read out of range");
}
return memory[address];
}
void Machine::writeByte(unsigned int address, unsigned char val) {
if (address > MAX_ADDRESS) {
throw std::out_of_range("Memory write out of range");
}
memory[address] = val;
}
unsigned int Machine::getWord(unsigned int address) {
if (address + 2 > MAX_ADDRESS) {
throw std::out_of_range("Memory read out of range");
}
unsigned int B1 = memory[address + 2];
unsigned int B2 = memory[address + 1];
unsigned int B3 = memory[address];
return B1 | (B2 << 8) | (B3 << 16);
}
void Machine::setWord(unsigned int address, unsigned int val) {
if (address + 2 > MAX_ADDRESS) {
throw std::out_of_range("Memory write out of range");
}
memory[address + 2] = val & 0xFF;
memory[address + 1] = (val >> 8) & 0xFF;
memory[address] = (val >> 16) & 0xFF;
}
Device* Machine::getDevice(int num) {
if (num < 0 || num > 255) { return nullptr; }
return devices[num];
}
void Machine::setDevice(int num, Device* device) {
if (num < 0 || num > 255 ) { return; }
delete devices[num];
devices[num] = device;
}
void Machine::notImplemented(int opcode) {
std::cerr << "Instruction: " << opcode << " is not yet implemented." << endl;
}
void Machine::invalidOpcode(int opcode) {
std::cerr << "Invalid opcode: " << to_string(opcode) << endl;
}
void Machine::invalidAddressing() {
std::cerr << "Invalid addressing used." << endl;
}
// Nalozi in vrni en bajt z naslova PC
// in ga poveca za 1
int Machine::fetch() {
int address = getReg(8);
setReg(8, address + 1);
return readByte(address);
}
bool Machine::execF1(int opcode){
switch (opcode) {
case Opcode::FIX:
setA(static_cast<int>(getF()));
return true;
case Opcode::FLOAT:
setF(static_cast<double>(getA()));
return true;
default:
notImplemented(opcode);
break;
}
return false;
}
bool Machine::execF2(int opcode, int operand) {
int r1 = (operand >> 4) & 0xF;
int r2 = operand & 0xF;
int n;
switch (opcode) {
case Opcode::ADDR:
setReg(r2, getReg(r2) + getReg(r1));
return true;
case Opcode::CLEAR: //v clear je le 1 reg podan in sicer v r1
setReg(r1, 0);
return true;
case Opcode::DIVR:
setReg(r2, getReg(r2) / getReg(r1));
return true;
case Opcode::MULR:
setReg(r2, getReg(r2) * getReg(r1));
return true;
case Opcode::RMO:
setReg(r2, getReg(r1));
return true;
case Opcode::SHIFTL:
n = r2;
setReg(r1, getReg(r1) << n);
return true;
case Opcode::SHIFTR:
n = r2;
setReg(r1, getReg(r1) >> n);
return true;
case Opcode::SUBR:
setReg(r2, getReg(r2) - getReg(r1));
return true;
default:
notImplemented(opcode);
break;
}
return false;
}
bool Machine::execSICF3F4(int opcode, int ni, int operand) {
int x_val = 0;
int operand2 = fetch();
int UA = 0;
int UV = 0;
int temp = 0;
bool ready = false;
int offset = 0;
if (((operand >> 7) & 0x1) == 1) { //preverimo ali je indeksno naslavljanje
x_val = getX();
}
if (ni == 0) { // stari SIC (neposredno in enostavno naslavljanje)
UA = (((operand & 0x7F) << 8) | operand2) + x_val; //izracun uporabnega naslova: operand brez bita x + drugi del naslova + x_val
if (UA < 0 || UA + 2 > MAX_ADDRESS) {
invalidAddressing();
return false;
}
UV = (memory[UA] << 16) | (memory[UA + 1] << 8) | memory[UA + 2]; //izracunamo operand oz. uporabno vrednost
} else if (((operand >> 4) & 0x1) == 0) { // ce e bit ni prizgan e je F3, sicer F4
bool b = ((operand >> 6) & 0x1) == 1; //preverimo bit za bazno naslavljanje
bool p = ((operand >> 5) & 0x1) == 1;
offset = (((operand & 0x0F) << 8) | operand2);
if (offset & 0x800) { // če je bit 11 = 1 (negativno)
offset |= 0xFFFFF000; // nastavi vse višje bite na 1 (sign extend)
}
offset += x_val;
if (!b && p) { //PC - relativno
UA = getPC() + offset;
} else if (b && !p) {
UA = getB() + offset;
} else if (!b && !p) { //neposredno
UA = (((operand & 0x0F) << 8) | operand2);
} else { // b && p je nedovoljeno!
invalidAddressing();
}
if (UA < 0 || UA + 2 > MAX_ADDRESS) {
invalidAddressing();
return false;
}
if (ni == 2) { //posredno
int UV_temp = (memory[UA] << 16) | (memory[UA + 1] << 8) | memory[UA + 2];
UV = (memory[UV_temp] << 16) | (memory[UV_temp + 1] << 8) | memory[UV_temp + 2];
} else if (ni == 1) { //takojšnje
UV = UA;
} else if (ni == 3) { //enostavno
UV = (memory[UA] << 16) | (memory[UA + 1] << 8) | memory[UA + 2]; //izracunamo operand oz. uporabno vrednost
}
} else {
int operand3 = fetch();
bool b = ((operand >> 6) & 0x1) == 1; //preverimo bit za bazno naslavljanje
bool p = ((operand >> 5) & 0x1) == 1;
offset = (((operand & 0x0F) << 16) | (operand2 << 8) | operand3);
if (offset & 0x80000) { // če je bit 11 = 1 (negativno)
offset |= 0xFFF00000; // nastavi vse višje bite na 1 (sign extend)
}
offset += x_val;
if (!b && p) { //PC - relativno
UA = getPC() + offset;
} else if (b && !p) {
UA = getB() + offset;
} else if (!b && !p) { //neposredno
UA = (((operand & 0x0F) << 16) | (operand2 << 8) | operand3);
} else { // b && p je nedovoljeno!
invalidAddressing();
}
if (UA < 0 || UA + 2 > MAX_ADDRESS) {
invalidAddressing();
return false;
}
if (ni == 2) { //posredno
int UV_temp = (memory[UA] << 16) | (memory[UA + 1] << 8) | memory[UA + 2];
UV = (memory[UV_temp] << 16) | (memory[UV_temp + 1] << 8) | memory[UV_temp + 2];
} else if (ni == 1) { //takojšnje
UV = UA;
} else if (ni == 3) { //enostavno
UV = (memory[UA] << 16) | (memory[UA + 1] << 8) | memory[UA + 2]; //izracunamo operand oz. uporabno vrednost
}
}
switch (opcode) {
case Opcode::ADD:
setA(getA() + UV);
return true;
case Opcode::AND:
setA(getA() & UV);
return true;
case Opcode::COMP:
temp = getA();
if (temp < UV) {
setSW(0x40);
} else if (temp == UV) {
setSW(0x0);
} else {
setSW(0x80);
}
return true;
break;
case Opcode::DIV:
if (UV == 0) {
cerr << "Error: Divison by zero." << endl;
return false;
}
setA(getA() / UV);
return true;
case Opcode::J:
setPC(UV);
return true;
case Opcode::JEQ:
if (getSW() == 0x00) {
setPC(UV);
}
return true;
case Opcode::JGT:
if (getSW() == 0x80) {
setPC(UV);
}
return true;
case Opcode::JLT:
if (getSW() == 0x40) {
setPC(UV);
}
return true;
case Opcode::JSUB:
setL(getPC());
setPC(UV);
return true;
case Opcode::LDA:
setA(UV);
return true;
case Opcode::LDB:
setB(UV);
return true;
case Opcode::LDCH:
setA((getA() & 0xFFFF00) | (UV & 0xFF));
return true;
case Opcode::LDL:
setL(UV);
return true;
case Opcode::LDS:
setS(UV);
return true;
case Opcode::LDT:
setT(UV);
return true;
case Opcode::LDX:
setX(UV);
return true;
case Opcode::MUL:
setA(getA() * UV);
return true;
case Opcode::OR:
setA(getA() | UV);
return true;
case Opcode::RD:
temp = UV & 0xFF;
if (devices[temp] != nullptr) {
setA(devices[temp]->read());
}
return true;
case Opcode::RSUB:
setPC(getL());
return true;
case Opcode::STA:
temp = getA();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STB:
temp = getB();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STS:
temp = getS();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STT:
temp = getA();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STCH:
temp = getA();
memory[UV] = temp & 0xFF;
return true;
case Opcode::STL:
temp = getL();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STSW:
temp = getSW();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::STX:
temp = getX();
memory[UV] = (temp >> 16) & 0xFF;
memory[UV + 1] = (temp >> 8) & 0xFF;
memory[UV + 2] = temp & 0xFF;
return true;
case Opcode::SUB:
setA(getA() - UV);
return true;
case Opcode::TD:
temp = UV & 0xFF;
ready = false;
if (devices[temp] != nullptr) {
ready = devices[temp]->test();
}
if (ready) {
setSW(0X40);
} else {
setSW(0x00);
}
return true;
case Opcode::TIX:
temp = getX();
setX(temp + 1);
if (temp < UV) setSW(0x40);
else if (temp == UV) setSW(0x00);
else setSW(0x80);
return true;
case Opcode::WD:
temp = UV & 0xFF;
if (devices[temp] != nullptr) {
devices[temp]->write(getA() & 0xFF);
}
return true;
default:
notImplemented(opcode);
break;
}
return false;
}
void Machine::execute() {
int opcode = fetch();
int operand;
switch (opcode) {
// Format 1
case Opcode::FIX:
case Opcode::FLOAT:
case Opcode::NORM:
case Opcode::HIO:
case Opcode::TIO:
case Opcode::SIO:
execF1(opcode);
break;
// Format 2
case Opcode::ADDR:
case Opcode::CLEAR:
case Opcode::DIVR:
case Opcode::MULR:
case Opcode::RMO:
case Opcode::SHIFTL:
case Opcode::SHIFTR:
case Opcode::SUBR:
case Opcode::SVC:
operand = fetch();
execF2(opcode, operand);
break;
// Format SIC/3/4 (SIC/XE)
default:
operand = fetch();
int ni = opcode & 0x3;
opcode = opcode & 0xFC;
execSICF3F4(opcode, ni, operand);
break;
}
}