;************************************************************************
;*	    Super Famicom SCC ( Z8530 ) driver   Version 1.00		*
;*		      Programmed by Y.Nishida				*
;*						[ Aug.23, 1991 ]	*
;************************************************************************

		native
		extend

		include		PPUregs.h
		include		CPUregs.h
		include		sfxdos.h

SCC_Driver	sect		rel
SFXDOS		group		SCC_Driver

		dpage		DOS_variable

;=============== Cross Definition =======================================

		global		Init_SCC_Driver		; Initial SCC(Z8530) driver
		global		Enable_SCC_Int		; Enable SCC interrupt
		global		Disable_SCC_Int		; Disable SCC interrupt
;------------------------------------------------------------------------
		global		Flush_Keyboard		; Flush keyboard buffer
		global		Input_Keyboard		; Input keyboard
		global		Output_Keyboard		; Output keyboard
		global		Sense_Keyboard		; Input sense keyboard
;------------------------------------------------------------------------
		global		Reset_RS232C		; Reset RS232C
		global		Input_RS232C		; Input data from RS232C
		global		Output_RS232C		; Output data to RS232C
		global		Sense_RS232C		; Ssense RS232C
;------------------------------------------------------------------------
		global		SCC_Interrupt		; SCC interrupt routine

;************************************************************************
;*	Initialize SCC ( Z8530 ) Driver routine				*
;*									*
;************************************************************************

		mem8
		idx8
Init_SCC_Driver
		php
		sei					; Disable IRQ
;------------------------------------------------------------------------
		lda		#09H
		sta		SCC_chanA_ctrl		; WR09
		lda		#11000000B
		sta		SCC_chanA_ctrl		; Hardware Reset
;------------------------------------------------------------------------
		lda		#00001010B
		sta		RS232C_baurate		; RS232C baurate = 9600
		lda		#01100100B
		sta		RS232C_status		; RS232C non parity,stop_bit=1,8bit,xoff
;------------------------------------------------------------------------
		jsr		Reset_Keyboard		; Reset keyboard ( SCC channel A )
		jsr		Reset_RS232C		; Reset RS232C   ( SCC channel B )
;------------------------------------------------------------------------
		plp
		rts

;************************************************************************
;*	Reset keyboard	( SCC channel A )				*
;*									*
;************************************************************************

		mem8
		idx8
Reset_Keyboard
		php
		sei					; Disable IRQ

;=============== Initial SCC channel A ==================================
Reset_Keyboard1
		ldx		#00H
;------------------------------------------------------------------------
Reset_channelA$	lda		ChanA_init_data,x
		bmi		Wait$
		sta		SCC_chanA_ctrl		; Set control register number
		inx
		lda		ChanA_init_data,x
		sta		SCC_chanA_ctrl		; Write data to register
		inx
		bra		Reset_channelA$
;------------------------------------------------------------------------
Wait$		mem16
		rep		#00100000B		; Memory 16 bit mode
		lda		#9000
Wait_loop$	dec		a			; 2 cycle
		bne		Wait_loop$		; 4 cycle  : Wait for oscillator
		sep		#00100000B
		mem8
;-------------------------------------------------------------------------
Start_channelA$	inx
		lda		ChanA_init_data,x
		bmi		Initial_buffer$
		sta		SCC_chanA_ctrl		; Set control register number
		inx
		lda		ChanA_init_data,x
		sta		SCC_chanA_ctrl		; Write data to register
		bra		Start_channelA$
;------------------------------------------------------------------------
Initial_buffer$	jsr		Flush_Keyboard
		lda		SCC_chanA_data		; Dummy read
Repeat$		jsr		Keyboard_Status		; Dummy read
		bcc		Repeat$

;=============== Reset Keyboard =========================================
Reset_Keyboard2
		lda		#03H
		sta		SCC_chanA_ctrl
		lda		#11000000B
		sta		SCC_chanA_ctrl		; WR03: Disable serial input
;------------------------------------------------------------------------
		lda		#05H
		sta		SCC_chanA_ctrl
		lda		#11101010B
		sta		SCC_chanA_ctrl		; WR05: Output break,DTR=L,RTS=L
		lda		#00000000B
		jsr		Output_Keyboard		; Output RESET command
;------------------------------------------------------------------------
		lda		#0
Wait1$		dec		a			; 2 cycle
		bne		Wait1$			; 4 cycle
;------------------------------------------------------------------------
		lda		#05H
		sta		SCC_chanA_ctrl
		lda		#11101010B
		sta		SCC_chanA_ctrl		; WR05: break off DTR=L,RTS=L
;------------------------------------------------------------------------
		lda		#100
Wait2$		dec		a			; 2 cycle
		bne		Wait2$			; 4 cycle
;------------------------------------------------------------------------
		lda		#05H
		sta		SCC_chanA_ctrl
		lda		#11101000B
		sta		SCC_chanA_ctrl		; WR05: Enable serial output DTR=L,RTS=H
;------------------------------------------------------------------------
		lda		#03H
		sta		SCC_chanA_ctrl
		lda		#11000001B
		sta		SCC_chanA_ctrl		; WR03: Enable serial input
;------------------------------------------------------------------------
		lda		SCC_chanA_data		; Dummy read
		plp
		rts

;===============  SCC channel A initial data  ===========================

ChanA_init_data	byte		009H,10000000B		; WR09: Reset SCC channel A
		byte		00EH,00000000B		; WR14: Stop baurate generator
		byte		001H,00000000B		; WR01: Disable interrupt
		byte		003H,11000000B		; WR03: Disable serial input
		byte		005H,01100010B		; WR05: Disable serial output
		byte		00FH,00000000B		; WR15: Disable all Status-Interrupt
		byte		000H,00010000B		; WR00: Reset Status-Interrupt
		byte		000H,00010000B		; WR00: Reset Status-Interrupt
		byte		004H,01000101B		; WR04: Set StopBit-1,NonParity
		byte		00BH,11010100B		; WR11: Set clock mode
		byte		0FFH

		byte		00CH,00000110B		; WR12: Set baurate status low
		byte		00DH,00000000B		; WR13: Set baurate status high
		byte		00EH,00000001B		; WR14: Start Baurate generator
		byte		001H,11110000B		; WR01: Enable receive-Interrupt
		byte		009H,00001010B		; WR09: Enable interrupt ( master switch )
		byte		005H,11101000B		; WR05: Enable serial output DTR=L,RTS=H
		byte		003H,11000001B		; WR03: Enable serial input
		byte		0FFH

;************************************************************************
;*	Reset RS232C ( SCC channel B )					*
;*									*
;************************************************************************

		mem8
		idx8
Reset_RS232C
		php
		sei					; Disable IRQ
		ldx		#00H

;===============  Reset SCC channel B  ==================================
Reset_channelB$
		lda		ChanB_init_data,x
		bmi		Set_baurate$
		sta		SCC_chanB_ctrl
		inx
		lda		ChanB_init_data,x
		sta		SCC_chanB_ctrl
		inx
		bra		Reset_channelB$
;-------------------------------------------------------------------------
Set_baurate$	mem8
		sep		#00100000B
		ldy		<RS232C_baurate
		lda		#0CH
		sta		SCC_chanB_ctrl		; WR12
		lda		Baurate_data+0,y
		sta		SCC_chanB_ctrl		; Set baurate low
		lda		#0DH
		sta		SCC_chanB_ctrl		; WR13
		lda		Baurate_data+1,y
		sta		SCC_chanB_ctrl		; Set baurate high
;-------------------------------------------------------------------------
Set_Stop_bit$	lda		#04H
		sta		SCC_chanB_ctrl		; WR04
		lda		<RS232C_status
		and		#00001111B
		ora		#01000000B
		sta		SCC_chanB_ctrl		; Set stop_bit & parity
;-------------------------------------------------------------------------
Enable_output$	lda		#05H
		sta		SCC_chanB_ctrl		; WR05
		lda		<RS232C_status
		and		#01100000B
		ora		#10001010B
		sta		SCC_chanB_ctrl		; Enable output
