Easy banking with SDCC / Newlib

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Easy banking with SDCC / Newlib

Post by jorgegv »

Hi guys,

I have been trying the banking support with ZX target, with the following source files:

main.c:

Code: Select all

// zcc +zx -vn --list -s -m --c-code-in-asm -lndos main.c bank6.c -create-app -o banked.bin

#include <stdio.h>

extern int banked_function( void ) __banked;

void main( void ) {
    printf( "Hello world from main bank!\n" );
    printf( "** Value from bank 6: 0x%04x\n", banked_function() );
    while ( 1 ) ;
}
bank6.c:

Code: Select all

#pragma bank 6

int banked_function( void ) __banked {
    return 0x1972;
}
When compiling with the above command line (which selects ZCCZ80 and Classic clib), everything works out of the box, which is great.

But when I try any of the following CFLAGS, it does not work:

Code: Select all

# sccz80, classic - this works
#CFLAGS = --list -s -m --c-code-in-asm -lndos -create-app -o banked.bin

# sccz80, newlib
#CFLAGS = --list -s -m -clib=new --c-code-in-asm -create-app -o banked.bin
# error: Warning: Non-empty UNASSIGNED section ignored -
#  this indicates that some code/data is not part of the memory map

# sdcc, classic
#CFLAGS = --list -s -m -compiler=sdcc --c-code-in-asm -lndos -create-app -o banked.bin
# error: Error: Section  overlaps section CODE by 3751 bytes
# zx: Aborting... one or more binaries overlap
# Building application code failed

# sdcc, newlib - this is the one I'm most interested in
#CFLAGS = --list -s -m -compiler=sdcc -clib=new --c-code-in-asm -create-app -o banked.bin
# (it does not parse stdio.h and gives lots of errors)
So my question: what's the status of no-fuss banking support with SDCC and/or newlib? I'd prefer to use SDCC+newlib for optimization reasons, but the fact that banking is sooooo easy with sccz80 and classic may compel me to migrate to that combination if the other is not available.

Thanks in advance :-)
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

Banked support has only been added to classic, for me it's a much more interesting idea when there's potentially lots of targets to support.

It should however, work with both sccz80 and sdcc in classic: I've just tried examples/banked by changing the CFLAGS_zx_bank line so that it has this:

Code: Select all

CFLAGS_zx_bank = -lndos -compiler=sdcc --legacy-banking
And the banked example appears to work.

Having said that, there's really not much that's needed to add support for it, the classic implementation is here: https://github.com/z88dk/z88dk/blob/mas ... d_call.asm - if you get the names of the sections correct you should just be able to add it to your project: If it does work, let me know and I can add it into the newlib library.

Onto your problems:

Code: Select all

# error: Error: Section  overlaps section CODE by 3751 bytes
# zx: Aborting... one or more binaries overlap
# Building application code failed
I see this a lot when I forget to clear out the residual files from previous compilations - the a_XXXX.bin files - maybe that's happened to you?

Code: Select all

# sdcc, newlib - this is the one I'm most interested in
#CFLAGS = --list -s -m -compiler=sdcc -clib=new --c-code-in-asm -create-app -o banked.bin
# (it does not parse stdio.h and gives lots of errors)
Your options (-compiler=sdcc -clib=new) are using SDCC with the newlib SCCZ80 library - just use -clib=sdcc_ix or -clib=sdcc_iy to switch to using sdcc.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

dom wrote: Mon Jan 10, 2022 7:54 pm Banked support has only been added to classic, for me it's a much more interesting idea when there's potentially lots of targets to support.

It should however, work with both sccz80 and sdcc in classic: I've just tried examples/banked by changing the CFLAGS_zx_bank line so that it has this:

Code: Select all

CFLAGS_zx_bank = -lndos -compiler=sdcc --legacy-banking
And the banked example appears to work.
It works for me also without the --legacy-banking switch, what's it supposed to do?
dom wrote: Mon Jan 10, 2022 7:54 pm Having said that, there's really not much that's needed to add support for it, the classic implementation is here: https://github.com/z88dk/z88dk/blob/mas ... d_call.asm - if you get the names of the sections correct you should just be able to add it to your project: If it does work, let me know and I can add it into the newlib library.
I'll keep you posted. I have already studied that implementation and know how it works, so I'll try to do it.
dom wrote: Mon Jan 10, 2022 7:54 pm
Onto your problems:

