created sicxe emulator project
This commit is contained in:
parent
cb38efe586
commit
3332b2971b
18 changed files with 1051 additions and 0 deletions
19
simulator_SIC_XE/src/device.cpp
Normal file
19
simulator_SIC_XE/src/device.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include "device.h"
|
||||
|
||||
Device::Device()
|
||||
{
|
||||
}
|
||||
|
||||
bool Device::test()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char Device::read()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Device::write(unsigned char value)
|
||||
{
|
||||
}
|
||||
45
simulator_SIC_XE/src/file_device.cpp
Normal file
45
simulator_SIC_XE/src/file_device.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "file_device.h"
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
||||
FileDevice::FileDevice(const std::string &filename)
|
||||
{
|
||||
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::binary);
|
||||
if (!fileStream.is_open()) {
|
||||
std::ofstream create(filename, std::ios::binary);
|
||||
if (!create) {
|
||||
throw std::runtime_error("Failed to create file: " + filename);
|
||||
}
|
||||
create.close();
|
||||
|
||||
fileStream.clear();
|
||||
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::binary);
|
||||
if (!fileStream.is_open()) {
|
||||
throw std::runtime_error("Failed to open file after creating: " + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDevice::~FileDevice()
|
||||
{
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char FileDevice::read()
|
||||
{
|
||||
unsigned char value = 0;
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.read(reinterpret_cast<char*>(&value), sizeof(value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void FileDevice::write(unsigned char value)
|
||||
{
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.write(reinterpret_cast<const char*>(&value), sizeof(value));
|
||||
fileStream.flush();
|
||||
}
|
||||
}
|
||||
20
simulator_SIC_XE/src/input_device.cpp
Normal file
20
simulator_SIC_XE/src/input_device.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "input_device.h"
|
||||
|
||||
InputDevice::InputDevice(std::istream &in)
|
||||
: inStream(in)
|
||||
{
|
||||
}
|
||||
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned char InputDevice::read()
|
||||
{
|
||||
char c;
|
||||
if (!inStream.get(c)) {
|
||||
// If stream is at EOF or error, return 0
|
||||
return 0;
|
||||
}
|
||||
return static_cast<unsigned char>(c);
|
||||
}
|
||||
60
simulator_SIC_XE/src/instructions.cpp
Normal file
60
simulator_SIC_XE/src/instructions.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#include "instructions.h"
|
||||
#include "machine.h"
|
||||
|
||||
|
||||
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 divr_handler(Machine& m, int r1, int r2) {
|
||||
|
||||
if (m.getReg(r2) == 0) {
|
||||
m.invalidOpcode(DIVR);
|
||||
return;
|
||||
}
|
||||
m.setReg(r2, m.getReg(r2) / m.getReg(r1));
|
||||
}
|
||||
|
||||
void mulr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r1) * m.getReg(r2));
|
||||
}
|
||||
|
||||
void rmo_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r1));
|
||||
}
|
||||
|
||||
void shiftl_handler(Machine &m, int r1, int n)
|
||||
{
|
||||
m.setReg(r1, m.getReg(r1) << n);
|
||||
}
|
||||
|
||||
void shiftr_handler(Machine &m, int r1, int n)
|
||||
{
|
||||
m.setReg(r1, m.getReg(r1) >> n);
|
||||
}
|
||||
void subr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r2) - m.getReg(r1));
|
||||
}
|
||||
|
||||
// TODO: implement SVC functionality
|
||||
void svc_handler(Machine &m, int n, int unused)
|
||||
{
|
||||
m.notImplemented("SVC");
|
||||
}
|
||||
|
||||
void tixr_handler(Machine &m, int r1, int unused)
|
||||
{
|
||||
m.setX(m.getX() + 1);
|
||||
int valX = m.getX();
|
||||
int valR1 = m.getReg(r1);
|
||||
m.setSW(sic_comp(valX, valR1, m.getSW()));
|
||||
}
|
||||
261
simulator_SIC_XE/src/machine.cpp
Normal file
261
simulator_SIC_XE/src/machine.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
#include "machine.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
|
||||
using std::make_shared;
|
||||
|
||||
string prefix = "Machine error: ";
|
||||
|
||||
|
||||
Machine::Machine()
|
||||
{
|
||||
devices.resize(NUM_DEVICES);
|
||||
// device 0: standard input
|
||||
devices[0] = make_shared<InputDevice>(std::cin);
|
||||
// device 1: standard output
|
||||
devices[1] = make_shared<OutputDevice>(std::cout);
|
||||
// device 2: standard error
|
||||
devices[2] = make_shared<OutputDevice>(std::cerr);
|
||||
}
|
||||
|
||||
Machine::~Machine()
|
||||
{
|
||||
for (auto& device : devices) {
|
||||
device.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::notImplemented(string mnemonic)
|
||||
{
|
||||
cout << prefix << "Not implemented: " << mnemonic << endl;
|
||||
}
|
||||
|
||||
void Machine::invalidOpcode(int opcode)
|
||||
{
|
||||
cout << prefix << "Invalid opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
void Machine::invalidAddressing()
|
||||
{
|
||||
cout << prefix << "Invalid addressing mode" << endl;
|
||||
}
|
||||
|
||||
void Machine::divisionByZero(int opcode)
|
||||
{
|
||||
cout << prefix << "Division by zero error in opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
void Machine::undefinedHandler(int opcode)
|
||||
{
|
||||
cout << prefix << "Undefined handler for opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
int Machine::getReg(int regNum) const
|
||||
{
|
||||
switch (regNum) {
|
||||
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:
|
||||
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle double for F register
|
||||
void Machine::setReg(int regNum, int value)
|
||||
{
|
||||
value = toSIC24(value);
|
||||
switch (regNum) {
|
||||
case 0: A = value; break;
|
||||
case 1: X = value; break;
|
||||
case 2: L = value; break;
|
||||
case 3: B = value; break;
|
||||
case 4: S = value; break;
|
||||
case 5: T = value; break;
|
||||
case 6: F = value; break;
|
||||
case 8: PC = value; break;
|
||||
case 9: SW = value; break;
|
||||
default:
|
||||
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Machine::getByte(int address)
|
||||
{
|
||||
if (address < 0 || address >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(memory[address]);
|
||||
}
|
||||
|
||||
void Machine::setByte(int address, int value)
|
||||
{
|
||||
if(address < 0 || address >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
memory[address] = static_cast<unsigned char>(value);
|
||||
}
|
||||
|
||||
// Assuming word is 3 bytes
|
||||
|
||||
int Machine::getWord(int address)
|
||||
{
|
||||
if (address < 0 || address + 2 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(memory[address]) | (static_cast<int>(memory[address + 1]) << 8) | (static_cast<int>(memory[address + 2]) << 16);
|
||||
}
|
||||
|
||||
// Assuming word is 3 bytes
|
||||
void Machine::setWord(int address, int value)
|
||||
{
|
||||
if(address < 0 || address + 2 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
memory[address] = static_cast<unsigned char>(value & 0xFF);
|
||||
memory[address + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
|
||||
memory[address + 2] = static_cast<unsigned char>((value >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
// TODO: implement proper float storage and retrieval
|
||||
double Machine::getFloat(int address)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void Machine::setFloat(int address, double value)
|
||||
{
|
||||
// TODO: implement proper float storage
|
||||
}
|
||||
|
||||
Device &Machine::getDevice(int num)
|
||||
{
|
||||
if(num < 0 || num >= static_cast<int>(devices.size()) || !devices[num]) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return fallbackDevice;
|
||||
}
|
||||
return *devices[num];
|
||||
}
|
||||
|
||||
void Machine::setDevice(int num, std::shared_ptr<Device> device)
|
||||
{
|
||||
if(num < 0 || num >= NUM_DEVICES) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return;
|
||||
}
|
||||
if(static_cast<int>(devices.size()) != NUM_DEVICES) {
|
||||
devices.resize(NUM_DEVICES);
|
||||
}
|
||||
// Enforce: devices with index >= 2 must be FileDevice instances
|
||||
if (num >= 2) {
|
||||
// try dynamic cast
|
||||
if (std::dynamic_pointer_cast<FileDevice>(device) == nullptr) {
|
||||
cerr << prefix << "Device at index " << num << " must be a FileDevice." << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
devices[num] = device;
|
||||
}
|
||||
|
||||
void Machine::setFileDevice(int num, const std::string &filename)
|
||||
{
|
||||
if(num < 0 || num >= NUM_DEVICES) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return;
|
||||
}
|
||||
if(static_cast<int>(devices.size()) != NUM_DEVICES) {
|
||||
devices.resize(NUM_DEVICES);
|
||||
}
|
||||
try {
|
||||
devices[num] = std::make_shared<FileDevice>(filename);
|
||||
} catch (const std::exception &e) {
|
||||
cerr << prefix << "Failed to create FileDevice for index " << num << ": " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool Machine::execF1(int opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case FIX:
|
||||
setA(static_cast<int>(getF()));
|
||||
return true;
|
||||
case FLOAT:
|
||||
setF(static_cast<double>(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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Machine::execF2(int opcode, int operand)
|
||||
{
|
||||
int r1 = (operand >> 4) & 0xF;
|
||||
int r2 = operand & 0xF;
|
||||
|
||||
if (instructions[opcode].handler) {
|
||||
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(instructions[opcode].handler);
|
||||
handler(*this, r1, r2);
|
||||
return true;
|
||||
}
|
||||
undefinedHandler(opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Machine::execSICF3F4(int opcode, int ni, int operand)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
27
simulator_SIC_XE/src/main.cpp
Normal file
27
simulator_SIC_XE/src/main.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include <iostream>
|
||||
#include "machine.h"
|
||||
#include "file_device.h"
|
||||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
int main()
|
||||
{
|
||||
loadInstructionSet();
|
||||
Machine machine;
|
||||
|
||||
cout << "Machine initialized successfully." << endl;
|
||||
|
||||
// COMPUTE A + B and store result in B
|
||||
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;
|
||||
machine.execute();
|
||||
cout << "After ADDR: A = " << machine.getA() << ", B = " << machine.getB() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
75
simulator_SIC_XE/src/opcode.cpp
Normal file
75
simulator_SIC_XE/src/opcode.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
#include <utility>
|
||||
|
||||
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<RawHandler>(addr_handler)};
|
||||
instructions[AND] = {"AND", InstructionType::TYPE3_4, nullptr};
|
||||
instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(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<RawHandler>(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<RawHandler>(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<RawHandler>(rmo_handler)};
|
||||
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, nullptr};
|
||||
instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftl_handler)};
|
||||
instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(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<RawHandler>(subr_handler)};
|
||||
instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast<RawHandler>(svc_handler)};
|
||||
instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(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};
|
||||
|
||||
// Mark uninitialized opcodes as INVALID
|
||||
for (int i = 0; i < 0xff; ++i) {
|
||||
if (instructions[i].name == nullptr) {
|
||||
instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||
}
|
||||
}
|
||||
}
|
||||
16
simulator_SIC_XE/src/output_device.cpp
Normal file
16
simulator_SIC_XE/src/output_device.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include "output_device.h"
|
||||
|
||||
OutputDevice::OutputDevice(std::ostream &out)
|
||||
: outStream(out)
|
||||
{
|
||||
}
|
||||
|
||||
OutputDevice::~OutputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void OutputDevice::write(unsigned char value)
|
||||
{
|
||||
outStream.put(static_cast<char>(value));
|
||||
outStream.flush();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue