#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <error.h> #include <errno.h> /* This program is a macro-processor written specifically to expand the code in the .sa assembly files at https://github.com/brouhaha/fp09/tree/master/src - it is not robust enough to be used on arbitrary new code. It was written specifically to remove the macros in these files so that the remaining assembly language code could then be converted 1:1 to any current assembler. It's a quick hack. Don't use this as a template for writing macro processors! Rather than use the macro bodies defined in the .sa files, it embeds the macro expansion in the C code below, and throws away the macro definitions when it hits them in the source files. Syntax is: expand file.sa and it writes the output to file.asm When testing, one of the input files was found to have some corruption (missing ENDIFs to some IF statements) and there were two occurances of symbolic literals being used, where this code relies on seeing actual literal numbers. The source file for that instance has to be edited as well: gtoal@linux:~/src/asm-expand$ diff fp09/ins.sa fp09/ins.sa~ 512c512 < MUL10 X,2 EXPLEN = 2 --- > MUL10 X,EXPLEN 515c515 < MUL10 X,8 ACCLEN = SIGLEN-1 = 9-1 = 8 --- > MUL10 X,ACCLEN gtoal@linux:~/src/asm-expand$ diff fp09/util.sa fp09/util.sa~ 147,154c147 < IFCC NE File appears to be corrupt here... ************************** < ENDIF < ENDIF < ENDIF < RTS < < < --- > IFCC NE************************** Graham Toal 2 April 2020. */ #ifndef FALSE #define FALSE (0!=0) #define TRUE (0==0) #endif int verbose = TRUE; FILE *infile, *actual_outfile, *outfile, *nullout; long start_of_line; // To keep the installation of this code simple, we'll define the macros in // the C code rather than via an external file that might get separated. #define ADCD 0 #define ADRTBL 1 #define BACK1 2 #define BCLRA 3 #define BCLRB 4 #define BSETA 5 #define BSETB 6 #define DECD 7 #define DECS 8 #define DECU 9 #define DECX 10 #define DECY 11 #define DOWN 12 #define ELSE 13 #define ENDIF 14 #define ENDWH 15 #define FPNUM 16 #define IF 17 #define IFCC 18 #define IFTST 19 #define INCD 20 #define INCS 21 #define INCU 22 #define INCX 23 #define INCY 24 #define IOP 25 #define LSHIFT 26 #define MOVA 27 #define MOVB 28 #define MOVD 29 #define MOVS 30 #define MOVU 31 #define MOVX 32 #define MOVY 33 #define MUL10 34 #define POP 35 #define PUSH 36 #define REGTST 37 #define RELCC 38 #define RELOP 39 #define RELTST 40 #define REPEAT 41 #define RIGHT1 42 #define RSHIFT 43 #define SBCD 44 #define SLD 45 #define SRD 46 #define UNTIL 47 #define UP 48 #define WHILE 49 #define XPLUSY 50 #define XSBTRY 51 #define DCALL 52 #define MCALL 53 #define DECBIN 54 #define BINDEC 55 char *macro[] = { "ADCD","ADRTBL","BACK1","BCLRA","BCLRB","BSETA","BSETB","DECD", "DECS","DECU","DECX","DECY","DOWN","ELSE","ENDIF","ENDWH", "FPNUM","IF","IFCC","IFTST","INCD","INCS","INCU","INCX","INCY", "IOP","LSHIFT","MOVA","MOVB","MOVD","MOVS","MOVU","MOVX","MOVY", "MUL10","POP","PUSH","REGTST","RELCC","RELOP","RELTST", "REPEAT","RIGHT1","RSHIFT","SBCD","SLD","SRD","UNTIL","UP", "WHILE","XPLUSY","XSBTRY","DCALL", "MCALL", "DECBIN", "BINDEC", NULL }; int argcnt[] = { //"ADCD","ADRTBL","BACK1","BCLRA","BCLRB","BSETA","BSETB","DECD", // ADRTBL can take 1 to 5 args. Others may be similarly optional. 1, 5, 0, 2, 2, 2, 2, 0, //"DECS","DECU","DECX","DECY","DOWN","ELSE","ENDIF","ENDWH", 0, 0, 0, 0, 1, 0, 0, 0, //"FPNUM","IF","IFCC","IFTST","INCD","INCS","INCU","INCX","INCY", 13, 3, 1, 3, 0, 0, 0, 0, 0, //"IOP","LSHIFT","MOVA","MOVB","MOVD","MOVS","MOVU","MOVX","MOVY", 1, 3, 2, 2, 2, 2, 2, 2, 2, //"MUL10","POP","PUSH","REGTST","RELCC","RELOP","RELTST", // not sure why MUL10 body seems to check for 2 args - only uses 1. 2, 0, 1, 1, 1, 1, 1, //"REPEAT","RIGHT1","RSHIFT","SBCD","SLD","SRD","UNTIL","UP", 0, 0, 3, 1, 1, 1, 3, 1, //"WHILE","XPLUSY","XSBTRY","DCALL", "MCALL", "DECBIN", "BINDEC", 3, 0, 0, 4, 3, 2, 3, -1 }; static int c; // lookahead character throughout code. Bit hacky, but it was a 1hr program ... int fixgetc(FILE *f) { int c = fgetc(f); if (c == '\r') c = fgetc(f); return c; } void put_string(char *s) { fprintf(outfile, "%s", s); } void drain(void) { while (c != '\n') { fputc(c, outfile); c = fixgetc(infile); } fputc(c, outfile); } static char lab[128]; void parse_label(int lookahead) { char *s = lab; c = lookahead; do { *s++ = c; c = fixgetc(infile); } while ((c != '\n') && (!isblank(c))); *s = '\0'; } static char opcode[128]; void parse_opcode(int lookahead) { char *s = opcode; c = lookahead; do { *s++ = c; c = fixgetc(infile); } while ((c != '\n') && (!isblank(c))); *s = '\0'; } int ismacro(char *s, int *argc, int *macidx) { int i = 0; while (macro[i]) { *argc = argcnt[i]; *macidx = i; if (strcasecmp(s, macro[i]) == 0) return TRUE; i = i + 1; } *argc = -1; return FALSE; } #define MAX_NEST 16 #define MAX_OPDS 16 static char operand[MAX_NEST][MAX_OPDS][128]; static int labelstack[MAX_NEST]; static int next_nest = 0; static int next_lab = 1000; void parse_operand(int lookahead, int idx) { int balance; char *s = &operand[next_nest][idx][0]; balance = 0; if (lookahead != ',') *s++ = lookahead; if ((lookahead == '(') || (lookahead == '[')) balance = balance + 1; for (;;) { c = fixgetc(infile); if ((c == '(') || (c == '[')) balance = balance + 1; if ((c == ')') || (c == ']')) balance = balance - 1; if ((c == ',') && (balance == 0)) break; if (isblank(c) || (c == '\n')) break; *s++ = c; } *s = '\0'; } char *invert(char *cc) { char *tst[] = { "EQ", "NE", "LT", "LE", "GT", "GE", "CC", "CS", "VC", "VS", NULL}; // see manual for rest HI LO etc char *inv[] = { "NE", "EQ", "GE", "LT", "LE", "LT", "CS", "CC", "VS", "VC", NULL}; int i = 0; while (tst[i]) { if (strcasecmp(cc, tst[i]) == 0) { return inv[i]; // might want to match case later... } i += 1; } fprintf(stderr, "Unknown CC: '%s'\n", cc); return cc; // or exit??? } int expected; void expand(char *opcode, int operands, int op) { int i, base_lab; char *opd[MAX_OPDS+1]; if (verbose) { fprintf(outfile, "\n* %s", opcode); for (i = 1; i <= operands; i++) { fprintf(outfile, " [%s]", operand[next_nest][i]); } fprintf(outfile, "\n"); } strcpy(operand[next_nest][0], opcode); switch (op) { case IF: opd[0] = "IF"; opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; labelstack[next_nest++] = base_lab = next_lab; next_lab += 1; fprintf(outfile, " CMP%s %s\n", opd[1], opd[3]); fprintf(outfile, " B%s endif%04d\n", invert(opd[2]), base_lab); return; case IFCC: opd[0] = "IFCC"; opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; labelstack[next_nest++] = base_lab = next_lab; next_lab += 1; fprintf(outfile, " B%s endif%04d\n", invert(opd[1]), base_lab); return; case IFTST: opd[0] = "IF"; opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; labelstack[next_nest++] = base_lab = next_lab; next_lab += 1; fprintf(outfile, " TST %s\n", opd[1]); fprintf(outfile, " B%s endif%04d\n", invert(opd[2]), base_lab); /* ********************************************************************** * * IFTST -- * THE 'IFTST' MACRO OPERATES LIKE AN 'IF' MACRO EXCEPT * THAT IT GENERATES A 'TST' INSTRUCTION INSTEAD OF A 'CMP'. * THE SYNTAX IS: * * IFTST <REGISTER OR ADDRESS EXPRESSION>,<RELATIONAL OP>,0 * * THE VALID RELATIONAL OPERATORS FOR USE WITH 'IFTST' ARE: 'EQ', * 'NE', 'LT', AND 'GE'. * */ return; case ELSE: if (strcmp(operand[next_nest-1][0], "IF") == 0) { strcpy(operand[next_nest-1][0], "IF;ELSE"); } else if (strcmp(operand[next_nest-1][0], "IFCC") == 0) { strcpy(operand[next_nest-1][0], "IFCC;ELSE"); } else { // coding error } opd[1] = operand[next_nest-1][1]; opd[2] = operand[next_nest-1][2]; opd[3] = operand[next_nest-1][3]; base_lab = labelstack[next_nest - 1]; fprintf(outfile, " B endelse%04d\n", base_lab); fprintf(outfile, "endif%04d\n", base_lab); return; case ENDIF: base_lab = labelstack[--next_nest]; opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; if (strcmp(operand[next_nest][0], "IF") == 0) { fprintf(outfile, "\nendif%04d\n", base_lab); } else if (strcmp(operand[next_nest][0], "IFCC") == 0) { fprintf(outfile, "\nendif%04d\n", base_lab); } else if (strcmp(operand[next_nest][0], "IFTST") == 0) { fprintf(outfile, "\nendif%04d\n", base_lab); } else if (strcmp(operand[next_nest][0], "IF;ELSE") == 0) { fprintf(outfile, "\nendelse%04d\n", base_lab); } else if (strcmp(operand[next_nest][0], "IFCC;ELSE") == 0) { fprintf(outfile, "\nendelse%04d\n", base_lab); } else { // coding error fprintf(stderr, "ENDIF: '%s'\n", operand[next_nest][0]); exit(1); } return; case WHILE: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; labelstack[next_nest++] = base_lab = next_lab; next_lab += 1; fprintf(outfile, "\nwhile%04d\n", base_lab); fprintf(outfile, " CMP%s %s\n", opd[1], opd[3]); fprintf(outfile, " B%s endwhile%04d\n", invert(opd[2]), base_lab); return; case ENDWH: base_lab = labelstack[--next_nest]; opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; fprintf(outfile, " BRA while%04d\n", base_lab); fprintf(outfile, "endwhile%04d\n", base_lab); return; case REPEAT: labelstack[next_nest++] = base_lab = next_lab; next_lab += 1; fprintf(outfile, "\nuntil%04d\n", base_lab); return; case UNTIL: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; opd[3] = operand[next_nest][3]; base_lab = labelstack[--next_nest]; fprintf(outfile, " CMP%s %s\n", opd[1], opd[3]); fprintf(outfile, " B%s until%04d\n", invert(opd[2]), base_lab); return; case RIGHT1: fprintf(outfile, " LSRA\n"); fprintf(outfile, " RORB\n"); fprintf(outfile, " ROR 3,X\n"); fprintf(outfile, " ROR 4,X\n"); fprintf(outfile, " ROR 5,X\n"); fprintf(outfile, " ROR 6,X\n"); fprintf(outfile, " ROR 7,X\n"); return; case RSHIFT: opd[1] = operand[next_nest][1]; // offset opd[2] = operand[next_nest][2]; // x y u opd[3] = operand[next_nest][3]; // length labelstack[next_nest] = base_lab = next_lab++; { int rc, i, iLength = 0; rc = sscanf(opd[3], "%d", &iLength); if (rc != 1) { fprintf(stderr, "expand: need a literal number in RSHIFT parameter '%s'\n", opd[3]); exit(EXIT_FAILURE); } if ((iLength < 1) || (iLength > 10)) { fprintf(stderr, "expand: RSHIFT parameter must be between 1 and 10\n"); exit(EXIT_FAILURE); } for (i = 0; i < iLength; i++) { fprintf(outfile, " ROR (%s+%s-%d),%s\n", opd[1],opd[3],i,opd[2]); } } /* * * * THIS MACRO DOES A RIGHT SHIFT ON A MULTI- * PRECISION OPERAND THAT IS UP TO 10 BYTES * LONG, WHOSE MOST SIG. BYTE IS POINTED TO * BY EITHER 0FF,X , OFF,Y , OR OFF,U; WHERE * "OFF" IS A CONSTANT OFFSET SPECIFIED UPON * INVOCATION OF THE MACRO. THE CARRY IS SHIFTED * IN FROM THE RIGHT. * * * TO INVOKE RSHIFT: * * RSHIFT <"OFF">,< X, Y OR U >,< BYTE LENGTH > */ return; case SBCD: opd[1] = operand[next_nest][1]; fprintf(outfile, " SBCB 1+%s\n", opd[1]); fprintf(outfile, " SBCA %s\n", opd[1]); return; case ADCD: opd[1] = operand[next_nest][1]; fprintf(outfile, " ADCB 1+%s\n", opd[1]); fprintf(outfile, " ADCA %s\n", opd[1]); return; case ADRTBL: { int i; for (i = 1; i <= expected; i++) opd[i] = operand[next_nest][i]; fprintf(outfile, " FDB "); for (i = 1; i <= expected; i++) { fprintf(outfile, "%s-ROMSTR", opd[i]); if (i < expected) fprintf(outfile, ","); } fprintf(outfile, "\n"); } return; case BACK1: fprintf(stderr, "expand: missing macro expansion for BACK1\n"); return; case BCLRA: // looks like 'bit clear' - remove any bits in param from reg opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDA %s\n", opd[2]); fprintf(outfile, " ANDA #(-(%s)-1)!.$FF\n", opd[1]); // this syntax for complementing a constant is probably not portable fprintf(outfile, " STA %s\n", opd[2]); // so may need to be changed to whatever assembler ends up being used. /* LDA \1 ANDA #(-(\0)-1)!.$FF STA \1 */ return; case BCLRB: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDB %s\n", opd[2]); fprintf(outfile, " ANDB #(-(%s)-1)!.$FF\n", opd[1]); fprintf(outfile, " STB %s\n", opd[2]); /* LDB \1 ANDB ((-(\0)-1)!.$FF STB \1 */ return; case BSETA: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDA %s\n", opd[2]); fprintf(outfile, " ORA #(%s)\n", opd[1]); fprintf(outfile, " STA %s\n", opd[2]); /* LDA \1 ORA #(\0) STA \1 */ return; case BSETB: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDB %s\n", opd[2]); fprintf(outfile, " ORB #(%s)\n", opd[1]); fprintf(outfile, " STB %s\n", opd[2]); /* LDB \1 ORB #(\0) STB \1 */ return; case DECD: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " TSTB\n"); fprintf(outfile, " BNE skip%04d\n", base_lab); fprintf(outfile, " DECA\n"); fprintf(outfile, "skip%04d\n", base_lab); fprintf(outfile, " DECB\n"); /* TSTB BNE \.1 DECA \.1 DECB */ return; case DECS: fprintf(outfile, " LEAS -1,S\n"); /* LEAS -1,S */ return; case DECU: fprintf(outfile, " LEAU -1,U\n"); /* LEAU -1,U */ return; case DECX: fprintf(outfile, " LEAX -1,X\n"); /* LEAX -1,X */ return; case DECY: fprintf(outfile, " LEAY -1,Y\n"); /* LEAY -1,Y */ return; case DOWN: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDB %s\n", opd[1]); fprintf(outfile, " LBSR DWNSUB\n"); /* LDB #\0 LBSR DWNSUB */ return; case FPNUM: { int i; for (i = 1; i <= expected; i++) opd[i] = operand[next_nest][i]; fprintf(outfile, " FCB "); for (i = 1; i <= expected; i++) { fprintf(outfile, "$%s", opd[i]); if (i < expected) fprintf(outfile, ","); } fprintf(outfile, "\n"); } /* FCB $\0,$\1,$\2,$\3,$\4,$\5,$\6,$\7,$\8,$\9,$\A,$\B,$\C */ return; case INCD: labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " INCB\n"); fprintf(outfile, " BNE skip%04d\n", base_lab); fprintf(outfile, " INCA\n"); fprintf(outfile, "skip%04d\n", base_lab); /* INCB BNE *+3 INCA */ return; case INCS: fprintf(outfile, " LEAS 1,S\n"); /* LEAS 1,S */ return; case INCU: fprintf(outfile, " LEAU 1,U\n"); /* LEAU 1,U */ return; case INCX: fprintf(outfile, " LEAX 1,X\n"); /* LEAX 1,X */ return; case INCY: fprintf(outfile, " LEAY 1,Y\n"); /* LEAY 1,Y */ return; case IOP: opd[1] = operand[next_nest][1]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDA #(%s)\n", opd[1]); fprintf(outfile, " LBSR IOPSUB SET IOP CODE,IOP BIT & RETURN A NAN\n"); /* * CHECK FOR ILLEGAL CASES IFLT (\0)-1 FAIL *** IOP N; N > 0 *** ENDC IFGT (\0)-BIGIOP FAIL *** IOP N; N TOO BIG *** ENDC * NOT ILLEGAL LDA #(\0) LBSR IOPSUB SET IOP CODE,IOP BIT & RETURN A NAN */ return; case LSHIFT: opd[1] = operand[next_nest][1]; // offset opd[2] = operand[next_nest][2]; // x y u opd[3] = operand[next_nest][3]; // length labelstack[next_nest] = base_lab = next_lab++; { int rc, i, iLength; rc = sscanf(opd[3], "%d", &iLength); if (rc != 1) { fprintf(stderr, "expand: need a literal number in LSHIFT parameter '%s'\n", opd[3]); exit(EXIT_FAILURE); } if ((iLength < 1) || (iLength > 10)) { fprintf(stderr, "expand: LSHIFT parameter must be between 1 and 10\n"); exit(EXIT_FAILURE); } for (i = 0; i < iLength; i++) { fprintf(outfile, " ROL (%s+%s-%d),%s\n", opd[1],opd[3],i,opd[2]); } } /* * * * THIS MACRO DOES A LEFT SHIFT ON A MULTI- * PRECISION OPERAND THAT IS UP TO 10 BYTES * LONG, WHOSE MOST SIG. BYTE IS POINTED TO * BY EITHER 0FF,X , OFF,Y , OR OFF,U; WHERE * "OFF" IS A CONSTANT OFFSET SPECIFIED UPON * INVOCATION OF THE MACRO. THE CARRY IS SHIFTED * IN FROM THE LEFT. * * TO INVOKE LSHIFT: * * LSHIFT <"0FF">,< X,Y OR U >,< BYTE LENGTH > */ return; case MOVA: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDA %s\n", opd[1]); fprintf(outfile, " STA %s\n", opd[2]); /* LDA \0 STA \1 */ return; case MOVB: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDB %s\n", opd[1]); fprintf(outfile, " STB %s\n", opd[2]); /* LDB \0 STB \1 */ return; case MOVD: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDD %s\n", opd[1]); fprintf(outfile, " STD %s\n", opd[2]); /* LDD \0 STD \1 */ return; case MOVS: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDS %s\n", opd[1]); fprintf(outfile, " STS %s\n", opd[2]); /* LDS \0 STS \1 */ return; case MOVU: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDU %s\n", opd[1]); fprintf(outfile, " STU %s\n", opd[2]); /* LDU \0 STU \1 */ return; case MOVX: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDX %s\n", opd[1]); fprintf(outfile, " STX %s\n", opd[2]); /* LDX \0 STX \1 */ return; case MOVY: opd[1] = operand[next_nest][1]; opd[2] = operand[next_nest][2]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDY %s\n", opd[1]); fprintf(outfile, " STY %s\n", opd[2]); /* LDY \0 STY \1 */ return; case MUL10: opd[1] = operand[next_nest][1]; // "X" opd[2] = operand[next_nest][2]; // byte length of operand labelstack[next_nest] = base_lab = next_lab; next_lab += 3; /* **************************************************** * * MUL10 * * MUL10 TAKES A MULTI- PRECISION BINARY INTEGER * AND MULTIPLIES IT BY 10. THIS OPERATION IS USEFULL * FOR PERFORMING DECIMAL TO BINARY CONVERSIONS. UPON * INVOCATION REG. X MUST POINT TO THE MSBYTE OF THE * MULTI-BYTE ARGUMENT. * * ON ENTRY: X - POINTS TO THE MSBYTE OF THE INPUT * OPERAND. * * ON EXIT: THE INPUT ARGUMENT IS MULTIPLIED BY 10 * * X IS UNCHANGED; A & B DESTROYED * * TO INVOKE MUL10: * * MUL10 X,< BYTE LENGTH OF OPERAND > * * CHECK FOR PROPER NO. OF ARGUMENTS * IFNE NARG-2 FAIL ** TOO MANY OR TOO FEW INPUT ARGUMENTS ** EXIT ENDC * * CREATE A TEMPORARY A TEMPORARY ACCUMULATOR ON THE STACK * */ fprintf(outfile, " LEAS -%s,S\n", opd[2]); fprintf(outfile, "* MULTIPLY INPUT BY 2 I.E. LEFT SHIFT ONCE\n"); fprintf(outfile, " ANDCC #NC CLEAR CARRY\n"); //fprintf(outfile, " LSHIFT 0,X,(%s) LEFT SHIFT\n", opd[2]); { int rc, i, iLength; rc = sscanf(opd[2], "%d", &iLength); if (rc != 1) { fprintf(stderr, "expand: need a literal number in MUL10:LSHIFT parameter '%s'\n", opd[2]); exit(EXIT_FAILURE); } for (i = 0; i < iLength; i++) { fprintf(outfile, " ROL (%s+(%s)-%d),%s\n", "0",opd[2],i,"X"); // WARNING: parameter in .sa file is "EXPLEN" or "ACCLEN" } // rather than a literal number. I have changed my copy } // to 2 and 8 so that these expansions work. fprintf(outfile, "* COPY ARG*2 TO A TEMPORARY ACCUMULATOR\n"); fprintf(outfile, "\n"); fprintf(outfile, " LDB #(%s) ARGUMENT SIZE\n", opd[2]); // WHILE B,GT,#0 fprintf(outfile, "\nwhile%04d\n", base_lab); fprintf(outfile, " CMP%s %s\n", "B", "#0"); fprintf(outfile, " B%s endwhile%04d\n", invert("GT"), base_lab); fprintf(outfile, " DECB\n"); fprintf(outfile, " LDA B,X SOURCE\n"); fprintf(outfile, " STA B,S DESTINATION\n"); // ENDWH fprintf(outfile, " BRA while%04d\n", base_lab); fprintf(outfile, "endwhile%04d\n", base_lab); fprintf(outfile, "* LEFT SHIFT ARGUMENT TWICE MORE TO YIELD 8*\n"); fprintf(outfile, "* INPUT ARGUMENT.\n"); fprintf(outfile, " LDB #2\n"); // WHILE B,GT,#0 fprintf(outfile, "\nwhile%04d\n", base_lab+1); fprintf(outfile, " CMP%s %s\n", "B", "#0"); fprintf(outfile, " B%s endwhile%04d\n", invert("GT"), base_lab+1); fprintf(outfile, " ANDCC #NC CLEAR CARRY\n"); //fprintf(outfile, " LSHIFT 0,X,(%s) LEFT SHIFT\n", opd[2]); { int rc, i, iLength; rc = sscanf(opd[2], "%d", &iLength); if (rc != 1) { fprintf(stderr, "expand: need a literal number in MUL10:LSHIFT parameter '%s'\n", opd[2]); exit(EXIT_FAILURE); } for (i = 0; i < iLength; i++) { fprintf(outfile, " ROL (%s+(%s)-%d),%s\n", "0",opd[2],i,"X"); } } fprintf(outfile, " DECB DECREMENT COUNTER\n"); // ENDWH fprintf(outfile, " BRA while%04d\n", base_lab+1); fprintf(outfile, "endwhile%04d\n", base_lab+1); fprintf(outfile, "* NOW ADD 8*INPUT TO 2*INPUT TO YIELD 10*INPUT\n"); fprintf(outfile, " LDB #(%s) ARGUMENT LENGTH\n", opd[2]); fprintf(outfile, " DECB CONVERT LENGTH TO OFFSET\n"); fprintf(outfile, " CLRA\n"); // WHILE B,GE,#0 fprintf(outfile, "\nwhile%04d\n", base_lab+2); fprintf(outfile, " CMP%s %s\n", "B", "#0"); fprintf(outfile, " B%s endwhile%04d\n", invert("GE"), base_lab+2); fprintf(outfile, " RORA RESTORE CARRY\n"); fprintf(outfile, " LDA B,X GET ARG1\n"); fprintf(outfile, " ADCA B,S ADD IN ARG2\n"); fprintf(outfile, " STA B,X SAVE RESULT\n"); fprintf(outfile, " ROLA SAVE CARRY IN A\n"); fprintf(outfile, " DECB DECREMENT INDEX\n"); // ENDWH fprintf(outfile, " BRA while%04d\n", base_lab+2); fprintf(outfile, "endwhile%04d\n", base_lab+2); fprintf(outfile, " LEAS %s,S CLEAN UP STACK\n", opd[2]); return; case POP: fprintf(stderr, "expand: missing macro expansion for POP\n"); return; case PUSH: fprintf(stderr, "expand: missing macro expansion for PUSH\n"); return; case REGTST: fprintf(stderr, "expand: missing macro expansion for REGTST\n"); return; case RELCC: fprintf(stderr, "expand: missing macro expansion for RELCC\n"); return; case RELOP: fprintf(stderr, "expand: missing macro expansion for RELOP\n"); return; case RELTST: fprintf(stderr, "expand: missing macro expansion for RELTST\n"); return; case SLD: opd[1] = operand[next_nest][1]; labelstack[next_nest] = base_lab = next_lab++; { int rc, i, iN; rc = sscanf(opd[1], "%d", &iN); if (rc != 1) { fprintf(stderr, "expand: need a literal number in SLD parameter '%s'\n", opd[1]); exit(EXIT_FAILURE); } if ((iN < 0) || (iN > 8)) { fprintf(stderr, "expand: SLD parameter must be between 0 and 8\n"); exit(EXIT_FAILURE); } for (i = 0; i < iN; i++) { fprintf(outfile, " LSLB\n"); fprintf(outfile, " ROLA\n"); } } return; case SRD: opd[1] = operand[next_nest][1]; labelstack[next_nest] = base_lab = next_lab++; { int rc, i, iN; rc = sscanf(opd[1], "%d", &iN); if (rc != 1) { fprintf(stderr, "expand: need a literal number in SRD parameter '%s'\n", opd[1]); exit(EXIT_FAILURE); } if ((iN < 0) || (iN > 8)) { fprintf(stderr, "expand: SRD parameter must be between 0 and 8\n"); exit(EXIT_FAILURE); } for (i = 0; i < iN; i++) { fprintf(outfile, " LSRA\n"); fprintf(outfile, " RORB\n"); } } return; case UP: opd[1] = operand[next_nest][1]; labelstack[next_nest] = base_lab = next_lab++; fprintf(outfile, " LDB #%s\n", opd[1]); fprintf(outfile, " LBRA DO_UP\n"); return; case XPLUSY: fprintf(outfile, " LDD 7,X\n"); fprintf(outfile, " ADDD 7,Y\n"); fprintf(outfile, " STD 7,X\n"); fprintf(outfile, " LDD 5,X\n"); fprintf(outfile, " ADCD (5,Y)\n"); fprintf(outfile, " STD 5,X\n"); fprintf(outfile, " LDD 3,X\n"); fprintf(outfile, " ADCD (3,Y)\n"); fprintf(outfile, " STD 3,X\n"); fprintf(outfile, " LDD 1,X\n"); fprintf(outfile, " ADCD (1,Y)\n"); fprintf(outfile, " STD 1,X\n"); fprintf(outfile, " LDB 0,X\n"); fprintf(outfile, " ADCB 0,Y\n"); fprintf(outfile, " STB 0,X\n"); return; case XSBTRY: fprintf(outfile, " LDD 7,X\n"); fprintf(outfile, " SUBD 7,Y\n"); fprintf(outfile, " STD 7,X\n"); fprintf(outfile, " LDD 5,X\n"); fprintf(outfile, " SBCD (5,Y)\n"); fprintf(outfile, " STD 5,X\n"); fprintf(outfile, " LDD 3,X\n"); fprintf(outfile, " SBCD (3,Y)\n"); fprintf(outfile, " STD 3,X\n"); fprintf(outfile, " LDD 1,X\n"); fprintf(outfile, " SBCD (1,Y)\n"); fprintf(outfile, " STD 1,X\n"); fprintf(outfile, " LDB 0,X\n"); fprintf(outfile, " SBCB 0,Y\n"); fprintf(outfile, " STB 0,X\n"); return; // // HERE ARE THE CALLING SEQUENCE MACROS for quad test // case MCALL: opd[1] = operand[next_nest][1]; // opd[2] = operand[next_nest][2]; // opd[3] = operand[next_nest][3]; // labelstack[next_nest] = base_lab = next_lab++; // // MCALL SETS UP A MONADIC REGISTER CALL. // // USAGE: MCALL <INPUT OPERAND>,<OPERATION>,<RESULT> // fprintf(outfile, " LEAY %s,PCR POINTER TO THE INPUT ARGUMENT\n", opd[1]); fprintf(outfile, " LEAX FPCB,PCR POINTER TO THE FLOATING POINT CONTROL BLOCK\n"); fprintf(outfile, " TFR X,D\n"); fprintf(outfile, " LEAX %s,PCR POINTER TO THE RESULT\n", opd[3]); fprintf(outfile, " LBSR FPREG CALL TO THE MC6839\n"); fprintf(outfile, " FCB %s OPCODE\n", opd[2]); return; case DCALL: opd[1] = operand[next_nest][1]; // opd[2] = operand[next_nest][2]; // opd[3] = operand[next_nest][3]; // opd[4] = operand[next_nest][4]; // labelstack[next_nest] = base_lab = next_lab++; // // DCALL SETS UP A DYADIC REGISTER CALL // // USAGE: DCALL <ARGUMENT #1>,<OPERATION>,<ARGUMENT #2>,<RESULT> // fprintf(outfile, " LEAU %s,PCR POINTER TO ARGUMENT #1\n", opd[1]); fprintf(outfile, " LEAY %s,PCR POINTER TO ARGUMENT #1\n", opd[3]); fprintf(outfile, " LEAX FPCB,PCR POINTER TO THE FLOATING POINT CONTROL BLOCK\n"); fprintf(outfile, " TFR X,D\n"); fprintf(outfile, " LEAX %s,PCR POINTER TO THE RESULT\n", opd[4]); fprintf(outfile, " LBSR FPREG CALL TO THE MC6839\n"); fprintf(outfile, " FCB %s OPCODE\n", opd[2]); return; case DECBIN: opd[1] = operand[next_nest][1]; // opd[2] = operand[next_nest][2]; // // opd[3] = operand[next_nest][3]; // labelstack[next_nest] = base_lab = next_lab++; // // DECBIN SETS UP A REGISTER CALL TO THE DECIMAL TO BINARY CONVERSION FUNCTION. // // USAGE: DECBIN <BCD STRING>,<BINARY RESULT> // fprintf(outfile, " LEAU %s,PCR POINTER TO THE BCD INPUT STRING\n", opd[1]); fprintf(outfile, " LEAX FPCB,PCR POINTER TO THE FLOATING POINT CONTROL BLOCK\n"); fprintf(outfile, " TFR X,D\n"); fprintf(outfile, " LEAX %s,PCR POINTER TO THE RESULT\n", opd[2]); fprintf(outfile, " LBSR FPREG CALL TO THE MC6839\n"); fprintf(outfile, " FCB DCBN OPCODE\n"); return; case BINDEC: opd[1] = operand[next_nest][1]; // opd[2] = operand[next_nest][2]; // opd[3] = operand[next_nest][3]; // labelstack[next_nest] = base_lab = next_lab++; // // BINDEC SETS UP A REGISTER CALL TO THE BINARY TO DECIMAL CONVERSION FUNCTION. // // USAGE: BINDEC <BINARY INPUT>,<BCD RESULT>,<# OF SIGNIFICANT DIGITS RESULT> // fprintf(outfile, " LDU %s # OF SIGNIFICANT DIGITS IN THE RESULT\n", opd[3]); fprintf(outfile, " LEAY %s,PCR POINTER TO THE BINARY INPUT\n", opd[1]); fprintf(outfile, " LEAX FPCB,PCR POINTER TO THE FLOATING POINT CONTROL BLOCK\n"); fprintf(outfile, " TFR X,D\n"); fprintf(outfile, " LEAX %s,PCR POINTER TO THE BCD RESULT\n", opd[2]); fprintf(outfile, " LBSR FPREG CALL TO THE MC6839\n"); fprintf(outfile, " FCB BNDC OPCODE\n"); return; default: // coding error return; } // end switch } int handle_line(int allow_expand) { expected = 0; while (isblank(c) && (c != '\n')) { fputc(c, outfile); c = fixgetc(infile); } if (c != '\n') { int MACNAME, i; if (c == '*') { drain(); return TRUE; } parse_opcode(c); while (isblank(c) && (c != '\n')) c = fixgetc(infile); if (allow_expand && ismacro(opcode, &expected, &MACNAME)) { i = 0; // no operands if (c != '\n') { for (i = 1; i <= expected; i++) { // initially no spaces in parameters, later allow spaces after ',' parse_operand(c, i); // $1 $2 etc in expansion. We'll skip 0 just to make coding simpler. if ((c != ',') && (i < expected)) break; // expected is a maximum, there may be fewer given... } if (i < expected) expected = i; // in case it was short } expand(opcode, expected, MACNAME); if (c != '\n') { fprintf(outfile, "\n* "); drain(); } else fputc(c, outfile); } else if (strcasecmp(opcode, "ENDM") == 0) { return FALSE; } else if (strncmp(opcode, "MACR", 4) == 0) { fflush(outfile); fseek(outfile, start_of_line, SEEK_SET); fflush(outfile); fprintf(outfile, "* Macro body %s removed.\n", lab); /* We want to completely skip the definition of the macro body, since we're defining the macros interally. Unfortunately the name of the macro has already been processed as a label, preceding the MACR keyword. So to get rid of that from the output file, we do a rather dirty hack, and rewind the output stream so that we can overwrite it. This is why I had to change the program to use files in the command line rather than writing to stdout, which can't be rewound as it is a stream, not a file. */ outfile = nullout; for (;;) { c = fixgetc(infile); // always at start of line if ((c == EOF) || (c == 26)) break; if (c == '\n') { fputc(c, outfile); continue; } else if (!isblank(c)) { // label or comment if (c == '*') { // comment drain(); continue; } else { parse_label(c); put_string(lab); if (c == '\n') continue; } } if (!handle_line(FALSE)) break; // exit on ENDM } outfile = actual_outfile; } else { fputc(' ', outfile); put_string(opcode); fputc(' ', outfile); drain(); } } else fputc(c, outfile); return TRUE; } int main(int argc, char **argv) { static char *outfilename; // for reasons explained elsewhere, I had to make the input file a parameter rather than // use redirection for the inputs and outputs. If the input file ends in .sa, the // extension will be stripped. ".asm" as always added. if (argc != 2) { fprintf(stderr, "syntax: expand filename.sa\n"); exit(EXIT_FAILURE); } infile = fopen(argv[1], "r"); if (infile == NULL) { fprintf(stderr, "expand: cannot open input %s - %s\n", argv[1], strerror(errno)); exit(errno); } outfilename = malloc(strlen(argv[1])+4); strcpy(outfilename, argv[1]); if (strlen(outfilename) >= 3) { if (strcasecmp(outfilename+strlen(outfilename)-3, ".sa") == 0) outfilename[strlen(outfilename)-3] = '\0'; } strcat(outfilename, ".asm"); actual_outfile = outfile = fopen(outfilename, "w"); if (outfile == NULL) { fprintf(stderr, "expand: cannot open output %s - %s\n", argv[1], strerror(errno)); exit(errno); } nullout = fopen("/dev/null", "w"); if (nullout == NULL) nullout = fopen("null.out", "w"); fprintf(stderr, "Writing output to %s\n", outfilename); for (;;) { fflush(outfile); start_of_line = ftell(outfile); c = fixgetc(infile); // always at start of line if ((c == EOF) || (c == 26)) break; if (c == '\n') { fputc(c, outfile); continue; } else if (!isblank(c)) { // label or comment if (c == '*') { // comment drain(); continue; } else { parse_label(c); put_string(lab); if (c == '\n') continue; } } handle_line(TRUE); } fclose(nullout); fflush(stdout); exit(EXIT_SUCCESS); }