%title 'misc procedures I took from my stdlib; (c) 1998 Henrik Nebrin'

        ideal
        p386
        nowarn  brk
        include 'proc.inc'

csmaxalign      equ     4
csdwordalign    equ     4

signed          equ     0001h           ;eax is a signed number
showsign        equ     0002h           ;always show '+' or '-'
bighex          equ     0004h           ;big hex characters (A-F) (ntoa:proc)

macro   procstart n
proc    n procptr
endm    procstart
macro   procend n
endp    n
endm    procend

        startcode

        ;
        ;   __antob:proc
        ;
        ;   convert an ascii number to a binary number in eax
        ;
        ;   input: es:di zero-terminated string
        ;          bh   base number (010B=bin,010O=oct,010D=dec,010H=hex)
        ;   output: cf=0=AOK=eax=number
        ;           cf=1=BAD NUMBER=eax=00000000
        ;   registers: eax
        ;
procstart       __antob
        public  __antob
        push    edx ebx di
        mov     al,[_es:di]
        test    al,al
        jz      short @@ErrorSetCF
        cmp     al,'-'
        je      short @@incdi
        cmp     al,'+'
        jne     short @@dont
@@incdi:
        inc     di
@@dont:
        xor     eax,eax
        xor     edx,edx
        movzx   ebx,bh                  ;ebx=number base
@@countloop:
        mov     al,[_es:di]
        inc     di
        sub     al,'0'                  ;get binary number
        jb      short @@error???
        cmp     al,0AH
        jb      short @@nothexdigit
        cmp     al,'A'-'0'
        jb      short @@error
        cmp     al,'Z'-'0'
        jbe     short @@AZ
        cmp     al,'a'-'0'
        jb      short @@error
if 01
        ;this is much faster. We'll do the test after the jmp
        sub     al,'a'-('0'+10)
        jmp     short @@nothexdigit
else
        cmp     al,'z'-'0'
        ja      short @@error
        sub     al,'a'-('0'+10)
        jnc     short @@nothexdigit
        jc      short @@error
endif
@@error???:
        cmp     al,0-'0'
        je      short @@loopend
@@error:
        xor     eax,eax
        pop     di
        cmc
        jmp     short @@666
@@AZ:
        sub     al,'A'-('0'+10)
@@nothexdigit:
        cmp     al,bl
        jae     short @@error
        imul    edx,ebx
        add     edx,eax
        jmp     short @@countloop
@@loopend:
        pop     di
        xchg    edx,eax
        cmp     [byte ptr _es:di],'-'
        jne     short @@notneg
        neg     eax
@@notneg:
        clc
@@666:
        pop     ebx edx
        ret
@@ErrorSetCF:
        pop     di
        stc
        jc      short @@666
procend         __antob

        ;
        ;   strlen:proc
        ;
        ;   returns the string's length, excluding the ending zero
        ;
        ;   Input: es:di string to count
        ;   Output: cx  # of bytes in the string
        ;   Registers: cx
        ;
procstart       strlen
ifdef strlenunroll
  loopunroll    equ     (strlenunroll)
else
  loopunroll    equ     3               ;unroll 4 times
endif
        public  strlen
if @datasize
        push    ds
endif
        push    ax di                   ;save'em
        xor     cx,cx                   ;zero count
if @datasize
        push    es
        pop     ds
        assume  ds:nothing
endif
        test    di,001h                 ;word aligned?
        jz      short @@strlen          ;yes
        mov     al,[di]                 ;get odd byte
        inc     di                      ;word align it
        test    al,al                   ;end of string?
        jz      short @@666             ;yes
if @datasize
  if csmaxalign ge 4
        dw      0C181H,0001H            ;add cx,0001
  elseif csmaxalign ge 2
        inc     cl                      ;don't wast any space!
  else
        inc     cx
  endif
else
  if csmaxalign ge 4
        add     cx,0001                 ;dword align and increment count
  else
        inc     cx                      ;word/byte align
  endif
endif
@@strlen:
        index   = 0                     ;index in the unrolled loop
        rept    loopunroll
        mov     ax,[di+index]           ;get two characters
        add     cx,002h                 ;increment counter
        test    al,al                   ;end of string?
        jz      short @@deccxtwice      ;yes
        test    ah,ah                   ;end of string?
        jz      short @@deccxonce       ;yes
        index   = index + 002h          ;next characters to check
        endm
        mov     ax,[di+index]           ;get characters
        index   = index + 002h
        add     di,index                ;increment string
        add     cx,002h                 ;increment count
        test    al,al                   ;end of string?
        jz      short @@deccxtwice      ;yes
        test    ah,ah                   ;end of string?
        jnz     short @@strlen          ;yes
