a2bejwld/a2bejwld/mockingboard_speech.s

473 lines
10 KiB
ArmAsm

;
; speech.s
; mocktest
;
; Created by Jeremy Rand on 2016-09-29.
; Copyright © 2016 Jeremy Rand. All rights reserved.
;
.export _mockingBoardSpeechInit, _mockingBoardSpeechShutdown, _mockingBoardSpeakPriv
.export _mockingBoardSpeechData, _mockingBoardSpeechLen
.export _mockingBoardSpeechBusy, _mockingBoardSpeechPlaying
.export _getMockingBoardSlot
.interruptor mock_irq
TMPPTR := $FB ; Temporary pointer used in interrupt handler
IRQL := $03FE ; Interrupt vector, low byte
IRQH := $03FF ; Interrupt vector, high byte
BASE := $40 ; First speech chip
DURPHON := BASE ; Register 0 of speech chip
INFLECT := BASE+$01 ; Register 1 of speech chip
RATEINF := BASE+$02 ; Register 2 of speech chip
CTTRAMP := BASE+$03 ; Register 3 of speech chip
FILFREQ := BASE+$04 ; Register 4 of speech chip
DDRB := $02
DDRA := $03
PCR := $8C ; Peripheral control register, 6522
IFR := $8D ; Interrupt flag register, 6522
IER := $8E
.DATA
_mockingBoardSpeechData: .byte $00, $00
_mockingBoardSpeechLen: .byte $00, $00
_outptr: .byte $00, $00
_endptr: .byte $00, $00
_mockingBoardSpeechBusy: .byte $00
_mockingBoardSpeechPlaying: .byte $00
mock_irq: .byte $60
.lobytes _mockInterrupt
.hibytes _mockInterrupt
.CODE
writeChip:
sta $C000,X
rts
readChip:
lda $C000,X
rts
;license:MIT
; By Andrew Roughan
; in the style of 4am for Total Replay
; ported into a2bejewld by jrand
;
; Mockingboard support functions
;
;------------------------------------------------------------------------------
; GetMockingboardSlot
; detect Mockingboard card by searching for 6522 timers across all slots 7->1
; access 6522 timers with deterministic cycle counts
;
; based on prior art in Mockingboard Developers Toolkit
; with optimisation from deater/french touch
; also takes into account FastChip //e clock difference
;
; in: none
; accelerators should be off
; out:
; if card was found, A = #$?n where n is the slot number of the card, otherwise #$00
; and bit 6 = 0 if Mockingboard Sound I found
; or bit 6 = 1 if Mockingboard Sound II or "A" found
; and bit 7 = 1 if Mockingboard Sound/Speech I or "C" found
; flags clobbered
; zp $80-$82 clobbered
; (jrand - and then restored because I am chicken and afraid of breaking cc65 runtime)
; X/Y clobbered
;------------------------------------------------------------------------------
.proc _getMockingBoardSlot
php
lda $80
sta zp80Backup
lda $81
sta zp81Backup
lda $82
sta zp82Backup
lda $3fe
sta irq1Backup
lda $3ff
sta irq2Backup
lda #$00
sta $80
sta $82 ; type
ldx #$C7
@slotLoop:
stx $81
ldy #$04 ; 6522 #1 $Cx04
jsr @timercheck
beq @foundI
dex
cpx #$C0
bne @slotLoop
ldx #00 ; not found
jmp @cleanup
@foundI: ; sound I or better
ldy #$84 ; 6522 #2 $Cx84
jsr @timercheck
beq @foundII
ldy #$0C
sty @mb_smc1 + 1
iny
sty @mb_smc10 + 1
iny
sty @mb_smc5 + 1
sty @mb_smc14 + 1
.BYTE $2C ; Hide next 2 bytes using a BIT opcode
@foundII: ; stereo
ror $82
lda $81
sta @mb_smc1 + 2
sta @mb_smc2 + 2
sta @mb_smc3 + 2
sta @mb_smc4 + 2
sta @mb_smc5 + 2
sta @mb_smc6 + 2
sta @mb_smc7 + 2
sta @mb_smc8 + 2
sta @mb_smc9 + 2
sta @mb_smc10 + 2
sta @mb_smc11 + 2
sta @mb_smc12 + 2
sta @mb_smc13 + 2
sta @mb_smc14 + 2
; detect speech chip
sei
lda #<@mb_irq
sta $3fe
lda #>@mb_irq
sta $3ff
lda #$0c
@mb_smc1:
sta $c48c
lda #$80
@mb_smc2:
sta $c443
lda #$c0
@mb_smc3:
sta $c440
lda #$70
@mb_smc4:
sta $c443
lda #$82
@mb_smc5:
sta $c48e
ldx #0
ldy #0
sec
cli
@wait_irq:
lda $80
bne @got_irq
iny
bne @wait_irq
inx
bne @wait_irq
clc
@got_irq:
sei
ror $82
ldy #$ff
@mb_smc6:
sty $c403
lda #$7
@mb_smc7:
sta $c402
@mb_smc8:
sty $c483
@mb_smc9:
sta $c482
and $81
ora $82
tax
iny
sty $80
tya
sta ($80),y
lda #4
sta ($80),y
tya
ldy #$80
sta ($80),y
lda #4
sta ($80),y
@cleanup:
lda zp80Backup
sta $80
lda zp81Backup
sta $81
lda zp82Backup
sta $82
lda irq1Backup
sta $3fe
lda irq2Backup
sta $3ff
txa
ldx #$00
plp
rts
@timercheck:
sec
lda ($80),y ; read 6522 timer low byte
sbc ($80),y ; second time
cmp #5 ; looking for (-)8 cycles between reads
beq :+
cmp #6 ; FastChip //e clock is different
: rts
@mb_irq:
lda #2
@mb_smc10:
sta $c48d
lda #$80
@mb_smc11:
sta $c443
lda #0
@mb_smc12:
sta $c440
lda #$70
@mb_smc13:
sta $c443
sta $80
lda #2
@mb_smc14:
sta $c48e
lda $45
rti
; Locals
zp80Backup: .BYTE $00
zp81Backup: .BYTE $00
zp82Backup: .BYTE $00
irq1Backup: .BYTE $00
irq2Backup: .BYTE $00
.endproc
.proc _mockingBoardSpeechInit
sei
; The accumulator has the slot number of the mockingboard.
; Turn that into the address of the slot and set the address
; in the read and write functions.
and #$7
ora #$c0
sta writeChip+2
sta readChip+2
; Write a jump instruction at mock_irq to turn on our handler
lda #$4c
sta mock_irq
cli
rts
.endproc
.proc _mockingBoardSpeechShutdown
sei
; Write a RTS instruction at mock_irq to disable our handler
lda #$60
sta mock_irq
cli
rts
.endproc
.proc _mockingBoardSpeakPriv
sei
; Get the starting address of the data and store in the work pointer
lda _mockingBoardSpeechData+1
sta _outptr+1
lda _mockingBoardSpeechData
sta _outptr
; Calculate the end address from the start address and the length
lda _mockingBoardSpeechLen+1
clc
adc _mockingBoardSpeechData+1
sta _endptr+1
lda _mockingBoardSpeechLen
clc
adc _mockingBoardSpeechData
bcc @L2
inc _endptr+1
@L2:
sta _endptr
; Set the busy flag
lda #$FF
sta _mockingBoardSpeechBusy
; Set peripheral control register to recognize the signal from the
; speech chip.
lda #$0C
ldx #PCR
jsr writeChip
; Raise control bit in register 3
lda #$80
ldx #CTTRAMP
jsr writeChip
; Set transitioned inflection mode in register 0
lda #$C0
ldx #DURPHON
jsr writeChip
; Lower control bit
lda #$70
ldx #CTTRAMP
jsr writeChip
; Enable 6522 interrupts
lda #$82
ldx #IER
jsr writeChip
cli
rts
.endproc
.proc _mockInterrupt
; If we have a 6522 interrupt, jump to L4.
ldx #IFR
jsr readChip
bmi @L4
; Otherwise clear the carry to indicate we didn't handle the interrupt
; and return to the caller.
clc
rts
@L4:
; Clear the interrupt flag
lda #$02
ldx #IFR
jsr writeChip
; Check for end of data file. If not the end, jump to L1
lda _outptr+1
cmp _endptr+1
bcc @L1
bne @L5
lda _outptr
cmp _endptr
bcc @L1
@L5:
; If at the end, turn everything off. Store a pause phoneme.
lda #$80
ldx #CTTRAMP
jsr writeChip
lda #$00
ldx #DURPHON
jsr writeChip
; Zero amplitude
lda #$70
ldx #CTTRAMP
jsr writeChip
; Clear busy and playing flags
lda #$00
sta _mockingBoardSpeechBusy
sta _mockingBoardSpeechPlaying
; Clear interrupt enable in 6522
lda #$02
ldx #IER
jsr writeChip
@L2:
; Set the carry flag to indicate we handled the interrupt and return to the caller.
sec
rts
@L1:
; Set the speach playing flag
lda #$ff
sta _mockingBoardSpeechPlaying
; Save the value of the tmp pointer on the stack
lda TMPPTR
pha
lda TMPPTR+1
pha
; Move the _outptr into the tmp pointer
lda _outptr
sta TMPPTR
lda _outptr+1
sta TMPPTR+1
; Init registers
ldy #$00
ldx #FILFREQ
@L6:
; Get the next data
lda (TMPPTR),Y
; Store in the speech chip
jsr writeChip
; Next data
inc TMPPTR
bne @L3
inc TMPPTR+1
@L3:
; Go to next register
dex
; If we are not done the last register, then loop back to L6
cpx #BASE-1
bne @L6
; We are done writing so move the tmp pointer back into _outptr
lda TMPPTR
sta _outptr
lda TMPPTR+1
sta _outptr+1
; Restore the tmp pointer from the stack
pla
sta TMPPTR+1
pla
sta TMPPTR
; Finish the interrupt handler
jmp @L2
.endproc