]> Shamusworld >> Repos - rmac/commitdiff
Initial commit.
authorJames Hammons <jlhamm@acm.org>
Mon, 26 Dec 2011 22:50:27 +0000 (22:50 +0000)
committerJames Hammons <jlhamm@acm.org>
Mon, 26 Dec 2011 22:50:27 +0000 (22:50 +0000)
44 files changed:
68kgen.c [new file with mode: 0644]
68kmn [new file with mode: 0644]
68ktab [new file with mode: 0644]
amode.c [new file with mode: 0644]
amode.h [new file with mode: 0644]
debug.c [new file with mode: 0644]
debug.h [new file with mode: 0644]
direct.c [new file with mode: 0644]
direct.h [new file with mode: 0644]
eagen.c [new file with mode: 0644]
eagen0.c [new file with mode: 0644]
error.c [new file with mode: 0644]
error.h [new file with mode: 0644]
expr.c [new file with mode: 0644]
expr.h [new file with mode: 0644]
kwgen.c [new file with mode: 0644]
kwtab [new file with mode: 0644]
listing.c [new file with mode: 0644]
listing.h [new file with mode: 0644]
mach.c [new file with mode: 0644]
mach.h [new file with mode: 0644]
macro.c [new file with mode: 0644]
macro.h [new file with mode: 0644]
makefile [new file with mode: 0644]
mark.c [new file with mode: 0644]
mark.h [new file with mode: 0644]
mntab [new file with mode: 0644]
object.c [new file with mode: 0644]
object.h [new file with mode: 0644]
parmode.h [new file with mode: 0644]
procln.c [new file with mode: 0644]
procln.h [new file with mode: 0644]
risca.c [new file with mode: 0644]
risca.h [new file with mode: 0644]
risctab [new file with mode: 0644]
rmac.c [new file with mode: 0644]
rmac.h [new file with mode: 0644]
sect.c [new file with mode: 0644]
sect.h [new file with mode: 0644]
symbol.c [new file with mode: 0644]
symbol.h [new file with mode: 0644]
token.c [new file with mode: 0644]
token.h [new file with mode: 0644]
version.h [new file with mode: 0644]