Code: Select all

# error: Error: Section  overlaps section CODE by 3751 bytes
# zx: Aborting... one or more binaries overlap
# Building application code failed
I see this a lot when I forget to clear out the residual files from previous compilations - the a_XXXX.bin files - maybe that's happened to you?
This was it. "make clean" and then compiled fine.
dom wrote: Mon Jan 10, 2022 7:54 pm

Code: Select all

# sdcc, newlib - this is the one I'm most interested in
#CFLAGS = --list -s -m -compiler=sdcc -clib=new --c-code-in-asm -create-app -o banked.bin
# (it does not parse stdio.h and gives lots of errors)
Your options (-compiler=sdcc -clib=new) are using SDCC with the newlib SCCZ80 library - just use -clib=sdcc_ix or -clib=sdcc_iy to switch to using sdcc.
Fine, thanks for the reminder, and also thanks for all your quick help.

Cheers
J.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

jorgegv wrote: Mon Jan 10, 2022 9:28 pmIt works for me also without the --legacy-banking switch, what's it supposed to do?
SDCC changed the default banking mechanism a while bank so that the function to call is specified in ehl rather than in memory. --legacy-banking switches it back: maybe you've got an older version of sdcc.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

dom wrote: Tue Jan 11, 2022 6:59 pm
jorgegv wrote: Mon Jan 10, 2022 9:28 pmIt works for me also without the --legacy-banking switch, what's it supposed to do?
SDCC changed the default banking mechanism a while bank so that the function to call is specified in ehl rather than in memory. --legacy-banking switches it back: maybe you've got an older version of sdcc.
Mmmm I have examined the ASM output and indeed it is generating the old "call banked_call" + "defq _my_banked_function" dance. But I usually use some Z88DK version near the nightly (last commit in my Z88DK installation is from october 13th 2021)...

Has this convention changed after that date or before?
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

I get the new calling convention in for example: zcc +zx -a bank1.c -compiler=sdcc -a - the call without parameters uses it. This is using r12555
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Strange. I just cloned the repo now and rebuilt Z88DK, and I still get the old convention by default. This is my compile line for SDCC + classic (sources are the ones above):

Code: Select all

zcc +zx -vn --list -s -m -compiler=sdcc --c-code-in-asm -lndos -create-app -o banked.bin main.c bank6.c
And examining main.c.lis, I see this:

Code: Select all

   445                          ;main.c:7: void main( void ) {
   446                          ;       ---------------------------------
   447                          ; Function main
   448                          ; ---------------------------------
   449                          _main:
   450                          ;main.c:8: printf( "Hello world from main bank!\n" );
   451   000000 210000                  ld      hl,___str_1
   452   000003 e5                      push    hl
   453   000004 cd0000                  call    _puts
   454   000007 f1                      pop     af
   455                          ;main.c:9: printf( "** Value from bank 6: 0x%04x\n", banked_function() );
   456   000008 cd0000                  call    banked_call
   457   00000b 00000000                defq    _banked_function
   458   00000f 010000                  ld      bc,___str_2+0
   459   000012 e5                      push    hl
   460   000013 c5                      push    bc
   461   000014 cd0000                  call    _printf
   462   000017 f1                      pop     af
   463   000018 f1                      pop     af
   464                          ;main.c:10: while ( 1 ) ;
   465                          l_main_00102:
   466                          ;main.c:11: }
   467   000019 1800                    jr      l_main_00102
...which I believe is the old banking scheme, right?

Last commit in my cloned repo is from today at about 18:30, so it seems I'm pretty much up to date :-)

Edit: If I add "-a" to my compile line, the generated ASM file also has the old convention:

Code: Select all

; Function main
; ---------------------------------
_main:
;main.c:8: printf( "Hello world from main bank!\n" );
        ld      hl,___str_1
        push    hl
        call    _puts
        pop     af
;main.c:9: printf( "** Value from bank 6: 0x%04x\n", banked_function() );
        call    banked_call
        defq    _banked_function
        ld      bc,___str_2+0
        push    hl
        push    bc
        call    _printf
        pop     af
        pop     af
;main.c:10: while ( 1 ) ;
l_main_00102:
;main.c:11: }
        jr      l_main_00102
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Just a thought: maybe the SDCC version which is downloaded when building Z88DK is not the trunk version? According to this: https://github.com/z88dk/z88dk/issues/1 ... -664559581 the --legacy-banking option was added to SDCC trunk, and the comment date is July 2020...

