Page 1 of 1
Temp/Scratch Variables
Posted: Tue Dec 14, 2010 10:28 pm
by Ian A
I've been using a few zp memory addresses as temporary storage space for various routines when it's inconvenient to use the stack. Is this bad practice?
Re: Temp/Scratch Variables
Posted: Tue Dec 14, 2010 11:24 pm
by tokumaru
Ian A wrote:Is this bad practice?
Not at all, as long as you are really careful when managing those bytes in order to avoid conflicts (i.e. different pieces of code trying to use the same temporary locations at the same time).
I for example have set aside the first few bytes of ZP for this purpose, so at the top of subroutines that need this memory I use ENUM and DSB commands to "declare" variables. At the end of the declarations I put a label to mark the next free temporary byte, so that if other subroutines run inside of this one they start their declarations from where the "parent" function left off. This is useful for subroutines that are always called from the same locations, so you know for sure how the memory is being used when they run.
I also use a few bytes that are meant for very temporary use, which every subroutine is free to use, but shouldn't expect the values to be kept if they make use of other subroutines, as any subroutine is free to modify them.
For the few cases that don't fit the above situations, I simply give the routines dedicated bytes which are not used for anything else. That rarely happens though, so it's not like I'm wasting a lot of memory with this.
It would be wonderful if the stack could be used for local variables and all parameter passing, but having to use an index register to access it and having to manipulate the stack pointer make that a very unpractical option.
Posted: Tue Dec 14, 2010 11:33 pm
by koitsu
Mirroring tokumaru's sentiments.
When developing code from scratch, I tend to use ZP heavily until I start getting close to running out of space (since you're limited to 256 bytes). I then re-work the scratchpad variable locations into scratchpad RAM, ensuring not to use $0100-01FF due to risks of smashing the stack[1]. I keep speed-sensitive things in ZP while everything else goes into scratchpad RAM.
This is one area where the 65816 often spoiled me (direct page (effectively relocatable ZP -- but then if it's in page 1 it's not ZERO is it!) is really great).
[1]: You can, of course, use $0100-01FF as scratchpad if you need to. There are lots of games which use $0100-01A0 or so, as the running code ensures it never pushes more than 0x60 bytes worth of data on the stack. I don't recommend doing this unless you're extra-super-duper careful. :-)
Posted: Tue Dec 14, 2010 11:53 pm
by tokumaru
In most of my programs, nearly all the variables are in ZP. If necessary, I let them (preferably the ones that would benefit less from faster access) spill into page 1, but the rest of the memory is mostly huge tables/arrays, never small 8 or 16-bit variables.
koitsu wrote:[1]: You can, of course, use $0100-01FF as scratchpad if you need to. There are lots of games which use $0100-01A0 or so, as the running code ensures it never pushes more than 0x60 bytes worth of data on the stack. I don't recommend doing this unless you're extra-super-duper careful.

I do that all the time!

My stacks are always really tiny, like, 32 to 40 bytes. I don't use the stack to make huge memory transfers or anything like that, so for trivial things like subroutines and interrupts that's more than enough for quite a few levels deep.
Posted: Wed Dec 15, 2010 8:13 am
by tepples
koitsu wrote:This is one area where the 65816 often spoiled me (direct page (effectively relocatable ZP -- but then if it's in page 1 it's not ZERO is it!) is really great).
I learned 6502 assembly language on an Apple IIe (6502), enhanced Apple IIe (65C02), and Apple IIGS (65816), owned by a middle school and a public library. The
Apple IIGS Hardware Reference had the full WDC 65C816 data sheet, which stated which instructions came from the 6502, which from the 65C02, and which were new. It used 'd' for an 8-bit address within direct page or the stack frame, 'a' for a 16-bit address within a bank, and 'al' for a 24-bit address. For example, the instructions doing indirect indexed with Y were called "LDA (d),Y". To this day, when I describe 6502 instructions, I use 'd' for an 8-bit address and 'a' for a 16-bit address, despite that "zero page" doesn't start with 'd'.
Programs used zero page far less on the Apple II and similar systems, in part because the BIOS ("Monitor ROM"), operating system (Apple DOS 3.3 or ProDOS), and especially Applesoft BASIC hogged most of it. This especially made the (d,x) addressing mode near useless, compared to how useful it is on NES for e.g. accessing music data through pointers.
You can, of course, use $0100-01FF as scratchpad if you need to. There are lots of games which use $0100-01A0 or so, as the running code ensures it never pushes more than 0x60 bytes worth of data on the stack. I don't recommend doing this unless you're extra-super-duper careful.

Or unless you can play-test your game under a debugger that can watch writes to $01A0-$01AF, or you can stick a canary value in $01AF and change e.g. the score display's color if that ever changes.
Posted: Wed Dec 15, 2010 10:49 am
by Ian A
Thanks! It's good to know I was on the right page!
Posted: Tue Jan 18, 2011 8:35 pm
by frantik
Both Super Mario Bros and Excitebike use the first few bytes of ZP for "temp/scratch" variables

