flat assembler
Message board for the users of flat assembler.

Index > DOS > X2B -- hex string to binary string

Author
Thread Post new topic Reply to topic
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 20 Sep 2016, 04:24
So when working on my (extremely simple) HEX program, it was grabbing hex (supporting uppercase only) using a bunch of comparisons/jumps, which I think is correct but banal and inefficient. (Although I swear jumps aren't nearly as bad as people claim, but minimizing them isn't the worst idea either.)

I had an older (naive, pathetic) number base converter program that had some mini-routine (cryptic) hex conversion that I must've gleaned from someone else ("cmp al,40h", "adc al,69h", "daa"). It does work, but again, uppercase only (so I have to massage input first).

So, in that line of thinking, I built up this simple example to test it. I was trying to find a (slightly) "better" way ... although there is no (obvious to me, yet) "perfect" way.

The validate routine is because it's too annoying having to be precise to avoid invalid chars. (Even avoiding extra blanks is practically mandatory.) Plus, only accepting uppercase is confusing (esp. without errorchecking! any input translated to incorrect output, without warning, isn't helpful!). Building the table at runtime is because it felt braindead to supply 256 bytes of bloat just for this (esp. when the whole program is smaller than that).

The two versions of writebin weren't necessary, but I felt the old, naive way was too ad hoc and sloppy. (Simple testing doesn't show much speed difference, roughly only 2% improvement, so it's not worth worrying. Same with size, it matters little because it's all wasting 512 bytes minimum anyways. But I left it for completeness.)

And no, in the .ASM I didn't bother mashing multiple instructions together (like my shorter hexdump variant) because, although the lines here are criminally short, it's harder to understand the program otherwise.

x2b.asm :
Code:
; X2B.ASM -- hex to binary converter
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"

VALIDATE=1


ARGV=81h
CR=13
LF=10
PUTC=2
WRITESTR=9

DOS equ int 21h
GOODBYE equ int 20h

b equ byte
w equ word

; .text
org 100h

Komenco:
  mov di,ARGV
  cmp b[di],CR
  jz Fino
  xor ch,ch
  mov cl,b[di-1]
  inc cl
.skiplead:
  mov al,' '
  push cx
  rep scasb
  pop cx
  dec di
  cmp b[di],CR
  jz Fino
  mov si,di
.skiptrail:
  mov bx,cx
  lea di,[bx+ARGV-2]
  std
  rep scasb
  cld
  mov b[di+2],CR

.begin:

if defined VALIDATE
  call validate
end if

  call readhex
  call writebin
Fino:
  GOODBYE


if defined VALIDATE
validate:
  push si
  xor bh,bh

  mov ax,CR shl 8 + CR
  mov cx,256/2
  mov di,table
  rep stosw

  mov cx,10
  mov ax,'00'
  call fill

  mov cx,6
  mov ax,'AA'
  call fill

  mov cx,6
  mov ax,'Aa'
  call fill

  mov bx,table
  pop si
  ret


fill:
  mov bl,ah
  lea di,[table+bx]
.char:
  stosb
  inc al
  loop .char
  ret
end if


readhex:
  mov di,value
  xor dx,dx
.nibble:
  lodsb

if defined VALIDATE
  xlatb
end if

  cmp al,CR
  jz .bye
  cmp al,40h
  adc al,69h
  daa
  cmp al,10
  cmc
  sbb cl,cl
  and cl,6
  sub al,cl

repeat 4
  rcl w[di],1
  rcl w[di+2],1
end repeat

  xor ah,ah
  add w[di],ax
  adc w[di+2],0

  inc dx
  cmp dx,8
  jnz .nibble

.bye:
  ret


writebin:
if defined OLD

  mov ah,PUTC
  mov cx,32
.bit:
  mov dl,'0'
  shl w[di+2],1
  adc dl,0
  shl w[di],1
  adc w[di+2],0
  DOS
  loop .bit
  mov dl,CR
  DOS
  mov dl,LF
  DOS
  ret

else

  mov cx,32
  push cx di
  mov si,binstr
  mov di,si
  mov al,'0'
  rep stosb
  mov ax,LF shl 8 + CR
  stosw
  mov b[di],'$'
  pop di cx
.bit:
  lodsb
  shl w[di+2],1
  adc b[si-1],0
  shl w[di],1
  adc w[di+2],0
  loop .bit
.print:
  mov ah,WRITESTR
  mov dx,binstr
  DOS
  ret

end if


; .data
value dd 0

; .bss
binstr rb 32+2+1
table rb 256

; EOF
    


I also (weakly) tried to verify the correctness with some REXX scripts:

