/*
* Z80SIM - a Z80-CPU simulator
*
* Copyright (C) 1987-92 by Udo Munk
*
* This modul contains the user interface, a full qualified ICE,
* for the Z80-CPU simulation. This modul is released to the public domain,
* and may be modified by user.
*
* History:
* 28-SEP-87 Development on TARGON/35 with AT&T Unix System V.3
* 11-JAN-89 Release 1.1
* 08-FEB-89 Release 1.2
* 13-MAR-89 Release 1.3
* 09-FEB-90 Release 1.4 Ported to TARGON/31 M10/30
* 20-DEC-90 Release 1.5 Ported to COHERENT 3.0
* 10-JUN-92 Release 1.6 long casting problem solved with COHERENT 3.2
* and some optimization
* 25-JUN-92 Release 1.7 comments in english and ported to COHERENT 4.0
*/
/*
* This modul is an ICE type user interface to debug Z80 programs
* on a host system.
*/
/*
* The function "mon()" is the dialog user interface, called
* from the simulation just after program start.
*/
mon()
{
register int eoj = 1;
static char cmd[LENCMD];
if (x_flag) {
if (do_getfile(xfn) == 0)
do_go("");
}
while (eoj) {
next:
printf(">>> ");
fflush(stdout);
if (fgets(cmd, LENCMD, stdin) == NULL) {
putchar('\n');
goto next;
}
switch (*cmd) {
case '\n':
do_step();
break;
case 't':
do_trace(cmd + 1);
break;
case 'g':
do_go(cmd + 1);
break;
case 'd':
do_dump(cmd + 1);
break;
#if !defined(COHERENT) || defined(_I386)
case 'l':
do_list(cmd + 1);
break;
#endif
case 'm':
do_modify(cmd + 1);
break;
case 'f':
do_fill(cmd + 1);
break;
#if !defined(COHERENT) || defined(_I386)
case 'v':
do_move(cmd + 1);
break;
#endif
case 'x':
do_reg(cmd + 1);
break;
case 'p':
do_port(cmd + 1);
break;
case 'b':
do_break(cmd + 1);
break;
case 'h':
do_hist(cmd + 1);
break;
case 'z':
do_count(cmd + 1);
break;
#if !defined(COHERENT) || defined(_I386)
case 'c':
do_clock();
break;
#endif
case 's':
do_show();
break;
case '?':
do_help();
break;
case 'r':
do_getfile(cmd + 1);
break;
case '!':
do_unix(cmd + 1);
break;
case 'q':
eoj = 0;
break;
default:
puts("what??");
break;
}
}
}
/*
* Execute a single step
*/
static do_step()
{
#if !defined(COHERENT) || defined(_I386)
BYTE *p;
#endif
cpu_state = SINGLE_STEP;
cpu_error = NONE;
cpu();
if (cpu_error == OPHALT)
handel_break();
cpu_err_msg();
print_head();
print_reg();
#if !defined(COHERENT) || defined(_I386)
p = PC;
disass(&p, p - ram);
#endif
}
/*
* Execute several steps with trace output
*/
static do_trace(s)
register char *s;
{
register int count, i;
while (isspace(*s))
s++;
if (*s == '\0')
count = 20;
else
count = atoi(s);
cpu_state = SINGLE_STEP;
cpu_error = NONE;
print_head();
print_reg();
for (i = 0; i < count; i++) {
cpu();
print_reg();
if (cpu_error) {
if (cpu_error == OPHALT) {
if (!handel_break()) {
break;
}
} else
break;
}
}
cpu_err_msg();
}
/*
* Run the CPU emulation endless
*/
static do_go(s)
register char *s;
{
while (isspace(*s))
s++;
if (isxdigit(*s))
PC = ram + exatoi(s);
cont:
cpu_state = CONTIN_RUN;
cpu_error = NONE;
cpu();
if (cpu_error == OPHALT)
if (handel_break())
if (!cpu_error)
goto cont;
cpu_err_msg();
print_head();
print_reg();
}
/*
* Handling of software breakpoints (HALT opcode):
* Behandlung von Software Breakpoints (HALT Op-Code)
* Output: 0 breakpoint or other HALT opcode reached (stop)
* 1 breakpoint reached, passcounter not reached (continue)
*/
static handel_break()
{
#ifdef SBSIZE
register int i;
int break_address;
for (i = 0; i < SBSIZE; i++) /* search for breakpoint */
if (soft[i].sb_adr == PC - ram - 1)
goto was_softbreak;
return(0);
was_softbreak:
#ifdef HISIZE
h_next--; /* correct history */
if (h_next < 0)
h_next = 0;
#endif
break_address = PC - ram - 1; /* store adr of breakpoint */
cpu_error = NONE; /* HALT was a breakpoint */
PC--; /* substitute HALT opcode by */
*PC = soft[i].sb_oldopc; /* original opcode */
cpu_state = SINGLE_STEP; /* and execute it */
cpu();
*(ram + soft[i].sb_adr) = 0x76; /* restore HALT opcode again */
soft[i].sb_passcount++; /* increment passcounter */
if (soft[i].sb_passcount != soft[i].sb_pass)
return(1); /* pass not reached, continue */
printf("Software breakpoint %d reached at %04x\n", i, break_address);
soft[i].sb_passcount = 0; /* reset passcounter */
return(0); /* pass reached, stop */
#endif
}
/*
* Memory dump
*/
static do_dump(s)
register char *s;
{
register int i, j;
BYTE c;
while (isspace(*s))
s++;
if (isxdigit(*s)) {
#if defined(COHERENT) && !defined(_I386)
if (exatoi(s) < 0) {
puts(adr_err);
return;
}
#endif
wrk_ram = ram + exatoi(s) - exatoi(s) % 16;
}
printf("Adr ");
for (i = 0; i < 16; i++)
printf("%02x ", i);
puts(" ASCII");
for (i = 0; i < 16; i++) {
printf("%04x - ", wrk_ram - ram);
for (j = 0; j < 16; j++) {
printf("%02x ", *wrk_ram);
wrk_ram++;
#if !defined(COHERENT) || defined(_I386)
if (wrk_ram > ram + 65535)
#else
if (wrk_ram > ram + 32767)
#endif
wrk_ram = ram;
}
putchar('\t');
for (j = -16; j < 0; j++)
printf("%c",((c = *(wrk_ram+j))>=' ' && c<=0x7f) ? c : '.');
putchar('\n');
}
}
/*
* Software breakpoints
*/
static do_break(s)
register char *s;
{
#ifndef SBSIZE
puts("Sorry, no breakpoints available");
puts("Please recompile with SBSIZE defined in sim.h");
#else
register int i;
if (!break_flag) {
puts("Can't use softbreaks with -h option.");
return;
}
if (*s == '\n') {
puts("No Addr Pass Counter");
for (i = 0; i < SBSIZE; i++)
if (soft[i].sb_pass)
printf("%02d %04x %05d %05d\n", i,
soft[i].sb_adr,soft[i].sb_pass,soft[i].sb_passcount);
return;
}
if (isxdigit(*s)) {
i = atoi(s++);
if (i >= SBSIZE) {
printf("breakpoint %d not available\n", i);
return;
}
} else {
i = sb_next++;
if (sb_next == SBSIZE)
sb_next = 0;
}
while (isspace(*s))
s++;
if (*s == 'c') {
*(ram + soft[i].sb_adr) = soft[i].sb_oldopc;
memset((char *) &soft[i], 0, sizeof(struct softbreak));
return;
}
#if defined(COHERENT) && !defined(_I386)
if (exatoi(s) < 0) {
puts(adr_err);
return;
}
#endif
if (soft[i].sb_pass)
*(ram + soft[i].sb_adr) = soft[i].sb_oldopc;
soft[i].sb_adr = exatoi(s);
soft[i].sb_oldopc = *(ram + soft[i].sb_adr);
*(ram + soft[i].sb_adr) = 0x76;
while (!iscntrl(*s) && !ispunct(*s))
s++;
if (*s != ',')
soft[i].sb_pass = 1;
else
soft[i].sb_pass = exatoi(++s);
soft[i].sb_passcount = 0;
#endif
}
/*
* History
*/
static do_hist(s)
register char *s;
{
#ifndef HISIZE
puts("Sorry, no history available");
puts("Please recompile with HISIZE defined in sim.h");
#else
register int i, l, b, e, c, sa;
while (isspace(*s))
s++;
switch (*s) {
case 'c':
memset((char *) his, 0, sizeof(struct history) * HISIZE);
h_next = 0;
h_flag = 0;
break;
default:
if ((h_next == 0) && (h_flag == 0)) {
puts("History memory is empty");
break;
}
e = h_next;
b = (h_flag) ? h_next + 1 : 0;
l = 0;
while (isspace(*s))
s++;
if (*s)
sa = exatoi(s);
else
sa = -1;
for (i = b; i != e; i++) {
if (i == HISIZE)
i = 0;
if (sa != -1)
if (his[i].h_adr < sa)
continue;
else
sa = -1;
printf("%04x AF=%04x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x SP=%04x\n",
his[i].h_adr, his[i].h_af, his[i].h_bc,
his[i].h_de, his[i].h_hl, his[i].h_ix,
his[i].h_iy, his[i].h_sp);
l++;
if (l == 20) {
l = 0;
printf("q = quit, else continue: ");
c = getkey();
putchar('\n');
if (toupper(c) == 'Q')
break;
}
}
break;
}
#endif
}
/*
* Runti
me measurement by counting the executed T states
*/
static do_count(s)
register char *s;
{
#ifndef WANT_TIM
puts("Sorry, no t-state count available");
puts("Please recompile with WANT_TIM defined in sim.h");
#else
while (isspace(*s))
s++;
if (*s == '\0') {
puts("start stop status T-states");
printf("%04x %04x %s %lu\n",
t_start - ram, t_end - ram, t_flag ? "on ": "off", t_states);
} else {
t_start = ram + exatoi(s);
while (*s != ',' && *s != '\0')
s++;
if (*s)
t_end = ram + exatoi(++s);
t_states = 0L;
t_flag = 0;
}
#endif
}
#if !defined(COHERENT) || defined(_I386)
/*
* Calculate the clock frequency of the emulated CPU:
* into memory locations 0000H to 0002H the following
* code will be stored:
* LOOP: JP LOOP
* It uses 10 T states for each execution. A 3 secound
* timer is started and then the CPU. For every opcode
* fetch the R register is incremented by one and after
* the timer is down and stopps the emulation, the clock
* speed of the CPU is calculated with:
* f = R / 300000
*/
static do_clock()
{
static BYTE save[3];
int timeout();
unsigned alarm();
/*
* This function is the signal handler for the timer interrupt.
* The CPU emulation is stopped here.
*/
static timeout()
{
cpu_state = STOPPED;
}
#endif
/*
* Output informations about compiling options
*/
static do_show()
{
register int i;
printf("Release: %s\n", RELEASE);
#ifdef HISIZE
i = HISIZE;
#else
i = 0;
#endif
printf("No. of entrys in history memory: %d\n", i);
#ifdef SBSIZE
i = SBSIZE;
#else
i = 0;
#endif
printf("No. of software breakpoints: %d\n", i);
#ifdef WANT_SPC
i = 1;
#else
i = 0;
#endif
printf("Stackpointer turn around %schecked\n", i ? "" : "not ");
#ifdef WANT_PCC
i = 1;
#else
i = 0;
#endif
printf("Programcounter turn around %schecked\n", i ? "" : "not ");
#ifdef WANT_TIM
i = 1;
#else
i = 0;
#endif
printf("T-State counting %spossible\n", i ? "" : "im");
#ifdef CNTL_C
i = 1;
#else
i = 0;
#endif
printf("CPU simulation %sstopped on cntl-c\n", i ? "" : "not ");
#ifdef CNTL_BS
i = 1;
#else
i = 0;
#endif
printf("CPU simulation %sstopped on cntl-\\\n", i ? "" : "not ");
}
/*
* Read a file into the memory of the emulated CPU.
* The following file formats are supported:
*
* binary images with Mostek header
*/
static do_getfile(s)
register char *s;
{
char fn[LENCMD];
BYTE fileb[5];
register char *pfn = fn;
int fd;
long lseek();