; ***************************************************************************
; *
; *                       IBM/Microsoft Confidential
; *
; *                 Copyright (c) IBM Corporation  1987, 1990
; *                 Copyright (c) Microsoft Corp.  1987, 1990
; *                           All Rights Reserved
; *
; ***************************************************************************

        PAGE    ,132
        .286p

        TITLE   com01.sys - Asynchronous Communication Device Driver
        NAME com01


;***    atinit.asm - Initialize the ports
;
;       Attempt to initialize the ports.
;
;       ComInit - Entry point for initialization request packet
;       InitQueues - initialize the queue structures (pointers)
;
;       Modification History
;
;       BD DG                   Original Creation
;       DJG     01/06/87        Re-written to conform to MS standard for
;                               style, clarity and efficiency.
;
;       YN      05/31/89        @VDM MVDM Support for OS/2 Release 2.0
;       ACW     04/16/91        @PVW Added perfview counters/timers
;       YN      11/03/91        COM 3 & 4 Support Added
;       JAG     08/24/93        71597 Init logic changed - parse once
;       JAG     09/24/93        74210 Fix single digit irq parsing
;       WDM     04/21/94        82548 pvwxport.inc now included in atcom.inc
;       JAG     08/26/94        95208 config.sys line pointer corrupted
;
;       RAR     09/10/94        A Virtual rewrite... with COMMENTS!
;       JAG     09/26/94        SMC workaround breaks PCMCIA w/octs=on
;       JAG     10/26/94        103142 Refix TP750 PCMCIA support
;       JAG     10/26/94        101958 Fix EISA support


.xlist
include basemid.inc
include config.inc
include devhlp.inc
include devsym.inc
include basemaca.inc
include realmac.inc
include aberror.inc
include infoseg.inc
include osmaca.inc
.list

include atcom.inc
include ateisa.inc


        EXTRN   CmxDone:FAR
        EXTRN   CmxGenFail:FAR
        EXTRN   ComInt1:FAR
        EXTRN   ComInt2:FAR
        EXTRN   ComInt3:FAR
        EXTRN   ComInt4:FAR
        EXTRN   SComInt1:FAR
        EXTRN   SComInt2:FAR
        EXTRN   SComInt3:FAR
        EXTRN   SComInt4:FAR
        EXTRN   PCOMStackEntry:FAR              ; @VDM
        EXTRN   IntRoutines:WORD
        extrn   EisaInit:near
        extrn   Flags:byte
        EXTRNFAR   InitSharedIRQ
        EXTRNFAR   InitQueues
        EXTRNFAR   OptimizeSharedIRQs


        extrn   _RMHELP_SetDevHelp:far
        extrn   _RMHELP_CreateDriver:far
        extrn   _RMHELP_GetPorts:far
        extrn   _RMHELP_PortInitComplete:far
        extrn   _RMHELP_PortDidntInstall:far
        extrn   _RMHELP_DestroyDriver:far
        extrn   _RMHELP_FindDetected:far


DSEG  SEGMENT
        even
        public  DevHlp
    DevHlp  dd      (?)     ; Pointer to device driver help routine

    public  ShrdIRQ1,ShrdIRQ2,ShrdIRQ3,ShrdIRQ4

    ShrdIRQ1 SharedIntData  <offset RSEG:SComInt1,,,,> ; 1st shrd IRQ struc.
    ShrdIRQ2 SharedIntData  <offset RSEG:SComInt2,,,,> ; 2nd shrd IRQ struc.
    ShrdIRQ3 SharedIntData  <offset RSEG:SComInt3,,,,> ; 3rd shrd IRQ struc.
    ShrdIRQ4 SharedIntData  <offset RSEG:SComInt4,,,,> ; 4th shrd IRQ struc.

;***
;***  For AssignIRQ, Com1 through Com4 must be continuous, Com1P through Com4P
;***  must be continuous as well.
;***
    public  NextCom, Com1, Com2, Com3, Com4, FirstCom
    public  Com1P, Com2P, Com3P, Com4P

    NextCom dw      0               ; offset of next cominfo structure to allocate
    Com1    dw      0               ; offset of cominfo for com1
    Com2    dw      0               ; offset of cominfo for com2
    Com3    dw      0               ; offset of cominfo for com3
    Com4    dw      0               ; offset of cominfo for com4
    Com1P   dw      0               ; hidden offset of cominfo for com1
    Com2P   dw      0               ; hidden offset of cominfo for com2
    Com3P   dw      0               ; hidden offset of cominfo for com3
    Com4P   dw      0               ; hidden offset of cominfo for com4

    public  Kernel_Type
    Kernel_Type    DW      -1          ; for ABIOS or non ABIOS kernels

    public  GAS_Switch
    GAS_Switch     DW      0           ; for ABIOS or non ABIOS kernels



    ; MASM/Link bug fix start

    FirstCom db size ComInfo dup(0) ; 1st cominfo structure
         db size ComInfo dup(0) ; 2nd cominfo structure
         db size ComInfo dup(0) ; 3rd cominfo structure
         db size ComInfo dup(0) ; 4th cominfo structure

    ; MASM/Link bug fix end

    public ENDSWAPDATA
    ENDSWAPDATA     dw      $       ; last swap data (include cominfo due to timer)

    Generic_Name    DB      'COM',0             ; generic name for this device
    FiniOncePass    DB      0                   ; FiniOnce passcounter

    public PCMCIA_Flag
    PCMCIA_Flag     DB      0

    PCMCIA_Dev_Name DB    "PCMCIA$  ",00h
    PCMCIA_Data     DW    6 dup (0)

    public IRQ3_Count, IRQ4_Count
    IRQ3_Count      DB      0
    IRQ4_Count      DB      0


DSEG  ENDS


HSEG  SEGMENT

    ; Data from here on gets released after system initalization

    ENDRESIDENTDATA EQU     $               ; end of data after initialization

        EXTRN   IsEISA:BYTE             ; EISA flag
        EXTRN   SystemCOMs:BYTE         ; data from EISA config. info.

    ; used by DOSGETMESSAGE, DOSPUTMESSAGE
    Num             DB      0               ;@@ Port Number
    PortName        DB      "COM"           ; port name (COM + port number => COM1)
    PortNum         DB      "1",0           ; port number (1-2) and null byte
    InitOncePass    DB      0               ; InitOnce pass-counter
    com_minor       DW      0               ; com minor number



; *****************                           *****************@PVW
; ************                                     ************@PVW
; ******                Name Block                       ******@PVW
; ************                                     ************@PVW
; *****************                           *****************@PVW

;************                                     ************@PVW
;*******  If Adding Counters or Timers then change      ******@PVW
;*******  the Number_Tmrs_Ctrs EQU                      ******@PVW
;**************                                   ************@PVW                 ;@PVW
Number_Tmrs_Ctrs   EQU     8       ;number of Ctrs + Tmrs    ;@PVW

str1  db   "Read Time"             ,0       ;@PVW
str2  db   "Write Time"            ,0       ;@PVW
str3  db   "Number Reads"          ,0       ;@PVW
str4  db   "Bytes Read"            ,0       ;@PVW
str5  db   "Number Writes"         ,0       ;@PVW
str6  db   "Bytes Written"         ,0       ;@PVW
str7  db   "Hardware Overruns"    ,0       ;@PVW
str8  db   "Software Overruns"    ,0       ;@PVW
;************                                     ************@PVW
;*******  If Adding Counters or Timers then see         ******@PVW
;*******  the Number_Tmrs_Ctrs EQU                      ******@PVW
;**************                                   ************@PVW                 ;@PVW
           even                                  ;@PVW
