#include #include #include #include #include "plasm.h" /* * Symbol table and fixup information. */ #define ID_LEN 32 static int consts = 0; static int externs = 0; static int globals = 0; static int locals = 0; static int localsize = 0; static int predefs = 0; static int defs = 0; static int asmdefs = 0; static int codetags = 1; // Fix check for break_tag and cont_tag static int fixups = 0; static int lastglobalsize = 0; static char idconst_name[1024][ID_LEN+1]; static int idconst_value[1024]; static char idglobal_name[1024][ID_LEN+1]; static int idglobal_type[1024]; static int idglobal_tag[1024]; static char idlocal_name[128][ID_LEN+1]; static int idlocal_type[128]; static int idlocal_offset[128]; static char fixup_size[2048]; static int fixup_type[2048]; static int fixup_tag[2048]; static int savelocalsize = 0; static int savelocals = 0; static char savelocal_name[128][ID_LEN+1]; static int savelocal_type[128]; static int savelocal_offset[128]; static t_opseq optbl[2048]; static t_opseq *freeop_lst = &optbl[0]; static t_opseq *pending_seq = 0; #define FIXUP_BYTE 0x00 #define FIXUP_WORD 0x80 int id_match(char *name, int len, char *id) { if (len == id[0]) { if (len > 16) len = 16; while (len--) { if (toupper(name[len]) != id[1 + len]) return (0); } return (1); } return (0); } int idconst_lookup(char *name, int len) { int i; for (i = 0; i < consts; i++) if (id_match(name, len, &(idconst_name[i][0]))) return (i); return (-1); } int idlocal_lookup(char *name, int len) { int i; for (i = 0; i < locals; i++) if (id_match(name, len, &(idlocal_name[i][0]))) return (i); return (-1); } int idglobal_lookup(char *name, int len) { int i; for (i = 0; i < globals; i++) if (id_match(name, len, &(idglobal_name[i][0]))) { if (idglobal_type[i] & EXTERN_TYPE) idglobal_type[i] |= ACCESSED_TYPE; return (i); } return (-1); } int idconst_add(char *name, int len, int value) { char c = name[len]; if (consts > 1024) { printf("Constant count overflow\n"); return (0); } name[len] = '\0'; emit_idconst(name, value); name[len] = c; idconst_name[consts][0] = len; if (len > ID_LEN) len = ID_LEN; while (len--) idconst_name[consts][1 + len] = toupper(name[len]); idconst_value[consts] = value; consts++; return (1); } int idlocal_add(char *name, int len, int type, int size) { char c = name[len]; if (localsize > 255) { printf("Local variable size overflow\n"); return (0); } if (idconst_lookup(name, len) > 0) { parse_error("const/local name conflict\n"); return (0); } if (idlocal_lookup(name, len) > 0) { parse_error("local label already defined\n"); return (0); } name[len] = '\0'; emit_idlocal(name, localsize); name[len] = c; idlocal_name[locals][0] = len; if (len > ID_LEN) len = ID_LEN; while (len--) idlocal_name[locals][1 + len] = toupper(name[len]); idlocal_type[locals] = type | LOCAL_TYPE; idlocal_offset[locals] = localsize; localsize += size; locals++; return (1); } int idglobal_add(char *name, int len, int type, int size) { char c = name[len]; if (globals > 1024) { printf("Global variable count overflow\n"); return (0); } if (idconst_lookup(name, len) > 0) { parse_error("const/global name conflict\n"); return (0); } if (idglobal_lookup(name, len) > 0) { parse_error("global label already defined\n"); return (0); } name[len] = '\0'; name[len] = c; idglobal_name[globals][0] = len; if (len > ID_LEN) len = ID_LEN; while (len--) idglobal_name[globals][1 + len] = toupper(name[len]); idglobal_type[globals] = type; if (!(type & EXTERN_TYPE)) { emit_idglobal(globals, size, name); idglobal_tag[globals] = globals; globals++; } else { printf("\t\t\t\t\t; %s -> X%03d\n", &idglobal_name[globals][1], externs); idglobal_tag[globals++] = externs++; } return (1); } int id_add(char *name, int len, int type, int size) { return ((type & LOCAL_TYPE) ? idlocal_add(name, len, type, size) : idglobal_add(name, len, type, size)); } void idlocal_reset(void) { locals = 0; localsize = 0; } void idlocal_save(void) { savelocals = locals; savelocalsize = localsize; memcpy(savelocal_name, idlocal_name, locals*(ID_LEN+1)); memcpy(savelocal_type, idlocal_type, locals*sizeof(int)); memcpy(savelocal_offset, idlocal_offset, locals*sizeof(int)); locals = 0; localsize = 0; } void idlocal_restore(void) { locals = savelocals; localsize = savelocalsize; memcpy(idlocal_name, savelocal_name, locals*(ID_LEN+1)); memcpy(idlocal_type, savelocal_type, locals*sizeof(int)); memcpy(idlocal_offset, savelocal_offset, locals*sizeof(int)); } int idfunc_add(char *name, int len, int type, int tag) { if (globals > 1024) { printf("Global variable count overflow\n"); return (0); } idglobal_name[globals][0] = len; if (len > ID_LEN) len = ID_LEN; while (len--) idglobal_name[globals][1 + len] = toupper(name[len]); idglobal_type[globals] = type; idglobal_tag[globals++] = tag; if (type & EXTERN_TYPE) printf("\t\t\t\t\t; %s -> X%03d\n", &idglobal_name[globals - 1][1], tag); return (1); } int idfunc_set(char *name, int len, int type, int tag) { int i; if (((i = idglobal_lookup(name, len)) >= 0) && (idglobal_type[i] & FUNC_TYPE)) { idglobal_tag[i] = tag; idglobal_type[i] = type; return (type); } parse_error("Undeclared identifier"); return (0); } void idglobal_size(int type, int size, int constsize) { if (size > constsize) emit_data(0, 0, 0, size - constsize); else if (size) emit_data(0, 0, 0, size); } void idlocal_size(int size) { localsize += size; if (localsize > 255) { parse_error("Local variable size overflow\n"); } } int id_tag(char *name, int len) { int i; if ((i = idlocal_lookup(name, len)) >= 0) return (idlocal_offset[i]); if ((i = idglobal_lookup(name, len)) >= 0) return (idglobal_tag[i]); return (-1); } int id_const(char *name, int len) { int i; if ((i = idconst_lookup(name, len)) >= 0) return (idconst_value[i]); parse_error("Undeclared constant"); return (0); } int id_type(char *name, int len) { int i; if ((i = idconst_lookup(name, len)) >= 0) return (CONST_TYPE); if ((i = idlocal_lookup(name, len)) >= 0) return (idlocal_type[i] | LOCAL_TYPE); if ((i = idglobal_lookup(name, len)) >= 0) return (idglobal_type[i]); parse_error("Undeclared identifier"); return (0); } int tag_new(int type) { if (type & EXTERN_TYPE) { if (externs > 254) parse_error("External variable count overflow\n"); return (externs++); } if (type & PREDEF_TYPE) return (predefs++); if (type & ASM_TYPE) return (asmdefs++); if (type & DEF_TYPE) return (defs++); if (type & BRANCH_TYPE) return (codetags++); return globals++; } int fixup_new(int tag, int type, int size) { fixup_tag[fixups] = tag; fixup_type[fixups] = type; fixup_size[fixups] = size; return (fixups++); } /* * Emit assembly code. */ static const char *DB = ".BYTE"; static const char *DW = ".WORD"; static const char *DS = ".RES"; static char LBL = (char) ':'; char *supper(char *s) { static char su[80]; int i; for (i = 0; s[i]; i++) su[i] = toupper(s[i]); su[i] = '\0'; return su; } char *tag_string(int tag, int type) { static char str[16]; char t; if (type & EXTERN_TYPE) t = 'X'; else if (type & DEF_TYPE) t = 'C'; else if (type & ASM_TYPE) t = 'A'; else if (type & BRANCH_TYPE) t = 'B'; else if (type & PREDEF_TYPE) t = 'P'; else t = 'D'; sprintf(str, "_%c%03d", t, tag); return str; } void emit_dci(char *str, int len) { if (len--) { printf("\t; DCI STRING: %s\n", supper(str)); printf("\t%s\t$%02X", DB, toupper(*str++) | (len ? 0x80 : 0x00)); while (len--) printf(",$%02X", toupper(*str++) | (len ? 0x80 : 0x00)); printf("\n"); } } void emit_flags(int flags) { if (flags & ACME) { DB = "!BYTE"; DW = "!WORD"; DS = "!FILL"; LBL = ' '; } } void emit_header(void) { int i; if (outflags & ACME) printf("; ACME COMPATIBLE OUTPUT\n"); else printf("; CA65 COMPATIBLE OUTPUT\n"); if (outflags & MODULE) { printf("\t%s\t_SEGEND-_SEGBEGIN\t; LENGTH OF HEADER + CODE/DATA + BYTECODE SEGMENT\n", DW); printf("_SEGBEGIN%c\n", LBL); printf("\t%s\t$6502\t\t\t; MAGIC #\n", DW); printf("\t%s\t_SYSFLAGS\t\t\t; SYSTEM FLAGS\n", DW); printf("\t%s\t_SUBSEG\t\t\t; BYTECODE SUB-SEGMENT\n", DW); printf("\t%s\t_DEFCNT\t\t\t; BYTECODE DEF COUNT\n", DW); printf("\t%s\t_INIT\t\t\t; MODULE INITIALIZATION ROUTINE\n", DW); } else { printf("\tJMP\t_INIT\t\t\t; MODULE INITIALIZATION ROUTINE\n"); } /* * Init free op sequence table */ for (i = 0; i < sizeof(optbl)/sizeof(t_opseq)-1; i++) optbl[i].nextop = &optbl[i+1]; optbl[i].nextop = NULL; } void emit_rld(void) { int i; printf(";\n; RE-LOCATEABLE DICTIONARY\n;\n"); /* * First emit the bytecode definition entrypoint information. */ for (i = 0; i < globals; i++) if (!(idglobal_type[i] & EXTERN_TYPE) && (idglobal_type[i] & DEF_TYPE)) { printf("\t%s\t$02\t\t\t; CODE TABLE FIXUP\n", DB); printf("\t%s\t_C%03d\t\t\n", DW, idglobal_tag[i]); printf("\t%s\t$00\n", DB); } /* * Now emit the fixup table. */ for (i = 0; i < fixups; i++) { if (fixup_type[i] & EXTERN_TYPE) { printf("\t%s\t$%02X\t\t\t; EXTERNAL FIXUP\n", DB, 0x11 + fixup_size[i] & 0xFF); printf("\t%s\t_F%03d-_SEGBEGIN\t\t\n", DW, i); printf("\t%s\t%d\t\t\t; ESD INDEX\n", DB, fixup_tag[i]); } else { printf("\t%s\t$%02X\t\t\t; INTERNAL FIXUP\n", DB, 0x01 + fixup_size[i] & 0xFF); printf("\t%s\t_F%03d-_SEGBEGIN\t\t\n", DW, i); printf("\t%s\t$00\n", DB); } } printf("\t%s\t$00\t\t\t; END OF RLD\n", DB); } void emit_esd(void) { int i; printf(";\n; EXTERNAL/ENTRY SYMBOL DICTIONARY\n;\n"); for (i = 0; i < globals; i++) { if (idglobal_type[i] & ACCESSED_TYPE) // Only refer to accessed externals { emit_dci(&idglobal_name[i][1], idglobal_name[i][0]); printf("\t%s\t$10\t\t\t; EXTERNAL SYMBOL FLAG\n", DB); printf("\t%s\t%d\t\t\t; ESD INDEX\n", DW, idglobal_tag[i]); } else if (idglobal_type[i] & EXPORT_TYPE) { emit_dci(&idglobal_name[i][1], idglobal_name[i][0]); printf("\t%s\t$08\t\t\t; ENTRY SYMBOL FLAG\n", DB); printf("\t%s\t%s\t\t\n", DW, tag_string(idglobal_tag[i], idglobal_type[i])); } } printf("\t%s\t$00\t\t\t; END OF ESD\n", DB); } void emit_trailer(void) { if (!(outflags & BYTECODE_SEG)) emit_bytecode_seg(); if (!(outflags & INIT)) printf("_INIT\t=\t0\n"); if (!(outflags & SYSFLAGS)) printf("_SYSFLAGS\t=\t0\n"); if (outflags & MODULE) { printf("_DEFCNT\t=\t%d\n", defs); printf("_SEGEND%c\n", LBL); emit_rld(); emit_esd(); } } void emit_moddep(char *name, int len) { if (outflags & MODULE) { if (name) { emit_dci(name, len); idglobal_add(name, len, EXTERN_TYPE | WORD_TYPE, 2); // Add to symbol table } else printf("\t%s\t$00\t\t\t; END OF MODULE DEPENDENCIES\n", DB); } } void emit_sysflags(int val) { printf("_SYSFLAGS\t=\t$%04X\t\t; SYSTEM FLAGS\n", val); outflags |= SYSFLAGS; } void emit_bytecode_seg(void) { if ((outflags & MODULE) && !(outflags & BYTECODE_SEG)) { if (lastglobalsize == 0) // Pad a byte if last label is at end of data segment printf("\t%s\t$00\t\t\t; PAD BYTE\n", DB); printf("_SUBSEG%c\t\t\t\t; BYTECODE STARTS\n", LBL); } outflags |= BYTECODE_SEG; } void emit_comment(char *s) { printf("\t\t\t\t\t; %s\n", s); } void emit_asm(char *s) { printf("%s\n", s); } void emit_idlocal(char *name, int value) { printf("\t\t\t\t\t; %s -> [%d]\n", name, value); } void emit_idglobal(int tag, int size, char *name) { lastglobalsize = size; if (size == 0) printf("_D%03d%c\t\t\t\t\t; %s\n", tag, LBL, name); else printf("_D%03d%c\t%s\t%d\t\t\t; %s\n", tag, LBL, DS, size, name); } void emit_idfunc(int tag, int type, char *name, int is_bytecode) { if (name) printf("%s%c\t\t\t\t\t; %s()\n", tag_string(tag, type), LBL, name); if (!(outflags & MODULE)) { //printf("%s%c\n", name, LBL); if (is_bytecode) printf("\tJSR\tINTERP\n"); } } void emit_lambdafunc(int tag, char *name, int cparams, t_opseq *lambda_seq) { emit_idfunc(tag, DEF_TYPE, name, 1); if (cparams) printf("\t%s\t$58,$%02X,$%02X\t\t; ENTER\t%d,%d\n", DB, cparams*2, cparams, cparams*2, cparams); emit_seq(lambda_seq); emit_pending_seq(); if (cparams) printf("\t%s\t$5A,$%02X\t\t\t; LEAVE\t%d\n", DB, cparams*2, cparams*2); else printf("\t%s\t$5C\t\t\t; RET\n", DB); } void emit_idconst(char *name, int value) { printf("\t\t\t\t\t; %s = %d\n", name, value); } int emit_data(int vartype, int consttype, long constval, int constsize) { int datasize, i; unsigned char *str; if (consttype == 0) { datasize = constsize; printf("\t%s\t$%02X\n", DS, constsize); } else if (consttype & STRING_TYPE) { str = (unsigned char *)constval; constsize = *str++; datasize = constsize + 1; printf("\t%s\t$%02X\n", DB, constsize); while (constsize-- > 0) { printf("\t%s\t$%02X", DB, *str++); for (i = 0; i < 7; i++) { if (constsize-- > 0) printf(",$%02X", *str++); else break; } printf("\n"); } } else if (consttype & ADDR_TYPE) { if (vartype & WORD_TYPE) { int fixup = fixup_new(constval, consttype, FIXUP_WORD); datasize = 2; if (consttype & EXTERN_TYPE) printf("_F%03d%c\t%s\t0\t\t\t; %s\n", fixup, LBL, DW, tag_string(constval, consttype)); else printf("_F%03d%c\t%s\t%s\n", fixup, LBL, DW, tag_string(constval, consttype)); } else { int fixup = fixup_new(constval, consttype, FIXUP_BYTE); datasize = 1; if (consttype & EXTERN_TYPE) printf("_F%03d%c\t%s\t0\t\t\t; %s\n", fixup, LBL, DB, tag_string(constval, consttype)); else printf("_F%03d%c\t%s\t%s\n", fixup, LBL, DB, tag_string(constval, consttype)); } } else { if (vartype & WORD_TYPE) { datasize = 2; printf("\t%s\t$%04lX\n", DW, constval & 0xFFFF); } else { datasize = 1; printf("\t%s\t$%02lX\n", DB, constval & 0xFF); } } return (datasize); } void emit_codetag(int tag) { emit_pending_seq(); printf("_B%03d%c\n", tag, LBL); } void emit_const(int cval) { emit_pending_seq(); if (cval == 0x0000) printf("\t%s\t$00\t\t\t; ZERO\n", DB); else if ((cval & 0xFF00) == 0x0000) printf("\t%s\t$2A,$%02X\t\t\t; CB\t%d\n", DB, cval, cval); else if ((cval & 0xFF00) == 0xFF00) printf("\t%s\t$5E,$%02X\t\t\t; CFFB\t%d\n", DB, cval&0xFF, cval); else printf("\t%s\t$2C,$%02X,$%02X\t\t; CW\t%d\n", DB, cval&0xFF,(cval>>8)&0xFF, cval); } void emit_conststr(long conststr) { printf("\t%s\t$2E\t\t\t; CS\n", DB); emit_data(0, STRING_TYPE, conststr, 0); } void emit_lb(void) { printf("\t%s\t$60\t\t\t; LB\n", DB); } void emit_lw(void) { printf("\t%s\t$62\t\t\t; LW\n", DB); } void emit_llb(int index) { printf("\t%s\t$64,$%02X\t\t\t; LLB\t[%d]\n", DB, index, index); } void emit_llw(int index) { printf("\t%s\t$66,$%02X\t\t\t; LLW\t[%d]\n", DB, index, index); } void emit_lab(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$68\t\t\t; LAB\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else { printf("\t%s\t$68,$%02X,$%02X\t\t; LAB\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } } void emit_law(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$6A\t\t\t; LAW\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else { printf("\t%s\t$6A,$%02X,$%02X\t\t; LAW\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } } void emit_sb(void) { printf("\t%s\t$70\t\t\t; SB\n", DB); } void emit_sw(void) { printf("\t%s\t$72\t\t\t; SW\n", DB); } void emit_slb(int index) { printf("\t%s\t$74,$%02X\t\t\t; SLB\t[%d]\n", DB, index, index); } void emit_slw(int index) { printf("\t%s\t$76,$%02X\t\t\t; SLW\t[%d]\n", DB, index, index); } void emit_dlb(int index) { printf("\t%s\t$6C,$%02X\t\t\t; DLB\t[%d]\n", DB, index, index); } void emit_dlw(int index) { printf("\t%s\t$6E,$%02X\t\t\t; DLW\t[%d]\n", DB, index, index); } void emit_sab(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$78\t\t\t; SAB\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else { printf("\t%s\t$78,$%02X,$%02X\t\t; SAB\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } } void emit_saw(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$7A\t\t\t; SAW\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else { printf("\t%s\t$7A,$%02X,$%02X\t\t; SAW\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } } void emit_dab(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$7C\t\t\t; DAB\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else printf("\t%s\t$7C,$%02X,$%02X\t\t; DAB\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } void emit_daw(int tag, int offset, int type) { if (type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$7E\t\t\t; DAW\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } else printf("\t%s\t$7E,$%02X,$%02X\t\t; DAW\t%d\n", DB, offset&0xFF,(offset>>8)&0xFF, offset); } void emit_localaddr(int index) { printf("\t%s\t$28,$%02X\t\t\t; LLA\t[%d]\n", DB, index, index); } void emit_globaladdr(int tag, int offset, int type) { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$26\t\t\t; LA\t%s+%d\n", DB, taglbl, offset); printf("_F%03d%c\t%s\t%s+%d\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl, offset); } void emit_indexbyte(void) { printf("\t%s\t$02\t\t\t; IDXB\n", DB); } void emit_indexword(void) { printf("\t%s\t$1E\t\t\t; IDXW\n", DB); } void emit_brfls(int tag) { printf("\t%s\t$4C\t\t\t; BRFLS\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_brtru(int tag) { printf("\t%s\t$4E\t\t\t; BRTRU\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_brnch(int tag) { emit_pending_seq(); printf("\t%s\t$50\t\t\t; BRNCH\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_breq(int tag) { emit_pending_seq(); printf("\t%s\t$3C\t\t\t; BREQ\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_brne(int tag) { emit_pending_seq(); printf("\t%s\t$3E\t\t\t; BRNE\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_brgt(int tag) { emit_pending_seq(); printf("\t%s\t$38\t\t\t; BRGT\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_brlt(int tag) { emit_pending_seq(); printf("\t%s\t$3A\t\t\t; BRLT\t_B%03d\n", DB, tag); printf("\t%s\t_B%03d-*\n", DW, tag); } void emit_call(int tag, int type) { if (type == CONST_TYPE) { printf("\t%s\t$54\t\t\t; CALL\t%i\n", DB, tag); printf("\t%s\t%i\t\t\n", DW, tag); } else { int fixup = fixup_new(tag, type, FIXUP_WORD); char *taglbl = tag_string(tag, type); printf("\t%s\t$54\t\t\t; CALL\t%s\n", DB, taglbl); printf("_F%03d%c\t%s\t%s\t\t\n", fixup, LBL, DW, type & EXTERN_TYPE ? "0" : taglbl); } } void emit_ical(void) { printf("\t%s\t$56\t\t\t; ICAL\n", DB); } void emit_leave(void) { emit_pending_seq(); if (localsize) printf("\t%s\t$5A,$%02X\t\t\t; LEAVE\t%d\n", DB, localsize, localsize); else printf("\t%s\t$5C\t\t\t; RET\n", DB); } void emit_ret(void) { emit_pending_seq(); printf("\t%s\t$5C\t\t\t; RET\n", DB); } void emit_enter(int cparams) { if (localsize) printf("\t%s\t$58,$%02X,$%02X\t\t; ENTER\t%d,%d\n", DB, localsize, cparams, localsize, cparams); } void emit_start(void) { printf("_INIT%c\n", LBL); outflags |= INIT; defs++; } void emit_drop(void) { emit_pending_seq(); printf("\t%s\t$30\t\t\t; DROP\n", DB); } void emit_dup(void) { printf("\t%s\t$32\t\t\t; DUP\n", DB); } int emit_unaryop(t_token op) { emit_pending_seq(); switch (op) { case NEG_TOKEN: printf("\t%s\t$10\t\t\t; NEG\n", DB); break; case COMP_TOKEN: printf("\t%s\t$12\t\t\t; COMP\n", DB); break; case LOGIC_NOT_TOKEN: printf("\t%s\t$20\t\t\t; NOT\n", DB); break; case INC_TOKEN: printf("\t%s\t$0C\t\t\t; INCR\n", DB); break; case DEC_TOKEN: printf("\t%s\t$0E\t\t\t; DECR\n", DB); break; case BPTR_TOKEN: emit_lb(); break; case WPTR_TOKEN: emit_lw(); break; default: printf("emit_unaryop(%c) ???\n", op & 0x7F); return (0); } return (1); } int emit_op(t_token op) { emit_pending_seq(); switch (op) { case MUL_TOKEN: printf("\t%s\t$06\t\t\t; MUL\n", DB); break; case DIV_TOKEN: printf("\t%s\t$08\t\t\t; DIV\n", DB); break; case MOD_TOKEN: printf("\t%s\t$0A\t\t\t; MOD\n", DB); break; case ADD_TOKEN: printf("\t%s\t$02\t\t\t; ADD\n", DB); break; case SUB_TOKEN: printf("\t%s\t$04\t\t\t; SUB\n", DB); break; case SHL_TOKEN: printf("\t%s\t$1A\t\t\t; SHL\n", DB); break; case SHR_TOKEN: printf("\t%s\t$1C\t\t\t; SHR\n", DB); break; case AND_TOKEN: printf("\t%s\t$14\t\t\t; AND\n", DB); break; case OR_TOKEN: printf("\t%s\t$16\t\t\t; IOR\n", DB); break; case EOR_TOKEN: printf("\t%s\t$18\t\t\t; XOR\n", DB); break; case EQ_TOKEN: printf("\t%s\t$40\t\t\t; ISEQ\n", DB); break; case NE_TOKEN: printf("\t%s\t$42\t\t\t; ISNE\n", DB); break; case GE_TOKEN: printf("\t%s\t$48\t\t\t; ISGE\n", DB); break; case LT_TOKEN: printf("\t%s\t$46\t\t\t; ISLT\n", DB); break; case GT_TOKEN: printf("\t%s\t$44\t\t\t; ISGT\n", DB); break; case LE_TOKEN: printf("\t%s\t$4A\t\t\t; ISLE\n", DB); break; case LOGIC_OR_TOKEN: printf("\t%s\t$22\t\t\t; LOR\n", DB); break; case LOGIC_AND_TOKEN: printf("\t%s\t$24\t\t\t; LAND\n", DB); break; case COMMA_TOKEN: break; default: return (0); } return (1); } /* * New/release sequence ops */ t_opseq *new_op(void) { t_opseq* op = freeop_lst; if (!op) { fprintf(stderr, "Compiler out of sequence ops!\n"); return (NULL); } freeop_lst = freeop_lst->nextop; op->nextop = NULL; return (op); } void release_op(t_opseq *op) { if (op) { op->nextop = freeop_lst; freeop_lst = op; } } void release_seq(t_opseq *seq) { t_opseq *op; while (seq) { op = seq; seq = seq->nextop; /* * Free this op */ op->nextop = freeop_lst; freeop_lst = op; } } /* * Indicate if an address is (or might be) memory-mapped hardware; used to avoid * optimising away accesses to such addresses. */ int is_hardware_address(int addr) { // TODO: I think this is reasonable for Apple hardware but I'm not sure. // It's a bit too strong for Acorn hardware but code is unlikely to try to // read from high addresses anyway, so there's no real harm in not // optimising such accesses anyway. return addr >= 0xC000; } /* * Replace all but the first of a series of identical load opcodes by DUP. This * doesn't reduce the number of opcodes but does reduce their size in bytes. * This is only called on the second optimisation pass because the DUP opcodes * may inhibit other peephole optimisations which are more valuable. */ int try_dupify(t_opseq *op) { int crunched = 0; t_opseq *opn = op->nextop; for (; opn; opn = opn->nextop) { if (op->code != opn->code) return crunched; switch (op->code) { case CONST_CODE: if (op->val != opn->val) return crunched; break; case LADDR_CODE: case LLB_CODE: case LLW_CODE: if (op->offsz != opn->offsz) return crunched; break; case GADDR_CODE: case LAB_CODE: case LAW_CODE: if ((op->tag != opn->tag) || (op->offsz != opn->offsz) /*|| (op->type != opn->type)*/) return crunched; break; default: return crunched; } opn->code = DUP_CODE; crunched = 1; } return crunched; } /* * Crunch sequence (peephole optimize) */ int crunch_seq(t_opseq **seq, int pass) { t_opseq *opnext, *opnextnext, *opprev = 0; t_opseq *op = *seq; int crunched = 0; int freeops = 0; int shiftcnt; while (op && (opnext = op->nextop)) { switch (op->code) { case CONST_CODE: if (op->val == 1) { if (opnext->code == BINARY_CODE(ADD_TOKEN)) { op->code = INC_CODE; freeops = 1; break; } if (opnext->code == BINARY_CODE(SUB_TOKEN)) { op->code = DEC_CODE; freeops = 1; break; } if (opnext->code == BINARY_CODE(SHL_TOKEN)) { op->code = DUP_CODE; opnext->code = BINARY_CODE(ADD_TOKEN); crunched = 1; break; } } switch (opnext->code) { case NEG_CODE: op->val = -(op->val); freeops = 1; break; case COMP_CODE: op->val = ~(op->val); freeops = 1; break; case LOGIC_NOT_CODE: op->val = op->val ? 0 : 1; freeops = 1; break; case UNARY_CODE(BPTR_TOKEN): case LB_CODE: op->offsz = op->val; op->code = LAB_CODE; freeops = 1; break; case UNARY_CODE(WPTR_TOKEN): case LW_CODE: op->offsz = op->val; op->code = LAW_CODE; freeops = 1; break; case SB_CODE: op->offsz = op->val; op->code = SAB_CODE; freeops = 1; break; case SW_CODE: op->offsz = op->val; op->code = SAW_CODE; freeops = 1; break; case BRFALSE_CODE: if (op->val) freeops = -2; // Remove constant and never taken branch else { op->code = BRNCH_CODE; // Always taken branch op->tag = opnext->tag; freeops = 1; } break; case BRTRUE_CODE: if (!op->val) freeops = -2; // Remove constant never taken branch else { op->code = BRNCH_CODE; // Always taken branch op->tag = opnext->tag; freeops = 1; } break; case NE_CODE: if (!op->val) freeops = -2; // Remove ZERO:ISNE break; case EQ_CODE: if (!op->val) { op->code = LOGIC_NOT_CODE; freeops = 1; } break; case CONST_CODE: // Collapse constant operation if ((opnextnext = opnext->nextop)) switch (opnextnext->code) { case BINARY_CODE(MUL_TOKEN): op->val *= opnext->val; freeops = 2; break; case BINARY_CODE(DIV_TOKEN): op->val /= opnext->val; freeops = 2; break; case BINARY_CODE(MOD_TOKEN): op->val %= opnext->val; freeops = 2; break; case BINARY_CODE(ADD_TOKEN): op->val += opnext->val; freeops = 2; break; case BINARY_CODE(SUB_TOKEN): op->val -= opnext->val; freeops = 2; break; case BINARY_CODE(SHL_TOKEN): op->val <<= opnext->val; freeops = 2; break; case BINARY_CODE(SHR_TOKEN): op->val >>= opnext->val; freeops = 2; break; case BINARY_CODE(AND_TOKEN): op->val &= opnext->val; freeops = 2; break; case BINARY_CODE(OR_TOKEN): op->val |= opnext->val; freeops = 2; break; case BINARY_CODE(EOR_TOKEN): op->val ^= opnext->val; freeops = 2; break; case BINARY_CODE(EQ_TOKEN): op->val = op->val == opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(NE_TOKEN): op->val = op->val != opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(GE_TOKEN): op->val = op->val >= opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(LT_TOKEN): op->val = op->val < opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(GT_TOKEN): op->val = op->val > opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(LE_TOKEN): op->val = op->val <= opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(LOGIC_OR_TOKEN): op->val = op->val || opnext->val ? 1 : 0; freeops = 2; break; case BINARY_CODE(LOGIC_AND_TOKEN): op->val = op->val && opnext->val ? 1 : 0; freeops = 2; break; } // End of collapse constant operation if ((pass > 0) && (freeops == 0) && (op->val != 0)) crunched = try_dupify(op); break; // CONST_CODE case BINARY_CODE(MUL_TOKEN): for (shiftcnt = 0; shiftcnt < 16; shiftcnt++) { if (op->val == (1 << shiftcnt)) { op->val = shiftcnt; opnext->code = BINARY_CODE(SHL_TOKEN); break; } } break; case BINARY_CODE(DIV_TOKEN): for (shiftcnt = 0; shiftcnt < 16; shiftcnt++) { if (op->val == (1 << shiftcnt)) { op->val = shiftcnt; opnext->code = BINARY_CODE(SHR_TOKEN); break; } } break; } break; // CONST_CODE case LADDR_CODE: switch (opnext->code) { case CONST_CODE: if ((opnextnext = opnext->nextop)) switch (opnextnext->code) { case ADD_CODE: case INDEXB_CODE: op->offsz += opnext->val; freeops = 2; break; case INDEXW_CODE: op->offsz += opnext->val * 2; freeops = 2; break; } break; case LB_CODE: op->code = LLB_CODE; freeops = 1; break; case LW_CODE: op->code = LLW_CODE; freeops = 1; break; case SB_CODE: op->code = SLB_CODE; freeops = 1; break; case SW_CODE: op->code = SLW_CODE; freeops = 1; break; } if ((pass > 0) && (freeops == 0)) crunched = try_dupify(op); break; // LADDR_CODE case GADDR_CODE: switch (opnext->code) { case CONST_CODE: if ((opnextnext = opnext->nextop)) switch (opnextnext->code) { case ADD_CODE: case INDEXB_CODE: op->offsz += opnext->val; freeops = 2; break; case INDEXW_CODE: op->offsz += opnext->val * 2; freeops = 2; break; } break; case LB_CODE: op->code = LAB_CODE; freeops = 1; break; case LW_CODE: op->code = LAW_CODE; freeops = 1; break; case SB_CODE: op->code = SAB_CODE; freeops = 1; break; case SW_CODE: op->code = SAW_CODE; freeops = 1; break; case ICAL_CODE: op->code = CALL_CODE; freeops = 1; break; } if ((pass > 0) && (freeops == 0)) crunched = try_dupify(op); break; // GADDR_CODE case LLB_CODE: if (pass > 0) crunched = try_dupify(op); break; // LLB_CODE case LLW_CODE: // LLW [n]:CB 8:SHR -> LLB [n+1] if ((opnext->code == CONST_CODE) && (opnext->val == 8)) { if ((opnextnext = opnext->nextop)) { if (opnextnext->code == SHR_CODE) { op->code = LLB_CODE; op->offsz++; freeops = 2; break; } } } if ((pass > 0) && (freeops == 0)) crunched = try_dupify(op); break; // LLW_CODE case LAB_CODE: if ((pass > 0) && (op->type || !is_hardware_address(op->offsz))) crunched = try_dupify(op); break; // LAB_CODE case LAW_CODE: // LAW x:CB 8:SHR -> LAB x+1 if ((opnext->code == CONST_CODE) && (opnext->val == 8)) { if ((opnextnext = opnext->nextop)) { if (opnextnext->code == SHR_CODE) { op->code = LAB_CODE; op->offsz++; freeops = 2; break; } } } if ((pass > 0) && (freeops == 0) && (op->type || !is_hardware_address(op->offsz))) crunched = try_dupify(op); break; // LAW_CODE case LOGIC_NOT_CODE: switch (opnext->code) { case BRFALSE_CODE: op->code = BRTRUE_CODE; op->tag = opnext->tag; freeops = 1; break; case BRTRUE_CODE: op->code = BRFALSE_CODE; op->tag = opnext->tag; freeops = 1; break; } break; // LOGIC_NOT_CODE case SLB_CODE: if ((opnext->code == LLB_CODE) && (op->offsz == opnext->offsz)) { op->code = DLB_CODE; freeops = 1; } break; // SLB_CODE case SLW_CODE: if ((opnext->code == LLW_CODE) && (op->offsz == opnext->offsz)) { op->code = DLW_CODE; freeops = 1; } break; // SLW_CODE case SAB_CODE: if ((opnext->code == LAB_CODE) && (op->tag == opnext->tag) && (op->offsz == opnext->offsz) && (op->type == opnext->type)) { op->code = DAB_CODE; freeops = 1; } break; // SAB_CODE case SAW_CODE: if ((opnext->code == LAW_CODE) && (op->tag == opnext->tag) && (op->offsz == opnext->offsz) && (op->type == opnext->type)) { op->code = DAW_CODE; freeops = 1; } break; // SAW_CODE } // // Free up crunched ops. If freeops is positive we free up that many ops // *after* op; if it's negative, we free up abs(freeops) ops *starting // with* op. if (freeops < 0) { freeops = -freeops; // If op is at the start of the sequence, we treat this as a special // case. if (op == *seq) { for (; freeops > 0; --freeops) { opnext = op->nextop; release_op(op); op = *seq = opnext; } crunched = 1; } // Otherwise we just move op back to point to the previous op and // let the following loop remove the required number of ops. else { op = opprev; opnext = op->nextop; } } while (freeops) { op->nextop = opnext->nextop; opnext->nextop = freeop_lst; freeop_lst = opnext; opnext = op->nextop; crunched = 1; freeops--; } opprev = op; op = opnext; } return (crunched); } /* * Generate a sequence of code */ t_opseq *gen_seq(t_opseq *seq, int opcode, long cval, int tag, int offsz, int type) { t_opseq *op; if (!seq) { op = seq = new_op(); } else { op = seq; while (op->nextop) op = op->nextop; op->nextop = new_op(); op = op->nextop; } op->code = opcode; op->val = cval; op->tag = tag; op->offsz = offsz; op->type = type; return (seq); } /* * Append one sequence to the end of another */ t_opseq *cat_seq(t_opseq *seq1, t_opseq *seq2) { t_opseq *op; if (!seq1) return (seq2); for (op = seq1; op->nextop; op = op->nextop); op->nextop = seq2; return (seq1); } /* * Emit a sequence of ops (into the pending sequence) */ int emit_seq(t_opseq *seq) { t_opseq *op; int emitted = 0; int string = 0; for (op = seq; op; op = op->nextop) { if (op->code == STR_CODE) string = 1; emitted++; } pending_seq = cat_seq(pending_seq, seq); // The source code comments in the output are much more logical if we don't // merge multiple sequences together. There's no value in doing this merging // if we're not optimizing, and we optionally allow it to be prevented even // when we are optimizing by specifing the -N (NO_COMBINE) flag. // // We must also force output if the sequence includes a CS opcode, as the // associated 'constant' is only temporarily valid. if (!(outflags & OPTIMIZE) || (outflags & NO_COMBINE) || string) return emit_pending_seq(); return (emitted); } /* * Emit the pending sequence */ int emit_pending_seq() { // This is called by some of the emit_*() functions to ensure that any // pending ops are emitted before they emit their own op when they are // called from the parser. However, this function itself calls some of those // emit_*() functions to emit instructions from the pending sequence, which // would cause an infinite loop if we weren't careful. We therefore set // pending_seq to null on entry and work with a local copy, so if this // function calls back into itself it is a no-op. if (!pending_seq) return 0; t_opseq *local_pending_seq = pending_seq; pending_seq = 0; t_opseq *op; int emitted = 0; if (outflags & OPTIMIZE) { int pass; for (pass = 0; pass < 2; pass++) while (crunch_seq(&local_pending_seq, pass)); } while (local_pending_seq) { op = local_pending_seq; switch (op->code) { case NEG_CODE: case COMP_CODE: case LOGIC_NOT_CODE: case INC_CODE: case DEC_CODE: case BPTR_CODE: case WPTR_CODE: emit_unaryop(op->code); break; case MUL_CODE: case DIV_CODE: case MOD_CODE: case ADD_CODE: case SUB_CODE: case SHL_CODE: case SHR_CODE: case AND_CODE: case OR_CODE: case EOR_CODE: case EQ_CODE: case NE_CODE: case GE_CODE: case LT_CODE: case GT_CODE: case LE_CODE: case LOGIC_OR_CODE: case LOGIC_AND_CODE: emit_op(op->code); break; case CONST_CODE: emit_const(op->val); break; case STR_CODE: emit_conststr(op->val); break; case LB_CODE: emit_lb(); break; case LW_CODE: emit_lw(); break; case LLB_CODE: emit_llb(op->offsz); break; case LLW_CODE: emit_llw(op->offsz); break; case LAB_CODE: emit_lab(op->tag, op->offsz, op->type); break; case LAW_CODE: emit_law(op->tag, op->offsz, op->type); break; case SB_CODE: emit_sb(); break; case SW_CODE: emit_sw(); break; case SLB_CODE: emit_slb(op->offsz); break; case SLW_CODE: emit_slw(op->offsz); break; case DLB_CODE: emit_dlb(op->offsz); break; case DLW_CODE: emit_dlw(op->offsz); break; case SAB_CODE: emit_sab(op->tag, op->offsz, op->type); break; case SAW_CODE: emit_saw(op->tag, op->offsz, op->type); break; case DAB_CODE: emit_dab(op->tag, op->offsz, op->type); break; case DAW_CODE: emit_daw(op->tag, op->offsz, op->type); break; case CALL_CODE: emit_call(op->tag, op->type); break; case ICAL_CODE: emit_ical(); break; case LADDR_CODE: emit_localaddr(op->offsz); break; case GADDR_CODE: emit_globaladdr(op->tag, op->offsz, op->type); break; case INDEXB_CODE: emit_indexbyte(); break; case INDEXW_CODE: emit_indexword(); break; case DROP_CODE: emit_drop(); break; case DUP_CODE: emit_dup(); break; case BRNCH_CODE: emit_brnch(op->tag); break; case BRFALSE_CODE: emit_brfls(op->tag); break; case BRTRUE_CODE: emit_brtru(op->tag); break; case CODETAG_CODE: printf("_B%03d%c\n", op->tag, LBL); break; case NOP_CODE: break; default: return (0); } emitted++; local_pending_seq = local_pending_seq->nextop; /* * Free this op */ op->nextop = freeop_lst; freeop_lst = op; } return (emitted); }