		; **********************
		; **                  **
		; ** SERIAL I/O INIT  **
		; **                  **
		; **********************

		; SIO initialization subroutine
	
SIOINT:		lda	#%00111100
		sta	PACTL		; turn off motor
		lda	#%00111100
		sta	PBCTL		; raise not command line
		lda	#3
		sta	SSKCTL		; get POKEY out of initialize mode
		sta	SOUNDR		; init poke address for quiet I/O
		sta	SKCTL
		rts

		; ***********************
		; **                   **
		; ** SIO MAIN ROUTINE  **
		; **                   **
		; ***********************

E971:		tsx
		stx	STACKP		; save stack pointer
		lda	#1
		sta	CRITIC
		lda	DDEVIC
		cmp	#$60
		bne	@1		; branch if not cassette
		jmp	CASENT		; otherwise jump to cassette enter

		; all devices except cassette are intelligent
	
@1:		lda	#0
		sta	CASFLG		; init cassette flag to no cassette
	
		lda	#1		; set number of device retries
		sta	DRETRY
@2:		lda	#13		; set number of command frame retries
		sta	CRETRY

		; send a command frame
	
@3:		lda	#$28		; set baud rate to 19200
		sta	$D204
		lda	#0
		sta	$D206
	
		clc			; set up command buffer
		lda	DDEVIC
		adc	DUNIT
		adc	#$FF		; subtract 1
		sta	CDEVIC		; set bus ID number
	
		lda	DCOMND		; set bus command
		sta	CCOMND
	
		lda	DAUX1		; store command frame aux bytes 1 and 2
		sta	CAUX1
		lda	DAUX2
		sta	CAUX2		; done setting up command buffer
	
		clc			; set buffer pointer to cmd frame buf
		lda	#$3A
		sta	BUFRLO		; and buffer end address
		adc	#4
		sta	BFENLO
		lda	#2
		sta	BUFRHI
		sta	BFENHI		; done setting up buffer pointer
	
		lda	#%00110100	; lower not command line
		sta	PBCTL
	
		jsr	SENDIN		; send command frame to a smart device
	
		lda	ERRFLG
		bne	@4		; branch if an error received
	
		tya
		bne	@5		; branch if ACK received
	
@4:		dec	CRETRY		; NACK or TIME OUT occured
		bpl	@3		; so branch if any retries left 

		jmp	@10		; otherwise, jump to return section
	
@5:		lda	DSTATS		; new stuff...
		bpl	@6

		; send a data frame to a peripheral
	
		lda	#13		; set number of retries
		sta	CRETRY
		jsr	LDPNTR		; load buffer pointer with DCB info
		jsr	SENDIN		; send data frame to smart device
		beq	@10		; branch if bad

		; wait for complete signal from peripheral
	
@6:		jsr	STTMOT		; set device time out values in Y,X
	
		lda	#0
		sta	ERRFLG		; clear error flag
	
		jsr	WAITER		; set up timer and wait
		beq	@8		; branch if time out

		; device did not time out
	 
		bit	DSTATS
		bvs	@7		; branch if more data follows
	
		lda	ERRFLG
		bne	@10		; branch if an error occured
		beq	EA2A		; otherwise return

		; receive a data frame from peripheral
	
@7:		jsr	LDPNTR		; load buffer pointer with DCB info
		jsr	RECEIV		; go receive a data frame
@8:		lda	ERRFLG
		beq	@9		; branch if no error preceeded data
	
		lda	TSTAT		; get temp status
		sta	STATUS		; store in real status
	
@9:		lda	STATUS
		cmp	#1
		beq	EA2A		; branch if completely successful
@10:		dec	DRETRY
		bmi	EA2A		; branch if out of device retries
		jmp	@2		; otherwise, one more time

