IGNORED

# What efficient ways can you set player speed based direction?

## Recommended Posts

```ProcessJoystick:

lda SWCHA
sta Temp     ; store Joysticks for later,
; going go be trashing the accumulator

ldx #0    ; joystick counter

PJLoop:
cpx #1
beq LowNibble ; J1 already has bits in correct position
lsr           ; J0 needs to shift bits right 4 times
lsr
lsr
lsr

LowNibble:
and #%00001111 ; we only want the low nibble
xor #%00001111 ; flip bits so 1=direction held

scb            ; set carry bit? necessary?
sbc #3
bmi MedSpeed   ; must be going vertically
sbc #2         ; all directions less than 5 aren't diag
bmi FastSpeed  ; probably going horizontally
sbc #3         ; subtract 8 total
beq FastSpeed  ; going Right
; otherwise ...
; going diagonally
lda Temp
pha     ; push it onto the stack
lda Frame      ; get the current frame
and #7         ; if 8th frame,
beq SlowMovement   ; branch to rest of handler, like Collect Tutorial
pla   ; pull off stack
lsr
lsr
lsr
lsr
jmp NextJoystick
MedSpeed:
lda Temp       ; restore Joystick
pha
lda Frame
and #3
beq SlowMovement
pla
lsr
lsr
lsr
lsr
jmp NextJoystick

SlowMovement:
pla   ; pull back from stack

FastSpeed:
NormalJP:
CheckRight
asl
bcs CheckLeft
ldy ObjectX,x
iny
<compare with Arena size>

CheckLeft:
asl
bcs CheckDown
...
CheckDown:
...
CheckUp:
...
NextJoystick:
inx
cpx #2
bne PJLoop

rts

```

If I wanted to set the player speed based upon which direction the player is moving, how best to do that?

Horizontally (fastest speed)

Vertically (medium speed)

Diagonally (slowest speed)

I know of the Fractional/Sub-pixel movement. This doubles the RAM requirements for X,Y positions for each object. Essentially, you do 16-bit math on the positionX (onscreen) and the fractionalX (sub-pixel), and when the carry overflows into the onscreen Y position (P0_YPosFromBottom+1), the actual pixels get updated.  I am still not sure In the example code, how I would modify it to identify the difference between moving vertical and moving vertically+horizontally simultaneously (diagonally).

```; for up and down, we INC or DEC
; the Y Position
;joystick down?
lda #%00010000
bit SWCHA
bne DoneMoveDown

;16 bit math, add both bytes
;of the ghost speed constant to
;the 2 bytes of the position
clc
lda P0_YPosFromBot
sta P0_YPosFromBot
lda P0_YPosFromBot+1
sta P0_YPosFromBot+1
DoneMoveDown

lda #%00100000 ;Up?
bit SWCHA
bne DoneMoveUp

;16 bit math, subtract both bytes
;of the ghost speed constant to
;the 2 bytes of the position
sec
lda P0_YPosFromBot
sbc #<C_GHOST_SPEED
sta P0_YPosFromBot
lda P0_YPosFromBot+1
sbc #>C_GHOST_SPEED
sta P0_YPosFromBot+1
DoneMoveUp```

In the Collect Tutorial, the speed is modified by reading the joystick button. It uses a similar approach to handle directional movement as the example above. It left-shifts the SWCHA bits into the carry flag, and then processes each individual direction. Instead of setting the speed via a fractional X/Y position, it checks the current frame and only processes movement if the frame is divisible by 8 (about every 133 millisecond )

My guess is that before I start processing the individual directions RLUDxxxx, I need to determine if the player is moving Diagonally, Vertically, or Horizontally.

