summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2020-03-22 21:28:09 +0000
committerIan C <ianc@noddybox.co.uk>2020-03-22 21:28:09 +0000
commite2f4e398c33f30f9b03448fb6687b7e06a65f21f (patch)
tree27225576cbcbb6e5400a2e57743a1a1c3cea578a
parentb324606fad1d596bf4f6319239f9c567a409941e (diff)
Added load/save and some extra instructions.
-rw-r--r--doc/instruction.txt20
-rw-r--r--src/Makefile1
-rw-r--r--src/czx81.c172
3 files changed, 186 insertions, 7 deletions
diff --git a/doc/instruction.txt b/doc/instruction.txt
index a399cd7..530dab9 100644
--- a/doc/instruction.txt
+++ b/doc/instruction.txt
@@ -44,6 +44,9 @@ KEYBOARD
BACKSPACE - Emulate the RUBOUT key (shift 0)
DELETE - Emulate the RUBOUT key (shift 0)
+ When emulating the keyboard the key is held down for a default of one frame.
+ This can be altered in the runtime menu.
+
LOADING AND SAVING
------------------
@@ -58,6 +61,23 @@ LOADING AND SAVING
be enabled. See the CONFIGURATION FILE section.
+RUNTIME MENU
+------------
+
+ When you press ESCAPE the runtime menu is displayed and offers the following
+ options:
+
+ F1 - Toggle tracing on and off. When on tracing will display the
+ disassembly at the PC and the state of the Z80 registers.
+
+ F2 - Alter the number of frames the key is held down following a
+ keypress. The default of 1 works OK for BASIC.
+
+ F3 - Reset the ZX81.
+
+ F4 - Quit the emulator.
+
+
CONFIGURATION FILE
------------------
diff --git a/src/Makefile b/src/Makefile
index 9fc766e..e1aa066 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -17,6 +17,7 @@
TARGET=czx81
# This library may need to be set to one of these:
+# curses
# cursesw
# ncursesw
CURSES=-lncurses
diff --git a/src/czx81.c b/src/czx81.c
index 27bfe5b..55bae6a 100644
--- a/src/czx81.c
+++ b/src/czx81.c
@@ -111,6 +111,9 @@ static int current_key = -1;
static int trace = FALSE;
+static char snappath[4096] = ".";
+static int allow_save = FALSE;
+
static struct
{
int key; /* The curses keycode */
@@ -291,6 +294,101 @@ static void RenderScreen(void)
}
+static FILE *OpenTapeFile(Z80Word addr, const char *mode)
+{
+ static const char zx_chars[] = "\"#$:?()><=+-*/;,."
+ "0123456789"
+ "abcdefghijklmnopqrstuvwxyz";
+ char full_fn[4096];
+ char fn[4096];
+ int f;
+ int done;
+
+ done = FALSE;
+ f = 0;
+
+ while(!done)
+ {
+ int ch;
+
+ ch = mem[addr++];
+
+ if (ch & 0x80)
+ {
+ done = TRUE;
+ ch &= 0x7f;
+ }
+
+ if (ch >= 11 && ch <=63)
+ {
+ fn[f++] = zx_chars[ch - 11];
+ }
+ }
+
+ fn[f] = 0;
+
+ sprintf(full_fn, "%s/%s", snappath, fn);
+
+ return fopen(full_fn, mode);
+}
+
+
+static void ReadConfig(void)
+{
+ char fn[4096] = {0};
+ char *env;
+ FILE *fp;
+
+ if ((env = getenv("HOME")))
+ {
+ strcpy(fn, env);
+ strcat(fn, "/");
+ }
+
+ strcat(fn, ".czx81rc");
+
+ if ((fp = fopen(fn, "r")))
+ {
+ char s[4096];
+
+ while(fgets(s, sizeof s, fp))
+ {
+ size_t l;
+ char *tok1 = NULL;
+ char *tok2 = NULL;
+
+ l = strlen(s);
+
+ while(l && s[l - 1] == '\n')
+ {
+ s[--l] = 0;
+ }
+
+ tok1 = strtok(s, " \t");
+
+ if (tok1)
+ {
+ tok2 = strtok(NULL, " \t");
+ }
+
+ if (strcmp(tok1, "snappath") == 0)
+ {
+ if (tok2)
+ {
+ strcpy(snappath, tok2);
+ }
+ }
+ else if (strcmp(tok1, "allowsave") == 0)
+ {
+ allow_save = TRUE;
+ }
+ }
+
+ fclose(fp);
+ }
+}
+
+
/* -------------------------------------------------- ZX81 CODE
*/
static void RomPatch(const Z80Byte patch[], Z80Word addr)
@@ -552,14 +650,34 @@ static void PressKey(int key)
static int EDCallback(Z80 *z80, Z80Val data)
{
- Z80Word pause;
-
switch((Z80Byte)data)
{
case ED_SAVE:
- if (z80->DE.w < 0x8000)
+ if (allow_save && z80->DE.w < 0x8000)
{
- /* TODO: Save */
+ FILE *fp;
+
+ fp = OpenTapeFile(z80->HL.w, "wb");
+
+ if (fp)
+ {
+ int f;
+ int end;
+
+ f = 0x4009;
+ end = PEEKW(E_LINE);
+
+ while(f <= end)
+ {
+ fputc(mem[f++], fp);
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ sprintf(message, "Failed to open file");
+ }
}
break;
@@ -567,7 +685,28 @@ static int EDCallback(Z80 *z80, Z80Val data)
case ED_LOAD:
if (z80->DE.w < 0x8000)
{
- /* TODO: Load */
+ FILE *fp;
+
+ fp = OpenTapeFile(z80->DE.w, "rb");
+
+ if (fp)
+ {
+ int c;
+ Z80Byte *a;
+
+ a = mem + 0x4009;
+
+ while((c = getc(fp)) != EOF)
+ {
+ *a++ = c;
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ sprintf(message, "Failed to open file");
+ }
}
mem[CDFLAG] = 0xc0;
@@ -584,20 +723,37 @@ static int EDCallback(Z80 *z80, Z80Val data)
break;
case ED_PAUSE:
+ {
+ Z80Word pause;
+ struct timespec start;
+
waitkey = TRUE;
pause = z80->BC.w;
while(pause-- && !(mem[CDFLAG]&1))
{
+ struct timespec end;
+ struct timespec pause = {0};
+
ClearScreen();
PressKey(getch());
CheckTimers(z80, FRAME_TSTATES);
RenderScreen();
+
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ start = end;
+ pause.tv_nsec = 20000000 - TimeDiff(&start, &end);
+
+ if (pause.tv_nsec > 0 && FRAME_TSTATES != FAST_TSTATES)
+ {
+ nanosleep(&pause, NULL);
+ }
}
waitkey = FALSE;
break;
+ }
}
return TRUE;
@@ -840,6 +996,8 @@ int main(void)
setlocale(LC_ALL, "");
+ ReadConfig();
+
z80 = Z80Init(ZX81ReadMem,
ZX81WriteMem,
ZX81ReadPort,
@@ -862,14 +1020,14 @@ int main(void)
clock_gettime(CLOCK_MONOTONIC, &start);
- ClearScreen();
-
while(!quit)
{
struct timespec end;
struct timespec pause = {0};
int ch;
+ ClearScreen();
+
Z80Exec(z80);
ch = getch();