Assembly Question - AKA I suck at 6502 maths

Discuss all aspects of programming here. From 8-bit through to modern architectures.
User avatar
FourthStone
Posts: 400
Joined: Thu Nov 17, 2016 2:29 am
Location: Melbourne, Australia

Assembly Question - AKA I suck at 6502 maths

Postby FourthStone » Mon Mar 27, 2017 8:15 am

Hi all,

I have a question around how to do something that is quite simple for me in Basic but I can't seem to wrap my head around in assembly. Hoping one of the 6502 masters can provide a little bit of direction, once I see it working I can usually adapt for my own needs but I haven't found anything that quite works for what I am after...

What I am trying to do is a simple additive horizontal movement routine, if 'Z' is pressed then variable xAdd is decremented down to a minimum amount and if 'X' is pressed then xAdd is incremented to a maximum amount. Then every frame, xAdd is added to the current horizontal position and the sprite is updated. Obviously Basic sorts out all the fractional maths whereas in Asm I need to code that very carefully to suit the movement of my sprite. I have the sprite drawing code sorted, I am just struggling with the maths for fractional movement.

The following Basic code example is what I am trying to do in Assembly, if anyone can offer any assistance I would be very grateful:

Code: Select all

X%=640
Y%=20
XA=0
YA=0
GP=.2
REPEAT
   PROCeraseship
   X%=X%+XA
   Y%=Y%+YA:YA=YA-GP
   PROCdrawship
   IF INKEY(-98) XA=XA-GP
   IF INKEY(-67) XA=XA+GP
   IF INKEY(-74) YA=YA+GP*2
UNTIL 0


I've tried a number of approaches like a counter that trigger a horizontal update and using 2 bytes with the lo byte as an counter and the hi byte as the horizontal adder but I can't seem to get these approaches working.

Let me know if more details are required, any help would be much appreciated.

RobC
Posts: 1821
Joined: Sat Sep 01, 2007 9:41 pm

Re: Assembly Question - AKA I suck at 6502 maths

Postby RobC » Mon Mar 27, 2017 9:48 am

I do something a bit like this to handle the player's vertical motion in my Bomb Jack clone. It sounds like you are on the right track :D

The trick is to use a fixed-point representation of the players position. For the vertical axis, there are at most 256 positions (as all Beeb graphics screens are 256 pixels high - BASIC maps this to a 1024 high grid but there are only 256 in reality).

So, a single byte can hold all actual 256 vertical screen positions. Using an additional byte allows us to have a fractional part.

You are using vertical units of 0.4 in BASIC which translate to units of 0.1 in real terms (as BASIC scales the screen by a factor of 4 vertically).

So, you could do something like this (this is in P65 format as I use Swift for developing Beeb games):

Code: Select all

.alias PLAYER_YDELTA 26      ; this is the increment (26 ~ 0.1 x 256)
.alias PLAYER_YLO $80      ; 2-bytes of player's Y-position (integer part and fractional part)
.alias PLAYER_YHI $81

CLC
LDA #PLAYER_YDELTA
ADC PLAYER_YLO
STA PLAYER_YLO
LDA PLAYER_YHI
ADC #0
STA PLAYER_YHI


You then use PLAYER_YHI as the Y co-ordinate for your sprite plotting routine.

For the X-axis, you can do something similar but it's more complicated as the number of possible locations depends on the screen mode. Also, the way pixels are represented in bytes can make single pixel horizontal movement more complicated.

In modes 2 and 5 there are only 160 pixels across so you can use the same technique and represent the integer X location in a single byte with another byte representing the fractional component. In my Bomb Jack game, I use mode 2 with 2-pixel horizontal positioning but with pairs of left/right sprites that are offset to give the impression of single pixel movement.

In other modes, there are more locations than can be represented in a single byte so you could choose to use part of the low byte to represent some of the integer position. You then have to shift and/or rotate the bytes to get the integer position.

Which screen mode are you using and what sort of horizontal movement are you looking to achieve?

User avatar
FourthStone
Posts: 400
Joined: Thu Nov 17, 2016 2:29 am
Location: Melbourne, Australia

Re: Assembly Question - AKA I suck at 6502 maths

Postby FourthStone » Mon Mar 27, 2017 9:20 pm

Thanks for that quick response, I'm using mode 1, I have four copies of my ship sprite so I can move at 1 pixel increments.

