IMPLEMENTATION MODULE Runtime ; (* 8086 runtime library, assembled byte by byte. Each little emitter below is one 8086 instruction; the ModR/M byte is spelled out in the comment so the encoding can be checked by hand against an 8086 table. This is the same approach the compiler itself takes in Compiler.mod (Ebyte/Eword/EmCall), so the library needs no external assembler and no binary artifact in the tree - RT_Build assembles it every run, which is why the entry offsets are known exactly and are not guesses. Memory model: a .COM image, so CS = DS = ES = SS = 0 and the whole thing lives in one 64K segment. The runtime occupies offsets 0..RT_Size-1, the generated program follows, and the program's globals follow at RT_Size + 1000H. Runtime data therefore sits at fixed low offsets. Calling conventions, matching Compiler.IoCall: WrInt/WrChar/WrBool/WrReal one 16-bit value on the stack (caller pops) RdInt/RdChar/RdBool one address on the stack (caller pops) WrLn/RdLn/StackChk nothing InitMem AX = offset of the program header word block ProgEnd/Halt nothing; exits with code 0 All entries preserve BP and SP, and every register except the documented result, so they can be called from the middle of an expression. *) FROM SYSTEM IMPORT BYTE ; CONST MaxRt = 4096 ; MaxLbl = 64 ; MaxFix = 400 ; MaxNm = 15 ; (* offsets inside the runtime's own data block *) D_NUM = 0 ; (* 8 bytes, decimal conversion scratch *) D_TRUE = 8 ; (* "TRUE$" *) D_FALSE = 14 ; (* "FALSE$" *) D_CRLF = 21 ; (* CR LF '$' *) D_REAL = 24 ; (* "?REAL?" - reals are not formatted yet *) D_END = 31 ; TYPE LblRec = RECORD nm : ARRAY [0..MaxNm] OF CHAR ; off : CARDINAL ; END ; FixRec = RECORD kind : CARDINAL ; (* 0 = rel8, 1 = rel16, 2 = data address *) place : CARDINAL ; (* offset of the displacement/address field *) nm : ARRAY [0..MaxNm] OF CHAR ; val : CARDINAL ; END ; VAR rt : ARRAY [0..MaxRt - 1] OF BYTE ; rpos : CARDINAL ; lbl : ARRAY [0..MaxLbl - 1] OF LblRec ; ltop : CARDINAL ; fix : ARRAY [0..MaxFix - 1] OF FixRec ; nfix : CARDINAL ; dataAt : CARDINAL ; built : BOOLEAN ; entNm : ARRAY [0..13] OF ARRAY [0..MaxNm] OF CHAR ; (* ---------------------------------------------------------------- *) (* name helpers *) (* ---------------------------------------------------------------- *) PROCEDURE StrEq (a, b : ARRAY OF CHAR ) : BOOLEAN ; VAR i : CARDINAL ; BEGIN i := 0 ; WHILE (i <= HIGH (a)) AND (i <= HIGH (b)) DO IF a [i] # b [i] THEN RETURN FALSE END ; INC (i) END ; RETURN TRUE END StrEq ; PROCEDURE SetStr (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR ) ; VAR i : CARDINAL ; BEGIN i := 0 ; WHILE (i <= HIGH (dst)) AND (i <= HIGH (src)) DO dst [i] := src [i] ; INC (i) END ; IF i <= HIGH (dst) THEN dst [i] := 0C END END SetStr ; (* ---------------------------------------------------------------- *) (* primitive emitters *) (* ---------------------------------------------------------------- *) PROCEDURE B (b : CARDINAL ) ; (* emit exactly ONE byte. A two-byte opcode must be written as two B calls - B masks to 100H, so B (8BE4H) would silently emit just E4. *) BEGIN IF rpos >= MaxRt THEN RETURN (* blob is oversized: drop the byte *) END ; rt [rpos] := VAL (BYTE, b MOD 100H) ; INC (rpos) END B ; PROCEDURE W (w : CARDINAL ) ; BEGIN B (w MOD 100H) ; B ((w DIV 100H) MOD 100H) (* little endian *) END W ; PROCEDURE M (nm : ARRAY OF CHAR ) ; (* mark: nm is the current offset *) BEGIN IF ltop < MaxLbl THEN SetStr (lbl [ltop].nm, nm) ; lbl [ltop].off := rpos ; INC (ltop) END END M ; PROCEDURE AddFix (kind, place : CARDINAL ; nm : ARRAY OF CHAR ; val : CARDINAL ) ; BEGIN IF nfix < MaxFix THEN fix [nfix].kind := kind ; fix [nfix].place := place ; fix [nfix].val := val ; SetStr (fix [nfix].nm, nm) ; INC (nfix) END END AddFix ; PROCEDURE LblOff (nm : ARRAY OF CHAR ) : CARDINAL ; VAR i : CARDINAL ; BEGIN i := 0 ; WHILE i < ltop DO IF StrEq (lbl [i].nm, nm) THEN RETURN lbl [i].off END ; INC (i) END ; RETURN 0 END LblOff ; PROCEDURE J8 (nm : ARRAY OF CHAR ) ; BEGIN B (0EBH) ; (* JMP rel8 *) AddFix (0, rpos, nm, 0) ; B (0) END J8 ; PROCEDURE C8 (nm : ARRAY OF CHAR ) ; BEGIN B (0E8H) ; (* CALL rel16 *) AddFix (1, rpos, nm, 0) ; W (0) END C8 ; PROCEDURE Jcc (code : CARDINAL ; nm : ARRAY OF CHAR ) ; BEGIN B (code) ; (* Jcc rel8 *) AddFix (0, rpos, nm, 0) ; B (0) END Jcc ; PROCEDURE Dd (delta : CARDINAL ) ; (* emit a 16-bit address into the runtime's data block; the value is only known once the data block has been placed, so it is a fixup *) BEGIN AddFix (2, rpos, "", delta) ; W (0) END Dd ; (* --- 8-bit / 16-bit register and memory forms, one instruction each --- *) PROCEDURE PushBp ; BEGIN B (55H) END PushBp ; PROCEDURE PopBp ; BEGIN B (5DH) END PopBp ; PROCEDURE MovBpSp ; BEGIN B (8BH) ; B (0ECH) END MovBpSp ; (* 8B EC: MOV BP,SP *) PROCEDURE MovSpBp ; BEGIN B (89H) ; B (0ECH) END MovSpBp ; (* 89 EC: MOV SP,BP *) PROCEDURE LeaveR ; BEGIN B (0C9H) END LeaveR ; PROCEDURE RetR ; BEGIN B (0C3H) END RetR ; PROCEDURE Int21 ; BEGIN B (0CDH) ; B (21H) END Int21 ; PROCEDURE PushDs ; BEGIN B (1EH) END PushDs ; PROCEDURE PopEs ; BEGIN B (7H) END PopEs ; PROCEDURE PushAx ; BEGIN B (50H) END PushAx ; PROCEDURE PopAx ; BEGIN B (58H) END PopAx ; PROCEDURE PushBx ; BEGIN B (53H) END PushBx ; PROCEDURE PopBx ; BEGIN B (5BH) END PopBx ; PROCEDURE PushCx ; BEGIN B (51H) END PushCx ; PROCEDURE PopCx ; BEGIN B (59H) END PopCx ; PROCEDURE PushDx ; BEGIN B (52H) END PushDx ; PROCEDURE PopDx ; BEGIN B (5AH) END PopDx ; PROCEDURE PushDi ; BEGIN B (57H) END PushDi ; PROCEDURE PopDi ; BEGIN B (5FH) END PopDi ; PROCEDURE XorAxAx ; BEGIN B (31H) ; B (0C0H) END XorAxAx ; (* 11 000 000 *) PROCEDURE XorCxCx ; BEGIN B (31H) ; B (0C9H) END XorCxCx ; (* 11 001 001 *) PROCEDURE XorDxDx ; BEGIN B (31H) ; B (0D2H) END XorDxDx ; (* 11 010 010 *) PROCEDURE XorDiDi ; BEGIN B (31H) ; B (0FFH) END XorDiDi ; (* 11 111 111 *) PROCEDURE IncCx ; BEGIN B (41H) END IncCx ; PROCEDURE IncSi ; BEGIN B (46H) END IncSi ; PROCEDURE DecSi ; BEGIN B (4EH) END DecSi ; PROCEDURE AddDi2 ; BEGIN B (83H) ; B (0C7H) ; B (2) END AddDi2 ; (* 11 000 111 *) PROCEDURE CmpAl (v : CARDINAL ) ; BEGIN B (3CH) ; B (v) END CmpAl ; PROCEDURE CmpAx0 ; BEGIN B (83H) ; B (0F8H) ; B (0) END CmpAx0 ; PROCEDURE CmpCx0 ; BEGIN B (83H) ; B (0F9H) ; B (0) END CmpCx0 ; PROCEDURE CmpSpW0 ; BEGIN B (83H) ; B (7CH) ; B (24H) ; B (0) ; B (0) END CmpSpW0 ; PROCEDURE CmpSiBx ; BEGIN B (39H) ; B (0DCH) END CmpSiBx ; (* 11 011 100 *) PROCEDURE CmpCxDx ; BEGIN B (39H) ; B (0D1H) END CmpCxDx ; (* 11 010 001 *) PROCEDURE CmpDiCx ; BEGIN B (39H) ; B (0CFH) END CmpDiCx ; (* 11 001 111 *) PROCEDURE AddDl (v : CARDINAL ) ; BEGIN B (80H) ; B (0C2H) ; B (v) END AddDl ; PROCEDURE SubAl (v : CARDINAL ) ; BEGIN B (2CH) ; B (v) END SubAl ; PROCEDURE NegAx ; BEGIN B (0F7H) ; B (0D8H) END NegAx ; PROCEDURE NegDi ; BEGIN B (0F7H) ; B (0DFH) END NegDi ; PROCEDURE DivCx ; BEGIN B (0F7H) ; B (0F1H) END DivCx ; (* 11 110 001 *) PROCEDURE MulBx ; BEGIN B (0F7H) ; B (0E3H) END MulBx ; (* 11 100 011 *) PROCEDURE MovAh (v : CARDINAL ) ; BEGIN B (0B4H) ; B (v) END MovAh ; PROCEDURE MovDl (v : CARDINAL ) ; BEGIN B (0B2H) ; B (v) END MovDl ; PROCEDURE MovBxV (v : CARDINAL ) ; BEGIN B (0BBH) ; W (v) END MovBxV ; PROCEDURE MovCxV (v : CARDINAL ) ; BEGIN B (0B9H) ; W (v) END MovCxV ; PROCEDURE MovDxV (v : CARDINAL ) ; BEGIN B (0BAH) ; W (v) END MovDxV ; PROCEDURE MovBxD (delta : CARDINAL ) ; BEGIN B (0BBH) ; Dd (delta) END MovBxD ; PROCEDURE MovDxD (delta : CARDINAL ) ; BEGIN B (0BAH) ; Dd (delta) END MovDxD ; PROCEDURE MovAxSp ; BEGIN B (8BH) ; B (44H) ; B (24H) ; B (0) END MovAxSp ; PROCEDURE MovSiAx ; BEGIN B (8BH) ; B (0C0H) END MovSiAx ; PROCEDURE MovAxDi ; BEGIN B (8BH) ; B (0C7H) END MovAxDi ; (* 11 000 111 *) PROCEDURE MovAxDx ; BEGIN B (8BH) ; B (0D2H) END MovAxDx ; PROCEDURE MovCxSi6 ; BEGIN B (8BH) ; B (4CH) ; B (6) END MovCxSi6 ; PROCEDURE MovDxSi2 ; BEGIN B (8BH) ; B (54H) ; B (2) END MovDxSi2 ; (* The program header is a block of words laid out by the compiler: +0 hdrFlag 1 = image is valid +2 hdrCS end of the generated code, in bytes +4 hdrDS base of the data area <- initmem wants these two +6 hdrHeap end of the data area <- +8 hdrMax max open files MovDxSi4/MovCxSi8 read the two that matter here. The obvious +2/+6 would be the code end and the data end, i.e. initmem would zero from the end of the code to the end of the data - a 4 KiB gap of nothing, and the globals themselves untouched. Same instruction length, so no entry offset moves. *) PROCEDURE MovCxSi8 ; BEGIN B (8BH) ; B (4CH) ; B (8) END MovCxSi8 ; PROCEDURE MovDxSi4 ; BEGIN B (8BH) ; B (54H) ; B (4) END MovDxSi4 ; PROCEDURE MovAxBp4 ; BEGIN B (8BH) ; B (45H) ; B (4) END MovAxBp4 ; PROCEDURE MovBxBp4 ; BEGIN B (8BH) ; B (5EH) ; B (4) END MovBxBp4 ; PROCEDURE MovAlDh ; BEGIN B (8AH) ; B (0C0H) END MovAlDh ; PROCEDURE MovDlSi ; BEGIN B (8AH) ; B (14H) END MovDlSi ; PROCEDURE MovDlSp ; BEGIN B (8AH) ; B (54H) ; B (24H) ; B (0) END MovDlSp ; PROCEDURE StDiAx ; BEGIN B (89H) ; B (7H) END StDiAx ; (* [DI] := AX *) PROCEDURE StDiBx ; BEGIN B (89H) ; B (1FH) END StDiBx ; (* [DI] := BX *) PROCEDURE StBxCx ; BEGIN B (89H) ; B (8BH) END StBxCx ; (* [BX] := CX *) PROCEDURE StSiDl ; BEGIN B (88H) ; B (14H) END StSiDl ; (* [SI] := DL *) PROCEDURE StBxDl ; BEGIN B (88H) ; B (93H) END StBxDl ; (* [BX] := DL *) PROCEDURE MovDhAl ; BEGIN B (88H) ; B (0C6H) END MovDhAl ; PROCEDURE MovDlAl ; BEGIN B (88H) ; B (0C2H) END MovDlAl ; PROCEDURE MovSiBx ; BEGIN B (89H) ; B (0DCH) END MovSiBx ; PROCEDURE MovDiDx ; BEGIN B (89H) ; B (0D7H) END MovDiDx ; PROCEDURE MovDiAx ; BEGIN B (89H) ; B (0C7H) END MovDiAx ; PROCEDURE AddDiAx ; BEGIN B (1H) ; B (0C7H) END AddDiAx ; PROCEDURE IncBx ; BEGIN B (43H) END IncBx ; PROCEDURE MovAlBx ; BEGIN B (8AH) ; B (07H) END MovAlBx ; (* 8A 07: AL:=[BX] *) PROCEDURE MovClBx ; BEGIN B (8AH) ; B (0FH) END MovClBx ; (* 8A 0F: CL:=[BX] *) PROCEDURE JmpBx ; BEGIN B (0FFH) ; B (0E3H) END JmpBx ; (* FF E3: JMP BX *) (* JE 74 JNE 75 JB 72 JBE 76 JGE 7D *) PROCEDURE Je8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (74H, nm) END Je8 ; PROCEDURE Jne8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (75H, nm) END Jne8 ; PROCEDURE Jb8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (72H, nm) END Jb8 ; PROCEDURE Jbe8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (76H, nm) END Jbe8 ; PROCEDURE Jge8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7DH, nm) END Jge8 ; PROCEDURE Ja8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (77H, nm) END Ja8 ; PROCEDURE Jcxz8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E3H, nm) END Jcxz8 ; PROCEDURE Loop8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E2H, nm) END Loop8 ; (* ---------------------------------------------------------------- *) (* the entries *) (* ---------------------------------------------------------------- *) PROCEDURE EmitInitMem ; (* AX = offset of the program header. The header holds, at +4 the base of the program's data area and at +8 its end, so the globals can be zeroed - Pascal leaves them undefined, TP3's runtime clears them. Also makes ES = DS so that any string instruction in the library would work. *) BEGIN M ("initmem") ; MovSiAx ; (* SI = AX = the header offset the caller passed *) MovDxSi4 ; (* DX = [SI+4] = hdrDS = data base *) MovCxSi8 ; (* CX = [SI+8] = hdrHeap = data end *) CmpCxDx ; Jbe8 ("im_done") ; MovDiDx ; (* DI = data base *) M ("im_zero") ; MovAxDx ; StDiAx ; AddDi2 ; CmpDiCx ; Jb8 ("im_zero") ; M ("im_done") ; PushDs ; PopEs ; RetR END EmitInitMem ; PROCEDURE EmitEnd ; (* progend and halt are the same code: the compiler already zeroes AX for progend, and a HALT argument is discarded at compile time, so both leave with exit code 0. *) BEGIN M ("progend") ; M ("halt") ; XorAxAx ; MovAh (4CH) ; Int21 ; RetR END EmitEnd ; PROCEDURE EmitStackChk ; (* called from every procedure prologue. Range and stack checking are not compiled in yet, so this must do nothing at all - in particular it must not touch a register, because the call site is in the middle of a partially evaluated expression. *) BEGIN M ("stackchk") ; RetR END EmitStackChk ; PROCEDURE EmitGetCh ; (* AL = next character, 1Ah at end of input. INT 21h AH=08h reads without echoing, so a redirected stdin behaves the same as a keyboard. *) BEGIN M ("getch") ; MovAh (8) ; Int21 ; RetR END EmitGetCh ; PROCEDURE EmitWrInt ; (* one signed 16-bit value on the stack. Div CX gives the remainder in DX, which is turned into a digit and stored backwards from the end of the scratch area, then printed forwards. *) BEGIN M ("wrint") ; PushBp ; MovBpSp ; MovAxBp4 ; CmpAx0 ; Jge8 ("wi_pos") ; PushAx ; MovDl (ORD ("-")) ; MovAh (2) ; Int21 ; PopAx ; NegAx ; M ("wi_pos") ; MovCxV (10) ; MovBxD (D_NUM + 8) ; (* BX = one past the last digit *) MovSiBx ; M ("wi_dig") ; XorDxDx ; DivCx ; AddDl (ORD ("0")) ; DecSi ; StSiDl ; CmpAx0 ; Jne8 ("wi_dig") ; M ("wi_out") ; CmpSiBx ; Je8 ("wi_done") ; MovDlSi ; MovAh (2) ; Int21 ; IncSi ; J8 ("wi_out") ; M ("wi_done") ; MovSpBp ; PopBp ; RetR END EmitWrInt ; PROCEDURE EmitWrChar ; (* the low byte of the pushed word *) BEGIN M ("wrchar") ; MovDlSp ; MovAh (2) ; Int21 ; RetR END EmitWrChar ; PROCEDURE EmitWrInl ; (* Write an inline string literal - TP3 TPSRC4 "xwrtinl". The compiler emits CALL wrtinl ... so the return address on the stack points at the length byte that follows the call. POP BX takes that address, CX picks up the length, and the routine finishes with JMP BX - returning to just past the last character. That is the whole trick: the literal is self-delimiting, so it needs no terminator, no length table and no space in the data segment, and it costs the code stream only the characters themselves (TPSRC10 "estring" emits exactly for the same reason). Consequently this entry has NO stack argument, unlike WrInt/WrChar: the return address has already been consumed by the POP. *) BEGIN M ("wrtinl") ; PopBx ; (* BX := address of the length byte *) XorCxCx ; MovClBx ; (* CX := length *) IncBx ; (* BX -> first character *) MovAh (2) ; (* INT 21h/02h: put character, AL *) Jcxz8 ("wn_end") ; (* empty string -> nothing to do *) M ("wn_loop") ; MovAlBx ; (* AL := next character *) Int21 ; (* (preserves every register but AL) *) IncBx ; Loop8 ("wn_loop") ; M ("wn_end") ; JmpBx (* resume past the string; no RET here, the return address is already gone *) END EmitWrInl ; PROCEDURE EmitWrBool ; BEGIN M ("wrbool") ; CmpSpW0 ; Jne8 ("wb_t") ; MovDxD (D_FALSE) ; J8 ("wb_o") ; M ("wb_t") ; MovDxD (D_TRUE) ; M ("wb_o") ; MovAh (9) ; Int21 ; RetR END EmitWrBool ; PROCEDURE EmitWrLn ; BEGIN M ("wrln") ; MovDxD (D_CRLF) ; MovAh (9) ; Int21 ; RetR END EmitWrLn ; PROCEDURE EmitWrReal ; (* the 6-byte real is on the stack but is not formatted: the compiler does not yet load real operands into a form the runtime could read. A visible marker beats printing the mantissa as an integer. *) BEGIN M ("wrreal") ; MovDxD (D_REAL) ; MovAh (9) ; Int21 ; RetR END EmitWrReal ; PROCEDURE EmitRdInt ; (* address on the stack; skips leading blanks, takes an optional sign, then digits, stopping *before* the delimiter so the following TU_RdLn throws away the rest of the line. Sign in CX, value in DI. *) BEGIN M ("rdint") ; PushBp ; MovBpSp ; PushAx ; PushBx ; PushCx ; PushDx ; PushDi ; M ("ri_skip") ; C8 ("getch") ; CmpAl (ORD (" ")) ; Je8 ("ri_skip") ; CmpAl (9) ; Je8 ("ri_skip") ; CmpAl (13) ; Je8 ("ri_skip") ; CmpAl (10) ; Je8 ("ri_skip") ; XorCxCx ; CmpAl (ORD ("-")) ; Jne8 ("ri_nos") ; IncCx ; C8 ("getch") ; J8 ("ri_dig0") ; M ("ri_nos") ; CmpAl (ORD ("+")) ; Jne8 ("ri_dig0") ; C8 ("getch") ; M ("ri_dig0") ; XorDiDi ; M ("ri_dig") ; CmpAl (ORD ("0")) ; Jb8 ("ri_done") ; CmpAl (ORD ("9")) ; Ja8 ("ri_done") ; SubAl (ORD ("0")) ; MovDhAl ; (* keep the digit across the multiply *) MovAxDi ; MovBxV (10) ; MulBx ; (* DX:AX := DI * 10 *) MovDiAx ; MovAh (0) ; MovAlDh ; AddDiAx ; C8 ("getch") ; J8 ("ri_dig") ; M ("ri_done") ; CmpCx0 ; Je8 ("ri_st") ; NegDi ; M ("ri_st") ; MovBxBp4 ; StDiBx ; PopDi ; PopDx ; PopCx ; PopBx ; PopAx ; MovSpBp ; PopBp ; RetR END EmitRdInt ; PROCEDURE EmitRdChar ; BEGIN M ("rdchar") ; PushBp ; MovBpSp ; PushAx ; PushBx ; C8 ("getch") ; MovDlAl ; MovBxBp4 ; StBxDl ; PopBx ; PopAx ; MovSpBp ; PopBp ; RetR END EmitRdChar ; PROCEDURE EmitRdBool ; (* one character, classified the way TP3 does: T/t/Y/y/1 true, anything else false. *) BEGIN M ("rdbool") ; PushBp ; MovBpSp ; PushAx ; PushBx ; PushCx ; C8 ("getch") ; XorCxCx ; CmpAl (ORD ("T")) ; Je8 ("rb_t") ; CmpAl (ORD ("t")) ; Je8 ("rb_t") ; CmpAl (ORD ("Y")) ; Je8 ("rb_t") ; CmpAl (ORD ("y")) ; Je8 ("rb_t") ; CmpAl (ORD ("1")) ; Je8 ("rb_t") ; J8 ("rb_s") ; M ("rb_t") ; IncCx ; M ("rb_s") ; MovBxBp4 ; StBxCx ; PopCx ; PopBx ; PopAx ; MovSpBp ; PopBp ; RetR END EmitRdBool ; PROCEDURE EmitRdLn ; (* discard the rest of the line, including the terminator *) BEGIN M ("rdln") ; PushAx ; M ("rl_loop") ; C8 ("getch") ; CmpAl (13) ; Je8 ("rl_e") ; CmpAl (10) ; Je8 ("rl_e") ; CmpAl (26) ; Je8 ("rl_e") ; (* ^Z: end of input *) J8 ("rl_loop") ; M ("rl_e") ; PopAx ; RetR END EmitRdLn ; PROCEDURE EmitData ; BEGIN dataAt := rpos ; (* 8 bytes of scratch, never read before written *) B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (ORD ("T")) ; B (ORD ("R")) ; B (ORD ("U")) ; B (ORD ("E")) ; B (ORD ("$")) ; B (ORD ("F")) ; B (ORD ("A")) ; B (ORD ("L")) ; B (ORD ("S")) ; B (ORD ("E")) ; B (ORD ("$")) ; B (13) ; B (10) ; B (ORD ("$")) ; B (ORD ("?")) ; B (ORD ("R")) ; B (ORD ("E")) ; B (ORD ("A")) ; B (ORD ("L")) ; B (ORD ("?")) ; WHILE rpos < dataAt + D_END DO B (0) END END EmitData ; PROCEDURE FixUp ; VAR i, t, rel : CARDINAL ; BEGIN i := 0 ; WHILE i < nfix DO IF fix [i].kind = 2 THEN t := (dataAt + fix [i].val) MOD 10000H ; rt [fix [i].place] := VAL (BYTE, t MOD 100H) ; rt [fix [i].place + 1] := VAL (BYTE, (t DIV 100H) MOD 100H) ELSE t := LblOff (fix [i].nm) ; IF fix [i].kind = 0 THEN (* rel8 is measured from the end of the instruction, i.e. one byte past the displacement field *) rel := (t + 100H - (fix [i].place + 1)) MOD 100H ; rt [fix [i].place] := VAL (BYTE, rel) ELSE rel := (t + 10000H - (fix [i].place + 2)) MOD 10000H ; rt [fix [i].place] := VAL (BYTE, rel MOD 100H) ; rt [fix [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H) END END ; INC (i) END END FixUp ; (* ---------------------------------------------------------------- *) (* public interface *) (* ---------------------------------------------------------------- *) PROCEDURE RT_Build ; BEGIN IF built THEN RETURN END ; rpos := 0 ; ltop := 0 ; nfix := 0 ; dataAt := 0 ; EmitInitMem ; EmitEnd ; EmitStackChk ; EmitWrInt ; EmitWrChar ; EmitWrBool ; EmitWrReal ; EmitWrLn ; EmitWrInl ; EmitRdInt ; EmitRdChar ; EmitRdBool ; EmitRdLn ; EmitGetCh ; EmitData ; FixUp ; SetStr (entNm [0], "initmem") ; SetStr (entNm [1], "progend") ; SetStr (entNm [2], "stackchk") ; SetStr (entNm [3], "wrint") ; SetStr (entNm [4], "wrchar") ; SetStr (entNm [5], "wrbool") ; SetStr (entNm [6], "wrreal") ; SetStr (entNm [7], "wrln") ; SetStr (entNm [8], "rdint") ; SetStr (entNm [9], "rdchar") ; SetStr (entNm [10], "rdbool") ; SetStr (entNm [11], "rdln") ; SetStr (entNm [12], "halt") ; SetStr (entNm [13], "wrtinl") ; built := TRUE END RT_Build ; PROCEDURE RT_Size () : CARDINAL ; BEGIN IF NOT built THEN RT_Build () END ; RETURN rpos END RT_Size ; PROCEDURE RT_Byte (i : CARDINAL ) : BYTE ; BEGIN IF NOT built THEN RT_Build () END ; IF i >= rpos THEN RETURN 0 END ; RETURN rt [i] END RT_Byte ; PROCEDURE RT_Entry (i : CARDINAL ) : CARDINAL ; BEGIN IF NOT built THEN RT_Build () END ; IF i > 13 THEN RETURN 0 END ; RETURN LblOff (entNm [i]) END RT_Entry ; END Runtime.