Jump to content
IGNORED

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


Random Terrain

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.

Link to comment
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).

Link to comment
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).

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
Link to comment
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
Link to comment
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.

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.

Link to comment
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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

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
Link to comment
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.

Link to comment
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.

 

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.randomterrain.com/atari-2600-me...mmands.html#bit

Link to comment
Share on other sites

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.randomterrain.com/atari-2600-me...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).

Link to comment
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.

Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...