Jump to content
Random Terrain

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

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
[quote name='Nukey Shay' post='1570968' date='Sat Aug 16, 2008 7: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).[/quote]
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
[quote name='Nukey Shay' post='1570978' date='Sat Aug 16, 2008 8: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.[/quote]
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='Random Terrain' post='1570948' date='Sat Aug 16, 2008 6: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.[/quote]
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.

[code] 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[/code]
Michael

Share this post


Link to post
Share on other sites
Here's a simpler example that doesn't use the modulo division:

[code] 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[/code]
Michael

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='SeaGtGruff' post='1571406' date='Sun Aug 17, 2008 2:20 AM']Here's a simpler example that doesn't use the modulo division:[/quote]
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.


[quote name='Nukey Shay' post='1571410' date='Sun Aug 17, 2008 2:49 AM']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[/quote]
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.

Share this post


Link to post
Share on other sites
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.)

[url="http://www.atariage.com/forums/index.php?showtopic=113003"]http://www.atariage.com/forums/index.php?showtopic=113003[/url]

Share this post


Link to post
Share on other sites
[quote name='SeaGtGruff' post='1571406' date='Sun Aug 17, 2008 2:20 AM']Here's a simpler example that doesn't use the modulo division:[/quote]
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:
[code] counter01 = counter01 + 17[/code]

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


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


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

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


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


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


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

Share this post


Link to post
Share on other sites
[quote name='Nukey Shay' post='1571410' date='Sun Aug 17, 2008 2:49 AM']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?[/quote]
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:

[code] temp2 = a // 16 : rem * to get the high nybble in temp2
rem * and now temp1 already contains the low nybble[/code]
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:

[code]; 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[/code]
[code]; suggested replacement code:
; b = a // 16
LDA a
STA temp1
AND %00001111
lsr
lsr
lsr
lsr
STA b[/code]
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='Random Terrain' post='1573799' date='Thu Aug 21, 2008 6: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.[/quote]

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.

[url="http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#bit"]http://www.randomterrain.com/atari-2600-me...mmands.html#bit[/url]

Share this post


Link to post
Share on other sites
[quote name='CurtisP' post='1573907' date='Thu Aug 21, 2008 11:46 AM']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.

[url="http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#bit"]http://www.randomterrain.com/atari-2600-me...mmands.html#bit[/url][/quote]
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).

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='Artlover' post='1573951' date='Thu Aug 21, 2008 2:11 PM']Of course in ASM, it was super ultra easy with the shift & rotate opcodes.[/quote]

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.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...