x2btest.rex :
Code:
/* REXX */

parse version v ; say v ; say
parse arg num ; if num='' then num=512
say 'Random iterations:' num ; say
call time 'r'
do num; call binary ; end
endtime=format(time(e),,2)
md5sum '*.out'
say ; say 'Elapsed time:' endtime 'secs.'
exit

binary: procedure
  str=''
  do random(1,8) ; str = str || d2x(random(0,15)) ; end
  call lineout 'rexx.out',copies('0',32-length(x2b(str)))x2b(str)
  'x2b.com' str '>>asm.out'
  return
    


x2btest2.rex :
Code:
/* REXX */

say ; parse version v ; say v ; say
parse arg list.0 ; if list.0='' then list.0=512
say 'Random iterations:' list.0 ; say
do i=1 to list.0;list.i=hexstr();end; say '...done generating...'
call timeit 'x2b' ; call timeit 'old'
md5sum '*.out' ; say
exit

timeit:
  parse arg me ; call time 'r'
  do i=1 to list.0 ; me'.com' list.i'>>'me'.out' ; end
  say me'.com = elapsed:' format(time(e),,2) 'secs.'
  return

hexstr: procedure
  str='' ; do random(1,8) ; str = str || d2x(random(0,15)) ; end
  return str
    
Post 20 Sep 2016, 04:24
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 23 Sep 2016, 00:01
So the opposite of X2B is B2X (binary to hex). This should be simpler to implement.

N.B. The "perfect" way I was talking about was trying to avoid (too many, unnecessary) jumps. I have no idea if it would really be "faster" (or even smaller), even compared to boring XLATB, but it still seemed wise to attempt (see VALIDHEX.ASM far below).

Even though I didn't really use/need them here, I now have a newfound appreciation for (386+) SETcc, BT, SHLD, etc.

Code:
; B2X.ASM -- binary string to hex string converter
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"

ARGV=81h
CR=13
LF=10
PUTC=2

DOS equ int 21h
GOODBYE equ int 20h

b equ byte
w equ word

org 100h

Komenco:
  mov di,ARGV
  cmp b[di],CR
  jz Fino
  xor ch,ch
  mov cl,b[di-1]
  inc cx
.skiplead:
  mov al,' '
  push cx
  rep scasb
  pop cx
  dec di
  cmp b[di],CR
  jz Fino
  mov si,di
.skiptrail:
  mov bx,cx
  lea di,[bx+ARGV-2]
  std
  rep scasb
  cld
  mov b[di+2],CR
.begin:
  call readbin
  call writehex
Fino:
  GOODBYE


readbin:
  mov cx,32
  xor dx,dx
  xor bx,bx
.bit:
  lodsb