@@deccxonce:
        inc     cx                      ;save cx
@@deccxtwice:
        sub     cx,002h                 ;decrement twice
@@666:
        pop     di ax
if @datasize
        pop     ds
        assume  ds:@data
endif
        ret                             ;and say bye bye
procend         strlen

        ;
        ;   ahtob:proc
        ;
        ;   convert an ascii hexadecimal number to a binary number in eax
        ;
        ;   input: es:di zero-terminated string
        ;   output: cf=0=eax is the converted number
        ;           cf=1=eax is zero (es:di=not hexadecimal number)
        ;   registers: eax
        ;
procstart       ahtob
        public  ahtob
        push    bx
        mov     bh,010H
        call    __antob
        pop     bx
        ret
procend         ahtob

        ;
        ;   antob:proc
        ;
        ;   convert an ascii number to a binary number in eax
        ;
        ;   input: es:di zero-terminated string. The string may have one
        ;                assembly base character as the last character
        ;                ('h' or 'H' = hex,etc). Default base is decimal
        ;   output: cf=0=eax is the converted number
        ;           cf=1=eax is zero (es:di=not decimal number)
        ;   registers: eax
        ;
procstart       antob
        public  antob
        push    cx bx
        call    strlen                  ;get es:di's length in cx
        jcxz    @@Error
        dec     cx
        mov     bx,cx                   ;put it in bx
if @codesize
        dw      06866H
        dd      @@666
else
        push    offset @@666
endif
        mov     al,[_es:bx+di]          ;get last character
        mov     [byte ptr _es:bx+di],00 ;assume al is NOT an ascii digit
        mov     bl,al
        or      al,020H                 ;lowercase
if 0;@codesize
  ;all stdlib.lib procedures are in the same segment now
  __antob__     equ     short @@__antobjmp
else
  __antob__     equ     __antob
endif
        mov     bh,010H
        cmp     al,'h'                  ;hexadecimal number?
        je      __antob__
        mov     bh,010B
        cmp     al,'b'                  ;binary number?
        je      __antob__
        mov     bh,010O
        cmp     al,'o'                  ;octal number?
        je      __antob__
        cmp     al,'q'
        je      __antob__
        cmp     al,'d'                  ;decimal number?
        je      short @@decimal
        ;al is not 'd','b','h',etc; it must've been an ascii digit
        mov     al,bl                   ;restore original char after lowercase
        mov     bx,cx
        mov     [_es:bx+di],al          ;restore last character
        mov     bl,al
@@decimal:
        mov     bh,010D                 ;default base is decmial,base 10
if 0;@codesize
@@__antobjmp:
endif
        jmp     __antob
@@666:
        xchg    bx,cx
        mov     [_es:bx+di],cl          ;restore the string
@@popretn:
        pop     bx cx
        ret
@@Error:
        xor     eax,eax
        cmc
        jc      short @@popretn
procend         antob

        ;
        ;   __ntoa:proc
        ;
        ;   convert a number to ascii
        ;
        ;   input: eax  number to convert to ascii
        ;          cx   minimum # of digits to print
        ;          bl   flags (see equates)
        ;          bh   base (010b=binary,010o=octal,010d=decimal,010h=hex)
        ;          es:di string to convert it to
        ;   output: es:di string with number in (NOT zero terminated!!!)
        ;           eax zero
        ;           cx  # of digits the string has (incl. '+' or '-')
        ;   registers: eax,cx
        ;
        align   csdwordalign
procstart       __ntoa
        public  __ntoa
if @datasize
        push    ds
endif
        push    edx ebp si di
if @datasize
        push    es
        pop     ds
        assume  ds:nothing
endif
        test    bl,signed
        jz      short @@10
        test    eax,eax
        jns     short @@10
        neg     eax                     ;make it positive
        mov     [byte ptr di],'-'
        jmp     short @@incdi
@@10:
        test    bl,showsign
        jz      short @@20
        mov     [byte ptr di],'+'
@@incdi:
        inc     di
@@20:
        test    cx,cx
        jnz     short @@30
        inc     cx
