This is the circuit for the CPU part of the brain. For the sake of clarity I have not shown the decoupling capacitors across each IC or all the unused logic gates. Any unused logic needs to have it's inputs tied either high or low to avoid odd things happening in the circuit. Floating inputs are bad! I have also left off the details of the power supply. I used a LM2940 Low Dropout regulator to give me a stable 5 volt supply. The input can be from a wallplug pack or from 4 D cells. Remember I am not an electronic engineer so this circuit may not be optimal. It does work though!
It is based rather heavily on the original 1541 disk drive circuit. I simply took off the bits I didn't need such as the second VIA, second ROM and all the I/O circuitry and added my own clock and manual reset circuit. I also used a 27C64 EPROM. The original drive uses two 2364 devices which are impossible to get now.
One thing people may wonder about is the flip-flop between the CPU and clock generator on the right hand side of the diagram. That is a clock divider circuit which I had to use since the CPU I have is a 1Mhz device and all I could get locally were 2Mhz clock crystals!
Below is the circuit for the audio part of the brain. The circuit is a little convoluted I guess and there are definitely much easier ways to do this but I was making use of the parts I had easily available. Basically a timer drives a binary counter which then addresses bits out of whichever ROM chip is selected by the CPU at the time (I could have up to ten but I only used three). The top four bits of the counter (A14 - A17) are presettable by the CPU and these are set just before a play starts. Setting the address also resets the two lower bit counters back to zero. Using these top four bits means the memory is effectively split into pages with each page start being addressable using these bits. The samples are stored starting on a page boundary so the CPU can start playing at the beginning of each sample. The samples may be longer than one page in length. To detect when to stop playing I encoded at the end of each sample the maximum 8 bit value, 255, and I detect when all the output bits go high with an 8 input NAND logic gate. This causes and interrupt signal to the VIA which then interrupts the processor which knows it should stop playing. The audio is converted from digital to analogue with a simple R-2R ladder. This provides good enough audio quality for the simple speech I am playing. I use a LM386 on the audio board as a small amplifier. The clock is a simple 555 timer based circuit which runs all the time. The clock signal is gated to the rest of the circuit by the CPU. When a play finishes the clock signal is switched off stopping the play and we simply wait for another play trigger input.
Below is the 6502 assembly language. Again I am not a professional software engineer. Actually, I am, but not in 6502 assembly language. This was my test code and first real 6502 assembly language program so again it might not be efficient or pretty but it does the job. I did that thing all software engineers will know about. I said I'll do a prototype then rewrite it once I know how it works. Of course we'll never ship with that prototype code.....
rom_start = $E000 ; Start of ROM memory
reset = $FFFC ; Reset vector
interrupt = $FFFE ; Interrupt vector
via = $1800
via_b = via + $0 ; VIA port b
via_a = via + $1 ; VIA port a
via_bdir = via + $2 ; VIA port b direction 1 = output
via_adir = via + $3 ; VIA port a direction 1 = output
via_pcr = via + $C ; VIA peripheral control register
via_ier = via + $E ; VIA interrupt enable register
chipsam = $07FE ; Store the chip and sample to play
playing = $07FF ; Are we currently playing flag
.ORG rom_start
;Initilisation
LDA #$FE ; Load ACC with b11111110
STA via_bdir ; Set VIA port b all output except b0 input
LDA #$FF ; Load ACC with b11111111
STA via_adir ; Set VIA port a all output
LDA #$CC ; Load ACC with b11001100 - PCR
STA via_pcr ; Set VIA PCR for CA2, CB2 held low, CA1, CB1 high->low transition
LDA #$82 ; Load ACC with b10000010 - IER
STA via_ier ; Set VIA IER for CA1 active
LDA #$00 ; Clear the playing flag
STA playing
CLI ; Enable interrupts
; Simply loop around storing each samples location in memory.
; Then poll to see if we were triggered to start playing.
loop
c1s1 LDA #$00 ; Data b0000 0000 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 0000
JSR poll
c1s2 LDA #$02 ; Data b0000 0010 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 0010
JSR poll
c1s3 LDA #$06 ; Data b0000 0110 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 0110
JSR poll
c1s4 LDA #$07 ; Data b0000 0111 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 0111
JSR poll
c1s5 LDA #$08 ; Data b0000 1000 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 1000
JSR poll
c1s6 LDA #$0A ; Data b0000 1010 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 1010
JSR poll
c1s7 LDA #$0C ; Data b0000 1100 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 1, address 1100
JSR poll
c2s1 LDA #$10 ; Data b0001 0000 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 2, address 0000
JSR poll
c2s2 LDA #$15 ; Data b0001 0101 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 2, address 0101
JSR poll
c2s3 LDA #$17 ; Data b0001 0111 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 2, address 0111
JSR poll
c2s4 LDA #$19 ; Data b0001 1001 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 2, address 1001
JSR poll
c2s5 LDA #$1D ; Data b0001 1101 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 2, address 1101
JSR poll
c3s1 LDA #$20 ; Data b0010 0000 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 0000
JSR poll
c3s2 LDA #$23 ; Data b0010 0011 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 0011
JSR poll
c3s3 LDA #$25 ; Data b0010 0101 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 0101
JSR poll
c3s4 LDA #$2B ; Data b0010 1011 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 1011
JSR poll
c3s5 LDA #$2D ; Data b0010 1101 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 1101
JSR poll
c3s6 LDA #$2E ; Data b0010 1110 - CHIP SELECT/ADDRESS
STA chipsam ; Store data - chip 3, address 1110
JSR poll
JMP loop
; Polling subroutine. In here we check if the button is pressed (portb 0 == 0)
; and if so start playing the currently stored sample.
poll
LDA playing ; Check if we are playing
AND #$01 ; 1 == playing
BNE return ; We are playing so just keep looping
LDA via_b ; Not playing so check VIA port b
AND #$01 ; Check portb 0 == 1 (button NOT pushed)
BEQ play ; No button is down, start playing
JMP return ; Button NOT pressed so return
play
LDA chipsam ; Retrieve the chip and sample data
STA via_a ; Push it to the VIA port a
LDA #$40 ; Data b0100 0000 - Reset and load
STA via_b ; Load data on port b
LDA #$80 ; Data b1000 0000 - Start playing
STA via_b ; Load data on port b
LDA #$01 ; Set the playing flag
STA playing
LDA #$82 ; Load ACC with b10000010 - IER
STA via_ier ; Set VIA IER for CA1 interrupt active
return
RTS ; Return
; Interrupt service routine. this is triggered when the 6522 VIA interrupts
; at the end of a sample. We store the ACC, clear the interrupt. We reset the
; audio board by clearing port a, resetting the counters and stopping the play.
; Then reset the playing flag, restore the ACC and continue.
isr
PHA ; Store the accumulator
LDA #$02 ; Load ACC with b00000000 - IER
STA via_ier ; Clear VIA IER for CA1
LDA #$00 ; Reset port back to zero
STA via_a ; Push it to the VIA port a
LDA #$40 ; Data b0100 0000 - Reset and load
STA via_b ; Load data on port b
LDA #$00 ; Data b0000 0000 - Stop playing
STA via_b ; Load data on port b
LDA #$00 ; Clear the playing flag
STA playing
PLA ; Retrieve the accumulator
RTI ; Return back to looping through samples
.ORG reset ; Reset vector
.WORD rom_start
.ORG interrupt ; Interrupt vector
.WORD isr