This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Program code in WRAM

Program code in WRAM
by on (#204622)
I like my code to remain in the fixed bank because I think switching the bank is too risky and too much of a hazzle. That's why I attempt to use the switchable banks only for data.

But now that my game uses MMC1 with battery save, I thought of a way to extend the available room for code should it ever become too small:

Would it be possible to use the otherwise unused space in WRAM for code?
You store the code that doesn't fit into the fixed bank anymore somewhere into another bank. And at program startup, you copy that code from the other bank into WRAM. Now you have up to 8 KB (minus the stuff you need for savestates) as additional always available program code.

Would this be possible?

The only problem that I see is: The WRAM has different addresses than the switchable bank. How do you organize your source code, so that you can still call these functions normally?
Re: Program code in WRAM
by on (#204624)
Yes, you can do this. You just have to assemble the code with an origin of $6000, even though it will be mapped somewhere else in ROM when you're copying it to RAM.

In ca65 you could probably create a memory section with start = $6000, size = $2000 and one or more segments that use it. Then, at run time, you'd switch to the bank that contains that chunk and copy it to WRAM. From then on you can use the code in WRAM as you would any code in ROM.
Re: Program code in WRAM
by on (#204626)
But how do I do this in practice, namely in cc65?
Re: Program code in WRAM
by on (#204627)
Edited my post. I'm not sure about C code, but what I wrote is what I'd do for assembly.
Re: Program code in WRAM
by on (#204628)
I know how to create the stuff in the cfg file and how to prepare the code in the bank, but I don't know how to make the compiler know the function addresses in WRAM, except for the first function:
Code:
.segment "SWITCHABLE_ROM_BANK"

Function1Rom:
    BLA
    RTS

Function2Rom:
    BLA
    BLA
    RTS

.segment "WRAM_CODE"

Function1:
    ; Do nothing.
    ; After the copy process,
    ; the code from Function1Rom
    ; will be here.

But how do I make the compiler/assembler know the RAM address for function 2?
Re: Program code in WRAM
by on (#204630)
Everything you put in the "WRAM_CODE" segment should use addresses in the $6000-$7FFF area if that segment uses a MEMORY area of start = $6000, size = $2000.
Re: Program code in WRAM
by on (#204631)
So, I assume I have to do something like this?
Code:
Function2 = WRAM_CODE_LOAD + Function2Rom - SWITCHABLE_ROM_BANK_LOAD
Re: Program code in WRAM
by on (#204632)
Use the LOAD and RUN attributes in your linker configuration file: http://cc65.github.io/doc/ld65.html#ss5.4

Point the LOAD attribute to ROM, and the RUN attribute to WRAM. Then you can use define=yes to get the linker to define symbols which you can use to copy the data from ROM to WRAM.
Re: Program code in WRAM
by on (#204635)
I didn't know there was a RUN attribute.
Re: Program code in WRAM
by on (#204637)
Oh crap, I forgot about LOAD and RUN!
Re: Program code in WRAM
by on (#204638)
Looks like this is really the most elegant solution. Thanks for that hint.
Re: Program code in WRAM
by on (#204643)
As a side note, if you're going to have code sit in WRAM anyway, you might as well compress it so you can save PRG-ROM (in this case, if this can make your game fit in 128 KB PRG-ROM instead of 256 KB PRG-ROM).
Re: Program code in WRAM
by on (#204644)
What codec is effective for 6502 machine code?
Re: Program code in WRAM
by on (#204646)
There is Aplib/pucrunch, or huffman.
Re: Program code in WRAM
by on (#204648)
I'd bet on Huffman since it could encode frequently used opcodes on few bits. I'm kind of fond of Huffman personally, it compress almost everything well.
Re: Program code in WRAM
by on (#204650)
Aplib has the two cases for small amounts of recently appearing data:

111, xxxx: (7 bits) any recent byte in last 16 bytes
110, B: (11 bits) length 2 or 3, offset 01-80

So single bytes that appeared recently eat up 7 bits of space, and 2-3 byte long sequences that recently appeared eat up 11 bits of space.

Meanwhile, huffman has to include a table before the compressed data can appear. But the compression can be good for bytes that appear frequently throughout the data, regardless of position and surrounding bytes.
Re: Program code in WRAM
by on (#204659)
Bregalad wrote:
As a side note, if you're going to have code sit in WRAM anyway, you might as well compress it so you can save PRG-ROM (in this case, if this can make your game fit in 128 KB PRG-ROM instead of 256 KB PRG-ROM).

I don't think the source code will be the thing that decides whether I need 128 or 256 KB. The deciding factor will be the game world data, the music and especially the dialogs.
Re: Program code in WRAM
by on (#204662)
By the way, are there speed differences between reading code from WRAM vs. reading it from regular ROM?
I'm asking, so that I know whether I should rather put the time-critical code into RAM (like sprite updates) or the code that is done when rendering is turned off (like CHR RAM updates).
Re: Program code in WRAM
by on (#204663)
NES CPU always operates at the same speed, regardless.

The big speed boost you get for code in RAM is self-modifying code.
Re: Program code in WRAM
by on (#204688)
Yeah, you get lda #xx \ sta $2007 loops when you use self-modifying code in RAM.
Re: Program code in WRAM
by on (#204711)
DRW wrote:
I don't think the source code will be the thing that decides whether I need 128 or 256 KB. The deciding factor will be the game world data, the music and especially the dialogs.

Definitely, but you can also compress those. For instance, you could use the same Huffman compression with a different Huffman table adapted to the type of data to be decompressed.
Re: Program code in WRAM
by on (#204712)
Maybe later. First I'll have to see how much stuff we even have. And counting on 256 KB means that I don't have to worry about space and we can also add some high quality in-game artworks to the unused ROM. A situation that will probably not happen if I aim to fit everything into 128 KB.
Re: Program code in WRAM
by on (#204721)
Well, using 256 KB doesn't prevent you from compressing data and code to make even more of it fit.
Re: Program code in WRAM
by on (#204724)
I assume using 256 KB is more than enough, so that I don't need to compress data. Maybe compressing dialogs to six bits per chacter instead of eight, but not much more.

If I don't use bankswitching for code, I can only use up to 8 KB of uncompressed additional code anyway: The space in WRAM.
And since, for data, the 15 switchable banks are probably more than enough for my whole game, what would be the advantage of compressing 8 KB of additional code, so that it takes a bit less space in the switchable bank of which we have more than enough anyway?

There's plenty room for game data (graphics, dialogs, enemy behaviors etc.): 15 x 16 KB.
But there's only 16 KB for code and maybe up to 8 KB if it's really necessary. Unless I want to fiddle with bank switching. Which I only want to do for data, not for code.

So, wasting code space for a compression algorithm, to compress data for which we just got twice as much space as originally intended, is a bad idea in this constellation.
Re: Program code in WRAM
by on (#204732)
Prematurely worrying about compression is definitely a bad idea.
Re: Program code in WRAM
by on (#204747)
tokumaru wrote:
Prematurely worrying about compression is definitely a bad idea.

Well I am extremely biased as I love fitting everything in a PRG-ROM as small as possible. In my game engine many things are compressed, code (in the form of interpreted bytecode instead of compressed 6502 code), levels, metasprites. Only small things such as code run every frame, palettes and level metatile definition aren't compressed. I even wrote CompressTools for the sole purpose of studying as much as possible about compression and be able to customize my compressions.

Quote:
If I don't use bankswitching for code, I can only use up to 8 KB of uncompressed additional code anyway: The space in WRAM.

Wrong, WRAM is rewritable so you can put as much code as you want there, just only 8KB at the same time.
To be honest copying only <8kb of code here to never touch it ever again makes few sense, basically it'd be just a small expansion on the fixed bank. However doing that multiple times and treat the WRAM area as an additional bankswitch room (where bank-switching is slow) makes more sense.

In the case of a JRPG, you could have the battle code loaded when starting a battle, the menu code loaded there when you open the menu, the dialogue box code loaded there when opening a dialog box, etc, etc... You could even have more than one bank, say, two 3kb banks and this leaves 2kb for save game data. Decompressing 3kb of code should take about one frame, and when you do it you have it at your disposal in WRAM.

You also get the gain of possibly self-modifying code, which can in some cases be faster (however it can also become a headache). If a RAM variable is only loaded at one place, for instance, you can just use LDA #imm and change the immediate as it is the variable itself. You can also change JMP and JSR adresses on the fly.

Quote:
And since, for data, the 15 switchable banks are probably more than enough for my whole game, what would be the advantage of compressing 8 KB of additional code, so that it takes a bit less space in the switchable bank of which we have more than enough anyway?

There's no point in decompressing code in WRAM only once and never touch it again, that'd be insignificant as you pointed out.

Considering what I stated above, which holds ture when using compression, then it suddenly makes a lot more sense as you can compress a lot more code that way, of course not the whole game's code, as you have constraints that is only <8kb of decompressed code at the same time and it takes time to rewrite it with other code, but you could compress a significant part of the code that way. It makes sense in the general case, but maybe not in your specific case. I don't know, as tokumaru said it's too soon to know but I thought it would just be good for you to know about the possibility (without using it), and resorting to it later if the situation ever calls for it.