Chapter 24
How a program is laid out
TATARA gives every label an address inside its segment, starting from 0 (chapter 10). TANREN decides where each segment of each module goes in memory, and so gives every label its final address. This chapter describes the rules it follows, the options that change them, and what ends up in the output file.
24.1 Code, then data
TANREN places all the code first and all the data after it:
- The code segments start at memory address 0100h, where MSX-DOS loads a .COM program.
- The data segments start straight after the last byte of code.
A segment used by several modules becomes one piece, with each module’s part in the order the modules are named on the command line.
These two modules each have some code and some data. A.AS also has a named code segment, extra:
; A.AS - code, data, and a named code segment. public astart,aextra,adata cseg astart: ld hl,adata ret cseg extra aextra: nop dseg adata: ds 4 end astart
; B.AS - code and data. public bcode,bdata cseg bcode: ld hl,bdata ret dseg bdata: db 1,2 end
/M makes TANREN print, among other things, the segments of the program with the address each one was given:
A:\>tanren /m a b ... Modules: 2 Groups: Segments: flags 00 group FF base 0100 size 0008 end 0107 C flags 00 group FF base 0108 size 0001 end 0108 extra flags 01 group FF base 0109 size 0006 end 010E D Symbols: D- 0100 astart D- 0108 aextra D- 0104 bcode D- 0109 adata D- 010D bdata 2 modules, 21 records, ends at EOF. Wrote A.COM, 0100-010E (15 bytes), entry 0100.
Each line under Segments: is one segment, in the order TANREN placed them. The last word is the segment’s name: C is the default code segment, D the default data segment, and any other name is a named segment. base is its first address, size its size in bytes, and end its last address. flags is 00 for code and 01 for data. The groups and the symbols, and the rest of /M, are described in chapter 25.
Figure 24.1 shows the same program in memory. The code segment holds A’s four bytes and then B’s, from 0100h to 0107h; extra follows at 0108h; and the data segment starts at 0109h, with A’s four bytes and then B’s two.
Naming the modules the other way round, tanren b a, puts B’s part first in each segment: bcode moves to 0100h, astart to 0104h, and the entry address with it.
24.1.1 What goes into the file
The output file holds the memory from the first byte that has content to the last one. A DS reserves memory without giving it any content (chapter 11), so a DS at the end of the program is not written to the file at all. With tanren b a, A’s ds 4 comes last, and the file ends at 010Ah, after B’s two bytes:
A:\>tanren /m b a /o:ba.com ... WARNING: BA.COM is entered at 0100, not at 0104. 2 modules, 21 records, ends at EOF. Wrote BA.COM, 0100-010A (11 bytes), entry 0104.
The same applies to the first bytes. The memory a DS leaves out of the file is not cleared either: when the program starts, it holds whatever was there before. A program that needs a variable to start at 0 must set it to 0 itself, or define it with DB or DW.
24.2 Named segments
A program can have several segments of each kind (section 10.5). Within each kind, TANREN places the default segment first, and then the named segments in the order they first appear in the modules, taken in command-line order. This module selects its named segments before the default ones:
; ORDER.AS - named segments written before the default ones. public zfirst,cmain,zdata,dmain cseg zcode zfirst: nop cseg cmain: ret dseg zvars zdata: db 1 dseg dmain: db 2 end cmain
A:\>tanren /m order ... Segments: flags 00 group FF base 0100 size 0001 end 0100 C flags 00 group FF base 0101 size 0001 end 0101 zcode flags 01 group FF base 0102 size 0001 end 0102 D flags 01 group FF base 0103 size 0001 end 0103 zvars
The default code segment still starts at 0100h. This matters for a .COM program, which MSX-DOS always starts at 0100h: the code that runs first belongs in the default code segment of the first module.
24.3 Moving the code and the data: /P: and /D:
/P: starts the code at another address, and the data follows it as before:
A:\>tanren /m /p:8000 a b /o:p8000.bin ... Segments: flags 00 group FF base 8000 size 0008 end 8007 C flags 00 group FF base 8008 size 0001 end 8008 extra flags 01 group FF base 8009 size 0006 end 800E D ... Wrote P8000.BIN, 8000-800E (15 bytes), entry 8000.
MSX-DOS loads a .COM program at 0100h, so a program linked for another address is not a .COM program: it is loaded in some other way, for example by MSX-BASIC’s BLOAD (chapter 26).
/D: starts the data at an address of its own, instead of straight after the code. The data can be above the code or below it. With /p:c000 /d:8000, the code is at C000h and the data under it at 8000h:
A:\>tanren /m /p:c000 /d:8000 a b /o:high.bin ... Segments: flags 00 group FF base C000 size 0008 end C007 C flags 00 group FF base C008 size 0001 end C008 extra flags 01 group FF base 8000 size 0006 end 8005 D ... Wrote HIGH.BIN, 8004-C008 (16389 bytes), entry C000.
The file starts at 8004h, not 8000h, because the first four bytes of data are A’s ds 4, which have no content (section 24.1.1).
The data cannot start inside the code: /d:0102, with code from 0100h to 0108h, stops TANREN with /D: would put the data on top of the code.
Warning. Everything between the code and the data is in the file, written as zeros. With /d:c000, a .COM program of fifteen bytes of code and data becomes a file of 48,902 bytes:
A:\>tanren /m /d:c000 a b /o:dc000.com ... Wrote DC000.COM, 0100-C005 (48902 bytes), entry 0100.A data segment made only of DS has no content, and adds nothing to the file wherever it is placed.
24.4 Transient segments across modules
The groups of a transient data segment (section 10.6) share the same memory, and TANREN applies this across modules: every group of a transient segment, in whichever module, starts at the same address, and the segment takes the size of its largest group. These two modules each add a group to the segment scratch:
; T1.AS - the group reading of the transient segment scratch. public inbuf dseg scratch,transient group reading inbuf: ds 64 end
; T2.AS - the group writing of the same segment. public outbuf dseg scratch,transient group writing outbuf: ds 32 end
A:\>tanren /m a b t1 t2 /o:tr.com ... Groups: reading writing Segments: flags 00 group FF base 0100 size 0008 end 0107 C flags 00 group FF base 0108 size 0001 end 0108 extra flags 01 group FF base 0109 size 0006 end 010E D flags 03 group 00 base 010F size 0040 end 014E scratch flags 03 group 01 base 010F size 0020 end 012E scratch ... Wrote TR.COM, 0100-010E (15 bytes), entry 0100.
A transient segment has one line for each group, with flags 03, and the number of the group in Groups:, counting from 00. Both groups start at 010Fh (figure 24.2), so inbuf and outbuf have the same address, and scratch takes 64 bytes, the size of reading. It holds only DS, so the file is still fifteen bytes long.
24.5 Absolute segments
An absolute segment (chapter 10) is not moved: its bytes go at the addresses written in the source. This module puts two bytes at 4000h:
; ABS.AS - two bytes at 4000h. public romhdr aseg org 4000h romhdr: db 'AB' end
Linked with A and B, it makes the program run from 0100h to 4001h, and everything between the last byte of data and 4000h is written as zeros (figure 24.3):
A:\>tanren /m a b abs /o:gap.com ... Wrote GAP.COM, 0100-4001 (16130 bytes), entry 0100.
Absolute segments are meant for programs that have to put bytes at fixed addresses, such as a ROM image (chapter 26). In a .COM program, bytes far above the code make a large file.
24.6 Messages
Table 24.1 lists the messages about the layout.
Message |
Meaning |
ERROR: /D: would put the data on top of the code. |
|
ERROR: the linked image would run past FFFFh. |
The program does not fit below FFFFh from the address it starts at, for example 300 bytes of code with /p:ff00. |