mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-06-08 03:29:32 +00:00
Mostly full implementations of the pulse channels
This commit is contained in:
parent
60d566e78c
commit
99184396fc
|
@ -1,3 +1,4 @@
|
||||||
|
mx %00
|
||||||
HexToChar dfb '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
HexToChar dfb '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
||||||
|
|
||||||
; Convert a byte (Acc) into a string and store at (Y)
|
; Convert a byte (Acc) into a string and store at (Y)
|
||||||
|
@ -40,6 +41,18 @@ Addr2ToString xba
|
||||||
jsr ByteToString
|
jsr ByteToString
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
; A=Value
|
||||||
|
; X=Screen offset
|
||||||
|
DrawByte phx ; Save register value
|
||||||
|
phy
|
||||||
|
ldy #ByteBuff+1
|
||||||
|
jsr ByteToString
|
||||||
|
ply
|
||||||
|
plx
|
||||||
|
lda #ByteBuff
|
||||||
|
jsr DrawString
|
||||||
|
rts
|
||||||
|
|
||||||
; A=Value
|
; A=Value
|
||||||
; X=Screen offset
|
; X=Screen offset
|
||||||
DrawWord phx ; Save register value
|
DrawWord phx ; Save register value
|
||||||
|
@ -57,6 +70,7 @@ ClearWord lda #EmptyBuff
|
||||||
rts
|
rts
|
||||||
|
|
||||||
EmptyBuff str ' '
|
EmptyBuff str ' '
|
||||||
|
ByteBuff str '00'
|
||||||
WordBuff str '0000'
|
WordBuff str '0000'
|
||||||
Addr3Buff str '000000' ; str adds leading length byte
|
Addr3Buff str '000000' ; str adds leading length byte
|
||||||
|
|
||||||
|
|
|
@ -197,15 +197,15 @@ EvtLoop
|
||||||
stz nmiCount
|
stz nmiCount
|
||||||
|
|
||||||
; sep #$20
|
; sep #$20
|
||||||
; lda #7
|
; lda #1
|
||||||
; stal ROMBase+$075f
|
; stal ROMBase+$075f
|
||||||
; stal ROMBase+$0766
|
; stal ROMBase+$0766
|
||||||
|
|
||||||
; lda #3
|
; lda #2
|
||||||
; stal ROMBase+$0763
|
; stal ROMBase+$0763
|
||||||
; stal ROMBase+$075c
|
; stal ROMBase+$075c
|
||||||
|
|
||||||
; lda #3
|
; lda #2
|
||||||
; stal ROMBase+$0767
|
; stal ROMBase+$0767
|
||||||
; stal ROMBase+$0760
|
; stal ROMBase+$0760
|
||||||
; rep #$30
|
; rep #$30
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
|
SMBProto.SHK=Type(E0),AuxType(8002),VersionCreate(80),MinVersion(87),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
|
||||||
|
SMBProto2.SHK=Type(E0),AuxType(8002),VersionCreate(80),MinVersion(87),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
|
||||||
|
APUSim=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
|
||||||
SuperMarioGS=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
|
SuperMarioGS=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
|
||||||
|
|
362
demos/smb/apu.s
362
demos/smb/apu.s
|
@ -315,17 +315,143 @@ pulse2_sound_settings = *
|
||||||
triangle_sound_settings = *
|
triangle_sound_settings = *
|
||||||
dfb $00+triangle_oscillator,default_freq ; frequency low register
|
dfb $00+triangle_oscillator,default_freq ; frequency low register
|
||||||
dfb $20+triangle_oscillator,default_freq/256 ; frequency high register
|
dfb $20+triangle_oscillator,default_freq/256 ; frequency high register
|
||||||
dfb $40+triangle_oscillator,128 ; volume register, volume = 0
|
dfb $40+triangle_oscillator,0 ; volume register, volume = 0
|
||||||
dfb $80+triangle_oscillator,5 ; wavetable pointer register, point to $0500
|
dfb $80+triangle_oscillator,5 ; wavetable pointer register, point to $0500
|
||||||
dfb $c0+triangle_oscillator,0 ; wavetable size register, 256 byte length
|
dfb $c0+triangle_oscillator,0 ; wavetable size register, 256 byte length
|
||||||
dfb $a0+triangle_oscillator,0 ; mode register, set to free run
|
dfb $a0+triangle_oscillator,0 ; mode register, set to free run
|
||||||
|
|
||||||
backup_interrupt_ptr ds 4
|
backup_interrupt_ptr ds 4
|
||||||
|
|
||||||
|
;-----------------------------------------------------------------------------------------
|
||||||
|
; APU internals
|
||||||
|
;-----------------------------------------------------------------------------------------
|
||||||
|
mx %11
|
||||||
|
clock_length mac
|
||||||
|
lda ]1+{APU_PULSE1_REG1-APU_PULSE1}
|
||||||
|
bit #PULSE_HALT_FLAG
|
||||||
|
bne no_count
|
||||||
|
lda ]1+{APU_PULSE1_LENGTH_COUNTER-APU_PULSE1}
|
||||||
|
beq no_count
|
||||||
|
dec
|
||||||
|
sta ]1+{APU_PULSE1_LENGTH_COUNTER-APU_PULSE1}
|
||||||
|
no_count <<<
|
||||||
|
|
||||||
|
clock_sweep mac
|
||||||
|
lda ]1+{APU_PULSE1_SWEEP_DIVIDER-APU_PULSE1}
|
||||||
|
dec
|
||||||
|
sta ]1+{APU_PULSE1_SWEEP_DIVIDER-APU_PULSE1}
|
||||||
|
bpl no_sweep
|
||||||
|
|
||||||
|
lda #1
|
||||||
|
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
|
||||||
|
and #$07
|
||||||
|
beq no_sweep ; shift must be != 0
|
||||||
|
asl
|
||||||
|
tax
|
||||||
|
|
||||||
|
lda ]1+{APU_PULSE1_REG2-APU_PULSE1} ; put the negate flag in the y register
|
||||||
|
and #$08
|
||||||
|
tay
|
||||||
|
|
||||||
|
rep #$20
|
||||||
|
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
|
||||||
|
bitshift da bitshift_0,bitshift_1,bitshift_2,bitshift_3,bitshift_4,bitshift_5,bitshift_6,bitshift_7
|
||||||
|
bitshift_7 lsr
|
||||||
|
bitshift_6 lsr
|
||||||
|
bitshift_5 lsr
|
||||||
|
bitshift_4 lsr
|
||||||
|
bitshift_3 lsr
|
||||||
|
bitshift_2 lsr
|
||||||
|
bitshift_1 lsr
|
||||||
|
bitshift_0
|
||||||
|
cpy #0 ; check if the negate flag was set
|
||||||
|
beq no_negate
|
||||||
|
eor #$FFFF ; pulse 1 uses 1's complement
|
||||||
|
DO ]2
|
||||||
|
inc
|
||||||
|
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
|
||||||
|
|
||||||
|
no_sweep0
|
||||||
|
sep #$20
|
||||||
|
no_sweep
|
||||||
|
lda ]1+{APU_PULSE1_RELOAD_FLAG-APU_PULSE1} ; check if we need to reload the sweep delay
|
||||||
|
beq no_reload
|
||||||
|
stz ]1+{APU_PULSE1_RELOAD_FLAG-APU_PULSE1}
|
||||||
|
lda ]1+{APU_PULSE1_REG2-APU_PULSE1}
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
and #7
|
||||||
|
sta ]1+{APU_PULSE1_SWEEP_DIVIDER-APU_PULSE1}
|
||||||
|
no_reload <<<
|
||||||
|
|
||||||
|
clock_envelope mac
|
||||||
|
lda ]1+{APU_PULSE1_START_FLAG-APU_PULSE1}
|
||||||
|
beq no_start
|
||||||
|
stz ]1+{APU_PULSE1_START_FLAG-APU_PULSE1} ; clear the start flag
|
||||||
|
lda #15
|
||||||
|
sta ]1+{APU_PULSE1_ENVELOPE-APU_PULSE1} ; reset the envelope saw wave decay value
|
||||||
|
lda ]1+{APU_PULSE1_REG1-APU_PULSE1}
|
||||||
|
and #$0F
|
||||||
|
sta ]1+{APU_PULSE1_ENVELOPE_DIVIDER-APU_PULSE1} ; reset the divider value
|
||||||
|
bra envelope_out ; nothing else to do
|
||||||
|
|
||||||
|
no_start
|
||||||
|
lda ]1+{APU_PULSE1_ENVELOPE_DIVIDER-APU_PULSE1} ; clock the divider
|
||||||
|
dec
|
||||||
|
sta ]1+{APU_PULSE1_ENVELOPE_DIVIDER-APU_PULSE1}
|
||||||
|
bpl envelope_out ; as long as divider is >=0, nothing to do
|
||||||
|
|
||||||
|
lda ]1+{APU_PULSE1_REG1-APU_PULSE1} ; reset the divider to the volume/envelope value
|
||||||
|
and #$0F
|
||||||
|
sta ]1+{APU_PULSE1_ENVELOPE_DIVIDER-APU_PULSE1}
|
||||||
|
|
||||||
|
lda ]1+{APU_PULSE1_ENVELOPE-APU_PULSE1}
|
||||||
|
bne tick_envelope
|
||||||
|
|
||||||
|
lda ]1+{APU_PULSE1_REG1-APU_PULSE1} ; if decay level counter is 0, check the loop bit and set counter to 15 if loop bit is set
|
||||||
|
bit #PULSE_HALT_FLAG
|
||||||
|
beq envelope_out
|
||||||
|
lda #16 ; Set to 15
|
||||||
|
tick_envelope
|
||||||
|
dec
|
||||||
|
sta ]1+{APU_PULSE1_ENVELOPE-APU_PULSE1}
|
||||||
|
envelope_out <<<
|
||||||
|
|
||||||
;-----------------------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------------------
|
||||||
; interupt handler
|
; interupt handler
|
||||||
;-----------------------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
apu_frame_steps equ 5
|
||||||
|
PULSE_HALT_FLAG equ $20
|
||||||
|
PULSE_CONST_VOL_FLAG equ $10
|
||||||
|
|
||||||
interrupt_handler = *
|
interrupt_handler = *
|
||||||
|
|
||||||
phb
|
phb
|
||||||
|
@ -345,6 +471,36 @@ interrupt_handler = *
|
||||||
sep #$30
|
sep #$30
|
||||||
mx %11
|
mx %11
|
||||||
|
|
||||||
|
; Update the frame counter. We double-count so that frame counter can be used directly to dispatch to the
|
||||||
|
; appropriate tick handler
|
||||||
|
|
||||||
|
ldx apu_frame_counter
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
cpx #2*apu_frame_steps ; TODO: This is set by MSB in $4017 (4 or 5). 4 = PAL, 5 = NTSC.
|
||||||
|
bcc *+4
|
||||||
|
ldx #0
|
||||||
|
stx apu_frame_counter
|
||||||
|
jmp (:frame_counter_proc,x)
|
||||||
|
:frame_counter_proc da :quarter_frame,:half_frame,:quarter_frame,:no_frame,:half_frame
|
||||||
|
:half_frame
|
||||||
|
|
||||||
|
; clock the length counters
|
||||||
|
clock_length APU_PULSE1
|
||||||
|
clock_length APU_PULSE2
|
||||||
|
|
||||||
|
; 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
|
||||||
|
|
||||||
|
; clock the envelopes and triangle linear counter
|
||||||
|
clock_envelope APU_PULSE1
|
||||||
|
clock_envelope APU_PULSE2
|
||||||
|
|
||||||
|
:no_frame
|
||||||
jsr access_doc_registers
|
jsr access_doc_registers
|
||||||
|
|
||||||
ldal osc_interrupt ; which oscillator generated the interrupt?
|
ldal osc_interrupt ; which oscillator generated the interrupt?
|
||||||
|
@ -358,19 +514,30 @@ interrupt_handler = *
|
||||||
|
|
||||||
lda #$80+pulse1_oscillator
|
lda #$80+pulse1_oscillator
|
||||||
sta sound_address
|
sta sound_address
|
||||||
lda APU_PULSE1_REG1 ; Get the cycle duty bits
|
lda APU_PULSE1_REG1 ; Get the cycle duty bits
|
||||||
jsr set_pulse_duty_cycle
|
jsr set_pulse_duty_cycle
|
||||||
|
|
||||||
lda #$40+pulse1_oscillator
|
lda #$40+pulse1_oscillator
|
||||||
sta sound_address
|
sta sound_address
|
||||||
|
|
||||||
|
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
|
||||||
|
lda APU_PULSE1_LENGTH_COUNTER ; If the length counter is zero, no output
|
||||||
|
beq :set_volume_pulse1
|
||||||
lda APU_PULSE1_REG1
|
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
|
jsr set_pulse_volume
|
||||||
|
|
||||||
rep #$30
|
rep #$30
|
||||||
lda APU_PULSE1_REG3
|
lda APU_PULSE1_CURRENT_PERIOD
|
||||||
jsr get_pulse_freq ; return freq in 16-bic accumulator
|
jsr get_pulse_freq ; return freq in 16-bit accumulator
|
||||||
sep #$30
|
sep #$30
|
||||||
|
|
||||||
ldx #$00+pulse1_oscillator
|
ldx #$00+pulse1_oscillator
|
||||||
stx sound_address
|
stx sound_address
|
||||||
sta sound_data
|
sta sound_data
|
||||||
|
@ -388,14 +555,25 @@ interrupt_handler = *
|
||||||
|
|
||||||
lda #$40+pulse2_oscillator
|
lda #$40+pulse2_oscillator
|
||||||
sta sound_address
|
sta sound_address
|
||||||
|
|
||||||
|
lda APU_PULSE2_MUTE ; If the sweep muted the channel, no output
|
||||||
|
beq :no_mute_pulse2
|
||||||
|
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
|
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
|
jsr set_pulse_volume
|
||||||
|
|
||||||
rep #$30
|
rep #$30
|
||||||
lda APU_PULSE2_REG3
|
lda APU_PULSE2_CURRENT_PERIOD
|
||||||
jsr get_pulse_freq ; return freq in 16-bic accumulator
|
jsr get_pulse_freq ; return freq in 16-bic accumulator
|
||||||
sep #$30
|
sep #$30
|
||||||
|
|
||||||
ldx #$00+pulse2_oscillator
|
ldx #$00+pulse2_oscillator
|
||||||
stx sound_address
|
stx sound_address
|
||||||
sta sound_data
|
sta sound_data
|
||||||
|
@ -405,6 +583,7 @@ interrupt_handler = *
|
||||||
sta sound_data
|
sta sound_data
|
||||||
|
|
||||||
; Now the triangle wave. This wave needs linear counter support to be silenced
|
; Now the triangle wave. This wave needs linear counter support to be silenced
|
||||||
|
; brl :not_timer
|
||||||
|
|
||||||
rep #$30
|
rep #$30
|
||||||
lda APU_TRIANGLE_REG3
|
lda APU_TRIANGLE_REG3
|
||||||
|
@ -463,7 +642,7 @@ set_pulse_volume
|
||||||
; Solving for F_HL = (1 / 0.200807) * 111860.812 / (t + 1)
|
; Solving for F_HL = (1 / 0.200807) * 111860.812 / (t + 1)
|
||||||
; = 557056.338 / (t + 1)
|
; = 557056.338 / (t + 1)
|
||||||
;
|
;
|
||||||
; if t < 8 this value is out of range and the scillator should be silenced
|
; if t < 8 this value is out of range and the oscillator should be silenced
|
||||||
;
|
;
|
||||||
; otherwise, break apart the ratio
|
; otherwise, break apart the ratio
|
||||||
;
|
;
|
||||||
|
@ -471,9 +650,7 @@ set_pulse_volume
|
||||||
;
|
;
|
||||||
get_pulse_freq
|
get_pulse_freq
|
||||||
mx %00
|
mx %00
|
||||||
and #$07FF ; Load the timer value (11-bits); freq = 1.79MHz / (16 * (t - 1)) = 111860Hz / (t-1)
|
and #$7FF ; prevent overflow...
|
||||||
cmp #8
|
|
||||||
bcc :no_sound
|
|
||||||
inc
|
inc
|
||||||
sta divisor
|
sta divisor
|
||||||
lda #55706
|
lda #55706
|
||||||
|
@ -495,13 +672,8 @@ get_pulse_freq
|
||||||
asl
|
asl
|
||||||
asl
|
asl
|
||||||
clc
|
clc
|
||||||
adc dividend ; multiple by 10 to get the approx DOC value (0.2Hz per + post-multiple)
|
adc dividend ; multiple by 10 to get the DOC value
|
||||||
asl
|
asl
|
||||||
|
|
||||||
; sta dividend
|
|
||||||
rts
|
|
||||||
:no_sound
|
|
||||||
lda #0
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
turn_off_interrupts
|
turn_off_interrupts
|
||||||
|
@ -514,6 +686,33 @@ turn_off_interrupts
|
||||||
plp
|
plp
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
; Internal APU registers.
|
||||||
|
;
|
||||||
|
; These variables track the internal flags, counters and other status bits that make up
|
||||||
|
; the core functionality of the different channel hardware
|
||||||
|
|
||||||
|
apu_frame_counter dw 0 ; frame counter, clocked at 240Hz from the interrupt handler
|
||||||
|
|
||||||
|
; Internal APU register offsets
|
||||||
|
;length_counter_halt equ 0
|
||||||
|
;length_counter equ 1
|
||||||
|
;constant_volume_flag equ 2
|
||||||
|
;volume equ 3
|
||||||
|
;current_period equ 4
|
||||||
|
;target_period equ 6
|
||||||
|
|
||||||
|
; Internal APU registers for channel 1
|
||||||
|
;apu_pulse1 ds 8
|
||||||
|
;apu_pulse2 ds 8
|
||||||
|
|
||||||
|
;apu_pulse1_length_counter dfb 0
|
||||||
|
;apu_pulse1_decay_level dfb 0
|
||||||
|
;apu_pulse1_current_period dw 0
|
||||||
|
|
||||||
|
;apu_pulse2_length_counter dfb 0
|
||||||
|
;apu_pulse2_decay_level dfb 0
|
||||||
|
;apu_pulse2_current_period dw 0
|
||||||
|
|
||||||
duty_cycle_page dfb $01,$02,$03,$04 ; Page of DOC RAM that holds the different duty cycle wavforms
|
duty_cycle_page dfb $01,$02,$03,$04 ; Page of DOC RAM that holds the different duty cycle wavforms
|
||||||
border_color dw 0
|
border_color dw 0
|
||||||
dividend dw 0
|
dividend dw 0
|
||||||
|
@ -539,17 +738,43 @@ _SetDOCReg mac
|
||||||
|
|
||||||
; Pulse Channel 1
|
; Pulse Channel 1
|
||||||
APU_PULSE1
|
APU_PULSE1
|
||||||
APU_PULSE1_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume
|
APU_PULSE1_REG1 ds 1 ; DDLC NNNN - Duty, length counter halt, constant volume/evelope, envelope period/volume
|
||||||
APU_PULSE1_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
APU_PULSE1_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
||||||
APU_PULSE1_REG3 ds 1 ; LLLL LLLL - Timer Low
|
APU_PULSE1_REG3 ds 1 ; LLLL LLLL - Timer Low
|
||||||
APU_PULSE1_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope)
|
APU_PULSE1_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope)
|
||||||
|
|
||||||
|
APU_PULSE1_LENGTH_COUNTER dfb 0 ; internal register for the length counter
|
||||||
|
APU_PULSE1_RELOAD_FLAG dfb 0 ; internal register to reload the sweep divider value
|
||||||
|
APU_PULSE1_SWEEP_DIVIDER dfb 0 ; internal register to track the sweep divider value
|
||||||
|
APU_PULSE1_TARGET_PERIOD dw 0 ; internal register to hold the sweep unit target period
|
||||||
|
APU_PULSE1_CURRENT_PERIOD dw 0 ; internal register to hold the current period driving the oscillator
|
||||||
|
APU_PULSE1_MUTE dfb 0
|
||||||
|
APU_PULSE1_START_FLAG dfb 0
|
||||||
|
APU_PULSE1_ENVELOPE_DIVIDER dfb 0
|
||||||
|
APU_PULSE1_ENVELOPE dfb 0
|
||||||
|
|
||||||
|
_apu_pulse1_last_period dw 0 ; optimization
|
||||||
|
|
||||||
|
|
||||||
APU_PULSE2
|
APU_PULSE2
|
||||||
APU_PULSE2_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume
|
APU_PULSE2_REG1 ds 1 ; DDLC NNNN - Duty, length counter halt, constant volume/evelope, envelope period/volume
|
||||||
APU_PULSE2_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
APU_PULSE2_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
||||||
APU_PULSE2_REG3 ds 1 ; LLLL LLLL - Timer Low
|
APU_PULSE2_REG3 ds 1 ; LLLL LLLL - Timer Low
|
||||||
APU_PULSE2_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope)
|
APU_PULSE2_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope)
|
||||||
|
|
||||||
|
APU_PULSE2_LENGTH_COUNTER dfb 0 ; internal register for the length counter
|
||||||
|
APU_PULSE2_RELOAD_FLAG dfb 0 ; internal register to reload the sweep divider value
|
||||||
|
APU_PULSE2_SWEEP_DIVIDER dfb 0 ; internal register to track the sweep divider value
|
||||||
|
APU_PULSE2_TARGET_PERIOD dw 0 ; internal register to hold the sweep unit target period
|
||||||
|
APU_PULSE2_CURRENT_PERIOD dw 0 ; internal register to hold the current period driving the oscillator
|
||||||
|
APU_PULSE2_MUTE dfb 0
|
||||||
|
APU_PULSE2_START_FLAG dfb 0
|
||||||
|
APU_PULSE2_ENVELOPE_DIVIDER dfb 0
|
||||||
|
APU_PULSE2_ENVELOPE dfb 0
|
||||||
|
|
||||||
|
_apu_pulse2_last_period dw 0 ; optimization
|
||||||
|
|
||||||
|
|
||||||
APU_TRIANGLE
|
APU_TRIANGLE
|
||||||
APU_TRIANGLE_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume
|
APU_TRIANGLE_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume
|
||||||
APU_TRIANGLE_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
APU_TRIANGLE_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count
|
||||||
|
@ -564,32 +789,103 @@ APU_PULSE1_REG1_WRITE ENT
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE1_REG2_WRITE ENT
|
APU_PULSE1_REG2_WRITE ENT
|
||||||
|
php
|
||||||
|
pha
|
||||||
stal APU_PULSE1_REG2
|
stal APU_PULSE1_REG2
|
||||||
|
lda #1
|
||||||
|
stal APU_PULSE1_RELOAD_FLAG ; mark that this register was written to
|
||||||
|
pla
|
||||||
|
plp
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE1_REG3_WRITE ENT
|
APU_PULSE1_REG3_WRITE ENT
|
||||||
|
stal APU_PULSE1_CURRENT_PERIOD
|
||||||
stal APU_PULSE1_REG3
|
stal APU_PULSE1_REG3
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE1_REG4_WRITE ENT
|
APU_PULSE1_REG4_WRITE ENT
|
||||||
|
php
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
|
||||||
stal APU_PULSE1_REG4
|
stal APU_PULSE1_REG4
|
||||||
|
and #$07
|
||||||
|
stal APU_PULSE1_CURRENT_PERIOD+1
|
||||||
|
|
||||||
|
; If the APU_STATUS bit is enabled, then load the length counter
|
||||||
|
ldal APU_STATUS
|
||||||
|
bit #$01
|
||||||
|
beq :no_reload
|
||||||
|
|
||||||
|
ldal APU_PULSE1_REG4
|
||||||
|
and #$F8
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
tax
|
||||||
|
ldal LengthTable,x
|
||||||
|
stal APU_PULSE1_LENGTH_COUNTER ; Immediately start the counter
|
||||||
|
lda #1
|
||||||
|
stal APU_PULSE1_START_FLAG
|
||||||
|
|
||||||
|
:no_reload
|
||||||
|
pla
|
||||||
|
plx
|
||||||
|
plp
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
|
; From https://www.nesdev.org/wiki/APU_Length_Counter
|
||||||
|
LengthTable
|
||||||
|
db 10,254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14
|
||||||
|
db 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
|
||||||
|
|
||||||
APU_PULSE2_REG1_WRITE ENT
|
APU_PULSE2_REG1_WRITE ENT
|
||||||
stal APU_PULSE2_REG1
|
stal APU_PULSE2_REG1
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE2_REG2_WRITE ENT
|
APU_PULSE2_REG2_WRITE ENT
|
||||||
|
php
|
||||||
|
pha
|
||||||
stal APU_PULSE2_REG2
|
stal APU_PULSE2_REG2
|
||||||
|
lda #1
|
||||||
|
stal APU_PULSE2_RELOAD_FLAG
|
||||||
|
pla
|
||||||
|
plp
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE2_REG3_WRITE ENT
|
APU_PULSE2_REG3_WRITE ENT
|
||||||
|
stal APU_PULSE2_CURRENT_PERIOD
|
||||||
stal APU_PULSE2_REG3
|
stal APU_PULSE2_REG3
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
APU_PULSE2_REG4_WRITE ENT
|
APU_PULSE2_REG4_WRITE ENT
|
||||||
|
php
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
|
||||||
stal APU_PULSE2_REG4
|
stal APU_PULSE2_REG4
|
||||||
|
and #$07
|
||||||
|
stal APU_PULSE2_CURRENT_PERIOD+1
|
||||||
|
|
||||||
|
ldal APU_STATUS
|
||||||
|
bit #$01
|
||||||
|
beq :no_reload
|
||||||
|
|
||||||
|
ldal APU_PULSE2_REG4
|
||||||
|
and #$F8
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
tax
|
||||||
|
ldal LengthTable,x
|
||||||
|
stal APU_PULSE2_LENGTH_COUNTER ; Immediately start the counter
|
||||||
|
lda #1
|
||||||
|
stal APU_PULSE2_START_FLAG
|
||||||
|
|
||||||
|
:no_reload
|
||||||
|
pla
|
||||||
|
plx
|
||||||
|
plp
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
|
|
||||||
|
@ -614,23 +910,21 @@ APU_STATUS_WRITE ENT
|
||||||
stal APU_STATUS
|
stal APU_STATUS
|
||||||
pha
|
pha
|
||||||
|
|
||||||
; Pulse 1 is OSC 0
|
; From NESDev Wiki: When the enabled bit is cleared (via $4015), the length counter is forced to 0
|
||||||
bit #$01
|
; and cannot be changed until enabled is set again (the length counter's previous value is lost).
|
||||||
beq :pulse1_off
|
; There is no immediate effect when enabled is set.
|
||||||
; _SetDOCReg #$40+pulse1_oscillator;#128
|
|
||||||
bra :pulse1_end
|
|
||||||
:pulse1_off
|
|
||||||
; _SetDOCReg #$40+pulse1_oscillator;#0
|
|
||||||
:pulse1_end
|
|
||||||
|
|
||||||
; Pulse 2 is OSC 2
|
; Pulse 1
|
||||||
|
bit #$01
|
||||||
|
bne :pulse1_on
|
||||||
|
stz APU_PULSE1_LENGTH_COUNTER
|
||||||
|
:pulse1_on
|
||||||
|
|
||||||
|
; Pulse 2
|
||||||
bit #$02
|
bit #$02
|
||||||
beq :pulse2_off
|
bne :pulse2_on
|
||||||
; _SetDOCReg #$40+pulse2_oscillator;#128
|
stz APU_PULSE2_LENGTH_COUNTER
|
||||||
bra :pulse2_end
|
:pulse2_on
|
||||||
:pulse2_off
|
|
||||||
; _SetDOCReg #$40+pulse2_oscillator;#0
|
|
||||||
:pulse2_end
|
|
||||||
|
|
||||||
; Triangle is OSC 4
|
; Triangle is OSC 4
|
||||||
; bit #$03
|
; bit #$03
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
; draw char at loc
|
; draw char at loc
|
||||||
; update loc
|
; update loc
|
||||||
; see if length hit - no? back to draw char
|
; see if length hit - no? back to draw char
|
||||||
rel
|
; rel
|
||||||
mx %00
|
mx %00
|
||||||
]F_Length ds 2 ;length of string (only one byte currently used)
|
]F_Length ds 2 ;length of string (only one byte currently used)
|
||||||
]F_CharIdx ds 2 ;index of current character
|
]F_CharIdx ds 2 ;index of current character
|
||||||
|
@ -19,6 +19,55 @@
|
||||||
]F_StrPtr equ $01 ;pointer to string (including length byte) / DP
|
]F_StrPtr equ $01 ;pointer to string (including length byte) / DP
|
||||||
]F_StrClr equ $03
|
]F_StrClr equ $03
|
||||||
|
|
||||||
|
;x = TopLeft screen pos
|
||||||
|
;y = font mask
|
||||||
|
;a = 8-bit char
|
||||||
|
DrawBottom
|
||||||
|
pha ; local variable space
|
||||||
|
pha
|
||||||
|
tsc
|
||||||
|
phd
|
||||||
|
tcd
|
||||||
|
|
||||||
|
sty ]F_StrClr
|
||||||
|
lda 3,s
|
||||||
|
sec
|
||||||
|
sbc #' '
|
||||||
|
asl ;*2
|
||||||
|
tay
|
||||||
|
|
||||||
|
jsr drawBottom
|
||||||
|
|
||||||
|
pld
|
||||||
|
pla
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
|
||||||
|
;x = TopLeft screen pos
|
||||||
|
;y = font mask
|
||||||
|
;a = 8-bit char
|
||||||
|
DrawChar
|
||||||
|
pha ; local variable space
|
||||||
|
pha
|
||||||
|
tsc
|
||||||
|
phd
|
||||||
|
tcd
|
||||||
|
|
||||||
|
sty ]F_StrClr
|
||||||
|
lda 3,s
|
||||||
|
sec
|
||||||
|
sbc #' '
|
||||||
|
asl ;*2
|
||||||
|
tay
|
||||||
|
|
||||||
|
jsr drawChar
|
||||||
|
|
||||||
|
pld
|
||||||
|
pla
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
|
||||||
|
|
||||||
DrawString
|
DrawString
|
||||||
pha ; local variable space
|
pha ; local variable space
|
||||||
pha
|
pha
|
||||||
|
@ -52,16 +101,20 @@ NextChar lda ]F_CharIdx
|
||||||
asl ;*2
|
asl ;*2
|
||||||
tay
|
tay
|
||||||
ldx ]F_CurrentPos
|
ldx ]F_CurrentPos
|
||||||
jsr :drawChar
|
jsr drawChar
|
||||||
inc ]F_CurrentPos ;compare to addition time (?)
|
inc ]F_CurrentPos ;compare to addition time (?)
|
||||||
inc ]F_CurrentPos
|
inc ]F_CurrentPos
|
||||||
inc ]F_CurrentPos
|
inc ]F_CurrentPos
|
||||||
inc ]F_CurrentPos ;update screen pos (2 words=8 pixels)
|
inc ]F_CurrentPos ;update screen pos (2 words=8 pixels)
|
||||||
bra NextChar
|
bra NextChar
|
||||||
|
|
||||||
;x = TopLeft screen pos
|
drawBottom lda FontTable,y ;get real address of char data
|
||||||
;y = char table offset
|
sec
|
||||||
:drawChar lda FontTable,y ;get real address of char data
|
sbc #FontData ;pivot offset - now a is offset of fontdata
|
||||||
|
tay ;so we'll index with that
|
||||||
|
brl bottom
|
||||||
|
|
||||||
|
drawChar lda FontTable,y ;get real address of char data
|
||||||
sec
|
sec
|
||||||
sbc #FontData ;pivot offset - now a is offset of fontdata
|
sbc #FontData ;pivot offset - now a is offset of fontdata
|
||||||
tay ;so we'll index with that
|
tay ;so we'll index with that
|
||||||
|
@ -106,6 +159,7 @@ NextChar lda ]F_CharIdx
|
||||||
and ]F_StrClr
|
and ]F_StrClr
|
||||||
stal {$E12000+160*4+2},x
|
stal {$E12000+160*4+2},x
|
||||||
|
|
||||||
|
bottom
|
||||||
lda FontData+20,y
|
lda FontData+20,y
|
||||||
and ]F_StrClr
|
and ]F_StrClr
|
||||||
stal {$E12000+160*5},x
|
stal {$E12000+160*5},x
|
||||||
|
|
|
@ -722,30 +722,37 @@ APU_IND_X_REG3_W
|
||||||
:reg_tbl dw APU_PULSE1_REG3_W,APU_PULSE1_REG3_W
|
:reg_tbl dw APU_PULSE1_REG3_W,APU_PULSE1_REG3_W
|
||||||
dw APU_PULSE2_REG3_W,APU_PULSE2_REG3_W,
|
dw APU_PULSE2_REG3_W,APU_PULSE2_REG3_W,
|
||||||
dw APU_TRIANGLE_REG3_W,APU_TRIANGLE_REG3_W
|
dw APU_TRIANGLE_REG3_W,APU_TRIANGLE_REG3_W
|
||||||
|
dw NO_OP,NO_OP
|
||||||
|
|
||||||
APU_IND_X_REG4_W
|
APU_IND_X_REG4_W
|
||||||
jmp (:reg_tbl,x)
|
jmp (:reg_tbl,x)
|
||||||
:reg_tbl dw APU_PULSE1_REG4_W,APU_PULSE1_REG4_W
|
:reg_tbl dw APU_PULSE1_REG4_W,APU_PULSE1_REG4_W
|
||||||
dw APU_PULSE2_REG4_W,APU_PULSE2_REG4_W,
|
dw APU_PULSE2_REG4_W,APU_PULSE2_REG4_W,
|
||||||
dw APU_TRIANGLE_REG4_W,APU_TRIANGLE_REG4_W
|
dw APU_TRIANGLE_REG4_W,APU_TRIANGLE_REG4_W
|
||||||
|
dw NO_OP,NO_OP
|
||||||
|
|
||||||
APU_PULSE1_REG1_W
|
APU_PULSE1_REG1_W
|
||||||
jsl APU_PULSE1_REG1_WRITE
|
jsl APU_PULSE1_REG1_WRITE
|
||||||
|
NO_OP
|
||||||
rts
|
rts
|
||||||
APU_PULSE1_REG1_WX
|
APU_PULSE1_REG1_WX
|
||||||
|
phx
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
jsl APU_PULSE1_REG1_WRITE
|
jsl APU_PULSE1_REG1_WRITE
|
||||||
pla
|
pla
|
||||||
|
plx
|
||||||
rts
|
rts
|
||||||
APU_PULSE1_REG2_W
|
APU_PULSE1_REG2_W
|
||||||
jsl APU_PULSE1_REG2_WRITE
|
jsl APU_PULSE1_REG2_WRITE
|
||||||
rts
|
rts
|
||||||
APU_PULSE1_REG2_WY
|
APU_PULSE1_REG2_WY
|
||||||
|
phy
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
jsl APU_PULSE1_REG2_WRITE
|
jsl APU_PULSE1_REG2_WRITE
|
||||||
pla
|
pla
|
||||||
|
ply
|
||||||
rts
|
rts
|
||||||
APU_PULSE1_REG3_W
|
APU_PULSE1_REG3_W
|
||||||
jsl APU_PULSE1_REG3_WRITE
|
jsl APU_PULSE1_REG3_WRITE
|
||||||
|
@ -758,25 +765,31 @@ APU_PULSE2_REG1_W
|
||||||
jsl APU_PULSE2_REG1_WRITE
|
jsl APU_PULSE2_REG1_WRITE
|
||||||
rts
|
rts
|
||||||
APU_PULSE2_REG1_WX
|
APU_PULSE2_REG1_WX
|
||||||
|
phx
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
jsl APU_PULSE2_REG1_WRITE
|
jsl APU_PULSE2_REG1_WRITE
|
||||||
pla
|
pla
|
||||||
|
plx
|
||||||
rts
|
rts
|
||||||
APU_PULSE2_REG2_W
|
APU_PULSE2_REG2_W
|
||||||
jsl APU_PULSE2_REG2_WRITE
|
jsl APU_PULSE2_REG2_WRITE
|
||||||
rts
|
rts
|
||||||
APU_PULSE2_REG2_WY
|
APU_PULSE2_REG2_WY
|
||||||
|
phy
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
jsl APU_PULSE2_REG2_WRITE
|
jsl APU_PULSE2_REG2_WRITE
|
||||||
pla
|
pla
|
||||||
|
ply
|
||||||
rts
|
rts
|
||||||
APU_PULSE2_REG2_WX
|
APU_PULSE2_REG2_WX
|
||||||
|
phx
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
jsl APU_PULSE2_REG2_WRITE
|
jsl APU_PULSE2_REG2_WRITE
|
||||||
pla
|
pla
|
||||||
|
plx
|
||||||
rts
|
rts
|
||||||
APU_PULSE2_REG3_W
|
APU_PULSE2_REG3_W
|
||||||
jsl APU_PULSE2_REG3_WRITE
|
jsl APU_PULSE2_REG3_WRITE
|
||||||
|
@ -802,7 +815,10 @@ APU_STATUS_W
|
||||||
rts
|
rts
|
||||||
APU_STATUS_WX
|
APU_STATUS_WX
|
||||||
phx
|
phx
|
||||||
|
pha
|
||||||
|
txa
|
||||||
jsl APU_STATUS_WRITE
|
jsl APU_STATUS_WRITE
|
||||||
|
pla
|
||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
; Hooks to call back to the GTE harness for PPU memory-mapped accesses
|
; Hooks to call back to the GTE harness for PPU memory-mapped accesses
|
||||||
|
|
Loading…
Reference in New Issue
Block a user