diff a09.c @ 0:9a224bd9b45f

os9 emulation
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 02 Jul 2018 02:12:31 +0900
parents
children 3c736a81b886
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/a09.c	Mon Jul 02 02:12:31 2018 +0900
@@ -0,0 +1,1544 @@
+/* A09, 6809 Assembler.
+
+   created 1993,1994 by L.C. Benschop.
+   copyleft (c) 1994-2014 by the sbc09 team, see AUTHORS for more details.
+   license: GNU General Public License version 2, see LICENSE for more details.
+   THERE IS NO WARRANTY ON THIS PROGRAM. 
+
+   Generates binary image file from the lowest to
+   the highest address with actually assembled data.
+
+   Machine dependencies:
+                  char is 8 bits.
+                  short is 16 bits.
+                  integer arithmetic is twos complement.
+
+   syntax a09 [-o filename] [-l filename] sourcefile.
+
+   Options
+   -o filename name of the output file (default name minus a09 suffix)
+   -s filename name of the s-record output file (default its a binary file)
+   -l filename list file name (default no listing)
+   -d enable debugging
+
+   recognized pseudoops:
+    extern public
+    macro endm if else endif
+    org equ set setdp
+    fcb fcw fdb fcc rmb
+    end include title
+
+   Not all of these are actually IMPLEMENTED!!!!!! 
+
+   Revisions:
+        1993-11-03 v0.1 
+                Initial version.
+        1994/03/21 v0.2
+                Fixed PC relative addressing bug
+                Added SET, SETDP, INCLUDE. IF/ELSE/ENDIF
+                No macros yet, and no separate linkable modules.
+        2012-06-04 j at klasek at
+                New: debugging parameter/option.
+                Fixed additional possible issue PC relative addressing.
+                Compatibility: Octal number prefix "&".
+        2014-07-15 j at klasek at
+                Fixed usage message.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define NLABELS 2048
+#define MAXIDLEN 16
+#define MAXLISTBYTES 8
+#define FNLEN 30
+#define LINELEN 128
+
+static int debug=0;
+
+struct oprecord{char * name;
+                unsigned char cat;
+                unsigned short code;};
+
+/* Instruction categories:
+   0 one byte oprcodes   NOP
+   1 two byte opcodes    SWI2
+   2 opcodes w. imm byte ANDCC
+   3 LEAX etc.
+   4 short branches. BGE
+   5 long branches 2byte opc LBGE
+   6 long branches 1byte opc LBRA
+   7 accumulator instr.      ADDA
+   8 double reg instr 1byte opc LDX
+   9 double reg instr 2 byte opc LDY
+   10 single address instrs NEG
+   11 TFR, EXG
+   12 push,pull
+   13 pseudoops
+*/
+
+struct oprecord optable[]={
+  {"ABX",0,0x3a},{"ADCA",7,0x89},{"ADCB",7,0xc9},
+  {"ADDA",7,0x8b},{"ADDB",7,0xcb},{"ADDD",8,0xc3},
+  {"ANDA",7,0X84},{"ANDB",7,0xc4},{"ANDCC",2,0x1c},
+  {"ASL",10,0x08},{"ASLA",0,0x48},{"ASLB",0,0x58},
+  {"ASR",10,0x07},{"ASRA",0,0x47},{"ASRB",0,0x57},
+  {"BCC",4,0x24},{"BCS",4,0x25},{"BEQ",4,0x27},
+  {"BGE",4,0x2c},{"BGT",4,0x2e},{"BHI",4,0x22},
+  {"BHS",4,0x24},{"BITA",7,0x85},{"BITB",7,0xc5},
+  {"BLE",4,0x2f},{"BLO",4,0x25},{"BLS",4,0x23},
+  {"BLT",4,0x2d},{"BMI",4,0x2b},{"BNE",4,0x26},
+  {"BPL",4,0x2a},{"BRA",4,0x20},{"BRN",4,0x21},
+  {"BSR",4,0x8d},
+  {"BVC",4,0x28},{"BVS",4,0x29},
+  {"CLC",1,0x1cfe},{"CLF",1,0x1cbf},{"CLI",1,0x1cef},
+  {"CLIF",1,0x1caf},
+  {"CLR",10,0x0f},{"CLRA",0,0x4f},{"CLRB",0,0x5f},
+  {"CLV",1,0x1cfd},
+  {"CMPA",7,0x81},{"CMPB",7,0xc1},{"CMPD",9,0x1083},
+  {"CMPS",9,0x118c},{"CMPU",9,0x1183},{"CMPX",8,0x8c},
+  {"CMPY",9,0x108c},
+  {"COM",10,0x03},{"COMA",0,0x43},{"COMB",0,0x53},
+  {"CWAI",2,0x3c},{"DAA",0,0x19},
+  {"DEC",10,0x0a},{"DECA",0,0x4a},{"DECB",0,0x5a},
+  {"DES",1,0x327f},{"DEU",1,0x335f},{"DEX",1,0x301f},
+  {"DEY",1,0x313f},
+  {"ELSE",13,1},
+  {"EMOD",13,25},
+  {"END",13,2},
+  {"ENDC",13,3},
+  {"ENDIF",13,3},
+  {"ENDM",13,4},
+  {"EORA",7,0x88},{"EORB",7,0xc8},
+  {"EQU",13,5},{"EXG",11,0x1e},{"EXTERN",13,6},
+  {"FCB",13,7},{"FCC",13,8},
+  {"FCS",13,23},
+  {"FCW",13,9},
+  {"FDB",13,9},
+  {"IF",13,10},
+  {"IFEQ",13,29},
+  {"IFGT",13,30},
+  {"IFNE",13,28},
+  {"IFP1",13,21},
+  {"INC",10,0x0c},{"INCA",0,0x4c},{"INCB",0,0x5c},
+  {"INCLUDE",13,16},
+  {"INS",1,0x3261},{"INU",1,0x3341},{"INX",1,0x3001},
+  {"INY",1,0x3121},{"JMP",10,0x0e},{"JSR",8,0x8d},
+  {"LBCC",5,0x1024},{"LBCS",5,0x1025},{"LBEQ",5,0x1027},
+  {"LBGE",5,0x102c},{"LBGT",5,0x102e},{"LBHI",5,0x1022},
+  {"LBHS",5,0x1024},
+  {"LBLE",5,0x102f},{"LBLO",5,0x1025},{"LBLS",5,0x1023},
+  {"LBLT",5,0x102d},{"LBMI",5,0x102b},{"LBNE",5,0x1026},
+  {"LBPL",5,0x102a},{"LBRA",6,0x16},{"LBRN",5,0x1021},
+  {"LBSR",6,0x17},
+  {"LBVC",5,0x1028},{"LBVS",5,0x1029},
+  {"LDA",7,0x86},{"LDB",7,0xc6},{"LDD",8,0xcc},
+  {"LDS",9,0x10ce},{"LDU",8,0xce},{"LDX",8,0x8e},
+  {"LDY",9,0x108e},{"LEAS",3,0x32},
+  {"LEAU",3,0x33},{"LEAX",3,0x30},{"LEAY",3,0x31},
+  {"LSL",10,0x08},{"LSLA",0,0x48},{"LSLB",0,0x58},
+  {"LSR",10,0x04},{"LSRA",0,0x44},{"LSRB",0,0x54},
+  {"MACRO",13,11},
+  {"MOD",13,24},
+  {"MUL",0,0x3d},
+  {"NAM",13,26},
+  {"NEG",10,0x00},{"NEGA",0,0x40},{"NEGB",0,0x50},
+  {"NOP",0,0x12},
+  {"OPT",13,19},
+  {"ORA",7,0x8a},{"ORB",7,0xca},{"ORCC",2,0x1a},
+  {"ORG",13,12},
+  {"OS9",13,32},
+  {"PAG",13,20}, {"PAGE",13,20},
+  {"PSHS",12,0x34},{"PSHU",12,0x36},{"PUBLIC",13,13},
+  {"PULS",12,0x35},{"PULU",12,0x37},{"RMB",13,0},
+  {"ROL",10,0x09},{"ROLA",0,0x49},{"ROLB",0,0x59},
+  {"ROR",10,0x06},{"RORA",0,0x46},{"RORB",0,0x56},
+  {"RTI",0,0x3b},{"RTS",0,0x39},
+  {"SBCA",7,0x82},{"SBCB",7,0xc2},
+  {"SEC",1,0x1a01},{"SEF",1,0x1a40},{"SEI",1,0x1a10},
+  {"SEIF",1,0x1a50},{"SET",13,15},
+  {"SETDP",13,14},{"SEV",1,0x1a02},{"SEX",0,0x1d},
+  {"STA",7,0x87},{"STB",7,0xc7},{"STD",8,0xcd},
+  {"STS",9,0x10cf},{"STU",8,0xcf},{"STX",8,0x8f},
+  {"STY",9,0x108f},
+  {"SUBA",7,0x80},{"SUBB",7,0xc0},{"SUBD",8,0x83},
+  {"SWI",0,0x3f},{"SWI2",1,0x103f},{"SWI3",1,0x113f},
+  {"SYNC",0,0x13},{"TFR",11,0x1f},
+  {"TITLE",13,18},
+  {"TST",10,0x0d},{"TSTA",0,0x4d},{"TSTB",0,0x5d},
+  {"TTL",13,18},
+  {"USE",13,27},
+};
+
+struct symrecord{char name[MAXIDLEN+1];
+                 char cat;
+                 unsigned short value;
+                };
+
+int symcounter=0;
+
+/* Symbol categories.
+   0 Constant value (from equ).
+   1 Variable value (from set)
+   2 Address within program module (label).
+   3 Variable containing address.
+   4 Adress in other program module (extern)
+   5 Variable containing external address.
+   6 Unresolved address.
+   7 Variable containing unresolved address.
+   8 Public label.
+   9 Macro definition.
+  10 Public label (yet undefined).
+  11 parameter name.
+  12 local label.
+  13 empty.
+*/
+
+struct symrecord symtable[NLABELS];
+
+void processfile(char *name);
+
+struct oprecord * findop(char * nm)
+/* Find operation (mnemonic) in table using binary search */
+{
+ int lo,hi,i,s;
+ lo=0;hi=sizeof(optable)/sizeof(optable[0])-1;
+ do {
+  i=(lo+hi)/2;
+  s=strcmp(optable[i].name,nm);
+  if(s<0) lo=i+1;
+  else if(s>0) hi=i-1;
+  else break;
+ } while(hi>=lo);
+ if (s) return NULL;
+ return optable+i;
+}
+
+struct symrecord * findsym(char * nm)
+/* finds symbol table record; inserts if not found
+   uses binary search, maintains sorted table */
+{
+ int lo,hi,i,j,s;
+ lo=0;hi=symcounter-1;
+ s=1;i=0;
+ while (hi>=lo) {
+  i=(lo+hi)/2;
+  s=strcmp(symtable[i].name,nm);
+  if(s<0) lo=i+1;
+  else if(s>0) hi=i-1;
+  else break;
+ }
+ if(s) {
+  i=(s<0?i+1:i);
+  if(symcounter==NLABELS) {
+   fprintf(stderr,"Sorry, no storage for symbols!!!");
+   exit(4);
+  }
+  for(j=symcounter;j>i;j--) symtable[j]=symtable[j-1];
+  symcounter++;
+  strcpy(symtable[i].name,nm);
+  symtable[i].cat=13;
+ }
+ return symtable+i;
+}
+
+FILE *listfile,*objfile;
+char *listname,*objname,*srcname,*curname;
+int lineno;
+
+void
+outsymtable()
+{
+ int i,j=0;
+ fprintf(listfile,"\nSYMBOL TABLE");
+ for(i=0;i<symcounter;i++)
+ if(symtable[i].cat!=13) {
+  if(j%4==0)fprintf(listfile,"\n");
+  fprintf(listfile,"%10s %02d %04x",symtable[i].name,symtable[i].cat,
+                       symtable[i].value);
+  j++;
+ }
+ fprintf(listfile,"\n");
+}
+
+struct regrecord{char *name;unsigned char tfr,psh;};
+struct regrecord regtable[]=
+                 {{"D",0x00,0x06},{"X",0x01,0x10},{"Y",0x02,0x20},
+                  {"U",0x03,0x40},{"S",0x04,0x40},{"PC",0x05,0x80},
+                  {"A",0x08,0x02},{"B",0x09,0x04},{"CC",0x0a,0x01},
+                  {"CCR",0x0a,0x01},{"DP",0x0b,0x08},{"DPR",0x0b,0x08}};
+
+struct regrecord * findreg(char *nm)
+{
+ int i;
+ for(i=0;i<12;i++) {
+  if(strcmp(regtable[i].name,nm)==0) return regtable+i;
+ }
+ return 0;
+}
+
+
+char pass;             /* Assembler pass=1 or 2 */
+char listing;          /* flag to indicate listing */
+char relocatable;      /* flag to indicate relocatable object. */
+char terminate;        /* flag to indicate termination. */
+char generating;       /* flag to indicate that we generate code */
+unsigned short loccounter,oldlc;  /* Location counter */
+
+char inpline[128];     /* Current input line (not expanded)*/
+char srcline[128];     /* Current source line */
+char * srcptr;         /* Pointer to line being parsed */
+
+char unknown;          /* flag to indicate value unknown */
+char certain;          /* flag to indicate value is certain at pass 1*/
+int error;             /* flags indicating errors in current line. */
+int errors;            /* number of errors */
+char exprcat;          /* category of expression being parsed, eg.
+                          label or constant, this is important when
+                          generating relocatable object code. */
+
+
+char namebuf[MAXIDLEN+1];
+
+void
+err(int er) {
+    error |= er ;
+}
+
+void
+scanname()
+{
+ int i=0;
+ char c;
+ while(1) {
+   c=*srcptr++;
+   if(c>='a'&&c<='z')c-=32;
+   if(c!='.'&&c!='$'&&(c<'0'||c>'9')&&(c<'A'||c>'Z'))break;
+   if(i<MAXIDLEN)namebuf[i++]=c;
+ }
+ namebuf[i]=0;
+ srcptr--;
+}
+
+void
+skipspace()
+{
+ char c;
+ do {
+  c=*srcptr++;
+ } while(c==' '||c=='\t');
+ srcptr--;
+}
+
+short scanexpr(int);
+
+short scandecimal()
+{
+ char c;
+ short t=0;
+ c=*srcptr++;
+ while(isdigit(c)) {
+  t=t*10+c-'0';
+  c=*srcptr++;
+ }
+ srcptr--;
+ return t;
+}
+
+short scanhex()
+{
+ short t=0,i=0;
+ srcptr++;
+ scanname();
+ while(namebuf[i]>='0'&&namebuf[i]<='F') {
+  t=t*16+namebuf[i]-'0';
+  if(namebuf[i]>'9')t-=7;
+  i++;
+ }
+ if(i==0)error|=1;
+ return t;
+}
+
+short scanchar()
+{
+ short t;
+ srcptr++;
+ t=*srcptr;
+ if(t)srcptr++;
+ if (*srcptr=='\'')srcptr++;
+ return t;
+}
+
+short scanbin()
+{
+ char c;
+ short t=0;
+ srcptr++;
+ c=*srcptr++;
+ while(c=='0'||c=='1') {
+  t=t*2+c-'0';
+  c=*srcptr++;
+ }
+ srcptr--;
+ return t;
+}
+
+short scanoct()
+{
+ char c;
+ short t=0;
+ srcptr++;
+ c=*srcptr++;
+ while(c>='0'&&c<='7') {
+  t=t*8+c-'0';
+  c=*srcptr++;
+ }
+ srcptr--;
+ return t;
+}
+
+
+short scanlabel()
+{
+ struct symrecord * p;
+ scanname();
+ p=findsym(namebuf);
+ if(p->cat==13) {
+   p->cat=6;
+   p->value=0;
+ }
+ if(p->cat==9||p->cat==11)error|=1;
+ exprcat=p->cat&14;
+ if(exprcat==6||exprcat==10)unknown=1;
+ if(((exprcat==2||exprcat==8)
+     && (unsigned short)(p->value)>(unsigned short)loccounter)||
+     exprcat==4)
+   certain=0;
+ if(exprcat==8||exprcat==6||exprcat==10)exprcat=2;
+ return p->value;
+}
+
+/* expression categories...
+   all zeros is ordinary constant.
+   bit 1 indicates address within module.
+   bit 2 indicates external address.
+   bit 4 indicates this can't be relocated if it's an address.
+   bit 5 indicates address (if any) is negative.
+*/
+
+
+
+short scanfactor()
+{
+ char c;
+ short t;
+ skipspace();
+ c=*srcptr;
+ if(isalpha(c))return scanlabel();
+ else if(isdigit(c))return scandecimal();
+ else switch(c) {
+  case '.' :
+  case '*' : srcptr++;exprcat|=2;return loccounter;
+  case '$' : return scanhex();
+  case '%' : return scanbin();
+  case '&' : /* compatibility */
+  case '@' : return scanoct();
+  case '\'' : return scanchar();
+  case '(' : srcptr++;t=scanexpr(0);skipspace();
+             if(*srcptr==')')srcptr++;else error|=1;
+             return t;
+  case '-' : srcptr++;exprcat^=32;return -scanfactor();
+  case '+' : srcptr++;return scanfactor();
+  case '!' : srcptr++;exprcat|=16;return !scanfactor();
+  case '^' : 
+  case '~' : srcptr++;exprcat|=16;return ~scanfactor();
+ }
+ error|=1;
+ return 0;
+}
+
+#define EXITEVAL {srcptr--;return t;}
+
+#define RESOLVECAT if((oldcat&15)==0)oldcat=0;\
+           if((exprcat&15)==0)exprcat=0;\
+           if((exprcat==2&&oldcat==34)||(exprcat==34&&oldcat==2)) {\
+             exprcat=0;\
+             oldcat=0;}\
+           exprcat|=oldcat;\
+/* resolve such cases as constant added to address or difference between
+   two addresses in same module */
+
+
+short scanexpr(int level) /* This is what you call _recursive_ descent!!!*/
+{
+ short t,u;
+ char oldcat,c;
+ exprcat=0;
+ if(level==10)return scanfactor();
+ t=scanexpr(level+1);
+ while(1) {
+  skipspace();
+  c=*srcptr++;
+  switch(c) {
+  case '*':oldcat=exprcat;
+           t*=scanexpr(10);
+           exprcat|=oldcat|16;
+           break;
+  case '/':oldcat=exprcat;
+           u=scanexpr(10);
+           if(u)t/=u;else error|=1;
+           exprcat|=oldcat|16;
+           break;
+  case '%':oldcat=exprcat;
+           u=scanexpr(10);
+           if(u)t%=u;else error|=1;
+           exprcat|=oldcat|16;
+           break;
+  case '+':if(level==9)EXITEVAL
+           oldcat=exprcat;
+           t+=scanexpr(9);
+           RESOLVECAT
+           break;
+  case '-':if(level==9)EXITEVAL
+           oldcat=exprcat;
+           t-=scanexpr(9);
+           exprcat^=32;
+           RESOLVECAT
+           break;
+  case '<':if(*(srcptr)=='<') {
+            if(level>=8)EXITEVAL
+            srcptr++;
+            oldcat=exprcat;
+            t<<=scanexpr(8);
+            exprcat|=oldcat|16;
+            break;
+           } else if(*(srcptr)=='=') {
+            if(level>=7)EXITEVAL
+            srcptr++;
+            oldcat=exprcat;
+            t=t<=scanexpr(7);
+            exprcat|=oldcat|16;
+            break;
+           } else {
+            if(level>=7)EXITEVAL
+            oldcat=exprcat;
+            t=t<scanexpr(7);
+            exprcat|=oldcat|16;
+            break;
+           }
+  case '>':if(*(srcptr)=='>') {
+            if(level>=8)EXITEVAL
+            srcptr++;
+            oldcat=exprcat;
+            t>>=scanexpr(8);
+            exprcat|=oldcat|16;
+            break;
+           } else if(*(srcptr)=='=') {
+            if(level>=7)EXITEVAL
+            srcptr++;
+            oldcat=exprcat;
+            t=t>=scanexpr(7);
+            exprcat|=oldcat|16;
+            break;
+           } else {
+            if(level>=7)EXITEVAL
+            oldcat=exprcat;
+            t=t>scanexpr(7);
+            exprcat|=oldcat|16;
+            break;
+           }
+  case '!':if(level>=6||*srcptr!='=')EXITEVAL
+           srcptr++;
+           oldcat=exprcat;
+           t=t!=scanexpr(6);
+           exprcat|=oldcat|16;
+           break;
+  case '=':if(level>=6)EXITEVAL
+           if(*srcptr=='=')srcptr++;
+           oldcat=exprcat;
+           t=t==scanexpr(6);
+           exprcat|=oldcat|16;
+           break;
+  case '&':if(level>=5)EXITEVAL
+           oldcat=exprcat;
+           t&=scanexpr(5);
+           exprcat|=oldcat|16;
+           break;
+  case '^':if(level>=4)EXITEVAL
+           oldcat=exprcat;
+           t^=scanexpr(4);
+           exprcat|=oldcat|16;
+           break;
+  case '|':if(level>=3)EXITEVAL
+           oldcat=exprcat;
+           t|=scanexpr(3);
+           exprcat|=oldcat|16;
+  default: EXITEVAL
+  }
+ }
+}
+
+char mode; /* addressing mode 0=immediate,1=direct,2=extended,3=postbyte
+               4=pcrelative(with postbyte) 5=indirect 6=pcrel&indirect*/
+char opsize; /*desired operand size 0=dunno,1=5,2=8,3=16*/
+short operand;
+unsigned char postbyte;
+
+int dpsetting;
+
+
+int scanindexreg()
+{
+ char c;
+ c=*srcptr;
+ if(islower(c))c-=32;
+ if (debug) fprintf(stderr,"DEBUG: scanindexreg: indexreg=%d, mode=%d, opsize=%d, error=%d, postbyte=%02X\n",c,mode,opsize,error,postbyte);
+ switch(c) {
+  case 'X':return 1;
+  case 'Y':postbyte|=0x20;return 1;
+  case 'U':postbyte|=0x40;return 1;
+  case 'S':postbyte|=0x60;return 1;
+  default: return 0;
+ }
+}
+
+void
+set3()
+{
+ if(mode<3)mode=3;
+}
+
+void
+scanspecial()
+{
+ set3();
+ skipspace();
+ if(*srcptr=='-') {
+  srcptr++;
+  if(*srcptr=='-') {
+   srcptr++;
+   postbyte=0x83;
+  } else postbyte=0x82;
+  if(!scanindexreg())error|=2;else srcptr++;
+ } else {
+  postbyte=0x80;
+  if(!scanindexreg())error|=2;else srcptr++;
+  if(*srcptr=='+') {
+   srcptr++;
+   if(*srcptr=='+') {
+    srcptr++;
+    postbyte+=1;
+   }
+  } else postbyte+=4;
+ }
+}
+
+void
+scanindexed()
+{
+ set3();
+ postbyte=0;
+ if(scanindexreg()) {
+   srcptr++;
+   if(opsize==0) { 
+                if(unknown||!certain)opsize=3;
+                else if(operand>=-16&&operand<16&&mode==3)opsize=1;
+                else if(operand>=-128&&operand<128)opsize=2;
+                else opsize=3;
+         }
+   switch(opsize) {
+   case 1:postbyte+=(operand&31);opsize=0;break;
+   case 2:postbyte+=0x88;break;
+   case 3:postbyte+=0x89;break;
+   }
+ } else { /*pc relative*/
+  if(toupper(*srcptr)!='P')error|=2;
+  else {
+    srcptr++;
+    if(toupper(*srcptr)!='C')error|=2;
+    else {
+     srcptr++;
+     if(toupper(*srcptr)=='R')srcptr++;
+    }
+  }
+  mode++;postbyte+=0x8c;
+  if(opsize==1)opsize=2;
+ }
+}
+
+#define RESTORE {srcptr=oldsrcptr;c=*srcptr;goto dodefault;}
+
+void
+scanoperands()
+{
+ char c,d,*oldsrcptr;
+ unknown=0;
+ opsize=0;
+ certain=1;
+ skipspace();
+ c=*srcptr;
+ mode=0;
+ if(c=='[') {
+  srcptr++;
+  c=*srcptr;
+  mode=5;
+ }
+ if (debug) fprintf(stderr,"DEBUG: scanoperands: c=%c (%02X)\n",c,c);
+ switch(c) {
+ case 'D': case 'd':
+  oldsrcptr=srcptr;
+  srcptr++;
+  skipspace();
+  if(*srcptr!=',')RESTORE else {
+     postbyte=0x8b;
+     srcptr++;
+     if(!scanindexreg())RESTORE else {srcptr++;set3();}
+  }
+  break;
+ case 'A': case 'a':
+  oldsrcptr=srcptr;
+  srcptr++;
+  skipspace();
+  if(*srcptr!=',')RESTORE else {
+     postbyte=0x86;
+     srcptr++;
+     if(!scanindexreg())RESTORE else {srcptr++;set3();}
+  }
+  break;
+ case 'B': case 'b':
+  oldsrcptr=srcptr;
+  srcptr++;
+  skipspace();
+  if(*srcptr!=',')RESTORE else {
+     postbyte=0x85;
+     srcptr++;
+     if (debug) fprintf(stderr,"DEBUG: scanoperands: breg preindex: c=%c (%02X)\n",*srcptr,*srcptr);
+     if(!scanindexreg())RESTORE else {srcptr++;set3();}
+     if (debug) fprintf(stderr,"DEBUG: scanoperands: breg: postindex c=%c (%02X)\n",*srcptr,*srcptr);
+  }
+  break;
+ case ',':
+  srcptr++;
+  scanspecial();
+  break;
+ case '#':
+  if(mode==5)error|=2;else mode=0;
+  srcptr++;
+  operand=scanexpr(0);
+  break;
+ case '<':
+  srcptr++;
+  if(*srcptr=='<') {
+   srcptr++;
+   opsize=1;
+  } else opsize=2;
+  goto dodefault;
+ case '>':
+  srcptr++;
+  opsize=3;
+ default: dodefault:
+  operand=scanexpr(0);
+  skipspace();
+  if(*srcptr==',') {
+   srcptr++;
+   scanindexed();
+  } else {
+   if(opsize==0) {
+    if(unknown||!certain||dpsetting==-1||
+         (unsigned short)(operand-dpsetting*256)>=256)
+    opsize=3; else opsize=2;
+   }
+   if(opsize==1)opsize=2;
+   if(mode==5){
+    postbyte=0x8f;
+    opsize=3;
+   } else mode=opsize-1;
+  }
+ }
+ if (debug) fprintf(stderr,"DEBUG: scanoperands: mode=%d, error=%d, postbyte=%02X\n",mode,error,postbyte);
+ if(mode>=5) {
+  skipspace();
+  postbyte|=0x10;
+  if(*srcptr!=']')error|=2;else srcptr++;
+ }
+ if(pass==2&&unknown)error|=4;
+}
+
+unsigned char codebuf[128];
+int codeptr; /* byte offset within instruction */
+int suppress; /* 0=no suppress 1=until ENDIF 2=until ELSE 3=until ENDM */
+int ifcount;  /* count of nested IFs within suppressed text */
+
+unsigned char outmode; /* 0 is binary, 1 is s-records */
+
+unsigned short hexaddr;
+int hexcount;
+unsigned char hexbuffer[16];
+unsigned int chksum;
+
+extern int os9crc(unsigned char c, int crcp);
+int crc;
+
+void
+reset_crc() 
+{
+  crc = -1;
+}
+
+
+void
+flushhex()
+{
+ int i;
+ if(hexcount){
+  fprintf(objfile,"S1%02X%04X",(hexcount+3)&0xff,hexaddr&0xffff);
+  for(i=0;i<hexcount;i++)fprintf(objfile,"%02X",hexbuffer[i]);
+  chksum+=(hexaddr&0xff)+((hexaddr>>8)&0xff)+hexcount+3;
+  fprintf(objfile,"%02X\n",0xff-(chksum&0xff));
+  hexaddr+=hexcount;
+  hexcount=0;
+  chksum=0;
+ }
+}
+
+void
+outhex(unsigned char x) 
+{
+ if(hexcount==16)flushhex();
+ hexbuffer[hexcount++]=x;
+ chksum+=x;
+}
+
+void
+outbuffer()
+{
+ int i;
+ for(i=0;i<codeptr;i++) {
+   crc = os9crc(codebuf[i],crc);   
+   if(!outmode)fputc(codebuf[i],objfile);else outhex(codebuf[i]);
+ }
+}
+
+char *errormsg[]={"Error in expression",
+                "Illegal addressing mode",
+                "Undefined label",
+                "Multiple definitions of label",
+                "Relative branch out of range",
+                "Missing label",
+                "","","","","","","","","",
+                "Illegal mnemonic"
+               };
+
+void
+report()
+{
+ int i;
+ fprintf(stderr,"File %s, line %d:%s\n",curname,lineno,srcline);
+ for(i=0;i<16;i++) {
+  if(error&1) {
+   fprintf(stderr,"%s\n",errormsg[i]);
+   if(pass==2&&listing)fprintf(listfile,"**** %s\n",errormsg[i]);
+  }
+  error>>=1;
+ }
+ errors++;
+}
+
+void
+outlist()
+{
+ int i;
+ fprintf(listfile,"%04X: ",oldlc);
+ for(i=0;i<codeptr&&i<MAXLISTBYTES;i++)
+  fprintf(listfile,"%02X",codebuf[i]);
+ for(;i<=MAXLISTBYTES;i++)
+  fprintf(listfile,"  ");
+ fprintf(listfile,"%s\n",srcline);
+ while(i<codeptr) {
+   fprintf(listfile,"%04X: ",oldlc + i);
+   for(int j=0;i<codeptr&&j<MAXLISTBYTES;j++) {
+    fprintf(listfile,"%02X",codebuf[i]); i++; 
+   }
+   fprintf(listfile,"\n"); 
+ }
+}
+
+void
+setlabel(struct symrecord * lp)
+{
+ if(lp) {
+  if(lp->cat!=13&&lp->cat!=6) {
+   if(lp->cat!=2||lp->value!=loccounter)
+    error|=8;
+  } else {
+   lp->cat=2;
+   lp->value=loccounter;
+  }
+ }
+}
+
+void
+putbyte(unsigned char b)
+{
+ codebuf[codeptr++]=b;
+}
+
+void
+putword(unsigned short w)
+{
+ codebuf[codeptr++]=w>>8;
+ codebuf[codeptr++]=w&0x0ff;
+}
+
+void
+doaddress() /* assemble the right addressing bytes for an instruction */
+{
+ int offs;
+ switch(mode) {
+ case 0: if(opsize==2)putbyte(operand);else putword(operand);break;
+ case 1: putbyte(operand);break;
+ case 2: putword(operand);break;
+ case 3: case 5: putbyte(postbyte);
+    switch(opsize) {
+     case 2: putbyte(operand);break;
+     case 3: putword(operand);
+    }
+    break;
+ case 4: case 6: offs=(unsigned short)operand-loccounter-codeptr-2;
+                if(offs<-128||offs>=128||opsize==3||unknown||!certain) {
+                 if((!unknown)&&opsize==2&&(offs<-128||offs>=128) )
+                   error|=16;
+                 offs--;
+                 opsize=3;
+                 postbyte++;
+                }
+                putbyte(postbyte);
+                if (debug) fprintf(stderr,"DEBUG: doaddress: mode=%d, opsize=%d, error=%d, postbyte=%02X, operand=%04X offs=%d\n",mode,opsize,error,postbyte,operand,offs);
+                if(opsize==3)putword(offs);
+                else putbyte(offs);
+ }
+}
+
+void
+onebyte(int co)
+{
+ putbyte(co);
+}
+
+void
+twobyte(int co)
+{
+ putword(co);
+}
+
+void
+oneimm(int co)
+{
+ scanoperands();
+ if(mode>=3)error|=2;
+ putbyte(co);
+ putbyte(operand);
+}
+
+void
+lea(int co)
+{
+ putbyte(co);
+ scanoperands();
+ if(mode==0) error|=2;
+ if(mode<3) {
+   opsize=3;
+   postbyte=0x8f;
+   mode=3;
+ }
+ if (debug) fprintf(stderr,"DEBUG: lea: mode=%d, opsize=%d, error=%d, postbyte=%02X, *src=%c\n",mode,opsize,error,postbyte,*srcptr);
+ doaddress();
+}
+
+void
+sbranch(int co)
+{
+ int offs;
+ scanoperands();
+ if(mode!=1&&mode!=2)error|=2;
+ offs=(unsigned short)operand-loccounter-2;
+ if(!unknown&&(offs<-128||offs>=128))error|=16;
+ if(pass==2&&unknown)error|=4;
+ putbyte(co);
+ putbyte(offs);
+}
+
+void
+lbra(int co)
+{
+ scanoperands();
+ if(mode!=1&&mode!=2)error|=2;
+ putbyte(co);
+ putword(operand-loccounter-3);
+}
+
+void
+lbranch(int co)
+{
+ scanoperands();
+ if(mode!=1&&mode!=2)error|=2;
+ putword(co);
+ putword(operand-loccounter-4);
+}
+
+void
+arith(int co)
+{
+ scanoperands();
+ switch(mode) {
+ case 0:opsize=2;putbyte(co);break;
+ case 1:putbyte(co+0x010);break;
+ case 2:putbyte(co+0x030);break;
+ default:putbyte(co+0x020);
+ }
+ doaddress();
+}
+
+void
+darith(int co)
+{
+ scanoperands();
+ switch(mode) {
+ case 0:opsize=3;putbyte(co);break;
+ case 1:putbyte(co+0x010);break;
+ case 2:putbyte(co+0x030);break;
+ default:putbyte(co+0x020);
+ }
+ doaddress();
+}
+
+void
+d2arith(int co)
+{
+ scanoperands();
+ switch(mode) {
+ case 0:opsize=3;putword(co);break;
+ case 1:putword(co+0x010);break;
+ case 2:putword(co+0x030);break;
+ default:putword(co+0x020);
+ }
+ doaddress();
+}
+
+void
+oneaddr(int co)
+{
+ scanoperands();
+ switch(mode) {
+ case 0: error|=2;break;
+ case 1: putbyte(co);break;
+ case 2: putbyte(co+0x70);break;
+ default: putbyte(co+0x60);break;
+ }
+ doaddress();
+}
+
+void
+tfrexg(int co)
+{
+ struct regrecord * p;
+ putbyte(co);
+ skipspace();
+ scanname();
+ if((p=findreg(namebuf))==0)error|=2;
+ else postbyte=(p->tfr)<<4;
+ skipspace();
+ if(*srcptr==',')srcptr++;else error|=2;
+ skipspace();
+ scanname();
+ if((p=findreg(namebuf))==0)error|=2;
+ else postbyte|=p->tfr;
+ putbyte(postbyte);
+}
+
+void
+pshpul(int co)
+{
+ struct regrecord *p;
+ putbyte(co);
+ postbyte=0;
+ do {
+  if(*srcptr==',')srcptr++;
+  skipspace();
+  scanname();
+  if((p=findreg(namebuf))==0)error|=2;
+  else postbyte|=p->psh;
+  skipspace();
+ }while (*srcptr==',');
+ putbyte(postbyte);
+}
+
+void
+skipComma()
+{
+ while(*srcptr && *srcptr!='\n' && *srcptr!=',')srcptr++;
+ if (*srcptr==',') {
+   srcptr++;
+ } else {
+   error|=1;  
+ }
+}
+
+int modStart;
+
+void os9begin()
+{
+ generating=1;
+ modStart = loccounter;
+ reset_crc();
+ putword(0x87cd);
+ putword(scanexpr(0)-loccounter);  // module size
+ skipComma();
+ putword(scanexpr(0)-loccounter);  // offset to module name
+ skipComma();
+ putbyte(scanexpr(0));             // type / language
+ skipComma();
+ putbyte(scanexpr(0));             // attribute
+ int parity=0;
+ for(int i=0; i< 8; i++) parity^=codebuf[i];
+ putbyte(parity^0xff);              // header parity
+ skipspace();
+ while (*srcptr==',') {             // there are some more
+   srcptr++;
+   putword(scanexpr(0));   
+   skipspace();
+ }
+}
+
+void os9end()
+{
+ crc = crc ^ 0xffffff;
+
+ putbyte((crc>>16)&0xff);
+ putbyte((crc>>8)&0xff);
+ putbyte(crc&0xff);
+}
+
+
+void
+pseudoop(int co,struct symrecord * lp)
+{
+ int i;
+ char c;
+ char *fname;
+ int locsave;
+
+ switch(co) {
+ case 0:/* RMB */
+        setlabel(lp);
+        operand=scanexpr(0);
+        if(unknown)error|=4;
+        loccounter+=operand;
+        if(generating&&pass==2) {
+           if(!outmode)for(i=0;i<operand;i++)fputc(0,objfile);
+           else flushhex();  
+        }   
+        hexaddr=loccounter;
+        break;
+ case 5:/* EQU */
+        operand=scanexpr(0);
+        if(!lp)error|=32;
+        else {
+         if(lp->cat==13||lp->cat==6||
+            (lp->value==(unsigned short)operand&&pass==2)) {
+          if(exprcat==2)lp->cat=2;
+          else lp->cat=0;
+          lp->value=operand;
+         } else error|=8;
+        }
+        break;
+ case 7:/* FCB */
+        setlabel(lp);
+        generating=1;
+        do {
+        if(*srcptr==',')srcptr++;
+        skipspace();
+        if(*srcptr=='\"') {
+         srcptr++;
+         while(*srcptr!='\"'&&*srcptr)
+          putbyte(*srcptr++);
+         if(*srcptr=='\"')srcptr++;
+        } else {
+          putbyte(scanexpr(0));
+          if(unknown&&pass==2)error|=4;
+        }
+        skipspace();
+        } while(*srcptr==',');
+        break;
+ case 8:/* FCC */
+        setlabel(lp);
+        skipspace();
+        c=*srcptr++;
+        while(*srcptr!=c&&*srcptr)
+         putbyte(*srcptr++);
+        if(*srcptr==c)srcptr++;
+        break;
+ case 9:/* FDB */
+        setlabel(lp);
+        generating=1;
+        do {
+         if(*srcptr==',')srcptr++;
+         skipspace();
+         putword(scanexpr(0));
+         if(unknown&&pass==2)error|=4;
+         skipspace();
+        } while(*srcptr==',');
+        break;
+ case 23 :/* FCS */
+        setlabel(lp);
+        generating=1;
+        skipspace();
+        int sep = *srcptr;
+        if(sep=='\"' || sep=='/') {
+         srcptr++;
+         while(*srcptr!=sep&&*srcptr)
+          putbyte(*srcptr++);
+         if(*srcptr==sep)srcptr++; 
+         codebuf[codeptr-1] |= 0x80;  // os9 string termination
+        }
+        break;
+ case 1: /* ELSE */
+        suppress=1;
+        break;
+ case 21: /* IFP1 */
+        if(pass==2)suppress=2;
+        break;                
+ case 29: /* IFGT */
+        operand=scanexpr(0);
+        if(unknown)error|=4;
+        if(operand>0)suppress=2;
+        break;                
+ case 31: /* IFLT */
+        operand=scanexpr(0);
+        if(unknown)error|=4;
+        if(operand<0)suppress=2;
+        break;                
+ case 28: /* IFNE */
+        operand=scanexpr(0);
+        if(unknown)error|=4;
+        if(operand==0)suppress=2;
+        break;                
+ case 30: /* IFEQ */
+ case 10: /* IF */
+        operand=scanexpr(0);
+        if(unknown)error|=4;
+        if(!operand)suppress=2;
+        break;                
+ case 12: /* ORG */
+         operand=scanexpr(0);
+         if(unknown)error|=4;
+         if(generating&&pass==2) {
+           for(i=0;i<(unsigned short)operand-loccounter;i++)
+                if(!outmode)fputc(0,objfile);else flushhex();
+         }             
+         loccounter=operand;
+         hexaddr=loccounter;
+         break;
+  case 14: /* SETDP */
+         operand=scanexpr(0);
+         if(unknown)error|=4;
+         if(!(operand&255))operand=(unsigned short)operand>>8;
+         if((unsigned)operand>255)operand=-1;
+         dpsetting=operand;              
+         break;
+  case 15: /* SET */
+        operand=scanexpr(0);
+        if(!lp)error|=32;
+        else {
+         if(lp->cat&1||lp->cat==6) {
+          if(exprcat==2)lp->cat=3;
+          else lp->cat=1;
+          lp->value=operand;
+         } else error|=8;
+        }
+        break;
+   case 2: /* END */
+        terminate=1;
+        break;     
+   case 27: /* USE */     
+        locsave = loccounter ;
+   case 16: /* INCLUDE */     
+        skipspace();
+        if(*srcptr=='"')srcptr++;
+        i = 0;
+        for(i=0; !(srcptr[i]==0||srcptr[i]=='"'); i++);
+        int len = i;
+        fname = calloc(1,len);
+        for(i=0;i<len;i++) {
+          if(*srcptr==0||*srcptr=='"')break;
+          fname[i]=*srcptr++;
+        }
+        fname[i]=0;
+        processfile(fname);
+        codeptr=0;
+        srcline[0]=0;
+        if (co==27) loccounter = locsave;
+        break; 
+   case 24: /* MOD */     
+        loccounter = 0;
+        setlabel(lp);
+        os9begin();
+        break; 
+   case 25: /* EMOD */     
+        os9end();
+        break; 
+   case 32: /* OS9 */     
+        setlabel(lp);
+      putword(0x103f); // SWI2
+        putbyte(scanexpr(0));
+        break; 
+   case 18: /* TTL */     
+        break;
+   case 19: /* OPT */     
+   case 26: /* NAM */     
+   case 20: /* PAG */     
+   case 3:  /* ENDIF/ENDC */     
+        break; 
+ }
+}
+
+
+void
+processline()
+{
+ struct symrecord * lp;
+ struct oprecord * op;
+ int co;
+ char c;
+ srcptr=srcline;
+ oldlc=loccounter;
+ error=0;
+ unknown=0;certain=1;
+ lp=0;
+ codeptr=0;
+ if(isalnum(*srcptr)) {
+  scanname();lp=findsym(namebuf);
+  if(*srcptr==':') srcptr++;
+ }
+ skipspace();
+ if(isalnum(*srcptr)) {
+  scanname();
+  op=findop(namebuf);
+  if(op) {
+   if(op->cat!=13){
+     setlabel(lp);
+     generating=1;
+   }
+   co=op->code;
+   switch(op->cat) {
+   case 0:onebyte(co);break;
+   case 1:twobyte(co);break;
+   case 2:oneimm(co);break;
+   case 3:lea(co);break;
+   case 4:sbranch(co);break;
+   case 5:lbranch(co);break;
+   case 6:lbra(co);break;
+   case 7:arith(co);break;
+   case 8:darith(co);break;
+   case 9:d2arith(co);break;
+   case 10:oneaddr(co);break;
+   case 11:tfrexg(co);break;
+   case 12:pshpul(co);break;
+   case 13:pseudoop(co,lp);
+   }
+   c=*srcptr;
+   if (debug) fprintf(stderr,"DEBUG: processline: mode=%d, opsize=%d, error=%d, postbyte=%02X c=%c\n",mode,opsize,error,postbyte,c);
+   if(c!=' '&&*(srcptr-1)!=' '&&c!=0&&c!=';')error|=2;
+  }
+  else error|=0x8000;
+ }else setlabel(lp);
+ if(pass==2) {
+  outbuffer();
+  if(listing)outlist();
+ }
+ if(error)report();
+ loccounter+=codeptr;
+}
+
+void
+suppressline()
+{
+ struct oprecord * op;
+ srcptr=srcline;
+ oldlc=loccounter;
+ codeptr=0;
+ if(isalnum(*srcptr)) {
+  scanname();
+  if(*srcptr==':')srcptr++;
+ }
+ skipspace();
+ scanname();op=findop(namebuf);
+ if(op && op->cat==13) {
+  if(op->code==10||op->code==13||op->code==29||op->code==28||op->code==21||op->code==30) ifcount++;
+  else if(op->code==3) {
+   if(ifcount>0)ifcount--;else if(suppress==1|suppress==2)suppress=0;
+  } else if(op->code==1) {
+   if(ifcount==0 && suppress==2)suppress=0;
+  }
+ }  
+ if(pass==2&&listing)outlist();
+}
+
+void
+usage(char*nm)
+{
+  fprintf(stderr,"Usage: %s [-o objname] [-l listname] [-s srecord-file] srcname\n",nm);
+  exit(2);
+}
+
+char *
+strconcat(char *s,int spos,char *d)
+{
+  int slen = strlen(s);
+  int dlen = strlen(d);
+  if ( spos == 0) spos = slen;
+  char *out = calloc(1,spos+dlen+1);
+  int i = 0;
+  for(; i< spos; i++ ) out[i] = s[i];
+  for(; i< spos+dlen+1; i++ ) out[i] = *d++;
+  return out;
+} 
+
+
+void
+getoptions(int c,char*v[])
+{
+ int i=0;
+ if(c==1)usage(v[0]);
+ if(strcmp(v[1],"-d")==0) {
+   debug=1;
+   i++;
+ }
+ if(strcmp(v[1],"-o")==0) {
+   if(c<4)usage(v[0]);
+   objname = v[2];
+   i+=2;
+ }
+ if(strcmp(v[i+1],"-s")==0) {
+   if(c<4+i)usage(v[0]);
+   objname=v[i+2];
+   outmode=1;
+   i+=2;
+ }
+ if(strcmp(v[i+1],"-l")==0) {
+   if(c<4+i)usage(v[0]);
+   listname=v[2+i];
+   i+=2;
+ }
+ srcname=v[1+i];
+ if(objname==0) {
+   for(i=0;srcname[i]!='.' && srcname[i]!=0 ;i++) ;
+   objname = strconcat(srcname,i,".b");
+ }
+ listing=(listname!=0);
+}
+
+void
+expandline()
+{
+ int i=0,j=0,k,j1;
+ for(i=0;i<128&&j<128;i++)
+ {
+  if(inpline[i]=='\n') {
+    srcline[j]=0;break;
+  }
+  if(inpline[i]=='\t') {
+    j1=j;
+    for(k=0;k<8-j1%8 && j<128;k++)srcline[j++]=' ';
+  }else srcline[j++]=inpline[i];
+ }
+ srcline[127]=0;
+}
+
+
+void
+processfile(char *name)
+{
+ char *oldname;
+ int oldno;
+ FILE *srcfile;
+ oldname=curname;
+ curname=name;
+ oldno=lineno;
+ lineno=0;
+ if((srcfile=fopen(name,"r"))==0) {
+   int i = strlen(oldname);
+   while(i>0 && oldname[i]!='/') i--;
+   if (i>0) {
+       char *next = strconcat(oldname,i+1,name);
+       if((srcfile=fopen(next,"r"))==0) {
+          fprintf(stderr,"Cannot open source file %s\n",next);
+          exit(4);
+       }
+       curname = next;
+   } else {
+     fprintf(stderr,"Cannot open source file %s\n",name);
+     exit(4);
+   }
+ }
+ while(!terminate&&fgets(inpline,128,srcfile)) {
+   expandline();
+   lineno++;
+   srcptr=srcline;
+   if(suppress)suppressline(); else processline();
+ }
+ fclose(srcfile);
+ if(suppress) {
+   fprintf(stderr,"improperly nested IF statements in %s",curname);
+   errors++;
+   suppress=0;
+ }
+ lineno=oldno;
+ curname=oldname;
+}
+
+int
+main(int argc,char *argv[])
+{
+ char c;
+ getoptions(argc,argv);
+ pass=1;
+ errors=0;
+ generating=0;
+ terminate=0;
+ processfile(srcname);
+ if(errors) {
+  fprintf(stderr,"%d Pass 1 Errors, Continue?",errors);
+  c=getchar();
+  if(c=='n'||c=='N') exit(3);
+ }
+ pass=2;
+ loccounter=0;
+ errors=0;
+ generating=0;
+ terminate=0;
+ if(listing&&((listfile=fopen(listname,"w"))==0)) {
+  fprintf(stderr,"Cannot open list file");
+  exit(4);
+ }
+ if((objfile=fopen(objname,outmode?"w":"wb"))==0) {
+  fprintf(stderr,"Cannot write object file\n");
+  exit(4);
+ }
+ processfile(srcname);
+ fprintf(stderr,"%d Pass 2 errors.\n",errors);
+ if(listing) {
+  fprintf(listfile,"%d Pass 2 errors.\n",errors);
+  outsymtable();
+  fclose(listfile);
+ }
+ if(outmode){
+  flushhex();
+  fprintf(objfile,"S9030000FC\n");
+ } 
+ fclose(objfile);
+ return 0;
+}
+