;
; file :  prnt.drv.text
; date :  07-JULY-1982  kb
;
; This is the Printer driver source
;
; ADDED  version date before PRNTRDRV : 4-6-82  kb
;
; Added GetTable function to unitstatus to return the current state of the printer control table.
;  returns the table in the values user sends to the change table functions : 4-7-82  kb
; Changed IORESULT definitions to use the global file definitions :  4-23-82  kb
; Added include of /ccos/os.gbl.asm.text : 4-23-82  kb
;
; Added Header, removed version date : 7-7-82 kb
;
; INCLUDE FILES USED :
;       /ccos/os.gbl.asm.text                    ;OS GLOBAL EQUATES
;
; INCLUDE OS GLOBALS HERE
;
            LIST       OFF
            INCLUDE    '/CCOS/OS.GBL.ASM.TEXT'
            LIST       ON
;
            PAGE       59
;
  page
; EQUATES FOR ALL PRINTER DRIVER SOFTWARE
; BIT NUMBER DEFINITIONS
;
BITD0       EQU        0                         ;BIT 0
BITD1       EQU        1                         ;BIT 1
BITD2       EQU        2                         ;BIT 2
BITD3       EQU        3                         ;BIT 3
BITD4       EQU        4                         ;BIT 4
BITD5       EQU        5                         ;BIT 5
BITD6       EQU        6                         ;BIT 6
BITD7       EQU        7                         ;BIT 7
;
; Internal Flag word bit definitions
; Low order byte
;
SAVSR       EQU        BITD0                     ;SET=SAVED ENTRY SR
BUFMTY      EQU        BITD1                     ;BUFFER EMPTY FLAG
BUFFUL      EQU        BITD2                     ;BUFFER FULL FLAG
BUSY        EQU        BITD3                     ;PRINTER BUSY
ENQFLG      EQU        BITD4                     ;SENT ENQ WAITING FOR ACK
SENDLF      EQU        BITD5                     ;SEND LF CHAR NEXT
;
; Internal Flag masks
;
BUFFLGM     EQU        1                         ;CLEAR ALL BUT SAVSR
;
; Printer Control Table Flag's flag bit definitions
;
LINE        EQU        BITD0                     ;LINE TYPE HANDSHAKE
XONXOFF     EQU        BITD1                     ;XON/XOFF HANDSHAKE
ENQACK      EQU        BITD2                     ;ENQ/ACK HANDSHAKE
CTSLIN      EQU        BITD3                     ;LINE IS CTS
DSRLIN      EQU        BITD4                     ;LINE IS DSR
DCDLIN      EQU        BITD5                     ;LINE IS DCD
INVBUSY     EQU        BITD6                     ;1=LINE IS INVERTED(0) WHEN BUSY
DATACOM     EQU        BITD7                     ;DATACOM 1 WHEN SET ELSE DC0
;
; Table flag's masks
;
DCMFLGM     EQU        $80                       ;LEAVE DATACOM UNTOUCHED
;
; 68000 Interrupt Auto Vector Addresses
;
VEC1        EQU        $64                       ;AUTO VECTOR #1-DATA COM CONTROL
VEC2        EQU        $68                       ;AUTO VECTOR #2-DC 1
VEC4        EQU        $70                       ;AUTO VECTOR #4-DC 0
;
; Unit I/O Command codes
;
READCMD     EQU        1                         ; read command
WRCMD       EQU        2                         ; write command
BUSYCMD     EQU        4                         ; busy command
UNMCMD      EQU        6                         ; unmount command
;
TBLSTATE    EQU        6                         ;RETURN TABLE STATE FUNCTION CODE
;
; Status Register values
;
DISINT4     EQU        $2400                     ; Disable all DataCom device ints
DISINT1     EQU        $2100                     ; Disable DataCom Control int
CARRYST     EQU        $0001                     ; CCR with carry set
;
; VIA Addresses
;
ORA         EQU        $30F63                    ; PORT A
DDRA        EQU        $30F67                    ; PORT A DATA DIRECTION REG.
NHIRA       EQU        $30F7F                    ; PORT A W/O HANDSHAKE(IGNORE DDRA)
;
; VIA register values
;
IODDRA      EQU        $80                       ; PORT A BIT CONFIGURATION
;
; UART register definitions
;
UARTDC0     EQU        $30F20                    ;BASE ADDRESS OF DATACOM 0 UART
DC1OFF      EQU        $20                       ;OFFSET FROM DC0 BASE TO DC1 BASE
DATAREG     EQU          1                       ;DATA PORT REGISTER INDEX
STATRI      EQU          3                       ;STATUS REGISTER INDEX
RCVBF       EQU        BITD3                     ;RECEIVE BUFFER FULL FLAG (SR)
XMITBE      EQU        BITD4                     ;XMIT BUFFER EMPTY FLAG(SR)
CMDREGI     EQU        5                         ;COMMAND REGISTER INDEX
CTLREGI     EQU        7                         ;CONTROL REGISTER INDEX
;
; UART Register constants for setup of UART
;
CTLRC       EQU        $10                       ;1 STOP BIT,BAUD RATE GENERATOR
CMDRC       EQU        $09                       ;NORMAL,NO XMIT INT,RCV INT, ENABLE DTR
TURNOFF     EQU        $02                       ;TURNOFF UART(CMD REG)
CLRD3D2     EQU        $F3                       ;CLEAR BITS D3 & D2
XMITENB     EQU        $04                       ;ENABLE XMIT INTERRUPT
XMITDIS     EQU        $08                       ;DISABLE XMIT INTERRUPT
;
; ASCII Control characters for printer control
;
XON         EQU        $11                       ;CAN XMIT (CTL-Q)
XOFF        EQU        $13                       ;STOP XMIT (CTL-S)
ENQ         EQU        $05                       ;READY FOR MORE? (CTL-E)
ACK         EQU        $06                       ;YES, I'M READY (CTL-F)
NULL        EQU        $00                       ;NULL CHARACTER-DO NOTHING
CR          EQU        $0D                       ;CARRIAGE RETURN
LF          EQU        $0A                       ;LINE FEED
;
; Maximum Parameter values for Unitstatus Set table entry functions
;
MAXBAUD     EQU        6                         ;FOR SET BAUD RATE
MAXPRTY     EQU        4                         ;FOR SET PARITY
MAXWRDS     EQU        1                         ;FOR SET WORD SIZE
MAXDTCM     EQU        1                         ;FOR SET DATACOM
MAXHNDS     EQU        7                         ;FOR SET HANDSHAKE TYPE
;
; error codes (IORESULT)
;
INVCMD      EQU        IOEioreq                  ;invalid cmd-(invalid I/O request)
INVTBLID    EQU        IOEtblid                  ;invalid table id
INVPRM      EQU        IOEuiopm                  ;invalid parameter
INVFNC      EQU        IOEfnccd                  ;invalid function code
;
; Miscellaneous definitions
;
TRUE        EQU        1                         ; Pascal true boolean value
ON          EQU        1                         ;LISTING CONTROL - START LISTING
OFF         EQU        0                         ;LISTING CONTROL - STOP LISTING
  page