EA2A:		jsr	SENDDS		; disable POKEY interrupts
		lda	#0
		sta	CRITIC
		ldy	STATUS		; return status in Y
		sty	DSTATS		; and the DCB status word
		rts

		; wait subroutine
		; waits for COMPLETE or ACK
		; returns Y=$FF if successfull, Y=$00 if not

WAIT:		lda	#0
		sta	ERRFLG		; clear error flag
	
		clc			; load buffer pointer with address
		lda	#$3E		; of temporary RAM cell
		sta	BUFRLO
		adc	#1
		sta	BFENLO		; also set buffer end +1 address
		lda	#2
		sta	BUFRHI
		sta	BFENHI		; done loading pointer
	
		lda	#$FF
		sta	NOCKSM		; set no checksum follows data flag
	
		jsr	RECEIV		; go receive a byte
	
		ldy	#$FF		; assume success
		lda	STATUS
		cmp	#1
		bne	@2		; branch if it did not work ok
	
		lda	TEMP		; make sure the byte successfully rec'd
		cmp	#'A'		; was actually an ACK or COMPLETE
		beq	@4
		cmp	#'C'
		beq	@4
	
		cmp	#'E'		; branch if device did not send back
		bne	@1
	
		lda	#144
		sta	STATUS		; set device error status
		bne	@2
	
@1:		lda	#139		; otherwise set NACK status
		sta	STATUS
	
@2:		lda	STATUS
		cmp	#138
		beq	@3		; branch if time out
	
		lda	#$FF
		sta	ERRFLG		; set some error flag 
		bne	@4		; return with out setting Y = 0
	
@3:		ldy	#0
	
@4:		lda	STATUS
		sta	TSTAT
		rts			; return

		; send subroutine
		; sends a buffer of bytes out over the serial bus

SEND:		lda	#1		; assume success 
		sta	STATUS
	
		jsr	SENDEN		; enable sending
	
		ldy	#0
		sty	CHKSUM		; clear check sum
		sty	CHKSNT		; checksum sent flag
		sty	XMTDON		; transmission done flag
	
		lda	(BUFRLO),Y	; put first byte from buffer

		.if	(OS_REVISION<=2)
	
		sta	SEROUT		; into the serial output register	
		sta	CHKSUM		; put it in checksum

		.else
	
		sta	CHKSUM		; put it in checksum
		sta	SEROUT		; into the serial output register	

		.endif
	
@1:		lda	BRKKEY
		bne	@2
		jmp	BROKE		; jump if break key pressed
	
@2:		lda	XMTDON		; loop until transmission done
		beq	@1
		jsr	SENDDS		; disable sending
		rts			; return

		; output data needed interrupt service routine
	
ISRODN:		tya
		pha			; save Y reg on stack

		inc	BUFRLO		; increment buffer pointer
		bne	@1
		inc	BUFRHI
	
@1:		lda	BUFRLO		; check if past end buffer
		cmp	BFENLO
		lda	BUFRHI		; high part
		sbc	BFENHI
		bcc	@4		; branch if not pas end of buffer
	
		lda	CHKSNT
		bne	@2
	
		lda	CHKSUM
		sta	SEROUT		; send check sum
		lda	#$FF
		sta	CHKSNT		; set checksum sent flag
		bne	@3
	
@2:		lda	POKMSK		; enable transmit done interrupt
		ora	#%00001000
		sta	POKMSK
		sta	IRQEN
	
@3:		pla
		tay			; restore Y reg
		pla			; return from interrupt
		rti

@4:		ldy	#0
		lda	(BUFRLO),Y	; put next byte from buffer
		sta	SEROUT		; into the serial output register
	
		clc
		adc	CHKSUM		; add it to checksum
		adc	#0
		sta	CHKSUM
	
		jmp	@3		; go return

		; transmit done interrupt service routine
	
ISRTD:		lda	CHKSNT
		beq	@1		; branch if checksum not yet sent
	
		sta	XMTDON		; otherwise set transmission done flag
	
		lda	POKMSK		; disable transmit done interrupt
		and	#%11110111
		sta	POKMSK
		sta	IRQEN
	
