;AM1006 Serial Port Interface Driver for the AM-1006
;
; ASL-xxxxx-xx
; NOTICE
;
;All rights reserved. This software is the property of Alpha Microsystems
;and the material contained herein is the proprietary property and trade
;secrets of Alpha Microsystems, embodying substantial creative efforts and
;confidential information, ideas and expressions, no part of which may be
;reproduced or transmitted in any form or by any means, electronic,
;mechanical, or otherwise, including photocopying or input into any
;information storage or retrieval system without the express written
;permission of Alpha Microsystems.
;
;CAUTION: Unauthorized distribution or reproduction of this material may
;subject you to legal action.
;
;
;This interface driver supports the 4/8 serial ports contained on the
;AM-1006 CPU board. Unlike the AM-100t ports, they have full modem
;(and therefore printer) control.
;
;Ports are addressed in the TRMDEF statement as 0 thru 7.
;
;Edit History:
;
; modify September 1985 by Tan Nguyen at the House of Reps. to have full
; individual port programable.
; Define 2681 Interrupt Status Register bits
IS$TRA = 1 ; Transmitter ready A
IS$RRA = 2 ; Receiver ready A
IS$TRB = 20 ; Transmitter ready B
IS$RRB = 40 ; Receiver ready B
;Define I/O Port Offsets
;
;These are defined as offset to the base of the serial port block.
P.MDR = 0 ; Mode Register (Input & Output)
P.SR = 1 ; Status Register (Input)
P.CLK = 1 ; Clock Select Register (Output)
P.CMR = 2 ; Command Register (Output)
P.RCV = 3 ; Receiver Holding Register (Input)
P.TXM = 3 ; Tranmitter Holding Register (Output)
P.ACR = 4 ; Auxiliary Control Register (Output)
P.ISR = 5 ; Interrupt Status Register (Input)
P.IMR = 5 ; Interrupt Mask Register (Output)
P.OCR = 15 ; Output port configuration register(Output)
P.SOB = 16 ; Set output port bits register(Output)
P.ROB = 17 ; Reset output port bits register(Output)
PAGE
;Driver Commumnications Area
;
;This is the standard branch table used by TRMDEF and TRMSER. Only TRMDEF
;is allowed to call the initialization routine.
AM1:
BR CHROUT ; character output routine
BR INIT ; initialization routine
NOP
CHROUT:
JMP CHROT
;CHROT Output Character Initiation Routine
;
;Enable the 2681 transmitter which generates an initial interrupt which
;will be dispatched to the transmitter routine, starting the output
;process. This routine is called by clock routine set up by TRMSER.
;
;Inputs: A5 Pointer to terminal definition block
;
;Outputs: None
;
;Modifies: A6
CHROT:
SAVE A6
MOV T.IHW(A5),A6 ; Get base address of DUART
MOVB #CM$RXEN!CM$TXEN,P.CMR(A6); Enable transmitter
REST A6
RTN
PAGE
;INIT Initialize the AM-1006 Port
;
;This routine is called by TRMDEF each time a port on the AM-1006 is defined.
;The routine must intialize the proper port to the specified configuration,
;or, if the default is specified, it must set the 1006 up for async 19200 baud
;operation.
;
;Inputs: D0 Specified baud rate code.
; A5 Points to terminal definition block.
;
;Outputs: Outputs to 1006 board only.
;
;Modifies: D1, A2, A3, A4; 1006 command register;
; Get baud rate code
INIT: LEA A6,BAUDTB ; index the baud rate table
ROLB D0,#1 ; Shift to word offset
MOVB 0(A6)[D0],D1 ; Get first byte of baud rate code
CMPB D1,#-1 ; Is it a valid baud rate ?
JEQ BDBAUD ; no - invalid baud rate
; Get port number and base addresses of DUART and channel
LEA A3,DUART1 ; get base of DUART address table
MOV T.IHM(A5),D2 ; use port # as displacement
MOV D2,D3 ; keep a copy
ANDB #06,D2 ; Reset bit 0, dont care channl A or B
ASL D2,#1 ; double for long word increments
MOV 0(A3)[D2],A2 ; Set A2 to base of DUART
MOV A2,A4 ; set A4 to base of specific channel
BTST #0,D3 ; port # indicates channel A or B
BEQ 10$ ; channel A
ADD #10,A4 ; channel B
; Set up baud rate
10$:
MOVB D1,P.ACR(A2) ; Send table select code
INC A6 ; Bump pointer
MOVB 0(A6)[D0],D1 ; Get baud rate code
ROLB D1,#4 ; Shift to receiver select bits
ORB 0(A6)[D0],D1 ; or int transmit clock select bits
MOVB D1,P.CLK(A4) ; Send to proper channel
; Reset everything
MOVB #CM$RMRP,P.CMR(A4)
MOVB #CM$RRCV,P.CMR(A4)
MOVB #CM$RTXM,P.CMR(A4)
MOVB #CM$REST,P.CMR(A4)
MOVB P.SR(A4),D1 ; Look at status reg
CMPB D1,#^H0FF ; If not there, should read FF
JEQ NOPORT ; If there, should be cleared
;***************************************************************************
; removed the section below (comments out) (TNN) tannguyen
; MOVB #M1$8B!M1$NP,P.MDR(A4) ; Set up mode register 1 (TNN)
; MOVB #M2$1SB!M2$NM!M2$CET,D1 ; Set up for mode register 2 (TNN)
; CMPB D0,#4 ; Did we want 110 baud (TNN)
; BNE 30$ (TNN)
; MOVB #M2$2SB!M2$NM!M2$CET,D1 ; Set to two stop bits (TNN)
;30$: (TNN)
; MOVB D1,P.MDR(A4) ; Set up mode register 2 (TNN)
;***************************************************************************
;***************************************************************************
; added this section below (TNN)
;
;*****************MODIFICATION FOR AM1006 STARTS HERE** (TNN) ***************
; INSERT THIS SECTION AND DELETE THE PREVIOUS SET MODE STATEMENTS
; PLEASE ALSO REFER TO THE PROGRAM ATTACHED ON [100,53] ON AMUS -
; BLKINP.M68 PASWOR.BAS
;
;This gets the mode registers from the tables. all you want to do when
;you assign different mode to any port (e.g, port 0=8bit,none parity
;port 7 7 bit odd parity,etc.) is set the table accordingly
;
SETMOD: LEA A6,MR1TAB ;get mode reg. 1 table
MOVB 0(A6)[D3],D1 ;get the mode for this port
MOVB D1,P.MDR(A4) ;SET IT
LEA A6,MR2TAB ;same thing for reg 2
MOVB 0(A6)[D3],D1 ;get mode for this port
MOVB D1,P.MDR(A4) ;set it
;done
;******************END OF MOD FOR AM1006 ENDS HERE (TNN)**********************
MOVB #CM$RXDI!CM$TXDI,P.CMR(A4) ; Temporarily disable interupts
CLR D1
MOVB #IM$TXRD!IM$RXRD,D1
CLR D1
MOVB #IM$TXRD!IM$RXRD,D1 ; Allow interrupts
BTST #0,D3 ; channel A ?
BEQ 40$ ; yes
ROLB D1,#4 ; Shift for channel B
40$:
LEA A6,IMASK1 ; Base address of IMR byte table
MOV D3,D2 ; Get another copy
ASR D2,#1 ; Index OPBIT table
ORB D1,0(A6)[D2] ; Set new bit pattern for given DUART
MOVB 0(A6)[D2],D1 ; Make sure D1 reflects both channels
MOVB D1,P.IMR(A2) ; Send to DUART
; Now enable DTR & RTS
MOVB #5,D1 ; Set up for channel A
BTST #0,D3 ; See if channel B
BEQ 50$ ; Nope
MOVB #22,D1 ; Set to channel B
50$:
LEA A6,OPBIT1 ; Base address of output byte table
ORB D1,0(A6)[D2] ; Set new pattern for given DUART
MOVB 0(A6)[D2],D1 ; Make sure D1 reflects both channels
MOVB D1,P.SOB(A2) ; Set bits on
MOVB #0,P.OCR(A2) ; Send to the DUART
; SET LOCAL MEMORY FLAGS AND POINTERS
LEA A0,PORTS ; index port initialization indicator
BSET D3,@A0 ; mark this port as initialized
ASL D3,#2 ; times 4 to index long words
LEA A3,TRMDF0 ; get beginning of TRMDEF table
MOV A5,0(A3)[D3] ; store pointer to TRMDEF block in tbl
MOV A4,T.IHW(A5) ; Save I/O address in terminal def blk
LEA A0,INTRPT ; Get addr of interrupt dispatch
MOV A0,INTVEC ; Save in autovector
MOVB #CM$RXEN,P.CMR(A4) ; Enable receiver interupts
RTN
BDBAUD:
TYPECR <?Invalid baud rate for specified interface>
RTN ; just ignore initialization
NOPORT: TYPECR <? Port does not exist !!>
RTN
PAGE
; INTRPT
; Come here via INTERRUPT vector to process all AM-1006 INTERRUPTs.
;
; Inputs: None
;
; Outputs: None
;
; Modifies: A0,A2,A5,D1
INTRPT:
SVLOK ; Lock all interrupts
SAVE A0-A6,D0-D7 ; Save all registers
MOV #INTADD,A0 ; Index the interrupt port
MOVB @A0,D1 ; read INTERRUPT byte
BTST #IOINT1,D1 ; Duart 1 I/O (receive/trans) INTERRUPT?
BEQ IOSRV1 ; yes, go service it
BTST #IOINT2,D1 ; Duart 2 ?
BEQ IOSRV2 ;
BTST #IOINT3,D1 ; Duart 3 ?
JEQ IOSRV3 ;
BTST #IOINT4,D1 ; Duart 4 ?
JEQ IOSRV4 ;
BTST #TIMR1I,D1 ; accidental timer INTERRUPT DUART 1 ?
JEQ TIMR1S ; yes, go turn it off
BTST #TIMR2I,D1 ; DUART 2 ?
JEQ TIMR2S ;
BTST #TIMR3I,D1 ; DUART 3 ?
JEQ TIMR3S ;
BTST #TIMR4I,D1 ; DUART 4 ?
JEQ TIMR4S ;
IOSRV1: MOVB #PORT01,D1 ; bit pattern indicates ports 0,1 initd
ANDB PORTS,D1 ; is either port of this DUART inited?
BEQ BADINT ; no, invalid INTERRUPT, ignore it
MOV DUART1,A2 ; get base of DUART
MOVB P.ISR(A2),D1 ; read INTERRUPT status reg
ANDB #IS$TRA!IS$RRA,D1 ; recv or transmit intrt. from ch A?
BEQ IOSR1B ; must be channel B
MOV TRMDF0,A5 ; get pointer to port 0's TRMDEF block
JMP GINTRP ; go to general processing routine
IOSR1B: MOV TRMDF1,A5 ; get pointer to port 1's TRMDEF block
JMP GINTRP ; go to general processing routine
IOSRV2: MOVB #PORT23,D1 ; bit pattern indicates ports 2,3 initd
ANDB PORTS,D1 ; is either port of this DUART inited?
BEQ BADINT ; no, invalid INTERRUPT, ignore it
MOV DUART2,A2 ; get base of DUART
MOVB P.ISR(A2),D1 ; read INTERRUPT status reg
ANDB #IS$TRA!IS$RRA,D1 ; recv or transmit intrt. from ch A?
BEQ IOSR2B ; must be channel B
MOV TRMDF2,A5 ; get pointer to port 2's TRMDEF block
JMP GINTRP ; go to general processing routine
IOSR2B: MOV TRMDF3,A5 ; get pointer to port 3's TRMDEF block
JMP GINTRP ; go to general processing routine
IOSRV3: MOVB #PORT45,D1 ; bit pattern indicates ports 4,5 initd
ANDB PORTS,D1 ; is either port of this DUART inited?
BEQ BADINT ; no, invalid INTERRUPT, ignore it
MOV DUART3,A2 ; get base of DUART
MOVB P.ISR(A2),D1 ; read INTERRUPT status reg
ANDB #IS$TRA!IS$RRA,D1 ; recv or transmit intrt. from ch A?
BEQ IOSR3B ; must be channel B
MOV TRMDF4,A5 ; get pointer to port 4's TRMDEF block
BR GINTRP ; go to general processing routine
IOSR3B: MOV TRMDF5,A5 ; get pointer to port 5's TRMDEF block
BR GINTRP ; go to general processing routine
IOSRV4: MOVB #PORT67,D1 ; bit pattern indicates ports 6,7 initd
ANDB PORTS,D1 ; is either port of this DUART inited?
JEQ BADINT ; no, invalid INTERRUPT, ignore it
MOV DUART4,A2 ; get base of DUART
MOVB P.ISR(A2),D1 ; read INTERRUPT status reg
ANDB #IS$TRA!IS$RRA,D1 ; recv or transmit intrt. from ch A?
BEQ IOSR4B ; must be channel B
MOV TRMDF6,A5 ; get pointer to port 6's TRMDEF block
BR GINTRP ; go to general processing routine
IOSR4B: MOV TRMDF7,A5 ; get pointer to port 7's TRMDEF block
BR GINTRP ; go to general processing routine
TIMR1S: MOVB #PORT01,D1 ; port init mask for this DUART
MOV DUART1,A2 ; get base of DUART
BR SHUTOF
TIMR2S: MOVB #PORT23,D1 ; port init mask for this DUART
MOV DUART2,A2 ; get base of DUART
BR SHUTOF
TIMR3S: MOVB #PORT45,D1 ; port init mask for this DUART
MOV DUART3,A2 ; get base of DUART
BR SHUTOF
TIMR4S: MOVB #PORT67,D1 ; port init mask for this DUART
MOV DUART4,A2 ; get base of DUART
BR SHUTOF
SHUTOF: ANDB PORTS,D1 ; either port on this DUART inited?
JEQ BADINT ; no, invalid INTERRUPT ignore it
MOVB #^H08,P.ROB(A2) ; shut off timer
REST A0-A6,D0-D7 ; restore registers
RTE
PAGE
;GINTRP General Serial Port Interrupt Processing Routine
;
; Come here to determine which channel (A or B) of given DUART generated the
; INTERRUPT.
;
;Inputs: A5 Terminal definition index
; All registers SAVEd on stack
;
;Outputs: D1 2681 Status Register
; A4 Pointer to base of I/O ports
;
;Modifies: D1, A4
GINTRP:
MOV T.IHW(A5),A4 ; get I/O register base into A4
MOVB P.SR(A4),D1 ; read the 2681 status register
BTST #ST$RDRF,D1 ; check receiver data register full flag
BNE INPR ; and go process if set
BTST #ST$TRDY,D1 ; check transmitter empty flag
BNE OUTPR ; and go process if set
BR FLSINT ; handle false interrupt
PAGE
;INPR Input Character Interrupt Routine
;
;Come here whenever we get a receiver interrupt. Read the character from
;the AM-1006 and pass on to TRMSER.
;
;Inputs: A2 DUART base
; A4 Port register base
; All registers SAVEd on stack
;
;Outputs: None
;
;Modifies: Restores registers
INPR: MOVB P.RCV(A4),D1 ; read the data character
TRMICP ; go process the character
REST A0-A6,D0-D7 ; restore registers
RTE
PAGE
;OUTPR Transmitter Interrupt Routine
;
;Come here on a transmitter interrupt. If another character is available
;for output, grab it and send it to the 2681.
;Otherwise, clear the OIP flag and dismiss interrupt.
;
;Inputs: D1 2681 Status Register
; A2 DUART base
; A4 I/O register base address
; A5 Terminal definition index
; All registers SAVEd on the stack
;
;Outputs: Only to AM-1006
;
;Modifies: Registers restored
OUTPR: TRMOCP ; get next output character from TRMSER
TST D1 ; data available?
BPL OUTREG ; yes -
ANDW #^C<OIP>,@A5 ; no - clear the OIP flag
MOVB #CM$TXDI,P.CMR(A4)
BR OUTRTN ; return
;Send character to transmitter
OUTREG: MOVB D1,P.TXM(A4) ; s
end character to 2681
OUTRTN: REST A0-A6,D0-D7 ; return
RTE
PAGE
;FLSINT False Interrupt Routine
;
;Come here when interrupt was caused by neither transmitter or receiver.
;The interrupt is either DCD change or spurious. In either case, we just
;ignore the interrupt.
;
;Inputs: D1 2681 Status Register
; A2 DUART base
; A4 I/O register base address
; All registers SAVEd on stack
;
;Outputs: None
;
;Modifies: Restores registers
OPBIT1: BYTE 0 ; DUART 1 output port bits
OPBIT2: BYTE 0 ; DUART 2 output port bits
OPBIT3: BYTE 0 ; DUART 3 output port bits
OPBIT4: BYTE 0 ; DUART 4 output port bits
INTADD = ^H0FFFF89 ; Interrupt port address
INTVEC = ^H064 ; Address of interrupt vector
IOINT1 = 0 ; Duart 1 I/O interrupt bit
IOINT2 = 1 ; Duart 2 I/O interrupt bit
IOINT3 = 2 ; Duart 3 I/O interrupt bit
IOINT4 = 3 ; Duart 4 I/O interrupt bit
TIMR1I = 4 ; Duart 1 timer interrupt bit
TIMR2I = 5 ; Duart 2 timer interrupt bit
TIMR3I = 6 ; Duart 3 timer interrupt bit
TIMR4I = 7 ; Duart 4 timer interrupt bit
;
; EACH PORT HAS ITS OWN MODE REGISTER 1 AND 2 VALUE
; this method helps to invidually set up different mode to different
; port. There are two tables: MR1TAB and MR2TAB for mode registers 1 and 2
;