Jump to content
Random Terrain

Need help with 8.8 fractions

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.

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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?

Share this post


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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites
41 minutes ago, Random Terrain said:

After looking at that again, I'm guessing that I'd just multiply each number from 1 to 99 by 256 and divide that number by 256.

I'm not sure I understand what you're doing here, sorry...

Share this post


Link to post
Share on other sites

I'm probably asking the wrong question. The user is going to select the speed using the score and that should change the speed of the sprite.

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


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

Share this post


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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites
  index = score & $F0 : index = (index / 4 + index)/2 : index = (score & $0F) + index

 
edit: the order and parenthesis are important or else bB will do goofy things with the stack

Edited by bogax
goofed the code, additional info
  • Like 1

Share this post


Link to post
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

Share this post


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

Share this post


Link to post
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

Share this post


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

 

Share this post


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

Share this post


Link to post
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

Share this post


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

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.
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...