| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- IMPLEMENTATION MODULE Linker ;
- (* Linker and .COM writer for TP3-compiled programs.
- The compiler has already produced the whole image - Runtime is copied to
- offset 0 of the code buffer and the program follows it, so every address in
- the image is already absolute (see Compiler.Inittur). There is therefore
- no relocation to do: "linking" simply writes the code image as a DOS .COM.
- The globals are NOT in that file. They live at Compiler.DataBase = 8000h
- image (8100h after the load bias), and the runtime's initmem zeroes
- [hdrDS, hdrHeap) before the program runs. This is the flat-segment
- equivalent of the original's layout: TP3 puts the runtime and program in CS
- and the globals in a separate DS segment, so its .COM is code-only too.
- Keeping the data out of the file is what lets the code grow past 4 KiB
- without overlapping it, and it is why the file length is ImageBytes ().
- The original wrote its .COM through the overlay loader (TPSRC7 opendest)
- from a different buffer layout with a larger header. Our header is our
- own - see Runtime.EmitInitMem - but the file/data separation is the same. *)
- FROM SYSTEM IMPORT BYTE, ADR ;
- FROM Posix IMPORT open, write, close, unlink ;
- FROM Compiler IMPORT ImageBytes, ImageByteAt ;
- CONST
- O_WRONLY = 1 ;
- O_CREAT = 64 ;
- O_TRUNC = 512 ;
- PROCEDURE DropCh (v : CHAR) ;
- BEGIN
- END DropCh ;
- PROCEDURE DropB (v : BOOLEAN) ;
- BEGIN
- END DropB ;
- PROCEDURE DropC (v : CARDINAL) ;
- BEGIN
- END DropC ;
- PROCEDURE ZCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
- (* Copy into a NUL-terminated buffer. The C bindings take a plain ADDRESS, so
- the string must be terminated in OUR buffer - passing the caller's
- unbounded array straight through would pass a pointer to whatever followed
- it.
- The VAR is load-bearing, and was found by running: without it the formal is
- a COPY, the writes land in the copy, and the caller writes to whatever
- garbage was in its buffer. The symptom was a .COM file named "END" -
- adjacent string data - created with the right size and the right contents
- in the wrong file. tests/OpenArrTest.mod measures the three variants. *)
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (dst) - 1) AND (i <= HIGH (src)) AND (src [i] # 0C) DO
- dst [i] := src [i] ;
- INC (i)
- END ;
- dst [i] := 0C
- END ZCopy ;
- PROCEDURE LinkSize () : CARDINAL ;
- (* The .COM contains the runtime and the program's code ONLY. The globals
- live at Compiler.DataBase = 8000H in memory (8100H after the load bias),
- outside the file, and the runtime's initmem zeroes them at startup. This
- matches the original's separate DS segment: the file is code, the data
- area is zero-filled memory. *)
- BEGIN
- RETURN ImageBytes ()
- END LinkSize ;
- PROCEDURE WriteCom (path : ARRAY OF CHAR) : BOOLEAN ;
- (* Write the linked image to `path` as a DOS .COM. Returns TRUE on success;
- on failure the file is removed, so a half-written .COM is never left
- behind to be mistaken for a good one. *)
- VAR fd : INTEGER ;
- n : LONGINT ;
- i, total, chunk : CARDINAL ;
- buf : ARRAY [0..255] OF BYTE ;
- z : ARRAY [0..511] OF CHAR ;
- BEGIN
- ZCopy (z, path) ;
- total := LinkSize () ;
- fd := open (ADR (z), O_WRONLY + O_CREAT + O_TRUNC, 420) ;
- IF fd < 0 THEN
- RETURN FALSE
- END ;
- i := 0 ;
- WHILE i < total DO
- chunk := 0 ;
- WHILE (chunk < 256) AND (i + chunk < total) DO
- buf [chunk] := ImageByteAt (i + chunk) ;
- INC (chunk)
- END ;
- n := write (fd, ADR (buf), chunk) ;
- IF n # VAL (LONGINT, chunk) THEN
- DropC (VAL (CARDINAL, close (fd))) ;
- DropB (unlink (ADR (z)) = 0) ;
- RETURN FALSE
- END ;
- i := i + chunk
- END ;
- IF close (fd) # 0 THEN
- DropB (unlink (ADR (z)) = 0) ;
- RETURN FALSE
- END ;
- RETURN TRUE
- END WriteCom ;
- END Linker.
|