Maybe you are using a newer SDCC version instead of the Z88DK downloaded one?
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Also a curiosity: when I run z88dk-zsdcc -h, it shows the --legacy-banking option as available...

What's going on here?
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

More info: I have just downloaded stock SDCC (yesterday's nightly), which appears to be r12871, and compiled main.c to asm with:

Code: Select all

sdcc -mz80 --asm=z80asm -S main.c
The generated assembler output is:

Code: Select all

._main
;main.c:9: printf( "Hello world from main bank!\n" );
	ld	hl, ___str_1
	call	_puts
;main.c:10: printf( "** Value from bank 6: 0x%04x\n", banked_function() );
	ld	e, BANK(_banked_function)
	ld	hl, _banked_function
	call	___sdcc_bcall_ehl
	push	de
	ld	hl, ___str_2
	push	hl
	call	_printf
	pop	af
	pop	af
;main.c:11: while ( 1 ) ;

.l_main00102
	jp	l_main00102
...which indeed uses the new EHL convention.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

More info: I have patched the Z88DK Makefile to download SDCC r12871 (from yesterday), rebuilt Z88DK with the new SDCC version (patch applied without issues) and rerun my banking test, and surprise, it is generating code using the old convention, but without using --legacy-banking switch.

So it seems something in Z88DK is forcing SDCC to generate old banking code regardless of the --legacy-banking switch being used or not, but the stock SDCC indeed obeys the switch if used.

Any ideas from the experts?
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

I believe I added in some rewrite rules when it looked like there wasn't going to be a compatibility flag: https://github.com/z88dk/z88dk/blob/mas ... opt.1#L598

It captures most of the cases, but unfortunately I didn't catch the tail call. case, so you end up with some horrible Frankenstein code in certain cases:

Code: Select all

extern int func_bank1() __banked;
extern int func_bank3(int value) __banked;

int main() {
        func_bank3(100);
        return func_bank1();
}

% zcc +test -a -compiler=sdcc eg.c

...
_main:
	ld	hl,0x0064
	push	hl
	call	banked_call
	defq	_func_bank3
	pop	af
	ld	e,b_func_bank1
	ld	hl,_func_bank1
	jp  ___sdcc_bcall_ehl
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Besides not catching the tail call case (easy to fix :-) ), it also looks like you wanted to replace the new banking code with the old one no matter the case: with that rule you always replace the EHL call with the DEFQ one.

Also the target/zx/driver/banking/zx_banked_call.asm implementation seems to be in line with this, since this routine is not prepared for receiving args in EHL, and only works with the DEFQ convention.

So it looks that only legacy banking is supported in Z88DK, right?
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

Yes, I think support for one banked call technique is enough. So with those rules, the flag is no longer needed which is where we started from.

Trampolining for a banked call is never going to be speedy so at the moment I can't see the advantage of the "new way" of doing things, apart from removing the possibility of supporting __z88dk_fastcall __banked functions.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Mmm I have taken a look at the SDCC implementation of the trampolines, and the EHL one seems to be quicker, at the very least because of the reduced number of instructions compared to the Z88DK one.

But indeed one implementation seems enough, yes. Thanks for the support, Dom.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Sorry to bother with this again, but I can't get automatic banking working with SDCC+NEWLIB, even manually adding my own banking function:

- I just copied the zx_banked_call.asm to my directory and included it in my compilation. The section this code goes into is "code_driver"
- I have included a custom mmap.inc to ensure the section "code_driver" is linked into the executable
- I have included a custom pragma file to make use of the custom mmap.inc

But still, when compiling this with sdcc+newlib, I always get the error that some code was not linked. Also the TAP file only shows one code block:

Code: Select all

$ make
zcc +zx -vn -c --list -s -m -compiler=sdcc -clib=sdcc_iy --c-code-in-asm -pragma-include zpragma.inc zx_banked_call.asm
zcc +zx -vn -c --list -s -m -compiler=sdcc -clib=sdcc_iy --c-code-in-asm -pragma-include zpragma.inc main.c
zcc +zx -vn -c --list -s -m -compiler=sdcc -clib=sdcc_iy --c-code-in-asm -pragma-include zpragma.inc bank6.c
zcc +zx -vn --list -s -m -compiler=sdcc -clib=sdcc_iy --c-code-in-asm -pragma-include zpragma.inc zx_banked_call.o main.o bank6.o -create-app -o banked.bin
Warning: Non-empty UNASSIGNED section ignored -
  this indicates that some code/data is not part of the memory map
 $ ../../rage1/tools/tapdump.pl *.tap
