Shell.mod 26 KB

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