/* espec - Sinclair Spectrum emulator 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 ------------------------------------------------------------------------- Provides the memory menu */ static const char ident[]="$Id$"; #include #include #include #include #include "memmenu.h" #include "spec.h" #include "gfx.h" #include "gui.h" #include "util.h" #include "expr.h" #include static const char ident_h[]=ESPEC_MEMMENU_H; #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define WHITE GFXRGB(255,255,255) #define BLACK GFXRGB(0,0,0) #define RED GFXRGB(255,100,100) #define GREEN GFXRGB(100,255,100) #define BLUE GFXRGB(100,100,255) #define TRACE "trace" #define TRACEMEM_WIN 10 #define HI(w) (((w)&0xff00)>>8) #define LO(w) ((w)&0xff) /* ---------------------------------------- TYPES */ typedef struct { int no; int *active; char **expr; } Breakpoint; typedef struct { Z80Word addr; Z80Word val; } MemTrace; typedef struct { Z80State s; MemTrace hl[TRACEMEM_WIN]; MemTrace de[TRACEMEM_WIN]; MemTrace sp[TRACEMEM_WIN]; } Trace; /* ---------------------------------------- STATIC DATA */ static FILE *trace=NULL; static Breakpoint bpoint={0,NULL,NULL}; static const char *brk=NULL; static int lodged=FALSE; /* ---------------------------------------- PROTOS */ static int Instruction(Z80 *z80, Z80Val data); static int Address(Z80 *z80, const char *p, Z80Word *addr); static int Expand(void *client, const char *p, long *res); /* ---------------------------------------- PRIVATE FUNCTIONS */ 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 Address(Z80 *z80, const char *p, Z80Word *addr) { long e=0; if (ExprEval(p,&e,Expand,z80)) { *addr=e; return TRUE; } else { return FALSE; } } static int Expand(void *client, const char *p, long *res) { Z80State s; int ok=TRUE; Z80GetState(client,&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; if (Address(client,p+1,&n)) { *res=SPECDisPeek(client,n); } else { ok=FALSE; } } return ok; } static int BreaksActive(void) { int f; int ret=FALSE; for(f=0;f=bpoint.no) return; free(bpoint.expr[no]); for(f=no;fPC,s->AF>>8,FlagString(s->AF&0xff)); y+=8; GFXPrintPaper(0,y,col,BLACK, "BC=%4.4x DE=%4.4x HL=%4.4x", s->BC,s->DE,s->HL); y+=8; GFXPrintPaper(0,y,col,BLACK, "IX=%4.4x IY=%4.4x SP=%4.4x", s->IX,s->IY,s->SP); y+=8; GFXPrintPaper(0,y,col,BLACK, "AF'=%4.4x BC'=%4.4x DE'=%4.4x HL'=%4.4x", s->AF_,s->BC_,s->DE_,s->HL_); y+=8; GFXPrintPaper(0,y,col,BLACK, "I=%2.2x IM=%2.2x R=%2.2x", s->I,s->IM,s->R); y+=8; GFXPrintPaper(0,y,col,BLACK, "IFF1=%2.2x IFF2=%2.2x", s->IFF1,s->IFF2); } static int EnterAddress(const char *prompt, Z80 *z80, Z80Word *w) { const char *p; long l; p=GUIInputString(prompt ? prompt : "Address?",""); if (*p) { if (ExprEval(p,&l,Expand,z80)) { *w=(Z80Word)l; return TRUE; } else { GUIMessage(eMessageBox,"ERROR","%s",ExprError()); } } return FALSE; } static void EnterLong(const char *prompt, long *l) { const char *p; p=GUIInputString(prompt ? prompt : "Value?",""); if (*p) *l=strtol(p,NULL,0); } static void DoDisassem(Z80 *z80, const Z80State *s) { static int hexmode=FALSE; Z80Word pc=s->PC; int quit=FALSE; while(!quit) { SDL_Event *e; Z80Word curr=0; Z80Word next=0; int f; GFXClear(BLACK); Centre("DISASSEMBLY",0,WHITE); Centre("Press F1 for help",9,RED); curr=pc; for(f=0;fkey.keysym.sym) { case SDLK_F1: GUIMessage (eMessageBox, "DISASSEMBLY HELP","%s", "ESC - Exit \n" "H - Disassembly/hex \n" "Enter - Enter address \n" "Up - Prev byte \n" "Down - Next op \n" "Page Up - Back 1 page \n" "Page Down - Forward 1 page \n" "Left - Forward 1024 bytes\n" "Right - Back 1024 bytes "); break; case SDLK_ESCAPE: quit=TRUE; break; case SDLK_h: hexmode=!hexmode; break; case SDLK_RETURN: case SDLK_KP_ENTER: EnterAddress(NULL,z80,&pc); break; case SDLK_UP: if (hexmode) pc-=8; else pc--; break; case SDLK_DOWN: pc=next; break; case SDLK_PAGEUP: if (hexmode) pc-=21*8; else pc-=21; break; case SDLK_PAGEDOWN: pc=curr; break; case SDLK_LEFT: pc-=1024; break; case SDLK_RIGHT: pc+=1024; break; default: break; } } } static void DoDisassemFile(Z80 *z80, const Z80State *s) { static char fname[FILENAME_MAX]=""; FILE *fp; Z80Word start; Z80Word len; Z80Word prev; const char *p; EnterAddress("Disassemble from?",z80,&start); EnterAddress("For how many bytes?",z80,&len); p=GUIInputString("To file?",fname); if (!strlen(p)) return; strcpy(fname,p); if (!(fp=fopen(fname,"w"))) { GUIMessage(eMessageBox,"ERROR","Couldn't create file:\n%s",fname); return; } prev=len; while(len<=prev) { Z80Word orig=start; fprintf(fp,"%4.4x: %s\n",orig,Z80Disassemble(z80,&start)); prev=len; len-=(start-orig); } fclose(fp); } static void GetMemTrace(Z80 *z80, MemTrace *t, Z80Word from, int count) { int f; f=0; while(fkey.keysym.sym) { case SDLK_F1: GUIMessage (eMessageBox,"PLAYBACK HELP","%s", "ESC - Exit \n" "Enter - Step number \n" "P - Search for PC \n" "Up - Prev step \n" "Down - Next step \n" "Page Up - Back 50 steps \n" "Page Down - Forward 50 steps \n" "Left - Back 1000 steps \n" "Right - Forward 1000 steps \n \n" "NOTE: Disassembly is as memory is NOW"); break; case SDLK_ESCAPE: quit=TRUE; break; case SDLK_RETURN: case SDLK_KP_ENTER: EnterLong("Step number?",&pos); pos--; break; case SDLK_p: if (EnterAddress("PC to search for?",z80,&pc)) { while((rd=fread(&t,sizeof t,1,fp))==1) { pos++; if (t.s.PC==pc) break; } if (rd!=1) { GUIMessage(eMessageBox,"NOTICE","Address not found"); pos=prev_pos; } } break; case SDLK_UP: pos--; break; case SDLK_DOWN: pos++; break; case SDLK_PAGEUP: pos-=50; break; case SDLK_PAGEDOWN: pos+=50; break; case SDLK_LEFT: pos-=1000; break; case SDLK_RIGHT: pos+=1000; break; default: break; } /* Check position before next loop */ if (pos<0) pos=0; if (pos>=max_pos) pos=max_pos-1; } fclose(fp); } static int DoAddBreakpoint(Z80 *z80) { const char *expr; long l; expr=GUIInputString("Expression?",""); if (!*expr) return -1; if (ExprEval(expr,&l,Expand,z80)) { return AddBreak(z80,expr); } else { GUIMessage(eMessageBox,"INVALID EXPRESSION","%s",ExprError()); return -1; } } static void DoActiveBreakpoint(Z80 *z80) { if (bpoint.no==0) { GUIMessage(eMessageBox,"NOTICE","No breakpoints to set"); return; } GFXClear(BLACK); GUIListOption("SELECT ACTIVE BREAKPOINTS", bpoint.no,bpoint.expr,bpoint.active); } static void DoRemoveBreakpoint(Z80 *z80) { int sel; if (bpoint.no==0) { GUIMessage(eMessageBox,"NOTICE","No breakpoints to delete"); return; } GFXClear(BLACK); sel=GUIListSelect("BREAKPOINT TO DELETE",bpoint.no,bpoint.expr); while (sel!=-1) { DeleteBreak(z80,sel); sel=GUIListSelect("BREAKPOINT TO DELETE",bpoint.no,bpoint.expr); } } static void DoClearBreakpoint(Z80 *z80) { if (GUIMessage(eYesNoBox,"BREAKPOINTS","Clear all breakpoints")) { int f; for(f=0;fkey.state!=SDL_PRESSED) e=NULL; } else e=GFXWaitKey(); if (e) { switch(e->key.keysym.sym) { case SDLK_F1: GUIMessage (eMessageBox,"PLAYBACK HELP","%s", "ESC - Exit \n" "ENTER - Single step processor \n" "R - Run till break or stop \n" "W - Watch till break or stop\n" "T - Add temp breakpoint \n" "SPACE - Stop running \n" "O - Toggle overlay mode \n" "5 - Add a new breakpoint \n" "6 - Set active breakpoints \n" "7 - Remove a breakpoint \n" "8 - Clear all breakpoints \n" "H - Alter reg index display \n" "S - Display current screen "); break; case SDLK_RETURN: case SDLK_KP_ENTER: step=TRUE; break; case SDLK_ESCAPE: quit=TRUE; break; case SDLK_r: running=TRUE; update=FALSE; GFXClear(BLACK); CentrePaper("MONITOR",0,WHITE,BLACK); CentrePaper("Press F1 for help",9,RED,BLACK); CentrePaper("Running...",27,RED,BLACK); GFXEndFrame(FALSE); break; case SDLK_w: running=TRUE; break; case SDLK_SPACE: running=FALSE; update=TRUE; break; case SDLK_o: if (update) { overlay=!overlay; } break; case SDLK_5: DoAddBreakpoint(z80); break; case SDLK_6: DoActiveBreakpoint(z80); break; case SDLK_7: DoRemoveBreakpoint(z80); break; case SDLK_8: DoClearBreakpoint(z80); break; case SDLK_s: if (update) { SPECShowScreen(); GFXEndFrame(FALSE); GFXWaitKey(); } break; case SDLK_h: regdisp=(regdisp+1)%4; break; case SDLK_t: if (trigger!=-1) { DeleteBreak(z80,trigger); trigger=-1; } trigger=DoAddBreakpoint(z80); break; default: break; } } if (running || step) Z80SingleStep(z80); if (running && (brk=Break())) { GUIMessage(eMessageBox,"BREAKPOINT","%s",brk); running=FALSE; update=TRUE; if (trigger!=-1) { DeleteBreak(z80,trigger); trigger=-1; } } } if (trigger!=-1) { DeleteBreak(z80,trigger); } SPECEnableScreen(TRUE); } /* ---------------------------------------- EXPORTED INTERFACES */ int MemoryMenu(Z80 *z80) { SDL_Event *e; int done=FALSE; int quit=FALSE; Z80State s; Z80GetState(z80,&s); GFXKeyRepeat(TRUE); while(!done) { DisplayMenu(); DisplayZ80State(&s,GFX_HEIGHT-70,RED); GFXEndFrame(FALSE); e=GFXWaitKey(); switch(e->key.keysym.sym) { case SDLK_1: DoDisassem(z80,&s); break; case SDLK_2: DoDisassemFile(z80,&s); break; case SDLK_3: if (!trace) EnableTrace(z80,&s); else DisableTrace(z80); break; case SDLK_4: DisableTrace(z80); PlaybackTrace(z80); break; case SDLK_5: DoAddBreakpoint(z80); break; case SDLK_6: DoActiveBreakpoint(z80); break; case SDLK_7: DoRemoveBreakpoint(z80); break; case SDLK_8: DoClearBreakpoint(z80); break; case SDLK_9: DoMonitor(z80); break; case SDLK_r: if (GUIMessage(eYesNoBox,"RESET","Sure?")) { Z80Reset(z80); SPECReset(z80); } break; case SDLK_x: done=TRUE; quit=TRUE; break; case SDLK_ESCAPE: done=TRUE; break; default: break; } } GFXKeyRepeat(FALSE); return quit; } void DisplayState(Z80 *z80) { Z80State s; Z80GetState(z80,&s); DisplayZ80State(&s,GFX_HEIGHT/2,RED); } const char *Break(void) { const char *ret; ret=brk; brk=NULL; return ret; } /* END OF FILE */