;       SCCSID = @(#)ateisai.asm        6.3 91/04/22
; ***************************************************************************
; *
; *                       IBM/Microsoft Confidential
; *
; *                 Copyright (c) IBM Corporation  1987, 1990
; *                 Copyright (c) Microsoft Corp.  1987, 1990
; *                           All Rights Reserved
; *
; ***************************************************************************

        PAGE ,132
        .286c

        TITLE   com01.sys - Asynchronous Communication Device Driver
        NAME com01

;***    ateisai.asm - Parse EISA configuration for COM port
;                     information.  This information will be used
;                     to intitialize the driver's data structures.
;
;   EisaInit - Entry point for EISA init. code
;
;       Modification History
;
;       ACW     04/16/91        @PVW Added perfview counters/timers
;
;       WDM     04/21/94        82548 - pvwxport.inc now included in atcom.inc
;


.xlist
; 82548 include pvwxport.inc            ;@PVW
include basemaca.inc                    ; various macro's (break, ljc, etc.)
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
include atcom.inc
include ateisa.inc                      ; equates for eisa COM code
include eisa.inc

        EXTRN   DOSDEVIOCTL:far
        EXTRN   DOSOPEN:far

HSEG    SEGMENT

        PUBLIC  IsEISA
        PUBLIC  SystemCOMs


oemhlp_name     db      "OEMHLP$ ",0    ; OEMHLP$ name
dev_str         db      "COM"           ; device id
LEN_DEV_STR     equ     $-dev_str       ; string length
type_str        db      "ASY"           ; device type
LEN_TYP_STR     equ     $-type_str      ; string length
sub_str         db      "COM"           ; device subtype
LEN_SUB_STR     equ     $-sub_str       ; string length
semi_str        db      ";"             ; subtype delimiter
LEN_SEMI_STR    equ     $-semi_str      ; string length
Slot            EisaSlotInfo <>         ; Slot info.
Function        EisaFuncInfo <>         ; Function info.
EisaCall        EisaCallParameters <>   ; EISA call info.
COMsFound       db      LEN_EISACOM_STRUC * MAXECOMPORTS DUP (0)   ; array for EISA info.
SystemCOMs      db      LEN_EISACOM_STRUC * MAXECOMPORTS DUP (0)   ; array for all system COM ports
com_index       db      0               ; offset in to COMsFound array
bios_found      dw      MAXISACOMPORTS DUP (0) ; match found in EISA config. info
Temp_COMFound   EISACom <>              ; temp variable for sorting
IsEISA          db      0               ; flag indicating valid EISA info.
oemhlp_handle   dw      0               ; handle for file access
action          dw      0               ; DOSOPEN parameter

HSEG    ENDS

FA_SYSTEM       equ     4               ; system file attribute
OEMHLP_CAT      equ     80h             ; DOSDEVIOCTL category
OPN_EXIST       equ     0001h           ; open an existing file/create a file
OPN_RDWRACC     equ     0042h           ; read/write access


RSEG    SEGMENT PUBLIC  'RESCODE'
        ASSUME  CS:RSEG,DS:HSEG,ES:NOTHING,SS:NOTHING

;**     EisaInit - entry point for EISA init. processing.
;
;       This subroutine will get called from ComInit to check
;       for the presence of an EISA system.  If
;       running on an EISA system, then this routine will
;       parse the EISA system's non-volatile configuration CMOS,
;       looking for COM port information.  This information will be
;       stored in a table, for use during ComInit's set-up of the 'ComN'
;       data structures.
;
;       ENTRY   None
;
;       EXIT    IsEISA flag = 1 if EISA COM ports found
;                           = 0 otherwise
;
;       USES    flags, all other regs. restored

        PUBLIC EisaInit
Procedure EisaInit
        ASSUME  CS:RSEG,DS:HSEG,ES:NOTHING,SS:NOTHING

        pusha                           ; save all regs.
        push    es                      ; and ES, too
        call    OpenOEMHLP              ; 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
                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,  dev_str    ; device string to look for
                mov     ax, LEN_DEV_STR ; string length
                call    strncmp         ; is this function a COM device?
                .if     < z > and       ;  Y: also check to see if it's enabled
                .if     < bit Function.efi_fbFunction z EISA_FUNC_ENABLED > ; if enabled
                    lea     si, Function.efi_achType    ; point to dev. string
                    add     si, LEN_DEV_STR             ; point to the comma
                    inc     si          ; point to the type string
                    lea     di, type_str ; type string to look for
                    mov     ax, LEN_TYP_STR ; string length
                    call    strncmp     ; is this an asynchronous port?
                    .if     < z >
                        call    RecordFunc  ;  Y: store the information
                    .endif
                .endif
                .if     < com_index e MAXECOMPORTS >  ; Just found eighth COM port?
                    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     < com_index g 0 >       ; if any COM info. found, then
            call    CompareBIOS         ;  compare results to BIOS data area
            call    AddEISA             ;  then add in any EISA only COM ports
            mov     IsEISA, 1           ;  indicate valid data
        .endif

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

;**     AddEISA - Add in EISA COM ports.
;
;       This routine fills in the data structure used to init. the driver
;       with any information that was found on non-ISA compatible
;       COM ports.  These are COM ports that do not begin
;       at the ISA compatible port addresses.
;
;       ENTRY   None.
;
;       EXIT    None.
;
;       USES    All registers destroyed.
;

public  AddEISA
AddEISA         proc    near

        .if     < com_index e 0 >       ; already matched up all the
            ret                         ;  EISA COM ports, so just exit
        .endif

        .if     < com_index g 1 >       ; if more than one un-matched
                                        ;  EISA COM port remaining
            ; bubble sort the EISA COM ports

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

scan_array:
            mov     al, [si].COMport_number     ; unit number
            mov     di, si
            add     di, LEN_EISACOM_STRUC       ; point to next entry
            .if     < al g [di].COMport_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_COMFound ; temp buffer
                mov     cx, LEN_EISACOM_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_EISACOM_STRUC / 2  ; prepare for word move
                rep     movsw           ; copy current+1 to current
                pop     di              ; get current+1 offset back
                mov     si, OFFSET Temp_COMFound ; temp buffer
                mov     cx, LEN_EISACOM_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_EISACOM_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 COM ports.

        mov     al, MAXISACOMPORTS      ; offset to first available EISA slot
        xor     ah, ah
        mov     bx, LEN_EISACOM_STRUC
        mul     bx                      ; construct an offset in to the
                                        ;  driver's data structure
        mov     di, OFFSET SystemCOMs   ; beginning of driver's data structure
        add     di, ax                  ; first empty entry in driver's data
        mov     cx, MAXECOMPORTS        ; max. number of EISA COM ports
        mov     si, OFFSET COMsFound    ; first entry in EISA array
        mov     dl, MAXISACOMPORTS      ; 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].COMport_number g 0 > and  ; if valid data
        .if     < [si].In_use e 0 > and ; and this COM port is not in use
        .if     < [si].base_addr ne COM1_PORT > and ; and it's not ISA COM1
        .if     < [si].base_addr ne COM2_PORT >     ; or ISA COM2

            ; 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].base_addr, ax  ; have the driver use it
            mov     al, [si].IRQ_number ; get IRQ level from EISA
            mov     [di].IRQ_number, al ; have the driver use it
            mov     al, [si].sharable_IRQ  ; get IRQ sharing info. from EISA
            mov     [di].sharable_IRQ, al  ; have the driver use it

            add     di, LEN_EISACOM_STRUC  ; point to next free entry
            inc     bx
            inc     bx                  ; point to next int. routine
            inc     dl                  ; add another COM port
            .if     < dl e MAXECOMPORTS > ; max reached?
                jmp     exit_add        ;  Y: exit
            .endif
        .endif

        add     si, LEN_EISACOM_STRUC   ; point to next EISA COM port
        loop    scan_eisa               ; process all EISA COM ports

