;   >>> this is file LOADLINI.ASM
;============================================================================
;   LOADLIN v1.6 (C) 1994..1996 Hans Lermen (lermen@elserv.ffm.fgan.de)
;
;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation; either version 2 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;----------------------------------------------------------------------------
;   Comments and bug reports are welcome and may be sent to:
;   E-Mail:    lermen@elserv.ffm.fgan.de
;   SnailMail: Hans Lermen
;              Am Muehlenweg 38
;              D53424 REMAGEN-Unkelbach
;              GERMANY
;
;============================================================================

relocate_setup_code proc near
; NOTE: This routine has to be updated whenever a new access to
;       High_seg or High_addr is inserted in the code !
;       It relocates those addresses, if the setup code is
;       not loaded at 9000h
;
         push_  eax,bx
         mov    have_relocated_setup,1
                ; these can simply overwriten
         mov    ax,High_seg
         mov    reloc_hseg_1,ax
         mov    reloc_hseg_2,ax

@@relocd macro  desc
         lea    bx,desc
         call   relocate_descriptor
         endm
                ; these descriptors have to get added the displacement
         @@relocd gdt_code
         @@relocd gdt_data
         @@relocd gdt_ldt
         @@relocd gdt_tss

                ; these DWORDS have to get added the displacement
         sub    ax,High_seg_
         movsx  eax,ax
         shl    eax,4
         add    our_CR3,eax
         add    our_GDTRptr,eax
         add    our_IDTRptr,eax
         add    laddr_GDT,eax
         add    laddr_IDT,eax
         add    pagedir_template,eax
         call   preset_pagedir_from_template

         pop_   eax,bx
         ret
relocate_setup_code endp


relocate_descriptor proc near
; input:
;   AX  =  new 'High_seg' frame
;   BX  =  offset of descriptor
;          bases address of descriptor must be relative to 'High_seg_'
; outpout:
;   all registers preserved, descriptor bases updated
         push_  ecx,eax
         mov    ch,[bx].base24
         mov    cl,[bx].base16
         shl    ecx,16
         mov    cx,[bx].base0
         sub    ax,High_seg_
         movsx  eax,ax
         shl    eax,4
         add    eax,ecx
         mov    [bx].base0,ax
         shr    eax,16
         mov    [bx].base16,al
         mov    [bx].base24,ah
         pop_   ecx,eax
         ret
relocate_descriptor endp


read_comline_from_file proc near
;;        mov      ax,DOS_OPEN_FILE shl 8
;;        lea      dx,image_name+1
;;        DosInt
        push     fs
        call     @@open
        jnc      @@isopen
@@err:
        lea      dx,err_comfile_tx
        call     print
        mov      word ptr comline-1,0
        jmp      @@ex
@@ioerr:
;;        DosCall  DOS_CLOSE_FILE
        call     @@close
        jmp      @@err
@@isopen:
        mov      bx,ax
        cld
        lea      di,comline+1
@@loop:
        mov      dx,di
        mov      cx,1
;;        DosCall  DOS_READ_FROM_HANDLE
        call     @@read
        jc       @@ioerr
        cmp      ax,1
        jne      @@eof
        mov      al,byte ptr [di]
        cmp      al,13
        je       @@loop
        cmp      al,'#'
        je       @@skipeol
@@skipcontinue:
        inc      di
        cmp      di, offset comline_end-1
        jnb      @@eof
        cmp      al,' '
        ja       @@loop
        cmp      di,offset comline+2
        jna      @@1
        cmp      byte ptr [di-2],' '  ; ignore multiple spaces
        jne      @@1
        dec      di
        jmp      @@loop
@@1:
        mov      byte ptr [di-1],' '
        jmp      @@loop
@@eof:
        mov      byte ptr [di],0
        mov      ax,di
        sub      ax,offset comline+1
        xchg     al,ah
        mov      word ptr comline-1,ax
;;        DosCall  DOS_CLOSE_FILE
        call     @@close
        jmp      @@ex
@@skipeol:
        mov      dx,di
        mov      cx,1
;;        DosCall  DOS_READ_FROM_HANDLE
        call     @@read
        jc       @@ioerr
        cmp      ax,1
        jne      @@eof
        cmp      byte ptr [di],10
        jne      @@skipeol
        jmp      @@skipcontinue

@@ex:
        pop      fs
        ret

; -----------

@@virtual db     0

@@open:                    ; virtual open routine
        cmp      dword ptr image_name,'ol@@'
        jne      @@open_d
        cmp      dword ptr image_name+4,'ilda'
        je      @@open_v