public  Name_Block                               ;@PVW
Name_Block dd    PVW_CT_TIMR   ;tmr  A           ;@PVW
           dw    size TIMR                       ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str1   ;; Read_Time      ;@PVW
           dw    SEG str1                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_TIMR   ;tmr  B           ;@PVW
           dw    size TIMR                       ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str2   ;; Write_Time     ;@PVW
           dw    SEG str2                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr A           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str3   ;; Num_Reads      ;@PVW
           dw    SEG str5                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr B           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str4   ;; Read_Bytes     ;@PVW
           dw    SEG str4                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr C           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str5   ;; Num_Writes     ;@PVW
           dw    SEG str5                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr D           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str6   ;; Write_Bytes    ;@PVW
           dw    SEG str6                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr E           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str7   ;; HW_Overruns    ;@PVW
           dw    SEG str7                        ;@PVW
                                                 ;@PVW
           dd    PVW_CT_CNT    ;cntr F           ;@PVW
           dw    size CNT                        ;@PVW
           dw    0                               ;@PVW
           dw    OFFSET str8   ;; SW_Overruns    ;@PVW
           dw    SEG str8                        ;@PVW
;************                                     ************@PVW
;*******  If Adding Counters or Timers then see         ******@PVW
;*******  the Number_Tmrs_Ctrs EQU                      ******@PVW
;**************                                   ************@PVW                 ;@PVW
public Name_Block_End                            ;@PVW
Name_Block_End       dw       $                  ;@PVW

; *****************                           *****************@PVW
; ************                                     ************@PVW
; ******                Text Block                       ******@PVW
; ************                                     ************@PVW
; *****************                           *****************@PVW

Public Text_Block                                ;@PVW
Text_Block dd  TBH_VER_2_0_0_0    ; usID         ;@PVW
                                                 ;@PVW
           dw  0       ; Block instance ID       ;@PVW
           dw  0       ; Block group ID          ;@PVW
;      Text Block Group name                     ;@PVW
           dd  0       ; Flags                   ;@PVW
           dw  0       ; Size                    ;@PVW
           dw  0       ; Message ID              ;@PVW
           dw  OFFSET  PortName                  ;@PVW Seg:Off of "COM#" string
           dw  SEG     PortName                  ;@PVW Updated automatically
;      Text Block instance name                  ;@PVW
           dd  0       ; Flags                   ;@PVW
           dw  0       ; Size                    ;@PVW
           dw  0       ; Message ID              ;@PVW
           dd  0                                 ;@PVW
                                                 ;@PVW
           dd  0                                 ;@PVW
           dd  0                                 ;@PVW
           dd  Number_Tmrs_Ctrs                  ;@PVW
           dw  OFFSET  Name_Block                ;@PVW Seg:Off for name block
           dw  SEG     Name_Block                ;@PVW Same for all ports

rbu        DB  size RequestBlock dup(0)  ; of struct

HSEG  ENDS


CSEG    SEGMENT
    public  endswapcode
    endswapcode     label   byte
CSEG    ENDS


RSEG  SEGMENT

        ASSUME cs:RSEG,ds:DSEG,es:NOTHING,ss:NOTHING

ENDOFCODE       EQU     $               ; end of code after initialization

;**     ComInit - initialize the device driver
;
;       Save the pointer to DevHlp.
;       Check for correct system type (ABIOS NOT present)
;       Check the 40:x data area for a valid port address.
;       Determine whether chip is a 16450 or an 8250 (scratch register)
;       Save the address of the interrupt handler.
;       Display version number message.
;       Display initialization message.
;       Give init code and data memory back to the system.
;
;       After system initialization, release as much memory as possible:
;               all init code
;               data after the ComInfo structures
;               data for ComInfo structures for ports that did not install
;               all memory (code and data) if no ports install
;
;       ENTRY   (es:di) -> request packet
;               (ax) = device minor number
;
;       EXIT    (es:di).InitEcode = end of code
;               (es:di).InitEdata = end of data
;
;       USES    ax bx cx dx si

Procedure ComInit,FAR
        ASSUME cs:RSEG,ds:HSEG,es:NOTHING,ss:NOTHING

        SaveReg         <ds>
        SaveReg         <es>
        setDS   HSEG
        mov     Num,al                          ; @@MD Save the port Number

        ; save the pointer to DevHlp
        setDS   DSEG
        mov     bx,es:[di].IOpData._hi
        mov     [DevHlp]._hi,bx
        mov     bx,es:[di].IOpData._lo
        mov     [DevHlp]._lo,bx
        .errnz  Com1Minor-0
        .errnz  Com2Minor-1

        setDS   HSEG
        mov     al,Num
        mov     PortNum,'1'             ; initialize port num string
        add     PortNum,al              ; add minor port number
        mov     com_minor,ax            ; (com_minor) = com minor number

