Tatara

Chapter 2
How an assembler and a linker work

This chapter explains what TATARA and TANREN each do, and why a program is built in two steps rather than one. It introduces the words that the rest of the manual uses, and follows one small program from the source files you write to the program you run.

2.1 Two steps

A program starts as one or more source files: text files that you write with an editor, in assembly language. Tatara’s source files are normally named with the extension .AS.

TATARA reads a source file and writes an object file, with the extension .TRO. An object file holds the program’s machine code, but it is not yet a program that can be run.

TANREN reads one or more object files and writes the finished program file. For MSX-DOS2 this is a file with the extension .COM, which you run by typing its name at the MSX-DOS2 prompt. Figure 2.1 shows the two steps.

PIC

Figure 2.1: The two steps from source to program.

In the terms of chapter 1, the source files are the raw materials, the object files are the raw steel, and the program is the finished blade. The rest of this chapter explains what happens in each step, and why the raw steel is worth having.

2.2 What the assembler does

A source file is a list of lines. Most of them are instructions for the Z80, written as short words that a person can read, such as ld c,_STROUT. The Z80 cannot read words. What it reads are bytes: numbers from 0 to 255, stored one after another in memory, which together are the program’s machine code. Turning each line of a source file into its bytes is called assembling.

Table 2.1 shows three lines and the bytes TATARA assembles them into. The bytes are written in hexadecimal.

Line Bytes
ld c,_STROUT 0E 09
call BDOS CD 05 00
ret C9
Table 2.1: Three lines and the bytes they become.

The first byte of each line says which instruction it is. The bytes after it are the instruction’s operand, if it has one. An address takes two bytes, and the Z80 stores the low byte first, so the address 0005h is stored as 05 followed by 00.

_STROUT and BDOS are not instructions. They are names for the numbers 09h and 0005h, and they are defined in MSXDOS.INC, one of the include files that come with Tatara (chapter 14). A program that includes that file can use the names, and the assembler replaces each name with its number. A name that stands for a number in this way is called a symbol.

A program also needs names for places in itself: the start of a loop, or a message to be printed. A label is such a name. It is written at the start of a line, followed by a colon, as in msg1:, and it stands for the address of the first byte that line produces: the number of the memory location where that byte will be. A label is a symbol too.

To know what each label stands for, the assembler counts the bytes as it goes. If a program’s first line produces three bytes and its second line produces three more, the third line starts six bytes after the first.

Note.  This manual assumes that you know what a byte, a hexadecimal number and a memory address are. The book or course you learn Z80 programming from explains them.

2.3 What the assembler cannot know

Counting bytes tells the assembler how far each label is from the start of the program. There are two things that a single source file cannot tell it.

  1. Where the program will be in memory. The assembler knows that a label is, say, 17 bytes after the start of the code, but it does not know at which address the code will start. That is decided later, when all the parts of the program are put together. An address that depends on where the code is placed is relocatable.
  2. Where things in other files are. A program can be split over several source files, and a line in one of them can call a routine that is written in another. TATARA assembles one file at a time, so it cannot know the address of a name that is defined in a different file.

TATARA therefore does all the work it can, and writes down what it could not finish. Where an address is still unknown, it leaves a hole in the machine code, and it records what belongs in the hole. The result is the object file: machine code with holes in it, and a note for each hole. This is the raw steel.

The code of one source file, as it is in its object file, is called a module. A module can offer some of its symbols to other modules, and it can use symbols that other modules offer:

A directive is a line that gives an instruction to the assembler rather than to the Z80; it produces no machine code of its own.

2.4 What the linker does

TANREN reads one or more object files and does three things.

  1. It places the modules one after another in memory, starting at address 0100h, which is where MSX-DOS2 loads a program and starts it.1
  2. It fills every hole. For a relocatable address, it adds the address where the module was placed to the value that TATARA left in the hole. For an external symbol, it finds the module that declared the symbol public and writes the symbol’s address into the hole. Filling the holes is called relocation.
  3. It writes the program file: the finished bytes, in a file that MSX-DOS2 can load and run.2

The whole job is called linking. It is the forging that turns the raw steel into a blade.

2.5 A worked example

The example TWOMOD is a program in two modules. It is one of the examples that come with Tatara; chapter 4 builds it, and chapter 28 returns to it. The first module, MAIN.AS, prints two lines by calling a routine named putstr, which it declares external:

; MAIN.AS - one of two modules. 
; 
; It knows that putstr exists. It does not know where putstr is, and 
; it does not need to: EXTRN says the name is somebody else's, and the 
; linker fills the address in. 
 
                extrn   putstr          ; defined in PUTSTR.AS 
 
                include msxdos.inc      ; BDOS and _TERM0 
 
                cseg 
 
start:          ld      hl,msg1 
                call    putstr 
                ld      hl,msg2 
                call    putstr 
                ld      c,_TERM0 
                jp      BDOS 
 
msg1:           db      "Two modules, one program.",13,10,0 
msg2:           db      "The linker joined them.",13,10,0 
 
                end     start   

The second module, PUTSTR.AS, defines putstr and declares it public:

; PUTSTR.AS - the other module. 
; 
; PUBLIC is what makes the name visible outside this file. Without it 
; the name would still assemble here and the link of MAIN.AS would 
; stop with an undefined symbol - which is worth trying once. 
 
                public  putstr 
 
                include msxdos.inc      ; BDOS, _CONOUT and "system" 
 
                cseg 
 
