summaryrefslogtreecommitdiff
path: root/gemma.c
diff options
context:
space:
mode:
Diffstat (limited to 'gemma.c')
-rw-r--r--gemma.c837
1 files changed, 837 insertions, 0 deletions
diff --git a/gemma.c b/gemma.c
new file mode 100644
index 0000000..015362b
--- /dev/null
+++ b/gemma.c
@@ -0,0 +1,837 @@
+/*
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+
+#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,"<span foreground=\"white\" background=\"black\">");
+ }
+
+ if (flag&b)
+ {
+ DSAdd(s,c[f]);
+ }
+ else
+ {
+ DSAdd(s,"-");
+ }
+
+ if ((last&b)!=(flag&b))
+ {
+ DSAdd(s,"</span>");
+ }
+ }
+}
+
+
+static void DisplayState(DString s, const char *label,
+ int width, Z80Val val, Z80Val last)
+{
+ static char buff[128];
+
+ sprintf(buff,"%s: %s%*.*lX</span>",
+ label,
+ (last==val) ?
+ "<span>" :
+ "<span foreground=\"white\" background=\"black\">",
+ 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,&reg);
+ DSAdd(ds,"<tt>");
+
+ for(f=0;f<20;f++)
+ {
+ if (f==0)
+ {
+ DSAdd(ds,"<span foreground=\"white\" background=\"black\">");
+ }
+
+ DSAdd(ds,Z80Disassemble(z80,&reg.PC));
+
+ if (f==0)
+ {
+ DSAdd(ds,"</span>");
+ }
+
+ DSAddChar(ds,'\n');
+ }
+
+ DSAdd(ds,"</tt>");
+ 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,&reg);
+
+ if (!reg_once)
+ {
+ last_reg=reg;
+ }
+
+ DSAdd(ds,"<tt>");
+
+ 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,"</tt>");
+
+ 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("<EOF>\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<len;f++)
+ {
+ Z80Byte b;
+
+ b=ToHex(buff[f*2+9])<<4|ToHex(buff[f*2+10]);
+ mem[addr++]=b;
+ }
+ }
+ }
+
+ fclose(fp);
+
+ GEMMA_UpdateDisplay(UPDATE_ALL_VIEWS);
+}
+
+
+void GEMMA_LoadLabels(const char *path)
+{
+ FILE *fp;
+ int f;
+ char buff[512];
+
+ if (!(fp=fopen(path,"r")))
+ {
+ Log("Failed to open %s\n",path);
+ 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);
+}
+
+
+void GEMMA_Step(void)
+{
+ Z80SingleStep(z80);
+ GEMMA_UpdateDisplay(UPDATE_ALL_VIEWS);
+}
+
+
+void GEMMA_StepOver(void)
+{
+ Z80Word next;
+ Z80State reg;
+
+ stop=FALSE;
+
+ Z80GetState(z80,&reg);
+ next=reg.PC;
+ Z80Disassemble(z80,&next);
+
+ RUNNING(TRUE);
+
+ while(reg.PC!=next && !stop)
+ {
+ GEMMA_Step();
+ Z80GetState(z80,&reg);
+ gtk_main_iteration_do(FALSE);
+ }
+
+ RUNNING(FALSE);
+}
+
+
+void GEMMA_Run(void)
+{
+ stop=FALSE;
+
+ RUNNING(TRUE);
+
+ while(!stop)
+ {
+ GEMMA_Step();
+ gtk_main_iteration_do(FALSE);
+ }
+
+ RUNNING(FALSE);
+}
+
+
+void GEMMA_RunUntil(const char *expr)
+{
+ stop=FALSE;
+
+ RUNNING(TRUE);
+
+ while(!stop)
+ {
+ long e;
+
+ if (!ExprEval(expr,&e,Expand,z80))
+ {
+ Log("%s\n",ExprError());
+ stop=TRUE;
+ continue;
+ }
+
+ if (e)
+ {
+ Log("%s TRUE\n",expr);
+ stop=TRUE;
+ continue;
+ }
+
+ GEMMA_Step();
+ gtk_main_iteration_do(FALSE);
+ }
+
+ RUNNING(FALSE);
+}
+
+
+void GEMMA_Stop(void)
+{
+ stop=TRUE;
+}
+
+
+void GEMMA_Init(GtkWidget *top)
+{
+ int f;
+
+ for(f=0;f<256;f++) memctl[f]=1;
+
+ z80=Z80Init(mem,memctl,WritePort,ReadPort);
+
+ 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");
+}
+
+
+void GEMMA_Interrupt(int nmi)
+{
+ if (nmi)
+ {
+ Z80NMI(z80);
+ }
+ else
+ {
+ Z80Interrupt(z80,0);
+ }
+}
+
+
+/* END OF FILE */