|
|
@@ -120,7 +120,7 @@ CONST
|
|
|
not implemented yet is a loud ENoLib, never a silent no-op. *)
|
|
|
BI_ClrScr = 6 ; BI_GotoXY = 7 ; BI_LowVideo = 8 ;
|
|
|
BI_NormVideo = 9 ; BI_ClrEol = 10 ; BI_Delay = 11 ;
|
|
|
- BI_KeyPressed = 12 ;
|
|
|
+ BI_KeyPressed = 12 ; BI_UpCase = 13 ;
|
|
|
(* the sub-codes EmCrtSeq takes *)
|
|
|
CrtClr = 0 ; CrtDl = 1 ; CrtDh = 2 ; CrtCaret = 3 ; CrtEol = 4 ;
|
|
|
CrtLow = 5 ; CrtNorm = 6 ; CrtDelay = 7 ; CrtKey = 8 ;
|
|
|
@@ -1408,6 +1408,48 @@ BEGIN
|
|
|
Ebyte (8AH) ; Ebyte (45H) ; Ebyte (VAL (BYTE, off))
|
|
|
END EmLdAlDiOff ;
|
|
|
|
|
|
+PROCEDURE EmCmpAlImm (v : CARDINAL) ;
|
|
|
+BEGIN Ebyte (3CH) ; Ebyte (VAL (BYTE, v)) END EmCmpAlImm ; (* CMP AL,imm8 *)
|
|
|
+
|
|
|
+PROCEDURE EmSubAlImm (v : CARDINAL) ;
|
|
|
+BEGIN Ebyte (2CH) ; Ebyte (VAL (BYTE, v)) END EmSubAlImm ; (* SUB AL,imm8 *)
|
|
|
+
|
|
|
+PROCEDURE EmMovClAl () ;
|
|
|
+BEGIN Ebyte (88H) ; Ebyte (0C1H) END EmMovClAl ; (* MOV CL,AL *)
|
|
|
+
|
|
|
+PROCEDURE EmAndClImm (v : CARDINAL) ;
|
|
|
+BEGIN Ebyte (80H) ; Ebyte (0E1H) ; Ebyte (VAL (BYTE, v)) END EmAndClImm ;
|
|
|
+
|
|
|
+PROCEDURE EmShrAx1 () ;
|
|
|
+BEGIN Ebyte (0D1H) ; Ebyte (0E8H) END EmShrAx1 ; (* SHR AX,1 *)
|
|
|
+
|
|
|
+PROCEDURE EmMovBxi (v : CARDINAL) ;
|
|
|
+BEGIN Ebyte (0BBH) ; Eword (v MOD 10000H) END EmMovBxi ; (* MOV BX,imm16 *)
|
|
|
+
|
|
|
+PROCEDURE EmLeaBx (off : CARDINAL) ;
|
|
|
+(* LEA BX,[BP+off]: the address of a local set. *)
|
|
|
+BEGIN
|
|
|
+ Ebyte (8DH) ; EmBpDisp (off, 3)
|
|
|
+END EmLeaBx ;
|
|
|
+
|
|
|
+PROCEDURE EmMovBxDi () ;
|
|
|
+BEGIN Ebyte (89H) ; Ebyte (0FBH) END EmMovBxDi ; (* MOV BX,DI *)
|
|
|
+
|
|
|
+PROCEDURE EmAddBxAx () ;
|
|
|
+BEGIN Ebyte (01H) ; Ebyte (0C3H) END EmAddBxAx ; (* ADD BX,AX *)
|
|
|
+
|
|
|
+PROCEDURE EmLdAlBx () ;
|
|
|
+BEGIN Ebyte (8AH) ; Ebyte (07H) END EmLdAlBx ; (* MOV AL,[BX] *)
|
|
|
+
|
|
|
+PROCEDURE EmMovDlImm (v : CARDINAL) ;
|
|
|
+BEGIN Ebyte (0B2H) ; Ebyte (VAL (BYTE, v)) END EmMovDlImm ;
|
|
|
+
|
|
|
+PROCEDURE EmShlDlCl () ;
|
|
|
+BEGIN Ebyte (0D2H) ; Ebyte (0E2H) END EmShlDlCl ; (* SHL DL,CL *)
|
|
|
+
|
|
|
+PROCEDURE EmAndAlDl () ;
|
|
|
+BEGIN Ebyte (20H) ; Ebyte (0D0H) END EmAndAlDl ; (* AND AL,DL *)
|
|
|
+
|
|
|
PROCEDURE EmAndAlImm (v : CARDINAL) ;
|
|
|
BEGIN Ebyte (24H) ; Ebyte (VAL (BYTE, v)) END EmAndAlImm ; (* AND AL,imm8 *)
|
|
|
|
|
|
@@ -3001,15 +3043,74 @@ PROCEDURE FoldSetIn (left, right : ERes ; VAR res : ERes) ;
|
|
|
it must stay LOUD rather than silently answer wrong. *)
|
|
|
VAR off, base, size, rel, bidx, bit, m, o : CARDINAL ;
|
|
|
isIn, foldable : BOOLEAN ;
|
|
|
+ jl, jg, jmp : CARDINAL ;
|
|
|
BEGIN
|
|
|
res.kind := 0 ;
|
|
|
res.imm := 0 ;
|
|
|
res.cls := TBool ;
|
|
|
res.chr := FALSE ;
|
|
|
- IF (left.kind # 0) OR (right.cls # TSet) THEN
|
|
|
+ IF right.cls # TSet THEN
|
|
|
Err (ENoLib) ;
|
|
|
RETURN
|
|
|
END ;
|
|
|
+ IF left.kind # 0 THEN
|
|
|
+ (* A RUN-TIME element (`Upcase(Ch) in ['Y','N']', `Ch in ['a'..'z']').
|
|
|
+ Materialise it in AX, subtract the set's base, reject the out-of-range
|
|
|
+ values with the two signed tests the compiler declares (L and G), then
|
|
|
+ index the bit array: byte = (e-base) DIV 8, bit = (e-base) MOD 8.
|
|
|
+ The set's address is formed according to where its bytes live: an
|
|
|
+ inline literal or typed constant is code-resident (image offset +
|
|
|
+ LoadBias), a data set is an absolute or BP-relative address. A set
|
|
|
+ already addressed through DI and an element that itself needs DI are
|
|
|
+ refused: the address would be lost. *)
|
|
|
+ IF right.kind = 5 THEN
|
|
|
+ Err (ENoLib) ;
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ LoadAtom (left) ;
|
|
|
+ IF NOT OK () THEN
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ IF right.setbase # 0 THEN
|
|
|
+ EmMovCxi (W16 (right.setbase)) ;
|
|
|
+ EmSubAxCx ()
|
|
|
+ END ;
|
|
|
+ EmCmpAxi (0) ;
|
|
|
+ jl := EmJcc (8CH, 0) ; (* JL -> false *)
|
|
|
+ EmCmpAxi (W16 (right.setsize * 8)) ;
|
|
|
+ jg := EmJcc (8FH, 0) ; (* JG -> false *)
|
|
|
+ EmMovClAl () ;
|
|
|
+ EmAndClImm (7) ;
|
|
|
+ EmShrAx1 () ; EmShrAx1 () ; EmShrAx1 () ; (* AX := byte index *)
|
|
|
+ IF right.kind = 7 THEN
|
|
|
+ EmMovBxi (W16 (right.setoff + LoadBias))
|
|
|
+ ELSIF right.kind = 1 THEN
|
|
|
+ IF symtab [right.idx].local THEN
|
|
|
+ EmLeaBx ((right.setoff + right.boff) MOD 10000H)
|
|
|
+ ELSE
|
|
|
+ EmMovBxi ((right.setoff + right.boff + LoadBias) MOD 10000H)
|
|
|
+ END
|
|
|
+ ELSE
|
|
|
+ Err (ENoLib) ;
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ EmAddBxAx () ; (* BX := the set's byte *)
|
|
|
+ EmLdAlBx () ;
|
|
|
+ EmMovDlImm (1) ;
|
|
|
+ EmShlDlCl () ; (* DL := 1 << bit *)
|
|
|
+ EmAndAlDl () ;
|
|
|
+ EmMovAh0 () ;
|
|
|
+ EmOrAxAx () ;
|
|
|
+ EmRelSetcc (2) ; (* SETNE: the bit was set *)
|
|
|
+ jmp := EmJmpNear (0) ;
|
|
|
+ SetPatTgt (jl, pc) ;
|
|
|
+ SetPatTgt (jg, pc) ;
|
|
|
+ EmMovAxi (0) ;
|
|
|
+ SetPatTgt (jmp, pc) ;
|
|
|
+ res.kind := 2 ;
|
|
|
+ res.cls := TBool ;
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
(* A code-resident set (an inline literal or a typed constant) folds at
|
|
|
compile time. A DATA set - a record field, local or global, or one
|
|
|
addressed through DI - must be tested at run time: its byte is in the
|
|
|
@@ -3503,6 +3604,7 @@ VAR idx : CARDINAL ;
|
|
|
quoted : BOOLEAN ;
|
|
|
ssize, sbase : CARDINAL ;
|
|
|
sbuf : ARRAY [0..31] OF CHAR ;
|
|
|
+ uj1, uj2 : CARDINAL ;
|
|
|
BEGIN
|
|
|
r.chr := FALSE ; (* default: not a quoted char literal *)
|
|
|
Skip () ;
|
|
|
@@ -3589,6 +3691,48 @@ BEGIN
|
|
|
fixture had ever exercised the fold. *)
|
|
|
ParseVar (r) ;
|
|
|
RETURN
|
|
|
+ ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_UpCase) THEN
|
|
|
+ (* UpCase(c): a CHAR function. A constant folds; otherwise AL is
|
|
|
+ mapped 'a'..'z' -> 'A'..'Z' and left alone outside that range. The
|
|
|
+ test is JL/JG (signed), which is the same answer for 0..127 chars and
|
|
|
+ leaves 128..255 unchanged; it also stays inside the four branch
|
|
|
+ conditions this compiler declares (check_8086's set). *)
|
|
|
+ IF NOT MatchDelim ('(') THEN
|
|
|
+ Err (ENoSemi) ;
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ ParseExpr (r) ;
|
|
|
+ ExpectDelim (')', ENoSemi) ;
|
|
|
+ IF NOT OK () THEN
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ IF (r.cls # TChar) AND (r.cls # TScalar) THEN
|
|
|
+ Err (ETypeErr) ; (* the STRING form is not implemented *)
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ IF r.kind = 0 THEN
|
|
|
+ IF (r.imm >= VAL (LONGINT, ORD ('a')))
|
|
|
+ AND (r.imm <= VAL (LONGINT, ORD ('z'))) THEN
|
|
|
+ r.imm := r.imm - 32
|
|
|
+ END ;
|
|
|
+ r.kind := 0 ;
|
|
|
+ r.cls := TChar ;
|
|
|
+ r.chr := TRUE ;
|
|
|
+ RETURN
|
|
|
+ END ;
|
|
|
+ LoadAtom (r) ;
|
|
|
+ EmCmpAlImm (ORD ('a')) ;
|
|
|
+ uj1 := EmJcc (8CH, 0) ; (* JL -> leave it *)
|
|
|
+ EmCmpAlImm (ORD ('z')) ;
|
|
|
+ uj2 := EmJcc (8FH, 0) ; (* JG -> leave it *)
|
|
|
+ EmSubAlImm (32) ;
|
|
|
+ SetPatTgt (uj1, pc) ;
|
|
|
+ SetPatTgt (uj2, pc) ;
|
|
|
+ EmMovAh0 () ;
|
|
|
+ r.kind := 2 ;
|
|
|
+ r.cls := TChar ;
|
|
|
+ r.chr := FALSE ;
|
|
|
+ RETURN
|
|
|
ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_KeyPressed) THEN
|
|
|
(* KeyPressed: a boolean FUNCTION with no argument, and TP3 lets it be
|
|
|
written without a parameter list (`until KeyPressed'). *)
|
|
|
@@ -4434,20 +4578,23 @@ BEGIN
|
|
|
Err (ETypeErr) ; (* READ needs a variable *)
|
|
|
RETURN
|
|
|
END ;
|
|
|
- acls := symtab [arg.idx].cls ;
|
|
|
- (* s[i] is a CHAR variable, so read(s[i]) must read one byte at
|
|
|
- off+boff, not the whole string at off. arg.cls is TChar
|
|
|
- (set by ParseSub) while the symbol's own class is still
|
|
|
- TString, which is exactly how the element is told from the
|
|
|
- whole string; the address push has to carry boff. For a
|
|
|
- plain variable arg.boff is 0, so nothing changes. *)
|
|
|
- IF (acls = TString) AND (arg.cls = TChar) THEN
|
|
|
- acls := TChar
|
|
|
- END ;
|
|
|
- IF (acls = TString) AND (arg.kind = 5) THEN
|
|
|
- (* a string field of a DI-addressed element *)
|
|
|
- Err (ENoLib) ;
|
|
|
- RETURN
|
|
|
+ IF arg.kind = 5 THEN
|
|
|
+ (* A DI-addressed element: its TYPE is on the expression
|
|
|
+ (WithLookup/ParseSub set it), because the symbol is the
|
|
|
+ containing array/record. s[i] is TChar; a WITH string
|
|
|
+ field is TString. *)
|
|
|
+ acls := arg.cls
|
|
|
+ ELSE
|
|
|
+ acls := symtab [arg.idx].cls ;
|
|
|
+ (* s[i] is a CHAR variable, so read(s[i]) must read one byte
|
|
|
+ at off+boff, not the whole string at off. arg.cls is
|
|
|
+ TChar (set by ParseSub) while the symbol's own class is
|
|
|
+ still TString, which is exactly how the element is told
|
|
|
+ from the whole string; the address push has to carry boff.
|
|
|
+ For a plain variable arg.boff is 0, so nothing changes. *)
|
|
|
+ IF (acls = TString) AND (arg.cls = TChar) THEN
|
|
|
+ acls := TChar
|
|
|
+ END
|
|
|
END ;
|
|
|
IF arg.kind = 5 THEN
|
|
|
(* A variable index: ParseSub computed the element's address
|
|
|
@@ -4536,12 +4683,6 @@ BEGIN
|
|
|
END ;
|
|
|
pushed := FALSE (* nothing was pushed for this one *)
|
|
|
ELSE
|
|
|
- IF (acls = TString) AND (arg.kind = 5) THEN
|
|
|
- (* a string field of a DI-addressed element: there is no
|
|
|
- symbol+offset address to push *)
|
|
|
- Err (ENoLib) ;
|
|
|
- RETURN
|
|
|
- END ;
|
|
|
IF acls = TString THEN
|
|
|
(* A string *variable*. Its value IS its address, so it is
|
|
|
passed exactly the way READ passes an address: push it
|
|
|
@@ -4556,9 +4697,15 @@ BEGIN
|
|
|
(* off + boff: a string FIELD of a record/array has a
|
|
|
non-zero boff, and the address is the field's, not the
|
|
|
containing variable's. `writeln (g[2,3].S)' printed
|
|
|
- the empty first element before this. *)
|
|
|
- EmPushVarAddr (symtab [arg.idx].local,
|
|
|
- (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
|
|
|
+ the empty first element before this.
|
|
|
+ A DI-addressed field (`with Sheet[I,J] do
|
|
|
+ Write(Contents)') already HAS its address in DI. *)
|
|
|
+ IF arg.kind = 5 THEN
|
|
|
+ EmPushDi ()
|
|
|
+ ELSE
|
|
|
+ EmPushVarAddr (symtab [arg.idx].local,
|
|
|
+ (symtab [arg.idx].off + arg.boff) MOD 10000H)
|
|
|
+ END ;
|
|
|
ent := TU_WrStr
|
|
|
ELSE
|
|
|
LoadAtom (arg) ;
|
|
|
@@ -6152,6 +6299,8 @@ BEGIN
|
|
|
symtab [i].defnd := TRUE ;
|
|
|
i := NewSym ("KEYPRESSED", KBuiltin, BI_KeyPressed, 0, 0, 0, 0, FALSE) ;
|
|
|
symtab [i].defnd := TRUE ;
|
|
|
+ i := NewSym ("UPCASE", KBuiltin, BI_UpCase, 0, 0, 0, 0, FALSE) ;
|
|
|
+ symtab [i].defnd := TRUE ;
|
|
|
(* KBD: the console keyboard as a TEXT file. This runtime's read entries
|
|
|
already read the console, so `Read(Kbd, Ch)' drops the file argument and
|
|
|
reads a character exactly as `Read(Ch)' does. *)
|