26

Re: 1-bit sound on BBC Micro / Acorn Electron

The noise channel probably won't be useful since it starts on a low cycle.

However https://www.smspower.org/Development/SN76489 claims that

If the register value is zero or one then the output is a constant value of +1. This is often used for sample playback on the SN76489.

so we won't get any more volume range than we already have anyway. Might still be worth a try to see if 0 gives a better range than 1.

Re: 1-bit sound on BBC Micro / Acorn Electron

Unfortunately I don’t hear any difference.  However, I’ve now ported the code to the Acorn Electron which only has an on/off state for sound (no volume control) and I can hear a recognisable tune - it has a slower 1MHz processor so needs some adjustment.  Based on what I’m hearing there may still be some issues with the pattern parsing.  I’ll look through a disassembly of XXL’s Atari Tritone code to see if there’s anything obviously different.

Re: 1-bit sound on BBC Micro / Acorn Electron

Posting an update as I finally managed to get the Tritone engine working for the 6502.  Drums aren’t implemented, but sounds pretty good.

;Tritone beeper music engine
;Original Z80 code by Shiru 03'2011
;6502 engine port by utz 03'2024
;BBC Micro port by Negative Charge 01'2025

; Constants
OSBYTE                  = $FFF4
OSWRCH                  = $FFEE
OSNEWL                  = $FFE7
SHEILABASE              = $FE00
SYSVIA_DDRA             = SHEILABASE + $43
SYSVIA_ORAS             = SHEILABASE + $4F
SYSVIA_REGB             = SHEILABASE + $40

DISPLAY_START           = $7c00

; Zero Page
ORG     $60
GUARD   $90

.vars_start

.ch0_acc_lo         SKIP 1
.ch0_acc_hi         SKIP 1
.ch1_acc_lo         SKIP 1
.ch1_acc_hi         SKIP 1
.ch2_acc_lo         SKIP 1
.ch2_acc_hi         SKIP 1

.ch0_div_lo         SKIP 1
.ch0_div_hi         SKIP 1
.ch1_div_lo         SKIP 1
.ch1_div_hi         SKIP 1
.ch2_div_lo         SKIP 1
.ch2_div_hi         SKIP 1

.ch0_duty           SKIP 1
.ch1_duty           SKIP 1
.ch2_duty           SKIP 1

.temp_output        SKIP 1

.loop_ptr           SKIP 2
.pattern_ptr        SKIP 2
.order_ptr          SKIP 2
.drum               SKIP 1
.row_length         SKIP 2

.vars_end

ORG     &1900
GUARD   DISPLAY_START

.start

MACRO sound_write_slow
    sta     SYSVIA_ORAS
    lda     #%00000000
    sta     SYSVIA_REGB
    nop:nop:nop
    lda     #%00001000
    sta     SYSVIA_REGB
ENDMACRO

MACRO RESET_SOUND_CHIP
    lda     #%11111111
    sound_write_slow
    lda     #%11011111
    sound_write_slow
    lda     #%10111111
    sound_write_slow
    lda     #%10011111
    sound_write_slow
ENDMACRO

.init
    lda     #%11111111
    sta     SYSVIA_DDRA

    sei

    RESET_SOUND_CHIP

    lda     #%10000001
    sound_write_slow
    lda     #%00000000
    sound_write_slow
    
    lda     #%10100001
    sound_write_slow
    lda     #%00000000
    sound_write_slow
    
    lda     #%11000001
    sound_write_slow
    lda     #%00000000
    sound_write_slow

    lda     #%00000000
    sta     SYSVIA_REGB
    
    lda     #LO(music_data)
    ldx     #HI(music_data)

.play
    pha
    txa
    pha
    
    ; Clear ZP
    lda     #0
    ldx     #vars_end-vars_start
.clear_loop
    sta     vars_start,x
    dex
    bpl     clear_loop
    
    pla
    sta     pattern_ptr+1
    pla
    sta     pattern_ptr+0

    lda     pattern_ptr+0
    clc
    adc     #2
    sta     loop_ptr+0
    lda     pattern_ptr+1
    adc     #0
    sta     loop_ptr+1

.play_loop
    lda     pattern_ptr+0
    sta     order_ptr+0
    lda     pattern_ptr+1
    sta     order_ptr+1

    ldy     #0
    lda     (pattern_ptr),y
    tax
    iny
    lda     (pattern_ptr),y
    
    bne     no_loop
    cpx     #0
    bne     no_loop
    
.return_loop
    lda     loop_ptr+0
    sta     pattern_ptr+0
    lda     loop_ptr+1
    sta     pattern_ptr+1
    jmp     play_loop

