;      /*****************************************************************/
;      /*                                                               */
;      /*           Copyright (c) IBM Corporation  1987, 1992           */
;      /*                     All Rights Reserved                       */
;      /*                                                               */
;      /*****************************************************************/
;      /******************* START OF SPECIFICATIONS *********************/
;      /*                                                               */
;      /*  SOURCE FILE NAME: INIT.ASM                                   */
;      /*                                                               */
;      /*  DESCRIPTIVE NAME: Initialization routine                     */
;      /*                                                               */
;      /*  COPYRIGHT:  IBM Corp. 1992                                   */
;      /*                                                               */
;      /*  STATUS:  Version 1.0                                         */
;      /*                                                               */
;      /*  NOTES: Perform all functions needed to initialize the        */
;      /*         driver. Routines are provided to parse the config.sys */
;      /*         line and to modify the driver name and driver device  */
;      /*         name.                                                 */
;      /*                                                               */
;      /*         Unit 0 variables are provided as follows:             */
;      /*         index 0 = device error count                          */
;      /*         index 1 = panic codes                                 */
;      /*                                                               */
;      /*         These variables can be queried with the pen device    */
;      /*         driver tool. Panic codes are defined in PEN.INC and   */
;      /*         provide problem determination if the driver is not    */
;      /*         functional.                                           */
;      /*                                                               */
;      /*  ENTRY POINTS:                                                */
;      /*      See public statements                                    */
;      /*  EXTERNAL REFERENCES:                                         */
;      /*      See extrn statements                                     */
;      /*                                                               */
;      /******************* END  OF  SPECIFICATIONS *********************/
.xlist
  include pensegs.inc
  include pen.inc
  include penei.inc
  include penidc.inc
  include penioctl.inc
  include devsym.inc
  include devhlp.inc
  include struc.inc
.list

.286p

;------------------------------------------------------------------------------
;  external data references
;------------------------------------------------------------------------------

extrn DDHdr1 : byte
extrn Dev_ErrorCount : dword
extrn Dev_Lev_Major : byte
extrn Dev_Lev_Minor : byte

;------------------------------------------------------------------------------
;  external routines
;------------------------------------------------------------------------------

extrn Dev_Init   : near
extrn Loc_Init   : near
extrn But_Init   : near
extrn Dsp_Init   : near
extrn Tim_Init   : near
extrn Trc_Init   : near
extrn Vmc_Init   : near
extrn Idc_DriverEntryPoint : near
extrn Gio_AddDCB : near
extrn Dsp_callback :near

;------------------------------------------------------------------------------
; local equates
;------------------------------------------------------------------------------

GDTINFOINDEX equ    1

;------------------------------------------------------------------------------
; global data declarations
;------------------------------------------------------------------------------
public Device_Help,RegCaps0

DSEG segment

Device_Help   dd 0 ; Dev helper entry point

public dcb0
  dcb0        DCB_DRV <>      ; driver dcb
public RegCaps0
  RegCaps0    DDCAP   <>      ; device driver registration packet

; define unit 0 unit variables, accessable via query|set unit variable IOCTL

public DDPanic
UVAR_COUNT    equ  2
numUVars DD UVAR_COUNT
UVarss   DD 0                 ; 0= error count
DDPanic  DD PANIC_DDS         ; 1= panic numbers (init to DDS not contact us)


Public GDTInfoSeg
GDTInfoSeg dw  0     ; Save GDT Selector to info seg

DSEG ends

;------------------------------------------------------------------------------
; local init data declarations
;------------------------------------------------------------------------------

DSEGI SEGMENT
  public ENDDATA
  ENDDATA label byte ; used to figure end of init data to free
  deviceName    db  'DRIVER  '    ; device name
  public ParmLine
  ParmLine      dd 0 ; pointer to config.sys line
  public InitPkt
  InitPkt   dd 0
DSEGI ends

CSEGI SEGMENT
  ASSUME    CS:CSEGI, SS:nothing, ES:nothing, DS:DGROUP
  public ENDCODE
  ENDCODE label near  ; used to know where to truncate after INIT command
;------------------------------------------------------------------------------
; OS/2 initialization routine entry point
; es:bx = request packet
;------------------------------------------------------------------------------
public Init_Init
Init_Init Proc  Near

; Get the address to the DevHelp router and save in our DS.

  mov     word ptr InitPkt, bx
  mov     word ptr InitPkt+2, es
  push es
  push bx

.386p
  mov  edx,dword ptr es:[bx.InitpBPB]      ; get config.sys statement pointer
  mov  ParmLine, edx
  mov  edx,dword ptr es:[bx].InitDevHlp
  mov  Device_Help, edx