;
; UNIT I/O PARAMETER PASSING DEFINITION
;
;   COMMAND       UNIT   ADDR   COUNT   BLOCK   MODE     IORESULT   BUSY
; 0 - INSTALL     DO.W                                   D7.W
; 1 - READ        D0.W   D1.L   D2.W                     D7.W
; 2 - WRITE       D0.W   D1.L   D2.W            D5.W     D7.W
; 3 - CLEAR       D0.W                                   D7.W
; 4 - BUSY        D0.W                                   D7.W       D0.W
; 5 - STATUS      D0.W   D1.L   D2.W <--FUNCTION CODE    D7.W
; 6 - UNMOUNT     D0.W                                   D7.W
;
; ALL REGISTER VALUES ON ENTRY ARE SAVED AND RESTORED EXCEPT D0 & D7
; INTERNAL REGISTER USEAGE :
  page
;
            GLOBAL     PRNTRDRV
;
; PRINTER DRIVER
;
PRNTRDRV
            BRA.S      PRNT001                   ;*070782* JUMP AROUND HEADER
            DATA.B     0                         ;DEVICE NOT BLOCKED
            DATA.B     29                        ;VALID CMDS - NOT UNITWRITE
            DATA.B     82,07,07                  ;DATE JULY 7 1982
            DATA.B     hmlen                     ;HEADER MSG LENGTH
xxx010      DATA.B     'PRINTER driver'          ;HEADER MSG
hmlen       EQU        %-xxx010
;
PRNT001     CMPI.W     #UNMCMD,D4                ;VALID COMMAND
            BHI.S      PRNDERR                   ;NO
            MOVEM.L    D1-D6/A0-A6,-(SP)         ;SAVE REGISTERS
            CLR.L      D7                        ;CLEAR IORESULT
            MOVEA.L    D1,A3                     ;ADDRESS OF USERS BUFFER
            LEA        PRNDTBL,A1                ;TURN THE COMMAND INTO A
            LSL.W      #1,D4                     ;INDEX TO THE FUNCTION
            MOVE.W     0(A1,D4.W),D4
            JSR        0(A1,D4.W)                ;DO FUNCTION
            MOVEM.L    (SP)+,D1-D6/A0-A6         ;*** temp* for busy return in D0
            RTS
;
; Invalid Command Error
;
PRNDERR     MOVE.W     #INVCMD,D7
            RTS
;
; THE PRINTER DRIVER JUMP TABLE
;
PRNDTBL     DATA.W     PRNINST-PRNDTBL           ;UNITINSTALL
            DATA.W     PRNRD-PRNDTBL             ;UNITREAD
            DATA.W     PRNWR-PRNDTBL             ;UNITWRITE
            DATA.W     PRNCLR-PRNDTBL            ;UNITCLEAR
            DATA.W     PRNBSY-PRNDTBL            ;UNITBUSY
            DATA.W     PRNST-PRNDTBL             ;UNITSTATUS
            DATA.W     PRNUNMT-PRNDTBL           ;UNITUNMOUNT
  page
;
; PRNRD - UNITREAD
; READ FROM THE PRINTER IS ILLEGAL
;
PRNRD       MOVE.W     #INVCMD,D7
            RTS
  page
;
; PRNINST - UNITINSTALL ==> SETUP THE DRIVER FOR THE DEFAULT PRINTER
;   Assumes that a spurrious DataCom Control interrupt is benign and will
;   be handled by the DataCom Control interrupt service routine correctly.
;
PRNINST     BSR.S      DISINTS                   ;DISABLE DATACOM INTERRUPTS
;
            LEA        PCTLTBL,A0                ;SETUP PRINTER CONTROL TABLE
            MOVE.B     DEFPCT,(A0)+              ;FROM DEFAULTS (5 BYTES)
            MOVE.L     DEFPCT+1,(A0)
;
; Initialize UART from constants and Printer Control Table & Initialize VIA
;
            MOVE.B     #IODDRA,DDRA.L            ;INITIALIZE DATA DIRECTION REG FOR PORT A
            BSR.S      SETUART
;
            LEA        FLAGS+1,A0
            BTST       #SAVSR,(A0)               ;ONLY RESET STATUS REG. IF
            BEQ.S      PINNOSR                   ;SAVED SR IN SAVESR1
            MOVE.W     #DISINT1,SR               ;ALLOW ALL BUT DC CONTROL
;
; Initialize Buffer, Buffer variables, and Control variables
;
PINNOSR     BSR.S      INITBUF
;
; Setup interrupt vectors
;
            BSR.S      SETVECS
