Assembly Listing?

Discussion about other targets
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Assembly Listing?

Post by bill2009 »

I'm using z88dk/sdcc with the following basic command line

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy --max-allocs-per-node200000 -o testembed.bin -notemp testembed.c

what do i have to add to get an assembly listing?

Also, does the crt get assembled each time so i could see it in the listing?

Also, my output seems to end up in testembed_code.bin, does that make sense? And I don't think i get that file if i don't use -notemp
Last edited by bill2009 on Wed Jan 06, 2016 3:14 pm, edited 1 time in total.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:I'm using z88dk/sdcc with the following basic command line

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy --max-allocs-per-node200000 -o testembed.bin -notemp testembed.c

what do i have to add to get an assembly listing?
You can add '-a' to stop after the asm listing is generated:

zcc +embedded -vn -a -SO3 -clib=sdcc_iy --max-allocs-per-node200000 testembed.c

"-startup=0" selects the crt to use so it's irrelevant in the C translation but you can leave it there no harm done.
"-o testembed.bin" will rename the generated asm file so you probably want to leave that out.
"-notemp" is discussed below.

The output will be the single file "testembed.opt" containing the translated C after all optimizations and translations have been applied.

"-O2" is implied in this compile line meaning sdcc's output is translated to Zilog form and a few code transformations are applied. If you drop it to "-O1" sdcc's calls to its primitives will not be changed to callee (this will negatively impact on code size). If you drop to "-O0" the output filename will change to "test.asm" (.asm meaning no -O applied) and this will be in asz80 form (sdcc's assembler).

If you drop the peephole level "-SO3" to "-SO1" the peephole rules applied will be sdcc's original set. If you drop to "-SO0" no peephole rules will be applied.

You can also add "--fverbose-asm" to see what peephole rules have been applied in the output asm:

zcc +embedded -vn -a -SO3 -clib=sdcc_iy --max-allocs-per-node200000 testembed.c --fverbose-asm

In the output comments will appear indicating where peephole rules were applied and comments at the top of each function will indicate if the code might be improved with higher "--max-allocs-per-node" setting.

An example:

Code: Select all

;        ---------------------------------
; Function main
; ---------------------------------
;        Register assignment is optimal.
; Stack space usage: 0 bytes.
"Register assignment is optimal" means "--max-allocs-per-node" is high enough for this function.
Also, does the crt get assembled each time so i could see it in the listing?
It does -- it gets assembled from raw state in the last linking step so that it can gather the final defined options from the "zcc_opt.def" file that is generated when other files are compiled.

Seeing it in the listing.. I think we missed a trick here. What you see in the listing is a thoroughly unhelpful INCLUDE that includes the actual crt. I'll try further to find a way to see the crt after options have been applied but at the moment it looks like you have to look at it in a disassembler after the binary is built :( . The crt will be right at the beginning of the output binary.
Also, my output seems to end up in testembed_code.bin, does that make sense? And I don't think i get that file if i don't use -notemp
It should be "testembed_CODE.bin". The binaries have a section name appended to the output filename corresponding to all code/data assigned to that section. The crts set up a memory map that consists of three major blocks: CODE, DATA, BSS and in the ram model the DATA and BSS sections are just appended to CODE so the output is a single binary corresponding to everything in the CODE section, including DATA and BSS.

If you have a disjoint memory map where you might want some stuff in a different location, the means to do that is to create a new section and assign an ORG to it. Then when the project is compiled the output will be *_CODE.bin and *_NAME.bin corresponding to the two different sections defined with their own ORG. You can see we prefer to output to raw binaries rather than ihx; ihx would bundle separate regions into a single file.

Just so it's clear: "testembed_CODE.bin" is the name of your binary output and you place that at the ORG address and execute from the ORG address to run the binary. If you didn't change your ORG address, this address is 0, otherwise you can change the ORG address by adding "#pragma output CRT_ORG_CODE = ????" to your embedtest.c source file where "????" is the address in decimal. It doesn't matter where -- you can stick it at the top, eg.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

To generate a listing file this is what I used:

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy -Ca--list -notemp --max-allocs-per-node200000 test.c -o test

"-notemp" is necessary otherwise the listing file is left in the temp dir.

"test.lst" is the listing file corresponding to "test.c"

"crt0.asm" and "crt0.opt" are the crts. As you can see, they only consist of INCLUDEs which don't help at all.

"test.c" go through these steps which all appear because of "-notemp":

test.i is test.c after the preprocessor is applied
test.i2 is test.i after zpragma is applied. this is the input to sdcc
test.asm is sdcc's output after peephole step
test.op1 is sdcc's output translated to Zilog and z80asm directives
test.opt is test.op1 after sdcc's calls to its primitives are changed to callee linkage

test_CODE.bin is the output binary
test_CODE.reloc contains relocation information if the binary needs to be patched to a new address


"test" is the binary contents of the unnamed section. This should be empty. Anything in this file indicates something was not assigned to a section and exists outside the memory map.


That's probably much more information than you need to know :P

I prefer the asm output file but if you want to see the output bytes a listing file is necessary. I can see that we need to copy the listing file out of the temp dir so that "-notemp" is unnecessary and we need to provide a way to see the final crt listing. z80asm is being actively developed so these usage issues do still crop up when someone tries something a little bit different. I'll add these two to the to-do list.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

It turns out it was fairly easy to do the list files so zcc has been updated and a new zcc binary will be in the Jan 7 nightly build. Just copy the zcc binary from z88dk/bin to your own z88dk/bin. I personally prefer the asm output with -a option but the list files will include the crt which is different.

If you are using windows, this is my compiled zcc.exe if you don't want to wait:
https://drive.google.com/file/d/0B6XhJJ ... sp=sharing

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy --list --max-allocs-per-node200000 test.c -o test

The new "--list" option will generate list files for all input source files including the crt.

At the stage that the list files are generated, addresses have not yet been patched by the linker. For example in a simple test compile something like this was generated in the list file:

Code: Select all

328   0000              _main:
329   0000  21 0E 00            ld        hl,___str_0
330   0003  E5                  push        hl
331   0004  21 00 00            ld        hl,_buffer
332   0007  E5                  push        hl
333   0008  CD 00 00            call        _printf
334   000B  F1                  pop        af
335   000C  F1                  pop        af
336   000D  C9                  ret
Notice the call to printf is calling address 0. This is because printf's address has not been resolved at the assemble stage when the list file is created.

For crt0.lst, the entire asm source listing is present, including IFDEF blocks that are present for implementing options. These options have already been processed at the list file stage so you can see what is actually present by looking at whether output is generated for asm statements.

Here's a brief example from a crt0.lst file:

Code: Select all

616   0000                 ; terminate
617   0000              
618   0000                 IF __crt_enable_restart
619   0000              
620   0000                    ; restart the program
621   0000              
622   0000                    IF __register_sp = -1
623   0000              
624   0000                       ; restore sp
625   0000              
626   0000                       ld sp,(__sp)
627   0000              
628   0000                    ENDIF
629   0000              
630   0000                    IF __crt_org_code = 0
631   0000              
632   0000                       di
633   0000              
634   0000                    ENDIF
635   0000              
636   0000                    jp __Restart
637   0000              
638   0000                 ELSE
639   0000              
640   0000                    ; exit program
641   0000              
642   0000  E1                pop hl                   ; hl = return status
643   0001              
644   0001                    IF __crt_org_code = 0
645   0001              
646   0001  76                   halt                  ; some tools like to see this
647   0002  18 FE                jr ASMPC              ; loop forever
648   0004              
649   0004                    ELSE
650   0004              
651   0004                       ld sp,(__sp)
652   0004                       ret                   ; return to host
653   0004              
654   0004                    ENDIF
655   0004              
656   0004                 ENDIF
What happens at program termination depends on crt options. The program might loop forever if there is no host present, the crt may restart the program or the program may exit to a host.

In the above you can see that three instructions are generated corresponding to "halt; jr ASMPC". This is an infinite loop so the crt is set up to halt the program on exit. The other options do not generate code.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

Ok, thanks again. I was able to see the crt assembly by running the compile with -v and reissung the second z80asm with --list

z80asm --list -a -m -Mo -oto -Lc:\z88dk\Lib\Config\\..\..\\libsrc\_DEVELOPMENT\lib\sdcc_iy -iembedded -D__SDCC -D__SDCC_IY -Ic:\z88dk\Lib\Config\\..\..\\libsrc\_DEVELOPMENT\target\embedded crt0.opt testembed.o

I'm currently puzzling through it but there may just be too much sophistication going on here for my simple mind and target.

I started poking at z88dk because SDCC required me to explicitly locate my code and data and it does runtime initialization of globals. I'm beginning to think z88dk is too steep a hill to climb for the benefit in my limited case. Clearly it's an excellent piece of work but it may just be too heavy duty for me in this instance.

I do thank you for the fast and effective response though. Much appreciated.

Actually, one more question. Would it be possible to have a CRT that did absolutely nothing other than jump to or call main?
Last edited by bill2009 on Wed Jan 06, 2016 10:12 pm, edited 1 time in total.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:I'm currently puzzling through it but there may just be too much sophistication going on here for my simple mind and target.
Most of the time, especially for embedded targets, the crt comes down to a couple dozen bytes or less. It's just that z88dk tries to be a complete C implementation which means we have to deal with streamed i/o and device instantiation, among other things. sdcc is much more minimal in terms of its crts and libraries, only implementing just enough to support simple embedded systems. There are no streams, eg, the entire scanf side is missing, etc. So the crts look very simple in comparison :) But we make sure crt options are able to pare down to the minimum required; the embedded target's crt is written to be suitable for very simple devices.

I find it much easier to look at the m4 macro defining the embedded target crt to understand what is going on. That was written by a human :) -- what is seen after that is the result of macro expansion.

