From 36ed1666705d71af806e2336484fb55a2cd533ac Mon Sep 17 00:00:00 2001 From: Jeremy Rand Date: Tue, 20 Dec 2016 00:05:41 -0500 Subject: [PATCH] Add mockingboard library and start reworking the sound code to prepare to integrate it. --- a2bejwld.xcodeproj/project.pbxproj | 27 +++ a2bejwld/Makefile | 2 +- a2bejwld/anim.c | 66 +------ a2bejwld/anim.h | 3 - a2bejwld/mockingboard.c | 189 ++++++++++++++++++++ a2bejwld/mockingboard.h | 128 ++++++++++++++ a2bejwld/mockingboard_speech.h | 26 +++ a2bejwld/mockingboard_speech.s | 265 +++++++++++++++++++++++++++++ a2bejwld/sound.c | 138 +++++++++++++++ a2bejwld/sound.h | 25 +++ a2bejwld/ui.c | 33 ++-- a2bejwld/ui.h | 7 +- 12 files changed, 822 insertions(+), 87 deletions(-) create mode 100644 a2bejwld/mockingboard.c create mode 100644 a2bejwld/mockingboard.h create mode 100644 a2bejwld/mockingboard_speech.h create mode 100644 a2bejwld/mockingboard_speech.s create mode 100644 a2bejwld/sound.c create mode 100644 a2bejwld/sound.h diff --git a/a2bejwld.xcodeproj/project.pbxproj b/a2bejwld.xcodeproj/project.pbxproj index a07e17a..e01128f 100644 --- a/a2bejwld.xcodeproj/project.pbxproj +++ b/a2bejwld.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 9D4D1AB31D6D709600D20BB8 /* game.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D6B47401D40098300F6D704 /* game.c */; }; 9D4D1AB41D6D709F00D20BB8 /* machine.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D4D1AA31D6D0E9B00D20BB8 /* machine.c */; }; 9D4D1AB51D6D70A300D20BB8 /* anim.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D6B47481D4270EC00F6D704 /* anim.c */; }; + 9DB20B3E1E078FE20065E263 /* mockingboard.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DB20B3A1E078FE20065E263 /* mockingboard.c */; }; + 9DB20B3F1E078FE20065E263 /* mockingboard_speech.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DB20B3C1E078FE20065E263 /* mockingboard_speech.s */; }; + 9DB20B421E07904A0065E263 /* sound.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DB20B401E07904A0065E263 /* sound.c */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -58,6 +61,12 @@ 9D6B47491D4270EC00F6D704 /* anim.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = anim.h; sourceTree = ""; }; 9D6B474A1D42DEB600F6D704 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 9D6B474C1D43BBAC00F6D704 /* a2bejwld.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = a2bejwld.png; sourceTree = ""; }; + 9DB20B3A1E078FE20065E263 /* mockingboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mockingboard.c; sourceTree = ""; }; + 9DB20B3B1E078FE20065E263 /* mockingboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mockingboard.h; sourceTree = ""; }; + 9DB20B3C1E078FE20065E263 /* mockingboard_speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = mockingboard_speech.s; sourceTree = ""; }; + 9DB20B3D1E078FE20065E263 /* mockingboard_speech.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mockingboard_speech.h; sourceTree = ""; }; + 9DB20B401E07904A0065E263 /* sound.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sound.c; sourceTree = ""; }; + 9DB20B411E07904A0065E263 /* sound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sound.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -125,7 +134,10 @@ 9D4D1AA41D6D0E9B00D20BB8 /* machine.h */, 9D6B47481D4270EC00F6D704 /* anim.c */, 9D6B47491D4270EC00F6D704 /* anim.h */, + 9DB20B401E07904A0065E263 /* sound.c */, + 9DB20B411E07904A0065E263 /* sound.h */, 9D6B472F1D3FB16F00F6D704 /* Makefile */, + 9DB20B391E078FC70065E263 /* Mockingboard */, 9D3A9FBE1D457973004C5897 /* joystick */, 9D3A9FBA1D4578B4004C5897 /* mouse */, 9D6B47301D3FB16F00F6D704 /* make */, @@ -156,6 +168,17 @@ name = screenshots; sourceTree = ""; }; + 9DB20B391E078FC70065E263 /* Mockingboard */ = { + isa = PBXGroup; + children = ( + 9DB20B3A1E078FE20065E263 /* mockingboard.c */, + 9DB20B3B1E078FE20065E263 /* mockingboard.h */, + 9DB20B3C1E078FE20065E263 /* mockingboard_speech.s */, + 9DB20B3D1E078FE20065E263 /* mockingboard_speech.h */, + ); + name = Mockingboard; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXLegacyTarget section */ @@ -233,9 +256,12 @@ buildActionMask = 2147483647; files = ( 9D4D1AB51D6D70A300D20BB8 /* anim.c in Sources */, + 9DB20B421E07904A0065E263 /* sound.c in Sources */, + 9DB20B3E1E078FE20065E263 /* mockingboard.c in Sources */, 9D4D1AB31D6D709600D20BB8 /* game.c in Sources */, 9D4D1AB11D6D708E00D20BB8 /* main.c in Sources */, 9D4D1AB21D6D709100D20BB8 /* ui.c in Sources */, + 9DB20B3F1E078FE20065E263 /* mockingboard_speech.s in Sources */, 9D4D1AB41D6D709F00D20BB8 /* machine.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -371,6 +397,7 @@ 9D4D1AB01D6D704800D20BB8 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 9D6B472B1D3FB16F00F6D704 /* Build configuration list for PBXProject "a2bejwld" */ = { isa = XCConfigurationList; diff --git a/a2bejwld/Makefile b/a2bejwld/Makefile index 9813900..7f88a46 100644 --- a/a2bejwld/Makefile +++ b/a2bejwld/Makefile @@ -31,7 +31,7 @@ PGM=a2bejwld # MACHINE = apple2enh # MACHINE = apple2enh-dos33 MACHINE = apple2enh-system -* MACHINE = apple2enh-loader +# MACHINE = apple2enh-loader # MACHINE = apple2enh-reboot # Uncomment and set this to your starting address in Apple II memory diff --git a/a2bejwld/anim.c b/a2bejwld/anim.c index a58dd58..8b0f523 100644 --- a/a2bejwld/anim.c +++ b/a2bejwld/anim.c @@ -18,6 +18,7 @@ #include "dbllores.h" #include "game.h" #include "machine.h" +#include "sound.h" #include "ui.h" @@ -28,13 +29,6 @@ #define DROP_ACCELERATION 1 -#define CLEAR_GEM_SOUND_NORMAL 0 -#define CLEAR_GEM_SOUND_STAR 1 -#define CLEAR_GEM_SOUND_SPECIAL 2 -#define CLEAR_GEM_SOUND_EXPLODE 3 -#define NUM_CLEAR_GEM_SOUNDS 4 - - #define VERTICAL_PIXELS 48 #define HORIZONTAL_PIXELS 64 @@ -70,7 +64,6 @@ typedef struct tStarAnimState typedef struct tClearGemAnimState { uint8_t squaresToClear[NUM_SQUARES / (sizeof(uint8_t))]; - uint8_t clearGemSound; bool gotOne; } tClearGemAnimState; @@ -105,28 +98,6 @@ static uint8_t gTempX; static uint8_t gTempY; static uint8_t gTempGemType; -static uint8_t gClearGemSoundFreq[NUM_CLEAR_GEM_SOUNDS][8] = { - { // CLEAR_GEM_SOUND_NORMAL - 30, 25, 20, 30, 30, 30, 30, 0 }, - { // CLEAR_GEM_SOUND_STAR - 10, 9, 8, 7, 6, 5, 4, 0 }, - { // CLEAR_GEM_SOUND_SPECIAL - 4, 6, 8, 10, 8, 6, 4, 0 }, - { // CLEAR_GEM_SOUND_EXPLODE - 50, 60, 50, 60, 50, 60, 50, 0 } -}; - -static uint8_t gClearGemSoundDuration[NUM_CLEAR_GEM_SOUNDS][8] = { - { // CLEAR_GEM_SOUND_NORMAL - 10, 15, 20, 10, 10, 10, 10, 0 }, - { // CLEAR_GEM_SOUND_STAR - 30, 31, 32, 33, 34, 35, 36, 0 }, - { // CLEAR_GEM_SOUND_SPECIAL - 36, 34, 32, 30, 32, 34, 36, 0 }, - { // CLEAR_GEM_SOUND_EXPLODE - 8, 8, 8, 8, 8, 8, 8, 0 }, -}; - static tClearGemHandler gClearGemHandler[] = { explodeGemFrame1, explodeGemFrame2, @@ -213,6 +184,7 @@ void doStarAnim(void) void beginClearGemAnim(void) { memset(&gClearGemAnimState, 0, sizeof(gClearGemAnimState)); + beginClearGemSound(); } @@ -235,50 +207,22 @@ void undoClearAtSquare(tSquare square) } -void playSoundForExplodingGem(void) -{ - if (gClearGemAnimState.clearGemSound < CLEAR_GEM_SOUND_EXPLODE) - gClearGemAnimState.clearGemSound = CLEAR_GEM_SOUND_EXPLODE; -} - - -void playSoundForStarringGem(void) -{ - if (gClearGemAnimState.clearGemSound < CLEAR_GEM_SOUND_STAR) - gClearGemAnimState.clearGemSound = CLEAR_GEM_SOUND_STAR; -} - - -void playSoundForSpecialGem(void) -{ - if (gClearGemAnimState.clearGemSound < CLEAR_GEM_SOUND_SPECIAL) - gClearGemAnimState.clearGemSound = CLEAR_GEM_SOUND_SPECIAL; -} - - #undef DEBUG_CLEAR_ANIM void endClearGemAnim(void) { tSquare square; uint8_t bit; uint8_t offset; - uint8_t *clearGemSoundFreq; - uint8_t *clearGemSoundDuration; uint8_t frame; if (!gClearGemAnimState.gotOne) return; - clearGemSoundFreq = &(gClearGemSoundFreq[gClearGemAnimState.clearGemSound][0]); - clearGemSoundDuration = &(gClearGemSoundDuration[gClearGemAnimState.clearGemSound][0]); - for (frame = 0; frame < (sizeof(gClearGemHandler) / sizeof(gClearGemHandler[0])); frame++) { bit = 1; offset = 0; - playSound(*clearGemSoundFreq, *clearGemSoundDuration); - clearGemSoundFreq++; - clearGemSoundDuration++; + playClearGemSound(frame); gVblWait(); for (square = 0; square < NUM_SQUARES; square++) { @@ -491,7 +435,7 @@ void endDropAnim(void) if (gemInfo->y == gemInfo->endY) { gemInfo->landed = true; - playSound(1, 1); + playLandingSound(); continue; } @@ -603,4 +547,4 @@ void endDropAnim(void) cgetc(); #endif } -} \ No newline at end of file +} diff --git a/a2bejwld/anim.h b/a2bejwld/anim.h index 0d33e4c..6c16ef2 100644 --- a/a2bejwld/anim.h +++ b/a2bejwld/anim.h @@ -27,9 +27,6 @@ extern void doStarAnim(void); extern void beginClearGemAnim(void); extern void addClearAtSquare(tSquare square); extern void undoClearAtSquare(tSquare square); -extern void playSoundForExplodingGem(void); -extern void playSoundForStarringGem(void); -extern void playSoundForSpecialGem(void); extern void endClearGemAnim(void); extern void swapSquares(tSquare square1, tGemType gemType1, bool starred1, diff --git a/a2bejwld/mockingboard.c b/a2bejwld/mockingboard.c new file mode 100644 index 0000000..cff3358 --- /dev/null +++ b/a2bejwld/mockingboard.c @@ -0,0 +1,189 @@ +// +// mockingboard.c +// mocktest +// +// Created by Jeremy Rand on 2016-09-10. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + + +#include +#include +#include + +#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; + + +// 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; +} + + +void mockingBoardInit(tSlot slot, bool hasSpeechChip) +{ + tMockingBoardSoundChip soundChip; + + 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(slot, gMockPortB[soundChip]); + gMockPortA[soundChip] = mapIOPointer(slot, gMockPortA[soundChip]); + gMockDataDirB[soundChip] = mapIOPointer(slot, gMockDataDirB[soundChip]); + gMockDataDirA[soundChip] = mapIOPointer(slot, gMockDataDirA[soundChip]); + + *(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(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; +} diff --git a/a2bejwld/mockingboard.h b/a2bejwld/mockingboard.h new file mode 100644 index 0000000..d7b57f5 --- /dev/null +++ b/a2bejwld/mockingboard.h @@ -0,0 +1,128 @@ +// +// mockingboard.h +// mocktest +// +// Created by Jeremy Rand on 2016-09-10. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + +#ifndef __mocktest__mockingboard__ +#define __mocktest__mockingboard__ + + +#include +#include + + +// Defines + +#define MOCK_NUM_CHANNELS 3 + + +#define TONE_PERIOD_C(octave) (0x7a3 >> (octave - 1)) +#define TONE_PERIOD_C_SHARP(octave) (0x735 >> (octave - 1)) +#define TONE_PERIOD_D(octave) (0x6cd >> (octave - 1)) +#define TONE_PERIOD_D_SHARP(octave) (0x66c >> (octave - 1)) +#define TONE_PERIOD_E(octave) (0x60f >> (octave - 1)) +#define TONE_PERIOD_F(octave) (0x5b8 >> (octave - 1)) +#define TONE_PERIOD_F_SHARP(octave) (0x566 >> (octave - 1)) +#define TONE_PERIOD_G(octave) (0x518 >> (octave - 1)) +#define TONE_PERIOD_G_SHARP(octave) (0x4cf >> (octave - 1)) +#define TONE_PERIOD_A(octave) (0x48a >> (octave - 1)) +#define TONE_PERIOD_A_SHARP(octave) (0x449 >> (octave - 1)) +#define TONE_PERIOD_B(octave) (0x40b >> (octave - 1)) + +#define MIN_NOISE_PERIOD 0 +#define MAX_NOISE_PERIOD 31 + +#define TONE_CHANNEL_A 1 +#define TONE_CHANNEL_B 2 +#define TONE_CHANNEL_C 4 +#define NOISE_CHANNEL_A 8 +#define NOISE_CHANNLE_B 16 +#define NOISE_CHANNEL_C 32 + +#define ENABLE_CHANNEL(channels) (0x3f ^ (channels)) + +#define MIN_AMPLITUDE 0 +#define MAX_AMPLITUDE 15 +#define VARIABLE_AMPLITUDE 16 + +#define MIN_ENVELOPE_PERIOD 0 +#define MAX_ENVELOPE_PERIOD 65535u +#define MILLISEC_TO_ENVELOP_PERIOD(ms) ((ms) * 4) + + +// Here is a table of the envelope shapes and how they look: +// +// ENVELOPE_SHAPE_ONE_SHOT_DECAY \__________... +// +// ENVELOPE_SHAPE_ONE_SHOT_ATTACK /__________... +// +// ENVELOPE_SHAPE_CONT_DECAY \|\|\|\|\|\... +// +// ENVELOPE_SHAPE_CONT_DECAY_HOLD \__________... +// +// ENVELOPE_SHAPE_CONT_DECAY_ALT \/\/\/\/\/\... +// _________ +// ENVELOPE_SHAPE_CONT_DECAY_ALT_HOLD \| ... +// +// ENVELOPE_SHAPE_CONT_ATTACK /|/|/|/|/|/... +// __________ +// ENVELOPE_SHAPE_CONT_ATTACK_HOLD / ... +// ENVELOPE_SHAPE_CONT_ATTACK_ALT /\/\/\/\/\/... +// +// ENVELOPE_SHAPE_CONT_ATTACK_ALT_HOLD /|_________... + +#define ENVELOPE_SHAPE_ONE_SHOT_DECAY 0 +#define ENVELOPE_SHAPE_ONE_SHOT_ATTACK 4 +#define ENVELOPE_SHAPE_CONT_DECAY 8 +#define ENVELOPE_SHAPE_CONT_DECAY_HOLD 9 +#define ENVELOPE_SHAPE_CONT_DECAY_ALT 10 +#define ENVELOPE_SHAPE_CONT_DECAY_ALT_HOLD 11 +#define ENVELOPE_SHAPE_CONT_ATTACK 12 +#define ENVELOPE_SHAPE_CONT_ATTACK_HOLD 13 +#define ENVELOPE_SHAPE_CONT_ATTACK_ALT 14 +#define ENVELOPE_SHAPE_CONT_ATTACK_ALT_HOLD 15 + + + +// Typedefs + +typedef uint8_t tSlot; + + +typedef enum { + SPEAKER_NONE = 0, + SPEAKER_LEFT = (1 << 0), + SPEAKER_RIGHT = (1 << 1), + SPEAKER_BOTH = (1 << 0) | (1 << 1) +} tMockingBoardSpeaker; + + +typedef struct tMockingSoundRegisters { + uint16_t tonePeriod[MOCK_NUM_CHANNELS]; + uint8_t noisePeriod; + uint8_t enable; + uint8_t amplitude[MOCK_NUM_CHANNELS]; + uint16_t envelopePeriod; + uint8_t envelopeShape; + uint8_t dummy1; + uint8_t dummy2; +} tMockingSoundRegisters; + + +// API + +extern void mockingBoardInit(tSlot slot, bool hasSpeechChip); +extern void mockingBoardShutdown(void); + +extern void mockingBoardPlaySound(tMockingBoardSpeaker speaker, tMockingSoundRegisters *registers); +extern void mockingBoardStopSound(tMockingBoardSpeaker speaker); + +extern bool mockingBoardSpeechIsBusy(void); +extern bool mockingBoardSpeechIsPlaying(void); +extern bool mockingBoardSpeak(uint8_t *data, uint16_t dataLen); + + +#endif /* defined(__mocktest__mockingboard__) */ diff --git a/a2bejwld/mockingboard_speech.h b/a2bejwld/mockingboard_speech.h new file mode 100644 index 0000000..3228735 --- /dev/null +++ b/a2bejwld/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/a2bejwld/mockingboard_speech.s b/a2bejwld/mockingboard_speech.s new file mode 100644 index 0000000..6364996 --- /dev/null +++ b/a2bejwld/mockingboard_speech.s @@ -0,0 +1,265 @@ +; +; 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 + .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 + + +.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 + 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 _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 #$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: +; 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 diff --git a/a2bejwld/sound.c b/a2bejwld/sound.c new file mode 100644 index 0000000..a2edc04 --- /dev/null +++ b/a2bejwld/sound.c @@ -0,0 +1,138 @@ +// +// sound.c +// a2bejwld +// +// Created by Jeremy Rand on 2016-12-18. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + +#include "sound.h" +#include "ui.h" + +#include + + +// Defines + +#define CLEAR_GEM_SOUND_NORMAL 0 +#define CLEAR_GEM_SOUND_STAR 1 +#define CLEAR_GEM_SOUND_SPECIAL 2 +#define CLEAR_GEM_SOUND_EXPLODE 3 +#define NUM_CLEAR_GEM_SOUNDS 4 + + +// Globals + +static uint8_t gSoundClearGem = 0; + +static uint8_t gClearGemSoundFreq[NUM_CLEAR_GEM_SOUNDS][8] = { + { // CLEAR_GEM_SOUND_NORMAL + 30, 25, 20, 30, 30, 30, 30, 0 }, + { // CLEAR_GEM_SOUND_STAR + 10, 9, 8, 7, 6, 5, 4, 0 }, + { // CLEAR_GEM_SOUND_SPECIAL + 4, 6, 8, 10, 8, 6, 4, 0 }, + { // CLEAR_GEM_SOUND_EXPLODE + 50, 60, 50, 60, 50, 60, 50, 0 } +}; + +static uint8_t gClearGemSoundDuration[NUM_CLEAR_GEM_SOUNDS][8] = { + { // CLEAR_GEM_SOUND_NORMAL + 10, 15, 20, 10, 10, 10, 10, 0 }, + { // CLEAR_GEM_SOUND_STAR + 30, 31, 32, 33, 34, 35, 36, 0 }, + { // CLEAR_GEM_SOUND_SPECIAL + 36, 34, 32, 30, 32, 34, 36, 0 }, + { // CLEAR_GEM_SOUND_EXPLODE + 8, 8, 8, 8, 8, 8, 8, 0 }, +}; + + + + +// Implementation + + +static void playSound(int8_t startFreq, int8_t duration) +{ + int8_t freq; + + while (duration > 0) { + asm ("STA %w", 0xc030); + freq = startFreq; + while (freq > 0) { + freq--; + } + duration--; + } +} + + +void beginClearGemSound(void) +{ + gSoundClearGem = (1 << CLEAR_GEM_SOUND_NORMAL); +} + + +void playSoundForExplodingGem(void) +{ + gSoundClearGem |= (1 << CLEAR_GEM_SOUND_EXPLODE); +} + + +void playSoundForStarringGem(void) +{ + gSoundClearGem |= (1 << CLEAR_GEM_SOUND_STAR); +} + + +void playSoundForSpecialGem(void) +{ + gSoundClearGem |= (1 << CLEAR_GEM_SOUND_SPECIAL); +} + + +void playClearGemSound(uint8_t frame) +{ + static uint8_t *clearGemSoundFreq; + static uint8_t *clearGemSoundDuration; + + if (!soundEnabled()) + return; + + if (mockingBoardEnabled()) { + // Do something here... + } else { + if (frame == 0) { + uint8_t clearGemSound = CLEAR_GEM_SOUND_NORMAL; + + if ((gSoundClearGem & (1 << CLEAR_GEM_SOUND_EXPLODE)) != 0) { + clearGemSound = CLEAR_GEM_SOUND_EXPLODE; + } else if ((gSoundClearGem & (1 << CLEAR_GEM_SOUND_SPECIAL)) != 0) { + clearGemSound = CLEAR_GEM_SOUND_SPECIAL; + } else if ((gSoundClearGem & (1 << CLEAR_GEM_SOUND_STAR)) != 0) { + clearGemSound = CLEAR_GEM_SOUND_STAR; + } + + clearGemSoundFreq = &(gClearGemSoundFreq[clearGemSound][0]); + clearGemSoundDuration = &(gClearGemSoundDuration[clearGemSound][0]); + } + + playSound(*clearGemSoundFreq, *clearGemSoundDuration); + clearGemSoundFreq++; + clearGemSoundDuration++; + } +} + + +void playLandingSound(void) +{ + if (!soundEnabled()) + return; + + if (mockingBoardEnabled()) { + // Do something here... + } else { + playSound(1, 1); + } +} diff --git a/a2bejwld/sound.h b/a2bejwld/sound.h new file mode 100644 index 0000000..2cf9232 --- /dev/null +++ b/a2bejwld/sound.h @@ -0,0 +1,25 @@ +// +// sound.h +// a2bejwld +// +// Created by Jeremy Rand on 2016-12-18. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + +#ifndef __a2bejwld__sound__ +#define __a2bejwld__sound__ + + +#include + + +extern void beginClearGemSound(void); +extern void playSoundForExplodingGem(void); +extern void playSoundForStarringGem(void); +extern void playSoundForSpecialGem(void); +extern void playClearGemSound(uint8_t frame); + +extern void playLandingSound(void); + + +#endif /* defined(__a2bejwld__sound__) */ diff --git a/a2bejwld/ui.c b/a2bejwld/ui.c index a6f3ff1..796b46a 100644 --- a/a2bejwld/ui.c +++ b/a2bejwld/ui.c @@ -19,12 +19,13 @@ #include "joystick.h" #include "machine.h" #include "mouseWrapper.h" +#include "sound.h" // Defines #define SAVE_OPTIONS_FILE "a2bejwld.opts" -#define VERSION "v1.2" +#define VERSION "v2.0" // Typedefs @@ -103,6 +104,18 @@ static tGameOptions gGameOptions = { // Implementation +bool soundEnabled(void) +{ + return gGameOptions.enableSound; +} + + +bool mockingBoardEnabled(void) +{ + return false; +} + + void badThingHappened(void) { if (gGameOptions.enableSound) @@ -110,24 +123,6 @@ void badThingHappened(void) } -void playSound(int8_t startFreq, int8_t duration) -{ - int8_t freq; - - if (!gGameOptions.enableSound) - return; - - while (duration > 0) { - asm ("STA %w", 0xc030); - freq = startFreq; - while (freq > 0) { - freq--; - } - duration--; - } -} - - static void showAndClearDblLoRes(void) { showDblLoRes(); diff --git a/a2bejwld/ui.h b/a2bejwld/ui.h index 05361ca..ed025e5 100644 --- a/a2bejwld/ui.h +++ b/a2bejwld/ui.h @@ -10,18 +10,19 @@ #define __a2bejwld__ui__ -#include +#include // API extern void initUI(void); +extern bool soundEnabled(void); +extern bool mockingBoardEnabled(void); + extern void printInstructions(void); extern void playGame(void); -extern void playSound(int8_t startFreq, int8_t duration); - #endif /* defined(__a2bejwld__ui__) */