Jump to content
IGNORED

Setting Multiple Flags Per Byte


TylerBarnes

Recommended Posts

Not sure if it has already been discussed elsewhere, but was wondering....

Simple projects on the NES are going certainly going to have plenty of slots in RAM to use for various flags, values, and buffer space. However, I could see certain cases where a project gets very large or large ram buffers are used. Anyone ever use a single byte in RAM for up to status 8 flags? 

I was thinking about this in that having tons of bytes in RAM that only ever toggle from 0 to 1 all the time is wasteful. Also I could see it used as a sort of organisation system if one byte did many related flags. 

Too convoluted or no? 

Setting flags in this way could look like this (in my head at least): 
 

PlayerState = $A0
IsStunned   = 1
IsJumping   = 2
HasPowerUp  = 4 
IsDead      = 8

GameState   = $A1
Paused      = 1
GameOver    = 2
Credits     = 4


Jump: 
	LDA PlayerState
	ORA #IsJumping
	STA PlayerState
	[Do jump stuff] 
	RTS

Physics:
	[Do gravity stuff] 
	[Do floor detection] 
	BEQ Landed
	RTS
Landed: 
	LDA PlayerState
	EOR #$FF
	ORA #IsJumping
	EOR #$FF 
	STA PlayerState
	RTS

 

Link to comment
Share on other sites

What, exactly are you asking?  I've never dug to deep in NES dev, but a lot of flags are bits, packed within specific bytes. I'm unfamiliar with the ASM syntax, but usually you do an AND mask of some form to check a bit, and then branch accordingly.

To be clear, this is the right way to save space and not waste bytes.

  • Thanks 1
Link to comment
Share on other sites

For NES development I do this constantly. It's very simpe to check towards a single bit/flag, it's just a single AND. So I don't really see why you shouldn't. I store a lot of data in bitmasks also, and enemy/object spawning/destroying uses a bitmask too, just using an 8-byte LUT to get the correct mask value.

For SNES development, I don't really bother. In fact, I frequently even reserve two bytes for tiny numbers, just so I don't have to worry about not having registers the correct size when I address it.. That system just has an infinite supply of RAM.

Edited by Sumez
  • Thanks 1
Link to comment
Share on other sites

6 hours ago, RH said:

What, exactly are you asking? 

The main inquiry was are people bothering with the extra logic involved when using every bit for flags and masking all the time, or if it is more rare to want/need to use all bits of a byte for flags. 
 

6 hours ago, RH said:

but usually you do an AND mask of some form to check a bit, and then branch accordingly.

Indeed. I forgot to include that sort of section in my example. 

Jump: 
	LDA PlayerState  ; Load Player Flags
	AND #IsJumping   ; Examine Only Jump Flag
	BNE Skip         ; Skip if flag is set 
	LDA PlayerState  ; Load Player Flags
	ORA #IsJumping   ; Set Jump Flag
	STA PlayerState  ; Save Flags
	[Do jump stuff] 
Skip:
	RTS



 

5 hours ago, Sumez said:

using an 8-byte LUT to get the correct mask value.

Do you have a small digestible example of this lookup table setup? I don't think I can picture what you mean. 

Link to comment
Share on other sites

33 minutes ago, TylerBarnes said:

Do you have a small digestible example of this lookup table setup? I don't think I can picture what you mean. 

MaskLUT:
   .hex 01 02 04 08 10 20 40 80

I think they're referring to something like the table above. Then if you want to get the 'i'th bit you and with 'MaskLUT + i'.

  • Like 1
Link to comment
Share on other sites

Ah, I see now. I was over thinking in that it was something more specific than that. I personally like the readability of declaring constants with labels. Seeing too many instances of MaskLUT+1, MaskLUT+3, etc in the code would throw me off in the context I am thinking of. Other contexts I'm sure would be great with the above. 

Link to comment
Share on other sites

Seems entirely reasonable to me. I'm working my way through Nerdy Nights and took a similar approach for flags.

Although I wonder why I haven't seen other people using BIT for this:

	LDA #8
	BIT buttons1
	BEQ MovePaddleDown

As far as I can tell, it's identical in cycles and bytes, so is it just a matter of taste or is there a drawback there? I guess the only benefit I see with using BIT over AND is that A is left alone, in case you wanted to use the same bitmask right away.

With the game states though, I probably wouldn't represent it at a bitfield, since the states are actually mutually exclusive. You won't ever have a state which is GameOver & Credits & Paused, for instance.

The approach I'm planning to use in my NN Pong is that "State" is just an address and then I do an Indirect JMP once per frame to do the updates. Sadly there's no indirect JSR. You also couldn't do a jumptable of states, since there's not even indexed JMP. Could be some drawback to this plan, but I guess that's what this whole phase of learning is for.

Link to comment
Share on other sites

6 hours ago, TylerBarnes said:

Ah, I see now. I was over thinking in that it was something more specific than that. I personally like the readability of declaring constants with labels. Seeing too many instances of MaskLUT+1, MaskLUT+3, etc in the code would throw me off in the context I am thinking of. Other contexts I'm sure would be great with the above. 

If you're working with constants there's no reason to use data space at all, and of course "MaskLUT+3" doesn't make any sense - that's not the purpose of a look-up table. 🙂

