Jump to content
IGNORED

Need help with 8.8 fractions


Random Terrain

Recommended Posts

The bB page says that we can access the fractional portion directly, but the fractional portion will be multiplied by 256.

 

Multiplied by 256? How would you deal with that? Divide by 256? Is that even possible in bB?

 

I'm trying to get the fractional part into the score using _sc3. For example, if I'm using "dim _P0_Speed = j.k" and start out with "_P0_Speed = 1.52" I'd have to move the 1 to the left in _sc2 have A in the second spot for the period ($1A) and _sc3 should contain 52. I should see 1.52 in the score. I don't know how to do that.

Link to comment
Share on other sites

5 hours ago, Random Terrain said:

The bB page says that we can access the fractional portion directly, but the fractional portion will be multiplied by 256.

 

Multiplied by 256? How would you deal with that? Divide by 256? Is that even possible in bB?

 

I'm trying to get the fractional part into the score using _sc3. For example, if I'm using "dim _P0_Speed = j.k" and start out with "_P0_Speed = 1.52" I'd have to move the 1 to the left in _sc2 have A in the second spot for the period ($1A) and _sc3 should contain 52. I should see 1.52 in the score. I don't know how to do that.

Fractional numbers in fixed-point math work pretty much like integers at the binary level: each bit position represents a value, and if you add all the values for the bits that are set you get the final value.

 

The bits in an integer, from high to low, are worth 128, 64, 32, 16, 8, 4, 2 and 1, and with combinations of those you can represent numbers from 0 to 255. The fractional part is just the continuation of that: from high to low, the bits are worth 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 and 1/256. To represent the number 0.75, for example, you need the bits that are worth 0.5 and 0.25, so that's 11000000 in binary.

 

The thing about the numbers being multiplied by 256 is that if you look at it as if it was an integer, the value 1/256 is 00000001 in binary, the same as the integer decimal 1.

 

You may have noticed that since there are only 8 specific fractions that can be added together to form values, not all decimal numbers can be represented precisely. Your example (.52) is one of them. 0.52 times 256 is 133.12 but with 8 bits you can only represent the 133 part, the .12 part will be truncated. So the closest you can get to 0.52 is 133 / 256, which is 0.51953125. The next one you can do is 144 / 256, which is 0.5234375. As you can see, you can get close, but not quite reach certain decimal numbers. But if you're going from binary to decimal, and not the other way around, this shouldn't be a problem.

 

As for displaying fractional numbers, that's actually something I've never done myself. The fastest method would obviously be a look-up table, where each input byte would map to an output byte containing 2 BCD digits, so the table would only be 256 bytes long. I know that in Atari 2600 land this is a huge amount of space, but if this is an important part of your program it might be worth it.

Edited by tokumaru
  • Like 1
Link to comment
Share on other sites

Another option for displaying the number would be to multiply it by 100 so you get 2 integer digits out of it, discard the fractional part and display the integer part as you would any other integer, doing a binary to decimal conversion. For example, say that the fractional part is $86 (134). Multiply that by 100 and you get $3458 (13,400). Get rid of the fractional part and you're left with $34 (52). Which is correct, since 134 / 256 is 0.5234375.

  • Like 1
Link to comment
Share on other sites

30 minutes ago, tokumaru said:

You may have noticed that since there are only 8 specific fractions that can be added together to form values, not all decimal numbers can be represented precisely. Your example (.52) is one of them. 0.52 times 256 is 133.12 but with 8 bits you can only represent the 133 part, the .12 part will be truncated. So the closest you can get to 0.52 is 133 / 256, which is 0.51953125. The next one you can do is 144 / 256, which is 0.5234375. As you can see, you can get close, but not quite reach certain decimal numbers. But if you're going from binary to decimal, and not the other way around, this shouldn't be a problem.

 

Thanks. That seems like a list of numbers that I should have on the bB page. I'm extremely bad at math. How would I figure out that list of precise numbers?

Link to comment
Share on other sites

