405 lines
7.9 KiB
C
405 lines
7.9 KiB
C
#define _POSIX_C_SOURCE 199309
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include "intel_8080_emulator.h"
|
|
#include "dis8080.h"
|
|
#include "globals.h"
|
|
|
|
uint8_t ram[0x10000];
|
|
|
|
struct watchpoint
|
|
{
|
|
uint16_t address;
|
|
uint8_t value;
|
|
bool enabled;
|
|
};
|
|
|
|
static uint16_t breakpoints[16];
|
|
static struct watchpoint watchpoints[16];
|
|
static bool tracing = false;
|
|
static bool singlestepping = true;
|
|
static bool bdosbreak = false;
|
|
|
|
static const char* delimiters = " \t\n\r";
|
|
|
|
uint8_t i8080_read(uint16_t addr)
|
|
{
|
|
return ram[addr];
|
|
}
|
|
|
|
void i8080_write(uint16_t addr, uint8_t value)
|
|
{
|
|
ram[addr] = value;
|
|
}
|
|
|
|
uint8_t i8080_inport(uint8_t addr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void i8080_outport(uint8_t addr, uint8_t value)
|
|
{
|
|
biosbdos_entry(addr & 0xff);
|
|
if (bdosbreak)
|
|
singlestepping = true;
|
|
}
|
|
|
|
void showregs(void)
|
|
{
|
|
uint16_t af = i8080_read_reg16(AF);
|
|
printf("%c%c.%c.%c%c%c sp=%04x af=%04x bc=%04x de=%04x hl=%04x\n",
|
|
(af & 0x80) ? 'S' : 's',
|
|
(af & 0x40) ? 'Z' : 'z',
|
|
(af & 0x10) ? 'H' : 'h',
|
|
(af & 0x04) ? 'P' : 'p',
|
|
(af & 0x02) ? 'N' : 'n',
|
|
(af & 0x01) ? 'C' : 'c',
|
|
i8080_read_reg16(SP),
|
|
af,
|
|
i8080_read_reg16(BC),
|
|
i8080_read_reg16(DE),
|
|
i8080_read_reg16(HL));
|
|
|
|
char buffer[80];
|
|
int tstates;
|
|
uint16_t pc = i8080_read_reg16(PC);
|
|
i8080_disassemble(buffer, sizeof(buffer), pc);
|
|
puts(buffer);
|
|
}
|
|
|
|
static void cmd_register(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
char* w2 = strtok(NULL, delimiters);
|
|
|
|
if (w1 && w2)
|
|
{
|
|
int reg = -1;
|
|
if (strcmp(w1, "sp") == 0)
|
|
reg = SP;
|
|
else if (strcmp(w1, "pc") == 0)
|
|
reg = PC;
|
|
else if (strcmp(w1, "af") == 0)
|
|
reg = AF;
|
|
else if (strcmp(w1, "bc") == 0)
|
|
reg = BC;
|
|
else if (strcmp(w1, "de") == 0)
|
|
reg = DE;
|
|
else if (strcmp(w1, "hl") == 0)
|
|
reg = HL;
|
|
else
|
|
{
|
|
printf("Bad register\n");
|
|
return;
|
|
}
|
|
|
|
i8080_write_reg16(reg, strtoul(w2, NULL, 16));
|
|
}
|
|
|
|
showregs();
|
|
}
|
|
|
|
static void cmd_break(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
{
|
|
uint16_t breakpc = strtoul(w1, NULL, 16);
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
{
|
|
if (breakpoints[i] == 0xffff)
|
|
{
|
|
breakpoints[i] = breakpc;
|
|
return;
|
|
}
|
|
}
|
|
printf("Too many breakpoints\n");
|
|
}
|
|
else
|
|
{
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
{
|
|
if (breakpoints[i] != 0xffff)
|
|
printf("%04x\n", breakpoints[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_watch(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
{
|
|
uint16_t watchaddr = strtoul(w1, NULL, 16);
|
|
for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
|
|
{
|
|
struct watchpoint* w = &watchpoints[i];
|
|
if (!w->enabled)
|
|
{
|
|
w->address = watchaddr;
|
|
w->enabled = true;
|
|
w->value = ram[watchaddr];
|
|
return;
|
|
}
|
|
}
|
|
printf("Too many breakpoints\n");
|
|
}
|
|
else
|
|
{
|
|
for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
|
|
{
|
|
struct watchpoint* w = &watchpoints[i];
|
|
if (w->enabled)
|
|
printf("%04x (current value: %02x)\n", w->address, w->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_delete_breakpoint(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
{
|
|
uint16_t breakpc = strtoul(w1, NULL, 16);
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
{
|
|
if (breakpoints[i] == breakpc)
|
|
{
|
|
breakpoints[i] = 0xffff;
|
|
return;
|
|
}
|
|
}
|
|
printf("No such breakpoint\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_delete_watchpoint(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
{
|
|
uint16_t address = strtoul(w1, NULL, 16);
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
{
|
|
struct watchpoint* w = &watchpoints[i];
|
|
if (w->enabled && (w->address == address))
|
|
{
|
|
w->enabled = false;
|
|
return;
|
|
}
|
|
}
|
|
printf("No such watchpoint\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_memory(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
char* w2 = strtok(NULL, delimiters);
|
|
|
|
if (!w2)
|
|
w2 = "100";
|
|
|
|
if (w1 && w2)
|
|
{
|
|
uint16_t startaddr = strtoul(w1, NULL, 16);
|
|
uint16_t endaddr = startaddr + strtoul(w2, NULL, 16);
|
|
uint16_t startrounded = startaddr & ~0xf;
|
|
uint16_t endrounded = (endaddr + 0xf) & ~0xf;
|
|
|
|
uint16_t p = startrounded;
|
|
|
|
while (p < endrounded)
|
|
{
|
|
printf("%04x : ", p);
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
uint16_t pp = p + i;
|
|
if ((pp >= startaddr) && (pp < endaddr))
|
|
printf("%02x ", ram[pp]);
|
|
else
|
|
printf(" ");
|
|
}
|
|
printf(": ");
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
uint16_t pp = p + i;
|
|
if ((pp >= startaddr) && (pp < endaddr))
|
|
{
|
|
uint8_t c = ram[pp];
|
|
if ((c < 32) || (c > 127))
|
|
c = '.';
|
|
putchar(c);
|
|
}
|
|
else
|
|
putchar(' ');
|
|
}
|
|
p += 16;
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_unassemble(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
char* w2 = strtok(NULL, delimiters);
|
|
uint16_t startaddr = i8080_read_reg16(PC);
|
|
uint16_t endaddr;
|
|
|
|
if (w1)
|
|
startaddr = strtoul(w1, NULL, 16);
|
|
endaddr = startaddr + 0x20;
|
|
if (w2)
|
|
endaddr = startaddr + strtoul(w2, NULL, 16);
|
|
|
|
while (startaddr < endaddr)
|
|
{
|
|
char buffer[80];
|
|
startaddr = i8080_disassemble(buffer, sizeof(buffer), startaddr);
|
|
puts(buffer);
|
|
}
|
|
}
|
|
|
|
static void cmd_bdos(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
bdosbreak = !!strtoul(w1, NULL, 16);
|
|
else
|
|
printf("break on bdos entry: %s\n", bdosbreak ? "on" : "off");
|
|
}
|
|
|
|
static void cmd_tracing(void)
|
|
{
|
|
char* w1 = strtok(NULL, delimiters);
|
|
if (w1)
|
|
tracing = !!strtoul(w1, NULL, 16);
|
|
else
|
|
printf("tracing: %s\n", tracing ? "on" : "off");
|
|
}
|
|
|
|
static void cmd_help(void)
|
|
{
|
|
printf("Sleazy debugger\n"
|
|
" r show registers\n"
|
|
" r <reg> <value> set register\n"
|
|
" b show breakpoints\n"
|
|
" b <addr> set breakpoint\n"
|
|
" db <addr> delete breakpoint\n"
|
|
" w <addr> set watchpoint\n"
|
|
" dw <addr> delete watchpoint\n"
|
|
" m <addr> <len> show memory\n"
|
|
" u <addr> <len> unassemble memory\n"
|
|
" s single step\n"
|
|
" g continue\n"
|
|
" bdos 0|1 enable break on bdos entry\n"
|
|
" tracing 0|1 enable tracing\n"
|
|
);
|
|
}
|
|
|
|
static void debug(void)
|
|
{
|
|
bool go = false;
|
|
showregs();
|
|
while (!go)
|
|
{
|
|
char cmdline[80];
|
|
printf("debug> ");
|
|
fflush(stdout);
|
|
if (!fgets(cmdline, sizeof(cmdline), stdin))
|
|
exit(0);
|
|
|
|
char* token = strtok(cmdline, delimiters);
|
|
if (token != NULL)
|
|
{
|
|
if (strcmp(token, "?") == 0)
|
|
cmd_help();
|
|
else if (strcmp(token, "r") == 0)
|
|
cmd_register();
|
|
else if (strcmp(token, "b") == 0)
|
|
cmd_break();
|
|
else if (strcmp(token, "w") == 0)
|
|
cmd_watch();
|
|
else if (strcmp(token, "db") == 0)
|
|
cmd_delete_breakpoint();
|
|
else if (strcmp(token, "dw") == 0)
|
|
cmd_delete_watchpoint();
|
|
else if (strcmp(token, "m") == 0)
|
|
cmd_memory();
|
|
else if (strcmp(token, "u") == 0)
|
|
cmd_unassemble();
|
|
else if (strcmp(token, "s") == 0)
|
|
{
|
|
singlestepping = true;
|
|
go = true;
|
|
}
|
|
else if (strcmp(token, "g") == 0)
|
|
{
|
|
singlestepping = false;
|
|
go = true;
|
|
}
|
|
else if (strcmp(token, "bdos") == 0)
|
|
cmd_bdos();
|
|
else if (strcmp(token, "tracing") == 0)
|
|
cmd_tracing();
|
|
else
|
|
printf("Bad command\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sigusr1_cb(int number)
|
|
{
|
|
singlestepping = true;
|
|
}
|
|
|
|
void emulator_init(void)
|
|
{
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
breakpoints[i] = 0xffff;
|
|
|
|
singlestepping = flag_enter_debugger;
|
|
|
|
struct sigaction action = {
|
|
.sa_handler = sigusr1_cb
|
|
};
|
|
sigaction(SIGUSR1, &action, NULL);
|
|
}
|
|
|
|
void emulator_run(void)
|
|
{
|
|
for (;;)
|
|
{
|
|
uint16_t pc = i8080_read_reg16(PC);
|
|
if (!singlestepping)
|
|
{
|
|
for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
|
|
if (pc == breakpoints[i])
|
|
singlestepping = true;
|
|
}
|
|
for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
|
|
{
|
|
struct watchpoint* w = &watchpoints[i];
|
|
if (w->enabled && (ram[w->address] != w->value))
|
|
{
|
|
printf("\nWatchpoint hit: %04x has changed from %02x to %02x\n",
|
|
w->address, w->value, ram[w->address]);
|
|
w->value = ram[w->address];
|
|
singlestepping = true;
|
|
}
|
|
}
|
|
|
|
if (singlestepping)
|
|
debug();
|
|
else if (tracing)
|
|
showregs();
|
|
|
|
i8080_exec(1);
|
|
}
|
|
}
|
|
|