@@open_d:
        mov      @@virtual,0
        lea      dx,image_name+1
        mov      ax,DOS_OPEN_FILE shl 8
        DosInt
        ret
@@open_v:
        mov      @@virtual,1
        mov      ax,High_Seg
        cmp      dword ptr image_name+4+4,'@@xn'
        jne      @@open_vhex
        mov      fs,ax
        lea      ax,params_from_loadlinX
        clc
        ret
@@open_vhex:
        push     si
        lea      si,image_name+4+2
        mov      word ptr [si],'x0'
        call     value_of
        mov      fs,ax
        xor      ax,ax ; carry is also 0
        pop      si
        ret

@@close:
        cmp      @@virtual,0
        jz       @@close_d
        ret
@@close_d:
        DosCall  DOS_CLOSE_FILE
        ret
@@read:
        cmp      @@virtual,0
        jz       @@read_d
        mov      al, byte ptr fs:[bx]
        or       al,al
        jz       @@read_eof
        inc      bx
        mov      byte ptr [di],al
        mov      ax,1
        clc
        ret
@@read_eof:
        xor      ax,ax
        clc
        ret
@@read_d:
        DosCall  DOS_READ_FROM_HANDLE
        ret

read_comline_from_file endp

build_arglist proc near
; input:
;   ES=DS=CS= seg of all pointers
;   SI      = pointing to commandline like string
;   DI      = target buffer
; output:
;   DI      = pointing to next free byte in target buffer
;   all other registers preserved
@@stack struc
  pushA_struc @@
@@stack ends
        pusha
        mov     bp,sp
        xor     cx,cx
@@1:
        inc     cx
        call    get_token
        jnz     @@1

        mov     di,[bp].@@di
        mov     bx,di
        inc     cx
        shl     cx,1
        add     di,cx
        mov     si,[bp].@@si
@@2:
        mov     word ptr [bx],di
        inc     bx
        inc     bx
        call    get_token
        mov     di,ax
        jnz     @@2
        mov     word ptr [bx-2],0
        mov     [bp].@@di,ax
        popa
        ret
build_arglist endp

handle_response_file proc near
IF 0
        call    read_comline_from_file
ELSE
        pushad
        cld
        lea     di,aligned_auxbuff
        mov     @@list,di
        call    build_arglist     ; we save the rest of the old commandline
        mov     @@ptr,di
        call    read_comline_from_file
        cmp     word ptr aligned_auxbuff,0 ; nothing left in the old ?
        jz      @@ex
        lea     si,comline+1
        mov     @@cptr,si
        cld
        mov     di,@@ptr
        call    build_arglist      ;we scan the read-in commandline
                         ; now we check if there are overwrites
        mov     si,word ptr aligned_auxbuff
        mov     @@list,si
        mov     dx,@@ptr
        mov     si,dx
        mov     si,word ptr [si]
        or      si,si
        jz      @@ex_0
                ; check if we have to replace the image
        lea     di,@@image
        mov     bx,@@list ; string list of old args
        call    check_token
        js      @@2
        mov     bx,word ptr aligned_auxbuff[bx]
        mov     byte ptr [bx],1    ; mark as 'used'
        cmp     byte ptr [bx+5],0
        je      @@2
        cmp     byte ptr [bx+6],0
        je      @@2
        lea     si,[bx+6]    ; take what is behind 'image='
        jmp     @@2
@@1:
        mov     si,dx
        mov     si,word ptr [si]
        or      si,si
        jz      @@ex_0
                           ; we check for the special case 'ro','rw'
        cmp     byte ptr [si+2],0
        jne     @@1_3
        cmp     word ptr [si],'or'
        jne     @@1_1
        lea     di,@@rw    ; search the opposite (rw replaces ro)
        jmp     @@1_2
@@1_1:
        cmp     word ptr [si],'wr'
        jne     @@1_3
        lea     di,@@ro    ; search the opposite (ro replaces rw)
@@1_2:
        mov     bx,@@list ; string list of old args
        call    check_token
        jmp     @@1_4
@@1_3:
        mov     di,si
        mov     bx,@@list ; string list of old args
        call    check_token
@@1_4:
        js      @@2
                ; we have to take the old one
        mov     si,word ptr aligned_auxbuff[bx]
@@2:
        push    si
        mov     di,@@cptr
        call    stringcpy
        pop     si
        mov     @@cptr,di
        mov     byte ptr [di-1],' '
        add     dx,2
        cmp     si,@@list       ; avoid overwriting our constants
        jb      @@1
        mov     byte ptr [si],1
        jmp     @@1