exit_add:
        ret

AddEISA         endp

;**     atoi
;
;       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   (ds:si) -> String to convert
;
;       EXIT    if 'C' clear
;                       AX = integer value
;               else
;                       invalid input
;
;       USES    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

;**     CompareBIOS
;
;       This function reconciles the data found in the EISA
;       config. information with the BIOS data area's COM
;       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 data structure that is used to initialize the driver.
;       After this is done, the result is checked against the
;       BIOS data area.  If there are any BIOS data area entries
;       that do not have a corresponding entry in the data structure used to
;       initialize the driver, then the driver data structure is filled
;       in with ISA defaults.
;
;       ENTRY   None
;
;       EXIT    None
;
;       USES    All registers destroyed.
;

public  CompareBIOS
CompareBIOS     proc    near

        push    es                      ; save our data selector
        mov     ax, COM_SEG             ; get selector for BIOS data area
        mov     es, ax
        xor     bx, bx                  ; create offset to BIOS data area

        mov     cl, com_index           ; number of COM ports in EISA info.
        xor     ch, ch                  ; clear high byte
        mov     di, OFFSET COMsFound    ; beginning of EISA array

check_all_EISA_COMs:
        push    cx                      ; save # of EISA COM ports loop count
        mov     cl, MAXISACOMPORTS      ; max. number of BIOS found COM ports
                                        ;  supported
        xor     dx, dx                  ; array index
        push    bx                      ; save beginning index