After looking at that again, I'm guessing that I'd just multiply each number from 0.01 to 0.99 by 256 and divide that number by 256. Example:

 

.01 * 256 = 2.56 : 2 / 256 = 0.00

.02 * 256 = 5.12 : 5 / 256 = 0.01

.03 * 256 = 7.68 : 7 / 256 = 0.02

.04 * 256 = 10.24 : 10 / 256 = 0.03

.05 * 256 = 12.8 : 12 / 256 = 0.04

.06 * 256 = 15.36 : 15 / 256 = 0.05

.07 * 256 = 17.92 : 17 / 256 = 0.06

.08 * 256 = 20.48 : 20 / 256 = 0.07

.09 * 256 = 23.04 : 23 / 256 = 0.08

.10 * 256 = 25.6 : 25 / 256 = 0.09

.11 * 256 = 28.16 : 28 / 256 = 0.10

.12 * 256 = 30.72 : 30 / 256 = 0.11

.13 * 256 = 33.28 : 33 / 256 = 0.12

.14 * 256 = 35.84 : 35 / 256 = 0.13

.15 * 256 = 38.4 : 38 / 256 = 0.14

.16 * 256 = 40.96 : 40 / 256 = 0.15

.17 * 256 = 43.52 : 43 / 256 = 0.16

.18 * 256 = 46.08 : 46 / 256 = 0.17

.19 * 256 = 48.64 : 48 / 256 = 0.18

.20 * 256 = 51.2 : 51 / 256 = 0.19

.21 * 256 = 53.76 : 53 / 256 = 0.20

 

. . .

 

.52 * 256 = 133.12 : 133 / 256 = 0.51

.53 * 256 = 135.68 : 135 / 256 = 0.52

.54 * 256 = 138.24 : 138 / 256 = 0.53

 

So far it looks like it's just one number less than the original each time.

Link to comment
Share on other sites

18 minutes ago, Random Terrain said:

 

Thanks. That seems like a list of numbers that I should have on the bB page. I'm extremely bad at math. How would I figure out that list of precise numbers?

The fractional numbers that you can represent precisely using 8 bits are those that are multiples of the smallest number you can represent, which is 1 / 256 = 0.00390625. In other words, you can do numbers from 0 to 0.99609375 (which is 255/256) in steps of 0.00390625 (which is 1/256).

  • Like 1
Link to comment
Share on other sites

Oh, so you do want to go from decimal to binary! The user can pick anything between .00 and .99 for the decimal part and you have to convert that to binary to do the physics calculations, is that it?

 

I don't have any experience with bB, so I don't know how exactly the source number is stored (is it BCD? 1 byte per digit?), but the math would go something like this (with an example input of 2.52)

 

1- Convert the fractional input from decimal to binary (52 -> $34) and save this as the integer part of an 8.8 number.

2- Divide the 8.8 number by 100, so the integer part becomes a fraction and the integer part becomes 0 ($3400 / 100 = $0085).

3- Convert the integer input from decimal to binary (2 -> $02) and use this as the integer part of the 8.8 number.

 

The final result is $0285 (with bB using it as $02.$85). In decimal, $0285 is 645, which divided by 256 is 2.51953125. Pretty close to the 2.52 input by the user, as close as can be in binary, and probably precise enough for any math you need to do.

 

The trickiest part of this conversion is the division by 100. I don't know if bB has built-in division or if you'd need to write your own dedicated subroutine. The faster alternative, again, would be to use a look-up table to directly convert all possible inputs (0 to 99) into their fractional binary equivalents:

 

00 * 256 / 100 = 0 -> $00

01 * 256 / 100 = 2.56 -> $02

02 * 256 / 100 = 5.12 -> $05

03 * 256 / 100 = 7.68 -> $07

04 * 256 / 100 = 10.24 -> $0A

05 * 256 / 100 = 12.8 -> $0C

(...)

98 * 256 / 100 = 250.88 -> $FA

99 * 256 / 100 = 253.44 -> $FD

 

