From 84ddb58602230fc405d37f7635735f69e3292120 Mon Sep 17 00:00:00 2001 From: Jeremy Rand Date: Fri, 30 Sep 2016 23:06:47 -0400 Subject: [PATCH] Initial commit with basic sound and speech support. --- mocktest.xcodeproj/project.pbxproj | 7 + .../xcschemes/mocktest.xcscheme | 34 +++++ mocktest/Makefile | 2 +- mocktest/main.c | 92 ++++++++++++ mocktest/make/V2Make.scpt | Bin 4742 -> 4710 bytes mocktest/mockingboard.c | 114 +++++++++++++++ mocktest/mockingboard.h | 123 ++++++++++++++++ mocktest/speech.s | 135 ++++++++++++++++++ 8 files changed, 506 insertions(+), 1 deletion(-) create mode 100644 mocktest/mockingboard.c create mode 100644 mocktest/mockingboard.h create mode 100644 mocktest/speech.s diff --git a/mocktest.xcodeproj/project.pbxproj b/mocktest.xcodeproj/project.pbxproj index 9633b01..ef7af1e 100644 --- a/mocktest.xcodeproj/project.pbxproj +++ b/mocktest.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXFileReference section */ + 9D16BCE21D9E070B005EE214 /* speech.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = speech.s; 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 = ""; }; @@ -17,6 +18,8 @@ 9DB3C9941D84A11600395532 /* prodos_template.dsk */ = {isa = PBXFileReference; lastKnownFileType = file; name = prodos_template.dsk; path = make/prodos_template.dsk; sourceTree = ""; }; 9DB3C9951D84A11600395532 /* tail.mk */ = {isa = PBXFileReference; lastKnownFileType = text; name = tail.mk; path = make/tail.mk; sourceTree = ""; }; 9DB3C9961D84A11600395532 /* V2Make.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = V2Make.scpt; path = make/V2Make.scpt; sourceTree = ""; }; + 9DB3C99C1D84A21E00395532 /* mockingboard.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mockingboard.c; sourceTree = ""; }; + 9DB3C99D1D84A21E00395532 /* mockingboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mockingboard.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -31,6 +34,9 @@ isa = PBXGroup; children = ( 9DB3C98C1D84A11600395532 /* main.c */, + 9DB3C99C1D84A21E00395532 /* mockingboard.c */, + 9DB3C99D1D84A21E00395532 /* mockingboard.h */, + 9D16BCE21D9E070B005EE214 /* speech.s */, 9DB3C98D1D84A11600395532 /* Makefile */, 9DB3C98E1D84A11600395532 /* make */, ); @@ -223,6 +229,7 @@ 9DB3C99B1D84A11600395532 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/mocktest.xcodeproj/xcuserdata/jrand.xcuserdatad/xcschemes/mocktest.xcscheme b/mocktest.xcodeproj/xcuserdata/jrand.xcuserdatad/xcschemes/mocktest.xcscheme index 13207a4..9ef9158 100644 --- a/mocktest.xcodeproj/xcuserdata/jrand.xcuserdatad/xcschemes/mocktest.xcscheme +++ b/mocktest.xcodeproj/xcuserdata/jrand.xcuserdatad/xcschemes/mocktest.xcscheme @@ -5,6 +5,22 @@ + + + + + + + + + + @@ -35,6 +60,15 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> + + + + diff --git a/mocktest/Makefile b/mocktest/Makefile index 701c783..9d0a861 100644 --- a/mocktest/Makefile +++ b/mocktest/Makefile @@ -25,7 +25,7 @@ PGM=mocktest # Uncomment the one you want below (the first one is the default): # MACHINE = apple2 # MACHINE = apple2-dos33 -# MACHINE = apple2-system +MACHINE = apple2-system # MACHINE = apple2-loader # MACHINE = apple2-reboot # MACHINE = apple2enh diff --git a/mocktest/main.c b/mocktest/main.c index 2565566..dbabe03 100644 --- a/mocktest/main.c +++ b/mocktest/main.c @@ -8,13 +8,105 @@ */ +#include #include +#include #include +#include "mockingboard.h" + + +extern void setupSpeech(void); +uint8_t **tabPtr = (uint8_t **)0x00f9; + + +tMockingSoundRegisters soundData1 = { + { TONE_PERIOD_A(4), 0, 0 }, // Tone period for the three channels + MIN_NOISE_PERIOD, // Noise period + ENABLE_CHANNEL(TONE_CHANNEL_A), // Enable + { MAX_AMPLITUDE, MIN_AMPLITUDE, MIN_AMPLITUDE }, // Amplitude for the three channels + MIN_ENVELOPE_PERIOD, // Envelope period + ENVELOPE_SHAPE_ONE_SHOT_DECAY, // Envelope shape + 0, // Dummy1 + 0 // Dummy2 +}; + + +tMockingSoundRegisters soundData2 = { + { TONE_PERIOD_F_SHARP(3), 0, 0 }, // Tone period for the three channels + MIN_NOISE_PERIOD, // Noise period + ENABLE_CHANNEL(TONE_CHANNEL_A), // Enable + { MAX_AMPLITUDE, MIN_AMPLITUDE, MIN_AMPLITUDE }, // Amplitude for the three channels + MIN_ENVELOPE_PERIOD, // Envelope period + ENVELOPE_SHAPE_ONE_SHOT_DECAY, // Envelope shape + 0, // Dummy1 + 0 // Dummy2 +}; + + +uint8_t speechData[] = { + 0xCF, 0x00, 0xE8, 0x70, 0xA8, 0x5F, 0x00, 0xE8, + 0x70, 0xA8, 0x5F, 0x00, 0xE8, 0x70, 0xA8, 0x5F, + 0x00, 0xE8, 0x7B, 0xA8, 0x5F, 0x63, 0xE7, 0x6B, + 0xA8, 0x59, 0x47, 0xE8, 0x6A, 0xA8, 0x61, 0x36, + 0xE8, 0x79, 0xA8, 0x65, 0x37, 0xE7, 0x79, 0xA8, + 0x69, 0x0E, 0xE7, 0x6A, 0xA8, 0x61, 0x29, 0xE8, + 0x6B, 0xA8, 0x59, 0xAD, 0xE8, 0x6B, 0xA8, 0x51, + 0xC0, 0xE8, 0x6A, 0xA8, 0x51, 0x07, 0xE8, 0x6A, + 0xA8, 0x4B, 0x39, 0xE8, 0x6A, 0xA8, 0x49, 0x64, + 0xE7, 0x6A, 0x88, 0x49, 0x11, 0xE7, 0x5A, 0xB8, + 0x51, 0x63, 0xE7, 0x5A, 0xB8, 0x59, 0x1D, 0xE8, + 0x7A, 0xA8, 0x61, 0x65, 0xE8, 0x79, 0xA8, 0x61, + 0xC0, 0xE8, 0x70, 0x78, 0x51, 0x00, 0xE8, 0x70, + 0x78, 0x51, 0x00, 0xE8, 0x70, 0x78, 0x59, 0x00, + 0xE7, 0x79, 0xA8, 0x65, 0x04, 0xE7, 0x6A, 0x98, + 0x61, 0x16, 0xE8, 0x6B, 0xA8, 0x61, 0x60, 0xE8, + 0x7C, 0xA8, 0x59, 0x78, 0xE7, 0x7C, 0xA8, 0x55, + 0x0A, 0xE8, 0x6B, 0xA8, 0x62, 0x33, 0xE8, 0x6A, + 0xA8, 0x59, 0x1C, 0xE8, 0x7A, 0xA8, 0x51, 0x64, + 0xE7, 0x7B, 0x88, 0x51, 0x01, 0xE8, 0x7C, 0xA8, + 0x59, 0x30, 0xE8, 0x7D, 0xA8, 0x59, 0x27, 0xE7, + 0x7D, 0x78, 0x61, 0x01, 0xE8, 0x6C, 0xA8, 0x61, + 0x28, 0xE8, 0x6B, 0xA8, 0x59, 0x32, 0xE8, 0x6A, + 0xA8, 0x4D, 0x60, 0xE7, 0x29, 0xA8, 0x41, 0x0A, + 0xE8, 0x78, 0xA8, 0x41, 0x30, 0xE8, 0x70, 0xA8, + 0x39, 0xFF, 0xE8, 0x70, 0xA8, 0x39, 0x00, 0xE8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + + +void delay(void) +{ + int i; + for (i = 0; i < 1000; i++) ; +} + int main(void) { + mockingBoardInit(4); + printf("HELLO, WORLD!\n"); + + while (!kbhit()) { + mockingBoardTableAccess(SOUND_CHIP_1, &soundData1); + delay(); + mockingBoardReset(SOUND_CHIP_1); + + mockingBoardTableAccess(SOUND_CHIP_2, &soundData2); + delay(); + mockingBoardReset(SOUND_CHIP_2); + } + cgetc(); + printf("RUN SPEECH TEST (Y/N) "); + if (cgetc() != 'Y') + return 0; + + *tabPtr = speechData; + setupSpeech(); + + cgetc(); + return 0; } diff --git a/mocktest/make/V2Make.scpt b/mocktest/make/V2Make.scpt index 7f623e571add293dbb6d675a4096a017b360293e..e3c7d860458e81f40be6051628b3a98de8694cef 100644 GIT binary patch delta 205 zcmZoueWtSEIych}j>!+Xy(WL)=9sL_Bghyv*_J1VgT04k*M%3i#V2p%u~EJ7QZb!@ zfq{{MgMp9TnNvgIC<7CNPH?b`m199cPG)jqNoIcDWJO-N$qu{*JZeaSo7;F#Ff+DI z_7_Zb&Sl7FNM^`pC}ButC!+Xy(WL)=9sL_Bgj}X*_J1VgW=eu_h&>i{U>kau?adOy0D*t zfq{{Mhk=jXnSqb>JkzwKEeuQy=E1=(R*nS)Iho0cC7Jno#a0ZixurRYCHX}_W>{uX zNoit^LTofLTQ9MofMK#euc4R$vQB-N#?3jrCzu(#CYuVTx;ruy0AUV8DuWS&6GJIO zCXk)N5CEhLfMPj7I*%chkD-x)aU(;!AVWKY08k*Gp@bokp%^Hg&ydH@(8Q25`L3Xy aFvzl0Bd5~LoRomfg4CSMywu4$LRJ7=icW9< diff --git a/mocktest/mockingboard.c b/mocktest/mockingboard.c new file mode 100644 index 0000000..2ce483d --- /dev/null +++ b/mocktest/mockingboard.c @@ -0,0 +1,114 @@ +// +// mockingboard.c +// mocktest +// +// Created by Jeremy Rand on 2016-09-10. +// Copyright © 2016 Jeremy Rand. All rights reserved. +// + + +#include +#include +#include + +#include "mockingboard.h" + + +// Defines + +#define LATCH_COMMAND 0x7 +#define WRITE_COMMAND 0x6 +#define RESET_COMMAND 0x0 +#define THROUGH_PORT_B 0x4 + + +// 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 gMockingBoardInitialized = false; + + +static uint8_t *mapIOPointer(uint8_t 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) +{ + tSoundChip 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++) { +#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 + } + + gMockingBoardInitialized = true; +} + + +static void writeCommand(tSoundChip soundChip, uint8_t command) +{ + volatile uint8_t *ptr = gMockPortB[soundChip]; + + *ptr = command; + *ptr = THROUGH_PORT_B; +} + + +void mockingBoardLatch(tSoundChip soundChip) +{ + writeCommand(soundChip, LATCH_COMMAND); +} + + +void mockingBoardWrite(tSoundChip soundChip) +{ + writeCommand(soundChip, WRITE_COMMAND); +} + + +void mockingBoardReset(tSoundChip soundChip) +{ + writeCommand(soundChip, RESET_COMMAND); +} + + +void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *registers) +{ + uint8_t *data = (uint8_t *)registers; + volatile uint8_t *ptr = gMockPortA[soundChip]; + uint8_t index; + + mockingBoardReset(soundChip); + for (index = 0; index < 16; index++) { + *ptr = index; + mockingBoardLatch(soundChip); + *ptr = *data; + mockingBoardWrite(soundChip); + data++; + } +} diff --git a/mocktest/mockingboard.h b/mocktest/mockingboard.h new file mode 100644 index 0000000..6ec6bcd --- /dev/null +++ b/mocktest/mockingboard.h @@ -0,0 +1,123 @@ +// +// 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 + + +// 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 { + SOUND_CHIP_1 = 0, + SOUND_CHIP_2 = 1, + NUM_SOUND_CHIPS = 2 +} tSoundChip; + + +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 + +void mockingBoardInit(tSlot slot); + +void mockingBoardLatch(tSoundChip soundChip); +void mockingBoardWrite(tSoundChip soundChip); +void mockingBoardReset(tSoundChip soundChip); + +void mockingBoardTableAccess(tSoundChip soundChip, tMockingSoundRegisters *registers); + + +#endif /* defined(__mocktest__mockingboard__) */ diff --git a/mocktest/speech.s b/mocktest/speech.s new file mode 100644 index 0000000..f7c2a4d --- /dev/null +++ b/mocktest/speech.s @@ -0,0 +1,135 @@ +; +; speech.s +; mocktest +; +; Created by Jeremy Rand on 2016-09-29. +; Copyright © 2016 Jeremy Rand. All rights reserved. +; + + + .export _setupSpeech + + +TABPTR := $F9 ;DATA POINTER +OUTPTR := $FB ;START OF DATA POINTER +ENDPTR := $FD ;END OF DATA POINTER +BUSY := $FF ;BUSY FLAG +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 REG-6522 +IFR := $C48D ;INTERRUPT FLAG REG-6522 +IER := $C48E + + +.CODE + +.proc _setupSpeech + SEI ;DISABLE INTERRUPTS + LDA #<_interr ;SET INTERRUPT VECTOR + STA IRQL ;TO POINT TO INTERRUPT + LDA #>_interr ;SERVICE ROUTINE + STA IRQH + LDA #$00 + STA DDRA + STA DDRB + + LDA TABPTR+1 ;GET HIGH ADDRESS OF DATA + STA OUTPTR+1 ;STORE IN WORK POINTER + LDX TABPTR ;GET LOW ADDRESS OF DATA + INX ;INCREMENT TWICE + INX ;TO SKIP OVER LENGTH BYTES + BNE @L1 ;CHECK FOR PAGE BOUNDARY + INC OUTPTR+1 +@L1: + STX OUTPTR ;STORE LOW BYTE + + LDY #$01 + LDA (TABPTR),Y ;GET HIGH LENGTH BYTE + CLC + ADC TABPTR+1 ;AND ADD TO BASE ADDRESS + STA ENDPTR+1 ;STORE END ADDRESS + DEY + LDA (TABPTR),Y ;GET LOW LENGTH BYTE + CLC + ADC TABPTR ;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 BUSY ;AND SET PERIPHERAL CONTROL + LDA #$0C ;REGISTER TO RECOGNIZE + STA PCR ;SIGNAL FROM SPEECH CHIP + LDA #$80 ;RAISE CTRL BIT IN REGISTER 3 + STA CTTRAMP + LDA #$C0 ;SET TRANSITIONED INFLECTION + STA DURPHON ;MODE IN REGISTER 0 + LDA #$70 ;LOWER CTRL BIT + STA CTTRAMP + LDA #$82 ;ENABLE 6522 INTERRUPTS + STA IER + CLI ;CLEAR INTERRUPT MASK + RTS ;RETURN TO CALLER +.endproc + + +.proc _interr + TXA ;SAVE REGISTERS + PHA + TYA + PHA + LDA IFR + BPL @L2 + LDA #$02 ;CLEAR INTERRUPT FLAG + STA IFR + LDY #$00 ;INIT REGISTERS + LDX #$04 + LDA OUTPTR ;CHECK FOR END OF DATA FILE + CMP ENDPTR + BCC @L1 ;IF NOT THEN CONTINUE + LDA OUTPTR+1 ;CHECK HIGH ADDRESS ALSO + CMP ENDPTR+1 + BCC @L1 ;IF NOT THEN CONTINUE + LDA #$00 ;IF END, TURN EVERYTHING OFF + STA DURPHON ;STORE PAUSE PHONEME + LDA #$70 ;ZERO AMPLITUDE + STA CTTRAMP + LDA #$00 ;CLEAR BUSY FLAG + STA BUSY + LDA #$02 ;CLEAR INTERRUPT ENABLE + STA IER ;IN 6522 + LDA #$FF + STA DDRA + LDA #$07 + STA DDRB +@L2: + PLA ;RESTORE REGISTERS + TAY + PLA + TAX + LDA $45 + RTI ;RETURN FROM INTERRUPT + +@L1: + LDA (OUTPTR),Y ;GET DATA + STA BASE,X ;STORE IN SPEECH CHIP + INC OUTPTR ;NEXT DATA + BNE @L3 + INC OUTPTR+1 + +@L3: + DEX ;NEXT REGISTER + CPX #$FF ;LAST REGISTER? + BNE @L1 ;NO, CONTINUE + BEQ @L2 ;YES, RETURN +.endproc +