diff --git a/68kgen.c b/68kgen.c
new file mode 100644 (file)
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 <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 <value>
+            printf("CONST=%ld", *tk++);
+            break;
+         case STRING:                                       // STRING <address>
+            printf("STRING='%s'", *tk++);
+            break;
+         case SYMBOL:                                       // SYMBOL <address> 
+            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 (file)
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 (file)
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] <<<cannot make local symbols global>>> -----------------------------
+//
+
+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 <expression>
+      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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 <macro-name>
+            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 (file)
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 (file)
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 <file1 >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 <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ *  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 (file)
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 (file)
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 (file)
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 <time.h>
+#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 (file)
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);
+}
+
+//
+// --- <op> #expr ----------------------------------------------------------------------------------
+//
+int m_imm(WORD inst, WORD siz) {
+   D_word(inst);
+   ea0gen(siz);
+
+   return(0);
+}
+
+//
+// --- <op>.b #expr --------------------------------------------------------------------------------
+//
+int m_imm8(WORD inst, WORD siz) {
+   siz = siz;
+   D_word(inst);
+   ea0gen(SIZB);
+
+   return(0);
+}
+
+//
+// --- <shift> Dn,Dn -------------------------------------------------------------------------------
+//
+int m_shr(WORD inst, WORD siz) {
+   inst |= reg_9[a0reg] | a1reg | siz_6[siz];
+   D_word(inst);
+
+   return(0);
+}
+
+//
+// --- <shift> #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 <C_ALL> <C_ALTDATA>
+//        MOVE <C_ALL> <M_AREG>
+// 
+// Optimize MOVE.L #<smalldata>,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 <rlist>,ea -- movem ea,<rlist> --------------------------------------------------------
+//
+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 #<expr>,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) {                     // <rlist>,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,<rlist> 
+      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,#<expr> 
+         ++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 (file)
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 (file)
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:
+      //            <directive>
+      //    symbol: <directive>
+      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 (file)
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 (file)
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 >kwtab.h
+
+risckw.h : kwtab kwgen
+       ./kwgen mr <risctab >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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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: <empty> | .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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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<format>
+            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<pagelen>
+            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<MAXFWDJUMPS; i++)
+               fwdjump[i] = 0;
+
+       if (argc > 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 (file)
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 <io.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#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 <sys/fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#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 <sys/fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#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 <value>
+#define ACONST       'A'                                    // ACONST <value> <attrib>
+#define STRING       'b'                                    // STRING <address>
+#define SYMBOL       'c'                                    // SYMBOL <address>
+#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 (file)
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 = &sect[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 = &sect[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 = &sect[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 = &sect[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 = &sect[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 = &sect[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 (file)
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    <to+flags>    section mark is relative to, and flags in upper byte
+// .L    <loc>         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 (file)
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 (file)
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 (file)
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 <non-name-character>
+// \{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:
+// :<name><whitespace>
+// (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 '?':                                       // \? <macro>  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 ^^ <operator-name>
+               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 <label>       goto directive
+// 
+// The label is searched for starting from the first line of the current, enclosing macro 
+// definition.  If no enclosing macro exists, an error is generated.
+// 
+// A label is of the form:
+// 
+// :<name><whitespace>
+// 
+// The colon must appear in column 1.  The label is stripped prior to macro expansion, and is NOT
+// subject to macro expansion.  The whitespace may also be EOL.
+// -------------------------------------------------------------------------------------------------
+//
+
+//int d_goto(WORD siz) {
+int d_goto(void) {
+   char *sym;                                               // Label to search for 
+   LONG *defln;                                             // Macro definition strings 
+   char *s1;                                                // Temps for string comparison 
+   char *s2;
+   IMACRO *imacro;                                          // Macro invocation block
+
+   // Setup for the search
+   if(*tok != SYMBOL) return(error("missing label"));
+   sym = (char *)tok[1];
+   tok += 2;
+
+   if(cur_inobj->in_type != SRC_IMACRO) return(error("goto not in macro"));
+   imacro = cur_inobj->inobj.imacro;
+   defln = (LONG *)imacro->im_macro->svalue;
+
+   // Find the label, starting with the first line.
+   for(; defln != NULL; defln = (LONG *)*defln)
+      if(*(char *)(defln + 1) == ':') {
+         // Compare names (sleazo string compare)
+         s1 = sym;
+         s2 = (char *)(defln + 1) + 1;
+         while(*s1 == *s2)
+            if(*s1 == EOS) break;
+            else {
+               ++s1;
+               ++s2;
+            }
+
+         // Found the label, set new macro next-line and return.
+         if((*s2 == EOS) || ((int)chrtab[*s2] & WHITE)) {
+            imacro->im_nextln = defln;
+            return(0);
+         }
+      }
+
+   return(error("goto label not found"));
+}
diff --git a/token.h b/token.h
new file mode 100644 (file)
index 0000000..1f57c90
--- /dev/null
+++ b/token.h
@@ -0,0 +1,155 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
+// TOKEN.H - 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
+
+#ifndef __TOKEN_H__
+#define __TOKEN_H__
+
+#include "rmac.h"
+//#include "risca.h"
+
+// Include Files and Macros
+#define SRC_IFILE       0                                   // Input source is IFILE
+#define SRC_IMACRO      1                                   // Input source is IMACRO
+#define SRC_IREPT       2                                   // Input source is IREPT
+
+// Macros
+#define INOBJ           struct _inobj
+#define IUNION          union _iunion
+#define IFILE           struct _incldfile
+#define IMACRO          struct _imacro
+#define IREPT           struct _irept
+#define IFENT           struct _ifent
+
+// Tunable definitions
+#define LNSIZ           256                                 // Maximum size of a line of text
+#define TOKBUFSIZE      400                                 // Size of token-line buffer
+#define QUANTUM         4096L                               // #bytes to eat at a time from a file
+#define LNBUFSIZ        (QUANTUM*2)                         // Size of file's buffer
+#define KWSIZE          7                                   // Maximum size of keyword in kwtab.h
+
+// (Normally) non-printable tokens
+#define COLON           ':'                                 // : (grumble: GNUmacs hates ':')
+#define CONST           'a'                                 // CONST <value>
+#define ACONST          'A'                                 // ACONST <value> <attrib>
+#define STRING          'b'                                 // STRING <address>
+#define SYMBOL          'c'                                 // SYMBOL <address>
+#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 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'                                 // .l or .L 
+#define ENDEXPR         'E'                                 // End of expression 
+
+// ^^ operators
+#define CR_DEFINED      'p'                                 // ^^defined - is symbol defined?
+#define CR_REFERENCED   'q'                                 // ^^referenced - was symbol referenced?
+#define CR_STREQ        'v'                                 // ^^streq - compare two strings
+#define CR_MACDEF       'w'                                 // ^^macdef - is macro defined?
+#define CR_TIME         'x'                                 // ^^time - DOS format time
+#define CR_DATE         'y'                                 // ^^date - DOS format date
+
+// Character Attributes
+#define ILLEG           0                                   // Illegal character (unused)
+#define DIGIT           1                                   // 0-9
+#define HDIGIT          2                                   // A-F, a-f
+#define STSYM           4                                   // A-Z, a-z, _~.
+#define CTSYM           8                                   // A-Z, a-z, 0-9, _~$?
+#define SELF            16                                  // Single-character tokens: ( ) [ ] etc
+#define WHITE           32                                  // Whitespace (space, tab, etc.)
+#define MULTX           64                                  // Multiple-character tokens
+#define DOT             128                                 // [bwlsBWSL] for what follows a `.'
+
+// Conditional assembly structures
+IFENT {
+   IFENT *if_prev;                                          // Ptr prev .if state block (or NULL)
+   WORD if_state;                                           // 0:enabled, 1:disabled
+};
+
+// Pointer to IFILE or IMACRO
+IUNION {
+   IFILE *ifile;
+   IMACRO *imacro;
+   IREPT *irept;
+};
+
+// Ptr to IFILEs, IMACROs, and so on; back-ptr to previous input objects
+INOBJ {
+   WORD in_type;                                            // 0=IFILE, 1=IMACRO ...
+   IFENT *in_ifent;                                                // Pointer to .if context on entry
+   INOBJ *in_link;                                                 // Pointer to previous INOBJ
+   TOKEN *in_otok;                                                 // Old `tok' value
+   TOKEN *in_etok;                                                 // Old `etok' value
+   IUNION inobj;                                                           // IFILE or IMACRO ...
+};
+
+// Information about a file
+IFILE {
+   IFILE *if_link;                                          // Pointer to ancient IFILEs
+   char *ifoldfname;                                        // Old file's name
+   int ifoldlineno;                                         // Old line number
+   int ifind;                                               // Position in file buffer
+   int ifcnt;                                               // #chars left in file buffer 
+   int ifhandle;                                            // File's descriptor
+   WORD ifno;                                               // File number
+   char ifbuf[LNBUFSIZ];                                    // Line buffer
+};
+
+// Information about a macro invocation
+IMACRO {
+   IMACRO *im_link;                                         // Pointer to ancient IMACROs
+   LONG *im_nextln;                                         // Next line to include
+   WORD im_nargs;                                           // # of arguments supplied on invocation
+   WORD im_siz;                                             // Size suffix supplied on invocation
+   LONG im_olduniq;                                         // Old value of 'macuniq'
+   SYM *im_macro;                                           // Pointer to macro we're in
+   char im_lnbuf[LNSIZ];                                    // Line buffer
+};
+
+// Information about a .rept invocation
+IREPT {
+   LONG *ir_firstln;                                        // Pointer to first line
+   LONG *ir_nextln;                                         // Pointer to next line
+   VALUE ir_count;                                          // Repeat count (decrements)
+};
+
+// Globals, externals etc
+extern int lnsave;
+extern int curlineno;
+extern char *curfname;
+extern WORD cfileno;
+extern TOKEN *tok;
+extern char lnbuf[];
+extern char lntag;
+extern char tolowertab[];
+extern INOBJ *cur_inobj;
+extern unsigned orgactive;
+extern unsigned orgaddr;
+extern LONG sloc;
+extern int mjump_align;
+
+// Prototypes
+int include(int, char *);
+void init_token(void);
+void setfnum(WORD);
+int tokln(void);
+int fpop(void);
+//int d_goto(WORD);
+int d_goto(void);
+INOBJ *a_inobj(int);
+
+#endif // __TOKEN_H__
\ No newline at end of file
diff --git a/version.h b/version.h
new file mode 100644 (file)
index 0000000..045c3df
--- /dev/null
+++ b/version.h
@@ -0,0 +1,18 @@
+//
+// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
+// VERSION.H - Version Information
+// 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 __VERSION_H__
+#define __VERSION_H__
+
+// Release Information
+
+#define MAJOR   1                      // Major version number
+#define MINOR   1                      // Minor version number
+#define PATCH   0                      // Patch release number
+
+#endif // __VERSION_H__