You may opt to round the results to the nearest integer instead of down (e.g. so that 7.68 becomes $08 instead of $07), but I don't think this will affect the results much.

  • Like 1
Link to comment
Share on other sites

10 hours ago, Random Terrain said:

So far it looks like it's just one number less than the original each time.

It's one less because you're rounding them down for display, but before rounding, during physics calculations, the numbers are closer to the ideal values.

 

You said that the user inputs fractional numbers that you then need to encode to binary. Due to lack of precision and rounding errors, the values will indeed be slightly off if you try to convert them back, so this kind of round trip should be avoided.

  • Like 1
Link to comment
Share on other sites

2 hours ago, tokumaru said:

Oh, so you do want to go from decimal to binary! The user can pick anything between .00 and .99 for the decimal part and you have to convert that to binary to do the physics calculations, is that it?

 

I don't have any experience with bB, so I don't know how exactly the source number is stored (is it BCD? 1 byte per digit?), but the math would go something like this (with an example input of 2.52)

 

1- Convert the fractional input from decimal to binary (52 -> $34) and save this as the integer part of an 8.8 number.

2- Divide the 8.8 number by 100, so the integer part becomes a fraction and the integer part becomes 0 ($3400 / 100 = $0085).

3- Convert the integer input from decimal to binary (2 -> $02) and use this as the integer part of the 8.8 number.

 

The final result is $0285 (with bB using it as $02.$85). In decimal, $0285 is 645, which divided by 256 is 2.51953125. Pretty close to the 2.52 input by the user, as close as can be in binary, and probably precise enough for any math you need to do.

 

The trickiest part of this conversion is the division by 100. I don't know if bB has built-in division or if you'd need to write your own dedicated subroutine. The faster alternative, again, would be to use a look-up table to directly convert all possible inputs (0 to 99) into their fractional binary equivalents:

 

00 * 256 / 100 = 0 -> $00

01 * 256 / 100 = 2.56 -> $02

02 * 256 / 100 = 5.12 -> $05

03 * 256 / 100 = 7.68 -> $07

04 * 256 / 100 = 10.24 -> $0A

05 * 256 / 100 = 12.8 -> $0C

(...)

98 * 256 / 100 = 250.88 -> $FA

99 * 256 / 100 = 253.44 -> $FD

 

You may opt to round the results to the nearest integer instead of down (e.g. so that 7.68 becomes $08 instead of $07), but I don't think this will affect the results much.

 

 

Thanks. I'm making a look-up table now and I'll see how that works once it's finished. I started it by not rounding up, but when a number is something like 17.92, it's kind of stupid of me not to, so I'm going back and rounding up anything I haven't so far.

  • Like 1
Link to comment
Share on other sites

I made the table, but can't get it to work:

 

   data _Data_Num_Conversion
   $00, $02, $05, $07, $0A, $0D, $0F, $12, $14, $17
   $1A, $1C, $1E, $21, $24, $26, $29, $2B, $2E, $31
   $33, $36, $38, $3B, $3D, $40, $42, $45, $48, $4A
   $4D, $4F, $52, $54, $57, $59, $5C, $5F, $61, $64
   $66, $69, $6B, $6E, $71, $73, $76, $78, $7B, $7D
   $80, $82, $85, $88, $8A, $8D, $8F, $92, $94, $97
   $99, $9C, $9F, $A1, $A4, $A6, $A9, $AB, $AE, $B1
   $B3, $B6, $B8, $BB, $BD, $C0, $C3, $C5, $C7, $CA
   $CC, $CF, $D2, $D4, $D7, $D9, $DC, $DF, $E1, $E4
   $E6, $E9, $EB, $EE, $F1, $F3, $F6, $F8, $FB, $FD
end

 

I'll play with it later today after I get some sleep and try to figure out what I'm doing wrong.

Link to comment
Share on other sites

I think you should consider, er.. something I think you were considering before: don't think/use fractions at all.  Think of each two digits of the score as separate hexadecimal numbers

 

 dim scr0 = score
 dim scr1 = score+1
 dim scr2 = score+2

 

