Topic: 6502 Conversion of Squeeker Plus Engine

I’ve been working on converting the Z80 Squeeker Plus engine by utz to the 6502.  A work in progress but getting reasonable results so far.

Drums are very off compared to the Z80, and the pitch still needs some work.

You can see the current progress here:

https://m.youtube.com/watch?v=W1Br7rr8cps

If you’re interested in the code (and can help eliminating bugs / inconsistencies / misinterpretations) this is what I have so far (BeebAsm format for the BBC Micro):

VIA_DDRA    = $FE43
VIA_DDRB    = $FE42
VIA_ORA     = $FE41
VIA_ORB     = $FE40
SN_LOUD     = $90
SN_MUTE     = $9F

SAMPLES_PER_FRAME = 260
SAMPLES_LO = SAMPLES_PER_FRAME AND 255
SAMPLES_HI = SAMPLES_PER_FRAME DIV 256

ORG 0

.seq_ptr        SKIP 2
.row_timer      SKIP 1
.sample_count   SKIP 2
.tmp_mix        SKIP 1
.ctrl_a         SKIP 1
.ctrl_b         SKIP 1
.pat_ptr        SKIP 2
.drum_flag      SKIP 1
.drum_index     SKIP 1
.drum_delay     SKIP 1
.drum_state     SKIP 1

.ch1_f          SKIP 2
.ch1_a          SKIP 2
.ch1_e          SKIP 2
.ch1_d          SKIP 1
.ch1_n          SKIP 1

.ch2_f          SKIP 2
.ch2_a          SKIP 2
.ch2_e          SKIP 2
.ch2_d          SKIP 1
.ch2_n          SKIP 1

.ch3_f          SKIP 2
.ch3_a          SKIP 2
.ch3_e          SKIP 2
.ch3_d          SKIP 1

.ch4_f          SKIP 2
.ch4_a          SKIP 2
.ch4_e          SKIP 2
.ch4_d          SKIP 1
.ch4_s          SKIP 1

ORG $1100
.start
    SEI
    JSR init_hw
    
    LDA #LO(music_data+2)
    STA seq_ptr
    LDA #HI(music_data+2)
    STA seq_ptr+1
    
    JSR fetch_new_pattern
    JMP check_row

.pattern_end 
    LDA seq_ptr
    CLC
    ADC #2
    STA seq_ptr
    BCC p_next
    INC seq_ptr+1
.p_next
    LDY #0
    LDA (seq_ptr),Y
    STA pat_ptr
    INY
    LDA (seq_ptr),Y
    STA pat_ptr+1
    
    LDA pat_ptr
    ORA pat_ptr+1
    BNE check_row
    
    LDA music_data
    STA seq_ptr
    LDA music_data+1
    STA seq_ptr+1
    JMP pattern_end

.check_row
    LDY #0
    LDA (pat_ptr),Y
    CMP #$40
    BEQ pattern_end

.rdseq
    LDA (pat_ptr),Y
    INY
    STA ctrl_a
    LDA (pat_ptr),Y
    INY
    STA row_timer
       
    LDA (pat_ptr),Y
    STA ch2_n
    INY
    LDA (pat_ptr),Y
    STA ch1_n
    INY

    LDA ctrl_a
    LSR A
    BCS ld2
    LDA (pat_ptr),Y
    STA ch1_f
    INY
    LDA (pat_ptr),Y
    STA ch1_f+1
    INY
    LDA (pat_ptr),Y
    STA ch1_e
    INY
    LDA (pat_ptr),Y
    STA ch1_e+1
    INY
    LDA #0
    STA ch1_a
    STA ch1_a+1
    STY tmp_mix
    LDY #0
    LDA (ch1_e),Y
    STA ch1_d
    LDY tmp_mix
.ld2
    LDA ctrl_a
    AND #4
    BNE ld3
    LDA (pat_ptr),Y
    STA ch2_f
    INY
    LDA (pat_ptr),Y
    STA ch2_f+1
    INY
    LDA (pat_ptr),Y
    STA ch2_e
    INY
    LDA (pat_ptr),Y
    STA ch2_e+1
    INY
    LDA #0
    STA ch2_a
    STA ch2_a+1
    STY tmp_mix
    LDY #0
    LDA (ch2_e),Y
    STA ch2_d
    LDY tmp_mix
.ld3
    BIT ctrl_a
    BMI ld4
    LDA (pat_ptr),Y
    STA ch3_f
    INY
    LDA (pat_ptr),Y
    STA ch3_f+1
    INY
    LDA (pat_ptr),Y
    STA ch3_e
    INY
    LDA (pat_ptr),Y
    STA ch3_e+1
    INY
    LDA #0
    STA ch3_a
    STA ch3_a+1
    STY tmp_mix
    LDY #0
    LDA (ch3_e),Y
    STA ch3_d
    LDY tmp_mix
