summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile24
-rw-r--r--src/gfx.c35
-rw-r--r--src/gfx.h23
-rw-r--r--src/gui.c299
-rw-r--r--src/gui.h28
-rw-r--r--src/main.c69
-rw-r--r--src/memmenu.c949
-rw-r--r--src/memmenu.h13
-rw-r--r--src/util.c131
-rw-r--r--src/util.h73
-rw-r--r--src/zx81.c96
-rw-r--r--src/zx81.h4
12 files changed, 1539 insertions, 205 deletions
diff --git a/src/Makefile b/src/Makefile
index 96ed1c0..96c80bd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -18,7 +18,7 @@
#
# -------------------------------------------------------------------------
#
-# $Id: Makefile,v 1.7 2003-12-23 23:30:39 ianc Exp $
+# $Id: Makefile,v 1.8 2004-01-22 02:38:13 ianc Exp $
#
@@ -38,6 +38,7 @@ SOURCE = main.c \
gfx.c \
gui.c \
memmenu.c \
+ util.c \
exit.c
OBJECTS = main.o \
@@ -46,6 +47,7 @@ OBJECTS = main.o \
gfx.o \
gui.o \
memmenu.o \
+ util.o \
exit.o
CFLAGS += -Iz80 `sdl-config --cflags`
@@ -57,7 +59,7 @@ $(TARGET): $(OBJECTS) $(Z80LIB)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
$(Z80LIB): z80/*.[ch]
- cd z80; make ; cd ..
+ cd z80; make "EXTERNAL_CFLAGS=`sdl-config --cflags`" ; cd ..
clean:
rm -f $(TARGET) $(OBJECTS) core
@@ -72,8 +74,9 @@ depend:
main.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
main.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
-main.o: /usr/include/stdio.h /usr/local/include/SDL/SDL.h
-main.o: /usr/local/include/SDL/SDL_main.h /usr/local/include/SDL/SDL_types.h
+main.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h
+main.o: /usr/local/include/SDL/SDL.h /usr/local/include/SDL/SDL_main.h
+main.o: /usr/local/include/SDL/SDL_types.h
main.o: /usr/local/include/SDL/SDL_getenv.h
main.o: /usr/local/include/SDL/SDL_error.h
main.o: /usr/local/include/SDL/begin_code.h
@@ -153,10 +156,11 @@ gui.o: /usr/local/include/SDL/SDL_keyboard.h
gui.o: /usr/local/include/SDL/SDL_keysym.h /usr/local/include/SDL/SDL_mouse.h
gui.o: /usr/local/include/SDL/SDL_video.h /usr/local/include/SDL/SDL_mutex.h
gui.o: /usr/local/include/SDL/SDL_quit.h /usr/local/include/SDL/SDL_version.h
-gui.o: gfx.h exit.h
+gui.o: gfx.h util.h exit.h
memmenu.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
memmenu.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
-memmenu.o: /usr/include/string.h /usr/include/strings.h memmenu.h z80/z80.h
+memmenu.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h
+memmenu.o: /usr/include/ctype.h /usr/include/runetype.h memmenu.h z80/z80.h
memmenu.o: zx81.h /usr/local/include/SDL/SDL.h
memmenu.o: /usr/local/include/SDL/SDL_main.h
memmenu.o: /usr/local/include/SDL/SDL_types.h
@@ -164,7 +168,7 @@ memmenu.o: /usr/local/include/SDL/SDL_getenv.h
memmenu.o: /usr/local/include/SDL/SDL_error.h
memmenu.o: /usr/local/include/SDL/begin_code.h
memmenu.o: /usr/local/include/SDL/close_code.h
-memmenu.o: /usr/local/include/SDL/SDL_rwops.h /usr/include/stdio.h
+memmenu.o: /usr/local/include/SDL/SDL_rwops.h
memmenu.o: /usr/local/include/SDL/SDL_timer.h
memmenu.o: /usr/local/include/SDL/SDL_audio.h
memmenu.o: /usr/local/include/SDL/SDL_byteorder.h
@@ -178,7 +182,11 @@ memmenu.o: /usr/local/include/SDL/SDL_mouse.h
memmenu.o: /usr/local/include/SDL/SDL_video.h
memmenu.o: /usr/local/include/SDL/SDL_mutex.h
memmenu.o: /usr/local/include/SDL/SDL_quit.h
-memmenu.o: /usr/local/include/SDL/SDL_version.h gfx.h gui.h
+memmenu.o: /usr/local/include/SDL/SDL_version.h gfx.h gui.h util.h
+util.o: /usr/include/stdio.h /usr/include/sys/cdefs.h
+util.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+util.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdarg.h
+util.o: util.h /usr/include/stdlib.h exit.h
exit.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
exit.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
exit.o: /usr/include/stdarg.h exit.h /usr/local/include/SDL/SDL.h
diff --git a/src/gfx.c b/src/gfx.c
index e7cecb8..ab7768c 100644
--- a/src/gfx.c
+++ b/src/gfx.c
@@ -50,9 +50,6 @@ static const char ident_fh[]=EZX81_FONT_H;
#define FALSE 0
#endif
-#define SCR_W 320
-#define SCR_H 200
-
#define LOCK do \
{ \
if (SDL_MUSTLOCK(surface)) \
@@ -171,8 +168,8 @@ void GFXInit(void)
if (SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO))
Exit("Failed to init SDL: %s\n",SDL_GetError());
- if (!(surface=SDL_SetVideoMode(SCR_W*scale,
- SCR_H*scale,
+ if (!(surface=SDL_SetVideoMode(GFX_WIDTH*scale,
+ GFX_HEIGHT*scale,
0,
IConfig(CONF_FULLSCREEN) ?
SDL_FULLSCREEN : 0)))
@@ -182,6 +179,8 @@ void GFXInit(void)
SDL_ShowCursor(SDL_DISABLE);
SDL_WM_SetCaption("eZX81","eZX81");
+
+ atexit(SDL_Quit);
}
@@ -262,6 +261,18 @@ SDL_Event *GFXWaitKey(void)
}
+void GFXLock(void)
+{
+ LOCK;
+}
+
+
+void GFXUnlock(void)
+{
+ UNLOCK;
+}
+
+
void GFXPlot(int x, int y, Uint32 col)
{
LOCK;
@@ -272,6 +283,12 @@ void GFXPlot(int x, int y, Uint32 col)
}
+void GFXFastPlot(int x, int y, Uint32 col)
+{
+ putpixel(x,y,col);
+}
+
+
void GFXRect(int x, int y, int w, int h, Uint32 col, int solid)
{
LOCK;
@@ -334,11 +351,11 @@ void GFXPrint(int x, int y, Uint32 col, const char *format, ...)
{
for(sy=0;sy<8;sy++)
{
- if (y+sy<SCR_H && x<SCR_W)
+ if (y+sy<GFX_HEIGHT && x<GFX_WIDTH)
{
for(sx=0;sx<8;sx++)
{
- if (font[(int)*p][sx+sy*8] && x+sx<SCR_W)
+ if (font[(int)*p][sx+sy*8] && x+sx<GFX_WIDTH)
putpixel(x+sx,y+sy,col);
}
}
@@ -372,11 +389,11 @@ void GFXPrintPaper(int x, int y, Uint32 col, Uint32 paper,
{
for(sy=0;sy<8;sy++)
{
- if (y+sy<SCR_H && x<SCR_W)
+ if (y+sy<GFX_HEIGHT && x<GFX_WIDTH)
{
for(sx=0;sx<8;sx++)
{
- if (font[(int)*p][sx+sy*8] && x+sx<SCR_W)
+ if (font[(int)*p][sx+sy*8] && x+sx<GFX_WIDTH)
putpixel(x+sx,y+sy,col);
else
putpixel(x+sx,y+sy,paper);
diff --git a/src/gfx.h b/src/gfx.h
index 14394d8..f700cb2 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -41,6 +41,11 @@
#define FONT_CURSOR '\007'
#define FONT_COPYRIGHT '\010'
+/* The size of the display
+*/
+#define GFX_WIDTH 320
+#define GFX_HEIGHT 200
+
/* ---------------------------------------- INTERFACES
*/
@@ -87,16 +92,32 @@ void GFXKeyRepeat(int repeat);
SDL_Event *GFXGetKey(void);
-/* Wait for a keypress (key up event)
+/* Wait for a keypress. Note that key up events are returned.
*/
SDL_Event *GFXWaitKey(void);
+/* Lock the screen for updates
+*/
+void GFXLock(void);
+
+
+/* Unlock the screen following updates
+*/
+void GFXUnlock(void);
+
+
/*
Note that no bound checking (except for GFXPrint()) is done - it is the
callers responsibility to plot onscreen.
*/
+/* Plot a point without locking. GFXLock() and GFXUnlock() MUST surround
+ calls to this
+*/
+void GFXFastPlot(int x, int y, Uint32 col);
+
+
/* Plot a point
*/
void GFXPlot(int x, int y, Uint32 col);
diff --git a/src/gui.c b/src/gui.c
index 9cbacf2..19d6135 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -33,11 +33,11 @@ static const char ident[]="$Id$";
#include "gui.h"
#include "gfx.h"
+#include "util.h"
#include "exit.h"
static const char ident_h[]=EZX81_GUI_H;
-
/* ---------------------------------------- MACROS
*/
#ifndef TRUE
@@ -48,57 +48,185 @@ static const char ident_h[]=EZX81_GUI_H;
#define FALSE 0
#endif
-/* Must match gfx.c
-*/
-#define SCR_W 320
-#define SCR_H 200
+#define WHITE GFXRGB(255,255,255)
+#define BLACK GFXRGB(0,0,0)
+#define RED GFXRGB(255,100,100)
+#define GREY GFXRGB(160,160,160)
+#define LOGREY GFXRGB(100,100,100)
+#define GREEN GFXRGB(100,255,100)
/* ---------------------------------------- STATICS
*/
-static Uint32 white;
-static Uint32 black;
-static Uint32 pale;
-static Uint32 red;
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
-static void Init()
+static void Trim(char *p,size_t len)
{
- static int init=FALSE;
-
- if (init)
- return;
+ if (strlen(p)>len)
+ {
+ p[len]=0;
+ p[len-1]='.';
+ p[len-2]='.';
+ }
+}
- init=TRUE;
- white=GFXRGB(255,255,255);
- black=GFXRGB(0,0,0);
- pale=GFXRGB(200,200,200);
- red=GFXRGB(255,100,100);
+static void Centre(const char *p, int y, Uint32 col)
+{
+ GFXPrint((GFX_WIDTH-strlen(p)*8)/2,y,col,"%s",p);
}
-static void *Malloc(size_t size)
+static void Box(const char *title, int x, int y, int width, int height)
{
- void *p=malloc(size);
+ GFXRect(x+1,y+1,
+ width-2,height-2,
+ BLACK,TRUE);
+
+ GFXRect(x+1,y+1,
+ width-2,11,
+ LOGREY,TRUE);
- if (!p)
- Exit("malloc failed for %lu bytes\n",(unsigned long)size);
+ GFXRect(x,y,
+ width,height,
+ GREY,FALSE);
- return p;
+ Centre(title,y+2,GREEN);
+ GFXHLine(x,x+width-1,y+11,GREY);
}
-static void Centre(const char *p, int y, Uint32 col)
+static int DoList(const char *title, int no, char * const list[], int *option)
{
- GFXPrint((SCR_W-strlen(p)*8)/2,y,col,"%s",p);
+ static const int max=GFX_HEIGHT/8-8;
+ SDL_Event *e;
+ int top;
+ int cur;
+ int done;
+ int f;
+
+ if (no==0)
+ return -1;
+
+ top=0;
+ cur=0;
+
+ done=FALSE;
+
+ while(!done)
+ {
+ Box(title,7,7,GFX_WIDTH-14,GFX_HEIGHT-14);
+
+ if (option)
+ {
+ Centre("Cursors to move, RETURN to accept",
+ GFX_HEIGHT-44,WHITE);
+ Centre("SPACE toggles, I inverts all",
+ GFX_HEIGHT-36,WHITE);
+ Centre("S select all, C clear all",
+ GFX_HEIGHT-28,WHITE);
+ }
+ else
+ Centre("Cursors and RETURN to select",GFX_HEIGHT-28,WHITE);
+
+ Centre("ESCAPE to cancel",GFX_HEIGHT-20,WHITE);
+
+ for(f=0;f<max;f++)
+ {
+ if (f+top<no)
+ {
+ Uint32 pen,paper;
+
+ if (f+top==cur)
+ {
+ pen=WHITE;
+ paper=RED;
+ }
+ else
+ {
+ pen=GREY;
+ paper=BLACK;
+ }
+
+ if (option)
+ GFXPrintPaper(16,20+f*8,pen,paper,"%c %-34.34s",
+ option[f+top] ? FONT_TICK:' ',list[f+top]);
+ else
+ GFXPrintPaper(16,20+f*8,pen,paper,"%-36.36s",list[f+top]);
+ }
+ }
+
+ GFXEndFrame(FALSE);
+
+ e=GFXWaitKey();
+
+ switch(e->key.keysym.sym)
+ {
+ case SDLK_RETURN:
+ done=TRUE;
+ break;
+
+ case SDLK_SPACE:
+ if (option)
+ option[cur]=!option[cur];
+ break;
+
+ case SDLK_i:
+ if (option)
+ for(f=0;f<no;f++)
+ option[f]=!option[f];
+ break;
+
+ case SDLK_s:
+ if (option)
+ for(f=0;f<no;f++)
+ option[f]=TRUE;
+ break;
+
+ case SDLK_c:
+ if (option)
+ for(f=0;f<no;f++)
+ option[f]=FALSE;
+ break;
+
+ case SDLK_ESCAPE:
+ cur=-1;
+ done=TRUE;
+ break;
+
+ case SDLK_UP:
+ if (cur>0)
+ {
+ cur--;
+
+ if (cur<top)
+ top=cur;
+ }
+ break;
+
+ case SDLK_DOWN:
+ if (cur<no-1)
+ {
+ cur++;
+
+ if (cur>top+max-2)
+ top=cur-max+2;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return cur;
}
/* ---------------------------------------- EXPORTED INTERFACES
*/
-void GUIMessage(const char *title, const char *format,...)
+int GUIMessage(GUIBoxType type, const char *title, const char *format,...)
{
char buff[1025];
va_list va;
@@ -109,8 +237,9 @@ void GUIMessage(const char *title, const char *format,...)
int width;
int height;
int x,y;
+ int ret;
- Init();
+ ret=FALSE;
va_start(va,format);
vsprintf(buff,format,va);
@@ -129,66 +258,96 @@ void GUIMessage(const char *title, const char *format,...)
line=Malloc(sizeof *line * no);
+ width=16;
+
line[0]=strtok(buff,"\n");
- width=strlen(line[0]);
+ Trim(line[0],38);
+
+ if (strlen(line[0])>width)
+ width=strlen(line[0]);
for(f=1;f<no;f++)
{
line[f]=strtok(NULL,"\n");
+ Trim(line[f],38);
if (strlen(line[f])>width)
width=strlen(line[f]);
}
- width=(width*8)+16;
- height=(no+3)*10;
-
- if (width>(SCR_W-10))
- width=SCR_W-10;
+ width=(width*8)+18;
+ height=(no+5)*10;
- if (height>(SCR_H-10))
- height=SCR_H-10;
+ if (width>(GFX_WIDTH-10))
+ width=GFX_WIDTH-10;
- y=(SCR_H-height)/2;
- x=(SCR_W-width)/2;
+ if (height>(GFX_HEIGHT-10))
+ height=GFX_HEIGHT-10;
- GFXRect(x-1,y-1,width+2,height+2,white,FALSE);
- GFXRect(x,y,width,height,black,TRUE);
+ y=(GFX_HEIGHT-height)/2;
+ x=(GFX_WIDTH-width)/2;
- Centre(title,y+2,white);
- GFXHLine(x+2,x+width-4,y+10,white);
+ Box(title,x,y,width,height);
for(f=0;f<no;f++)
- Centre(line[f],y+5+10*(f+1),pale);
+ Centre(line[f],y+5+10*(f+1),WHITE);
- Centre("Press a key",y+height-10,red);
+ if (type==eMessageBox)
+ Centre("Press a key",y+height-12,RED);
+ else
+ Centre("Press Y/N",y+height-12,RED);
GFXEndFrame(FALSE);
- GFXWaitKey();
+ if (type==eYesNoBox)
+ {
+ SDL_Event *e;
+
+ while(TRUE)
+ {
+ e=GFXWaitKey();
+
+ if (e->key.keysym.sym==SDLK_y)
+ {
+ ret=TRUE;
+ break;
+ }
+ else if (e->key.keysym.sym==SDLK_n)
+ {
+ ret=FALSE;
+ break;
+ }
+ }
+ }
+ else
+ GFXWaitKey();
+
free(line);
+
+ return ret;
}
const char *GUIInputString(const char *prompt, const char *orig)
{
- static const int y_pos=SCR_H-8;
+ static const int y_pos=GFX_HEIGHT-8;
static char buff[41];
size_t len;
int done=FALSE;
+ unsigned char c;
SDL_Event *e;
- Init();
-
buff[0]=0;
strncat(buff,orig,40);
len=strlen(buff);
+ SDL_EnableUNICODE(1);
+
while(!done)
{
- GFXRect(0,y_pos,SCR_W,8,black,TRUE);
- GFXPrint(0,y_pos,white,"%s %s%c",prompt,buff,FONT_CURSOR);
+ GFXRect(0,y_pos,GFX_WIDTH,8,BLACK,TRUE);
+ GFXPrint(0,y_pos,WHITE,"%s %s%c",prompt,buff,FONT_CURSOR);
GFXEndFrame(FALSE);
e=GFXWaitKey();
@@ -213,17 +372,53 @@ const char *GUIInputString(const char *prompt, const char *orig)
break;
default:
- if (len<40 && isprint(e->key.keysym.sym))
+ c=(unsigned char)e->key.keysym.unicode;
+
+ if (len<40 && isprint(c))
{
- buff[len++]=(char)e->key.keysym.sym;
+ buff[len++]=c;
buff[len]=0;
}
break;
}
}
+ SDL_EnableUNICODE(0);
+
return buff;
}
+int GUIListSelect(const char *title, int no, char * const list[])
+{
+ return DoList(title,no,list,NULL);
+}
+
+
+int GUIListOption(const char *title, int no, char * const list[], int option[])
+{
+ int *o;
+ int sel;
+ int f;
+
+ if (no==0)
+ return FALSE;
+
+ o=Malloc(no * sizeof *o);
+
+ for(f=0;f<no;f++)
+ o[f]=option[f];
+
+ sel=DoList(title,no,list,o);
+
+ if (sel!=-1)
+ for(f=0;f<no;f++)
+ option[f]=o[f];
+
+ free(o);
+
+ return sel!=-1;
+}
+
+
/* END OF FILE */
diff --git a/src/gui.h b/src/gui.h
index 4f03394..3744739 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -33,10 +33,20 @@
/* ---------------------------------------- INTERFACES
*/
+/* Message types
+*/
+typedef enum {eMessageBox, eYesNoBox} GUIBoxType;
+
/* Display a simple message box. A message of longer than 1024 bytes causes
- undefined behaviour. Newlines cause a line break.
+ undefined behaviour. Newlines cause a line break. If a line is over 38
+ characters then it will be truncated.
+
+ If type is eYesNoBox then TRUE/FALSE is returned depending on whether
+ yes/no was selected.
*/
-void GUIMessage(const char *title, const char *format,...);
+int GUIMessage(GUIBoxType type,
+ const char *title,
+ const char *format,...);
/* Enter a string, max 40 characters
@@ -44,6 +54,20 @@ void GUIMessage(const char *title, const char *format,...);
const char *GUIInputString(const char *prompt, const char *orig);
+/* Selects an entry from a list. Returns the index selected, or
+ -1 if cancelled.
+*/
+int GUIListSelect(const char *title, int no, char * const list[]);
+
+
+/* Allows options to be toggled in a list. Returns FALSE for cancelled (in
+ which case option will be as it was. TRUE if accepted, and option will be
+ updated.
+*/
+int GUIListOption(const char *title,
+ int no, char * const list[], int option[]);
+
+
#endif
diff --git a/src/main.c b/src/main.c
index 8841f6e..f6c5e6e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,6 +25,7 @@ static const char id[]="$Id$";
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include "SDL.h"
@@ -74,10 +75,11 @@ int main(int argc, char *argv[])
z80=Z80Init(ZX81WriteMem,
ZX81ReadMem,
+ ZX81WriteWord,
+ ZX81ReadWord,
ZX81WritePort,
ZX81ReadPort,
- ZX81ReadForDisassem,
- NULL);
+ ZX81ReadForDisassem);
GFXInit();
@@ -89,8 +91,16 @@ int main(int argc, char *argv[])
quit=FALSE;
+ /* Check for initial memory menu usage
+ TODO: Proper switch handling
+ */
+ if (argc>1 && strcmp(argv[1],"-m")==0)
+ quit=MemoryMenu(z80);
+
while(!quit)
{
+ const char *brk;
+
Z80SingleStep(z80);
if (trace)
@@ -99,22 +109,67 @@ int main(int argc, char *argv[])
GFXEndFrame(FALSE);
}
+ if ((brk=Break()))
+ {
+ GUIMessage(eMessageBox,"BREAKPOINT","%s",brk);
+ quit=MemoryMenu(z80);
+ }
+
while((e=GFXGetKey()))
{
switch (e->key.keysym.sym)
{
+ case SDLK_F1:
+ if (e->key.state==SDL_PRESSED)
+ GUIMessage(eMessageBox,
+ "HELP",
+ "ESC - Quit \n"
+ "F1 - Help \n"
+ "F2 - About \n"
+ "F3 - View ZX81 keyboad \n"
+ "F10 - Close all open tape files \n"
+ "F11 - Memory Menu \n"
+ "F12 - Toggle onscreen trace ");
+ break;
+
+ case SDLK_F2:
+ if (e->key.state==SDL_PRESSED)
+ GUIMessage(eMessageBox,
+ "eZX81 - ZX81 Emulator",
+ "(c) 2004 Ian Cowburn\n"
+ " \n"
+ "This software comes with ABSOLUTELY \n"
+ "NO WARRANTY, and you are free to \n"
+ "to redistribute it under certain \n"
+ "conditions. See the supplied GNU \n"
+ "General Public License in LICENSE \n"
+ "for details. \n"
+ " \n"
+ "If you did not recieve a license, \n"
+ "vist www.gnu.org or wrote to: \n"
+ " \n"
+ "Free Software Foundation, Inc., \n"
+ "59 Temple Place, Suite 330, \n"
+ "Boston, MA 02111-1307 USA ");
+ break;
+
+ case SDLK_F3:
+ if (e->key.state==SDL_PRESSED)
+ GUIMessage(eMessageBox,"TODO","Sorry, not done yet");
+ break;
+
case SDLK_ESCAPE:
- if (e->key.state==SDL_RELEASED)
- quit=TRUE;
+ if (e->key.state==SDL_PRESSED)
+ quit=GUIMessage(eYesNoBox,"QUIT","Sure?");
break;
case SDLK_F11:
- if (e->key.state==SDL_RELEASED)
- MemoryMenu(z80);
+ if (e->key.state==SDL_PRESSED)
+ quit=MemoryMenu(z80);
break;
case SDLK_F12:
- if (e->key.state==SDL_RELEASED)
+ if (e->key.state==SDL_PRESSED)
trace=!trace;
break;
diff --git a/src/memmenu.c b/src/memmenu.c
index 3bb800c..95429f9 100644
--- a/src/memmenu.c
+++ b/src/memmenu.c
@@ -26,6 +26,7 @@
static const char ident[]="$Id$";
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <ctype.h>
@@ -33,33 +34,112 @@ static const char ident[]="$Id$";
#include "zx81.h"
#include "gfx.h"
#include "gui.h"
+#include "util.h"
#include <SDL.h>
static const char ident_h[]=EZX81_MEMMENU_H;
#ifndef TRUE
-#define TRUE 1
+#define TRUE 1
#endif
#ifndef FALSE
-#define FALSE 0
+#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 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 MENU
+#define TRACE "trace"
+#define TRACEMEM_WIN 6
+
+
+/* ---------------------------------------- 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);
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
+static int BreaksActive(void)
+{
+ int f;
+ int ret=FALSE;
+
+ for(f=0;f<bpoint.no;f++)
+ ret|=bpoint.active[f];
+
+ return ret;
+}
+
+
+static void SetCallback(Z80 *z80)
+{
+ if ((trace || BreaksActive()) && !lodged)
+ {
+ Z80LodgeCallback(z80,eZ80_Instruction,Instruction);
+ lodged=TRUE;
+ }
+}
+
+
+static void ClearCallback(Z80 *z80)
+{
+ if (!trace && !BreaksActive() && 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);
+ GFXPrint((GFX_WIDTH-strlen(p)*8)/2,y,col,"%s",p);
+}
+
+
+static void CentrePaper(const char *p, int y, Uint32 pen, Uint32 paper)
+{
+ GFXPrintPaper((GFX_WIDTH-strlen(p)*8)/2,y,pen,paper,"%s",p);
}
@@ -67,8 +147,19 @@ static void DisplayMenu(void)
{
static const char *menu[]=
{
- "1. Disassemble/Hex dump",
- "2. Return ",
+ "1 - Disassemble/Hex dump ",
+ "2 - Disassemble to file ",
+ "3 - Start/Stop trace log ",
+ "4 - Playback trace log ",
+ "5 - Add a new breakpoint ",
+ "6 - Set active breakpoints",
+ "7 - Remove a breakpoint ",
+ "8 - Clear all breakpoints ",
+ "9 - Monitor ",
+ "R - Reset ",
+ "S - Show current CPU state",
+ "X - Exit (without confirm)",
+ "ESC - Return ",
NULL
};
@@ -77,13 +168,13 @@ static void DisplayMenu(void)
GFXClear(BLACK);
Centre("MEMORY MENU",0,WHITE);
- Centre("Select an option",8,WHITE);
+ Centre("Select an option",10,RED);
f=0;
while(menu[f])
{
- Centre(menu[f],20+f*10,WHITE);
+ Centre(menu[f],25+f*10,WHITE);
f++;
}
}
@@ -105,66 +196,56 @@ static const char *FlagString(Z80Byte flag)
}
-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 Z80Word Address(Z80 *z80, const char *p)
+int DisplayZ80State(Z80State *s, int y, Uint32 col)
{
- Z80State s;
-
- Z80GetState(z80,&s);
+ 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,
+ "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);
- if (StrEq(p,"AF"))
- return s.AF;
- else if (StrEq(p,"BC"))
- return s.BC;
- else if (StrEq(p,"DE"))
- return s.DE;
- else if (StrEq(p,"HL"))
- return s.HL;
- else if (StrEq(p,"IX"))
- return s.IX;
- else if (StrEq(p,"IY"))
- return s.IY;
- else if (StrEq(p,"SP"))
- return s.SP;
- else if (StrEq(p,"PC"))
- return s.PC;
-
- return (Z80Word)strtoul(p,NULL,0);
+ return y+8;
}
-static int EnterAddress(Z80 *z80, Z80Word *w)
+static int EnterAddress(const char *prompt, Z80 *z80, Z80Word *w)
{
- unsigned long ul;
const char *p;
+ char *error;
+ long l;
- p=GUIInputString("Address?","");
+ p=GUIInputString(prompt ? prompt : "Address?","");
if (*p)
{
- ul=Address(z80,p);
-
- if (ul>0xffff)
+ if (!Z80Expression(z80,p,&l,&error))
{
- GUIMessage("ERROR","Bad address");
+ GUIMessage(eMessageBox,
+ "ERROR","%s",error ? error:"Invalid expression");
+
+ free(error);
}
else
{
- *w=(Z80Word)ul;
+ *w=(Z80Word)l;
return TRUE;
}
}
@@ -173,6 +254,17 @@ static int EnterAddress(Z80 *z80, Z80Word *w)
}
+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;
@@ -188,31 +280,45 @@ static void DoDisassem(Z80 *z80, const Z80State *s)
GFXClear(BLACK);
- Centre("Press F1 for help",0,RED);
+ Centre("DISASSEMBLY",0,WHITE);
+ Centre("Press F1 for help",9,RED);
curr=pc;
- for(f=0;f<21;f++)
+ for(f=0;f<GFX_HEIGHT/8-8;f++)
{
char s[80];
char *p;
int y;
- y=16+f*8;
+ y=24+f*8;
GFXPrint(0,y,GREEN,"%4.4x",curr);
if (hexmode)
{
int n;
+ char asc[9];
for(n=0;n<8;n++)
{
- GFXPrint(40+n*24,y,WHITE,"%2.2x",
- (int)ZX81ReadForDisassem(z80,curr));
+ Z80Byte b;
+
+ b=ZX81ReadForDisassem(z80,curr);
+
+ GFXPrint(40+n*24,y,WHITE,"%2.2x",(int)b);
curr++;
+
+ if (isprint(b))
+ asc[n]=b;
+ else
+ asc[n]='.';
}
+
+ asc[8]=0;
+
+ GFXPrint(40+8*24,y,RED,"%s",asc);
}
else
{
@@ -234,16 +340,16 @@ static void DoDisassem(Z80 *z80, const Z80State *s)
{
case SDLK_F1:
GUIMessage
- ("DISASSEMBLY HELP","%s",
- "ESC - Exit\n"
- "H - Switch between disassembly/hex\n"
- "Enter - Enter address to display\n"
- "Cursor Up - Prev op\n"
- "Cursor Down - Next op\n"
- "Page Up - Move back a page\n"
- "Page Down - Move forward a page\n"
- "Cursor Left - Move PC by -1024\n"
- "Cursor Right - Move PC by 1024");
+ (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:
@@ -256,7 +362,7 @@ static void DoDisassem(Z80 *z80, const Z80State *s)
case SDLK_RETURN:
case SDLK_KP_ENTER:
- EnterAddress(z80,&pc);
+ EnterAddress(NULL,z80,&pc);
break;
case SDLK_UP:
@@ -296,22 +402,619 @@ static void DoDisassem(Z80 *z80, const Z80State *s)
}
+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(f<count)
+ {
+ Z80Word w;
+
+ w=(Z80Word)ZX81ReadForDisassem(z80,from)|
+ (Z80Word)ZX81ReadForDisassem(z80,from+1)<<8;
+
+ t[f].addr=from;
+ t[f].val=w;
+
+ from+=2;
+ f++;
+ }
+}
+
+
+static int Instruction(Z80 *z80, Z80Val data)
+{
+ int f;
+
+ if (trace)
+ {
+ Trace t;
+
+ Z80GetState(z80,&t.s);
+
+ GetMemTrace(z80,t.hl,t.s.HL-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+ GetMemTrace(z80,t.de,t.s.DE-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+ GetMemTrace(z80,t.sp,t.s.SP-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+
+ fwrite(&t,sizeof t,1,trace);
+ }
+
+ for(f=0;f<bpoint.no;f++)
+ if (bpoint.active[f])
+ {
+ long l;
+
+ if (Z80Expression(z80,bpoint.expr[f],&l,NULL))
+ {
+ if (l)
+ brk=bpoint.expr[f];
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void EnableTrace(Z80 *z80, Z80State *s)
+{
+ if (!trace)
+ {
+ trace=fopen(TRACE,"wb");
+
+ if (trace)
+ {
+ GUIMessage(eMessageBox,"NOTICE","Created trace log");
+ SetCallback(z80);
+ }
+ else
+ {
+ GUIMessage(eMessageBox,"ERROR","Failed to create trace log");
+ }
+ }
+}
+
+
+static void DisableTrace(Z80 *z80)
+{
+ if (trace)
+ {
+ GUIMessage(eMessageBox,"NOTICE","Closing current trace log");
+ fclose(trace);
+ trace=NULL;
+ ClearCallback(z80);
+ }
+}
+
+
+static void DisplayTraceMem(int x, int y, const char *name,
+ MemTrace *m, Z80Word expect)
+{
+ int f;
+
+ GFXPrint(x,y,GREEN,"%s",name);
+
+ for(f=0;f<TRACEMEM_WIN;f++)
+ {
+ Uint32 paper;
+
+ if (m[f].addr==expect)
+ paper=RED;
+ else
+ paper=BLACK;
+
+ GFXPrintPaper(x,y+f*8+12,GREEN,paper,"%4.4x",m[f].addr);
+ GFXPrintPaper(x+32,y+f*8+12,WHITE,paper," %4.4x",m[f].val);
+ }
+}
+
+static void PlaybackTrace(Z80 *z80)
+{
+ static int showmem=FALSE;
+ FILE *fp;
+ int quit;
+ long prev_pos;
+ long pos;
+ long max_pos;
+ Z80Word pc;
+ Trace t;
+
+ fp=fopen(TRACE,"rb");
+
+ if (!fp)
+ {
+ GUIMessage(eMessageBox,"ERROR","Couldn't open trace file");
+ return;
+ }
+
+ fseek(fp,0,SEEK_END);
+
+ max_pos=ftell(fp)/sizeof t;
+
+ if (max_pos==0)
+ {
+ GUIMessage(eMessageBox,"ERROR","Empty trace file");
+ fclose(fp);
+ return;
+ }
+
+ pos=0;
+ prev_pos=pos;
+ pc=0;
+ quit=FALSE;
+
+ while(!quit)
+ {
+ SDL_Event *e;
+ size_t rd;
+ int f;
+
+ GFXClear(BLACK);
+
+ Centre("TRACE PLAYBACK",0,WHITE);
+ Centre("Press F1 for help",9,RED);
+
+ fseek(fp,pos*sizeof t,SEEK_SET);
+ fread(&t,sizeof t,1,fp);
+
+ if (showmem)
+ {
+ DisplayTraceMem(0,136,"MEM (SP)",t.sp,t.s.SP);
+ DisplayTraceMem(100,136,"MEM (HL)",t.hl,t.s.HL);
+ DisplayTraceMem(200,136,"MEM (DE)",t.de,t.s.DE);
+ }
+ else
+ DisplayZ80State(&t.s,136,WHITE);
+
+ 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 ",t.s.PC);
+
+ strcpy(str,Z80Disassemble(z80,&t.s.PC));
+ p=strtok(str,";");
+ GFXPrintPaper(40,y,WHITE,paper,"%s",str);
+ }
+
+ GFXPrint(0,112,GREEN,"Current step: %ld",pos+1);
+ GFXPrint(0,120,GREEN,"Total steps : %ld",max_pos);
+
+ prev_pos=pos;
+
+ GFXEndFrame(FALSE);
+
+ e=GFXWaitKey();
+
+ switch(e->key.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;
+ 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.active=Realloc(bpoint.active,bpoint.no * sizeof(*bpoint.active));
+
+ bpoint.expr[bpoint.no-1]=StrCopy(expr);
+ bpoint.active[bpoint.no-1]=TRUE;
+
+ SetCallback(z80);
+ }
+}
+
+
+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;f<bpoint.no-1;f++)
+ {
+ bpoint.expr[f]=bpoint.expr[f+1];
+ bpoint.active[f]=bpoint.active[f+1];
+ }
+
+ bpoint.no--;
+
+ if (bpoint.no==0)
+ {
+ free(bpoint.expr);
+ bpoint.expr=NULL;
+ bpoint.active=NULL;
+ }
+ else
+ {
+ bpoint.expr=Realloc(bpoint.expr,
+ bpoint.no * sizeof(*bpoint.expr));
+ bpoint.active=Realloc(bpoint.active,
+ bpoint.no * sizeof(*bpoint.active));
+ }
+
+ ClearCallback(z80);
+
+ 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;f<bpoint.no;f++)
+ free(bpoint.expr[f]);
+
+ free(bpoint.expr);
+ bpoint.expr=NULL;
+ bpoint.no=0;
+
+ ClearCallback(z80);
+ }
+}
+
+
+static void DoMonitor(Z80 *z80)
+{
+ static int showmem=FALSE;
+ int quit=FALSE;
+ int running=FALSE;
+ int step=FALSE;
+
+ ZX81EnableScreen(FALSE);
+
+ while(!quit)
+ {
+ MemTrace mt[TRACEMEM_WIN];
+ const char *brk;
+ SDL_Event *e;
+ Z80State s;
+ int f;
+
+ step=FALSE;
+
+ GFXClear(BLACK);
+
+ CentrePaper("MONITOR",0,WHITE,BLACK);
+ CentrePaper("Press F1 for help",9,RED,BLACK);
+
+ Z80GetState(z80,&s);
+
+ if (showmem)
+ {
+ GetMemTrace(z80,mt,s.SP-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+ DisplayTraceMem(0,136,"MEM (SP)",mt,s.SP);
+ GetMemTrace(z80,mt,s.HL-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+ DisplayTraceMem(100,136,"MEM (HL)",mt,s.HL);
+ GetMemTrace(z80,mt,s.DE-(TRACEMEM_WIN/2+1),TRACEMEM_WIN);
+ DisplayTraceMem(200,136,"MEM (DE)",mt,s.DE);
+ }
+ else
+ {
+ int y;
+
+ y=DisplayZ80State(&s,136,WHITE);
+ GFXPrint(0,y,GREEN,"%s",ZX81Info(z80));
+ }
+
+ 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 ",s.PC);
+
+ strcpy(str,Z80Disassemble(z80,&s.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
*/
-void MemoryMenu(Z80 *z80)
+int MemoryMenu(Z80 *z80)
{
SDL_Event *e;
+ int done=FALSE;
int quit=FALSE;
Z80State s;
+ int y;
Z80GetState(z80,&s);
GFXKeyRepeat(TRUE);
- while(!quit)
+ while(!done)
{
DisplayMenu();
- DisplayState(z80);
GFXEndFrame(FALSE);
e=GFXWaitKey();
@@ -322,17 +1025,78 @@ void MemoryMenu(Z80 *z80)
DoDisassem(z80,&s);
break;
- case SDLK_ESCAPE:
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);
+ 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(&s,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;
}
@@ -342,31 +1106,20 @@ void DisplayState(Z80 *z80)
int y;
Z80GetState(z80,&s);
+ y=DisplayZ80State(&s,GFX_HEIGHT/2,RED);
+ GFXPrintPaper(0,y,GREEN,BLACK,"%s",ZX81Info(z80));
+}
- y=136;
-
- GFXPrintPaper(0,y,RED,BLACK,"PC=%4.4x A=%2.2x F=%s",
- s.PC,s.AF>>8,FlagString(s.AF&0xff));
- y+=8;
-
- GFXPrintPaper(0,y,RED,BLACK,"BC=%4.4x DE=%4.4x HL=%4.4x",s.BC,s.DE,s.HL);
- y+=8;
-
- GFXPrintPaper(0,y,RED,BLACK,"IX=%4.4x IY=%4.4x SP=%4.4x",s.IX,s.IY,s.SP);
- y+=8;
- GFXPrintPaper(0,y,RED,BLACK,"I=%2.2x IM=%2.2x R=%2.2x",s.I,s.IM,s.R);
- y+=8;
+const char *Break(void)
+{
+ const char *ret;
- GFXPrintPaper(0,y,RED,BLACK,"IFF1=%2.2x IFF2=%2.2x",s.IFF1,s.IFF2);
- y+=8;
+ ret=brk;
+ brk=NULL;
- y+=8;
- GFXPrintPaper(0,y,RED,BLACK,"%s\n",ZX81Info(z80));
- y+=8;
+ return ret;
}
-
-
/* END OF FILE */
diff --git a/src/memmenu.h b/src/memmenu.h
index bacbef9..1caecb5 100644
--- a/src/memmenu.h
+++ b/src/memmenu.h
@@ -30,14 +30,19 @@
#include "z80.h"
-/* Memory menu
+/* Memory menu. Returns TRUE if exit (from program) selected.
*/
-void MemoryMenu(Z80 *z80);
+int MemoryMenu(Z80 *z80);
-/* Display the state of the ZX81 at the bottom of the screen
+/* Display the state of the SPEC at the bottom of the screen
*/
-void DisplayState(Z80 *z80);
+void DisplayState(Z80 *z80);
+
+
+/* Non-NULL (the breakpoint hit) if a breakpoint has been hit
+*/
+const char *Break(void);
#endif
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..566255d
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,131 @@
+/*
+
+ 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
+
+ -------------------------------------------------------------------------
+
+ Usual library wrappers and utils
+
+*/
+static const char ident[]="$Id$";
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "util.h"
+#include "exit.h"
+
+static const char ident_h[]=ESPEC_UTIL_H;
+
+
+/* ---------------------------------------- MACROS
+*/
+#define DIRSEP '/'
+
+/* ---------------------------------------- INTERFACES
+*/
+void *Malloc(size_t size)
+{
+ void *new=malloc(size);
+
+ if (!new)
+ Exit("malloc failed for %lu bytes\n",(unsigned long)size);
+
+ return new;
+}
+
+
+void *Realloc(void *p, size_t size)
+{
+ void *new=realloc(p,size);
+
+ if (!new)
+ Exit("realloc failed for %lu bytes\n",(unsigned long)size);
+
+ return new;
+}
+
+
+char *StrCopy(const char *source)
+{
+ return strcpy(Malloc(strlen(source)+1),source);
+}
+
+
+const char *Basename(const char *path)
+{
+ const char *p=path+strlen(path);
+
+ while(p>path && *p!=DIRSEP)
+ p--;
+
+ if (p>path)
+ p++;
+
+ return p;
+}
+
+
+const char *Dirname(const char *path)
+{
+ static char dir[FILENAME_MAX];
+ char *p;
+
+ strcpy(dir,path);
+
+ p=dir+strlen(dir);
+
+ while(p>dir && *p!=DIRSEP)
+ p--;
+
+ if (p>path)
+ {
+ p++;
+ *p=0;
+ }
+ else
+ {
+ strcpy(dir,".");
+ }
+
+ return dir;
+}
+
+
+void Debug(const char *format, ...)
+{
+ static FILE *fp=NULL;
+ va_list ap;
+
+ if (!fp)
+ {
+ fp=fopen("debug.txt","w");
+ setbuf(fp,NULL);
+ }
+
+ if (!fp)
+ return;
+
+ va_start(ap,format);
+ vfprintf(fp,format,ap);
+ va_end(ap);
+}
+
+/* END OF FILE */
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..922f641
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,73 @@
+/*
+
+ 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
+
+ -------------------------------------------------------------------------
+
+ Usual library wrappers and utils
+
+*/
+
+#ifndef ESPEC_UTIL_H
+#define ESPEC_UTIL_H "$Id$"
+
+#include <stdlib.h>
+
+/* ---------------------------------------- INTERFACES
+*/
+
+/* Returns result from malloc(size), calling Exit() if it fails.
+*/
+void *Malloc(size_t size);
+
+
+/* Returns result from realloc(p,size), calling Exit() if it fails.
+*/
+void *Realloc(void *p, size_t size);
+
+
+/* Copies a string. The result must be freed.
+*/
+char *StrCopy(const char *source);
+
+
+/* Returns the filename portion of path. Note returned pointer is pointing
+ inside of path.
+*/
+const char *Basename(const char *path);
+
+
+/* Returns the directory portion of path. Note returned pointer is internal
+ static storage. If there are no directory seperators in path, "." is
+ returned.
+*/
+const char *Dirname(const char *path);
+
+
+/* Writes the passed text to debug.txt. Note this is interface is not
+ able to be switched on/off, so debug is only used in a transient way
+ (once the bug is fixed, calls to this should be removed).
+*/
+void Debug(const char *format,...);
+
+
+#endif
+
+
+/* END OF FILE */
diff --git a/src/zx81.c b/src/zx81.c
index 603bbd9..a7299e4 100644
--- a/src/zx81.c
+++ b/src/zx81.c
@@ -51,21 +51,21 @@ static const int ROMLEN=0x2000;
static const int ROM_SAVE=0x2fc;
static const int ROM_LOAD=0x347;
-/* No of cycles in each 64us HSYNC (hopefully)
+/* No of T-states in each 64us HSYNC (hopefully)
*/
-static const int HSYNC_PERIOD=321;
+static const Z80Val HSYNC_PERIOD=321;
/* The ZX81 screen
*/
-static const int SCR_W=256;
-static const int SCR_H=192;
-static const int TXT_W=32;
-static const int TXT_H=24;
+static int scr_enable=TRUE;
-/* These assume a 320x200 screen
-*/
-static const int OFF_X=(320-256)/2;
-static const int OFF_Y=(200-192)/2;
+#define SCR_W 256
+#define SCR_H 192
+#define TXT_W 32
+#define TXT_H 24
+
+#define OFF_X (GFX_WIDTH-SCR_W)/2
+#define OFF_Y (GFX_HEIGHT-SCR_H)/2
static Z80Byte mem[0x10000];
@@ -249,7 +249,7 @@ static void LoadTape(Z80State *state)
if (strlen(p)==0)
{
- GUIMessage("ERROR","Can't load empty filename");
+ GUIMessage(eMessageBox,"ERROR","Can't load empty filename");
return;
}
@@ -260,7 +260,7 @@ static void LoadTape(Z80State *state)
if (!(fp=fopen(path,"rb")))
{
- GUIMessage("ERROR","Can't load file:\n%s",path);
+ GUIMessage(eMessageBox,"ERROR","Can't load file:\n%s",path);
return;
}
@@ -288,7 +288,7 @@ static void SaveTape(Z80State *state)
if (strlen(p)==0)
{
- GUIMessage("ERROR","Can't save empty filename");
+ GUIMessage(eMessageBox,"ERROR","Can't save empty filename");
return;
}
@@ -299,7 +299,7 @@ static void SaveTape(Z80State *state)
if (!(fp=fopen(path,"wb")))
{
- GUIMessage("ERROR","Can't write file:\n%s",path);
+ GUIMessage(eMessageBox,"ERROR","Can't write file:\n%s",path);
return;
}
@@ -345,6 +345,9 @@ static void ULA_Video_Shifter(Z80 *z80, Z80Byte val)
int inv;
int b;
+ if (!scr_enable)
+ return;
+
Z80GetState(z80,&state);
/* Extra check due to out dodgy ULA emulation
@@ -396,12 +399,12 @@ static int CheckTimers(Z80 *z80, Z80Val val)
{
if (val>HSYNC_PERIOD)
{
- Z80ResetCycles(z80,0);
+ Z80ResetCycles(z80,val-HSYNC_PERIOD);
- if (nmigen)
+ if (nmigen && hsync)
{
- Z80NMI(z80,0xff);
- printf("NMIGEN\n");
+ Z80NMI(z80);
+ /*printf("NMIGEN\n");*/
}
else if (hsync)
{
@@ -429,22 +432,24 @@ void ZX81Init(Z80 *z80)
if (!(fp=fopen(SConfig(CONF_ROMFILE),"rb")))
{
- GUIMessage("ERROR","Failed to open ZX81 ROM\n%s",SConfig(CONF_ROMFILE));
+ GUIMessage(eMessageBox,
+ "ERROR","Failed to open ZX81 ROM\n%s",SConfig(CONF_ROMFILE));
Exit("");
}
if (fread(mem,1,ROMLEN,fp)!=ROMLEN)
{
fclose(fp);
- GUIMessage("ERROR","ROM file must be %d bytes long\n",ROMLEN);
+ GUIMessage(eMessageBox,
+ "ERROR","ROM file must be %d bytes long\n",ROMLEN);
Exit("");
}
/* Patch the ROM
*/
RomPatch();
- Z80LodgeCallback(z80,Z80_EDHook,EDCallback);
- Z80LodgeCallback(z80,Z80_Fetch,CheckTimers);
+ Z80LodgeCallback(z80,eZ80_EDHook,EDCallback);
+ Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers);
/* Mirror the ROM
*/
@@ -544,6 +549,16 @@ Z80Byte ZX81ReadMem(Z80 *z80, Z80Word addr)
}
+Z80Word ZX81ReadWord(Z80 *z80, Z80Word addr)
+{
+ Z80Word l,h;
+
+ l=ZX81ReadMem(z80,addr);
+ h=ZX81ReadMem(z80,addr+1);
+ return (h<<8)|l;
+}
+
+
void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
{
addr=addr&0x7fff;
@@ -553,6 +568,18 @@ void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
}
+void ZX81WriteWord(Z80 *z80, Z80Word addr, Z80Word val)
+{
+ if (addr>=RAMBOT && addr<=RAMTOP)
+ mem[addr]=val&0xff;
+
+ addr++;
+
+ if (addr>=RAMBOT && addr<=RAMTOP)
+ mem[addr]=val>>8;
+}
+
+
Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port)
{
Z80Byte b=0;
@@ -597,6 +624,7 @@ Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port)
*/
if (!nmigen && hsync)
{
+ printf("HSYNC OFF\n");
hsync=FALSE;
GFXEndFrame(TRUE);
@@ -637,14 +665,17 @@ void ZX81WritePort(Z80 *z80, Z80Word port, Z80Byte val)
switch(port&0xff)
{
case 0xfd: /* NMI generator OFF */
+ printf("NMIGEN OFF\n");
nmigen=FALSE;
break;
case 0xfe: /* NMI generator ON */
+ printf("NMIGEN ON\n");
nmigen=TRUE;
break;
case 0xff: /* HSYNC generator ON */
+ printf("HSYNC ON\n");
hsync=TRUE;
Z80ResetCycles(z80,0);
break;
@@ -662,12 +693,29 @@ const char *ZX81Info(Z80 *z80)
{
static char buff[80];
- sprintf(buff,"NMIGEN: %s HSYNC: %s",
+ sprintf(buff,"NMI: %s HS: %s ULA: (%d,%d,%d,%d)",
nmigen ? "ON":"OFF",
- hsync ? "ON":"OFF");
+ hsync ? "ON":"OFF",
+ ULA.x,ULA.y,ULA.c,ULA.release);
return buff;
}
+void ZX81EnableScreen(int enable)
+{
+ scr_enable=enable;
+}
+
+
+void ZX81Reset(Z80 *z80)
+{
+ scr_enable=TRUE;
+ nmigen=FALSE;
+ hsync=FALSE;
+
+ GFXStartFrame();
+}
+
+
/* END OF FILE */
diff --git a/src/zx81.h b/src/zx81.h
index 855c4a5..f308e8b 100644
--- a/src/zx81.h
+++ b/src/zx81.h
@@ -43,6 +43,8 @@ void ZX81KeyEvent(SDL_Event *e);
*/
Z80Byte ZX81ReadMem(Z80 *z80, Z80Word addr);
void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val);
+Z80Word ZX81ReadWord(Z80 *z80, Z80Word addr);
+void ZX81WriteWord(Z80 *z80, Z80Word addr, Z80Word val);
Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port);
void ZX81WritePort(Z80 *z80, Z80Word port, Z80Byte val);
Z80Byte ZX81ReadForDisassem(Z80 *z80, Z80Word addr);
@@ -50,6 +52,8 @@ Z80Byte ZX81ReadForDisassem(Z80 *z80, Z80Word addr);
/* Interfaces for memory menu
*/
const char *ZX81Info(Z80 *z80);
+void ZX81EnableScreen(int enable);
+void ZX81Reset(Z80 *z80);
#endif