From d99a9070450157dd8fc3e5d6ad5172af4cc0be59 Mon Sep 17 00:00:00 2001 From: Ian C Date: Wed, 6 Apr 2016 23:37:07 +0100 Subject: Added ZX81 output driver --- src/Makefile | 12 ++- src/casm.c | 11 ++- src/codepage.c | 10 +- src/example/Makefile | 14 ++- src/example/zx81.asm | 34 +++++++ src/label.c | 8 +- src/output.c | 14 +-- src/output.h | 7 ++ src/rawout.c | 2 +- src/specout.c | 2 +- src/t64out.c | 2 +- src/t64out.h | 2 +- src/zx81out.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/zx81out.h | 56 +++++++++++ 14 files changed, 411 insertions(+), 31 deletions(-) create mode 100644 src/example/zx81.asm create mode 100644 src/zx81out.c create mode 100644 src/zx81out.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 0812105..29ef34c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,8 @@ SOURCE = casm.c \ z80.c \ rawout.c \ specout.c \ - t64out.c + t64out.c \ + zx81out.c OBJECTS = casm.o \ expr.o \ @@ -60,7 +61,8 @@ OBJECTS = casm.o \ z80.o \ rawout.o \ specout.o \ - t64out.o + t64out.o \ + zx81out.o $(TARGET): $(OBJECTS) $(CC) $(CLAGS) -o $(TARGET) $(OBJECTS) @@ -73,7 +75,7 @@ clean: alias.o: alias.c global.h basetype.h util.h state.h alias.h casm.o: casm.c global.h basetype.h util.h state.h expr.h label.h macro.h \ cmd.h parse.h codepage.h stack.h listing.h alias.h output.h rawout.h \ - specout.h z80.h 6502.h + specout.h t64out.h zx81out.h z80.h 6502.h codepage.o: codepage.c global.h basetype.h util.h state.h codepage.h \ parse.h cmd.h expr.o: expr.c global.h basetype.h util.h state.h expr.h label.h @@ -84,7 +86,7 @@ listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \ macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.h \ cmd.h varchar.h macro.h output.o: output.c global.h basetype.h util.h state.h output.h parse.h \ - cmd.h rawout.h specout.h t64out.h + cmd.h rawout.h specout.h t64out.h zx81out.h parse.o: parse.c global.h basetype.h util.h state.h codepage.h parse.h \ cmd.h rawout.o: rawout.c global.h basetype.h util.h state.h rawout.h parse.h \ @@ -100,3 +102,5 @@ varchar.o: varchar.c global.h basetype.h util.h state.h codepage.h \ parse.h cmd.h varchar.h z80.o: z80.c global.h basetype.h util.h state.h expr.h label.h parse.h \ cmd.h codepage.h varchar.h z80.h +zx81out.o: zx81out.c global.h basetype.h util.h state.h codepage.h \ + parse.h cmd.h zx81out.h diff --git a/src/casm.c b/src/casm.c index 7624978..bad07f1 100644 --- a/src/casm.c +++ b/src/casm.c @@ -40,10 +40,7 @@ #include "stack.h" #include "listing.h" #include "alias.h" - #include "output.h" -#include "rawout.h" -#include "specout.h" /* ---------------------------------------- PROCESSORS */ @@ -404,6 +401,14 @@ static CommandStatus OPTION(const char *label, int argc, char *argv[], { return SpecTAPOutputSetOption(entry->value, ac, args, q, err, errsize); } + else if ((entry = ParseTable(opt, T64OutputOptions()))) + { + return T64OutputSetOption(entry->value, ac, args, q, err, errsize); + } + else if ((entry = ParseTable(opt, ZX81OutputOptions()))) + { + return ZX81OutputSetOption(entry->value, ac, args, q, err, errsize); + } else if ((entry = ParseTable(opt, cpu->options()))) { return cpu->set_option(entry->value, ac, args, q, err, errsize); diff --git a/src/codepage.c b/src/codepage.c index 4cbc1bc..cecdf66 100644 --- a/src/codepage.c +++ b/src/codepage.c @@ -101,8 +101,8 @@ static CodepageDef cp_ascii[] = static CodepageDef cp_zx81[] = { - {' ', 0x00}, {'!', 0x00}, {'"', 0x0b}, {'#', 0x0c}, - {'$', 0x0d}, {'%', 0x00}, {'&', 0x00}, {'\'', 0x0b}, + {' ', 0x00}, {'!', 0x8f}, {'"', 0x0b}, {'#', 0x0c}, + {'$', 0x0d}, {'%', 0x00}, {'&', 0x00}, {'\'', 0x8b}, {'(', 0x10}, {')', 0x11}, {'*', 0x17}, {'+', 0x15}, {',', 0x1a}, {'-', 0x16}, {'.', 0x1b}, {'/', 0x24}, {'0', 0x1c}, {'1', 0x1d}, {'2', 0x1e}, {'3', 0x1f}, @@ -116,15 +116,15 @@ static CodepageDef cp_zx81[] = {'P', 0xb5}, {'Q', 0xb6}, {'R', 0xb7}, {'S', 0xb8}, {'T', 0xb9}, {'U', 0xba}, {'V', 0xbb}, {'W', 0xbc}, {'X', 0xbd}, {'Y', 0xbe}, {'Z', 0xbf}, {'[', 0x10}, - {'\\', 0x24}, {']', 0x11}, {'^', 0xde}, {'_', 0x80}, - {'`', 0x60}, {'a', 0x26}, {'b', 0x27}, {'c', 0x28}, + {'\\', 0x98}, {']', 0x11}, {'^', 0xde}, {'_', 0x80}, + {'`', 0x76}, {'a', 0x26}, {'b', 0x27}, {'c', 0x28}, {'d', 0x29}, {'e', 0x2a}, {'f', 0x2b}, {'g', 0x2c}, {'h', 0x2d}, {'i', 0x2e}, {'j', 0x2f}, {'k', 0x30}, {'l', 0x31}, {'m', 0x32}, {'n', 0x33}, {'o', 0x34}, {'p', 0x35}, {'q', 0x36}, {'r', 0x37}, {'s', 0x38}, {'t', 0x39}, {'u', 0x3a}, {'v', 0x3b}, {'w', 0x3c}, {'x', 0x3d}, {'y', 0x3e}, {'z', 0x3f}, {'{', 0x90}, - {'|', 0x00}, {'}', 0x91}, {'~', 0x96}, + {'|', 0x80}, {'}', 0x91}, {'~', 0x96}, {0, 0} }; diff --git a/src/example/Makefile b/src/example/Makefile index e9ddfce..7c53209 100644 --- a/src/example/Makefile +++ b/src/example/Makefile @@ -20,18 +20,24 @@ # Makefile for examples # -ALL = spectrum.tap c64.t64 # zx81.p +ALL = spectrum.tap c64.t64 zx81.p CASM = ../casm -all: $(ALL) +all: $(ALL) $(CASM) + +$(CASM): ../*.[ch] + cd .. ; make remake: clean all -spectrum.tap: spectrum.asm +spectrum.tap: spectrum.asm $(CASM) $(CASM) spectrum.asm -c64.t64: c64.asm +c64.t64: c64.asm $(CASM) $(CASM) c64.asm +zx81.p: zx81.asm $(CASM) + $(CASM) zx81.asm + clean: rm -f $(ALL) diff --git a/src/example/zx81.asm b/src/example/zx81.asm new file mode 100644 index 0000000..3a0021c --- /dev/null +++ b/src/example/zx81.asm @@ -0,0 +1,34 @@ + ; Simple example ZX81 code + ; + + option output-file,zx81.p + option output-format,zx81 + + option zx81-margin,pal + option zx81-autorun,on + option zx81-collapse-dfile,off + + option codepage,zx81 + +DFILE: equ 16396 + + org 16514 + + ld hl,(DFILE) + inc hl + ld de,hello +loop: + ld a,(de) + cp 255 + ret z + ld (hl),a + inc hl + inc de + + ld a,(hl) + cp $76 + jr nz,loop + inc hl + jr loop + +hello: db "HELLO|WORLD hello world (#69.99 well spent)",255 diff --git a/src/label.c b/src/label.c index c15627f..75270ef 100644 --- a/src/label.c +++ b/src/label.c @@ -277,17 +277,17 @@ int LabelExpand(const char *expr, int *result) { found = ParseBase(expr + 1, 16, result, '\0'); } - else if (last == 'h') + else if (last == 'h' || last == 'H') { - found = ParseBase(expr, 16, result, 'h'); + found = ParseBase(expr, 16, result, last); } else if (first == '%') { found = ParseBase(expr, 2, result, '\0'); } - else if (last == 'b') + else if (last == 'b' || last == 'B') { - found = ParseBase(expr, 2, result, 'b'); + found = ParseBase(expr, 2, result, last); } else if (first == '\'' && last == '\'' && len == 3) { diff --git a/src/output.c b/src/output.c index 4c03e4e..8476f9d 100644 --- a/src/output.c +++ b/src/output.c @@ -28,12 +28,6 @@ #include "global.h" #include "output.h" -#include "rawout.h" -#include "specout.h" -#include "t64out.h" -/* TODO #include "zx81out.h" */ - - /* ---------------------------------------- GLOBALS */ @@ -56,7 +50,8 @@ typedef enum { RAW, TAP, - T64 + T64, + ZX81 } Format; static char output[4096] = "output"; @@ -69,6 +64,7 @@ static ValueTable format_table[] = {"raw", RAW}, {"spectrum", TAP}, {"t64", T64}, + {"zx81", ZX81}, {NULL} }; @@ -142,6 +138,10 @@ int OutputCode(void) return T64Output(output, output_bank, bank, count, error, sizeof error); + case ZX81: + return ZX81Output(output, output_bank, bank, count, + error, sizeof error); + default: break; } diff --git a/src/output.h b/src/output.h index cb0e5ca..8ea1c1d 100644 --- a/src/output.h +++ b/src/output.h @@ -29,6 +29,13 @@ #include "parse.h" #include "cmd.h" +/* Pull in the output drivers +*/ +#include "rawout.h" +#include "specout.h" +#include "t64out.h" +#include "zx81out.h" + /* ---------------------------------------- INTERFACES */ diff --git a/src/rawout.c b/src/rawout.c index 35ff650..3f75b50 100644 --- a/src/rawout.c +++ b/src/rawout.c @@ -67,7 +67,7 @@ int RawOutput(const char *filename, const char *filename_bank, if (!(fp = fopen(name, "wb"))) { - snprintf(error, error_size, "Failed to open %s\n", name); + snprintf(error, error_size, "Failed to open %s", name); return FALSE; } diff --git a/src/specout.c b/src/specout.c index e5ed897..b29b6ba 100644 --- a/src/specout.c +++ b/src/specout.c @@ -81,7 +81,7 @@ int SpecTAPOutput(const char *filename, const char *filename_bank, if (!fp) { - snprintf(error, error_size, "Failed to create %s\n", filename); + snprintf(error, error_size, "Failed to create %s", filename); return FALSE; } diff --git a/src/t64out.c b/src/t64out.c index 0fd308f..6aa82f2 100644 --- a/src/t64out.c +++ b/src/t64out.c @@ -122,7 +122,7 @@ int T64Output(const char *filename, const char *filename_bank, if (!fp) { - snprintf(error, error_size, "Failed to create %s\n", filename); + snprintf(error, error_size, "Failed to create %s", filename); return FALSE; } diff --git a/src/t64out.h b/src/t64out.h index 2743d0e..b4c5103 100644 --- a/src/t64out.h +++ b/src/t64out.h @@ -43,7 +43,7 @@ CommandStatus T64OutputSetOption(int opt, int argc, char *argv[], size_t error_size); -/* Spectrum TAP output of assembly. Returns TRUE if OK, FALSE for failure. +/* C64 T64 tape output of assembly. Returns TRUE if OK, FALSE for failure. */ int T64Output(const char *filename, const char *filename_bank, MemoryBank **bank, int count, 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 . + + ------------------------------------------------------------------------- + + 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 +*/ diff --git a/src/zx81out.h b/src/zx81out.h new file mode 100644 index 0000000..e6f3190 --- /dev/null +++ b/src/zx81out.h @@ -0,0 +1,56 @@ +/* + + 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 . + + ------------------------------------------------------------------------- + + ZX81 .P file output + +*/ + +#ifndef CASM_ZX81OUT_H +#define CASM_ZX81OUT_H + +#include "parse.h" +#include "state.h" +#include "cmd.h" + +/* ---------------------------------------- INTERFACES +*/ + + +/* RAW Output options +*/ +const ValueTable *ZX81OutputOptions(void); + +CommandStatus ZX81OutputSetOption(int opt, int argc, char *argv[], + int quoted[], char *error, + size_t error_size); + + +/* ZX81 P file output of assembly. Returns TRUE if OK, FALSE for failure. +*/ +int ZX81Output(const char *filename, const char *filename_bank, + MemoryBank **bank, int count, + char *error, size_t error_size); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ -- cgit v1.2.3