implemented timer, opcode decoding

This commit is contained in:
privsk 2025-12-03 10:13:58 +01:00
parent 5a06b2bc0b
commit 76b2c711ef
2 changed files with 384 additions and 1 deletions

View file

@ -1,10 +1,12 @@
#include "Machine.h"
#include <chrono>
#include <cstring>
#include <iostream>
#include "FileDevice.h"
#include "InputDevice.h"
#include "Opcode.h"
#include "OutputDevice.h"
Machine::Machine() {
@ -18,6 +20,10 @@ Machine::Machine() {
PC = 0;
SW = 0;
running = false;
timerThread = nullptr;
speed = 1000; // default 1000 kHz = 1 MHz
for (int i = 0; i <= MAX_ADDRESS; i++) {
memory[i] = 0;
}
@ -32,6 +38,8 @@ Machine::Machine() {
}
Machine::~Machine() {
stop();
for (int i = 0; i < MAX_DEVICES; i++) {
if (devices[i] != nullptr) {
delete devices[i];
@ -232,3 +240,352 @@ void Machine::setDevice(int num, Device* device) {
devices[num] = device;
}
}
void Machine::notImplemented(const char* mnemonic) {
std::cerr << "Error: instruction " << mnemonic << " not implemented" << std::endl;
}
void Machine::invalidOpcode(int opcode) {
std::cerr << "Error: invalid opcode 0x" << std::hex << opcode << std::endl;
}
void Machine::invalidAddressing() {
std::cerr << "Error: invalid addressing mode" << std::endl;
}
int Machine::fetch() {
int byte = getByte(PC);
PC = (PC + 1) & 0xFFFFFF;
return byte;
}
void Machine::execute() {
int byte1 = fetch();
int opcode = byte1 & 0xFC;
int ni = byte1 & 0x03;
// format 1 (no operand)
if (execF1(opcode)) {
return;
}
int byte2 = fetch();
// format 2 (register operand)
if (execF2(opcode, byte2)) {
return;
}
// format 3 or 4
int xbpe = (byte2 >> 4) & 0x0F;
int disp = byte2 & 0x0F;
int byte3 = fetch();
disp = (disp << 8) | byte3;
// format 4 (extended)
if (xbpe & 0x01) {
int byte4 = fetch();
disp = (disp << 8) | byte4;
}
int addr = disp;
// base relative
if (xbpe & 0x04) {
addr = B + disp;
}
// pc relative
else if (xbpe & 0x02) {
// sign extend 12-bit displacement for format 3
if (!(xbpe & 0x01) && (disp & 0x800)) {
disp = disp | 0xFFFFF000;
}
addr = PC + disp;
}
// indexed
if (xbpe & 0x08) {
addr = addr + X;
}
addr = addr & 0xFFFFFF;
execSICF3F4(opcode, ni, addr);
}
bool Machine::execF1(int opcode) {
switch (opcode) {
case Opcode::FIX:
A = (int)F;
return true;
case Opcode::FLOAT:
F = (double)A;
return true;
case Opcode::NORM:
notImplemented("NORM");
return true;
case Opcode::SIO:
case Opcode::HIO:
case Opcode::TIO:
notImplemented("SIO/HIO/TIO");
return true;
}
return false;
}
bool Machine::execF2(int opcode, int operand) {
int r1 = (operand >> 4) & 0x0F;
int r2 = operand & 0x0F;
switch (opcode) {
case Opcode::ADDR:
setReg(r2, getReg(r1) + getReg(r2));
return true;
case Opcode::SUBR:
setReg(r2, getReg(r2) - getReg(r1));
return true;
case Opcode::MULR:
setReg(r2, getReg(r1) * getReg(r2));
return true;
case Opcode::DIVR:
setReg(r2, getReg(r2) / getReg(r1));
return true;
case Opcode::COMPR: {
int val1 = getReg(r1);
int val2 = getReg(r2);
if (val1 < val2)
setCC_less();
else if (val1 == val2)
setCC_equal();
else
setCC_greater();
return true;
}
case Opcode::SHIFTL:
setReg(r1, getReg(r1) << (r2 + 1));
return true;
case Opcode::SHIFTR:
setReg(r1, getReg(r1) >> (r2 + 1));
return true;
case Opcode::RMO:
setReg(r2, getReg(r1));
return true;
case Opcode::CLEAR:
setReg(r1, 0);
return true;
case Opcode::TIXR: {
X = X + 1;
int val = getReg(r1);
if (X < val)
setCC_less();
else if (X == val)
setCC_equal();
else
setCC_greater();
return true;
}
case Opcode::SVC:
notImplemented("SVC");
return true;
}
return false;
}
bool Machine::execSICF3F4(int opcode, int ni, int operand) {
int addr = operand;
int value;
// get value based on addressing mode
if (ni == 0x00) {
// SIC format (simple)
value = getWord(addr);
} else if (ni == 0x01) {
// immediate
value = addr;
} else if (ni == 0x02) {
// indirect
addr = getWord(addr);
value = getWord(addr);
} else {
// simple (ni == 0x03)
value = getWord(addr);
}
switch (opcode) {
case Opcode::LDA:
A = value;
return true;
case Opcode::LDX:
X = value;
return true;
case Opcode::LDL:
L = value;
return true;
case Opcode::LDB:
B = value;
return true;
case Opcode::LDS:
S = value;
return true;
case Opcode::LDT:
T = value;
return true;
case Opcode::LDCH:
if (ni == 0x01) {
A = (A & 0xFFFF00) | (addr & 0xFF);
} else {
A = (A & 0xFFFF00) | getByte(addr);
}
return true;
case Opcode::STA:
setWord(addr, A);
return true;
case Opcode::STX:
setWord(addr, X);
return true;
case Opcode::STL:
setWord(addr, L);
return true;
case Opcode::STB:
setWord(addr, B);
return true;
case Opcode::STS:
setWord(addr, S);
return true;
case Opcode::STT:
setWord(addr, T);
return true;
case Opcode::STSW:
setWord(addr, SW);
return true;
case Opcode::STCH:
setByte(addr, A & 0xFF);
return true;
case Opcode::ADD:
A = A + value;
return true;
case Opcode::SUB:
A = A - value;
return true;
case Opcode::MUL:
A = A * value;
return true;
case Opcode::DIV:
A = A / value;
return true;
case Opcode::AND:
A = A & value;
return true;
case Opcode::OR:
A = A | value;
return true;
case Opcode::COMP:
if (A < value)
setCC_less();
else if (A == value)
setCC_equal();
else
setCC_greater();
return true;
case Opcode::TIX:
X = X + 1;
if (X < value)
setCC_less();
else if (X == value)
setCC_equal();
else
setCC_greater();
return true;
case Opcode::J:
PC = addr;
return true;
case Opcode::JEQ:
if (getCC() == 0x40) PC = addr;
return true;
case Opcode::JGT:
if (getCC() == 0x80) PC = addr;
return true;
case Opcode::JLT:
if (getCC() == 0x00) PC = addr;
return true;
case Opcode::JSUB:
L = PC;
PC = addr;
return true;
case Opcode::RSUB:
PC = L;
return true;
case Opcode::TD: {
Device* dev = getDevice(getByte(addr));
if (dev != nullptr && dev->test()) {
setCC_less();
} else {
setCC_equal();
}
return true;
}
case Opcode::RD: {
Device* dev = getDevice(getByte(addr));
if (dev != nullptr) {
A = (A & 0xFFFF00) | dev->read();
}
return true;
}
case Opcode::WD: {
Device* dev = getDevice(getByte(addr));
if (dev != nullptr) {
dev->write(A & 0xFF);
}
return true;
}
default:
invalidOpcode(opcode);
return false;
}
}
void Machine::start() {
if (running) return;
running = true;
timerThread = new std::thread([this]() {
while (running) {
int instructionsPerTick = speed / 1000;
if (instructionsPerTick < 1) instructionsPerTick = 1;
for (int i = 0; i < instructionsPerTick && running; i++) {
execute();
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
});
}
void Machine::stop() {
running = false;
if (timerThread != nullptr) {
timerThread->join();
delete timerThread;
timerThread = nullptr;
}
}
bool Machine::isRunning() const {
return running;
}
int Machine::getSpeed() const {
return speed;
}
void Machine::setSpeed(int kHz) {
speed = kHz;
}

View file

@ -2,6 +2,8 @@
#define MACHINE_H
#include <cstdint>
#include <thread>
#include <atomic>
#include "Device.h"
@ -24,6 +26,11 @@ class Machine {
int PC; // Program Counter (24-bit)
int SW; // Status Word (24-bit)
// timer simulation
std::atomic<bool> running;
std::thread* timerThread;
int speed; // kHz
public:
Machine();
~Machine();
@ -62,12 +69,31 @@ class Machine {
int getByte(int addr) const;
void setByte(int addr, int val);
int getWord(int addr) const;
void setWord(int addr, int val);
Device* getDevice(int num) const;
void setDevice(int num, Device* device);
// error handling
void notImplemented(const char* mnemonic);
void invalidOpcode(int opcode);
void invalidAddressing();
// execution
int fetch();
void execute();
bool execF1(int opcode);
bool execF2(int opcode, int operand);
bool execSICF3F4(int opcode, int ni, int operand);
// timer simulation
void start();
void stop();
bool isRunning() const;
int getSpeed() const;
void setSpeed(int kHz);
};
#endif // MACHINE_H