Compare commits

...

2 commits

Author SHA1 Message Date
zanostro
098766bb65 fixed bugs 2025-12-06 22:55:01 +01:00
zanostro
d3ab78e76c ui changes 2025-12-05 19:00:11 +01:00
9 changed files with 195 additions and 32 deletions

View file

@ -42,8 +42,13 @@ void MachineController::runLoop() {
try { try {
if (m_machine) { if (m_machine) {
m_machine->execute(); m_machine->execute();
m_machine->tick(); m_machine->tick();
emit tick(); emit tick();
if (m_machine->isStopped()) {
m_running.store(false);
break;
}
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
emit error(QString::fromStdString(e.what())); emit error(QString::fromStdString(e.what()));

View file

@ -17,6 +17,9 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QLabel> #include <QLabel>
#include <QFont> #include <QFont>
#include <QFileDialog>
#include <QMessageBox>
#include <QScrollBar>
class Loader; class Loader;
@ -69,8 +72,8 @@ MainWindow::MainWindow(QWidget *parent) :
ui->regF_bin_field->setValidator(floatBinValidator); ui->regF_bin_field->setValidator(floatBinValidator);
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays); connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateRegisterDisplays, Qt::QueuedConnection);
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay); connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateMemoryDisplay, Qt::QueuedConnection);
connectRegisterFields(); connectRegisterFields();
@ -95,15 +98,16 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->DisasmDec65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec256); connect(ui->DisasmDec65536Btn, &QPushButton::clicked, this, &MainWindow::onDisassemblyDec256);
connect(ui->DisasmGoToStart, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToStart); connect(ui->DisasmGoToStart, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToStart);
connect(ui->DisasmGoToEnd, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToEnd); connect(ui->DisasmGoToEnd, &QPushButton::clicked, this, &MainWindow::onDisassemblyGoToEnd);
connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateDisassemblyDisplay); connect(m_controller.get(), &MachineController::tick, this, &MainWindow::updateDisassemblyDisplay, Qt::QueuedConnection);
// Connect menu actions
connect(ui->actionLoad_Object_File, &QAction::triggered, this, &MainWindow::loadObjectFile);
setupMemoryDisplay(); setupMemoryDisplay();
setupDisassemblyDisplay(); setupDisassemblyDisplay();
loadInstructionSet(); loadInstructionSet();
//loadDemoProgram(); // Don't load any program by default - user will load via File menu
Loader loader(m_machine, std::string(PATH_RESOURCES) + "print.obj");
loader.load();
updateRegisterDisplays(); updateRegisterDisplays();
updateMemoryDisplay(); updateMemoryDisplay();
@ -695,7 +699,18 @@ MainWindow::DisassembledInstruction MainWindow::disassembleAt(int address)
QString reg1Str = (r1 < 10) ? regNames[r1] : "?"; QString reg1Str = (r1 < 10) ? regNames[r1] : "?";
QString reg2Str = (r2 < 10) ? regNames[r2] : "?"; QString reg2Str = (r2 < 10) ? regNames[r2] : "?";
result.operand = QString("%1, %2").arg(reg1Str).arg(reg2Str); // Check if this is a single-operand Format 2 instruction
QString mnem = result.mnemonic.toUpper();
if (mnem == "CLEAR" || mnem == "TIXR") {
result.operand = reg1Str;
} else if (mnem == "SVC") {
result.operand = QString::number(r1);
} else if (mnem == "SHIFTL" || mnem == "SHIFTR") {
result.operand = QString("%1, %2").arg(reg1Str).arg(r2);
} else {
// Two register operands (ADDR, SUBR, COMPR, etc.)
result.operand = QString("%1, %2").arg(reg1Str).arg(reg2Str);
}
} }
break; break;
} }
@ -760,7 +775,7 @@ MainWindow::DisassembledInstruction MainWindow::disassembleAt(int address)
QString addrMode = ""; QString addrMode = "";
if (p) { if (p) {
ea += m_machine->getPC(); ea += (address + 3);
addrMode = " (PC)"; addrMode = " (PC)";
} else if (b) { } else if (b) {
ea += m_machine->getB(); ea += m_machine->getB();
@ -849,7 +864,13 @@ void MainWindow::updateDisassemblyDisplay()
layout->addStretch(); layout->addStretch();
container->setLayout(layout); container->setLayout(layout);
// Save scroll position before updating
int scrollPos = ui->DisasemblyScrollArea->verticalScrollBar()->value();
ui->DisasemblyScrollArea->setWidget(container); ui->DisasemblyScrollArea->setWidget(container);
// Restore scroll position after updating
ui->DisasemblyScrollArea->verticalScrollBar()->setValue(scrollPos);
} }
void MainWindow::updateMemoryDisplay() void MainWindow::updateMemoryDisplay()
@ -916,5 +937,44 @@ void MainWindow::updateMemoryDisplay()
layout->addStretch(); layout->addStretch();
container->setLayout(layout); container->setLayout(layout);
int scrollPos = ui->MemoryScrollArea->verticalScrollBar()->value();
ui->MemoryScrollArea->setWidget(container); ui->MemoryScrollArea->setWidget(container);
ui->MemoryScrollArea->verticalScrollBar()->setValue(scrollPos);
}
void MainWindow::loadObjectFile()
{
QString fileName = QFileDialog::getOpenFileName(this,
tr("Load Object File"),
QString(),
tr("Object Files (*.obj);;All Files (*)"));
if (fileName.isEmpty()) {
return;
}
try {
// Stop execution if running
m_controller->stop();
// Reset machine state
m_machine->reset();
// Load the object file
Loader loader(m_machine, fileName.toStdString());
loader.load();
// Update displays
updateRegisterDisplays();
updateMemoryDisplay();
updateDisassemblyDisplay();
QMessageBox::information(this, tr("Success"),
tr("Object file loaded successfully"));
} catch (const std::exception &e) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to load object file: %1").arg(e.what()));
}
} }

