ack/plat/cpm/emu/emulator.c
2019-02-09 13:42:09 +01:00

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);
}
}