z88dk/libsrc/_DEVELOPMENT/target/embedded/startup/embedded_crt_rom.m4
(there is only one crt for the embedded target and it is generated from this)
http://z88dk.cvs.sourceforge.net/viewvc ... iew=markup

The base directory for the includes is
z88dk/libsrc/_DEVELOPMENT/target/embedded/

Lines 11-17 settle on the final crt options. The C library defines defaults, the target can override those and finally pragmas embedded in the C source override those. crt options are things like code org, data org, bss org, heap size and so on.

Lines 19-23 define the memory map. The supplied memory map creates the standard CODE/DATA/BSS sections.

Lines 25-30 define some global symbols. These are things internal to the c lib as well as defines for IOCTLs and error numbers.

Lines 32-43 instantiate drivers and define FILE streams (stdin, stdout, stderr) and what file descriptors device drivers are assigned to. The embedded target doesn't have any drivers so there is just a begin and end with nothing in between.

Line 45 and up is the first actual startup code belonging to the crt. There are two cases for the embedded target that complicate the reading somewhat. If the code org is 0, the crt inserts the z80 restarts. Otherwise the restarts are not inserted. If your org is away from 0, the startup code then looks like this:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STARTUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SECTION CODE

PUBLIC __Start, __Exit

EXTERN _main

