diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/driver.s | 30 | ||||
| -rw-r--r-- | src/jetpac.s | 1 | ||||
| -rw-r--r-- | src/player.s | 317 |
3 files changed, 347 insertions, 1 deletions
diff --git a/src/driver.s b/src/driver.s index 521c335..8b7f8ce 100644 --- a/src/driver.s +++ b/src/driver.s @@ -1,6 +1,14 @@ .segment "CODE" .scope Driver + ;; Timer for the player to be able to pick up the joypad upon entering the + ;; game. + ;; + ;; NOTE: this memory address is shared with `zp_title_timer`, as they can + ;; never conflict with each other. + zp_player_timer = $30 + PLAYER_TIMER_VALUE = HZ * 2 + .proc switch ;; Get the assets ready for the main screen. That is, make sure that the ;; palettes and such are as desired since the title screen needed @@ -11,6 +19,14 @@ lda #%10001010 sta PPU::zp_control + ;; Setup the player timer. + .ifdef PARTIAL + lda #1 + .else + lda #PLAYER_TIMER_VALUE + .endif + sta zp_player_timer + ;; Mark the state of the game as "game". That is, the player has ;; started. Also set the `ppu` flag so the PPU control update takes ;; place. @@ -22,7 +38,19 @@ .endproc .proc update - ;; TODO + lda zp_player_timer + beq @game + + dec zp_player_timer + beq @load_player + + ;; TODO: blinking of the selected player (every HZ count?). rts + + @load_player: + jsr Player::init + + @game: + JAL Player::update .endproc .endscope diff --git a/src/jetpac.s b/src/jetpac.s index d3eef5a..e997af1 100644 --- a/src/jetpac.s +++ b/src/jetpac.s @@ -38,6 +38,7 @@ .include "../include/globals.s" .include "assets.s" +.include "player.s" .include "driver.s" .include "title.s" .include "vectors.s" diff --git a/src/player.s b/src/player.s new file mode 100644 index 0000000..cee79cf --- /dev/null +++ b/src/player.s @@ -0,0 +1,317 @@ +.segment "CODE" + +;; Functions and variables that keep up with the player's sprite. That is, +;; movement, heading, animation, collision with the environment, etc. +.scope Player + ;;; + ;; *Movement* is done with subpixel precision. Hence, we need to handle + ;; things via fixed-point arithemetic. In particular, the `zp_screen_*` + ;; variables refer to pure screen coordinates, and they are the only uint8_t + ;; variables. As for the rest: + ;; + ;; - Velocity (actual and target) are 4.4 fixed-point signed values. Hence, + ;; the high nibble represents the signed integer part, and the low nibble + ;; the fractional one. + ;; - Each Position is a 16-bit signed fixed-point value in little-endian + ;; format like so: |llll/ffff| - |0000/hhhh|. That is, the low byte is + ;; split with the fractional part and the low nibble of the integer part, + ;; and the high byte only contains the high nibble of the signed integer + ;; part (and the high nibble of that high byte is discarded). + ;; + ;; This has the following properties: + ;; + ;; - Adding the velocity to a position is just a matter of performing an + ;; `adc` between the low byte of the position and the velocity. + ;; - Translating positions into screen coordinates it's a matter of simply + ;; rolling the low nibble of the high byte into the low byte. + + INIT_Y_POSITION_LO = $80 + INIT_Y_POSITION_HI = $0C + INIT_Y_VELOCITY = $08 + + INIT_X_POSITION_LO = $00 + INIT_X_POSITION_HI = $04 + + THROTTLE = $D8 + BLAST_OFF = $F8 + GRAVITY = $28 + + UPPER_LIMIT = 10 + GROUND_LIMIT = 200 + + zp_screen_y = $40 + zp_position_y = $41 ; NOTE: 16-bit. + zp_target_velocity_y = $43 ; TODO: needed? + zp_velocity_y = $44 + + zp_screen_x = $45 + zp_position_x = $46 ; NOTE: 16-bit. + zp_target_velocity_x = $48 ; TODO: needed? + zp_velocity_x = $49 + + ;; Initialize the player's sprite. Note that for the sprite to look + ;; correctly on screen you still need to call `Player::update` afterwards. + .proc init + ;; Reset velocity + lda #$00 + sta zp_target_velocity_y + sta zp_velocity_y + sta zp_target_velocity_x + sta zp_velocity_x + + ;; Set position, and the screen coordinates will be updated upong + ;; calling `update`, which on initialization will happen right after. + lda #INIT_Y_POSITION_LO + sta zp_position_y + lda #INIT_Y_POSITION_HI + sta zp_position_y + 1 + lda #INIT_X_POSITION_LO + sta zp_position_x + lda #INIT_X_POSITION_HI + sta zp_position_x + 1 + + ;;; + ;; TODO: this should really just go away + + lda #$00 + sta $201 + lda #$00 + sta $202 + + lda #$01 + sta $205 + lda #$00 + sta $206 + + lda #$10 + sta $209 + lda #$00 + sta $20A + + lda #$11 + sta $20D + lda #$00 + sta $20E + + lda #$20 + sta $211 + lda #$00 + sta $212 + + lda #$21 + sta $215 + lda #$00 + sta $216 + + rts + .endproc + + .proc update + jsr update_vertical_position + + ;; TODO: horizontal + + ;; At this point all positions are clear, transform them into screen + ;; coordinates, eject out from boundaries and platforms, and update the + ;; sprite with the new state. + jsr position_to_screen + jsr bound_check + JAL update_sprite + .endproc + + ;; Updates the `zp_velocity_y` and the `zp_position_y` depending on whether + ;; the player is throttling or gravity should just apply. + .proc update_vertical_position + ;; Check if the player is asking to throttle, otherwise apply gravity. + lda #(Joypad::BUTTON_UP | Joypad::BUTTON_A) + and Joypad::zp_buttons1 + beq @set_gravity + lda zp_velocity_y + beq @blast_off + lda #THROTTLE + bne @compute_vertical + @set_gravity: + lda #GRAVITY + + @compute_vertical: + sta Globals::zp_tmp0 + + ;; Check the difference between the given target velocity and what we + ;; have now. If it equals to zero, then we change nothing in regards to + ;; the velocity. + lda zp_velocity_y + sec + sbc Globals::zp_tmp0 + beq @apply_velocity + + ;; Increase or decrease depending on what we have now. + ;; TODO: inc/dec might not quite cut it in NTSC vs PAL + bmi @down + dec zp_velocity_y + jmp @apply_velocity + @down: + inc zp_velocity_y + jmp @apply_velocity + + @blast_off: + lda #BLAST_OFF + sta zp_velocity_y + jmp @going_up + + @apply_velocity: + lda zp_velocity_y + bmi @going_up + + ;; The velocity is positive, so it's just a 16-bit addition. + clc + adc zp_position_y + sta zp_position_y + lda #0 + adc zp_position_y + 1 + sta zp_position_y + 1 + rts + + @going_up: + ;; Negative velocity, we need to go up. This is probably not optimal, + ;; but we just invert the number and subtract with that. + lda #0 + sec + sbc zp_velocity_y + sta Globals::zp_tmp0 + lda zp_position_y + sec + sbc Globals::zp_tmp0 + sta zp_position_y + lda zp_position_y + 1 + sbc #0 + sta zp_position_y + 1 + + rts + .endproc + + ;; Convert the positions with subpixel precision into mere screen + ;; coordinates. That is, update the values on `zp_screen_{x,y}` given the + ;; current values of `zp_position_{x,y}`. + .proc position_to_screen + ;; We save the high byte into a temporary value, and we load the low + ;; byte into the accumulator. + lda zp_position_y + 1 + sta Globals::zp_tmp0 + lda zp_position_y + + ;; And now it's a matter of rotating the high byte into the low one to + ;; match a full byte. + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + + ;; Ecce Y coordinates. + sta zp_screen_y + + ;; And the same for the X coordinates. + lda zp_position_x + 1 + sta Globals::zp_tmp0 + lda zp_position_x + + ;; And rolling... + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + lsr Globals::zp_tmp0 + ror + + ;; Ecce X coordinates. + sta zp_screen_x + + rts + .endproc + + ;; Check on whether the player is out of bounds in any way and provide an + ;; ejection logic for each situation. + .proc bound_check + ;; Are we at the top? + ;; TODO: actually buggy, but nevermind for now + lda zp_screen_y + cmp #UPPER_LIMIT + beq @too_high_icarus + bcs @check_ground + @too_high_icarus: + lda #0 + sta zp_velocity_y + lda #UPPER_LIMIT + sta zp_screen_y + rts + + ;; Nope, are we at the ground? + @check_ground: + cmp #(GROUND_LIMIT - 24) + bcc @above_ground + + ;; We appear to be either at the ground or below it (e.g. we are + ;; standing still but initial gravity is pulling us down). In this case, + ;; just reset the Y velocity and the Y position. + lda #0 + sta zp_velocity_y + lda #(GROUND_LIMIT - 24) + sta zp_screen_y + lda #$F0 + and zp_position_y + sta zp_position_y + rts + + ;; Nope, let's check for the platforms. + @above_ground: + ;; TODO: notice how ground and top are just cases on the general + ;; "collision up/down". Next commits will merge these logics. + + rts + .endproc + + .proc update_sprite + ;; TODO: + ;; - Update heading + ;; - Update motion state + ;; - Update tiles + + jsr update_sprite_coordinates + + rts + .endproc + + ;; Update the coordinate for the six sprites that make up the player. + .proc update_sprite_coordinates + ;; Y axis. + lda zp_screen_y + sta $0200 + sta $0204 + clc + adc #8 + sta $0208 + sta $020C + clc + adc #8 + sta $0210 + sta $0214 + + ;; X axis. + lda zp_screen_x + sta $0203 + sta $020B + sta $0213 + clc + adc #8 + sta $0207 + sta $020F + sta $0217 + + rts + .endproc +.endscope |
