* * SpeechLab 20A RAM code * (c) 1978, Heuristics * (c) 2019, Brutal Deluxe Software * Visit http://www.brutaldeluxe.fr/ * * Thank you, Jeremy Apple * org $6000 mx %11 lst off *--------------------------------------------------------- * Version * * v1.0 - 20190120 - Antoine Vignau * After a couple of tries, it is now working * Thank you for your help, Jeremy Apple * It would be cool to have a new version of the card! * *--- Equates numWORDS = 32 ; number of words one can record (variable) wordSIZE = 64 ; length of a spoken word (fixed) *--- Zero page equates CSWL = $36 ; for output CSWH = CSWL+1 KSWL = CSWH+1 ; for input KSWH = KSWL+1 A5H = $45 ; ACC XREG = A5H+1 ; $46 YREG = XREG+1 ; $47 STATUS = YREG+1 ; $48 zpF0 = $fc ; Zero page addresses I use zpF1 = zpF0+1 ; $FC..$FF zpPTR = zpF1+1 *--- Firmware/ROM equates BELL1 = $FBDD ; Beep COUT1 = $FDF0 ; Output a char RESTORE = $FF3F ; Restore A/X/Y/P SAVE = $FF4A ; Save A/X/Y/P/S IORTS = $FF58 ; The official RTS *--------------------------------------------------------- * How to use * * Installation of the driver * 10 PRINT CHR$(4);"BLOAD SPEECHLAB20A RAM DRIVER" * 20 DP = 24576 : REM $6000 FOR PR# * 30 DI = DP + 3 : REM $6003 FOR IN# * 40 POKE DP + 8, 5 : REM SLOT OF THE CARD * REM NO NEED FOR LOMEM ANYMORE * * Init the card * 10 CALL DP : PRINT : CALL DP * ==> simulates PR#slot : PRINT : PR#0 * * Get the buffer address * 10 BU = PEEK(DP + 7) * 256 + PEEK(DP + 6) * * Record a word * 10 CALL DP : PRINT "WORD" : CALL DP * ==> simulates PR#slot : PRINT "WORD" : PR#0 * * Get a word * 10 CALL DI : INPUT V$ : CALL DI * ==> simulates IN#slot : INPUT V$ : IN#0 * ==> If V$ is an empty string, no word was recognized * *--------------------------------------------------------- * Room for improvement * * Make buffers movable * The buffers follow the code. One could add pointers * to put them in other areas of memory * * Change the number of words * That one is easy, change numWORDS to the value you want * * PR# and IN# * One could keep the doPR and doIN to simulate the PR# * and IN# BASIC calls. PR#0 and IN#0 could be kept * That would save one page of code * *--------------------------------------------------------- * Entry point jmp doPR ; CALL $6000 jmp doIN ; CALL $6003 da ptrBUFFER ; PEEK($6007)*256 + PEEK($6006) L12F2 ds 1 ; POKE($6008),slot ; later transformed to slot*16 *--------------------------------------------------------- * The card runs with the INT ROM on * If the "modern" ROMs are called, * then calls to SAVE/RESTORE will be useless LC809 ; our new entry point lda L12F2 ; is slot already cmp #7+1 ; set to slot*16? bcs LC817 ; yes asl asl asl asl sta L12F2 ; no, do slot*16 LC817 JSR SAVE ; save all parms LDA CSWH ; output is already set to our card? CMP #>LC900 ; L12F1 BEQ LC878 ; yes LDA #LC84B STA KSWH LDA #IORTS STA CSWH LDY #$00 ; string index is 0 STY L12F4 JSR LC956 ; DO MAGIC CMP #numWORDS BPL LC875 TAX ; index LDA L12F6,X ; pointer STA zpPTR LDA L1317,X STA zpPTR+1 JSR RESTORE ; restore LC84B JSR SAVE ; save LDY L12F4 ; get index LDA (zpPTR),Y ; get char INC L12F4 ; y++ CMP #$8D ; end of string? BNE LC864 ; no LC85A LDA #LC817 STA KSWH LDA #$8D ; exit with a RET LC864 PHA ; save JSR RESTORE ; restore PLA ; pull A RTS ; return LC86A LDA #COUT1 STA CSWH rts ; exit to RANGE ERR (modified) LC875 JMP LC85A *--- Card is already init'ed LC878 LDA A5H ; get a char CMP #$8D ; 8D means reset list of words BEQ LC8D8 LDA #LC89F STA CSWH LDY #$00 ; Y as a counter STY L12F4 LDX L12F3 ; word index CPX #numWORDS BPL LC86A ; we're full! LDA L12F6,X ; get its pointer STA zpPTR LDA L1317,X STA zpPTR+1 JSR RESTORE ; restore registers LC89F JSR SAVE ; save registers LDA A5H ; get A LDY L12F4 ; get Y STA (zpPTR),Y ; save char INC L12F4 ; y++ CMP #$8D BNE LC8D4 LDA L12F3 ; we're done, get word index JSR LC909 ; DO MAGIC INC L12F3 ; next index LDX L12F3 ; get it again LDA zpPTR ; ptr to char CLC ADC L12F4 ; +index in string STA L12F6,X ; save pointer low LDA zpPTR+1 ADC #$00 STA L1317,X ; save pointer high LDA #LC817 STA CSWH LC8D4 jmp RESTORE ; restore and exit (JMP) LC8D8 LDA #$00 ; word index STA L12F3 LDA #L1339 STA L1317 LDA #$00 ; init table of words LDX #numWORDS-1 LC8EB STA L0820,X DEX BPL LC8EB jmp RESTORE ; restore all and return (JMP) *--------------------------------------------------------- *--- Buffers and friends L081F ds 1 ; a value L12F3 ds 1 ; index in list of words, see L12F6 L12F4 ds 1 ; Y as an index L12F5 ds 1 ; A as an index ds \ *--------------------------------------------------------- * The $Cs00 page LC900 JSR LC809 LC909 STA L081F TAX LDA #$01 STA L0820,X LC912 JSR LC973 BEQ LC96D JSR LCB2D LDA #L0AF1 STA L0808+2 LDA #$00 LSR L081F BCC LC92D LDA #wordSIZE LC92D LSR L081F BCC LC934 ORA #$80 LC934 CLC ADC L0808+1 STA L0808+1 LDA L0808+2 ADC L081F STA L0808+2 LDA #L0AB1 STA L0805+2 LDA #wordSIZE-1 JSR L0804 LDA #$00 RTS LC956 JSR LC973 CMP #$00 BEQ LC967 JSR LCB2D JSR LCA94 LDA L0840 RTS LC967 JSR BELL1 JMP LC956 LC96D JSR BELL1 JMP LC912 *--- CALLED BY $Cs00 LC973 LDA #$00 STA L0840+$1 STA L0840+$2 LC97B LDA #L0859 STA L080F+2 JSR LC9E9 BEQ LC97B LDA #$08 STA L0840+$7 LC98F JSR LC9E9 BEQ LC973 DEC L0840+$7 BNE LC98F LDA #$0A STA L0840+$8 LC99E JSR LC9E9 BEQ LC9B9 LDA #$0A STA L0840+$8 LC9A8 LDA L080F+2 ; end of buffer? CMP #>L0AB1 BNE LC99E LDA L080F+1 CMP #L0859 ROR ROR L0840+$15 ROR ROR L0840+$15 LDA L0840+$15 RTS *--------------------------------------------------------- LC9E9 LDY #$00 LDX L12F2 STY L0840+$9 LDA $C080,X EOR #$FF AND #$01 BEQ LCA0B STA L0840+$9 JMP LCA09 ds \ *--------------------------------------------------------- LCA09 LDA #$08 LCA0B JSR L080F LDA $C080,X EOR #$FF AND #$10 BEQ LCA1A STA L0840+$9 LCA1A LDA #$08 JSR L080F JSR LCA26 LDA L0840+$9 RTS LCA26 LDA #$C8 ; counter STA L0840+$A LDA #$00 STA L0840+$4 STA L0840+$5 LDA L0840+$1 STA L0840+$B LDA L0840+$2 STA L0840+$C LCA3F LDA $C080,X AND #$08 CMP L0840+$B BEQ LCA52 STA L0840+$B INC L0840+$4 LCA4F JMP LCA5B LCA52 LDA L0840+$4 JMP LCA58 LCA58 JMP LCA4F LCA5B LDA $C080,X AND #$80 CMP L0840+$C BEQ LCA6E STA L0840+$C INC L0840+$5 LCA6B JMP LCA77 LCA6E LDA L0840+$5 JMP LCA74 LCA74 JMP LCA6B LCA77 DEC L0840+$A BNE LCA3F LDA L0840+$4 CMP #$20 BCC LCA86 STA L0840+$9 LCA86 JSR L080F LDA L0840+$5 CMP #$50 BCC LCA90 LCA90 JSR L080F RTS LCA94 LDA #L0AF1 SBC #$00 STA L0800+2 LDA #$F4 STA L0840+$D LDA #$01 STA L0840+$E LDA #$20 STA L0840 LDY #$FF LCAB4 INY CPY #numWORDS BMI LCABA RTS LCABA LDA #wordSIZE ; next CLC ADC L0800+1 STA L0800+1 LDA #$00 ADC L0800+2 STA L0800+2 LDA L0820,Y BEQ LCAB4 LDA #$00 STA L0840+$F STA L0840+$10 LDX #wordSIZE-1 LCADA JSR L0800 SEC SBC L0AB1,X BCS LCAE7 EOR #$FF ADC #$01 LCAE7 CLC ADC L0840+$10 STA L0840+$10 LDA #$00 ADC L0840+$F STA L0840+$F CMP L0840+$D JMP LCB09 ds \ *--------------------------------------------------------- LCB09 BCC LCB15 BNE LCAB4 LDA L0840+$10 CMP L0840+$E BCS LCAB4 LCB15 DEX BMI LCB1B JMP LCADA LCB1B LDA L0840+$F STA L0840+$D LDA L0840+$10 STA L0840+$E STY L0840 JMP LCAB4 *--- CALLED BY $Cs00 LCB2D LDA #L0AB1 STA L080F+2 LDA L0840+$15 STA L0840+$13 LDA #$00 STA L0840+$11 LDA #$10 STA L0840+$12 JSR LCBA3 STA zpF0 STA zpF1 LDA L0840+$13 ASL ASL STA L0840+$16 BEQ LCB5B SEC SBC #$04 LCB5B CLC ADC #L0859 ADC #$00 STA L081B+2 LDA #$10 STA L12F5 LCB6D LDY #$00 LCB6F JSR L081B JSR L080F INY CPY #$04 BNE LCB6F LDA zpF0 CLC ADC zpF1 STA zpF0 SEC SBC #$10 BMI LCB8C STA zpF0 LDA #$04 BPL LCB8E LCB8C LDA #$00 LCB8E CLC ADC L0840+$16 ADC L081B+1 STA L081B+1 BCC LCB9D INC L081B+2 LCB9D DEC L12F5 BNE LCB6D RTS LCBA3 CLC LDX #$F7 LDA L0840+$11 LCBA9 ROL L0840+$13 INX BMI LCBB2 JMP LCBC7 LCBB2 ROL BCC LCBBB SBC L0840+$12 SEC BCS LCBA9 LCBBB SEC SBC L0840+$12 BCS LCBA9 ADC L0840+$12 CLC BCC LCBA9 LCBC7 STA L0840+$11 RTS *--------------------------------------------------------- L0800 LDA |$0000,X ; CODE AT $0800 RTS L0804 TAX L0805 LDA |$0000,X L0808 STA |$0000,X DEX BPL L0805 RTS L080F STA |$0000 INC L080F+1 BNE L081A INC L080F+2 L081A RTS L081B LDA |$0000,Y RTS ds \ *--------------------------------------------------------- * doPR * We simulate PR#slot and here doPR lda #0 bne doPRR ; restore ldx CSWL ; put our hook routine stx oldCSWL ldy CSWH sty oldCSWH ldx #LC900 tya ; A is not 0 doPRE stx CSWL ; save values sty CSWH sta doPR+1 ; flip/flop rts doPRR ldx oldCSWL ; restore old values ldy oldCSWH lda #0 beq doPRE oldCSWL ds 1 oldCSWH ds 1 *--------------------------------------------------------- * doIN * We simulate IN#slot and here doIN lda #0 bne doINR ; restore ldx KSWL ; put our hook routine stx oldKSWL ldy KSWH sty oldKSWH ldx #LC900 tya ; A is not 0 doINE stx KSWL ; save values sty KSWH sta doIN+1 ; flip/flop rts doINR ldx oldKSWL ; restore old values ldy oldKSWH lda #0 beq doINE oldKSWL ds 1 oldKSWH ds 1 *--------------------------------------------------------- *--- Buffers and friends ptrBUFFER ; address of the buffer L0840 ds $19 ; variables L0859 ds 600 ; read buffer ; the buffer is $258 (600d) bytes long L0AB1 ds wordSIZE ; a buffer ($40 bytes) L0AF1 ds numWORDS*wordSIZE ; a buffer ($800 bytes) L0820 ds numWORDS ; a table of numWORDS bytes (0: no word, 1: a word) L12F6 ds numWORDS+1 ; table of pointers low ($21 bytes) L1317 ds numWORDS+1 ; table of pointers high ($21 bytes) L1339 ds numWORDS*numWORDS ; a buffer for words (the words)