├── .github ├── FUNDING.yml └── workflows │ └── c-cpp.yml ├── source ├── crc.c ├── expr.h ├── Makefile ├── nes.h ├── vars.h ├── protos.h ├── externs.h ├── defs.h ├── func.c ├── inst.h ├── input.c ├── output.c ├── macro.c ├── proc.c ├── assemble.c ├── symbol.c ├── nes.c ├── code.c ├── main.c └── expr.c ├── documentation ├── history.txt └── cpu_inst.txt └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ClusterM] 2 | custom: ["https://www.buymeacoffee.com/cluster", "https://boosty.to/cluster"] 3 | -------------------------------------------------------------------------------- /source/crc.c: -------------------------------------------------------------------------------- 1 | 2 | /* locals */ 3 | static unsigned int crc_table[256]; 4 | 5 | 6 | /* ---- 7 | * crc_init() 8 | * ---- 9 | */ 10 | 11 | void 12 | crc_init(void) 13 | { 14 | int i; 15 | unsigned int t, *p, *q; 16 | unsigned int poly = 0x864CFB; 17 | 18 | p = q = crc_table; 19 | *q++ = 0; 20 | *q++ = poly; 21 | 22 | for (i = 1; i < 128; i++) { 23 | t = *(++p); 24 | if (t & 0x800000) { 25 | t <<= 1; 26 | *q++ = t ^ poly; 27 | *q++ = t; 28 | } 29 | else { 30 | t <<= 1; 31 | *q++ = t; 32 | *q++ = t ^ poly; 33 | } 34 | } 35 | } 36 | 37 | 38 | /* ---- 39 | * crc_calc() 40 | * ---- 41 | * 24-bit crc 42 | */ 43 | 44 | unsigned int 45 | crc_calc(unsigned char *data, int len) 46 | { 47 | unsigned int crc = 0; 48 | int i; 49 | 50 | for (i = 0; i < len; i++) 51 | crc = (crc << 8) ^ crc_table[(unsigned char)(crc >> 16) ^ *data++]; 52 | 53 | /* ok */ 54 | return (crc & 0xFFFFFF); 55 | } 56 | 57 | -------------------------------------------------------------------------------- /source/expr.h: -------------------------------------------------------------------------------- 1 | /* value types */ 2 | #define T_DECIMAL 0 3 | #define T_HEXA 1 4 | #define T_BINARY 2 5 | #define T_CHAR 3 6 | #define T_SYMBOL 4 7 | #define T_PC 5 8 | 9 | /* operators */ 10 | #define OP_START 0 11 | #define OP_OPEN 1 12 | #define OP_ADD 2 13 | #define OP_SUB 3 14 | #define OP_MUL 4 15 | #define OP_DIV 5 16 | #define OP_MOD 6 17 | #define OP_NEG 7 18 | #define OP_SHL 8 19 | #define OP_SHR 9 20 | #define OP_OR 10 21 | #define OP_XOR 11 22 | #define OP_AND 12 23 | #define OP_COM 13 24 | #define OP_NOT 14 25 | #define OP_EQUAL 15 26 | #define OP_NOT_EQUAL 16 27 | #define OP_LOWER 17 28 | #define OP_LOWER_EQUAL 18 29 | #define OP_HIGHER 19 30 | #define OP_HIGHER_EQUAL 20 31 | #define OP_DEFINED 21 32 | #define OP_HIGH 22 33 | #define OP_LOW 23 34 | #define OP_PAGE 24 35 | #define OP_BANK 25 36 | #define OP_VRAM 26 37 | #define OP_PAL 27 38 | #define OP_SIZEOF 28 39 | 40 | /* operator priority */ 41 | int op_pri[] = { 42 | 0 /* START */, 0 /* OPEN */, 43 | 7 /* ADD */, 7 /* SUB */, 8 /* MUL */, 8 /* DIV */, 44 | 8 /* MOD */, 10 /* NEG */, 6 /* SHL */, 6 /* SHR */, 45 | 1 /* OR */, 2 /* XOR */, 3 /* AND */, 10 /* COM */, 46 | 9 /* NOT */, 4 /* = */, 4 /* <> */, 5 /* < */, 47 | 5 /* <= */, 5 /* > */, 5 /* >= */, 48 | 10 /* DEFIN.*/, 10 /* HIGH */, 10 /* LOW */, 10 /* PAGE */, 49 | 10 /* BANK */, 10 /* VRAM */, 10 /* PAL */, 10 /* SIZEOF*/ 50 | }; 51 | 52 | unsigned int op_stack[64] = { OP_START }; /* operator stack */ 53 | unsigned int val_stack[64]; /* value stack */ 54 | int op_idx, val_idx; /* index in the operator and value stacks */ 55 | int need_operator; /* when set await an operator, else await a value */ 56 | char *expr; /* pointer to the expression string */ 57 | char *expr_stack[16]; /* expression stack */ 58 | struct t_symbol *expr_lablptr; /* pointer to the lastest label */ 59 | int expr_lablcnt; /* number of label seen in an expression */ 60 | char *keyword[8] = { /* predefined functions */ 61 | "\7DEFINED", 62 | "\4HIGH", "\3LOW", 63 | "\4PAGE", "\4BANK", 64 | "\4VRAM", "\3PAL", 65 | "\6SIZEOF" 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: Build test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | 12 | build-linux: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: make clean all x64 18 | run: make -C source clean all 19 | - name: Upload artifact 20 | uses: actions/upload-artifact@v4 21 | with: 22 | name: nesasm_linux_x64 23 | path: . 24 | - name: apt update 25 | run: sudo apt-get update 26 | - name: Get ARM toolchain 27 | run: sudo apt-get install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu 28 | - name: make clean all arm 29 | run: make -C source clean all CC=arm-linux-gnueabihf-gcc 30 | - name: Upload artifact 31 | uses: actions/upload-artifact@v4 32 | with: 33 | name: nesasm_linux_arm 34 | path: . 35 | - name: make clean all arm64 36 | run: make -C source clean all CC=aarch64-linux-gnu-gcc 37 | - name: Upload artifact 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: nesasm_linux_arm64 41 | path: . 42 | 43 | build-macos: 44 | runs-on: macos-latest 45 | steps: 46 | - name: Install argp 47 | run: brew install argp-standalone 48 | - name: Checkout 49 | uses: actions/checkout@v4 50 | - name: make clean all 51 | run: make -C source clean all 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: nesasm_macos 56 | path: . 57 | 58 | build-windows: 59 | runs-on: windows-latest 60 | defaults: 61 | run: 62 | shell: msys2 {0} 63 | steps: 64 | - uses: msys2/setup-msys2@v2 65 | with: 66 | update: true 67 | install: >- 68 | base-devel 69 | gcc 70 | git 71 | libargp-devel 72 | - name: Checkout 73 | uses: actions/checkout@v4 74 | - name: make clean all 75 | run: make -C source clean all 76 | - name: Upload artifact 77 | uses: actions/upload-artifact@v4 78 | with: 79 | name: nesasm_windows 80 | path: . 81 | -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | .SUFFIXES: 3 | 4 | CC ?= cc 5 | RM ?= rm -f 6 | INSTALL ?= install -p 7 | COMMIT := 8 | COMMIT_INFO = commit.h 9 | 10 | EXTRA_CFLAGS ?= -O2 -Wall -Wno-restrict -Wno-unknown-warning-option 11 | 12 | IS_MACARM := 0 13 | 14 | # build on macos(arm) support 15 | ifneq ($(OS),Windows_NT) 16 | UNAME_S := $(shell uname -s) 17 | ifeq ($(UNAME_S),Darwin) 18 | UNAME_P := $(shell uname -p) 19 | ifneq ($(filter arm%,$(UNAME_P)),) 20 | EXTRA_CFLAGS += -I$(shell brew --prefix)/include 21 | IS_MACARM = 1 22 | endif 23 | endif 24 | endif 25 | 26 | CFLAGS += ${EXTRA_CFLAGS} -I. 27 | 28 | EXEDIR ?= .. 29 | PREFIX ?= /usr 30 | BINDIR ?= $(PREFIX)/bin 31 | 32 | ifeq ($(OS),Windows_NT) 33 | PROGRAM = $(EXEDIR)/nesasm.exe 34 | else 35 | PROGRAM = $(EXEDIR)/nesasm 36 | endif 37 | 38 | ifeq ($(COMMIT),) 39 | COMMIT = $(shell git rev-parse --short HEAD) 40 | else 41 | COMMIT += $(shell git diff-index HEAD) (dirty) 42 | endif 43 | 44 | ifeq ($(OS),Windows_NT) 45 | LDFLAGS += -largp 46 | $(PROGRAM) = $(EXEDIR)/$(PROGRAM).exe 47 | else 48 | UNAME_S := $(shell uname -s) 49 | ifeq ($(UNAME_S),Darwin) 50 | LDFLAGS += -largp 51 | 52 | # build on macos(arm) support 53 | ifeq ($(IS_MACARM), 1) 54 | LDFLAGS += -L$(shell brew --prefix)/lib 55 | endif 56 | endif 57 | endif 58 | 59 | # Object files 60 | 61 | OBJS = main.o input.o assemble.o expr.o code.o command.o\ 62 | macro.o func.o proc.o symbol.o output.o crc.o\ 63 | nes.o 64 | 65 | # All *.c file compiled to *.o 66 | .SUFFIXES: .c .o 67 | .c.o: 68 | $(CC) $(CFLAGS) -o $@ -c $< 69 | 70 | # default target 71 | all: commit_file $(PROGRAM) 72 | 73 | commit_file: 74 | @printf "#define COMMIT \"$(COMMIT)\"\\n" > $(COMMIT_INFO) 75 | 76 | clean: 77 | 78 | ifeq ($(OS),Windows_NT) 79 | @if [ -f "$(PROGRAM)" ]; then for f in `cygcheck "$(PROGRAM)" | grep .dll | grep msys` ; do rm -f $(EXEDIR)/`basename "$$f"` ; done fi 80 | endif 81 | 82 | $(RM) $(OBJS) $(PROGRAM) $(COMMIT_INFO) 83 | 84 | # Link objects into executable file 85 | 86 | $(PROGRAM): $(OBJS) 87 | $(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS) $(LDADD) 88 | 89 | ifeq ($(OS),Windows_NT) 90 | @for f in `cygcheck "$(PROGRAM)" | grep .dll | grep msys` ; do cp -vf `cygpath "$$f"` $(EXEDIR)/ ; done 91 | endif 92 | 93 | .PHONY: all commit_file clean 94 | 95 | -------------------------------------------------------------------------------- /source/nes.h: -------------------------------------------------------------------------------- 1 | 2 | /* NES.C */ 3 | void nes_write_header(FILE *f, int banks); 4 | int nes_pack_8x8_tile(unsigned char *buffer, void *data, int line_offset, int format); 5 | void nes_defchr(int *ip); 6 | void nes_inesprg(int *ip); 7 | void nes_ineschr(int *ip); 8 | void nes_inesprgram(int *ip); 9 | void nes_inesprgnvram(int *ip); 10 | void nes_ineschrram(int *ip); 11 | void nes_ineschrnvram(int *ip); 12 | void nes_inesmap(int *ip); 13 | void nes_inessubmap(int *ip); 14 | void nes_inesmir(int *ip); 15 | void nes_inesbat(int *ip); 16 | void nes_inestim(int *ip); 17 | 18 | /* NES specific pseudos */ 19 | struct t_opcode nes_pseudo[] = { 20 | {NULL, "DEFCHR", nes_defchr, PSEUDO, P_DEFCHR, 0}, 21 | {NULL, "INESPRG", nes_inesprg, PSEUDO, P_INESPRG, 0}, 22 | {NULL, "INESCHR", nes_ineschr, PSEUDO, P_INESCHR, 0}, 23 | {NULL, "INESPRGRAM", nes_inesprgram, PSEUDO, P_INESPRGRAM, 0}, 24 | {NULL, "INESCHRRAM", nes_ineschrram, PSEUDO, P_INESCHRRAM, 0}, 25 | {NULL, "INESPRGNVRAM", nes_inesprgnvram, PSEUDO, P_INESPRGNVRAM, 0}, 26 | {NULL, "INESCHRNVRAM", nes_ineschrnvram, PSEUDO, P_INESCHRNVRAM, 0}, 27 | {NULL, "INESMAP", nes_inesmap, PSEUDO, P_INESMAP, 0}, 28 | {NULL, "INESSUBMAP", nes_inessubmap, PSEUDO, P_INESSUBMAP, 0}, 29 | {NULL, "INESMIR", nes_inesmir, PSEUDO, P_INESMIR, 0}, 30 | {NULL, "INESBAT", nes_inesbat, PSEUDO, P_INESBAT, 0}, 31 | {NULL, "INESTIM", nes_inestim, PSEUDO, P_INESTIM, 0}, 32 | 33 | {NULL, ".DEFCHR", nes_defchr, PSEUDO, P_DEFCHR, 0}, 34 | {NULL, ".INESPRG", nes_inesprg, PSEUDO, P_INESPRG, 0}, 35 | {NULL, ".INESCHR", nes_ineschr, PSEUDO, P_INESCHR, 0}, 36 | {NULL, ".INESPRGRAM", nes_inesprgram, PSEUDO, P_INESPRGRAM, 0}, 37 | {NULL, ".INESCHRRAM", nes_ineschrram, PSEUDO, P_INESCHRRAM, 0}, 38 | {NULL, ".INESPRGNVRAM", nes_inesprgnvram, PSEUDO, P_INESPRGNVRAM, 0}, 39 | {NULL, ".INESCHRNVRAM", nes_ineschrnvram, PSEUDO, P_INESCHRNVRAM, 0}, 40 | {NULL, ".INESMAP", nes_inesmap, PSEUDO, P_INESMAP, 0}, 41 | {NULL, ".INESSUBMAP", nes_inessubmap, PSEUDO, P_INESSUBMAP, 0}, 42 | {NULL, ".INESMIR", nes_inesmir, PSEUDO, P_INESMIR, 0}, 43 | {NULL, ".INESBAT", nes_inesbat, PSEUDO, P_INESBAT, 0}, 44 | {NULL, ".INESTIM", nes_inestim, PSEUDO, P_INESTIM, 0}, 45 | 46 | {NULL, NULL, NULL, 0, 0, 0} 47 | }; 48 | 49 | /* NES machine description */ 50 | struct t_machine nes = { 51 | MACHINE_NES, /* type */ 52 | "NESASM", /* asm_name */ 53 | "NES Assembler (v3.0)", /* asm_title */ 54 | ".nes", /* rom_ext */ 55 | "NES_INCLUDE", /* include_env */ 56 | 0x100, /* zp_limit */ 57 | 0x800, /* ram_limit */ 58 | 0, /* ram_base */ 59 | 0, /* ram_page */ 60 | RESERVED_BANK, /* ram_bank */ 61 | NULL, /* inst */ 62 | nes_pseudo, /* pseudo_inst */ 63 | nes_pack_8x8_tile, /* pack_8x8_tile */ 64 | NULL, /* pack_16x16_tile */ 65 | NULL, /* pack_16x16_sprite */ 66 | nes_write_header /* write_header */ 67 | }; 68 | -------------------------------------------------------------------------------- /source/vars.h: -------------------------------------------------------------------------------- 1 | unsigned char rom[MAX_BANKS][BANK_SIZE]; 2 | unsigned char map[MAX_BANKS][BANK_SIZE]; 3 | char bank_name[MAX_BANKS][64]; 4 | int bank_loccnt[4][MAX_BANKS]; 5 | int bank_page[4][MAX_BANKS]; 6 | int max_zp; /* higher used address in zero page */ 7 | int max_bss; /* higher used address in ram */ 8 | int max_bank; /* last bank used */ 9 | int data_loccnt; /* data location counter */ 10 | int data_size; /* size of binary output (in bytes) */ 11 | int data_level; /* data output level, must be <= listlevel to be outputed */ 12 | int loccnt; /* location counter */ 13 | int bank; /* current bank */ 14 | int bank_base; /* bank base index */ 15 | int rom_limit; /* bank limit */ 16 | int bank_limit; /* rom max. size in bytes */ 17 | int page; /* page */ 18 | int rsbase; /* .rs counter */ 19 | int section; /* current section: S_ZP, S_BSS, S_CODE or S_DATA */ 20 | int section_bank[4]; /* current bank for each section */ 21 | int stop_pass; /* stop the program; set by fatal_error() */ 22 | int errcnt; /* error counter */ 23 | struct t_machine *machine; 24 | struct t_opcode *inst_tbl[256]; /* instructions hash table */ 25 | struct t_symbol *hash_tbl[256]; /* label hash table */ 26 | struct t_symbol *lablptr; /* label pointer into symbol table */ 27 | struct t_symbol *glablptr; /* pointer to the latest defined global label */ 28 | struct t_symbol *lastlabl; /* last label we have seen */ 29 | struct t_symbol *bank_glabl[4][MAX_BANKS]; /* latest global symbol for each bank */ 30 | char hex[5]; /* hexadecimal character buffer */ 31 | void (*opproc)(int *); /* instruction gen proc */ 32 | int opflg; /* instruction flags */ 33 | int opval; /* instruction value */ 34 | int optype; /* instruction type */ 35 | char opext; /* instruction extension (.l or .h) */ 36 | int pass; /* pass counter */ 37 | char prlnbuf[LAST_CH_POS+4]; /* input line buffer */ 38 | char tmplnbuf[LAST_CH_POS+4]; /* temporary line buffer */ 39 | int slnum; /* source line number counter */ 40 | char symbol[SBOLSZ+1]; /* temporary symbol storage */ 41 | int undef; /* undefined symbol in expression flg */ 42 | unsigned int value; /* operand field value */ 43 | int opvaltab[6][16] = { 44 | {0x08, 0x08, 0x04, 0x14, 0x14, 0x11, 0x00, 0x10, // CPX CPY LDX LDY 45 | 0x0C, 0x1C, 0x18, 0x2C, 0x3C, 0x00, 0x00, 0x00}, 46 | {0x00, 0x00, 0x04, 0x14, 0x14, 0x00, 0x00, 0x00, // ST0 ST1 ST2 TAM TMA 47 | 0x0C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00}, 48 | {0x00, 0x89, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00, // BIT 49 | 0x2C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 50 | {0x3A, 0x00, 0xC6, 0xD6, 0x00, 0x00, 0x00, 0x00, // DEC 51 | 0xCE, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 52 | {0x1A, 0x00, 0xE6, 0xF6, 0x00, 0x00, 0x00, 0x00, // INC 53 | 0xEE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 54 | {0x00, 0x00, 0x64, 0x74, 0x00, 0x00, 0x00, 0x00, // STZ 55 | 0x9C, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /source/protos.h: -------------------------------------------------------------------------------- 1 | 2 | /* ASSEMBLE.C */ 3 | void assemble(void); 4 | int oplook(int *idx); 5 | void addinst(struct t_opcode *optbl); 6 | int check_eol(int *ip); 7 | void do_if(int *ip); 8 | void do_else(int *ip); 9 | void do_endif(int *ip); 10 | void do_ifdef(int *ip); 11 | 12 | /* CODE.C */ 13 | void class1(int *ip); 14 | void class2(int *ip); 15 | void class3(int *ip); 16 | void class4(int *ip); 17 | void class5(int *ip); 18 | void class6(int *ip); 19 | void class7(int *ip); 20 | void class8(int *ip); 21 | void class9(int *ip); 22 | void class10(int *ip); 23 | int getoperand(int *ip, int flag, int last_char); 24 | int getstring(int *ip, char *buffer, int size); 25 | 26 | /* COMMAND.C */ 27 | void do_pseudo(int *ip); 28 | void do_list(int *ip); 29 | void do_mlist(int *ip); 30 | void do_nolist(int *ip); 31 | void do_nomlist(int *ip); 32 | void do_db(int *ip); 33 | void do_str(int *ip); 34 | void do_dw(int *ip); 35 | void do_equ(int *ip); 36 | void do_sequ(int *ip); 37 | void do_page(int *ip); 38 | void do_org(int *ip); 39 | void do_bank(int *ip); 40 | void do_incbin(int *ip); 41 | void do_mx(char *fname); 42 | void do_include(int *ip); 43 | void do_rsset(int *ip); 44 | void do_rs(int *ip); 45 | void do_ds(int *ip); 46 | void do_fail(int *ip); 47 | void do_section(int *ip); 48 | void do_opt(int *ip); 49 | int htoi(char *str, int nb); 50 | 51 | /* CRC.C */ 52 | void crc_init(void); 53 | unsigned int crc_calc(unsigned char *data, int len); 54 | 55 | /* EXPR.C */ 56 | int evaluate(int *ip, char flag); 57 | int push_val(int type); 58 | int getsym(void); 59 | int check_keyword(void); 60 | int push_op(int op); 61 | int do_op(void); 62 | int check_func_args(char *func_name); 63 | 64 | /* FUNC.C */ 65 | void do_func(int *ip); 66 | int func_look(void); 67 | int func_install(int ip); 68 | int func_extract(int ip); 69 | int func_getargs(void); 70 | 71 | /* INPUT.C */ 72 | void init_path(void); 73 | int readline(void); 74 | int open_input(char *name); 75 | int close_input(void); 76 | FILE *open_file(char *fname, char *mode); 77 | 78 | /* MACRO.C */ 79 | void do_macro(int *ip); 80 | void do_endm(int *ip); 81 | struct t_macro *macro_look(int *ip); 82 | int macro_getargs(int ip); 83 | int macro_install(void); 84 | int macro_getargtype(char *arg); 85 | 86 | /* MAIN.C */ 87 | int main(int argc, char **argv); 88 | int calc_bank_base(void); 89 | void help(void); 90 | void show_seg_usage(void); 91 | 92 | /* OUTPUT.C */ 93 | void println(void); 94 | void clearln(void); 95 | void loadlc(int offset, int f); 96 | void hexcon(int digit, int num); 97 | void putbyte(int offset, int data); 98 | void putword(int offset, int data); 99 | void putbuffer(void *data, int size); 100 | void write_srec(char *fname, char *ext, int base); 101 | void error(char *stptr); 102 | void warning(char *stptr); 103 | void fatal_error(char *stptr); 104 | 105 | /* PROC.C */ 106 | void do_call(int *ip); 107 | void do_proc(int *ip); 108 | void do_endp(int *ip); 109 | void proc_reloc(void); 110 | 111 | /* SYMBOL.C */ 112 | int symhash(void); 113 | int colsym(int *ip); 114 | struct t_symbol *stlook(int flag); 115 | struct t_symbol *stinstall(int hash, int type); 116 | int labldef(int lval, int flag); 117 | void lablset(char *name, int val); 118 | void lablremap(void); 119 | void stlist(char *file, int bank_offset); 120 | void constset(char *name, int val); 121 | void strconstset(char *name, char *val); 122 | int strconstget(char *buffer, int size); 123 | -------------------------------------------------------------------------------- /documentation/history.txt: -------------------------------------------------------------------------------- 1 | 2 | -*[ NES ASM ]*- 3 | --------- 4 | 5 | 6 | History 7 | ------- 8 | v3.6 : May 31th, 2021 9 | ----- 10 | - Support for string constants 11 | - Option to assign a string value to a symbol from the 12 | command line 13 | 14 | 15 | v3.5 : May 26th, 2021 16 | ----- 17 | - Option to assign an integet value to a symbol from the 18 | command line 19 | 20 | 21 | v3.4 : Nov 8th, 2020 22 | ----- 23 | - Predefined NES specific constants: PPU/APU registers 24 | 25 | 26 | v3.3 : Nov 1th, 2020 27 | ----- 28 | - Output to stdout support (use '-' as output filename) 29 | - All messages redirected to stderr instead of stdout now 30 | - NES 2.0 support with very large files support, mappers up 31 | to 4095, submappers, etc. 32 | - New directives for NES 2.0 header: .INESSUBMAP, .INESPRGRAM, 33 | .INESPRGNVRAM, .INESCHRRAM, .INESCHRNVRAM, .INESBAT, .INESTIM 34 | - .INESPRG and .INESCHR can be used with actual size now, not 35 | only number of banks, e.g. .INESPRG 8 and .INESPRG $20000 36 | means the same now 37 | 38 | 39 | v3.0 : Aug 30th, 2018 40 | ----- 41 | - Bugfixes 42 | 43 | 44 | v2.51 : September 9th, 2000 45 | ----- 46 | Assembler core : 47 | - added support for a new file format to the INCBIN directive, 48 | INCBIN can now include '.fmp' map files created with the 49 | mapper program Mappy. 50 | 51 | 52 | v2.50 : July 27th, 2000 53 | ----- 54 | Assembler core : 55 | - fixed a few bugs here and there. 56 | - added a new predefined function SIZEOF that returns 57 | the size of a data element (ex. included with #INCBIN) 58 | - added four experimental directives .proc/.endp, 59 | and .procgroup/.endprocgroup; don't use them yet. 60 | 61 | 62 | v2.10 : January 22th, 1999 63 | ----- 64 | Assembler core: 65 | - fixed a bug in backward branch code. 66 | - fixed a little bug in the expression parser. 67 | - merged the code of the NES and the PCE assembler, now 68 | there's only one program. 69 | - made the code more portable; it can now be compiled 70 | without any change on most Unix systems. 71 | - added '.byte' and '.word' directives (same as '.db' and 72 | '.dw') for compatibility with other assemblers. 73 | - added a new command line switch, '-srec' to ouput a 74 | Motorola S-record file (for EEPROM/EPROM programming). 75 | 76 | 77 | v2.01 : December 21th, 1998 78 | ----- 79 | - minor update, fixed a litte bug in the bank code. 80 | 81 | 82 | v2.00 : December 19th, 1998 83 | ----- 84 | - merged Charles Doty's NES assembler in MagicKit, which add 85 | NES support to MagicKit. :) 86 | - added a version of .defchr and .incchr directives adapted 87 | to the NES. 88 | 89 | 90 | v0.10 : May 4th, 1998 91 | ----- 92 | - removed all the HuC6820 opcodes. 93 | - added four directives to support the INES file format 94 | (inesprg, ineschr, inesmap, inesmir). 95 | 96 | 97 | -- 98 | 99 | -------------------------------------------------------------------------------- /source/externs.h: -------------------------------------------------------------------------------- 1 | extern unsigned char rom[MAX_BANKS][BANK_SIZE]; 2 | extern unsigned char map[MAX_BANKS][BANK_SIZE]; 3 | extern char bank_name[MAX_BANKS][64]; 4 | extern int bank_loccnt[4][MAX_BANKS]; 5 | extern int bank_page[4][MAX_BANKS]; 6 | extern int max_zp; /* higher used address in zero page */ 7 | extern int max_bss; /* higher used address in ram */ 8 | extern int max_bank; /* last bank used */ 9 | extern int data_loccnt; /* data location counter */ 10 | extern int data_size; /* size of binary output (in bytes) */ 11 | extern int data_level; /* data output level, must be <= listlevel to be outputed */ 12 | extern int loccnt; /* location counter */ 13 | extern int bank; /* current bank */ 14 | extern int bank_base; /* bank base index */ 15 | extern int bank_limit; /* bank limit */ 16 | extern int rom_limit; /* rom max. size in bytes */ 17 | extern int page; /* page */ 18 | extern int rsbase; /* .rs counter */ 19 | extern int section; /* current section: S_ZP, S_BSS, S_CODE or S_DATA */ 20 | extern int section_bank[4]; /* current bank for each section */ 21 | extern int in_if; /* true if in a '.if' statement */ 22 | extern int if_expr; /* set when parsing an .if expression */ 23 | extern int if_level; /* level of nested .if's */ 24 | extern int skip_lines; /* when true skip lines */ 25 | extern int continued_line; /* set when a line is the continuation of another line */ 26 | extern char *expr; /* expression string pointer */ 27 | extern int mopt; 28 | extern int in_macro; 29 | extern int expand_macro; 30 | extern char marg[8][10][80]; 31 | extern int midx; 32 | extern int mcounter, mcntmax; 33 | extern int mcntstack[8]; 34 | extern struct t_line *mstack[8]; 35 | extern struct t_line *mlptr; 36 | extern struct t_macro *macro_tbl[256]; 37 | extern struct t_macro *mptr; 38 | extern struct t_func *func_tbl[256]; 39 | extern struct t_func *func_ptr; 40 | extern struct t_proc *proc_ptr; 41 | extern int proc_nb; 42 | extern char func_arg[8][10][80]; 43 | extern int func_idx; 44 | extern int infile_error; 45 | extern int infile_num; 46 | extern FILE *out_fp; /* file pointers, output */ 47 | extern FILE *in_fp; /* input */ 48 | extern FILE *lst_fp; /* listing */ 49 | extern struct t_input_info input_file[8]; 50 | extern struct t_machine *machine; 51 | extern struct t_machine nes; 52 | extern struct t_opcode *inst_tbl[256]; /* instructions hash table */ 53 | extern struct t_symbol *hash_tbl[256]; /* label hash table */ 54 | extern struct t_symbol *lablptr; /* label pointer into symbol table */ 55 | extern struct t_symbol *glablptr; /* pointer to the latest defined global symbol */ 56 | extern struct t_symbol *lastlabl; /* last label we have seen */ 57 | extern struct t_symbol *bank_glabl[4][MAX_BANKS]; /* latest global label in each bank */ 58 | extern char hex[]; /* hexadecimal character buffer */ 59 | extern int stop_pass; /* stop the program; set by fatal_error() */ 60 | extern int errcnt; /* error counter */ 61 | extern void (*opproc)(int *); /* instruction gen proc */ 62 | extern int opflg; /* instruction flags */ 63 | extern int opval; /* instruction value */ 64 | extern int optype; /* instruction type */ 65 | extern char opext; /* instruction extension (.l or .h) */ 66 | extern int pass; /* pass counter */ 67 | extern char prlnbuf[]; /* input line buffer */ 68 | extern char tmplnbuf[]; /* temporary line buffer */ 69 | extern int slnum; /* source line number counter */ 70 | extern char symbol[]; /* temporary symbol storage */ 71 | extern int undef; /* undefined symbol in expression flag */ 72 | extern unsigned int value; /* operand field value */ 73 | extern int mlist_opt; /* macro listing main flag */ 74 | extern int xlist; /* listing file main flag */ 75 | extern int list_level; /* output level */ 76 | extern int asm_opt[8]; /* assembler option state */ 77 | extern int opvaltab[6][16]; 78 | 79 | -------------------------------------------------------------------------------- /documentation/cpu_inst.txt: -------------------------------------------------------------------------------- 1 | 2 | -*[ NES ASM v2.51 ]*- 3 | --------------- 4 | 5 | 6 | Instructions set 7 | ---------------- 8 | 9 | +------+----------+-----------------------------+ 10 | | | NVTBDIZC | Description | 11 | +------+----------+-----------------------------+ 12 | | ADC | XX0---XX | Add with Carry | 13 | | AND | X-0---X- | Logical AND | 14 | | ASL | X-0---XX | Arithmetic Shift left | 15 | | BCC | --0----- | Branch if Carry Clear | 16 | | BCS | --0----- | Branch if Carry Set | 17 | | BEQ | --0----- | Branch if Equal | 18 | | BIT | XX0---X- | Bit Test | 19 | | BMI | --0----- | Branch if Minus | 20 | | BNE | --0----- | Branch if Not Equal | 21 | | BPL | --0----- | Branch if Plus | 22 | | BRA | --0----- | Branch Always | 23 | | BRK | --0----- | Break | 24 | | BVC | --0----- | Branch if Overflow Clear | 25 | | BVS | --0----- | Branch if Overflow Set | 26 | | CLC | --0----0 | Clear Carry flag | 27 | | CLD | --0-0--- | Clear Decimal flag | 28 | | CLI | --0--0-- | Clear Interrupt disable | 29 | | CLV | -00----- | Clear Overflow flag | 30 | | CMP | X-0---XX | Compare A with source | 31 | | CPX | X-0---XX | Compare X with source | 32 | | CPY | X-0---XX | Compare Y with source | 33 | | DEC | X-0---X- | Decrement | 34 | | DEX | X-0---X- | Decrement X | 35 | | DEY | X-0---X- | Decrement Y | 36 | | EOR | X-0---X- | Logical Exclusive OR | 37 | | INC | X-0---X- | Increment | 38 | | INX | X-0---X- | Increment X | 39 | | INY | X-0---X- | Increment Y | 40 | | JMP | --0----- | Jump | 41 | | JSR | --0----- | Jump to Sub Routine | 42 | | LDA | X-0---X- | Load A | 43 | | LDX | X-0---X- | Load X | 44 | | LDY | X-0---X- | Load Y | 45 | | LSR | 0-0---XX | Logical Shift Right | 46 | | NOP | --0----- | No Operation | 47 | | ORA | X-0---X- | Logical inclusive OR | 48 | | PHA | --0----- | Push A | 49 | | PHP | --0----- | Push P | 50 | | PLA | X-0---X- | Pull A | 51 | | PLP | XXXXXXXX | Pull P | 52 | | ROL | X-0---XX | Rotate Left | 53 | | ROR | X-0---XX | Rotate Right | 54 | | RTI | XXXXXXXX | Return from Interrupt | 55 | | RTS | --0----- | Return from Sub Routine | 56 | | SBC | XX0---XX | Substract with Carry | 57 | | SEC | --0----1 | Set Carry flag | 58 | | SED | --0-1--- | Set Decimal flag | 59 | | SEI | --0--1-- | Set Interrupt disable | 60 | | STA | --0----- | Store A | 61 | | STX | --0----- | Store X | 62 | | STY | --0----- | Store Y | 63 | | TAX | X-0---X- | Transfer A to X | 64 | | TAY | X-0---X- | Transfer A to Y | 65 | | TSX | X-0---X- | Transfer S to X | 66 | | TXA | X-0---X- | Transfer X to A | 67 | | TXS | --0----- | Transfer X to S | 68 | | TYA | X-0---X- | Transfer Y to A | 69 | +------+----------+-----------------------------+ 70 | 71 | 72 | Operand syntax 73 | -------------- 74 | 75 | A accumulator 76 | #i immediate 77 | 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | struct t_func *func_tbl[256]; 10 | struct t_func *func_ptr; 11 | char func_line[128]; 12 | char func_arg[8][10][80]; 13 | int func_idx; 14 | 15 | 16 | /* ---- 17 | * do_func() 18 | * ---- 19 | * .func pseudo 20 | */ 21 | 22 | void 23 | do_func(int *ip) 24 | { 25 | if (pass == LAST_PASS) 26 | println(); 27 | else { 28 | /* error checking */ 29 | if (lablptr == NULL) { 30 | error("No name for this function!"); 31 | return; 32 | } 33 | if (lablptr->refcnt) { 34 | switch (lablptr->type) { 35 | case MACRO: 36 | fatal_error("Symbol already used by a macro!"); 37 | 38 | case FUNC: 39 | fatal_error("Function already defined!"); 40 | return; 41 | 42 | case DEFSTR: 43 | fatal_error("Symbol already used by a string constant!"); 44 | return; 45 | 46 | default: 47 | fatal_error("Symbol already used by a label!"); 48 | return; 49 | } 50 | } 51 | 52 | /* install this new function in the hash table */ 53 | if (!func_install(*ip)) 54 | return; 55 | } 56 | } 57 | 58 | /* search a function */ 59 | 60 | int 61 | func_look(void) 62 | { 63 | int hash; 64 | 65 | /* search the function in the hash table */ 66 | hash = symhash(); 67 | func_ptr = func_tbl[hash]; 68 | while (func_ptr) { 69 | if (!strcmp(&symbol[1], func_ptr->name)) 70 | break; 71 | func_ptr = func_ptr->next; 72 | } 73 | 74 | /* ok */ 75 | if (func_ptr) 76 | return (1); 77 | 78 | /* didn't find a function with this name */ 79 | return (0); 80 | } 81 | 82 | /* install a function in the hash table */ 83 | 84 | int 85 | func_install(int ip) 86 | { 87 | int hash; 88 | 89 | /* mark the function name as reserved */ 90 | lablptr->type = FUNC; 91 | 92 | /* check function name syntax */ 93 | if (strchr(&symbol[1], '.')) { 94 | error("Invalid function name!"); 95 | return (0); 96 | } 97 | 98 | /* extract function body */ 99 | if (func_extract(ip) == -1) 100 | return (0); 101 | 102 | /* allocate a new func struct */ 103 | if ((func_ptr = (void *)malloc(sizeof(struct t_func))) == NULL) { 104 | error("Out of memory!"); 105 | return (0); 106 | } 107 | 108 | /* initialize it */ 109 | strcpy(func_ptr->name, &symbol[1]); 110 | strcpy(func_ptr->line, func_line); 111 | hash = symhash(); 112 | func_ptr->next = func_tbl[hash]; 113 | func_tbl[hash] = func_ptr; 114 | 115 | /* ok */ 116 | return (1); 117 | } 118 | 119 | /* extract function body */ 120 | 121 | int 122 | func_extract(int ip) 123 | { 124 | char *ptr; 125 | char c; 126 | int i, arg, max_arg; 127 | int end; 128 | 129 | /* skip spaces */ 130 | while (isspace((int)prlnbuf[ip])) 131 | ip++; 132 | 133 | /* get function body */ 134 | ptr = func_line; 135 | max_arg = 0; 136 | end = 0; 137 | i = 0; 138 | 139 | while (!end) { 140 | c = prlnbuf[ip++]; 141 | switch (c) { 142 | /* end of line */ 143 | case ';': 144 | case '\0': 145 | *ptr++ = '\0'; 146 | end = 1; 147 | break; 148 | 149 | /* function arg */ 150 | case '\\': 151 | *ptr++ = c; 152 | i++; 153 | c = prlnbuf[ip++]; 154 | if ((c < '1') || (c > '9')) { 155 | error("Invalid function argument!"); 156 | return (-1); 157 | } 158 | arg = c - '1'; 159 | if (max_arg < arg) 160 | max_arg = arg; 161 | 162 | /* other */ 163 | default: 164 | *ptr++ = c; 165 | i++; 166 | if (i == 127) { 167 | error("Function line too long!"); 168 | return (-1); 169 | } 170 | break; 171 | } 172 | } 173 | 174 | /* return the number of args */ 175 | return (max_arg); 176 | } 177 | 178 | /* extract function args */ 179 | 180 | int 181 | func_getargs(void) 182 | { 183 | char c, *ptr, *line; 184 | int arg, level, space, flag; 185 | int i, x; 186 | 187 | /* can not nest too much macros */ 188 | if (func_idx == 7) { 189 | error("Too many nested function calls!"); 190 | return (0); 191 | } 192 | 193 | /* skip spaces */ 194 | while (isspace((int)*expr)) 195 | expr++; 196 | 197 | /* function args must be enclosed in parenthesis */ 198 | if (*expr++ != '(') 199 | return (0); 200 | 201 | /* initialize args */ 202 | line = NULL; 203 | ptr = func_arg[func_idx][0]; 204 | arg = 0; 205 | 206 | for (i = 0; i < 9; i++) 207 | func_arg[func_idx][i][0] = '\0'; 208 | 209 | /* get args one by one */ 210 | for (;;) { 211 | /* skip spaces */ 212 | while (isspace((int)*expr)) 213 | expr++; 214 | 215 | c = *expr++; 216 | switch (c) { 217 | /* empty arg */ 218 | case ',': 219 | arg++; 220 | ptr = func_arg[func_idx][arg]; 221 | if (arg == 9) { 222 | error("Too many arguments for a function!"); 223 | return (0); 224 | } 225 | break; 226 | 227 | /* end of line */ 228 | case ';': 229 | case '\0': 230 | error("Syntax error in function call!"); 231 | return (0); 232 | 233 | /* end of function */ 234 | case ')': 235 | return (1); 236 | 237 | /* arg */ 238 | default: 239 | space = 0; 240 | level = 0; 241 | flag = 0; 242 | i = 0; 243 | x = 0; 244 | for (;;) { 245 | if (c == '\0') { 246 | if (flag == 0) 247 | break; 248 | else { 249 | flag = 0; 250 | c = *expr++; 251 | continue; 252 | } 253 | } 254 | else if (c == ';') { 255 | break; 256 | } 257 | else if (c == ',') { 258 | if (level == 0) 259 | break; 260 | } 261 | else if (c == '\\') { 262 | if (func_idx == 0) { 263 | error("Syntax error!"); 264 | return (0); 265 | } 266 | c = *expr++; 267 | if (c < '1' || c > '9') { 268 | error("Invalid function argument index!"); 269 | return (0); 270 | } 271 | line = func_arg[func_idx - 1][c - '1']; 272 | flag = 1; 273 | c = *line++; 274 | continue; 275 | } 276 | else if (c == '(') { 277 | level++; 278 | } 279 | else if (c == ')') { 280 | if (level == 0) 281 | break; 282 | level--; 283 | } 284 | if (space) { 285 | if (c != ' ') { 286 | while (i < x) 287 | ptr[i++] = ' '; 288 | ptr[i++] = c; 289 | space = 0; 290 | } 291 | } 292 | else if (c == ' ') { 293 | space = 1; 294 | } 295 | else { 296 | ptr[i++] = c; 297 | } 298 | if (i == 80) { 299 | error("Invalid function argument length!"); 300 | return (0); 301 | } 302 | x++; 303 | if (flag) 304 | c = *line++; 305 | else 306 | c = *expr++; 307 | } 308 | ptr[i] = '\0'; 309 | expr--; 310 | break; 311 | } 312 | } 313 | } 314 | 315 | -------------------------------------------------------------------------------- /source/inst.h: -------------------------------------------------------------------------------- 1 | 2 | /* instruction table */ 3 | struct t_opcode base_inst[57] = { 4 | {NULL, "ADC", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0x61, 0}, 5 | {NULL, "AND", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0x21, 0}, 6 | {NULL, "ASL", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x02, 0}, 7 | {NULL, "BCC", class2, 0, 0x90, 0}, 8 | {NULL, "BCS", class2, 0, 0xB0, 0}, 9 | {NULL, "BEQ", class2, 0, 0xF0, 0}, 10 | {NULL, "BIT", class4, IMM|ZP|ZP_X|ABS|ABS_X, 0x00, 2}, 11 | {NULL, "BMI", class2, 0, 0x30, 0}, 12 | {NULL, "BNE", class2, 0, 0xD0, 0}, 13 | {NULL, "BPL", class2, 0, 0x10, 0}, 14 | {NULL, "BRK", class1, 0, 0x00, 0}, 15 | {NULL, "BVC", class2, 0, 0x50, 0}, 16 | {NULL, "BVS", class2, 0, 0x70, 0}, 17 | {NULL, "CLC", class1, 0, 0x18, 0}, 18 | {NULL, "CLD", class1, 0, 0xD8, 0}, 19 | {NULL, "CLI", class1, 0, 0x58, 0}, 20 | {NULL, "CLV", class1, 0, 0xB8, 0}, 21 | {NULL, "CMP", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0xC1, 0}, 22 | {NULL, "CPX", class4, IMM|ZP|ABS, 0xE0, 1}, 23 | {NULL, "CPY", class4, IMM|ZP|ABS, 0xC0, 1}, 24 | {NULL, "DEC", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x00, 3}, 25 | {NULL, "DEX", class1, 0, 0xCA, 0}, 26 | {NULL, "DEY", class1, 0, 0x88, 0}, 27 | {NULL, "EOR", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0x41, 0}, 28 | {NULL, "INC", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x00, 4}, 29 | {NULL, "INX", class1, 0, 0xE8, 0}, 30 | {NULL, "INY", class1, 0, 0xC8, 0}, 31 | {NULL, "JMP", class4, ABS|ABS_IND|ABS_IND_X, 0x40, 0}, 32 | {NULL, "JSR", class4, ABS, 0x14, 0}, 33 | {NULL, "LDA", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0xA1, 0}, 34 | {NULL, "LDX", class4, IMM|ZP|ZP_Y|ABS|ABS_Y, 0xA2, 1}, 35 | {NULL, "LDY", class4, IMM|ZP|ZP_X|ABS|ABS_X, 0xA0, 1}, 36 | {NULL, "LSR", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x42, 0}, 37 | {NULL, "NOP", class1, 0, 0xEA, 0}, 38 | {NULL, "ORA", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0x01, 0}, 39 | {NULL, "PHA", class1, 0, 0x48, 0}, 40 | {NULL, "PHP", class1, 0, 0x08, 0}, 41 | {NULL, "PLA", class1, 0, 0x68, 0}, 42 | {NULL, "PLP", class1, 0, 0x28, 0}, 43 | {NULL, "ROL", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x22, 0}, 44 | {NULL, "ROR", class4, ACC|ZP|ZP_X|ABS|ABS_X, 0x62, 0}, 45 | {NULL, "RTI", class1, 0, 0x40, 0}, 46 | {NULL, "RTS", class1, 0, 0x60, 0}, 47 | {NULL, "SBC", class4, IMM|ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0xE1, 0}, 48 | {NULL, "SEC", class1, 0, 0x38, 0}, 49 | {NULL, "SED", class1, 0, 0xF8, 0}, 50 | {NULL, "SEI", class1, 0, 0x78, 0}, 51 | {NULL, "STA", class4, ZP|ZP_X|ZP_IND|ZP_IND_X|ZP_IND_Y|ABS|ABS_X|ABS_Y, 0x81, 0}, 52 | {NULL, "STX", class4, ZP|ZP_Y|ABS, 0x82, 0}, 53 | {NULL, "STY", class4, ZP|ZP_X|ABS, 0x80, 0}, 54 | {NULL, "TAX", class1, 0, 0xAA, 0}, 55 | {NULL, "TAY", class1, 0, 0xA8, 0}, 56 | {NULL, "TSX", class1, 0, 0xBA, 0}, 57 | {NULL, "TXA", class1, 0, 0x8A, 0}, 58 | {NULL, "TXS", class1, 0, 0x9A, 0}, 59 | {NULL, "TYA", class1, 0, 0x98, 0}, 60 | {NULL, NULL, NULL, 0, 0, 0} 61 | }; 62 | 63 | /* pseudo instruction table */ 64 | struct t_opcode base_pseudo[79] = { 65 | {NULL, "=", do_equ, PSEUDO, P_EQU, 0}, 66 | 67 | {NULL, "BANK", do_bank, PSEUDO, P_BANK, 0}, 68 | {NULL, "BSS", do_section, PSEUDO, P_BSS, S_BSS}, 69 | {NULL, "BYTE", do_db, PSEUDO, P_DB, 0}, 70 | {NULL, "CALL", do_call, PSEUDO, P_CALL, 0}, 71 | {NULL, "CODE", do_section, PSEUDO, P_CODE, S_CODE}, 72 | {NULL, "DATA", do_section, PSEUDO, P_DATA, S_DATA}, 73 | {NULL, "DB", do_db, PSEUDO, P_DB, 0}, 74 | {NULL, "DW", do_dw, PSEUDO, P_DW, 0}, 75 | {NULL, "DS", do_ds, PSEUDO, P_DS, 0}, 76 | {NULL, "ELSE", do_else, PSEUDO, P_ELSE, 0}, 77 | {NULL, "ENDIF", do_endif, PSEUDO, P_ENDIF, 0}, 78 | {NULL, "ENDM", do_endm, PSEUDO, P_ENDM, 0}, 79 | {NULL, "ENDP", do_endp, PSEUDO, P_ENDP, P_PROC}, 80 | {NULL, "ENDPROCGROUP", do_endp, PSEUDO, P_ENDPG, P_PGROUP}, 81 | {NULL, "EQU", do_equ, PSEUDO, P_EQU, 0}, 82 | {NULL, "SEQU", do_sequ, PSEUDO, P_SEQU, 0}, 83 | {NULL, "FAIL", do_fail, PSEUDO, P_FAIL, 0}, 84 | {NULL, "FUNC", do_func, PSEUDO, P_FUNC, 0}, 85 | {NULL, "IF", do_if, PSEUDO, P_IF, 0}, 86 | {NULL, "IFDEF", do_ifdef, PSEUDO, P_IFDEF, 1}, 87 | {NULL, "IFNDEF", do_ifdef, PSEUDO, P_IFNDEF, 0}, 88 | {NULL, "INCBIN", do_incbin, PSEUDO, P_INCBIN, 0}, 89 | {NULL, "INCLUDE", do_include, PSEUDO, P_INCLUDE, 0}, 90 | {NULL, "LIST", do_list, PSEUDO, P_LIST, 0}, 91 | {NULL, "MAC", do_macro, PSEUDO, P_MACRO, 0}, 92 | {NULL, "MACRO", do_macro, PSEUDO, P_MACRO, 0}, 93 | {NULL, "MLIST", do_mlist, PSEUDO, P_MLIST, 0}, 94 | {NULL, "NOLIST", do_nolist, PSEUDO, P_NOLIST, 0}, 95 | {NULL, "NOMLIST", do_nomlist, PSEUDO, P_NOMLIST, 0}, 96 | {NULL, "OPT", do_opt, PSEUDO, P_OPT, 0}, 97 | {NULL, "ORG", do_org, PSEUDO, P_ORG, 0}, 98 | {NULL, "PAGE", do_page, PSEUDO, P_PAGE, 0}, 99 | {NULL, "PROC", do_proc, PSEUDO, P_PROC, P_PROC}, 100 | {NULL, "PROCGROUP", do_proc, PSEUDO, P_PGROUP, P_PGROUP}, 101 | {NULL, "RSSET", do_rsset, PSEUDO, P_RSSET, 0}, 102 | {NULL, "RS", do_rs, PSEUDO, P_RS, 0}, 103 | {NULL, "WORD", do_dw, PSEUDO, P_DW, 0}, 104 | {NULL, "ZP", do_section, PSEUDO, P_ZP, S_ZP}, 105 | {NULL, "STR", do_str, PSEUDO, P_STR, 0}, 106 | 107 | {NULL, ".BANK", do_bank, PSEUDO, P_BANK, 0}, 108 | {NULL, ".BSS", do_section, PSEUDO, P_BSS, S_BSS}, 109 | {NULL, ".BYTE", do_db, PSEUDO, P_DB, 0}, 110 | {NULL, ".CODE", do_section, PSEUDO, P_CODE, S_CODE}, 111 | {NULL, ".DATA", do_section, PSEUDO, P_DATA, S_DATA}, 112 | {NULL, ".DB", do_db, PSEUDO, P_DB, 0}, 113 | {NULL, ".DW", do_dw, PSEUDO, P_DW, 0}, 114 | {NULL, ".DS", do_ds, PSEUDO, P_DS, 0}, 115 | {NULL, ".ELSE", do_else, PSEUDO, P_ELSE, 0}, 116 | {NULL, ".ENDIF", do_endif, PSEUDO, P_ENDIF, 0}, 117 | {NULL, ".ENDM", do_endm, PSEUDO, P_ENDM, 0}, 118 | {NULL, ".ENDP", do_endp, PSEUDO, P_ENDP, P_PROC}, 119 | {NULL, ".ENDPROCGROUP", do_endp, PSEUDO, P_ENDPG, P_PGROUP}, 120 | {NULL, ".EQU", do_equ, PSEUDO, P_EQU, 0}, 121 | {NULL, ".SEQU", do_sequ, PSEUDO, P_SEQU, 0}, 122 | {NULL, ".FAIL", do_fail, PSEUDO, P_FAIL, 0}, 123 | {NULL, ".FUNC", do_func, PSEUDO, P_FUNC, 0}, 124 | {NULL, ".IF", do_if, PSEUDO, P_IF, 0}, 125 | {NULL, ".IFDEF", do_ifdef, PSEUDO, P_IFDEF, 1}, 126 | {NULL, ".IFNDEF", do_ifdef, PSEUDO, P_IFNDEF, 0}, 127 | {NULL, ".INCBIN", do_incbin, PSEUDO, P_INCBIN, 0}, 128 | {NULL, ".INCLUDE", do_include, PSEUDO, P_INCLUDE, 0}, 129 | {NULL, ".LIST", do_list, PSEUDO, P_LIST, 0}, 130 | {NULL, ".MAC", do_macro, PSEUDO, P_MACRO, 0}, 131 | {NULL, ".MACRO", do_macro, PSEUDO, P_MACRO, 0}, 132 | {NULL, ".MLIST", do_mlist, PSEUDO, P_MLIST, 0}, 133 | {NULL, ".NOLIST", do_nolist, PSEUDO, P_NOLIST, 0}, 134 | {NULL, ".NOMLIST", do_nomlist, PSEUDO, P_NOMLIST, 0}, 135 | {NULL, ".OPT", do_opt, PSEUDO, P_OPT, 0}, 136 | {NULL, ".ORG", do_org, PSEUDO, P_ORG, 0}, 137 | {NULL, ".PAGE", do_page, PSEUDO, P_PAGE, 0}, 138 | {NULL, ".PROC", do_proc, PSEUDO, P_PROC, P_PROC}, 139 | {NULL, ".PROCGROUP", do_proc, PSEUDO, P_PGROUP, P_PGROUP}, 140 | {NULL, ".RSSET", do_rsset, PSEUDO, P_RSSET, 0}, 141 | {NULL, ".RS", do_rs, PSEUDO, P_RS, 0}, 142 | {NULL, ".WORD", do_dw, PSEUDO, P_DW, 0}, 143 | {NULL, ".ZP", do_section, PSEUDO, P_ZP, S_ZP}, 144 | {NULL, ".STR", do_str, PSEUDO, P_STR, 0}, 145 | {NULL, NULL, NULL, 0, 0, 0} 146 | }; 147 | 148 | -------------------------------------------------------------------------------- /source/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | int infile_error; 10 | int infile_num; 11 | struct t_input_info input_file[8]; 12 | char incpath[10][128]; 13 | 14 | 15 | /* ---- 16 | * init_path() 17 | * ---- 18 | * init the include path 19 | */ 20 | 21 | void 22 | init_path(void) 23 | { 24 | char *p,*pl; 25 | int i, l; 26 | 27 | p = getenv(machine->include_env); 28 | 29 | if (p == NULL) 30 | return; 31 | 32 | for (i = 0; i < 10; i++) { 33 | 34 | pl = strchr(p, ';'); 35 | 36 | if (pl == NULL) 37 | l = strlen(p); 38 | else 39 | l = pl-p; 40 | 41 | if (l == 0) { 42 | incpath[i][0] = '\0'; 43 | } else { 44 | strncpy(incpath[i],p,l); 45 | p += l; 46 | while (*p == ';') p++; 47 | } 48 | 49 | if (incpath[i][strlen(incpath[i])] != PATH_SEPARATOR) { 50 | strcat(incpath[i], PATH_SEPARATOR_STRING); 51 | } 52 | } 53 | } 54 | 55 | 56 | /* ---- 57 | * readline() 58 | * ---- 59 | * read and format an input line. 60 | */ 61 | 62 | int 63 | readline(void) 64 | { 65 | char *ptr, *arg, num[8]; 66 | int j, n; 67 | int i; /* pointer into prlnbuf */ 68 | int c; /* current character */ 69 | int temp; /* temp used for line number conversion */ 70 | 71 | start: 72 | for (i = 0; i < LAST_CH_POS; i++) 73 | prlnbuf[i] = ' '; 74 | 75 | /* if 'expand_macro' is set get a line from macro buffer instead */ 76 | if (expand_macro) { 77 | if (mlptr == NULL) { 78 | while (mlptr == NULL) { 79 | midx--; 80 | mlptr = mstack[midx]; 81 | mcounter = mcntstack[midx]; 82 | if (midx == 0) { 83 | mlptr = NULL; 84 | expand_macro = 0; 85 | break; 86 | } 87 | } 88 | } 89 | 90 | /* expand line */ 91 | if (mlptr) { 92 | i = SFIELD; 93 | ptr = mlptr->data; 94 | for (;;) { 95 | c = *ptr++; 96 | if (c == '\0') 97 | break; 98 | if (c != '\\') 99 | prlnbuf[i++] = c; 100 | else { 101 | c = *ptr++; 102 | prlnbuf[i] = '\0'; 103 | 104 | /* \@ */ 105 | if (c == '@') { 106 | n = 5; 107 | sprintf(num, "%05i", mcounter); 108 | arg = num; 109 | } 110 | 111 | /* \# */ 112 | else if (c == '#') { 113 | for (j = 9; j > 0; j--) 114 | if (strlen(marg[midx][j - 1])) 115 | break; 116 | n = 1; 117 | sprintf(num, "%i", j); 118 | arg = num; 119 | } 120 | 121 | /* \?1 - \?9 */ 122 | else if (c == '?') { 123 | c = *ptr++; 124 | if (c >= '1' && c <= '9') { 125 | n = 1; 126 | sprintf(num, "%i", macro_getargtype(marg[midx][c - '1'])); 127 | arg = num; 128 | } 129 | else { 130 | error("Invalid macro argument index!"); 131 | return (-1); 132 | } 133 | } 134 | 135 | /* \1 - \9 */ 136 | else if (c >= '1' && c <= '9') { 137 | j = c - '1'; 138 | n = strlen(marg[midx][j]); 139 | arg = marg[midx][j]; 140 | } 141 | 142 | /* unknown macro special command */ 143 | else { 144 | error("Invalid macro argument index!"); 145 | return (-1); 146 | } 147 | 148 | /* check for line overflow */ 149 | if ((i + n) >= LAST_CH_POS - 1) { 150 | error("Invalid line length!"); 151 | return (-1); 152 | } 153 | 154 | /* copy macro string */ 155 | strncpy(&prlnbuf[i], arg, n); 156 | i += n; 157 | } 158 | if (i >= LAST_CH_POS - 1) 159 | i = LAST_CH_POS - 1; 160 | } 161 | prlnbuf[i] = '\0'; 162 | mlptr = mlptr->next; 163 | return (0); 164 | } 165 | } 166 | 167 | /* put source line number into prlnbuf */ 168 | i = 4; 169 | temp = ++slnum; 170 | while (temp != 0) { 171 | prlnbuf[i--] = temp % 10 + '0'; 172 | temp /= 10; 173 | } 174 | 175 | /* get a line */ 176 | i = SFIELD; 177 | c = getc(in_fp); 178 | if (c == EOF) { 179 | if (close_input()) 180 | return (-1); 181 | goto start; 182 | } 183 | for (;;) { 184 | /* check for the end of line */ 185 | if (c == '\r') { 186 | c = getc(in_fp); 187 | if (c == '\n' || c == EOF) 188 | break; 189 | ungetc(c, in_fp); 190 | break; 191 | } 192 | if (c == '\n' || c == EOF) 193 | break; 194 | 195 | /* store char in the line buffer */ 196 | prlnbuf[i] = c; 197 | i += (i < LAST_CH_POS) ? 1 : 0; 198 | 199 | /* expand tab char to space */ 200 | if (c == '\t') { 201 | prlnbuf[--i] = ' '; 202 | i += (8 - ((i - SFIELD) % 8)); 203 | } 204 | 205 | /* get next char */ 206 | c = getc(in_fp); 207 | } 208 | prlnbuf[i] = '\0'; 209 | return(0); 210 | } 211 | 212 | /* ---- 213 | * open_input() 214 | * ---- 215 | * open input files - up to 7 levels. 216 | */ 217 | 218 | int 219 | open_input(char *name) 220 | { 221 | FILE *fp; 222 | char *p; 223 | char temp[128]; 224 | int i; 225 | 226 | /* only 7 nested input files */ 227 | if (infile_num == 7) { 228 | error("Too many include levels, max. 7!"); 229 | return (1); 230 | } 231 | 232 | /* backup current input file infos */ 233 | if (infile_num) { 234 | input_file[infile_num].lnum = slnum; 235 | input_file[infile_num].fp = in_fp; 236 | } 237 | 238 | /* get a copy of the file name */ 239 | strcpy(temp, name); 240 | 241 | /* auto add the .asm file extension */ 242 | if ((p = strrchr(temp, '.')) != NULL) { 243 | if (strchr(p, PATH_SEPARATOR)) 244 | strcat(temp, ".asm"); 245 | } 246 | else { 247 | strcat(temp, ".asm"); 248 | } 249 | 250 | /* check if this file is already opened */ 251 | if (infile_num) { 252 | for (i = 1; i < infile_num; i++) { 253 | if (!strcmp(input_file[i].name, temp)) { 254 | error("Repeated include file!"); 255 | return (1); 256 | } 257 | } 258 | } 259 | 260 | /* open the file */ 261 | if ((fp = open_file(temp, "r")) == NULL) 262 | return (-1); 263 | 264 | /* update input file infos */ 265 | in_fp = fp; 266 | slnum = 0; 267 | infile_num++; 268 | input_file[infile_num].fp = fp; 269 | input_file[infile_num].if_level = if_level; 270 | strcpy(input_file[infile_num].name, temp); 271 | if ((pass == LAST_PASS) && (xlist) && (list_level)) 272 | fprintf(lst_fp, "#[%i] %s\n", infile_num, input_file[infile_num].name); 273 | 274 | /* ok */ 275 | return (0); 276 | } 277 | 278 | 279 | /* ---- 280 | * close_input() 281 | * ---- 282 | * close an input file, return -1 if no more files in the stack. 283 | */ 284 | 285 | int 286 | close_input(void) 287 | { 288 | if (proc_ptr) { 289 | fatal_error("Incomplete PROC!"); 290 | return (-1); 291 | } 292 | if (in_macro) { 293 | fatal_error("Incomplete MACRO definition!"); 294 | return (-1); 295 | } 296 | if (input_file[infile_num].if_level != if_level) { 297 | fatal_error("Incomplete IF/ENDIF statement!"); 298 | return (-1); 299 | } 300 | if (infile_num <= 1) 301 | return (-1); 302 | 303 | fclose(in_fp); 304 | infile_num--; 305 | infile_error = -1; 306 | slnum = input_file[infile_num].lnum; 307 | in_fp = input_file[infile_num].fp; 308 | if ((pass == LAST_PASS) && (xlist) && (list_level)) 309 | fprintf(lst_fp, "#[%i] %s\n", infile_num, input_file[infile_num].name); 310 | 311 | /* ok */ 312 | return (0); 313 | } 314 | 315 | 316 | /* ---- 317 | * open_file() 318 | * ---- 319 | * open a file - browse paths 320 | */ 321 | 322 | FILE * 323 | open_file(char *name, char *mode) 324 | { 325 | FILE *fileptr; 326 | char testname[256]; 327 | int i; 328 | 329 | fileptr = fopen(name, mode); 330 | if (fileptr != NULL) return(fileptr); 331 | 332 | for (i = 0; i < 10; i++) { 333 | if (strlen(incpath[i])) { 334 | strcpy(testname, incpath[i]); 335 | strcat(testname, name); 336 | 337 | fileptr = fopen(testname, mode); 338 | if (fileptr != NULL) break; 339 | } 340 | } 341 | 342 | return (fileptr); 343 | } 344 | 345 | -------------------------------------------------------------------------------- /source/output.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "defs.h" 4 | #include "externs.h" 5 | #include "protos.h" 6 | 7 | 8 | /* ---- 9 | * println() 10 | * ---- 11 | * prints the contents of prlnbuf 12 | */ 13 | 14 | void 15 | println(void) 16 | { 17 | int nb, cnt; 18 | int i; 19 | 20 | /* check if output possible */ 21 | if (list_level == 0) 22 | return; 23 | if (!xlist || !asm_opt[OPT_LIST] || (expand_macro && !asm_opt[OPT_MACRO])) 24 | return; 25 | 26 | /* update line buffer if necessary */ 27 | if (continued_line) 28 | strcpy(prlnbuf, tmplnbuf); 29 | 30 | /* output */ 31 | if (data_loccnt == -1) 32 | /* line buffer */ 33 | fprintf(lst_fp, "%s\n", prlnbuf); 34 | else { 35 | /* line buffer + data bytes */ 36 | loadlc(data_loccnt, 0); 37 | 38 | /* number of bytes */ 39 | nb = loccnt - data_loccnt; 40 | 41 | /* check level */ 42 | if ((data_level > list_level) && (nb > 3)) 43 | /* doesn't match */ 44 | fprintf(lst_fp, "%s\n", prlnbuf); 45 | else { 46 | /* ok */ 47 | cnt = 0; 48 | for (i = 0; i < nb; i++) { 49 | if (bank >= RESERVED_BANK) { 50 | prlnbuf[16 + (3*cnt)] = '-'; 51 | prlnbuf[17 + (3*cnt)] = '-'; 52 | } 53 | else { 54 | hexcon(2, rom[bank][data_loccnt]); 55 | prlnbuf[16 + (3*cnt)] = hex[1]; 56 | prlnbuf[17 + (3*cnt)] = hex[2]; 57 | } 58 | data_loccnt++; 59 | cnt++; 60 | if (cnt == data_size) { 61 | cnt = 0; 62 | fprintf(lst_fp, "%s\n", prlnbuf); 63 | clearln(); 64 | loadlc(data_loccnt, 0); 65 | } 66 | } 67 | if (cnt) 68 | fprintf(lst_fp, "%s\n", prlnbuf); 69 | } 70 | } 71 | } 72 | 73 | 74 | /* ---- 75 | * clearln() 76 | * ---- 77 | * clear prlnbuf 78 | */ 79 | 80 | void 81 | clearln(void) 82 | { 83 | int i; 84 | 85 | for (i = 0; i < SFIELD; i++) 86 | prlnbuf[i] = ' '; 87 | prlnbuf[i] = 0; 88 | } 89 | 90 | 91 | /* ---- 92 | * loadlc() 93 | * ---- 94 | * load 16 bit value in printable form into prlnbuf 95 | */ 96 | 97 | void 98 | loadlc(int offset, int pos) 99 | { 100 | int i; 101 | 102 | if (pos) 103 | i = 16; 104 | else 105 | i = 7; 106 | 107 | if (pos == 0) { 108 | if (bank >= RESERVED_BANK) { 109 | prlnbuf[i++] = '-'; 110 | prlnbuf[i++] = '-'; 111 | } 112 | else { 113 | hexcon(2, bank); 114 | prlnbuf[i++] = hex[1]; 115 | prlnbuf[i++] = hex[2]; 116 | } 117 | prlnbuf[i++] = ':'; 118 | offset += page << 13; 119 | } 120 | hexcon(4, offset); 121 | prlnbuf[i++] = hex[1]; 122 | prlnbuf[i++] = hex[2]; 123 | prlnbuf[i++] = hex[3]; 124 | prlnbuf[i] = hex[4]; 125 | } 126 | 127 | 128 | /* ---- 129 | * hexcon() 130 | * ---- 131 | * convert number supplied as argument to hexadecimal in hex[digit] 132 | */ 133 | 134 | void 135 | hexcon(int digit, int num) 136 | { 137 | for (; digit > 0; digit--) { 138 | hex[digit] = (num & 0x0f) + '0'; 139 | if (hex[digit] > '9') 140 | hex[digit] += 'A' - '9' - 1; 141 | num >>= 4; 142 | } 143 | } 144 | 145 | 146 | /* ---- 147 | * putbyte() 148 | * ---- 149 | * store a byte in the rom 150 | */ 151 | 152 | void 153 | putbyte(int offset, int data) 154 | { 155 | if (bank >= RESERVED_BANK) 156 | return; 157 | if (offset < 0x2000) { 158 | rom[bank][offset] = (data) & 0xFF; 159 | map[bank][offset] = section + (page << 5); 160 | 161 | /* update rom size */ 162 | if (bank > max_bank) 163 | max_bank = bank; 164 | } 165 | } 166 | 167 | 168 | /* ---- 169 | * putword() 170 | * ---- 171 | * store a word in the rom 172 | */ 173 | 174 | void 175 | putword(int offset, int data) 176 | { 177 | if (bank >= RESERVED_BANK) 178 | return; 179 | if (offset < 0x1FFF) { 180 | /* low byte */ 181 | rom[bank][offset] = (data) & 0xFF; 182 | map[bank][offset] = section + (page << 5); 183 | 184 | /* high byte */ 185 | rom[bank][offset+1] = (data >> 8) & 0xFF; 186 | map[bank][offset+1] = section + (page << 5); 187 | 188 | /* update rom size */ 189 | if (bank > max_bank) 190 | max_bank = bank; 191 | } 192 | } 193 | 194 | 195 | /* ---- 196 | * putbuffer() 197 | * ---- 198 | * copy a buffer at the current location 199 | */ 200 | 201 | void 202 | putbuffer(void *data, int size) 203 | { 204 | int addr; 205 | 206 | /* check size */ 207 | if (size == 0) 208 | return; 209 | 210 | /* check if the buffer will fit in the rom */ 211 | if (bank >= RESERVED_BANK) { 212 | addr = loccnt + size; 213 | 214 | if (addr > 0x1FFF) { 215 | fatal_error("PROC overflow!"); 216 | return; 217 | } 218 | } 219 | else { 220 | addr = loccnt + size + (bank << 13); 221 | 222 | if (addr > rom_limit) { 223 | fatal_error("ROM overflow!"); 224 | return; 225 | } 226 | 227 | /* copy the buffer */ 228 | if (pass == LAST_PASS) { 229 | if (data) { 230 | memcpy(&rom[bank][loccnt], data, size); 231 | memset(&map[bank][loccnt], section + (page << 5), size); 232 | } 233 | else { 234 | memset(&rom[bank][loccnt], 0, size); 235 | memset(&map[bank][loccnt], section + (page << 5), size); 236 | } 237 | } 238 | } 239 | 240 | /* update the location counter */ 241 | bank += (loccnt + size) >> 13; 242 | loccnt = (loccnt + size) & 0x1FFF; 243 | 244 | /* update rom size */ 245 | if (bank < RESERVED_BANK) { 246 | if (bank > max_bank) { 247 | if (loccnt) 248 | max_bank = bank; 249 | else 250 | max_bank = bank - 1; 251 | } 252 | } 253 | } 254 | 255 | 256 | /* ---- 257 | * write_srec() 258 | * ---- 259 | */ 260 | 261 | void 262 | write_srec(char *file, char *ext, int base) 263 | { 264 | unsigned char data, chksum; 265 | char fname[128]; 266 | int addr, dump, cnt, pos, i, j; 267 | FILE *fp; 268 | 269 | /* status message */ 270 | if (!strcmp(ext, "mx")) 271 | fprintf(stderr, "writing mx file... "); 272 | else 273 | fprintf(stderr, "writing s-record file... "); 274 | 275 | /* flush output */ 276 | fflush(stdout); 277 | 278 | /* add the file extension */ 279 | strcpy(fname, file); 280 | strcat(fname, "."); 281 | strcat(fname, ext); 282 | 283 | /* open the file */ 284 | if ((fp = fopen(fname, "w")) == NULL) { 285 | fprintf(stderr, "can not open file '%s'!\n", fname); 286 | return; 287 | } 288 | 289 | /* dump the rom */ 290 | dump = 0; 291 | cnt = 0; 292 | pos = 0; 293 | 294 | for (i = 0; i <= max_bank; i++) { 295 | for (j = 0; j < 8192; j++) { 296 | if (map[i][j] != 0xFF) { 297 | /* data byte */ 298 | if (cnt == 0) 299 | pos = j; 300 | cnt++; 301 | if (cnt == 32) 302 | dump = 1; 303 | } 304 | else { 305 | /* free byte */ 306 | if (cnt) 307 | dump = 1; 308 | } 309 | if (j == 8191) 310 | if (cnt) 311 | dump = 1; 312 | 313 | /* dump */ 314 | if (dump) { 315 | dump = 0; 316 | addr = base + (i << 13) + pos; 317 | chksum = cnt + ((addr >> 16) & 0xFF) + 318 | ((addr >> 8) & 0xFF) + 319 | ((addr) & 0xFF) + 320 | 4; 321 | 322 | /* number, address */ 323 | fprintf(fp, "S2%02X%06X", cnt + 4, addr); 324 | 325 | /* code */ 326 | while (cnt) { 327 | data = rom[i][pos++]; 328 | chksum += data; 329 | fprintf(fp, "%02X", data); 330 | cnt--; 331 | } 332 | 333 | /* chksum */ 334 | fprintf(fp, "%02X\n", (~chksum) & 0xFF); 335 | } 336 | } 337 | } 338 | 339 | /* starting address */ 340 | addr = ((map[0][0] >> 5) << 13); 341 | chksum = ((addr >> 8) & 0xFF) + (addr & 0xFF) + 4; 342 | fprintf(fp, "S804%06X%02X", addr, (~chksum) & 0xFF); 343 | 344 | /* ok */ 345 | fclose(fp); 346 | fprintf(stderr, "OK\n"); 347 | } 348 | 349 | 350 | /* ---- 351 | * fatal_error() 352 | * ---- 353 | * stop compilation 354 | */ 355 | 356 | void 357 | fatal_error(char *stptr) 358 | { 359 | error(stptr); 360 | stop_pass = 1; 361 | } 362 | 363 | 364 | /* ---- 365 | * error() 366 | * ---- 367 | * error printing routine 368 | */ 369 | 370 | void 371 | error(char *stptr) 372 | { 373 | warning(stptr); 374 | errcnt++; 375 | } 376 | 377 | 378 | /* ---- 379 | * warning() 380 | * ---- 381 | * warning printing routine 382 | */ 383 | 384 | void 385 | warning(char *stptr) 386 | { 387 | int i, temp; 388 | 389 | /* put the source line number into prlnbuf */ 390 | i = 4; 391 | temp = slnum; 392 | while (temp != 0) { 393 | prlnbuf[i--] = temp % 10 + '0'; 394 | temp /= 10; 395 | } 396 | 397 | /* update the current file name */ 398 | if (infile_error != infile_num) { 399 | infile_error = infile_num; 400 | fprintf(stderr, "#[%i] %s\n", infile_num, input_file[infile_num].name); 401 | } 402 | 403 | /* output the line and the error message */ 404 | loadlc(loccnt, 0); 405 | fprintf(stderr, "%s\n", prlnbuf); 406 | fprintf(stderr, " %s\n", stptr); 407 | } 408 | 409 | -------------------------------------------------------------------------------- /source/macro.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "defs.h" 7 | #include "externs.h" 8 | #include "protos.h" 9 | 10 | int mopt; 11 | int in_macro; 12 | int expand_macro; 13 | char marg[8][10][80]; 14 | int midx; 15 | int mcounter, mcntmax; 16 | int mcntstack[8]; 17 | struct t_line *mstack[8]; 18 | struct t_line *mlptr; 19 | struct t_macro *macro_tbl[256]; 20 | struct t_macro *mptr; 21 | 22 | /* .macro pseudo */ 23 | 24 | void 25 | do_macro(int *ip) 26 | { 27 | if (pass == LAST_PASS) 28 | println(); 29 | else { 30 | /* error checking */ 31 | if (expand_macro) { 32 | error("Can not nest macro definitions!"); 33 | return; 34 | } 35 | if (lablptr == NULL) { 36 | /* skip spaces */ 37 | while (isspace((int)prlnbuf[*ip])) 38 | (*ip)++; 39 | 40 | /* search a label after the .macro */ 41 | if (colsym(ip) == 0) { 42 | error("No name for this macro!"); 43 | return; 44 | } 45 | 46 | /* put the macro name in the symbol table */ 47 | if ((lablptr = stlook(1)) == NULL) 48 | return; 49 | } 50 | if (lablptr->refcnt) { 51 | switch (lablptr->type) { 52 | case MACRO: 53 | fatal_error("Macro already defined!"); 54 | return; 55 | 56 | case FUNC: 57 | fatal_error("Symbol already used by a function!"); 58 | return; 59 | 60 | case DEFSTR: 61 | fatal_error("Symbol already used by a string constant!"); 62 | return; 63 | 64 | default: 65 | fatal_error("Symbol already used by a label!"); 66 | return; 67 | } 68 | } 69 | if (!check_eol(ip)) 70 | return; 71 | 72 | /* install this new macro in the hash table */ 73 | if (!macro_install()) 74 | return; 75 | } 76 | in_macro = 1; 77 | } 78 | 79 | /* .endm pseudo */ 80 | 81 | void 82 | do_endm(int *ip) 83 | { 84 | error("Unexpected ENDM!"); 85 | return; 86 | } 87 | 88 | /* search a macro in the hash table */ 89 | 90 | struct t_macro *macro_look(int *ip) 91 | { 92 | struct t_macro *ptr; 93 | char name[32]; 94 | char c; 95 | int hash; 96 | int l; 97 | 98 | /* calculate the symbol hash value and check syntax */ 99 | l = 0; 100 | hash = 0; 101 | for (;;) { 102 | c = prlnbuf[*ip]; 103 | if (c == '\0' || c == ' ' || c == '\t' || c == ';') 104 | break; 105 | if (!isalnum(c) && c != '_') 106 | return (NULL); 107 | if (l == 0) { 108 | if (isdigit(c)) 109 | return (NULL); 110 | } 111 | if (l == 31) 112 | return (NULL); 113 | name[l++] = c; 114 | hash += c; 115 | hash = (hash << 3) + (hash >> 5) + c; 116 | (*ip)++; 117 | } 118 | name[l] = '\0'; 119 | hash &= 0xFF; 120 | 121 | /* browse the hash table */ 122 | ptr = macro_tbl[hash]; 123 | while (ptr) { 124 | if (!strcmp(name, ptr->name)) 125 | break; 126 | ptr = ptr->next; 127 | } 128 | 129 | /* return result */ 130 | return (ptr); 131 | } 132 | 133 | /* extract macro arguments */ 134 | 135 | int 136 | macro_getargs(int ip) 137 | { 138 | char *ptr; 139 | char c, t; 140 | int i, j, f, arg; 141 | int level; 142 | 143 | /* can not nest too much macros */ 144 | if (midx == 7) { 145 | error("Too many nested macro calls!"); 146 | return (0); 147 | } 148 | 149 | /* initialize args */ 150 | mcntstack[midx] = mcounter; 151 | mstack[midx++] = mlptr; 152 | ptr = marg[midx][0]; 153 | arg = 0; 154 | 155 | for (i = 0; i < 9; i++) 156 | marg[midx][i][0] = '\0'; 157 | 158 | /* extract args */ 159 | for (;;) { 160 | /* skip spaces */ 161 | while (isspace((int)prlnbuf[ip])) 162 | ip++; 163 | 164 | c = prlnbuf[ip++]; 165 | switch (c) { 166 | /* no arg */ 167 | case ',': 168 | arg++; 169 | ptr = marg[midx][arg]; 170 | if (arg == 9) { 171 | error("Too many arguments for a macro!"); 172 | return (0); 173 | } 174 | break; 175 | 176 | /* string */ 177 | case '{': 178 | c = '}'; 179 | case '\"': 180 | i = 0; 181 | if (c == '\"') 182 | ptr[i++] = c; 183 | for (;;) { 184 | t = prlnbuf[ip++]; 185 | if (t == '\0') { 186 | error("Unterminated string!"); 187 | return (0); 188 | } 189 | if (i == 80) { 190 | error("String too long, max. 80 characters!"); 191 | return (0); 192 | } 193 | if (t == c) 194 | break; 195 | ptr[i++] = t; 196 | } 197 | if (c == '\"') 198 | ptr[i++] = t; 199 | 200 | /* skip spaces */ 201 | while (isspace((int)prlnbuf[ip])) 202 | ip++; 203 | 204 | /* check end of arg */ 205 | switch (prlnbuf[ip]) { 206 | case '\0': 207 | case ',': 208 | case ';': 209 | break; 210 | 211 | default: 212 | error("Syntax error!"); 213 | return (0); 214 | } 215 | 216 | /* end arg string */ 217 | ptr[i] = '\0'; 218 | break; 219 | 220 | /* end of line */ 221 | case ';': 222 | case '\0': 223 | return (1); 224 | 225 | /* continuation char */ 226 | case '\\': 227 | /* skip spaces */ 228 | i = ip; 229 | while (isspace((int)prlnbuf[i])) 230 | i++; 231 | 232 | /* check */ 233 | if (prlnbuf[i] == ';' || prlnbuf[i] == '\0') { 234 | /* output line */ 235 | if (pass == LAST_PASS) { 236 | println(); 237 | clearln(); 238 | } 239 | 240 | /* read a new line */ 241 | if (readline() == -1) 242 | return (0); 243 | 244 | /* rewind line pointer and continue */ 245 | ip = SFIELD; 246 | break; 247 | } 248 | 249 | /* other */ 250 | default: 251 | i = 0; 252 | j = 0; 253 | f = 0; 254 | level = 0; 255 | while (c) { 256 | if (c == ',') { 257 | if (level == 0) 258 | break; 259 | } 260 | else if ((c == '(') || (c == '[')) { 261 | level++; 262 | } 263 | else if ((c == ')') || (c == ']')) { 264 | if (level) 265 | level--; 266 | } 267 | else if (c == ';') { 268 | break; 269 | } 270 | if (f) { 271 | if (c != ' ') { 272 | while (i < j) 273 | ptr[i++] = ' '; 274 | ptr[i++] = c; 275 | f = 0; 276 | } 277 | } 278 | else if (c == ' ') { 279 | f = 1; 280 | } 281 | else { 282 | ptr[i++] = c; 283 | } 284 | if (i == 80) { 285 | error("Macro argument string too long, max. 80 characters!"); 286 | return (0); 287 | } 288 | j++; 289 | c = prlnbuf[ip++]; 290 | } 291 | ptr[i] = '\0'; 292 | ip--; 293 | 294 | /* check if arg is X or Y */ 295 | if (strlen(ptr) && arg) { 296 | c = tolower(ptr[0]); 297 | 298 | if ((c == 'x') || (c == 'y')) { 299 | if ((strcasecmp(ptr, "x++") == 0) || 300 | (strcasecmp(ptr, "y++") == 0) || 301 | (strlen(ptr) == 1)) 302 | { 303 | arg--; 304 | ptr = marg[midx][arg]; 305 | 306 | /* check string length */ 307 | if (strlen(ptr) > 75) { 308 | error("Macro argument string too long, max. 80 characters!"); 309 | return (0); 310 | } 311 | 312 | /* attach current arg to the previous one */ 313 | strcat(ptr, ","); 314 | strcat(ptr, marg[midx][arg + 1]); 315 | ptr = marg[midx][arg + 1]; 316 | ptr[0] = '\0'; 317 | } 318 | } 319 | } 320 | break; 321 | } 322 | } 323 | } 324 | 325 | /* install a macro in the hash table */ 326 | 327 | int 328 | macro_install(void) 329 | { 330 | char c; 331 | int hash = 0; 332 | int i; 333 | 334 | /* mark the macro name as reserved */ 335 | lablptr->type = MACRO; 336 | 337 | /* check macro name syntax */ 338 | if (strchr(&symbol[1], '.')) { 339 | error("Invalid macro name!"); 340 | return (0); 341 | } 342 | 343 | /* calculate symbol hash value */ 344 | for (i = 1; i <= symbol[0]; i++) { 345 | c = symbol[i]; 346 | hash += c; 347 | hash = (hash << 3) + (hash >> 5) + c; 348 | } 349 | hash &= 0xFF; 350 | 351 | /* allocate a macro struct */ 352 | mptr = (void *)malloc(sizeof(struct t_macro)); 353 | if (mptr == NULL) { 354 | error("Out of memory!"); 355 | return (0); 356 | } 357 | 358 | /* initialize it */ 359 | strcpy(mptr->name, &symbol[1]); 360 | mptr->line = NULL; 361 | mptr->next = macro_tbl[hash]; 362 | macro_tbl[hash] = mptr; 363 | mlptr = NULL; 364 | 365 | /* ok */ 366 | return (1); 367 | } 368 | 369 | /* send back the addressing mode of a macro arg */ 370 | 371 | int 372 | macro_getargtype(char *arg) 373 | { 374 | struct t_symbol *sym; 375 | char c; 376 | int i; 377 | 378 | /* skip spaces */ 379 | while (isspace((int)*arg)) 380 | arg++; 381 | 382 | /* get type */ 383 | switch (toupper(*arg++)) { 384 | case '\0': 385 | return (NO_ARG); 386 | 387 | case '"': 388 | return (ARG_STRING); 389 | 390 | case '#': 391 | return (ARG_IMM); 392 | 393 | case '[': 394 | return (ARG_INDIRECT); 395 | 396 | case 'A': 397 | case 'X': 398 | case 'Y': 399 | if (*arg == '\0') 400 | return (ARG_REG); 401 | 402 | default: 403 | /* symbol */ 404 | for(i = 0; i < SBOLSZ; i++) { 405 | c = arg[i]; 406 | if (isdigit(c) && (i == 0)) 407 | break; 408 | if ((!isalnum(c)) && (c != '_') && (c != '.')) 409 | break; 410 | } 411 | 412 | if (i == 0) 413 | return (ARG_ABS); 414 | else { 415 | if (c != '\0') 416 | return (ARG_ABS); 417 | else { 418 | strncpy(&symbol[1], arg, i); 419 | symbol[0] = i; 420 | symbol[i+1] = '\0'; 421 | 422 | if ((sym = stlook(0)) == NULL) 423 | return (ARG_LABEL); 424 | else { 425 | if((sym->type == UNDEF) || (sym->type == IFUNDEF)) 426 | return (ARG_LABEL); 427 | if (sym->bank == RESERVED_BANK) 428 | return (ARG_ABS); 429 | else 430 | return (ARG_LABEL); 431 | } 432 | } 433 | } 434 | } 435 | } 436 | 437 | -------------------------------------------------------------------------------- /source/proc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | struct t_proc *proc_tbl[256]; 10 | struct t_proc *proc_ptr; 11 | struct t_proc *proc_first; 12 | struct t_proc *proc_last; 13 | int proc_nb; 14 | int call_ptr; 15 | int call_bank; 16 | 17 | /* protos */ 18 | struct t_proc *proc_look(void); 19 | int proc_install(void); 20 | void poke(int addr, int data); 21 | 22 | 23 | /* ---- 24 | * do_call() 25 | * ---- 26 | * call pseudo 27 | */ 28 | 29 | void 30 | do_call(int *ip) 31 | { 32 | struct t_proc *ptr; 33 | int value; 34 | 35 | /* define label */ 36 | labldef(loccnt, 1); 37 | 38 | /* update location counter */ 39 | data_loccnt = loccnt; 40 | loccnt += 3; 41 | 42 | /* generate code */ 43 | if (pass == LAST_PASS) { 44 | /* skip spaces */ 45 | while (isspace((int)prlnbuf[*ip])) 46 | (*ip)++; 47 | 48 | /* extract name */ 49 | if (!colsym(ip)) { 50 | if (symbol[0] == 0) 51 | fatal_error("Syntax error!"); 52 | return; 53 | } 54 | 55 | /* check end of line */ 56 | check_eol(ip); 57 | 58 | /* lookup proc table */ 59 | if((ptr = proc_look())) { 60 | /* check banks */ 61 | if (bank == ptr->bank) 62 | value = ptr->org + 0xA000; 63 | else { 64 | /* different */ 65 | if (ptr->call) 66 | value = ptr->call; 67 | else { 68 | /* new call */ 69 | value = call_ptr + 0x8000; 70 | ptr->call = value; 71 | 72 | /* init */ 73 | if (call_ptr == 0) 74 | call_bank = ++max_bank; 75 | 76 | /* install */ 77 | poke(call_ptr++, 0xA8); // tay 78 | poke(call_ptr++, 0x43); // tma #5 79 | poke(call_ptr++, 0x20); 80 | poke(call_ptr++, 0x48); // pha 81 | poke(call_ptr++, 0xA9); // lda #... 82 | poke(call_ptr++, ptr->bank); 83 | poke(call_ptr++, 0x53); // tam #5 84 | poke(call_ptr++, 0x20); 85 | poke(call_ptr++, 0x98); // tya 86 | poke(call_ptr++, 0x20); // jsr ... 87 | poke(call_ptr++, (ptr->org & 0xFF)); 88 | poke(call_ptr++, (ptr->org >> 8) + 0xA0); 89 | poke(call_ptr++, 0xA8); // tay 90 | poke(call_ptr++, 0x68); // pla 91 | poke(call_ptr++, 0x53); // tam #5 92 | poke(call_ptr++, 0x20); 93 | poke(call_ptr++, 0x98); // tya 94 | poke(call_ptr++, 0x60); // rts 95 | } 96 | } 97 | } 98 | else { 99 | /* lookup symbol table */ 100 | if ((lablptr = stlook(0)) == NULL) { 101 | fatal_error("Undefined destination!"); 102 | return; 103 | } 104 | 105 | /* get symbol value */ 106 | value = lablptr->value; 107 | } 108 | 109 | /* opcode */ 110 | putbyte(data_loccnt, 0x20); 111 | putword(data_loccnt+1, value); 112 | 113 | /* output line */ 114 | println(); 115 | } 116 | } 117 | 118 | 119 | /* ---- 120 | * do_proc() 121 | * ---- 122 | * .proc pseudo 123 | */ 124 | 125 | void 126 | do_proc(int *ip) 127 | { 128 | struct t_proc *ptr; 129 | 130 | /* check if nesting procs/groups */ 131 | if (proc_ptr) { 132 | if (optype == P_PGROUP) { 133 | fatal_error("Can not declare a group inside a proc/group!"); 134 | return; 135 | } 136 | else { 137 | if (proc_ptr->type == P_PROC) { 138 | fatal_error("Can not nest procs!"); 139 | return; 140 | } 141 | } 142 | } 143 | 144 | /* get proc name */ 145 | if (lablptr) { 146 | strcpy(&symbol[1], &lablptr->name[1]); 147 | symbol[0] = strlen(&symbol[1]); 148 | } 149 | else { 150 | /* skip spaces */ 151 | while (isspace((int)prlnbuf[*ip])) 152 | (*ip)++; 153 | 154 | /* extract name */ 155 | if (!colsym(ip)) { 156 | if (symbol[0]) 157 | return; 158 | if (optype == P_PROC) { 159 | fatal_error("Proc name is missing!"); 160 | return; 161 | } 162 | 163 | /* default name */ 164 | sprintf(&symbol[1], "__group_%i__", proc_nb + 1); 165 | symbol[0] = strlen(&symbol[1]); 166 | } 167 | 168 | /* lookup symbol table */ 169 | if ((lablptr = stlook(1)) == NULL) 170 | return; 171 | } 172 | 173 | /* check symbol */ 174 | if (symbol[1] == '.') { 175 | fatal_error("Proc/group name can not be local!"); 176 | return; 177 | } 178 | 179 | /* check end of line */ 180 | if (!check_eol(ip)) 181 | return; 182 | 183 | /* search (or create new) proc */ 184 | if((ptr = proc_look())) 185 | proc_ptr = ptr; 186 | else { 187 | if (!proc_install()) 188 | return; 189 | } 190 | if (proc_ptr->refcnt) { 191 | fatal_error("Proc/group multiply defined!"); 192 | return; 193 | } 194 | 195 | /* incrememte proc ref counter */ 196 | proc_ptr->refcnt++; 197 | 198 | /* backup current bank infos */ 199 | bank_glabl[section][bank] = glablptr; 200 | bank_loccnt[section][bank] = loccnt; 201 | bank_page[section][bank] = page; 202 | proc_ptr->old_bank = bank; 203 | proc_nb++; 204 | 205 | /* set new bank infos */ 206 | bank = proc_ptr->bank; 207 | page = 5; 208 | loccnt = proc_ptr->org; 209 | glablptr = lablptr; 210 | 211 | /* define label */ 212 | labldef(loccnt, 1); 213 | 214 | /* output */ 215 | if (pass == LAST_PASS) { 216 | loadlc((page << 13) + loccnt, 0); 217 | println(); 218 | } 219 | } 220 | 221 | 222 | /* ---- 223 | * do_endp() 224 | * ---- 225 | * .endp pseudo 226 | */ 227 | 228 | void 229 | do_endp(int *ip) 230 | { 231 | if (proc_ptr == NULL) { 232 | fatal_error("Unexpected ENDP/ENDPROCGROUP!"); 233 | return; 234 | } 235 | if (optype != proc_ptr->type) { 236 | fatal_error("Unexpected ENDP/ENDPROCGROUP!"); 237 | return; 238 | } 239 | 240 | /* check end of line */ 241 | if (!check_eol(ip)) 242 | return; 243 | 244 | /* record proc size */ 245 | bank = proc_ptr->old_bank; 246 | proc_ptr->size = loccnt - proc_ptr->base; 247 | proc_ptr = proc_ptr->group; 248 | 249 | /* restore previous bank settings */ 250 | if (proc_ptr == NULL) { 251 | page = bank_page[section][bank]; 252 | loccnt = bank_loccnt[section][bank]; 253 | glablptr = bank_glabl[section][bank]; 254 | } 255 | 256 | /* output */ 257 | if (pass == LAST_PASS) 258 | println(); 259 | } 260 | 261 | 262 | /* ---- 263 | * proc_reloc() 264 | * ---- 265 | * 266 | */ 267 | 268 | void 269 | proc_reloc(void) 270 | { 271 | struct t_symbol *sym; 272 | struct t_symbol *local; 273 | struct t_proc *group; 274 | int i; 275 | int addr; 276 | int tmp; 277 | 278 | if (proc_nb == 0) 279 | return; 280 | 281 | /* init */ 282 | proc_ptr = proc_first; 283 | bank = max_bank + 1; 284 | addr = 0; 285 | 286 | /* alloc memory */ 287 | while (proc_ptr) { 288 | /* proc */ 289 | if (proc_ptr->group == NULL) { 290 | tmp = addr + proc_ptr->size; 291 | 292 | /* bank change */ 293 | if (tmp > 0x2000) { 294 | bank++; 295 | addr = 0; 296 | } 297 | if (bank > bank_limit) { 298 | fatal_error("Not enough ROM space for procs!"); 299 | return; 300 | } 301 | 302 | /* reloc proc */ 303 | proc_ptr->bank = bank; 304 | proc_ptr->org = addr; 305 | addr += proc_ptr->size; 306 | } 307 | 308 | /* group */ 309 | else { 310 | /* reloc proc */ 311 | group = proc_ptr->group; 312 | proc_ptr->bank = bank; 313 | proc_ptr->org += (group->org - group->base); 314 | } 315 | 316 | /* next */ 317 | max_bank = bank; 318 | proc_ptr->refcnt = 0; 319 | proc_ptr = proc_ptr->link; 320 | } 321 | 322 | /* remap proc symbols */ 323 | for (i = 0; i < 256; i++) { 324 | sym = hash_tbl[i]; 325 | 326 | while (sym) { 327 | proc_ptr = sym->proc; 328 | 329 | /* remap addr */ 330 | if (sym->proc) { 331 | sym->bank = proc_ptr->bank; 332 | sym->value += (proc_ptr->org - proc_ptr->base); 333 | 334 | /* local symbols */ 335 | if (sym->local) { 336 | local = sym->local; 337 | 338 | while (local) { 339 | proc_ptr = local->proc; 340 | 341 | /* remap addr */ 342 | if (local->proc) { 343 | local->bank = proc_ptr->bank; 344 | local->value += (proc_ptr->org - proc_ptr->base); 345 | } 346 | 347 | /* next */ 348 | local = local->next; 349 | } 350 | } 351 | } 352 | 353 | /* next */ 354 | sym = sym->next; 355 | } 356 | } 357 | 358 | /* reserve call bank */ 359 | lablset("_call_bank", max_bank + 1); 360 | 361 | /* reset */ 362 | proc_ptr = NULL; 363 | proc_nb = 0; 364 | } 365 | 366 | 367 | /* ---- 368 | * proc_look() 369 | * ---- 370 | * 371 | */ 372 | 373 | struct t_proc * 374 | proc_look(void) 375 | { 376 | struct t_proc *ptr; 377 | int hash; 378 | 379 | /* search the procedure in the hash table */ 380 | hash = symhash(); 381 | ptr = proc_tbl[hash]; 382 | while (ptr) { 383 | if (!strcmp(&symbol[1], ptr->name)) 384 | break; 385 | ptr = ptr->next; 386 | } 387 | 388 | /* ok */ 389 | return (ptr); 390 | } 391 | 392 | 393 | /* ---- 394 | * proc_install() 395 | * ---- 396 | * install a procedure in the hash table 397 | * 398 | */ 399 | 400 | int 401 | proc_install(void) 402 | { 403 | struct t_proc *ptr; 404 | int hash; 405 | 406 | /* allocate a new proc struct */ 407 | if ((ptr = (void *)malloc(sizeof(struct t_proc))) == NULL) { 408 | error("Out of memory!"); 409 | return (0); 410 | } 411 | 412 | /* initialize it */ 413 | strcpy(ptr->name, &symbol[1]); 414 | hash = symhash(); 415 | ptr->bank = (optype == P_PGROUP) ? GROUP_BANK : PROC_BANK; 416 | ptr->base = proc_ptr ? loccnt : 0; 417 | ptr->org = ptr->base; 418 | ptr->size = 0; 419 | ptr->call = 0; 420 | ptr->refcnt = 0; 421 | ptr->link = NULL; 422 | ptr->next = proc_tbl[hash]; 423 | ptr->group = proc_ptr; 424 | ptr->type = optype; 425 | proc_ptr = ptr; 426 | proc_tbl[hash] = proc_ptr; 427 | 428 | /* link it */ 429 | if (proc_first == NULL) { 430 | proc_first = proc_ptr; 431 | proc_last = proc_ptr; 432 | } 433 | else { 434 | proc_last->link = proc_ptr; 435 | proc_last = proc_ptr; 436 | } 437 | 438 | /* ok */ 439 | return (1); 440 | } 441 | 442 | 443 | /* ---- 444 | * poke() 445 | * ---- 446 | * 447 | */ 448 | 449 | void 450 | poke(int addr, int data) 451 | { 452 | rom[call_bank][addr] = data; 453 | map[call_bank][addr] = S_CODE + (4 << 5); 454 | } 455 | 456 | -------------------------------------------------------------------------------- /source/assemble.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | int in_if; /* set when we are in an .if statement */ 10 | int if_expr; /* set when parsing an .if expression */ 11 | int if_level; /* level of nested .if's */ 12 | int if_state[256]; /* status when entering the .if */ 13 | int if_flag[256]; /* .if/.else status */ 14 | int skip_lines; /* set when lines must be skipped */ 15 | int continued_line; /* set when a line is the continuation of another line */ 16 | 17 | 18 | /* ---- 19 | * assemble() 20 | * ---- 21 | * translate source line to machine language 22 | */ 23 | void 24 | assemble(void) 25 | { 26 | struct t_line *ptr; 27 | char *buf; 28 | char c; 29 | int flag; 30 | int ip, i, j; /* prlnbuf pointer */ 31 | 32 | /* init variables */ 33 | lablptr = NULL; 34 | continued_line = 0; 35 | data_loccnt = -1; 36 | data_size = 3; 37 | data_level = 1; 38 | 39 | /* macro definition */ 40 | if (in_macro) { 41 | i = SFIELD; 42 | if (colsym(&i)) 43 | if (prlnbuf[i] == ':') 44 | i++; 45 | while (isspace((int)prlnbuf[i])) 46 | i++; 47 | if (pass == LAST_PASS) 48 | println(); 49 | if (oplook(&i) >= 0) { 50 | if (opflg == PSEUDO) { 51 | if (opval == P_MACRO) { 52 | error("Can not nest macro definitions!"); 53 | return; 54 | } 55 | if (opval == P_ENDM) { 56 | if (!check_eol(&i)) 57 | return; 58 | in_macro = 0; 59 | return; 60 | } 61 | } 62 | } 63 | if (pass == FIRST_PASS) { 64 | ptr = (void *)malloc(sizeof(struct t_line)); 65 | buf = (void *)malloc(strlen(&prlnbuf[SFIELD]) + 1); 66 | if ((ptr == NULL) || (buf == NULL)) { 67 | error("Out of memory!"); 68 | return; 69 | } 70 | strcpy(buf, &prlnbuf[SFIELD]); 71 | ptr->next = NULL; 72 | ptr->data = buf; 73 | if (mlptr) 74 | mlptr->next = ptr; 75 | else 76 | mptr->line = ptr; 77 | mlptr = ptr; 78 | } 79 | return; 80 | } 81 | 82 | /* IF/ELSE section; 83 | * check for a '.else' or '.endif' 84 | * to toggle state 85 | */ 86 | if (in_if) { 87 | i = SFIELD; 88 | while (isspace((int)prlnbuf[i])) 89 | i++; 90 | if (oplook(&i) >= 0) { 91 | if (opflg == PSEUDO) { 92 | switch (opval) { 93 | case P_IF: // .if 94 | case P_IFDEF: // .ifdef 95 | case P_IFNDEF: // .ifndef 96 | if (skip_lines) { 97 | if_level++; 98 | if_state[if_level] = 0; 99 | } 100 | break; 101 | 102 | case P_ELSE: // .else 103 | if (!check_eol(&i)) 104 | return; 105 | if (if_state[if_level]) { 106 | skip_lines = !if_flag[if_level]; 107 | if (pass == LAST_PASS) 108 | println(); 109 | } 110 | return; 111 | 112 | case P_ENDIF: // .endif 113 | if (!check_eol(&i)) 114 | return; 115 | if (if_state[if_level] && (pass == LAST_PASS)) 116 | println(); 117 | skip_lines = !if_state[if_level]; 118 | if_level--; 119 | if (if_level == 0) 120 | in_if = 0; 121 | return; 122 | } 123 | } 124 | } 125 | } 126 | 127 | if (skip_lines) 128 | return; 129 | 130 | /* comment line */ 131 | c = prlnbuf[SFIELD]; 132 | if (c == ';' || c == '*' || c == '\0') { 133 | // if (c == '\0') 134 | lastlabl = NULL; 135 | if (pass == LAST_PASS) 136 | println(); 137 | return; 138 | } 139 | 140 | /* search for a label */ 141 | i = SFIELD; 142 | j = 0; 143 | while (isspace((int)prlnbuf[i])) 144 | i++; 145 | for (;;) { 146 | c = prlnbuf[i + j]; 147 | if (isdigit(c) && (j == 0)) 148 | break; 149 | if (!isalnum(c) && (c != '_') && (c != '.')) 150 | break; 151 | j++; 152 | } 153 | 154 | if ((j == 0) || ((i != SFIELD) && (c != ':'))) 155 | i = SFIELD; 156 | else { 157 | if (colsym(&i) != 0) 158 | if ((lablptr = stlook(1)) == NULL) 159 | return; 160 | if ((lablptr) && (prlnbuf[i] == ':')) 161 | i++; 162 | } 163 | 164 | /* skip spaces */ 165 | while (isspace((int)prlnbuf[i])) 166 | i++; 167 | 168 | /* is it a macro? */ 169 | ip = i; 170 | mptr = macro_look(&ip); 171 | if (mptr) { 172 | /* define label */ 173 | labldef(loccnt, 1); 174 | 175 | /* output location counter */ 176 | if (pass == LAST_PASS) { 177 | if (!asm_opt[OPT_MACRO]) 178 | loadlc((page << 13) + loccnt, 0); 179 | } 180 | 181 | /* get macro args */ 182 | if (!macro_getargs(ip)) 183 | return; 184 | 185 | /* output line */ 186 | if (pass == LAST_PASS) 187 | println(); 188 | 189 | /* ok */ 190 | mcntmax++; 191 | mcounter = mcntmax; 192 | expand_macro = 1; 193 | mlptr = mptr->line; 194 | return; 195 | } 196 | 197 | /* an instruction then */ 198 | ip = i; 199 | flag = oplook(&ip); 200 | if (flag < 0) { 201 | labldef(loccnt, 1); 202 | if (flag == -1) 203 | error("Unknown instruction!"); 204 | if ((flag == -2) && (pass == LAST_PASS)) { 205 | if (lablptr) 206 | loadlc(loccnt, 0); 207 | println(); 208 | } 209 | lastlabl = NULL; 210 | return; 211 | } 212 | 213 | /* generate code */ 214 | if (opflg == PSEUDO) 215 | do_pseudo(&ip); 216 | else if (labldef(loccnt, 1) == -1) 217 | return; 218 | else { 219 | /* output infos */ 220 | data_loccnt = loccnt; 221 | 222 | /* check if we are in the CODE section */ 223 | if (section != S_CODE) 224 | fatal_error("Instructions not allowed in this section!"); 225 | 226 | /* generate code */ 227 | opproc(&ip); 228 | 229 | /* reset last label pointer */ 230 | lastlabl = NULL; 231 | } 232 | } 233 | 234 | 235 | /* ---- 236 | * oplook() 237 | * ---- 238 | * operation code table lookup 239 | * return symbol length if found 240 | * return -1 on syntax error 241 | * return -2 if no symbol 242 | */ 243 | 244 | int 245 | oplook(int *idx) 246 | { 247 | struct t_opcode *ptr; 248 | char name[16]; 249 | char c; 250 | int flag; 251 | int hash; 252 | int i; 253 | 254 | /* get instruction name */ 255 | i = 0; 256 | opext = 0; 257 | flag = 0; 258 | hash = 0; 259 | 260 | for (;;) { 261 | c = toupper(prlnbuf[*idx]); 262 | if (c == ' ' || c == '\t' || c == '\0' || c == ';') 263 | break; 264 | if (!isalnum(c) && c != '.' && c != '*' && c != '=') 265 | return (-1); 266 | if (i == 15) 267 | return (-1); 268 | 269 | /* handle instruction extension */ 270 | if (c == '.' && i) { 271 | if (flag) 272 | return (-1); 273 | flag = 1; 274 | (*idx)++; 275 | continue; 276 | } 277 | if (flag) { 278 | if (opext) 279 | return (-1); 280 | opext = c; 281 | (*idx)++; 282 | continue; 283 | } 284 | 285 | /* store char */ 286 | name[i++] = c; 287 | hash += c; 288 | hash = (hash << 3) + (hash >> 5) + c; 289 | (*idx)++; 290 | 291 | /* break if '=' directive */ 292 | if (c == '=') 293 | break; 294 | } 295 | 296 | /* check extension */ 297 | if (flag) { 298 | if ((opext != 'L') && (opext != 'H')) 299 | return (-1); 300 | } 301 | 302 | /* end name string */ 303 | name[i] = '\0'; 304 | 305 | /* return if no instruction */ 306 | if (i == 0) 307 | return (-2); 308 | 309 | /* search the instruction in the hash table */ 310 | ptr = inst_tbl[hash & 0xFF]; 311 | 312 | while (ptr) { 313 | if (!strcmp(name, ptr->name)) { 314 | opproc = ptr->proc; 315 | opflg = ptr->flag; 316 | opval = ptr->value; 317 | optype = ptr->type_idx; 318 | 319 | if (opext) { 320 | /* no extension for pseudos */ 321 | if (opflg == PSEUDO) 322 | return (-1); 323 | /* extension valid only for these addressing modes */ 324 | if (!(opflg & (IMM|ZP|ZP_X|ZP_IND_Y|ABS|ABS_X|ABS_Y))) 325 | return (-1); 326 | } 327 | return (i); 328 | } 329 | ptr = ptr->next; 330 | } 331 | 332 | /* didn't find this instruction */ 333 | return (-1); 334 | } 335 | 336 | 337 | /* ---- 338 | * addinst() 339 | * ---- 340 | * add a list of instructions to the instruction 341 | * hash table 342 | */ 343 | 344 | void 345 | addinst(struct t_opcode *optbl) 346 | { 347 | int hash; 348 | int len; 349 | int i; 350 | char *ptr; 351 | char c; 352 | 353 | if (optbl == NULL) 354 | return; 355 | 356 | /* parse list */ 357 | while (optbl->name) { 358 | /* calculate instruction hash value */ 359 | hash = 0; 360 | len = strlen(optbl->name); 361 | ptr = optbl->name; 362 | 363 | for (i = 0; i < len; i++) { 364 | c = *ptr++; 365 | hash += c; 366 | hash = (hash << 3) + (hash >> 5) + c; 367 | } 368 | 369 | hash &= 0xFF; 370 | 371 | /* insert the instruction in the hash table */ 372 | optbl->next = inst_tbl[hash]; 373 | inst_tbl[hash] = optbl; 374 | 375 | /* next instruction */ 376 | optbl++; 377 | } 378 | } 379 | 380 | 381 | /* ---- 382 | * check_eol() 383 | * ---- 384 | * check the end of line for garbage 385 | */ 386 | 387 | int 388 | check_eol(int *ip) 389 | { 390 | while (isspace((int)prlnbuf[*ip])) 391 | (*ip)++; 392 | if (prlnbuf[*ip] == ';' || prlnbuf[*ip] == '\0') 393 | return (1); 394 | else { 395 | error("Syntax error!"); 396 | return (0); 397 | } 398 | } 399 | 400 | /* .if pseudo */ 401 | 402 | void 403 | do_if(int *ip) 404 | { 405 | labldef(loccnt, 1); 406 | 407 | /* get expression */ 408 | if_expr = 1; 409 | if (!evaluate(ip, ';')) { 410 | if_expr = 0; 411 | return; 412 | } 413 | if_expr = 0; 414 | 415 | /* check for '.if' stack overflow */ 416 | if (if_level == 255) { 417 | fatal_error("Too many nested IF/ENDIF!"); 418 | return; 419 | } 420 | in_if = 1; 421 | if_level++; 422 | if_state[if_level] = !skip_lines; 423 | if (!skip_lines) 424 | skip_lines = if_flag[if_level] = value ? 0 : 1; 425 | 426 | if (pass == LAST_PASS) { 427 | loadlc(value, 1); 428 | println(); 429 | } 430 | } 431 | 432 | /* .else pseudo */ 433 | 434 | void 435 | do_else(int *ip) 436 | { 437 | if (!in_if) 438 | fatal_error("Unexpected ELSE!"); 439 | } 440 | 441 | /* .endif pseudo */ 442 | 443 | void 444 | do_endif(int *ip) 445 | { 446 | if (!in_if) 447 | fatal_error("Unexpected ENDIF!"); 448 | } 449 | 450 | /* .ifdef/.ifndef pseudo */ 451 | 452 | void 453 | do_ifdef(int *ip) 454 | { 455 | labldef(loccnt, 1); 456 | 457 | /* skip spaces */ 458 | while (isspace((int)prlnbuf[*ip])) 459 | (*ip)++; 460 | 461 | /* get symbol */ 462 | if (!colsym(ip)) { 463 | error("Syntax error!"); 464 | return; 465 | } 466 | if (!check_eol(ip)) 467 | return; 468 | lablptr = stlook(0); 469 | 470 | /* check for '.if' stack overflow */ 471 | if (if_level == 255) { 472 | fatal_error("Too many nested IF/ENDIF!"); 473 | return; 474 | } 475 | in_if = 1; 476 | if_level++; 477 | if_state[if_level] = !skip_lines; 478 | if (!skip_lines) { 479 | if (optype) { 480 | /* .ifdef */ 481 | skip_lines = if_flag[if_level] = (lablptr == NULL) ? 1 : 0; 482 | } 483 | else { 484 | /* .ifndef */ 485 | skip_lines = if_flag[if_level] = (lablptr == NULL) ? 0 : 1; 486 | } 487 | } 488 | 489 | if (pass == LAST_PASS) { 490 | loadlc(!skip_lines, 1); 491 | println(); 492 | } 493 | } 494 | 495 | -------------------------------------------------------------------------------- /source/symbol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | 10 | /* ---- 11 | * symhash() 12 | * ---- 13 | * calculate the hash value of a symbol 14 | */ 15 | 16 | int 17 | symhash(void) 18 | { 19 | int i; 20 | char c; 21 | int hash = 0; 22 | 23 | /* hash value */ 24 | for (i = 1; i <= symbol[0]; i++) { 25 | c = symbol[i]; 26 | hash += c; 27 | hash = (hash << 3) + (hash >> 5) + c; 28 | } 29 | 30 | /* ok */ 31 | return (hash & 0xFF); 32 | } 33 | 34 | 35 | /* ---- 36 | * colsym() 37 | * ---- 38 | * collect a symbol from prlnbuf into symbol[], 39 | * leaves prlnbuf pointer at first invalid symbol character, 40 | * returns 0 if no symbol collected 41 | */ 42 | 43 | int 44 | colsym(int *ip) 45 | { 46 | int err = 0; 47 | int i = 0; 48 | char c; 49 | 50 | /* get the symbol */ 51 | for (;;) { 52 | c = prlnbuf[*ip]; 53 | if (isdigit(c) && (i == 0)) 54 | break; 55 | if ((!isalnum(c)) && (c != '_') && (c != '.')) 56 | break; 57 | if (i < (SBOLSZ - 1)) 58 | symbol[++i] = c; 59 | (*ip)++; 60 | } 61 | 62 | symbol[0] = i; 63 | symbol[i+1] = '\0'; 64 | 65 | /* check if it's a reserved symbol */ 66 | if (i == 1) { 67 | switch (toupper(symbol[1])) { 68 | case 'A': 69 | case 'X': 70 | case 'Y': 71 | err = 1; 72 | break; 73 | } 74 | } 75 | if (check_keyword()) 76 | err = 1; 77 | 78 | /* error */ 79 | if (err) { 80 | fatal_error("Reserved symbol!"); 81 | return (0); 82 | } 83 | 84 | /* ok */ 85 | return (i); 86 | } 87 | 88 | FILE* stlist_file(FILE *fp, char * basename, int n) 89 | { 90 | if (fp) return fp; 91 | char fname[256]; 92 | strcpy(fname, basename); 93 | if (n < 0) 94 | strcat(fname, ".ram.nl"); 95 | else { 96 | char ext[16]; 97 | sprintf(ext, ".%X.nl", n); 98 | strcat(fname, ext); 99 | } 100 | if ((fp = fopen(fname, "w")) == NULL) { 101 | fprintf(stderr, "can not open file '%s'!\n", fname); 102 | return 0; 103 | } 104 | return fp; 105 | } 106 | 107 | void stlist(char *file, int bank_offset) 108 | { 109 | struct t_symbol *sym; 110 | struct t_symbol *local; 111 | FILE *files[256]; 112 | memset(files, 0, sizeof(files)); 113 | int i, bank, fnum; 114 | for (i = 0; i < sizeof(hash_tbl) / sizeof(hash_tbl[0]); i++) 115 | { 116 | sym = hash_tbl[i]; 117 | if (!sym) continue; 118 | do 119 | { 120 | /* skipping reserved symbols and constants */ 121 | if (sym->reserved || sym->equ) continue; 122 | 123 | bank = sym->value < 0x8000 ? -1 : sym->bank/2 + bank_offset; 124 | fnum = bank >= 0 ? bank : (sizeof(files) / sizeof(FILE*) - 1); 125 | files[fnum] = stlist_file(files[fnum], file, bank); 126 | if (sym->data_size <= 1) 127 | fprintf(files[fnum], "$%04X#%s#\n", sym->value, sym->name+1); 128 | else 129 | fprintf(files[fnum], "$%04X/%02X#%s#\n", sym->value, sym->data_size, sym->name+1); 130 | 131 | local = sym->local; 132 | while (local) 133 | { 134 | bank = local->value < 0x8000 ? -1 : local->bank/2 + bank_offset; 135 | fnum = bank >= 0 ? bank : (sizeof(files) / sizeof(FILE*) - 1); 136 | files[fnum] = stlist_file(files[fnum], file, bank); 137 | if (sym->data_size <= 1) 138 | fprintf(files[fnum], "$%04X#%s (%s)#\n", local->value, local->name+1, sym->name+1); 139 | else 140 | fprintf(files[fnum], "$%04X/%02X#%s (%s)#\n", local->value, local->data_size, local->name+1, sym->name+1); 141 | local = local->next; 142 | } 143 | } while ((sym = sym->next) != NULL); 144 | } 145 | for (i = 0; i < sizeof(files) / sizeof(FILE*); i++) 146 | if (files[i]) 147 | fclose(files[i]); 148 | } 149 | 150 | /* ---- 151 | * stlook() 152 | * ---- 153 | * symbol table lookup 154 | * if found, return pointer to symbol 155 | * else, install symbol as undefined and return pointer 156 | */ 157 | 158 | struct t_symbol *stlook(int flag) 159 | { 160 | struct t_symbol *sym; 161 | int sym_flag = 0; 162 | int hash; 163 | 164 | /* local symbol */ 165 | if (symbol[1] == '.') { 166 | if (glablptr) { 167 | /* search the symbol in the local list */ 168 | sym = glablptr->local; 169 | 170 | while (sym) { 171 | if (!strcmp(symbol, sym->name)) 172 | break; 173 | sym = sym->next; 174 | } 175 | 176 | /* new symbol */ 177 | if (sym == NULL) { 178 | if (flag) { 179 | sym = stinstall(0, 1); 180 | sym_flag = 1; 181 | } 182 | } 183 | } 184 | else { 185 | error("Local symbol not allowed here!"); 186 | return (NULL); 187 | } 188 | } 189 | 190 | /* global symbol */ 191 | else { 192 | /* search symbol */ 193 | hash = symhash(); 194 | sym = hash_tbl[hash]; 195 | while (sym) { 196 | if (!strcmp(symbol, sym->name)) 197 | break; 198 | sym = sym->next; 199 | } 200 | 201 | /* new symbol */ 202 | if (sym == NULL) { 203 | if (flag) { 204 | sym = stinstall(hash, 0); 205 | sym_flag = 1; 206 | } 207 | } 208 | } 209 | 210 | /* incremente symbol reference counter */ 211 | if (sym_flag == 0) { 212 | if (sym) 213 | sym->refcnt++; 214 | } 215 | 216 | /* ok */ 217 | return (sym); 218 | } 219 | 220 | 221 | /* ---- 222 | * stinstall() 223 | * ---- 224 | * install symbol into symbol hash table 225 | */ 226 | 227 | struct t_symbol *stinstall(int hash, int type) 228 | { 229 | struct t_symbol *sym; 230 | 231 | /* allocate symbol structure */ 232 | if ((sym = (void *)malloc(sizeof(struct t_symbol))) == NULL) { 233 | fatal_error("Out of memory!"); 234 | return (NULL); 235 | } 236 | 237 | /* init the symbol struct */ 238 | sym->type = if_expr ? IFUNDEF : UNDEF; 239 | sym->value = 0; 240 | sym->str_value = NULL; 241 | sym->local = NULL; 242 | sym->proc = NULL; 243 | sym->bank = RESERVED_BANK; 244 | sym->nb = 0; 245 | sym->size = 0; 246 | sym->page = -1; 247 | sym->vram = -1; 248 | sym->pal = -1; 249 | sym->refcnt = 0; 250 | sym->reserved = 0; 251 | sym->equ = 0; 252 | sym->data_type = -1; 253 | sym->data_size = 0; 254 | strcpy(sym->name, symbol); 255 | 256 | /* add the symbol to the hash table */ 257 | if (type) { 258 | /* local */ 259 | sym->next = glablptr->local; 260 | glablptr->local = sym; 261 | } 262 | else { 263 | /* global */ 264 | sym->next = hash_tbl[hash]; 265 | hash_tbl[hash] = sym; 266 | } 267 | 268 | /* ok */ 269 | return (sym); 270 | } 271 | 272 | 273 | /* ---- 274 | * labldef() 275 | * ---- 276 | * assign to label pointed to by lablptr, 277 | * checking for valid definition, etc. 278 | */ 279 | 280 | int 281 | labldef(int lval, int flag) 282 | { 283 | char c; 284 | 285 | /* check for NULL ptr */ 286 | if (lablptr == NULL) 287 | return (0); 288 | 289 | /* adjust symbol address */ 290 | if (flag) 291 | lval = (lval & 0x1FFF) | (page << 13); 292 | 293 | /* first pass */ 294 | if (pass == FIRST_PASS) { 295 | switch (lablptr->type) { 296 | /* undefined */ 297 | case UNDEF: 298 | lablptr->type = DEFABS; 299 | lablptr->value = lval; 300 | break; 301 | 302 | /* already defined - error */ 303 | case IFUNDEF: 304 | error("Can not define this label, declared as undefined in an IF expression!"); 305 | return (-1); 306 | 307 | case MACRO: 308 | error("Symbol already used by a macro!"); 309 | return (-1); 310 | 311 | case FUNC: 312 | error("Symbol already used by a function!"); 313 | return (-1); 314 | 315 | case DEFSTR: 316 | error("Symbol already used by a string constant!"); 317 | return (-1); 318 | 319 | default: 320 | /* reserved label */ 321 | if (lablptr->reserved) { 322 | fatal_error("Reserved symbol!"); 323 | return (-1); 324 | } 325 | 326 | /* compare the values */ 327 | if (lablptr->value == lval) 328 | break; 329 | 330 | /* normal label */ 331 | lablptr->type = MDEF; 332 | lablptr->value = 0; 333 | error("Label multiply defined!"); 334 | return (-1); 335 | } 336 | } 337 | 338 | /* second pass */ 339 | else { 340 | if ((lablptr->value != lval) || 341 | ((flag) && (bank < bank_limit) && (lablptr->bank != bank_base + bank))) 342 | { 343 | fatal_error("Internal error[1]!"); 344 | return (-1); 345 | } 346 | } 347 | 348 | /* update symbol data */ 349 | if (flag) { 350 | if (section == S_CODE) 351 | lablptr->proc = proc_ptr; 352 | lablptr->bank = bank_base + bank; 353 | lablptr->page = page; 354 | 355 | /* check if it's a local or global symbol */ 356 | c = lablptr->name[1]; 357 | if (c == '.') 358 | /* local */ 359 | lastlabl = NULL; 360 | else { 361 | /* global */ 362 | glablptr = lablptr; 363 | lastlabl = lablptr; 364 | } 365 | } 366 | 367 | /* ok */ 368 | return (0); 369 | } 370 | 371 | 372 | /* ---- 373 | * lablset() 374 | * ---- 375 | * create/update a reserved symbol 376 | */ 377 | 378 | void 379 | lablset(char *name, int val) 380 | { 381 | int len; 382 | 383 | len = strlen(name); 384 | lablptr = NULL; 385 | 386 | if (len) { 387 | symbol[0] = len; 388 | strcpy(&symbol[1], name); 389 | lablptr = stlook(1); 390 | 391 | if (lablptr) { 392 | lablptr->type = DEFABS; 393 | lablptr->value = val; 394 | lablptr->reserved = 1; 395 | } 396 | } 397 | 398 | /* ok */ 399 | return; 400 | } 401 | 402 | 403 | /* ---- 404 | * lablremap() 405 | * ---- 406 | * remap all the labels 407 | */ 408 | 409 | void 410 | lablremap(void) 411 | { 412 | struct t_symbol *sym; 413 | int i; 414 | 415 | /* browse the symbol table */ 416 | for (i = 0; i < 256; i++) { 417 | sym = hash_tbl[i]; 418 | while (sym) { 419 | /* remap the bank */ 420 | if (sym->bank <= bank_limit) 421 | sym->bank += bank_base; 422 | sym = sym->next; 423 | } 424 | } 425 | } 426 | 427 | 428 | /* ---- 429 | * constset() 430 | * ---- 431 | * create/update a predefined constant (.equ) 432 | */ 433 | 434 | void 435 | constset(char *name, int val) 436 | { 437 | int len; 438 | 439 | len = strlen(name); 440 | lablptr = NULL; 441 | 442 | if (len) { 443 | symbol[0] = len; 444 | strcpy(&symbol[1], name); 445 | lablptr = stlook(1); 446 | 447 | if (lablptr) { 448 | lablptr->type = DEFABS; 449 | lablptr->value = val; 450 | lablptr->equ = 1; 451 | } 452 | } 453 | 454 | /* ok */ 455 | return; 456 | } 457 | 458 | /* ---- 459 | * strconstset() 460 | * ---- 461 | * create/update a predefined string constant 462 | */ 463 | 464 | void 465 | strconstset(char *name, char *val) 466 | { 467 | int len; 468 | 469 | len = strlen(name); 470 | lablptr = NULL; 471 | 472 | if (len) { 473 | symbol[0] = len; 474 | strcpy(&symbol[1], name); 475 | lablptr = stlook(1); 476 | 477 | if (lablptr) { 478 | if (lablptr->str_value) 479 | error("Symbol already used by a string constant!"); 480 | lablptr->type = DEFSTR; 481 | lablptr->str_value = malloc(strlen(val) + 1); 482 | strcpy((char*)lablptr->str_value, val); 483 | lablptr->value = -1; 484 | lablptr->equ = 1; 485 | } 486 | } 487 | 488 | /* ok */ 489 | return; 490 | } 491 | 492 | /* ---- 493 | * strconstget() 494 | * ---- 495 | * fills buffer with a predefined string constant value 496 | */ 497 | 498 | int 499 | strconstget(char *buffer, int size) 500 | { 501 | t_symbol* lablptr = stlook(0); 502 | 503 | if (lablptr && (lablptr->type == DEFSTR)) { 504 | if (strlen(lablptr->str_value) > size) 505 | error("String too long!"); 506 | strcpy(buffer, lablptr->str_value); 507 | } 508 | else if (lablptr) 509 | { 510 | error("It is not a symbol constant!"); 511 | return (-1); 512 | } 513 | else { 514 | error("Symbol constant is not defined!"); 515 | return (-1); 516 | } 517 | 518 | /* ok */ 519 | return (0); 520 | } 521 | -------------------------------------------------------------------------------- /source/nes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "defs.h" 4 | #include "externs.h" 5 | #include "protos.h" 6 | #include "nes.h" 7 | 8 | /* locals */ 9 | static int ines_prg = 0; /* size of PRG */ 10 | static int ines_chr = 0; /* size of CHR */ 11 | static int ines_mapper = 0; /* ROM mapper type */ 12 | static int ines_submapper = 0; /* ROM submapper type */ 13 | static int ines_mirroring = 0; /* ROM mirroring type */ 14 | static int ines_prg_ram = 0; /* size of PRG RAM */ 15 | static int ines_prg_nvram = 0; /* size of PRG NVRAM */ 16 | static int ines_chr_ram = 0; /* size of CHR RAM */ 17 | static int ines_chr_nvram = 0; /* size of CHR NVRAM */ 18 | static int ines_battery = 0; /* non-volatile memory flag */ 19 | static int ines_timing = 0; /* CPU/PPU timing */ 20 | 21 | static struct INES20 { /* INES rom header */ 22 | unsigned char id[4]; 23 | unsigned char prg_size_lsb; 24 | unsigned char chr_size_lsb; 25 | unsigned char flags6; 26 | unsigned char flags7; 27 | unsigned char mapper_msb_submapper; 28 | unsigned char prg_chr_size_msb; 29 | unsigned char prg_ram_size; 30 | unsigned char chr_ram_size; 31 | unsigned char timing; 32 | unsigned char system_console_type; 33 | unsigned char misc_roms; 34 | unsigned char exp_device; 35 | } header; 36 | 37 | 38 | /* ---- 39 | * write_header() 40 | * ---- 41 | * generate and write rom header 42 | */ 43 | 44 | void 45 | nes_write_header(FILE *f, int banks) 46 | { 47 | /* setup INES header */ 48 | memset(&header, 0, sizeof(header)); 49 | header.id[0] = 'N'; 50 | header.id[1] = 'E'; 51 | header.id[2] = 'S'; 52 | header.id[3] = 26; 53 | header.prg_size_lsb = ines_prg & 0xFF; 54 | header.chr_size_lsb = ines_chr & 0xFF; 55 | switch (ines_mirroring) 56 | { 57 | default: 58 | case 0: /* Horizontal or mapper-controlled */ 59 | header.flags6 |= 0; 60 | break; 61 | case 1: /* Vertical */ 62 | header.flags6 |= 1; 63 | break; 64 | case 2: /* Hard-wired four-screen mode */ 65 | case 3: 66 | case 4: 67 | header.flags6 |= 8; 68 | break; 69 | } 70 | if (ines_prg_nvram || ines_chr_nvram) 71 | ines_battery = 1; 72 | if (ines_battery) 73 | header.flags6 |= 2; 74 | header.flags6 |= (ines_mapper & 0x0F) << 4; /* Mapper Number D0..D3 */ 75 | // TODO: Console Type 76 | header.flags7 |= 8; /* NES 2.0 identifier */ 77 | header.flags7 |= (ines_mapper & 0xF0); /* Mapper Number D4..D7 */ 78 | header.mapper_msb_submapper |= (ines_mapper & 0xF00) >> 8; 79 | header.mapper_msb_submapper |= ines_submapper << 4; 80 | header.prg_chr_size_msb |= (ines_prg & 0xF00) >> 8; 81 | header.prg_chr_size_msb |= (ines_chr & 0xF00) >> 4; 82 | if (ines_battery && !ines_prg_ram && !ines_prg_nvram) /* for backward compatibility */ 83 | ines_prg_nvram = 7; 84 | header.prg_ram_size |= ines_prg_ram & 0x0F; 85 | header.prg_ram_size |= (ines_prg_nvram & 0x0F) << 4; 86 | if (!ines_chr && !ines_chr_ram) /* for backward compatibility */ 87 | ines_chr_ram = 7; 88 | header.chr_ram_size |= ines_chr_ram & 0x0F; 89 | header.chr_ram_size |= (ines_chr_nvram & 0x0F) << 4; 90 | header.timing = ines_timing; 91 | // TODO: System Type 92 | // TODO: Miscellaneous ROMs 93 | // TODO: Default Expansion Device 94 | 95 | /* write */ 96 | fwrite(&header, sizeof(header), 1, f); 97 | } 98 | 99 | 100 | /* ---- 101 | * pack_8x8_tile() 102 | * ---- 103 | * encode a 8x8 tile for the NES 104 | */ 105 | 106 | int 107 | nes_pack_8x8_tile(unsigned char *buffer, void *data, int line_offset, int format) 108 | { 109 | int i, j; 110 | int cnt, err; 111 | unsigned int pixel; 112 | unsigned char *ptr; 113 | unsigned int *packed; 114 | 115 | /* pack the tile only in the last pass */ 116 | if (pass != LAST_PASS) 117 | return (16); 118 | 119 | /* clear buffer */ 120 | memset(buffer, 0, 16); 121 | 122 | /* encode the tile */ 123 | switch (format) { 124 | case CHUNKY_TILE: 125 | /* 8-bit chunky format */ 126 | cnt = 0; 127 | ptr = data; 128 | 129 | for (i = 0; i < 8; i++) { 130 | for (j = 0; j < 8; j++) { 131 | pixel = ptr[j ^ 0x07]; 132 | buffer[cnt] |= (pixel & 0x01) ? (1 << j) : 0; 133 | buffer[cnt+8] |= (pixel & 0x02) ? (1 << j) : 0; 134 | } 135 | ptr += line_offset; 136 | cnt += 1; 137 | } 138 | break; 139 | 140 | case PACKED_TILE: 141 | /* 4-bit packed format */ 142 | cnt = 0; 143 | err = 0; 144 | packed = data; 145 | 146 | for (i = 0; i < 8; i++) { 147 | pixel = packed[i]; 148 | 149 | for (j = 0; j < 8; j++) { 150 | /* check for errors */ 151 | if (pixel & 0x0C) 152 | err++; 153 | 154 | /* convert the tile */ 155 | buffer[cnt] |= (pixel & 0x01) ? (1 << j) : 0; 156 | buffer[cnt+8] |= (pixel & 0x02) ? (1 << j) : 0; 157 | pixel >>= 4; 158 | } 159 | cnt += 1; 160 | } 161 | 162 | /* error message */ 163 | if (err) 164 | error("Incorrect pixel color index!"); 165 | break; 166 | 167 | default: 168 | /* other formats not supported */ 169 | error("Internal error: unsupported format passed to 'pack_8x8_tile'!"); 170 | break; 171 | } 172 | 173 | /* ok */ 174 | return (16); 175 | } 176 | 177 | 178 | /* ---- 179 | * do_defchr() 180 | * ---- 181 | * .defchr pseudo 182 | */ 183 | 184 | void 185 | nes_defchr(int *ip) 186 | { 187 | unsigned char buffer[16]; 188 | unsigned int data[8]; 189 | int size; 190 | int i; 191 | 192 | /* define label */ 193 | labldef(loccnt, 1); 194 | 195 | /* output infos */ 196 | data_loccnt = loccnt; 197 | data_size = 3; 198 | data_level = 3; 199 | 200 | /* get tile data */ 201 | for (i = 0; i < 8; i++) { 202 | /* get value */ 203 | if (!evaluate(ip, (i < 7) ? ',' : ';')) 204 | return; 205 | 206 | /* store value */ 207 | data[i] = value; 208 | } 209 | 210 | /* encode tile */ 211 | size = nes_pack_8x8_tile(buffer, data, 0, PACKED_TILE); 212 | 213 | /* store tile */ 214 | putbuffer(buffer, size); 215 | 216 | /* output line */ 217 | if (pass == LAST_PASS) 218 | println(); 219 | } 220 | 221 | 222 | /* ---- 223 | * do_inesprg() 224 | * ---- 225 | * .inesprg pseudo 226 | */ 227 | 228 | void 229 | nes_inesprg(int *ip) 230 | { 231 | if (!evaluate(ip, ';')) 232 | return; 233 | 234 | if ((value < 0) || (value > 0xEFF * 0x4000)) 235 | { 236 | error("PRG size value out of range!"); 237 | 238 | return; 239 | } else if (value > 0xEFF) 240 | { 241 | if ((value % 0x4000) != 0) 242 | { 243 | error("Invalid PRG size value!"); 244 | 245 | return; 246 | } 247 | value /= 0x4000; 248 | } 249 | 250 | ines_prg = value; 251 | 252 | if (pass == LAST_PASS) 253 | { 254 | println(); 255 | } 256 | } 257 | 258 | 259 | /* ---- 260 | * do_ineschr() 261 | * ---- 262 | * .ineschr pseudo 263 | */ 264 | 265 | void 266 | nes_ineschr(int *ip) 267 | { 268 | if (!evaluate(ip, ';')) 269 | return; 270 | 271 | if ((value < 0) || (value > 0xEFF * 0x2000)) 272 | { 273 | error("CHR size value out of range!"); 274 | 275 | return; 276 | } else if (value > 0xEFF) 277 | { 278 | if ((value % 0x2000) != 0) 279 | { 280 | error("Invalid CHR size value!"); 281 | 282 | return; 283 | } 284 | value /= 0x2000; 285 | } 286 | 287 | ines_chr = value; 288 | 289 | if (pass == LAST_PASS) 290 | { 291 | println(); 292 | } 293 | } 294 | 295 | /* ---- 296 | * do_inesprgram() 297 | * ---- 298 | * .inesprgram pseudo 299 | */ 300 | 301 | void 302 | nes_inesprgram(int *ip) 303 | { 304 | if (!evaluate(ip, ';')) 305 | return; 306 | 307 | if ((value < 0) || (value > 0x200000)) 308 | { 309 | error("PRG RAM value out of range!"); 310 | 311 | return; 312 | } else if (value > 15) 313 | { 314 | unsigned char shift = 0; 315 | while (((64 << shift) != value) && (shift < 16)) shift++; 316 | if (shift >= 16) 317 | { 318 | error("Invalid PRG RAM value!"); 319 | 320 | return; 321 | } 322 | value = shift; 323 | } 324 | 325 | ines_prg_ram = value; 326 | 327 | if (pass == LAST_PASS) 328 | { 329 | println(); 330 | } 331 | } 332 | 333 | 334 | /* ---- 335 | * do_inesprgnvram() 336 | * ---- 337 | * .inesprgnvram pseudo 338 | */ 339 | 340 | void 341 | nes_inesprgnvram(int *ip) 342 | { 343 | if (!evaluate(ip, ';')) 344 | return; 345 | 346 | if ((value < 0) || (value > 0x200000)) 347 | { 348 | error("PRG NVRAM value out of range!"); 349 | 350 | return; 351 | } else if (value > 15) 352 | { 353 | unsigned char shift = 0; 354 | while (((64 << shift) != value) && (shift < 16)) shift++; 355 | if (shift >= 16) 356 | { 357 | error("Invalid PRG NVRAM value!"); 358 | 359 | return; 360 | } 361 | value = shift; 362 | } 363 | 364 | ines_prg_nvram = value; 365 | if (value) ines_battery = 1; 366 | 367 | if (pass == LAST_PASS) 368 | { 369 | println(); 370 | } 371 | } 372 | 373 | 374 | /* ---- 375 | * do_ineschrram() 376 | * ---- 377 | * .ineschrram pseudo 378 | */ 379 | 380 | void 381 | nes_ineschrram(int *ip) 382 | { 383 | if (!evaluate(ip, ';')) 384 | return; 385 | 386 | if ((value < 0) || (value > 0x200000)) 387 | { 388 | error("CHR RAM value out of range!"); 389 | 390 | return; 391 | } else if (value > 15) 392 | { 393 | unsigned char shift = 0; 394 | while (((64 << shift) != value) && (shift < 16)) shift++; 395 | if (shift >= 16) 396 | { 397 | error("Invalid CHR RAM value!"); 398 | 399 | return; 400 | } 401 | value = shift; 402 | } 403 | 404 | ines_chr_ram = value; 405 | 406 | if (pass == LAST_PASS) 407 | { 408 | println(); 409 | } 410 | } 411 | 412 | 413 | /* ---- 414 | * do_ineschrnvram() 415 | * ---- 416 | * .ineschrnvram pseudo 417 | */ 418 | 419 | void 420 | nes_ineschrnvram(int *ip) 421 | { 422 | if (!evaluate(ip, ';')) 423 | return; 424 | 425 | if ((value < 0) || (value > 0x200000)) 426 | { 427 | error("CHR NVRAM value out of range!"); 428 | 429 | return; 430 | } else if (value > 15) 431 | { 432 | unsigned char shift = 0; 433 | while (((64 << shift) != value) && (shift < 16)) shift++; 434 | if (shift >= 16) 435 | { 436 | error("Invalid CHR NVRAM value!"); 437 | 438 | return; 439 | } 440 | value = shift; 441 | } 442 | 443 | ines_chr_nvram = value; 444 | if (value) ines_battery = 1; 445 | 446 | if (pass == LAST_PASS) 447 | { 448 | println(); 449 | } 450 | } 451 | 452 | 453 | /* ---- 454 | * do_inesmap() 455 | * ---- 456 | * .inesmap pseudo 457 | */ 458 | 459 | void 460 | nes_inesmap(int *ip) 461 | { 462 | if (!evaluate(ip, ';')) 463 | return; 464 | 465 | if ((value < 0) || (value > 4095)) 466 | { 467 | error("Mapper value out of range!"); 468 | 469 | return; 470 | } 471 | 472 | ines_mapper = value; 473 | 474 | if (pass == LAST_PASS) 475 | { 476 | println(); 477 | } 478 | } 479 | 480 | 481 | /* ---- 482 | * do_inessubmap() 483 | * ---- 484 | * .inessubmap pseudo 485 | */ 486 | 487 | void 488 | nes_inessubmap(int *ip) 489 | { 490 | if (!evaluate(ip, ';')) 491 | return; 492 | 493 | if ((value < 0) || (value > 15)) 494 | { 495 | error("Submapper value out of range!"); 496 | 497 | return; 498 | } 499 | 500 | ines_submapper = value; 501 | 502 | if (pass == LAST_PASS) 503 | { 504 | println(); 505 | } 506 | } 507 | 508 | 509 | /* ---- 510 | * do_inesmir() 511 | * ---- 512 | * .inesmir pseudo 513 | */ 514 | 515 | void 516 | nes_inesmir(int *ip) 517 | { 518 | if (!evaluate(ip, ';')) 519 | return; 520 | 521 | if ((value < 0) || (value > 4)) 522 | { 523 | error("Mirror value out of range!"); 524 | 525 | return; 526 | } 527 | 528 | ines_mirroring = value; 529 | 530 | if (pass == LAST_PASS) 531 | { 532 | println(); 533 | } 534 | } 535 | 536 | 537 | /* ---- 538 | * do_inesbat() 539 | * ---- 540 | * .inesbat pseudo 541 | */ 542 | 543 | void 544 | nes_inesbat(int *ip) 545 | { 546 | if (!evaluate(ip, ';')) 547 | return; 548 | 549 | if ((value < 0) || (value > 1)) 550 | { 551 | error("Battery value out of range!"); 552 | 553 | return; 554 | } 555 | 556 | ines_battery = value; 557 | 558 | if (pass == LAST_PASS) 559 | { 560 | println(); 561 | } 562 | } 563 | 564 | 565 | /* ---- 566 | * do_inestim() 567 | * ---- 568 | * .inestim pseudo 569 | */ 570 | 571 | void 572 | nes_inestim(int *ip) 573 | { 574 | if (!evaluate(ip, ';')) 575 | return; 576 | 577 | if ((value < 0) || (value > 3)) 578 | { 579 | error("Timing value out of range!"); 580 | 581 | return; 582 | } 583 | 584 | ines_timing = value; 585 | 586 | if (pass == LAST_PASS) 587 | { 588 | println(); 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /source/code.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | 9 | unsigned char auto_inc; 10 | unsigned char auto_tag; 11 | unsigned int auto_tag_value; 12 | 13 | 14 | /* ---- 15 | * class1() 16 | * ---- 17 | * 1 byte, no operand field 18 | */ 19 | 20 | void 21 | class1(int *ip) 22 | { 23 | check_eol(ip); 24 | 25 | /* update location counter */ 26 | loccnt++; 27 | 28 | /* generate code */ 29 | if (pass == LAST_PASS) { 30 | /* opcode */ 31 | putbyte(data_loccnt, opval); 32 | 33 | /* output line */ 34 | println(); 35 | } 36 | } 37 | 38 | 39 | /* ---- 40 | * class2() 41 | * ---- 42 | * 2 bytes, relative addressing 43 | */ 44 | 45 | void 46 | class2(int *ip) 47 | { 48 | unsigned int addr; 49 | 50 | /* update location counter */ 51 | loccnt += 2; 52 | 53 | /* get destination address */ 54 | if (!evaluate(ip, ';')) 55 | return; 56 | 57 | /* generate code */ 58 | if (pass == LAST_PASS) { 59 | /* opcode */ 60 | putbyte(data_loccnt, opval); 61 | 62 | /* calculate branch offset */ 63 | addr = value - (loccnt + (page << 13)); 64 | 65 | /* check range */ 66 | if (addr > 0x7F && addr < 0xFFFFFF80) { 67 | error("Branch address out of range!"); 68 | return; 69 | } 70 | 71 | /* offset */ 72 | putbyte(data_loccnt+1, addr); 73 | 74 | /* output line */ 75 | println(); 76 | } 77 | } 78 | 79 | 80 | /* ---- 81 | * class3() 82 | * ---- 83 | * 2 bytes, inherent addressing 84 | */ 85 | 86 | void 87 | class3(int *ip) 88 | { 89 | check_eol(ip); 90 | 91 | /* update location counter */ 92 | loccnt += 2; 93 | 94 | /* generate code */ 95 | if (pass == LAST_PASS) { 96 | /* opcodes */ 97 | putbyte(data_loccnt, opval); 98 | putbyte(data_loccnt+1, optype); 99 | 100 | /* output line */ 101 | println(); 102 | } 103 | } 104 | 105 | 106 | /* ---- 107 | * class4() 108 | * ---- 109 | * various addressing modes 110 | */ 111 | 112 | void 113 | class4(int *ip) 114 | { 115 | char buffer[32]; 116 | char c; 117 | int len, mode; 118 | int i; 119 | 120 | /* skip spaces */ 121 | while (isspace((int)prlnbuf[*ip])) 122 | (*ip)++; 123 | 124 | /* low/high byte prefix string */ 125 | if (isalpha((int)prlnbuf[*ip])) { 126 | len = 0; 127 | i = *ip; 128 | 129 | /* extract string */ 130 | for (;;) { 131 | c = prlnbuf[i]; 132 | if (c == '\0' || c == ' ' || c == '\t' || c == ';') 133 | break; 134 | if ((!isalpha((int)c) && c != '_') || (len == 31)) { 135 | len = 0; 136 | break; 137 | } 138 | buffer[len++] = c; 139 | i++; 140 | } 141 | 142 | /* check */ 143 | if (len) { 144 | buffer[len] = '\0'; 145 | 146 | if (strcasecmp(buffer, "low_byte") == 0) { 147 | opext = 'L'; 148 | *ip = i; 149 | } 150 | if (strcasecmp(buffer, "high_byte") == 0) { 151 | opext = 'H'; 152 | *ip = i; 153 | } 154 | } 155 | } 156 | 157 | /* get operand */ 158 | mode = getoperand(ip, opflg, ';'); 159 | if (!mode) 160 | return; 161 | 162 | /* make opcode */ 163 | if (pass == LAST_PASS) { 164 | for (i = 0; i < 32; i++) { 165 | if (mode & (1 << i)) 166 | break; 167 | } 168 | opval += opvaltab[optype][i]; 169 | } 170 | 171 | /* auto-tag */ 172 | if (auto_tag) { 173 | if (pass == LAST_PASS) { 174 | putbyte(loccnt, 0xA0); 175 | putbyte(loccnt+1, auto_tag_value); 176 | } 177 | loccnt += 2; 178 | } 179 | 180 | /* generate code */ 181 | switch(mode) { 182 | case ACC: 183 | /* one byte */ 184 | if (pass == LAST_PASS) 185 | putbyte(loccnt, opval); 186 | 187 | loccnt++; 188 | break; 189 | 190 | case IMM: 191 | case ZP: 192 | case ZP_X: 193 | case ZP_Y: 194 | case ZP_IND: 195 | case ZP_IND_X: 196 | case ZP_IND_Y: 197 | /* two bytes */ 198 | if (pass == LAST_PASS) { 199 | putbyte(loccnt, opval); 200 | putbyte(loccnt+1, value); 201 | } 202 | loccnt += 2; 203 | break; 204 | 205 | case ABS: 206 | case ABS_X: 207 | case ABS_Y: 208 | case ABS_IND: 209 | case ABS_IND_X: 210 | /* three bytes */ 211 | if (pass == LAST_PASS) { 212 | putbyte(loccnt, opval); 213 | putword(loccnt+1, value); 214 | } 215 | loccnt += 3; 216 | break; 217 | } 218 | 219 | /* auto-increment */ 220 | if (auto_inc) { 221 | if (pass == LAST_PASS) 222 | putbyte(loccnt, auto_inc); 223 | 224 | loccnt += 1; 225 | } 226 | 227 | /* output line */ 228 | if (pass == LAST_PASS) 229 | println(); 230 | } 231 | 232 | 233 | /* ---- 234 | * class5() 235 | * ---- 236 | * 3 bytes, zp/relative addressing 237 | */ 238 | 239 | void 240 | class5(int *ip) 241 | { 242 | int zp; 243 | unsigned int addr; 244 | int mode; 245 | 246 | /* update location counter */ 247 | loccnt += 3; 248 | 249 | /* get first operand */ 250 | mode = getoperand(ip, ZP, ','); 251 | zp = value; 252 | if (!mode) 253 | return; 254 | 255 | /* get second operand */ 256 | mode = getoperand(ip, ABS, ';'); 257 | if (!mode) 258 | return; 259 | 260 | /* generate code */ 261 | if (pass == LAST_PASS) { 262 | /* opcodes */ 263 | putbyte(data_loccnt, opval); 264 | putbyte(data_loccnt+1, zp); 265 | 266 | /* calculate branch offset */ 267 | addr = value - (loccnt + (page << 13)); 268 | 269 | /* check range */ 270 | if (addr > 0x7F && addr < 0xFFFFFF80) { 271 | error("Branch address out of range!"); 272 | return; 273 | } 274 | 275 | /* offset */ 276 | putbyte(data_loccnt+2, addr); 277 | 278 | /* output line */ 279 | println(); 280 | } 281 | } 282 | 283 | 284 | /* ---- 285 | * class6() 286 | * ---- 287 | * 7 bytes, src/dest/length 288 | */ 289 | 290 | void 291 | class6(int *ip) 292 | { 293 | int i; 294 | int addr[3]; 295 | 296 | /* update location counter */ 297 | loccnt +=7; 298 | 299 | /* get operands */ 300 | for (i = 0; i < 3; i++) { 301 | if (!evaluate(ip, (i < 2) ? ',' : ';')) 302 | return; 303 | if (pass == LAST_PASS) { 304 | if (value & 0xFFFF0000) { 305 | error("Operand size error!"); 306 | return; 307 | } 308 | } 309 | addr[i] = value; 310 | } 311 | 312 | /* generate code */ 313 | if (pass == LAST_PASS) { 314 | /* opcodes */ 315 | putbyte(data_loccnt, opval); 316 | putword(data_loccnt+1, addr[0]); 317 | putword(data_loccnt+3, addr[1]); 318 | putword(data_loccnt+5, addr[2]); 319 | 320 | /* output line */ 321 | println(); 322 | } 323 | } 324 | 325 | 326 | /* ---- 327 | * class7() 328 | * ---- 329 | * TST instruction 330 | */ 331 | 332 | void 333 | class7(int *ip) 334 | { 335 | int mode; 336 | int addr, imm; 337 | 338 | /* get first operand */ 339 | mode = getoperand(ip, IMM, ','); 340 | imm = value; 341 | if (!mode) 342 | return; 343 | 344 | /* get second operand */ 345 | mode = getoperand(ip, (ZP | ZP_X | ABS | ABS_X), ';'); 346 | addr = value; 347 | if (!mode) 348 | return; 349 | 350 | /* make opcode */ 351 | if (mode & (ZP | ZP_X)) 352 | opval = 0x83; 353 | if (mode & (ABS | ABS_X)) 354 | opval = 0x93; 355 | if (mode & (ZP_X | ABS_X)) 356 | opval+= 0x20; 357 | 358 | /* generate code */ 359 | if (pass == LAST_PASS) { 360 | /* opcodes */ 361 | putbyte(loccnt, opval); 362 | putbyte(loccnt+1, imm); 363 | 364 | if (mode & (ZP | ZP_X)) 365 | /* zero page */ 366 | putbyte(loccnt+2, addr); 367 | else 368 | /* absolute */ 369 | putword(loccnt+2, addr); 370 | } 371 | 372 | /* update location counter */ 373 | if (mode & (ZP | ZP_X)) 374 | loccnt += 3; 375 | else 376 | loccnt += 4; 377 | 378 | /* auto-increment */ 379 | if (auto_inc) { 380 | if (pass == LAST_PASS) 381 | putbyte(loccnt, auto_inc); 382 | 383 | loccnt += 1; 384 | } 385 | 386 | /* output line */ 387 | if (pass == LAST_PASS) 388 | println(); 389 | } 390 | 391 | 392 | /* ---- 393 | * class8() 394 | * ---- 395 | * TAM/TMA instruction 396 | */ 397 | 398 | void 399 | class8(int *ip) 400 | { 401 | int mode; 402 | 403 | /* update location counter */ 404 | loccnt += 2; 405 | 406 | /* get operand */ 407 | mode = getoperand(ip, IMM, ';'); 408 | if (!mode) 409 | return; 410 | 411 | /* generate code */ 412 | if (pass == LAST_PASS) { 413 | /* check page index */ 414 | if (value & 0xF8) { 415 | error("Incorrect page index!"); 416 | return; 417 | } 418 | 419 | /* opcodes */ 420 | putbyte(data_loccnt, opval); 421 | putbyte(data_loccnt+1, (1 << value)); 422 | 423 | /* output line */ 424 | println(); 425 | } 426 | } 427 | 428 | 429 | /* ---- 430 | * class9() 431 | * ---- 432 | * RMB/SMB instructions 433 | */ 434 | 435 | void 436 | class9(int *ip) 437 | { 438 | int bit; 439 | int mode; 440 | 441 | /* update location counter */ 442 | loccnt += 2; 443 | 444 | /* get the bit index */ 445 | mode = getoperand(ip, IMM, ','); 446 | bit = value; 447 | if (!mode) 448 | return; 449 | 450 | /* get the zero page address */ 451 | mode = getoperand(ip, ZP, ';'); 452 | if (!mode) 453 | return; 454 | 455 | /* generate code */ 456 | if (pass == LAST_PASS) { 457 | /* check bit number */ 458 | if (bit > 7) { 459 | error("Incorrect bit number!"); 460 | return; 461 | } 462 | 463 | /* opcodes */ 464 | putbyte(data_loccnt, opval + (bit << 4)); 465 | putbyte(data_loccnt+1, value); 466 | 467 | /* output line */ 468 | println(); 469 | } 470 | } 471 | 472 | 473 | /* ---- 474 | * class10() 475 | * ---- 476 | * BBR/BBS instructions 477 | */ 478 | 479 | void 480 | class10(int *ip) 481 | { 482 | int bit; 483 | int zp; 484 | int mode; 485 | unsigned int addr; 486 | 487 | /* update location counter */ 488 | loccnt += 3; 489 | 490 | /* get the bit index */ 491 | mode = getoperand(ip, IMM, ','); 492 | bit = value; 493 | if (!mode) 494 | return; 495 | 496 | /* get the zero page address */ 497 | mode = getoperand(ip, ZP, ','); 498 | zp = value; 499 | if (!mode) 500 | return; 501 | 502 | /* get the jump address */ 503 | mode = getoperand(ip, ABS, ';'); 504 | if (!mode) 505 | return; 506 | 507 | /* generate code */ 508 | if (pass == LAST_PASS) { 509 | /* check bit number */ 510 | if (bit > 7) { 511 | error("Incorrect bit number!"); 512 | return; 513 | } 514 | 515 | /* opcodes */ 516 | putbyte(data_loccnt, opval + (bit << 4)); 517 | putbyte(data_loccnt+1, zp); 518 | 519 | /* calculate branch offset */ 520 | addr = value - (loccnt + (page << 13)); 521 | 522 | /* check range */ 523 | if (addr > 0x7F && addr < 0xFFFFFF80) { 524 | error("Branch address out of range!"); 525 | return; 526 | } 527 | 528 | /* offset */ 529 | putbyte(data_loccnt+2, addr); 530 | 531 | /* output line */ 532 | println(); 533 | } 534 | } 535 | 536 | 537 | /* ---- 538 | * getoperand() 539 | * ---- 540 | */ 541 | 542 | int 543 | getoperand(int *ip, int flag, int last_char) 544 | { 545 | unsigned int tmp; 546 | char c; 547 | int code; 548 | int mode; 549 | int pos; 550 | int end; 551 | 552 | /* init */ 553 | auto_inc = 0; 554 | auto_tag = 0; 555 | 556 | /* skip spaces */ 557 | while (isspace((int)prlnbuf[*ip])) 558 | (*ip)++; 559 | 560 | /* check addressing mode */ 561 | switch (prlnbuf[*ip]) { 562 | case '\0': 563 | case ';': 564 | /* no operand */ 565 | error("Operand missing!"); 566 | return (0); 567 | 568 | case 'A': 569 | case 'a': 570 | /* accumulator */ 571 | c = prlnbuf[(*ip)+1]; 572 | if (isspace((int)c) || c == '\0' || c == ';' || c == ',') { 573 | mode = ACC; 574 | (*ip)++; 575 | break; 576 | } 577 | 578 | default: 579 | /* other */ 580 | switch(prlnbuf[*ip]) { 581 | case '#': 582 | /* immediate */ 583 | mode = IMM; 584 | (*ip)++; 585 | break; 586 | 587 | case '<': 588 | /* zero page */ 589 | mode = ZP | ZP_X | ZP_Y; 590 | (*ip)++; 591 | break; 592 | 593 | case '[': 594 | /* indirect */ 595 | mode = ABS_IND | ABS_IND_X | ZP_IND | ZP_IND_X | ZP_IND_Y; 596 | (*ip)++; 597 | break; 598 | 599 | default: 600 | /* absolute */ 601 | mode = ABS | ABS_X | ABS_Y; 602 | break; 603 | } 604 | 605 | /* get value */ 606 | if (!evaluate(ip, 0)) 607 | return (0); 608 | 609 | /* check addressing mode */ 610 | code = 0; 611 | end = 0; 612 | pos = 0; 613 | 614 | while (!end) { 615 | c = prlnbuf[*ip]; 616 | if (c == ';' || c == '\0') 617 | break; 618 | switch (toupper(c)) { 619 | case ',': /* , = 5 */ 620 | if (!pos) 621 | pos = *ip; 622 | else { 623 | end = 1; 624 | break; 625 | } 626 | code++; 627 | case '+': /* + = 4 */ 628 | code++; 629 | case ']': /* ] = 3 */ 630 | code++; 631 | if (prlnbuf[*ip + 1] == '.') { 632 | end = 1; 633 | break; 634 | } 635 | case 'X': /* X = 2 */ 636 | code++; 637 | case 'Y': /* Y = 1 */ 638 | code++; 639 | code <<= 4; 640 | case ' ': 641 | case '\t': 642 | (*ip)++; 643 | break; 644 | default: 645 | code = 0xFFFFFF; 646 | end = 1; 647 | break; 648 | } 649 | } 650 | 651 | /* absolute, zp, or immediate */ 652 | if (code == 0x000000) 653 | mode &= (ABS | ZP | IMM); 654 | 655 | /* indirect */ 656 | else if (code == 0x000030) 657 | mode &= (ZP_IND | ABS_IND); // ] 658 | 659 | /* indexed modes */ 660 | else if (code == 0x000510) 661 | mode &= (ABS_Y | ZP_Y); // ,Y 662 | else if (code == 0x000520) 663 | mode &= (ABS_X | ZP_X); // ,X 664 | else if (code == 0x005230) 665 | mode &= (ZP_IND_X | ABS_IND_X); // ,X] 666 | else if (code == 0x003510) 667 | mode &= (ZP_IND_Y); // ],Y 668 | else if (code == 0x000001) { 669 | mode &= (ZP_IND_Y); // ].tag 670 | (*ip) += 2; 671 | 672 | /* get tag */ 673 | tmp = value; 674 | 675 | if (!evaluate(ip, 0)) 676 | return (0); 677 | 678 | /* ok */ 679 | auto_tag = 1; 680 | auto_tag_value = value; 681 | value = tmp; 682 | } 683 | /* indexed modes with post-increment */ 684 | else if (code == 0x051440) { 685 | mode &= (ABS_Y | ZP_Y); // ,Y++ 686 | auto_inc = 0xC8; 687 | } 688 | else if (code == 0x052440) { 689 | mode &= (ABS_X | ZP_X); // ,X++ 690 | auto_inc = 0xE8; 691 | } 692 | else if (code == 0x351440) { 693 | mode &= (ZP_IND_Y); // ],Y++ 694 | auto_inc = 0xC8; 695 | } 696 | 697 | /* absolute, zp, or immediate (or error) */ 698 | else { 699 | mode &= (ABS | ZP | IMM); 700 | if (pos) 701 | *ip = pos; 702 | } 703 | 704 | /* check value on last pass */ 705 | if (pass == LAST_PASS) { 706 | /* zp modes */ 707 | if (mode & (ZP | ZP_X | ZP_Y | ZP_IND | ZP_IND_X | ZP_IND_Y) & flag) { 708 | /* extension stuff */ 709 | if (opext && !auto_inc) { 710 | if (mode & (ZP_IND | ZP_IND_X | ZP_IND_Y)) 711 | error("Instruction extension not supported in indirect modes!"); 712 | if (opext == 'H') 713 | value++; 714 | } 715 | /* check address validity */ 716 | if ((value & 0xFFFFFF00) && ((value & 0xFFFFFF00) != machine->ram_base)) 717 | error("Incorrect zero page address!"); 718 | } 719 | 720 | /* immediate mode */ 721 | else if (mode & (IMM) & flag) { 722 | /* extension stuff */ 723 | if (opext == 'L') 724 | value = (value & 0xFF); 725 | else if (opext == 'H') 726 | value = (value & 0xFF00) >> 8; 727 | else { 728 | /* check value validity */ 729 | if ((value > 0xFF) && (value < 0xFFFFFF00)) 730 | error("Incorrect immediate value!"); 731 | } 732 | } 733 | 734 | /* absolute modes */ 735 | else if (mode & (ABS | ABS_X | ABS_Y | ABS_IND | ABS_IND_X) & flag) { 736 | /* extension stuff */ 737 | if (opext && !auto_inc) { 738 | if (mode & (ABS_IND | ABS_IND_X)) 739 | error("Instruction extension not supported in indirect modes!"); 740 | if (opext == 'H') 741 | value++; 742 | } 743 | /* check address validity */ 744 | if (value & 0xFFFF0000) 745 | error("Incorrect absolute address!"); 746 | } 747 | } 748 | break; 749 | } 750 | 751 | /* compare addressing mode */ 752 | mode &= flag; 753 | if (!mode) { 754 | error("Incorrect addressing mode!"); 755 | return (0); 756 | } 757 | 758 | /* skip spaces */ 759 | while (isspace((int)prlnbuf[*ip])) 760 | (*ip)++; 761 | 762 | /* get last char */ 763 | c = prlnbuf[*ip]; 764 | 765 | /* check if it's what the user asked for */ 766 | switch (last_char) { 767 | case ';': 768 | /* last operand */ 769 | if (c != ';' && c != '\0') { 770 | error("Syntax error!"); 771 | return (0); 772 | } 773 | (*ip)++; 774 | break; 775 | 776 | case ',': 777 | /* need more operands */ 778 | if (c != ',') { 779 | error("Operand missing!"); 780 | return (0); 781 | } 782 | (*ip)++; 783 | break; 784 | } 785 | 786 | /* ok */ 787 | return (mode); 788 | } 789 | 790 | 791 | /* ---- 792 | * getstring() 793 | * ---- 794 | * get a string from prlnbuf 795 | */ 796 | 797 | int 798 | getstring(int *ip, char *buffer, int size) 799 | { 800 | char c; 801 | int i; 802 | 803 | /* skip spaces */ 804 | while (isspace((int)prlnbuf[*ip])) 805 | (*ip)++; 806 | 807 | if (prlnbuf[*ip] == '\"') { 808 | /* string value */ 809 | (*ip)++; 810 | /* get string */ 811 | i = 0; 812 | for (;;) { 813 | c = prlnbuf[(*ip)++]; 814 | if (c == '\"' || !c) 815 | break; 816 | if (i >= size) { 817 | error("String too long!"); 818 | return (0); 819 | } 820 | buffer[i++] = c; 821 | } 822 | /* end the string */ 823 | buffer[i] = '\0'; 824 | } 825 | else { 826 | /* string constant */ 827 | 828 | /* get constant name */ 829 | i = 1; 830 | for (;;) { 831 | c = prlnbuf[(*ip)++]; 832 | if (isspace(c) || !c) 833 | break; 834 | if (i >= SBOLSZ) { 835 | error("String constant name too long!"); 836 | return (0); 837 | } 838 | symbol[i++] = c; 839 | } 840 | symbol[0] = i - 1; 841 | symbol[i] = '\0'; 842 | if (strconstget(buffer, size)) 843 | return 0; 844 | } 845 | 846 | /* skip spaces */ 847 | while (isspace((int)prlnbuf[*ip])) 848 | (*ip)++; 849 | 850 | /* ok */ 851 | return (1); 852 | } 853 | 854 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nesasm CE v3.6 - a 6502 assembler with specific NES support 2 | 3 | Just another modification of nesasm. Based on modification by Tim Hentenaar which is based on modification by Bob Rost which is based on modification of nesasm 2.51 from MagicKit which is based on 6502 assembler by J. H. Van Ornum. 4 | 5 | ## What's new in this modification? 6 | 7 | * Support for much longer filenames and labels 8 | * Automatic generation of symbol files for FCEUX debugger 9 | * NES 2.0 support with very large files support, mappers up to 4095, submappers, etc. 10 | * Predefined NES specific constants: PPU/APU registers 11 | * GNU/POSIX style command line options 12 | * It's possible to define all output filenames now 13 | * Option to assign a value to a symbol from command line 14 | * Code cleanup: all warnings are fixed, PCE code leftovers removed 15 | 16 | ## Usage 17 | 18 | Usage: nesasm [OPTION...] 19 | 20 | -C, --sequ== Assign a string value to a symbol 21 | -D, --equ== Assign an integer value to a symbol 22 | -f, --symbols[=] Create FCEUX symbol files 23 | -F, --symbols-offset= Bank offset for FCEUX symbol files 24 | -i, --listing Force listing 25 | -l, --listing-level=# Listing file output level (0-3) 26 | -L, --listing-file= Name of the listing file 27 | -m, --macro-expansion Force macro expansion in listing 28 | -o, --output= Name of the output file, use '-' for stdout 29 | -r, --raw Prevent adding a ROM header 30 | -s, --segment-usage Show (more) segment usage 31 | -W, --warnings Show overflow warnings 32 | -?, --help give this help list 33 | --usage give a short usage message 34 | -V, --version print program version 35 | 36 | The assembler accepts only one input file 'infile' that will be assembled into ROM file (.NES extension) directly useable by an emulator. 37 | 38 | A listing file can also be generated (.LST extension) if the LIST directive is encountered in the input file. 39 | 40 | Here's a description of the different options: 41 | 42 | 43 | Option Description 44 | ------ ----------- 45 | 46 | -o Set output filename. 47 | The default is input filename + ".nes" extension. 48 | Use '-' for stdout output. 49 | 50 | -D = Assign an integer value to a symbol. 51 | Example: -D delay=10 52 | It will be equal to: delay .equ 10 53 | at the beginning of your code, also you can use '$' and '%' 54 | prefixes for hexadecimal and binary values. 55 | 56 | -C = Assign a string value to a symbol. 57 | Example: -C image_file=image.bin 58 | It will be equal to: image_file .sequ "image.bin" 59 | at the beginning of your code. 60 | 61 | -f [prefix] Enable generation of symbol files for FCEUX debugger, 62 | optionally you can specify filenames prefix. 63 | 64 | -F [offset] Set bank offset for FCEUX symbol files. 65 | 66 | -L Set listing filename. 67 | The default is output filename + ".lst" extension. 68 | 69 | -l # Control output of the listing file: 70 | 71 | 0 - disable completely the listing file even if the 72 | LIST directive is used in the input file 73 | 1 - minimun level; code produced by DB, DW and DEFCHR 74 | will not be dumped 75 | 2 - normal level; only code produced by DEFCHR will not 76 | be dumped 77 | 3 - maximun level; all the code is dumped in the 78 | listing file 79 | 80 | The default level is level 2. 81 | 82 | -s Show segment usage. If one of those options is specified 83 | the assembler will display information on the ROM bank 84 | usage. Use '-s' to show basic information and '-ss' to 85 | show more detailed information. 86 | 87 | -i Force listing file writing, even if the 88 | LIST directive is not seen in the input file. 89 | 90 | -m Force macros expansion in the listing file, even if the 91 | MLIST directive is not seen in the input file. 92 | 93 | -r Control the header generation. By default the assembler 94 | always adds an header to the ROM file; unless '-raw' is 95 | specified, in this case no ROM header is generated. 96 | 97 | -W Show warnings on bank overflow when using .inc* directives. 98 | 99 | 100 | ### Include path 101 | 102 | By default the assembler looks in the current directory when loading an include file, but when it doesn't find the file it then uses the environment variable 'NES_INCLUDE' to get a list of include paths. 103 | 104 | 105 | ### Symbols 106 | 107 | Two types of symbol are supported, global symbols and local symbols. Local symbols are preceded by a dot '.' and are valid only between two global symbols. A symbol can be followed by a colon ':' but this is not necessary. 108 | 109 | 110 | ### Expressions 111 | 112 | The assembler supports very complex expressions. You can use as many level of parenthesis as you want and spaces between operators and numbers are possible. 113 | 114 | Numbers can be written in three bases : hexadecimal ($7F), binary (%0101) and decimal (48). Character values are also supported ('A'). 115 | 116 | All the usual operators are present : 117 | 118 | +, -, *, /, %, ^, &, |, ~, <<, >> 119 | 120 | As well as the comparison operators : 121 | 122 | =, !=, !, <, >, <=, >= 123 | 124 | For the priority, the same rules as C apply. 125 | 126 | You can also use predefined or user-defined functions in an expression. 127 | 128 | 129 | ### Predefined functions 130 | 131 | * HIGH() - Returns the high byte of a value. 132 | * LOW() - Returns the low byte. 133 | * BANK() - Returns the bank index of a symbol. If no symbol, or more than one, are given, the function will return an error. 134 | * PAGE() - Returns the page index of a label. See above for errors. 135 | * SIZEOF() - Returns the size of a data element. 136 | 137 | 138 | ### Predefined constants 139 | 140 | There are predefines NES register addresses: 141 | 142 | * PPUCTRL and PPU_CTRL - $2000 143 | * PPUMASK and PPU_MASK - $2001 144 | * PPUSTATUS and PPU_STATUS - $2002 145 | * OAMADDR and OAM_ADDR - $2003 146 | * OAMDATA and OAM_DATA - $2004 147 | * PPUSCROLL and PPU_SCROLL - $2005 148 | * PPUADDR and PPU_ADDR - $2006 149 | * PPUDATA and PPU_DATA - $2007 150 | * OAMDMA and OAM_DMA - $4014 151 | * SQ1VOL and SQ1_VOL - $4000 152 | * SQ1SWEEP and SQ1_SWEEP - $4001 153 | * SQ1LO and SQ1_LO - $4002 154 | * SQ1HI and SQ1_HI - $4003 155 | * SQ2VOL and SQ2_VOL - $4004 156 | * SQ2SWEEP and SQ2_SWEEP - $4005 157 | * SQ2LO and SQ2_LO - $4006 158 | * SQ2HI and SQ2_HI - $4007 159 | * TRILINEAR and TRI_LINEAR - $4008 160 | * TRILO and TRI_LO - $400A 161 | * TRIHI and TRI_HI - $400B 162 | * NOISEVOL and NOISE_VOL - $400C 163 | * NOISELO and NOISE_LO - $400E 164 | * NOISEHI and NOISE_HI - $400F 165 | * DMCFREQ and DMC_FREQ - $4010 166 | * DMCRAW and DMC_RAW - $4011 167 | * DMCSTART and DMC_START - $4012 168 | * DMCLEN and DMC_LEN - $4013 169 | * APUSTATUS and APU_STATUS - $4015 170 | * JOY1 - $4016 171 | * JOY2 and JOY2_FRAME - $4017 172 | 173 | 174 | ### User-defined functions 175 | 176 | User-defined functions are declared with the .FUNC directive, for example: 177 | 178 | SCR_ADDR .func (\1) + ((\2) << 5) 179 | 180 | Up to nine arguments, \1 to \9, can be used. 181 | 182 | To call a function simply enclose arguments within parenthesis and separate them with a comma: 183 | 184 | stw #SCR_ADDR(10,4)+$2000,<$20 185 | 186 | User-defined functions can be very useful, one often needs to use the same calculation again and again in expressions. Defining a function will save you a lot of work, and reduce typo errors. :) Note that function calls can be nested, you can call one function from another without any problem, however, recursive calls will produce an error. 187 | 188 | 189 | ### Macros 190 | 191 | While functions are very useful to replace common expressions by just a function call, macros are used to replace common groups of instructions by a single line of code. 192 | 193 | You start a macro definition with: 194 | 195 | label .macro 196 | 197 | Or you can also place the label after the '.macro' keyword, like this: 198 | 199 | .macro label 200 | 201 | After follow the body of the macro, which is terminated by the '.endm' directive. 202 | 203 | As an example let's define a 'neg' macro to negate the accumulator. 204 | 205 | neg .macro 206 | eor #$FF 207 | inc A 208 | .endm 209 | 210 | Macros can also have parameters. In the macro body, you refer to a parameter by using the backslash character ('\') followed by a digit. Nine parameters can be used, \1 to \9. 211 | 212 | Here's another example: 213 | 214 | add .macro ; add a value to register A 215 | clc ; (handle carry flag) 216 | adc \1+1 217 | .endm 218 | 219 | Other 'special' parameters can be used, here's a list of all the possible parameter you can use inside a macro: 220 | 221 | Parameter Description 222 | --------- ----------- 223 | \1 - \9 Input parameter - up to nine can be used in a macro call 224 | 225 | \# Number of input parameters 226 | 227 | \?1 - \?9 Returns 'type' of input parameter: 228 | ARG_NONE (= 0) = No argument 229 | ARG_REG (= 1) = register -> A, X, Y 230 | ARG_IMMEDIATE (= 2) = Immediate data type -> #xx 231 | ARG_ABSOLUTE (= 3) = Abosulte addressing -> label, $xxxx 232 | ARG_INDIRECT (= 4) = Indirect addressing -> [label] 233 | ARG_STRING (= 5) = String argument -> "..." 234 | ARG_LABEL (= 6) = Label argument -> label 235 | 236 | \@ Special parameter that returns a different number for 237 | each macro; can be used to define local symbols inside 238 | macros: 239 | 240 | abs .macro 241 | lda \1 242 | bpl .x\@ 243 | eor #$FF 244 | inc A 245 | sta \1 246 | .x\@: 247 | .endm 248 | 249 | 250 | ### Directives 251 | 252 | LIST - Enable the listing file generation. You can later stop 253 | temporarily the output with the NOLIST directive and 254 | restart it again with LIST. 255 | 256 | NOLIST - Stop the listing output. 257 | 258 | MLIST - Allow macro expansion in the listing file. 259 | 260 | NOMLIST - Stop expanding macros in the listing file. This directive 261 | won't have any effect if you use the '-m' command line 262 | option. 263 | 264 | EQU - Assign an integer value to a symbol. The character '=' has 265 | the same function too. 266 | 267 | SEQU - Assign a string value to a symbol. 268 | 269 | BANK - Select a 8KB ROM bank (0-127) and reset the location 270 | counter to the latest known position in this bank. 271 | 272 | ORG - Set the location of the program counter. The thirteen 273 | lower bits of the address inform the assembler about 274 | the offset in the ROM bank and the third upper bits 275 | represent the page index. 276 | 277 | DB - Store one or more data bytes at the current location. 278 | 279 | STR - Stores a string, the first byte is the length of the string, 280 | followed by the string content, 281 | The effect is equivalent to . DB is preceded with a length, 282 | here's a small example: 283 | ;use DB specified a length + string: 284 | DB 12,"Hello World!" 285 | ;can be replaced with STR: 286 | STR "Hello World!" 287 | 288 | DW - Store data words. 289 | 290 | BYTE - Same as DB. 291 | 292 | WORD - Same as DW. 293 | 294 | DS - Reserve space at the current location. This space will 295 | be filled with zeroes if this directive is used in the 296 | CODE or DATA group. 297 | 298 | RSSET - Set the internal counter of the RS directive to 299 | a specified value. 300 | 301 | RS - Assign a value to a symbol; a bit like EQU but here 302 | the value assigned is taken from an internal counter, 303 | and after the assignation this counter is increased 304 | by the amount specified in the RS directive. 305 | This is a very handy way of defining structure member 306 | offsets, here's a small example: 307 | 308 | ; C: 309 | ; -- 310 | ; struct { 311 | ; short p_x; 312 | ; short p_y; 313 | ; byte p_color; 314 | ; } pixel; 315 | ; 316 | ; ASM: 317 | ; ---- 318 | 319 | .rsset $0 ; set the initial value of RS counter 320 | P_X .rs 2 321 | P_Y .rs 2 322 | P_COLOR .rs 1 323 | 324 | You can later use these symbols as offsets in a 'pixel' 325 | struct: 326 | 327 | ldy #P_COLOR 328 | lda [pixel_ptr],Y 329 | 330 | MACRO - Start a macro definition. 331 | 332 | ENDM - End a macro definition. 333 | 334 | INCBIN - Include a binary file at the current location. If the file 335 | is bigger than a ROM bank, as many successive banks as 336 | necessary will be used. 337 | 338 | INCLUDE - Include a source file at the current location. 339 | Up to 7 levels are possible. 340 | DEFCHR - Define a character tile (8x8 pixels). The directive takes 341 | 8 arguments (stored as 32-bit values of 8 nybbles each), 342 | one argument for each row of pixel data. This directive 343 | takes also care to reorganize the pixel data to the NES 344 | required bit format. Note that only color indexes 0 to 3 345 | can be used, as the NES tiles are only 4-color. An error 346 | will be generated if you try to use more colors. 347 | 348 | zero: .defchr $00111110,\ 349 | $01000011,\ 350 | $01000101,\ 351 | $01001001,\ 352 | $01010001,\ 353 | $01100001,\ 354 | $00111110,\ 355 | $00000000 356 | 357 | ZP - Select the Zero-Page section ($0000-$00FF). 358 | 359 | BSS - Select the RAM section ($0200-$07FF). 360 | 361 | CODE - Select the program code section. 362 | 363 | DATA - Select the program data section. 364 | 365 | Note: In ZP and BSS sections you can only allocate storage, 366 | ---- you can *not* store initial values. 367 | 368 | IF - Conditional assembly directive. This directive will evaluate 369 | the supplied expression and then turn conditional assembly 370 | on or off depending on the result. If the result is null 371 | conditional assembly is turned off, and on if the result is 372 | non null. 373 | IFDEF 374 | IFNDEF - These directives allow conditional assembly depending on 375 | whether a label is defined or not. 376 | 377 | ELSE - Toggle conditional assembly on to off, or vice verca. 378 | 379 | ENDIF - Terminate the current level of conditional assembly. 380 | Report an error if the number of IF's and ENDIF's doesn't 381 | match. 382 | 383 | FAIL - When the assembler encounters this directive, it aborts 384 | the compilation. Can be used within a macro for argument 385 | error detection. 386 | 387 | INESPRG - Specifies the number of 16k PRG banks or just PRG size if it > $EFF. 388 | 389 | INESCHR - Specifies the number of 8k CHR banks or just CHR size if it > $EFF. 390 | 391 | INESMAP - Specifies the NES mapper used, up to 4095. 392 | 393 | INESSUBMAP - Specifies the NES submapper used, up to 15. 394 | 395 | INESMIR - Specifies VRAM mirroring of the banks. 396 | 0: Horizontal or mapper-controlled, 1: Vertical, 2: Hard-wired four-screen 397 | 398 | INESPRGRAM - Specifies the size of PRG RAM. 399 | 400 | INESPRGNVRAM - Specifies the size of PRG NVRAM (non-volatile). 401 | 402 | INESCHRRAM - Specifies the size of CHR RAM. 403 | 404 | INESCHRNVRAM - Specifies the size of CHR NVRAM (non-volatile). 405 | 406 | INESBAT - Specifies "battery" and other non-volatile memory 407 | 0: Not present, 1: Present 408 | 409 | INESTIM - Specifies CPU/PPU timing 410 | 0: RP2C02 ("NTSC NES"), 1: RP2C07 ("Licensed PAL NES"), 2: Multiple-region, 3: UMC 6527P ("Dendy") 411 | 412 | 413 | ## How to build 414 | 415 | ### Linux 416 | * Just run `make all` from `sources` directory 417 | 418 | ### Windows 419 | * Install [MSYS2](https://www.msys2.org/) 420 | * Install *base-devel*, *gcc*, *git* and *libargp-devel* packages using command: `pacman -S base-devel gcc git libargp-devel` 421 | * Run `make all` from `sources` directory 422 | 423 | ### MacOS 424 | * Install *argp-standalone* package using command: `brew install argp-standalone` 425 | * Run `make all` from `sources` directory 426 | 427 | 428 | ## Contacts 429 | 430 | https://clusterrr.com 431 | 432 | clusterrr@clusterrr.com 433 | 434 | 435 | [![C/C++ CI](https://github.com/ClusterM/nesasm/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/ClusterM/nesasm/actions/workflows/c-cpp.yml) 436 | 437 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MagicKit assembler 3 | * ---- 4 | * This program was originaly a 6502 assembler written by J. H. Van Ornum, 5 | * it has been modified and enhanced to support the PC Engine and NES consoles. 6 | * 7 | * This program is freeware. You are free to distribute, use and modifiy it 8 | * as you wish. 9 | * 10 | * Enjoy! 11 | * ---- 12 | * Original 6502 version by: 13 | * J. H. Van Ornum 14 | * 15 | * PC-Engine version by: 16 | * David Michel 17 | * Dave Shadoff 18 | * 19 | * NES version by: 20 | * Charles Doty 21 | * 22 | * Modifications by: 23 | * Bob Rost 24 | * Alexey 'Cluster' Avdyukhin 25 | * 26 | */ 27 | 28 | #define VERSION "v3.6" 29 | #define DESCRIPTION "a 6502 assembler with specific NES support" 30 | #define GITHUB_URL "https://github.com/ClusterM/nesasm/" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "defs.h" 39 | #include "externs.h" 40 | #include "protos.h" 41 | #include "vars.h" 42 | #include "inst.h" 43 | #include "commit.h" 44 | 45 | /* variables */ 46 | unsigned char ipl_buffer[4096]; 47 | char *in_fname; /* file names, input */ 48 | char bin_fname[1024]; /* binary */ 49 | char lst_fname[1024]; /* listing */ 50 | char sym_fname[1024]; /* symbols */ 51 | FILE *in_fp; /* file pointers, input */ 52 | FILE *lst_fp; /* listing */ 53 | char section_name[4][8] = { " ZP", " BSS", "CODE", "DATA" }; 54 | int dump_seg; 55 | int zero_fill; 56 | int out_stdout; 57 | int header_opt; 58 | int sym_opt; /* export symbols for FCEUX flag */ 59 | char sym_bank_offset_opt; /* bank offset for FCEUX symbol files */ 60 | int list_opt; /* listing main flag */ 61 | int mlist_opt; /* macro listing main flag */ 62 | int warnings_opt; /* warnings main flag */ 63 | int xlist; /* listing file main flag */ 64 | int list_level; /* output level */ 65 | int asm_opt[8]; /* assembler options */ 66 | 67 | /* Program description. */ 68 | static char program_desc[256]; 69 | const char *argp_program_version = program_desc; 70 | const char *argp_program_bug_address = GITHUB_URL; 71 | 72 | /* Program arguments description. */ 73 | static char argp_program_args_desc[] = ""; 74 | 75 | /* The options we understand. */ 76 | static struct argp_option options[] = { 77 | { "equ", 'D', "=", 0, "Assign an integer value to a symbol" }, 78 | { "sequ", 'C', "=", 0, "Assign a string value to a symbol" }, 79 | { "segment-usage", 's', 0, 0, "Show (more) segment usage" }, 80 | { 0, 'S', 0, OPTION_HIDDEN, "" }, 81 | { "listing", 'i', 0, 0, "Force listing" }, 82 | { "macro-expansion", 'm', 0, 0, "Force macro expansion in listing" }, 83 | { "raw", 'r', 0, 0, "Prevent adding a ROM header" }, 84 | { "symbols", 'f', "", OPTION_ARG_OPTIONAL, "Create FCEUX symbol files" }, 85 | { "symbols-offset", 'F', "", 0, "Bank offset for FCEUX symbol files" }, 86 | { "listing-level", 'l', "#", 0, "Listing file output level (0-3)" }, 87 | { "listing-file", 'L', "", 0, "Name of the listing file" }, 88 | { "warnings", 'W', 0, 0, "Show overflow warnings" }, 89 | { "output", 'o', "", 0, "Name of the output file, use '-' for stdout" }, 90 | { "zero-fill", 'z', 0, 0, "Fill unused space in ROM with zeroes" }, 91 | { 0 } 92 | }; 93 | 94 | /* Parse a --equ option. */ 95 | static int 96 | parse_equ_opt (char *equ) 97 | { 98 | /* Split by '=' character */ 99 | char* value_str = strchr(equ, '='); 100 | if (!value_str) 101 | { 102 | printf("Invalid assigment format: %s\n", equ); 103 | return 1; 104 | } 105 | *value_str = 0; 106 | value_str++; 107 | /* Determine base */ 108 | int base = 10; 109 | if ((strlen(value_str) > 0) && (value_str[0] == '%')) 110 | { 111 | base = 2; 112 | value_str++; 113 | } 114 | if ((strlen(value_str) >= 0) && (value_str[0] == '$')) 115 | { 116 | base = 16; 117 | value_str++; 118 | } 119 | /* Check for empty value */ 120 | if (strlen(value_str) == 0) 121 | { 122 | printf("Value for %s is empty\n", equ); 123 | return 1; 124 | } 125 | /* Parse and set */ 126 | int value = strtol(value_str, 0, base); 127 | constset(equ, value); 128 | return 0; 129 | } 130 | 131 | /* Parse a --sequ option. */ 132 | static int 133 | parse_sequ_opt (char *equ) 134 | { 135 | /* Split by '=' character */ 136 | char* value_str = strchr(equ, '='); 137 | if (!value_str) 138 | { 139 | printf("Invalid assigment format: %s\n", equ); 140 | return 1; 141 | } 142 | *value_str = 0; 143 | value_str++; 144 | strconstset(equ, value_str); 145 | return 0; 146 | } 147 | 148 | /* Parse a single option. */ 149 | static error_t 150 | parse_opt (int key, char *arg, struct argp_state *state) 151 | { 152 | switch (key) 153 | { 154 | case 'D': 155 | if (parse_equ_opt(arg)) 156 | argp_usage(state); 157 | break; 158 | case 'C': 159 | if (parse_sequ_opt(arg)) 160 | argp_usage(state); 161 | break; 162 | case 's': 163 | dump_seg++; 164 | if (dump_seg > 2) dump_seg = 2; 165 | break; 166 | case 'S': 167 | dump_seg = 2; 168 | break; 169 | case 'z': 170 | zero_fill=1; 171 | case 'i': 172 | list_opt = 1; 173 | break; 174 | case 'm': 175 | mlist_opt = 1; 176 | break; 177 | case 'W': 178 | warnings_opt = 1; 179 | break; 180 | case 'r': 181 | header_opt = 0; 182 | break; 183 | case 'o': 184 | strncpy(bin_fname, arg, sizeof(bin_fname)-1); 185 | break; 186 | case 'L': 187 | strncpy(lst_fname, arg, sizeof(lst_fname)-1); 188 | break; 189 | case 'f': 190 | sym_opt = 1; 191 | if (arg) strncpy(sym_fname, arg, sizeof(sym_fname)-1); 192 | break; 193 | case 'F': 194 | sym_bank_offset_opt = atol(arg); 195 | break; 196 | case 'l': 197 | list_level = atol(arg); 198 | /* check range */ 199 | if (list_level < 0 || list_level > 3) 200 | list_level = 2; 201 | break; 202 | case ARGP_KEY_ARG: 203 | if (state->arg_num >= 1) 204 | /* Too many arguments. */ 205 | argp_usage(state); 206 | in_fname = arg; 207 | break; 208 | case ARGP_KEY_END: 209 | if (state->arg_num < 1) 210 | /* Not enough arguments. */ 211 | argp_usage(state); 212 | break; 213 | default: 214 | return ARGP_ERR_UNKNOWN; 215 | } 216 | return 0; 217 | } 218 | 219 | /* Our argp parser. */ 220 | static struct argp argp = { options, parse_opt, argp_program_args_desc, NULL }; 221 | 222 | /* ---- 223 | * main() 224 | * ---- 225 | */ 226 | 227 | int 228 | main(int argc, char **argv) 229 | { 230 | FILE *fp; 231 | char *p; 232 | int i, j; 233 | int ram_bank; 234 | 235 | sprintf(program_desc, "nesasm %s - %s\ncommit: %s @ %s", 236 | VERSION, DESCRIPTION, COMMIT, GITHUB_URL); 237 | 238 | if (argc == 1) 239 | fprintf(stderr, "%s\n", program_desc); 240 | 241 | /* init assembler options */ 242 | machine = &nes; 243 | list_level = 2; 244 | header_opt = 1; 245 | list_opt = 0; 246 | mlist_opt = 0; 247 | warnings_opt = 0; 248 | sym_opt = 0; 249 | sym_bank_offset_opt = 0; 250 | zero_fill = 0; 251 | out_stdout = 0; 252 | 253 | bin_fname[0] = 0; 254 | lst_fname[0] = 0; 255 | sym_fname[0] = 0; 256 | 257 | /* clear symbol hash tables */ 258 | for (i = 0; i < 256; i++) { 259 | hash_tbl[i] = NULL; 260 | macro_tbl[i] = NULL; 261 | func_tbl[i] = NULL; 262 | inst_tbl[i] = NULL; 263 | } 264 | 265 | /* parse command line */ 266 | argp_parse(&argp, argc, argv, 0, 0, 0); 267 | 268 | if (zero_fill==1) dump_seg = 0; // disable segment info as zero filling makes it inaccurate 269 | 270 | /* search file extension */ 271 | char basename[strlen(in_fname)+1]; 272 | strcpy(basename, in_fname); 273 | if ((p = strrchr(basename, '.')) != NULL) { 274 | if (!strchr(p, PATH_SEPARATOR)) 275 | *p = '\0'; 276 | else 277 | p = NULL; 278 | } 279 | 280 | /* auto-add file extensions */ 281 | if (!bin_fname[0]) 282 | { 283 | strcpy(bin_fname, basename); 284 | strcat(bin_fname, machine->rom_ext); 285 | } 286 | /* enable output to stroud if need */ 287 | else if (strcmp(bin_fname, "-") == 0) 288 | { 289 | out_stdout = 1; 290 | } 291 | char bin_basename[strlen(bin_fname)+1]; 292 | strcpy(bin_basename, bin_fname); 293 | if ((p = strrchr(bin_basename, '.')) != NULL) { 294 | if (!strchr(p, PATH_SEPARATOR)) 295 | *p = '\0'; 296 | else 297 | p = NULL; 298 | } 299 | if (!lst_fname[0]) 300 | { 301 | strcpy(lst_fname, bin_basename); 302 | strcat(lst_fname, ".lst"); 303 | } 304 | if (!sym_fname[0]) 305 | { 306 | strcpy(sym_fname, bin_fname); 307 | } 308 | 309 | /* init include path */ 310 | init_path(); 311 | 312 | /* init crc functions */ 313 | crc_init(); 314 | 315 | /* open the input file */ 316 | if (open_input(in_fname)) { 317 | fprintf(stderr, "Can not open input file '%s'!\n", in_fname); 318 | exit(1); 319 | } 320 | 321 | /* clear the ROM array */ 322 | memset(rom, zero_fill ? 0 : 0xff, sizeof(rom)); 323 | memset(map, zero_fill ? 0 : 0xff, sizeof(map)); 324 | 325 | /* fill the instruction hash table */ 326 | addinst(base_inst); 327 | addinst(base_pseudo); 328 | 329 | /* add machine specific instructions and pseudos */ 330 | addinst(machine->inst); 331 | addinst(machine->pseudo_inst); 332 | 333 | /* predefined symbols */ 334 | lablset("_bss_end", 0); 335 | lablset("_bank_base", 0); 336 | lablset("_nb_bank", 1); 337 | lablset("_call_bank", 0); 338 | 339 | /* NES specific predefined symbols */ 340 | constset("PPUCTRL", 0x2000); 341 | constset("PPU_CTRL", 0x2000); 342 | constset("PPUMASK", 0x2001); 343 | constset("PPU_MASK", 0x2001); 344 | constset("PPUSTAT", 0x2002); 345 | constset("PPUSTATUS", 0x2002); 346 | constset("PPU_STATUS", 0x2002); 347 | constset("OAMADDR", 0x2003); 348 | constset("OAM_ADDR", 0x2003); 349 | constset("PPU_OAM_ADDR", 0x2003); 350 | constset("OAMDATA", 0x2004); 351 | constset("OAM_DATA", 0x2004); 352 | constset("PPU_OAM_DATA", 0x2004); 353 | constset("PPUSCROLL", 0x2005); 354 | constset("PPU_SCROLL", 0x2005); 355 | constset("PPUADDR", 0x2006); 356 | constset("PPU_ADDR", 0x2006); 357 | constset("PPUDATA", 0x2007); 358 | constset("PPU_DATA", 0x2007); 359 | constset("SQ1VOL", 0x4000); 360 | constset("SQ1_VOL", 0x4000); 361 | constset("SQ1SWEEP", 0x4001); 362 | constset("SQ1_SWEEP", 0x4001); 363 | constset("SQ1LO", 0x4002); 364 | constset("SQ1_LO", 0x4002); 365 | constset("SQ1HI", 0x4003); 366 | constset("SQ1_HI", 0x4003); 367 | constset("SQ2VOL", 0x4004); 368 | constset("SQ2_VOL", 0x4004); 369 | constset("SQ2SWEEP", 0x4005); 370 | constset("SQ2_SWEEP", 0x4005); 371 | constset("SQ2LO", 0x4006); 372 | constset("SQ2_LO", 0x4006); 373 | constset("SQ2HI", 0x4007); 374 | constset("SQ2_HI", 0x4007); 375 | constset("TRILINEAR", 0x4008); 376 | constset("TRI_LINEAR", 0x4008); 377 | constset("TRILO", 0x400A); 378 | constset("TRI_LO", 0x400A); 379 | constset("TRIHI", 0x400B); 380 | constset("TRI_HI", 0x400B); 381 | constset("NOISEVOL", 0x400C); 382 | constset("NOISE_VOL", 0x400C); 383 | constset("NOISELO", 0x400E); 384 | constset("NOISE_LO", 0x400E); 385 | constset("NOISEHI", 0x400F); 386 | constset("NOISE_HI", 0x400F); 387 | constset("DMCFREQ", 0x4010); 388 | constset("DMC_FREQ", 0x4010); 389 | constset("DMCRAW", 0x4011); 390 | constset("DMC_RAW", 0x4011); 391 | constset("DMCSTART", 0x4012); 392 | constset("DMC_START", 0x4012); 393 | constset("DMCLEN", 0x4013); 394 | constset("DMC_LEN", 0x4013); 395 | constset("OAMDMA", 0x4014); 396 | constset("OAM_DMA", 0x4014); 397 | constset("PPU_OAM_DMA", 0x4014); 398 | constset("APUSTATUS", 0x4015); 399 | constset("APU_STATUS", 0x4015); 400 | constset("JOY1", 0x4016); 401 | constset("JOY2", 0x4017); 402 | constset("JOY2FRAME", 0x4017); 403 | constset("JOY2_FRAME", 0x4017); 404 | 405 | /* init global variables */ 406 | max_zp = 0x01; 407 | max_bss = 0x0201; 408 | max_bank = 0; 409 | rom_limit = MAX_BANKS * BANK_SIZE; 410 | bank_limit = MAX_BANKS * BANK_SIZE / 0x2000 - 1; 411 | bank_base = 0; 412 | errcnt = 0; 413 | 414 | /* assemble */ 415 | for (pass = FIRST_PASS; pass <= LAST_PASS; pass++) { 416 | infile_error = -1; 417 | page = 7; 418 | bank = 0; 419 | loccnt = 0; 420 | slnum = 0; 421 | mcounter = 0; 422 | mcntmax = 0; 423 | xlist = list_opt; 424 | glablptr = NULL; 425 | skip_lines = 0; 426 | rsbase = 0; 427 | proc_nb = 0; 428 | 429 | /* reset assembler options */ 430 | asm_opt[OPT_LIST] = list_opt; 431 | asm_opt[OPT_MACRO] = mlist_opt; 432 | asm_opt[OPT_WARNING] = warnings_opt; 433 | asm_opt[OPT_OPTIMIZE] = 0; 434 | 435 | /* reset bank arrays */ 436 | for (i = 0; i < 4; i++) { 437 | for (j = 0; j < MAX_BANKS; j++) { 438 | bank_loccnt[i][j] = 0; 439 | bank_glabl[i][j] = NULL; 440 | bank_page[i][j] = 0; 441 | } 442 | } 443 | 444 | /* reset sections */ 445 | ram_bank = machine->ram_bank; 446 | section = S_CODE; 447 | 448 | /* .zp */ 449 | section_bank[S_ZP] = ram_bank; 450 | bank_page[S_ZP][ram_bank] = machine->ram_page; 451 | bank_loccnt[S_ZP][ram_bank] = 0x0000; 452 | 453 | /* .bss */ 454 | section_bank[S_BSS] = ram_bank; 455 | bank_page[S_BSS][ram_bank] = machine->ram_page; 456 | bank_loccnt[S_BSS][ram_bank] = 0x0200; 457 | 458 | /* .code */ 459 | section_bank[S_CODE] = 0x00; 460 | bank_page[S_CODE][0x00] = 0x07; 461 | bank_loccnt[S_CODE][0x00] = 0x0000; 462 | 463 | /* .data */ 464 | section_bank[S_DATA] = 0x00; 465 | bank_page[S_DATA][0x00] = 0x07; 466 | bank_loccnt[S_DATA][0x00] = 0x0000; 467 | 468 | /* assemble */ 469 | while (readline() != -1) { 470 | assemble(); 471 | if (loccnt > 0x2000) { 472 | if (proc_ptr == NULL) 473 | fatal_error("Bank overflow, offset > $1FFF!"); 474 | else { 475 | char tmp[256]; 476 | 477 | sprintf(tmp, "Proc : '%s' is too large (code > 8KB)!", proc_ptr->name); 478 | fatal_error(tmp); 479 | } 480 | break; 481 | } 482 | if (stop_pass) 483 | break; 484 | } 485 | 486 | /* relocate procs */ 487 | if (pass == FIRST_PASS) 488 | proc_reloc(); 489 | 490 | /* abort pass on errors */ 491 | if (errcnt) { 492 | fprintf(stderr, "# %d error(s)\n", errcnt); 493 | break; 494 | } 495 | 496 | /* adjust bank base */ 497 | if (pass == FIRST_PASS) 498 | bank_base = 0; 499 | 500 | /* update predefined symbols */ 501 | if (pass == FIRST_PASS) { 502 | lablset("_bss_end", machine->ram_base + max_bss); 503 | lablset("_bank_base", bank_base); 504 | lablset("_nb_bank", max_bank + 1); 505 | } 506 | 507 | /* rewind input file */ 508 | rewind(in_fp); 509 | 510 | /* open the listing file */ 511 | if (pass == FIRST_PASS) { 512 | if (xlist && list_level) { 513 | if ((lst_fp = fopen(lst_fname, "w")) == NULL) { 514 | fprintf(stderr, "Can not open listing file '%s'!\n", lst_fname); 515 | exit(1); 516 | } 517 | fprintf(lst_fp, "#[1] %s\n", input_file[1].name); 518 | } 519 | } 520 | } 521 | 522 | /* rom */ 523 | if (errcnt == 0) { 524 | /* save */ 525 | /* open file */ 526 | if (out_stdout) fp = stdout; 527 | else if ((fp = fopen(bin_fname, "wb")) == NULL) { 528 | fprintf(stderr, "Can not open binary file '%s'!\n", bin_fname); 529 | exit(1); 530 | } 531 | 532 | /* write header */ 533 | if (header_opt) 534 | machine->write_header(fp, max_bank + 1); 535 | 536 | /* write rom */ 537 | fwrite(rom, 8192, (max_bank + 1), fp); 538 | fclose(fp); 539 | } 540 | 541 | /* close listing file */ 542 | if (xlist && list_level) 543 | fclose(lst_fp); 544 | 545 | /* close input file */ 546 | fclose(in_fp); 547 | 548 | if (errcnt) 549 | return(1); 550 | 551 | /* dump the bank table */ 552 | if (dump_seg) 553 | show_seg_usage(); 554 | 555 | if (sym_opt) 556 | stlist(sym_fname, sym_bank_offset_opt); 557 | 558 | /* ok */ 559 | return(0); 560 | } 561 | 562 | /* ---- 563 | * show_seg_usage() 564 | * ---- 565 | */ 566 | 567 | void 568 | show_seg_usage(void) 569 | { 570 | int i, j; 571 | int addr, start, stop, nb; 572 | int rom_used; 573 | int rom_free; 574 | int ram_base = machine->ram_base; 575 | 576 | fprintf(stderr, "segment usage:\n"); 577 | fprintf(stderr, "\n"); 578 | 579 | /* zp usage */ 580 | if (max_zp <= 1) 581 | fprintf(stderr, " ZP -\n"); 582 | else { 583 | start = ram_base; 584 | stop = ram_base + (max_zp - 1); 585 | fprintf(stderr, " ZP $%04X-$%04X [%4i]\n", start, stop, stop - start + 1); 586 | } 587 | 588 | /* bss usage */ 589 | if (max_bss <= 0x201) 590 | fprintf(stderr, " BSS -\n"); 591 | else { 592 | start = ram_base + 0x200; 593 | stop = ram_base + (max_bss - 1); 594 | fprintf(stderr, " BSS $%04X-$%04X [%4i]\n", start, stop, stop - start + 1); 595 | } 596 | 597 | /* bank usage */ 598 | rom_used = 0; 599 | rom_free = 0; 600 | 601 | if (max_bank) 602 | fprintf(stderr, "\t\t\t\t USED/FREE\n"); 603 | 604 | /* scan banks */ 605 | for (i = 0; i <= max_bank; i++) { 606 | start = 0; 607 | addr = 0; 608 | nb = 0; 609 | 610 | /* count used and free bytes */ 611 | for (j = 0; j < 8192; j++) 612 | if (map[i][j] != 0xFF) 613 | nb++; 614 | 615 | /* display bank infos */ 616 | if (nb) 617 | fprintf(stderr, "BANK% 4i %20s %4i/%4i\n", 618 | i, bank_name[i], nb, 8192 - nb); 619 | else { 620 | fprintf(stderr, "BANK% 4i %20s 0/8192\n", i, bank_name[i]); 621 | continue; 622 | } 623 | 624 | /* update used/free counters */ 625 | rom_used += nb; 626 | rom_free += 8192 - nb; 627 | 628 | /* scan */ 629 | if (dump_seg == 1) 630 | continue; 631 | 632 | for (;;) { 633 | /* search section start */ 634 | for (; addr < 8192; addr++) 635 | if (map[i][addr] != 0xFF) 636 | break; 637 | 638 | /* check for end of bank */ 639 | if (addr > 8191) 640 | break; 641 | 642 | /* get section type */ 643 | section = map[i][addr] & 0x0F; 644 | page = (map[i][addr] & 0xE0) << 8; 645 | start = addr; 646 | 647 | /* search section end */ 648 | for (; addr < 8192; addr++) 649 | if ((map[i][addr] & 0x0F) != section) 650 | break; 651 | 652 | /* display section infos */ 653 | fprintf(stderr, " %s $%04X-$%04X [%4i]\n", 654 | section_name[section], /* section name */ 655 | start + page, /* starting address */ 656 | addr + page - 1, /* end address */ 657 | addr - start); /* size */ 658 | } 659 | } 660 | 661 | /* total */ 662 | rom_used = (rom_used + 1023) >> 10; 663 | rom_free = (rom_free) >> 10; 664 | fprintf(stderr, "\t\t\t\t ---- ----\n"); 665 | fprintf(stderr, "\t\t\t\t %4iK%4iK\n", rom_used, rom_free); 666 | } 667 | 668 | -------------------------------------------------------------------------------- /source/expr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "externs.h" 7 | #include "protos.h" 8 | #include "expr.h" 9 | 10 | /* ---- 11 | * evaluate() 12 | * ---- 13 | * evaluate an expression 14 | */ 15 | 16 | int 17 | evaluate(int *ip, char last_char) 18 | { 19 | int end, level; 20 | int op, type; 21 | int arg; 22 | int i; 23 | unsigned char c; 24 | 25 | end = 0; 26 | level = 0; 27 | undef = 0; 28 | op_idx = 0; 29 | val_idx = 0; 30 | value = 0; 31 | val_stack[0] = 0; 32 | need_operator = 0; 33 | expr_lablptr = NULL; 34 | expr_lablcnt = 0; 35 | op = OP_START; 36 | func_idx = 0; 37 | 38 | /* array index to pointer */ 39 | expr = &prlnbuf[*ip]; 40 | 41 | /* skip spaces */ 42 | cont: 43 | while (isspace((int)*expr)) 44 | expr++; 45 | 46 | /* search for a continuation char */ 47 | if (*expr == '\\') { 48 | /* skip spaces */ 49 | i = 1; 50 | while (isspace((int)expr[i])) 51 | i++; 52 | 53 | /* check if end of line */ 54 | if (expr[i] == ';' || expr[i] == '\0') { 55 | /* output */ 56 | if (!continued_line) { 57 | /* replace '\' with three dots */ 58 | strcpy(expr, "..."); 59 | 60 | /* store the current line */ 61 | strcpy(tmplnbuf, prlnbuf); 62 | } 63 | 64 | /* ok */ 65 | continued_line++; 66 | 67 | /* read a new line */ 68 | if (readline() == -1) 69 | return (0); 70 | 71 | /* rewind line pointer and continue */ 72 | expr = &prlnbuf[SFIELD]; 73 | goto cont; 74 | } 75 | } 76 | 77 | /* parser main loop */ 78 | while (!end) { 79 | c = *expr; 80 | 81 | /* number */ 82 | if (isdigit(c)) { 83 | if (need_operator) 84 | goto error; 85 | if (!push_val(T_DECIMAL)) 86 | return (0); 87 | } 88 | 89 | /* symbol */ 90 | else 91 | if (isalpha(c) || c == '_' || c == '.') { 92 | if (need_operator) 93 | goto error; 94 | if (!push_val(T_SYMBOL)) 95 | return (0); 96 | } 97 | 98 | /* operators */ 99 | else { 100 | switch (c) { 101 | /* function arg */ 102 | case '\\': 103 | if (func_idx == 0) { 104 | error("Syntax error in expression!"); 105 | return (0); 106 | } 107 | expr++; 108 | c = *expr++; 109 | if (c < '1' || c > '9') { 110 | error("Invalid function argument index!"); 111 | return (0); 112 | } 113 | arg = c - '1'; 114 | expr_stack[func_idx++] = expr; 115 | expr = func_arg[func_idx - 2][arg]; 116 | break; 117 | 118 | /* hexa prefix */ 119 | case '$': 120 | if (need_operator) 121 | goto error; 122 | if (!push_val(T_HEXA)) 123 | return (0); 124 | break; 125 | 126 | /* character prefix */ 127 | case '\'': 128 | if (need_operator) 129 | goto error; 130 | if (!push_val(T_CHAR)) 131 | return (0); 132 | break; 133 | 134 | /* round brackets */ 135 | case '(': 136 | if (need_operator) 137 | goto error; 138 | if (!push_op(OP_OPEN)) 139 | return (0); 140 | level++; 141 | expr++; 142 | break; 143 | case ')': 144 | if (!need_operator) 145 | goto error; 146 | if (level == 0) 147 | goto error; 148 | while (op_stack[op_idx] != OP_OPEN) { 149 | if (!do_op()) 150 | return (0); 151 | } 152 | op_idx--; 153 | level--; 154 | expr++; 155 | break; 156 | 157 | /* not equal, left shift, lower, lower or equal */ 158 | case '<': 159 | if (!need_operator) 160 | goto error; 161 | expr++; 162 | switch (*expr) { 163 | case '>': 164 | op = OP_NOT_EQUAL; 165 | expr++; 166 | break; 167 | case '<': 168 | op = OP_SHL; 169 | expr++; 170 | break; 171 | case '=': 172 | op = OP_LOWER_EQUAL; 173 | expr++; 174 | break; 175 | default: 176 | op = OP_LOWER; 177 | break; 178 | } 179 | if (!push_op(op)) 180 | return (0); 181 | break; 182 | 183 | /* right shift, higher, higher or equal */ 184 | case '>': 185 | if (!need_operator) 186 | goto error; 187 | expr++; 188 | switch (*expr) { 189 | case '>': 190 | op = OP_SHR; 191 | expr++; 192 | break; 193 | case '=': 194 | op = OP_HIGHER_EQUAL; 195 | expr++; 196 | break; 197 | default: 198 | op = OP_HIGHER; 199 | break; 200 | } 201 | if (!push_op(op)) 202 | return (0); 203 | break; 204 | 205 | /* equal */ 206 | case '=': 207 | if (!need_operator) 208 | goto error; 209 | if (!push_op(OP_EQUAL)) 210 | return (0); 211 | expr++; 212 | break; 213 | 214 | /* one complement */ 215 | case '~': 216 | if (need_operator) 217 | goto error; 218 | if (!push_op(OP_COM)) 219 | return (0); 220 | expr++; 221 | break; 222 | 223 | /* sub, neg */ 224 | case '-': 225 | if (need_operator) 226 | op = OP_SUB; 227 | else 228 | op = OP_NEG; 229 | if (!push_op(op)) 230 | return (0); 231 | expr++; 232 | break; 233 | 234 | /* not, not equal */ 235 | case '!': 236 | if (!need_operator) 237 | op = OP_NOT; 238 | else { 239 | op = OP_NOT_EQUAL; 240 | expr++; 241 | if (*expr != '=') 242 | goto error; 243 | } 244 | if (!push_op(op)) 245 | return (0); 246 | expr++; 247 | break; 248 | 249 | /* binary prefix, current PC */ 250 | case '%': 251 | case '*': 252 | if (!need_operator) { 253 | if (c == '%') 254 | type = T_BINARY; 255 | else 256 | type = T_PC; 257 | if (!push_val(type)) 258 | return (0); 259 | break; 260 | } 261 | 262 | /* modulo, mul, add, div, and, xor, or */ 263 | case '+': 264 | case '/': 265 | case '&': 266 | case '^': 267 | case '|': 268 | if (!need_operator) 269 | goto error; 270 | switch (c) { 271 | case '%': op = OP_MOD; break; 272 | case '*': op = OP_MUL; break; 273 | case '+': op = OP_ADD; break; 274 | case '/': op = OP_DIV; break; 275 | case '&': op = OP_AND; break; 276 | case '^': op = OP_XOR; break; 277 | case '|': op = OP_OR; break; 278 | } 279 | if (!push_op(op)) 280 | return (0); 281 | expr++; 282 | break; 283 | 284 | /* skip immediate operand prefix if in macro */ 285 | case '#': 286 | if (expand_macro) 287 | expr++; 288 | else 289 | end = 3; 290 | break; 291 | 292 | /* space or tab */ 293 | case ' ': 294 | case '\t': 295 | expr++; 296 | break; 297 | 298 | /* end of line */ 299 | case '\0': 300 | if (func_idx) { 301 | func_idx--; 302 | expr = expr_stack[func_idx]; 303 | break; 304 | } 305 | case ';': 306 | end = 1; 307 | break; 308 | case ',': 309 | end = 2; 310 | break; 311 | default: 312 | end = 3; 313 | break; 314 | } 315 | } 316 | } 317 | 318 | if (!need_operator) 319 | goto error; 320 | if (level != 0) 321 | goto error; 322 | while (op_stack[op_idx] != OP_START) { 323 | if (!do_op()) 324 | return (0); 325 | } 326 | 327 | /* get the expression value */ 328 | value = val_stack[val_idx]; 329 | 330 | /* any undefined symbols? trap that if in the last pass */ 331 | if (undef) { 332 | if (pass == LAST_PASS) 333 | error("Undefined symbol in operand field!"); 334 | } 335 | 336 | /* check if the last char is what the user asked for */ 337 | switch (last_char) { 338 | case ';': 339 | if (end != 1) 340 | goto error; 341 | expr++; 342 | break; 343 | case ',': 344 | if (end != 2) { 345 | error("Argument missing!"); 346 | return (0); 347 | } 348 | expr++; 349 | break; 350 | } 351 | 352 | /* convert back the pointer to an array index */ 353 | *ip = (long)expr - (long)prlnbuf; 354 | 355 | /* ok */ 356 | return (1); 357 | 358 | /* syntax error */ 359 | error: 360 | error("Syntax error in expression!"); 361 | return (0); 362 | } 363 | 364 | 365 | /* ---- 366 | * push_val() 367 | * ---- 368 | * extract a number and push it on the value stack 369 | */ 370 | 371 | int 372 | push_val(int type) 373 | { 374 | unsigned int mul, val; 375 | int op; 376 | char c; 377 | 378 | val = 0; 379 | c = *expr; 380 | 381 | switch (type) { 382 | /* program counter */ 383 | case T_PC: 384 | if (data_loccnt == -1) 385 | val = (loccnt + (page << 13)); 386 | else 387 | val = (data_loccnt + (page << 13)); 388 | expr++; 389 | break; 390 | 391 | /* char ascii value */ 392 | case T_CHAR: 393 | expr++; 394 | val = *expr++; 395 | if ((*expr != c) || (val == 0)) { 396 | error("Syntax Error!"); 397 | return (0); 398 | } 399 | expr++; 400 | break; 401 | 402 | /* symbol */ 403 | case T_SYMBOL: 404 | /* extract it */ 405 | if (!getsym()) 406 | return (0); 407 | 408 | /* an user function? */ 409 | if (func_look()) { 410 | if (!func_getargs()) 411 | return (0); 412 | 413 | expr_stack[func_idx++] = expr; 414 | expr = func_ptr->line; 415 | return (1); 416 | } 417 | 418 | /* a predefined function? */ 419 | op = check_keyword(); 420 | if (op) { 421 | if (!push_op(op)) 422 | return (0); 423 | else 424 | return (1); 425 | } 426 | 427 | /* search the symbol */ 428 | expr_lablptr = stlook(1); 429 | 430 | /* check if undefined, if not get its value */ 431 | if (expr_lablptr == NULL) 432 | return (0); 433 | else if (expr_lablptr->type == UNDEF) 434 | undef++; 435 | else if (expr_lablptr->type == IFUNDEF) 436 | undef++; 437 | else 438 | val = expr_lablptr->value; 439 | 440 | /* remember we have seen a symbol in the expression */ 441 | expr_lablcnt++; 442 | break; 443 | 444 | /* binary number %1100_0011 */ 445 | case T_BINARY: 446 | mul = 2; 447 | goto extract; 448 | 449 | /* hexa number $15AF */ 450 | case T_HEXA: 451 | mul = 16; 452 | goto extract; 453 | 454 | /* decimal number 48 (or hexa 0x5F) */ 455 | case T_DECIMAL: 456 | if((c == '0') && (toupper(expr[1]) == 'X')) { 457 | mul = 16; 458 | expr++; 459 | } 460 | else { 461 | mul = 10; 462 | val = c - '0'; 463 | } 464 | /* extract a number */ 465 | extract: 466 | for (;;) { 467 | expr++; 468 | c = *expr; 469 | 470 | if (isdigit(c)) 471 | c -= '0'; 472 | else if (isalpha(c)) { 473 | c = toupper(c); 474 | if (c < 'A' && c > 'F') 475 | break; 476 | else { 477 | c -= 'A'; 478 | c += 10; 479 | } 480 | } 481 | else if (c == '_' && mul == 2) 482 | continue; 483 | else 484 | break; 485 | if (c >= mul) 486 | break; 487 | val = (val * mul) + c; 488 | } 489 | break; 490 | } 491 | 492 | /* check for too big expression */ 493 | if (val_idx == 63) { 494 | error("Expression too complex!"); 495 | return (0); 496 | } 497 | 498 | /* push the result on the value stack */ 499 | val_idx++; 500 | val_stack[val_idx] = val; 501 | 502 | /* next must be an operator */ 503 | need_operator = 1; 504 | 505 | /* ok */ 506 | return (1); 507 | } 508 | 509 | 510 | /* ---- 511 | * getsym() 512 | * ---- 513 | * extract a symbol name from the input string 514 | */ 515 | 516 | int 517 | getsym(void) 518 | { 519 | int valid; 520 | int i; 521 | char c; 522 | 523 | valid = 1; 524 | i = 0; 525 | 526 | /* get the symbol, stop to the first 'non symbol' char */ 527 | while (valid) { 528 | c = *expr; 529 | if (isalpha(c) || c == '_' || c == '.' || (isdigit(c) && i >= 1)) { 530 | if (i < SBOLSZ - 1) 531 | symbol[++i] = c; 532 | expr++; 533 | } 534 | else { 535 | valid = 0; 536 | } 537 | } 538 | 539 | /* is it a reserved symbol? */ 540 | if (i == 1) { 541 | switch (toupper(symbol[1])) { 542 | case 'A': 543 | case 'X': 544 | case 'Y': 545 | error("Symbol is reserved (A, X or Y)!"); 546 | i = 0; 547 | } 548 | } 549 | 550 | /* store symbol length */ 551 | symbol[0] = i; 552 | symbol[i+1] = '\0'; 553 | return (i); 554 | } 555 | 556 | 557 | /* ---- 558 | * check_keyword() 559 | * ---- 560 | * verify if the current symbol is a reserved function 561 | */ 562 | 563 | int 564 | check_keyword(void) 565 | { 566 | int op = 0; 567 | 568 | /* check if its an assembler function */ 569 | if (!strcasecmp(symbol, keyword[0])) 570 | op = OP_DEFINED; 571 | else if (!strcasecmp(symbol, keyword[1])) 572 | op = OP_HIGH; 573 | else if (!strcasecmp(symbol, keyword[2])) 574 | op = OP_LOW; 575 | else if (!strcasecmp(symbol, keyword[3])) 576 | op = OP_PAGE; 577 | else if (!strcasecmp(symbol, keyword[4])) 578 | op = OP_BANK; 579 | else if (!strcasecmp(symbol, keyword[7])) 580 | op = OP_SIZEOF; 581 | else { 582 | if (machine->type == MACHINE_PCE) { 583 | /* PCE specific functions */ 584 | if (!strcasecmp(symbol, keyword[5])) 585 | op = OP_VRAM; 586 | else if (!strcasecmp(symbol, keyword[6])) 587 | op = OP_PAL; 588 | } 589 | } 590 | 591 | /* extra setup for functions that send back symbol infos */ 592 | switch (op) { 593 | case OP_DEFINED: 594 | case OP_HIGH: 595 | case OP_LOW: 596 | case OP_PAGE: 597 | case OP_BANK: 598 | case OP_VRAM: 599 | case OP_PAL: 600 | case OP_SIZEOF: 601 | expr_lablptr = NULL; 602 | expr_lablcnt = 0; 603 | break; 604 | } 605 | 606 | /* ok */ 607 | return (op); 608 | } 609 | 610 | 611 | /* ---- 612 | * push_op() 613 | * ---- 614 | * push an operator on the stack 615 | */ 616 | 617 | int 618 | push_op(int op) 619 | { 620 | if (op != OP_OPEN) { 621 | while (op_pri[op_stack[op_idx]] >= op_pri[op]) { 622 | if (!do_op()) 623 | return (0); 624 | } 625 | } 626 | if (op_idx == 63) { 627 | error("Expression too complex!"); 628 | return (0); 629 | } 630 | op_idx++; 631 | op_stack[op_idx] = op; 632 | need_operator = 0; 633 | return (1); 634 | } 635 | 636 | 637 | /* ---- 638 | * do_op() 639 | * ---- 640 | * apply an operator to the value stack 641 | */ 642 | 643 | int 644 | do_op(void) 645 | { 646 | int val[2] ={0,0}; 647 | int op; 648 | 649 | /* operator */ 650 | op = op_stack[op_idx--]; 651 | 652 | /* first arg */ 653 | val[0] = val_stack[val_idx]; 654 | 655 | /* second arg */ 656 | if (op_pri[op] < 9) 657 | val[1] = val_stack[--val_idx]; 658 | 659 | switch (op) { 660 | /* BANK */ 661 | case OP_BANK: 662 | if (!check_func_args("BANK")) 663 | return (0); 664 | if (pass == LAST_PASS) { 665 | if (expr_lablptr->bank == RESERVED_BANK) { 666 | error("No BANK index for this symbol!"); 667 | val[0] = 0; 668 | break; 669 | } 670 | } 671 | val[0] = expr_lablptr->bank; 672 | break; 673 | 674 | /* PAGE */ 675 | case OP_PAGE: 676 | if (!check_func_args("PAGE")) 677 | return (0); 678 | val[0] = expr_lablptr->page; 679 | break; 680 | 681 | /* VRAM */ 682 | case OP_VRAM: 683 | if (!check_func_args("VRAM")) 684 | return (0); 685 | if (pass == LAST_PASS) { 686 | if (expr_lablptr->vram == -1) 687 | error("No VRAM address for this symbol!"); 688 | } 689 | val[0] = expr_lablptr->vram; 690 | break; 691 | 692 | /* PAL */ 693 | case OP_PAL: 694 | if (!check_func_args("PAL")) 695 | return (0); 696 | if (pass == LAST_PASS) { 697 | if (expr_lablptr->pal == -1) 698 | error("No palette index for this symbol!"); 699 | } 700 | val[0] = expr_lablptr->pal; 701 | break; 702 | 703 | /* DEFINED */ 704 | case OP_DEFINED: 705 | if (!check_func_args("DEFINED")) 706 | return (0); 707 | if ((expr_lablptr->type != IFUNDEF) && (expr_lablptr->type != UNDEF)) 708 | val[0] = 1; 709 | else { 710 | val[0] = 0; 711 | undef--; 712 | } 713 | break; 714 | 715 | /* SIZEOF */ 716 | case OP_SIZEOF: 717 | if (!check_func_args("SIZEOF")) 718 | return (0); 719 | if (pass == LAST_PASS) { 720 | if (expr_lablptr->data_type == -1) { 721 | error("No size attributes for this symbol!"); 722 | return (0); 723 | } 724 | } 725 | val[0] = expr_lablptr->data_size; 726 | break; 727 | 728 | /* HIGH */ 729 | case OP_HIGH: 730 | val[0] = (val[0] & 0xFF00) >> 8; 731 | break; 732 | 733 | /* LOW */ 734 | case OP_LOW: 735 | val[0] = val[0] & 0xFF; 736 | break; 737 | 738 | case OP_ADD: 739 | val[0] = val[1] + val[0]; 740 | break; 741 | 742 | case OP_SUB: 743 | val[0] = val[1] - val[0]; 744 | break; 745 | 746 | case OP_MUL: 747 | val[0] = val[1] * val[0]; 748 | break; 749 | 750 | case OP_DIV: 751 | if (val[0] == 0) { 752 | error("Divide by zero!"); 753 | return (0); 754 | } 755 | val[0] = val[1] / val[0]; 756 | break; 757 | 758 | case OP_MOD: 759 | if (val[0] == 0) { 760 | error("Divide by zero!"); 761 | return (0); 762 | } 763 | val[0] = val[1] % val[0]; 764 | break; 765 | 766 | case OP_NEG: 767 | val[0] = -val[0]; 768 | break; 769 | 770 | case OP_SHL: 771 | val[0] = val[1] << (val[0] & 0x1F); 772 | break; 773 | 774 | case OP_SHR: 775 | val[0] = val[1] >> (val[0] & 0x1f); 776 | break; 777 | 778 | case OP_OR: 779 | val[0] = val[1] | val[0]; 780 | break; 781 | 782 | case OP_XOR: 783 | val[0] = val[1] ^ val[0]; 784 | break; 785 | 786 | case OP_AND: 787 | val[0] = val[1] & val[0]; 788 | break; 789 | 790 | case OP_COM: 791 | val[0] = ~val[0]; 792 | break; 793 | 794 | case OP_NOT: 795 | val[0] = !val[0]; 796 | break; 797 | 798 | case OP_EQUAL: 799 | val[0] = (val[1] == val[0]); 800 | break; 801 | 802 | case OP_NOT_EQUAL: 803 | val[0] = (val[1] != val[0]); 804 | break; 805 | 806 | case OP_LOWER: 807 | val[0] = (val[1] < val[0]); 808 | break; 809 | 810 | case OP_LOWER_EQUAL: 811 | val[0] = (val[1] <= val[0]); 812 | break; 813 | 814 | case OP_HIGHER: 815 | val[0] = (val[1] > val[0]); 816 | break; 817 | 818 | case OP_HIGHER_EQUAL: 819 | val[0] = (val[1] >= val[0]); 820 | break; 821 | 822 | default: 823 | error("Invalid operator in expression!"); 824 | return (0); 825 | } 826 | 827 | /* result */ 828 | val_stack[val_idx] = val[0]; 829 | return (1); 830 | } 831 | 832 | 833 | /* ---- 834 | * check_func_args() 835 | * ---- 836 | * check BANK/PAGE/VRAM/PAL function arguments 837 | */ 838 | 839 | int 840 | check_func_args(char *func_name) 841 | { 842 | char string[64]; 843 | 844 | if (expr_lablcnt == 1) 845 | return (1); 846 | else if (expr_lablcnt == 0) 847 | sprintf(string, "No symbol in function %s!", func_name); 848 | else { 849 | sprintf(string, "Too many symbols in function %s!", func_name); 850 | } 851 | 852 | /* output message */ 853 | error(string); 854 | return (0); 855 | } 856 | 857 | --------------------------------------------------------------------------------