From 25bc35fbadebbf28eed3c89978e435d2df557b42 Mon Sep 17 00:00:00 2001
From: Ian C <ianc@noddybox.co.uk>
Date: Tue, 6 Jan 2004 02:06:37 +0000
Subject: Devel checkin - added util and snap objects and non-working TAP
 support

---
 src/Makefile  |  30 ++++++---
 src/gui.c     |  14 ++++
 src/gui.h     |  27 +++++++-
 src/main.c    |  54 ++++++++++++++++
 src/memmenu.c |  40 ++++++------
 src/snap.c    | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/snap.h    |  73 +++++++++++++++++++++
 src/spec.c    | 100 ++++++++++++++++++++++++-----
 src/spec.h    |   9 +++
 src/util.c    | 100 +++++++++++++++++++++++++++++
 src/util.h    |  61 ++++++++++++++++++
 11 files changed, 661 insertions(+), 47 deletions(-)
 create mode 100644 src/snap.c
 create mode 100644 src/snap.h
 create mode 100644 src/util.c
 create mode 100644 src/util.h

diff --git a/src/Makefile b/src/Makefile
index 79b1d6a..68ecd6d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -18,7 +18,7 @@
 # 
 # -------------------------------------------------------------------------
 # 
-# $Id: Makefile,v 1.4 2004-01-05 01:07:07 ianc Exp $
+# $Id: Makefile,v 1.5 2004-01-06 02:06:36 ianc Exp $
 # 
 
 
@@ -34,18 +34,22 @@ Z80LIB	=	z80/z80.a
 
 SOURCE	=	main.c		\
 		spec.c		\
+		snap.c		\
 		config.c	\
 		gfx.c		\
 		gui.c		\
 		memmenu.c	\
+		util.c		\
 		exit.c
 
 OBJECTS	=	main.o		\
 		spec.o		\
+		snap.o		\
 		config.o	\
 		gfx.o		\
 		gui.o		\
 		memmenu.o	\
+		util.o		\
 		exit.o
 
 CFLAGS +=	-Iz80 `sdl-config --cflags`
@@ -72,8 +76,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
@@ -111,7 +116,11 @@ spec.o: /usr/local/include/SDL/SDL_keyboard.h
 spec.o: /usr/local/include/SDL/SDL_keysym.h
 spec.o: /usr/local/include/SDL/SDL_mouse.h /usr/local/include/SDL/SDL_video.h
 spec.o: /usr/local/include/SDL/SDL_mutex.h /usr/local/include/SDL/SDL_quit.h
-spec.o: /usr/local/include/SDL/SDL_version.h gfx.h gui.h config.h exit.h
+spec.o: /usr/local/include/SDL/SDL_version.h snap.h gfx.h gui.h config.h
+spec.o: exit.h
+snap.o: snap.h /usr/include/stdlib.h /usr/include/sys/cdefs.h
+snap.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+snap.o: /usr/include/stdio.h z80/z80.h
 config.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
 config.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
 config.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h
@@ -139,9 +148,10 @@ gui.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
 gui.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
 gui.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h
 gui.o: /usr/include/stdarg.h /usr/include/ctype.h /usr/include/runetype.h
-gui.o: gui.h /usr/local/include/SDL/SDL.h /usr/local/include/SDL/SDL_main.h
-gui.o: /usr/local/include/SDL/SDL_types.h /usr/local/include/SDL/SDL_getenv.h
-gui.o: /usr/local/include/SDL/SDL_error.h /usr/local/include/SDL/begin_code.h
+gui.o: gui.h gfx.h /usr/local/include/SDL/SDL.h
+gui.o: /usr/local/include/SDL/SDL_main.h /usr/local/include/SDL/SDL_types.h
+gui.o: /usr/local/include/SDL/SDL_getenv.h /usr/local/include/SDL/SDL_error.h
+gui.o: /usr/local/include/SDL/begin_code.h
 gui.o: /usr/local/include/SDL/close_code.h /usr/local/include/SDL/SDL_rwops.h
 gui.o: /usr/local/include/SDL/SDL_timer.h /usr/local/include/SDL/SDL_audio.h
 gui.o: /usr/local/include/SDL/SDL_byteorder.h