;
; If saved SR then restore it
;
            BSR.S      ENBINTS
            RTS
  page
;
; DISINTS - disable level 4 interrupts if current level is less than 4
;
DISINTS     LEA        FLAGS+1,A0
            BCLR       #SAVSR,(A0)               ;ASSUME NOT SAVED STATUS REG
            MOVE.W     SR,D0
            CMPI.W     #DISINT4,D0               ;IF CURRENT STATE IS >= DON'T SAVE
            BCC.S      DITEXIT                   ;DON'T SAVE
            BSET       #SAVSR,(A0)               ;MARK SAVED SR
            LEA        SAVESR1,A0                ;SAVE THE CURRENT INT STATUS
            MOVE.W     D0,(A0)
            MOVE.W     #DISINT4,SR               ;PREVENT ALL DATACOM INTERUPTS
DITEXIT     RTS
;
; ENBINTS - Restore saved SR if saved it
;
ENBINTS     LEA        FLAGS+1,A0                ;IF SAVED SR FLAG SET
            BTST       #SAVSR,(A0)               ;THEN RESTORE SR
            BEQ.S      EITEXIT                   ;DIDN'T SAVE SO EXIT
            MOVE.W     SAVESR1,SR
EITEXIT     RTS
;
; SETVECS - Put interrupt routine's entry addresses into the interrupt vectors
SETVECS     LEA        DCTLINT,A0                ;PUT DATA COM CONTROL
            MOVE.L     A0,VEC1.W                 ;INT ROUTINE IN VEC 1
            BSR.S      SETDCVEC                  ;PUT IN DATA COM XMIT/RCV RTN
            RTS
  page
;
; INITBUF - Initialize Buffer variables to EMPTY Buffer also ENQ, BUSY and
;           SENDLF are cleared to false.
;
INITBUF     LEA        FRONT,A0
            LEA        BUFFER,A1
            MOVE.L     A1,(A0)+                  ;FRONT := @BUFFER
            MOVE.L     A1,(A0)+                  ;REAR := @BUFFER
            CLR.W      (A0)+                     ;COUNT := 0
            MOVE.W     #BUFLEN,(A0)+             ;FREE SPACE := BUFFER LENGTH
;
            ANDI.W     #BUFFLGM,(A0)             ;CLEAR ALL BUFFER FLAGS
            ADDQ.L     #1,A0
            BSET       #BUFMTY,(A0)              ;BUFFER_EMPTY := TRUE
            RTS
  page
;
; SETUART - Initialize UART from constants and Printer Control Table
;
; Get UART Register Base address
;
SETUART     BSR.S      GETBASE                   ;RETURNS BASE IN A0
;
; Setup UART's Control register - index = 7  from Base
;
            MOVEQ      #CTLRC,D0                 ;1 STOP BIT,BAUD RATE GEN
            MOVE.B     WORDSIZ,D1                ;ADD WORD SIZE-7 OR 8 BITS
            LSL.B      #5,D1                     ;MOVE INTO HI ORDER BITS
            OR.B       D1,D0                     ;00=8 BITS,01=7 BITS
            OR.B       BAUDR,D0                  ;ADD BAUD RATE FROM TABLE
            MOVE.B     D0,CTLREGI(A0)            ;PUT IN CONTROL REGISTER
;
; Setup UART's Command register - index = 5 from Base
;
            MOVEQ      #CMDRC,D0                 ;CMD CONSTANTS
            MOVE.B     PARITY,D1                 ;GET TABLE PARITY
            LSL.B      #5,D1                     ;PUT IN CORRECT BIT POSITION
            OR.B       D1,D0
            MOVE.B     D0,CMDREGI(A0)            ;PUT IN COMMAND REGISTER
;
; Read the Data Port and Status Register to clear all Status flags
;
            MOVE.B     DATAREG(A0),D0            ;DATA PORT AT INDEX = 1
            MOVE.B     #0,DATAREG(A0)            ;CLEAR XMIT INT
            MOVE.B     STATRI(A0),D0             ;STATUS REG AT INDEX = 3
            RTS
;
; GETBASE - Get address of UART's register Base address in memory
;           EXIT : (A0) = Base address
;
GETBASE     LEA        UARTDC0.L,A0              ;ASSUME USING DATACOM 0
            CLR.L      D0
            LEA        PTFLAGS+1,A1              ;IF FLAG IS SET THEN MAKE
            BTST       #DATACOM,(A1)             ;D0 = THE ADDRESS OFFSET TO
            SNE        D0                        ;UART 1'S REGISTERS ELSE
            ANDI.B     #DC1OFF,D0                ;MAKE D0 = 0
            ADDA.L     D0,A0                     ;BASE := OFFSET+UART DC0 BSE ADDR
            RTS
  page
;
; SETDCVEC - Depending on which DataCom printer is connected to, place the
;            address of the Xmit/Rcv DataCom int routine's entry address in
;            the interrupt vector for that DataCom.  Put a pointer to a RTE
;            instruction into the vector not being used.
;
SETDCVEC    LEA        VEC4.W,A0                 ;ASSUME DC0 (VECTOR 4)
            LEA        VEC2.W,A1                 ;DC1 IS VECTOR 2
            LEA        PTFLAGS+1,A2
            BTST       #DATACOM,(A2)             ;IS IT DC0
            BEQ.S      SDVUSE0                   ;YES
            EXG        A0,A1                     ;NO,USE DC1 (VECTOR 2)
;
SDVUSE0     LEA        DCOMINT,A2                ;ADDR OF XMIT/RCV INT ROUTINE
            LEA        PTRRTE,A3
            MOVE.L     A2,(A0)                   ;INTERRUPT ROUTINE
            MOVE.L     A3,(A1)                   ;PTR TO RTE
            RTS
  page
