;.lib "axm"
;
;begin
;title "A2232 serial board driver"
;
;set modules "2232"
;set executable "2232.bin"
;
;;;;set nolink
;
;set temporary directory "t:"
;
;set assembly options "-m6502 -l60:t:list"
;set link options "bin"; loadadr"
;;;bin2c 2232.bin msc6502.h msc6502code
;end
;
;
; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ###
;
; - Created 950501 by JM -
;
;
; Serial board driver software.
;
;
% Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>.
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions
% are met:
% 1. Redistributions of source code must retain the above copyright
%    notice, and the entire permission notice in its entirety,
%    including the disclaimer of warranties.
% 2. Redistributions in binary form must reproduce the above copyright
%    notice, this list of conditions and the following disclaimer in the
%    documentation and/or other materials provided with the distribution.
% 3. The name of the author may not be used to endorse or promote
%    products derived from this software without specific prior
%    written permission.
%
% ALTERNATIVELY, this product may be distributed under the terms of
% the GNU General Public License, in which case the provisions of the
% GPL are required INSTEAD OF the above restrictions.  (This clause is
% necessary due to a potential bad interaction between the GPL and
% the restrictions contained in a BSD-style copyright.)
%
% THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
% DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
% STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
% OF THE POSSIBILITY OF SUCH DAMAGE.
;
;
; Bugs:
;
; - Can't send a break yet
;
;
;
; Edited:
;
; - 950501 by JM -> v0.1	- Created this file.
; - 951029 by JM -> v1.3	- Carrier Detect events now queued in a separate
;				  queue.
;
;


CODE		equ	$3800		; start address for program code


CTL_CHAR	equ	$00		; byte in ibuf is a character
CTL_EVENT	equ	$01		; byte in ibuf is an event

EVENT_BREAK	equ	$01
EVENT_CDON	equ	$02
EVENT_CDOFF	equ	$03
EVENT_SYNC	equ	$04

XON		equ	$11
XOFF		equ	$13


VARBASE		macro	*starting_address	; was VARINIT
_varbase	set	\1
		endm

VARDEF		macro	*name space_needs
\1		equ	_varbase
_varbase	set	_varbase+\2
		endm


stz		macro	* address
		 db	$64,\1
		endm

stzax		macro	* address
		 db	$9e,<\1,>\1
		endm


biti		macro	* immediate value
		db	$89,\1
		endm

smb0		macro	* address
		db	$87,\1
		endm
smb1		macro	* address
		db	$97,\1
		endm
smb2		macro	* address
		db	$a7,\1
		endm
smb3		macro	* address
		db	$b7,\1
		endm
smb4		macro	* address
		db	$c7,\1
		endm
smb5		macro	* address
		db	$d7,\1
		endm
smb6		macro	* address
		db	$e7,\1
		endm
smb7		macro	* address
		db	$f7,\1
		endm



;-----------------------------------------------------------------------;
;									;
; stuff common for all ports, non-critical (run once / loop)		;
;									;
DO_SLOW		macro	* port_number					;
		.local			;				;
		lda	CIA+C_PA	; check all CD inputs		;
		cmp	CommonCDo	; changed from previous accptd?	;
		beq	=over		; nope, do nothing else here	;
					;				;
		cmp	CommonCDb	; bouncing?			;
		beq	=nobounce	; nope ->			;
					;				;
		sta	CommonCDb	; save current state		;
		lda	#64		; reinitialize counter		;
		sta	CommonCDc	;				;
		jmp	=over		; skip CD save			;
					;				;
=nobounce	dec	CommonCDc	; no, decrement bounce counter	;
		bpl	=over		; not done yet, so skip CD save	;
					;				;
=saveCD		ldx	CDHead		; get write index		;
		sta	cdbuf,x		; save status in buffer		;
		inx			;				;
		cpx	CDTail		; buffer full?			;
		.if	ne		; no: preserve status:		;
		 stx	CDHead		; update index in RAM		;
		 sta	CommonCDo	; save state for the next check	;
		.end			;				;
=over		.end	local						;
		endm							;
									;
;-----------------------------------------------------------------------;


; port specific stuff (no data transfer)

