CompilerIO.mod 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  1. IMPLEMENTATION MODULE CompilerIO ;
  2. (* Standard-procedure dispatch: WRITE/WRITELN/READ, CRT, files, strings,
  3. sets, reals, heap and DOS calls. The parser hands IoCall the builtin's
  4. symbol index; each argument is parsed and loaded here so the runtime gets
  5. a value, never a descriptor. *)
  6. FROM SYSTEM IMPORT BYTE ;
  7. FROM CompilerShared IMPORT AposC, ArrRec, BI_Abs, BI_Arctan, BI_Assign,
  8. BI_BlockRead, BI_BlockWrite, BI_Chr, BI_Close,
  9. BI_ClrEol, BI_ClrScr, BI_Concat, BI_Copy, BI_Cos,
  10. BI_Delay, BI_Delete, BI_Dispose, BI_Eof, BI_Erase,
  11. BI_Exp, BI_FilePos, BI_FileSize, BI_FillChar,
  12. BI_GetDir, BI_GotoXY, BI_Halt, BI_Insert, BI_Int,
  13. BI_Intr, BI_IoResult, BI_KeyPressed, BI_Length,
  14. BI_Ln, BI_LowVideo, BI_Move, BI_Msdos, BI_New,
  15. BI_NormVideo, BI_Ofs, BI_Ord, BI_Pos, BI_Pred,
  16. BI_Random, BI_Randomize, BI_Read, BI_ReadLn,
  17. BI_Rename, BI_Reset, BI_Rewrite, BI_Round, BI_Seek,
  18. BI_Seg, BI_Sin, BI_SizeOf, BI_Sqr, BI_Sqrt, BI_Str,
  19. BI_Succ, BI_Trunc, BI_UpCase, BI_Val, BI_Write,
  20. BI_WriteLn, CodeMax, CrtCaret, CrtClr, CrtDelay,
  21. CrtDh, CrtDl, CrtEol, CrtKey, CrtLow, CrtNorm,
  22. DataStart, DirRec, ECompOvf, EConstRange, EFileNF,
  23. EInclNest, EIoHere, EMemOvf, ENoLib, ENoSemi,
  24. EPointExp, ERes, ESimpType, ETypeErr, EUnknown,
  25. EntSize, Err, FileVarSize, FldRec, KBuiltin,
  26. KConst, KFunc, KLabel, KProc, KType, KVar, MaxArrs,
  27. MaxCode, MaxFields, MaxLine, MaxName, MaxPatch,
  28. MaxPend, OK, OpAdd, OpDiv, OpMul, OpShl, OpShr,
  29. OpSub, PatchRec, PendRec, TArray, TBool, TChar,
  30. TEnum, TFile, TNone, TPtr, TReal, TRecord, TScalar,
  31. TSet, TString, TText, TU_BlockRead, TU_BlockWrite,
  32. TU_CmpStr, TU_Erase, TU_FileAssign, TU_FileClose,
  33. TU_FileEof, TU_FilePos, TU_FileRdln, TU_FileRdtok,
  34. TU_FileReadln, TU_FileReset, TU_FileRewrite,
  35. TU_FileSize, TU_FillChar, TU_GetDir, TU_Halt,
  36. TU_HeapNew, TU_InitMem, TU_IntFmt, TU_IntFmtW,
  37. TU_IoResult, TU_IoStore, TU_MemCopy, TU_Move,
  38. TU_Msdos, TU_ProgEnd, TU_Random, TU_Randomize,
  39. TU_RdBool, TU_RdByte, TU_RdChar, TU_RdInt, TU_RdLn,
  40. TU_RdStr, TU_RealAbs, TU_RealAdd, TU_RealArctan,
  41. TU_RealCmp, TU_RealCos, TU_RealDiv, TU_RealExp,
  42. TU_RealFmt, TU_RealFromInt, TU_RealIntFunc,
  43. TU_RealLn, TU_RealMul, TU_RealSin, TU_RealSqrt,
  44. TU_RealSub, TU_RealToInt, TU_RealVal, TU_RealWrite,
  45. TU_Rename, TU_RunErr, TU_Seek, TU_SetCmp,
  46. TU_SetCopy, TU_SetOp, TU_SetSingle, TU_StackChk,
  47. TU_StrCat, TU_StrCopy, TU_StrDelete, TU_StrFmtW,
  48. TU_StrIndex, TU_StrInsert, TU_StrPos, TU_StrStore,
  49. TU_ValInt, TU_ValReal, TU_WrBool, TU_WrBoolW,
  50. TU_WrChar, TU_WrCharW, TU_WrInl, TU_WrInt,
  51. TU_WrIntW, TU_WrLn, TU_WrReal, TU_WrStr, TU_WrStrW,
  52. TUntyp, TkAbsolute, TkAnd, TkArray, TkBegin,
  53. TkCase, TkConst, TkDiv, TkDo, TkDownto, TkElse,
  54. TkEnd, TkExit, TkExternal, TkFile, TkFor,
  55. TkForward, TkFunction, TkGoto, TkIf, TkIn, TkLabel,
  56. TkMod, TkNil, TkNone, TkNot, TkOf, TkOr, TkOverlay,
  57. TkPacked, TkProcedure, TkProgram, TkRecord,
  58. TkRepeat, TkSet, TkString, TkText, TkThen, TkTo,
  59. TkType, TkUntil, TkVar, TkWhile, TkWith, abortFac,
  60. arrTop, arrs, brkN, brkSave, callScratch, caseJmp,
  61. caseN, cbuf, codeSz, constBlob, crtAttrOff,
  62. curIsFunc, curProc, curStaticOff, dataBase, dataSz,
  63. dc, dirs, entRel, enumHi, enumLo, errNum, exitCnt,
  64. exitPatch, fields, fldTop, hdrCS, hdrDS, hdrFlag,
  65. hdrHeap, hdrMax, imgReady, lblHi, lblJmp, lblLo,
  66. locBytes, locFree, loopTy, nPatch, nPend,
  67. newArrIdx, parmOff, patches, pc, pend, prologAt,
  68. recFldFirst, recFldLast, resultIsReal,
  69. resultPtrOff, resultVar, rtSz, srcPos, tmpA, tmpB,
  70. txerrPos, varspc, withBoff, withFF, withFL,
  71. withIdx, withIsDI, withN, withSlot ;
  72. FROM CompilerEmit IMPORT AddPatch, Ebyte, EmAddAxCx, EmAndAxCx, EmBpDisp,
  73. EmBxDisp, EmCall, EmCmpAxCx, EmCmpAxi, EmDecAx,
  74. EmIDivAxCx, EmIncAx, EmJcc, EmJmpNear, EmLdBxBx,
  75. EmLeaDiBx, EmLoadVarBx, EmMovAh0, EmMovAxCx,
  76. EmMovAxDx, EmMovAxSp, EmMovAxi, EmMovBpSp,
  77. EmMovBxBp, EmMovChAl, EmMovChImm, EmMovCxSp,
  78. EmMovCxi, EmMovDxAx, EmMovDxBp, EmMovDxBx,
  79. EmMulAxCx, EmNegAx, EmNotAx, EmOrAxAx, EmOrAxCx,
  80. EmPopAx, EmPopBx, EmPopCx, EmPopDx, EmPopSi,
  81. EmPushAx, EmPushBx, EmPushCx, EmPushDx, EmRelSetcc,
  82. EmSetcc, EmStoreVarBx, EmSubAxCx, EmXchgAxCx,
  83. EmXorAl01, EmXorAxAx, Eword, JccShort, JccShortInv,
  84. PatchWord, ResolvePatches, SetPatTgt ;
  85. FROM CompilerSource IMPORT CurCh, ExpandIncludes, GetCh, PeekAhead,
  86. SetSourcePath, Upper, inclActive, inclEnd, srcLen ;
  87. FROM CompilerUtil IMPORT Alpha, AlphaNum, BitAnd, BitNot, BitOr, CopyStr,
  88. Digit, DropB, DropC, DropCh, IsHexCh, NameEq,
  89. NumToName, W16 ;
  90. FROM CompilerSymbols IMPORT CopyWord, DupTest, HideLocals, MaxSym, NewSym,
  91. SaveWord, Search, SymEntry, lexnest, symTop,
  92. symtab, wrd ;
  93. FROM CompilerLex IMPORT DeclaresProc, ExpectDelim, GetWord, HexVal, InitKeys,
  94. IsBlank, KwAhead, MatchAssign, MatchDelim,
  95. MatchKey, MatchRange, PeekKw, RdConst, RdIntConst,
  96. Skip, StrNew, StrPut, WddTok, rdStrX, strCnt,
  97. strLen, strOff, strPool, strTop ;
  98. FROM CompilerExpr IMPORT AddPend, AllocSetScratch, BinOpEmit, ConstAdd,
  99. ConstCmp, ConstMul, ConstSub, EmAsciiZLiteral,
  100. EmCallMost, EmCopyRealTo, EmPushRealAddr,
  101. EmPushRealSlot, EmPushSetAddr, EmPushStrAddr,
  102. EmPushStrOperand, EmPushStrSlot, EmSetBlob,
  103. EmStrBlob, EmXchgAxDx, EmitBoundsCheck, FoldSetIn,
  104. IntToMbf, IsCharArray, LoadAtom, LoadPair,
  105. MaterializeDyn, NewScratchReal,
  106. NewScratchRealBlock, NewScratchStr, NewScratchWord,
  107. ParmIsVar, ParmSlot, ParseAdd, ParseAtom,
  108. ParseCall, ParseCallArgs, ParseCmp, ParseExpr,
  109. ParseMul, ParseNeg, ParseSub, ParseVar, RdSetBase,
  110. RdSetLiteral, RealBin, RealOperand, RealUnary,
  111. SaveLeft, SetBinEmit, SetPairSize, SetRangeBits,
  112. StrConcat, StrOperand, VarSize, WithLookup ;
  113. FROM Compiler IMPORT Assignment, BoundsOf, CodeByteAt, CodeBytes, Compile,
  114. Compound, DataBase, DataBytes, DefBuiltins,
  115. DefConst, DefLabelPart, DefPart, DefType, DefVar,
  116. EmAddBxAx, EmAddDiAx, EmAddDiImm, EmAddSp,
  117. EmAndAlDl, EmAndAlImm, EmAndClImm, EmCmpAlImm,
  118. EmCrtSeq, EmFileIo, EmIncDi, EmIncSi, EmInlineCh,
  119. EmInlineCrLf, EmLdAlBx, EmLdAlDi, EmLdAlDiOff,
  120. EmLdAlSi, EmLdAxDi, EmLdDiGlob, EmLeaBx, EmLeaDi,
  121. EmLeave, EmLoadVar, EmLoadVarInd, EmLoadVarIndSym,
  122. EmMovAxDi, EmMovAxDs, EmMovBxAx, EmMovBxDi,
  123. EmMovBxi, EmMovClAl, EmMovDiAx, EmMovDlImm,
  124. EmPopDi, EmPushBp, EmPushDi, EmPushSi,
  125. EmPushSymAddr, EmPushVarAddr, EmRet, EmShlAxCl,
  126. EmShlDlCl, EmShrAx1, EmShrAxCl, EmStDiAl,
  127. EmStDiAlOff, EmStDiGlob, EmStDiImm, EmStoreInd,
  128. EmStoreVar, EmStoreVarIndPrep, EmSubAlImm, EmSubSp,
  129. EmXorByteGlob, EmitFrameBase, EmitStaticLink,
  130. HeadWord, IfMatchSemi, ImageByteAt, ImageBytes,
  131. Inittur, IsOuterLocal, LabelColonAhead,
  132. LeaDiSym, LoadSymVar, ParamSlotBytes,
  133. ParseLabelStmt, ParseType, ProcFunc, SetSource,
  134. SlotSize, Statmnt, StoreSymVar, StoreVarIndPrepSym,
  135. SymEpi ;
  136. PROCEDURE IoCall (idx : CARDINAL) ;
  137. (* WRITE / WRITELN / READ / READLN / HALT.
  138. TP3 (TPSRC8 pwriteln, pwrloop, prdtyped) does not pass a descriptor to
  139. the runtime: it looks at each argument's class and emits a *different*
  140. call per type, so the formatting is fixed at compile time. Mirrored
  141. here - one call per argument, then a final call for the line break.
  142. WRITE/WRITELN push the value; READ/READLN push the address, so the
  143. runtime can store. As everywhere else in this compiler the caller
  144. cleans the argument off the stack. *)
  145. VAR arg : ERes ;
  146. nArgs, ent, which, acls : CARDINAL ;
  147. argBytes : CARDINAL ; (* bytes pushed for this argument: 2, or 4 with
  148. a field width *)
  149. reading : BOOLEAN ;
  150. pushed : BOOLEAN ;
  151. skipArg, handled : BOOLEAN ;
  152. k, n, p, scratch, cap : CARDINAL ;
  153. dummy, errV : ERes ;
  154. BEGIN
  155. which := symtab [idx].cls ; (* BI_* *)
  156. IF which = BI_Halt THEN
  157. IF MatchDelim ('(') THEN (* halt(0) - code ignored *)
  158. ParseExpr (dummy) ;
  159. ExpectDelim (')', ENoSemi)
  160. END ;
  161. DropC (EmCall (TU_Halt)) ;
  162. RETURN
  163. END ;
  164. IF which = BI_New THEN
  165. (* New(P): allocate sizeof(P^) bytes and store the address in P. The
  166. pointer symbol's `elem' is the pointee TYPE symbol; its size is the
  167. allocation. *)
  168. IF MatchDelim ('(') THEN
  169. ParseExpr (arg) ;
  170. IF OK () AND ((arg.kind = 1) OR (arg.kind = 5)) AND
  171. (arg.cls = TPtr) THEN
  172. p := arg.ptrElem ;
  173. IF p = 0 THEN
  174. p := symtab [arg.idx].elem
  175. END ;
  176. EmMovAxi (symtab [p].size) ;
  177. EmPushAx () ; (* size -> [BP+6] *)
  178. IF arg.kind = 5 THEN
  179. EmPushDi () (* element pointer slot *)
  180. ELSE
  181. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  182. END ;
  183. DropC (EmCall (TU_HeapNew)) ;
  184. EmAddSp (4)
  185. ELSIF OK () THEN
  186. Err (ETypeErr)
  187. END ;
  188. ExpectDelim (')', ENoSemi)
  189. ELSE
  190. Err (ENoSemi)
  191. END ;
  192. RETURN
  193. END ;
  194. IF which = BI_Dispose THEN
  195. (* Dispose(P): consume the argument. The allocator is a bump pointer
  196. with no free list; CALC allocates once at the start of Help and
  197. disposes only after the help display is finished, so not reclaiming
  198. the block has no observable effect. A future allocation after a
  199. Dispose would need a real free list. *)
  200. IF MatchDelim ('(') THEN
  201. ParseExpr (arg) ;
  202. IF OK () AND NOT (((arg.kind = 1) OR (arg.kind = 5))
  203. AND (arg.cls = TPtr)) THEN
  204. Err (ETypeErr)
  205. END ;
  206. ExpectDelim (')', ENoSemi)
  207. ELSE
  208. Err (ENoSemi)
  209. END ;
  210. RETURN
  211. END ;
  212. IF which = BI_ClrScr THEN
  213. EmCrtSeq (CrtClr) ;
  214. RETURN
  215. END ;
  216. IF which = BI_ClrEol THEN
  217. (* clear from the cursor to the end of the line - a real INT 10h call,
  218. not a no-op: AH=03 reads the cursor, CX := it, DL := 79, AH=06 blanks
  219. that window. *)
  220. EmCrtSeq (CrtEol) ;
  221. RETURN
  222. END ;
  223. IF which = BI_GotoXY THEN
  224. (* GotoXY (x, y): 1-based; evaluate x first and park it, then y, so the
  225. code for y cannot clobber DL. *)
  226. IF MatchDelim ('(') THEN
  227. ParseExpr (dummy) ;
  228. IF OK () THEN
  229. LoadAtom (dummy) ;
  230. EmPushAx ()
  231. END ;
  232. ExpectDelim (',', ENoSemi) ;
  233. ParseExpr (dummy) ;
  234. IF OK () THEN
  235. LoadAtom (dummy) ;
  236. EmCrtSeq (CrtDh)
  237. END ;
  238. ExpectDelim (')', ENoSemi) ;
  239. EmPopAx () ;
  240. EmCrtSeq (CrtDl) ;
  241. EmCrtSeq (CrtCaret)
  242. ELSE
  243. Err (ENoSemi)
  244. END ;
  245. RETURN
  246. END ;
  247. IF which = BI_LowVideo THEN
  248. (* set the attribute the CLEAR operations use to 7; see EmCrtSeq *)
  249. EmCrtSeq (CrtLow) ;
  250. RETURN
  251. END ;
  252. IF which = BI_NormVideo THEN
  253. EmCrtSeq (CrtNorm) ;
  254. RETURN
  255. END ;
  256. IF which = BI_Delay THEN
  257. (* Delay(ms): a real busy loop (EmCrtSeq's CrtDelay), no INT, so it is
  258. identical under qemu and Exec86. *)
  259. IF MatchDelim ('(') THEN
  260. ParseExpr (dummy) ;
  261. IF OK () THEN
  262. LoadAtom (dummy) ;
  263. EmCrtSeq (CrtDelay)
  264. END ;
  265. ExpectDelim (')', ENoSemi)
  266. ELSE
  267. Err (ENoSemi)
  268. END ;
  269. RETURN
  270. END ;
  271. IF which = BI_Insert THEN
  272. (* Insert(Source, Dest, Index). A CHAR source is materialised as a
  273. one-character counted string in a scratch; Dest must be a modifiable
  274. string variable/field. Stack order is source, dest, index, max, so
  275. the runtime sees max at [BP+4] and source at [BP+10]. *)
  276. IF NOT MatchDelim ('(') THEN
  277. Err (ENoSemi) ;
  278. RETURN
  279. END ;
  280. ParseExpr (arg) ;
  281. IF OK () AND (arg.cls = TString) AND
  282. ((arg.kind = 1) OR (arg.kind = 3) OR (arg.kind = 5)) THEN
  283. EmPushStrAddr (arg)
  284. ELSIF OK () AND ((arg.cls = TChar) OR
  285. ((arg.cls = TScalar) AND arg.chr)) THEN
  286. scratch := NewScratchStr () ;
  287. LoadAtom (arg) ;
  288. EmStoreVar (FALSE, (symtab [scratch].off + 1) MOD 10000H, 1) ;
  289. EmMovAxi (1) ;
  290. EmStoreVar (FALSE, symtab [scratch].off, 1) ;
  291. EmPushVarAddr (FALSE, symtab [scratch].off)
  292. ELSIF OK () THEN
  293. Err (ETypeErr) ;
  294. RETURN
  295. END ;
  296. ExpectDelim (',', ENoSemi) ;
  297. ParseExpr (dummy) ;
  298. IF OK () AND (dummy.cls = TString) AND
  299. ((dummy.kind = 1) OR (dummy.kind = 5)) THEN
  300. EmPushStrAddr (dummy) ;
  301. cap := VarSize (dummy) - 1 ;
  302. ExpectDelim (',', ENoSemi) ;
  303. ParseExpr (errV) ;
  304. IF OK () AND ((errV.cls = TScalar) OR (errV.cls = TChar)) THEN
  305. LoadAtom (errV) ; EmPushAx () ;
  306. EmMovAxi (cap) ; EmPushAx () ;
  307. DropC (EmCall (TU_StrInsert)) ;
  308. EmAddSp (8)
  309. ELSIF OK () THEN
  310. Err (ETypeErr)
  311. END
  312. ELSIF OK () THEN
  313. Err (ETypeErr)
  314. END ;
  315. ExpectDelim (')', ENoSemi) ;
  316. RETURN
  317. END ;
  318. IF which = BI_Delete THEN
  319. (* Delete(S, pos, len): S must be a variable (or a DI-addressed field);
  320. the runtime mutates it in place. S's address is parked while the
  321. two integer expressions are parsed. *)
  322. IF NOT MatchDelim ('(') THEN
  323. Err (ENoSemi) ;
  324. RETURN
  325. END ;
  326. ParseExpr (arg) ;
  327. IF OK () AND (arg.cls = TString) AND
  328. ((arg.kind = 1) OR (arg.kind = 5)) THEN
  329. EmPushStrAddr (arg) ;
  330. ExpectDelim (',', ENoSemi) ;
  331. ParseExpr (dummy) ;
  332. IF OK () AND ((dummy.cls = TScalar) OR (dummy.cls = TChar)) THEN
  333. LoadAtom (dummy) ; EmPushAx () ;
  334. ExpectDelim (',', ENoSemi) ;
  335. ParseExpr (errV) ;
  336. IF OK () AND ((errV.cls = TScalar) OR (errV.cls = TChar)) THEN
  337. LoadAtom (errV) ; EmPushAx () ;
  338. EmPopAx () ; (* len *)
  339. EmPopBx () ; (* pos *)
  340. EmPopCx () ; (* source *)
  341. EmPushAx () ; EmPushBx () ; EmPushCx () ;
  342. DropC (EmCall (TU_StrDelete)) ;
  343. EmAddSp (6)
  344. ELSIF OK () THEN
  345. Err (ETypeErr)
  346. END
  347. ELSIF OK () THEN
  348. Err (ETypeErr)
  349. END
  350. ELSIF OK () THEN
  351. Err (ETypeErr)
  352. END ;
  353. ExpectDelim (')', ENoSemi) ;
  354. RETURN
  355. END ;
  356. IF which = BI_Val THEN
  357. (* Val(S, V, E): integer conversion. E receives 0 on success or the
  358. 1-based position of the first invalid character; V is left unchanged
  359. on error. *)
  360. IF NOT MatchDelim ('(') THEN
  361. Err (ENoSemi) ;
  362. RETURN
  363. END ;
  364. ParseExpr (arg) ;
  365. IF OK () AND (arg.cls = TString) AND
  366. ((arg.kind = 1) OR (arg.kind = 3) OR (arg.kind = 5)) THEN
  367. ExpectDelim (',', ENoSemi) ;
  368. ParseExpr (dummy) ;
  369. ExpectDelim (',', ENoSemi) ;
  370. ParseExpr (errV) ;
  371. IF OK () AND (dummy.kind = 1) AND (dummy.cls = TScalar)
  372. AND (VarSize (dummy) = 2) AND (errV.kind = 1)
  373. AND (errV.cls = TScalar) AND (VarSize (errV) = 2) THEN
  374. EmPushSymAddr (errV.idx, (symtab [errV.idx].off + errV.boff) MOD 10000H) ;
  375. EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) ;
  376. EmPushStrAddr (arg) ;
  377. DropC (EmCall (TU_ValInt)) ;
  378. EmAddSp (6)
  379. ELSIF OK () AND (dummy.cls = TReal) AND
  380. ((dummy.kind = 1) OR (dummy.kind = 5)) AND
  381. (errV.kind = 1) AND (errV.cls = TScalar) AND (VarSize (errV) = 2)
  382. THEN
  383. EmPushSymAddr (errV.idx, (symtab [errV.idx].off + errV.boff) MOD 10000H) ;
  384. EmPushRealAddr (dummy) ;
  385. EmPushStrAddr (arg) ;
  386. DropC (EmCall (TU_ValReal)) ;
  387. EmAddSp (6)
  388. ELSIF OK () THEN
  389. Err (ETypeErr)
  390. END
  391. ELSIF OK () THEN
  392. Err (ETypeErr)
  393. END ;
  394. ExpectDelim (')', ENoSemi) ;
  395. RETURN
  396. END ;
  397. IF which = BI_Str THEN
  398. (* Str(Value[:FW[:DEC]], S): format a REAL into a string variable.
  399. The source address is pushed first and parked on the stack while the
  400. width/decimals expressions and the destination are parsed; the two
  401. stack words are then swapped to real_fmt's order (source at [BP+4],
  402. destination at [BP+6]). CX carries (decimals << 8) | width; it is
  403. parked in tmpA across the destination parse, which may clobber CX.
  404. Defaults match TPSRC8 pstdef1/pstdef3: width 18, decimals FFFFh. *)
  405. IF NOT MatchDelim ('(') THEN
  406. Err (ENoSemi) ;
  407. RETURN
  408. END ;
  409. ParseExpr (arg) ;
  410. IF OK () AND (arg.cls = TScalar) THEN
  411. IF MatchDelim (':') THEN
  412. ParseExpr (errV) ;
  413. IF NOT OK () THEN
  414. RETURN
  415. END ;
  416. LoadAtom (arg) ; EmPushAx () ;
  417. LoadAtom (errV) ; EmPushAx () ;
  418. IF NOT MatchDelim (',') THEN
  419. Err (ENoSemi) ;
  420. RETURN
  421. END ;
  422. ParseExpr (dummy) ;
  423. IF OK () AND (dummy.cls = TString) AND
  424. ((dummy.kind = 1) OR (dummy.kind = 5)) THEN
  425. EmPushStrAddr (dummy) ;
  426. DropC (EmCall (TU_IntFmtW)) ;
  427. EmAddSp (6)
  428. ELSIF OK () THEN
  429. Err (ETypeErr)
  430. END ;
  431. ExpectDelim (')', ENoSemi) ;
  432. RETURN
  433. END ;
  434. LoadAtom (arg) ;
  435. EmPushAx () ; (* value at [BP+6] *)
  436. IF NOT MatchDelim (',') THEN
  437. Err (ENoSemi) ;
  438. RETURN
  439. END ;
  440. ParseExpr (dummy) ;
  441. IF OK () AND (dummy.cls = TString) AND
  442. ((dummy.kind = 1) OR (dummy.kind = 5)) THEN
  443. EmPushStrAddr (dummy) ; (* destination at [BP+4] *)
  444. DropC (EmCall (TU_IntFmt)) ;
  445. EmAddSp (4)
  446. ELSIF OK () THEN
  447. Err (ETypeErr)
  448. END ;
  449. ExpectDelim (')', ENoSemi) ;
  450. RETURN
  451. END ;
  452. IF OK () AND (arg.cls = TReal) AND
  453. ((arg.kind = 1) OR (arg.kind = 5)) THEN
  454. IF arg.kind = 5 THEN
  455. EmPushDi ()
  456. ELSE
  457. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  458. END ;
  459. IF MatchDelim (':') THEN
  460. ParseExpr (dummy) ;
  461. IF OK () THEN
  462. LoadAtom (dummy)
  463. END ;
  464. IF MatchDelim (':') THEN
  465. EmPushAx () ;
  466. ParseExpr (dummy) ;
  467. IF OK () THEN
  468. LoadAtom (dummy) ;
  469. EmMovChAl () ;
  470. EmPopAx () ;
  471. EmMovClAl ()
  472. END
  473. ELSIF OK () THEN
  474. EmMovClAl () ;
  475. EmMovChImm (0FFH)
  476. END
  477. ELSE
  478. EmMovCxi (0FF12H) (* width 18, decimals FFFFh *)
  479. END ;
  480. IF NOT OK () THEN
  481. RETURN
  482. END ;
  483. EmMovAxCx () ;
  484. EmStoreVar (FALSE, symtab [tmpA].off, 2) ;
  485. IF NOT MatchDelim (',') THEN
  486. Err (ENoSemi) ;
  487. RETURN
  488. END ;
  489. ParseExpr (dummy) ;
  490. IF OK () AND (dummy.cls = TString) AND
  491. ((dummy.kind = 1) OR (dummy.kind = 5)) THEN
  492. EmPushStrAddr (dummy) ;
  493. EmPopAx () ; (* destination *)
  494. EmPopBx () ; (* source *)
  495. EmPushAx () ; (* destination at [BP+6] *)
  496. EmPushBx () ; (* source at [BP+4] *)
  497. EmLoadVar (FALSE, symtab [tmpA].off, 2) ;
  498. EmXchgAxCx () ; (* CX := packed width/decimals *)
  499. DropC (EmCall (TU_RealFmt)) ;
  500. EmAddSp (4)
  501. ELSIF OK () THEN
  502. Err (ETypeErr)
  503. END
  504. ELSIF OK () THEN
  505. Err (ETypeErr)
  506. END ;
  507. ExpectDelim (')', ENoSemi) ;
  508. RETURN
  509. END ;
  510. IF which = BI_Randomize THEN
  511. DropC (EmCall (TU_Randomize)) ;
  512. RETURN
  513. END ;
  514. IF which = BI_FillChar THEN
  515. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  516. ParseExpr (arg) ;
  517. IF NOT OK () OR ((arg.kind # 1) AND (arg.kind # 5)) THEN
  518. Err (ETypeErr) ; RETURN
  519. END ;
  520. IF arg.kind = 5 THEN EmPushDi () ELSE
  521. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  522. END ;
  523. ExpectDelim (',', ENoSemi) ;
  524. ParseExpr (dummy) ;
  525. IF NOT OK () THEN RETURN END ;
  526. LoadAtom (dummy) ; EmPushAx () ;
  527. ExpectDelim (',', ENoSemi) ;
  528. ParseExpr (dummy) ;
  529. IF NOT OK () THEN RETURN END ;
  530. LoadAtom (dummy) ; EmPushAx () ;
  531. EmPopAx () ; EmPopBx () ; EmPopCx () ;
  532. EmPushAx () ; EmPushBx () ; EmPushCx () ;
  533. DropC (EmCall (TU_FillChar)) ; EmAddSp (6) ;
  534. ExpectDelim (')', ENoSemi) ;
  535. RETURN
  536. END ;
  537. IF which = BI_Move THEN
  538. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  539. ParseExpr (arg) ;
  540. IF NOT OK () OR ((arg.kind # 1) AND (arg.kind # 5)) THEN Err (ETypeErr) ; RETURN END ;
  541. IF arg.kind = 5 THEN EmPushDi () ELSE EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) END ;
  542. ExpectDelim (',', ENoSemi) ;
  543. ParseExpr (dummy) ;
  544. IF NOT OK () OR ((dummy.kind # 1) AND (dummy.kind # 5)) THEN Err (ETypeErr) ; RETURN END ;
  545. IF dummy.kind = 5 THEN EmPushDi () ELSE EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) END ;
  546. ExpectDelim (',', ENoSemi) ;
  547. ParseExpr (dummy) ;
  548. IF NOT OK () THEN RETURN END ;
  549. LoadAtom (dummy) ; EmPushAx () ;
  550. EmPopAx () ; EmPopBx () ; EmPopCx () ;
  551. EmPushAx () ; EmPushBx () ; EmPushCx () ;
  552. DropC (EmCall (TU_Move)) ; EmAddSp (6) ;
  553. ExpectDelim (')', ENoSemi) ;
  554. RETURN
  555. END ;
  556. IF which = BI_Seek THEN
  557. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  558. ParseExpr (arg) ;
  559. IF NOT OK () OR (arg.kind # 1) OR ((arg.cls # TFile) AND (arg.cls # TText) AND (arg.cls # TUntyp)) THEN
  560. Err (ETypeErr) ; RETURN
  561. END ;
  562. p := symtab [arg.idx].elem ; IF p = 0 THEN p := 1 END ;
  563. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  564. ExpectDelim (',', ENoSemi) ;
  565. ParseExpr (dummy) ;
  566. IF NOT OK () THEN RETURN END ;
  567. LoadAtom (dummy) ; EmPushAx () ;
  568. EmPopAx () ; EmPopBx () ;
  569. EmMovCxi (p) ; EmPushCx () ; EmPushAx () ; EmPushBx () ;
  570. DropC (EmCall (TU_Seek)) ; EmAddSp (6) ;
  571. ExpectDelim (')', ENoSemi) ; RETURN
  572. END ;
  573. IF which = BI_Erase THEN
  574. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  575. ParseExpr (arg) ;
  576. IF NOT OK () OR (arg.kind # 1) OR ((arg.cls # TFile) AND (arg.cls # TText) AND (arg.cls # TUntyp)) THEN
  577. Err (ETypeErr) ; RETURN
  578. END ;
  579. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  580. DropC (EmCall (TU_Erase)) ; EmAddSp (2) ;
  581. ExpectDelim (')', ENoSemi) ; RETURN
  582. END ;
  583. IF which = BI_Rename THEN
  584. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  585. ParseExpr (arg) ;
  586. IF NOT OK () OR (arg.kind # 1) OR ((arg.cls # TFile) AND (arg.cls # TText) AND (arg.cls # TUntyp)) THEN
  587. Err (ETypeErr) ; RETURN
  588. END ;
  589. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  590. ExpectDelim (',', ENoSemi) ;
  591. ParseExpr (dummy) ;
  592. IF NOT OK () OR (dummy.cls # TString) THEN Err (ETypeErr) ; RETURN END ;
  593. EmPushStrAddr (dummy) ;
  594. EmPopAx () ; EmPopBx () ; EmPushAx () ; EmPushBx () ;
  595. DropC (EmCall (TU_Rename)) ; EmAddSp (4) ;
  596. ExpectDelim (')', ENoSemi) ; RETURN
  597. END ;
  598. IF (which = BI_BlockRead) OR (which = BI_BlockWrite) THEN
  599. IF NOT MatchDelim ('(') THEN Err (ENoSemi) ; RETURN END ;
  600. ParseExpr (arg) ;
  601. IF NOT OK () OR (arg.kind # 1) OR ((arg.cls # TFile) AND (arg.cls # TText) AND (arg.cls # TUntyp)) THEN
  602. Err (ETypeErr) ; RETURN
  603. END ;
  604. p := symtab [arg.idx].elem ; IF p = 0 THEN p := 1 END ;
  605. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  606. ExpectDelim (',', ENoSemi) ;
  607. ParseExpr (dummy) ;
  608. IF NOT OK () OR ((dummy.kind # 1) AND (dummy.kind # 5)) THEN Err (ETypeErr) ; RETURN END ;
  609. IF dummy.kind = 5 THEN EmPushDi () ELSE EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) END ;
  610. ExpectDelim (',', ENoSemi) ;
  611. ParseExpr (dummy) ;
  612. IF NOT OK () THEN RETURN END ;
  613. LoadAtom (dummy) ; EmPushAx () ;
  614. IF MatchDelim (',') THEN
  615. ParseExpr (dummy) ;
  616. IF NOT OK () OR ((dummy.kind # 1) AND (dummy.kind # 5)) THEN Err (ETypeErr) ; RETURN END ;
  617. IF dummy.kind = 5 THEN EmPushDi () ELSE
  618. EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H)
  619. END
  620. ELSE
  621. EmMovAxi (0) ; EmPushAx ()
  622. END ;
  623. EmPopDx () ; EmPopAx () ; EmPopBx () ; EmPopCx () ;
  624. EmStoreVar (FALSE, symtab [tmpB].off, 2) ;
  625. EmPushDx () ; EmMovAxi (p) ; EmPushAx () ;
  626. EmLoadVar (FALSE, symtab [tmpB].off, 2) ; EmPushAx () ;
  627. EmPushBx () ; EmPushCx () ;
  628. IF which = BI_BlockRead THEN DropC (EmCall (TU_BlockRead)) ELSE DropC (EmCall (TU_BlockWrite)) END ;
  629. EmAddSp (10) ;
  630. ExpectDelim (')', ENoSemi) ; RETURN
  631. END ;
  632. IF which = BI_GetDir THEN
  633. IF MatchDelim ('(') THEN
  634. ParseExpr (arg) ;
  635. IF OK () AND ((arg.cls = TScalar) OR (arg.cls = TChar)) THEN
  636. LoadAtom (arg) ;
  637. EmPushAx () ; (* drive *)
  638. ExpectDelim (',', ENoSemi) ;
  639. ParseExpr (dummy) ;
  640. IF OK () AND (dummy.cls = TString) AND (dummy.kind = 1) THEN
  641. EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) ;
  642. DropC (EmCall (TU_GetDir)) ;
  643. EmAddSp (4)
  644. ELSIF OK () THEN
  645. Err (ETypeErr)
  646. END
  647. ELSIF OK () THEN
  648. Err (ETypeErr)
  649. END ;
  650. ExpectDelim (')', ENoSemi)
  651. ELSE
  652. Err (ENoSemi)
  653. END ;
  654. RETURN
  655. END ;
  656. IF (which = BI_Msdos) OR (which = BI_Intr) THEN
  657. IF MatchDelim ('(') THEN
  658. IF which = BI_Intr THEN
  659. ParseExpr (dummy) ;
  660. IF OK () AND NOT ((dummy.kind = 0) AND (W16 (dummy.imm) = 21H)) THEN
  661. Err (ENoLib) ;
  662. RETURN
  663. END ;
  664. ExpectDelim (',', ENoSemi)
  665. END ;
  666. ParseExpr (arg) ;
  667. IF OK () AND (arg.kind = 1) AND (arg.cls = TRecord) THEN
  668. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  669. DropC (EmCall (TU_Msdos)) ;
  670. EmAddSp (2)
  671. ELSIF OK () THEN
  672. Err (ETypeErr)
  673. END ;
  674. ExpectDelim (')', ENoSemi)
  675. ELSE
  676. Err (ENoSemi)
  677. END ;
  678. RETURN
  679. END ;
  680. IF which = BI_Assign THEN
  681. (* Assign(F, Name): the file variable's address and the counted
  682. string's address. The runtime copies the characters into the file
  683. control block and marks it closed; Reset/Rewrite do the DOS open.
  684. Name is pushed first, so the runtime sees F at [BP+4] and Name at
  685. [BP+6]. *)
  686. IF MatchDelim ('(') THEN
  687. ParseExpr (arg) ;
  688. IF OK () AND (arg.kind = 1) AND
  689. ((arg.cls = TFile) OR (arg.cls = TText) OR (arg.cls = TUntyp)) THEN
  690. ExpectDelim (',', ENoSemi) ;
  691. ParseExpr (dummy) ;
  692. IF OK () THEN
  693. IF dummy.cls = TString THEN
  694. EmPushStrAddr (dummy) ;
  695. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  696. DropC (EmCall (TU_FileAssign)) ;
  697. EmAddSp (4)
  698. ELSE
  699. Err (ETypeErr)
  700. END
  701. END
  702. ELSIF OK () THEN
  703. Err (ETypeErr)
  704. END ;
  705. ExpectDelim (')', ENoSemi)
  706. ELSE
  707. Err (ENoSemi)
  708. END ;
  709. RETURN
  710. END ;
  711. IF (which = BI_Reset) OR (which = BI_Rewrite) OR (which = BI_Close) THEN
  712. IF MatchDelim ('(') THEN
  713. ParseExpr (arg) ;
  714. IF OK () THEN
  715. IF (arg.kind = 1) AND
  716. ((arg.cls = TFile) OR (arg.cls = TText) OR (arg.cls = TUntyp)) THEN
  717. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  718. IF which = BI_Reset THEN
  719. DropC (EmCall (TU_FileReset))
  720. ELSIF which = BI_Rewrite THEN
  721. DropC (EmCall (TU_FileRewrite))
  722. ELSE
  723. DropC (EmCall (TU_FileClose))
  724. END ;
  725. EmAddSp (2)
  726. ELSE
  727. Err (ETypeErr)
  728. END
  729. END ;
  730. ExpectDelim (')', ENoSemi)
  731. ELSE
  732. Err (ENoSemi)
  733. END ;
  734. RETURN
  735. END ;
  736. reading := (which = BI_Read) OR (which = BI_ReadLn) ;
  737. nArgs := 0 ;
  738. IF MatchDelim ('(') THEN
  739. IF CurCh () # ')' THEN
  740. LOOP
  741. IF nArgs >= 16 THEN
  742. Err (ECompOvf) ;
  743. EXIT
  744. END ;
  745. ParseExpr (arg) ;
  746. IF NOT OK () THEN
  747. (* An argument that did not type-check has no value and arg.idx
  748. is not a symbol. The string arm below dereferences
  749. symtab[arg.idx] for ANY value whose class is TString - unlike
  750. the string assign in Assignment, which can tell a failed
  751. concat from a real one by its kind - so a `writeln' of a
  752. rejected expression read symtab with whatever idx held
  753. before and dereferenced it. That is exactly how
  754. `writeln('a' + 'b')' - two CHARs, a type error since
  755. v-TP3-STRCAT - segfaulted the compiler instead of reporting
  756. the error. ParseSub guards its subscript the same way (see
  757. `IF OK () THEN' before symtab[r.idx] there), and the
  758. statement loop stops at the next iteration, so leaving the
  759. rest of this WRITELN unparsed is harmless. *)
  760. RETURN
  761. END ;
  762. (* One argument at a time: parse it, give it its own push or its
  763. own runtime call, and only then read the next one. TPSRC8
  764. pwrloop does exactly this, and it has to - a computed argument
  765. exists only in AX, so parsing the argument after it, and the
  766. call made for THIS one, are both free to destroy it. Reading
  767. the whole list first and emitting afterwards printed the first
  768. argument as the last argument's value, and pushed an argument
  769. that an earlier inline string's call had already overwritten. *)
  770. skipArg := FALSE ;
  771. handled := FALSE ;
  772. IF (nArgs = 0) AND (arg.cls = TText) THEN
  773. (* A leading TEXT file argument. KBD is the console keyboard
  774. and this runtime's read entries already read the console, so
  775. the file is dropped and the rest of the argument list is read
  776. exactly as without it. *)
  777. IF reading AND (arg.kind = 1) AND symtab [arg.idx].isKbd THEN
  778. IF NOT MatchDelim (',') THEN
  779. Err (ENoSemi) ;
  780. RETURN
  781. END ;
  782. skipArg := TRUE
  783. ELSIF reading THEN
  784. (* Text-file reads. A number is read as a whitespace-
  785. delimited token and handed to ValInt/ValReal; a string
  786. still uses the line reader. Readln consumes the rest
  787. of the line after all numeric arguments. *)
  788. IF NOT MatchDelim (',') THEN
  789. Err (ENoSemi) ;
  790. RETURN
  791. END ;
  792. LOOP
  793. ParseExpr (dummy) ;
  794. IF NOT OK () THEN
  795. RETURN
  796. END ;
  797. IF dummy.cls = TString THEN
  798. IF (dummy.kind = 1) OR (dummy.kind = 5) THEN
  799. EmMovAxi ((VarSize (dummy) - 1) MOD 10000H) ;
  800. EmPushAx () ;
  801. EmPushStrAddr (dummy) ;
  802. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  803. DropC (EmCall (TU_FileReadln)) ;
  804. EmAddSp (6)
  805. ELSE
  806. Err (ETypeErr) ;
  807. RETURN
  808. END
  809. ELSIF IsCharArray (dummy) AND (dummy.kind = 1) THEN
  810. scratch := NewScratchStr () ;
  811. k := 1 ;
  812. WHILE k <= VarSize (dummy) DO
  813. EmMovAxi (ORD (' ')) ;
  814. EmStoreVar (FALSE, (symtab [scratch].off + k) MOD 10000H, 1) ;
  815. INC (k)
  816. END ;
  817. EmMovAxi (VarSize (dummy)) ;
  818. EmPushAx () ;
  819. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  820. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  821. DropC (EmCall (TU_FileReadln)) ;
  822. EmAddSp (6) ;
  823. EmPushVarAddr (FALSE, (symtab [scratch].off + 1) MOD 10000H) ;
  824. EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) ;
  825. EmMovCxi (VarSize (dummy)) ;
  826. DropC (EmCall (TU_MemCopy)) ;
  827. EmAddSp (4)
  828. ELSIF (dummy.kind = 1) AND
  829. ((dummy.cls = TScalar) OR (dummy.cls = TReal)) THEN
  830. scratch := NewScratchStr () ;
  831. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  832. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  833. DropC (EmCall (TU_FileRdtok)) ;
  834. EmAddSp (4) ;
  835. EmPushVarAddr (FALSE, symtab [tmpA].off) ;
  836. EmPushSymAddr (dummy.idx, (symtab [dummy.idx].off + dummy.boff) MOD 10000H) ;
  837. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  838. IF dummy.cls = TReal THEN
  839. DropC (EmCall (TU_ValReal))
  840. ELSE
  841. DropC (EmCall (TU_ValInt))
  842. END ;
  843. EmAddSp (6)
  844. ELSE
  845. Err (ETypeErr) ;
  846. RETURN
  847. END ;
  848. IF NOT MatchDelim (',') THEN
  849. EXIT
  850. END
  851. END ;
  852. IF which = BI_ReadLn THEN
  853. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  854. DropC (EmCall (TU_FileRdln)) ;
  855. EmAddSp (2)
  856. END ;
  857. ExpectDelim (')', ENoSemi) ;
  858. RETURN
  859. ELSE
  860. (* Text-file output: Writeln(File) is CRLF, Write(File, C)
  861. is one CHAR, and writeln(File, S) is S followed by CRLF.
  862. Write each byte range with DOS AH=40h; a string argument
  863. carries its own length byte, a one-CHAR literal becomes a
  864. tiny code-resident <1><char>. *)
  865. IF (arg.kind # 1) OR (symtab [arg.idx].cls # TText) THEN
  866. Err (ETypeErr) ;
  867. RETURN
  868. END ;
  869. IF MatchDelim (',') THEN
  870. LOOP
  871. ParseExpr (dummy) ;
  872. IF NOT OK () THEN
  873. RETURN
  874. END ;
  875. IF dummy.cls = TString THEN
  876. IF MatchDelim (':') THEN
  877. ParseExpr (errV) ;
  878. IF NOT OK () THEN
  879. RETURN
  880. END ;
  881. LoadAtom (errV) ;
  882. EmPushAx () ; (* width *)
  883. EmPushStrAddr (dummy) ; (* source *)
  884. scratch := NewScratchStr () ;
  885. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  886. DropC (EmCall (TU_StrFmtW)) ;
  887. EmAddSp (6) ;
  888. EmFileIo (FALSE, symtab [arg.idx].local,
  889. symtab [arg.idx].off, 1, FALSE,
  890. symtab [scratch].off, 0, TRUE)
  891. ELSIF dummy.kind = 1 THEN
  892. EmFileIo (FALSE, symtab [arg.idx].local,
  893. symtab [arg.idx].off, 1,
  894. symtab [dummy.idx].local,
  895. (symtab [dummy.idx].off + dummy.boff)
  896. MOD 10000H,
  897. 0, TRUE)
  898. ELSIF dummy.kind = 5 THEN
  899. EmFileIo (FALSE, symtab [arg.idx].local,
  900. symtab [arg.idx].off, 5, FALSE,
  901. 0, 0, TRUE)
  902. ELSIF dummy.kind = 3 THEN
  903. EmFileIo (FALSE, symtab [arg.idx].local,
  904. symtab [arg.idx].off, 4, FALSE,
  905. EmStrBlob (dummy), 0, TRUE)
  906. ELSE
  907. Err (ENoLib) ;
  908. RETURN
  909. END
  910. ELSIF (dummy.cls = TChar) OR
  911. ((dummy.cls = TScalar) AND dummy.chr) THEN
  912. IF dummy.kind = 1 THEN
  913. EmFileIo (FALSE, symtab [arg.idx].local,
  914. symtab [arg.idx].off, 1,
  915. symtab [dummy.idx].local,
  916. (symtab [dummy.idx].off + dummy.boff)
  917. MOD 10000H,
  918. 1, FALSE)
  919. ELSIF dummy.kind = 5 THEN
  920. EmFileIo (FALSE, symtab [arg.idx].local,
  921. symtab [arg.idx].off, 5, FALSE,
  922. 0, 1, FALSE)
  923. ELSIF dummy.kind = 2 THEN
  924. EmStoreVar (FALSE, symtab [tmpA].off, 1) ;
  925. EmFileIo (FALSE, symtab [arg.idx].local,
  926. symtab [arg.idx].off, 1, FALSE,
  927. symtab [tmpA].off, 1, FALSE)
  928. ELSIF dummy.kind = 0 THEN
  929. EmFileIo (FALSE, symtab [arg.idx].local,
  930. symtab [arg.idx].off, 4, FALSE,
  931. EmInlineCh (VAL (CARDINAL, dummy.imm)),
  932. 0, TRUE)
  933. ELSE
  934. Err (ENoLib) ;
  935. RETURN
  936. END
  937. ELSIF dummy.cls = TReal THEN
  938. EmPushRealAddr (dummy) ;
  939. IF MatchDelim (':') THEN
  940. ParseExpr (errV) ;
  941. IF OK () THEN LoadAtom (errV) END ;
  942. IF MatchDelim (':') THEN
  943. EmPushAx () ;
  944. ParseExpr (errV) ;
  945. IF OK () THEN
  946. LoadAtom (errV) ;
  947. EmMovChAl () ;
  948. EmPopAx () ;
  949. EmMovClAl ()
  950. END
  951. ELSIF OK () THEN
  952. EmMovClAl () ;
  953. EmMovChImm (0FFH)
  954. END
  955. ELSE
  956. EmMovCxi (0FF12H)
  957. END ;
  958. EmMovAxCx () ;
  959. EmStoreVar (FALSE, symtab [tmpA].off, 2) ;
  960. scratch := NewScratchStr () ;
  961. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  962. EmPopAx () ;
  963. EmPopBx () ;
  964. EmPushAx () ;
  965. EmPushBx () ;
  966. EmLoadVar (FALSE, symtab [tmpA].off, 2) ;
  967. EmXchgAxCx () ;
  968. DropC (EmCall (TU_RealFmt)) ;
  969. EmAddSp (4) ;
  970. EmFileIo (FALSE, symtab [arg.idx].local,
  971. symtab [arg.idx].off, 1, FALSE,
  972. symtab [scratch].off, 0, TRUE)
  973. ELSIF dummy.cls = TScalar THEN
  974. (* integer with the optional TP3 `:width' *)
  975. LoadAtom (dummy) ;
  976. EmPushAx () ;
  977. IF MatchDelim (':') THEN
  978. ParseExpr (errV) ;
  979. IF NOT OK () THEN
  980. RETURN
  981. END ;
  982. LoadAtom (errV) ;
  983. EmPushAx ()
  984. ELSE
  985. EmMovAxi (0) ;
  986. EmPushAx ()
  987. END ;
  988. scratch := NewScratchStr () ;
  989. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  990. DropC (EmCall (TU_IntFmtW)) ;
  991. EmAddSp (6) ;
  992. EmFileIo (FALSE, symtab [arg.idx].local,
  993. symtab [arg.idx].off, 1, FALSE,
  994. symtab [scratch].off, 0, TRUE)
  995. ELSE
  996. Err (ENoLib) ;
  997. RETURN
  998. END ;
  999. IF NOT MatchDelim (',') THEN
  1000. EXIT
  1001. END
  1002. END
  1003. END ;
  1004. IF which = BI_WriteLn THEN
  1005. EmFileIo (FALSE, symtab [arg.idx].local,
  1006. symtab [arg.idx].off, 4, FALSE,
  1007. EmInlineCrLf (), 0, TRUE)
  1008. END ;
  1009. ExpectDelim (')', ENoSemi) ;
  1010. RETURN
  1011. END
  1012. ELSIF (nArgs = 0) AND (arg.cls = TFile) THEN
  1013. (* Typed-file block I/O: `write(MCfile, Sheet[I,J])' and the
  1014. symmetric `read(MCfile, Sheet[FX,FY])'. TP3 moves exactly
  1015. one element per call, so consume the single data argument
  1016. here and emit the DOS block call. The element size comes
  1017. from the data expression and must agree with the file's
  1018. declared element size; a mismatch is a type error, not a
  1019. reason to write the wrong number of bytes. *)
  1020. IF (arg.kind # 1) OR NOT MatchDelim (',') THEN
  1021. Err (ETypeErr) ;
  1022. RETURN
  1023. END ;
  1024. ParseExpr (dummy) ;
  1025. IF NOT OK () THEN
  1026. RETURN
  1027. END ;
  1028. IF ((dummy.kind # 1) AND (dummy.kind # 5))
  1029. OR (VarSize (dummy) # symtab [arg.idx].elem) THEN
  1030. Err (ETypeErr) ;
  1031. RETURN
  1032. END ;
  1033. IF dummy.kind = 5 THEN
  1034. EmFileIo (reading, symtab [arg.idx].local,
  1035. symtab [arg.idx].off, 5, FALSE, 0, VarSize (dummy),
  1036. FALSE)
  1037. ELSE
  1038. EmFileIo (reading, symtab [arg.idx].local,
  1039. symtab [arg.idx].off, 1, symtab [dummy.idx].local,
  1040. (symtab [dummy.idx].off + dummy.boff) MOD 10000H,
  1041. VarSize (dummy), FALSE)
  1042. END ;
  1043. ExpectDelim (')', ENoSemi) ;
  1044. RETURN
  1045. END ;
  1046. IF NOT skipArg AND IsCharArray (arg) AND (arg.kind = 1) THEN
  1047. IF reading THEN
  1048. (* Readln(Place): read through a temporary counted string,
  1049. then copy its fixed-width bytes over the char array.
  1050. The temporary is pre-filled with spaces so a short line
  1051. leaves the array padded, and rdstr leaves the newline
  1052. pending for the Readln wrapper. *)
  1053. scratch := NewScratchStr () ;
  1054. k := 1 ;
  1055. WHILE k <= VarSize (arg) DO
  1056. EmMovAxi (ORD (' ')) ;
  1057. EmStoreVar (FALSE, (symtab [scratch].off + k) MOD 10000H, 1) ;
  1058. INC (k)
  1059. END ;
  1060. EmPushVarAddr (FALSE, symtab [scratch].off) ;
  1061. EmMovCxi (VarSize (arg)) ;
  1062. DropC (EmCall (TU_RdStr)) ;
  1063. EmAddSp (2) ;
  1064. EmPushVarAddr (FALSE, (symtab [scratch].off + 1) MOD 10000H) ;
  1065. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H) ;
  1066. EmMovCxi (VarSize (arg)) ;
  1067. DropC (EmCall (TU_MemCopy)) ;
  1068. EmAddSp (4)
  1069. ELSE
  1070. (* Write a fixed char array one byte at a time. *)
  1071. k := 0 ;
  1072. WHILE k < VarSize (arg) DO
  1073. LoadSymVar (arg.idx, (symtab [arg.idx].off + arg.boff + k) MOD 10000H, 1) ;
  1074. EmMovAh0 () ;
  1075. EmPushAx () ;
  1076. DropC (EmCall (TU_WrChar)) ;
  1077. EmAddSp (2) ;
  1078. INC (k)
  1079. END
  1080. END ;
  1081. handled := TRUE
  1082. END ;
  1083. IF NOT skipArg AND NOT handled THEN
  1084. pushed := TRUE ; (* default: value is on the stack -> call + pop *)
  1085. argBytes := 2 ;
  1086. IF reading THEN
  1087. IF (arg.kind # 1) AND (arg.kind # 5) THEN
  1088. Err (ETypeErr) ; (* READ needs a variable *)
  1089. RETURN
  1090. END ;
  1091. IF arg.kind = 5 THEN
  1092. (* A DI-addressed element: its TYPE is on the expression
  1093. (WithLookup/ParseSub set it), because the symbol is the
  1094. containing array/record. s[i] is TChar; a WITH string
  1095. field is TString. *)
  1096. acls := arg.cls
  1097. ELSE
  1098. acls := symtab [arg.idx].cls ;
  1099. (* s[i] is a CHAR variable, so read(s[i]) must read one byte
  1100. at off+boff, not the whole string at off. arg.cls is
  1101. TChar (set by ParseSub) while the symbol's own class is
  1102. still TString, which is exactly how the element is told
  1103. from the whole string; the address push has to carry boff.
  1104. For a plain variable arg.boff is 0, so nothing changes. *)
  1105. IF (acls = TString) AND (arg.cls = TChar) THEN
  1106. acls := TChar
  1107. END
  1108. END ;
  1109. IF arg.kind = 5 THEN
  1110. (* A variable index: ParseSub computed the element's address
  1111. into DI, and nothing has been emitted since, so that is
  1112. the address READ wants. A constant index folds into boff
  1113. and takes the LEA path below. *)
  1114. EmPushDi ()
  1115. ELSIF symtab [arg.idx].isVar THEN
  1116. (* A VAR parameter: its slot holds the address READ must
  1117. store through, not the address of the slot. *)
  1118. IF arg.boff # 0 THEN
  1119. Err (ENoLib) ;
  1120. RETURN
  1121. END ;
  1122. LoadSymVar (arg.idx, symtab [arg.idx].off, 2) ;
  1123. EmPushAx ()
  1124. ELSE
  1125. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  1126. END ;
  1127. IF acls = TString THEN
  1128. (* readln(s)/read(s) of a string variable: TPSRC8 prdstr
  1129. pushes the address, emits MOV CL,max_len and CALL
  1130. xrdstr. Here the limit is a full MOV CX,imm16 (this
  1131. runtime has no byte-register CH/CL setup), and rdstr
  1132. leaves the terminator pending so the readln wrapper can
  1133. consume the newline. *)
  1134. EmMovCxi ((VarSize (arg) - 1) MOD 10000H) ;
  1135. ent := TU_RdStr
  1136. ELSIF acls = TReal THEN
  1137. ent := TU_RdInt (* real reads: not yet *)
  1138. ELSIF acls = TBool THEN
  1139. (* TP3 refuses boolean I/O: TPSRC8 prderrtp is error 66,
  1140. "I/O not allowed here", and the runtime has no xrdbool
  1141. at all. rdbool stores a WORD, so one byte of an
  1142. accepted read would land on the variable AFTER b - the
  1143. exact overflow t37_readbyte pins for a byte. TP3's
  1144. answer to the whole class is the error; so is ours. *)
  1145. Err (EIoHere) ;
  1146. RETURN
  1147. ELSIF acls = TChar THEN
  1148. ent := TU_RdChar (* one byte, not a word *)
  1149. ELSIF acls = TScalar THEN
  1150. (* Dispatch on the SIZE, as TPSRC8 prdnum does:
  1151. `CMP varsize,#$01 / JA prdemit' keeps xrdint for
  1152. anything wider and falls through to `MOV AX,#xrdbyte'
  1153. for a one-byte variable. A BYTE is still TScalar (it
  1154. is *written* as an integer, as in TP3), but reading one
  1155. through rdint stored a word - and the second byte of
  1156. that word is the byte that FOLLOWS the variable. *)
  1157. IF VarSize (arg) = 1 THEN
  1158. ent := TU_RdByte
  1159. ELSE
  1160. ent := TU_RdInt
  1161. END
  1162. ELSE
  1163. Err (ETypeErr) ;
  1164. RETURN
  1165. END
  1166. ELSE
  1167. acls := arg.cls ;
  1168. IF (acls = TString) AND (arg.kind = 3) THEN
  1169. (* An inline string literal. TP3 TPSRC8 pwrinlin special-cases a
  1170. literal that is followed directly by ',' or ')' - i.e. an
  1171. argument, not an expression - and emits
  1172. CALL wrtinl <length byte> <characters...>
  1173. with no stack argument at all; wrtinl reads the length from the
  1174. return address and returns to just past the last character.
  1175. A following `:width' needs the ordinary address form, so
  1176. wrstrw can pad it; the no-width path keeps the compact
  1177. inline byte stream. *)
  1178. IF strLen [arg.strx] > 255 THEN
  1179. (* The length is one byte, so a literal of 256 characters or
  1180. more would wrap: 300 characters emitted behind a length of
  1181. 44, and the runtime would print 44 of them and silently drop
  1182. the rest. TP3 strings are at most 255 characters, so refuse
  1183. rather than truncate. *)
  1184. Err (EConstRange) ;
  1185. RETURN
  1186. END ;
  1187. IF MatchDelim (':') THEN
  1188. (* A string literal with a field width: make its bytes an
  1189. ordinary code-resident address, then pad before it. *)
  1190. EmPushStrAddr (arg) ;
  1191. ParseExpr (dummy) ;
  1192. IF NOT OK () THEN
  1193. RETURN
  1194. END ;
  1195. LoadAtom (dummy) ;
  1196. EmPushAx () ;
  1197. ent := TU_WrStrW ;
  1198. argBytes := 4
  1199. ELSE
  1200. DropC (EmCall (TU_WrInl)) ;
  1201. Ebyte (VAL (BYTE, strLen [arg.strx])) ;
  1202. n := 0 ;
  1203. WHILE n < strLen [arg.strx] DO
  1204. Ebyte (VAL (BYTE, ORD (strPool [strOff [arg.strx] + n]))) ;
  1205. INC (n)
  1206. END ;
  1207. pushed := FALSE (* nothing was pushed for this one *)
  1208. END
  1209. ELSE
  1210. IF acls = TString THEN
  1211. (* A string *variable*. Its value IS its address, so it is
  1212. passed exactly the way READ passes an address: push it
  1213. and call the owner of the layout - wrstr - which reads
  1214. the length byte and prints that many characters. TPSRC8
  1215. pwrstr/pwrchk route a string argument to xwrtstr, and
  1216. xwrtstr reaches its argument through BP just like WrChar
  1217. does. The only difference from the inline-literal arm
  1218. above is where the bytes live: a literal's are in the
  1219. code stream (wrtinl) and a variable's are in the data
  1220. segment or the frame (wrstr). *)
  1221. (* off + boff: a string FIELD of a record/array has a
  1222. non-zero boff, and the address is the field's, not the
  1223. containing variable's. `writeln (g[2,3].S)' printed
  1224. the empty first element before this.
  1225. A DI-addressed field (`with Sheet[I,J] do
  1226. Write(Contents)') already HAS its address in DI. *)
  1227. IF arg.kind = 5 THEN
  1228. EmPushDi ()
  1229. ELSIF symtab [arg.idx].isVar THEN
  1230. (* VAR string: the slot holds the address of the
  1231. caller's characters; push that, not the slot. *)
  1232. LoadSymVar (arg.idx, symtab [arg.idx].off, 2) ;
  1233. EmPushAx ()
  1234. ELSE
  1235. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  1236. END ;
  1237. ent := TU_WrStr ;
  1238. IF MatchDelim (':') THEN
  1239. ParseExpr (dummy) ;
  1240. IF NOT OK () THEN
  1241. RETURN
  1242. END ;
  1243. LoadAtom (dummy) ;
  1244. EmPushAx () ;
  1245. ent := TU_WrStrW ;
  1246. argBytes := 4
  1247. END
  1248. ELSE
  1249. IF acls = TReal THEN
  1250. (* A REAL is six bytes, so the writer takes its
  1251. ADDRESS, not its value: push the address first,
  1252. then the width, then the decimals. The entry is
  1253. real_write, with [BP+4]=decimals, [BP+6]=width and
  1254. [BP+8]=real. TP3's defaults are width 18 and
  1255. decimals FFFFh (its scientific mode); a width with
  1256. no second colon keeps that default, which is
  1257. exactly what TPSRC8 pwrdef/pwrdef3 push. *)
  1258. IF arg.kind = 5 THEN
  1259. EmPushDi ()
  1260. ELSE
  1261. EmPushSymAddr (arg.idx, (symtab [arg.idx].off + arg.boff) MOD 10000H)
  1262. END ;
  1263. IF MatchDelim (':') THEN
  1264. ParseExpr (dummy) ;
  1265. IF NOT OK () THEN
  1266. RETURN
  1267. END ;
  1268. LoadAtom (dummy) ;
  1269. EmPushAx () ;
  1270. IF MatchDelim (':') THEN
  1271. ParseExpr (dummy) ;
  1272. IF NOT OK () THEN
  1273. RETURN
  1274. END ;
  1275. LoadAtom (dummy) ;
  1276. EmPushAx ()
  1277. ELSE
  1278. EmMovAxi (0FFFFH) ;
  1279. EmPushAx ()
  1280. END
  1281. ELSE
  1282. EmMovAxi (18) ;
  1283. EmPushAx () ;
  1284. EmMovAxi (0FFFFH) ;
  1285. EmPushAx ()
  1286. END ;
  1287. ent := TU_RealWrite ;
  1288. argBytes := 6
  1289. ELSE
  1290. LoadAtom (arg) ;
  1291. EmPushAx () ;
  1292. IF arg.chr THEN
  1293. ent := TU_WrChar (* 'a' - one char, not 97 *)
  1294. ELSIF acls = TBool THEN
  1295. ent := TU_WrBool
  1296. ELSIF acls = TChar THEN
  1297. ent := TU_WrChar (* c : char - one char *)
  1298. ELSIF acls = TScalar THEN
  1299. ent := TU_WrInt
  1300. ELSE
  1301. Err (ETypeErr) ;
  1302. RETURN
  1303. END ;
  1304. IF MatchDelim (':') THEN
  1305. (* Field width: `writeln(I:2)'. TP3 evaluates the
  1306. width (CALC passes `FW', a variable), so it is
  1307. parsed and pushed AFTER the value: width at BP+4,
  1308. value at BP+6. Char, BOOLEAN and integer each
  1309. have their own width entry; a second colon is
  1310. only legal for REAL, handled above. *)
  1311. ParseExpr (dummy) ;
  1312. IF NOT OK () THEN
  1313. RETURN
  1314. END ;
  1315. LoadAtom (dummy) ;
  1316. EmPushAx () ;
  1317. IF MatchDelim (':') THEN
  1318. Err (ENoLib) ;
  1319. RETURN
  1320. END ;
  1321. IF arg.chr OR (acls = TChar) THEN
  1322. ent := TU_WrCharW
  1323. ELSIF acls = TBool THEN
  1324. ent := TU_WrBoolW
  1325. ELSE
  1326. ent := TU_WrIntW
  1327. END ;
  1328. argBytes := 4
  1329. END
  1330. END
  1331. END
  1332. END
  1333. END ;
  1334. IF pushed THEN
  1335. DropC (EmCall (ent)) ;
  1336. EmAddSp (argBytes) (* 2, or 4 with a width *)
  1337. END ;
  1338. INC (nArgs)
  1339. END ;
  1340. IF NOT skipArg THEN
  1341. IF NOT MatchDelim (',') THEN
  1342. EXIT
  1343. END
  1344. END
  1345. END ;
  1346. IF NOT MatchDelim (')') THEN
  1347. Err (ENoSemi) ;
  1348. RETURN
  1349. END
  1350. ELSE
  1351. DropCh (GetCh ())
  1352. END
  1353. END ;
  1354. IF reading AND (nArgs = 0) THEN
  1355. (* readln with no variable: just skip to the next line *)
  1356. DropC (EmCall (TU_RdLn)) ;
  1357. RETURN
  1358. END ;
  1359. IF which = BI_WriteLn THEN
  1360. DropC (EmCall (TU_WrLn))
  1361. ELSIF which = BI_ReadLn THEN
  1362. DropC (EmCall (TU_RdLn))
  1363. END
  1364. END IoCall ;
  1365. END CompilerIO.