This post will show how to make a fully custom build using the +z80 target in the newlib. The other course is to generate a new target in newlib that would be tailored to your hw.
1.
Create a "zpragma.inc" file to hold these pragmas. The pragmas get written to a file "zcc_opt.def" during compilation and these are defines read by the library-supplied crt at assemble time. Some of these defines will tell the library crt to use your crt instead.
zpragma.inc
Code: Select all
// use my crt in file "crt.asm.m4"
#pragma output startup = -1
// use my crt configuration in file "crt_cfg.inc"
#pragma output __CRTCFG = -1
// use my memory map in file "mmap.inc"
#pragma output __MMAP = -1 ;; I suggest keeping the library's by setting to 0
// no heaps
#pragma output CLIB_MALLOC_HEAP_SIZE = 0
#pragma output CLIB_STDIO_HEAP_SIZE = 0
This pragma file will be provided on the zcc compile line. The main crt file "z80_crt.asm.m4" will read it:
https://github.com/z88dk/z88dk/blob/mas ... asm.m4#L21
Because "__STARTUP=-1" (this comes from "startup" for legacy reasons), your crt file will replace the one supplied by the library.
Also there are a couple of constants defining a default memory map (__MMAP) and a default crt configuration (__CRTCFG). If you use the pragmas above, these will be set to -1 which indicate that you will be providing these files as well. You may actually want to use the default __MMAP=2 at least.
At this point your crt is all there is. It is up to you what is in the crt and whether you pay attention to CRTCFG or MMAP. But what you do have to do at minimum is set up a memory map.
You can use the library's crt as guide:
https://github.com/z88dk/z88dk/blob/mas ... t_0.asm.m4
I would start your crt almost the same way:
Code: Select all
include(`z88dk.m4') ;;; include some m4 macro definitions in z88dk/src/m4 which does nothing if you don't use them
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; z80 standalone target ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GLOBAL SYMBOLS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
include "config_z80_public.inc" ;;; machine generated defines from target configuration that are exported to the program
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CRT AND CLIB CONFIGURATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
include "../crt_defaults.inc" ;; z88dk crt configuration defaults
include "crt_config.inc" ;; +z80 crt configuration defaults override z88dk ones
include(`../crt_rules.inc') ;; rules determine final value of crt configuration
include(`z80_rules.inc') ;; more crt configuration for the +z80 target only
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SET UP MEMORY MAP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
include(`crt_memory_map.inc') ;; define the memory map
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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.
;;; cost of an empty driver instantiation section is about 6 bytes. It will allow
;;; the sscanf and sprintf families to run as these generate a fake FILE structure
;;; and this fake FILE structure is tested for validity before running. The validity
;;; test requires those 6 bytes. The validity test is a library build time option that
;;; could be disabled if you rebuild the z80 library and then this section could
;;; be eliminated completely. However, should you ever want to add any streams
;;; this is where they go.
include(`../clib_instantiate_begin.m4')
ifelse(eval(M4__CRT_INCLUDE_DRIVER_INSTANTIATION == 0), 1,,
`
include(`crt_driver_instantiation.asm.m4')
')
include(`../clib_instantiate_end.m4')
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STARTUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SECTION CODE
PUBLIC __Start, __Exit
EXTERN _main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PAGE ZERO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IF (ASMPC = 0) && (__crt_org_code = 0)
;;;;;include "../crt_page_zero_z80.inc"
;;; include or place your page zero code here
;;; fall through or jump to __Start
ENDIF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CRT INIT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
__Start:
.....
;;;; select what you want from the library crt or rewrite yourself. The library crt
;;;; ensures things like the exit stack can function and offers options for what is
;;;; done on program exit.
The home directory for the includes is z88dk/libsrc/_DEVELOPMENT/target/z80/
https://github.com/z88dk/z88dk/tree/mas ... target/z80
You can see what the crt is doing and how by following the includes.
Your crt is a .asm.m4 file which means it is processed by m4 first before being assembled. You can ignore m4 and just use asm if you want. I left some comments in the above detailing what the purpose of some parts is.
The "CRT AND CLIB CONFIGURATION" section determines the final value of pragmas. Some of these are used by appmake to form the output rom or binary. Having these here will solve the "not compiled by z88dk" problem. Appmake looks at the org address in these pragmas for information while generating the output. It is up to your crt to implement the options defined by these pragmas or not (see
https://www.z88dk.org/wiki/doku.php?id= ... figuration ). In the zpragma.inc file, specifying "__CRTCFG = -1" means you will be supplying a crt configuration file which this bit will use to generate the final crt configuration values. You'll want to change or copy one of the configs supplied by the library:
https://github.com/z88dk/z88dk/blob/mas ... g.inc#L117
The memory map is the other important item. It tells the linker where to put code and variables. I'd strongly suggest using MMAP=0 so the library supplies the memory map:
https://github.com/z88dk/z88dk/blob/mas ... ap.inc#L22
This includes the default library memory map:
https://github.com/z88dk/z88dk/blob/mas ... el_z80.inc
There are a lot of sections defined in there for modules in the library so that a custom memory map would have fine control over where the library code is placed. However this memory map essentially defines three regions: CRT+CODE, DATA, BSS. code or data are assigned to sections by name which are just containers that hold bytes. This memory map sequences those containers in memory. sections without an org are appended to the section above them. A section with org is output as an independent binary. So you can see in this memory map there are only three sections that might have an org: CODE,DATA,BSS. The output from the linker will be up to three binaries: *_CODE.bin, *_DATA.bin and *_BSS.bin which will hold the contents of these master sections. Then appmake will use this and information in the map file to make the final output in the form requested.
The other feature of this memory map is how it sequences the crt itself:
section code_crt_init
section code_crt_main
section code_crt_exit
section code_crt_return
section code_crt_common
If you look at the library's crt you'll see these sections mentioned. "section code_crt_init" is a point where outside code (library or user) can insert initialization code before main is called. "section code_crt_exit" is a point where outside code can insert exit code run before files are closed.
Other sections allow placement of an im2 vector table inside page zero or elsewhere, and so on.
You can create your own memory map but I think it's much easier to just use this one unless you have some radically different needs.
One last thing. You can see how the library's bss and data section initialization is performed here:
https://github.com/z88dk/z88dk/blob/mas ... it_bss.inc
https://github.com/z88dk/z88dk/blob/mas ... t_data.inc
They grab end points defined in the memory map to know how big these sections are and where they are in ram. For rom compiles, it is expected that either a plain copy or a compressed copy of the data section is appended to the code stored in rom so that the data section contents can be copied from there to ram. The code stored in rom is the *_CODE.bin file. So you'd append either *_DATA.bin or a compressed version of *_DATA.bin to that file to form what goes in rom. This is what -create-app will do for you automatically.
******
Anyway, that should be enough to get going. But I would encourage you to look at the +z80 target as-is because it has a great deal of flexibility built in to the supplied crt that can likely do what you want already.