;-------------------------------------------------------------------------
Enable_input$	lda		#03H
		sta		SCC_chanB_ctrl		; WR03
		lda		<RS232C_status
		asl		a
		and		#11000000B
		ora		#00000001B
		sta		SCC_chanB_ctrl		; Enable serial input
;------------------------------------------------------------------------
Start_channelB$	inx
		lda		ChanB_init_data,x
		bmi		Initial_buffer$
		sta		SCC_chanB_ctrl
		inx
		lda		ChanB_init_data,x
		sta		SCC_chanB_ctrl
		bra		Start_channelB$
;------------------------------------------------------------------------
Initial_buffer$	jsr		Flush_RS232C
		plp
		rts

;===============  SCC channel B initial data  ===========================

ChanB_init_data	byte		009H,01000000B		; WR09: Reset SCC channel B
		byte		00EH,00000000B		; WR14: Stop baurate generator
		byte		001H,00000000B		; WR01: Disable interrupt
		byte		003H,11000000B		; WR03: Disable serial input
		byte		005H,11100010B		; WR05: Disable serial output
		byte		00FH,00000000B		; WR15: Disable all Status-Interrupt
		byte		000H,00010000B		; WR00: Reset Status-Interrupt
		byte		000H,00010000B		; WR00: Reset Status-Interrupt
		byte		00BH,01010000B		; WR11: Set clock mode
		byte		0FFH

		byte		00EH,00000001B		; WR14: Start Baurate generator
		byte		009H,00001010B		; WR09: Enable interrupt ( master switch )
		byte		001H,00010000B		; WR01: Enable receive-Interrupt
		byte		0FFH

;===============  RS232C baurate data  ==================================

Baurate_data	word		510			;   300
		word		254			;   600
		word		126			;  1200
		word		62			;  2400
		word		30			;  4800
		word		14			;  9600
		word		6			; 19200

;************************************************************************
;*	Clear keyboard buffer						*
;*									*
;************************************************************************

		mem8
		idx8
Flush_Keyboard
		php
		sei					; Disable IRQ
		stz		<keybuf_datcnt		; Clear Keyboard data counter
		stz		<keybuf_putptr		; Clear Keyboard buffer data put pointer
		stz		<keybuf_getptr		; Clear Keyboard buffer data get pointer
		plp
		rts

;************************************************************************
;*	Clear RS232C buffer						*
;*									*
;************************************************************************

		mem8
		idx8
Flush_RS232C
		php
		sei					; Disable IRQ
		stz		<keybuf_datcnt		; Clear Keyboard data counter
		stz		<keybuf_putptr		; Clear Keyboard buffer data put pointer
		stz		<keybuf_getptr		; Clear Keyboard buffer data get pointer
		plp
		rts

;************************************************************************
;*	Enable SCC interrupt						*
;*									*
;************************************************************************

		mem8
Enable_SCC_Int
		php
		sei					; Disable IRQ
		lda		#09H
		sta		SCC_chanA_ctrl		; Set control register number
		lda		#00001010B
		sta		SCC_chanA_ctrl		; Enable interrupt
		plp
		rts

;************************************************************************
;*	Disable SCC interrupt						*
;*									*
;************************************************************************

		mem8
Disable_SCC_Int
		php
		sei					; Disable IRQ
		lda		#09H
		sta		SCC_chanA_ctrl		; Set control register number
		lda		#00000010B
		sta		SCC_chanA_ctrl		; Disable interrupt
		plp
		rts

;************************************************************************
;*	Input keyboard ( wait until input )				*
;*									*
;*		Exit		Acc = ascii code			*
;************************************************************************

		mem8
		idx8
Input_Keyboard
		lda		<keybuf_datcnt		; Data avirable ?
		beq		Input_Keyboard		; no.
;------------------------------------------------------------------------
		ldx		<keybuf_getptr
		lda		Keyboard_buffer,x	; Get ascii code
		xba
		txa
		inc		a
		and		#1FH
		sta		<keybuf_getptr		; Increment get pointer
		dec		<keybuf_datcnt		; Decrement data counter
		xba
		rts

