gbdk-n icon indicating copy to clipboard operation
gbdk-n copied to clipboard

Banked code support

Open joekarl opened this issue 8 years ago • 22 comments

So I've been playing around with this for a bit and banked code seems to be a major problem with gbdk-n currently. And it's not necessarily that the gbdk-n code is bad but there's definitely issues compiling code that results in multi bank roms.

After a bunch of digging I think I've pinned this to the SDCC linker not allowing for an extended address space > 64kb.

Before I go and file a bug over there, do you have any examples of working multi bank code (specifically generating a rom > 64kb)?

joekarl avatar Oct 29 '15 21:10 joekarl

I here looking for the same answers. I haven't had any luck banking with -n Here some code that works with the old gbdk: https://github.com/bokny/bank-switching

bokney avatar Nov 18 '15 23:11 bokney

I would really like to get this feature working, so if you have found something more, please post it. I will investigate the issue myself as soon as I got the time.

andreasjhkarlsson avatar Mar 07 '16 09:03 andreasjhkarlsson

Been working on banking, and while I have not managed to get the linker to produce the correct adresses I did find somethings:

  • The makebin program in sdcc refuses to produce roms > 32 kb, but simply removing the check seems to work just fine.
  • When compiling c files with the "#pragma bank <1,2,3..>" directive and -ba & -bo flags sdcc generates segments _DATA_1,2,3... & _CODE_1,2,3..., this seems fine, but the segments always ends up in the default data location in the final rom (0xc0a0 as is default in gbdk-n), in fact, all segments that aren't explicitly placed somewhere seems to end up there.
  • I managed to do some simple banking by absolutely placing a segment in 0x8000 (bank 2) and then switching to it during runtime with gbdk-n and referencing the data from 0x4000, obviously we need sdcc to be able to resolve these adresses automatically, but it shows that it can be done with the current tools.

