diff --git a/mocktest.xcodeproj/project.pbxproj b/mocktest.xcodeproj/project.pbxproj index b69fce8..863185a 100644 --- a/mocktest.xcodeproj/project.pbxproj +++ b/mocktest.xcodeproj/project.pbxproj @@ -7,10 +7,8 @@ objects = { /* Begin PBXFileReference section */ - 9D16BCE21D9E070B005EE214 /* speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = speech.s; sourceTree = ""; }; - 9D49929C1DB5C5F700606789 /* speech.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = speech.h; sourceTree = ""; }; - 9D49929D1DB5C65200606789 /* speech_api.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = speech_api.c; sourceTree = ""; }; - 9D49929E1DB5C65200606789 /* speech_api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = speech_api.h; sourceTree = ""; }; + 9D16BCE21D9E070B005EE214 /* mockingboard_speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = mockingboard_speech.s; sourceTree = ""; }; + 9D49929C1DB5C5F700606789 /* mockingboard_speech.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mockingboard_speech.h; sourceTree = ""; }; 9DB3C98C1D84A11600395532 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 9DB3C98D1D84A11600395532 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; 9DB3C98F1D84A11600395532 /* AppleCommander.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; name = AppleCommander.jar; path = make/AppleCommander.jar; sourceTree = ""; }; @@ -39,10 +37,8 @@ 9DB3C98C1D84A11600395532 /* main.c */, 9DB3C99C1D84A21E00395532 /* mockingboard.c */, 9DB3C99D1D84A21E00395532 /* mockingboard.h */, - 9D16BCE21D9E070B005EE214 /* speech.s */, - 9D49929C1DB5C5F700606789 /* speech.h */, - 9D49929D1DB5C65200606789 /* speech_api.c */, - 9D49929E1DB5C65200606789 /* speech_api.h */, + 9D16BCE21D9E070B005EE214 /* mockingboard_speech.s */, + 9D49929C1DB5C5F700606789 /* mockingboard_speech.h */, 9DB3C98D1D84A11600395532 /* Makefile */, 9DB3C98E1D84A11600395532 /* make */, ); diff --git a/mocktest/main.c b/mocktest/main.c index 648c640..6084090 100644 --- a/mocktest/main.c +++ b/mocktest/main.c @@ -14,7 +14,6 @@ #include #include "mockingboard.h" -#include "speech_api.h" tMockingSoundRegisters soundData1 = { @@ -80,7 +79,7 @@ void delay(void) int main(void) { - mockingBoardInit(4); + mockingBoardInit(4, false); printf("HELLO, WORLD!\n"); @@ -96,16 +95,19 @@ int main(void) cgetc(); printf("RUN SPEECH TEST (Y/N) "); - if (cgetc() != 'Y') - return 0; - printf("\n"); - - speakMessage(mySpeechData, sizeof(mySpeechData)); - while (speechIsBusy()) { + if (cgetc() == 'Y') { + printf("\n"); + + mockingBoardInit(4, true); + mockingBoardSpeak(mySpeechData, sizeof(mySpeechData)); + while (mockingBoardSpeechIsBusy()) { + } + + printf("\nDone speaking\n"); + cgetc(); } - printf("\nDone speaking\n"); - cgetc(); + mockingBoardShutdown(); return 0; } diff --git a/mocktest/mockingboard.c b/mocktest/mockingboard.c index 2ce483d..a485316 100644 --- a/mocktest/mockingboard.c +++ b/mocktest/mockingboard.c @@ -12,6 +12,7 @@ #include #include "mockingboard.h" +#include "mockingboard_speech.h" // Defines @@ -25,20 +26,21 @@ // Globals // Addresses for the two 6522's (assuming slot 4 for now) -static uint8_t *gMockPortB[NUM_SOUND_CHIPS] = { (uint8_t *)0xc400, (uint8_t *)0xc480 }; -static uint8_t *gMockPortA[NUM_SOUND_CHIPS] = { (uint8_t *)0xc401, (uint8_t *)0xc481 }; -static uint8_t *gMockDataDirB[NUM_SOUND_CHIPS] = { (uint8_t *)0xc402, (uint8_t *)0xc482 }; -static uint8_t *gMockDataDirA[NUM_SOUND_CHIPS] = { (uint8_t *)0xc403, (uint8_t *)0xc483 }; +static uint8_t *gMockPortB[NUM_SOUND_CHIPS] = { (uint8_t *)0xc000, (uint8_t *)0xc080 }; +static uint8_t *gMockPortA[NUM_SOUND_CHIPS] = { (uint8_t *)0xc001, (uint8_t *)0xc081 }; +static uint8_t *gMockDataDirB[NUM_SOUND_CHIPS] = { (uint8_t *)0xc002, (uint8_t *)0xc082 }; +static uint8_t *gMockDataDirA[NUM_SOUND_CHIPS] = { (uint8_t *)0xc003, (uint8_t *)0xc083 }; static uint8_t gMockingBoardInitialized = false; +static uint8_t gMockingBoardSpeechInitialized = false; -static uint8_t *mapIOPointer(uint8_t slot, uint8_t *ptr) +static uint8_t *mapIOPointer(tSlot slot, uint8_t *ptr) { uint16_t temp1 = (uint16_t)ptr; uint16_t temp2 = slot; - temp2 << 8; + temp2 <<= 8; temp1 &= 0xf0ff; temp1 |= temp2; @@ -46,7 +48,8 @@ static uint8_t *mapIOPointer(uint8_t slot, uint8_t *ptr) return ptr; } -void mockingBoardInit(tSlot slot) + +void mockingBoardInit(tSlot slot, bool hasSpeechChip) { tSoundChip soundChip; @@ -55,21 +58,41 @@ void mockingBoardInit(tSlot slot) } for (soundChip = SOUND_CHIP_1; soundChip < NUM_SOUND_CHIPS; soundChip++) { -#if 0 gMockPortB[soundChip] = mapIOPointer(slot, gMockPortB[soundChip]); gMockPortA[soundChip] = mapIOPointer(slot, gMockPortA[soundChip]); gMockDataDirB[soundChip] = mapIOPointer(slot, gMockDataDirB[soundChip]); gMockDataDirA[soundChip] = mapIOPointer(slot, gMockDataDirA[soundChip]); -#endif *(gMockDataDirA[soundChip]) = 0xff; // Set port A for output *(gMockDataDirB[soundChip]) = 0x7; // Set port B for output } + if (hasSpeechChip) { + if (gMockingBoardSpeechInitialized) { + mockingBoardSpeechShutdown(); + } + mockingBoardSpeechInit(slot); + gMockingBoardSpeechInitialized = true; + } else if (gMockingBoardSpeechInitialized) { + mockingBoardSpeechShutdown(); + gMockingBoardSpeechInitialized = false; + } + gMockingBoardInitialized = true; } +void mockingBoardShutdown(void) +{ + if (gMockingBoardSpeechInitialized) { + mockingBoardSpeechShutdown(); + gMockingBoardSpeechInitialized = false; + } + + gMockingBoardInitialized = false; +} + + static void writeCommand(tSoundChip soundChip, uint8_t command) { volatile uint8_t *ptr = gMockPortB[soundChip]; @@ -103,6 +126,9 @@ void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *regis volatile uint8_t *ptr = gMockPortA[soundChip]; uint8_t index; + if (!gMockingBoardInitialized) + return; + mockingBoardReset(soundChip); for (index = 0; index < 16; index++) { *ptr = index; @@ -112,3 +138,31 @@ void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *regis data++; } } + + +bool mockingBoardSpeechIsBusy(void) +{ + return (mockingBoardSpeechBusy != 0); +} + + +bool mockingBoardSpeechIsPlaying(void) +{ + return (mockingBoardSpeechPlaying != 0); +} + + +bool mockingBoardSpeak(uint8_t *data, uint16_t dataLen) +{ + if (!gMockingBoardSpeechInitialized) + return false; + + if (mockingBoardSpeechIsBusy()) + return false; + + mockingBoardSpeechData = data; + mockingBoardSpeechLen = dataLen + 1; + mockingBoardSpeakPriv(); + + return true; +} diff --git a/mocktest/mockingboard.h b/mocktest/mockingboard.h index 6ec6bcd..dc31b77 100644 --- a/mocktest/mockingboard.h +++ b/mocktest/mockingboard.h @@ -10,6 +10,7 @@ #define __mocktest__mockingboard__ +#include #include @@ -111,13 +112,18 @@ typedef struct tMockingSoundRegisters { // API -void mockingBoardInit(tSlot slot); +extern void mockingBoardInit(tSlot slot, bool hasSpeechChip); +extern void mockingBoardShutdown(void); -void mockingBoardLatch(tSoundChip soundChip); -void mockingBoardWrite(tSoundChip soundChip); -void mockingBoardReset(tSoundChip soundChip); +extern void mockingBoardLatch(tSoundChip soundChip); +extern void mockingBoardWrite(tSoundChip soundChip); +extern void mockingBoardReset(tSoundChip soundChip); -void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *registers); +extern void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *registers); + +extern bool mockingBoardSpeechIsBusy(void); +extern bool mockingBoardSpeechIsPlaying(void); +extern bool mockingBoardSpeak(uint8_t *data, uint16_t dataLen); #endif /* defined(__mocktest__mockingboard__) */ diff --git a/mocktest/mockingboard_speech.h b/mocktest/mockingboard_speech.h new file mode 100644 index 0000000..3228735 --- /dev/null +++ b/mocktest/mockingboard_speech.h @@ -0,0 +1,26 @@ +// +// mockingboard_speech.h +// mocktest +// +// Created by Jeremy Rand on 2016-10-17. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + +#ifndef mockingboard_speech_h +#define mockingboard_speech_h + + +#include + + +extern uint8_t *mockingBoardSpeechData; +extern uint16_t mockingBoardSpeechLen; +extern uint8_t mockingBoardSpeechBusy; +extern uint8_t mockingBoardSpeechPlaying; + +extern void mockingBoardSpeechInit(uint8_t slot); +extern void mockingBoardSpeechShutdown(void); +extern void mockingBoardSpeakPriv(void); + + +#endif /* mockingboard_speech_h */ diff --git a/mocktest/mockingboard_speech.s b/mocktest/mockingboard_speech.s new file mode 100644 index 0000000..af7e778 --- /dev/null +++ b/mocktest/mockingboard_speech.s @@ -0,0 +1,288 @@ +; +; 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 + + +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 +_irqLandingPad: .byte $4C, $00, $00 + + +.CODE + +writeChip: + sta $C000,X + rts + +readChip: + lda $C000,X + rts + + +.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 + +; Save the old interrupt vector + lda IRQL + sta _irqLandingPad+1 + lda IRQH + sta _irqLandingPad+2 + +; Set the interrupt vector to point to the interrupt service +; routine. + lda #<_interr + sta IRQL + lda #>_interr + sta IRQH + cli + rts +.endproc + + +.proc _mockingBoardSpeechShutdown + sei + +; Restore the old interrupt vector + lda _irqLandingPad+1 + sta IRQL + lda _irqLandingPad+2 + sta IRQH + cli + rts +.endproc + + +.proc _mockingBoardSpeakPriv + sei + lda #$00 + ldx #DDRA + jsr writeChip + ldx #DDRB + jsr writeChip + +; 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 _interr +; Save the accumulator and X registers + pha + txa + pha + +; If we have a 6522 interrupt, jump to L4. + ldx #IFR + jsr readChip + bmi @L4 + +; Otherwise restore the accumulator and X registers and jump +; to the old interrupt handler. + pla + tax + pla + jmp _irqLandingPad + +@L4: +; Save the Y register also + tya + pha + +; 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 #$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 + lda #$FF + ldx #DDRA + jsr writeChip + lda #$07 + ldx #DDRB + jsr writeChip + +@L2: +; Restore registers + pla + tay + pla + tax + pla + +; Return from interrupt + rti + +@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 + diff --git a/mocktest/speech.h b/mocktest/speech.h deleted file mode 100644 index d9d1263..0000000 --- a/mocktest/speech.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// speech.h -// mocktest -// -// Created by Jeremy Rand on 2016-10-17. -// Copyright © 2016 Jeremy Rand. All rights reserved. -// - -#ifndef speech_h -#define speech_h - - -extern uint8_t *speechData; -extern uint16_t speechLen; -extern uint8_t speechBusy; - -extern void setupSpeech(void); -extern void unsetupSpeech(void); - - -#endif /* speech_h */ diff --git a/mocktest/speech.s b/mocktest/speech.s deleted file mode 100644 index 341ad82..0000000 --- a/mocktest/speech.s +++ /dev/null @@ -1,175 +0,0 @@ -; -; speech.s -; mocktest -; -; Created by Jeremy Rand on 2016-09-29. -; Copyright © 2016 Jeremy Rand. All rights reserved. -; - - - .export _setupSpeech, _unsetupSpeech - .export _speechData, _speechLen, _speechBusy - - -TMPPTR := $FB ; Temporary pointer used in interrupt handler -IRQL := $03FE ; Interrupt vector, low byte -IRQH := $03FF ; Interrupt vector, high byte -BASE := $C440 ; 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 := $C402 -DDRA := $C403 -PCR := $C48C ; Peripheral control register, 6522 -IFR := $C48D ; Interrupt flag register, 6522 -IER := $C48E - - -.DATA -_speechData: .byte $00, $00 -_speechLen: .byte $00, $00 -_outptr: .byte $00, $00 -_endptr: .byte $00, $00 -_speechBusy: .byte $00 -_irqLandingPad: .byte $4C, $00, $00 - - -.CODE - -.proc _setupSpeech - sei ; Disable interrupts - - lda IRQL ; Store the old interrupt vector - sta _irqLandingPad+1 - lda IRQH - sta _irqLandingPad+2 - - lda #<_interr ; Set interrupt vector - sta IRQL ; to point to interrupt - lda #>_interr ; service routine - sta IRQH - lda #$00 - sta DDRA - sta DDRB - - lda _speechData+1 ; Get high address of data - sta _outptr+1 ; Store in work pointer - lda _speechData ; Get low address of data - sta _outptr ; Store low byte - - lda _speechLen+1 ; Get high length byte - clc - adc _speechData+1 ; And add to base address - sta _endptr+1 ; Store end address - lda _speechLen ; Get low length byte - clc - adc _speechData ; And add to base address - bcc @L2 ; Check for page boundary - inc _endptr+1 -@L2: - sta _endptr ; Store end address - - lda #$FF ; Set busy flag - sta _speechBusy ; And set peripheral control - lda #$0C ; Register to recognize - sta PCR ; Signal from speech chip - lda #$80 ; Raise control bit in register 3 - sta CTTRAMP - lda #$C0 ; Set transitioned inflection - sta DURPHON ; Mode in register 0 - lda #$70 ; Lower control bit - sta CTTRAMP - lda #$82 ; Enable 6522 interrupts - sta IER - cli ; Clear interrupt mask - rts ; Return to caller -.endproc - - -.proc _unsetupSpeech - sei ; Restore the old interrupt vector - lda _irqLandingPad+1 - sta IRQL - lda _irqLandingPad+2 - sta IRQH - cli - rts -.endproc - - -.proc _interr - pha ; Save accumulator - lda IFR - bmi @L4 ; If we have 6522 interrupt jump to L4 - pla ; Otherwise restore the accumulator - jmp _irqLandingPad ; And jump to the old interrupt handler -@L4: - txa ; Save other registers - pha - tya - pha - lda #$02 ; Clear interrupt flag - sta IFR - ldy #$00 ; Init registers - ldx #$04 - lda _outptr+1 - cmp _endptr+1 - bcc @L1 - bne @L5 - lda _outptr ; Check for end of data file - cmp _endptr - bcc @L1 ; If not, then continue -@L5: - lda #$00 ; If end, turn everything off - sta DURPHON ; Store pause phoneme - lda #$70 ; Zero amplitude - sta CTTRAMP - lda #$00 ; Clear busy flag - sta _speechBusy - lda #$02 ; Clear interrupt enable - sta IER ; In 6522 - lda #$FF - sta DDRA - lda #$07 - sta DDRB -@L2: - pla ; Restore registers - tay - pla - tax - pla - rti ; Return from interrupt - -@L1: - lda TMPPTR ; Save the value in the tmp pointer - pha - lda TMPPTR+1 - pha - lda _outptr ; Move the _outptr into the tmp pointer - sta TMPPTR - lda _outptr+1 - sta TMPPTR+1 -@L6: - lda (TMPPTR),Y ; Get data - sta BASE,X ; Store in speech chip - inc TMPPTR ; Next data - bne @L3 - inc TMPPTR+1 - -@L3: - dex ; Next register - cpx #$FF ; Last register? - bne @L6 ; No, then continue - lda TMPPTR ; We are done, move tmp pointer to _outptr - sta _outptr - lda TMPPTR+1 - sta _outptr+1 - pla ; Restore the tmp pointer - sta TMPPTR+1 - pla - sta TMPPTR - jmp @L2 ; Finish the interrupt handler -.endproc - diff --git a/mocktest/speech_api.c b/mocktest/speech_api.c deleted file mode 100644 index bdcb0d4..0000000 --- a/mocktest/speech_api.c +++ /dev/null @@ -1,32 +0,0 @@ -// -// speech_api.c -// mocktest -// -// Created by Jeremy Rand on 2016-10-17. -// Copyright © 2016 Jeremy Rand. All rights reserved. -// - - -#include - -#include "speech_api.h" -#include "speech.h" - - -bool speechIsBusy(void) -{ - return (speechBusy != 0); -} - - -bool speakMessage(uint8_t *data, uint16_t dataLen) -{ - if (speechIsBusy()) - return false; - - speechData = data; - speechLen = dataLen + 1; - setupSpeech(); - - return true; -} diff --git a/mocktest/speech_api.h b/mocktest/speech_api.h deleted file mode 100644 index e4e72a5..0000000 --- a/mocktest/speech_api.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// speech_api.h -// mocktest -// -// Created by Jeremy Rand on 2016-10-17. -// Copyright © 2016 Jeremy Rand. All rights reserved. -// - -#ifndef __mocktest__speech_api__ -#define __mocktest__speech_api__ - - -#include -#include - - -extern bool speechIsBusy(void); -extern bool speakMessage(uint8_t *data, uint16_t dataLen); - - - -#endif /* defined(__mocktest__speech_api__) */