#include "mainwindow.h" #include "ui_mainwindow.h" #include "MachineController.h" #include "../../include/machine.h" #include "../../include/instructions.h" #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_machine(std::make_shared()), m_controller(std::make_unique(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); setupMemoryDisplay(); loadDemoProgram(); updateRegisterDisplays(); updateMemoryDisplay(); } 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(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(regPrefix + "_dec_field"); if (decField && !decField->hasFocus()) { decField->setText(QString::number(value)); } // Update hex field QLineEdit* hexField = findChild(regPrefix + "_hex_field"); if (hexField && !hexField->hasFocus()) { // Convert to 24-bit representation, handle negative numbers unsigned int unsignedValue = static_cast(value) & 0xFFFFFF; hexField->setText(QString("0x%1").arg(unsignedValue, 6, 16, QChar('0')).toUpper()); } // Update binary field QLineEdit* binField = findChild(regPrefix + "_bin_field"); if (binField && !binField->hasFocus()) { // Convert to 24-bit binary representation unsigned int unsignedValue = static_cast(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(regPrefix + "_dec_field"); if (decField && !decField->hasFocus()) { decField->setText(QString::number(value, 'g', 10)); } // Update hex field (48-bit float representation) QLineEdit* hexField = findChild(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(&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(regPrefix + "_bin_field"); if (binField && !binField->hasFocus()) { // Convert double to 48-bit binary representation uint64_t* intPtr = reinterpret_cast(&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(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(&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(&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: Accumulator Loop"; const int TEMP_ADDR = 0x50; const int LOOP_ADDR = 0x03; // clear TEMP m_machine->setByte(TEMP_ADDR, 0); // Program (addresses): // 0x00 LDA #1 // 0x03 LDB TEMP // 0x06 ADDR A,B // 0x08 RMO B,A // 0x0A STA TEMP // 0x0D J LOOP // LDA #1 m_machine->setByte(0x00, 0x01); m_machine->setByte(0x01, 0x00); m_machine->setByte(0x02, 0x01); // LDB TEMP m_machine->setByte(0x03, 0x6B); m_machine->setByte(0x04, 0x00); m_machine->setByte(0x05, TEMP_ADDR); // ADDR A,B m_machine->setByte(0x06, 0x90); m_machine->setByte(0x07, 0x03); // RMO B,A m_machine->setByte(0x08, 0xAC); m_machine->setByte(0x09, 0x30); // STA TEMP m_machine->setByte(0x0A, 0x0F); m_machine->setByte(0x0B, 0x00); m_machine->setByte(0x0C, TEMP_ADDR); // J LOOP m_machine->setByte(0x0D, 0x3F); m_machine->setByte(0x0E, 0x00); m_machine->setByte(0x0F, LOOP_ADDR); // Set PC to start of program m_machine->setPC(0x00); qDebug() << "Program loaded. TEMP at 0x" << QString::number(TEMP_ADDR, 16).toUpper(); qDebug() << "PC set to 0x00. Ready to execute."; } 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::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); }