DO_PORT		macro	* port_number
		.local			;				;
		lda	SetUp\1		; reconfiguration request?	;
		.if	ne		; yes:				;
		 lda	SoftFlow\1	; get XON/XOFF flag		;
		 sta	XonOff\1	; save it			;
		 lda	Param\1		; get parameter			;
		 ora	#%00010000	; use baud generator for Rx	;
		 sta	ACIA\1+A_CTRL	; store in control register	;
		 stz	OutDisable\1	; enable transmit output	;
		 stz	SetUp\1		; no reconfiguration no more	;
		.end			;				;
					;				;
		lda	InHead\1	; get write index		;
		sbc	InTail\1	; buffer full soon?		;
		cmp	#200		; 200 chars or more in buffer?	;
		lda	Command\1	; get Command reg value		;
		and	#%11110011	; turn RTS OFF by default	;
		.if	cc		; still room in buffer:		;
		 ora	#%00001000	; turn RTS ON			;
		.end			;				;
		sta	ACIA\1+A_CMD	; set/clear RTS			;
					;				;
		lda	OutFlush\1	; request to flush output buffer;
		.if	ne		; yessh!			;
		 lda	OutHead\1	; get head			;
		 sta	OutTail\1	; save as tail			;
		 stz	OutDisable\1	; enable transmit output	;
		 stz	OutFlush\1	; clear request			;
		.end
		.end	local
		endm


DO_DATA		macro	* port number
		.local
		lda	ACIA\1+A_SR	; read ACIA status register	;
		biti	[1<<3]		; something received?		;
		.if	ne		; yes:				;
		 biti	[1<<1]		; framing error?		;
		 .if	ne		; yes:				;
		  lda	ACIA\1+A_DATA	; read received character	;
		  bne	=SEND		; not break -> ignore it	;
		  ldx	InHead\1	; get write pointer		;
		  lda	#CTL_EVENT	; get type of byte		;
		  sta	ictl\1,x	; save it in InCtl buffer	;
		  lda	#EVENT_BREAK	; event code			;
		  sta	ibuf\1,x	; save it as well		;
		  inx			;				;
		  cpx	InTail\1	; still room in buffer?		;
		  .if	ne		; absolutely:			;
		   stx	InHead\1	; update index in memory	;
		  .end			;				;
		  jmp	=SEND		; go check if anything to send	;
		 .end			;				;
		 			; normal char received:		;
		 ldx	InHead\1	; get write index		;
		 lda	ACIA\1+A_DATA	; read received character	;
		 sta	ibuf\1,x	; save char in buffer		;
		 stzax	ictl\1		; set type to CTL_CHAR		;
		 inx			;				;
		 cpx	InTail\1	; buffer full?			;
		 .if	ne		; no: preserve character:	;
		  stx	InHead\1	; update index in RAM		;
		 .end			;				;
		 and	#$7f		; mask off parity if any	;
		 cmp	#XOFF		; XOFF from remote host?	;
		 .if	eq		; yes:				;
		  lda	XonOff\1	; if XON/XOFF handshaking..	;
		  sta	OutDisable\1	; ..disable transmitter		;
		 .end			;				;
		.end			;				;
					;				;
					; BUFFER FULL CHECK WAS HERE	;
					;				;
=SEND		lda	ACIA\1+A_SR	; transmit register empty?	;
		and	#[1<<4]		;				;
		.if	ne		; yes:				;
		 ldx	OutCtrl\1	; sending out XON/XOFF?		;
		 .if	ne		; yes:				;
		  lda	CIA+C_PB	; check CTS signal		;
		  and	#[1<<\1]	; (for this port only)		;
		  bne	=DONE		; not allowed to send -> done	;
		  stx	ACIA\1+A_DATA	; transmit control char		;
		  stz	OutCtrl\1	; clear flag			;
		  jmp	=DONE		; and we're done		;
		 .end			;				;
					;				;
		 ldx	OutTail\1	; anything to transmit?		;
		 cpx	OutHead\1	;				;
		 .if	ne		; yes:				;
		  lda	OutDisable\1	; allowed to transmit?		;
		  .if	eq		; yes:				;
		   lda	CIA+C_PB	; check CTS signal		;
		   and	#[1<<\1]	; (for this port only)		;
		   bne	=DONE		; not allowed to send -> done	;
		   lda	obuf\1,x	; get a char from buffer	;
		   sta	ACIA\1+A_DATA	; send it away			;
		   inc	OutTail\1	; update read index		;
		  .end			;				;
		 .end			;				;
		.end			;				;
