/* EMMA - Z80 testbed Copyright (C) 2003 Ian Cowburn (ianc@noddybox.demon.co.uk) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- */ static const char id[]="$Id$"; #include #include #include #include #include #include #include "z80.h" #define TRUE 1 #define FALSE 0 #define HI(w) (((w)&0xff00)>>8) #define LO(w) ((w)&0xff) #define MK(h,l) (((Z80Word)(h))<<8|(l)) typedef struct { Z80Word address; const char *label; } Z80Label; /* ---------------------------------------- GLOBALS */ static Z80 *z80; static Z80Byte mem[0x10000]; static int memctl[256]; static sig_atomic_t stop=FALSE; static int quit=FALSE; static int quiet_run=FALSE; static int single_step=FALSE; static int mem_trace=FALSE; static Z80Word bottom=0; static Z80Word top=0xffff; static Z80Val int_raise=0; static Z80Label *label=NULL; static FILE *log=NULL; /* ---------------------------------------- PROTOS */ static void Log(const char *format, ...); static void Run(char *p); static Z80Word Address(const char *p); static const char *GetLabel(Z80 *z80, Z80Word addr); static const char *Disassemble(Z80Word *addr); /* ---------------------------------------- SIGNALS */ static void SigInt(int sig) { stop=TRUE; signal(SIGINT,SigInt); } /* ---------------------------------------- COMMANDS AND ASSOC UTILS */ static void Log(const char *format, ...) { static char buff[4096]; va_list va; va_start(va,format); vsprintf(buff,format,va); printf("%s",buff); if (log) fprintf(log,"%s",buff); va_end(va); } static void *Malloc(size_t len) { void *n; n=malloc(len); if (!n) { fprintf(stderr,"malloc failed\n"); exit(EXIT_FAILURE); } return n; } static void *Realloc(void *p, size_t len) { void *n; n=realloc(p,len); if (!n) { fprintf(stderr,"realloc failed\n"); exit(EXIT_FAILURE); } return n; } static char *StrCopy(const char *p) { return strcpy(Malloc(strlen(p)+1),p); } static const int ToHex(char c) { c=toupper(c); if (c>='0' && c<='9') return c-'0'; if (c>='A' && c<='F') return c-'A'+10; return 0; } static const char *FlagString(Z80Byte flag) { static char s[]="76543210"; static char c[]="SZ5H3PNC"; int f; for(f=0;f<8;f++) if (flag&(1<<(7-f))) s[f]=c[f]; else s[f]='-'; return s; } static void DisplayState(void) { Z80State s; Z80GetState(z80,&s); Log("A=%2.2x F=%s ",s.AF>>8,FlagString(s.AF&0xff)); Log("BC=%4.4x DE=%4.4x HL=%4.4x ",s.BC,s.DE,s.HL); Log("IX=%4.4x IY=%4.4x SP=%4.4x\n",s.IX,s.IY,s.SP); Log("I=%2.2x IM=%2.2x R=%2.2x ",s.I,s.IM,s.R); Log("IFF1=%2.2x IFF2=%2.2x\n",s.IFF1,s.IFF2); Log("%4.4x: ",s.PC); Log("%s\n",Disassemble(&s.PC)); } static int StrEq(const char *a, const char *b) { while(*a && *b && tolower(*a)==tolower(*b)) { a++; b++; } if (*a || *b) return FALSE; else return TRUE; } static int Expand(const char *p, long *res) { Z80State s; int ok=TRUE; Z80GetState(z80,&s); if (StrEq(p,"AF")) *res=s.AF; else if (StrEq(p,"BC")) *res=s.BC; else if (StrEq(p,"DE")) *res=s.DE; else if (StrEq(p,"HL")) *res=s.HL; else if (StrEq(p,"IX")) *res=s.IX; else if (StrEq(p,"IY")) *res=s.IY; else if (StrEq(p,"SP")) *res=s.SP; else if (StrEq(p,"PC")) *res=s.PC; else if (StrEq(p,"A")) *res=HI(s.AF); else if (StrEq(p,"F")) *res=LO(s.AF); else if (StrEq(p,"B")) *res=HI(s.BC); else if (StrEq(p,"C")) *res=LO(s.BC); else if (StrEq(p,"D")) *res=HI(s.DE); else if (StrEq(p,"E")) *res=LO(s.DE); else if (StrEq(p,"H")) *res=HI(s.HL); else if (StrEq(p,"L")) *res=LO(s.HL); else if (StrEq(p,"AF_")) *res=s.AF_; else if (StrEq(p,"BC_")) *res=s.BC_; else if (StrEq(p,"DE_")) *res=s.DE_; else if (StrEq(p,"HL_")) *res=s.HL_; else if (StrEq(p,"A_")) *res=HI(s.AF_); else if (StrEq(p,"F_")) *res=LO(s.AF_); else if (StrEq(p,"B_")) *res=HI(s.BC_); else if (StrEq(p,"C_")) *res=LO(s.BC_); else if (StrEq(p,"D_")) *res=HI(s.DE_); else if (StrEq(p,"E_")) *res=LO(s.DE_); else if (StrEq(p,"H_")) *res=HI(s.HL_); else if (StrEq(p,"L_")) *res=LO(s.HL_); else if (StrEq(p,"IM")) *res=s.IM; else if (StrEq(p,"R")) *res=s.R; else if (StrEq(p,"IFF1")) *res=s.IFF1; else if (StrEq(p,"IFF2")) *res=s.IFF2; else if (p[0]=='@') { Z80Word n; n=Address(p+1); *res=mem[n]; } else if (p[0]=='#') { Z80Word n; n=Address(p+1); *res=MK(mem[n+1],mem[n]); } else /* Check for labels */ { int f; ok=FALSE; for(f=0;label && label[f].label;f++) { if (StrEq(p,label[f].label)) { *res=label[f].address; ok=TRUE; break; } } } return ok; } static Z80Word Address(const char *p) { long e=0; if (!Expand(p,&e)) { e=strtol(p,NULL,0); } return (Z80Word)e; } static const char *Disassemble(Z80Word *addr) { return Z80Disassemble(z80,addr); } static Z80Val Val(const char *p) { long e=0; if (!Expand(p,&e)) { e=strtol(p,NULL,0); } return (Z80Val)e; } static void DoHelp(int no, const char *arg[]) { Log("? - help\n"); Log("RETURN - Repeat last command if was d, x or n\n"); Log("!cmd - run shell cmd\n"); Log(". file - run commands from file\n"); Log("> [file] - Set logging file (no argument to close)\n"); Log("l file addr - load file at address\n"); Log("i file - load intel HEX format file\n"); Log("d [addr [len]] - disassemble from addr for len bytes\n"); Log("x [addr [len]] - dump from addr for len bytes\n"); Log("D file - set disassembly labels\n"); Log("p [addr] - Set PC to addr [pop PC from stack]\n"); Log("n - Single step from current PC\n"); Log("r [addr] - Run from addr [current PC]\n"); Log("u expr [addr] - Run from addr [current PC] until expr is !0\n"); Log("z - Show Z80 state\n"); Log("s - Toggle silent running\n"); Log("S - Toggle single step\n"); Log("w bottom top - Set range of writable memory\n"); Log("I T-state - Raise an interrupt every T-state\n"); Log("N - Raise an NMI\n"); Log("T - Toggle memory tracing\n"); Log("q - quit\n"); } static void DoScript(int no, const char *arg[]) { FILE *fp; char buff[1024]; if (no<2) { Log("Missing arguments\n"); return; } if (!(fp=fopen(arg[1],"r"))) { Log("Failed to open %s\n",arg[1]); return; } while(fgets(buff,sizeof buff,fp)) { Log("EMMA> %s",buff); if (buff[0] && buff[0]!='\n') Run(buff); } fclose(fp); } static void DoLog(int no, const char *arg[]) { if (no<2) { if (log) fclose(log); log=NULL; return; } if (!(log=fopen(arg[1],"w"))) { Log("Failed to open %s\n",arg[1]); return; } else setbuf(log,NULL); } static void DoLoad(int no, const char *arg[]) { FILE *fp; Z80Word addr; int c; int total; if (no<3) { Log("Missing arguments\n"); return; } addr=Address(arg[2]); if (!(fp=fopen(arg[1],"rb"))) { Log("Failed to open %s\n",arg[1]); return; } total=0; while((c=getc(fp))!=EOF) { mem[addr++]=(Z80Byte)c; total++; } Log("Read 0x%4.4x bytes from %s\n",total,arg[1]); fclose(fp); } static void DoIntel(int no, const char *arg[]) { FILE *fp; char buff[1024]; int done=FALSE; if (no<2) { Log("Missing arguments\n"); return; } if (!(fp=fopen(arg[1],"r"))) { Log("Failed to open %s\n",arg[1]); return; } while(!done) { if (!fgets(buff,sizeof buff,fp)) { Log("Missing EOF record\n"); done=TRUE; } if (!done && buff[0]!=':') { Log("Invalid Intel HEX file\n"); done=TRUE; } if (!done && buff[8]=='1') { Log("\n"); done=TRUE; } if (!done) { Z80Word addr; int len; int f; len=ToHex(buff[1])<<4|ToHex(buff[2]); addr=ToHex(buff[3])<<12|ToHex(buff[4])<<8| ToHex(buff[5])<<4|ToHex(buff[6]); Log("Segment of 0x%2.2x bytes at address 0x%4.4x\n",len,addr); for(f=0;f0) { if ((count%8)==0) Log("0x%4.4x: ",addr); Log(" 0x%2.2x",mem[addr]); if (isprint(mem[addr])) asc[count%8]=mem[addr]; else asc[count%8]='.'; asc[(count%8)+1]=0; addr++; count++; if ((count%8)==0) Log(" %s\n",asc); len--; } if (count%8) { while(count%8) { Log(" "); count++; } Log(" %s\n",asc); } last=addr; } static void DoLabel(int no, const char *arg[]) { FILE *fp; char buff[1024]; int f; if (no<2) { Log("Missing arguments\n"); return; } if (!(fp=fopen(arg[1],"r"))) { Log("Failed to open %s\n",arg[1]); return; } for(f=0;label && label[f].label;f++) free((void *)label[f].label); free(label); label=NULL; f=0; while(fgets(buff,sizeof buff,fp)) { char *name=NULL; char *addr=NULL; if (strlen(buff) && buff[strlen(buff)-1]=='\n') buff[strlen(buff)-1]=0; name=strtok(buff," \t"); if (name) addr=strtok(NULL," \t"); if (name && addr) { label=Realloc(label,sizeof(*label)*(f+2)); label[f].label=StrCopy(name); label[f].address=Address(addr); f++; } } if (label) label[f].label=NULL; fclose(fp); } static void DoStep(int no, const char *arg[]) { DisplayState(); Z80SingleStep(z80); stop=FALSE; } static void DoSetPC(int no, const char *arg[]) { Z80State s; Z80GetState(z80,&s); if (no<2) { s.PC=MK(mem[s.SP+1],mem[s.SP]); s.SP+=2; DisplayState(); } else s.PC=Address(arg[1]); Z80SetState(z80,&s); } static void DoRun(int no, const char *arg[]) { if (no>1) { Z80State s; Z80GetState(z80,&s); s.PC=Address(arg[1]); Z80SetState(z80,&s); } while(!stop) { if (!quiet_run) DisplayState(); Z80SingleStep(z80); if (single_step) { Log(""); fflush(stdout); getchar(); } } stop=FALSE; } static void DoUntil(int no, const char *arg[]) { Log("No supported\n"); #if 0 if (no<2) { Log("Missing arguments\n"); return; } if (no>2) { Z80State s; Z80GetState(z80,&s); s.PC=Address(arg[2]); Z80SetState(z80,&s); } while(!stop) { long e; if (!ExprEval(arg[1],&e,Expand,z80)) Log("%s\n",ExprError()); if (e) { Log("%s TRUE\n",arg[1]); stop=TRUE; } else { if (!quiet_run) DisplayState(); Z80SingleStep(z80); } } stop=FALSE; #endif } static void DoShow(int no, const char *arg[]) { DisplayState(); } static void DoSilent(int no, const char *arg[]) { quiet_run=!quiet_run; if (quiet_run) Log("Quiet running set\n"); else Log("Noisy running set\n"); } static void DoRange(int no, const char *arg[]) { int f; if (no<3) { Log("Missing arguments\n"); return; } bottom=(Address(arg[1]))/256; top=Address(arg[2])/256; for(f=0;f<256;f++) { memctl[f]=(f>=bottom && f<=top); } Log("Writable memory between 0x%4.4x and 0x%4.4x\n",bottom*256,top*256); } static void DoSingle(int no, const char *arg[]) { single_step=!single_step; if (single_step) Log("Single step running set\n"); else Log("Continuous running set\n"); } static void DoIntRaise(int no, const char *arg[]) { if (no<1) { Log("Missing arguments\n"); return; } int_raise=Val(arg[1]); if (int_raise) Log("Interrupt every %lu T-states\n",int_raise); else Log("Interrupt disabled\n"); } static void DoNMIRaise(int no, const char *arg[]) { Z80NMI(z80); } static void DoMemTrace(int no, const char *arg[]) { mem_trace=!mem_trace; if (mem_trace) Log("Memory tracing on\n"); else Log("Memory tracing off\n"); } static void DoQuit(int no, const char *arg[]) { quit=TRUE; } static void Run(char *cmd) { static char last_cmd[1024]={0}; static const struct { const char *cmd; const int store; void (*handler)(int no, const char *arg[]); } cmd_table[]= { {"?", FALSE, DoHelp}, {".", FALSE, DoScript}, {">", FALSE, DoLog}, {"l", FALSE, DoLoad}, {"i", FALSE, DoIntel}, {"d", TRUE, DoDis}, {"x", TRUE, DoDump}, {"D", FALSE, DoLabel}, {"p", FALSE, DoSetPC}, {"n", TRUE, DoStep}, {"r", FALSE, DoRun}, {"u", FALSE, DoUntil}, {"z", FALSE, DoShow}, {"s", FALSE, DoSilent}, {"S", FALSE, DoSingle}, {"w", FALSE, DoRange}, {"I", FALSE, DoIntRaise}, {"N", FALSE, DoNMIRaise}, {"T", FALSE, DoMemTrace}, {"q", FALSE, DoQuit}, {NULL, FALSE, NULL} }; char buff[1024]; const char *arg[4]; int use_last=FALSE; int no; if (cmd[0]!='\n') { strcpy(buff,cmd); } else { strcpy(buff,last_cmd); use_last=TRUE; } if (buff[0]=='#') return; if (buff[0]=='!') system(buff+1); else { no=0; arg[0]=strtok(buff," \t\n"); while(no<3 && arg[no]) arg[++no]=strtok(NULL," \t\n"); if (arg[0]) { int f; int done=FALSE; for(f=0;!done && cmd_table[f].cmd;f++) if (cmd_table[f].cmd[0]==arg[0][0]) { if (cmd_table[f].store) { if (!use_last) strcpy(last_cmd,cmd); } else last_cmd[0]=0; done=TRUE; cmd_table[f].handler(no,arg); } if (!done) Log("Syntax error\n"); } } } /* ---------------------------------------- MEMORY */ static Z80Byte ReadPort(Z80 *z80, Z80Word addr) { Z80Byte b=0xff; Z80Word ptr; Z80State s; char expr[1024]; char *p; Z80GetState(z80,&s); switch(addr&0xff) { case 0x80: ptr=MK(mem[1],mem[0]); p=expr; while(mem[ptr]) *p++=mem[ptr++]; *p=0; b=(Z80Byte)Address(expr); /* Log("%s -> %u\n",expr,b); */ break; case 0x81: ptr=s.DE; p=expr; while(mem[ptr]) *p++=mem[ptr++]; *p=0; b=(Z80Byte)Address(expr); break; default: Log("Read from port 0x%4.4x\n",(int)addr); break; } return b; } static void WritePort(Z80 *z80, Z80Word addr, Z80Byte val) { Z80State s; Z80GetState(z80,&s); switch(addr&0xff) { case 0x80: putchar(val); fflush(stdout); break; case 0x81: Log("Stop requested\n"); stop=TRUE; break; case 0x82: while(mem[s.DE]!='$') { if (isspace(mem[s.DE]) || isprint(mem[s.DE])) putchar(mem[s.DE]); s.DE++; } fflush(stdout); break; case 0x83: while(mem[s.DE]) { putchar(mem[s.DE]); s.DE++; } fflush(stdout); break; case 0x84: DisplayState(); break; default: Log("Wrote 0x%2.2x to port 0x%4.4x\n",(int)val,(int)addr); break; } } static const char *GetLabel(Z80 *z80, Z80Word addr) { int f; for(f=0;label && label[f].label;f++) if (label[f].address==addr) return label[f].label; return NULL; } static int Halt(Z80 *z80, Z80Val v) { Log("Processor halted\n"); stop=TRUE; return TRUE; } static int IntCheck(Z80 *z80, Z80Val v) { if (int_raise && v>int_raise) { Log("Interrupt raised\n"); Z80Interrupt(z80,0xff); Z80ResetCycles(z80,v-int_raise); } return TRUE; } /* ---------------------------------------- MAIN */ int main(int argc, char *argv[]) { const char *autoarg[2]={".","auto"}; char buff[1024]; int f; for(f=0;f<256;f++) memctl[f]=1; z80=Z80Init(mem,memctl,WritePort,ReadPort); if (!z80) { printf("Failed to initialise Z80\n"); return EXIT_FAILURE; } Z80LodgeCallback(z80,eZ80_Halt,Halt); Z80LodgeCallback(z80,eZ80_Instruction,IntCheck); signal(SIGINT,SigInt); if (argc>1) { if (strcmp(argv[1],"-a")!=0) { autoarg[1]=argv[1]; DoScript(2,autoarg); } } else DoScript(2,autoarg); while(!quit) { Log("EMMA> "); fflush(stdout); if (!fgets(buff,sizeof buff,stdin)) { quit=TRUE; continue; } Run(buff); } return EXIT_SUCCESS; } /* END OF FILE */