Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - z88dk output asm

Reply to topic
Author Message
  • Joined: 25 Feb 2013
  • Posts: 384
  • Location: Osaka
Reply with quote
z88dk output asm
Post 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?
  View user's profile Send private message
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post 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
  View user's profile Send private message Visit poster's website
  • Joined: 17 Nov 2015
  • Posts: 97
  • Location: Canada
Reply with quote
Post Posted: Thu May 18, 2017 5:13 pm
Quote

- 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?


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.

Quote

can someone understand the role of the sections?


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.
  View user's profile Send private message Visit poster's website
  • Joined: 17 Nov 2015
  • Posts: 97
  • Location: Canada
Reply with quote
Post Posted: Thu May 18, 2017 5:40 pm
Quote

I use this to invoke the compiler

zcc +sms test.c -o test.bin --no-crt -create-app -no-cleanup



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.
  View user's profile Send private message Visit poster's website
  • Joined: 25 Feb 2013
  • Posts: 384
  • Location: Osaka
Reply with quote
Post 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>
  View user's profile Send private message
  • Joined: 17 Nov 2015
  • Posts: 97
  • Location: Canada
Reply with quote
Post Posted: Fri May 19, 2017 2:35 pm
kamillebidan wrote
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.


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.
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!