@@ex_0:
                ; we have to take the rest of the old commandline
        lea     bx,aligned_auxbuff ; string list of old args
@@ex_1:
        mov     si,word ptr [bx]
        add     bx,2
        or      si,si
        jz      @@ex_2
        cmp     byte ptr [si],1
        je      @@ex_1       ; skip, what we already replaced
        mov     di,@@cptr
        call    stringcpy
        mov     @@cptr,di
        mov     byte ptr [di-1],' '
        jmp     @@ex_1
@@ex_2:
        mov     bx,@@cptr
        mov     byte ptr [bx-1],0
        sub     bx, offset comline+1
        jz      @@ex_5
        dec     bx
@@ex_5:
        xchg    bh,bl
        mov     word ptr comline-1,bx
@@ex:
        popad
        ret
@@ptr   dw      0
@@list  dw      0
@@cptr  dw      0
@@image db      'image=',0
@@rw    db      'rw',0
@@ro    db      'ro',0
ENDIF
handle_response_file endp


check_token proc near
; input:
;   ES=DS=CS= seg of all pointers
;   DI = pointing to token
;   BX = pointing to string-table, terminated by a ZERO-string
; output:
;   AX = -1, no match found
;      =  0, found a matching string, BX = index*2 of that string
;      =  1, same as AX=0, but token was terminated by '='
;   DI =  pointing to token behind terminator (0 or '=')
;
        push_   cx,dx
        cld
        xor     dx,dx
        jmp     @@loop_entry
@@loop:
        pop     di
        inc     dx
        xchg    bx,di
        xor     ax,ax
        mov     cx,-1
        repnz scasb       ; skip to next string in table
        xchg    bx,di
        cmp     byte ptr [bx],0
        jz      @@not_found
@@loop_entry:
        push    di
        mov     cx,size aux_token
        call    name_compare
        jz      @@loop
        pop     ax     ; clean up stack
        xchg    dx,bx  ; get index
        shl     bx,1
        xor     ax,ax
        cmp     byte ptr [di-1],'='
        jne     @@ex
        inc     ax
@@ex:
        pop_    cx,dx
        test    ax,ax
        ret
@@not_found:
        mov     ax,-1
        jmp     @@ex
check_token endp

get_setup_version proc near
        mov      cs:setup_version,0
        cmp      dword ptr ds:setup_header_sign,SIGNATURE
        jne      @@ex
        push     word ptr cs:setup_header_version
        pop      cs:setup_version
@@ex:
        ret
get_setup_version endp


parscommandline proc near
        mov     image_name,0
parscommandline_:
        call    clear_to_default
        mov     cx,word ptr comline-1
        xchg    ch,cl
        jcxz    @@ex_carry
        mov     si,cx
        mov     comline[si+1],0
        mov     command_line,0
        mov     cl_pointer,offset command_line
        lea     si,comline+1
        cld
        cmp     image_name,0
        jnz     @@1
        lea     di,image_name
        call    get_token   ; get the zImage-file name
        jz      @@ex_carry

        cmp     image_name,'-'
        jne     @@0
        mov     dword ptr image_name,'amiz'
        mov     word ptr image_name+4,'eg'
        mov     image_name+6,0
        jmp     parscommandline_
@@0:
                            ; got zImage-name
                            ; try to get params
        cmp     image_name,'@'
        jne     @@1
        call    handle_response_file
        jmp     parscommandline
@@1:
        lea     di,image_name
        call    tolower
        jmp     @@next_token

@@token_table label byte
              db    'ramdisk',0
              db    'vga',0
              db    'mem',0
              db    'root',0
              db    'ro',0
              db    'rw',0
              db    'no387',0
              db    'single',0
              db    'auto',0
              db    '-v',0
              db    '-t',0
              db    '-d',0
              db    '-rb',0
              db    '-rx',0
              db    '-ja',0
              db    '-clone',0
;              db    '-oldxd',0
              db    'debug',0
              db    'no-hlt',0
              db    'reserve',0
              db    'hd',0
              db    'bmouse',0
              db    'max_scsi_luns',0
              db    'xd',0
              db    '-n',0
              db    '-txmode',0
              db    '-f',0
              db    'initrd',0
              db    '-noheap',0
              db    '-wait',0
              db    '-dskreset',0
              db    0