; putstr - print a string. 
; 
;   HL is pushed around the BDOS call because MSX-DOS promises nothing 
;   about the registers it hands back. 
; 
; Input:        HL -> the string, ending in a zero byte 
; Output:       it is printed 
; Modifies:     AF, BC, DE, HL 
 
putstr:         ld      a,(hl) 
                or      a 
                ret     z 
                push    hl 
                ld      e,a 
                system  _CONOUT 
                pop     hl 
                inc     hl 
                jr      putstr 
 
                end   

Both modules include MSXDOS.INC. The INCLUDE line reads that file as if its lines were written at that point, and it produces no bytes of its own. PUTSTR.AS uses system _CONOUT: system is a macro defined in MSXDOS.INC, a single name that stands for a group of lines. Here it stands for ld c,_CONOUT followed by call BDOS. Chapter 16 explains macros.

2.5.1 What TATARA writes for MAIN.AS

MAIN.AS assembles to 71 bytes of machine code with four holes in it, as table 2.2 shows. The offset is the number of bytes from the start of the module.

Offset Line Bytes Hole
0 ld hl,msg1 21 11 00 relocatable: msg1, 17 bytes in
3 call putstr CD 00 00 external: putstr
6 ld hl,msg2 21 2D 00 relocatable: msg2, 45 bytes in
9 call putstr CD 00 00 external: putstr
12 ld c,_TERM0 0E 00 none
14 jp BDOS C3 05 00 none: BDOS is always 0005h
17 msg1: db ... 28 bytes of text none
45 msg2: db ... 26 bytes of text none
Table 2.2: The module MAIN, as TATARA writes it.

A relocatable hole holds the label’s offset: 11h is 17, the offset of msg1. An external hole holds zero, because the assembler knows nothing about putstr except its name. The jp BDOS at offset 14 is not a hole: BDOS is the fixed address 0005h, the same wherever the program is placed.

2.5.2 What TATARA writes for PUTSTR.AS

PUTSTR.AS assembles to 14 bytes, and it has no holes at all. The call BDOS inside system _CONOUT calls a fixed address. The jr putstr at its end does not hold an address either: it holds a distance, “jump back 14 bytes”, which is the same wherever the code is placed. The module has one public symbol, putstr, at offset 0.

2.5.3 What TANREN does

TANREN places MAIN first, because it is named first, and PUTSTR straight after it. Then it fills the four holes, as table 2.3 shows.

MAIN placed at 0100h (71 bytes, to 0146h)
PUTSTR placed at 0147h (14 bytes, to 0154h)
so putstr is 0147h
hole at 0101h 0011h + 0100h = 0111h, the address of msg1
hole at 0104h 0147h, the address of putstr
hole at 0107h 002Dh + 0100h = 012Dh, the address of msg2
hole at 010Ah 0147h, the address of putstr
Table 2.3: How TANREN links MAIN and PUTSTR.

The result is TWOMOD.COM, 85 bytes long, which MSX-DOS2 loads at 0100h. Figure 2.2 shows the two object files and the program they become.

PIC

Figure 2.2: Linking MAIN.TRO and PUTSTR.TRO into TWOMOD.COM.

2.6 Code and data: segments

A program holds two kinds of thing: instructions, and the data they work on. Tatara lets you keep them apart, in two segments. The code segment is started with the CSEG directive, and the data segment with DSEG.

When TANREN places the modules, it puts the code segments of all the modules first, one after another, and the data segments after them. In the finished program each kind is therefore in one piece.

TWOMOD keeps its two messages in the code segment, which is fine for a program this small, and its data segment is empty. Chapter 10 explains segments in full, including segments with names of their own and data areas that different parts of a program can share.

2.7 Why two steps are worth it

Building a program in two steps takes one more command than building it in one, and it gives four things in return.

Reuse.

A module such as PUTSTR can be linked into any number of programs without being assembled again.

Size.

A large program is easier to work on when it is split over several files, and only the file that changed needs to be assembled again.

Collaboration.

A programmer can write a library of routines, assemble it, and make its object files available. Other programmers link it into their own programs without copying its code into theirs, and without needing its source files at all: the object file is enough.

Choice of output.

Where the program is placed is decided only at the end, so the same object files can become a program for MSX-DOS2, a binary for MSX-BASIC’s BLOAD, or a ROM cartridge (chapter 26).

2.8 The words in this chapter

Table 2.4 lists the terms this chapter has introduced. The glossary (appendix L) lists them with every other term in the manual.

Term

Meaning

source file

the text file you write, in assembly language

machine code

the bytes the Z80 reads and runs

assembling

turning source lines into machine code

symbol

a name that stands for a number

label

a symbol that stands for an address in the program

address

the number of a memory location

relocatable

an address that depends on where the code is placed

hole

a place in the machine code where an address is still unknown

object file

machine code with holes, written by TATARA

module

the code of one source file, in its object file

public symbol

a symbol a module offers to other modules

external symbol

a symbol a module uses but another module defines

relocation

filling the holes

linking

placing the modules, filling the holes, and writing the program

program file

the finished program, written by TANREN

segment

an area of the program for one kind of content: code or data

Table 2.4: The words introduced in this chapter.

1This is the default, because TANREN builds MSX-DOS2 programs unless it is told otherwise. It can be given another start address, to build a program that runs somewhere else in memory, such as a ROM cartridge or a binary for MSX-BASIC’s BLOAD (chapter 24).

2Again, this is the default. TANREN can also write a binary for MSX-BASIC’s BLOAD, or the image of a ROM cartridge (chapter 26).