added disasembly and better demo program
This commit is contained in:
parent
280a3b62fc
commit
ad3078ba48
3 changed files with 651 additions and 105 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#include "MachineController.h"
|
||||
#include "../../include/machine.h"
|
||||
#include "../../include/instructions.h"
|
||||
#include "../../include/opcode.h"
|
||||
|
||||
#include <QIntValidator>
|
||||
#include <QLineEdit>
|
||||
|
|
@ -77,12 +78,26 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
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();
|
||||
|
||||
updateRegisterDisplays();
|
||||
updateMemoryDisplay();
|
||||
updateDisassemblyDisplay();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
|
@ -375,55 +390,117 @@ void MainWindow::loadDemoProgram()
|
|||
// 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);
|
||||
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. TEMP at 0x" << QString::number(TEMP_ADDR, 16).toUpper();
|
||||
qDebug() << "PC set to 0x00. Ready to execute.";
|
||||
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()
|
||||
|
|
@ -486,6 +563,285 @@ void MainWindow::onMemoryDec65536()
|
|||
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;
|
||||
|
|
@ -501,7 +857,7 @@ void MainWindow::updateMemoryDisplay()
|
|||
monoFont.setPointSize(9);
|
||||
|
||||
// Header with current offset range
|
||||
QString headerText = QString("Address Hex Value Char [0x%1 - 0x%2]")
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue