V10/cmd/spitbol/xfervax.spt
-IN80
-TITLE RA-R2 CP-R3 IA2-R4 IA-R5 WA-R6 WB-R7 WC-R8 XR-R9 XL/XT-R10 SR-R11 XS-SP
-STITL REVISION HISTORY
*
* 09-MAR-82 (SGD):
* CHK is back - better than ever...
* Copy .INI file to start of .MAR file
* 10-SEP-81 (SGD):
* Read in problem label table from filename.PLB
* 01-AUG-81 (SGD):
* Better encoding of CTB,CTW,BTW and WTB
* 13-MAY-81 (SGD):
* Added logic in XOP to put a longword type displacement on displacement
* operands as VMS assembler assumes a word displacement. (Phhhhaaa - ed)
* 15-MAR-81 (SGD):
* Made CHK a comment, as now handled as exception.
* 10-SEP-80 (SGD):
* Made revisions to make XFER reflect VAX SBL capabilities. Note that
* this version of XFER is no longer capable of running on PDP-11, since
* it will be too big. [Major change was to substitute tables for LOOKUP
* strings, and corresponding addition of TINIT].
* 02-AUG-80 (SGD):
* Altered translation of conditional branches to emit conditional
* branch directly. Those which cause problems are listed in a
* table that inhibits the direct translation.
* 18-MAY-80 (SGD):
* Fixed translation of vertical tab (CH$VT) from ASCII 011 to ASCII 012
* to conform to SOS Editor standard.
*
-STITL INTRODUCTION
* < XFER >
* MINIMAL To VAX 11/780 Translator
* ________________________________
*
* Coded by:
* Steven G. Duff
* 1345-M16 Cabrillo Park Dr.
* Santa Ana, California 96701
* (714) 541-9619
*
* This is a Macro Spitbol program for translation of the Macro
* Spitbol Minimal Source to Vax 11/780 Macro Source. The program
* is fairly simple in order that it be able to run in the limited
* address space of a PDP-11. As a consequence, certain of the
* Minimal Opcodes are left untranslated, to be expanded as macros
* at assembly time. These macros are:
*
* AOV, BSW, CMC, CVD, CVM, ESW,
* IFF, LSX, MCB, MFI, MVC, MVW, MWB, RMI, SEC, TRC.
*
* There are two principal parts of Minimal that make translation
* tricky vis-a-vis VAX MACRO-32:
*
*
* Tricky #1: BSW,IFF and ESW normally require buffering, sorting
* etc. These can (and are) handled by macros though. BSW
* emits a CASEL and a word table containing the default
* value. IFF re-biases the location counter during assembly
* and overwrites the default word. ESW resets the location
* counter back. XFER is not capable of handling the needed
* computation without more working store.
*
* Tricky #2: Minimal operands of the form DLBL(X) must be translated
* to 4*DLBL(X) as called for by the language spec. This
* requires that XFER be able to distinguish DLBLs from other
* stuff. This in turn means that a record of all EQU labels
* must be kept. This eats up a lot of working store, but
* can't be helped.
*
* The other macro-ops are simple enough, and are omitted purely to
* avoid using up dynamic unnecessarily.
-EJECT
* This program works with four files, with the same name, and
* different extensions. Name.MIN is used as the source input
* file. Name.MAR is created and becomes the source output file.
* Name.ERR is created, and is where the ERR and ERB messages and
* numbers are written. Name.INI is a prefix (MACRO32) file
* that is read as input and copied to Name.MAR before translation
* begins.
*
* The VAX assembler does not permit an equate to a register symbol
* for the purpose of creating symbolic register names.
* Thus this translator maps registers from Minimal to Vax thusly:
*
* RA <=> R2
* CP <=> R3
* IA <=> R5
* WA <=> R6
* WB <=> R7
* WC <=> R8
* XR <=> R9
* XL <=> R10 (XT ALSO)
* XS <=> SP (R14)
*
* Additionally, there are two other 'phantom' registers assumed
* by the translator - a scratch register (SR) which is used by
* a few instructions and by some of the character macros for
* holding temps, and EXI for returns. For remaindering,
* a register (IA2) is presumed to be available immediately below IA.
* The mappings of these registers are:
*
* IA2 <=> R4
* SR <=> R11
*
* Changes to this mapping are ill-advised and difficult.
*
*
* XFER aint got much smarts (it cant afford them). Except for cursory
* syntax and opcode checks, almost anything will get through, so it
* should be said that it expects valid Minimal Source. Simple errors
* are flagged on the listing (with a traceback) and a count given at
* the end, so they should not be too hard to find.
-STITL INITIALIZATION
* WARNING - POST NO LABELS!!!!!
* -----------------------------
* No labels should appear in this initializing code that would prevent
* the code from being garbage-collected by SPITBOL.
-SPACE 3
* Keyword initialization
*
&ANCHOR = 1; &TRIM = 1; &STLIMIT = -1
*
* Useful constants
*
MINLETS = 'ABCDEFGHIJKLMNOPQRSTUVWXY$'
NOS = '0123456789'
TAB = ' '
REGNAME = ('X' ANY('LSTR')) | ('W' ANY('ABC')) | 'IA' | 'RA' | 'CP'
LL = 5000 ;*USED FOR LOCAL LABEL GENERATION
*
* Zero the counts
*
LABCNT = NOUTLINES = NLINES = NSTMTS = NTARGET = NERRORS = 0
*
* Get file name
*
TERMINAL = 'Files are name.MIN for input, name.MAR (created) for'
TERMINAL = ' the MACRO-32 file, name.ERR (created) for'
TERMINAL = ' the messages from ERR, ERB ops., name.PLB (input)'
TERMINAL = ' for the problem labels. and name.INI for the'
TERMINAL = ' prefix file.'
TERMINAL =
TERMINAL = 'Enter File name sans extension (null for TT: testing)'
FILENAME = TERMINAL
TERMINAL =
TERMINAL = 'Do you want full-line comments passed to the output? [Y/N]'
FLCFLAG = TERMINAL
*
* No page ejects without full line comments
*
TERMINAL = DIFFER(FLCFLAG,'N')
TERMINAL = DIFFER(FLCFLAG,'N') 'Do you want EJC ops (Page Ejects)'
+ 'passed? [Y/N]'
EJCFLAG = (DIFFER(FLCFLAG,'N') TERMINAL, 'N')
-STITL XFER FUNCTIONS
* TINIT is used during initialization to take a string of the
* form "index1[value1]index2[value2]...indexn[valuen]" and
* stuff the index/value pairs into a table which it returns.
*
DEFINE('TINIT(STR)POS,CNT,INDEX,VAL,LASTVAL')
* CRACK parses STMT into a STMT data plex and returns it.
* It fails if there is a syntax error.
*
DEFINE('CRACK(LINE)LABEL,OPCODE,OPERANDS,COMMENT,OPERAND,CHAR')
*
* STMT is the common data plex used to hold the components of
* a statement (either Minimal or VAX) during processing.
*
DATA('STMT(LABEL,OPCODE,OP1,OP2,OP3,COMMENT)')
*
* MINLABEL is a pattern matching a valid Minimal Source Label.
*
MINLABEL = ANY(MINLETS) ANY(MINLETS) ANY(MINLETS NOS)
+ ANY(MINLETS NOS) ANY(MINLETS NOS)
*
* MINCOND is a pattern that matches Minimal Conditional assembly ops
*
MINCOND = 'IF' | 'THEN' | 'ELSE' | 'FI' | 'DEF' | 'UNDEF'
*
* CSPARSE parses out the components of the input line in STMT,
* and puts them into the locals: LABEL, OPCODE, OPERANDS, COMMENT
*
CSPARSE = (((MINLABEL . LABEL) | (' ' '' . LABEL)) ' '
+ LEN(3) . OPCODE
+ ((' ' (BREAK(' ') | RTAB(0)) . OPERANDS
+ (SPAN(' ') | '') RTAB(0) . COMMENT) |
+ (RPOS(0) . OPERANDS . COMMENT))) |
+ ('.' '' . LABEL MINCOND . OPCODE
+ ((TAB(7) '.' LEN(4) . OPERANDS) | (RPOS(0) . OPERANDS))
+ '' . COMMENT)
*
* CSOPERAND breaks out the next operand in the OPERANDS string.
*
CSOPERAND = (BREAK(',') . OPERAND ',') | ((LEN(1) RTAB(0)) . OPERAND)
*
* CSDTC is a pattern that handles the special case of the Minimal DTC op
*
CSDTC = ((MINLABEL . LABEL) | (' ' '' . LABEL))
+ LEN(7) (LEN(1) $ CHAR BREAK(*CHAR) LEN(1)) . OPERAND
+ (SPAN(' ') | '') RTAB(0) . COMMENT
-EJECT
* DOSTMT is the driver routine that causes processing of the
* statement plex in THISSTMT.
*
DEFINE('DOSTMT()LABEL,OPCODE,OP1,OP2,OP3,COMMENT,T')
*
* HANDLER is a table providing the name of the processing appendage
* for every Minimal Op-Code. The name in this table is prefixed with
* "H_" to get the string name of the appendage. Every op must be
* in this string, including conditional ops.
*
HANDLER = TINIT(
+ 'ADD[H]ADI[ADD2]ADR[ADD2]ANB[ANB]'
+ 'AOV[H]BCT[H]BEQ[BCMP]BGE[BCMP]'
+ 'BGT[BCMP]BHI[BCMP]BLE[BCMP]BLO[BCMP]'
+ 'BLT[BCMP]BNE[BCMP]BRN[H]BRI[BRI]'
+ 'BNZ[BTST]'
+ 'BSW[H]BTW[BTW]BZE[BTST]CEQ[BCMP]'
+ 'CHK[H]CMB[CMB]CMC[H]CNE[BCMP]'
+ 'CSC[NOOP]CTB[CTX]CTW[CTX]CVD[H]'
+ 'CVM[H]DAC[H]DBC[H]DCA[NEW1]'
+ 'DCV[H]DEF[DEF]DIC[H]DRC[H]'
+ 'DTC[DTC]DVI[ADD2]DVR[ADD2]EJC[EJC]'
+ 'ELSE[H]END[END]ENP[NOOP]ENT[ENT]'
+ 'EQU[EQU]ERB[ERX]ERR[ERX]ESW[H]'
+ 'EXI[EXI]EXP[NOOP]FI[H]ICA[NEW1]'
+ 'ICP[ICP]ICV[H]IEQ[ATST]IF[H]'
+ 'IFF[H]IGE[ATST]IGT[ATST]ILE[ATST]'
+ 'ILT[ATST]INE[ATST]INO[OVF]INP[NOOP]'
+ 'INR[NOOP]IOV[OVF]ITR[ITR]JSR[H]'
+ 'LCH[SWP12]LCT[LCT]LCP[ADD2]LCW[NEW1]'
+ 'LDI[ADD2]LDR[ADD2]LEI[LEI]LSH[XSH]'
+ 'LSX[H]MCB[H]MFI[H]MLI[ADD2]MLR[ADD2]'
+ 'MNZ[NEW1]MOV[H]MTI[ADD2]MVC[H]'
+ 'MVW[H]MWB[H]NGI[NGX]NGR[NGX]'
+ 'NZB[BTST]ORB[H]PLC[PXC]PPM[PPM]'
+ 'PRC[PRC]PSC[PXC]REQ[ATST]RGE[ATST]'
+ 'RGT[ATST]RLE[ATST]RLT[ATST]RMI[H]'
+ 'RNE[ATST]RNO[OVF]ROV[OVF]RSH[XSH]'
+ 'RSX[H]RTI[RTI]RTN[NOOP]SBI[ADD2]'
+ 'SBR[ADD2]SCH[H]SCP[NEW1]SEC[SEC]'
+ 'SSL[NOOP]SSS[NOOP]STI[STX]STR[STX]'
+ 'SUB[H]THEN[H]TRC[H]TTL[TTL]'
+ 'UNDEF[UNDEF]WTB[WTB]XOB[H]'
+ 'ZER[H]ZGB[NOOP]ZRB[BTST]')
*
* H_ADD2.OPS is used by the H_ADD2 appendage to find
* the operand it is to insert.
*
H_ADD2.OPS = TINIT(
+ 'ADI[R5]ADR[R2]DVI[R5]DVR[R2]LCP[R3]LDI[R5]'
+ 'LDR[R2]MLI[R5]MLR[R2]MTI[R5]SBI[R5]SBR[R2]')
-EJECT
* H_BNCH.OPCS provides opcode translations for branch-type
* instructions. Branches are emitted directly, unless they are
* in the H_BNCH.PLAB problem label table, in which case an inverted
* branch/jump combination is emitted.
*
H_BNCH.OPCS = TINIT('BEQ[BEQLU]BGE[BGEQU]BGT[BGTRU]BHI[BGEQU]'
+ 'BLE[BLEQU]BLO[BLEQU]BLT[BLSSU]BNE[BNEQU]'
+ 'BNZ[BNEQU]BZE[BEQLU]CEQ[BEQLU]'
+ 'CNE[BNEQU]IEQ[BEQL]IGE[BGEQ]IGT[BGTR]'
+ 'ILE[BLEQ]ILT[BLSS]INE[BNEQ]INO[BVC]'
+ 'IOV[BVS]NZB[BNEQU]'
+ 'REQ[BEQL]RGE[BGEQ]RGT[BGTR]RLE[BLEQ]'
+ 'RLT[BLSS]RNE[BNEQ]RNO[BVC]ROV[BVS]'
+ 'ZRB[BEQLU]')
*
* H_BNCH.PLAB is a list of problem labels which for which 'short'
* conditional jumps cannot be issued, because one or more instructions
* in the code cause range trouble. Inverted branches are emitted
* instead.
*
H_BNCH.PLAB = TABLE(101)
*
* H_BNCH.IOCS is a translate list for the inverted branches needed
* for problem labels.
*
H_BNCH.IOCS = TINIT('BEQ[BNEQU]BGE[BLSSU]BGT[BLEQU]BHI[BLSSU]'
+ 'BLE[BGTRU]BLO[BGTRU]BLT[BGEQU]BNE[BEQLU]'
+ 'BNZ[BEQLU]BZE[BNEQU]CEQ[BNEQU]'
+ 'CNE[BEQLU]IEQ[BNEQ]IGE[BLSS]IGT[BLEQ]'
+ 'ILE[BGTR]ILT[BGEQ]INE[BEQL]INO[BVS]'
+ 'IOV[BVC]NZB[BEQLU]'
+ 'REQ[BNEQ]RGE[BLSS]RGT[BLEQ]RLE[BGTR]'
+ 'RLT[BGEQ]RNE[BEQL]RNO[BVS]ROV[BVC]'
+ 'ZRB[BNEQU]')
*
* H_EQU.DEFS is used by H_EQU to insert the fluid EQU
* definitions (...EQU *).
*
H_EQU.DEFS = TINIT(
+ 'CFP$A[256]CFP$B[4]CFP$C[4]CFP$F[8]'
+ 'CFP$I[1]CFP$M[^X7FFFFFFF]CFP$N[32]'
+ 'NSTMX[10]CFP$R[1]CFP$S[6]CFP$X[2]'
+ 'E$SRS[50]E$STS[512]E$CBS[512]E$HNB[253]'
+ 'E$HNW[3]E$FSP[20]'
+ 'CH$LA[065]CH$LB[066]CH$LC[067]CH$LD[068]'
+ 'CH$LE[069]CH$LF[070]CH$LG[071]CH$LH[072]'
+ 'CH$LI[073]CH$LJ[074]CH$LK[075]CH$LL[076]'
+ 'CH$LM[077]CH$LN[078]CH$LO[079]CH$LP[080]'
+ 'CH$LQ[081]CH$LR[082]CH$LS[083]CH$LT[084]'
+ 'CH$LU[085]CH$LV[086]CH$LW[087]CH$LX[088]'
+ 'CH$LY[089]CH$L$[090]'
+ 'CH$D0[048]CH$D1[049]CH$D2[050]CH$D3[051]'
+ 'CH$D4[052]CH$D5[053]CH$D6[054]CH$D7[055]'
+ 'CH$D8[056]CH$D9[057]'
+ 'CH$$A[097]CH$$B[098]CH$$C[099]CH$$D[100]'
+ 'CH$$E[101]CH$$F[102]CH$$G[103]CH$$H[104]'
+ 'CH$$I[105]CH$$J[106]CH$$K[107]CH$$L[108]'
+ 'CH$$M[109]CH$$N[110]CH$$O[111]CH$$P[112]'
+ 'CH$$Q[113]CH$$R[114]CH$$S[115]CH$$T[116]'
+ 'CH$$U[117]CH$$V[118]CH$$W[119]CH$$X[120]'
+ 'CH$$Y[121]CH$$$[122]'
+ 'CH$AM[038]CH$AS[042]CH$AT[064]CH$BB[060]'
+ 'CH$BL[032]CH$BR[124]CH$CL[058]CH$CM[044]'
+ 'CH$DL[036]CH$DT[046]CH$DQ[034]CH$EQ[061]'
+ 'CH$EX[033]CH$MN[045]CH$NM[035]CH$NT[126]'
+ 'CH$PC[037]CH$PL[043]CH$PP[040]CH$RB[062]'
+ 'CH$RP[041]CH$QU[063]CH$SL[047]CH$SM[059]'
+ 'CH$SQ[039]CH$UN[095]CH$OB[091]CH$CB[093]'
+ 'CH$HT[009]CH$VT[012]IODEL[047]')
*
* EQUATES is used by H_EQU and XOP. It contains a directory of
* all labels that were defined by EQU instructions. This allows
* XOP to properly translate operands of the DLBL(X) category.
*
EQUATES = TABLE(501)
-EJECT
* H_H.XOPS is a table that encodes opcode translation
* for H_H.
*
H_H.XOPS = TINIT(
+ 'ADD[ADDL2]ADI[ADDL2]ADR[ADDF2]ANB[BICL2]'
+ 'BCT[SOBGTR]'
+ 'BRN[JMP]'
+ 'CMB[MCOML]'
+ 'CTB[BICL2]CTW[ASHL]'
+ 'DAC[.LONG]DBC[.LONG]'
+ 'DCA[SUBL2]DCV[DECL]DIC[.LONG]DRC[.FLOAT]'
+ 'DVI[DIVL2]DVR[DIVF2]EJC[.PAGE]'
+ 'ELSE[.IF_FALSE]ERB[JMP]'
+ 'ERR[.ADDRESS]FI[.ENDC]'
+ 'ICA[ADDL2]ICP[TSTL]ICV[INCL]'
+ 'IF[.IF NOT_EQUAL]'
+ 'ITR[CVTLF]JSR[JSB]'
+ 'LCH[MOVZBL]LCT[MOVL]LCP[MOVL]LCW[MOVL]'
+ 'LDI[MOVL]LDR[MOVF]LEI[MOVZWL]LSH[ASHL]'
+ 'MLI[MULL2]MLR[MULF2]'
+ 'MNZ[MOVL]MOV[MOVL]MTI[MOVL]'
+ 'NGI[MNEGL]NGR[MNEGF]'
+ 'ORB[BISL2]PPM[.ADDRESS]'
+ 'RSH[ASHL]'
+ 'SBI[SUBL2]'
+ 'SBR[SUBF2]SCH[MOVB]SCP[MOVL]'
+ 'STI[MOVL]STR[MOVF]'
+ 'SUB[SUBL2]THEN[.IF_TRUE]'
+ 'TTL[.SUBTITLE]'
+ 'XOB[XORL2]ZER[CLRL]')
*
* H_NEW1.OPS is a table used by the H_NEW1 appendage to find
* the operand to insert. It is indexed by opcode.
*
H_NEW1.OPS = TINIT(
+ 'ICA[#4]DCA[#4]'
+ 'LCW[(R3)+]MNZ[SP]SCP[R3]')
*
* Associate file for ERB,ERR messages in H_ERX
*
OUTPUT(.ERRFILE,4,(IDENT(FILENAME) 'TT:', FILENAME '.ERR'))
-EJECT
* Error is used to report an error for THISSTMT
*
DEFINE('ERROR(TEXT)')
-SPACE 3
* OUTSTMT is used to send a target statement to the target code
* output file (OUTFILE <=> LU2)
*
DEFINE('OUTSTMT(LABEL,OPCODE,OP1,OP2,OP3,COMMENT)T,STMTOUT')
*
* Associate output file
*
OUTPUT(.OUTFILE,2,(IDENT(FILENAME) 'TT:', FILENAME '.MAR'))
*
* OS.LLS is used by OUTSTMT to recognise local labels
*
OS.LLS = SPAN(NOS) '$' RPOS(0)
*
* READLINE is called to return the next non-comment line from
* the Minimal input file (INFILE <=> LU1). Note that it will
* not fail on EOF, but it will return a Minimal END statement
*
DEFINE('READLINE()')
*
* Associate input file to LU1
*
INPUT(.INFILE,1,(IDENT(FILENAME) 'TT:', FILENAME '.MIN'))
-EJECT
* XOP is called to translate a Minimal Operand to a VAX Macro Operand.
*
DEFINE('XOP(XOP)VAL,PREFIX')
*
* XOP.REGS is a pattern to match out register names for translation.
*
XOP.REGS = (*REGNAME . VAL RPOS(0) . PREFIX) |
+ (BREAK('(') LEN(1)) . PREFIX LEN(2) . VAL
*
* XOP.XREGS is a table with register translations
*
XOP.XREGS = TINIT('IA[R5]RA[R2]CP[R3]WA[R6]WB[R7]WC[R8]XR[R9]'
+ 'XL[R10]XT[R10]XS[SP]')
*
* XPINTX is a pattern that will match the INT(X) type operand
*
XPINTX = SPAN(NOS) . VAL '('
*
* XPDLBLX is a pattern that will match the DLBL(X) type operand
*
XPDLBLX = MINLABEL . VAL '('
-STITL MAIN PROGRAM
* Here follows the driver code for the "main" program.
-SPACE 3
* Read the problem label table
*
INPUT(.PLTAB,.PLTAB,DIFFER(FILENAME) FILENAME '.PLB') :F(MN02)
MN01 H_BNCH.PLAB[PLTAB] = 'X' :S(MN01)
ENDFILE(.PLTAB)
*
* Read the prefix file and copy to the output side
*
MN02 INPUT(.PREFIXIN,.PREFIXIN,DIFFER(FILENAME) FILENAME '.INI') :F(MN03)
MN02A OUTFILE = PREFIXIN :S(MN02A)
*
* Loop until program exits via H_END
*
MN03 DOSTMT() :(MN03)
-STITL CRACK(LINE)
* CRACK is called to create a STMT plex containing the various
* entrails of the Minimal Source statement in LINE. For
* conditional assembly ops, the opcode is the op, and OP1
* is the symbol. Note that DTC is handled as a special case to
* assure that the decomposition is correct.
*
* CRACK will print an error and fail if a syntax error occurs.
*
CRACK NSTMTS = NSTMTS + 1
LINE CSPARSE :F(CS03)
CRACK = STMT(LABEL,OPCODE,,,,COMMENT)
CRACKERR =
IDENT(OPCODE,'DTC') :S(CS02)
*
* Now pick out operands until none left
*
OPERANDS CSOPERAND = :F(CS01)
OP1(CRACK) = XOP(OPERAND)
OPERANDS CSOPERAND = :F(CS01)
OP2(CRACK) = XOP(OPERAND)
OPERANDS CSOPERAND :F(CS01)
OP3(CRACK) = XOP(OPERAND)
*
* Operands all parsed out. That's all folks.
*
CS01 :(RETURN)
*
* DTC - Special case
*
CS02 LINE CSDTC :F(CS03)
OP1(CRACK) = OPERAND
COMMENT(CRACK) = COMMENT :(CS01)
*
* Here on syntax error
*
CS03 ERROR('SOURCE LINE SYNTAX ERROR') :(FRETURN)
-STITL DOSTMT()
* DOSTMT is invoked to initiate processing of the next line from
* READLINE. For efficient access
* DOSTMT puts name values corresponding to the components in
* variables with the same names (LABEL, OPCODE, OP1,OP2,OP3 and
* COMMENT) which allows the various handlers to $var to store/fetch
* the values of the statment.
*
* After doing this, DOSTMT branches to the handler routine indicated
* for this opcode in the HANDLER table (there must be an entry or
* an error results). The handlers all have entry points beginning
* with "H_", and can be considered a logical extension of the
* DOSTMT routine. The handlers have the choice of branching back
* to DSGEN to cause the THISSTMT plex to be sent to OUTSTMT, or
* of RETURNing themselves, in which case the handler must output
* all needed code itself.
*
* The handlers are listed in a separate section below.
*
DOSTMT THISLINE = READLINE()
THISSTMT = CRACK(THISLINE) :F(DOSTMT)
LABEL = .LABEL(THISSTMT)
OPCODE = .OPCODE(THISSTMT)
MINOP = $OPCODE
OP1 = .OP1(THISSTMT)
OP2 = .OP2(THISSTMT)
OP3 = .OP3(THISSTMT)
COMMENT = .COMMENT(THISSTMT)
*
* Get handler entry point (less "H_" prefix)
*
DIFFER(T = HANDLER[$OPCODE]) :F(DS01)
*
* Jump to handler
*
:($('H_' T))
*
* Here if bad OpCode
*
DS01 ERROR('BAD OP-CODE') :(RETURN)
*
* Handlers can come back here to cause code generation of THISSTMT
*
DSGEN OUTSTMT($LABEL,$OPCODE,$OP1,$OP2,$OP3,$COMMENT) :(RETURN)
-STITL ERROR(TEXT)
* This module handles reporting of errors with the offending
* statement text in THISLINE. Comments explaining
* the error are written to the listing (including error chain), and
* the appropriate counts are updated.
*
ERROR OUTFILE = '; *???* ' THISLINE
OUTFILE = '; ' TEXT
+ (IDENT(LASTERROR),'. LAST ERROR WAS LINE ' LASTERROR)
LASTERROR = NOUTLINES
NOUTLINES = NOUTLINES + 2
NERRORS = NERRORS + 1
+ :(RETURN)
-STITL OUTSTMT(LABEL,OPCODE,OP1,OP2,OP3,COMMENT)
* This module writes the components of the VAX MACRO statement
* passed in the argument list to the formatted .MAR file
*
OUTSTMT STMTOUT = (IDENT(LABEL) TAB,
+ LABEL ':' (?(LABEL ? OS.LLS), ':')
+ (GT(SIZE(LABEL),5), TAB))
+ OPCODE (GT(SIZE(OPCODE),7) ' ', TAB)
+ (IDENT(OP1), OP1
+ (IDENT(OP2), ',' OP2
+ (IDENT(OP3), ',' OP3)))
+ (IDENT(COMMENT),
+ (GT(T = SIZE(OP1 OP2 OP3), 16) ' ',
+ DUPL(TAB, (22 - T) / 8))
+ ';' COMMENT)
*
* Send text to OUTFILE
*
OUTFILE = STMTOUT
NTARGET = NTARGET + 1
NOUTLINES = NOUTLINES + 1
+ :(RETURN)
-STITL READLINE()
* This routine returns the next statement line in the input file
* to the caller. It never fails. If there is no more input,
* then a Minimal END statement is returned.
* Comments are passed through to the output file directly.
*
*
READLINE READLINE = INFILE :F(RL02)
NLINES = NLINES + 1
READLINE ANY('*') = ';' :F(RL01)
*
* Only print comment if requested.
*
OUTFILE = IDENT(FLCFLAG,'Y') READLINE :F(READLINE)
NOUTLINES = NOUTLINES + 1 :(READLINE)
*
* Here if not a comment line
*
RL01 :(RETURN)
*
* Here on EOF
*
RL02 READLINE = ' END'
:(RL01)
-STITL TINIT(STR)
* This routine is called to initialize a table from a string of
* index/value pairs.
*
TINIT POS = 0
*
* Count the number of "[" symbols to get an assessment of the table
* size we need.
*
TIN01 STR (TAB(*POS) '[' BREAK(']') *?(CNT = CNT + 1) @POS)
+ :S(TIN01)
*
* Allocate the table, and then fill it. Note that a small memory
* optimisation is attempted here by trying to re-use the previous
* value string if it is the same as the present one.
*
TINIT = TABLE(CNT)
TIN02 STR (BREAK('[') $ INDEX LEN(1) BREAK(']') $ VAL LEN(1)) =
+ :F(RETURN)
VAL = IDENT(VAL,LASTVAL) LASTVAL
LASTVAL = VAL
TINIT[INDEX] = VAL :(TIN02)
-STITL XOP(OPERAND)
* XOP is called to Translate a Minimal Source Operand into
* a semantically equivalent VAX/Macro Operand. Most of the
* Minimal Operands are basically OK, the following transformations
* must be applied:
*
* - All operands beginning with "=" have the "=" changed
* to a VAX immediate mode beginning with "#"
* - Byte immediate "*..." is changed to "#4*..."
* - INT(X) is changed to "4*INT(X)"
* - DLBL(X) is changed to "4*DLBL(X)"
* - CLBL(X) and WLBL(X) are changed to L^XLBL(X)
*
*
* Check for immediate mode
*
XOP XOP '=' = '#' :S(XP01)
*
* Else check for byte immediate
*
XOP ('*' LEN(1) . VAL) = '#4*' VAL :S(XP01)
*
* Else check for INT(X)
*
XOP XPINTX = (IDENT(VAL,'0'), '4*' VAL) '(' :S(XP01)
*
* Else check for DLBL(X), CLBL(X) or WLBL(X)
*
XOP XPDLBLX = (DIFFER(EQUATES[VAL]) '4*', 'L^') VAL '('
*
* Merge here with XOP containing syntax fixes. Now map registers
*
XP01 XOP XOP.REGS = PREFIX XOP.XREGS[VAL] :(RETURN)
-STITL OPCODE HANDLER APPENDAGES
* Ops that need a second operand (get from table).
*
H_ADD2 $OP2 = H_ADD2.OPS[$OPCODE] :(H_H)
-SPACE 3
* Do ANB opcode
*
H_ANB OUTSTMT($LABEL,'MCOML',$OP1,'R11',,$COMMENT)
$OP1 = 'R11'
$LABEL = $COMMENT = :(H_H)
-SPACE 3
* Real and Integer Branch tests
*
H_ATST OUTSTMT($LABEL,'TST' (IDENT(SUBSTR($OPCODE,1,1),'R') 'F', 'L'),
+ XOP(SUBSTR($OPCODE,1,1) 'A'),,,$COMMENT)
$OP2 = $OP1 :(H_BNCH)
-SPACE 3
* Comparison branches - emit a CMPL
*
H_BCMP OUTSTMT($LABEL,'CMPL',$OP1,$OP2,,$COMMENT)
$OP2 = $OP3 :(H_BNCH)
-SPACE 3
* Entered via various handlers to generate conditional branch code
*
H_BNCH DIFFER(H_BNCH.PLAB[$OP2]) :S(H_BNCH01)
OUTSTMT(,H_BNCH.OPCS[$OPCODE],$OP2) :(RETURN)
H_BNCH01 OUTSTMT(,H_BNCH.IOCS[$OPCODE],(LL = LL + 1) '$')
OUTSTMT(,'JMP',$OP2)
OUTSTMT(LL '$') :(RETURN)
-SPACE 3
* Handle BRI instruction with indirection
*
H_BRI OUTSTMT($LABEL,'MOVL',$OP1,'R11',,$COMMENT)
OUTSTMT(,'JMP','(R11)') :(RETURN)
-SPACE 3
* Zero branch comparisons
*
H_BTST OUTSTMT($LABEL,'TSTL',$OP1,,,$COMMENT)
+ :(H_BNCH)
-EJECT
* BTW opcode
*
H_BTW OUTSTMT($LABEL,'ASHL','#-2',$OP1,$OP1,$COMMENT) :(RETURN)
-SPACE 3
* Do CMB instruction by duping operand for MCOML
*
H_CMB $OP2 = $OP1 :(H_H)
-SPACE 3
* Do CTB and CTW
*
H_CTX OUTSTMT($LABEL,'MOVAB','3+<4*' $OP2 '>(' $OP1 ')',$OP1,,$COMMENT)
$LABEL = $COMMENT =
$OP2 = $OP1
$OP1 = (IDENT($OPCODE,'CTB') '#3','#-2')
$OP3 = IDENT($OPCODE,'CTW') $OP2 :(H_H)
-SPACE 3
* Do DEF Conditional op with an equate to 1 (to 'define')
*
H_DEF $OPCODE = $OP1 '='
$OP1 = '1' :(DSGEN)
-SPACE 3
* Handle DTC by emitting .ASCII and then alignment order
*
H_DTC OUTSTMT($LABEL,'.ASCII',$OP1,,,$COMMENT)
OUTSTMT(,'.ALIGN','LONG','0') :(RETURN)
-SPACE 3
* EJC checks to see if page feeds are to be passed.
*
H_EJC IDENT(EJCFLAG,'Y') :S(H_H)F(RETURN)
-SPACE 3
* END prints statistics on terminal then exits program
*
H_END OUTSTMT(,'.END',,,,$COMMENT)
TERMINAL = '*** TRANSLATION COMPLETE ***'
TERMINAL = NLINES ' LINES READ.'
TERMINAL = NSTMTS ' STATEMENTS PROCESSED.'
TERMINAL = NTARGET ' TARGET CODE LINES PRODUCED.'
TERMINAL = NERRORS ' ERRORS OCCURRED.'
TERMINAL = DIFFER(LASTERROR) 'THE LAST ERROR WAS IN LINE ' LASTERROR
&CODE = NE(NERRORS) 196648
:(END)
-EJECT
* ENT emits the word ID (if needed) and the entry label
*
H_ENT IDENT($OP1) :S(H_ENT01)
OUTSTMT(,'.ALIGN','WORD')
OUTSTMT(,'.WORD',$OP1)
*
* Merge here to emit label entry point
*
H_ENT01 OUTSTMT($LABEL,,,,,$COMMENT) :(RETURN)
* Handle EQU by inserting label in EQUATES for DLBL routine, and
* substituting '*' operands from definitions table if necessary
*
H_EQU EQUATES[$LABEL] = 'X'
$OP1 = IDENT($OP1,'*') H_EQU.DEFS[$LABEL]
$OPCODE = $LABEL '=='
$LABEL = :(DSGEN)
-SPACE 3
* Handle ERB and ERR in essentially the same way. First, send
* the message to the auxilliary .ERR file. Then make sure to
* note if this is the highest error # seen so far, so branch
* table can be properly emitted (see SEC). Then set OP1 to
* be "ERROR_" concatenated with the error number. This label
* refers to a label in the jump table that will load this error
* code in WA and jump to ERROR$.
*
H_ERX ERRFILE = LPAD($OP1,3,0) ($COMMENT = (IDENT($OP2), $OP2 ' ')
+ $COMMENT)
MAXERR = GT($OP1,MAXERR) $OP1
$OP1 = 'ERROR_' LPAD($OP1,3,0)
$OP2 = :(H_H)
-EJECT
* There are 8 cases to EXI, partitioned along 3 binary dimensions.
* These are: 1. OP1 given/not given, 2. PTYPE is R/E or N,
* 3. OP1=1 (or #Ppms=0 if OP1 not given). Each possibility generates
* slightly different code.
*
H_EXI IDENT($OP1) :S(H_EXI00)
T = (IDENT(PTYPE,'N') PNAME '_SAVE','(SP)+')
(EQ($OP1,1) OUTSTMT($LABEL,'MOVL',T,'R11',,$COMMENT),
+ OUTSTMT($LABEL,'ADDL3','#4*' $OP1 - 1,T,'R11',$COMMENT))
OUTSTMT(,'JMP','@(R11)+') :(H_EXI04)
*
* Here if EXI has no OP1 given (normal exit)
*
H_EXI00 EQ(PPMS,0) :S(H_EXI02)
IDENT(PTYPE,'N') :S(H_EXI01)
*
* No OP1, #PPMs>0 and R/E-type
*
OUTSTMT($LABEL,'ADDL2','#4*' PPMS,'(SP)',,$COMMENT)
OUTSTMT(,'RSB') :(H_EXI04)
*
* Here if N-type PRC with no OP1 given & #PPMs > 0
*
H_EXI01 OUTSTMT($LABEL,'ADDL3','#4*' PPMS,PNAME '_SAVE','R11',$COMMENT)
OUTSTMT(,'JMP','(R11)') :(H_EXI04)
*
* Here if no OP1, & #PPMs = 0
*
H_EXI02 IDENT(PTYPE,'N') :S(H_EXI03)
*
* No OP1, #PPMs=0 and R/E-type
*
OUTSTMT($LABEL,'RSB',,,,$COMMENT) :(H_EXI04)
*
* No OP1, #PPMs=0 and N-type
*
H_EXI03 OUTSTMT($LABEL,'JMP','@' PNAME '_SAVE',,,$COMMENT)
*
* Merge to exit
*
H_EXI04 :(RETURN)
-EJECT
* H is entered directly by some opcodes, and eventually by
* most others. It performs an opcode translation if an entry exists
* for it (non-null) in the H_H.XOPS table and then goes to DSGEN to
* dump the statement.
*
H_H $OPCODE = DIFFER(TEMP = H_H.XOPS[$OPCODE]) TEMP :(DSGEN)
-SPACE 3
* Handle ICP opcode with a TSTL on (CP)+
*
H_ICP $OP1 = '(R3)+' :(H_H)
-SPACE 3
* Handle ITR with a MOVLF on IA to RA
*
H_ITR $OP1 = 'R5'
$OP2 = 'R2' :(H_H)
-SPACE 3
* LCT does not emit if both operands are the same
*
H_LCT DIFFER($OP1,$OP2) :S(H_SWP12)
OUTSTMT($LABEL,,,,,$COMMENT) :(RETURN)
-SPACE 3
* LEI uses MOVZWL on the word just prior to the entry point
*
H_LEI $OP2 = $OP1
$OP1 = '-2(' $OP1 ')' :(H_H)
-EJECT
* Make operand 1 operand 2, and put in a new operand 1 according
* to the table.
*
H_NEW1 $OP2 = $OP1
$OP1 = H_NEW1.OPS[$OPCODE] :(H_H)
-SPACE 3
* No-op instructions. Comment out the opcode
*
H_NOOP (IDENT($OPCODE,'INP'), IDENT($OPCODE,'INR'), IDENT($OPCODE,'EXP'))
+ :S(RETURN)
$OPCODE = ';' $OPCODE :(DSGEN)
-SPACE 3
* Do arithmetic negate ops
*
H_NGX $OP1 = $OP2 = XOP(SUBSTR($OPCODE,3,1) 'A') :(H_H)
-SPACE 3
* Handle arithmetic overflow tests [IOV,INO,ROV,RNO]
*
H_OVF $OP2 = $OP1
$OP1 = :(H_BNCH)
-SPACE 3
* Insert dummy PPM branch point if none given
*
H_PPM $OP1 = IDENT($OP1) 'INVALID$' :(H_H)
-SPACE 3
* PRC notes the operands for later EXIs in global variables, and
* emits save area code if N-type.
*
H_PRC PTYPE = $OP1
PPMS = $OP2
PNAME = $LABEL
OUTSTMT(,'.ENABLE','LOCAL_BLOCK')
*
* If N-type, then we need a save area word
*
DIFFER(PTYPE,'N') :S(H_PRC01)
OUTSTMT($LABEL,'MOVL','(SP)+',PNAME '_SAVE',,$COMMENT)
OUTSTMT(,'.SAVE_PSECT','LOCAL_BLOCK')
OUTSTMT(,'.PSECT','PRC_SAVE','NOEXE')
OUTSTMT(PNAME '_SAVE','.LONG','0')
OUTSTMT(,'.RESTORE_PSECT')
:(H_PRC02)
*
* Here if R/E type
*
H_PRC01 OUTSTMT($LABEL,';PRC',,,,$COMMENT)
*
* Merge to exit
*
H_PRC02 :(RETURN)
-EJECT
* Do PLC, PSC ops
*
H_PXC (IDENT($OP2) OUTSTMT($LABEL,'MOVAB','CFP$F(' $OP1 ')',$OP1,,$COMMENT))
+ :S(RETURN)
$OP2 ((('R' ANY(NOS) (ANY(NOS) | '')) | 'SP') RPOS(0)) :S(H_PXC1)
OUTSTMT($LABEL,'MOVL',$OP2,'R11',,'[GET IN SCRATCH REGISTER]')
$LABEL =
$OP2 = 'R11'
H_PXC1 OUTSTMT($LABEL,'MOVAB','CFP$F(' $OP1 ')[' $OP2 ']',$OP1,,$COMMENT)
+ :(RETURN)
-SPACE 3
* Handle RTI with CVTFL and then BVS if PLBL present
*
H_RTI OUTSTMT($LABEL,'CVTFL','R2','R5',,$COMMENT)
(DIFFER($OP1) OUTSTMT(,'BVS',$OP1)) :(RETURN)
-SPACE 3
*
* SEC does various things depending on the current section
*
H_SEC H_SEC.CNT = H_SEC.CNT + 1
*
* Get rid of the extrinsic defs. if past defs. section (saves space)
*
H_EQU.DEFS = EQ(H_SEC.CNT,3)
*
* If we have reached the Stack Ovfl. sect. then dump the ERR list
*
NE(H_SEC.CNT,6) :S(H_SEC02)
OUTSTMT(,'.PAGE')
OUTSTMT('ERR_ADDR','.ADDRESS','ERROR$')
T = 0
*
* Loop here to emit code for saved up ERR objects.
*
H_SEC01 T = LT(T,MAXERR) T + 1 :F(H_SEC02)
OUTSTMT('ERROR_' LPAD(T,3,'0'), 'MOVZWL', '#' T, 'R6')
OUTSTMT(,'JMP','@ERR_ADDR') :(H_SEC01)
*
* Merged when finished with ERRs list.
*
H_SEC02 :(H_H)
-EJECT
* Arithmetic store ops (STI,STR)
*
H_STX $OP2 = $OP1
$OP1 = XOP(SUBSTR($OPCODE,3,1) 'A') :(H_H)
-SPACE 3
* Ops that need to have op1 and op2 switched.
*
H_SWP12 T = $OP1
$OP1 = $OP2
$OP2 = T :(H_H)
-SPACE 3
*
* TTL restores the title text from OP1 and COMMENT
*
H_TTL OUTSTMT(,'.PAGE')
$OP1 = (IDENT($OP1), $OP1 ' ') $COMMENT
$COMMENT = :(H_H)
-SPACE 3
* UNDEF is done with an equate to 0, to "undefine" the symbol
H_UNDEF $OPCODE = $OP1 '='
$OP1 = '0' :(DSGEN)
-SPACE 3
* Immediate mode shifts
*
H_XSH $OP3 = $OP1
$OP1 = '#' (IDENT($OPCODE,'LSH'),'-') $OP2
$OP2 = $OP3 :(H_H)
-SPACE 3
* WTB opcode
H_WTB OUTSTMT($LABEL,'MOVAL','0[' $OP1 ']',$OP1,,$COMMENT) :(RETURN)
-EJECT
END