diff --git a/Makefile b/Makefile index b5d141d..e6ec30d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: test_mock freq +all: test_mock scales freq .PHONY: test_mock @@ -8,6 +8,10 @@ test_mock: src2dsk test_mock.s applewin.sh -d1 test_mock.dsk +scales: scales.s + src2dsk $< + applewin.sh -d1 $(basename $<).dsk + freq: freq.c g++ $(C_FLAGS) $< -o $@ diff --git a/scales.dsk b/scales.dsk new file mode 100644 index 0000000..96886da Binary files /dev/null and b/scales.dsk differ diff --git a/scales.s b/scales.s new file mode 100644 index 0000000..287960a --- /dev/null +++ b/scales.s @@ -0,0 +1,431 @@ +; Mockingboard Demo Scales +; Clean up by Michael Pohoreski +; http: https://github.com/Michaelangel007/apple2_mockingboard.git +; Assembler: cc65 + + __MAIN = $8000 + +; Uncomment next 2 lines for ProDOS + .word __MAIN ; 2 byte BLOAD address + .word __END - __MAIN ; 2 byte BLOAD size + +; Mockingboard Base Address + ADDR6522 = $C400 ; *** Default Mockingboard to slot 4 *** + +; Programmable Sound Generator + MOCK_TONE_LOW = $0 ; Tone Control Fine Reg 0/2/4: lo 8-bit + MOCK_TONE_HIGH = $1 ; Tone Control Coarse Reg 1/3/5: hi 4-bit + + MOCK_TONE_A_LO = $0 ; 12-bit + MOCK_TONE_A_HI = $1 + + MOCK_TONE_B_LO = $2 ; 12-bit + MOCK_TONE_B_HI = $3 + + MOCK_TONE_C_LO = $4 ; 12-bit + MOCK_TONE_C_HI = $5 + + MOCK_NOISE = $6 ; Noise Period Reg 6 : 5-bit + + ; a b x x x y y y + ; \___/ + ; C B A + ; 0=Tone Disabled + ; \___/ + ; C B A + ; 0=Noise Disabled + ; B input=0 + ; A input=0 + DISABLE_TONE_A = %000001 ; $01 + DISABLE_TONE_B = %000010 ; $02 + DISABLE_TONE_C = %000100 ; $04 + DISABLE_NOISE_A = %001000 ; $08 + DISABLE_NOISE_B = %010000 ; $10 + DISABLE_NOISE_C = %100000 ; $20 + MOCK_CHANNELS = $7 ; Disabled Chans Mask Reg 7 : 6-bit + + ; D s s s s + ; \_____/ + ; Static + ; 1=Dynamic + AMP_STATIC = %01111 + AMP_DYNAMIC = %10000 + MOCK_AMPLITUDE = $8 ; Amplitude Reg 8/9/A: 5-bit + MOCK_AMP_A = $8 + MOCK_AMP_B = $9 + MOCK_AMP_C = $A + + MOCK_ENV_LOW = $B ; Envelope Fine Reg B : lo 8-bit + MOCK_ENV_HIGH = $C ; Envelope Coarse Reg C : hi 8-bit + + ; C K A H + ; Continue + ; Attack + ; Alternate + ; Hold + ENV_CONT = %0001 + ENV_ATTACK = %0010 + ENV_ALT = %0100 + ENV_HOLD = %1000 + MOCK_ENV_TYPE = $D ; Envelope Shape Reg D : 4-bit + + MOCK_REG_FIRST = $0 + MOCK_REG_LAST = $D + MOCK_REG_NUM = $E + +; Left Speaker + MOCK1_OR_B = ADDR6522+0 ; OR Register B + MOCK1_OR_A = ADDR6522+1 ; OR Register A + MOCK1_DIR_B = ADDR6522+2 ; Data Direction Reg B + MOCK1_DIR_A = ADDR6522+3 ; Data Direction Reg A +; Right Speaker + MOCK2_OR_B = ADDR6522+0+$80 + MOCK2_OR_A = ADDR6522+1+$80 + MOCK2_DIR_B = ADDR6522+2+$80 + MOCK2_DIR_A = ADDR6522+3+$80 + + ; 6522 Commands + ; DIR BC1 BC0 + _6522_RESET = $0 ; 0 0 0 + _6522_PORT_B = $4 ; 1 0 0 + _6522_WRITE = $6 ; 1 1 0 + _6522_LATCH = $7 ; 1 1 1 + + _6522_PORT_A_OUT = $07 ; TODO: is $FF safe? + _6522_PORT_B_OUT = $FF + + + note_index = $FF + +; === CODE === + + .org __MAIN ; .org must come after header else offsets are wrong + +; Cleaned up code + jsr MOCK1_Init + jsr MOCK2_Init + + lda #0 + sta note_index + + ldx #demo_regs_1 + jsr MOCK1_Play + +play_loop: + ldy note_index + ldx demo_half_notes,y + bmi play_done + + jsr MOCK1_ANoteX + lda #0 + jsr DelayA + + inc note_index + bne play_loop + +play_done: + jmp MOCK1_Stop + +; === DATA === + +; $3E = Only enable Tone A +channels = DISABLE_NOISE_C | DISABLE_NOISE_B | DISABLE_NOISE_A | DISABLE_TONE_C | DISABLE_TONE_B + +demo_regs_1: + .byte $80,$00 ; [0] Tone A + .byte $80,$00 ; [2] Tone B + .byte $80,$00 ; [4] Tone C + .byte $00 ; [6] Noise + .byte channels ; [7] Disable + .byte $0F,$0F,$0F ; [8] Amp A, B, C + .byte $00 ; [B] Env Lo + .byte $00 ; [C] Env Hi + .byte $00 ; [D] Env Type + +demo_half_notes: + .byte $00 ; C4 + .byte $02 ; D4 + .byte $04 ; E4 + .byte $05 ; F4 + .byte $07 ; G4 + .byte $09 ; A4 + .byte $0B ; B4 + .byte $0C ; C5 + .byte $FF ; end-of-music + + ;Tone Control Fine 128 ($80) Register 0/2/4 + ;Tone Control Coarse 0 Register 1/3/5 + ;Noise Period 0 Register 6 + ;Enable Mask 62 ($3E) Register 7 + ;Amplitude 15 ($0F) Register 8/9/A + ;Envelope Fine 0 Register B + ;Envelope Coarse 0 Register C + ;Envelope Shape 0 Register D + +; ____________________ Utility ____________________ + +; ==================== +; Delay ((A * ? + ?) * (A * ? + ?) +; ==================== +DelayA: + tax + tay +@1: inx + bne @1 + iny + bne @1 + rts + +; ____________________ Mockingboard 1 ____________________ + +; ==================== +; Initialize Mockingboard +; ==================== +MOCK1_Init: + lda #_6522_PORT_B_OUT + sta MOCK1_DIR_B +; lda #_6522_PORT_A_OUT + sta MOCK1_DIR_A + ; NOTE: Intentional fall-into reset! +MOCK1_Reset: + lda #_6522_RESET ; -->+ + .byte $2C ; | +MOCK1_SetReg: ; | + lda #_6522_LATCH ;-->+ | + .byte $2C ; | | +MOCK1_SetData: ; | | + lda #_6522_WRITE ; | | + sta MOCK1_OR_B ;<--+ <--+ + lda #_6522_PORT_B + sta MOCK1_OR_B + rts + +; ==================== +; IN: +; X = Note Index +; OUT: +; A = trash +; X = trash +; Y = trash +; ==================== +MOCK1_ANoteX: + lda #MOCK_TONE_A_LO + .byte $2c +MOCK1_BNoteX: + lda #MOCK_TONE_B_LO + .byte $2c +MOCK1_CNoteX: + lda #MOCK_TONE_C_LO + +MOCK1_nNoteX: + sta _mock_note+1 ; X = Mockingboard Register, 0=Tone A, 2=Tone B, 4=Tone C + + lda note_to_tone_hi,x + pha + lda note_to_tone_lo,x +_mock_note: + ldx #MOCK_TONE_A_LO ; *** SELF-MODIFIED! + jsr MOCK1_PokeXA + inx ; X = MOCK_TONE_A_HI + pla + ; NOTE: Intentional fall-into MOCK1_PokeXA ! + +; ==================== +; Plays one note +; IN: +; X = Register: $0 .. $D +; A = Value +; OUT: +; A = trash +; X = unchanged +; Y = trash +; ==================== +MOCK1_PokeXA: + TAY + ; NOTE: Intentional fall-into! + +; ==================== +; Poke (set) byte to Mockingboard +; IN: +; X = Register: $0 .. $D +; Y = Value +; OUT: +; A = trash +; X = unchanged +; Y = unchanged +; ==================== +MOCK1_PokeXY: + stx MOCK1_OR_A + jsr MOCK1_SetReg + sty MOCK1_OR_A + jmp MOCK1_SetData + +; ==================== +; Stop all sounds by setting all regs to zero +; ==================== +MOCK1_Stop: + LDX #mockN_off_data + ; NOTE: Intentional fall-into! + +; ==================== +; Plays one note by setting all Mockingboard registers +; IN: +; X = Low Address of registers +; Y = High Address of +; ==================== +MOCK1_Play: + STX _mock1_load_reg+1 ; *** SELF-MODIFIES! + STY _mock1_load_reg+2 +_mock1all_regs: + ldy #0 + ldx #MOCK_REG_FIRST +_mock1_load_reg: + lda mockN_off_data,X ; *** SELF-MODIFIED! + jsr MOCK1_PokeXA + inx + cpx #MOCK_REG_NUM ; BUG: $B ! + bcc _mock1_load_reg + rts + +; === DATA === + +note_to_tone_lo: + .byte $F4 ; C4 261.626 Hz + .byte $E6 ; C4# 277.183 Hz + .byte $D9 ; D4 293.665 Hz + .byte $CD ; D4# 311.127 Hz + .byte $C1 ; E4 329.628 Hz + .byte $B7 ; F4 349.228 Hz + .byte $AC ; F4# 369.994 Hz + .byte $A3 ; G4 391.995 Hz + .byte $99 ; G4# 415.305 Hz + .byte $91 ; A4 440.000 Hz + .byte $89 ; A4# 466.164 Hz + .byte $81 ; B4 493.883 Hz + + .byte $7A ; C5 523.251 Hz + .byte $73 ; C5# 554.365 Hz + +note_to_tone_hi: + +; NOTE: Intentional Data overlap/re-use +; .byte $00 ; C4 261.626 Hz +; .byte $00 ; C4# 277.183 Hz +; .byte $00 ; D4 293.665 Hz +; .byte $00 ; D4# 311.127 Hz +; .byte $00 ; E4 329.628 Hz +; .byte $00 ; F4 349.228 Hz +; .byte $00 ; F4# 369.994 Hz +; .byte $00 ; G4 391.995 Hz +; .byte $00 ; G4# 415.305 Hz +; .byte $00 ; A4 440.000 Hz +; .byte $00 ; A4# 466.164 Hz +; .byte $00 ; B4 493.883 Hz +; .byte $00 ; C5 523.251 Hz +; .byte $00 ; C5# 554.365 Hz + +mockN_off_data: + BRK ; Reg 0,1 Tone A + BRK + BRK ; Reg 2,3 Tone B + BRK + BRK ; Reg 4,5 Tone C + BRK + BRK ; Reg 6 Noise + BRK ; Reg 7 Enable + BRK ; Reg 8 Amp A + BRK ; Reg 9 Amp B + BRK ; Reg A Amp C + BRK ; Reg B Env Low + + BRK ; Reg C Env Hi + BRK ; Reg D Env Type + + + +; ____________________ Mockingboard 2 ____________________ + +; ==================== +; Initialize Mockingboard +; ==================== +MOCK2_Init: + lda #_6522_PORT_B_OUT + sta MOCK2_DIR_B +; lda #_6522_PORT_A_OUT + sta MOCK2_DIR_A + ; NOTE: Intentional fall-into reset! +MOCK2_Reset: + lda #_6522_RESET ; -->+ + .byte $2C ; | +MOCK2_SetReg: ; | + lda #_6522_LATCH ;-->+ | + .byte $2C ; | | +MOCK2_SetData: ; | | + lda #_6522_WRITE ; | | + sta MOCK2_OR_B ;<--+ <--+ + lda #_6522_PORT_B + sta MOCK2_OR_B + rts + +; ==================== +; Plays one note +; IN: +; X = Register: $0 .. $D +; A = Value +; OUT: +; A = trash +; X = unchanged +; Y = trash +; ==================== +MOCK2_PokeXA: + TAY + ; NOTE: Intentional fall-into! + +; ==================== +; Poke (set) byte to Mockingboard +; IN: +; X = Register: $0 .. $D +; Y = Value +; OUT: +; A = trash +; X = unchanged +; Y = unchanged +; ==================== +MOCK2_PokeXY: + stx MOCK2_OR_A + jsr MOCK2_SetReg + sty MOCK2_OR_A + jmp MOCK2_SetData + +; ==================== +; Stop all sounds by setting all regs to zero +; ==================== +MOCK2_Stop: + LDX #mockN_off_data + ; NOTE: Intentional fall-into! + +; ==================== +; Plays one note by setting all Mockingboard registers +; IN: +; X = Low Address of registers +; Y = High Address of +; ==================== +MOCK2_Play: + STX _mock2_load_reg+1 ; *** SELF-MODIFIES! + STY _mock2_load_reg+2 +_mock2_all_regs: + ldy #0 + ldx #MOCK_REG_FIRST +_mock2_load_reg: + lda mockN_off_data,X ; *** SELF-MODIFIED! + jsr MOCK2_PokeXA + inx + cpx #MOCK_REG_NUM ; BUG: $B ! + bcc _mock2_load_reg + rts + +__END: +