| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- MODULE ComTest ;
- (* Builds the linked .COM for fixtures and checks it, so the linker is tested
- by running it rather than by "it compiled".
- Reads fixture paths from stdin like CompileTest, and for each one prints
- <path> OK com=<bytes> image=<bytes> data=<bytes>
- plus, under @hex, the first bytes and the bytes around the code/data
- boundary - the two places where a linker bug would show up.
- What is actually being asserted, and why these bytes:
- * the file must be LinkSize bytes, i.e. padded past the code to cover
- the data area. A .COM shorter than the data area leaves the globals
- outside the file, so the first read of a global reads whatever the
- loader happened to leave there.
- * the file's byte 0 must be the runtime's first byte (8B C0 = MOV AX,AX,
- the head of initmem). If it is not, the runtime was not prepended and
- every CALL target is wrong.
- * the bytes from ImageBytes to the end must be ZERO. That gap is what
- the program's globals live in, so an uninitialised filler byte would
- be a global that starts as garbage.
- The @hex dump is the last line of defence: it is what caught the header
- words being written with a nibble shift instead of a byte shift. *)
- FROM Posix IMPORT read, write, open, close ;
- FROM TextBuf IMPORT TextLimit, Clear, Length, CharAt, InsertCh ;
- FROM Compiler IMPORT Compile, CodeBytes, DataBytes, ImageBytes, ImageByteAt, DataBase ;
- FROM Linker IMPORT LinkSize, WriteCom ;
- FROM SYSTEM IMPORT ADR, BYTE ;
- CONST
- STDIN = 0 ;
- STDOUT = 1 ;
- O_RDONLY = 0 ;
- O_RDONLY_RB = 0 ;
- TYPE
- PathBuf = ARRAY [0..511] OF CHAR ;
- VAR
- lineBuf : ARRAY [0..255] OF CHAR ;
- pathCopy : PathBuf ;
- outPath : PathBuf ;
- hexMode : BOOLEAN ;
- hexOn : BOOLEAN ;
- PROCEDURE PutCh (ch : CHAR) ;
- VAR n : LONGINT ;
- BEGIN
- n := write (STDOUT, ADR (ch), 1)
- END PutCh ;
- PROCEDURE PutStr (s : ARRAY OF CHAR) ;
- VAR i : CARDINAL ; n : LONGINT ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
- n := write (STDOUT, ADR (s [i]), 1) ;
- INC (i)
- END
- END PutStr ;
- PROCEDURE PutCard (n : CARDINAL) ;
- VAR dig : ARRAY [0..9] OF CHAR ; i : CARDINAL ;
- BEGIN
- IF n = 0 THEN
- PutCh ("0")
- ELSE
- i := 0 ;
- WHILE n > 0 DO
- dig [i] := CHR (ORD ("0") + (n MOD 10)) ;
- n := n DIV 10 ;
- INC (i)
- END ;
- WHILE i > 0 DO
- DEC (i) ;
- PutCh (dig [i])
- END
- END
- END PutCard ;
- PROCEDURE NL ;
- BEGIN
- PutCh (CHR (13)) ; PutCh (CHR (10))
- END NL ;
- PROCEDURE Hex (b : BYTE ; VAR out : ARRAY OF CHAR) ;
- VAR d : CARDINAL ;
- BEGIN
- d := VAL (CARDINAL, b) DIV 16 ;
- IF d < 10 THEN
- out [0] := CHR (ORD ("0") + d)
- ELSE
- out [0] := CHR (ORD ("A") + d - 10)
- END ;
- d := VAL (CARDINAL, b) MOD 16 ;
- IF d < 10 THEN
- out [1] := CHR (ORD ("0") + d)
- ELSE
- out [1] := CHR (ORD ("A") + d - 10)
- END
- END Hex ;
- PROCEDURE PutHexNib (d : CARDINAL) ;
- VAR c : CHAR ;
- BEGIN
- IF d < 10 THEN
- c := CHR (ORD ("0") + d)
- ELSE
- c := CHR (ORD ("A") + d - 10)
- END ;
- PutCh (c)
- END PutHexNib ;
- PROCEDURE PutCardHex4 (n : CARDINAL) ;
- VAR d : CARDINAL ;
- BEGIN
- d := 4096 ;
- WHILE d > 0 DO
- PutHexNib ((n DIV d) MOD 16) ;
- d := d DIV 16
- END ;
- PutStr (": ")
- END PutCardHex4 ;
- PROCEDURE StrCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (dst)) AND (i <= HIGH (src)) AND (src [i] # 0C) DO
- dst [i] := src [i] ;
- INC (i)
- END ;
- IF i <= HIGH (dst) THEN
- dst [i] := 0C
- END
- END StrCopy ;
- PROCEDURE AppStr (VAR dst : ARRAY OF CHAR ; s : ARRAY OF CHAR ) ;
- VAR i, j : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (dst)) AND (dst [i] # 0C) DO
- INC (i)
- END ;
- j := 0 ;
- WHILE (j <= HIGH (s)) AND (s [j] # 0C) AND (i <= HIGH (dst)) DO
- dst [i] := s [j] ;
- INC (i) ; INC (j)
- END ;
- IF i <= HIGH (dst) THEN
- dst [i] := 0C
- END
- END AppStr ;
- PROCEDURE ReadLineStr (VAR s : ARRAY OF CHAR) : BOOLEAN ;
- VAR ch : CHAR ; n : LONGINT ; i : CARDINAL ;
- BEGIN
- i := 0 ; s [0] := 0C ;
- LOOP
- n := read (STDIN, ADR (ch), 1) ;
- IF n # 1 THEN
- IF i = 0 THEN RETURN FALSE END ;
- s [i] := 0C ; RETURN TRUE
- END ;
- IF (ch = CHR (10)) OR (ch = CHR (13)) THEN
- IF i = 0 THEN RETURN FALSE END ;
- s [i] := 0C ; RETURN TRUE
- END ;
- IF i < HIGH (s) THEN
- s [i] := ch ; INC (i)
- END
- END
- END ReadLineStr ;
- PROCEDURE LoadFile (p : ARRAY OF CHAR) : BOOLEAN ;
- (* same CR/LF normalisation as the shell's LoadWorkFile *)
- VAR fd : INTEGER ; k : LONGINT ; b : BYTE ; prevCR : BOOLEAN ;
- z : ARRAY [0..511] OF CHAR ;
- BEGIN
- StrCopy (z, p) ;
- fd := open (ADR (z), O_RDONLY, 0) ;
- IF fd < 0 THEN
- RETURN FALSE
- END ;
- Clear ;
- prevCR := FALSE ;
- LOOP
- IF Length () >= TextLimit THEN EXIT END ;
- k := read (fd, ADR (b), 1) ;
- IF k # 1 THEN EXIT END ;
- IF b = 26 THEN
- EXIT
- ELSIF b = 10 THEN
- IF NOT prevCR THEN InsertCh (Length (), CHR (13)) END ;
- prevCR := FALSE
- ELSIF b = 13 THEN
- InsertCh (Length (), CHR (13)) ;
- prevCR := TRUE
- ELSE
- InsertCh (Length (), CHR (ORD (b))) ;
- prevCR := FALSE
- END
- END ;
- k := close (fd) ;
- RETURN TRUE
- END LoadFile ;
- (* ---- reading the .COM back ---------------------------------------- *)
- VAR
- fbuf : ARRAY [0..65535] OF BYTE ;
- fsize : CARDINAL ;
- PROCEDURE ReadCom (p : ARRAY OF CHAR) : BOOLEAN ;
- VAR fd : INTEGER ; k : LONGINT ; z : ARRAY [0..511] OF CHAR ;
- BEGIN
- StrCopy (z, p) ;
- fd := open (ADR (z), O_RDONLY, 0) ;
- IF fd < 0 THEN
- RETURN FALSE
- END ;
- fsize := 0 ;
- LOOP
- IF fsize > 65535 THEN EXIT END ;
- k := read (fd, ADR (fbuf [fsize]), 65535 - fsize) ;
- IF k <= 0 THEN EXIT END ;
- fsize := fsize + VAL (CARDINAL, k)
- END ;
- k := close (fd) ;
- RETURN TRUE
- END ReadCom ;
- PROCEDURE DumpAt (at, cnt : CARDINAL) ;
- VAR i, n : CARDINAL ; hx : ARRAY [0..1] OF CHAR ;
- BEGIN
- PutStr (" " ) ;
- PutCardHex4 (at) ;
- n := 0 ;
- WHILE (n < cnt) AND (at + n < fsize) DO
- Hex (fbuf [at + n], hx) ;
- PutCh (hx [0]) ; PutCh (hx [1]) ; PutCh (" ") ;
- INC (n)
- END ;
- NL
- END DumpAt ;
- (* Count non-zero bytes in [a,b) - the gap between the code and the data must
- be entirely zero, or a program's globals start as garbage. *)
- PROCEDURE NonZero (a, b : CARDINAL) : CARDINAL ;
- VAR i, k : CARDINAL ;
- BEGIN
- k := 0 ; i := a ;
- WHILE (i < b) AND (i < fsize) DO
- IF fbuf [i] # 0 THEN
- k := k + 1
- END ;
- INC (i)
- END ;
- RETURN k
- END NonZero ;
- PROCEDURE BaseName (src : PathBuf ; VAR dst : PathBuf) ;
- (* the fixture's own name, so the .COM lands beside us as NAME.COM rather
- than trying to graft a directory onto the source path.
- FIXED array formals, not open ones: with `VAR dst : ARRAY OF CHAR` the
- write did not reach the caller's buffer at all, so the .COM was written to
- a file named after whatever garbage was in the destination - literally
- "END", taken from adjacent string data. An open-array VAR formal is not
- worth the subtlety in a test harness. *)
- VAR i, j, last : CARDINAL ;
- BEGIN
- i := 0 ; j := 0 ; last := 0 ;
- WHILE (i <= HIGH (src)) AND (src [i] # 0C) DO
- IF src [i] = "/" THEN
- last := i + 1
- END ;
- INC (i)
- END ;
- i := last ;
- WHILE (i <= HIGH (src)) AND (src [i] # 0C) AND (j <= HIGH (dst)) DO
- dst [j] := src [i] ;
- INC (i) ; INC (j)
- END ;
- (* drop the .pas and add .COM *)
- IF j > 4 THEN
- IF (dst [j - 4] = ".") AND (dst [j - 3] = "p") AND (dst [j - 2] = "a")
- AND (dst [j - 1] = "s") THEN
- j := j - 4
- END
- END ;
- IF j + 4 <= HIGH (dst) THEN
- dst [j] := "." ; dst [j + 1] := "C" ; dst [j + 2] := "O" ;
- dst [j + 3] := "M" ; dst [j + 4] := 0C
- END
- END BaseName ;
- PROCEDURE IsHexCmd () : BOOLEAN ;
- VAR i : CARDINAL ;
- BEGIN
- IF lineBuf [0] # "@" THEN RETURN FALSE END ;
- i := 0 ;
- WHILE (i <= HIGH (lineBuf)) AND (lineBuf [i] # 0C) DO INC (i) END ;
- IF i # 4 THEN RETURN FALSE END ;
- RETURN (lineBuf [1] = "h") AND (lineBuf [2] = "e") AND (lineBuf [3] = "x")
- END IsHexCmd ;
- VAR
- errNo, errPos : CARDINAL ;
- ok : BOOLEAN ;
- zout : PathBuf ;
- nz : CARDINAL ;
- BEGIN
- PutStr ("FIXTURE RESULT") ;
- NL ;
- WHILE ReadLineStr (lineBuf) DO
- IF IsHexCmd () THEN
- hexOn := TRUE
- ELSE
- StrCopy (pathCopy, lineBuf) ;
- IF LoadFile (pathCopy) THEN
- ok := Compile (errNo, errPos) ;
- IF ok THEN
- BaseName (pathCopy, zout) ;
- IF WriteCom (zout) THEN
- IF ReadCom (zout) THEN
- nz := NonZero (ImageBytes (), fsize) ;
- PutStr (lineBuf) ;
- PutStr (" OK com=") ;
- PutCard (fsize) ;
- PutStr (" image=") ;
- PutCard (ImageBytes ()) ;
- PutStr (" data=") ;
- PutCard (DataBytes ()) ;
- PutStr (" nonzeroInGap=") ;
- PutCard (nz) ;
- NL ;
- IF hexOn THEN
- DumpAt (0, 16) ; (* runtime head *)
- DumpAt (ImageBytes () - 16, 16) ; (* code tail *)
- DumpAt (ImageBytes (), 16) (* the gap *)
- END
- ELSE
- PutStr (lineBuf) ; PutStr (" CANNOT READ BACK") ; NL
- END
- ELSE
- PutStr (lineBuf) ; PutStr (" WRITE_COM_FAILED") ; NL
- END
- ELSE
- PutStr (lineBuf) ; PutStr (" ERROR ") ;
- PutCard (errNo) ; PutStr (" at pos ") ; PutCard (errPos) ; NL
- END
- ELSE
- PutStr (lineBuf) ; PutStr (" CANNOT OPEN") ; NL
- END
- END
- END
- END ComTest.
|