@1:		pla			; return from interrupt
		rti

RECEIV:		lda	#0
	
		ldy	CASFLG		; branch if cassette
		bne	@1
	
		sta	CHKSUM		; clear checksum
	
@1:		sta	BUFRFL		; buffer full flag
		sta	RECVDN		; receive done flag
	
		lda	#1
		sta	STATUS		; set good status for default case
		jsr	RECVEN		; do receive enable
		lda	#%00111100	; command frame hi command
		sta	PBCTL		; store in PIA
@2:		lda	BRKKEY
		bne	@3
		jmp	BROKE		; jump if break key pressed
	
@3:	 	lda	TIMFLG		; no,
		beq	TOUT		; if timeout, go set error status
		lda	RECVDN
		beq	@2		; done?
		rts
	
TOUT:		lda	#138		; yes,
		sta	STATUS		; set timeout status
		rts

		.if	(OS_REVISION=0)
		.res	9,0
		jmp	$C28B
		.res	20,0
		.endif

ISRSIR:		tya
		pha			; save Y reg on stack
	
		lda	SKSTAT
		sta	SKRES		; reset status register
		bmi	@1		; branch if no framing error
		ldy	#140
		sty	STATUS		; set frame error status
	
@1:		and	#%00100000
		bne	@2		; branch if no overrun error
	
		ldy	#142
		sty	STATUS		; set overrun error status
	
@2:		lda	BUFRFL
		beq	@5		; branch if buffer was not yet filled
	
		lda	SERIN		; this input byte is the checksum
		cmp	CHKSUM
		beq	@3		; branch if checksums match
	
		ldy	#143
		sty	STATUS		; set checksum error status
	
@3:		lda	#$FF
		sta	RECVDN		; set receive done flag
	
@4:		pla
		tay			; restore Y reg
		pla			; return from interrupt
		rti

		.if	(OS_REVISION=0)
		.res	3,0
		jmp	$E74E
		.res	26,0
		.endif
	
@5:		lda	SERIN
		ldy	#0
		sta	(BUFRLO),Y	; store input register into buffer
	
		clc			; add it to checksum
		adc	CHKSUM
		adc	#0
		sta	CHKSUM
	
		inc	BUFRLO		; increment buffer pointer
		bne	@6
		inc	BUFRHI
	
@6:		lda	BUFRLO
		cmp	BFENLO
		lda	BUFRHI
		sbc	BFENHI
		bcc	@4		; branch if new bufadr is in buffer L
	
		lda	NOCKSM
		beq	@7		; branch if a checksum will follow data
	
		lda	#0
		sta	NOCKSM		; clear no checksum flag
	
		beq	@3		; go return and set receive done flag
	
@7:		lda	#$FF
		sta	BUFRFL		; set buffer full flag
	
		bne	@4		; go return

		; load buffer pointer subroutine
		; load DCB buffer pointer with DCB buffer information
	
LDPNTR:		clc
		lda	DBUFLO
		sta	BUFRLO
		adc	DBYTLO
		sta	BFENLO		; also set buffer end + 1 address
	
		lda	DBUFHI
		sta	BUFRHI
		adc	DBYTHI
		sta	BFENHI
	
		rts			; return

		; cassette handling code
	
CASENT:		lda	DSTATS
		bpl	@3		; branch if input from cassette
	
		lda	#<1484		; set baud rate to 600
		sta	AUDF3
		lda	#>1484
		sta	AUDF4
	
		jsr	SENDEN		; turn on POKEY mark tone
	
		ldx	PALNTS
		ldy	NTSCPAL+4,X	; load short write interrecord gap time
		lda	DAUX2
		bmi	@1		; branch if short gap is desired
		ldy	NTSCPAL,X
	
