Files
nes/plat.s

2143 lines
37 KiB
ArmAsm

.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
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
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