Jump to content
IGNORED

Fractional move problem


Vincehood

Recommended Posts

Hi,

this issue must be so obvious that I have been hesitating creating this topic but I will do it anyway ?!

Here are two small programs where a ball is moving from left to right. Once reaching the right side, it starts all over from the left side.

The first program (horizontal_move.bas) uses integer positioning (x) and works as expected.

The second program (horizontal_move_fract.bas) uses fractional positioning (#x) and does not work. Looks like the IF statement is never properly evaluated.

Help!?

 

 

horizontal_move.bas horizontal_move_fract.bas

Link to comment
Share on other sites

Ah, you mean fractional move, not fractal.  Totally different things! ?

 

That said, there is something screwy with the way the values are interpreted.  You see, by scaling the values (x256) some of them stand the chance of looking like negative values.  I believe that signed values is the default mode of IntyBASIC.

 

Thus, internally, the #x value is treated as if it were signed and its range is constrained to (-32,768 .. 32,767).  So, when you compare #x against (160 * 256), the latter is also interpreted as signed, and instead of +40,960, it looks like -24,576.  Because #x starts at zero it'll never be less, and the condition fails always.

 

I think IntyBASIC offers a way to force 16-bit values to be treated as signed or unsigned.  Try defining #x like this:

UNSIGNED #x = 0

 

UPDATE:  Yes, I just looked it up in the IntyBASIC manual and it has the following under "current limitations":

  • The constants 32768-65535 don't imply unsigned numbers, if these are used in a comparison with a variable, the variable should be marked as UNSIGNED.

 

To avoid this in the future, ensure that all your 16-bit variables employed for scaled values and other non-mathematical uses (e.g., bitmasks, memory addresses, etc.) are always marked as UNSIGNED.

 

UPDATE II: I tried it with that change on line #12 and it works now, although it seems rather slow. ;)

 

   -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

19 minutes ago, Vincehood said:

Oh great, thanks!

It completely makes sense now!

 

Yes I meant fractional and somehow wrote fractal. Yet another indication that this brain is too tired ?

 

:thumbsup:

 

Now, the last bit you may want to work on is fixed point values.  Operating fractional values as fixed point, generates faster code.

 

   dZ.

Link to comment
Share on other sites

2 hours ago, Vincehood said:

Oh great, thanks!

It completely makes sense now!

 

Yes I meant fractional and somehow wrote fractal. Yet another indication that this brain is too tired ?

 

UPDATE III:  It seems that the "unsigned" directive accepts only a variable to mark it as unsigned, not an assignment.  So the "= 0" is being ignored in the line I mentioned above.  Now, that works because all variables are initialized to zero at the start of the program, but if you needed to assign a different value, say, to start at 1, you'd have to do this:

UNSIGNED #x
#x = 1.0

 

In other news, here's your program utilizing fixed point values:

INCLUDE "constants.bas"

CLS
MODE 0,0,0,0,0
WAIT
DEFINE 0,1,ball_bitmaps
WAIT

'variables

unsigned #x

#x = 0.00
#dx = 0.15
y = 20


main_loop:

IF ((#x % 256) < 160) THEN
 #x = (#x +.#dx)
 PRINT AT 0 COLOR 6,"MOVING "
ELSE
 #x = 0
 PRINT AT 0 COLOR 6,"RESTART"
END IF

SPRITE 0,(#x % 256) + VISIBLE + HIT,y,SPR00 + SPR_BLUE
WAIT

GOTO main_loop


ball_bitmaps:
 BITMAP "...XX..."
 BITMAP "..XXXX.."
 BITMAP ".XXXXXX."
 BITMAP ".XXXXXX."
 BITMAP ".XXXXXX."
 BITMAP ".XXXXXX."
 BITMAP "..XXXX.."
 BITMAP "...XX..."

 

A few things to note.  First, fixed-point values are fractional values in which the decimal point is always "fixed" to a given position.  In this case, a 16-bit value is split into two 8-bit parts:  the "whole" and the "fraction" with the virtual point fixed exactly in the middle of both.

 

Normally, these would look like this:

               6.5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|1|1|0|1|0|0|0|0|0|0|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
\_____________/ ^ \_____________/
     Whole      |    Fraction
      (6)       |  (0.5 = 256/2)
                |
           Fixed Point
            (virtual)

 

As in the integer example that @nanochess offered, the fractional part is typically scaled to 256 in order to offer 8-bit precision.  This is why the diagram above shows that 0.5 (one-half) is depicted as 256/2, which is one-half of the 8-bit range.

 

However, because this sort of scheme is typically used for computing velocity and animation rates, they can be optimized for this case by reversing the two bytes.  That way, the lower part fits neatly in 8-bit variables or registers (like the STIC MOB positions).

 

So, this is precisely how IntyBASIC implements them.  It then offers special addition and subtraction operators (+. and -.) that automatically take care of this reverse quirk for you, so you can still think of them as normal fixed-point fractions.

 

               6.5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|0|0|0|0|0|0|0|0|0|0|0|0|1|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
\_____________/ ^ \_____________/
    Fraction    |      Whole
 (0.5 = 256/2)  |       (6)
                |
           Fixed Point
            (virtual)

The upside is that, whenever you want to get the whole part, if you are assigning to an 8-bit storage, you can just do it directly, since the upper byte will be discarded.  Otherwise, all you need to do is mask the lower byte to clear the fractional bits, by either "AND 255" or "% 256" both of which have been optimized in IntyBASIC to support this feature.

 

Notice then that in the updated code above, #x starts at zero, and is incremented by 0.15 on every iteration.  Internally, IntyBASIC will treat these as special fixed-point values, inverting them as necessary, and scaling them to 256 -- all automatically, without you having to worry about it.  All you need to do is use "+." when adding them to instruct IntyBASIC to do its special magic.

 

There are two other things to note:

  • When comparing the position, you want to make sure you compare only the whole part.  Remember, IntyBASIC still treats these as 16-bit values, and the fractional byte at the head sort of makes them look weird, and the special magic is done only during addition or subtraction.  That's why I use (% 256) in the comparison to isolate the whole portion only.
  • When assigning the position to the SPRITE x register, you also want to only use the whole portion.  The reason for this that, although the position field of the MOB is 8-bits, the X register is 14-bits wide, and includes other bits like the "Visible" and "Interaction" flags, as well as horizontal magnification attribute.  The upper byte in your fixed-point #x (i.e., the fraction) may interfere with those, so we again use (% 256) to isolate the lower byte.

 

I hope the above makes sense.  Please feel free to ask any questions if anything is not clear.

 

    -dZ.

Edited by DZ-Jay
  • Thanks 1
Link to comment
Share on other sites

52 minutes ago, Vincehood said:

Thanks a lot for such a great explanation! I will take the time needed to digest it and come back if needed!

 

Sure thing.  In the trivial case of your example, the difference between both the scaled integer and fixed point, is rather small if non-existent.  However, in more complex cases, where you have to account for acceleration, gravity, friction, and other factors on top of velocity; you get an speed edge with fixed point since the scaling is done at compile time, as opposed to having to multiply and divide by 256 every single term in an expression.

 

52 minutes ago, Vincehood said:

(I had also noticed that #UNSIGNED did not accept an assignment because the following code did not compile:

UNSIGNED #x = 160 * 256)

Yeah, that was my bad.  I hadn't checked the manual yet.  Also, I do not use IntyBASIC normally, so my knowledge of it is a bit limited.

 

   -dZ.

Link to comment
Share on other sites

  • 5 months later...
4 hours ago, Brian's Man Cave said:

Is there a way to make the ball go slightly faster (like accelerate). I have been try to make a sprite speed up and slow down by fractional amounts instead of just +1 or +2 :)

To condense what's been said earlier, the original Mattel games which utilized the Executive ROM maintained sprite positions in a sort of virtualized larger 2D space.  The 16-bit memory locations that maintain sprite records has a 16-bit value for the horizontal position and another 16-bit value for the vertical position.  Only the upper 8 bits are used for the actual pixel position on screen, leaving the lower 8 bits for sub-pixel precision.  I simulated that behavior in the game DILLIGAS.

 

Here's an example that would take a sprite, in this case shaped like a ball, start at the left edge in the middle, and have it travel across the screen at a random vertical velocity, stopping when it reaches a certain point horizontally.

CONST GRAM = $800
CONST MO_VISIBLE = $200
CONST MO_TALL = $100
CONST MO_PINK = $1004 
CONST PIC_BALL = GRAM + 0*0 + MO_PINK  ' First GRAM slot
CONST TWIPS_PER_PIXEL = 256  ' A twip is normally 1/1440th of an inch, commonly used in sub-pixel measurements.

DIM #ballposx  ' Horizontal position
DIM #ballposy  ' Vertical position
DIM #ballvelx  ' Horizontal velocity
DIM #ballvely  ' Vertical velocity

DEFINE 0, 1, BallGraphics
CLS
MODE 0, 0, 0, 0, 0  ' Solid black background
#ballposx = 8 * TWIPS_PER_PIXEL  ' Left edge, fully in view
#ballposy = 52 * TWIPS_PER_PIXEL  ' Middle of the screen
#ballvelx = 1 * TWIPS_PER_PIXEL  ' One pixel each video frame
#ballvely = RAND - 128  ' Range -128..127
DO
	WAIT
	SPRITE 0, MO_VISIBLE + #ballposx / TWIPS_PER_PIXEL, \
		MO_TALL + #ballposy / TWIPS_PER_PIXEL, PIC_BALL
	#ballposx = #ballposx + #ballvelx  ' Update position.
	#ballposy = #ballposy + #ballvely  ' May go up or down.
LOOP WHILE #ballposx > 0  ' At x position 128, the sign bit gets raised.  We'll stop here.
WHILE 1: WEND  ' Infinite loop

BallGraphics:
GRAPHICS "..XXXX.."
GRAPHICS ".XXXXXX."
GRAPHICS "XXXXXXXX"
GRAPHICS "XXXXXXXX"
GRAPHICS "XXXXXXXX"
GRAPHICS "XXXXXXXX"
GRAPHICS ".XXXXXX."
GRAPHICS "..XXXX.."

What's happening is that the ball's vertical position is being updated in increments of no more than half a pixel each video frame in either direction, while it travels one pixel to the right, until it gets to about three quarters of the way across the screen.  This is the one thing that's tricky, and I had to check for this in DILLIGAS.  At x position 128, #ballposx becomes a negative value.  To make the example simple, I just exited the loop at that point.

 

Watch the ball as it travels, and notice how it moves vertically.  By implementing a system like this for sprite placement, you can easily accelerate an object by incrementing the velocity variables by values smaller than TWIPS_PER_PIXEL.  I hope that helps.

Edited by Zendocon
A couple code corrections
Link to comment
Share on other sites

6 hours ago, Brian's Man Cave said:

Is there a way to make the ball go slightly faster (like accelerate). I have been try to make a sprite speed up and slow down by fractional amounts instead of just +1 or +2 :)

 

To accelerate, just add an acceleration coefficient to your velocities before adjusting the positions.  Consider the fixed-point example I provided above, which applies the fractional velocity like this:

#x = (#x +.#dx)

 

Where #x and #dx are the horizontal position and fractional displacement, respectively (as before); and #ax is the fractional horizontal acceleration value.

 

All you need to do is adjust the displacement with your fractional acceleration:

#dx = (#dx +.#ax)   ' Update velocity

 

From then on, the displacement will be adjusted by the acceleration you added, so updating the position should reflect a faster travel.


 

 

      -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

On 8/6/2021 at 12:26 PM, nanochess said:

For the record I'll disable in future versions of IntyBASIC the fractional add/subtraction operators because there are better ways of doing it, like using 16-bit variables, and the division by 256 is pretty optimized using SWAP/ANDI.

 

Isn’t that what the +. and -. operators do already when using 16-bit variables?

 

I would imagine that having the compiler perform the operation directly for you is superior than having to always manually divide the value by 256.

 

It’s a single narrow use case, but an exceedingly common one.  It sounds to me like something the language should handle.

 

    dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

6 hours ago, DZ-Jay said:

Isn’t that what the +. and -. operators do already when using 16-bit variables?

 

I would imagine that having the compiler perform the operation directly for you is superior than having to always manually divide the value by 256.

 

It’s a single narrow use case, but an exceedingly common one.  It sounds to me like something the language should handle.

 

    dZ.

It was a good idea at the time.

 

However the operators +. and -. implement support for a 8.8 fractional point support reversed, where the fraction is in the upper byte, while the integer is in the lower byte. This creates lots of problems, in special with negative numbers, also it wasn't properly devised how it would be supported in all the language, so people has trouble with it.

 

The support in the future will be enhanced for the 8.8 format where the upper byte is the integer, and the lower byte is the fraction.

 

I've a kind of philosophy for IntyBASIC where there is only one way of doing things, instead of several potentially confusing things.

Edited by nanochess
  • Like 2
Link to comment
Share on other sites

21 hours ago, nanochess said:

It was a good idea at the time.

 

However the operators +. and -. implement support for a 8.8 fractional point support reversed, where the fraction is in the upper byte, while the integer is in the lower byte. This creates lots of problems, in special with negative numbers, also it wasn't properly devised how it would be supported in all the language, so people has trouble with it.

 

The support in the future will be enhanced for the 8.8 format where the upper byte is the integer, and the lower byte is the fraction.

 

I've a kind of philosophy for IntyBASIC where there is only one way of doing things, instead of several potentially confusing things.

I understand.  I took your comment to mean that no support for this sort of fixed-point arithmetic was going to be included, leaving programmers to have to deal with it themselves in different ways.

 

I personally do it with the integer part on the high-order byte, but I believe GroovyBee had recommended it in reverse in order to directly apply it to byte-length variables and STIC registers.

 

   dZ.

Edited by DZ-Jay
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...