.286p

; get the info seg

  push es
  push bx
  mov  al, GDTINFOINDEX         ; Request GDT InfoSeg value = 1
  mov  dl, DevHlp_GetDOSVar     ; DevHelp function number
  call Device_Help              ; Invoke GetDOSVar Function
  .if  c
     PANIC PANIC_DEVHLP1
  .else
     mov  es, ax                ; Get the GDT level 0 selector and
     mov  ax, word ptr es:[bx]  ; Overwrite the level 3 Selector
     mov  GDTInfoSeg, ax        ; Save GDT Selector
  .endif
  pop  bx
  pop  es

; add a dcb for the driver to list as unit 0

  lea  bx, dcb0
  call Gio_AddDCB
  call initDCB0
  call Tim_Init

; Set up all devices and add all DCBs

  les  di,ParmLine ; Set ES:DI -> to config.sys
  call Dev_Init
  .if  <zero ax>

; register with penPM device services

     mov  si, offset DDHdr1 + 10       ; driver name
     mov  ax, cs
     mov  bx, offset Idc_DriverEntryPoint
     mov  cx, 2
     mov  di, REG_EXT_IF
     mov  dl, DevHlp_RegisterDeviceClass
     call Device_Help
     .if c
        PANIC PANIC_DEVHLP2
     .endif

; do device type initializations

     CALL_TYPE Loc_Init,DT_LOCATOR
     CALL_TYPE But_Init,DT_BUTTON
     CALL_TYPE Dsp_Init,DT_DISPLAY
     CALL_ALL  Int_ChkNames

; do driver initialazations

     les  di,ParmLine ; Set ES:DI -> to config.sys line
     call Trc_Init
     les  di,ParmLine ; Set ES:DI -> to config.sys line
     call Vmc_Init
     xor  ax,ax
  .endif

; free the init code and data segments

  pop  bx
  pop  es
  mov  byte ptr es:[bx].InitcUnit, 0
  .if  <zero ax>
     mov  word ptr es:[bx].InitpEnd, offset ENDCODE
     mov  word ptr es:[bx].InitpEnd+2, offset ENDDATA
  .else
     mov  es:[bx].PktStatus, ax         ; set error return code
     mov  word ptr es:[bx].InitpEnd, 0
     mov  word ptr es:[bx].InitpEnd+2, 0
     PANIC PANIC_DEVINIT ; This won't show up since the dd will terminate
  .endif
  ret

Init_Init  EndP

;------------------------------------------------------------------------------
; Set up the driver device DCB
;------------------------------------------------------------------------------
public initDCB0
initDCB0 proc
  mov  dcb0.dcb_@RegCaps, offset RegCaps0
  mov  al, dcb0.dcb_Unit
  mov  bx, dcb0.dcb_@regCaps
  mov  [bx].ccap_unit, al
  mov  [bx].ccap_device_type,DT_DRIVER
  mov  [bx].ccap_length, size DDCAP
  mov  al, Dev_Lev_Major
  mov  [bx].ddcap_rev_major,al
  mov  al, Dev_Lev_Minor
  mov  [bx].ddcap_rev_minor,al
  mov  [bx].ddcap_ioc_major,PEN_IOCTL_LEV_MAJOR
  mov  [bx].ddcap_ioc_minor,PEN_IOCTL_LEV_MINOR
  mov  [bx].ddcap_eif_major,PEN_EI_LEV_MAJOR
  mov  [bx].ddcap_eif_minor,PEN_EI_LEV_MINOR
  mov  [bx].ddcap_idc_major,PEN_DDS_LEV_MAJOR
  mov  [bx].ddcap_idc_minor,PEN_DDS_LEV_MINOR

  mov  si, offset DDHdr1 + 10       ; driver name
  lea  di, [bx].ccap_driver_name
  cld
  push ds
  pop  es
  mov  cx,SIZEOF_DRIVER_NAME
  rep  movsb
  mov  si, offset deviceName        ; device name
  lea  di, [bx].ccap_device_name
  mov  cx, SIZEOF_DRIVER_NAME
  rep  movsb

  mov  dcb0.dcb_@UVars,      offset numUVars

  mov  dcb0.dcb_@Dev_quv,    offset unit0_quv
  mov  dcb0.dcb_@Dev_suv,    offset unit0_suv
  mov  dcb0.dcb_@Dev_Start,  offset Null_rtn

  mov  dcb0.dcb_@Dev_CallBack,offset Dsp_callback
  ret
initDCB0 endp

