mirror of
https://github.com/makarcz/vm6502.git
synced 2024-09-28 04:54:32 +00:00
c90ad3e7c0
Small refactoring and documentation update.
1442 lines
46 KiB
C++
1442 lines
46 KiB
C++
/*
|
|
*--------------------------------------------------------------------
|
|
* Project: VM65 - Virtual Machine/CPU emulator programming
|
|
* framework.
|
|
*
|
|
* File: main.cpp
|
|
*
|
|
* Purpose: Define User Interface, Debug Console and main loop
|
|
* of the app.
|
|
*
|
|
* Date: 8/25/2016
|
|
*
|
|
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
|
|
*
|
|
* Contact: makarcz@yahoo.com
|
|
*
|
|
* License Agreement and Warranty:
|
|
|
|
This software is provided with No Warranty.
|
|
I (Marek Karcz) will not be held responsible for any damage to
|
|
computer systems, data or user's health resulting from use.
|
|
Please proceed responsibly and exercise common sense.
|
|
This software is provided in hope that it will be useful.
|
|
It is free of charge for non-commercial and educational use.
|
|
Distribution of this software in non-commercial and educational
|
|
derivative work is permitted under condition that original
|
|
copyright notices and comments are preserved. Some 3-rd party work
|
|
included with this project may require separate application for
|
|
permission from their respective authors/copyright owners.
|
|
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <bitset>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <string.h>
|
|
#include "system.h"
|
|
#include "MKCpu.h"
|
|
#include "Memory.h"
|
|
#include "Display.h"
|
|
#include "VMachine.h"
|
|
#include "GraphDisp.h"
|
|
#include "MemMapDev.h"
|
|
#include "ConsoleIO.h"
|
|
#include "MKGenException.h"
|
|
|
|
using namespace std;
|
|
using namespace MKBasic;
|
|
|
|
#define ANIM_DELAY 250
|
|
#define PROMPT_ADDR "Address (0..FFFF): "
|
|
#define PROMPT_START_ADDR "Start address (0..FFFF): "
|
|
#define PROMPT_RANGE_ADDR "Enter address range (0..0xFFFF).."
|
|
#define PROMPT_END_ADDR "End address (0..FFFF): "
|
|
|
|
const bool ClsIfDirty = true;
|
|
|
|
char diss_buf[DISS_BUF_SIZE]; // last disassembled instruction buffer
|
|
char curr_buf[DISS_BUF_SIZE]; // current disassembled instruction buffer
|
|
|
|
VMachine *pvm = NULL;
|
|
ConsoleIO *pconio = NULL;
|
|
Regs *preg = NULL;
|
|
bool ioecho = false, opbrk = false, needhelp = false;
|
|
bool loadbin = false, loadhex = false, reset = false, execvm = false;
|
|
int g_stackdisp_lines = 1;
|
|
string ramfile = "dummy.ram";
|
|
|
|
bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat);
|
|
void ShowHelp();
|
|
void CmdArgHelp(string prgname);
|
|
void CopyrightBanner();
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: PressEnter2Cont()
|
|
* Purpose: Print a message and wait for ENTER to be pressed.
|
|
* Arguments: msg - string : message
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void PressEnter2Cont(string msg)
|
|
{
|
|
string mesg = msg;
|
|
if (0 == msg.length()) mesg = "Press [ENTER]...";
|
|
cout << mesg;
|
|
fflush(stdin);
|
|
while (true) {
|
|
int c = getchar();
|
|
if ('\n' == c || EOF == c) break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: RunSingleInstr()
|
|
* Purpose: Execute single instruction of the CPU (all cycles).
|
|
* Arguments: addr - unsigned short, instruction address
|
|
* Returns: pointer to CPU registers
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
Regs *RunSingleInstr(unsigned short addr)
|
|
{
|
|
Regs *ret = NULL;
|
|
|
|
pvm->Disassemble(addr, diss_buf);
|
|
// skip # cycles per op-code specs
|
|
do {
|
|
ret = pvm->Step(addr);
|
|
} while (ret->CyclesLeft > 0);
|
|
// and now execute the actual op-code
|
|
ret = pvm->Step(addr);
|
|
pvm->Disassemble(ret->PtrAddr, curr_buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: RunSingleCurrInstr()
|
|
* Purpose: Execute single instruction of the CPU (all cycles)
|
|
* at current address.
|
|
* Arguments: n/a
|
|
* Returns: pointer to CPU registers
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
Regs *RunSingleCurrInstr()
|
|
{
|
|
Regs *ret = NULL;
|
|
|
|
pvm->Disassemble(preg->PtrAddr, diss_buf);
|
|
// skip # cycles per op-code specs
|
|
do {
|
|
ret = pvm->Step();
|
|
} while (ret->CyclesLeft > 0);
|
|
// and now execute the actual op-code
|
|
ret = pvm->Step();
|
|
pvm->Disassemble(ret->PtrAddr, curr_buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: VMErr
|
|
* Purpose: Data structure and macros supporting VM errors
|
|
* messages
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
|
|
#define WARN_UNEXPECTED_EOF "WARNING: Unexpected EOF (image shorter than 64kB)."
|
|
#define WARN_NOHDR_BINIMG "WARNING: No header found in binary image."
|
|
#define WARN_HDRPRBLM_BINIMG "WARNING: Problem with binary image header."
|
|
|
|
struct VMErr {
|
|
|
|
int id;
|
|
char text1[80];
|
|
char text2[80];
|
|
|
|
} g_vmerrtbl[] = {
|
|
|
|
{MEMIMGERR_RAMBIN_EOF, WARN_UNEXPECTED_EOF, ""},
|
|
{MEMIMGERR_RAMBIN_OPEN, "WARNING: Unable to open memory image file.", ""},
|
|
{MEMIMGERR_RAMBIN_HDR, WARN_HDRPRBLM_BINIMG, ""},
|
|
{MEMIMGERR_RAMBIN_NOHDR, WARN_NOHDR_BINIMG, ""},
|
|
{MEMIMGERR_RAMBIN_HDRANDEOF, WARN_HDRPRBLM_BINIMG, WARN_UNEXPECTED_EOF},
|
|
{MEMIMGERR_RAMBIN_NOHDRANDEOF,WARN_NOHDR_BINIMG, WARN_UNEXPECTED_EOF},
|
|
{MEMIMGERR_INTELH_OPEN, "WARNING: Unable to open Intel HEX file.", ""},
|
|
{MEMIMGERR_INTELH_SYNTAX, "ERROR: Syntax error.", ""},
|
|
{MEMIMGERR_INTELH_FMT, "ERROR: Intel HEX format error.", ""},
|
|
{MEMIMGERR_VM65_OPEN, "ERROR: Unable to open memory definition file.", ""},
|
|
{MEMIMGERR_VM65_IGNPROCWRN, "WARNING: There were problems while processing memory definition file.", ""},
|
|
{VMERR_SAVE_SNAPSHOT, "WARNING: There was a problem saving memory snapshot.", ""},
|
|
{-1, "", ""}
|
|
|
|
};
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: PrintVMErr()
|
|
* Purpose: Print the warning/error message.
|
|
* Arguments: err - integer, error code
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void PrintVMErr(int err)
|
|
{
|
|
bool pressenter = false;
|
|
|
|
for (int i=0; g_vmerrtbl[i].id >= 0; i++) {
|
|
if (g_vmerrtbl[i].id == err) {
|
|
pressenter = true;
|
|
if (strlen(g_vmerrtbl[i].text1)) {
|
|
cout << g_vmerrtbl[i].text1 << endl;
|
|
}
|
|
if (strlen(g_vmerrtbl[i].text2)) {
|
|
cout << g_vmerrtbl[i].text2 << endl;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pressenter) {
|
|
PressEnter2Cont("");
|
|
}
|
|
}
|
|
|
|
#if defined(LINUX)
|
|
|
|
#include <signal.h>
|
|
|
|
void trap_signal(int signum);
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: trap_signal()
|
|
* Purpose: handle signal
|
|
* Arguments: signum - signal #
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void trap_signal(int signum)
|
|
{
|
|
if (NULL != pconio) {
|
|
pconio->CloseCursesScr();
|
|
}
|
|
if (NULL != pvm && NULL != preg) {
|
|
pvm->SetOpInterrupt(true);
|
|
opbrk = true;
|
|
}
|
|
cout << "Signal caught: " << dec << signum << endl;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: reset_terminal_mode()
|
|
* Purpose: Close curses window.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void reset_terminal_mode()
|
|
{
|
|
if (NULL != pconio) pconio->CloseCursesScr();
|
|
cout << "Thank you for using VM65." << endl;
|
|
}
|
|
|
|
|
|
#endif // #if defined(LINUX)
|
|
|
|
#if defined(WINDOWS)
|
|
#include <windows.h>
|
|
|
|
BOOL CtrlHandler(DWORD fdwCtrlType);
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: CtrlHandler()
|
|
* Purpose: handle signal
|
|
* Arguments: fdwCtrlType - event type
|
|
* Returns: BOOL - TRUE if event handled, FALSE if needs further
|
|
* processing.
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
BOOL CtrlHandler(DWORD fdwCtrlType)
|
|
{
|
|
switch( fdwCtrlType )
|
|
{
|
|
case CTRL_C_EVENT:
|
|
|
|
if (NULL != pvm && NULL != preg) {
|
|
pvm->SetOpInterrupt(true);
|
|
opbrk = true;
|
|
}
|
|
return TRUE;
|
|
|
|
|
|
case CTRL_CLOSE_EVENT:
|
|
|
|
cout << "Ctrl-Close event" << endl;
|
|
return TRUE ;
|
|
|
|
case CTRL_BREAK_EVENT:
|
|
|
|
if (NULL != pvm && NULL != preg) {
|
|
pvm->SetOpInterrupt(true);
|
|
opbrk = true;
|
|
}
|
|
return TRUE;
|
|
|
|
case CTRL_LOGOFF_EVENT:
|
|
|
|
cout << "Ctrl-Logoff event" << endl;
|
|
return FALSE;
|
|
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
Beep( 750, 500 );
|
|
cout << "Ctrl-Shutdown event" << endl;
|
|
return FALSE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: PromptNewAddress()
|
|
* Purpose: Prompt user to enter 16-bit address (hex) in console.
|
|
* Arguments: prompt - prompt text
|
|
* Returns: unsigned int - address entered by user
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned int PromptNewAddress(string prompt)
|
|
{
|
|
unsigned int newaddr = 0x10000;
|
|
|
|
while (newaddr > 0xFFFF) {
|
|
cout << prompt;
|
|
cin >> hex >> newaddr;
|
|
}
|
|
|
|
return newaddr;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Thank you stackoverflow.com.
|
|
* http://stackoverflow.com/questions/111928/
|
|
* is-there-a-printf-converter-to-print-in-binary-format
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
|
|
#define BYTETOBINARY(byte) \
|
|
(byte & 0x80 ? 1 : 0), \
|
|
(byte & 0x40 ? 1 : 0), \
|
|
(byte & 0x20 ? 1 : 0), \
|
|
(byte & 0x10 ? 1 : 0), \
|
|
(byte & 0x08 ? 1 : 0), \
|
|
(byte & 0x04 ? 1 : 0), \
|
|
(byte & 0x02 ? 1 : 0), \
|
|
(byte & 0x01 ? 1 : 0)
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ShowRegs()
|
|
* Purpose: Display status of CPU registers on DOS console.
|
|
* Arguments: preg - pointer to registers structure
|
|
* pvm - pointer to VM
|
|
* ioaddr - address setup for char I/O emulation
|
|
* ioecho - local I/O echo flag
|
|
* showiostat - if true, I/O emulation status is shown
|
|
* Returns: boolean - true if the stack pointer was longer than
|
|
* 15 (the screen must be cleared).
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat)
|
|
{
|
|
bool ret = false;
|
|
char sBuf[80] = {0};
|
|
|
|
sprintf(sBuf, "| PC: $%04x | Acc: $%02x (" BYTETOBINARYPATTERN
|
|
") | X: $%02x | Y: $%02x |",
|
|
preg->PtrAddr, preg->Acc, BYTETOBINARY(preg->Acc),
|
|
preg->IndX, preg->IndY);
|
|
cout << "*-------------*-----------------------*----------*----------*";
|
|
cout << endl;
|
|
cout << sBuf << endl;
|
|
cout << "*-------------*-----------------------*----------*----------*";
|
|
cout << endl;
|
|
cout << "| NV-BDIZC |";
|
|
cout << " : " << diss_buf << " " << endl;
|
|
cout << "| " << bitset<8>((int)preg->Flags) << " |";
|
|
cout << " : " << curr_buf << " " << endl;
|
|
cout << "*-------------*" << endl;
|
|
cout << endl;
|
|
cout << "Stack: $" << hex << (unsigned short)preg->PtrStack << " " << endl;
|
|
cout << " ";
|
|
cout << " \r";
|
|
// display stack contents
|
|
cout << " [";
|
|
int j = 0, stacklines = 1;
|
|
for (unsigned int addr = 0x0101 + preg->PtrStack; addr < 0x0200; addr++) {
|
|
unsigned int hv = (unsigned int)pvm->MemPeek8bit(addr);
|
|
if (hv < 16) {
|
|
cout << 0;
|
|
}
|
|
cout << hex << hv << " ";
|
|
j++;
|
|
if (j > 15) {
|
|
cout << "]" << endl;
|
|
cout << " [";
|
|
j=0;
|
|
stacklines++;
|
|
}
|
|
}
|
|
cout << "] " << endl;
|
|
ret = (stacklines < g_stackdisp_lines);
|
|
g_stackdisp_lines = stacklines;
|
|
// end display stack contents
|
|
|
|
if (showiostat) {
|
|
cout << endl << "I/O status: ";
|
|
cout << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", ";
|
|
cout << " at: $" << hex << pvm->GetCharIOAddr() << ", ";
|
|
cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl;
|
|
cout << "Graphics status: ";
|
|
cout << (pvm->GetGraphDispActive() ? "enabled" : "disabled") << ", ";
|
|
cout << " at: $" << hex << pvm->GetGraphDispAddr() << endl;
|
|
cout << "ROM: ";
|
|
cout << ((pvm->IsROMEnabled()) ? "enabled." : "disabled.") << " ";
|
|
cout << "Range: $" << hex << pvm->GetROMBegin() << " - $";
|
|
cout << hex << pvm->GetROMEnd() << "." << endl;
|
|
cout << "Op-code execute history: ";
|
|
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled");
|
|
cout << "." << endl;
|
|
}
|
|
cout << " \r";
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ShowMenu()
|
|
* Purpose: Print available commands on the console.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowMenu()
|
|
{
|
|
cout << "------------------------------------+----------------------------------------" << endl;
|
|
cout << " C - continue, S - step | A - set address for next step" << endl;
|
|
cout << " G - go/cont. from new address | N - go number of steps, P - IRQ" << endl;
|
|
cout << " I - toggle char I/O emulation | X - execute from new address" << endl;
|
|
cout << " T - show I/O console | B - blank (clear) screen" << endl;
|
|
cout << " E - toggle I/O local echo | F - toggle registers animation" << endl;
|
|
cout << " J - set animation delay | M - dump memory, W - write memory" << endl;
|
|
cout << " K - toggle ROM emulation | R - show registers, Y - snapshot" << endl;
|
|
cout << " L - load memory image | O - display op-code exec. history" << endl;
|
|
cout << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl;
|
|
cout << " V - toggle graphics emulation | U - enable/disable exec. history" << endl;
|
|
cout << " Z - enable/disable debug traces | 1 - enable/disable perf. stats" << endl;
|
|
cout << " 2 - display debug traces | ? - show this menu" << endl;
|
|
cout << "------------------------------------+----------------------------------------" << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: RunSteps()
|
|
* Purpose: Execute multiple steps of CPU emulation.
|
|
* Arguments:
|
|
* step - boolean flag, true if step by step mode
|
|
* nsteps - # if steps
|
|
* brk - current status of break flag
|
|
* preg - pointer to CPU registers
|
|
* stct - step counter
|
|
* pvm - pointer to VM
|
|
* lrts - status of last RTS flag
|
|
* anim - boolean flag, true - registers animation mode
|
|
* delay - delay for anim mode
|
|
*
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
inline void RunSteps(bool step,
|
|
int nsteps,
|
|
bool brk,
|
|
Regs *preg,
|
|
int stct,
|
|
VMachine *pvm,
|
|
bool lrts,
|
|
bool anim,
|
|
int delay)
|
|
{
|
|
bool cls = false;
|
|
brk = preg->SoftIrq;
|
|
lrts = preg->LastRTS;
|
|
while(step && nsteps > 1 && !brk && !lrts && !opbrk) {
|
|
preg = RunSingleCurrInstr();
|
|
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct;
|
|
cout << " \r";
|
|
if (anim) {
|
|
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; }
|
|
pvm->ScrHome();
|
|
cls = ShowRegs(preg,pvm,false,false);
|
|
cout << endl;
|
|
this_thread::sleep_for(chrono::milliseconds(delay));
|
|
}
|
|
brk = preg->SoftIrq;
|
|
lrts = preg->LastRTS;
|
|
nsteps--;
|
|
stct++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ShowSpeedStats()
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowSpeedStats()
|
|
{
|
|
if (pvm->IsPerfStatsActive()) {
|
|
cout << endl;
|
|
cout << dec;
|
|
cout << "CPU emulation speed stats: " << endl;
|
|
cout << "|-> Average speed based on 1MHz CPU: " << pvm->GetPerfStats().perf_onemhz << " %" << endl;
|
|
cout << "|-> Last measured # of cycles exec.: " << pvm->GetPerfStats().prev_cycles << endl;
|
|
cout << "|-> Last measured time of execution: " << pvm->GetPerfStats().prev_usec << " usec" << endl;
|
|
cout << endl;
|
|
} else {
|
|
cout << endl;
|
|
cout << "Emulation performance stats is OFF." << endl;
|
|
cout << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ExecHistory()
|
|
* Purpose: Display history of executed VM65 code in assembly
|
|
* mnemonics format.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ExecHistory()
|
|
{
|
|
if (pvm->IsExecHistoryActive()) {
|
|
queue<string> exechist(pvm->GetExecHistory());
|
|
cout << "PC : INSTR ACC | X | Y | PS | SP";
|
|
cout << endl;
|
|
cout << "------------------------------------+-----+-----+-----+-----";
|
|
cout << endl;
|
|
while (exechist.size()) {
|
|
cout << exechist.front() << endl;
|
|
exechist.pop();
|
|
}
|
|
} else {
|
|
cout << "Sorry. Op-code execute history is currently disabled." << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: LoadImage()
|
|
* Purpose: Load memory image from file. Set new execute address.
|
|
* Arguments: newaddr - current execute address
|
|
* Returns: unsigned int - new execute address
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned int LoadImage(unsigned int newaddr)
|
|
{
|
|
char typ = 0;
|
|
for (char c = tolower(typ);
|
|
c != 'a' && c != 'b' && c != 'h' && c != 'd';
|
|
c = tolower(typ)) {
|
|
cout << "Type (A - auto/B - binary/H - Intel HEX/D - definition): ";
|
|
cin >> typ;
|
|
}
|
|
cout << " [";
|
|
switch (tolower(typ)) {
|
|
case 'a': cout << "auto"; break;
|
|
case 'b': cout << "binary"; break;
|
|
case 'h': cout << "Intel HEX"; break;
|
|
case 'd': cout << "definition"; break;
|
|
default: break; // should never happen
|
|
}
|
|
cout << "]" << endl;
|
|
string name;
|
|
cout << "Memory Image File Name: ";
|
|
cin >> name;
|
|
cout << " [" << name << "]" << endl;
|
|
if (typ == 'b') PrintVMErr (pvm->LoadRAMBin(name));
|
|
else if (typ == 'h') PrintVMErr (pvm->LoadRAMHex(name));
|
|
else if (typ == 'd') {
|
|
PrintVMErr (pvm->LoadRAMDef(name));
|
|
if (pvm->IsAutoExec()) execvm = true;
|
|
if (newaddr == 0) newaddr = 0x10000;
|
|
}
|
|
else { // automatic file format detection
|
|
pvm->LoadRAM(name);
|
|
if (pvm->IsAutoExec()) execvm = true;
|
|
if (newaddr == 0) newaddr = 0x10000;
|
|
}
|
|
PrintVMErr(pvm->GetLastError());
|
|
|
|
return newaddr;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ToggleIO()
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned int ToggleIO(unsigned int ioaddr)
|
|
{
|
|
if (pvm->GetCharIOActive()) {
|
|
pvm->DisableCharIO();
|
|
cout << "I/O deactivated." << endl;
|
|
} else {
|
|
ioaddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << ioaddr << "]" << endl;
|
|
pvm->SetCharIO(ioaddr, ioecho);
|
|
cout << "I/O activated." << endl;
|
|
}
|
|
|
|
return ioaddr;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ToggleGrDisp()
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned int ToggleGrDisp(unsigned int graddr)
|
|
{
|
|
if (pvm->GetGraphDispActive()) {
|
|
pvm->DisableGraphDisp();
|
|
cout << "Graphics display deactivated." << endl;
|
|
} else {
|
|
graddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << graddr << "]" << endl;
|
|
pvm->SetGraphDisp(graddr);
|
|
cout << "Graphics display activated." << endl;
|
|
}
|
|
|
|
return graddr;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: WriteToMemory()
|
|
* Purpose: Take user input and write to memory.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void WriteToMemory()
|
|
{
|
|
unsigned int tmpaddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << tmpaddr << "]" << endl;
|
|
cout << "Enter hex bytes [00..FF] values separated with NL or spaces, end with [100]:" << endl;
|
|
unsigned short v = 0;
|
|
while (true) {
|
|
cin >> hex >> v;
|
|
cout << " " << hex << v;
|
|
if (v > 0xFF) break;
|
|
pvm->MemPoke8bit(tmpaddr++, v & 0xFF);
|
|
};
|
|
cout << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DisassembleMemory()
|
|
* Purpose: Disassemble machine code in memory to symbolic format.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void DisassembleMemory()
|
|
{
|
|
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
|
cout << PROMPT_RANGE_ADDR << endl;
|
|
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
|
|
cout << " [" << hex << addrbeg << "]" << endl;
|
|
addrend = PromptNewAddress(PROMPT_END_ADDR);
|
|
cout << " [" << hex << addrend << "]" << endl;
|
|
cout << endl;
|
|
for (unsigned int addr = addrbeg; addr <= addrend;) {
|
|
char instrbuf[DISS_BUF_SIZE];
|
|
addr = pvm->Disassemble((unsigned short)addr, instrbuf);
|
|
cout << instrbuf << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DumpMemory()
|
|
* Purpose: Display contents of memory, range entered by user.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void DumpMemory()
|
|
{
|
|
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
|
cout << PROMPT_RANGE_ADDR << endl;
|
|
addrbeg = PromptNewAddress(PROMPT_START_ADDR);
|
|
cout << " [" << hex << addrbeg << "]" << endl;
|
|
addrend = PromptNewAddress(PROMPT_END_ADDR);
|
|
cout << " [" << hex << addrend << "]" << endl;
|
|
cout << endl;
|
|
for (unsigned int addr = addrbeg; addr <= addrend; addr+=16) {
|
|
cout << "\t|";
|
|
for (unsigned int j=0; j < 16; j++) {
|
|
unsigned int hv = (unsigned int)pvm->MemPeek8bit(addr+j);
|
|
if (hv < 16) {
|
|
cout << 0;
|
|
}
|
|
cout << hex << hv << " ";
|
|
}
|
|
cout << "|";
|
|
for (int j=0; j < 16; j++) {
|
|
char cc = (char)pvm->MemPeek8bit(addr+j);
|
|
if (isprint(cc))
|
|
cout << cc;
|
|
else
|
|
cout << "?";
|
|
}
|
|
cout << '\r';
|
|
cout << hex << addr;
|
|
cout << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ToggleDebugTraces()
|
|
* Purpose: Enable/disable debug traces.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ToggleDebugTraces()
|
|
{
|
|
if (pvm->IsDebugTraceActive()) {
|
|
pvm->DisableDebugTrace();
|
|
cout << "Debug traces disabled." << endl;
|
|
} else {
|
|
pvm->EnableDebugTrace();
|
|
cout << "Debug traces enabled." << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Macro: SCRDIV_xxCOL
|
|
* Purpose: Print line out of xx '-' signs, no NL.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
#define SCRDIV_20COL cout << "--------------------";
|
|
#define SCRDIV_19COL cout << "-------------------";
|
|
#define SCRDIV_79COL SCRDIV_20COL; SCRDIV_20COL; SCRDIV_20COL; SCRDIV_19COL;
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DebugTraces()
|
|
* Purpose: Show debug traces.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void DebugTraces()
|
|
{
|
|
if (pvm->IsDebugTraceActive()) {
|
|
queue<string> dbgtrc(pvm->GetDebugTraces());
|
|
cout << "Time [usec] : Message" << endl;
|
|
SCRDIV_79COL; cout << endl;
|
|
int n=0;
|
|
while (dbgtrc.size()) {
|
|
cout << dbgtrc.front() << endl;
|
|
dbgtrc.pop();
|
|
if (n++ == 20) {
|
|
n = 0;
|
|
cout << endl;
|
|
PressEnter2Cont("Press [ENTER] for more...");
|
|
cout << endl;
|
|
}
|
|
}
|
|
SCRDIV_79COL; cout << endl;
|
|
} else {
|
|
cout << "Sorry. Debug traces are currently disabled." << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: TogglePerfStats()
|
|
* Purpose: Enable/disable performance stats.
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void TogglePerfStats()
|
|
{
|
|
if (pvm->IsPerfStatsActive()) {
|
|
pvm->DisablePerfStats();
|
|
cout << "Performance stats were disabled." << endl;
|
|
} else {
|
|
pvm->EnablePerfStats();
|
|
cout << "Performance stats were enabled." << endl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: LoadArgs()
|
|
* Purpose: Parse command line arguments.
|
|
* Arguments: int argc, char *argv[], standard C command line args.
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void LoadArgs(int argc, char *argv[])
|
|
{
|
|
for (int i=1; i<argc; i++) {
|
|
if (!strcmp(argv[i], "-r")) {
|
|
reset = true;
|
|
execvm = true;
|
|
} else if (!strcmp(argv[i], "-b")) {
|
|
loadbin = true;
|
|
} else if (!strcmp(argv[i], "-x")) {
|
|
loadhex = true;
|
|
} else if (!strcmp(argv[i], "-h")) {
|
|
needhelp = true;
|
|
} else {
|
|
ramfile = argv[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/************ corrected in makefile
|
|
|
|
// Quick and dirty SDL2 workaround to 'undefined reference to WinMain'
|
|
|
|
#ifdef main
|
|
#undef main
|
|
#endif
|
|
|
|
*****************/
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: main()
|
|
* Purpose: Application entry point/main loop.
|
|
* Arguments: int argc, char *argv[], standard C command line args.
|
|
* Returns: int - general principle is to return 0 if OK, non-zero
|
|
* otherwise
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
|
|
int main(int argc, char *argv[]) {
|
|
#if defined(LINUX)
|
|
signal(SIGINT, trap_signal);
|
|
signal(SIGTERM, trap_signal);
|
|
if (atexit(reset_terminal_mode)) {
|
|
cout << "WARNING: Can't set exit function." << endl;
|
|
PressEnter2Cont("");
|
|
}
|
|
#endif
|
|
#if defined(WINDOWS)
|
|
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );
|
|
#endif
|
|
pconio = new ConsoleIO();
|
|
if (NULL == pconio) throw MKGenException("Out of memory - ConsoleIO");
|
|
pconio->InitCursesScr();
|
|
pconio->CloseCursesScr();
|
|
string romfile("dummy.rom");
|
|
LoadArgs(argc, argv);
|
|
if (needhelp) { CmdArgHelp(argv[0]); exit(0); }
|
|
if (loadbin && loadhex) {
|
|
cout << "ERROR: Can't load both formats at the same time." << endl;
|
|
exit(-1);
|
|
}
|
|
try {
|
|
cout << endl;
|
|
if (loadbin) {
|
|
pvm = new VMachine(romfile, "dummy.ram");
|
|
if (NULL != pvm) {
|
|
PrintVMErr (pvm->LoadRAMBin(ramfile));
|
|
if (!reset) { reset = execvm = pvm->IsAutoReset(); }
|
|
}
|
|
} else if (loadhex) {
|
|
pvm = new VMachine(romfile, "dummy.ram");
|
|
if (NULL != pvm) PrintVMErr (pvm->LoadRAMHex(ramfile));
|
|
}
|
|
else {
|
|
pvm = new VMachine(romfile, ramfile);
|
|
if (NULL != pvm) PrintVMErr(pvm->GetLastError());
|
|
if (NULL != pvm && !reset) { reset = execvm = pvm->IsAutoReset(); }
|
|
}
|
|
if (NULL == pvm) {
|
|
throw MKGenException("Out of memory - VMachine");
|
|
}
|
|
pvm->ClearScreen();
|
|
CopyrightBanner();
|
|
string cmd;
|
|
bool runvm = false, step = false, brk = false, execaddr = false, stop = true;
|
|
bool lrts = false, anim = false, enrom = pvm->IsROMEnabled(), show_menu = true;
|
|
unsigned int newaddr = pvm->GetRunAddr(), ioaddr = pvm->GetCharIOAddr();
|
|
unsigned int graddr = pvm->GetGraphDispAddr();
|
|
unsigned int rombegin = pvm->GetROMBegin(), romend = pvm->GetROMEnd(), delay = ANIM_DELAY;
|
|
int nsteps = 0;
|
|
if (pvm->IsAutoExec()) {
|
|
execvm = true;
|
|
}
|
|
if (newaddr == 0) newaddr = 0x10000;
|
|
bool bloop = true;
|
|
while (bloop) {
|
|
preg = pvm->GetRegs();
|
|
if (runvm) {
|
|
if (anim) pvm->ClearScreen();
|
|
int stct = 1;
|
|
if (execaddr) {
|
|
preg = ((step) ? RunSingleInstr(newaddr) : pvm->Run(newaddr));
|
|
RunSteps(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
|
execaddr = false;
|
|
} else {
|
|
preg = ((step) ? RunSingleCurrInstr() : pvm->Run());
|
|
RunSteps(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
|
}
|
|
pconio->InitCursesScr();
|
|
pconio->CloseCursesScr();
|
|
if (step)
|
|
cout << "\rExecuted " << dec << stct << ((stct == 1) ? " step." : " steps.") << " " << endl;
|
|
nsteps = 0;
|
|
runvm = step = false;
|
|
newaddr = 0x10000;
|
|
} else if (execvm) {
|
|
if (reset) {
|
|
pvm->Reset();
|
|
preg = pvm->GetRegs();
|
|
reset = false;
|
|
} else {
|
|
preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec());
|
|
}
|
|
execvm = false;
|
|
execaddr = false;
|
|
brk = preg->SoftIrq;
|
|
lrts = preg->LastRTS;
|
|
newaddr = 0x10000;
|
|
}
|
|
pconio->InitCursesScr();
|
|
pconio->CloseCursesScr();
|
|
if (brk || opbrk || stop || lrts) {
|
|
pvm->ClearScreen();
|
|
pvm->ShowIO();
|
|
cout << endl;
|
|
if (opbrk) {
|
|
cout << "Interrupted at " << hex << preg->PtrAddr << endl;
|
|
} else if (brk) {
|
|
cout << "BRK at " << hex << preg->PtrAddr << endl;
|
|
} else if (lrts) {
|
|
cout << "FINISHED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl;
|
|
} else if (stop) {
|
|
cout << "STOPPED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl;
|
|
}
|
|
ShowSpeedStats();
|
|
opbrk = brk = stop = lrts = false;
|
|
pvm->SetOpInterrupt(false);
|
|
ShowRegs(preg,pvm,ioecho,true);
|
|
show_menu = true;
|
|
}
|
|
if (show_menu) {
|
|
|
|
ShowMenu();
|
|
show_menu = false;
|
|
|
|
} else {
|
|
|
|
cout << endl;
|
|
cout << "Type '?' and press [ENTER] for Menu ..." << endl;
|
|
cout << endl;
|
|
|
|
}
|
|
cout << "> ";
|
|
cin >> cmd;
|
|
char c = tolower(cmd.c_str()[0]);
|
|
|
|
// Interpret and execute user input / commands.
|
|
switch (c) {
|
|
|
|
case '?': show_menu = true;
|
|
break;
|
|
// display help
|
|
case 'h': ShowHelp();
|
|
break;
|
|
// Interrupt ReQuest
|
|
case 'p': pvm->Interrupt();
|
|
cout << "OK" << endl;
|
|
show_menu = true;
|
|
break;
|
|
// save snapshot of current CPU and memory in binary image
|
|
case 'y': {
|
|
string name;
|
|
cout << "Enter file name: ";
|
|
cin >> name;
|
|
cout << " [" << name << "]" << endl;
|
|
if (0 == pvm->SaveSnapshot(name)) {
|
|
cout << "OK" << endl;
|
|
} else {
|
|
cout << "ERROR!" << endl;
|
|
cout << "errno=" << errno << endl;
|
|
}
|
|
}
|
|
break;
|
|
// reset CPU
|
|
case '0': reset = true;
|
|
execvm = true;
|
|
runvm = false;
|
|
show_menu = true;
|
|
break;
|
|
|
|
case 'o': ExecHistory();
|
|
break;
|
|
// load memory image
|
|
case 'l': newaddr = LoadImage(newaddr);
|
|
ioaddr = pvm->GetCharIOAddr();
|
|
break;
|
|
// toggle ROM emulation
|
|
case 'k': if (!enrom) {
|
|
enrom = true;
|
|
do {
|
|
rombegin = PromptNewAddress("ROM begin (0200..FFFF): ");
|
|
} while (rombegin < 0x0200);
|
|
cout << " [" << hex << rombegin << "]" << endl;
|
|
do {
|
|
romend = PromptNewAddress("ROM end (ROMBEGIN+1..FFFF): ");
|
|
} while (romend <= rombegin);
|
|
cout << " [" << hex << romend << "]" << endl;
|
|
pvm->EnableROM(rombegin, romend);
|
|
cout << "ROM activated." << endl;
|
|
} else {
|
|
enrom = false;
|
|
pvm->DisableROM();
|
|
cout << "ROM deactivated." << endl;
|
|
}
|
|
break;
|
|
// set registers animation delay
|
|
case 'j': cout << "Delay [ms]: ";
|
|
cin >> dec >> delay;
|
|
cout << " [" << dec << delay << "]" << endl;
|
|
break;
|
|
// toggle registers animation in step mode
|
|
case 'f': anim = !anim;
|
|
cout << "Registers status animation " << ((anim) ? "enabled." : "disabled.") << endl;
|
|
break;
|
|
// clear screen
|
|
case 'b': pconio->CloseCursesScr();
|
|
pvm->ClearScreen();
|
|
break;
|
|
// show registers
|
|
case 'r': stop = true;
|
|
break;
|
|
// toggle local echo for I/O console
|
|
case 'e': if (pvm->GetCharIOActive()) {
|
|
ioecho = !ioecho;
|
|
cout << "I/O echo is " << (ioecho ? "activated." : "deactivated.") << endl;
|
|
pvm->SetCharIO(ioaddr, ioecho);
|
|
} else {
|
|
cout << "ERROR: I/O is deactivated." << endl;
|
|
}
|
|
break;
|
|
// show I/O console
|
|
case 't': if (pvm->GetCharIOActive()) {
|
|
pvm->ShowIO();
|
|
} else {
|
|
cout << "ERROR: I/O is deactivated." << endl;
|
|
}
|
|
break;
|
|
// toggle I/O
|
|
case 'i': ioaddr = ToggleIO(ioaddr);
|
|
break;
|
|
// toggle graphics display
|
|
case 'v': graddr = ToggleGrDisp(graddr);
|
|
break;
|
|
// write to memory
|
|
case 'w': WriteToMemory();
|
|
break;
|
|
// change run address
|
|
case 'a': execaddr = stop = true;
|
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
break;
|
|
|
|
case 's': runvm = step = stop = true;
|
|
break;
|
|
// execute # of steps
|
|
case 'n': nsteps = 0;
|
|
while (nsteps < 1) {
|
|
cout << "# of steps [n>1]: ";
|
|
cin >> dec >> nsteps;
|
|
}
|
|
cout << " [" << dec << nsteps << "]" << endl;
|
|
runvm = step = stop = true;
|
|
show_menu = true;
|
|
break;
|
|
// continue running code
|
|
case 'c': runvm = true;
|
|
show_menu = true;
|
|
break;
|
|
// run from new address until BRK
|
|
case 'g': runvm = true;
|
|
execaddr = true;
|
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
show_menu = true;
|
|
break;
|
|
// execute code at address
|
|
case 'x': execvm = true;
|
|
execaddr = true;
|
|
newaddr = PromptNewAddress(PROMPT_ADDR);
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
show_menu = true;
|
|
break;
|
|
// quit
|
|
case 'q': bloop = false;
|
|
break;
|
|
// disassemble code in memory
|
|
case 'd': DisassembleMemory();
|
|
break;
|
|
// dump memory
|
|
case 'm': DumpMemory();
|
|
break;
|
|
// toggle enable/disable op-code exec. history
|
|
case 'u': pvm->EnableExecHistory(!pvm->IsExecHistoryActive());
|
|
cout << "Op-code execute history has been ";
|
|
cout << (pvm->IsExecHistoryActive() ? "enabled" : "disabled") << ".";
|
|
cout << endl;
|
|
break;
|
|
// toggle enable/disable debug traces in VM
|
|
case 'z': ToggleDebugTraces();
|
|
break;
|
|
// show debug traces
|
|
case '2': DebugTraces();
|
|
break;
|
|
// toggle enable/disable perf. stats
|
|
case '1': TogglePerfStats();
|
|
break;
|
|
|
|
default: cout << "ERROR: Unknown command." << endl;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (NULL != pconio) pconio->CloseCursesScr();
|
|
}
|
|
catch (MKGenException& ex) {
|
|
if (NULL != pconio) pconio->CloseCursesScr();
|
|
cout << ex.GetCause() << endl;
|
|
}
|
|
catch (...) {
|
|
if (NULL != pconio) pconio->CloseCursesScr();
|
|
cout << "ERROR: Fatal." << endl;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: CopyrightBanner()
|
|
* Purpose: Display copyright information.
|
|
* Arguments: n/a
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void CopyrightBanner()
|
|
{
|
|
cout << "Virtual Machine/CPU Emulator (MOS 6502) and Debugger." << endl;
|
|
cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: CmdArgHelp()
|
|
* Purpose: Display command line arguments help/Usage.
|
|
* Arguments: prgname - string, program name
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void CmdArgHelp(string prgname)
|
|
{
|
|
CopyrightBanner();
|
|
|
|
cout << endl << endl;
|
|
cout << "Usage:" << endl << endl;
|
|
cout << "\t" << prgname;
|
|
cout << " [-h] | [ramdeffile] [-b | -x] [-r]" << endl;
|
|
cout << endl << endl;
|
|
cout << "Where:" << endl << endl;
|
|
cout << "\tramdeffile - RAM definition file name" << endl;
|
|
cout << "\t-b - specify input format as binary" << endl;
|
|
cout << "\t-x - specify input format as Intel HEX" << endl;
|
|
cout << "\t-r - after loading, perform CPU RESET" << endl;
|
|
cout << "\t-h - print this help screen" << endl;
|
|
cout << R"(
|
|
|
|
When ran with no arguments, program will load default memory
|
|
definition files: default.rom, default.ram and will enter the debug
|
|
console menu.
|
|
When ramdeffile argument is provided with no input format specified,
|
|
program will attempt to automatically detect input format and load the
|
|
memory definition from the file, set the flags and parameters depending
|
|
on the contents of the memory definition file and enter the corresponding
|
|
mode of operation as defined in that file.
|
|
If input format is specified (-b|-x), program will load memory from the
|
|
provided image file and enter the debug console menu.
|
|
|
|
)";
|
|
cout << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ShowHelp()
|
|
* Purpose: Display Debugger Console Command Reference help.
|
|
* Arguments: n/a
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowHelp()
|
|
{
|
|
cout << R"(Debugger Console Command Reference.
|
|
|
|
S - step
|
|
Executes single opcode at current address.
|
|
C - continue
|
|
Continues code execution from current address until BRK.
|
|
M - dump memory
|
|
Usage: M [startaddr] [endaddr]
|
|
Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF].
|
|
Dumps contents of memory, hexadecimal and ASCII formats."
|
|
G - go/continue from new address until BRK
|
|
Usage: G [address]
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
|
Executes code at provided address, interrupted by BRK opcode.
|
|
X - execute code from new address until RTS
|
|
Usage: X [address]
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
|
Executes code at provided address, until RTS (last one).
|
|
Q - quit
|
|
Exits from the emulator/debugger.
|
|
A - set address for next step
|
|
Usage: A [address]
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
|
Sets current address to a new value.
|
|
N - go number of steps
|
|
Usage: N [steps]
|
|
Where: steps - number of steps in decimal format
|
|
Execute number of opcodes provided in steps argument starting
|
|
from current address.
|
|
P - IRQ
|
|
Send maskable interrupt request to CPU (set the IRQ line LOW).
|
|
W - write to memory
|
|
Usage: W [address] [hexval] [hexval] ... 100
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
|
hexval - byte value in hexadecimal format [00.FF].
|
|
Writes provided values to memory starting at specified address.
|
|
I - toggle char I/O emulation
|
|
Usage: I [address]
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
|
Toggles basic character I/O emulation. When enabled, all writes
|
|
to the specified memory address also writes a character code to
|
|
to a virtual console. All reads from specified memory address
|
|
are interpreted as console character input.
|
|
V - toggle graphics display (video) emulation
|
|
Usage: V [address]
|
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
|
Toggles basic raster (pixel) based RGB graphics display emulation.
|
|
When enabled, window with graphics screen will open and several
|
|
registers are available to control the device starting at provided
|
|
base address. Read programmers reference for detailed documentation
|
|
regarding the available registers and their functions.
|
|
R - show registers
|
|
Displays CPU registers, flags and stack.
|
|
Y - snapshot
|
|
Usage: Y [file_name]
|
|
Where: file_name - the name of the output file.
|
|
Save snapshot of current CPU and memory in a binary file.
|
|
T - show I/O console
|
|
Displays/prints the contents of the virtual console screen.
|
|
Note that in run mode (commands X, G or C), virtual screen is
|
|
displayed automatically in real-time if I/O emulation is enabled.
|
|
E - toggle I/O local echo
|
|
Toggles local echo on/off when I/O emulation is enabled.
|
|
B - blank (clear) screen
|
|
Clears the screen, useful when after exiting I/O emulation or
|
|
registers animation (long stack) your screen is messed up.
|
|
F - toggle registers animation mode
|
|
When in multi-step debug mode (command: N), displaying registers
|
|
can be suppressed or, when animation mode is enabled - they will
|
|
be continuously displayed after each executed step.
|
|
J - set registers status animation delay
|
|
Usage: J [delay]
|
|
Where: delay - time of delay in milliseconds,
|
|
Sets the time added at the end of each execution step in multi
|
|
step mode (command: N). The default value is 250 ms.
|
|
K - toggle ROM emulation
|
|
Usage: K [rombegin] [romend] - to enable,
|
|
K - to disable,
|
|
(OR just use 'K' in both cases and be prompted for arguments.)
|
|
Where:
|
|
rombegin - hexadecimal address [0200..FFFF],
|
|
romend - hexadecimal address [rombegin+1..FFFF].
|
|
Enable/disable ROM emulation and define address range to which the ROM
|
|
(read-only memory) will be mapped. Default range: $D000-$DFFF.
|
|
L - load memory image
|
|
Usage: L [image_type] [image_name]
|
|
Where:
|
|
image_type - A - (auto), B (binary), H (Intel HEX) OR D (definition),
|
|
image_name - name of the image file.
|
|
This function allows to load new memory image from either binary
|
|
image file, Intel HEX format file or the ASCII definition file.
|
|
With option 'A' selected, automatic input format detection will be
|
|
attempted.
|
|
The binary image is always loaded from address 0x0000 and can be up to
|
|
64kB long. The definition file format is a plain text file that can
|
|
contain following keywords and data:
|
|
|
|
ADDR This keyword defines the run address of the executable code.
|
|
It is optional, but if exists, it must be the 1-st keyword
|
|
in the definition file.
|
|
Address in decimal or hexadecimal ($xxxx) format must follow
|
|
in the next line.
|
|
|
|
ORG Changes the current address counter. The line that follows
|
|
sets the new address in decimal or hexadecimal format.
|
|
Data that follows will be put in memory starting from that
|
|
address. This keyword is optional and can be used multiple
|
|
times in the definition file.
|
|
|
|
IOADDR Defines the address of the character I/O emulation. The
|
|
next line sets the address of I/O emulation in decimal or
|
|
hexadecimal format. If the I/O emulation is enabled
|
|
(see ENIO keyword), then any character written to this
|
|
address will be sent to the virtual console. The reading
|
|
from that address will invoke character input from the
|
|
emulated console. That input procedure is of blocking
|
|
type. To invoke non-blocking character procedure, reading
|
|
should be performed from IOADDR+1.
|
|
|
|
ROMBEGIN Defines the address in memory where the beginning of the
|
|
Read Only memory is mapped. The next line that follows this
|
|
keyword sets the address in decimal or hexadecimal format.
|
|
|
|
ROMEND Defines the address in memory where the end of the Read
|
|
Only Memory is mapped. The next line that follows this
|
|
keyword sets the address in decimal or hexadecimal format.
|
|
|
|
ENIO Putting this keyword in memory definition file enables
|
|
rudimentary character I/O emulation and virtual console
|
|
emulation.
|
|
|
|
ENROM Putting this keyword in memory definition file enables
|
|
emulation of Read Only Memory, in range of addresses
|
|
defined by ROMBEGIN and ROMEND keywords.
|
|
|
|
EXEC Define starting address of code which will be automatically
|
|
executed after the memory image is loaded.
|
|
The next line that follows this keyword sets the address
|
|
in decimal or hexadecimal format.
|
|
|
|
RESET Enables auto-reset of the CPU. After loading the memory
|
|
definition file, the CPU reset sequence will be initiated.
|
|
|
|
ENGRAPH Enables raster graphics device emulation.
|
|
|
|
GRAPHADDR Defines the base address of raster graphics device. The next
|
|
line that follows sets the address in decimal or hexadecimal
|
|
format.
|
|
|
|
NOTE: The binary image file can contain a header which contains
|
|
definitions corresponding to the above parameters at fixed
|
|
positions. This header is created when user saves the snapshot of
|
|
current emulator memory image and status. Example use scenario:
|
|
* User loads the image definition file.
|
|
* User adjusts various parameters of the emulator
|
|
(enables/disables devices, sets addresses, changes memory
|
|
contents).
|
|
* User saves the snapshot with 'Y' command.
|
|
* Next time user loads the snapshot image, all the parameters
|
|
and memory contents stick. This way game status can be saved
|
|
or a BASIC interpreter with BASIC program in it.
|
|
See command 'Y' for details.
|
|
|
|
O - display op-codes history
|
|
Show the history of last executed op-codes/instructions, full with
|
|
disassembled mnemonic, argument and CPU registers and status.
|
|
NOTE: op-codes execute history must be enabled, see command 'U'.
|
|
D - diassemble code in memory
|
|
Usage: D [startaddr] [endaddr]
|
|
Where: startaddr,endaddr - hexadecimal address [0000..FFFF].
|
|
Attempt to disassemble code in specified address range and display
|
|
the results (print) on the screen in symbolic form.
|
|
0 - reset
|
|
Run the processor initialization sequence, just like the real CPU
|
|
when its RTS signal is set to LOW and HIGH again. CPU will disable
|
|
interrupts, copy address from vector $FFFC to processors PC and will
|
|
start executing code. Programmer must put initialization routine
|
|
under address pointed by $FFFC vector, which will set the arithmetic
|
|
mode, initialize stack, I/O devices and enable IRQ if needed before
|
|
jumping to main loop. The reset routine disables trapping last RTS
|
|
opcode if stack is empty, so the VM will never return from opcodes
|
|
execution loop unless user interrupts with CTRL-C or CTRL-Break.
|
|
? - display commands menu
|
|
Display the menu of all available in Debug Console commands.
|
|
U - enable/disable exec. history
|
|
Toggle enable/disable of op-codes execute history.
|
|
Disabling this feature improves performance.
|
|
Z - enable/disable debug traces
|
|
Toggle enable/disable of debug traces.
|
|
2 - display debug traces
|
|
Display recent debug traces.
|
|
1 - enable/disable performance stats
|
|
Toggle enable/disable emulation speed measurement.
|
|
|
|
NOTE:
|
|
1. If no arguments provided, each command will prompt user to enter
|
|
missing data.
|
|
2. It is possible to exit from running program to debugger console
|
|
by pressing CTRL-C or CTRL-Pause/Break, which will generate
|
|
a "Operator Interrupt". However in the character input mode
|
|
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
|
|
You may need to press ENTER after that in input mode (DOS).
|
|
)";
|
|
cout << endl;
|
|
}
|