;************************************************************************
;*	Output keyboard command						*
;*									*
;*		Entry	A = keyboard command				*
;************************************************************************

		mem8
		idx8
Output_Keyboard
		pha
;------------------------------------------------------------------------
Repeat$		lda		SCC_chanA_ctrl
		bit		#00000100B		; Be able to send ?
		beq		Repeat$			; no.
;------------------------------------------------------------------------
		pla
		sta		SCC_chanA_data		; Send data
		rts

;************************************************************************
;*	Input sense keyboard						*
;*									*
;*		Exit	A = Number of datas in buffer			*
;************************************************************************

		mem8
Sense_Keyboard
		lda		<keybuf_datcnt		; Data avirable ?
		rts

;************************************************************************
;*	Receive keyboard status						*
;*									*
;*		Exit	A = keyboard status	; CY=1: Time out	*
;************************************************************************

		mem8
		idx16
Keyboard_Status
		rep		#00010000B		; Index 16 bit mode
		ldx		#700
;------------------------------------------------------------------------
Repeat$		lda		SCC_chanA_ctrl		; 4 cycle
		lsr		a			; 2 cycle
		bcs		Receive$		; 3 cycle
		dex					; 2 cycle
		bne		Repeat$			; 4 cycle	15 cycle
;------------------------------------------------------------------------
Time_out$	lda		#11111111B
		sep		#00010001B		; Index 8 bit mode
		rts
;------------------------------------------------------------------------
Receive$	idx8
		sep		#00010000B		; Index 8 bit mode
		lda		SCC_chanA_data
		ldx		#38H
		stx		SCC_chanA_ctrl		; SCC IUS reset
		clc
		rts

;************************************************************************
;*	Input data from RS232C						*
;*									*
;*		Exit	A = code					*
;************************************************************************

		mem8
		idx8
Input_RS232C
		lda		<serial_datcnt		; Data avirable ?
		beq		Input_RS232C		; no.
;------------------------------------------------------------------------
		ldx		<serial_getptr
		lda		RS232C_buffer,x		; Get code
		inx
		stx		<serial_getptr		; Increment get pointer
		dec		<serial_datcnt		; Decrement data counter
		rts

;************************************************************************
;*	Output data to RS232C						*
;*									*
;*		Entry	A = code					*
;************************************************************************

		mem8
Output_RS232C
		pha
;------------------------------------------------------------------------
Repeat$		lda		SCC_chanB_ctrl
		bit		#00000100B		; Be able to send ?
		beq		Repeat$			; no.
;------------------------------------------------------------------------
		pla
		sta		SCC_chanB_data		; Send data
		rts

;************************************************************************
;*	Output sense keyboard						*
;*									*
;*		Exit	A = flags ; B = Number of datas inbuffer	*
;************************************************************************

		mem8
Sense_RS232C
		lda		SCC_chanB_ctrl
		and		#00000100B
		beq		Skip1$
		lda		#10000000B
;------------------------------------------------------------------------
Skip1$		xba
		lda		<serial_datcnt		; Data avirable ?
		beq		Skip2$
;------------------------------------------------------------------------
		xba
		ora		#01000000B
		rts
;------------------------------------------------------------------------
Skip2$		xba
		rts

;************************************************************************
;*	SCC interrupt routine entry					*
;*									*
;************************************************************************

		mem8
		idx8
SCC_Interrupt
		lda		SCC_chanA_ctrl
		lsr		a
		bcc		SCC_Interrupt_2
;------------------------------------------------------------------------
		jsr		Scan_keyboard
		lda		#38H
		sta		SCC_chanA_ctrl		; SCC IUS reset
		lda		#10000000B
		sta		<IRQ_status		; SCC Interrupt
;------------------------------------------------------------------------
SCC_Interrupt_2	lda		SCC_chanB_ctrl
		lsr		a
		bcc		SCC_Int_exit
;------------------------------------------------------------------------
		jsr		RS232C_receive
		lda		#38H
		sta		SCC_chanB_ctrl		; SCC IUS reset
		lda		#10000000B
		sta		<IRQ_status		; SCC Interrupt
