; ADITIO.M68 - interface software for MAcrotech Adit card.

;                               Disclaimer
; Macrotech makes no representations or warranties with respect to the
; contents hereof and specifically disclaims any implied warranties of
; merchantibility or fitness for any purpose, useful or otherwise.
;
;
;
;This interface driver supports an Adit serial i/o board upto 16 channels.
;All ports have full modem (and therefore printer) control.
;
;Ports are addressed in the TRMDEF statement as 0 through 17 octal.

; Beta-test prerelease version 0.1 01JUN84 - not for distribution
; This driver is not DMA driven. This driver supports only one ADIT
; upto 16 channels. Tested with AMOS 1.1A(68). Future release will
; incorporate DMA features, extended driver entry
; routines for advanced features (ie: high speed block i/o through
; DMA), etc.

; last update 01JUN84

; base port = 0E0h
; interrupt level = 7

ADITP=^H0FFFFE0

       SEARCH  SYS
       SEARCH  SYSSYM
       SEARCH  TRM

       objnam  0,0,[IDV]
; i/o port register map
AIC=0                   ; command register
AIR1=1                  ; data register
AIR2=2                  ; for setup
AIR3=3                  ; ditto
SEMA=4                  ; semaphore, busy when bit 0 is high
ACK=5                   ; read acks interrupt & gets status
; status register defines
STS.IN=0                                ; input is available
STS.OT=1                                ; space for output data
STS.BS=2                                ; channel is busy
STS.DC=4                                ; data carrier detect
STS.BR=5                                ; break detected
STS.PA=6                                ; UART parity error
STS.FR=7                                ; framing error

DEFINE  NOTBSY
10$$:   BTST    #0, SEMA(A3)
       BNE     10$$
       ENDM



       PSECT
       BR      CHROUT                  ; output init routine
       BR      INIT                    ; interface init routine

INTLVL: WORD    7

CHROUT:
       SAVE    A0-A6, D0-D7
       MOV     T.IHM(A5), D7           ; get unit number
       MOV     T.IHW(A5),A3            ; and hardware address
       AND     #17, D7                 ; D7 gets channel number
       LEA     A6,COMAND+2
       MOVB    D7, @A6
       MOVB    #^H70, D0               ; interrupt on buffer ready
       NOTBSY
       CALL    COMAND
       REST    A0-A6, D0-D7
       RTN

; init sets up interrupt structure ,modem block parms and initializes modem
; board.
INIT:   SUPVR
       SVLOK                           ; lock cpu for init
       MOV     #ADITP,A3               ; index baord
       LEA     A6,INITA                ; INDEX INIT FLAG
       TSTB    @A6                     ; ADIT given full reset yet ?
       BNE     10$                     ; yes-once is enuf.
       SETB    @A6                     ; flag we did reset it
       NOTBSY                          ; wait for it
       MOVB    #^H0C0,D0               ; reset board command
       CALL    COMAND                  ; reset it
       CLR     D7
       MOVW    INTLVL, D7              ; get interrupt level
       LSL     D7, #2                  ; make it lword offset
       MOVW    #434, A6                        ; index int vector table
       SUB     D7, A6                  ; A6 points to table entry
       LEA     A1, IROUT
       MOV     A1, @A6
10$:    MOVL    T.IHM(A5), D7           ; D7 gets unit #
       MOV     D7, D6                  ; copy it
       DIV     D7, #16.                ; get board unit number
       ASL     D7, #3.                 ;  multiply by eight for offset
       ADDW    D7, A3                  ; A3 gets board's base address
       MOV     A3, T.IHW(A5)           ; save it for later
       MOV     #ADITP,A3               ; get base adress again
       ASL     D6, #2.                 ; make offset longword/channel
       LEA     A6, TTABL
       ADD     D6, A6                  ; A6 index interrupt TRMDEF table
       MOV     A5, @A6                 ; set TRMDEF refererence
       NOTBSY                          ; wait for not busy
; When set up for single buffer, the ADIT hangs up on output
       MOVB    #304, AIR1(A3)  ; set 8 data, 1 stop, no parity or XON/XOFF
; So for now, use multiple output buffers
;;;     MOVB    #344, AIR1(A3)  ; set 8 data, 1 stop, no parity or XON/XOFF
; set up baud rate
       MOVW    T.BAU(A5), D7           ; get system rate
       LEA     A6, BTABLE-2            ; index baud rate table -4
