Jump to content

Photo

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


17 replies to this topic

#1 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,727 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 Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,970 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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sat Aug 16, 2008 6:30 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).

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 Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,970 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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sat Aug 16, 2008 8:32 PM

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 Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,970 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 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,443 posts
  • Location:Georgia, USA

Posted Sun Aug 17, 2008 1:17 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.

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 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,443 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 Shay OFFLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,970 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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Aug 17, 2008 2:18 AM

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.


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 batari OFFLINE  

batari

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

  • 6,464 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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Aug 17, 2008 4:00 AM

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 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,443 posts
  • Location:Georgia, USA

Posted Sun Aug 17, 2008 12:11 PM

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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 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 CurtisP OFFLINE  

CurtisP

    Chopper Commander

  • 211 posts

Posted Thu Aug 21, 2008 10:46 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.


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 Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • Topic Starter
  • 24,727 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Thu Aug 21, 2008 11:50 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.

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 Artlover OFFLINE  

Artlover

    Super Amazing Guy

  • 6,354 posts
  • Bad ass mother 3000, twice as fast as your ass.
  • 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 CurtisP OFFLINE  

CurtisP

    Chopper Commander

  • 211 posts

Posted Wed Aug 27, 2008 5:47 PM

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