My example was in the context of accessing a dynamic index into a byte with 8 flags in it, for example because you have 8 on-screen enemy slots with indexes from 0 to 7, and need to access one of them.
 

LDY CurrentEnemy	; Let's say this is enemy index 3, so Y is now = #3
LDA EnemyFlags		; A byte with 8 flags, one for each enemy. We are looking for bit 3, but our code doesn't know that so we can't use a constant
AND MaskLUT, Y		; This will AND the byte with %00001000 (8) to check for the flag state in index #3
BEQ WhateverBranch	; Branches based on enemy 3's flag

 

Edited by Sumez
Link to comment
Share on other sites

2 hours ago, treed said:

As far as I can tell, it's identical in cycles and bytes, so is it just a matter of taste or is there a drawback there? I guess the only benefit I see with using BIT over AND is that A is left alone, in case you wanted to use the same bitmask right away.

Though of course BIT has other usages, that's the primary reason to use BIT over AND when you are only interested in the Zero flag. Though in your example it would probably make more sense to load "buttons1" into A and then BIT with the bitmask, allowing you to use the button states again, immediately after your branch.

  • Like 1
Link to comment
Share on other sites

9 hours ago, treed said:

Although I wonder why I haven't seen other people using BIT for this:

Never thought to use BIT. Being able to BIT through multiple states in a row on the same button state without additional LDAs would be helpful. 

 

7 hours ago, Sumez said:

and of course "MaskLUT+3" doesn't make any sense

You are right. Brain fart. Of course MaskLUT, y would be to index. I somehow understood deafcode's example and then proceeded to act like I didn't know how to index a table. lol. Thank you for the example. That definitely shows the advantage of a table vs using the bits for different states. 

 

Link to comment
Share on other sites

12 hours ago, Sumez said:

Though of course BIT has other usages, that's the primary reason to use BIT over AND when you are only interested in the Zero flag. Though in your example it would probably make more sense to load "buttons1" into A and then BIT with the bitmask, allowing you to use the button states again, immediately after your branch.

Yeah, I originally thought about that, but BIT has no immediate mode. It's only got ZP and Absolute. 😞

Link to comment
Share on other sites

6 hours ago, TylerBarnes said:

You are right. Brain fart. Of course MaskLUT, y would be to index. I somehow understood deafcode's example and then proceeded to act like I didn't know how to index a table. lol. Thank you for the example. That definitely shows the advantage of a table vs using the bits for different states. 

Ah, sorry. It's been a little while since I've programmed 6502, so I forgot what the indexing syntax was. 

Link to comment
Share on other sites

On 12/8/2019 at 11:58 AM, Sumez said:

X/Y indexing is the bread and butter of the 6502 along with quick zeropage access 😄 

It's not very C friendly, but it's very effective when your code and data structure is optimized towards it.

Very true; Indexing makes many things soooo much easier. My favorite thing to do for code re usability is add pointer support to functions that could be used for multiple tables. Lets say you have different palettes for different levels. 
 

;---------------Variables-----------
data = $C2
dataH = $C3
;----------------Setup--------------

LoadLevel1Palettes: 
	LDA #<Lev1Pal               ; Load Level1 Palette's  
	LDX #>Lev1Pal               ; location into a pointer.
	STA data                    
	STX dataH
	JSR LoadPalette             ; Run the load palette routine
	
Forever:
	JMP Forever

;---------------Subroutines----------

LoadPalette: 
	LDA #$3F                     ; Load PPU location to write
	LDY #$00                     ; $3F00 is for Palettes
	STA $2006                     
	STY $2006                    
PalLoop:              
	LDA (data), y                ; Index into loacation in Pointer (data)
	STA $2007                    ; Write Byte
	INY                          ; Incriment Index
	CPY #$20                     ; Takes 32 writes to complete 
	BNE PalLoop                  ; Keeps looping till done
	RTS
;----------------Data----------------

Lev1Pal: 
	.incbin "Level1.pal"

 

Link to comment
Share on other sites

My answer:  I've definitely used it. When to use it is a tradeoff of how often you access something vs the space required for it.  For a flag that gets used multiple times in a frame in a loop, and isn't part of a giant array of flags, I'd just waste a byte for it.  It's so much faster, and what's 1 byte of out 2K?  Example: In my current game Halcyon, there's a flag for whether the player is in the tank or out of it. I just waste a byte. No point in packing that for something that simple.

But for something that's a big array, particularly if it's not accessed often, then bit-packing is the way to go. In Halcyon, for storing where a player has been (for the minimap), I use a bit per screen. And a bit per item picked up in the global game status. It's a little slower, but for storing that much state, it would be really wasteful to have a byte per item.

 

 

  • Thanks 1
Link to comment
Share on other sites

On 12/9/2019 at 3:07 PM, TylerBarnes said:

I see, that definitely makes a lot of sense. When the space saving starts to impede on performance of a commonly checked bit, it starts become more of a con than a pro. 

Oh, and performance isn't the only tradeoff -- the other is in readability/maintainability. If you're skimming through your code 6 months later, trying to figure out what's going, simpler is better.

My mantra:  I ALWAYS optimize my code.  But the first optimization priority is for readability.  Only when performance or space becomes an issue do you shift and optimize for one of them instead.

 

Link to comment
Share on other sites


×
×
  • Create New...