=DONE		.end	local
		endm



PORTVAR		macro	* port number
		VARDEF	InHead\1 1
		VARDEF	InTail\1 1
		VARDEF	OutDisable\1 1
		VARDEF	OutHead\1 1
		VARDEF	OutTail\1 1
		VARDEF	OutCtrl\1 1
		VARDEF	OutFlush\1 1
		VARDEF	SetUp\1 1
		VARDEF	Param\1 1
		VARDEF	Command\1 1
		VARDEF	SoftFlow\1 1
		; private:
		VARDEF	XonOff\1 1
		endm


 VARBASE 0	; start variables at address $0000
 PORTVAR 0	; define variables for port 0
 PORTVAR 1	; define variables for port 1
 PORTVAR 2	; define variables for port 2
 PORTVAR 3	; define variables for port 3
 PORTVAR 4	; define variables for port 4
 PORTVAR 5	; define variables for port 5
 PORTVAR 6	; define variables for port 6



 VARDEF	Crystal	1	; 0 = unknown, 1 = normal, 2 = turbo
 VARDEF	Pad_a	1
 VARDEF	TimerH	1
 VARDEF	TimerL	1
 VARDEF	CDHead	1
 VARDEF	CDTail	1
 VARDEF	CDStatus 1
 VARDEF	Pad_b	1

 VARDEF	CommonCDo 1	; for carrier detect optimization
 VARDEF	CommonCDc 1	; for carrier detect debouncing
 VARDEF	CommonCDb 1	; for carrier detect debouncing


 VARBASE $0200
 VARDEF	obuf0 256	; output data (characters only)
 VARDEF	obuf1 256
 VARDEF	obuf2 256
 VARDEF	obuf3 256
 VARDEF	obuf4 256
 VARDEF	obuf5 256
 VARDEF	obuf6 256

 VARDEF	ibuf0 256	; input data (characters, events etc - see ictl)
 VARDEF	ibuf1 256
 VARDEF	ibuf2 256
 VARDEF	ibuf3 256
 VARDEF	ibuf4 256
 VARDEF	ibuf5 256
 VARDEF	ibuf6 256

 VARDEF	ictl0 256	; input control information (type of data in ibuf)
 VARDEF	ictl1 256
 VARDEF	ictl2 256
 VARDEF	ictl3 256
 VARDEF	ictl4 256
 VARDEF	ictl5 256
 VARDEF	ictl6 256

 VARDEF	cdbuf 256	; CD event queue


ACIA0		equ	$4400
ACIA1		equ	$4c00
ACIA2		equ	$5400
ACIA3		equ	$5c00
ACIA4		equ	$6400
ACIA5		equ	$6c00
ACIA6		equ	$7400

A_DATA		equ	$00
A_SR		equ	$02
A_CMD		equ	$04
A_CTRL		equ	$06
;  00	write transmit data	read received data
;  02	reset ACIA		read status register
;  04	write command register	read command register
;  06	write control register	read control register

CIA		equ	$7c00		; 8520 CIA
C_PA		equ	$00		; port A data register
C_PB		equ	$02		; port B data register
C_DDRA		equ	$04		; data direction register for port A
C_DDRB		equ	$06		; data direction register for port B
C_TAL		equ	$08		; timer A
C_TAH		equ	$0a
C_TBL		equ	$0c		; timer B
C_TBH		equ	$0e
C_TODL		equ	$10		; TOD LSB
C_TODM		equ	$12		; TOD middle byte
C_TODH		equ	$14		; TOD MSB
C_DATA		equ	$18		; serial data register
C_INTCTRL	equ	$1a		; interrupt control register
C_CTRLA		equ	$1c		; control register A
C_CTRLB		equ	$1e		; control register B





		section	main,code,CODE-2

		db	>CODE,<CODE

