Files
nes/plat.s

910 lines
12 KiB
ArmAsm
Raw Normal View History

2026-02-14 18:08:10 -05:00
.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
2026-02-16 00:27:56 -05:00
lda #%00100000 ; 8x16 sprites
2026-02-14 18:08:10 -05:00
sta $2000
2026-02-16 00:27:56 -05:00
lda #0
2026-02-14 18:08:10 -05:00
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"
2026-02-16 00:27:56 -05:00
level:
2026-02-16 20:33:50 -05:00
.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
2026-02-16 00:27:56 -05:00
.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
2026-02-16 20:33:50 -05:00
metatiles:
.byte 0,0,0,0,0
.byte 3,4,5,6,1
2026-02-14 18:08:10 -05:00
example_palette:
2026-02-16 00:27:56 -05:00
.byte $0F,$00,$3D,$20 ; greyscale
.byte $0F,$09,$1A,$16 ; grass
2026-02-14 18:08:10 -05:00
.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"
2026-02-16 00:27:56 -05:00
buttons: .res 1
vel_x: .res 1
vel_y: .res 1
2026-02-14 18:08:10 -05:00
cursor_x: .res 1
cursor_y: .res 1
temp_x: .res 1
temp_y: .res 1
2026-02-16 00:27:56 -05:00
temp_mul: .res 1
2026-02-14 18:08:10 -05:00
var_m: .res 1
var_n: .res 1
var_o: .res 1
var_p: .res 1
2026-02-16 20:33:50 -05:00
jump_pressed_last_frame: .res 1
2026-02-14 18:08:10 -05:00
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
2026-02-16 00:27:56 -05:00
jsr controller
jsr movement
@draw:
jsr update_background
jsr ppu_update
2026-02-14 18:08:10 -05:00
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 :-
2026-02-16 00:27:56 -05:00
lda #0
sta vel_x
sta vel_y
2026-02-14 18:08:10 -05:00
rts
draw:
2026-02-16 00:27:56 -05:00
@end:
rts
2026-02-14 18:08:10 -05:00
2026-02-16 00:27:56 -05:00
movement:
; Horizontal velocity
2026-02-14 18:08:10 -05:00
ldx #0
2026-02-16 00:27:56 -05:00
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
2026-02-16 20:33:50 -05:00
jsr btn_a
2026-02-16 00:27:56 -05:00
cmp #0
2026-02-16 20:33:50 -05:00
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 vel_y
@fail_jump:
jsr btn_a
sta jump_pressed_last_frame
2026-02-16 00:27:56 -05:00
; Gravity
lda frame_counter
and #1
cmp #0
bne :+
lda vel_y
2026-02-14 18:08:10 -05:00
clc
adc #1 ; Add 2 to velocity
2026-02-16 00:27:56 -05:00
sta vel_y
:
2026-02-14 18:08:10 -05:00
2026-02-16 00:27:56 -05:00
; Apply Y velocity
lda oam+0
clc
adc vel_y
sta oam+0
2026-02-14 18:08:10 -05:00
2026-02-16 00:27:56 -05:00
; Keep grounded
cmp #200
bcc :+
lda #0
sta vel_y
lda #200
sta oam+0
:
2026-02-14 18:08:10 -05:00
2026-02-16 20:33:50 -05:00
; Bonk
lda #8
cmp oam+0
bcc :+
lda #0
sta vel_y
lda #8
sta oam+0
:
2026-02-16 00:27:56 -05:00
; Apply X velocity
lda oam+3
clc
adc vel_x
sta oam+3
2026-02-14 18:08:10 -05:00
2026-02-16 00:27:56 -05:00
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
2026-02-14 18:08:10 -05:00
setup_background:
2026-02-16 20:33:50 -05:00
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
2026-02-14 18:08:10 -05:00
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
2026-02-16 00:27:56 -05:00
lda #0
2026-02-14 18:08:10 -05:00
ldx #64 ; 64 bytes
:
2026-02-16 00:27:56 -05:00
txa
and #%00000011
asl
asl
asl
asl
sta temp
txa
and #%00000011
ora temp
lda #0
2026-02-14 18:08:10 -05:00
sta $2007
dex
bne :-
2026-02-16 00:27:56 -05:00
2026-02-14 18:08:10 -05:00
; fill in an area in the middle with 1/2 checkerboard
lda #1
2026-02-16 00:27:56 -05:00
ldy #0 ; start at row 8
2026-02-14 18:08:10 -05:00
:
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
2026-02-16 00:27:56 -05:00
mul_x:
2026-02-16 20:33:50 -05:00
cmp #0
beq @zero
cpx #0
beq @zero
2026-02-16 00:27:56 -05:00
sta temp_mul
2026-02-16 20:33:50 -05:00
txa
pha
2026-02-16 00:27:56 -05:00
lda #0
:
clc
adc temp_mul
dex
bne :-
2026-02-16 20:33:50 -05:00
sta temp_mul
pla
tax
lda temp_mul
rts
@zero:
lda #0
2026-02-16 00:27:56 -05:00
rts
mul_y:
2026-02-16 20:33:50 -05:00
cmp #0
beq @zero
cpy #0
beq @zero
2026-02-16 00:27:56 -05:00
sta temp_mul
2026-02-16 20:33:50 -05:00
tya
pha
2026-02-16 00:27:56 -05:00
lda #0
:
clc
adc temp_mul
dey
bne :-
2026-02-16 20:33:50 -05:00
sta temp_mul
pla
tay
lda temp_mul
rts
@zero:
lda #0
2026-02-16 00:27:56 -05:00
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