.no_loop
    ; Set pattern_ptr to pattern data
    sta     pattern_ptr+1
    stx     pattern_ptr+0
    
    ; Read speed from pattern
    ldy     #0
    lda     (pattern_ptr),y
    sta     row_length+0
    iny
    lda     (pattern_ptr),y
    sta     row_length+1
    
    lda     pattern_ptr+0
    adc     #1
    sta     pattern_ptr+0
    bcc     row
    inc     pattern_ptr+1
    jmp     row

.pattern_end
    lda     order_ptr+0
    clc
    adc     #2
    sta     pattern_ptr+0
    lda     order_ptr+1
    adc     #0
    sta     pattern_ptr+1
    jmp     play_loop

.row
    ldy     #0
    lda     (pattern_ptr),y
    iny
    
    cmp     #255
    beq     pattern_end
    
    cmp     #128
    bcs     ch0
    
    cmp     #2
    bcc     ch0
    
    sta     drum
    lda     (pattern_ptr),y
    iny

.ch0
    cmp     #1
    beq     skip_ch0
    
    cmp     #2
    bcs     ch0_note
    
    sta     ch0_duty
    sta     ch0_div_lo
    sta     ch0_div_hi
    jmp     skip_ch0

.ch0_note
    tax
    and     #$0f
    sta     ch0_div_hi
    txa
    and     #$f0
    sta     ch0_duty
    lda     (pattern_ptr),y
    iny
    sta     ch0_div_lo

.skip_ch0
    lda     (pattern_ptr),y
    iny
    
    cmp     #1
    beq     skip_ch1
    
    cmp     #2
    bcs     ch1_note
    
    sta     ch1_duty
    sta     ch1_div_lo
    sta     ch1_div_hi
    jmp     skip_ch1

.ch1_note
    tax
    and     #$0f
    sta     ch1_div_hi
    txa
    and     #$f0
    sta     ch1_duty
    lda     (pattern_ptr),y
    iny
    sta     ch1_div_lo

.skip_ch1
    lda     (pattern_ptr),y
    iny
    
    cmp     #1
    beq     skip_ch2
    
    cmp     #2
    bcs     ch2_note
    
    sta     ch2_duty
    sta     ch2_div_lo
    sta     ch2_div_hi
    jmp     skip_ch2

.ch2_note
    tax
    and     #$0f
    sta     ch2_div_hi
    txa
    and     #$f0
    sta     ch2_duty
    lda     (pattern_ptr),y
    iny
    sta     ch2_div_lo

.skip_ch2
    tya
    clc
    adc     pattern_ptr+0
    sta     pattern_ptr+0
    bcc     setup_play_loop
    inc     pattern_ptr+1

.setup_play_loop
    lda     row_length+0
    ora     row_length+1
    beq     row_finished
    
    ldx     row_length+0
    ldy     row_length+1

.play_note
    ; CH0
    lda     ch0_acc_lo          ; 3
    adc     ch0_div_lo          ; 3
    sta     ch0_acc_lo          ; 3
    lda     ch0_acc_hi          ; 3
    adc     ch0_div_hi          ; 3
    sta     ch0_acc_hi          ; 3
    cmp     ch0_duty            ; 3 - carry set if acc_hi >= duty
    lda     #$00                ; 2
    sbc     #$00                ; 2 - A = $00 if carry, $FF if !carry
    sta     temp_output         ; 3
    
    ; CH1
    lda     ch1_acc_lo          ; 3
    adc     ch1_div_lo          ; 3
    sta     ch1_acc_lo          ; 3
    lda     ch1_acc_hi          ; 3
    adc     ch1_div_hi          ; 3
    sta     ch1_acc_hi          ; 3
    cmp     ch1_duty            ; 3
    lda     #$00                ; 2
    sbc     #$00                ; 2
    ora     temp_output         ; 3 - mix CH0 | CH1
    sta     temp_output         ; 3
    
    ; CH2
    lda     ch2_acc_lo          ; 3
    adc     ch2_div_lo          ; 3
    sta     ch2_acc_lo          ; 3
    lda     ch2_acc_hi          ; 3
    adc     ch2_div_hi          ; 3
    sta     ch2_acc_hi          ; 3
    cmp     ch2_duty            ; 3
    lda     #$00                ; 2
    sbc     #$00                ; 2
    ora     temp_output         ; 3 - final mix (A = CH0 | CH1 | CH2)
    
    bne     play_sound          ; 2/3
    lda     #$9F                ; 2 - silent
    bne     play_output         ; 3 (always branches)
.play_sound
    lda     #$90                ; 2 - sound
.play_output
    sta     SYSVIA_ORAS         ; 4
    dex                         ; 2
    bne     play_note           ; 3
    dey                         ; 2
    bne     play_note           ; 3
    
.row_finished
    jmp     row

INCLUDE "tracks\lb_zx_tritone.6502" 

.end

SAVE "MAIN",start,end,init