40$:    ADDW    #2, A6                  ; pre-increment
       MOVB    (A6)+, D6               ; compare to possible
       BEQ     100$                    ; end of list
       CMPW    D6, D7                  ; match ?
       BNE     40$                     ; no-try next
       MOVB    (A6)+, AIR2(A3)                 ; set AIR2
       MOVB    (A6)+, AIR3(A3)         ; set AIR3
       LEA     A6, COMAND+2
       MOV     T.IHM(A5), D7
       AND     #17, D7
       MOVB    D7, @A6                         ; set channel number
       MOV     #^H30, D0               ; init channel command
       CALL    COMAND                  ; wait for ADIT, give command
       MOV     #^H50, D0
       NOTBSY
       CALL    COMAND                  ; enable receive interrupts
       LSTS    #0                      ; return to user mode, unlock CPU.
       RTN

100$:   LSTS    #0
       TYPECR  <?Invalid baud rate for ADIT>
       RTN

; interrupt routine for reading characters
IROUT:  SVLOK
       SAVE    A0-A6, D0-D7
       MOV     #ADITP, A3              ; A3 indexs hardware
20$:    MOVB    ACK(A3), D0             ; acknowledge interrupt & get channel
       MOV     D0,D2
; use imbedded channel number to do table lookup.
       AND     #17, D0                 ; strip to channel only

;; This is NASTY, SELF MODIFYING CODE, used for economy & speed.
       LEA     A6,COMAND+2
       MOVB    D0,@A6                  ; save channel number
; This stores the current channel number in the OR #???,D0 op code in the
; COMAND subroutine, so we do not have to keep generating the channel number
; prior to each COMAND call.
       ASL     D0, #2.                 ; x 4
       LEA     A6, TTABL
       MOV     0(A6)[D0], D7           ; D7 gets TRMDEF pointer
       JEQ     SPUR                    ;  if zero its spurious
       MOV     D7, A5                  ; else point A5 to TRMDEF
       AND     #^H0F0,D2               ; strip out channel from interrupt poll
       CMPB    D2,#^H050               ; is it input ?
       BEQ     RECV                    ;  no-check for input
; send output data to ADIT as long as it is not busy and data is available.
; limit transfer to 64 bytes for now.
XMIT:   MOV     #10.,D3                 ; set maximum transfer count to 11.
10$:    PUSH    D3
       TRMOCP
       POP     D3
       TST     D1
       BMI     DISARM                  ; disable transmitter
       NOTBSY
       MOVB    D1, AIR2(A3)            ;  yes-set data output byte
       MOV     #^H60, D0               ; set single out command
       CALL    COMAND                  ; do it
       NOTBSY
       MOVB    AIR1(A3), D2            ; D2 gets ADIT status
       BTST    #STS.OT, D2             ; more output space ?
       BEQ     20$                     ; no
       DBF     D3,10$                  ; yes-loop if max count no te exceeded.
; end output process
20$:    MOV     #^H70, D0               ; enable xmit interrupts for next time
       CALL    COMAND
       BR      DONE

; no more data to output - clear OIP bit in status word
DISARM: BCLR    #7, @A5                 ; clear OIP
       BR      DONE

; receive all characters buffered.
RECV:   MOV     #^H40, D0               ; make it read single data
       NOTBSY
       CALL    COMAND                  ; send the command
       NOTBSY
       MOVB    AIR2(A3), D1            ; get the byte
       SAVE    A3,A5
       TRMICP                          ; pass it to TRMSER
       REST    A3,A5
       MOVB    AIR1(A3), D2            ; check status
       BTST    #STS.IN, D2             ; got more data ?
       BNE     RECV                    ;  yes-loop til gone
       MOV     #^H50, D0               ;  no-rearm input interrupt
       CALL    COMAND                  ; send the command
       BR      DONE

SPUR:   MOV     #^H20, D0
       NOTBSY
       CALL    COMAND                  ; clear framing & parity errors

DONE:
       REST    A0-A6, D0-D7
       RTE


; COMAND adds the channel bias (stored in the immediate field of the first op)
; and sends the command to the ADIT card. Then, it waits for the ADIT to become
; ready for another command.
COMAND: ORB     #0, D0                  ; add channel bias to command
       MOVB    D0, AIC(A3)             ; give to ADIT
       RTN
       EVEN
INITA:  WORD    0                       ; board inted flag
TTABL:  BLKL    32.                     ; map of 32 possible TRMDEFs


DEFINE  BBITS   RATE, AIR2, AIR3
       BYTE    RATE, AIR2, AIR3
       ENDM

BTABLE: BBITS   021,002,300             ; 38400
       BBITS   020,006,300             ; 19200
       BBITS   017,016,300             ;  9600
       BBITS   015,036,300             ;  4800
       BBITS   013,076,300             ;  2400
       BBITS   010,176,300             ;  1200
       BBITS   007,376,300             ;   600
       BBITS   006,376,301             ;   300
       BBITS   004,376,303             ;   150
       BBITS   002,162,305             ;   110
       BYTE    0

       EVEN

       END