__Start:

   ; set stack address
   
   IF (__register_sp = -1) || (__crt_enable_restart = 0)
   
      ; save current sp if sp not supplied or returning to host
      
      ld (__sp),sp
   
   ENDIF

__Restart:

   IF __register_sp != -1
   
      ; crt supplies sp
   
      ld sp,__register_sp

   ENDIF
   
   ; commandline
   
   IF __crt_enable_commandline = 1
   
      ld hl,0
      push hl                  ; argv[argc] = NULL
      add hl,sp
      push hl                  ; argv[0] = ""
      dec hl
      dec hl                   ; hl = argv
      ld bc,1                  ; bc = argc = 1
      
      IF __SDCC | __SDCC_IX | __SDCC_IY

         push hl               ; argv
         push bc               ; argc
      
      ELSE
      
         push bc               ; argc
         push hl               ; argv

      ENDIF
   
   ENDIF

   ; initialize data section

   include "../clib_init_data.inc"

   ; initialize bss section

   include "../clib_init_bss.inc"

SECTION code_crt_init          ; user and library initialization
SECTION code_crt_main

   ; call user program
   
   call _main                  ; hl = return status

   ; run exit stack

   IF __clib_exit_stack_size > 0
   
      EXTERN asm_exit
      jp asm_exit              ; exit function jumps to __Exit
   
   ENDIF

__Exit:

   IF __crt_enable_restart = 0
   
      ; returning to host
      
      push hl                  ; save return status
   
   ENDIF

SECTION code_crt_exit          ; user and library cleanup
SECTION code_crt_return

   ; close files
   
   include "../clib_close.inc"

   ; terminate
   
   IF __crt_enable_restart

      ; restart the program
   
      IF __register_sp = -1
   
         ; restore sp
   
         ld sp,(__sp)
   
      ENDIF
      
      IF __crt_org_code = 0
      
         di
      
      ENDIF
      
      jp __Restart

   ELSE

      ; exit program

      pop hl                   ; hl = return status

      IF __crt_org_code = 0
      
         halt                  ; some tools like to see this
         jr ASMPC              ; loop forever

      ELSE

         ld sp,(__sp)
         ret                   ; return to host
   
      ENDIF
      
   ENDIF

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; RUNTIME VARS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

IF (__register_sp = -1) || (__crt_enable_restart = 0)

   SECTION BSS_UNINITIALIZED
   __sp:  defw 0

ENDIF

include "../clib_variables.inc"
include "clib_target_variables.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CLIB STUBS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include "../clib_stubs.inc"
which is a little more manageable. There are still options present that are deciding whether the program will loop forever, restart, or return to host on program exit and whether argc and argv have to be generated.

If the stack pointer must be saved, it is saved in the special BSS_UNINITIALIZED section that is separate but adjacent to BSS. This prevents the stored SP value from being zeroed when the BSS section is initialized.

The variables and stubs are c lib quantities like initialization of the heap and things that are not implemented yet but need a name defined to allow compilation to complete.


What's controlling which crt options are defined are your program's pragmas and the defaults defined in:
z88dk/libsrc/_DEVELOPMENT/target/embedded/crt_target_defaults.inc
http://z88dk.cvs.sourceforge.net/viewvc ... iew=markup

You're using startup=0 which sets __CRTDEF=0 so the active crt options are between lines 18 and 51. These crt options are discussed in the wiki:
http://www.z88dk.org/wiki/doku.php?id=t ... figuration

The minimal crt will result by turning most things off so these changes to those defaults is what I would add to my main.c:

Code: Select all