I also found your thread on gbdev (http://gbdev.gg8.se/forums/viewtopic.php?id=309) @joekarl, maybe we could join some IM and see if we can work it out?

andreasjhkarlsson avatar Mar 08 '16 23:03 andreasjhkarlsson

Hello. I found an alternative linker for SDCC, which supports banked code. The description, source code and Windows binary are available at the link below. http://www.tensi.eu/thomas/programming/asxxx-linker/asxxxlinker.html I have not tried it out yet, but it looks promising.

majstar avatar Jul 13 '16 08:07 majstar

That's great! I'm creating a seperate issue for it.

andreasjhkarlsson avatar Aug 16 '16 10:08 andreasjhkarlsson

Expanding on what rotmoset found:

You can do multi-bank code by linking a separate .gb for each switchable bank (setting location to 0x4000 for the current bank and 0x8000 for the rest) and then manually placing each bank in the correct location in the final ROM. This way all banked code and data point to 0x4000 even though they might be located at 0x8000 or later in the actual ROM.

Couple that with trampoline functions in the fixed bank and you can call banked functions from anywhere without worrying which bank is active. Accessing banked data is already supported by SDCC with non-intrinsic named address spaces (section 3.5.4 in the manual).

wootguy avatar Sep 13 '16 23:09 wootguy

Just my two cents, as I've not tested this yet.

Would doing something simmilar to how SMSlib from DevKitSMS manages bank switching do the trick?

How to use more than 48KB in your ROM ('ROM paging'):

  • in your program, use the SMSlib provided SMS_mapROMBank(n) macro to map the bank you need (your code should be restrained to the first 32KB as the last 16KB will be paged out)
  • put your data into a separate .c file for each 16KB ROM bank starting from bank2, for example bank2.c, bank3.c etc... (you can use the assets2banks and folder2c tools described above) compiling each one with a different CONST segment name, I suggest using BANK# for descriptiveness:

sdcc -c -mz80 --constseg BANK2 bank2.c sdcc -c -mz80 --constseg BANK3 bank3.c

compile your program:

sdcc -c -mz80 --peep-file peep-rules.txt your_program.c

link all the objects together adding a parameter for the linker for each bank (_BANK#) and adding each .rel file to be linked (proper crt0 file goes always first) then all the bank#.rel files last, in ascending order:

sdcc -o your_program.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK2=0x8000 -Wl-b_BANK3=0x8000 crt0_sms.rel

Check it out here:

https://github.com/sverx/devkitSMS

Regards.

mojontwins avatar Jan 10 '17 09:01 mojontwins

The SMS way of doing this is faster than what I was doing (only has to link once) but you still have to switch banks manually or use trampoline functions. Also, the order of files becomes important so you need to remember to update your linker command whenever you move code to a different bank.

wootguy avatar Mar 17 '17 23:03 wootguy

I assumed the need to use manual banking and / or trampolines. Those are needed in every banked system I have coded for, namely 8 bit computers, the SMS, or the NES. I mean, there's no compiler support for automaticly handling banks in any of the systems I've tried, so it's not a big deal.

As for the order of files, no big deal either. The cc65 compiler handles this properly as you can feed the compiler a configuration files with an extended memory map, and then locate your code and assets from the very sources using pragmas. But lacking such a feature, I don't think it is a big deal to organize your code manually.


From: wootguy [email protected] Sent: Saturday, March 18, 2017 12:51 AM To: andreasjhkarlsson/gbdk-n Cc: mojontwins; Comment Subject: Re: [andreasjhkarlsson/gbdk-n] Banked code support (#5)

The SMS way of doing this is faster than what I was doing (it only has to link once) but you still have to switch banks manually or use trampoline functions. Also, the order of the files becomes important so you need to remember to update your linker command whenever you move code to a different bank.

— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/andreasjhkarlsson/gbdk-n/issues/5#issuecomment-287498315, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AFwBaEX8nJ23EeC27js61VvmZ6iUpztaks5rmxyLgaJpZM4GYkZa.

mojontwins avatar Mar 20 '17 07:03 mojontwins

Not saying it's a bad solution, but I thought the goal here was to get the original gbdk functionality working (automatic bank switching).

Personally, I think we should just make a gameboy version of ihx2sms and consider this solved.

wootguy avatar Mar 20 '17 16:03 wootguy

Could it be posible to just make it work the way it currently is in 2.95-3? For what I've seeen those automatic things doesn't work but you can use #pragma bank to define the bank where a file compilation should be placed Also there are always those things like accessing an array of data stored in one from another bank that are imposible to automatize Banks in 2.95-3 work perfectly so I don't think we need any kind of improvement over it

Zal0 avatar Apr 27 '17 11:04 Zal0

After several attemps I finally managed to use banks using the linker in the old gbdk instead of the one that comes with sdcc As stated here http://gbdev.gg8.se/forums/viewtopic.php?id=309 the linker in the sdcc has some issues. For what I've seen both the linker and the makebin tool are not fully working and cannot produce a valid gb supporting banks Basically the sdcc linker doesn't output any memory offset to the ihx file and the makebin is not ready to read them. Until someone finds some time to fix this I am gonna stick to my current solution that so far has been working really well

Zal0 avatar Jul 03 '17 07:07 Zal0

@Zal0 😲

Is that all that's required to get banking working on modern SDCC/GBDK-N? You can literally just use the old linker to gain banks but still benefit from all the niceness of modern SDCC?

I ask because I had literally just a few days ago begun writing a new linker for modern SDCC that would output to a .gb, with the aim of supporting banking (as well as other conveniences like command line options to provide values for ROM header fields). But if "just use the old linker" literally solves the banking problem without significant downsides, then I'm not sure the effort to write a brand new linker could provide enough value to be justified.

If the old linker really does solve this problem, then that should probably be adopted as the solution to this issue. My understanding is banking is the only major thing that's been holding back GBDK-N from being reasonably recommendable as a replacement to GBDK in nearly all situations.

DonaldHays avatar Jul 05 '17 17:07 DonaldHays

@DonaldHays

Yes, that's the only thing you need. And you don't even need gbdk-n. You can use it or not. A few things to take into account, that might become problematic in the future:

  • lcc requires you to have gb.lib and crt0.o inside $(GBDK_HOME)\lib\small\asxxxx\gb I haven't found any way to force it to get it from other folder
  • for some reason lcc also requires all .a files inside that folder (so you need to change the extension in all your .rel files and copy them there if you want to use gbdk-n)
  • lcc also requires gbz80.lib to be inside $(GBDK_HOME)\lib\small\asxxxx\gbz80 with all the .o The main problem I found here is that I needed to include __sdcc_call_hl.s, div.s, mul.s and _memcpy.c from the sdcc source code (inside sdcc-3.6.0\device\lib). There migh be other linking issues that I am not aware since I am not using all the functions

since this is all a bit problematic I think fixing the problems on sdcc would be the best solution. I wouldn't start a new linker from scratch, just fix the one we already have

Zal0 avatar Jul 05 '17 17:07 Zal0

@Zal0 @DonaldHays

This may be irrelevant. I'm yet to use bank switching myself, so I don't know the first thing about it.

Anyway, I stumbled upon libstdgb today. A newly written C library for Game Boy development and an alternative to gbdk-n.

It seems to have rudimentary support for bank switching as you can see in this search result. A Python script, gbromgen.py, accomplishes this by altering the output of sdcc's makebin tool.

svendahlstrand avatar Jul 05 '17 18:07 svendahlstrand

So I went back to a more humble idea than a custom linker, which was trying to hack banking support together at the final ihx-to-gb stage. For my most recent game I wrote a custom tool called IHX2GB that replaces makebin in the modern SDCC pipeline (at ~300 lines of code, it's a far smaller project than a linker).

I've created a branch of my game at https://github.com/DonaldHays/bubblefactory/tree/experiment/banking where I've modified IHX2GB to work with code banking, and modified my game to use code banking. The output ROM is 64KB, but both IHX2GB and the technique used support larger ROMs than that.

How it works: in the linker stage (see the makefile in the game directory), you set the code bank locations to 0x014000, 0x024000, 0x034000, etc. IHX2GB parses the .map file found in the same directory as your .ihx file to learn about your code banks, and uses that information to determine the write locations in the ROM when it writes your banks (also it uses the .map file to generate a .sym file, useful for debugging in BGB).

IHX2GB also contains features like full options for configuring header fields (name, cbg/sgb support, licensee code, etc), including cartridge type, rom size, and ram size. It computes the size of the output .gb based on the value you provide for --rom, which is what enables it work with sizes greater than 64KB.

This all seems to work, and really seems like a viable road to working with banked code in modern SDCC on Game Boy.

But, there is one serious downside: it's fundamentally very hacky. The IHX file itself is nothing but a list of addresses and bytes. There's no additional semantic information, and so there's no way from that to know which address maps to which bank (except that any writes >= address 0x4000 correspond to banked code in general). My observation was that the MAP file lists code banks in the same order as they appear in the IHX file, and so I use that observation to work out the banks. I know of no guarantee that code banks appear in the MAP file in the same order as the IHX file, and so IHX2GB's banking support is built on an assumption. The assumption holds in my testing, but it's an assumption nonetheless.

I agree with @Zal0 that the correct solution would be for SDCC to be fixed, since banking would work correctly through the intended, supported tools, and not a "crossing your fingers and praying really really hard that the assumption holds" tool. But, assuming we don't observe any cases that contradict this assumption, this may be a functional solution in the interim.

If this seems like a good road to take, I can look into spinning the tool out into an independent repo and polishing it up.

DonaldHays avatar Jul 07 '17 04:07 DonaldHays

A while ago, I found a modified version of GBDK with extended libs from 2010. That one uses a newer SDCC that has been modified to support banking (and it works). Maybe you could check it out and see if that would help here? http://www.rrsd.com/software_development/gameboy_development/ That link is dead ATM, so here's the relevant zip file: gbdevelopmentkit.zip

robbi-blechdose avatar Jan 30 '18 09:01 robbi-blechdose

I’m playing around with banking for non-code: I wrote an image converter that can place images at specific addresses in ROM https://github.com/basxto/png2gb

An excerpt from my makefile:

CC=$(BIN)/gbdk-n-compile.sh -Wa-l
LK?=$(BIN)/gbdk-n-link.sh -Wl-m
MKROM?=$(BIN)/gbdk-n-make-rom.sh -yc -ya 4 -yt 2 -yo 4
EMU?=retroarch -L /usr/lib/libretro/gambatte_libretro.so
pngconvert=$(DEV)/png2gb/png2gb.py

# maximum of 128 tiles in this spritesheet
# maximum 8 palettes
# maximum of 256 mapped tiles
pix/overworld_a_gbc_data.c: pix/overworld_a_gbc.png
	$(pngconvert) --width 2 --height 2 --datarom "0x7FFF-0x1000" --palrom "0x7FFF-0x1080" --maprom "0x7FFF-0x1280" $^

pix/overworld_b_gbc_data.c: pix/overworld_b_gbc.png
	$(pngconvert) --width 2 --height 2 --datarom "0x7FFF-0x800" --palrom "0x7FFF-0x1040" --maprom "0x7FFF-0x1180" $^

pix/overworld_a_test_gbc_data.c: pix/overworld_a_gbc.png
	$(pngconvert) --width 2 --height 2 $^ --datarom "0x7FFF-0x800" --palrom "0x7FFF-0x1040" --maprom "0x5FFF-0x1180" -o pix/overworld_a_test_gbc

pix/overworld_b_test_gbc_data.c: pix/overworld_b_gbc.png
	$(pngconvert) --width 2 --height 2 $^ --datarom "0x7FFF-0x1000" --palrom "0x7FFF-0x1080" --maprom "0x5FFF-0x1280" -o pix/overworld_b_test_gbc

banking.gb: banking.ihx
	$(MKROM) $< $@

banking.bank: banking.gb
	dd skip=`printf "%d" 0x3FFF` count=`printf "%d" 0x4000` if=$^ of=$@ bs=1

banking.ihx: nomain.rel pix/overworld_a_test_gbc_data.rel pix/overworld_b_test_gbc_data.rel
	$(LK) -o $@ $^

bigrom.gb: $(ROM) banking.bank
	cat $^ > $@

runbigrom: bigrom.gb
	$(EMU) $^

But I haven’t gotten it running this way yet.

basxto avatar Mar 31 '20 02:03 basxto

you never get this working, because of the modern sdcc linker issues, and makebin can not make files other 32768 bytes size. the only way is to use an old linker with the modern sdcc. thisway: https://github.com/untoxa/sdcc4_banks_example/blob/master/make.bat

you can use an old gbdk library with this construction, you can use gbdk-n, you can use a new gbdk-2020 - that all works.

untoxa avatar Apr 26 '20 22:04 untoxa

ps: and i also found a way to make trampoline functions work. you declare a function, say, int some_bank2_proc(int a, int b, int c) __banked { printf(" in %s\n", hello2); return a + b + c; } put it in bank2, compile with -bo2 switch, and then you just call it from some place, say, main(): int somevar = some_bank2_proc(16, 32, 64); no manual bank switching at all. here's an example and more detailed explanation: https://github.com/untoxa/sdcc4_farcalls_example

untoxa avatar Apr 28 '20 08:04 untoxa

Revision 11763 of SDCC adds support for banking with #pragma bank 1, RAM banking is still broken. The .ihx file has the data at the right place, everything else in sdcc gets virtual addresses. #pragma bank 2 will end up at the virtual address 0x24000 and at the ROM address 0x8000

basxto avatar Jul 23 '20 11:07 basxto

How it works: in the linker stage (see the makefile in the game directory), you set the code bank locations to 0x014000, 0x024000, 0x034000, etc. IHX2GB parses the .map file found in the same directory as your .ihx file to learn about your code banks, and uses that information to determine the write locations in the ROM when it writes your banks

That will break, since sdcc translates the addresses when creating the .ihx file

(also it uses the .map file to generate a .sym file, useful for debugging in BGB).

That should still work, since the .map file will have 0x014000 for _CODE_1, _CODE_N defaults to 0xN4000 in general now.

IHX2GB also contains features like full options for configuring header fields (name, cbg/sgb support, licensee code, etc), including cartridge type, rom size, and ram size. It computes the size of the output .gb based on the value you provide for --rom, which is what enables it work with sizes greater than 64KB.

makebin already had cgb and name support, but I hope I’ll get to adding the other header options to makebin. Maybe I can even add -yp, the old linker had.

basxto avatar Jul 23 '20 12:07 basxto