; PRNWR - UNITWRITE
;           Setup the printer driver and place the user' characters
;           into it.  Start up the Printer interrupt routines if the buffer
;           was previously empty.
;           Uses D5 (Mode flag).  If (D5) = 1 then doesn't do Auto Linefeed.
;
PRNWR       TST.W      D2                        ;IS USER COUNT DONE?
            BEQ.S      PWREXIT                   ;YES
            SUBQ.W     #1,D2                     ;ELSE PUT CHAR IN BUFFER
;
            LEA        FLAGS+1,A0                ;IF BUFFER IS FULL THEN
PWRWAIT     BTST       #BUFFUL,(A0)              ;WAIT TIL IT IS NOT FULL
            BNE.S      PWRWAIT                   ;IS FULL
;
; Get next character and do pre-disable preparation
;
            BSR.S      GETNXTC                   ;GET NEXT CHAR INTO D1
            BSR.S      CHKCR                     ;SEE IF CR FOR AUTO LINE FEED
            LEA        REAR,A5
            MOVEA.L    (A5),A1                   ;REAR OF QUEUE POINTER
            LEA        BUFFER+BUFLEN,A2          ;IF AFTER INC REAR IT POINTS
            MOVEA.L    A1,A4                     ;BEYOND THE BUFFER THEN PTR
            ADDQ.L     #1,A4                     ;MUST WRAP BACK TO THE BEGIN
            CMPA.L     A4,A2                     ;BUFFER ADDRESS SPACE
            BNE.S      PWRNOWR                   ;NO WRAP-A4 IS THE UPDATEDREAR PTR
            LEA        BUFFER,A4
;
; Disable interrupt - put character in buffer
;
PWRNOWR     BSR        DISINTS                   ;DISABLE INTS/CLOBBERS D0
            LEA        FLAGS+1,A0
            MOVE.B     D1,(A1)                   ;PUT CHAR IN PRINTER BUFFER
            CMPA.L     FRONT,A4                  ;IF FRONT=(UPDATED REAR) THEN
            BNE.S      PWRNOTF                   ;FULL-- BRANCH NOT FULL
            BSET       #BUFFUL,(A0)              ;BUFFER FULL
PWRNOTF     MOVE.L     A4,(A5)                   ;SAVE UPDATED REAR PTR
            LEA        FREESPC,A1
            SUBI.W     #1,(A1)                   ;1 BYTE LESS OF FREE SPACE
;
; If Buffer was empty then start Transmissino Process
;
            BCLR       #BUFMTY,(A0)              ;TEST & CLEAR--BUFFER NOT EMPTY
            BEQ.S      PWRWNE                    ;WASN'T EMPTY BEFORE CLEAR
            BSR.S      STRTXMIT                  ;START XMIT PROCESS
;
; Restore interrupts
;
PWRWNE      BSR        ENBINTS
            LEA        FLAGS+1,A0
            BTST       #SENDLF,(A0)
            BNE.S      PWRWAIT                   ;SEND OUT LF CHAR
            BRA.S      PRNWR                     ;SEE IF ANOTHER USER CHAR
  page
;
; GETNXTC - Get the next character to send out.  If LF flag set, next char is
;           a LF character ($0A).
;           ENTRY : (A0) = address of FLAGS+1
;                   (A3) = address of next char in user's buffer
;           EXIT  : (D1) = the next character to send to printer
;
GETNXTC     BTST       #SENDLF,(A0)              ;IN AUTO LINE FEED MODE?
            BEQ.S      GNCNALF                   ;NO, GIVE NEXT CHAR
;
            MOVEQ      #LF,D1                    ;YES, SEND A LF
            BCLR       #SENDLF,(A0)              ;DID OPERATION
            BRA.S      GNCEXIT
;
GNCNALF     MOVE.B     (A3)+,D1                  ;GET NEXT FROM USER BUFFER
PWREXIT                                          ;USED BY PRNWR ALSO
GNCEXIT     RTS
;
; CHKCR -   Check next character to be sent.  If it is a CR ($0D) and Mode flag
;           specifies Auto Line Feed then set the SENDLF flag.
;           ENTRY : (A0) = address of FLAGS+1
;                   (D1) = Next character to send
;                   (D5) = Mode flag
;
CHKCR       TST.W      D5                        ;MODE FLAG IN AUTO LINEFEED MODE
            BNE.S      CCREXIT                   ;NO
;
            CMPI.B     #CR,D1                    ;NEXT CHAR A CARRIAGE RETURN
            BNE.S      CCREXIT                   ;NO
;
            BSET       #SENDLF,(A0)
CCREXIT     RTS
  page
;
; STRTXMIT - start xmit interrupt process by enabling UART to interrupt
;            on transmit buffer empty.
; STOPXMIT - stop xmit interrupt process by disabling UART to interrupt
;            on transmit buffer empty.
;
STRTXMIT
            MOVEQ      #XMITENB,D1               ;ENABLE XMIT INT
            BRA.S      SXTGETB
STOPXMIT
            MOVEQ      #XMITDIS,D1               ;DISABLE XMIT INT
;
SXTGETB     BSR        GETBASE                   ;GET UART BASE ADDRESS
            MOVE.B     CMDREGI(A0),D0            ;GET CURRENT CMD REG
            ANDI.B     #CLRD3D2,D0               ;CLEAR BITS D3 & D2
            OR.B       D1,D0                     ;DON'T CHANGE OTHER BITS
            MOVE.B     D0,CMDREGI(A0)            ;SAVE CHANGED CMD REG
            RTS
  page
; DCOMINT - DataCom Interrupt routine for XMIT/RCV interrupts.
;
DCOMINT     MOVEM.L    D0-A6,-(SP)               ;SAVE ALL REGISTERS
            BSR        GETBASE                   ;GET UART BASE ADDRESS
;
; If Receive interrupt then see if should process character.
;
            MOVE.B     STATRI(A0),D0             ;GET STATUS OF UART
            BTST       #RCVBF,D0                 ;TEST FOR RECEIVE BUFFER FULL
            BNE.S      DCIRCVC                   ;PROCESS RECEIVED CHAR
