1
0
mirror of https://github.com/nzeemin/ukncbtl-qt.git synced 2025-04-19 17:02:15 +03:00
ukncbtl-qt/emulator/qscripting.cpp
2023-08-26 18:15:53 +03:00

430 lines
12 KiB
C++

#include "stdafx.h"
#include <QApplication>
#include <QCloseEvent>
#include "main.h"
#include "mainwindow.h"
#include "Emulator.h"
#include "emubase/Emubase.h"
#include "emubase/Processor.h"
#include "qscripting.h"
//////////////////////////////////////////////////////////////////////
// QEmulator
QEmulator::QEmulator(QScriptWindow * window) :
m_window(window),
m_cpu(window->getEngine(), g_pBoard->GetCPU()),
m_ppu(window->getEngine(), g_pBoard->GetPPU())
{
}
void QEmulator::reset()
{
Emulator_Reset();
Global_getMainWindow()->updateAllViews();
}
bool QEmulator::run(int frames)
{
bool result = true;
for (int i = 0; i < frames; i++)
{
int res = Emulator_SystemFrame();
if (!res)
{
result = false;
break;
}
if (i % 25 == 24) // Update the screen every 25 frames
{
Global_getMainWindow()->updateAllViews();
Global_getApplication()->processEvents();
if (m_window->isAborted())
return false;
}
}
Global_getMainWindow()->updateAllViews();
Global_getApplication()->processEvents();
if (m_window->isAborted())
return false;
return result;
}
float QEmulator::getUptime()
{
return Emulator_GetUptime();
}
bool QEmulator::addCPUBreakpoint(quint16 address)
{
return Emulator_AddCPUBreakpoint((quint16)address);
}
bool QEmulator::addPPUBreakpoint(quint16 address)
{
return Emulator_AddPPUBreakpoint((quint16)address);
}
bool QEmulator::removeCPUBreakpoint(quint16 address)
{
return Emulator_RemoveCPUBreakpoint((quint16)address);
}
bool QEmulator::removePPUBreakpoint(quint16 address)
{
return Emulator_RemovePPUBreakpoint((quint16)address);
}
bool QEmulator::isBreakpoint()
{
return Emulator_IsBreakpoint();
}
QScriptValue QEmulator::getCPUBreakpoints()
{
const quint16* bps = Emulator_GetCPUBreakpointList();
int count = 0;
for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
{
if (bps[i] == 0177777)
break;
count++;
}
QScriptEngine* engine = m_window->getEngine();
QScriptValue list = engine->newArray(count);
for (int i = 0; i < count; i++)
{
list.setProperty(i, engine->newVariant(bps[i]));
}
return list;
}
QScriptValue QEmulator::getPPUBreakpoints()
{
const quint16* bps = Emulator_GetPPUBreakpointList();
int count = 0;
for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
{
if (bps[i] == 0177777)
break;
count++;
}
QScriptEngine* engine = m_window->getEngine();
QScriptValue list = engine->newArray(count);
for (int i = 0; i < count; i++)
{
list.setProperty(i, engine->newVariant(bps[i]));
}
return list;
}
void QEmulator::saveScreenshot(const QString &filename)
{
Global_getMainWindow()->saveScreenshot(filename);
}
bool QEmulator::attachCartridge(int slot, const QString & filename)
{
if (slot < 1 || slot > 2) return false;
return Global_getMainWindow()->attachCartridge(slot, filename);
}
void QEmulator::detachCartridge(int slot)
{
if (slot < 1 || slot > 2) return;
Global_getMainWindow()->detachCartridge(slot);
}
bool QEmulator::attachFloppy(int slot, const QString & filename)
{
if (slot < 0 || slot > 3) return false;
return Global_getMainWindow()->attachFloppy(slot, filename);
}
void QEmulator::detachFloppy(int slot)
{
if (slot < 0 || slot > 3) return;
Global_getMainWindow()->detachFloppy(slot);
}
bool QEmulator::attachHard(int slot, const QString &filename)
{
if (slot < 1 || slot > 2) return false;
return Global_getMainWindow()->attachHardDrive(slot, filename);
}
void QEmulator::detachHard(int slot)
{
if (slot < 1 || slot > 2) return;
Global_getMainWindow()->detachHardDrive(slot);
}
void QEmulator::keyScan(uchar ukncscan, int timeout)
{
g_pBoard->KeyboardEvent(ukncscan, true);
run(timeout);
g_pBoard->KeyboardEvent(ukncscan, false);
run(3);
}
void QEmulator::keyScanShift(uchar ukncscan, int timeout)
{
g_pBoard->KeyboardEvent(0105, true);
run(2);
g_pBoard->KeyboardEvent(ukncscan, true);
run(timeout);
g_pBoard->KeyboardEvent(ukncscan, false);
run(2);
g_pBoard->KeyboardEvent(0105, false);
run(3);
}
const uchar arrChar2UkncScan[128] =
{
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/*0*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0153, 0000, 0000, 0153, 0000, 0000,
/*1*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*2*/ 0113, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0117, 0175, 0146, 0173,
/*3*/ 0176, 0030, 0031, 0032, 0013, 0034, 0035, 0016, 0017, 0177, 0174, 0007, 0000, 0000, 0000, 0000,
/*4*/ 0077, 0072, 0076, 0050, 0057, 0033, 0047, 0055, 0156, 0073, 0027, 0052, 0056, 0112, 0054, 0075,
/*5*/ 0053, 0067, 0074, 0111, 0114, 0051, 0137, 0071, 0115, 0070, 0157, 0036, 0136, 0037, 0110, 0155,
/*6*/ 0126, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*7*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0155, 0000, 0000, 0000, 0000,
};
const uchar arrChar2UkncScanShift[128] =
{
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/*0*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*1*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*2*/ 0000, 0030, 0031, 0032, 0013, 0034, 0035, 0016, 0017, 0177, 0174, 0007, 0000, 0000, 0000, 0000,
/*3*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0117, 0175, 0135, 0173,
/*4*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*5*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
/*6*/ 0077, 0072, 0076, 0050, 0057, 0033, 0047, 0055, 0156, 0073, 0027, 0052, 0056, 0112, 0054, 0075,
/*7*/ 0053, 0067, 0074, 0111, 0114, 0051, 0137, 0071, 0115, 0070, 0157, 0036, 0136, 0037, 0110, 0000,
};
void QEmulator::keyChar(char ch, int timeout)
{
if (ch < 0) return; // ASCII only
uchar scan = arrChar2UkncScanShift[(uchar)ch];
if (scan != 0)
{
keyScanShift(scan, timeout);
return;
}
scan = arrChar2UkncScan[(uchar)ch];
if (scan == 0)
return;
keyScan(scan, timeout);
}
void QEmulator::keyString(QString str)
{
for (int i = 0; i < str.length(); i++)
{
char ch = str[i].toLatin1();
keyChar(ch);
if (m_window->isAborted())
return;
}
}
void QEmulator::saveState(const QString &filename)
{
Global_getMainWindow()->saveStateImage(filename);
}
void QEmulator::loadState(const QString &filename)
{
Global_getMainWindow()->loadStateImage(filename);
}
void QEmulator::consolePrint(const QString &message)
{
Global_getMainWindow()->consolePrint(message);
}
//////////////////////////////////////////////////////////////////////
// QEmulatorProcessor
QEmulatorProcessor::QEmulatorProcessor(QScriptEngine *engine, CProcessor *processor)
: m_engine(engine), m_processor(processor)
{
}
QString QEmulatorProcessor::getName()
{
return QString(m_processor->GetName());
}
ushort QEmulatorProcessor::getReg(int regno)
{
if (regno < 0 || regno > 7) return 0;
return m_processor->GetReg(regno);
}
ushort QEmulatorProcessor::getPSW()
{
return m_processor->GetPSW();
}
bool QEmulatorProcessor::isHalt()
{
return m_processor->IsHaltMode();
}
void QEmulatorProcessor::setReg(int regno, ushort value)
{
if (regno < 0 || regno > 7) return;
m_processor->SetReg(regno, value);
Global_getMainWindow()->updateAllViews();
}
void QEmulatorProcessor::setPSW(ushort value)
{
m_processor->SetPSW(value);
Global_getMainWindow()->updateAllViews();
}
ushort QEmulatorProcessor::readWord(ushort addr)
{
int addrtype;
return m_processor->GetMemoryController()->GetWordView(addr, m_processor->IsHaltMode(), false, &addrtype);
}
uchar QEmulatorProcessor::readByte(ushort addr)
{
int addrtype;
ushort word = m_processor->GetMemoryController()->GetWordView(addr, m_processor->IsHaltMode(), false, &addrtype);
if ((addrtype == ADDRTYPE_IO) || (addrtype == ADDRTYPE_DENY))
return 0;
return (addr & 1) ? word & 0xff : (word >> 8) & 0xff;
}
QScriptValue QEmulatorProcessor::disassemble(ushort addr)
{
CMemoryController* pMemCtl = m_processor->GetMemoryController();
quint16 buffer[4];
quint16 current = addr;
for (int i = 0; i < 4; i++)
{
int addrtype;
buffer[i] = pMemCtl->GetWordView(current, m_processor->IsHaltMode(), false, &addrtype);
current += 2;
}
char instr[8], args[32];
int instrlen = DisassembleInstruction(buffer, addr, instr, args);
QScriptValue list = m_engine->newArray(4);
list.setProperty(0, m_engine->newVariant(addr));
list.setProperty(1, m_engine->newVariant(instr));
list.setProperty(2, m_engine->newVariant(args));
list.setProperty(3, m_engine->newVariant(instrlen));
return list;
}
//////////////////////////////////////////////////////////////////////
// QScriptWindow
QScriptWindow::QScriptWindow(QWidget * parent)
: QDialog(parent, Qt::Dialog),
m_aborted(false)
{
setWindowTitle(tr("Script Running"));
setMinimumSize(300, 125);
setMaximumSize(400, 200);
m_cancelButton.setText(tr("Stop"));
m_vlayout.addWidget(&m_static);
m_vlayout.addWidget(&m_cancelButton, 0, Qt::AlignHCenter);
setLayout(&m_vlayout);
setModal(true);
QObject::connect(&m_cancelButton, SIGNAL(clicked()), this, SLOT(cancelButtonPressed()));
m_emulator = new QEmulator(this);
m_engine.globalObject().setProperty("emulator", m_engine.newQObject(m_emulator));
m_engine.globalObject().setProperty("emu", m_engine.newQObject(m_emulator));
}
QScriptWindow::~QScriptWindow()
{
delete m_emulator;
}
void QScriptWindow::runScript(const QString & script)
{
show();
raise();
activateWindow();
QString message;
for (;;)
{
m_static.setText(tr("Syntax check..."));
Global_getApplication()->processEvents();
QScriptSyntaxCheckResult checkResult = QScriptEngine::checkSyntax(script);
if (checkResult.state() != QScriptSyntaxCheckResult::Valid)
{
message = tr("Syntax check FAILED:\n\n%1\n\nat line %2 column %3.")
.arg(checkResult.errorMessage())
.arg(checkResult.errorLineNumber())
.arg(checkResult.errorColumnNumber());
break;
}
m_static.setText(tr("Running script..."));
Global_getApplication()->processEvents();
m_engine.setProcessEventsInterval(250);
QScriptValue result = m_engine.evaluate(script);
if (m_aborted)
{
message.append(tr("The script was STOPPED."));
break;
}
message.append(tr("The script FINISHED. The result is:\n\n"))
.append(result.toString());
break;
}
m_static.setText(message);
m_cancelButton.setText(tr("Close"));
exec();
}
void QScriptWindow::cancelButtonPressed()
{
if (m_engine.isEvaluating())
{
m_aborted = true;
m_engine.abortEvaluation();
}
else // The script is not running
{
QDialog::reject(); // Close the window
}
}
void QScriptWindow::reject()
{
if (m_engine.isEvaluating())
{
m_aborted = true;
m_engine.abortEvaluation();
}
else // The script is not running
{
QDialog::reject();
}
}
//////////////////////////////////////////////////////////////////////