@@jmp_table   dw  @@ramdisk
              dw  @@vga
              dw  @@mem      ;@@tolower  ; mem
              dw  @@tolower  ; root
              dw  @@tolower  ; ro
              dw  @@tolower  ; rw
              dw  @@tolower  ; no387
              dw  @@tolower  ; single
              dw  @@tolower  ; auto
              dw  @@option_v
              dw  @@option_t
              dw  @@option_d
              dw  @@option_realbios
              dw  @@option_rx
              dw  @@option_ja
              dw  @@option_clone
;              dw  @@option_oldxd
              dw  @@tolower  ; debug
              dw  @@tolower  ; no-hlt
              dw  @@tolower  ; reserve=
              dw  @@tolower  ; hd=
              dw  @@tolower  ; bmouse=
              dw  @@tolower  ; max_scsi_luns=
              dw  @@tolower  ; xd=
              dw  @@option_n
              dw  @@option_txmode
              dw  @@option_force
              dw  @@option_initrd
              dw  @@option_noheap
              dw  @@option_wait
              dw  @@option_dskreset

@@next_token:
        lea     di,aux_token
        call    get_token
        jz      @@ex0
        lea     di,aux_token
        lea     bx,@@token_table
        call    check_token
        js      @@not_my_token
        jmp     @@jmp_table[bx]

@@mem:
        jz      @@not_my_token
        push    di
        lea     di,aux_token
        call    tolower
        pop     di
        cmp     byte ptr [di],'n'  ; = 'nopentium'
        je      @@not_my_token
        xchg    si,di
        call    value_of
        call    adjust_k_or_m
        xchg    si,di
        mov     end_of_physmem,eax
        jmp     @@not_my_token

@@option_wait:
        jz      @@not_my_token
        push    di
        lea     di,aux_token
        call    tolower
        pop     di
        xchg    si,di
        call    value_of
        xchg    si,di
        mov     option_wait,ax
        jmp     @@next_token

@@option_initrd:
        jz      @@not_my_token
        push_   si,di
        xchg    si,di
        lea     di,rdimage_name
        call    stringcpy
        pop_    si,di
        mov     option_initrd,1
        jmp     @@next_token



@@ramdisk:      ; have ramdisk param
        mov     got_ram_disk,1
        mov     new_ram_disk,1440
        jz      @@next_token
        xchg    si,di
        call    value_of
        cmp     word ptr [si],'on' ; disable diskchange prompt ?
        jne     @@ramdisk_1
        mov     option_nodiskprompt,1
@@ramdisk_1:
        xchg    si,di
        mov     new_ram_disk,ax
        jmp     @@next_token

@@token_table_vga label byte
              db    'normal',0
              db    'extended',0
              db    'ask',0
              db    0

@@vga:          ; have vga - param
        mov     got_vga_mode,1
        mov     new_vga_mode,-1  ; default = NORMAL
        jz      @@next_token
        mov     bx,di
        cmp     byte ptr [bx],'-'
        jne     @@vga_1
        inc     bx
@@vga_1:
        cmp     byte ptr [bx],'0'
        jb      @@check_vga_sym
        cmp     byte ptr [bx],'9'
        jna     @@check_vga_num
@@check_vga_sym:
        ; DI already pointing to value
        lea     bx,@@token_table_vga
        call    check_token
        js      @@next_token
        shr     bx,1
        not     bx
        mov     new_vga_mode,bx
        jmp     @@next_token
@@check_vga_num:
        xchg    si,di
        call    value_of
        xchg    si,di
        mov     new_vga_mode,ax
        jmp     @@next_token

@@option_t:
        mov     option_t,1     ; forces also option -v
@@option_v:
        mov     option_v,1
        jmp     @@next_token
@@option_noheap:
        mov     option_noheap,1
        jmp     @@next_token

@@option_d:
        lea     di,aux_token   ; get output file name
        call    get_token
        jz      @@option_t     ; no file name
        push_   bx,cx,dx
        xor     cx,cx
        lea     dx,aux_token
        DosCall DOS_CREATE_FILE
        pop_    bx,cx,dx
        jc      @@option_t
        mov     debug_file_handle,ax
        jmp     @@option_t

@@option_realbios:
        mov     option_realbios,1
        jmp     @@next_token
@@option_rx:
        mov     option_rx,1
        jmp     @@next_token
@@option_ja:
        mov     option_ja,1
        jmp     @@next_token
@@option_clone:
        mov     option_clone,1
        dec     token_count
        jmp     @@next_token
@@option_n:
        mov     option_n,1
        jmp     @@next_token