The code I have tried so far looks similar to what you posted but I must be missing something. I am trying to get sub pixel movement (or increments really) and when the increment hits the pixel threshold then the sprite moves by one pixel by increasing the sprite index. Once the sprite index reaches 4 it gets reset to 0 and the addr pointer gets incremented to the next column... actually just writing that down helps make it a bit clearer in my head. Time for another play I think...

EDIT: I probably should add that I am trying to combine the index increments with the addr increments so maybe I should pull those two components apart... I'll post my code up a bit later and see if anyone can spot the (obvious) flaw(s) :roll:

RobC
Posts: 1821
Joined: Sat Sep 01, 2007 9:41 pm

Re: Assembly Question - AKA I suck at 6502 maths

Postby RobC » Tue Mar 28, 2017 8:26 am

Keeping track of them separately would work. You could then have the screen byte position incremented by 1/4 of the increment and the sprite/frame value incremented by the original amount.

If you wanted to keep them together, you can use the top two bits of the position's low byte to represent the frame and then use:

Code: Select all

CLC
AND #$C0
ROL
ROL
ROL


to move the top two bits into bits 1 and 0.

User avatar
tricky
Posts: 1918
Joined: Tue Jun 21, 2011 8:25 am
Contact:

Re: Assembly Question - AKA I suck at 6502 maths

Postby tricky » Tue Mar 28, 2017 12:31 pm

I do something similar in Frogger, where it can have fractional movement up to 4 pixels per frame.
I use the low byte as 1/64ths of a pixel, and then change the address when overflow happens.
I use the low byte AND &C0 as the offset to read the bytes of my sprite from, storing each row of the sprite offsets 64 bytes apart.

If the first bit didn't make sense, save this next bit for another day ;)
If the maximum movement was 1 pixel and the screen with 256 (what I usually use), I would go for low byte is fraction, high byte is in pixels.
For mode 1, this gives pixels AND 3 is the page, so I would store my sprites 1 pixel offset per page and pixels AND &F8 ASL is byte offset in line with the carry being the offset in pages along a line.

User avatar
FourthStone
Posts: 400
Joined: Thu Nov 17, 2016 2:29 am
Location: Melbourne, Australia

Re: Assembly Question - AKA I suck at 6502 maths

Postby FourthStone » Tue Mar 28, 2017 11:29 pm

Thanks guys, with your help I've managed to get my ship moving in one direction (left) with sub pixel increments :D

My next challenge, which I've have wrought upon myself, is that I am using the full width mode 1, which is 80 bytes wide, which quite inconveniently for me doesn't fit into the 6 bits of my hi byte used for storing my pixel index and addr index... Tricky I understand now another reason why you use 256 pixel width often.

Any suggestions apart from the obvious which would be to use another byte for the pixel index and a whole byte for the addr index? Don't really want to use 256 pixels as this game uses a lot of screen space for the terrain.


My solution in the end was to use 2 bytes for the Xadd variable, this allows a fine precision with horizontal movement allowing for 1 pixel movement every 250 odd frames, good for extra slow and careful sideways movement.

The second variable for Xpos is also 2 bytes, this gets incremented by the value in Xadd and then converted to pixel index and addr index... apart from the problem described above :roll:

Code: Select all

   ; Z - left
   LDA #97:STA &FE4F:LDA &FE4F:BPL skipleft
   clc: lda Xadd: adc #1: sta Xadd: lda Xadd+1: adc#0: cmp #5: beq skipleft: sta Xadd+1
   .skipleft
   
   ; subtract Xadd from Xpos, reset Xadd if Xpos reaches 0 - left of screen
   sec: lda Xpos: sbc Xadd: sta Xpos: lda Xpos+1: sbc #0: sbc Xadd+1: bpl PosDone: lda #0: sta Xadd: sta Xadd+1:
   .PosDone: sta Xpos+1
   
   ; get pixel index and addr index and store in sprite object
   lda Xpos+1: and #3: sta SprDraw: lda Xpos+1: lsr a: lsr a: sta SprDraw+1
   


And then I've got to work out the other main thing i've been avoiding, get the ship to move right as well... should be a snap at the rate i'm coding :lol:


Return to “programming”

Who is online

Users browsing this forum: No registered users and 3 guests