
; SCCSID = @(#)prthwiat.asm     6.21 92/03/24
;/**********************************************************************
;/*                                                                    *
;/*                       IBM/Microsoft Confidential                   *
;/*                                                                    *
;/*                 Copyright (c) IBM Corporation  1987, 1990          *
;/*                 Copyright (c) Microsoft Corp.  1987, 1990          *
;/*                           All Rights Reserved                      *
;/*                                                                    *
;/**********************************************************************
 TITLE PRINTDD - PRINTER DEVICE DRIVER HARDWARE ROUTINES
 NAME PRINTDD
PAGE ,132
.286C

;***********************************************************************
; CODING CONVENTIONS
; all psuedo-ops, equates, documentation, publics, and externs are in uppercase.
; all code and data names are in lowercase.
;
; ROUTINES IN THIS MODULE:
;       INITIALPRT,HYBRID
;       PRTDEINSTALL
;       PRINTBLOCK
;       PRT_TRANSMIT
;       SEARCHANDSTEAL
;       eInt_1
;       eInt_2
;       eInt_3
;       PRTINT05
;       PRTINT07
;       STARTTIMER
;       STOPTIMER
;       PRTTIMGR
;       TIMERINTERRUPT
;       CHANGEIRQTOST
;       CHECKSTATUS
;       INTERRUPT
;       SENDCHAR
;       SENDTODEV
;       PRT_TIMEOUT_1
;       PRT_TIMEOUT_2
;       PRT_TIMEOUT_3
;       PRT_TIMEOUT
;       CLEANUP
;       PRTERP
;       GETSTATUS,HYBRID
;       PRTINIT
;       prtdeterminehardware
;       RM_PRT_RegisterResources
;       PARSECMDLINE
;
;       MODIFICATION HISTORY
;
;   87416       07/11/94   SLC (ChgTeam)   Added one line to proc interrupt
;                                          to disable interrupts in
;                                          interrupt01 before returning to
;                                          interrupt manager.
;
;***********************************************************************

        .XCREF
        .XLIST
        INCLUDE basemaca.inc            ; VARIOUS MACRO'S (BREAK, LJC, ETC.)
        INCLUDE osmaca.inc
        INCLUDE devsym.inc
        INCLUDE devhlp.inc              ; DEFINITION OF DEVICE HELP CALLS.
        INCLUDE struc.inc               ; STRUCTURED MACRO SUPPORT
        INCLUDE infoseg.inc             ; STRUCTURES DEFINING THE INFOSEG
        INCLUDE pvwxport.inc            ; PERFVIEW STRUCS AND EQUATES.
        INCLUDE iniequ.inc              ; CONFIG.SYS DEFAULT EQUATES
        INCLUDE iodelay.inc             ; IODELAY MACROS
        .LIST
        .CREF
        INCLUDE prtdd.inc               ; PRINTER DEVICE DRIVER INCLUDE FILE
        INCLUDE prtdd1.inc              ; PRINTER DEVICE DRIVER INCLUDE FILE
        INCLUDE prtinit.inc             ; PRINTER DEVICE DRIVER INCLUDE FILE
        INCLUDE prteisa.inc             ; EISA SUPPORT EQUATES

BREAK <DATA FOR THE PRINTER DEVICE DRIVER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: PRTDATA                                           */
;/*                                                                    */
;/* DESCRIPTIVE NAME: PRINTER DEVICE DRIVER DATA DECLARATIONS          */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
DSEG    SEGMENT public  'data'

        EXTRN   glinfoseg:DWORD
        EXTRN   dosvar:DWORD
        EXTRN   device_help:DWORD
        EXTRN   _Device_Help:DWORD
        EXTRN   numofprts:BYTE
        EXTRN   flags:BYTE
        EXTRN   perprtarea:BYTE
        EXTRN   moncache:BYTE
        EXTRN   plptname:BYTE
        PUBLIC  irq5index
        PUBLIC  irq7owner
        PUBLIC  timeoutval
        PUBLIC  deinstallflg
        PUBLIC  doserrors
        PUBLIC  fIRQStolen
                EVEN
irq5index       dw      ?               ; INDEX INTO PERPRTDATA AREA
irq7owner       dw      ?               ; INDEX INTO PERPRTDATA AREA
timeoutval      dw      ?               ; TIME OUT FOR 1 SECOND (TICKS)
timercount      db      0               ; NUMBER OF TIMER TICKS SINCE IRQ
fIRQStolen      db      0               ; FLAG SET TO 1 IF IRQ WAS STOLEN
timerneeded     db      0               ; BIT 0 = 0 - TIMER NOT NEEDED FOR LPT1
                                        ; BIT 0 = 1 - TIMER NEEDED FOR LPT1
                                        ; BIT 1 = 0 - TIMER NOT NEEDED FOR LPT2
                                        ; BIT 1 = 1 - TIMER NEEDED FOR LPT2
                                        ; BIT 2 = 0 - TIMER NOT NEEDED FOR LPT3
                                        ; BIT 2 = 1 - TIMER NEEDED FOR LPT3
deinstallflg    db      0               ; DEVICES PDD NO LONGER SUPPORTS
                                        ; 0 = LPT1, 1 = LPT2, 2 = LPT3
bustype         dw      0               ; BUS TYPE 1=ISA, 2=EISA, and 4=MC
                EVEN
                ; index 0   2    4    6    8    10
blksiz          dw      8, 32, 128, 256, 512, 1024      ; BURST RATE IN BYTES
allowedpending  dw      1,  6,  32,  60, 118,  230      ; MAX OTHER IRQS PENDING
nextindx        dw      0,  0,   2,   4,   0,    0      ; NEXT LOWER BURST RATE
MAXBLKINDX      EQU     (allowedpending-blksiz-2)       ; MAX BURST RATE

blkindx         dw      MAXBLKINDX                      ; BURST RATE INDEX
pendirq         dw      0                               ; PENDING IRQ COUNTER

doserrors       dw REQDONE,NOACCESS,PAPEROUTERR,WRITEFAULT

PUBLIC  data_block                      ; PERFVIEW DATA STRUCTURES
data_block      LABEL   BYTE
db_length       dw      DATA_BLOCK_LEN  ; DATA BLOCK TOTAL LENGTH IN BYTES
                dw      0               ; IDENTIFIER (ORDINAL)
                dd      0               ; INSTANCE ID/ GROUP ID
                dd      RPC_FL_16BIT    ; FLAGS
                dd      0               ; SEMAPHORE OR SEMAPHORE HANDLE
                dd      0               ; POINTER TO TIMER ADD FUNCTION
                dd      0               ; POINTER TO TIMER SUB FUNCTION
DATA_BLOCK_LEN  EQU     $ - data_block  ; LENGTH OF DATA_BLOCK

prfvw_ctrs      dd      12 dup (0)      ; PERFVIEW PER DEVICE COUNTER AREA

PUBLIC  text_block
text_block      LABEL   BYTE
                dd      TBH_VER_2_0_0_0 ; VERSION NUMBER
; TEXT BLOCK IDENTIFIER
                dw      0               ; BLOCK INSTANCE ID
                dw      0               ; BLOCK GROUP ID
; TEXT BLOCK GROUP NAME
                dd      0               ; FLAGS
                dw      0               ; SIZE
                dw      0               ; MESSAGE ID
                dw      OFFSET driver_name ; OFFSET OF DEVICE DRIVER NAME
                dw      SEG driver_name ; SEGMENT OF DEVICE DRIVER NAME
; TEXT BLOCK INSTANCE NAME
                dd      0               ; FLAGS
                dw      0               ; SIZE
                dw      0               ; MESSAGE ID
                dd      0               ; DEFAULT MESSAGE
                dd      0               ; MESSAGE FILE NAME
                dd      0               ; HELP FILE NAME
tb_totalctrs    dd      0               ; NUMBER OF TIMERS AND COUNTERS
                dw      OFFSET name_block ; OFFSET OF NAME BLOCK
                dw      SEG name_block  ; SEGMENT OF NAME BLOCK

dataend         dw      OFFSET monbuffers ; PTR TO END OF RESIDENT DATA SEGMENT
                                          ; ALL DATA AFTER THIS POINT IS
                                          ; RELEASED AFTER INITIALIZATION

monbuffers      db      (MAXPRINTERS * 2) * MaxPBUFCount DUP (00H)

;  ALL DATA AFTER THIS POINT IS RELEASED AFTER INITIALIZATION

NUMBER_TMRS_CTRS EQU    3               ; NUMBER OF TIMERS AND COUNTERS

str1            db      'LPT1 Write Requests',0
str2            db      'LPT1 Write Time',0
str3            db      'LPT1 Write Bytes',0
str4            db      'LPT2 Write Requests',0
str5            db      'LPT2 Write Time',0
str6            db      'LPT2 Write Bytes',0
str7            db      'LPT3 Write Requests',0
str8            db      'LPT3 Write Time',0
str9            db      'LPT3 Write Bytes',0

PUBLIC  name_block
name_block      LABEL   BYTE
                dd      PVW_CT_CNT      ; NUMBER OF WRITES COUNTER
                dw      SIZE num_writes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str1     ; OFFSET OF COUNTER NAME
                dw      SEG str1        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_TIMR     ; WRITE TIME COUNTER
                dw      SIZE TIMR       ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str2     ; OFFSET OF COUNTER NAME
                dw      SEG str2        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_CNT      ; NUMBER OF BYTES WRITTEN COUNTER
                dw      SIZE write_bytes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str3     ; OFFSET OF COUNTER NAME
                dw      SEG str3        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_CNT      ; NUMBER OF WRITES COUNTER
                dw      SIZE num_writes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str4     ; OFFSET OF COUNTER NAME
                dw      SEG str4        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_TIMR     ; WRITE TIME COUNTER
                dw      SIZE TIMR       ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str5     ; OFFSET OF COUNTER NAME
                dw      SEG str5        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_CNT      ; NUMBER OF BYTES WRITTEN COUNTER
                dw      SIZE write_bytes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str6     ; OFFSET OF COUNTER NAME
                dw      SEG str6        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_CNT      ; NUMBER OF WRITES COUNTER
                dw      SIZE num_writes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str7     ; OFFSET OF COUNTER NAME
                dw      SEG str7        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_TIMR     ; WRITE TIME COUNTER
                dw      SIZE TIMR       ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str8     ; OFFSET OF COUNTER NAME
                dw      SEG str8        ; SEGMENT OF COUNTER NAME

                dd      PVW_CT_CNT      ; NUMBER OF BYTES WRITTEN COUNTER
                dw      SIZE write_bytes ; SIZE OF COUNTER
                dw      0
                dw      OFFSET str9     ; OFFSET OF COUNTER NAME
                dw      SEG str9        ; SEGMENT OF COUNTER NAME

PUBLIC driver_name
driver_name     db      'Parallel Port Device Driver',0
ddname          db      'print01.sys',0

DSEG    ENDS

SWAPSEG SEGMENT PUBLIC  'code'
        ASSUME  CS:SWAPSEG,DS:DSEG,ES:NOTHING,SS:NOTHING

        EXTRN   PLPTCMD_Entry:FAR

BREAK <INITAILIZE PRINTER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: INITIALPRT                                        */
;/*                                                                    */
;/* DESCRIPTIVE NAME: INITIALIZE PRINTERS                              */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS ROUTINE IS TO INITIALIZE THE        */
;/*           PRINTER WHOSE DEVICE ADDRESS IS IN AX.                   */
;/*                                                                    */
;/* NOTES:                                                             */
;/*                                                                    */
;/* ENTRY POINT: INITIALPRT                                            */
;/*    LINKAGE : CALL NEAR OR FAR                                      */
;/*                                                                    */
;/* INPUT:     DX = 0FFH IF CALLED FROM PRTDD INITIALIZE ROUTINE.      */
;/*                                                                    */
;/* EXIT-NORMAL:   N/A                                                 */
;/*                                                                    */
;/* EXIT-ERROR:    N/A                                                 */
;/*                                                                    */
;/* INTERNAL REFERENCES:  getstatus                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  ProcBlock                       */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure initialprt,HYBRID
        ASSUME  CS:SWAPSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        SaveReg <cx,dx>                                 ; SAVE REGISTERS
        mov     dx,[di].deviceaddr                      ; GET DEVICE ADDRESS
        inc     dx
        inc     dx                                      ; POINT TO CONTROL PORT
        mov     al,INITLINELOW                          ; SET INIT LINE LOW
        out     dx,al                                   ; SEND NEW CONTROL VALUE
        mov     ax,INITLOOPCNT                          ; LOOP COUNT
initprt:
        dec     ax
        jnz     initprt                                 ; LOOP UNTIL RESET DONE
        mov     al,INITLINEHIGH                         ; SET INIT LINE HIGH
        out     dx,al                                   ; SEND NEW CONTROL VALUE

        ; MUST WAIT UNTIL NOT BUSY BEFORE RETURNING TO USER OR USER'S NEXT
        ; REQUEST WILL FAIL.

        pop     dx                                      ; GET DD INIT FLAG
        push    dx

        test    [di].commonflags1,BOOTINIT              ; IS THIS BOOT TIME?
        jnz     initprt2                                ; JUMP IF IT IS

        ; PROPRINTER RETURNS WITH NORMAL STATUS BUT NEXT PRINT REQUEST WILL FAIL
        ; UNLESS THIS THREAD IS BLOCKED.

        mov     cx,BUSYRETRY                            ; LOOP CONTROL VARIABLE
initprt1:
        SaveReg <ax,bx,cx,dx,di>                        ; SAVE REGISTERS
        mov     bx,[di].dosoffset                       ; OFF. OF THIS REQ BLK
        inc     bx                                      ; MAKE BLOCK ID UNIQUE
        mov     ax,[di].dosrqseg                        ; SEG. OF THIS REQ BLK
        mov     cx,0FFH                                 ; TIME LIMIT LOW VALUE
        mov     di,0                                    ; TIME LIMIT HIGH VALUE
        mov     dh,BLOCKSLEEPNOTINT                     ; SLEEP NOTINTERRUPTIBLE
        mov     dl,DevHlp_ProcBlock                     ; BLOCK FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP TO BLOCK
        RestoreReg <di,dx,cx,bx,ax>                     ; RESTORE REGISTERS
        CALLFAR getstatus                               ; GET THE PORT STATUS
        test    ah,NOTBUSY                              ; IF DEVICE NOT BUSY
        jnz     initprt2                                ; THEN RETURN
        loop    initprt1                                ; SEE IF NOT BUSY
initprt2:
        RestoreReg <dx,cx>                              ; RESTORE REGISTERS
        ret
EndProc initialprt

BREAK <PRINT DEVICE DRIVER DEINSTALL ROUTINE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRTDEINSTALL                                     */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  PRINT DEVICE DRIVER DEINSTALL ROUTINE.          */
;/*                                                                    */
;/* FUNCTION:  THE FUNCTION OF THIS SUBROUTINE IS TO DEINSTALL THE     */
;/*            THE PRINTER DEVICE DRIVER ONE DEVICE AT A TIME.         */
;/*                                                                    */
;/* NOTES:  DEINSTALL COMES IN ONLY AT INIT TIME                       */
;/*         DEINSTALL OCCURS ON A DEVICE BASIS                         */
;/*         KERNEL INSURES NO MORE REQUESTS AFTER DEINSTALL            */
;/*         SPOOLER CANNOT BE ACTIVE YET                               */
;/*                                                                    */
;/* ENTRY POINT: PRTDEINSTALL                                          */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT:     ES = VIRTUAL  SEGMENT OF REQUEST BLOCK                  */
;/*            BX = VIRTUAL  OFFSET OF REQUEST BLOCK                   */
;/*            DI = OFFSET TO APPROPRIATE PERPRTDATA AREA              */
;/*                                                                    */
;/* REGISTERS USED: ALL REGISTERS SAVED BY THE KERNEL                  */
;/*                                                                    */
;/* EXIT-NORMAL:  ES:[BX].PktStatus = REQDONE                          */
;/*                                                                    */
;/* EXIT-ERROR:   N/A                                                  */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  RegisterPDD                     */
;/*    ROUTINES:                       FreeGDTSelector                 */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prtdeinstall near
        ASSUME  CS:SWAPSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        cmp     [di].accessctr,0                        ; NO ONE ACCESSING PDD
        je      prtdein1                                ; TRY TO DEINSTALL
        mov     es:[bx].PktStatus,UNKNOWNCMD            ; ELSE REFUSE TO DEIN
        jmp     SHORT prtdein3                          ; EXIT

prtdein1:
        SaveReg  <bx,es>                                ; SAVE REGISTERS
        mov     al,[di].deviceflag                      ; GET DEVICE FLAG
        or      deinstallflg,al                         ; SET DEINSTALL FLAG
        cmp     deinstallflg,DEINSTALLMSK               ; IF ALL DEV != DEINSTAL
        jne     prtdein2                                ; THEN RETURN

        ; DEREGISTER THE PLPT'S VDD SERVICES ENTRY POINT WITH VLPT.

        push    di                                      ; SAVE PER PRT AREA
        lea     si,plptname                             ; LOAD PLPT NAME
        push    0
        pop     es                                      ; MAKE NULL PTR
.386
        xor     edi,edi                                 ; MAKE NULL PTR
.286
        mov     dl,DevHlp_RegisterPDD                   ; REGISTERPDD FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
        pop     di

prtdein2:
        ; FREE GDT SELECTOR ALLOCATED AT DEVICE DRIVER INITIALIZATION TIME

        mov     ax,[di].gdtuserbuf                      ; GET GDT SELECTOR
        mov     dl,DevHlp_FreeGDTSelector               ; FREE GDT SEL FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
        mov     [di].gdtuserbuf,0                       ; CLEAR GDT SELECTOR

        ; FREE PRINTBUF GDT SELECTOR ALLOCATED AT DEVICE DRIVER INIT TIME

        mov     ax,[di].gdtprintbuf                     ; GET GDT SELECTOR
        mov     dl,DevHlp_FreeGDTSelector               ; FREE GDT SEL FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
        mov     [di].gdtprintbuf,0                      ; CLEAR GDT SELECTOR

        RestoreReg <es,bx>                              ; RESTORE REGISTERS

        mov     es:[bx].PktStatus,REQDONE               ; SET THE DONE BIT
prtdein3:
        ret
EndProc prtdeinstall

SWAPSEG ENDS

CSEG    SEGMENT public  'code'
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING

        EXTRN   sendmonerpkt:NEAR
        EXTRN   eisainit:NEAR
        EXTRN   prt_polling:NEAR
        EXTRN   prt_timeout_polling:NEAR
        EXTRN   _RM_PRT_CreateDriver:NEAR
        EXTRN   _RM_PRT_DestroyDriver:NEAR
        EXTRN   _RM_PRT_AllocPorts:NEAR
        EXTRN   _RM_PRT_DeallocPorts:NEAR
        EXTRN   _RM_PRT_AllocIRQ:NEAR
        EXTRN   _RM_PRT_DeallocIRQ:NEAR
        EXTRN   _RM_PRT_CreateAdapter:NEAR
        EXTRN   _RM_PRT_DestroyAdapter:NEAR

BREAK <PRINT A BLOCK OF CHARACTERS>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRINTBLOCK                                       */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Schedule a print block request.                 */
;/*                                                                    */
;/* FUNCTION:   This routine will throttle back PIO print requests     */
;/*             in order to improve mouse and keyboard usability when  */
;/*             printing is generating many interrupts.  Due to the    */
;/*             rotation of the 8259 programable interrupt controller, */
;/*             mouse and keyboard hardware interrupts have a lower    */
;/*             priority than the printer.                             */
;/*                                                                    */
;/* NOTES:                                                             */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  printblock:near                                  */
;/*                   call printblock                                  */
;/*                                                                    */
;/* INPUT:  ES = Virtual segment of Kernel request block               */
;/*         BX = Virtual offset of Kernel request block                */
;/*      DS:DI = Offset to appropriate perprtdata area                 */
;/*      IOACTIVE FLAG OR MONPRINTING FLAG SET TO ON                   */
;/*                                                                    */
;/* EXIT-NORMAL:  ES:[BX] = Kernel Request Block                       */
;/*               DS:[DI] = Perprtdata area                            */
;/*               DS:[SI] = ABIOS Request Block                        */
;/*               ES:[BX].Pktstatus = filled in.                       */
;/*               ES:[BX].IOCount = filled in.                         */
;/*               IOACTIVE FLAG SET TO OFF                             */
;/*               CANMONPKT FLAG SET TO OFF                            */
;/*                                                                    */
;/* EXIT-ERROR:   See exit normal above.                               */
;/*                                                                    */
;/* EFFECTS:                                                           */
;/*                                                                    */
;/* INTERNAL REFERENCES:  prt_transmit                                 */
;/*    ROUTINES:          prt_polling                                  */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  PhysToGDTSelector               */
;/*    ROUTINES:                       ProcBlock                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure printblock far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        LocalVar        _IOCount,WORD                   ; COUNT TO TRANSMIT
        LocalVar        _Printed,WORD                   ; COUNT PRINTED

        EnterProc

        test    [di].commonflags, USEPOLLING            ; USE POLLING?
        jz      prtblk0                                 ; NO, USE INTS
        call    prt_polling                             ; CALL POLL ROUTINE
        jmp     prtblk6                                 ; AND RETURN

prtblk0:
        mov     cx,es:[bx].IOCount
        mov     _IOCount,cx                             ; SAVE COUNT TO TRANSMIT
        mov     _Printed,0                              ; INITIALIZE PRT COUNT

        mov     [di].printbufoff,0                      ; SET OFFSET TO ZERO
        push    bx                                      ; SAVE REGISTER
        mov     ax,WORD PTR es:[bx].IOpData + 2         ; GET PHYS ADDR - HIGH
        mov     bx,WORD PTR es:[bx].IOpData             ; GET PHYS ADDR - LOW
        mov     si,[di].gdtprintbuf                     ; GET GDT SELECTOR
        mov     dl,DevHlp_PhysToGDTSelector             ; CONVERT PHYS TO GDT
        call    DWORD PTR [device_help]                 ; DO ADDRESS CONVERSION
        pop     bx                                      ; RESTORE REGISTER

prtblk1:
        mov     pendirq,0                               ; CLEAR PENDING IRQ CNT
        mov     si,blkindx                              ; GET BURST RATE INDEX
        mov     ax,blksiz[si]                           ; GET BURST RATE
        cmp     es:[bx].IOCount,ax                      ; IF LENGTH <= BLKSIZ
        jbe     prtblk2                                 ; NO LOW IRQ PREEMPTING
        mov     es:[bx].IOCount,ax                      ; SET PREEMPT INTERVAL

prtblk2:
        call    prt_transmit                            ; TRANSMIT BYTES

        mov     ax,pendirq                              ; GET PENDING IRQ COUNT
        mov     si,blkindx                              ; GET BURST RATE INDEX
        cmp     allowedpending[si],ax                   ; IF ALLOWED >= PENDING
        jae     prtblk3                                 ; THEN INC BURST RATE
        mov     si,nextindx[si]                         ; ELSE DEC BURST RATE
        jmp     SHORT prtblk31
prtblk3:
        cmp     si,MAXBLKINDX                           ; IF MAX BURST RATE
        je      prtblk32                                ; THEN DON'T INCREMENT
        add     si,2                                    ; ELSE INC BURST RATE
prtblk31:
        mov     blkindx,si                              ; SAVE NEXT RATE INDEX
prtblk32:

        mov     ax,es:[bx].IOCount                      ; GET PRINTED COUNT
        add     _Printed,ax                             ; ADD PRINTED TO TOTAL

        add     [di].printbufoff,ax                     ; ADD PRINTED TO OFFSET

        mov     ax,_IOCount                             ; GET INITIAL COUNT
        sub     ax,_Printed                             ; CALC REMAINING
        jz      prtblk5                                 ; IF ZERO, EXIT

        cmp     es:[bx].PktStatus,REQDONE               ; ERROR?
        jne     prtblk5                                 ; YES, EXIT

        mov     es:[bx].IOCount,ax                      ; MOVE REMAIN TO REQ PKT
        mov     es:[bx].PktStatus,0                     ; RESET RETURN CODE

        ; COMPENSATE FOR HIGH HARDWARE INTERRUPT PRIORITY BY PREEMPTING
        ; TIME SLICE AND ALLOWING MOUSE AND KEYBOARD TIME TO SERVICE.

        SaveReg <bx,di>                                 ; SAVE REGISTERS
        mov     ax,es                                   ; REQ BLK SEG ADDRESS
        inc     bx                                      ; UNIQUE BLOCK ID
        mov     cx,1                                    ; TIME LIMIT LOW VALUE
        mov     di,0                                    ; TIME LIMIT HIGH VALUE
        mov     dh,BLOCKSLEEPNOTINT                     ; SLEEP NOTINTERRUPTIBLE
        mov     dl,DevHlp_ProcBlock                     ; BLOCK FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP TO BLOCK
        RestoreReg <di,bx>                              ; RESTORE REGISTERS
        jmp     SHORT prtblk1                           ; DO MORE PRINTING

prtblk5:
        mov     ax,_Printed                             ; GET PRINTED COUNT
        mov     es:[bx].IOCount,ax                      ; RETURN PRINTED COUNT

prtblk6:
        and     [di].commonflags1,NOT CANMONPKT         ; TURN OFF CANCEL FLAG
        LeaveProc
        ret
EndProc printblock

BREAK <TRANSMIT A BLOCK OF CHARACTERS>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRT_TRANSMIT                                     */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  TRANSMIT A BLOCK OF DATA ROUTINE                */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO PRINT A BLOCK      */
;/*           OF CHARACTERS. IT WILL ENABLE OR DISABLE THE INTERRUPT   */
;/*           AT THE PORT OF THE SPECIFIED DEVICE, SETUP THE WAITTIME  */
;/*           AND RESUME POINTERS IN THE PRINT REQUEST BLOCK AND CALL  */
;/*           SENDCHAR TO SEND THE FIRST CHARACTER.                    */
;/*                                                                    */
;/*                                                                    */
;/* NOTES:                                                             */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prt_transmit:near                                */
;/*                   call prt_transmit                                */
;/*                                                                    */
;/* INPUT:  ES = Virtual segment of Kernel request block               */
;/*         BX = Virtual offset of Kernel request block                */
;/*      DS:DI = Offset to appropriate perprtdata area                 */
;/*      IOACTIVE FLAG OR MONPRINTING FLAG SET TO ON                   */
;/*                                                                    */
;/* EXIT-NORMAL:  ES:[BX] = Kernel Request Block                       */
;/*               DS:[DI] = Perprtdata area                            */
;/*               ES:[BX].Pktstatus = filled in.                       */
;/*               ES:[BX].IOCount = filled in.                         */
;/*               IOACTIVE FLAG SET TO OFF                             */
;/*                                                                    */
;/* EXIT-ERROR:   See exit normal above.                               */
;/*                                                                    */
;/* EFFECTS:                                                           */
;/*                                                                    */
;/* INTERNAL REFERENCES:  sendchar                                     */
;/*    ROUTINES:          starttimer                                   */
;/*                       cleanup                                      */
;/*                       searchandsteal                               */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  SetIRQ                          */
;/*    ROUTINES:                       TickCount                       */
;/*                                    ProcBlock                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prt_transmit near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pvtimer 1                                       ; SUB FROM PFVW TIMER
        mov     ax,es:[bx].IOcount                      ; INITIAL COUNT
        mov     [di].initialcount,ax                    ; SAVE INITIALCOUNT
        mov     [di].prtcount, 0                        ; CLEAR PRINTED COUNT

        ; TRY TO SET THE APPROPRIATE IRQ LEVEL.  IF IT FAILS, WE KNOW IT'S IN
        ; USE AND WILL HAVE TO DRIVE THE DEVICE BY TIMER INTERRUPTS.

        test    [di].share_interrupt, INT_SHARING       ; INT SHARING SUPPORT?
        jz      prtxmit2                                ; NO
        xor     ch,ch                                   ; CLEAR HIGH BYTE
        mov     cl,[di].intlevel                        ; SPECIFY INT LEVEL
        jmp     short prtxmit3                          ; ALREADY HAVE THE IRQ

prtxmit2:
        push    bx
        mov     ax,[di].introutine                      ; OFF HARD INT HANDLER
        xor     bh,bh                                   ; ZERO OUT BH
        mov     bl,[di].intlevel                        ; HARD INT LEVEL NUMBER
        mov     dh,0                                    ; LEVEL NOT SHARED
        cli                                             ; DISABLE INTERRUPTS
        mov     dl,DevHlp_SetIRQ                        ; ASSIGN HARDWARE INT
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        mov     [di].curintlevel,0                      ; ASSUME RUN OFF TIMER
        mov     cx,bx
        pop     bx
        jc      prtxmit4                                ; IF IN-USE RUN ON TIMER

prtxmit3:
        mov     [di].curintlevel,cl                     ; SAVE HARD INT LEVEL
        cmp     cl,IRQ7INTNUM                           ; IF IRQ NOT IRQ 7
        jne     prtxmit4                                ; THEN JUMP
        mov     al,[di].deviceindex                     ; GET DEVICE INDEX
        xor     ah,ah                                   ; ZERO OUT AH
        mov     irq7owner,ax                            ; SET OWNER OF IRQ7
prtxmit4:
        sti                                             ; ENABLE INTERRUPTS
        push    bx
        mov     [di].timeoutctr,0                       ; CLEAR TIMEOUT CTR
        mov     ax,[di].timeout_off                     ; TIMEOUT ENTRY POINT
        mov     bx,timeoutval                           ; TIMEOUT IN TICKCOUNTS
        mov     dl,DevHlp_TickCount                     ; CALL TIMER SERVICES
        call    DWORD PTR [device_help]

        call    sendchar                                ; CHECK STATUS PORT AND
        pop     bx                                      ; PRINT FIRST CHARACTER

        cmp     [di].curintlevel,0                      ; IF !RUNNING ON TIMER
        jne     prtxmit41                               ; THEN WAIT FOR HARD INT
        call    starttimer                              ; START THE TIMER

prtxmit41:
        cli                                             ; DISABLE INTERRUPTS
        test    es:[bx].PktStatus,REQDONE               ; IF REQUEST IS DONE
        jnz     prtxmit5                                ; THEN EXIT
        push    di                                      ; SAVE DI
        mov     ax,es                                   ; REQ BLK SEG ADDRESS
        inc     bx                                      ; UNIQUE BLOCK ID
        mov     cx,BLOCKLOWVALUE                        ; -1 NEVER TIMEOUT
        mov     di,BLOCKLOWVALUE                        ; -1 NEVER TIMEOUT
        mov     dh,BLOCKSLEEPINT                        ; SLEEP INTERRUPTIBLE
        mov     dl,DevHlp_ProcBlock                     ; PROCBLOCK FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        dec     bx                                      ; ADDRESS REQ BLOCK
        pop     di                                      ; RESTORE DI
        jc      prtxmit45                               ; UNUSUAL WAKEUP
        jmp     prtxmit41                               ; RETEST DONE

prtxmit45:
        mov     es:[bx].PktStatus,CHARIOINT             ; RETURN STATUS
        call    cleanup                                 ; TERMINATE PRT REQ
prtxmit5:
        cmp     [di].curintlevel,IRQ7INTNUM             ; DID IRQ7 FINISH?
        jne     prtxmit6                                ; YES, GIVE TO TIMER DEV
        call    searchandsteal                          ; GIVE IRQ7 TO SYS TIMER
prtxmit6:
        sti                                             ; ENABLE INTERRUPTS
        pvtimer                                         ; ADD TO PFVW TIMER/CTRS
        ret
EndProc prt_transmit

BREAK <SEARCH FOR TIMER AND GIVE IT THE IRQ>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: SEARCHANDSTEAL                                    */
;/*                                                                    */
;/* DESCRIPTIVE NAME: SEARCH FOR DEVICE ON TIMER, REPLACE WITH IRQ7.   */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO SEARCH FOR A       */
;/*           REQUEST RUNNING ON A DEVICE USING THE SYSTEM TIMER AND   */
;/*           TRANSFER IT TO RUN OFF OF IRQ7.                          */
;/*                                                                    */
;/* ENTRY POINT: SEARCHANDSTEAL                                        */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: NONE                                                        */
;/*                                                                    */
;/* EXIT-NORMAL:  REQ on system timer transfered to IRQ7               */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  stoptimer                                    */
;/*    ROUTINES:          sendchar                                     */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  SetIRQ                          */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure searchandsteal near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha                                           ; SAVE REGESTERS
        push    es                                      ; SAVE EXTRA SEGMENT
        test    timerneeded,TIMERMASK                   ; IF TIMER RUNNING
        jnz     srchandstl0                             ; CONTINUE
        jmp     srchandstl5                             ; EXIT

        ; FIND DEVICE RUNNING OFF OF THE SYSTEM TIMER

srchandstl0:
        mov     ax,1                                    ; SET AX = 1
        xor     ch,ch                                   ; MAKE CH 0
        mov     cl,numofprts                            ; # OF PARALLEL PORTS
        mov     di,OFFSET perprtarea                    ; DI = START OF DATABASE
srchandstl1:
        test    timerneeded,al                          ; IF DEVICE USING TIMER
        jnz     srchandstl3                             ; THEN FOUND DEVICE

srchandstl2:
        add     di,SIZE printer_database                ; DX = SIZE OF ONE ENTRY
        sal     al,1                                    ; CHECK NEXT DEVICE
        loop    srchandstl1                             ; TRY NEXT DEVICE

        jcxz    srchandstl5                             ; IF CX=0, !FOUND, EXIT
srchandstl3:
        mov     ax,[di].initialcount                    ; GET INITIAL COUNT
        cmp     ax,[di].prtcount                        ; IF NO BYTES REMAINING
        je      srchandstl2                             ; THEN TRY NEXT DEVICE

        ; SETUP SYSTEM TIMER JOB TO RUN ON IRQ7

        cmp     [di].share_interrupt,INT_SHARING        ; IF INT SHARING
        je      srchandstl4                             ; THEN DON'T SET AGAIN

        mov     ax,[di].introutine                      ; OFF HARD INT HANDLER
        xor     bh,bh                                   ; ZERO OUT BH
        mov     bl,[di].intlevel                        ; HARD INT LEVEL NUMBER
        mov     dh,0                                    ; LEVEL NOT SHARED
        mov     dl,DevHlp_SetIRQ                        ; ASSIGN HARDWARE INT
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        jc      srchandstl5                             ; IF ERROR, CAN'T SWITCH

srchandstl4:
        mov     [di].curintlevel,IRQ7INTNUM             ; SET LEVEL TO IRQ7

        mov     al,[di].deviceindex                     ; GET DEVICE INDEX
        xor     ah,ah                                   ; ZERO OUT AH
        mov     irq7owner,ax                            ; SET OWNER OF IRQ7

        ; SHUT DOWN DEVICE ON SYSTEM TIMER AND RESTART IT ON HARDWARE
        ; INTERRUPT LEVEL 7 (IRQ7)

        call    stoptimer                               ; STOP DEVICE ON TIMER
        call    sendchar                                ; PRINT NEXT CHAR W IRQ7

srchandstl5:
        pop     es                                      ; RESTORE EXTRA SEGMENT
        popa                                            ; RESTORE REGESTERS
        ret
EndProc searchandsteal

BREAK <SHARED INTERRUPT HANDLERS>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SOURCE FILE NAME:  eInt_1                                          */
;/*                    eInt_2                                          */
;/*                    eInt_3                                          */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Printer Hardware Interrupt Handlers for shared  */
;/*                    interrupts on an EISA system.                   */
;/*                                                                    */
;/* FUNCTION:  These routines are used as entry points to the          */
;/*            interrupt service routine when OS/2 is run on an EISA   */
;/*            system that is sharing the printer's interrupt line.    */
;/*            This "interrupt sharing" is TRUE hardware interrupt     */
;/*            sharing, by use of level triggered interrupt lines.     */
;/*            This allows the interrupt line to be shared between the */
;/*            printer and other non-printer devices that are          */
;/*            configured for the same interrupt line.  These entry    */
;/*            points will ONLY be used when the EISA configuration    */
;/*            information indicates that the printer's interrupt line */
;/*            is being shared.  Otherwise, the ISA compatible entry   */
;/*            points, PRTINT05 and PRTINT07 will be used.             */
;/*                                                                    */
;/* NOTE:   The Hardware Interrupt Manager saves all registers and     */
;/*         calls with interrupts enabled.  On exit, interrupts are    */
;/*         disabled to prevent nested interrupts after EOI.           */
;/*                                                                    */
;/* INPUT:  DS = setup to our data segment.                            */
;/*                                                                    */
;/* ENTRY POINT:  eInt_1, eInt_2, eInt_3                               */
;/*    LINKAGE :  CALL FAR                                             */
;/*                                                                    */
;/* EXIT-NORMAL:  Return to the hardware interrupt manager.            */
;/*                                                                    */
;/* EXIT-ERROR:   N/A                                                  */
;/*                                                                    */
;/* INTERNAL REFERENCES:  interrupt                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure eInt_1 far
        mov     di,OFFSET perprtarea                    ; FIRST PERPRTDATA AREA
        mov     si, LPT1_INDEX                          ; SET-UP FOR INTERRUPT
        test    [di].share_interrupt, INT_SHARING       ; SUPPORTS INT SHARING?
        jz      eInt11                                  ; NO: GO PROCESS INT.
        test    [di].commonflags,CHARACTER_OUT          ; HAS A CHAR BEEN SENT?
        jz      eInt10                                  ; JUMP IF NO.
        test    [di].commonflags1,PDDDIRACCESS          ; PDD HAS PORT ACCESS?
        jz      eInt10                                  ; NO, IGNORE PORT
        mov     dx, [di].deviceaddr                     ; DATA PORT
        inc     dx                                      ; STATUS PORT
        cli                                             ; INTS BACK OFF
        in      al, dx                                  ; GET CURRENT STATUS
        test    al, INT_PENDING                         ; OUR INTERRUPT?
        jz      eInt11                                  ; Y: PROCESS IT
eInt10:                                                 ;
        stc                                             ; N: SIGNAL KERNEL
        ret                                             ;  AND RETURN
eInt11:
        call    interrupt                               ; PROCESS INTERRUPT
        ret
EndProc  eInt_1

Procedure eInt_2 far
        mov     di,OFFSET perprtarea                    ; FIRST PERPRTDATA AREA
        add     di, (SIZE printer_database * LPT2_INDEX); 2ND PERPRTDATA AREA
        mov     si, LPT2_INDEX                          ; SET-UP FOR INTERRUPT
        test    [di].share_interrupt, INT_SHARING       ; SUPPORTS INT SHARING?
        jz      eInt21                                  ; NO: GO PROCESS INT.
        test    [di].commonflags,CHARACTER_OUT          ; HAS A CHAR BEEN SENT?
        jz      eInt20                                  ; JUMP IF NO.
        test    [di].commonflags1,PDDDIRACCESS          ; PDD HAS PORT ACCESS?
        jz      eInt20                                  ; NO, IGNORE PORT
        mov     dx, [di].deviceaddr                     ; DATA PORT
        inc     dx                                      ; STATUS PORT
        cli                                             ; INTS BACK OFF
        in      al, dx                                  ; GET CURRENT STATUS
        test    al, INT_PENDING                         ; OUR INTERRUPT?
        jz      eInt21                                  ; Y: PROCESS IT
eInt20:                                                 ;
        stc                                             ; N: SIGNAL KERNEL
        ret                                             ;  AND RETURN
eInt21:
        call    interrupt                               ; PROCESS INTERRUPT
        ret
EndProc  eInt_2

Procedure eInt_3 far
        mov     di,OFFSET perprtarea                    ; FIRST PERPRTDATA AREA
        add     di, (SIZE printer_database * LPT3_INDEX); 3RD PERPRTDATA AREA
        mov     si, LPT3_INDEX                          ; SET-UP FOR INTERRUPT
        test    [di].share_interrupt, INT_SHARING       ; SUPPORTS INT SHARING?
        jz      eInt31                                  ; NO: GO PROCESS INT.
        test    [di].commonflags,CHARACTER_OUT          ; HAS A CHAR BEEN SENT?
        jz      eInt30                                  ; JUMP IF NO.
        test    [di].commonflags1,PDDDIRACCESS          ; PDD HAS PORT ACCESS?
        jz      eInt30                                  ; NO, IGNORE PORT
        mov     dx, [di].deviceaddr                     ; DATA PORT
        inc     dx                                      ; STATUS PORT
        cli                                             ; INTS BACK OFF
        in      al, dx                                  ; GET CURRENT STATUS
        test    al, INT_PENDING                         ; INT PENDING?
        jz      eInt31                                  ; Y: PROCESS IT
eInt30:                                                 ;
        stc                                             ; N: SIGNAL KERNEL
        ret                                             ;  AND RETURN
eInt31:
        call    interrupt                               ; PROCESS INTERRUPT
        ret
EndProc  eInt_3

BREAK <IRQ5 INTERRUPT HANDLER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SOURCE FILE NAME:  PRTINT05                                        */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Printer Hardware Interrupt Handler for IRQ5.    */
;/*                                                                    */
;/* FUNCTION:  The function of PRTINT05 is to set si to the device     */
;/*            index which caused the hardware interrupt on level 5.   */
;/*                                                                    */
;/* NOTE:   The Hardware Interrupt Manager saves all registers and     */
;/*         calls with interrupt disabled.  On exit, interrupts are    */
;/*         disabled to prevent nested interrupts after EOI.           */
;/*                                                                    */
;/* INPUT:  DS = setup to our data segment.                            */
;/*                                                                    */
;/* ENTRY POINT:  PRTINT05                                             */
;/*    LINKAGE :  CALL FAR                                             */
;/*                                                                    */
;/* EXIT-NORMAL:  Return to the hardware interrupt manager.            */
;/*                                                                    */
;/* EXIT-ERROR:   N/A                                                  */
;/*                                                                    */
;/* INTERNAL REFERENCES:  interrupt                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prtint05 far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     si,irq5index                            ; IND TO CURR OWNER IRQ5
        call    interrupt                               ; CALL COMMON ROUTINE
        ret
EndProc prtint05

BREAK <IRQ7 INTERRUPT HANDLER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SOURCE FILE NAME:  PRTINT07                                        */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  PRINTER HARDWARE INTERRUPT HANDLER FOR IRQ7.    */
;/*                                                                    */
;/* FUNCTION:  THE FUNCTION OF PRTINT07 IS TO SET SI TO THE DEVICE     */
;/*            INDEX WHICH CAUSED THE HARDWARE INTERRUPT ON LEVEL 7.   */
;/*                                                                    */
;/* NOTE:   The Hardware Interrupt Manager saves all registers and     */
;/*         calls with interrupt disabled.  On exit, interrupts are    */
;/*         disabled to prevent nested interrupts after EOI.           */
;/*                                                                    */
;/* INPUT:  DS = setup to our data segment.                            */
;/*                                                                    */
;/* ENTRY POINT:  PRTINT07                                             */
;/*    LINKAGE :  CALL FAR                                             */
;/*                                                                    */
;/* EXIT-NORMAL:  Return to the hardware interrupt manager.            */
;/*                                                                    */
;/* EXIT-ERROR:   N/A                                                  */
;/*                                                                    */
;/* INTERNAL REFERENCES:  interrupt                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prtint07 far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     si,irq7owner                            ; IND TO CURR OWNER IRQ7
        mov     timercount,0                            ; CLEAR TIMER COUNTER
        call    interrupt                               ; CALL COMMON ROUTINE
        ret
EndProc prtint07

BREAK <START A TIMER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  STARTTIMER                                       */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Printer Device Driver Start Timer Routine       */
;/*                                                                    */
;/* FUNCTION:  The function of this subroutine is to start a timer     */
;/*            manager to get invoked on each timer tick.  If the      */
;/*            timer manager is already started, this routine will not */
;/*            attempt to start it again.                              */
;/*                                                                    */
;/* NOTE:                                                              */
;/*        This routine is only called if the SetIRQ function call     */
;/*        fails in the printblock routine or if no hardware interrupt */
;/*        is assigned to this device.                                 */
;/*                                                                    */
;/* ENTRY POINT:  STARTTIMER                                           */
;/*    LINKAGE :  CALL NEAR                                            */
;/*                                                                    */
;/* INPUT:  di = offset to appropriate perprtdata area                 */
;/*                                                                    */
;/* EXIT-NORMAL: timerneeded = 001B LPT1 is running off of the timer.  */
;/*                            010B LPT2 is running off of the timer.  */
;/*                            100B LPT3 is running off of the timer.  */
;/*              [di].prttime = reset running count of ticks           */
;/*              [di].waittime = set maximum count of ticks to wait    */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  SetTimer                        */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure starttimer near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     [di].prttime,0                          ; RESET RUNNING COUNT
        mov     [di].waittime,MINWAITTIME               ; # OF TICKS TO WAIT

        test    timerneeded,TIMERMASK                   ; IF TIMERMGR PREV REG
        jnz     starttimer1                             ; THEN DON'T REREGISTER

        mov     ax,OFFSET prttimgr                      ; OFFSET PRT TIMER MGR
        mov     dl,DevHlp_SetTimer                      ; SET TIMER FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
starttimer1:
        mov     al,[di].deviceflag                      ; GET DEVICE FLAG
        or      timerneeded,al                          ; DEVICE RUNS BY TIMER
        ret
EndProc starttimer

BREAK <STOP A TIMER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  STOPTIMER                                        */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Printer Device Driver Stop Timer Routine        */
;/*                                                                    */
;/* FUNCTION:  The function of this subroutine is to stop a timer      */
;/*            manager. If the timer manager is needed by another      */
;/*            device, it will only deregister this request from       */
;/*            running on the timer.                                   */
;/*                                                                    */
;/* NOTE:                                                              */
;/*                                                                    */
;/* ENTRY POINT:  STOPTIMER                                            */
;/*    LINKAGE :  CALL NEAR                                            */
;/*                                                                    */
;/* INPUT:  di = offset to appropriate perprtdata area                 */
;/*                                                                    */
;/* EXIT-NORMAL: timerneeded = 001B LPT1 is running off of the timer.  */
;/*                            010B LPT2 is running off of the timer.  */
;/*                            100B LPT3 is running off of the timer.  */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  ResetTimer                      */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure stoptimer near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     al,[di].deviceflag                      ; GET DEVICE FLAG
        xor     al,0ffh                                 ; GET REVERSE BITMASK
        and     timerneeded,al                          ; SET FLAG OFF
        test    timerneeded,TIMERMASK                   ; IF PRT TIMERMGR NEEDED
        jnz     stoptimer1                              ; THEN DON'T DEREGISTER

        mov     ax,OFFSET prttimgr                      ; ELSE DEREGISTER TIMER
        mov     dl,DevHlp_ResetTimer                    ; TURN OFF TIMER
        call    DWORD PTR [device_help]                 ; CALL TIMER SERVICES

stoptimer1:
        ret
EndProc stoptimer

BREAK <PRINTER DEVICE DRIVER TIMER MANAGER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* MODULE NAME:  PRTTIMGR                                             */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Print timer manager                             */
;/*                                                                    */
;/* FUNCTION:  The function of this module is to call interrupt when   */
;/*            the certain time interval has elasped.                  */
;/*                                                                    */
;/* NOTES:                                                             */
;/*         It is the timer routine's responsibility to save and       */
;/*         restore all registers used in the printer timer handler.   */
;/*                                                                    */
;/* ENTRY POINT: PRTTIMGR                                              */
;/*    LINKAGE : CALL FAR                                              */
;/*                                                                    */
;/* INPUT:  DS = our data segment                                      */
;/*         numofprts   = number of parallel ports installed.          */
;/*         timerneeded = 001B LPT1 is running off of the timer.       */
;/*                       010B LPT2 is running off of the timer.       */
;/*                       100B LPT3 is running off of the timer.       */
;/*         [DI].prttime = running count of ticks                      */
;/*         [DI].waittime = # of ticks to wait                         */
;/*                                                                    */
;/* EXIT-NORMAL:  All registers are restored.                          */
;/*               [DI].prttime = [di].prttime + 1 or                   */
;/*               [di].prttime = 0 if prttime = waittime.              */
;/*                                                                    */
;/* EXIT-ERROR:  See EXIT-NORMAL above.                                */
;/*                                                                    */
;/* INTERNAL REFERENCES:  timerinterrupt                               */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prttimgr far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha                                           ; SAVE REGESTERS
        push    es                                      ; SAVE EXTRA SEGMENT
        test    timerneeded,TIMERMASK                   ; IF TIMER NOT REQUESTED
        jz      prttimgr3                               ; THEN EXIT

        mov     ax,1                                    ; SET AX = 1
        xor     ch,ch                                   ; MAKE CH 0
        mov     cl,numofprts                            ; # OF PARALLEL PORTS
        mov     di,OFFSET perprtarea                    ; DI = START OF DATABASE
prttimgr1:
        test    timerneeded,al                          ; IF DEV !NEEDS T. TICKS
        jz      prttimgr2                               ; THEN TRY NEXT DEVICE

        inc     [di].prttime                            ; ADD 1 TO TIMER COUNT
        mov     dx,[di].prttime                         ; DX = TIMER COUNT
        cmp     dx,[di].waittime                        ; IF TIMER IS < WAITTIME
        jb      prttimgr2                               ; THEN GET NEXT REQUEST
        SaveReg <ax,cx>                                 ; SAVE REGISTERS

        call    timerinterrupt                          ; HANDLE REQUEST
        mov     [di].prttime,0                          ; RESET RUNNING COUNT

        RestoreReg <cx,ax>                              ; RESTORE REGISTERS
prttimgr2:
        sal     al,1                                    ; CHECK NEXT DEVICE
        add     di,SIZE printer_database                ; DI -> NEXT DEVICE
        loop    prttimgr1                               ; TRY NEXT DEVICE
prttimgr3:
        pop     es                                      ; RESTORE EXTRA SEGMENT
        popa                                            ; RESTORE REGESTERS
        ret
EndProc prttimgr

BREAK <TIMER INTERRUPT HANDLER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  TIMERINTERRUPT                                   */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Timer Interrupt Handler                         */
;/*                                                                    */
;/* FUNCTION:  The function of interrupt will be to perform the timer  */
;/*            interrupt handler functions.                            */
;/*                                                                    */
;/* NOTE:   The Hardware Interrupt Manager saves all registers and     */
;/*         calls with interrupts disabled.                            */
;/*                                                                    */
;/* ENTRY POINT:  TIMEINTERRUPT                                        */
;/*    LINKAGE :  CALL NEAR                                            */
;/*                                                                    */
;/* INPUT:  DI = INDEX TO APPROPRIATE PERPRTDATA AREA                  */
;/*                                                                    */
;/* EXIT-NORMAL:                                                       */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:                                               */
;/*    ROUTINES:  sendchar                                             */
;/*               cleanup                                              */
;/*               checkstatus                                          */
;/*               changeirqtost                                        */
;/*                                                                    */
;/* EXTERNAL REFERENCES:                                               */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure timerinterrupt near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING

        test    [di].commonflags,MONPRINTING            ; IF MON PRINTING
        jnz     tinterrupt0                             ; CONTINUE
        test    [di].commonflags,IOACTIVE               ; IF I/O !ACTIVE
        jz      tinterrupt3                             ; THEN JUST RETURN

tinterrupt0:
        and     [di].commonflags,NOT CHARACTER_OUT      ; RESET CHAR SENT FLAG.
        mov     fIRQStolen,0                            ; CLEAR IRQ STOLEN FLAG

        ; I/O IS ACTIVE ON THIS DEVICE

        mov     ax,[di].initialcount                    ; GET INITIAL COUNT
        cmp     ax,[di].prtcount                        ; IF BYTES REMAINING
        jne     tinterrupt1                             ; THEN CONTINUE PRINTING

        ; COMPLETELY FINISHED PRINTING

        call    cleanup                                 ; CALL CLEANUP
        jmp     SHORT tinterrupt3                       ; RET TO HARD INT MGR

        ; NOT FINISHED PRINTING
tinterrupt1:
        inc     timercount                              ; INCREMENT TIMER COUNT
        cmp     timercount,TICKSTOWAIT                  ; CHECK IRQ STATUS?
        jbe     tinterrupt05                            ; IF NOT CONTINUE PRT.

        mov     timercount,0                            ; ZERO TIMER COUNT
        cli                                             ; DISABLE INTERRUPTS
        mov     al,IRQ7INTNUM
        call    checkstatus                             ; CHECK IRQ7 PORT STATUS

        cmp     al,0                                    ; IF AL = 0
        je      tinterrupt05                            ; THEN CONTINUE ON TIMER
        mov     fIRQStolen,1                            ; SET IRQ STOLEN FLAG
        call    changeirqtost                           ; ELSE GRAB IRQ

tinterrupt05:
        sti                                             ; ENABLE INTERRUPTS

tinterrupt2:
        call    sendchar                                ; PRINT NEXT CHAR

tinterrupt3:
        ret
EndProc timerinterrupt

BREAK <GRAB IRQ WHEN PRINTER NOT USED>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  CHANGEIRQTOST                                    */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Change IRQ job to ST and ST job to IRQ          */
;/*                                                                    */
;/* FUNCTION:  The function of this routine is to change the internal  */
;/*            data structures of the print jobs to represent the      */
;/*            change from IRQ to ST.                                  */
;/*                                                                    */
;/* ENTRY POINT:  CHANGEIRQTOST                                        */
;/*    LINKAGE :  CALL NEAR                                            */
;/*                                                                    */
;/* INPUT:  DI = POINTER TO ST PERPRTDATA DATA                         */
;/*                                                                    */
;/* EXIT-NORMAL:  none                                                 */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  starttimer                                   */
;/*    ROUTINES:          stoptimer                                    */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure changeirqtost near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        push    bx                                      ; SAVE BX
        push    di                                      ; SAVE DI FOR ST JOB

        ; DI MUST BE INDEXED TO IRQ JOB AT THIS POINT!!!!!
        ; THEREFORE, THE FOLLOWING LOOP WILL SEARCH FOR THE
        ; IRQ JOB AND SET DI APPROPRIATELY

        xor     ch,ch                                   ; MAKE CH 0
        mov     cl,numofprts                            ; # OF PARALLEL PORTS
        mov     di,OFFSET perprtarea                    ; DI = START OF DATABASE
change0:
        cmp     [di].curintlevel,IRQ7INTNUM             ; IF DEV USING IRQ7
        je      change1                                 ; THEN HAVE DEVICE
        add     di,SIZE printer_database                ; DX = SIZE OF ONE ENTRY
        loop    change0                                 ; TRY NEXT DEVICE

        pop     di
        jmp     SHORT changeEND
change1:
        mov     [di].curintlevel,0                      ; CHANGE IRQ TO TIMER
        call    starttimer                              ; START THE TIMER

        ; DISABLE INTS AT OLD IRQ PORT

        mov     dx,[di].deviceaddr                      ; GET PORT ADDRESS
        inc     dx                                      ; POINT TO CONTROL PORT
        inc     dx                                      ; POINT TO CONTROL PORT
        mov     al,DISABLEINT                           ; DISABLE INT AT PORT
        out     dx,al                                   ; NEW CTRL VALUE

        ; If the interrupt line that was just disabled was level triggered (EISA
        ; systems have this option), then we need to clear any pending interrupt
        ; that occurred since the CLI at the top of this routine.  This can be
        ; done by reading the printer status port.  Doing this will cause the
        ; PIC to report a spurious interrupt to the CPU, but OS/2's kernel will
        ; handle this, and our interrupt service routine will not be called.

        mov     dx,[di].deviceaddr                      ; GET PORT ADDRESS
        inc     dx                                      ; POINT TO STATUS PORT
        in      al,dx                                   ; READ THE STATUS PORT

;******************************************************************************
;* NOW WE PROCESS THE ST JOB TO CONVERT IT TO THE IRQ                         *
;******************************************************************************

        ; DI MUST BE INDEXED TO ST JOB AT THIS POINT!!!!!

        pop     di                                      ; RESTORE DI FOR ST JOB

        mov     [di].curintlevel,IRQ7INTNUM             ; CHANGE TIMER TO IRQ

        xor     ah,ah                                   ; ZERO AH
        mov     al,[di].deviceindex                     ; GET DEVICE INDEX
        mov     irq7owner,ax                            ; SET IRQ7 OWNER

        call    stoptimer                               ; STOP TIMER ON ST JOB
changeEND:
        pop     bx                                      ; RESTORE BX
        ret
EndProc changeirqtost

BREAK <PRINTER ERROR STATUS PROCESSING>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: CHECKSTATUS                                       */
;/*                                                                    */
;/* DESCRIPTIVE NAME: ANALYZE STATUS FLAGS                             */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO ANALYZE THE STATUS */
;/*           INFORMATION RETURNED FROM "GETSTATUS".                   */
;/*                                                                    */
;/* ENTRY POINT: CHECKSTATUS                                           */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: al = interrupt level to check status on                     */
;/*                                                                    */
;/* EXIT-NORMAL:  al = 1 if ok to steal IRQ, 0 otherwise               */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  getstatus                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure checkstatus near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        push    di                                      ; SAVE ST INDEX

        ; SEARCH FOR IRQ JOB

        xor     ch,ch                                   ; MAKE CH 0
        mov     cl,numofprts                            ; # OF PARALLEL PORTS
        mov     di,OFFSET perprtarea                    ; DI = START OF DATABASE
checkstatus0:
        cmp     [di].curintlevel,al                     ; IF DEV USING IRQ7
        je      checkstatus1                            ; THEN FOUND IT!
        add     di,SIZE printer_database                ; DX = SIZE OF ONE ENTRY
        loop    checkstatus0                            ; TRY NEXT DEVICE

        jcxz    checkstatus2                            ; NOT OK, EXIT
checkstatus1:
        mov     ax,[di].initialcount                    ; GET INITIAL COUNT
        cmp     [di].prtcount,ax                        ; IF NONE REMAIN
        je      checkstatus2                            ; EXIT
        call    getstatus                               ; GET PORT STATUS (AH)

        xor     al,al                                   ; NO ERROR
;*******************************************************************************
;* B0 = NOT BUSY & PAPER OUT & SELECTED = !CABLED ON MONO / SERIAL ADAPTER #20 *
;*******************************************************************************
        cmp     ah,0b0h
        jnz     cs1
        mov     al,1                                    ; SET SELECT ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 38 = BUSY & PAPER OUT & SELECTED & I/O ERROR = OUT OF PAPER             #28 *
;*******************************************************************************
cs1:
        cmp     ah,38h
        jnz     cs2
        mov     al,1                                    ; SET PAPER OUT ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 30 = BUSY & PAPER OUT & SELECTED & NOT I/O ERROR = OUT OF PAPER         #28 *
;*******************************************************************************
cs2:
        cmp     ah,30h
        jnz     cs3
        mov     al,1                                    ; SET PAPER OUT ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 28 = BUSY & PAPER OUT & NOT SELECTED & I/O ERROR = OUT OF PAPER         #28 *
;*******************************************************************************
cs3:
        cmp     ah,28h
        jnz     cs4
        mov     al,1                                    ; SET PAPER OUT ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 18 = BUSY & I/O ERROR & SELECTED = OFF LINE                             #29 *
;*******************************************************************************
cs4:
        cmp     ah,18h
        jnz     cs5
        mov     al,1                                    ; SET I/O ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 10 = BUSY & SELECTED = NOT READY                                        #29 *
;*******************************************************************************
cs5:
        cmp     ah,10h
        jnz     cs6
        mov     al,1                                    ; SET I/O ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 08 = BUSY & I/O ERROR = OFF LINE                                        #29 *
;*******************************************************************************
cs6:
        cmp     ah,08h
        jnz     cs7
        mov     al,1                                    ; SET I/O ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 00 = BUSY & NOTHING ELSE = NOT READY                                    #29 *
;*******************************************************************************
cs7:
        cmp     ah,00h
        jnz     cs8
        mov     al,1                                    ; SET I/O ERROR
        jmp     SHORT checkstatus3
;*******************************************************************************
;* 88 = NOT BUSY & I/O ERROR = POWERED OFF                                 #20 *
;*******************************************************************************
cs8:
        cmp     ah,88h
        jnz     checkstatus2
        mov     al,1                                    ; SET SELECT ERROR
        jmp     SHORT checkstatus3

checkstatus2:
        mov     al,0

checkstatus3:
        pop     di                                      ; RESTORE ST INDEX
        ret
EndProc checkstatus

BREAK <HARDWARE INTERRUPT HANDLER>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  INTERRUPT                                        */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Hardware Interrupt Handler                      */
;/*                                                                    */
;/* FUNCTION:  The function of interrupt will be to perform the        */
;/*            hardware interrupt handler functions.                   */
;/*                                                                    */
;/* NOTE:   The Hardware Interrupt Manager saves all registers and     */
;/*         calls with interrupts enabled.  On exit, interrupts are    */
;/*         disabled to prevent nested interrupts after EOI.           */
;/*                                                                    */
;/* ENTRY POINT:  INTERRUPT                                            */
;/*    LINKAGE :  CALL NEAR                                            */
;/*                                                                    */
;/* INPUT:  SI = INDEX TO APPROPRIATE PERPRTDATA AREA                  */
;/*                                                                    */
;/* EXIT-NORMAL:  NC = TELL INT MGR, MY INT (HARDWARE INTS ONLY)       */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  copynext                                     */
;/*    ROUTINES:          sendchar                                     */
;/*                       cleanup                                      */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  EOI                             */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure interrupt near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     di,OFFSET perprtarea                    ; FIRST PERPRTDATA AREA
        mov     ax,SIZE printer_database                ; SIZE OF PRT DATABASE
        mul     si                                      ; MULT BY DEVICE INDEX
        add     di,ax                                   ; INDEX INTO DEV AREA

        ; IF OTHER HARDWARE INTERRUPT PENDING IN THE SYSTEM, COUNT THESE TO
        ; DETERMINE WHEN TO STOP PRINTING AND ALLOW THESE LOWER PRIORITY
        ; INTERRUPTS TIME TO SERVICE.

ifdef SMP
        jmp interrupt01         ; skip this for now, after the april beta
                                ; we will check _PSDInfo flags for adv
                                ; int mode bit
endif

        cli                                             ; DISABLE INTERRUPTS
        in      al,21h                                  ; GET 8259 MASTER IMR
        or      al,0E0h                                 ; MASK OFF IRQ7
        mov     ah,al                                   ; AH=MASTER IMR
        in      al,0a1h                                 ; GET 8259 SLAVE IMR
        not     ax                                      ; INVERT FOR ACTIVE IRQS
        mov     si,ax                                   ; SAVE ACTIVE IRQS
        mov     ax,0a0ah                                ; READ IRR CMD
        out     20h,al                                  ; TO 8259 MASTER
        DevIODelay <bx>                                 ; IO DELAY
        in      al,20h                                  ; GET 8259 MASTER IRR
        xchg    ah,al                                   ; SAVE MASTER IRR
        out     0a0h,al                                 ; READ IRR CMD TO SLAVE
        DevIODelay <bx>                                 ; IO DELAY
        in      al,0a0h                                 ; GET 8259 SLAVE IRR
        and     ax,si                                   ; ENABLED IRQS-REQS PEND
        jz      interrupt01                             ; JMP IF NO PENDING IRQS
        inc     pendirq                                 ; ELSE INC PENDING IRQ
interrupt01:
        sti                                             ; ENABLE INTERRUPTS

;/** START SPURIOUS TEST ***********************************************/
;/*                                                                    */
;/* THE FOLLOWING CODE WAS ADDED IN ORDER TO PROCESS SPURIOUS          */
;/* INTERRUPTS.  WE ARE CURRENTLY GETTING SPURIOUS INTERRUPTS FROM THE */
;/* NEW 3 MEG CARDS WITH SERIAL/PARALLEL PORTS AND FROM THE BAD "FIRST */
;/* ISSUE" SERIAL/PARALLEL CARDS.  FUTURE ENHANCEMENTS TO THIS CODE    */
;/* WOULD BE TO COUNT THE NUMBER OF SPURIOUS INTERRUPTS IN A GIVEN     */
;/* AMOUNT OF TIME AND TO ALERT THE INTERRUPT MANAGER TO DISABLE THIS  */
;/* LEVEL ALTOGETHER.  THIS WOULD ALLOW THE PRINTER DEVICE DRIVER TO   */
;/* RUN FROM VIA THE TIMER OPERATION. THE CODE NOW REQUIRES THAT IN    */
;/* ORDER FOR US TO RECOGNIZE THE INTERRUPT AS OURS, WE MUST HAVE SENT */
;/* A CHARACTER OUT TO THE PRINTER.                                    */
;/*                                                                    */
;/**********************************************************************/

        test    [di].commonflags,CHARACTER_OUT          ; HAS A CHAR BEEN SENT?
        jnz     interrupt05                             ; JUMP IF YES.

        cmp     fIRQStolen,1                            ; IS THIS AN "OLD" INT
        je      interrupt3                              ; YES, EOI

        ; PRINT REQUEST, HAVE SETIRQ, PRINTER OFF, PRTRESTART SET, PRINTER ON
        ; MAY NOT HAVE SENT CHAR BUT LASER PRINTERS GENERATE IRQ WHEN TURNED ON

        test    [di].commonflags,PRTRESTART             ; REQUEST IN PROGRESS?
        jnz     interrupt05                             ; JUMP IF YES.

        stc                                             ; OTHERWISE, SET CARRY
        cli                                             ; DISABLE INTS 87416
        jmp     SHORT interrupt4                        ; AND RETURN.

interrupt05:
        and     [di].commonflags,NOT CHARACTER_OUT      ; RESET CHAR SENT FLAG.

;/** END SPURIOUS TEST *************************************************/
;/*                                                                    */
;/**********************************************************************/

        test    [di].commonflags,MONPRINTING            ; IF !MON PRINTING
        jnz     interrupt0                              ; CONTINUE
        test    [di].commonflags,IOACTIVE               ; IF I/O !ACTIVE
        jz      interrupt3                              ; THEN JUST RETURN

interrupt0:

        ; I/O IS ACTIVE ON THIS DEVICE

        mov     ax,[di].initialcount                    ; GET INITIAL COUNT
        cmp     ax,[di].prtcount                        ; IF BYTES REMAIN
        jne     interrupt1                              ; THEN CONTINUE PRINTING

        ; COMPLETELY FINISHED PRINTING

        call    cleanup                                 ; CALL CLEANUP
        jmp     SHORT interrupt3                        ; RET TO HARD INT MGR

interrupt1:
        ;NOT FINISISHED PRINTING
;/**********************************************************************/
;/*     INTERRUPTS WILL BE ENABLED BY SENDCHAR ROUTINE                 */
;/**********************************************************************/
        call    sendchar                                ; PRINT NEXT CHAR

interrupt3:
        cli                                             ; DONT ALLOW NESTED INTS

ifndef SMP                                              ; #81531 start
        mov     al,20h                                  ; NON SPECIFIC EOI
        out     20h,al                                  ; ISSUE EOI TO 8259
else
        mov     al,[di].intlevel                        ; IRQ
        mov     dl,DevHlp_EOI                           ; END OF INTERRUPT
        call    DWORD PTR [device_help]                 ; CALL DEVHLP TO BLOCK
endif                                                   ; #81531 end

        clc                                             ; TELL INT MGR, MY INT
interrupt4:
        ret
EndProc interrupt

BREAK <PREPARE TO SEND A CHAR. TO A DEVICE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: SENDCHAR                                          */
;/*                                                                    */
;/* DESCRIPTIVE NAME: SEND CHARACTER ROUTINE                           */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO SEND A CHARACTER   */
;/*           TO A DEVICE IF THAT DEVICE IS NOT IN ERROR.              */
;/*                                                                    */
;/* ENTRY POINT: SENDCHAR                                              */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: DI = OFFSET TO APPROPRIATE PERPRTDATA AREA                  */
;/*                                                                    */
;/* EXIT-NORMAL:  [DI].cancelflags = filled in.                        */
;/*               Interrupts are enabled.                              */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  getstatus                                    */
;/*    ROUTINES:          sendtodev                                    */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure sendchar near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     cx,BUSYRETRY                            ; SET RETRY CNT
        .REPEAT
        call    getstatus                               ; GET DEVICE STATUS
        dec     cx                                      ; DECREMENT COUNT LOOP
        .UNTIL <BIT ah NZ NOTBUSY> OR                   ; UNTIL NOT BUSY OR
        .UNTIL CXZ                                      ; COUNT REACHES "0"

        SaveReg <es,bx>                                 ; SAVE REGISTERS
        mov     es,[di].gdtprintbuf                     ; GET USER BUF GDT SEL
        mov     bx,[di].printbufoff                     ; GET USER BUF OFFSET
        add     bx,[di].prtcount                        ; ADD PRINTED COUNT

        cmp     ah,NORMALSTAT                           ; IF NORMAL PORT STATUS
        je      sendchar1                               ; THEN SEND CHAR TO DEV
        cmp     ah,NOTBUSY                              ; IF ! DESELECTED
        jne     sendchar0                               ; THEN TRY TO RESTART
        cmp     BYTE PTR es:[bx],SELECT                 ; ELSEIF BYTE 1 = SELECT
        je      sendchar1                               ; THEN SEND CHAR TO DEV
sendchar0:
        or      [di].commonflags,PRTRESTART             ; ELSE SET RESTART FLAG
        mov     [di].cancelstatus,ah                    ; SAVE STATUS FLAGS
        jmp     SHORT sendchar2                         ; EXIT
sendchar1:
        and     [di].commonflags,NOT MONERRPKTSENT      ; CLEAR PKT SENT FLAG
        and     [di].commonflags,NOT PRTRESTART         ; CLEAR RESTART FLAG
        mov     [di].cancelstatus,CANNOERROR            ; CLEAR CANCEL STATUS
        mov     [di].cancelflags,CANNOERROR             ; SET NO ERROR
        mov     [di].timeoutctr,0                       ; RESET TIMEOUT COUNTER
        call    sendtodev                               ; SEND CHAR TO DEVICE
sendchar2:
        RestoreReg <bx,es>                              ; RESTORE REGISTERS
        sti                                             ; ENABLE INTERRUPTS
        ret
EndProc sendchar

BREAK <SEND A CHARACTER TO A DEVICE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: SENDTODEV                                         */
;/*                                                                    */
;/* DESCRIPTIVE NAME: SEND CHARACTER ROUTINE                           */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO SEND A CHARACTER   */
;/*           TO A DEVICE.                                             */
;/*                                                                    */
;/* ENTRY POINT: SENDTODEV                                             */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: DI = OFFSET TO APPROPRIATE PERPRTDATA AREA                  */
;/*        DX = DATA PORT ADDRESS                                      */
;/*        ES:BX = PTR TO APPROPRIATE DATA BUFFER                         */
;/*                                                                    */
;/* EXIT-NORMAL:  [di].prtcount = incremented by 1.                    */
;/*               set CHARACTER_OUT in commonflags                     */
;/*                                                                    */
;/* EXIT-ERROR : N/A                                                   */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure sendtodev near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     al,BYTE PTR es:[bx]                     ; GET NEXT CHAR TO PRINT
        inc     [di].prtcount                           ; INC PRINTED COUNT

        cli                                             ; DISABLE INTERRUPTS
        or      [di].commonflags,CHARACTER_OUT          ; SET CHAR OUT FLAG

        out     dx,al                                   ; SEND DATA TO DATA PORT
        inc     dx                                      ; POINT TO CONTROL PORT
        inc     dx                                      ; POINT TO CONTROL PORT
        mov     al,STROBELOW                            ; SET STROBE LOW BIT
        cmp     [di].curintlevel,0                      ; IF !RUNNING ON TIMER
        jne     sendtodev1                              ; THEN IRQ ENABLE SET
        and     al,NOT IRQENABLEBIT                     ; ELSE NO IRQ ENABLE
sendtodev1:
        out     dx,al                                   ; STROBE >1us AND <5us
        DevIODelay <bx>                                 ; I/O DELAY - STR WIDTH
        DevIODelay <bx>                                 ; I/O DELAY - STR WIDTH
        mov     al,STROBEHIGH                           ; SET THE STROBE HIGH
        cmp     [di].curintlevel,0                      ; IF !RUNNING ON TIMER
        jne     sendtodev2                              ; THEN IRQ ENABLE SET
        and     al,NOT IRQENABLEBIT                     ; ELSE NO IRQ ENABLE
sendtodev2:
        out     dx,al                                   ; NEW CTRL VALUE TO PORT
        sti                                             ; ELSE ENABLE INTERRUPTS
        ret
EndProc sendtodev

BREAK <TIMEOUT ROUTINE FOR DEVICE LPT1>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRT_TIMEOUT_1                                    */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Timeout routine for LPT1                        */
;/*                                                                    */
;/* FUNCTION:   This routine calculates the index into the device data */
;/*             area and calls the general timeout routine.  This      */
;/*             routine is called by timer services once every second. */
;/*                                                                    */
;/* NOTES:  It is the timeout routine's responsibility to save and     */
;/*         restore all registers used in the printer timeout handler. */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prt_timeout_1:far                                */
;/*                   call prt_timeout_1                               */
;/*                                                                    */
;/* INPUT:  DS = our data segment                                      */
;/*                                                                    */
;/* EXIT-NORMAL:  All registers are restored.                          */
;/*                                                                    */
;/* EXIT-ERROR:  See EXIT-NORMAL above.                                */
;/*                                                                    */
;/* INTERNAL REFERENCES:  prt_timeout                                  */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prt_timeout_1 far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha                                           ; SAVE ALL REGISTERS
        push    es                                      ; SAVE EXTRA SEGMENT
        mov     di,(LPT1_INDEX * SIZE printer_database) ; TIMEOUT DEVICE = LPT1
        call    prt_timeout                             ; GEN TIMEOUT ROUTINE
        pop     es                                      ; RESTORE EXTRA SEGMENT
        popa                                            ; RESTORE ALL REGISTERS
        ret                                             ; RET TO TIMER MANAGER
EndProc prt_timeout_1

BREAK <TIMEOUT ROUTINE FOR DEVICE LPT2>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRT_TIMEOUT_2                                    */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Timeout routine for LPT2                        */
;/*                                                                    */
;/* FUNCTION:   This routine calculates the index into the device data */
;/*             area and calls the general timeout routine.  This      */
;/*             routine is called by timer services once every second. */
;/*                                                                    */
;/* NOTES:  It is the timeout routine's responsibility to save and     */
;/*         restore all registers used in the printer timeout handler. */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prt_timeout_2:far                                */
;/*                   call prt_timeout_2                               */
;/*                                                                    */
;/* INPUT:  DS = our data segment                                      */
;/*                                                                    */
;/* EXIT-NORMAL:  All registers are restored.                          */
;/*                                                                    */
;/* EXIT-ERROR:  See EXIT-NORMAL above.                                */
;/*                                                                    */
;/* INTERNAL REFERENCES:  prt_timeout                                  */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prt_timeout_2 far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha                                           ; SAVE ALL REGISTERS
        push    es                                      ; SAVE EXTRA SEGMENT
        mov     di,(LPT2_INDEX * SIZE printer_database) ; TIMEOUT DEVICE = LPT2
        call    prt_timeout                             ; GEN TIMEOUT ROUTINE
        pop     es                                      ; RESTORE EXTRA SEGMENT
        popa                                            ; RESTORE ALL REGISTERS
        ret                                             ; RET TO TIMER MANAGER
EndProc prt_timeout_2

BREAK <TIMEOUT ROUTINE FOR DEVICE LPT3>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRT_TIMEOUT_3                                    */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Timeout routine for LPT3                        */
;/*                                                                    */
;/* FUNCTION:   This routine calculates the index into the device data */
;/*             area and calls the general timeout routine.  This      */
;/*             routine is called by timer services once every second. */
;/*                                                                    */
;/* NOTES:  It is the timeout routine's responsibility to save and     */
;/*         restore all registers used in the printer timeout handler. */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prt_timeout_3:far                                */
;/*                   call prt_timeout_3                               */
;/*                                                                    */
;/* INPUT:  DS = our data segment                                      */
;/*                                                                    */
;/* EXIT-NORMAL:  All registers are restored.                          */
;/*                                                                    */
;/* EXIT-ERROR:  See EXIT-NORMAL above.                                */
;/*                                                                    */
;/* INTERNAL REFERENCES:  prt_timeout                                  */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prt_timeout_3 far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha                                           ; SAVE ALL REGISTERS
        push    es                                      ; SAVE EXTRA SEGMENT
        mov     di,(LPT3_INDEX * SIZE printer_database) ; TIMEOUT DEVICE = LPT3
        call    prt_timeout                             ; GEN TIMEOUT ROUTINE
        pop     es                                      ; RESTORE EXTRA SEGMENT
        popa                                            ; RESTORE ALL REGISTERS
        ret                                             ; RET TO TIMER MANAGER
EndProc prt_timeout_3

BREAK <GENERAL TIMEOUT ROUTINE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRT_TIMEOUT                                      */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  The timeout entry point is used to cancel the   */
;/*                    timed out print block request.                  */
;/*                                                                    */
;/* FUNCTION:  This routine is called 1 time a second.  It will count  */
;/*            timeoutmax times to see if a timeout has occurred.      */
;/*            If the restart flag is on, this routine will try to     */
;/*            immediately restart the request otherwise if timeout    */
;/*            equals timeoutmax, it will deregister the timeout       */
;/*            handler from the Timer Manager, and will call the       */
;/*            cleanup routine to cancel the request.  If infinite     */
;/*            retry is set, this routine will try to restart the      */
;/*            print request.                                          */
;/*                                                                    */
;/* NOTES:                                                             */
;/*                                                                    */
;/* ENTRY POINT:  PRT_TIMEOUT:NEAR                                     */
;/*    LINKAGE:  CALL PRT_TIMEOUT                                      */
;/*                                                                    */
;/* INPUT:  DI = offset of device into perprtarea.                     */
;/*         [DI].timeoutmax = maximum wait time in seconds before      */
;/*                           canceling the print request.             */
;/*         [DI].timeoutctr = counter maintained to reach timeoutmax   */
;/*                                                                    */
;/* EXIT-NORMAL:  [DI].cancelflags = CANIOERROR                        */
;/*                                                                    */
;/* EXIT-ERROR:  See EXIT-NORMAL above.                                */
;/*                                                                    */
;/* INTERNAL REFERENCES:  cleanup                                      */
;/*    ROUTINES:          prterp                                       */
;/*                       sendmonerpkt                                 */
;/*                       sendchar                                     */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prt_timeout near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        add     di,OFFSET perprtarea                    ; START OF PERPRTDATA

        test    [di].commonflags, USEPOLLING            ; USING POLLING?
        jz      prt_timeout_interrupts                  ; NO, USING INTERUPTS
        call    prt_timeout_polling                     ; YES, CALL POLLING FUNC
        jmp     prt_time_2                              ; AND EXIT

prt_timeout_interrupts:
        test    [di].commonflags1,CANMONPKT             ; CANCEL MON PRT REQ?
        jnz     prt_time_05                             ; YES

        inc     [di].timeoutctr                         ; INC CTR TO 20 SECONDS
        mov     ax,[di].timeoutmax                      ; GET TIMEOUT LIMIT
        cmp     [di].timeoutctr,ax                      ; IF !20 SECOND TIMEOUT
        jb      prt_time_1                              ; THEN TRY TO RESEND

        mov     [di].timeoutctr,0                       ; ELSE CLEAR THE COUNTER
        test    [di].commonflags,MONPRINTING            ; ERROR ON MON REQ?
        jz      prt_time_0                              ; NO
        test    [di].commonflags,MONERRPKTSENT          ; ERR PKT ALREADY SENT
        jnz     prt_time_0                              ; YES
        call    prterp                                  ; GET ERROR CODE
        call    sendmonerpkt                            ; SEND MON ERROR PKT
prt_time_0:
        or      [di].commonflags,PRTRESTART             ; ASSUME INF RETRY
        test    [di].commonflags,INFINRETRY             ; IF INF RETRY
        jnz     prt_time_1                              ; THEN PROC INF RETRY
        and     [di].commonflags,NOT PRTRESTART         ; ASSUMED WRONG, RESET
prt_time_05:
        call    prterp                                  ; GET CANCEL FLAGS
        call    cleanup                                 ; CLEANUP
        jmp     prt_time_2                              ; EXIT

prt_time_1:                                             ; THEN INFINITE RETRY
        cmp     [di].curintlevel,0                      ; IF !USING INTERRUPTS
        je      prt_time_2                              ; THEN USING TIMER, NOP
        test    [di].commonflags,PRTRESTART             ; ELSEIF RESTART IS OFF
        jz      prt_time_2                              ; THEN NO ERRORS, NOP
        call    sendchar                                ; ELSE INTS/ERRS RESTART
prt_time_2:
        ret
EndProc prt_timeout

BREAK <CLEANUP ROUTINE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  CLEANUP ROUTINE                                  */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Print cleanup routine.                          */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO PERFORM THE        */
;/*           CLEANUP WORK WHEN A REQUEST IS COMPLETED.  THIS ROUTINE  */
;/*           RETURN INFORMATION IN THE REQUEST BLOCK AND ISSUES A     */
;/*           DEVICE DONE ON THE REQUEST BLOCK.                        */
;/*                                                                    */
;/* NOTE:     SetIRQ done on printblock call and UnSetIRQ done on      */
;/*           cleanup calls.  This allows sharing of IRQ5 and 7 on AT. */
;/*                                                                    */
;/* ENTRY POINT: CLEANUP                                               */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: DI = offset to appropriate perprtdata area                  */
;/*        [DI].cancelflags = filled in.                               */
;/*                                                                    */
;/* EXIT-NORMAL:                                                       */
;/*                                                                    */
;/* EXIT-ERROR :                                                       */
;/*                                                                    */
;/* INTERNAL REFERENCES:  stoptimer                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  ResetTimer                      */
;/*    ROUTINES:                       UnSetIRQ                        */
;/*                                    ProcRun                         */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure cleanup near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     ax,[di].timeout_off                     ; TIMEOUT ENTRY POINT
        mov     dl,DevHlp_ResetTimer                    ; RESET TIMEOUT TIMER
        call    DWORD PTR [device_help]                 ; CALL TIMER SERVICES

        cmp     [di].curintlevel,0                      ; IF !RUNNING ON TIMER
        jne     cleanup1                                ; THEN RUNNING ON INTS
        call    stoptimer                               ; RESET DEVICE ON TIMER
        jmp     SHORT cleanup2

cleanup1:
        xor     bh,bh                                   ; MAKE BH ZERO
        mov     bl,[di].intlevel                        ; IRQ NUMBER 0-F
        mov     dl,DevHlp_UnSetIRQ                      ; UNSET IRQ AT 8259
        call    DWORD PTR [device_help]                 ; CALL HARD INT MGR

        mov     dx,[di].deviceaddr                      ; GET DEVICE ADDRESS
        inc     dx
        inc     dx                                      ; POINT TO CONTROL PORT
        mov     al,DISABLEINT                           ; DISABLE INT AT PORT
        out     dx,al                                   ; SEND NEW CTRL VALUE

cleanup2:
        mov     es,[di].dosrqseg                        ; ASSUME DOS REQUEST BLK
        mov     bx,[di].dosoffset                       ; ASSUME DOS REQUEST BLK
        test    [di].commonflags,MONPRINTING            ; IF !MONITOR REQUEST
        jz      cleanup3                                ; THEN ASSUMED CORRECTLY
        mov     es,[di].monseg                          ; MON REQUEST BLOCK
        mov     bx,[di].monoffset                       ; MON REQUEST BLOCK
cleanup3:
        cmp     es:[bx].PktCmd,CMDOUTPUT                ; IF COMMAND = WRITE
        je      cleanup4                                ; THEN RETURN PRT COUNT
        cmp     es:[bx].PktCmd,CMDOUTPUTV               ; IF COMMAND = WRITE/V
        jne     cleanup5                                ; THEN RETURN PRT COUNT
cleanup4:
        mov     dx,[di].prtcount                        ; GET COUNT PRINTED

;/**********************************************************************/
;/*                                                                    */
;/* ANOTHER 3X BOX KLUGE - PTM 7095                                    */
;/*                                                                    */
;/**********************************************************************/
;/*                                                                    */
;/* THE FOLLOWING CODE IS REQUIRED TO FIX A SITUATION IN THE REAL MODE */
;/* BOX CAUSED BY THE "COPY" AND "TYPE" COMMANDS WHICH ARE DIRECTED TO */
;/* THE PRINTER.  THESE COMMANDS ISSUE CHARACTER REQUESTS ONE          */
;/* CHARACTER AT A TIME.  IF THE PRINTER IS A BUFFERED DEVICE AND IT   */
;/* IS TURNED OFF LINE, IT IS QUITE POSSIBLE THAT THE CHARACTER HAS    */
;/* BEEN SENT TO THE PRINTER BUFFER BUT THE INTERRUPT WON'T BE         */
;/* RECEIVED AND WE WILL TIME OUT.  THIS RESULTS IN AN ERROR BEING     */
;/* RETURNED IN THE REQUEST PACKET BUT THE BYTE COUNT PRINTED IS THE   */
;/* TOTAL BYTE COUNT REQUESTED.  THE HARD ERROR HANDLER GETS THE ERROR */
;/* AND ALLOWS THE USER TO SELECT THE RETRY FUNCTION.  THIS CAUSES THE */
;/* SAME CHARACTER TO BE SENT TO THE PRINTER WHEN IT IS PUT BACK ON    */
;/* LINE AGAIN.                                                        */
;/*                                                                    */
;/* THE FOLLOWING CODE WILL CLEAR ANY ERROR STATUS SET BY OTHER PARTS  */
;/* OF THE DEVICE DRIVER ONLY IF THE TOTAL NUMBER OF CHARS REQUESTED   */
;/* HAVE BEEN SENT TO THE DEVICE.  THIS MEANS THAT THE REQUEST IS      */
;/* COMPLETE.  THIS WAY, THE HARD ERROR HANDLER WON'T RE-ISSUE THE     */
;/* REQUEST FOR THE SAME CHARACTER IF RETRY IS SELECTED.  THE HARD     */
;/* ERROR HANDLER WON'T SEE THE ERROR FOR THIS CHARACTER.              */
;/*                                                                    */
;/**********************************************************************/

        cmp     [di].initialcount,dx                    ; IF BYTES REMAINING
        jne     cleanup45                               ; THEN MUST SET ERROR
        mov     [di].cancelflags,CANNOERROR             ; SET NO ERROR
cleanup45:

;/**********************************************************************/
;/*                                                                    */
;/* END OF KLUGE.                                                      */
;/*                                                                    */
;/**********************************************************************/

        mov     es:[bx].IOcount,dx                      ; PUT COUNT IN REQ PKT
cleanup5:
        cmp     es:[bx].PktStatus,CHARIOINT             ; CTRL C PRESSED BY USER
        je      cleanup6                                ; YES, EXIT

        xor     ah,ah                                   ; ZERO OUT AH
        mov     al,[di].cancelflags                     ; GET CANCEL FLAGS
        mov     [di].cancelflags,0                      ; ZERO OUT CANCEL FLAGS
        mov     si,ax                                   ; GET ERROR CODE
        sal     si,1                                    ; GET WORD INDEX
        mov     ax,doserrors[si]                        ; GET ERROR CODE
        mov     es:[bx].PktStatus,ax                    ; PUT ERR. IN REQ.BLK

        mov     ax,es                                   ; BLOCK ID
        inc     bx                                      ; UNIQUE BLOCK ID
        mov     dl,DevHlp_ProcRun                       ; RUN PRINTBLOCK THREAD
        call    DWORD PTR [device_help]                 ; DEVICE HELP
        dec     bx                                      ; ADDRESS REQUEST BLOCK

cleanup6:
        ret
EndProc cleanup

BREAK <PRINTER ERROR STATUS PROCESSING>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: PRTERP                                            */
;/*                                                                    */
;/* DESCRIPTIVE NAME: ANALYZE STATUS FLAGS                             */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO ANALYZE THE STATUS */
;/*           INFORMATION RETURNED FROM "GETSTATUS" AND SAVED IN THE   */
;/*           CANCELSTATUS BYTE.                                       */
;/*                                                                    */
;/* ENTRY POINT: PRTERP                                                */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: DI = OFFSET TO APPROPRIATE PERPRTDATA AREA                  */
;/*        AH = PRINTER STATUS                                         */
;/*                                                                    */
;/* EXIT-NORMAL:  [DI].cancelflags = filled in.                        */
;/*                                                                    */
;/* EXIT-ERROR :  See EXIT-NORMAL above.                               */
;/*                                                                    */
;/* INTERNAL REFERENCES:  getstatus                                    */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
;***********************************************************************
;*                                                                     *
;*      CHECK STATUS BITS AND TAKE APPROPRIATE ACTION.                 *
;*      MESSAGES CURRENTLY ISSUED FOR ERROR CONDITIONS:                *
;*                                                                     *
;*      #20 = THE SYSTEM CANNOT FIND THE DEVICE SPECIFIED.             *
;*                                                                     *
;*      #28 = THE PRINTER IS OUT OF PAPER.                             *
;*                                                                     *
;*      #29 = THE SYSTEM CANNOT WRITE TO THE SPECIFIED DEVICE.         *
;*                                                                     *
;***********************************************************************
Procedure prterp near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     [di].cancelflags,CANNOERROR             ; CLEAR CANCEL FLAGS
        mov     ah,[di].cancelstatus                    ; GET STATUS FLAGS
        and     ah,ah                                   ; CHECK IF ZERO
        .IF     Z
          call    getstatus
          mov     [di].cancelstatus,ah                  ; SAVE CANCEL STATUS
        .ENDIF
;*******************************************************************************
;* B0 = NOT BUSY & PAPER OUT & SELECTED = !CABLED ON MONO / SERIAL ADAPTER #20 *
;*******************************************************************************
        .IF <BIT ah NZ NOTBUSY> AND                     ; IF NOT BUSY AND
        .IF <BIT ah NZ PAPEROUT> AND                    ; IF PAPER OUT AND
        .IF <BIT ah NZ NOTSELECTED>                     ; IF SELECTED
          mov     [di].cancelflags,CANNOTSELECT         ; SET SELECT ERROR
;*******************************************************************************
;* 30 = BUSY & PAPER OUT & SELECTED & NOT I/O ERROR = OUT OF PAPER         #28 *
;*******************************************************************************
        .ELSEIF <BIT ah Z NOTBUSY> AND                  ; IF BUSY AND
        .IF <BIT ah NZ PAPEROUT> AND                    ; IF PAPER OUT AND
        .IF <BIT ah NZ NOTSELECTED> AND                 ; IF SELECTED AND
        .IF <BIT ah Z IOERROR>                          ; IF NOT I/O ERROR
          mov     [di].cancelflags,CANPAPEROUT          ; SET PAPER OUT ERROR
;*******************************************************************************
;* 28 = BUSY & PAPER OUT & NOT SELECTED & I/O ERROR = OUT OF PAPER         #28 *
;*******************************************************************************
        .ELSEIF <BIT ah Z NOTBUSY> AND                  ; IF BUSY AND
        .IF <BIT ah NZ PAPEROUT> AND                    ; IF PAPER OUT AND
        .IF <BIT ah Z NOTSELECTED> AND                  ; IF NOT SELECTED AND
        .IF <BIT ah NZ IOERROR>                         ; IF I/O ERROR
          mov     [di].cancelflags,CANPAPEROUT          ; SET PAPER OUT ERROR
;*******************************************************************************
;* 10 = BUSY & SELECTED = NOT READY                                        #29 *
;*******************************************************************************
        .ELSEIF <BIT ah Z NOTBUSY> AND                  ; IF BUSY AND
        .IF <BIT ah NZ NOTSELECTED>                     ; IF SELECTED
          mov     [di].cancelflags,CANIOERROR           ; SET I/O ERROR
;*******************************************************************************
;* 08 = BUSY & I/O ERROR = OFF LINE                                        #29 *
;*******************************************************************************
        .ELSEIF <BIT ah Z NOTBUSY> AND                  ; IF BUSY AND
        .IF <BIT ah NZ IOERROR>                         ; IF I/O ERROR
          mov     [di].cancelflags,CANIOERROR           ; SET I/O ERROR
;*******************************************************************************
;* 00 = BUSY & NOTHING ELSE = NOT READY                                    #29 *
;*******************************************************************************
        .ELSEIF <ah EQ 0>                               ; IF ONLY BUSY
          mov     [di].cancelflags,CANIOERROR           ; SET I/O ERROR
;*******************************************************************************
;* 88 = NOT BUSY & I/O ERROR = POWERED OFF                                 #20 *
;*******************************************************************************
        .ELSEIF <BIT ah NZ NOTBUSY> AND                 ; IF NOT BUSY AND
        .IF <BIT ah NZ IOERROR>                         ; IF I/O ERROR
          mov     [di].cancelflags,CANNOTSELECT         ; SET SELECT ERROR
        .ELSE                                           ; NOT A "SELECT" CHAR
          mov     [di].cancelflags,CANNOTSELECT         ; SET SELECT ERROR
        .ENDIF
        xor     ah,ah                                   ; ZERO OUT AH
        mov     [di].cancelstatus,ah                    ; SET STATUS FLAGS = 0
        RET
EndProc prterp

BREAK <GET STATUS OF SPECIFIED DEVICE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: GETSTATUS                                         */
;/*                                                                    */
;/* DESCRIPTIVE NAME: GET STATUS ROUTINE                               */
;/*                                                                    */
;/* FUNCTION: THE FUNCTION OF THIS SUBROUTINE IS TO RETURN THE STATUS  */
;/*           OF THE REQUESTED DEVICE IN REGISTER AH.                  */
;/*                                                                    */
;/* NOTES:  DATA, STATUS, THEN CONTROL PORT.                           */
;/*                                                                    */
;/* ENTRY POINT: GETSTATUS                                             */
;/*    LINKAGE : CALL NEAR OR FAR                                      */
;/*                                                                    */
;/* INPUT: DI = OFFSET TO APPROPRIATE PERPRTDATA AREA                  */
;/*                                                                    */
;/* EXIT-NORMAL:  AH = DEVICE STATUS, DX = DATA PORT ADDRESS           */
;/*                                                                    */
;/* EXIT-ERROR :  AH = DEVICE STATUS, DX = DATA PORT ADDRESS           */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure getstatus,HYBRID
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        mov     dx,[di].deviceaddr                      ; GET DEVICE ADDRESS
        inc     dx                                      ; POINT TO STATUS PORT
        in      al,dx                                   ; GET STATUS BYTE
        mov     ah,al                                   ; MOV STATUS INTO AH
        and     ah,UNUSEDBITS                           ; TURN OFF UNUSED BITS
        xor     ah,REVERSEBITS                          ; FLIP A COUPLE OF BITS
        dec     dx                                      ; RESET DX TO DATA ADDR
        ret
EndProc getstatus

;  *** END OF RESIDENT CODE ***
;  ALL CODE AFTER THIS POINT IS RELEASED AFTER INITIALIZATION
CODEEND EQU     $ - 1                                   ; USE LIMIT OF SEGMENT

BREAK <INITIALIZATION ROUTINE>
;/*********************** END OF SPECIFICATIONS ************************/
;/*                                                                    */
;/* SUBROUTINE NAME:  PRTINIT                                          */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Initialize the printer device driver            */
;/*                                                                    */
;/* FUNCTION:  This routine is called to perform any parallel port     */
;/*            device driver initialization.  This routine will be     */
;/*            called once for each device header defined in the data  */
;/*            segment.  All initialization is performed on the first  */
;/*            call.                                                   */
;/*                                                                    */
;/* NOTES:                                                             */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prtinit:NEAR                                     */
;/*                   call prtinit                                     */
;/*                                                                    */
;/* INPUT:  ES = Virtual segment of kernel request block               */
;/*         BX = Virtual offset of kernel request block                */
;/*         DI = Offset to appropriate perprtdata area                 */
;/*                                                                    */
;/* EXIT-NORMAL:  Status field of Kernel Request Block is filled in.   */
;/*               See the specific request in the CP/DOS OS and Util.  */
;/*                 Functional Specification for other return values.  */
;/*               timeoutval = # of ticks to wait for timeout          */
;/*                                                                    */
;/* EXIT-ERROR:  see EXIT-NORMAL above.                                */
;/*                                                                    */
;/* EFFECTS:                                                           */
;/*                                                                    */
;/* INTERNAL REFERENCES:  eisainit                                     */
;/*    ROUTINES:          _RM_PRT_CreateDriver                         */
;/*                       parsecmdline                                 */
;/*                       RM_PRT_RegisterResources                     */
;/*                       initialprt                                   */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  DEVICE_HELP  GetDOSVar                       */
;/*    ROUTINES:                       RegisterPDD                     */
;/*                                    AllocGDTSelector                */
;/*                                    VirtToPhys                      */
;/*                                    RegisterPerfCtrs                */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prtinit far
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        SaveReg <es,bx>                                 ; SAVE REGISTERS
        test    flags,FIRSTINIT                         ; IF 1ST TIME THEN
        jz      prtinit0                                ; DO ONCE ONLY CODE
        jmp     prtinit1                                ; SKIP ONCE ONLY CODE

        ; INITIALIZATION CODE DONE ONCE ONLY (DONE DURING PRN INIT CALL)
        ; (FROM HERE TO PRTINIT1)

prtinit0:
        or      flags,FIRSTINIT                         ; SET FIRST INIT ON

        ; SAVE ADDRESS OF DEVHLP ROUTINE

        mov     ax,WORD PTR es:[bx].InitpEnd            ; OFFSET OF DEVHLP
        mov     WORD PTR device_help,ax                 ; OFFSET OF DEVHLP
        mov     WORD PTR _Device_Help,ax                ; OFFSET OF DEVHLP
        mov     ax,WORD PTR es:[bx].InitpEnd + 2        ; SEG OF DEVHLP
        mov     WORD PTR device_help + 2,ax             ; SEG OF DEVHLP
        mov     WORD PTR _Device_Help + 2,ax            ; SEG OF DEVHLP

        ; GET POINTER TO GLOBAL INFO SEG

        mov     al,GLOBINFOSEG                          ; GET VAR GLOBALINFOSEG
        mov     dl,DevHlp_GetDOSVar                     ; GET DOS VAR FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        mov     es,ax                                   ; AX:BX -> GLOBALINFOSEG
        mov     es,WORD PTR es:[bx]                     ; ES:BX -> ADDR/GLOBINFO
        xor     bx,bx
        mov     WORD PTR glinfoseg,bx                   ; GLOBAL INFOSEG OFF
        mov     WORD PTR glinfoseg + 2,es               ; GLOBAL INFOSEG SEG
        mov     bx,es:[bx].SIS_ClkIntrvl                ; GET TIMER INTERVAL

        xor     dx,dx
        mov     ax,10000                                ; MICROSEC TO SEC RULE
        div     bx                                      ; TICKS / MICROSEC
        mov     timeoutval,ax                           ; SAVE TICKS / SEC

        ; GET POINTER TO LOCAL INFO SEG

        mov     al,LOCINFOSEG                           ; GET VAR LOCAL INFOSEG
        mov     dl,DevHlp_GetDOSVar                     ; GET DOS VAR FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        mov     WORD PTR dosvar,bx                      ; OFF LOCAL INFOSEG
        mov     WORD PTR dosvar + 2,ax                  ; SEG LOCAL INFOSEG

        ; REGISTER THE PLPT'S VDD SERVICES ENTRY POINT WITH VLPT.
        ; THIS ENTRY POINT IS THE ROUTER FOR ALL THE IDC COMMUNICATIONS.

        push    di                                      ; SAVE PER PRT AREA
        lea     si,plptname                             ; LOAD PLPT NAME
        push    SWAPSEG                                 ; SETUP ES
        pop     es                                      ;
        lea     di,PLPTCMD_Entry                        ; ES:DI -> PLPT ROUTER
        mov     dl,DevHlp_RegisterPDD                   ; REGISTERPDD FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
        pop     di

        ; REGISTER DRIVER WITH RESOURCE MANAGER

        push    di                                      ; SAVE PERPRTDATA AREA
        push    ds                                      ; DRIVER NAME SEG
        lea     ax,ddname                               ; GET DRIVER NAME OFF
        push    ax                                      ; DRIVER NAME OFF
        call    _RM_PRT_CreateDriver                    ; REGISTER DRIVER
        add     sp,4                                    ; CLEAN UP STACK
        pop     di                                      ; RESTORE PERPRTDATA

        ; DETERMINE BUS TYPE

        mov     bustype,ISABUS                          ; ASSUME ISA BUS SYSTEM
        call    eisainit                                ; CHECK FOR EISA SYSTEM
        jc      prtinit0a                               ; ASSUMED CORRECT - ISA
        mov     bustype,EISABUS                         ; EISA BUS SYSTEM
prtinit0a:

        ; SET TIMEOUT ENTRY POINTS AND DEVICE INDICES

        RestoreReg <bx,es>                              ; RESTORE REGISTERS
        SaveReg <es,bx>                                 ; SAVE REGISTERS
        push    di                                      ; SAVE PERPRTAREA PTR
        les     bx,es:[bx].InitParms                    ; GET CONFIG.SYS PARMS

        mov     di,OFFSET perprtarea                    ; STARTING POINT
        mov     [di].deviceindex,LPT1_INDEX             ; INDEX FOR LPT1
        mov     [di].deviceflag,LPT1_FLAG               ; FLAG FOR LPT1
        mov     ax,es:[bx].LPT1BufSize                  ; GET LPT1 BUFFER SIZE
        mov     [di].configbufsize,ax                   ; SAVE LPT1 BUFFER SIZE
        mov     [di].prfvw_off,OFFSET prfvw_ctrs        ; START OF DEVICE CTRS
        mov     [di].timeout_off,OFFSET prt_timeout_1   ; LPT1 EPT

        add     di,SIZE printer_database
        mov     [di].deviceindex,LPT2_INDEX             ; INDEX FOR LPT2
        mov     [di].deviceflag,LPT2_FLAG               ; FLAG FOR LPT2
        mov     ax,es:[bx].LPT2BufSize                  ; GET LPT2 BUFFER SIZE
        mov     [di].configbufsize,ax                   ; SAVE LPT2 BUFFER SIZE
        mov     [di].prfvw_off,OFFSET prfvw_ctrs        ; START OF PERFVIEW CTRS
        add     [di].prfvw_off,SIZE prfvw_devarea       ; START OF DEVICE CTRS
        mov     [di].timeout_off,OFFSET prt_timeout_2   ; LPT2 EPT

        add     di,SIZE printer_database
        mov     [di].deviceindex,LPT3_INDEX             ; INDEX FOR LPT3
        mov     [di].deviceflag,LPT3_FLAG               ; FLAG FOR LPT3
        mov     ax,es:[bx].LPT3BufSize                  ; GET LPT3 BUFFER SIZE
        mov     [di].configbufsize,ax                   ; SAVE LPT3 BUFFER SIZE
        mov     [di].prfvw_off,OFFSET prfvw_ctrs        ; START OF PERFVIEW CTRS
        add     [di].prfvw_off,(2 * (SIZE prfvw_devarea)) ; START OF DEVICE CTRS
        mov     [di].timeout_off,OFFSET prt_timeout_3   ; LPT3 EPT
        pop     di                                      ; RESTORE PERPRTAREA PTR
        jmp     prtinit3                                ; EXIT

        ; PDD INITIALIZATION CODE FOR EACH PHYSICAL DEVICE (LPT1, LPT2, LPT3)

prtinit1:
        xor     ah,ah                                   ; CLEAR AH
        mov     al,[di].deviceindex                     ; GET ZERO BASED INDEX
        add     [di].signature+3,al                     ; PPDA SIGNATURE

        ; ALLOCATE GDT SELECTOR FOR SENDING DATA TO MONITORS

        SaveReg <ax,di>                                 ; SAVE REGISTERS
        push    ds
        pop     es
        lea     di,[di].gdtuserbuf                      ; ES:DI -> GDT
        mov     cx,1                                    ; ALLOCATE 1 SELECTOR
        mov     dl,DevHlp_AllocGDTSelector              ; ALLOC GDT SEL FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        RestoreReg <di,ax>                              ; RESTORE REGISTERS
        jnc     prtinit11                               ; IF NO ERROR, CONTINUE
        jmp     prtinit4                                ; IF ERROR, EXIT
prtinit11:

        ; ALLOCATE GDT SELECTOR FOR PRINTING

        SaveReg <ax,di>                                 ; SAVE REGISTERS
        lea     di,[di].gdtprintbuf                     ; ES:DI -> GDT
        mov     cx,1                                    ; ALLOCATE 1 SELECTOR
        mov     dl,DevHlp_AllocGDTSelector              ; ALLOC GDT SEL FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVHLP
        RestoreReg <di,ax>                              ; RESTORE REGISTERS
        jnc     prtinit12                               ; IF NO ERROR, CONTINUE
        jmp     prtinit4                                ; IF ERROR, EXIT
prtinit12:

        ; SETUP POINTER TO CACHE TO BUFFER SMALL WRITE REQUESTS WITH THE
        ; SAME SFN. AT APPROPRIATE TIME, SEND PACKET TO MONITOR.

        imul    si,ax,SIZE mcache                       ; CALC OFF IN MON CACHE
        add     si,OFFSET moncache                      ; SI -> DEVICE MON CACHE
        mov     [di].cache_off,si                       ; SAVE MON CACHE OFFSET

        ; SETUP POINTER TO BUFFER TO CONTAIN MONITOR PACKET TO BE SENT
        ; TO MONITORS FOR THIS DEVICE.

        mov     cx,dataend                              ; GET CURRENT MBUF START
        mov     [di].tomonbuf_off,cx                    ; SAVE TO MON BUF OFFSET
        add     cx,[di].configbufsize                   ; CALC NEW MON BUF START

        ; SETUP POINTER TO BUFFER TO CONTAIN MONITOR PACKET TO BE RECEIVED
        ; FROM MONITORS FOR THIS DEVICE (MONITOR FINAL BUFFER).

        mov     [di].frommonbuf_off,cx                  ; SAVE FROM MON BUF OFF
        add     cx,[di].configbufsize                   ; CALC NEW MON BUF START
        mov     dataend,cx                              ; SET NEW DATA SEG END

        ; CONVERT VIRTUAL MONITOR FINAL BUFFER ADDRESS TO PHYSICAL ADDRESS
        ; AND SAVE IT FOR FUTURE MONITOR REQUESTS.

        mov     si,[di].frommonbuf_off                  ; DS:SI -> VIRT MON PKT
        lea     si,[si].frommonbuf                      ; DS:SI -> VIRT ADDR BUF
        mov     dl,DevHlp_VirtToPhys                    ; VIRTTOPHYS FUNCTION
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
        jc      prtinit4                                ; IF ERROR, EXIT
        mov     [di].physbufoff,bx                      ; SAVE PHYS BUF OFF
        mov     [di].physbufseg,ax                      ; SAVE PHYS BUF SEG

        ; PARSE FOR COMMAND LINE PARAMETERS

        RestoreReg <bx,es>                              ; RESTORE REGISTERS
        SaveReg <es,bx>                                 ; SAVE REGISTERS
        call    ParseCmdLine                            ; PARSE CMD LINE PARMS

        ; DETERMINE HARDWARE CAPABILITIES

        call    prtdeterminehardware                    ; CHECK FOR HARDWARE
        jc      prtinit4                                ; IF ERROR, EXIT

        test    [di].commonflags1,PORTEXISTS            ; IF PORT DOESN'T EXIST
        jz      prtinit2                                ; THEN DON'T REGISTER

        ; REGISTER WITH RESOURCE MANAGER

        call    RM_PRT_RegisterResources                ; OBTAIN RESOURCES
        jc      prtinit4                                ; IF ERROR, EXIT

        ; INITIALIZE DEVICE

        or      [di].commonflags1,BOOTINIT              ; SET BOOTINIT FLAG
        CALLFAR initialprt                              ; INITIALIZE DEVICE
        and     [di].commonflags1,NOT BOOTINIT          ; RESET BOOTINIT FLAG

        ; ADD PER-DEVICE PERFVIEW COUNTERS

        add     db_length,SIZE prfvw_devarea            ; ADD DEV TO DATABLOCK
        add     WORD PTR tb_totalctrs,NUMBER_TMRS_CTRS  ; ADD DEV CTRS TO TOTAL
prtinit2:
        cmp     [di].deviceindex,LPT3_INDEX             ; IF DEVICE != LPT3
        jne     prtinit3                                ; THEN CONTINUE

        ; SET PERFVIEW TO POINT TO DATA STRUCTURES

        mov     si,SEG data_block                       ; SEGMENT OF DATA_BLOCK
        mov     ax,OFFSET data_block                    ; OFFSET OF DATA_BLOCK
        mov     di,SEG text_block                       ; SEGMENT OF TEXT_BLOCK
        mov     bx,OFFSET text_block                    ; OFFSET OF TEXT_BLOCK
        mov     cx,WORD PTR data_block.dbh_flFlags      ; GET FLAGS
        mov     dl,DevHlp_RegisterPerfCtrs              ; REGISTER PERFVIEW FUNC
        call    DWORD PTR [device_help]                 ; CALL DEVICE HELP
                                                        ; CY = ERROR, AX = CODE
prtinit3:
        RestoreReg <bx,es>                              ; RESTORE REGISTERS

        ; SET ENDING ADDRESS OF CODE AND DATA SEGMENT IN DOS REQUEST BLOCK

        mov     ax,dataend                              ; END OF DATA SEGMENT
        dec     ax                                      ; CONVERT TO LIMIT
        mov     WORD PTR es:[bx].InitpEnd+2,ax          ; END OF DATA SEGMENT
        mov     WORD PTR es:[bx].InitpEnd,OFFSET CODEEND ; END OF CODE SEGMENT
        mov     es:[bx].PktStatus,REQDONE               ; SET THE DONE BIT
        jmp     SHORT prtinit5                          ; EXIT

prtinit4:
        pop     bx
        pop     es
        mov     WORD PTR es:[bx].InitpEnd+2,0           ; END OF DATA SEGMENT
        mov     WORD PTR es:[bx].InitpEnd,0             ; END OF CODE SEGMENT
        mov     es:[bx].PktStatus,GENFAILURE            ; SET THE ERROR CODE

prtinit5:
        ret
EndProc prtinit

BREAK <DETERMINE HARDWARE ROUTINE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  DETERMINE HARDWARE ROUTINE                       */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Determine parallel port hardware.               */
;/*                                                                    */
;/* FUNCTION:   This routine determines the parallel port hardware     */
;/*             for ISA-bus systems and updates the perprtdata area    */
;/*             accordingly.                                           */
;/*                                                                    */
;/* NOTES: This routine is called for each device initialized.         */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  prtdeterminehardware:NEAR                        */
;/*                   call prtdeterminehardware                        */
;/*                                                                    */
;/* INPUT:  DS = Data segment                                          */
;/*         DI = Offset to appropriate perprtdata area                 */
;/*                                                                    */
;/* EXIT-NORMAL: NC                                                    */
;/*              [di].deviceaddr = base port address                   */
;/*              numofprts incremented                                 */
;/*              [di].commonflags1 |= PORTEXISTS                       */
;/*              [di].introutine = hardware interrupt routine offset   */
;/*              [di].intlevel = hardware interrupt level              */
;/*                                                                    */
;/* EXIT-ERROR:  CY                                                    */
;/*                                                                    */
;/* EFFECTS: irq5index                                                 */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure prtdeterminehardware near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING

        test    bustype,EISABUS                         ; IF !EISA MACHINE
        jz      prtdethard1                             ; THEN CONTINUE SETUP
        cmp     [di].deviceaddr,0                       ; PORT EXIST?
        je      prtdethard5                             ; NO, EXIT
        or      [di].commonflags1,PORTEXISTS            ; SAVE PORT INSTALLED
        jmp     SHORT prtdethard5                       ; PREV SETUP, EISAINIT

        ; GET ADDRESSIBILITY TO BIOS ROM DATA AREA

prtdethard1:
        push    BIOSROMDATA                             ; SEG OF BIOS AREA
        pop     es                                      ; SEG OF BIOS AREA
        xor     bx,bx                                   ; OFF OF BIOS AREA

        sub     ah,ah                                   ; CLEAR AH
        mov     al,[di].deviceindex                     ; GET ZERO BASED INDEX
        mov     si,ax                                   ; PORT ADDRESS OFFSET
        sal     si,1                                    ; WORD ALIGN
        mov     ax,BIOSDATA.printer_adapt[si]           ; GET PORT ADDRESS
        mov     [di].deviceaddr,ax                      ; SAVE PORT ADDRESS

        or      ax,ax                                   ; IS PORT AVAILABLE?
        jz      prtdethard5                             ; NO, EXIT

        inc     numofprts                               ; # OF PRTS INSTALLED
        or      [di].commonflags1,PORTEXISTS            ; SAVE PORT INSTALLED

        cmp     ax,PORT278H                             ; IF DEV ADDR != 278H
        jne     prtdethard2                             ; THEN PROCEED

        ; SET UP DEVICE INFO FOR IRQ 5 (INT 0DH) PROCESSING

        mov     [di].introutine,OFFSET prtint05         ; OFFSET INT HANDLER
        mov     [di].intlevel,LEVEL5                    ; INTERRUPT LEVEL = 5

        xor     ah,ah                                   ; CLEAR AH
        mov     al,[di].deviceindex                     ; GET DEVICE INDEX
        mov     irq5index,ax                            ; INDEX OF IRQ5 DEV
        jmp     SHORT prtdethard5                       ; EXIT

        ; SET UP DEVICE INFO FOR IRQ 7 (INT 0FH) PROCESSING

prtdethard2:
        mov     [di].introutine,OFFSET prtint07         ; OFFSET INT HANDLER
        mov     [di].intlevel,LEVEL7                    ; INTERRUPT LEVEL = 7
        jmp     SHORT prtdethard5                       ; EXIT

        ; SET ERROR INDICATION
prtdethard4:
        stc                                             ; SET ERROR INDICATION
        jmp     SHORT prtdethard6                       ; EXIT

        ; SET SUCCESS INDICATION
prtdethard5:
        clc                                             ; SET SUCCESS INDICATION

        ; EXIT
prtdethard6:
        ret
EndProc prtdeterminehardware

BREAK <REGISTER RESOURCES ROUTINE>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME:  REGISTER RESOURCES WITH RESOURCE MANAGER ROUTINE */
;/*                                                                    */
;/* DESCRIPTIVE NAME:  Call resource manager with resources needed.    */
;/*                                                                    */
;/* FUNCTION:   This routine calls the resource manager with the       */
;/*             resources required by the parallel port device driver. */
;/*                                                                    */
;/* NOTES: We currently cannot determine the DMA channel from ABIOS    */
;/*        so we do not report it.  We must allocate the resources     */
;/*        before we register the adapter.                             */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  RM_PRT_RegisterResources:NEAR                    */
;/*                   call RM_PRT_RegisterResources                    */
;/*                                                                    */
;/* INPUT:  DS = Data segment                                          */
;/*         DI = Offset to appropriate perprtdata area                 */
;/*         [di].deviceaddr = base port address                        */
;/*         [di].intlevel = hardware interrupt level                   */
;/*         [di].deviceindex = adapter number (zero based)             */
;/*         bustype = bus architecture (ISA, EISA, MC)                 */
;/*                                                                    */
;/* EXIT-NORMAL: NC                                                    */
;/*                                                                    */
;/* EXIT-ERROR:  CY                                                    */
;/*                                                                    */
;/* EFFECTS:                                                           */
;/*                                                                    */
;/* INTERNAL REFERENCES:  _RM_PRT_AllocPorts                           */
;/*    ROUTINES:          _RM_PRT_AllocIRQ                             */
;/*                       _RM_PRT_CreateAdapter                        */
;/*                       _RM_PRT_DeallocPorts                         */
;/*                       _RM_PRT_DeallocIRQ                           */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure RM_PRT_RegisterResources near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING
        pusha
        push    es

        ; OBTAIN CONFLICT-FREE PORT RESOURCES FROM RESOURCE MANAGER

        push    di                                      ; SAVE PERPRTDATA AREA
        push    [di].deviceaddr                         ; BASE PORT
        call    _RM_PRT_AllocPorts                      ; CLAIM PORTS
        add     sp,2                                    ; CLEAN UP STACK
        pop     di                                      ; RESTORE PERPRTDATA
        or      ax,ax                                   ; IF ERROR
        jnz     RM_PRT_Reg3                             ; EXIT

        ; OBTAIN CONFLICT-FREE IRQ RESOURCES FROM RESOURCE MANAGER

        test    [di].commonflags,USEPOLLING             ; IF USE POLLING
        jnz     RM_PRT_Reg0                             ; DON'T ALLOC IRQ
        push    di                                      ; SAVE PERPRTDATA AREA
        sub     ah,ah                                   ; CLEAR AH
        mov     al,[di].intlevel                        ; GET INTERRUPT LEVEL
        push    ax                                      ; INTERRUPT LEVEL
        push    bustype                                 ; BUS TYPE
        call    _RM_PRT_AllocIRQ                        ; CLAIM IRQ
        add     sp,4                                    ; CLEAN UP STACK
        pop     di                                      ; RESTORE PERPRTDATA
        or      ax,ax                                   ; IF ERROR
        jnz     RM_PRT_Reg2                             ; EXIT

        ; REGISTER ADAPTER AND RESOURCES WITH RESOURCE MANAGER

RM_PRT_Reg0:
        push    di                                      ; SAVE PERPRTDATA AREA
        sub     ah,ah                                   ; CLEAR AH
        mov     al,[di].deviceindex                     ; GET ADAPTER NUMBER
        push    ax                                      ; ADAPTER NUMBER
        push    bustype                                 ; BUS TYPE
        call    _RM_PRT_CreateAdapter                   ; REGISTER ADAPTER
        add     sp,4                                    ; CLEAN UP STACK
        pop     di                                      ; RESTORE PERPRTDATA
        or      ax,ax                                   ; IF NO ERROR
        jz      RM_PRT_Reg4                             ; EXIT

        ; IRQ ALLOCATED, DEALLOCATE IRQ
RM_PRT_Reg1:
        test    [di].commonflags,USEPOLLING             ; IF USE POLLING
        jnz     RM_PRT_Reg2                             ; DON'T DEALLOC IRQ
        call    _RM_PRT_DeallocIRQ                      ; DEALLOC IRQ RESOURCE

        ; PORTS ALLOCATED, DEALLOCATE PORTS
RM_PRT_Reg2:
        call    _RM_PRT_DeallocPorts                    ; DEALLOC PORT RESOURCE

        ; SET ERROR INDICATION
RM_PRT_Reg3:
        stc                                             ; SET ERROR INDICATION
        jmp     SHORT RM_PRT_Reg5                       ; EXIT

        ; SET SUCCESS INDICATION
RM_PRT_Reg4:
        clc                                             ; SET SUCCESS INDICATION

        ; EXIT
RM_PRT_Reg5:
        pop     es
        popa                                            ; RESTORE REGISTERS
        ret
EndProc RM_PRT_RegisterResources

BREAK <PARSE COMMAND LINE ARGS>
;/********************** START OF SPECIFICATIONS ***********************/
;/*                                                                    */
;/* SUBROUTINE NAME: ParseCmdLine                                      */
;/*                                                                    */
;/* DESCRIPTIVE NAME: PARSE COMMAND LINE ARGS                          */
;/*                                                                    */
;/* FUNCTION: THE PURPOSE OF THIS SUBROUTINE IS TO EXAMINE COMMAND     */
;/*           LINE ARGS AND PERFORM APPROPRIATE ACTIONS.               */
;/*                                                                    */
;/* NOTES:  FOR COMPATIBILITY WITH PREVIOUS VERSIONS OF PRINT0?.SYS    */
;/*         THE COMMAND LINE ARGS BEGIN IN THE ReqPacket:InitParms     */
;/*         BUFFER OFFSET BY THE 3 WORDS THAT CONTAIN THE BUFSIZE      */
;/*         VALUES (See prtinit.asm & prtinit.inc)                     */
;/*                                                                    */
;/*         CURRENTLY, THE CHECK FOR '/IRQ' IS VERY HARD CODED IN      */
;/*         THIS ROUTINE SINCE WITH THE ONE ARG WE DONT WANT TO        */
;/*         BLOAT PRINT0?.SYS WITH SOPHISTICATED PARSING CODE.         */
;/*                                                                    */
;/* COMMAND LINE ARGS SUPPORTED:                                       */
;/*                                                                    */
;/* None - directs the driver to use POLLING for printing rather than  */
;/*        IRQs.                                                       */
;/*                                                                    */
;/* /IRQ - (Case insensitive) directs the driver to use IRQs for       */
;/*        printing rather than polling.                               */
;/*                                                                    */
;/* ENTRY POINT: PARSECMDLINE                                          */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT: DI = OFFSET TO APPROPRIATE PERPRTDATA AREA                  */
;/*        ES:BX = PTR TO KERNEL REQUEST PACKET                        */
;/*                                                                    */
;/* REGSITERS USED:  All registers preserved                           */
;/*                                                                    */
;/* EXIT-NORMAL:  [di].commonflags USEPOLLING BIT SET APPROPRIATELY    */
;/*                                                                    */
;/* EXIT-ERROR :  NO ERROR STATUS RETURNED                             */
;/*                                                                    */
;/* INTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:  NONE                                         */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
Procedure ParseCmdLine near
        ASSUME  CS:CSEG,DS:DSEG,ES:NOTHING,SS:NOTHING

        pusha                                           ; SAVE REGISTERS
        push    es                                      ; SAVE ES

        or      [di].commonflags, USEPOLLING            ; ASSUME POLLING

        mov     si, di                                  ; SAVE DI in SI

        cld                                             ; CLEAR DIRECTION FLAG
        les     bx, es:[bx].InitParms                   ; GET CONFIG.SYS PARMS
        lea     di, es:[bx].CmdLineParms                ; OFFSET PAST BUF SIZES
                                                        ; (SEE PRT_Parm_List
                                                        ; STRUC IN PRTINIT.INC)

        ;/***********************************************/
        ;/*                                             */
        ;/* ES:DI NOW POINTING AT COMMAND LINE ARGS,    */
        ;/*                                             */
        ;/* DO A VERY BRUTE FORCE SEARCH FOR "/IRQ"     */
        ;/* (CASE INSENSITIVE). FIRST, FIND THE "/".    */
        ;/*                                             */
        ;/***********************************************/

        mov     al, '/'                                 ; FIND FORWARD SLASH
        mov     cx, CMDLINE_ARGSIZE                     ; PRTINIT.INC EQU
        repne   scasb                                   ; LOOK FOR IT
        jnz     ParseCmdLine_Exit                       ; JUMP IF DIDNT FIND

        ;/***********************************************/
        ;/* FOUND THE '/', NOW ES:DI IS POINTING AT     */
        ;/* FIRST CHAR PAST THE '/'. LETS CHECK IF THE  */
        ;/* NEXT 3 CHARS ARE "IRQ" (CASE-INSENSITIVE)   */
        ;/***********************************************/

        ; CHECK FOR I or i

        cmp     byte ptr es:[di], 'I'                   ; IS IT 'I'?
        je      ParseCmdLine_CheckFor_R                 ; YES, CHECK NEXT CHAR

        cmp     byte ptr es:[di], 'i'                   ; IS IT 'i'?
        je      ParseCmdLine_CheckFor_R                 ; YES, CHECK NEXT CHAR

        jmp     short ParseCmdLine_Exit                 ; NO MATCH

ParseCmdLine_CheckFor_R:

        ; CHECK FOR R or r

        inc     di                                      ; POINT TO NEXT CHAR
        cmp     byte ptr es:[di], 'R'                   ; IS IT 'R'?
        je      ParseCmdLine_CheckFor_Q                 ; YES, CHECK NEXT CHAR

        cmp     byte ptr es:[di], 'r'                   ; IS IT 'r'?
        je      ParseCmdLine_CheckFor_Q                 ; YES, CHECK NEXT CHAR

        jmp     short ParseCmdLine_Exit                 ; NO MATCH

ParseCmdLine_CheckFor_Q:

        ; CHECK FOR Q or q

        inc     di                                      ; POINT TO NEXT CHAR
        cmp     byte ptr es:[di], 'Q'                   ; IS IT 'Q'?
        je      ParseCmdLine_Match                      ; YES, MATCH

        cmp     byte ptr es:[di], 'q'                   ; IS IT 'q'?
        je      ParseCmdLine_Match                      ; YES, MATCH

        jmp     short ParseCmdLine_Exit                 ; NO MATCH

ParseCmdLine_Match:

        ; FOUND "/IRQ" ON COMMAND LINE, DONT USE POLLING

        mov     di, si                                  ; RESTORE DI
        and     [di].commonflags, NOT USEPOLLING        ; RESET POLLING FLAG

ParseCmdLine_Exit:

        pop     es                                      ; RESTORE ES
        popa                                            ; RESTORE REGISTERS

        ret
EndProc ParseCmdLine

CSEG    ENDS
        END