@@30:
        xor     si,si                   ;number count
        movzx   ebp,bh                  ;ebp=base
        db      66h
@@pushloop:
        xor     dx,dx                   ;the remainder (edx) cannot be > FFh
        div     ebp                     ; since ebp is max FFh. edx[31:16]=0000
        push    dx
        inc     si
        test    eax,eax
        jnz     short @@pushloop

        mov     dx,si
        mov     bp,cx
        sub     cx,si                   ;count # of zeros to fill ds:di with
        jbe     short @@40
        mov     dx,bp
        mov     al,'0'
        rep stosb
@@40:
        mov     cx,dx                   ;restore cx
        mov     dx,('0' shl 8) or ('a'-('0'+10))
        test    bl,bighex
        jz      short @@no
        mov     dl,'A'-('0'+10)
@@no:
@@poploop:
        pop     ax
        add     al,dh
        cmp     al,'9'
        jbe     short @@50
        add     al,dl
@@50:
        mov     [di],al
        inc     di
        dec     si
        jg      short @@poploop
@@666:
        pop     di si ebp edx
        mov     al,[di]
        cmp     al,'-'
        je      short @@60
        cmp     al,'+'
        jne     short @@70
@@60:
        inc     cx                      ;we printed a '+' or a '-' too!
@@70:
        xor     eax,eax
if @datasize
        pop     ds
        assume  ds:@data
endif
        ret
        assume  ds:@data
procend         __ntoa

        ;
        ;   strcpy:proc
        ;
        ;   copy a string to another string
        ;
        ;   input: ds:si string to copy
        ;          es:di string to copy to
        ;   output: none
        ;   registers: none
        ;
procstart       strcpy
        public  strcpy
        push    ax cx si di             ;save regs
        ;si is most used in the loop so it's best to word align it
        ;(si is already used twice before the first al=0 test)
ifdef aligndi
        test    di,001h                 ;word align di
else
        test    si,001h                 ;word align si
endif
        jz      short @@strcpy          ;already word aligned
        mov     al,[si]                 ;get character
        inc     si                      ;increment source string
        mov     [_es:di],al             ;copy character (es: if @datasize)
if @datasize
  if csmaxalign eq 2
        dw      0C7FFH                  ;inc di
  else
        inc     di
  endif
else
        inc     di                      ;increment destination string
endif
        test    al,al                   ;end of string?
        jz      short @@666             ;yes
if @datasize
  if csmaxalign ge 4
        add     cx,0                    ;dword align it (because of es: ovr.)
  endif
endif
@@strcpy:
        mov     ax,[si]                 ;get two characters
        mov     cx,[si+002h]            ;get two characters
        test    al,al                   ;end of string?
        jz      short @@copyal          ;yes,copy al to [es:di]
        mov     [_es:di],ax             ;copy two characters
        add     si,004h                 ;advance source string
        test    ah,ah                   ;end of string?
        jz      short @@666             ;yes,exit
        test    cl,cl                   ;end of string
        jz      short @@copycl          ;yes,copy cl to [es:di+002h]
        mov     [_es:di+002h],cx        ;copy two characters
        add     di,004h                 ;advance destination string
        test    ch,ch                   ;end of string
        jnz     short @@strcpy          ;no
@@666:
        pop     di si cx ax             ;restore regs
        ret                             ;bye
@@copyal:
        mov     [_es:di],al             ;zero terminate the string
        pop     di si cx ax             ;restore regs
        ret                             ;bye bye
@@copycl:
        mov     [_es:di+002h],cl        ;zero terminate the string
        pop     di si cx ax             ;restore regs
        ret                             ;good bye
procend         strcpy

        ;
        ;   ntoa:proc
        ;
        ;   convert a number to ascii
        ;
        ;   input: eax  number to convert to ascii
        ;          cx   minimum # of digits to print
        ;          bl   flags (see equates)
        ;          bh   base (010b=binary,010o=octal,010d=decimal,010h=hex)
        ;          es:di string to convert it to
        ;   output: es:di string with number in (zero terminated)
        ;           eax zero
        ;           cx  # of digits the string has (incl. '+' or '-')
        ;   registers: eax,cx
        ;
procstart       ntoa
        public  ntoa
        call    __ntoa
        push    di
        add     di,cx
        mov     [byte ptr _es:di],000H
        pop     di
        ret
procend         ntoa
        endcode

        end
