/* casm - Simple, portable assembler Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.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 . ------------------------------------------------------------------------- ZX81 tape output handler. */ #include #include #include #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 */