1

# Nybble me this, Batman. How do you treat one variable like it's two?

17 replies to this topic

### #1 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Sat Aug 16, 2008 4:15 AM

I know we can use bit operations to basically turn a variable into 8 very simple variables, but this is something different.

How do I use one variable like it is two separate variables where I can go as high as 15 in both halves? I want to use one variable as two counters, but how do I change and check the high nybble and low nybble separately?

Thanks.

### #2 Nukey ShayOFFLINE

Nukey Shay

Sheik Yerbouti

• 20,782 posts
• Location:The land of Gorch

Posted Sat Aug 16, 2008 6:21 AM

Just use inline asm within bBasic (it has that function, right?).

To update either half, you'll need to clear the bits you are working with (via AND #\$F0 or AND #\$0F for the low or high nybbles respectively), store the value temporarily, fetch or calculate your updated value, then ORA the temp back in before the final store.

To check either half, just perform an AND in reverse order than as shown above.  If you are using the upper nybble for a table index, you might want to drop it down into the low nybble's position by using 4 LSR's (implied mode).

### #3 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Sat Aug 16, 2008 6:30 AM

Nukey Shay, on Sat Aug 16, 2008 7:21 AM, said:

Just use inline asm within bBasic (it has that function, right?).

To update either half, you'll need to clear the bits you are working with (via AND #\$F0 or AND #\$0F for the low or high nybbles respectively), store the value temporarily, fetch or calculate your updated value, then ORA the temp back in before the final store.

To check either half, just perform an AND in reverse order than as shown above.  If you are using the upper nybble for a table index, you might want to drop it down into the low nybble's position by using 4 LSR's (implied mode).
Thanks. Yep, you can use inline asm with bB, but I have limited experience with it. Seems like nybbling is more trouble than it's worth.

Edited by Random Terrain, Sat Aug 16, 2008 7:52 AM.

### #4 Nukey ShayOFFLINE

Nukey Shay

Sheik Yerbouti

• 20,782 posts
• Location:The land of Gorch

Posted Sat Aug 16, 2008 7:14 AM

It just looks complicated.  It becomes quite easy when you get used to it.  The difficult part is keeping track of which ones are combined (to know when to preserve bits).  If one of the nybbles is to be a counter that's bumped after certian conditions, you can easily use the high nybble for that without worrying about preservation of the low (which wouldn't change so long as the carry flag is properly cleared or set)...
LDA merged_variable
CLC
ADC #\$10 ;change upper nybble only
STA merged_variable

The other way can be more complicated...
LDA merged_variable
PHA ;save original value for later
CLC
ADC #\$01 ;upper nybble may be affected
AND #\$0F ;so do this to ignore those bits
STA merged_variable ;and save temporarily
PLA ;get back original value
AND #\$F0 ;keep only the upper nybble
ORA merged_variable ;mix with the updated low
STA merged_variable

Whether or not it's worth it depends on your program's requirements.  Merging (or reusing) variables is the only way to get more of them...short of using the Superchip option.

Edited by Nukey Shay, Sat Aug 16, 2008 7:15 AM.

### #5 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Sat Aug 16, 2008 8:32 PM

Nukey Shay, on Sat Aug 16, 2008 8:14 AM, said:

It just looks complicated.  It becomes quite easy when you get used to it.  The difficult part is keeping track of which ones are combined (to know when to preserve bits).  If one of the nybbles is to be a counter that's bumped after certian conditions, you can easily use the high nybble for that without worrying about preservation of the low (which wouldn't change so long as the carry flag is properly cleared or set)...
LDA merged_variable
CLC
ADC #\$10 ;change upper nybble only
STA merged_variable

The other way can be more complicated...
LDA merged_variable
PHA ;save original value for later
CLC
ADC #\$01 ;upper nybble may be affected
AND #\$0F ;so do this to ignore those bits
STA merged_variable ;and save temporarily
PLA ;get back original value
AND #\$F0 ;keep only the upper nybble
ORA merged_variable ;mix with the updated low
STA merged_variable

Whether or not it's worth it depends on your program's requirements.  Merging (or reusing) variables is the only way to get more of them...short of using the Superchip option.
Thanks. Too bad bB can't do strings because I could use 1 through 9 and 10 through 90 and read the number by converting it to a string and just look at the ones or tens place.

