diff options
Diffstat (limited to 'src/zx81out.c')
-rw-r--r-- | src/zx81out.c | 268 |
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 +*/ |