@1:		ldx	#0		; set write IRG time
		jsr	SETVBX
	
		lda	#%00110100
		sta	PACTL		; turn on motor
@2:		lda	TIMFLG		; loop until done
		bne	@2
		jsr	LDPNTR		; load buffer pointer with DCB info
		jsr	SEND		; send a buffer
		jmp	@6		; go return

		; receive a record
	
@3:		lda	#$FF
		sta	CASFLG		; set cassette flag
	
		ldx	PALNTS
		ldy	NTSCPAL+6,X	; load short read interrecord gap time
		lda	DAUX2
		bmi	@4		; branch if short gap is desired
		ldy	NTSCPAL+2,X
@4:		ldx	#0
		jsr	SETVBX
	
		lda	#%00110100
		sta	PACTL		; turn on motor
	
@5:		lda	TIMFLG		; loop until done
		bne	@5
	
		jsr	LDPNTR		; load buffer pointer with DCB info
	
		jsr	STTMOT		; set device time out in Y,X
		jsr	SETVBX
	
		jsr	BEGIN		; set initial baud rate
	
		jsr	RECEIV		; go receive a block
	
@6:		lda	DAUX2
		bmi	@7		; branch if doing short IRG's

		lda	#%00111100
		sta	PACTL		; turn off motor
@7:		jmp	EA2A

JTIMER:		lda	#0
		sta	TIMFLG		; set time out flag
		rts

		; send enable subroutine
	
SENDEN:		lda	#%00000111	; mask off prev serial bus control bits
		and	SSKCTL
		ora	#%00100000	; set transmit mode
	
		ldy	DDEVIC
		cpy	#$60
		bne	@1		; branch if not cassette
	
		ora	#%00001000	; set the FSK output bit
	
		ldy	#7		; set FSK tome frequencies
		sty	AUDF2
		ldy	#5
		sty	AUDF1
	
@1:		sta	SSKCTL		; store new value to system mask
		sta	SKCTL		; store to actual register
	
		lda	#%11000111	; mask off prev serial bus intr bits
		and	POKMSK
		ora	#%00010000	; enable output data needed interrupt
	
		jmp	EC56		; continue in receive enable subroutine

		; receive enable subroutine
	
RECVEN:		lda	#%00000111	; mask off prev serial bus control bits
		and	SSKCTL
		ora	#%00010000	; set receive mode asynchronous
		sta	SSKCTL		; store new value to system mask
		sta	SKCTL		; store to actual register
	
		sta	SKRES		; reset serial port/keyboard status reg
	
		lda	#%11000111	; mask off prev serial bus intr bits
		and	POKMSK
		ora	#%00100000	; enable receive interrupt

EC56:		sta	POKMSK		; store new value to system mask
		sta	IRQEN		; store to actual register
	
		lda	#%00101000	; clock CH@3 with 1@79 MHz
		sta	AUDCTL		; clock CH@4 with CH@3
	
		ldx	#6		; set pure tones, no volume
		lda	#%10101000
		ldy	SOUNDR		; test quiet I/O flag
		bne	@1		; NE is normal (noisy)
	
		lda	#%10100000
@1:		sta	AUDC1,X
		dex
		dex
		bpl	@1
	
		lda	#%10100000
		sta	AUDC3		; turn off sound on channel 3
		ldy	DDEVIC
		cpy	#$60
		beq	@2		; branch if cassette is desired
		sta	AUDC1		; otherwise turn off channels 1 and 2
		sta	AUDC2
	
@2:		rts			; return

SENDDS:		nop			; ?huh?
	
		lda	#%11000111	; mask off serial bus interrupts
		and	POKMSK
		sta	POKMSK		; store new value to system mask
		sta	IRQEN		; store to actual register
	
		ldx	#6
		lda	#0
@1:		sta	AUDC1,X
		dex
		dex
		bpl	@1		; turn off audio volume
		rts			; return

		; set device time-out values in Y,X subroutine
	