### #6 Nukey ShayOFFLINE

Nukey Shay

Sheik Yerbouti

• 20,782 posts
• Location:The land of Gorch

Posted Sat Aug 16, 2008 9:31 PM

That would also imply that both halves of the byte are being adjusted in decimal (BCD) mode.  And it doesn't seem any quicker than using AND #\$0F or AND #\$F0 just to read the status of the lower or upper halves respectively.  Bit preservation only comes into play when a value needs adjusting (which is true regardless of mode).

Also, it should be mentioned that asm doesn't care how the byte is split up.  You could, for example, use the 2 high bits for quick flags of single-bit variables (to be branched via BPL/BMI or BVC/BVS respectively), while the lower 6 bits are used for a counter.

### #7 SeaGtGruffOFFLINE

SeaGtGruff

River Patroller

• 4,993 posts
• Location:Georgia, USA

Posted Sun Aug 17, 2008 1:17 AM

Random Terrain, on Sat Aug 16, 2008 6:15 AM, said:

I know we can use bit operations to basically turn a variable into 8 very simple variables, but this is something different.

How do I use one variable like it is two separate variables where I can go as high as 15 in both halves? I want to use one variable as two counters, but how do I change and check the high nybble and low nybble separately?

Thanks.
Here's an example that uses the "//" modulo division. The remainder is returned in temp1, but the bits are in reverse order, so we can use "//" again to reverse them back into the correct order.

```   rem * Nybble me this, Batman!
rem * Variable "a" will be used to store two nybble values.

rem * You need to include div_mul.asm for this.
include div_mul.asm

rem * Store the two values in "b" and "c," just for now.
b = 5
c = 10

rem * Use multiplication and addition to set "a."
rem * The "b" value will go in the high nybble,
rem * and the "c" value will go in the low nybble.
a = 16 * b + c

rem * Now clear "b" and "c."
b = 0
c = 0

rem * Here's how to retrieve the two nybbles:
b = a // 16
c = temp1 // 16
c = temp1
rem * There's a bug in the "//" routine that causes the
rem * bits of remainder temp1 to be in reverse order,
rem * so that's why we do "//" again on temp1.

rem * Now let's use the score to display them:
score = 0
if b > 0 then for i = 1 to b : score = score + 1000 : next
if c > 0 then for i = 1 to c : score = score + 1 : next

COLUBK = \$00
scorecolor = \$1A

loop_1

drawscreen

if !joy0fire then loop_1

rem * Here's how to change just the high nybble (to 3):
a = a & %00001111
a = a | 16 * 3

rem * Here's how to change just the low nybble (to 6):
a = a & %11110000
a = a | 6

rem * Now get and display the new values:
b = a // 16
c = temp1 // 16
c = temp1

score = 0
if b > 0 then for i = 1 to b : score = score + 1000 : next
if c > 0 then for i = 1 to c : score = score + 1 : next

loop_2

drawscreen

goto loop_2```
Michael

### #8 SeaGtGruffOFFLINE

SeaGtGruff

River Patroller

• 4,993 posts
• Location:Georgia, USA

Posted Sun Aug 17, 2008 1:20 AM

Here's a simpler example that doesn't use the modulo division:

```   rem * Nybble me this, Batman!
rem * Variable "a" will be used to store two nybble values.

rem * You need to include div_mul.asm for this.
include div_mul.asm

rem * Store the two values in "b" and "c," just for now.
b = 5
c = 10

rem * Use multiplication and addition to set "a."
rem * The "b" value will go in the high nybble,
rem * and the "c" value will go in the low nybble.
a = 16 * b + c

rem * Now clear "b" and "c."
b = 0
c = 0

rem * Here's how to retrieve the two nybbles:
b = a / 16
c = a & %00001111

rem * Now let's use the score to display them:
score = 0
if b > 0 then for i = 1 to b : score = score + 1000 : next
if c > 0 then for i = 1 to c : score = score + 1 : next

COLUBK = \$00
scorecolor = \$1A

loop_1

drawscreen

if !joy0fire then loop_1

rem * Here's how to change just the high nybble (to 3):
a = a & %00001111
a = a | 16 * 3

rem * Here's how to change just the low nybble (to 6):
a = a & %11110000
a = a | 6

rem * Now get and display the new values:
b = a / 16
c = a & %00001111

score = 0
if b > 0 then for i = 1 to b : score = score + 1000 : next
if c > 0 then for i = 1 to c : score = score + 1 : next

loop_2

drawscreen

goto loop_2```
Michael