@@ -153,7 +163,7 @@ 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: 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/stdio.h /usr/include/string.h /usr/include/strings.h
@@ -180,6 +190,10 @@ 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
+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 util.h
+util.o: /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/gui.c b/src/gui.c
index 3a4b031..5072be1 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -207,4 +207,18 @@ const char *GUIInputString(const char *prompt, const char *orig)
 }
 
 
+int GUIFileSelect(const char *prompt, int load,
+		  const char *start_dir, char path[])
+{
+    /* TODO */
+    if (load)
+	strcpy(path,"/files/emu/spectrum/thrust1.tap");
+    else
+	strcpy(path,"/files/emu/spectrum/testespec.tap");
+
+    strcpy(path,"/files/emu/spectrum/testespec.tap");
+    return TRUE;
+}
+
+
 /* END OF FILE */
diff --git a/src/gui.h b/src/gui.h
index 6d2fcb9..953aac9 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -27,8 +27,6 @@
 #ifndef ESPEC_GUI_H
 #define ESPEC_GUI_H "$Id$"
 
-#include "SDL.h"
-
 
 /* ---------------------------------------- INTERFACES
 */
@@ -44,6 +42,31 @@ void		GUIMessage(const char *title, const char *format,...);
 const char	*GUIInputString(const char *prompt, const char *orig);
 
 
+/* Select a file from the given directory.
+
+   If load is TRUE then a new name cannot be entered.
+
+   Returns TRUE for selected, FALSE for cancelled.
+
+   path holds the new file entered.  Note that start_dir can be a path to a
+   file - if chdir(start_dir) would not work, then it is tried with the
+   dirname(1) of start_dir.
+
+   Also start_dir and path can be the same pointer:
+
+	char file[FILENAME_MAX]="/home/foobar/dir/file.tap";
+
+	if (GUI_Fsel("Select tape",TRUE,file,file))
+	    ....
+
+   Will work fine.
+*/
+int		GUIFileSelect(const char *prompt,
+			      int load,
+			      const char *start_dir,
+			      char path[]);
+
+
 #endif
 
 
diff --git a/src/main.c b/src/main.c
index 79f0994..e25ce75 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"
 
