/* 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 #ifdef ENABLE_READLINE #include #include #endif #include "z80.h" #include "expr.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)) /* ---------------------------------------- GLOBALS */ static Z80 *z80; Z80Byte Z80_MEMORY[0x10000]; static sig_atomic_t stop=FALSE; static int quit=FALSE; static int quiet_run=TRUE; 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); } /* ---------------------------------------- MEMORY */ #ifndef ENABLE_ARRAY_MEMORY static Z80Byte Peek(Z80 *cpu, Z80Word addr) { if (mem_trace) { Log("Read %2.2x from %4.4x\n",Z80_MEMORY[addr],addr); } return Z80_MEMORY[addr]; } static Z80Byte DisPeek(Z80 *cpu, Z80Word addr) { return Z80_MEMORY[addr]; } static void Poke(Z80 *cpu, Z80Word addr, Z80Byte b) { if (addr>=bottom && addr<=top) { if (mem_trace) { Log("Wrote %2.2x to %4.4x\n",b,addr); } Z80_MEMORY[addr]=b; } } #endif /* ---------------------------------------- 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) { Z80Word pc; pc=z80->PC; Log("A=%2.2x F=%s ", z80->AF.b.hi,FlagString(z80->AF.b.lo)); Log("BC=%4.4x DE=%4.4x HL=%4.4x ",z80->BC.w,z80->DE.w,z80->HL.w); Log("IX=%4.4x IY=%4.4x SP=%4.4x\n",z80->IX.w,z80->IY.w,z80->SP); Log("I=%2.2x IM=%2.2x R=%2.2x ",z80->I,z80->IM,z80->R); Log("IFF1=%2.2x IFF2=%2.2x CYCLES=%lu\n", z80->IFF1,z80->IFF2,Z80Cycles(z80)); Log("%4.4x: ",pc); Log("%s\n",Disassemble(&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(void *client, const char *p, long *res) { int ok=TRUE; if (StrEq(p,"AF")) *res=z80->AF.w; else if (StrEq(p,"BC")) *res=z80->BC.w; else if (StrEq(p,"DE")) *res=z80->DE.w; else if (StrEq(p,"HL")) *res=z80->HL.w; else if (StrEq(p,"IX")) *res=z80->IX.w; else if (StrEq(p,"IY")) *res=z80->IY.w; else if (StrEq(p,"SP")) *res=z80->SP; else if (StrEq(p,"PC")) *res=z80->PC; else if (StrEq(p,"A")) *res=HI(z80->AF.w); else if (StrEq(p,"F")) *res=LO(z80->AF.w); else if (StrEq(p,"B")) *res=HI(z80->BC.w); else if (StrEq(p,"C")) *res=LO(z80->BC.w); else if (StrEq(p,"D")) *res=HI(z80->DE.w); else if (StrEq(p,"E")) *res=LO(z80->DE.w); else if (StrEq(p,"H")) *res=HI(z80->HL.w); else if (StrEq(p,"L")) *res=LO(z80->HL.w); else if (StrEq(p,"AF_")) *res=z80->AF_; else if (StrEq(p,"BC_")) *res=z80->BC_; else if (StrEq(p,"DE_")) *res=z80->DE_; else if (StrEq(p,"HL_")) *res=z80->HL_; else if (StrEq(p,"A_")) *res=HI(z80->AF_); else if (StrEq(p,"F_")) *res=LO(z80->AF_); else if (StrEq(p,"B_")) *res=HI(z80->BC_); else if (StrEq(p,"C_")) *res=LO(z80->BC_); else if (StrEq(p,"D_")) *res=HI(z80->DE_); else if (StrEq(p,"E_")) *res=LO(z80->DE_); else if (StrEq(p,"H_")) *res=HI(z80->HL_); else if (StrEq(p,"L_")) *res=LO(z80->HL_); else if (StrEq(p,"IM")) *res=z80->IM; else if (StrEq(p,"R")) *res=z80->R; else if (StrEq(p,"IFF1")) *res=z80->IFF1; else if (StrEq(p,"IFF2")) *res=z80->IFF2; else if (p[0]=='@') { Z80Word n; n=Address(p+1); *res=Z80_MEMORY[n]; } else if (p[0]=='#') { Z80Word n; n=Address(p+1); *res=MK(Z80_MEMORY[n+1],Z80_MEMORY[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 (!ExprEval(p,&e,Expand,z80)) Log("%s\n",ExprError()); return (Z80Word)e; } static const char *Disassemble(Z80Word *addr) { return Z80Disassemble(z80,addr); } static Z80Val Val(const char *p) { long e=0; if (!ExprEval(p,&e,Expand,z80)) Log("%s\n",ExprError()); 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) { Z80_MEMORY[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",Z80_MEMORY[addr]); if (isprint(Z80_MEMORY[addr])) asc[count%8]=Z80_MEMORY[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) { for(f=0;label && label[f].label;f++) printf("%-20.20s = %8.8x\n",label[f].label,label[f].address); 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; Z80SetLabels(label); fclose(fp); } static void DoStep(int no, const char *arg[]) { DisplayState(); Z80SingleStep(z80); stop=FALSE; } static void DoSetPC(int no, const char *arg[]) { if (no<2) { z80->PC=MK(Z80_MEMORY[z80->SP+1],Z80_MEMORY[z80->SP]); z80->SP+=2; DisplayState(); } else z80->PC=Address(arg[1]); } static void DoRun(int no, const char *arg[]) { if (no>1) { z80->PC=Address(arg[1]); } 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[]) { if (no<2) { Log("Missing arguments\n"); return; } if (no>2) { z80->PC=Address(arg[2]); } 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; } 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[]) { if (no<3) { Log("Missing arguments\n"); return; } bottom=(Address(arg[1]))/256; top=Address(arg[2])/256; 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] && 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; char expr[1024]; char *p; switch(addr&0xff) { case 0x80: ptr=MK(Z80_MEMORY[1],Z80_MEMORY[0]); p=expr; while(Z80_MEMORY[ptr]) *p++=Z80_MEMORY[ptr++]; *p=0; b=(Z80Byte)Address(expr); /* Log("%s -> %u\n",expr,b); */ break; case 0x81: ptr=z80->DE.w; p=expr; while(Z80_MEMORY[ptr]) *p++=Z80_MEMORY[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) { Z80Word de; de=z80->DE.w; switch(addr&0xff) { case 0x80: Log("%c",val); fflush(stdout); break; case 0x81: Log("Stop requested\n"); stop=TRUE; break; case 0x82: while(Z80_MEMORY[de]!='$') { if (isspace(Z80_MEMORY[de]) || isprint(Z80_MEMORY[de])) Log("%c",Z80_MEMORY[de]); de++; } fflush(stdout); break; case 0x83: while(Z80_MEMORY[de]) { Log("%c",Z80_MEMORY[de]); 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"}; #ifndef ENABLE_ARRAY_MEMORY z80=Z80Init(Peek,Poke,ReadPort,WritePort,DisPeek); #else z80=Z80Init(ReadPort,WritePort); #endif 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) { #ifdef ENABLE_READLINE char *line; if ((line=readline("EMMA> "))) { Run(line); if (line[0]) { add_history(line); } free(line); } else { quit=TRUE; continue; } #else char buff[1024]; Log("EMMA> "); fflush(stdout); if (!fgets(buff,sizeof buff,stdin)) { quit=TRUE; continue; } Run(buff); #endif } return EXIT_SUCCESS; } /* END OF FILE */