STTMOT:		lda	DTIMLO		; get device time out in 1 second incr
		ror			; put 6 hi bits in X, lo 2 bits in Y
		ror	
		tay			; temp save
		and	#%00111111	; mask off hi 2 bits
		tax			; this is hi byte of time out
	
		tya
		ror
		and	#%11000000	; mask off all but 2 hi bits
		tay			; this is lo byte of time out
	
		rts

		; these are called INTTBL in the OS source, but are not used!

		.word	ISRSIR		; serial input ready
		.word	ISRODN		; output data needed
		.word	ISRTD		; transmission done

		; send a data frame to an intelligent peripheral subroutine

SENDIN:		ldx	#1		; wait a short time...
@1:		ldy	#$FF
@2:		dey
		bne	@2
		dex
		bne	@1
	
		jsr	SEND		; go send the data frame
	
		ldy	#2		; set ack time out
		ldx	#0
WAITER:		jsr	SETVBX
	
		jsr	WAIT		; wait for ack
	
		tya			; if Y=0, a TIME OUT or NACK occured
	
		rts			; return

		; compute value for POKEY freq regs for the baud rate as
		; measured by an interval of the 'VCOUNT' timer.
	
COMPUT:		sta	TIMER2
		sty	TIMER2+1	; save final timer value
		jsr	ADJUST		; adjust VCOUNT value
		sta	TIMER2		; save adjusted value
		lda	TIMER1
		jsr	ADJUST		; adjust
		sta	TIMER1		; save adjusted TIMER1 value
		lda	TIMER2
		sec
		sbc	TIMER1
		sta	TEMP1		; find VCOUNT difference
		lda	TIMER2+1
		sec
		sbc	TIMER1+1
		tay			; find VBLANK count difference
		ldx	PALNTS
		lda	#0
		sec
		sbc	NTSCPAL+8,X
@1:		clc
		adc	NTSCPAL+8,X	; accumulate multiplication
		dey
		bpl	@1		; done?
		clc
		adc	TEMP1		; total VCOUNT difference
		tay			; save accum
		lsr
		lsr
		lsr
		asl
		sec
		sbc	#22		; adjust table index
		tax			; div interval by 4 to get table index
		tya			; restore accum
		and	#%00000111
		tay			; pull off 3 lo bits of interval
		lda	#$F5	        ;  -11
@2:		clc
		adc	#11		; accumulate interpolation constant
		dey
		bpl	@2		; interp constant computation done?
	
		ldy	#0
		sec
		sbc	#7		; adjust interpolation constant
		bpl	@3
		dey
@3:		clc
		adc	TPFV,X		; add constant to lo byte table value
		sta	CBAUDL
		tya
		adc	TPFV+1,X	; add carry to hi byte table value
		sta	CBAUDH
		rts

ADJUST:		cmp	#$7C
		bmi	@1		; larger than '7C' ?
		sec			; yes
		sbc	#$7C
		rts
@1:		clc
		ldx	PALNTS
		adc	NTSCPAL+10,X
		rts

		; initial baud rate measurement -- used to set the
		; baud rate at the start of a record
		; it is assumed that the first two bytes of every record are 'AA' hex
	
BEGIN:		lda	BRKKEY
		bne	@1
		jmp	BROKE		; jump if break key pressed
	
@1:		sei
	
		lda	TIMFLG
		bne	@2		; branch if not timed out
		beq	@4		; branch if time out
	
@2:		lda	SKSTAT
		and	#%00010000	; read serial port
		bne	BEGIN		; start bit?
		sta	SAVIO		; save ser. data in
		ldx	VCOUNT		; read vertical line counter
		ldy	RTCLOK+2	; read lo byte of VBLANK counter
		stx	TIMER1
		sty	TIMER1+1	; save initial timer value
	
		ldx	#1		; set mode flag
		stx	TEMP3
		ldy	#10		; set bit counter for 10 bits