check_all_BIOS_entries:
        mov     ax, [di].base_addr      ; EISA base addr.
        .if     < <word ptr es:[bx]> g 0 > and     ; if there is a BIOS data entry
        .if     < es:[bx] ge ax >       ;  and it could be in range
            mov     ax, [di].end_addr   ; get the end of the range
            .if     < es:[bx] le ax > and ; if in range
            .if     < [di].In_use e 0 > ; 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    dx              ; save array index
                push    bx              ; save offset to BIOS data area
                mov     ax, dx          ; get array index
                mov     bx, LEN_EISACOM_STRUC
                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     SystemCOMs[bx].base_addr, ax ; save for use by the driver
                mov     al, [di].IRQ_number     ; get IRQ level from EISA
                mov     SystemCOMs[bx].IRQ_number, al  ; save for use by the driver
                mov     al, [di].sharable_IRQ   ; get IRQ sharing info. from EISA
                mov     SystemCOMs[bx].sharable_IRQ, al ; save for use by the driver

                pop     bx              ; restore offset to BIOS data area
                pop     dx              ; restore array index
                push    bx              ; save offset to BIOS data area
                mov     bx, dx          ; get the array index
                shl     bx, 1           ; adjust to word offset
                mov     bios_found[bx], 1  ; indicate valid info.
                pop     bx              ; restore offset to BIOS data area
                dec     com_index       ; keep track of how many EISA LPTs used
            .endif
        .endif

        inc     bx
        inc     bx                      ; point to the next BIOS data entry
        inc     dx                      ; next array index
        loop    check_all_BIOS_entries  ; process each valid BIOS entry

        pop     bx                      ; restore initial offset to BIOS
        pop     cx                      ; restore EISA loop count
        add     di, LEN_EISACOM_STRUC   ; point to next array entry
        loop    check_all_EISA_COMs     ; process all EISA COM ports found

        mov     cx, MAXISACOMPORTS      ; loop counter
        xor     bx, bx                  ; offset into BIOS data area
        mov     di, OFFSET SystemCOMs   ; "save" array

fill_in_ISA_values:
        .if     < <word ptr es:[bx]> ne 0 > and    ; if there is a COM port
        .if     < bios_found[bx] e 0 >  ; and no EISA info. was found

            ; Fill in the total system COM port array with ISA defaults,
            ; since no EISA config. information was found.
            mov     [di].sharable_IRQ, 0    ; no IRQ sharing
            mov     ax, es:[bx]         ; port addr.
            mov     [di].base_addr, ax  ; have device driver use it
            .if     < ax e COM1_PORT >  ; COM1 base address?
                mov     [di].IRQ_number, COM1_VEC       ; use IRQ4
            .else                       ; use IRQ3
                mov     [di].IRQ_number, COM2_VEC
            .endif
        .endif
        inc     bx
        inc     bx                      ; next BIOS data area entry
        add     di, LEN_EISACOM_STRUC   ; next detice driver entry
        loop    fill_in_ISA_values      ; process all BIOS data area COM ports

        pop     es                      ; get our data selector back
        ret
CompareBIOS     endp

;**     GetFuncInfo
;
;       Set up an IOCTL structure to call OEMHLP for EISA
;       function information.
;
;       ENTRY   <cl> = Slot number requested
;               <ch> = Function number requested
;
;       EXIT    if <ah> = 0
;                       Variable 'Function' contains result
;               else
;                       <ah> contains error code
;
;       USES    ax
;

public  GetFuncInfo
GetFuncInfo     proc    near

        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

        push    ds
        push    OFFSET Function         ; returned data structure
        push    ds
        push    OFFSET EisaCall         ; parm. list
        push    EISA_FNNUMBER           ; request code for EISA BIOS call
        push    OEMHLP_CAT              ; IOCTL category code
        push    [oemhlp_handle]         ; file handle
        call    DOSDEVIOCTL             ; make call to OEMHLP$
        .if     < ax ne 0 >             ; 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

        ret