@@option_force:
        mov     option_force,1
        jmp     @@next_token
@@option_dskreset:
        mov     option_dskreset,1
        jmp     @@next_token

@@option_txmode:
        pusha
        push_   ds,es
        xor     ax,ax
        mov     es,ax
        mov     ax,3003h ; al = color mode
        and     ah,byte ptr es:[0410h] ; get EQUIPPEMENT-flags
        cmp     ah,30h
        jne     @@option_txmode6
        mov     al,7     ; al = mono mode
@@option_txmode6:
        xor     ah,ah
        INT     10h
        pop_    ds,es
        popa
        jmp     @@next_token


@@not_my_token:
        push    si
        lea     si,aux_token
        call    @@append_to_commandline
        pop     si
        jmp     @@next_token
@@tolower:
        lea     di,aux_token
        call    tolower
        jmp     @@not_my_token



@@ex0:
        lea     si,@@boot_tx
        call    @@append_to_commandline
        dec     cl_pointer
        lea     si,image_name
        cld
        mov     di,si
@@ex1:
        lodsb
        test    al,al
        jz      @@ex3
        cmp     al,':'
        je      @@ex2
        cmp     al,'\'
        je      @@ex2
        cmp     al,'/'
        jne     @@ex1
@@ex2:
        mov     di,si
        jmp     @@ex1
@@ex3:
        mov     si,di
        call    @@append_to_commandline
@@ex:
        clc
        ret
@@ex_carry:
        stc
        ret

@@append_to_commandline:
        mov     di,cl_pointer
@@append_to_1:
        lodsb
        stosb
        test    al,al
        jnz     @@append_to_1
        mov     byte ptr [di-1],' '
        mov     byte ptr [di],al
        mov     cl_pointer,di
        ret

@@boot_tx     db    'BOOT_IMAGE=',0

parscommandline endp


handle_kernel_specifics proc near
IF 0
        cmp     kernelversion,0
        jz      @@ex
; put here kernel dependent stuff
@@ex:
ENDIF
        ret
handle_kernel_specifics endp

get_token proc near
; returns:
;   ZFLAG = 1 on EOF
;   AX = increased DI
        push    di
@@skip_loop:
        lodsb
        cmp     al,' '
        je      @@skip_loop
        jmp     @@ok
@@loop:
        lodsb
        cmp     al,' '
        jne     @@ok
        xor     al,al
@@ok:
        stosb
        test    al,al
        jne     @@loop
        dec     si
        mov     ax,di
        pop     di
        cmp     byte ptr es:[di],0
        jz      @@ex
        inc     token_count
@@ex:
        test    byte ptr es:[di],255
        ret
get_token endp


readstring proc near
        push    dx
        mov     comline-1,255-2
        lea     dx,comline-1
        DosCall DOS_BUFFERED_INPUT
        lea     dx,newline_tx
        call    print
        push    bx
        movzx   bx,comline
        mov     comline[bx+1],0
        mov     comline-1,0
        pop     bx
        pop     dx
        ret
readstring endp


stringcpy proc near
; input:
;   DS:SI= source address
;   DS:DI= target address
        push_    ds,ds
        pop     es
        cld
@@loop:
        lodsb
        stosb
        or      al,al
        jnz     @@loop
@@ex:
        pop     ds
        ret
stringcpy endp


tolower proc near
; input:
;   DS:DI= address to ASCIIZ-string
        cld
        xchg    si,di
@@loop:
        lodsb
        or      al,al
        jz      @@ex
        cmp     al,'A'        ; tolower !
        jb      @@loop
        cmp     al,'Z'
        ja      @@loop
        add     byte ptr [si-1],'a'-'A'
        jmp     @@loop
@@ex:
        xchg    si,di
        ret
tolower endp

name_compare proc near
; input:
;   DS:DI= address to ASCIIZ-string  (name1)
;          (a "=" marks end of string also)
;   CS:BX= address to ASCIIZ-string  (name2)
;          (a "=" marks end of string also)
;   CX=    max number of character to compare
;   compare is done by ignoring upper/lower-case
; Output:
;   AX=    0 if not equal else equal
;   DI=    as changed by lodsb, pointing behind terminator (0 or '=')
;   all other register preserved
          cld
          push    bx
          xchg    si,di
          sub     bx,si
@@m3:
          lodsb
          cmp     al,'='        ; may be name in environement
          jne     short @@32
          xor     al,al
          jmp     short @@m35
@@32:
          cmp     al,'A'        ; tolower !
          jb      short @@m35
          cmp     al,'Z'
          ja      short @@m35
          add     al,'a'-'A'
