mirror of
https://github.com/jeremysrand/a2bejwld.git
synced 2024-06-26 14:29:32 +00:00
This means there are three booleans in the options related to sound now. The first one enables/disables sound entirely and is default on. The second is only relevant if the sound is enabled and says "use a mockingboard if present". Again this is true by default. Finally, the this says "use a speech chip on the mockingboard if present" and is only relevant if the mockingboard is also enabled. So, the basic approach now is to default the "best" sound options and auto-detect the sound HW at launch. The user can then use the options to downgrade their sound all the way to basic Apple // sound or turn off the sound entirely.
224 lines
5.3 KiB
C
224 lines
5.3 KiB
C
//
|
|
// mockingboard.c
|
|
// mocktest
|
|
//
|
|
// Created by Jeremy Rand on 2016-09-10.
|
|
// Copyright © 2016 Jeremy Rand. All rights reserved.
|
|
//
|
|
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "mockingboard.h"
|
|
#include "mockingboard_speech.h"
|
|
|
|
|
|
// Defines
|
|
|
|
#define LATCH_COMMAND 0x7
|
|
#define WRITE_COMMAND 0x6
|
|
#define RESET_COMMAND 0x0
|
|
#define THROUGH_PORT_B 0x4
|
|
|
|
#define MOCKINGBOARD_LATCH(soundChip) writeCommand((soundChip), LATCH_COMMAND)
|
|
#define MOCKINGBOARD_WRITE(soundChip) writeCommand((soundChip), WRITE_COMMAND)
|
|
#define MOCKINGBOARD_RESET(soundChip) writeCommand((soundChip), RESET_COMMAND)
|
|
|
|
|
|
// Typedefs
|
|
|
|
typedef enum {
|
|
SOUND_CHIP_1 = 0,
|
|
SOUND_CHIP_2 = 1,
|
|
NUM_SOUND_CHIPS = 2
|
|
} tMockingBoardSoundChip;
|
|
|
|
|
|
// Globals
|
|
|
|
// Addresses for the two 6522's (assuming slot 4 for now)
|
|
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 gMockingBoardSearchDone = false;
|
|
static tSlot gMockingBoardSlot = 0;
|
|
static bool gMockingBoardHasSpeech = false;
|
|
|
|
|
|
// Implementation
|
|
|
|
static uint8_t *mapIOPointer(tSlot slot, uint8_t *ptr)
|
|
{
|
|
uint16_t temp1 = (uint16_t)ptr;
|
|
uint16_t temp2 = slot;
|
|
|
|
temp2 <<= 8;
|
|
temp1 &= 0xf0ff;
|
|
|
|
temp1 |= temp2;
|
|
ptr = (uint8_t *)temp1;
|
|
return ptr;
|
|
}
|
|
|
|
|
|
bool mockingBoardInit(void)
|
|
{
|
|
tMockingBoardSoundChip soundChip;
|
|
|
|
if (!gMockingBoardSearchDone)
|
|
{
|
|
gMockingBoardSlot = getMockingBoardSlot();
|
|
|
|
if ((gMockingBoardSlot & 0x80) != 0)
|
|
gMockingBoardHasSpeech = true;
|
|
|
|
gMockingBoardSlot &= 0x7;
|
|
|
|
gMockingBoardSearchDone = true;
|
|
}
|
|
|
|
if (gMockingBoardSlot == 0)
|
|
return false;
|
|
|
|
if (gMockingBoardInitialized)
|
|
return true;
|
|
|
|
if (sizeof(tMockingSoundRegisters) != 16) {
|
|
printf("The sound registers must be 16 bytes long!\n");
|
|
}
|
|
|
|
for (soundChip = SOUND_CHIP_1; soundChip < NUM_SOUND_CHIPS; soundChip++) {
|
|
gMockPortB[soundChip] = mapIOPointer(gMockingBoardSlot, gMockPortB[soundChip]);
|
|
gMockPortA[soundChip] = mapIOPointer(gMockingBoardSlot, gMockPortA[soundChip]);
|
|
gMockDataDirB[soundChip] = mapIOPointer(gMockingBoardSlot, gMockDataDirB[soundChip]);
|
|
gMockDataDirA[soundChip] = mapIOPointer(gMockingBoardSlot, gMockDataDirA[soundChip]);
|
|
|
|
*(gMockDataDirA[soundChip]) = 0xff; // Set port A for output
|
|
*(gMockDataDirB[soundChip]) = 0x7; // Set port B for output
|
|
}
|
|
|
|
if (gMockingBoardHasSpeech) {
|
|
if (gMockingBoardSpeechInitialized) {
|
|
mockingBoardSpeechShutdown();
|
|
}
|
|
mockingBoardSpeechInit(gMockingBoardSlot);
|
|
gMockingBoardSpeechInitialized = true;
|
|
} else if (gMockingBoardSpeechInitialized) {
|
|
mockingBoardSpeechShutdown();
|
|
gMockingBoardSpeechInitialized = false;
|
|
}
|
|
|
|
gMockingBoardInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
void mockingBoardShutdown(void)
|
|
{
|
|
if (gMockingBoardSpeechInitialized) {
|
|
mockingBoardSpeechShutdown();
|
|
gMockingBoardSpeechInitialized = false;
|
|
}
|
|
|
|
gMockingBoardInitialized = false;
|
|
}
|
|
|
|
|
|
tSlot mockingBoardSlot(void)
|
|
{
|
|
return gMockingBoardSlot;
|
|
}
|
|
|
|
|
|
bool mockingBoardHasSpeechChip(void)
|
|
{
|
|
return gMockingBoardHasSpeech;
|
|
}
|
|
|
|
|
|
static void writeCommand(tMockingBoardSoundChip soundChip, uint8_t command)
|
|
{
|
|
volatile uint8_t *ptr = gMockPortB[soundChip];
|
|
|
|
*ptr = command;
|
|
*ptr = THROUGH_PORT_B;
|
|
}
|
|
|
|
|
|
static void mockingBoardTableAccess(tMockingBoardSoundChip soundChip, tMockingSoundRegisters *registers)
|
|
{
|
|
uint8_t *data = (uint8_t *)registers;
|
|
volatile uint8_t *ptr = gMockPortA[soundChip];
|
|
uint8_t index;
|
|
|
|
if (!gMockingBoardInitialized)
|
|
return;
|
|
|
|
MOCKINGBOARD_RESET(soundChip);
|
|
for (index = 0; index < 16; index++) {
|
|
*ptr = index;
|
|
MOCKINGBOARD_LATCH(soundChip);
|
|
*ptr = *data;
|
|
MOCKINGBOARD_WRITE(soundChip);
|
|
data++;
|
|
}
|
|
}
|
|
|
|
|
|
void mockingBoardPlaySound(tMockingBoardSpeaker speaker, tMockingSoundRegisters *registers)
|
|
{
|
|
if ((speaker & SPEAKER_LEFT) != 0) {
|
|
mockingBoardTableAccess(SOUND_CHIP_1, registers);
|
|
}
|
|
|
|
if ((speaker & SPEAKER_RIGHT) != 0) {
|
|
mockingBoardTableAccess(SOUND_CHIP_2, registers);
|
|
}
|
|
}
|
|
|
|
|
|
void mockingBoardStopSound(tMockingBoardSpeaker speaker)
|
|
{
|
|
if ((speaker & SPEAKER_LEFT) != 0) {
|
|
MOCKINGBOARD_RESET(SOUND_CHIP_1);
|
|
}
|
|
|
|
if ((speaker & SPEAKER_RIGHT) != 0) {
|
|
MOCKINGBOARD_RESET(SOUND_CHIP_2);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|