;------------------------------------------------------------------------
SCC_Int_exit	rts

;************************************************************************
;*		RS232C receive						*
;************************************************************************

		mem8
		idx8
RS232C_receive
		lda		#01H
		sta		SCC_chanB_ctrl		; RR01
		lda		SCC_chanB_ctrl		; Get error status
		and		#01110000B		; error ?
		bne		RS232C_error		; yes.

;===============  RS232C get data  ======================================
RS232C_get_data
		lda		SCC_chanB_data		; Get scan code
		ldx		<serial_datcnt
		cpx		#255			; buffer full ?
		beq		Exit$			; yes.
		ldx		<serial_putptr
		sta		RS232C_buffer,x
		inx
		stx		<serial_putptr
		inc		<serial_datcnt
Exit$		rts

;===============  Error reset  =========================================
RS232C_error
		sta		<serial_errsta		; Store error status
		lda		SCC_chanB_data		; Dummy read
		lda		#00110000B
		sta		SCC_chanB_ctrl		; Error reset
		rts

;************************************************************************
;*		Keyboard scan						*
;************************************************************************

		mem8
		idx8
Scan_keyboard
		lda		#01H
		sta		SCC_chanA_ctrl		; RR01
		lda		SCC_chanA_ctrl		; Get error status
		and		#01110000B		; error ?
		beq		Scan_character		; no.

;===============  Error reset  =========================================
Scan_key_error
		lda		SCC_chanA_data		; Dummy read
		lda		#00110000B
		sta		SCC_chanA_ctrl		; Error reset
		rts

;===============  Check shift key  =====================================
Scan_character
		lda		SCC_chanA_data		; Get scan code
		tax
		bmi		Shift_out_check
		cpx		#70H
		bcc		Scan_character2
;-----------------------------------------------------------------------
Shift_in_check	lda		Shift_table-70H,x
		beq		Capital$
		tsb		<shift_flags		; shift in
		rts
Capital$	dec		a
		sta		<capital_lock		; Capital lock on
		rts
;-----------------------------------------------------------------------
Shift_out_check	cpx		#0F0H
		bcc		Exit$		
		lda		Shift_table-0F0H,x
		beq		Capital$
		trb		<shift_flags		; shift out
		rts
Capital$	sta		<capital_lock		; Capital lock off
Exit$		rts
;-----------------------------------------------------------------------
Shift_table	byte		01000000B		; 70H : SHIFT key
		byte		00000000B		; 71H : CAPS key
		byte		00100000B		; 72H : KANA key
		byte		00010000B		; 73H : GRPH key
		byte		10000000B		; 74H : CTRL key

;===============  Check scan code  =====================================
Scan_character2
		ldy		<keybuf_datcnt
		cpy		#31			; Keyboard buffer full ?
		beq		Scan_exit		; yes.
;-----------------------------------------------------------------------
		lda		#00100000B
		bit		<shift_flags
		bmi		Scan_ctrl
		bvs		Scan_shift
		bne		Scan_kana
		bra		Scan_normal

;===============  Scan control character  ==============================
Scan_ctrl
		lda		Keyboard_ctrl,x
		beq		Scan_exit
		bra		Scan_char_set

;===============  Scan shift character  ================================
Scan_shift
		bne		Scan_kana_shift
		lda		Keyboard_shift,x
		beq		Scan_exit
		ldy		capital_lock
		beq		Scan_char_set
;-----------------------------------------------------------------------
		cmp		#41H
		bcc		Scan_char_set
		cmp		#5BH
		bcs		Scan_char_set
		adc		#20H
		bra		Scan_char_set		

;===============  Scan kana character  =================================
Scan_kana
		bra		Scan_exit		; Not support

;===============  Scan kana shift character  ===========================
Scan_kana_shift
		bra		Scan_exit		; Not support

;===============  Scan normal character  ===============================
Scan_normal
		lda		Keyboard_normal,x
		beq		Scan_exit
		ldy		capital_lock
		beq		Scan_char_set
		cmp		#61H
		bcc		Scan_char_set
		cmp		#7BH
		bcs		Scan_char_set
		sec
		sbc		#20H

