From 25302a2513307a3a41b76f5e568f0916713c0620 Mon Sep 17 00:00:00 2001 From: Ian C Date: Tue, 21 Sep 2021 21:17:14 +0100 Subject: Imported V1.9 --- CHANGELOG | 5 ++ doc/casm.html | 78 +++++++++++++++++++ src/Makefile | 58 +++++++------- src/casm.c | 3 +- src/example/Makefile | 11 ++- src/example/prg.asm | 24 ++++++ src/example/t64.asm | 24 ++++++ src/output.c | 8 +- src/output.h | 1 + src/prgout.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/prgout.h | 56 ++++++++++++++ src/t64out.c | 55 +++++++++++-- 12 files changed, 500 insertions(+), 37 deletions(-) create mode 100644 CHANGELOG create mode 100644 src/example/prg.asm create mode 100644 src/example/t64.asm create mode 100644 src/prgout.c create mode 100644 src/prgout.h diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..b7112e8 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,5 @@ +V1.9 +==== +* Added CHANGELOG +* Added Commodore 64 PRG output. +* Added start-address option to T64 output. diff --git a/doc/casm.html b/doc/casm.html index c7e17ab..9d78b7b 100644 --- a/doc/casm.html +++ b/doc/casm.html @@ -1012,6 +1012,13 @@ A NES ROM file. An Amstrad CPC CDT file. + +prg + + +A Commodore 64 PRG file. + + @@ -1172,6 +1179,26 @@ to start the machine code, e.g.

Any remaining blocks will be stored as-is without any basic loader.

+

C64 T64 Tape Output Format options

+ +

The C64 T64 output driver supports the following settings that can be +set via an option command. +

+ + + + + + + + + +
OptionDescription
+option t64-start, address + +The start address for the generated BASIC loader. If left undefined defaults +to the start of written memory in the first bank. +

Nintendo Gameboy ROM File Output Format

@@ -1497,10 +1524,61 @@ The start address for the tape file. If left undefined defaults to the start of written memory. + +option cpc-loader, <on|off> + + +Whether the CDT file should contain a BASIC loader to load and run the machine +code. + + +

C64 PRG Output Format

+

+Generates a PRG file for an emulator or real hardware. +

+ +

The tape file will be given the same name as the output filename, and the +internal code block will also be given the same name, unless memory banks +have been used, in which case then each entry in the tape file will use the +output-bank setting to generate the filename for each entry. +

+ +

All banks will have a small BASIC program inserted as part of +the generated file. For this reason the bank should start near the BASIC +area (0x820 should be a safe place to start) unless you have a great +desire for a PRG full of zero bytes. This BASIC will simply hold a SYS command +to start the machine code, e.g. +

+ +
+10 SYS 2080
+
+ +

C64 PRG Output Format options

+ +

The C64 PRG output driver supports the following settings that can be +set via an option command. +

+ + + + + + + + + +
OptionDescription
+option prg-start, address + +The start address for the generated BASIC loader. If left undefined defaults +to the start of written memory in the bank. +
+

Listing

