/* GEMMA - Z80 testbed Copyright (C) 2006 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 "gemma.h" #include "expr.h" #include "gtkutil.h" #include "support.h" #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 Z80Label *label=NULL; static GtkWidget *run_button; static GtkWidget *step_button; static GtkWidget *step_over_button; static GtkWidget *stop_button; static GtkWidget *run_until_button; static GtkWidget *breakpoint_text; static GtkWidget *top_window; static GtkWidget *regview; static GtkWidget *memview; static GtkWidget *viewreg; static GtkWidget *logview; static GtkWidget *assemview; static GtkWidget *memview_combo; static GtkWidget *memview_check; #define RUNNING(t) gtk_widget_set_sensitive(run_button,!t); \ gtk_widget_set_sensitive(step_button,!t); \ gtk_widget_set_sensitive(step_over_button,!t); \ gtk_widget_set_sensitive(run_until_button,!t); \ gtk_widget_set_sensitive(stop_button,t); /* ---------------------------------------- PROTOS */ static void Log(const char *format, ...); static Z80Word Address(const char *p); static void *Malloc(size_t len); static void *Realloc(void *p, size_t len); /* ---------------------------------------- PC BUFFER */ #define PC_BUFFER 100 static Z80Word pc_buffer[PC_BUFFER]={0}; static int pc_head=0; static Z80Word Get_PC(int back) { return pc_buffer[(pc_head+back+1)%PC_BUFFER]; } static void Step(void) { pc_buffer[pc_head]=z80->PC; if (pc_head) pc_head--; else pc_head=PC_BUFFER-1; Z80SingleStep(z80); } /* ---------------------------------------- DYNAMIC STRINGS */ typedef struct { size_t len; char *text; } *DString; #define BLOCKSIZE 512 DString DSInit(void) { DString ds; ds=Malloc(sizeof *ds); ds->len=0; ds->text=Malloc(BLOCKSIZE); ds->text[0]=0; return ds; } void DSFree(DString ds) { if (ds) { free(ds->text); free(ds); } } DString DSAddChar(DString to, char c) { if (((to->len+2)%BLOCKSIZE)==0) { to->text=Realloc(to->text,(((to->len+2)/BLOCKSIZE)+1)*BLOCKSIZE); } to->text[to->len++]=c; to->text[to->len]=0; return to; } DString DSAdd(DString to, const char *from) { to->len+=strlen(from); to->text=Realloc(to->text,(((to->len+1)/BLOCKSIZE)+1)*BLOCKSIZE); strcat(to->text,from); return to; } /* ---------------------------------------- PRIVATE FUNCTIONS */ static void Log(const char *format, ...) { static char buff[4096]; va_list va; va_start(va,format); vsprintf(buff,format,va); va_end(va); AppendText(logview,buff); } 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 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 void FlagString(DString s, Z80Byte flag, Z80Byte last) { static char *c[]={"S","Z","5","H","3","P","N","C"}; int f; DSAdd(s,"Flags: "); for(f=0;f<8;f++) { int b=1<<(7-f); if ((last&b)!=(flag&b)) { DSAdd(s,""); } if (flag&b) { DSAdd(s,c[f]); } else { DSAdd(s,"-"); } if ((last&b)!=(flag&b)) { DSAdd(s,""); } } } static void DisplayState(DString s, const char *label, int width, Z80Val val, Z80Val last) { static char buff[128]; sprintf(buff,"%s: %s%*.*lX", label, (last==val) ? "" : "", width,width, val); DSAdd(s,buff); } /* ---------------------------------------- MEMORY */ #ifndef ENABLE_ARRAY_MEMORY static Z80Byte Peek(Z80 *z80, Z80Word addr) { return Z80_MEMORY[addr]; } static void Poke(Z80 *z80, Z80Word addr, Z80Byte b) { Z80_MEMORY[addr]=b; } #endif 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; default: Log("Wrote 0x%2.2x to port 0x%4.4x\n",(int)val,(int)addr); break; } } static int Halt(Z80 *z80, Z80Val v) { Log("**** Processor halted\n"); DialogOK("Processor halted\n"); stop=TRUE; return FALSE; } /* ---------------------------------------- EXPORTED FUNCTIONS */ void GEMMA_UpdateDisplay(GEMMA_View view) { if (view&UPDATE_ASSEM_VIEW) { Z80Word pc; DString ds; char buff[10]; int f; ds=DSInit(); DSAdd(ds,""); for(f=10;f>=0;f--) { pc=Get_PC(f); sprintf(buff,"%4.4X: ",pc); DSAdd(ds,buff); DSAdd(ds,Z80Disassemble(z80,&pc)); DSAddChar(ds,'\n'); } pc=z80->PC; for(f=0;f<12;f++) { if (f==0) { DSAdd(ds,""); } sprintf(buff,"%4.4X: ",pc); DSAdd(ds,buff); DSAdd(ds,Z80Disassemble(z80,&pc)); if (f==0) { DSAdd(ds,""); } DSAddChar(ds,'\n'); } DSAdd(ds,""); gtk_label_set_markup(GTK_LABEL(assemview),ds->text); DSFree(ds); } if (view&UPDATE_REG_VIEW) { static int reg_once=FALSE; static Z80 last_reg; DString ds; ds=DSInit(); if (!reg_once) { last_reg=*z80; reg_once=TRUE; } DSAdd(ds,""); DisplayState(ds,"A ",2,HI(z80->AF.w),HI(last_reg.AF.w)); DSAdd(ds," "); FlagString(ds,LO(z80->AF.w),LO(last_reg.AF.w)); DSAdd(ds,"\n"); DisplayState(ds,"BC ",4,z80->BC.w,last_reg.BC.w); DSAdd(ds," "); DisplayState(ds,"DE ",4,z80->DE.w,last_reg.DE.w); DSAdd(ds," "); DisplayState(ds,"HL ",4,z80->HL.w,last_reg.HL.w); DSAdd(ds,"\n"); DisplayState(ds,"SP ",4,z80->SP,last_reg.SP); DSAdd(ds," "); DisplayState(ds,"IX ",4,z80->IX.w,last_reg.IX.w); DSAdd(ds," "); DisplayState(ds,"IY ",4,z80->IY.w,last_reg.IY.w); DSAdd(ds,"\n\n"); DisplayState(ds,"A' ",2,HI(z80->AF_),HI(last_reg.AF_)); DSAdd(ds," "); FlagString(ds,LO(z80->AF_),LO(last_reg.AF_)); DSAdd(ds,"\n"); DisplayState(ds,"BC'",4,z80->BC_,last_reg.BC_); DSAdd(ds," "); DisplayState(ds,"DE'",4,z80->DE_,last_reg.DE_); DSAdd(ds," "); DisplayState(ds,"HL'",4,z80->HL_,last_reg.HL_); DSAdd(ds,"\n\n"); DisplayState(ds,"PC ",4,z80->PC,last_reg.PC); DSAdd(ds," "); DisplayState(ds,"CYCLE",8,Z80Cycles(z80),0); DSAdd(ds,"\n\n"); DisplayState(ds,"R ",2,z80->R,last_reg.R); DSAdd(ds," "); DisplayState(ds,"IFF1",1,z80->IFF1,last_reg.IFF1); DSAdd(ds," "); DisplayState(ds,"IFF2",1,z80->IFF2,last_reg.IFF2); DSAdd(ds,"\n"); DisplayState(ds,"IM ",1,z80->IM,last_reg.IM); DSAdd(ds," "); DisplayState(ds,"I",1,z80->I,last_reg.I); DSAdd(ds,"\n"); DSAdd(ds,""); gtk_label_set_markup(GTK_LABEL(regview),ds->text); DSFree(ds); last_reg=*z80; } if (view&UPDATE_MEM_VIEW) { Z80Word addr; Z80Word orig; DString ds; int f; gboolean as_words; ds=DSInit(); as_words=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(memview_check)); switch(gtk_combo_box_get_active(GTK_COMBO_BOX(memview_combo))) { case 0: addr=z80->HL.w; break; case 1: addr=z80->SP; as_words=TRUE; break; case 2: addr=z80->IX.w; break; case 3: addr=z80->IY.w; break; case 4: addr=z80->BC.w; break; case 5: addr=z80->DE.w; break; default: addr=z80->PC; break; } DSAdd(ds,""); orig=addr; addr-=6*8; for(f=0;f<13;f++) { DString asc; char buff[10]; Z80Word off; asc=DSInit(); sprintf(buff,"%4.4X: ",addr); DSAdd(ds,buff); for(off=0;off<8;off++) { gchar *p; p=g_markup_printf_escaped ("%c",isprint(Z80_MEMORY[addr+off]) ? Z80_MEMORY[addr+off] : '.'); DSAdd(asc,p); g_free(p); } for(off=0;off<8;off+=(as_words ? 2:1)) { Z80Word a; a=addr+off; if (a==orig) { DSAdd(ds,""); } if (as_words) { sprintf(buff,"%4.4X ",MK(Z80_MEMORY[a+1],Z80_MEMORY[a])); } else { sprintf(buff,"%2.2X ",Z80_MEMORY[a]); } DSAdd(ds,buff); if (a==orig) { DSAdd(ds,""); } } DSAdd(ds," "); DSAdd(ds,asc->text); DSAdd(ds,"\n"); DSFree(asc); addr+=8; } DSAdd(ds,""); gtk_label_set_markup(GTK_LABEL(memview),ds->text); DSFree(ds); } } void GEMMA_LoadHEX(const char *path) { FILE *fp; char buff[1024]; int done=FALSE; if (!(fp=fopen(path,"r"))) { Log("Failed to open %s\n",path); 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;fPC; Z80Disassemble(z80,&next); RUNNING(TRUE); while(z80->PC!=next && !stop) { Step(); gtk_main_iteration_do(FALSE); } GEMMA_UpdateDisplay(UPDATE_ALL_VIEWS); RUNNING(FALSE); } void GEMMA_Run(void) { stop=FALSE; RUNNING(TRUE); while(!stop) { GEMMA_Step(); gtk_main_iteration_do(FALSE); } RUNNING(FALSE); } void GEMMA_RunUntil(void) { char *expr; expr=StrCopy(gtk_entry_get_text(GTK_ENTRY(breakpoint_text))); stop=FALSE; RUNNING(TRUE); while(!stop) { long e; if (!ExprEval(expr,&e,Expand,z80)) { DialogOK("Error evaluating express:\n%s",ExprError()); stop=TRUE; continue; } if (e) { DialogOK("%s TRUE",expr); stop=TRUE; continue; } GEMMA_Step(); gtk_main_iteration_do(FALSE); } free(expr); RUNNING(FALSE); } void GEMMA_Stop(void) { stop=TRUE; } void GEMMA_Init(GtkWidget *top) { #ifdef ENABLE_ARRAY_MEMORY z80=Z80Init(ReadPort,WritePort); #else z80=Z80Init(Peek,Poke,ReadPort,WritePort,Peek); #endif z80->PC=0x100; if (!z80) { fprintf(stderr,"Failed to initialise Z80\n"); exit(EXIT_FAILURE); } Z80LodgeCallback(z80,eZ80_Halt,Halt); top_window=top; regview=lookup_widget(top_window,"register_view"); memview=lookup_widget(top_window,"memory_view"); viewreg=lookup_widget(top_window,"memory_view_choice"); logview=lookup_widget(top_window,"log_view"); assemview=lookup_widget(top_window,"assem_view"); memview_combo=lookup_widget(top_window,"memory_view_choice"); memview_check=lookup_widget(top_window,"viewmode_check"); breakpoint_text=lookup_widget(top_window,"breakpoint_text"); run_button=lookup_widget(top_window,"run_button"); step_button=lookup_widget(top_window,"step_button"); step_over_button=lookup_widget(top_window,"step_over_button"); stop_button=lookup_widget(top_window,"stop_button"); run_until_button=lookup_widget(top_window,"run_until_button"); gtk_combo_box_set_active(GTK_COMBO_BOX(memview_combo),0); } void GEMMA_Interrupt(int nmi) { if (nmi) { Z80NMI(z80); } else { Z80Interrupt(z80,0); } } /* END OF FILE */