diff --git a/tb1_snes/checksum.inc b/tb1_snes/checksum.inc index f0ddd07..84b7d3c 100644 --- a/tb1_snes/checksum.inc +++ b/tb1_snes/checksum.inc @@ -1,2 +1,2 @@ - .word $a4b0 ; Complement of checksum - .word $5b4f ; Unsigned 16-bit sum of ROM + .word $da00 ; Complement of checksum + .word $25ff ; Unsigned 16-bit sum of ROM diff --git a/tb1_snes/level_1.s b/tb1_snes/level_1.s index 34b44f1..a3c77e3 100644 --- a/tb1_snes/level_1.s +++ b/tb1_snes/level_1.s @@ -1,11 +1,86 @@ ; Level 1 +;; GAME PARAMETERS +NUM_MISSILES = 2 +NUM_ENEMIES = 6 +UP_SHIELDS = 32 +WAVE_SIZE = 16 +WAVES_TILL_BOSS = 5 + +;; BONUS_FLAGS +PERFECT_SHIELDS = $80 +PERFECT_KILLS = $40 +PERFECT_AIM = $1 + + ; Page zero locations -joypad1 = $0000 -joypad2 = $0001 -shipx = $0002 -star_scroll = $0003 -star_scroll_h = $0004 ; 0004 too, 16-bit +joypad1 = $00 +joypad2 = $01 +shipx = $02 +star_scroll = $03 +star_scroll_h = $04 ; 0004 too, 16-bit + +HISCORE_1 = $cb +HISCORE_2 = $cc +HISCORE_3 = $cd +HISCORE_H = $ce +HISCORE_L = $cf + +BOSS_X = $d0 +BOSS_XADD = $d1 +BOSS_COUNT = $d2 +BOSS_SMOKE = $d3 +BOSS_EXPLODING = $d4 +BOSS_WAITING = $d5 +BOSS_HITS = $d6 +BOSS_SHOOTING = $d7 +ENEMIES_SPAWNED = $d8 +ENEMY_TYPE = $d9 +ENEMY_WAVE = $da +CURRENT_INIT_X = $db +CURRENT_ENEMY_KIND = $dc +TOTAL_ENEMIES_OUT = $dd +SCROLL = $de +SOUND_ON = $df + +SHIPXADD = $e1 +ENEMY_PL = $e2 +ENEMY_PH = $e3 +MISSILE_PL = $e4 +MISSILE_PH = $e5 +GR_PAGE = $e6 +LEVEL = $e7 +SHIELDS = $e8 +SCOREL = $e9 +SCOREH = $ea +BONUS_FLAGS = $eb +BCD_BYTEH = $ec +BCD_BYTE = $ed + +COL_X1 = $ec +COL_X2 = $ed +COL_X3 = $ee +COL_X4 = $ef + +ENEMY_EXPLODING = $f0 +ENEMY_KIND = $f1 +ENEMY_X = $f2 +ENEMY_Y = $f3 +ENEMY_XADD = $f4 +ENEMY_YADD = $f5 +ENEMY_XMIN = $f6 +ENEMY_XMAX = $f7 + +BETWEEN_DELAY = $f8 +ENEMY_WAIT = $f9 + +STRINGL = $fa +STRINGH = $fb +PARAM2 = $fc +RESULT = $fd +LASTKEY = $fe +TEMP = $ff + .setcpu "65816" @@ -244,18 +319,2038 @@ level1_setup_video: jsr svmw_fade_in - ; init vars + ;==================== + ; setup the high score + ;===================== + + lda #$01 + sta HISCORE_H + lda #$00 + sta HISCORE_L + + ; modestly make me the high scorer + lda #$D6 + sta HISCORE_1 + lda #$CD + sta HISCORE_2 + lda #$D7 + sta HISCORE_3 + +; lda #>(score_string+31) +; sta STRINGH +; lda #<(score_string+31) +; sta STRINGL + jsr print_high_score + + +;========================================================== +; START NEW GAME +;========================================================== + + +do_new_game: + + ;; set up struct pointers + + lda #>missile_0 ; clear the missile struct + sta MISSILE_PH ; should make this clear all BSS + lda #enemy_0 + sta ENEMY_PH + lda #(level_string+9) +; sta STRINGH +; lda #<(level_string+9) +; sta STRINGL + + lda #0 + sta BCD_BYTEH + lda LEVEL + sta BCD_BYTE +; jsr print_bcd_byte + + + ;====================== + ; Print level on screen + ;====================== + +; lda #>(level_string_xy) +; sta STRINGH +; lda #<(level_string_xy) +; sta STRINGL + +; jsr print_text_xy + +; ldx #20 +; jsr wait_X_100msec ; pause for 3 seconds +; bit KEYRESET ; clear keyboard + + ;================================== + ; Enter graphics mode, clear screen + ;================================== + + + + level1_loop: wai ; wait for interrupt + ;/========================\ + ;+ + + ;+ MAIN GAME LOOP + + ;+ + + ;\========================/ + +main_game_loop: +; jsr clear_screen ; clear screen +; jsr show_stars + + + + + +done_scrolling: + + ; ================================ + ; put out new enemies (if needed) + ; ================================ + + inc BETWEEN_DELAY ; inc how long we've delayed + lda BETWEEN_DELAY ; load it in + cmp ENEMY_WAIT ; have we waited long enough? + beq reset_delay + + jmp move_enemies ; if not, go on to movement +reset_delay: + + ; delay==wait, so attempt to put out new enemy + + lda BETWEEN_DELAY + and #$1 + sta BETWEEN_DELAY ; reset delay + + ; special case for boss + + lda #$9 ; if boss, don't keep track of + cmp ENEMY_TYPE ; how many enemies were spawned + bne not_boss_dont_clear + + lda #$1 ; store 1 so we don't increment wave + sta ENEMIES_SPAWNED + +not_boss_dont_clear: + + ; see if we are at a new wave + ; basically, if 16 have been spawned, change + + lda ENEMIES_SPAWNED + and #$0f + bne same_enemy_type ; if not 16 gone by, move on + + ;======================= + ; change the enemy type + + inc ENEMIES_SPAWNED + +; jsr random_number + and #$7 ; get a random number 0-7 + sta ENEMY_TYPE + + inc ENEMY_WAVE + + lda ENEMY_WAVE ; have we gone enough waves to reach boss? + cmp #WAVES_TILL_BOSS + bne not_boss_yet + + lda #$8 + sta ENEMY_TYPE + +not_boss_yet: + + ; set various constants + ; these may be overriden later + + lda #20 + sec + sbc LEVEL + sta ENEMY_WAIT ; enemy_wait=20-level + + ; set kind and init x to be random by default + + lda #$ff + sta CURRENT_ENEMY_KIND + sta CURRENT_INIT_X + + + +same_enemy_type: + + ; find empty enemy slot + + ldy #$0 ; point to enemies[0] + tya + +find_empty_enemy: + pha + lda (ENEMY_PL),Y ; get enemy[y].out + beq add_enemy + + pla + clc + adc #$9 + tay + cpy #(NUM_ENEMIES*9) + bne find_empty_enemy + + + jmp move_enemies ; no empty, slots, move on + + +add_enemy: + pla + + ;============================================== + ; First see if we must wait for enemy to clear + ; types 2 and 8 + + lda ENEMY_TYPE + cmp #$2 + + bne check_type_8 + + lda TOTAL_ENEMIES_OUT + beq change_to_type_3 + jmp move_enemies +change_to_type_3: + lda #$3 + sta ENEMY_TYPE + +; jsr random_number + and #$8 + sta CURRENT_ENEMY_KIND + +; jsr random_number + and #$1F ; mask off so 0-31 + clc + adc #$2 + asl A + sta CURRENT_INIT_X + jmp setup_enemy_defaults + +check_type_8: + + cmp #$8 + beq before_boss_stuff + jmp check_type_9 + +before_boss_stuff: + + ;====================== + ; before boss stuff + + lda TOTAL_ENEMIES_OUT + beq prepare_for_boss + jmp move_enemies + +prepare_for_boss: + + ;=============== + ; HANDLE BONUSES + ;=============== + + ; Set text mode + +; jsr set_page0_text +; jsr HOME + + ; Print "BONUS POINTS" + +; lda #>bonus_string +; sta STRINGH +; lda #bonus_shields +; sta STRINGH +; lda #bonus_kills +; sta STRINGH +; lda #bonus_aim +; sta STRINGH +; lda #no_bonus_string +; sta STRINGH +; lda #34 + + lda CURRENT_INIT_X + bpl store_init_x + +; jsr random_number + and #$1f + clc + adc #$2 + asl + +store_init_x: + iny ; X + sta (ENEMY_PL),Y + + ; enemy_y is always 0 by default + + iny ; Y + lda #$0 + sta (ENEMY_PL),Y + + lda #$0 + iny + sta (ENEMY_PL),Y ; xadd + iny + sta (ENEMY_PL),Y ; yadd + lda #$2 + iny + sta (ENEMY_PL),Y ; xmin + iny + lda #$24 + sta (ENEMY_PL),Y ; ymin + + dey ; xmin + dey ; yadd + dey ; xadd + + + ;=========================================== + ; Enemy specific inits + + lda ENEMY_TYPE + beq enemy_type_0 + cmp #$1 + beq enemy_type_1 + jmp enemy_type_2 + +enemy_type_0: +enemy_type_1: + + ;================================ + ; ENEMY TYPE 0 and 1 + ; diagonal, no wait + ; movement proportional to level + + lda LEVEL ; xadd = level + sta (ENEMY_PL),Y + + iny + + lsr A + ora #$1 + sta (ENEMY_PL),Y ; yadd = level/2 + jmp move_enemies + +enemy_type_2: + ;===================== + ; Enemy Type 2 + ; just a place-holder + ; waits for enemies to die then moves on to 3 + + cmp #$2 + bne enemy_type_3 + jmp move_enemies + + +enemy_type_3: + + cmp #$3 + bne enemy_type_4 + + ;====================== + ; Enemy type 3 + + lda #$1 + sta (ENEMY_PL),Y ; xadd=1 + + iny + + lda LEVEL + sta (ENEMY_PL),Y ; yadd=level + + jmp move_enemies + +enemy_type_4: + + cmp #$4 + bne enemy_type_5 + + + ;========================= + ; Enemy Type 4 + ; Horizontal, then fall + + lda #$2 + sta (ENEMY_PL),Y ; xadd = 2 + + iny + +; jsr random_number + ora #$80 ; set negative + sta (ENEMY_PL),y ; yadd = -(random%128) + ; this means bop back and forth a random + ; time, then drop + + jmp move_enemies + + +enemy_type_5: + cmp #$5 + bne enemy_type_6 + + ;======================== + ; Enemy Type 5 + ; "wiggle" + + lda #$1 + sta (ENEMY_PL),y ; xadd=1 + + iny + lda LEVEL + sta (ENEMY_PL),y ; yadd=2 + + iny +; jsr random_number + and #$0f + clc + adc #$2 + sta (ENEMY_PL),y ; xmin=(rand%16)+2 + + + dey ; yadd + dey ; xadd + dey ; y + dey ;x + asl A + sta (ENEMY_PL),y + iny ;y + iny ; xadd + iny ; yadd + iny ; xmin + +; jsr random_number + and #$0f + clc + adc (ENEMY_PL),Y + adc #$02 + iny + sta (ENEMY_PL),Y ; xmax = xmin+(rand%16)+2 + + jmp move_enemies + + +enemy_type_6: + cmp #$6 + beq enemy_type_7 + cmp #$7 + beq enemy_type_7 + jmp enemy_type_8 +enemy_type_7: + ;===================== + ; Enemy Types 6+7 + ; "Rain" + +; jsr random_number + and #6 + bne no_use_own_x + + dey ; y + dey ; x + + lda shipx + cmp #$2 + bpl shipx_ok + lda #$2 ; stupid bug where gets stuck is < xmin + +shipx_ok: + + asl A + sta (ENEMY_PL),Y ; one-in-four chance we use shipx as X + + iny ; y + iny ; xadd + +no_use_own_x: + + lda #$0 + sta (ENEMY_PL),Y ; xadd=0 + iny + lda #$1 + sta (ENEMY_PL),Y ; yadd = 1 + + jmp move_enemies + +enemy_type_8: +enemy_type_9: + + ;====================== + ; Things flung by boss + + + dey ; y + dey ; x + + lda BOSS_X + clc + adc #$5 + asl A + sta (ENEMY_PL),Y ; enemy_x=boss_x+5 + + iny + lda #$3 + asl A + asl A + sta (ENEMY_PL),Y ; enemy_y=3 + + + iny + lda #$0 + sta (ENEMY_PL),Y ; xadd=0 + + iny + lda #$2 + sta (ENEMY_PL),Y ; yadd=2 + + + + + +move_enemies: + + ;============================================== + ; Move Enemies! (first thing, if no new added) + ;============================================== + + ldy #$0 ; point to enemies[0] +handle_enemies: + + tya + pha ; store y on stack + + lda (ENEMY_PL),Y ; get enemy[y].out + bne load_enemy_zero_page ; if enemy.out then we are good + + jmp skip_to_next_enemy ; enemy is not out, so skip to next + + + + ;========================================== + ; load this enemy stuff into zero page for + ; easier access + ;========================================== + +load_enemy_zero_page: + ldx #ENEMY_EXPLODING +load_to_zero_page: + iny ; point to exploding + lda (ENEMY_PL),Y + sta 0,X ; store to zero page + inx + cpx #(ENEMY_XMAX+1) ; see if reached end + bne load_to_zero_page ; if not keep copying + + ;================================ + ; skip all movement and collision + ; if exploding + ;================================ + + lda ENEMY_EXPLODING + beq move_enemy_x + jmp draw_enemy + + ;================================ + ; Start the enemy movement engine + ;================================ + + + ;======== + ; Move X + ;======== + +move_enemy_x: + clc + lda ENEMY_X ; X + adc ENEMY_XADD ; x+=xadd + sta ENEMY_X + + lsr A + + cmp ENEMY_XMIN ; are we less than xmin? + bmi switch_dir_enemy_x ; if so, switch direction + + cmp ENEMY_XMAX ; are we greater than xmax? + bpl switch_dir_enemy_x ; if so, switch direction + + jmp move_enemy_y + + +switch_dir_enemy_x: + + ; switch X direction + + lda #$0 ; load zero + sec + sbc ENEMY_XADD ; 0 - ENEMY_XADD + sta ENEMY_XADD ; store it back out, negated + jmp move_enemy_x ; re-add it in + + ;======== + ; Move Y + ;======== + +move_enemy_y: + + lda #$0 ; load in zero + cmp ENEMY_YADD ; compare to YADD + + bmi no_y_special_case ; if minus, we have special case + + inc ENEMY_YADD + bne done_enemy_y + + lda #$0 + sta ENEMY_XADD + lda #$2 + sta ENEMY_YADD + + ; increment y + ; is it > 0? + ; if not keep going + ; if so, yadd=level*2 + + jmp done_enemy_y + +no_y_special_case: + clc + lda ENEMY_Y ; get Y + adc ENEMY_YADD ; y+=yadd + sta ENEMY_Y ; store back out + + lsr A + lsr A + + cmp #$12 ; is y<=12? + bmi done_enemy_y ; if so no need to do anything + beq done_enemy_y + + ; off screen + + pla ; pop saved Y off stack + tay + pha ; push y back on stack + + lda #$0 + sta (ENEMY_PL),Y ; set enemy[i].out=0 + + dec TOTAL_ENEMIES_OUT + + lda BONUS_FLAGS + and #<(~PERFECT_KILLS) + sta BONUS_FLAGS + + jmp skip_to_next_enemy ; skip to next enemy + + +done_enemy_y: + + ;=============== + ; Done Movement + ;=============== + + ;====================== + ; Check for Collisions + ;====================== + + + ;================================== + ; Check ENEMY <> MISSILE collision + ;================================== + +check_enemy_missile_collision: + + ldy #$0 +; sty YSAV +check_missile_loop: + lda (MISSILE_PL),Y + beq missile_not_out + + iny ; point to missile.x + lda (MISSILE_PL),Y ; load missile.x + + sta COL_X1 + sta COL_X2 + + lda ENEMY_X + lsr A + sta COL_X3 + clc + adc #3 + sta COL_X4 + + jsr check_inside + + bcc missile_done + +x_in_range: + + iny + lda (MISSILE_PL),Y ; load missile.y + + sta COL_X3 + clc + adc #2 + sta COL_X4 + + lda ENEMY_Y + lsr A + lsr A + sta COL_X1 + clc + adc #1 + sta COL_X2 + + jsr check_inside + + bcc missile_done + +horrible_explosion: + + ; clear missile + +; ldy YSAV + lda #$0 + sta (MISSILE_PL),Y + + ; clear enemy + + lda #$1 + sta ENEMY_EXPLODING + lda #$40 + sta ENEMY_KIND + + jsr inc_score + + jmp draw_enemy + + +missile_done: +missile_not_out: +; ldy YSAV + iny + iny + iny +; sty YSAV + cpy #(NUM_MISSILES*3) + bne check_missile_loop + + ;================================= + ; Done missile <> enemy collision + ;================================= + + ;==================================== + ; check for ship <-> enemy collision + ;==================================== + + lda shipx + sta COL_X3 + clc + adc #8 + sta COL_X4 ; big check is shipx - shipx+8 + + lda ENEMY_X + lsr A + sta COL_X1 + clc + adc #2 + sta COL_X2 ; small check enemy_x - enemy_x+2 + + jsr check_inside ; check if overlap + + bcc draw_enemy ; if not, move ahead + + lda #16 + sta COL_X3 + lda #18 + sta COL_X4 ; big check is 16 - 18 + + lda ENEMY_Y + lsr A + lsr A + sta COL_X1 + clc + adc #$1 + sta COL_X2 ; little check is enemy_y - enemy_y+1 + + jsr check_inside ; check if overlap + + bcc draw_enemy ; if not, move ahead + + ; make the enemy explode + + lda #$1 + sta ENEMY_EXPLODING + lda #$40 + sta ENEMY_KIND + + dec SHIELDS + jsr update_shields ; move shields down + + lda #<(~PERFECT_SHIELDS) ; (~PERFECT_SHIELDS) + and BONUS_FLAGS ; remove perfect shield bonus + sta BONUS_FLAGS + + + ;===================================== + ; Done ship <> enemy collision detect + ;===================================== + + +draw_enemy: + + ; See if the enemy is currently exploding + ; if so, do explosion stuff + +check_enemy_explode: + lda ENEMY_EXPLODING ; load enemy[i].exploding + beq not_exploding ; if 0 then not exploding + +handle_exploding: + +; jsr click ; make some noise + + clc + lda ENEMY_KIND ; move to next step in explosion + adc #$4 + sta ENEMY_KIND + + cmp #$58 ; have we cycles through explosion? + bne draw_enemy_sprite ; if not, we are still exploding + + dec TOTAL_ENEMIES_OUT ; total_enemies_out-- + + pla + tay ; load y + pha + + lda #$0 ; enemy[i].out=0 + sta (ENEMY_PL),Y + + jmp skip_to_next_enemy + + + ; point to enemies_x + ; goto enemies_xy + + +not_exploding: + + + +draw_enemy_sprite: + + ; point to proper sprite + +; lda #>enemy_sprite0 ; point to the missile sprite +; sta STRINGH +; lda #boss_sprite +; sta STRINGH +; lda #smoke_sprite0 ; point to the missile sprite +; sta STRINGH +; lda #laser_sprite0 ; point to the missile sprite +; sta STRINGH +; lda #smoke_sprite0 ; point to the missile sprite +; sta STRINGH +; lda #missile_sprite ; point to the missile sprite +; sta STRINGH +; lda #ship_sprite +; sta STRINGH +; lda #shields_string +; sta STRINGH +; lda #score_string +; sta STRINGH +; lda #level_string +; sta STRINGH +; lda #game_over_string +; sta STRINGH +; lda #(score_string+31) +; sta STRINGH +; lda #<(score_string+31) +; sta STRINGL + jsr print_high_score + +; jsr HOME + + ; print new high score message + +; lda #>new_high_score_string +; sta STRINGH +; lda #high_score_string +; sta STRINGH +; lda #shields_string +; sta STRINGH +; lda #(score_string+7) +; sta STRINGH +; lda #<(score_string+7) +; sta STRINGL + +; tya +; pha ; save Y on stack + +; ldy #$0 + +; lda SCOREH +; sta BCD_BYTEH + +; lda SCOREL +; sta BCD_BYTE +; jsr print_bcd_word + +; pla ; restore Y +; tay + + rts + +;====================== +; print high_score +;====================== + ; location to output to in STRINGH/STRINGL + +print_high_score: +; tya +; pha ; save Y on stack + +; ldy #$0 + +; lda HISCORE_H +; sta BCD_BYTEH + +; lda HISCORE_L +; sta BCD_BYTE +; jsr print_bcd_word + +; pla ; restore Y +; tay + + rts + +;========================================================== +; check inside +;========================================================== + ; Simple collision detection. Have small line x1<->x2 + ; Want to see if it overlaps long line x3<---------->x4 + ; so: + ; if ((x1>x3)&&(x1x3) && (x2