That Apple II sure has lousy sound. And no interrupts. Let's interface those two awesome aspects of the Commodore series with our Apples! Thanks Sark for the great idea and the hardware knowhow!
Let's see if we can talk to the sid first. Whipping up a quick BASIC program should pose no particular challenge..
5 S = 50176
10 FOR N = S TO S + 24: POKE N,0: NEXT N
20 POKE S+24,15
30 FOR T = 1 TO 1000: NEXT
35 F = 0
40 POKE S + 1,F
50 POKE S + 5,12
60 POKE S + 6,4
70 POKE S + 4, 33
70 F = F + 1
80 FOR X = 1 TO 1400: NEXT
90 POKE S + 4,0
100 GOTO 40
That program will play a whole range of tones, incrementing the frequency high byte for oscillator 1 each iteration
Line 10 clears all the SID registers - shouldn't need that, since they're suppoed to be cleared when you reset
line 70 turns on the triangle wave and opens the gate
line 90 shuts the gate off
line 30 is in there because there is this popping noise when you change the volume on the SID, and i wanted it to be away from the tones
Testing the CIA means doing tricky things with interrupts. So before we get into that, let us consult http://support.apple.com/kb/TA28361?viewlocale=en_US and give ourselves a quick primer on Apple II interrupts (in BASIC using ROM routines). They have a quick and simple program to demonstrate how to combine a machine language routine and the ONERR statement to modify the execution of an Applesoft program when the interrupt occurs.
10 POKE 800,162: POKE 801,100: POKE 802,104: POKE 803,104
20 POKE 804,104: POKE 805,76 : POKE 806,233: POKE 807,242
30 POKE 1022,0 : POKE 1023,8
40 ONERR GOTO 1000
50 PRINT "NO INTERRUPT"
60 GOTO 50
1000 IF PEEK (222) <> 100 THEN END
1010 PRINT "INTERRUPT!!!"
1020 RESUME
The POKES at lines 10 and 20 deserves some additional explanation. http://en.wikipedia.org/wiki/Interrupts_in_65xx_processors tells us all we need to know about the 6502's behavior during an interrupt. The stack gets these 3 bytes pushed onto it in order: high PC register, low PC register, status register. (Unknown whether or not the interrupt bit is set when it is pushed. Does it matter?) (Why is it sending to 0800
800 / $0320: LDX #$64 ;; load 100 decimal into x
802 / $0322: PLA ;; pop saved status register from stack
803 / $0323: PLA ;; pop low byte of pc register
804 / $0324: PLA ;; pop hi byte
805 / $0325: JMP $F2E9 ;; continue to applesoft error handling routine?
1022 / $03FE: $00 ;; low byte of interrupt routine
1023 / $03FF: $08 ;; high byte of interrupt routine
Let's turn on some interrupts and test it out.
1 T = 0
5 GOSUB 5000
8 POKE 798,88: POKE 799,96
9 CALL 798
10 POKE 800,162: POKE 801,100: POKE 802,104: POKE 803,104
20 POKE 804,104: POKE 805,76 : POKE 806,233: POKE 807,242
30 POKE 1022,0 : POKE 1023,8
40 ONERR GOTO 1000
50 PRINT "NO INTERRUPT"
60 GOTO 50
1000 IF PEEK (222) <> 100 THEN GOTO 2000
1010 PRINT "INTERRUPT!!!"
1012 T = T + 1:IF T > 10 GOTO 6000
1020 RESUME
2000 PRINT "ONERR GOTO REACHED.. PEEK(222) == ";
2010 PRINT PEEK (222)
2020 END
5000 REM RESET CIA, TURN ON INTERRUPTS
5010 S = 49344
5020 POKE S + 5,0 : rem poke 0 into timer high register
5030 POKE S + 4,255: rem poke 255 into timer low register
5040 POKE S + 15, 33: rem change control register A
5050 RETURN
6000 REM GOT TEN INTERRUPTS, TURN THEM OFF
6010 POKE S + 15, 32: rem turn off timer
6020 GOTO 50
This time we'll try using the SSC in slot #1 to generate transmit interrupts. UPDATE Holy Shit, This Works!!!! =D
300:A2 10 A9 11 9D 8B C0 A9 05 9D 8A C0 58 9D 88 C0
310:A9 AE 20 F0 FD AD 00 C0 10 F6 A2 10 A9 00 9D 8A
320:C0 8D 10 C0 78 60 A9 23 20 F0 FD AD 99 C0 8D 98
330:C0 A5 45 40
3FE: 26 03
(code below)
;; turn on interrupts
300- A2 10 LDX #10 ; ssc in slot 1
302- A9 11 LDA #11 ; set baud rate to 50 baud
304- 9D 8B C0 STA C08B,X
307- A9 05 LDA #5 ; dtr on, xmit interrupt enabled 00000101
309- 9D 8A C0 STA C08A,X
30C- 58 CLI ; clear interrupt mask, allow 6502 to receive intr
30D- 9D 88 C0 sta c088,X ; prime transmitter by inserting a byte??
;; print dots..
310- A9 AE LDA #AE ; load a period
312- 20 F0 FD JSR FDF0 ; print it
315- AD 00 C0 LDA C000 ; check keyboard
318- 10 F6 BPL 0310 ; continue if not pressed
;; received keypress, disable interrupts CLEAR KBD STROBE and exit
31A- A2 10 ldx #10 ; again, ssc in slot 1
31C- A9 00 LDA #0 ; reset SSC
31E- 9D 8A C0 STA C08A,X
321- 8D 10 C0 sta c010 ; clear kbd strobe
324- 78 sei ; disable interrupts
325- 60 RTS ; return/end
; interrupt handler (print not a dot)
326- A9 23 LDA #23 ; inverse #
328- 20 F0 FD JSR $FDF0 ; print it
32B- AD 99 C0 LDA C099 ; load status register, to clear irq??
32E- 8D 98 C0 STA c098 ; 'got xmit irq so fill transmitter?'
331- A5 45 LDA $45 ; restore accumulator
333- 40 RTI
3FE: 26 03 ; set $03FE / $03FF to our interrupt handler
changing slots:
make these n0 : 301 318
hardcoded: 329 (n9) 32c (n8)
Now that we know that this code works, let's replace the interrupt toggling code with CIA-specifics.
300:A9 00 8D C5 C0 A9 FF 8D C4 C0 A9 01 8D CE C0 EA
310:EA EA EA EA EA EA EA EA EA 58 A9 AE 20 F0 FD AD
320:00 C0 10 F6 A9 00 8D CE C0 8D 10 C0 78 60 A9 23
330:20 F0 FD AD CD C0 EA EA EA EA EA EA EA A5 45 40
3FE: 2E 03 ; set $03FE / $03FF to our interrupt handler
;; turn on CIA timer A interrupt
300- A9 00 LDA #0
302- 8D C5 C0 STA C0C5 ; timer A high
305- A9 FF LDA #FF
307- 8D C4 C0 STA C0C4 ; timer A low
30A- A9 01 LDA #01
30C- 8D CE C0 STA C0CE ; control register A
30F- A9 81 LDA #81
311- 8D CD C0 STA C0CD ; set MASK
314- EA NOP
315- EA NOP
316- EA NOP
317- EA NOP
318- EA NOP
319- 58 CLI ; clear interrupt mask, allow 6502 to receive intr
;; print dots..
31A- A9 AE LDA #AE ; load a period
31C- 20 F0 FD JSR FDF0 ; print it
31F- AD 00 C0 LDA C000 ; check keyboard
322- 10 F6 BPL 0E1A ; continue if not pressed
;; received keypress, disable interrupts CLEAR KBD STROBE and exit
324- A9 00 LDA #0 ; reset CIA?
326- 8D CE C0 STA C0CE ; control register A
329- 8D 10 C0 sta c010 ; clear kbd strobe
32C- 78 sei ; disable interrupts
32D- 60 RTS ; return/end
; interrupt handler (print not a dot), read/reset interrupt
32E- A9 23 LDA #23 ; inverse #
330- 20 F0 FD JSR $FDF0 ; print it
333- AD CD C0 LDA C0CD ; read from INTR CTRL REG - toss result but required
336- EA NOP ; to reset interrupt!!!!
337- EA NOP
338- EA NOP
339- EA NOP
33A- EA NOP
33B- EA NOP
33C- EA NOP
33D- A5 45 LDA $45 ; restore accumulator
33F- 40 RTI
3FE: 2E 03 ; set $03FE / $03FF to our interrupt handler
Success! The CIA generates interrupts. Not exactly clear on the correlation between timer HIGH/LOW byte and actual millisecond timing - apparently it counts phi-0 clock pulses from the Apple II bus. Let's figure out the math!
Stay tuned for part TWO of this series. These URLs look promising:
http://sid.kubarth.com/ http://sidplayer.cebix.net/