CompilerExpr.mod 118 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266
  1. IMPLEMENTATION MODULE CompilerExpr ;
  2. FROM SYSTEM IMPORT BYTE ;
  3. FROM TextBuf IMPORT CharAt ;
  4. FROM Runtime IMPORT LoadBias ;
  5. (* Expression grammar and value construction: ParseExpr's precedence climb,
  6. atoms/subscripts/fields, calls, constant folding, type checks and the
  7. scalar/string/set/real operand machinery it shares with assignment and
  8. I/O. *)
  9. FROM CompilerShared IMPORT AposC, ArrRec, BI_Abs, BI_Arctan, BI_Assign,
  10. BI_BlockRead, BI_BlockWrite, BI_Chr, BI_Close,
  11. BI_ClrEol, BI_ClrScr, BI_Concat, BI_Copy, BI_Cos,
  12. BI_Delay, BI_Delete, BI_Dispose, BI_Eof, BI_Erase,
  13. BI_Exp, BI_FilePos, BI_FileSize, BI_FillChar,
  14. BI_GetDir, BI_GotoXY, BI_Halt, BI_Insert, BI_Int,
  15. BI_Intr, BI_IoResult, BI_KeyPressed, BI_Length,
  16. BI_Ln, BI_LowVideo, BI_Move, BI_Msdos, BI_New,
  17. BI_NormVideo, BI_Ofs, BI_Ord, BI_Pos, BI_Pred,
  18. BI_Random, BI_Randomize, BI_Read, BI_ReadLn,
  19. BI_Rename, BI_Reset, BI_Rewrite, BI_Round, BI_Seek,
  20. BI_Seg, BI_Sin, BI_SizeOf, BI_Sqr, BI_Sqrt, BI_Str,
  21. BI_Succ, BI_Trunc, BI_UpCase, BI_Val, BI_Write,
  22. BI_WriteLn, CodeMax, CrtCaret, CrtClr, CrtDelay,
  23. CrtDh, CrtDl, CrtEol, CrtKey, CrtLow, CrtNorm,
  24. DataStart, DirRec, ECompOvf, EConstRange, EFileNF,
  25. EInclNest, EIoHere, EMemOvf, ENoLib, ENoSemi,
  26. EPointExp, ERes, ESimpType, ETypeErr, EUnknown,
  27. EntSize, Err, FileVarSize, FldRec, KBuiltin,
  28. KConst, KFunc, KLabel, KProc, KType, KVar, MaxArrs,
  29. MaxCode, MaxFields, MaxLine, MaxName, MaxPatch,
  30. MaxPend, OK, OpAdd, OpDiv, OpMul, OpShl, OpShr,
  31. OpSub, PatchRec, PendRec, TArray, TBool, TChar,
  32. TEnum, TFile, TNone, TPtr, TReal, TRecord, TScalar,
  33. TSet, TString, TText, TU_BlockRead, TU_BlockWrite,
  34. TU_CmpStr, TU_Erase, TU_FileAssign, TU_FileClose,
  35. TU_FileEof, TU_FilePos, TU_FileRdln, TU_FileRdtok,
  36. TU_FileReadln, TU_FileReset, TU_FileRewrite,
  37. TU_FileSize, TU_FillChar, TU_GetDir, TU_Halt,
  38. TU_HeapNew, TU_InitMem, TU_IntFmt, TU_IntFmtW,
  39. TU_IoResult, TU_IoStore, TU_MemCopy, TU_Move,
  40. TU_Msdos, TU_ProgEnd, TU_Random, TU_Randomize,
  41. TU_RdBool, TU_RdByte, TU_RdChar, TU_RdInt, TU_RdLn,
  42. TU_RdStr, TU_RealAbs, TU_RealAdd, TU_RealArctan,
  43. TU_RealCmp, TU_RealCos, TU_RealDiv, TU_RealExp,
  44. TU_RealFmt, TU_RealFromInt, TU_RealIntFunc,
  45. TU_RealLn, TU_RealMul, TU_RealSin, TU_RealSqrt,
  46. TU_RealSub, TU_RealToInt, TU_RealVal, TU_RealWrite,
  47. TU_Rename, TU_RunErr, TU_Seek, TU_SetCmp,
  48. TU_SetCopy, TU_SetOp, TU_SetSingle, TU_StackChk,
  49. TU_StrCat, TU_StrCopy, TU_StrDelete, TU_StrFmtW,
  50. TU_StrIndex, TU_StrInsert, TU_StrPos, TU_StrStore,
  51. TU_ValInt, TU_ValReal, TU_WrBool, TU_WrBoolW,
  52. TU_WrChar, TU_WrCharW, TU_WrInl, TU_WrInt,
  53. TU_WrIntW, TU_WrLn, TU_WrReal, TU_WrStr, TU_WrStrW,
  54. TUntyp, TkAbsolute, TkAnd, TkArray, TkBegin,
  55. TkCase, TkConst, TkDiv, TkDo, TkDownto, TkElse,
  56. TkEnd, TkExit, TkExternal, TkFile, TkFor,
  57. TkForward, TkFunction, TkGoto, TkIf, TkIn, TkLabel,
  58. TkMod, TkNil, TkNone, TkNot, TkOf, TkOr, TkOverlay,
  59. TkPacked, TkProcedure, TkProgram, TkRecord,
  60. TkRepeat, TkSet, TkString, TkText, TkThen, TkTo,
  61. TkType, TkUntil, TkVar, TkWhile, TkWith, abortFac,
  62. arrTop, arrs, brkN, brkSave, callScratch, caseJmp,
  63. caseN, cbuf, codeSz, constBlob, crtAttrOff,
  64. curIsFunc, curProc, curStaticOff, dataBase, dataSz,
  65. dc, dirs, entRel, enumHi, enumLo, errNum, exitCnt,
  66. exitPatch, fields, fldTop, hdrCS, hdrDS, hdrFlag,
  67. hdrHeap, hdrMax, imgReady, lblHi, lblJmp, lblLo,
  68. locBytes, locFree, loopTy, nPatch, nPend,
  69. newArrIdx, parmOff, patches, pc, pend, prologAt,
  70. recFldFirst, recFldLast, resultIsReal,
  71. resultPtrOff, resultVar, rtSz, srcPos, tmpA, tmpB,
  72. txerrPos, varspc, withBoff, withFF, withFL,
  73. withIdx, withIsDI, withN, withSlot ;
  74. FROM CompilerEmit IMPORT AddPatch, Ebyte, EmAddAxCx, EmAndAxCx, EmBpDisp,
  75. EmBxDisp, EmCall, EmCmpAxCx, EmCmpAxi, EmDecAx,
  76. EmIDivAxCx, EmIncAx, EmJcc, EmJmpNear, EmLdBxBx,
  77. EmLeaDiBx, EmLoadVarBx, EmMovAh0, EmMovAxCx,
  78. EmMovAxDx, EmMovAxSp, EmMovAxi, EmMovBpSp,
  79. EmMovBxBp, EmMovChAl, EmMovChImm, EmMovCxSp,
  80. EmMovCxi, EmMovDxAx, EmMovDxBp, EmMovDxBx,
  81. EmMulAxCx, EmNegAx, EmNotAx, EmOrAxAx, EmOrAxCx,
  82. EmPopAx, EmPopBx, EmPopCx, EmPopDx, EmPopSi,
  83. EmPushAx, EmPushBx, EmPushCx, EmPushDx, EmRelSetcc,
  84. EmSetcc, EmStoreVarBx, EmSubAxCx, EmXchgAxCx,
  85. EmXorAl01, EmXorAxAx, Eword, JccShort, JccShortInv,
  86. PatchWord, ResolvePatches, SetPatTgt ;
  87. FROM CompilerSource IMPORT CurCh, ExpandIncludes, GetCh, PeekAhead,
  88. SetSourcePath, Upper, inclActive, inclEnd, srcLen ;
  89. FROM CompilerUtil IMPORT Alpha, AlphaNum, BitAnd, BitNot, BitOr, CopyStr,
  90. Digit, DropB, DropC, DropCh, IsHexCh, NameEq,
  91. NumToName, W16 ;
  92. FROM CompilerSymbols IMPORT CopyWord, DupTest, HideLocals, MaxSym, NewSym,
  93. SaveWord, Search, SymEntry, lexnest, symTop,
  94. symtab, wrd ;
  95. FROM CompilerLex IMPORT DeclaresProc, ExpectDelim, GetWord, HexVal, InitKeys,
  96. IsBlank, KwAhead, MatchAssign, MatchDelim,
  97. MatchKey, MatchRange, PeekKw, RdConst, RdIntConst,
  98. Skip, StrNew, StrPut, WddTok, rdStrX, strCnt,
  99. strLen, strOff, strPool, strTop ;
  100. FROM Compiler IMPORT Assignment, BoundsOf, CodeByteAt, CodeBytes, Compile,
  101. Compound, DataBase, DataBytes, DefBuiltins,
  102. DefConst, DefLabelPart, DefPart, DefType, DefVar,
  103. EmAddBxAx, EmAddDiAx, EmAddDiImm, EmAddSp,
  104. EmAndAlDl, EmAndAlImm, EmAndClImm, EmCmpAlImm,
  105. EmCrtSeq, EmFileIo, EmIncDi, EmIncSi, EmInlineCh,
  106. EmInlineCrLf, EmLdAlBx, EmLdAlDi, EmLdAlDiOff,
  107. EmLdAlSi, EmLdAxDi, EmLdDiGlob, EmLeaBx, EmLeaDi,
  108. EmLeave, EmLoadVar, EmLoadVarInd, EmLoadVarIndSym,
  109. EmMovAxDi, EmMovAxDs, EmMovBxAx, EmMovBxDi,
  110. EmMovBxi, EmMovClAl, EmMovDiAx, EmMovDlImm,
  111. EmPopDi, EmPushBp, EmPushDi, EmPushSi,
  112. EmPushSymAddr, EmPushVarAddr, EmRet, EmShlAxCl,
  113. EmShlDlCl, EmShrAx1, EmShrAxCl, EmStDiAl,
  114. EmStDiAlOff, EmStDiGlob, EmStDiImm, EmStoreInd,
  115. EmStoreVar, EmStoreVarIndPrep, EmSubAlImm, EmSubSp,
  116. EmXorByteGlob, EmitFrameBase, EmitStaticLink,
  117. HeadWord, IfMatchSemi, ImageByteAt, ImageBytes,
  118. Inittur, IsOuterLocal, LabelColonAhead,
  119. LeaDiSym, LoadSymVar, ParamSlotBytes, ParseType,
  120. ProcFunc, SetSource, SlotSize, Statmnt,
  121. StoreSymVar, StoreVarIndPrepSym, SymEpi ;
  122. PROCEDURE ParseExpr (VAR r : ERes) ; FORWARD ;
  123. PROCEDURE ParseAdd (VAR r : ERes) ; FORWARD ;
  124. PROCEDURE ParseMul (VAR r : ERes) ; FORWARD ;
  125. PROCEDURE ParseNeg (VAR r : ERes) ; FORWARD ;
  126. PROCEDURE ParseAtom (VAR r : ERes) ; FORWARD ;
  127. PROCEDURE ParseCall (idx : CARDINAL) ; FORWARD ;
  128. PROCEDURE ParseCallArgs (idx : CARDINAL) ; FORWARD ;
  129. PROCEDURE EmCallMost (idx, cleanup : CARDINAL) ; FORWARD ;
  130. PROCEDURE RealBin (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ; FORWARD ;
  131. PROCEDURE RealUnary (ent : CARDINAL ; VAR r : ERes) ; FORWARD ;
  132. PROCEDURE IntToMbf (v : LONGINT ; VAR b : ARRAY OF CHAR) ; FORWARD ;
  133. PROCEDURE VarSize (r : ERes) : CARDINAL ;
  134. (* The width of a kind-1 value. An element (`b[i]') or a field (`r.f') has a
  135. size of its own, which is NOT its variable's; when the parser has recorded
  136. one, it wins. Otherwise the variable's declared size is the answer. *)
  137. BEGIN
  138. IF r.sz > 0 THEN
  139. RETURN r.sz
  140. END ;
  141. RETURN symtab [r.idx].size
  142. END VarSize ;
  143. PROCEDURE IsCharArray (r : ERes) : BOOLEAN ;
  144. (* A fixed `array[..] of char', as opposed to a counted STRING. *)
  145. BEGIN
  146. IF (r.cls # TArray) OR (r.arrIdx = 0) THEN
  147. RETURN FALSE
  148. END ;
  149. RETURN arrs [r.arrIdx - 1].ecls = TChar
  150. END IsCharArray ;
  151. PROCEDURE LoadAtom (VAR r : ERes) ;
  152. (* load value of r into AX (folding constants).
  153. A string literal is refused here, and this is the single place that has to:
  154. assignment, IF, WHILE, FOR, REPEAT, CASE, array subscripts and every
  155. operator all reach their operand through LoadAtom, and none of them can use
  156. a counted string where a 16-bit word is expected. Reporting it here rather
  157. than in the parser means writeln('hi') still works - IoCall handles a
  158. literal before it ever calls LoadAtom. *)
  159. VAR emsz : CARDINAL ;
  160. BEGIN
  161. IF (r.kind = 1) AND symtab [r.idx].isPort THEN
  162. EmLoadVar (FALSE, symtab [tmpA].off, 2) ;
  163. EmMovDxAx () ;
  164. Ebyte (0ECH) ; (* IN AL,DX *)
  165. EmMovAh0 () ;
  166. r.kind := 2 ;
  167. RETURN
  168. END ;
  169. IF r.cls = TString THEN
  170. Err (ENoLib) ; (* string value used as a number *)
  171. r.kind := 2 ;
  172. RETURN
  173. END ;
  174. IF r.cls = TSet THEN
  175. (* A set is bytes, not a 16-bit value. It is legal as the right operand
  176. of `in' (and later as a whole-value operand of `+'/`*'/'='/assignment),
  177. all of which handle it BEFORE LoadAtom - but never as a number, which
  178. would otherwise load its first two bytes as if it were an integer. *)
  179. Err (ENoLib) ;
  180. r.kind := 2 ;
  181. RETURN
  182. END ;
  183. IF r.kind = 5 THEN
  184. (* An element whose address ParseSub computed into DI (LEA DI,[base] /
  185. ADD DI,AX): a string character, or an array element of one or two
  186. bytes. Zeroing AH keeps a one-byte value a clean 16-bit char. The
  187. read must happen HERE, before anything else is emitted: the caller
  188. reaches this immediately after parsing the subscript, or - for a
  189. binary operator - SaveLeft has already materialised it, so DI is
  190. still the element's address. *)
  191. IF VarSize (r) > 2 THEN
  192. Err (ENoLib) ; (* a whole record/array as a value *)
  193. r.kind := 2 ;
  194. RETURN
  195. END ;
  196. IF VarSize (r) = 2 THEN
  197. EmLdAxDi ()
  198. ELSE
  199. EmLdAlDi () ;
  200. EmMovAh0 ()
  201. END ;
  202. r.kind := 2 ;
  203. RETURN
  204. END ;
  205. IF r.kind = 0 THEN
  206. EmMovAxi (W16 (r.imm)) ;
  207. r.kind := 2
  208. ELSIF r.kind = 1 THEN
  209. IF symtab [r.idx].isVar THEN
  210. (* A VAR parameter: [BP+off] holds the address, and the value is one
  211. level down. r.boff is necessarily 0 - a by-reference parameter is
  212. a scalar in this increment, so it has nothing to subscript. *)
  213. IF r.boff # 0 THEN
  214. Err (ENoLib) ;
  215. r.kind := 2 ;
  216. RETURN
  217. END ;
  218. EmLoadVarIndSym (r.idx, VarSize (r)) ;
  219. r.kind := 2 ;
  220. RETURN
  221. END ;
  222. IF (VarSize (r) > 2) AND NOT
  223. ((r.cls = TChar) AND (symtab [r.idx].cls = TString)) THEN
  224. Err (ENoLib)
  225. ELSE
  226. (* s[i] indexes one byte of a string, not the string, so its width is
  227. one whatever the variable's declared width is. *)
  228. emsz := VarSize (r) ;
  229. IF (r.cls = TChar) AND (symtab [r.idx].cls = TString) THEN
  230. emsz := 1
  231. END ;
  232. LoadSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, emsz) ;
  233. IF emsz = 1 THEN
  234. EmMovAh0 ()
  235. END ;
  236. r.kind := 2
  237. END
  238. END
  239. END LoadAtom ;
  240. PROCEDURE EmitBoundsCheck (lo, hi, code : CARDINAL) ;
  241. (* `{$R+}' test: AX must be in [lo,hi]. The error path exits through
  242. runtime_error; the normal path jumps over the error stub. *)
  243. VAR jl, jg, over : CARDINAL ;
  244. BEGIN
  245. IF NOT dirs.rng THEN RETURN END ;
  246. IF lo = hi THEN RETURN END ;
  247. EmCmpAxi (W16 (lo)) ; jl := EmJcc (8CH, 0) ;
  248. EmCmpAxi (W16 (hi)) ; jg := EmJcc (8FH, 0) ;
  249. over := EmJmpNear (0) ;
  250. SetPatTgt (jl, pc) ; SetPatTgt (jg, pc) ;
  251. EmMovAxi (code) ; DropC (EmCall (TU_RunErr)) ;
  252. SetPatTgt (over, pc)
  253. END EmitBoundsCheck ;
  254. PROCEDURE ParseSub (VAR r : ERes) ;
  255. (* consume '[' expr ']' while present.
  256. A CONSTANT index folds into the base offset boff, exactly as before. A
  257. VARIABLE index on a STRING cannot fold - off+i is a run-time value - so it
  258. is computed once here into DI and r.kind drops to 5, which is how LoadAtom,
  259. Assignment and IoCall are told the element's address is in DI. Arrays
  260. still accept only a constant index: a variable array subscript needs the
  261. element-size scaling that a one-byte string element does not, and it is
  262. item 2. *)
  263. VAR t : ERes ;
  264. ai, fi, nd, k, st, p : CARDINAL ;
  265. coff : LONGINT ;
  266. accOnStack, haveVar, more : BOOLEAN ;
  267. baseDI, found : BOOLEAN ;
  268. BEGIN
  269. LOOP
  270. Skip () ;
  271. IF ((r.cls = TPtr) OR ((r.kind = 1) AND (symtab [r.idx].cls = TPtr)))
  272. AND (CurCh () = '^') THEN
  273. (* Dereference after a variable, a field or an array element. The
  274. pointer word may be an ordinary symbol (kind 1) or the element
  275. address already in DI (kind 5, e.g. Friend[Index]^). *)
  276. DropCh (GetCh ()) ;
  277. p := r.ptrElem ;
  278. IF p = 0 THEN
  279. p := symtab [r.idx].elem
  280. END ;
  281. IF r.kind = 5 THEN
  282. EmLdAxDi () ;
  283. ELSE
  284. LoadSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, 2)
  285. END ;
  286. EmMovDiAx () ;
  287. r.idx := p ;
  288. r.kind := 5 ;
  289. r.boff := 0 ;
  290. r.cls := symtab [p].cls ;
  291. r.sz := symtab [p].size ;
  292. r.fldFirst := symtab [p].fldFirst ;
  293. r.fldLast := symtab [p].fldLast ;
  294. r.arrIdx := symtab [p].arrIdx ;
  295. r.ptrElem := symtab [p].elem ;
  296. r.hasBounds := ((symtab [p].cls = TScalar) OR (symtab [p].cls = TEnum)
  297. OR (symtab [p].cls = TChar)) AND
  298. (symtab [p].lo # symtab [p].hi) ;
  299. r.blo := symtab [p].lo ; r.bhi := symtab [p].hi
  300. ELSIF CurCh () = '.' THEN
  301. (* FIELD selection: `r.f'. The field's offset adds to the record
  302. base's, and the value's class and WIDTH become the field's - which
  303. is the whole reason a width rides on the expression (VarSize). *)
  304. DropCh (GetCh ()) ;
  305. Skip () ;
  306. IF NOT Alpha (CurCh ()) THEN
  307. Err (EUnknown) ;
  308. RETURN
  309. END ;
  310. GetWord () ;
  311. IF r.fldFirst >= r.fldLast THEN
  312. Err (ESimpType) ; (* not a record *)
  313. RETURN
  314. END ;
  315. fi := r.fldFirst ;
  316. found := FALSE ;
  317. WHILE (fi < r.fldLast) AND NOT found DO
  318. IF NameEq (fields [fi].name, wrd) THEN
  319. found := TRUE
  320. ELSE
  321. INC (fi)
  322. END
  323. END ;
  324. IF NOT found THEN
  325. Err (EUnknown) ;
  326. RETURN
  327. END ;
  328. IF r.kind = 5 THEN
  329. (* The element's address is in DI: a field is a fixed offset from
  330. it. The array symbol already carries the element record's
  331. field range (DefVar copies recFldFirst/recFldLast), which is
  332. why the lookup above works for `Sheet[I,J].Field' too. *)
  333. IF fields [fi].off # 0 THEN
  334. EmAddDiImm (fields [fi].off)
  335. END
  336. ELSE
  337. r.boff := W16 (VAL (LONGINT, r.boff)
  338. + VAL (LONGINT, fields [fi].off)) ;
  339. END ;
  340. r.cls := fields [fi].cls ;
  341. r.sz := fields [fi].size ;
  342. r.fldFirst := fields [fi].fldFirst ;
  343. r.fldLast := fields [fi].fldLast ;
  344. r.arrIdx := fields [fi].arrIdx ;
  345. r.ptrElem := fields [fi].ptrElem ;
  346. r.hasBounds := ((fields [fi].cls = TScalar) OR (fields [fi].cls = TEnum)
  347. OR (fields [fi].cls = TChar)) AND
  348. (fields [fi].lo # fields [fi].hi) ;
  349. r.blo := fields [fi].lo ; r.bhi := fields [fi].hi ;
  350. IF r.cls = TSet THEN
  351. (* ParseVar set setoff only when the SYMBOL itself was a set; for
  352. an array/record the class was still TArray/TRecord then, so the
  353. base offset has to be recorded now. *)
  354. r.setoff := symtab [r.idx].off ;
  355. r.setbase := fields [fi].elem ;
  356. r.setsize := fields [fi].size
  357. END
  358. ELSIF CurCh () = '[' THEN
  359. DropCh (GetCh ()) ;
  360. IF r.cls = TString THEN
  361. (* s[i]: a STRING is a length byte at s[0] followed by the
  362. characters, so index i selects the one byte at off+i. A second
  363. subscript on a character is not a type. *)
  364. ParseExpr (t) ;
  365. IF OK () THEN
  366. IF r.cls = TChar THEN
  367. Err (ESimpType) ;
  368. RETURN
  369. END ;
  370. IF symtab [r.idx].isVar THEN
  371. (* VAR string parameter: the base is the address in the
  372. parameter slot, not LEA of the slot. Compute DI once for
  373. both constant and variable indices, so LoadAtom and
  374. Assignment see the same kind-5 character address they do
  375. for an ordinary string. *)
  376. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  377. EmMovDiAx () ;
  378. IF t.kind = 0 THEN
  379. EmAddDiImm ((r.boff + W16 (t.imm)) MOD 10000H)
  380. ELSE
  381. IF r.boff # 0 THEN
  382. EmAddDiImm (r.boff)
  383. END ;
  384. LoadAtom (t) ;
  385. EmAddDiAx ()
  386. END ;
  387. r.kind := 5
  388. ELSIF t.kind = 0 THEN
  389. (* constant index: fold it into boff, as an array's does, and
  390. r.cls drops to TChar so every consumer below - LoadAtom,
  391. Assignment, IoCall - sees a one-byte value instead of the
  392. whole string. *)
  393. IF dirs.rng AND ((t.imm < 1) OR (t.imm > VAL (LONGINT, VarSize (r) - 1))) THEN
  394. Err (EConstRange) ; RETURN
  395. END ;
  396. r.boff := W16 (VAL (LONGINT, r.boff) + t.imm)
  397. ELSE
  398. (* Variable index. Compute the element address once, now:
  399. the index is evaluated into AX (LoadAtom is a no-op for a
  400. kind-2 value and re-reads [DI] for a nested subscript), then
  401. DI := base + AX. This is TP3's indstr/ialoop shape (TPSRC9
  402. 1100-1111, 1200-1229), which likewise holds the address in
  403. DI until the byte is loaded or stored; a register is what
  404. lets a later LoadAtom read [DI] and a later assignment PUSH
  405. DI across its right-hand side. *)
  406. LoadAtom (t) ;
  407. IF dirs.rng THEN
  408. EmPushAx () ;
  409. IF symtab [r.idx].isVar THEN
  410. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  411. EmPushAx ()
  412. ELSE
  413. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  414. END ;
  415. DropC (EmCall (TU_StrIndex)) ;
  416. EmAddSp (4)
  417. END ;
  418. LeaDiSym (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  419. EmAddDiAx () ;
  420. r.kind := 5
  421. END ;
  422. r.cls := TChar ;
  423. r.sz := 1
  424. END ;
  425. ExpectDelim (']', ENoSemi)
  426. ELSIF r.arrIdx > 0 THEN
  427. (* An array subscript LIST: one expression per dimension, comma
  428. separated. The byte offset is
  429. sum over k of (i[k] - lo[k]) * stride[k]
  430. where stride[k] is the element size times the count of every
  431. LATER dimension (row-major: the LAST dimension varies fastest).
  432. A constant index folds into coff; a run-time one is scaled as
  433. soon as it is parsed and summed on the STACK, because parsing
  434. the next index may clobber AX and DI. *)
  435. baseDI := (r.kind = 5) ;
  436. ai := r.arrIdx - 1 ;
  437. nd := 0 ;
  438. coff := 0 ;
  439. accOnStack := FALSE ;
  440. haveVar := FALSE ;
  441. LOOP
  442. IF nd >= arrs [ai].n THEN
  443. Err (ETypeErr) ;
  444. RETURN
  445. END ;
  446. ParseExpr (t) ;
  447. IF NOT OK () THEN
  448. RETURN
  449. END ;
  450. Skip () ;
  451. IF CurCh () = ':' THEN
  452. (* TP3 segment:offset form in Mem[...]; the segment
  453. expression is consumed and the offset is the index. *)
  454. DropCh (GetCh ()) ;
  455. ParseExpr (t) ;
  456. IF NOT OK () THEN
  457. RETURN
  458. END
  459. END ;
  460. st := arrs [ai].elem ;
  461. k := nd + 1 ;
  462. WHILE k < arrs [ai].n DO
  463. st := st * W16 (arrs [ai].hi [k] - arrs [ai].lo [k] + 1) ;
  464. INC (k)
  465. END ;
  466. Skip () ;
  467. more := CurCh () = ',' ;
  468. IF t.kind = 0 THEN
  469. IF dirs.rng AND ((t.imm < arrs [ai].lo [nd]) OR
  470. (t.imm > arrs [ai].hi [nd])) THEN
  471. Err (EConstRange) ; RETURN
  472. END ;
  473. coff := coff + (t.imm - arrs [ai].lo [nd])
  474. * VAL (LONGINT, st)
  475. ELSE
  476. LoadAtom (t) ;
  477. EmitBoundsCheck (arrs [ai].lo [nd], arrs [ai].hi [nd], 16) ;
  478. IF arrs [ai].lo [nd] # 0 THEN
  479. EmMovCxi (W16 (arrs [ai].lo [nd])) ;
  480. EmSubAxCx ()
  481. END ;
  482. IF st # 1 THEN
  483. EmMovCxi (st) ;
  484. EmMulAxCx ()
  485. END ;
  486. IF more THEN
  487. (* the next dimension's expression will clobber AX: park
  488. the running sum. A SINGLE index skips this entirely,
  489. so 1-D code is byte-identical to the old path. *)
  490. IF accOnStack THEN
  491. EmPopCx () ;
  492. EmAddAxCx () ;
  493. EmPushAx ()
  494. ELSE
  495. EmPushAx () ;
  496. accOnStack := TRUE
  497. END
  498. ELSE
  499. (* last dimension: AX is the total *)
  500. IF accOnStack THEN
  501. EmPopCx () ;
  502. EmAddAxCx () ;
  503. accOnStack := FALSE
  504. END ;
  505. haveVar := TRUE
  506. END
  507. END ;
  508. INC (nd) ;
  509. IF more THEN
  510. DropCh (GetCh ())
  511. ELSE
  512. EXIT
  513. END
  514. END ;
  515. IF nd # arrs [ai].n THEN
  516. (* fewer subscripts than dimensions: the value would be a
  517. sub-array, which has no scalar representation *)
  518. Err (ETypeErr) ;
  519. RETURN
  520. END ;
  521. ExpectDelim (']', ENoSemi) ;
  522. IF accOnStack THEN
  523. (* the last dimensions were constant: the parked sum is still
  524. on the stack, and AX is the total *)
  525. EmPopAx () ;
  526. accOnStack := FALSE
  527. END ;
  528. IF baseDI THEN
  529. (* The base address is already in DI: a pointer dereference
  530. (`Buffer^[i]'). Add the computed byte offset to it; a
  531. constant-only index can use ADD DI,imm16. *)
  532. IF haveVar THEN
  533. EmAddDiAx ()
  534. ELSE
  535. IF coff # 0 THEN
  536. EmAddDiImm (W16 (coff))
  537. END
  538. END ;
  539. r.kind := 5
  540. ELSIF haveVar THEN
  541. LeaDiSym (r.idx, (symtab [r.idx].off + r.boff
  542. + W16 (coff)) MOD 10000H) ;
  543. EmAddDiAx () ;
  544. r.kind := 5
  545. ELSE
  546. r.boff := W16 (VAL (LONGINT, r.boff) + coff)
  547. END ;
  548. r.sz := arrs [ai].elem ;
  549. r.cls := arrs [ai].ecls ;
  550. r.fldFirst := arrs [ai].fldFirst ;
  551. r.fldLast := arrs [ai].fldLast ;
  552. r.arrIdx := 0 ;
  553. r.ptrElem := arrs [ai].elemType ;
  554. r.hasBounds := ((arrs [ai].ecls = TScalar) OR (arrs [ai].ecls = TEnum)
  555. OR (arrs [ai].ecls = TChar)) AND
  556. (arrs [ai].elemLo # arrs [ai].elemHi) ;
  557. r.blo := arrs [ai].elemLo ; r.bhi := arrs [ai].elemHi
  558. ELSE
  559. ParseExpr (t) ;
  560. IF OK () THEN
  561. IF t.kind # 0 THEN
  562. Err (ENoLib)
  563. ELSE
  564. Err (ESimpType)
  565. END
  566. END ;
  567. ExpectDelim (']', ENoSemi)
  568. END
  569. ELSE
  570. RETURN
  571. END
  572. END
  573. END ParseSub ;
  574. PROCEDURE ParseVar (VAR r : ERes) ;
  575. (* name [ '[' constExpr ']' ]* ; a function name maps to its result var *)
  576. VAR idx, p : CARDINAL ;
  577. t : ERes ;
  578. BEGIN
  579. IF NOT Search (wrd, idx) THEN
  580. Err (EUnknown) ;
  581. RETURN
  582. END ;
  583. IF symtab [idx].isPort THEN
  584. (* TP3's Port[address]: a byte-wide pseudo-array implemented with IN/OUT.
  585. The index is evaluated once into the compiler scratch word; assignment
  586. and LoadAtom turn it into OUT DX,AL / IN AL,DX. *)
  587. Skip () ;
  588. IF CurCh () # '[' THEN
  589. Err (ENoSemi) ;
  590. RETURN
  591. END ;
  592. DropCh (GetCh ()) ;
  593. ParseExpr (t) ;
  594. IF NOT OK () THEN RETURN END ;
  595. IF (t.cls # TScalar) AND (t.cls # TChar) THEN
  596. Err (ETypeErr) ;
  597. RETURN
  598. END ;
  599. LoadAtom (t) ;
  600. EmStoreVar (FALSE, symtab [tmpA].off, 2) ;
  601. ExpectDelim (']', ENoSemi) ;
  602. r.kind := 1 ;
  603. r.idx := idx ;
  604. r.cls := TScalar ;
  605. r.sz := 1 ;
  606. r.boff := 0 ;
  607. r.hasBounds := FALSE ;
  608. RETURN
  609. END ;
  610. IF symtab [idx].cls = TPtr THEN
  611. Skip () ;
  612. IF CurCh () = '^' THEN
  613. (* Dereference: the pointer word is the base address. r.idx becomes
  614. the pointee TYPE symbol, because ParseSub reads an array/record's
  615. metadata from the symbol, not from the expression. *)
  616. DropCh (GetCh ()) ;
  617. p := symtab [idx].elem ;
  618. LoadSymVar (idx, symtab [idx].off, 2) ;
  619. EmMovDiAx () ;
  620. r.idx := p ;
  621. r.kind := 5 ;
  622. r.boff := 0 ;
  623. r.cls := symtab [p].cls ;
  624. r.sz := symtab [p].size ;
  625. r.fldFirst := symtab [p].fldFirst ;
  626. r.fldLast := symtab [p].fldLast ;
  627. r.arrIdx := symtab [p].arrIdx ;
  628. r.ptrElem := symtab [p].elem ;
  629. r.hasBounds := ((symtab [p].cls = TScalar) OR (symtab [p].cls = TEnum)
  630. OR (symtab [p].cls = TChar)) AND
  631. (symtab [p].lo # symtab [p].hi) ;
  632. r.blo := symtab [p].lo ; r.bhi := symtab [p].hi ;
  633. ParseSub (r) ;
  634. RETURN
  635. END
  636. END ;
  637. r.idx := idx ;
  638. r.kind := 1 ;
  639. r.boff := 0 ;
  640. r.cls := symtab [idx].cls ;
  641. IF symtab [idx].tag = KFunc THEN
  642. idx := symtab [idx].resvar ;
  643. r.idx := idx ;
  644. r.cls := symtab [idx].cls
  645. ELSIF (symtab [idx].tag # KVar) AND (symtab [idx].tag # KType) THEN
  646. Err (EUnknown) ;
  647. RETURN
  648. END ;
  649. IF r.cls = TSet THEN
  650. (* a set value: carry where its bytes are so ParseCmp can fold `in' and
  651. (later) compare. A set VARIABLE is still refused at its declaration,
  652. so today this is a typed constant or an inline literal. *)
  653. r.setoff := symtab [r.idx].off ;
  654. r.setbase := symtab [r.idx].elem ;
  655. r.setsize := symtab [r.idx].size
  656. END ;
  657. r.sz := symtab [r.idx].size ;
  658. r.fldFirst := symtab [r.idx].fldFirst ;
  659. r.fldLast := symtab [r.idx].fldLast ;
  660. r.arrIdx := symtab [r.idx].arrIdx ;
  661. r.ptrElem := symtab [r.idx].elem ;
  662. r.hasBounds := ((symtab [r.idx].cls = TScalar) OR (symtab [r.idx].cls = TEnum)
  663. OR (symtab [r.idx].cls = TChar)) AND
  664. (symtab [r.idx].lo # symtab [r.idx].hi) ;
  665. r.blo := symtab [r.idx].lo ; r.bhi := symtab [r.idx].hi ;
  666. ParseSub (r)
  667. END ParseVar ;
  668. PROCEDURE ConstAdd (a, b : LONGINT) : LONGINT ;
  669. BEGIN
  670. RETURN VAL (LONGINT, W16 (a + b))
  671. END ConstAdd ;
  672. PROCEDURE ConstSub (a, b : LONGINT) : LONGINT ;
  673. BEGIN
  674. RETURN VAL (LONGINT, W16 (a - b))
  675. END ConstSub ;
  676. PROCEDURE ConstMul (a, b : LONGINT) : LONGINT ;
  677. BEGIN
  678. RETURN VAL (LONGINT, W16 (a * b))
  679. END ConstMul ;
  680. PROCEDURE EmXchgAxDx () ;
  681. (* 92h = XCHG AX,DX. Named for what it EMITS, which is the point of the
  682. whole naming convention: this used to be called EmMoveAxDx, which is what
  683. somebody would expect the opcode to be, and it is not - 89 D8 is
  684. MOV AX,DX, 92h is the exchange. Here the exchange is what is wanted, so
  685. the name is the only thing that was wrong, and it was wrong in the exact
  686. way this file's names are not allowed to be: reading as "a move" when it
  687. is a swap. After EmIDivAxCx the remainder is in DX and `mod` wants it in
  688. AX; an exchange gets it there in one byte where a move also would, so the
  689. behaviour is identical either way and only the name lied. *)
  690. BEGIN
  691. Ebyte (92H)
  692. END EmXchgAxDx ;
  693. PROCEDURE StrConcat (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ; FORWARD ;
  694. PROCEDURE StrOperand (r : ERes) : BOOLEAN ; FORWARD ;
  695. PROCEDURE BinOpEmit (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
  696. (* binary operation at one precedence level; folds constant operands *)
  697. VAR f : LONGINT ;
  698. okc : BOOLEAN ;
  699. BEGIN
  700. IF (left.cls = TReal) OR (right.cls = TReal) OR (op = OpDiv) THEN
  701. (* `/` is real division even when both operands are integers; Pascal's
  702. `div` is the integer operator and has its own token. *)
  703. RealBin (op, left, right, res) ;
  704. RETURN
  705. END ;
  706. IF StrOperand (left) OR StrOperand (right) THEN
  707. (* A string OPERAND - a STRING or a CHAR, which is a one-character string
  708. for this purpose - makes this a string operation, and `+' is the only
  709. one ISO Modula-2 defines: every other binary operator reached here
  710. (OR, and the multiplicative and additive numeric ops) has no meaning
  711. over a STRING, and StrConcat rejects it.
  712. The test is StrOperand and NOT `cls = TString' because of the two
  713. CHAR operands. `'a' + 'b'' has no STRING in it at all, so a
  714. TString-only test let it fall through to the integer path, where the
  715. character codes were added and CHR(195) was printed - a silently
  716. wrong program rather than an error (see t53_strcat_type). A CHAR
  717. operand is still a string operand, which is also what makes `a + ch'
  718. concatenate.
  719. Handling it here, at the head of the one routine every binary
  720. operator funnels through, is what keeps `a + b', `writeln(a + b)',
  721. `(a + b) = c' and `s := a + b + c' all going through the same
  722. emission. *)
  723. StrConcat (op, left, right, res) ;
  724. RETURN
  725. END ;
  726. IF op = TkAnd THEN
  727. IF (left.kind = 0) AND (right.kind = 0) THEN
  728. res.kind := 0 ;
  729. res.imm := BitAnd (left.imm, right.imm) ;
  730. IF (left.cls = TBool) OR (right.cls = TBool) THEN
  731. res.cls := TBool
  732. ELSE
  733. res.cls := left.cls
  734. END ;
  735. RETURN
  736. END ;
  737. LoadPair (left, right) ;
  738. EmAndAxCx () ;
  739. res.kind := 2 ;
  740. IF (left.cls = TBool) OR (right.cls = TBool) THEN
  741. res.cls := TBool
  742. ELSE
  743. res.cls := left.cls
  744. END ;
  745. RETURN
  746. END ;
  747. IF op = TkOr THEN
  748. IF (left.kind = 0) AND (right.kind = 0) THEN
  749. res.kind := 0 ;
  750. res.imm := BitOr (left.imm, right.imm) ;
  751. IF (left.cls = TBool) OR (right.cls = TBool) THEN
  752. res.cls := TBool
  753. ELSE
  754. res.cls := left.cls
  755. END ;
  756. RETURN
  757. END ;
  758. LoadPair (left, right) ;
  759. EmOrAxCx () ;
  760. res.kind := 2 ;
  761. IF (left.cls = TBool) OR (right.cls = TBool) THEN
  762. res.cls := TBool
  763. ELSE
  764. res.cls := left.cls
  765. END ;
  766. RETURN
  767. END ;
  768. IF (left.kind = 0) AND (right.kind = 0) THEN
  769. okc := FALSE ;
  770. CASE op OF
  771. OpAdd : f := ConstAdd (left.imm, right.imm) ; okc := TRUE ;
  772. | OpSub : f := ConstSub (left.imm, right.imm) ; okc := TRUE ;
  773. | OpMul : f := ConstMul (left.imm, right.imm) ; okc := TRUE ;
  774. | TkDiv : okc := (right.imm # 0) AND (right.imm > 0)
  775. AND (left.imm >= 0) ;
  776. IF okc THEN f := left.imm DIV right.imm END ;
  777. | TkMod : okc := (right.imm # 0) AND (right.imm > 0)
  778. AND (left.imm >= 0) ;
  779. IF okc THEN f := left.imm MOD right.imm END ;
  780. ELSE
  781. okc := FALSE
  782. END ;
  783. IF okc THEN
  784. res.kind := 0 ;
  785. res.imm := VAL (LONGINT, W16 (f)) ;
  786. res.cls := left.cls ;
  787. RETURN
  788. ELSIF op = TkDiv THEN
  789. Err (EConstRange) ;
  790. RETURN
  791. END
  792. END ;
  793. LoadPair (left, right) ;
  794. CASE op OF
  795. OpAdd : EmAddAxCx ;
  796. | OpSub : EmSubAxCx ;
  797. | OpMul : EmMulAxCx ;
  798. | OpShl : EmShlAxCl ;
  799. | OpShr : EmShrAxCl ;
  800. | TkDiv : EmIDivAxCx ;
  801. | TkMod : EmIDivAxCx ; EmXchgAxDx ;
  802. ELSE
  803. Err (ETypeErr)
  804. END ;
  805. res.kind := 2 ;
  806. res.cls := left.cls
  807. END BinOpEmit ;
  808. PROCEDURE ConstCmp (op : CARDINAL ; a, b : LONGINT ; VAR f : LONGINT)
  809. : BOOLEAN ;
  810. VAR a16, b16 : CARDINAL ;
  811. BEGIN
  812. a16 := W16 (a) ;
  813. b16 := W16 (b) ;
  814. f := 0 ;
  815. CASE op OF
  816. 1 : f := VAL (LONGINT, ORD (a16 = b16)) ;
  817. | 2 : f := VAL (LONGINT, ORD (a16 # b16)) ;
  818. | 3 : f := VAL (LONGINT, ORD (a16 < b16)) ;
  819. | 4 : f := VAL (LONGINT, ORD (a16 >= b16)) ;
  820. | 5 : f := VAL (LONGINT, ORD (a16 > b16)) ;
  821. | 6 : f := VAL (LONGINT, ORD (a16 <= b16)) ;
  822. ELSE
  823. RETURN FALSE
  824. END ;
  825. RETURN TRUE
  826. END ConstCmp ;
  827. (* ---------------------------------------------------------------- *)
  828. (* keeping an operand alive across the parse of the other one *)
  829. (* ---------------------------------------------------------------- *)
  830. PROCEDURE SaveLeft (VAR left : ERes) ;
  831. VAR sk : CARDINAL ;
  832. (* Called the moment the operator has been recognised, i.e. *before* the
  833. right-hand operand is parsed.
  834. Every binary level in this parser has the shape
  835. left := r ; ParseXxx (right) ; BinOpEmit (op, left, right, r) ;
  836. so the right-hand operand's code is emitted between those two lines, and
  837. the left-hand operand has to survive that. For kind 0 (a constant) and
  838. kind 1 (a named variable) there is nothing to survive: LoadAtom re-emits
  839. the MOV or the load at the operator site, and no code has been emitted
  840. for the left yet. A kind-2 value is different - its code is already in
  841. the image and its value exists only in AX, which the right-hand parse is
  842. about to overwrite. The value had to be parked on the stack instead.
  843. TPSRC9 does exactly this in the other order: addptr and mulptr CALL
  844. pushres (which calls loadatom and sets flgpshax) *before* CALL mullevel,
  845. so PUSH AX lands in the image ahead of the right-hand operand's code.
  846. Without this, `(p > q) or (q > p)` emitted both comparisons and then
  847. ORed the *second* comparison's result with itself - which is why it
  848. printed FALSE for TRUE or FALSE. *)
  849. BEGIN
  850. IF left.cls = TReal THEN
  851. (* Park six bytes in a fresh scratch, exactly as kind-5 does for a
  852. scalar: the right-hand parse may reuse DI or call a routine. *)
  853. sk := NewScratchReal () ;
  854. EmCopyRealTo (symtab [sk].off, left) ;
  855. left.idx := sk ;
  856. left.kind := 1 ;
  857. left.cls := TReal ;
  858. left.sz := 6 ;
  859. left.boff := 0
  860. ELSIF left.kind = 2 THEN
  861. EmPushAx () ;
  862. left.kind := 4 (* 4 = on the top of the stack *)
  863. ELSIF left.kind = 5 THEN
  864. (* A string element whose address is in DI. This is the one place the
  865. address must be spent BEFORE the right-hand operand is parsed: DI
  866. cannot survive it, because the right operand may hold a subscript of
  867. its own (which reuses DI) or call a routine (which may clobber it).
  868. Reading [DI] now - which LoadAtom does, turning kind 5 into kind 2 -
  869. and pushing the value is exactly what the kind-2 case above does
  870. next, so the two branches converge on kind 4. A subscript on the
  871. RIGHT is fine: it sets DI after this point and LoadPair reads it
  872. immediately, with no parse in between. *)
  873. LoadAtom (left) ; (* AL := [DI], AH := 0; kind := 2 *)
  874. EmPushAx () ;
  875. left.kind := 4
  876. END
  877. END SaveLeft ;
  878. PROCEDURE LoadPair (VAR left, right : ERes) ;
  879. (* Materialise both operands of a binary operator so that, once this
  880. returns, AX holds the LEFT one and CX the RIGHT one - which is the state
  881. EmAddAxCx/EmSubAxCx/EmMulAxCx/EmIDivAxCx/EmAndAxCx/EmOrAxCx/EmCmpAxCx
  882. all expect (see EmXchgAxCx).
  883. Three shapes, and only three:
  884. left.kind = 4 SaveLeft pushed it before the right operand was
  885. parsed, so the right value is in AX and the left one
  886. is on the stack: load the right (a no-op for kind 2),
  887. pop the left into CX, then swap.
  888. right.kind = 2 The right value is in AX, and loading the left would
  889. destroy it: push the right, load the left, then pop
  890. the right straight into CX. No swap needed.
  891. neither Both can still be materialised from scratch, and
  892. neither is at risk, so this is the original sequence.
  893. A kind-5 right-hand operand takes that last shape, and is safe there: the
  894. only instruction emitted before its LoadAtom is the left's, and MOV AX,[var]
  895. and MOV AX,imm do not touch DI, so [DI] still holds the element address.
  896. Whichever shape is taken, exactly one push is matched by exactly one
  897. pop. *)
  898. BEGIN
  899. IF left.kind = 4 THEN
  900. LoadAtom (right) ;
  901. EmPopCx () ;
  902. EmXchgAxCx ()
  903. ELSIF right.kind = 2 THEN
  904. EmPushAx () ;
  905. LoadAtom (left) ;
  906. EmPopCx ()
  907. ELSE
  908. LoadAtom (left) ; EmPushAx () ;
  909. LoadAtom (right) ; EmPopCx () ;
  910. EmXchgAxCx ()
  911. END
  912. END LoadPair ;
  913. PROCEDURE EmStrBlob (r : ERes) : CARDINAL ;
  914. (* Emit a string literal's <len><chars> in the code stream behind a JMP and
  915. return the image offset of the LENGTH byte. Shared by EmPushStrAddr (which
  916. pushes that address) and EmPushStrSlot (which copies the bytes onto the
  917. stack for a string value parameter). *)
  918. VAR n, len, here : CARDINAL ;
  919. BEGIN
  920. len := strLen [r.strx] ;
  921. IF len > 255 THEN
  922. (* As in IoCall: the length is one byte, so a longer literal would wrap
  923. and silently carry only its first (length MOD 256) characters.
  924. TP3 strings are at most 255 characters. *)
  925. Err (EConstRange) ;
  926. RETURN 0
  927. END ;
  928. IF len + 1 <= 127 THEN
  929. Ebyte (0EBH) ; Ebyte (VAL (BYTE, len + 1)) (* JMP rel8 over it *)
  930. ELSE
  931. Ebyte (0E9H) ; Eword (len + 1) (* JMP rel16 over it *)
  932. END ;
  933. here := pc ; (* the length byte: the literal's address *)
  934. Ebyte (VAL (BYTE, len)) ;
  935. n := 0 ;
  936. WHILE n < len DO
  937. Ebyte (VAL (BYTE, ORD (strPool [strOff [r.strx] + n]))) ;
  938. INC (n)
  939. END ;
  940. RETURN here
  941. END EmStrBlob ;
  942. PROCEDURE EmAsciiZLiteral (start, stop : CARDINAL) : CARDINAL ;
  943. (* Emit the source text [start,stop) as a NUL-terminated code-resident string
  944. behind a JMP and return its image offset. Used for REAL literals, which are
  945. parsed by the runtime's ascreal rather than by a host-side decimal parser. *)
  946. VAR here, n, len : CARDINAL ;
  947. BEGIN
  948. len := stop - start ;
  949. IF len <= 126 THEN
  950. Ebyte (0EBH) ; Ebyte (VAL (BYTE, len + 1))
  951. ELSE
  952. Ebyte (0E9H) ; Eword (len + 1)
  953. END ;
  954. here := pc ;
  955. n := start ;
  956. WHILE n < stop DO
  957. Ebyte (VAL (BYTE, ORD (CharAt (n)))) ;
  958. INC (n)
  959. END ;
  960. Ebyte (0) ;
  961. RETURN here
  962. END EmAsciiZLiteral ;
  963. PROCEDURE EmPushSetAddr (r : ERes) ;
  964. (* Push the ADDRESS of a set value: the runtime's set entries take addresses.
  965. A set literal/typed constant is code-resident (kind 7), a set-expression
  966. result is a data buffer (kind 8), a named set is symbol+boff, and a field
  967. reached through DI is already addressed. *)
  968. BEGIN
  969. IF r.kind = 5 THEN
  970. EmPushDi ()
  971. ELSIF r.kind = 1 THEN
  972. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  973. ELSIF (r.kind = 7) OR (r.kind = 8) THEN
  974. EmPushVarAddr (FALSE, r.setoff)
  975. ELSE
  976. Err (ENoLib)
  977. END
  978. END EmPushSetAddr ;
  979. PROCEDURE SetPairSize (left, right : ERes ; VAR size, base : CARDINAL) : BOOLEAN ;
  980. (* The byte count for a set operation. An inline literal is a 32-byte
  981. `set of Char' by default; when the other operand is a typed set (CALC's
  982. one-byte `set of Attributes') that type wins. Two non-literals must have
  983. the same origin and are taken at the smaller size; the runtime only ever
  984. touches that many bytes. *)
  985. BEGIN
  986. IF left.setbase # right.setbase THEN
  987. RETURN FALSE
  988. END ;
  989. base := left.setbase ;
  990. IF left.kind = 7 THEN
  991. IF right.kind = 7 THEN
  992. size := left.setsize
  993. ELSE
  994. size := right.setsize
  995. END
  996. ELSIF right.kind = 7 THEN
  997. size := left.setsize
  998. ELSE
  999. size := left.setsize ;
  1000. IF right.setsize < size THEN
  1001. size := right.setsize
  1002. END
  1003. END ;
  1004. RETURN (size > 0) AND (size <= 32)
  1005. END SetPairSize ;
  1006. PROCEDURE AllocSetScratch (size : CARDINAL) : CARDINAL ;
  1007. (* A fresh data buffer for one set expression. One per operation, so a nested
  1008. or repeated expression cannot overwrite the other side while it is still
  1009. being consumed. The bytes need no initialisation: every operation writes
  1010. all `size' of them before the value is read. *)
  1011. VAR off : CARDINAL ;
  1012. BEGIN
  1013. IF dc + size > 0F000H THEN
  1014. Err (EConstRange) ;
  1015. RETURN 0
  1016. END ;
  1017. off := dc ;
  1018. dc := dc + size ;
  1019. RETURN off
  1020. END AllocSetScratch ;
  1021. PROCEDURE SetBinEmit (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
  1022. (* The caller has already pushed the LEFT address (before parsing the right
  1023. operand, so a DI-addressed field survives). Finish the argument list -
  1024. right, destination, (op<<8)|size - call the runtime, and return the buffer
  1025. as a kind-8 set value. *)
  1026. VAR size, base, dest : CARDINAL ;
  1027. BEGIN
  1028. IF NOT SetPairSize (left, right, size, base) THEN
  1029. Err (ENoLib) ;
  1030. RETURN
  1031. END ;
  1032. dest := AllocSetScratch (size) ;
  1033. IF NOT OK () THEN
  1034. RETURN
  1035. END ;
  1036. EmPushSetAddr (right) ;
  1037. EmPushVarAddr (FALSE, dest) ;
  1038. EmMovAxi (W16 (op * 256 + size)) ;
  1039. EmPushAx () ;
  1040. DropC (EmCall (TU_SetOp)) ;
  1041. EmAddSp (8) ;
  1042. res.kind := 8 ;
  1043. res.cls := TSet ;
  1044. res.chr := FALSE ;
  1045. res.setoff := dest ;
  1046. res.setbase := base ;
  1047. res.setsize := size
  1048. END SetBinEmit ;
  1049. PROCEDURE MaterializeDyn (VAR r : ERes ; base, size : CARDINAL) ;
  1050. (* Build a scratch set of `size' bytes with the single dynamic element at
  1051. `base' set. Used for TP3's `[Storage[Index]]' constructor. *)
  1052. VAR dest : CARDINAL ;
  1053. BEGIN
  1054. dest := AllocSetScratch (size) ;
  1055. IF NOT OK () THEN
  1056. RETURN
  1057. END ;
  1058. EmMovAxi (size) ; EmPushAx () ;
  1059. EmMovAxi (base) ; EmPushAx () ;
  1060. LoadSymVar (r.dynElem, symtab [r.dynElem].off, 2) ;
  1061. EmPushAx () ;
  1062. EmPushVarAddr (FALSE, dest) ;
  1063. DropC (EmCall (TU_SetSingle)) ;
  1064. EmAddSp (8) ;
  1065. r.kind := 8 ;
  1066. r.setoff := dest ;
  1067. r.setbase := base ;
  1068. r.setsize := size
  1069. END MaterializeDyn ;
  1070. PROCEDURE EmPushStrAddr (r : ERes) ;
  1071. (* Push the ADDRESS of a whole-string operand - the argument shape cmpstr
  1072. takes, one per operand.
  1073. A string VARIABLE's value already is its address, so this is the same push
  1074. READ makes (EmPushVarAddr), through symtab's local/off.
  1075. A string LITERAL has no storage, and the image carries no initialised data
  1076. to put one in (globals are left to the loader's zero fill), so the
  1077. characters have to travel in the code stream and the operand is the address
  1078. of those bytes. TP3 does the same in TPSRC9 fullvar (993): it builds the
  1079. `JMP' opcode in AL and the length in AH (`MOV AL,#$EB' / `MOV.B AH,CH'),
  1080. emits that word, and takes the position immediately after it as varofs,
  1081. with varseg := CS - the instruction stream steps over the constant instead
  1082. of running into it.
  1083. The difference is one byte, and it is deliberate. TP3's constant is an
  1084. ARRAY [1..n] OF CHAR, and `estr2' (TPSRC10 85) emits it WITHOUT a length
  1085. byte because the length travelled on the stack instead. Every string in
  1086. THIS runtime - variable or literal - is a `string': a length byte and then
  1087. the characters, which is what rdstr writes and what cmpstr reads. So a
  1088. literal operand has to look the same, and the jump carries one extra byte:
  1089. <len><chars>, with the ADDRESS pushed being the length byte itself. The
  1090. jump still exists only to keep the CPU from walking into the characters.
  1091. Nothing is added to the data segment and no runtime entry is involved in
  1092. reading the literal; LEA AX,[that] is absolute into the code segment, which
  1093. is where a .COM's DS already is.
  1094. A 1-character literal is NOT a TString here: RdConst reports it as TScalar
  1095. (its character code), so `s = 'a'` compares a string with a character and
  1096. is a type error rather than a length-1 string compare. That is deliberate
  1097. - it is the same rule `c := 'a'` relies on - and the caller rejects it. *)
  1098. BEGIN
  1099. IF r.kind = 5 THEN
  1100. (* a string element/field computed through DI - `Sheet[X,Y].Contents'
  1101. and the `with' form. DI holds the field's address, which is exactly
  1102. what cmpstr/strcat/strstore take on the stack, so push it. This used
  1103. to be ENoLib, and it was the CALC rung after Write(Value:FW:DEC):
  1104. `if Sheet[UFX,UFY].Contents <> '' then'. *)
  1105. EmPushDi ()
  1106. ELSIF r.kind = 1 THEN
  1107. IF symtab [r.idx].isVar THEN
  1108. (* A VAR string parameter: the slot at [BP+off] holds the address of
  1109. the caller's characters, so push its CONTENTS, not the address of
  1110. the slot. boff is zero for the whole string; an element/subscript
  1111. is kind 5 and handled above. *)
  1112. IF r.boff # 0 THEN
  1113. Err (ENoLib)
  1114. ELSE
  1115. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  1116. EmPushAx ()
  1117. END
  1118. ELSE
  1119. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  1120. END
  1121. ELSIF r.kind = 3 THEN
  1122. EmPushVarAddr (FALSE, EmStrBlob (r)) (* the literal travels inline *)
  1123. ELSE
  1124. Err (ENoLib) (* a computed string is not a value *)
  1125. END
  1126. END EmPushStrAddr ;
  1127. PROCEDURE EmPushRealAddr (r : ERes) ;
  1128. (* Push the address of a six-byte REAL actual: ordinary variable, VAR REAL
  1129. parameter (slot holds the address), or a kind-5 field/function result. *)
  1130. BEGIN
  1131. IF r.kind = 5 THEN
  1132. EmPushDi ()
  1133. ELSIF r.kind = 1 THEN
  1134. IF symtab [r.idx].isVar THEN
  1135. LoadSymVar (r.idx, symtab [r.idx].off, 2) ;
  1136. EmPushAx ()
  1137. ELSE
  1138. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  1139. END
  1140. ELSE
  1141. Err (ENoLib)
  1142. END
  1143. END EmPushRealAddr ;
  1144. PROCEDURE EmPushRealSlot (r : ERes ; reserve : CARDINAL) ;
  1145. (* Push a six-byte value REAL argument's whole slot onto the stack. *)
  1146. BEGIN
  1147. EmPushRealAddr (r) ;
  1148. EmPopSi () ;
  1149. EmSubSp (reserve) ;
  1150. Ebyte (89H) ; Ebyte (0E7H) ; (* MOV DI,SP *)
  1151. EmMovCxi (reserve) ;
  1152. Ebyte (8AH) ; Ebyte (04H) ; (* MOV AL,[SI] *)
  1153. Ebyte (88H) ; Ebyte (05H) ; (* MOV [DI],AL *)
  1154. Ebyte (46H) ; Ebyte (47H) ; (* INC SI / INC DI *)
  1155. Ebyte (0E2H) ; Ebyte (0F8H) (* LOOP back *)
  1156. END EmPushRealSlot ;
  1157. PROCEDURE EmCopyRealTo (dstOff : CARDINAL ; r : ERes) ;
  1158. (* Copy six REAL bytes from r's address into the image global at dstOff, or
  1159. materialise an integer constant there as MBF. *)
  1160. VAR k : CARDINAL ;
  1161. rbuf : ARRAY [0..5] OF CHAR ;
  1162. BEGIN
  1163. IF r.cls = TReal THEN
  1164. EmPushRealAddr (r) ;
  1165. EmPopSi () ;
  1166. EmLeaDi (FALSE, dstOff) ;
  1167. EmMovCxi (6) ;
  1168. Ebyte (8AH) ; Ebyte (04H) ; (* MOV AL,[SI] *)
  1169. Ebyte (88H) ; Ebyte (05H) ; (* MOV [DI],AL *)
  1170. Ebyte (46H) ; Ebyte (47H) ;
  1171. Ebyte (0E2H) ; Ebyte (0F8H) (* LOOP -8 *)
  1172. ELSIF (r.cls = TScalar) AND (r.kind = 0) THEN
  1173. IntToMbf (r.imm, rbuf) ;
  1174. k := 0 ;
  1175. WHILE k < 6 DO
  1176. EmMovAxi (ORD (rbuf [k])) ;
  1177. EmStoreVar (FALSE, (dstOff + k) MOD 10000H, 1) ;
  1178. INC (k)
  1179. END
  1180. ELSE
  1181. Err (ENoLib)
  1182. END
  1183. END EmCopyRealTo ;
  1184. PROCEDURE EmPushStrSlot (r : ERes ; reserve, copyN : CARDINAL) ;
  1185. (* Push a STRING VALUE argument's whole slot: reserve `reserve' bytes on the
  1186. stack and copy `copyN' of the source string's <len><chars> into them, so
  1187. that after the CALL the callee's BP+4 is the length byte and the characters
  1188. follow it.
  1189. <push the source address, then POP SI> ; local/global/literal alike
  1190. SUB SP,reserve ; MOV DI,SP ; MOV CX,copyN
  1191. loop: MOV AL,[SI] ; MOV [DI],AL ; INC SI ; INC DI ; LOOP loop
  1192. The address comes from EmPushStrAddr, which already knows how to form it for
  1193. a local ([BP+off]), a global (absolute) or a literal (inline blob) - so a
  1194. caller's LOCAL string can be passed too, not just a global.
  1195. A byte loop, not REP MOVSB, on purpose: MOVS/STOS address through ES, and a
  1196. .COM's ES happens to equal DS but nothing in this compiler establishes that
  1197. contract. Every access here is DS-relative, which is the one segment the
  1198. image already guarantees. `copyN' is at most `reserve' (a shorter actual
  1199. string than the formal leaves the tail of the slot untouched, harmless
  1200. because the length byte bounds every read). *)
  1201. BEGIN
  1202. EmPushStrAddr (r) ; (* push the source address *)
  1203. EmPopSi () ; (* SI := source *)
  1204. EmSubSp (reserve) ;
  1205. Ebyte (89H) ; Ebyte (0E7H) ; (* MOV DI,SP *)
  1206. EmMovCxi (copyN) ;
  1207. IF copyN > 0 THEN
  1208. Ebyte (8AH) ; Ebyte (04H) ; (* MOV AL,[SI] *)
  1209. Ebyte (88H) ; Ebyte (05H) ; (* MOV [DI],AL *)
  1210. Ebyte (46H) ; (* INC SI *)
  1211. Ebyte (47H) ; (* INC DI *)
  1212. Ebyte (0E2H) ; Ebyte (0F8H) (* LOOP -8: back to MOV AL,[SI] *)
  1213. END
  1214. END EmPushStrSlot ;
  1215. (* ---------------------------------------------------------------- *)
  1216. (* sets - type sizing, literals, compile-time membership *)
  1217. (* ---------------------------------------------------------------- *)
  1218. PROCEDURE SetRangeBits (VAR bytes : ARRAY OF CHAR ; size, base : CARDINAL ;
  1219. lo, hi : LONGINT) ;
  1220. (* Set the bits for every element in [lo, hi], using TP3's hybrid rule
  1221. (coeur-tp-ocr 4.7.2): element e is bit (e MOD 8) of byte
  1222. ((e DIV 8) - (base DIV 8)), and is IGNORED unless that byte is in
  1223. [0, size). A value that lands outside the used bytes is truncated away, as
  1224. TP3 does; one inside them but outside the DECLARED range still lands, which
  1225. is also TP3's behaviour. *)
  1226. VAR rel, first, last, bidx, bit, m, o : CARDINAL ;
  1227. BEGIN
  1228. IF (hi < 0) OR (lo > 255) THEN
  1229. RETURN
  1230. END ;
  1231. IF lo < 0 THEN first := 0 ELSE first := VAL (CARDINAL, lo) END ;
  1232. IF hi > 255 THEN last := 255 ELSE last := VAL (CARDINAL, hi) END ;
  1233. rel := first ;
  1234. WHILE rel <= last DO
  1235. IF rel >= base THEN
  1236. bidx := (rel - base) DIV 8 ;
  1237. IF bidx < size THEN
  1238. bit := (rel - base) MOD 8 ;
  1239. m := 1 ;
  1240. WHILE bit > 0 DO
  1241. m := m * 2 ; DEC (bit)
  1242. END ;
  1243. o := ORD (bytes [bidx]) ;
  1244. IF (o DIV m) MOD 2 = 0 THEN
  1245. bytes [bidx] := CHR (o + m)
  1246. END
  1247. END
  1248. END ;
  1249. INC (rel)
  1250. END
  1251. END SetRangeBits ;
  1252. PROCEDURE RdSetLiteral (base, size : CARDINAL ; VAR bytes : ARRAY OF CHAR) : BOOLEAN ;
  1253. (* Read a set constructor `[e, a..b, ...]' and fill `bytes' (already size
  1254. long). `[]' is the empty set. *)
  1255. VAR v, hi : LONGINT ; subcls : CARDINAL ; strf : BOOLEAN ; k : CARDINAL ;
  1256. BEGIN
  1257. k := 0 ;
  1258. WHILE k < size DO
  1259. bytes [k] := CHR (0) ;
  1260. INC (k)
  1261. END ;
  1262. ExpectDelim ('[', ENoSemi) ;
  1263. IF MatchDelim (']') THEN
  1264. RETURN OK () (* [] - the empty set *)
  1265. END ;
  1266. LOOP
  1267. RdConst (v, subcls, strf) ;
  1268. IF strf THEN
  1269. Err (ETypeErr) ;
  1270. RETURN FALSE
  1271. END ;
  1272. IF MatchRange () THEN
  1273. RdConst (hi, subcls, strf) ;
  1274. IF strf THEN
  1275. Err (ETypeErr) ;
  1276. RETURN FALSE
  1277. END
  1278. ELSE
  1279. hi := v
  1280. END ;
  1281. SetRangeBits (bytes, size, base, v, hi) ;
  1282. IF MatchDelim (',') THEN
  1283. (* another element *)
  1284. ELSE
  1285. EXIT
  1286. END
  1287. END ;
  1288. ExpectDelim (']', ENoSemi) ;
  1289. RETURN OK ()
  1290. END RdSetLiteral ;
  1291. PROCEDURE EmSetBlob (VAR bytes : ARRAY OF CHAR ; size : CARDINAL) : CARDINAL ;
  1292. (* Emit a set's bytes into the code stream behind a JMP over them - the same
  1293. trick EmPushStrAddr uses for a string literal - and return the image offset
  1294. of the first byte. Nothing goes in the data segment: a .COM's DS already
  1295. covers the code segment, which is why this port needs no `seg cs' where the
  1296. original did (see CALC-COMPILE.md's typed-constant storage decision). *)
  1297. VAR here, k : CARDINAL ;
  1298. BEGIN
  1299. IF size + 2 <= 127 THEN
  1300. Ebyte (0EBH) ; Ebyte (VAL (BYTE, size)) (* JMP rel8 over it *)
  1301. ELSE
  1302. Ebyte (0E9H) ; Eword (size) (* JMP rel16 over it *)
  1303. END ;
  1304. here := pc ;
  1305. k := 0 ;
  1306. WHILE k < size DO
  1307. Ebyte (VAL (BYTE, ORD (bytes [k]))) ;
  1308. INC (k)
  1309. END ;
  1310. RETURN here
  1311. END EmSetBlob ;
  1312. PROCEDURE FoldSetIn (left, right : ERes ; VAR res : ERes) ;
  1313. (* `e IN s' where e is a compile-time ordinal and s a set whose bytes are
  1314. already in the image: answer at compile time, exactly as TPSRC2's xsetin
  1315. answers at run time (AH > 255 -> FALSE, else test the bit). A run-time
  1316. element or a run-time set is still ENoLib - that is the next milestone, and
  1317. it must stay LOUD rather than silently answer wrong. *)
  1318. VAR off, base, size, rel, bidx, bit, m, o : CARDINAL ;
  1319. isIn, foldable : BOOLEAN ;
  1320. jl, jg, jmp : CARDINAL ;
  1321. BEGIN
  1322. res.kind := 0 ;
  1323. res.imm := 0 ;
  1324. res.cls := TBool ;
  1325. res.chr := FALSE ;
  1326. IF right.cls # TSet THEN
  1327. Err (ENoLib) ;
  1328. RETURN
  1329. END ;
  1330. IF left.kind # 0 THEN
  1331. (* A RUN-TIME element (`Upcase(Ch) in ['Y','N']', `Ch in ['a'..'z']').
  1332. Materialise it in AX, subtract the set's base, reject the out-of-range
  1333. values with the two signed tests the compiler declares (L and G), then
  1334. index the bit array: byte = (e-base) DIV 8, bit = (e-base) MOD 8.
  1335. The set's address is formed according to where its bytes live: an
  1336. inline literal or typed constant is code-resident (image offset +
  1337. LoadBias), a data set is an absolute or BP-relative address. A set
  1338. already addressed through DI and an element that itself needs DI are
  1339. refused: the address would be lost. *)
  1340. IF right.kind = 5 THEN
  1341. Err (ENoLib) ;
  1342. RETURN
  1343. END ;
  1344. LoadAtom (left) ;
  1345. IF NOT OK () THEN
  1346. RETURN
  1347. END ;
  1348. IF right.setbase # 0 THEN
  1349. EmMovCxi (W16 (right.setbase)) ;
  1350. EmSubAxCx ()
  1351. END ;
  1352. EmCmpAxi (0) ;
  1353. jl := EmJcc (8CH, 0) ; (* JL -> false *)
  1354. EmCmpAxi (W16 (right.setsize * 8)) ;
  1355. jg := EmJcc (8FH, 0) ; (* JG -> false *)
  1356. EmMovClAl () ;
  1357. EmAndClImm (7) ;
  1358. EmShrAx1 () ; EmShrAx1 () ; EmShrAx1 () ; (* AX := byte index *)
  1359. IF right.kind = 7 THEN
  1360. EmMovBxi (W16 (right.setoff + LoadBias))
  1361. ELSIF right.kind = 1 THEN
  1362. IF symtab [right.idx].local THEN
  1363. EmLeaBx ((right.setoff + right.boff) MOD 10000H)
  1364. ELSE
  1365. EmMovBxi ((right.setoff + right.boff + LoadBias) MOD 10000H)
  1366. END
  1367. ELSE
  1368. Err (ENoLib) ;
  1369. RETURN
  1370. END ;
  1371. EmAddBxAx () ; (* BX := the set's byte *)
  1372. EmLdAlBx () ;
  1373. EmMovDlImm (1) ;
  1374. EmShlDlCl () ; (* DL := 1 << bit *)
  1375. EmAndAlDl () ;
  1376. EmMovAh0 () ;
  1377. EmOrAxAx () ;
  1378. EmRelSetcc (2) ; (* SETNE: the bit was set *)
  1379. jmp := EmJmpNear (0) ;
  1380. SetPatTgt (jl, pc) ;
  1381. SetPatTgt (jg, pc) ;
  1382. EmMovAxi (0) ;
  1383. SetPatTgt (jmp, pc) ;
  1384. res.kind := 2 ;
  1385. res.cls := TBool ;
  1386. RETURN
  1387. END ;
  1388. (* A code-resident set (an inline literal or a typed constant) folds at
  1389. compile time. A DATA set - a record field, local or global, or one
  1390. addressed through DI - must be tested at run time: its byte is in the
  1391. image's data area, not in cbuf. A typed constant's offset is below
  1392. dataBase; a global's is at or above it. *)
  1393. foldable := (right.kind = 7) ;
  1394. IF (right.kind = 1) AND NOT symtab [right.idx].local
  1395. AND (right.setoff + right.boff < dataBase) THEN
  1396. foldable := TRUE
  1397. END ;
  1398. IF NOT foldable THEN
  1399. IF (left.imm >= 0) AND (left.imm <= 255) THEN
  1400. base := right.setbase ; size := right.setsize ;
  1401. rel := VAL (CARDINAL, left.imm) ;
  1402. IF rel >= base THEN
  1403. bidx := (rel - base) DIV 8 ;
  1404. IF bidx < size THEN
  1405. bit := (rel - base) MOD 8 ;
  1406. m := 1 ;
  1407. WHILE bit > 0 DO
  1408. m := m * 2 ; DEC (bit)
  1409. END ;
  1410. IF right.kind = 5 THEN
  1411. EmLdAlDiOff (bidx)
  1412. ELSIF right.kind = 1 THEN
  1413. LoadSymVar (right.idx, (right.setoff + right.boff + bidx) MOD 10000H, 1)
  1414. ELSE
  1415. Err (ENoLib) ;
  1416. RETURN
  1417. END ;
  1418. EmAndAlImm (m) ;
  1419. EmMovAh0 () ;
  1420. EmOrAxAx () ;
  1421. EmRelSetcc (2) ; (* SETNE: the bit was set *)
  1422. res.kind := 2 ;
  1423. res.cls := TBool ;
  1424. RETURN
  1425. END
  1426. END
  1427. END ;
  1428. res.imm := 0 ;
  1429. RETURN
  1430. END ;
  1431. isIn := FALSE ;
  1432. IF (left.imm >= 0) AND (left.imm <= 255) THEN
  1433. off := right.setoff ; base := right.setbase ; size := right.setsize ;
  1434. rel := VAL (CARDINAL, left.imm) ;
  1435. IF rel >= base THEN
  1436. bidx := (rel - base) DIV 8 ;
  1437. IF bidx < size THEN
  1438. bit := (rel - base) MOD 8 ;
  1439. m := 1 ;
  1440. WHILE bit > 0 DO
  1441. m := m * 2 ; DEC (bit)
  1442. END ;
  1443. o := VAL (CARDINAL, cbuf [off + bidx]) ;
  1444. isIn := (o DIV m) MOD 2 # 0
  1445. END
  1446. END
  1447. END ;
  1448. res.imm := ORD (isIn)
  1449. END FoldSetIn ;
  1450. PROCEDURE RdSetBase (VAR baseCls : CARDINAL ; VAR lo, hi : LONGINT) ;
  1451. (* The base type of `set of ...': an ordinal range. `Char' and `byte' are
  1452. 0..255; a literal subrange `a..b' is read directly. A named or enumerated
  1453. base is ENoLib for now - `set of Attributes' waits for enums. *)
  1454. VAR idx, subcls : CARDINAL ; strf : BOOLEAN ;
  1455. BEGIN
  1456. baseCls := TScalar ;
  1457. Skip () ;
  1458. IF Alpha (CurCh ()) THEN
  1459. GetWord () ;
  1460. IF Search (wrd, idx) AND (symtab [idx].tag = KType) THEN
  1461. IF symtab [idx].cls = TEnum THEN
  1462. lo := symtab [idx].lo ;
  1463. hi := symtab [idx].hi ;
  1464. baseCls := TEnum
  1465. ELSIF (symtab [idx].cls = TChar)
  1466. OR ((symtab [idx].cls = TScalar) AND (symtab [idx].size = 1)) THEN
  1467. lo := 0 ; hi := 255 ; baseCls := symtab [idx].cls
  1468. ELSE
  1469. Err (ENoLib)
  1470. END
  1471. ELSE
  1472. Err (EUnknown)
  1473. END
  1474. ELSE
  1475. RdConst (lo, subcls, strf) ;
  1476. IF NOT MatchRange () THEN
  1477. Err (ESimpType) ;
  1478. RETURN
  1479. END ;
  1480. RdConst (hi, subcls, strf) ;
  1481. IF (lo < 0) OR (hi > 255) OR (lo > hi) THEN
  1482. Err (EConstRange)
  1483. END
  1484. END
  1485. END RdSetBase ;
  1486. PROCEDURE NewScratchStr () : CARDINAL ;
  1487. (* Allocate a fresh 256-byte global STRING and return its symbol index.
  1488. A concatenation result needs STORAGE the emitted code can name. Making the
  1489. result a string VARIABLE (kind 1) is what keeps every downstream consumer
  1490. untouched: Assignment, IoCall and ParseCmp already handle kind 1, so `s :=
  1491. a + b', `writeln(a + b)' and `(a + b) = c' work with no code of their own.
  1492. One scratch PER `+' in the source, so a repeated or nested concatenation
  1493. never shares a destination - `a + b + c' is two occurrences and gets two
  1494. scratch strings, and the first result survives until the second consumes it.
  1495. (A loop reuses its site's scratch, which is correct because each execution
  1496. writes before it reads.) Recursion would break that, but this compiler
  1497. compiles no procedures yet - see the item list.
  1498. 256 bytes is the STRING type's own size, length byte plus 255 characters:
  1499. see ParseType's TkString arm and the predefined STRING in Inittur. The name
  1500. is a placeholder (nothing looks a scratch up by name) spelled with '@', as
  1501. tmpA/tmpB are, so it cannot collide with a Modula-2 identifier. *)
  1502. BEGIN
  1503. DropC (NewSym ("@@SC", KVar, TString, 256, 1, dc, 0, FALSE)) ;
  1504. dc := dc + 256 ;
  1505. RETURN symTop - 1
  1506. END NewScratchStr ;
  1507. PROCEDURE NewScratchWord () : CARDINAL ;
  1508. (* Two bytes for a dynamic set literal's element value. *)
  1509. BEGIN
  1510. DropC (NewSym ("@@DW", KVar, TScalar, 2, 2, dc, 0, FALSE)) ;
  1511. dc := dc + 2 ;
  1512. RETURN symTop - 1
  1513. END NewScratchWord ;
  1514. PROCEDURE NewScratchReal () : CARDINAL ;
  1515. (* Six bytes of storage for a REAL function result, one per call site. *)
  1516. BEGIN
  1517. DropC (NewSym ("@@RL", KVar, TReal, 6, 6, dc, 0, FALSE)) ;
  1518. dc := dc + 6 ;
  1519. RETURN symTop - 1
  1520. END NewScratchReal ;
  1521. PROCEDURE NewScratchRealBlock () : CARDINAL ;
  1522. (* Twelve bytes for a binary REAL operation: a followed by b. *)
  1523. BEGIN
  1524. DropC (NewSym ("@@RB", KVar, TReal, 12, 6, dc, 0, FALSE)) ;
  1525. dc := dc + 12 ;
  1526. RETURN symTop - 1
  1527. END NewScratchRealBlock ;
  1528. PROCEDURE StrOperand (r : ERes) : BOOLEAN ;
  1529. (* Is r usable as one operand of `+'? A STRING, or a CHAR - which can appear
  1530. three ways: a quoted literal (cls TScalar with chr set), a CHAR variable
  1531. (cls TChar), and a string element s[i] (cls TChar, kind 5). A number is
  1532. not, and neither is a BOOLEAN. *)
  1533. BEGIN
  1534. RETURN (r.cls = TString) OR (r.cls = TChar) OR
  1535. ((r.cls = TScalar) AND r.chr)
  1536. END StrOperand ;
  1537. PROCEDURE EmPushStrOperand (r : ERes) ;
  1538. (* Push the ADDRESS of one concatenation operand, converting a CHAR to a
  1539. length-1 string first.
  1540. A STRING operand is pushed exactly as cmpstr pushes one (EmPushStrAddr):
  1541. a variable's value already IS its address, and a literal travels inline.
  1542. A CHAR operand has no such address, so it is materialised as a
  1543. `<length 1><character>' string in a fresh 2-byte scratch and the address of
  1544. THAT is pushed. This is TPSRC9's addch/addch2, which splice a character
  1545. into a concatenation by building a one-character string. The character is
  1546. loaded into AL (LoadAtom), the character is stored at [coff+1] and the
  1547. length 1 at [coff]. At most one operand of a concatenation is a CHAR -
  1548. StrConcat rejects char+char - so at most one such scratch is ever made. *)
  1549. VAR coff : CARDINAL ;
  1550. BEGIN
  1551. IF r.cls = TString THEN
  1552. EmPushStrAddr (r)
  1553. ELSE
  1554. coff := dc ;
  1555. dc := dc + 2 ;
  1556. LoadAtom (r) ; (* AX := the character *)
  1557. EmStoreVar (FALSE, (coff + 1) MOD 10000H, 1) ; (* [coff+1] := AL *)
  1558. EmMovAxi (1) ; (* AX := 1 *)
  1559. EmStoreVar (FALSE, coff MOD 10000H, 1) ; (* [coff] := AL = length *)
  1560. EmPushVarAddr (FALSE, coff MOD 10000H)
  1561. END
  1562. END EmPushStrOperand ;
  1563. PROCEDURE StrConcat (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
  1564. (* `left + right' for strings: TPSRC2's xconcat, emitted as two runtime calls.
  1565. The layout is `<length byte><characters>' and `+' needs a place to build its
  1566. result, so this allocates one 256-byte global scratch (NewScratchStr) and
  1567. copies into it: first `dest := left' through the runtime's strstore, then
  1568. `dest := dest + right' through strcat, which appends. Using strstore for
  1569. the left operand and strcat for the right keeps the append logic in one
  1570. place and gives the compiler exactly one new entry to reach; it also means
  1571. the two calls have the same argument shape (source pushed first, destination
  1572. last, limit in CX), so the emission differs only in the entry named.
  1573. The limit is 255 - the scratch's 255 usable characters. An overflowing
  1574. result is a RUNTIME ERROR with exit code 16, TP3's `String too long', not a
  1575. truncation; see Runtime.EmitStrCat.
  1576. The operator rule: `+' concatenates, anything else over a STRING is a type
  1577. error, and char+char is ALSO a type error. ISO Modula-2 gives no meaning to
  1578. two characters summed, and inventing one (a two-character string?) would be
  1579. a guess this compiler does not make; TP3's own behaviour there is not
  1580. something the reference pins down. That char+char choice is the one place
  1581. this differs from a literal reading of the reference, and it is recorded
  1582. here and in v-TP3-STRCAT.md rather than left to be discovered.
  1583. The guard below is what ENFORCES that rule, and it only runs because
  1584. BinOpEmit's hook routes a CHAR operand here too. A hook that tested for a
  1585. STRING operand only would send `'a' + 'b'' to the integer path instead and
  1586. this test would never be reached - which is exactly what it did until
  1587. t53_strcat_type caught it. Three ways to fail it: a non-`+' operator over
  1588. a string operand, an operand that is neither a string nor a CHAR (a number),
  1589. and two CHARs with no STRING on either side. *)
  1590. VAR dest : CARDINAL ;
  1591. BEGIN
  1592. (* A placeholder for the paths below that bail out through Err. Err only
  1593. records the first error and does not unwind, so the caller goes on to use
  1594. res regardless: after `Err (ETypeErr)' in the guard, ParseAdd hands res
  1595. straight back to to Assignment or to an enclosing concatenation.
  1596. The placeholder's SHAPE therefore matters. It must not claim to be a
  1597. STRING, because the string consumers reach symtab[res.idx] for any TString
  1598. (IoCall's `writeln' arm does) and idx is not a symbol here; and it must be
  1599. something LoadAtom can fold, because the numeric consumers will try. The
  1600. integer 0 is both. Leaving kind-2/TString here, as this once did, turned
  1601. `writeln('a' + 'b')' - two CHARs, a type error - into a compiler segfault
  1602. instead of an error message. *)
  1603. res.kind := 0 ;
  1604. res.imm := 0 ;
  1605. res.cls := TScalar ;
  1606. res.boff := 0 ;
  1607. res.chr := FALSE ;
  1608. IF (op # OpAdd) OR NOT StrOperand (left) OR NOT StrOperand (right) OR
  1609. ((left.cls # TString) AND (right.cls # TString)) THEN
  1610. Err (ETypeErr) ; (* a STRING on the wrong operator, a number,
  1611. or two CHARs *)
  1612. RETURN
  1613. END ;
  1614. dest := NewScratchStr () ;
  1615. (* dest := left *)
  1616. EmPushStrOperand (left) ;
  1617. EmPushVarAddr (FALSE, (symtab [dest].off) MOD 10000H) ;
  1618. EmMovCxi (255) ;
  1619. DropC (EmCall (TU_StrStore)) ;
  1620. EmAddSp (4) ; (* pop the two addresses strstore took *)
  1621. (* dest := dest + right *)
  1622. EmPushStrOperand (right) ;
  1623. EmPushVarAddr (FALSE, (symtab [dest].off) MOD 10000H) ;
  1624. EmMovCxi (255) ;
  1625. DropC (EmCall (TU_StrCat)) ;
  1626. EmAddSp (4) ;
  1627. res.kind := 1 ;
  1628. res.idx := dest ;
  1629. res.cls := TString ;
  1630. res.boff := 0 ;
  1631. res.chr := FALSE
  1632. END StrConcat ;
  1633. PROCEDURE ParseCmp (VAR r : ERes) ;
  1634. (* "=" | "<>" | "<" | "<=" | ">" | ">=" *)
  1635. VAR op : CARDINAL ;
  1636. left, right : ERes ;
  1637. f : LONGINT ;
  1638. size, base, block, scratch : CARDINAL ;
  1639. BEGIN
  1640. ParseAdd (r) ;
  1641. LOOP
  1642. op := 0 ;
  1643. Skip () ;
  1644. IF CurCh () = '=' THEN
  1645. op := 1 ; DropCh (GetCh ())
  1646. ELSIF (CurCh () = '<') AND (PeekAhead (1) = '>') THEN
  1647. op := 2 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1648. ELSIF CurCh () = '<' THEN
  1649. IF PeekAhead (1) = '=' THEN
  1650. op := 6 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1651. ELSE
  1652. op := 3 ; DropCh (GetCh ())
  1653. END
  1654. ELSIF CurCh () = '>' THEN
  1655. IF PeekAhead (1) = '=' THEN
  1656. op := 5 ; DropCh (GetCh ()) ; DropCh (GetCh ())
  1657. ELSE
  1658. op := 4 ; DropCh (GetCh ())
  1659. END
  1660. END ;
  1661. IF op = 0 THEN
  1662. IF KwAhead ("IN") THEN
  1663. (* `e IN s' is at the comparison level in TP3 (TPSRC9 cmpin, 116).
  1664. Only the compile-time case is implemented; see FoldSetIn. *)
  1665. GetWord () ;
  1666. left := r ;
  1667. ParseAdd (right) ;
  1668. FoldSetIn (left, right, r) ;
  1669. RETURN
  1670. END ;
  1671. RETURN
  1672. END ;
  1673. left := r ;
  1674. IF left.cls = TSet THEN
  1675. (* Set comparison. set_cmp returns a mask (bit 0 equal, bit 1 strict
  1676. subset, bit 2 strict superset); the operator selects which bits
  1677. answer it, and the shared EmRelSetcc arm turns the flags into a
  1678. boolean. The left address is pushed before the right operand is
  1679. parsed, for the same DI reason as strings and set arithmetic. *)
  1680. EmPushSetAddr (left) ;
  1681. ParseAdd (right) ;
  1682. IF NOT OK () THEN
  1683. RETURN
  1684. END ;
  1685. IF right.cls # TSet THEN
  1686. Err (ETypeErr) ;
  1687. RETURN
  1688. END ;
  1689. IF NOT SetPairSize (left, right, size, base) THEN
  1690. Err (ENoLib) ;
  1691. RETURN
  1692. END ;
  1693. EmPushSetAddr (right) ;
  1694. EmMovAxi (size) ;
  1695. EmPushAx () ;
  1696. DropC (EmCall (TU_SetCmp)) ;
  1697. EmAddSp (4) ;
  1698. CASE op OF
  1699. 1 : EmMovCxi (1) ; EmAndAxCx () ; EmOrAxAx () ; EmRelSetcc (2) ;
  1700. | 2 : EmMovCxi (1) ; EmAndAxCx () ; EmXorAl01 () ;
  1701. EmOrAxAx () ; EmRelSetcc (2) ;
  1702. | 3 : EmMovCxi (2) ; EmAndAxCx () ; EmOrAxAx () ; EmRelSetcc (2) ;
  1703. | 4 : EmMovCxi (4) ; EmAndAxCx () ; EmOrAxAx () ; EmRelSetcc (2) ;
  1704. | 5 : EmMovCxi (5) ; EmAndAxCx () ; EmOrAxAx () ; EmRelSetcc (2) ;
  1705. | 6 : EmMovCxi (3) ; EmAndAxCx () ; EmOrAxAx () ; EmRelSetcc (2)
  1706. END ;
  1707. r.kind := 2 ;
  1708. r.cls := TBool
  1709. ELSIF left.cls = TString THEN
  1710. (* A string comparison: cmpstr, the entry TPSRC9's vcompare (133)
  1711. reaches through TPSRC2's csteq/cstne/cstge/cstle/cstg/cstl
  1712. wrappers (890-925).
  1713. The operands are pushed as ADDRESSES, before the right-hand parse
  1714. can clobber DI. This is why the left is pushed here rather than
  1715. through SaveLeft: a kind-5 left is a string FIELD whose address is
  1716. in DI, and SaveLeft's job is to materialise a scalar in AX, which
  1717. would read the length byte as a number (or, for a whole string,
  1718. refuse it). `if Sheet[UFX,UFY].Contents <> '' then' was exactly
  1719. that: the CALC rung after Write(Value:FW:DEC).
  1720. cmpstr returns AX = -1/0/+1 (see Runtime.mod for why a value and
  1721. not flags), so the second half is exactly the numeric arm's: put
  1722. the sign in the flags and let the shared six-arm table answer.
  1723. EmOrAxAx is OR AX,AX rather than EmCmpAxi(0) on purpose: tests/
  1724. check_8086.py reads every `CMP AX,0000' in the program region as
  1725. an IF/WHILE/REPEAT test and requires a branch right after it, and
  1726. a comparison used as a VALUE (writeln(a = b)) is not one. *)
  1727. EmPushStrAddr (left) ;
  1728. ParseAdd (right) ;
  1729. IF (right.cls = TChar) OR ((right.cls = TScalar) AND right.chr) THEN
  1730. (* `S <> Chr($FF)' / `S <> 'x'': a CHAR is a one-character string.
  1731. Materialise it as a counted scratch so cmpstr sees the same
  1732. layout as every other string operand. *)
  1733. scratch := NewScratchStr () ;
  1734. IF right.kind = 0 THEN
  1735. EmMovAxi (W16 (right.imm)) ;
  1736. EmStoreVar (FALSE, (symtab [scratch].off + 1) MOD 10000H, 1) ;
  1737. EmMovAxi (1) ;
  1738. EmStoreVar (FALSE, symtab [scratch].off, 1)
  1739. ELSE
  1740. LoadAtom (right) ;
  1741. EmStoreVar (FALSE, (symtab [scratch].off + 1) MOD 10000H, 1) ;
  1742. EmMovAxi (1) ;
  1743. EmStoreVar (FALSE, symtab [scratch].off, 1)
  1744. END ;
  1745. right.kind := 1 ; right.idx := scratch ; right.cls := TString ;
  1746. right.sz := 256 ; right.boff := 0
  1747. END ;
  1748. IF right.cls # TString THEN
  1749. Err (ETypeErr) ; (* a string compared with a number *)
  1750. RETURN
  1751. END ;
  1752. EmPushStrAddr (right) ;
  1753. DropC (EmCall (TU_CmpStr)) ;
  1754. EmAddSp (4) ;
  1755. EmOrAxAx () ;
  1756. EmRelSetcc (op) ;
  1757. r.kind := 2 ;
  1758. r.cls := TBool
  1759. ELSIF left.cls = TReal THEN
  1760. (* REAL comparison: recmp takes one 12-byte block holding a then b.
  1761. SaveLeft parks the left address; the right is copied first when it
  1762. is DI-addressed, before the block copy clobbers DI. *)
  1763. SaveLeft (left) ;
  1764. ParseAdd (right) ;
  1765. IF NOT OK () THEN
  1766. RETURN
  1767. END ;
  1768. IF (right.cls = TScalar) AND (right.kind = 0) THEN
  1769. scratch := NewScratchReal () ;
  1770. EmCopyRealTo (symtab [scratch].off, right) ;
  1771. right.idx := scratch ; right.kind := 1 ; right.cls := TReal ;
  1772. right.sz := 6 ; right.boff := 0
  1773. END ;
  1774. IF right.cls # TReal THEN
  1775. Err (ETypeErr) ;
  1776. RETURN
  1777. END ;
  1778. block := NewScratchRealBlock () ;
  1779. IF right.kind = 5 THEN
  1780. EmCopyRealTo (symtab [block].off + 6, right) ;
  1781. EmCopyRealTo (symtab [block].off, left)
  1782. ELSE
  1783. EmCopyRealTo (symtab [block].off, left) ;
  1784. EmCopyRealTo (symtab [block].off + 6, right)
  1785. END ;
  1786. EmPushVarAddr (FALSE, symtab [block].off) ;
  1787. DropC (EmCall (TU_RealCmp)) ;
  1788. EmAddSp (2) ;
  1789. EmOrAxAx () ;
  1790. EmRelSetcc (op) ;
  1791. r.kind := 2 ;
  1792. r.cls := TBool
  1793. ELSE
  1794. SaveLeft (left) ;
  1795. ParseAdd (right) ;
  1796. IF (right.cls = TString) OR (right.cls = TSet) THEN
  1797. Err (ETypeErr) ; (* a number compared with a string/set *)
  1798. RETURN
  1799. END ;
  1800. IF (left.kind = 0) AND (right.kind = 0) THEN
  1801. IF ConstCmp (op, left.imm, right.imm, f) THEN
  1802. r.kind := 0 ;
  1803. r.imm := f ;
  1804. r.cls := TBool
  1805. ELSE
  1806. r.kind := 0 ;
  1807. r.imm := 0 ;
  1808. r.cls := TBool
  1809. END
  1810. ELSE
  1811. LoadPair (left, right) ;
  1812. EmCmpAxCx () ;
  1813. EmRelSetcc (op) ;
  1814. r.kind := 2 ;
  1815. r.cls := TBool
  1816. END
  1817. END
  1818. END
  1819. END ParseCmp ;
  1820. PROCEDURE ParseAdd (VAR r : ERes) ;
  1821. VAR op : CARDINAL ;
  1822. left, right : ERes ;
  1823. BEGIN
  1824. ParseMul (r) ;
  1825. LOOP
  1826. op := 0 ;
  1827. Skip () ;
  1828. IF CurCh () = '+' THEN
  1829. op := OpAdd ; DropCh (GetCh ())
  1830. ELSIF CurCh () = '-' THEN
  1831. op := OpSub ; DropCh (GetCh ())
  1832. ELSIF KwAhead ("OR") THEN
  1833. GetWord () ;
  1834. op := TkOr
  1835. ELSE
  1836. RETURN
  1837. END ;
  1838. left := r ;
  1839. IF left.cls = TSet THEN
  1840. (* Set union/difference. The left address goes on the stack BEFORE
  1841. the right is parsed: a DI-addressed set field must survive the
  1842. right-hand expression. `OR' is boolean, not set union. *)
  1843. IF (op = OpAdd) OR (op = OpSub) THEN
  1844. EmPushSetAddr (left) ;
  1845. ParseMul (right) ;
  1846. IF NOT OK () THEN
  1847. RETURN
  1848. END ;
  1849. IF right.cls # TSet THEN
  1850. Err (ETypeErr) ;
  1851. RETURN
  1852. END ;
  1853. IF right.kind = 9 THEN
  1854. MaterializeDyn (right, left.setbase, left.setsize)
  1855. END ;
  1856. IF op = OpAdd THEN
  1857. SetBinEmit (1, left, right, r)
  1858. ELSE
  1859. SetBinEmit (2, left, right, r)
  1860. END
  1861. ELSE
  1862. Err (ETypeErr) ;
  1863. RETURN
  1864. END
  1865. ELSE
  1866. SaveLeft (left) ;
  1867. ParseMul (right) ;
  1868. IF NOT OK () THEN
  1869. RETURN
  1870. END ;
  1871. IF right.cls = TSet THEN
  1872. Err (ETypeErr) ;
  1873. RETURN
  1874. END ;
  1875. BinOpEmit (op, left, right, r)
  1876. END
  1877. END
  1878. END ParseAdd ;
  1879. PROCEDURE ParseMul (VAR r : ERes) ;
  1880. VAR op : CARDINAL ;
  1881. left, right : ERes ;
  1882. BEGIN
  1883. ParseNeg (r) ;
  1884. LOOP
  1885. op := 0 ;
  1886. Skip () ;
  1887. IF CurCh () = '*' THEN
  1888. op := OpMul ; DropCh (GetCh ()) (* OpAdd here meant a*b -> a+b *)
  1889. ELSIF CurCh () = '/' THEN
  1890. op := OpDiv ; DropCh (GetCh ())
  1891. ELSIF KwAhead ("DIV") THEN
  1892. GetWord () ; op := TkDiv
  1893. ELSIF KwAhead ("MOD") THEN
  1894. GetWord () ; op := TkMod
  1895. ELSIF KwAhead ("AND") THEN
  1896. GetWord () ; op := TkAnd
  1897. ELSIF KwAhead ("SHL") THEN
  1898. GetWord () ; op := OpShl
  1899. ELSIF KwAhead ("SHR") THEN
  1900. GetWord () ; op := OpShr
  1901. ELSE
  1902. RETURN
  1903. END ;
  1904. left := r ;
  1905. IF left.cls = TSet THEN
  1906. (* Set intersection. Same push-before-the-right rule as ParseAdd. *)
  1907. IF op = OpMul THEN
  1908. EmPushSetAddr (left) ;
  1909. ParseNeg (right) ;
  1910. IF NOT OK () THEN
  1911. RETURN
  1912. END ;
  1913. IF right.cls # TSet THEN
  1914. Err (ETypeErr) ;
  1915. RETURN
  1916. END ;
  1917. SetBinEmit (0, left, right, r)
  1918. ELSE
  1919. Err (ETypeErr) ;
  1920. RETURN
  1921. END
  1922. ELSE
  1923. SaveLeft (left) ;
  1924. ParseNeg (right) ;
  1925. IF NOT OK () THEN
  1926. RETURN
  1927. END ;
  1928. IF right.cls = TSet THEN
  1929. Err (ETypeErr) ;
  1930. RETURN
  1931. END ;
  1932. BinOpEmit (op, left, right, r)
  1933. END
  1934. END
  1935. END ParseMul ;
  1936. PROCEDURE ParseNeg (VAR r : ERes) ;
  1937. BEGIN
  1938. Skip () ;
  1939. IF CurCh () = '+' THEN
  1940. DropCh (GetCh ()) ;
  1941. ParseNeg (r) ;
  1942. RETURN
  1943. ELSIF CurCh () = '-' THEN
  1944. DropCh (GetCh ()) ;
  1945. ParseNeg (r) ;
  1946. IF r.cls = TReal THEN
  1947. r.idx := NewScratchReal () ;
  1948. EmCopyRealTo (symtab [r.idx].off, r) ;
  1949. EmXorByteGlob (symtab [r.idx].off + 5, 80H) ;
  1950. r.kind := 1 ;
  1951. r.cls := TReal ;
  1952. r.sz := 6 ;
  1953. r.boff := 0
  1954. ELSIF r.kind = 0 THEN
  1955. r.imm := VAL (LONGINT, W16 (0 - r.imm))
  1956. ELSE
  1957. LoadAtom (r) ;
  1958. EmNegAx () ;
  1959. r.kind := 2
  1960. END ;
  1961. RETURN
  1962. ELSIF KwAhead ("NOT") THEN
  1963. GetWord () ;
  1964. ParseNeg (r) ;
  1965. (* TPSRC9 neglevel branches on the operand's type before emitting
  1966. anything: CMP CL,#$0A (integer) -> NOT AX, CMP CL,#$0B (boolean)
  1967. -> XOR AL,#01, anything else -> error. This used to emit NOT AX
  1968. for both, so `not (a = a)' produced 0FFFEh and the runtime's
  1969. wrbool test ([BP+4] <> 0) read it as TRUE.
  1970. The integer constant fold stays: the 16 bits are the same either
  1971. way. A boolean constant must not go through BitNot - 0FFFFH-1 is
  1972. 0FFFEh, which wrbool also reads as TRUE - so it is loaded and
  1973. xor-ed, exactly as neglevel does it. *)
  1974. IF r.cls = TBool THEN
  1975. LoadAtom (r) ;
  1976. EmXorAl01 () ;
  1977. r.kind := 2
  1978. ELSIF r.cls = TScalar THEN
  1979. IF r.kind = 0 THEN
  1980. r.imm := BitNot (r.imm)
  1981. ELSE
  1982. LoadAtom (r) ;
  1983. EmNotAx () ;
  1984. r.kind := 2
  1985. END
  1986. ELSE
  1987. Err (ETypeErr)
  1988. END ;
  1989. RETURN
  1990. END ;
  1991. ParseAtom (r)
  1992. END ParseNeg ;
  1993. PROCEDURE WithLookup (nm : ARRAY OF CHAR ; VAR r : ERes) : BOOLEAN ;
  1994. (* Resolve a bare identifier against the active WITH scopes, innermost first.
  1995. ISO Pascal lets a record field shadow an outer declaration, so this is
  1996. consulted BEFORE Search. The result is filled exactly as a variable
  1997. reference: kind 1 (base symbol + boff) for a record variable or a
  1998. constant-index element, kind 5 (address in DI) for a run-time-indexed one,
  1999. where the base is reloaded from the WITH slot because the body may have
  2000. used DI. *)
  2001. VAR w, fi : CARDINAL ;
  2002. found : BOOLEAN ;
  2003. BEGIN
  2004. w := withN ;
  2005. WHILE w > 0 DO
  2006. DEC (w) ;
  2007. fi := withFF [w] ;
  2008. found := FALSE ;
  2009. WHILE (fi < withFL [w]) AND NOT found DO
  2010. IF NameEq (fields [fi].name, nm) THEN
  2011. found := TRUE
  2012. ELSE
  2013. INC (fi)
  2014. END
  2015. END ;
  2016. IF found THEN
  2017. IF withIsDI [w] THEN
  2018. EmLdDiGlob (withSlot [w]) ;
  2019. IF fields [fi].off # 0 THEN
  2020. EmAddDiImm (fields [fi].off)
  2021. END ;
  2022. r.kind := 5
  2023. ELSE
  2024. r.kind := 1 ;
  2025. r.setoff := symtab [withIdx [w]].off ;
  2026. r.boff := W16 (VAL (LONGINT, withBoff [w])
  2027. + VAL (LONGINT, fields [fi].off))
  2028. END ;
  2029. r.idx := withIdx [w] ;
  2030. r.cls := fields [fi].cls ;
  2031. r.sz := fields [fi].size ;
  2032. r.fldFirst := fields [fi].fldFirst ;
  2033. r.fldLast := fields [fi].fldLast ;
  2034. r.arrIdx := fields [fi].arrIdx ;
  2035. r.ptrElem := fields [fi].ptrElem ;
  2036. r.hasBounds := ((fields [fi].cls = TScalar) OR (fields [fi].cls = TEnum)
  2037. OR (fields [fi].cls = TChar)) AND
  2038. (fields [fi].lo # fields [fi].hi) ;
  2039. r.blo := fields [fi].lo ; r.bhi := fields [fi].hi ;
  2040. IF r.cls = TSet THEN
  2041. r.setbase := fields [fi].elem ;
  2042. r.setsize := fields [fi].size
  2043. END ;
  2044. r.chr := FALSE ;
  2045. RETURN TRUE
  2046. END
  2047. END ;
  2048. RETURN FALSE
  2049. END WithLookup ;
  2050. PROCEDURE ParseAtom (VAR r : ERes) ;
  2051. (* const | variable | func(params) | '(' expr ')' | '[' set ']' *)
  2052. VAR idx : CARDINAL ;
  2053. strf : BOOLEAN ;
  2054. quoted : BOOLEAN ;
  2055. ssize, sbase : CARDINAL ;
  2056. sbuf : ARRAY [0..31] OF CHAR ;
  2057. uj1, uj2 : CARDINAL ;
  2058. litStart, scratch, asciiAddr, savePos : CARDINAL ;
  2059. dyn, acc, conArg : ERes ;
  2060. BEGIN
  2061. r.chr := FALSE ; (* default: not a quoted char literal *)
  2062. Skip () ;
  2063. IF CurCh () = '(' THEN
  2064. DropCh (GetCh ()) ;
  2065. ParseExpr (r) ;
  2066. ExpectDelim (')', ENoSemi) ;
  2067. RETURN
  2068. END ;
  2069. IF CurCh () = '[' THEN
  2070. (* An inline set literal. A literal whose first element is a variable
  2071. (e.g. `[Storage[Index]]') is dynamic: evaluate the element once and
  2072. mark it kind 9, to be materialised when the target's base/size is
  2073. known. Constant literals stay code-resident. *)
  2074. savePos := srcPos ;
  2075. DropCh (GetCh ()) ;
  2076. Skip () ;
  2077. IF Alpha (CurCh ()) THEN
  2078. GetWord () ;
  2079. IF Search (wrd, idx) AND ((symtab [idx].tag = KVar) OR
  2080. (symtab [idx].tag = KFunc)) THEN
  2081. srcPos := savePos + 1 ;
  2082. ParseExpr (dyn) ;
  2083. ExpectDelim (']', ENoSemi) ;
  2084. IF NOT OK () THEN
  2085. RETURN
  2086. END ;
  2087. IF (dyn.cls # TScalar) AND (dyn.cls # TChar) AND
  2088. (dyn.cls # TEnum) THEN
  2089. Err (ETypeErr) ;
  2090. RETURN
  2091. END ;
  2092. scratch := NewScratchWord () ;
  2093. LoadAtom (dyn) ;
  2094. EmStoreVar (FALSE, symtab [scratch].off, 2) ;
  2095. r.kind := 9 ;
  2096. r.cls := TSet ;
  2097. r.setbase := 0 ;
  2098. r.setsize := 0 ;
  2099. r.dynElem := scratch ;
  2100. RETURN
  2101. END
  2102. END ;
  2103. srcPos := savePos ;
  2104. ssize := 32 ;
  2105. sbase := 0 ;
  2106. IF RdSetLiteral (sbase, ssize, sbuf) THEN
  2107. r.kind := 7 ;
  2108. r.cls := TSet ;
  2109. r.setoff := EmSetBlob (sbuf, ssize) ;
  2110. r.setbase := sbase ;
  2111. r.setsize := ssize
  2112. END ;
  2113. RETURN
  2114. END ;
  2115. IF (CurCh () = '$') OR (Digit (CurCh ())) OR (ORD (CurCh ()) = AposC)
  2116. OR (CurCh () = '^') OR (CurCh () = '#') THEN
  2117. (* remember that this was a quoted literal BEFORE RdConst consumes it:
  2118. RdConst reports a 1-character literal as TScalar (its char code),
  2119. which is right for "c := 'a'" but would make writeln('a') print 97.
  2120. Mark it so the writer picks the char entry, not the integer one. A
  2121. caret (^M) or decimal (#27) character constant is a character too. *)
  2122. quoted := (ORD (CurCh ()) = AposC) OR (CurCh () = '^')
  2123. OR (CurCh () = '#') ;
  2124. litStart := srcPos ;
  2125. RdConst (r.imm, r.cls, strf) ;
  2126. r.chr := quoted AND (r.cls = TScalar) AND NOT strf ;
  2127. IF r.cls = TReal THEN
  2128. (* A REAL literal is converted at run time by ascreal. Keep the
  2129. literal text as an ASCIIZ code blob and call real_val into a
  2130. six-byte scratch; the expression then behaves like a REAL
  2131. variable whose address is the scratch. *)
  2132. scratch := NewScratchReal () ;
  2133. asciiAddr := EmAsciiZLiteral (litStart, srcPos) ;
  2134. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  2135. EmPushVarAddr (FALSE, asciiAddr) ;
  2136. DropC (EmCall (TU_RealVal)) ;
  2137. EmAddSp (4) ;
  2138. EmLeaDi (FALSE, symtab [scratch].off) ;
  2139. r.idx := scratch ;
  2140. r.kind := 5 ;
  2141. r.cls := TReal ;
  2142. r.sz := 6 ;
  2143. RETURN
  2144. END ;
  2145. IF strf THEN
  2146. (* A string literal is legal here as a *value* - it is not rejected
  2147. at this point, because writeln('hi') needs it and IoCall is the
  2148. only place that knows how to emit one. Everywhere else the
  2149. literal has to end up as a machine word, and that is caught by
  2150. LoadAtom, which refuses a TString. *)
  2151. r.strx := rdStrX ;
  2152. r.kind := 3 ;
  2153. RETURN
  2154. END ;
  2155. r.kind := 0 ;
  2156. RETURN
  2157. END ;
  2158. IF NOT Alpha (CurCh ()) THEN
  2159. Err (EUnknown) ;
  2160. RETURN
  2161. END ;
  2162. GetWord () ;
  2163. IF WithLookup (wrd, r) THEN
  2164. ParseSub (r) ;
  2165. RETURN
  2166. END ;
  2167. IF NameEq (wrd, "NIL") THEN
  2168. r.kind := 0 ;
  2169. r.cls := TPtr ;
  2170. r.imm := 0 ;
  2171. r.sz := 2 ;
  2172. RETURN
  2173. END ;
  2174. IF NameEq (wrd, "ADDR") THEN
  2175. (* Addr(x): the run-time address of x, as a pointer value in AX. TP3
  2176. spells this Addr; the @ operator is a later Turbo spelling and is not
  2177. part of the TP3 subset yet. *)
  2178. IF NOT MatchDelim ('(') THEN
  2179. Err (ENoSemi) ;
  2180. RETURN
  2181. END ;
  2182. Skip () ;
  2183. IF NOT Alpha (CurCh ()) THEN
  2184. Err (EUnknown) ;
  2185. RETURN
  2186. END ;
  2187. GetWord () ;
  2188. ParseVar (r) ;
  2189. ExpectDelim (')', ENoSemi) ;
  2190. IF NOT OK () THEN
  2191. RETURN
  2192. END ;
  2193. IF r.kind = 5 THEN
  2194. EmMovAxDi ()
  2195. ELSIF r.kind = 1 THEN
  2196. IF symtab [r.idx].isVar THEN
  2197. LoadSymVar (r.idx, symtab [r.idx].off, 2)
  2198. ELSE
  2199. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  2200. EmPopAx ()
  2201. END
  2202. ELSE
  2203. Err (ETypeErr) ;
  2204. RETURN
  2205. END ;
  2206. r.kind := 2 ;
  2207. r.cls := TPtr ;
  2208. r.sz := 2 ;
  2209. r.idx := 0 ;
  2210. RETURN
  2211. END ;
  2212. IF NameEq (wrd, "SEG") THEN
  2213. (* TP3 Seg(x): the current DS. The argument is parsed for its type
  2214. and side effects; the .COM has one segment. *)
  2215. IF NOT MatchDelim ('(') THEN
  2216. Err (ENoSemi) ;
  2217. RETURN
  2218. END ;
  2219. ParseExpr (r) ;
  2220. ExpectDelim (')', ENoSemi) ;
  2221. IF NOT OK () THEN
  2222. RETURN
  2223. END ;
  2224. EmMovAxDs () ; (* MOV AX,DS *)
  2225. r.kind := 2 ; r.cls := TScalar ; r.sz := 2 ;
  2226. RETURN
  2227. END ;
  2228. IF NameEq (wrd, "OFS") THEN
  2229. (* TP3 Ofs(x): the run-time address of x. *)
  2230. IF NOT MatchDelim ('(') THEN
  2231. Err (ENoSemi) ;
  2232. RETURN
  2233. END ;
  2234. Skip () ;
  2235. IF NOT Alpha (CurCh ()) THEN
  2236. Err (EUnknown) ;
  2237. RETURN
  2238. END ;
  2239. GetWord () ;
  2240. ParseVar (r) ;
  2241. ExpectDelim (')', ENoSemi) ;
  2242. IF NOT OK () THEN
  2243. RETURN
  2244. END ;
  2245. IF r.kind = 5 THEN
  2246. EmMovAxDi ()
  2247. ELSIF r.kind = 1 THEN
  2248. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  2249. EmPopAx ()
  2250. ELSE
  2251. Err (ETypeErr) ;
  2252. RETURN
  2253. END ;
  2254. r.kind := 2 ; r.cls := TScalar ; r.sz := 2 ;
  2255. RETURN
  2256. END ;
  2257. IF NOT Search (wrd, idx) THEN
  2258. Err (EUnknown) ;
  2259. RETURN
  2260. END ;
  2261. IF (symtab [idx].tag = KVar) AND (symtab [idx].resvar # 0) THEN
  2262. (* A function's result variable shadows its own name inside the body;
  2263. a following '(' means this is a recursive call, not a read of the
  2264. result slot. *)
  2265. Skip () ;
  2266. IF CurCh () = '(' THEN
  2267. idx := symtab [idx].resvar
  2268. END
  2269. END ;
  2270. IF symtab [idx].tag = KConst THEN
  2271. r.kind := 0 ;
  2272. r.imm := symtab [idx].lval ;
  2273. r.cls := symtab [idx].cls ;
  2274. RETURN
  2275. ELSIF symtab [idx].tag = KFunc THEN
  2276. ParseCall (idx) ;
  2277. IF symtab [idx].cls = TReal THEN
  2278. (* A REAL function returns into the caller's six-byte scratch; DI is
  2279. reloaded with its address so real consumers can use the value. *)
  2280. EmLeaDi (FALSE, symtab [callScratch].off) ;
  2281. r.kind := 5 ;
  2282. r.cls := TReal ;
  2283. r.sz := 6
  2284. ELSE
  2285. r.kind := 2 ;
  2286. r.cls := symtab [idx].cls
  2287. END ;
  2288. RETURN
  2289. ELSIF (symtab [idx].tag = KType) AND (symtab [idx].cls = TChar)
  2290. AND (CurCh () = '(') THEN
  2291. (* TP3 also spells the character cast `Char(n)', not only `Chr(n)'. *)
  2292. DropCh (GetCh ()) ;
  2293. ParseExpr (r) ;
  2294. ExpectDelim (')', ENoSemi) ;
  2295. IF NOT OK () THEN
  2296. RETURN
  2297. END ;
  2298. IF (r.cls # TScalar) AND (r.cls # TChar) THEN
  2299. Err (ETypeErr) ;
  2300. RETURN
  2301. END ;
  2302. IF r.kind = 0 THEN
  2303. r.imm := VAL (LONGINT, W16 (r.imm) MOD 256) ;
  2304. r.chr := TRUE
  2305. ELSE
  2306. LoadAtom (r) ;
  2307. r.kind := 2 ;
  2308. r.chr := FALSE
  2309. END ;
  2310. r.cls := TChar ;
  2311. r.sz := 1 ;
  2312. RETURN
  2313. ELSIF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KType) THEN
  2314. (* ParseVar initialises boff to 0 and then ParseSub folds any constant
  2315. subscript into it. Do NOT reset boff here: doing so after ParseVar
  2316. returns threw away the subscript, so s[i] read/wrote off+0 (the
  2317. length byte) while still reporting itself as a char. This was
  2318. latent for arrays because an array cannot be declared yet, so no
  2319. fixture had ever exercised the fold. *)
  2320. ParseVar (r) ;
  2321. RETURN
  2322. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Chr) THEN
  2323. (* Chr(n): the character with code n. A constant folds; otherwise the
  2324. integer is loaded and its class becomes CHAR. The value is one byte,
  2325. so an assignment stores AL and a CHAR writer uses AL. *)
  2326. IF NOT MatchDelim ('(') THEN
  2327. Err (ENoSemi) ;
  2328. RETURN
  2329. END ;
  2330. ParseExpr (r) ;
  2331. ExpectDelim (')', ENoSemi) ;
  2332. IF NOT OK () THEN
  2333. RETURN
  2334. END ;
  2335. IF (r.cls # TScalar) AND (r.cls # TChar) THEN
  2336. Err (ETypeErr) ;
  2337. RETURN
  2338. END ;
  2339. IF r.kind = 0 THEN
  2340. r.imm := VAL (LONGINT, W16 (r.imm) MOD 256) ;
  2341. r.chr := TRUE
  2342. ELSE
  2343. LoadAtom (r) ;
  2344. r.kind := 2 ;
  2345. r.chr := FALSE
  2346. END ;
  2347. r.cls := TChar ;
  2348. r.sz := 1 ;
  2349. RETURN
  2350. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Eof) THEN
  2351. (* Eof(F): text/typed-file end of file. The runtime compares the DOS
  2352. current position with the file size and also remembers a ^Z seen by
  2353. the text reader. *)
  2354. IF NOT MatchDelim ('(') THEN
  2355. Err (ENoSemi) ;
  2356. RETURN
  2357. END ;
  2358. ParseExpr (r) ;
  2359. IF OK () THEN
  2360. IF (r.kind = 1) AND
  2361. ((r.cls = TText) OR (r.cls = TFile)) THEN
  2362. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  2363. DropC (EmCall (TU_FileEof)) ;
  2364. EmAddSp (2) ;
  2365. r.kind := 2 ;
  2366. r.cls := TBool ;
  2367. r.chr := FALSE
  2368. ELSE
  2369. Err (ETypeErr)
  2370. END
  2371. END ;
  2372. ExpectDelim (')', ENoSemi) ;
  2373. RETURN
  2374. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_IoResult) THEN
  2375. (* IOResult: the last file operation's DOS status; reading it clears the
  2376. slot in the runtime. It is a no-argument function. *)
  2377. IF MatchDelim ('(') THEN
  2378. ExpectDelim (')', ENoSemi)
  2379. END ;
  2380. DropC (EmCall (TU_IoResult)) ;
  2381. r.kind := 2 ;
  2382. r.cls := TScalar ;
  2383. r.chr := FALSE ;
  2384. RETURN
  2385. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_UpCase) THEN
  2386. (* UpCase(c): a CHAR function. A constant folds; otherwise AL is
  2387. mapped 'a'..'z' -> 'A'..'Z' and left alone outside that range. The
  2388. test is JL/JG (signed), which is the same answer for 0..127 chars and
  2389. leaves 128..255 unchanged; it also stays inside the four branch
  2390. conditions this compiler declares (check_8086's set). *)
  2391. IF NOT MatchDelim ('(') THEN
  2392. Err (ENoSemi) ;
  2393. RETURN
  2394. END ;
  2395. ParseExpr (r) ;
  2396. ExpectDelim (')', ENoSemi) ;
  2397. IF NOT OK () THEN
  2398. RETURN
  2399. END ;
  2400. IF (r.cls # TChar) AND (r.cls # TScalar) THEN
  2401. Err (ETypeErr) ; (* the STRING form is not implemented *)
  2402. RETURN
  2403. END ;
  2404. IF r.kind = 0 THEN
  2405. IF (r.imm >= VAL (LONGINT, ORD ('a')))
  2406. AND (r.imm <= VAL (LONGINT, ORD ('z'))) THEN
  2407. r.imm := r.imm - 32
  2408. END ;
  2409. r.kind := 0 ;
  2410. r.cls := TChar ;
  2411. r.chr := TRUE ;
  2412. RETURN
  2413. END ;
  2414. LoadAtom (r) ;
  2415. EmCmpAlImm (ORD ('a')) ;
  2416. uj1 := EmJcc (8CH, 0) ; (* JL -> leave it *)
  2417. EmCmpAlImm (ORD ('z')) ;
  2418. uj2 := EmJcc (8FH, 0) ; (* JG -> leave it *)
  2419. EmSubAlImm (32) ;
  2420. SetPatTgt (uj1, pc) ;
  2421. SetPatTgt (uj2, pc) ;
  2422. EmMovAh0 () ;
  2423. r.kind := 2 ;
  2424. r.cls := TChar ;
  2425. r.chr := FALSE ;
  2426. RETURN
  2427. ELSIF (symtab [idx].tag = KBuiltin) AND
  2428. ((symtab [idx].cls = BI_Succ) OR (symtab [idx].cls = BI_Pred)) THEN
  2429. (* Succ(x) / Pred(x) on an ordinal. CALC's `FX := Succ(FX)' is the
  2430. first use. A constant folds; otherwise load the value and INC/DEC
  2431. it, preserving the operand's class so an assignment back to a CHAR
  2432. subrange stores one byte as before. *)
  2433. IF NOT MatchDelim ('(') THEN
  2434. Err (ENoSemi) ;
  2435. RETURN
  2436. END ;
  2437. ParseExpr (r) ;
  2438. ExpectDelim (')', ENoSemi) ;
  2439. IF NOT OK () THEN
  2440. RETURN
  2441. END ;
  2442. IF (r.cls # TChar) AND (r.cls # TScalar) AND (r.cls # TEnum) THEN
  2443. Err (ETypeErr) ;
  2444. RETURN
  2445. END ;
  2446. IF r.kind = 0 THEN
  2447. IF symtab [idx].cls = BI_Succ THEN
  2448. r.imm := VAL (LONGINT, W16 (r.imm + 1))
  2449. ELSE
  2450. r.imm := VAL (LONGINT, W16 (r.imm - 1))
  2451. END
  2452. ELSE
  2453. LoadAtom (r) ;
  2454. IF symtab [idx].cls = BI_Succ THEN
  2455. EmIncAx ()
  2456. ELSE
  2457. EmDecAx ()
  2458. END ;
  2459. r.kind := 2
  2460. END ;
  2461. r.chr := FALSE ;
  2462. RETURN
  2463. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_KeyPressed) THEN
  2464. (* KeyPressed: a boolean FUNCTION with no argument, and TP3 lets it be
  2465. written without a parameter list (`until KeyPressed'). *)
  2466. EmCrtSeq (CrtKey) ;
  2467. r.kind := 2 ;
  2468. r.cls := TBool ;
  2469. r.chr := FALSE ;
  2470. RETURN
  2471. ELSIF (symtab [idx].tag = KBuiltin) AND
  2472. ((symtab [idx].cls = BI_Abs) OR (symtab [idx].cls = BI_Sqrt)
  2473. OR (symtab [idx].cls = BI_Ln) OR (symtab [idx].cls = BI_Exp)
  2474. OR (symtab [idx].cls = BI_Sin) OR (symtab [idx].cls = BI_Cos)
  2475. OR (symtab [idx].cls = BI_Arctan) OR (symtab [idx].cls = BI_Int)) THEN
  2476. IF NOT MatchDelim ('(') THEN
  2477. Err (ENoSemi) ;
  2478. RETURN
  2479. END ;
  2480. ParseExpr (r) ;
  2481. ExpectDelim (')', ENoSemi) ;
  2482. IF NOT OK () THEN
  2483. RETURN
  2484. END ;
  2485. IF (r.cls = TScalar) AND (r.kind = 0) THEN
  2486. scratch := NewScratchReal () ;
  2487. EmCopyRealTo (symtab [scratch].off, r) ;
  2488. r.idx := scratch ; r.kind := 1 ; r.cls := TReal ;
  2489. r.sz := 6 ; r.boff := 0
  2490. END ;
  2491. IF r.cls # TReal THEN
  2492. Err (ETypeErr) ;
  2493. RETURN
  2494. END ;
  2495. IF symtab [idx].cls = BI_Abs THEN
  2496. RealUnary (TU_RealAbs, r)
  2497. ELSIF symtab [idx].cls = BI_Sqrt THEN
  2498. RealUnary (TU_RealSqrt, r)
  2499. ELSIF symtab [idx].cls = BI_Ln THEN
  2500. RealUnary (TU_RealLn, r)
  2501. ELSIF symtab [idx].cls = BI_Exp THEN
  2502. RealUnary (TU_RealExp, r)
  2503. ELSIF symtab [idx].cls = BI_Sin THEN
  2504. RealUnary (TU_RealSin, r)
  2505. ELSIF symtab [idx].cls = BI_Cos THEN
  2506. RealUnary (TU_RealCos, r)
  2507. ELSIF symtab [idx].cls = BI_Arctan THEN
  2508. RealUnary (TU_RealArctan, r)
  2509. ELSE
  2510. RealUnary (TU_RealIntFunc, r)
  2511. END ;
  2512. RETURN
  2513. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Sqr) THEN
  2514. IF NOT MatchDelim ('(') THEN
  2515. Err (ENoSemi) ;
  2516. RETURN
  2517. END ;
  2518. ParseExpr (r) ;
  2519. ExpectDelim (')', ENoSemi) ;
  2520. IF NOT OK () THEN
  2521. RETURN
  2522. END ;
  2523. IF r.cls # TReal THEN
  2524. Err (ETypeErr) ;
  2525. RETURN
  2526. END ;
  2527. RealBin (OpMul, r, r, r) ;
  2528. RETURN
  2529. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_SizeOf) THEN
  2530. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  2531. savePos := srcPos ;
  2532. Skip () ;
  2533. IF Alpha (CurCh ()) THEN
  2534. GetWord () ;
  2535. IF Search (wrd, idx) AND (symtab [idx].tag = KType) THEN
  2536. r.kind := 0 ; r.cls := TScalar ; r.imm := symtab [idx].size ;
  2537. ExpectDelim (')', ENoSemi) ;
  2538. RETURN
  2539. END
  2540. END ;
  2541. srcPos := savePos ;
  2542. ParseExpr (r) ;
  2543. IF OK () THEN
  2544. r.kind := 0 ; r.cls := TScalar ; r.imm := VarSize (r)
  2545. END ;
  2546. ExpectDelim (')', ENoSemi) ;
  2547. RETURN
  2548. ELSIF (symtab [idx].tag = KBuiltin) AND
  2549. ((symtab [idx].cls = BI_FilePos) OR (symtab [idx].cls = BI_FileSize)) THEN
  2550. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  2551. ParseExpr (r) ;
  2552. ExpectDelim (')', ENoSemi) ;
  2553. IF NOT OK () OR (r.kind # 1) OR
  2554. ((r.cls # TFile) AND (r.cls # TText) AND (r.cls # TUntyp)) THEN
  2555. Err (ETypeErr) ; RETURN
  2556. END ;
  2557. EmMovAxi (symtab [r.idx].elem) ; EmPushAx () ;
  2558. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H) ;
  2559. IF symtab [idx].cls = BI_FilePos THEN
  2560. DropC (EmCall (TU_FilePos))
  2561. ELSE
  2562. DropC (EmCall (TU_FileSize))
  2563. END ;
  2564. EmAddSp (4) ;
  2565. r.kind := 2 ; r.cls := TScalar ; r.sz := 2 ;
  2566. RETURN
  2567. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Random) THEN
  2568. IF MatchDelim ('(') THEN
  2569. ParseExpr (r) ;
  2570. ExpectDelim (')', ENoSemi) ;
  2571. IF NOT OK () THEN RETURN END ;
  2572. IF (r.cls # TScalar) AND (r.cls # TChar) THEN
  2573. Err (ETypeErr) ; RETURN
  2574. END ;
  2575. LoadAtom (r) ; EmPushAx ()
  2576. ELSE
  2577. EmMovAxi (0) ; EmPushAx ()
  2578. END ;
  2579. DropC (EmCall (TU_Random)) ; EmAddSp (2) ;
  2580. r.kind := 2 ; r.cls := TScalar ; r.sz := 2 ;
  2581. RETURN
  2582. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Pos) THEN
  2583. IF NOT MatchDelim ('(') THEN
  2584. Err (ENoSemi) ;
  2585. RETURN
  2586. END ;
  2587. ParseExpr (r) ;
  2588. IF OK () AND (r.cls = TString) THEN
  2589. EmPushStrAddr (r) ;
  2590. ExpectDelim (',', ENoSemi) ;
  2591. ParseExpr (r) ;
  2592. IF OK () AND (r.cls = TString) THEN
  2593. EmPushStrAddr (r) ;
  2594. DropC (EmCall (TU_StrPos)) ;
  2595. EmAddSp (4) ;
  2596. r.kind := 2 ;
  2597. r.cls := TScalar ;
  2598. r.sz := 2
  2599. ELSIF OK () THEN
  2600. Err (ETypeErr)
  2601. END
  2602. ELSIF OK () THEN
  2603. Err (ETypeErr)
  2604. END ;
  2605. ExpectDelim (')', ENoSemi) ;
  2606. RETURN
  2607. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Concat) THEN
  2608. IF NOT MatchDelim ('(') THEN
  2609. Err (ENoSemi) ;
  2610. RETURN
  2611. END ;
  2612. IF CurCh () = ')' THEN
  2613. scratch := NewScratchStr () ;
  2614. EmStoreVar (FALSE, symtab [scratch].off, 1) ;
  2615. r.kind := 1 ; r.idx := scratch ; r.cls := TString ; r.sz := 256 ;
  2616. ExpectDelim (')', ENoSemi) ;
  2617. RETURN
  2618. END ;
  2619. ParseExpr (r) ;
  2620. IF OK () AND NOT StrOperand (r) THEN
  2621. Err (ETypeErr) ;
  2622. RETURN
  2623. END ;
  2624. IF OK () AND MatchDelim (',') THEN
  2625. LOOP
  2626. ParseExpr (conArg) ;
  2627. IF NOT OK () THEN
  2628. RETURN
  2629. END ;
  2630. IF NOT StrOperand (conArg) THEN
  2631. Err (ETypeErr) ;
  2632. RETURN
  2633. END ;
  2634. StrConcat (OpAdd, r, conArg, acc) ;
  2635. r := acc ;
  2636. IF NOT MatchDelim (',') THEN
  2637. EXIT
  2638. END
  2639. END
  2640. END ;
  2641. ExpectDelim (')', ENoSemi) ;
  2642. RETURN
  2643. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Copy) THEN
  2644. (* Copy(S, start, len): a counted-string result in a fresh 256-byte
  2645. scratch. S's address is parked while start/len are parsed, then the
  2646. four words are re-pushed in the runtime's [source,dest,start,len]
  2647. order. *)
  2648. IF NOT MatchDelim ('(') THEN
  2649. Err (ENoSemi) ;
  2650. RETURN
  2651. END ;
  2652. ParseExpr (r) ;
  2653. IF OK () AND (r.cls = TString) AND
  2654. ((r.kind = 1) OR (r.kind = 3) OR (r.kind = 5)) THEN
  2655. EmPushStrAddr (r) ; (* park S *)
  2656. ExpectDelim (',', ENoSemi) ;
  2657. ParseExpr (r) ;
  2658. IF OK () AND ((r.cls = TScalar) OR (r.cls = TChar)) THEN
  2659. LoadAtom (r) ; EmPushAx () ;
  2660. ExpectDelim (',', ENoSemi) ;
  2661. ParseExpr (r) ;
  2662. IF OK () AND ((r.cls = TScalar) OR (r.cls = TChar)) THEN
  2663. LoadAtom (r) ; EmPushAx () ;
  2664. scratch := NewScratchStr () ;
  2665. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  2666. EmPopAx () ; (* dest *)
  2667. EmPopBx () ; (* len *)
  2668. EmPopCx () ; (* start *)
  2669. EmPopDx () ; (* source *)
  2670. EmPushBx () ; EmPushCx () ; EmPushAx () ; EmPushDx () ;
  2671. DropC (EmCall (TU_StrCopy)) ;
  2672. EmAddSp (8) ;
  2673. r.kind := 1 ; r.idx := scratch ; r.cls := TString ;
  2674. r.sz := 256 ; r.boff := 0 ; r.chr := FALSE
  2675. ELSIF OK () THEN
  2676. Err (ETypeErr)
  2677. END
  2678. ELSIF OK () THEN
  2679. Err (ETypeErr)
  2680. END ;
  2681. ExpectDelim (')', ENoSemi)
  2682. ELSIF OK () THEN
  2683. Err (ETypeErr)
  2684. END ;
  2685. RETURN
  2686. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Ord) THEN
  2687. IF NOT MatchDelim ('(') THEN
  2688. Err (ENoSemi) ;
  2689. RETURN
  2690. END ;
  2691. ParseExpr (r) ;
  2692. ExpectDelim (')', ENoSemi) ;
  2693. IF NOT OK () THEN
  2694. RETURN
  2695. END ;
  2696. IF (r.cls # TChar) AND (r.cls # TEnum) AND (r.cls # TScalar)
  2697. AND (r.cls # TBool) THEN
  2698. Err (ETypeErr) ;
  2699. RETURN
  2700. END ;
  2701. LoadAtom (r) ;
  2702. r.kind := 2 ;
  2703. r.cls := TScalar ;
  2704. r.chr := FALSE ;
  2705. RETURN
  2706. ELSIF (symtab [idx].tag = KBuiltin) AND
  2707. ((symtab [idx].cls = BI_Round) OR (symtab [idx].cls = BI_Trunc)) THEN
  2708. IF NOT MatchDelim ('(') THEN
  2709. Err (ENoSemi) ;
  2710. RETURN
  2711. END ;
  2712. ParseExpr (r) ;
  2713. ExpectDelim (')', ENoSemi) ;
  2714. IF NOT OK () THEN
  2715. RETURN
  2716. END ;
  2717. IF r.cls = TReal THEN
  2718. IF r.kind = 5 THEN
  2719. EmPushDi ()
  2720. ELSIF r.kind = 1 THEN
  2721. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  2722. ELSE
  2723. Err (ETypeErr) ;
  2724. RETURN
  2725. END ;
  2726. IF symtab [idx].cls = BI_Round THEN
  2727. EmMovAxi (1)
  2728. ELSE
  2729. EmMovAxi (0)
  2730. END ;
  2731. EmPushAx () ;
  2732. DropC (EmCall (TU_RealToInt)) ;
  2733. EmAddSp (4) ;
  2734. r.kind := 2 ;
  2735. r.cls := TScalar ;
  2736. r.sz := 2 ;
  2737. RETURN
  2738. ELSIF (r.cls = TScalar) OR (r.cls = TChar) OR (r.cls = TEnum) THEN
  2739. LoadAtom (r) ;
  2740. r.kind := 2 ;
  2741. r.cls := TScalar ;
  2742. r.sz := 2 ;
  2743. RETURN
  2744. ELSE
  2745. Err (ETypeErr) ;
  2746. RETURN
  2747. END ;
  2748. ELSIF (symtab [idx].tag = KBuiltin) AND (symtab [idx].cls = BI_Length) THEN
  2749. (* LENGTH (s): the string's length byte, loaded inline. No runtime
  2750. entry, and no address is pushed - a string's value already carries its
  2751. length at [addr]. A literal is emitted inline (EmStrBlob) and the
  2752. byte is read from that image offset. *)
  2753. IF NOT MatchDelim ('(') THEN
  2754. Err (ENoSemi) ;
  2755. RETURN
  2756. END ;
  2757. ParseExpr (r) ;
  2758. ExpectDelim (')', ENoSemi) ;
  2759. IF NOT OK () THEN
  2760. RETURN
  2761. END ;
  2762. IF r.cls # TString THEN
  2763. Err (ETypeErr) ;
  2764. RETURN
  2765. END ;
  2766. IF r.kind = 5 THEN
  2767. (* A string field/element addressed by DI (e.g. a typed-constant
  2768. array element): its length byte is at [DI]. *)
  2769. EmLdAlDi () ; EmMovAh0 ()
  2770. ELSIF (r.kind = 1) AND symtab [r.idx].isVar THEN
  2771. (* VAR string: the slot holds the address; read the length byte
  2772. through that pointer. *)
  2773. IF r.boff # 0 THEN
  2774. Err (ENoLib)
  2775. ELSE
  2776. EmLoadVarIndSym (r.idx, 1)
  2777. END
  2778. ELSIF r.kind = 1 THEN
  2779. LoadSymVar (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H, 1)
  2780. ELSIF r.kind = 3 THEN
  2781. EmLoadVar (FALSE, EmStrBlob (r), 1)
  2782. ELSE
  2783. Err (ENoLib) ;
  2784. RETURN
  2785. END ;
  2786. EmMovAh0 () ;
  2787. r.kind := 2 ;
  2788. r.cls := TScalar ;
  2789. r.chr := FALSE ;
  2790. RETURN
  2791. ELSE
  2792. Err (EUnknown)
  2793. END
  2794. END ParseAtom ;
  2795. PROCEDURE AddPend (kind, who, place : CARDINAL) ;
  2796. BEGIN
  2797. IF nPend < MaxPend THEN
  2798. pend [nPend].kind := kind ;
  2799. pend [nPend].who := who ;
  2800. pend [nPend].place := place ;
  2801. INC (nPend)
  2802. ELSE
  2803. Err (ECompOvf)
  2804. END
  2805. END AddPend ;
  2806. PROCEDURE ParmSlot (idx, k : CARDINAL) : CARDINAL ;
  2807. (* The stack slot of argument k (0-based) of procedure/function idx, from the
  2808. parameter list recorded on the symbol. Out of range means the source passed
  2809. more arguments than there are parameters: keep the old two-byte assumption
  2810. rather than inventing an arity error here. *)
  2811. VAR pi : CARDINAL ;
  2812. BEGIN
  2813. IF (symtab [idx].parmCount = 0) OR (k >= symtab [idx].parmCount) THEN
  2814. RETURN 2
  2815. END ;
  2816. pi := symtab [idx].parmFirst + k ;
  2817. RETURN ParamSlotBytes (symtab [pi])
  2818. END ParmSlot ;
  2819. PROCEDURE ParmIsVar (idx, k : CARDINAL) : BOOLEAN ;
  2820. (* Is argument k (0-based) of procedure/function idx declared VAR? Out of
  2821. range means the source passed more arguments than there are formals; treat
  2822. it as a value parameter, as ParmSlot already does. *)
  2823. VAR pi : CARDINAL ;
  2824. BEGIN
  2825. IF (symtab [idx].parmCount = 0) OR (k >= symtab [idx].parmCount) THEN
  2826. RETURN FALSE
  2827. END ;
  2828. pi := symtab [idx].parmFirst + k ;
  2829. RETURN symtab [pi].isVar
  2830. END ParmIsVar ;
  2831. PROCEDURE EmCallMost (idx, cleanup : CARDINAL) ;
  2832. (* emit the call to sym 'idx' and clean up `cleanup' bytes of arguments, which
  2833. is the SUM of their slot sizes - not 2 per argument once a slot can be
  2834. larger than a word. *)
  2835. VAR p : CARDINAL ;
  2836. BEGIN
  2837. EmitStaticLink (idx) ;
  2838. IF symtab [idx].defnd THEN
  2839. DropC (EmCall (symtab [idx].goPos))
  2840. ELSE
  2841. p := EmCall (0) ;
  2842. AddPend (1, idx, p)
  2843. END ;
  2844. IF cleanup > 0 THEN
  2845. EmAddSp (cleanup)
  2846. END
  2847. END EmCallMost ;
  2848. PROCEDURE ParseCallArgs (idx : CARDINAL) ;
  2849. (* '(' already consumed: read args ')' then call.
  2850. Each argument is loaded and pushed the moment it has been parsed, which is
  2851. what TPSRC8 cproc/cprlp1/cprlp2 do - CALL exprsave, CALL epushax, one per
  2852. argument, then the CALL. A computed argument (kind 2) exists only in AX,
  2853. so parsing the argument after it would destroy it before the push loop
  2854. ever reached it: `p (a > b, x)' passed x twice.
  2855. Pushing in parse order is also the order the callee is laid out for: the
  2856. first argument goes on first and so ends up farthest from BP, and the LAST
  2857. declared parameter is the one at BP+4 (RESUME-TP3.md 3.11). *)
  2858. VAR arg : ERes ;
  2859. nArgs, cleanup, slot, nCopy : CARDINAL ;
  2860. BEGIN
  2861. nArgs := 0 ;
  2862. cleanup := 0 ;
  2863. IF CurCh () = ')' THEN
  2864. DropCh (GetCh ())
  2865. ELSE
  2866. LOOP
  2867. IF nArgs >= 16 THEN
  2868. Err (ECompOvf) ;
  2869. EXIT
  2870. END ;
  2871. ParseExpr (arg) ;
  2872. slot := ParmSlot (idx, nArgs) ;
  2873. IF ParmIsVar (idx, nArgs) THEN
  2874. (* VAR formal: push the actual's ADDRESS. Only a variable has
  2875. one; a constant or a computed value is refused rather than
  2876. quietly passed by value. An element with a constant index
  2877. (`s[i]') is a variable too: its address is off+boff, which
  2878. EmPushVarAddr forms directly. *)
  2879. IF arg.kind = 5 THEN
  2880. (* An element/field address already in DI, including a record
  2881. field passed as a VAR actual: Sheet[I,J].Contents. *)
  2882. EmPushDi ()
  2883. ELSIF arg.kind # 1 THEN
  2884. Err (ENoLib) ;
  2885. EXIT
  2886. ELSIF symtab [arg.idx].isVar THEN
  2887. (* Passing a VAR parameter on: its slot already HOLDS the
  2888. address, so push that, not the address of the slot. *)
  2889. IF arg.boff # 0 THEN
  2890. Err (ENoLib) ;
  2891. EXIT
  2892. END ;
  2893. LoadSymVar (arg.idx, symtab [arg.idx].off, 2) ;
  2894. EmPushAx ()
  2895. ELSE
  2896. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  2897. END
  2898. ELSIF slot > 2 THEN
  2899. (* A STRING VALUE parameter: push its whole slot, copied.
  2900. `copyN' is at most the slot: an actual shorter than the formal
  2901. leaves the tail untouched, which is harmless because the length
  2902. byte bounds every read. *)
  2903. IF arg.cls = TString THEN
  2904. nCopy := slot ;
  2905. IF arg.kind = 1 THEN
  2906. IF VarSize (arg) < nCopy THEN
  2907. nCopy := VarSize (arg)
  2908. END
  2909. ELSIF arg.kind = 3 THEN
  2910. IF strLen [arg.strx] + 1 < nCopy THEN
  2911. nCopy := strLen [arg.strx] + 1
  2912. END
  2913. ELSE
  2914. Err (ENoLib) ; (* a computed string is not a value *)
  2915. EXIT
  2916. END ;
  2917. EmPushStrSlot (arg, slot, nCopy)
  2918. ELSIF arg.cls = TReal THEN
  2919. EmPushRealSlot (arg, slot)
  2920. ELSE
  2921. Err (ETypeErr) ;
  2922. EXIT
  2923. END
  2924. ELSE
  2925. LoadAtom (arg) ;
  2926. EmPushAx ()
  2927. END ;
  2928. cleanup := cleanup + slot ;
  2929. INC (nArgs) ;
  2930. IF NOT MatchDelim (',') THEN
  2931. EXIT
  2932. END
  2933. END ;
  2934. ExpectDelim (')', ENoSemi)
  2935. END ;
  2936. EmCallMost (idx, cleanup)
  2937. END ParseCallArgs ;
  2938. PROCEDURE ParseCall (idx : CARDINAL) ;
  2939. (* procedure/function call; '(' optional - same parse-then-push as above,
  2940. reached from ParseAtom when a FUNCTION is called in an expression *)
  2941. VAR arg : ERes ;
  2942. nArgs, cleanup, slot, nCopy : CARDINAL ;
  2943. BEGIN
  2944. nArgs := 0 ;
  2945. cleanup := 0 ;
  2946. IF MatchDelim ('(') THEN
  2947. IF CurCh () # ')' THEN
  2948. LOOP
  2949. IF nArgs >= 16 THEN
  2950. Err (ECompOvf) ;
  2951. EXIT
  2952. END ;
  2953. ParseExpr (arg) ;
  2954. slot := ParmSlot (idx, nArgs) ;
  2955. IF ParmIsVar (idx, nArgs) THEN
  2956. (* A VAR formal in a FUNCTION call: the callee wants the
  2957. address, exactly as ParseCallArgs does for a procedure. This
  2958. path was missing, so `Length(s)' inside a function whose
  2959. formal is `var s' pushed the string's VALUE and the callee
  2960. would have read it as an address. *)
  2961. IF arg.kind = 5 THEN
  2962. EmPushDi ()
  2963. ELSIF arg.kind # 1 THEN
  2964. Err (ENoLib) ;
  2965. EXIT
  2966. ELSIF symtab [arg.idx].isVar THEN
  2967. IF arg.boff # 0 THEN
  2968. Err (ENoLib) ;
  2969. EXIT
  2970. END ;
  2971. LoadSymVar (arg.idx, symtab [arg.idx].off, 2) ;
  2972. EmPushAx ()
  2973. ELSE
  2974. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  2975. END
  2976. ELSIF slot > 2 THEN
  2977. (* see ParseCallArgs: push the whole string slot *)
  2978. IF arg.cls = TString THEN
  2979. nCopy := slot ;
  2980. IF arg.kind = 1 THEN
  2981. IF VarSize (arg) < nCopy THEN
  2982. nCopy := VarSize (arg)
  2983. END
  2984. ELSIF arg.kind = 3 THEN
  2985. IF strLen [arg.strx] + 1 < nCopy THEN
  2986. nCopy := strLen [arg.strx] + 1
  2987. END
  2988. ELSE
  2989. Err (ENoLib) ;
  2990. EXIT
  2991. END ;
  2992. EmPushStrSlot (arg, slot, nCopy)
  2993. ELSIF arg.cls = TReal THEN
  2994. EmPushRealSlot (arg, slot)
  2995. ELSE
  2996. Err (ETypeErr) ;
  2997. EXIT
  2998. END
  2999. ELSE
  3000. LoadAtom (arg) ;
  3001. EmPushAx ()
  3002. END ;
  3003. cleanup := cleanup + slot ;
  3004. INC (nArgs) ;
  3005. IF NOT MatchDelim (',') THEN
  3006. EXIT
  3007. END
  3008. END ;
  3009. ExpectDelim (')', ENoSemi)
  3010. ELSE
  3011. DropCh (GetCh ())
  3012. END
  3013. END ;
  3014. callScratch := 0 ;
  3015. IF symtab [idx].cls = TReal THEN
  3016. callScratch := NewScratchReal () ;
  3017. EmLeaDi (FALSE, symtab [callScratch].off)
  3018. END ;
  3019. EmCallMost (idx, cleanup)
  3020. END ParseCall ;
  3021. PROCEDURE ParseExpr (VAR r : ERes) ;
  3022. BEGIN
  3023. ParseCmp (r)
  3024. END ParseExpr ;
  3025. (* ---------------------------------------------------------------- *)
  3026. (* statements (TPSRC8) *)
  3027. (* ---------------------------------------------------------------- *)
  3028. PROCEDURE IntToMbf (v : LONGINT ; VAR b : ARRAY OF CHAR) ;
  3029. (* TP3's 6-byte Real, as measured in CALC-PORT.md against the cells holding
  3030. 1, 2, 3, 4 and 5:
  3031. value = (1 + N / 2^39) * 2^(E - 129)
  3032. byte 0 is E (biased by 129); bytes 1..5 are N, little-endian; the SIGN is
  3033. bit 7 of byte 5 (the book's `sign + 39-bit mantissa'). An all-zero value
  3034. is 0.0. 1 -> 81 00 00 00 00 00, 3 -> 82 00 00 00 00 40,
  3035. 5 -> 83 00 00 00 00 20 - all cross-checked against CALCDEMO.MCS.
  3036. Only INTEGER values are converted; real arithmetic and formatting are a
  3037. later stage, and a real RHS that is not a constant or a copy is ENoLib. *)
  3038. VAR a, p, n : LONGINT ;
  3039. e, k, i : CARDINAL ;
  3040. neg : BOOLEAN ;
  3041. BEGIN
  3042. i := 0 ;
  3043. WHILE i < 6 DO
  3044. b [i] := CHR (0) ;
  3045. INC (i)
  3046. END ;
  3047. (* The parser represents a negated 16-bit INTEGER constant as its
  3048. unsigned 16-bit bit pattern (W16), so -1 arrives as 65535. Convert that
  3049. back to a signed value before building a REAL; otherwise `R := -1' would
  3050. become +65535. *)
  3051. IF (v > 32767) AND (v <= 65535) THEN
  3052. v := v - 65536
  3053. END ;
  3054. IF v = 0 THEN
  3055. RETURN
  3056. END ;
  3057. neg := v < 0 ;
  3058. IF neg THEN
  3059. a := -v
  3060. ELSE
  3061. a := v
  3062. END ;
  3063. (* k = floor(log2(a)); p = 2^k *)
  3064. p := 1 ;
  3065. k := 0 ;
  3066. WHILE (k < 39) AND (p * 2 <= a) DO
  3067. p := p * 2 ;
  3068. INC (k)
  3069. END ;
  3070. IF k >= 39 THEN
  3071. Err (EConstRange) ;
  3072. RETURN
  3073. END ;
  3074. e := 129 + k ;
  3075. b [0] := CHR (e) ;
  3076. n := a - p ;
  3077. i := 39 - k ;
  3078. WHILE i > 0 DO
  3079. n := n * 2 ;
  3080. DEC (i)
  3081. END ;
  3082. i := 1 ;
  3083. WHILE i <= 5 DO
  3084. b [i] := CHR (VAL (CARDINAL, n MOD 256)) ;
  3085. n := n DIV 256 ;
  3086. INC (i)
  3087. END ;
  3088. IF neg THEN
  3089. b [5] := CHR (ORD (b [5]) + 128)
  3090. END
  3091. END IntToMbf ;
  3092. PROCEDURE RealOperand (r : ERes ; dstOff : CARDINAL) ;
  3093. (* Copy a REAL operand into a 6-byte scratch, converting an ordinal value to
  3094. REAL first. RealBin and RealUnary used to call EmCopyRealTo directly; an
  3095. INTEGER variable there was refused, so `E := A/B' could not compile. *)
  3096. VAR tmp : CARDINAL ;
  3097. BEGIN
  3098. IF r.cls = TReal THEN
  3099. EmCopyRealTo (dstOff, r)
  3100. ELSIF (r.cls = TSet) OR (r.cls = TString) THEN
  3101. Err (ETypeErr)
  3102. ELSE
  3103. IF r.kind = 5 THEN
  3104. EmPushDi ()
  3105. ELSIF r.kind = 1 THEN
  3106. EmPushSymAddr (r.idx, (symtab [r.idx].off + r.boff) MOD 10000H)
  3107. ELSE
  3108. LoadAtom (r) ;
  3109. EmStoreVar (FALSE, symtab [tmpA].off, 2) ;
  3110. EmPushVarAddr (FALSE, symtab [tmpA].off)
  3111. END ;
  3112. EmPushVarAddr (FALSE, dstOff) ;
  3113. DropC (EmCall (TU_RealFromInt)) ;
  3114. EmAddSp (4)
  3115. END
  3116. END RealOperand ;
  3117. PROCEDURE RealBin (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
  3118. (* REAL + - * / using the runtime's 12-byte block entries. SaveLeft has
  3119. already copied a REAL left operand to a stable scratch; a kind-5 right is
  3120. copied first, while its DI address is live. *)
  3121. VAR block, ent : CARDINAL ;
  3122. BEGIN
  3123. block := NewScratchRealBlock () ;
  3124. IF right.kind = 5 THEN
  3125. RealOperand (right, symtab [block].off + 6) ;
  3126. RealOperand (left, symtab [block].off)
  3127. ELSE
  3128. RealOperand (left, symtab [block].off) ;
  3129. RealOperand (right, symtab [block].off + 6)
  3130. END ;
  3131. CASE op OF
  3132. OpAdd : ent := TU_RealAdd
  3133. | OpSub : ent := TU_RealSub
  3134. | OpMul : ent := TU_RealMul
  3135. | OpDiv : ent := TU_RealDiv
  3136. ELSE
  3137. Err (ETypeErr) ;
  3138. RETURN
  3139. END ;
  3140. EmPushVarAddr (FALSE, symtab [block].off) ;
  3141. DropC (EmCall (ent)) ;
  3142. EmAddSp (2) ;
  3143. res.kind := 1 ;
  3144. res.idx := block ;
  3145. res.cls := TReal ;
  3146. res.sz := 6 ;
  3147. res.boff := 0
  3148. END RealBin ;
  3149. PROCEDURE RealUnary (ent : CARDINAL ; VAR r : ERes) ;
  3150. (* A one-argument REAL builtin: copy the operand into a 12-byte block and let
  3151. the runtime entry leave its result in the first six bytes. *)
  3152. VAR block : CARDINAL ;
  3153. BEGIN
  3154. block := NewScratchRealBlock () ;
  3155. EmCopyRealTo (symtab [block].off, r) ;
  3156. EmPushVarAddr (FALSE, symtab [block].off) ;
  3157. DropC (EmCall (ent)) ;
  3158. EmAddSp (2) ;
  3159. r.kind := 1 ;
  3160. r.idx := block ;
  3161. r.cls := TReal ;
  3162. r.sz := 6 ;
  3163. r.boff := 0
  3164. END RealUnary ;
  3165. END CompilerExpr.