#pragma output CRT_ORG_CODE = 32768   // away from address 0 means no restarts
#pragma output CRT_ENABLE_CLOSE = 0  // don't close files on exit as there aren't any
#pragma output CLIB_EXIT_STACK_SIZE = 0  // no functions can be registered with atexit()
#pragma output CLIB_MALLOC_HEAP_SIZE = 0  // no heap
#pragma output CLIB_STDIO_HEAP_SIZE = 0   // no stdio heap means no files can be created
If I compile a test program and look at the code generated, the crt assembles to 17 bytes:

Code: Select all

__Start:

   ld (__sp),sp

__Restart:

   ld sp,0x0000
   call _main

__Exit:

   push hl
   pop hl
   ld sp,(__sp)

   ret
The crt is especially written for embedded targets so unless you are adding streams, you probably don't have to change it but there's no harm in understanding what is going on :)


If you are looking for a way to add stdin, stdout, stderr let me know. The easiest target to follow would be cp/m for that but it's best if I just give an example implementation for the sort of i/o device you are using.

I'm beginning to think z88dk is too steep a hill to climb for the benefit in my limited case. Clearly it's an excellent piece of work but it may just be too heavy duty for me in this instance.
It will look a little daunting at first but I promise it's fairly straightforward. There's nothing wrong with using sdcc but if you use it through z88dk, sdcc's output is improbed, sometimes substantially :)
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:Actually, one more question. Would it be possible to have a CRT that did absolutely nothing other than jump to or call main?
Yes when I'm back online later tonight I'll give you a boiler plate crt which you can modify with your own startup code.

