/* X68 Cross Assembler Overlay Segment SEG1 */ /* Pass One of assembly. */ /* * PERMISSION IS GRANTED FOR THE PUBLIC DOMAIN * NON COMMERCIAL USE OF THIS SOFTWARE. * * (C) CHRIS UNDERY 25th OCTOBER 1982 * 11 MARGARET ST NEWTOWN 2042 * SYDNEY, AUSTRALIA */ /* NOTE Parts of this program were translated from a Reality DATA/BASIC program .In particular, it was necessary to keep some `line numbers`. These are used as goto labels , passed to functions and returned as values from functions. The latter useage is somewhat obscure but should become apparent upon studying the functions. Major Changes from Data Basic Version 1. Allowed use of Tektronix mnemonics 2. Hash group linked symbol table (fast) 3. Expression evaluator 4. BIAS psuedo op for ROM system development 5. Origin of assembly can be set by console command 6. Symbol table dump to file and listing device 7. Intel hex output with adjustable address field 8. Optional pre-processor overlay 9. listing controls inlc titles 10. single level include operation 11. symbol table can be purged of local symbols */ #include "x68.h" #define COMMENT ';' main() { char temp[150], ip_name[20], op_name[20]; int opc; /* Result code */ strcpy(title,"\t\t\t\t"); including = bias = 0; /* clear rom bias */ printf("Pass one (Version A.1)\n"); strcpy(ip_name,filename); /* Get input file name */ if (index(ip_name,".") < 0 ) strcat(ip_name,".ASM"); strcpy(iname,ip_name); if (fopen(ip_name,ibuf) == ERROR) { printf("Cannot open: %s\007\n",ip_name); exit(); } strcpy(op_name,filename); /* Create UFN.TMP */ strcat(op_name,".TMP"); if (fcreat(op_name,obuf) == ERROR ) { printf("DISK FULL: %s\007\n",op_name); exit(); } lineno = errors = 0; aloop: if (!including) { /* if doing single level include */ if (fgets(line,ibuf)) { if (line[0] == '\n') { /* cater for blank lines */ fprintf(obuf,";\n"); } else { opc = assemble(); if (opc == 33) goto endop; if (opc != 10) fputs(line,obuf); numbytes += nb; } goto aloop; /* get another load of code */ } else goto endop; /* have eof in primary source file */ } else { /* we are including */ while (fgets(line,ifile)) { if (line[0] == '\n') { fprintf(obuf,";\n"); } else { opc = assemble(); numbytes += nb; if (opc == 33) goto incend; if (opc != 10) fputs(line,obuf); } } incend: including = 0; /* no longer including */ fclose(ifile); /* close the file buffer for next include */ goto aloop; /* get next load from primary instead */ } endop: numlines = lineno; /* For statistics */ fclose(ibuf); putc(0x1a,obuf); fflush(obuf); /* Flush all buffers */ fclose(obuf); /* And terminate pass1 */ return(1); } /* Assemble Next line of source code from LINE */ /* placing output to UFN.TMP */ assemble() { int result; /* Operation result handshake */ char c; tokenise(); /* Next TOKENS please */ c = label[0]; /* Test for comment and null */ if ( c == ';' || c == '*' ) return(1); if (index(mnem,"END") >=0 ) return(33); /* End of processing */ if (index(mnem,"INCLUDE") >= 0) { including = 1; /* start including */ iname[0] = '\0'; strcat(iname,operand); /* open file */ strcat(iname,".ASM"); printf("Including %s\n",iname); if (fopen(iname,ifile) == ERROR) { printf("\nCannot open include file\n"); exit(); } label[0] = ';'; /* make into a comment */ return(10); } if (index(mnem,"SPACE") >= 0) return(1); if (index(mnem,"NOLIST") >= 0) return(1); if (index(mnem,"LIST") >= 0) return(1); if (index(mnem,"PAGE") >= 0) return(1); if (index(mnem,"TITLE") >= 0) return(1); if (index(mnem,"PURGE") >= 0) { /* purge local symbols */ purge(operand); return(10); /* dont write to listing file */ } blankem(); /* Initialise assy registers */ if (newlab() == 90) { /* New label handler */ wrapup(); return(1); /* error so write line as is */ } if (index(mnem,"EQU") >= 0 ) { /* Piss off easy ones */ wrapup(); return(1); } if (index(mnem,"ORG") >= 0 ) { /* set location counter */ wrapup(); return(1); } if (index(mnem,"BLOCK") >= 0 ) { /* reserve memory space */ wrapup(); return(1); } if (index(mnem,"ASCII") >= 0 ) { /* inline string */ return(fcc()); } if (index(mnem,"BYTE") >= 0 ) { /* form constant byte */ return(fcb('b')); } if (index(mnem,"HALT") >= 0 ) { /* Do adm special instruction */ halt(); return(10); } if (index(mnem,"WORD") >= 0 ) { return(fcb('w')); /* word sized operation */ } if (index(mnem,"BIAS") >=0 ) { /* get prolog m980 buffer offset */ if (!evaluate(operand)) { fault(600); } nb = 0; /* Dont generate code */ bias = dec; strcpy(i1," "); wrapup(); return(1); } idecode(); /* Do Instruction decode */ wrapup(); /* Create *.TMP record */ } newlab() /* Extract label and put in symbol table */ { struct nlist *sp, *lookup(), *install(); char ind_ref[16]; /* trick string */ if (index(mnem,"ORG") >= 0) { /* Handle new origin */ if (!evaluate(operand)) fault(600); decad = dec; /* get result */ nb = 0; strcpy(i1," "); } tohex(hexad,decad); if (label[0] == '\0') goto N15; /* But must check for RMB */ if ((sp = lookup(label)) == NULL) goto N15; /* if new pass over here */ strcpy(labrec,sp->def); /* Move out of dynamic store */ if (labrec[0] != '?') { /* A duplicity of labels */ fault(14); return(90); /* Tell proc above we fucked out */ } strcpy(labrec,hexad); /* else copy address to symbol def */ N15: if (index(mnem,"BLOCK") >= 0) { /* reserve space operation */ if (!evaluate(operand)) fault(600); nb = dec; /* Get result */ strcpy(i1," "); /* no machine code to generate */ } if (label[0] == NULL ) return(1); /* Fix bug in original vers */ if (index(mnem,"EQU") < 0 ) { /* Perform EQUATE operation */ strcpy(labrec,hexad); goto N18; } if (evaluate(operand)) { /* dont be fooled by this */ tohex(labrec,dec); /* assume good operation */ getsub(operand); /* if either = ?? ng */ if ((sp = lookup(xp1)) != NULL) { if (index(sp->def,"??") >= 0) goto NG; } if ((sp = lookup(xp2)) != NULL) { if (index(sp->def,"??") >= 0) goto NG; } } else { /* we have complex on pass 1 */ NG: strcpy(labrec,"????"); strcpy(i1," "); strcpy(i2,"??"); strcpy(i3,"??"); /* throw unresolved label into st */ if ((sp = install(label,labrec)) == NULL) { printf("SYMBOL TABLE FULL\n"); exit(); } nb = 0; /* dont increment address cntr */ return(1); /* dont install complex exp ! */ } N16: strcpy(i1," "); /* Now fix up assy registers */ strcpy(brad,labrec); nb = 0; strcpy(hexad," "); N18: if ((sp = install(label,labrec)) == NULL ) { printf("\nLine %d SYMBOL TABLE OVERFLOW\007\n",lineno); exit(); /* TERMINATE OVERLAY */ } return(1); /* Else good result */ } tokenise() /* Break down line into TOKENS */ { lineno++; getok(label,line,1,8); /* Parse input line */ getok(mnem,line,2,9); getok(operand,line,3,16); /* Could be complex expr */ getcomment(line); if (index(comment,"\n") < 0) strcpy(comment,"\n"); return(1); } blankem() /* Clear assembler registers */ { strcpy(i1,"??"); /* Indicate unresolved instruction */ strcpy(i2," "); /* and clear other registers */ strcpy(i3," "); strcpy(brad," "); nb = 3; /* Assume 3 byte instruction */ } wrapup() /* Recombine Tokens and new data for output line */ { tohex(hexad,decad); /* Calculate HEXaddress */ strcpy(line,hexad); /* Hex address first */ strcat(line," "); strcat(line,i1); /* Machine instruction 1 */ strcat(line," "); strcat(line,i2); /* Machine instruction 2 */ strcat(line,i3); /* Machine instruction 3 */ strcat(line," "); strcat(line,brad); /* Branch address */ strcat(line," "); strcat(line,label); /* Label must LEFT JUSTIFY IT LATER */ strcat(line,"\t"); /* TAB will do just fine */ strcat(line,mnem); /* Mnemonic */ strcat(line,"\t"); strcat(line,operand); /* Operand */ strcat(line,"\t"); strcat(line,comment); /* Comments fill up remainder */ decad += nb; /* Adjust PC for next line through */ } idecode() /* INSTRUCTION DECODER LOGIC */ { char x; if (!binary(mnem)) { /* Mnemonic not found in table */ fault(20); /* Error code 20 */ return(90); /* Indicate error to procs above */ } strcpy(i1,inst); /* Get instruction from table */ switch(dest[0]) { /* Use steering logic from table */ case '1': { if (operand[0] != '\0') { strcpy(temp," "); strcat(temp,operand); strcat(temp,comment); strcpy(comment,temp); /* moove it back */ operand[0] = '\0'; } /* Second space forgotten */ nb = 1; /* Single byte instruction */ return(45); } case '2': return(regadr(200)); /* Register and address */ case '3': case '4': { I24: if (operand[0] == '#' ) { fault(23); return(90); } return(regadr(200)); /* Register etc */ } case '5': { strcpy(m4,"A"); return(regadr(200)); /* Reg decode */ } case '6': { strcpy(m4,"B"); return(regadr(200)); /* Reg decode */ } case '7': { strcpy(m4,"A"); return(regadr(200)); } case '8': { strcpy(m4,"B"); return(regadr(200)); } case '9': return(regadr(201)); /* Branch instruction */ } /* End case switch */ printf("\"Opcodes\" file is corrupted\7\n"); } /* End Instruction Decoder Logic */ /* Regadr is passed the old DATA BASIC line number in order */ /* to force entry at the correct point */ regadr(n) /* Register and Address decoder */ int n; /* DATA BASIC line number */ { struct nlist *symptr, *lookup(), *install(); if (n == 201) goto R201; /* Its the only way */ if (operand[0] == '#') goto R210; if (operand[0] == 'X') { if (!isalpha(operand[1])) goto R230; } if (index(operand,",X") >= 0 ) goto R230; /* Index inst */ R201: if ((symptr = lookup(operand)) == NULL) goto R202; strcpy(labrec,symptr->def); /* Get definition */ if (labrec[0] == '?') goto R203; goto R205; /* This really sucks badly */ R202: if (!evaluate(operand)) { /* might just be num literal */ strcpy(labrec,"????"); /* nope.. stash it charlie */ goto R203; } else { tohex(labrec,dec); } goto R205; R203: strcpy(i2,"??"); R204: if ((symptr = install(operand,labrec)) == NULL) { printf("SYMBOL TABLE OVERFLOW\007\n"); exit(); /* ABORT ABORT ABORT ABORT */ } noput: if (dest[0] == '9') { nb = 2; return(45); /* Another line number */ } strcpy(i3,"??"); goto R241; R205: if ((symptr = install(operand,labrec)) == NULL) { printf("SYMBOL TABLE OVERFLOW\007\n"); exit(); } smov(labad,labrec,4); /* get first 4 bytes of labrec */ dlabad = todec(labad,16); /* Convert to decimal */ if (dest[0] == '9') goto R250; /* Relative address */ if (dest[0] == '3') goto R240; /* Extended */ if (m4[0] == SP) goto R240; /* Extended */ if (dest[0] == '7') goto R240; /* cannot have 2byte jsr */ if (dlabad > 256 || dlabad < 0) goto R240; /* Extended */ if (m4[0] == '\0') goto R240; /* Hope to trap nasties */ goto R220; /* Direct addressing */ R210: nb = 2; class[0] = operand[1]; /* Determine operand class */ if (m4[0] == 'A') insert(i1,'8'); if (m4[0] == 'B') insert(i1,'C'); if (dest[0] > '4') goto R215; /* CPX LDX or LDS */ if (!evaluate(operand)) { fault(210); return(90); } tohex(hex,dec); /* Convert result to hex */ smov(i2,&hex[2],2); return(45); R215: nb = 3; strcpy(i3," "); if (!evaluate(operand)) { /* assume pass 2 will fix it */ strcpy(i2,"??");strcpy(i3,"??"); return(90); } tohex(hex,dec); /* Get result of op */ smov(i2,&hex[0],2); smov(i3,&hex[2],2); /* fix bug with lds ldx etc vers a.1 */ return(45); R220: nb = 2; smov(i2,&labad[2],2); if (m4[0] == 'A') { insert(i1,'9'); return(45); } if (m4[0] == 'B') { insert(i1,'D'); return(45); } fault(220); return(90); R230: nb = 2; if (!getexp(temp,operand)) dec = 0; else evaluate(temp); tohex(hex,dec); smov(i2,&hex[2],2); if (m4[0] == '\0') { insert(i1,'6'); return(45); } if (m4[0] == 'A') { insert(i1,'A'); return(45); } if (m4[0] == 'B') { insert(i1,'E'); return(45); } fault(231); return(90); R240: strcpy(i2,labad); i3[0] = '\0'; R241: if (m4[0] == '\0') { /* Label not known */ insert(i1,'7'); return(45); } if (m4[0] == 'A') { insert(i1,'B'); return(45); } if (m4[0] == 'B') { insert(i1,'F'); return(45); } fault(241); return(90); R250: dec = dlabad - decad + 254; /* Label known */ if (dec < 128 ) { fault(250); return(90); } if (dec > 383) { fault(251); return(90); } tohex(hex,dec); smov(i2,&hex[2],2); nb = 2; strcpy(brad,labad); return(45); } getexp(to,from) /* Get expression delimted by COMMA */ char *to, *from; { while (*to = *from) { /* Move characters */ if (*to == COMMA) { *++to = NULL; return(1); } to++; from++; } return(0); } getnxtp(to, from, n) char *to, *from; int n; { int count; count = 0; while (count != n) { if (*from == COMMA) count++; if (*from == NULL) { *to = NULL; return(0); } from++; } getexp(to, from); } insert(s,c) /* Insert character in front of string */ char s[], c; { s[1] = s[0]; /* move first character back one space */ s[0] = c; /* plonk character in front */ s[2] = '\0'; /* terminate the string */ } fcc() /* STRING psuedo op */ { int byteno, i; int j; char up; int commacnt; up = 1; commacnt = 0; byteno = j = 0; while (up) { if ((operand[j] == 0x22) || (operand[j] == 0x27)) { j++; commacnt++; if (commacnt == 2) up = 0; } else { temp[0] = 0x22; temp[1] = operand[j]; temp[2] = '\0'; evaluate(temp); tohex(hex,dec); smov(i1,&hex[2],2); buildline(++byteno,'b'); j++; } } return(10); } fcb(n) /* byte or word psuedo operations */ char n; { int byteno, i, j; indx1 = indx2 = 0; i = byteno = 0; /* Assume no bytes ! */ j = numargs(operand); while (i++ < j) { /* Loop through arguments */ movenxt(); if (n == 'b') { /* do byte specifics */ if (!evaluate(temp)) { strcpy(i1,"??"); } else { tohex(hex,dec); /* Extract value */ smov(i1,&hex[2],2); } } else { /* word operation */ if (!evaluate(temp)) { strcpy(i1," "); strcpy(i2,"??"); strcpy(i3,"??"); } else { tohex(hex,dec); smov(i2,&hex[0],2); smov(i3,&hex[2],2); } } buildline(++byteno,n); } return(10); } movenxt() /* Move next argument into temp string */ { int i; /* String index */ char movf; /* boolean control flg */ i = 0; movf = 1; while (movf) { temp[i] = operand[indx1]; /* Use global for memory */ if ((temp[i] == NULL) || (temp[i] == ',')) { indx1++; /* Crank index for next */ temp[i] = NULL; movf = 0; /* Make flag false */ } else { i++; /* BUmp pointers */ indx1++; } } } buildline(bn,n) /* Build output line for BYTE, STRING */ int bn,n; { if (bn == 1) { /* First time into this shit */ tohex(hexad,decad); strcpy(line,hexad); /* wish I had good cat function */ strcat(line," "); if (n == 'b') { strcat(line,i1); strcat(line," "); } else { strcat(line," "); strcat(line,i2); strcat(line,i3); strcat(line," "); } strcat(line,label); strcat(line,"\t"); strcat(line,mnem); strcat(line,"\t"); strcat(line,operand); strcat(line,"\t"); strcat(line,comment); } else { tohex(hexad,decad); /* Bump address */ strcpy(line,hexad); strcat(line," "); if (n != 'b') { strcat(line," "); strcat(line,i2); strcat(line,i3); } else strcat(line,i1); strcat(line,"\n"); /* Add newline character */ } fputs(line,obuf); /* Internal write */ if (n == 'b') decad += 1; else decad += 2; /* for word operation */ } /* this function handles the macro instruction HALT which */ /* is used by the ADM31/32 firmware. The instruction is added */ /* as a `built-in' */ halt() { tohex(hexad,decad); strcpy(line,hexad); strcat(line,"+B7 7000 ####\tHALT\t"); fputs(line,obuf); fputs(comment,obuf); /* for user noise */ decad += 3; tohex(hexad,decad); strcpy(line,hexad); strcat(line,"+01 ####\n"); fputs(line,obuf); decad++; } .