;------------------------------------------------------------------------------
; make sure all names are null terminated
;------------------------------------------------------------------------------
public  Int_ChkNames
Int_ChkNames proc
  mov di,[bx].dcb_@RegCaps
  mov [di].ccap_device_name+SIZEOF_DEVICE_NAME-1,0
  stc ;do all
  ret
Int_ChkNames endp

CSEGI    ENDS

;-- ROUTINES FOR DRIVER DEVICE ------------------------------------------------
;
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; Query unit variable, variable 0 is used to return the number of device errors
;------------------------------------------------------------------------------

CSEG segment
     ASSUME    CS:CGROUP, SS:nothing, ES:nothing, DS:DGROUP

public unit0_quv
unit0_quv proc
 .if <ax eq 0>
    .386p
    mov  edx,Dev_ErrorCount
    .286p
 .endif
 ret
unit0_quv endp

;------------------------------------------------------------------------------
; Query unit variable, variable 0 can be reset to 0
;------------------------------------------------------------------------------
public unit0_suv
unit0_suv proc
 .if <ax eq 0>
    .386p
    mov  Dev_ErrorCount,edx
    .286p
 .endif
 ret
unit0_suv endp

;------------------------------------------------------------------------------
; Avaiable for use
;------------------------------------------------------------------------------
public Null_rtn
Null_rtn proc
  xor ax,ax
  ret
Null_rtn endp

public Null_rtn_far
Null_rtn_far proc far
  stc
  ret
Null_rtn_far endp

CSEG     ENDS

;-- SOME UTILITY ROUTINES FOR INIT TIME ---------------------------------------
;
;------------------------------------------------------------------------------
CSEGI SEGMENT
  ASSUME    CS:CSEGI, SS:nothing, ES:nothing, DS:DGROUP

;------------------------------------------------------------------------------
; Set Driver Name
; si = driver name (8 characters)
;------------------------------------------------------------------------------
public SetDriverName
SetDriverName proc
  cld
  push ds
  pop  es

  push si                       ;fix up device header
  mov  di, offset DDHdr1 + 10
  mov  cx,SIZEOF_DRIVER_NAME
  rep  movsb
  pop  si

  lea  di,RegCaps0.ccap_driver_name    ;fix up dcb 0
  mov  cx,SIZEOF_DRIVER_NAME
  rep  movsb
  ret
SetDriverName endp

;------------------------------------------------------------------------------
; Set Device Name
; si = device name (SIZEOF_DEVICE_NAME characters,null terminated)
;------------------------------------------------------------------------------
public SetDeviceName
SetDeviceName proc
  cld
  push ds
  pop  es

  lea  di,RegCaps0.ccap_device_name    ;fix up dcb 0
  mov  cx,SIZEOF_DEVICE_NAME
  .repeat
     movsb
     .leave <<byte ptr [si]> eq 0>
  .loop
  ret
SetDeviceName endp

;------------------------------------------------------------------------------
; Set IDC Entry point
; di = device dependent IDC entry point
;------------------------------------------------------------------------------
public SetIDCEntry
SetIDCEntry   proc
  mov word ptr DDHdr1 + 8,di
  ret
SetIDCEntry   endp


CR equ 0dh
LF equ 0ah

;*********************** String Compare w/o Case ****************************
;* ENTRY:
;*    ds:si => ASCIIZ string 1 ( master )
;*    es:di => ASCII  string 2 ( to compare )
;* EXIT:
;*    ax    ==  0          strings equal for length of master
;*    ax    == -1  (FFFF)  string 1 less than string 2
;*    ax    ==  1          string 1 greater than string 2
;*   es:[di] => next character after match string if ax == 0
;* USES:
;*    si, di
;*    Direction Flag Cleared
;* DESCRIPTION:
;*    Will compare string 2 to string 1.  Strings must macth over the whole
;*    length of string 1.  If string 2 has characters past the end of string
;*    1, then that is still a match
;****************************************************************************
public str_i_cmp
str_i_cmp proc near

        ; test for end of string 1
sic_loop:
        cmp     byte ptr [si], 0
        jnz     sic_not_end
        mov     ax, 0
        jmp     sic_done

        ; compare the current two characters ( after converting to upper case )
sic_not_end:
        mov     al, es:[di]
        inc     di
        call    to_upper
        mov     ah, al

        mov     al, [si]
        inc     si
        call    to_upper

        sub     al, ah
        je      sic_loop

        ; getting here means string are diffrent
        mov     ax, 1                   ; assume str1 > str2
        jnc     sic_done                ; actually test
        mov     ax, 0FFFFh              ; fix assumtion as it was wrong

sic_done:
        ret
str_i_cmp endp