.ld4
    LDA (pat_ptr),Y
    STA ctrl_b
    INY
    INY
    
    LDA #0
    STA drum_flag
    LDA ctrl_b
    AND #$04
    BEQ no_kick
    LDA #1
    STA drum_flag
    LDA #0
    STA drum_index
    STA drum_delay
    LDA #SN_MUTE
    STA drum_state
.no_kick
    LDA ctrl_b
    AND #$80
    BEQ no_hat
    LDA #2
    STA drum_flag
    LDA #0
    STA drum_index
    STA drum_delay
    LDA #SN_MUTE
    STA drum_state
.no_hat
    
    LDA ctrl_b
    AND #$40
    BNE skip4
    LDA (pat_ptr),Y
    STA ch4_f
    INY
    LDA (pat_ptr),Y
    STA ch4_f+1
    INY
    LDA (pat_ptr),Y
    STA ch4_e
    INY
    LDA (pat_ptr),Y
    STA ch4_e+1
    INY
    LDA #0
    STA ch4_a
    STA ch4_a+1
    STY tmp_mix
    LDY #0
    LDA (ch4_e),Y
    STA ch4_d
    LDY tmp_mix
.skip4
    LDA ctrl_b
    AND #1
    STA ch4_s

    TYA
    CLC
    ADC pat_ptr
    STA pat_ptr
    BCC p_adv
    INC pat_ptr+1
.p_adv
    LDA ch1_n
    BEQ ch1_no_noise_smc
    LDA #$6A
    STA ch1_noise_op
    LDA #$EA
    STA ch1_noise_op+1
    JMP ch2_smc
.ch1_no_noise_smc
    LDA #$EA
    STA ch1_noise_op
    STA ch1_noise_op+1
    
.ch2_smc
    LDA ch2_n
    BEQ ch2_no_noise_smc
    LDA #$6A
    STA ch2_noise_op
    LDA #$EA
    STA ch2_noise_op+1
    JMP start_synth
.ch2_no_noise_smc
    LDA #$EA
    STA ch2_noise_op
    STA ch2_noise_op+1
    
.start_synth
    LDA #SAMPLES_LO
    STA sample_count
    LDA #SAMPLES_HI
    STA sample_count+1
    CLC

.synth_loop
    LDA #0
    STA tmp_mix

    \ Channel 1
    LDA ch1_a
    ADC ch1_f
    STA ch1_a
    LDA ch1_a+1
    ADC ch1_f+1
    STA ch1_a+1
.ch1_noise_op
    NOP
    NOP
    ADC ch1_d
    ROL tmp_mix

    \ Channel 2
    LDA ch2_a
    ADC ch2_f
    STA ch2_a
    LDA ch2_a+1
    ADC ch2_f+1
    STA ch2_a+1
.ch2_noise_op
    NOP
    NOP
    ADC ch2_d
    ROL tmp_mix

    \ Channel 3
    LDA ch3_a
    ADC ch3_f
    STA ch3_a
    LDA ch3_a+1
    ADC ch3_f+1
    STA ch3_a+1
    ADC ch3_d
    ROL tmp_mix

    \ Channel 4
    LDA ch4_a
    ADC ch4_f
    STA ch4_a
    LDA ch4_a+1
    ADC ch4_f+1
    STA ch4_a+1
    ADC ch4_d
    ROL tmp_mix

    LDA tmp_mix
    ADC #$0F
    TAX

    CPX #$10
    LDA #SN_MUTE        
    BCS vol_set        
    LDA #SN_LOUD       
.vol_set
    TAY                

    LDX drum_flag
    BEQ output_no_drum 
    
    LDX drum_delay
    BEQ need_new_sample
    DEC drum_delay
    LDA drum_state
    JMP mix_tone_drum
    
.need_new_sample
    LDX drum_flag
    DEX
    BEQ need_load_kick
    DEX
    BEQ need_load_hat
    LDA drum_state
    JMP mix_tone_drum
    
.need_load_kick
    LDX drum_index
    CPX #20
    BCS need_kick_end
    LDA kick_data,X
    STA drum_delay
    INC drum_index
    LDA drum_state
    EOR #$0F
    STA drum_state
    JMP mix_tone_drum

.need_kick_end
    LDA #0
    STA drum_flag
    STA drum_delay
    LDA drum_state
    JMP mix_tone_drum

