|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
Author | Message |
---|---|
|
z88dk output asm
Posted: Thu May 18, 2017 7:52 am
|
I tried to compile the following program with z88dk
void f(){ char a=0; while(1){ a++; } } void main(){ f(); } I use this to invoke the compiler zcc +sms test.c -o test.bin --no-crt -create-app -no-cleanup the assembly generated is the following MODULE test INCLUDE "z80_crt0.hdr" SECTION code_compiler ._f dec sp pop hl ld l,#(0 % 256) push hl .i_2 ld hl,0 ;const add hl,sp push hl call l_gchar inc hl pop de ld a,l ld (de),a dec hl jp i_2 .i_3 inc sp ret ._main call _f ret can someone understand the role of the sections? - why is this the sp decremented? isn't that done by "call"? - is it decremented for setting the "frame" ? even if the function takes no arguments? |
|
|
Posted: Thu May 18, 2017 8:34 am |
It should be something like this (writing from phone so I might oversee something): MODULE test INCLUDE "z80_crt0.hdr" SECTION code_compiler ._f dec sp ; Reserve 1 byte on stack for local variable a pop hl ; these 3 lines load the value 0 into variable a (the reseeved byte on stack) ld l,#(0 % 256) push hl .i_2 ld hl,0 ; these 2 lines load the address of variable a into hl add hl,sp push hl ; secure hl as it will get overwritten by the call call l_gchar ; Just a guess: Load char value from address hl into hl inc hl ; First part of a++ pop de ; Restore hl into de (So we have address of variable a in de) ld a,l ; These 3 lines are the rest of a++ ld (de),a dec hl ; This is because of the post-increment (which means increment value but use value before increment) jp i_2 ; while (1) .i_3 inc sp ; Free up memory that got reserved for variable a (after that sp points to the return address again) ret ._main call _f ret |
|
|
Posted: Thu May 18, 2017 5:13 pm |
Calindro's comments are exactly right. The "dec sp" is to reserve one byte for the local variable "a". There are two c compilers in z88dk and you're using sccz80 in that compile. sccz80 does not use ix to establish a stack frame and tries to reduce code size by using subroutines to implement compiler primitives (the 'call l_gchar'). The other c compiler is zsdcc (an integrated sdcc) which inlines most code and tries to reduce size by optimizing the result. It establishes a stack frame using ix.
Sections are named containers that will hold assembled bytes. A memory map (normally defined in the crt) determines where those sections are placed in memory. In this memory map, sections can be given their own ORG address or they can be sequenced after other sections. Sections are essential because the tools have to be able to separate code or const data that will be placed in ROM from variables that will be placed in RAM. They also make it possible to place code and data in separate memory banks. By default the compiler will assign code and data to the following sections: * code_compiler :- this is read only code * rodata_compiler :- this is read only data ("const int a = 500;") * data_compiler :- this is non-zero data in ram ("int a = 500;") * bss_compiler :- this is uninitialized data in ram ("int a;") You can change the section names used by the compiler with command line options "--constseg", "--codeseg", "--dataseg" (sccz80), "--bssseg" (sccz80). This is how you can get the compiler to put things into other memory banks, for example. (sdcc calls sections 'areas' and we've renamed the sections that zsdcc uses to the above) Then these sections will be organized in memory by the linker according to the memory map. A simple memory map might look like: section CODE org 0x0000 section code_compiler section code_user section DATA org 0xc000 section data_compiler section data_user section BSS section bss_compiler section bss_user This one will create a big section named "CODE" with ORG 0 containing little sections "code_compiler" and "code_user". Another big section "DATA" with ORG 0xc000 will contain "data_compiler", "data_user", "BSS" (note no ORG), "bss_compiler" and "bss_user". The linker will output binaries "foo_CODE.bin" and "foo_DATA.bin" corresponding to the big sections with their own ORG. The actual memory map used for the sms under the new c library is this for Banks 0+1 and RAM combined with this for bankswitched memory. You can probably guess that it's a simple matter to put things into memory banks by assigning to one of the "BANK_XX" sections. If you write asm code you will also have to assign that to appropriate sections defined by the memory map in order for it to be made part of the output. Some sections have been defined for user assembly code for this purpose: * code_user * rodata_user * smc_user :- for self-modifying code. This code will be copied to ram at startup * data_user * bss_user These sections have the same purpose as those defined above for the compiler. You can also assign to the memory banks using sections "BANK_XX" or to any other section defined in the memory map. One useful one might be "section code_crt_init" which gives a way for you to wedge initialization code into the crt that will be run just before main() is called. At the end of a z88dk compile, if "-create-app" is on the compile line, the z88dk tool appmake will be invoked to pick up the output binaries corresponding to these sections and make a final .sms file containing everything. |
|
|
Posted: Thu May 18, 2017 5:40 pm |
The "-no-cleanup" is going to leave all the temporary files in your temp directory. There is an easier way to see the output assembly language by telling zcc to stop after the assembly is generated: zcc +sms -v -a test.c The output file will be "test.c.asm". This file can also act as input to the compile process so, if you have a lot of time :), you can hand edit it and then continue the compile. zcc +sms -v --list test.c -o test -lndos -create-app (-lndos is required for classiclib compiles because the crt initializes stdio and stdio for the classiclib must have a disk component. ndos -- no dos -- is the dummy disk component). This will compile your program and create the .sms file as well as generate a list file for test.c "test.c.lis". This file is the assembly code just prior to input to the linker. You'll see assembly mnemonics and partially assembled bytes before absolute addresses have been patched in. Some more options you might find of interest: * -m generate a map file showing all symbols and their addresses * -vn keep zcc quiet * -g generate a global defines file showing only values of public symbols (-gp, -gpf are related) With your invocation there is something else going on: zcc +sms test.c -o test.bin --no-crt -create-app -no-cleanup The "--no-crt" means the library will not supply startup code. The crt(s) that z88dk provide define a memory map, set up the z80 page zero, initialize your program's variables and intiialize the sms hardware itself (mapper, psg, vdp, etc). Without a memory map, all your code and variables will be jumbled together and placed in rom. So the generated .sms file will not work. If you are going to use "--no-crt" you will have to supply those things that the crt normally supplies. z88dk will treat the first file on the compile line as the "effective crt" and will compile it last so that it has access to all symbol data. This is a bit of an advanced topic though :) I also want to point out that there are two c libraries in z88dk, called the classiclib and the newlib. The classiclib is the original library and it supports about 50 z80 targets. The newlib is a rewrite in asm aiming for a subset of C11 with an object-oriented unix i/o model. It supports six targets currently, including the sms. The newlib, which has recently been discussed here, incorporates devkitSMS as the base api. The classiclib is using another api whose origins I am unsure of. Someone mentioned it may have been written by haroldoop on these forums a long time ago. The newlib has a number of crt options including a minimal one that I think you are trying to achieve with the "--no-crt" option. Your compile could go like this: zcc +sms -vn --list -clib=new test.c -o test -create-app zcc +sms -vn --list -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app "clib=new" selects the newlib and sccz80 as compiler. "clib=sdcc_iy" selects the newlib and zsdcc as compiler. Both these select the default crt (-startup=0 is the default) which is the minimal one. The crt can be reduced further with pragmas but that is probably another topic. The particular crt that is used is controlled by a "-startup=n" parameter. The newlib's crts are described here. For the classic lib your compile line could look like this: zcc +sms -vn --list test.c -o test.bin -lndos -create-app zcc +sms -vn --list -compiler=sdcc -SO3 --max-allocs-per-node200000 test.c -o test.bin -lndos -create-app The classic lib has one crt for the sms. It will create stdin,stdout,stderr but the inherent code size penalty isn't too large. |
|
|
Posted: Fri May 19, 2017 12:56 am |
Dear Calindro and Alcoholics Anonymous,
thank you very much!! Now I really get what the compiler is doing As for zsdcc, I have problems compiling it. While the original sdcc compiles without errors (at least for the current version on their svn), compilation of zsdcc fails. when doing make -k, I get a lot of errors of the type Error: <u> undefined symbol encountered during assembly The first one is generated by ../../bin/sdcc -I../../device/include -I../../device/include/mcs51 -mds390 --nostdinc --std-c99 -c printf_large.c -o ds390/printf_large.rel
This is configure's output sdcc 3.6.6 is now configured for Build: x86_64-unknown-linux-gnu Host: x86_64-unknown-linux-gnu Source directory: . C compiler: gcc -std=gnu99 CFLAGS: -fms-extensions -std=gnu99 -pipe -ggdb -g -O2 C++ compiler: g++ CXXFLAGS: -pipe -ggdb -g -O2 CPPFLAGS: LDFLAGS: ENABLED Ports: ds390 yes ds400 yes hc08 yes s08 yes mcs51 yes pic14 yes pic16 yes z80 yes z180 yes r2k yes r3ka yes gbz80 yes tlcs90 yes stm8 yes Disable non-free lib: 0 Disable packihx: 0 Disable ucsim: 0 Disable device lib: 0 Disable sdcpp: 0 Disable sdcdb: 0 Disable sdbinutil: 0 Enable documentation: 0 Enable libgc: 0 Install paths: binary files: ${prefix} include/library files: ${datarootdir}/sdcc include files: ${datarootdir}/sdcc/include library files: ${datarootdir}/sdcc/lib non-free files: ${datarootdir}/sdcc/non-free non-free include files: ${datarootdir}/sdcc/non-free/include non-free library files: ${datarootdir}/sdcc/non-free/lib documentation: ${datarootdir}/doc/${PACKAGE} prefix: /usr/local datadir: ${datarootdir} datarootdir: ${prefix}/share Search paths (incomplete, see manual for all search paths): binary files: $SDCC_HOME/bin include files: /share/sdcc/include path(argv[0])/../share/sdcc/include /usr/local/share/sdcc/include /share/sdcc/non-free/include path(argv[0])/../share/sdcc/non-free/include /usr/local/share/sdcc/non-free/include library files: $SDCC_HOME/share/sdcc/lib/<model> path(argv[0])/../share/sdcc/lib/<model> /usr/local/share/sdcc/lib/<model> $SDCC_HOME/share/sdcc/non-free/lib/<model> path(argv[0])/../share/sdcc/non-free/lib/<model> /usr/local/share/sdcc/non-free/lib/<model> |
|
|
Posted: Fri May 19, 2017 2:35 pm |
You may get errors when some of the target libraries are built. When making changes for z80asm (z88dk's assembler), we made sure the changes worked with the asxxx family that sdcc uses but at the time we didn't realize there were other assemblers in the backend, for the dsxxx for example. So they may fail to build their libraries. For z88dk, this does not matter because the backend is done by z80asm and we only care about the z80 family. In other words, I think you probably have a successful compile. All you need built are "sdcc" and "sdcpp" - everything else doesn't matter. Rename those to "zsdcc" and "zsdcpp" and move them to z88dk/bin. There may still be some bugs in the latest sdcc & z88dk - some have been mentioned in the "SMS Target for Z88DK...".... Edit: I think the bugs have been found and fixed. Everything should be stable in the current github or in the May 21 build. |
|