#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 6: return F;
        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) {
        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(string mnemonic) {
    std::cerr << "Instruction: " << mnemonic << " 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);
}


void Machine::execute() {
    int opcode = fetch();
    int operand;
    switch (opcode) {
    // Format 1
    case Opcode::FIX:
    case Opcode::FLOAT:
    case Opcode::NORM:
        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:
        operand = fetch();
        execF2(opcode, operand);
        break;

    // Format 3/4 (SIC/XE)
    default:
        decode_F3_F4(opcode);
        break;
    }
}











