Does z88dk/sdcc output a map file or file that can be used to see...

Other misc things
Post Reply
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Does z88dk/sdcc output a map file or file that can be used to see...

Post by alank2 »

Does z88dk/sdcc output a map file or file that can be used to see the size of functions and variables making up the final code?
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

If you add "-m" you will get a map file with all the symbols in the compile, and I mean everything including local defines and section boundaries.

For purposes of an example, I'm using terms.c from this program:
https://github.com/z88dk/z88dk/tree/mas ... r_priority

and I'm trying to find the length of the "print_line_word_wrapped" function:
https://github.com/z88dk/z88dk/tree/mas ... r_priority

Adding "-m" to the compile gives the map file "terms.map". A search for the symbol "_print_line_word_wrapped" yields this snippet:

Code: Select all

_main                           = $9360 ; addr, public, , terms_c, code_compiler, terms.c:1526
_isr                            = $9319 ; addr, public, , terms_c, code_compiler, terms.c:1476
_print_line_word_wrapped        = $91D0 ; addr, public, , terms_c, code_compiler, terms.c:1284
_dy                             = $DD83 ; addr, public, , terms_c, bss_compiler, terms.c:1250
The symbols are not sorted in address order. So to find the length of the function I look for the function following "print_line_word_wrapped" which is "isr" and the difference will give the size of "print_line_word_wrapped":

_isr - _print_line_word_wrapped = $9319 - $91d0 = 329 bytes. This will not include any library routines the function calls.


There is another perhaps easier way to do this and that's to generate list files during the compile. Adding "--list" to the compile cause zcc to generate the assembly file translation of c code just before it goes to the linker. So adding that there will be a "terms.c.lis" file. In that one I can find the two functions "print_line_word_wrapped" and "isr":

(a zsdcc compile here, sccz80 will look different but the idea is the same)

Code: Select all

1281  0000              ;        ---------------------------------
1282  0000              ; Function print_line_word_wrapped
1283  0000              ; ---------------------------------
1284  0000              _print_line_word_wrapped:
1285  0000  DD E5               push        ix
1286  0002  DD 21 00 00         ld        ix,0
1287  0006  DD 39               add        ix,sp
1288  0008  F5                  push        af
1289  0009  DD 4E 06            ld        c,(ix+6)
....
1473  0149              ;        ---------------------------------
1474  0149              ; Function isr
1475  0149              ; ---------------------------------
1476  0149              _isr:
1477  0149  F5                  push        af
1478  014A  C5                  push        bc
1479  014B  D5                  push        de
...
The second hex number in this line:

Code: Select all

1284  0000              _print_line_word_wrapped:
indicates the current assembly offset for the label "_print_line_word_wrapped"

and the second hex number in this line:

Code: Select all

1476  0149              _isr:
indicates the current assembly offset for the label "_isr".

Their difference will give the byte size of function "_print_line_word_wrapped":

0x0149 - 0x0000 = 329 bytes as above.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The section information at the end of the map file gives better information about what is laid out in memory and where. But it may be just as easy to look at the output binaries to see the final sizes of each block placed in memory. For the newlib, *_CODE.bin is the code section, *_BSS.bin is the bss section and *_DATA.bin is the data section.

In the map file, each defined section gets three variables defined by the linker:

__SECTIONNAME_head : the start address of the section
__SECTIONNAME_tail : one byte past the last address of the section
__SECTIONNAME_size : the size in bytes of the section

By default, the c compilers put their stuff into sections "code_compiler" - code, "rodata_compiler" - const data, "data_compiler" - initialized variables and "bss_compiler" - initially zero variables. This will remain unchanged unless you deliberately compile to different memory banks or your own sections. So you could look up "__code_compiler_size" in the map file to find out how much code the compiler generated. And of course, the library adds code too and that is not included in the compiler sections.

The overall size of the output binaries depends on the memory map. Sections are placed adjacent to each other according to the memory map (supplied by z88dk by default) and one binary is produced per controlling section, which is a section with an ORG address and such a controlling section also absorbs adjacent sections without an ORG.

For newlib compiles, the default z80 memory map looks like this:
https://github.com/z88dk/z88dk/blob/mas ... el_z80.inc

One controlling section is CODE because it's a section with an org address:

Code: Select all

SECTION CODE
org __crt_org_code
and the linker will output a binary with name "*_CODE.bin" which consists of this section and all following non-controlling sections. In this memory map that's section CODE through section CODE_END.

So to know the size of the output binary "*_CODE.bin", you'd compute from the map file:
__CODE_END_tail - CODE_head
desertkun
New member
Posts: 4
Joined: Sat Dec 05, 2020 12:39 am

Re: Does z88dk/sdcc output a map file or file that can be used to see...

Post by desertkun »

I wrote this python script to automate this:

Code: Select all

import fileinput

last_size = 0
last_item = None
entries = []

for line in fileinput.input():
    size_, entry_ = line.split(",")
    entry_ = entry_.strip()
    if entry_.startswith("i_") or entry_.startswith("__code_") or entry_.startswith("__rodata_") or entry_.startswith("__data_"):
        continue

    size_ = int(size_, 16)
    if size_ > 65535:
        continue

    diff = size_ - last_size

    if diff <= 0:
        last_size = size_
        continue

    if diff >= 64:
        entries.append((last_item, diff))

    last_size = size_
    last_item = entry_

for key, value in sorted(entries, key=lambda x: x[1], reverse=True):
    print("{0}: {1}".format(key, value))

name it whatever.py and use like so

Code: Select all

cat map_file.map | sed -n "s/^\\([a-zA-Z0-9_]*\\).*= .\([A-Z0-9]*\).*/\2,\1/p" | sort | python whatever.py
Post Reply