;
; Not Receive, if Transmit interrupt then see if can send character
;
            BTST       #XMITBE,D0                ;XMIT BUFFER EMPTY?
            BEQ.S      DCIEXIT                   ;NO, UNKNOWN INTERRUPT - EXIT
DCIPX       BSR.S      PRXMIT                    ;YES, PROCESS XMIT
DCIEXIT     MOVEM.L    (SP)+,D0-A6               ;EXIT-RESTORE REGISTERS
PTRRTE      RTE                                  ;EXIT INTERRUPT
;
; process received character
;
DCIRCVC     MOVE.B     DATAREG(A0),D0            ;GET CHAR/CLEARS INTERRUPT
            BCLR       #BITD7,D0                 ;CLEAR D7 OF CHAR JUST IN CASE
            LEA        PTFLAGS+1,A1              ;IF LINE TYPE OF HANDSHAKE
            BTST       #LINE,(A1)                ;BETWEEN PRINTER & DRIVER
            BNE.S      DCIEXIT                   ;THEN IGNORE CHARACTER
;
            LEA        FLAGS+1,A2                ;SAVE COPY OF BUSY FLAG
            MOVE.B     (A2),D1
;
; If XON/XOFF handshake then see if Char is a XON or a XOFF
;
            BTST       #XONXOFF,(A1)
            BEQ.S      DCICHKA                   ;NOT XON/XOFF, SEE IF ENQ/ACK
            CMPI.B     #XON,D0                   ;IS IT XON?
            BNE.S      DCIXOFF                   ;NO
            BCLR       #BUSY,(A2)                ;YES, BUSY := FALSE
            BRA.S      DCITSTB                   ;SEE IF SHOULD START XMIT
DCIXOFF     CMPI.B     #XOFF,D0                  ;IS IT XOFF CHAR?
            BNE.S      DCIEXIT                   ;NO, IGNORE CHAR
            BSET       #BUSY,(A2)                ;YES, BUSY := TRUE
            BRA.S      DCIEXIT                   ;EXIT - STOPPED XMIT
;
; must be ENQ/ACK handshake check for ACK
;
DCICHKA     CMPI.B     #ACK,D0                   ;IS IT ACK?
            BNE.S      DCIEXIT                   ;NO,IGNORE CHARACTER
            BCLR       #BUSY,(A2)                ;YES, BUSY := FALSE
;
; If was Busy before then try to start up XMIT process

DCITSTB     BTST       #BUSY,D1                  ;CHECK SAVED BUSY
            BEQ.S      DCIEXIT                   ;WASN'T BUSY BEFORE
            BSR        STRTXMIT                  ;ELSE START XMIT PROCESS
            BRA.S      DCIEXIT
  page
; PRXMIT - process transmission interrupt
;           ENTRY : (A0) = UART Base address
;
PRXMIT      LEA        FLAGS+1,A1
            BTST       #ENQFLG,(A1)              ;SEE IF SHOULD SEND A ENQ
            BEQ.S      PXTNENQ                   ;NO
            BCLR       #ENQFLG,(A1)              ;SEND THE ENQ CHAR
            MOVE.B     #ENQ,DATAREG(A0)          ;CLEARS INTERRUPT
            BRA.S      PXTEXIT                   ;DONE
;
; If (NOT Busy) and (NOT BufferEmpty) then send next char from buffer to printer
;
PXTNENQ     BTST       #BUSY,(A1)
            BNE.S      PXTCLRI                   ;IF WAS BUSY OF BUFFER EMPTY
            BTST       #BUFMTY,(A1)              ;THEN CLEAR INTERRUPT & EXIT
            BNE.S      PXTCLRI
;
            LEA        FRONT,A5
            MOVEA.L    (A5),A2                   ;FRONT OF QUEUE PTR
            LEA        BUFFER+BUFLEN,A4
            MOVE.B     (A2)+,DATAREG(A0)         ;SEND NEXT CHAR
;
            CMPA.L     A2,A4                     ;IF FRONT POINTS BEYOND THE
            BNE.S      PXTNOWR                   ;BUFFER WRAP PTR BACK TO
            LEA        BUFFER,A2                 ;BEGINNING OF BUFFER
PXTNOWR     MOVE.L     A2,(A5)                   ;SAVE UPDATED FRONT PTR
            CMPA.L     REAR,A2                   ;IF FRONT=REAR THEN BUFFER
            BNE.S      PXTNE                     ;EMPTY := TRUE
            BSET       #BUFMTY,(A1)
PXTNE       BCLR       #BUFFUL,(A1)              ;BUFFER FULL ALWAYS FALSE
            LEA        FREESPC,A0
            ADDI.W     #1,(A0)                   ;1 BYTE MORE OF FREE SPACE
;
; if handshake method is ENQ/ACK then see if should send ENQ next time
;
            LEA        PTFLAGS+1,A4
            BTST       #ENQACK,(A4)
            BEQ.S      PXTEXIT                   ;NOT ENQ/ACK HANDSHAKE
;
            LEA        COUNT,A4                  ;COUNT := COUNT + 1
            MOVE.W     (A4),D0
            ADDQ.W     #1,D0
            CMP.W      #80,D0                    ;IF SENT 80 CHARS THEN SEND A ENQ
            BNE.S      PXTSCNT                   ;NO, SAVE CURRENT COUNT
            CLR.L      D0                        ;COUNT := 0
            BSET       #ENQFLG,(A1)              ;SHOW SEND ENQ STATE
            BSET       #BUSY,(A1)                ;BUSY UNTIL RECEIVE ACK
PXTSCNT     MOVE.W     D0,(A4)                   ;SAVE COUNT
PXTEXIT     RTS
;
; Can't send characters to Printer, stop xmit buffer empty interrupt
;
PXTCLRI     BRA        STOPXMIT                  ;STOP XMIT INT & EXIT
  page
