Files
nes/plat.s
2026-02-16 00:27:56 -05:00

765 lines
9.8 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 #%00100000 ; 8x16 sprites
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 #%10001000
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 #%10001000
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 #%10001000
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,0,0,0,0
.byte 0,1,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,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0
.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,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,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
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
vel_x: .res 1
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
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 ppu_update
jmp @loop
init_objects:
lda #0
sta var_n
ldx #0
:
lda var_n
clc
adc #8
sta var_n
sta oam+0, X ; Set Y position
sta oam+3, X ; Set X position
txa
lsr
lsr
sta oam+1, X
inx
inx
inx
inx
cpx #(8*4)
bne :-
lda #0
sta vel_x
sta vel_y
rts
draw:
@end:
rts
movement:
; Horizontal velocity
ldx #0
jsr btn_right
cmp #0
beq :+
ldx #1
lda #3
sta vel_x
:
jsr btn_left
cmp #0
beq :+
ldx #1
lda #253
sta vel_x
:
cpx #1
beq @end
lda frame_counter
and #1
cmp #0
bne @end
lda #0
cmp vel_x
beq @end ; If vel_x = 0, skip decay
; If vel_x > 0, decrement
bpl :+
ldx vel_x
dex
stx vel_x
jmp @end
:
; Else if vel_x < 0, increment
ldx vel_x
inx
stx vel_x
@end:
; Jump
jsr btn_up
cmp #0
beq :+
lda #250
sta vel_y
:
; Gravity
lda frame_counter
and #1
cmp #0
bne :+
lda vel_y
clc
adc #1 ; Add 2 to velocity
sta vel_y
:
; Apply Y velocity
lda oam+0
clc
adc vel_y
sta oam+0
; Keep grounded
cmp #200
bcc :+
lda #0
sta vel_y
lda #200
sta oam+0
:
; Apply X velocity
lda oam+3
clc
adc vel_x
sta oam+3
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 :--
; 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 :-
lda $2002
lda $20
sta $2006
lda $00
sta $2006
ldy #0
:
lda level, Y
lda #4
sta $2007
iny
cpy #240
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:
sta temp_mul
lda #0
:
clc
adc temp_mul
dex
bne :-
rts
mul_y:
sta temp_mul
lda #0
:
clc
adc temp_mul
dey
bne :-
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