ComTest.mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. MODULE ComTest ;
  2. (* Builds the linked .COM for fixtures and checks it, so the linker is tested
  3. by running it rather than by "it compiled".
  4. Reads fixture paths from stdin like CompileTest, and for each one prints
  5. <path> OK com=<bytes> image=<bytes> data=<bytes>
  6. plus, under @hex, the first bytes and the bytes around the code/data
  7. boundary - the two places where a linker bug would show up.
  8. What is actually being asserted, and why these bytes:
  9. * the file must be LinkSize = ImageBytes bytes, i.e. exactly the
  10. runtime and the program's code. The globals live outside the file at
  11. Compiler.DataBase (8000h image, 8100h memory) and initmem zeroes them
  12. at startup, as the original's separate DS segment is zeroed. A file
  13. padded to the data base would be a different layout, not this one.
  14. * the file's byte 0 must be the E9 entry jump (Compiler.Inittur); the
  15. runtime starts three bytes later. If it is not, execution starts in
  16. the wrong place and every absolute address is still 0100h low.
  17. * there must be no byte past ImageBytes: the file is code-only, so a
  18. longer file means the linker is padding to the old data base again.
  19. The @hex dump is the last line of defence: it is what caught the header
  20. words being written with a nibble shift instead of a byte shift. *)
  21. FROM Posix IMPORT read, write, open, close ;
  22. FROM TextBuf IMPORT TextLimit, Clear, Length, CharAt, InsertCh ;
  23. FROM Compiler IMPORT Compile, CodeBytes, DataBytes, ImageBytes, ImageByteAt, SetSource ;
  24. FROM Linker IMPORT LinkSize, WriteCom ;
  25. FROM SYSTEM IMPORT ADR, BYTE ;
  26. CONST
  27. STDIN = 0 ;
  28. STDOUT = 1 ;
  29. O_RDONLY = 0 ;
  30. O_RDONLY_RB = 0 ;
  31. TYPE
  32. PathBuf = ARRAY [0..511] OF CHAR ;
  33. VAR
  34. lineBuf : ARRAY [0..255] OF CHAR ;
  35. pathCopy : PathBuf ;
  36. outPath : PathBuf ;
  37. hexMode : BOOLEAN ;
  38. hexOn : BOOLEAN ;
  39. PROCEDURE PutCh (ch : CHAR) ;
  40. VAR n : LONGINT ;
  41. BEGIN
  42. n := write (STDOUT, ADR (ch), 1)
  43. END PutCh ;
  44. PROCEDURE PutStr (s : ARRAY OF CHAR) ;
  45. VAR i : CARDINAL ; n : LONGINT ;
  46. BEGIN
  47. i := 0 ;
  48. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  49. n := write (STDOUT, ADR (s [i]), 1) ;
  50. INC (i)
  51. END
  52. END PutStr ;
  53. PROCEDURE PutCard (n : CARDINAL) ;
  54. VAR dig : ARRAY [0..9] OF CHAR ; i : CARDINAL ;
  55. BEGIN
  56. IF n = 0 THEN
  57. PutCh ("0")
  58. ELSE
  59. i := 0 ;
  60. WHILE n > 0 DO
  61. dig [i] := CHR (ORD ("0") + (n MOD 10)) ;
  62. n := n DIV 10 ;
  63. INC (i)
  64. END ;
  65. WHILE i > 0 DO
  66. DEC (i) ;
  67. PutCh (dig [i])
  68. END
  69. END
  70. END PutCard ;
  71. PROCEDURE NL ;
  72. BEGIN
  73. PutCh (CHR (13)) ; PutCh (CHR (10))
  74. END NL ;
  75. PROCEDURE Hex (b : BYTE ; VAR out : ARRAY OF CHAR) ;
  76. VAR d : CARDINAL ;
  77. BEGIN
  78. d := VAL (CARDINAL, b) DIV 16 ;
  79. IF d < 10 THEN
  80. out [0] := CHR (ORD ("0") + d)
  81. ELSE
  82. out [0] := CHR (ORD ("A") + d - 10)
  83. END ;
  84. d := VAL (CARDINAL, b) MOD 16 ;
  85. IF d < 10 THEN
  86. out [1] := CHR (ORD ("0") + d)
  87. ELSE
  88. out [1] := CHR (ORD ("A") + d - 10)
  89. END
  90. END Hex ;
  91. PROCEDURE PutHexNib (d : CARDINAL) ;
  92. VAR c : CHAR ;
  93. BEGIN
  94. IF d < 10 THEN
  95. c := CHR (ORD ("0") + d)
  96. ELSE
  97. c := CHR (ORD ("A") + d - 10)
  98. END ;
  99. PutCh (c)
  100. END PutHexNib ;
  101. PROCEDURE PutCardHex4 (n : CARDINAL) ;
  102. VAR d : CARDINAL ;
  103. BEGIN
  104. d := 4096 ;
  105. WHILE d > 0 DO
  106. PutHexNib ((n DIV d) MOD 16) ;
  107. d := d DIV 16
  108. END ;
  109. PutStr (": ")
  110. END PutCardHex4 ;
  111. PROCEDURE StrCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
  112. VAR i : CARDINAL ;
  113. BEGIN
  114. i := 0 ;
  115. WHILE (i <= HIGH (dst)) AND (i <= HIGH (src)) AND (src [i] # 0C) DO
  116. dst [i] := src [i] ;
  117. INC (i)
  118. END ;
  119. IF i <= HIGH (dst) THEN
  120. dst [i] := 0C
  121. END
  122. END StrCopy ;
  123. PROCEDURE AppStr (VAR dst : ARRAY OF CHAR ; s : ARRAY OF CHAR ) ;
  124. VAR i, j : CARDINAL ;
  125. BEGIN
  126. i := 0 ;
  127. WHILE (i <= HIGH (dst)) AND (dst [i] # 0C) DO
  128. INC (i)
  129. END ;
  130. j := 0 ;
  131. WHILE (j <= HIGH (s)) AND (s [j] # 0C) AND (i <= HIGH (dst)) DO
  132. dst [i] := s [j] ;
  133. INC (i) ; INC (j)
  134. END ;
  135. IF i <= HIGH (dst) THEN
  136. dst [i] := 0C
  137. END
  138. END AppStr ;
  139. PROCEDURE ReadLineStr (VAR s : ARRAY OF CHAR) : BOOLEAN ;
  140. VAR ch : CHAR ; n : LONGINT ; i : CARDINAL ;
  141. BEGIN
  142. i := 0 ; s [0] := 0C ;
  143. LOOP
  144. n := read (STDIN, ADR (ch), 1) ;
  145. IF n # 1 THEN
  146. IF i = 0 THEN RETURN FALSE END ;
  147. s [i] := 0C ; RETURN TRUE
  148. END ;
  149. IF (ch = CHR (10)) OR (ch = CHR (13)) THEN
  150. IF i = 0 THEN RETURN FALSE END ;
  151. s [i] := 0C ; RETURN TRUE
  152. END ;
  153. IF i < HIGH (s) THEN
  154. s [i] := ch ; INC (i)
  155. END
  156. END
  157. END ReadLineStr ;
  158. PROCEDURE LoadFile (p : ARRAY OF CHAR) : BOOLEAN ;
  159. (* same CR/LF normalisation as the shell's LoadWorkFile *)
  160. VAR fd : INTEGER ; k : LONGINT ; b : BYTE ; prevCR : BOOLEAN ;
  161. z : ARRAY [0..511] OF CHAR ;
  162. BEGIN
  163. StrCopy (z, p) ;
  164. fd := open (ADR (z), O_RDONLY, 0) ;
  165. IF fd < 0 THEN
  166. RETURN FALSE
  167. END ;
  168. SetSource (z) ; (* {$I} resolves beside this file *)
  169. Clear ;
  170. prevCR := FALSE ;
  171. LOOP
  172. IF Length () >= TextLimit THEN EXIT END ;
  173. k := read (fd, ADR (b), 1) ;
  174. IF k # 1 THEN EXIT END ;
  175. IF b = 26 THEN
  176. EXIT
  177. ELSIF b = 10 THEN
  178. IF NOT prevCR THEN InsertCh (Length (), CHR (13)) END ;
  179. prevCR := FALSE
  180. ELSIF b = 13 THEN
  181. InsertCh (Length (), CHR (13)) ;
  182. prevCR := TRUE
  183. ELSE
  184. InsertCh (Length (), CHR (ORD (b))) ;
  185. prevCR := FALSE
  186. END
  187. END ;
  188. k := close (fd) ;
  189. RETURN TRUE
  190. END LoadFile ;
  191. (* ---- reading the .COM back ---------------------------------------- *)
  192. VAR
  193. fbuf : ARRAY [0..65535] OF BYTE ;
  194. fsize : CARDINAL ;
  195. PROCEDURE ReadCom (p : ARRAY OF CHAR) : BOOLEAN ;
  196. VAR fd : INTEGER ; k : LONGINT ; z : ARRAY [0..511] OF CHAR ;
  197. BEGIN
  198. StrCopy (z, p) ;
  199. fd := open (ADR (z), O_RDONLY, 0) ;
  200. IF fd < 0 THEN
  201. RETURN FALSE
  202. END ;
  203. fsize := 0 ;
  204. LOOP
  205. IF fsize > 65535 THEN EXIT END ;
  206. k := read (fd, ADR (fbuf [fsize]), 65535 - fsize) ;
  207. IF k <= 0 THEN EXIT END ;
  208. fsize := fsize + VAL (CARDINAL, k)
  209. END ;
  210. k := close (fd) ;
  211. RETURN TRUE
  212. END ReadCom ;
  213. PROCEDURE DumpAt (at, cnt : CARDINAL) ;
  214. VAR i, n : CARDINAL ; hx : ARRAY [0..1] OF CHAR ;
  215. BEGIN
  216. PutStr (" " ) ;
  217. PutCardHex4 (at) ;
  218. n := 0 ;
  219. WHILE (n < cnt) AND (at + n < fsize) DO
  220. Hex (fbuf [at + n], hx) ;
  221. PutCh (hx [0]) ; PutCh (hx [1]) ; PutCh (" ") ;
  222. INC (n)
  223. END ;
  224. NL
  225. END DumpAt ;
  226. (* Count non-zero bytes in [a,b). The .COM is code-only now, so this is
  227. called with a = ImageBytes and b = fsize: it must be zero because there is
  228. no byte past the image. The independent checker pins the file length; this
  229. line keeps the ComTest report honest about what was read back. *)
  230. PROCEDURE NonZero (a, b : CARDINAL) : CARDINAL ;
  231. VAR i, k : CARDINAL ;
  232. BEGIN
  233. k := 0 ; i := a ;
  234. WHILE (i < b) AND (i < fsize) DO
  235. IF fbuf [i] # 0 THEN
  236. k := k + 1
  237. END ;
  238. INC (i)
  239. END ;
  240. RETURN k
  241. END NonZero ;
  242. PROCEDURE BaseName (src : PathBuf ; VAR dst : PathBuf) ;
  243. (* the fixture's own name, so the .COM lands beside us as NAME.COM rather
  244. than trying to graft a directory onto the source path.
  245. FIXED array formals, not open ones: with `VAR dst : ARRAY OF CHAR` the
  246. write did not reach the caller's buffer at all, so the .COM was written to
  247. a file named after whatever garbage was in the destination - literally
  248. "END", taken from adjacent string data. An open-array VAR formal is not
  249. worth the subtlety in a test harness. *)
  250. VAR i, j, last : CARDINAL ;
  251. BEGIN
  252. i := 0 ; j := 0 ; last := 0 ;
  253. WHILE (i <= HIGH (src)) AND (src [i] # 0C) DO
  254. IF src [i] = "/" THEN
  255. last := i + 1
  256. END ;
  257. INC (i)
  258. END ;
  259. i := last ;
  260. WHILE (i <= HIGH (src)) AND (src [i] # 0C) AND (j <= HIGH (dst)) DO
  261. dst [j] := src [i] ;
  262. INC (i) ; INC (j)
  263. END ;
  264. (* drop the .pas and add .COM *)
  265. IF j > 4 THEN
  266. IF (dst [j - 4] = ".") AND (dst [j - 3] = "p") AND (dst [j - 2] = "a")
  267. AND (dst [j - 1] = "s") THEN
  268. j := j - 4
  269. END
  270. END ;
  271. IF j + 4 <= HIGH (dst) THEN
  272. dst [j] := "." ; dst [j + 1] := "C" ; dst [j + 2] := "O" ;
  273. dst [j + 3] := "M" ; dst [j + 4] := 0C
  274. END
  275. END BaseName ;
  276. PROCEDURE IsHexCmd () : BOOLEAN ;
  277. VAR i : CARDINAL ;
  278. BEGIN
  279. IF lineBuf [0] # "@" THEN RETURN FALSE END ;
  280. i := 0 ;
  281. WHILE (i <= HIGH (lineBuf)) AND (lineBuf [i] # 0C) DO INC (i) END ;
  282. IF i # 4 THEN RETURN FALSE END ;
  283. RETURN (lineBuf [1] = "h") AND (lineBuf [2] = "e") AND (lineBuf [3] = "x")
  284. END IsHexCmd ;
  285. VAR
  286. errNo, errPos : CARDINAL ;
  287. ok : BOOLEAN ;
  288. zout : PathBuf ;
  289. nz : CARDINAL ;
  290. BEGIN
  291. PutStr ("FIXTURE RESULT") ;
  292. NL ;
  293. WHILE ReadLineStr (lineBuf) DO
  294. IF IsHexCmd () THEN
  295. hexOn := TRUE
  296. ELSE
  297. StrCopy (pathCopy, lineBuf) ;
  298. IF LoadFile (pathCopy) THEN
  299. ok := Compile (errNo, errPos) ;
  300. IF ok THEN
  301. BaseName (pathCopy, zout) ;
  302. IF WriteCom (zout) THEN
  303. IF ReadCom (zout) THEN
  304. nz := NonZero (ImageBytes (), fsize) ;
  305. PutStr (lineBuf) ;
  306. PutStr (" OK com=") ;
  307. PutCard (fsize) ;
  308. PutStr (" image=") ;
  309. PutCard (ImageBytes ()) ;
  310. PutStr (" data=") ;
  311. PutCard (DataBytes ()) ;
  312. PutStr (" nonzeroInGap=") ;
  313. PutCard (nz) ;
  314. NL ;
  315. IF hexOn THEN
  316. DumpAt (0, 16) ; (* runtime head *)
  317. DumpAt (ImageBytes () - 16, 16) ; (* code tail *)
  318. DumpAt (ImageBytes (), 16) (* past the file *)
  319. END
  320. ELSE
  321. PutStr (lineBuf) ; PutStr (" CANNOT READ BACK") ; NL
  322. END
  323. ELSE
  324. PutStr (lineBuf) ; PutStr (" WRITE_COM_FAILED") ; NL
  325. END
  326. ELSE
  327. PutStr (lineBuf) ; PutStr (" ERROR ") ;
  328. PutCard (errNo) ; PutStr (" at pos ") ; PutCard (errPos) ; NL
  329. END
  330. ELSE
  331. PutStr (lineBuf) ; PutStr (" CANNOT OPEN") ; NL
  332. END
  333. END
  334. END
  335. END ComTest.