Does z88dk/sdcc output a map file or file that can be used to see...
Does z88dk/sdcc output a map file or file that can be used to see...
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?
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:
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)
The second hex number in this line:
indicates the current assembly offset for the label "_print_line_word_wrapped"
and the second hex number in this line:
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.
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
_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
...
Code: Select all
1284 0000 _print_line_word_wrapped:
and the second hex number in this line:
Code: Select all
1476 0149 _isr:
Their difference will give the byte size of function "_print_line_word_wrapped":
0x0149 - 0x0000 = 329 bytes as above.
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:
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
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
So to know the size of the output binary "*_CODE.bin", you'd compute from the map file:
__CODE_END_tail - CODE_head
Re: Does z88dk/sdcc output a map file or file that can be used to see...
I wrote this python script to automate this:
name it whatever.py and use like so
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))
Code: Select all
cat map_file.map | sed -n "s/^\\([a-zA-Z0-9_]*\\).*= .\([A-Z0-9]*\).*/\2,\1/p" | sort | python whatever.py