View file

@ -50,6 +50,7 @@ private slots:
void onDisassemblyDec256(); void onDisassemblyDec256();
void onDisassemblyGoToStart(); void onDisassemblyGoToStart();
void onDisassemblyGoToEnd(); void onDisassemblyGoToEnd();
void loadObjectFile();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;

View file

@ -866,6 +866,18 @@
</widget> </widget>
</widget> </widget>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
<property name="geometry"> <property name="geometry">
<rect> <rect>
@ -875,8 +887,43 @@
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLoad_Object_File"/>
</widget>
<widget class="QMenu" name="menuMachine">
<property name="title">
<string>Machine</string>
</property>
<addaction name="actionFrequency"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuMachine"/>
<addaction name="menuHelp"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <action name="actionLoad_Object_File">
<property name="text">
<string>Load Object File</string>
</property>
</action>
<action name="actionFrequency">
<property name="text">
<string>Frequency</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View file

@ -17,6 +17,7 @@ private:
std::fstream fileStream; std::fstream fileStream;
std::string filename; std::string filename;
bool fileCreated; bool fileCreated;
std::streampos readPosition;
}; };
#endif // FILE_DEVICE_H #endif // FILE_DEVICE_H

View file

@ -26,7 +26,7 @@ using std::cout;
class Machine { class Machine {
public: public:
Machine(); Machine();
Machine(int speedkHz) : Machine() { this->speedkHz = speedkHz; _instructionsTable = instructions; } Machine(int speedHz) : Machine() { this->speedHz = speedHz; _instructionsTable = instructions; }
~Machine(); ~Machine();
int getA() const { return A; } int getA() const { return A; }
@ -84,11 +84,13 @@ public:
// Execution and speed control // Execution and speed control
int getSpeed() const; int getSpeed() const;
void setSpeed(int kHz); void setSpeed(int Hz);
void start(); void start();
void stop(); void stop();
void tick(); void tick();
void halt(); void halt();
bool isStopped() const { return _stopped; }
void reset();
// error handling methods // error handling methods
void notImplemented(string mnemonic); void notImplemented(string mnemonic);
@ -128,7 +130,7 @@ private:
// Execution control // Execution control
std::atomic<bool> running{false}; std::atomic<bool> running{false};
std::atomic<int> speedkHz{1}; // Default 1 kHz std::atomic<int> speedHz{10}; // Default 10 Hz
bool execF1(int opcode); bool execF1(int opcode);
bool execF2(int opcode, int operand); bool execF2(int opcode, int operand);

View file

@ -3,9 +3,8 @@
#include <fstream> #include <fstream>
FileDevice::FileDevice(const std::string &filename) FileDevice::FileDevice(const std::string &filename)
: filename(filename), fileCreated(false) : filename(filename), fileCreated(false), readPosition(0)
{ {
// Don't create the file yet - wait until first write
} }
FileDevice::~FileDevice() FileDevice::~FileDevice()
@ -18,9 +17,16 @@ FileDevice::~FileDevice()
void FileDevice::ensureFileOpen() void FileDevice::ensureFileOpen()
{ {
if (!fileStream.is_open()) { if (!fileStream.is_open()) {
if (fileCreated) { // Check if file exists
fileStream.open(filename, std::ios::in | std::ios::out); std::ifstream checkFile(filename);
bool fileExists = checkFile.good();
checkFile.close();
if (fileExists) {
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::ate);
fileCreated = true;
} else { } else {
// Create new file
std::ofstream create(filename); std::ofstream create(filename);
if (!create) { if (!create) {
throw std::runtime_error("Failed to create file: " + filename); throw std::runtime_error("Failed to create file: " + filename);
@ -41,9 +47,11 @@ unsigned char FileDevice::read()
unsigned char value = 0; unsigned char value = 0;
ensureFileOpen(); ensureFileOpen();
if (fileStream.is_open()) { if (fileStream.is_open()) {
fileStream.seekg(readPosition);
char ch; char ch;
if (fileStream.get(ch)) { if (fileStream.get(ch)) {
value = static_cast<unsigned char>(ch); value = static_cast<unsigned char>(ch);
readPosition = fileStream.tellg();
} }
} }
return value; return value;
@ -53,6 +61,7 @@ void FileDevice::write(unsigned char value)
{ {
ensureFileOpen(); ensureFileOpen();
if (fileStream.is_open()) { if (fileStream.is_open()) {
fileStream.seekp(0, std::ios::end);
fileStream.put(static_cast<char>(value)); fileStream.put(static_cast<char>(value));
fileStream.flush(); fileStream.flush();
} }

View file

@ -1,8 +1,10 @@
#include "loader.h" #include "loader.h"
#include "file_reader.h" #include "file_reader.h"
#include "string_reader.h"
#include "machine.h" #include "machine.h"
#include "constants.h" #include "constants.h"
#include <algorithm> #include <algorithm>
#include <cstdio>
Loader::~Loader() Loader::~Loader()
{ {
@ -82,20 +84,24 @@ Loader::TextRecord Loader::readTextRecord()
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
// Read length (1 byte, 2 hex digits) // Read length (1 byte, 2 hex digits)
int length = std::stoi(_file_reader->readString(2), nullptr, 16); std::string lengthStr = _file_reader->readString(2);
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte(); int length = std::stoi(lengthStr, nullptr, 16);
// Read the rest of the line (data bytes with spaces)
std::string dataLine = _file_reader->readLine();
// Remove all whitespace from the data line
dataLine.erase(std::remove_if(dataLine.begin(), dataLine.end(), ::isspace), dataLine.end());
// Now use StringReader to parse the hex bytes
StringReader stringReader(dataLine);
record.data.resize(length); record.data.resize(length);
int index = 0;
string byteStr = _file_reader->readLine();
// Remove spaces, newlines, and other whitespace characters
byteStr.erase(std::remove_if(byteStr.begin(), byteStr.end(), ::isspace), byteStr.end());
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
std::string byteHex = byteStr.substr(i * 2, 2); std::string byteHex = stringReader.readString(2);
record.data[i] = static_cast<uint8_t>(std::stoi(byteHex, nullptr, 16)); record.data[i] = static_cast<uint8_t>(std::stoi(byteHex, nullptr, 16));
} }
return record; return record;
} }

View file

@ -14,6 +14,17 @@ string prefix = "Machine error: ";
Machine::Machine() Machine::Machine()
{ {
// Initialize registers and memory to zero
A = B = X = L = S = T = PC = SW = 0;
F = 0.0;
for (int i = 0; i < MEMORY_SIZE; i++) {
memory[i] = 0;
}
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
VA[i] = VS[i] = VT[i] = 0;
}
_stopped = false;
devices.resize(NUM_DEVICES); devices.resize(NUM_DEVICES);
// device 0: standard input // device 0: standard input
devices[0] = make_shared<InputDevice>(std::cin); devices[0] = make_shared<InputDevice>(std::cin);
@ -45,12 +56,12 @@ Machine::~Machine()
int Machine::getSpeed() const int Machine::getSpeed() const
{ {
return speedkHz.load(); return speedHz.load();
} }
void Machine::setSpeed(int kHz) void Machine::setSpeed(int Hz)
{ {
speedkHz.store(kHz); speedHz.store(Hz);
} }
// TODO: implement errors // TODO: implement errors
@ -138,10 +149,10 @@ void Machine::setVT(const int *values)
void Machine::tick() void Machine::tick()
{ {
const int speed = speedkHz.load(); const int speed = speedHz.load();
if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick"); if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick");
const auto delay = std::chrono::microseconds(1000 / speed); const auto delay = std::chrono::milliseconds(1000 / speed);
std::this_thread::sleep_for(delay); std::this_thread::sleep_for(delay);
} }
@ -150,6 +161,27 @@ void Machine::halt()
_stopped = true; _stopped = true;
} }
void Machine::reset()
{
// Reset all registers
A = B = X = L = S = T = PC = SW = 0;
F = 0.0;
// Clear memory
for (int i = 0; i < MEMORY_SIZE; i++) {
memory[i] = 0;
}
// Reset execution state
_stopped = false;
running.store(false);
// Reset vector registers
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
VA[i] = VS[i] = VT[i] = 0;
}
}
int Machine::getReg(int regNum) const int Machine::getReg(int regNum) const
{ {
switch (regNum) { switch (regNum) {