APU fixed

* Fixed type that caused triangle linear counter to be ignored
 * Changed sweep to shift the current period, not the register value
 * Moved muting logic early to avoid setting DOC registers when not needed
This commit is contained in:
Lucas Scharenbroich 2023-06-17 23:50:38 -05:00
parent 8011f268a2
commit bdb4006e22
2 changed files with 582 additions and 147 deletions

View File

@ -105,6 +105,9 @@ copy_instruments_to_doc
lda #$0500
jsr copy_triangle
lda #$0600
jsr copy_noise
rts
;--------------------------
@ -190,6 +193,24 @@ copy_triangle
mx %00
rts
copy_noise
sep #$30
mx %11
stz sound_address
xba
sta sound_address+1
ldx #0
:loop
lda noise_wave,x
sta sound_data
inx
bne :loop
rep #$30
mx %00
rts
;--------------------------
triangle_wave
@ -210,6 +231,23 @@ triangle_wave
hex 41424446484a4c4e50525456585a5c5e
hex 60626466686a6c6e70727476787a7c7e
noise_wave
hex 8f968f763e6fd49ab1e564e295a9bcc9
hex 717b6629e6970b865dc0e0d840d32a96
hex 3bd4c5d407b78923d8c9766bea128e8a
hex c9ee5ddbed3119ff14b4d9a44bfbb7c4
hex 7a56e26e8aac9ebf1653c0260446231b
hex 73431495fc585e943edacf8f5bb970e6
hex 118dc361bee99c98f32d25f06a33715a
hex 585344f7f3e2f3c36c37cfd78e40147f
hex a4b20624ac633b42b3aac5407fac4ba9
hex a4d71a1d020a7757ea244b103f0b7a76
hex 9b533a60cda31e0fa2ce3491b55c4f26
hex ea47a61f661deec128129372c3471a9b
hex f85c3c077168d413184a139440460950
hex dee3f9bdb65e162b08ed9231a72fb943
hex 1ba599be80dc2812afa63cc2317cdb1a
hex 8d99d56327bc50dc975bee94754f561b
;--------------------------
setup_doc_registers
@ -227,6 +265,9 @@ setup_doc_registers
ldx #triangle_sound_settings
jsr copy_register_config
ldx #noise_sound_settings
jsr copy_register_config
rep #$20
mx %00
@ -295,7 +336,8 @@ timer_sound_settings = * ; set up oscillator 30 for i
pulse1_oscillator = 0
pulse2_oscillator = 2
triangle_oscillator = 4
default_freq = 5000
noise_oscillator = 6
default_freq = 800
pulse1_sound_settings = *
dfb $00+pulse1_oscillator,default_freq ; frequency low register
dfb $20+pulse1_oscillator,default_freq/256 ; frequency high register
@ -320,6 +362,14 @@ triangle_sound_settings = *
dfb $c0+triangle_oscillator,0 ; wavetable size register, 256 byte length
dfb $a0+triangle_oscillator,0 ; mode register, set to free run
noise_sound_settings = *
dfb $00+noise_oscillator,default_freq ; frequency low register
dfb $20+noise_oscillator,default_freq/256 ; frequency high register
dfb $40+noise_oscillator,128 ; volume register, volume = 0
dfb $80+noise_oscillator,6 ; wavetable pointer register, point to $0600
dfb $c0+noise_oscillator,0 ; wavetable size register, 256 byte length
dfb $a0+noise_oscillator,0 ; mode register, set to free run
backup_interrupt_ptr ds 4
;-----------------------------------------------------------------------------------------
@ -365,9 +415,9 @@ clock_sweep mac
sta ]1+{APU_PULSE1_RELOAD_FLAG-APU_PULSE1}
lda ]1+{APU_PULSE1_REG2-APU_PULSE1} ; get the barrel shift argument from the register
bpl no_sweep ; if sweep is not enabled, do nothing
bpl no_sweep ; if sweep is not enabled, do nothing
and #$07
beq no_sweep ; shift must be != 0
beq no_sweep ; shift must be != 0
asl
tax
@ -379,10 +429,7 @@ clock_sweep mac
lda ]1+{APU_PULSE1_CURRENT_PERIOD-APU_PULSE1}
cmp #8
bcc no_sweep0 ; current period must be >= 8
lda ]1+{APU_PULSE1_REG3-APU_PULSE1} ; raw period stored in the register
and #$7FF
jmp (bitshift,x) ; shift it by the shifter amount
jmp (bitshift,x) ; shift it by the shifter amount
bitshift da bitshift_0,bitshift_1,bitshift_2,bitshift_3,bitshift_4,bitshift_5,bitshift_6,bitshift_7
bitshift_7 lsr
bitshift_6 lsr
@ -400,21 +447,9 @@ bitshift_0
FIN
no_negate clc
adc ]1+{APU_PULSE1_CURRENT_PERIOD-APU_PULSE1}
sta ]1+{APU_PULSE1_TARGET_PERIOD-APU_PULSE1}
stz ]1+{APU_PULSE1_MUTE-APU_PULSE1}
ldy #1
cmp #$800
bcc *+5
sty ]1+{APU_PULSE1_MUTE-APU_PULSE1} ; mute the output is the period is too large
bcs *+5
sta ]1+{APU_PULSE1_CURRENT_PERIOD-APU_PULSE1} ; set the current period if the target is within a valid range
lda ]1+{APU_PULSE1_CURRENT_PERIOD-APU_PULSE1}
cmp #8
bcs *+5
sty ]1+{APU_PULSE1_MUTE-APU_PULSE1} ; mute the output if the period is too small
bcs no_sweep0
sta ]1+{APU_PULSE1_CURRENT_PERIOD-APU_PULSE1}
no_sweep0
sep #$20
no_sweep
@ -469,11 +504,19 @@ envelope_out <<<
apu_frame_steps equ 5
PULSE_HALT_FLAG equ $20
NOISE_HALT_FLAG equ $20 ; noise and pulse channels have halt flagin same bit position in REG1
PULSE_CONST_VOL_FLAG equ $10
NOISE_CONST_VOL_FLAG equ $10
TRIANGLE_HALT_FLAG equ $80
mx %11
interrupt_handler = *
ldal $E0C034 ; save the border color
stal border_color
lda #1
jsr setborder
phb
phd
@ -482,14 +525,17 @@ interrupt_handler = *
clc
xce
rep #$30
mx %00
lda #$c000
tcd
pea $c000
pld
sep #$30
mx %11
; Make sure it's the oscillator we care about
ldal osc_interrupt ; which oscillator generated the interrupt?
and #%00111110
cmp #2*interrupt_oscillator
beq *+5
brl :not_timer ; Only service timer interrupts
; Update the frame counter. We double-count so that frame counter can be used directly to dispatch to the
; appropriate tick handler
@ -509,12 +555,13 @@ interrupt_handler = *
clock_length_counter APU_PULSE1;#PULSE_HALT_FLAG
clock_length_counter APU_PULSE2;#PULSE_HALT_FLAG
clock_length_counter APU_TRIANGLE;#TRIANGLE_HALT_FLAG
clock_length_counter APU_NOISE;#NOISE_HALT_FLAG
; clock the sweep units
clock_sweep APU_PULSE1;0
clock_sweep APU_PULSE2;1
; quarter frame updates run every APU frame (for 4-cycle)
; quarter frame updates run every APU frame
:quarter_frame
; clock the envelopes and triangle linear counter
@ -522,43 +569,24 @@ interrupt_handler = *
clock_envelope APU_PULSE1
clock_envelope APU_PULSE2
clock_envelope APU_NOISE
:no_frame
jsr access_doc_registers
ldal osc_interrupt ; which oscillator generated the interrupt?
and #%00111110
lsr
cmp #interrupt_oscillator
beq *+5
brl :not_timer ; Only service timer interrupts
; Set the parameters for the first square wave channel
lda #$80+pulse1_oscillator
sta sound_address
lda APU_PULSE1_REG1 ; Get the cycle duty bits
jsr set_pulse_duty_cycle
lda #$40+pulse1_oscillator
sta sound_address
; Set the parameters for the first square wave channel.
;
; First, set the frequency, if the period is <8 then the pulse channel is muted,
; to test that first
lda APU_PULSE1_MUTE ; If the sweep muted the channel, no output
beq :no_mute_pulse1
lda #0
bra :set_volume_pulse1
:no_mute_pulse1
bne :mute_pulse1
lda APU_PULSE1_LENGTH_COUNTER ; If the length counter is zero, no output
beq :set_volume_pulse1
lda APU_PULSE1_REG1
bit #PULSE_CONST_VOL_FLAG ; Check the constant volume bit
bne :set_volume_pulse1
lda APU_PULSE1_ENVELOPE
:set_volume_pulse1
jsr set_pulse_volume
beq :mute_pulse1
rep #$30
lda APU_PULSE1_CURRENT_PERIOD
cmp #8
bcc :mute_pulse1
jsr get_pulse_freq ; return freq in 16-bit accumulator
sep #$30
ldx #$00+pulse1_oscillator
@ -569,32 +597,36 @@ interrupt_handler = *
xba
sta sound_data
; Now do the second square wave
lda #$80+pulse2_oscillator
lda #$80+pulse1_oscillator
sta sound_address
lda APU_PULSE2_REG1 ; Get the cycle duty bits
lda APU_PULSE1_REG1 ; Get the cycle duty bits
jsr set_pulse_duty_cycle
lda #$40+pulse2_oscillator
lda #$40+pulse1_oscillator
sta sound_address
lda APU_PULSE1_REG1
bit #PULSE_CONST_VOL_FLAG ; Check the constant volume bit
bne :set_volume_pulse1
lda APU_PULSE1_ENVELOPE
bra :set_volume_pulse1
lda APU_PULSE2_MUTE ; If the sweep muted the channel, no output
beq :no_mute_pulse2
:mute_pulse1
sep #$30
lda #$40+pulse1_oscillator
sta sound_address
lda #0
bra :set_volume_pulse2
:no_mute_pulse2
lda APU_PULSE2_LENGTH_COUNTER ; If the length counter is zero, no output
beq :set_volume_pulse2
lda APU_PULSE2_REG1
bit #PULSE_CONST_VOL_FLAG ; Check the constant volume bit
bne :set_volume_pulse2
lda APU_PULSE2_ENVELOPE
:set_volume_pulse2
jsr set_pulse_volume
:set_volume_pulse1 jsr set_pulse_volume
; Now do the second square wave
lda APU_PULSE2_MUTE ; If the sweep muted the channel, no output
bne :mute_pulse2
lda APU_PULSE2_LENGTH_COUNTER ; If the length counter is zero, no output
beq :mute_pulse2
rep #$30
lda APU_PULSE2_CURRENT_PERIOD
cmp #8
bcc :mute_pulse2
jsr get_pulse_freq ; return freq in 16-bic accumulator
sep #$30
ldx #$00+pulse2_oscillator
@ -605,25 +637,48 @@ interrupt_handler = *
xba
sta sound_data
; Now the triangle wave. This wave needs linear counter support to be silenced
; brl :not_timer
lda #$40+triangle_oscillator
lda #$80+pulse2_oscillator
sta sound_address
lda APU_TRIANGLE_LENGTH_COUNTER ; If the length counter is zero, no output
beq :set_volume_triangle
lda APU_TRIANGLE_LENGTH_COUNTER
beq :set_volume_triangle
lda #12 ; Triangle is a bit softer than pulse channels
:set_volume_triangle
jsr set_pulse_volume
lda APU_PULSE2_REG1 ; Get the cycle duty bits
jsr set_pulse_duty_cycle
lda #$40+pulse2_oscillator
sta sound_address
lda APU_PULSE2_REG1
bit #PULSE_CONST_VOL_FLAG ; Check the constant volume bit
bne :set_volume_pulse2
lda APU_PULSE2_ENVELOPE
bra :set_volume_pulse2
:mute_pulse2
sep #$30
lda #$40+pulse2_oscillator
sta sound_address
lda #0
:set_volume_pulse2 jsr set_pulse_volume
; Now the triangle wave. This wave needs linear counter support to be silenced
lda APU_TRIANGLE_LENGTH_COUNTER ; If the length counter is zero, no output
beq :mute_triangle
lda APU_TRIANGLE_LINEAR_COUNTER ; If the linear counter is zero, no output
beq :mute_triangle
rep #$30
lda APU_TRIANGLE_REG3
lda APU_TRIANGLE_CURRENT_PERIOD
cmp #2
bcc :mute_triangle
; NOTE on Triangle channel frequence from https://www.nesdev.org/wiki/APU_Triangle
;
; Unlike the pulse channels, the triangle channel supports frequencies up to the maximum frequency the
; timer will allow, meaning frequencies up to fCPU/32 (about 55.9 kHz for NTSC) are possible - far above
; the audible range. Some games, e.g. Mega Man 2, "silence" the triangle channel by setting the timer to
; zero, which produces a popping sound when an audible frequency is resumed, easily heard e.g. in Crash
; Man's stage. At the expense of accuracy, these can be eliminated in an emulator e.g. by halting the
; triangle channel when an ultrasonic frequency is set (a timer value less than 2).
jsr get_pulse_freq ; return freq in 16-bic accumulator
lsr
sep #$30
ldx #$00+triangle_oscillator
stx sound_address
sta sound_data
@ -632,14 +687,50 @@ interrupt_handler = *
xba
sta sound_data
; lda border_color
; inc
; and #$03
; sta border_color
; jsr setborder
lda #$40+triangle_oscillator
sta sound_address
lda #12 ; Triangle is a bit softer than pulse channels
bra :set_volume_triangle
:mute_triangle
sep #$30
lda #$40+triangle_oscillator
sta sound_address
lda #0
:set_volume_triangle jsr set_pulse_volume
; Now the noise channel. It's mixer volume output is ~half of the pulse channels
lda #$40+noise_oscillator
sta sound_address
lda APU_NOISE_LENGTH_COUNTER ; If the length counter is zero, no output
beq :set_volume_noise
lda APU_NOISE_REG1
bit #NOISE_CONST_VOL_FLAG ; Check the constant volume bit
bne :set_volume_noise
lda APU_NOISE_ENVELOPE
:set_volume_noise
jsr set_pulse_volume
rep #$30
lda APU_NOISE_CURRENT_PERIOD
jsr get_pulse_freq ; return freq in 16-bic accumulator
sep #$30
ldx #$00+noise_oscillator
stx sound_address
sta sound_data
ldx #$20+noise_oscillator
stx sound_address
xba
sta sound_data
:not_timer
sep #$30
lda #0
jsr setborder
pld
plb
clc
@ -819,6 +910,23 @@ APU_TRIANGLE_CURRENT_PERIOD dw 0
APU_TRIANGLE_START_FLAG dfb 0
APU_TRIANGLE_LINEAR_COUNTER dfb 0
APU_NOISE
APU_NOISE_REG1 ds 1 ; --LC NNNN - length counter halt, constant volume/evelope, envelope period/volume
APU_NOISE_REG2 ds 1 ; ---- ---- - Unused
APU_NOISE_REG3 ds 1 ; M--- PPPP - Mode and period lookup
APU_NOISE_REG4 ds 1 ; llll l--- - Length counter load
APU_NOISE_LENGTH_COUNTER dfb 0 ; internal register for the length counter
APU_NOISE_RELOAD_FLAG dfb 0 ; unused
APU_NOISE_SWEEP_DIVIDER dfb 0 ; unused
APU_NOISE_TARGET_PERIOD dw 0 ; unused
APU_NOISE_CURRENT_PERIOD dw 0 ; internal register to hold the current period driving the oscillator
APU_NOISE_MUTE dfb 0 ; unused
APU_NOISE_START_FLAG dfb 0
APU_NOISE_ENVELOPE_DIVIDER dfb 0
APU_NOISE_ENVELOPE dfb 0
APU_STATUS ds 1
mx %11
@ -906,7 +1014,7 @@ APU_PULSE2_REG4_WRITE ENT
stal APU_PULSE2_CURRENT_PERIOD+1
ldal APU_STATUS
bit #$01
bit #$02
beq :no_reload
ldal APU_PULSE2_REG4
@ -971,6 +1079,65 @@ APU_TRIANGLE_REG4_WRITE ENT
rtl
APU_NOISE_REG1_WRITE ENT
stal APU_NOISE_REG1
rtl
APU_NOISE_REG2_WRITE ENT
stal APU_NOISE_REG2
rtl
APU_NOISE_REG3_WRITE ENT
php
phx
pha
stal APU_NOISE_REG3
and #$0F
asl
tax
ldal NoisePeriodTable,x
sta APU_NOISE_CURRENT_PERIOD
ldal NoisePeriodTable+1,x
sta APU_NOISE_CURRENT_PERIOD+1
pla
plx
plp
rtl
APU_NOISE_REG4_WRITE ENT
php
phx
pha
stal APU_NOISE_REG4
ldal APU_STATUS
bit #$08
beq :no_reload
ldal APU_NOISE_REG4
and #$F8
lsr
lsr
lsr
tax
ldal LengthTable,x
stal APU_NOISE_LENGTH_COUNTER ; Immediately start the counter
lda #1
stal APU_NOISE_START_FLAG
:no_reload
pla
plx
plp
rtl
; Lookup from bottom 4 bits of NOISE_REG3
NoisePeriodTable dw 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
APU_STATUS_WRITE ENT
stal APU_STATUS
pha
@ -997,5 +1164,11 @@ APU_STATUS_WRITE ENT
stz APU_TRIANGLE_LENGTH_COUNTER
:triangle_on
; Noise
bit #$08
bne :noise_on
stz APU_NOISE_LENGTH_COUNTER
:noise_on
pla
rtl