### #9 Nukey ShayOFFLINE

Nukey Shay

Sheik Yerbouti

• 20,782 posts
• Location:The land of Gorch

Posted Sun Aug 17, 2008 1:49 AM

Much thanks for the bB equalivants.  My head's stuck in .asm, so I never had time to learn it...but it looks as if it should work for smaller breakdowns as well.

But wouldn't the use of variables (b) and © to calculate (a) end up causing bB to reserve 8-bits of ram for those as well...potentially 16 bits of ram wasted...unless these variables are used for other things elsewhere?  If so, it might be better to just imbed some inline asm that works with (a) directly without the overhead.

### #10 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Sun Aug 17, 2008 2:18 AM

SeaGtGruff, on Sun Aug 17, 2008 2:20 AM, said:

Here's a simpler example that doesn't use the modulo division:
Thanks. That seems like an easier to understand solution that doesn't require asm. Looks like I can replace b and c with a couple of temporary variables such as temp1 and temp2.

Nukey Shay, on Sun Aug 17, 2008 2:49 AM, said:

But wouldn't the use of variables (b) and © to calculate (a) end up causing bB to reserve 8-bits of ram for those as well...potentially 16 bits of ram wasted...unless these variables are used for other things elsewhere?  If so, it might be better to just imbed some inline asm that works with (a) directly without the overhead
I already use temporary variables for little jobs where the values don't need to be remembered, so they can be used instead of b and c.

I knew one of you guys could figure out a fairly simple way to do this. It seems I can only get halfway there at best before my brain melts and leaks out of my ears.

### #11 batariOFFLINE

