Added comments; changed zero page so it will mostly work on the atari

This commit is contained in:
Rob McMullen 2017-08-01 19:27:54 -07:00
parent 51ef7374a9
commit b1a142d0f0
5 changed files with 65 additions and 93 deletions

View File

@ -5,6 +5,14 @@ Fujirun
My (winning!) entry in the `KansasFest <https://www.kansasfest.org/>`_ 2017 `HackFest <https://www.kansasfest.org/hackfest/>`_ competition. My (winning!) entry in the `KansasFest <https://www.kansasfest.org/>`_ 2017 `HackFest <https://www.kansasfest.org/hackfest/>`_ competition.
Code Walkthrough
================
TBD
* any place you see the "_smc" extension, that's a target for self-modifying code. Got that from Quinn Dunki.
References References
========== ==========

View File

@ -1,7 +1,7 @@
level_enemies .byte 55, 4, 5, 6, 7, 8 ;# level starts counting from 1, so dummy zeroth level info level_enemies .byte 55, 4, 5, 6, 7, 8 ;# level starts counting from 1, so dummy zeroth level info
level_speed_l .byte 255, 200, 210, 220, 230, 240 ;# increment of fractional pixel per game frame level_speed_l .byte 255, 200, 210, 220, 230, 240 ;# increment of fractional pixel per game frame
level_speed_h .byte 2, 2, 2, 2, 2, 2 level_speed_h .byte 2, 2, 2, 2, 2, 2 ; increment of whole pixels
;level_speed_h .byte 0, 0, 0, 0, 0, 0
player_score_row .byte 2, 7, 12, 17 player_score_row .byte 2, 7, 12, 17
player_lives_row .byte 3, 8, 13, 18 player_lives_row .byte 3, 8, 13, 18
player_score_l .byte 0, 0, 0, 0 player_score_l .byte 0, 0, 0, 0

67
logic.s
View File

@ -1,51 +1,4 @@
; check the player and change its state if necessary
; def draw_actors():
; zp.current_actor = 0
; while zp.current_actor <= zp.last_enemy:
; r = actor_row[zp.current_actor]
; c = actor_col[zp.current_actor]
; get_sprite()
; draw_sprite(r, c)
; zp.current_actor += 1
;
; def get_sprite():
; a = actor_status[zp.current_actor]
; if a == PLAYER_ALIVE:
; c = ord("$") + zp.current_actor
; elif a == PLAYER_EXPLODING:
; collision_log.debug("p%d: exploding, frame=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
; c = ord(exploding_char[actor_frame_counter[zp.current_actor]])
; actor_frame_counter[zp.current_actor] -= 1
; if actor_frame_counter[zp.current_actor] <= 0:
; actor_status[zp.current_actor] = PLAYER_DEAD
; actor_frame_counter[zp.current_actor] = DEAD_TIME
; elif a == PLAYER_DEAD:
; collision_log.debug("p%d: dead, waiting=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
; c = None
; actor_frame_counter[zp.current_actor] -= 1
; if actor_frame_counter[zp.current_actor] <= 0:
; player_lives[zp.current_actor] -= 1
; if player_lives[zp.current_actor] > 0:
; init_player()
; actor_status[zp.current_actor] = PLAYER_REGENERATING
; actor_frame_counter[zp.current_actor] = REGENERATING_TIME
; else:
; actor_status[zp.current_actor] = GAME_OVER
; elif a == PLAYER_REGENERATING:
; collision_log.debug("p%d: regenerating, frame=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
; if actor_frame_counter[zp.current_actor] & 1:
; c = ord("$") + zp.current_actor
; else:
; c = ord(" ")
; actor_frame_counter[zp.current_actor] -= 1
; if actor_frame_counter[zp.current_actor] <= 0:
; actor_status[zp.current_actor] = PLAYER_ALIVE
; elif a == AMIDAR_NORMAL or a == ORBITER_NORMAL:
; c = ord("0") + zp.current_actor - FIRST_AMIDAR
; else:
; c = None
; zp.sprite_addr = c
evaluate_status nop evaluate_status nop
; update pixel position ; update pixel position
lda actor_status,x lda actor_status,x
@ -100,7 +53,7 @@ evaluate_status nop
sta actor_status,x sta actor_status,x
?end rts ?end rts
; convert tile and sub-tile position into coordinate on screen
get_sprite nop get_sprite nop
lda actor_row,x lda actor_row,x
tay tay
@ -114,17 +67,10 @@ get_sprite nop
clc clc
adc actor_xpixel,x adc actor_xpixel,x
sta actor_x,x sta actor_x,x
?end ?end
rts rts
?game_over
rts
;
;
; ##### Game logic
;
; # Determine which of the 4 directions is allowed at the given row, col ; # Determine which of the 4 directions is allowed at the given row, col
; def get_allowed_dirs(r, c): ; def get_allowed_dirs(r, c):
; addr = mazerow(r) ; addr = mazerow(r)
@ -140,7 +86,6 @@ get_allowed_dirs nop
rts rts
; # See if current tile has a dot ; # See if current tile has a dot
; def has_dot(r, c): ; def has_dot(r, c):
; addr = mazerow(r) ; addr = mazerow(r)
@ -154,11 +99,6 @@ has_dot nop
rts rts
; # clear a dot
; def clear_dot(r, c):
; addr = mazerow(r)
; addr[c] &= ~TILE_DOT
;
; # Determine the tile location given the direction of the actor's movement ; # Determine the tile location given the direction of the actor's movement
; def get_next_tile(r, c, dir): ; def get_next_tile(r, c, dir):
; if dir & TILE_UP: ; if dir & TILE_UP:
@ -270,7 +210,8 @@ get_target_col nop
rts rts
; actor_target_col[zp.current_actor] = target_col ; actor_target_col[zp.current_actor] = target_col
; return current ; return current
;
; def check_midpoint(current): ; def check_midpoint(current):
; # set up decision point flag to see if we have crossed the midpoint ; # set up decision point flag to see if we have crossed the midpoint
; # after the movement ; # after the movement

47
main.s
View File

@ -16,7 +16,7 @@ param_index .ds 1
param_count .ds 1 param_count .ds 1
param_save .ds 1 param_save .ds 1
*= $0010 *= $0080
; scratch areas: these may be modified by child subroutines ; scratch areas: these may be modified by child subroutines
scratch_addr .ds 2 scratch_addr .ds 2
scratch_ptr .ds 2 scratch_ptr .ds 2
@ -28,8 +28,8 @@ scratch_count .ds 1
scratch_col .ds 1 scratch_col .ds 1
scratch_row .ds 1 scratch_row .ds 1
*= $0020 *= $0090
; required variables for HiSprite ; required variables for HiSprite/asmgen
damageindex .ds 1 damageindex .ds 1
damageindex1 .ds 1 damageindex1 .ds 1
damageindex2 .ds 1 damageindex2 .ds 1
@ -40,7 +40,7 @@ damageptr .ds 2
damageptr1 .ds 2 damageptr1 .ds 2
damageptr2 .ds 2 damageptr2 .ds 2
*= $0030 *= $00a0
tdamageindex .ds 1 tdamageindex .ds 1
tdamageindex1 .ds 1 tdamageindex1 .ds 1
tdamageindex2 .ds 1 tdamageindex2 .ds 1
@ -51,7 +51,7 @@ end .ds 2
count .ds 2 count .ds 2
delta .ds 2 delta .ds 2
*= $0040 *= $00b0
; global variables for this program ; global variables for this program
rendercount .ds 1 rendercount .ds 1
drawpage .ds 1 ; pos = page1, neg = page2 drawpage .ds 1 ; pos = page1, neg = page2
@ -65,7 +65,7 @@ temprow .ds 1
tempcol .ds 1 tempcol .ds 1
tempcheck .ds 1 tempcheck .ds 1
*= $0050 *= $00c0
mazeaddr .ds 2 mazeaddr .ds 2
next_level_box .ds 1 next_level_box .ds 1
box_row_save .ds 1 box_row_save .ds 1
@ -77,7 +77,7 @@ frame_count .ds 2
countdown_time .ds 1 countdown_time .ds 1
still_alive .ds 1 still_alive .ds 1
*= $0060 *= $00d0
current_actor .ds 1 current_actor .ds 1
current .ds 1 ; current direction current .ds 1 ; current direction
last_dir .ds 1 last_dir .ds 1
@ -95,7 +95,7 @@ c2 .ds 1
size .ds 1 size .ds 1
dot .ds 1 dot .ds 1
* = $0070 * = $00e0
before .ds 1 before .ds 1
crossed .ds 1 crossed .ds 1
round_robin_index .ds 2 round_robin_index .ds 2
@ -130,21 +130,19 @@ start jsr set_hires ; start with HGR page 1, full screen
sta dst+1 sta dst+1
jsr unpack_lz4 jsr unpack_lz4
;jsr clrscr restart jsr init_once
jsr init_once jsr title_screen
restart jsr title_screen
jsr init_game jsr init_game
jsr game_loop jsr game_loop
check_restart ldx #34 check_restart ldx #34 ; x coord on screen for "GAME"
ldy player_score_row ldy player_score_row
lda #<game_text lda #<game_text
sta scratch_ptr sta scratch_ptr
lda #>game_text lda #>game_text
sta scratch_ptr+1 sta scratch_ptr+1
jsr printstr jsr printstr
ldx #35 ldx #35 ; x coordinate for "OVER"
ldy player_lives_row ldy player_lives_row
lda #<over_text lda #<over_text
sta scratch_ptr sta scratch_ptr
@ -196,8 +194,19 @@ initbackground jsr init_damage
jsr copy2to1 jsr copy2to1
rts rts
; main game loop. Rather than optimize things and unroll all this stuff into
; a single big function, I'm calling a bunch of subroutines in hopes that
; it is easier to follow.
;
; The "actor" is either a player or an enemy, and the actor number is placed
; in the X register. Subroutines must save the X register and restore it
; upon return if they modify it.
;
; All of the game logic routines expect the player/enemy number in X as they
; use it as an index into whatever specific data they need, like position or
; direction.
game_loop nop game_loop nop
inc frame_count inc frame_count ; frame count isn't used for anything other than debugging
bne ?1 bne ?1
inc frame_count+1 inc frame_count+1
?1 jsr userinput ?1 jsr userinput
@ -234,7 +243,8 @@ game_loop nop
; if any player is still alive, the game continues. Once the last player ; if any player is still alive, the game continues. Once the last player
; dies, the countdown timer allows the enemies to continue to move a ; dies, the countdown timer allows the enemies to continue to move a
; little while before the game ends ; little while before the game ends. Returning from the game loop means
; that the game is over.
?alive lda still_alive ?alive lda still_alive
bne ?draw bne ?draw
dec countdown_time dec countdown_time
@ -254,8 +264,7 @@ game_loop nop
jmp game_loop jmp game_loop
; updates the game state based on player status changes
handle_player nop handle_player nop
; if actor_status[zp.current_actor] == PLAYER_REGENERATING: ; if actor_status[zp.current_actor] == PLAYER_REGENERATING:
; # If regenerating, change to alive if the player starts to move ; # If regenerating, change to alive if the player starts to move
@ -283,7 +292,6 @@ handle_player nop
; # only check for points if still alive ; # only check for points if still alive
; check_dots() ; check_dots()
; check_boxes() ; check_boxes()
?dots lda actor_status,x ?dots lda actor_status,x
cmp #PLAYER_ALIVE cmp #PLAYER_ALIVE
bne ?final bne ?final
@ -293,7 +301,6 @@ handle_player nop
; if actor_status[zp.current_actor] != GAME_OVER: ; if actor_status[zp.current_actor] != GAME_OVER:
; still_alive += 1 ; still_alive += 1
; zp.current_actor += 1 ; zp.current_actor += 1
?final lda actor_status,x ?final lda actor_status,x
cmp #GAME_OVER cmp #GAME_OVER
beq ?end beq ?end

View File

@ -78,7 +78,7 @@ input_up lda #TILE_UP
check_down cmp #$af ; down arrow check_down cmp #$af ; down arrow
beq input_down beq input_down
cmp #$bb ; ';' key (dvorak keyboards) cmp #$bb ; ';' key (dvorak keyboards!)
beq input_down beq input_down
cmp #$da ; 'Z' key cmp #$da ; 'Z' key
beq input_down beq input_down
@ -159,6 +159,9 @@ wait_inner ; inner loop: 14 + 2 + 3
bne wait_outer bne wait_outer
rts rts
; Offsets in pixels from upper left corner of sprite to where it should
; appear on screen given the tile coords of where the game logic thinks
; the sprite is.
; [i*7-3 for i in range(40)] ; [i*7-3 for i in range(40)]
player_col_to_x .byte 0, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 101, 108, 115, 122, 129, 136, 143, 150, 157, 164, 171, 178, 185, 192, 199, 206, 213, 220, 227, 234, 241, 248, 248, 248, 248, 248, player_col_to_x .byte 0, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 101, 108, 115, 122, 129, 136, 143, 150, 157, 164, 171, 178, 185, 192, 199, 206, 213, 220, 227, 234, 241, 248, 248, 248, 248, 248,
@ -168,15 +171,14 @@ player_col_to_x .byte 0, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94,
player_row_to_y .byte 0, 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 123, 131, 139, 147, 155, 163, 171, 179 player_row_to_y .byte 0, 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 123, 131, 139, 147, 155, 163, 171, 179
; defines the zone around the midpoint where the player can change to any direction, not just backtracking. ; defines the zone around the midpoint where the player can change to any
; direction, not just backtracking. On the apple, there are 7 x positions but
; 8 y positions
x_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0 x_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0
y_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0, 0 y_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0, 0
;# Returns address of tile in col 0 of row y ; Returns address of tile in col 0 of row y
;def mazerow(y):
; return maze[y]
; row in Y ; row in Y
mazerow lda textrows_l,y mazerow lda textrows_l,y
sta mazeaddr sta mazeaddr
@ -185,8 +187,7 @@ mazerow lda textrows_l,y
rts rts
; text & hgr screen utils ; initialize screen to draw to page 1 (displays page 2)
init_screen_once nop init_screen_once nop
lda #0 lda #0
sta KBDSTROBE sta KBDSTROBE
@ -195,6 +196,8 @@ init_screen_once nop
rts rts
; set an entire column (24 rows) of the HGR screen to the specified character
; on page 1
; character in A, col in X ; character in A, col in X
text_put_col nop text_put_col nop
sta $0400,x ; row 0 sta $0400,x ; row 0
@ -223,6 +226,7 @@ text_put_col nop
sta $07d0,x ; row 23 sta $07d0,x ; row 23
rts rts
; ditto except for page 2
text_put_col2 nop text_put_col2 nop
sta $0800,x ; row 0 sta $0800,x ; row 0
sta $0880,x ; row 1 sta $0880,x ; row 1
@ -259,6 +263,8 @@ textrows_h .byte $04, $04, $05, $05, $06, $06, $07, $07
.byte $04, $04, $05, $05, $06, $06, $07, $07 .byte $04, $04, $05, $05, $06, $06, $07, $07
; sets HGR page 1 to all white using a vertical wipe starting from the
; top moving down
wipeclear1 ldy #0 wipeclear1 ldy #0
sty param_y sty param_y
wipeclear1_loop lda HGRROWS_L,y wipeclear1_loop lda HGRROWS_L,y
@ -285,6 +291,9 @@ wipeclear1_wait nop
bcc wipeclear1_loop bcc wipeclear1_loop
rts rts
; sets HGR page 1 to the contents of page 2 using a vertical wipe starting
; from the top moving down
wipe2to1 ldy #0 wipe2to1 ldy #0
sty param_y sty param_y
wipe2to1_loop lda HGRROWS_H2,y wipe2to1_loop lda HGRROWS_H2,y
@ -314,6 +323,8 @@ wipe2to1_wait nop
bcc wipe2to1_loop bcc wipe2to1_loop
rts rts
; simple (slow, non-optimized) copy of the data from HGR page 2 to HGR page 1
copy2to1 lda #$40 copy2to1 lda #$40
sta ?source+2 sta ?source+2
lda #$20 lda #$20
@ -331,6 +342,9 @@ copy2to1 lda #$40
rts rts
; copy the entire text screen to the current HGR page. The dest_smc address
; is set in the pageflip routine below so they always point to the page
; in back, not the page currently being shown.
copytexthgr nop copytexthgr nop
ldy #0 ; y is rows ldy #0 ; y is rows
copytexthgr_outer copytexthgr_outer
@ -353,6 +367,8 @@ copytexthgr_dest_smc
rts rts
; change pages based on the state of the drawpage variable. drawpage == 0
; is page 1, drawpage == $80 is page 2
pageflip nop pageflip nop
lda drawpage lda drawpage
eor #$80 eor #$80