.need_load_hat
    LDX drum_index
    CPX #20
    BCS need_hat_end
    LDA hat_data,X
    STA drum_delay
    INC drum_index
    LDA drum_state
    EOR #$0F
    STA drum_state
    JMP mix_tone_drum

.need_hat_end
    LDA #0
    STA drum_flag
    STA drum_delay
    LDA drum_state
    JMP mix_tone_drum

.output_no_drum
    TYA                
    STA VIA_ORA
    LDA #0
    STA VIA_ORB
    LDA #8
    STA VIA_ORB
    JMP sample_decrement
    
\ Mix tone and drum
.mix_tone_drum
    STA ctrl_a         
    CPY #SN_LOUD
    BNE drum_only      
    CMP #SN_LOUD
    BNE tone_only       
    LDA #$98            
    JMP output_mixed
.drum_only
    LDA ctrl_a          
    JMP output_mixed
.tone_only
    TYA                 
.output_mixed
    STA VIA_ORA
    LDA #0
    STA VIA_ORB
    LDA #8
    STA VIA_ORB

.sample_decrement
    LDA sample_count
    BNE dec_lo
    DEC sample_count+1
.dec_lo
    DEC sample_count
    LDA sample_count
    ORA sample_count+1
    BEQ update_timer
    JMP synth_loop

.update_timer
    LDY #0
    INC ch1_e
    BNE e1
    INC ch1_e+1
.e1 
    LDA (ch1_e),Y
    CMP #$80
    BEQ e2
    STA ch1_d
.e2 
    INC ch2_e
    BNE e2_skip
    INC ch2_e+1
.e2_skip 
    LDA (ch2_e),Y
    CMP #$80
    BEQ e3
    STA ch2_d
.e3 
    INC ch3_e
    BNE e3_skip
    INC ch3_e+1
.e3_skip 
    LDA (ch3_e),Y
    CMP #$80
    BEQ e4
    STA ch3_d
.e4 
    INC ch4_e
    BNE e4_skip
    INC ch4_e+1
.e4_skip 
    LDA (ch4_e),Y
    CMP #$80
    BEQ slide_proc
    STA ch4_d

.slide_proc
    LDA ch4_s
    BEQ noslide
    LDA ch4_f+1
    LSR A
    TAX
    LDA ch4_f
    ROR A
    TAY
    SEC
    LDA ch4_f
    STY tmp_mix
    SBC tmp_mix
    STA ch4_f
    LDA ch4_f+1
    STX tmp_mix
    SBC tmp_mix
    STA ch4_f+1
.noslide
    DEC row_timer
    BNE play_same_row
    JMP check_row

.play_same_row
    LDA #SAMPLES_LO
    STA sample_count
    LDA #SAMPLES_HI
    STA sample_count+1
    CLC
    JMP synth_loop

.fetch_new_pattern
    LDY #0
    LDA (seq_ptr),Y
    STA pat_ptr
    INY
    LDA (seq_ptr),Y
    STA pat_ptr+1
    
    LDA pat_ptr
    ORA pat_ptr+1
    BNE fetch_done
    
    LDA music_data
    STA seq_ptr
    LDA music_data+1
    STA seq_ptr+1
    JMP fetch_new_pattern
.fetch_done
    RTS

.kick_data
    EQUB 1,1,1,1,2,2,2,2
    EQUB 3,3,3,4,4,4,5,5
    EQUB 6,6,7,7

.hat_data
    EQUB 16,3,12,6,9
    EQUB 20,4,8,2,14
    EQUB 9,17,5,8,12
    EQUB 4,7,16,13,22
    EQUB 5,3,16,3,12

.init_hw
    LDA #$9F
    JSR sn_w
    LDA #$BF
    JSR sn_w
    LDA #$DF
    JSR sn_w
    LDA #$FF
    JSR sn_w
    LDA #$FF
    STA VIA_DDRA
    LDA #$0F
    STA VIA_DDRB
    LDA #$81
    JSR sn_w
    LDA #$00
    JSR sn_w
    RTS
.sn_w
    STA VIA_ORA
    LDX #0
    STX VIA_ORB
    LDX #8
    STX VIA_ORB
    RTS

ORG $2000
    INCLUDE "tracks\Squeeker Plus\1-bit_high_and_rising.asm"
.end
SAVE "MAIN",start,end

2

Re: 6502 Conversion of Squeeker Plus Engine

Cheers negative charge, and thanks for your work! Sounds pretty good, save for the drums. Pitch shouldn't be too hard to fix, since your code is faster thatn the original smile

My 6502 skills are unfortunately rather rusty, so I can't understand your drum code at a quick glance, but the drum data seems to be off. The kick drum PWM period should double every 4 half-cycles. The hat data probably needs to be longer for the result to sound somewhat like white noise.