iigs-game-engine/demos/smb/apu_sim.s

729 lines
17 KiB
ArmAsm

; APU Sim
;
; Interactive driver for the APU emulation. Inspired by the liveNES ROM (https://github.com/plgDavid/livenes)
REL
use EDS.GSOS.Macs
; Keycodes
LEFT_ARROW equ $08
RIGHT_ARROW equ $15
UP_ARROW equ $0B
DOWN_ARROW equ $0A
SHADOW_REG equ $E0C035
KBD_REG equ $E0C000
KBD_STROBE_REG equ $E0C010
VBL_STATE_REG equ $E0C019
MOD_REG equ $E0C025
COMMAND_KEY_REG equ $E0C061
OPTION_KEY_REG equ $E0C062
; ReadControl return value bits
PAD_BUTTON_B equ $0100
PAD_BUTTON_A equ $0200
PAD_KEY_DOWN equ $0400
; Direct page space
MyUserId equ 0
LastKey equ 2
CursorRow equ 4
CursorCol equ 6
Tmp0 equ 128
Tmp1 equ 130
mx %00
phk
plb
sta MyUserId
stz CursorCol
stz CursorRow
jsr InitGraphics
jsr DrawStaticUI
jsr APUStartUp
sep #$30
lda #$f
jsl APU_STATUS_WRITE ; turn on the first four channels by default
rep #$30
:update
jsr DrawUI
:evtloop
; jsr DrawDynUI ; Always update to see changing internal APU values
jsr _ReadControl
bit #PAD_KEY_DOWN ; Only response to actual key presses
beq :evtloop
and #$007F
cmp #'q'
beq :done
cmp #UP_ARROW
bne *+8
jsr MoveUp
brl :update
cmp #DOWN_ARROW
bne *+8
jsr MoveDown
brl :update
cmp #LEFT_ARROW
bne *+8
jsr MoveLeft
brl :update
cmp #RIGHT_ARROW
bne *+8
jsr MoveRight
brl :update
cmp #' '
bne :not_spc
jsr Toggle
brl :update
:not_spc cmp #'a'
bne :not_a
lda show_border
eor #1
sta show_border
:not_a
brl :evtloop
:done
jsr APUShutDown
Quit
_QuitGS qtRec
qtRec adrl $0000
da $00
; Toggle an APU bit
Toggle
mx %11
sep #$30
lda CursorRow
asl
tax
jmp (:toggle1,x)
:toggle1 da Toggle4000,Toggle4001,Toggle4002,Toggle4003
da Toggle4008,Toggle400A,Toggle400B,ToggleNoop
da Toggle4004,Toggle4005,Toggle4006,Toggle4007
da Toggle400C,Toggle400E,Toggle400F,Toggle4015
ToggleNoop
brl ToggleExit
Toggle4000
lda APU_PULSE1_REG1
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG1_WRITE
brl ToggleExit
Toggle4001
lda APU_PULSE1_REG2
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG2_WRITE
brl ToggleExit
Toggle4002
lda APU_PULSE1_REG3
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG3_WRITE
brl ToggleExit
Toggle4003
lda APU_PULSE1_REG4
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG4_WRITE
brl ToggleExit
Toggle4004
lda APU_PULSE2_REG1
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE2_REG1_WRITE
brl ToggleExit
Toggle4005
lda APU_PULSE2_REG2
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE2_REG2_WRITE
brl ToggleExit
Toggle4006
lda APU_PULSE2_REG3
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE2_REG3_WRITE
brl ToggleExit
Toggle4007
lda APU_PULSE2_REG4
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE2_REG4_WRITE
brl ToggleExit
Toggle4008
lda APU_TRIANGLE_REG1
ldx CursorCol
eor ToggleTable,x
jsl APU_TRIANGLE_REG1_WRITE
brl ToggleExit
Toggle400A
lda APU_TRIANGLE_REG3
ldx CursorCol
eor ToggleTable,x
jsl APU_TRIANGLE_REG3_WRITE
brl ToggleExit
Toggle400B
lda APU_TRIANGLE_REG4
ldx CursorCol
eor ToggleTable,x
jsl APU_TRIANGLE_REG4_WRITE
brl ToggleExit
Toggle400C
lda APU_NOISE_REG1
ldx CursorCol
eor ToggleTable,x
jsl APU_NOISE_REG1_WRITE
brl ToggleExit
Toggle400E
lda APU_NOISE_REG3
ldx CursorCol
eor ToggleTable,x
jsl APU_NOISE_REG3_WRITE
brl ToggleExit
Toggle400F
lda APU_NOISE_REG4
ldx CursorCol
eor ToggleTable,x
jsl APU_NOISE_REG4_WRITE
brl ToggleExit
Toggle4015
lda APU_STATUS
ldx CursorCol
eor ToggleTable,x
jsl APU_STATUS_WRITE
bra ToggleExit
TogglePulse2
ToggleExit
rep #$30
rts
ToggleTable dfb $80,$40,$20,$10,$08,$04,$02,$01
; Cursor navigation
mx %00
MoveUp
lda CursorRow
bne :not_0
lda #6
sta CursorRow
rts
:not_0
cmp #8
bne :not_8
lda #15
sta CursorRow
rts
:not_8
dec
sta CursorRow
rts
MoveDown
lda CursorRow
cmp #6
bne :not_6
stz CursorRow
rts
:not_6
cmp #15
bne :not_15
lda #8
sta CursorRow
rts
:not_15
inc
sta CursorRow
rts
MoveLeft
lda CursorCol
dec
bpl :store
lda CursorRow
cmp #15
beq :skip
eor #8
sta CursorRow
:skip
lda #7
:store sta CursorCol
rts
MoveRight
lda CursorCol
inc
cmp #8
bcc :store
lda CursorRow
cmp #15
beq :skip
eor #8
sta CursorRow
:skip
lda #0
:store sta CursorCol
rts
; Read the keyboard and paddle controls and return in a game-controller-like format
mx %00
_ReadControl pea $0000 ; low byte = key code, high byte = %------AB
sep #$20
ldal OPTION_KEY_REG ; 'B' button
and #$80
beq :BNotDown
lda #>PAD_BUTTON_B
ora 2,s
sta 2,s
:BNotDown
ldal COMMAND_KEY_REG
and #$80
beq :ANotDown
lda #>PAD_BUTTON_A
ora 2,s
sta 2,s
:ANotDown
ldal KBD_STROBE_REG ; read the keyboard
bit #$80
beq :KbdNotDwn ; check the key-down status
and #$7f
ora 1,s
sta 1,s
cmp LastKey
beq :KbdDown
sta LastKey
lda #>PAD_KEY_DOWN ; set the keydown flag
ora 2,s
sta 2,s
bra :KbdDown
:KbdNotDwn
stz LastKey
:KbdDown
ldal KBD_REG
rep #$20
pla
rts
mx %00
InitGraphics
sep #$20
lda #$C1
stal $E0C029 ; screen on
ldal SHADOW_REG ; shadow on
and #$F7
stal SHADOW_REG
rep #$20
lda #0
ldx #0
:scbloop stal $012000,x
inx
inx
cpx #$7E00
bcc :scbloop
ldx #0
:palloop lda DefaultPalette,x
stal $019E00,x
inx
inx
cpx #$20
bcc :palloop
rts
DefaultPalette ENT
dw $0000,$0777,$0841,$072C
dw $000F,$0080,$0F70,$0D00
dw $0FA9,$0FF0,$00E0,$04DF
dw $0DAF,$078F,$0CCC,$0FFF
; UI string templates
PULSE1_TITLE str 'PULSE1'
PULSE2_TITLE str 'PULSE2'
TRIANGLE_TITLE str 'TRIANGLE'
NOISE_TITLE str 'NOISE'
CONTROL_TITLE str 'CONTROL'
PULSE_REG1_STR str 'DDLCVVVV'
PULSE_REG2_STR str 'EPPPNSSS'
PULSE_REG3_STR str 'TTTTTTTT'
PULSE_REG4_STR str 'LLLLLTTT'
TRIANGLE_REG1_STR str 'CRRRRRRR'
TRIANGLE_REG3_STR str 'TTTTTTTT'
TRIANGLE_REG4_STR str 'LLLLLTTT'
NOISE_REG1_STR str '--LCVVVV'
NOISE_REG3_STR str 'L---PPPP'
NOISE_REG4_STR str 'LLLLL---'
APU_STATUS_STR str '---DNT21'
ROW_SPAN equ {8*160}
DrawDynUI
ldx #{18*4}+{ROW_SPAN*5}
ldy #$3333
lda APU_PULSE1_LENGTH_COUNTER
jsr DrawByte
ldx #{18*4}+{ROW_SPAN*6}
ldy #$3333
lda APU_PULSE1_MUTE
jsr DrawByte
ldx #{18*4}+{ROW_SPAN*7}
ldy #$3333
lda APU_PULSE1_ENVELOPE
jsr DrawByte
ldx #{18*4}+{ROW_SPAN*8}
ldy #$3333
lda APU_PULSE1_CURRENT_PERIOD
jsr DrawWord
; Draw stuff that will never be updated
DrawStaticUI
ldx #{6*4}+{ROW_SPAN*3}
ldy #$1111
lda #PULSE1_TITLE
jsr DrawString
ldx #{22*4}+{ROW_SPAN*3}
ldy #$1111
lda #PULSE2_TITLE
jsr DrawString
ldx #{6*4}+{ROW_SPAN*10}
ldy #$1111
lda #TRIANGLE_TITLE
jsr DrawString
ldx #{22*4}+{ROW_SPAN*10}
ldy #$1111
lda #NOISE_TITLE
jsr DrawString
ldx #{22*4}+{ROW_SPAN*16}
ldy #$1111
lda #CONTROL_TITLE
jsr DrawString
DrawUI
jsr DrawPulse1
jsr DrawPulse2
jsr DrawTriangle
jsr DrawNoise
jsr DrawControl
jsr DrawCursor
rts
PULSE1_X equ 1
PULSE1_Y equ 5
DrawPulse1
ldx #{PULSE1_X*4}+{ROW_SPAN*PULSE1_Y}
ldy #$4444
lda #$4000
jsr DrawWord
ldy #PULSE_REG1_STR
lda APU_PULSE1_REG1
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*PULSE1_Y}
jsr DrawBitsHL
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+1}}
ldy #$4444
lda #$4001
jsr DrawWord
ldy #PULSE_REG2_STR
lda APU_PULSE1_REG2
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*{PULSE1_Y+1}}
jsr DrawBitsHL
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+2}}
ldy #$4444
lda #$4002
jsr DrawWord
ldy #PULSE_REG3_STR
lda APU_PULSE1_REG3
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*{PULSE1_Y+2}}
jsr DrawBitsHL
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+3}}
ldy #$4444
lda #$4003
jsr DrawWord
ldy #PULSE_REG4_STR
lda APU_PULSE1_REG4
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*{PULSE1_Y+3}}
jsr DrawBitsHL
rts
PULSE2_X equ 17
PULSE2_Y equ 5
DrawPulse2
ldx #{PULSE2_X*4}+{ROW_SPAN*PULSE2_Y}
ldy #$4444
lda #$4004
jsr DrawWord
ldy #PULSE_REG1_STR
lda APU_PULSE2_REG1
ldx #{{PULSE2_X+5}*4}+{ROW_SPAN*PULSE2_Y}
jsr DrawBitsHL
ldx #{PULSE2_X*4}+{ROW_SPAN*{PULSE2_Y+1}}
ldy #$4444
lda #$4005
jsr DrawWord
ldy #PULSE_REG2_STR
lda APU_PULSE2_REG2
ldx #{{PULSE2_X+5}*4}+{ROW_SPAN*{PULSE2_Y+1}}
jsr DrawBitsHL
ldx #{PULSE2_X*4}+{ROW_SPAN*{PULSE2_Y+2}}
ldy #$4444
lda #$4006
jsr DrawWord
ldy #PULSE_REG3_STR
lda APU_PULSE2_REG3
ldx #{{PULSE2_X+5}*4}+{ROW_SPAN*{PULSE2_Y+2}}
jsr DrawBitsHL
ldx #{PULSE2_X*4}+{ROW_SPAN*{PULSE2_Y+3}}
ldy #$4444
lda #$4007
jsr DrawWord
ldy #PULSE_REG4_STR
lda APU_PULSE2_REG4
ldx #{{PULSE2_X+5}*4}+{ROW_SPAN*{PULSE2_Y+3}}
jsr DrawBitsHL
rts
TRIANGLE_X equ 1
TRIANGLE_Y equ 12
DrawTriangle
ldx #{TRIANGLE_X*4}+{ROW_SPAN*TRIANGLE_Y}
ldy #$4444
lda #$4008
jsr DrawWord
ldy #TRIANGLE_REG1_STR
lda APU_TRIANGLE_REG1
ldx #{{TRIANGLE_X+5}*4}+{ROW_SPAN*TRIANGLE_Y}
jsr DrawBitsHL
ldx #{TRIANGLE_X*4}+{ROW_SPAN*{TRIANGLE_Y+1}}
ldy #$4444
lda #$400A
jsr DrawWord
ldy #TRIANGLE_REG3_STR
lda APU_TRIANGLE_REG3
ldx #{{TRIANGLE_X+5}*4}+{ROW_SPAN*{TRIANGLE_Y+1}}
jsr DrawBitsHL
ldx #{TRIANGLE_X*4}+{ROW_SPAN*{TRIANGLE_Y+2}}
ldy #$4444
lda #$400B
jsr DrawWord
ldy #TRIANGLE_REG4_STR
lda APU_TRIANGLE_REG4
ldx #{{TRIANGLE_X+5}*4}+{ROW_SPAN*{TRIANGLE_Y+2}}
jsr DrawBitsHL
rts
NOISE_X equ 17
NOISE_Y equ 12
DrawNoise
ldx #{NOISE_X*4}+{ROW_SPAN*NOISE_Y}
ldy #$4444
lda #$400C
jsr DrawWord
ldy #NOISE_REG1_STR
lda APU_NOISE_REG1
ldx #{{NOISE_X+5}*4}+{ROW_SPAN*NOISE_Y}
jsr DrawBitsHL
ldx #{NOISE_X*4}+{ROW_SPAN*{NOISE_Y+1}}
ldy #$4444
lda #$400E
jsr DrawWord
ldy #NOISE_REG3_STR
lda APU_NOISE_REG3
ldx #{{NOISE_X+5}*4}+{ROW_SPAN*{NOISE_Y+1}}
jsr DrawBitsHL
ldx #{NOISE_X*4}+{ROW_SPAN*{NOISE_Y+2}}
ldy #$4444
lda #$400F
jsr DrawWord
ldy #NOISE_REG4_STR
lda APU_NOISE_REG4
ldx #{{NOISE_X+5}*4}+{ROW_SPAN*{NOISE_Y+2}}
jsr DrawBitsHL
rts
; Draw the APU Status byte
CONTROL_X equ 17
CONTROL_Y equ 18
DrawControl
ldx #{CONTROL_X*4}+{ROW_SPAN*CONTROL_Y}
ldy #$4444
lda #$4015
jsr DrawWord
ldy #APU_STATUS_STR
lda APU_STATUS
ldx #{{CONTROL_X+5}*4}+{ROW_SPAN*CONTROL_Y}
jsr DrawBitsHL
rts
DrawCursor
lda CursorRow
asl
tax
lda row2screen,x ; Get the physical position of each logical row
asl
asl
asl
asl
asl
pha
asl
asl
clc
adc 1,s
asl
asl
asl
sta 1,s
lda CursorCol
clc
adc col2screen,x
asl
asl
clc
adc 1,s
tax
pla
lda #'_'
ldy #$FFFF
jsr DrawBottom
rts
row2screen dw 5,6,7,8,12,13,14,0,5,6,7,8,12,13,14,18 ; 16 logical rows, 0-7 are column 1, 8-15 column 2
col2screen dw 6,6,6,6,6, 6, 6, 0,22,22,22,22,22,22,22,22 ;
; Draw a byte as a series of bits using the template strings and
; the bit values to set the color
;
; x = screen coordinates
; y = string template
; a = byte value
DrawBitsHL
and #$00FF
xba
sta Tmp0
iny ; advance past string length byte
sty Tmp1
ldy #0
:loop
phy
lda (Tmp1),y
and #$00FF
ldy #$1111 ; dk. grey
asl Tmp0
bcc *+5
ldy #$FFFF ; white
jsr DrawChar
txa
clc
adc #4
tax
ply
iny
cpy #8
bcc :loop
rts
setborder
php
sep #$20
eorl $E0C034
and #$0F
eorl $E0C034
stal $E0C034
plp
rts
put App.Msg.s
put font.s
put apu.s