GetFuncInfo     endp


;**     GetSlotInfo
;
;       Set up an IOCTL structure to call OEMHLP for EISA
;       slot information.
;
;       ENTRY   <cl> = Slot number requested
;
;       EXIT    if <ah> = 0
;                       Variable 'Slot' contains result
;               else
;                       <ah> contains error code
;
;       USES    ax
;

public GetSlotInfo
GetSlotInfo     proc    near

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

        push    ds
        push    OFFSET Slot             ; returned data structure
        push    ds
        push    OFFSET EisaCall         ; parm. list
        push    EISA_FNNUMBER           ; request code for EISA BIOS call
        push    OEMHLP_CAT              ; IOCTL category code
        push    [oemhlp_handle]         ; file handle
        call    DOSDEVIOCTL             ; make call to OEMHLP$
        .if     < ax ne 0 >             ; 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

        ret

GetSlotInfo     endp

;**     OpenOEMHLP
;
;       Uses DOSOPEN to get a handle for access to OEMHLP$
;
;       ENTRY   None
;
;       EXIT    if 'C' clear
;                       OEMHLP_handle set
;               else
;                       error
;
;       USES    AX
;

public OpenOEMHLP
OpenOEMHLP    proc    near

        push    ds
        push    OFFSET oemhlp_name      ; device name
        push    ds
        push    OFFSET oemhlp_handle    ; file handle
        push    ds
        push    OFFSET action           ; action taken
        push    0                       ; high order half of file size
        push    0                       ; low order half of file size
        push    FA_SYSTEM               ; file attribute
        push    OPN_EXIST               ; fail if it does not exist
        push    OPN_RDWRACC             ; get read/write access
        push    0                       ; reserved
        push    0                       ; reserved
        call    DOSOPEN                 ; get a handle for OEMHLP$
        .if     < ax e 0 >              ; error?
            clc                         ;  N: indicate no error
        .else
            stc                         ;  Y: indicate error
        .endif

        ret

OpenOEMHLP    endp

;**     RecordFunc
;
;       After a function has been found that is an asynchronous COM device,
;       this routine is called to save the configuration
;       of the device.
;
;       ENTRY   Function - contains information on a COM device
;
;       EXIT    COMs - updated
;
;       USES    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 sub_str      ; beginning of subtype string
        mov     bx, LEN_SUB_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 'COM'?
        .if     < nz >                  ;  N: return to caller
            jmp     record_exit
        .endif

        dec     si                      ; check to make sure 'COM' is alone:
        cld                             ; make sure we're going forward
        lodsb                           ; get char. right before 'COM'
        .if     < al e ';' > or         ; 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 'COM', exit
        .endif

        add     si, LEN_SUB_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_EISACOM_STRUC   ; struc. size
        mul     com_index               ;  multiplied by current index
        mov     bx, ax                  ;  yields an offset.
        inc     com_index               ; increment for next time
        pop     ax                      ; get integer back
        mov     COMsFound[bx].COMport_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     COMsFound[bx].sharable_IRQ, INT_SHARING   ; it is sharable
        .else
            mov     COMsFound[bx].sharable_IRQ, 0   ; not sharable
        .endif
        and     al, 0Fh                 ; mask off all but IRQ number
        mov     COMsFound[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     COMsFound[bx].base_addr, ax     ; store base addr
        add     ax, cx                  ; calculate last port addr
        mov     COMsFound[bx].end_addr, ax      ; store end addr

record_exit:
        popa                            ; restore regs.
        ret
RecordFunc endp

;**     ScanString
;
;       Searches the string at DS:SI for an occurrence of the string
;       at ES:DI.  Updates DS:SI.
;
;       ENTRY   <ax> = length of string to be scanned
;               <bx> = pattern length
;               <ds:si> - String to search
;               <es:di> - Pattern to match
;
;       EXIT    if 'Z' = 1
;                       search pattern was found, <si> -> offset
;               else
;                       search pattern not found
;
;       USES    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

;**     strncmp
;
;       Compares two strings for a given length; ignores null
;
;       ENTRY   <ax> = string length
;               <ds:si> - string 1
;               <es:di> - string 2
;
;       EXIT    if 'Z' = 1
;                       the strings did compare
;               else
;                       the strings did not compare
;
;       USES    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

RSEG    ENDS
        END