;**************************** Skip Over Whitespace **************************
;* Function:    Advance to next non-whitespace character
;* Entry:       es:[di] => ascii text
;* Exit:        es:[di] => next non-whitespace or end of string
;* Preserves:   all except di
;****************************************************************************
public skip_white
skip_white proc near
        dec     di

sw_loop:
        inc     di
        call is_white
        jz      sw_loop

        ; not white space, return
        ret
skip_white endp

;************************** Skip To Next Whitespace *************************
;* Function:    Advance to next whitespace character
;* Entry:       es:[di] => ascii text
;* Exit:        es:[di] => next whitespace or end of string
;* Preserves:   all except di
;****************************************************************************
public skip_to_white
skip_to_white proc near
        push    ax

stw_loop:
        mov     al, es:[di]
        cmp     al, ' '
        jz      stw_done
        cmp     al, CR
        jz      stw_done
        cmp     al, LF
        jz      stw_done
        cmp     al, 0
        jz      stw_done
        cmp     al, 09h         ; tab
        jz      stw_done

        ; not white space, advance
        inc     di
        jmp     stw_loop

stw_done:
        pop     ax
        ret
skip_to_white endp

;************************** Is Whitespace? *************************
;* Function:    Test character for whitespace
;* Entry:       es:[di] => ascii text
;* Exit:        zero flag set if whitespace
;* Preserves:   all
;****************************************************************************
public is_white
is_white  proc near
        push    ax

        mov     al, es:[di]
        cmp     al, ' '
        jz      iw_done
        cmp     al, ','
        jz      iw_done
        cmp     al, 09h         ; tab
        jz      iw_done

iw_done:
        pop     ax
        ret
is_white  endp

;************************** Is End of Line? *************************
;* Function:    Test character for end of line character (CR,LF,0)
;* Entry:       es:[di] => ascii text
;* Exit:        zero flag set if end of line
;* Preserves:   all
;****************************************************************************
public is_endln
is_endln  proc near
        push    ax

        mov     al, es:[di]
        cmp     al, CR
        jz      ie_done
        cmp     al, LF
        jz      ie_done
        cmp     al, 0
        jz      ie_done

ie_done:
        pop     ax
        ret
is_endln  endp



;********************** Convert Character to Upper Case *********************
;* ENTRY:
;*    al    = char to convert
;* EXIT:
;*    al    = converted ( if needed) char
;* USES:
;*    none
;****************************************************************************
public to_upper
to_upper proc near
        cmp     al, 'a'
        jb      tu_done
        cmp     al, 'z'
        ja      tu_done
        sub     al, 'a'-'A'
tu_done:
        ret
to_upper endp

;***************************** Get Hex Number *******************************
;* Function:    Parse a hex number from an ascii string
;* Entry:       es:[di] => ascii text
;* Exit:        es:[di] => first non hex character
;*              ax value of number
;* Preserves:   std C
;****************************************************************************
public get_hex_num
get_hex_num proc near

        mov     ax, 0

ghn_loop:
        mov     bl, es:[di]
        cmp     bl, '0'
        jb      ghn_exit
        cmp     bl, '9'
        ja      ghn_tst_let
        sub     bl, '0'
        jmp     ghn_add

ghn_tst_let:
        and     bl, not ('A' xor 'a')   ; convert letter to uppercase
        cmp     bl, 'A'
        jb      ghn_exit
        cmp     bl, 'F'
        ja      ghn_exit
        sub     bl, 'A' - 10            ; bl = ( bl - 'A') + 10

ghn_add:
        mov     cl, 4
        shl     ax, cl
        add     al, bl
        jmp     ghn_adv

ghn_adv:
        inc     di
        jmp     ghn_loop

ghn_exit:
        ret
get_hex_num endp

;***************************** Get Hex Number *******************************
;* Function:    Parse a deciaml number from an ascii string
;* Entry:       es:[di] => ascii text
;* Exit:        es:[di] => first non hex character
;*              ax value of number
;* Preserves:   std C
;****************************************************************************
temp    equ     bp-2
public get_dec_num
get_dec_num proc near
        enter   2,0

        xor     ax,ax
        xor     bx,bx
        mov     word ptr [temp],10

gdn_loop:
        mov     bl, es:[di]
        cmp     bl, '0'
        jb      gdn_exit
        cmp     bl, '9'
        ja      gdn_exit
        sub     bl, '0'

gdn_add:
        imul    word ptr [temp]
        add     ax, bx
        inc     di
        jmp     gdn_loop

gdn_exit:
        leave
        ret
get_dec_num endp

CSEGI    ENDS
         END
