Jump to content

Mord's Blog

  • entries
    170
  • comments
    93
  • views
    98,653

Saving cycles.

Sign in to follow this  
Mord

167 views

Of course, this means saving compared to how I use to do it. To maintain player movement while they're trying to push along a wall as they move in the collision tests I have to take into account wasted frames and on successive direction tests jump further to make up for lost time.

 

Since horizontal tests are done first, to test movement in the horizontal direction I double the speed prior to moving. Originally I just went through the full 16-bit addition sequence twice. On average that would take up about 80 odd cycles due to the generalized functions I used in it. Those functions are still in the rom but I'm not using them for player movement/collision anymore. I can use those for moving less frequent movement. (monsters/items notably. For the rare couple of items that the player will actually be able to move around with I'll have the item refresh it's X/Y with an offset from the player's X/Y instead of calculating it's movement separately. That should be faster...)

 

Anyway this is what I did for the Move-Right test. I tested it in the rom and it seems to work reliably. IE: Far more efficient than what I was doing. :ponder:

 

The subtraction routine (moving left) however is still a bit icky. Right now I just skipped out with putting the subtraction in a tiny 2-pass loop. Still more efficient than before but I'm hoping to think up something similar like the below.

 

  ; Horizontal Move Test ( Moving Right )
  ; This forces a double-step in the right direction.

clc			   ; 1, 2 ; This part handles double-addition of whole part.
lda SpeedX		; 2, 3 ; Realistically "SpeedX" will never be very high, normally 0-4 at most.)
asl			   ; 1, 2 ; so there's not going to be any carry flag to worry about.
adc PlayerX	   ; 2, 3 ;
sta PlayerX	   ; 2, 3 ;

  ; we can assume the carry is still cleared.

lda SpeedXf	   ; 2, 3  ;
asl			   ; 1, 2  ;
bcc .MovePlayerHorizontal_Skip1; 2, 2/3;
  inc PlayerX	 ; 2, 5  ;
  clc			 ; 1, 2  ;
.MovePlayerHorizontal_Skip1 

adc PlayerXf	  ; 2, 3  ;
sta PlayerXf	  ; 2, 3  ;
lda #0			; 2, 2  ;
adc PlayerX	   ; 2, 3  ; adds any carry left over to PlayerX.
sta PlayerX	   ; 2, 3  ;
				  ;	 35-41 cycles.

Sign in to follow this  


4 Comments


Recommended Comments

lda SpeedXf
asl
bcc 1$; no overflow
clc
adc PlayerXf
sta PlayerXf
lda SpeedX
rol
sec
bcs 2$
1$
sbc PlayerXf
sta PlayerXf
lda SpeedX
rol
2$
sbc PlayerX
sta PlayerX

27 bytes, 25 or 31 cycles depending on SpeedXf

 

lda SpeedXf
asl
bcc 1$; no overflow
sbc PlayerXf
sta PlayerXf
lda SpeedX
rol
sec
bcs 2$
1$
sec
adc PlayerXf
sta PlayerXf
lda SpeedX
rol
2$
adc PlayerX
sta PlayerX

27 bytes, 27 or 29 cycles.

Share this comment


Link to comment

lda SpeedXf
asl
bcc 1$; no overflow
clc
adc PlayerXf
sta PlayerXf
lda SpeedX
rol
sec
bcs 2$
1$
sbc PlayerXf
sta PlayerXf
lda SpeedX
rol
2$
sbc PlayerX
sta PlayerX

27 bytes, 25 or 31 cycles depending on SpeedXf

 

 

Almost worked although had to change the sbc's to adc's it seems. :D

 

This left me with: (After my extensive comments being added to help my primitive thought processes ;) )

 

 lda SpeedXf; load SpeedX fractional.
 asl		; double it. (So we now have double the fractional portion in A.
 bcc NoOverflow; Was there overflow? (didn't need to pre-set/clear carry.) 
 clc		; clear our carry for addition.
 adc PlayerXf  ; <-- add playerX fractional to the fractional speed..
 sta PlayerXf  ; <-- store the new playerX fractional.
 lda SpeedX	; <-- load SpeedX (Never more than 4-8)
 rol		; <-- rol for double instead of asl works in this case beca.... ZOMG!!!! 
			;	 THE CARRY GOT SET PROPERLY!!!!!! (only good because we know SpeedX < 8)
 sec		; set the carry. (as we know it won't be.)
 bcs Finishing ; <-- skip NoOverFlow section.
 
NoOverflow:	 ; <-- asuming no overflow occurred.
 adc PlayerXf  ; add
 sta PlayerXf  ;
 lda SpeedX	;
 rol		;
Finishing: 
 adc PlayerX
 sta PlayerX

 

When the sbcs were in there for the branches, we ended up with the walls being sticky when trying to push against them.

You can tell from the comments added what tricks I didn't realize could work.

 

... Looking over the code again, we can probably save some more by removing the sec... (And in fact the sec probably throws off the addition when I changed the sbc's to adc's.) Since we know the carry will always be cleared due to the low value of SpeedX...

 

The subtraction variation for moving in the other direction doesn't work unfortunately. :) With addition we can add the two numbers in any order, but the order does matter for subtraction (7 - 3) != (3 - 7)! So we can't have SpeedX(f) in the A register, we need PlayerX(f) in there.

 

Hm. can't post attachments in replies so can't show what happens. Essentially with the subtraction method given, when you try pushing against horizontal wall (like the bottom of the screen) instead of sliding against it as you move left, you end up moving right. (moonwalking? ;) ) Interestingly tho, if you try to move sideways, the ball ends up acting like a solid wall and pushes you - but stops pushing you and passes over if you stop moving. :D

 

Thanks for the help! I've modified my move-right routine for it. :D Every byte I save now could mean a feature saved later. *grin*

Share this comment


Link to comment

Let's back up and make sure we're saving space/cycles over the simple stuff.

  ldy #2; do it as a loop for space savings
 clc
1$
 lda PlayerXf
 adc SpeedXf
 sta PlayerXf
 lda PlayerX
 adc SpeedX
 sta PlayerX
 dey
 bne 1$

Swap adc/sbc and clc/sec (move after 1$ loop) for subtraction. 18 bytes, 49 cycles (51 for subtract), unrolling the loop doubles the size for a small number of cycles.

 

Okay, back to my original idea (fixed - I was trying to make the subtract version and modified the wrong routine):

  lda SpeedXf
 asl ; multiply SpeedXf by two
 bcc 1$ ; no overflow
 clc; clear carry for add
 adc PlayerXf
 sta PlayerXf
 lda SpeedX
 rol ; multiply SpeedX by two, shift in overflow from PlayerXf += 2 * SpeedXf
 sec ; set carry from 2 * SpeedXf overflow
 bcs 2$ ; branch on known condition
1$
 adc PlayerXf
 sta PlayerXf
 lda SpeedX
 rol ; multiply SpeedX by two, shift in overflow from PlayerXf += 2 * SpeedXf
2$
 adc PlayerX
 sta PlayerX

27 bytes, 25 or 31 cycles depending on SpeedXf - 50% bigger code but almost half the cycles on average.

 

Now you pointed out that 2 * SpeedX - PlayerX != PlayerX - 2 * SpeedX, which does make my subtraction routine bogus. I'm trying to figure out whether it would be easier to implement -SpeedX * 2 + PlayerX or -( Speed X * 2 - PlayerX ). Or maybe there's a reasonable way to implement PlayerX - SpeedX - SpeedX (i.e. only load/store PlayerX once).

Share this comment


Link to comment

 sec
 lda PlayerXf
 sbc SpeedXf
 bcc 1$; overflow on first subtract
 sbc SpeedXf
 sta PlayerXf
 lda PlayerX
 sbc SpeedX; assume PlayerX - 2 * SpeedX is never negative
 sbc SpeedX; thus carry is set after both subtracts
 bcs 2$
1$
 sec
 sbc SpeedXf
 sta PlayerXf
 lda PlayerX
 sbc SpeedX
 clc; pull in first overflow
 sbc SpeedX
2$
 sta PlayerX

33 bytes, 33 or 35 cycles

Share this comment


Link to comment
Guest
Add a comment...

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