/* 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 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" /* ---------------------------------------- TYPES */ typedef struct { int no; char **expr; } Breakpoint; /* ---------------------------------------- STATIC DATA */ static FILE *trace=NULL; static Breakpoint bpoint={NULL,0}; static const char *brk=NULL; static int lodged=FALSE; /* ---------------------------------------- PROTOS */ static int Instruction(Z80 *z80, Z80Val data); /* ---------------------------------------- PRIVATE FUNCTIONS */ static void SetCallback(Z80 *z80) { if ((trace || bpoint.no) && !lodged) { Z80LodgeCallback(z80,eZ80_Instruction,Instruction); lodged=TRUE; } } static void ClearCallback(Z80 *z80) { if (!trace && !bpoint.no && lodged) { Z80RemoveCallback(z80,eZ80_Instruction,Instruction); lodged=FALSE; } } static void Centre(const char *p, int y, Uint32 col) { GFXPrint((320-strlen(p)*8)/2,y,col,"%s",p); } static void DisplayMenu(void) { static const char *menu[]= { "1. Disassemble/Hex dump ", "2. Disassemble to file ", "3. Start/Stop trace log ", "4. Playback trace log ", "5. Add a new breakpoint ", "6. Clear a breakpoint ", "7. Display breakpoints ", "8. Clear all breakpoints", "9. Return ", NULL }; int f; GFXClear(BLACK); Centre("MEMORY MENU",0,WHITE); Centre("Select an option",10,RED); f=0; while(menu[f]) { Centre(menu[f],25+f*10,WHITE); f++; } } static const char *FlagString(Z80Byte flag) { static char s[]="76543210"; static char c[]="SZ5H3PNC"; int f; for(f=0;f<8;f++) if (flag&(1<<(7-f))) s[f]=c[f]; else s[f]='-'; return s; } void DisplayZ80State(Z80State *s, int y, Uint32 col) { GFXPrintPaper(0,y,col,BLACK, "PC=%4.4x A=%2.2x F=%s", s->PC,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, "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; char *error; long l; p=GUIInputString(prompt ? prompt : "Address?",""); if (*p) { if (!Z80Expression(z80,p,&l,&error)) { GUIMessage(eMessageBox, "ERROR","%s",error ? error:"Invalid expression"); free(error); } else { *w=(Z80Word)l; return TRUE; } } 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; 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, 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 int Instruction(Z80 *z80, Z80Val data) { int f; if (trace) { Z80State s; Z80GetState(z80,&s); fwrite(&s,sizeof s,1,trace); } for(f=0;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(&s,sizeof s,1,fp))==1) { pos++; if (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 void DoAddBreakpoint(Z80 *z80) { const char *expr; char *error; long l; expr=GUIInputString("Expression?",""); if (!*expr) return; if (!Z80Expression(z80,expr,&l,&error)) { if (error) { GUIMessage(eMessageBox,"INVALID EXPRESSION","%s",error); free(error); } else { GUIMessage(eMessageBox,"ERROR","Expression is invalid"); free(error); } } else { bpoint.no++; bpoint.expr=Realloc(bpoint.expr,bpoint.no * sizeof(*bpoint.expr)); bpoint.expr[bpoint.no-1]=StrCopy(expr); SetCallback(z80); } } static void DoRemoveBreakpoint(Z80 *z80, int delete) { if (bpoint.no==0) { if (delete) GUIMessage(eMessageBox,"NOTICE","No breakpoints to delete"); else GUIMessage(eMessageBox,"NOTICE","No breakpoints to display"); return; } GFXClear(BLACK); if (delete) { int sel; sel=GUIListSelect("BREAKPOINT TO DELETE",bpoint.no,bpoint.expr); while (sel!=-1) { int f; free(bpoint.expr[sel]); for(f=sel;fkey.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: DoRemoveBreakpoint(z80,TRUE); break; case SDLK_7: DoRemoveBreakpoint(z80,FALSE); break; case SDLK_8: DoClearBreakpoint(z80); break; case SDLK_ESCAPE: case SDLK_9: quit=TRUE; break; default: break; } } GFXKeyRepeat(FALSE); } void DisplayState(Z80 *z80) { Z80State s; Z80GetState(z80,&s); DisplayZ80State(&s,136,RED); } const char *Break(void) { const char *ret; ret=brk; brk=NULL; return ret; } /* END OF FILE */