mirror of https://github.com/makarcz/vm6502.git
334 lines
12 KiB
C++
334 lines
12 KiB
C++
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <bitset>
|
|
#include "MKCpu.h"
|
|
#include "Memory.h"
|
|
#include "Display.h"
|
|
#include "VMachine.h"
|
|
#include "MKGenException.h"
|
|
|
|
using namespace std;
|
|
using namespace MKBasic;
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: ShowHelp()
|
|
* Purpose: Display commands help.
|
|
* Arguments: n/a
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowHelp()
|
|
{
|
|
cout << "Virtual Machine/CPU emulator/Debugger Command Reference." << endl << endl;
|
|
cout << "S - step" << endl;
|
|
cout << " Executes single opcode at current address." << endl;
|
|
cout << "C - continue" << endl;
|
|
cout << " Continues code execution from current address until BRK." << endl;
|
|
cout << "D - dump memory" << endl;
|
|
cout << " Usage: D [startaddr] [endaddr]" << endl;
|
|
cout << " Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF]." << endl;
|
|
cout << " Dumps contents of memory, hexadecimal and ASCII formats." << endl;
|
|
cout << "G - go/continue from new address until BRK" << endl;
|
|
cout << " Usage: G [address]" << endl;
|
|
cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl;
|
|
cout << " Executes code at provided address, interrupted by BRK opcode." << endl;
|
|
cout << "X - execute code from new address until RTS" << endl;
|
|
cout << " Usage: X [address]" << endl;
|
|
cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl;
|
|
cout << " Executes code at provided address, until RTS (last one)." << endl;
|
|
cout << "Q - quit" << endl;
|
|
cout << " Exits from the emulator/debugger." << endl;
|
|
cout << "A - set address for next step" << endl;
|
|
cout << " Usage: A [address]" << endl;
|
|
cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl;
|
|
cout << " Sets current address to a new value." << endl;
|
|
cout << "N - go number of steps" << endl;
|
|
cout << " Usage: N [steps]" << endl;
|
|
cout << " Where: steps - number of steps in decimal format" << endl;
|
|
cout << " Execute number of opcodes provided in steps argument starting" << endl;
|
|
cout << " from current address." << endl;
|
|
cout << "W - write to memory" << endl;
|
|
cout << " Usage: W [address] [hexval] [hexval] ... 100" << endl;
|
|
cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]," << endl;
|
|
cout << " hexval - byte value in hexadecimal format [00.FF]." << endl;
|
|
cout << " Writes provided values to memory starting at specified address." << endl;
|
|
cout << "I - toggle char I/O emulation" << endl;
|
|
cout << " Usage: I [address]" << endl;
|
|
cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]," << endl;
|
|
cout << " Toggles basic character I/O emulation. When enabled, all writes" << endl;
|
|
cout << " to the specified memory address also writes a character code to" << endl;
|
|
cout << " to a virtual console. All reads from specified memory address" << endl;
|
|
cout << " are interpreted as console character input." << endl;
|
|
cout << "R - regs" << endl;
|
|
cout << " Displays CPU registers and flags." << endl;
|
|
cout << "T - show I/O console" << endl;
|
|
cout << " Displays/prints the contents of the virtual console screen." << endl;
|
|
cout << " Note that in run mode (commands X, G or C), virtual screen is" << endl;
|
|
cout << " displayed automatically in real-time if I/O emulation is enabled." << endl;
|
|
cout << "E - toggle I/O local echo" << endl;
|
|
cout << " Toggles local echo on/off when I/O emulation is enabled." << endl;
|
|
cout << "B - blank (clear) screen" << endl;
|
|
cout << " Clears the screen, useful when after exiting I/O emulation" << endl;
|
|
cout << " your screen is messed up." << endl;
|
|
cout << "NOTE:" << endl;
|
|
cout << " If no arguments provided, each command will prompt user to enter" << endl;
|
|
cout << " missing data." << endl;
|
|
cout << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowRegs(Regs *preg, VMachine *pvm, unsigned short ioaddr, bool ioecho)
|
|
{
|
|
cout << "Registers:" << endl;
|
|
cout << " Acc: $" << hex << (unsigned short)preg->Acc << "\t(%" << bitset<8>((int)preg->Acc) << ")" << endl;
|
|
cout << " X: $" << hex << (unsigned short)preg->IndX << endl;
|
|
cout << " Y: $" << hex << (unsigned short)preg->IndY << endl;
|
|
cout << " Addr: $" << hex << preg->PtrAddr << endl;
|
|
cout << " Acc16: $" << hex << preg->Acc16 << endl;
|
|
cout << " Ptr16: $" << hex << preg->Ptr16 << endl;
|
|
cout << " Stack: $" << hex << (unsigned short)preg->PtrStack << endl;
|
|
cout << " Flags: NV-BDIZC" << endl;
|
|
cout << " " << bitset<8>((int)preg->Flags) << endl;
|
|
cout << endl << "I/O status: " << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", ";
|
|
cout << " at: $" << hex << ioaddr << ", ";
|
|
cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl;
|
|
// cout << "-------------------------------------------------------------------------------" << endl;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void ShowMenu()
|
|
{
|
|
cout << "---------------------------------------------------------------------------" << endl;
|
|
cout << "S - step | C - continue, D - dump memory | G - go/continue from new address" << endl;
|
|
cout << "Q - quit | A - set address for next step | N - go number of steps" << endl;
|
|
cout << "H - help | I - toggle char I/O emulation | W - write to memory" << endl;
|
|
cout << "R - regs | T - show I/O console | E - toggle I/O local echo" << endl;
|
|
cout << " | X - execute from new address | B - blank (clear) screen" << endl;
|
|
cout << "---------------------------------------------------------------------------" << endl;
|
|
}
|
|
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
#define RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts) \
|
|
brk = preg->SoftIrq; \
|
|
lrts = preg->LastRTS; \
|
|
while(step && nsteps > 1 && !brk && !lrts) { \
|
|
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct << "\r"; \
|
|
preg = pvm->Step(); \
|
|
brk = preg->SoftIrq; \
|
|
nsteps--; \
|
|
stct++; \
|
|
}
|
|
|
|
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
|
|
|
|
int main(int argc, char** argv) {
|
|
string romfile("dummy.rom"), ramfile("dummy.ram");
|
|
if (argc > 1) {
|
|
ramfile = argv[1];
|
|
}
|
|
try {
|
|
cout << endl;
|
|
VMachine *pvm = new VMachine(romfile, ramfile);
|
|
pvm->ClearScreen();
|
|
cout << "Welcome to Virtual Machine/CPU Emulator (6502)/Debugger." << endl;
|
|
cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl;
|
|
string cmd;
|
|
bool runvm = false, step = false, brk = false, execaddr = false, stop = true;
|
|
bool ioecho = false, lrts = false, execvm = false;
|
|
unsigned int newaddr = 0x10000, ioaddr = 0xE000, tmpaddr = 0x0000;
|
|
int nsteps = 0;
|
|
while (true) {
|
|
Regs *preg = pvm->GetRegs();
|
|
if (runvm) {
|
|
int stct = 1;
|
|
if (execaddr) {
|
|
preg = ((step) ? pvm->Step(newaddr) : pvm->Run(newaddr));
|
|
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts);
|
|
execaddr = false;
|
|
newaddr = 0x10000;
|
|
} else {
|
|
preg = ((step) ? pvm->Step() : pvm->Run());
|
|
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts);
|
|
}
|
|
if (step)
|
|
cout << "\rExecuted " << dec << stct << ((stct == 1) ? " step." : " steps.") << " " << endl;
|
|
nsteps = 0;
|
|
runvm = step = false;
|
|
} else if (execvm) {
|
|
preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec());
|
|
execvm = false;
|
|
brk = preg->SoftIrq;
|
|
lrts = preg->LastRTS;
|
|
}
|
|
if (brk || stop || lrts) {
|
|
cout << endl;
|
|
if (brk) {
|
|
cout << "BRK at " << hex << preg->PtrAddr << endl;
|
|
brk = stop = lrts = false;
|
|
} else if (lrts) {
|
|
cout << "FINISHED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl;
|
|
brk = stop = lrts = false;
|
|
} else if (stop) {
|
|
cout << "STOPPED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl;
|
|
brk = stop = lrts = false;
|
|
}
|
|
ShowRegs(preg,pvm,ioaddr,ioecho);
|
|
}
|
|
ShowMenu();
|
|
cout << "> ";
|
|
cin >> cmd;
|
|
char c = tolower(cmd.c_str()[0]);
|
|
if (c == 'h') {
|
|
ShowHelp();
|
|
} else if (c == 'b') {
|
|
pvm->ClearScreen();
|
|
} else if (c == 'r') {
|
|
stop = true;
|
|
} else if (c == '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;
|
|
}
|
|
} else if (c == 't') {
|
|
if (pvm->GetCharIOActive()) {
|
|
pvm->ShowIO();
|
|
} else {
|
|
cout << "ERROR: I/O is deactivated." << endl;
|
|
}
|
|
} else if (c == 'i') {
|
|
if (pvm->GetCharIOActive()) {
|
|
pvm->DisableCharIO();
|
|
cout << "I/O deactivated." << endl;
|
|
} else {
|
|
ioaddr = PromptNewAddress("Address (0..FFFF): ");
|
|
cout << " [" << hex << ioaddr << "]" << endl;
|
|
pvm->SetCharIO(ioaddr, ioecho);
|
|
cout << "I/O activated." << endl;
|
|
}
|
|
} else if (c == 'w') {
|
|
tmpaddr = PromptNewAddress("Address (0..FFFF): ");
|
|
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;
|
|
} else if (c == 'a') {
|
|
execaddr = stop = true;
|
|
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
} else if (c == 's') {
|
|
runvm = step = stop = true;
|
|
} else if (c == 'n') {
|
|
nsteps = 0;
|
|
while (nsteps < 1) {
|
|
cout << "# of steps [n>1]: ";
|
|
cin >> dec >> nsteps;
|
|
}
|
|
cout << " [" << dec << nsteps << "]" << endl;
|
|
runvm = step = stop = true;
|
|
} else if (c == 'c') {
|
|
runvm = true;
|
|
} else if (c == 'g') {
|
|
runvm = true;
|
|
execaddr = true;
|
|
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
} else if (c == 'x') {
|
|
execvm = true;
|
|
execaddr = true;
|
|
newaddr = PromptNewAddress("Address (0..FFFF): ");
|
|
cout << " [" << hex << newaddr << "]" << endl;
|
|
} else if (c == 'q') {
|
|
break;
|
|
} else if (c == 'd') {
|
|
unsigned int addrbeg = 0x10000, addrend = 0x10000;
|
|
cout << "Enter address range (0..0xFFFF)..." << endl;
|
|
addrbeg = PromptNewAddress("Start address (0..FFFF): ");
|
|
cout << " [" << hex << addrbeg << "]" << endl;
|
|
addrend = PromptNewAddress("End address (0..FFFF): ");
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (MKGenException& ex) {
|
|
cout << "ERROR: " << ex.GetCause() << endl;
|
|
}
|
|
catch (...) {
|
|
cout << "ERROR: Fatal." << endl;
|
|
}
|
|
return 0;
|
|
}
|