diff --git a/src/Makefile b/src/Makefile index 68d4ef3..448d332 100644 --- a/src/Makefile +++ b/src/Makefile @@ -50,7 +50,8 @@ SOURCE = casm.c \ snesout.c \ libout.c \ nesout.c \ - cpcout.c + cpcout.c \ + prgout.c OBJECTS = casm.o \ expr.o \ @@ -78,7 +79,8 @@ OBJECTS = casm.o \ snesout.o \ libout.o \ nesout.o \ - cpcout.o + cpcout.o \ + prgout.o $(TARGET): $(OBJECTS) $(CC) $(CLAGS) -o $(TARGET) $(OBJECTS) @@ -87,54 +89,56 @@ clean: rm -f $(TARGET) $(TARGET).exe $(OBJECTS) core *.core 6502.o: 6502.c global.h basetype.h util.h state.h expr.h label.h parse.h \ - cmd.h codepage.h 6502.h + cmd.h codepage.h 6502.h 65c816.o: 65c816.c global.h basetype.h util.h state.h expr.h label.h \ - parse.h cmd.h codepage.h 65c816.h + parse.h cmd.h codepage.h 65c816.h 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 t64out.h zx81out.h gbout.h snesout.h libout.h nesout.h \ - cpcout.h z80.h 6502.h gbcpu.h 65c816.h spc700.h + cmd.h parse.h codepage.h stack.h listing.h alias.h output.h rawout.h \ + specout.h t64out.h zx81out.h gbout.h snesout.h libout.h nesout.h \ + cpcout.h prgout.h z80.h 6502.h gbcpu.h 65c816.h spc700.h codepage.o: codepage.c global.h basetype.h util.h state.h codepage.h \ - parse.h cmd.h + parse.h cmd.h cpcout.o: cpcout.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h cpcout.h expr.h + cmd.h cpcout.h expr.h expr.o: expr.c global.h basetype.h util.h state.h expr.h label.h gbcpu.o: gbcpu.c global.h basetype.h util.h state.h expr.h label.h \ - parse.h cmd.h codepage.h varchar.h gbcpu.h + parse.h cmd.h codepage.h varchar.h gbcpu.h gbout.o: gbout.c global.h basetype.h util.h state.h expr.h codepage.h \ - parse.h cmd.h gbout.h + parse.h cmd.h gbout.h label.o: label.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h stack.h label.h + cmd.h stack.h label.h libout.o: libout.c global.h basetype.h util.h state.h libout.h parse.h \ - cmd.h label.h + cmd.h label.h listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \ - cmd.h parse.h expr.h varchar.h listing.h + cmd.h parse.h expr.h varchar.h listing.h macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h varchar.h macro.h + cmd.h varchar.h macro.h nesout.o: nesout.c global.h basetype.h util.h state.h expr.h codepage.h \ - parse.h cmd.h nesout.h + parse.h cmd.h nesout.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 zx81out.h gbout.h snesout.h libout.h \ - nesout.h cpcout.h + cmd.h rawout.h specout.h t64out.h zx81out.h gbout.h snesout.h libout.h \ + nesout.h cpcout.h prgout.h parse.o: parse.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h + cmd.h +prgout.o: prgout.c global.h basetype.h util.h state.h codepage.h parse.h \ + cmd.h prgout.h expr.h rawout.o: rawout.c global.h basetype.h util.h state.h rawout.h parse.h \ - cmd.h + cmd.h snesout.o: snesout.c global.h basetype.h util.h state.h expr.h codepage.h \ - parse.h cmd.h snesout.h + parse.h cmd.h snesout.h spc700.o: spc700.c global.h basetype.h util.h state.h expr.h label.h \ - parse.h cmd.h codepage.h spc700.h + parse.h cmd.h codepage.h spc700.h specout.o: specout.c global.h basetype.h util.h state.h specout.h parse.h \ - cmd.h expr.h + cmd.h expr.h stack.o: stack.c global.h basetype.h util.h state.h stack.h state.o: state.c global.h basetype.h util.h state.h expr.h t64out.o: t64out.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h t64out.h + cmd.h t64out.h expr.h util.o: util.c global.h basetype.h util.h state.h varchar.o: varchar.c global.h basetype.h util.h state.h codepage.h \ - parse.h cmd.h varchar.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 + 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 + parse.h cmd.h zx81out.h diff --git a/src/casm.c b/src/casm.c index 10c1725..55acca4 100644 --- a/src/casm.c +++ b/src/casm.c @@ -59,7 +59,7 @@ */ static const char *casm_usage = -"Version 1.8\n" +"Version 1.9\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" @@ -695,6 +695,7 @@ int main(int argc, char *argv[]) PushValTableHandler(LibOutputOptions(), LibOutputSetOption); PushValTableHandler(NESOutputOptions(), NESOutputSetOption); PushValTableHandler(CPCOutputOptions(), CPCOutputSetOption); + PushValTableHandler(PRGOutputOptions(), PRGOutputSetOption); ClearState(); diff --git a/src/example/Makefile b/src/example/Makefile index 93bef30..a76a21f 100644 --- a/src/example/Makefile +++ b/src/example/Makefile @@ -20,7 +20,8 @@ # Makefile for examples # -ALL = spectrum.tap c64.t64 zx81.p gb.gb vcs.bin snes.sfc nes.nes cpc.cdt +ALL = spectrum.tap t64.t64 zx81.p gb.gb vcs.bin snes.sfc nes.nes cpc.cdt \ + prg.prg CASM = ../casm all: $(ALL) $(CASM) @@ -33,8 +34,8 @@ remake: clean all spectrum.tap: spectrum.asm $(CASM) $(CASM) spectrum.asm -c64.t64: c64.asm $(CASM) - $(CASM) c64.asm +t64.t64: t64.asm $(CASM) + $(CASM) t64.asm cpc.cdt: cpc.asm $(CASM) $(CASM) cpc.asm @@ -54,5 +55,9 @@ snes.sfc: snes.asm $(CASM) nes.nes: nes.asm tiles.chr nes.pal $(CASM) $(CASM) nes.asm +prg.prg: prg.asm $(CASM) + $(CASM) prg.asm + clean: rm -f $(ALL) + diff --git a/src/example/prg.asm b/src/example/prg.asm new file mode 100644 index 0000000..7433117 --- /dev/null +++ b/src/example/prg.asm @@ -0,0 +1,24 @@ + ; Simple example C64 code + ; + + cpu 6502 + + option codepage,cbm + + option output-file,prg.prg + option output-format,prg + option prg-start,start + + org $820 + +main: + lda #0 + clc +loop: + sta 53280 + adc #1 + and #$0f + jmp loop + +start: + jmp main diff --git a/src/example/t64.asm b/src/example/t64.asm new file mode 100644 index 0000000..b5a6ded --- /dev/null +++ b/src/example/t64.asm @@ -0,0 +1,24 @@ + ; Simple example C64 code + ; + + cpu 6502 + + option codepage,cbm + + option output-file,t64.t64 + option output-format,t64 + option t64-start,start + + org $820 + +main: + lda #0 + clc +loop: + sta 53280 + adc #1 + and #$0f + jmp loop + +start: + jmp main diff --git a/src/output.c b/src/output.c index 66abfae..9a30a76 100644 --- a/src/output.c +++ b/src/output.c @@ -56,7 +56,8 @@ typedef enum SNES, LIBRARY, NES, - CPC + CPC, + PRG } Format; static char output[4096] = "output"; @@ -75,6 +76,7 @@ static ValueTable format_table[] = {"lib", LIBRARY}, {"nes", NES}, {"cpc", CPC}, + {"prg", PRG}, {NULL} }; @@ -172,6 +174,10 @@ int OutputCode(void) return CPCOutput(output, output_bank, bank, count, error, sizeof error); + case PRG: + return PRGOutput(output, output_bank, bank, count, + error, sizeof error); + default: break; } diff --git a/src/output.h b/src/output.h index 84d0e33..b63e3a0 100644 --- a/src/output.h +++ b/src/output.h @@ -40,6 +40,7 @@ #include "libout.h" #include "nesout.h" #include "cpcout.h" +#include "prgout.h" /* ---------------------------------------- INTERFACES */ diff --git a/src/prgout.c b/src/prgout.c new file mode 100644 index 0000000..d752c08 --- /dev/null +++ b/src/prgout.c @@ -0,0 +1,214 @@ +/* + + 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 . + + ------------------------------------------------------------------------- + + Commodore T64 tape output handler. + +*/ +#include +#include +#include + +#include "global.h" +#include "codepage.h" +#include "prgout.h" +#include "expr.h" + + +/* ---------------------------------------- MACROS & TYPES +*/ + +/* ---------------------------------------- PRIVATE TYPES AND VARS +*/ +enum option_t +{ + OPT_START_ADDR +}; + +static const ValueTable option_set[]= +{ + {"prg-start", OPT_START_ADDR}, + {NULL} +}; + +typedef struct +{ + int start_addr; +} Options; + +static Options options = +{ + -1 +}; + + +/* ---------------------------------------- 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); +} + + +static int PokeS(Byte *mem, int addr, const char *str) +{ + while(*str) + { + addr = PokeB(mem, addr, CodeFromNative(CP_CBM, *str++)); + } + + return addr; +} + + +static void WriteByte(FILE *fp, Byte b) +{ + putc(b, fp); +} + + +static void WriteWord(FILE *fp, int w) +{ + WriteByte(fp, w & 0xff); + WriteByte(fp, (w & 0xff00) >> 8); +} + + +/* ---------------------------------------- INTERFACES +*/ +const ValueTable *PRGOutputOptions(void) +{ + return option_set; +} + +CommandStatus PRGOutputSetOption(int opt, int argc, char *argv[], + int quoted[], char *err, size_t errsize) +{ + CommandStatus stat = CMD_OK; + + CMD_ARGC_CHECK(1); + + switch(opt) + { + case OPT_START_ADDR: + CMD_EXPR(argv[0], options.start_addr); + break; + + default: + break; + } + + return stat; +} + +int PRGOutput(const char *filename, const char *filename_bank, + MemoryBank **bank, int count, char *error, size_t error_size) +{ + int f; + + for(f = 0; f < count; f++) + { + FILE *fp; + char buff[4096]; + const char *name; + Byte *mem; + int min, max, len; + char sys[16]; + int addr = 0x803; + int next; + + if (count == 1) + { + name = filename; + } + else + { + snprintf(buff, sizeof buff, filename_bank, bank[f]->number); + name = buff; + } + + if (!(fp = fopen(name, "wb"))) + { + snprintf(error, error_size, "Failed to open %s", name); + return FALSE; + } + + mem = bank[f]->memory; + min = bank[f]->min_address_used; + max = bank[f]->max_address_used; + + /* We're going to prepend some BASIC + */ + if (min < 0x810) + { + snprintf(error, error_size, "Bank starts below a safe " + "area to add BASIC loader"); + + return FALSE; + } + + if (options.start_addr == -1) + { + snprintf(sys, sizeof sys, "%d", bank[f]->min_address_used); + } + else + { + snprintf(sys, sizeof sys, "%d", options.start_addr); + } + + addr = PokeW(mem, addr, 10); + addr = PokeB(mem, addr, 0x9e); + addr = PokeS(mem, addr, sys); + addr = PokeB(mem, addr, 0x00); + + next = addr; + + addr = PokeW(mem, addr, 0x00); + + PokeW(mem, 0x801, next); + + min = 0x801; /* Start of BASIC */ + + len = max - min + 1; + + /* Output PRG file + */ + WriteWord(fp, min); + fwrite(mem + min, len, 1, fp); + + fclose(fp); + } + + return TRUE; +} + + +/* +vim: ai sw=4 ts=8 expandtab +*/ diff --git a/src/prgout.h b/src/prgout.h new file mode 100644 index 0000000..668f48f --- /dev/null +++ b/src/prgout.h @@ -0,0 +1,56 @@ +/* + + 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 . + + ------------------------------------------------------------------------- + + Commodore PRG tape output + +*/ + +#ifndef CASM_PRGOUT_H +#define CASM_PRGOUT_H + +#include "parse.h" +#include "state.h" +#include "cmd.h" + +/* ---------------------------------------- INTERFACES +*/ + + +/* PRG Output options +*/ +const ValueTable *PRGOutputOptions(void); + +CommandStatus PRGOutputSetOption(int opt, int argc, char *argv[], + int quoted[], char *error, + size_t error_size); + + +/* C64 PRG output of assembly. Returns TRUE if OK, FALSE for failure. +*/ +int PRGOutput(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 +*/ diff --git a/src/t64out.c b/src/t64out.c index 18bfcba..e97e1ae 100644 --- a/src/t64out.c +++ b/src/t64out.c @@ -29,6 +29,7 @@ #include "global.h" #include "codepage.h" #include "t64out.h" +#include "expr.h" /* ---------------------------------------- MACROS & TYPES @@ -36,6 +37,30 @@ #define NOT_USED 0 +/* ---------------------------------------- PRIVATE TYPES AND VARS +*/ +enum option_t +{ + OPT_START_ADDR +}; + +static const ValueTable option_set[]= +{ + {"t64-start", OPT_START_ADDR}, + {NULL} +}; + +typedef struct +{ + int start_addr; +} Options; + +static Options options = +{ + -1 +}; + + /* ---------------------------------------- PRIVATE FUNCTIONS */ @@ -103,14 +128,27 @@ static void WriteString(FILE *fp, const char *p, int len, */ const ValueTable *T64OutputOptions(void) { - return NULL; + return option_set; } CommandStatus T64OutputSetOption(int opt, int argc, char *argv[], - int quoted[], - char *error, size_t error_size) + int quoted[], char *err, size_t errsize) { - return CMD_NOT_KNOWN; + CommandStatus stat = CMD_OK; + + CMD_ARGC_CHECK(1); + + switch(opt) + { + case OPT_START_ADDR: + CMD_EXPR(argv[0], options.start_addr); + break; + + default: + break; + } + + return stat; } int T64Output(const char *filename, const char *filename_bank, @@ -216,7 +254,14 @@ int T64Output(const char *filename, const char *filename_bank, int a = 0x803; int next; - snprintf(sys, sizeof sys, "%u", min); + if (options.start_addr == -1) + { + snprintf(sys, sizeof sys, "%u", min); + } + else + { + snprintf(sys, sizeof sys, "%d", options.start_addr); + } a = PokeW(mem, a, 10); a = PokeB(mem, a, 0x9e); -- cgit v1.2.3