From d28f432296e812643e236d1bfc9b556a7b11c461 Mon Sep 17 00:00:00 2001 From: James Hammons Date: Mon, 26 Dec 2011 22:50:27 +0000 Subject: [PATCH] Initial commit. --- 68kgen.c | 121 ++++++ 68kmn | 132 ++++++ 68ktab | 242 +++++++++++ amode.c | 160 +++++++ amode.h | 113 +++++ debug.c | 305 ++++++++++++++ debug.h | 19 + direct.c | 1176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ direct.h | 70 ++++ eagen.c | 32 ++ eagen0.c | 171 ++++++++ error.c | 161 ++++++++ error.h | 31 ++ expr.c | 564 +++++++++++++++++++++++++ expr.h | 39 ++ kwgen.c | 384 +++++++++++++++++ kwtab | 76 ++++ listing.c | 415 +++++++++++++++++++ listing.h | 44 ++ mach.c | 590 ++++++++++++++++++++++++++ mach.h | 47 +++ macro.c | 489 ++++++++++++++++++++++ macro.h | 29 ++ makefile | 119 ++++++ mark.c | 228 ++++++++++ mark.h | 27 ++ mntab | 246 +++++++++++ object.c | 158 +++++++ object.h | 21 + parmode.h | 226 ++++++++++ procln.c | 611 +++++++++++++++++++++++++++ procln.h | 35 ++ risca.c | 715 ++++++++++++++++++++++++++++++++ risca.h | 59 +++ risctab | 66 +++ rmac.c | 826 ++++++++++++++++++++++++++++++++++++ rmac.h | 214 ++++++++++ sect.c | 694 +++++++++++++++++++++++++++++++ sect.h | 146 +++++++ symbol.c | 380 +++++++++++++++++ symbol.h | 30 ++ token.c | 1196 +++++++++++++++++++++++++++++++++++++++++++++++++++++ token.h | 155 +++++++ version.h | 18 + 44 files changed, 11580 insertions(+) create mode 100644 68kgen.c create mode 100644 68kmn create mode 100644 68ktab create mode 100644 amode.c create mode 100644 amode.h create mode 100644 debug.c create mode 100644 debug.h create mode 100644 direct.c create mode 100644 direct.h create mode 100644 eagen.c create mode 100644 eagen0.c create mode 100644 error.c create mode 100644 error.h create mode 100644 expr.c create mode 100644 expr.h create mode 100644 kwgen.c create mode 100644 kwtab create mode 100644 listing.c create mode 100644 listing.h create mode 100644 mach.c create mode 100644 mach.h create mode 100644 macro.c create mode 100644 macro.h create mode 100644 makefile create mode 100644 mark.c create mode 100644 mark.h create mode 100644 mntab create mode 100644 object.c create mode 100644 object.h create mode 100644 parmode.h create mode 100644 procln.c create mode 100644 procln.h create mode 100644 risca.c create mode 100644 risca.h create mode 100644 risctab create mode 100644 rmac.c create mode 100644 rmac.h create mode 100644 sect.c create mode 100644 sect.h create mode 100644 symbol.c create mode 100644 symbol.h create mode 100644 token.c create mode 100644 token.h create mode 100644 version.h 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