411 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			411 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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	char* w1 = strtok(NULL, delimiters);
 | 
						|
	if (w1)
 | 
						|
	{
 | 
						|
		uint16_t breakpc = strtoul(w1, NULL, 16);
 | 
						|
		for (i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
						|
		{
 | 
						|
			if (breakpoints[i] == 0xffff)
 | 
						|
			{
 | 
						|
				breakpoints[i] = breakpc;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		printf("Too many breakpoints\n");
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		for (i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
						|
		{
 | 
						|
			if (breakpoints[i] != 0xffff)
 | 
						|
				printf("%04x\n", breakpoints[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void cmd_watch(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	char* w1 = strtok(NULL, delimiters);
 | 
						|
	if (w1)
 | 
						|
	{
 | 
						|
		uint16_t watchaddr = strtoul(w1, NULL, 16);
 | 
						|
		for (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 (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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	char* w1 = strtok(NULL, delimiters);
 | 
						|
	if (w1)
 | 
						|
	{
 | 
						|
		uint16_t breakpc = strtoul(w1, NULL, 16);
 | 
						|
		for (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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	char* w1 = strtok(NULL, delimiters);
 | 
						|
	if (w1)
 | 
						|
	{
 | 
						|
		uint16_t address = strtoul(w1, NULL, 16);
 | 
						|
		for (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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	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 (i = 0; i < 16; i++)
 | 
						|
			{
 | 
						|
				uint16_t pp = p + i;
 | 
						|
				if ((pp >= startaddr) && (pp < endaddr))
 | 
						|
					printf("%02x ", ram[pp]);
 | 
						|
				else
 | 
						|
					printf("   ");
 | 
						|
			}
 | 
						|
			printf(": ");
 | 
						|
			for (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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	for (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)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
		uint16_t pc = i8080_read_reg16(PC);
 | 
						|
		if (!singlestepping)
 | 
						|
		{
 | 
						|
			for (i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
						|
				if (pc == breakpoints[i])
 | 
						|
					singlestepping = true;
 | 
						|
		}
 | 
						|
		for (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);
 | 
						|
	}
 | 
						|
}
 | 
						|
 |