1021 lines
23 KiB
C
1021 lines
23 KiB
C
/*
|
|
* Z80SIM - a Z80-CPU simulator
|
|
*
|
|
* Copyright (C) 1987-2006 by Udo Munk
|
|
*
|
|
* This modul contains the user interface, a full qualified ICE,
|
|
* for the Z80-CPU simulation.
|
|
*
|
|
* 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
|
|
* 02-OCT-06 Release 1.8 modified to compile on modern POSIX OS's
|
|
*/
|
|
|
|
/*
|
|
* This modul is an ICE type user interface to debug Z80 programs
|
|
* on a host system.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
#include <memory.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include "sim.h"
|
|
#include "simglb.h"
|
|
|
|
extern void cpu(void);
|
|
extern void disass(unsigned char **, int);
|
|
extern int exatoi(char *);
|
|
extern int getkey(void);
|
|
extern void int_on(void), int_off(void);
|
|
|
|
static void do_step(void);
|
|
static void do_trace(char *);
|
|
static void do_go(char *);
|
|
static int handel_break(void);
|
|
static void do_dump(char *);
|
|
static void do_list(char *);
|
|
static void do_modify(char *);
|
|
static void do_fill(char *);
|
|
static void do_move(char *);
|
|
static void do_port(char *);
|
|
static void do_reg(char *);
|
|
static void print_head(void);
|
|
static void print_reg(void);
|
|
static void do_break(char *);
|
|
static void do_hist(char *);
|
|
static void do_count(char *);
|
|
static void do_clock(void);
|
|
static void timeout(int);
|
|
static void do_show(void);
|
|
static int do_getfile(char *);
|
|
static int load_mos(int, char *);
|
|
static void do_unix(char *);
|
|
static void do_help(void);
|
|
static void cpu_err_msg(void);
|
|
|
|
struct termios old_term;
|
|
|
|
/*
|
|
* The function "mon()" is the dialog user interface, called
|
|
* from the simulation just after program start.
|
|
*/
|
|
void mon(void)
|
|
{
|
|
register int eoj = 1;
|
|
static char cmd[LENCMD];
|
|
|
|
tcgetattr(0, &old_term);
|
|
|
|
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;
|
|
case 'l':
|
|
do_list(cmd + 1);
|
|
break;
|
|
case 'm':
|
|
do_modify(cmd + 1);
|
|
break;
|
|
case 'f':
|
|
do_fill(cmd + 1);
|
|
break;
|
|
case 'v':
|
|
do_move(cmd + 1);
|
|
break;
|
|
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;
|
|
case 'c':
|
|
do_clock();
|
|
break;
|
|
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 void do_step(void)
|
|
{
|
|
BYTE *p;
|
|
|
|
cpu_state = SINGLE_STEP;
|
|
cpu_error = NONE;
|
|
cpu();
|
|
if (cpu_error == OPHALT)
|
|
handel_break();
|
|
cpu_err_msg();
|
|
print_head();
|
|
print_reg();
|
|
p = PC;
|
|
disass(&p, p - ram);
|
|
}
|
|
|
|
/*
|
|
* Execute several steps with trace output
|
|
*/
|
|
static void do_trace(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 void do_go(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):
|
|
*
|
|
* Output: 0 breakpoint or other HALT opcode reached (stop)
|
|
* 1 breakpoint reached, passcounter not reached (continue)
|
|
*/
|
|
static int handel_break(void)
|
|
{
|
|
#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 void do_dump(char *s)
|
|
{
|
|
register int i, j;
|
|
BYTE c;
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
if (isxdigit(*s))
|
|
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 - ", (unsigned int)(wrk_ram - ram));
|
|
for (j = 0; j < 16; j++) {
|
|
printf("%02x ", *wrk_ram);
|
|
wrk_ram++;
|
|
if (wrk_ram > ram + 65535)
|
|
wrk_ram = ram;
|
|
}
|
|
putchar('\t');
|
|
for (j = -16; j < 0; j++)
|
|
printf("%c",((c = *(wrk_ram+j))>=' ' && c<=0x7f) ? c : '.');
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Disassemble
|
|
*/
|
|
static void do_list(char *s)
|
|
{
|
|
register int i;
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
if (isxdigit(*s))
|
|
wrk_ram = ram + exatoi(s);
|
|
for (i = 0; i < 10; i++) {
|
|
printf("%04x - ", (unsigned int)(wrk_ram - ram));
|
|
disass(&wrk_ram, wrk_ram - ram);
|
|
if (wrk_ram > ram + 65535)
|
|
wrk_ram = ram;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Memory modify
|
|
*/
|
|
static void do_modify(char *s)
|
|
{
|
|
static char nv[LENCMD];
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
if (isxdigit(*s))
|
|
wrk_ram = ram + exatoi(s);
|
|
for (;;) {
|
|
printf("%04x = %02x : ", (unsigned int)(wrk_ram - ram),
|
|
*wrk_ram);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
if (nv[0] == '\n') {
|
|
wrk_ram++;
|
|
if (wrk_ram > ram + 65535)
|
|
wrk_ram = ram;
|
|
continue;
|
|
}
|
|
if (!isxdigit(nv[0]))
|
|
break;
|
|
*wrk_ram++ = exatoi(nv);
|
|
if (wrk_ram > ram + 65535)
|
|
wrk_ram = ram;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Memory fill
|
|
*/
|
|
static void do_fill(char *s)
|
|
{
|
|
register BYTE *p;
|
|
register int i;
|
|
register BYTE val;
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
p = ram + exatoi(s);
|
|
while (*s != ',' && *s != '\0')
|
|
s++;
|
|
if (*s) {
|
|
i = exatoi(++s);
|
|
} else {
|
|
puts("count missing");
|
|
return;
|
|
}
|
|
while (*s != ',' && *s != '\0')
|
|
s++;
|
|
if (*s) {
|
|
val = exatoi(++s);
|
|
} else {
|
|
puts("value missing");
|
|
return;
|
|
}
|
|
while (i--) {
|
|
*p++ = val;
|
|
if (p > ram + 65535)
|
|
p = ram;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Memory move
|
|
*/
|
|
static void do_move(char *s)
|
|
{
|
|
register BYTE *p1, *p2;
|
|
register int count;
|
|
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
p1 = ram + exatoi(s);
|
|
while (*s != ',' && *s != '\0')
|
|
s++;
|
|
if (*s) {
|
|
p2 = ram + exatoi(++s);
|
|
} else {
|
|
puts("to missing");
|
|
return;
|
|
}
|
|
while (*s != ',' && *s != '\0')
|
|
s++;
|
|
if (*s) {
|
|
count = exatoi(++s);
|
|
} else {
|
|
puts("count missing");
|
|
return;
|
|
}
|
|
while (count--) {
|
|
*p2++ = *p1++;
|
|
if (p1 > ram + 65535)
|
|
p1 = ram;
|
|
if (p2 > ram + 65535)
|
|
p2 = ram;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Port modify
|
|
*/
|
|
static void do_port(char *s)
|
|
{
|
|
register BYTE port;
|
|
static char nv[LENCMD];
|
|
extern BYTE io_out(), io_in();
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
port = exatoi(s);
|
|
printf("%02x = %02x : ", port, io_in(port));
|
|
fgets(nv, sizeof(nv), stdin);
|
|
if (isxdigit(*nv))
|
|
io_out(port, (BYTE) exatoi(nv));
|
|
}
|
|
|
|
/*
|
|
* Register modify
|
|
*/
|
|
static void do_reg(char *s)
|
|
{
|
|
static char nv[LENCMD];
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
if (*s == '\0') {
|
|
print_head();
|
|
print_reg();
|
|
} else {
|
|
if (strncmp(s, "bc'", 3) == 0) {
|
|
printf("BC' = %04x : ", B_ * 256 + C_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
B_ = (exatoi(nv) & 0xffff) / 256;
|
|
C_ = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "de'", 3) == 0) {
|
|
printf("DE' = %04x : ", D_ * 256 + E_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
D_ = (exatoi(nv) & 0xffff) / 256;
|
|
E_ = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "hl'", 3) == 0) {
|
|
printf("HL' = %04x : ", H_ * 256 + L_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
H_ = (exatoi(nv) & 0xffff) / 256;
|
|
L_ = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "pc", 2) == 0) {
|
|
printf("PC = %04x : ", (unsigned int)(PC - ram));
|
|
fgets(nv, sizeof(nv), stdin);
|
|
PC = ram + (exatoi(nv) & 0xffff);
|
|
} else if (strncmp(s, "bc", 2) == 0) {
|
|
printf("BC = %04x : ", B * 256 + C);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
B = (exatoi(nv) & 0xffff) / 256;
|
|
C = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "de", 2) == 0) {
|
|
printf("DE = %04x : ", D * 256 + E);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
D = (exatoi(nv) & 0xffff) / 256;
|
|
E = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "hl", 2) == 0) {
|
|
printf("HL = %04x : ", H * 256 + L);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
H = (exatoi(nv) & 0xffff) / 256;
|
|
L = (exatoi(nv) & 0xffff) % 256;
|
|
} else if (strncmp(s, "ix", 2) == 0) {
|
|
printf("IX = %04x : ", IX);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
IX = exatoi(nv) & 0xffff;
|
|
} else if (strncmp(s, "iy", 2) == 0) {
|
|
printf("IY = %04x : ", IY);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
IY = exatoi(nv) & 0xffff;
|
|
} else if (strncmp(s, "sp", 2) == 0) {
|
|
printf("SP = %04x : ", (unsigned int)(STACK - ram));
|
|
fgets(nv, sizeof(nv), stdin);
|
|
STACK = ram + (exatoi(nv) & 0xffff);
|
|
} else if (strncmp(s, "fs", 2) == 0) {
|
|
printf("S-FLAG = %c : ", (F & S_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | S_FLAG) : (F & ~S_FLAG);
|
|
} else if (strncmp(s, "fz", 2) == 0) {
|
|
printf("Z-FLAG = %c : ", (F & Z_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | Z_FLAG) : (F & ~Z_FLAG);
|
|
} else if (strncmp(s, "fh", 2) == 0) {
|
|
printf("H-FLAG = %c : ", (F & H_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | H_FLAG) : (F & ~H_FLAG);
|
|
} else if (strncmp(s, "fp", 2) == 0) {
|
|
printf("P-FLAG = %c : ", (F & P_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | P_FLAG) : (F & ~P_FLAG);
|
|
} else if (strncmp(s, "fn", 2) == 0) {
|
|
printf("N-FLAG = %c : ", (F & N_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | N_FLAG) : (F & ~N_FLAG);
|
|
} else if (strncmp(s, "fc", 2) == 0) {
|
|
printf("C-FLAG = %c : ", (F & C_FLAG) ? '1' : '0');
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = (exatoi(nv)) ? (F | C_FLAG) : (F & ~C_FLAG);
|
|
} else if (strncmp(s, "a'", 2) == 0) {
|
|
printf("A' = %02x : ", A_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
A_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "f'", 2) == 0) {
|
|
printf("F' = %02x : ", F_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "b'", 2) == 0) {
|
|
printf("B' = %02x : ", B_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
B_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "c'", 2) == 0) {
|
|
printf("C' = %02x : ", C_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
C_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "d'", 2) == 0) {
|
|
printf("D' = %02x : ", D_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
D_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "e'", 2) == 0) {
|
|
printf("E' = %02x : ", E_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
E_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "h'", 2) == 0) {
|
|
printf("H' = %02x : ", H_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
H_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "l'", 2) == 0) {
|
|
printf("L' = %02x : ", L_);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
L_ = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "i", 1) == 0) {
|
|
printf("I = %02x : ", I);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
I = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "a", 1) == 0) {
|
|
printf("A = %02x : ", A);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
A = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "f", 1) == 0) {
|
|
printf("F = %02x : ", F);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
F = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "b", 1) == 0) {
|
|
printf("B = %02x : ", B);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
B = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "c", 1) == 0) {
|
|
printf("C = %02x : ", C);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
C = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "d", 1) == 0) {
|
|
printf("D = %02x : ", D);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
D = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "e", 1) == 0) {
|
|
printf("E = %02x : ", E);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
E = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "h", 1) == 0) {
|
|
printf("H = %02x : ", H);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
H = exatoi(nv) & 0xff;
|
|
} else if (strncmp(s, "l", 1) == 0) {
|
|
printf("L = %02x : ", L);
|
|
fgets(nv, sizeof(nv), stdin);
|
|
L = exatoi(nv) & 0xff;
|
|
} else
|
|
printf("can't change register %s\n", nv);
|
|
print_head();
|
|
print_reg();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output header for the CPU registers
|
|
*/
|
|
static void print_head(void)
|
|
{
|
|
printf("\nPC A SZHPNC I IFF BC DE HL A'F' B'C' D'E' H'L' IX IY SP\n");
|
|
}
|
|
|
|
/*
|
|
* Output all CPU registers
|
|
*/
|
|
static void print_reg(void)
|
|
{
|
|
printf("%04x %02x ", (unsigned int)(PC - ram), A);
|
|
printf("%c", F & S_FLAG ? '1' : '0');
|
|
printf("%c", F & Z_FLAG ? '1' : '0');
|
|
printf("%c", F & H_FLAG ? '1' : '0');
|
|
printf("%c", F & P_FLAG ? '1' : '0');
|
|
printf("%c", F & N_FLAG ? '1' : '0');
|
|
printf("%c", F & C_FLAG ? '1' : '0');
|
|
printf(" %02x ", I);
|
|
printf("%c", IFF & 1 ? '1' : '0');
|
|
printf("%c", IFF & 2 ? '1' : '0');
|
|
printf(" %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %04x %04x %04x\n",
|
|
B, C, D, E, H, L, A_, F_, B_, C_, D_, E_, H_, L_, IX, IY,
|
|
(unsigned int)(STACK - ram));
|
|
}
|
|
|
|
/*
|
|
* Software breakpoints
|
|
*/
|
|
static void do_break(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 (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 void do_hist(char *s)
|
|
{
|
|
#ifndef HISIZE
|
|
puts("Sorry, no history available");
|
|
puts("Please recompile with HISIZE defined in sim.h");
|
|
#else
|
|
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
|
|
}
|
|
|
|
/*
|
|
* Runtime measurement by counting the executed T states
|
|
*/
|
|
static void do_count(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",
|
|
(unsigned int)(t_start - ram),
|
|
(unsigned int)(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
|
|
}
|
|
|
|
/*
|
|
* 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 void do_clock(void)
|
|
{
|
|
static BYTE save[3];
|
|
|
|
save[0] = *(ram + 0x0000); /* save memory locations */
|
|
save[1] = *(ram + 0x0001); /* 0000H - 0002H */
|
|
save[2] = *(ram + 0x0002);
|
|
*(ram + 0x0000) = 0xc3; /* store opcode JP 0000H at address */
|
|
*(ram + 0x0001) = 0x00; /* 0000H */
|
|
*(ram + 0x0002) = 0x00;
|
|
PC = ram + 0x0000; /* set PC to this code */
|
|
R = 0L; /* clear refresh register */
|
|
cpu_state = CONTIN_RUN; /* initialize CPU */
|
|
cpu_error = NONE;
|
|
signal(SIGALRM, timeout); /* initialize timer interrupt handler */
|
|
alarm(3); /* start 3 secound timer */
|
|
cpu(); /* start CPU */
|
|
*(ram + 0x0000) = save[0]; /* restore memory locations */
|
|
*(ram + 0x0001) = save[1]; /* 0000H - 0002H */
|
|
*(ram + 0x0002) = save[2];
|
|
if (cpu_error == NONE)
|
|
printf("clock frequency = %5.2f Mhz\n", ((float) R) / 300000.0);
|
|
else
|
|
puts("Interrupted by user");
|
|
}
|
|
|
|
/*
|
|
* This function is the signal handler for the timer interrupt.
|
|
* The CPU emulation is stopped here.
|
|
*/
|
|
static void timeout(int sig)
|
|
{
|
|
cpu_state = STOPPED;
|
|
}
|
|
|
|
/*
|
|
* Output informations about compiling options
|
|
*/
|
|
static void do_show(void)
|
|
{
|
|
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 int do_getfile(char *s)
|
|
{
|
|
char fn[LENCMD];
|
|
BYTE fileb[5];
|
|
register char *pfn = fn;
|
|
int fd;
|
|
|
|
while (isspace(*s))
|
|
s++;
|
|
while (*s != ',' && *s != '\n' && *s != '\0')
|
|
*pfn++ = *s++;
|
|
*pfn = '\0';
|
|
if (strlen(fn) == 0) {
|
|
puts("no input file given");
|
|
return(1);
|
|
}
|
|
if ((fd = open(fn, O_RDONLY)) == -1) {
|
|
printf("can't open file %s\n", fn);
|
|
return(1);
|
|
}
|
|
if (*s == ',')
|
|
wrk_ram = ram + exatoi(++s);
|
|
else
|
|
wrk_ram = NULL;
|
|
read(fd, (char *) fileb, 5); /* read first 5 bytes of file */
|
|
if (*fileb == (BYTE) 0xff) { /* Mostek header ? */
|
|
lseek(fd, 0l, 0);
|
|
return (load_mos(fd, fn));
|
|
}
|
|
else {
|
|
printf("unkown format, can't load file %s\n", fn);
|
|
close(fd);
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Loader for binary images with Mostek header.
|
|
* Format of the first 3 bytes:
|
|
*
|
|
* 0xff ll lh
|
|
*
|
|
* ll = load address low
|
|
* lh = load address high
|
|
*/
|
|
static int load_mos(int fd, char *fn)
|
|
{
|
|
BYTE fileb[3];
|
|
unsigned count, readed;
|
|
int rc = 0;
|
|
|
|
read(fd, (char *) fileb, 3); /* read load address */
|
|
if (wrk_ram == NULL) /* and set if not given */
|
|
wrk_ram = ram + (fileb[2] * 256 + fileb[1]);
|
|
count = ram + 65535 - wrk_ram;
|
|
if ((readed = read(fd, (char *) wrk_ram, count)) == count) {
|
|
puts("Too much to load, stopped at 0xffff");
|
|
rc = 1;
|
|
}
|
|
close(fd);
|
|
printf("Loader statistics for file %s:\n", fn);
|
|
printf("START : %04x\n", (unsigned int)(wrk_ram - ram));
|
|
printf("END : %04x\n", (unsigned int)(wrk_ram - ram + readed - 1));
|
|
printf("LOADED: %04x\n", readed);
|
|
PC = wrk_ram;
|
|
return(rc);
|
|
}
|
|
|
|
/*
|
|
* Call system function from simulator
|
|
*/
|
|
static void do_unix(char *s)
|
|
{
|
|
int_off();
|
|
system(s);
|
|
int_on();
|
|
}
|
|
|
|
/*
|
|
* Output help text
|
|
*/
|
|
static void do_help(void)
|
|
{
|
|
puts("r filename[,address] read object into memory");
|
|
puts("d [address] dump memory");
|
|
puts("l [address] list memory");
|
|
puts("m [address] modify memory");
|
|
puts("f address,count,value fill memory");
|
|
puts("v from,to,count move memory");
|
|
puts("p address show/modify port");
|
|
puts("g [address] run program");
|
|
puts("t [count] trace program");
|
|
puts("return single step program");
|
|
puts("x [register] show/modify register");
|
|
puts("x f<flag> modify flag");
|
|
puts("b[no] address[,pass] set soft breakpoint");
|
|
puts("b show soft breakpoints");
|
|
puts("b[no] c clear soft breakpoint");
|
|
puts("h [address] show history");
|
|
puts("h c clear history");
|
|
puts("z start,stop set trigger adr for t-state count");
|
|
puts("z show t-state count");
|
|
puts("c measure clock frequency");
|
|
puts("s show settings");
|
|
puts("! command execute UNIX command");
|
|
puts("q quit");
|
|
}
|
|
|
|
/*
|
|
* Error handler after CPU is stopped
|
|
*/
|
|
static void cpu_err_msg(void)
|
|
{
|
|
switch (cpu_error) {
|
|
case NONE:
|
|
break;
|
|
case OPHALT:
|
|
printf("HALT Op-Code reached at %04x\n",
|
|
(unsigned int)(PC - ram - 1));
|
|
break;
|
|
case IOTRAP:
|
|
printf("I/O Trap at %04x\n", (unsigned int)(PC - ram));
|
|
break;
|
|
case OPTRAP1:
|
|
printf("Op-code trap at %04x %02x\n",
|
|
(unsigned int)(PC - 1 - ram), *(PC-1));
|
|
break;
|
|
case OPTRAP2:
|
|
printf("Op-code trap at %04x %02x %02x\n",
|
|
(unsigned int)(PC - 2 - ram),
|
|
*(PC-2), *(PC-1));
|
|
break;
|
|
case OPTRAP4:
|
|
printf("Op-code trap at %04x %02x %02x %02x %02x\n",
|
|
(unsigned int)(PC - 4 - ram), *(PC-4), *(PC-3),
|
|
*(PC-2), *(PC-1));
|
|
break;
|
|
case USERINT:
|
|
puts("User Interrupt");
|
|
break;
|
|
default:
|
|
printf("Unknown error %d\n", cpu_error);
|
|
break;
|
|
}
|
|
}
|