;
; DCTLINT - Data Com Control interrupt service routine.
;           Ignores the interrupt if wasn't a DataCom Control interrupt,
;           therefore an Apple slot interrupt, or if NOT Line type
;           handshake method.  Always clears the interrupt.
;
DCTLINT     MOVEM.L    D0-A6,-(SP)               ;SAVE REGISTERS
            BSR.S      INITDCC                   ;CLEAR INTERRUPT/(D0) = PORT A
;
; If (type of handshake <> Line) then exit
;
            LEA        PTFLAGS+1,A1
            BTST       #LINE,(A1)
            BEQ.S      DCLEXIT                   ;NOT LINE HANDSHAKE, EXIT
;
; Determine which Line is used as Busy line Port A
;
            BSR.S      FINDLIN                   ;NEEDS A1 = PTR TO PTFLAGS+1
;
;set or clear Busy depending on state of line and whether it's Busy inverted or not
;
            LEA        FLAGS+1,A2
            MOVE.B     (A2),D1                   ;SAVE BUSY FLAG
            MOVE.W     #DISINT4,SR               ;DISABLE INTS
            BSET       #BUSY,(A2)                ;ASSUME LINE IS BUSY = TRUE
            BSR.S      TSTLINE                   ;TEST LINE & INVERTED FLAG
            BNE.S      DCLEXIT                   ;IS BUSY
            BCLR       #BUSY,(A2)
;
; if wasn't Busy before then start up transmission process
;
            BTST       #BUSY,D1                  ;TEST SAVED BUSY STATE
            BEQ.S      DCLEXIT                   ;WASN'T BUSY
            BSR        STRTXMIT                  ;START XMIT IF BUFFER NOT EMPTY
;
DCLEXIT     MOVEM.L    (SP)+,D0-A6               ;EXIT-RESTORE REGISTERS
            RTE
  page
;
; INITDCC - Turnoff DataCom Control interrupt by toggling IOX.  Will also
;           stop interrupt if unknown Apple slot device is interrupting.
;           ASSUMES :  DDR for Port A is untampered and set at $80
;           Exit : (D0) = Port A with IOX toggled
;                  (A0) = address of Port A
;
INITDCC     LEA        NHIRA.L,A0
            MOVE.B     (A0),D0                   ;READ PORTA W/O HANDSHAKE
            BCHG       #7,D0                     ;TOGGLE IOX
            MOVE.B     D0,(A0)                   ;WRITE OUT CHANGED IOX
            RTS
;
; CALLIDCC - Call INITDCC when driver unmounted and get a DataCom Control
;            interrupt.  Toggles IOX to clear level 1 interrupt.
;
CALLIDCC    MOVEM.L    D0/A0,-(SP)               ;SAVE REGS USED BY INITDCC
            BSR.S      INITDCC
            MOVEM.L    (SP)+,D0/A0               ;RESTORE REGS
            RTE
  page
;
; FINDLIN - Find which Line is used for Handshaking in Port A
;           ENTRY : (A1) = address of PTRFLAGS+1
;           EXIT  : (D3) = Bit # in Port A specifying line used for Busy
;
FINDLIN     MOVEQ      #1,D3                     ;BIT NUMBER IN PORT A CORRESPONDING TO
            MOVEQ      #CTSLIN,D4                ;FLAG BIT NUMBER
;
; Assumes that it will always find a line flag set
;
FLNLOOK     BTST       D4,(A1)                   ;IS BIT SET?
            BNE.S      FLNGOT                    ;YES, D3 PORT A BIT FOR DC 0
            ADDQ.B     #2,D3
            ADDQ.B     #1,D4                     ;TRY NEXT BIT FLAG
            CMPI.B     #DCDLIN+1,D4              ;DID LAST FLAG
            BNE.S      FLNLOOK                   ;NO
;
; if (DataCom flag is set) then bit# := bit# + 1 - DC 1 bits in Port A are next bit up
;
FLNGOT      BTST       #DATACOM,(A1)
            BEQ.S      FLNEXIT
            ADDQ.B     #1,D3
FLNEXIT     RTS
;
; TSTLINE - test Port A line used for Busy and the inverted flag to show if
;           Busy or NOT Busy.
;           ENTRY : (A1) = address of PTRFLAGS+1
;                   (D0) = Port A
;                   (D3) = bit number in Port A of Line used by Busy
;           EXIT  : (NE) = Busy
;                   (EQ) = NOT Busy
;
TSTLINE     BTST       D3,D0                     ;Create Line Boolean
            SNE        D4
            BTST       #INVBUSY,(A1)             ;Create Inverted Boolean
            SNE        D5
            EOR.B      D4,D5                     ;IF RESULT IS $FF THEN BUSY
            RTS
  page
;
; PRNCLR - UNITCLEAR
;           Initialize Buffer to empty.  Initialize Communications control
;           variables.  Initialize UART from Printer Control Table.
;
PRNCLR      BSR        DISINTS                   ;DISABLE INTERRUPTS
            BSR        INITBUF                   ;INIT BUFFER & CONTROL VARIABLES
            BSR        SETUART                   ;INIT UART FROM CONSTANTS & TABLE
            BSR        ENBINTS                   ;ENABLE INTERRUPTS
            RTS
;
; PRNBSY - UNITBUSY
;           Return the state of the Printer, the Busy flag.
;           True means printer will not except characters.
;
PRNBSY      LEA        FLAGS+1,A0                ;UNITBUSY
            BTST       #BUSY,(A0)
            SNE        D0                        ;MAKE BOOLEAN
            ANDI.B     #1,D0                     ;MAKE PASCAL BOOLEAN
            RTS
  page
