Close

Splitting the code to files properly

A project log for Semyon

A small simon game using the 8 pin STC15F104W, written in 8051 assembly using the SDAS(ASXXXX) assembler from the SDCC toolchain.

hummusprinceHummusPrince 08/22/2020 at 00:380 Comments

When the code has grown big enough, it made sense to split it into several files. So I've split it into files which make sense together. How can it be done?

First, one must have define files. Akin to header files in C, these are used to define constants - such as SFR locations - and macros - such as SFR assignments.

These files are pure assembly files, and as it seems that ASXXXX is agnostic to file extension, I'll go with ASXXXX examples and call these "define.def" and "macro.def".

These are used in other assembly files using the .include directive, just like C preprocessor #include.

The cruedest way to get all my files together is to create a capital assembly file which includes all these files. Here is "semyon.asm" where it all goes together:

.module semyon

;Def file includes
.include "define.def"
.include "macro.def"

;Asm file includes
.include "main.asm"
.include "intv.asm"
.include "inth.asm"
.include "dseg.asm"
.include "io.asm"
.include "delay.asm"
.include "pwm.asm"

The "include" directive just copies the included files into the caller file. Unsurprisingly, it gets assembled just as good as assembling it all in one file.

But now doing it properly

This approach is not good. The conceptual problem is that it's not doing what I intended - I didn't want it to simply copy and paste my code together, but to assemble it in pieces and then assign all the addresses and tie the hex file together.

The practical problem is that ASXXXX is not smart enough to trace bugs into the included files. A bug in "io.asm" will come out as a bug in "semyon.asm" in line 12, which is where the problematic file is included. It leaves you guessing where in that file the error has occurred, and I'm not masochistic enough for that.

Nope, the proper way mandates that I assemble each file independently. It makes sense to include all the ".def" files in each ".asm" file then, but some labels are cross referenced - for example, "main.asm" calls for functions from "io.asm". This can be solved by assembling all the files with global flags.

This is the makefile:

build:
    as8051 -losga main.asm
    as8051 -losga intv.asm
    as8051 -losga inth.asm
    as8051 -losga io.asm
    as8051 -losga delay.asm
    as8051 -losga dseg.asm
    as8051 -losga pwm.asm
    
    aslink -f semyon
    packihx semyon.ihx > semyon.hex

The "los" flags are the old output files flags. The "g" and "a" flags make user-defined and undefined symbols global, respectively (see docs.). It means that the output files expect to assign these at linking time.

Linking

The linker is called aslink (sdld in the sdcc version) and is quite simple. It can get directives from a file, using the "-f" flag.

The directives are simply structured: Linker flags, output file name, list of input files, and the reminder of the flags, mostly link-time symbol value assignments.

My linking file "semyon.lnk" is looking like that:

-mxiu
semyon
main
intv
inth
delay
pwm
dseg
io
-b CODE = 0x0090
-e

the -mxiu flags means generate map file, hex base, intel hex output, and update the list files, respectively. See more.

 "semyon" is the name of the output file, the rest are the input files (extensions get ignored).

"-e" is end-of-file marker flag.

About the .area directive

The mysterious .area directive which bugged me makes sense now when multi-file code is concerned - the linker should know how to put the assembled files together, at what addresses and so on.

The "intv.asm" file, which includes the interrupt vectors, should be strictly located in predefined addresses using the ".org" directive. Thus the area it got, called INTV, must be defined ABS, which means that the addresses must be manually assigned.

Most of the code however goes in the CODE area, which got the REL flag. That means each file to whom this area was assigned will be concatenated upon the other files in that area, and the ".org" directive is prohibited.

However, the REL areas must begin somewhere. Originally I wanted it to be at 0x90, after all the interrupt vectors. I can assign this to REL areas in link time, using the "-b" flag:

-b CODE = 0x0090

The code links very well this way, and function exactly as if it was a one-filer.

Discussions