;===============  Scan character set  =================================
Scan_char_set
		ldx		<keybuf_putptr
		sta		Keyboard_buffer,x
		txa
		inc		a
		and		#1FH
		sta		<keybuf_putptr
		inc		<keybuf_datcnt
Scan_exit	rts

;************************************************************************
;*		PC-9800 Keyboard data					*
;************************************************************************

Keyboard_normal	byte		01BH,031H,032H,033H,034H,035H,036H,037H		; 00H
		byte		038H,039H,030H,02DH,05EH,05CH,008H,009H
		byte		071H,077H,065H,072H,074H,079H,075H,069H		; 10H
		byte		06FH,070H,040H,05BH,00DH,061H,073H,064H
		byte		066H,067H,068H,06AH,06BH,06CH,03BH,03AH		; 20H
		byte		05DH,07AH,078H,063H,076H,062H,06EH,06DH
		byte		02CH,02EH,02FH,000H,020H,00EH,013H,014H		; 30H
		byte		012H,07FH,01EH,01DH,01CH,01FH,00CH,001H
		byte		02DH,02FH,037H,038H,039H,02AH,034H,035H		; 40H
		byte		036H,02BH,031H,032H,033H,03DH,030H,02CH
		byte		02EH,00FH,0FAH,0FBH,0FCH,0FDH,0FEH,000H		; 50H
		byte		000H,000H,000H,000H,000H,000H,000H,000H
		byte		003H,002H,0F0H,0F1H,0F2H,0F3H,0F4H,0F5H		; 60H
		byte		0F6H,0F7H,0F8H,0F9H,000H,000H,000H,000H

Keyboard_shift	byte		01BH,021H,022H,023H,024H,025H,026H,027H		; 00H
		byte		028H,029H,000H,03DH,060H,07CH,008H,009H
		byte		051H,057H,045H,052H,054H,059H,055H,049H		; 10H
		byte		04FH,050H,07EH,07BH,00DH,041H,053H,044H
		byte		046H,047H,048H,04AH,04BH,04CH,02BH,02AH		; 20H
		byte		07DH,05AH,058H,043H,056H,042H,04EH,04DH
		byte		03CH,03EH,03FH,05FH,020H,00EH,013H,014H		; 30H
		byte		012H,07FH,01EH,01DH,01CH,01FH,00BH,001H
		byte		02DH,02FH,037H,038H,039H,02AH,034H,035H		; 40H
		byte		036H,02BH,031H,032H,033H,03DH,030H,02CH
		byte		02EH,00FH,0EAH,0EBH,0ECH,0EDH,0EEH,000H		; 50H
		byte		000H,000H,000H,000H,000H,000H,000H,000H
		byte		018H,002H,0E0H,0E1H,0E2H,0E3H,0E4H,0E5H		; 60H
		byte		0E6H,0E7H,0E8H,0E9H,000H,000H,000H,000H

Keyboard_ctrl	byte		01BH,000H,000H,000H,000H,000H,000H,000H		; 00H
		byte		000H,000H,000H,000H,01EH,01CH,008H,009H
		byte		011H,017H,005H,012H,014H,019H,015H,009H		; 10H
		byte		00FH,010H,000H,01BH,00DH,001H,013H,004H
		byte		006H,007H,008H,00AH,00BH,00CH,000H,000H		; 20H
		byte		01DH,01AH,018H,003H,016H,002H,00EH,00DH
		byte		000H,000H,000H,01FH,000H,00EH,013H,014H		; 30H
		byte		012H,07FH,01EH,01DH,01CH,01FH,00CH,001H
		byte		02DH,02FH,037H,038H,039H,02AH,034H,035H		; 40H
		byte		036H,02BH,031H,032H,033H,03DH,030H,02CH
		byte		02EH,00FH,09AH,09BH,09CH,09DH,09EH,000H		; 50H
		byte		000H,000H,000H,000H,000H,000H,000H,000H
		byte		0FFH,002H,090H,091H,092H,093H,094H,095H		; 60H
		byte		096H,097H,098H,099H,000H,000H,000H,000H

		end
