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:
+             &n