Compiler.mod 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847
  1. IMPLEMENTATION MODULE Compiler ;
  2. (* Turbo Pascal 3-style single-pass Pascal -> 8086 compiler in
  3. GNU Modula-2 (-fiso), following the structure of the original
  4. TPSRC6 'turbo' entry / TPSRC7-10:
  5. Inittur reset state, pre-defined types, scratch temporaries
  6. Skip lexer: blanks, comments { } and (* *), directives {$ }
  7. GetWord/WddTok/MatchKey word lexing with a keyword table (kName/kTk)
  8. PeekKw lookahead keyword check WITHOUT consuming (via saved
  9. srcPos) - needed because declarations and compound
  10. statements peek at END/ELSE/etc
  11. RdIntConst/RdConst integer, hex and char constants
  12. Search symbol table lookup filtered by lexical level
  13. ParseExpr -> ParseCmp -> ParseAdd -> ParseMul -> ParseNeg
  14. -> ParseAtom precedence climb (TPSRC9)
  15. Statmnt statements: if/while/repeat/for/case/goto/exit/begin
  16. assignment and calls (TPSRC8)
  17. ParseType/Decls ARRAY, STRING, scalar/subrange types; variable,
  18. constant, label and procedure/function definitions
  19. Compile driver: optional PROGRAM header, DefPart, progpart,
  20. final '.', header size patch, patch resolution.
  21. Ebyte/Eword/Ecall/Ejmp + patch list code emission (TPSRC10).
  22. The emitted image is a byte array (mode word, CS/DS, size words,
  23. CALL initmem, MOV BP,SP, then generated code). Forward labels and
  24. forward procedure calls resolve through a patch list (ptc records).
  25. Working subset (v0.4): integer/char/boolean/byte scalars, constants
  26. with folding, globals, locals, value parameters, procedures and
  27. scalar-result functions, ARRAY[const..const] with constant indexing,
  28. control flow, GOTO/EXIT, and the standard procedures WRITE, WRITELN,
  29. READ, READLN, HALT (DefBuiltins + IoCall).
  30. The standard procedures are dispatched per argument, as the original
  31. does (TPSRC8 pwriteln/pwrloop inspects each argument's class and emits
  32. a different call per type), so the runtime is handed a value and never
  33. a descriptor.
  34. Still not implemented: real/set/record/file and the string runtime
  35. raise Err (ENoLib) - the original's "not implemented" path. The
  36. runtime blob itself, the linker that rebases the TU_* entry offsets
  37. by the runtime's size, and CmdRun (the interpreter) are still pending,
  38. so a compiled image cannot be executed yet. *)
  39. FROM TextBuf IMPORT Length, CharAt ;
  40. FROM SYSTEM IMPORT BYTE ;
  41. (* ---------------------------------------------------------------- *)
  42. (* constants *)
  43. (* ---------------------------------------------------------------- *)
  44. CONST
  45. MaxLine = 128 ;
  46. MaxName = 31 ;
  47. MaxCode = 24000 ;
  48. MaxSym = 3000 ;
  49. MaxPatch = 2000 ;
  50. MaxPend = 400 ;
  51. (* type codes (TP3 vartp) *)
  52. TNone = 0 ; TArray = 1 ; TRecord = 2 ; TSet = 3 ;
  53. TPtr = 4 ; TFile = 5 ; TText = 6 ; TUntyp = 7 ;
  54. TString = 8 ; TReal = 9 ; TScalar = 10 ; TBool = 11 ;
  55. (* symbol kinds *)
  56. KLabel = 100H ; KConst = 200H ; KType = 300H ;
  57. KVar = 400H ; KProc = 500H ; KFunc = 600H ;
  58. KBuiltin = 700H ; (* standard procedure, see BI_* below *)
  59. (* which standard procedure a KBuiltin symbol denotes *)
  60. BI_Write = 0 ; BI_WriteLn = 1 ; BI_Read = 2 ;
  61. BI_ReadLn = 3 ; BI_Halt = 4 ;
  62. (* keyword tokens *)
  63. TkNone = 0 ; TkProgram = 1 ; TkBegin = 2 ; TkEnd = 3 ;
  64. TkIf = 4 ; TkThen = 5 ; TkElse = 6 ; TkWhile = 7 ;
  65. TkDo = 8 ; TkRepeat = 9 ; TkUntil = 10 ; TkFor = 11 ;
  66. TkTo = 12 ; TkDownto = 13 ; TkCase = 14 ; TkOf = 15 ;
  67. TkGoto = 16 ; TkExit = 17 ; TkWith = 18 ; TkVar = 19 ;
  68. TkConst= 20 ; TkType = 21 ; TkLabel = 22 ; TkProcedure = 23 ;
  69. TkFunction = 24 ; TkNil = 25 ; TkAnd = 26 ; TkOr = 27 ;
  70. TkNot = 28 ; TkDiv = 29 ; TkMod = 30 ; TkIn = 31 ;
  71. TkFile = 32 ; TkText = 33 ; TkRecord = 34 ; TkArray = 35 ;
  72. TkSet = 36 ; TkPacked = 37 ; TkForward = 38 ; TkExternal = 39 ;
  73. TkAbsolute = 40 ; TkOverlay = 41 ; TkString = 42 ;
  74. (* runtime entry offsets in the emitted image (TU_InitMem etc.) *)
  75. TU_InitMem = 8H ;
  76. TU_ProgEnd = 10H ;
  77. TU_StackChk = 18H ;
  78. (* Standard-procedure runtime entries. TP3 does NOT pass a descriptor:
  79. TPSRC8 pwriteln/pwrloop inspects each argument's class in CL and emits
  80. a *different* call per type, so the type is fixed at compile time and
  81. the runtime needs only the value. Mirrored here. *)
  82. TU_WrInt = 20H ; TU_WrChar = 28H ; TU_WrBool = 30H ;
  83. TU_WrReal = 38H ; TU_WrLn = 40H ;
  84. TU_RdInt = 48H ; TU_RdChar = 50H ; TU_RdBool = 58H ;
  85. TU_RdLn = 60H ; TU_Halt = 68H ;
  86. (* TP3 error numbers *)
  87. ENoSemi = 1 ; EPointExp = 10 ; ESimpType = 30 ;
  88. EUnknown = 41 ; EConstRange = 45 ; EMemOvf = 98 ;
  89. ECompOvf = 99 ; ENoLib = 102 ; ETypeErr = 56 ;
  90. AposC = 39 ; (* ORD ("'") - avoids quote-in-quote *)
  91. (* ---------------------------------------------------------------- *)
  92. (* types *)
  93. (* ---------------------------------------------------------------- *)
  94. TYPE
  95. SymEntry =
  96. RECORD
  97. name : ARRAY [0..MaxName] OF CHAR ;
  98. tag : CARDINAL ;
  99. cls : CARDINAL ;
  100. size : CARDINAL ;
  101. elem : CARDINAL ;
  102. off : CARDINAL ;
  103. lval : LONGINT ;
  104. level : CARDINAL ;
  105. local : BOOLEAN ;
  106. resvar : CARDINAL ;
  107. goPos : CARDINAL ;
  108. defnd : BOOLEAN ;
  109. fwd : BOOLEAN ;
  110. END ;
  111. PatchRec =
  112. RECORD
  113. place : CARDINAL ;
  114. target : CARDINAL ;
  115. filled : BOOLEAN ;
  116. END ;
  117. PendRec =
  118. RECORD
  119. kind : CARDINAL ; (* 0 goto, 1 call *)
  120. who : CARDINAL ;
  121. place : CARDINAL ; (* patch slot index *)
  122. END ;
  123. ERes =
  124. RECORD
  125. cls : CARDINAL ;
  126. kind : CARDINAL ; (* 0 const, 1 var, 2 value in AX *)
  127. imm : LONGINT ;
  128. idx : CARDINAL ;
  129. boff : CARDINAL ; (* constant fold-in for subscripts *)
  130. chr : BOOLEAN ; (* single-quoted literal, e.g. 'a' *)
  131. END ;
  132. DirRec = RECORD rng, chk : BOOLEAN END ;
  133. (* ---------------------------------------------------------------- *)
  134. (* state *)
  135. (* ---------------------------------------------------------------- *)
  136. VAR
  137. srcPos, srcLen : CARDINAL ;
  138. wrd : ARRAY [0..MaxName] OF CHAR ;
  139. symtab : ARRAY [0..MaxSym - 1] OF SymEntry ;
  140. symTop : CARDINAL ;
  141. patches : ARRAY [0..MaxPatch - 1] OF PatchRec ;
  142. nPatch : CARDINAL ;
  143. pend : ARRAY [0..MaxPend - 1] OF PendRec ;
  144. nPend : CARDINAL ;
  145. exitPatch : ARRAY [0..63] OF CARDINAL ;
  146. exitCnt : CARDINAL ;
  147. brkSave : ARRAY [0..15] OF CARDINAL ;
  148. loopTy : ARRAY [0..15] OF CARDINAL ; (* 1 while/repeat, 2 for *)
  149. brkN : CARDINAL ;
  150. caseJmp : ARRAY [0..63] OF CARDINAL ;
  151. caseN : CARDINAL ;
  152. pc, dc : CARDINAL ;
  153. varspc : CARDINAL ;
  154. cbuf : ARRAY [0..MaxCode - 1] OF BYTE ;
  155. codeSz, dataSz : CARDINAL ;
  156. abortFac : BOOLEAN ;
  157. errNum : CARDINAL ; (* NOT "errNo": Compile's formal of that
  158. name would shadow it, and the caller's
  159. errNo would never be filled in *)
  160. txerrPos : CARDINAL ;
  161. lexnest : CARDINAL ;
  162. curIsFunc : BOOLEAN ;
  163. resultVar : CARDINAL ;
  164. locFree : CARDINAL ; (* next local slot (BP-relative, 8-bit) *)
  165. locBytes : CARDINAL ; (* frame size for SUB SP *)
  166. parmOff : CARDINAL ; (* next parameter slot (BP-relative) *)
  167. dirs : DirRec ;
  168. tmpA, tmpB : CARDINAL ; (* global scratch word addresses *)
  169. hdrFlag, hdrCS, hdrDS, hdrHeap, hdrMax : CARDINAL ;
  170. kName : ARRAY [0..42] OF ARRAY [0..15] OF CHAR ;
  171. kTk : ARRAY [0..42] OF CARDINAL ;
  172. (* ---------------------------------------------------------------- *)
  173. (* small char helpers *)
  174. (* ---------------------------------------------------------------- *)
  175. PROCEDURE CurCh () : CHAR ;
  176. BEGIN
  177. IF srcPos >= srcLen THEN
  178. RETURN 0C
  179. END ;
  180. RETURN CharAt (srcPos)
  181. END CurCh ;
  182. PROCEDURE GetCh () : CHAR ;
  183. VAR ch : CHAR ;
  184. BEGIN
  185. ch := CurCh () ;
  186. IF srcPos < srcLen THEN
  187. INC (srcPos)
  188. END ;
  189. RETURN ch
  190. END GetCh ;
  191. PROCEDURE PeekAhead (k : CARDINAL) : CHAR ;
  192. BEGIN
  193. IF srcPos + k >= srcLen THEN
  194. RETURN 0C
  195. END ;
  196. RETURN CharAt (srcPos + k)
  197. END PeekAhead ;
  198. PROCEDURE Digit (ch : CHAR) : BOOLEAN ;
  199. BEGIN
  200. RETURN (ch >= '0') AND (ch <= '9')
  201. END Digit ;
  202. PROCEDURE Alpha (ch : CHAR) : BOOLEAN ;
  203. BEGIN
  204. RETURN ((ch >= 'A') AND (ch <= 'Z')) OR ((ch >= 'a') AND (ch <= 'z'))
  205. OR (ch = '_')
  206. END Alpha ;
  207. PROCEDURE AlphaNum (ch : CHAR) : BOOLEAN ;
  208. BEGIN
  209. RETURN (Alpha (ch)) OR (Digit (ch))
  210. END AlphaNum ;
  211. PROCEDURE IsHexCh (ch : CHAR) : BOOLEAN ;
  212. BEGIN
  213. RETURN (Digit (ch)) OR ((ch >= 'A') AND (ch <= 'F'))
  214. OR ((ch >= 'a') AND (ch <= 'f'))
  215. END IsHexCh ;
  216. PROCEDURE Upper (ch : CHAR) : CHAR ;
  217. BEGIN
  218. IF (ch >= 'a') AND (ch <= 'z') THEN
  219. RETURN CHR (ORD (ch) - ORD ('a') + ORD ('A'))
  220. END ;
  221. RETURN ch
  222. END Upper ;
  223. PROCEDURE W16 (x : LONGINT) : CARDINAL ;
  224. (* fold x modulo 10000H, handling negatives (no negative MOD) *)
  225. VAR m : CARDINAL ;
  226. BEGIN
  227. IF x >= 0 THEN
  228. RETURN VAL (CARDINAL, x MOD 10000H)
  229. END ;
  230. m := VAL (CARDINAL, (0 - x) MOD 10000H) ;
  231. RETURN (10000H - m) MOD 10000H
  232. END W16 ;
  233. PROCEDURE DropCh (v : CHAR) ;
  234. BEGIN
  235. END DropCh ;
  236. PROCEDURE DropB (v : BOOLEAN) ;
  237. BEGIN
  238. END DropB ;
  239. PROCEDURE DropC (v : CARDINAL) ;
  240. BEGIN
  241. END DropC ;
  242. PROCEDURE BitAnd (a, b : LONGINT) : LONGINT ;
  243. BEGIN
  244. RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, W16 (a))
  245. * VAL (BITSET, W16 (b))))
  246. END BitAnd ;
  247. PROCEDURE BitOr (a, b : LONGINT) : LONGINT ;
  248. BEGIN
  249. RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, W16 (a))
  250. + VAL (BITSET, W16 (b))))
  251. END BitOr ;
  252. PROCEDURE BitNot (a : LONGINT) : LONGINT ;
  253. BEGIN
  254. RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, 0FFFFH)
  255. - VAL (BITSET, W16 (a))))
  256. END BitNot ;
  257. (* ---------------------------------------------------------------- *)
  258. (* errors *)
  259. (* ---------------------------------------------------------------- *)
  260. PROCEDURE Err (n : CARDINAL) ;
  261. BEGIN
  262. IF NOT abortFac THEN
  263. abortFac := TRUE ;
  264. errNum := n ;
  265. txerrPos := srcPos
  266. END
  267. END Err ;
  268. PROCEDURE OK () : BOOLEAN ;
  269. BEGIN
  270. RETURN NOT abortFac
  271. END OK ;
  272. (* ---------------------------------------------------------------- *)
  273. (* emission : ebyte / eword / ecall / ejump *)
  274. (* ---------------------------------------------------------------- *)
  275. PROCEDURE Ebyte (b : BYTE) ;
  276. BEGIN
  277. IF pc >= MaxCode THEN
  278. Err (EMemOvf)
  279. ELSE
  280. cbuf [pc] := b ;
  281. INC (pc)
  282. END
  283. END Ebyte ;
  284. PROCEDURE Eword (w : CARDINAL) ;
  285. BEGIN
  286. Ebyte (VAL (BYTE, w MOD 100H)) ;
  287. Ebyte (VAL (BYTE, (w DIV 100H) MOD 100H))
  288. END Eword ;
  289. PROCEDURE AddPatch (place, target : CARDINAL) ;
  290. BEGIN
  291. IF nPatch < MaxPatch THEN
  292. patches [nPatch].place := place ;
  293. patches [nPatch].target := target ;
  294. patches [nPatch].filled := FALSE ;
  295. INC (nPatch)
  296. ELSE
  297. Err (ECompOvf)
  298. END
  299. END AddPatch ;
  300. PROCEDURE SetPatTgt (idx, t : CARDINAL) ;
  301. BEGIN
  302. IF idx < nPatch THEN
  303. patches [idx].target := t
  304. END
  305. END SetPatTgt ;
  306. PROCEDURE EmCall (target : CARDINAL) : CARDINAL ;
  307. (* E8 rel16 near call; target = 0 => forward (patched later).
  308. Returns the patch slot, or 0 when resolved directly. *)
  309. VAR rel, p : CARDINAL ;
  310. BEGIN
  311. Ebyte (0E8H) ;
  312. IF target = 0 THEN
  313. Eword (0) ;
  314. p := nPatch ;
  315. AddPatch (pc - 2, 0) ;
  316. RETURN p
  317. END ;
  318. (* rel16 is measured from the END of the instruction. Here pc already
  319. points past the opcode(s) and at the displacement field, so the
  320. instruction ends at pc+2 - the same convention ResolvePatches uses
  321. with "place + 2". Omitting the +2 lands every direct call/jump 2 bytes
  322. past its target. *)
  323. rel := (target + 10000H - (pc + 2)) MOD 10000H ;
  324. Eword (rel) ;
  325. RETURN 0
  326. END EmCall ;
  327. PROCEDURE EmJmpNear (target : CARDINAL) : CARDINAL ;
  328. (* E9 rel16; target = 0 => forward. Returns patch slot or 0. *)
  329. VAR rel, p : CARDINAL ;
  330. BEGIN
  331. Ebyte (0E9H) ;
  332. IF target = 0 THEN
  333. Eword (0) ;
  334. p := nPatch ;
  335. AddPatch (pc - 2, 0) ;
  336. RETURN p
  337. END ;
  338. rel := (target + 10000H - (pc + 2)) MOD 10000H ; (* see EmCall *)
  339. Eword (rel) ;
  340. RETURN 0
  341. END EmJmpNear ;
  342. PROCEDURE EmJcc (cc : BYTE ; target : CARDINAL) : CARDINAL ;
  343. (* 0F 8x rel16 near conditional; target = 0 => forward. *)
  344. VAR rel, p : CARDINAL ;
  345. BEGIN
  346. Ebyte (0FH) ;
  347. Ebyte (cc) ;
  348. IF target = 0 THEN
  349. Eword (0) ;
  350. p := nPatch ;
  351. AddPatch (pc - 2, 0) ;
  352. RETURN p
  353. END ;
  354. rel := (target + 10000H - (pc + 2)) MOD 10000H ; (* see EmCall *)
  355. Eword (rel) ;
  356. RETURN 0
  357. END EmJcc ;
  358. PROCEDURE ResolvePatches () ;
  359. VAR i : CARDINAL ;
  360. rel : CARDINAL ;
  361. BEGIN
  362. i := 0 ;
  363. WHILE i < nPatch DO
  364. IF NOT patches [i].filled THEN
  365. rel := (patches [i].target + 10000H - (patches [i].place + 2))
  366. MOD 10000H ;
  367. cbuf [patches [i].place] := VAL (BYTE, rel MOD 100H) ;
  368. cbuf [patches [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H) ;
  369. patches [i].filled := TRUE
  370. END ;
  371. INC (i)
  372. END
  373. END ResolvePatches ;
  374. (* 1-byte const emitters *)
  375. PROCEDURE EmMovAxi (imm : CARDINAL) ;
  376. BEGIN
  377. Ebyte (0B8H) ; Eword (imm)
  378. END EmMovAxi ;
  379. PROCEDURE EmMovBpSp () ;
  380. BEGIN
  381. Ebyte (8BH) ; Ebyte (0ECH)
  382. END EmMovBpSp ;
  383. PROCEDURE EmMovAh0 () ;
  384. BEGIN
  385. Ebyte (0B4H) ; Ebyte (0H)
  386. END EmMovAh0 ;
  387. PROCEDURE EmMovAxSp () ;
  388. (* MOV AX,[SP]. Needs a SIB byte, and [SP] cannot be encoded with mod=00
  389. (that would compute BP+SP), so it is mod=01 / SIB=24h / disp8=0. Emitting
  390. just "8B 04" left the SIB slot unfilled, which silently ate the *next*
  391. instruction - the case-label CMP - and turned every case comparison into
  392. a load from a garbage address. *)
  393. BEGIN
  394. Ebyte (8BH) ; Ebyte (44H) ; Ebyte (24H) ; Ebyte (0)
  395. END EmMovAxSp ;
  396. PROCEDURE EmMovCxSp () ;
  397. BEGIN
  398. Ebyte (8BH) ; Ebyte (0CH)
  399. END EmMovCxSp ;
  400. PROCEDURE EmPushAx () ;
  401. BEGIN
  402. Ebyte (50H)
  403. END EmPushAx ;
  404. PROCEDURE EmPopCx () ;
  405. BEGIN
  406. Ebyte (59H)
  407. END EmPopCx ;
  408. PROCEDURE EmPopDx () ;
  409. BEGIN
  410. Ebyte (5AH)
  411. END EmPopDx ;
  412. PROCEDURE EmXchgAxCx () ;
  413. BEGIN
  414. Ebyte (93H)
  415. END EmXchgAxCx ;
  416. PROCEDURE EmXorAxAx () ;
  417. BEGIN
  418. Ebyte (33H) ; Ebyte (0C0H)
  419. END EmXorAxAx ;
  420. PROCEDURE EmAddAxCx () ;
  421. BEGIN
  422. Ebyte (3H) ; Ebyte (0C1H)
  423. END EmAddAxCx ;
  424. PROCEDURE EmSubAxCx () ;
  425. BEGIN
  426. Ebyte (2BH) ; Ebyte (0C1H)
  427. END EmSubAxCx ;
  428. PROCEDURE EmMulAxCx () ;
  429. BEGIN
  430. Ebyte (0F7H) ; Ebyte (0E9H)
  431. END EmMulAxCx ;
  432. PROCEDURE EmIDivAxCx () ;
  433. BEGIN
  434. Ebyte (99H) ; Ebyte (0F7H) ; Ebyte (0F9H)
  435. END EmIDivAxCx ;
  436. PROCEDURE EmAndAxCx () ;
  437. BEGIN
  438. Ebyte (23H) ; Ebyte (0C1H)
  439. END EmAndAxCx ;
  440. PROCEDURE EmOrAxCx () ;
  441. BEGIN
  442. Ebyte (0BH) ; Ebyte (0C1H)
  443. END EmOrAxCx ;
  444. PROCEDURE EmNegAx () ;
  445. BEGIN
  446. Ebyte (0F7H) ; Ebyte (0D8H)
  447. END EmNegAx ;
  448. PROCEDURE EmNotAx () ;
  449. BEGIN
  450. Ebyte (0F7H) ; Ebyte (0D0H)
  451. END EmNotAx ;
  452. PROCEDURE EmCmpAxCx () ;
  453. BEGIN
  454. Ebyte (3BH) ; Ebyte (0C1H)
  455. END EmCmpAxCx ;
  456. PROCEDURE EmCmpAxi (imm : CARDINAL) ;
  457. BEGIN
  458. Ebyte (03DH) ; Eword (imm)
  459. END EmCmpAxi ;
  460. PROCEDURE EmSetcc (cc : BYTE) ;
  461. BEGIN
  462. Ebyte (0FH) ; Ebyte (cc) ; Ebyte (0C0H) ;
  463. EmMovAh0 ()
  464. END EmSetcc ;
  465. PROCEDURE EmIncAx () ;
  466. BEGIN
  467. Ebyte (40H)
  468. END EmIncAx ;
  469. PROCEDURE EmDecAx () ;
  470. BEGIN
  471. Ebyte (48H)
  472. END EmDecAx ;
  473. PROCEDURE EmLoadVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
  474. VAR disp : CARDINAL ;
  475. BEGIN
  476. disp := off MOD 100H ;
  477. IF nbytes = 1 THEN
  478. IF local THEN
  479. Ebyte (8AH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
  480. ELSE
  481. Ebyte (0A0H) ; Eword (off)
  482. END
  483. ELSE
  484. IF local THEN
  485. Ebyte (8BH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
  486. ELSE
  487. Ebyte (0A1H) ; Eword (off)
  488. END
  489. END
  490. END EmLoadVar ;
  491. PROCEDURE EmStoreVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
  492. VAR disp : CARDINAL ;
  493. BEGIN
  494. disp := off MOD 100H ;
  495. IF nbytes = 1 THEN
  496. IF local THEN
  497. Ebyte (88H) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
  498. ELSE
  499. Ebyte (0A2H) ; Eword (off)
  500. END
  501. ELSE
  502. IF local THEN
  503. Ebyte (89H) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
  504. ELSE
  505. Ebyte (0A3H) ; Eword (off)
  506. END
  507. END
  508. END EmStoreVar ;
  509. PROCEDURE EmPushVarAddr (local : BOOLEAN ; off : CARDINAL) ;
  510. (* LEA AX,[BP+disp] / LEA AX,[off] then PUSH AX - READ passes the address of
  511. a variable, not its value. 8D 46 disp is LEA AX,[BP+disp] and 8D 06 off
  512. is LEA AX,[off] (mod=00 rm=110 = direct disp16), both 8086-legal. *)
  513. VAR disp : CARDINAL ;
  514. BEGIN
  515. disp := off MOD 100H ;
  516. IF local THEN
  517. Ebyte (8DH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
  518. ELSE
  519. Ebyte (8DH) ; Ebyte (06H) ; Eword (off)
  520. END ;
  521. EmPushAx ()
  522. END EmPushVarAddr ;
  523. PROCEDURE EmSubSp (n : CARDINAL) ;
  524. BEGIN
  525. Ebyte (81H) ; Ebyte (0ECH) ; Eword (n MOD 10000H)
  526. END EmSubSp ;
  527. PROCEDURE EmAddSp (n : CARDINAL) ;
  528. BEGIN
  529. IF n <= 126 THEN
  530. Ebyte (83H) ; Ebyte (0C4H) ; Ebyte (VAL (BYTE, n))
  531. ELSE
  532. Ebyte (81H) ; Ebyte (0C4H) ; Eword (n MOD 10000H)
  533. END
  534. END EmAddSp ;
  535. PROCEDURE EmPushBp () ;
  536. BEGIN
  537. Ebyte (55H)
  538. END EmPushBp ;
  539. PROCEDURE EmLeave () ;
  540. BEGIN
  541. Ebyte (0C9H)
  542. END EmLeave ;
  543. PROCEDURE EmRet () ;
  544. BEGIN
  545. Ebyte (0C3H)
  546. END EmRet ;
  547. (* ---------------------------------------------------------------- *)
  548. (* symbol table *)
  549. (* ---------------------------------------------------------------- *)
  550. PROCEDURE NameEq (a : ARRAY OF CHAR ; b : ARRAY OF CHAR) : BOOLEAN ;
  551. VAR i : CARDINAL ;
  552. BEGIN
  553. i := 0 ;
  554. LOOP
  555. IF i > HIGH (a) THEN
  556. RETURN FALSE
  557. END ;
  558. IF i > HIGH (b) THEN
  559. RETURN FALSE
  560. END ;
  561. IF a [i] # b [i] THEN
  562. RETURN FALSE
  563. END ;
  564. IF a [i] = 0C THEN
  565. RETURN TRUE
  566. END ;
  567. INC (i)
  568. END
  569. END NameEq ;
  570. PROCEDURE CopyStr (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
  571. VAR i : CARDINAL ;
  572. BEGIN
  573. i := 0 ;
  574. LOOP
  575. IF i > HIGH (dst) THEN
  576. dst [HIGH (dst)] := 0C ;
  577. RETURN
  578. END ;
  579. IF i > HIGH (src) THEN
  580. dst [i] := 0C ;
  581. RETURN
  582. END ;
  583. dst [i] := src [i] ;
  584. IF src [i] = 0C THEN
  585. RETURN
  586. END ;
  587. INC (i)
  588. END
  589. END CopyStr ;
  590. PROCEDURE CopyWord (name : ARRAY OF CHAR) ;
  591. (* stash current word into global wrd (uppercased) *)
  592. VAR i : CARDINAL ;
  593. BEGIN
  594. i := 0 ;
  595. LOOP
  596. IF i > HIGH (name) THEN
  597. wrd [i] := 0C ;
  598. RETURN
  599. END ;
  600. IF i > MaxName THEN
  601. wrd [MaxName] := 0C ;
  602. RETURN
  603. END ;
  604. IF name [i] = 0C THEN
  605. wrd [i] := 0C ;
  606. RETURN
  607. END ;
  608. wrd [i] := Upper (name [i]) ;
  609. INC (i)
  610. END
  611. END CopyWord ;
  612. PROCEDURE SaveWord (VAR dst : ARRAY OF CHAR) ;
  613. (* copy wrd into dst *)
  614. VAR i : CARDINAL ;
  615. BEGIN
  616. i := 0 ;
  617. LOOP
  618. IF i > HIGH (dst) THEN
  619. dst [HIGH (dst)] := 0C ;
  620. RETURN
  621. END ;
  622. IF i > MaxName THEN
  623. dst [MaxName] := 0C ;
  624. RETURN
  625. END ;
  626. dst [i] := wrd [i] ;
  627. IF wrd [i] = 0C THEN
  628. RETURN
  629. END ;
  630. INC (i)
  631. END
  632. END SaveWord ;
  633. PROCEDURE NumToName (n : CARDINAL ; VAR dst : ARRAY OF CHAR) ;
  634. VAR buf : ARRAY [0..9] OF CHAR ;
  635. i, j : CARDINAL ;
  636. BEGIN
  637. IF n = 0 THEN
  638. dst [0] := '0' ;
  639. dst [1] := 0C ;
  640. RETURN
  641. END ;
  642. i := 0 ;
  643. WHILE n > 0 DO
  644. IF i <= 9 THEN
  645. buf [i] := CHR (ORD ('0') + (n MOD 10)) ;
  646. INC (i)
  647. END ;
  648. n := n DIV 10
  649. END ;
  650. j := 0 ;
  651. WHILE i > 0 DO
  652. DEC (i) ;
  653. IF j <= HIGH (dst) THEN
  654. dst [j] := buf [i] ;
  655. INC (j)
  656. END
  657. END ;
  658. IF j <= HIGH (dst) THEN
  659. dst [j] := 0C
  660. END
  661. END NumToName ;
  662. PROCEDURE NewSym (name : ARRAY OF CHAR ; tag : CARDINAL ;
  663. cls, size, elem, off : CARDINAL ; v : LONGINT ;
  664. local : BOOLEAN) : CARDINAL ;
  665. VAR e : SymEntry ;
  666. BEGIN
  667. IF symTop >= MaxSym THEN
  668. Err (ECompOvf) ;
  669. RETURN 0
  670. END ;
  671. CopyWord (name) ;
  672. CopyStr (e.name, wrd) ;
  673. e.tag := tag ;
  674. e.cls := cls ;
  675. e.size := size ;
  676. e.elem := elem ;
  677. e.off := off ;
  678. e.lval := v ;
  679. e.level := lexnest ;
  680. e.local := local ;
  681. e.resvar := 0 ;
  682. e.goPos := 0 ;
  683. e.defnd := FALSE ;
  684. e.fwd := FALSE ;
  685. symtab [symTop] := e ;
  686. INC (symTop) ;
  687. RETURN symTop - 1
  688. END NewSym ;
  689. PROCEDURE Search (nm : ARRAY OF CHAR ; VAR idx : CARDINAL) : BOOLEAN ;
  690. (* find nm among symbols visible at the current lexical level *)
  691. VAR p : CARDINAL ;
  692. BEGIN
  693. CopyWord (nm) ;
  694. p := symTop ;
  695. WHILE p > 0 DO
  696. DEC (p) ;
  697. IF symtab [p].level <= lexnest THEN
  698. IF NameEq (symtab [p].name, wrd) THEN
  699. idx := p ;
  700. RETURN TRUE
  701. END
  702. END
  703. END ;
  704. RETURN FALSE
  705. END Search ;
  706. PROCEDURE DupTest (nm : ARRAY OF CHAR) ;
  707. VAR i : CARDINAL ;
  708. BEGIN
  709. IF Search (nm, i) THEN
  710. Err (EUnknown)
  711. END
  712. END DupTest ;
  713. (* ---------------------------------------------------------------- *)
  714. (* lexer *)
  715. (* ---------------------------------------------------------------- *)
  716. PROCEDURE InitKeys () ;
  717. VAR i : CARDINAL ;
  718. BEGIN
  719. FOR i := 0 TO 42 DO
  720. kTk [i] := 0 ;
  721. kName [i] [0] := 0C
  722. END ;
  723. kName [1] := "PROGRAM" ; kTk [1] := TkProgram ;
  724. kName [2] := "BEGIN" ; kTk [2] := TkBegin ;
  725. kName [3] := "END" ; kTk [3] := TkEnd ;
  726. kName [4] := "IF" ; kTk [4] := TkIf ;
  727. kName [5] := "THEN" ; kTk [5] := TkThen ;
  728. kName [6] := "ELSE" ; kTk [6] := TkElse ;
  729. kName [7] := "WHILE" ; kTk [7] := TkWhile ;
  730. kName [8] := "DO" ; kTk [8] := TkDo ;
  731. kName [9] := "REPEAT" ; kTk [9] := TkRepeat ;
  732. kName [10] := "UNTIL" ; kTk [10] := TkUntil ;
  733. kName [11] := "FOR" ; kTk [11] := TkFor ;
  734. kName [12] := "TO" ; kTk [12] := TkTo ;
  735. kName [13] := "DOWNTO" ; kTk [13] := TkDownto ;
  736. kName [14] := "CASE" ; kTk [14] := TkCase ;
  737. kName [15] := "OF" ; kTk [15] := TkOf ;
  738. kName [16] := "GOTO" ; kTk [16] := TkGoto ;
  739. kName [17] := "EXIT" ; kTk [17] := TkExit ;
  740. kName [18] := "WITH" ; kTk [18] := TkWith ;
  741. kName [19] := "VAR" ; kTk [19] := TkVar ;
  742. kName [20] := "CONST" ; kTk [20] := TkConst ;
  743. kName [21] := "TYPE" ; kTk [21] := TkType ;
  744. kName [22] := "LABEL" ; kTk [22] := TkLabel ;
  745. kName [23] := "PROCEDURE" ; kTk [23] := TkProcedure ;
  746. kName [24] := "FUNCTION" ; kTk [24] := TkFunction ;
  747. kName [25] := "NIL" ; kTk [25] := TkNil ;
  748. kName [26] := "AND" ; kTk [26] := TkAnd ;
  749. kName [27] := "OR" ; kTk [27] := TkOr ;
  750. kName [28] := "NOT" ; kTk [28] := TkNot ;
  751. kName [29] := "DIV" ; kTk [29] := TkDiv ;
  752. kName [30] := "MOD" ; kTk [30] := TkMod ;
  753. kName [31] := "IN" ; kTk [31] := TkIn ;
  754. kName [32] := "FILE" ; kTk [32] := TkFile ;
  755. kName [33] := "TEXT" ; kTk [33] := TkText ;
  756. kName [34] := "RECORD" ; kTk [34] := TkRecord ;
  757. kName [35] := "ARRAY" ; kTk [35] := TkArray ;
  758. kName [36] := "SET" ; kTk [36] := TkSet ;
  759. kName [37] := "PACKED" ; kTk [37] := TkPacked ;
  760. kName [38] := "FORWARD" ; kTk [38] := TkForward ;
  761. kName [39] := "EXTERNAL" ; kTk [39] := TkExternal ;
  762. kName [40] := "ABSOLUTE" ; kTk [40] := TkAbsolute ;
  763. kName [41] := "OVERLAY" ; kTk [41] := TkOverlay ;
  764. kName [42] := "STRING" ; kTk [42] := TkString
  765. END InitKeys ;
  766. PROCEDURE IsBlank (ch : CHAR) : BOOLEAN ;
  767. BEGIN
  768. RETURN (ch = ' ') OR (ORD (ch) = 09H) OR (ORD (ch) = 0DH) OR (ORD (ch) = 0AH) OR (ORD (ch) = 0CH)
  769. END IsBlank ;
  770. PROCEDURE Skip () ;
  771. (* blanks, comments { } and (* *), compiler directives {$ } / (*$ *)
  772. letter + sign toggles rng/chk *)
  773. VAR ch : CHAR ;
  774. letter : CHAR ;
  775. BEGIN
  776. WHILE NOT abortFac DO
  777. WHILE IsBlank (CurCh ()) DO
  778. ch := GetCh ()
  779. END ;
  780. IF CurCh () = '{' THEN
  781. ch := GetCh () ;
  782. IF CurCh () = '$' THEN
  783. ch := GetCh () ;
  784. letter := GetCh () ;
  785. ch := GetCh () ;
  786. IF ch = '+' THEN
  787. IF letter = 'R' THEN dirs.rng := TRUE END ;
  788. IF letter = 'I' THEN dirs.chk := TRUE END
  789. ELSIF ch = '-' THEN
  790. IF letter = 'R' THEN dirs.rng := FALSE END ;
  791. IF letter = 'I' THEN dirs.chk := FALSE END
  792. END
  793. END ;
  794. WHILE (CurCh () # '}') AND (CurCh () # 0C) DO
  795. ch := GetCh ()
  796. END ;
  797. IF CurCh () = '}' THEN
  798. ch := GetCh ()
  799. END
  800. ELSIF (CurCh () = '(') AND (PeekAhead (1) = '*') THEN
  801. ch := GetCh () ;
  802. ch := GetCh () ;
  803. IF CurCh () = '$' THEN
  804. ch := GetCh () ;
  805. letter := GetCh () ;
  806. ch := GetCh () ;
  807. IF ch = '+' THEN
  808. IF letter = 'R' THEN dirs.rng := TRUE END
  809. ELSIF ch = '-' THEN
  810. IF letter = 'R' THEN dirs.rng := FALSE END
  811. END
  812. END ;
  813. LOOP
  814. IF (CurCh () = '*') AND (PeekAhead (1) = ')') THEN
  815. ch := GetCh () ;
  816. ch := GetCh () ;
  817. EXIT
  818. END ;
  819. IF CurCh () = 0C THEN
  820. EXIT
  821. END ;
  822. ch := GetCh ()
  823. END
  824. ELSE
  825. RETURN
  826. END
  827. END
  828. END Skip ;
  829. PROCEDURE GetWord () ;
  830. (* read identifier into wrd (uppercased); next char must be alpha *)
  831. VAR i : CARDINAL ;
  832. ch : CHAR ;
  833. BEGIN
  834. i := 0 ;
  835. ch := GetCh () ;
  836. LOOP
  837. IF i > MaxName THEN
  838. wrd [MaxName] := 0C ;
  839. RETURN
  840. END ;
  841. wrd [i] := Upper (ch) ;
  842. INC (i) ;
  843. ch := CurCh () ;
  844. IF NOT AlphaNum (ch) THEN
  845. wrd [i] := 0C ;
  846. RETURN
  847. END ;
  848. ch := GetCh ()
  849. END
  850. END GetWord ;
  851. PROCEDURE WddTok () : CARDINAL ;
  852. (* map wrd -> keyword token *)
  853. VAR i : CARDINAL ;
  854. BEGIN
  855. i := 1 ;
  856. WHILE i <= 42 DO
  857. IF kName [i] [0] # 0C THEN
  858. IF NameEq (wrd, kName [i]) THEN
  859. RETURN kTk [i]
  860. END
  861. END ;
  862. INC (i)
  863. END ;
  864. RETURN TkNone
  865. END WddTok ;
  866. PROCEDURE KwAhead (word : ARRAY OF CHAR) : BOOLEAN ;
  867. (* does the next token (past blanks/comments) equal the keyword 'word',
  868. without consuming it? srcPos is saved and restored. *)
  869. VAR save : CARDINAL ;
  870. k : BOOLEAN ;
  871. BEGIN
  872. save := srcPos ;
  873. Skip () ;
  874. k := FALSE ;
  875. IF Alpha (CurCh ()) THEN
  876. GetWord () ;
  877. k := NameEq (wrd, word)
  878. END ;
  879. srcPos := save ;
  880. RETURN k
  881. END KwAhead ;
  882. PROCEDURE PeekKw (VAR tok : CARDINAL) ;
  883. (* peek at the next keyword token without consuming it *)
  884. VAR i : CARDINAL ;
  885. BEGIN
  886. tok := TkNone ;
  887. i := 1 ;
  888. WHILE i <= 42 DO
  889. IF kName [i] [0] # 0C THEN
  890. IF KwAhead (kName [i]) THEN
  891. tok := kTk [i] ;
  892. RETURN
  893. END
  894. END ;
  895. INC (i)
  896. END
  897. END PeekKw ;
  898. PROCEDURE MatchKey (VAR tok : CARDINAL) : BOOLEAN ;
  899. (* skip; if next symbol is a word, read it into wrd and set its token.
  900. Returns TRUE when a word was read (tok = TkNone for plain ids). *)
  901. BEGIN
  902. tok := TkNone ;
  903. Skip () ;
  904. IF NOT Alpha (CurCh ()) THEN
  905. RETURN FALSE
  906. END ;
  907. GetWord () ;
  908. tok := WddTok () ;
  909. RETURN TRUE
  910. END MatchKey ;
  911. PROCEDURE MatchDelim (ch : CHAR) : BOOLEAN ;
  912. BEGIN
  913. Skip () ;
  914. IF CurCh () = ch THEN
  915. DropCh (GetCh ()) ;
  916. RETURN TRUE
  917. END ;
  918. RETURN FALSE
  919. END MatchDelim ;
  920. PROCEDURE MatchAssign () : BOOLEAN ;
  921. BEGIN
  922. Skip () ;
  923. IF (CurCh () = ':') AND (PeekAhead (1) = '=') THEN
  924. DropCh (GetCh ()) ;
  925. DropCh (GetCh ()) ;
  926. RETURN TRUE
  927. END ;
  928. RETURN FALSE
  929. END MatchAssign ;
  930. PROCEDURE MatchRange () : BOOLEAN ;
  931. BEGIN
  932. Skip () ;
  933. IF (CurCh () = '.') AND (PeekAhead (1) = '.') THEN
  934. DropCh (GetCh ()) ;
  935. DropCh (GetCh ()) ;
  936. RETURN TRUE
  937. END ;
  938. RETURN FALSE
  939. END MatchRange ;
  940. PROCEDURE ExpectDelim (ch : CHAR ; n : CARDINAL) ;
  941. BEGIN
  942. Skip () ;
  943. IF CurCh () = ch THEN
  944. DropCh (GetCh ())
  945. ELSE
  946. Err (n)
  947. END
  948. END ExpectDelim ;
  949. PROCEDURE HexVal (ch : CHAR) : CARDINAL ;
  950. BEGIN
  951. IF (ch >= '0') AND (ch <= '9') THEN
  952. RETURN ORD (ch) - ORD ('0')
  953. ELSIF (ch >= 'A') AND (ch <= 'F') THEN
  954. RETURN ORD (ch) - ORD ('A') + 10
  955. END ;
  956. RETURN ORD (ch) - ORD ('a') + 10
  957. END HexVal ;
  958. PROCEDURE RdIntConst (VAR v : LONGINT) ;
  959. (* bare integer constant; current char is digit or '$' *)
  960. VAR acc : LONGINT ;
  961. BEGIN
  962. acc := 0 ;
  963. IF CurCh () = '$' THEN
  964. DropCh (GetCh ()) ;
  965. WHILE IsHexCh (CurCh ()) DO
  966. acc := acc * 16 + VAL (LONGINT, HexVal (CurCh ())) ;
  967. DropCh (GetCh ())
  968. END
  969. ELSE
  970. WHILE Digit (CurCh ()) DO
  971. acc := acc * 10 + VAL (LONGINT, ORD (CurCh ()) - ORD ('0')) ;
  972. DropCh (GetCh ())
  973. END
  974. END ;
  975. v := acc
  976. END RdIntConst ;
  977. PROCEDURE RdConst (VAR v : LONGINT ; VAR cls : CARDINAL ;
  978. VAR isStr : BOOLEAN) ;
  979. (* scalar or string constant. String values (isStr) can only be
  980. rejected with ENoLib by the caller. *)
  981. CONST q = AposC ;
  982. BEGIN
  983. isStr := FALSE ;
  984. cls := TScalar ;
  985. v := 0 ;
  986. Skip () ;
  987. IF CurCh () = '$' THEN
  988. RdIntConst (v) ;
  989. cls := TScalar
  990. ELSIF Digit (CurCh ()) THEN
  991. RdIntConst (v) ;
  992. IF (CurCh () = '.') AND (Digit (PeekAhead (1))) THEN
  993. cls := TReal ;
  994. DropCh (GetCh ())
  995. END ;
  996. IF (CurCh () = 'E') OR (CurCh () = 'e') THEN
  997. cls := TReal ;
  998. DropCh (GetCh ())
  999. END ;
  1000. IF cls = TReal THEN
  1001. WHILE AlphaNum (CurCh ()) OR (CurCh () = '.') OR (CurCh () = '-')
  1002. OR (CurCh () = '+') DO
  1003. DropCh (GetCh ())
  1004. END
  1005. END
  1006. ELSIF ORD (CurCh ()) = q THEN
  1007. DropCh (GetCh ()) ;
  1008. IF ORD (CurCh ()) = q THEN
  1009. DropCh (GetCh ()) ;
  1010. v := VAL (LONGINT, q) ;
  1011. cls := TScalar
  1012. ELSIF (CurCh () = 0C) OR ((ORD (CurCh ()) = 0DH)) OR ((ORD (CurCh ()) = 0AH)) THEN
  1013. Err (EUnknown)
  1014. ELSE
  1015. v := VAL (LONGINT, ORD (GetCh ())) ;
  1016. cls := TScalar ;
  1017. IF ORD (CurCh ()) = q THEN
  1018. DropCh (GetCh ())
  1019. ELSE
  1020. isStr := TRUE ;
  1021. cls := TString ;
  1022. (* Scan to the closing quote. NB: the loop condition must test
  1023. the CURRENT character and only the current character. The
  1024. obvious-looking "while CurCh # quote" with a
  1025. "if PeekAhead(1) = quote then consume two" body is wrong:
  1026. consuming the quote moves the cursor past it, so the next
  1027. condition test sees the character AFTER the literal, is
  1028. satisfied, and the scan runs on to end-of-buffer - which
  1029. silently eats the rest of the program and makes every later
  1030. error point at end-of-file. Stop on the quote itself, and
  1031. treat a doubled quote as one embedded quote character. *)
  1032. LOOP
  1033. IF ORD (CurCh ()) = q THEN
  1034. IF ORD (PeekAhead (1)) = q THEN
  1035. DropCh (GetCh ()) ; DropCh (GetCh ()) (* '' inside *)
  1036. ELSE
  1037. EXIT (* closing quote *)
  1038. END
  1039. ELSIF (CurCh () = 0C) OR (ORD (CurCh ()) = 0DH) THEN
  1040. Err (EUnknown) ; (* unterminated *)
  1041. EXIT
  1042. ELSE
  1043. DropCh (GetCh ())
  1044. END
  1045. END ;
  1046. IF ORD (CurCh ()) = q THEN
  1047. DropCh (GetCh ())
  1048. END
  1049. END
  1050. END
  1051. ELSE
  1052. Err (EUnknown)
  1053. END
  1054. END RdConst ;
  1055. (* ---------------------------------------------------------------- *)
  1056. (* forward declarations *)
  1057. (* ---------------------------------------------------------------- *)
  1058. PROCEDURE ParseExpr (VAR r : ERes) ; FORWARD ;
  1059. PROCEDURE ParseAdd (VAR r : ERes) ; FORWARD ;
  1060. PROCEDURE ParseMul (VAR r : ERes) ; FORWARD ;
  1061. PROCEDURE ParseNeg (VAR r : ERes) ; FORWARD ;
  1062. PROCEDURE ParseAtom (VAR r : ERes) ; FORWARD ;
  1063. PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ; FORWARD ;
  1064. PROCEDURE Statmnt () ; FORWARD ;
  1065. PROCEDURE ParseCall (idx : CARDINAL) ; FORWARD ;
  1066. PROCEDURE ParseCallArgs (idx : CARDINAL) ; FORWARD ;
  1067. PROCEDURE EmCallMost (idx, nk : CARDINAL) ; FORWARD ;
  1068. (* ---------------------------------------------------------------- *)
  1069. (* expressions (TPSRC9) *)
  1070. (* ---------------------------------------------------------------- *)
  1071. PROCEDURE LoadAtom (VAR r : ERes) ;
  1072. (* load value of r into AX (folding constants) *)
  1073. BEGIN
  1074. IF r.kind = 0 THEN
  1075. EmMovAxi (W16 (r.imm)) ;
  1076. r.kind := 2
  1077. ELSIF r.kind = 1 THEN
  1078. IF symtab [r.idx].size > 2 THEN
  1079. Err (ENoLib)
  1080. ELSE
  1081. EmLoadVar (symtab [r.idx].local,
  1082. (symtab [r.idx].off + r.boff) MOD 10000H,
  1083. symtab [r.idx].size) ;
  1084. IF symtab [r.idx].size = 1 THEN
  1085. EmMovAh0 ()
  1086. END ;
  1087. r.kind := 2
  1088. END
  1089. END
  1090. END LoadAtom ;
  1091. PROCEDURE ParseSub (VAR r : ERes) ;
  1092. (* consume '[' constExpr ']' while present, folding the index into the
  1093. base offset (constant indexing only) *)
  1094. VAR t : ERes ;
  1095. BEGIN
  1096. LOOP
  1097. Skip () ;
  1098. IF CurCh () # '[' THEN
  1099. RETURN
  1100. END ;
  1101. DropCh (GetCh ()) ;
  1102. ParseExpr (t) ;
  1103. IF OK () THEN
  1104. IF t.kind # 0 THEN
  1105. Err (ENoLib) ;
  1106. RETURN
  1107. END ;
  1108. IF symtab [r.idx].cls = TArray THEN
  1109. r.boff := W16 (VAL (LONGINT, r.boff)
  1110. + t.imm * VAL (LONGINT, symtab [r.idx].elem))
  1111. ELSE
  1112. Err (ESimpType) ;
  1113. RETURN
  1114. END
  1115. END ;
  1116. ExpectDelim (']', ENoSemi)
  1117. END
  1118. END ParseSub ;
  1119. PROCEDURE ParseVar (VAR r : ERes) ;
  1120. (* name [ '[' constExpr ']' ]* ; a function name maps to its result var *)
  1121. VAR idx : CARDINAL ;
  1122. BEGIN
  1123. IF NOT Search (wrd, idx) THEN
  1124. Err (EUnknown) ;
  1125. RETURN
  1126. END ;
  1127. r.idx := idx ;
  1128. r.kind := 1 ;
  1129. r.boff := 0 ;
  1130. r.cls := symtab [idx].cls ;
  1131. IF symtab [idx].tag = KFunc THEN
  1132. idx := symtab [idx].resvar ;
  1133. r.idx := idx ;
  1134. r.cls := symtab [idx].cls
  1135. ELSIF (symtab [idx].tag # KVar) AND (symtab [idx].tag # KType) THEN
  1136. Err (EUnknown) ;
  1137. RETURN
  1138. END ;
  1139. ParseSub (r)
  1140. END ParseVar ;
  1141. PROCEDURE ConstAdd (a, b : LONGINT) : LONGINT ;
  1142. BEGIN
  1143. RETURN VAL (LONGINT, W16 (a + b))
  1144. END ConstAdd ;
  1145. PROCEDURE ConstSub (a, b : LONGINT) : LONGINT ;
  1146. BEGIN
  1147. RETURN VAL (LONGINT, W16 (a - b))
  1148. END ConstSub ;
  1149. PROCEDURE ConstMul (a, b : LONGINT) : LONGINT ;
  1150. BEGIN
  1151. RETURN VAL (LONGINT, W16 (a * b))
  1152. END ConstMul ;
  1153. PROCEDURE EmMoveAxDx () ;
  1154. BEGIN
  1155. Ebyte (92H)
  1156. END EmMoveAxDx ;
  1157. PROCEDURE BinOpEmit (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
  1158. (* binary operation at one precedence level; folds constant operands *)
  1159. VAR f : LONGINT ;
  1160. okc : BOOLEAN ;
  1161. BEGIN
  1162. IF op = TkAnd THEN
  1163. IF (left.kind = 0) AND (right.kind = 0) THEN
  1164. res.kind := 0 ;
  1165. res.imm := BitAnd (left.imm, right.imm) ;
  1166. res.cls := TBool ;
  1167. RETURN
  1168. END ;
  1169. LoadAtom (left) ; EmPushAx () ;
  1170. LoadAtom (right) ; EmPopCx () ;
  1171. EmAndAxCx () ;
  1172. res.kind := 2 ; res.cls := TBool ;
  1173. RETURN
  1174. END ;
  1175. IF op = TkOr THEN
  1176. IF (left.kind = 0) AND (right.kind = 0) THEN
  1177. res.kind := 0 ;
  1178. res.imm := BitOr (left.imm, right.imm) ;
  1179. res.cls := TBool ;
  1180. RETURN
  1181. END ;
  1182. LoadAtom (left) ; EmPushAx () ;
  1183. LoadAtom (right) ; EmPopCx () ;
  1184. EmOrAxCx () ;
  1185. res.kind := 2 ; res.cls := TBool ;
  1186. RETURN
  1187. END ;
  1188. IF (left.kind = 0) AND (right.kind = 0) THEN
  1189. okc := FALSE ;
  1190. CASE op OF
  1191. 1 : f := ConstAdd (left.imm, right.imm) ; okc := TRUE ;
  1192. | 2 : f := ConstSub (left.imm, right.imm) ; okc := TRUE ;
  1193. | 3 : f := ConstMul (left.imm, right.imm) ; okc := TRUE ;
  1194. | TkDiv : okc := (right.imm # 0) AND (right.imm > 0)
  1195. AND (left.imm >= 0) ;
  1196. IF okc THEN f := left.imm DIV right.imm END ;
  1197. | TkMod : okc := (right.imm # 0) AND (right.imm > 0)
  1198. AND (left.imm >= 0) ;
  1199. IF okc THEN f := left.imm MOD right.imm END ;
  1200. ELSE
  1201. okc := FALSE
  1202. END ;
  1203. IF okc THEN
  1204. res.kind := 0 ;
  1205. res.imm := VAL (LONGINT, W16 (f)) ;
  1206. res.cls := left.cls ;
  1207. RETURN
  1208. ELSIF op = TkDiv THEN
  1209. Err (EConstRange) ;
  1210. RETURN
  1211. END
  1212. END ;
  1213. LoadAtom (left) ; EmPushAx () ;
  1214. LoadAtom (right) ; EmPopCx () ;
  1215. EmXchgAxCx () ;
  1216. CASE op OF
  1217. 1 : EmAddAxCx ;
  1218. | 2 : EmSubAxCx ;
  1219. | 3 : EmMulAxCx ;
  1220. | TkDiv : EmIDivAxCx ;
  1221. | TkMod : EmIDivAxCx ; EmMoveAxDx ;
  1222. ELSE
  1223. Err (ETypeErr)
  1224. END ;
  1225. res.kind := 2 ;
  1226. res.cls := left.cls
  1227. END BinOpEmit ;
  1228. PROCEDURE ConstCmp (op : CARDINAL ; a, b : LONGINT ; VAR f : LONGINT)
  1229. : BOOLEAN ;
  1230. VAR a16, b16 : CARDINAL ;
  1231. BEGIN
  1232. a16 := W16 (a) ;
  1233. b16 := W16 (b) ;
  1234. f := 0 ;
  1235. CASE op OF
  1236. 1 : f := VAL (LONGINT, ORD (a16 = b16)) ;
  1237. | 2 : f := VAL (LONGINT, ORD (a16 # b16)) ;
  1238. | 3 : f := VAL (LONGINT, ORD (a16 < b16)) ;
  1239. | 4 : f := VAL (LONGINT, ORD (a16 >= b16)) ;
  1240. | 5 : f := VAL (LONGINT, ORD (a16 > b16)) ;
  1241. | 6 : f := VAL (LONGINT, ORD (a16 <= b16)) ;
  1242. ELSE
  1243. RETURN FALSE
  1244. END ;
  1245. RETURN TRUE
  1246. END ConstCmp ;
  1247. PROCEDURE ParseCmp (VAR r : ERes) ;
  1248. (* "=" | "<>" | "<" | "<=" | ">" | ">=" *)
  1249. VAR op : CARDINAL ;
  1250. left, right : ERes ;
  1251. f : LONGINT ;
  1252. BEGIN
  1253. ParseAdd (r) ;
  1254. LOOP
  1255. op := 0 ;
  1256. Skip () ;
  1257. IF CurCh () = '=' THEN
  1258. op := 1 ; DropCh (GetCh ())
  1259. ELSIF (CurCh () = '<') AND (PeekAhead (1) = '>') THEN
  1260. op := 2 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1261. ELSIF CurCh () = '<' THEN
  1262. IF PeekAhead (1) = '=' THEN
  1263. op := 6 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1264. ELSE
  1265. op := 3 ; DropCh (GetCh ())
  1266. END
  1267. ELSIF CurCh () = '>' THEN
  1268. IF PeekAhead (1) = '=' THEN
  1269. op := 5 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1270. ELSE
  1271. op := 4 ; DropCh (GetCh ())
  1272. END
  1273. END ;
  1274. IF op = 0 THEN
  1275. RETURN
  1276. END ;
  1277. left := r ;
  1278. ParseAdd (right) ;
  1279. IF (left.kind = 0) AND (right.kind = 0) THEN
  1280. IF ConstCmp (op, left.imm, right.imm, f) THEN
  1281. r.kind := 0 ;
  1282. r.imm := f ;
  1283. r.cls := TBool
  1284. ELSE
  1285. r.kind := 0 ;
  1286. r.imm := 0 ;
  1287. r.cls := TBool
  1288. END
  1289. ELSE
  1290. LoadAtom (left) ; EmPushAx () ;
  1291. LoadAtom (right) ; EmPopCx () ;
  1292. EmXchgAxCx () ;
  1293. EmCmpAxCx () ;
  1294. CASE op OF
  1295. 1 : EmSetcc (94H) ;
  1296. | 2 : EmSetcc (95H) ;
  1297. | 3 : EmSetcc (9CH) ;
  1298. | 4 : EmSetcc (9DH) ;
  1299. | 5 : EmSetcc (9FH) ;
  1300. | 6 : EmSetcc (9EH)
  1301. END ;
  1302. r.kind := 2 ;
  1303. r.cls := TBool
  1304. END
  1305. END
  1306. END ParseCmp ;
  1307. PROCEDURE ParseAdd (VAR r : ERes) ;
  1308. VAR op : CARDINAL ;
  1309. left, right : ERes ;
  1310. BEGIN
  1311. ParseMul (r) ;
  1312. LOOP
  1313. op := 0 ;
  1314. Skip () ;
  1315. IF CurCh () = '+' THEN
  1316. op := 1 ; DropCh (GetCh ())
  1317. ELSIF CurCh () = '-' THEN
  1318. op := 2 ; DropCh (GetCh ())
  1319. ELSIF KwAhead ("OR") THEN
  1320. GetWord () ;
  1321. op := TkOr
  1322. ELSE
  1323. RETURN
  1324. END ;
  1325. left := r ;
  1326. ParseMul (right) ;
  1327. BinOpEmit (op, left, right, r)
  1328. END
  1329. END ParseAdd ;
  1330. PROCEDURE ParseMul (VAR r : ERes) ;
  1331. VAR op : CARDINAL ;
  1332. left, right : ERes ;
  1333. BEGIN
  1334. ParseNeg (r) ;
  1335. LOOP
  1336. op := 0 ;
  1337. Skip () ;
  1338. IF CurCh () = '*' THEN
  1339. op := 1 ; DropCh (GetCh ())
  1340. ELSIF CurCh () = '/' THEN
  1341. op := 2 ; DropCh (GetCh ()) ; Err (ENoLib)
  1342. ELSIF KwAhead ("DIV") THEN
  1343. GetWord () ; op := TkDiv
  1344. ELSIF KwAhead ("MOD") THEN
  1345. GetWord () ; op := TkMod
  1346. ELSIF KwAhead ("AND") THEN
  1347. GetWord () ; op := TkAnd
  1348. ELSE
  1349. RETURN
  1350. END ;
  1351. IF op = 2 THEN
  1352. RETURN
  1353. END ;
  1354. left := r ;
  1355. ParseNeg (right) ;
  1356. BinOpEmit (op, left, right, r)
  1357. END
  1358. END ParseMul ;
  1359. PROCEDURE ParseNeg (VAR r : ERes) ;
  1360. BEGIN
  1361. Skip () ;
  1362. IF CurCh () = '+' THEN
  1363. DropCh (GetCh ()) ;
  1364. ParseNeg (r) ;
  1365. RETURN
  1366. ELSIF CurCh () = '-' THEN
  1367. DropCh (GetCh ()) ;
  1368. ParseNeg (r) ;
  1369. IF r.kind = 0 THEN
  1370. r.imm := VAL (LONGINT, W16 (0 - r.imm))
  1371. ELSE
  1372. LoadAtom (r) ;
  1373. EmNegAx () ;
  1374. r.kind := 2
  1375. END ;
  1376. RETURN
  1377. ELSIF KwAhead ("NOT") THEN
  1378. GetWord () ;
  1379. ParseNeg (r) ;
  1380. IF r.kind = 0 THEN
  1381. r.imm := BitNot (r.imm)
  1382. ELSE
  1383. LoadAtom (r) ;
  1384. EmNotAx () ;
  1385. r.kind := 2
  1386. END ;
  1387. RETURN
  1388. END ;
  1389. ParseAtom (r)
  1390. END ParseNeg ;
  1391. PROCEDURE ParseAtom (VAR r : ERes) ;
  1392. (* const | variable | func(params) | '(' expr ')' *)
  1393. VAR idx : CARDINAL ;
  1394. strf : BOOLEAN ;
  1395. quoted : BOOLEAN ;
  1396. BEGIN
  1397. r.chr := FALSE ; (* default: not a quoted char literal *)
  1398. Skip () ;
  1399. IF CurCh () = '(' THEN
  1400. DropCh (GetCh ()) ;
  1401. ParseExpr (r) ;
  1402. ExpectDelim (')', ENoSemi) ;
  1403. RETURN
  1404. END ;
  1405. IF (CurCh () = '$') OR (Digit (CurCh ())) OR (ORD (CurCh ()) = AposC) THEN
  1406. (* remember that this was a quoted literal BEFORE RdConst consumes it:
  1407. RdConst reports a 1-character literal as TScalar (its char code),
  1408. which is right for "c := 'a'" but would make writeln('a') print 97.
  1409. Mark it so the writer picks the char entry, not the integer one. *)
  1410. quoted := (ORD (CurCh ()) = AposC) ;
  1411. RdConst (r.imm, r.cls, strf) ;
  1412. r.chr := quoted AND (r.cls = TScalar) AND NOT strf ;
  1413. IF r.cls = TReal THEN
  1414. Err (ENoLib) ;
  1415. r.kind := 2 ;
  1416. RETURN
  1417. END ;
  1418. IF strf THEN
  1419. Err (ENoLib) ;
  1420. r.kind := 2 ;
  1421. RETURN
  1422. END ;
  1423. r.kind := 0 ;
  1424. RETURN
  1425. END ;
  1426. IF NOT Alpha (CurCh ()) THEN
  1427. Err (EUnknown) ;
  1428. RETURN
  1429. END ;
  1430. GetWord () ;
  1431. IF NOT Search (wrd, idx) THEN
  1432. Err (EUnknown) ;
  1433. RETURN
  1434. END ;
  1435. IF symtab [idx].tag = KConst THEN
  1436. r.kind := 0 ;
  1437. r.imm := symtab [idx].lval ;
  1438. r.cls := symtab [idx].cls ;
  1439. RETURN
  1440. ELSIF symtab [idx].tag = KFunc THEN
  1441. ParseCall (idx) ;
  1442. r.kind := 2 ;
  1443. r.cls := symtab [idx].cls ;
  1444. RETURN
  1445. ELSIF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KType) THEN
  1446. ParseVar (r) ;
  1447. r.boff := 0 ;
  1448. RETURN
  1449. ELSE
  1450. Err (EUnknown)
  1451. END
  1452. END ParseAtom ;
  1453. PROCEDURE AddPend (kind, who, place : CARDINAL) ;
  1454. BEGIN
  1455. IF nPend < MaxPend THEN
  1456. pend [nPend].kind := kind ;
  1457. pend [nPend].who := who ;
  1458. pend [nPend].place := place ;
  1459. INC (nPend)
  1460. ELSE
  1461. Err (ECompOvf)
  1462. END
  1463. END AddPend ;
  1464. PROCEDURE EmCallMost (idx, nk : CARDINAL) ;
  1465. (* emit the call to sym 'idx' and clean up nk value arguments *)
  1466. VAR p : CARDINAL ;
  1467. BEGIN
  1468. IF symtab [idx].defnd THEN
  1469. DropC (EmCall (symtab [idx].goPos))
  1470. ELSE
  1471. p := EmCall (0) ;
  1472. AddPend (1, idx, p)
  1473. END ;
  1474. IF nk > 0 THEN
  1475. EmAddSp (2 * nk)
  1476. END
  1477. END EmCallMost ;
  1478. PROCEDURE ParseCallArgs (idx : CARDINAL) ;
  1479. (* '(' already consumed: read args ')' then call. Arguments are pushed
  1480. right-to-left so the first-declared parameter lands at BP+4. *)
  1481. VAR args : ARRAY [0..15] OF ERes ;
  1482. nArgs, i : CARDINAL ;
  1483. BEGIN
  1484. nArgs := 0 ;
  1485. IF CurCh () = ')' THEN
  1486. DropCh (GetCh ())
  1487. ELSE
  1488. LOOP
  1489. IF nArgs >= 16 THEN
  1490. Err (ECompOvf) ;
  1491. EXIT
  1492. END ;
  1493. ParseExpr (args [nArgs]) ;
  1494. INC (nArgs) ;
  1495. IF NOT MatchDelim (',') THEN
  1496. EXIT
  1497. END
  1498. END ;
  1499. ExpectDelim (')', ENoSemi)
  1500. END ;
  1501. i := nArgs ;
  1502. WHILE i > 0 DO
  1503. DEC (i) ;
  1504. LoadAtom (args [i]) ;
  1505. EmPushAx ()
  1506. END ;
  1507. EmCallMost (idx, nArgs)
  1508. END ParseCallArgs ;
  1509. PROCEDURE ParseCall (idx : CARDINAL) ;
  1510. (* procedure/function call; '(' optional *)
  1511. VAR args : ARRAY [0..15] OF ERes ;
  1512. nArgs, i : CARDINAL ;
  1513. BEGIN
  1514. nArgs := 0 ;
  1515. IF MatchDelim ('(') THEN
  1516. IF CurCh () # ')' THEN
  1517. LOOP
  1518. IF nArgs >= 16 THEN
  1519. Err (ECompOvf) ;
  1520. EXIT
  1521. END ;
  1522. ParseExpr (args [nArgs]) ;
  1523. INC (nArgs) ;
  1524. IF NOT MatchDelim (',') THEN
  1525. EXIT
  1526. END
  1527. END ;
  1528. ExpectDelim (')', ENoSemi)
  1529. ELSE
  1530. DropCh (GetCh ())
  1531. END
  1532. END ;
  1533. i := nArgs ;
  1534. WHILE i > 0 DO
  1535. DEC (i) ;
  1536. LoadAtom (args [i]) ;
  1537. EmPushAx ()
  1538. END ;
  1539. EmCallMost (idx, nArgs)
  1540. END ParseCall ;
  1541. PROCEDURE ParseExpr (VAR r : ERes) ;
  1542. BEGIN
  1543. ParseCmp (r)
  1544. END ParseExpr ;
  1545. (* ---------------------------------------------------------------- *)
  1546. (* statements (TPSRC8) *)
  1547. (* ---------------------------------------------------------------- *)
  1548. PROCEDURE ParseLabelStmt () ;
  1549. (* numeric label definition 'n :' *)
  1550. VAR n : CARDINAL ;
  1551. nm : ARRAY [0..9] OF CHAR ;
  1552. idx : CARDINAL ;
  1553. i : CARDINAL ;
  1554. BEGIN
  1555. n := 0 ;
  1556. WHILE Digit (CurCh ()) DO
  1557. n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
  1558. DropCh (GetCh ())
  1559. END ;
  1560. ExpectDelim (':', ENoSemi) ;
  1561. NumToName (n, nm) ;
  1562. IF Search (nm, idx) THEN
  1563. IF symtab [idx].tag = KLabel THEN
  1564. symtab [idx].defnd := TRUE ;
  1565. symtab [idx].goPos := pc ;
  1566. i := 0 ;
  1567. WHILE i < nPend DO
  1568. IF (pend [i].kind = 0) AND (pend [i].who = idx) THEN
  1569. SetPatTgt (pend [i].place, pc) ;
  1570. pend [i].kind := 99
  1571. END ;
  1572. INC (i)
  1573. END
  1574. ELSE
  1575. Err (EUnknown)
  1576. END
  1577. ELSE
  1578. idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
  1579. symtab [idx].defnd := TRUE ;
  1580. symtab [idx].goPos := pc
  1581. END
  1582. END ParseLabelStmt ;
  1583. PROCEDURE Assignment (r : ERes) ;
  1584. (* ':=' already consumed by the caller; store expression into r *)
  1585. VAR src : ERes ;
  1586. BEGIN
  1587. IF r.kind # 1 THEN
  1588. Err (EUnknown) ;
  1589. RETURN
  1590. END ;
  1591. IF symtab [r.idx].size > 2 THEN
  1592. Err (ENoLib) ;
  1593. RETURN
  1594. END ;
  1595. ParseExpr (src) ;
  1596. LoadAtom (src) ;
  1597. EmStoreVar (symtab [r.idx].local,
  1598. (symtab [r.idx].off + r.boff) MOD 10000H,
  1599. symtab [r.idx].size)
  1600. END Assignment ;
  1601. PROCEDURE Compound () ;
  1602. (* BEGIN statement ';' ... END; END is consumed here *)
  1603. VAR tok : CARDINAL ;
  1604. BEGIN
  1605. LOOP
  1606. PeekKw (tok) ;
  1607. IF tok = TkEnd THEN
  1608. DropB (MatchKey (tok)) ;
  1609. RETURN
  1610. END ;
  1611. Statmnt () ;
  1612. IF NOT OK () THEN
  1613. RETURN
  1614. END ;
  1615. IF NOT MatchDelim (';') THEN
  1616. PeekKw (tok) ;
  1617. IF tok = TkEnd THEN
  1618. DropB (MatchKey (tok)) ;
  1619. RETURN
  1620. END ;
  1621. Err (ENoSemi) ;
  1622. RETURN
  1623. END
  1624. END
  1625. END Compound ;
  1626. PROCEDURE IoCall (idx : CARDINAL) ;
  1627. (* WRITE / WRITELN / READ / READLN / HALT.
  1628. TP3 (TPSRC8 pwriteln, pwrloop, prdtyped) does not pass a descriptor to
  1629. the runtime: it looks at each argument's class and emits a *different*
  1630. call per type, so the formatting is fixed at compile time. Mirrored
  1631. here - one call per argument, then a final call for the line break.
  1632. WRITE/WRITELN push the value; READ/READLN push the address, so the
  1633. runtime can store. As everywhere else in this compiler the caller
  1634. cleans the argument off the stack. *)
  1635. VAR args : ARRAY [0..15] OF ERes ;
  1636. nArgs, i, ent, which, acls : CARDINAL ;
  1637. reading : BOOLEAN ;
  1638. dummy : ERes ;
  1639. BEGIN
  1640. which := symtab [idx].cls ; (* BI_* *)
  1641. IF which = BI_Halt THEN
  1642. IF MatchDelim ('(') THEN (* halt(0) - code ignored *)
  1643. ParseExpr (dummy) ;
  1644. ExpectDelim (')', ENoSemi)
  1645. END ;
  1646. DropC (EmCall (TU_Halt)) ;
  1647. RETURN
  1648. END ;
  1649. reading := (which = BI_Read) OR (which = BI_ReadLn) ;
  1650. nArgs := 0 ;
  1651. IF MatchDelim ('(') THEN
  1652. IF CurCh () # ')' THEN
  1653. LOOP
  1654. IF nArgs >= 16 THEN
  1655. Err (ECompOvf) ;
  1656. EXIT
  1657. END ;
  1658. ParseExpr (args [nArgs]) ;
  1659. INC (nArgs) ;
  1660. IF NOT MatchDelim (',') THEN
  1661. EXIT
  1662. END
  1663. END ;
  1664. IF NOT MatchDelim (')') THEN
  1665. Err (ENoSemi) ;
  1666. RETURN
  1667. END
  1668. ELSE
  1669. DropCh (GetCh ())
  1670. END
  1671. END ;
  1672. IF reading AND (nArgs = 0) THEN
  1673. (* readln with no variable: just skip to the next line *)
  1674. DropC (EmCall (TU_RdLn)) ;
  1675. RETURN
  1676. END ;
  1677. (* NB: guard the loop bound - with nArgs = 0, "nArgs - 1" would wrap round
  1678. to 65535 in CARDINAL and spin 65536 times. *)
  1679. IF nArgs > 0 THEN
  1680. FOR i := 0 TO nArgs - 1 DO
  1681. IF reading THEN
  1682. IF args [i].kind # 1 THEN
  1683. Err (ETypeErr) ; (* READ needs a variable *)
  1684. RETURN
  1685. END ;
  1686. acls := symtab [args [i].idx].cls ;
  1687. IF acls = TString THEN
  1688. Err (ENoLib) ; (* string runtime pending *)
  1689. RETURN
  1690. END ;
  1691. EmPushVarAddr (symtab [args [i].idx].local, symtab [args [i].idx].off) ;
  1692. IF acls = TReal THEN
  1693. ent := TU_RdInt (* real reads: not yet *)
  1694. ELSIF acls = TBool THEN
  1695. ent := TU_RdBool
  1696. ELSIF acls = TScalar THEN
  1697. ent := TU_RdInt
  1698. ELSE
  1699. ent := TU_RdChar
  1700. END
  1701. ELSE
  1702. acls := args [i].cls ;
  1703. IF acls = TString THEN
  1704. Err (ENoLib) ; (* string runtime pending *)
  1705. RETURN
  1706. END ;
  1707. LoadAtom (args [i]) ;
  1708. EmPushAx () ;
  1709. IF args [i].chr THEN
  1710. ent := TU_WrChar (* 'a' - one char, not 97 *)
  1711. ELSIF acls = TReal THEN
  1712. ent := TU_WrReal
  1713. ELSIF acls = TBool THEN
  1714. ent := TU_WrBool
  1715. ELSIF acls = TScalar THEN
  1716. ent := TU_WrInt
  1717. ELSE
  1718. ent := TU_WrChar
  1719. END
  1720. END ;
  1721. DropC (EmCall (ent)) ;
  1722. EmAddSp (2) (* one 16-bit argument *)
  1723. END
  1724. END ;
  1725. IF which = BI_WriteLn THEN
  1726. DropC (EmCall (TU_WrLn))
  1727. ELSIF which = BI_ReadLn THEN
  1728. DropC (EmCall (TU_RdLn))
  1729. END
  1730. END IoCall ;
  1731. PROCEDURE Statmnt () ;
  1732. VAR tok : CARDINAL ;
  1733. idx, i2 : CARDINAL ;
  1734. t, src : ERes ;
  1735. L1, zj, zj2, exj : CARDINAL ;
  1736. lo, hi, v : LONGINT ;
  1737. clso : CARDINAL ;
  1738. i : CARDINAL ;
  1739. nm : ARRAY [0..MaxName] OF CHAR ;
  1740. strf : BOOLEAN ;
  1741. dow : BOOLEAN ;
  1742. BEGIN
  1743. Skip () ;
  1744. IF Digit (CurCh ()) THEN
  1745. ParseLabelStmt () ; (* consumed 'n' ':' *)
  1746. Statmnt () ; (* 'n : statement' - the statement follows
  1747. the label directly, with no ';' between *)
  1748. RETURN
  1749. END ;
  1750. IF NOT Alpha (CurCh ()) THEN
  1751. ExpectDelim (';', ENoSemi) ;
  1752. RETURN
  1753. END ;
  1754. DropB (MatchKey (tok)) ;
  1755. IF tok = TkBegin THEN
  1756. Compound ()
  1757. ELSIF tok = TkIf THEN
  1758. ParseExpr (t) ;
  1759. LoadAtom (t) ;
  1760. EmCmpAxi (0) ;
  1761. zj := EmJcc (84H, 0) ; (* JZ -> else/end *)
  1762. IF NOT (MatchKey (tok) AND (tok = TkThen)) THEN
  1763. Err (ENoSemi)
  1764. END ;
  1765. Statmnt () ;
  1766. IF MatchKey (tok) AND (tok = TkElse) THEN
  1767. exj := EmJmpNear (0) ;
  1768. SetPatTgt (zj, pc) ;
  1769. Statmnt () ;
  1770. SetPatTgt (exj, pc)
  1771. ELSE
  1772. SetPatTgt (zj, pc)
  1773. END
  1774. ELSIF tok = TkWhile THEN
  1775. L1 := pc ;
  1776. ParseExpr (t) ;
  1777. LoadAtom (t) ;
  1778. EmCmpAxi (0) ;
  1779. zj := EmJcc (84H, 0) ; (* JZ -> end *)
  1780. IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
  1781. Err (ENoSemi)
  1782. END ;
  1783. brkSave [brkN] := exitCnt ;
  1784. loopTy [brkN] := 1 ;
  1785. INC (brkN) ;
  1786. Statmnt () ;
  1787. DEC (brkN) ;
  1788. i := brkSave [brkN] ;
  1789. WHILE i < exitCnt DO
  1790. SetPatTgt (exitPatch [i], pc) ;
  1791. INC (i)
  1792. END ;
  1793. exitCnt := brkSave [brkN] ;
  1794. DropC (EmJmpNear (L1)) ;
  1795. SetPatTgt (zj, pc)
  1796. ELSIF tok = TkRepeat THEN
  1797. L1 := pc ;
  1798. brkSave [brkN] := exitCnt ;
  1799. loopTy [brkN] := 1 ;
  1800. INC (brkN) ;
  1801. Statmnt () ;
  1802. IF NOT (MatchKey (tok) AND (tok = TkUntil)) THEN
  1803. Err (ENoSemi)
  1804. END ;
  1805. ParseExpr (t) ;
  1806. LoadAtom (t) ;
  1807. EmCmpAxi (0) ;
  1808. zj := EmJcc (85H, L1) ; (* JNZ -> body again *)
  1809. DEC (brkN) ;
  1810. i := brkSave [brkN] ;
  1811. WHILE i < exitCnt DO
  1812. SetPatTgt (exitPatch [i], pc) ;
  1813. INC (i)
  1814. END ;
  1815. exitCnt := brkSave [brkN]
  1816. ELSIF tok = TkFor THEN
  1817. (* control variable *)
  1818. Skip () ; (* after the FOR keyword: skip blanks *)
  1819. IF NOT Alpha (CurCh ()) THEN
  1820. Err (EUnknown) ;
  1821. RETURN
  1822. END ;
  1823. GetWord () ;
  1824. IF NOT Search (wrd, idx) THEN
  1825. Err (EUnknown) ;
  1826. RETURN
  1827. END ;
  1828. IF symtab [idx].size > 2 THEN
  1829. Err (ENoLib) ;
  1830. RETURN
  1831. END ;
  1832. IF NOT MatchAssign () THEN
  1833. Err (ENoSemi)
  1834. END ;
  1835. ParseExpr (src) ;
  1836. LoadAtom (src) ;
  1837. EmStoreVar (symtab [idx].local, symtab [idx].off,
  1838. symtab [idx].size) ;
  1839. IF NOT MatchKey (tok) THEN
  1840. Err (ESimpType) ;
  1841. RETURN
  1842. END ;
  1843. IF (tok = TkTo) OR (tok = TkDownto) THEN
  1844. dow := (tok = TkDownto)
  1845. ELSE
  1846. Err (ESimpType) ;
  1847. RETURN
  1848. END ;
  1849. ParseExpr (t) ;
  1850. LoadAtom (t) ;
  1851. EmPushAx () ; (* loop bound on the stack *)
  1852. IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
  1853. Err (ENoSemi)
  1854. END ;
  1855. brkSave [brkN] := exitCnt ;
  1856. loopTy [brkN] := 2 ;
  1857. INC (brkN) ;
  1858. L1 := pc ; (* Ltest *)
  1859. Statmnt () ;
  1860. DEC (brkN) ;
  1861. i := brkSave [brkN] ;
  1862. WHILE i < exitCnt DO
  1863. SetPatTgt (exitPatch [i], pc) ;
  1864. INC (i)
  1865. END ;
  1866. exitCnt := brkSave [brkN] ;
  1867. (* test then step: ax = var ; cx = bound (from [sp]) *)
  1868. EmMovCxSp () ;
  1869. EmLoadVar (symtab [idx].local, symtab [idx].off,
  1870. symtab [idx].size) ;
  1871. EmCmpAxCx () ;
  1872. IF dow THEN
  1873. zj := EmJcc (8CH, 0) (* JL -> done *)
  1874. ELSE
  1875. zj := EmJcc (8FH, 0) (* JG -> done *)
  1876. END ;
  1877. EmLoadVar (symtab [idx].local, symtab [idx].off,
  1878. symtab [idx].size) ;
  1879. IF dow THEN
  1880. EmDecAx ()
  1881. ELSE
  1882. EmIncAx ()
  1883. END ;
  1884. EmStoreVar (symtab [idx].local, symtab [idx].off,
  1885. symtab [idx].size) ;
  1886. DropC (EmJmpNear (L1)) ;
  1887. SetPatTgt (zj, pc) ; (* done: drop bound, continue *)
  1888. EmAddSp (2)
  1889. ELSIF tok = TkCase THEN
  1890. ParseExpr (t) ;
  1891. LoadAtom (t) ;
  1892. EmPushAx () ; (* selector on the stack *)
  1893. IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
  1894. Err (ENoSemi)
  1895. END ;
  1896. caseN := 0 ;
  1897. LOOP
  1898. Skip () ;
  1899. IF MatchDelim (';') THEN
  1900. Skip ()
  1901. END ;
  1902. PeekKw (tok) ;
  1903. IF (tok = TkEnd) OR (tok = TkElse) THEN
  1904. EXIT
  1905. END ;
  1906. (* case label : constant identifier or literal *)
  1907. IF Alpha (CurCh ()) THEN
  1908. GetWord () ;
  1909. IF Search (wrd, i2) AND (symtab [i2].tag = KConst) THEN
  1910. lo := symtab [i2].lval
  1911. ELSE
  1912. Err (EUnknown) ;
  1913. EXIT
  1914. END
  1915. ELSE
  1916. RdConst (lo, clso, strf)
  1917. END ;
  1918. IF MatchRange () THEN
  1919. RdConst (hi, clso, strf)
  1920. ELSE
  1921. hi := lo
  1922. END ;
  1923. ExpectDelim (':', ENoSemi) ;
  1924. EmMovAxSp () ;
  1925. EmCmpAxi (W16 (lo)) ;
  1926. zj := EmJcc (85H, 0) ; (* JNZ -> next *)
  1927. IF hi # lo THEN
  1928. EmCmpAxi (W16 (hi)) ;
  1929. zj2 := EmJcc (85H, 0)
  1930. ELSE
  1931. zj2 := 0
  1932. END ;
  1933. Statmnt () ;
  1934. IF caseN >= 64 THEN
  1935. Err (ECompOvf) ;
  1936. EXIT
  1937. END ;
  1938. caseJmp [caseN] := EmJmpNear (0) ;
  1939. INC (caseN) ;
  1940. SetPatTgt (zj, pc) ;
  1941. IF zj2 # 0 THEN
  1942. SetPatTgt (zj2, pc)
  1943. END
  1944. END ;
  1945. IF tok = TkElse THEN
  1946. DropB (MatchKey (tok)) ;
  1947. Statmnt () ;
  1948. IF NOT (MatchKey (tok) AND (tok = TkEnd)) THEN
  1949. Err (ENoSemi)
  1950. END
  1951. ELSE
  1952. DropB (MatchKey (tok))
  1953. END ;
  1954. EmAddSp (2) ;
  1955. FOR i := 0 TO caseN - 1 DO
  1956. SetPatTgt (caseJmp [i], pc)
  1957. END
  1958. ELSIF tok = TkGoto THEN
  1959. v := 0 ;
  1960. Skip () ; (* after the GOTO keyword: skip blanks *)
  1961. IF Digit (CurCh ()) THEN
  1962. RdIntConst (v) ;
  1963. NumToName (W16 (v), nm) ;
  1964. IF Search (nm, idx) THEN
  1965. IF symtab [idx].tag = KLabel THEN
  1966. IF symtab [idx].defnd THEN
  1967. DropC (EmJmpNear (symtab [idx].goPos))
  1968. ELSE
  1969. zj := EmJmpNear (0) ;
  1970. AddPend (0, idx, zj)
  1971. END
  1972. ELSE
  1973. Err (EUnknown)
  1974. END
  1975. ELSE
  1976. idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
  1977. zj := EmJmpNear (0) ;
  1978. AddPend (0, idx, zj)
  1979. END
  1980. ELSE
  1981. Err (EUnknown)
  1982. END
  1983. ELSIF tok = TkExit THEN
  1984. IF brkN = 0 THEN
  1985. Err (EUnknown)
  1986. ELSE
  1987. IF loopTy [brkN - 1] = 2 THEN
  1988. EmAddSp (2) (* drop FOR bound *)
  1989. END ;
  1990. zj := EmJmpNear (0) ;
  1991. IF exitCnt < 64 THEN
  1992. exitPatch [exitCnt] := zj ;
  1993. INC (exitCnt)
  1994. END
  1995. END
  1996. ELSIF tok = TkWith THEN
  1997. Err (ENoLib)
  1998. ELSE
  1999. (* identifier statement: assignment or call *)
  2000. IF NOT Search (wrd, idx) THEN
  2001. Err (EUnknown) ;
  2002. RETURN
  2003. END ;
  2004. IF symtab [idx].tag = KBuiltin THEN
  2005. IoCall (idx) ;
  2006. RETURN
  2007. END ;
  2008. IF symtab [idx].tag = KProc THEN
  2009. IF MatchDelim ('(') THEN
  2010. ParseCallArgs (idx)
  2011. ELSE
  2012. ParseCall (idx)
  2013. END ;
  2014. RETURN
  2015. END ;
  2016. IF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KFunc) THEN
  2017. t.idx := idx ;
  2018. t.kind := 1 ;
  2019. t.boff := 0 ;
  2020. t.cls := symtab [idx].cls ;
  2021. IF symtab [idx].tag = KFunc THEN
  2022. t.idx := symtab [idx].resvar ;
  2023. t.cls := symtab [t.idx].cls
  2024. END ;
  2025. ParseSub (t) ;
  2026. IF MatchAssign () THEN
  2027. Assignment (t) ;
  2028. RETURN
  2029. END ;
  2030. Err (ENoSemi) ;
  2031. RETURN
  2032. END ;
  2033. Err (ENoSemi)
  2034. END
  2035. END Statmnt ;
  2036. (* ---------------------------------------------------------------- *)
  2037. (* types and declarations (TPSRC7) *)
  2038. (* ---------------------------------------------------------------- *)
  2039. PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ;
  2040. VAR tok : CARDINAL ;
  2041. idx : CARDINAL ;
  2042. lo, hi : LONGINT ;
  2043. s2, e2 : CARDINAL ;
  2044. subcls : CARDINAL ;
  2045. strf : BOOLEAN ;
  2046. consumed : BOOLEAN ;
  2047. BEGIN
  2048. cls := TNone ; size := 0 ; elem := 0 ;
  2049. consumed := FALSE ;
  2050. Skip () ; (* after ':' / '=' : skip blanks *)
  2051. IF Alpha (CurCh ()) THEN
  2052. DropB (MatchKey (tok)) ;
  2053. consumed := TRUE
  2054. ELSE
  2055. tok := TkNone
  2056. END ;
  2057. IF tok = TkArray THEN
  2058. ExpectDelim ('[', ENoSemi) ;
  2059. RdConst (lo, subcls, strf) ;
  2060. IF NOT MatchRange () THEN
  2061. Err (ESimpType)
  2062. END ;
  2063. RdConst (hi, subcls, strf) ;
  2064. ExpectDelim (']', ENoSemi) ;
  2065. IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
  2066. Err (ENoSemi)
  2067. END ;
  2068. ParseType (cls, s2, e2) ;
  2069. cls := TArray ;
  2070. elem := s2 ;
  2071. size := s2 * (W16 (VAL (LONGINT, W16 (hi))
  2072. - VAL (LONGINT, W16 (lo)) + 1))
  2073. ELSIF tok = TkString THEN
  2074. cls := TString ;
  2075. size := 256 ;
  2076. elem := 1 ;
  2077. IF MatchDelim ('[') THEN
  2078. RdConst (hi, subcls, strf) ;
  2079. ExpectDelim (']', ENoSemi) ;
  2080. size := W16 (hi) + 1
  2081. END
  2082. ELSIF tok = TkSet THEN
  2083. Err (ENoLib) ;
  2084. IF MatchKey (tok) AND (tok = TkOf) THEN
  2085. ParseType (cls, s2, e2)
  2086. END
  2087. ELSIF tok = TkRecord THEN
  2088. Err (ENoLib) ;
  2089. LOOP
  2090. PeekKw (tok) ;
  2091. IF tok = TkEnd THEN
  2092. DropB (MatchKey (tok)) ;
  2093. EXIT
  2094. END ;
  2095. IF CurCh () = 0C THEN
  2096. EXIT
  2097. END ;
  2098. Skip () ;
  2099. IF Alpha (CurCh ()) THEN
  2100. DropCh (GetCh ())
  2101. ELSE
  2102. DropCh (GetCh ())
  2103. END
  2104. END
  2105. ELSIF (tok = TkFile) OR (tok = TkText) THEN
  2106. cls := TFile ;
  2107. size := 0 ;
  2108. elem := 0 ;
  2109. Err (ENoLib)
  2110. ELSE
  2111. IF consumed THEN
  2112. IF Search (wrd, idx) AND (symtab [idx].tag = KType) THEN
  2113. cls := symtab [idx].cls ;
  2114. size := symtab [idx].size ;
  2115. elem := symtab [idx].size
  2116. ELSE
  2117. Err (EUnknown)
  2118. END
  2119. ELSE
  2120. (* subrange lo .. hi *)
  2121. RdConst (lo, subcls, strf) ;
  2122. IF NOT MatchRange () THEN
  2123. Err (ESimpType) ;
  2124. RETURN
  2125. END ;
  2126. RdConst (hi, subcls, strf) ;
  2127. cls := TScalar ;
  2128. size := 2 ;
  2129. elem := 2
  2130. END
  2131. END
  2132. END ParseType ;
  2133. PROCEDURE DefVar () ;
  2134. (* 'name' (',' name)* ':' type [ABSOLUTE addr] ; group repeated until a
  2135. declaration keyword appears *)
  2136. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2137. cls, size, elem : CARDINAL ;
  2138. tok : CARDINAL ;
  2139. v : LONGINT ;
  2140. off : CARDINAL ;
  2141. BEGIN
  2142. LOOP
  2143. Skip () ; (* after the VAR keyword: skip blanks *)
  2144. IF NOT Alpha (CurCh ()) THEN
  2145. Err (EUnknown) ;
  2146. RETURN
  2147. END ;
  2148. LOOP
  2149. GetWord () ;
  2150. SaveWord (nm) ;
  2151. DupTest (nm) ;
  2152. IF NOT MatchDelim (':') THEN
  2153. Err (ENoSemi)
  2154. END ;
  2155. ParseType (cls, size, elem) ;
  2156. off := 0 ;
  2157. IF lexnest = 0 THEN
  2158. IF size > 2 THEN
  2159. Err (ENoLib) ;
  2160. RETURN
  2161. END ;
  2162. PeekKw (tok) ;
  2163. IF tok = TkAbsolute THEN
  2164. DropB (MatchKey (tok)) ;
  2165. IF (CurCh () = '$') OR (Digit (CurCh ())) THEN
  2166. RdIntConst (v) ;
  2167. off := W16 (v)
  2168. ELSE
  2169. Err (EUnknown)
  2170. END
  2171. ELSE
  2172. off := dc ;
  2173. dc := dc + size
  2174. END ;
  2175. DropC (NewSym (nm, KVar, cls, size, elem, off, 0, FALSE)) ;
  2176. varspc := varspc + size
  2177. ELSE
  2178. IF size > 2 THEN
  2179. Err (ENoLib) ;
  2180. RETURN
  2181. END ;
  2182. DropC (NewSym (nm, KVar, cls, size, elem, locFree, 0, TRUE)) ;
  2183. locFree := (locFree - size) MOD 10000H ;
  2184. locBytes := locBytes + size
  2185. END ;
  2186. IF NOT MatchDelim (',') THEN
  2187. EXIT
  2188. END
  2189. END ;
  2190. IF NOT MatchDelim (';') THEN
  2191. Err (ENoSemi) ;
  2192. RETURN
  2193. END ;
  2194. PeekKw (tok) ;
  2195. IF tok # TkNone THEN
  2196. RETURN
  2197. END
  2198. END
  2199. END DefVar ;
  2200. PROCEDURE DefConst () ;
  2201. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2202. v : LONGINT ;
  2203. cls : CARDINAL ;
  2204. isStr : BOOLEAN ;
  2205. tok : CARDINAL ;
  2206. BEGIN
  2207. LOOP
  2208. PeekKw (tok) ;
  2209. IF tok # TkNone THEN
  2210. RETURN
  2211. END ;
  2212. Skip () ; (* after the CONST keyword: skip blanks *)
  2213. IF NOT Alpha (CurCh ()) THEN
  2214. Err (EUnknown) ;
  2215. RETURN
  2216. END ;
  2217. GetWord () ;
  2218. SaveWord (nm) ;
  2219. DupTest (nm) ;
  2220. ExpectDelim ('=', ENoSemi) ;
  2221. RdConst (v, cls, isStr) ;
  2222. IF isStr THEN
  2223. Err (ENoLib)
  2224. END ;
  2225. DropC (NewSym (nm, KConst, cls, 0, 0, 0, v, FALSE)) ;
  2226. IF NOT MatchDelim (';') THEN
  2227. Err (ENoSemi) ;
  2228. RETURN
  2229. END
  2230. END
  2231. END DefConst ;
  2232. PROCEDURE DefLabelPart () ;
  2233. VAR nm : ARRAY [0..9] OF CHAR ;
  2234. n : CARDINAL ;
  2235. BEGIN
  2236. LOOP
  2237. Skip () ;
  2238. IF NOT Digit (CurCh ()) THEN
  2239. Err (EUnknown) ;
  2240. RETURN
  2241. END ;
  2242. n := 0 ;
  2243. WHILE Digit (CurCh ()) DO
  2244. n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
  2245. DropCh (GetCh ())
  2246. END ;
  2247. NumToName (n, nm) ;
  2248. IF NOT Search (nm, n) THEN
  2249. DropC (NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE))
  2250. END ;
  2251. IF NOT MatchDelim (',') THEN
  2252. IF MatchDelim (';') THEN
  2253. RETURN
  2254. END ;
  2255. Err (ENoSemi) ;
  2256. RETURN
  2257. END
  2258. END
  2259. END DefLabelPart ;
  2260. PROCEDURE IfMatchSemi () ;
  2261. BEGIN
  2262. IF NOT MatchDelim (';') THEN
  2263. Err (ENoSemi)
  2264. END
  2265. END IfMatchSemi ;
  2266. PROCEDURE SymEpi () ;
  2267. (* function result: AX := result var *)
  2268. BEGIN
  2269. IF curIsFunc THEN
  2270. IF OK () THEN
  2271. EmLoadVar (TRUE, symtab [resultVar].off, symtab [resultVar].size)
  2272. END
  2273. END
  2274. END SymEpi ;
  2275. PROCEDURE ProcFunc () ;
  2276. (* PROCEDURE name (params) ; body | FUNCTION name (params) : type ; body *)
  2277. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2278. idx, old : CARDINAL ;
  2279. tok, tok2 : CARDINAL ;
  2280. parmNm : ARRAY [0..MaxName] OF CHAR ;
  2281. cls, size, elem : CARDINAL ;
  2282. isFunc : BOOLEAN ;
  2283. saveNest, saveLoc, saveRes, saveF : CARDINAL ;
  2284. saveLB, savePO : CARDINAL ;
  2285. i : CARDINAL ;
  2286. BEGIN
  2287. isFunc := curIsFunc ;
  2288. Skip () ; (* after the PROCEDURE/FUNCTION keyword *)
  2289. IF NOT Alpha (CurCh ()) THEN
  2290. Err (EUnknown) ;
  2291. RETURN
  2292. END ;
  2293. GetWord () ;
  2294. SaveWord (nm) ;
  2295. IF Search (nm, idx) AND (symtab [idx].tag = KProc)
  2296. AND (symtab [idx].fwd) THEN
  2297. old := idx
  2298. ELSIF Search (nm, idx) THEN
  2299. Err (EUnknown) ;
  2300. RETURN
  2301. ELSE
  2302. old := NewSym (nm, KProc, TNone, 0, 0, 0, 0, FALSE)
  2303. END ;
  2304. IF isFunc THEN
  2305. symtab [old].tag := KFunc
  2306. END ;
  2307. saveNest := lexnest ;
  2308. saveLoc := locFree ;
  2309. saveRes := resultVar ;
  2310. saveF := VAL (CARDINAL, ORD (curIsFunc)) ;
  2311. saveLB := locBytes ;
  2312. savePO := parmOff ;
  2313. INC (lexnest) ;
  2314. locFree := 0FFFEH ;
  2315. locBytes := 0 ;
  2316. parmOff := 4 ;
  2317. IF MatchDelim ('(') THEN
  2318. IF CurCh () # ')' THEN
  2319. LOOP
  2320. (* PeekKw, not MatchKey: MatchKey CONSUMES the word it reads, so
  2321. using it to test for VAR would eat the parameter's name. *)
  2322. PeekKw (tok) ;
  2323. IF tok = TkVar THEN
  2324. (* VAR parameter recorded as value in this milestone *)
  2325. DropB (MatchKey (tok))
  2326. END ;
  2327. Skip () ; (* blanks before the parameter name *)
  2328. IF NOT Alpha (CurCh ()) THEN
  2329. Err (EUnknown) ;
  2330. RETURN
  2331. END ;
  2332. GetWord () ;
  2333. SaveWord (parmNm) ;
  2334. DupTest (parmNm) ;
  2335. ExpectDelim (':', ENoSemi) ; (* formal is 'name : type' *)
  2336. ParseType (cls, size, elem) ;
  2337. IF size > 2 THEN
  2338. Err (ENoLib) ;
  2339. RETURN
  2340. END ;
  2341. DropC (NewSym (parmNm, KVar, cls, size, elem, parmOff, 0, TRUE)) ;
  2342. parmOff := parmOff + 2 ;
  2343. IF NOT MatchDelim (',') THEN
  2344. IF MatchDelim (')') THEN
  2345. EXIT
  2346. END ;
  2347. Err (ENoSemi) ;
  2348. EXIT
  2349. END
  2350. END
  2351. ELSE
  2352. DropCh (GetCh ())
  2353. END
  2354. END ;
  2355. IF isFunc THEN
  2356. IF MatchDelim (':') THEN
  2357. ParseType (cls, size, elem)
  2358. ELSE
  2359. cls := TScalar ;
  2360. size := 2 ;
  2361. elem := 2
  2362. END ;
  2363. IF size > 2 THEN
  2364. Err (ENoLib) ;
  2365. RETURN
  2366. END ;
  2367. symtab [old].cls := cls ;
  2368. symtab [old].size := size ;
  2369. resultVar := NewSym (nm, KVar, cls, size, elem, locFree, 0, TRUE) ;
  2370. symtab [old].resvar := resultVar ;
  2371. locFree := (locFree - size) MOD 10000H ;
  2372. locBytes := locBytes + size
  2373. END ;
  2374. IfMatchSemi () ;
  2375. PeekKw (tok2) ;
  2376. IF (tok2 = TkForward) OR (tok2 = TkExternal) THEN
  2377. DropB (MatchKey (tok2)) ;
  2378. symtab [old].fwd := TRUE ;
  2379. symtab [old].defnd := (tok2 = TkExternal) ;
  2380. IfMatchSemi () ;
  2381. lexnest := saveNest ;
  2382. locFree := saveLoc ;
  2383. resultVar := saveRes ;
  2384. curIsFunc := (saveF # 0) ;
  2385. locBytes := saveLB ;
  2386. parmOff := savePO ;
  2387. RETURN
  2388. END ;
  2389. (* body *)
  2390. symtab [old].goPos := pc ;
  2391. symtab [old].defnd := TRUE ;
  2392. EmPushBp () ;
  2393. EmMovBpSp () ;
  2394. DefPart () ; (* nested declarations; stops at BEGIN *)
  2395. IF locBytes > 0 THEN
  2396. EmSubSp (locBytes)
  2397. END ;
  2398. DropC (EmCall (TU_StackChk)) ;
  2399. Statmnt () ; (* body *)
  2400. IF OK () THEN
  2401. SymEpi () ;
  2402. EmLeave () ;
  2403. EmRet ()
  2404. END ;
  2405. (* patch pending forward calls to this proc *)
  2406. i := 0 ;
  2407. WHILE i < nPend DO
  2408. IF (pend [i].kind = 1) AND (pend [i].who = old) THEN
  2409. SetPatTgt (pend [i].place, symtab [old].goPos) ;
  2410. pend [i].kind := 99
  2411. END ;
  2412. INC (i)
  2413. END ;
  2414. lexnest := saveNest ;
  2415. locFree := saveLoc ;
  2416. resultVar := saveRes ;
  2417. curIsFunc := (saveF # 0) ;
  2418. locBytes := saveLB ;
  2419. parmOff := savePO
  2420. END ProcFunc ;
  2421. PROCEDURE DefType () ;
  2422. (* 'name' '=' typeDef ; ... until a declaration keyword appears *)
  2423. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2424. cls, size, elem : CARDINAL ;
  2425. tok : CARDINAL ;
  2426. BEGIN
  2427. LOOP
  2428. PeekKw (tok) ;
  2429. IF tok # TkNone THEN
  2430. RETURN
  2431. END ;
  2432. Skip () ; (* after the TYPE keyword: skip blanks *)
  2433. IF NOT Alpha (CurCh ()) THEN
  2434. Err (EUnknown) ;
  2435. RETURN
  2436. END ;
  2437. GetWord () ;
  2438. SaveWord (nm) ;
  2439. DupTest (nm) ;
  2440. ExpectDelim ('=', ENoSemi) ;
  2441. ParseType (cls, size, elem) ;
  2442. IF NOT OK () THEN
  2443. RETURN
  2444. END ;
  2445. DropC (NewSym (nm, KType, cls, size, elem, 0, 0, FALSE)) ;
  2446. IF NOT MatchDelim (';') THEN
  2447. Err (ENoSemi) ;
  2448. RETURN
  2449. END
  2450. END
  2451. END DefType ;
  2452. PROCEDURE DefPart () ;
  2453. (* LABEL / CONST / TYPE / VAR / OVERLAY / PROC / FUNCTION / BEGIN *)
  2454. VAR tok : CARDINAL ;
  2455. BEGIN
  2456. LOOP
  2457. IF MatchDelim (';') THEN
  2458. (* separator between declarations *)
  2459. ELSE
  2460. PeekKw (tok) ;
  2461. IF tok = TkBegin THEN
  2462. RETURN
  2463. END ;
  2464. IF NOT MatchKey (tok) THEN
  2465. Err (EUnknown) ;
  2466. RETURN
  2467. END ;
  2468. CASE tok OF
  2469. TkLabel : DefLabelPart () ;
  2470. | TkConst : DefConst () ;
  2471. | TkType : DefType () ;
  2472. | TkVar : DefVar () ;
  2473. | TkOverlay :
  2474. LOOP
  2475. Skip () ;
  2476. IF CurCh () = ';' THEN
  2477. DropCh (GetCh ()) ;
  2478. EXIT
  2479. END ;
  2480. IF CurCh () = 0C THEN
  2481. Err (ENoSemi) ;
  2482. EXIT
  2483. END ;
  2484. DropCh (GetCh ())
  2485. END ;
  2486. | TkProcedure : curIsFunc := FALSE ; ProcFunc () ;
  2487. | TkFunction : curIsFunc := TRUE ; ProcFunc () ;
  2488. ELSE
  2489. Err (EUnknown) ;
  2490. RETURN
  2491. END ;
  2492. IF NOT OK () THEN
  2493. RETURN
  2494. END
  2495. END
  2496. END
  2497. END DefPart ;
  2498. (* ---------------------------------------------------------------- *)
  2499. (* driver (TPSRC7 compile) *)
  2500. (* ---------------------------------------------------------------- *)
  2501. PROCEDURE DefBuiltins () ;
  2502. (* The standard procedures. Without these, WRITELN is absent from the
  2503. symbol table, Statmnt's identifier branch fails its Search and every
  2504. program that prints anything dies with EUnknown (41) on the '(' after the
  2505. call name - the single remaining cause of failure in the fixture matrix.
  2506. Tagged KBuiltin (not KProc) because these are not called generically:
  2507. WRITE/WRITELN/READ/READLN need TP3's per-argument type dispatch
  2508. (IoCall), and HALT takes no argument at all. defnd is TRUE because the
  2509. entry point is known - there is no forward reference to patch. *)
  2510. VAR i : CARDINAL ;
  2511. BEGIN
  2512. i := NewSym ("WRITE" , KBuiltin, BI_Write , 0, 0, 0, 0, FALSE) ;
  2513. symtab [i].defnd := TRUE ;
  2514. symtab [i].goPos := TU_WrInt ;
  2515. i := NewSym ("WRITELN", KBuiltin, BI_WriteLn, 0, 0, 0, 0, FALSE) ;
  2516. symtab [i].defnd := TRUE ;
  2517. symtab [i].goPos := TU_WrInt ;
  2518. i := NewSym ("READ" , KBuiltin, BI_Read , 0, 0, 0, 0, FALSE) ;
  2519. symtab [i].defnd := TRUE ;
  2520. symtab [i].goPos := TU_RdInt ;
  2521. i := NewSym ("READLN" , KBuiltin, BI_ReadLn , 0, 0, 0, 0, FALSE) ;
  2522. symtab [i].defnd := TRUE ;
  2523. symtab [i].goPos := TU_RdInt ;
  2524. i := NewSym ("HALT" , KBuiltin, BI_Halt , 0, 0, 0, 0, FALSE) ;
  2525. symtab [i].defnd := TRUE ;
  2526. symtab [i].goPos := TU_Halt
  2527. END DefBuiltins ;
  2528. PROCEDURE Inittur () ;
  2529. (* reset compiler state and define the standard types *)
  2530. BEGIN
  2531. abortFac := FALSE ;
  2532. errNum := 0 ;
  2533. txerrPos := 0 ;
  2534. srcPos := 0 ;
  2535. srcLen := Length () ;
  2536. pc := 0 ;
  2537. dc := 100H ;
  2538. varspc := 0 ;
  2539. symTop := 0 ;
  2540. nPatch := 0 ;
  2541. nPend := 0 ;
  2542. exitCnt := 0 ;
  2543. brkN := 0 ;
  2544. caseN := 0 ;
  2545. lexnest := 0 ;
  2546. curIsFunc := FALSE ;
  2547. resultVar := 0 ;
  2548. locFree := 0FFFEH ;
  2549. locBytes := 0 ;
  2550. parmOff := 4 ;
  2551. dirs.rng := TRUE ;
  2552. dirs.chk := TRUE ;
  2553. InitKeys () ;
  2554. DropC (NewSym ("INTEGER", KType, TScalar, 2, 2, 0, 0, FALSE)) ;
  2555. DropC (NewSym ("BYTE" , KType, TScalar, 1, 1, 0, 0, FALSE)) ;
  2556. DropC (NewSym ("CHAR" , KType, TScalar, 1, 1, 0, 0, FALSE)) ;
  2557. DropC (NewSym ("BOOLEAN", KType, TBool , 1, 1, 0, 0, FALSE)) ;
  2558. DropC (NewSym ("REAL" , KType, TReal , 6, 6, 0, 0, FALSE)) ;
  2559. DropC (NewSym ("STRING" , KType, TString, 256, 1, 0, 0, FALSE)) ;
  2560. DropC (NewSym ("TRUE" , KConst, TBool, 1, 1, 0, 1, FALSE)) ;
  2561. DropC (NewSym ("FALSE" , KConst, TBool, 1, 1, 0, 0, FALSE)) ;
  2562. DefBuiltins () ;
  2563. tmpA := NewSym ("@@T1", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
  2564. dc := dc + 2 ;
  2565. tmpB := NewSym ("@@T2", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
  2566. dc := dc + 2
  2567. END Inittur ;
  2568. PROCEDURE HeadWord (VAR slot : CARDINAL) ;
  2569. BEGIN
  2570. slot := pc ;
  2571. Eword (0)
  2572. END HeadWord ;
  2573. PROCEDURE Compile (VAR errNo, errPos : CARDINAL) : BOOLEAN ;
  2574. VAR tok : CARDINAL ;
  2575. BEGIN
  2576. Inittur () ;
  2577. IF OK () THEN
  2578. (* program prologue: header words, CALL initmem, MOV BP,SP *)
  2579. HeadWord (hdrFlag) ;
  2580. HeadWord (hdrCS) ;
  2581. HeadWord (hdrDS) ;
  2582. HeadWord (hdrHeap) ;
  2583. HeadWord (hdrMax) ;
  2584. Eword (16) ; (* max open files *)
  2585. Eword (0) ; (* input buffer word *)
  2586. Eword (0) ; (* output buffer word *)
  2587. DropC (EmCall (TU_InitMem)) ;
  2588. EmMovBpSp () ;
  2589. IF MatchKey (tok) AND (tok = TkProgram) THEN
  2590. (* MatchKey stops right after "PROGRAM", so the optional program
  2591. name normally follows blanks. Skip them before testing for the
  2592. name: otherwise Alpha sees the blank, the name is never consumed
  2593. and IfMatchSemi reports ENoSemi at the name. *)
  2594. Skip () ;
  2595. IF Alpha (CurCh ()) THEN
  2596. GetWord ()
  2597. END ;
  2598. IF MatchDelim ('(') THEN
  2599. WHILE NOT MatchDelim (')') DO
  2600. IF Alpha (CurCh ()) THEN
  2601. GetWord ()
  2602. END ;
  2603. IF CurCh () = ',' THEN
  2604. DropCh (GetCh ())
  2605. END
  2606. END
  2607. END ;
  2608. IfMatchSemi ()
  2609. END ;
  2610. IF OK () THEN
  2611. DefPart () ;
  2612. IF OK () THEN
  2613. IF MatchKey (tok) AND (tok = TkBegin) THEN
  2614. Compound () ;
  2615. IF OK () THEN
  2616. EmXorAxAx () ;
  2617. DropC (EmCall (TU_ProgEnd)) ;
  2618. ResolvePatches () ;
  2619. codeSz := pc ;
  2620. dataSz := dc ;
  2621. cbuf [hdrCS] := VAL (BYTE, (codeSz DIV 16) MOD 100H) ;
  2622. cbuf [hdrCS + 1] := VAL (BYTE, ((codeSz DIV 16) DIV 100H) MOD 100H) ;
  2623. cbuf [hdrDS] := VAL (BYTE, (dataSz DIV 16) MOD 100H) ;
  2624. cbuf [hdrDS + 1] := VAL (BYTE, ((dataSz DIV 16) DIV 100H) MOD 100H) ;
  2625. cbuf [hdrFlag] := 1 ;
  2626. cbuf [hdrFlag + 1] := 0 ;
  2627. cbuf [hdrHeap] := 0 ;
  2628. cbuf [hdrHeap + 1] := 0 ;
  2629. cbuf [hdrMax] := 0 ;
  2630. cbuf [hdrMax + 1] := 0
  2631. END
  2632. ELSE
  2633. Err (EUnknown)
  2634. END
  2635. END
  2636. END
  2637. END ;
  2638. IF NOT MatchDelim ('.') THEN
  2639. Err (EPointExp)
  2640. END ;
  2641. IF abortFac THEN
  2642. errNo := errNum ; (* was "errNo := errNo": a self-assignment,
  2643. because the formal shadowed the module
  2644. variable, so the error code always
  2645. reached the caller as 0 *)
  2646. errPos := txerrPos ;
  2647. RETURN FALSE
  2648. END ;
  2649. errNo := 0 ;
  2650. errPos := 0 ;
  2651. RETURN TRUE
  2652. END Compile ;
  2653. PROCEDURE CodeBytes () : CARDINAL ;
  2654. BEGIN
  2655. RETURN codeSz
  2656. END CodeBytes ;
  2657. PROCEDURE DataBytes () : CARDINAL ;
  2658. BEGIN
  2659. RETURN dataSz
  2660. END DataBytes ;
  2661. PROCEDURE CodeByteAt (i : CARDINAL) : BYTE ;
  2662. (* i-th byte of the emitted image, for test harnesses that need to check
  2663. the generated 8086 code rather than just its size. Returns 0 past the
  2664. end of the image. *)
  2665. BEGIN
  2666. IF i >= codeSz THEN
  2667. RETURN 0
  2668. END ;
  2669. RETURN cbuf [i]
  2670. END CodeByteAt ;
  2671. END Compiler.