The official documentation of linker configuration files is ld65 Users Guide. You'll need a MEMORY area for each 32K bank and probably for the pseudo-fixed bank. If you want, I could make a BNROM version of my SNROM/UNROM template. This should help because GTROM is the same as BNROM except for the port moved down to $5000 and bank switching of CHR RAM and nametable memory.

1)define your "pseudo fixed bank" as a segment.2)in source, place whatever needs to be fixed (ie the bare game engine + probably also music driver) in that segment.3)instruct the linker to paste that segment at the same addr in each bank it needs to be in.

That way the "fixed bank" is always the size of the engine and not a byte more, which leaves optimal and maximized space for level/song data - and any plausible extras; like if there were level-specific functions or tables for example.

Having the engine in the fixed bank makes the programming easier, but it wastes a lot of space. Each fixed byte takes up 16 bytes in the ROM, which is a lot!

Technically, all you need in the fixed bank is the vectors, their handlers, and a trampoline for bank switches. Also DPCM data, if you're using that. The engine code can be placed in a separate 32k bank, and the music engine + songs can exist in their own 32 bank. I think this is how Lizard does things.

Might be a tangent for the compo, but it's a very useful discussion for the project, so please don't feel you need to hold back! It's all very informative, so thanks!

Hadn't even considered that. I don't have the source and am not sure i could make sense of it either, but i think we're pretty tight on cycles, NMI-wise especially. Would trampolines like that be a significant increase in cycles?

Quote:

Each fixed byte takes up 16 bytes in the ROM, which is a lot!

Only if you need to paste the engine across all banks. As i see it, you'd only need to do so across in-game engine/level banks. A bank strictly for storing graphics to be loaded in bulk between levels wouldn't need it, and titles, cutscenes and such wouldn't need it either. But yeah, i think we'd be looking at 12-14 bytes per "fixed" byte with this method. Fortunately, there's 512k of them if we go this path. On the other hand, *some* trampoline would need to be written at least for loading stuff at certain points.

honestly, the efficiency of data storage is on the bottom rung of things i care about here. most important is to free up some space, any space. even the inefficient way should yield about 8k, which is far more than i need.

Quote:

Having the engine in the fixed bank makes the programming easier,

priority #1. i'm a beginner here, this is my first real project. i'm not interested in making the best thing ever, just a fun game that people can enjoy in a style that i like.

do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?

With regard to detailed working steps for causing ca65 and ld65 to emit the code repeated in all banks, I cannot give an authoritative answer for several hours because I am not at a PC on which my NESdev toolchain (Git, Make, cc65, Python, GIMP, Windows or Wine, and FCEUX debugger) is installed. In the meantime, I'll answer the following question:

FrankenGraphics wrote:

Would trampolines like that be a significant increase in cycles?

I'll sketch an untested trampoline for BNROM here for the purpose of estimating how many cycles it would use. Some changes are needed for GTROM because PRG bank is in the same port as CHR and nametable switching, but they should be minimal.

Code:

; These parts go in all banks, using a method to be determined; once I am at a PC on which my NESdev toolchain is installed

; A ROM location reflecting which bank is currently switched incurrent_bank: .byte I

; This part goes in any bank.segment "MUSICCODE".proc nmi_handler_body ; TODO: Push display list to OAM ; TODO: Push VRAM changes to CHR RAM and nametable ; TODO: Call music engine ; (Watch out for reentrancy when the NMI might interrupt ; changing music or playing a sound effect!) jmp nmi_handler_epilog.endproc

identity: .byte 0, 1, 2, 3

An NMI handler that does significant work will already be saving and restoring AXY. The parts of this not in a "typical" NMI handler are saving the old bank, switching to the new bank, and the JMP in and out of the body, and restoring the old bank, which total about 30 cycles.

The preceding doesn't include the logic to put the prolog and epilog in all banks. To make that, I would need to be at a PC on which my NESdev toolchain is installed.

FrankenGraphics wrote:

A bank strictly for storing graphics to be loaded in bulk between levels wouldn't need it

It'd still need the trampolines in case graphics loading is interrupted, and it'd need the code for decompressing said graphics.

do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?

i think there are three ways; somewhat combinable:

.segment [name] = anything between this directive and the next .segment can be used by the linker to paste the segment exactly where you want it in ROM. this should give you the most convenient control.

.inc or .include [filename] = includes a separate source file - you still need to define a .segment, though.

.incbin [filename] = includes a separate binary as raw data. This isn't preferable imo as you need to pre-assemble it. It's mainly for assets. also, .segment is still what gives you control over where it is placed in ROM.

Correct me if i'm wrong, but I don't think you can escape using .segment in any way without complicating stuff further.

Still, i've seen at least one instance where partytimehexcellent .incbinned the famitracker driver + music data at a specified ROM addr to be done with it. This was done to a simpler mapper using another assembler without a linker, though. with the cc65 suite (nesicide included), the linker is supposed to do ROM organization stuff for you - via the linker cfg.

It would be a waste to replicate the entire engine across multiple banks! What I normally do is put data and functions that use that specific type of data in their own banks. For example, all banks that contain level maps will include copies of the routines that prepare rows and columns of tiles for scrolling, and also the routines that test for collisions between objects and level geometry. Banks with compressed graphics will have a copy of the decompression routine, and so on. It's the same principle of having audio data in the same bank as the audio driver.

Switching banks in simple mappers like GTROM is pretty fast, so that shouldn't have a significant impact on your vblank bandwidth. I'm obsessed with making the most out of the vblank time myself, so I don't even push A, X and Y to the stack during an expected vblank, I only do it on unexpected vblanks (i.e. lag frames). This is controlled by a flag on bit 7 of some variable (e.g. "FrameRead") so that it can be checked with BIT without corrupting any registers. The main thread waits for vblank by waiting for this flag to be cleared, and the NMI handler will only bother saving A, X and Y when the flag is already clear when the NMI fires.

Something along these lines. Saves a few cycles if you're really really tight on vblank time.

As for simulating a fixed bank when using 32KB PRG-ROM switching, you can .include the "fixed bank", but you have to do something about the repeated labels. One thing you can do is include the file normally only once, and all other times surround it in a new scope:

Code:

.segment "FIXED0".scope .include "fixed.asm".endscope

.segment "FIXED1".scope .include "fixed.asm".endscope

.segment "FIXED2".scope .include "fixed.asm".endscope

;(...).segment "FIXED15".include "fixed.asm"

It's similar to the macro approach, but with includes. If you need to put the bank number in each bank, you can just do .byte <.bank(*).

Who is online

Users browsing this forum: No registered users and 0 guests

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum