.segment "HEADER" INES_MAPPER = 0 INES_MIRROR = 1 INES_SRAM = 0 .byte 'N', 'E', 'S', $1A .byte $02 .byte $01 .byte INES_MIRROR .byte (INES_MAPPER & %11110000) .byte $0, $0, $0, $0, $0, $0, $0, $0 .segment "TILES" .incbin "background.chr" .incbin "sprite.chr" .segment "VECTORS" .word nmi .word reset .word irq .segment "CODE" reset: sei lda #0 sta $2000 lda #0 sta $2001 sta $4015 sta $4010 lda #40 sta $4017 cld ldx #$FF txs bit $2002 : bit $2002 bpl :- lda #0 ldx #0 : sta $0000, X sta $0100, X sta $0200, X sta $0300, X sta $0400, X sta $0500, X sta $0600, X sta $0700, X inx bne :- lda #255 ldx #0 : sta oam, X inx inx inx inx bne :- : bit $2002 bpl :- lda #%10101000 ; NMI enable, 8x16 sprites, ignored due to 8x16 mode sta $2000 jmp main .segment "ZEROPAGE" nmi_lock: .res 1 nmi_count: .res 1 nmi_ready: .res 1 nmt_update_len: .res 1 nmt_column: .res 1 nmt_update_frame: .res 1 scroll_x: .res 1 scroll_y: .res 1 scroll_nmt: .res 1 scroll_page: .res 1 temp: .res 1 PPUCTRL_default = %10101000 .segment "BSS" nmt_update: .res 256 palette: .res 32 .segment "OAM" oam: .res 256 .segment "CODE" nmi: pha txa pha tya pha lda nmi_lock beq :+ jmp @nmi_end : lda #1 sta nmi_lock lda nmi_ready bne :+ jmp @ppu_update_end : cmp #2 bne :+ lda #%00000000 sta $2001 ldx #0 stx nmi_ready jmp @ppu_update_end : ldx #0 stx $2003 lda #>oam sta $4014 lda #%10101000 ; NMI enable, 8x16 sprites, ignored due to 8x16 mode sta $2000 lda $2002 lda #$3F sta $2006 stx $2006 ldx #0 : lda palette, X sta $2007 inx cpx #32 bcc :- ldx #0 cpx nmt_update_len bcs @scroll lda PPUCTRL_default ;ora #%00000100 sta $2000 ldy #0 ldx #0 @nmt_update_loop: lda nmt_update_frame cmp #0 bne :+ lda nmt_update+0, X sta $2006 lda nmt_update+1, X sta $2006 lda nmt_update+2, X sta $2007 lda nmt_update+3, X sta $2007 jmp :++ : lda nmt_update+0, X sta $2006 lda nmt_update+1, X clc adc #32 sta $2006 lda nmt_update+4, X sta $2007 lda nmt_update+5, X sta $2007 : inx inx inx inx inx inx iny cpy nmt_update_len bcc @nmt_update_loop lda PPUCTRL_default sta $2000 inc nmt_update_frame lda nmt_update_frame cmp #2 bne :+ lda #0 sta nmt_update_frame sta nmt_update_len : @scroll: lda scroll_nmt and #%00000011 ora #%10101000 sta $2000 lda scroll_x sta $2005 lda scroll_y sta $2005 lda #%00011110 sta $2001 ldx #0 stx nmi_ready @ppu_update_end: lda #0 sta nmi_lock @nmi_end: pla tay pla tax pla rti .segment "CODE" irq: rti .segment "CODE" ppu_update: lda #1 sta nmi_ready : lda nmi_ready bne :- rts ppu_skip: lda nmi_count : cmp nmi_count beq :- rts ppu_off: lda #2 sta nmi_ready : lda nmi_ready bne :- rts PAD_A = $01 PAD_B = $02 PAD_SELECT = $04 PAD_START = $08 PAD_U = $10 PAD_D = $20 PAD_L = $40 PAD_R = $80 .segment "RODATA" default_level: .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $00,$00,$00,$00,$00,$00,$00,$81,$00,$81,$00,$81,$00,$81,$00,$00 ;.byte $82,$83,$00,$00,$00,$00,$00,$00,$81,$00,$0C,$00,$81,$00,$00,$00 ;.byte $84,$85,$00,$00,$00,$00,$00,$81,$00,$81,$00,$81,$00,$81,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$0C,$00,$0C,$00,$0C,$00,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$81,$00,$81,$00,$81,$00,$81,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$81,$00,$0C,$00,$81,$00,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$81,$00,$81,$00,$81,$00,$81,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$8C,$87 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $0F,$8C,$87,$87,$87,$87,$87,$87,$87,$87,$87,$87,$87,$87,$87,$87 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;.byte $0F,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 default_level2: .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$8C,$8C,$8C,$8C,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$09,$01,$01,$01,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$8C,$8C,$8C,$01,$06,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$09,$01,$01,$01,$8C,$00,$00,$00,$00,$00,$82,$83,$00,$00,$00 .byte $00,$8C,$8C,$8C,$8C,$8C,$00,$00,$00,$00,$00,$84,$85,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$82,$83 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$84,$85 .byte $00,$00,$00,$00,$00,$00,$00,$00,$82,$83,$00,$0F,$0E,$00,$0F,$0E .byte $87,$8C,$00,$00,$00,$00,$00,$00,$84,$85,$00,$0F,$0E,$00,$0F,$0E .byte $00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$0F,$0E,$00,$0F,$0E .byte $00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$0F,$0E,$00,$0F,$0E .byte $87,$87,$87,$87,$87,$8C,$00,$00,$0F,$0E,$00,$0F,$0E,$00,$0F,$0E .byte $00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$8A,$8A,$8A,$8A,$0E .byte $00,$00,$00,$00,$00,$00,$00,$00,$0F,$0E,$00,$8B,$8B,$8B,$8B,$0E ;level: ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,1,0,0,1,0,0,0,0,1,0,0,2,7,7,3 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,9 ;.byte 0,1,0,0,1,7,7,7,7,1,7,7,6,0,0,9 ;.byte 0,0,1,1,0,0,0,0,0,1,0,0,4,8,8,5 ;.byte 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10 ;.byte 0,0,1,0,0,0,0,1,0,0,0,11,11,11,11,11 ;.byte 0,1,2,3,4,5,6,7,8,9,0,11,11,11,11,11 ;.byte 0,0,0,0,0,0,0,0,0,0,0,11,11,11,11,11 metatiles: .byte 0,0,0,0,0 .byte $14,$15,$16,$17,%00000000 ; 1: Full block .byte $14,$18,$1B,$00,%00000000 ; 2: up / left .byte $18,$15,$00,$19,%00000000 ; 3: up / right .byte $1B,$00,$16,$1A,%00000000 ; 4: down / left .byte $00,$19,$1A,$17,%00000000 ; 5: down / right .byte $1B,$00,$1B,$00,%00000000 ; 6: left .byte $18,$18,$00,$00,%00000000 ; 7: up .byte $00,$00,$1A,$1A,%00000000 ; 8: down .byte $00,$19,$00,$19,%00000000 ; 9: right .byte $10,$11,$12,$13,%00000010 ; A: grass top .byte $12,$12,$12,$12,%00000010 ; B: grass bottom .byte $14,$15,$16,$17,%00000001 ; C: Intangible full block .byte $01,$01,$01,$01,%00000001 ; D: Intangible grey .byte $1B,$00,$1B,$00,%00000000 ; E: Intangible left .byte $00,$19,$00,$19,%00000000 ; F: Intangible right example_palette: .byte $0F,$00,$3D,$20 ; greyscale .byte $0F,$3D,$30,$00 ; whitescale .byte $0F,$09,$1A,$16 ; grass .byte $0F,$15,$26,$37 ; bg0 purple/pink .byte $0F,$2D,$10,$3D ; reboot bottom .byte $0F,$2D,$10,$2C ; reboot eye .byte $0F,$01,$11,$21 ; bg2 blue .byte $0F,$00,$10,$30 ; bg3 greyscale ; .byte $0F,$18,$28,$38 ; sp0 yellow ; .byte $0F,$14,$24,$34 ; sp1 purple ; .byte $0F,$1B,$2B,$3B ; sp2 teal ; .byte $0F,$12,$22,$32 ; sp3 marine terrain: ; page pos struct .byte $00, $00, $00 .segment "ZEROPAGE" buttons: .res 1 player_pos_x: .res 1 player_nmt_x: .res 1 player_pos_y: .res 1 player_subpos_x: .res 1 ; XXXXYYYY | first 4 bits X subposition, last 4 bits Y player_subpos_y: .res 1 ; XXXXYYYY | first 4 bits X subposition, last 4 bits Y player_vel_x: .res 1 ; +PPPSSSS | first bit sign, next 3 pixels, last subpixels player_vel_y: .res 1 ; in subpixels player_status: .res 1 ; 76543210 | 0: facing (0 right, 1 left) ; | 1: nametable X ; | 2: nametable Y ; | 7: talking horizontal_speed: .res 1 camera_speed: .res 1 cursor_x: .res 1 cursor_y: .res 1 temp_x: .res 1 temp_y: .res 1 temp_mul: .res 1 var_m: .res 1 var_n: .res 1 var_o: .res 1 var_p: .res 1 var_wide1:.res 2 var_wide2:.res 2 jump_pressed_last_frame: .res 1 frame_counter: .res 1 last_frame_jumped: .res 1 last_frame_moving: .res 1 column_pattern_pointer: .res 2 tile_update_pos: .res 1 pointer: .res 2 .segment "BSS" level: .res 240 level2: .res 240 room_buffer: .res 240 .segment "CODE" main: ldx #0 : lda example_palette, X sta palette, X inx cpx #32 bcc :- jsr setup_background jsr ppu_update lda #$40 sta cursor_x sta cursor_y lda #0 sta frame_counter sta last_frame_jumped jsr init_objects jsr update_room_buffer @loop: lda $2002 lda frame_counter clc adc #1 sta frame_counter jsr controller jsr movement jsr move_camera @draw: jsr draw_player jsr ppu_update jmp @loop move_camera: jmp @no lda player_pos_x clc adc #240 cmp scroll_x bmi :++ lda #1 clc adc scroll_x cmp scroll_x sta scroll_x bcs :+ lda scroll_nmt eor #%00000001 sta scroll_nmt : : rts @no: jsr btn_select cmp #0 beq :+ lda #%11111111 sta camera_speed jmp @end : jsr btn_start cmp #0 beq :+ lda #1 sta camera_speed : @end: jsr add_scroll_x jsr check_column_update rts draw_player: lda player_pos_y sec sbc #1 sta oam + (4 * 2) + 0 sta oam + (4 * 3) + 0 sec sbc #16 sta oam + (4 * 0) + 0 sta oam + (4 * 1) + 0 ; RIGHT HALF LOGIC ; see if we can skip drawing the player because it's off screen lda player_pos_x sec sbc scroll_x cmp player_pos_x ; if underflow, nametable bits must be different to continue beq @skip_bcs_right bcs :+ @skip_bcs_right: lda player_nmt_x eor scroll_nmt bne @hide_player_right jmp :++ : ; if nothing, nametable bits must be same to continue lda player_nmt_x eor scroll_nmt beq @hide_player_right : jmp @end_hide_player_right @hide_player_right: lda player_status and #%00000001 cmp #0 bne :+ lda #250 sta oam + (4 * 1) + 0 sta oam + (4 * 3) + 0 jmp :++ : lda #250 sta oam + (4 * 0) + 0 sta oam + (4 * 2) + 0 : @end_hide_player_right: ; LEFT HALF LOGIC ; store what nametable the player's left half is on in var_n ; store what position the player's left half is at in var_p lda player_pos_x sec sbc #8 sta var_p bcs :+ lda player_nmt_x eor #%00000001 sta var_n jmp :++ : lda player_nmt_x sta var_n : lda var_p sec sbc scroll_x cmp var_p ; if underflow, nametable bits must be different to continue beq @skip_bcs_left bcs :+ @skip_bcs_left: lda var_n eor scroll_nmt bne @hide_player_left jmp :++ : ; if nothing, nametable bits must be same to continue lda var_n eor scroll_nmt beq @hide_player_left : jmp @end_hide_player_left @hide_player_left: lda player_status and #%00000001 cmp #0 bne :+ lda #250 sta oam + (4 * 0) + 0 sta oam + (4 * 2) + 0 jmp :++ : lda #250 sta oam + (4 * 1) + 0 sta oam + (4 * 3) + 0 : @end_hide_player_left: lda player_status and #%00000001 cmp #0 bne :+ lda player_pos_x sec sbc scroll_x sta oam + (4 * 1) + 3 sta oam + (4 * 3) + 3 sec sbc #8 sta oam + (4 * 0) + 3 sta oam + (4 * 2) + 3 jmp :++ : lda player_pos_x sec sbc scroll_x sta oam + (4 * 0) + 3 sta oam + (4 * 2) + 3 sec sbc #8 sta oam + (4 * 1) + 3 sta oam + (4 * 3) + 3 : lda player_status and #%10000000 cmp #0 bne :+ lda #$45 jmp :++ : lda #$49 : sta oam + (4 * 0) + 1 clc adc #2 sta oam + (4 * 1) + 1 lda #$65 sta oam + (4 * 2) + 1 lda #$67 sta oam + (4 * 3) + 1 lda player_status and #%00000001 cmp #0 beq :+ lda #%01000000 jmp :++ : lda #%00000000 : ora #%00000001 sta oam + (4 * 0) + 2 ora #%00000001 sta oam + (4 * 1) + 2 and #%11111100 sta oam + (4 * 2) + 2 and #%11111100 sta oam + (4 * 3) + 2 rts init_objects: lda #96 sta player_pos_x ; Set X position lda #128 sta player_pos_y ; Set Y position lda #0 sta player_vel_x sta player_vel_y sta player_status sta scroll_nmt sta player_nmt_x lda #%00101000 sta horizontal_speed lda #0 sta camera_speed sta tile_update_pos rts draw: @end: rts movement: ; Talking ldx #0 jsr btn_b cmp #0 beq :+ lda player_status ora #%10000000 sta player_status jmp :++ : lda player_status and #%01111111 sta player_status : ; Horizontal velocity ldx #0 jsr btn_right cmp #0 beq :+ ldx #1 lda horizontal_speed sta player_vel_x lda player_status and #%11111110 sta player_status lda frame_counter sta last_frame_moving : jsr btn_left cmp #0 beq :+ ldx #1 lda horizontal_speed eor #%11111111 clc adc #1 sta player_vel_x lda player_status ora #%00000001 sta player_status lda frame_counter sta last_frame_moving : cpx #1 ; skip decay if right or left pressed beq @end lda frame_counter clc sbc last_frame_moving and #3 cmp #0 bne @end lda player_vel_x and #%01111111 cmp #0 beq @end ; If player_vel_x = 0, skip decay lda player_vel_x and #%10000000 ; If player_vel_x positive, decrement bne :++ lda player_vel_x sec sbc #16 sta player_vel_x cmp #0 bpl :+ ; negative, clamp to 0 lda #0 sta player_vel_x : jmp @end : ; Else if player_vel_x < 0, increment lda player_vel_x clc adc #16 sta player_vel_x cmp #0 beq :+ bmi :+ ; positive, clamp to 0 lda #0 sta player_vel_x : @end: ; Jump jsr btn_a cmp #0 beq @fail_jump ; If jump not pressed, forget it lda jump_pressed_last_frame cmp #1 beq @fail_jump ; If jump last frame, forget it ; Jump newly pressed this frame lda #249 sta player_vel_y lda frame_counter sta last_frame_jumped @fail_jump: jsr btn_a sta jump_pressed_last_frame ; Gravity lda frame_counter sec sbc last_frame_jumped and #%00000001 cmp #0 bne :+ lda player_vel_y clc adc #1 ; Add 2 to velocity sta player_vel_y : ; Apply Y velocity lda player_pos_y clc adc player_vel_y sta player_pos_y ; Apply X velocity ; Position lda player_vel_x and #%10000000 cmp #0 bne :+ ; 0: right lda player_vel_x sta var_o ; store initial velocity in o and #%01111111 clc ror clc ror clc ror clc ror clc adc player_pos_x sta player_pos_x ; add pixels bcc @end_pixel_overflow_right lda player_nmt_x eor #%00000001 sta player_nmt_x @end_pixel_overflow_right: lda player_vel_x ; A contains signed subpixels clc adc player_subpos_x ; add subposition and velocity subpixels and #%00001111 cmp player_subpos_x ; if A (result) is less than current subpos, overflow occurred sta player_subpos_x beq @end_add_subpos bcs @end_add_subpos ; so skip if subpos is smaller than result lda player_pos_x clc adc #1 sta player_pos_x bcc @end_subpixel_overflow_right lda player_nmt_x eor #%00000001 sta player_nmt_x @end_subpixel_overflow_right: @end_add_subpos: jmp :++ : ; 1: left lda player_vel_x sta var_o sec ror sec ror sec ror sec ror ora #%10000000 clc adc player_pos_x sta player_pos_x ; add pixels bcs @end_pixel_overflow_left lda player_nmt_x eor #%00000001 sta player_nmt_x @end_pixel_overflow_left: ; :| lda player_pos_x clc adc #1 sta player_pos_x ; add... correction? bcc @end_correction lda player_nmt_x eor #%00000001 sta player_nmt_x @end_correction: lda player_vel_x ; A contains signed subpixels clc adc player_subpos_x ; add subposition and velocity subpixels and #%00001111 cmp player_subpos_x ; if A (result) is greater than current subpos, underflow occurred sta player_subpos_x beq @end_sub_subpos bcc @end_sub_subpos ; so skip if subpos is bigger than result lda player_pos_x sec sbc #1 sta player_pos_x bcs @end_subpixel_overflow_left lda player_nmt_x eor #%00000001 sta player_nmt_x @end_subpixel_overflow_left: @end_sub_subpos: : ; load x pos, filter blocks, check up and down lda player_vel_y cmp #0 bmi :+ ; branch if moving up jsr downward_collision_check jsr left_collision_check jsr right_collision_check jsr upward_collision_check jmp :++ : jsr upward_collision_check jsr left_collision_check jsr right_collision_check jsr downward_collision_check : rts ; Keep grounded cmp #200 bcc :+ lda #0 sta player_vel_y lda #200 sta player_pos_y : ; Bonk lda #8 cmp player_pos_y bcc :+ lda #0 sta player_vel_y lda #8 sta player_pos_y : rts setup_background: ldx #0 : lda default_level, X sta level, X inx cpx #240 bne :- ldx #0 : lda default_level2, X sta level2, X inx cpx #240 bne :- ldx #0 : lda default_level, X sta level, X inx cpx #240 bne :- ldx #0 : lda default_level2, X sta level2, X inx cpx #240 bne :- lda $2002 ; reset latch lda #$20 sta $2006 lda #$00 sta $2006 ; empty nametable lda #0 ldy #30 ; 30 rows : ldx #32 ; 32 columns : sta $2007 dex bne :- dey bne :-- ; ------------- Nametable 1: level lda $2002 lda #$20 sta $2006 lda #$00 sta $2006 ldy #0 @row_start_nmt1: ldx #0 : txa pha ; index is (Y * 16) + X stx temp_x lda #16 jsr mul_y clc adc temp_x tax lda level, X and #%01111111 ldx #5 jsr mul_x tax lda metatiles+0, X sta $2007 lda metatiles+1, X sta $2007 pla tax inx cpx #16 bne :- ; again ldx #0 : txa pha ; index is (Y * 16) + X stx temp_x lda #16 jsr mul_y clc adc temp_x tax lda level, X and #%01111111 ldx #5 jsr mul_x tax lda metatiles+2, X sta $2007 lda metatiles+3, X sta $2007 pla tax inx cpx #16 bne :- iny cpy #15 bne @row_start_nmt1 ; clear attributes ;lda #0 ;ldx #64 ; 64 bytes ;: ; lda #%11100100 ; ; sta $2007 ; dex ; bne :- lda #0 sta $50 ldy #0 : tya pha ldx #0 : tya pha txa pha tya asl asl asl asl asl sta temp_y txa asl clc adc temp_y tax lda level, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 sta temp_x lda level+1, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl adc temp_x sta temp_x lda level+16, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl asl asl adc temp_x sta temp_x lda level+17, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl asl asl asl asl adc temp_x sta $2007 ldx $50 inx stx $50 sta $60, X pla tax pla tay inx cpx #8 beq @left_jump jmp :- @left_jump: pla tay iny cpy #8 beq :+ jmp :-- : ; ---------------- End nametable 1: Level ; ---------------- Begin nametable 2: Level 2 lda $2002 lda #$24 sta $2006 lda #$00 sta $2006 ldy #0 @row_start_nmt2: ldx #0 : txa pha ; index is (Y * 16) + X stx temp_x lda #16 jsr mul_y clc adc temp_x tax lda level2, X and #%01111111 ldx #5 jsr mul_x tax lda metatiles+0, X sta $2007 lda metatiles+1, X sta $2007 pla tax inx cpx #16 bne :- ; again ldx #0 : txa pha ; index is (Y * 16) + X stx temp_x lda #16 jsr mul_y clc adc temp_x tax lda level2, X and #%01111111 ldx #5 jsr mul_x tax lda metatiles+2, X sta $2007 lda metatiles+3, X sta $2007 pla tax inx cpx #16 bne :- iny cpy #15 bne @row_start_nmt2 ; clear attributes ;lda #0 ;ldx #64 ; 64 bytes ;: ; lda #%11100100 ; ; sta $2007 ; dex ; bne :- lda #0 sta $50 ldy #0 : tya pha ldx #0 : tya pha txa pha tya asl asl asl asl asl sta temp_y txa asl clc adc temp_y tax lda level2, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 sta temp_x lda level2+1, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl adc temp_x sta temp_x lda level2+16, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl asl asl adc temp_x sta temp_x lda level2+17, X and #%01111111 tay lda #5 jsr mul_y tay lda metatiles+4, Y and #%00000011 asl asl asl asl asl asl adc temp_x sta $2007 ldx $50 inx stx $50 sta $60, X pla tax pla tay inx cpx #8 beq @right_jump jmp :- @right_jump: pla tay iny cpy #8 beq :+ jmp :-- : rts add_scroll_x: lda camera_speed bmi @left clc adc scroll_x cmp scroll_x sta scroll_x bcs :+ lda scroll_nmt eor #%00000001 sta scroll_nmt ldx scroll_page inx sta scroll_page : rts @left: clc adc scroll_x cmp scroll_x sta scroll_x bcc :+ lda scroll_nmt eor #%00000001 sta scroll_nmt ldx scroll_page dex sta scroll_page : rts check_column_update: clc lda scroll_nmt beq :+ sec : lda scroll_x ror lsr lsr lsr ; Thought: Level layout will be decoded from somewhere into the room_buffer_r or _l buffers, then they will be indexed into by this to load their column patterns cmp tile_update_pos bne :+ jsr load_column_pattern ; Apply pattern jsr update_nmt_column ; Increment tile_update_pos lda tile_update_pos clc adc #1 and #%00011111 sta tile_update_pos : rts load_column_pattern: ; Based on tile update pos r, use that row to index in to the room buffer. Columns in the room buffer are stored as rows for sequential access. ; ; IDEA: Do we only need one tile update position? If for example we're going right and updating there, we don't need to do anything going left until that point is reached again on the left side. But it's the same point. It's the "seam" in the level. Maybe just an offset by one or something. lda #room_buffer sta column_pattern_pointer+1 ; Since column pattern pointer points to the current column we need to draw, we start it at the beginning of the room buffer (which stores columns as rows), then we add 16 (going to the next "row" in memory which is the next column in reality) how many ever times in column_pattern_pointer to point to the correct row. From there, anything can just loop 16 times from that pointer to get the currently drawing row. lda tile_update_pos and #%00001111 tax : lda column_pattern_pointer clc adc #16 sta column_pattern_pointer lda column_pattern_pointer+1 adc #0 sta column_pattern_pointer+1 dex cpx #0 bne :- rts update_nmt_column: ; X represents the row of the current tile being updated ldx #0 @row_loop: lda tile_update_pos clc ; Add offset in tiles from left edge of screen adc #16 sta var_n ; Nametable base lda #$20 sta var_o lda #level sta pointer+1 lda var_n and #%00010000 cmp #0 beq:+ lda pointer clc adc #240 sta pointer lda pointer+1 adc #0 sta pointer+1 lda #$24 sta var_o : lda var_n and #%00001111 sta var_p lda #16 jsr mul_x clc adc var_p tay ; Specifies what tile gets written to the column ; ; T------> ;lda column_pattern ;ror ;sta var_wide1 ; >-----BX ;lda column_pattern+1 ;ror ;sta var_wide1+1 ;stx var_m ;: ;lda var_wide1+1 ;rol ;sta var_wide1+1 ;lda var_wide1 ;rol ;sta var_wide1 ;dec var_m ;bpl :- ;lda var_wide1 ;and #%10000000 ;cmp #0 ;beq :+ ; What metatile do we load for a non-empty shape? ;lda #%10000001 ;: ; Preserve Y (our index in to level) tya pha ; Transfer X to Y (our current row in the updated column) txa tay ; Instead of all that nonsense constructing the tile that we want to draw, just index in to the column_pattern_pointer ; Storing in var wide 1 because I know it's not used lda (column_pattern_pointer), Y sta var_wide1 ; Restore Y pla tay ; Write our new metatile to level (for collision mostly) lda var_wide1 sta (pointer), Y ; ...and actually perform a nametable update and #%01111111 ldy #5 jsr mul_y tay ; Preserve X txa pha lda #6 jsr mul_x tax lda metatiles+0, Y sta nmt_update+2, X lda metatiles+1, Y sta nmt_update+3, X lda metatiles+2, Y sta nmt_update+4, X lda metatiles+3, Y sta nmt_update+5, X ; Recover X (to Y) pla tay lsr lsr ; Add nametable base clc adc var_o sta nmt_update+0, X lda #64 jsr mul_y clc adc var_p clc adc var_p sta nmt_update+1, X tya tax inx cpx #15 beq :+ jmp @row_loop : lda #15 sta nmt_update_len rts update_room_buffer: ldx #0 : lda default_level2, X sta room_buffer, X inx cpx #240 bne :- rts mul_x: cmp #0 beq @zero cpx #0 beq @zero sta temp_mul txa pha lda #0 : clc adc temp_mul dex bne :- sta temp_mul pla tax lda temp_mul rts @zero: lda #0 rts mul_y: cmp #0 beq @zero cpy #0 beq @zero sta temp_mul tya pha lda #0 : clc adc temp_mul dey bne :- sta temp_mul pla tay lda temp_mul rts @zero: lda #0 rts controller: lda #1 sta $4016 sta buttons lda #0 sta $4016 : lda $4016 lsr rol buttons bcc :- rts btn_right: lda buttons and #%00000001 rts btn_left: lda buttons and #%00000010 lsr rts btn_down: lda buttons and #%00000100 lsr lsr rts btn_up: lda buttons and #%00001000 lsr lsr lsr rts btn_start: lda buttons and #%00010000 lsr lsr lsr lsr rts btn_select: lda buttons and #%00100000 lsr lsr lsr lsr lsr rts btn_b: lda buttons and #%01000000 lsr lsr lsr lsr lsr lsr rts btn_a: lda buttons and #%10000000 lsr lsr lsr lsr lsr lsr lsr rts downward_collision_check: lda #0 sta var_n @begin_check: lda player_pos_x ldx var_n cpx #0 bne :+ sec sbc #5 jmp :++ : clc adc #4 : ; divide by 16 clc ror clc ror clc ror clc ror sta temp_x lda player_pos_y ; divide by 16 clc adc #8 clc ror clc ror clc ror clc ror tay lda #16 jsr mul_y adc temp_x tax lda player_nmt_x cmp #0 beq :+ lda level2, X jmp :++ : lda level, X : and #%10000000 cmp #0 beq :+ tya asl asl asl asl sec sbc #8 cmp player_pos_y bcs :+ sta player_pos_y lda #0 sta player_vel_y : ldx var_n inx stx var_n cpx #1 beq @begin_check rts upward_collision_check: lda #0 sta var_n @begin_check: lda player_pos_x ldx var_n cpx #0 bne :+ sec sbc #5 jmp :++ : clc adc #4 : ; divide by 16 clc ror clc ror clc ror clc ror sta temp_x lda player_pos_y ; divide by 16 sec sbc #8 clc ror clc ror clc ror clc ror tay lda #16 jsr mul_y adc temp_x tax lda player_nmt_x cmp #0 beq :+ lda level2, X jmp :++ : lda level, X : and #%10000000 cmp #0 beq :+ iny tya asl asl asl asl clc adc #8 cmp player_pos_y bcc :+ sta player_pos_y lda #0 sta player_vel_y : ldx var_n inx stx var_n cpx #1 beq @begin_check rts right_collision_check: lda #0 sta var_n @begin_check: lda player_pos_x ; divide by 16 clc adc #8 clc ror clc ror clc ror clc ror sta temp_x lda player_pos_y ldx var_n cpx #0 bne :+ sec sbc #7 jmp :++ : clc adc #6 : ; divide by 16 clc ror clc ror clc ror clc ror tay lda #16 jsr mul_y adc temp_x tax lda player_nmt_x cmp #0 beq :+ lda level2, X jmp :++ : lda level, X : and #%10000000 cmp #0 beq :+ lda temp_x asl asl asl asl sec sbc #8 cmp player_pos_x bcs :+ sta player_pos_x lda #0 sta player_vel_x : ldx var_n inx stx var_n cpx #1 beq @begin_check rts left_collision_check: lda #0 sta var_n @begin_check: lda player_pos_x ; divide by 16 sec sbc #8 clc ror clc ror clc ror clc ror sta temp_x lda player_pos_y ldx var_n cpx #0 bne :+ sec sbc #7 jmp :++ : clc adc #6 : ; divide by 16 clc ror clc ror clc ror clc ror tay lda #16 jsr mul_y adc temp_x tax lda player_nmt_x cmp #0 beq :+ lda level2, X jmp :++ : lda level, X : and #%10000000 cmp #0 beq :+ lda temp_x clc adc #1 asl asl asl asl clc adc #8 cmp player_pos_x bcc :+ sta player_pos_x lda #0 sta player_vel_x : ldx var_n inx stx var_n cpx #1 beq @begin_check rts