; ; sound ; Code and data structures for playing sound effects ; ; Created by Quinn Dunki on 6/30/23 ; NUM_SOUNDS = 5 SOUND_HOWL=0 SOUND_MEOW1=2 SOUND_MEOW2=4 SOUND_MEOW3=6 SOUND_MEOW4=8 soundTable: ; Sound Ram Address, Wave Size, Low Frequency .byte $00,WAVE_SIZE_8192,200 ; SOUND_HOWL .byte $20,WAVE_SIZE_8192,150 ; SOUND_MEOW1 .byte $40,WAVE_SIZE_8192,200 ; SOUND_MEOW2 .byte $60,WAVE_SIZE_8192,150 ; SOUND_MEOW3 .byte $80,WAVE_SIZE_8192,150 ; SOUND_MEOW4 ; Ensoniq Control Register bit patterns DOC_RAMACCESS = $40 ; or DOC_DOCACCESS = $bf ; and DOC_AUTOINC = $20 ; or DOC_NOINC = $df ; and ; Oscillator Control Register bit patterns OSC_MODE_FREE = $00 OSC_MODE_ONESHOT = $02 OSC_MODE_SYNC = $04 OSC_MODE_SWAP = $06 OSC_CHANNEL_ALL = $70 OSC_CHANNEL_RIGHT = $00 OSC_CHANNEL_LEFT = $10 ; Internal oscillator-related registers (additional per-oscillator registers per the big table on page 110 of Apple IIGS Hardware Reference Manual) OSC_REG_INTERRUPT = $e0 OSC_REG_ENABLE = $e1 OSC_REG_DIGITIZE = $e2 ; Wave table page size bit patterns WAVE_SIZE_256 = $07 WAVE_SIZE_512 = $0f WAVE_SIZE_1024 = $17 WAVE_SIZE_2048 = $1f WAVE_SIZE_4096 = $27 WAVE_SIZE_8192 = $2f WAVE_SIZE_16384 = $37 WAVE_SIZE_32768 = $3f ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ENSONIQ_WAIT ; ; Polls the high bit of the Sound Control Register, which tells us when we're allowed ; to talk to the DOC ; .macro ENSONIQ_WAIT .local busyLoop BITS8A busyLoop: lda ENSONIQ_CONTROL bmi busyLoop BITS16 .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; writeInternalRegister - Call in 8 BIT MODE ONLY (also why it's a macro) ; ; Writes to the internal registers of the Ensoniq ; ; A = Data to write (8 bits) ; X = Register number to write to (8 bits) ; ; Trashes A ; .macro writeInternalRegister pha txa sta ENSONIQ_ADDRL pla sta ENSONIQ_DATA .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; writeInternalRegisterPair - Call in 8 BIT MODE ONLY (also why it's a macro) ; ; Writes to the internal registers of the Ensoniq for a pair of oscillators ; ; A = Data to write (8 bits) ; X = Register number to write to (8 bits). X+1 will also be written ; ; Trashes A ; .macro writeInternalRegisterPair pha txa sta ENSONIQ_ADDRL pla sta ENSONIQ_DATA pha inx txa sta ENSONIQ_ADDRL pla sta ENSONIQ_DATA .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initSoundSystem ; ; Trashes SCRATCHL ; initSoundSystem: SAVE_AXY ENSONIQ_WAIT ; Load sound file bank into Ensoniq RAM lda #$0000 ; Sound location in bank 4 sta PARAML0 ldx #$04 lda soundBankSize ; Far pointer tay jsr copyToSoundRAM ; Configure all our oscillators lda #0 sta SCRATCHL ldy #0 initSoundSystemLoop: BITS8A lda soundTable,y sta PARAM0 iny lda soundTable,y sta PARAM1 iny lda soundTable,y sta PARAM2 iny BITS16 phy ldy SCRATCHL jsr configureOscillatorPair iny iny cpy #NUM_SOUNDS*2 beq initSoundSystemReady sty SCRATCHL ply bra initSoundSystemLoop initSoundSystemReady: ply ; Enable oscillators (see page 108 of Apple IIGS Hardware Reference Manual) BITS8 lda #(NUM_SOUNDS*2)<<1 ; Two oscillators per sound and leaving low bit untouched ldx #OSC_REG_ENABLE writeInternalRegister BITS16 RESTORE_AXY rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; playSound ; ; Y = Oscillator to start (+1 also starts) ; playSound: SAVE_AX ENSONIQ_WAIT BITS8 ; Configure run mode for our oscillators lda #OSC_CHANNEL_RIGHT | OSC_MODE_ONESHOT ldx docRegisterTableControl,y writeInternalRegister lda #OSC_CHANNEL_LEFT | OSC_MODE_ONESHOT ldx docRegisterTableControl+1,y writeInternalRegister BITS16 RESTORE_AX rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; configureOscillatorPair ; ; Y = Oscillator number (+1 will also be configured) ; PARAM0 = Wave table address (one byte) ; PARAM1 = Wave table page size (must use WAVE_SIZE constants) ; PARAM2 = Low frequency (high frequency is always 0) ; configureOscillatorPair: SAVE_AXY ENSONIQ_WAIT BITS8 ; Configure low frequency lda PARAM2 ldx docRegisterTableLowFreq,y writeInternalRegisterPair ; Configure high frequency lda #0 ldx docRegisterTableHighFreq,y writeInternalRegisterPair ; Configure volume lda #$ff ; This is relative to system volume, so set it to max ldx docRegisterTableVolume,y writeInternalRegisterPair ; Configure sound RAM pointer lda PARAM0 ldx docRegisterTableWavePointer,y writeInternalRegisterPair ; Configure sound RAM size ; Format of this register is tricky. See page 114 of Apple IIGS Hardware Reference Manual lda PARAM1 ldx docRegisterTableWaveSize,y writeInternalRegisterPair BITS16 RESTORE_AXY rts ; DOC register locations for each oscillator docRegisterTableLowFreq: .byte $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f docRegisterTableHighFreq: .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f docRegisterTableVolume: .byte $40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$5b,$5c,$5d,$5e,$5f docRegisterTableData: .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f,$70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f docRegisterTableWavePointer: .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91,$92,$93,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f docRegisterTableControl: .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af,$b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf docRegisterTableWaveSize: .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf,$d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; copyToSoundRAM ; ; Copies data from main RAM to sound RAM ; ; PARAML0 = Pointer to sound data in main RAM ; X = Bank number of sound data in main RAM ; Y = Size of sound bank in bytes ; ; Trashes SCRATCHL ; copyToSoundRAM: SAVE_AXY sty SCRATCHL ENSONIQ_WAIT lda #0 BITS8 stx copyToSoundRAMLoop+3 ; Self modifying code. Don't panic ; Enable sound RAM access and auto-increment mode lda ENSONIQ_CONTROL ora #DOC_RAMACCESS | DOC_AUTOINC sta ENSONIQ_CONTROL BITS16 ; Prepare start address in sound RAM lda #$0000 sta ENSONIQ_ADDRL ; Copy all the data lda #0 BITS8A ldx #0 copyToSoundRAMLoop: lda $ff0000,x ; Note: 8 bit accumulator! DOC takes one byte at a time sta ENSONIQ_DATA ; Do not use indexed addressing here- see page 107 of Apple IIGS Hardware Reference Manual inx cpx SCRATCHL bne copyToSoundRAMLoop ; Enable DOC access and disable auto-increment mode ; This is a nicer mode to leave the Ensoniq in for normal usage once loading is complete lda ENSONIQ_CONTROL and #DOC_DOCACCESS & DOC_NOINC sta ENSONIQ_CONTROL BITS16 RESTORE_AXY rts