Since the Atari sets bits as 0 when that direction is held (which does seem backwards), I would need to read SWCHA, store it (so it can be restored when processing the other joystick), set a counter for JoystickNo (X reg), shift-right if processing J0, then AND it with the P1 bitmask (P1_MASK = #%00001111, then XOR with the same mask to get bit=1 (true) for holding that direction.

LowerNibble possible Joystick Values, (after AND + XOR)

#%1010 - 10, RU-diag, slow speed

#%1001 - 9, RD-diag, slow speed

#%0110 - 6, LU-diag, slow speed

#%0101 - 5, LD-dish, slow speed

#%1000 - 8, R, fast speed

#%0100 - 4, L, fast speed

#%0010 - 2, U, med speed

#%0001 - 1, D, med speed

#%0000 - 0, not moving

(Other impossible combinations, without special controllers)

At this point I see two possibilities

Poss1 - I should be able to use the lower nibble as a pointer offset to a speed table of possible speeds. (Not sure if I need a table that is 16 bytes or 11 bytes. 11 is the maximum when dealing with normal controllers since some joystick combinations aren’t possible). The table could have fractional speeds (so once again 2-bytes for each speed (20 total ROM bytes), and 4-bytes for each object. (X,Y and fractional for each).

Poss2 - I can do what was done in the Collect Tutorial. Instead of saving the speed values, check the current frame and only process joysticks on frames divisible by some number (2^x-1 for best results).  I can have conditional branches that first check if we are moving Up/Down (conveniently, this means low-nibble < 3) {only process every 4th frame}, then check if low-nibble is >4 (all low-nibbles 5 or greater are diagonal with the exception of Right = 0x08) and not exactly 8 {only process every 8th frame}, all other conditions {process every 2nd frame}. This should conveniently handle impossible joystick combinations.

Code at TOP (seems to be full of errors. I must not be pushing/popping from the stack properly. I also seem to be controlling P0/P1 with the same controller)

Edited by CapitanClassic
##### Share on other sites

I would use the 4 bit joystick value as an index into a direction/speed table.  Something like this:

```    lda SWCHA
lsr
lsr
lsr
lsr ; now have a value from 0-15 for the left player

asl ; times 2 to look up data in a Word table
tax

lda DirSpeedX,x
; do 16 bit add to current X position here
lda DirSpeedX+1,x
; do 16 bit add to current X position here

lda DirSpeedY,x
; do 16 bit add to current Y position here
lda DirSpeedY+1,x
; do 16 bit add to current Y position here

DirSpeedX:            ;  RLDU
.word 0           ; %0000 = invalid, all directions held
.word 0           ; %0001 = invalid, Right & Left held at same time
.word 0           ; %0010 = invalid, Right & Left held at same time
.word 0           ; %0011 = invalid, Right & Left held at same time
.word 0           ; %0100 = invalid, Down & Up held at same time
.word SLOW        ; %0101 = Right + Down
.word SLOW        ; %0110 = Right + Up
.word FAST        ; %0111 = Right
.word 0           ; %1000 = invalid, Down & Up held at same time
.word -SLOW       ; %1001 = Left + Down
.word -SLOW       ; %1010 = Left + Up
.word -FAST       ; %1011 = Left
.word 0           ; %1100 = invalid, Down & Up held at same time
.word MEDIUM      ; %1101 = Down
.word -MEDIUM     ; %1110 = Up
.word 0           ; %1111 = no direction held

DirSpeedY:
;    Similar to DirSpeedX
.word             ; %0111```

If you're not using direction, such as for an endless runner game, then drop the - sign on the Left and Up entries.

To use that for the right player you'd do this to get the index:

```    lda SWCHA
and #\$0F
asl
tax
...```

##### Share on other sites

Thanks! @SpiceWare, I really appreciate the Collect tutorials, and I am looking forward to the newest Arena blog post for CDFJ Collect 3. I am going to try a small game first in 4K/8K, and probably hit some limitations with regard to available time in the kernel.

Speaking of which, Collect is a great example of DoDraw and a 2LK.

I haven't been able to find a good tutorial for other Kernel strategies such as SwitchDraw, SkipDraw, (others?)

Your post here leads me to believe that SkipDraw uses two pointers, one to a ROM mask (which would have to be the size of the Arena) that blanks out the player sprite by  setting the pointer such that the mask is 0x00 when the player Sprite isnt being displayed. The kernel is always drawing the player in the Arena, it is just being blanked out when the Y-value of the Arena doesn't match the Y-value of the player (plus player_height).

SwitchDraw on the other hand can only be used for short Arenas, because the max Y value is 0×7F (127), but doesn't require the large bit mask.

I am trying to draw a dynamic asymmetric playfield (similar to Cosmic Swarm, where playfield bits can be turned on/off dynamically during gameplay), which requires writes to both left and right sides of the playfield (minimum cycles, 6 PFx writes (18) + LDA (18)  = 36 cycles). I also want to update both players, both missiles, and ball but only need 2LK accuracy. Is this even possible within 8k/128bytes, or am I going to have to either go with a reflected playfield or CDFJ?

##### Share on other sites

Haven't looked into the other methods, so haven't tracked down tutorials. The Tricks page at MiniDig links to @Thomas Jentzsch's skip draw routine from the Stella Mailing List days: https://www.biglist.com/lists/stella/archives/200102/msg00282.html

And here's @DEBRO explaining how it works: https://www.biglist.com/lists/stella/archives/200309/msg00056.html

Might be able to - the castle kernels in Medieval Mayhem do this on every scanline:

• updates left PF0, left PF1, right PF1, right PF0
• updates ball, and both missiles

Every-other scanline it reads 2 paddles (23 cycles), and on some scanlines it applies a texture to the playfield (8-14 cycles).

The players are within zones, so updates in 8 cycles using lda (shieldptr),y and sta grp0 with zero padded graphics.

I also unrolled the kernel, which eliminated the loop overhead at the expense of ROM.  I do wish I had a handle on using macros for the unrolling, it would have saved a lot of typing. I did the same unrolling in Stay Frosty, and for that I did use macros so the game kernel looks like this for the 5 zones:

```KernelCode

SECTION 4
SECTION 3
SECTION 2
SECTION 1
SECTION 0```

A brief snippet of the SECTION macro, the ... represents about 250 more lines of code:

```; usage SECTION #
; # = 0 thru 4 for 5 sections
; level 0 is lowest section and has special condition
; of being solid in the platform area.
MAC SECTION
.SEC    SET {1}
ldy Heights+.SEC                ;    load Y with height of current section
lda #ICE_TOP_COLOR
sta COLUPF
lda (FrostyImagePtr+.SEC*2),y
tax
lda (FrostyColorPtr+.SEC*2),y
tay
if .SEC = 4
sta HMCLR
endif
lda FireBallX+.SEC*2
bne .repositionFireball
sta WSYNC
stx GRP0
sty COLUP0
sta WSYNC

...

ENDM```

• 1
• 1

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

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.