From: James Hammons Date: Mon, 26 Dec 2011 22:50:27 +0000 (+0000) Subject: Initial commit. X-Git-Tag: 1.3.0~46 X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=rmac;a=commitdiff_plain;h=d28f432296e812643e236d1bfc9b556a7b11c461 Initial commit. --- d28f432296e812643e236d1bfc9b556a7b11c461 diff --git a/68kgen.c b/68kgen.c new file mode 100644 index 0000000..eb4df1a --- /dev/null +++ b/68kgen.c @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// 68KGEN.C - Tool to Generate 68000 Opcode Table +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include +#include +#include + +#define EOS '\0' + +int kwnum = 1; /* current op# for kwgen output */ + +FILE *kfp; /* keyword file */ + +int lineno = 0; + +void error(char *, char *); +void procln(int, char **); + +void main(int argc, char **argv) { + char *namv[256]; + char *s; + int namcnt; + char ln[256]; + + if (argc == 2) + if ((kfp = fopen(argv[1], "w")) == NULL) + error("Cannot create: %s", argv[1]); + + while (gets(ln) != NULL) + { + ++lineno; /* bump line# */ + if (*ln == '#') /* ignore comments */ + continue; + + /* + * Tokenize line (like the way "argc, argv" works) + * and pass it to the parser. + */ + namcnt = 0; + s = ln; + while (*s) + if (isspace(*s)) + ++s; + else + { + namv[namcnt++] = s; + while (*s && !isspace(*s)) + ++s; + if (isspace(*s)) + *s++ = EOS; + } + + if (namcnt) + procln(namcnt, namv); + } +} + + +/* + * Parse line + */ +void procln(int namc, char **namv) { + int i, j; + char *s; + + if (namc == 1) /* alias for previous entry */ + { + fprintf(kfp, "%s\t%d\n", namv[0], kwnum-1+1000); + return; + } + + if (namc < 5) + { + fprintf(stderr, "%d: missing fields\n", lineno); + exit(1); + } + + if (*namv[0] != '-') /* output keyword name */ + fprintf(kfp, "%s\t%d\n", namv[0], kwnum + 1000); + + printf("/*%4d %-6s*/ {", kwnum, namv[0]); + + if (*namv[1] == '!') + printf("CGSPECIAL"); + else for (s = namv[1], i=0; *s; ++s) + printf("%sSIZ%c", (i++ ? "|" : ""), *s); + printf(", %s, %s, ", namv[2], namv[3]); + + if (*namv[4] == '%') /* enforce little fascist percent signs */ + { + for (i=1, j=0; i < 17; ++i) + { + j <<= 1; + if (namv[4][i] == '1' || + isupper(namv[4][i])) + ++j; + } + printf("0x%04x, ", j); + } + else printf("%s, ", namv[4]); + + if (namc == 7 && + *namv[6] == '+') + printf("%d, ", kwnum+1); + else printf("0, "); + + printf("%s},\n", namv[5]); + + ++kwnum; +} + +void error(char *s, char *s1) { + fprintf(stderr, s, s1); + fprintf(stderr, "\n"); + exit(1); +} + diff --git a/68kmn b/68kmn new file mode 100644 index 0000000..e82767a --- /dev/null +++ b/68kmn @@ -0,0 +1,132 @@ +abcd 1001 +add 1003 +adda 1005 +addi 1006 +addq 1007 +addx 1008 +and 1010 +andi 1012 +asl 1015 +asr 1019 +bcc 1023 +bhs 1023 +bcs 1024 +blo 1024 +beq 1025 +bze 1025 +bz 1025 +bge 1026 +bgt 1027 +bhi 1028 +ble 1029 +bls 1030 +blt 1031 +bmi 1032 +bne 1033 +bnz 1033 +bpl 1034 +bvc 1035 +bvs 1036 +bchg 1037 +bclr 1041 +bra 1045 +bt 1045 +bset 1046 +bsr 1050 +btst 1051 +chk 1055 +clr 1056 +cmp 1058 +cmpa 1060 +cmpi 1061 +cmpm 1062 +dbcc 1063 +dbcs 1064 +dblo 1064 +dbeq 1065 +dbze 1065 +dbf 1066 +dbra 1066 +dbge 1067 +dbgt 1068 +dbhi 1069 +dbhs 1069 +dble 1070 +dbls 1071 +dblt 1072 +dbmi 1073 +dbne 1074 +dbnz 1074 +dbpl 1075 +dbt 1076 +dbvc 1077 +dbvs 1078 +divs 1079 +divu 1080 +eor 1081 +eori 1082 +exg 1085 +ext 1086 +illegal 1088 +jmp 1089 +jsr 1090 +lea 1091 +link 1092 +lsl 1093 +lsr 1097 +move 1101 +movea 1108 +movem 1109 +movep 1110 +moveq 1112 +muls 1113 +mulu 1114 +nbcd 1115 +neg 1116 +negx 1117 +nop 1118 +not 1119 +or 1120 +ori 1122 +pea 1125 +reset 1126 +rol 1127 +ror 1131 +roxl 1135 +roxr 1139 +rte 1143 +rtr 1144 +rts 1145 +sbcd 1146 +scc 1148 +shs 1148 +scs 1149 +slo 1149 +seq 1150 +sze 1150 +sf 1151 +sge 1152 +sgt 1153 +shi 1154 +sle 1155 +sls 1156 +slt 1157 +smi 1158 +sne 1159 +snz 1159 +spl 1160 +st 1161 +svc 1162 +svs 1163 +stop 1164 +sub 1165 +suba 1167 +subi 1168 +subq 1169 +subx 1170 +swap 1172 +tas 1173 +trap 1174 +trapv 1175 +tst 1176 +unlk 1177 diff --git a/68ktab b/68ktab new file mode 100644 index 0000000..1427a03 --- /dev/null +++ b/68ktab @@ -0,0 +1,242 @@ +abcd NB M_DREG M_DREG %1100rrr100000rrr m_abcd + +- NB M_APREDEC M_APREDEC %1100rrr100001rrr m_abcd + +add NBWL C_ALL M_DREG %1101rrr0sseR1100 m_ea + +- NBWL M_DREG C_ALTMEM %1101rrr1sseR0101 m_ea + +adda NWL C_ALL M_AREG %1101rrrs11eeeeee m_adda + +addi NWBL M_IMMED C_ALTDATA %00000110sseeeS11 m_ea + +addq NBWL M_IMMED C_ALT %0101ddd0sseeeeee m_addq + +addx NBWL M_DREG M_DREG %1101rrr1ss000rrS m_abcd + +- NBWL M_APREDEC M_APREDEC %1101rrr1ss001rrS m_abcd + +and NBWL C_DATA M_DREG %1100rrr0sseR1S00 m_ea + +- NBWL M_DREG C_ALTMEM %1100rrr1sseR0S01 m_ea + +andi NBWL M_IMMED C_ALTDATA %00000010sseeeS11 m_ea + +- NB M_IMMED M_AM_CCR %0000001000111100 m_imm8 + +- NW M_IMMED M_AM_SR %0000001001111100 m_imm + +asl NBWL M_DREG M_DREG %1110rrr1ss100rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc1ss000rrr m_shi + +- NBWL M_DREG M_AM_NONE %11100011ss000rrS m_reg + +- NW C_ALTMEM M_AM_NONE %1110000111eee000 m_ea + +asr NBWL M_DREG M_DREG %1110rrr0ss100rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc0ss000rrr m_shi + +- NBWL M_DREG M_AM_NONE %11100010ss000rrS m_reg + +- NW C_ALTMEM M_AM_NONE %1110000011eee000 m_ea + +bcc NBW C_LABEL M_AM_NONE %01100100bbbbbbbb m_br +bhs +bcs NBW C_LABEL M_AM_NONE %01100101bbbbbbbb m_br +blo +beq NBW C_LABEL M_AM_NONE %01100111bbbbbbbb m_br +bze +bz +bge NBW C_LABEL M_AM_NONE %01101100bbbbbbbb m_br +bgt NBW C_LABEL M_AM_NONE %01101110bbbbbbbb m_br +bhi NBW C_LABEL M_AM_NONE %01100010bbbbbbbb m_br +ble NBW C_LABEL M_AM_NONE %01101111bbbbbbbb m_br +bls NBW C_LABEL M_AM_NONE %01100011bbbbbbbb m_br +blt NBW C_LABEL M_AM_NONE %01101101bbbbbbbb m_br +bmi NBW C_LABEL M_AM_NONE %01101011bbbbbbbb m_br +bne NBW C_LABEL M_AM_NONE %01100110bbbbbbbb m_br +bnz +bpl NBW C_LABEL M_AM_NONE %01101010bbbbbbbb m_br +bvc NBW C_LABEL M_AM_NONE %01101000bbbbbbbb m_br +bvs NBW C_LABEL M_AM_NONE %01101001bbbbbbbb m_br + +bchg NL M_DREG M_DREG %0000rrr101eeeeee m_bitop + +- NB M_DREG C_ALTDATA %0000rrr101eeeeee m_bitop + +- NL M_IMMED M_DREG %0000100001eeeeee m_bitop + +- NB M_IMMED C_ALTDATA %0000100001eeeeee m_bitop + +bclr NL M_DREG M_DREG %0000rrr110eeeeee m_bitop + +- NB M_DREG C_ALTDATA %0000rrr110eeeeee m_bitop + +- NL M_IMMED M_DREG %0000100010eeeeee m_bitop + +- NB M_IMMED C_ALTDATA %0000100010eeeeee m_bitop + +bra NBW C_LABEL M_AM_NONE %01100000bbbbbbbb m_br +bt + +bset NL M_DREG M_DREG %0000rrr111eeeeee m_bitop + +- NB M_DREG C_ALTDATA %0000rrr111eeeeee m_bitop + +- NL M_IMMED M_DREG %0000100011eeeeee m_bitop + +- NB M_IMMED C_ALTDATA %0000100011eeeeee m_bitop + +bsr NBW C_LABEL M_AM_NONE %01100001bbbbbbbb m_br + +btst NL M_DREG M_DREG %0000rrr100eeeeee m_bitop + +- NB M_DREG C_DATA %0000rrr100eeeeee m_bitop + +- NL M_IMMED M_DREG %0000100000eeeeee m_bitop + +- NB M_IMMED C_DATA-M_IMMED %0000100000eeeeee m_bitop + +chk NW C_DATA M_DREG %0100rrr110eR1000 m_ea + +clr NBWL C_ALTDATA M_AM_NONE %01000010sseeeS00 m_ea + +- NWL M_AREG M_AM_NONE %1001rrrs11001rrr m_clra + +cmp NWL M_AREG M_DREG %1011rrr0sseR1S00 m_ea + +- NBWL C_ALL M_DREG %1011rrr0sseR1S00 m_ea + +cmpa NWL C_ALL M_AREG %1011rrrs11eeeeee m_adda + +cmpi NBWL M_IMMED C_ALTDATA %00001100sseeeS11 m_ea + +cmpm NBWL M_APOSTINC M_APOSTINC %1011xxx1ss001yRS m_reg + +dbcc NW M_DREG C_LABEL %0101010011001rrr m_dbra +dbcs NW M_DREG C_LABEL %0101010111001rrr m_dbra +dblo +dbeq NW M_DREG C_LABEL %0101011111001rrr m_dbra +dbze +dbf NW M_DREG C_LABEL %0101000111001rrr m_dbra +dbra +dbge NW M_DREG C_LABEL %0101110011001rrr m_dbra +dbgt NW M_DREG C_LABEL %0101111011001rrr m_dbra +dbhi NW M_DREG C_LABEL %0101001011001rrr m_dbra +dbhs +dble NW M_DREG C_LABEL %0101111111001rrr m_dbra +dbls NW M_DREG C_LABEL %0101001111001rrr m_dbra +dblt NW M_DREG C_LABEL %0101110111001rrr m_dbra +dbmi NW M_DREG C_LABEL %0101101111001rrr m_dbra +dbne NW M_DREG C_LABEL %0101011011001rrr m_dbra +dbnz +dbpl NW M_DREG C_LABEL %0101101011001rrr m_dbra +dbt NW M_DREG C_LABEL %0101000011001rrr m_dbra +dbvc NW M_DREG C_LABEL %0101100011001rrr m_dbra +dbvs NW M_DREG C_LABEL %0101100111001rrr m_dbra + +divs NW C_DATA M_DREG %1000rrr111eR1000 m_ea + +divu NW C_DATA M_DREG %1000rrr011eR1000 m_ea + +eor NBWL M_DREG C_ALTDATA %1011rrr1sseR0S01 m_ea + +eori NBWL M_IMMED C_ALTDATA %00001010sseeeS11 m_ea + +- NB M_IMMED M_AM_CCR %0000101000111100 m_imm8 + +- NW M_IMMED M_AM_SR %0000101001111100 m_imm + +exg NL M_DREG|M_AREG M_DREG|M_AREG %1100rrr1ooooorrr m_exg + +ext NW M_DREG M_AM_NONE %0100100010000rrr m_reg + +- L M_DREG M_AM_NONE %0100100011000rrr m_reg + +illegal N M_AM_NONE M_AM_NONE %0100101011111100 m_self + +jmp N C_CTRL M_AM_NONE %0100111011eee000 m_ea + +jsr N C_CTRL M_AM_NONE %0100111010eee000 m_ea + +lea NL C_CTRL M_AREG %0100rrr111eR1000 m_ea + +link N M_AREG M_IMMED %0100111001010rrr m_link + +lsl NBWL M_DREG M_DREG %1110rrr1ss101rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc1ss001rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110001111eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100011ss001rrS m_reg + +lsr NBWL M_DREG M_DREG %1110rrr0ss101rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc0ss001rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110001011eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100010ss001rrS m_reg + +move NBWL C_ALL C_ALTDATA %00ssddddddssssss m_move + +- NWL C_ALL M_AREG %00ssddd001ssssss m_move + +- NW C_DATA M_AM_SR %0100011011sss000 m_ea + +- NW M_AM_SR C_ALTDATA %0100000011ddd001 m_ea + +- NW C_DATA M_AM_CCR %0100010011sss000 m_ea + +- NL M_AM_USP M_AREG %0100111001101rrr m_usp + +- NL M_AREG M_AM_USP %0100111001100rrr m_usp + +movea NWL C_ALL M_AREG %00ssddd001ssssss m_move + +movem ! M_AM_NONE M_AM_NONE %01001d001seeeeee m_movem + +movep NWL M_DREG M_AIND|M_ADISP %0000rrr11s001aaa m_movep + +- NWL M_AIND|M_ADISP M_DREG %0000rrr10s001aaa m_movep + +moveq NL M_IMMED M_DREG %0111rrr0dddddddd m_moveq + +muls NW C_DATA M_DREG %1100rrr111eR1000 m_ea +mulu NW C_DATA M_DREG %1100rrr011eR1000 m_ea +nbcd NB C_ALTDATA M_AM_NONE %0100100000eee000 m_ea +neg NBWL C_ALTDATA M_AM_NONE %01000100sseeeS00 m_ea +negx NBWL C_ALTDATA M_AM_NONE %01000000sseeeS00 m_ea +nop N M_AM_NONE M_AM_NONE 0x4e71 m_self +not NBWL C_ALTDATA M_AM_NONE %01000110sseee100 m_ea + +or NBWL C_DATA M_DREG %1000rrr0sseR1S00 m_ea + +- NBWL M_DREG C_MEM %1000rrr1sseR0S01 m_ea + +ori NBWL M_IMMED C_ALTDATA %00000000sseeeS11 m_ea + +- NB M_IMMED M_AM_CCR %0000000000111100 m_imm8 + +- NW M_IMMED M_AM_SR %0000000001111100 m_imm + +pea NL C_CTRL M_AM_NONE %0100100001eee000 m_ea + +reset N M_AM_NONE M_AM_NONE 0x4e70 m_self + +rol NBWL M_DREG M_DREG %1110rrr1ss111rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc1ss011rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110011111eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100011ss011rrS m_reg + +ror NBWL M_DREG M_DREG %1110rrr0ss111rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc0ss011rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110011011eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100010ss011rrS m_reg + +roxl NBWL M_DREG M_DREG %1110rrr1ss110rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc1ss010rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110010111eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100011ss010rrS m_reg + +roxr NBWL M_DREG M_DREG %1110rrr0ss110rrr m_shr + +- NBWL M_IMMED M_DREG %1110ccc0ss010rrr m_shi + +- NBWL C_ALTMEM M_AM_NONE %1110010011eee000 m_ea + +- NBWL M_DREG M_AM_NONE %11100010ss010rrS m_reg + +rte N M_AM_NONE M_AM_NONE 0x4e73 m_self +rtr N M_AM_NONE M_AM_NONE 0x4e77 m_self +rts N M_AM_NONE M_AM_NONE 0x4e75 m_self + +sbcd NB M_DREG M_DREG %1000rrr100000rrr m_abcd + +- NB M_APREDEC M_APREDEC %1000rrr100001rrr m_abcd + +scc NB C_ALTDATA M_AM_NONE %0101010011eee000 m_ea +shs +scs NB C_ALTDATA M_AM_NONE %0101010111eee000 m_ea +slo +seq NB C_ALTDATA M_AM_NONE %0101011111eee000 m_ea +sze +sf NB C_ALTDATA M_AM_NONE %0101000111eee000 m_ea +sge NB C_ALTDATA M_AM_NONE %0101110011eee000 m_ea +sgt NB C_ALTDATA M_AM_NONE %0101111011eee000 m_ea +shi NB C_ALTDATA M_AM_NONE %0101001011eee000 m_ea +sle NB C_ALTDATA M_AM_NONE %0101111111eee000 m_ea +sls NB C_ALTDATA M_AM_NONE %0101001111eee000 m_ea +slt NB C_ALTDATA M_AM_NONE %0101110111eee000 m_ea +smi NB C_ALTDATA M_AM_NONE %0101101111eee000 m_ea +sne NB C_ALTDATA M_AM_NONE %0101011011eee000 m_ea +snz +spl NB C_ALTDATA M_AM_NONE %0101101011eee000 m_ea +st NB C_ALTDATA M_AM_NONE %0101000011eee000 m_ea +svc NB C_ALTDATA M_AM_NONE %0101100011eee000 m_ea +svs NB C_ALTDATA M_AM_NONE %0101100111eee000 m_ea + +stop N M_IMMED M_AM_NONE %0100111001110010 m_imm + +sub NBWL C_ALL M_DREG %1001rrr0sseR1S00 m_ea + +- NBWL M_DREG C_ALTMEM %1001rrr1sseR0S01 m_ea + +suba NWL C_ALL M_AREG %1001rrrs11eeeeee m_adda + +subi NBWL M_IMMED C_ALTDATA %00000100sseeeS11 m_ea + +subq NBWL M_IMMED C_ALT %0101ddd1sseeeeee m_addq + +subx NBWL M_DREG M_DREG %1001xxx1ss000yyS m_abcd + +- NBWL M_APREDEC M_APREDEC %1001xxx1ss001yyS m_abcd + +swap NW M_DREG M_AM_NONE %0100100001000rrr m_reg +tas NB C_ALTDATA M_AM_NONE %0100101011eee000 m_ea +trap N M_IMMED M_AM_NONE %010011100100vvvv m_trap +trapv N M_AM_NONE M_AM_NONE 0x4e76 m_self +tst NBWL C_ALTDATA M_AM_NONE %01001010sseeeS00 m_ea +unlk N M_AREG M_AM_NONE %0100111001011rrr m_reg diff --git a/amode.c b/amode.c new file mode 100644 index 0000000..93e31d3 --- /dev/null +++ b/amode.c @@ -0,0 +1,160 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// AMODE.C - Addressing Modes +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "amode.h" +#include "error.h" +#include "token.h" +#include "expr.h" +#include "rmac.h" + +#define DEF_KW +#include "kwtab.h" +#define DEF_MN +#include "mntab.h" + +// Address-mode information +int nmodes; // Number of addr'ing modes found +int am0; // Addressing mode +int a0reg; // Register +TOKEN a0expr[EXPRSIZE]; // Expression +VALUE a0exval; // Expression's value +WORD a0exattr; // Expression's attribute +int a0ixreg; // Index register +int a0ixsiz; // Index register size (and scale) +TOKEN a0oexpr[EXPRSIZE]; // Outer displacement expression +VALUE a0oexval; // Outer displacement value +WORD a0oexattr; // Outer displacement attribute +SYM *a0esym; // External symbol involved in expr + +int am1; // Addressing mode +int a1reg; // Register +TOKEN a1expr[EXPRSIZE]; // Expression +VALUE a1exval; // Expression's value +WORD a1exattr; // Expression's attribute +int a1ixreg; // Index register +int a1ixsiz; // Index register size (and scale) +TOKEN a1oexpr[EXPRSIZE]; // Outer displacement expression +VALUE a1oexval; // Outer displacement value +WORD a1oexattr; // Outer displacement attribute +SYM *a1esym; // External symbol involved in expr + +// +// --- Parse Addressing Mode ----------------------------------------------------------------------- +// + +int amode(int acount) { + // Initialize global return values + nmodes = a0reg = a1reg = 0; + am0 = am1 = AM_NONE; + a0expr[0] = a0oexpr[0] = a1expr[0] = a1oexpr[0] = ENDEXPR; + a0exattr = a0oexattr = a1exattr = a1oexattr = 0; + a0esym = a1esym = (SYM *)NULL; + + // If at EOL, then no addr modes at all + if(*tok == EOL) + return(0); + + // Parse first addressing mode + #define AnOK a0ok + #define AMn am0 + #define AnREG a0reg + #define AnIXREG a0ixreg + #define AnIXSIZ a0ixsiz + #define AnEXPR a0expr + #define AnEXVAL a0exval + #define AnEXATTR a0exattr + #define AnOEXPR a0oexpr + #define AnOEXVAL a0oexval + #define AnOEXATTR a0oexattr + #define AnESYM a0esym + #define AMn_IX0 am0_ix0 + #define AMn_IXN am0_ixn + #include "parmode.h" + + // If caller wants only one mode, return just one (ignore comma); + // If there is no second addressing mode (no comma), then return just one anyway. + nmodes = 1; + if(acount == 0 || *tok != ',') + return(1); + ++tok; // Eat comma + + // Parse second addressing mode + #define AnOK a1ok + #define AMn am1 + #define AnREG a1reg + #define AnIXREG a1ixreg + #define AnIXSIZ a1ixsiz + #define AnEXPR a1expr + #define AnEXVAL a1exval + #define AnEXATTR a1exattr + #define AnOEXPR a1oexpr + #define AnOEXVAL a1oexval + #define AnOEXATTR a1oexattr + #define AnESYM a1esym + #define AMn_IX0 am1_ix0 + #define AMn_IXN am1_ixn + #include "parmode.h" + + nmodes = 2; + return(2); + + // Error messages: + badmode: + + return(error("addressing mode syntax")); + + unmode: + + return(error("unimplemented addressing mode")); +} + +// +// --- Parse Register List ------------------------------------------------------------------------- +// + +int reglist(WORD *a_rmask) { + static WORD msktab[] = { + 0x0001, 0x0002, 0x0004, 0x0008, + 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, + 0x1000, 0x2000, 0x4000, 0x8000 + }; + WORD rmask; + int r, cnt; + + rmask = 0; + for(;;) { + if(*tok >= KW_D0 && *tok <= KW_A7) + r = *tok++ & 15; + else break; + + if(*tok == '-') { + ++tok; + if(*tok >= KW_D0 && *tok <= KW_A7) + cnt = *tok++ & 15; + else + return(error("register list syntax")); + + if(cnt < r) + return(error("register list order")); + cnt -= r; + } else + cnt = 0; + + while(cnt-- >= 0) + rmask |= msktab[r++]; + if(*tok != '/') + break; + ++tok; + } + + *a_rmask = rmask; + + return(OK); +} + + diff --git a/amode.h b/amode.h new file mode 100644 index 0000000..c7bd6fe --- /dev/null +++ b/amode.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// AMODE.H - Addressing Modes +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __AMODE_H__ +#define __AMODE_H__ + +#include "rmac.h" + +// 68000 and 68020 addressing modes +#define DREG 000 // Dn +#define AREG 010 // An +#define AIND 020 // (An) +#define APOSTINC 030 // (An)+ +#define APREDEC 040 // -(An) +#define ADISP 050 // (d16,An) d16(An) +#define AINDEXED 060 // (d8,An,Xn) d8(An,Xn) +#define ABSW 070 // xxx.W +#define ABSL 071 // xxx or xxx.L +#define PCDISP 072 // (d16,PC) d16(PC) +#define PCINDEXED 073 // (d16,PC,Xn) d16(PC,Xn) +#define IMMED 074 // #data +#define ABASE 0100 // (bd,An,Xn) +#define MEMPOST 0101 // ([bd,An],Xn,od) +#define MEMPRE 0102 // ([bc,An,Xn],od) +#define PCBASE 0103 // (bd,PC,Xn) +#define PCMPOST 0104 // ([bd,PC],Xn,od) +#define PCMPRE 0105 // ([bc,PC,Xn],od) +#define AM_USP 0106 +#define AM_SR 0107 +#define AM_CCR 0110 +#define AM_NONE 0111 // Nothing + +// Addressing-mode masks +#define M_DREG 0x00000001L // Dn +#define M_AREG 0x00000002L // An +#define M_AIND 0x00000004L // (An) +#define M_APOSTINC 0x00000008L // (An)+ +#define M_APREDEC 0x00000010L // -(An) +#define M_ADISP 0x00000020L // (d16,An) d16(An) +#define M_AINDEXED 0x00000040L // (d8,An,Xn) d8(An,Xn) +#define M_ABSW 0x00000080L // xxx.W +#define M_ABSL 0x00000100L // xxx or xxx.L +#define M_PCDISP 0x00000200L // (d16,PC) d16(PC) +#define M_PCINDEXED 0x00000400L // (d16,PC,Xn) d16(PC,Xn) +#define M_IMMED 0x00000800L // #data +#define M_ABASE 0x00001000L // (bd,An,Xn) +#define M_MEMPOST 0x00002000L // ([bd,An],Xn,od) +#define M_MEMPRE 0x00004000L // ([bc,An,Xn],od) +#define M_PCBASE 0x00008000L // (bd,PC,Xn) +#define M_PCMPOST 0x00010000L // ([bd,PC],Xn,od) +#define M_PCMPRE 0x00020000L // ([bc,PC,Xn],od) +#define M_AM_USP 0x00040000L // USP +#define M_AM_SR 0x00080000L // SR +#define M_AM_CCR 0x00100000L // CCR +#define M_AM_NONE 0x00200000L // (nothing) + +// Addr mode categories +#define C_ALL 0x00000fffL +#define C_DATA 0x00000ffdL +#define C_MEM 0x00000ffcL +#define C_CTRL 0x000007e4L +#define C_ALT 0x000001ffL + +#define C_ALTDATA (C_DATA&C_ALT) +#define C_ALTMEM (C_MEM&C_ALT) +#define C_ALTCTRL (C_CTRL&C_ALT) +#define C_LABEL (M_ABSW|M_ABSL) +#define C_NONE M_AM_NONE + +// Scales +#define TIMES1 00000 // (empty or *1) +#define TIMES2 01000 // *2 +#define TIMES4 02000 // *4 +#define TIMES8 03000 // *8 + +#define EXPRSIZE 128 // Maximum #tokens in an expression + +// Addressing mode variables, output of amode() +extern int nmodes; +extern int am0, am1; +extern int a0reg, a1reg; +extern TOKEN a0expr[], a1expr[]; +extern VALUE a0exval, a1exval; +extern WORD a0exattr, a1exattr; +extern int a0ixreg, a1ixreg; +extern int a0ixsiz, a1ixsiz; +extern TOKEN a0oexpr[], a1oexpr[]; +extern VALUE a0oexval, a1oexval; +extern WORD a0oexattr, a1oexattr; +extern SYM *a0esym, *a1esym; + +// Mnemonic table structure +#define MNTAB struct _mntab +MNTAB { + WORD mnattr; // Attributes (CGSPECIAL, SIZN, ...) + LONG mn0, mn1; // Addressing modes + WORD mninst; // Instruction mask + WORD mncont; // Continuation (or -1) + int (*mnfunc)(); // Mnemonic builder +}; + +// mnattr: +#define CGSPECIAL 0x8000 // Special (don't parse addr modes) + +// Prototypes +int amode(int); +int reglist(WORD *); + +#endif // __AMODE_H__ \ No newline at end of file diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..8c2a8d9 --- /dev/null +++ b/debug.c @@ -0,0 +1,305 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// DEBUG.C - Debugging Messages +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "debug.h" +#include "sect.h" +#include "amode.h" +#include "mark.h" + +static int siztab[4] = {3, 5, 9, 9}; + +// +// --- Print 'c' Visibly --------------------------------------------------------------------------- +// + +int visprt(char c) { + if(c < 0x20 || c >= 0x7f) + putchar('.'); + else + putchar(c); + + return(0); +} + +// +// --- Print expression, return ptr to just past the ENDEXPR --------------------------------------- +// + +TOKEN *printexpr(TOKEN *tp) { + if(tp != NULL) + while(*tp != ENDEXPR) + switch((int)*tp++) { + case SYMBOL: + printf("`%s' ", ((SYM *)*tp)->sname); + ++tp; + break; + case CONST: + printf("$%lx ", *tp++); + break; + case ACONST: + printf("ACONST=($%lx,$%lx) ", *tp, tp[1]); + tp += 2; + break; + default: + printf("%c ", (char)tp[-1]); + break; + } + printf(";\n"); + return(tp + 1); +} + +// +// --- Dump data in a chunk (and maybe others) in the appropriate format --------------------------- +// + +int chdump(CHUNK *ch, int format) { + while(ch != NULL) { + printf("chloc=$%08lx, chsize=$%lx\n", ch->chloc, ch->ch_size); + mdump(ch->chptr, ch->ch_size, format, ch->chloc); + ch = ch->chnext; + } + + return(0); +} + +// +// --- Dump fixup records in printable format ------------------------------------------------------ +// + +int fudump(CHUNK *ch) { + PTR p; + char *ep; + WORD attr, esiz; + WORD line, file; + LONG loc; + + for(; ch != NULL;) { + p.cp = ch->chptr; + ep = ch->chptr + ch->ch_size; + while(p.cp < ep) { + attr = *p.wp++; + loc = *p.lp++; + file = *p.wp++; + line = *p.wp++; + + printf("$%04x $%08lx %d.%d: ", (int)attr, loc, (int)file, (int)line); + + if(attr & FU_EXPR) { + esiz = *p.wp++; + printf("(%d long) ", (int)esiz); + p.tk = printexpr(p.tk); + } else { + printf("`%s' ;\n", (*p.sy)->sname); + ++p.lp; + } + } + ch = ch->chnext; + } + + return(0); +} + +// +// --- Dump marks ---------------------------------------------------------------------------------- +// + +int mudump(void) { + MCHUNK *mch; + PTR p; + WORD from; + WORD w; + LONG loc; + SYM *symbol; + + from = 0; + for(mch = firstmch; mch != NULL; mch = mch->mcnext) { + printf("mch=$%08lx mcptr=$%08lx mcalloc=$%lx mcused=$%x\n", + mch, + mch->mcptr, + mch->mcalloc, + mch->mcused); + + p = mch->mcptr; + for(;;) { + w = *p.wp++; + if(w & MCHEND) + break; + + symbol = NULL; + loc = *p.lp++; + + if(w & MCHFROM) + from = *p.wp++; + + if(w & MSYMBOL) + symbol = *p.sy++; + + printf("m=$%04x to=%d loc=$%lx from=%d siz=%s", + w, w & 0x00ff, loc, from, (w & MLONG) ? "long" : "word"); + + if(symbol != NULL) + printf(" sym=`%s'", symbol->sname); + printf("\n"); + } + } + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Dump memory from 'start' for 'count' bytes; `flg' is the following ORed together: +// 0 - bytes +// 1 - words +// 2 - longwords +// +// if `base' is not -1, then print it at the start of each line, incremented accordingly. +// ------------------------------------------------------------------------------------------------- +// + +int mdump(char *start, LONG count, int flg, LONG base) { + int i, j, k; + + j = 0; + for(i = 0; i < (int)count;) { + if((i & 15) == 0) { + if(j < i) { + printf(" "); + while(j < i) + visprt(start[j++]); + putchar('\n'); + } + j = i; + if(base != -1) + printf("%08lx ", base); + } + + switch(flg & 3) { + case 0: + printf("%02x ", start[i] & 0xff); + ++i; + break; + case 1: + printf("%02x%02x ", start[i] & 0xff, start[i+1] & 0xff); + i += 2; + break; + case 2: + printf("%02x%02x%02x%02x ", start[i] & 0xff, start[i+1] & 0xff, + start[i+2] & 0xff, start[i+3] & 0xff); + i += 4; + break; + case 3: + break; + } + + if(base != -1) + base += 1 << (flg & 3); + } + + // Print remaining bit of ascii; the hairy expression computes the number of + // spaces to print to make the ascii line up nicely. + if(j != i) { + k = ((16 - (i - j)) / (1 << (flg & 3))) * siztab[flg & 3]; + while(k--) + putchar(' '); + printf(" "); + while(j < i) + visprt(start[j++]); + putchar('\n'); + } + + return(0); +} + +// +// --- Dump list of tokens on stdout in printable form --------------------------------------------- +// + +int dumptok(TOKEN *tk) { + int flg = 0; + + while(*tk != EOL) { + if(flg++) + printf(" "); + + if(*tk >= 128) { + printf("REG=%ld", *tk++ - 128); + continue; + } + + switch((int)*tk++) { + case CONST: // CONST + printf("CONST=%ld", *tk++); + break; + case STRING: // STRING
+ printf("STRING='%s'", *tk++); + break; + case SYMBOL: // SYMBOL
+ printf("SYMBOL='%s'", *tk++); + break; + case EOL: // End of line + printf("EOL"); + break; + case TKEOF: // End of file (or macro) + printf("TKEOF"); + break; + case DEQUALS: // == + printf("DEQUALS"); + break; + case DCOLON: // :: + printf("DCOLON"); + break; + case GE: // >= + printf("GE"); + break; + case LE: // <= + printf("LE"); + break; + case NE: // <> or != + printf("NE"); + break; + case SHR: // >> + printf("SHR"); + break; + case SHL: // << + printf("SHL"); + break; + default: + printf("%c", tk[-1]); + break; + } + } + printf("\n"); + + return(0); +} + +// +// --- Dump Everything ----------------------------------------------------------------------------- +// + +int dump_everything(void) { + int i; + + for(i = 1; i < NSECTS; ++i) + if(sect[i].scattr & SUSED) { + printf("Section %d sloc=$%lx\n", i, sect[i].sloc); + printf("Code:\n"); + chdump(sect[i].sfcode, 1); + + printf("Fixup:\n"); + fudump(sect[i].sffix); + + printf("\n"); + } + + printf("\nMarks:\n"); + mudump(); // Dump marks + printf("Total memory allocated=$%lx\n", amemtot); + + return(0); +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..853d5f2 --- /dev/null +++ b/debug.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// DEBUG.H - Debugging Messages +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include "rmac.h" + +// Prototypes +int mudump(void); +int mdump(char *, LONG, int, LONG); +int dumptok(TOKEN *); +int dump_everything(void); + +#endif // __DEBUG_H__ \ No newline at end of file diff --git a/direct.c b/direct.c new file mode 100644 index 0000000..54a185f --- /dev/null +++ b/direct.c @@ -0,0 +1,1176 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// DIRECT.C - Directive Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// + +#include "direct.h" +#include "sect.h" +#include "risca.h" +#include "error.h" +#include "token.h" +#include "procln.h" +#include "expr.h" +#include "mach.h" +#include "listing.h" +#include "mark.h" +#include "symbol.h" + +#define DEF_KW +#include "kwtab.h" + +TOKEN exprbuf[128]; // Expression buffer + +// Directive handler table +int (*dirtab[])() = { + d_org, // 0 org + d_even, // 1 even + d_unimpl, // 2 .6502 + d_68000, // 3 .68000 + d_bss, // 4 bss + d_data, // 5 data + d_text, // 6 text + d_abs, // 7 abs + d_comm, // 8 comm + d_init, // 9 init + d_cargs, // 10 cargs + d_goto, // 11 goto + d_dc, // 12 dc + d_ds, // 13 ds + d_undmac, // 14 undefmac + d_gpu, // 15 .gpu + d_dsp, // 16 .dsp + d_dcb, // 17 dcb + d_unimpl, // 18* set + d_unimpl, // 19* reg + d_unimpl, // 20 dump + d_incbin, // 21 .incbin //load + d_unimpl, // 22 disable + d_unimpl, // 23 enable + d_globl, // 24 globl + d_regbank0, // 25 .regbank0 + d_regbank1, // 26 .regbank1 + d_unimpl, // 27 xdef + d_assert, // 28 assert + d_unimpl, // 29* if + d_unimpl, // 30* endif + d_unimpl, // 31* endc + d_unimpl, // 32* iif + d_include, // 33 include + fpop, // 34 end + d_unimpl, // 35* macro + exitmac, // 36* exitm + d_unimpl, // 37* endm + d_list, // 38 list + d_nlist, // 39 nlist + d_long, // 40* rept + d_phrase, // 41* endr + d_dphrase, // 42 struct + d_qphrase, // 43 ends + d_title, // 44 title + d_subttl, // 45 subttl + eject, // 46 eject + d_unimpl, // 47 error + d_unimpl, // 48 warn + d_noclear, // 49 .noclear + d_equrundef, // 50 .equrundef/.regundef + d_ccundef, // 51 .ccundef + d_print, // 52 .print + d_gpumain, // 53 .gpumain + d_jpad, // 54 .jpad + d_nojpad, // 55 .nojpad + d_fail, // 56 .fail +}; + +// +// --- .org - Set origin --------------------------------------------------------------------------- +// + +int d_fail(void) { + fatal("user abort"); + return(0); +} + +// +// --- .org - Set origin --------------------------------------------------------------------------- +// + +int d_org(void) { + VALUE address; + + if(!rgpu && !rdsp) + return(error(".org permitted only in gpu/dsp section")); + + orgaddr = 0; + + if(abs_expr(&address) == ERROR) { + error("cannot determine org'd address"); + return(ERROR); + } + + orgaddr = address; + orgactive = 1; + + return(0); +} + +// +// --- NOP Padding Directive ----------------------------------------------------------------------- +// + +int d_jpad(void) { + jpad = 1; + return(0); +} + +int d_nojpad(void) { + jpad = 0; + return(0); +} + +// +// --- Print Directive ----------------------------------------------------------------------------- +// + +int d_print(void) { + char prntstr[LNSIZ]; // String for PRINT directive + char format[LNSIZ]; // Format for PRINT directive + int formatting = 0; // Formatting on/off + int wordlong = 0; // WORD = 0, LONG = 1 + int outtype = 0; // 0:hex, 1:decimal, 2:unsigned + + VALUE eval; // Expression value + WORD eattr; // Expression attributes + SYM *esym; // External symbol involved in expr. + TOKEN r_expr[EXPRSIZE]; + + while(*tok != EOL) { + switch(*tok) { + case STRING: + sprintf(prntstr, "%s", (char*)tok[1]); + printf("%s", prntstr); + if(list_fd) + write(list_fd, prntstr, (LONG)strlen(prntstr)); + tok+=2; + break; + case '/': + formatting = 1; + if(tok[1] != SYMBOL) goto token_err; + strcpy(prntstr, (char*)tok[2]); + switch(prntstr[0]) { + case 'l': case 'L': wordlong = 1; break; + case 'w': case 'W': wordlong = 0; break; + case 'x': case 'X': outtype = 0; break; + case 'd': case 'D': outtype = 1; break; + case 'u': case 'U': outtype = 2; break; + default: + error("unknown print format flag"); + return(ERROR); + } + tok += 3; + break; + case ',': + tok++; + break; + default: + if(expr(r_expr, &eval, &eattr, &esym) != OK) + goto token_err; + else { + switch(outtype) { + case 0: strcpy(format, "%X"); break; + case 1: strcpy(format, "%d" ); break; + case 2: strcpy(format, "%u" ); break; + } + if(wordlong) sprintf(prntstr, format, eval); + else sprintf(prntstr, format, eval & 0xFFFF); + printf("%s", prntstr); + if(list_fd) + write(list_fd, prntstr, (LONG)strlen(prntstr)); + formatting = 0; + wordlong = 0; + outtype = 0; + } + break; + } + } + + printf("\n"); + println("\n"); + + return(0); + + token_err: + error("illegal print token"); + return(ERROR); +} + +// +// --- Undefine an Equated Condition Code ---------------------------------------------------------- +// + +int d_ccundef(void) { + SYM *ccname; + + if(!rgpu && !rdsp) { // Check that we are in a RISC section + error(".ccundef must be defined in .gpu/.dsp section"); + return(ERROR); + } + if(*tok != SYMBOL) { + error(syntax_error); + return(ERROR); + } + ccname = lookup((char *)tok[1], LABEL, 0); + if(!ccname || !(ccname->sattre & EQUATEDCC)) { // Make sure symbol is a valid ccdef + error("invalid equated condition name specified"); + return(ERROR); + } + + ccname->sattre |= UNDEF_CC; + + return(0); +} + +// +// --- Undefine an Equated Register ---------------------------------------------------------------- +// + +int d_equrundef(void) { + SYM *regname; + + if(!rgpu && !rdsp) { // Check that we are in a RISC section + error(".equrundef/.regundef must be defined in .gpu/.dsp section"); + return(ERROR); + } + + while(*tok != EOL) { + if(*tok == ',') tok++; // Skip preceeding or seperating commas + + if(*tok != SYMBOL) { // Check we are dealing with a symbol + error(syntax_error); + return(ERROR); + } + + regname = lookup((char *)tok[1], LABEL, 0); // Lookup and undef if equated register + if(regname && (regname->sattre & EQUATEDREG)) + regname->sattre |= UNDEF_EQUR; + + tok += 2; // Skip over symbol token and address + } + + return(0); +} + +// +// --- Do Not Allow the Use of the CLR.L Opcode ---------------------------------------------------- +// + +int d_noclear(void) { + return(0); +} + +// +// --- Include Binary File ------------------------------------------------------------------------- +// + +int d_incbin(void) { + int i, j; + int bytes = 0; + long pos, size; + char buf; + + if(*tok != STRING) { + error(syntax_error); + return(ERROR); + } + + if((j = open((char *)tok[1], _OPEN_INC)) >= 0) { + size = lseek(j, 0L, SEEK_END); + chcheck(size); + pos = lseek(j, 0L, SEEK_SET); + + for(i = 0; i < size; i++) { + buf = '\0'; + bytes = read(j, &buf, 1); + D_byte(buf); + } + + } else { + errors("cannot open include binary file (%s)", (char*)tok[1]); + return(ERROR); + } + + close(j); + return(0); +} + +// +// --- Set RISC Register Banks --------------------------------------------------------------------- +// + +int d_regbank0(void) { + regbank = BANK_0; // Set active register bank zero + return(0); +} + +int d_regbank1(void) { + regbank = BANK_1; // Set active register bank one + return(0); +} + +// +// --- Adjust Location to an EVEN Value ------------------------------------------------------------ +// + +int d_even(void) { + if(sloc & 1) { + if((scattr & SBSS) == 0) { + chcheck(1); + D_byte(0); + } else { + ++sloc; + } + } + + return(0); +} + +// +// --- Adjust Location to an LONG Value ------------------------------------------------------------ +// + +int d_long(void) { + unsigned i; + unsigned val = 4; + + i = sloc & ~(val - 1); + if(i != sloc) val = val - (sloc - i); + else val = 0; + + if(val) { + if((scattr & SBSS) == 0) { + chcheck(val); + for(i = 0; i < val; i++) + D_byte(0); + } else { + sloc += val; + } + } + + return(0); +} + +// +// --- Adjust Location to an PHRASE Value ---------------------------------------------------------- +// + +int d_phrase(void) { + unsigned i; + unsigned val = 8; + + i = sloc & ~(val - 1); + if(i != sloc) val = val - (sloc - i); + else val = 0; + + if(val) { + if((scattr & SBSS) == 0) { + chcheck(val); + for(i = 0; i < val; i++) + D_byte(0); + } else { + sloc += val; + } + } + + return(0); +} + +// +// --- Adjust Location to an DPHRASE Value --------------------------------------------------------- +// + +int d_dphrase(void) { + unsigned i; + unsigned val = 16; + + i = sloc & ~(val - 1); + if(i != sloc) val = val - (sloc - i); + else val = 0; + + if(val) { + if((scattr & SBSS) == 0) { + chcheck(val); + for(i = 0; i < val; i++) + D_byte(0); + } else { + sloc += val; + } + } + + return(0); +} + +// +// --- Adjust Location to an QPHRASE Value --------------------------------------------------------- +// + +int d_qphrase(void) { + unsigned i; + unsigned val = 32; + + i = sloc & ~(val - 1); + if(i != sloc) val = val - (sloc - i); + else val = 0; + + if(val) { + if((scattr & SBSS) == 0) { + savsect(); + chcheck(val); + for(i = 0; i < val; i++) + D_byte(0); + } else { + sloc += val; + } + } + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Do auto-even. This must be called ONLY if 'sloc' is odd. +// +// This is made hairy because, if there was a label on the line, we also have to adjust its value. +// This won't work with more than one label on the line, which is OK since multiple labels are only +// allowed in AS68 kludge mode, and the C compiler is VERY paranoid and uses ".even" whenever it can +// ------------------------------------------------------------------------------------------------- +// + +void auto_even(void) { + if(scattr & SBSS) + ++sloc; // Bump BSS section + else { + D_byte(0) // Deposit 0.b in non-BSS + } + + if(lab_sym != NULL) // Bump label if we have to + ++lab_sym->svalue; +} + +// +// --- Unimplemened Directive Error ---------------------------------------------------------------- +// + +int d_unimpl(void) { + return(error("unimplemented directive")); +} + +// +// --- Return absolute (not TDB) and defined expression or return an error ------------------------- +// + +int abs_expr(VALUE *a_eval) { + WORD eattr; + + if(expr(exprbuf, a_eval, &eattr, NULL) < 0) + return(ERROR); + if(!(eattr & DEFINED)) + return(error(undef_error)); + if(eattr & TDB) + return(error(rel_error)); + + return(OK); +} + +// +// --- Hand symbols in a symbol-list to a function (kind of like mapcar...) ------------------------ +// + +int symlist(int(*func)()) { + char *em = "symbol list syntax"; + + for(;;) { + if(*tok != SYMBOL) return(error(em)); + if((*func)(tok[1]) != OK) break; + tok += 2; + if(*tok == EOL) break; + if(*tok != ',') return(error(em)); + ++tok; + } + + return(0); +} + +// +// --- .include "filename" ------------------------------------------------------------------------- +// + +int d_include(void) { + int j; + int i; + char *fn; + char buf[128]; + char buf1[128]; + + if(*tok == STRING) // Leave strings ALONE + fn = (char *)*++tok; + else if(*tok == SYMBOL) { // Try to append ".s" to symbols + strcpy(buf, (char *)*++tok); + fext(buf, ".s", 0); + fn = &buf[0]; + } else // Punt if no STRING or SYMBOL + return(error("missing filename")); + + // Make sure the user didn't try anything like: + // .include equates.s + if(*++tok != EOL) return(error("extra stuff after filename -- enclose it in quotes")); + + // Attempt to open the include file in the current directory, then (if that failed) try list + // of include files passed in the enviroment string or by the "-d" option. + if((j = open(fn, 0)) < 0) { + for(i = 0; nthpath("RMACPATH", i, buf1) != 0; ++i) { + j = strlen(buf1); + if(j > 0 && buf1[j-1] != SLASHCHAR) // Append path char if necessary + strcat(buf1, SLASHSTRING); + strcat(buf1, fn); + if((j = open(buf1, 0)) >= 0) + goto allright; + } + + return(errors("cannot open: \"%s\"", fn)); + } + + allright: + + include(j, fn); + return(0); +} + +// +// --- .assert expression [, expression...] -------------------------------------------------------- +// + +int d_assert(void) { + WORD eattr; + VALUE eval; + + for(; expr(exprbuf, &eval, &eattr, NULL) == OK; ++tok) { + if(!(eattr & DEFINED)) + return(error("forward or undefined .assert")); + if(!eval) + return(error("assert failure")); + if(*tok != ',') + break; + } + at_eol(); + return(0); +} + +// +// --- .globl symbol [, symbol] <<>> ----------------------------- +// + +int globl1(char *p) { + SYM *sy; + + if(*p == '.') + return(error("cannot .globl local symbol")); + if((sy = lookup(p, LABEL, 0)) == NULL) { + sy = newsym(p, LABEL, 0); + sy->svalue = 0; + sy->sattr = GLOBAL; + } else + sy->sattr |= GLOBAL; + + return(OK); +} + +int d_globl(void) { + symlist(globl1); + return(0); +} + +// +// --- .abs [expression] --------------------------------------------------------------------------- +// + +int d_abs(void) { + VALUE eval; + + savsect(); + if(*tok == EOL) + eval = 0; + else + if(abs_expr(&eval) != OK) + return(0); + + switchsect(ABS); + sloc = eval; + return(0); +} + +// +// --- Switch Segments ----------------------------------------------------------------------------- +// + +int d_text(void) { + if(rgpu || rdsp) + return(error("directive forbidden in gpu/dsp mode")); + + if(cursect != TEXT) { + savsect(); + switchsect(TEXT); + } + return(0); +} + +int d_data(void) { + if(rgpu || rdsp) + return(error("directive forbidden in gpu/dsp mode")); + + if(cursect != DATA) { + savsect(); + switchsect(DATA); + } + return(0); +} + +int d_bss(void) { + if(rgpu || rdsp) + return(error("directive forbidden in gpu/dsp mode")); + + if(cursect != BSS) { + savsect(); + switchsect(BSS); + } + return(0); +} + +// +// --- .ds[.size] expression ----------------------------------------------------------------------- +// + +int d_ds(WORD siz) { + VALUE eval; + + // This gets kind of stupid. This directive is disallowed in normal 68000 mode ("for your own + // good!"), but is permitted for 6502 and Alcyon-compatibility modes. + // For obvious reasons, no auto-even is done in 8-bit processor modes. + if(as68_flag == 0 && (scattr & SBSS) == 0) + return(error(".ds permitted only in BSS")); + + if(siz != SIZB && (sloc & 1)) // Automatic .even + auto_even(); + + if(abs_expr(&eval) != OK) return(0); + + // In non-TDB section (BSS, ABS and M6502) just advance the location counter appropriately. + // In TDB sections, deposit (possibly large) chunks of zeroed memory.... + if((scattr & SBSS)) { + listvalue(eval); + eval *= siz; + sloc += eval; + just_bss = 1; // No data deposited (8-bit CPU mode) + } else { + dep_block(eval, siz, (VALUE)0, (WORD)(DEFINED|ABS), NULL); + } + + at_eol(); + return(0); +} + +// +// --- dc.b, dc.w / dc, dc.l ----------------------------------------------------------------------- +// + +int d_dc(WORD siz) { + WORD eattr; + VALUE eval; + WORD tdb; + WORD defined; + LONG i; + char *p; + int movei = 0; // movei flag for dc.i + + if((scattr & SBSS) != 0) + return(error("illegal initialization of section")); + + if((siz != SIZB) && (sloc & 1)) + auto_even(); + + for(;; ++tok) { + // dc.b 'string' [,] ... + if (siz == SIZB && *tok == STRING && (tok[2] == ',' || tok[2] == EOL)) { + i = strlen((const char*)tok[1]); + if((challoc - ch_size) < i) + chcheck(i); + for(p = (char *)tok[1]; *p != EOS; ++p) + D_byte(*p); + tok += 2; + goto comma; + } + + if(*tok == 'I') { + movei = 1; + tok++; + siz = SIZL; + } + + // dc.x + if(expr(exprbuf, &eval, &eattr, NULL) != OK) + return(0); + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + + switch(siz) { + case SIZB: + if(!defined) { + fixup(FU_BYTE|FU_SEXT, sloc, exprbuf); + D_byte(0); + } else { + if(tdb) + return(error("non-absolute byte value")); + if(eval + 0x100 >= 0x200) + return(error(range_error)); + D_byte(eval); + } + break; + case SIZW: + case SIZN: + if(!defined) { + fixup(FU_WORD|FU_SEXT, sloc, exprbuf); + D_word(0); + } else { + if(tdb) + rmark(cursect, sloc, tdb, MWORD, NULL); + if(eval + 0x10000 >= 0x20000) + return(error(range_error)); + // Deposit 68000 or 6502 (byte-reversed) word + D_word(eval); + } + break; + case SIZL: + if(!defined) { + if(movei) + fixup(FU_LONG|FU_MOVEI, sloc, exprbuf); + else + fixup(FU_LONG, sloc, exprbuf); + D_long(0); + } else { + if(tdb) + rmark(cursect, sloc, tdb, MLONG, NULL); + if(movei) + eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000); + D_long(eval); + } + break; + } + + comma: + + if(*tok != ',') + break; + } + + at_eol(); + return(0); +} + +// +// --- dcb[.siz] expr1,expr2 - Make 'expr1' copies of 'expr2' -------------------------------------- +// + +int d_dcb(WORD siz) { + VALUE evalc, eval; + WORD eattr; + + if((scattr & SBSS) != 0) + return(error("illegal initialization of section")); + + if(abs_expr(&evalc) != OK) + return(0); + + if(*tok++ != ',') + return(error("missing comma")); + + if(expr(exprbuf, &eval, &eattr, NULL) < 0) + return(0); + + if((siz != SIZB) && (sloc & 1)) + auto_even(); + + dep_block(evalc, siz, eval, eattr, exprbuf); + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Generalized initialization directive +// +// .init[.siz] [#count,] expression [.size] , ... +// +// The size suffix on the ".init" directive becomes the default size of the objects to deposit. +// If an item is preceeded with a sharp (immediate) sign and an expression, it specifies a repeat +// count. The value to be deposited may be followed by a size suffix, which overrides the +// default size. +// ------------------------------------------------------------------------------------------------- +// + +int d_init(WORD def_siz) { + VALUE count; + VALUE eval; + WORD eattr; + WORD siz; + + if((scattr & SBSS) != 0) + return(error(".init not permitted in BSS or ABS")); + + if(rgpu || rdsp) + return(error("directive forbidden in gpu/dsp mode")); + + for(;;) { + // Get repeat count (defaults to 1) + if(*tok == '#') { + ++tok; + if(abs_expr(&count) != OK) + return(0); + if(*tok++ != ',') + return(error(comma_error)); + } else + count = 1; + + // Evaluate expression to deposit + if(expr(exprbuf, &eval, &eattr, NULL) < 0) + return(0); + + switch((int)*tok++) { // Determine size of object to deposit + case DOTB: siz = SIZB; break; + case DOTW: siz = SIZB; break; + case DOTL: siz = SIZL; break; + default: + siz = def_siz; + --tok; + break; + } + + dep_block(count, siz, eval, eattr, exprbuf); + + switch((int)*tok) { + case EOL: + return(0); + case ',': + ++tok; + continue; + default: + return(error(comma_error)); + } + } +} + +// +// --- Deposit 'count' values of size 'siz' in the current (non-BSS) segment ----------------------- +// + +int dep_block(VALUE count, WORD siz, VALUE eval, WORD eattr, TOKEN *exprbuf) { + WORD tdb; + WORD defined; + + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + + while(count--) { + if((challoc - ch_size) < 4) + chcheck(4L); + + switch(siz) { + case SIZB: + if(!defined) { + fixup(FU_BYTE|FU_SEXT, sloc, exprbuf); + D_byte(0); + } else { + if(tdb) + return(error("non-absolute byte value")); + if(eval + 0x100 >= 0x200) + return(error(range_error)); + D_byte(eval); + } + break; + case SIZW: + case SIZN: + if(!defined) { + fixup(FU_WORD|FU_SEXT, sloc, exprbuf); + D_word(0); + } else { + if(tdb) + rmark(cursect, sloc, tdb, MWORD, NULL); + if(eval + 0x10000 >= 0x20000) + return(error(range_error)); + + // Deposit 68000 or 6502 (byte-reversed) word + D_word(eval); + } + break; + case SIZL: + if(!defined) { + fixup(FU_LONG, sloc, exprbuf); + D_long(0); + } else { + if(tdb) + rmark(cursect, sloc, tdb, MLONG, NULL); + D_long(eval); + } + break; + } + } + return(0); +} + +// +// --- .comm symbol, size -------------------------------------------------------------------------- +// + +int d_comm(void) { + SYM *sym; + char *p; + VALUE eval; + + if(*tok != SYMBOL) + return(error("missing symbol")); + p = (char *)tok[1]; + tok += 2; + + if(*p == '.') // Cannot .comm a local symbol + return(error(locgl_error)); + + if((sym = lookup(p, LABEL, 0)) == NULL) + sym = newsym(p, LABEL, 0); + else { + if(sym->sattr & DEFINED) + return(error(".comm symbol already defined")); + } + + sym->sattr = GLOBAL|COMMON|BSS; + if(*tok++ != ',') + return(error(comma_error)); + + if(abs_expr(&eval) != OK) // Parse size of common region + return(0); + sym->svalue = eval; // Install common symbol's size + at_eol(); + return(0); +} + +// +// --- .list - Turn listing on -------------------------------------------------------------------- +// + +int d_list(void) { + if(list_flag) + ++listing; + return(0); +} + +// +// --- .nlist - Turn listing off ------------------------------------------------------------------- +// + +int d_nlist(void) { + if(list_flag) + --listing; + return(0); +} + +// +// --- .68000 - Back to 68000 TEXT segment --------------------------------------------------------- +// + +int d_68000(void) { + rgpu = rdsp = 0; + in_main = 0; + // Switching from gpu/dsp sections should reset any ORG'd Address + orgactive = 0; + orgwarning = 0; + savsect(); + switchsect(TEXT); + return(0); +} + +// +// --- .gpu - Switch to GPU Assembler -------------------------------------------------------------- +// + +int d_gpu(void) { + if((cursect != TEXT) && (cursect != DATA)) { + error(".gpu can only be used in the TEXT or DATA segments"); + return(ERROR); + } + // If previous section was dsp or 68000 then we need to reset ORG'd Addresses + if(!rgpu) { + orgactive = 0; + orgwarning = 0; + } + rgpu = 1; // Set GPU assembly + rdsp = 0; // Unset DSP assembly + regbank = BANK_N; // Set no default register bank + in_main = 0; + jpad = 0; + return(0); +} + +// +// --- GPU Main Code Directive --------------------------------------------------------------------- +// + +int d_gpumain(void) { + if((cursect != TEXT) && (cursect != DATA)) { + error(".gpumain can only be used in the TEXT or DATA segments"); + return(ERROR); + } + // If previous section was dsp or 68000 then we need to reset ORG'd Addresses + if(!rgpu) { + orgactive = 0; + orgwarning = 0; + } + rgpu = 1; // Set GPU assembly + rdsp = 0; // Unset DSP assembly + regbank = BANK_N; // Set no default register bank + in_main = 1; // Enable main code execution rules + jpad = 0; + return(0); +} + +// +// --- .dsp - Switch to DSP Assembler -------------------------------------------------------------- +// + +int d_dsp(void) { + if((cursect != TEXT) && (cursect != DATA)) { + error(".dsp can only be used in the TEXT or DATA segments"); + return(ERROR); + } + // If previous section was gpu or 68000 then we need to reset ORG'd Addresses + if(!rdsp) { + orgactive = 0; + orgwarning = 0; + } + rdsp = 1; // Set DSP assembly + rgpu = 0; // Unset GPU assembly + regbank = BANK_N; // Set no default register bank + in_main = 0; + jpad = 0; + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// .cargs [#offset], symbol[.size], ... +// +// Lists of registers may also be mentioned; they just take up space. Good for "documentation" +// purposes. +// +// .cargs a6,.arg1, .arg2, .arg3... +// +// The symbols are ABS and EQUATED. +// ------------------------------------------------------------------------------------------------- +// + +int d_cargs(void) { + VALUE eval; + WORD rlist; + SYM *sy; + char *p; + int env; + int i; + + if(rgpu || rdsp) + return(error("directive forbidden in gpu/dsp mode")); + + if(*tok == '#') { + ++tok; + if(abs_expr(&eval) != OK) + return(0); + if(*tok == ',') // Eat comma if it's there + ++tok; + } else + eval = 4; + + for(;;) { + if(*tok == SYMBOL) { + p = (char *)tok[1]; + if(*p == '.') + env = curenv; + else + env = 0; + + sy = lookup(p, LABEL, env); + if(sy == NULL) { + sy = newsym(p, LABEL, env); + sy->sattr = 0; + } else + if(sy->sattr & DEFINED) + return(errors("multiply-defined label '%s'", p)); + + // Put symbol in "order of definition" list + if(!(sy->sattr & SDECLLIST)) sym_decl(sy); + + sy->sattr |= ABS|DEFINED|EQUATED; + sy->svalue = eval; + tok += 2; + + switch((int)*tok) { + case DOTL: + eval += 2; + case DOTB: + case DOTW: + ++tok; + } + eval += 2; + } else + if(*tok >= KW_D0 && *tok <= KW_A7) { + if(reglist(&rlist) < 0) + return(0); + for(i = 0; i++ < 16; rlist >>= 1) + if(rlist & 1) + eval += 4; + } else + switch((int)*tok) { + case KW_USP: + case KW_SSP: + case KW_PC: + eval += 2; + // FALLTHROUGH + case KW_SR: + case KW_CCR: + eval += 2; + ++tok; + break; + case EOL: + return(0); + default: + return(error(".cargs syntax")); + } + + if(*tok == ',') + ++tok; + } +} + +// +// --- Undefine a macro - .undefmac macname [, macname...] ----------------------------------------- +// + +int undmac1(char *p) { + SYM *sy; + + // If the macro symbol exists, cause it to dissappear + if((sy = lookup(p, MACRO, 0)) != NULL) sy->stype = (BYTE)SY_UNDEF; + return(OK); +} + +int d_undmac(void) { + symlist(undmac1); + return(0); +} diff --git a/direct.h b/direct.h new file mode 100644 index 0000000..d88ab24 --- /dev/null +++ b/direct.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// DIRECT.H - Directive Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __DIRECT_H__ +#define __DIRECT_H__ + +#include "rmac.h" + +// Globals, Externals etc +extern TOKEN exprbuf[]; +extern int (*dirtab[])(); + +// Prototypes +int d_even(void); +void auto_even(void); +int dep_block(VALUE, WORD, VALUE, WORD, TOKEN *); +int d_unimpl(void); +int d_even(void); +int d_6502(void); +int d_68000(void); +int d_bss(void); +int d_data(void); +int d_text(void); +int d_abs(void); +int d_comm(void); +int d_dc(WORD); +int d_ds(WORD); +int d_dcb(WORD); +int d_globl(void); +int d_gpu(void); +int d_dsp(void); +int d_assert(void); +int d_if(void); +int d_endif(void); +int d_include(void); +int exitmac(void); +int d_list(void); +int d_nlist(void); +int d_title(void); +int d_subttl(void); +int eject(void); +int d_error(char *); +int d_warn(char *); +int d_org(void); +int d_init(WORD); +int d_cargs(void); +int d_undmac(void); +int d_regbank0(void); +int d_regbank1(void); +int d_long(void); +int d_phrase(void); +int d_dphrase(void); +int d_qphrase(void); +int d_incbin(void); +int d_noclear(void); +int d_equrundef(void); +int d_ccundef(void); +int d_print(void); +int d_gpumain(void); +int d_jpad(void); +int d_nojpad(void); +int d_fail(void); +int symlist(int(*)()); +int abs_expr(VALUE *); + +#endif // __DIRECT_H__ diff --git a/eagen.c b/eagen.c new file mode 100644 index 0000000..fbea606 --- /dev/null +++ b/eagen.c @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// EAGEN.C - Effective Address Code Generation +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "rmac.h" +#include "amode.h" +#include "sect.h" +#include "mark.h" +#include "error.h" +#include "mach.h" +#include "risca.h" + +#define eaNgen ea0gen +#define amN am0 +#define aNexattr a0exattr +#define aNexval a0exval +#define aNexpr a0expr +#define aNixreg a0ixreg +#define aNixsiz a0ixsiz +#include "eagen0.c" + +#define eaNgen ea1gen +#define amN am1 +#define aNexattr a1exattr +#define aNexval a1exval +#define aNexpr a1expr +#define aNixreg a1ixreg +#define aNixsiz a1ixsiz +#include "eagen0.c" diff --git a/eagen0.c b/eagen0.c new file mode 100644 index 0000000..c51844a --- /dev/null +++ b/eagen0.c @@ -0,0 +1,171 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// EAGEN0.C - Effective Address Code Generation +// Generated Code for eaN (Included twice by "eagen.c") +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +int eaNgen(WORD siz) { + WORD w; + VALUE v; + WORD tdb; + + v = aNexval; + w = (WORD)(aNexattr & DEFINED); + tdb = (WORD)(aNexattr & TDB); + + switch(amN) { + case DREG: // "Do nothing" - they're in the opword + case AREG: + case AIND: + case APOSTINC: + case APREDEC: + case AM_USP: + case AM_CCR: + case AM_SR: + case AM_NONE: + break; // This is a performance hit, though + case ADISP: // expr(An) + if(w) { // Just deposit it + if(tdb) + rmark(cursect, sloc, tdb, MWORD, NULL); + if(v + 0x8000 >= 0x18000) + return(error(range_error)); + D_word(v); + } else { // Arrange for fixup later on + fixup(FU_WORD|FU_SEXT, sloc, aNexpr); + D_word(0); + } + break; + case PCDISP: + if(w) { // Just deposit it + if((aNexattr & TDB) == cursect) + v -= (VALUE)sloc; + else if((aNexattr & TDB) != ABS) + error(rel_error); + + if(v + 0x8000 >= 0x10000) + return(error(range_error)); + D_word(v); + } else { // Arrange for fixup later on + fixup(FU_WORD|FU_SEXT|FU_PCREL, sloc, aNexpr); + D_word(0); + } + break; + case AINDEXED: + w = (WORD)((aNixreg << 12) | aNixsiz); // Compute ixreg and size+scale + if(aNexattr & DEFINED) { // Deposit a byte... + if(tdb) + return(error(abs_error)); // Can't mark bytes + if(v + 0x80 >= 0x180) + return(error(range_error)); + w |= v & 0xff; + D_word(w); + } else { // Fixup the byte later + fixup(FU_BYTE|FU_SEXT, sloc+1, aNexpr); + D_word(w); + } + break; + case PCINDEXED: + w = (WORD)((aNixreg << 12) | aNixsiz); // Compute ixreg and size+scale + if(aNexattr & DEFINED) { // Deposit a byte... + if((aNexattr & TDB) == cursect) + v -= (VALUE)sloc; + else if((aNexattr & TDB) != ABS) + error(rel_error); + + if(v + 0x80 >= 0x100) + return(error(range_error)); + w |= v & 0xff; + D_word(w); + } else { // Fixup the byte later + fixup(FU_WBYTE|FU_SEXT|FU_PCREL, sloc, aNexpr); + D_word(w); + } + break; + case IMMED: + switch(siz) { + case SIZB: + if(w) { + if(tdb) + return(error("illegal byte-sized relative reference")); + if(v + 0x100 >= 0x200) + return(error(range_error)); + D_word(v); + } else { + fixup(FU_BYTE|FU_SEXT, sloc+1, aNexpr); + D_word(0); + } + break; + case SIZW: + case SIZN: + if(w) { + if(tdb) + rmark(cursect, sloc, tdb, MWORD, NULL); + if(v + 0x10000 >= 0x20000) + return(error(range_error)); + D_word(v); + } else { + fixup(FU_WORD|FU_SEXT, sloc, aNexpr); + D_word(0); + } + break; + case SIZL: + if(w) { + if(tdb) + rmark(cursect, sloc, tdb, MLONG, NULL); + D_long(v); + } else { + fixup(FU_LONG, sloc, aNexpr); + D_long(0); + } + break; + default: + interror(1); // IMMED size problem + } + break; + case ABSW: + if(w) { + if(tdb) + rmark(cursect, sloc, tdb, MWORD, NULL); + if(v + 0x8000 >= 0x10000) + return(error(range_error)); + D_word(v); + } else { + fixup(FU_WORD|FU_SEXT, sloc, aNexpr); + D_word(0); + } + break; + case ABSL: + if(w) { + if(tdb) + rmark(cursect, sloc, tdb, MLONG, NULL); + D_long(v); + } else { + fixup(FU_LONG, sloc, aNexpr); + D_long(0); + } + break; + case ABASE: + case MEMPOST: + case MEMPRE: + case PCBASE: + case PCMPOST: + case PCMPRE: + return(error("unsupported 68020 addressing mode")); + default: + interror(3); // Bad addressing mode in ea gen + } + + return(OK); +} + +// Undefine dirty macros +#undef eaNgen +#undef amN +#undef aNexattr +#undef aNexval +#undef aNexpr +#undef aNixreg +#undef aNixsiz diff --git a/error.c b/error.c new file mode 100644 index 0000000..9a4baeb --- /dev/null +++ b/error.c @@ -0,0 +1,161 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// ERROR.C - Error Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// + +#include "error.h" +#include "token.h" +#include "listing.h" + +int errcnt; // Error count +char *err_fname; // Name of error message file + +static char nl[] = "\n"; + +// +// --- Report error if not at EOL ------------------------------------------------------------------ +// + +int at_eol(void) { + if(*tok != EOL) + error("syntax error"); + return(0); +} + +// +// --- Cannot Create a File ------------------------------------------------------------------------ +// + +void cantcreat(char *fn) { + printf("cannot create: '%s'\n", fn); + exit(1); +} + +// +// --- Setup for Error Message --------------------------------------------------------------------- +// o Create error listing file (if necessary) +// o Set current filename +// + +void err_setup(void) { + char fnbuf[FNSIZ]; + + setfnum(cfileno); + if(err_fname != NULL) { + strcpy(fnbuf, err_fname); + if(*fnbuf == EOS) { + strcpy(fnbuf, firstfname); + } + err_fname = NULL; + + if((err_fd = open(fnbuf, _OPEN_FLAGS, _PERM_MODE)) < 0) + cantcreat(fnbuf); + err_flag = 1; + } +} + +// +// --- Display Error Message ----------------------------------------------------------------------- +// + +int error(char *s) { + char buf[EBUFSIZ]; + unsigned int length; + + err_setup(); + if(listing > 0) ship_ln(s); + sprintf(buf, "%s[%d]: Error: %s%s", curfname, curlineno, s, nl); + length = strlen(buf); + if(err_flag) write(err_fd, buf, length); + else printf("%s", buf); + taglist('E'); + ++errcnt; + + return(ERROR); +} + +int errors(char *s, char *s1) { + char buf[EBUFSIZ]; + char buf1[EBUFSIZ]; + + err_setup(); + sprintf(buf, s, s1); + if(listing > 0) ship_ln(buf); + sprintf(buf1, "%s[%d]: Error: %s%s", curfname, curlineno, buf, nl); + if(err_flag) write(err_fd, buf1, (LONG)strlen(buf1)); + else printf("%s", buf1); + taglist('E'); + ++errcnt; + + return(ERROR); +} + +int warn(char *s) { + char buf[EBUFSIZ]; + + err_setup(); + if(listing > 0) ship_ln(s); + sprintf(buf, "%s[%d]: Warning: %s%s", curfname, curlineno, s, nl); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s", buf); + taglist('W'); + + return(OK); +} + +int warns(char *s, char *s1) { + char buf[EBUFSIZ]; + char buf1[EBUFSIZ]; + + err_setup(); + sprintf(buf, s, s1); + if(listing > 0) ship_ln(s); + sprintf(buf1, "%s[%d]: Warning: %s%s", curfname, curlineno, buf, nl); + if(err_flag) write(err_fd, buf1, (LONG)strlen(buf1)); + else printf("%s", buf1); + taglist('W'); + + return(OK); +} + +int warni(char *s, unsigned i) { + char buf[EBUFSIZ]; + char buf1[EBUFSIZ]; + + err_setup(); + sprintf(buf, s, i); + if(listing > 0) ship_ln(buf); + sprintf(buf1, "%s[%d]: Warning: %s%s", curfname, curlineno, buf, nl); + if(err_flag) write(err_fd, buf1, (LONG)strlen(buf1)); + else printf("%s", buf1); + taglist('W'); + + return(OK); +} + +int fatal(char *s) { + char buf[EBUFSIZ]; + + err_setup(); + if(listing > 0) ship_ln(s); + sprintf(buf, "%s[%d]: Fatal: %s%s", curfname, curlineno, s, nl); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s", buf); + + exit(1); +} + +int interror(int n) { + char buf[EBUFSIZ]; + + err_setup(); + sprintf(buf, "%s[%d]: Internal Error Number %d%s", curfname, curlineno, n, nl); + if(listing > 0) ship_ln(buf); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s", buf); + + exit(1); +} \ No newline at end of file diff --git a/error.h b/error.h new file mode 100644 index 0000000..2e67284 --- /dev/null +++ b/error.h @@ -0,0 +1,31 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// ERROR.H - Error Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +#include "rmac.h" + +#define EBUFSIZ 200 // Max size of an error message + +// Globals, externals etc +extern int errcnt; +extern char *err_fname; + +// Prototypes +int error(char *); +int errors(char *, char *); +int fatal(char *); +int warn(char *); +int warns(char *, char *); +int warni(char *, unsigned); +int interror(int); +void cantcreat(char *); +void err_setup(void); + +#endif // __ERROR_H__ diff --git a/expr.c b/expr.c new file mode 100644 index 0000000..2057d4f --- /dev/null +++ b/expr.c @@ -0,0 +1,564 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// EXPR.C - Expression Analyzer +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "expr.h" +#include "token.h" +#include "listing.h" +#include "error.h" +#include "procln.h" +#include "symbol.h" +#include "sect.h" +#include "mach.h" +#include "risca.h" + +#define DEF_KW // Declare keyword values +#include "kwtab.h" // Incl generated keyword tables & defs + +static char tokcl[128]; // Generated table of token classes +static VALUE evstk[EVSTACKSIZE]; // Evaluator value stack +static WORD evattr[EVSTACKSIZE]; // Evaluator attribute stack + +// Token-class initialization list +char itokcl[] = { + 0, // END + CONST, SYMBOL, 0, // ID + '(', '[', '{', 0, // OPAR + ')', ']', '}', 0, // CPAR + CR_DEFINED, CR_REFERENCED, // SUNARY (special unary) + CR_STREQ, CR_MACDEF, + CR_DATE, CR_TIME, 0, + '!', '~', UNMINUS, 0, // UNARY + '*', '/', '%', 0, // MULT + '+', '-', 0, // ADD + SHL, SHR, 0, // SHIFT + LE, GE, '<', '>', NE, '=', 0, // REL + '&', 0, // AND + '^', 0, // XOR + '|', 0, // OR + 1 // (the end) +}; + +char missym_error[] = "missing symbol"; +char *str_error = "missing symbol or string"; + +// Convert expression to postfix +static TOKEN *tk; // Deposit tokens here +SYM *lookup(); +SYM *newsym(); + +// +// --- Obtain a String Value ----------------------------------------------------------------------- +// + +static VALUE str_value(char *p) { + VALUE v; + + for(v = 0; *p; ++p) + v = (v << 8) | (*p & 0xff); + return(v); +} + +// +// --- Initialize Expression Analyzer -------------------------------------------------------------- +// + +void init_expr(void) { + int i; // Iterator + char *p; // Token pointer + + // Initialize token-class table + for(i = 0; i < 128; ++i) // Mark all entries END + tokcl[i] = END; + + for(i = 0, p = itokcl; *p != 1; ++p) + if(*p == 0) + ++i; + else + tokcl[(int)(*p)] = (char)i; +} + +// +// --- Binary operators (all the same precedence) -------------------------------------------------- +// + +int expr0(void) { + TOKEN t; + + if(expr1() != OK) + return(ERROR); + while(tokcl[*tok] >= MULT) { + t = *tok++; + if(expr1() != OK) + return(ERROR); + *tk++ = t; + } + return(OK); +} + +// +// --- Unary operators (detect unary '-') ---------------------------------------------------------- +// + +int expr1(void) { + int class; + TOKEN t; + SYM *sy; + char *p, *p2; + WORD w; + int j; + + class = tokcl[*tok]; + + if(*tok == '-' || class == UNARY) { + t = *tok++; + if(expr2() != OK) + return(ERROR); + if(t == '-') + t = UNMINUS; + *tk++ = t; + } else if(class == SUNARY) + switch((int)*tok++) { + case CR_TIME: + *tk++ = CONST; + *tk++ = dos_time(); + break; + case CR_DATE: + *tk++ = CONST; + *tk++ = dos_date(); + break; + case CR_MACDEF: // ^^macdef + if(*tok++ != SYMBOL) return(error(missym_error)); + p = (char *)*tok++; + if(lookup(p, MACRO, 0) == NULL) w = 0; + else w = 1; + + *tk++ = CONST; + *tk++ = (TOKEN)w; + break; + case CR_DEFINED: + w = DEFINED; + goto getsym; + case CR_REFERENCED: + w = REFERENCED; + + getsym: + + if(*tok++ != SYMBOL) return(error(missym_error)); + p = (char *)*tok++; + j = 0; + if(*p == '.') j = curenv; + if((sy = lookup(p, LABEL, j)) != NULL && (sy->sattr & w)) w = 1; + else w = 0; + + *tk++ = CONST; + *tk++ = (TOKEN)w; + break; + case CR_STREQ: + if(*tok != SYMBOL && *tok != STRING) return(error(str_error)); + p = (char *)tok[1]; + tok +=2; + + if(*tok++ != ',') return(error(comma_error)); + + if(*tok != SYMBOL && *tok != STRING) return(error(str_error)); + p2 = (char *)tok[1]; + tok += 2; + + w = (WORD)(!strcmp(p, p2)); + *tk++ = CONST; + *tk++ = (TOKEN)w; + break; + } + else + return(expr2()); + + return(OK); +} + +// +// --- Terminals (CONSTs) and parenthesis grouping ------------------------------------------------- +// + +int expr2(void) { + char *p; + SYM *sy; + int j; + + switch((int)*tok++) { + case CONST: + *tk++ = CONST; + *tk++ = *tok++; + break; + case SYMBOL: + p = (char *)*tok++; + j = 0; + if(*p == '.') + j = curenv; + sy = lookup(p, LABEL, j); + if(sy == NULL) + sy = newsym(p, LABEL, j); + + if(sy->sattre & EQUATEDREG) { // Check register bank usage + if((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok) + warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname); + if((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok) + warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname); + } + + *tk++ = SYMBOL; + *tk++ = (TOKEN)sy; + break; + case STRING: + *tk++ = CONST; + *tk++ = str_value((char *)*tok++); + break; + case '(': + if(expr0() != OK) + return(ERROR); + if(*tok++ != ')') + return(error("missing close parenthesis ')'")); + break; + case '[': + if(expr0() != OK) + return(ERROR); + if(*tok++ != ']') + return(error("missing close parenthesis ']'")); + break; + case '$': + *tk++ = ACONST; // Attributed const + *tk++ = sloc; // Current location + *tk++ = cursect | DEFINED; // Store attribs + break; + case '*': + *tk++ = ACONST; // Attributed const + if(orgactive) + *tk++ = orgaddr; + else + *tk++ = pcloc; // Location at start of line + *tk++ = ABS | DEFINED; // Store attribs + break; + default: + return(error("bad expression")); + } + return(OK); +} + +// +// --- Recursive-descent expression analyzer (with some simple speed hacks) ------------------------ +// + +int expr(TOKEN *otk, VALUE *a_value, WORD *a_attr, SYM **a_esym) { + SYM *sy; + char *p; + int j; + + tk = otk; + + // Optimize for single constant or single symbol. + if((tok[1] == EOL) || + (((*tok == CONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31)) && + (tokcl[tok[2]] < UNARY))) { + + if(*tok >= KW_R0 && *tok <= KW_R31) { + *tk++ = CONST; + *tk++ = *a_value = (*tok - KW_R0); + *a_attr = ABS | DEFINED; + if(a_esym != NULL) + *a_esym = NULL; + tok++; + *tk++ = ENDEXPR; + return(OK); + } else if(*tok == CONST) { + *tk++ = CONST; + *tk++ = *a_value = tok[1]; + *a_attr = ABS | DEFINED; + if(a_esym != NULL) + *a_esym = NULL; + } else if(*tok == '*') { + *tk++ = CONST; + if(orgactive) + *tk++ = *a_value = orgaddr; + else + *tk++ = *a_value = pcloc; + *a_attr = ABS | DEFINED; + //*tk++ = + if(a_esym != NULL) + *a_esym = NULL; + tok--; + } else { + p = (char *)tok[1]; + j = 0; + if(*p == '.') + j = curenv; + sy = lookup(p, LABEL, j); + + if(sy == NULL) + sy = newsym(p, LABEL, j); + sy->sattr |= REFERENCED; + + if(sy->sattre & EQUATEDREG) { // Check register bank usage + if((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok) + warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname); + if((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok) + warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname); + } + + *tk++ = SYMBOL; + *tk++ = (TOKEN)sy; + + if(sy->sattr & DEFINED) + *a_value = sy->svalue; + else *a_value = 0; + + if(sy->sattre & EQUATEDREG) + *a_value &= 0x1F; + + *a_attr = (WORD)(sy->sattr & ~GLOBAL); + + if((sy->sattr & (GLOBAL|DEFINED)) == GLOBAL && a_esym != NULL) { + *a_esym = sy; + } + } + tok += 2; + *tk++ = ENDEXPR; + return(OK); + } + + if(expr0() != OK) + return(ERROR); + *tk++ = ENDEXPR; + return(evexpr(otk, a_value, a_attr, a_esym)); +} + +// +// ------------------------------------------------------------------------------------------------- +// Evaluate expression. +// If the expression involves only ONE external symbol, the expression is UNDEFINED, but it's value +// includes everything but the symbol value, and `a_esym' is set to the external symbol. +// ------------------------------------------------------------------------------------------------- +// + +int evexpr(TOKEN *tk, VALUE *a_value, WORD *a_attr, SYM **a_esym) { + WORD *sattr; + VALUE *sval; + WORD attr; + SYM *sy; + SYM *esym; + WORD sym_seg; + + sval = evstk; // (Empty) initial stack + sattr = evattr; + esym = NULL; // No external symbol involved + sym_seg = 0; + + while(*tk != ENDEXPR) + switch((int)*tk++) { + case SYMBOL: + sy = (SYM *)*tk++; + sy->sattr |= REFERENCED; // Set "referenced" bit + + if(!(sy->sattr & DEFINED)) { + if(!(sy->sattr & GLOBAL)) { // Reference to undefined symbol + *a_attr = 0; + *a_value = 0; + return(OK); + } + if(esym != NULL) // Check for multiple externals + return(error(seg_error)); + esym = sy; + } + + if(sy->sattr & DEFINED) { + *++sval = sy->svalue; // Push symbol's value + } else { + *++sval = 0; // 0 for undefined symbols + } + + *++sattr = (WORD)(sy->sattr & ~GLOBAL); // Push attribs + sym_seg = (WORD)(sy->sattr & (TEXT|DATA|BSS)); + break; + case CONST: + *++sval = *tk++; // Push value + *++sattr = ABS|DEFINED; // Push simple attribs + break; + case ACONST: + *++sval = *tk++; // Push value + *++sattr = (WORD)*tk++; // Push attribs + break; + + // Binary "+" and "-" matrix: + // + // ABS Sect Other + // ---------------------------- + // ABS | ABS | Sect | Other | + // Sect | Sect | [1] | Error | + // Other | Other | Error | [1] | + // ---------------------------- + // + // [1] + : Error + // - : ABS + case '+': + --sval; // Pop value + --sattr; // Pop attrib + *sval += sval[1]; // Compute value + + if(!(*sattr & (TEXT|DATA|BSS))) + *sattr = sattr[1]; + else if(sattr[1] & (TEXT|DATA|BSS)) + return(error(seg_error)); + break; + case '-': + --sval; // Pop value + --sattr; // Pop attrib + *sval -= sval[1]; // Compute value + + attr = (WORD)(*sattr & (TEXT|DATA|BSS)); + if(!attr) + *sattr = sattr[1]; + else if(sattr[1] & (TEXT|DATA|BSS)) { + if(!(attr & sattr[1])) { + return(error(seg_error)); + } else { + *sattr &= ~(TEXT|DATA|BSS); + } + } + break; + // Unary operators only work on ABS items + case UNMINUS: + if(*sattr & (TEXT|DATA|BSS)) + error(seg_error); + *sval = -(int)*sval; + *sattr = ABS|DEFINED; // Expr becomes absolute + break; + case '!': + if(*sattr & (TEXT|DATA|BSS)) + error(seg_error); + *sval = !*sval; + *sattr = ABS|DEFINED; // Expr becomes absolute + break; + case '~': + if(*sattr & (TEXT|DATA|BSS)) + error(seg_error); + *sval = ~*sval; + *sattr = ABS|DEFINED; // Expr becomes absolute + break; + // Comparison operators must have two values that + // are in the same segment, but that's the only requirement. + case LE: + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval <= sval[1]; + break; + case GE: + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval >= sval[1]; + break; + case '>': + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval > sval[1]; + break; + case '<': + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval < sval[1]; + break; + case NE: + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval != sval[1]; + break; + case '=': + --sattr; + --sval; + if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error); + *sattr = ABS|DEFINED; + *sval = *sval == sval[1]; + break; + // All other binary operators must have two ABS items + // to work with. They all produce an ABS value. + default: + // GH - Removed for v1.0.15 as part of the fix for indexed loads. + //if((*sattr & (TEXT|DATA|BSS)) || (*--sattr & (TEXT|DATA|BSS))) + //error(seg_error); + + *sattr = ABS|DEFINED; // Expr becomes absolute + switch((int)tk[-1]) { + case '*': + --sval; + --sattr; // Pop attrib + *sval *= sval[1]; + break; + case '/': + --sval; + --sattr; // Pop attrib + if(sval[1] == 0) + return(error("divide by zero")); + *sval /= sval[1]; + break; + case '%': + --sval; + --sattr; // Pop attrib + if(sval[1] == 0) + return(error("mod (%) by zero")); + *sval %= sval[1]; + break; + case SHL: + --sval; + --sattr; // Pop attrib + *sval <<= sval[1]; + break; + case SHR: + --sval; + --sattr; // Pop attrib + *sval >>= sval[1]; + break; + case '&': + --sval; + --sattr; // Pop attrib + *sval &= sval[1]; + break; + case '^': + --sval; + --sattr; // Pop attrib + *sval ^= sval[1]; + break; + case '|': + --sval; + --sattr; // Pop attrib + *sval |= sval[1]; + break; + default: + interror(5); // Bad operator in expression stream + } + } + + if(esym != NULL) *sattr &= ~DEFINED; + if(a_esym != NULL) *a_esym = esym; + + // sym_seg added in 1.0.16 to solve a problem with forward symbols in expressions where absolute + // values also existed. The absolutes were overiding the symbol segments and not being included :( + //*a_attr = *sattr | sym_seg; // Copy value + attrib + + *a_attr = *sattr; // Copy value + attrib + *a_value = *sval; + + return(OK); +} + + diff --git a/expr.h b/expr.h new file mode 100644 index 0000000..05bf918 --- /dev/null +++ b/expr.h @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// EXPR.H - Expression Analyzer +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __EXPR_H__ +#define __EXPR_H__ + +#include "rmac.h" + +// Tunable definitions +#define STKSIZE 64 // Size of expression stacks +#define EVSTACKSIZE 64 // Expression evaluator stack size + +// Token classes in order of precedence +#define END 0 // End/beginning of input +#define ID 1 // Symbol or constant +#define OPAR 2 // ( +#define CPAR 3 // ) +#define SUNARY 4 // Special unary (^^defined, etc.) +#define UNARY 5 // Unary class: ! ~ - +#define MULT 6 // Multiplicative class: * / % +#define ADD 7 // Additive class: + - +#define SHIFT 8 // Shift class: << >> +#define REL 9 // Relational class: <= >= < > <> = != +#define AND 10 // Bitwise and: & +#define XOR 11 // Bitwise xor: ^ +#define OR 12 // Bitwise or: | + +// Prototypes +void init_expr(void); +int expr1(void); +int expr2(void); +int expr(TOKEN *, VALUE *, WORD *, SYM **); +int evexpr(TOKEN *, VALUE *, WORD *, SYM **); + +#endif // __EXPR_H__ \ No newline at end of file diff --git a/kwgen.c b/kwgen.c new file mode 100644 index 0000000..9a077b9 --- /dev/null +++ b/kwgen.c @@ -0,0 +1,384 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// KWGEN.C - Keyword & Mnemonic Definition and State Machine Creation Tool +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +/* + * keyword transition-table generation utility + * + * + *---------------- + * + * SYNOPSIS: kw basename file2 + * + * `-d' turns on debugging messages + * + * Accepts a list of keywords and corresponding values. Lines + * beginning with '#' are comments. Values may be empty (resulting + * in a default value of the previous item's value plus 1, or zero + * if there is no previous item), decimal numbers, or characters + * enclosed between single quotes. + * + * # this is a comment + * .byte 34 + * .word 'A' + * .long + * + * The `basename' is a string prepended to the beginning of each of + * the output array names, and should be one or two characters. + * + */ +#include +#include +#include +#include + +/* + * Tunable definitions + * + */ +#define TABSIZE 1024 /* state table size */ +#define NSTRINGS 500 /* maximum number of keywords */ +#define STRPOOLSIZ (NSTRINGS * 10) /* size of string pool */ + + +/* + * Do not change these + */ +#define EOS '\0' /* null */ +#define UNUSED -1 /* slot in ktab[] is unused */ +#define MARKED -2 /* slot in ktab[] is used, not assigned yet */ + + +/* + * Table-building tables + */ +int kmax = 0; /* largest index into ktab */ +int ktab[TABSIZE]; /* next state number (or -1) */ +int kcheck[TABSIZE]; /* check state number (or -1) */ +int kaccept[TABSIZE]; /* accept (or -1) */ +int kbase[TABSIZE]; /* ktab[] index for a state `i' */ +int nstates; /* number of states in kbase[] */ + +/* + * Temporary tables + */ +int tab[128]; /* tmp table for building ktab[] */ +int accept[128]; /* tmp table for building accept[] */ + +struct name_entry { + char *nstr; /* -> name string */ + int nval; /* = name's value */ +} namtab[NSTRINGS]; + +int nnames; /* number of keywords */ +char strpool[STRPOOLSIZ]; /* pool for keyword text */ +char *basename; /* -> string to prepend to array names */ +char uppername[100]; /* all-uppercase version of basename */ +int debug = 0; /* 1, enable debugging messages */ + +int comp_entry(); +void panic(char *); +int nmatch(int, char *, char *); +void wiredown(void); +void print_tables(void); +void dumptab(char *, char *, int *, int); +void traverse(int); + +int main(int argc, char **argv) { + register int i, /*j,*/ k, w; + char *s, *s1; + int found1; + int valid = 0; + //int base; + int state; + + if (argc != 2) + { + panic("bad commandline"); + } + + basename = argv[1]; + for (s = basename, s1 = uppername; *s; ++s, ++s1) + *s1 = (char)toupper(*s); + *s1 = EOS; + + /* + * init tables + */ + for (i = 0; i < TABSIZE; ++i) + { + ktab[i] = -1; + kcheck[i] = -1; + kaccept[i] = -1; + } + nstates = 0; + + /* + * read keyword list + * + */ + s = strpool; + while (gets(s) != NULL) + { + if (*s == '#' || !*s) /* ignore comment and empty lines */ + continue; + + namtab[nnames].nstr = s; + while (*s && !isspace(*s)) + ++s; + + if (!*s) + { + ++s; + goto empty; + } + + if (*s && isspace(*s)) + *s++ = '\0'; + + s1 = s; + while (*s1 && isspace(*s1)) + ++s1; + + if (!*s1) + { +empty: /* use previous entry + 1 */ + if (!nnames) /* complain if no previous entry */ + namtab[nnames].nval = 0; + else namtab[nnames].nval = namtab[nnames-1].nval + 1; + } + else if (isdigit(*s1)) + namtab[nnames].nval = atoi(s1); + else if (*s1 == '\'') + namtab[nnames].nval = *++s1; + else if (*s1 == '=') + { /* same as previous entry */ + if (!nnames) /* zero for first entry */ + namtab[nnames].nval = 0; + else namtab[nnames].nval = namtab[nnames-1].nval; + } + else + { + fprintf(stderr, "bad expression at '%s'\n", namtab[nnames].nstr); + exit(1); + } + + ++nnames; + if (nnames >= NSTRINGS) + panic("name table overflow"); + if (s >= &strpool[STRPOOLSIZ-100]) + panic("string table overflow"); + } + + qsort(namtab, nnames, sizeof(struct name_entry), comp_entry); + + /* + * compute table start indices + */ + found1 = 1; + + for (k = 1; found1; ++k) + { + found1 = 0; + + for (w = 0; w <= nnames; ++w) + { + if (w == 0 || + w == nnames || + !nmatch(k-1, namtab[w].nstr, namtab[w-1].nstr)) + { + if (w != 0 && + valid != 0) + { + wiredown(); + + if (k > 1) + { + state = 0; + for (i = 0; i < k-2; ++i) + { + if (ktab[kbase[state] + *(namtab[w-1].nstr + i)] < 0) + panic("table build error"); + else state = ktab[kbase[state] + + *(namtab[w-1].nstr + i)]; + } + + ktab[kbase[state] + *(namtab[w-1].nstr + k-2)] = nstates; + } + ++nstates; + found1 = 1; + } + + for (i = 0; i < 128; ++i) + { + tab[i] = UNUSED; + accept[i] = -1; + } + + valid = 0; + } + + if ( w >= nnames || + (int)strlen(namtab[w].nstr) < k) + { + continue; + } + + tab[*(namtab[w].nstr + k-1)] = MARKED; + if (*(namtab[w].nstr + k) == '\0') + { + accept[*(namtab[w].nstr + k-1)] = namtab[w].nval; + } + valid = 1; + } + } + + traverse(0); + print_tables(); + + return 0; +} + + +/* + * find position for set of characters; + * + */ +void wiredown(void) { + register int base; + register int i; + + for (base = 0; base < TABSIZE-128; ++base) + { + for (i = 0; i < 128; ++i) + if (ktab[base+i] != UNUSED && + tab[i] == MARKED) + break; + if (i >= 128) + break; + } + + if (base >= TABSIZE-128) + panic("Cannot build table (won't fit in tables)\n"); + + for (i = 0; i < 128; ++i) + if (tab[i] == MARKED) + { + ktab[base + i] = MARKED; + kaccept[base + i] = accept[i]; + kcheck[base + i] = nstates; + } + kbase[nstates] = base; + + if (kmax < base) + kmax = base; +} + + +void print_tables(void) { +// int i; + + printf("\n#ifdef DECL_%s\n", uppername); + printf("/*\n * keyword state-machine tables\n *\n */\n"); + dumptab("base", basename, kbase, nstates); + dumptab("tab", basename, ktab, kmax + 128); + dumptab("check", basename, kcheck, kmax + 128); + dumptab("accept", basename, kaccept, kmax + 128); + printf("#endif\n"); +} + + +void dumptab(char *tabname, char *tabprefix, int *table, int tabsize) { + int i, j; + + printf("\nint %s%s[%d] = {\n", + tabprefix, tabname, tabsize); + + for (i = j = 0; i < tabsize; ++i) + { + printf(" %d", table[i]); + if (i != tabsize-1) + putchar(','); + if (++j == 8) + { + j = 0; + putchar('\n'); + } + } + if (j) + putchar('\n'); + printf("};\n"); +} + + +int comp_entry(struct name_entry *ent1, struct name_entry *ent2) { + return strcmp(ent1->nstr, ent2->nstr); +} + + +int nmatch(int len, char *s1, char *s2) { + while (len--) + if (*s1++ != *s2++) + return 0; + return 1; +} + + +char nam[128]; +char *pnam; + +void traverse(int state) { + register int base, i;//, j; + char *s, c; + + if (state == 0) + { + printf("#ifdef DEF_%s\n", uppername); + printf("/*\n * Keyword definitions\n */\n"); + pnam = nam; + *pnam = 0; + } + + base = kbase[state]; + for (i = 0; i < 128; ++i) + if (kcheck[base + i] == state) + { + *pnam++ = (char)i; + *pnam = '\0'; + + for (s = nam; *s; ++s) + if (isupper(*s)) + break; + + if (kaccept[base + i] >= 0 && + !isupper(*s)) + { + printf("#define\t%s_", uppername); + for (s = nam; (c = *s); ++s) + { + if (c == '.') + c = '_'; + else if ((c >= 'a') && (c <= 'z')) + c -= 32; + printf("%c", c); + } + printf("\t%d\n", kaccept[base + i]); + } + + if (ktab[base + i] >= 0) + traverse(ktab[base + i]); + *--pnam = '\0'; + } + + if (state == 0) + printf("#endif\n"); +} + + +void panic(char *s) { + fprintf(stderr, "Panic: %s\n", s); + exit(1); +} diff --git a/kwtab b/kwtab new file mode 100644 index 0000000..c8404fc --- /dev/null +++ b/kwtab @@ -0,0 +1,76 @@ +d0 128 +d1 129 +d2 130 +d3 131 +d4 132 +d5 133 +d6 134 +d7 135 + +a0 136 +a1 137 +a2 138 +a3 139 +a4 140 +a5 141 +a6 142 +a7 143 + +sp 143 +usp 143 +ssp 144 +pc 145 +sr 146 +ccr 147 + +.equ 61 +equ 61 +.equr 148 +equr 148 +.regequ 148 +regequ 148 +set 149 +reg 150 +r0 151 +r1 152 +r2 153 +r3 154 +r4 155 +r5 156 +r6 157 +r7 158 +r8 159 +r9 160 +r10 161 +r11 162 +r12 163 +r13 164 +r14 165 +r15 166 +r16 167 +r17 168 +r18 169 +r19 170 +r20 171 +r21 172 +r22 173 +r23 174 +r24 175 +r25 176 +r26 177 +r27 178 +r28 179 +r29 180 +r30 181 +r31 182 +.ccdef 183 +ccdef 183 +defined 112 +referenced 113 +streq 118 +macdef 119 +time 120 +date 121 + + + diff --git a/listing.c b/listing.c new file mode 100644 index 0000000..e31c7ef --- /dev/null +++ b/listing.c @@ -0,0 +1,415 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// LISTING.C - Listing Output +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// ------------------------------------------------------------------------------------------------- +// 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 +// 012345678901234567890123456789012345678901234567890123456789012345678901234567 +// filename.... Reboot's Macro Assembler N.N.NN (Unknown) +// nnnnn aaaaaaaa dddddddddddddddddddd T source code +// nnnnn aaaaaaaa dddddddddddddddd +// nnnnn =vvvvvvvv + +#include "listing.h" +#include "version.h" +#include "token.h" +#include "procln.h" +#include "sect.h" +#include "error.h" + +char *list_fname; // Listing filename +char subttl[TITLESIZ]; // Current subtitle +int listing; // Listing level +int pagelen = 61; // Lines on a page +int nlines; // #lines on page so far +LONG lsloc; // `sloc' at start of line + +// Private +static int lcursect; // `cursect' at start of line +static int llineno; // `curlineno' at start of line +static int pageno; // Current page number +static int pagewidth; // #columns on a page +static int subflag; // 0, don't do .eject on subttl (set 1) +static char lnimage[IMAGESIZ]; // Image of output line +static char title[TITLESIZ]; // Current title +static char datestr[20]; // Current date dd-mon-yyyy +static char timestr[20]; // Current time hh:mm:ss [am|pm] +static char buf[IMAGESIZ]; // Buffer for numbers + +static char *month[16] = { "", "Jan", "Feb", "Mar", + "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", + "Dec", "", "", "" }; + +// +// --- Eject the Page (Print Empty Lines), Reset the Line Count and Bump the Page Number ----------- +// + +int eject(void) { + if(listing > 0) { + println("\f"); + nlines = 0; + } + return(0); +} + +// +// --- Return GEMDOS Format Date ------------------------------------------------------------------- +// + +VALUE dos_date(void) { + VALUE v; + struct tm *tm; + time_t tloc; + + time(&tloc); + tm = localtime(&tloc); + v = ((tm->tm_year - 80) << 9) | ((tm->tm_mon+1) << 5) | tm->tm_mday; + + return(v); +} + +// +// --- Return GEMDOS Format Time ------------------------------------------------------------------- +// + +VALUE dos_time(void) { + VALUE v; + struct tm *tm; + time_t tloc; + + time(&tloc); + tm = localtime(&tloc); + v = (tm->tm_hour << 11) | (tm->tm_min) << 5 | tm->tm_sec; + + return(v); +} + +// +// --- Generate a Time String ---------------------------------------------------------------------- +// + +void time_string(char *buf, VALUE time) { + int hour; + char *ampm; + + hour = (time >> 11); + if(hour > 12) { + hour -= 12; + ampm = "pm"; + } else ampm = "am"; + + sprintf(buf, "%d:%02d:%02d %s", + hour, (int)((time >> 5) & 0x3f), (int)((time & 0x1f) << 1), ampm); +} + +// +// --- Generate a Date String ---------------------------------------------------------------------- +// + +void date_string(char *buf, VALUE date) { + sprintf(buf, "%d-%s-%d", + (int)(date & 0x1f), month[(date >> 5) & 0xf], (int)((date >> 9) + 1980)); +} + +// +// ------------------------------------------------------------------------------------------------- +// Copy `n' Characters from `src' to `dest' (also stops on EOS in src). +// Does not null-terminate dest. +// ------------------------------------------------------------------------------------------------- +// + +void scopy(char *dest, char *src, int len) { + if(len < 0) + len = 1000; // Some large number + while(len-- && *src) + *dest++ = *src++; +} + +// +// ------------------------------------------------------------------------------------------------- +// Transform letters a-f in the address and data columns of the listing to uppercase. (People seem +// to like uppercase hex better in assembly-language listings....) +// ------------------------------------------------------------------------------------------------- +// + +void uc_ln(char *ln) { + int i; + char j; + + for(i = LOC_COL; i < SRC_COL; ++i) + if((j = ln[i]) >= 'a' && j <= 'f') + ln[i] = (char)(j - 0x20); +} + +// +// --- Fill Region `dest' with `len' Characters `c' and Null Terminate the Region ------------------ +// + +void lnfill(char *dest, int len, char chr) { + while(len--) + *dest++ = chr; + *dest = EOS; +} + +// +// --- Create Listing File with the Appropriate Name ----------------------------------------------- +// + +void list_setup(void) { + char fnbuf[FNSIZ]; + + strcpy(fnbuf, list_fname); + if(*fnbuf == EOS) { + strcpy(fnbuf, firstfname); + fext(fnbuf, ".prn", 1); + } + list_fname = NULL; + + if((list_fd = open(fnbuf, _OPEN_FLAGS, _PERM_MODE)) < 0) + cantcreat(fnbuf); +} + +// +// --- Tag Listing with a Character, Typically for Errors or Warnings ------------------------------ + +void taglist(char chr) { + lnimage[TAG_COL+1] = chr; +} + +// +// --- Print a Line to the Listing File ------------------------------------------------------------ +// + +void println(char *ln) { + unsigned int length; + + if(list_fname != NULL) // Create listing file, if necessary + list_setup(); + + length = strlen(ln); + write(list_fd, ln, length); + write(list_fd, "\n", 1L); +} + +// +// --- Ship Line `ln' Out; Do Page Breaks and Title Stuff ------------------------------------------ +// + +void ship_ln(char *ln) { + // If listing level is <= 0, then don't print anything + if(listing <= 0) + return; + + // Notice bottom of page + if(nlines >= pagelen - BOT_MAR) + eject(); + + // Print title, boilerplate, and subtitle at top of page + if(nlines == 0) { + ++pageno; + println(""); + date_string(datestr, dos_date()); + time_string(timestr, dos_time()); + sprintf(buf, + "%-40s%-20s Page %-4d %s %s RMAC %01i.%01i.%02i (%s)", + title, curfname, pageno, timestr, datestr, MAJOR, MINOR, PATCH, PLATFORM); + println(buf); + sprintf(buf, "%s", subttl); + println(buf); + println(""); + nlines = 4; + } + + println(ln); + ++nlines; +} + +// +// --- Initialize Listing Generator ---------------------------------------------------------------- +// + +void init_list(void) { + extern VALUE dos_date(), dos_time(); + + subflag = 0; + pageno = 0; + nlines = 0; + pagelen = 61; + pagewidth = 132; + strcpy(title, ""); + strcpy(subttl, ""); + date_string(datestr, dos_date()); + time_string(timestr, dos_time()); +} + +// +// --- Listing EOL --------------------------------------------------------------------------------- +// + +void listeol(void) { + CHUNK *ch; + char *p; + int col; + LONG count; + int fixcount; + + DEBUG printf("~list: lsloc=$%lx sloc=$%lx\n", lsloc, sloc); + + if(lsloc != sloc) { + sprintf(buf, "%08lx", lsloc); + scopy(lnimage+LOC_COL, buf, 8); + } + + if(llineno != curlineno) { + sprintf(buf, "%5d", llineno); + scopy(lnimage+LN_COL, buf, 5); + } + + // List bytes only when section stayed the same and the section is not a "no-data" (SBSS) + // section. An extra annoyance is caused by "ds.b" in a microprocessor mode, which prints + // out bytes of zero as if they had been deposited with dcb. The fix (kludge) is an extra + // variable which records the fact that a 'ds.x' directive generated all the data, and it + // shouldn't be listed + savsect(); // Update section variables + if(lcursect == cursect && (sect[lcursect].scattr & SBSS) == 0 && lsloc != sloc && just_bss==0) { + ch = sect[lcursect].sfcode; + for(; ch != NULL; ch = ch->chnext) + if(lsloc >= ch->chloc && lsloc < (ch->chloc + ch->ch_size)) + break; + + if(ch == NULL) { // Fatal: Can't find chunk holding code + + nochunk: + + interror(6); // Can't find generated code in section + } + + p = ch->chptr + (lsloc - ch->chloc); + col = DATA_COL; + fixcount = 0; + for(count = sloc - lsloc; count--; col += 2, ++lsloc) { + if(col >= DATA_END) { // Ship the line + col = DATA_COL; + uc_ln(lnimage); + ship_ln(lnimage); + lnfill(lnimage, SRC_COL, SPACE); // Fill with spaces + sprintf(buf, "%08lx", lsloc); + scopy(lnimage+LOC_COL, buf, 8); + } + + if(lsloc >= (ch->chloc + ch->ch_size)) { + if((ch = ch->chnext) == NULL) + goto nochunk; + p = ch->chptr; + } + + if(!fixcount) + fixcount = fixtest(lcursect, lsloc); + + if(fixcount) { + --fixcount; + strcpy(buf, "xx"); + ++p; // Advance anyway + } else + sprintf(buf, "%02x", *p++ & 0xff); + scopy(lnimage+col, buf, 2); + } + + if(col > DATA_COL) { // Flush partial line + uc_ln(lnimage); + ship_ln(lnimage); + } + } else { + uc_ln(lnimage); + ship_ln(lnimage); + } +} + +// +// --- Copy Current (Saved) Line to Output Buffer and Tag it with a Character ---------------------- +// + +void lstout(char tag) { + char *s; + char *d; + + lsloc = sloc; + lcursect = cursect; + llineno = curlineno; + + lnfill(lnimage, SRC_COL, SPACE); // Fill with spaces + lnimage[TAG_COL] = tag; + + // Copy line image and handle control characters + d = lnimage + SRC_COL; + for(s = lnbuf; *s; ++s) + if(*s >= 0x20 || *s == '\t') + *d++ = *s; + else { + *d++ = '^'; + *d++ = (char)(*s + 0x40); + } + *d++ = EOS; +} + +// +// --- Output a Value to Listing ------------------------------------------------------------------- +// + +int listvalue(VALUE v) { + sprintf(buf, "=%08lx", v); + scopy(lnimage+DATA_COL-1, buf, 9); + return(0); +} + +/* + * .subttl [-] "string" + * + * Set subtitle; + * o leading '-' supresses page eject + * o don't .eject on first .subttl, but do so on all other ones, + * o arrange not to print the .subttl directive + * + */ +int d_subttl(void) { + int ejectok; + + ejectok = 1; + if(*tok == '-') { + ejectok = 0; + ++tok; + } + + if(*tok != STRING) + return(error("missing string")); + strcpy(subttl, (char*)tok[1]); + + tok += 2; + if(ejectok && (subflag || pageno > 1)) // Always eject on pages 2+ + eject(); + subflag = 1; + + return(0); +} + +// +// --- Set title on titles not on the first page, do an eject and clobber the subtitle ------------- +// + +int d_title(void) { + if(*tok != STRING) + return(error("missing string")); + strcpy(title, (char*)tok[1]); + tok += 2; + + if(pageno > 1) { + strcpy(subttl, ""); + eject(); + } + + return(0); +} + diff --git a/listing.h b/listing.h new file mode 100644 index 0000000..329ea93 --- /dev/null +++ b/listing.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// LISTING.H - Listing Output +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __LISTING_H__ +#define __LISTING_H__ + +#include +#include "rmac.h" + +#define BOT_MAR 1 // #blank lines on bottom of page +#define IMAGESIZ 200 // Max size of a line of text +#define TITLESIZ 200 // Max size of a title +#define LN_COL 0 // Column for line numbers +#define LOC_COL 7 // Location ptr +#define DATA_COL 17 // Data start (for 20 chars, usually 16) +#define DATA_END (DATA_COL+20) // End+1th data column +#define TAG_COL 38 // Tag character +#define SRC_COL 40 // Source start + +// Globals, externals etc +extern char *list_fname; +extern int listing; +extern int pagelen; +extern int nlines; +extern LONG lsloc; + +// Prototypes +void init_list(void); +void ship_ln(char *); +void taglist(char); +void println(char *); +void listeol(void); +VALUE dos_date(void); +VALUE dos_time(void); +void lstout(char); +int listvalue(VALUE); +int d_subttl(void); +int d_title(void); + +#endif // __LISTING_H__ \ No newline at end of file diff --git a/mach.c b/mach.c new file mode 100644 index 0000000..414c835 --- /dev/null +++ b/mach.c @@ -0,0 +1,590 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MACH.C - Code Generation +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "mach.h" +#include "error.h" +#include "sect.h" +#include "direct.h" +#include "token.h" +#include "procln.h" +#include "risca.h" + +#define DEF_KW +#include "kwtab.h" + +// Common error messages +char *range_error = "expression out of range"; +char *abs_error = "illegal absolute expression"; +char *seg_error = "bad (section) expression"; +char *rel_error = "illegal relative address"; +char *siz_error = "bad size specified"; +char *undef_error = "undefined expression"; +char *fwd_error = "forward or undefined expression"; + +extern int ea0gen(WORD); +extern int ea1gen(WORD); + +// Include code tables +MNTAB machtab[] = { + { (WORD)-1, (unsigned long)-1L, (unsigned long)-1L, 0x0000, 0, m_badmode }, // 0 + #include "68ktab.h" + { 0, 0L, 0L, 0x0000, 0, m_unimp } // Last entry +}; + +// Register number << 9 +WORD reg_9[8] = { + 0, 1<<9, 2<<9, 3<<9, + 4<<9, 5<<9, 6<<9, 7<<9 +}; + +// SIZB==>00, SIZW==>01, SIZL==>10, SIZN==>01 << 6 +WORD siz_6[] = { + (WORD)-1, // n/a + 0, // SIZB + 1<<6, (WORD)-1, // SIZW, n/a + 2<<6, (WORD)-1, (WORD)-1, (WORD)-1, // SIZL, n/a, n/a, n/a + 1<<6 // SIZN +}; + +// Byte/word/long size for MOVE instrs +WORD siz_12[] = { + (WORD)-1, + 0x1000, // Byte + 0x3000, (WORD)-1, // Word + 0x2000, (WORD)-1, (WORD)-1, (WORD)-1, // Long + 0x3000 // Word (SIZN) +}; + +// Word/long size (0=.w, 1=.l) in bit 8 +WORD lwsiz_8[] = { + (WORD)-1, // n/a + (WORD)-1, // SIZB + 0, (WORD)-1, // SIZW, n/a + 1<<8, (WORD)-1, (WORD)-1, (WORD)-1, // SIZL, n/a, n/a, n/a + 0 // SIZN +}; + +// Addressing mode in bits 6..11 (register/mode fields are reversed) +WORD am_6[] = { + 00000, 01000, 02000, 03000, 04000, 05000, 06000, 07000, + 00100, 01100, 02100, 03100, 04100, 05100, 06100, 07100, + 00200, 01200, 02200, 03200, 04200, 05200, 06200, 07200, + 00300, 01300, 02300, 03300, 04300, 05300, 06300, 07300, + 00400, 01400, 02400, 03400, 04400, 05400, 06400, 07400, + 00500, 01500, 02500, 03500, 04500, 05500, 06500, 07500, + 00600, 01600, 02600, 03600, 04600, 05600, 06600, 07600, + 00700, 01700, 02700, 03700, 04700, 05700, 06700, 07700 +}; + +// Error messages +int m_unimp(void) { return((int)error("unimplemented mnemonic")); } +int m_badmode(void) { return((int)error("inappropriate addressing mode")); } + +int m_self(WORD inst) { D_word(inst); return(0);} + +// +// ------------------------------------------------------------------------------------------------- +// Do one EA in bits 0..5 +// +// Bits in `inst' have the following meaning: +// +// Bit zero specifies which ea (ea0 or ea1) to generate in the lower six bits of the instr. +// +// If bit one is set, the OTHER ea (the one that wasn't generated by bit zero) is generated after +// the instruction. Regardless of bit 0's value, ea0 is always deposited in memory before ea1. +// +// If bit two is set, standard size bits are set in the instr in bits 6 and 7. +// +// If bit four is set, bit three specifies which eaXreg to place in bits 9..11 of the instr. +// ------------------------------------------------------------------------------------------------- +// + +int m_ea(WORD inst, WORD siz) { + WORD flg; + + flg = inst; // Save flag bits + inst &= ~0x3f; // Clobber flag bits in instr + + if(flg & 4) // Install "standard" instr size bits + inst |= siz_6[siz]; + + if(flg & 16) { // OR-in register number + if(flg & 8) { + inst |= reg_9[a1reg]; // ea1reg in bits 9..11 + } else { + inst |= reg_9[a0reg]; // ea0reg in bits 9..11 + } + } + + if(flg & 1) { // Use am1 + inst |= am1 | a1reg; // Get ea1 into instr + D_word(inst); // Deposit instr + if(flg & 2) // Generate ea0 if requested + ea0gen(siz); + ea1gen(siz); // Generate ea1 + } else { // Use am0 + inst |= am0 | a0reg; // Get ea0 into instr + D_word(inst); // Deposit instr + ea0gen(siz); // Generate ea0 + if(flg & 2) // Generate ea1 if requested + ea1gen(siz); + } + + return(0); +} + +// +// --- Dx,Dy nnnnXXXnssnnnYYY If bit 0 of `inst' is set, install size bits in bits 6..7 ------------ +// + +int m_abcd(WORD inst, WORD siz) { + if(inst & 1) { // Install size bits + --inst; + inst |= siz_6[siz]; + } + + inst |= a0reg | reg_9[a1reg]; + D_word(inst); + + return(0); +} + +// +// --- {adda} ea,AREG ------------------------------------------------------------------------------ +// + +int m_adda(WORD inst, WORD siz) { + inst |= am0 | a0reg | lwsiz_8[siz] | reg_9[a1reg]; + D_word(inst); + ea0gen(siz); // Gen EA + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// If bit 0 of `inst' is 1, install size bits in bits 6..7 of instr. +// If bit 1 of `inst' is 1, install a1reg in bits 9..11 of instr. +// ------------------------------------------------------------------------------------------------- +// + +int m_reg(WORD inst, WORD siz) { + if(inst & 1) // Install size bits + inst |= siz_6[siz]; + if(inst & 2) // Install other register (9..11) + inst |= reg_9[a1reg]; + + inst &= ~7; // Clear off crufty bits + inst |= a0reg; // Install first register + D_word(inst); + + return(0); +} + +// +// --- #expr ---------------------------------------------------------------------------------- +// + +int m_imm(WORD inst, WORD siz) { + D_word(inst); + ea0gen(siz); + + return(0); +} + +// +// --- .b #expr -------------------------------------------------------------------------------- +// + +int m_imm8(WORD inst, WORD siz) { + siz = siz; + D_word(inst); + ea0gen(SIZB); + + return(0); +} + +// +// --- Dn,Dn ------------------------------------------------------------------------------- +// + +int m_shr(WORD inst, WORD siz) { + inst |= reg_9[a0reg] | a1reg | siz_6[siz]; + D_word(inst); + + return(0); +} + +// +// --- #n,Dn ------------------------------------------------------------------------------- +// + +int m_shi(WORD inst, WORD siz) { + inst |= a1reg | siz_6[siz]; + + if(a0exattr & DEFINED) { + if(a0exval > 8) + return(error(range_error)); + inst |= (a0exval & 7) << 9; + D_word(inst); + } else { + fixup(FU_QUICK, sloc, a0expr); + D_word(inst); + } + + return(0); +} + +// +// --- {bset, btst, bchg, bclr} -- #immed,ea -- Dn,ea ---------------------------------------------- +// + +int m_bitop(WORD inst, WORD siz) { + // Enforce instruction sizes + if(am1 == DREG) { // X,Dn must be .n or .l + if(siz & (SIZB|SIZW)) + return(error(siz_error)); + } else + if(siz & (SIZW|SIZL)) // X,ea must be .n or .b + return error(siz_error); + + // Construct instr and EAs + inst |= am1 | a1reg; + if(am0 == IMMED) { + D_word(inst); + ea0gen(SIZB); // Immediate bit number + } else { + inst |= reg_9[a0reg]; + D_word(inst); + } + + ea1gen(SIZB); // ea to bit-munch + + return(0); +} + +int m_dbra(WORD inst, WORD siz) { + VALUE v; + + siz = siz; + inst |= a0reg; + D_word(inst); + if(a1exattr & DEFINED) { + if((a1exattr & TDB) != cursect) + return(error(rel_error)); + v = a1exval - sloc; + + if(v + 0x8000 > 0x10000) + return(error(range_error)); + D_word(v); + } else { + fixup(FU_WORD|FU_PCREL|FU_ISBRA, sloc, a1expr); + D_word(0); + } + + return(0); +} + +// +// --- EXG ----------------------------------------------------------------------------------------- +// + +int m_exg(WORD inst, WORD siz) { + int m; + + siz = siz; + if(am0 == DREG && am1 == DREG) + m = 0x0040; // Dn,Dn + else if(am0 == AREG && am1 == AREG) + m = 0x0048; // An,An + else { + if(am0 == AREG) { // Dn,An or An,Dn + m = a1reg; // Get AREG into a1reg + a1reg = a0reg; + a0reg = m; + } + m = 0x0088; + } + inst |= m | reg_9[a0reg] | a1reg; + D_word(inst); + + return(0); +} + +// +// --- LINK ---------------------------------------------------------------------------------------- +// + +int m_link(WORD inst, WORD siz) { + siz = siz; + inst |= a0reg; + D_word(inst); + ea1gen(SIZW); + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Handle MOVE +// MOVE +// +// Optimize MOVE.L #,D0 to a MOVEQ +// ------------------------------------------------------------------------------------------------- +// + +int m_move(WORD inst, int siz) { + // Try to optimize to MOVEQ + if(siz == SIZL && am0 == IMMED && am1 == DREG && (a0exattr & (TDB|DEFINED)) == DEFINED && + a0exval + 0x80 < 0x100) { + m_moveq((WORD)0x7000, (WORD)0); + } else { + inst |= siz_12[siz] | am_6[am1] | reg_9[a1reg] | am0 | a0reg; + + D_word(inst); + if(am0 >= ADISP) ea0gen((WORD)siz); + if(am1 >= ADISP) ea1gen((WORD)siz); + } + + return(0); +} + +// +// --- move USP,An -- move An,USP ------------------------------------------------------------------ +// + +int m_usp(WORD inst, WORD siz) { + siz = siz; + if(am0 == AM_USP) inst |= a1reg; // USP,An + else inst |= a0reg; // An,USP + D_word(inst); + + return(0); +} + +// +// --- moveq --------------------------------------------------------------------------------------- +// + +int m_moveq(WORD inst, WORD siz) { + siz = siz; + if(!(a0exattr & DEFINED)) { // Arrange for future fixup + fixup(FU_BYTE|FU_SEXT, sloc+1, a0expr); + a0exval = 0; + } else + if(a0exval + 0x100 >= 0x200) + return(error(range_error)); + + inst |= reg_9[a1reg] | (a0exval & 0xff); + D_word(inst); + + return(0); +} + +// +// --- movep Dn,disp(An) -- movep disp(An),Dn ------------------------------------------------------ +// + +int m_movep(WORD inst, WORD siz) { + //WORD k; + + if(siz == SIZL) + inst |= 0x0040; + + if(am0 == DREG) { + inst |= reg_9[a0reg] | a1reg; + D_word(inst); + if(am1 == AIND) { + D_word(0); + } else + ea1gen(siz); + } else { + inst |= reg_9[a1reg] | a0reg; + D_word(inst); + if(am0 == AIND) { + D_word(0); + } else + ea0gen(siz); + } + + return(0); +} + +// +// --- Bcc -- BSR ---------------------------------------------------------------------------------- +// + +int m_br(WORD inst, WORD siz) { + VALUE v; + + if(a0exattr & DEFINED) { + if((a0exattr & TDB) != cursect) + return(error(rel_error)); + + v = a0exval - (sloc + 2); + + // Optimize branch instr. size + if(siz == SIZN) { + if(v != 0 && v + 0x80 < 0x100) { // Fits in .B + inst |= v & 0xff; + D_word(inst); + return(0); + } else { // Fits in .W + if(v + 0x8000 > 0x10000) + return(error(range_error)); + D_word(inst); + D_word(v); + return(0); + } + } + + if(siz == SIZB) { + if(v + 0x80 >= 0x100) + return(error(range_error)); + inst |= v & 0xff; + D_word(inst); + } else { + if(v + 0x8000 >= 0x10000) + return(error(range_error)); + D_word(inst); + D_word(v); + } + return(0); + } else + if(siz == SIZN) + siz = SIZW; + + if(siz == SIZB) { // .B + fixup(FU_BBRA|FU_PCREL|FU_SEXT, sloc, a0expr); + D_word(inst); + return(0); + } else { // .W + D_word(inst); + fixup(FU_WORD|FU_PCREL|FU_LBRA|FU_ISBRA, sloc, a0expr); + D_word(0); + } + + return(0); +} + +// +// --- ADDQ -- SUBQ -------------------------------------------------------------------------------- +// + +int m_addq(WORD inst, WORD siz) { + inst |= siz_6[siz] | am1 | a1reg; + + if(a0exattr & DEFINED) { + if(a0exval > 8 || a0exval == 0) // Range in 1..8 + return(error(range_error)); + inst |= (a0exval & 7) << 9; + D_word(inst); + } else { + fixup(FU_QUICK, sloc, a0expr); + D_word(inst); + } + ea1gen(siz); + + return(0); +} + +// +// --- trap #n ------------------------------------------------------------------------------------- +// + +int m_trap(WORD inst, WORD siz) { + siz = siz; + if(a0exattr & DEFINED) { + if(a0exattr & TDB) + return(error(abs_error)); + if(a0exval >= 16) + return(error(range_error)); + inst |= a0exval; + D_word(inst); + } else + return(error(undef_error)); + + return(0); +} + +// +// --- movem ,ea -- movem ea, -------------------------------------------------------- +// + +int m_movem(WORD inst, WORD siz) { + VALUE eval; + WORD i; + WORD w; + WORD rmask; + + if(siz & SIZB) return(error("bad size suffix")); + + if(siz == SIZL) inst |= 0x0040; + + if(*tok == '#') { // Handle #,ea + ++tok; + if(abs_expr(&eval) != OK) return(0); + if(eval >= 0x10000L) return(error(range_error)); + rmask = (WORD)eval; + goto immed1; + } + + if(*tok >= KW_D0 && *tok <= KW_A7) { // ,ea + if(reglist(&rmask) < 0) return(0); + + immed1: + + if(*tok++ != ',') return(error("missing comma")); + if(amode(0) < 0) return(0); + inst |= am0 | a0reg; + + if(!(amsktab[am0] & (C_ALTCTRL|M_APREDEC))) + return(error("invalid addressing mode")); + + // If APREDEC, reverse register mask + if(am0 == APREDEC) { + w = rmask; + rmask = 0; + for(i = 0x8000; i; i >>= 1, w >>= 1) + rmask = (WORD)((rmask << 1) | w & 1); + } + } else { // ea, + if(amode(0) < 0) return(0); + inst |= 0x0400 | am0 | a0reg; + if(*tok++ != ',') return(error("missing comma")); + if(*tok == EOL) return(error("missing register list")); + + if(*tok == '#') { // ea,# + ++tok; + if(abs_expr(&eval) != OK) return(0); + if(eval >= 0x10000) return(error(range_error)); + rmask = (WORD)eval; + } else + if(reglist(&rmask) < 0) return(0); + + if(!(amsktab[am0] & (C_CTRL|M_APOSTINC))) + return(error("invalid addressing mode")); + } + + D_word(inst); + D_word(rmask); + ea0gen(siz); + + return(0); +} + +// +// --- CLR.x An ==> SUBA.x An,An ------------------------------------------------------------------- +// + +int m_clra(WORD inst, WORD siz) { + inst |= a0reg | reg_9[a0reg] | lwsiz_8[siz]; + D_word(inst); + + return(0); +} + diff --git a/mach.h b/mach.h new file mode 100644 index 0000000..c76f109 --- /dev/null +++ b/mach.h @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MACH.H - Code Generation +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __MACH_H__ +#define __MACH_H__ + +#include "rmac.h" +#include "amode.h" + +// Globals, Externals etc +extern char *seg_error; +extern char *undef_error; +extern char *rel_error; +extern char *range_error; +extern char *abs_error; +extern MNTAB machtab[]; + +// Prototypes +int m_unimp(), m_badmode(), m_bad6mode(), m_bad6inst(); +int m_self(WORD); +int m_abcd(WORD, WORD); +int m_reg(WORD, WORD); +int m_imm(WORD, WORD); +int m_imm8(WORD, WORD); +int m_shi(WORD, WORD); +int m_shr(WORD, WORD); +int m_bitop(WORD, WORD); +int m_exg(WORD, WORD); +int m_ea(WORD, WORD); +int m_br(WORD, WORD); +int m_dbra(WORD, WORD); +int m_link(WORD, WORD); +int m_adda(WORD, WORD); +int m_addq(WORD, WORD); +int m_move(WORD, int); +int m_moveq(WORD, WORD); +int m_usp(WORD, WORD); +int m_movep(WORD, WORD); +int m_trap(WORD, WORD); +int m_movem(WORD, WORD); +int m_clra(WORD, WORD); + +#endif // __MACH_H__ \ No newline at end of file diff --git a/macro.c b/macro.c new file mode 100644 index 0000000..b8097f3 --- /dev/null +++ b/macro.c @@ -0,0 +1,489 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MACRO.C - Macro Definition and Invocation +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "macro.h" +#include "token.h" +#include "error.h" +#include "expr.h" +#include "listing.h" +#include "symbol.h" +#include "procln.h" +#include "direct.h" +#include "debug.h" + +LONG curuniq; // Current macro's unique number +TOKEN **argp; // Free spot in argptrs[] +int macnum; // Unique number for macro definition + +static LONG macuniq; // Unique-per-macro number +static SYM *curmac; // Macro currently being defined +static char **curmln; // Previous macro line (or NULL) +static VALUE argno; // Formal argument count + +static LONG *firstrpt; // First .rept line +static LONG *nextrpt; // Last .rept line +static int rptlevel; // .rept nesting level + +// +// --- Initialize Macro Processor ------------------------------------------------------------------ +// + +void init_macro(void) { + macuniq = 0; + macnum = 1; + argp = NULL; + ib_macro(); +} + +// +// ------------------------------------------------------------------------------------------------- +// Exit from a Macro; +// o pop any intervening include files and repeat blocks; +// o restore argument stack; +// o pop the macro. +// ------------------------------------------------------------------------------------------------- +// + +int exitmac(void) { + IMACRO *imacro; + TOKEN **p; + + // Pop intervening include files and .rept blocks + while(cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO) + fpop(); + + if(cur_inobj == NULL) + fatal("too many ENDMs"); + + // Restore + // o old arg context + // o old unique number + // ...and then pop the macro. + + imacro = cur_inobj->inobj.imacro; + curuniq = imacro->im_olduniq; + + p = --argp; + argp = (TOKEN **)*argp; + + fpop(); + + mjump_align = 0; + + return(0); +} + +// +// --- Add a Formal Argument to a Macro Definition ------------------------------------------------- +// + +int defmac2(char *argname) { + SYM *arg; + + if(lookup(argname, MACARG, (int)curmac->sattr) != NULL) + return(error("multiple formal argument definition")); + arg = newsym(argname, MACARG, (int)curmac->sattr); + arg->svalue = argno++; + + return(OK); +} + + +// +// ------------------------------------------------------------------------------------------------- +// Add a line to a macro definition; also print lines to listing file (if enabled). +// The last line of the macro (containing .endm) is not included in the macro. A label on that line +// will be lost. `endflg' is misleading here. It is -1 for all lines but the last one (.endm), +// when it is 0. +// ------------------------------------------------------------------------------------------------- +// + +int defmac1(char *ln, int endflg) { + PTR p; + LONG len; + + if(list_flag) { + listeol(); // Flush previous source line + lstout('.'); // Mark macro definition with period + } + + if(endflg) { + len = strlen(ln) + 1 + sizeof(LONG); + p.cp = amem(len); + *p.lp = 0; + strcpy(p.cp + sizeof(LONG), ln); + + // Link line of text onto end of list + if(curmln == NULL) + curmac->svalue = (VALUE)p.cp; + else + *curmln = p.cp; + curmln = (char **)p.cp; + return(1); // Keep looking + } + else + return(0); // Stop looking at the end +} + +// +// ------------------------------------------------------------------------------------------------- +// Define macro +// +// macro foo arg1,arg2,... +// : +// : +// endm +// +// Helper functions: +// ----------------- +// `defmac1' adds lines of text to the macro definition +// `defmac2' processes the formal arguments (and sticks them into the symbol table) +// ------------------------------------------------------------------------------------------------- +// + +int defmac(void) { + char *p; + SYM *mac; + + // Setup entry in symbol table, make sure the macro isn't a duplicate entry, and that + // it doesn't override any processor mnemonic or assembler directive. + if(*tok++ != SYMBOL) return(error("missing symbol")); + p = (char *)*tok++; + if(lookup(p, MACRO, 0) != NULL) + return(error("multiple macro definition")); + + curmac = mac = newsym(p, MACRO, 0); + mac->svalue = 0; + mac->sattr = (WORD)(macnum++); + + // Parse and define formal arguments in symbol table + if(*tok != EOL) { + argno = 0; + symlist(defmac2); + at_eol(); + } + + // Suck in the macro definition; we're looking for an ENDM symbol on a line + // by itself to terminate the definition. + curmln = NULL; + lncatch(defmac1, "endm "); + + return(0); +} + +// +// --- Add lines to a .rept definition ------------------------------------------------------------- +// + +int defr1(char *ln, int kwno) { + LONG len; + LONG *p; + + if(list_flag) { + listeol(); // Flush previous source line + lstout('#'); // Mark this a 'rept' block + } + + switch(kwno) { + case 0: // .endr + if(--rptlevel == 0) + return(0); + goto addln; + case 1: // .rept + ++rptlevel; + default: + + addln: + + // Allocate length of line + 1('\0') + LONG + len = strlen(ln) + 1 + sizeof(LONG); + p = (LONG *)amem(len); + *p = 0; + + strcpy((char*)(p + 1), ln); + + if(nextrpt == NULL) { + firstrpt = p; // First line of rept statement + } else { + *nextrpt = (LONG)p; + } + nextrpt = p; + + return(rptlevel); + } +} + +// +// --- Define a .rept block, this gets hairy because they can be nested ---------------------------- +// + +int defrept(void) { + INOBJ *inobj; + IREPT *irept; + VALUE eval; + + // Evaluate repeat expression + if(abs_expr(&eval) != OK) + return(ERROR); + + // Suck in lines for .rept block + firstrpt = NULL; + nextrpt = NULL; + rptlevel = 1; + lncatch(defr1, "endr rept "); + + // Alloc and init input object + if(firstrpt) { + inobj = a_inobj(SRC_IREPT); // Create a new REPT input object + irept = inobj->inobj.irept; + irept->ir_firstln = firstrpt; + irept->ir_nextln = NULL; + irept->ir_count = eval; + } + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Hand off lines of text to the function `lnfunc' until a line containing one of the directives in +// `dirlist' is encountered. Return the number of the keyword encountered (0..n) +// +// `dirlist' contains null-seperated terminated keywords. A final null terminates the list. +// Directives are compared to the keywords without regard to case. +// +// If `lnfunc' is NULL, then lines are simply skipped. +// If `lnfunc' returns an error, processing is stopped. +// +// `lnfunc' is called with an argument of -1 for every line but the last one, when it is called +// with an argument of the keyword number that caused the match. +// ------------------------------------------------------------------------------------------------- +// + +int lncatch(int (*lnfunc)(), char *dirlist) { + char *p; + int k; + + if(lnfunc != NULL) + ++lnsave; // Tell tokenizer to keep lines + + for(;;) { + if(tokln() == TKEOF) { + errors("encountered end-of-file looking for '%s'", dirlist); + fatal("cannot continue"); + } + + // Test for end condition. Two cases to handle: + // + // symbol: + p = NULL; + k = -1; + + if(*tok == SYMBOL) { + if((tok[2] == ':' || tok[2] == DCOLON)) { + if(tok[3] == SYMBOL) // label: symbol + p = (char *)tok[4]; + } else { + p = (char *)tok[1]; // symbol + } + } + + if(p != NULL) { + if(*p == '.') // ignore leading '.'s + ++p; + k = kwmatch(p, dirlist); + } + + // Hand-off line to function + // if it returns 0, and we found a keyword, stop looking. + // if it returns 1, hand off the line and keep looking. + if(lnfunc != NULL) + k = (*lnfunc)(lnbuf, k); + + if(!k) + break; + } + + if(lnfunc != NULL) + --lnsave; // Tell tokenizer to stop keeping lines + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// See if the string `kw' matches one of the keywords in `kwlist'. If so, return the number of +// the keyword matched. Return -1 if there was no match. +// Strings are compared without regard for case. +// ------------------------------------------------------------------------------------------------- +// + +int kwmatch(char *kw, char *kwlist) { + char *p; + char c1; + char c2; + int k; + + for(k = 0; *kwlist; ++k) { + for(p = kw;;) { + c1 = *kwlist++; + c2 = *p++; + + if(c2 >= 'A' && c2 <= 'Z') + c2 += 32; + + if(c1 == ' ' && c2 == EOS) + return(k); + + if(c1 != c2) + break; + } + + // Skip to beginning of next keyword in `kwlist' + while(*kwlist && *kwlist != ' ') + ++kwlist; + if(*kwlist== ' ') + ++kwlist; + } + + return(-1); +} + +// +// ------------------------------------------------------------------------------------------------- +// Invoke a macro +// o parse, count and copy arguments +// o push macro's string-stream +// ------------------------------------------------------------------------------------------------- +// + +int invokemac(SYM *mac, WORD siz) { + TOKEN *p = NULL; + IMACRO *imacro; + INOBJ *inobj; + int dry_run; + WORD nargs; + WORD arg_siz = 0; + TOKEN **argptr = NULL; + TOKEN *beg_tok; + + if((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main) { + error("macro cannot be used outside of .gpumain"); + return(ERROR); + } + + inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO + imacro = inobj->inobj.imacro; + imacro->im_siz = siz; + nargs = 0; + beg_tok = tok; + + for(dry_run = 1;; --dry_run) { + for(tok = beg_tok; *tok != EOL;) { + if(dry_run) ++nargs; + else *argptr++ = p; + + while(*tok != ',' && *tok != EOL) { + if(*tok == '\\' && tok[1] != EOL) ++tok; + switch((int)*tok) { + case CONST: + case SYMBOL: + case ACONST: + if(dry_run) arg_siz += sizeof(TOKEN), ++tok; + else *p++ = *tok++; + // FALLTHROUGH + default: + if(dry_run) arg_siz += sizeof(TOKEN), ++tok; + else *p++ = *tok++; + break; + } + } + + if(dry_run) arg_siz += sizeof(TOKEN); + else *p++ = EOL; + + if(*tok == ',') ++tok; + } + + // Allocate space for argument ptrs and so on and then go back and construct the arg frame + if(dry_run) { + if(nargs != 0) p = (TOKEN *)malloc((LONG)(arg_siz + 1)); + argptr = (TOKEN **)malloc((LONG)((nargs + 1) * sizeof(LONG))); + *argptr++ = (TOKEN *)argp; + argp = argptr; + } else + break; + } + + + // Setup imacro: + // o #arguments; + // o -> macro symbol; + // o -> macro definition string list; + // o save 'curuniq', to be restored when the macro pops; + // o bump `macuniq' counter and set 'curuniq' to it; + imacro->im_nargs = nargs; + imacro->im_macro = mac; + imacro->im_nextln = (LONG *)mac->svalue; + imacro->im_olduniq = curuniq; + curuniq = ++macuniq; + + DEBUG { + printf("nargs=%d\n", nargs); + for(nargs = 0; nargs < imacro->im_nargs; ++nargs) { + printf("arg%d=", nargs); + dumptok(argp[imacro->im_nargs - nargs - 1]); + } + } + + return(OK); +} + +// +// ------------------------------------------------------------------------------------------------- +// Setup inbuilt macros +// ------------------------------------------------------------------------------------------------- +// + +void ib_macro(void) { + SYM *mac; + + curmac = mac = newsym("mjump", MACRO, 0); + mac->svalue = 0; + mac->sattr = (WORD)(macnum++); + argno = 0; + defmac2("cc"); + defmac2("addr"); + defmac2("jreg"); + curmln = NULL; + defmac1(" nop", -1); + defmac1(" movei #\\addr,\\jreg", -1); + defmac1(" jump \\cc,(\\jreg)", -1); + defmac1(" nop", -1); + defmac1(" nop", -1); + + curmac = mac = newsym("mjr", MACRO, 0); + mac->svalue = 0; + mac->sattr = (WORD)(macnum++); + argno = 0; + defmac2("cc"); + defmac2("addr"); + curmln = NULL; + defmac1(" jr \\cc,\\addr", -1); + defmac1(" nop", -1); + defmac1(" nop", -1); + + curmac = mac = newsym("mpad", MACRO, 0); + mac->svalue = 0; + mac->sattr = (WORD)(macnum++); + argno = 0; + defmac2("size"); + curmln = NULL; + defmac1(" .rept (\\size/2)", -1); + defmac1(" nop", -1); + defmac1(" .endr", -1); +} \ No newline at end of file diff --git a/macro.h b/macro.h new file mode 100644 index 0000000..bd4f147 --- /dev/null +++ b/macro.h @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MACRO.H - Macro Definition and Invocation +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __MACRO_H__ +#define __MACRO_H__ + +#include "rmac.h" + +// Globals, externals etc +extern LONG curuniq; +extern TOKEN **argp; +extern int mjump_align; + +// Prototypes +void init_macro(void); +int exitmac(void); +int defmac(void); +int defrept(void); +int lncatch(int (*)(), char *); +int kwmatch(char *, char *); +int invokemac(SYM *, WORD); +void ib_macro(void); + +#endif // __MACRO_H__ + diff --git a/makefile b/makefile new file mode 100644 index 0000000..74ac5dc --- /dev/null +++ b/makefile @@ -0,0 +1,119 @@ +# +# RMAC - Reboot's Macro Assembler for the Atari Jaguar +# Copyright (C) 199x Landon Dyer, 2011 Reboot & Friends +# MAKEFILE for Non-Windows Compilation +# Currently tested against Cygwin under Windows XP Pro +# + +rm = /bin/rm -f +CC = cc + +CFLAGS = -g -D__GCCUNIX__ -I. + +SRCS = amode.c debug.c direct.c eagen.c error.c expr.c listing.c mach.c macro.c mark.c object.c procln.c risca.c rmac.c sect.c symbol.c token.c + +OBJS = amode.o debug.o direct.o eagen.o error.o expr.o listing.o mach.o macro.o mark.o object.o procln.o risca.o rmac.o sect.o symbol.o token.o + +# +# Build everything +# + +all : mntab.h 68ktab.h kwtab.h risckw.h rmac + +# +# Generated Sources for State Machines and Keyword, Directive and Mnemonic Definitions +# + +mntab.h : mntab 68kmn kwgen + cat mntab 68kmn | ./kwgen mn >mntab.h + +68ktab.h 68kmn : 68ktab 68ktab 68kgen + ./68kgen 68kmn <68ktab >68ktab.h + +kwtab.h : kwtab kwgen + ./kwgen kw kwtab.h + +risckw.h : kwtab kwgen + ./kwgen mr risckw.h + +# +# Build Tools +# + +kwgen.o : kwgen.c + $(CC) $(CFLAGS) -c kwgen.c + +kwgen : kwgen.o + $(CC) $(CFLAGS) -o kwgen kwgen.o + +68kgen.o : 68kgen.c + $(CC) $(CFLAGS) -c 68kgen.c + +68kgen : 68kgen.o + $(CC) $(CFLAGS) -o 68kgen 68kgen.o + +# +# Build RMAC Executable +# + +amode.o : amode.c + $(CC) $(CFLAGS) -c amode.c + +debug.o : debug.c + $(CC) $(CFLAGS) -c debug.c + +direct.o : direct.c + $(CC) $(CFLAGS) -c direct.c + +eagen.o : eagen.c + $(CC) $(CFLAGS) -c eagen.c + +error.o : error.c + $(CC) $(CFLAGS) -c error.c + +expr.o : expr.c + $(CC) $(CFLAGS) -c expr.c + +listing.o : listing.c + $(CC) $(CFLAGS) -c listing.c + +mach.o : mach.c + $(CC) $(CFLAGS) -c mach.c + +macro.o : macro.c + $(CC) $(CFLAGS) -c macro.c + +mark.o : mark.c + $(CC) $(CFLAGS) -c mark.c + +object.o : object.c + $(CC) $(CFLAGS) -c object.c + +procln.o : procln.c + $(CC) $(CFLAGS) -c procln.c + +risca.o : risca.c + $(CC) $(CFLAGS) -c risca.c + +rmac.o : rmac.c + $(CC) $(CFLAGS) -c rmac.c + +sect.o : sect.c + $(CC) $(CFLAGS) -c sect.c + +symbol.o : symbol.c + $(CC) $(CFLAGS) -c symbol.c + +token.o : token.c + $(CC) $(CFLAGS) -c token.c + +rmac : $(OBJS) + $(CC) $(CFLAGS) -o rmac $(OBJS) + +# +# Clean Build Environment +# + +clean: + $(rm) $(OBJS) kwgen.o 68kgen.o rmac kwgen 68kgen kwtab.h 68ktab.h mntab.h risckw.h + diff --git a/mark.c b/mark.c new file mode 100644 index 0000000..855d925 --- /dev/null +++ b/mark.c @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MARK.C - A record of things that are defined relative to any of the sections +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "mark.h" +#include "error.h" +#include "object.h" +#include "risca.h" + +MCHUNK *firstmch; // First mark chunk +MCHUNK *curmch; // Current mark chunk +PTR markptr; // Deposit point in current mark chunk +LONG mcalloc; // #bytes alloc'd to current mark chunk +LONG mcused; // #bytes used in current mark chunk +WORD curfrom; // Current "from" section + +// +// --- Initialize Marker --------------------------------------------------------------------------- +// + +void init_mark(void) { + firstmch = curmch = NULL; + mcalloc = mcused = 0; + curfrom = 0; +} + +// +// --- Wrap up marker (called after final mark is made) -------------------------------------------- +// + +void stopmark(void) { + if(curmch) { + *markptr.wp = MCHEND; // Mark end of block + curmch->mcused = mcused; // Update #used in mark block + } +} + +// +// --- Mark a word or longword relocatable --------------------------------------------------------- +// + +int rmark(int from, LONG loc, int to, int size, SYM *symbol) { + WORD w; + + if((mcalloc - mcused) < MIN_MARK_MEM) + amark(); + + w = (WORD)(size | to); + if(from != curfrom) + w |= MCHFROM; + if(symbol != NULL) + w |= MSYMBOL; + + mcused += sizeof(WORD) + sizeof(LONG); + *markptr.wp++ = w; + *markptr.lp++ = loc; + + if(w & MCHFROM) { + *markptr.wp++ = (WORD)from; + curfrom = (WORD)from; + mcused += sizeof(WORD); + } + + if(w & MSYMBOL) { + *markptr.sy++ = symbol; + mcused += sizeof(LONG); + } + + *markptr.wp = 0x0000; + + return(0); +} + +// +// --- Allocate another chunk of mark space -------------------------------------------------------- +// + +int amark(void) { + MCHUNK *p; + + // Alloc mark block header (and data) and set it up. + p = (MCHUNK *)amem((long)(sizeof(MCHUNK)) + MARK_ALLOC_INCR); + p->mcnext = NULL; + p->mcalloc = MARK_ALLOC_INCR; + p->mcptr.cp = (char *)(((char *)p) + sizeof(MCHUNK)); + + if(curmch) { // Link onto previous chunk + *markptr.wp++ = MCHEND; // Mark end of block + curmch->mcused = mcused; + curmch->mcnext = p; + } + if(!firstmch) + firstmch = p; + + curmch = p; // Setup global vars + markptr = p->mcptr; + mcalloc = MARK_ALLOC_INCR; + mcused = 0; + + return(0); +} + +// +// --- Make mark image for BSD .o file ------------------------------------------------------------- +// + +LONG bsdmarkimg(char *mp, LONG siz, LONG tsize, int reqseg) { + MCHUNK *mch; // Mark chunk + PTR p; // Source point from within mark chunk + WORD from; // Section fixups are currently FROM + WORD w; // A word (temp) + LONG loc; // Location (temp) + SYM *symbol; // Symbols (temp) + char *wp; // Pointer into raw relocation info + char *dp; // Deposit point for RELMOD info + LONG diff; // Difference to relocate (RELMOD) + LONG raddr, rflag = 0; // BSD relocation address and flags + LONG rsize; // Relocation size + int validsegment = 0; // Valid segment being processed + + rsize = 0; // Initialise relocation size + chptr = mp; + + from = 0; + for(mch = firstmch; mch != NULL; mch = mch->mcnext) + for(p = mch->mcptr;;) { + w = *p.wp++; // Next mark entry + + if(w & MCHEND) break; // End of mark chunk + + // Get mark record + symbol = NULL; + loc = *p.lp++; // Mark location + if(w & MCHFROM) { // Maybe change "from" section + from = *p.wp++; + if(obj_format == BSD) { + if(reqseg == TEXT) { // Requested segment is TEXT + if(from == TEXT) validsegment = 1; + else validsegment = 0; + } else { // Requested segment is DATA + if(from == DATA) validsegment = 1; + else validsegment = 0; + } + } + } + + if(w & MSYMBOL) // Maybe includes a symbol + symbol = *p.sy++; + + if(obj_format == BSD) { + raddr = loc; // Set relocation address + if(validsegment) + D_long(raddr); // Write relocation address + if(w & MPCREL) + rflag = 0x000000A0; // PC-relative fixup + else + rflag = 0x00000040; // Absolute fixup + if(w & MMOVEI) + rflag |= 0x00000001; + } + + // Compute mark position in relocation information; + // in RELMOD mode, get address of data to fix up. + if(from == DATA) + loc += tsize; + wp = (char *)(mp + loc); + + if(symbol) { + // Deposit external reference + if(obj_format == BSD) { + rflag |= 0x00000010; // Set external reloc flag bit + rflag |= (symbol->senv << 8); // Put symbol index in flags + if(symbol->sattre & RISCSYM) rflag |= 0x00000001; + if(validsegment) { + D_long(rflag); // Write relocation flags + rsize += 8; // Increment relocation size + } + } + + } else { + + if(obj_format == BSD) { + w &= TDB; // Set reloc flags to segment + switch(w) { + case TEXT: rflag |= 0x00000400; break; + case DATA: rflag |= 0x00000600; break; + case BSS: rflag |= 0x00000800; break; + } + if(validsegment) { + D_long(rflag); // Write relocation flags + rsize += 8; // Increment relocation size + } + w &= TDB; + if(validsegment) { + if(w & (DATA|BSS)) { + dp = objimage + BSDHDRSIZE + loc; + diff = ((LONG)(*dp++ & 0xff)) << 24; + diff |= ((LONG)(*dp++ & 0xff)) << 16; + diff |= ((LONG)(*dp++ & 0xff)) << 8; + diff |= (LONG)(*dp & 0xff); + DEBUG printf("diff=%lx ==> ", diff); + if(rflag & 0x01) + diff = ((diff >> 16) & 0x0000FFFF) | ((diff << 16) & 0xFFFF0000); + diff += sect[TEXT].sloc; + if(w == BSS) + diff += sect[DATA].sloc; + if(rflag & 0x01) + diff = ((diff >> 16) & 0x0000FFFF) | ((diff << 16) & 0xFFFF0000); + dp = objimage + BSDHDRSIZE + loc; + *dp++ = (char)(diff >> 24); + *dp++ = (char)(diff >> 16); + *dp++ = (char)(diff >> 8); + *dp = (char)diff; + DEBUG printf("%lx\n", diff); + } + } + } + } + } + + if(obj_format == BSD) // Return relocation size + return(rsize); + else + return(siz); +} diff --git a/mark.h b/mark.h new file mode 100644 index 0000000..52b8410 --- /dev/null +++ b/mark.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// MARK.H - A record of things that are defined relative to any of the sections +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __MARK_H__ +#define __MARK_H__ + +#include "rmac.h" +#include "sect.h" + +#define MARK_ALLOC_INCR 1024 // #bytes to alloc for more mark space +#define MIN_MARK_MEM (3*sizeof(WORD)+2*sizeof(LONG)) + +// Globals, Externals etc +extern MCHUNK *firstmch; + +// Prototypes +void init_mark(void); +void stopmark(void); +int rmark(int, LONG, int, int, SYM *); +int amark(void); +LONG bsdmarkimg(char *, LONG, LONG, int); + +#endif // __MARK_H__ \ No newline at end of file diff --git a/mntab b/mntab new file mode 100644 index 0000000..1ba0247 --- /dev/null +++ b/mntab @@ -0,0 +1,246 @@ +.org 0 +org 0 +.even 1 +even 1 +.68000 3 +.bss 4 +bss 4 +.data 5 +data 5 +.text 6 +text 6 +.offset 7 +offset 7 +.comm 8 +comm 8 +.init 9 +init 9 +.cargs 10 +cargs 10 +.goto 11 +goto 11 +.dc 12 +dc 12 +.ds 13 +ds 13 +.undefmac 14 +undefmac 14 +.macundef 14 +macundef 14 +.gpu 15 +.dsp 16 +.dcb 17 +dcb 17 +.equ 18 +equ 18 +.dump 20 +dump 20 +.incbin 21 +incbin 21 +.disable 22 +disable 22 +.enable 23 +enable 23 +.extern 24 +.globl 24 +extern 24 +globl 24 +.regbank0 25 +.regbank1 26 +.assert 28 +assert 28 +.include 33 +include 33 +.end 34 +end 34 +.list 38 +list 38 +.nlist 39 +nlist 39 +.nolist 39 +nolist 39 +.long 40 +long 40 +.phrase 41 +phrase 41 +.dphrase 42 +dphrase 42 +.qphrase 43 +qphrase 43 +.title 44 +title 44 +.subttl 45 +subttl 45 +.eject 46 +eject 46 +.error 47 +error 47 +.warn 48 +warn 48 +.noclear 49 +noclear 49 +.equrundef 50 +equrundef 50 +.regundef 50 +regundef 50 +.ccundef 51 +ccundef 51 +.print 52 +print 52 +.gpumain 53 +.jpad 54 +jpad 54 +.nojpad 55 +nojpad 55 +.fail 56 +fail 56 +.if 500 +if 500 +.else 501 +else 501 +.endif 502 +endif 502 +.iif 503 +iif 503 +.macro 504 +macro 504 +.endm 505 +endm 505 +.rept 506 +rept 506 +.endr 507 +endr 507 +.exitm 510 +exitm 510 +abcd 1001 +add 1003 +adda 1005 +addi 1006 +addq 1007 +addx 1008 +and 1010 +andi 1012 +asl 1015 +asr 1019 +bcc 1023 +bhs 1023 +bcs 1024 +blo 1024 +beq 1025 +bz 1025 +bze 1025 +bge 1026 +bgt 1027 +bhi 1028 +ble 1029 +bls 1030 +blt 1031 +bmi 1032 +bne 1033 +bnz 1033 +bpl 1034 +bvc 1035 +bvs 1036 +bchg 1037 +bclr 1041 +bra 1045 +bt 1045 +bset 1046 +bsr 1050 +btst 1051 +chk 1055 +clr 1056 +cmp 1058 +cmpa 1060 +cmpi 1061 +cmpm 1062 +dbcc 1063 +dbcs 1064 +dblo 1064 +dbeq 1065 +dbze 1065 +dbf 1066 +dbra 1066 +dbge 1067 +dbgt 1068 +dbhi 1069 +dbhs 1069 +dble 1070 +dbls 1071 +dblt 1072 +dbmi 1073 +dbne 1074 +dbnz 1074 +dbpl 1075 +dbt 1076 +dbvc 1077 +dbvs 1078 +divs 1079 +divu 1080 +eor 1081 +eori 1082 +exg 1085 +ext 1086 +illegal 1088 +jmp 1089 +jsr 1090 +lea 1091 +link 1092 +lsl 1093 +lsr 1097 +move 1101 +movea 1108 +movem 1109 +movep 1110 +moveq 1112 +muls 1113 +mulu 1114 +nbcd 1115 +neg 1116 +negx 1117 +nop 1118 +not 1119 +or 1120 +ori 1122 +pea 1125 +reset 1126 +rol 1127 +ror 1131 +roxl 1135 +roxr 1139 +rte 1143 +rtr 1144 +rts 1145 +sbcd 1146 +scc 1148 +shs 1148 +scs 1149 +slo 1149 +seq 1150 +sze 1150 +sf 1151 +sge 1152 +sgt 1153 +shi 1154 +sle 1155 +sls 1156 +slt 1157 +smi 1158 +sne 1159 +snz 1159 +spl 1160 +st 1161 +svc 1162 +svs 1163 +stop 1164 +sub 1165 +suba 1167 +subi 1168 +subq 1169 +subx 1170 +swap 1172 +tas 1173 +trap 1174 +trapv 1175 +tst 1176 +unlk 1177 diff --git a/object.c b/object.c new file mode 100644 index 0000000..b6a1520 --- /dev/null +++ b/object.c @@ -0,0 +1,158 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// OBJECT.C - Writing Object Files +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "object.h" +#include "sect.h" +#include "symbol.h" +#include "mark.h" +#include "error.h" +#include "risca.h" + +LONG symsize = 0; // Size of BSD symbol table +LONG strindx = 0x00000004; // BSD string table index +char *strtable; // Pointer to the symbol string table +char *objimage; // Global object image pointer + +// +// --- Add an entry to the BSD symbol table -------------------------------------------------------- +// + +char *constr_bsdsymtab(char *buf, SYM *sym, int globflag) { + LONG z; // Scratch long + WORD w1; // Scratch word + int w2; // Scratch long + + chptr = buf; // Point to buffer for deposit longs + D_long(strindx); // Deposit the symbol string index + + w1 = sym->sattr; // Obtain symbol attribute + w2 = sym->sattre; + z = 0; // Initialise resulting symbol flags + if(w1 & EQUATED) { + z = 0x02000000; // Set equated flag + } else { + switch(w1 & TDB) { + case TEXT: z = 0x04000000; break; // Set TEXT segment flag + case DATA: z = 0x06000000; break; // Set DATA segment flag + case BSS : z = 0x08000000; break; // Set BSS segment flag + } + } + if(globflag) z |= 0x01000000; // Set global flag if requested + D_long(z); // Deposit symbol attribute + + z = sym->svalue; // Obtain symbol value + w1 &= DATA|BSS; // Determine DATA or BSS flag + if(w1) + z += sect[TEXT].sloc; // If DATA or BSS add TEXT segment size + if(w1 & BSS) + z += sect[DATA].sloc; // If BSS add DATA segment size + D_long(z); // Deposit symbol value + + strcpy(strtable + strindx, sym->sname); + + strindx += strlen(sym->sname) + 1; // Incr string index incl null terminate + buf += 12; // Increment buffer to next record + symsize += 12; // Increment symbol table size + + return(buf); +} + +// +// --- Generate object file ------------------------------------------------------------------------ +// + +int object(WORD fd) { + LONG t; // Scratch long + LONG tds; // TEXT & DATA segment size + int i; // Temporary int + CHUNK *cp; // Chunk (for gather) + char *buf; // Scratch area + char *p; // Temporary ptr + LONG ssize; // Size of symbols + LONG trsize, drsize; // Size of relocations + + // Write requested object file... + switch(obj_format) { + case BSD: + ssize = ((LONG)sy_assign(NULL, NULL)); // Assign index numbers to the symbols + tds = sect[TEXT].sloc + sect[DATA].sloc; // Get size of TEXT and DATA segment + buf = malloc(0x400000); // Allocate 4mb object file image memory + if(buf == NULL) { + error("cannot allocate object file memory (in BSD mode)"); + return(ERROR); + } + memset(buf, 0, 0x400000); // Reset allocated memory + objimage = buf; // Set global object image pointer + strtable = malloc(0x200000); // Allocate 2mb scratch buffer + if(strtable == NULL) { + error("cannot allocate string table memory (in BSD mode)"); + return(ERROR); + } + memset(strtable, 0, 0x200000); // Reset allocated memory + + // Build object file header + chptr = buf; // Base of header + t = 0x00000107; + D_long(t); // Magic number + t = sect[TEXT].sloc; // TEXT size + D_long(t); + t = sect[DATA].sloc; // DATA size + D_long(t); + t = sect[BSS].sloc; // BSS size + D_long(t); + t = 0x00000000; + D_long(t); // Symbol size + D_long(t); // First entry (0L) + D_long(t); // TEXT relocation size + D_long(t); // BSD relocation size + + // Construct TEXT and DATA segments (without relocation changes) + p = buf + BSDHDRSIZE; + for(i = TEXT; i <= DATA; ++i) + for(cp = sect[i].sfcode; cp != NULL; cp = cp->chnext) { + copy(p, cp->chptr, cp->ch_size); + p += cp->ch_size; + } + + // Do relocation tables (and make changes to segment data) + p = buf + (BSDHDRSIZE + tds); // Move obj image ptr to reloc info + trsize = bsdmarkimg(p, tds, sect[TEXT].sloc, TEXT);// Do TEXT relocation table + chptr = buf + 24; // Point to relocation hdr entry + D_long(trsize); // Write the relocation table size + p = buf + (BSDHDRSIZE + tds + trsize); // Move obj image ptr to reloc info + drsize = bsdmarkimg(p, tds, sect[TEXT].sloc, DATA);// Do DATA relocation table + chptr = buf + 28; // Point to relocation hdr entry + D_long(drsize); // Write the relocation table size + + p = buf + (BSDHDRSIZE + tds + trsize + drsize); // Point to start of symbol table + sy_assign(p, constr_bsdsymtab); // Build symbol and string tables + chptr = buf + 16; // Point to sym table size hdr entry + D_long(symsize); // Write the symbol table size + + // Point to string table + p = buf + (BSDHDRSIZE + tds + trsize + drsize + symsize); + + memcpy(p, strtable, strindx); // Copy string table to object image + if(buf) free(strtable); // Free allocated memory + chptr = p; // Point to string table size long + D_long(strindx); // Write string table size + + // Write the BSD object file from the object image buffer + write(fd, buf, BSDHDRSIZE + tds + trsize + drsize + symsize + strindx + 4); + + if(buf) free(buf); // Free allocated memory + break; + } + + return(0); +} + + + + + + diff --git a/object.h b/object.h new file mode 100644 index 0000000..6d3776d --- /dev/null +++ b/object.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// OBJECT.H - Writing Object Files +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __OBJECT_H__ +#define __OBJECT_H__ + +#include "rmac.h" + +#define BSDHDRSIZE 0x20 // Size of BSD header + +// Globals, externals etc +extern char *objimage; + +// Prototypes +int object(WORD); + +#endif // __OBJECT_H__ diff --git a/parmode.h b/parmode.h new file mode 100644 index 0000000..93852fc --- /dev/null +++ b/parmode.h @@ -0,0 +1,226 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// PARMODE.C - Addressing Modes Parser Include +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +// This file is included (twice) to parse two addressing modes, into slightly different var names +{ + // Dn + // An + // # expression + if((*tok >= KW_D0) && (*tok <= KW_D7)) { + AMn = DREG; + AnREG = *tok++ & 7; + } else if((*tok >= KW_A0) && (*tok <= KW_A7)) { + AMn = AREG; + AnREG = *tok++ & 7; + } else if(*tok == '#') { + ++tok; + if(expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK) + return(ERROR); + AMn = IMMED; + } + // (An) + // (An)+ + // (An,Xn[.siz][*scale]) + // (PC,Xn[.siz][*scale]) + // (d16,An) + // (d8,An,Xn[.siz][*scale]) + // (d16,PC) + // (d8,PC,Xn[.siz][*scale]) + // ([bd,An],Xn,od) + // ([bd,An,Xn],od) + // ([bd,PC],Xn,od) + // ([bd,PC,Xn],od) + else if(*tok == '(') { + ++tok; + if((*tok >= KW_A0) && (*tok <= KW_A7)) { + AnREG = *tok++ & 7; + if(*tok == ')') { + ++tok; + if(*tok == '+') { + ++tok; + AMn = APOSTINC; + } else AMn = AIND; + goto AnOK; + } + AMn = AINDEXED; + goto AMn_IX0; // Handle ",Xn[.siz][*scale])" + } else if(*tok == KW_PC) { // (PC,Xn[.siz][*scale]) + ++tok; + AMn = PCINDEXED; + + // Common index handler; enter here with `tok' pointing at the comma. + + AMn_IX0: // Handle indexed with missing expr + + AnEXVAL = 0; + AnEXATTR = ABS | DEFINED; + + AMn_IXN: // Handle any indexed (tok -> a comma) + + if(*tok++ != ',') + goto badmode; + if(*tok < KW_D0 || *tok > KW_A7) + goto badmode; + AnIXREG = *tok++ & 15; + + switch((int)*tok) { // Index reg size: | .W | .L + case DOTW: + ++tok; + default: + AnIXSIZ = 0; + break; + case DOTL: + AnIXSIZ = 0x0800; + ++tok; + break; + case DOTB: // .B not allowed here... + goto badmode; + } + + if(*tok == '*') { // scale: *1, *2, *4, *8 + ++tok; + if(*tok++ != CONST || *tok > 8) + goto badmode; + + switch((int)*tok++) { + case 1: + break; + case 2: + AnIXSIZ |= TIMES2; + break; + case 4: + AnIXSIZ |= TIMES4; + break; + case 8: + AnIXSIZ |= TIMES8; + break; + default: + goto badmode; + } + } + + if(*tok++ != ')') // final ")" + goto badmode; + goto AnOK; + } else if(*tok == '[') { // ([... + goto unmode; + } else { // (expr... + if(expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK) + return ERROR; + if(*tok++ != ',') + goto badmode; + + if((*tok >= KW_A0) && (*tok <= KW_A7)) { + AnREG = *tok & 7; + ++tok; + if(*tok == ',') { + AMn = AINDEXED; + goto AMn_IXN; + } else if(*tok == ')') { + AMn = ADISP; + ++tok; + goto AnOK; + } else goto badmode; + } else if(*tok == KW_PC) { + if(*++tok == ',') { // expr(PC,Xn...) + AMn = PCINDEXED; + goto AMn_IXN; + } else if(*tok == ')') { + AMn = PCDISP; // expr(PC) + ++tok; + goto AnOK; + } else goto badmode; + } else goto badmode; + } + } else if(*tok=='-' && tok[1]=='(' && ((tok[2]>=KW_A0) && (tok[2]<=KW_A7)) && tok[3]==')') { + AMn = APREDEC; + AnREG = tok[2] & 7; + tok += 4; + } else if(*tok == KW_CCR) { + AMn = AM_CCR; + ++tok; + goto AnOK; + } else if(*tok == KW_SR) { + AMn = AM_SR; + ++tok; + goto AnOK; + } else if(*tok == KW_USP) { + AMn = AM_USP; + ++tok; + goto AnOK; + } + // expr + // expr.w + // expr.l + // d16(An) + // d8(An,Xn[.siz]) + // d16(PC) + // d8(PC,Xn[.siz]) + else { + if(expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK) + return ERROR; + + if(*tok == DOTW) { // expr.W + ++tok; + AMn = ABSW; + goto AnOK; + } else if(*tok != '(') { // expr[.L] + AMn = ABSL; + // Defined, absolute values from $FFFF8000..$00007FFF get optimized to absolute short + if((AnEXATTR & (TDB|DEFINED)) == DEFINED && (AnEXVAL + 0x8000) < 0x10000) + AMn = ABSW; + + if(*tok == DOTL) { // force .L + ++tok; + AMn = ABSL; + } + goto AnOK; + } + + ++tok; + if((*tok >= KW_A0) && (*tok <= KW_A7)) { + AnREG = *tok++ & 7; + if(*tok == ')') { + AMn = ADISP; + ++tok; + goto AnOK; + } + AMn = AINDEXED; + goto AMn_IXN; + } else if(*tok == KW_PC) { + if(*++tok == ')') { + AMn = PCDISP; + ++tok; + goto AnOK; + } + AMn = PCINDEXED; + goto AMn_IXN; + } + goto badmode; + } + + // Addressing mode OK + + AnOK: + ; +} + +// Cleanup dirty little macros +#undef AnOK +#undef AMn +#undef AnREG +#undef AnIXREG +#undef AnIXSIZ +#undef AnEXPR +#undef AnEXVAL +#undef AnEXATTR +#undef AnOEXPR +#undef AnOEXVAL +#undef AnOEXATTR +#undef AnESYM +#undef AMn_IX0 +#undef AMn_IXN diff --git a/procln.c b/procln.c new file mode 100644 index 0000000..3f8ade5 --- /dev/null +++ b/procln.c @@ -0,0 +1,611 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// PROCLN.C - Line Processing +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "procln.h" +#include "listing.h" +#include "amode.h" +#include "error.h" +#include "sect.h" +#include "expr.h" +#include "mach.h" +#include "direct.h" +#include "macro.h" +#include "symbol.h" +#include "risca.h" + +#define DEF_KW // Declare keyword values +#include "kwtab.h" // Incl generated keyword tables & defs + +#define DEF_MN // Incl 68k keyword definitions +#define DECL_MN // Incl 68k keyword state machine tables +#include "mntab.h" + +#define DEF_MR +#define DECL_MR +#include "risckw.h" + +IFENT *ifent; // Current ifent +static IFENT ifent0; // Root ifent +static IFENT *f_ifent; // Freelist of ifents +static int disabled; // Assembly conditionally disabled +int just_bss; // 1, ds.b in microprocessor mode +VALUE pcloc; // Value of "PC" at beginning of line +IFENT *ifent; // Current ifent +SYM *lab_sym; // Label on line (or NULL) + + +char extra_stuff[] = "extra (unexpected) text found after addressing mode"; +char *comma_error = "missing comma"; +char *syntax_error = "syntax error"; +char *locgl_error = "cannot GLOBL local symbol"; +char *lab_ignored = "label ignored"; + +// Table to convert an addressing-mode number to a bitmask. +LONG amsktab[0112] = { + M_DREG, M_DREG, M_DREG, M_DREG, + M_DREG, M_DREG, M_DREG, M_DREG, + + M_AREG, M_AREG, M_AREG, M_AREG, + M_AREG, M_AREG, M_AREG, M_AREG, + + M_AIND, M_AIND, M_AIND, M_AIND, + M_AIND, M_AIND, M_AIND, M_AIND, + + M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC, + M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC, + + M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC, + M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC, + + M_ADISP, M_ADISP, M_ADISP, M_ADISP, + M_ADISP, M_ADISP, M_ADISP, M_ADISP, + + M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED, + M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED, + + M_ABSW, // 070 + M_ABSL, // 071 + M_PCDISP, // 072 + M_PCINDEXED, // 073 + M_IMMED, // 074 + 0L, // 075 + 0L, // 076 + 0L, // 077 + M_ABASE, // 0100 + M_MEMPOST, // 0101 + M_MEMPRE, // 0102 + M_PCBASE, // 0103 + M_PCMPOST, // 0104 + M_PCMPRE, // 0105 + M_AM_USP, // 0106 + M_AM_SR, // 0107 + M_AM_CCR, // 0110 + M_AM_NONE // 0111 +}; // 0112 length + +// +// --- Initialize Line Processor ------------------------------------------------------------------- +// + +void init_procln(void) { + disabled = 0; + ifent = &ifent0; + f_ifent = ifent0.if_prev = NULL; + ifent0.if_state = 0; +} + +// +// --- Line Processor ------------------------------------------------------------------------------ +// + +void assemble(void) { + int state; // Keyword machine state (output) + int j; // Random int, must be fast + char *p; // Random char ptr, must be fast + TOKEN *tk; // First token in line + char *label; // Symbol (or NULL) + char *equate; // Symbol (or NULL) + int labtyp = 0; // Label type (':', DCOLON) + int equtyp = 0; // Equ type ('=', DEQUALS) + VALUE eval; // Expression value + WORD eattr; // Expression attributes + SYM *esym; // External symbol involved in expr. + WORD siz = 0; // Size suffix to mnem/diretve/macro + LONG amsk0, amsk1; // Address-type masks for ea0, ea1 + MNTAB *m; // Code generation table pointer + SYM *sy, *sy2; // Symbol (temp usage) + char *opname = NULL; // Name of dirctve/mnemonic/macro + int listflag; // 0: Don't call listeol() + int as68mode = 0; // 1: Handle multiple labels + WORD rmask; // Register list, for REG + int registerbank; // RISC register bank + int riscreg; // RISC register + + listflag = 0; // Initialise listing flag + + loop: // Line processing loop label + + if(tokln() == TKEOF) { // Get another line of tokens + if(list_flag && listflag) // Flush last line of source + listeol(); + if(ifent->if_prev != NULL) // Check conditional token + error("hit EOF without finding matching .endif"); + return; + } + + if(list_flag) { + if(listflag && listing > 0) listeol(); // Tell listing generator about EOL + lstout((char)(disabled ? '-' : lntag)); // Prepare new line for listing + listflag = 1; // OK to call `listeol' now + just_bss = 0; // Reset just_bss mode + } + + state = -3; // No keyword (just EOL) + label = NULL; // No label + lab_sym = NULL; // No (exported) label + equate = NULL; // No equate + tk = tok; // Save first token in line + pcloc = (VALUE)sloc; // Set beginning-of-line PC + + loop1: // Internal line processing loop + + if(*tok == EOL) // Restart loop if end-of-line + goto loop; + + if(*tok != SYMBOL) { // First token MUST be a symbol + error(syntax_error); + goto loop; + } + + j = (int)tok[2]; // Skip equates (normal statements) + if(j == '=' || j == DEQUALS || j == SET || j == REG || j == EQUREG || j == CCDEF) { + equate = (char *)tok[1]; + equtyp = j; + tok += 3; + goto normal; + } + + if(j == ':' || j == DCOLON) { // Skip past label (but record it) + + as68label: + + label = (char *)tok[1]; // Get label name + labtyp = tok[2]; // Get label type + tok += 3; // Go to next line token + + // Handle multiple labels; if there's another label, go process it, + // and come back at `as68label' above. + if(as68_flag) { + as68mode = 0; + if(*tok == SYMBOL && tok[2] == ':') { + as68mode = 1; + goto do_label; + } + } + } + + if(*tok == EOL) // EOL is legal here... + goto normal; + + if(*tok++ != SYMBOL) { // Next token MUST be a symbol + error(syntax_error); + goto loop; + } + opname = p = (char *)*tok++; // Store opcode name here + + // Check to see if the SYMBOL is a keyword (a mnemonic or directive). + // On output, `state' will have one of the values: + // -3 there was no symbol (EOL) + // -2..-1 the symbol didn't match any keyword + // 0..499 vanilla directives (dc, ds, etc.) + // 500..999 electric directives (macro, rept, etc.) + // 1000..+ mnemonics (move, lsr, etc.) + for(state = 0; state >= 0;) { + j = mnbase[state] + (int)tolowertab[*p]; + if(mncheck[j] != state) { // Reject, character doesn't match + state = -1; // No match + break; + } + if(!*++p) { // Must accept or reject at EOS + state = mnaccept[j]; // (-1 on no terminal match) + break; + } + state = mntab[j]; + } + + // Check for ".b" ".w" ".l" after directive, macro or mnemonic. + siz = SIZN; + if(*tok == DOTW) + siz = SIZW, ++tok; + else if(*tok == DOTL) + siz = SIZL, ++tok; + else if(*tok == DOTB) + siz = SIZB, ++tok; + + // Do special directives (500..999) (These must be handled in "real time") + if(state >= 500 && state < 1000) + switch(state) { + case MN_IF: + d_if(); + goto loop; + case MN_ELSE: + d_else(); + goto loop; + case MN_ENDIF: + d_endif(); + goto loop; + case MN_IIF: // .iif --- immediate if + if(disabled || expr(exprbuf, &eval, &eattr, &esym) != OK) + goto loop; + if(!(eattr & DEFINED)) { + error(undef_error); + goto loop; + } + if(*tok++ != ',') { + error(comma_error); + goto loop; + } + if(eval == 0) + goto loop; + goto loop1; + case MN_MACRO: // .macro --- macro definition + if(!disabled) { + if(label != NULL) + warn(lab_ignored); + defmac(); + } + goto loop; + case MN_EXITM: // .exitm --- exit macro + case MN_ENDM: // .endm --- same as .exitm + if(!disabled) { + if(label != NULL) + warn(lab_ignored); + exitmac(); + } + goto loop; + case MN_REPT: + if(!disabled) { + if(label != NULL) + warn(lab_ignored); + defrept(); + } + goto loop; + case MN_ENDR: + if(!disabled) + error("mis-nested .endr"); + goto loop; + } + + normal: + + if(disabled) // Conditionally disabled code + goto loop; + + // Do equates + if(equate != NULL) { + j = 0; // Pick global or local sym enviroment + if(*equate == '.') + j = curenv; + + sy = lookup(equate, LABEL, j); + if(sy == NULL) { + sy = newsym(equate, LABEL, j); + sy->sattr = 0; + if(equtyp == DEQUALS) { + if(j) { // Can't GLOBAL a local symbol + error(locgl_error); + goto loop; + } + sy->sattr = GLOBAL; + } + } else if((sy->sattr & DEFINED) && equtyp != SET) { + if((equtyp == EQUREG) && (sy->sattre & UNDEF_EQUR)) { + sy->sattre |= ~UNDEF_EQUR; + sy->svalue = 0; + } else if((equtyp == CCDEF) && (sy->sattre & UNDEF_CC)) { + sy->sattre |= ~UNDEF_CC; + sy->svalue = 0; + } else { + errors("multiple equate to '%s'", sy->sname); + goto loop; + } + } + + // Put symbol in "order of definition" list + if(!(sy->sattr & SDECLLIST)) sym_decl(sy); + + // Parse value to equate symbol to; + // o .equr + // o .reg + // o everything else + if(equtyp == EQUREG) { + if(!rgpu && !rdsp) { // Check that we are in a RISC section + error(".equr/.regequ must be defined in .gpu/.dsp section"); + goto loop; + } + if((*tok >= KW_R0) && (*tok <= KW_R31)) { // Check for register to equate to + sy->sattre = EQUATEDREG | RISCSYM; // Mark as equated register + riscreg = (*tok - KW_R0); + sy->sattre |= (riscreg << 8); // Store register number + if((tok[1] == ',') && (tok[2] == CONST)) { + tok += 3; + if(*tok == 0) registerbank = BANK_0; + else if(*tok == 1) registerbank = BANK_1; + else registerbank = BANK_N; + } else { + registerbank = BANK_N; + } + sy->sattre |= regbank; // Store register bank + eattr = ABS | DEFINED | GLOBAL; + eval = 0x80000080 + (riscreg) + (registerbank << 8); + tok++; + } else if(tok[0] == SYMBOL) { // Checking for a register symbol + sy2 = lookup((char *)tok[1], LABEL, j); + if(!sy2 || !(sy2->sattre & EQUATEDREG)) { // Make sure symbol is a valid equreg + error("invalid GPU/DSP .equr/.regequ definition"); + goto loop; + } else { + eattr = ABS | DEFINED | GLOBAL; // Copy symbols attributes + sy->sattre = sy2->sattre; + eval = (sy2->svalue & 0xFFFFF0FF); + tok += 2; + } + } else { + error("invalid GPU/DSP .equr/.regequ definition"); + goto loop; + } + } else if(equtyp == REG) { + if(reglist(&rmask) < 0) + goto loop; + eval = (VALUE)rmask; + eattr = ABS | DEFINED; + } else if(equtyp == CCDEF) { + sy->sattre |= EQUATEDCC; + eattr = ABS | DEFINED | GLOBAL; + if(tok[0] == SYMBOL) { + sy2 = lookup((char *)tok[1], LABEL, j); + if(!sy2 || !(sy2->sattre & EQUATEDCC)) { + error("invalid gpu/dsp .ccdef definition"); + goto loop; + } else { + eattr = ABS | DEFINED | GLOBAL; + sy->sattre = sy2->sattre; + eval = sy2->svalue; + tok += 2; + } + } else + if(expr(exprbuf, &eval, &eattr, &esym) != OK) + goto loop; + } else if(*tok == SYMBOL) { //equ a equr + sy2 = lookup((char *)tok[1], LABEL, j); + if(sy2 && (sy2->sattre & EQUATEDREG)) { + sy->stype = sy2->stype; + sy->sattr = sy2->sattr; + sy->sattre = sy2->sattre; + sy->svalue = (sy2->svalue & 0xFFFFF0FF); + goto loop; + } else + if(expr(exprbuf, &eval, &eattr, &esym) != OK) + goto loop; + } else + if(expr(exprbuf, &eval, &eattr, &esym) != OK) + goto loop; + + if(!(eattr & DEFINED)) { + error(undef_error); + goto loop; + } + + + sy->sattr |= eattr | EQUATED; // Symbol inherits value and attributes + sy->svalue = eval; + if(list_flag) // Put value in listing + listvalue(eval); + + at_eol(); // Must be at EOL now + goto loop; + } + + // Do labels + if(label != NULL) { + + do_label: + + j = 0; + if(*label == '.') + j = curenv; + sy = lookup(label, LABEL, j); + + if(sy == NULL) { + sy = newsym(label, LABEL, j); + sy->sattr = 0; + sy->sattre = RISCSYM; + } else if(sy->sattr & DEFINED) { + errors("multiply-defined label '%s'", label); + goto loop; + } + + // Put symbol in "order of definition" list + if(!(sy->sattr & SDECLLIST)) sym_decl(sy); + + if(orgactive) { + sy->svalue = orgaddr; + sy->sattr |= ABS | DEFINED | EQUATED; + } else { + sy->svalue = sloc; + sy->sattr |= DEFINED | cursect; + } + + lab_sym = sy; + if(!j) + ++curenv; + + if(labtyp == DCOLON) { // Make label global + if(j) { + error(locgl_error); + goto loop; + } + sy->sattr |= GLOBAL; + } + + // If we're in as68 mode, and there's another label, go back and handle it + if(as68_flag && as68mode) + goto as68label; + } + + // Punt on EOL + if(state == -3) + goto loop; + + // If we are in GPU or DSP mode and still in need of a mnemonic then search for one + if((rgpu || rdsp) && (state < 0 || state >= 1000)) { + for(state = 0, p = opname; state >= 0;) { + j = mrbase[state] + (int)tolowertab[*p]; + if(mrcheck[j] != state) { // Reject, character doesn't match + state = -1; // No match + break; + } + + if(!*++p) { // Must accept or reject at EOS + state = mraccept[j]; // (-1 on no terminal match) + break; + } + state = mrtab[j]; + } + + // Call RISC code generator if we found a mnemonic + if(state >= 3000) { + risccg(state); + goto loop; + } + } + + // Invoke macro or complain about bad mnemonic + if(state < 0) { + if((sy = lookup(opname, MACRO, 0)) != NULL) + invokemac(sy, siz); + else errors("unknown op '%s'", opname); + goto loop; + } + + // Call directive handlers + if(state < 500) { + (*dirtab[state])(siz); + goto loop; + } + + // Do mnemonics + // o can't deposit instrs in BSS or ABS + // o do automatic .EVEN for instrs + // o allocate space for largest possible instr + // o can't do ".b" operations with an address register + if(scattr & SBSS) { + error("cannot initialize non-storage (BSS) section"); + goto loop; + } + + if(sloc & 1) // Automatic .even + auto_even(); + + if(challoc - ch_size < 18) // Make sure have space in current chunk + chcheck(0L); + + m = &machtab[state - 1000]; + if(m->mnattr & CGSPECIAL) { // Call special-mode handler + (*m->mnfunc)(m->mninst, siz); + goto loop; + } + + if(amode(1) < 0) // Parse 0, 1 or 2 addr modes + goto loop; + + if(*tok != EOL) + error(extra_stuff); + + amsk0 = amsktab[am0]; + amsk1 = amsktab[am1]; + + // Catch attempts to use ".B" with an address register (yes, this check does work at this level) + if(siz == SIZB && (am0 == AREG || am1 == AREG)) { + error("cannot use '.b' with an address register"); + goto loop; + } + + for(;;) { + if((m->mnattr & siz) && (amsk0 & m->mn0) != 0 && (amsk1 & m->mn1) != 0) { + (*m->mnfunc)(m->mninst, siz); + goto loop; + } + m = &machtab[m->mncont]; + } +} + +// +// --- .if, Start Conditional Assembly ------------------------------------------------------------- +// + +int d_if(void) { + IFENT *rif; + WORD eattr; + VALUE eval; + SYM *esym; + + // Alloc an IFENTRY + if((rif = f_ifent) == NULL) rif = (IFENT *)amem((LONG)sizeof(IFENT)); + else f_ifent = rif->if_prev; + + rif->if_prev = ifent; + ifent = rif; + + if(!disabled) { + if(expr(exprbuf, &eval, &eattr, &esym) != OK) return(0); + if((eattr & DEFINED) == 0) return(error(undef_error)); + disabled = !eval; + } + rif->if_state = (WORD)disabled; + return(0); +} + +// +// --- .else, Do Alternate Case For .if ------------------------------------------------------------ +// + +int d_else(void) { + IFENT *rif; + + rif = ifent; + + if(rif->if_prev == NULL) return(error("mismatched .else")); + + if(disabled) disabled = rif->if_prev->if_state; + else disabled = 1; + + rif->if_state = (WORD)disabled; + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// .endif, End of conditional assembly block +// This is also called by fpop() to pop levels of IFENTs in case a macro or include file exits +// early with `exitm' or `end'. +// ------------------------------------------------------------------------------------------------- +// + +int d_endif(void) { + IFENT *rif; + + rif = ifent; + if(rif->if_prev == NULL) return(error("mismatched .endif")); + + ifent = rif->if_prev; + disabled = rif->if_prev->if_state; + rif->if_prev = f_ifent; + f_ifent = rif; + return(0); +} + diff --git a/procln.h b/procln.h new file mode 100644 index 0000000..2544867 --- /dev/null +++ b/procln.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// PROCLN.H - Line Processing +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __PROCLN_H__ +#define __PROCLN_H__ + +#include "rmac.h" +#include "token.h" + +// Globals, externals etc +extern IFENT *ifent; +extern char *comma_error; +extern char *locgl_error; +extern char *syntax_error; +extern int just_bss; +extern VALUE pcloc; +extern IFENT *ifent; +extern SYM *lab_sym; +extern char extra_stuff[]; +extern LONG amsktab[]; + +// Prototypes +void init_procln(void); +void assemble(void); +int eject(void); +int d_if(void); +int d_else(void); +int d_endif(void); +int at_eol(void); + +#endif // __PROCLN_H__ \ No newline at end of file diff --git a/risca.c b/risca.c new file mode 100644 index 0000000..2545158 --- /dev/null +++ b/risca.c @@ -0,0 +1,715 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// RISCA.C - GPU/DSP Assembler +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "risca.h" +#include "error.h" +#include "sect.h" +#include "token.h" +#include "expr.h" +#include "direct.h" +#include "mark.h" +#include "amode.h" + +#define DEF_MR // Declar keyword values +#include "risckw.h" // Incl generated risc keywords + +#define DEF_KW // Declare keyword values +#include "kwtab.h" // Incl generated keyword tables & defs + +unsigned altbankok = 0; // Ok to use alternate register bank +unsigned orgactive = 0; // RISC org directive active +unsigned orgaddr = 0; // Org'd address +unsigned orgwarning = 0; // Has an ORG warning been issued +int jpad = 0; +unsigned previousop = 0; // Used for NOP padding checks +unsigned currentop = 0; // Used for NOP padding checks +unsigned mjump_defined, mjump_dest; // mjump macro flags, values etc + +char reg_err[] = "missing register R0...R31"; + +// Jaguar Jump Condition Names +char condname[MAXINTERNCC][5] = { + "NZ", "Z", "NC", "NCNZ", "NCZ", "C", "CNZ", "CZ", "NN", "NNNZ", "NNZ", "N", "N_NZ", "N_Z ", + "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO", "PL", "MI", "F" +}; + +// Jaguar Jump Condition Numbers +char condnumber[] = {1, 2, 4, 5, 6, 8, 9, 10, 20, 21, 22, 24, 25, 26, + 0, 0, 1, 2, 4, 4, 5, 8, 8, 20, 24, 31}; + +struct opcoderecord roptbl[] = { + { MR_ADD, RI_TWO, 0 }, + { MR_ADDC, RI_TWO, 1 }, + { MR_ADDQ, RI_NUM_32, 2 }, + { MR_ADDQT, RI_NUM_32, 3 }, + { MR_SUB, RI_TWO, 4 }, + { MR_SUBC, RI_TWO, 5 }, + { MR_SUBQ, RI_NUM_32, 6 }, + { MR_SUBQT, RI_NUM_32, 7 }, + { MR_NEG, RI_ONE, 8 }, + { MR_AND, RI_TWO, 9 }, + { MR_OR, RI_TWO, 10 }, + { MR_XOR, RI_TWO, 11 }, + { MR_NOT, RI_ONE, 12 }, + { MR_BTST, RI_NUM_31, 13 }, + { MR_BSET, RI_NUM_31, 14 }, + { MR_BCLR, RI_NUM_31, 15 }, + { MR_MULT, RI_TWO, 16 }, + { MR_IMULT, RI_TWO, 17 }, + { MR_IMULTN, RI_TWO, 18 }, + { MR_RESMAC, RI_ONE, 19 }, + { MR_IMACN, RI_TWO, 20 }, + { MR_DIV, RI_TWO, 21 }, + { MR_ABS, RI_ONE, 22 }, + { MR_SH, RI_TWO, 23 }, + { MR_SHLQ, RI_NUM_32, 24 + SUB32 }, + { MR_SHRQ, RI_NUM_32, 25 }, + { MR_SHA, RI_TWO, 26 }, + { MR_SHARQ, RI_NUM_32, 27 }, + { MR_ROR, RI_TWO, 28 }, + { MR_RORQ, RI_NUM_32, 29 }, + { MR_ROLQ, RI_NUM_32, 29 + SUB32 }, + { MR_CMP, RI_TWO, 30 }, + { MR_CMPQ, RI_NUM_15, 31 }, + { MR_SAT8, RI_ONE, 32 + GPUONLY }, + { MR_SUBQMOD, RI_NUM_32, 32 + DSPONLY }, + { MR_SAT16, RI_ONE, 33 + GPUONLY }, + { MR_SAT16S, RI_ONE, 33 + DSPONLY }, + { MR_MOVEQ, RI_NUM_31, 35 }, + { MR_MOVETA, RI_TWO, 36 }, + { MR_MOVEFA, RI_TWO, 37 }, + { MR_MOVEI, RI_MOVEI, 38 }, + { MR_LOADB, RI_LOADN, 39 }, + { MR_LOADW, RI_LOADN, 40 }, + { MR_LOADP, RI_LOADN, 42 + GPUONLY }, + { MR_SAT32S, RI_ONE, 42 + DSPONLY }, + { MR_STOREB, RI_STOREN, 45 }, + { MR_STOREW, RI_STOREN, 46 }, + { MR_STOREP, RI_STOREN, 48 + GPUONLY }, + { MR_MIRROR, RI_ONE, 48 + DSPONLY }, + { MR_JUMP, RI_JUMP, 52 }, + { MR_JR, RI_JR, 53 }, + { MR_MMULT, RI_TWO, 54 }, + { MR_MTOI, RI_TWO, 55 }, + { MR_NORMI, RI_TWO, 56 }, + { MR_NOP, RI_NONE, 57 }, + { MR_SAT24, RI_ONE, 62 }, + { MR_UNPACK, RI_ONE, 63 + GPUONLY }, + { MR_PACK, RI_ONE, 63 + GPUONLY }, + { MR_ADDQMOD, RI_NUM_32, 63 + DSPONLY }, + { MR_MOVE, RI_MOVE, 0 }, + { MR_LOAD, RI_LOAD, 0 }, + { MR_STORE, RI_STORE, 0 } +}; + +// +// --- Convert a String to Uppercase --------------------------------------------------------------- +// + +void strtoupper(char *s) { + while(*s) { + *s = (char)(toupper(*s)); + s++; + } +} + +// +// --- Build RISC Instruction Word ----------------------------------------------------------------- +// + +void risc_instruction_word(unsigned short parm, int reg1, int reg2) { + int value = 0xE400; + + previousop = currentop; // Opcode tracking for nop padding + currentop = parm; + + if(!orgwarning) { // Check for absolute address setting + if(!orgactive && !in_main) { + warn("GPU/DSP code outside of absolute section"); + orgwarning = 1; + } + } + + if(jpad) { // JPAD directive + // JUMP JR NOP + if(((previousop == 52) || (previousop == 53)) && (currentop != 57)) + D_word(value); // Insert NOP + } else { + // JUMP JR + if((previousop == 52) || (previousop == 53)) { + switch(currentop) { + case 38: warn("NOP inserted before MOVEI instruction."); D_word(value); break; + case 53: warn("NOP inserted before JR instruction."); D_word(value); break; + case 52: warn("NOP inserted before JUMP instruction."); D_word(value); break; + case 51: warn("NOP inserted before MOVE PC instruction."); D_word(value); break; + default: + break; + } + } + } + + if(currentop == 20) { // IMACN checks + if((previousop != 18) && (previousop != 20)) { + error("IMULTN/IMACN instruction must preceed IMACN instruction"); + } + } + + if(currentop == 19) { // RESMAC checks + if(previousop != 20) { + error("IMACN instruction must preceed RESMAC instruction"); + } + } + + value =((parm & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F); + D_word(value); +} + +// +// --- Get a RISC Register ------------------------------------------------------------------------- +// + +int getregister(WORD rattr) { + VALUE eval; // Expression value + WORD eattr; // Expression attributes + SYM *esym; // External symbol involved in expr. + TOKEN r_expr[EXPRSIZE]; // Expression token list + WORD defined; // Symbol defined flag + + if(expr(r_expr, &eval, &eattr, &esym) != OK) { + error("malformed opcode"); + return(ERROR); + } else { + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + fixup((WORD)(FU_WORD|rattr), sloc, r_expr); + return(0); + } else { + if((eval >= 0) && (eval <= 31)) { // Check for specified register, r0->r31 + return(eval); + } else { + error(reg_err); + return(ERROR); + } + } + } + + return(ERROR); +} + +// +// --- Do RISC Code Generation --------------------------------------------------------------------- +// + +int risccg(int state) { + unsigned short parm; // Opcode parameters + unsigned type; // Opcode type + int reg1; // Register 1 + int reg2; // Register 2 + int val = 0; // Constructed value + char scratch[80]; + SYM *ccsym; + SYM *sy; + int i; // Iterator + int t, c; + WORD tdb; + unsigned locptr = 0; // Address location pointer + unsigned page_jump = 0; // Memory page jump flag + VALUE eval; // Expression value + WORD eattr; // Expression attributes + SYM *esym; // External symbol involved in expr. + TOKEN r_expr[EXPRSIZE]; // Expression token list + WORD defined; // Symbol defined flag + WORD attrflg; + int indexed; // Indexed register flag + + parm = (WORD)(roptbl[state-3000].parm); // Get opcode parameter and type + type = roptbl[state-3000].typ; + + // Detect whether the opcode parmeter passed determines that the opcode is specific to only one + // of the RISC processors and ensure it is legal in the current code section. + // If not then error and return. + if(((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu) ) { + error("opcode is not valid in this code section"); + return(ERROR); + } + + // Process RISC opcode + switch(type) { + // No operand instructions + // NOP + case RI_NONE: + risc_instruction_word(parm, 0, 0); + break; + // Single operand instructions (Rd) + // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK + case RI_ONE: + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, parm >> 6, reg2); + break; + // Two operand instructions (Rs,Rd) + // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT, + // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR + case RI_TWO: + if(parm == 37) altbankok = 1; // MOVEFA + reg1 = getregister(FU_REGONE); + CHECK_COMMA; + if(parm == 36) altbankok = 1; // MOVETA + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, reg1, reg2); + break; + // Numeric operand (n,Rd) where n = -16..+15 + // CMPQ + case RI_NUM_15: + // Numeric operand (n,Rd) where n = 0..31 + // BCLR, BSET, BTST, MOVEQ + case RI_NUM_31: + // Numeric operand (n,Rd) where n = 1..32 + // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ + case RI_NUM_32: + switch(type) { + case RI_NUM_15: reg1 = -16; reg2 = 15; attrflg = FU_NUM15; break; + default: + case RI_NUM_31: reg1 = 0; reg2 = 31; attrflg = FU_NUM31; break; + case RI_NUM_32: reg1 = 1; reg2 = 32; attrflg = FU_NUM32; break; + } + if(parm & SUB32) attrflg |= FU_SUB32; + if(*tok == '#') { + ++tok; + if(expr(r_expr, &eval, &eattr, &esym) != OK) + goto malformed; + else { + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + fixup((WORD)(FU_WORD|attrflg), sloc, r_expr); + reg1 = 0; + } else { + if((int)eval < reg1 || (int)eval > reg2) { + error("constant out of range"); + return(ERROR); + } + if(parm & SUB32) + reg1 = 32 - eval; + else if(type == RI_NUM_32) + reg1 = (reg1 == 32) ? 0 : eval; + else + reg1 = eval; + } + } + } else goto malformed; + CHECK_COMMA; + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, reg1, reg2); + break; + // Move Immediate - n,Rn - n in Second Word + case RI_MOVEI: + if(*tok == '#') { + ++tok; + if(expr(r_expr, &eval, &eattr, &esym) != OK) { + malformed: + error("malformed opcode"); + return(ERROR); + } else { + // Opcode tracking for nop padding + previousop = currentop; + currentop = parm; + // JUMP or JR + if((previousop == 52) || (previousop == 53) && !jpad) { + warn("NOP inserted before MOVEI instruction."); + D_word(0xE400); + } + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + fixup(FU_LONG|FU_MOVEI, sloc + 2, r_expr); + eval = 0; + } else { + if(tdb) { + rmark(cursect, sloc + 2, tdb, MLONG|MMOVEI, NULL); + } + } + val = eval; + // Store the defined flags and value of the movei when used in mjump + if(mjump_align) { + mjump_defined = defined; + mjump_dest = val; + } + } + } else goto malformed; + ++tok; + reg2 = getregister(FU_REGTWO); + at_eol(); + D_word((((parm & 0x3F) << 10) + reg2)); + val = ((val >> 16) & 0x0000FFFF) | ((val << 16) & 0xFFFF0000); + D_long(val); + break; + case RI_MOVE: // PC,Rd or Rs,Rd + if(*tok == KW_PC) { + parm = 51; + reg1 = 0; + ++tok; + } else { + parm = 34; + reg1 = getregister(FU_REGONE); + } + CHECK_COMMA; + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, reg1, reg2); + break; + // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59 + case RI_LOAD: + indexed = 0; + parm = 41; + if(*tok != '(') goto malformed; + ++tok; + if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')')) + indexed = (*tok - KW_R0); + if(*tok == SYMBOL) { + sy = lookup((char *)tok[1], LABEL, 0); + if(!sy) { + error(reg_err); + return(ERROR); + } + if(sy->sattre & EQUATEDREG) + if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) { + indexed = (sy->svalue & 0x1F); + ++tok; + } + } + if(!indexed) { + reg1 = getregister(FU_REGONE); + } else { + reg1 = indexed; + indexed = 0; + ++tok; + if(*tok == '+') { + parm = (WORD)(reg1 - 14 + 58); + tok++; + if(*tok >= KW_R0 && *tok <= KW_R31) { + indexed = 1; + } + if(*tok == SYMBOL) { + sy = lookup((char *)tok[1], LABEL, 0); + if(!sy) { + error(reg_err); + return(ERROR); + } + if(sy->sattre & EQUATEDREG) { + indexed = 1; + } + } + if(indexed) { + reg1 = getregister(FU_REGONE); + } else { + if(expr(r_expr, &eval, &eattr, &esym) != OK) { + goto malformed; + } else { + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + error("constant expected"); + return(ERROR); + //fixup(FU_WORD|FU_REGONE, sloc, r_expr); + reg1 = 0; + } else { + reg1 = eval; + if(reg1 == 0) { + reg1 = 14+(parm-58); + parm = 41; + warn("NULL offset removed"); + } else { + if(reg1 < 1 || reg1 > 32) { + error("constant out of range"); + return(ERROR); + } + if(reg1 == 32) reg1 = 0; + parm = (WORD)(parm - 58 + 43); + } + } + } + } + } else { + reg1 = getregister(FU_REGONE); + } + } + if(*tok != ')') goto malformed; + ++tok; + CHECK_COMMA; + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, reg1, reg2); + break; + // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61 + case RI_STORE: + parm = 47; + reg1 = getregister(FU_REGONE); + CHECK_COMMA; + if(*tok != '(') goto malformed; + ++tok; + indexed = 0; + if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')')) + indexed = (*tok - KW_R0); + if(*tok == SYMBOL) { + sy = lookup((char *)tok[1], LABEL, 0); + if(!sy) { + error(reg_err); + return(ERROR); + } + if(sy->sattre & EQUATEDREG) + if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) { + indexed = (sy->svalue & 0x1F); + ++tok; + } + } + if(!indexed) { + reg2 = getregister(FU_REGTWO); + } else { + reg2 = indexed; + indexed = 0; + ++tok; + if(*tok == '+') { + parm = (WORD)(reg2 - 14 + 60); + tok++; + if(*tok >= KW_R0 && *tok <= KW_R31) { + indexed = 1; + } + if(*tok == SYMBOL) { + sy = lookup((char *)tok[1], LABEL, 0); + if(!sy) { + error(reg_err); + return(ERROR); + } + if(sy->sattre & EQUATEDREG) { + indexed = 1; + } + } + if(indexed) { + reg2 = getregister(FU_REGTWO); + } else { + if(expr(r_expr, &eval, &eattr, &esym) != OK) { + goto malformed; + } else { + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + fixup(FU_WORD|FU_REGTWO, sloc, r_expr); + reg2 = 0; + } else { + reg2 = eval; + if(reg2 == 0 ) { + reg2 = 14+(parm-60); + parm = 47; + warn("NULL offset removed"); + } else { + if(reg2 < 1 || reg2 > 32) { + error("constant out of range"); + return(ERROR); + } + if(reg2 == 32) reg2 = 0; + parm = (WORD)(parm - 60 + 49); + } + } + } + } + } else { + reg2 = getregister(FU_REGTWO); + } + } + if(*tok != ')') goto malformed; + ++tok; + at_eol(); + risc_instruction_word(parm, reg2, reg1); + break; + // LOADB/LOADP/LOADW (Rn),Rn + case RI_LOADN: + if(*tok != '(') goto malformed; + ++tok; + reg1 = getregister(FU_REGONE); + if(*tok != ')') goto malformed; + ++tok; + CHECK_COMMA; + reg2 = getregister(FU_REGTWO); + at_eol(); + risc_instruction_word(parm, reg1, reg2); + break; + // STOREB/STOREP/STOREW Rn,(Rn) + case RI_STOREN: + reg1 = getregister(FU_REGONE); + CHECK_COMMA; + if(*tok != '(') goto malformed; + ++tok; + reg2 = getregister(FU_REGTWO); + if(*tok != ')') goto malformed; + ++tok; + at_eol(); + risc_instruction_word(parm, reg2, reg1); + break; + case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc + case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc + // Check to see if there is a comma in the token string. If not then the JR or JUMP should + // default to 0, Jump Always + t = i = c = 0; + while(t != EOL) { + t = *(tok + i); + if(t == ',') c = 1; + i++; + } + if(c) { // Comma present in token string + if(*tok == CONST) { // CC using a constant number + ++tok; + val = *tok; + ++tok; + CHECK_COMMA; + } else if(*tok == SYMBOL) { + val = 99; + for(i = 0; i < MAXINTERNCC; i++) { + strcpy(scratch, (char *)tok[1]); + strtoupper(scratch); + if(!strcmp(condname[i], scratch)) + val = condnumber[i]; + } + if(val == 99) { + ccsym = lookup((char *)tok[1], LABEL, 0); + if(ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC)) { + val = ccsym->svalue; + } else { + error("unknown condition code"); + return(ERROR); + } + } + tok += 2; + CHECK_COMMA; + } else if(*tok == '(') { + val = 0; // Jump always + } + } else { + val = 0; // Jump always + } + + if(val < 0 || val > 31) { + error("condition constant out of range"); + return(ERROR); + } else { + reg1 = val; // Store condition code + } + if(type == RI_JR) { // JR cc,n + if(expr(r_expr, &eval, &eattr, &esym) != OK) + goto malformed; + else { + tdb = (WORD)(eattr & TDB); + defined = (WORD)(eattr & DEFINED); + if((challoc - ch_size) < 4) + chcheck(4L); + if(!defined) { + if(in_main) { + fixup(FU_WORD|FU_MJR, sloc, r_expr); + } else { + fixup(FU_WORD|FU_JR, sloc, r_expr); + } + reg2 = 0; + } else { + val = eval; + if(orgactive) { + reg2 = ((int)(val - (orgaddr + 2))) / 2; + if((reg2 < -16) || (reg2 > 15)) + error("PC relative overflow"); + locptr = orgaddr; + } else { + reg2 = ((int)(val - (sloc + 2))) / 2; + if((reg2 < -16) || (reg2 > 15)) + error("PC relative overflow"); + locptr = sloc; + } + } + if(in_main) { + if(defined) { + if(((locptr >= 0xF03000) && (locptr < 0xF04000) && (val < 0xF03000)) || + ((val >= 0xF03000) && (val < 0xF04000) && (locptr < 0xF03000)) ) { + warn("* cannot jump relative between main memory and local gpu ram"); + } else { + page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00); + if(page_jump) { + if(val % 4) { + warn("* destination address not aligned for long page jump relative, " + "insert a \'nop\' before the destination label/address"); + } + } else { + if((val - 2) % 4) { + warn("* destination address not aligned for short page jump relative, " + "insert a \'nop\' before the destination label/address"); + } + } + } + } + } + } + risc_instruction_word(parm, reg2, reg1); + } else { // JUMP cc, (Rn) + if(*tok != '(') goto malformed; + ++tok; + reg2 = getregister(FU_REGTWO); + if(*tok != ')') goto malformed; + ++tok; + at_eol(); + if(in_main) { + if(!mjump_align) { + warn("* \'jump\' is not recommended for .gpumain as destination addresses " + "cannot be validated for alignment, use \'mjump\'"); + locptr = (orgactive) ? orgaddr : sloc; + if(locptr % 4) { + warn("* source address not aligned for long or short jump, " + "insert a \'nop\' before the \'jump\'"); + } + } else { + if(mjump_defined) { + locptr = (orgactive) ? orgaddr : sloc; + page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00); + if(page_jump) { + if(mjump_dest % 4) { + warn("* destination address not aligned for long page jump, " + "insert a \'nop\' before the destination label/address"); + } + } else { + if(!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4)) { + warn("* destination address not aligned for short page jump, " + "insert a \'nop\' before the destination label/address"); + } + } + } else { + locptr = (orgactive) ? orgaddr : sloc; + fwdjump[fwindex++] = locptr; + } + } + + } + risc_instruction_word(parm, reg2, reg1); + } + break; + // Should never get here :D + default: + error("unknown risc opcode type"); + return(ERROR); + break; + + } + + return(0); +} + diff --git a/risca.h b/risca.h new file mode 100644 index 0000000..0c49cf5 --- /dev/null +++ b/risca.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// RISCA.H - GPU/DSP Assembler +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __RISCA_H__ +#define __RISCA_H__ + +#include "rmac.h" +#include "procln.h" + +#define MAXINTERNCC 26 // Maximum internal condition codes + +// RISC Instruction Types +#define RI_NONE 0x0000 // No Operands - NOP +#define RI_ONE 0x0001 // One Operand - Rd - ABS/NEG/etc +#define RI_TWO 0x0002 // Two Operands - Rs,Rd - Most Instructions +#define RI_NUM_15 0x0003 // Numeric Operand - n,Rd - n=-16..+15 - CMPQ +#define RI_NUM_31 0x0004 // Numeric Operand - n,Rd - n=0..31 - BCLR/BSET/BTST/MOVEQ +#define RI_NUM_32 0x0005 // Numeric Operand - n,Rd - n=1..32 - ADDQ/SUBQ +#define RI_JR 0x0006 // Jump Relative - cc,n - n=-16..+15 words, reg2=cc +#define RI_JUMP 0x0007 // Jump Absolute - cc,(Rs) - reg2=cc +#define RI_MOVEI 0x0008 // Move Immediate - n,Rn - n in second word +#define RI_MOVE 0x0009 // MOVE Instruction - PC,Rn / Rn,Rn +#define RI_LOAD 0x000A // LOAD Instruction - Various Forms +#define RI_LOADN 0x000B // LOADB/LOADP/LOADW - (Rs),Rd +#define RI_STORE 0x000C // STORE Instruction - Various Forms +#define RI_STOREN 0x000D // STOREB/STOREP/STOREM - Rs,(Rd) +#define RI_MJMP 0x000E // MJMP psuedo instruction + +// Supplementry Instruction Flags +#define SUB32 0x2000 // (n = 32-n) +#define GPUONLY 0x4000 // Opcode is for the GPU Only +#define DSPONLY 0x8000 // Opcode is for the DSP Only + +#define CHECK_COMMA if(*tok++ != ',') { error(comma_error); return(ERROR); } + +// Opcode Specific Data +struct opcoderecord { + short state; // Opcode Name + unsigned short typ; // Opcode Type + unsigned parm; // Opcode Parameter +}; + +// Globals, externals etc +extern unsigned orgactive; +extern unsigned orgaddr; +extern unsigned orgwarning; +extern unsigned altbankok; +extern int jpad; + +// Prototypes +int risccg(int); +int d_orgrisc(void); + +#endif // __RISCA_H__ + diff --git a/risctab b/risctab new file mode 100644 index 0000000..6603b2e --- /dev/null +++ b/risctab @@ -0,0 +1,66 @@ +# +# Jaguar RISC mnemonics +# +add 3000 +addc 3001 +addq 3002 +addqt 3003 +sub 3004 +subc 3005 +subq 3006 +subqt 3007 +neg 3008 +and 3009 +or 3010 +xor 3011 +not 3012 +btst 3013 +bset 3014 +bclr 3015 +mult 3016 +imult 3017 +imultn 3018 +resmac 3019 +imacn 3020 +div 3021 +abs 3022 +sh 3023 +shlq 3024 +shrq 3025 +sha 3026 +sharq 3027 +ror 3028 +rorq 3029 +rolq 3030 +cmp 3031 +cmpq 3032 +sat8 3033 +subqmod 3034 +sat16 3035 +sat16s 3036 +moveq 3037 +moveta 3038 +movefa 3039 +movei 3040 +loadb 3041 +loadw 3042 +loadp 3043 +sat32s 3044 +storeb 3045 +storew 3046 +storep 3047 +mirror 3048 +jump 3049 +jr 3050 +mmult 3051 +mtoi 3052 +normi 3053 +nop 3054 +sat24 3055 +unpack 3056 +pack 3057 +addqmod 3058 +move 3059 +load 3060 +store 3061 + diff --git a/rmac.c b/rmac.c new file mode 100644 index 0000000..e5474da --- /dev/null +++ b/rmac.c @@ -0,0 +1,826 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// RMAC.C - Main Application Code +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// + +#include "rmac.h" +#include "error.h" +#include "listing.h" +#include "procln.h" +#include "token.h" +#include "expr.h" +#include "sect.h" +#include "mark.h" +#include "macro.h" +#include "risca.h" +#include "direct.h" +#include "version.h" +#include "debug.h" +#include "symbol.h" +#include "object.h" + +int perm_verb_flag; // Permanently verbose, interactive mode +int list_flag; // "-l" Listing flag on command line +int verb_flag; // Be verbose about what's going on +int as68_flag; // as68 kludge mode +int glob_flag; // Assume undefined symbols are global +int lsym_flag; // Include local symbols in object file +int sbra_flag; // Warn about possible short branches +int obj_format; // Object format flag +int debug; // [1..9] Enable debugging levels +int err_flag; // '-e' specified +int err_fd; // File to write error messages to +int rgpu, rdsp; // Assembling Jaguar GPU or DSP code +int list_fd; // File to write listing to +int regbank; // RISC register bank +int segpadsize; // Segment padding size +int in_main; // In main memory flag for GPUMAIN +int endian; // Processor endianess +char *objfname; // Object filename pointer +char *firstfname; // First source filename +char *cmdlnexec; // Executable name, pointer to ARGV[0] +char *searchpath; // Search path for include files +char defname[] = "noname.o"; // Default output filename + +// Under Windows and UNIX malloc() is an expensive call, so for small amounts of memory we allocate +// from a previously allocated buffer. + +#define A_AMOUNT 4096 // Amount to malloc() at a time +#define A_THRESH 64 // Use malloc() for amounts >= A_THRESH + +static LONG a_amount; // Amount left at a_ptr +static char *a_ptr; // Next free chunk +LONG amemtot; // amem() total of requests + +// Qsort; The THRESHold below is the insertion sort threshold, and has been adjusted +// for records of size 48 bytes.The MTHREShold is where we stop finding a better median. + +#define THRESH 4 // Threshold for insertion +#define MTHRESH 6 // Threshold for median + +static int (*qcmp)(); // The comparison routine +static int qsz; // Size of each record +static int thresh; // THRESHold in chars +static int mthresh; // MTHRESHold in chars + +// qst: Do a quicksort. First, find the median element, and put that one in the first place as +// the discriminator. (This "median" is just the median of the first, last and middle elements). +// (Using this median instead of the first element is a big win). Then, the usual +// partitioning/swapping, followed by moving the discriminator into the right place. Then, +// figure out the sizes of the two partions, do the smaller one recursively and the larger one +// via a repeat of this code. Stopping when there are less than THRESH elements in a partition +// and cleaning up with an insertion sort (in our caller) is a huge win. All data swaps are done +// in-line, which is space-losing but time-saving. (And there are only three places where +// this is done). + +static int qst(char *base, char *max) { + char c, *i, *j, *jj; + int ii; + char *mid, *tmp; + long lo, hi; + + /* + * At the top here, lo is the number of characters of elements in the + * current partition. (Which should be max - base). + * Find the median of the first, last, and middle element and make + * that the middle element. Set j to largest of first and middle. + * If max is larger than that guy, then it's that guy, else compare + * max with loser of first and take larger. Things are set up to + * prefer the middle, then the first in case of ties. + */ + lo = max - base; /* number of elements as chars */ + do { + mid = i = base + qsz * ((lo / qsz) >> 1); + if (lo >= mthresh) { + j = (qcmp((jj = base), i) > 0 ? jj : i); + if (qcmp(j, (tmp = max - qsz)) > 0) { + /* switch to first loser */ + j = (j == jj ? i : jj); + if (qcmp(j, tmp) < 0) + j = tmp; + } + if (j != i) { + ii = qsz; + do { + c = *i; + *i++ = *j; + *j++ = c; + } while (--ii); + } + } + /* + * Semi-standard quicksort partitioning/swapping + */ + for (i = base, j = max - qsz; ; ) { + while (i < mid && qcmp(i, mid) <= 0) + i += qsz; + while (j > mid) { + if (qcmp(mid, j) <= 0) { + j -= qsz; + continue; + } + tmp = i + qsz; /* value of i after swap */ + if (i == mid) { + /* j <-> mid, new mid is j */ + mid = jj = j; + } else { + /* i <-> j */ + jj = j; + j -= qsz; + } + goto swap; + } + if (i == mid) { + break; + } else { + /* i <-> mid, new mid is i */ + jj = mid; + tmp = mid = i; /* value of i after swap */ + j -= qsz; + } +swap: + ii = qsz; + do { + c = *i; + *i++ = *jj; + *jj++ = c; + } while (--ii); + i = tmp; + } + /* + * Look at sizes of the two partitions, do the smaller + * one first by recursion, then do the larger one by + * making sure lo is its size, base and max are update + * correctly, and branching back. But only repeat + * (recursively or by branching) if the partition is + * of at least size THRESH. + */ + i = (j = mid) + qsz; + if ((lo = j - base) <= (hi = max - i)) { + if (lo >= thresh) + qst(base, j); + base = i; + lo = hi; + } else { + if (hi >= thresh) + qst(i, max); + max = j; + } + } while (lo >= thresh); + + return(0); +} + +/* + * qsort: + * First, set up some global parameters for qst to share. Then, quicksort + * with qst(), and then a cleanup insertion sort ourselves. Sound simple? + * It's not... + */ + +int rmac_qsort(char *base, int n, int size, int (*compar)()) { + register char c, *i, *j, *lo, *hi; + char *min, *max; + + if (n <= 1) + return(0); + qsz = size; + qcmp = compar; + thresh = qsz * THRESH; + mthresh = qsz * MTHRESH; + max = base + n * qsz; + if (n >= THRESH) { + qst(base, max); + hi = base + thresh; + } else { + hi = max; + } + /* + * First put smallest element, which must be in the first THRESH, in + * the first position as a sentinel. This is done just by searching + * the first THRESH elements (or the first n if n < THRESH), finding + * the min, and swapping it into the first position. + */ + for (j = lo = base; (lo += qsz) < hi; ) + if (qcmp(j, lo) > 0) + j = lo; + if (j != base) { + /* swap j into place */ + for (i = base, hi = base + qsz; i < hi; ) { + c = *j; + *j++ = *i; + *i++ = c; + } + } + /* + * With our sentinel in place, we now run the following hyper-fast + * insertion sort. For each remaining element, min, from [1] to [n-1], + * set hi to the index of the element AFTER which this one goes. + * Then, do the standard insertion sort shift on a character at a time + * basis for each element in the frob. + */ + for (min = base; (hi = min += qsz) < max; ) { + while (qcmp(hi -= qsz, min) > 0) + /* void */; + if ((hi += qsz) != min) { + for (lo = min + qsz; --lo >= min; ) { + c = *lo; + for (i = j = lo; (j -= qsz) >= hi; i = j) + *i = *j; + *i = c; + } + } + } + return(0); +} + +// +// --- Allocate memory; Panic and Quit if we Run Out ----------------------------------------------- +// + +char * amem(LONG amount) +{ + char * p; + + if (amount & 1) // Keep word alignment + ++amount; + + if (amount < A_THRESH) + { // Honor *small* request + if (a_amount < amount) + { + a_ptr = amem(A_AMOUNT); + a_amount = A_AMOUNT; + } + + p = a_ptr; + a_ptr += amount; + a_amount -= amount; + } + else + { + amemtot += amount; // Bump total alloc + p = (char *)malloc(amount); // Get memory from malloc + + if ((LONG)p == (LONG)NULL) + fatal("memory exhausted"); + + memset(p, 0, amount); + } + + return p; +} + +// +// --- Copy stuff around, return pointer to dest+count+1 (doesn't handle overlap) ------------------ +// + +char * copy(char * dest, char * src, LONG count) +{ + while (count--) + *dest++ = *src++; + + return dest; +} + +// +// --- Clear a region of memory -------------------------------------------------------------------- +// + +void clear(char *dest, LONG count) +{ + while(count--) + *dest++ = 0; +} + +// +// --- Check to see if the string is a keyword. Returns -1, or a value from the 'accept[]' table --- +// + +int kmatch(char * p, int * base, int * check, int * tab, int * accept) +{ + int state; + int j; + + for(state=0; state>=0;) + { + j = base[state] + (int)tolowertab[*p]; + + if (check[j] != state) + { // Reject, character doesn't match + state = -1; // No match + break; + } + + if (!*++p) + { // Must accept or reject at EOS + state = accept[j]; // (-1 on no terminal match) + break; + } + + state = tab[j]; + } + + return state; +} + +// +// --- Auto-even a section ------------------------------------------------------------------------- +// + +void autoeven(int sect) +{ + switchsect(sect); + d_even(); + savsect(); +} + +// +// ------------------------------------------------------------------------------------------------- +// Manipulate file extension. +// `name' must be large enough to hold any possible filename. +// If `stripp' is nonzero, any old extension is removed. +// Then, if the file does not already have an extension, +// `extension' is appended to the filename. +// ------------------------------------------------------------------------------------------------- +// + +char *fext(char *name, char *extension, int stripp) { + char *s, *beg; // String pointers + + // Find beginning of "real" name + beg = name + strlen(name) - 1; + for(; beg > name; --beg) { + if(*beg == SLASHCHAR) { + ++beg; + break; + } + } + + if(stripp) { // Clobber any old extension + for(s = beg; *s && *s != '.'; ++s) + ; + *s = '\0'; + } + + for(s = beg; *s != '.'; ++s) { + if(!*s) { // Append the new extension + strcat(beg, extension); + break; + } + } + + return(name); +} + +// +// ------------------------------------------------------------------------------------------------- +// Return `item'nth element of semicolon-seperated pathnames specified in the enviroment string `s'. +// Copy the pathname to `buf'. Return 0 if the `item'nth path doesn't exist. +// +// [`item' ranges from 0 to N-1, where N = #elements in search path] +// ------------------------------------------------------------------------------------------------- +// + +int nthpath(char * env_var, int itemno, char * buf) +{ + char * s = searchpath; + + if (s == NULL) + s = getenv(env_var); + + if (s == NULL) + return 0; + + while (itemno--) + while (*s != EOS && *s++ != ';') + ; + + if (*s == EOS) + return 0; + + while (*s != EOS && *s != ';') + *buf++ = *s++; + + *buf++ = EOS; + + return 1; +} + +// +// --- Display Command Line Help ----------------------------------------------- +// + +void display_help(void) +{ + printf("Usage:\n"); + printf(" %s [options] srcfile\n", cmdlnexec); + printf("\n"); + printf("Options:\n"); + printf(" -? or -h display usage information\n"); + printf(" -dsymbol[=value] define symbol\n"); + printf(" -e[errorfile] send error messages to file, not stdout\n"); + printf(" -f[format] output object file format\n"); + printf(" b: BSD (use this for Jaguar)\n"); + printf(" -i[path] directory to search for include files\n"); + printf(" -l[filename] create an output listing file\n"); + printf(" -o file output file name\n"); + printf(" -r[size] pad segments to boundary size specified\n"); + printf(" w: word (2 bytes, default alignment)\n"); + printf(" l: long (4 bytes)\n"); + printf(" p: phrase (8 bytes)\n"); + printf(" d: double phrase (16 bytes)\n"); + printf(" q: quad phrase (32 bytes)\n"); + printf(" -s warn about possible short branches\n"); + printf(" -u force referenced and undefined symbols global\n"); + printf(" -v set verbose mode\n"); + printf(" -y[pagelen] set page line length (default: 61)\n"); + printf("\n"); +} + +// +// --- Display Version Information --------------------------------------------- +// + +void display_version(void) +{ + printf("\nReboot's Macro Assembler for Atari Jaguar\n"); + printf("Copyright (C) 199x Landon Dyer, 2011 Reboot\n"); + printf("V%01i.%01i.%01i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM); +} + +// +// --- Process Command Line Arguments and do an Assembly ------------------------------------------- +// + +int process(int argc, char **argv) { + int argno; // Argument number + SYM *sy; // Pointer to a symbol record + char *s; // String pointer + int fd; // File descriptor + char fnbuf[FNSIZ]; // Filename buffer + int i; // Iterator + + errcnt = 0; // Initialise error count + listing = 0; // Initialise listing level + list_flag = 0; // Initialise listing flag + verb_flag = perm_verb_flag; // Initialise verbose flag + as68_flag = 0; // Initialise as68 kludge mode + glob_flag = 0; // Initialise .globl flag + sbra_flag = 0; // Initialise short branch flag + debug = 0; // Initialise debug flag + searchpath = NULL; // Initialise search path + objfname = NULL; // Initialise object filename + list_fname = NULL; // Initialise listing filename + err_fname = NULL; // Initialise error filename + obj_format = BSD; // Initialise object format + firstfname = NULL; // Initialise first filename + err_fd = ERROUT; // Initialise error file descriptor + err_flag = 0; // Initialise error flag + rgpu = 0; // Initialise GPU assembly flag + rdsp = 0; // Initialise DSP assembly flag + lsym_flag = 1; // Include local symbols in object file + regbank = BANK_N; // No RISC register bank specified + orgactive = 0; // Not in RISC org section + orgwarning = 0; // No ORG warning issued + a_amount = 0; + segpadsize = 2; // Initialise segment padding size + in_main = 0; + + // Initialise modules + init_sym(); // Symbol table + init_token(); // Tokenizer + init_procln(); // Line processor + init_expr(); // Expression analyzer + init_sect(); // Section manager / code generator + init_mark(); // Mark tape-recorder + init_macro(); // Macro processor + init_list(); // Listing generator + + // Process command line arguments and assemble source files + for(argno = 0; argno < argc; ++argno) { + if(*argv[argno] == '-') { + switch(argv[argno][1]) { + case 'd': // Define symbol + case 'D': + for(s = argv[argno] + 2; *s != EOS;) { + if(*s++ == '=') { + s[-1] = EOS; + break; + } + } + if(argv[argno][2] == EOS) { + printf("-d: empty symbol\n"); + ++errcnt; + return(errcnt); + } + sy = lookup(argv[argno] + 2, 0, 0); + if(sy == NULL) { + sy = newsym(argv[argno] + 2, LABEL, 0); + sy->svalue = 0; + } + sy->sattr = DEFINED | EQUATED | ABS; + if(*s) + sy->svalue = (VALUE)atoi(s); + else + sy->svalue = 0; + break; + case 'e': // Redirect error message output + case 'E': + err_fname = argv[argno] + 2; + break; + case 'f': // -f + case 'F': + switch(argv[argno][2]) { + case EOS: + case 'b': // -fb = BSD (Jaguar Recommended) + case 'B': + obj_format = BSD; + break; + default: + printf("-f: unknown object format specified\n"); + ++errcnt; + return(errcnt); + } + break; + case 'g': // Debugging flag + case 'G': + printf("Debugging flag (-g) not yet implemented\n"); + break; + case 'i': // Set directory search path + case 'I': + searchpath = argv[argno] + 2; + break; + case 'l': // Produce listing file + case 'L': + list_fname = argv[argno] + 2; + listing = 1; + list_flag = 1; + ++lnsave; + break; + case 'o': // Direct object file output + case 'O': + if(argv[argno][2] != EOS) objfname = argv[argno] + 2; + else { + if(++argno >= argc) { + printf("Missing argument to -o"); + ++errcnt; + return(errcnt); + } + objfname = argv[argno]; + } + break; + case 'r': // Pad seg to requested boundary size + case 'R': + switch(argv[argno][2]) { + case 'w': case 'W': segpadsize = 2; break; + case 'l': case 'L': segpadsize = 4; break; + case 'p': case 'P': segpadsize = 8; break; + case 'd': case 'D': segpadsize = 16; break; + case 'q': case 'Q': segpadsize = 32; break; + default: segpadsize = 2; break; // Effective autoeven(); + } + break; + case 's': // Warn about possible short branches + case 'S': + sbra_flag = 1; + break; + case 'u': // Make undefined symbols .globl + case 'U': + glob_flag = 1; + break; + case 'v': // Verbose flag + case 'V': + verb_flag++; + if(verb_flag > 1) display_version(); + break; + case 'x': // Turn on debugging + case 'X': + debug = 1; + printf("~ Debugging ON\n"); + break; + case 'y': // -y + case 'Y': + pagelen = atoi(argv[argno] + 2); + if(pagelen < 10) { + printf("-y: bad page length\n"); + ++errcnt; + return(errcnt); + } + break; + case EOS: // Input is stdin + if(firstfname == NULL) // Kludge first filename + firstfname = defname; + include(0, "(stdin)"); + assemble(); + break; + case 'h': // Display command line usage + case 'H': + case '?': + display_version(); + display_help(); + ++errcnt; + break; + default: + display_version(); + printf("Unknown switch: %s\n\n", argv[argno]); + display_help(); + ++errcnt; + break; + } + } else { + // Record first filename. + if(firstfname == NULL) + firstfname = argv[argno]; + strcpy(fnbuf, argv[argno]); + fext(fnbuf, ".s", 0); + fd = open(fnbuf, 0); + if(fd < 0) { + printf("Cannot open: %s\n", fnbuf); + ++errcnt; + continue; + } + include(fd, fnbuf); + assemble(); + } + } + + // Wind-up processing; + // o save current section (no more code generation) + // o do auto-even of all sections (or boundary alignment as requested through '-r') + // o determine name of object file: + // - "foo.o" for linkable output; + // - "foo.prg" for GEMDOS executable (-p flag). + savsect(); + for(i = TEXT; i <= BSS; i <<= 1) { + switchsect(i); + switch(segpadsize) { + case 2: d_even(); break; + case 4: d_long(); break; + case 8: d_phrase(); break; + case 16: d_dphrase(); break; + case 32: d_qphrase(); break; + } + savsect(); + } + + if(objfname == NULL) { + if(firstfname == NULL) + firstfname = defname; + strcpy(fnbuf, firstfname); + //fext(fnbuf, prg_flag ? ".prg" : ".o", 1); + fext(fnbuf, ".o", 1); + objfname = fnbuf; + } + + // With one pass finished, go back and: + // (1) run through all the fixups and resolve forward references; + // (1.5) ensure that remaining fixups can be handled by the linker + // (`lo68' format, extended (postfix) format....) + // (2) generate the output file image and symbol table; + // (3) generate relocation information from left-over fixups. + fixups(); // Do all fixups + stopmark(); // Stop mark tape-recorder + if(errcnt == 0) { + if((fd = open(objfname, _OPEN_FLAGS, _PERM_MODE)) < 0) + cantcreat(objfname); + if(verb_flag) { + s = "object"; + printf("[Writing %s file: %s]\n", s, objfname); + } + object((WORD)fd); + close(fd); + if(errcnt != 0) + unlink(objfname); + } + + if(list_flag) { + if(verb_flag) printf("[Wrapping-up listing file]\n"); + listing = 1; + symtable(); + close(list_fd); + } + + if(err_flag) + close(err_fd); + + DEBUG dump_everything(); + + return(errcnt); +} + +// +// --- Interactive Mode ---------------------------------------------------------------------------- +// + +void interactive(void) +{ + char * s; // String pointer for banner + char ln[LNSIZ]; // Input line + char * argv[MAXARGV]; // Argument values + int argcnt; // Argument count + + // As there is no command line, print a copyright message and prompt for command line + s = "*****************************************************\n"; + printf("\n%s* RMAC - Reboot's Macro Assembler for Atari Jaguar *\n", s); + printf("* Copyright (C) 199x Landon Dyer, 2011 Reboot *\n"); + printf("* Version %01i.%01i.%01i Platform: %-9s *\n",MAJOR,MINOR,PATCH,PLATFORM); + printf("* ------------------------------------------------- *\n"); + printf("* INTERACTIVE MODE *\n%s\n", s); + + perm_verb_flag = 1; // Enter permanent verbose mode + + // Handle commandlines until EOF or we get an empty one + for(;;) + { + loop: + printf("* "); + fflush(stdout); // Make prompt visible + + if (gets(ln) == NULL || !*ln) // Get input line + break; + + argcnt = 0; // Process input line + s = ln; + + while (*s) + { + if (isspace(*s)) + { // Skip whitespace + ++s; + } + else + { + if (argcnt >= MAXARGV) + { + printf("Too many arguments\n"); + goto loop; + } + + argv[argcnt++] = s; + + while (*s && !isspace(*s)) + ++s; + + if (isspace(*s)) + *s++ = EOS; + } + } + + if (argcnt == 0) // Exit if no arguments + break; + + process(argcnt, argv); // Process arguments + + if (errcnt) + printf("%d assembly error%s\n", errcnt, (errcnt > 1) ? "s" : ""); + } +} + +// +// --- Determine Processor Endianess --------------------------------------------------------------- +// + +int get_endianess(void) +{ + int i = 1; + char * p = (char *)&i; + + if (p[0] == 1) + return 0; + + return 1; +} + +// +// --- Application Entry Point; Handle the Command Line -------------------------------------------- +// + +int main(int argc, char ** argv) +{ + int status; // Status flag + int i; + + perm_verb_flag = 0; // Clobber "permanent" verbose flag + cmdlnexec = argv[0]; // Obtain executable name + + endian = get_endianess(); // Get processor endianess + + for(i=0; i 1) + { // Full command line passed + status = process(argc - 1, argv + 1); + } + else + { // Interactive mode + status = 0; + interactive(); + } + + return status; +} diff --git a/rmac.h b/rmac.h new file mode 100644 index 0000000..b3eb171 --- /dev/null +++ b/rmac.h @@ -0,0 +1,214 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// RMAC.H - Main Application Code +// Copyright (C) 199x Landon Dyer, 2011 Reboot & Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer +// + +#ifndef __RMAC_H__ +#define __RMAC_H__ + +// +// TARGET SPECIFIC BUILD SETTINGS +// + +#ifdef WIN32 +#define PLATFORM "Win32" // Release platform - windows +#define _OPEN_FLAGS _O_TRUNC|_O_CREAT|_O_BINARY|_O_RDWR +#define _OPEN_INC _O_RDONLY|_O_BINARY +#define _PERM_MODE _S_IREAD|_S_IWRITE +#ifdef _MSC_VER + #if _MSC_VER > 1000 + #pragma warning(disable:4996) + #endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#else +#ifdef __GCCUNIX__ +#define PLATFORM "OSX/Linux" // Release platform - mac OS-X or linux +#define _OPEN_FLAGS O_TRUNC|O_CREAT|O_RDWR +#define _OPEN_INC O_RDONLY +#define _PERM_MODE S_IREAD|S_IWRITE +#include +#include +#include +#include +#include +#include +#include +#include +#else +#define PLATFORM "Unknown" // Release platform - not specified +#define _OPEN_FLAGS O_TRUNC|O_CREAT|O_RDWR +#define _OPEN_INC O_RDONLY +#define _PERM_MODE S_IREAD|S_IWRITE +#include +#include +#include +#include +#include +#include +#include +#endif +#endif + + + +#define BYTE unsigned char +#define WORD unsigned short +#define LONG unsigned long +#define VOID void + +#define ERROR (-1) // Generic error return +#define EOS '\0' // End of string +#define SPACE ' ' // Ascii space +#define SLASHCHAR '/' +#define SLASHSTRING "/" +#define VALUE LONG // Assembler value +#define TOKEN LONG // Assembler token +#define FNSIZ 128 // Maximum size of a filename +#define OK 0 // OK return +#define DEBUG if(debug) // Debug conditional +#define MAXARGV 100 // Maximum number of commandline args +#define STDOUT 1 // Standard output +#define ERROUT 2 // Error output +#define CREATMASK 0 + +// (Normally) non-printable tokens +#define COLON ':' // : (grumble: GNUmacs hates ':') +#define CONST 'a' // CONST +#define ACONST 'A' // ACONST +#define STRING 'b' // STRING
+#define SYMBOL 'c' // SYMBOL
+#define EOL 'e' // End of line +#define TKEOF 'f' // End of file (or macro) +#define DEQUALS 'g' // == +#define SET 149 // set +#define REG 'R' // reg +#define EQUREG 148 // equreg +#define CCDEF 183 // ccdef +#define DCOLON 'h' // :: +#define GE 'i' // >= +#define LE 'j' // <= +#define NE 'k' // <> or != +#define SHR 'l' // >> +#define SHL 'm' // << +#define UNMINUS 'n' // Unary '-' +#define DOTB 'B' // .b or .B or .s or .S +#define DOTW 'W' // .w or .W +#define DOTL 'L' // .l or .L +#define DOTI 'I' // .i or .I +#define ENDEXPR 'E' // End of expression + +// Object code formats +#define ALCYON 0 // Alcyon/DRI C object format +#define MWC 1 // Mark Williams object format +#define BSD 2 // BSD object format + +// Symbols +#define SYM struct _sym +SYM +{ + SYM *snext; // * -> Next symbol on hash-chain + SYM *sorder; // * -> Next sym in order of refrence + SYM *sdecl; // * -> Next sym in order of decleration + BYTE stype; // Symbol type + WORD sattr; // Attribute bits + LONG sattre; // Extended attribute bits + WORD senv; // Enviroment number + LONG svalue; // Symbol value + char *sname; // * -> Symbol's print-name +}; + +// Pointer type that can point to (almost) anything +#define PTR union _ptr +PTR +{ + char *cp; // Char + WORD *wp; // WORD + LONG *lp; // LONG + LONG lw; // LONG + SYM **sy; // SYM + TOKEN *tk; // TOKEN +}; + +// Symbol spaces +#define LABEL 0 // User-defined symbol +#define MACRO 1 // Macro definition +#define MACARG 2 // Macro argument +#define SY_UNDEF -1 // Undefined (lookup never matches it) + +// Symbol and expression attributes +#define DEFINED 0x8000 // Symbol has been defined +#define GLOBAL 0x4000 // Symbol has been .GLOBL'd +#define COMMON 0x2000 // Symbol has been .COMM'd +#define REFERENCED 0x1000 // Symbol has been referenced +#define EQUATED 0x0800 // Symbol was equated +#define SDECLLIST 0x0400 // Symbol is on 'sdecl'-order list + +// Expression spaces, ORed with symbol and expression attributes above +#define ABS 0x0000 // In absolute space +#define TEXT 0x0001 // Relative to text +#define DATA 0x0002 // Relative to data +#define BSS 0x0004 // Relative to BSS +//#define M6502 0x0008 // 6502/microprocessor (absolute) +#define TDB (TEXT|DATA|BSS) // Mask for text+data+bss + +// Sizes +#define SIZB 0x0001 // .b +#define SIZW 0x0002 // .w +#define SIZL 0x0004 // .l +#define SIZN 0x0008 // no .(size) specifier + +// RISC register bank definitions (used in extended symbol attributes also) +#define BANK_N 0x0000 // No register bank specified +#define BANK_0 0x0001 // Register bank zero specified +#define BANK_1 0x0002 // Register bank one specified +#define EQUATEDREG 0x0008 // Equated register symbol +#define UNDEF_EQUR 0x0010 +#define EQUATEDCC 0x0020 +#define UNDEF_CC 0x0040 + +#define RISCSYM 0x00010000 + +// Globals, externals etc +extern int verb_flag; +extern int debug; +extern int rgpu, rdsp; +extern int err_flag; +extern int err_fd; +extern int regbank; +extern char *firstfname; +extern int list_fd; +extern int as68_flag; +extern int list_flag; +extern int glob_flag; +extern int lsym_flag; +extern int sbra_flag; +extern int obj_format; +extern LONG amemtot; +extern int in_main; + +// Prototypes +void init_sym(void); +SYM *lookup(char *, int, int); +SYM *newsym(char *, int, int); +char *fext(char *, char *, int); +void cantcreat(char *); +int kmatch(char *, int *, int *, int *, int *); +void autoeven(int); +int nthpath(char *, int, char *); +void clear(char *, LONG); +char *copy(char *, char *, LONG); +int rmac_qsort(char *, int, int, int (*)()); +char *amem(LONG); + +#endif // __RMAC_H__ diff --git a/sect.c b/sect.c new file mode 100644 index 0000000..07170d8 --- /dev/null +++ b/sect.c @@ -0,0 +1,694 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// SECT.C - Code Generation, Fixups and Section Management +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "sect.h" +#include "error.h" +#include "mach.h" +#include "token.h" +#include "mark.h" +#include "expr.h" +#include "symbol.h" +#include "risca.h" +#include "listing.h" + +// Section descriptors +SECT sect[NSECTS]; // All sections... +int cursect; // Current section number + +// These are copied from the section descriptor, the current code chunk descriptor and the current +// fixup chunk descriptor when a switch is made into a section. They are copied back to the +// descriptors when the section is left. +WORD scattr; // Section attributes +LONG sloc; // Current loc in section + +CHUNK *scode; // Current (last) code chunk +LONG challoc; // #bytes alloc'd to code chunk +LONG ch_size; // #bytes used in code chunk +char *chptr; // Deposit point in code chunk buffer + +CHUNK *sfix; // Current (last) fixup chunk +LONG fchalloc; // #bytes alloc'd to fixup chunk +LONG fchsize; // #bytes used in fixup chunk +PTR fchptr; // Deposit point in fixup chunk buffer + +unsigned fwdjump[MAXFWDJUMPS]; // forward jump check table +unsigned fwindex = 0; // forward jump index + +// Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is associated +// with a location. +static char fusiztab[] = { + 0, // FU_QUICK + 1, // FU_BYTE + 2, // FU_WORD + 2, // FU_WBYTE + 4, // FU_LONG + 1, // FU_BBRA + 0, // (unused) + 1, // FU_6BRA +}; + +// Offset to REAL fixup location +static char fusizoffs[] = { + 0, // FU_QUICK + 0, // FU_BYTE + 0, // FU_WORD + 1, // FU_WBYTE + 0, // FU_LONG + 1, // FU_BBRA + 0, // (unused) + 0, // FU_6BRA +}; + +// +// --- Make a New (Clean) Section ------------------------------------------------------------------ +// + +void mksect(int sno, WORD attr) { + SECT *p; // Section pointer + + p = §[sno]; + p->scattr = attr; + p->sloc = 0; + p->scode = p->sfcode = NULL; + p->sfix = p->sffix = NULL; +} + +// +// --- Switch to Another Section (Copy Section & Chunk Descriptors to Global Vars for Fast Access) - +// + +void switchsect(int sno) { + SECT *p; // Section pointer + CHUNK *cp; // Chunk pointer + + cursect = sno; + p = §[sno]; + + scattr = p->scattr; // Copy section vars + sloc = p->sloc; + scode = p->scode; + sfix = p->sfix; + + if((cp = scode) != NULL) { // Copy code chunk vars + challoc = cp->challoc; + ch_size = cp->ch_size; + chptr = cp->chptr + ch_size; + } else challoc = ch_size = 0; + + if((cp = sfix) != NULL) { // Copy fixup chunk vars + fchalloc = cp->challoc; + fchsize = cp->ch_size; + fchptr.cp = cp->chptr + fchsize; + } else fchalloc = fchsize = 0; +} + +// +// --- Save Current Section ------------------------------------------------------------------------ +// + +void savsect(void) { + SECT *p; + + p = §[cursect]; + + p->scattr = scattr; // Bailout section vars + p->sloc = sloc; + + if(scode != NULL) // Bailout code chunk + scode->ch_size = ch_size; + + if(sfix != NULL) // Bailout fixup chunk + sfix->ch_size = fchsize; +} + +// +// --- Initialize Sections; Setup initial ABS, TEXT, DATA and BSS sections ------------------------- +// + +void init_sect(void) { + int i; // Iterator + + // Cleanup all sections + for(i = 0; i < NSECTS; ++i) + mksect(i, 0); + + // Construct default sections, make TEXT the current section + mksect(ABS, SUSED|SABS|SBSS); // ABS + mksect(TEXT, SUSED|TEXT ); // TEXT + mksect(DATA, SUSED|DATA ); // DATA + mksect(BSS, SUSED|BSS |SBSS); // BSS +// mksect(M6502, SUSED|TEXT ); // 6502 code section + + switchsect(TEXT); // Switch to TEXT for starters +} + +// +// ------------------------------------------------------------------------------------------------- +// Test to see if a location has a fixup sic'd on it. This is used by the listing +// generator to print 'xx's instead of '00's for forward references +// ------------------------------------------------------------------------------------------------- +// + +int fixtest(int sno, LONG loc) { + CHUNK *ch; + PTR fup; + char *fuend; + WORD w; + LONG xloc; + + stopmark(); // Force update to sect[] variables + + // Hairy, ugly linear search for a mark on our location; + // the speed doesn't matter, since this is only done when generating a listing, which is SLOW. + for(ch = sect[sno].sffix; ch != NULL; ch = ch->chnext) { + fup.cp = (char *)ch->chptr; + fuend = fup.cp + ch->ch_size; + + while(fup.cp < fuend) { + w = *fup.wp++; + xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK]; + fup.wp += 2; + + if(xloc == loc) + return((int)fusiztab[w & FUMASK]); + + if(w & FU_EXPR) { + w = *fup.wp++; + fup.lp += w; + } else ++fup.lp; + } + } + + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Check that there are at least `amt' bytes left in the current chunk. If there are not, +// allocate another chunk of at least `amt' bytes (and probably more). +// +// If `amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise. +// ------------------------------------------------------------------------------------------------- +// + +int chcheck(LONG amt) { + CHUNK *cp; + SECT *p; + + if(scattr & SBSS) return(0); // If in BSS section, forget it + + if(!amt) amt = CH_THRESHOLD; + + if((int)(challoc - ch_size) >= (int)amt) + return(0); + + if(amt < CH_CODE_SIZE) amt = CH_CODE_SIZE; + p = §[cursect]; + cp = (CHUNK *)amem((long)(sizeof(CHUNK) + amt)); + if(scode == NULL) { // First chunk in section + cp->chprev = NULL; + p->sfcode = cp; + } else { // Add chunk to other chunks + cp->chprev = scode; + scode->chnext = cp; + scode->ch_size = ch_size; // Save old chunk's globals + } + + // Setup chunk and global vars + cp->chloc = sloc; + cp->chnext = NULL; + challoc = cp->challoc = amt; + ch_size = cp->ch_size = 0; + chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK); + scode = p->scode = cp; + + return(0); +} + +// +// --- Arrange for a fixup on a location ----------------------------------------------------------- +// + +int fixup(WORD attr, LONG loc, TOKEN *fexpr) { + LONG i; + LONG len = 0; + CHUNK *cp; + SECT *p; + + // Compute length of expression (could be faster); determine if it's the single-symbol case; + // no expression if it's just a mark. This code assumes 16 bit WORDs and 32 bit LONGs + if(*fexpr == SYMBOL && fexpr[2] == ENDEXPR) + //if((attr & 0x0F00) == FU_JR) { + if((attr & 0x0200) == FU_JR) { + i = 18; // Just a single symbol + } else { + i = 14; + } + else { + attr |= FU_EXPR; + for(len = 0; fexpr[len] != ENDEXPR; ++len) + if(fexpr[len] == CONST || fexpr[len] == SYMBOL) + ++len; + ++len; // Add 1 for ENDEXPR + i = (len << 2) + 12; + } + + // Maybe alloc another fixup chunk for this one to fit in + if((fchalloc - fchsize) < i) { + p = §[cursect]; + cp = (CHUNK *)amem((long)(sizeof(CHUNK) + CH_FIXUP_SIZE)); + if(sfix == NULL) { // First fixup chunk in section + cp->chprev = NULL; + p->sffix = cp; + } else { // Add to other chunks + cp->chprev = sfix; + sfix->chnext = cp; + sfix->ch_size = fchsize; + } + + // Setup fixup chunk and its global vars + cp->chnext = NULL; + fchalloc = cp->challoc = CH_FIXUP_SIZE; + fchsize = cp->ch_size = 0; + fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK); + sfix = p->sfix = cp; + } + + // Record fixup type, fixup location, and the file number and line number the fixup is + // located at. + *fchptr.wp++ = attr; + *fchptr.lp++ = loc; + *fchptr.wp++ = cfileno; + *fchptr.wp++ = (WORD)curlineno; + // Store postfix expression or pointer to a single symbol, or nothing for a mark. + if(attr & FU_EXPR) { + *fchptr.wp++ = (WORD)len; + while(len--) + *fchptr.lp++ = (LONG)*fexpr++; + } else { + *fchptr.lp++ = (LONG)fexpr[1]; + } + + //if((attr & 0x0F00) == FU_JR) { + if((attr & 0x0200) == FU_JR) { + if(orgactive) *fchptr.lp++ = orgaddr; + else *fchptr.lp++ = 0x00000000; + } + + fchsize += i; + + return(0); +} + +// +// --- Resolve all Fixups -------------------------------------------------------------------------- +// + +int fixups(void) { + unsigned i; // Iterator + char buf[EBUFSIZ]; + + if(glob_flag) // Make undefined symbols GLOBL + syg_fix(); + resfix(TEXT); + resfix(DATA); + + // We need to do a final check of forward 'jump' destination addresses that are external + for(i = 0; i < MAXFWDJUMPS; i++) { + if(fwdjump[i]) { + err_setup(); + sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and " + "cannot have its aligment validated", fwdjump[i]); + if(listing > 0) ship_ln(buf); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s\n", buf); + } + } + + return(0); +} + +// +// --- Resolve Fixups in a Section ----------------------------------------------------------------- +// + +int resfix(int sno) { + SECT *sc; // Section + CHUNK *ch; + PTR fup; // Current fixup + WORD *fuend; // End of last fixup (in this chunk) + CHUNK *cch; // Cached chunk for target + WORD w; // Fixup word (type+modes+flags) + char *locp; // Location to fix (in cached chunk) + LONG loc; // Location to fixup + VALUE eval; // Expression value + WORD eattr; // Expression attrib + SYM *esym; // External symbol involved in expr + SYM *sy; // (Temp) pointer to a symbol + WORD i; // (Temp) word + WORD tdb; // eattr & TDB + LONG oaddr; + int reg2; + WORD flags; + unsigned page_jump = 0; + unsigned address = 0; + unsigned j; // iterator + char buf[EBUFSIZ]; + + sc = §[sno]; + ch = sc->sffix; + + if(ch == NULL) + return(0); + + cch = sc->sfcode; // "cache" first chunk + if(cch == NULL) // Can't fixup a sect with nothing in it + return(0); + + do { + fup.cp = ch->chptr; // fup -> start of chunk + fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk + while(fup.wp < fuend) { + w = *fup.wp++; + loc = *fup.lp++; + cfileno = *fup.wp++; + curlineno = (int)*fup.wp++; + + esym = NULL; + // Search for chunk containing location to fix up; compute a pointer to the location + // (in the chunk). Often we will find the fixup is in the "cached" chunk, so the + // linear-search is seldom executed. + if(loc < cch->chloc || loc >= (cch->chloc + cch->ch_size)) { + for(cch = sc->sfcode; cch != NULL; cch = cch->chnext) + if(loc >= cch->chloc && loc < (cch->chloc + cch->ch_size)) + break; + if(cch == NULL) { + interror(7); // Fixup (loc) out of range + // NOTREACHED + } + } + locp = cch->chptr + (loc - cch->chloc); + + eattr = 0; + + // Compute expression/symbol value and attribs + if(w & FU_EXPR) { // Complex expression + i = *fup.wp++; + if(evexpr(fup.tk, &eval, &eattr, &esym) != OK) { + fup.lp += i; + continue; + } + fup.lp += i; + } else { // Simple symbol + sy = *fup.sy++; + eattr = sy->sattr; + if(eattr & DEFINED) + eval = sy->svalue; + else eval = 0; + + if((eattr & (GLOBAL|DEFINED)) == GLOBAL) + esym = sy; + } + tdb = (WORD)(eattr & TDB); + // If the expression is undefined and no external symbol is involved, then it's an error. + if(!(eattr & DEFINED) && esym == NULL) { + error(undef_error); + continue; + } + + + if(((w & 0x0F00) == FU_MOVEI) && esym) + esym->sattre |= RISCSYM; + + // Do the fixup + // + // If a PC-relative fixup is undefined, its value is *not* subtracted from the location + // (that will happen in the linker when the external reference is resolved). + // + // MWC expects PC-relative things to have the LOC subtracted from the value, if the + // value is external (that is, undefined at this point). + // + // PC-relative fixups must be DEFINED and either in the same section (whereupon the + // subtraction takes place) or ABS (with no subtract). + if(w & FU_PCREL) { + if(eattr & DEFINED) { + if(tdb == sno) eval -= (VALUE)loc; + else if(tdb) { + error("PC-relative expr across sections"); + continue; + } + + if(sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100)) + warn("unoptimized short branch"); + } else + if(obj_format == MWC) eval -= (VALUE)loc; + + tdb = 0; + eattr &= ~TDB; + } + + // Do fixup classes + switch((int)(w & FUMASK)) { + // FU_BBRA fixes up a one-byte branch offset. + case FU_BBRA: + if(!(eattr & DEFINED)) { + error("external short branch"); + continue; + } + eval -= 2; + if(eval + 0x80 >= 0x100) + goto range; + if(eval == 0) { + error("illegal bra.s with zero offset"); + continue; + } + *++locp = (char)eval; + break; + // Fixup one-byte value at locp + 1. + case FU_WBYTE: + ++locp; + // FALLTHROUGH + // Fixup one-byte forward references + case FU_BYTE: + if(!(eattr & DEFINED)) { + error("external byte reference"); + continue; + } + if(tdb) { + error("non-absolute byte reference"); + continue; + } + if((w & FU_PCREL) && eval + 0x80 >= 0x100) goto range; + if(w & FU_SEXT) { + if(eval + 0x100 >= 0x200) goto range; + } else + if(eval >= 0x100) goto range; + + *locp = (char)eval; + break; + // Fixup WORD forward references; + // the word could be unaligned in the section buffer, so we have to be careful. + case FU_WORD: + if(((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR)) { + oaddr = *fup.lp++; + if(oaddr) { + reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F; + } else { + reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F; + } + if((w & 0x0F00) == FU_MJR) { + // Main code destination alignment checking here for forward declared labels + address = (oaddr) ? oaddr : loc; + if(((address >= 0xF03000) && (address < 0xF04000) && (eval < 0xF03000)) || + ((eval >= 0xF03000) && (eval < 0xF04000) && (address < 0xF03000)) ) { + warni("* \'jr\' at $%08X - cannot jump relative between " + "main memory and local gpu ram", address); + } else { + page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00); + if(page_jump) { + // This jump is to a page outside of the current 256 byte page + if(eval % 4) { + warni("* \'jr\' at $%08X - destination address not aligned for long page jump, " + "insert a \'nop\' before the destination address", address); + } + } else { + // This jump is in the current 256 byte page + if((eval - 2) % 4) { + warni("* \'jr\' at $%08X - destination address not aligned for short page jump, " + "insert a \'nop\' before the destination address", address); + } + } + } + } + if((reg2 < -16) || (reg2 > 15)) { + error("relative jump out of range"); + break; + } + *locp = (char)(*locp | ((reg2 >> 3) & 0x03)); + locp++; + *locp = (char)(*locp | ((reg2 & 0x07) << 5)); + break; + } + if((w & 0x0F00) == FU_NUM15) { + if(eval < -16 || eval > 15) { + error("constant out of range"); + break; + } + *locp = (char)(*locp | ((eval >> 3) & 0x03)); + locp++; + *locp = (char)(*locp | ((eval & 0x07) << 5)); + break; + } + if((w & 0x0F00) == FU_NUM31) { + if(eval < 0 || eval > 31) { + error("constant out of range"); + break; + } + *locp = (char)(*locp | ((eval >> 3) & 0x03)); + locp++; + *locp = (char)(*locp | ((eval & 0x07) << 5)); + break; + } + if((w & 0x0F00) == FU_NUM32) { + if(eval < 1 || eval > 32) { + error("constant out of range"); + break; + } + if(w & FU_SUB32) + eval = (32 - eval); + eval = (eval == 32) ? 0 : eval; + *locp = (char)(*locp | ((eval >> 3) & 0x03)); + locp++; + *locp = (char)(*locp | ((eval & 0x07) << 5)); + break; + } + if((w & 0x0F00) == FU_REGONE) { + if(eval < 0 || eval > 31) { + error("register value out of range"); + break; + } + *locp = (char)(*locp | ((eval >> 3) & 0x03)); + locp++; + *locp = (char)(*locp | ((eval & 0x07) << 5)); + break; + } + if((w & 0x0F00) == FU_REGTWO) { + if(eval < 0 || eval > 31) { + error("register value out of range"); + break; + } + locp++; + *locp = (char)(*locp | (eval & 0x1F)); + break; + } + + if(!(eattr & DEFINED)) { + if(w & FU_PCREL) + w = MPCREL | MWORD; + else w = MWORD; + rmark(sno, loc, 0, w, esym); + } else { + if(tdb) + rmark(sno, loc, tdb, MWORD, NULL); + if(w & FU_SEXT) { + if(eval + 0x10000 >= 0x20000) + goto range; + } else + if(w & FU_ISBRA) { // Range-check BRA and DBRA + if(eval + 0x8000 >= 0x10000) + goto range; + } else + if(eval >= 0x10000) + goto range; + } + + *locp++ = (char)(eval >> 8); + *locp = (char)eval; + break; + // Fixup LONG forward references; + // the long could be unaligned in the section buffer, so be careful (again). + case FU_LONG: + if((w & 0x0F00) == FU_MOVEI) { + address = loc + 4; + if(eattr & DEFINED) { + for(j = 0; j < fwindex; j++) { + if(fwdjump[j] == address) { + page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00); + if(page_jump) { + if(eval % 4) { + err_setup(); + sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, " + "insert a \'nop\' before the destination address", address); + if(listing > 0) ship_ln(buf); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s\n", buf); + } + } else { + if(!(eval & 0x0000000F) || ((eval - 2) % 4)) { + err_setup(); + sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, " + "insert a \'nop\' before the destination address", address); + if(listing > 0) ship_ln(buf); + if(err_flag) write(err_fd, buf, (LONG)strlen(buf)); + else printf("%s\n", buf); + } + } + // Clear this jump as it has been checked + fwdjump[j] = 0; + j = fwindex; + } + } + } + eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000); + flags = (MLONG|MMOVEI); + } else flags = MLONG; + if(!(eattr & DEFINED)) { + rmark(sno, loc, 0, flags, esym); + } + else if(tdb) { + rmark(sno, loc, tdb, flags, NULL); + } + *locp++ = (char)(eval >> 24); + *locp++ = (char)(eval >> 16); + *locp++ = (char)(eval >> 8); + *locp = (char)eval; + break; + // Fixup a 3-bit "QUICK" reference in bits 9..1 + // (range of 1..8) in a word. Really bits 1..3 in a byte. + case FU_QUICK: + if(!(eattr & DEFINED)) { + error("External quick reference"); + continue; + } + + if(eval < 1 || eval > 8) + goto range; + *locp |= (eval & 7) << 1; + break; + // Fix up 6502 funny branch + case FU_6BRA: + eval -= (loc + 1); + if(eval + 0x80 >= 0x100) + goto range; + *locp = (char)eval; + break; + default: + interror(4); // Bad fixup type + // NOTREACHED + } + continue; + + range: + + error("expression out of range"); + } + + ch = ch->chnext; + } while(ch != NULL); + + return(0); +} diff --git a/sect.h b/sect.h new file mode 100644 index 0000000..7594056 --- /dev/null +++ b/sect.h @@ -0,0 +1,146 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// SECT.H - Code Generation, Fixups and Section Management +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __SECT_H__ +#define __SECT_H__ + +#include "rmac.h" + +// Macros to deposit code in the current section +// D_rword deposits a "6502" format (low, high) word (01). +// D_rlong deposits a MWC "canonical byte order" longword (2301). +#define D_byte(b) {*chptr++=(char)b; ++sloc; ++ch_size; if(orgactive) ++orgaddr;} +#define D_word(w) {chcheck(2);*chptr++=(char)(w>>8); *chptr++=(char)w; \ + sloc+=2; ch_size+=2; if(orgactive) orgaddr += 2;} +#define D_long(lw) {*chptr++=(char)(lw>>24); *chptr++=(char)(lw>>16);\ + *chptr++=(char)(lw>>8); *chptr++=(char)lw; \ + sloc+=4; ch_size += 4; if(orgactive) orgaddr += 4;} +#define D_rword(w) {*chptr++=(char)w; *chptr++=(char)(w>>8); \ + sloc+=2; ch_size+=2;if(orgactive) orgaddr += 2;} +#define D_rlong(lw) {*chptr++=(char)(lw>>16);*chptr++=(char)(lw>>24);\ + *chptr++=(char)lw;*chptr++=(char)(lw>>8); \ + sloc+=4; ch_size += 4;if(orgactive) orgaddr += 4;} + +#define NSECTS 16 // Max. number of sections + +// Tunable (storage) definitions +#define CH_THRESHOLD 64 // Minimum amount of space in code chunk +#define CH_CODE_SIZE 2048 // Code chunk normal allocation +#define CH_FIXUP_SIZE 1024 // Fixup chunk normal allocation + +// Section attributes (.scattr) +#define SUSED 0x8000 // Section is used (really, valid) +#define SBSS 0x4000 // Section can contain no data +#define SABS 0x2000 // Section is absolute +#define SPIC 0x1000 // Section is position-independent code + +// Fixup record a WORD of these bits, followed by a loc and then a pointer +// to a symbol or an ENDEXPR-terminated postfix expression. +// +// SYMBOL EXPRESSION +// ------ ---------- +// ~FU_EXPR FU_EXPR fixup type +// loc.L loc.L location in section +// fileno.W fileno.W file number fixup occurred in +// lineno.W lineno.W line number fixup occurred in +// symbol.L size.W &symbol / size of expression +// token.L expression list +// (etc) +// ENDEXPR.L (end of expression) +#define FUMASK 007 // Mask for fixup cases: +#define FU_QUICK 000 // Fixup 3-bit quick instr field +#define FU_BYTE 001 // Fixup byte +#define FU_WORD 002 // Fixup word +#define FU_WBYTE 003 // Fixup byte (at loc+1) +#define FU_LONG 004 // Fixup long +#define FU_BBRA 005 // Fixup byte branch +#define FU_6BRA 007 // Fixup 6502-format branch offset +#define FU_SEXT 010 // Ok to sign extend +#define FU_PCREL 020 // Subtract PC first +#define FU_EXPR 040 // Expression (not symbol) follows + +#define FU_MOVEI 0x0100 +#define FU_JR 0x0200 +#define FU_MJR 0x0300 +#define FU_REGONE 0x0400 +#define FU_NUM15 0x0500 +#define FU_NUM31 0x0600 +#define FU_NUM32 0x0700 +#define FU_REGTWO 0x0800 +#define FU_SUB32 0x1000 +#define FU_ISBRA 0x2000 // Word forward fixup is a BRA or DBRA +#define FU_LBRA 0x4000 // Long branch, for short branch detect +#define FU_DONE 0x8000 // Fixup has been done + +// Chunks are used to hold generated code and fixup records +#define CHUNK struct _chunk +CHUNK { + CHUNK *chnext; // Next, previous chunks in section + CHUNK *chprev; + LONG chloc; // Base addr of this chunk + LONG challoc; // #bytes allocated for chunk + LONG ch_size; // #bytes chunk actually uses + char *chptr; // Data for this chunk +}; + +// Section descriptor +#define SECT struct _sect +SECT { + WORD scattr; // Section attributes + LONG sloc; // Current loc-in / size-of section + CHUNK *sfcode; // First chunk in section + CHUNK *scode; // Last chunk in section + CHUNK *sffix; // First fixup chunk + CHUNK *sfix; // Last fixup chunk +}; + +// A mark is of the form: +// .W section mark is relative to, and flags in upper byte +// .L location of mark in "from" section +// .W [from] new from section +// .L [symbol] symbol involved in external reference +#define MCHUNK struct _mchunk +MCHUNK { + MCHUNK *mcnext; // Next mark chunk + PTR mcptr; // Vector of marks + LONG mcalloc; // #marks allocted to mark block + LONG mcused; // #marks used in block +}; + +#define MWORD 0x0000 // Marked word +#define MLONG 0x0100 // Marked long +#define MMOVEI 0x0200 +#define MCHFROM 0x8000 // Mark includes change-to-from +#define MSYMBOL 0x4000 // Mark includes symbol number +#define MCHEND 0x2000 // Indicates end of mark chunk +#define MPCREL 0x1000 // Mark is PC-relative + +#define MAXFWDJUMPS 1024 // Maximum forward jumps to check +extern unsigned fwdjump[MAXFWDJUMPS]; +extern unsigned fwindex; + +// Globals, external etc +extern LONG sloc; +extern WORD scattr; +extern char *chptr; +extern LONG ch_size; +extern int cursect; +extern SECT sect[]; +extern LONG challoc; +extern CHUNK *scode; + +// Prototypes +void init_sect(void); +void switchsect(int); +void savsect(void); +int fixtest(int, LONG); +int chcheck(LONG); +int fixup(WORD, LONG, TOKEN *); +int fixups(void); +int resfix(int); + +#endif // __SECT_H__ \ No newline at end of file diff --git a/symbol.c b/symbol.c new file mode 100644 index 0000000..855fe44 --- /dev/null +++ b/symbol.c @@ -0,0 +1,380 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// SYMBOL.C - Symbol Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "symbol.h" +#include "listing.h" +#include "procln.h" +#include "error.h" + +static SYM *sytab[NBUCKETS]; // User symbol-table header +int curenv; // Current enviroment number +SYM *sorder; // * -> Symbols, in order of reference +SYM *sordtail; // * -> Last symbol in sorder list +SYM *sdecl; // * -> Symbols, in order of declaration +SYM *sdecltail; // * -> Last symbol in sdecl list + +// Tags for marking symbol spaces +// a = absolute +// t = text +// d = data +// ! = "impossible!" +// b = BSS +static char tdb_text[8] = { + 'a', 't', 'd', '!', 'b', SPACE, SPACE, SPACE +}; + +// +// --- Initialize Symbol Table --------------------------------------------------------------------- +// + +void init_sym(void) { + int i; // Iterator + + for(i = 0; i < NBUCKETS; ++i) // Initialise symbol hash table + sytab[i] = NULL; + + curenv = 1; // Init local symbol enviroment + sorder = NULL; // Init symbol-reference list + sordtail = NULL; + sdecl = NULL; // Init symbol-decl list + sdecltail = NULL; +} + +// +// --- Allocate and Return Pointer to a Copy of a String ------------------------------------------- +// + +char *nstring(char *str) { + long i; + char *s, *d; + + for(i = 0; str[i]; ++i) + ; + s = d = amem(i + 1); + while(*str) + *d++ = *str++; + *d++ = '\0'; + + return(s); +} + +// +// --- Hash the Print Name and Enviroment Number --------------------------------------------------- +// +int syhash(char *name, int envno) { + int sum, k; // Hash calculation + + k = 0; + for(sum = envno; *name; ++name) { + if(k++ == 1) + sum += *name << 2; + else + sum += *name; + } + + return(sum & (NBUCKETS - 1)); +} + +// +// --- Make a new symbol of type `type' in enviroment `envno' -------------------------------------- +// + +SYM *newsym(char *name, int type, int envno) { + int hash; // Symbol hash value + SYM *sy; // Pointer to symbol + + + // Allocate the symbol + sy = (SYM *)amem((long)(sizeof(SYM))); + if(sy == NULL) { + printf("SYMALLOC ERROR (%s)\n", name); + return(NULL); + } + + sy->sname = nstring(name); + + // Fill-in the symbol + sy->stype = (BYTE)type; + sy->senv = (WORD)envno; + sy->sattr = 0; + if(rgpu || rdsp) sy->sattre = RISCSYM; + else sy->sattre = 0; + sy->svalue = 0; + + // Install symbol in symbol table + hash = syhash(name, envno); + sy->snext = sytab[hash]; + sytab[hash] = sy; + + // Append symbol to symbol-order list + if(sorder == NULL) + sorder = sy; // Add first symbol + else + sordtail->sorder = sy; // Or append to tail of list + + sy->sorder = NULL; + sordtail = sy; + + return(sy); // Return pointer to symbol +} + +// +// --- Lookup the symbol `name', of the specified type, with the specified enviroment level -------- +// + +SYM *lookup(char *name, int type, int envno) { + SYM *sy; // Symbol record pointer + int k, sum; // Hash bucket calculation + char *s; // String pointer + + // Pick a hash-bucket (SAME algorithm as syhash()) + k = 0; + s = name; + for(sum = envno; *s;) { + if(k++ == 1) + sum += *s++ << 2; + else sum += *s++; + } + + sy = sytab[sum & (NBUCKETS-1)]; + + // Do linear-search for symbol in bucket + while(sy != NULL) { + if(sy->stype == type && // Type, envno and name must match + sy->senv == envno && + *name == *sy->sname && // Fast check for first character + !strcmp(name, sy->sname)) + break; + else sy = sy->snext; + } + + return(sy); // Return NULL or matching symbol +} + +// +// --- Put symbol on "order-of-declaration" list of symbols ---------------------------------------- +// + +void sym_decl(SYM *sym) { + if(sym->sattr & SDECLLIST) return; // Already on list + sym->sattr |= SDECLLIST; // Mark "already on list" + + if(sdecl == NULL) + sdecl = sym; // First on decl-list + else + sdecltail->sdecl = sym; // Add to end of list + + sym->sdecl = NULL; // Fix up list's tail + sdecltail = sym; +} + +// +// --- Make all referenced, undefined symbols global ----------------------------------------------- +// + +int syg_fix(void) { + SYM *sy; + + DEBUG printf("~syg_fix()\n"); + + // Scan through all symbols; + // If a symbol is REFERENCED but not DEFINED, then make it global. + for(sy = sorder; sy != NULL; sy = sy->sorder) + if(sy->stype == LABEL && sy->senv == 0 && + ((sy->sattr & (REFERENCED|DEFINED)) == REFERENCED)) + sy->sattr |= GLOBAL; + + return(0); +} + +// +// --- Convert string to uppercase ----------------------------------------------------------------- +// + +int uc_string(char *s) { + for(; *s; ++s) + if(*s >= 'a' && *s <= 'z') + *s -= 32; + return(0); +} + +// +// ------------------------------------------------------------------------------------------------- +// Assign numbers to symbols that are to be exported or imported. The symbol number is put in +// `.senv'. Return the number of symbols that will be in the symbol table. +// ------------------------------------------------------------------------------------------------- +// + +int sy_assign(char *buf, char *(*constr)()) { + SYM *sy; + int scount; + //int i; + + scount = 0; + + if(buf == NULL) + // Append all symbols not appearing on the .sdecl list to the end of the .sdecl list + for(sy = sorder; sy != NULL; sy = sy->sorder) { + + // Essentially the same as 'sym_decl()' above: + if(sy->sattr & SDECLLIST) continue; // Already on list + sy->sattr |= SDECLLIST; // Mark "on the list" + + if(sdecl == NULL) sdecl = sy; // First on decl-list + else sdecltail->sdecl = sy; // Add to end of list + + sy->sdecl = NULL; // Fix up list's tail + sdecltail = sy; + } + + // Run through all symbols (now on the .sdecl list) and assign numbers to them. We also pick + // which symbols should be global or not here. + for(sy = sdecl; sy != NULL; sy = sy->sdecl) { + + if(sy->sattre & UNDEF_EQUR) continue; // Don't want undefined on our list + if(sy->sattre & UNDEF_CC) continue; + + // Export or import external references, and export COMMON blocks. + if((sy->stype == LABEL) && + ((sy->sattr & (GLOBAL|DEFINED)) == (GLOBAL|DEFINED) || + (sy->sattr & (GLOBAL|REFERENCED)) == (GLOBAL|REFERENCED)) || + (sy->sattr & COMMON)) { + sy->senv = (WORD)scount++; + if(buf != NULL) buf = (*constr)(buf, sy, 1); + } else + // Export vanilla labels (but don't make them global). An exception is made for equates, + // which are not exported unless they are referenced. + if(sy->stype == LABEL && lsym_flag && + (sy->sattr & (DEFINED|REFERENCED)) != 0 && + (!as68_flag || *sy->sname != 'L') ) { + sy->senv = (WORD)scount++; + if(buf != NULL) buf = (*constr)(buf, sy, 0); + } + } + + return(scount); +} + +// +// --- Generate symbol table for listing file ------------------------------------------------------ +// + +int symtable(void) { + int i; + int j; + SYM *q = NULL; + SYM *p; + SYM *r; + SYM *k; + SYM **sy; + SYM *colptr[4]; + char ln[150]; + char ln1[150]; + char ln2[20]; + char c, c1; + WORD w; + int ww; + int colhei; + extern int pagelen; + + colhei = pagelen - 5; + + // Allocate storage for list headers and partition all labels. + // Throw away macros and macro arguments. + sy = (SYM **)amem((LONG)(128 * sizeof(LONG))); + for(i = 0; i < 128; ++i) sy[i] = NULL; + + for(i = 0; i < NBUCKETS; ++i) + for(p = sytab[i]; p != NULL; p = k) { + k = p->snext; + j = *p->sname; + r = NULL; + if(p->stype != LABEL) continue; // Ignore non-labels + if(p->sattre & UNDEF_EQUR) continue; + + for(q = sy[j]; q != NULL; q = q->snext) + if(strcmp(p->sname, q->sname) < 0) + break; + else r = q; + + if(r == NULL) { // Insert at front of list + p->snext = sy[j]; + sy[j] = p; + } else { // Insert in middle or append to list + p->snext = r->snext; + r->snext = p; + } + } + + // Link all symbols onto one list again + p = NULL; + for(i = 0; i < 128; ++i) + if((r = sy[i]) != NULL) { + if(p == NULL) + q = r; + else q->snext = r; + + while(q->snext != NULL) + q = q->snext; + + if(p == NULL) + p = r; + } + + eject(); + strcpy(subttl, "Symbol Table"); + + while(p != NULL) { + for (i = 0; i < 4; ++i) { + colptr[i] = p; + for(j = 0; j < colhei; ++j) + if(p == NULL) + break; + else p = p->snext; + } + + for(i = 0; i < colhei; ++i) { + *ln = EOS; + if(colptr[0] == NULL) + break; + + for(j = 0; j < 4; ++j) { + if((q = colptr[j]) == NULL) + break; + colptr[j] = q->snext; + w = q->sattr; + ww = q->sattre; + // Pick a tag: + // c common + // x external reference + // g global (export) + // space nothing special + c1 = SPACE; + c = SPACE; + + if(w & COMMON) c = 'c'; + else if((w & (DEFINED|GLOBAL)) == GLOBAL) c = 'x'; + else if(w & GLOBAL) c = 'g'; + + c1 = tdb_text[w & TDB]; + if(c == 'x') strcpy(ln2, "external"); + else { + sprintf(ln2, "%08lx", q->svalue); + uc_string(ln2); + } + + sprintf(ln1, " %16s %s %c%c%c", q->sname, ln2, (ww & EQUATEDREG) ? 'e' : SPACE, c1, c); + + strcat(ln, ln1); + } + ship_ln(ln); + } + eject(); + } + + return(0); +} diff --git a/symbol.h b/symbol.h new file mode 100644 index 0000000..899ba6a --- /dev/null +++ b/symbol.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// SYMBOL.H - Symbol Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#ifndef __SYMBOL_H__ +#define __SYMBOL_H__ + +#include "rmac.h" + +// Macros +#define NBUCKETS 256 // Number of hash buckets (power of 2) + +// Globals, externals etc +extern int curenv; +extern char subttl[]; + +// Prototypes +SYM *lookup(char *, int, int); +void init_sym(void); +SYM *newsym(char *, int, int); +char *nstring(char *); +void sym_decl(SYM *); +int syg_fix(void); +int symtable(void); +int sy_assign(char *, char *(*)()); + +#endif // __SYMBOL_H__ \ No newline at end of file diff --git a/token.c b/token.c new file mode 100644 index 0000000..ad462f3 --- /dev/null +++ b/token.c @@ -0,0 +1,1196 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// TOKEN.C - Token Handling +// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source Utilised with the Kind Permission of Landon Dyer + +#include "token.h" +#include "symbol.h" +#include "procln.h" +#include "macro.h" +#include "error.h" + +#define DECL_KW // Declare keyword arrays +#define DEF_KW // Declare keyword values +#include "kwtab.h" // Incl generated keyword tables & defs + +int lnsave; // 1; strcpy() text of current line +int curlineno; // Current line number +int totlines; // Total # of lines +int mjump_align = 0; // mjump alignment flag +char lntag; // Line tag +char *curfname; // Current filename +char tolowertab[128]; // Uppercase ==> lowercase +char hextab[128]; // Table of hex values +char dotxtab[128]; // Table for ".b", ".s", etc. +char irbuf[LNSIZ]; // Text for .rept block line +char lnbuf[LNSIZ]; // Text of current line +WORD filecount; // Unique file number counter +WORD cfileno; // Current file number +TOKEN *tok; // Ptr to current token +TOKEN *etok; // Ptr past last token in tokbuf[] +TOKEN tokeol[1] = {EOL}; // Bailout end-of-line token + +// File record, used to maintain a list of every include file ever visited +#define FILEREC struct _filerec +FILEREC { + FILEREC *frec_next; + char *frec_name; +}; + +FILEREC *filerec; +FILEREC *last_fr; + +INOBJ *cur_inobj; // Ptr current input obj (IFILE/IMACRO) +static INOBJ *f_inobj; // Ptr list of free INOBJs +static IFILE *f_ifile; // Ptr list of free IFILEs +static IMACRO *f_imacro; // Ptr list of free IMACROs + +static TOKEN tokbuf[TOKBUFSIZE]; // Token buffer (stack-like, all files) + +char chrtab[] = { + ILLEG, ILLEG, ILLEG, ILLEG, // NUL SOH STX ETX + ILLEG, ILLEG, ILLEG, ILLEG, // EOT ENQ ACK BEL + ILLEG, WHITE, ILLEG, ILLEG, // BS HT LF VT + WHITE, ILLEG, ILLEG, ILLEG, // FF CR SO SI + + ILLEG, ILLEG, ILLEG, ILLEG, // DLE DC1 DC2 DC3 + ILLEG, ILLEG, ILLEG, ILLEG, // DC4 NAK SYN ETB + ILLEG, ILLEG, ILLEG, ILLEG, // CAN EM SUB ESC + ILLEG, ILLEG, ILLEG, ILLEG, // FS GS RS US + + WHITE, MULTX, MULTX, SELF, // SP ! " # + MULTX+CTSYM, MULTX, SELF, MULTX, // $ % & ' + SELF, SELF, SELF, SELF, // ( ) * + + SELF, SELF, STSYM, SELF, // , - . / + + DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 0 1 + DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 2 3 + DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 4 5 + DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 6 7 + DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 8 9 + MULTX, MULTX, // : ; + MULTX, MULTX, MULTX, STSYM+CTSYM, // < = > ? + + MULTX, STSYM+CTSYM+HDIGIT, // @ A + (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // B C + STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // D E + STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // F G + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // H I J K + (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // L M N O + + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // P Q R S + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // T U V W + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // X Y Z [ + SELF, SELF, MULTX, STSYM+CTSYM, // \ ] ^ _ + + ILLEG, STSYM+CTSYM+HDIGIT, // ` a + (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // b c + STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // d e + STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // f g + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // h i j k + (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // l m n o + + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // p q r s + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // t u v w + STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // x y z { + SELF, SELF, SELF, ILLEG // | } ~ DEL +}; + +// Names of registers +static char *regname[] = { + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "pc", "ssp", "usp", "sr", "ccr" +}; + +static char *riscregname[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" +}; + +// +// --- Make `fnum' the Current `curfname' ---------------------------------------------------------- +// + +void setfnum(WORD fnum) { + FILEREC *fr; + + for(fr = filerec; fr != NULL && fnum--; fr = fr->frec_next) + ; + + if(fr == NULL) + curfname = "(*top*)"; + else + curfname = fr->frec_name; +} + +// +// --- Allocate an IFILE or IMACRO ----------------------------------------------------------------- +// + +INOBJ *a_inobj(int typ) { + INOBJ *inobj; + IFILE *ifile; + IMACRO *imacro; + + // Allocate and initialize INOBJ first + if(f_inobj == NULL) + inobj = (INOBJ *)amem((LONG)sizeof(INOBJ)); + else { + inobj = f_inobj; + f_inobj = f_inobj->in_link; + } + + switch(typ) { + case SRC_IFILE: // Alloc and init an IFILE + if(f_ifile == NULL) + ifile = (IFILE *)amem((LONG)sizeof(IFILE)); + else { + ifile = f_ifile; + f_ifile = f_ifile->if_link; + } + inobj->inobj.ifile = ifile; + break; + case SRC_IMACRO: // Alloc and init an IMACRO + if(f_imacro == NULL) + imacro = (IMACRO *)amem((LONG)sizeof(IMACRO)); + else { + imacro = f_imacro; + f_imacro = f_imacro->im_link; + } + inobj->inobj.imacro = imacro; + break; + case SRC_IREPT: // Alloc and init an IREPT + inobj->inobj.irept = (IREPT *)amem((LONG)sizeof(IREPT)); + DEBUG printf("alloc IREPT\n"); + break; + } + + // Install INOBJ on top of input stack + inobj->in_ifent = ifent; // Record .if context on entry + inobj->in_type = (WORD)typ; + inobj->in_otok = tok; + inobj->in_etok = etok; + inobj->in_link = cur_inobj; + cur_inobj = inobj; + + return(inobj); +} + +// +// ------------------------------------------------------------------------------------------------- +// Perform macro substitution from 'orig' to 'dest'. Return OK or some error. +// A macro reference is in one of two forms: +// \name +// \{name} +// A doubled backslash (\\) is compressed to a single backslash (\). +// Argument definitions have been pre-tokenized, so we have to turn them back into text. This +// means that numbers, in particular, become hex, regardless of their representation when the macro +// was invoked. This is a hack. +// A label may appear at the beginning of the line: +// : +// (the colon must be in the first column). These labels are stripped before macro expansion takes +// place. +// ------------------------------------------------------------------------------------------------- +// + +int mexpand(char *src, char *dest, int destsiz) { + char *s; + char *d = NULL; + char *dst; // Next dest slot + char *edst; // End+1 of dest buffer + int i; + int questmark; // \? for testing argument existence + TOKEN *tk; + char mname[128]; // Assume max size of a formal arg name + int macnum; + SYM *arg; + IMACRO *imacro; + char numbuf[20]; // Buffer for text of CONSTs + + imacro = cur_inobj->inobj.imacro; + macnum = (int)(imacro->im_macro->sattr); + + --destsiz; + dst = dest; + edst = dest + destsiz; + + // Check for (and skip over) any "label" on the line + s = src; + if(*s == ':') { + while(*s != EOS && !(chrtab[*s] & WHITE)) ++s; + if(*s != EOS) ++s; // Skip first whitespace + } + + // Expand the rest of the line + while(*s != EOS) + if(*s != '\\') { // Copy single character + if(dst >= edst) + goto overflow; + *dst++ = *s++; + } else { // Do macro expansion + questmark = 0; + + // Do special cases + switch(*++s) { + case '\\': // \\, \ (collapse to single backslash) + if(dst >= edst) + goto overflow; + *dst++ = *s++; + continue; + case '?': // \? set `questmark' flag + ++s; + questmark = 1; + break; + case '#': // \#, number of arguments + sprintf(numbuf, "%d", (int)imacro->im_nargs); + goto copystr; + case '!': // \! size suffix supplied on invocation + switch((int)imacro->im_siz) { + case SIZN: d = ""; break; + case SIZB: d = ".b"; break; + case SIZW: d = ".w"; break; + case SIZL: d = ".l"; break; + } + goto copy_d; + case '~': // ==> unique label string Mnnnn... + sprintf(numbuf, "M%ld", curuniq); + + copystr: + + d = numbuf; + + copy_d: + + ++s; + while(*d != EOS) + if(dst >= edst) goto overflow; + else *dst++ = *d++; + continue; + case EOS: + return(error("missing argument name")); + } + + // \n ==> argument number 'n', 0..9 + if(chrtab[*s] & DIGIT) { + i = *s++ - '1'; + if(i < 0) + i = 9; + goto arg_num; + } + + // Get argument name: \name, \{name} + d = mname; + if(*s != '{') { // \foo + do + *d++ = *s++; + while(chrtab[*s] & CTSYM); + } else { // \\{foo} + for(++s; *s != EOS && *s != '}';) + *d++ = *s++; + if(*s != '}') return(error("missing '}'")); + else ++s; + } + *d = EOS; + + // Lookup the argument and copy its (string) value into the destination string + DEBUG printf("mname='%s'\n", mname); + if((arg = lookup(mname, MACARG, macnum)) == NULL) + return(errors("undefined argument: '%s'", mname)); + else { + // Convert a string of tokens (terminated with EOL) back into text. If an argument + // is out of range (not specified in the macro invocation) then it is ignored. + i = (int)arg->svalue; + + arg_num: + + DEBUG printf("~argnumber=%d\n", i); + + tk = NULL; + if(i < imacro->im_nargs) + tk = argp[i]; + + // \?arg yields: + // 0 if the argument is empty or non-existant, + // 1 if the argument is not empty + if(questmark) { + if(tk == NULL || *tk == EOL) + questmark = 0; + if(dst >= edst) + goto overflow; + *dst++ = (char)(questmark + '0'); + continue; + } + + if(tk != NULL) // arg# is in range, so expand it + while(*tk != EOL) { + // Reverse-translation from a token number to a string. This is a hack. + // It might be better table-driven. + d = NULL; + if((*tk >= KW_D0) && !rdsp && !rgpu) { + d = regname[(int)*tk++ - KW_D0]; + goto strcopy; + } else if((*tk >= KW_R0) && (*tk <= KW_R31)) { + d = riscregname[(int)*tk++ - KW_R0]; + goto strcopy; + } else + switch((int)*tk++) { + case SYMBOL: + d = (char *)*tk++; + break; + case STRING: + d = (char *)*tk++; + + if(dst >= edst) + goto overflow; + *dst++ = '"'; + + while(*d != EOS) + if(dst >= edst) + goto overflow; + else *dst++ = *d++; + + if(dst >= edst) + goto overflow; + *dst++ = '"'; + continue; + break; + case CONST: + sprintf(numbuf, "$%lx", (LONG)*tk++); + d = numbuf; + break; + case DEQUALS: + d = "=="; + break; + case SET: + d = "set"; + break; + case COLON: + d = ":"; + break; + case DCOLON: + d = "::"; + break; + case GE: + d = ">="; + break; + case LE: + d = "<="; + break; + case NE: + d = "<>"; + break; + case SHR: + d = ">>"; + break; + case SHL: + d = "<<"; + break; + case DOTB: + d = ".b"; + break; + case DOTW: + d = ".w"; + break; + case DOTL: + d = ".l"; + break; + case CR_DATE: + d = "^^date"; + break; + case CR_TIME: + d = "^^time"; + break; + case CR_DEFINED: + d = "^^defined "; + break; + case CR_REFERENCED: + d = "^^referenced "; + break; + case CR_STREQ: + d = "^^streq "; + break; + case CR_MACDEF: + d = "^^macdef "; + break; + default: + if(dst >= edst) + goto overflow; + *dst++ = (char)*(tk-1); + break; + } + + // If 'd' != NULL, copy string to destination + if(d != NULL) { + + strcopy: + + DEBUG printf("d='%s'\n", d); + while(*d != EOS) + if(dst >= edst) + goto overflow; + else *dst++ = *d++; + } + } + } + } + + + *dst = EOS; + return(OK); + + overflow: + + *dst = EOS; + return(fatal("line too long as a result of macro expansion")); +} + +// +// --- Get Next Line of Text from a Macro ---------------------------------------------------------- +// + +char *getmln(void) { + IMACRO *imacro; + LONG *strp; + unsigned source_addr; + + imacro = cur_inobj->inobj.imacro; + strp = imacro->im_nextln; + + if(strp == NULL) // End-of-macro + return(NULL); + + imacro->im_nextln = (LONG *)*strp; + mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ); + + if(!strcmp(imacro->im_macro->sname, "mjump") && !mjump_align) { + // if we need to adjust the alignment of the jump source address to meet the rules of + // gpu main execution we need to skip the first nop of the macro. This is simpler than + // trying to insert nop's mid macro. + source_addr = (orgactive) ? orgaddr : sloc; + source_addr += 8; + if(source_addr % 4) { + strp = imacro->im_nextln; + if(strp == NULL) return(NULL); + imacro->im_nextln = (LONG *)*strp; + mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ); + } + mjump_align = 1; + } + + return(imacro->im_lnbuf); +} + +// +// --- Get Next Line of Text from a Repeat Block --------------------------------------------------- +// + +char *getrln(void) { + IREPT *irept; + LONG *strp; + + irept = cur_inobj->inobj.irept; + strp = irept->ir_nextln; // initial null + + // Do repeat at end of .rept block's string list + if(strp == NULL) { + DEBUG printf("back-to-top-of-repeat-block count=%d\n", irept->ir_count); + irept->ir_nextln = irept->ir_firstln; // copy first line + if(irept->ir_count-- == 0) { + DEBUG printf("end-repeat-block\n"); + return(NULL); + } + strp = irept->ir_nextln; //strp + } + + strcpy(irbuf, (char*)(irept->ir_nextln + 1)); + + DEBUG printf("repeat line='%s'\n", irbuf); + irept->ir_nextln = (LONG *)*strp; + + return(irbuf); +} + +// +// --- Include a Source File used at the Root, and for ".include" Files ---------------------------- +// + +int include(int handle, char *fname) { + IFILE *ifile; + INOBJ *inobj; + FILEREC *fr; + + if(verb_flag) printf("[Including: %s]\n", fname); // Verbose mode + + // Alloc and initialize include-descriptors + inobj = a_inobj(SRC_IFILE); + ifile = inobj->inobj.ifile; + + ifile->ifhandle = handle; // Setup file handle + ifile->ifind = ifile->ifcnt = 0; // Setup buffer indices + ifile->ifoldlineno = curlineno; // Save old line number + ifile->ifoldfname = curfname; // Save old filename + ifile->ifno = cfileno; // Save old file number + cfileno = ++filecount; // Compute new file number + curfname = nstring(fname); // Set current filename (alloc storage) + curlineno = 0; // Start on line zero + + // Add another file to the file-record + fr = (FILEREC *)amem((LONG)sizeof(FILEREC)); + fr->frec_next = NULL; + fr->frec_name = curfname; + if(last_fr == NULL) + filerec = fr; // Add first filerec + else + last_fr->frec_next = fr; // Append to list of filerecs + last_fr = fr; + + return(OK); +} + +// +// --- Initialize Tokenizer ------------------------------------------------------------------------ +// + +void init_token(void) { + int i; // Iterator + char *htab = "0123456789abcdefABCDEF"; // Hex character table + + lnsave = 0; // Don't save lines + curfname = ""; // No file, empty filename + filecount = (WORD)-1; + cfileno = (WORD)-1; // cfileno gets bumped to 0 + curlineno = 0; + totlines = 0; + etok = tokbuf; + f_inobj = NULL; + f_ifile = NULL; + f_imacro = NULL; + cur_inobj = NULL; + filerec = NULL; + last_fr = NULL; + lntag = SPACE; + + // Initialize hex, "dot" and tolower tables + for(i = 0; i < 128; ++i) { + hextab[i] = -1; + dotxtab[i] = 0; + tolowertab[i] = (char)i; + } + for(i = 0; htab[i] != EOS; ++i) + hextab[htab[i]] = (char)((i < 16) ? i : i - 6); + for(i = 'A'; i <= 'Z'; ++i) + tolowertab[i] |= 0x20; + + // These characters are legal immediately after a period + dotxtab['b'] = DOTB; // .b .B .s .S + dotxtab['B'] = DOTB; + dotxtab['s'] = DOTB; + dotxtab['S'] = DOTB; + dotxtab['w'] = DOTW; // .w .W + dotxtab['W'] = DOTW; + dotxtab['l'] = DOTL; // .l .L + dotxtab['L'] = DOTL; + dotxtab['I'] = DOTI; // .l .L + dotxtab['I'] = DOTI; +} + +// +// --- Pop the Current Input Level ----------------------------------------------------------------- +// +int fpop(void) { + INOBJ *inobj; + IFILE *ifile; + IMACRO *imacro; + LONG *p, *p1; + + inobj = cur_inobj; + if(inobj != NULL) { + // Pop IFENT levels until we reach the conditional assembly context we were at when the + // input object was entered. + while(ifent != inobj->in_ifent) + d_endif(); + + tok = inobj->in_otok; // Restore tok and otok + etok = inobj->in_etok; + + switch(inobj->in_type) { + case SRC_IFILE: // Pop and release an IFILE + if(verb_flag) + printf("[Leaving: %s]\n", curfname); + ifile = inobj->inobj.ifile; + ifile->if_link = f_ifile; + f_ifile = ifile; + close(ifile->ifhandle); // Close source file + curfname = ifile->ifoldfname; // Set current filename + curlineno = ifile->ifoldlineno; // Set current line# + DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno); + cfileno = ifile->ifno; // Restore current file number + break; + case SRC_IMACRO: // Pop and release an IMACRO + imacro = inobj->inobj.imacro; + imacro->im_link = f_imacro; + f_imacro = imacro; + break; + case SRC_IREPT: // Pop and release an IREPT + DEBUG printf("dealloc IREPT\n"); + p = inobj->inobj.irept->ir_firstln; + while(p != NULL) { + p1 = (LONG *)*p; + p = p1; + } + break; + } + + cur_inobj = inobj->in_link; + inobj->in_link = f_inobj; + f_inobj = inobj; + } + + return(0); +} + +// +// --- Get line from file into buf, return NULL on EOF or ptr to the start of a null-term line ----- +// + +char *getln(void) { + IFILE *fl; + int i, j; + char *p, *d; + int readamt; + + readamt = -1; // 0 if last read() yeilded 0 bytes + fl = cur_inobj->inobj.ifile; + + for(;;) { + // Scan for next end-of-line; handle stupid text formats by treating \r\n the same as \n. + // (lone '\r' at end of buffer means we have to check for '\n'). + i = 0; + j = fl->ifcnt; + d = &fl->ifbuf[fl->ifind]; + + for(p = d; i < j; ++i, ++p) { + if(*p == '\r' || *p == '\n') { + ++i; + if(*p == '\r') { + if(i >= j) { + break; // Look for '\n' to eat + } else if(p[1] == '\n') { + ++i; + } + } + + *p = '\0'; + + fl->ifind += i; + fl->ifcnt -= i; + return(d); + } + } + + // Handle hanging lines by ignoring them (Input file is exhausted, no \r or \n on last line) + if(!readamt && fl->ifcnt) { + fl->ifcnt = 0; + *p = '\0'; + return(NULL); + } + + // Truncate and return absurdly long lines. + if(fl->ifcnt >= QUANTUM) { + fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0'; + fl->ifcnt = 0; + return(&fl->ifbuf[fl->ifind]); + } + + // Relocate what's left of a line to the beginning of the buffer, and read some more of the + // file in; return NULL if the buffer's empty and on EOF. + if(fl->ifind != 0) { + p = &fl->ifbuf[fl->ifind]; + d = &fl->ifbuf[fl->ifcnt & 1]; + for(i = 0; i < fl->ifcnt; ++i) + *d++ = *p++; + fl->ifind = fl->ifcnt & 1; + } + + if((readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM)) < 0) + return(NULL); + + if((fl->ifcnt += readamt) == 0) + return(NULL); + } +} + +// +// --- Tokenize a Line ----------------------------------------------------------------------------- +// + +int tokln(void) { + char *ln = NULL; // Ptr to current position in line + char *p; // Random character ptr + TOKEN *tk; // Token-deposit ptr + int state = 0; // State for keyword detector + int j = 0; // Var for keyword detector + char c; // Random char + VALUE v; // Random value + char *nullspot = NULL; // Spot to clobber for SYMBOL terminatn + int stuffnull; // 1:terminate SYMBOL '\0' at *nullspot + char c1; + + retry: + + if(cur_inobj == NULL) // Return EOF if input stack is empty + return(TKEOF); + + // Get another line of input from the current input source: a file, a macro, or a repeat-block + switch(cur_inobj->in_type) { + // Include-file: + // o handle EOF; + // o bump source line number; + // o tag the listing-line with a space; + // o kludge lines generated by Alcyon C. + case SRC_IFILE: + if((ln = getln()) == NULL) { + fpop(); // Pop input level + goto retry; // Try for more lines + } + ++curlineno; // Bump line number + lntag = SPACE; + if(as68_flag) { + // AS68 compatibility, throw away all lines starting with back-quotes, tildes, or '*' + // On other lines, turn the first '*' into a semi-colon. + if(*ln == '`' || *ln == '~' || *ln == '*') *ln = ';'; + else for(p = ln; *p != EOS; ++p) { + if(*p == '*') { + *p = ';'; + break; + } + } + } + break; + + // Macro-block: + // o Handle end-of-macro; + // o tag the listing-line with an at (@) sign. + case SRC_IMACRO: + if((ln = getmln()) == NULL) { + exitmac(); // Exit macro (pop args, do fpop(), etc) + goto retry; // Try for more lines... + } + lntag = '@'; + break; + + // Repeat-block: + // o Handle end-of-repeat-block; + // o tag the listing-line with a pound (#) sign. + case SRC_IREPT: + if((ln = getrln()) == NULL) { + fpop(); + goto retry; + } + lntag = '#'; + break; + } + + // Save text of the line. We only do this during listings and within macro-type blocks, + // since it is expensive to unconditionally copy every line. + if(lnsave) strcpy(lnbuf, ln); + + // General house-keeping + tok = tokeol; // Set "tok" to EOL in case of error + tk = etok; // Reset token ptr + stuffnull = 0; // Don't stuff nulls + ++totlines; // Bump total #lines assembled + + // See if the entire line is a comment. This is a win if the programmer puts in lots of comments + if(*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln+1) == '/'))) goto goteol; + + // Main tokenization loop; + // o skip whitespace; + // o handle end-of-line; + // o handle symbols; + // o handle single-character tokens (operators, etc.); + // o handle multiple-character tokens (constants, strings, etc.). + for(; *ln != EOS;) { + // Skip whitespace, handle EOL + while((int)chrtab[*ln] & WHITE) + ++ln; + + // Handle EOL, comment with ';' + if(*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln+1) == '/'))) + break; + + // Handle start of symbol. Symbols are null-terminated in place. The termination is + // always one symbol behind, since there may be no place for a null in the case that + // an operator immediately follows the name. + c = chrtab[*ln]; + if(c & STSYM) { + if(stuffnull) // Terminate old symbol + *nullspot = EOS; + v = 0; // Assume no DOT attrib follows symbol + stuffnull = 1; + p = nullspot = ln++; // Nullspot -> start of this symbol + + // Find end of symbol (and compute its length) + for(j = 1; (int)chrtab[*ln] & CTSYM; ++j) + ++ln; + + // Handle "DOT" special forms (like ".b") that follow a normal symbol or keyword: + if(*ln == '.') { + *ln++ = EOS; // Terminate symbol + stuffnull = 0; // And never try it again + + // Character following the `.' must have a DOT attribute, and the chararacter after + // THAT one must not have a start-symbol attribute (to prevent symbols that look + // like, for example, "zingo.barf", which might be a good idea anyway....) + if((((int)chrtab[*ln] & DOT) == 0) || ((int)dotxtab[*ln] <= 0)) + return(error("[bwsl] must follow `.' in symbol")); + v = (VALUE)dotxtab[*ln++]; + if((int)chrtab[*ln] & CTSYM) + return(error("misuse of `.', not allowed in symbols")); + } + + // If the symbol is small, check to see if it's really the name of a register. + if(j <= KWSIZE) { + for(state = 0; state >= 0;) { + j = (int)tolowertab[*p++]; + j += kwbase[state]; + if(kwcheck[j] != state) { + j = -1; + break; + } + + if(*p == EOS || p == ln) { + j = kwaccept[j]; + break; + } + + state = kwtab[j]; + } + } else { + j = -1; + } + + //make j = -1 if time, date etc with no preceeding ^^ + //defined, referenced, streq, macdef, date and time + switch((TOKEN)j) { + case 112: // defined + case 113: // referenced + case 118: // streq + case 119: // macdef + case 120: // time + case 121: // date + j = -1; + break; + } + + if(j < 0 || state < 0) { + *tk++ = SYMBOL; + *tk++ = (TOKEN)nullspot; + } else { + *tk++ = (TOKEN)j; + stuffnull = 0; + } + + if(v) // Record attribute token (if any) + *tk++ = (TOKEN)v; + + if(stuffnull) // Arrange for string termination + nullspot = ln; + continue; + } + + // Handle identity tokens + if(c & SELF) { + *tk++ = *ln++; + continue; + } + + // Handle multiple-character tokens + if(c & MULTX) { + switch(*ln++) { + case '!': // ! or != + if(*ln == '=') { + *tk++ = NE; + ++ln; + } else *tk++ = '!'; + continue; + case '\'': // 'string' + case '\"': // "string" + c1 = ln[-1]; + *tk++ = STRING; + *tk++ = (TOKEN)ln; + + for(p = ln; *ln != EOS && *ln != c1;) { + c = *ln++; + if(c == '\\') + switch(*ln++) { + case EOS: + return(error("unterminated string")); + case 'e': + c = '\033'; + break; + case 'n': + c = '\n'; + break; + case 'b': + c = '\b'; + break; + case 't': + c = '\t'; + break; + case 'r': + c = '\r'; + break; + case 'f': + c = '\f'; + break; + case '\"': + c = '\"'; + break; + case '\'': + c = '\''; + break; + case '\\': + c = '\\'; + break; + default: + warn("bad backslash code in string"); + --ln; + break; + } + *p++ = c; + } + + if(*ln++ != c1) + return(error("unterminated string")); + *p++ = EOS; + continue; + case '$': // $, hex constant + if((int)chrtab[*ln] & HDIGIT) { + v = 0; + while((int)hextab[*ln] >= 0) + v = (v << 4) + (int)hextab[*ln++]; + if(*ln == '.') { + if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; } + if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; } + if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; } + } + *tk++ = CONST; + *tk++ = v; + } else *tk++ = '$'; + continue; + case '<': // < or << or <> or <= + switch(*ln) { + case '<': + *tk++ = SHL; + ++ln; + continue; + case '>': + *tk++ = NE; + ++ln; + continue; + case '=': + *tk++ = LE; + ++ln; + continue; + default: + *tk++ = '<'; + continue; + } + case ':': // : or :: + if(*ln == ':') { + *tk++ = DCOLON; + ++ln; + } else *tk++ = ':'; + continue; + case '=': // = or == + if(*ln == '=') { + *tk++ = DEQUALS; + ++ln; + } else *tk++ = '='; + continue; + case '>': // > or >> or >= + switch(*ln) { + case '>': + *tk++ = SHR; + ++ln; + continue; + case '=': + *tk++ = GE; + ++ln; + continue; + default: + *tk++ = '>'; + continue; + } + case '%': // % or binary constant + if(*ln < '0' || *ln > '1') { + *tk++ = '%'; + continue; + } + v = 0; + while(*ln >= '0' && *ln <= '1') + v = (v << 1) + *ln++ - '0'; + if(*ln == '.') { + if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; } + if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; } + if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; } + } + *tk++ = CONST; + *tk++ = v; + continue; + case '@': // @ or octal constant + if(*ln < '0' || *ln > '7') { + *tk++ = '@'; + continue; + } + v = 0; + while(*ln >= '0' && *ln <= '7') + v = (v << 3) + *ln++ - '0'; + if(*ln == '.') { + if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; } + if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; } + if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; } + } + *tk++ = CONST; + *tk++ = v; + continue; + case '^': // ^ or ^^ + if(*ln != '^') { + *tk++ = '^'; + continue; + } + + if(((int)chrtab[*++ln] & STSYM) == 0) { + error("invalid symbol following ^^"); + continue; + } + + p = ln++; + while((int)chrtab[*ln] & CTSYM) + ++ln; + + for(state = 0; state >= 0;) { + // Get char, convert to lowercase + j = *p++; + if(j >= 'A' && j <= 'Z') + j += 0x20; + + j += kwbase[state]; + if(kwcheck[j] != state) { + j = -1; + break; + } + + if(*p == EOS || p == ln) { + j = kwaccept[j]; + break; + } + state = kwtab[j]; + } + + if(j < 0 || state < 0) { + error("unknown symbol following ^^"); + continue; + } + + *tk++ = (TOKEN)j; + continue; + default: + interror(2); // Bad MULTX entry in chrtab + continue; + } + } + + + // Handle decimal constant + if(c & DIGIT) { + v = 0; + while((int)chrtab[*ln] & DIGIT) + v = (v * 10) + *ln++ - '0'; + if(*ln == '.') { + if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; } + if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; } + if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; } + } + *tk++ = CONST; + *tk++ = v; + continue; + } + + // Handle illegal character + return(error("illegal character")); + } + + // Terminate line of tokens and return "success." + + goteol: + + tok = etok; // Set tok to beginning of line + if(stuffnull) // Terminate last SYMBOL + *nullspot = EOS; + *tk++ = EOL; + + return(OK); +} + +// +// ------------------------------------------------------------------------------------------------- +// .GOTO