@@m35:
          mov     ah,al
          mov     al,byte ptr cs:[si+bx-1]
          cmp     al,'='        ; may be name in environement
          jne     short @@34
          xor     al,al
          jmp     short @@m36
@@34:
          cmp     al,'A'        ; tolower !
          jb      short @@m36
          cmp     al,'Z'
          ja      short @@m36
          add     al,'a'-'A'
@@m36:
          or      al,al
          jz      short @@ex_
          or      ah,ah
          jz      short @@exfalse
          cmp     ah,al
          jne     @@exfalse
          loop    @@m3
@@extrue:
          mov     ax,1
@@ex:
          xchg    si,di
          pop     bx
          or      ax,ax
          ret
@@ex_:
          or      ax,ax
          jz      @@extrue
@@exfalse:
          xor     ax,ax         ;  not equal
          jmp     @@ex
name_compare endp


get_env_variable proc near
; Input
;    CS:BX= Pointer to string (ASCIIZ) of desired env-variable
; Output:
;    ES:DI=    pointer to string (ASCIIZ) of variable contents (without "=")
;            or ZERO-string if not found
;    All other registers preserved
          push    ds
          push    ax
          push    si
          mov     ds,PSP_frame
          mov     ds,ds:PSP_envir_frame
          xor     si,si

          mov     al,byte ptr [si]
@@m1:
          mov     cx,80
          or      al,al
          mov     di,si
          jz      @@ex
          call    name_compare
          jnz     @@found    ; string is equal, found the variable
          cld
@@m2:                   ; skip to next string
          lodsb
          or      al,al
          jnz     @@m2
          mov     al,byte ptr [si]
          jmp     @@m1
@@found:
@@ex:
          push    ds
          pop     es
          pop     si
          pop     ax
          pop     ds
          ret
get_env_variable endp


adjust_k_or_m proc near
  ; Input:
  ;   DS:SI, EAX as delivered by 'value_of'
          pushf
          push    cx
          xor     cx,cx
          mov     ch, byte ptr [si-1]
          cmp     ch,'k'
          jne     @@1
          mov     cl,10
@@ex:
          shl     eax,cl
@@ex0:
          pop     cx
          popf
          ret
@@1:
          cmp     ch,'m'
          jne     @@ex0
          mov     cl,20
          jmp     @@ex
adjust_k_or_m endp

value_of proc near
  ; Input:
  ;   DS:SI= pointer to string (ASCIIZ), which contains number to be converted
  ;          (i.e.: 1234, 0xa00, ... ), all non-digit-characters (0..9,a..f,A..F)
  ;          are "delimiters" and mark the end of the string
  ; Output:
  ;   EAX= converted number
  ;   SI= pointing at the first byte behind the delimiter
          cmp    byte ptr [si],'-'
          pushf
          jnz    @@m0
          inc    si
  @@m0:
          push_  ebx,ecx,edx,edi,esi
          mov    ebx,10
          xor    edx,edx
          xor    edi,edi
          inc    edi
          cld
          lodsb
          call   @@check_white
          jc     @@ex
          jne    @@m1
          lodsb
          or     al,20h   ; convert to lowercase
          cmp    al,'x'
          jne    @@m1
          mov    ebx,16
          lodsb
@@m1:
          call   @@check_white
          jb     @@ex
          xor    ecx,ecx
@@m2:
          inc    ecx
          lodsb
          call   @@check_white
          jnc    @@m2
          std
          mov    [esp],esi ;advance pointer
          dec    si
          dec    si
@@m3:
          lodsb
          or     al,20h    ; convert to lowercase
          cmp    al,'9'
          jna    short @@m4
          add    al,9
@@m4:
          and    al,0fh
          movzx  eax,al
          imul   eax,edi
          imul   edi,ebx
          add    edx,eax
          loop   @@m3
@@ex:
          mov    eax,edx
          pop_   ebx,ecx,edx,edi,esi
          popf
          jnz    @@ex_
          neg    eax
@@ex_:
          ret

@@check_white:
          mov    ah,'f'
          cmp    bl,16
          je     short @@7
          mov    ah,'9'
@@7:
          or     al,20h
          cmp    al,'0'
          jb     short @@9
          cmp    al,ah
          ja     short @@9
          cmp    al,'9'
          jbe    short @@8
          cmp    al,'a'
          jae    short @@8
@@9:
          mov    al,' '
@@8:
          cmp    al,'0'
          ret

value_of endp


