Compiler.mod 152 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001
  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 TextLimit, Length, CharAt, InsertStr, DeleteFromTo ;
  40. FROM SYSTEM IMPORT BYTE, ADR ;
  41. FROM Posix IMPORT open, read, close ;
  42. FROM Runtime IMPORT RT_Build, RT_Size, RT_Byte, RT_Entry, LoadBias ;
  43. (* The runtime is copied to the front of the code buffer and pc/dc start past
  44. it, so every emitted address is image-absolute and no relocation pass is
  45. needed. See Inittur.
  46. `LoadBias' comes from Runtime because it is the same constant on both
  47. sides of the image: Runtime adds it to every data address it bakes into its
  48. own code (FixUp, kind 2), and this module adds it to every ABSOLUTE address
  49. it bakes into the program's. It is deliberately ONE constant in ONE place
  50. rather than 0100h written out at six sites, because getting it wrong at one
  51. site is invisible - see the note on LoadBias in Runtime.mod. Relative
  52. encodings (the entry JMP, every CALL and JMP) must NOT get it: both
  53. operands shift together and the +0100h cancels. *)
  54. FROM CompilerShared IMPORT AposC, ArrRec, BI_Abs, BI_Arctan, BI_Assign,
  55. BI_BlockRead, BI_BlockWrite, BI_Chr, BI_Close,
  56. BI_ClrEol, BI_ClrScr, BI_Concat, BI_Copy, BI_Cos,
  57. BI_Delay, BI_Delete, BI_Dispose, BI_Eof, BI_Erase,
  58. BI_Exp, BI_FilePos, BI_FileSize, BI_FillChar,
  59. BI_GetDir, BI_GotoXY, BI_Halt, BI_Insert, BI_Int,
  60. BI_Intr, BI_IoResult, BI_KeyPressed, BI_Length,
  61. BI_Ln, BI_LowVideo, BI_Move, BI_Msdos, BI_New,
  62. BI_NormVideo, BI_Ofs, BI_Ord, BI_Pos, BI_Pred,
  63. BI_Random, BI_Randomize, BI_Read, BI_ReadLn,
  64. BI_Rename, BI_Reset, BI_Rewrite, BI_Round, BI_Seek,
  65. BI_Seg, BI_Sin, BI_SizeOf, BI_Sqr, BI_Sqrt, BI_Str,
  66. BI_Succ, BI_Trunc, BI_UpCase, BI_Val, BI_Write,
  67. BI_WriteLn, CodeMax, CrtCaret, CrtClr, CrtDelay,
  68. CrtDh, CrtDl, CrtEol, CrtKey, CrtLow, CrtNorm,
  69. DataStart, DirRec, ECompOvf, EConstRange, EFileNF,
  70. EInclNest, EIoHere, EMemOvf, ENoLib, ENoSemi,
  71. EPointExp, ERes, ESimpType, ETypeErr, EUnknown,
  72. EntSize, Err, FileVarSize, FldRec, KBuiltin,
  73. KConst, KFunc, KLabel, KProc, KType, KVar, MaxArrs,
  74. MaxCode, MaxFields, MaxLine, MaxName, MaxPatch,
  75. MaxPend, OK, OpAdd, OpDiv, OpMul, OpShl, OpShr,
  76. OpSub, PatchRec, PendRec, TArray, TBool, TChar,
  77. TEnum, TFile, TNone, TPtr, TReal, TRecord, TScalar,
  78. TSet, TString, TText, TU_BlockRead, TU_BlockWrite,
  79. TU_CmpStr, TU_Erase, TU_FileAssign, TU_FileClose,
  80. TU_FileEof, TU_FilePos, TU_FileRdln, TU_FileRdtok,
  81. TU_FileReadln, TU_FileReset, TU_FileRewrite,
  82. TU_FileSize, TU_FillChar, TU_GetDir, TU_Halt,
  83. TU_HeapNew, TU_InitMem, TU_IntFmt, TU_IntFmtW,
  84. TU_IoResult, TU_IoStore, TU_MemCopy, TU_Move,
  85. TU_Msdos, TU_ProgEnd, TU_Random, TU_Randomize,
  86. TU_RdBool, TU_RdByte, TU_RdChar, TU_RdInt, TU_RdLn,
  87. TU_RdStr, TU_RealAbs, TU_RealAdd, TU_RealArctan,
  88. TU_RealCmp, TU_RealCos, TU_RealDiv, TU_RealExp,
  89. TU_RealFmt, TU_RealFromInt, TU_RealIntFunc,
  90. TU_RealLn, TU_RealMul, TU_RealSin, TU_RealSqrt,
  91. TU_RealSub, TU_RealToInt, TU_RealVal, TU_RealWrite,
  92. TU_Rename, TU_RunErr, TU_Seek, TU_SetCmp,
  93. TU_SetCopy, TU_SetOp, TU_SetSingle, TU_StackChk,
  94. TU_StrCat, TU_StrCopy, TU_StrDelete, TU_StrFmtW,
  95. TU_StrIndex, TU_StrInsert, TU_StrPos, TU_StrStore,
  96. TU_ValInt, TU_ValReal, TU_WrBool, TU_WrBoolW,
  97. TU_WrChar, TU_WrCharW, TU_WrInl, TU_WrInt,
  98. TU_WrIntW, TU_WrLn, TU_WrReal, TU_WrStr, TU_WrStrW,
  99. TUntyp, TkAbsolute, TkAnd, TkArray, TkBegin,
  100. TkCase, TkConst, TkDiv, TkDo, TkDownto, TkElse,
  101. TkEnd, TkExit, TkExternal, TkFile, TkFor,
  102. TkForward, TkFunction, TkGoto, TkIf, TkIn, TkLabel,
  103. TkMod, TkNil, TkNone, TkNot, TkOf, TkOr, TkOverlay,
  104. TkPacked, TkProcedure, TkProgram, TkRecord,
  105. TkRepeat, TkSet, TkString, TkText, TkThen, TkTo,
  106. TkType, TkUntil, TkVar, TkWhile, TkWith, abortFac,
  107. arrTop, arrs, brkN, brkSave, callScratch, caseJmp,
  108. caseN, cbuf, codeSz, constBlob, crtAttrOff,
  109. curIsFunc, curProc, curStaticOff, dataBase, dataSz,
  110. dc, dirs, entRel, enumHi, enumLo, errNum, exitCnt,
  111. exitPatch, fields, fldTop, hdrCS, hdrDS, hdrFlag,
  112. hdrHeap, hdrMax, imgReady, lblHi, lblJmp, lblLo,
  113. locBytes, locFree, loopTy, nPatch, nPend,
  114. newArrIdx, parmOff, patches, pc, pend, prologAt,
  115. recFldFirst, recFldLast, resultIsReal,
  116. resultPtrOff, resultVar, rtSz, srcPos, tmpA, tmpB,
  117. txerrPos, varspc, withBoff, withFF, withFL,
  118. withIdx, withIsDI, withN, withSlot ;
  119. FROM CompilerEmit IMPORT Ebyte, Eword, PatchWord, AddPatch, SetPatTgt, EmCall, EmJmpNear, JccShort, JccShortInv, EmJcc, ResolvePatches, EmMovAxi, EmMovCxi, EmMovBpSp, EmMovAh0, EmMovAxSp, EmMovCxSp, EmPushAx, EmPopCx, EmPopDx, EmPopSi, EmPopAx, EmPushBx, EmPushCx, EmPushDx, EmPopBx, EmMovAxCx, EmMovChAl, EmMovChImm, EmXchgAxCx, EmXorAxAx, EmAddAxCx, EmSubAxCx, EmMulAxCx, EmIDivAxCx, EmAndAxCx, EmOrAxCx, EmOrAxAx, EmNegAx, EmNotAx, EmXorAl01, EmCmpAxCx, EmCmpAxi, EmSetcc, EmRelSetcc, EmIncAx, EmDecAx, EmBpDisp, EmBxDisp, EmMovBxBp, EmMovDxBp, EmMovDxBx, EmMovDxAx, EmMovAxDx, EmLdBxBx, EmLoadVarBx, EmStoreVarBx, EmLeaDiBx ;
  120. FROM CompilerSource IMPORT CurCh, GetCh, PeekAhead, Upper, SetSourcePath, ExpandIncludes, srcLen, inclActive, inclEnd ;
  121. FROM CompilerUtil IMPORT Digit, Alpha, AlphaNum, IsHexCh, W16, DropCh,
  122. DropB, DropC, BitAnd, BitOr, BitNot, NameEq,
  123. CopyStr, NumToName ;
  124. FROM CompilerSymbols IMPORT SymEntry, symtab, symTop, lexnest, wrd,
  125. NewSym, Search, DupTest, HideLocals,
  126. CopyWord, SaveWord ;
  127. FROM CompilerLex IMPORT InitKeys, IsBlank, Skip, GetWord, WddTok,
  128. DeclaresProc, KwAhead, PeekKw, MatchKey,
  129. MatchDelim, MatchAssign, MatchRange, ExpectDelim,
  130. HexVal, RdIntConst, StrNew, StrPut, RdConst,
  131. strPool, strOff, strLen, strTop, strCnt, rdStrX ;
  132. FROM CompilerIO IMPORT IoCall ;
  133. FROM CompilerExpr IMPORT AddPend, AllocSetScratch, BinOpEmit, ConstAdd,
  134. ConstCmp, ConstMul, ConstSub, EmAsciiZLiteral,
  135. EmCallMost, EmCopyRealTo, EmPushRealAddr,
  136. EmPushRealSlot, EmPushSetAddr, EmPushStrAddr,
  137. EmPushStrOperand, EmPushStrSlot, EmSetBlob,
  138. EmStrBlob, EmXchgAxDx, EmitBoundsCheck, FoldSetIn,
  139. IntToMbf, IsCharArray, LoadAtom, LoadPair,
  140. MaterializeDyn, NewScratchReal,
  141. NewScratchRealBlock, NewScratchStr, NewScratchWord,
  142. ParmIsVar, ParmSlot, ParseAdd, ParseAtom,
  143. ParseCall, ParseCallArgs, ParseCmp, ParseExpr,
  144. ParseMul, ParseNeg, ParseSub,
  145. ParseVar, RdSetBase, RdSetLiteral, RealBin,
  146. RealOperand, RealUnary, SaveLeft, SetBinEmit,
  147. SetPairSize, SetRangeBits, StrConcat, StrOperand,
  148. VarSize, WithLookup ;
  149. (* ---------------------------------------------------------------- *)
  150. (* constants *)
  151. (* ---------------------------------------------------------------- *)
  152. (* ---------------------------------------------------------------- *)
  153. (* small char helpers *)
  154. (* ---------------------------------------------------------------- *)
  155. (* ---------------------------------------------------------------- *)
  156. (* errors *)
  157. (* ---------------------------------------------------------------- *)
  158. (* ---------------------------------------------------------------- *)
  159. (* {$I file} include *)
  160. (* ---------------------------------------------------------------- *)
  161. PROCEDURE SetSource (path : ARRAY OF CHAR) ;
  162. (* Public name kept for Shell/Linker; the source module owns the directory
  163. string used by {$I file} resolution. *)
  164. BEGIN
  165. SetSourcePath (path)
  166. END SetSource ;
  167. PROCEDURE EmLoadVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
  168. (* A local is [BP+off] and `off' is already a frame displacement, so it needs
  169. no bias. A global is [off] with a DIRECT displacement, i.e. an absolute
  170. address, and that is the image offset + LoadBias - see Runtime.LoadBias. *)
  171. BEGIN
  172. IF nbytes = 1 THEN
  173. IF local THEN
  174. Ebyte (8AH) ; EmBpDisp (off, 0)
  175. ELSE
  176. Ebyte (0A0H) ; Eword ((off + LoadBias) MOD 10000H)
  177. END
  178. ELSE
  179. IF local THEN
  180. Ebyte (8BH) ; EmBpDisp (off, 0)
  181. ELSE
  182. Ebyte (0A1H) ; Eword ((off + LoadBias) MOD 10000H)
  183. END
  184. END
  185. END EmLoadVar ;
  186. PROCEDURE EmXorByteGlob (off, v : CARDINAL) ;
  187. BEGIN Ebyte (80H) ; Ebyte (36H) ; Eword ((off + LoadBias) MOD 10000H) ;
  188. Ebyte (VAL (BYTE, v)) END EmXorByteGlob ;
  189. PROCEDURE EmStoreVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
  190. BEGIN
  191. IF nbytes = 1 THEN
  192. IF local THEN
  193. Ebyte (88H) ; EmBpDisp (off, 0)
  194. ELSE
  195. Ebyte (0A2H) ; Eword ((off + LoadBias) MOD 10000H)
  196. END
  197. ELSE
  198. IF local THEN
  199. Ebyte (89H) ; EmBpDisp (off, 0)
  200. ELSE
  201. Ebyte (0A3H) ; Eword ((off + LoadBias) MOD 10000H)
  202. END
  203. END
  204. END EmStoreVar ;
  205. PROCEDURE EmPushVarAddr (local : BOOLEAN ; off : CARDINAL) ;
  206. (* LEA AX,[BP+disp] / LEA AX,[off] then PUSH AX - READ passes the address of
  207. a variable, not its value. 8D 46 disp is LEA AX,[BP+disp8] and
  208. 8D 86 lo hi is LEA AX,[BP+disp16]; 8D 06 off is LEA AX,[off]
  209. (mod=00 rm=110 = the direct disp16 form). All three are 8086-legal.
  210. The [off] form is absolute and so carries LoadBias; the [BP+disp] forms
  211. are displacements and so do not. *)
  212. BEGIN
  213. IF local THEN
  214. Ebyte (8DH) ; EmBpDisp (off, 0)
  215. ELSE
  216. Ebyte (8DH) ; Ebyte (06H) ; Eword ((off + LoadBias) MOD 10000H)
  217. END ;
  218. EmPushAx ()
  219. END EmPushVarAddr ;
  220. PROCEDURE EmLeaDi (local : BOOLEAN ; off : CARDINAL) ;
  221. (* LEA DI,[BP+disp] / LEA DI,[off] - the run-time address of an indexed
  222. string's element, computed once in ParseSub and read by LoadAtom,
  223. Assignment and IoCall.
  224. It is the same three encodings EmPushVarAddr uses for AX - 8D 7E disp8,
  225. 8D BE disp16, 8D 3E off - with the ModR/M reg field set to DI (111)
  226. instead of the accumulator, which is why EmBpDisp now takes that field and
  227. this is still the one place the disp8/disp16 choice is made. A local is
  228. [BP+disp] with no bias; a global is [off] and carries LoadBias, absolute.
  229. The later `MOV AL,[DI]` / `MOV [DI],AL` read through DS rather than SS.
  230. That is correct here for the same reason TP3's own string ops use DI with
  231. the default segment: a .COM loads with DS=SS=CS=ES, and nothing in this
  232. compiler or its runtime ever reloads a segment register, so a BP-relative
  233. frame address held in DI addresses the same byte. *)
  234. BEGIN
  235. IF local THEN
  236. Ebyte (8DH) ; EmBpDisp (off, 7)
  237. ELSE
  238. Ebyte (8DH) ; Ebyte (3EH) ; Eword ((off + LoadBias) MOD 10000H)
  239. END
  240. END EmLeaDi ;
  241. PROCEDURE EmMovBxAx () ;
  242. BEGIN Ebyte (89H) ; Ebyte (0C3H) END EmMovBxAx ; (* MOV BX,AX *)
  243. PROCEDURE EmitFrameBase (level : CARDINAL) ;
  244. (* Leave BX pointing at the frame for lexical scope `level', pushing the old
  245. BX. The current frame is at `lexnest'; each step follows the hidden
  246. static link stored by the enclosing procedure. The caller must POP BX
  247. after the access. *)
  248. VAR p, k : CARDINAL ;
  249. BEGIN
  250. EmPushBx () ;
  251. EmMovBxBp () ;
  252. p := curProc ;
  253. k := lexnest ;
  254. WHILE k > level DO
  255. EmLdBxBx (symtab [p].staticOff) ;
  256. p := symtab [p].parent ;
  257. DEC (k)
  258. END
  259. END EmitFrameBase ;
  260. PROCEDURE IsOuterLocal (idx : CARDINAL) : BOOLEAN ;
  261. BEGIN
  262. RETURN symtab [idx].local AND (symtab [idx].level < lexnest)
  263. END IsOuterLocal ;
  264. PROCEDURE LoadSymVar (idx, off, nbytes : CARDINAL) ;
  265. BEGIN
  266. IF IsOuterLocal (idx) THEN
  267. EmitFrameBase (symtab [idx].level) ;
  268. EmLoadVarBx (off, nbytes) ;
  269. EmPopBx ()
  270. ELSE
  271. EmLoadVar (symtab [idx].local, off, nbytes)
  272. END
  273. END LoadSymVar ;
  274. PROCEDURE StoreSymVar (idx, off, nbytes : CARDINAL) ;
  275. BEGIN
  276. IF IsOuterLocal (idx) THEN
  277. EmitFrameBase (symtab [idx].level) ;
  278. EmStoreVarBx (off, nbytes) ;
  279. EmPopBx ()
  280. ELSE
  281. EmStoreVar (symtab [idx].local, off, nbytes)
  282. END
  283. END StoreSymVar ;
  284. PROCEDURE EmPushSymAddr (idx, off : CARDINAL) ;
  285. BEGIN
  286. IF IsOuterLocal (idx) THEN
  287. EmitFrameBase (symtab [idx].level) ;
  288. Ebyte (8DH) ; EmBxDisp (off, 0) ; (* LEA AX,[BX+off] *)
  289. EmPopBx () ; (* restore BX before pushing AX *)
  290. EmPushAx ()
  291. ELSE
  292. EmPushVarAddr (symtab [idx].local, off)
  293. END
  294. END EmPushSymAddr ;
  295. PROCEDURE LeaDiSym (idx, off : CARDINAL) ;
  296. BEGIN
  297. IF IsOuterLocal (idx) THEN
  298. EmitFrameBase (symtab [idx].level) ;
  299. EmLeaDiBx (off) ;
  300. EmPopBx ()
  301. ELSE
  302. EmLeaDi (symtab [idx].local, off)
  303. END
  304. END LeaDiSym ;
  305. PROCEDURE EmLoadVarIndSym (idx, nbytes : CARDINAL) ;
  306. (* AX := the value reached through a VAR parameter's address slot. *)
  307. BEGIN
  308. LoadSymVar (idx, symtab [idx].off, 2) ;
  309. EmMovBxAx () ;
  310. IF nbytes = 1 THEN
  311. Ebyte (8AH) ; Ebyte (07H) ;
  312. EmMovAh0 ()
  313. ELSE
  314. Ebyte (8BH) ; Ebyte (07H)
  315. END
  316. END EmLoadVarIndSym ;
  317. PROCEDURE StoreVarIndPrepSym (idx : CARDINAL) ;
  318. (* DI := the address held by a VAR parameter's slot, then PUSH DI. *)
  319. BEGIN
  320. LoadSymVar (idx, symtab [idx].off, 2) ;
  321. EmMovDiAx () ;
  322. EmPushDi ()
  323. END StoreVarIndPrepSym ;
  324. PROCEDURE EmitStaticLink (idx : CARDINAL) ;
  325. (* Pass the lexical parent frame in DX before a call to a nested procedure.
  326. The callee saves it in its hidden static-link local. A top-level procedure
  327. has no parent frame and gets nothing. *)
  328. VAR level : CARDINAL ;
  329. BEGIN
  330. level := symtab [idx].level ;
  331. IF level = 0 THEN
  332. RETURN
  333. END ;
  334. IF level = lexnest THEN
  335. EmMovDxBp ()
  336. ELSE
  337. EmitFrameBase (level) ;
  338. EmMovDxBx () ;
  339. EmPopBx ()
  340. END
  341. END EmitStaticLink ;
  342. PROCEDURE EmAddDiAx () ;
  343. BEGIN Ebyte (01H) ; Ebyte (0C7H) END EmAddDiAx ; (* 01 C7: DI := DI + AX *)
  344. PROCEDURE EmAddDiImm (n : CARDINAL) ;
  345. (* ADD DI,imm16: a record field's offset when the element's address is already
  346. in DI (`Sheet[I,J].Field', `with Sheet[I,J] do'). *)
  347. BEGIN
  348. Ebyte (81H) ; Ebyte (0C7H) ; Eword (n MOD 10000H)
  349. END EmAddDiImm ;
  350. PROCEDURE EmLdDiGlob (off : CARDINAL) ;
  351. (* MOV DI,[off]: recover the base address WITH saved in a scratch word. The
  352. body of a WITH may clobber DI, so the address is parked in the data area
  353. and reloaded for every field access. *)
  354. BEGIN
  355. Ebyte (8BH) ; Ebyte (3EH) ; Eword ((off + LoadBias) MOD 10000H)
  356. END EmLdDiGlob ;
  357. PROCEDURE EmStDiGlob (off : CARDINAL) ;
  358. (* MOV [off],DI: the WITH statement's base-address scratch. *)
  359. BEGIN
  360. Ebyte (89H) ; Ebyte (3EH) ; Eword ((off + LoadBias) MOD 10000H)
  361. END EmStDiGlob ;
  362. PROCEDURE EmLdAlDi () ;
  363. BEGIN Ebyte (8AH) ; Ebyte (05H) END EmLdAlDi ; (* 8A 05: AL := [DI] *)
  364. PROCEDURE EmLdAlSi () ;
  365. BEGIN Ebyte (8AH) ; Ebyte (04H) END EmLdAlSi ; (* 8A 04: AL := [SI] *)
  366. PROCEDURE EmIncSi () ;
  367. BEGIN Ebyte (46H) END EmIncSi ;
  368. PROCEDURE EmIncDi () ;
  369. BEGIN Ebyte (47H) END EmIncDi ;
  370. PROCEDURE EmMovDiAx () ;
  371. BEGIN Ebyte (89H) ; Ebyte (0C7H) END EmMovDiAx ; (* 89 C7: DI := AX *)
  372. PROCEDURE EmMovAxDi () ;
  373. BEGIN Ebyte (89H) ; Ebyte (0F8H) END EmMovAxDi ; (* 89 F8: AX := DI *)
  374. PROCEDURE EmLdAxDi () ;
  375. BEGIN Ebyte (8BH) ; Ebyte (05H) END EmLdAxDi ; (* 8B 05: AX := [DI] *)
  376. PROCEDURE EmStDiAl () ;
  377. BEGIN Ebyte (88H) ; Ebyte (05H) END EmStDiAl ; (* 88 05: [DI] := AL *)
  378. PROCEDURE EmPushDi () ;
  379. BEGIN Ebyte (57H) END EmPushDi ; (* PUSH DI *)
  380. PROCEDURE EmPushSi () ;
  381. BEGIN Ebyte (56H) END EmPushSi ; (* PUSH SI *)
  382. PROCEDURE EmMovAxDs () ;
  383. BEGIN Ebyte (8CH) ; Ebyte (0D8H) END EmMovAxDs ; (* MOV AX,DS *)
  384. PROCEDURE EmPopDi () ;
  385. BEGIN Ebyte (5FH) END EmPopDi ; (* POP DI *)
  386. PROCEDURE EmLoadVarInd (off, nbytes : CARDINAL) ;
  387. (* AX := the value of a VAR parameter. [BP+off] holds the ADDRESS; BX carries
  388. it so the byte/word load has a base register. The address is a DS offset:
  389. a .COM has one segment, which is why no segment register appears here. *)
  390. BEGIN
  391. EmLoadVar (TRUE, off, 2) ; (* AX := [BP+off], the address *)
  392. Ebyte (89H) ; Ebyte (0C3H) ; (* MOV BX,AX *)
  393. IF nbytes = 1 THEN
  394. Ebyte (8AH) ; Ebyte (07H) ; (* MOV AL,[BX] *)
  395. EmMovAh0 ()
  396. ELSE
  397. Ebyte (8BH) ; Ebyte (07H) (* MOV AX,[BX] *)
  398. END
  399. END EmLoadVarInd ;
  400. PROCEDURE EmStoreVarIndPrep (off : CARDINAL) ;
  401. (* DI := the address of a VAR parameter, then PUSH DI. The push protects it
  402. across the parse of the right-hand side, which may use DI for a string
  403. element of its own. EmStoreInd finishes the store after POP DI. *)
  404. BEGIN
  405. Ebyte (8BH) ; EmBpDisp (off, 7) ; (* MOV DI,[BP+off] *)
  406. EmPushDi ()
  407. END EmStoreVarIndPrep ;
  408. PROCEDURE EmStDiImm (off, v : CARDINAL) ;
  409. (* MOV byte [DI+off],imm8: one byte of a set constructor stored through a
  410. DI-addressed record field. Sets are at most 32 bytes, so disp8 is enough. *)
  411. BEGIN
  412. Ebyte (0C6H) ; Ebyte (45H) ; Ebyte (VAL (BYTE, off)) ; Ebyte (VAL (BYTE, v))
  413. END EmStDiImm ;
  414. PROCEDURE EmStDiAlOff (off : CARDINAL) ;
  415. (* MOV [DI+off],AL: one byte of a real copied through a DI-addressed field. *)
  416. BEGIN
  417. Ebyte (88H) ; Ebyte (45H) ; Ebyte (VAL (BYTE, off))
  418. END EmStDiAlOff ;
  419. PROCEDURE EmLdAlDiOff (off : CARDINAL) ;
  420. (* MOV AL,[DI+off]: the byte of a set variable that holds an element's bit. *)
  421. BEGIN
  422. Ebyte (8AH) ; Ebyte (45H) ; Ebyte (VAL (BYTE, off))
  423. END EmLdAlDiOff ;
  424. PROCEDURE EmCmpAlImm (v : CARDINAL) ;
  425. BEGIN Ebyte (3CH) ; Ebyte (VAL (BYTE, v)) END EmCmpAlImm ; (* CMP AL,imm8 *)
  426. PROCEDURE EmSubAlImm (v : CARDINAL) ;
  427. BEGIN Ebyte (2CH) ; Ebyte (VAL (BYTE, v)) END EmSubAlImm ; (* SUB AL,imm8 *)
  428. PROCEDURE EmMovClAl () ;
  429. BEGIN Ebyte (88H) ; Ebyte (0C1H) END EmMovClAl ; (* MOV CL,AL *)
  430. PROCEDURE EmAndClImm (v : CARDINAL) ;
  431. BEGIN Ebyte (80H) ; Ebyte (0E1H) ; Ebyte (VAL (BYTE, v)) END EmAndClImm ;
  432. PROCEDURE EmShrAx1 () ;
  433. BEGIN Ebyte (0D1H) ; Ebyte (0E8H) END EmShrAx1 ; (* SHR AX,1 *)
  434. PROCEDURE EmShlAxCl () ;
  435. BEGIN Ebyte (0D3H) ; Ebyte (0E0H) END EmShlAxCl ; (* SHL AX,CL *)
  436. PROCEDURE EmShrAxCl () ;
  437. BEGIN Ebyte (0D3H) ; Ebyte (0E8H) END EmShrAxCl ; (* SHR AX,CL *)
  438. PROCEDURE EmMovBxi (v : CARDINAL) ;
  439. BEGIN Ebyte (0BBH) ; Eword (v MOD 10000H) END EmMovBxi ; (* MOV BX,imm16 *)
  440. PROCEDURE EmLeaBx (off : CARDINAL) ;
  441. (* LEA BX,[BP+off]: the address of a local set. *)
  442. BEGIN
  443. Ebyte (8DH) ; EmBpDisp (off, 3)
  444. END EmLeaBx ;
  445. PROCEDURE EmMovBxDi () ;
  446. BEGIN Ebyte (89H) ; Ebyte (0FBH) END EmMovBxDi ; (* MOV BX,DI *)
  447. PROCEDURE EmAddBxAx () ;
  448. BEGIN Ebyte (01H) ; Ebyte (0C3H) END EmAddBxAx ; (* ADD BX,AX *)
  449. PROCEDURE EmLdAlBx () ;
  450. BEGIN Ebyte (8AH) ; Ebyte (07H) END EmLdAlBx ; (* MOV AL,[BX] *)
  451. PROCEDURE EmMovDlImm (v : CARDINAL) ;
  452. BEGIN Ebyte (0B2H) ; Ebyte (VAL (BYTE, v)) END EmMovDlImm ;
  453. PROCEDURE EmShlDlCl () ;
  454. BEGIN Ebyte (0D2H) ; Ebyte (0E2H) END EmShlDlCl ; (* SHL DL,CL *)
  455. PROCEDURE EmAndAlDl () ;
  456. BEGIN Ebyte (20H) ; Ebyte (0D0H) END EmAndAlDl ; (* AND AL,DL *)
  457. PROCEDURE EmAndAlImm (v : CARDINAL) ;
  458. BEGIN Ebyte (24H) ; Ebyte (VAL (BYTE, v)) END EmAndAlImm ; (* AND AL,imm8 *)
  459. PROCEDURE EmStoreInd (nbytes : CARDINAL) ;
  460. (* [DI] := AL/WORD, after POP DI - the companion of EmStoreVarIndPrep. *)
  461. BEGIN
  462. IF nbytes = 1 THEN
  463. EmStDiAl () (* MOV [DI],AL *)
  464. ELSE
  465. Ebyte (89H) ; Ebyte (05H) (* MOV [DI],AX *)
  466. END
  467. END EmStoreInd ;
  468. PROCEDURE EmFileIo (reading : BOOLEAN ; fileLocal : BOOLEAN ; fileOff : CARDINAL ;
  469. addrKind : CARDINAL ; addrLocal : BOOLEAN ;
  470. addrOff, count : CARDINAL ; stringArg : BOOLEAN) ;
  471. (* Typed-file block I/O: read/write `count' bytes at the data argument to the
  472. open file in the file variable.
  473. ABI to DOS: BX = handle, CX = byte count, DS:DX = buffer, AH = 3Fh read /
  474. 40h write. The handle is the first word of the file control block. For a
  475. DI-addressed record (`Sheet[I,J]') the address is already in DI; a plain
  476. variable's address is formed by LEA. The DOS result is handed to the
  477. io_store runtime entry, so IOResult sees it.
  478. This is emitted directly rather than through a runtime entry because the
  479. runtime has only ~45 bytes of probe headroom left; the code is two words
  480. per call site and the compiler's code budget is the loose one. *)
  481. BEGIN
  482. IF addrKind = 5 THEN
  483. (* DI already holds a kind-5 element's address. *)
  484. ELSIF addrKind = 4 THEN
  485. EmLeaDi (FALSE, addrOff) (* a code-resident blob *)
  486. ELSIF addrKind = 1 THEN
  487. EmLeaDi (addrLocal, addrOff)
  488. ELSE
  489. Err (ETypeErr) ;
  490. RETURN
  491. END ;
  492. Ebyte (89H) ; Ebyte (0FAH) ; (* MOV DX,DI *)
  493. IF stringArg THEN
  494. (* DX points at the characters, CX = the length byte at [DI]. *)
  495. Ebyte (42H) ; (* INC DX *)
  496. EmLdAlDi () ;
  497. EmMovClAl () ;
  498. Ebyte (30H) ; Ebyte (0EDH) (* XOR CH,CH *)
  499. ELSE
  500. EmMovCxi (count)
  501. END ;
  502. EmLoadVar (fileLocal, fileOff, 2) ; (* AX := the DOS handle *)
  503. Ebyte (89H) ; Ebyte (0C3H) ; (* MOV BX,AX *)
  504. Ebyte (0B4H) ; (* MOV AH,3Fh/40h *)
  505. IF reading THEN Ebyte (3FH) ELSE Ebyte (40H) END ;
  506. Ebyte (0CDH) ; Ebyte (21H) ; (* INT 21h *)
  507. DropC (EmCall (TU_IoStore))
  508. END EmFileIo ;
  509. PROCEDURE EmInlineCh (ch : CARDINAL) : CARDINAL ;
  510. (* Emit a one-character string literal behind a JMP and return the image
  511. offset of its length byte. Used by text-file Write(File, ' '). *)
  512. VAR here : CARDINAL ;
  513. BEGIN
  514. Ebyte (0EBH) ; Ebyte (2) ;
  515. here := pc ;
  516. Ebyte (1) ; Ebyte (VAL (BYTE, ch)) ;
  517. RETURN here
  518. END EmInlineCh ;
  519. PROCEDURE EmInlineCrLf () : CARDINAL ;
  520. (* The CR LF pair every text-file Writeln emits. *)
  521. VAR here : CARDINAL ;
  522. BEGIN
  523. Ebyte (0EBH) ; Ebyte (3) ;
  524. here := pc ;
  525. Ebyte (2) ; Ebyte (13) ; Ebyte (10) ;
  526. RETURN here
  527. END EmInlineCrLf ;
  528. PROCEDURE EmSubSp (n : CARDINAL) ;
  529. BEGIN
  530. Ebyte (81H) ; Ebyte (0ECH) ; Eword (n MOD 10000H)
  531. END EmSubSp ;
  532. PROCEDURE EmAddSp (n : CARDINAL) ;
  533. BEGIN
  534. IF n <= 126 THEN
  535. Ebyte (83H) ; Ebyte (0C4H) ; Ebyte (VAL (BYTE, n))
  536. ELSE
  537. Ebyte (81H) ; Ebyte (0C4H) ; Eword (n MOD 10000H)
  538. END
  539. END EmAddSp ;
  540. PROCEDURE EmPushBp () ;
  541. BEGIN
  542. Ebyte (55H)
  543. END EmPushBp ;
  544. PROCEDURE EmLeave () ;
  545. BEGIN
  546. Ebyte (0C9H)
  547. END EmLeave ;
  548. PROCEDURE EmRet () ;
  549. BEGIN
  550. Ebyte (0C3H)
  551. END EmRet ;
  552. (* ---------------------------------------------------------------- *)
  553. (* symbol table *)
  554. (* ---------------------------------------------------------------- *)
  555. (* ---------------------------------------------------------------- *)
  556. (* lexer *)
  557. (* ---------------------------------------------------------------- *)
  558. (* ---------------------------------------------------------------- *)
  559. (* forward declarations *)
  560. (* ---------------------------------------------------------------- *)
  561. PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ; FORWARD ;
  562. PROCEDURE Statmnt () ; FORWARD ;
  563. PROCEDURE SlotSize (cls, size : CARDINAL) : CARDINAL ; FORWARD ;
  564. PROCEDURE ParamSlotBytes (e : SymEntry) : CARDINAL ; FORWARD ;
  565. PROCEDURE ParseLabelStmt () ;
  566. (* numeric or named label definition 'n :' / 'Name:' *)
  567. VAR n : CARDINAL ;
  568. nm : ARRAY [0..MaxName] OF CHAR ;
  569. idx : CARDINAL ;
  570. i : CARDINAL ;
  571. BEGIN
  572. IF Alpha (CurCh ()) THEN
  573. GetWord () ;
  574. SaveWord (nm)
  575. ELSE
  576. n := 0 ;
  577. WHILE Digit (CurCh ()) DO
  578. n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
  579. DropCh (GetCh ())
  580. END ;
  581. NumToName (n, nm)
  582. END ;
  583. ExpectDelim (':', ENoSemi) ;
  584. IF Search (nm, idx) THEN
  585. IF symtab [idx].tag = KLabel THEN
  586. symtab [idx].defnd := TRUE ;
  587. symtab [idx].goPos := pc ;
  588. i := 0 ;
  589. WHILE i < nPend DO
  590. IF (pend [i].kind = 0) AND (pend [i].who = idx) THEN
  591. SetPatTgt (pend [i].place, pc) ;
  592. pend [i].kind := 99
  593. END ;
  594. INC (i)
  595. END
  596. ELSE
  597. Err (EUnknown)
  598. END
  599. ELSE
  600. idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
  601. symtab [idx].defnd := TRUE ;
  602. symtab [idx].goPos := pc
  603. END
  604. END ParseLabelStmt ;
  605. PROCEDURE BoundsOf (r : ERes ; VAR lo, hi : CARDINAL) : BOOLEAN ;
  606. (* Find a scalar/subrange bound for an assignment target. Direct symbols use
  607. their SymEntry bounds; a record field is found by its byte offset. *)
  608. VAR fi : CARDINAL ;
  609. BEGIN
  610. lo := 0 ; hi := 0 ;
  611. IF r.hasBounds THEN
  612. lo := W16 (r.blo) ; hi := W16 (r.bhi) ;
  613. RETURN TRUE
  614. END ;
  615. IF r.kind # 1 THEN RETURN FALSE END ;
  616. IF symtab [r.idx].fldFirst < symtab [r.idx].fldLast THEN
  617. fi := symtab [r.idx].fldFirst ;
  618. WHILE fi < symtab [r.idx].fldLast DO
  619. IF fields [fi].off = r.boff THEN
  620. IF ((fields [fi].cls = TScalar) OR (fields [fi].cls = TEnum) OR
  621. (fields [fi].cls = TChar)) AND
  622. (fields [fi].lo # fields [fi].hi) THEN
  623. lo := W16 (fields [fi].lo) ; hi := W16 (fields [fi].hi) ;
  624. RETURN TRUE
  625. END ;
  626. RETURN FALSE
  627. END ;
  628. INC (fi)
  629. END ;
  630. RETURN FALSE
  631. END ;
  632. IF r.boff = 0 THEN
  633. IF ((r.cls = TScalar) OR (r.cls = TEnum) OR (r.cls = TChar)) AND
  634. (symtab [r.idx].lo # symtab [r.idx].hi) THEN
  635. lo := W16 (symtab [r.idx].lo) ; hi := W16 (symtab [r.idx].hi) ;
  636. RETURN TRUE
  637. END
  638. END ;
  639. RETURN FALSE
  640. END BoundsOf ;
  641. PROCEDURE Assignment (r : ERes) ;
  642. (* ':=' already consumed by the caller; store expression into r *)
  643. VAR src : ERes ;
  644. k, n, savePos, scratch, blo, bhi : CARDINAL ;
  645. sbuf : ARRAY [0..31] OF CHAR ;
  646. rbuf : ARRAY [0..5] OF CHAR ;
  647. BEGIN
  648. IF (r.kind # 1) AND (r.kind # 5) THEN
  649. Err (EUnknown) ;
  650. RETURN
  651. END ;
  652. IF (r.kind = 1) AND symtab [r.idx].isPort THEN
  653. ParseExpr (src) ;
  654. IF NOT OK () THEN RETURN END ;
  655. LoadAtom (src) ;
  656. EmLoadVar (FALSE, symtab [tmpA].off, 2) ;
  657. EmMovDxAx () ;
  658. Ebyte (0EEH) ; (* OUT DX,AL *)
  659. RETURN
  660. END ;
  661. IF IsCharArray (r) AND ((r.kind = 1) OR (r.kind = 5)) THEN
  662. (* Fixed CHAR arrays are Pascal strings too, but they have no length
  663. byte: a literal fills the array and pads the tail with spaces. *)
  664. ParseExpr (src) ;
  665. IF NOT OK () THEN
  666. RETURN
  667. END ;
  668. IF src.kind = 3 THEN
  669. n := VarSize (r) ;
  670. k := 0 ;
  671. WHILE k < n DO
  672. IF k < strLen [src.strx] THEN
  673. IF r.kind = 5 THEN
  674. EmStDiImm (k, ORD (strPool [strOff [src.strx] + k]))
  675. ELSE
  676. EmMovAxi (ORD (strPool [strOff [src.strx] + k])) ;
  677. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1)
  678. END
  679. ELSE
  680. IF r.kind = 5 THEN
  681. EmStDiImm (k, ORD (' '))
  682. ELSE
  683. EmMovAxi (ORD (' ')) ;
  684. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1)
  685. END
  686. END ;
  687. INC (k)
  688. END ;
  689. RETURN
  690. END ;
  691. Err (ETypeErr) ;
  692. RETURN
  693. END ;
  694. IF ((r.kind = 1) OR (r.kind = 5)) AND (VarSize (r) > 2)
  695. AND ((r.cls = TRecord) OR (r.cls = TArray)) THEN
  696. (* Whole record/array assignment: copy VarSize(r) bytes. This is what
  697. BIGREC/DYNREC's `Mother := Self', `Father^ := Mother^' and
  698. `Friend[Index] := Mother' need. A DI-addressed destination has to
  699. be parked before the source expression can reuse DI. *)
  700. IF r.kind = 5 THEN
  701. EmPushDi ()
  702. END ;
  703. ParseExpr (src) ;
  704. IF NOT OK () THEN
  705. RETURN
  706. END ;
  707. IF (src.kind # 1) AND (src.kind # 5) THEN
  708. Err (ETypeErr) ;
  709. RETURN
  710. END ;
  711. IF VarSize (src) # VarSize (r) THEN
  712. Err (ETypeErr) ;
  713. RETURN
  714. END ;
  715. IF src.kind = 5 THEN
  716. EmPushDi ()
  717. ELSE
  718. EmPushSymAddr (src.idx, (symtab [src.idx].off + src.boff) MOD 10000H)
  719. END ;
  720. IF r.kind = 5 THEN
  721. (* stack now holds dest, source; make it source, dest for the ABI *)
  722. EmPopAx () ;
  723. EmPopBx () ;
  724. EmPushAx () ;
  725. EmPushBx ()
  726. ELSE
  727. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  728. END ;
  729. EmMovCxi (VarSize (r)) ;
  730. DropC (EmCall (TU_MemCopy)) ;
  731. EmAddSp (4) ;
  732. RETURN
  733. END ;
  734. IF r.cls = TReal THEN
  735. (* A 6-byte REAL. A compile-time ordinal constant is converted to the
  736. measured MBF layout; another REAL field is copied byte for byte.
  737. Real arithmetic and real-valued expressions are a later stage and
  738. are refused, not guessed. *)
  739. IF (r.kind = 1) AND symtab [r.idx].isVar THEN
  740. (* VAR REAL parameter: the slot holds the destination address. *)
  741. ParseExpr (src) ;
  742. IF NOT OK () THEN
  743. RETURN
  744. END ;
  745. IF (src.kind = 0) AND (src.cls # TString) AND (src.cls # TSet) THEN
  746. IntToMbf (src.imm, rbuf) ;
  747. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  748. EmMovDiAx () ;
  749. k := 0 ;
  750. WHILE k < 6 DO
  751. EmStDiImm (k, ORD (rbuf [k])) ;
  752. INC (k)
  753. END ;
  754. RETURN
  755. END ;
  756. IF (src.cls = TReal) AND ((src.kind = 1) OR (src.kind = 5)) THEN
  757. (* Destination pointer first, then the source address; pop SI
  758. (source), DI (destination), and copy six bytes. *)
  759. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  760. EmPushAx () ;
  761. IF src.kind = 5 THEN
  762. EmPushDi ()
  763. ELSIF symtab [src.idx].isVar THEN
  764. LoadSymVar (src.idx, symtab [src.idx].off, 2) ;
  765. EmPushAx ()
  766. ELSE
  767. EmPushSymAddr (src.idx, (symtab [src.idx].off + src.boff) MOD 10000H)
  768. END ;
  769. EmPopSi () ;
  770. EmPopDi () ;
  771. k := 0 ;
  772. WHILE k < 6 DO
  773. EmLdAlSi () ;
  774. EmStDiAl () ;
  775. EmIncSi () ;
  776. EmIncDi () ;
  777. INC (k)
  778. END ;
  779. RETURN
  780. END ;
  781. Err (ENoLib) ;
  782. RETURN
  783. END ;
  784. IF r.kind = 5 THEN
  785. EmPushDi ()
  786. END ;
  787. ParseExpr (src) ;
  788. IF NOT OK () THEN
  789. RETURN
  790. END ;
  791. IF (src.cls # TReal) AND (src.cls # TString) AND (src.cls # TSet)
  792. AND (src.kind # 0) AND ((r.kind = 1) OR (r.kind = 5)) THEN
  793. (* INTEGER/CHAR/BOOLEAN -> REAL, e.g. Size := Count in CONVERT. *)
  794. scratch := NewScratchReal () ;
  795. RealOperand (src, symtab [scratch].off) ;
  796. IF r.kind = 5 THEN
  797. EmPopDi ()
  798. END ;
  799. k := 0 ;
  800. WHILE k < 6 DO
  801. LoadSymVar (scratch, (symtab [scratch].off + k) MOD 10000H, 1) ;
  802. IF r.kind = 5 THEN
  803. EmStDiAlOff (k)
  804. ELSE
  805. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1)
  806. END ;
  807. INC (k)
  808. END ;
  809. RETURN
  810. END ;
  811. IF (src.kind = 0) AND (src.cls # TString) AND (src.cls # TSet) THEN
  812. IntToMbf (src.imm, rbuf) ;
  813. IF r.kind = 5 THEN
  814. EmPopDi () ;
  815. k := 0 ;
  816. WHILE k < 6 DO
  817. EmStDiImm (k, ORD (rbuf [k])) ;
  818. INC (k)
  819. END
  820. ELSIF r.kind = 1 THEN
  821. k := 0 ;
  822. WHILE k < 6 DO
  823. EmMovAxi (ORD (rbuf [k])) ;
  824. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1) ;
  825. INC (k)
  826. END
  827. ELSE
  828. Err (ENoLib)
  829. END ;
  830. RETURN
  831. END ;
  832. IF (src.kind = 5) AND (src.cls = TReal) AND (r.kind = 5) THEN
  833. EmPushDi () ; (* source address *)
  834. EmPopSi () ;
  835. EmPopDi () ; (* destination address *)
  836. k := 0 ;
  837. WHILE k < 6 DO
  838. EmLdAlSi () ; EmStDiAl () ; EmIncSi () ; EmIncDi () ;
  839. INC (k)
  840. END ;
  841. RETURN
  842. END ;
  843. IF (src.kind = 5) AND (src.cls = TReal) AND (r.kind = 1) THEN
  844. EmPushDi () ;
  845. EmPopSi () ;
  846. k := 0 ;
  847. WHILE k < 6 DO
  848. EmLdAlSi () ;
  849. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1) ;
  850. EmIncSi () ;
  851. INC (k)
  852. END ;
  853. RETURN
  854. END ;
  855. IF (src.kind = 1) AND (src.cls = TReal) THEN
  856. IF r.kind = 5 THEN
  857. IF src.kind = 5 THEN
  858. Err (ENoLib) ;
  859. RETURN
  860. END ;
  861. EmPopDi () ;
  862. k := 0 ;
  863. WHILE k < 6 DO
  864. LoadSymVar (src.idx, (symtab [src.idx].off + src.boff + k) MOD 10000H, 1) ;
  865. EmStDiAlOff (k) ;
  866. INC (k)
  867. END
  868. ELSIF r.kind = 1 THEN
  869. k := 0 ;
  870. WHILE k < 6 DO
  871. LoadSymVar (src.idx, (symtab [src.idx].off + src.boff + k) MOD 10000H, 1) ;
  872. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1) ;
  873. INC (k)
  874. END
  875. ELSE
  876. Err (ENoLib)
  877. END ;
  878. RETURN
  879. END ;
  880. Err (ENoLib) ;
  881. RETURN
  882. END ;
  883. IF r.cls = TSet THEN
  884. (* `field := [elements]'. The constructor's TYPE comes from the target:
  885. CALC's CellStatus is `set of Attributes' (one byte), not the default
  886. 32-byte `set of Char' an untyped literal would become. The bytes are
  887. known at compile time; store them at symbol+boff or through DI. *)
  888. IF (r.setsize = 0) OR (r.setsize > 32) THEN
  889. Err (ENoLib) ;
  890. RETURN
  891. END ;
  892. Skip () ;
  893. IF CurCh () = '[' THEN
  894. (* A leading constructor normally belongs to the TARGET, because its
  895. type (e.g. CALC's one-byte `set of Attributes') is narrower than
  896. the default 32-byte literal. But `[A..D] - X' is an expression:
  897. parse it through ParseExpr instead of storing it directly. The
  898. source cursor is reversible, so try the constructor first. *)
  899. savePos := srcPos ;
  900. IF NOT RdSetLiteral (r.setbase, r.setsize, sbuf) THEN
  901. RETURN
  902. END ;
  903. Skip () ;
  904. IF (CurCh () = '+') OR (CurCh () = '-') OR (CurCh () = '*') THEN
  905. srcPos := savePos
  906. ELSE
  907. IF r.kind = 5 THEN
  908. k := 0 ;
  909. WHILE k < r.setsize DO
  910. EmStDiImm (k, ORD (sbuf [k])) ;
  911. INC (k)
  912. END
  913. ELSIF r.kind = 1 THEN
  914. k := 0 ;
  915. WHILE k < r.setsize DO
  916. EmMovAxi (ORD (sbuf [k])) ;
  917. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + k) MOD 10000H, 1) ;
  918. INC (k)
  919. END
  920. ELSE
  921. Err (ENoLib)
  922. END ;
  923. RETURN
  924. END
  925. END ;
  926. (* `set := set' or a set expression. The destination is a variable
  927. or a DI-addressed field; save the DI address BEFORE the right-hand
  928. expression can reuse DI, then copy the runtime's bytes. *)
  929. IF r.kind = 5 THEN
  930. EmPushDi ()
  931. END ;
  932. ParseExpr (src) ;
  933. IF NOT OK () THEN
  934. RETURN
  935. END ;
  936. IF src.cls # TSet THEN
  937. Err (ETypeErr) ;
  938. RETURN
  939. END ;
  940. IF src.kind = 9 THEN
  941. MaterializeDyn (src, r.setbase, r.setsize)
  942. END ;
  943. IF r.kind = 1 THEN
  944. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  945. ELSIF r.kind # 5 THEN
  946. Err (ENoLib) ;
  947. RETURN
  948. END ;
  949. EmPushSetAddr (src) ;
  950. n := r.setsize ;
  951. IF (src.kind = 8) AND (src.setsize < n) THEN
  952. n := src.setsize
  953. END ;
  954. EmMovAxi (n) ;
  955. EmPushAx () ;
  956. DropC (EmCall (TU_SetCopy)) ;
  957. EmAddSp (4) ;
  958. RETURN
  959. END ;
  960. IF (r.kind = 5) AND (r.cls = TString) THEN
  961. (* A string field of a DI-addressed record element. DI is the field's
  962. address; park it because the source expression may reuse DI. *)
  963. EmPushDi () ;
  964. ParseExpr (src) ;
  965. IF NOT OK () THEN
  966. RETURN
  967. END ;
  968. n := VarSize (r) - 1 ; (* usable characters *)
  969. IF src.kind = 3 THEN
  970. EmPopDi () ;
  971. IF strLen [src.strx] < n THEN
  972. n := strLen [src.strx]
  973. END ;
  974. EmStDiImm (0, n) ; (* the length byte *)
  975. k := 1 ;
  976. WHILE k <= n DO
  977. EmStDiImm (k, ORD (strPool [strOff [src.strx] + k - 1])) ;
  978. INC (k)
  979. END
  980. ELSIF src.kind = 1 THEN
  981. EmPushSymAddr (src.idx, (symtab [src.idx].off + src.boff) MOD 10000H) ;
  982. EmPopDi () ; (* destination = the field *)
  983. EmPushDi () ;
  984. EmMovCxi (n) ; (* strstore's limit *)
  985. DropC (EmCall (TU_StrStore)) ;
  986. EmAddSp (4)
  987. ELSIF src.kind = 5 THEN
  988. EmPushDi () ; (* source field address *)
  989. EmPopSi () ;
  990. EmPopDi () ; (* destination field address *)
  991. EmPushSi () ; (* source address *)
  992. EmPushDi () ;
  993. EmMovCxi (n) ;
  994. DropC (EmCall (TU_StrStore)) ;
  995. EmAddSp (4)
  996. ELSE
  997. Err (ENoLib)
  998. END ;
  999. RETURN
  1000. END ;
  1001. IF (r.kind = 1) AND (r.cls = TString) AND symtab [r.idx].isVar THEN
  1002. (* A VAR string parameter: the slot holds the caller's string address.
  1003. Copy through the runtime, exactly as a normal string variable does:
  1004. source address first, destination address second, limit in CX. This
  1005. covers literals and whole-string expressions alike; the destination
  1006. address is loaded from the parameter slot rather than formed with
  1007. LEA, which would point at the slot itself. *)
  1008. ParseExpr (src) ;
  1009. IF NOT OK () THEN
  1010. RETURN
  1011. END ;
  1012. IF src.cls # TString THEN
  1013. Err (ETypeErr) ;
  1014. RETURN
  1015. END ;
  1016. EmPushStrAddr (src) ;
  1017. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  1018. EmPushAx () ;
  1019. EmMovCxi ((VarSize (r) - 1) MOD 10000H) ;
  1020. DropC (EmCall (TU_StrStore)) ;
  1021. EmAddSp (4) ;
  1022. RETURN
  1023. END ;
  1024. IF (r.kind = 1) AND symtab [r.idx].isVar THEN
  1025. (* `varparam := expr': store through the address the slot holds. DI is
  1026. parked across the right-hand side because that expression may use DI
  1027. for a string element of its own. *)
  1028. IF r.boff # 0 THEN
  1029. Err (ENoLib) ;
  1030. RETURN
  1031. END ;
  1032. StoreVarIndPrepSym (r.idx) ;
  1033. ParseExpr (src) ;
  1034. LoadAtom (src) ;
  1035. EmPopDi () ;
  1036. EmStoreInd (VarSize (r)) ;
  1037. RETURN
  1038. END ;
  1039. IF (r.cls = TString) AND (VarSize (r) > 1) THEN
  1040. (* s := <string literal>. TPSRC8 asn2 sends a string through the scalar
  1041. store path, and estcpl's string arm emits `MOV CL,max_len' (max_len =
  1042. varsize-1) then `CALL strstore', which copies the source - length byte
  1043. and characters - into the destination and TRUNCATES a source longer
  1044. than max_len by rewriting the length byte (TPSRC2 strstore's
  1045. `CMP.B CL,AL / JBE stslenok / MOV.B [SI],AL'). Here the literal is
  1046. known at compile time, so its bytes become immediate stores instead of
  1047. a stack push and a block move. The variable that results is the same,
  1048. truncation included, and the tail past the new length is left as it
  1049. was, exactly as strstore leaves it. *)
  1050. ParseExpr (src) ;
  1051. IF (src.cls = TChar) OR ((src.cls = TScalar) AND src.chr) THEN
  1052. (* `s := ch': a one-character string. This is CALC's
  1053. `EXY := FactorCh' before Val. *)
  1054. IF src.kind = 0 THEN
  1055. EmMovAxi (1) ;
  1056. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, 1) ;
  1057. EmMovAxi (W16 (src.imm)) ;
  1058. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + 1) MOD 10000H, 1)
  1059. ELSE
  1060. LoadAtom (src) ;
  1061. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + 1) MOD 10000H, 1) ;
  1062. EmMovAxi (1) ;
  1063. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, 1)
  1064. END ;
  1065. RETURN
  1066. END ;
  1067. IF (src.cls # TString) THEN
  1068. Err (ENoLib) ;
  1069. RETURN
  1070. END ;
  1071. IF src.kind # 3 THEN
  1072. (* s := <string variable>. TPSRC8 asnvar pushes the destination
  1073. address, then the expression (strload) pushes the source, and
  1074. estcpl emits MOV CL,max_len then CALL strstore, which copies
  1075. length byte + chars, TRUNCATING to max_len and leaving the
  1076. destination tail past the new length untouched. Here both
  1077. addresses are pushed - source first, so it sits at [BP+6] and the
  1078. destination at [BP+4] - the caller pops both, and the limit is a
  1079. full MOV CX,imm16 because this runtime reads it in CX outright. *)
  1080. IF src.kind = 1 THEN
  1081. EmPushSymAddr (src.idx, (symtab [src.idx].off + src.boff) MOD 10000H)
  1082. ELSIF src.kind = 5 THEN
  1083. (* A string element reached through DI, including a pointer
  1084. dereference: Buffer^[i]. Its address is already in DI. *)
  1085. EmPushDi ()
  1086. ELSE
  1087. Err (ENoLib) ; (* a computed string is not a value *)
  1088. RETURN
  1089. END ;
  1090. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  1091. EmMovCxi ((VarSize (r) - 1) MOD 10000H) ;
  1092. DropC (EmCall (TU_StrStore)) ;
  1093. EmAddSp (4) ;
  1094. RETURN
  1095. END ;
  1096. n := strLen [src.strx] ;
  1097. IF n > VarSize (r) - 1 THEN
  1098. n := VarSize (r) - 1
  1099. END ;
  1100. EmMovAxi (n) ;
  1101. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, 1) ;
  1102. k := 0 ;
  1103. WHILE k < n DO
  1104. EmMovAxi (ORD (strPool [strOff [src.strx] + k])) ;
  1105. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff + 1 + k) MOD 10000H, 1) ;
  1106. INC (k)
  1107. END ;
  1108. RETURN
  1109. END ;
  1110. IF (r.kind = 5) OR (VarSize (r) > 2) THEN
  1111. IF r.kind = 5 THEN
  1112. (* An element addressed by DI: a string character (one byte) or an
  1113. array element (one or two). A wider element - a record - is
  1114. refused rather than stored as its first two bytes.
  1115. The destination address is in DI, and the right-hand side is
  1116. parsed next, so DI has to be saved first - the RHS may hold a
  1117. subscript of its own, which reuses DI, or call a routine that
  1118. clobbers it. This is TPSRC8 asnscal2/asnscal3 (758-760, 786-789),
  1119. which set flgpshdi around the expression for the same reason. *)
  1120. IF VarSize (r) > 2 THEN
  1121. Err (ENoLib) ;
  1122. RETURN
  1123. END ;
  1124. EmPushDi () ;
  1125. ParseExpr (src) ;
  1126. LoadAtom (src) ;
  1127. IF BoundsOf (r, blo, bhi) THEN
  1128. EmitBoundsCheck (blo, bhi, 17)
  1129. END ;
  1130. EmPopDi () ; (* DI := the destination again *)
  1131. EmStoreInd (VarSize (r)) ; (* [DI] := AL or AX *)
  1132. RETURN
  1133. END ;
  1134. (* A value wider than a word with no address to store through: an
  1135. assignment to a whole string/record/array is not a scalar store. *)
  1136. Err (ENoLib) ;
  1137. RETURN
  1138. END ;
  1139. ParseExpr (src) ;
  1140. LoadAtom (src) ;
  1141. IF BoundsOf (r, blo, bhi) THEN
  1142. EmitBoundsCheck (blo, bhi, 17)
  1143. END ;
  1144. StoreSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, VarSize (r))
  1145. END Assignment ;
  1146. PROCEDURE Compound () ;
  1147. (* BEGIN statement ';' ... END; END is consumed here *)
  1148. VAR tok : CARDINAL ;
  1149. BEGIN
  1150. LOOP
  1151. PeekKw (tok) ;
  1152. IF tok = TkEnd THEN
  1153. DropB (MatchKey (tok)) ;
  1154. RETURN
  1155. END ;
  1156. Statmnt () ;
  1157. IF NOT OK () THEN
  1158. RETURN
  1159. END ;
  1160. IF NOT MatchDelim (';') THEN
  1161. PeekKw (tok) ;
  1162. IF tok = TkEnd THEN
  1163. DropB (MatchKey (tok)) ;
  1164. RETURN
  1165. END ;
  1166. Err (ENoSemi) ;
  1167. RETURN
  1168. END
  1169. END
  1170. END Compound ;
  1171. PROCEDURE EmCrtSeq (which : CARDINAL) ;
  1172. (* The CRT byte sequences, in one parameterised helper. Every one is a fixed
  1173. BIOS call:
  1174. CrtClr - INT 10h AH=06 blank the whole window, then AH=02 home the cursor
  1175. CrtDl - DEC AX ; MOV DL,AL (a 1-based column -> 0-based)
  1176. CrtDh - DEC AX ; MOV DH,AL (a 1-based row)
  1177. CrtCaret - INT 10h AH=02, page 0, cursor at DH,DL
  1178. It takes a parameter on purpose: audit_helpers audits helpers with an EMPTY
  1179. parameter list byte-for-byte, and these multi-instruction sequences have no
  1180. single name grammar. Listed as COMP in NON_ONE_LINE, like SetBlob. *)
  1181. BEGIN
  1182. IF which = CrtClr THEN
  1183. Ebyte (0B8H) ; Ebyte (00H) ; Ebyte (06H) ; (* MOV AX,0600h *)
  1184. Ebyte (8AH) ; Ebyte (3EH) ; Eword ((crtAttrOff + LoadBias) MOD 10000H) ; (* MOV BH,[crtAttr] *)
  1185. Ebyte (0B9H) ; Ebyte (00H) ; Ebyte (00H) ; (* MOV CX,0000h *)
  1186. Ebyte (0BAH) ; Ebyte (4FH) ; Ebyte (18H) ; (* MOV DX,184Fh *)
  1187. Ebyte (0CDH) ; Ebyte (10H) ; (* INT 10h *)
  1188. Ebyte (0B8H) ; Ebyte (00H) ; Ebyte (02H) ; (* MOV AX,0200h *)
  1189. Ebyte (0B7H) ; Ebyte (00H) ; (* MOV BH,00h *)
  1190. Ebyte (0BAH) ; Ebyte (00H) ; Ebyte (00H) ; (* MOV DX,0000h *)
  1191. Ebyte (0CDH) ; Ebyte (10H) (* INT 10h *)
  1192. ELSIF which = CrtDl THEN
  1193. Ebyte (48H) ; (* DEC AX *)
  1194. Ebyte (88H) ; Ebyte (0C2H) (* MOV DL,AL *)
  1195. ELSIF which = CrtDh THEN
  1196. Ebyte (48H) ; (* DEC AX *)
  1197. Ebyte (88H) ; Ebyte (0C6H) (* MOV DH,AL *)
  1198. ELSIF which = CrtEol THEN
  1199. Ebyte (0B4H) ; Ebyte (03H) ; (* MOV AH,03h get cursor *)
  1200. Ebyte (0B7H) ; Ebyte (00H) ; (* MOV BH,00h *)
  1201. Ebyte (0CDH) ; Ebyte (10H) ; (* INT 10h -> DH,DL *)
  1202. Ebyte (89H) ; Ebyte (0D1H) ; (* MOV CX,DX upper-left *)
  1203. Ebyte (0B2H) ; Ebyte (4FH) ; (* MOV DL,4Fh col 79 *)
  1204. Ebyte (0B8H) ; Ebyte (00H) ; Ebyte (06H) ; (* MOV AX,0600h blank *)
  1205. Ebyte (8AH) ; Ebyte (3EH) ; Eword ((crtAttrOff + LoadBias) MOD 10000H) ; (* MOV BH,[crtAttr] *)
  1206. Ebyte (0CDH) ; Ebyte (10H) (* INT 10h *)
  1207. ELSIF which = CrtLow THEN
  1208. (* [crtAttr] := 7: the dim attribute ClrScr/ClrEol use from now on. The
  1209. runtime's console writer is INT 21h teletype, which has no attribute
  1210. channel, but the clear operations do - so this is where the setting is
  1211. observable, not a no-op. *)
  1212. Ebyte (0C6H) ; Ebyte (06H) ; Eword ((crtAttrOff + LoadBias) MOD 10000H) ; Ebyte (07H)
  1213. ELSIF which = CrtNorm THEN
  1214. Ebyte (0C6H) ; Ebyte (06H) ; Eword ((crtAttrOff + LoadBias) MOD 10000H) ; Ebyte (0FH)
  1215. ELSIF which = CrtKey THEN
  1216. (* KeyPressed: DOS AH=0Bh asks whether a character is waiting. AL is
  1217. 0FFh when one is, 0 when not; the boolean representation here is 0/1.
  1218. AND AX,1, not AND AL,1: the interrupt leaves AH = 0Bh, so the
  1219. comparison that follows (`CMP AX,0') would read 0B00h and call the
  1220. false case TRUE. bootcom happened to return AH=0 (it IRETs the
  1221. handler's whole AX) and hid this; hlt. No argument, no runtime
  1222. entry. *)
  1223. Ebyte (0B4H) ; Ebyte (0BH) ; (* MOV AH,0Bh *)
  1224. Ebyte (0CDH) ; Ebyte (21H) ; (* INT 21h *)
  1225. Ebyte (25H) ; Ebyte (01H) ; Ebyte (00H) (* AND AX,1 *)
  1226. ELSIF which = CrtDelay THEN
  1227. (* Delay(AX) milliseconds: TP3's xdelay, a calibrated busy loop. The
  1228. count 110 is TP3's default `delaycnt' for 1 ms (TPSRC1). AX = 0 does
  1229. nothing. *)
  1230. (* OR AX,AX, not CMP AX,0: check_8086 treats every `CMP AX,0000' in the
  1231. program region as one of the IF/WHILE/REPEAT tests and demands
  1232. Jcc+3;EJMP after it. This is neither. *)
  1233. Ebyte (0BH) ; Ebyte (0C0H) ; (* OR AX,AX *)
  1234. Ebyte (74H) ; Ebyte (08H) ; (* JZ past the loop *)
  1235. Ebyte (0B9H) ; Ebyte (6EH) ; Ebyte (00H) ; (* MOV CX,110 (1 ms) *)
  1236. Ebyte (0E2H) ; Ebyte (0FEH) ; (* LOOP -> itself *)
  1237. Ebyte (48H) ; (* DEC AX *)
  1238. Ebyte (75H) ; Ebyte (0F8H) (* JNZ -> MOV CX *)
  1239. ELSE
  1240. Ebyte (0B4H) ; Ebyte (02H) ; (* MOV AH,02h *)
  1241. Ebyte (0B7H) ; Ebyte (00H) ; (* MOV BH,00h *)
  1242. Ebyte (0CDH) ; Ebyte (10H) (* INT 10h *)
  1243. END
  1244. END EmCrtSeq ;
  1245. PROCEDURE LabelColonAhead () : BOOLEAN ;
  1246. (* `Label:' is a statement label, but `Name;' is a procedure call. Look
  1247. past an identifier and blanks without consuming input. *)
  1248. VAR k : CARDINAL ;
  1249. ch : CHAR ;
  1250. BEGIN
  1251. k := 0 ;
  1252. ch := PeekAhead (k) ;
  1253. IF NOT Alpha (ch) THEN
  1254. RETURN FALSE
  1255. END ;
  1256. WHILE AlphaNum (ch) DO
  1257. INC (k) ;
  1258. ch := PeekAhead (k)
  1259. END ;
  1260. WHILE ch = ' ' DO
  1261. INC (k) ;
  1262. ch := PeekAhead (k)
  1263. END ;
  1264. IF ch # ':' THEN
  1265. RETURN FALSE
  1266. END ;
  1267. (* `Name := ...' is an assignment, not a label definition. *)
  1268. INC (k) ;
  1269. ch := PeekAhead (k) ;
  1270. WHILE ch = ' ' DO
  1271. INC (k) ;
  1272. ch := PeekAhead (k)
  1273. END ;
  1274. RETURN ch # '='
  1275. END LabelColonAhead ;
  1276. PROCEDURE Statmnt () ;
  1277. VAR tok : CARDINAL ;
  1278. idx, i2 : CARDINAL ;
  1279. t, src : ERes ;
  1280. L1, zj, zj2, exj : CARDINAL ;
  1281. lo, hi, v : LONGINT ;
  1282. clso : CARDINAL ;
  1283. i : CARDINAL ;
  1284. k, nlbl, nlblJmp, bodyPc, skipJmp : CARDINAL ;
  1285. nm : ARRAY [0..MaxName] OF CHAR ;
  1286. strf : BOOLEAN ;
  1287. dow : BOOLEAN ;
  1288. savedWith : CARDINAL ;
  1289. BEGIN
  1290. Skip () ;
  1291. IF Digit (CurCh ()) OR LabelColonAhead () THEN
  1292. ParseLabelStmt () ; (* consumed the label and ':' *)
  1293. Statmnt () ; (* the statement follows the label directly *)
  1294. RETURN
  1295. END ;
  1296. IF NOT Alpha (CurCh ()) THEN
  1297. ExpectDelim (';', ENoSemi) ;
  1298. RETURN
  1299. END ;
  1300. DropB (MatchKey (tok)) ;
  1301. IF tok = TkBegin THEN
  1302. Compound ()
  1303. ELSIF tok = TkIf THEN
  1304. ParseExpr (t) ;
  1305. LoadAtom (t) ;
  1306. EmCmpAxi (0) ;
  1307. zj := EmJcc (84H, 0) ; (* JZ -> else/end *)
  1308. IF NOT (MatchKey (tok) AND (tok = TkThen)) THEN
  1309. Err (ENoSemi)
  1310. END ;
  1311. Statmnt () ;
  1312. (* Peek the ELSE, do not match it. `MatchKey (tok) AND (tok = TkElse)`
  1313. consumes whatever the next keyword is even when the AND fails, so an
  1314. `if` that was the LAST statement of a BEGIN..END block ate the block's
  1315. own END: Compound then found neither ';' nor END and raised ENoSemi.
  1316. Any `if` as the last statement of a compound was unparseable - not
  1317. in a loop, not anywhere - and no fixture had one, so nothing noticed.
  1318. The visible symptom was a parse error at the statement AFTER the
  1319. block, which points at entirely the wrong piece of source. *)
  1320. PeekKw (tok) ;
  1321. IF tok = TkElse THEN
  1322. DropB (MatchKey (tok)) ;
  1323. exj := EmJmpNear (0) ;
  1324. SetPatTgt (zj, pc) ;
  1325. Statmnt () ;
  1326. SetPatTgt (exj, pc)
  1327. ELSE
  1328. SetPatTgt (zj, pc)
  1329. END
  1330. ELSIF tok = TkWhile THEN
  1331. L1 := pc ;
  1332. ParseExpr (t) ;
  1333. LoadAtom (t) ;
  1334. EmCmpAxi (0) ;
  1335. zj := EmJcc (84H, 0) ; (* JZ -> end *)
  1336. IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
  1337. Err (ENoSemi)
  1338. END ;
  1339. brkSave [brkN] := exitCnt ;
  1340. loopTy [brkN] := 1 ;
  1341. INC (brkN) ;
  1342. Statmnt () ;
  1343. DEC (brkN) ;
  1344. i := brkSave [brkN] ;
  1345. WHILE i < exitCnt DO
  1346. SetPatTgt (exitPatch [i], pc) ;
  1347. INC (i)
  1348. END ;
  1349. exitCnt := brkSave [brkN] ;
  1350. DropC (EmJmpNear (L1)) ;
  1351. SetPatTgt (zj, pc)
  1352. ELSIF tok = TkRepeat THEN
  1353. L1 := pc ;
  1354. brkSave [brkN] := exitCnt ;
  1355. loopTy [brkN] := 1 ;
  1356. INC (brkN) ;
  1357. (* The body is a STATEMENT SEQUENCE, not one statement: `repeat a; b
  1358. until c'. This used to call Statmnt once and then demand UNTIL, so
  1359. any body with a second statement was ENoSemi at the first ';'. t12's
  1360. two bodies are single statements and never saw it; CALC's `Flash' does
  1361. (GotoXY; Blink := not Blink; if ...; Write; Delay). Same shape as
  1362. Compound, stopping at UNTIL instead of END. *)
  1363. LOOP
  1364. PeekKw (tok) ;
  1365. IF tok = TkUntil THEN
  1366. DropB (MatchKey (tok)) ;
  1367. EXIT
  1368. END ;
  1369. Statmnt () ;
  1370. IF NOT OK () THEN
  1371. RETURN
  1372. END ;
  1373. IF NOT MatchDelim (';') THEN
  1374. PeekKw (tok) ;
  1375. IF tok = TkUntil THEN
  1376. DropB (MatchKey (tok)) ;
  1377. EXIT
  1378. END ;
  1379. Err (ENoSemi) ;
  1380. RETURN
  1381. END
  1382. END ;
  1383. ParseExpr (t) ;
  1384. LoadAtom (t) ;
  1385. EmCmpAxi (0) ;
  1386. (* UNTIL exits when the condition is TRUE, so the body runs again when
  1387. it is FALSE. The condition is a 0/1 in AX and EmCmpAxi (0) has just
  1388. compared it with 0, so ZF=1 means "condition false" -- which is
  1389. exactly the case that loops, hence JZ.
  1390. This was JNZ, which compiled repeat-until as while-until: the body
  1391. ran once, the condition was tested, and it stopped. t12_repeat is
  1392. `i:=0; repeat i:=i+1 until i>5' and it printed 1.
  1393. No patch slot: L1 is backwards and already known, so EmJcc returns
  1394. 0 and there is nothing to SetPatTgt. *)
  1395. DropC (EmJcc (84H, L1)) ; (* JZ -> body again *)
  1396. DEC (brkN) ;
  1397. i := brkSave [brkN] ;
  1398. WHILE i < exitCnt DO
  1399. SetPatTgt (exitPatch [i], pc) ;
  1400. INC (i)
  1401. END ;
  1402. exitCnt := brkSave [brkN]
  1403. ELSIF tok = TkFor THEN
  1404. (* control variable *)
  1405. Skip () ; (* after the FOR keyword: skip blanks *)
  1406. IF NOT Alpha (CurCh ()) THEN
  1407. Err (EUnknown) ;
  1408. RETURN
  1409. END ;
  1410. GetWord () ;
  1411. IF NOT Search (wrd, idx) THEN
  1412. Err (EUnknown) ;
  1413. RETURN
  1414. END ;
  1415. IF symtab [idx].size > 2 THEN
  1416. Err (ENoLib) ;
  1417. RETURN
  1418. END ;
  1419. IF NOT MatchAssign () THEN
  1420. Err (ENoSemi)
  1421. END ;
  1422. ParseExpr (src) ;
  1423. LoadAtom (src) ;
  1424. StoreSymVar (idx, symtab [idx].off, symtab [idx].size) ;
  1425. IF NOT MatchKey (tok) THEN
  1426. Err (ESimpType) ;
  1427. RETURN
  1428. END ;
  1429. IF (tok = TkTo) OR (tok = TkDownto) THEN
  1430. dow := (tok = TkDownto)
  1431. ELSE
  1432. Err (ESimpType) ;
  1433. RETURN
  1434. END ;
  1435. ParseExpr (t) ;
  1436. LoadAtom (t) ;
  1437. EmPushAx () ; (* loop bound on the stack *)
  1438. IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
  1439. Err (ENoSemi)
  1440. END ;
  1441. brkSave [brkN] := exitCnt ;
  1442. loopTy [brkN] := 2 ;
  1443. INC (brkN) ;
  1444. L1 := pc ; (* Ltest: the test comes FIRST *)
  1445. (* The test is emitted before the body, not after it. It used to be
  1446. emitted after, which is a post-test loop and runs the body one time
  1447. too many: with `for i := 1 to 5`, the sequence of i at the test is
  1448. 1,2,3,4,5,6 - the test at i=5 is `5 > 5`, which is false, so the
  1449. body ran a sixth time with i=6. `for i := 1 to 5 do s := s + i`
  1450. printed 21. The size was right and the shape was right; only the
  1451. order was wrong, and no byte check can see an order.
  1452. The bound stays on the stack for the whole loop, so EmMovCxSp has to
  1453. re-read it every iteration - which is also what makes the bound a
  1454. *variable* rather than a constant. [SP] cannot be encoded on the
  1455. 8086, so EmMovCxSp is POP CX ; PUSH CX, an observational no-op that
  1456. leaves the bound in place. *)
  1457. EmMovCxSp () ;
  1458. LoadSymVar (idx, symtab [idx].off, symtab [idx].size) ;
  1459. IF symtab [idx].size = 1 THEN
  1460. (* A CHAR/BYTE control variable loads into AL only. Without zeroing
  1461. AH, the previous body's leftover AH (wrchar leaves 02h there) makes
  1462. the 16-bit CMP read 0242h and the loop stops after 'A'. *)
  1463. EmMovAh0 ()
  1464. END ;
  1465. EmCmpAxCx () ;
  1466. IF dow THEN
  1467. zj := EmJcc (8CH, 0) (* JL -> done *)
  1468. ELSE
  1469. zj := EmJcc (8FH, 0) (* JG -> done *)
  1470. END ;
  1471. Statmnt () ;
  1472. DEC (brkN) ;
  1473. (* A FOR's exits are NOT patched here, even though `done` is not known
  1474. yet. For a WHILE or REPEAT, "just after the body" is a correct
  1475. target: the jump back to the test re-evaluates the condition and
  1476. leaves. For a FOR there is a STEP between the body and `done`, so
  1477. an EXIT that jumped here would increment the control variable and
  1478. jump back to the test - and if the incremented value still satisfied
  1479. the bound, it would run the body AGAIN. `exit` did not exit.
  1480. The fix needs no new bookkeeping: `brkSave [brkN] .. exitCnt` still
  1481. names exactly this loop's exits, because Statmnt may have added more
  1482. and nothing has reset exitCnt. So they are patched at `done`, below.
  1483. A WHILE nested inside the FOR saves and restores its own range and
  1484. leaves this one intact. *)
  1485. (* step *)
  1486. LoadSymVar (idx, symtab [idx].off, symtab [idx].size) ;
  1487. IF symtab [idx].size = 1 THEN
  1488. EmMovAh0 ()
  1489. END ;
  1490. IF dow THEN
  1491. EmDecAx ()
  1492. ELSE
  1493. EmIncAx ()
  1494. END ;
  1495. StoreSymVar (idx, symtab [idx].off, symtab [idx].size) ;
  1496. DropC (EmJmpNear (L1)) ;
  1497. SetPatTgt (zj, pc) ; (* done: *)
  1498. (* A WHILE loop here, not a FOR over the exit range. The range is
  1499. usually EMPTY - most loops have no `exit` - and `exitCnt` is a
  1500. CARDINAL, so `TO exitCnt - 1` with exitCnt = 0 is `TO 65535`: the
  1501. loop does not terminate, it wraps, and it walks exitPatch [0..65535]
  1502. off the end of a 64-element array. `for i := 1 to 10 do i := i` has
  1503. no exit, so this is the ORDINARY case, and it faulted with
  1504. "invalid address referenced" on every FOR loop without an exit. *)
  1505. i := brkSave [brkN] ;
  1506. WHILE i < exitCnt DO
  1507. SetPatTgt (exitPatch [i], pc) ;
  1508. INC (i)
  1509. END ;
  1510. exitCnt := brkSave [brkN] ;
  1511. EmAddSp (2) (* drop the loop bound *)
  1512. ELSIF tok = TkCase THEN
  1513. ParseExpr (t) ;
  1514. LoadAtom (t) ;
  1515. EmPushAx () ; (* selector on the stack *)
  1516. IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
  1517. Err (ENoSemi)
  1518. END ;
  1519. caseN := 0 ;
  1520. LOOP
  1521. Skip () ;
  1522. IF MatchDelim (';') THEN
  1523. Skip ()
  1524. END ;
  1525. PeekKw (tok) ;
  1526. IF (tok = TkEnd) OR (tok = TkElse) THEN
  1527. EXIT
  1528. END ;
  1529. (* Case labels: `v' | `lo..hi', comma-separated, all selecting the
  1530. same statement (CALC's `'G', 'I', 'O', 'Q':'). Read the whole list
  1531. first; the emission then knows whether this is the one-label path
  1532. or a list. *)
  1533. nlbl := 0 ;
  1534. LOOP
  1535. IF Alpha (CurCh ()) THEN
  1536. GetWord () ;
  1537. IF Search (wrd, i2) AND (symtab [i2].tag = KConst) THEN
  1538. lo := symtab [i2].lval
  1539. ELSE
  1540. Err (EUnknown) ;
  1541. RETURN
  1542. END
  1543. ELSE
  1544. RdConst (lo, clso, strf)
  1545. END ;
  1546. IF MatchRange () THEN
  1547. RdConst (hi, clso, strf)
  1548. ELSE
  1549. hi := lo
  1550. END ;
  1551. IF nlbl >= 32 THEN
  1552. Err (ECompOvf) ;
  1553. RETURN
  1554. END ;
  1555. lblLo [nlbl] := lo ;
  1556. lblHi [nlbl] := hi ;
  1557. INC (nlbl) ;
  1558. IF MatchDelim (',') THEN
  1559. Skip ()
  1560. ELSE
  1561. EXIT
  1562. END
  1563. END ;
  1564. Skip () ;
  1565. ExpectDelim (':', ENoSemi) ;
  1566. IF NOT OK () THEN
  1567. RETURN
  1568. END ;
  1569. EmMovAxSp () ;
  1570. IF nlbl = 1 THEN
  1571. (* One label: the original bytes, except that a RANGE is now a
  1572. real range. It used to compare AX with lo and then with hi,
  1573. both demanding equality, so `1..5' matched NOTHING unless
  1574. lo = hi - a silently wrong answer, not an error. *)
  1575. lo := lblLo [0] ;
  1576. hi := lblHi [0] ;
  1577. IF hi = lo THEN
  1578. EmCmpAxi (W16 (lo)) ;
  1579. zj := EmJcc (85H, 0) ; (* JNZ -> next arm *)
  1580. zj2 := 0
  1581. ELSE
  1582. EmCmpAxi (W16 (lo)) ;
  1583. zj := EmJcc (8CH, 0) ; (* JL -> next arm *)
  1584. EmCmpAxi (W16 (hi)) ;
  1585. zj2 := EmJcc (8FH, 0) (* JG -> next arm *)
  1586. END ;
  1587. Statmnt () ;
  1588. IF caseN >= 64 THEN
  1589. Err (ECompOvf) ;
  1590. RETURN
  1591. END ;
  1592. caseJmp [caseN] := EmJmpNear (0) ;
  1593. INC (caseN) ;
  1594. SetPatTgt (zj, pc) ;
  1595. IF zj2 # 0 THEN
  1596. SetPatTgt (zj2, pc)
  1597. END
  1598. ELSE
  1599. (* A list. A single label branches INTO the statement; a range
  1600. first sends AX below lo to the next label, then `<= hi' to the
  1601. body. The body jumps are patched once the body's address is
  1602. known, and the no-match EJMP skips the body to the next arm. *)
  1603. nlblJmp := 0 ;
  1604. FOR k := 0 TO nlbl - 1 DO
  1605. IF lblHi [k] = lblLo [k] THEN
  1606. EmCmpAxi (W16 (lblLo [k])) ;
  1607. lblJmp [nlblJmp] := EmJcc (84H, 0) ; (* JZ -> body *)
  1608. INC (nlblJmp)
  1609. ELSE
  1610. (* `lo <= AX <= hi', built from the two conditions this
  1611. compiler declares for a branch (L and G): below lo skips
  1612. the label, above hi skips it, otherwise an unconditional
  1613. jump enters the body. JLE would be shorter and is legal
  1614. 8086, but check_8086's declared condition set is exactly
  1615. Z/NZ/L/G (the four the parser's comparisons use), and a
  1616. new condition in CASE would rightly be reported there. *)
  1617. EmCmpAxi (W16 (lblLo [k])) ;
  1618. zj := EmJcc (8CH, 0) ; (* JL -> next label *)
  1619. EmCmpAxi (W16 (lblHi [k])) ;
  1620. zj2 := EmJcc (8FH, 0) ; (* JG -> next label *)
  1621. lblJmp [nlblJmp] := EmJmpNear (0) ; (* in range -> body *)
  1622. INC (nlblJmp) ;
  1623. SetPatTgt (zj, pc) ;
  1624. SetPatTgt (zj2, pc)
  1625. END
  1626. END ;
  1627. skipJmp := EmJmpNear (0) ;
  1628. bodyPc := pc ;
  1629. FOR k := 0 TO nlblJmp - 1 DO
  1630. SetPatTgt (lblJmp [k], bodyPc)
  1631. END ;
  1632. Statmnt () ;
  1633. IF caseN >= 64 THEN
  1634. Err (ECompOvf) ;
  1635. RETURN
  1636. END ;
  1637. caseJmp [caseN] := EmJmpNear (0) ;
  1638. INC (caseN) ;
  1639. SetPatTgt (skipJmp, pc)
  1640. END
  1641. END ;
  1642. IF tok = TkElse THEN
  1643. DropB (MatchKey (tok)) ;
  1644. Statmnt () ;
  1645. (* A semicolon between the ELSE statement and the CASE's END is an
  1646. empty statement, and TP3/CALC writes it after a compound body:
  1647. `else begin ... end; end`. The non-ELSE path already tolerates
  1648. the same separator at the top of the arm loop. *)
  1649. IF MatchDelim (';') THEN
  1650. Skip ()
  1651. END ;
  1652. IF NOT (MatchKey (tok) AND (tok = TkEnd)) THEN
  1653. Err (ENoSemi)
  1654. END
  1655. ELSE
  1656. DropB (MatchKey (tok))
  1657. END ;
  1658. EmAddSp (2) ;
  1659. FOR i := 0 TO caseN - 1 DO
  1660. SetPatTgt (caseJmp [i], pc)
  1661. END
  1662. ELSIF tok = TkGoto THEN
  1663. v := 0 ;
  1664. Skip () ; (* after the GOTO keyword: skip blanks *)
  1665. IF Alpha (CurCh ()) THEN
  1666. GetWord () ;
  1667. SaveWord (nm)
  1668. ELSIF Digit (CurCh ()) THEN
  1669. RdIntConst (v) ;
  1670. NumToName (W16 (v), nm)
  1671. ELSE
  1672. Err (EUnknown) ;
  1673. RETURN
  1674. END ;
  1675. IF Search (nm, idx) THEN
  1676. IF symtab [idx].tag = KLabel THEN
  1677. IF symtab [idx].defnd THEN
  1678. DropC (EmJmpNear (symtab [idx].goPos))
  1679. ELSE
  1680. zj := EmJmpNear (0) ;
  1681. AddPend (0, idx, zj)
  1682. END
  1683. ELSE
  1684. Err (EUnknown)
  1685. END
  1686. ELSE
  1687. idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
  1688. zj := EmJmpNear (0) ;
  1689. AddPend (0, idx, zj)
  1690. END
  1691. ELSIF tok = TkExit THEN
  1692. IF brkN = 0 THEN
  1693. Err (EUnknown)
  1694. ELSE
  1695. (* No EmAddSp (2) here, even inside a FOR. The FOR's `done` label
  1696. drops the bound, so an EXIT that jumped to `done` would drop it a
  1697. second time - 4 bytes off a stack that only had 2 to give, which
  1698. silently corrupts the caller's frame. It used to do exactly
  1699. that, and it was doubly wrong: the exits were patched to the STEP
  1700. rather than to `done`, so the EXIT also incremented the control
  1701. variable and jumped back into the test. *)
  1702. zj := EmJmpNear (0) ;
  1703. IF exitCnt < 64 THEN
  1704. exitPatch [exitCnt] := zj ;
  1705. INC (exitCnt)
  1706. END
  1707. END
  1708. ELSIF tok = TkWith THEN
  1709. (* WITH base (, base)* DO statement. The record's field range comes from
  1710. the base symbol: DefVar copies the ELEMENT record's recFldFirst/
  1711. recFldLast onto an array variable, which is what makes
  1712. `Sheet[I,J].Field' and `with Sheet[I,J] do' work. Each base is
  1713. pushed on the scope stack and popped after the statement. *)
  1714. savedWith := withN ;
  1715. LOOP
  1716. ParseExpr (t) ;
  1717. IF NOT OK () THEN
  1718. RETURN
  1719. END ;
  1720. IF t.cls # TRecord THEN
  1721. Err (ETypeErr) ;
  1722. RETURN
  1723. END ;
  1724. IF withN >= 4 THEN
  1725. Err (ECompOvf) ;
  1726. RETURN
  1727. END ;
  1728. IF t.fldFirst >= t.fldLast THEN
  1729. Err (ETypeErr) ;
  1730. RETURN
  1731. END ;
  1732. IF t.kind = 5 THEN
  1733. (* the base address is in DI; park it. Allocating two data bytes
  1734. here is safe: the statement is parsed once, and the store
  1735. executes wherever the WITH does. *)
  1736. withSlot [withN] := dc ;
  1737. dc := dc + 2 ;
  1738. EmStDiGlob (withSlot [withN]) ;
  1739. withIsDI [withN] := TRUE ;
  1740. withBoff [withN] := 0
  1741. ELSIF t.kind = 1 THEN
  1742. withIsDI [withN] := FALSE ;
  1743. withBoff [withN] := t.boff
  1744. ELSE
  1745. Err (ENoLib) ;
  1746. RETURN
  1747. END ;
  1748. withIdx [withN] := t.idx ;
  1749. withFF [withN] := t.fldFirst ;
  1750. withFL [withN] := t.fldLast ;
  1751. INC (withN) ;
  1752. IF MatchDelim (',') THEN
  1753. (* another base; the last one is innermost *)
  1754. ELSE
  1755. EXIT
  1756. END
  1757. END ;
  1758. IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
  1759. Err (ENoSemi) ;
  1760. RETURN
  1761. END ;
  1762. Statmnt () ;
  1763. withN := savedWith
  1764. ELSE
  1765. (* identifier statement: assignment or call *)
  1766. IF WithLookup (wrd, t) THEN
  1767. (* A field inside WITH may be followed by `.subfield' or `[index]'. *)
  1768. ParseSub (t) ;
  1769. IF MatchAssign () THEN
  1770. Assignment (t) ;
  1771. RETURN
  1772. END ;
  1773. Err (ENoSemi) ;
  1774. RETURN
  1775. END ;
  1776. IF NOT Search (wrd, idx) THEN
  1777. Err (EUnknown) ;
  1778. RETURN
  1779. END ;
  1780. IF symtab [idx].isPort THEN
  1781. (* Assignment targets take this direct path instead of ParseVar, so
  1782. Port[...] must be routed through the same special parser here. *)
  1783. ParseVar (t) ;
  1784. IF NOT OK () THEN RETURN END ;
  1785. IF MatchAssign () THEN
  1786. Assignment (t)
  1787. ELSE
  1788. Err (ENoSemi)
  1789. END ;
  1790. RETURN
  1791. END ;
  1792. IF symtab [idx].tag = KBuiltin THEN
  1793. IoCall (idx) ;
  1794. RETURN
  1795. END ;
  1796. IF symtab [idx].tag = KProc THEN
  1797. IF MatchDelim ('(') THEN
  1798. ParseCallArgs (idx)
  1799. ELSE
  1800. ParseCall (idx)
  1801. END ;
  1802. RETURN
  1803. END ;
  1804. IF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KFunc) THEN
  1805. t.idx := idx ;
  1806. t.kind := 1 ;
  1807. t.boff := 0 ;
  1808. t.cls := symtab [idx].cls ;
  1809. IF symtab [idx].tag = KFunc THEN
  1810. t.idx := symtab [idx].resvar ;
  1811. t.cls := symtab [t.idx].cls
  1812. END ;
  1813. t.sz := symtab [t.idx].size ; (* ParseSub may narrow it further *)
  1814. t.fldFirst := symtab [t.idx].fldFirst ;
  1815. t.fldLast := symtab [t.idx].fldLast ;
  1816. t.arrIdx := symtab [t.idx].arrIdx ;
  1817. t.ptrElem := symtab [t.idx].elem ;
  1818. t.hasBounds := ((symtab [t.idx].cls = TScalar) OR (symtab [t.idx].cls = TEnum)
  1819. OR (symtab [t.idx].cls = TChar)) AND
  1820. (symtab [t.idx].lo # symtab [t.idx].hi) ;
  1821. t.blo := symtab [t.idx].lo ; t.bhi := symtab [t.idx].hi ;
  1822. IF t.cls = TSet THEN
  1823. (* The identifier-statement path does not go through ParseVar, so
  1824. it must carry the same set metadata: Assignment's set branch
  1825. uses setsize/setbase to build the constructor. *)
  1826. t.setoff := symtab [t.idx].off ;
  1827. t.setbase := symtab [t.idx].elem ;
  1828. t.setsize := symtab [t.idx].size
  1829. END ;
  1830. ParseSub (t) ;
  1831. IF MatchAssign () THEN
  1832. Assignment (t) ;
  1833. RETURN
  1834. END ;
  1835. Err (ENoSemi) ;
  1836. RETURN
  1837. END ;
  1838. Err (ENoSemi)
  1839. END
  1840. END Statmnt ;
  1841. (* ---------------------------------------------------------------- *)
  1842. (* types and declarations (TPSRC7) *)
  1843. (* ---------------------------------------------------------------- *)
  1844. PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ;
  1845. VAR tok : CARDINAL ;
  1846. idx : CARDINAL ;
  1847. lo, hi : LONGINT ;
  1848. s2, e2 : CARDINAL ;
  1849. subcls : CARDINAL ;
  1850. strf : BOOLEAN ;
  1851. isCharRange : BOOLEAN ;
  1852. consumed : BOOLEAN ;
  1853. running, groupFirst, i, an, recStart : CARDINAL ;
  1854. innerIdx, innerN, leafElem, leafCls, j : CARDINAL ;
  1855. elemFldFirst, elemFldLast, fieldFldFirst, fieldFldLast, fieldArrIdx : CARDINAL ;
  1856. elemType : CARDINAL ;
  1857. elemLo, elemHi : LONGINT ;
  1858. fieldPtrElem : CARDINAL ;
  1859. variantBase, armRunning, armMax, savePosVar : CARDINAL ;
  1860. moreFields : BOOLEAN ;
  1861. tagName : ARRAY [0..MaxName] OF CHAR ;
  1862. alo, ahi : ARRAY [0..3] OF LONGINT ;
  1863. ecls2 : CARDINAL ;
  1864. BEGIN
  1865. cls := TNone ; size := 0 ; elem := 0 ;
  1866. consumed := FALSE ;
  1867. newArrIdx := 0 ;
  1868. Skip () ; (* after ':' / '=' : skip blanks *)
  1869. IF CurCh () = '^' THEN
  1870. (* A pointer type: ^Type. The pointer itself is a word; its `elem'
  1871. field holds the pointee TYPE symbol, not a byte size. The pointee's
  1872. array/record metadata is copied onto the pointer variable so a
  1873. dereference can hand it to ParseSub unchanged. *)
  1874. DropCh (GetCh ()) ;
  1875. Skip () ;
  1876. IF NOT Alpha (CurCh ()) THEN
  1877. Err (ESimpType) ;
  1878. RETURN
  1879. END ;
  1880. GetWord () ;
  1881. IF Search (wrd, idx) THEN
  1882. IF symtab [idx].tag # KType THEN
  1883. Err (ESimpType) ;
  1884. RETURN
  1885. END
  1886. ELSE
  1887. (* Forward type reference: `Next : ^Person' before the record is
  1888. declared. Create a placeholder that DefType will fill. *)
  1889. idx := NewSym (wrd, KType, TNone, 0, 0, 0, 0, FALSE) ;
  1890. symtab [idx].fwd := TRUE
  1891. END ;
  1892. cls := TPtr ;
  1893. size := 2 ;
  1894. elem := idx ; (* pointee type symbol *)
  1895. newArrIdx := symtab [idx].arrIdx ;
  1896. recFldFirst := symtab [idx].fldFirst ;
  1897. recFldLast := symtab [idx].fldLast ;
  1898. enumLo := symtab [idx].lo ;
  1899. enumHi := symtab [idx].hi ;
  1900. RETURN
  1901. END ;
  1902. IF CurCh () = '(' THEN
  1903. (* An enumeration: `(Constant, Formula, Txt, ...)'. Each enumerator
  1904. becomes a constant with its ordinal position as its value, and the
  1905. TYPE is 1 byte (TP3 uses one below 256 values) with bounds 0..n-1,
  1906. which DefType records so `set of Attributes' can size its base.
  1907. Enumerators are TScalar, not TEnum: they behave as ordinals; only the
  1908. TYPE carries the enum identity and its bounds. *)
  1909. DropCh (GetCh ()) ;
  1910. lo := 0 ;
  1911. hi := -1 ;
  1912. LOOP
  1913. Skip () ;
  1914. IF NOT Alpha (CurCh ()) THEN
  1915. Err (EUnknown) ;
  1916. RETURN
  1917. END ;
  1918. GetWord () ;
  1919. DupTest (wrd) ;
  1920. DropC (NewSym (wrd, KConst, TScalar, 1, 1, 0, hi + 1, FALSE)) ;
  1921. hi := hi + 1 ;
  1922. IF MatchDelim (',') THEN
  1923. (* another enumerator *)
  1924. ELSE
  1925. EXIT
  1926. END
  1927. END ;
  1928. ExpectDelim (')', ENoSemi) ;
  1929. enumLo := lo ;
  1930. enumHi := hi ;
  1931. cls := TEnum ;
  1932. size := 1 ;
  1933. elem := 1 ;
  1934. RETURN
  1935. END ;
  1936. IF Alpha (CurCh ()) THEN
  1937. DropB (MatchKey (tok)) ;
  1938. consumed := TRUE
  1939. ELSE
  1940. tok := TkNone
  1941. END ;
  1942. IF tok = TkArray THEN
  1943. (* array [range, range, ...] of T. Several ranges are allowed; the LAST
  1944. varies fastest (row-major), so `Sheet[fx,fy]' strides the first range
  1945. by the second's count (RESUME-TP3.md §2). The shape is kept in the
  1946. array table; the symbol carries a pointer to it (SymEntry.arrIdx) so
  1947. indexing can scale by the element size and subtract each lower bound. *)
  1948. ExpectDelim ('[', ENoSemi) ;
  1949. an := 0 ;
  1950. LOOP
  1951. Skip () ;
  1952. IF Alpha (CurCh ()) THEN
  1953. (* An enum/subrange TYPE as the index: `array[Standardfunction]'.
  1954. Its bounds are on the type symbol. *)
  1955. GetWord () ;
  1956. IF NOT Search (wrd, idx) OR (symtab [idx].tag # KType)
  1957. OR (symtab [idx].cls # TEnum) THEN
  1958. Err (ESimpType) ;
  1959. RETURN
  1960. END ;
  1961. lo := symtab [idx].lo ;
  1962. hi := symtab [idx].hi
  1963. ELSE
  1964. RdConst (lo, subcls, strf) ;
  1965. IF NOT MatchRange () THEN
  1966. Err (ESimpType) ;
  1967. RETURN
  1968. END ;
  1969. RdConst (hi, subcls, strf)
  1970. END ;
  1971. IF an < 4 THEN
  1972. alo [an] := lo ;
  1973. ahi [an] := hi ;
  1974. INC (an)
  1975. END ;
  1976. IF MatchDelim (',') THEN
  1977. (* another range *)
  1978. ELSE
  1979. EXIT
  1980. END
  1981. END ;
  1982. ExpectDelim (']', ENoSemi) ;
  1983. IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
  1984. Err (ENoSemi) ;
  1985. RETURN
  1986. END ;
  1987. ParseType (cls, s2, e2) ;
  1988. ecls2 := cls ;
  1989. innerIdx := newArrIdx ;
  1990. elemFldFirst := recFldFirst ;
  1991. elemFldLast := recFldLast ;
  1992. elemLo := enumLo ; elemHi := enumHi ;
  1993. elemType := 0 ;
  1994. IF cls = TPtr THEN
  1995. elemType := e2
  1996. END ;
  1997. IF (cls = TArray) AND (innerIdx > 0) THEN
  1998. (* Flatten array-of-array: the outer symbol must index the same
  1999. number of dimensions as `array[a,b]'. A named element array
  2000. (`Airplane = array[...] of Dog_Food') carries its shape in the
  2001. inner ArrRec; keep only the leaf element size and class here. *)
  2002. leafElem := arrs [innerIdx - 1].elem ;
  2003. leafCls := arrs [innerIdx - 1].ecls ;
  2004. elemFldFirst := arrs [innerIdx - 1].fldFirst ;
  2005. elemFldLast := arrs [innerIdx - 1].fldLast ;
  2006. elemLo := arrs [innerIdx - 1].elemLo ;
  2007. elemHi := arrs [innerIdx - 1].elemHi ;
  2008. elemType := arrs [innerIdx - 1].elemType ;
  2009. innerN := arrs [innerIdx - 1].n
  2010. ELSE
  2011. leafElem := s2 ;
  2012. leafCls := ecls2 ;
  2013. innerN := 0
  2014. END ;
  2015. cls := TArray ;
  2016. elem := leafElem ;
  2017. size := s2 ;
  2018. i := 0 ;
  2019. WHILE i < an DO
  2020. size := size * W16 (VAL (LONGINT, W16 (ahi [i]))
  2021. - VAL (LONGINT, W16 (alo [i])) + 1) ;
  2022. INC (i)
  2023. END ;
  2024. (* register the flattened shape so indexing can scale by leaf element
  2025. size and subtract each lower bound; the symbol points at this slot *)
  2026. IF (an + innerN <= 4) AND (arrTop < MaxArrs) THEN
  2027. arrs [arrTop].n := an + innerN ;
  2028. arrs [arrTop].elem := leafElem ;
  2029. arrs [arrTop].ecls := leafCls ;
  2030. arrs [arrTop].fldFirst := elemFldFirst ;
  2031. arrs [arrTop].fldLast := elemFldLast ;
  2032. arrs [arrTop].elemType := elemType ;
  2033. arrs [arrTop].elemLo := elemLo ;
  2034. arrs [arrTop].elemHi := elemHi ;
  2035. i := 0 ;
  2036. WHILE i < an DO
  2037. arrs [arrTop].lo [i] := alo [i] ;
  2038. arrs [arrTop].hi [i] := ahi [i] ;
  2039. INC (i)
  2040. END ;
  2041. j := 0 ;
  2042. WHILE j < innerN DO
  2043. arrs [arrTop].lo [an + j] := arrs [innerIdx - 1].lo [j] ;
  2044. arrs [arrTop].hi [an + j] := arrs [innerIdx - 1].hi [j] ;
  2045. INC (j)
  2046. END ;
  2047. newArrIdx := arrTop + 1 ;
  2048. INC (arrTop)
  2049. ELSE
  2050. Err (ECompOvf)
  2051. END
  2052. ELSIF tok = TkString THEN
  2053. cls := TString ;
  2054. size := 256 ;
  2055. elem := 1 ;
  2056. IF MatchDelim ('[') THEN
  2057. RdConst (hi, subcls, strf) ;
  2058. ExpectDelim (']', ENoSemi) ;
  2059. size := W16 (hi) + 1
  2060. END
  2061. ELSIF tok = TkSet THEN
  2062. (* set of <ordinal>. TP3's hybrid layout (coeur-tp-ocr 4.7.2): only the
  2063. whole BYTES covering the base range are stored, so `set of Char' is 32
  2064. bytes and `set of 12..17' is 2. `elem' carries the origin - the first
  2065. element of the first byte, a multiple of 8 - which is all the
  2066. membership and constructor arithmetic needs. *)
  2067. PeekKw (tok) ;
  2068. IF tok = TkOf THEN
  2069. DropB (MatchKey (tok)) ;
  2070. RdSetBase (subcls, lo, hi) ;
  2071. IF OK () THEN
  2072. cls := TSet ;
  2073. elem := (VAL (CARDINAL, lo) DIV 8) * 8 ;
  2074. size := (VAL (CARDINAL, hi) DIV 8)
  2075. - (VAL (CARDINAL, lo) DIV 8) + 1
  2076. END
  2077. ELSE
  2078. Err (ENoSemi)
  2079. END
  2080. ELSIF tok = TkRecord THEN
  2081. (* record fieldList end, fields NON-COMPACTED and juxtaposed
  2082. (RESUME-TP3.md §2). Each name in a `a, b : T' group is a field of its
  2083. own at the running offset; the field table is where `x.f' looks, keyed
  2084. per record so field names need not be globally unique.
  2085. recFldFirst/recFldLast hand the range to DefType, or to the enclosing
  2086. record's field entry when records nest. *)
  2087. recStart := fldTop ;
  2088. recFldFirst := fldTop ;
  2089. running := 0 ;
  2090. LOOP
  2091. PeekKw (tok) ;
  2092. IF tok = TkEnd THEN
  2093. DropB (MatchKey (tok)) ;
  2094. EXIT
  2095. END ;
  2096. IF CurCh () = 0C THEN
  2097. Err (EUnknown) ;
  2098. EXIT
  2099. END ;
  2100. IF tok = TkCase THEN
  2101. (* Variant part: fields in all arms overlay at the same base.
  2102. The tag, when named, is an ordinary field before the arms. *)
  2103. DropB (MatchKey (tok)) ;
  2104. Skip () ;
  2105. tagName [0] := CHR (0) ;
  2106. savePosVar := srcPos ;
  2107. IF Alpha (CurCh ()) THEN
  2108. GetWord () ;
  2109. Skip () ;
  2110. IF CurCh () = ':' THEN
  2111. CopyStr (tagName, wrd) ;
  2112. DropCh (GetCh ()) ;
  2113. ParseType (subcls, s2, e2) ;
  2114. IF OK () THEN
  2115. IF fldTop < MaxFields THEN
  2116. CopyStr (fields [fldTop].name, tagName) ;
  2117. fields [fldTop].cls := subcls ;
  2118. fields [fldTop].size := s2 ;
  2119. fields [fldTop].elem := e2 ;
  2120. fields [fldTop].off := running ;
  2121. fields [fldTop].fldFirst := recFldFirst ;
  2122. fields [fldTop].fldLast := recFldLast ;
  2123. fields [fldTop].arrIdx := newArrIdx ;
  2124. fields [fldTop].ptrElem := 0 ;
  2125. INC (fldTop) ;
  2126. running := running + s2
  2127. ELSE
  2128. Err (ECompOvf)
  2129. END
  2130. ELSE
  2131. RETURN
  2132. END
  2133. ELSE
  2134. srcPos := savePosVar ;
  2135. ParseType (subcls, s2, e2) ;
  2136. IF NOT OK () THEN
  2137. RETURN
  2138. END
  2139. END
  2140. ELSE
  2141. ParseType (subcls, s2, e2) ;
  2142. IF NOT OK () THEN
  2143. RETURN
  2144. END
  2145. END ;
  2146. IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
  2147. Err (ENoSemi) ;
  2148. RETURN
  2149. END ;
  2150. variantBase := running ;
  2151. armMax := 0 ;
  2152. LOOP
  2153. Skip () ;
  2154. PeekKw (tok) ;
  2155. IF tok = TkEnd THEN
  2156. EXIT
  2157. END ;
  2158. RdConst (lo, subcls, strf) ;
  2159. IF NOT MatchDelim (':') THEN
  2160. Err (ENoSemi) ;
  2161. RETURN
  2162. END ;
  2163. IF NOT MatchDelim ('(') THEN
  2164. Err (ENoSemi) ;
  2165. RETURN
  2166. END ;
  2167. armRunning := variantBase ;
  2168. moreFields := TRUE ;
  2169. WHILE moreFields AND (CurCh () # ')') DO
  2170. groupFirst := fldTop ;
  2171. LOOP
  2172. Skip () ;
  2173. IF NOT Alpha (CurCh ()) THEN
  2174. Err (EUnknown) ;
  2175. RETURN
  2176. END ;
  2177. GetWord () ;
  2178. IF fldTop < MaxFields THEN
  2179. CopyStr (fields [fldTop].name, wrd) ;
  2180. fields [fldTop].off := 0 ;
  2181. INC (fldTop)
  2182. ELSE
  2183. Err (ECompOvf)
  2184. END ;
  2185. IF NOT MatchDelim (',') THEN
  2186. EXIT
  2187. END
  2188. END ;
  2189. ExpectDelim (':', ENoSemi) ;
  2190. ParseType (subcls, s2, e2) ;
  2191. IF NOT OK () THEN
  2192. RETURN
  2193. END ;
  2194. fieldFldFirst := recFldFirst ;
  2195. fieldFldLast := recFldLast ;
  2196. fieldArrIdx := newArrIdx ;
  2197. fieldPtrElem := 0 ;
  2198. IF subcls = TPtr THEN
  2199. fieldPtrElem := e2
  2200. END ;
  2201. i := groupFirst ;
  2202. WHILE i < fldTop DO
  2203. fields [i].cls := subcls ;
  2204. fields [i].size := s2 ;
  2205. fields [i].elem := e2 ;
  2206. fields [i].off := armRunning ;
  2207. fields [i].lo := enumLo ;
  2208. fields [i].hi := enumHi ;
  2209. fields [i].fldFirst := fieldFldFirst ;
  2210. fields [i].fldLast := fieldFldLast ;
  2211. fields [i].arrIdx := fieldArrIdx ;
  2212. fields [i].ptrElem := fieldPtrElem ;
  2213. armRunning := armRunning + s2 ;
  2214. INC (i)
  2215. END ;
  2216. IF MatchDelim (';') THEN
  2217. (* another field group in this arm *)
  2218. ELSE
  2219. moreFields := FALSE
  2220. END
  2221. END ;
  2222. ExpectDelim (')', ENoSemi) ;
  2223. IF armRunning - variantBase > armMax THEN
  2224. armMax := armRunning - variantBase
  2225. END ;
  2226. IF MatchDelim (';') THEN
  2227. (* another variant arm *)
  2228. ELSE
  2229. EXIT
  2230. END
  2231. END ;
  2232. running := variantBase + armMax ;
  2233. (* the record's own END is read by the outer loop *)
  2234. ELSE
  2235. (* the group's names, then ONE `: type' *)
  2236. groupFirst := fldTop ;
  2237. LOOP
  2238. Skip () ;
  2239. IF NOT Alpha (CurCh ()) THEN
  2240. Err (EUnknown) ;
  2241. RETURN
  2242. END ;
  2243. GetWord () ;
  2244. IF fldTop < MaxFields THEN
  2245. CopyStr (fields [fldTop].name, wrd) ;
  2246. fields [fldTop].off := 0 ;
  2247. INC (fldTop)
  2248. ELSE
  2249. Err (ECompOvf)
  2250. END ;
  2251. IF NOT MatchDelim (',') THEN
  2252. EXIT
  2253. END
  2254. END ;
  2255. ExpectDelim (':', ENoSemi) ;
  2256. ParseType (subcls, s2, e2) ;
  2257. IF NOT OK () THEN
  2258. RETURN
  2259. END ;
  2260. fieldFldFirst := recFldFirst ;
  2261. fieldFldLast := recFldLast ;
  2262. fieldArrIdx := newArrIdx ;
  2263. fieldPtrElem := 0 ;
  2264. IF subcls = TPtr THEN
  2265. fieldPtrElem := e2
  2266. END ;
  2267. (* every name in the group gets the type and its own slot *)
  2268. i := groupFirst ;
  2269. WHILE i < fldTop DO
  2270. fields [i].cls := subcls ;
  2271. fields [i].size := s2 ;
  2272. fields [i].elem := e2 ;
  2273. fields [i].off := running ;
  2274. fields [i].lo := enumLo ;
  2275. fields [i].hi := enumHi ;
  2276. fields [i].fldFirst := fieldFldFirst ;
  2277. fields [i].fldLast := fieldFldLast ;
  2278. fields [i].arrIdx := fieldArrIdx ;
  2279. fields [i].ptrElem := fieldPtrElem ;
  2280. running := running + s2 ;
  2281. INC (i)
  2282. END ;
  2283. IF NOT MatchDelim (';') THEN
  2284. Err (ENoSemi) ;
  2285. RETURN
  2286. END
  2287. END ; (* the variant IF *)
  2288. END ; (* the record field LOOP *)
  2289. (* The field TYPES are parsed by ParseType, and its named-type arm
  2290. copies the TYPE's field range into recFldFirst/recFldLast - for
  2291. `Integer' that is 0/0. So recFldFirst set at the top of this branch
  2292. is overwritten by every field type. It happens to be right for the
  2293. FIRST record in a compile (fldTop is 0 then), and wrong for every
  2294. later one: t77's second record started at 2 but recorded 0, which
  2295. made a WITH field lookup scan the previous record's fields. Restore
  2296. the saved start here, after the last field type has been parsed. *)
  2297. recFldFirst := recStart ;
  2298. recFldLast := fldTop ;
  2299. cls := TRecord ;
  2300. size := running ;
  2301. elem := running
  2302. ELSIF tok = TkText THEN
  2303. (* A TEXT file variable. We do our own unbuffered INT 21h I/O, so the
  2304. file variable is just our control block (see FileVarSize) and the
  2305. element is one byte; TP3 keeps a 128-byte buffer in its own, which is
  2306. a divergence we do not need. *)
  2307. cls := TText ;
  2308. size := FileVarSize ;
  2309. elem := 1
  2310. ELSIF tok = TkFile THEN
  2311. (* `file' (untyped) or `file of T'. The element type is parsed for its
  2312. SIZE, which is what a Read/Write moves per record; the variable
  2313. itself is always the control block. CALC.INC's Exist declares
  2314. `F : file', so the OF must be optional: requiring it produced ENoSemi
  2315. at the declaration's semicolon, which was misread as the function
  2316. declaration being unsupported. TUntyp keeps the untyped file
  2317. distinguishable, because a Read/Write through it has no element size
  2318. and must stay refused. *)
  2319. PeekKw (tok) ;
  2320. IF tok = TkOf THEN
  2321. DropB (MatchKey (tok)) ;
  2322. ParseType (subcls, s2, e2) ;
  2323. IF OK () THEN
  2324. cls := TFile ;
  2325. size := FileVarSize ;
  2326. elem := s2
  2327. END
  2328. ELSE
  2329. cls := TUntyp ;
  2330. size := FileVarSize ;
  2331. elem := 1
  2332. END
  2333. ELSE
  2334. IF consumed THEN
  2335. IF Search (wrd, idx) AND (symtab [idx].tag = KConst) THEN
  2336. (* An enum/ordinal subrange written with named constants:
  2337. `Work = Mon..Fri`. Without this, `Mon' was mistaken for a
  2338. named TYPE and failed at the first dot. *)
  2339. lo := symtab [idx].lval ;
  2340. subcls := symtab [idx].cls ;
  2341. IF NOT MatchRange () THEN
  2342. Err (ESimpType) ;
  2343. RETURN
  2344. END ;
  2345. RdConst (hi, subcls, strf) ;
  2346. IF NOT OK () THEN
  2347. RETURN
  2348. END ;
  2349. cls := TEnum ;
  2350. IF (lo >= 0) AND (hi <= 255) AND (lo <= hi) THEN
  2351. size := 1
  2352. ELSE
  2353. size := 2
  2354. END ;
  2355. elem := size ;
  2356. enumLo := lo ;
  2357. enumHi := hi
  2358. ELSIF Search (wrd, idx) AND (symtab [idx].tag = KType) THEN
  2359. cls := symtab [idx].cls ;
  2360. size := symtab [idx].size ;
  2361. (* The type's ELEMENT size, not its total size. They are equal for
  2362. a record, but not for an array (`Letters' is 6 bytes of 3
  2363. Integers) or a file. A named array typed constant used to get
  2364. typElem = 6, wrote six bytes per element and wrapped the
  2365. little-endian divider to zero: SIGFPE in DefConst. *)
  2366. elem := symtab [idx].elem ;
  2367. enumLo := symtab [idx].lo ;
  2368. enumHi := symtab [idx].hi ;
  2369. recFldFirst := symtab [idx].fldFirst ;
  2370. recFldLast := symtab [idx].fldLast ;
  2371. newArrIdx := symtab [idx].arrIdx
  2372. ELSE
  2373. Err (EUnknown)
  2374. END
  2375. ELSE
  2376. (* subrange lo .. hi. TP3 sizes it by its bounds: ONE byte when both
  2377. are inside 0..255, else two (RESUME-TP3.md §2). `0..20' and
  2378. `'A'..'G'' are one byte - which is what makes CellRec 80 and not
  2379. 82, and `Sheet' 147*80. The bounds are kept for `set of T'. *)
  2380. Skip () ;
  2381. isCharRange := (ORD (CurCh ()) = AposC) OR (CurCh () = '^')
  2382. OR (CurCh () = '#') ;
  2383. RdConst (lo, subcls, strf) ;
  2384. IF NOT MatchRange () THEN
  2385. Err (ESimpType) ;
  2386. RETURN
  2387. END ;
  2388. RdConst (hi, subcls, strf) ;
  2389. IF isCharRange THEN
  2390. cls := TChar
  2391. ELSE
  2392. cls := TScalar
  2393. END ;
  2394. IF (lo >= 0) AND (hi <= 255) AND (lo <= hi) THEN
  2395. size := 1
  2396. ELSE
  2397. size := 2
  2398. END ;
  2399. elem := size ;
  2400. enumLo := lo ;
  2401. enumHi := hi
  2402. END
  2403. END
  2404. END ParseType ;
  2405. PROCEDURE DefVar () ;
  2406. (* 'name' (',' name)* ':' type [ABSOLUTE addr] ; group repeated until a
  2407. declaration keyword appears *)
  2408. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2409. cls, size, elem : CARDINAL ;
  2410. tok : CARDINAL ;
  2411. v : LONGINT ;
  2412. off : CARDINAL ;
  2413. first, i : CARDINAL ;
  2414. segAbs : BOOLEAN ;
  2415. BEGIN
  2416. LOOP
  2417. Skip () ; (* after the VAR keyword: skip blanks *)
  2418. IF NOT Alpha (CurCh ()) THEN
  2419. Err (EUnknown) ;
  2420. RETURN
  2421. END ;
  2422. (* A group is `name (, name)* : type' - TPSRC7 rdvarlst/rdvloop collects
  2423. the whole comma-separated list of names FIRST (`CALL ccomma / JZ
  2424. rdvloop'), then reads one `: type' with ecolon/rdvartp, then vardef2
  2425. gives every name in the list that one type. The names are created as
  2426. placeholders here and filled in once the type is known, which is the
  2427. same shape as TP3 reserving a symtab slot per name and storing the
  2428. type pointer into each afterwards. *)
  2429. first := symTop ;
  2430. LOOP
  2431. Skip () ; (* GetWord does not skip blanks, and the cursor
  2432. sits on the space after the ',' *)
  2433. IF NOT Alpha (CurCh ()) THEN
  2434. Err (EUnknown) ;
  2435. RETURN
  2436. END ;
  2437. GetWord () ;
  2438. SaveWord (nm) ;
  2439. DupTest (nm) ;
  2440. DropC (NewSym (nm, KVar, TNone, 0, 0, 0, 0, lexnest # 0)) ;
  2441. IF NOT MatchDelim (',') THEN
  2442. EXIT
  2443. END
  2444. END ;
  2445. ExpectDelim (':', ENoSemi) ;
  2446. ParseType (cls, size, elem) ;
  2447. IF OK () THEN
  2448. IF lexnest = 0 THEN
  2449. IF (size > 2) AND (cls # TString) AND (cls # TRecord)
  2450. AND (cls # TArray) AND (cls # TFile) AND (cls # TText)
  2451. AND (cls # TUntyp) AND (cls # TReal) AND (cls # TSet) THEN
  2452. (* Still the catch-all for a type whose storage this compiler does
  2453. not lay out yet (an ARRAY at its point of use, a RECORD, ...).
  2454. A STRING is the exception now: its layout is length byte +
  2455. characters, and the rest of the compiler knows it - see
  2456. Assignment and IoCall. *)
  2457. Err (ENoLib) ;
  2458. RETURN
  2459. END ;
  2460. PeekKw (tok) ;
  2461. IF tok = TkAbsolute THEN
  2462. DropB (MatchKey (tok)) ;
  2463. Skip () ;
  2464. segAbs := FALSE ;
  2465. IF Alpha (CurCh ()) THEN
  2466. (* Turbo's `Cseg:$80' / `Dseg:$80'. Our globals carry
  2467. LoadBias in their direct addresses, so subtract it from
  2468. the requested absolute address. *)
  2469. GetWord () ;
  2470. ExpectDelim (':', ENoSemi) ;
  2471. segAbs := TRUE
  2472. END ;
  2473. IF (CurCh () = '$') OR (Digit (CurCh ())) THEN
  2474. RdIntConst (v) ;
  2475. IF segAbs THEN
  2476. off := W16 (v - VAL (LONGINT, LoadBias))
  2477. ELSE
  2478. off := W16 (v)
  2479. END
  2480. ELSE
  2481. Err (EUnknown) ;
  2482. off := 0
  2483. END ;
  2484. (* TP3's absolute path (vdstore) leaves the offset fixed, so
  2485. every name in the group aliases the same address. *)
  2486. i := first ;
  2487. WHILE i < symTop DO
  2488. symtab [i].cls := cls ; symtab [i].size := size ;
  2489. symtab [i].elem := elem ; symtab [i].off := off ;
  2490. symtab [i].lo := enumLo ; symtab [i].hi := enumHi ;
  2491. symtab [i].fldFirst := recFldFirst ;
  2492. symtab [i].fldLast := recFldLast ;
  2493. symtab [i].arrIdx := newArrIdx ;
  2494. INC (i)
  2495. END
  2496. ELSE
  2497. i := first ;
  2498. WHILE i < symTop DO
  2499. symtab [i].cls := cls ; symtab [i].size := size ;
  2500. symtab [i].elem := elem ; symtab [i].off := dc ;
  2501. symtab [i].lo := enumLo ; symtab [i].hi := enumHi ;
  2502. symtab [i].fldFirst := recFldFirst ;
  2503. symtab [i].fldLast := recFldLast ;
  2504. symtab [i].arrIdx := newArrIdx ;
  2505. dc := dc + size ;
  2506. INC (i)
  2507. END
  2508. END ;
  2509. varspc := varspc + size * (symTop - first)
  2510. ELSE
  2511. PeekKw (tok) ;
  2512. IF tok = TkAbsolute THEN
  2513. DropB (MatchKey (tok)) ;
  2514. Skip () ;
  2515. segAbs := FALSE ;
  2516. IF Alpha (CurCh ()) THEN
  2517. GetWord () ;
  2518. ExpectDelim (':', ENoSemi) ;
  2519. segAbs := TRUE
  2520. END ;
  2521. IF (CurCh () = '$') OR (Digit (CurCh ())) THEN
  2522. RdIntConst (v) ;
  2523. IF segAbs THEN
  2524. off := W16 (v - VAL (LONGINT, LoadBias))
  2525. ELSE
  2526. off := W16 (v)
  2527. END
  2528. ELSE
  2529. Err (EUnknown) ;
  2530. off := 0
  2531. END ;
  2532. i := first ;
  2533. WHILE i < symTop DO
  2534. symtab [i].cls := cls ; symtab [i].size := size ;
  2535. symtab [i].elem := elem ; symtab [i].off := off ;
  2536. symtab [i].lo := enumLo ; symtab [i].hi := enumHi ;
  2537. symtab [i].fldFirst := recFldFirst ;
  2538. symtab [i].fldLast := recFldLast ;
  2539. symtab [i].arrIdx := newArrIdx ;
  2540. symtab [i].local := FALSE ;
  2541. INC (i)
  2542. END
  2543. ELSE
  2544. IF (size > 2) AND (cls # TString) AND (cls # TRecord)
  2545. AND (cls # TArray) AND (cls # TFile) AND (cls # TText)
  2546. AND (cls # TUntyp) AND (cls # TReal) AND (cls # TSet) THEN
  2547. Err (ENoLib) ;
  2548. RETURN
  2549. END ;
  2550. (* A local occupies [off, off+size-1] and lives BELOW the saved BP,
  2551. because BP-relative locals grow down from the top of the frame
  2552. while a STRING grows UP from its base (length byte, then chars).
  2553. Top-anchored packing keeps both true at once: locFree is the
  2554. highest still-free byte (-1 at the top), the new symbol's base
  2555. is placed so that its LAST byte lands on that byte, and locFree
  2556. then drops by the whole size. So the first INTEGER (size 2) is
  2557. at -2 and the second at -4, byte for byte what the old
  2558. pre-decrement rule produced, while a string[5] local (size 6) is
  2559. at -6..-1 instead of the old -2..+3 - which straddled the saved
  2560. BP at [BP] and the return address at [BP+2] and made `readln(s)'
  2561. into a local store characters over its own return address.
  2562. Each name in the group gets its own slot. *)
  2563. i := first ;
  2564. WHILE i < symTop DO
  2565. symtab [i].cls := cls ; symtab [i].size := size ;
  2566. symtab [i].elem := elem ;
  2567. symtab [i].lo := enumLo ; symtab [i].hi := enumHi ;
  2568. symtab [i].fldFirst := recFldFirst ;
  2569. symtab [i].fldLast := recFldLast ;
  2570. symtab [i].arrIdx := newArrIdx ;
  2571. symtab [i].off := (locFree - size + 1) MOD 10000H ;
  2572. locFree := (locFree - size) MOD 10000H ;
  2573. INC (i)
  2574. END ;
  2575. locBytes := locBytes + size * (symTop - first)
  2576. END
  2577. END
  2578. END ;
  2579. IF MatchDelim (',') THEN
  2580. (* The older `name : type, name : type' spelling: another group
  2581. follows without a semicolon. TP3's grammar is names-then-one-type
  2582. with ',' inside a group and ';' between groups (TPSRC7 rdvarlst /
  2583. rdvloop then ecolon/rdvartp), and both spellings reach the same
  2584. symbols, so both separators are accepted here. *)
  2585. ELSIF MatchDelim (';') THEN
  2586. PeekKw (tok) ;
  2587. IF tok # TkNone THEN
  2588. RETURN
  2589. END
  2590. ELSE
  2591. Err (ENoSemi) ;
  2592. RETURN
  2593. END
  2594. END
  2595. END DefVar ;
  2596. PROCEDURE DefConst () ;
  2597. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2598. v : LONGINT ;
  2599. cls, typCls, typSize, typElem, soff, ci : CARDINAL ;
  2600. isStr : BOOLEAN ;
  2601. tok : CARDINAL ;
  2602. typed, setTyped, arrTyped : BOOLEAN ;
  2603. nb, k, divi, w, slen, eltCls : CARDINAL ;
  2604. BEGIN
  2605. LOOP
  2606. PeekKw (tok) ;
  2607. IF tok # TkNone THEN
  2608. RETURN
  2609. END ;
  2610. Skip () ; (* after the CONST keyword: skip blanks *)
  2611. IF NOT Alpha (CurCh ()) THEN
  2612. Err (EUnknown) ;
  2613. RETURN
  2614. END ;
  2615. GetWord () ;
  2616. SaveWord (nm) ;
  2617. DupTest (nm) ;
  2618. (* Two spellings: a plain constant `name = value', and a TYPED constant
  2619. `name : Type = value', which TP3 3.0 added (TPSRC7's constant reader
  2620. takes the type first and the value second). For a SCALAR type the
  2621. value still folds to an immediate, so the declared type is simply
  2622. recorded as the symbol's class - `FXMax : Char = 'G'' is a TChar
  2623. constant holding 71, and `writeln (FXMax)' prints G rather than 71.
  2624. A SET typed constant (`Numbers : set of Char = ['0'..'9']') is a value
  2625. with a layout, so it cannot fold: its bytes are emitted code-resident
  2626. and the name becomes an addressable value. See the storage decision
  2627. in CALC-COMPILE.md. An ARRAY or RECORD typed constant is still
  2628. refused loudly - those layouts come later. *)
  2629. typed := FALSE ;
  2630. setTyped := FALSE ;
  2631. arrTyped := FALSE ;
  2632. Skip () ;
  2633. IF CurCh () = ':' THEN
  2634. DropCh (GetCh ()) ;
  2635. ParseType (typCls, typSize, typElem) ;
  2636. IF OK () THEN
  2637. IF typCls = TSet THEN
  2638. setTyped := TRUE
  2639. ELSIF typCls = TArray THEN
  2640. arrTyped := TRUE
  2641. ELSIF typSize > 2 THEN
  2642. Err (ENoLib)
  2643. ELSE
  2644. typed := TRUE
  2645. END
  2646. END
  2647. END ;
  2648. IF OK () AND setTyped THEN
  2649. ExpectDelim ('=', ENoSemi) ;
  2650. IF RdSetLiteral (typElem, typSize, constBlob) THEN
  2651. soff := EmSetBlob (constBlob, typSize) ;
  2652. DropC (NewSym (nm, KVar, TSet, typSize, typElem, soff, 0, FALSE))
  2653. END
  2654. ELSIF OK () AND arrTyped THEN
  2655. (* array typed constant: `(v1, v2, ...)' in element order. Integers
  2656. are written little-endian in typElem bytes; a STRING element is
  2657. <length><chars>. CALC uses both XPOS (Integers) and
  2658. StandardfunctionNames (string[6]). *)
  2659. ExpectDelim ('=', ENoSemi) ;
  2660. ExpectDelim ('(', ENoSemi) ;
  2661. eltCls := 0 ;
  2662. IF newArrIdx > 0 THEN
  2663. eltCls := arrs [newArrIdx - 1].ecls
  2664. END ;
  2665. nb := 0 ;
  2666. LOOP
  2667. RdConst (v, cls, isStr) ;
  2668. IF nb + typElem > 512 THEN
  2669. Err (ECompOvf) ;
  2670. EXIT
  2671. END ;
  2672. IF isStr OR ((eltCls = TString) AND (v >= 0) AND (v <= 255)) THEN
  2673. (* A string element is <length><chars>, padded with NULs to
  2674. typElem bytes. StandardfunctionNames is array of string[6].
  2675. A single-character literal ('I') is a TScalar, not a
  2676. string, so an array of STRING must still store it as a
  2677. one-character element. Treating it as an integer divided
  2678. by 256 once per byte and hit DIV by zero at the fourth. *)
  2679. IF isStr THEN
  2680. slen := strLen [rdStrX] ;
  2681. IF slen > typElem - 1 THEN
  2682. slen := typElem - 1
  2683. END
  2684. ELSE
  2685. slen := 1
  2686. END ;
  2687. constBlob [nb] := CHR (slen) ;
  2688. k := 0 ;
  2689. WHILE k < typElem - 1 DO
  2690. IF k < slen THEN
  2691. IF isStr THEN
  2692. constBlob [nb + 1 + k] :=
  2693. strPool [strOff [rdStrX] + k]
  2694. ELSE
  2695. constBlob [nb + 1 + k] := CHR (v)
  2696. END
  2697. ELSE
  2698. constBlob [nb + 1 + k] := CHR (0)
  2699. END ;
  2700. INC (k)
  2701. END ;
  2702. nb := nb + typElem
  2703. ELSIF typElem > 2 THEN
  2704. (* An array of records/reals has no scalar encoding here.
  2705. Refuse loudly instead of shifting a 16-bit value through
  2706. divi until it wraps to zero and aborts the compiler. *)
  2707. Err (ENoLib) ;
  2708. EXIT
  2709. ELSE
  2710. w := VAL (CARDINAL, W16 (v)) ;
  2711. divi := 1 ;
  2712. k := 0 ;
  2713. WHILE k < typElem DO
  2714. constBlob [nb] := CHR ((w DIV divi) MOD 256) ;
  2715. divi := divi * 256 ;
  2716. INC (nb) ;
  2717. INC (k)
  2718. END
  2719. END ;
  2720. IF MatchDelim (',') THEN
  2721. (* another element *)
  2722. ELSE
  2723. EXIT
  2724. END
  2725. END ;
  2726. ExpectDelim (')', ENoSemi) ;
  2727. IF OK () THEN
  2728. soff := EmSetBlob (constBlob, typSize) ;
  2729. ci := NewSym (nm, KVar, TArray, typSize, typElem, soff, 0, FALSE) ;
  2730. symtab [ci].arrIdx := newArrIdx
  2731. END
  2732. ELSIF OK () THEN
  2733. ExpectDelim ('=', ENoSemi) ;
  2734. RdConst (v, cls, isStr) ;
  2735. IF isStr THEN
  2736. Err (ENoLib)
  2737. END ;
  2738. IF OK () THEN
  2739. IF typed THEN
  2740. cls := typCls
  2741. END ;
  2742. DropC (NewSym (nm, KConst, cls, 0, 0, 0, v, FALSE))
  2743. END
  2744. END ;
  2745. IF NOT MatchDelim (';') THEN
  2746. Err (ENoSemi) ;
  2747. RETURN
  2748. END
  2749. END
  2750. END DefConst ;
  2751. PROCEDURE DefLabelPart () ;
  2752. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2753. n : CARDINAL ;
  2754. BEGIN
  2755. LOOP
  2756. Skip () ;
  2757. IF Alpha (CurCh ()) THEN
  2758. GetWord () ;
  2759. SaveWord (nm)
  2760. ELSIF Digit (CurCh ()) THEN
  2761. n := 0 ;
  2762. WHILE Digit (CurCh ()) DO
  2763. n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
  2764. DropCh (GetCh ())
  2765. END ;
  2766. NumToName (n, nm)
  2767. ELSE
  2768. Err (EUnknown) ;
  2769. RETURN
  2770. END ;
  2771. IF NOT Search (nm, n) THEN
  2772. DropC (NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE))
  2773. END ;
  2774. IF NOT MatchDelim (',') THEN
  2775. IF MatchDelim (';') THEN
  2776. RETURN
  2777. END ;
  2778. Err (ENoSemi) ;
  2779. RETURN
  2780. END
  2781. END
  2782. END DefLabelPart ;
  2783. PROCEDURE IfMatchSemi () ;
  2784. BEGIN
  2785. IF NOT MatchDelim (';') THEN
  2786. Err (ENoSemi)
  2787. END
  2788. END IfMatchSemi ;
  2789. PROCEDURE SymEpi () ;
  2790. (* function result: AX := result var for a scalar, or copy six bytes to the
  2791. caller's result scratch for a REAL function. *)
  2792. VAR k : CARDINAL ;
  2793. BEGIN
  2794. IF curIsFunc THEN
  2795. IF OK () THEN
  2796. IF resultIsReal THEN
  2797. EmLoadVar (TRUE, resultPtrOff, 2) ;
  2798. EmMovDiAx () ;
  2799. k := 0 ;
  2800. WHILE k < 6 DO
  2801. EmLoadVar (TRUE, symtab [resultVar].off + k, 1) ;
  2802. EmStDiAlOff (k) ;
  2803. INC (k)
  2804. END
  2805. ELSE
  2806. LoadSymVar (resultVar, symtab [resultVar].off, symtab [resultVar].size)
  2807. END
  2808. END
  2809. END
  2810. END SymEpi ;
  2811. PROCEDURE SlotSize (cls, size : CARDINAL) : CARDINAL ;
  2812. (* How many bytes a value parameter occupies in the callee's stack frame.
  2813. TP3's `tstscal' (TPSRC7 1243): a scalar-like parameter is AT LEAST two
  2814. bytes on the stack even when its type is one byte; anything larger spends
  2815. its own size. Generalised here so step 2 can put a string's 71 bytes on
  2816. the stack without changing what a scalar does today. *)
  2817. BEGIN
  2818. IF (cls = TScalar) OR (cls = TChar) OR (cls = TBool) OR (cls = TEnum) THEN
  2819. RETURN 2
  2820. END ;
  2821. RETURN size
  2822. END SlotSize ;
  2823. PROCEDURE ParamSlotBytes (e : SymEntry) : CARDINAL ;
  2824. (* The bytes a parameter occupies in the frame. A VAR parameter's slot is the
  2825. ADDRESS, always two bytes, whatever its type: `size' stays the type's width
  2826. so loads and stores know whether they read one byte or two. *)
  2827. BEGIN
  2828. IF e.isVar THEN
  2829. RETURN 2
  2830. END ;
  2831. RETURN SlotSize (e.cls, e.size)
  2832. END ParamSlotBytes ;
  2833. PROCEDURE ProcFunc () ;
  2834. (* PROCEDURE name (params) ; body | FUNCTION name (params) : type ; body *)
  2835. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  2836. idx, old : CARDINAL ;
  2837. tok, tok2 : CARDINAL ;
  2838. parmNm : ARRAY [0..MaxName] OF CHAR ;
  2839. cls, size, elem : CARDINAL ;
  2840. isFunc, groupVar : BOOLEAN ;
  2841. saveNest, saveLoc, saveRes, saveF : CARDINAL ;
  2842. saveLB, savePO, saveRP : CARDINAL ;
  2843. saveProc, saveStatic, staticOff : CARDINAL ;
  2844. saveR : BOOLEAN ;
  2845. realResult : BOOLEAN ;
  2846. reusedFwd : BOOLEAN ;
  2847. nestMark : CARDINAL ; (* symTop just inside this procedure *)
  2848. i, first : CARDINAL ;
  2849. BEGIN
  2850. isFunc := curIsFunc ;
  2851. Skip () ; (* after the PROCEDURE/FUNCTION keyword *)
  2852. IF NOT Alpha (CurCh ()) THEN
  2853. Err (EUnknown) ;
  2854. RETURN
  2855. END ;
  2856. GetWord () ;
  2857. SaveWord (nm) ;
  2858. IF Search (nm, idx) AND ((symtab [idx].tag = KProc)
  2859. OR (symtab [idx].tag = KFunc)) AND (symtab [idx].fwd) THEN
  2860. old := idx
  2861. ELSIF Search (nm, idx) THEN
  2862. Err (EUnknown) ;
  2863. RETURN
  2864. ELSE
  2865. old := NewSym (nm, KProc, TNone, 0, 0, 0, 0, FALSE)
  2866. END ;
  2867. IF isFunc THEN
  2868. symtab [old].tag := KFunc
  2869. END ;
  2870. saveProc := curProc ;
  2871. saveStatic := curStaticOff ;
  2872. symtab [old].parent := curProc ;
  2873. curProc := old ;
  2874. saveNest := lexnest ;
  2875. saveLoc := locFree ;
  2876. saveRes := resultVar ;
  2877. saveF := VAL (CARDINAL, ORD (curIsFunc)) ;
  2878. saveLB := locBytes ;
  2879. savePO := parmOff ;
  2880. saveRP := resultPtrOff ;
  2881. saveR := resultIsReal ;
  2882. INC (lexnest) ;
  2883. nestMark := symTop ; (* after the proc's own name, before its params *)
  2884. locFree := 0FFFFH ;
  2885. locBytes := 0 ;
  2886. parmOff := 4 ;
  2887. IF MatchDelim ('(') THEN
  2888. IF CurCh () # ')' THEN
  2889. LOOP
  2890. (* PeekKw, not MatchKey: MatchKey CONSUMES the word it reads, so
  2891. using it to test for VAR would eat the parameter's name. *)
  2892. PeekKw (tok) ;
  2893. groupVar := FALSE ;
  2894. IF tok = TkVar THEN
  2895. (* A by-reference parameter: the slot will hold an ADDRESS.
  2896. `size' still carries the type's own width, because the
  2897. callee's loads and stores must know whether they move one
  2898. byte or two. *)
  2899. DropB (MatchKey (tok)) ;
  2900. groupVar := TRUE
  2901. END ;
  2902. first := symTop ;
  2903. LOOP
  2904. Skip () ; (* blanks before the parameter name *)
  2905. IF NOT Alpha (CurCh ()) THEN
  2906. Err (EUnknown) ;
  2907. RETURN
  2908. END ;
  2909. GetWord () ;
  2910. SaveWord (parmNm) ;
  2911. DupTest (parmNm) ;
  2912. DropC (NewSym (parmNm, KVar, TNone, 0, 0, 0, 0, TRUE)) ;
  2913. IF NOT MatchDelim (',') THEN
  2914. EXIT
  2915. END
  2916. END ;
  2917. ExpectDelim (':', ENoSemi) ; (* formal is 'name : type' *)
  2918. ParseType (cls, size, elem) ;
  2919. IF groupVar AND (size > 2) AND (cls # TString) AND (cls # TReal) THEN
  2920. (* A VAR parameter of a record or array would need the callee
  2921. to address through a larger object than the scalar [BX]
  2922. paths; refusing is the honest answer. VAR STRING and VAR
  2923. REAL are the exceptions: both slots are two-byte ADDRESSES,
  2924. and both value types already have address-based runtime
  2925. paths. *)
  2926. Err (ENoLib) ;
  2927. RETURN
  2928. END ;
  2929. IF (size > 2) AND (cls # TString) AND (cls # TReal) THEN
  2930. (* A string VALUE parameter is a whole-slot copy (n+1 bytes on
  2931. the stack); any other type wider than a word - a record, an
  2932. array, a set - is still refused. A VAR REAL is allowed:
  2933. its slot is an address, not the six bytes. *)
  2934. Err (ENoLib) ;
  2935. RETURN
  2936. END ;
  2937. i := first ;
  2938. WHILE i < symTop DO
  2939. symtab [i].cls := cls ; symtab [i].size := size ;
  2940. symtab [i].elem := elem ;
  2941. symtab [i].lo := enumLo ; symtab [i].hi := enumHi ;
  2942. symtab [i].off := parmOff ;
  2943. symtab [i].isVar := groupVar ;
  2944. IF groupVar THEN
  2945. parmOff := parmOff + 2 (* the address, whatever the type *)
  2946. ELSE
  2947. parmOff := parmOff + SlotSize (cls, size)
  2948. END ;
  2949. INC (i)
  2950. END ;
  2951. IF MatchDelim (')') THEN
  2952. EXIT
  2953. END ;
  2954. IF MatchDelim (';') THEN
  2955. (* next group *)
  2956. ELSIF MatchDelim (',') THEN
  2957. (* the older `name : type, name : type' spelling *)
  2958. ELSE
  2959. Err (ENoSemi) ;
  2960. EXIT
  2961. END
  2962. END
  2963. ELSE
  2964. DropCh (GetCh ())
  2965. END
  2966. END ;
  2967. (* TP3 lays a parameter list out BACKWARDS from BP+4: the LAST declared
  2968. parameter sits at BP+4 and each earlier one is 2 bytes farther from BP
  2969. (RESUME-TP3.md 3.11 - "le dernier paramètre déclaré est le plus proche
  2970. (BP+4)" - and the Code Generation Internals stack frame, "BP+4: last
  2971. parameter"). This is the other half of pushing the arguments as they
  2972. are parsed: the first argument is pushed first and so ends up farthest
  2973. from BP, which has to be the FIRST declared parameter's slot.
  2974. During the loop the offsets were provisional, in declaration order
  2975. (parmOff starts at 4 and grows by each parameter's SLOT size), so
  2976. parameter k sat at 4 + sum of the slots before it. For slot sizes s and
  2977. frame total T, the parameter's final base is
  2978. 4 + (T - prefix - s) = 8 + T - off - s, which sends the FIRST declared
  2979. parameter to the far end and the LAST to BP+4. With every slot 2 -
  2980. every scalar, which is all this compiler accepts today - that reduces
  2981. exactly to the old `parmOff + 2 - off`, so no existing frame moves.
  2982. A slot larger than 2 (a string's 71 bytes) falls out of the same
  2983. formula, and a VAR parameter is exactly 2 even when its TYPE is one
  2984. byte: ParamSlotBytes carries that rule.
  2985. The range is exactly the parameters: the only symbol created while the
  2986. list is read is the parameter itself (NewSym inside the loop),
  2987. ParseType creates no symbols, the procedure's own name predates
  2988. nestMark, and a function's result variable is created after this point. *)
  2989. i := nestMark ;
  2990. WHILE i < symTop DO
  2991. symtab [i].off := 8 + (parmOff - 4) - symtab [i].off
  2992. - ParamSlotBytes (symtab [i]) ;
  2993. INC (i)
  2994. END ;
  2995. (* Record the parameter list on the procedure symbol: a call site has to
  2996. know each argument's SLOT size to push and clean up the right number of
  2997. bytes. Only when this definition actually read a list - a FORWARD's
  2998. list is kept, and the later `function Expression;' that repeats only the
  2999. name must not wipe it. *)
  3000. IF symTop > nestMark THEN
  3001. symtab [old].parmFirst := nestMark ;
  3002. symtab [old].parmCount := symTop - nestMark
  3003. END ;
  3004. (* TP3 lets the definition of a forward repeat only the name. CALC writes
  3005. `function Expression{(...):Real;forward};` -- the parameter list is in a
  3006. comment, but the body still uses ExpressionCh. The forward's formal
  3007. symbols are still in the table (HideLocals only changed their level), so
  3008. make exactly that range visible for this body again. *)
  3009. reusedFwd := FALSE ;
  3010. IF (symTop = nestMark) AND symtab [old].fwd
  3011. AND (symtab [old].parmCount > 0) THEN
  3012. i := symtab [old].parmFirst ;
  3013. WHILE i < symtab [old].parmFirst + symtab [old].parmCount DO
  3014. symtab [i].level := lexnest ;
  3015. INC (i)
  3016. END ;
  3017. reusedFwd := TRUE
  3018. END ;
  3019. realResult := FALSE ;
  3020. IF isFunc THEN
  3021. IF MatchDelim (':') THEN
  3022. ParseType (cls, size, elem)
  3023. ELSIF symtab [old].fwd AND (symtab [old].cls # TNone) THEN
  3024. (* The definition of a forward repeats only the name; inherit the
  3025. result type declared by the forward. *)
  3026. cls := symtab [old].cls ;
  3027. size := symtab [old].size ;
  3028. elem := symtab [old].elem
  3029. ELSE
  3030. cls := TScalar ;
  3031. size := 2 ;
  3032. elem := 2
  3033. END ;
  3034. IF (size > 2) AND (cls # TReal) THEN
  3035. Err (ENoLib) ;
  3036. RETURN
  3037. END ;
  3038. symtab [old].cls := cls ;
  3039. symtab [old].size := size ;
  3040. resultVar := NewSym (nm, KVar, cls, size, elem,
  3041. (locFree - size + 1) MOD 10000H, 0, TRUE) ;
  3042. symtab [old].resvar := resultVar ;
  3043. symtab [resultVar].resvar := old ; (* reverse link for self-calls *)
  3044. locFree := (locFree - size) MOD 10000H ;
  3045. locBytes := locBytes + size ;
  3046. resultIsReal := (cls = TReal) ;
  3047. IF resultIsReal THEN
  3048. (* Hidden two-byte local holding the caller's result destination.
  3049. The caller passes it in DI before the CALL. *)
  3050. resultPtrOff := (locFree - 1) MOD 10000H ;
  3051. locFree := (locFree - 2) MOD 10000H ;
  3052. locBytes := locBytes + 2
  3053. ELSE
  3054. resultPtrOff := 0
  3055. END
  3056. END ;
  3057. IfMatchSemi () ;
  3058. PeekKw (tok2) ;
  3059. IF (tok2 = TkForward) OR (tok2 = TkExternal) THEN
  3060. DropB (MatchKey (tok2)) ;
  3061. symtab [old].fwd := TRUE ;
  3062. symtab [old].defnd := (tok2 = TkExternal) ;
  3063. IfMatchSemi () ;
  3064. HideLocals (nestMark) ; (* a FORWARD's parameters are not the
  3065. caller's to see either *)
  3066. lexnest := saveNest ;
  3067. locFree := saveLoc ;
  3068. resultVar := saveRes ;
  3069. resultIsReal := saveR ;
  3070. resultPtrOff := saveRP ;
  3071. curIsFunc := (saveF # 0) ;
  3072. locBytes := saveLB ;
  3073. parmOff := savePO ;
  3074. curProc := saveProc ;
  3075. curStaticOff := saveStatic ;
  3076. RETURN
  3077. END ;
  3078. (* A procedure declared inside another one receives its lexical parent's
  3079. frame in DX. Reserve a hidden local for it before ordinary locals, so
  3080. nested code can walk the static chain. Top-level procedures have no
  3081. parent frame and no slot. *)
  3082. IF symtab [old].level > 0 THEN
  3083. staticOff := (locFree - 1) MOD 10000H ;
  3084. locFree := (locFree - 2) MOD 10000H ;
  3085. locBytes := locBytes + 2 ;
  3086. symtab [old].staticOff := staticOff ;
  3087. curStaticOff := staticOff
  3088. ELSE
  3089. staticOff := 0 ;
  3090. curStaticOff := 0
  3091. END ;
  3092. (* Body. Nested procedures are emitted BEFORE the enclosing procedure's
  3093. prologue: otherwise execution entering the outer procedure falls straight
  3094. through the nested bodies. The outer entry point is therefore set only
  3095. after DefPart has emitted them. *)
  3096. DefPart () ; (* nested declarations; stops at BEGIN *)
  3097. symtab [old].goPos := pc ;
  3098. symtab [old].defnd := TRUE ;
  3099. EmPushBp () ;
  3100. EmMovBpSp () ;
  3101. IF symtab [old].level > 0 THEN
  3102. (* The caller passed the parent frame in DX. *)
  3103. EmMovAxDx () ;
  3104. EmStoreVar (TRUE, staticOff, 2)
  3105. END ;
  3106. IF resultIsReal THEN
  3107. (* The caller put the six-byte result destination in DI. Save it
  3108. before declarations/body can reuse DI. *)
  3109. EmMovAxDi () ;
  3110. EmStoreVar (TRUE, resultPtrOff, 2)
  3111. END ;
  3112. IF locBytes > 0 THEN
  3113. EmSubSp (locBytes)
  3114. END ;
  3115. DropC (EmCall (TU_StackChk)) ;
  3116. Statmnt () ; (* body *)
  3117. IF OK () THEN
  3118. SymEpi () ;
  3119. EmLeave () ;
  3120. EmRet ()
  3121. END ;
  3122. (* patch pending forward calls to this proc *)
  3123. i := 0 ;
  3124. WHILE i < nPend DO
  3125. IF (pend [i].kind = 1) AND (pend [i].who = old) THEN
  3126. SetPatTgt (pend [i].place, symtab [old].goPos) ;
  3127. pend [i].kind := 99
  3128. END ;
  3129. INC (i)
  3130. END ;
  3131. HideLocals (nestMark) ; (* parameters and locals stop here *)
  3132. IF reusedFwd THEN
  3133. (* Hide only the forward's parameter range. HideLocals starts at
  3134. parmFirst and would also hide every procedure declared between the
  3135. forward and its definition; t120's `B' disappeared when A was
  3136. defined that way. *)
  3137. i := symtab [old].parmFirst ;
  3138. WHILE i < symtab [old].parmFirst + symtab [old].parmCount DO
  3139. symtab [i].level := 0FFFFH ;
  3140. INC (i)
  3141. END
  3142. END ;
  3143. lexnest := saveNest ;
  3144. locFree := saveLoc ;
  3145. resultVar := saveRes ;
  3146. resultIsReal := saveR ;
  3147. resultPtrOff := saveRP ;
  3148. curIsFunc := (saveF # 0) ;
  3149. locBytes := saveLB ;
  3150. parmOff := savePO ;
  3151. curProc := saveProc ;
  3152. curStaticOff := saveStatic
  3153. END ProcFunc ;
  3154. PROCEDURE DefType () ;
  3155. (* 'name' '=' typeDef ; ... until a declaration keyword appears *)
  3156. VAR nm : ARRAY [0..MaxName] OF CHAR ;
  3157. cls, size, elem, tidx : CARDINAL ;
  3158. tok : CARDINAL ;
  3159. BEGIN
  3160. LOOP
  3161. PeekKw (tok) ;
  3162. IF tok # TkNone THEN
  3163. RETURN
  3164. END ;
  3165. Skip () ; (* after the TYPE keyword: skip blanks *)
  3166. IF NOT Alpha (CurCh ()) THEN
  3167. Err (EUnknown) ;
  3168. RETURN
  3169. END ;
  3170. GetWord () ;
  3171. SaveWord (nm) ;
  3172. (* A type name may already exist as a forward placeholder created by
  3173. `Next : ^Person' before `Person = record'. Reuse that entry; only a
  3174. real duplicate is an error. *)
  3175. IF Search (nm, tidx) AND (symtab [tidx].tag = KType)
  3176. AND (symtab [tidx].cls = TNone) THEN
  3177. (* fill the placeholder below *)
  3178. ELSE
  3179. DupTest (nm) ;
  3180. tidx := NewSym (nm, KType, TNone, 0, 0, 0, 0, FALSE)
  3181. END ;
  3182. ExpectDelim ('=', ENoSemi) ;
  3183. ParseType (cls, size, elem) ;
  3184. IF NOT OK () THEN
  3185. RETURN
  3186. END ;
  3187. symtab [tidx].cls := cls ;
  3188. symtab [tidx].size := size ;
  3189. symtab [tidx].elem := elem ;
  3190. symtab [tidx].fwd := FALSE ;
  3191. IF (cls = TEnum) OR (cls = TChar) OR (cls = TScalar) THEN
  3192. (* Enumeration and scalar/CHAR subrange bounds, set by ParseType. *)
  3193. symtab [tidx].lo := enumLo ;
  3194. symtab [tidx].hi := enumHi
  3195. END ;
  3196. IF cls = TRecord THEN
  3197. (* and a record's fields live in the field table; the type records
  3198. which range is its own *)
  3199. symtab [tidx].fldFirst := recFldFirst ;
  3200. symtab [tidx].fldLast := recFldLast
  3201. END ;
  3202. IF cls = TArray THEN
  3203. symtab [tidx].arrIdx := newArrIdx ;
  3204. (* ...and the ELEMENT's field table, if it is a record. A variable
  3205. declared `Sheet : Cells' gets its field metadata through the named
  3206. type; without this, `Sheet[I,J].Field' and `with Sheet[I,J]' found
  3207. fldFirst = fldLast = 0 and reported "not a record". An INLINE
  3208. `array ... of Rec' worked because ParseType's element call left
  3209. the globals set for DefVar; the named form had lost them. *)
  3210. symtab [tidx].fldFirst := recFldFirst ;
  3211. symtab [tidx].fldLast := recFldLast
  3212. END ;
  3213. DropC (tidx) ;
  3214. IF NOT MatchDelim (';') THEN
  3215. Err (ENoSemi) ;
  3216. RETURN
  3217. END
  3218. END
  3219. END DefType ;
  3220. PROCEDURE DefPart () ;
  3221. (* LABEL / CONST / TYPE / VAR / OVERLAY / PROC / FUNCTION / BEGIN *)
  3222. VAR tok : CARDINAL ;
  3223. BEGIN
  3224. LOOP
  3225. IF MatchDelim (';') THEN
  3226. (* separator between declarations *)
  3227. ELSE
  3228. PeekKw (tok) ;
  3229. IF tok = TkBegin THEN
  3230. RETURN
  3231. END ;
  3232. IF NOT MatchKey (tok) THEN
  3233. Err (EUnknown) ;
  3234. RETURN
  3235. END ;
  3236. CASE tok OF
  3237. TkLabel : DefLabelPart () ;
  3238. | TkConst : DefConst () ;
  3239. | TkType : DefType () ;
  3240. | TkVar : DefVar () ;
  3241. | TkOverlay :
  3242. LOOP
  3243. Skip () ;
  3244. IF CurCh () = ';' THEN
  3245. DropCh (GetCh ()) ;
  3246. EXIT
  3247. END ;
  3248. IF CurCh () = 0C THEN
  3249. Err (ENoSemi) ;
  3250. EXIT
  3251. END ;
  3252. DropCh (GetCh ())
  3253. END ;
  3254. | TkProcedure : curIsFunc := FALSE ; ProcFunc () ;
  3255. | TkFunction : curIsFunc := TRUE ; ProcFunc () ;
  3256. ELSE
  3257. Err (EUnknown) ;
  3258. RETURN
  3259. END ;
  3260. IF NOT OK () THEN
  3261. RETURN
  3262. END
  3263. END
  3264. END
  3265. END DefPart ;
  3266. (* ---------------------------------------------------------------- *)
  3267. (* driver (TPSRC7 compile) *)
  3268. (* ---------------------------------------------------------------- *)
  3269. PROCEDURE DefBuiltins () ;
  3270. (* The standard procedures. Without these, WRITELN is absent from the
  3271. symbol table, Statmnt's identifier branch fails its Search and every
  3272. program that prints anything dies with EUnknown (41) on the '(' after the
  3273. call name - the single remaining cause of failure in the fixture matrix.
  3274. Tagged KBuiltin (not KProc) because these are not called generically:
  3275. WRITE/WRITELN/READ/READLN need TP3's per-argument type dispatch
  3276. (IoCall), and HALT takes no argument at all. defnd is TRUE because the
  3277. entry point is known - there is no forward reference to patch. *)
  3278. VAR i : CARDINAL ;
  3279. BEGIN
  3280. i := NewSym ("WRITE" , KBuiltin, BI_Write , 0, 0, 0, 0, FALSE) ;
  3281. symtab [i].defnd := TRUE ;
  3282. symtab [i].goPos := TU_WrInt ;
  3283. i := NewSym ("WRITELN", KBuiltin, BI_WriteLn, 0, 0, 0, 0, FALSE) ;
  3284. symtab [i].defnd := TRUE ;
  3285. symtab [i].goPos := TU_WrInt ;
  3286. i := NewSym ("READ" , KBuiltin, BI_Read , 0, 0, 0, 0, FALSE) ;
  3287. symtab [i].defnd := TRUE ;
  3288. symtab [i].goPos := TU_RdInt ;
  3289. i := NewSym ("READLN" , KBuiltin, BI_ReadLn , 0, 0, 0, 0, FALSE) ;
  3290. symtab [i].defnd := TRUE ;
  3291. symtab [i].goPos := TU_RdInt ;
  3292. i := NewSym ("HALT" , KBuiltin, BI_Halt , 0, 0, 0, 0, FALSE) ;
  3293. symtab [i].defnd := TRUE ;
  3294. symtab [i].goPos := TU_Halt ;
  3295. i := NewSym ("LENGTH" , KBuiltin, BI_Length , 0, 0, 0, 0, FALSE) ;
  3296. symtab [i].defnd := TRUE ;
  3297. symtab [i].goPos := 0 ; (* inline: the length byte, no entry *)
  3298. (* The CRT identifiers. ClrScr and GotoXY emit real INT 10h code; the other
  3299. four are registered so they are recognised, and IoCall refuses them with
  3300. ENoLib - loud, not inert. *)
  3301. i := NewSym ("CLRSCR" , KBuiltin, BI_ClrScr , 0, 0, 0, 0, FALSE) ;
  3302. symtab [i].defnd := TRUE ;
  3303. i := NewSym ("GOTOXY" , KBuiltin, BI_GotoXY , 0, 0, 0, 0, FALSE) ;
  3304. symtab [i].defnd := TRUE ;
  3305. i := NewSym ("LOWVIDEO", KBuiltin, BI_LowVideo, 0, 0, 0, 0, FALSE) ;
  3306. symtab [i].defnd := TRUE ;
  3307. i := NewSym ("NORMVIDEO", KBuiltin, BI_NormVideo, 0, 0, 0, 0, FALSE) ;
  3308. symtab [i].defnd := TRUE ;
  3309. i := NewSym ("CLREOL" , KBuiltin, BI_ClrEol , 0, 0, 0, 0, FALSE) ;
  3310. symtab [i].defnd := TRUE ;
  3311. i := NewSym ("DELAY" , KBuiltin, BI_Delay , 0, 0, 0, 0, FALSE) ;
  3312. symtab [i].defnd := TRUE ;
  3313. i := NewSym ("KEYPRESSED", KBuiltin, BI_KeyPressed, 0, 0, 0, 0, FALSE) ;
  3314. symtab [i].defnd := TRUE ;
  3315. i := NewSym ("UPCASE", KBuiltin, BI_UpCase, 0, 0, 0, 0, FALSE) ;
  3316. i := NewSym ("ORD", KBuiltin, BI_Ord, 0, 0, 0, 0, FALSE) ;
  3317. symtab [i].defnd := TRUE ;
  3318. i := NewSym ("ROUND", KBuiltin, BI_Round, 0, 0, 0, 0, FALSE) ;
  3319. symtab [i].defnd := TRUE ;
  3320. i := NewSym ("TRUNC", KBuiltin, BI_Trunc, 0, 0, 0, 0, FALSE) ;
  3321. symtab [i].defnd := TRUE ;
  3322. i := NewSym ("MSDOS", KBuiltin, BI_Msdos, 0, 0, 0, 0, FALSE) ;
  3323. symtab [i].defnd := TRUE ;
  3324. i := NewSym ("INTR", KBuiltin, BI_Intr, 0, 0, 0, 0, FALSE) ;
  3325. symtab [i].defnd := TRUE ;
  3326. i := NewSym ("GETDIR", KBuiltin, BI_GetDir, 0, 0, 0, 0, FALSE) ;
  3327. symtab [i].defnd := TRUE ;
  3328. i := NewSym ("SEG", KBuiltin, BI_Seg, 0, 0, 0, 0, FALSE) ;
  3329. symtab [i].defnd := TRUE ;
  3330. i := NewSym ("OFS", KBuiltin, BI_Ofs, 0, 0, 0, 0, FALSE) ;
  3331. symtab [i].defnd := TRUE ;
  3332. i := NewSym ("POS", KBuiltin, BI_Pos, 0, 0, 0, 0, FALSE) ;
  3333. symtab [i].defnd := TRUE ;
  3334. i := NewSym ("CONCAT", KBuiltin, BI_Concat, 0, 0, 0, 0, FALSE) ;
  3335. symtab [i].defnd := TRUE ;
  3336. i := NewSym ("RANDOM", KBuiltin, BI_Random, 0, 0, 0, 0, FALSE) ;
  3337. symtab [i].defnd := TRUE ;
  3338. i := NewSym ("RANDOMIZE", KBuiltin, BI_Randomize, 0, 0, 0, 0, FALSE) ;
  3339. symtab [i].defnd := TRUE ;
  3340. i := NewSym ("FILLCHAR", KBuiltin, BI_FillChar, 0, 0, 0, 0, FALSE) ;
  3341. symtab [i].defnd := TRUE ;
  3342. i := NewSym ("MOVE", KBuiltin, BI_Move, 0, 0, 0, 0, FALSE) ;
  3343. symtab [i].defnd := TRUE ;
  3344. i := NewSym ("SEEK", KBuiltin, BI_Seek, 0, 0, 0, 0, FALSE) ;
  3345. symtab [i].defnd := TRUE ;
  3346. i := NewSym ("FILEPOS", KBuiltin, BI_FilePos, 0, 0, 0, 0, FALSE) ;
  3347. symtab [i].defnd := TRUE ;
  3348. i := NewSym ("FILESIZE", KBuiltin, BI_FileSize, 0, 0, 0, 0, FALSE) ;
  3349. symtab [i].defnd := TRUE ;
  3350. i := NewSym ("ERASE", KBuiltin, BI_Erase, 0, 0, 0, 0, FALSE) ;
  3351. symtab [i].defnd := TRUE ;
  3352. i := NewSym ("RENAME", KBuiltin, BI_Rename, 0, 0, 0, 0, FALSE) ;
  3353. symtab [i].defnd := TRUE ;
  3354. i := NewSym ("BLOCKREAD", KBuiltin, BI_BlockRead, 0, 0, 0, 0, FALSE) ;
  3355. symtab [i].defnd := TRUE ;
  3356. i := NewSym ("BLOCKWRITE", KBuiltin, BI_BlockWrite, 0, 0, 0, 0, FALSE) ;
  3357. symtab [i].defnd := TRUE ;
  3358. i := NewSym ("SIZEOF", KBuiltin, BI_SizeOf, 0, 0, 0, 0, FALSE) ;
  3359. symtab [i].defnd := TRUE ;
  3360. i := NewSym ("SUCC", KBuiltin, BI_Succ, 0, 0, 0, 0, FALSE) ;
  3361. i := NewSym ("PRED", KBuiltin, BI_Pred, 0, 0, 0, 0, FALSE) ;
  3362. symtab [i].defnd := TRUE ;
  3363. (* File operations. These are the names CALC actually uses; Eof, not
  3364. EndOfFile, because CALC declares a variable called EndOfFile. *)
  3365. i := NewSym ("ASSIGN", KBuiltin, BI_Assign, 0, 0, 0, 0, FALSE) ;
  3366. symtab [i].defnd := TRUE ;
  3367. i := NewSym ("RESET", KBuiltin, BI_Reset, 0, 0, 0, 0, FALSE) ;
  3368. symtab [i].defnd := TRUE ;
  3369. i := NewSym ("REWRITE", KBuiltin, BI_Rewrite, 0, 0, 0, 0, FALSE) ;
  3370. symtab [i].defnd := TRUE ;
  3371. i := NewSym ("CLOSE", KBuiltin, BI_Close, 0, 0, 0, 0, FALSE) ;
  3372. symtab [i].defnd := TRUE ;
  3373. i := NewSym ("IORESULT", KBuiltin, BI_IoResult, 0, 0, 0, 0, FALSE) ;
  3374. symtab [i].defnd := TRUE ;
  3375. i := NewSym ("CHR", KBuiltin, BI_Chr, 0, 0, 0, 0, FALSE) ;
  3376. symtab [i].defnd := TRUE ;
  3377. i := NewSym ("STR", KBuiltin, BI_Str, 0, 0, 0, 0, FALSE) ;
  3378. symtab [i].defnd := TRUE ;
  3379. i := NewSym ("NEW", KBuiltin, BI_New, 0, 0, 0, 0, FALSE) ;
  3380. symtab [i].defnd := TRUE ;
  3381. i := NewSym ("DISPOSE", KBuiltin, BI_Dispose, 0, 0, 0, 0, FALSE) ;
  3382. symtab [i].defnd := TRUE ;
  3383. i := NewSym ("EOF", KBuiltin, BI_Eof, 0, 0, 0, 0, FALSE) ;
  3384. symtab [i].defnd := TRUE ;
  3385. i := NewSym ("VAL", KBuiltin, BI_Val, 0, 0, 0, 0, FALSE) ;
  3386. DropC (i) ;
  3387. i := NewSym ("COPY", KBuiltin, BI_Copy, 0, 0, 0, 0, FALSE) ;
  3388. symtab [i].defnd := TRUE ;
  3389. i := NewSym ("ABS", KBuiltin, BI_Abs, 0, 0, 0, 0, FALSE) ;
  3390. symtab [i].defnd := TRUE ;
  3391. i := NewSym ("SQRT", KBuiltin, BI_Sqrt, 0, 0, 0, 0, FALSE) ;
  3392. symtab [i].defnd := TRUE ;
  3393. i := NewSym ("SQR", KBuiltin, BI_Sqr, 0, 0, 0, 0, FALSE) ;
  3394. symtab [i].defnd := TRUE ;
  3395. i := NewSym ("LN", KBuiltin, BI_Ln, 0, 0, 0, 0, FALSE) ;
  3396. symtab [i].defnd := TRUE ;
  3397. i := NewSym ("EXP", KBuiltin, BI_Exp, 0, 0, 0, 0, FALSE) ;
  3398. symtab [i].defnd := TRUE ;
  3399. i := NewSym ("SIN", KBuiltin, BI_Sin, 0, 0, 0, 0, FALSE) ;
  3400. symtab [i].defnd := TRUE ;
  3401. i := NewSym ("COS", KBuiltin, BI_Cos, 0, 0, 0, 0, FALSE) ;
  3402. symtab [i].defnd := TRUE ;
  3403. i := NewSym ("ARCTAN", KBuiltin, BI_Arctan, 0, 0, 0, 0, FALSE) ;
  3404. symtab [i].defnd := TRUE ;
  3405. i := NewSym ("INT", KBuiltin, BI_Int, 0, 0, 0, 0, FALSE) ;
  3406. symtab [i].defnd := TRUE ;
  3407. i := NewSym ("DELETE", KBuiltin, BI_Delete, 0, 0, 0, 0, FALSE) ;
  3408. symtab [i].defnd := TRUE ;
  3409. i := NewSym ("INSERT", KBuiltin, BI_Insert, 0, 0, 0, 0, FALSE) ;
  3410. symtab [i].defnd := TRUE ;
  3411. (* KBD: the console keyboard as a TEXT file. This runtime's read entries
  3412. already read the console, so `Read(Kbd, Ch)' drops the file argument and
  3413. reads a character exactly as `Read(Ch)' does. *)
  3414. i := NewSym ("KBD", KVar, TText, FileVarSize, 1, 0, 0, FALSE) ;
  3415. symtab [i].isKbd := TRUE ;
  3416. (* LST is TP3's printer device. The corpus uses it for listings; define
  3417. the identifier as a TEXT variable so Write(Lst, ...) type-checks. *)
  3418. i := NewSym ("LST", KVar, TText, FileVarSize, 1, 0, 0, FALSE)
  3419. END DefBuiltins ;
  3420. PROCEDURE Inittur () ;
  3421. (* reset compiler state and define the standard types *)
  3422. VAR i, rt : CARDINAL ;
  3423. BEGIN
  3424. abortFac := FALSE ;
  3425. errNum := 0 ;
  3426. txerrPos := 0 ;
  3427. srcPos := 0 ;
  3428. srcLen := Length () ;
  3429. inclActive := FALSE ;
  3430. inclEnd := 0 ;
  3431. (* The image layout is
  3432. [JMP rel16][runtime][program header][program code]
  3433. The runtime is copied to the front, which is what the original does:
  3434. TPSRC7 "copyrt" runs REPZ MOVSB with SI=DI=0 and then "MOV pc,#$2D7C".
  3435. Because the runtime sits at (near) offset 0, every address the compiler
  3436. emits is already image-absolute - the data symbols' offsets, the TU_*
  3437. call targets and the rel16 displacements all need no relocation pass.
  3438. (The base shift would in fact cancel in EmCall's arithmetic, since both
  3439. sides of a CALL move together; making the offsets absolute just means
  3440. the linker has nothing to do but copy bytes.)
  3441. The JMP is new, and it is not cosmetic. A DOS .COM is entered at
  3442. CS:0100, i.e. FILE offset 0, and for a long time offset 0 held the
  3443. runtime's first bytes - so a .COM built by this compiler started by
  3444. executing initmem with AX holding whatever the loader left in it. Every
  3445. test up to that point checked bytes and never ran the thing, so it could
  3446. not see this. The jump is the program's entry and the runtime is
  3447. ordinary data to it; keeping the runtime at the front is what preserves
  3448. the no-relocation property, so the jump goes in front of the runtime
  3449. rather than the runtime being moved behind the program.
  3450. dc is put a fixed 4 KiB above the end of the program so that data cannot
  3451. collide with code in a single 64 KiB .COM segment. LIMITATION: a
  3452. program whose code exceeds 4 KiB overruns its own data area. TP3 had
  3453. overlay segments for this; we do not, and the check belongs where the
  3454. limit is documented rather than as a silent truncation. *)
  3455. RT_Build (EntSize) ;
  3456. rt := RT_Size () ;
  3457. IF rt >= MaxCode THEN
  3458. Err (EMemOvf) ; (* cannot happen: rt is 436 *)
  3459. RETURN
  3460. END ;
  3461. (* The entry jump, at image offset 0. See the layout note above: a DOS
  3462. .COM is entered at CS:0100, which is file offset 0, so whatever sits
  3463. at offset 0 is the program's first executed instruction. *)
  3464. (* The jump's three bytes are written out longhand rather than through
  3465. Eword, because Eword writes at pc and advances it, and pc is stale at
  3466. this point -- the operand landed wherever the last compile left pc. *)
  3467. cbuf [0] := 0E9H ; (* JMP rel16 *)
  3468. entRel := 1 ;
  3469. cbuf [entRel] := 0 ;
  3470. cbuf [entRel + 1] := 0 ;
  3471. i := EntSize ;
  3472. WHILE i - EntSize < rt DO
  3473. cbuf [i] := RT_Byte (i - EntSize) ;
  3474. INC (i)
  3475. END ;
  3476. pc := rt + EntSize ;
  3477. rtSz := rt + EntSize ;
  3478. dataBase := DataStart ;
  3479. dc := dataBase ;
  3480. imgReady := TRUE ;
  3481. strTop := 0 ;
  3482. strCnt := 0 ;
  3483. rdStrX := 0 ;
  3484. varspc := 0 ;
  3485. symTop := 0 ;
  3486. (* Per-compile tables. They used to survive into the next file when the
  3487. driver compiles several in one process (the compile matrix does exactly
  3488. that): array indices kept growing, and a record's field range could be
  3489. read against a stale table. One compile must not depend on the last. *)
  3490. fldTop := 0 ;
  3491. arrTop := 0 ;
  3492. withN := 0 ;
  3493. nPatch := 0 ;
  3494. nPend := 0 ;
  3495. exitCnt := 0 ;
  3496. brkN := 0 ;
  3497. caseN := 0 ;
  3498. lexnest := 0 ;
  3499. curProc := 0 ;
  3500. curStaticOff := 0 ;
  3501. curIsFunc := FALSE ;
  3502. resultVar := 0 ;
  3503. resultIsReal := FALSE ;
  3504. resultPtrOff := 0 ;
  3505. callScratch := 0 ;
  3506. locFree := 0FFFFH ;
  3507. locBytes := 0 ;
  3508. parmOff := 4 ;
  3509. dirs.rng := FALSE ;
  3510. dirs.chk := TRUE ;
  3511. InitKeys () ;
  3512. DropC (NewSym ("INTEGER", KType, TScalar, 2, 2, 0, 0, FALSE)) ;
  3513. DropC (NewSym ("BYTE" , KType, TScalar, 1, 1, 0, 0, FALSE)) ;
  3514. DropC (NewSym ("CHAR" , KType, TChar, 1, 1, 0, 0, FALSE)) ;
  3515. DropC (NewSym ("BOOLEAN", KType, TBool , 1, 1, 0, 0, FALSE)) ;
  3516. DropC (NewSym ("REAL" , KType, TReal , 6, 6, 0, 0, FALSE)) ;
  3517. DropC (NewSym ("STRING" , KType, TString, 256, 1, 0, 0, FALSE)) ;
  3518. DropC (NewSym ("TRUE" , KConst, TBool, 1, 1, 0, 1, FALSE)) ;
  3519. DropC (NewSym ("FALSE" , KConst, TBool, 1, 1, 0, 0, FALSE)) ;
  3520. (* TP3's absolute byte array `Mem[address]'. The base is chosen so the
  3521. normal LoadBias addition lands on absolute offset zero. *)
  3522. arrs [0].n := 1 ; arrs [0].elem := 1 ; arrs [0].ecls := TScalar ;
  3523. arrs [0].lo [0] := 0 ; arrs [0].hi [0] := 65535 ;
  3524. arrs [0].fldFirst := 0 ; arrs [0].fldLast := 0 ; arrs [0].elemType := 0 ;
  3525. i := NewSym ("MEM", KVar, TArray, 65535, 1,
  3526. W16 (-VAL (LONGINT, LoadBias)), 0, FALSE) ;
  3527. symtab [i].arrIdx := 1 ;
  3528. arrTop := 1 ;
  3529. i := NewSym ("PORT", KVar, TArray, 1, 1, 0, 0, FALSE) ;
  3530. symtab [i].isPort := TRUE ;
  3531. DefBuiltins () ;
  3532. tmpA := NewSym ("@@T1", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
  3533. dc := dc + 2 ;
  3534. tmpB := NewSym ("@@T2", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
  3535. dc := dc + 2 ;
  3536. (* The text attribute LowVideo/NormVideo set (default 7, written by the
  3537. prologue). One byte, and its address is baked into the clear sequences. *)
  3538. crtAttrOff := dc ;
  3539. DropC (NewSym ("@@ATTR", KVar, TScalar, 1, 1, dc, 0, FALSE)) ;
  3540. dc := dc + 1 ;
  3541. (* Runtime entry offsets, derived from the blob rather than assumed. This
  3542. has to happen after RT_Build, since RT_Entry only knows where the code
  3543. landed once the blob is assembled.
  3544. RT_Entry returns an IMAGE-ABSOLUTE address, already biased by the base
  3545. RT_Build was given, so nothing here has to know where the runtime
  3546. landed. It used to return a blob-relative offset, and the bias was
  3547. applied here instead - three bytes' worth, for the entry jump. With
  3548. that line missing, every CALL landed three bytes short, in the middle of
  3549. a neighbouring runtime entry, and a CALL into the middle of wrtin's
  3550. `INT 21h' behaves perfectly plausibly: the program runs, prints nothing
  3551. and hangs. Only running it finds that. *)
  3552. IF (RT_Entry (13) = 0) OR (RT_Entry (11) = 0) OR (RT_Entry (3) = 0) THEN
  3553. (* RT_Entry returns 0 for an unknown selector. initmem sits at 0
  3554. legitimately, so it cannot appear in this test - but wrtinl, rdln
  3555. and wrint never can, so catching them is enough to catch a runtime
  3556. that failed to build or a selector that went stale. This test is on
  3557. 0 is not a usable address here, since RT_Entry returns an
  3558. image-absolute address and the base is EntSize. *)
  3559. Err (EMemOvf)
  3560. END ;
  3561. TU_InitMem := RT_Entry (0) ;
  3562. TU_ProgEnd := RT_Entry (1) ;
  3563. TU_StackChk := RT_Entry (2) ;
  3564. TU_WrInt := RT_Entry (3) ;
  3565. TU_WrChar := RT_Entry (4) ;
  3566. TU_WrBool := RT_Entry (5) ;
  3567. TU_WrCharW := RT_Entry (56) ;
  3568. TU_WrBoolW := RT_Entry (57) ;
  3569. TU_WrStrW := RT_Entry (58) ;
  3570. TU_MemCopy := RT_Entry (59) ;
  3571. TU_RealToInt := RT_Entry (60) ;
  3572. TU_Msdos := RT_Entry (61) ;
  3573. TU_GetDir := RT_Entry (64) ;
  3574. TU_StrPos := RT_Entry (68) ;
  3575. TU_Randomize := RT_Entry (69) ;
  3576. TU_Random := RT_Entry (70) ;
  3577. TU_FillChar := RT_Entry (71) ;
  3578. TU_Move := RT_Entry (72) ;
  3579. TU_Seek := RT_Entry (73) ;
  3580. TU_FilePos := RT_Entry (74) ;
  3581. TU_FileSize := RT_Entry (75) ;
  3582. TU_Erase := RT_Entry (76) ;
  3583. TU_Rename := RT_Entry (77) ;
  3584. TU_BlockRead := RT_Entry (78) ;
  3585. TU_BlockWrite := RT_Entry (79) ;
  3586. TU_RunErr := RT_Entry (80) ;
  3587. TU_StrIndex := RT_Entry (81) ;
  3588. TU_WrReal := RT_Entry (6) ;
  3589. TU_RealWrite := RT_Entry (30) ; (* fmtreal + padding + output *)
  3590. TU_RealFmt := RT_Entry (28) ; (* fmtreal into a string variable *)
  3591. TU_RealVal := RT_Entry (29) ; (* ascreal: ASCIIZ -> REAL *)
  3592. TU_RealCmp := RT_Entry (26) ; (* recmp: AX = -1/0/+1 *)
  3593. TU_RealAdd := RT_Entry (22) ;
  3594. TU_RealFromInt := RT_Entry (21) ;
  3595. TU_RealSub := RT_Entry (23) ;
  3596. TU_RealMul := RT_Entry (24) ;
  3597. TU_RealDiv := RT_Entry (25) ;
  3598. TU_WrLn := RT_Entry (7) ;
  3599. TU_RdInt := RT_Entry (8) ;
  3600. TU_RdChar := RT_Entry (9) ;
  3601. TU_RdBool := RT_Entry (10) ;
  3602. TU_RdLn := RT_Entry (11) ;
  3603. TU_Halt := RT_Entry (12) ;
  3604. TU_WrInl := RT_Entry (13) ; (* inline string literal *)
  3605. TU_RdByte := RT_Entry (14) ; (* a BYTE read stores one byte *)
  3606. TU_SetOp := RT_Entry (31) ;
  3607. TU_SetCmp := RT_Entry (32) ;
  3608. TU_SetCopy := RT_Entry (33) ;
  3609. TU_SetSingle := RT_Entry (67) ;
  3610. TU_FileAssign := RT_Entry (34) ;
  3611. TU_FileReset := RT_Entry (35) ;
  3612. TU_FileRewrite := RT_Entry (36) ;
  3613. TU_FileClose := RT_Entry (37) ;
  3614. TU_IoResult := RT_Entry (38) ;
  3615. TU_IoStore := RT_Entry (39) ;
  3616. TU_HeapNew := RT_Entry (40) ;
  3617. TU_FileEof := RT_Entry (41) ;
  3618. TU_FileReadln := RT_Entry (42) ;
  3619. TU_FileRdtok := RT_Entry (65) ;
  3620. TU_FileRdln := RT_Entry (66) ;
  3621. TU_IntFmt := RT_Entry (43) ;
  3622. TU_IntFmtW := RT_Entry (62) ;
  3623. TU_StrFmtW := RT_Entry (63) ;
  3624. TU_ValInt := RT_Entry (44) ;
  3625. TU_StrCopy := RT_Entry (45) ;
  3626. TU_ValReal := RT_Entry (46) ;
  3627. TU_RealAbs := RT_Entry (47) ;
  3628. TU_RealSqrt := RT_Entry (48) ;
  3629. TU_RealLn := RT_Entry (49) ;
  3630. TU_RealExp := RT_Entry (50) ;
  3631. TU_RealSin := RT_Entry (51) ;
  3632. TU_RealCos := RT_Entry (52) ;
  3633. TU_RealArctan := RT_Entry (53) ;
  3634. TU_RealIntFunc := RT_Entry (27) ;
  3635. TU_StrDelete := RT_Entry (54) ;
  3636. TU_StrInsert := RT_Entry (55) ;
  3637. TU_WrStr := RT_Entry (15) ; (* a string variable is written through
  3638. the address on the stack *)
  3639. TU_RdStr := RT_Entry (16) ; (* readln(s)/read(s): TPSRC8 prdstr,
  3640. TPSRC4 xrdstr *)
  3641. TU_StrStore := RT_Entry (17) ; (* s := t: TPSRC8 asnvar, TPSRC2 strstore *)
  3642. TU_CmpStr := RT_Entry (18) ; (* string compare: TPSRC9 vcompare,
  3643. TPSRC2 cmpstr *)
  3644. TU_StrCat := RT_Entry (19) ; (* string concatenation: TPSRC2 xconcat *)
  3645. TU_WrIntW := RT_Entry (20) ; (* width-aware integer: `I:2' *)
  3646. END Inittur ;
  3647. PROCEDURE HeadWord (VAR slot : CARDINAL) ;
  3648. BEGIN
  3649. slot := pc ;
  3650. Eword (0)
  3651. END HeadWord ;
  3652. PROCEDURE Compile (VAR errNo, errPos : CARDINAL) : BOOLEAN ;
  3653. VAR tok : CARDINAL ;
  3654. overProc : CARDINAL ;
  3655. hasProc : BOOLEAN ;
  3656. BEGIN
  3657. Inittur () ;
  3658. IF OK () THEN
  3659. (* Resolve every {$I} first: the parser (and DeclaresProc's lookahead)
  3660. must see one finished buffer. See ExpandIncludes. *)
  3661. ExpandIncludes ()
  3662. END ;
  3663. IF OK () THEN
  3664. (* program prologue: header words, CALL initmem, MOV BP,SP *)
  3665. HeadWord (hdrFlag) ;
  3666. HeadWord (hdrCS) ;
  3667. HeadWord (hdrDS) ;
  3668. HeadWord (hdrHeap) ;
  3669. HeadWord (hdrMax) ;
  3670. Eword (16) ; (* max open files *)
  3671. Eword (0) ; (* input buffer word *)
  3672. Eword (0) ; (* output buffer word *)
  3673. (* Here, and not one line earlier, is the first instruction of the
  3674. program: everything above is the header, which is DATA. The entry
  3675. jump has to land exactly here. Recorded rather than assumed, so that
  3676. a header that grows a word moves the target with it. *)
  3677. prologAt := pc ;
  3678. (* TU_InitMem takes the header offset in AX, not on the stack, so the
  3679. AX load has to precede the call. Previously the prologue called
  3680. offset 8 - which in this layout is the hdrMax word - and that was
  3681. coherent only because the runtime was not there. Now it is the
  3682. real header. *)
  3683. EmMovAxi ((rtSz + LoadBias) MOD 10000H) ;
  3684. DropC (EmCall (TU_InitMem)) ;
  3685. EmMovBpSp () ;
  3686. (* [@@ATTR] := 7 - the default text attribute, before any LowVideo or
  3687. NormVideo runs. The data area is zero-filled, and attribute 0 would
  3688. make every clear black-on-black. Through EmCrtSeq, not raw Ebyte:
  3689. Compile is not a one-line emitter, and the helper audit rightly
  3690. refuses to let a byte-emitting statement hide inside it. *)
  3691. EmCrtSeq (CrtLow) ;
  3692. IF MatchKey (tok) AND (tok = TkProgram) THEN
  3693. (* MatchKey stops right after "PROGRAM", so the optional program
  3694. name normally follows blanks. Skip them before testing for the
  3695. name: otherwise Alpha sees the blank, the name is never consumed
  3696. and IfMatchSemi reports ENoSemi at the name. *)
  3697. Skip () ;
  3698. IF Alpha (CurCh ()) THEN
  3699. GetWord ()
  3700. END ;
  3701. IF MatchDelim ('(') THEN
  3702. WHILE NOT MatchDelim (')') DO
  3703. IF Alpha (CurCh ()) THEN
  3704. GetWord ()
  3705. END ;
  3706. IF CurCh () = ',' THEN
  3707. DropCh (GetCh ())
  3708. END
  3709. END
  3710. END ;
  3711. IfMatchSemi ()
  3712. END ;
  3713. IF OK () THEN
  3714. (* Jump over the procedure bodies, if there are any. DefPart
  3715. compiles them HERE, between the prologue and the main statement
  3716. part, and there was no jump - so a program with a procedure fell
  3717. off the end of the prologue into the first procedure. See
  3718. DeclaresProc for why the condition is asked before DefPart runs
  3719. and not after. *)
  3720. hasProc := DeclaresProc () ;
  3721. IF hasProc THEN
  3722. overProc := EmJmpNear (0)
  3723. END ;
  3724. DefPart () ;
  3725. IF OK () THEN
  3726. (* A separate flag, NOT `overProc # 0`. EmJmpNear returns a
  3727. patch SLOT, and slot 0 is a perfectly ordinary slot - the
  3728. first forward jump in a program is slot 0. So a zero test
  3729. cannot tell "no forward jump" from "forward jump in slot 0",
  3730. it just skips the first patch, and the jump keeps its
  3731. placeholder target of 0. The program then jumped to image
  3732. offset 0, i.e. back to the entry jump, and ran the runtime
  3733. and the whole program again, forever. The slot is only
  3734. valid together with a boolean saying a slot was taken. *)
  3735. IF hasProc THEN
  3736. SetPatTgt (overProc, pc)
  3737. END ;
  3738. IF MatchKey (tok) AND (tok = TkBegin) THEN
  3739. Compound () ;
  3740. IF OK () THEN
  3741. EmXorAxAx () ;
  3742. DropC (EmCall (TU_ProgEnd)) ;
  3743. ResolvePatches () ;
  3744. (* Program-only sizes. The runtime is not part of the
  3745. program's code, and the fixture table has always meant
  3746. "the program's own code", so subtract it here rather
  3747. than making every expectation in expected.tsv wrong. *)
  3748. codeSz := pc - rtSz ;
  3749. dataSz := dc - dataBase ;
  3750. (* Header words. The layout is ours (the original's is
  3751. bigger and serves a real overlay loader), but
  3752. Runtime.EmitInitMem reads +4 and +8, so hdrDS and
  3753. hdrHeap must be the data base and the data end. *)
  3754. (* The entry jump's displacement. A .COM is entered at
  3755. CS:0100 = file offset 0, so the jump is the only thing
  3756. that decides where execution starts, and it has to land
  3757. on the START of the program code - the prologue, which is
  3758. at rtSz - not on pc, which is the END of it. (Patching
  3759. pc - EntSize, i.e. the end, lands one byte past the last
  3760. instruction, in the zero-filled code/data gap, where the
  3761. CPU slides through `ADD [BX+SI],AL' until it faults.)
  3762. The displacement is measured from the END of the jump,
  3763. and both addresses are image-absolute, so the load
  3764. segment cancels. *)
  3765. (* The entry jump's displacement. A .COM is entered at
  3766. CS:0100 = file offset 0, so this jump is the only thing
  3767. that decides where execution starts.
  3768. The target is prologAt - where the prologue ACTUALLY
  3769. began, recorded before the header words were emitted,
  3770. and the header is 16 bytes long, so this is rtSz + 16 and
  3771. NOT rtSz. Landing on rtSz lands on the HEADER, which is
  3772. data, and the CPU then decodes sixteen bytes of it as
  3773. instructions. That failure is spectacularly
  3774. non-deterministic across programs: 01 00 is
  3775. `ADD [BX+SI],AX' and is harmless, so writeln('hi') ran
  3776. fine by sliding through the header into the prologue,
  3777. while t07's hdrHeap word 90 12 decodes as a LOCK-prefixed
  3778. ADD whose displacement crosses a page and faults, and the
  3779. program hung with no output at all. Both looked like
  3780. "the jump is in the right area". Recording the position
  3781. rather than assuming it means a future header that grows
  3782. a word cannot silently reintroduce this. *)
  3783. PatchWord (entRel, (prologAt - EntSize) MOD 10000H) ;
  3784. PatchWord (hdrFlag, 1) ;
  3785. (* Every OFFSET field in the header is a segment offset,
  3786. i.e. an image offset plus LoadBias - one convention for
  3787. the whole structure, so that nobody has to remember
  3788. which of these five words is numbered which way.
  3789. hdrFlag 1 set, so a loader can recognise the header
  3790. hdrCS end of the generated code
  3791. hdrDS first byte of the data area <- read by initmem
  3792. hdrHeap one past the last <- read by initmem
  3793. hdrMax 0 (no overlay loader yet)
  3794. hdrDS and hdrHeap are the two that are CONSUMED, and
  3795. omitting the bias there is a silent no-op: initmem would
  3796. clear a range starting 0100h below the data, off the
  3797. front of the image, and never reach the globals at the
  3798. end. Nothing crashes, and the globals keep whatever the
  3799. loader left in them. *)
  3800. PatchWord (hdrCS, pc + LoadBias) ;
  3801. PatchWord (hdrDS, dataBase + LoadBias) ;
  3802. PatchWord (hdrHeap, dc + LoadBias) ;
  3803. PatchWord (hdrMax, 0)
  3804. END
  3805. ELSE
  3806. Err (EUnknown)
  3807. END
  3808. END
  3809. END
  3810. END ;
  3811. IF NOT MatchDelim ('.') THEN
  3812. Err (EPointExp)
  3813. END ;
  3814. IF abortFac THEN
  3815. errNo := errNum ; (* was "errNo := errNo": a self-assignment,
  3816. because the formal shadowed the module
  3817. variable, so the error code always
  3818. reached the caller as 0 *)
  3819. errPos := txerrPos ;
  3820. RETURN FALSE
  3821. END ;
  3822. errNo := 0 ;
  3823. errPos := 0 ;
  3824. RETURN TRUE
  3825. END Compile ;
  3826. PROCEDURE CodeBytes () : CARDINAL ;
  3827. BEGIN
  3828. RETURN codeSz
  3829. END CodeBytes ;
  3830. PROCEDURE DataBytes () : CARDINAL ;
  3831. BEGIN
  3832. RETURN dataSz
  3833. END DataBytes ;
  3834. PROCEDURE ImageBytes () : CARDINAL ;
  3835. (* Total linked image size: rtSz (the runtime) + CodeBytes (the program).
  3836. The data is NOT part of this image: globals live at DataBase (8000h image)
  3837. and initmem zeroes them. The .COM is exactly these bytes. *)
  3838. BEGIN
  3839. RETURN rtSz + codeSz
  3840. END ImageBytes ;
  3841. PROCEDURE DataBase () : CARDINAL ;
  3842. (* image-absolute offset at which the data area begins (DataStart = 0A000H).
  3843. The data is outside the .COM; initmem zeroes [dataBase, dataBase+dataSz)
  3844. through the header's hdrDS/hdrHeap words. *)
  3845. BEGIN
  3846. RETURN dataBase
  3847. END DataBase ;
  3848. PROCEDURE ImageByteAt (i : CARDINAL) : BYTE ;
  3849. (* i-th byte of the WHOLE image, runtime included, so a test can check the
  3850. real thing a .COM would contain. Returns 0 past the end. *)
  3851. BEGIN
  3852. IF i >= rtSz + codeSz THEN
  3853. RETURN 0
  3854. END ;
  3855. RETURN cbuf [i]
  3856. END ImageByteAt ;
  3857. PROCEDURE CodeByteAt (i : CARDINAL) : BYTE ;
  3858. (* i-th byte of the emitted image, for test harnesses that need to check
  3859. the generated 8086 code rather than just its size. Returns 0 past the
  3860. end of the image. *)
  3861. BEGIN
  3862. IF i >= codeSz THEN
  3863. RETURN 0
  3864. END ;
  3865. RETURN cbuf [i]
  3866. END CodeByteAt ;
  3867. END Compiler.