apple2_mockingboard/scales.s

430 lines
12 KiB
ArmAsm

; 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
ldy #>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 ; *** SELF-MODIFIES!
LDY #>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 $9A ; 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
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 ; *** SELF-MODIFIES!
LDY #>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: