Files
nes/plat.s
2026-02-17 09:53:46 -05:00

928 lines
12 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
scroll_x: .res 1
scroll_y: .res 1
scroll_nmt: .res 1
temp: .res 1
.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
@nmt_update_loop:
lda nmt_update, X
sta $2006
inx
lda nmt_update, X
sta $2006
inx
lda nmt_update, X
sta $2007
cpx nmt_update_len
bcc @nmt_update_loop
lda #0
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
ppu_address_tile:
lda $2002
tya
lsr
lsr
lsr
ora #$20
sta $2006
tya
asl
asl
asl
asl
asl
sta temp
txa
ora temp
sta $2006
rts
ppu_update_tile:
pha
txa
pha
ldx nmt_update_len
tya
lsr
lsr
lsr
ora #$20
sta nmt_update, X
inx
tya
asl
asl
asl
asl
asl
sta temp
pla
ora temp
sta nmt_update, X
inx
pla
sta nmt_update, X
inx
stx nmt_update_len
rts
ppu_update_byte:
pha
tya
pha
ldy nmt_update_len
txa
sta nmt_update, Y
iny
pla
sta nmt_update, Y
iny
pla
sta nmt_update, Y
iny
sty nmt_update_len
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 "ZEROPAGE"
gamepad: .res 1
.segment "CODE"
; gamepad_poll: this reads the gamepad state into the variable labelled "gamepad"
; This only reads the first gamepad, and also if DPCM samples are played they can
; conflict with gamepad reading, which may give incorrect results.
gamepad_poll:
; strobe the gamepad to latch current button state
lda #1
sta $4016
lda #0
sta $4016
; read 8 bytes from the interface at $4016
ldx #8
:
pha
lda $4016
; combine low two bits and store in carry bit
and #%00000011
cmp #%00000001
pla
; rotate carry into gamepad variable
ror
dex
bne :-
sta gamepad
rts
.segment "RODATA"
level:
.byte 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1
.byte 0,1,0,0,1,0,0,0,0,0,0,0,1,1,1,1
.byte 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1
.byte 0,1,0,0,1,0,0,0,0,0,0,0,1,1,1,1
.byte 0,0,1,1,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,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,1,0,1,0,1,0,0,0
.byte 0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0
.byte 0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0
.byte 0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
metatiles:
.byte 0,0,0,0,0
.byte 3,4,5,6,1
example_palette:
.byte $0F,$00,$3D,$20 ; greyscale
.byte $0F,$09,$1A,$16 ; grass
.byte $0F,$15,$26,$37 ; bg0 purple/pink
.byte $0F,$09,$19,$29 ; bg1 green
.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
.segment "ZEROPAGE"
buttons: .res 1
player_pos_x: .res 1
player_pos_y: .res 1
player_vel_x: .res 1
player_vel_y: .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
jump_pressed_last_frame: .res 1
frame_counter: .res 1
.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
jsr init_objects
@loop:
lda frame_counter
clc
adc #1
sta frame_counter
jsr controller
jsr movement
@draw:
jsr update_background
jsr draw_player
jsr ppu_update
jmp @loop
draw_player:
lda player_pos_y
sec
sbc #9
sta oam + (4 * 0) + 0
sta oam + (4 * 1) + 0
lda player_pos_x
sta oam + (4 * 1) + 3
sec
sbc #8
sta oam + (4 * 0) + 3
rts
init_objects:
lda #0
sta var_n
ldx #0
:
lda var_n
clc
adc #8
sta var_n
sta player_pos_y, X ; Set Y position
sta player_pos_x, X ; Set X position
txa
lsr
lsr
sta oam+1, X
inx
inx
inx
inx
cpx #(8*4)
bne :-
lda #0
sta player_vel_x
sta player_vel_y
rts
draw:
@end:
rts
movement:
; Horizontal velocity
ldx #0
jsr btn_right
cmp #0
beq :+
ldx #1
lda #3
sta player_vel_x
:
jsr btn_left
cmp #0
beq :+
ldx #1
lda #253
sta player_vel_x
:
cpx #1
beq @end
lda frame_counter
and #1
cmp #0
bne @end
lda #0
cmp player_vel_x
beq @end ; If player_vel_x = 0, skip decay
; If player_vel_x > 0, decrement
bpl :+
ldx player_vel_x
dex
stx player_vel_x
jmp @end
:
; Else if player_vel_x < 0, increment
ldx player_vel_x
inx
stx 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 #250
sta player_vel_y
@fail_jump:
jsr btn_a
sta jump_pressed_last_frame
; Gravity
lda frame_counter
and #1
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
; 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
:
; Apply X velocity
lda player_pos_x
clc
adc player_vel_x
sta player_pos_x
rts
update_background:
rts
;lda $2002
;lda #$20
;sta $2006
;lda #$00
;sta $2006
ldx #1
ldy #1
jsr ppu_update_tile
lda #1
sta $2007
rts
setup_background:
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 :--
lda $2002
lda #$20
sta $2006
lda #$00
sta $2006
ldy #0
@row_start:
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
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
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
; clear attributes
lda #0
ldx #64 ; 64 bytes
:
lda #%11100100
sta $2007
dex
bne :-
rts
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 :--
; set all attributes to 0
lda #0
ldx #64 ; 64 bytes
:
txa
and #%00000011
asl
asl
asl
asl
sta temp
txa
and #%00000011
ora temp
lda #0
sta $2007
dex
bne :-
; fill in an area in the middle with 1/2 checkerboard
lda #1
ldy #0 ; start at row 8
:
pha ; temporarily store A, it will be clobbered by ppu_address_tile routine
ldx #8 ; start at column 8
jsr ppu_address_tile
pla ; recover A
ldx #8
:
sta $2007
eor #$3
inx
cpx #(32-8)
bcc :-
eor #$3
iny
cpy #(30-8)
bcc :--
lda #$24
sta $2006
lda #$00
sta $2006
lda #$00
ldy #30
:
ldx #32
:
sta $2007
clc
adc #1
and #3
dex
bne :-
clc
adc #1
and #3
dey
bne :--
lda #0
ldy #4
:
ldx #16
:
sta $2007
dex
bne :-
clc
adc #%01010101
dey
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