/* 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; static Z80Byte mem[0x10000]; static int memctl[256]; 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 *top_window; static GtkWidget *regview; static GtkWidget *memview; static GtkWidget *viewreg; static GtkWidget *logview; static GtkWidget *assemview; #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); /* ---------------------------------------- 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) { 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 (!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 buff[32]; static char *c[]={"S","Z","5","H","3","P","N","C"}; int f; sprintf(buff,"%-10.10s: ","Flags"); DSAdd(s,buff); 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 */ 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; DString ds; ds=DSInit(); Z80GetState(z80,&s); switch(addr&0xff) { case 0x80: DSAddChar(ds,val); break; case 0x81: DSAdd(ds,"Stop requested via OUT(0x81)\n"); stop=TRUE; break; case 0x82: while(mem[s.DE]!='$') { if (isspace(mem[s.DE]) || isprint(mem[s.DE])) { DSAddChar(ds,mem[s.DE]); } s.DE++; } break; case 0x83: while(mem[s.DE]) { DSAddChar(ds,mem[s.DE]); s.DE++; } break; default: Log("Wrote 0x%2.2x to port 0x%4.4x\n",(int)val,(int)addr); return; } Log(ds->text); DSFree(ds); } 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) { Z80State reg; DString ds; int f; ds=DSInit(); Z80GetState(z80,®); DSAdd(ds,""); for(f=0;f<20;f++) { if (f==0) { DSAdd(ds,""); } 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 Z80State last_reg; Z80State reg; DString ds; ds=DSInit(); Z80GetState(z80,®); if (!reg_once) { last_reg=reg; } DSAdd(ds,""); DisplayState(ds,"A",2,HI(reg.AF),HI(last_reg.AF)); DSAdd(ds," "); FlagString(ds,LO(reg.AF),LO(last_reg.AF)); DSAdd(ds,"\n"); DisplayState(ds,"BC",4,reg.BC,last_reg.BC); DSAdd(ds," "); DisplayState(ds,"DE",4,reg.DE,last_reg.DE); DSAdd(ds," "); DisplayState(ds,"HL",4,reg.HL,last_reg.HL); DSAdd(ds,"\n"); DisplayState(ds,"SP",4,reg.SP,last_reg.SP); DSAdd(ds," "); DisplayState(ds,"IX",4,reg.IX,last_reg.IX); DSAdd(ds," "); DisplayState(ds,"IY",4,reg.IY,last_reg.IY); DSAdd(ds,"\n"); DisplayState(ds,"AF'",4,reg.AF_,last_reg.AF_); DSAdd(ds," "); DisplayState(ds,"BC'",4,reg.BC_,last_reg.BC_); DSAdd(ds," "); DisplayState(ds,"DE'",4,reg.DE_,last_reg.DE_); DSAdd(ds," "); DisplayState(ds,"HL'",4,reg.HL_,last_reg.HL_); DSAdd(ds,"\n"); DisplayState(ds,"PC",4,reg.PC,last_reg.PC); DSAdd(ds,"\n"); DisplayState(ds,"R",4,reg.R,last_reg.R); DSAdd(ds,"\n"); DisplayState(ds,"CYCLES",8,reg.cycle,last_reg.cycle); DSAdd(ds,"\n"); DSAdd(ds,""); gtk_label_set_markup(GTK_LABEL(regview),ds->text); DSFree(ds); last_reg=reg; } } 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;f