@3:		lda	BRKKEY
		beq	BROKE		; branch if break key pressed
	
		lda	TIMFLG
		bne	@5		; branch if not timed out
@4:		cli
		jmp	TOUT		; branch if time out

@5:		lda	SKSTAT
		and	#%00010000	; read serial port
		cmp	SAVIO		; data in changed yet?
		beq	@3
		sta	SAVIO		; yes, save ser. data in
		dey			; decr. bit counter
		bne	@3		; done?
	
		dec	TEMP3		; yes,
		bmi	@6		; done with both modes?
		lda	VCOUNT
		ldy	RTCLOK+2	; read timer lo & hi bytes
		jsr	COMPUT		; no, compute baud rate
		ldy	#9		; set bit counter for 9 bits
		bne	@3

@6:		lda	CBAUDL
		sta	AUDF3
		lda	CBAUDH
		sta	AUDF4		; set POKEY freq regs for baud rate
		lda	#0
		sta	SKCTL
		lda	SSKCTL
		sta	SKCTL		; init POKEY serial port
		lda	#%01010101
		sta	(BUFRLO),Y	; store '$55' as first rcv. buffer
		iny
		sta	(BUFRLO),Y
		lda	#%10101010
		sta	CHKSUM		; store checksum for 2 bytes of '$AA'
		clc
		lda	BUFRLO
		adc	#2
		sta	BUFRLO
		lda	BUFRHI
		adc	#0
		sta	BUFRHI		; incr. buffer pointer by 1
		cli
		rts

BROKE:		jsr	SENDDS		; break key was pressed, so prepare
		lda	#%00111100	; to return
		sta	PACTL		; turn off motor
		lda	#%00111100
		sta	PBCTL		; raise not command line
	
		lda	#128
		sta	STATUS		; store break abort status code
	
		ldx	STACKP
		txs			; restore stack pointer
	
		dec	BRKKEY		; set break key flag to nonzero
		cli			; allow IRQ's
	
		jmp	EA2A		; go return

SETVBX:		lda	#<JTIMER	; store time out routine address
		sta	CDTMA1
		lda	#>JTIMER
		sta	CDTMA1+1
	
		lda	#1		; set for timer 1
	
		sei			; the SETVBL needs this to cut short
		jsr	SETVBV		; any VBLANKs that occur
		lda	#1		; set for timer 2
		sta	TIMFLG		; set flag to not timed out
		cli
		rts

		; 'VCOUNT' interval timer measurement -- TO -- POKEY freq reg value
		;               conversion table
		; The values stored in the table are 'AUDF+7'
		; The followinf formulas were used to determine the table values:
		; F OUT = F IN/(2*(AUDF+M)) , where F IN=1@78979 MHz & M=7
		; From this was derived a formula used to compute the table values
		; based on a measurement of the period by an interval of the VCOUNT
		; timer.
		;   AUDF+7=(11@365167)*T OUT, where T OUT=# of counts
		;   (127 usec. resolution) of VCOUNT for 1 character time
		;   (10 bit times).

		;       AUDF+7		  BAUD RATE	VCOUNT INTERVAL
		;       ------            -----------   ----------------
TPFV:		.word	$3E8		;  895          88
		.word	$443		;  820          96
		.word	$49E		;  757          104
		.word	$4F9		;  703          112
		.word	$554		;  656          120
		.word	$5AF		;  615          128
		.word	$60A		;  579          136
		.word	$665		;  547          144
		.word	$6C0		;  518          152
		.word	$71A		;  492          160
		.word	$775		;  469          168
		.word	$7D0		;  447          176

		; PAL / NTSC timing values

NTSCPAL:	.byte	$B4,$96
		.byte	$78,$64
		.byte	$0F,$0D
		.byte	$0A,$08
		.byte	$83,$9C
		.byte	$07,$20
