Compiler.mod 88 KB

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