{ > PasCgen } { Tutu 17 feb 86 - 1.07 - Range check errors - new non-fatals start at 170 } { Tutu 29 jan 86 - Constant pointer size now 4 bytes } { Tutu 17 dec 85 - Altered typsz to return ptrsize for most tos quantities also removed maxproc restriction } { Tutu 04 sep 85 - Mods to GENUW/SI/STR assume code file open before !} PROCEDURE ERROR (N : SI); { Turns code generation off. If an error has not already occurred at the current character position, then insert the error number into the error array and add the current character position to the set which contains the position of the error on the line. Since all errors over error 164 are fatal, if N is > 164 then goto the end of the program. } BEGIN GENCODE := FALSE; IF X.ERRNO < MAXERRS THEN IF NOT (X.CHRNO IN ERRSET) THEN BEGIN ERRSET := ERRSET + [X.CHRNO MOD 255]; X.ERRNO := SUCC (X.ERRNO); X.ERRORS := SUCC (X.ERRORS); ERRARAY[X.ERRNO] := N END; if (n > 164) and (n < 170) then goto 999 { Tutu 1.07 } { Tutu - was IF N > 164 THEN GOTO 999 { Fatal } END; { error } PROCEDURE ISOERR; { This procedure is called when an Acorn extension has been detected and checks that the extensions compiler option is on $X+ . } BEGIN IF NOT EXT THEN ERROR (14) END; { isoerr } PROCEDURE GENUB (VALUE : UB); { This procedure outputs a byte value to the temporary code file (pfile), creating the file first if neccessary and then increments the code pointer. } BEGIN IF GENCODE THEN BEGIN IF NOTOPEN THEN BEGIN REWRITE (PFILE); NOTOPEN := FALSE END; WRITE (PFILE, VALUE) END; X.CADR := SUCC (X.CADR) END; { genub } PROCEDURE GENSTR (PTR, LENGTH : UW); { This proc outputs a string residing anywhere in memory (of length LENGTH) to the temp code file one byte at a time using procedure GENUB. Nope - Tutu The start of the string in RAM is pointed to by PTR. Code file must be open } VAR INDEX : UW; NAMES: PACKED RECORD CASE BOOLEAN OF TRUE : (TADR : UW); { Set TADR to address of byte in memory } FALSE : (TPTR : ^CHAR) { then output value using pointer } END; BEGIN if gencode then FOR INDEX := PTR TO PRED (PTR+LENGTH) DO BEGIN NAMES.TADR := INDEX; write (pfile, ord (NAMES.TPTR^)); { Tutu - was GENUB (ORD (NAMES.TPTR^)) } X.CADR := succ (X.CADR) END END; { genstr } PROCEDURE PFLIST; { This procedure adds a record containing the entry point of the current procedure to the end of a linked list and increments the current procedure number by 1. If there are more than 127 procedures (MAXPROC) then an error is generated. The list is used at the end of the program to append a table of procedure entry points to the end of the program because procedures are called indirectly through the table to compact the code. A procedure is called using its index into the table and since the 6502 only has 8 bit registers, the table can only be 254 bytes long. Proc. number size on ARM is integer now. } BEGIN NEW (PFNAMES^.NEXT); PFNAMES := PFNAMES^.NEXT; PFNAMES^.ADR := X.CADR; PFNAMES^.NEXT := NIL; X.PROCNO := SUCC (X.PROCNO); { IF X.PROCNO > MAXPROC THEN ERROR (30) { Tutu - ARM code unrestricted } END; { pflist } PROCEDURE DBUGNAME (DESC : SYMREC); { This procedure outputs the procedure name (accessed through the procedure's identifier descriptor (DESC)) to the temp code file using procedure GENSTR, having first determined the length of the name. The name is always output, or else the translator would get confused. Tutu } VAR LENGTH : UW; NAMES : PACKED RECORD CASE BOOLEAN OF TRUE : (TADR : UW); { Set TADR to address of byte in } FALSE : (TPTR : ^CHAR) { memory then output value using pntr } END; BEGIN { Get length of procedure's name } LENGTH := 0; REPEAT LENGTH := SUCC (LENGTH); NAMES.TADR := SUCC (DESC.NAMLINK + LENGTH) UNTIL NAMES.TPTR^ = ' '; IF LENGTH > 250 THEN LENGTH := 250; GENUB (77); {Here_is proc_name} GENUB (LENGTH); GENSTR (SUCC (DESC.NAMLINK), LENGTH) END; { dbugname } PROCEDURE OPEN; { This procedure opens the compiler source file (name held in SOURCEFILE). } BEGIN X.SRCLINE := 0; RESET (SOURCE, SOURCEFILE); { If Command Line Options not being parsed } IF NOT CLO THEN CH := ' '; NXTCH := ' '; NEWLINE := TRUE END; { open } PROCEDURE CLRSTR (VAR NAME : FNAMETYP); { This procedure sets the string passed into the procedure in NAME to null. } VAR INDEX : UB; BEGIN FOR INDEX := MAXFNAME DOWNTO 1 DO NAME[INDEX] := CHR (TERMCH) END; { clrstr } PROCEDURE GETNAME (VAR FNAME : FNAMETYP); { This procedure reads a filename from the command line (into the string FNAME) if one is present. The string is ended by a space, end_of_line or '{'. } LABEL 13; BEGIN CLRSTR (FNAME); X.INDEX := 1; WHILE (SOURCE^ <> ' ') AND (SOURCE^ <> '{') DO BEGIN IF X.INDEX >= MAXFNAME THEN BEGIN ERROR (93); GOTO 13 END; FNAME[X.INDEX] := SOURCE^; X.INDEX := SUCC (X.INDEX); GET (SOURCE) END; 13:; END; { getname } FUNCTION BYTES (LOW, HIGH : SI): UB; { This function returns the size of a variable that would be needed to hold values in the range LOW..HIGH. 0..255 = 1 byte 0..65535 = 2 bytes others = 4 bytes } BEGIN BYTES := 1; IF (LOW < 0) OR (HIGH > MAXUW) THEN BYTES := 4 ELSE IF HIGH > 255 THEN BYTES := 2 END; { bytes } PROCEDURE GENUW (VALUE : UW); { This procedure outputs a 2 byte value to the temp code file by calling GENUB twice. Not any more it doesn't ! - Tutu. Relies on code file being open } VAR V : PACKED RECORD CASE BOOLEAN OF TRUE : ( X : UW); FALSE : ( B1 : UB; { B1 and B2 overlay X } B2 : UB) END; begin if gencode then begin V.X := VALUE; write (pfile, V.B1, V.B2) end; x.cadr := succ (succ (x.cadr)) { Tutu - was GENUB (V.B1); GENUB (V.B2) } end; { genuw } PROCEDURE GENSI (VALUE : SI); { This procedure outputs a 4 byte value to the temp code file by calling GENUW twice. Well, it USED to do ! - Tutu. Relies on code file being open } VAR V : PACKED RECORD CASE BOOLEAN OF TRUE : ( X : SI); FALSE : ( B1 : UB; { B1 .. B4 overlay X - Used to be W1, W2 : UW} B2 : UB; B3 : UB; B4 : UB) END; begin if gencode then begin V.X := VALUE; write (pfile, V.B1, V.B2, V.B3, V.B4) end; x.cadr := x.cadr + 4 { Tutu - was GENUW (V.W1); GENUW (V.W2) } end; { gensi } PROCEDURE FLUSH; FORWARD; PROCEDURE FLUSHNGEN (OP : UB); { This procedure calls procedure FLUSH to flush any BL-code which may be in the code buffer and then outputs the BL-code held in OP to the temp code file. } BEGIN FLUSH; GENUB (OP) END; { flushngen } PROCEDURE GENOPWTB (OP : UB; TWOBYTES : UW); { This procedure (GENerateOPWithTwoBytes) calls FLUSHNGEN (see above) and then outputs a 2 byte value to the temp code file, setting X.LASTADR to the code address of the first of the 2 bytes. } BEGIN FLUSHNGEN (OP); X.LASTADR := X.CADR; GENUW (TWOBYTES) END; { genopwtb } PROCEDURE GENJMP (JUMPOP : UB; ADRES : UW); { This procedure is used to output a BL-code jump instruction to the temp code file and resets the active declaration level to the current level (see description of X.ACTIVLEV in global var section). } BEGIN GENOPWTB (JUMPOP, ADRES); X.ACTIVLEV := X.LEVEL END; { genjmp } PROCEDURE GENLORS (OP : UB; OPERAND : UW); { Variables are accessed at run time by an offset in their activation record. If this is less than 255 then a short form of instruction is used otherwise the long form is used. This procedure (GENerateLongORShort) is passed the short form of the BL-code in OP and adds 42 (to create the long form) to it if the OPERAND is > 255. The instruction and the operand are then output to the temp code file. } BEGIN genopwtb (op+42, operand) { Tutu - always generate long form for ARM/Comm } { Tutu - was IF OPERAND > 255 THEN GENOPWTB (OP+42, OPERAND) ELSE BEGIN FLUSHNGEN (OP); GENUB (OPERAND) END } END; { genlors } PROCEDURE SETLEV (LEV : UB); { This procedure generates code to set the active display level to LEV. 47 = global, 48 = current (declaration level), 46, X = level (X) } BEGIN IF LEV = 1 THEN GENUB (47) ELSE IF LEV = X.LEVEL THEN GENUB (48) ELSE BEGIN GENUB (46); GENUB (PRED (LEV)) END; X.ACTIVLEV := LEV END; { setlev } PROCEDURE FLUSH; { This procedure flushes the BL-code buffer if it is not empty, generating code to set the active level to that of the variable held in the buffer before generating the variable access BL-code. The buffer is a single instruction buffer used to modify variable access BL-codes. } BEGIN IF CBUFF.BASICOP <> 0 THEN BEGIN CBUFF.BASICOP := 0; IF CBUFF.LEV <> X.ACTIVLEV THEN SETLEV (CBUFF.LEV); GENLORS (CBUFF.OPCODE, CBUFF.VARADR) END END; { flush } FUNCTION ISINT (DESC : SYMREC) : BOOLEAN; { This function returns true if the type described by DESC is an integer. } BEGIN ISINT := (DESC.IDTYP = SUBR) AND (DESC.REF^.TYP = INT) END; { isint } PROCEDURE TYPSZ (DESC : SYMREC; VAR OP, SIZE : UB; PACKCONSTS : BOOLEAN); { This procedure returns : in SIZE the No. bytes that this variable / constant uses ie 1234 = 2 bytes, in OP the offset to add to a basic BL-code instruction to get the actual instruction eg. VAR a : integer; r : packed record f : 0..1000 end; a := r.f; To push the f onto the stack OP would be 6 and SIZE 2. To pop value into a, OP would be 2 and SIZE 4. offsets are 0 : bce (ByteCharEnum) 1 : ptr (pointer 2 : int (integer) 3 : rea (real) 4 : set (set) 5 : uby (UnsignedByte) 6 : uwd (UnsignedWord) 7 : blk (Block) } VAR OPCOPY, SIZECOPY : UB; BEGIN OPCOPY := CODE0 (TABSEARCH, ORD (DESC.IDTYP), MLO, MHI{MOVTYPVAL}) MOD 128; { OPCOPY now contains an offset to add to a basic BL-code PULL/PUSH instruction depending on the type of variable } IF (DESC.IDTYP = PNTR) AND (DESC.OBJ = KONST) THEN SIZECOPY := ptrsize; { Tutu - was 2 } IF OPCOPY = 3 THEN SIZECOPY := reasize; { Tutu - real vbl - was 5 } IF OPCOPY = 0 { ie variable is a subrange } THEN BEGIN SIZECOPY := DESC.REF^.SUBRSIZ; IF DESC.OBJ = KONST THEN BEGIN IF PACKCONSTS THEN SIZECOPY := BYTES (DESC.VAL, DESC.VAL) END ELSE IF DESC.PAK THEN SIZECOPY := BYTES (DESC.REF^.LOW, DESC.REF^.HIGH); CASE SIZECOPY OF 4 : OPCOPY := 2; 1 : IF DESC.REF^.TYP = INT THEN OPCOPY := 5; 2 : OPCOPY := 6 END OTHERWISE END; OP := OPCOPY; SIZE := SIZECOPY END; { typsz } PROCEDURE GENMOV (OPBASE : UB; DESC : SYMREC); { This procedure is used to construct all push, pop, locate and identify variable BL-codes. The instruction may be left in the code buffer (in which case it is an instruction that could be added to, ie. part of a record), or sent to the temp code file. } VAR OPTYP, OP, SIZE : UB; BEGIN TYPSZ (DESC, OPTYP, SIZE, TRUE); OP := OPBASE + OPTYP; IF DESC.OBJ = KONST {constants} THEN BEGIN FLUSHNGEN (OPTYP); IF SIZE >= 4 THEN BEGIN GENSI (DESC.VAL); if size = reasize then if reasize=5 then genub (desc.rv) else gensi (desc.rv) { Tutu - was IF SIZE = 5 THEN GENUB (DESC.RV)} END ELSE IF SIZE = 2 THEN GENUW (DESC.VAL) ELSE GENUB (DESC.VAL) END ELSE IF OPBASE <= POP THEN {locates, pushes & pops} IF DESC.NRM THEN { value parameter } BEGIN FLUSH; CBUFF.BASICOP := OPBASE; CBUFF.LEV := DESC.LEV; CBUFF.VARADR := DESC.ADR; CBUFF.OPCODE := OP; IF OPBASE = LOCATE THEN CBUFF.OPCODE := LOCATE ELSE FLUSH END ELSE { var parameter } BEGIN DESC.IDTYP := PNTR; DESC.NRM := TRUE; GENMOV (PUSH, DESC); IF OPBASE = PUSH THEN GENUB (IPUSH+OPTYP) ELSE IF OPBASE = POP THEN GENUB (IPOP+OPTYP) END ELSE IF OPTYP = 7 THEN { block copy or block to stack } BEGIN IF DESC.IDTYP = CARAY THEN BEGIN IF X.ACTIVLEV <> ADESCREF^.ALEV MOD 128 THEN SETLEV (ADESCREF^.ALEV MOD 128); GENLORS (10, ADESCREF^.ADESCOFF); { Psh_Ptr } IF ADESC <> 0 THEN GENLORS (41, ADESC); { Use_Offset } GENUB (218) { Conformant array block copy } END ELSE GENLORS (OP, DESC.REF^.VARSIZ) END ELSE IF CBUFF.BASICOP = LOCATE THEN BEGIN IF OPBASE = IPOP THEN CBUFF.OPCODE := POP + OPTYP ELSE IF OPBASE = IDENTIFY THEN CBUFF.OPCODE := 8 { Identify_S } ELSE CBUFF.OPCODE := PUSH + OPTYP; FLUSH END ELSE FLUSHNGEN (OP) END; { genmov } PROCEDURE GENRECOFF (VALUE : UW); { This procedure is used in code generation and adds the offset of a field inside a record to the base already present if there is one in the code buffer. If the code buffer is empty then the offset is put into the buffer in case another offset comes along later. } BEGIN IF VALUE <> 0 THEN IF (CBUFF.BASICOP = LOCATE) OR (CBUFF.BASICOP = USEOP) THEN CBUFF.VARADR := VALUE + CBUFF.VARADR ELSE BEGIN FLUSH; CBUFF.VARADR := VALUE; CBUFF.BASICOP := USEOP; CBUFF.OPCODE := USEOP END END; { genrecoff } PROCEDURE STORPATCH (ADR : UW; VAL : UW); { This procedure adds a record containing the address in the code produced which needs patching and the value to patch it with, to a linked list of patches. A machine code routine is used to find the correct position in the list to insert the record (because it is faster than Pascal). } VAR PATCHDATA, AFTER, BEFORE : PATCHP; PATCH : PATCHREC; TEMP : SI; BEGIN NEW (PATCHDATA); {The list of patches are a linked list of patches ordered in increasing address value so to insert a patch in the list the correct position in the list must first be found } AFTER := PATCH1; TEMP := CODE1 (SEARCH, 0, ADR); IF AFTER = NIL THEN PATCHDATA^.NEXT := NIL ELSE IF AFTER^.ADDR = ADR THEN BEGIN {A patch to this address already exists so don't do it again} DISPOSE (PATCHDATA); PATCHDATA := AFTER END ELSE PATCHDATA^.NEXT := AFTER; BEFORE^.NEXT := PATCHDATA; { Store patch data in linked list record } PATCHDATA^.ADDR := ADR; PATCHDATA^.VALUE := VAL END; { storpatch } {$S'PasScan'}