/* ezx81 - X11 ZX81 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 "zx81.h" #include "gfx.h" #include "gui.h" #include "expr.h" #include "util.h" #include static const char ident_h[]=EZX81_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 6 #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 { Z80 s; Z80Val c; 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) { Z80 *cpu; int ok=TRUE; cpu=client; if (StrEq(p,"AF")) *res=cpu->AF.w; else if (StrEq(p,"BC")) *res=cpu->BC.w; else if (StrEq(p,"DE")) *res=cpu->DE.w; else if (StrEq(p,"HL")) *res=cpu->HL.w; else if (StrEq(p,"IX")) *res=cpu->IX.w; else if (StrEq(p,"IY")) *res=cpu->IY.w; else if (StrEq(p,"SP")) *res=cpu->SP; else if (StrEq(p,"PC")) *res=cpu->PC; else if (StrEq(p,"A")) *res=HI(cpu->AF.w); else if (StrEq(p,"F")) *res=LO(cpu->AF.w); else if (StrEq(p,"B")) *res=HI(cpu->BC.w); else if (StrEq(p,"C")) *res=LO(cpu->BC.w); else if (StrEq(p,"D")) *res=HI(cpu->DE.w); else if (StrEq(p,"E")) *res=LO(cpu->DE.w); else if (StrEq(p,"H")) *res=HI(cpu->HL.w); else if (StrEq(p,"L")) *res=LO(cpu->HL.w); else if (StrEq(p,"AF_")) *res=cpu->AF_; else if (StrEq(p,"BC_")) *res=cpu->BC_; else if (StrEq(p,"DE_")) *res=cpu->DE_; else if (StrEq(p,"HL_")) *res=cpu->HL_; else if (StrEq(p,"A_")) *res=HI(cpu->AF_); else if (StrEq(p,"F_")) *res=LO(cpu->AF_); else if (StrEq(p,"B_")) *res=HI(cpu->BC_); else if (StrEq(p,"C_")) *res=LO(cpu->BC_); else if (StrEq(p,"D_")) *res=HI(cpu->DE_); else if (StrEq(p,"E_")) *res=LO(cpu->DE_); else if (StrEq(p,"H_")) *res=HI(cpu->HL_); else if (StrEq(p,"L_")) *res=LO(cpu->HL_); else if (StrEq(p,"IM")) *res=cpu->IM; else if (StrEq(p,"R")) *res=cpu->R; else if (StrEq(p,"IFF1")) *res=cpu->IFF1; else if (StrEq(p,"IFF2")) *res=cpu->IFF2; else if (p[0]=='@') { Z80Word n; if (Address(client,p+1,&n)) { *res=ZX81ReadForDisassem(client,n); } else { ok=FALSE; } } return ok; } static int BreaksActive(void) { int f; int ret=FALSE; for(f=0;fPC,s->AF.w>>8,FlagString(s->AF.w&0xff)); y+=8; GFXPrintPaper(0,y,col,BLACK, "BC=%4.4x DE=%4.4x HL=%4.4x", s->BC.w,s->DE.w,s->HL.w); y+=8; GFXPrintPaper(0,y,col,BLACK, "IX=%4.4x IY=%4.4x SP=%4.4x", s->IX.w,s->IY.w,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 CY=%8.8lx", s->IFF1,s->IFF2,cycle); return y+8; } 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) { static int hexmode=FALSE; Z80Word pc=z80->PC; int quit=FALSE; while(!quit) { SDL_Event *e; Z80Word curr; Z80Word next; 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) { 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" "M - Toggle CPU state/memory \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_m: showmem=!showmem; 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 void DoAddBreakpoint(Z80 *z80) { const char *expr; long l; expr=GUIInputString("Expression?",""); if (!*expr) return; if (ExprEval(expr,&l,Expand,z80)) { bpoint.no++; bpoint.expr=Realloc(bpoint.expr,bpoint.no * sizeof(*bpoint.expr)); bpoint.active=Realloc(bpoint.active,bpoint.no * sizeof(*bpoint.active)); bpoint.expr[bpoint.no-1]=StrCopy(expr); bpoint.active[bpoint.no-1]=TRUE; SetCallback(z80); } else { GUIMessage(eMessageBox,"INVALID EXPRESSION","%s",ExprError()); } } 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) { int f; free(bpoint.expr[sel]); for(f=sel;fSP-(TRACEMEM_WIN/2+1),TRACEMEM_WIN); DisplayTraceMem(0,136,"MEM (SP)",mt,z80->SP); GetMemTrace(z80,mt,z80->HL.w-(TRACEMEM_WIN/2+1),TRACEMEM_WIN); DisplayTraceMem(100,136,"MEM (HL)",mt,z80->HL.w); GetMemTrace(z80,mt,z80->DE.w-(TRACEMEM_WIN/2+1),TRACEMEM_WIN); DisplayTraceMem(200,136,"MEM (DE)",mt,z80->DE.w); } else { int y; y=DisplayZ80State(z80,Z80Cycles(z80),136,WHITE); GFXPrint(0,y,GREEN,"%s",ZX81Info(z80)); } pc=z80->PC; for(f=0;f<10;f++) { char str[80]; char *p; int paper; int y; y=24+f*8; if (f==0) paper=RED; else paper=BLACK; GFXPrintPaper(0,y,GREEN,paper,"%4.4x ",pc); strcpy(str,Z80Disassemble(z80,&pc)); p=strtok(str,";"); GFXPrintPaper(40,y,WHITE,paper,"%s",str); } GFXEndFrame(FALSE); if (running) { e=GFXGetKey(); if (e && e->key.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" "SPACE - Stop running \n" "M - Toggle CPU state/memory \n" "5 - Add a new breakpoint \n" "6 - Set active breakpoints \n" "7 - Remove a breakpoint \n" "8 - Clear all breakpoints "); break; case SDLK_RETURN: case SDLK_KP_ENTER: step=TRUE; break; case SDLK_ESCAPE: quit=TRUE; break; case SDLK_r: running=TRUE; break; case SDLK_SPACE: running=FALSE; break; case SDLK_m: showmem=!showmem; 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; default: break; } } if (running || step) Z80SingleStep(z80); if (running && (brk=Break())) { GUIMessage(eMessageBox,"BREAKPOINT","%s",brk); running=FALSE; } } ZX81EnableScreen(TRUE); } /* ---------------------------------------- EXPORTED INTERFACES */ int MemoryMenu(Z80 *z80) { SDL_Event *e; int done=FALSE; int quit=FALSE; int y; GFXKeyRepeat(TRUE); while(!done) { DisplayMenu(); GFXEndFrame(FALSE); e=GFXWaitKey(); switch(e->key.keysym.sym) { case SDLK_1: DoDisassem(z80); break; case SDLK_2: DoDisassemFile(z80); break; case SDLK_3: if (!trace) EnableTrace(z80); 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); ZX81Reset(z80); } break; case SDLK_x: done=TRUE; quit=TRUE; break; case SDLK_s: GFXClear(BLACK); Centre("CURRENT STATE",0,WHITE); Centre("Press a key",9,RED); y=DisplayZ80State(z80,Z80Cycles(z80),20,WHITE); GFXPrint(0,y,GREEN,"%s",ZX81Info(z80)); GFXEndFrame(FALSE); GFXWaitKey(); break; case SDLK_ESCAPE: done=TRUE; break; default: break; } } GFXKeyRepeat(FALSE); GFXClear(BLACK); return quit; } void DisplayState(Z80 *z80) { int y; y=DisplayZ80State(z80,Z80Cycles(z80),GFX_HEIGHT/2,RED); GFXPrintPaper(0,y,GREEN,BLACK,"%s",ZX81Info(z80)); } const char *Break(void) { const char *ret; ret=brk; brk=NULL; return ret; } /* END OF FILE */