Compiler.mod 89 KB

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