| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- 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 <length byte> <character>...
- 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 <length byte><chars> 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.
|