batari

)66]U('=I;B\$*

• 6,341 posts
• begin 644 contest

Posted Sun Aug 17, 2008 2:27 AM

This question was asked a while back, and here is some more sample code (I never tested it, so I can't make any promises.)

http://www.atariage....howtopic=113003

### #12 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Sun Aug 17, 2008 4:00 AM

SeaGtGruff, on Sun Aug 17, 2008 2:20 AM, said:

Here's a simpler example that doesn't use the modulo division:
I have adapted your code to work with what I already had and it seems to work. I'll show the different parts to make sure I'm not messing something up.

This adds 1 to both high and low nybble counters:
`   counter01 = counter01 + 17`

-------------------------------------------------------------------

This checks the high nybble counter and skips the sprite animation if it's not time yet:
```   temp1 = counter01 / 16
if temp1<animtrigger then goto skipanim```

If it's time for the sprite animation to change, the following clears the high nybble counter:
`   counter01= counter01 & %00001111`

-------------------------------------------------------------------

This checks the low nybble counter and skips background color flipping and scrolling if it's not time yet:
```   temp1=counter01 & %00001111
if temp1<speed_current then goto skip_all```

If it's time for background color flipping and scrolling, the following clears the low nybble counter:
`   counter01 = counter01 & %11110000`

So far it doesn't look like I messed anything up and it seems to do exactly what I want. If there isn't a problem, I'll see how many other variables I can split in half. Maybe now I'll have enough variables to do everything I want.

Thanks again.

Edited by Random Terrain, Sun Aug 17, 2008 4:01 AM.

### #13 SeaGtGruffOFFLINE

SeaGtGruff

River Patroller

• 4,993 posts
• Location:Georgia, USA

Posted Sun Aug 17, 2008 12:11 PM

Nukey Shay, on Sun Aug 17, 2008 2:49 AM, said:

But wouldn't the use of variables (b) and ( c ) to calculate (a) end up causing bB to reserve 8-bits of ram for those as well...potentially 16 bits of ram wasted...unless these variables are used for other things elsewhere?
Yes. I only used them because I was too lazy to look up which temp variables to stay away from! The best choice would be to use two temp variables, but bB uses the temp variables for its own purposes, and some are used in the mathematical operations. I just checked, and temp1 and temp2 are used in the "mul8" routine, but none of the temp variables are used when dividing by 16 or doing bit operations, so they should be safe to use.

The modulo division example is kind of silly. When I saw "//" described in the batari Basic help manual, and that it returns the remainder in temp1, I thought that would be ideal:

```   temp2 = a // 16 : rem * to get the high nybble in temp2
rem * and now temp1 already contains the low nybble```
But when I displayed the results in the score, the low nybble was wrong. And when I checked the compiled assembly code, I realized that the LSR and ROL instructions were moving the remainder into temp1 with the bits in reverse order. I think the generic "//" operation uses a routine in the div_mul16.asm include file, but if the bB compiler sees "// 16" (or 2, 4, 8, 16, 32, 64, or 128) it apparently just uses LSR for the division, and ROL for the remainder. The LSR is okay, but I think it would be easier/better to put the remainder in temp1 using AND:

```; current buggy code:
;  b  =  a  //  16
LDA   a
ldx   #0
stx   temp1
lsr
rol   temp1
lsr
rol   temp1
lsr
rol   temp1
lsr
rol   temp1
STA   b```
```; suggested replacement code:
;  b  =  a  //  16
LDA   a
STA   temp1
AND   %00001111
lsr
lsr
lsr
lsr
STA   b```
The bB compiler would fill the section from "AND %00001111" through the last "LSR" with the appropriate bit mask and number of LSRs as determined by the power of 2 after the "//" operand. If this gets fixed in the next version of bB, then the "//" operation would be the simplest method, as shown in my first example above ("temp2 = a // 16" which would set temp1 to the low nybble automatically).

Michael

Edited by SeaGtGruff, Sun Aug 17, 2008 12:12 PM.

### #14 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Thu Aug 21, 2008 5:41 AM

This is pretty cool. So far in the game I'm working on, I have 4 double variables and one that I'm using as 8 simple off/on variables, so now 5 variables have been turned into 16. Getting more out of these variables is really fun and makes me feel confident that I won't run out of variables before the game is done.

Thanks again for the help.

### #15 CurtisPOFFLINE

CurtisP

Chopper Commander

• 211 posts

Posted Thu Aug 21, 2008 10:46 AM

Random Terrain, on Thu Aug 21, 2008 6:41 AM, said:

This is pretty cool. So far in the game I'm working on, I have 4 double variables and one that I'm using as 8 simple off/on variables, so now 5 variables have been turned into 16. Getting more out of these variables is really fun and makes me feel confident that I won't run out of variables before the game is done.

Thanks again for the help.

If you are doing simple on/off, yes/no, true/false, then you should be using single bits. This would give you 8 variables per actual variable.

http://www.randomter...mmands.html#bit

### #16 Random TerrainOFFLINE

Random Terrain

Visual batari Basic User

• 22,680 posts
• Controlled Randomness
Replay Value
Nonlinear
• Location:North Carolina (USA)

Posted Thu Aug 21, 2008 11:50 AM

CurtisP, on Thu Aug 21, 2008 11:46 AM, said:

If you are doing simple on/off, yes/no, true/false, then you should be using single bits. This would give you 8 variables per actual variable.

http://www.randomter...mmands.html#bit
Yep, that's what I'm doing with one variable so far. I have 1 variable acting like 8 variables and 4 other variables are split in half which means 5 variables are acting like 16 (2 + 2 +2 +2 + 8 = 16).

### #17 ArtloverOFFLINE

Artlover

Super Amazing Guy

• 6,354 posts
• Location:Miami, FL

Posted Thu Aug 21, 2008 12:11 PM

Oh, this brings back memories of doing audio digitizing on the C64. Since the volume was only 0-15, only needed 4 bit samples, so two samples could be stored per byte. Really came in handy when doing stereo digi's, where each byte held the left & right samples. Of course in ASM, it was super ultra easy with the shift & rotate opcodes.

<Artlover> needs to dig his C-64 out again one of these days.

### #18 CurtisPOFFLINE

CurtisP

Chopper Commander

• 211 posts

Posted Wed Aug 27, 2008 5:47 PM

Artlover, on Thu Aug 21, 2008 2:11 PM, said:

Of course in ASM, it was super ultra easy with the shift & rotate opcodes.

Keep in mind that in Batari Basic, when you multiply or divide by a constant that is a power of two, then compiled code just does shifts, so it's really the same thing.

#### 0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users