I was curious to see how small a gt1 file the C compiler can produce for Horizon. This is also a good way to show how one can use compiler and linker options to change fine settings or investigate further.
- The first obvious change is to inline the function BusyWait, which causes main to first become a leaf function (a function that does not call another function) and, because we can allocate all variables into registers, it becomes a frameless function.
- Then we can play linker games. The first one is to use option --no-runtime-bss-initialization because, well, there is nothing to initialize and therefore no need to import the bss clearing code. The second one is to move the program into the 0x200-0x6ff area instead of splitting it into the video memory holes. I can achieve this by defining an overlay file, "horizon.ovl", that changes the segment allocation table of the 32k map.
Code: Select all
# horizon.ovl
# ------------size----addr----step----end---- flags (1=nocode, 2=nodata)
segments = [ (0x0500, 0x0200, None, None, 0),
(0x0060, 0x08a0, 0x0100, 0x80a0, 0) ]
The compilation command is
Code: Select all
$ ./build/glcc -rom=at67x -map=32k,./horizon.ovl stuff/horizon.c -o horizon_at67x.gt1 -Wl-d --no-runtime-bss-initialization
Compiling with option --rom=at67x to target at67's rom, -map=32k,./horizon.ovl to use the 32kb map with the above overlay, and -Wl-d to give a lot of information about the code placement. The resulting file is 941 bytes long (compared to at67's 756) and runs fast because of at67's native multiplication and division.
The ROMv5 equivalent is 1133 bytes long. The main difference is that it has to include the division code instead of using the native one.
Now we can look at all the information that comes with option -Wl-d. When it says that code fragment 'main is 372+10 bytes long, that means 372 bytes if all jumps are short, and 10 additional bytes for long jumps (with your rom, that's just for BRA). Functions marked 'nohop' are assembly code functions that are required to fit in a single page.
Code: Select all
$ ./build/glcc -rom=at67x -map=32k,./horizon.ovl stuff/horizon.c -o horizon_at67x.gt1 -Wl-d --no-runtime-bss-initialization
(glink debug) reading '/tmp/lcc2206852.o'
(glink debug) synthetizing module '_gt1exec.s' at address 0x200
(glink debug) reading '/home/quartus/gigatron/gigatron-lcc/build/cpu6/libc.a'
(glink debug) including module 'libc.a(_start.s)' for symbol '_start'
(glink debug) - code fragment '_start' is 63+0 bytes long
(glink debug) - code fragment '.callchain' is 37 bytes long (nohop)
(glink debug) including module 'stuff/horizon.c' for symbol 'main'
(glink debug) - code fragment 'main' is 372+10 bytes long
(glink debug) including module 'libc.a(_exitm)' for symbol '_exitm'
(glink debug) - code fragment '_exitm' is 33 bytes long (nohop)
(glink debug) including module '_gt1exec.s' for symbol '_gt1exec'
(glink debug) - code fragment '_gt1exec' is 36 bytes long (org:0x200)
(glink debug) including module 'libc.a(rt_mods.s)' for symbol '_@_mods'
(glink debug) - code fragment '_@_mods' is 19+0 bytes long
(glink debug) including module 'libc.a(rt_divs.s)' for symbol '_@_divs'
(glink debug) - code fragment '_@_divs' is 61+0 bytes long
(glink debug) including module 'libc.a(raise.s)' for symbol '_@_raise'
(glink debug) - code fragment 'raise' is 45 bytes long (nohop)
(glink debug) including module 'libc.a(_exits.c)' for symbol '_exits'
(glink debug) - code fragment '_exitmsg' is 51+4 bytes long
(glink debug) - code fragment '_exits' is 58+0 bytes long
(glink debug) including module 'libc.a(rt_save.s)' for symbol '_@_save_c0'
(glink debug) - code fragment '_@_save_ff' is 31 bytes long (nohop)
(glink debug) - code fragment '_@_rtrn_ff' is 37 bytes long (nohop)
(glink debug) pass 1
(glink debug) pass 2
(glink debug) pass 3
(glink debug) pass 4
(glink debug) assembling code fragment '_gt1exec' at 0x200 in Segment(0x200,0x300)
(glink debug) assembling code fragment 'main' at 0x224 in Segment(0x200,0x300)
(glink debug) - continuing code fragment 'main' at 0x300 in Segment(0x300,0x400)
(glink debug) assembling code fragment '_start' at 0x39d in Segment(0x300,0x400)
(glink debug) assembling code fragment '.callchain' at 0x400 in Segment(0x400,0x500)
(glink debug) assembling code fragment '_exitm' at 0x3dc in Segment(0x300,0x400)
(glink debug) assembling code fragment 'raise' at 0x425 in Segment(0x400,0x500)
(glink debug) assembling code fragment '_exitmsg' at 0x452 in Segment(0x400,0x500)
(glink debug) assembling code fragment '_exits' at 0x485 in Segment(0x400,0x500)
(glink debug) assembling code fragment '_@_save_ff' at 0x4bf in Segment(0x400,0x500)
(glink debug) assembling code fragment '_@_rtrn_ff' at 0x500 in Segment(0x500,0x600)
(glink debug) assembling code fragment '_@_divs' at 0x525 in Segment(0x500,0x600)
(glink debug) assembling code fragment '_@_mods' at 0x4de in Segment(0x400,0x500)
(glink debug) assembling DATA fragment '__glink_magic_init' at 0x4f1 in Segment(0x400,0x500)
(glink debug) assembling DATA fragment '__glink_magic_fini' at 0x4f4 in Segment(0x400,0x500)
(glink debug) assembling DATA fragment '_exitm_msgfunc' at 0x4f6 in Segment(0x400,0x500)
(glink debug) assembling DATA fragment '.13' at 0x562 in Segment(0x500,0x600)
(glink debug) assembling DATA fragment '.12' at 0x57b in Segment(0x500,0x600)
(glink debug) assembling DATA fragment '.9' at 0x593 in Segment(0x500,0x600)
(glink debug) assembling DATA fragment '.4' at 0x4f8 in Segment(0x400,0x500)
The function main --which is the only function here-- takes only 377 bytes long (from 0x224 to 0x39d) which is about the same as the size reported by at67. This swells to 425 bytes if we compile for rom v5a. So the main difference is the runtime which is substantially larger than that of at67's basic. Because the division code can raise a division-by-zero exception, the linker includes the function raise() and the corresponding exit messages (see
https://github.com/lb3361/gigatron-lcc/ ... c/_exits.c). They are useless because, in the absence of console support, they're not going to be printed ever. This is a bit silly but it does not bother me as much as the real weight of functions like scanf() or printf()...
There is a convenient option to produce the assembly code annotated with source code.
Code: Select all
$ ./build/glcc -rom=at67x -S -Wf-g,##### stuff/horizon.c
I had to disguise the resulting file as a text file to upload it. If you read the file, you'll notice some opcodes prefixed by an underscore (e.g., _MUL, _BEQ, etc.) These are defined by the assembler/linker glink.py. For instance _BEQ can be a short or a long branch, _LDI can be LDI, LDWI, or LDNI, _MUL calls a runtime routine, etc.