Lots more cleanup. Make everything slot independent including the speech routines. Adopt the mockingBoard prefix for everything. Put the speech APIs right into mockingboard.h.

This commit is contained in:
Jeremy Rand 2016-12-16 00:00:33 -05:00
parent 80cabff983
commit cbb37cae43
10 changed files with 404 additions and 282 deletions

View File

@ -7,10 +7,8 @@
objects = {
/* Begin PBXFileReference section */
9D16BCE21D9E070B005EE214 /* speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = speech.s; sourceTree = "<group>"; };
9D49929C1DB5C5F700606789 /* speech.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = speech.h; sourceTree = "<group>"; };
9D49929D1DB5C65200606789 /* speech_api.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = speech_api.c; sourceTree = "<group>"; };
9D49929E1DB5C65200606789 /* speech_api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = speech_api.h; sourceTree = "<group>"; };
9D16BCE21D9E070B005EE214 /* mockingboard_speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = mockingboard_speech.s; sourceTree = "<group>"; };
9D49929C1DB5C5F700606789 /* mockingboard_speech.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mockingboard_speech.h; sourceTree = "<group>"; };
9DB3C98C1D84A11600395532 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
9DB3C98D1D84A11600395532 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
9DB3C98F1D84A11600395532 /* AppleCommander.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; name = AppleCommander.jar; path = make/AppleCommander.jar; sourceTree = "<group>"; };
@ -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 */,
);

View File

@ -14,7 +14,6 @@
#include <conio.h>
#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;
}

View File

@ -12,6 +12,7 @@
#include <stdio.h>
#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;
}

View File

@ -10,6 +10,7 @@
#define __mocktest__mockingboard__
#include <stdbool.h>
#include <stdint.h>
@ -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__) */

View File

@ -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 <stdint.h>
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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -1,32 +0,0 @@
//
// speech_api.c
// mocktest
//
// Created by Jeremy Rand on 2016-10-17.
// Copyright © 2016 Jeremy Rand. All rights reserved.
//
#include <stdio.h>
#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;
}

View File

@ -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 <stdbool.h>
#include <stdint.h>
extern bool speechIsBusy(void);
extern bool speakMessage(uint8_t *data, uint16_t dataLen);
#endif /* defined(__mocktest__speech_api__) */