View File

@ -44,6 +44,7 @@ Tmp1 equ 130
stz CursorRow
jsr InitGraphics
jsr DrawStaticUI
jsr APUStartUp
sep #$30
@ -55,7 +56,7 @@ Tmp1 equ 130
:update
jsr DrawUI
:evtloop
jsr DrawDynUI ; Always update to see changing internal APU values
; jsr DrawDynUI ; Always update to see changing internal APU values
jsr _ReadControl
bit #PAD_KEY_DOWN ; Only response to actual key presses
beq :evtloop
@ -107,31 +108,100 @@ Toggle
asl
tax
jmp (:toggle1,x)
:toggle1 da Toggle4000,Toggle4001,Toggle4002,Toggle4003,Toggle4015
: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
bra ToggleExit
brl ToggleExit
Toggle4001
lda APU_PULSE1_REG2
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG2_WRITE
bra ToggleExit
brl ToggleExit
Toggle4002
lda APU_PULSE1_REG3
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG3_WRITE
bra ToggleExit
brl ToggleExit
Toggle4003
lda APU_PULSE1_REG4
ldx CursorCol
eor ToggleTable,x
jsl APU_PULSE1_REG4_WRITE
bra ToggleExit
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
@ -150,36 +220,66 @@ ToggleTable dfb $80,$40,$20,$10,$08,$04,$02,$01
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
bpl *+5
lda #4
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
cmp #5
bcc *+5
lda #0
sta CursorRow
rts
MoveLeft
lda CursorCol
dec
bpl *+5
bpl :store
lda CursorRow
cmp #15
beq :skip
eor #8
sta CursorRow
:skip
lda #7
sta CursorCol
:store sta CursorCol
rts
MoveRight
lda CursorCol
inc
cmp #8
bcc *+5
bcc :store
lda CursorRow
cmp #15
beq :skip
eor #8
sta CursorRow
:skip
lda #0
sta CursorCol
:store sta CursorCol
rts
; Read the keyboard and paddle controls and return in a game-controller-like format
@ -260,10 +360,24 @@ DefaultPalette ENT
; 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}
@ -289,96 +403,232 @@ DrawDynUI
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
ldx #{1*4}+{ROW_SPAN*5}
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 #{6*4}+{ROW_SPAN*5}
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*PULSE1_Y}
jsr DrawBitsHL
ldx #{15*4}+{ROW_SPAN*5}
ldy #$5555
lda APU_PULSE1_REG1
jsr DrawByte
ldx #{1*4}+{ROW_SPAN*6}
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+1}}
ldy #$4444
lda #$4001
jsr DrawWord
ldy #PULSE_REG2_STR
lda APU_PULSE1_REG2
ldx #{6*4}+{ROW_SPAN*6}
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*{PULSE1_Y+1}}
jsr DrawBitsHL
ldx #{15*4}+{ROW_SPAN*6}
ldy #$5555
lda APU_PULSE1_REG2
jsr DrawByte
ldx #{1*4}+{ROW_SPAN*7}
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+2}}
ldy #$4444
lda #$4002
jsr DrawWord
ldy #PULSE_REG3_STR
lda APU_PULSE1_REG3
ldx #{6*4}+{ROW_SPAN*7}
ldx #{{PULSE1_X+5}*4}+{ROW_SPAN*{PULSE1_Y+2}}
jsr DrawBitsHL
ldx #{15*4}+{ROW_SPAN*7}
ldy #$5555
lda APU_PULSE1_REG3
jsr DrawByte
ldx #{1*4}+{ROW_SPAN*8}
ldx #{PULSE1_X*4}+{ROW_SPAN*{PULSE1_Y+3}}
ldy #$4444
lda #$4003
jsr DrawWord
ldy #PULSE_REG4_STR
lda APU_PULSE1_REG4
ldx #{6*4}+{ROW_SPAN*8}
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 #{15*4}+{ROW_SPAN*8}
ldy #$5555
lda APU_PULSE1_REG4
jsr DrawByte
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_PULSE2_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
ldx #{1*4}+{ROW_SPAN*10}
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 #{6*4}+{ROW_SPAN*10}
ldx #{{CONTROL_X+5}*4}+{ROW_SPAN*CONTROL_Y}
jsr DrawBitsHL
ldx #{15*4}+{ROW_SPAN*10}
ldy #$5555
lda APU_STATUS
jsr DrawByte
rts
DrawCursor
lda CursorRow
asl
tax
lda row2screen,x ; Get the physical position of each logical row
asl
asl
asl
@ -393,9 +643,10 @@ DrawUI
asl
asl
sta 1,s
lda CursorCol
clc
adc #6
adc col2screen,x
asl
asl
clc
@ -409,7 +660,8 @@ DrawUI
rts
row2screen dw 5,6,7,8,10
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
;
@ -448,6 +700,16 @@ DrawBitsHL
bcc :loop
rts
setborder
php
sep #$20
eorl $E0C034
and #$0F
eorl $E0C034
stal $E0C034
plp
rts
put App.Msg.s
put font.s