The embedded crt does give you a pretty small crt as noted in the last post. With regard to that one you may still want to change the location of the stack (#pragma output REGISTER_SP = ???) if the top of memory is occupied. If you need a heap, change the malloc heap size to something larger than 14 bytes and one will be created in the BSS section. If you set that to -1 (the default), the heap will automatically occupy the space between the end of BSS and the bottom of the stack (the default assumption is the stack size is 512 bytes). However if the computed heap size turns out to be negative, the program will exit without any indication. Memstreams are fairly new but in case you need to create them, you must have some bytes allocated to the stdio heap and the malloc heap; the former is used to create FILE*s.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

again, thanks - tremendous help.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I think the best way to make a custom crt is to make a new one for the embedded target. If you start adding i/o streams, a new target should be started.

A zip file containing all modified files described below:
https://drive.google.com/file/d/0B6XhJJ ... sp=sharing

First I created a crt.m4 named "embedded_crt_99.m4" inside z88dk/libsrc/_DEVELOPMENT/target/embedded/startup
The 99 is just some out of the way number.


embedded_crt_99.m4

Code: Select all

dnl############################################################
dnl##       EMBEDDED_CRT_99.M4 - STANDALONE TARGET           ##
dnl############################################################
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                embedded standalone target                 ;;
;;  generated by target/embedded/startup/embedded_crt_99.m4  ;;
;;                                                           ;;
;;                  flat 64k address space                   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CRT AND CLIB CONFIGURATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include "../crt_defaults.inc"
include "crt_target_defaults.inc"
include "../crt_rules.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SET UP MEMORY MODEL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include "memory_model.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GLOBAL SYMBOLS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include "../clib_constants.inc"
include "clib_target_constants.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INSTANTIATE DRIVERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; The embedded target has no device drivers so it cannot
; instantiate FILEs.

; It can use sprint/sscanf + family and it can create
; memstreams in the default configuration.

include(../../clib_instantiate_begin.m4)
include(../../clib_instantiate_end.m4)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STARTUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SECTION CODE

PUBLIC __Start, __Exit

EXTERN _main

__Start:

   nop

   ; set stack address (optional)
   
   ; IF __register_sp != -1
   ;
   ;    ld sp,__register_sp
   ;
   ; ENDIF

   ; initialize data section

   include "../clib_init_data.inc"

   ; initialize bss section

   include "../clib_init_bss.inc"

SECTION code_crt_init          ; user and library initialization
SECTION code_crt_main

   ; call user program
   
   call _main                  ; hl = return status

   ; run exit stack

   IF __clib_exit_stack_size > 0
   
      EXTERN asm_exit
      jp asm_exit              ; exit function jumps to __Exit
   
   ENDIF

__Exit:

   ; hl = return status

   push hl

SECTION code_crt_exit          ; user and library cleanup
SECTION code_crt_return

   ; close files
   
   include "../clib_close.inc"

   pop hl                      ; hl = return status

   jr ASMPC                    ; infinite loop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; RUNTIME VARS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SECTION BSS_UNINITIALIZED

; place any uninitialized data here (eg saved stack pointer)
; bss and data section initialization will not touch it

include "../clib_variables.inc"
include "clib_target_variables.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CLIB STUBS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include "../clib_stubs.inc"
Most of the pragmas will still be active and you will still be able to automatically create a heap, eg, if you add the necessary pragma to your source code.

The crt code begins at "__Start" above. I've placed a "nop" there so that I can control the name of the output file. Without it there is no code generated until after "SECTION code_crt_main" so the first non-empty section will be "code_crt_main" which means the name of the output file would be "test_code_crt_main.bin". With the nop there the first non-empty section is "CODE" and the output name will be "test_CODE.bin". If you add something underneath the CODE section you can get rid of the nop. A better solution would be to put a short library function in there that is always used.

Next you can insert optional code to initialize the stack pointer. As noted in the comments, if __register_sp is -1, the user is indicating that the stack pointer should not be changed. You're the user so you can decide if you want to pay attention to the __register_sp pragma.

Initialization of the data and bss sections follow. In the RAM model, neither is done and this inserts no code.

"SECTION code_crt_init" is a point for the user or library to insert initialization code. If a heap is present, the c library will insert heap initialization code here.

Next main is called. You can launch main however you want but the crt code shown will be consistent with how C programs are supposed to behave.

After main returns, exit() is called if exit functions can be registered (__clib_exit_stack_size > 0).

The label "_Exit" is the point that exit() jumps to after calling the exit functions. It should be present even if __clib_exit_stack_size = 0.

At this point HL holds the return value. You can choose to save it or ignore it. The code saves it.

"SECTION code_crt_exit" is a point for the user or library to insert exit code. Destructors, eg.

There are no files so the close file include does nothing.

Then you have to decide what to do with a program exit. The code here just loops forever.


Second I expanded the embedded_crt_99.m4 macro into an asm file "embedded_crt_99.asm"

This is done by running m4 on it:

m4 embedded_crt_99.m4 > embedded_crt_99.asm

If your using something other than windows you probably have this already. Otherwise it's easy to install: http://www.z88dk.org/wiki/doku.php?id=temp:front#m4

If you don't want to install m4 you can copy the embedded_crt_99.asm file I included in the zip. The asm file contains the crt used in the compile so if you want to edit the crt embedded_crt_99.asm is what needs to be changed.


Third, one directory up in z88dk/libsrc/_DEVELOPMENT/target/embedded I edited "embedded_crt.asm" to add a startup=99 option and to make startup=99 the default.

You can copy the entire file from the zip but these are the relevant edits:

Code: Select all

IFNDEF startup

   ; startup undefined so select a default
   
   defc startup = 99

ENDIF
This makes startup=99 the default. When you compile with zcc +embedded you no longer have to add "-startup=99" to get your crt selected.

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; custom crt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

IF startup = 99

   ; custom crt
   
   IFNDEF __CRTDEF
   
      defc __CRTDEF = 99

   ENDIF
   
   IFNDEF __MMAP
   
      defc __MMAP = 0

   ENDIF
   
   INCLUDE "startup/embedded_crt_99.asm"

ENDIF
At the end of the file this was added to define what happens when "-startup=99" appears on the compile line. We load up "embedded_crt_99.asm", we select the normal memory map (__MMAP=0) defining CODE/DATA/BSS sections, and we choose new crt options indicated by __CRTDEF=99.


Fourth, new crt options are defined in z88dk/libsrc/_DEVELOPMENT/target/embedded/crt_target_defaults.inc under __CRTDEF=99


Again you can copy the relevant file from the zip.

The change adds this to the file:

Code: Select all

IF __CRTDEF = 99

   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;; custom crt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

   defc TAR__crt_org_code              = 32768   ;; code origin is 32768
   defc TAR__crt_org_data              = 0   ;; data section appends to code
   defc TAR__crt_org_bss               = 0   ;; bss section appends to data

   defc TAR__crt_model                 = 0  ;; ram model
   
   defc TAR__register_sp               = 0   ;; initial stack pointer set to 0 (this is ignored in the crt written)
   defc TAR__crt_stack_size            = 512   ;; stack size is 512 bytes (only used if the heap is automatically initialized)
      
   defc TAR__crt_initialize_bss        = 0  ;; do not initialize bss section to zero (it's zero already in the binary generated for the ram model)
   
   defc TAR__crt_enable_commandline    = 0   ;; no command line needed (ignored in the crt written)
   defc TAR__crt_enable_restart        = 0   ;; not restarting the program on exit (ignored in the crt written)
   defc TAR__crt_enable_close          = 0   ;; do not close files on exit
   
   defc TAR__crt_enable_rst            = 0   ;; no z80 restarts implemented by user (ignored in crt written)
   defc TAR__crt_enable_nmi            = 0  ;; nmi not implemented by user (ignored in crt written)
   
   ; clib defaults
   
   defc TAR__clib_exit_stack_size      = 0
   defc TAR__clib_quickexit_stack_size = 0
   
   defc TAR__clib_malloc_heap_size     = 0
   defc TAR__clib_stdio_heap_size      = 0
   
   defc TAR__clib_balloc_table_size    = 0
   
   defc TAR__clib_fopen_max            = 0
   defc TAR__clib_open_max             = 0

ENDIF
When your crt is selected, these will be the crt options active. You can still override them with pragmas in the C source.



After these changes are made, a compile like this:

zcc +embedded -vn -SO3 -clib=sdcc_iy --max-allocs-per-node200000 test.c -o test

will choose -startup=99 and your crt by default. You can still access the built-in crts by specifying a startup value of 0, 1, or 2.


Let me know if things work out.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

All right! The .m4 is certainly easier to follow. I'll get to try it this weekend.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

I went through the mechanics of downloading the new nightly build and adding your changes and compiled a simple test program. I see the lst for my module and the simple crt, and the code.bin file looks right.

Thanks again and i'll give it a good go tomorrow.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Glad to hear it worked.

Another option you might need is "-m" which will create a map file listing values of all labels first in alphabetical order then in address order (there is a top half and a bottom half).

If you need to sprintf floats using %aefg, they have to be enabled. They are disabled by default so a non-float printf core is available for compiles. You can enable that or otherwise customize the library by editing the clib_cfg.asm file in target/embedded. There is some description of that in http://www.z88dk.org/wiki/doku.php?id=t ... figuration . Documentation for the meaning of the customizations is in the clib_cfg.asm file itself.

I've started documentation on creating a new target and writing drivers but it may take a couple of days to complete: http://www.z88dk.org/wiki/doku.php?id=t ... g_a_target
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

woohoo, works a treat! The simpler crt let me find my way through the .bin and i have it here happily blinking and sending serial to the pc at the same time - this is like walking and chewing gum.

I'll consolidate where I am and wait eagerly for your target/driver documentation.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

Ok. This probably warrants its own thread but I'm following along with the new target instructions. I have the .cfg and the directory structure done and i've changed all the "temp" references to my target name "zmc". I'm at the stage now where it's looking for zmc.lib and not finding it. I've looked around for anything like embedded.lib that i could copy but no sign of any .lib files. Eagerly awaiting next steps....

(in case the .lib referrence is caused by some error of mine, the failing command is below)

z80asm -a -m -Mo -oa.bin -Lh:\apps\z88dk\Lib\Config\\..\..\/libsrc/_DEVELOPMENT/lib/sdcc_iy -izmc -D__SDCC -D__SDCC_IY -Ih:\apps\z88dk\Lib\Config\\..\..\/libsrc/_DEVELOPMENT/target/zmc --list C:\Users\Bill\AppData\Local\Temp\s3suk_1.opt C:\Users\Bill\AppData\Local\Temp\s3suk_.o

Error: cannot read file 'zmc.lib'
1 errors occurred during assembly
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:Ok. This probably warrants its own thread but I'm following along with the new target instructions. I have the .cfg and the directory structure done and i've changed all the "temp" references to my target name "zmc". I'm at the stage now where it's looking for zmc.lib and not finding it. I've looked around for anything like embedded.lib that i could copy but no sign of any .lib files. Eagerly awaiting next steps....
The bit for building the library is not there yet but it's easy to add.

In z88dk/libsrc/_DEVELOPMENT, Winmake.bat and Makefile are responsible for building the z80 libraries. You just have to add "zmc" to the build lists.

In Winmake.bat (in Windows you'll have to edit it by right clicking and choosing "send to" + your text editor) change this line:

Code: Select all

set alltargets= embedded cpm m zx
to

Code: Select all

set alltargets= embedded cpm m zmc zx
This alltargets variable has to have a space before the list of targets and after. Putting zmc in there in alphabetical order puts it in the middle and this also prevents accidentally erasing those spaces.

In the Makefile change this line:

Code: Select all

TARGET ?= embedded zx m cpm
to

Code: Select all

TARGET ?= embedded zmc zx m cpm
Then in a console from directory z88dk/libsrc/_DEVELOPMENT you can build the zmc libraries by executing this:

Winmake zmc (windows)
make TARGET=zmc (non-windows)

There shouldn't be any errros. Three libraries are built and should appear as "zmc.lib" in z88dk/libsrc/_DEVELOPMENT/lib/sccz80, lib/sdcc_ix, lib/sdcc_iy.


The other thing that has to be done is to make the crt. This will be the same as in already discussed above and after that is done there should be a functioning target. The next steps would be adding any target-specific code and writing device drivers.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

I have a functioning Target! I tried it with the default CRT and it worked but i copied the embedded_CRT_99 to zmc_CRT_99 and fiddled target_defaults.inc and zmc_crt.asm to make this the default and i'm now getting the simpler crt.

I did have to remove the @sound line from the three .lst files - as you note it needs some defines which i don't have.

So to use printf I need a driver?
Last edited by bill2009 on Sun Jan 10, 2016 6:58 pm, edited 1 time in total.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I was just working on the wiki now and was going to let you know that sound has to be excluded from the library if some defines are missing, otherwise the library will fail to build. So I will be changing the download zip to exclude sound from the example target.
bill2009 wrote:I have a functioning Target! I tried it with the default CRT and it worked but i copied the embedded_CRT_99 to zmc_CRT_99 and fiddled target_defaults.inc and zmc_crt.asm to make this the default and i'm now getting the simpler crt.

So to use printf I need a driver?
Nice! You can assign CRT numbers as you like -- there is some method behind the crt number assignments for the zx and cpm targets that are linked to the stdin/stdout drivers which you can choose to adopt or not.

But yes to get printf to work you'll need to write a driver. I'll skip the crt discussion in the wiki for now and jump to the driver so maybe it can have something substantive there today. What's your output device like and do you have an input one as well? For output is it a serial device (like a physical terminal or simply rs232) or is it bitmapped? If you have an input device you can think about console functionality otherwise it can be a simpler output driver.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

The console I/O is bit-bang serial I have routines in ROM that do the tricky bits. I just need to call them with the character in A and they return the same way. for example the simplest putchar is shown below. I did it in assembly because I need to get the char from L to A before calling

void putchar(unsigned char) __z88dk_fastcall;
__asm
_putchar::
ld a,l //for z88dk declared as fastcall
call 0x0FE9
ret
__endasm;
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Device drivers are going to take a lot of explaining so it may take me several days :-/

Instead I will supply a driver for you and explain some of the parts.

A zip file containing a serial output driver can be downloaded here:
https://drive.google.com/open?id=0B6XhJ ... lpETmwyajg

The zip file contains a "driver" directory. That's the same driver directory as your target's : z88dk/libsrc/_DEVELOPMENT/target/zmc/driver

You can copy the files in there.

zmc/driver/driver.lst lists all the device driver related source files
zmc/driver/character contains character oriented device drivers

Inside this last directory are files associated with the new driver called "zmc_00_output_bbserial". I'm really bad at names but the "00_output" part is supposed to help distinguish what part of the library the code is derived from. "bbserial" stands for bit-bang serial.

zmc_00_output_bbserial.lst lists all the source files making up the driver
zmc_00_output_bbserial.m4 is an m4 macro that will instantiate the driver in your crt. Instantiation means it will statically declare the data structures required in ram
zmc_00_output_bbserial.asm is the actual driver.

If you look at the driver code there are a lot of comments and then this:

Code: Select all

SECTION code_driver
SECTION code_driver_character_output

PUBLIC zmc_00_output_bbserial

EXTERN OCHAR_MSG_PUTC
EXTERN zmc_00_output_bbserial_ochar_msg_putc, character_00_output

zmc_00_output_bbserial:

   cp OCHAR_MSG_PUTC
   jp z, zmc_00_output_bbserial_ochar_msg_putc

   jp character_00_output      ; forward to library
This driver inherits from "character_00_output" which you can find in the z88dk/libsrc/_DEVELOPMENT/drivers hierarchy if you care to look. What the driver is doing is intercepting one message "OCHAR_MSG_PUTC" which is requesting that you output a char. All other messages are forwarded to the base class.

The "zmc_00_output_bbserial_ochar_msg_putc" function is your putchar code and it is implemented in the subdirectory "zmc_00_output_bbserial".

If you go in there you will see how this "putchar" is implemented:

Code: Select all

SECTION code_driver
SECTION code_driver_character_output

PUBLIC zmc_00_output_bbserial_ochar_msg_putc

zmc_00_output_bbserial_ochar_msg_putc:

   ;   enter   :  c = char
   ;   exit    : carry set if error
   ;   can use : af, bc, de, hl, af'

   ld a,c
   
   call 0x0fe9
   
   or a
   ret
Pay attention to the allowed register use. If the code at 0x0fe9 alters any other registers (including the EXX set!) you will have to push&pop them. Carry must be reset to indicate succes before return. If there is an error you can generate an errno by exiting via one of the error functions that sets carry flag in z88dk/libsrc/_DEVELOPMENT/error . If the carry flag is set, HL=0 to indicate error and -1 for EOF (closed serial port I guess?). Returning with the carry flag set will place the driver in an error state that won't accept further output unless clearerr() is run.

This driver is an ascii stream that does CR/LF conversion. On the C side "\n" will be turned into "\r\n" on the output stream.

Turning that off is under program control with "ioctl(1, IOCTL_OCHAR_CRLF, 0);" -- the last '0' is actually on=1 or off=0. But it can also be turned off by default in the macro (I elected to turn it on).

You can also make a binary stream so that no text CR./LF is considered. If you back up to the top:

Code: Select all

zmc_00_output_bbserial:

   cp OCHAR_MSG_PUTC
   jp z, zmc_00_output_bbserial_ochar_msg_putc

   jp character_00_output      ; forward to library
You can intercept the message "OCHAR_MSG_PUTC_BIN" instead of "OCHAR_MSG_PUTC" and this will deactivate the text stream stuff.



You don't have to modify anything I've done. The next thing to do is add the driver.lst file to your libraries.

In target/zmc/library, add "@target/zmc/driver/driver.lst" to each of the list files.


Next you need to modify your crt to instantiate the output driver on stdout.


Edit the m4 crt file "target/zmc/startup/zmc_crt_0.m4" (or whatever you have called it)

Where it used to say this:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INSTANTIATE DRIVERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include(../../clib_instantiate_begin.m4)
include(../../clib_instantiate_end.m4)
replace with this:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INSTANTIATE DRIVERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dnl
dnl############################################################
dnl## LIST OF AVAILABLE DRIVERS WITH STATIC INSTANTIATORS #####
dnl############################################################
dnl
dnl
dnl## output streams
dnl
dnl#include(../driver/character/zmc_00_output_bbserial.m4)
dnl
dnl## file dup
dnl
dnl#include(../../m4_file_dup.m4)dnl
dnl
dnl## empty fd slot
dnl
dnl#include(../../m4_file_absent.m4)dnl
dnl
dnl############################################################
dnl## INSTANTIATE DRIVERS #####################################
dnl############################################################
dnl

include(../../clib_instantiate_begin.m4)

;; fd=0, stdin

include(../../m4_file_absent.m4)
m4_file_absent

;; fd=1, stdout

include(../driver/character/zmc_00_output_bbserial.m4)
m4_zmc_00_output_bbserial(_stdout, 0x0010)

;; fd=2, stderr

include(../../m4_file_dup.m4)
m4_file_dup(_stderr, 0x80, __i_fcntl_fdstruct_1)

include(../../clib_instantiate_end.m4)
The paths are relative to the startup directory where m4 is run from.

The comments at the beginning list the available drivers. Then the drivers are instantiated between the "begin" and "end" macros. The file descriptors assigned will begin at 0 and count upward which is why the first driver instantiated is an absent one (to skip fd=0=stdin) and then second is the output driver just added. You don't have to add stderr but I put it in there as a dup of stdout.


With the m4 crt edited, create the crt by running m4 on it:

m4 zmc_crt_0.m4 > zmc_crt_0.asm



Now go up to z88dk/libsrc/_DEVELOPMENT and rebuild your library with "Winmake zmc" or "make TARGET=zmc".


One that's done, printf should be working. Let me know if you have any problems with this!
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

alvin wrote:Device drivers are going to take a lot of explaining
Holy cr*p I guess they will! I really appreciate all the work you're going to here. I'm sure the overall explanation will be valuable for others as well.

**I got a "You need permission" error on the google drive access.** Can you allow me access?

I've checked and my low-level code saves any regs that it uses so I should be ok so far.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:**I got a "You need permission" error on the google drive access.** Can you allow me access?
I created the link again:

https://drive.google.com/file/d/0B6XhJJ ... sp=sharing

Anyone who has the link should be able to download or at least that's what it tells me :)

None of us like to write documentation but if you don't people are unaware of what's in there. z88dk is plagued by a lack of documentation just about everywhere. It's a but of a pain but it's something that has to be done too.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

I think I followed the instructions and everything seemed ok until I tried to compile something with printf()
C:\Users\Bill\Desktop\olduinoZ\z88dk>zcc +zmc testprintf.c
1 file(s) copied.
1 file(s) copied.
1 file(s) copied.
Error at file 'stdio/z80/asm_vprintf_unlocked.asm' line 48: symbol '_stdout' not defined
1 errors occurred during assembly
Key to filenames:
C:\Users\Bill\AppData\Local\Temp\s4o4o_.o = testprintf.c
Error at file 'stdio/z80/asm_vprintf_unlocked.asm' line 48: symbol '_stdout' not defined
The line that gives the error is, not surprisingly:
ld ix,(_stdout)
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

bill2009 wrote:Error at file 'stdio/z80/asm_vprintf_unlocked.asm' line 48: symbol '_stdout' not defined
The line that gives the error is, not surprisingly:
ld ix,(_stdout)
Make sure you are using a crt that has had stdout added. If you're using the 99 one you mentioned you have to do the device instantiation into the 99 m4 one and then create the crt with "m4 zmc_crt_99.m4 > zmc_crt_99.asm"

In the m4 file this is the part that declares stdout:

Code: Select all

;; fd=1, stdout

include(../driver/character/zmc_00_output_bbserial.m4)
m4_zmc_00_output_bbserial(_stdout, 0x0010)
Maybe check if that "_stdout" is not misspelled.

The m4_zmc_00_output_bbserial macro is defined in zmc/driver/character:

Code: Select all

.....
   ; FILE *
      
   PUBLIC $1
      
   $1:  defw __i_stdio_file_`'__I_STDIO_NUM_FILE + 2
.....
The "$1" is the first argument to the macro which will be "_stdout" -- it will be made public (global) and it appears as a label on the next line.



But I think maybe you are compiling use a crt.asm that hasn't had the driver instantiated. Maybe forgetting to run m4 to make it again or creating a crt.asm that isn't used in your compile (crt_0 instead of crt_99)?
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

haha - i had copied your command line and re-m4'd the 0 crt. trying again.
bill2009
Member
Posts: 28
Joined: Wed Jan 06, 2016 2:16 pm

Post by bill2009 »

And that did it! Thanks again
[img]http://olduino.files.wordpress.com/2016 ... printf.jpg[\img]
Last edited by bill2009 on Mon Jan 11, 2016 8:55 pm, edited 1 time in total.
Post Reply