Shell.mod 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. MODULE Shell ;
  2. (* TP3 main-menu shell. Draws the Turbo Pascal 3.0 screen with ANSI
  3. escapes and dispatches on the same command letters as the original
  4. (TPSRC4 kmenu / kcmdtab). Editor, compiler and interpreter are
  5. placeholders for now; the shell logic (files, directory, options,
  6. save/load with .BAK) mirrors the original closely. *)
  7. FROM Term IMPORT
  8. Open, Close, ClrScr, GotoXY, PutCh, PutStr, PutCard, PutLongCard,
  9. ScrnWide, Marked, Normal, GetCh, Beep ;
  10. FROM Posix IMPORT
  11. read, write, open, close, unlink, rename,
  12. getcwd, chdir, opendir, readdir, closedir, statvfs,
  13. Dir, dirent, statvfsbuf ;
  14. FROM Compiler IMPORT Compile, CodeBytes, DataBytes, ImageBytes, ImageByteAt ;
  15. FROM Linker IMPORT LinkSize, WriteCom ;
  16. FROM Editor IMPORT Run, GotoOffset ;
  17. FROM TextBuf IMPORT
  18. TextLimit, Clear, Length, CharAt, InsertCh ;
  19. (* The 86 suffix on these three is not decoration: ISO Modula-2 has neither
  20. import renaming nor a procedure-local import, so every import in this
  21. module shares one flat namespace, and `Clear' and `Run' were already
  22. TextBuf's and Editor's. Exec86.def's header records the same fact from
  23. the other side. *)
  24. FROM Exec86 IMPORT Clear86, Poke86, Run86 ;
  25. FROM SYSTEM IMPORT ADR, ADDRESS, BYTE ;
  26. CONST
  27. O_RDONLY = 0 ; (* linux *)
  28. VAR
  29. drive : CHAR ;
  30. workName : ARRAY [0..255] OF CHAR ;
  31. mainName : ARRAY [0..255] OF CHAR ;
  32. changed : BOOLEAN ;
  33. codeDest : CARDINAL ; (* 0=Memory, 1=COM, 2=CHN *)
  34. errNo, errPos : CARDINAL ;
  35. minCode, minData, minStack, maxStack : CARDINAL ;
  36. paramLine : ARRAY [0..255] OF CHAR ;
  37. (* ------------------------------------------------------------------ *)
  38. (* string helpers *)
  39. (* ------------------------------------------------------------------ *)
  40. PROCEDURE StrLen (VAR s : ARRAY OF CHAR) : CARDINAL ;
  41. VAR i : CARDINAL ;
  42. BEGIN
  43. i := 0 ;
  44. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  45. INC (i)
  46. END ;
  47. RETURN i
  48. END StrLen ;
  49. PROCEDURE StrClear (VAR s : ARRAY OF CHAR) ;
  50. BEGIN
  51. s [0] := 0C
  52. END StrClear ;
  53. PROCEDURE StrCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
  54. VAR i : CARDINAL ;
  55. BEGIN
  56. i := 0 ;
  57. LOOP
  58. IF i > HIGH (dst) THEN
  59. dst [HIGH (dst)] := 0C ;
  60. EXIT
  61. END ;
  62. IF i > HIGH (src) THEN
  63. dst [i] := 0C ;
  64. EXIT
  65. END ;
  66. IF src [i] = 0C THEN
  67. dst [i] := 0C ;
  68. EXIT
  69. END ;
  70. dst [i] := src [i] ;
  71. INC (i)
  72. END
  73. END StrCopy ;
  74. PROCEDURE StrAppend (VAR dst : ARRAY OF CHAR ; suffix : ARRAY OF CHAR) ;
  75. VAR i, j : CARDINAL ;
  76. BEGIN
  77. i := StrLen (dst) ;
  78. j := 0 ;
  79. LOOP
  80. IF i > HIGH (dst) THEN
  81. EXIT
  82. END ;
  83. IF j > HIGH (suffix) THEN
  84. dst [i] := 0C ;
  85. EXIT
  86. END ;
  87. IF suffix [j] = 0C THEN
  88. dst [i] := 0C ;
  89. EXIT
  90. END ;
  91. dst [i] := suffix [j] ;
  92. INC (i) ;
  93. INC (j)
  94. END
  95. END StrAppend ;
  96. PROCEDURE StrEq (VAR a, b : ARRAY OF CHAR) : BOOLEAN ;
  97. VAR i : CARDINAL ;
  98. BEGIN
  99. i := 0 ;
  100. LOOP
  101. IF (i > HIGH (a)) OR (i > HIGH (b)) THEN
  102. RETURN FALSE
  103. END ;
  104. IF (a [i] = 0C) AND (b [i] = 0C) THEN
  105. RETURN TRUE
  106. END ;
  107. IF a [i] # b [i] THEN
  108. RETURN FALSE
  109. END ;
  110. INC (i)
  111. END ;
  112. RETURN FALSE
  113. END StrEq ;
  114. PROCEDURE Upper (ch : CHAR) : CHAR ;
  115. BEGIN
  116. IF (ch >= "a") AND (ch <= "z") THEN
  117. RETURN CHR (ORD (ch) - (ORD ("a") - ORD ("A")))
  118. END ;
  119. RETURN ch
  120. END Upper ;
  121. PROCEDURE CrLf ;
  122. BEGIN
  123. PutCh (CHR (13)) ;
  124. PutCh (CHR (10))
  125. END CrLf ;
  126. PROCEDURE GetCwd (VAR buf : ARRAY OF CHAR) ;
  127. VAR i : CARDINAL ;
  128. BEGIN
  129. IF getcwd (ADR (buf), 4096) = NIL THEN
  130. buf [0] := 0C
  131. END
  132. END GetCwd ;
  133. (* ------------------------------------------------------------------ *)
  134. (* marks one command letter then prints the rest of the label *)
  135. (* ------------------------------------------------------------------ *)
  136. PROCEDURE Key (letter, rest : ARRAY OF CHAR) ;
  137. BEGIN
  138. Marked ;
  139. PutCh (letter [0]) ;
  140. Normal ;
  141. PutStr (rest)
  142. END Key ;
  143. (* ------------------------------------------------------------------ *)
  144. (* input line editing (no echo from termios): CR or LF ends the line, *)
  145. (* backspace deletes the last char, the result is stored 0C-terminated*)
  146. (* ------------------------------------------------------------------ *)
  147. PROCEDURE ReadLine (VAR s : ARRAY OF CHAR) : CARDINAL ;
  148. VAR len : CARDINAL ;
  149. ch : CHAR ;
  150. BEGIN
  151. StrClear (s) ;
  152. len := 0 ;
  153. LOOP
  154. GetCh (ch) ;
  155. IF (ch = CHR (13)) OR (ch = CHR (10)) THEN
  156. PutCh (CHR (13)) ;
  157. PutCh (CHR (10)) ;
  158. EXIT
  159. ELSIF (ch = CHR (8)) OR (ch = CHR (127)) THEN
  160. IF len > 0 THEN
  161. DEC (len) ;
  162. PutStr (" ") (* backspace over the char *)
  163. END
  164. ELSIF ch = CHR (27) THEN
  165. (* ESC: cancel the current edit *)
  166. EXIT
  167. ELSIF (ch >= " ") AND (ch <= "~") AND (len < HIGH (s)) THEN
  168. s [len] := ch ;
  169. INC (len) ;
  170. PutCh (ch)
  171. END
  172. END ;
  173. s [len] := 0C ;
  174. RETURN len
  175. END ReadLine ;
  176. PROCEDURE Pause ;
  177. VAR ch : CHAR ;
  178. BEGIN
  179. Beep ;
  180. GetCh (ch)
  181. END Pause ;
  182. PROCEDURE WaitEsc ;
  183. VAR ch : CHAR ;
  184. BEGIN
  185. LOOP
  186. GetCh (ch) ;
  187. IF (ch = CHR (27)) OR (ch = "q") OR (ch = "Q") THEN
  188. EXIT
  189. END
  190. END
  191. END WaitEsc ;
  192. (* return true if the user confirms with Y / y *)
  193. PROCEDURE Confirm (prompt : ARRAY OF CHAR) : BOOLEAN ;
  194. VAR ch : CHAR ;
  195. BEGIN
  196. PutStr (prompt) ;
  197. GetCh (ch) ;
  198. IF (ch = "y") OR (ch = "Y") THEN
  199. CrLf ;
  200. RETURN TRUE
  201. END ;
  202. CrLf ;
  203. RETURN FALSE
  204. END Confirm ;
  205. (* ------------------------------------------------------------------ *)
  206. (* file name handling *)
  207. (* ------------------------------------------------------------------ *)
  208. (* Does the name contain a '.' ? *)
  209. PROCEDURE HasExt (VAR s : ARRAY OF CHAR) : BOOLEAN ;
  210. VAR i : CARDINAL ;
  211. BEGIN
  212. i := 0 ;
  213. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  214. IF s [i] = "." THEN
  215. RETURN TRUE
  216. END ;
  217. INC (i)
  218. END ;
  219. RETURN FALSE
  220. END HasExt ;
  221. (* replaces the current work file, appending .PAS when needed *)
  222. PROCEDURE SetWorkName (VAR s : ARRAY OF CHAR) ;
  223. BEGIN
  224. StrCopy (workName, s) ;
  225. IF NOT HasExt (workName) THEN
  226. StrAppend (workName, ".PAS")
  227. END
  228. END SetWorkName ;
  229. (* The .COM name for the current work file: the same path with .COM.
  230. TP3 derives the output name the same way (it hands the destination to the
  231. overlay loader with the extension swapped), so FOO.PAS links to FOO.COM
  232. in whatever directory the source lives, not to the current directory.
  233. The dot is found by scanning for the LAST one, not the first: a Pascal
  234. path may well contain a directory component with a dot in it, and taking
  235. the first would truncate the directory and write the .COM somewhere else. *)
  236. PROCEDURE ComName (VAR dst : ARRAY OF CHAR) ;
  237. VAR i, dot : CARDINAL ;
  238. BEGIN
  239. IF StrLen (workName) = 0 THEN
  240. StrClear (dst) ;
  241. RETURN
  242. END ;
  243. StrCopy (dst, workName) ;
  244. dot := 0 ;
  245. i := 0 ;
  246. WHILE (i <= HIGH (workName)) AND (workName [i] # 0C) DO
  247. IF workName [i] = "." THEN
  248. dot := i
  249. END ;
  250. INC (i)
  251. END ;
  252. IF dot = 0 THEN
  253. (* no extension at all - just append *)
  254. StrCopy (dst, workName) ;
  255. StrAppend (dst, ".COM")
  256. ELSE
  257. (* cut at the dot, then append *)
  258. i := dot ;
  259. WHILE (i <= HIGH (dst)) DO
  260. dst [i] := 0C ;
  261. INC (i)
  262. END ;
  263. StrAppend (dst, ".COM")
  264. END
  265. END ComName ;
  266. (* ------------------------------------------------------------------ *)
  267. (* buffered save of the work file: .BAK = old version, then ^Z *)
  268. (* ------------------------------------------------------------------ *)
  269. PROCEDURE SaveWorkFile ;
  270. VAR fd : INTEGER ;
  271. path : ARRAY [0..511] OF CHAR ;
  272. w : LONGINT ;
  273. i : CARDINAL ;
  274. msg : ARRAY [0..255] OF CHAR ;
  275. b : BYTE ;
  276. BEGIN
  277. IF StrLen (workName) = 0 THEN
  278. PutStr (" ...work file not set") ;
  279. CrLf ;
  280. Pause ;
  281. RETURN
  282. END ;
  283. StrClear (msg) ;
  284. StrAppend (msg, "Saving A:") ;
  285. StrAppend (msg, workName) ;
  286. PutStr (msg) ;
  287. StrClear (path) ;
  288. StrAppend (path, workName) ;
  289. StrAppend (path, ".BAK") ;
  290. w := unlink (ADR (path)) ; (* drop old backup *)
  291. StrClear (path) ;
  292. StrAppend (path, workName) ;
  293. StrAppend (path, ".BAK") ;
  294. StrClear (msg) ;
  295. StrAppend (msg, workName) ;
  296. w := rename (ADR (msg), ADR (path)) ; (* old version -> .BAK *)
  297. StrClear (path) ;
  298. StrAppend (path, workName) ;
  299. fd := open (ADR (path), 1 + 512 + 64, 420) ; (* O_WRONLY|O_TRUNC|O_CREAT, 0644 *)
  300. IF fd < 0 THEN
  301. CrLf ;
  302. PutStr (" ...cannot create file") ;
  303. Pause ;
  304. RETURN
  305. END ;
  306. i := 0 ;
  307. WHILE i < Length () DO
  308. IF CharAt (i) = CHR (13) THEN
  309. b := CHR (13) ;
  310. w := write (fd, ADR (b), 1) ;
  311. b := CHR (10) ;
  312. w := write (fd, ADR (b), 1)
  313. ELSE
  314. b := VAL (BYTE, ORD (CharAt (i))) ;
  315. w := write (fd, ADR (b), 1)
  316. END ;
  317. INC (i)
  318. END ;
  319. b := CHR (26) ; (* EOF marker ^Z, TP3 style *)
  320. w := write (fd, ADR (b), 1) ;
  321. w := close (fd) ;
  322. changed := FALSE ;
  323. CrLf
  324. END SaveWorkFile ;
  325. (* ------------------------------------------------------------------ *)
  326. (* load a file into the text buffer; stop at ^Z or TextLimit *)
  327. (* ------------------------------------------------------------------ *)
  328. PROCEDURE LoadWorkFile ;
  329. VAR fd : INTEGER ;
  330. path : ARRAY [0..511] OF CHAR ;
  331. k : LONGINT ;
  332. b : BYTE ;
  333. prevCR, tooBig : BOOLEAN ;
  334. BEGIN
  335. StrClear (path) ;
  336. StrAppend (path, workName) ;
  337. fd := open (ADR (path), O_RDONLY, 0) ;
  338. IF fd < 0 THEN
  339. PutStr ("New File") ;
  340. CrLf ;
  341. Clear ;
  342. changed := FALSE ;
  343. Pause ;
  344. RETURN
  345. END ;
  346. tooBig := FALSE ;
  347. prevCR := FALSE ;
  348. Clear ;
  349. LOOP
  350. IF Length () >= TextLimit THEN
  351. tooBig := TRUE ;
  352. EXIT
  353. END ;
  354. k := read (fd, ADR (b), 1) ;
  355. IF k # 1 THEN
  356. EXIT
  357. END ;
  358. IF b = CHR (26) THEN
  359. EXIT
  360. ELSIF b = CHR (10) THEN
  361. IF NOT prevCR THEN
  362. InsertCh (Length (), CHR (13)) (* lone LF -> CR *)
  363. END ;
  364. prevCR := FALSE
  365. ELSIF b = CHR (13) THEN
  366. InsertCh (Length (), CHR (13)) ;
  367. prevCR := TRUE
  368. ELSE
  369. InsertCh (Length (), CHR (ORD (b))) ;
  370. prevCR := FALSE
  371. END
  372. END ;
  373. k := close (fd) ;
  374. IF tooBig THEN
  375. PutStr ("File too big") ;
  376. CrLf
  377. END ;
  378. changed := FALSE ;
  379. Pause
  380. END LoadWorkFile ;
  381. (* ------------------------------------------------------------------ *)
  382. (* directory listing (kdir) *)
  383. (* ------------------------------------------------------------------ *)
  384. PROCEDURE MatchMask (VAR m, n : ARRAY OF CHAR) : BOOLEAN ;
  385. VAR mi, ni, saveMi, saveNi : CARDINAL ;
  386. isAll : BOOLEAN ;
  387. BEGIN
  388. (* DOS-style: "*.*" and "*" mean "everything" *)
  389. isAll := TRUE ;
  390. mi := 0 ;
  391. WHILE (mi <= HIGH (m)) AND (m [mi] # 0C) DO
  392. IF NOT ((m [mi] = "*") OR (m [mi] = ".")) THEN
  393. isAll := FALSE
  394. END ;
  395. INC (mi)
  396. END ;
  397. IF isAll THEN
  398. RETURN TRUE
  399. END ;
  400. mi := 0 ;
  401. ni := 0 ;
  402. saveMi := 0 ;
  403. saveNi := 0 ;
  404. LOOP
  405. (* mask exhausted *)
  406. IF (mi <= HIGH (m)) AND (m [mi] = "*") THEN
  407. saveMi := mi ;
  408. WHILE (mi <= HIGH (m)) AND (m [mi] = "*") DO
  409. INC (mi)
  410. END ;
  411. IF mi > HIGH (m) THEN
  412. RETURN TRUE
  413. END ;
  414. saveNi := ni
  415. ELSIF (mi > HIGH (m)) OR (m [mi] = 0C) THEN
  416. RETURN (ni <= HIGH (n)) AND (n [ni] = 0C)
  417. ELSIF (ni <= HIGH (n)) AND (n [ni] # 0C) AND
  418. (Upper (m [mi]) = Upper (n [ni])) THEN
  419. INC (mi) ;
  420. INC (ni)
  421. ELSIF saveMi > 0 THEN
  422. INC (saveNi) ;
  423. IF saveNi > HIGH (n) THEN
  424. RETURN FALSE
  425. END ;
  426. ni := saveNi ;
  427. mi := saveMi + 1
  428. ELSIF (ni > HIGH (n)) OR (n [ni] = 0C) THEN
  429. RETURN FALSE
  430. ELSE
  431. RETURN FALSE
  432. END
  433. END ;
  434. RETURN FALSE
  435. END MatchMask ;
  436. PROCEDURE DirCmd ;
  437. VAR mask : ARRAY [0..255] OF CHAR ;
  438. d : Dir ;
  439. p : POINTER TO dirent ;
  440. count : CARDINAL ;
  441. freeK : LONGCARD ;
  442. sv : statvfsbuf ;
  443. i : CARDINAL ;
  444. w : INTEGER ;
  445. BEGIN
  446. PutStr ("Dir mask: ") ;
  447. IF ReadLine (mask) = 0 THEN
  448. StrCopy (mask, "*.*")
  449. END ;
  450. CrLf ;
  451. d := opendir (ADR (".")) ;
  452. IF d = NIL THEN
  453. PutStr ("No files") ;
  454. CrLf ;
  455. Pause ;
  456. RETURN
  457. END ;
  458. count := 0 ;
  459. LOOP
  460. p := readdir (d) ;
  461. IF p = NIL THEN
  462. EXIT
  463. END ;
  464. IF (p^.d_name [0] # ".") AND
  465. (MatchMask (mask, p^.d_name)) THEN
  466. INC (count) ;
  467. PutStr (p^.d_name) ;
  468. i := 1 + (12 - StrLen (p^.d_name) MOD 12) ;
  469. IF i > 12 THEN
  470. i := 12
  471. END ;
  472. ScrnWide (i)
  473. END
  474. END ;
  475. w := closedir (d) ;
  476. CrLf ;
  477. IF count = 0 THEN
  478. PutStr ("No files") ;
  479. CrLf
  480. END ;
  481. IF statvfs (ADR ("."), ADR (sv)) = 0 THEN
  482. freeK := (sv.f_bavail * sv.f_frsize) DIV 1024 ;
  483. PutLongCard (freeK) ;
  484. PutStr ("k bytes free") ;
  485. CrLf
  486. END ;
  487. Pause
  488. END DirCmd ;
  489. (* ------------------------------------------------------------------ *)
  490. (* main menu *)
  491. (* ------------------------------------------------------------------ *)
  492. PROCEDURE DrawMenu ;
  493. VAR cwd : ARRAY [0..4095] OF CHAR ;
  494. freeB : CARDINAL ;
  495. i : CARDINAL ;
  496. BEGIN
  497. ClrScr ;
  498. GotoXY (1, 1) ;
  499. Key ("L", "ogged drive: ") ;
  500. PutCh (drive) ;
  501. GetCwd (cwd) ;
  502. GotoXY (2, 1) ;
  503. Key ("A", "ctive directory: ") ;
  504. PutStr (cwd) ;
  505. GotoXY (4, 1) ;
  506. Key ("W", "ork file: A:") ;
  507. PutStr (workName) ;
  508. GotoXY (5, 1) ;
  509. Key ("M", "ain file: A:") ;
  510. PutStr (mainName) ;
  511. GotoXY (7, 1) ;
  512. Key ("E", "dit ") ;
  513. Key ("C", "ompile ") ;
  514. Key ("R", "un ") ;
  515. Key ("S", "ave") ;
  516. GotoXY (9, 1) ;
  517. Key ("D", "ir ") ;
  518. Key ("Q", "uit compiler ") ;
  519. Key ("O", "ptions") ;
  520. GotoXY (11, 1) ;
  521. PutStr ("Text: ") ;
  522. PutCard (Length ()) ;
  523. PutStr (" bytes") ;
  524. freeB := TextLimit - Length ();
  525. GotoXY (12, 1) ;
  526. PutStr ("Free: ") ;
  527. PutCard (freeB) ;
  528. PutStr (" bytes") ;
  529. GotoXY (14, 1) ;
  530. Marked ;
  531. PutCh (">") ;
  532. Normal
  533. END DrawMenu ;
  534. (* ------------------------------------------------------------------ *)
  535. (* command handlers *)
  536. (* ------------------------------------------------------------------ *)
  537. PROCEDURE CmdLogDrive ;
  538. VAR nm : ARRAY [0..255] OF CHAR ;
  539. ln : CARDINAL ;
  540. BEGIN
  541. ClrScr ;
  542. GotoXY (1, 1) ;
  543. PutStr ("New drive: ") ;
  544. ln := ReadLine (nm) ;
  545. IF ln = 1 THEN
  546. drive := Upper (nm [0]) ;
  547. IF (drive < "A") OR (drive > "P") THEN
  548. drive := "A"
  549. END
  550. ELSIF ln > 1 THEN
  551. (* treat as a directory instead (linux has no real drives) *)
  552. IF chdir (ADR (nm)) # 0 THEN
  553. PutStr ("File not found") ;
  554. CrLf ;
  555. Pause
  556. END
  557. END
  558. END CmdLogDrive ;
  559. PROCEDURE CmdActDir ;
  560. VAR nm : ARRAY [0..255] OF CHAR ;
  561. BEGIN
  562. ClrScr ;
  563. GotoXY (1, 1) ;
  564. PutStr ("New directory: ") ;
  565. IF ReadLine (nm) > 0 THEN
  566. IF chdir (ADR (nm)) # 0 THEN
  567. PutStr ("File not found") ;
  568. CrLf ;
  569. Pause
  570. END
  571. END
  572. END CmdActDir ;
  573. PROCEDURE CmdWorkFile ;
  574. VAR nm : ARRAY [0..255] OF CHAR ;
  575. txtTitle : ARRAY [0..511] OF CHAR ;
  576. BEGIN
  577. ClrScr ;
  578. GotoXY (1, 1) ;
  579. PutStr ("Work file name: ") ;
  580. IF ReadLine (nm) > 0 THEN
  581. IF changed THEN
  582. CrLf ;
  583. PutStr (" ") ;
  584. IF Confirm ("Save current work file now (Y/N) ?") THEN
  585. SaveWorkFile
  586. END
  587. END ;
  588. ClrScr ;
  589. GotoXY (1, 1) ;
  590. SetWorkName (nm) ;
  591. StrClear (txtTitle) ;
  592. StrAppend (txtTitle, "Loading A:") ;
  593. StrAppend (txtTitle, workName) ;
  594. PutStr (txtTitle) ;
  595. CrLf ;
  596. LoadWorkFile
  597. END
  598. END CmdWorkFile ;
  599. PROCEDURE CmdMainFile ;
  600. VAR nm : ARRAY [0..255] OF CHAR ;
  601. BEGIN
  602. ClrScr ;
  603. GotoXY (1, 1) ;
  604. PutStr ("Main file name: ") ;
  605. IF ReadLine (nm) > 0 THEN
  606. StrCopy (mainName, nm) ;
  607. IF NOT HasExt (mainName) THEN
  608. StrAppend (mainName, ".PAS")
  609. END
  610. END
  611. END CmdMainFile ;
  612. PROCEDURE CmdCompile ;
  613. VAR comPath : ARRAY [0..255] OF CHAR ;
  614. BEGIN
  615. ClrScr ;
  616. GotoXY (1, 1) ;
  617. IF Compile (errNo, errPos) THEN
  618. PutStr ("Compiled OK - code ") ;
  619. PutCard (CodeBytes ()) ;
  620. PutStr (" bytes, data ") ;
  621. PutCard (DataBytes ()) ;
  622. CrLf ;
  623. (* Destination, as TP3's Options submenu sets it: 0 = Memory (the
  624. debugger runs the code in place), 1 = .COM, 2 = .CHN.
  625. Until now the choice was displayed and then ignored, which was a
  626. fidelity gap: selecting COM did nothing at all. *)
  627. CASE codeDest OF
  628. | 0 :
  629. PutStr ("Destination: Memory (code left in the buffer)") ;
  630. CrLf
  631. | 2 :
  632. (* .CHN is Turbo Pascal's TBIOS overlay format; the original
  633. wrote it with its own overlay loader. Refuse rather than
  634. pretend - the user's next R would not find anything. *)
  635. PutStr ("Destination: .CHN is not implemented") ;
  636. CrLf
  637. ELSE
  638. PutStr ("Destination: .COM") ;
  639. CrLf ;
  640. ComName (comPath) ;
  641. IF WriteCom (comPath) THEN
  642. PutStr (" wrote ") ;
  643. PutStr (comPath) ;
  644. PutStr (" ") ;
  645. PutCard (LinkSize ()) ;
  646. PutStr (" bytes (image ") ;
  647. PutCard (ImageBytes ()) ;
  648. PutStr (")")
  649. ELSE
  650. (* WriteCom unlinked any partial file, so there is no half
  651. file to clean up here. *)
  652. PutStr (" could not write ") ;
  653. PutStr (comPath) ;
  654. PutStr (" - disk error?")
  655. END ;
  656. CrLf
  657. END ;
  658. PutStr ("press ESC to return to the editor") ;
  659. CrLf ;
  660. WaitEsc
  661. ELSE
  662. PutStr ("TP3-style error ") ;
  663. PutCard (errNo) ;
  664. PutStr (" at relative pos ") ;
  665. PutCard (errPos) ;
  666. CrLf ;
  667. PutStr ("press ESC, then the editor opens on the error") ;
  668. CrLf ;
  669. WaitEsc ;
  670. (* TP3 kcwait: waitesc ; BX:=txerrpos ; DEC BX ; JMP editor2.
  671. Net cursor position = txbeg + txerrpos, i.e. errPos here. *)
  672. GotoOffset (errPos) ;
  673. CmdEditor
  674. END
  675. END CmdCompile ;
  676. PROCEDURE CmdRun ;
  677. (* TP3's `R': compile, then run what came out.
  678. The original has no separate loader here - krungo runs the image where it
  679. already is, in the same 64 KB, at the address DOS would give a .COM.
  680. Exec86 has the same shape: the linked image is poked into the
  681. interpreter's own 64 KB at 0100H and executed in this process, with no
  682. file written, so R does the same thing whether Destination is Memory or
  683. .COM.
  684. The guest's output goes to this process's fd 1 through the runtime's
  685. INT 21h AH=02/09/08, and it lands between our two status lines rather
  686. than being collected and replayed: Term writes one byte per write(2) and
  687. buffers nothing, so the two streams stay in order.
  688. Clear86, Poke86 and Run86 are module-level imports like everything else
  689. here; the suffix is explained where they are imported. *)
  690. VAR
  691. status, exitCode, i, n : CARDINAL ;
  692. steps : LONGCARD ;
  693. BEGIN
  694. ClrScr ;
  695. GotoXY (1, 1) ;
  696. IF NOT Compile (errNo, errPos) THEN
  697. PutStr ("TP3-style error ") ;
  698. PutCard (errNo) ;
  699. PutStr (" at relative pos ") ;
  700. PutCard (errPos) ;
  701. CrLf ;
  702. PutStr ("press ESC, then the editor opens on the error") ;
  703. CrLf ;
  704. WaitEsc ;
  705. GotoOffset (errPos) ;
  706. CmdEditor ;
  707. RETURN
  708. END ;
  709. PutStr ("Compiled OK - code ") ;
  710. PutCard (CodeBytes ()) ;
  711. PutStr (" bytes, data ") ;
  712. PutCard (DataBytes ()) ;
  713. CrLf ;
  714. (* LinkSize, not ImageBytes: the .COM DOS would load is the image padded
  715. out to cover the whole data area, and the globals the program is about
  716. to use live in that padding. ImageByteAt answers 0 past the end of the
  717. image, which is exactly the zero fill the padding is made of. *)
  718. n := LinkSize () ;
  719. Clear86 ;
  720. i := 0 ;
  721. WHILE i < n DO
  722. Poke86 (0100H + i, ORD (ImageByteAt (i))) ;
  723. INC (i)
  724. END ;
  725. PutStr ("running ") ;
  726. PutCard (n) ;
  727. PutStr (" bytes at 0100h") ;
  728. CrLf ;
  729. CrLf ;
  730. status := Run86 (exitCode, steps) ;
  731. CrLf ;
  732. CASE status OF
  733. | 0 :
  734. PutStr ("program terminated (INT 21h AH=4Ch, code ") ;
  735. PutCard (exitCode) ;
  736. PutStr (")")
  737. | 1 :
  738. PutStr ("interpreter fault - diagnostic above")
  739. | 2 :
  740. PutStr ("step limit reached - runaway loop?")
  741. ELSE
  742. PutStr ("unexpected status ") ;
  743. PutCard (status)
  744. END ;
  745. CrLf ;
  746. PutStr ("steps executed: ") ;
  747. PutLongCard (steps) ;
  748. CrLf ;
  749. PutStr ("press ESC to return to the editor") ;
  750. CrLf ;
  751. WaitEsc
  752. END CmdRun ;
  753. PROCEDURE CmdEditor ;
  754. VAR edChanged : BOOLEAN ;
  755. BEGIN
  756. IF StrLen (workName) = 0 THEN
  757. (* kgetfn: no work file yet -> prompt just like W does *)
  758. CmdWorkFile ;
  759. IF StrLen (workName) = 0 THEN
  760. RETURN
  761. END
  762. END ;
  763. edChanged := FALSE ;
  764. Run (drive, workName, edChanged) ;
  765. IF edChanged THEN
  766. changed := TRUE
  767. END
  768. END CmdEditor ;
  769. (* ------------------------------------------------------------------ *)
  770. (* TP3 "Options" submenu (optmenu) *)
  771. (* ------------------------------------------------------------------ *)
  772. PROCEDURE PutHex (n : CARDINAL) ;
  773. VAR dig : ARRAY [0..7] OF CHAR ;
  774. i, d : CARDINAL ;
  775. BEGIN
  776. i := 0 ;
  777. LOOP
  778. d := n MOD 16 ;
  779. IF d < 10 THEN
  780. dig [i] := CHR (ORD ("0") + d)
  781. ELSE
  782. dig [i] := CHR (ORD ("A") + d - 10)
  783. END ;
  784. n := n DIV 16 ;
  785. INC (i) ;
  786. IF (n = 0) OR (i = 8) THEN
  787. EXIT
  788. END
  789. END ;
  790. WHILE i > 0 DO
  791. DEC (i) ;
  792. PutCh (dig [i])
  793. END
  794. END PutHex ;
  795. PROCEDURE OptionsMenu ;
  796. VAR ch : CHAR ;
  797. s : ARRAY [0..255] OF CHAR ;
  798. done : BOOLEAN ;
  799. BEGIN
  800. done := FALSE ;
  801. REPEAT
  802. ClrScr ;
  803. GotoXY (1, 1) ;
  804. Marked ; PutCh ("M") ; Normal ;
  805. PutStr ("emory ") ;
  806. Marked ; PutCh ("C") ; Normal ;
  807. PutStr ("OM ") ;
  808. Marked ; PutCh ("H") ; Normal ;
  809. PutStr ("CHN") ;
  810. CrLf ;
  811. PutStr (" ") ;
  812. IF codeDest = 1 THEN
  813. PutStr ("Compile -> COM")
  814. ELSIF codeDest = 2 THEN
  815. PutStr ("Compile -> CHN")
  816. ELSE
  817. PutStr ("Compile -> Memory")
  818. END ;
  819. CrLf ;
  820. PutStr ("Text: ") ;
  821. PutCard (Length ()) ;
  822. PutStr (" Code: ") ;
  823. PutHex (minCode) ;
  824. PutStr (" Data: ") ;
  825. PutHex (minData) ;
  826. PutStr (" Stack: ") ;
  827. PutHex (minStack) ;
  828. CrLf ;
  829. PutStr ("Command line Params: ") ;
  830. PutStr (paramLine) ;
  831. CrLf ;
  832. Marked ; PutCh ("F") ; Normal ;
  833. PutStr ("ind run-time error ") ;
  834. Marked ; PutCh ("Q") ; Normal ;
  835. PutStr ("uit") ;
  836. CrLf ;
  837. CrLf ;
  838. Marked ; PutCh (">") ; Normal ;
  839. GetCh (ch) ;
  840. ch := Upper (ch) ;
  841. CASE ch OF
  842. | "M" : codeDest := 0
  843. | "C" : codeDest := 1
  844. | "H" : codeDest := 2
  845. | "O" :
  846. ClrScr ; GotoXY (1,1) ;
  847. PutStr ("Min Code Segment (hex): ") ;
  848. IF ReadLine (s) > 0 THEN minCode := 0 END
  849. | "D" :
  850. ClrScr ; GotoXY (1,1) ;
  851. PutStr ("Min Data Segment (hex): ") ;
  852. IF ReadLine (s) > 0 THEN minData := 0 END
  853. | "I" :
  854. ClrScr ; GotoXY (1,1) ;
  855. PutStr ("Min Free Dyn Mem / Stack (hex): ") ;
  856. IF ReadLine (s) > 0 THEN minStack := 0 END
  857. | "A" :
  858. ClrScr ; GotoXY (1,1) ;
  859. PutStr ("Max Free Dyn Mem / Stack (hex): ") ;
  860. IF ReadLine (s) > 0 THEN maxStack := 0 END
  861. | "P" :
  862. ClrScr ; GotoXY (1,1) ;
  863. PutStr ("Command line Params: ") ;
  864. IF ReadLine (paramLine) > 0 THEN
  865. (* keep the parameter string *)
  866. END
  867. | "F" :
  868. ClrScr ; GotoXY (1,1) ;
  869. PutStr ("Find run-time error address (hex) : ") ;
  870. IF ReadLine (s) > 0 THEN
  871. PutStr ("Searching ... (not implemented)") ;
  872. CrLf ;
  873. Pause
  874. END
  875. | "Q" :
  876. done := TRUE
  877. ELSE
  878. (* ignore *)
  879. END
  880. UNTIL done
  881. END OptionsMenu ;
  882. (* ------------------------------------------------------------------ *)
  883. PROCEDURE terminal ;
  884. VAR ch : CHAR ;
  885. BEGIN
  886. Open ;
  887. drive := "A" ;
  888. codeDest := 0 ;
  889. minCode := 0 ; minData := 0 ; minStack := 0 ; maxStack := 0 ;
  890. StrClear (workName) ;
  891. StrClear (mainName) ;
  892. StrClear (paramLine) ;
  893. Clear ;
  894. changed := FALSE ;
  895. LOOP
  896. DrawMenu ;
  897. GetCh (ch) ;
  898. ch := Upper (ch) ;
  899. CASE ch OF
  900. | "L" : CmdLogDrive
  901. | "A" : CmdActDir
  902. | "W" : CmdWorkFile
  903. | "M" : CmdMainFile
  904. | "E" : CmdEditor
  905. | "C" : CmdCompile
  906. | "R" : CmdRun
  907. | "S" :
  908. IF StrLen (workName) > 0 THEN
  909. SaveWorkFile ;
  910. Pause
  911. END
  912. | "D" : DirCmd
  913. | "O" : OptionsMenu
  914. | "Q" :
  915. IF changed THEN
  916. CrLf ;
  917. IF Confirm ("Work file not saved. Save (Y/N) ?") THEN
  918. SaveWorkFile
  919. END
  920. END ;
  921. Close ;
  922. (* gm2 -fiso: a bare HALT aborts (SIGABRT, shell status 134);
  923. an explicit HALT (0) exits cleanly with status 0. *)
  924. HALT (0)
  925. ELSE
  926. (* any other key: redraw the menu, like TP3 *)
  927. END
  928. END
  929. END terminal ;
  930. BEGIN
  931. terminal
  932. END Shell.