aboutsummaryrefslogtreecommitdiff
path: root/src/zx81out.c
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2016-04-06 23:37:07 +0100
committerIan C <ianc@noddybox.co.uk>2016-04-06 23:37:07 +0100
commitd99a9070450157dd8fc3e5d6ad5172af4cc0be59 (patch)
tree55d0b494d0ea1d1df8df08fe9f95495a5b7acc98 /src/zx81out.c
parentfdef5d70c1890c7970f31061184a73e699ccd9fb (diff)
Added ZX81 output driver
Diffstat (limited to 'src/zx81out.c')
-rw-r--r--src/zx81out.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/zx81out.c b/src/zx81out.c
new file mode 100644
index 0000000..fe4d83c
--- /dev/null
+++ b/src/zx81out.c
@@ -0,0 +1,268 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 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 3 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, see <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ ZX81 tape output handler.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "global.h"
+#include "codepage.h"
+#include "zx81out.h"
+
+
+/* ---------------------------------------- MACROS & TYPES
+*/
+
+enum option_t
+{
+ OPT_MARGIN,
+ OPT_AUTORUN,
+ OPT_DFILE_COLLAPSED
+};
+
+static const ValueTable option_set[] =
+{
+ {"zx81-margin", OPT_MARGIN},
+ {"zx81-autorun", OPT_AUTORUN},
+ {"zx81-collapse-dfile", OPT_DFILE_COLLAPSED},
+ {NULL}
+};
+
+typedef enum
+{
+ PAL = 55,
+ NTSC = 31
+} TVFormat;
+
+static ValueTable format_table[] =
+{
+ {"pal", PAL},
+ {"NTSC", NTSC},
+ {NULL}
+};
+
+static TVFormat tv_format = PAL;
+static int run = TRUE;
+static int collapse_dfile = FALSE;
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+
+static int PokeB(Byte *mem, int addr, Byte b)
+{
+ mem[addr++] = b;
+ return (addr % 0x10000);
+}
+
+
+static int PokeW(Byte *mem, int addr, int w)
+{
+ addr = PokeB(mem, addr, w & 0xff);
+ return PokeB(mem, addr, (w & 0xff00) >> 8);
+}
+
+
+/* ---------------------------------------- INTERFACES
+*/
+const ValueTable *ZX81OutputOptions(void)
+{
+ return option_set;
+}
+
+CommandStatus ZX81OutputSetOption(int opt, int argc, char *argv[],
+ int quoted[],
+ char *err, size_t errsize)
+{
+ const ValueTable *val;
+
+ CMD_ARGC_CHECK(1);
+
+ switch(opt)
+ {
+ case OPT_MARGIN:
+ CMD_TABLE(argv[0], format_table, val);
+ tv_format = val->value;
+ break;
+
+ case OPT_AUTORUN:
+ run = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ case OPT_DFILE_COLLAPSED:
+ collapse_dfile = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ default:
+ break;
+ }
+
+ return CMD_OK;
+}
+
+int ZX81Output(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count, char *error, size_t error_size)
+{
+ static const int line[] =
+ {
+ 0, 10, 14, 0, 0xf9, 0xd4, 0x1d, 0x22, 0x21, 0x1d,
+ 0x20, 0x7e, 0x8f, 0x01, 0x04, 0x00, 0x00, 0x76, -1
+ };
+
+ FILE *fp = fopen(filename, "wb");
+ Byte *mem;
+ int min;
+ int max;
+ int len;
+ int next;
+ int dfile;
+ int vars;
+ int addr;
+ int f;
+
+ mem = bank[0]->memory;
+ min = bank[0]->min_address_used;
+ max = bank[0]->max_address_used;
+
+ len = max - min + 1;
+
+ if (min != 16514)
+ {
+ snprintf(error, error_size, "Code must start at 16514 to work with the "
+ "ZX81 output driver.");
+ return FALSE;
+ }
+
+ if (!fp)
+ {
+ snprintf(error, error_size, "Failed to create %s", filename);
+ return FALSE;
+ }
+
+ /* Create the system variables and BASIC program around the supplied code.
+ BASIC is done first to calculate various system variables.
+ */
+ addr = 16509;
+
+ /* Program line with REM statement
+ */
+ addr = PokeW(mem, addr, 0x0000); /* Line number */
+ addr = PokeW(mem, addr, len + 2); /* Line length */
+ addr = PokeB(mem, addr, 0xea); /* REM token */
+
+ addr += len; /* Skip code */
+
+ addr = PokeB(mem, addr, 0x76); /* NL token */
+
+ /* Program line to launch the code
+ */
+ next = addr;
+
+ f = 0;
+ while (line[f] != -1)
+ {
+ addr = PokeB(mem, addr, line[f++]);
+ }
+
+ /* Display file
+ */
+ dfile = addr;
+
+ if (collapse_dfile)
+ {
+ for(f = 0; f < 25; f++)
+ {
+ addr = PokeB(mem, addr, 0x76);
+ }
+ }
+ else
+ {
+ addr = PokeB(mem, addr, 0x76);
+
+ for(f = 0; f < 24; f++)
+ {
+ int n;
+
+ for(n = 0; n < 32; n++)
+ {
+ addr = PokeB(mem, addr, 0);
+ }
+
+ addr = PokeB(mem, addr, 0x76);
+ }
+ }
+
+ /* Vars
+ */
+ vars = addr;
+ addr = PokeB(mem, addr, 0x80);
+
+ /* System variables
+ */
+ addr = 0x4009;
+
+ addr = PokeB(mem, addr, 0); /* VERSN */
+ addr = PokeW(mem, addr, 0); /* E_PPC */
+ addr = PokeW(mem, addr, dfile); /* D_FILE */
+ addr = PokeW(mem, addr, dfile + 1); /* DF_CC */
+ addr = PokeW(mem, addr, vars); /* VARS */
+ addr = PokeW(mem, addr, 0); /* DEST */
+ addr = PokeW(mem, addr, vars + 1); /* E_LINE */
+ addr = PokeW(mem, addr, vars - 1); /* CH_ADD */
+ addr = PokeW(mem, addr, 0); /* X_PTR */
+ addr = PokeW(mem, addr, vars + 5); /* STKBOT */
+ addr = PokeW(mem, addr, vars + 5); /* STKEND */
+ addr = PokeB(mem, addr, 0); /* BREG */
+ addr = PokeW(mem, addr, 16477); /* MEM */
+ addr = PokeB(mem, addr, 0); /* unused */
+ addr = PokeB(mem, addr, 2); /* DF_SZ */
+ addr = PokeW(mem, addr, 2); /* S_TOP */
+ addr = PokeW(mem, addr, 0xffff); /* LAST_K */
+ addr = PokeB(mem, addr, 0xff); /* LAST_K */
+ addr = PokeB(mem, addr, tv_format); /* MARGIN */
+ addr = PokeW(mem, addr, run ? next : dfile);/* NXTLIN */
+ addr = PokeW(mem, addr, 0); /* OLDPPC */
+ addr = PokeB(mem, addr, 0); /* FLAGX */
+ addr = PokeW(mem, addr, 0); /* STRLEN */
+ addr = PokeW(mem, addr, 0xc8d); /* T_ADDR */
+ addr = PokeW(mem, addr, 0); /* SEED */
+ addr = PokeW(mem, addr, 0x0ffff); /* FRAMES */
+ addr = PokeW(mem, addr, 0); /* COORDS */
+ addr = PokeB(mem, addr, 0xbc); /* PR_CC */
+ addr = PokeB(mem, addr, 33); /* S_POSN */
+ addr = PokeB(mem, addr, 24); /* S_POSN */
+ addr = PokeW(mem, addr, 0x40); /* CDFLAG */
+
+ /* Write the constructed P file
+ */
+ fwrite(mem + 0x4009, vars - 0x4009 + 1, 1, fp);
+
+ fclose(fp);
+
+ return TRUE;
+}
+
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/