
; SCCSID = @(#)prteisai.asm     6.3 90/12/10
;/**********************************************************************
;/*                                                                    *
;/*                       IBM/Microsoft Confidential                   *
;/*                                                                    *
;/*                 Copyright (c) IBM Corporation  1987, 1990          *
;/*                 Copyright (c) Microsoft Corp.  1987, 1990          *
;/*                           All Rights Reserved                      *
;/*                                                                    *
;/**********************************************************************
 TITLE PRINTDD - PRINTER DEVICE DRIVER EISA INIT. TIME ROUTINE
 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:
;       EISAINIT
;***********************************************************************

        .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 infoseg.inc             ; STRUCTURES DEFINING THE INFOSEG.
        INCLUDE filemode.inc            ; FILE SYSTEM FILE MODE EQUATES.
        INCLUDE struc.inc               ; STRUCTURED MACROS
        .LIST
        .CREF
        INCLUDE prtdd.inc               ; PRINTER DEVICE DRIVER INCLUDE FILE
        INCLUDE prtdd1.inc              ; PRINTER DEVICE DRIVER INCLUDE FILE
        INCLUDE prteisa.inc             ; EQUATES FOR EISA CODE
        INCLUDE eisa.inc

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   device_help:DWORD
        EXTRN   irq5index:WORD
        EXTRN   irq7owner:WORD
        EXTRN   numofprts:BYTE
        EXTRN   perprtarea:BYTE

o_name          db      "OEMHLP$ "      ; OEMHLP$ name
par_str         db      "PAR"           ; device id
LEN_PAR_STR     equ     $-par_str       ; string length
lpt_str         db      "LPT"           ; device subtype
LEN_LPT_STR     equ     $-lpt_str       ; string length
semi_str        db      ";"             ; subtype delimiter
LEN_SEMI_STR    equ     $-semi_str      ; string length
OEMinfo         AttachDDstr     <>      ; attachDD return info
OEMpkt          Packet  <>              ; IOCTL packet for OEMHLP$
Slot            EisaSlotInfo <>         ; Slot info.
Function        EisaFuncInfo <>         ; Function info.
EisaCall        EisaCallParameters <>   ; EISA call info.
LPTsFound       db      SIZE EISALpt * MAXPRINTERS DUP (0)   ; array for info. found
lpt_index       db      0               ; offset in to LPTsFound array
bios_found      dw      3 DUP (0)       ; match found in EISA config. info
Temp_LPTFound   EISALpt <>              ; temp variable for sorting

Eisa_int_routines       LABEL   WORD
                dw      OFFSET  eInt_1  ; interrupt routine for LPT1
                dw      OFFSET  eInt_2  ; interrupt routine for LPT2
                dw      OFFSET  eInt_3  ; interrupt routine for LPT3

DSEG    ENDS

        EXTRNFAR initialprt

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

        EXTRN   prtint07:NEAR
        EXTRN   prtint05:NEAR
        EXTRN   eInt_1:NEAR
        EXTRN   eInt_2:NEAR
        EXTRN   eInt_3:NEAR

BREAK <EISA INITIALIZATION ROUTINE>
;/*********************** END OF SPECIFICATIONS ************************/
;/*                                                                    */
;/* SUBROUTINE NAME: EISAINIT                                          */
;/*                                                                    */
;/* DESCRIPTIVE NAME: PRINT DEVICE DRIVER EISA INIT. ROUTINE.          */
;/*                                                                    */
;/* FUNCTION: This subroutine will get called from PRTINIT to check    */
;/*           for the presence of an EISA system.  If                  */
;/*           running on an EISA system, then this routine will        */
;/*           perform the intitialization of the printer device        */
;/*           driver's data structures.  This routine will use the     */
;/*           EISA system's non-volatile configuration CMOS to         */
;/*           determine the port address and IRQ line for each printer */
;/*           attached to the system.  If not running on an EISA       */
;/*           system, this routine will set CY, indicating to PRTINIT  */
;/*           that it should initialize the data structures, with      */
;/*           the default ISA values.                                  */
;/*                                                                    */
;/* ENTRY POINT: EISAINIT                                              */
;/*    LINKAGE : CALL NEAR                                             */
;/*                                                                    */
;/* INPUT:     ES = SELECTOR TO BIOS DATA AREA                         */
;/*            BX = OFFSET TO BIOS DATA AREA                           */
;/*            NUMOFPRTS = NUMBER OF PRINTERS ATTACHED                 */
;/*                                                                    */
;/* REGISTERS USED: NONE                                               */
;/*                                                                    */
;/* EXIT-NORMAL:   CY = 0, EISA System                                 */
;/*                                                                    */
;/* EXIT-ERROR:    CY = 1, Non-EISA system                             */
;/*                                                                    */
;/* INTERNAL REFERENCES: NONE                                          */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:                                               */
;/*                                                                    */
;/*********************** END OF SPECIFICATIONS ************************/
        PUBLIC eisainit
Procedure eisainit

        pusha                           ; save all regs.
        push    es                      ; and ES, too
        call    AttachOEMHLP            ; get access to OEMHLP$ driver,
        .if     < c >                   ; error?
            jmp     exit_eisai          ;  Y: exit
        .endif
        mov     cl, 0                   ; first slot #

        push    ds                      ; set ES to our DGROUP
        pop     es

slot_loop:
        call    GetSlotInfo             ; get info. for first slot
        .if     < ah ne 0 >             ; error?
            .if     < ah e EISA_ERROR_EMPTY_SLOT > ; empty slot?
                inc     cl              ;  Y: go to next slot number,
                jmp     slot_loop       ;     and process next slot
            .elseif < ah e EISA_INVALID_SLOT >  ; invalid slot?
                jmp     reconcile       ;  Y: reconcile info. found
            .else                       ; not invalid or empty, so error exit
                stc                     ; indicate error
                jmp     exit_eisai      ; then exit
            .endif
        .else                           ; Valid slot, check for I/O ports
            .if < bit Slot.esi_fbFunc and EISA_HAS_IO_ENTRIES > I/O ports on this slot?
                mov     ch, Slot.esi_cbFunc    ; Y: get number of functions
                dec     ch              ; make it relative to 0

function_loop:
                call    GetFuncInfo     ; get 320 byte record for this func.
                .if     < ah ne 0 >     ; Error?
                    stc                 ;  Y: indicate error, then
                    jmp     exit_eisai  ;  error exit
                .endif

                lea     si, Function.efi_achType   ; point to device string
                lea     di,  par_str    ; device string to look for
                mov     ax, LEN_PAR_STR ; string length
                call    strncmp         ; is this function a PAR device?
                .if     < z > and       ;  Y: also check to see if it's enabled
                .if     < bit Function.efi_fbFunction z EISA_FUNC_ENABLED > ; if enabled
                    call    RecordFunc  ;  Y: store the information
                .endif
                .if     < lpt_index e MAXPRINTERS >  ; Just found third printer?
                    jmp     reconcile   ;  Y: no need to search further
                .endif
                dec     ch              ; next function to check
                .if     < ch ge 0 >     ; More functions to check?
                    jmp     function_loop       ;  Y: check next function
                .endif
            .endif                      ; I/O ports
        .endif                          ; No error on slot call
        inc     cl                      ; next slot to check
        jmp     slot_loop               ; check next slot

reconcile:
        .if     < lpt_index g 0 >       ; if any LPT info. found, then
            call    CompareBIOS         ;  compare results to BIOS data area
            call    AddEISA             ;  then add in any EISA only printers
            clc                         ; indicate no error
        .else                           ; otherwise,
            stc                         ;  treat it like an ISA machine
        .endif

exit_eisai:
        pop     es                      ; restore regs.
        popa
        ret
EndProc eisainit

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  AddEISA                                                 *
;                                                                           *
; DESCRIPTIVE NAME: Add EISA printers to device driver data structure.      *
;                                                                           *
; FUNCTION:     This routine fills in the device driver's data structure    *
;               with any information that was found on non-ISA compatible   *
;               printer ports.  These are printer ports that do not begin   *
;               at the ISA compatible port addresses.  If all three         *
;               ISA compatible printers were found by the BIOS at POST      *
;               time, then this routine will do nothing.  Also, if          *
;               all the printers found in EISA config. were already         *
;               matched up with ISA compatible printers found by the BIOS,  *
;               then this routine will do nothing.  If neither of the two   *
;               conditions above were true, then this routine will "left    *
;               justify" the EISA printers, and fill them in to the         *
;               driver's data structure.                                    *
;                                                                           *
; ENTRY POINT:  AddEISA                                                     *
;     LINKAGE:  Call near (local and discarded after init)                  *
;                                                                           *
; EXIT-NORMAL:  None.                                                       *
;                                                                           *
; EXIT-ERROR:   None.                                                       *
;                                                                           *
; EFFECTS:      All registers destroyed.                                    *
;                                                                           *
;****************************************************************************

public  AddEISA
AddEISA         proc    near

        .if     < numofprts e MAXPRINTERS >     ; 3 ISA compatible printers,
            ret                         ; so just exit
        .endif

        .if     < lpt_index e 0 >       ; already matched up all the
            ret                         ;  EISA printers, so just exit
        .endif

        .if     < lpt_index g 1 >       ; if more than one EISA printer
                                        ;  remaining
            ; bubble sort the EISA printers

bubble_sort:
            xor     dx, dx              ; assume sort is finished
            mov     cx, MAXPRINTERS     ; number of array items
            dec     cx                  ; adjust to loop count
            mov     si, OFFSET LPTsFound ; start of EISA array

scan_array:
            mov     al, [si].printer_number     ; unit number
            mov     di, si
            add     di, LEN_EISALPT_STRUC       ; point to next entry
            .if     < al g [di].printer_number > ; if these aren't in order
                mov     dx, 1           ; not through sorting yet
                push    si              ; save current offset
                push    cx              ; save loop count
                push    di              ; save current+1 offset
                push    si              ; save current offset
                mov     di, OFFSET Temp_LPTFound ; temp buffer
                mov     cx, LEN_EISALPT_STRUC / 2  ; prepare for word move
                rep     movsw           ; copy current array entry to temp.
                pop     di              ; get current offset back
                pop     si              ; get current+1 offset back
                push    si              ; save current+1 offset
                mov     cx, LEN_EISALPT_STRUC / 2  ; prepare for word move
                rep     movsw           ; copy current+1 to current
                pop     di              ; get current+1 offset back
                mov     si, OFFSET Temp_LPTFound ; temp buffer
                mov     cx, LEN_EISALPT_STRUC / 2  ; prepare for word move
                rep     movsw           ; copy from tmp to current+1
                pop     cx              ; get loop count back
                pop     si              ; get current offset back
            .endif

            add     si, LEN_EISALPT_STRUC  ; point to next array element
            loop    scan_array          ; check next two items
            .if     < dx e 1 >          ; is the array sorted yet?
                jmp     bubble_sort     ;  N: try again
            .endif
        .endif

        ; Now scan the sorted array, looking for un-used EISA printers.
        ; The variable "numofprts" tells us how many printers the BIOS
        ; found, which by this time is also the number of printers
        ; we've initialized the driver for, since we give the ISA
        ; printers top priority for initializing the driver's data
        ; structures.  Therefore, the number of EISA printers we
        ; can add now is: MAXPRINTERS - numofprts

        mov     al, numofprts           ; number of printers BIOS found
        xor     ah, ah
        mov     bx, SIZE printer_database
        mul     bx                      ; construct an offset in to the
                                        ;  driver's data structure
        mov     di, OFFSET perprtarea   ; beginning of driver's data structure
        add     di, ax                  ; first empty entry in driver's data
        mov     cx, MAXPRINTERS         ; max. number of EISA printers
        mov     si, OFFSET LPTsFound    ; first entry in EISA array
        mov     dl, numofprts           ; to keep track of total
        mov     bl, dl                  ; use as an index into int. routines
        xor     bh, bh
        shl     bx, 1                   ; X 2 to create an offset

scan_eisa:
        .if     < [si].printer_number g 0 > and  ; if valid data
        .if     < [si].In_use e 0 >     ; and this printer is not in use

            ; fill in the device driver's data structure with the
            ; EISA config. information
            mov     ax, [si].base_addr  ; get base addr from EISA
            mov     [di].deviceaddr, ax ; have the driver use it
            mov     al, [si].IRQ_number ; get IRQ level from EISA
            mov     [di].intlevel, al   ; have the driver use it
            mov     al, [si].sharable_IRQ  ; get IRQ sharing info. from EISA
            mov     [di].share_interrupt, al  ; have the driver use it
            mov     ax, Eisa_Int_routines[bx]  ; get offset of int hdl.
            mov     [di].introutine, ax ; have the driver use it
            inc     numofprts           ; increase global counter

            push    ax                  ; save interrupt handler addr
            or      [di].commonflags1, BOOTINIT ; indicate boot time
            CALLFAR initialprt          ; initialize the printer
            and     [di].commonflags1, NOT BOOTINIT ; clear boot flag
            pop     ax                  ; get interrupt handler addr

            ; if we're going to share this interrupt line, grab it
            ; now and then we won't have to keep hooking it and un-hooking
            ; for each file printed.
            .if     < [di].share_interrupt e INT_SHARING >
                push    bx              ; save offset to int. routine
                push    dx              ; save printer count
                xor     bh, bh          ; clear high byte
                mov     bl, [di].intlevel  ; IRQ number
                mov     dh, INT_SHARING ; request the IRQ shared
                mov     dl, DevHlp_SetIRQ ; request code
                call    DWORD PTR [device_help] ; request the IRQ line
                .if     < c >           ; if CY set, the request failed
                    mov     [di].share_interrupt, 0  ; no sharing
                .endif
                pop     dx              ; get printer count back
                pop     bx              ; get offset to int. routines
            .endif

            add     di, SIZE printer_database   ; point to next free entry
            inc     bx
            inc     bx                  ; point to next int. routine
            inc     dl                  ; add another printer
            .if     < dl e MAXPRINTERS > ; max reached?
                jmp     exit_add        ;  Y: exit
            .endif
        .endif

        add     si, LEN_EISALPT_STRUC   ; point to next EISA printer
        loop    scan_eisa               ; process all EISA printers

exit_add:
        ret

AddEISA         endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  atoi                                                    *
;                                                                           *
; FUNCTION: Converts the string at DS:SI in to an integer value.  Does      *
;           not assume NULL termination - searches for the first            *
;           non-ASCII digit instead.                                        *
;                                                                           *
; ENTRY POINT:  atoi                                                        *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               DS:SI - String to convert                                   *
;                                                                           *
; EXIT-NORMAL:  AX = integer value.                                         *
;               CY clear                                                    *
;                                                                           *
; EXIT-ERROR:   CY set indicates invalid input                              *
;                                                                           *
; EFFECTS:      Flags, all other registers restored.                        *
;                                                                           *
;****************************************************************************

public  atoi
atoi proc near

        push    si                      ; save regs.
        push    bx
        push    cx
        push    dx

        xor     ah, ah                  ; clear high order byte
        mov     bx, 1                   ; initial power of ten multiplier
        xor     cx, cx                  ; initial string size
        xor     dx, dx                  ; eventual result

        cld                             ; go forward to find the end

find_end_digits:
        lodsb                           ; get a char
        .if     < al ge '0' >  and      ; if in range
        .if     < al le '9' >           ;  then
            inc     cx                  ;  increment string size
            jmp     find_end_digits     ;  and keep looking
        .endif

        .if     < cx e 0 >              ; no digits found?
            stc                         ;  indicate error
            jmp     exit_atoi           ;  then exit
        .endif

        dec     si                      ; back up to non-digit char.
        dec     si                      ; back up to least significant digit
        std                             ; go backwards from least to most sig.

convert:
        lodsb                           ; get a digit
        sub     al, '0'                 ; strip off the ASCII portion
        push    dx                      ; save the work in progress
        mul     bx                      ; calculate this position
        pop     dx                      ; retrieve work done so far
        add     dx, ax                  ; accumulate this new position
        .if     < bx e 1 >              ; special case for first time
            mov     bx, 10              ; initial powers of ten value
        .else
            mov     ax, 10              ; to create next power of ten
            push    dx                  ; save work in progress
            mul     bx                  ; create next power of ten
            pop     dx                  ; restore work in progress
            mov     bx, ax              ; put back in powers of 10 reg.
        .endif
        loop    convert                 ; loop until all are converted

        mov     ax, dx                  ; move result to return value
        clc                             ; indicate no error

exit_atoi:
        pop     dx                      ; restore regs
        pop     cx
        pop     bx
        pop     si

        ret

atoi    endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  AttachOEMHLP                                            *
;                                                                           *
; DESCRIPTIVE NAME: Perform an attachDD call so that we may do IOCTLs to    *
;                   OEMHLP$ to get EISA config. information.                *
;                                                                           *
; FUNCTION:     Set up AttachDD data structure so that calls may be made to *
;               OEMHLP$ during printer driver init.                         *
;                                                                           *
; ENTRY POINT:  AttachOEMHLP                                                *
;     LINKAGE:  Call near (local and discarded after init)                  *
;                                                                           *
;                                                                           *
; EXIT-NORMAL:  CY = 0                                                      *
;                                                                           *
; EXIT-ERROR:   CY = 1                                                      *
;                                                                           *
; EFFECTS:      DX destroyed.                                               *
;                                                                           *
;****************************************************************************

public AttachOEMHLP
AttachOEMHLP    proc    near

        push    bx
        push    di
        mov     bx, OFFSET o_name       ; get the OEMHLP$ name
        mov     di ,OFFSET OEMinfo      ; point to the data area for info
        mov     dl, DevHlp_AttachDD     ; request code
        call    DWORD PTR [device_help] ; perform the devhlp to attach
        pop     di
        pop     bx
        ret

AttachOEMHLP    endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  CompareBIOS                                             *
;                                                                           *
; DESCRIPTIVE NAME: Compare EISA config. information to the BIOS data area. *
;                                                                           *
; FUNCTION:     This function reconciles the data found in the EISA         *
;               config. information with the BIOS data area's printer       *
;               base address array.  The purpose of this routine is to      *
;               ensure ISA/DOS compatibility.  By looking at the BIOS       *
;               data area first, we can handle the possiblity of an ISA     *
;               card being plugged in to an EISA machine without a          *
;               corresponding EISA config. file.                            *
;                                                                           *
;               To accomplish this, two things are done.  First, the EISA   *
;               config. information is compared to the BIOS data area.      *
;               For each match between the two, an entry is initialized     *
;               in the driver's database.  After this is done, the          *
;               resulting database for the driver is checked against the    *
;               BIOS data area.  If there are any BIOS data area entries    *
;               that do not have a corresponding entry in the driver's      *
;               database, then the driver's database is filled in with the  *
;               ISA defaults.                                               *
;                                                                           *
; ENTRY POINT:  CompareBIOS                                                 *
;     LINKAGE:  Call near (local and discarded after init)                  *
;                                                                           *
; EXIT-NORMAL:  None.                                                       *
;                                                                           *
; EXIT-ERROR:   None.                                                       *
;                                                                           *
; EFFECTS:      All registers destroyed.                                    *
;                                                                           *
;****************************************************************************

public  CompareBIOS
CompareBIOS     proc    near

        push    es                      ; save our data selector
        mov     ax, BIOSROMDATA         ; get selector for BIOS data area
        mov     es, ax
        xor     bx, bx                  ; create offset to BIOS data area
        xor     si, si                  ; index in to BIOS data area

        mov     cl, lpt_index           ; number of printers in EISA info.
        xor     ch, ch                  ; clear high byte
        mov     di, OFFSET LPTsFound    ; beginning of EISA array

check_all_EISA_printers:
        push    cx                      ; save # of EISA printers loop count
        mov     cl, numofprts           ; number of printers the BIOS found
                                        ;  during POST
        jncxz   compbios_0              ; if ports, continue.
        ; got here when EISA info exists and no BIOS info.  User needs to
        ; run EISA configuration to remove EISA info.  Nothing to initialize.
        pop     cx                      ; restore # of EISA printers loop count
        jmp     compbios_1              ; if no ports, exit.
compbios_0:
        xor     dx, dx                  ; array index
        push    si                      ; save beginning index

check_all_BIOS_entries:
        mov     ax, [di].base_addr      ; EISA base addr.
        .if     < biosdata.printer_adapt[si] ge ax > near ; could be in range
            mov     ax, [di].end_addr   ; get the end of the range
            .if     < biosdata.printer_adapt[si] le ax > near and ; if in range
            .if     < [di].In_use e 0 > near ; and this EISA info. hasn't been used
                mov     [di].In_use, 1  ; a match has been found, mark this
                                        ;  entry as being in use.
                push    bx              ; save offset to BIOS data area
                push    dx              ; save array index
                mov     ax, dx          ; get array index
                mov     bx, SIZE printer_database
                mul     bx              ; create an offset
                mov     bx, ax          ; set up an index

                ; fill in the device driver's data structure with the
                ; EISA config. information
                mov     ax, [di].base_addr      ; get base addr from EISA
                mov     perprtarea[bx].deviceaddr, ax ; have the driver use it
                mov     al, [di].IRQ_number     ; get IRQ level from EISA
                mov     perprtarea[bx].intlevel, al  ; have the driver use it
                mov     al, [di].sharable_IRQ   ; get IRQ sharing info. from EISA
                mov     perprtarea[bx].share_interrupt, al ; have the driver use it
                mov     ax, Eisa_Int_routines[si]    ; get offset of int hdl.
                mov     perprtarea[bx].introutine, ax ; have the driver use it

                or      perprtarea[bx].commonflags1, BOOTINIT ; indicate boot time
                push    ax              ; save interrupt handler addr
                push    di              ; save EISA array pointer
                mov     di, OFFSET perprtarea
                add     di, bx          ; point to current driver structure

                CALLFAR initialprt      ; initialize the printer
                pop     di              ; get EISA array pointer back
                and     perprtarea[bx].commonflags1, NOT BOOTINIT ; clear boot flag
                pop     ax              ; get interrupt handler addr

                ; if we're going to share this interrupt line, grab it
                ; now and then we won't have to keep hooking it and un-hooking
                ; it for each file printed.

                .if     < perprtarea[bx].share_interrupt e INT_SHARING >
                    push    bx                  ; save offset to perprtarea
                    mov     bl, perprtarea[bx].intlevel ; IRQ number
                    xor     bh, bh                      ; clear high byte
                    mov     dh, INT_SHARING             ; request the IRQ shared
                    mov     dl, DevHlp_SetIRQ           ; request code
                    call    DWORD PTR [device_help]     ; request the IRQ line
                    pop     bx                          ; offset to perprtarea
                    .if     < c >                       ; if CY set, req failed
                        mov     perprtarea[bx].share_interrupt, 0  ; no sharing
                    .endif
                .endif

                pop     dx              ; restore array index

                ; now that the interrupt sharing issue has been sorted out,
                ; check for interrupt sharing again.  If it is not going to
                ; be done for this printer, then use the standard ISA
                ; entry points in to the interrupt service routine, if
                ; the printer is configured for IRQ7 or IRQ5.
                .if     < perprtarea[bx].share_interrupt ne INT_SHARING >
                    .if     < perprtarea[bx].intlevel e LEVEL5 >
                        mov     perprtarea[bx].introutine, OFFSET prtint05
                        mov     irq5index, dx   ; save index into driver str.
                    .elseif < perprtarea[bx].intlevel e LEVEL7 >
                        mov     perprtarea[bx].introutine, OFFSET prtint07
                    .endif
                .endif

                mov     bios_found[si], 1  ; indicate valid info.
                dec     lpt_index       ; keep track of how many EISA LPTs used
                pop     bx              ; restore offset to BIOS data area
            .endif
        .endif

        inc     si
        inc     si                      ; point to the next BIOS data entry
        inc     dx                      ; next array index
        dec     cx                      ; too much code for loop inst.
        jz      next_eisa               ; process next EISA printer
        jmp     check_all_BIOS_entries  ; process each valid BIOS entry

next_eisa:
        pop     si                      ; restore initial offset to BIOS
        pop     cx                      ; restore EISA loop count
        add     di, LEN_EISALPT_STRUC   ; point to next array entry
        dec     cx                      ; too much code for loop inst.
        jz      use_ISA_values          ; revert to ISA defaults
        jmp     check_all_EISA_printers ; process all EISA printers found

use_ISA_values:
        mov     cx, MAXPRINTERS         ; loop counter
        xor     si, si                  ; offset into BIOS data area
        xor     dx, dx                  ; array index
        mov     di, OFFSET perprtarea   ; device driver's array

fill_in_ISA_values:
        .if     < biosdata.printer_adapt[si] ne 0 > and ; if there is a printer,
        .if     < bios_found[si] e 0 >            ; and no EISA info. was found

            ; Fill in the device driver's data structure with ISA defaults,
            ; since no EISA config. information was found.

            mov     [di].share_interrupt, 0             ; no IRQ sharing
            mov     ax, biosdata.printer_adapt[si]      ; port addr.
            mov     [di].deviceaddr, ax                 ; have device driver use it
            .if     < ax e PORT278H >                   ; LPT3 base address?
                mov     [di].introutine, OFFSET prtint05 ; use IRQ5
                mov     [di].intlevel, LEVEL5
                mov     irq5index, dx
            .else                                       ; use IRQ7
                mov     [di].introutine, OFFSET prtint07
                mov     [di].intlevel, LEVEL7
            .endif
            or      [di].commonflags1, BOOTINIT         ; indicate boot time
            CALLFAR initialprt                          ; initialize the printer
            and     [di].commonflags1, NOT BOOTINIT     ; clear boot flag
        .endif
        inc     dx                                      ; array index
        inc     si
        inc     si                              ; next BIOS data area entry
        add     di, SIZE printer_database       ; next detice driver entry
        loop    fill_in_ISA_values              ; process all BIOS data area LPTs

compbios_1:
        pop     es                      ; get our data selector back
        ret
CompareBIOS     endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  GetFuncInfo                                             *
;                                                                           *
; DESCRIPTIVE NAME: Call OEMHLP to get EISA function information.           *
;                                                                           *
; FUNCTION:     Set up an IOCTL structure to call OEMHLP for EISA           *
;               function information.                                       *
;                                                                           *
; ENTRY POINT:  GetFuncInfo                                                 *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               CL = Slot number requested                                  *
;               CH = Function number requested                              *
;                                                                           *
; EXIT-NORMAL:  AH = 0                                                      *
;               Variable 'Function' contains result                         *
;                                                                           *
; EXIT-ERROR:   AH = Error code.                                            *
;                                                                           *
; EFFECTS:      AX destroyed.                                               *
;                                                                           *
;****************************************************************************

public  GetFuncInfo
GetFuncInfo     proc    near

        push    bx                      ; Save the work registers
        push    bp
        push    es

        mov     EisaCall.ecp_SubFunction, EISA_FUNCTION_REQUEST   ; tell OEMHLP$ "get function info."
        mov     EisaCall.ecp_SlotNumber, cl  ; requested slot number
        mov     EisaCall.ecp_FunctionNumber, ch ; requested function number

        mov     bx,OFFSET OEMpkt        ; point es:bx to IOCTL packet
        mov     es:[bx].PktLen,LenGenIOCTL ; get length
        mov     es:[bx].PktCmd,CMDGenIOCTL ; get ioctl cmd
        mov     es:[bx].GIOCategory,0    ; get category for base drivers
        mov     es:[bx].GIOFunction,EISA_FNNUMBER ; get function for EISA info.
        mov     word ptr es:[bx].GIODataPack+2,ds       ; get data addr
        mov     word ptr es:[bx].GIODataPack,OFFSET Function ; get data addr
        mov     word ptr es:[bx].GIOParaPack+2,ds       ; get parm addr
        mov     word ptr es:[bx].GIOParaPack,OFFSET EisaCall

        push    word ptr [OEMinfo].pcs  ; setup pointer to function
        push    word ptr [OEMinfo].poffset
        mov     bp, sp
        call    dword ptr [bp]          ; call external OEMHLP IOCTL routine
        mov     di, ax                  ; save success state

        pop     ax                      ; remove call address from stack
        pop     ax

        and     es:[bx].PktStatus, NOT STDON ; remove init. time flag
        .if     < es:[bx].PktStatus ne 0 > ; OEMHLP error?
            mov ah, EISA_INVALID_BIOS_CALL ;  Y: Indicate not EISA machine
        .else
            mov ah, Function.efi_bReturn    ;  N: Use the EISA return code
        .endif

        pop     es                      ; restore work regs.
        pop     bp
        pop     bx

        ret

GetFuncInfo     endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  GetSlotInfo                                             *
;                                                                           *
; DESCRIPTIVE NAME: Call OEMHLP to get EISA slot information.               *
;                                                                           *
; FUNCTION:     Set up an IOCTL structure to call OEMHLP for EISA slot      *
;               information.                                                *
;                                                                           *
; ENTRY POINT:  GetSlotInfo                                                 *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               CL = Slot number requested                                  *
;                                                                           *
; EXIT-NORMAL:  AH = 0                                                      *
;               Variable 'Slot' contains result                             *
;                                                                           *
; EXIT-ERROR:   AH = Error code.                                            *
;                                                                           *
; EFFECTS:      AX destroyed.                                               *
;                                                                           *
;****************************************************************************

public GetSlotInfo
GetSlotInfo     proc    near

        push    bx                      ; Save the work registers
        push    bp
        push    es

        mov     EisaCall.ecp_SubFunction, EISA_SLOT_REQUEST  ; tell OEMHLP$ "get slot info."
        mov     EisaCall.ecp_SlotNumber, cl  ; requested slot number

        mov     bx,OFFSET OEMpkt        ; point es:bx to IOCTL packet
        mov     es:[bx].PktLen,LenGenIOCTL ; get length
        mov     es:[bx].PktCmd,CMDGenIOCTL ; get ioctl cmd
        mov     es:[bx].GIOCategory,0    ; get category for base drivers
        mov     es:[bx].GIOFunction,EISA_FNNUMBER ; get function for EISA info.
        mov     word ptr es:[bx].GIODataPack+2,ds       ; get data addr
        mov     word ptr es:[bx].GIODataPack,OFFSET Slot ; get data addr
        mov     word ptr es:[bx].GIOParaPack+2,ds       ; get parm addr
        mov     word ptr es:[bx].GIOParaPack,OFFSET EisaCall

        push    word ptr [OEMinfo].pcs  ; setup pointer to function
        push    word ptr [OEMinfo].poffset
        mov     bp, sp
        call    dword ptr [bp]          ; call external OEMHLP IOCTL routine

        pop     ax                      ; remove call address from stack
        pop     ax

        and     es:[bx].PktStatus, NOT STDON ; remove init. time flag
        .if     < es:[bx].PktStatus ne 0 > ; OEMHLP error?
            mov ah, EISA_INVALID_BIOS_CALL ;  Y: Indicate not EISA machine
        .else
            mov ah, Slot.esi_bReturn    ;  N: Use the EISA return code
        .endif

        pop     es                      ; restore work regs.
        pop     bp
        pop     bx

        ret

GetSlotInfo     endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  RecordFunc                                              *
;                                                                           *
; DESCRIPTIVE NAME: Record EISA function information.                       *
;                                                                           *
; FUNCTION:     After a function has been found that is a parallel device,  *
;               this routine is called to save the configuration            *
;               of the device, if it is a line printer (LPT).               *
;                                                                           *
; ENTRY POINT:  RecordFunc                                                  *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               Function - contains information on a PAR device             *
;                                                                           *
; EXIT-NORMAL:  Lpts - updated                                              *
;                                                                           *
; EXIT-ERROR:   None.                                                       *
;                                                                           *
; EFFECTS:      None. All registers restored.                               *
;                                                                           *
;****************************************************************************
public  RecordFunc
RecordFunc proc near

        pusha                           ; save regs.
        lea     si, Function.efi_achType   ; device type string
        mov     di, OFFSET semi_str     ; delimiter string to look for
        mov     bx, LEN_SEMI_STR        ; length of delimiter string
        mov     ax, EISA_DEVICE_TYPE_LEN ; size of type ASCII string field
        call    ScanString              ; is there a subtype delimiter?
        .if     < nz >                  ;  N: return to caller
            jmp     record_exit
        .endif

        mov     di, OFFSET lpt_str      ; beginning of subtype string
        mov     bx, LEN_LPT_STR         ; length of subtye string
        inc     si                      ; first char. after subtype delim.
        mov     ax, OFFSET Function     ; beginning of structure
        add     ax, OFFSET efi_achType  ; adjusted to beginning of ASCII
        add     ax, EISA_DEVICE_TYPE_LEN  ; add max. offset
        sub     ax, si                  ; subtract current offset for len.
        call    ScanString              ; is the subtype string 'LPT'?
        .if     < nz >                  ;  N: return to caller
            jmp     record_exit
        .endif

        dec     si                      ; check to make sure 'LPT' is alone:
        cld                             ; make sure we're going forward
        lodsb                           ; get char. right before 'LPT'
        .if     < al e ';' > or         ; is it a semicolon?
        .if     < al e ' ' > or         ; or a space?
        .if     < al e TAB >            ; or a TAB?
            nop                         ;  Y: then it's OK, keep going
        .else
            jmp     record_exit         ;  N: something in front of 'LPT', exit
        .endif

        add     si, LEN_LPT_STR         ; point to unit number
        call    atoi                    ; attempt to convert to integer
        .if     < c >                   ; valid integer?
            jmp     record_exit         ;  N: so exit
        .endif

        push    ax                      ; save the integer value
        mov     ax, LEN_EISALPT_STRUC   ; struc. size
        mul     lpt_index               ;  multiplied by current index
        mov     bx, ax                  ;  yields an offset.
        inc     lpt_index               ; increment for next time
        pop     ax                      ; get integer back
        mov     LPTsFound[bx].printer_number, al  ; store the unit number
        lea     si, Function.efi_eiri
        lodsb                           ; get IRQ info.
        .if     < bit al and EISA_IRQ_SHARABLE > and      ; is the IRQ sharable?
        .if     < bit al and EISA_IRQ_LEVEL >             ; AND level triggered?
            mov     LPTsFound[bx].sharable_IRQ, INT_SHARING   ; it is sharable
        .else
            mov     LPTsFound[bx].sharable_IRQ, 0   ; not sharable
        .endif
        and     al, 0Fh                 ; mask off all but IRQ number
        mov     LPTsFound[bx].IRQ_number, al    ; store IRQ number
        lea     si, Function.efi_epi    ; port info.
        cld                             ; go forward
        lodsb                           ; get I/O port information
        and     al, EISA_NUMBER_PORTS   ; mask off all but number of ports
        mov     cx, ax                  ; save the number of ports
        lodsw                           ; get base port addr
        mov     LPTsFound[bx].base_addr, ax     ; store base addr
        add     ax, cx                  ; calculate last port addr
        mov     LPTsFound[bx].end_addr, ax      ; store end addr

record_exit:
        popa                            ; restore regs.
        ret
RecordFunc endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  ScanString                                              *
;                                                                           *
; FUNCTION: Searches the string at DS:SI for an occurrence of the string    *
;           at ES:DI.  Updates DS:SI.                                       *
;                                                                           *
; ENTRY POINT:  ScanString                                                  *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               AX = length of string to be scanned                         *
;               BX = pattern length                                         *
;               DS:SI - String to search                                    *
;               ES:DI - Pattern to match                                    *
;                                                                           *
; EXIT-NORMAL:  Z = 1; search pattern was found, SI points to offset        *
;                                                                           *
; EXIT-ERROR:   Z = 0; the strings did not compare                          *
;                                                                           *
; EFFECTS:      SI                                                          *
;                                                                           *
;****************************************************************************

public  ScanString
ScanString proc near

        push    cx                      ; save count register
        mov     cx, ax                  ; get the string length
        mov     ax, bx                  ; get the pattern length
        dec     si                      ; pre-decrement for loop

search_string:
        inc     si                      ; point to next attempt
        call    strncmp                 ; compare the strings
        loopnz  search_string           ; keep trying

        pop     cx                      ; restore the count register
        ret                             ; and exit

ScanString endp

;****************************************************************************
;                                                                           *
; SUBROUTINE NAME:  strncmp                                                 *
;                                                                           *
; FUNCTION: Compares two strings for a given length; ignores null           *
;                                                                           *
; ENTRY POINT:  strncmp                                                     *
;     LINKAGE:  Call near (local and discarded after init)                  *
;               AX = string length                                          *
;               DS:SI - string 1                                            *
;               ES:DI - string 2                                            *
;                                                                           *
; EXIT-NORMAL:  Z = 1; the strings did compare                              *
;                                                                           *
; EXIT-ERROR:   Z = 0; the strings did not compare                          *
;                                                                           *
; EFFECTS:      none, all registers restored                                *
;                                                                           *
;****************************************************************************

strncmp proc near

        push    cx                      ; save regs.
        push    si
        push    di

        mov     cx, ax                  ; get the string length
        cld                             ; go forward
        repe    cmpsb                   ; compare the strings

        pop     di                      ; restore regs.
        pop     si
        pop     cx
        ret                             ; and exit

strncmp endp

CSEG    ENDS
        END
