Splitting infrequently used code out into banks...

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
Xalior
New member
Posts: 6
Joined: Tue Mar 06, 2018 10:38 am

Splitting infrequently used code out into banks...

Post by Xalior »

To best exhibit the this I've created a reduced case repo at https://github.com/Xalior/z88dk-FakeDesk

Imagine I have a big and complex app. Basically created out of 4 (or more) c files.

They are broken down as main, core, stuff1, and stuff2.

In my github example my "stuff1" is a Markdown viewer, and "stuff2" is an icon editor, or something like that... ;-)

Anyway and these two "modules" reference things in main and core, but are infrequently used and thus I want to bank switch them and the associated static functions out, only to bring them back if and when they are needed, keeping main and core in the "main" memory at all times.

I've had a good google about, but I can't find an obvious way to do this on newlib - how to structure my code and my makefiles leaving me with a manageable way which I can work out which C files end up in which bank, and how I then call those (non static) functions once bank switched back in...

My eventual, dream, goal would be to have the facility to write these banks out to disk using the ESXDOS API (something I've got working just fine else where in ZXDesk, um, I mean FakeDesk, *cough*) and use that to "manage virtual memory" on 48k machines that could not normally bank switch...

Could anyone give me some starter pointers? Ta!

-Dx
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The c compilers compile into the following sections:

code_compiler:
read only code

rodata_compiler:
read only data like "const int a = 5;" or strings like "Hello World"

data_compiler:
initialized ram variables like "int a = 5;"

bss_compiler:
uninitialized ram variables set to zero like "int a;"

In the default memory map these sections are part of the "main binary" that is loaded into banks 5,2,0.

You can change what sections the compiler compiles into on the compile line:

--codesegSECTIONNAME
change the name of "code_compiler"

--constsegSECTIONNAME
change the name of "rodata_compiler"

--datasegSECTIONNAME
change the name of "data_compiler"

--bsssegSECTIONNAME
change the name of "bss_compiler"

z88dk defines sections for banked memory. The 128k spectrum has sections BANK_0 through BANK_7 defined. The zx next has much more defined, among them BANK_0 through BANK_111, which are names for ram in 16k chunks for compatibility with the 128k spectrum. This same ram as another alias in PAGE_0 through PAGE_223 for viewing memory in 8k pages which is the native banking scheme of the zx next.

So if you want some c code in PAGE_30 you'd first compile it to an object file like this:

zcc +zxn -vn -c --codesegPAGE_30 --constsegPAGE_30 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 foo.c bar.c baz.c -o page30

foo.c, bar.c and baz.c will go into PAGE_30 and the output file is "page30.o".

To form the final executable, add this object file to the compile line:

zcc +zxn -vn -clib=sdcc_iy ... page30.o other.c etc.asm -o prog -subtype=sna .... -create-app

Effectively we split the compile into pieces. Anything assigned to another bank is compiled to an object file separately where the --codeseg, etc, bank specifiers can be used on a selection of c files, and then those object files are used in the final compile that puts it all together.

Now the awkward part: only sccz80 supports all those bank modifier options (compile with "-clib=new"). zsdcc only supports --codeseg and --constseg. So zsdcc is unable to change the data_compiler and bss_compiler sections which means it will always place ram variables in the main bank. To get around this, variables destined for a specific bank should be defined in assembler instead. Assembler has the "SECTION" directive which can change the section assignment of anything at any point within an asm file.

This is a partial description of how you can assign code and data to specific memory banks. There is more you can do that makes it as flexible as possible, such as choosing org addresses for code assigned to pages. In the zx next target, an 8k page can be placed in any of eight mmu slots so code in a specific page can be orged pretty much to any address in the z80's 64k address space.


Another thing you can do, and this may be what you want for the 48k target, is you can place code and data in a section you define. You can add these sections to your memory map so that the linker knows about them with a pragma. Suppose you want part of your code on disk in a section called "BOOTCODE". Then you'd add this pragma:

#pragma output CRT_APPEND_MMAP = 1

This will cause z88dk to append the file "mmap.inc" to its default memory map. So create a file "mmap.inc" and add the sections you want:

mmap.inc

Code: Select all

section BOOTCODE
org 0xc000

section DISKCODE
org 0xc000
I've defined two sections to show how it's done. Both are ORGed at 0xc000, following the 128k spectrum banking model and likely how you'd do this for the 48k - ie loading code into the top 16k.

Now you can assign the compiler's code and data to these sections to get what you want there.

What the linker will do with these sections depends on appmake - ie what type of output you are generating.

If an sna, these sections would be assumed to be part of the "mainbank" and they would be merged into banks 5,2,0 which is not what you want. So when you build an sna, you'd instruct appmake to exclude them like this:

zcc +zxn -vn -clib= ... main.c foo.o -o prog -subtype=sna -Cz"--clean --exclude-sections BOOTCODE --exclude-sections DISKCODE" -create-app

Now the output will be the sna and two extra files "prog_BOOTCODE.bin" and "prog_DISKCODE.bin" which is probably what you want.

You can even treat the 128k or zx next differently by arranging in your mmap.inc to make those sections part of the defined memory banks BANK_n or PAGE_n so that an sna would build with all that code in memory instead of generated as a separate binary that must be loaded via esxdos.


Anyway that's a start, feel free to ask more questions.
Xalior
New member
Posts: 6
Joined: Tue Mar 06, 2018 10:38 am

Post by Xalior »

Yowzers! That's a brilliant kick up the bum to my knowledge! Thanks, I understand things a lot more now, I think.

I'll get playing with this and see if I can achieve the desire I want.

Thanks, especially, for the extra points for the 48k bits, because I know my ambitions in that area are specifically quite ambitious.

-Dx

P.S. I also saw your new ticket on github... It looks like I started your mental gears turning on this one, sorry ;-) x
Xalior
New member
Posts: 6
Joined: Tue Mar 06, 2018 10:38 am

Post by Xalior »

Xalior wrote:Yowzers! That's a brilliant kick up the bum to my knowledge! Thanks, I understand things a lot more now, I think.

I'll get playing with this and see if I can achieve my desires & wants.

Thanks, especially, for the extra points for the 48k bits and sccz80, because I know my ambitions in that area are specifically quite ambitious.

-Dx

P.S. I also saw your new ticket on github... It looks like I started your mental gears turning on this one, sorry ;-) x
Xalior
New member
Posts: 6
Joined: Tue Mar 06, 2018 10:38 am

Post by Xalior »

(sorry for double post there, the edit function and this forum and me seem to make a special combination at time ;-) )
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

This problem has been percolating for some time but maybe we can do something about it now.
Post Reply