;
; PRNUNMT - UNITUNMOUNT
;           Turnoff interrupt capabilities of Printer driver & current DataCom
;
PRNUNMT     BSR        DISINTS                   ;DISABLE INTERRUPTS
;
            BSR        GETBASE                   ;GET UART BASE
            MOVE.B     #TURNOFF,CMDREGI(A0)      ;TURNOFF UART
;
; have vectors point to a RTe instruction
;
            LEA        CALLIDCC,A0               ;HAVE DATA COM CONTROL INT
            MOVE.L     A0,VEC1.W                 ;RESET IOX TO CLEAR THE INT
            LEA        VEC4.W,A1                 ;ASSUME DC0 (VECTOR 4)
            LEA        PTFLAGS+1,A2
            BTST       #DATACOM,(A2)             ;IS IT DC0
            BEQ.S      PUNUSE0                   ;YES
            LEA        VEC2.W,A1                 ;DC1 IS VECTOR 2
PUNUSE0     MOVE.L     A0,(A1)                   ;PTR TO RTE IN CURRENT DC VECTOR
;
; Restore Interrupts
;
            BSR        ENBINTS
            RTS
  page
;
; PRNST - UNITSTATUS
; call the Table change or buffer free Functions
;
PRNST       CMPI.W     #TBLSTATE,D2              ;VALID FUNCTION CODE
            BHI.S      PSTERR                    ;NO
            MOVE.W     (A3),D0                   ;GET PARAMETER
            LEA        PSTTBL,A1                 ;TURN THE FUNCTION CODE INTO
            LSL.W      #1,D2                     ;AN INDEX TO THE FUNCTION
            MOVE.W     0(A1,D2.W),D2
            JMP        0(A1,D2.W)                ;DO FUNCTION
;
; Invalid Function Code Error
;
PSTERR      MOVE.W     #INVFNC,D7
            RTS
;
; THE PRINTER DRIVER JUMP TABLE
;
PSTTBL      DATA.W     STBUFFR-PSTTBL            ;BUFFER FREE SPACE
            DATA.W     STBAUD-PSTTBL             ;SET BAUD RATE
            DATA.W     STPRITY-PSTTBL            ;SET PARITY
            DATA.W     STDTACOM-PSTTBL           ;SET DATA COM
            DATA.W     STWRDSZ-PSTTBL            ;SET WORD SIZE
            DATA.W     STHNDSK-PSTTBL            ;SET HANDSHAKE METHOD
            DATA.W     STTBLST-PSTTBL            ;GET TABLE STATE
  page
;
; STBUFFR - Return to the user the Free space in the Queue (Buffer).
;
STBUFFR     BSR        DISINTS                   ;DISABLE INTERRUPTS
            MOVE.W     FREESPC,(A3)              ;GIVE USER FREE SPACE
            BSR        ENBINTS                   ;ENABLE INTERRUPTS
            RTS
  page
;
; STBAUD - Set the Baud Rate
;
STBAUD
            CMPI.W     #MAXBAUD,D0               ;IS IT A VALID PARAMETER
            BHI.S      SETERR                    ;NO
;
            LEA        BAUDR,A0                  ;WHERE TO PUT VALUE
            LEA        BAUDCNV,A1                ;CONVERSION ARRAY
            BRA.S      SAVPARM                   ;SAVE CONVERTED PARAMETER
;
; STPRITY - Set the Parity
;
STPRITY
            CMPI.W     #MAXPRTY,D0               ;IS IT A VALID PARAMETER
            BHI.S      SETERR                    ;NO
;
            LEA        PARITY,A0                 ;WHERE TO PUT VALUE
            LEA        PRTYCNV,A1                ;CONVERSION ARRAY
            BRA.S      SAVPARM                   ;SAVE CONVERTED PARAMETER
;
; STWRDSZ - Set the word size to transmit (7 or 8)
;
STWRDSZ
            CMPI.W     #MAXWRDS,D0               ;IS IT A VALID PARAMETER
            BHI.S      SETERR                    ;NO
;
            LEA        WORDSIZ,A0                ;WHERE TO PUT VALUE
            MOVE.B     D0,(A0)                   ;PUT IN WORD SIZE VALUE
            BRA.S      RSTUART                   ;RESET UART FROM TABLE
  page
;
; common code to STBAUDR, STPRITY, STWRDSZ, STDTACOM, & STHNDSK
;
SAVPARM     MOVE.B     0(A1,D0.W),(A0)           ;SAVE CONVERTED PARAMETER
;
RSTUART     BSR        DISINTS                   ;DISABLE INTERRUPTS
RSTUART1    BSR        SETUART                   ;SETUP UART FROM TABLE
            BSR        ENBINTS                   ;ENABLE INTERRUPTS
            RTS
;
; Invalid Parameter error
;
SETERR      MOVE.W     #INVPRM,D7
            RTS
  page
;
; STDTACOM  - Change the DataCom being used.  Either DataCom 0 or 1.
;
STDTACOM
            CMPI.W     #MAXDTCM,D0               ;IS IT A VALID PARAMETER
            BHI.S      SETERR                    ;NO
;
; change Table Flags
;
            LEA        PTFLAGS+1,A0              ;FLAGS
            BSET       #DATACOM,(A0)             ;ASSUME PARAMETER=1
            TST.B      D0
            BNE.S      SDCCNGV                   ;IS DC 1, CHANGE VECTORS
            BCLR       #DATACOM,(A0)
;
; Change interrupt vectors.  The old vector point at a RTE instruction.
;
SDCCNGV     BSR        DISINTS                   ;DISABLE INTERRUPTS
            BSR        SETDCVEC                  ;SET VECTORS
            BRA.S      RSTUART1
  page
;
; STHNDSK - Set Handshake type.  Convert parameter into the flags and put these
;           flag values into the Printer Control Table.  Don't need to reset
;           UART.
;
STHNDSK
            CMPI.W     #MAXHNDS,D0               ;IS IT A VALID PARAMETER
            BHI.S      SETERR                    ;NO
