aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile15
-rw-r--r--src/casm.c2
-rw-r--r--src/example/Makefile5
-rw-r--r--src/example/nes.asm82
-rw-r--r--src/nesout.c352
-rw-r--r--src/nesout.h56
-rw-r--r--src/output.c8
-rw-r--r--src/output.h1
8 files changed, 514 insertions, 7 deletions
diff --git a/src/Makefile b/src/Makefile
index fe9fc6d..e95a5c8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -48,7 +48,8 @@ SOURCE = casm.c \
zx81out.c \
gbout.c \
snesout.c \
- libout.c
+ libout.c \
+ nesout.c
OBJECTS = casm.o \
expr.o \
@@ -74,7 +75,8 @@ OBJECTS = casm.o \
zx81out.o \
gbout.o \
snesout.o \
- libout.o
+ libout.o \
+ nesout.o
$(TARGET): $(OBJECTS)
$(CC) $(CLAGS) -o $(TARGET) $(OBJECTS)
@@ -89,8 +91,8 @@ 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 t64out.h zx81out.h gbout.h snesout.h libout.h z80.h 6502.h \
- gbcpu.h 65c816.h spc700.h
+ specout.h t64out.h zx81out.h gbout.h snesout.h libout.h nesout.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
expr.o: expr.c global.h basetype.h util.h state.h expr.h label.h
@@ -106,8 +108,11 @@ 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
macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.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
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
+ cmd.h rawout.h specout.h t64out.h zx81out.h gbout.h snesout.h libout.h \
+ nesout.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 \
diff --git a/src/casm.c b/src/casm.c
index f1de63b..c85c578 100644
--- a/src/casm.c
+++ b/src/casm.c
@@ -691,6 +691,8 @@ int main(int argc, char *argv[])
PushValTableHandler(ZX81OutputOptions(), ZX81OutputSetOption);
PushValTableHandler(GBOutputOptions(), GBOutputSetOption);
PushValTableHandler(SNESOutputOptions(), SNESOutputSetOption);
+ PushValTableHandler(LibOutputOptions(), LibOutputSetOption);
+ PushValTableHandler(NESOutputOptions(), NESOutputSetOption);
ClearState();
diff --git a/src/example/Makefile b/src/example/Makefile
index 3c57e50..e92b2ec 100644
--- a/src/example/Makefile
+++ b/src/example/Makefile
@@ -20,7 +20,7 @@
# Makefile for examples
#
-ALL = spectrum.tap c64.t64 zx81.p gb.gb vcs.bin snes.sfc
+ALL = spectrum.tap c64.t64 zx81.p gb.gb vcs.bin snes.sfc nes.nes
CASM = ../casm
all: $(ALL) $(CASM)
@@ -48,5 +48,8 @@ vcs.bin: vcs.asm $(CASM)
snes.sfc: snes.asm $(CASM)
$(CASM) snes.asm
+nes.nes: nes.asm $(CASM)
+ $(CASM) nes.asm
+
clean:
rm -f $(ALL)
diff --git a/src/example/nes.asm b/src/example/nes.asm
new file mode 100644
index 0000000..c10ca66
--- /dev/null
+++ b/src/example/nes.asm
@@ -0,0 +1,82 @@
+ processor 6502
+
+ option output-file,"nes.nes"
+ option output-format,nes
+
+ option nes-vector,reset,start
+ option nes-vector,nmi,nmi
+ option nes-vector,brk,nmi
+
+start: org $c000
+
+ ;
+ ; Code taken from NES example
+ ;
+
+ sei ; disable IRQs
+ cld ; disable decimal mode
+ ldx #$40
+ stx $4017 ; dsiable APU frame IRQ
+ ldx #$ff ; Set up stack
+ txs ; .
+ inx ; now X = 0
+ stx $2000 ; disable NMI
+ stx $2001 ; disable rendering
+ stx $4010 ; disable DMC IRQs
+
+ ;; first wait for vblank to make sure PPU is ready
+vblankwait1:
+ bit $2002
+ bpl vblankwait1
+
+clear_memory:
+ lda #$00
+ sta $0000, x
+ sta $0100, x
+ sta $0200, x
+ sta $0300, x
+ sta $0400, x
+ sta $0500, x
+ sta $0600, x
+ sta $0700, x
+ inx
+ bne clear_memory
+
+ ;; second wait for vblank, PPU is ready after this
+vblankwait2:
+ bit $2002
+ bpl vblankwait2
+
+clear_palette:
+ ;; Need clear both palettes to $00. Needed for Nestopia. Not
+ ;; needed for FCEU* as they're already $00 on powerup.
+ lda $2002 ; Read PPU status to reset PPU address
+ lda #$3f ; Set PPU address to BG palette RAM ($3F00)
+ sta $2006
+ lda #$00
+ sta $2006
+
+ ldx #$20 ; Loop $20 times (up to $3F20)
+ lda #$00 ; Set each entry to $00
+.loop
+ sta $2007
+ dex
+ bne loop
+
+ lda #%01000000 ; intensify blues
+ sta $2001
+
+forever:
+ jmp forever
+
+nmi:
+ rti
+
+
+;
+; Dummy VROM
+;
+ org 0
+ bank 1
+
+ db 0
diff --git a/src/nesout.c b/src/nesout.c
new file mode 100644
index 0000000..7877e12
--- /dev/null
+++ b/src/nesout.c
@@ -0,0 +1,352 @@
+/*
+
+ 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 <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ NES ROM output handler.
+
+ Notes:
+
+ * ROM is 0x8000 to 0xffff (just 0xc000 to 0xffff if 16K ROM)
+
+ * VROM is at 0x0000 to 0x1fff.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "global.h"
+#include "expr.h"
+#include "codepage.h"
+#include "nesout.h"
+
+
+/* ---------------------------------------- MACROS & TYPES
+*/
+
+enum option_t
+{
+ OPT_VECTOR,
+ OPT_TV_FORMAT,
+ OPT_MAPPER
+};
+
+static const ValueTable option_set[] =
+{
+ {"nes-vector", OPT_VECTOR},
+ {"nes-tv-format", OPT_TV_FORMAT},
+ {"nes-mapper", OPT_MAPPER},
+ {NULL}
+};
+
+typedef enum
+{
+ VECTOR_RESET,
+ VECTOR_NMI,
+ VECTOR_BRK
+} VectorType;
+
+static ValueTable vector_table[] =
+{
+ {"reset", VECTOR_RESET},
+ {"nmi", VECTOR_NMI},
+ {"brk", VECTOR_BRK},
+ {NULL}
+};
+
+typedef enum
+{
+ PAL = 1,
+ NTSC = 0
+} TVFormat;
+
+static ValueTable format_table[] =
+{
+ {"pal", PAL},
+ {"NTSC", NTSC},
+ {NULL}
+};
+
+
+static struct
+{
+ int vector[3];
+ TVFormat tv_format;
+ int mapper;
+} option =
+{
+ {-1, -1, -1}, PAL, 0
+};
+
+
+/* ---------------------------------------- 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, int maxlen, char pad)
+{
+ while(*str && maxlen--)
+ {
+ addr = PokeB(mem, addr, CodeFromNative(CP_ASCII, *str++));
+ }
+
+ while(maxlen-- > 0)
+ {
+ addr = PokeB(mem, addr, CodeFromNative(CP_ASCII, pad));
+ }
+
+ return addr;
+}
+
+static unsigned CalcChecksum(Byte *p, int len, unsigned csum)
+{
+ while(len-- > 0)
+ {
+ csum += *p++;
+ }
+
+ return csum & 0xffff;
+}
+
+
+/* ---------------------------------------- INTERFACES
+*/
+const ValueTable *NESOutputOptions(void)
+{
+ return option_set;
+}
+
+CommandStatus NESOutputSetOption(int opt, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ const ValueTable *val;
+ int f;
+
+ CMD_ARGC_CHECK(1);
+
+ switch(opt)
+ {
+ case OPT_VECTOR:
+ CMD_ARGC_CHECK(2);
+ CMD_TABLE(argv[0], vector_table, val);
+ CMD_EXPR(argv[1], f);
+ option.vector[val->value] = f;
+ break;
+
+ case OPT_TV_FORMAT:
+ CMD_TABLE(argv[0], format_table, val);
+ option.tv_format = val->value;
+ break;
+
+ case OPT_MAPPER:
+ CMD_EXPR(argv[1], f);
+ option.mapper = f;
+ break;
+
+ default:
+ break;
+ }
+
+ return CMD_OK;
+
+}
+
+int NESOutput(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count, char *error, size_t error_size)
+{
+ FILE *fp;
+ Byte *mem;
+ int num_rom;
+ int num_vrom;
+ int is_16k;
+ int first_code;
+ int start;
+ int f;
+
+ /* Go through the banks and see which are code (ROM), and which char (VROM)
+ */
+ num_rom = 0;
+ num_vrom = 0;
+ is_16k = FALSE;
+ first_code = -1;
+
+ for(f = 0; f < count; f++)
+ {
+ if (bank[f]->max_address_used < 0x2000)
+ {
+ num_vrom++;
+ }
+ else if (bank[f]->min_address_used >= 0xc000)
+ {
+ if (first_code == -1)
+ {
+ first_code = f;
+ }
+
+ is_16k = TRUE;
+ num_rom++;
+ }
+ else if (bank[f]->min_address_used >= 0x8000)
+ {
+ if (first_code == -1)
+ {
+ first_code = f;
+ }
+
+ is_16k = FALSE;
+ num_rom++;
+ }
+ else
+ {
+ snprintf(error, error_size,
+ "Banks should use memory in the range 0x0000 - 0x1fff "
+ "to indicate they are\nvideo ROM, 0x8000 - 0xffff to "
+ "indicate they are program ROM or 0xc000 - 0xffff\n"
+ "to indicate a single 16K ROM segment.");
+
+ return FALSE;
+ }
+ }
+
+ if (first_code == -1)
+ {
+ snprintf(error, error_size,
+ "No ROM code banks present;\n"
+ "Banks should use memory in the range 0x0000 - 0x1fff "
+ "to indicate they are\nvideo ROM, 0x8000 - 0xffff to "
+ "indicate they are program ROM or 0xc000 - 0xffff\n"
+ "to indicate a single 16K ROM segment.");
+
+ return FALSE;
+ }
+
+ if (!(fp = fopen(filename, "wb")))
+ {
+ snprintf(error, error_size, "Failed to create %s", filename);
+ return FALSE;
+ }
+
+ /* Setup vectors
+ */
+ mem = bank[first_code]->memory;
+
+ start = option.vector[VECTOR_RESET];
+
+ if (start == -1)
+ {
+ start = is_16k ? 0xc000 : 0x8000;
+
+ fprintf(stderr, "WARNING: No reset vector provided; assuming 0x%4.4x\n",
+ start);
+ }
+
+ PokeW(mem, 0xfffc, start);
+
+ if (option.vector[VECTOR_NMI] != -1)
+ {
+ PokeW(mem, 0xfffa, option.vector[VECTOR_NMI]);
+ }
+ else
+ {
+ fprintf(stderr, "WARNING: NMI vector not set\n");
+ }
+
+ if (option.vector[VECTOR_BRK] != -1)
+ {
+ PokeW(mem, 0xfffe, option.vector[VECTOR_BRK]);
+ }
+
+ /* Output header
+ */
+ fputs("NES", fp);
+ fputc(26, fp);
+
+ if (is_16k)
+ {
+ fputc(1, fp);
+ }
+ else
+ {
+ fputc(num_rom * 2, fp);
+ }
+
+ fputc(num_vrom, fp);
+
+ fputc((option.mapper & 0x0f) << 4, fp);
+ fputc(option.mapper & 0xf0, fp);
+
+ fputc(0, fp);
+
+ fputc(option.tv_format, fp);
+
+ for(f = 0; f < 5; f++)
+ {
+ fputc(0, fp);
+ }
+
+ /* Output ROM contents, first code segments
+ */
+ for(f = 0; f < count; f++)
+ {
+ if (bank[f]->min_address_used > 0x2000)
+ {
+ if (is_16k)
+ {
+ fwrite(bank[f]->memory + 0xc000, 0x4000, 1, fp);
+ }
+ else
+ {
+ fwrite(bank[f]->memory + 0x8000, 0x8000, 1, fp);
+ }
+ }
+ }
+
+ /* Then video ROM banks
+ */
+ for(f = 0; f < count; f++)
+ {
+ if (bank[f]->min_address_used < 0x2000)
+ {
+ fwrite(bank[f]->memory, 0x2000, 1, fp);
+ }
+ }
+
+ fclose(fp);
+
+ return TRUE;
+}
+
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/nesout.h b/src/nesout.h
new file mode 100644
index 0000000..722db02
--- /dev/null
+++ b/src/nesout.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 <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ Gameboy ROM output
+
+*/
+
+#ifndef CASM_NESOUT_H
+#define CASM_NESOUT_H
+
+#include "parse.h"
+#include "state.h"
+#include "cmd.h"
+
+/* ---------------------------------------- INTERFACES
+*/
+
+
+/* NES Output options
+*/
+const ValueTable *NESOutputOptions(void);
+
+CommandStatus NESOutputSetOption(int opt, int argc, char *argv[],
+ int quoted[], char *error,
+ size_t error_size);
+
+
+/* NES ROM output of assembly. Returns TRUE if OK, FALSE for failure.
+*/
+int NESOutput(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/output.c b/src/output.c
index ac05216..1067b96 100644
--- a/src/output.c
+++ b/src/output.c
@@ -54,7 +54,8 @@ typedef enum
ZX81,
GAMEBOY,
SNES,
- LIBRARY
+ LIBRARY,
+ NES
} Format;
static char output[4096] = "output";
@@ -71,6 +72,7 @@ static ValueTable format_table[] =
{"gameboy", GAMEBOY},
{"snes", SNES},
{"lib", LIBRARY},
+ {"nes", NES},
{NULL}
};
@@ -160,6 +162,10 @@ int OutputCode(void)
return LibOutput(output, output_bank, bank, count,
error, sizeof error);
+ case NES:
+ return NESOutput(output, output_bank, bank, count,
+ error, sizeof error);
+
default:
break;
}
diff --git a/src/output.h b/src/output.h
index 03131e4..b2e208c 100644
--- a/src/output.h
+++ b/src/output.h
@@ -38,6 +38,7 @@
#include "gbout.h"
#include "snesout.h"
#include "libout.h"
+#include "nesout.h"
/* ---------------------------------------- INTERFACES
*/