diff options
author | Ian C <ianc@noddybox.co.uk> | 2006-08-30 22:24:41 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2006-08-30 22:24:41 +0000 |
commit | c1d4cc4739583f906b639daf3bd9384dd791530e (patch) | |
tree | 27f471459ec61067210883ff21396d0bc14a7fdc /gemma.c | |
parent | cb4cd60cbce11439b0e0529df20e671e5b49b6f1 (diff) |
Development checkin
Diffstat (limited to 'gemma.c')
-rw-r--r-- | gemma.c | 837 |
1 files changed, 837 insertions, 0 deletions
@@ -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,®); + DSAdd(ds,"<tt>"); + + for(f=0;f<20;f++) + { + if (f==0) + { + DSAdd(ds,"<span foreground=\"white\" background=\"black\">"); + } + + DSAdd(ds,Z80Disassemble(z80,®.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,®); + + 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,®); + next=reg.PC; + Z80Disassemble(z80,&next); + + RUNNING(TRUE); + + while(reg.PC!=next && !stop) + { + GEMMA_Step(); + Z80GetState(z80,®); + 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 */ |