Not a complete idea I know.  But, if you think of the score as *just* the display side of things that could free you to work out the math side on your own terms.

Link to comment
Share on other sites

4 hours ago, Random Terrain said:

I made the table, but can't get it to work:

 


   data _Data_Num_Conversion
   $00, $02, $05, $07, $0A, $0D, $0F, $12, $14, $17
   $1A, $1C, $1E, $21, $24, $26, $29, $2B, $2E, $31
   $33, $36, $38, $3B, $3D, $40, $42, $45, $48, $4A
   $4D, $4F, $52, $54, $57, $59, $5C, $5F, $61, $64
   $66, $69, $6B, $6E, $71, $73, $76, $78, $7B, $7D
   $80, $82, $85, $88, $8A, $8D, $8F, $92, $94, $97
   $99, $9C, $9F, $A1, $A4, $A6, $A9, $AB, $AE, $B1
   $B3, $B6, $B8, $BB, $BD, $C0, $C3, $C5, $C7, $CA
   $CC, $CF, $D2, $D4, $D7, $D9, $DC, $DF, $E1, $E4
   $E6, $E9, $EB, $EE, $F1, $F3, $F6, $F8, $FB, $FD
end

 

I'll play with it later today after I get some sleep and try to figure out what I'm doing wrong.

Time to do some debugging, I guess... can you trace through the program in a dubugger to see when exactly things don't go as you expect them to?

 

I noticed that your table goes straight from the value for 9 to the value for 10, so I assume that the original number is not in BCD format... How are you calculating that (the look-up table index, I mean)?

 

EDIT: If the input digits are separate, you'd need to do tens * 10 + ones in order to calculate the index to use with the look-up table. For example, if the user selects 2.67, and you have the values 2, 6 and 7 in memory, you have to do 6 * 10 + 7 to calculate the index. If the multiplication is an issue, another small table can be used to multiply values 0 through 9. If you did it like this, preparing the fractional part in assembly would be super easy:

  ldx tens
  lda times10, x
  clc
  adc ones
  tax
  lda fractions, x
  sta speedlow ;this is the bottom of the 8.8 

Sorry if I can't do the bB equivalent from the top of my head, but if you show me what you have, maybe I can help.

Edited by tokumaru
Link to comment
Share on other sites

22 minutes ago, bogax said:

not sure I'm following all this but

the 0..99 score will be in BCD

If that's the case, then one of two things must happen:

 

1- The fractions look-up table needs to have "holes" in it, for all $xA-$xF values, which are skipped in BCD.

 

OR

 

2- The BCD value has to be converted to binary before it can be used as an index into the look-up table: binary = (bcd & $f0) / 16 * 10 + (bcd & $0f)

Edited by tokumaru
  • Like 1
Link to comment
Share on other sites

It occurs to me that you might as well build the BCD index conversion into the table(s) (sort of)

using a bipartite table

I think it would be about as fast and take less ROM

 

something like

 

  temp1 = scr & $0F
  temp2 = scr / 16
  binary = lo_tbl[temp1] + hi_tbl[temp2]

    data lo_tbl
    0,   5,   3,   8,  10,  13,  15,  18,  20,  23
end

    data hi_tbl
    0,  26,  51,  77, 102, 128, 154, 179, 205, 230
end

 

Edited by bogax
typos, clarity
  • Like 1
Link to comment
Share on other sites

1 hour ago, bogax said:

It occurs to me that you might as well build the BCD index conversion into the table(s) (sort of)

using a bipartite table

I think it would be about as fast and take less ROM

 

something like

 


  temp1 = scr & $0F
  temp2 = scr / 16
  binary = lo_tbl[temp1] + hi_tbl[temp2]

    data lo_tbl
    0,   5,   3,   8,  10,  13,  15,  18,  20,  23
end

    data hi_tbl
    0,  26,  51,  77, 102, 128, 154, 179, 205, 230
end

 

 