Block #0: pos = 0, size = 19, flag = 0 (header), cksum = 59
  [Header] Type: PROGRAM, File Name: "Loader    ", Length: 30, Autostart: 10, Program Length: 30
Block #1: pos = 21, size = 32, flag = 255 (data), cksum = 101
  [Data] Size: 30
Block #2: pos = 55, size = 19, flag = 0 (header), cksum = 136
  [Header] Type: CODE, File Name: "banked.bin", Length: 5591, Start Address: 33156 (0x8184)
Block #3: pos = 76, size = 5593, flag = 255 (data), cksum = 51
  [Data] Size: 5591
If I compile with SDCC+Classic, everything works fine, apparently:

Code: Select all

$ make
zcc +zx -vn -c --list -s -m -compiler=sdcc --c-code-in-asm -lndos zx_banked_call.asm
zcc +zx -vn -c --list -s -m -compiler=sdcc --c-code-in-asm -lndos main.c
zcc +zx -vn -c --list -s -m -compiler=sdcc --c-code-in-asm -lndos bank6.c
zcc +zx -vn --list -s -m -compiler=sdcc --c-code-in-asm -lndos zx_banked_call.o main.o bank6.o -create-app -o banked.bin
$ ../../rage1/tools/tapdump.pl *.tap
Block #0: pos = 0, size = 19, flag = 0 (header), cksum = 59
  [Header] Type: PROGRAM, File Name: "Loader    ", Length: 30, Autostart: 10, Program Length: 30
Block #1: pos = 21, size = 32, flag = 255 (data), cksum = 105
  [Data] Size: 30
Block #2: pos = 55, size = 19, flag = 0 (header), cksum = 66
  [Header] Type: CODE, File Name: "banked.bin", Length: 3970, Start Address: 32768 (0x8000)
Block #3: pos = 76, size = 3972, flag = 255 (data), cksum = 135
  [Data] Size: 3970
Block #4: pos = 4050, size = 6, flag = 255 (data), cksum = 124
  [Data] Size: 4
I believe there is some missing thing with the linker here, but I can't see what it is, any help?

The code for my full tests here: https://github.com/jorgegv/banked-sdcc-newlib-tests

And after that, another question on the same topic, but unrelated to the problem above:

Why the banked_call asm function in Classic uses a separate call stack for banked functions? It is reserved from the main stack, but it is located several bytes below (adjusted with CLIB_BANKING_STACK_SIZE pragma), but I can't see why a separate stack is needed.

Of course there is a need to save the previous bank before switching to the new one (so that banked calls are reentrant), but this can also be achieved by using just the regular stack, so why switch to an alternate one? This stack-switching dance impacts performance of banked calls.

For the record, my (private) banking routine just uses the regular stack and everything works fine.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

The .map file should (with a bit work) be able to tell you what's going on. I suspect there's a section being used that isn't defined in the memory map - I think UNASSIGNED marks the start of it so look at the sections after that address.
Why the banked_call asm function in Classic uses a separate call stack for banked functions? It is reserved from the main stack, but it is located several bytes below (adjusted with CLIB_BANKING_STACK_SIZE pragma), but I can't see why a separate stack is needed.
I think it was to ensure that the banked function could be called from anywhere (even same bank) with the parameters at the regular offset. Without it you end up with two extra words on the stack which ends up displacing the arguments (__z88dk_params_offset mitigates this, but bank local calls would fail).
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: Easy banking with SDCC / Newlib

Post by dom »

dom wrote: Mon Oct 24, 2022 11:01 pm The .map file should (with a bit work) be able to tell you what's going on. I suspect there's a section being used that isn't defined in the memory map - I think UNASSIGNED marks the start of it so look at the sections after that address.
So, it's the code that's in bank6.c that's being pushed into UNASSIGNED - you'll need to copy the banking setup/orgs into your memory map.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: Easy banking with SDCC / Newlib

Post by jorgegv »

Thanks Dom, Will check
Post Reply