if defined USE_QUOTE
  cmp al,''''
  jz .bit
end if

  cmp al,'1'+1
  cmc
  sbb di,di

  cmp al,'0'
  push si
  sbb si,si
  or di,si
  pop si

  test di,di
  jnz .bye

  shr al,1
  rcl bx,1
  rcl dx,1

  loop .bit
.bye:
  ret

writehex:
  mov ax,dx
  or ax,bx
  test ax,ax
  jz .bye

  xchg ax,dx
  call hexword
  xchg ax,bx
  call hexword

  mov al,CR
  call putchar
  mov al,LF
  call putchar
.bye:
  ret

hexword:
  mov cx,404h
.nibble:
  rol ax,cl
  push ax
  and al,0Fh
  cmp al,10
  sbb al,69h
  das
  call putchar
  pop ax
  dec ch
  jnz .nibble
  ret

putchar:
  push ax
  mov ah,PUTC
  mov dl,al
  DOS
  pop ax
  ret

; EOF
    


validhex.asm :
Code:
PUTC=2

DOS equ int 21h
GOODBYE equ int 20h

macro AAM16 {
  mov ah,al
  and al,15
  mov cl,4
  shr ah,cl
}

macro NIB2ASC {
  cmp al,10
  sbb al,105
  das
}

org 100h

Komenco:
  mov cx,256 ; max chars (0-255)
  xor ax,ax  ; start at '\0' (NUL)
.isvalid:
  push ax cx
  call validate
  pop cx ax
  inc ax
  loop .isvalid
Fino:
  GOODBYE

validate:
; IF (k IN ['0'..'9']) AND (k IN ['a'..'f']) AND (k IN ['A'..'F'])

; mov cx,('9' shl 8) + '0'
  mov cx,'09'
  push cx
  call rangecheck
  sbb bx,bx
  push bx

  mov cx,'af'
  push cx
  call rangecheck
  sbb bx,bx
  push bx

  mov cx,'AF'
  push cx
  call rangecheck
  sbb bx,bx
.combine:
  pop cx
  and bx,cx
  pop cx
  and bx,cx
  test bx,bx
  jz outhex
  ret

rangecheck: ; in: (upper_limit shl 8) + lower_limit
  pop bp
  pop bx    ; mov bx,[sp+2]
  push bp
.check:
; int3
  cmp al,bl
  sbb ch,ch
  inc bh
  cmp al,bh
  cmc
  sbb bl,bl
  or bl,ch
  cmp bl,1  ; set CF if BL == 0
  cmc       ; return NC if AL within valid range
  ret

outhex:
; int3
  mov cx,'!~' ; also don't print space, aka isgraph()
  push cx
  call rangecheck
  jnc .show
.split:
; int3
  AAM16
.showhex:
  xchg ah,al
  NIB2ASC
  call putchar
  mov al,ah
  NIB2ASC
.show:
  call putchar
  mov al,' '
  call putchar
.bye:
  ret

putchar:
  push ax
  mov ah,PUTC
  mov dl,al
  DOS
  pop ax
  ret
    


b2xtest.rex :
Code:
/* REXX */

maxhexstr=8 ; maxbinstr=32
parse version v ; say v ; say
parse arg num ; if num='' then num=512
say 'Random iterations:' num ; say
call time 'r'
do i=1 to num
  if /* (i // 1000)=0 */ right(i,3) == '000' then call charout ,'.'
  call binary
end
endtime=format(time(e),,2)
md5sum '*.out'
say ; say 'Elapsed time:' endtime 'secs.'
exit

binary: procedure expose maxhexstr maxbinstr
  do until str \= 0
    str='' ; do random(1,maxbinstr) ; str = str || random(0,1) ; end
  end
  call lineout 'rexx.out',copies('0',maxhexstr-length(b2x(str)))b2x(str)
  'b2x.com' str '>>asm.out'
  return
    
Post 23 Sep 2016, 00:01
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 24 Sep 2016, 22:11
Code:
; X2D.ASM -- hex string to decimal string converter
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"

;COMMAS=1
VALIDATE=1


ARGV=81h
CR=13
LF=10
PUTC=2

DOS equ int 21h
GOODBYE equ int 20h

b equ byte
w equ word

; .text
org 100h

Komenco:
  mov di,ARGV
  cmp b[di],CR
  jz Fino
  xor ch,ch
  mov cl,b[di-1]
  inc cx
.skiplead:
  mov al,' '
  push cx
  rep scasb
  pop cx
  dec di
  cmp b[di],CR
  jz Fino
  mov si,di
.skiptrail:
  mov bx,cx
  lea di,[bx+ARGV-2]
  std
  rep scasb
  cld
  mov b[di+2],CR

.begin:

if defined VALIDATE
  call validate
end if

  call readhex
  call writedec
Fino:
  GOODBYE


if defined VALIDATE
validate:
  push si

  xor bh,bh
  mov ax,CR shl 8 + CR
  mov cx,256/2

  mov di,table
  push di

  rep stosw

  mov cl,10
  mov ax,'00'
  call fill

  mov cl,6
  mov ax,'AA'
  call fill

  mov cl,6
  mov ax,'Aa'
  call fill

  pop bx ; table

  pop si
  ret

fill:
  mov bl,ah
  lea di,[table+bx]
.char:
  stosb
  inc al
  loop .char
  ret
end if

readhex:
  mov di,value
  xor dx,dx
.nibble:
  lodsb

if defined VALIDATE
  xlatb
end if

  cmp al,CR
  jz .bye
  cmp al,40h
  adc al,69h
  daa
  cmp al,10
  cmc
  sbb cl,cl
  and cl,6
  sub al,cl

repeat 4
  rcl w[di],1
  rcl w[di+2],1
end repeat

  xor ah,ah
  add w[di],ax
  adc w[di+2],0

  inc dx
  cmp dx,8
  jnz .nibble
.bye:
  ret

bin2dec:
  mov cx,10
  mov si,decstr
  xor di,di
.start:
  xchg ax,dx
  push dx
  xor dx,dx
  div cx
  xchg ax,bx
  pop ax
  div cx
  xchg dx,bx
  or bl,'0'
  mov b[si],bl
  inc di
  cmp di,3
  jb .no_comma
if defined COMMAS
  inc si
  mov b[si],','
  xor di,di
end if
.no_comma:
  inc si
  test dx,dx
  jnz .start
  test ax,ax
  jnz .start
  dec si
  cmp b[si],','
  jz .bye
  inc si
.bye:
  mov b[si],al
  dec si
  ret

writedec:
  mov dx,w[di+2]
  mov ax,w[di]
  call bin2dec
  lea cx,[si-decstr+1]
  std
.load:
  lodsb
  xchg ax,dx
  mov ah,PUTC
  DOS
  loop .load
  cld
  mov dl,CR
  DOS
  mov dl,LF
  DOS
  ret

; .data
value dd 0

; .bss
decstr rb 10+3
table rb 256

; EOF
    


x2dtest.rex :
Code:
/* REXX */

maxhexstr=8 ; maxdecstr=10 ; numeric digits maxdecstr
parse version v ; say v ; say
parse arg num ; if num='' then num=512
say 'Random iterations:' num ; say
call time 'r'
do i=1 to num
  if /* (i // 1000)=0 */ right(i,3)=='000' then call charout ,'.'
  call decimal
end
endtime=format(time(e),,2)
md5sum '*.out'
say ; say 'Elapsed time:' endtime 'secs.'
exit

decimal: procedure expose maxhexstr maxdecstr
  str='';do random(1,maxhexstr);str=str || d2x(random(0,15));end
  call lineout 'rexx.out',x2d(str)
  'x2d.com' str '>>asm.out'
  return
    
Post 24 Sep 2016, 22:11
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 25 Sep 2016, 21:07
Code:
; D2X.ASM -- decimal string to hex string converter
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"


;COMMA=1


ARGV=81h
CR=13
LF=10
PUTC=2

DOS equ int 21h
GOODBYE equ int 20h

b equ byte
w equ word

macro MUL10 {
repeat 2        ; x4
  shl ax,1
  rcl dx,1
end repeat

  add ax,[di]   ; add original x1 (= x5)
  adc dx,[di+2]

  shl ax,1      ; x2
  rcl dx,1
                ; = x10
}

org 100h

Komenco:
  mov di,ARGV
  cmp b[di],CR
  jz Fino
  xor ch,ch
  mov cl,b[di-1]
  inc cx
.skiplead:
  mov al,' '
  push cx
  rep scasb
  pop cx
  dec di
  cmp b[di],CR
  jz Fino
  mov si,di
.skiptrail:
  mov bx,cx
  lea di,[bx+ARGV-2]
  std
  rep scasb
  cld
  mov b[di+2],CR
.begin:
  call readdec
  call writehex
Fino:
  GOODBYE


readdec:
  mov cx,10
  xor dx,dx
.bit:
  lodsb

if defined COMMA
  cmp al,','
  jz .bit
end if

  cmp al,'0'
  sbb di,di

  cmp al,'9'+1
  cmc
  push si
  sbb si,si
  or di,si
  pop si

  test di,di
  jnz .bye

  xor ah,ah
  mov di,value
  aaa
  xchg ax,bx
  mov ax,[di]
  mov dx,[di+2]

  MUL10
  add ax,bx
  adc dx,0

  stosw
  xchg ax,dx
  stosw

  loop .bit
.bye:
  ret

writehex:
  mov ax,w[value+2]
  call hexword
  mov ax,w[value]
  call hexword

  mov al,CR
  call putchar
  mov al,LF
  jmp putchar
.bye:
  ret

hexword:
  mov cx,404h
.nibble:
  rol ax,cl
  push ax
  and al,0Fh
  cmp al,10
  sbb al,105
  das
  call putchar
  pop ax
  dec ch
  jnz .nibble
  ret

putchar:
  push ax
  mov ah,PUTC
  mov dl,al
  DOS
  pop ax
  ret

value dd 0

; EOF
    


d2xtest.rex :
Code:
/* REXX */

maxhexstr=8 ; maxdecstr=10 ; numeric digits maxdecstr
parse version v ; say v ; say
parse arg num ; if num='' then num=512
say 'Random iterations:' num ; say
call time 'r'
do i=1 to num
  if /* (i // 1000)=0 */ right(i,3) == '000' then call charout ,'.'
  call number
end
endtime=format(time(e),,2)
md5sum '*.out'
say ; say 'Elapsed time:' endtime 'secs.'
exit

number: procedure expose maxhexstr maxdecstr
  do until str <= 2**32-1
    str='';do random(1,maxdecstr);str=str || random(0,maxdecstr-1);end
  end
  call lineout 'rexx.out',copies('0',maxhexstr-length(d2x(str)))d2x(str)
  'd2x.com' str '>>asm.out'
  return
    
Post 25 Sep 2016, 21:07
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.