@@ -63,6 +64,8 @@ static Uint32	grey;
 */
 int main(int argc, char *argv[])
 {
+    char tape_in[FILENAME_MAX];
+    char tape_out[FILENAME_MAX];
     Z80 *z80;
     SDL_Event *e;
     int quit;
@@ -72,6 +75,9 @@ int main(int argc, char *argv[])
 
     trace=IConfig(CONF_TRACE);
 
+    strcpy(tape_in,SConfig(CONF_TAPEDIR));
+    strcpy(tape_out,SConfig(CONF_TAPEDIR));
+
     z80=Z80Init(SPECWriteMem,
     		SPECReadMem,
 		SPECWritePort,
@@ -89,6 +95,12 @@ 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)
+    	MemoryMenu(z80);
+
     while(!quit)
     {
 	Z80State s1,s2;
@@ -118,6 +130,48 @@ int main(int argc, char *argv[])
 			quit=TRUE;
 		    break;
 
+		case SDLK_F1:
+		    if (e->key.state==SDL_PRESSED)
+			GUIMessage("HELP",
+				   "ESC - Quit                        \n"
+				   "F1  - Help                        \n"
+				   "F8  - Select tape file for loading\n"
+				   "F9  - Select tape file for saving \n"
+				   "F10 - Close all open tape files   \n"
+				   "F11 - Memory Menu                 \n"
+				   "F12 - Toggle onscreen trace       ");
+		    break;
+
+		case SDLK_F8:
+		    if (e->key.state==SDL_PRESSED)
+		    {
+			if (GUIFileSelect("TAPE TO LOAD",TRUE,
+					  tape_in,tape_in))
+			{
+			    SPECMount(SPEC_TAPE_IN,tape_in);
+			}
+		    }
+		    break;
+
+		case SDLK_F9:
+		    if (e->key.state==SDL_PRESSED)
+		    {
+			if (GUIFileSelect("TAPE TO SAVE",FALSE,
+					  tape_out,tape_out))
+			{
+			    SPECMount(SPEC_TAPE_OUT,tape_out);
+			}
+		    }
+		    break;
+
+		case SDLK_F10:
+		    if (e->key.state==SDL_PRESSED)
+		    {
+			SPECUnmount(SPEC_TAPE_IN);
+			SPECUnmount(SPEC_TAPE_OUT);
+		    }
+		    break;
+
 		case SDLK_F11:
 		    if (e->key.state==SDL_PRESSED)
 		    	MemoryMenu(z80);
diff --git a/src/memmenu.c b/src/memmenu.c
index 807e49f..50b9acb 100644
--- a/src/memmenu.c
+++ b/src/memmenu.c
@@ -280,15 +280,15 @@ 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");
+		       "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:
@@ -437,7 +437,7 @@ static void PlaybackTrace(Z80 *z80)
 
     if (!fp)
     {
-	GUIMessage("ERROR","Couldn't open tracefile: " TRACE);
+	GUIMessage("ERROR","Couldn't open trace file");
 	return;
     }
 
@@ -447,7 +447,7 @@ static void PlaybackTrace(Z80 *z80)
 
     if (max_pos==0)
     {
-	GUIMessage("ERROR","Empty tracefile: " TRACE);
+	GUIMessage("ERROR","Empty trace file");
 	fclose(fp);
 	return;
     }
@@ -508,15 +508,15 @@ static void PlaybackTrace(Z80 *z80)
 	    case SDLK_F1:
 		GUIMessage
 		    ("PLAYBACK HELP","%s",
-		       "ESC - Exit\n"
-		       "Enter - Step number to display\n"
-		       "P - Search for PC\n"
-		       "Cursor Up - Prev step\n"
-		       "Cursor Down - Next step\n"
-		       "Page Up - Back 50 steps\n"
-		       "Page Down - Forward 50 steps\n"
-		       "Cursor Left - Back 1000 steps\n"
-		       "Cursor Right - Forward 1000 steps\n \n"
+		       "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;
 
diff --git a/src/snap.c b/src/snap.c
new file mode 100644
index 0000000..c67c0bf
--- /dev/null
+++ b/src/snap.c
@@ -0,0 +1,200 @@
+/*
+
+    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
+
+    -------------------------------------------------------------------------
+
+    Utilities for handling snapshots
+
+*/
+static const char ident[]="$Id$";
+
+#include "snap.h"
+
+static const char ident_h[]=ESPEC_SNAP_H;
+
+
+/* ---------------------------------------- MACROS
+*/
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ROMLEN 0x4000
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+static Z80Word GetLSBWord(FILE *fp)
+{
+    if (fp)
+    {
+        int c1,c2;
+
+        c1=getc(fp);
+        c2=getc(fp);
+
+        if (c1==EOF || c2==EOF)
+            return 0;
+
+        return c1+(c2<<8);
+    }
+    else
+        return 0;
+}
+
+
+static void PutLSBWord(FILE *fp, Z80Word w)
+{
+    if (fp)
+    {
+	putc(w&0xff,fp);
+	putc(w>>8,fp);
+    }
+}
+
+
+static Z80Byte GetByte(FILE *fp)
+{
+    int c;
+
+    if (!fp || (c=getc(fp))==EOF)
+        return 0;
+
+    return c;
+}
+
+
+static void PutByte(FILE *fp, Z80Byte b)
+{
+    putc(b,fp);
+}
+
+
+
+/* ---------------------------------------- INTERFACES
+*/
+int TAPLoad(FILE *fp, Z80Byte id, Z80Word *addr, Z80Word *len, Z80Byte *mem)
+{
+    Z80Word blen;
+    Z80Byte type,b,csum,tape_csum;
+
+    if (!fp)
+    	return FALSE;
+
+    /* Get length (wrapping file if at eof)
+    */
+    blen=GetLSBWord(fp);
+
+    if (feof(fp))
+    {
+    	rewind(fp);
+	blen=GetLSBWord(fp);
+    }
+
+    type=GetByte(fp);
+    csum=id;
+
+    /* Have we found the requested block?
+    */
+    if (id==type)
+    {
+    	/* Knock of block type
+	*/
+	blen--;
+
+	while(blen && *len)
+	{
+	    b=GetByte(fp);
+	    csum^=b;
+
+	    if (*addr>=ROMLEN)
+	    	mem[*addr]=b;
+
+	    (*addr)++;
+	    (*len)--;
+	    blen--;
+	}
+
+	/* Get the checksum
+	*/
+	if (blen)
+	{
+	    tape_csum=GetByte(fp);
+	    blen--;
+	}
+	else
+	    tape_csum=b;
+
+	/* In case we've been request less bytes than the block size
+	*/
+	while(blen--)
+	    fgetc(fp);
+
+	/* Check the checksum
+	*/
+	return csum==tape_csum;
+    }
+    else
+    {
+    	/* If it's the wrong type, skip it
+	*/
+	while(blen--)
+	    fgetc(fp);
+
+	return FALSE;
+    }
+}
+
+
+int TAPSave(FILE *fp, Z80Byte id, Z80Word *addr, Z80Word *len, Z80Byte *mem)
+{
+    Z80Byte csum;
+
+    if (!fp)
+    	return FALSE;
+
+    /* Write out the length and ID byte
+    */
+    PutLSBWord(fp,(*len)+2);
+    PutByte(fp,id);
+
+    /* Write out data and calc checksum
+    */
+    csum=id;
+
+    while(*len)
+    {
+    	PutByte(fp,mem[*addr]);
+	csum^=mem[*addr];
+	(*addr)++;
+	(*len)--;
+    }
+
+    PutByte(fp,csum);
+
+    return TRUE;
+}
+
+
+/* END OF FILE */
diff --git a/src/snap.h b/src/snap.h
new file mode 100644
index 0000000..04d771a
--- /dev/null
+++ b/src/snap.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
+
+    -------------------------------------------------------------------------
+
+    Utilities for handling snapshots
+
+*/
+
+#ifndef ESPEC_SNAP_H
+#define ESPEC_SNAP_H "$Id$"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "z80.h"
+
+/* ---------------------------------------- INTERFACES
+*/
+
+/* Loads a block from a TAP file.  Returns FALSE for failure.
+   Won't write below location 0x4000 in mem.
+*/
+int	TAPLoad(FILE *fp, Z80Byte id, Z80Word *addr,
+		Z80Word *len, Z80Byte *mem);
+
+
+/* Saves a block to a TAP file.  Returns FALSE for failure
+   (which it never does as long as fp is not NULL).
+*/
+int	TAPSave(FILE *fp, Z80Byte id, Z80Word *addr,
+		Z80Word *len, Z80Byte *mem);
+
+
+/* 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);
+
+
+#endif
+
+
+/* END OF FILE */
diff --git a/src/spec.c b/src/spec.c
index 6ace002..bd6d5ff 100644
--- a/src/spec.c
+++ b/src/spec.c
@@ -29,6 +29,7 @@ static const char ident[]="$Id$";
 #include <stdio.h>
 #include <string.h>
 #include "spec.h"
+#include "snap.h"
 #include "gfx.h"
 #include "gui.h"
 #include "config.h"
@@ -44,6 +45,9 @@ static const char ident_h[]=ESPEC_SPECH;
 #define FALSE 0
 #endif
 
+#define HIBYTE(w)	((w)>>8)
+#define LOBYTE(w)	((w)&0xff)
+
 
 /* ---------------------------------------- STATICS
 */
@@ -51,6 +55,9 @@ static const int	ROMLEN=0x4000;
 static const int	ROM_SAVE=0x4c6;
 static const int	ROM_LOAD=0x562;
 
+static FILE		*tape_in;
+static FILE		*tape_out;
+
 #define LOAD_PATCH	0xf0
 #define SAVE_PATCH	0xf1
 
@@ -317,18 +324,6 @@ static void RomPatch(void)
 
 
 
-static void LoadTape(Z80State *state)
-{
-    state->AF|=Z80_F_Carry;
-}
-
-
-static void SaveTape(Z80State *state)
-{
-    state->AF|=Z80_F_Carry;
-}
-
-
 static int EDCallback(Z80 *z80, Z80Val data)
 {
     Z80State state;
@@ -338,13 +333,39 @@ static int EDCallback(Z80 *z80, Z80Val data)
     switch((Z80Byte)data)
     {
     	case SAVE_PATCH:
-	    puts("Called tape save");
-	    SaveTape(&state);
+	    if (!tape_out)
+	    {
+		state.AF|=Z80_F_Carry;
+	    }
+	    else
+	    {
+	    	if (TAPSave(tape_out,HIBYTE(state.AF),&state.IX,&state.DE,mem))
+		{
+		    state.AF&=~Z80_F_Carry;
+		}
+		else
+		{
+		    state.AF|=Z80_F_Carry;
+		}
+	    }
 	    break;
 
     	case LOAD_PATCH:
-	    puts("Called tape load");
-	    LoadTape(&state);
+	    if (!tape_in)
+	    {
+		state.AF|=Z80_F_Carry;
+	    }
+	    else
+	    {
+	    	if (TAPLoad(tape_in,HIBYTE(state.AF),&state.IX,&state.DE,mem))
+		{
+		    state.AF&=~Z80_F_Carry;
+		}
+		else
+		{
+		    state.AF|=Z80_F_Carry;
+		}
+	    }
 	    break;
 
 	default:
@@ -533,7 +554,6 @@ Z80Byte SPECReadPort(Z80 *z80, Z80Word port)
 	default:	/* ULA */
 	    if (!(lo&1))
 	    {
-		border=(border+1)%16; /* TODO: Remove debug code */
 		/* Key matrix
 		*/
 		b=0xff;
@@ -580,6 +600,8 @@ Z80Byte SPECReadForDisassem(Z80 *z80, Z80Word addr)
 }
 
 
+/* TODO: Implement this as a binary search
+*/
 const char *SPECGetLabel(Z80 *z80, Z80Word addr)
 {
     static const struct
@@ -788,4 +810,48 @@ const char *SPECInfo(Z80 *z80)
 }
 
 
+void SPECMount(SPECMountType type, const char *path)
+{
+    switch(type)
+    {
+    	case SPEC_TAPE_IN:
+	    if (tape_in)
+	    	fclose(tape_in);
+
+	    tape_in=fopen(path,"rb");
+	    break;
+
+    	case SPEC_TAPE_OUT:
+	    if (tape_out)
+	    	fclose(tape_out);
+
+	    tape_out=fopen(path,"wb");
+	    break;
+    }
+}
+
+
+void SPECUnmount(SPECMountType type)
+{
+    switch(type)
+    {
+    	case SPEC_TAPE_IN:
+	    if (tape_in)
+	    {
+	    	fclose(tape_in);
+		tape_in=NULL;
+	    }
+	    break;
+
+    	case SPEC_TAPE_OUT:
+	    if (tape_out)
+	    {
+	    	fclose(tape_out);
+		tape_out=NULL;
+	    }
+	    break;
+    }
+}
+
+
 /* END OF FILE */
diff --git a/src/spec.h b/src/spec.h
index 558ba6a..bb62b91 100644
--- a/src/spec.h
+++ b/src/spec.h
@@ -31,6 +31,10 @@
 #include "z80.h"
 #include "SDL.h"
 
+/* Types for Mount and Unmount
+*/
+typedef enum {SPEC_TAPE_IN,SPEC_TAPE_OUT} SPECMountType;
+
 
 /* Initialise the SPEC
 */
@@ -54,6 +58,11 @@ const char	*SPECGetLabel(Z80 *z80, Z80Word addr);
 const char	*SPECInfo(Z80 *z80);
 
 
+/* Interfaces for snapshot and device control
+*/
+void		SPECMount(SPECMountType type, const char *path);
+void		SPECUnmount(SPECMountType type);
+
 #endif
 
 
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..c3396eb
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,100 @@
+/*
+
+    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 "util.h"
+#include "exit.h"
+
+static const char ident_h[]=ESPEC_UTIL_H;
+
+
+/* ---------------------------------------- MACROS
+*/
+#define DIRSEP	'/'
+
+/* ---------------------------------------- INTERFACES
+*/
+void *Malloc(size_t size)
+{
+    void *p=malloc(size);
+
+    if (!p)
+    	Exit("malloc failed for %lu bytes\n",(unsigned long)size);
+
+    return p;
+}
+
+
+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;
+}
+
+
+/* END OF FILE */
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..f391170
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,61 @@
+/*
+
+    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);
+
+
+/* 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);
+
+
+#endif
+
+
+/* END OF FILE */
-- 
cgit v1.2.3