918 lines
32 KiB
C++
918 lines
32 KiB
C++
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
#include "MachineController.h"
|
|
#include "../../include/machine.h"
|
|
#include "../../include/instructions.h"
|
|
#include "../../include/opcode.h"
|
|
#include "../../include/constants.h"
|
|
#include "../../../include/loader.h"
|
|
|
|
#include <QIntValidator>
|
|
#include <QLineEdit>
|
|
#include <QDebug>
|
|
#include <QRegularExpressionValidator>
|
|
#include <QDoubleValidator>
|
|
#include <QPushButton>
|
|
#include <cstdint>
|
|
#include <QVBoxLayout>
|
|
#include <QLabel>
|
|
#include <QFont>
|
|
|
|
class Loader;
|
|
|
|
std::shared_ptr<Loader> g_loader;
|
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
|
QMainWindow(parent),
|
|
ui(new Ui::MainWindow),
|
|
m_machine(std::make_shared<Machine>()),
|
|
m_controller(std::make_unique<MachineController>(m_machine, this))
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
ui->regA_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
|
ui->regB_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
|
ui->regS_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
|
ui->regT_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
|
ui->regX_dec_field->setValidator(new QIntValidator(-8388608, 8388607, this));
|
|
// unsigned 24 bit
|
|
ui->regL_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
|
ui->regPC_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
|
ui->regSW_dec_field->setValidator(new QIntValidator(0, 16777215, this));
|
|
// float
|
|
ui->regF_dec_field->setValidator(new QDoubleValidator(-3.402823e38, 3.402823e38, 6, this));
|
|
|
|
QRegularExpressionValidator* hexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,6}$"), this);
|
|
ui->regA_hex_field->setValidator(hexValidator);
|
|
ui->regB_hex_field->setValidator(hexValidator);
|
|
ui->regX_hex_field->setValidator(hexValidator);
|
|
ui->regS_hex_field->setValidator(hexValidator);
|
|
ui->regT_hex_field->setValidator(hexValidator);
|
|
ui->regL_hex_field->setValidator(hexValidator);
|
|
ui->regPC_hex_field->setValidator(hexValidator);
|
|
ui->regSW_hex_field->setValidator(hexValidator);
|
|
|
|
QRegularExpressionValidator* binValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,24}$"), this);
|
|
ui->regA_bin_field->setValidator(binValidator);
|
|
ui->regB_bin_field->setValidator(binValidator);
|
|
ui->regX_bin_field->setValidator(binValidator);
|
|
ui->regS_bin_field->setValidator(binValidator);
|
|
ui->regT_bin_field->setValidator(binValidator);
|
|
ui->regL_bin_field->setValidator(binValidator);
|
|
ui->regPC_bin_field->setValidator(binValidator);
|
|
ui->regSW_bin_field->setValidator(binValidator);
|
|
|
|
QRegularExpressionValidator* floatHexValidator = new QRegularExpressionValidator(QRegularExpression("^(0x)?[0-9A-Fa-f]{1,12}$"), this);
|
|
ui->regF_hex_field->setValidator(floatHexValidator);
|
|
|
|
QRegularExpressionValidator* floatBinValidator = new QRegularExpressionValidator(QRegularExpression("^[01]{1,48}$"), this);
|
|
ui->regF_bin_field->setValidator(floatBinValidator);
|
|
|
|
|
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays);
|
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay);
|
|
|
|
connectRegisterFields();
|
|
|
|
connect(ui->StartBtn, &QPushButton::clicked, this, &MainWindow::startExecution);
|
|
connect(ui->StopBtn, &QPushButton::clicked, this, &MainWindow::stopExecution);
|
|
connect(ui->StepBtn, &QPushButton::clicked, this, &MainWindow::stepExecution);
|
|
|
|
connect(ui->MemoryInc256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc256);
|
|
connect(ui->MemoryInc4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc4096);
|
|
connect(ui->MemoryInc65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryInc65536);
|
|
connect(ui->MemoryDec256Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec256);
|
|
connect(ui->MemoryDec4096Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec4096);
|
|
connect(ui->MemoryDec65536Btn, &QPushButton::clicked, this, &MainWindow::onMemoryDec65536);
|
|
connect(ui->MemoryGoToStart_2, &QPushButton::clicked, this, &MainWindow::onMemoryGoToStart);
|
|
connect(ui->MemoryGoToEnd, &QPushButton::clicked, this, &MainWindow::onMemoryGoToEnd);
|
|
|
|
connect(ui->DisasmInc256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc);
|
|
connect(ui->DisasmInc4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc16);
|
|
connect(ui->DisasmInc65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyInc256);
|
|
connect(ui->DisasmDec256Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec);
|
|
connect(ui->DisasmDec4096Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec16);
|
|
connect(ui->DisasmDec65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec256);
|
|
connect(ui->DisasmGoToStart, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToStart);
|
|
connect(ui->DisasmGoToEnd, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToEnd);
|
|
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateDisassemblyDisplay);
|
|
|
|
setupMemoryDisplay();
|
|
setupDisassemblyDisplay();
|
|
//loadDemoProgram();
|
|
g_loader = std::make_shared<Loader>(machine(), std::string(PATH_RESOURCES) + "demo_program.obj");
|
|
g_loader->load();
|
|
|
|
updateRegisterDisplays();
|
|
updateMemoryDisplay();
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void MainWindow::updateRegisterDisplays()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
// Update all register display formats (decimal, hex, binary)
|
|
updateAllFormatsForRegister("regA", m_machine->getA());
|
|
updateAllFormatsForRegister("regB", m_machine->getB());
|
|
updateAllFormatsForRegister("regX", m_machine->getX());
|
|
updateAllFormatsForRegister("regS", m_machine->getS());
|
|
updateAllFormatsForRegister("regT", m_machine->getT());
|
|
updateAllFormatsForRegister("regL", m_machine->getL());
|
|
updateAllFormatsForRegister("regPC", m_machine->getPC());
|
|
updateAllFormatsForRegister("regSW", m_machine->getSW());
|
|
updateFloatRegisterFormats("regF", m_machine->getF());
|
|
}
|
|
|
|
void MainWindow::updateSingleRegisterDisplay(const QString& fieldName, int value)
|
|
{
|
|
QLineEdit* field = findChild<QLineEdit*>(fieldName);
|
|
if (field) {
|
|
// Only update if the field doesn't have focus (to avoid interfering with user input)
|
|
if (!field->hasFocus()) {
|
|
field->setText(QString::number(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateAllFormatsForRegister(const QString& regPrefix, int value)
|
|
{
|
|
// Update decimal field
|
|
QLineEdit* decField = findChild<QLineEdit*>(regPrefix + "_dec_field");
|
|
if (decField && !decField->hasFocus()) {
|
|
decField->setText(QString::number(value));
|
|
}
|
|
|
|
// Update hex field
|
|
QLineEdit* hexField = findChild<QLineEdit*>(regPrefix + "_hex_field");
|
|
if (hexField && !hexField->hasFocus()) {
|
|
// Convert to 24-bit representation, handle negative numbers
|
|
unsigned int unsignedValue = static_cast<unsigned int>(value) & 0xFFFFFF;
|
|
hexField->setText(QString("0x%1").arg(unsignedValue, 6, 16, QChar('0')).toUpper());
|
|
}
|
|
|
|
// Update binary field
|
|
QLineEdit* binField = findChild<QLineEdit*>(regPrefix + "_bin_field");
|
|
if (binField && !binField->hasFocus()) {
|
|
// Convert to 24-bit binary representation
|
|
unsigned int unsignedValue = static_cast<unsigned int>(value) & 0xFFFFFF;
|
|
QString binaryStr = QString::number(unsignedValue, 2);
|
|
// Pad to 24 bits
|
|
binaryStr = binaryStr.rightJustified(24, '0');
|
|
binField->setText(binaryStr);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateFloatRegisterFormats(const QString& regPrefix, double value)
|
|
{
|
|
// Update decimal field
|
|
QLineEdit* decField = findChild<QLineEdit*>(regPrefix + "_dec_field");
|
|
if (decField && !decField->hasFocus()) {
|
|
decField->setText(QString::number(value, 'g', 10));
|
|
}
|
|
|
|
// Update hex field (48-bit float representation)
|
|
QLineEdit* hexField = findChild<QLineEdit*>(regPrefix + "_hex_field");
|
|
if (hexField && !hexField->hasFocus()) {
|
|
// Convert double to 48-bit hex representation
|
|
// For SIC/XE, we need to convert to the 48-bit float format
|
|
uint64_t* intPtr = reinterpret_cast<uint64_t*>(&value);
|
|
uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits
|
|
hexField->setText(QString("0x%1").arg(bits48, 12, 16, QChar('0')).toUpper());
|
|
}
|
|
|
|
// Update binary field (48-bit float representation)
|
|
QLineEdit* binField = findChild<QLineEdit*>(regPrefix + "_bin_field");
|
|
if (binField && !binField->hasFocus()) {
|
|
// Convert double to 48-bit binary representation
|
|
uint64_t* intPtr = reinterpret_cast<uint64_t*>(&value);
|
|
uint64_t bits48 = (*intPtr) & 0xFFFFFFFFFFFFULL; // Mask to 48 bits
|
|
QString binaryStr = QString::number(bits48, 2);
|
|
// Pad to 48 bits
|
|
binaryStr = binaryStr.rightJustified(48, '0');
|
|
binField->setText(binaryStr);
|
|
}
|
|
}
|
|
|
|
void MainWindow::connectRegisterFields()
|
|
{
|
|
// Connect decimal register fields to update machine registers when changed
|
|
connect(ui->regA_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regB_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regX_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regS_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regT_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regL_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regPC_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regSW_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
connect(ui->regF_dec_field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
|
|
// Connect hex register fields
|
|
QLineEdit* hexFields[] = {
|
|
ui->regA_hex_field, ui->regB_hex_field, ui->regX_hex_field,
|
|
ui->regS_hex_field, ui->regT_hex_field, ui->regL_hex_field,
|
|
ui->regPC_hex_field, ui->regSW_hex_field, ui->regF_hex_field
|
|
};
|
|
for (auto* field : hexFields) {
|
|
if (field) {
|
|
connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
}
|
|
}
|
|
|
|
// Connect binary register fields
|
|
QLineEdit* binFields[] = {
|
|
ui->regA_bin_field, ui->regB_bin_field, ui->regX_bin_field,
|
|
ui->regS_bin_field, ui->regT_bin_field, ui->regL_bin_field,
|
|
ui->regPC_bin_field, ui->regSW_bin_field, ui->regF_bin_field
|
|
};
|
|
for (auto* field : binFields) {
|
|
if (field) {
|
|
connect(field, &QLineEdit::editingFinished, this, &MainWindow::onRegisterFieldChanged);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::onRegisterFieldChanged()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
QLineEdit* field = qobject_cast<QLineEdit*>(sender());
|
|
if (!field) return;
|
|
|
|
QString objectName = field->objectName();
|
|
QString regName = objectName.split('_')[0];
|
|
|
|
|
|
if (regName == "regF") {
|
|
handleFloatRegisterFieldChanged(field, objectName);
|
|
return;
|
|
}
|
|
|
|
// Handle integer registers
|
|
int value = 0;
|
|
bool ok = false;
|
|
|
|
// Parse value based on field type
|
|
if (objectName.contains("_dec_field")) {
|
|
value = field->text().toInt(&ok);
|
|
} else if (objectName.contains("_hex_field")) {
|
|
QString hexText = field->text();
|
|
// Remove 0x prefix if present
|
|
if (hexText.startsWith("0x", Qt::CaseInsensitive)) {
|
|
hexText = hexText.mid(2);
|
|
}
|
|
value = hexText.toInt(&ok, 16);
|
|
|
|
if (ok && (regName == "regA" || regName == "regB" || regName == "regX" ||
|
|
regName == "regS" || regName == "regT")) {
|
|
if (value > 0x7FFFFF) {
|
|
value = value - 0x1000000;
|
|
}
|
|
}
|
|
} else if (objectName.contains("_bin_field")) {
|
|
value = field->text().toInt(&ok, 2);
|
|
|
|
if (ok && (regName == "regA" || regName == "regB" || regName == "regX" ||
|
|
regName == "regS" || regName == "regT")) {
|
|
if (value > 0x7FFFFF) {
|
|
value = value - 0x1000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
|
|
updateRegisterDisplays();
|
|
return;
|
|
}
|
|
|
|
if (regName == "regA") {
|
|
m_machine->setA(value);
|
|
updateAllFormatsForRegister("regA", m_machine->getA());
|
|
} else if (regName == "regB") {
|
|
m_machine->setB(value);
|
|
updateAllFormatsForRegister("regB", m_machine->getB());
|
|
} else if (regName == "regX") {
|
|
m_machine->setX(value);
|
|
updateAllFormatsForRegister("regX", m_machine->getX());
|
|
} else if (regName == "regS") {
|
|
m_machine->setS(value);
|
|
updateAllFormatsForRegister("regS", m_machine->getS());
|
|
} else if (regName == "regT") {
|
|
m_machine->setT(value);
|
|
updateAllFormatsForRegister("regT", m_machine->getT());
|
|
} else if (regName == "regL") {
|
|
m_machine->setL(value);
|
|
updateAllFormatsForRegister("regL", m_machine->getL());
|
|
} else if (regName == "regPC") {
|
|
m_machine->setPC(value);
|
|
updateAllFormatsForRegister("regPC", m_machine->getPC());
|
|
} else if (regName == "regSW") {
|
|
m_machine->setSW(value);
|
|
updateAllFormatsForRegister("regSW", m_machine->getSW());
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName)
|
|
{
|
|
double value = 0.0;
|
|
bool ok = false;
|
|
|
|
if (objectName.contains("_dec_field")) {
|
|
value = field->text().toDouble(&ok);
|
|
} else if (objectName.contains("_hex_field")) {
|
|
QString hexText = field->text();
|
|
if (hexText.startsWith("0x", Qt::CaseInsensitive)) {
|
|
hexText = hexText.mid(2);
|
|
}
|
|
|
|
uint64_t intValue = hexText.toULongLong(&ok, 16);
|
|
if (ok) {
|
|
intValue &= 0xFFFFFFFFFFFFULL;
|
|
double* floatPtr = reinterpret_cast<double*>(&intValue);
|
|
value = *floatPtr;
|
|
}
|
|
} else if (objectName.contains("_bin_field")) {
|
|
uint64_t intValue = field->text().toULongLong(&ok, 2);
|
|
if (ok) {
|
|
intValue &= 0xFFFFFFFFFFFFULL;
|
|
double* floatPtr = reinterpret_cast<double*>(&intValue);
|
|
value = *floatPtr;
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
updateRegisterDisplays();
|
|
return;
|
|
}
|
|
|
|
m_machine->setF(value);
|
|
updateFloatRegisterFormats("regF", m_machine->getF());
|
|
}
|
|
|
|
void MainWindow::setTestRegisterValues()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
// Set some test values to demonstrate the register updating
|
|
m_machine->setA(12345); // Decimal: 12345, Hex: 0x003039, Binary: 000000011000000111001
|
|
m_machine->setB(-1000); // Negative value to test signed representation
|
|
m_machine->setX(0xABCDEF); // Hex value to test various formats
|
|
m_machine->setS(255); // Simple power of 2 minus 1
|
|
m_machine->setT(0x7FFFFF); // Maximum positive 24-bit value
|
|
|
|
// Update all displays
|
|
updateRegisterDisplays();
|
|
}
|
|
|
|
void MainWindow::startExecution()
|
|
{
|
|
if (m_controller) {
|
|
m_controller->start();
|
|
}
|
|
}
|
|
|
|
void MainWindow::stopExecution()
|
|
{
|
|
if (m_controller) {
|
|
m_controller->stop();
|
|
}
|
|
}
|
|
|
|
void MainWindow::stepExecution()
|
|
{
|
|
if (m_controller) {
|
|
m_controller->step();
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadDemoProgram()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
// Load the instruction set first
|
|
loadInstructionSet();
|
|
|
|
qDebug() << "Loading SIC/XE Demo Program: Array Sum with Indirect Addressing";
|
|
|
|
// Memory layout
|
|
const int ARRAY_ADDR = 0x100; // Array of 3 numbers
|
|
const int PTR_ADDR = 0x200; // Pointer to array
|
|
const int SUM_ADDR = 0x300; // Result storage
|
|
const int COUNTER_ADDR = 0x310; // Loop counter
|
|
|
|
// Initialize array with values: 10, 20, 30
|
|
m_machine->setWord(ARRAY_ADDR, 10);
|
|
m_machine->setWord(ARRAY_ADDR + 3, 20);
|
|
m_machine->setWord(ARRAY_ADDR + 6, 30);
|
|
|
|
// Initialize pointer to point to array
|
|
m_machine->setWord(PTR_ADDR, ARRAY_ADDR);
|
|
|
|
// Initialize counter to 3
|
|
m_machine->setWord(COUNTER_ADDR, 3);
|
|
|
|
// Initialize sum to 0
|
|
m_machine->setWord(SUM_ADDR, 0);
|
|
|
|
int addr = 0x00;
|
|
|
|
// Program: Sum array elements using indirect addressing
|
|
// 0x00: LDA #0 ; Initialize accumulator to 0
|
|
m_machine->setByte(addr++, 0x01); // LDA with immediate (n=0,i=1)
|
|
m_machine->setByte(addr++, 0x00);
|
|
m_machine->setByte(addr++, 0x00);
|
|
|
|
// 0x03: STA SUM_ADDR ; Store 0 in SUM
|
|
m_machine->setByte(addr++, 0x0F); // STA
|
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
|
|
|
// 0x06: LDX #0 ; Initialize index to 0
|
|
m_machine->setByte(addr++, 0x05); // LDX with immediate
|
|
m_machine->setByte(addr++, 0x00);
|
|
m_machine->setByte(addr++, 0x00);
|
|
|
|
// LOOP (0x09):
|
|
const int LOOP_START = addr;
|
|
|
|
// 0x09: LDA @PTR_ADDR ; Load value indirectly through pointer
|
|
m_machine->setByte(addr++, 0x02); // LDA with indirect (n=1,i=0)
|
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
|
|
|
// 0x0C: ADD SUM_ADDR ; Add to sum
|
|
m_machine->setByte(addr++, 0x1B); // ADD
|
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
|
|
|
// 0x0F: STA SUM_ADDR ; Store result back
|
|
m_machine->setByte(addr++, 0x0F); // STA
|
|
m_machine->setByte(addr++, (SUM_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, SUM_ADDR & 0xFF);
|
|
|
|
// 0x12: LDA PTR_ADDR ; Load current pointer value
|
|
m_machine->setByte(addr++, 0x03); // LDA
|
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
|
|
|
// 0x15: ADD #3 ; Add 3 to move to next array element
|
|
m_machine->setByte(addr++, 0x19); // ADD with immediate
|
|
m_machine->setByte(addr++, 0x00);
|
|
m_machine->setByte(addr++, 0x03);
|
|
|
|
// 0x18: STA PTR_ADDR ; Store updated pointer
|
|
m_machine->setByte(addr++, 0x0F); // STA
|
|
m_machine->setByte(addr++, (PTR_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, PTR_ADDR & 0xFF);
|
|
|
|
// 0x1B: LDA COUNTER_ADDR ; Load counter
|
|
m_machine->setByte(addr++, 0x03); // LDA
|
|
m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, COUNTER_ADDR & 0xFF);
|
|
|
|
// 0x1E: ADD #-1 ; Decrement counter (add -1)
|
|
m_machine->setByte(addr++, 0x19); // ADD with immediate
|
|
m_machine->setByte(addr++, 0x0F); // -1 in 12-bit two's complement
|
|
m_machine->setByte(addr++, 0xFF);
|
|
|
|
// 0x21: STA COUNTER_ADDR ; Store counter
|
|
m_machine->setByte(addr++, 0x0F); // STA
|
|
m_machine->setByte(addr++, (COUNTER_ADDR >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, COUNTER_ADDR & 0xFF);
|
|
|
|
// 0x24: COMP #0 ; Compare with 0
|
|
m_machine->setByte(addr++, 0x29); // COMP with immediate
|
|
m_machine->setByte(addr++, 0x00);
|
|
m_machine->setByte(addr++, 0x00);
|
|
|
|
// 0x27: JGT LOOP ; Jump if greater than 0
|
|
m_machine->setByte(addr++, 0x37); // JGT
|
|
m_machine->setByte(addr++, (LOOP_START >> 8) & 0xFF);
|
|
m_machine->setByte(addr++, LOOP_START & 0xFF);
|
|
|
|
// 0x2A: J 0x2A ; Infinite loop (halt)
|
|
m_machine->setByte(addr++, 0x3F); // J
|
|
m_machine->setByte(addr++, 0x00);
|
|
m_machine->setByte(addr++, 0x2A);
|
|
|
|
// Set PC to start of program
|
|
m_machine->setPC(0x00);
|
|
|
|
qDebug() << "Program loaded:";
|
|
qDebug() << " Array at 0x" << QString::number(ARRAY_ADDR, 16).toUpper() << " = [10, 20, 30]";
|
|
qDebug() << " Pointer at 0x" << QString::number(PTR_ADDR, 16).toUpper();
|
|
qDebug() << " Sum will be stored at 0x" << QString::number(SUM_ADDR, 16).toUpper();
|
|
qDebug() << " Expected result: 60 (0x3C)";
|
|
}
|
|
|
|
void MainWindow::setupMemoryDisplay()
|
|
{
|
|
// Set the title
|
|
ui->MemorygroupBox->setTitle("Memory (RAM)");
|
|
}
|
|
|
|
void MainWindow::onMemoryInc256()
|
|
{
|
|
m_memoryOffset += 256;
|
|
if (m_memoryOffset > 1048576 - 256) {
|
|
m_memoryOffset = 1048576 - 256;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryInc4096()
|
|
{
|
|
m_memoryOffset += 4096;
|
|
if (m_memoryOffset > 1048576 - 256) {
|
|
m_memoryOffset = 1048576 - 256;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryInc65536()
|
|
{
|
|
m_memoryOffset += 65536;
|
|
if (m_memoryOffset > 1048576 - 256) {
|
|
m_memoryOffset = 1048576 - 256;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryDec256()
|
|
{
|
|
m_memoryOffset -= 256;
|
|
if (m_memoryOffset < 0) {
|
|
m_memoryOffset = 0;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryDec4096()
|
|
{
|
|
m_memoryOffset -= 4096;
|
|
if (m_memoryOffset < 0) {
|
|
m_memoryOffset = 0;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryDec65536()
|
|
{
|
|
m_memoryOffset -= 65536;
|
|
if (m_memoryOffset < 0) {
|
|
m_memoryOffset = 0;
|
|
}
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryGoToStart()
|
|
{
|
|
m_memoryOffset = 0;
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::onMemoryGoToEnd()
|
|
{
|
|
m_memoryOffset = 1048576 - 256;
|
|
updateMemoryDisplay();
|
|
}
|
|
|
|
void MainWindow::setupDisassemblyDisplay()
|
|
{
|
|
ui->MemorygroupBox_3->setTitle("Disassembly");
|
|
}
|
|
|
|
void MainWindow::onDisassemblyInc()
|
|
{
|
|
// Move forward by 1 instruction
|
|
auto instr = disassembleAt(m_disassemblyOffset);
|
|
m_disassemblyOffset += instr.size;
|
|
if (m_disassemblyOffset > 1048576 - 16) {
|
|
m_disassemblyOffset = 1048576 - 16;
|
|
}
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyInc16()
|
|
{
|
|
// Move forward by 16 instructions
|
|
for (int i = 0; i < 16 && m_disassemblyOffset < 1048576 - 16; i++) {
|
|
auto instr = disassembleAt(m_disassemblyOffset);
|
|
m_disassemblyOffset += instr.size;
|
|
}
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyInc256()
|
|
{
|
|
// Move forward by 256 instructions
|
|
for (int i = 0; i < 256 && m_disassemblyOffset < 1048576 - 16; i++) {
|
|
auto instr = disassembleAt(m_disassemblyOffset);
|
|
m_disassemblyOffset += instr.size;
|
|
}
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyDec()
|
|
{
|
|
// Move back by trying to find previous instruction (assume max 4 bytes)
|
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 4);
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyDec16()
|
|
{
|
|
// Move back by approximately 16 instructions (16*3 = 48 bytes avg)
|
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 48);
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyDec256()
|
|
{
|
|
// Move back by approximately 256 instructions (256*3 = 768 bytes avg)
|
|
m_disassemblyOffset = std::max(0, m_disassemblyOffset - 768);
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyGoToStart()
|
|
{
|
|
m_disassemblyOffset = 0;
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
void MainWindow::onDisassemblyGoToEnd()
|
|
{
|
|
m_disassemblyOffset = std::max(0, 1048576 - 1024);
|
|
updateDisassemblyDisplay();
|
|
}
|
|
|
|
MainWindow::DisassembledInstruction MainWindow::disassembleAt(int address)
|
|
{
|
|
DisassembledInstruction result;
|
|
result.address = address;
|
|
result.size = 1;
|
|
result.mnemonic = "???";
|
|
result.operand = "";
|
|
result.effectiveAddr = -1;
|
|
result.isImmediate = false;
|
|
result.isIndirect = false;
|
|
|
|
if (address >= 1048576) {
|
|
return result;
|
|
}
|
|
|
|
int byte1 = m_machine->getByte(address);
|
|
int opcode = byte1 & 0xFC; // Mask off lower 2 bits (n, i flags)
|
|
|
|
if (opcode >= 0xff || instructions[opcode].type == InstructionType::INVALID) {
|
|
result.mnemonic = QString("BYTE 0x%1").arg(byte1, 2, 16, QChar('0')).toUpper();
|
|
return result;
|
|
}
|
|
|
|
result.mnemonic = QString(instructions[opcode].name);
|
|
|
|
switch (instructions[opcode].type) {
|
|
case InstructionType::TYPE1:
|
|
result.size = 1;
|
|
break;
|
|
|
|
case InstructionType::TYPE2: {
|
|
result.size = 2;
|
|
if (address + 1 < 1048576) {
|
|
int byte2 = m_machine->getByte(address + 1);
|
|
int r1 = (byte2 >> 4) & 0xF;
|
|
int r2 = byte2 & 0xF;
|
|
|
|
const char* regNames[] = {"A", "X", "L", "B", "S", "T", "F", "?", "PC", "SW"};
|
|
QString reg1Str = (r1 < 10) ? regNames[r1] : "?";
|
|
QString reg2Str = (r2 < 10) ? regNames[r2] : "?";
|
|
|
|
result.operand = QString("%1, %2").arg(reg1Str).arg(reg2Str);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case InstructionType::TYPE3_4: {
|
|
if (address + 2 >= 1048576) {
|
|
result.size = 3;
|
|
break;
|
|
}
|
|
|
|
int byte2 = m_machine->getByte(address + 1);
|
|
int byte3 = m_machine->getByte(address + 2);
|
|
|
|
int ni = (byte1 >> 0) & 0x3;
|
|
int x = (byte2 >> 7) & 0x1;
|
|
int b = (byte2 >> 6) & 0x1;
|
|
int p = (byte2 >> 5) & 0x1;
|
|
int e = (byte2 >> 4) & 0x1;
|
|
|
|
if (e) {
|
|
// Format 4 - add + prefix to mnemonic
|
|
result.mnemonic = "+" + result.mnemonic;
|
|
result.size = 4;
|
|
if (address + 3 < 1048576) {
|
|
int byte4 = m_machine->getByte(address + 3);
|
|
int addr = ((byte2 & 0xF) << 16) | (byte3 << 8) | byte4;
|
|
|
|
result.isImmediate = (ni == 0x1);
|
|
result.isIndirect = (ni == 0x2);
|
|
|
|
if (!result.isImmediate) {
|
|
result.effectiveAddr = addr;
|
|
}
|
|
|
|
QString prefix = "";
|
|
if (ni == 0x1) prefix = "#"; // Immediate
|
|
else if (ni == 0x2) prefix = "@"; // Indirect
|
|
|
|
result.operand = QString("%1%2").arg(prefix).arg(addr, 5, 16, QChar('0')).toUpper();
|
|
if (x) result.operand += ",X";
|
|
}
|
|
} else {
|
|
result.size = 3;
|
|
int disp = ((byte2 & 0xF) << 8) | byte3;
|
|
|
|
if (disp & 0x800) {
|
|
disp |= 0xFFFFF000;
|
|
}
|
|
|
|
result.isImmediate = (ni == 0x1);
|
|
result.isIndirect = (ni == 0x2);
|
|
|
|
QString prefix = "";
|
|
if (ni == 0x1) prefix = "#"; // Immediate
|
|
else if (ni == 0x2) prefix = "@"; // Indirect
|
|
|
|
if (ni == 0x1 && !p && !b) {
|
|
result.operand = QString("#%1").arg(disp & 0xFFF);
|
|
} else {
|
|
// Calculate effective address for display
|
|
int ea = disp;
|
|
QString addrMode = "";
|
|
|
|
if (p) {
|
|
ea += m_machine->getPC();
|
|
addrMode = " (PC)";
|
|
} else if (b) {
|
|
ea += m_machine->getB();
|
|
addrMode = " (B)";
|
|
}
|
|
|
|
if (!result.isImmediate && !x) {
|
|
result.effectiveAddr = ea & 0xFFFFF;
|
|
}
|
|
|
|
result.operand = QString("%1%2%3").arg(prefix).arg(ea & 0xFFFFF, 4, 16, QChar('0')).toUpper().arg(addrMode);
|
|
if (x) result.operand += ",X";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void MainWindow::updateDisassemblyDisplay()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
QWidget* container = new QWidget();
|
|
QVBoxLayout* layout = new QVBoxLayout(container);
|
|
layout->setSpacing(1);
|
|
layout->setContentsMargins(5, 5, 5, 5);
|
|
|
|
QFont monoFont("Courier New");
|
|
monoFont.setPointSize(9);
|
|
|
|
// Header
|
|
QString headerText = QString("Address Mnemonic Operand *var **var");
|
|
QLabel* header = new QLabel(headerText);
|
|
QFont headerFont = monoFont;
|
|
headerFont.setBold(true);
|
|
header->setFont(headerFont);
|
|
layout->addWidget(header);
|
|
|
|
int pc = m_machine->getPC();
|
|
int currentAddr = m_disassemblyOffset;
|
|
|
|
// Disassemble up to 255 instructions
|
|
for (int i = 0; i < 255 && currentAddr < 1048576; i++) {
|
|
auto instr = disassembleAt(currentAddr);
|
|
|
|
QString varCol = "";
|
|
QString varVar = "";
|
|
|
|
// *var column - show value at effective address (if not immediate)
|
|
if (instr.effectiveAddr >= 0 && instr.effectiveAddr < 1048576) {
|
|
int value = m_machine->getWord(instr.effectiveAddr);
|
|
varCol = QString("0x%1").arg(value & 0xFFFFFF, 6, 16, QChar('0')).toUpper();
|
|
|
|
// **var column - if indirect (@), dereference again
|
|
if (instr.isIndirect && value >= 0 && value < 1048576) {
|
|
int derefValue = m_machine->getWord(value);
|
|
varVar = QString("0x%1").arg(derefValue & 0xFFFFFF, 6, 16, QChar('0')).toUpper();
|
|
}
|
|
}
|
|
|
|
QString line = QString("0x%1 %2 %3 %4 %5")
|
|
.arg(instr.address, 5, 16, QChar('0')).toUpper()
|
|
.arg(instr.mnemonic, -9)
|
|
.arg(instr.operand, -14)
|
|
.arg(varCol, -9)
|
|
.arg(varVar, -9);
|
|
|
|
QLabel* instrLine = new QLabel(line);
|
|
instrLine->setFont(monoFont);
|
|
|
|
// Highlight current PC
|
|
if (pc == instr.address) {
|
|
instrLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;");
|
|
}
|
|
|
|
layout->addWidget(instrLine);
|
|
currentAddr += instr.size;
|
|
}
|
|
|
|
layout->addStretch();
|
|
container->setLayout(layout);
|
|
|
|
ui->DisasemblyScrollArea->setWidget(container);
|
|
}
|
|
|
|
void MainWindow::updateMemoryDisplay()
|
|
{
|
|
if (!m_machine) return;
|
|
|
|
// Create a widget to hold the memory display
|
|
QWidget* container = new QWidget();
|
|
QVBoxLayout* layout = new QVBoxLayout(container);
|
|
layout->setSpacing(1);
|
|
layout->setContentsMargins(5, 5, 5, 5);
|
|
|
|
// Create monospace font for memory display
|
|
QFont monoFont("Courier New");
|
|
monoFont.setPointSize(9);
|
|
|
|
// Header with current offset range
|
|
QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]")
|
|
.arg(m_memoryOffset, 5, 16, QChar('0')).toUpper()
|
|
.arg(m_memoryOffset + 255, 5, 16, QChar('0')).toUpper();
|
|
QLabel* header = new QLabel(headerText);
|
|
QFont headerFont = monoFont;
|
|
headerFont.setBold(true);
|
|
header->setFont(headerFont);
|
|
layout->addWidget(header);
|
|
|
|
// Get PC for highlighting
|
|
int pc = m_machine->getPC();
|
|
|
|
// Display memory byte by byte - ONLY 256 bytes from current offset
|
|
for (int i = 0; i < 256; i++) {
|
|
int addr = m_memoryOffset + i;
|
|
int byte = m_machine->getByte(addr);
|
|
|
|
QString addressStr = QString("0x%1").arg(addr, 5, 16, QChar('0')).toUpper();
|
|
|
|
// Hex value column
|
|
QString hexStr = QString("0x%1").arg(byte, 2, 16, QChar('0')).toUpper();
|
|
|
|
// Char representation
|
|
QString charStr;
|
|
if (byte >= 32 && byte <= 126) {
|
|
charStr = QChar(byte);
|
|
} else {
|
|
charStr = '.';
|
|
}
|
|
|
|
QString line = QString("%1 %2 %3")
|
|
.arg(addressStr, -12)
|
|
.arg(hexStr, -12)
|
|
.arg(charStr);
|
|
|
|
QLabel* memLine = new QLabel(line);
|
|
memLine->setFont(monoFont);
|
|
|
|
// Highlight the current PC address
|
|
if (pc == addr) {
|
|
memLine->setStyleSheet("background-color: #FFFF99; font-weight: bold;");
|
|
}
|
|
|
|
layout->addWidget(memLine);
|
|
}
|
|
|
|
layout->addStretch();
|
|
container->setLayout(layout);
|
|
|
|
ui->MemoryScrollArea->setWidget(container);
|
|
}
|