i'm sure other games did too
Posted: Tue Jan 18, 2011 11:35 pm
by Celius
I use both temp vars in ZP and the stack. Like some other people, I am never using the stack for anything large. Most of the time it's just a byte or two that I push on if I'm calling a routine and need to save values outside of temp variables. However, I will NEVER directly write to $100-$1FF. I consider it a unique memory range only to be used for pushing and pulling values.
I actually use about 32 bytes in my current project for temporary storage:
8 temp vars for main loop
4 temp addresses for main loop
8 temp vars for NMI
4 temp addresses for NMI
Though now that I've nearly completed my game engine, I'm seeing that I didn't use nearly as many temp vars in the NMI routine as I did in the Main loop, and I think I don't even use more than 2 temp addresses in either the main loop or NMI. I set these up in the beginning of my project not knowing how much of them I was going to use.
tokumaru wrote:I for example have set aside the first few bytes of ZP for this purpose, so at the top of subroutines that need this memory I use ENUM and DSB commands to "declare" variables. At the end of the declarations I put a label to mark the next free temporary byte, so that if other subroutines run inside of this one they start their declarations from where the "parent" function left off. This is useful for subroutines that are always called from the same locations, so you know for sure how the memory is being used when they run.
Just out of curiosity, how do you go about debugging when you don't know the exact address of each variable you're using? Is there some other way you keep track of it? For example, I mean writing something like this:
ZTempVar0: .ds 1
Rather than:
.DEFINE ZTempVar0 $30
Where you just set aside the next available byte(s) for a given variable rather than explicitly assigning it a memory location. It seems like it would be difficult to keep track of where those variables are allocated in RAM without explicitly defining it. Is there an easier way that I'm just missing?
Posted: Wed Jan 19, 2011 12:12 am
by MetalSlime
Celius wrote:
Just out of curiosity, how do you go about debugging when you don't know the exact address of each variable you're using? Is there some other way you keep track of it?
I use ca65 and I tell the assembler/linker to output list/map files. Then I use a utility that Gradualore wrote called nlgen that reads ca65 .lst/.map files and generates .nl files that FCEUXDSP can use to import symbols into it's debugger. All RAM address get replaced by their variable names in the debugger.
Posted: Thu Jan 20, 2011 11:16 am
by Celius
It's funny, you can call me crazy, but I still define the exact placement of every single variable I reference. If I need to look at the contents of RAM, I just look at my variable declarations and I see exactly what value I'm looking for.
Posted: Fri Jan 21, 2011 7:34 am
by GradualGames
MetalSlime wrote:Celius wrote:
Just out of curiosity, how do you go about debugging when you don't know the exact address of each variable you're using? Is there some other way you keep track of it?
I use ca65 and I tell the assembler/linker to output list/map files. Then I use a utility that Gradualore wrote called nlgen that reads ca65 .lst/.map files and generates .nl files that FCEUXDSP can use to import symbols into it's debugger. All RAM address get replaced by their variable names in the debugger.
Don't forget it inserts the comments, too!

Posted: Fri Jan 21, 2011 8:56 pm
by tokumaru
Celius wrote:It's funny, you can call me crazy, but I still define the exact placement of every single variable I reference.
You are crazy. I move my variables around so much that I would soon be crazy if I had to modify their addresses one by one.
If I need to look at the contents of RAM, I just look at my variable declarations and I see exactly what value I'm looking for.
If I want to know the address of a variable I just put it somewhere easy to find in the ROM (i.e. .dw MyVariable), then I just look it up and start debugging. It doesn't take much of my time, and I don't even need to do it very often.
Posted: Fri Jan 21, 2011 9:52 pm
by Celius
See, that's the thing with me. I'm always watching the contents of RAM. I've got so many variables that I constantly have to look up the placement of them.
I would go crazy if I had to find some obscure way of looking up their locations

.
And also, I find little reason to move the contents of RAM very often. I only have to go through my variables only a couple times to close up gaps from removing unused variables. The most important thing is differentiating between zero page and non-zero page variables. I can understand moving things to and from zero page often, but otherwise I just haven't had to rearrange variables very frequently.
The biggest pain is when I have to modify my object stacks/slots. Almost everything in my engine works by instantiating objects, and having them piled onto some sort of stack or inserted into slots. For example, enemies are instantiated as you move throughout the level and they are inserted into slots in RAM. Each enemy is allotted, say, a 21 byte slot of RAM. There are 8 slots available, making a total of 168 reserved bytes of RAM. When I say, "Gee, I think I should allow each enemy an extra variable", that means I really have to go in and make a lot of changes! Otherwise, maintaining the list of variables isn't that hard.