CapitanClassic Posted July 7, 2020 Share Posted July 7, 2020 (edited) 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). https://alienbill.com/2600/cookbook/subpixel.html ; 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 adc #<C_GHOST_SPEED sta P0_YPosFromBot lda P0_YPosFromBot+1 adc #>C_GHOST_SPEED 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 July 7, 2020 by CapitanClassic Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted July 7, 2020 Share Posted July 7, 2020 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 ... 1 Quote Link to comment Share on other sites More sharing options...
CapitanClassic Posted July 7, 2020 Author Share Posted July 7, 2020 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? Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted July 7, 2020 Share Posted July 7, 2020 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 updates both players 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 and (FrostyMaskPtr+.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 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.