; RAROSE's helpful guide to COM appearing now...
;
; 1. Some things (like determining if we're on an ABIOS machine) need to
;    be done only once in the entire lifetime of the whole driver.  These
;    things are done within InitOnce. (Command line parsing is also done here)
;    And of course this routine requires that es:di still have the request
;    packet pointer in it.
;
        call    InitOnce                ; Do 1-time code
        ljc     genfail                 ; General Failure
;
; 2. InitEvery contains code that must be run for every com port init.  The
;    setting of SI to the ComInfo structure is done here too!
;
        call    InitEvery               ; Do init every time through
        mov     ax,FFF_INITEVERY        ; Set error location
        ljc     FailFree

;
; 3. If we're on an ABIOS machine we need to verify we can get the LID and
;    and we should also get device information while we're there.  This is
;    done here by CheckAbiosLID.
;    JAG103142 - If can't get LID, check to see if PCMCIA
;
        call    CheckAbiosLID             ; Do per-device ABIOS work
        jnc     initcominfo               ; Got LID ok
        setDS   DSEG
        test    PCMCIA_Flag,PCMCIA_SYSTEM ; Check if this is PCMCIA
        setDS   HSEG
        jnz     initcominfo               ; PCMCIA non-ABIOS aware
        mov     ax,FFF_ABIOSLID           ; Set error location
        jmp     FailFree

;
; 4. There is apparently a MASM bug where we cannot initialize the ComInfo
;    structures statically.  So we do it here...
;

public initcominfo
initcominfo:

        setDS   DSEG
        ; MASM/Link bug fix start
.386p
        mov     [si].ci_dcb_writeto, WRITE_TO_INIT      ; write timeout
        mov     [si].ci_dcb_readto, READ_TO_INIT        ; read  timeout
        mov     [si].ci_dcb_flags1, F1_INIT             ; first  flags byte
        mov     [si].ci_dcb_flags2, F2_INIT             ; second flags byte
        mov     [si].ci_dcb_flags3, F3_INIT             ; third  flags byte
        mov     [si].ci_dcb_XonChar, xonequ             ; XON  character
        mov     [si].ci_dcb_XoffChar, xoffequ           ; XOFF character
        mov     [si].ci_signature, SIGNATURE            ; signature ID word
        mov     [si].ci_baud, DEFAULT_BAUD              ; baud rate
        mov     [si].ci_bytesize, DEFAULT_BYTESIZE      ; TX/RX byte size
        mov     [si].ci_parity, DEFAULT_PARITY          ; parity
        mov     [si].ci_stopbits, DEFAULT_STOPBITS      ; stop bits
        mov     [si].ci_cmask, DEFAULT_CHAR_MASK      ; received character mask
        mov     [si].ci_depth, D_NONE                   ; interrupt depth
        mov     [si].ci_r_rpl.rpl_signature, SIGNATURE
        mov     [si].ci_w_rpl.rpl_signature, SIGNATURE
        mov     [si].ci_int_sharing, 0                  ; int. sharing
        mov     [si].ci_mult_COMs_IRQ, 0                ; no other COM ports
                                                        ; on this IRQ line
        or      [si].ci_dcb_flags2,F2_BRK_CHAR  ;break replace active
        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_TX   ;@VDM
        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_RX   ;@VDM
        or      [si].ci_vdm_flag,VDM_Flag_InOut_Handshake_Init
        mov     [si].ci_vdm_Rx_State,1          ;@VDM set to Rx state 1
        mov     [si].ci_vdm_Tx_State,1          ;@VDM set to Tx state 1
        mov     [si].ci_vdm_Rx_Count,0                         ;@VDM
        mov     [si].ci_vdm_Tx_Count,0                         ;@VDM
        mov     [si].ci_vdm_LastMSR,0                          ;@VDM
        mov     [si].ci_flagx1,0                               ;JAG
.286p

        ChkComInfoPtr

        xor     bh,bh                           ; clear high byte of BX
        setDS   HSEG                            ; Num is in HSEG
        mov     bl,Num
        setDS   DSEG                            ; ComInfo is in DSEG
        mov     [si].ci_port_number,bl          ; set port number in ComInfo

        .errnz  Com1Minor-0
        .errnz  Com2Minor-1

;
; 5. Is this an EISA machine?
;      If it is, fill in missing ComInfo value with the data
;      taken from the nonvolatile EISA configuration memory.
;
        setDS   HSEG                    ; HSEG selector
        cmp     IsEISA,1                ; EISA machine?
        jne     ISA_MCA_Install         ;  N: check for MCA or ISA
        call    GetEISAConfig
        mov     ax,FFF_EISACONFIG       ; Set error location
        ljc     FailFree
        jmp short AllocPorts

;
; 6. If this is an ISA or MCA machine, we find the port addresses
;    from looking at the 0040:0000 BIOS data area.
;
public ISA_MCA_Install
ISA_MCA_Install:
        call    GetISAMCAConfig
        mov     ax,FFF_ISAMCACONFIG     ; Set error location
        ljc     FailFree
        SetDS   DSEG

;
; 7. A fully filled out structure indicating the I/O range and interrupt
;    that this port is going to require now exists.  We call our Resource
;    Manager C-helper code now to get us access to goodies.
;

public AllocPorts
AllocPorts:
        mov     bx,1                    ; EISA=1
        setDS   HSEG
        mov     al,IsEISA
        cmp     al,0
        jnz     havmt
        mov     bx,2                    ; uChannel=2
        setDS   DSEG
        cmp     Kernel_Type,ABIOS_COM
        jz      havmt
        mov     bx,3                    ; PCMCIA=3
        test    [si].ci_flagx1,FX1_PCMCIA_MODEM
        jnz     DoIRQstuff
        mov     bx,4                    ; ISA=4
        ;BUG BUG - test for PCI here
        ;BUG BUG - test for PCI here
havmt:  setDS   DSEG
        push    DSEG
        push    si
        push    bx
        call    _RMHELP_GetPorts
        add     sp,6
        cmp     ax,0
        mov     ax,FFF_GETPORTS                 ; Set error location
        ljnz    FailFree                        ; couldn't get the ports...

; 7. We now do interrupt related stuff.  Namely we initialize the interrupt
;    to be shared if our ComInfo structure indicated that it will.
;
DoIRQstuff:

        setDS   DSEG
        mov     al,[si].ci_int_sharing          ; get the sharing flag
        cmp     al,INT_SHARING                  ; sharing this IRQ line?
        jne     SetIDflags
        CALLFAR InitSharedIRQ                   ; Y: init. shared data

;
; 8. We now attempt to determine the chip type by prodding it in
;    (hopefully) the right ways.
;
SetIDflags:
        ; DX=base port
        ; SI=structure containing ci_flagx
        ; This port is not enhanced, and never shall be...

        test    [si].ci_flagx1, FX1_PCMCIA_MODEM ; Check if this is PCMCIA ;JAG
        jnz     OverChipDetect
        call    ChipDetect
OverChipDetect:

        CLR     [si].ci_eflags,EF_POSSIBLE_ENHANCED
        CLR     [si].ci_eflags,EF_MODE_ENHANCED

;
; 9. We attempt to grab the interrupt vector in order to make sure it is
;    available.  If it isn't we error out.  If we can hook it, we reset
;    the UART to a state where it won't bother us.
;
        test    [si].ci_flagx1, FX1_PCMCIA_MODEM ; Check if this is PCMCIA ;JAG
        ljnz    init40                           ; yes  Install            ;JAG

        setDS   HSEG
        mov     ax,com_minor            ; relative to 0

        call    CheckIRQ
        mov     ax,FFF_CHECKIRQ         ; Set error location
        ljc     FailFree                ; error, couldn't get IRQ

        call    QuietPort               ; Turn off port for time being...

;
; 10. To date we've found the port, identified it, made sure we can grab
;     its interrupt and reset the UART (whew! we've been busy!)  We now
;     update our ci_isr variable with the correct Interrupt Service Routine
;
init40:
        setDS   HSEG                    ; HSEG selector
        mov     bx,com_minor            ; (bx) = minor number
        shl     bx,1                    ; (es:bx) -> 40:X
        setDS   DSEG
        mov     dx, IntRoutines[bx]     ; bx = com_minor*2
        add     bx, OFFSET DSEG:Com1    ; adjust to com. data struc.

        mov     [si].ci_isr,dx          ; save in cominfo
        mov     [bx],si                 ; init ComInfo ptr.

;
; 11. We initialize our queues now, and then we take care of the PerfView
;     stuff if we have it linked in.  Finally we allocate the GDT selectors
;     we need to handle this port.
;
        CALLFAR InitQueues              ; initialize input and output queues

ifdef PERFVIEW
        call    Initialize_PerfView_Data ; DS:SI -> cominfo for this port ;@PVW
endif

        call    GetGDTSels              ; Allocate GDT Selectors
        mov     ax,FFF_GETGDT           ; Set error location
        jc      FailFree                ; error, couldn't get GDT sels

;
; 12. If this is a PCMCIA system, we now go and assign the interrupt numbers
;     for the PCMCIA resident com ports.  Why?  I haven't a clue...
;
        test    [si].ci_flagx1, FX1_PCMCIA_MODEM ; Check if this is PCMCIA ;JAG
        jz      init55
        call    AssignIRQ
        mov     ax,FFF_ASSIGNIRQ        ; Set error location
        jc      FailFree

init55: cmp     NextCom,0
        jne     init60                  ; not first installation

        mov     NextCom,OFFSET DSEG:FirstCom

init60:
        add     NextCom,SIZE ComInfo    ; add cominfo size to allocate it

;
; 13. Now we're almost there...  Call FiniEvery to say we have a successful
;     installation of a port.
;
        call    FiniEvery

;
; 14. We're done! Can you believe it?  We now go and call FiniOnce.  FiniOnce
;     is called only once after the first successful installation of a port.
;     Here we currently do only one thing: register the PDD<->VDD entry point.
;
        call    FiniOnce

        mov     dx,OFFSET CSEG:CmxDone  ; set up exit address
        jmp     SHORT init91

; fail install of this device, return General Failure.
; The system will print a message (so I don't need to).

;
; 15. ERROR!  We come here if there was an error anytime in processing after
;     we created an adapter for this particular subdevice.
;
FailFree:
        setDS   HSEG
        mov     bx,com_minor
        setDS   DSEG

        push    bx
        push    ax
        call    _RMHELP_PortDidntInstall
        add     sp,4

genfail:
        mov     dx,OFFSET CSEG:CmxGenFail

init91:
        setDS   DSEG                    ; reset to swappable segment
        RestoreReg      <es>            ; (es:di) -> request packet
        ASSUME  es:NOTHING
        ChkRPPtr

        setDS   HSEG                            ; set to header segment
        cmp     com_minor, MAXCOMPORTS-1        ; last COM port?
        jne     init92                          ; N: keep going
;
; 15. If this is the last COM port to get an attempt at installation (whether
;     or not anybody has actually installed), the call AllCOMInit to do
;     final init time processing (such as optimizing shared IRQs and resource
;     manager destroy driver stuff)
;
        call    AllCOMInit                      ; Say everybody's tried...

init92:
        setDS   DSEG                            ; reset to swappable segment
        mov     bx,NextCom
        mov     es:[di].InitEdata,bx
        or      bx,bx
        jz      init95                          ; no one installed

        mov     bx,OFFSET ENDRESIDENTDATA       ; get correct size
        mov     es:[di].InitEdata,bx            ; save as data size
        setDS   DSEG                            ; reset to swappable segment

        mov     bx,OFFSET ENDOFCODE

init95:
        mov     es:[di].InitEcode,bx
        mov     es:[di].InitcUnit,0
        mov     es:[di].InitParms._hi,0
        mov     es:[di].InitParms._lo,0

        mov     bx,dx                   ; get exit address
        RestoreReg      <DS>
        ret                             ; done

EndProc ComInit

;*** InitOnce - Do One-Time Initialization
;***    ES:DI = Request Packet
;***    Returns with CARRY set on error
;***
Procedure InitOnce, NEAR
        SaveReg <ax,bx,dx,di,es,ds>

        SetDS   HSEG
        inc     InitOncePass
        cmp     InitOncePass,1
        jne     iniok

        push    es                      ; save init rp
        push    di

        SetDS   DSEG
        push    [DevHlp]._hi            ; Tell 'C' code about DevHlp Add;RAR
        push    [DevHlp]._lo                                            ;RAR
        call    _RMHELP_SetDevHelp                                      ;RAR
        add     sp,4                                                    ;RAR
        call    _RMHELP_CreateDriver    ; Create our Driver handle      ;RAR

        pop     di                      ; restore init rp
        pop     es
        setDS   HSEG
        mov     bx,es:[di+18]           ; set es:bx to point to cmd line
        Call    ParseCommandLine        ; check the cmd line for parms
        jc      inierr

        push    DSEG
        push    OFFSET DSEG:FirstCom
        call    _RMHELP_FindDetected
        add     sp,4


        push    es
        setES   DSEG
        setDS   HSEG
        call    EisaInit                ; call EISA init
        pop     es

        setDS   DSEG
        mov     bx,OFFSET DS:PCMCIA_Dev_Name
        mov     di,OFFSET DS:PCMCIA_Data
        mov     dl,DevHlp_AttachDD
        call    [DevHlp]
        jc      inipcid
        or      PCMCIA_flag,PCMCIA_SYSTEM
inipcid:

iniok:  clc
inibye: RestoreReg <ds,es,di,dx,bx,ax>
        ret
inierr: stc
        jmp short inibye
EndProc InitOnce

;*** InitEvery - Initialization code that is run for every COM port
;***
;***
Procedure InitEvery
;
; 1. Set SI to point to the current ComInfo structure
;
        setDS   DSEG
        mov     si,NextCom              ; (ds:si) -> next available ComInfo
        or      si,si
        jnz     ienot1                  ; not the first installation
        mov     si,OFFSET DSEG:FirstCom
ienot1:
        clc
        ret
EndProc   InitEvery

;*** CheckAbiosLID - Do ABIOS related initialization
;***    Returns with CARRY if rbu device information not valid
;***
;***
;***
Procedure CheckAbiosLID
        setDS   HSEG
        mov     al,DEVICE_ID            ; set device ID value
        mov     bl,Num                  ; see, if device is available
        inc     bl                      ; get correct device #
        mov     dx,DevHlp_GetLIDEntry
        setDS   DSEG
        mov     Kernel_Type,ABIOS_COM   ; assume, we are running on PS/2
        push    es
        push    ds
        pop     es

        xor     dh,dh                   ; clear it !
        SetDS   HSEG
        call    es:[DevHlp]
        jnc     dabget                  ; got LID, free it
        pop     es

        SetDS   DSEG                         ; restore Data Seg
        cmp     ax,ERROR_LID_ALREADY_OWNED   ; is owned?
        je      daberr                       ; yes, error
        cmp     ax,ERROR_LID_DOES_NOT_EXIST  ; does not exist
        je      dabat                        ; yes, treat port as AT
        mov     Kernel_Type, Non_ABIOS_COM   ; ERROR_ABIOS_NOT_PRESENT
dabat:  jmp     dabbye                       ; return, no error

daberr: stc
        ret

dabget:                      ; get the LID parameters for this COM device
        setDS   HSEG
        mov     rbu.RBLength,RB_LEN             ; Set Request Block Length
        mov     rbu.LogicalID,ax                ; move in the logical lid #
        mov     rbu.ReturnCode,F_FLAG           ; FFFFh flag in RC
        mov     rbu.CancelFlag,CANCEL_ALL_INTS  ; cancel all ints
        mov     rbu.Function_ab,AF_RTNLIDPARMS  ; fn is Return Lid Param.
        push    ax                              ; save LID
        push    si                              ; save @ of CIDA
        mov     si,offset rbu                   ; move to the rbu req block
        mov     dl,DevHlp_ABIOSCall             ; set for ABIOS call
        xor     dh,dh                           ; clear the dh register
        setDS   HSEG
        call    es:[DevHlp]                     ; call devhelp
        cmp     rbu.ReturnCode,RC_COMPLETE      ; check for ABIOS error
        jne     dabfre                          ; IF NOT error, continue
        push    ax                              ; save LID
        mov     ax,rbu.Enhanced_18H             ; set up rb length
        mov     rbu.RBLength,ax                 ;
        xor     ax,ax
        mov     rbu.Enhanced_18H,ax             ; 0 out 18h
        pop     ax                              ; restore LID

        mov     rbu.Function_ab,AF_READDEVPARMS ; fn is Return Dev Parm.
        mov     si,offset rbu                   ; move to the rbu req block
        mov     dl,DevHlp_ABIOSCall             ; set for ABIOS call
        xor     dh,dh                           ; clear the dh register
        setDS   HSEG
        call    es:[DevHlp]                     ; call devhelp
dabfre: pop     si                              ; restore @ of CIDA
        pop     ax                              ; (ax) = LID
        mov     dl,DevHlp_FreeLIDEntry          ; Free LID
        xor     dh,dh
        setDS   HSEG
        call    es:[DevHlp]
        pop     es


dabid:  setDS   HSEG
        cmp     rbu.ReturnCode,RC_COMPLETE      ; check for ABIOS error
        jne     dabbye                          ; IF NOT error, continue
        test    rbu.AsyncByte_4,FF_FIFO_SUPPORT ; is fifo supported by device
        jz      dabbye                          ; no
        setDS   DSEG
        or      [si].ci_flagx,FX_16550A         ; chip type = 16550A

dabbye: clc
        ret
EndProc CheckAbiosLID

;*** FiniEvery - Do Per-Port Post-Initialization
;***    Called after the first successful installation of a COM port
;***
;***
Procedure FiniEvery, NEAR
;
; 1. We have successfully installed the port.  We now call the resource
;    manager helper code so that it can bind the resources to us.  Also
;    we update our device information to reflect anything we might have
;    learned during chip detection, etc.
;
        mov     bx,1                    ; EISA=1
        setDS   HSEG
        mov     al,IsEISA
        setDS   DSEG
        cmp     al,0
        jnz     havbus
        mov     bx,2                    ; uChannel=2
        cmp     Kernel_Type,ABIOS_COM
        jz      havbus
        mov     bx,4                    ; ISA=4
        ;BUG BUG - test for PCI here
        ;BUG BUG - test for PCI here

havbus: push    bx                      ; pass on bus infomation
        push    DSEG
        push    si
        call    _RMHELP_PortInitComplete
        add     sp,6
        ret
EndProc   FiniEvery

;*** FiniOnce - Do One-Time Post-Initialization
;***    Called only once, after the first successful installation of
;***    a COM port
;***
Procedure FiniOnce, NEAR
        ; call VDMM to register a stack-based entry point
        SaveReg <si,di,ds,es>
        setDS   DSEG

        cmp     FiniOncePass,0
        jne     findun
        inc     FiniOncePass

        lea     si,Generic_Name         ; ds:si -> name string
        mov     dx,CSEG                 ; es:di -> routine being registered
        and     dl,0FCh                 ; make sure this is ring 0 !!!!!!!!!!!!
        mov     es,dx
        lea     di,CSEG:PCOMStackEntry
        mov     dl,DevHlp_RegisterPDD
        call    [DevHlp]
findun:
        RestoreReg <es,ds,di,si>
        ret
EndProc FiniOnce

;*** AllCOMInit - Called when all COM ports have had a chance to init
;***
;***
;***
Procedure AllCOMInit
        CALLFAR OptimizeSharedIRQs      ; Y: check shared IRQs
        ret
EndProc   AllCOMInit

;*** AssignIRQ - Set up default interrupts for COM ports (PCMCIA only)
;***    SI = offset of ComInfo structure
;***
;***    Returns with CARRY set on error
;***
Procedure AssignIRQ, NEAR
        cmp     [si].ci_port,0
        jne     airdun

        xor     cx,cx
        xor     bx,bx
        mov     al,IRQ4_Count           ; al = number of COM ports with IRQ4
        mov     ah,IRQ3_Count           ; ah = number of COM ports with IRQ3
        mov     bl,[si].ci_port_number

        cmp     bx,3
        jg      airerr

        shl     bx,1

        cmp     bx,0                    ; First COM port?
        jne     airoth

        xchg    ah,al                   ; 1st port is an exception for IRQ
                                        ; assignments.
airoth: xchg    cx,Com1[bx]
        mov     Com1P[bx],cx

        cmp     [si].ci_irq,0           ; if it has an IRQ, don't mess with it
        jne     airdun
        cmp     al,ah                   ; if IRQ4_usage >= IRQ3_usage....
        jge     airus2                  ;      then use IRQ3 else....

airus1: mov     ds:[si].ci_irq,COM1_VEC ; use COM1_VEC (IRQ4)
        inc     IRQ4_Count
        jmp short airdun

airus2: mov     [si].ci_irq,COM2_VEC    ; use COM2_VEC (IRQ3)
        inc     IRQ3_Count
airdun:
        clc                             ; OK! Clear carry and return
        ret
airerr: stc                             ; Error! Set carry and return
        ret
EndProc   AssignIRQ

;***
;*** GetEISAConfig - Update ComInfo with EISA system configuration info
;***
;***
;***
Procedure GetEISAConfig
        push    es
        push    ds
        setDS   HSEG                    ; HSEG selector
        setES   DSEG                    ; DSEG selector
        mov     ax, LEN_EISACOM_STRUC   ; use EISA COM port info.
        mov     bx, com_minor           ; get port number, rel. 0
        mul     bx                      ; create an offset
        mov     bx, ax                  ; set up index reg.
;
; 1. Does this port already have a address assigned?
;    If not, get one from the EISA data area
;
        mov     dx,es:[si].ci_port      ; get the port address
        cmp     dx,0
        jne     eii1
        mov     ax,SystemCOMs[bx].base_addr
        or      ax,ax                   ; does the port exist?
        jz      eiierr                  ; No: error exit
        mov     dx,ax                   ; update the port address
;
; 2. Does this port already have an IRQ assigned?
;    If not, get one from the EISA data area
;
eii1:   cmp     es:[si].ci_irq,0
        jne     eii3
        mov     al,SystemCOMs[bx].IRQ_number    ; get the IRQ line
        mov     es:[si].ci_irq,al               ; and save it
        cmp     al,3
        jne     eii2
        inc     es:IRQ3_Count
        jmp     SHORT eii3
eii2:
        cmp     al,4
        jne     eii3
        inc     es:IRQ4_Count
;
; 3. Is this a sharable IRQ?
;    If so, mark it and initialize it
;
eii3:
        mov     al,SystemCOMs[bx].sharable_IRQ  ; get sharing status
        mov     es:[si].ci_int_sharing, al      ; and save it
        mov     es:[si].ci_mult_COMs_IRQ,al     ; if we're sharing, assume
                                                ; it's with another COM port
        mov     SystemCOMs[bx].base_addr,dx     ; save the port as well
        mov     es:[si].ci_port,dx              ; save port JAG101958
        clc
        pop     ds
        pop     es
        ret
eiierr: stc
        pop     ds
        pop     es
        ret
EndProc GetEISAConfig

;***
;*** GetISAMCAConfig - Update ComInfo with algorithmically determined data
;***
;***
;***
Procedure GetISAMCAConfig
        push    es
        push    ds
        SetDS   HSEG
        mov     ax,COM_SEG              ; 40: tiled selector to ROM data
        mov     es,ax
        mov     bx,com_minor
        mov     ax,bx
        shl     bx,1                            ; (es:bx) -> 40:X

        mov     dx,SIZE ComInfo
        mul     dx                              ; ax -> ComInfo offset

        SetDS   DSEG
        push    di
        mov     di,OFFSET FirstCom
        add     di,ax
        mov     dx,[di].ci_port                 ; Check if the port addr is
        cmp     dx,0                            ; filled by DEVICE= parameter
        jz      Check40X
        cmp     di,si                           ; different ComInfos
        jz      NoUpdate
        mov     al,BYTE PTR [di].ci_port_number
        mov     BYTE PTR [si].ci_port_number,al
        mov     ax,WORD PTR [di].ci_port
        mov     WORD PTR [si].ci_port,ax
        mov     al,BYTE PTR [di].ci_irq
        mov     BYTE PTR [si].ci_irq,al
        mov     ax,WORD PTR [di].ci_badmax
        mov     WORD PTR [si].ci_badmax,ax
        mov     al,BYTE PTR [di].ci_flagx
        mov     BYTE PTR [si].ci_flagx,al
        mov     BYTE PTR [di].ci_port_number, 0
        mov     WORD PTR [di].ci_port, 0
NoUpdate:
        pop     di
        jmp     InstallCmdLine

Check40X:
        pop     di
        mov     dx,es:[bx]                      ; (dx) = port address
        or      dx,dx                           ; Check 40:X
        jz      CheckPCMCIA                     ; port not available
        add     dx, R_INTEN                     ; go check to see if hw there
        in      al, dx                          ; read int enable register
        cmp     al, 0ffh                        ; lines float?
        jne     installit                       ; no, install it
        mov     word ptr es:[bx],0              ; hw not there, zero 40:x
        jmp     short CheckPCMCIA               ; still could be pcmcia

installit:
        mov dx, es:[bx]
        ; dx holds es:[bx]
        ; if no one owns on CmdLine: install
        ; we know the comport for this install doesn't
        ; else check pcmcia
        push   si
        mov    si, OFFSET DSEG:FirstCom
        mov    cl, MAXCOMPORTS
        mov    ax, 0                     ; flag for when we find
Loop2:
        cmp    dx,[si].ci_port           ; find match?
        jnz    NoMatch
        inc    ax
NoMatch:
        add    si, SIZE ComInfo
        cmp    cl,0
        dec    cl
        jnz    Loop2
OutLoop2:
        pop    si
        or     ax,ax
        jz     init14_6                  ; No one else wants - go install
        mov    WORD PTR es:[bx], 0       ; update 40:X
                                         ; Someone else wants it, but maybe
                                         ; we should install anyway
CheckPCMCIA:
        test    PCMCIA_Flag,PCMCIA_SYSTEM         ; Check if this is PCMCIA
        jz      short gimerr                      ; No, this is an ERROR!
        or      [si].ci_flagx1, FX1_PCMCIA_MODEM
        jmp short gimdun                          ; yes, save for pcmcia tho

InstallCmdLine:
        ; dx holds port#
        ; update and install
        mov     es:[bx], dx                     ; update 40:X

init14_6:
        setDS   DSEG
        cmp     [si].ci_irq,0
        jne     init14_9
        cmp     dx,COM1_PORT                     ; is it COM1 port address ?
        je      init14_8                         ; Y: then, IRQ 4

        mov     [si].ci_irq, COM2_VEC            ; N: then, IRQ 3
        inc     IRQ3_Count
        jmp     SHORT init14_9

init14_8:
        mov     [si].ci_irq, COM1_VEC            ; assume COM1 IRQ
        inc     IRQ4_Count

init14_9:
        cmp     Kernel_Type, ABIOS_COM             ; is it MCA machine ?
        jne     gimnab
        mov     [si].ci_int_sharing,INT_SHARING    ; mark the interrupt as
        mov     [si].ci_mult_COMs_IRQ,INT_SHARING  ;      sharable

gimnab: mov     ax,[si].ci_port         ; is it set already?
        cmp     ax,0
        jne     gimdun                  ; yes
                                        ; no
        mov     [si].ci_port,dx         ; save port address
gimdun: clc
        pop     ds
        pop     es
        ret
gimerr: stc
        pop     ds
        pop     es
        ret
EndProc GetISAMCAConfig

;***
;*** ChipDetect - Find out UART type (8250,16450,16550,16550A)
;***    DX = Base port of UART
;***    SI = ComInfo Structure
;***    (the 16550 is only used in 16450 mode because the FIFO is bad)
;***
Procedure ChipDetect, NEAR
        push    ds
        setDS   DSEG
        .errnz  R_FIFOC - R_INTID

        ; check for 16550A (FIFO)
        add     dx,R_FIFOC              ; (dx) -> FIFO control register
        mov     al,FF_CLEAR_RX OR FF_CLEAR_TX
        mout    dx,al                   ; try to turn off the 16550A FIFOs

                                        ; Have we been overrided to NONFIFO
        test    [si].ci_flagx,FX_16450  ; operation by the command line?
        jnz     cd16450
        test    [si].ci_flagx,FX_16550A
        jnz     cdifdi

        mov     al,FF_ENABLE
        mout    dx,al                   ; try to turn on the 16550A FIFOs
        min     al,dx                   ; (al) = interrupt id
        and     al,II_16550A
        cmp     al,II_16550A
        jne     cd16450                 ; NO 16550A FIFO; go check for 16450

        or      [si].ci_flagx,FX_16550A ; chip type = 16550A
cdifdi:
        and     [si].ci_dcb_flags3,NOT F3_FIFO_ALL ; FIFO auto priority override
        or      [si].ci_dcb_flags3,F3_FIFO_ON OR F3_RX_8 OR F3_TX_16 ;

        xor     al,al
        mout    dx,al                   ; disable the 16550 FIFOs

        jmp     short cdend

cd16450:; check for 16450 (or 16550) by looking for the scratch register
        ; that is only on the 16450, 16550 and 16550A (not on 8250x).
        MAGIC   EQU     10101100b
        and     [si].ci_flagx,NOT FX_16450      ;clear command line flag

        xor     al,al
        mout    dx,al                   ; disable the 16550 FIFOs

        add     dx,R_SCRATCH-R_FIFOC    ; (dx) -> scratch register
        mov     al,MAGIC
        mout    dx,al                   ; (scratch) = MAGIC
        min     al,dx                   ; (al) = (scratch)
        cmp     al,MAGIC
        jne     cdend                   ; NO scratch register; must be 8250

        or      [si].ci_flagx,FX_16450  ; set type flag to 16450
cdend:  pop     ds
        ret
EndProc ChipDetect

Procedure CheckIRQ, NEAR
        ; AX=Port index to check
        ; SI=ComInfo structure
        ; Returns CARRY if can't hook interrupt

        push    ds
        push    es
        push    dx
        setDS   DSEG
        setES   DSEG
        shl     ax,1                            ; create word offset
        mov     bx,ax                           ; set up index reg.
        mov     ax,IntRoutines[bx]              ; grab the offset

        xor     bx,bx
        mov     bl,[si].ci_irq                  ; (bx) = interrupt to be set
        mov     dh,[si].ci_int_sharing          ; 0 on ISA, var. on EISA
        mov     dl,DevHlp_SetIRQ                ; devhlp request code

        ; Do fancy segment swapping so DevHlp is called with DS set to
        ; driver's resident data segment.
        setDS   HSEG                            ; DS = driver's resident data seg
        call    es:[DevHlp]

        jc      cidun                           ; didn't get IRQ.. return

        setDS   DSEG                            ; restore DS
        xor     bh,bh
        mov     bl,[si].ci_irq
        mov     dl,DevHlp_UnSetIRQ

        ; Do fancy segment swapping so DevHlp is called with DS set to
        ; driver's resident data segment.
        setDS   HSEG                            ; DS = driver's resident data seg
        call    es:[DevHlp]
cidun:
        pop     dx
        pop     es
        pop     ds
        ret
EndProc CheckIRQ

;*** QuietPort - Make sure port doesn't bother us
;***    SI = ComInfo Structure
;***
;***
Procedure QuietPort, NEAR
        setDS   DSEG
        mov     dx,[si].ci_port         ; get port address
        add     dx,R_MODMC              ; (dx) -> modem control reg.
        xor     al,al                   ; (al) = 0
        mout    dx,al                   ; lower all modem control lines

        add     dx,R_LINEC-R_MODMC      ; (dx) -> line control reg.
        in      al,dx                   ; (al) = line control register
        and     al,NOT LC_BREAK         ; turn off break
        mout    dx,al                   ; send to port
        ret
EndProc   QuietPort

Procedure GetGDTSels, NEAR
   ASSUME cs:RSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        push    es
        push    di
        push    ax
        push    cx

        push    ds
        pop     es
        lea     di,[si].ci_GDTSelRead           ; allocate 2 GDT selectors
        mov     cx,2                            ; starting at ci_GDTSelRead
        mov     dx,DevHlp_AllocGDTSelector

        call    [DevHlp]

        pop     cx
        pop     ax
        pop     di
        pop     es

        ret
EndProc GetGDTSels


; defines for paramter parsing
SPACECHAR       equ ' '
LEFTPAREN       equ '('
RIGHTPAREN      equ ')'
COMMACHAR       equ ','
SLASHCHAR       equ '/'
ZEROCHAR        equ '0'
NULLCHAR        equ 0
ONECHAR         equ '1'
FOURCHAR        equ '4'
NINECHAR        equ '9'
CAPACHAR        equ 'A'
LOWACHAR        equ 'a'
CAPFCHAR        equ 'F'
LOWFCHAR        equ 'f'
CAPICHAR        equ 'I'
LOWICHAR        equ 'i'
CAPPCHAR        equ 'P'
LOWPCHAR        equ 'p'
CAPDCHAR        equ 'D'
LOWDCHAR        equ 'd'
CAPGCHAR        equ 'G'
LOWGCHAR        equ 'g'

;**     ParseCommandLine
;
;       ENTRY   es:[bx] points to command line of the form:
;
;               device=...\com.sys (COM#,PORT@,IRQ#,BADINT,FIFO)
;               where - COM# is the com port number (1-4)
;                       PORT@ is the base port address
;                       IRQ# is the IRQ to use for this port number
;                       BADINT says what to do for bad interrupts
;                       (i-ignore, d-disable port, p-post to application)
;                       FIFO says if this is a FIFO port or not (f)
;
;               EXAMPLE: (3,3220,5,d,f) is the definition for coma port 3
;                       which has a base address of 3220 on IRQ 5.  This
;                       port should be disabled if more than 1000 bad
;                       interrupts are received and the chip supports FIFO.
;
;       EXIT    all variable set according to input parms
;               CF set for error, CF clear for no error
;
;       USES    ax, bx, cx, dx
;               DS is DSEG on exit
;

Procedure ParseCommandLine,NEAR
   ASSUME cs:RSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        setDS   DSEG
        push    si

EatNonBlanks:
        mov     al,es:[bx]
        cmp     al,SPACECHAR                    ; Scan til SPACE CHAR
        je      blank
        cmp     al,NULLCHAR                     ; ASCIIZ string terminator?
        lje     ParseEnd
        inc     bx
        jmp     short EatNonBlanks

blank:                                          ; eat blanks
        inc     bx
        mov     al,es:[bx]
        cmp     al,SPACECHAR                    ; Scan til no more SPACE
        je      blank

        cmp     al,NULLCHAR                     ; ASCIIZ string terminator?
        lje     ParseEnd

        cmp     al,SLASHCHAR
        jne     normal

        inc     bx
        mov     al,es:[bx]
        cmp     al,CAPGCHAR
        jne     check_lowG
        jmp     enableGAS

check_lowG:
        cmp     al,LOWGCHAR
        jne     normal

enableGAS:

        inc     GAS_Switch

normal:
        cmp     al,LEFTPAREN                    ; Check if it is '('
        ljne    ignore                          ; no, ignore

        inc     bx

        mov     al,es:[bx]
        cmp     al,ONECHAR                      ; if less than '1' ignore
        ljl     ignore
        cmp     al,FOURCHAR                     ; if greater than '4' ignore
        ljg     ignore

        sub     al,ZEROCHAR                     ; convert char to number

public store_port_num
store_port_num:

        dec      al
        mov      cl, al
        mov      si, OFFSET DSEG:FirstCom
Loop1:
        cmp      cl, 0
        jz       OutOfLoop1
        add      si, SIZE ComInfo
        dec      cl
        jmp      Loop1
OutOfLoop1:
        ; Now si should be the cominfo we are parsing

        mov     [si].ci_port_number,al

        inc     bx                              ; advance the pointer

        mov     al,es:[bx]
        cmp     al,COMMACHAR                    ; Check if the char is ','
        ljne    ignore                          ; if not, ignore

        mov     cx,5
        xor     dx,dx
port_addr:
        inc     bx                              ; advance the pointer
        xor     ah,ah
        mov     al,es:[bx]                      ; get next char
        cmp     al,COMMACHAR                    ; Check if the char is ','
        jne     cont_addr                       ; no continue

        cmp     cx,2
        ljg     ignore                          ; if addr is less than 3 char
                                                ; yes, too short for COM addr

        inc     bx                              ; Advance the pointer
        jmp     check_port_addr                 ; process the irq level

cont_addr:
        cmp     al,ZEROCHAR                     ; if less than '0' ignore
        ljl     ignore                          ;
        cmp     al,NINECHAR                     ; if greater than '9' check
        jg      alphabetic                      ; alphabetic
        sub     al,ZEROCHAR                     ; convert char '0'-'9' to num
        jmp     set_addr                        ;

alphabetic:                                     ;
        cmp     al,CAPACHAR                     ; if less than 'A' ignore
        ljl     ignore                          ;
        cmp     al,CAPFCHAR                     ; if greater than 'F' check
        jg      lowercase                       ; lower case alphabetic
                                                ;
        sub     al,CAPACHAR-10                  ;
        jmp     set_addr                        ; convert char 'A'-'F' to num
                                                ;
lowercase:
        cmp     al,LOWACHAR                     ; if less than 'a' ignore
        ljl     ignore                          ;
        cmp     al,LOWFCHAR                     ; if greater than 'f' ignore
        ljg     ignore                          ;
                                                ;
        sub     al,LOWACHAR-10                  ; convert char 'a'-'f' to num

set_addr:                                       ;
        shl     dx,4
        or      dx,ax                           ;
                                                ;
next_pos:
        loop    port_addr

check_port_addr:

        cmp     dx,0
        lje     ParseEndError

;--- Check if this is a real existing port ---
;
;       push    dx
;       add     dx,R_LINEC
;       min     al,dx                           ; Get LCR
;       or      al,LC_DLAB                      ; Turn on DLAB
;       mout    dx,al
;       min     al,dx                           ; Get LCR
;       pop     dx
;
;       test    al,LC_DLAB
;       ljz     ignore                          ; DLAB is still off then error
;
;       push    dx
;       add     dx,R_LINEC
;       min     al,dx                           ; Get LCR
;       and     al,NOT LC_DLAB                  ; Turn off DLAB
;       mout    dx,al
;       min     al,dx                           ; Get LCR
;       pop     dx
;
;       test    al,LC_DLAB                      ; Is DLAB off?
;       ljnz    ignore                          ; DLAB is still on, then error
;
;--- End of COM port check ---

store_port_addr:
        mov     [si].ci_port,dx

irq_num:
        xor     dx,dx
        mov     al,es:[bx]
        cmp     al,ZEROCHAR                     ; if less than '0' ignore
        ljl     ignore
        cmp     al,NINECHAR                     ; if greater than '9' ignore
        ljg     ignore

        inc     bx
        mov     al,es:[bx]

        cmp     al,RIGHTPAREN                   ; if the char ')'
        je      single_dig_irq

        cmp     al,COMMACHAR                    ; if the char ','
        je      single_dig_irq

        cmp     al,ZEROCHAR                     ; if less than '0' ignore
        ljl     ignore
        cmp     al,NINECHAR                     ; if greater than '9' ignore
        ljg     ignore

double_dig_irq:

        sub     al,ZEROCHAR                     ; convert char to number

        add     dl,al

        dec     bx
        mov     al,es:[bx]

        cmp     al,ONECHAR                      ; tens digit must be '1'
        ljne    ignore

        add     dl,10

        mov     [si].ci_irq,dl
        inc     bx
spagetti:
        cmp     dl,3                            ; PCMCIA Support code
        jne     irq_pcmcia1
        inc     IRQ3_Count
        jmp     SHORT irq_pcmcia2
irq_pcmcia1:
        cmp     dl,4
        jne     irq_pcmcia2
        inc     IRQ4_Count
irq_pcmcia2:
        jmp     end_irq_num

single_dig_irq:
        dec     bx
        mov     al,es:[bx]

        sub     al,ZEROCHAR                     ; convert char to number
        mov     [si].ci_irq,al
        mov     dl,al
        jmp     SHORT spagetti

end_irq_num:

        inc     bx                              ; Advance the pointer
        mov     al,es:[bx]

        cmp     al,RIGHTPAREN                   ; if the char ')'
        jne     int_ctrl                        ; no, int_ctrl
        jmp     blank

int_ctrl:

        cmp     al,COMMACHAR                    ; Check if the char is ','
        jne     ignore                          ; if not, ignore

        inc     bx                              ; Advance the pointer
        mov     al,es:[bx]
        cmp     al,CAPICHAR                     ; 'I'
        je      ignore_error_int
        cmp     al,LOWICHAR                     ; 'i'
        je      ignore_error_int
        cmp     al,CAPPCHAR                     ; 'P'
        je      post_error_int
        cmp     al,LOWPCHAR                     ; 'p'
        je      post_error_int
        cmp     al,CAPDCHAR                     ; 'D'
        je      deinst
        cmp     al,LOWDCHAR                     ; 'd'
        je      deinst

        cmp     al,RIGHTPAREN                   ; if the char ')'
        je      BackUpOne                       ; yes, use default

        cmp     al,COMMACHAR                    ; Check if the char is ','
        jne     deinst                          ; if not, use default (deinst)

BackUpOne:                                      ;
        dec     bx                              ; if ',' or ')' then use default
                                                ; interrupt control (deinst)
                                                ; and get ready for FIFO

deinst:
        mov     [si].ci_badmax,DEINSTALL
        jmp     end_int_ctrl

ignore_error_int:
        mov     [si].ci_badmax,IGNORE_ERROR
        jmp     end_int_ctrl

post_error_int:
        mov     [si].ci_badmax,POST_ERROR
        jmp     end_int_ctrl

end_int_ctrl:
        inc     bx                              ; Advance the pointer
        mov     al,es:[bx]

        cmp     al,RIGHTPAREN                   ; if the char ')'
        jne     uart_type                       ; no, ignore
        jmp     blank

uart_type:

        cmp     al,COMMACHAR                    ; Check if the char is ','
        jne     ignore                          ; if not, ignore

        inc     bx                              ; Advance the pointer
        mov     al,es:[bx]

        cmp     al,CAPFCHAR                     ; 'F'
        je      fifo_uart
        cmp     al,LOWFCHAR                     ; 'f'
        je      fifo_uart

        or      [si].ci_flagx,FX_16450          ; Non FIFO Uart

        jmp     end_uart_type
fifo_uart:

        or      [si].ci_flagx,FX_16550A         ; Non FIFO Uart

        inc     bx                              ; Advance the pointer
        mov     al,es:[bx]                      ; get char after the f/F

end_uart_type:
        cmp     al,RIGHTPAREN                   ; if the char ')'
        jne     ignore                          ; no, ignore
        jmp     blank

ignore:
        mov     [si].ci_port_number,0
        jmp     EatNonBlanks

ParseEndError:
        pop     si
        stc
        ret

ParseEnd:
        pop     si
        clc
        ret

EndProc ParseCommandLine


ifdef PERFVIEW
;**     Initialize_PerfView_Data                          ;@PVW
;                                                         ;@PVW
;       Register the Perfview Timers/Counters for the     ;@PVW
;       COM port with ComInfo pointed to by DS:SI         ;@PVW
;                                                         ;@PVW
;       ENTRY   DS:SI -> This port's ComInfo data area    ;@PVW
;                                                         ;@PVW
;       EXIT    nothing                                   ;@PVW
;                                                         ;@PVW
;       USES    NONE                                      ;@PVW
                                                          ;@PVW
Procedure Initialize_PerfView_Data                    ;@PVW
.386                                                  ;@PVW
        SaveReg <si, edi, ax, bx, ecx>                ;@PVW
                                                      ;@PVW
;************                                         ;@PVW
;****   Register Data & Text Blocks w/. PerfView      ;@PVW
;************                                         ;@PVW
                                                      ;@PVW
        lea   ax, [si].ci_Data_Block                  ;@PVW ;OFF of data block
                                                      ;@PVW
        xor   edi, edi                                ;@PVW
        lea   di, [si].ci_SW_Overruns                 ;@PVW ;get length of
        sub   di, ax                                  ;@PVW ;data block
        add   di, 4                                   ;@PVW
        mov   [si].ci_Data_Block.dbh_ulTotLen, edi    ;@PVW
                                                      ;@PVW
        mov   ecx, RPC_FL_16BIT OR RPC_FL_DD          ;@PVW
        mov   [si].ci_Data_Block.dbh_flFlags, ecx     ;@PVW ;set flags
        mov   [si].ci_Data_Block.dbh_ulSem,0          ;@PVW ;initialize sema4
                                                      ;@PVW
        ; AX has offset of data block                 ;@PVW
        mov   si, DS                                  ;@PVW ;SEG of data block
        mov   di, SEG    Text_Block                   ;@PVW ;SEG of text block
        mov   bx, OFFSET Text_Block                   ;@PVW ;OFF of text block
        ; CX has Flags                                ;@PVW

        push  ds                             ;@PVW Should not have      ????
        mov   ds,di                          ;@PVW to do this!          ????
        mov   word ptr ds:[bx].tbh_bidID,0   ;@PVW Steve Best is looking????
        mov   word ptr ds:[bx].tbh_bidID+2,0 ;@PVW into this            ????
        pop   ds                             ;@PVW                      ????
                                                      ;@PVW
        mov     dl,DevHlp_RegisterPerfCtrs            ;@PVW
        call    [DevHlp]                              ;@PVW
                                                      ;@PVW
        RestoreReg <ecx, bx, ax, edi, si>             ;@PVW
                                                      ;@PVW
        ret                                           ;@PVW
                                                      ;@PVW
EndProc   Initialize_PerfView_Data                    ;@PVW

endif         ;PERFVIEW

RSEG    ENDS

END