;
            LEA        HNDSCNV,A1                ;CONVERSION ARRAY
            LEA        PTFLAGS+1,A0              ;FLAGS
            MOVE.B     0(A1,D0.W),D1             ;CONVERTED VALUE
            MOVE.B     (A0),D2                   ;GET FLAG BYTE
            ANDI.B     #DCMFLGM,D2               ;REMOVE CURRENT HANDSHAKE FLAGS
            OR.B       D1,D2                     ;PUT IN NEW FLAGS
            MOVE.B     D2,(A0)                   ;RESTORE FLAGS
            RTS
  page
;
; STTBLST - Return to the user in the parameter block the state of the Printer Control Table.
;       ParameterBlock = record
;                          BaudRate  : integer; {range = 0..6}
;                          Parity    : integer; {range = 0..4}
;                          DataCom   : integer; {range = 0..1}
;                          WordSize  : integer; {range = 0..1}
;                          HandShake : integer; {range = 0..7}
;                        end;
;
STTBLST     CLR.L      D1                        ;MAKE SURE NO GARBAGE IN REGISTER
;
; GET BAUD RATE
;
            MOVE.W     #MAXBAUD,D0               ;MAX BAUD RATE PARAMETER VALUE
            MOVE.B     BAUDR,D1                  ;CURRENT TABLE VALUE
            LEA        BAUDCNV,A0                ;CONVERT TO INTEGER RANGE
            BSR.S      GETVAL
;
; GET PARITY
;
            MOVE.W     #MAXPRTY,D0               ;MAX PARITY PARAMETER VALUE
            MOVE.B     PARITY,D1                 ;CURRENT TABLE VALUE
            LEA        PRTYCNV,A0                ;CONVERT TO INTEGER RANGE
            BSR.S      GETVAL
;
; GET DATACOM
;
            MOVE.B     PTFLAGS+1,D1              ;GET FLAG BYTE WITH DATACOM FLAG IN IT
            LSR.B      #7,D1                     ;TURN FLAG INTO A 0 OR A 1 INTEGER
            MOVE.W     D1,(A3)+
;
; GET WORD SIZE
;
            MOVE.B     WORDSIZ,D1
            MOVE.W     D1,(A3)+
;
; GET HANDSHAKE
;
            MOVE.W     #MAXHNDS,D0               ;MAX HANDSHAKE PARAMETER VALUE
            MOVE.B     PTFLAGS+1,D1              ;CURRENT TABLE VALUE
            BCLR       #DATACOM,D1               ;REMOVE DATACOM FLAG
            LEA        HNDSCNV,A0                ;CONVERT TO INTEGER RANGE
;
; GET PARAMETER VALUE AN PUT IN PARAMETER BLOCK
;
GETVAL      CMP.B      0(A0,D0.W),D1             ;SEE WHICH CONVERSION VALUE = CURRENT VALUE
            DBEQ       D0,GETVAL                 ;THE INDEX OF ONE = IS THE PARAMETER VALUE TO
            MOVE.W     D0,(A3)+                  ;RETURN TO USER IN PARAMETER BLOCK
            RTS
  page
;
;
; constant data area
;
; Conversion arrays for Set functions of Unitstatus
;
BAUDCNV     DATA.B     6,7,8,$A,$C,$E,$F         ;BAUD RATE
; 6=300,7=600,8=1200,A=2400,C=4800,E=9600,F=19200
;
PRTYCNV     DATA.B     0,1,3,5,7                 ;PARITY
; 0=DISABLED,1=ODD,3=EVEN,5=MARK XMIT/NO RCV,7=SPACE XMIT/NO RCV
;
HNDSCNV     DATA.B     $49                       ;LINE/CTS/INV
            DATA.B     $09                       ;LINE/CTS/NOT INV
            DATA.B     $51                       ;LINE/DSR/INV
            DATA.B     $11                       ;LINE/DSR/NOT INV
            DATA.B     $61                       ;LINE/DCD/INV
            DATA.B     $21                       ;LINE/DCD/NOT INV
            DATA.B     $02                       ;XON/XOFF
            DATA.B     $04                       ;ENQ/ACK
;
; default Printer Control Table
;           values for the Epson MX-100 printer with serial interface
;
            DATA.B     0                         ;FILL
DEFPCT      DATA.B     $0C                       ;BAUD RATE-4800
            DATA.B       0                       ;PARITY-DISABLED
            DATA.B       0                       ;WORD SIZE = 8 BITS (1=7 BITS)
            DATA.W     $91                       ;LINE/DSR/NOT INV/DATACOM 1
  page
;
; Variable data area
;
; Printer Control Table
;
            DATA.B     0                         ;FILL
PCTLTBL
BAUDR       DATA.B     0                         ;BAUD RATE
PARITY      DATA.B     0                         ;PARITY
WORDSIZ     DATA.B     0                         ;WORD SIZE
PTFLAGS     DATA.W     0                         ;FLAGS-HANDSHAKE TYPE & DATACOM
;
; save areas for current SR
;
SAVESR1     DATA.W     0
;
; buffer variables
;
FRONT       DATA.L     0                         ;BUFFER QUEUE FRONT POINTER
REAR        DATA.L     0                         ;BUFFER QUEUE REAR POINTER
COUNT       DATA.W     0                         ;COUNT OF CHARS BETWEEN ENQ'S
FREESPC     DATA.W     0                         ;COUNT IN BYTES OF BUFFER FREE SPACE
FLAGS       DATA.W     0                         ;INTERNAL FLAGS
;
; The Printer Driver Output Buffer - 256 bytes
;
BUFFER      DATA.L     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
            DATA.L     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
            DATA.L     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
            DATA.L     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
BUFLEN      EQU        %-BUFFER                  ;BUFFER LENGTH
            END        PRNTRDRV