;-----------------------------------------------------------------------;
; here's the initialization code:					;
;									;
R_RESET		ldx	#$ff						;
		txs			; initialize stack pointer	;
		cld			; in case a 6502 is used...	;
		ldx	#0		;				;
		lda	#0		;				;
		ldy	#Crystal	; this many bytes to clear	;
clr_loop	sta	0,x		; clear zero page variables	;
		inx			;				;
		dey			;				;
		bne	clr_loop	;				;
					;				;
		stz	CommonCDo	; force CD test at boot		;
		stz	CommonCDb	;				;
		stz	CDHead		; clear queue			;
		stz	CDTail		;				;
					;				;
		lda	#0		;				;
		sta	Pad_a		;				;
		lda	#170		; test cmp			;
		cmp	#100		;				;
		.if	cs		;				;
		 inc	Pad_a		; C was set			;
		.end			;				;
									;
;-----------------------------------------------------------------------;
; Speed check								;
;-----------------------------------------------------------------------;
									;
		lda	Crystal		; speed already set?		;
		beq	DoSpeedy	;				;
		jmp	LOOP		; yes, skip speed test		;
					;				;
DoSpeedy	lda	#%10011000	; 8N1, 1200/2400 bps		;
		sta	ACIA0+A_CTRL	;				;
		lda	#%00001011	; enable DTR			;
		sta	ACIA0+A_CMD	;				;
		lda	ACIA0+A_SR	; read status register		;
					;				;
		lda	#%10000000	; disable all ints (unnecessary);
		sta	CIA+C_INTCTRL	;				;
		lda	#255		; program the timer		;
		sta	CIA+C_TAL	;				;
		sta	CIA+C_TAH	;				;
					;				;
		ldx	#0		;				;
		stx	ACIA0+A_DATA	; transmit a zero		;
		nop			;				;
		nop			;				;
		lda	ACIA0+A_SR	; read status			;
		nop			;				;
		nop			;				;
		stx	ACIA0+A_DATA	; transmit a zero		;
Speedy1		lda	ACIA0+A_SR	; read status			;
		and	#[1<<4]		; transmit data reg empty?	;
		beq	Speedy1		; not yet, wait more		;
					;				;
		lda	#%00010001	; load & start the timer	;
		stx	ACIA0+A_DATA	; transmit one more zero	;
		sta	CIA+C_CTRLA	;				;
Speedy2		lda	ACIA0+A_SR	; read status			;
		and	#[1<<4]		; transmit data reg empty?	;
		beq	Speedy2		; not yet, wait more		;
		stx	CIA+C_CTRLA	; stop the timer		;
					;				;
		lda	CIA+C_TAL	; copy timer value for 68k	;
		sta	TimerL		;				;
		lda	CIA+C_TAH	;				;
		sta	TimerH		;				;
		cmp	#$d0		; turbo or normal?		;
		.if	cs		;				;
		 lda	#2		; turbo! :-)			;
		.else			;				;
		 lda	#1		; normal :-(			;
		.end			;				;
		sta	Crystal		;				;
		lda	#0		;				;
		sta	ACIA0+A_SR	;				;
		sta	ACIA0+A_CTRL	; reset UART			;
		sta	ACIA0+A_CMD	;				;
									;
		jmp	LOOP						;
									;
;									;
;-----------------------------------------------------------------------;
;									;
; The Real Thing:							;
;									;
LOOP		DO_SLOW			; do non-critical things	;
		jsr	do_input	; check for received data
		DO_PORT	0
		jsr	do_input
		DO_PORT	1
		jsr	do_input
		DO_PORT	2
		jsr	do_input
		DO_PORT	3
		jsr	do_input
		DO_PORT	4
		jsr	do_input
		DO_PORT	5
		jsr	do_input
		DO_PORT	6
		jsr	do_input
		jmp	LOOP


do_input	DO_DATA	0
		DO_DATA	1
		DO_DATA	2
		DO_DATA	3
		DO_DATA	4
		DO_DATA	5
		DO_DATA	6
		rts


;-----------------------------------------------------------------------;
		section	vectors,data,$3ffa
		dw	$d0d0
		dw	R_RESET
		dw	$c0ce
;-----------------------------------------------------------------------;



		end