Thanks. What does scr stand for? I like to spell things out so I won't forget what they mean. I'll also change _tbl to _Table.

Link to comment
Share on other sites

That's a really good idea! Definitely the best so far!

 

The explanation is that each digit in the BCD number has its worth: the top one is worth 0.1 and the next one is worth 0.01. So instead of converting the BCD number to a large binary number (which requires a large lookup table to to be converted into a fraction), we convert each digit of the BCD to a fraction first, according to their worth, and then we add the two fractions for the final result. It's pretty fast and uses much smaller look-up tables, since the input only goes up to 9.

 

The first table contains the binary representations of 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08 and 0.09.

 

The second table contains the binary representations of 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80 and 0.90.

 

EDIT: The scr means "score". It's the byte containing the 2 decimal digits in BCD format.

Edited by tokumaru
  • Like 1
Link to comment
Share on other sites

Thanks. I have this part working so far:

 

  temp5 = _sc3 & $0F
  temp6 = _sc3 / 16
  _P0_Right_Number = _DATA_Lo_Table[temp5] + _DATA_Hi_Table[temp6]

 

I have _sc2 (thousands and hundreds digits) fixed to show a period in the hundreds place using an "A" thanks to help in another thread:

 

https://atariage.com/forums/topic/331397-which-minikernel-lets-you-put-a-dot-period-in-the-score/?tab=comments#comment-5006382

 

I'm trying to figure out how to only increase or decrease the thousands digit when it's time and leave the period alone. I'm guessing that I'd add or subtract 16 from _sc2. I can't get that far to see if it works since even adding 1 to the score with score = score + 1 eliminates the period.

 

Update:

 

After looking at the bB page some more it seems I need to use "dec _sc3 = _sc3 + $01" instead of score = score + 1. The period doesn't go away when I do that.

 

Link to comment
Share on other sites

The number on the right side of the period is working perfectly, but when I use something like "temp4 = _sc2 & $F0" to look at the number to the left of the period (to ignore the "A" that makes the period) the sprite goes crazy.

 

Am I supposed to use a conversion table for that also?

 

Update:

 

Never mind. I wasted an extra variable and now I don't have to convert anything for the number to the left of the period.

Link to comment
Share on other sites

It seems to be working properly now.

 

Here's the .bin file to use with your favorite emulator:

 

z_bb_ex_8_8_type_speed_change_2022y_02m_22d_0429t.bin

 

Move the joystick up, down, left or right to change the speed of the current object (up/down is faster, left/right is slower).

 

To select another object, hold down the fire button and press the joystick either up or down.

 

 

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...
On 2/22/2022 at 4:57 AM, Random Terrain said:

It seems to be working properly now.

 

Here's the .bin file to use with your favorite emulator:

 

z_bb_ex_8_8_type_speed_change_2022y_02m_22d_0429t.bin 4 kB · 5 downloads

 

Move the joystick up, down, left or right to change the speed of the current object (up/down is faster, left/right is slower).

 

To select another object, hold down the fire button and press the joystick either up or down.

 

 

I was looking to try and implement a speed change in a program but was having a bit of difficulty figuring it out.  I thought it would be something easier like

 if joy0fire then g=g+1: goto SPEEDUP
 goto AFTERSPEEDUP
SPEEDUP
 if g=20 then velocity=velocity+0.55:g=0
  p0x=p0x-velocity
 m0x=m0x-velocity
 if w{2} then player6x=player6x+1
AFTERSPEEDUP
 p0x=p0x-velocity
 m0x=m0x-velocity

or something along those lines but I couldn't quite get it to work. I was playing around with and trying to get a gradual acceleration when you press fire. I saw your .bin for the 8.8 Speed Change on your site but didn't see a .bas sample program.  Were you still working on a .bas file?

 

EDIT: I switched the code I originally posted with this, using a counter that when resets increments the velocity up and it kind of works as intended.  Not as smooth as yours but still neat. Just thought I'd look at different methods to try and understand more fully.

 

Thanks!

 

Edited by KevKelley
  • Like 1
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...