flat assembler
Message board for the users of flat assembler.

Index > DOS > BYTEFIX -- change a hex byte in a binary file

Author
Thread Post new topic Reply to topic
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 08 Mar 2017, 01:32
Not quite a full hex editor, but it's very small and usable inside .BAT files (automated/scriptable). It tries to check for most common errors, too.

Alas, it could definitely be written "better", but for now this will have to do.

Code:
; BYTEFIX.ASM -- change a hex byte in a binary file
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"


ARGC=80h
ARGV=81h
BYTE1=ARGV+1+8+1
CR=13
FILENAME=ARGV+1+8+1+2+1+2+1
LF=10
MINARGC=15+1

CLOSEFILE=3Eh
GOODBYE=4Ch
OPENFILERW=3D02h
READFILE=3Fh
SEEKSETFILE=4200h
WRITEFILE=40h
WRITESTR=9

DOS equ int 21h

b equ byte
w equ word


; section .text

org 100h ; DOS .COM

Main:
  call fixargs
  cmp b[ARGC],MINARGC
  jae .okay
  mov ah,WRITESTR
  mov dx,usagemsg
  DOS
  mov b[errlvl],255
  jmp Fino
.okay:
  mov si,ARGV+1
  call scanofs
  jc Fino
  call fixbyte
Fino:
  mov ah,GOODBYE
  mov al,b[errlvl]
  DOS
; end Main


; proc
fixargs:
  xor ch,ch
  mov cl,b[ARGC]
  jcxz .ret
  push cx
  call stripblanks
  pop cx
  mov si,ARGV+1
  mov di,si
.loop:
  lodsb
  push cx
  mov bx,'az'
  push bx
  call rangecheck
  pop cx
  sbb bl,bl
  or bl,not ' '
  and al,bl
  stosb
  loop .loop
.ret:
  ret
; endp


; proc
stripblanks:
  mov di,ARGV
  lea si,[di+1]
  inc cx
  mov al,' '
  rep scasb
  dec di
  mov b[ARGC],cl
  xchg si,di
  push cx cx
  rep movsb
  pop cx di
  add di,ARGV
  mov al,' '
  std
  rep scasb
  cld
  inc cx
  mov b[di+2],0 ; NUL (for ASCIIZ filename)
  mov b[ARGC],cl
  ret
; endp


; proc
fixbyte:
  call getbytes
.open:
  mov ax,OPENFILERW
  mov dx,FILENAME
  DOS
  jc .ret
  mov [handle],ax
.seek:
  call seekbyte
  jc .close
.read:
  mov ah,READFILE
  mov bx,[handle]
  mov cx,1
  mov dx,foundbyte
  DOS
  jc .close
.compare:
  mov al,[foundbyte]
  cmp al,b[oldbyte]
  jnz .close
  call seekbyte
.write:
  mov ah,WRITEFILE
  mov bx,[handle]
  mov cx,1
  mov dx,newbyte
  DOS
  jc .close
  mov b[errlvl],0
.close:
  mov ah,CLOSEFILE
  mov bx,[handle]
  DOS
.ret:
  ret
; endp


; proc
seekbyte:
  mov ax,SEEKSETFILE
  mov bx,[handle]
  mov cx,w[ofs+2]
  mov dx,w[ofs]
  DOS
  ret
;endp


; proc
getbytes:
  mov si,BYTE1
  call hexbyte
  mov [oldbyte],al
  call hexbyte
  mov [newbyte],al
  ret
; endp


; proc
hexbyte:
  lodsb
  call ishex
  jc .ret
  call asc2hex
  mov cl,4
  shl al,cl
  mov dl,al
  lodsb
  push dx
  call ishex
  pop dx
  jc .ret
  call asc2hex
  add dl,al
  inc si
  xchg ax,dx
.ret:
  ret
; endp


; proc
scanofs:
  mov cx,8
.load:
  lodsb
  push cx
  call ishex
  pop cx
  jc .ret
  push cx
  call asc2hex
  pop cx
.adjust:
  push ax cx
  mov ax,w[ofs]
  mov dx,w[ofs+2]
  mov cl,4
.shift:
  rcl ax,1
  rcl dx,1
  loop .shift
  pop cx
  mov w[ofs],ax
  mov w[ofs+2],dx
.add:
  pop ax
  xor ah,ah
  add w[ofs],ax
  adc w[ofs+2],0
.continue:
  loop .load
.ret:
  ret
; endp


; proc
asc2hex:
  cmp al,40h
  adc al,69h
  daa
  mov ah,al
  and al,15
  mov cl,4
  shr ah,cl
  aad
  ret
; endp


; proc
ishex:
  mov cx,'09'
  push cx
  call rangecheck
  sbb bh,bh

; mov cx,'af'
; push cx
; call rangecheck
; sbb bl,bl
; and bh,bl

  mov cx,'AF'
  push cx
  call rangecheck
  sbb bl,bl
  and bh,bl

  neg bh
  clc
  lahf
  or ah,bh
  sahf

  ret       ; return CF if not valid hex char
; endp


; proc
rangecheck: ; in: (upper_limit shl 8) + lower_limit
  pop bp
  pop dx    ; mov dx,[sp+2]
  push bp
.check:
  cmp al,dl
  sbb ch,ch
  inc dh
  cmp al,dh
  cmc
  sbb dl,dl
  or dl,ch
  cmp dl,1  ; set CF if DL == 0
  cmc       ; return NC if AL within valid range
  ret
; endp


; section .data

errlvl db 1
handle dw 0
ofs    dd 0
usagemsg db CR,LF,'Usage:  bytefix.com CaFe5150 1a 2b file.dat',CR,LF,'$'


; section .bss

oldbyte rb 1
newbyte rb 1
foundbyte rb 1

; EOF
    
Post 08 Mar 2017, 01:32
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 10 Mar 2017, 20:39
rugxulo wrote:

Alas, it could definitely be written "better", but for now this will have to do.


Code:
--- bytefix.old 2017-03-07 14:13:52 -0600
+++ bytefix.new 2017-03-10 12:19:20 -0600
@@ -40,3 +40,3 @@
   DOS
-  mov b[errlvl],255
+  neg b[errlvl]
   jmp Fino
@@ -48,4 +48,3 @@
 Fino:
-  mov ah,GOODBYE
-  mov al,b[errlvl]
+  mov ax,w[errlvl]
   DOS
@@ -109,2 +108,5 @@
   call getbytes
+.issame:
+  cmp al,[di-2]
+  jz .ret
 .open:
@@ -161,6 +163,7 @@
   mov si,BYTE1
+  mov di,oldbyte
   call hexbyte
-  mov [oldbyte],al
-  call hexbyte
-  mov [newbyte],al
+  stosb
+  call hexbyte ; newbyte
+  stosb
   ret
@@ -206,12 +209,8 @@
   push ax cx
-  mov ax,w[ofs]
-  mov dx,w[ofs+2]
   mov cl,4
 .shift:
-  rcl ax,1
-  rcl dx,1
+  rcl w[ofs],1
+  rcl w[ofs+2],1
   loop .shift
   pop cx
-  mov w[ofs],ax
-  mov w[ofs+2],dx
 .add:
@@ -261,7 +260,3 @@
 
-  neg bh
-  clc
-  lahf
-  or ah,bh
-  sahf
+  shr bh,1
 
@@ -292,3 +287,3 @@
 
-errlvl db 1
+errlvl db 1, GOODBYE
 handle dw 0
    
Post 10 Mar 2017, 20:39
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 16 Mar 2017, 03:13
Made a (very) few fixes and improvements. Is it worth keeping the original version (and/or diff)? Meh, but for now, for completeness, I'll leave 'em "as is".

Code:
; BYTEFIX.ASM -- change a hex byte in a binary file
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"


ARGC=80h
ARGV=81h
BYTE1=ARGV+1+8+1
CR=13
FILENAME=ARGV+1+8+1+2+1+2+1
LF=10
MINARGC=15+1

CLOSEFILE=3Eh
GOODBYE=4Ch
OPENFILERW=3D02h
READFILE=3Fh
SEEKSETFILE=4200h
WRITEFILE=40h
WRITESTR=9

DOS equ int 21h

b equ byte
w equ word

macro proc name {
  if used name
name:
}
endp fix end if


; section .text

org 100h

Main:
  call fixargs
  cmp b[ARGC],MINARGC
  jae .okay
  mov ah,WRITESTR
  mov dx,usagemsg
  DOS
  neg [errlvl]
  jmp Fino
.okay:
  mov si,ARGV+1
  call scanofs
  jc Fino
  call fixbyte
Fino:
  mov ax,w[errlvl]
  DOS
; end Main


proc fixargs
  xor ch,ch
  mov cl,b[ARGC]
  jcxz .ret
  push cx
  call stripblanks
  pop cx
  mov si,ARGV+1
  mov di,si
.loop:
  lodsb
  push cx
  mov bx,'az'
  push bx
  call rangecheck
  pop cx
  sbb bl,bl
  or bl,not ' '
  and al,bl
  stosb
  loop .loop
.ret:
  ret
endp


proc stripblanks
  mov di,ARGV
  lea si,[di+1]
  inc cx
  mov al,' '
  rep scasb
  dec di
  mov b[ARGC],cl
  xchg si,di
  push cx cx
  rep movsb
  pop cx di
  add di,ARGV
  mov al,' '
  std
  rep scasb
  cld
  inc cx
  mov b[di+2],0 ; NUL (for ASCIIZ filename)
  mov b[ARGC],cl
  ret
endp


proc fixbyte
  cmp b[BYTE1],'?'
  jz .open
  call getbytes
  jc .ret
.issame:
  cmp al,[di-2]
  jz .ret
.open:
  mov ax,OPENFILERW
  mov dx,FILENAME
  DOS
  jc .ret
  mov [handle],ax
.seek:
  call seekbyte
  jc .close
.read:
  mov ah,READFILE
  mov bx,[handle]
  mov cx,1
  mov dx,foundbyte
  DOS
  jc .close
.query:
  cmp b[BYTE1],'?'
  jnz .compare
  mov al,[foundbyte]
  mov [errlvl],al
  jmp .close
.compare:
  mov al,[foundbyte]
  cmp al,b[oldbyte]
  jnz .close
  call seekbyte
.write:
  mov ah,WRITEFILE
  mov bx,[handle]
  mov cx,1
  mov dx,newbyte
  DOS
  jc .close
  dec [errlvl]
.close:
  mov ah,CLOSEFILE
  mov bx,[handle]
  DOS
.ret:
  ret
endp


proc seekbyte
  mov ax,SEEKSETFILE
  mov bx,[handle]
  mov cx,w[ofs+2]
  mov dx,w[ofs]
  DOS
  ret
endp


proc getbytes
  mov si,BYTE1
  mov di,oldbyte
  call hexbyte
  jc .ret
  call hexbyte ; newbyte
.ret:
  ret
endp


proc hexbyte
  lodsb
  call ishex
  jc .ret
  call asc2hex
  mov cl,4
  shl al,cl
  mov dl,al
  lodsb
  push dx
  call ishex
  pop dx
  jc .ret
  call asc2hex
  add dl,al
  inc si
  xchg ax,dx
  stosb
.ret:
  ret
endp


proc scanofs
  mov cx,8
.load:
  lodsb
  push cx
  call ishex
  pop cx
  jc .ret
  push cx
  call asc2hex
  pop cx
.adjust:
  push ax cx
  mov cl,4
.shift:
  rcl w[ofs],1
  rcl w[ofs+2],1
  loop .shift
  pop cx
.add:
  pop ax
  xor ah,ah
  add w[ofs],ax
  adc w[ofs+2],0
.continue:
  loop .load
.ret:
  ret
endp


proc asc2hex
  cmp al,40h
  adc al,69h
  daa
  mov ah,al
  and al,15
  mov cl,4
  shr ah,cl
  aad
  ret
endp


proc ishex
  mov cx,'09'
  push cx
  call rangecheck
  sbb bh,bh

; mov cx,'af'
; push cx
; call rangecheck
; sbb bl,bl
; and bh,bl

  mov cx,'AF'
  push cx
  call rangecheck
  sbb bl,bl
  and bh,bl

  shr bh,1

  ret       ; return CF if not valid hex char
endp


proc rangecheck
            ; in: (upper_limit shl 8) + lower_limit
  pop bp
  pop dx    ; mov dx,[sp+2]
  push bp
.check:
  cmp al,dl
  sbb ch,ch
  inc dh
  cmp al,dh
  cmc
  sbb dl,dl
  or dl,ch
  cmp dl,1  ; set CF if DL == 0
  cmc       ; return NC if AL within valid range
  ret
endp


; section .data

errlvl db 1, GOODBYE
handle dw 0
ofs    dd 0
usagemsg db CR,LF,'Usage:  bytefix.com CaFe5150 Ed A1 file.dat',CR,LF,'$'


; section .bss

oldbyte rb 1
newbyte rb 1
foundbyte rb 1

; EOF
    
Post 16 Mar 2017, 03:13
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 10 Aug 2017, 00:52
Here's another improvement ... but I'm not too confident about it. Despite some light testing, I'm worried about bugs. Also, it's hitting the 512-byte limit, which was my goal to stay under. At this point I'm considering rewriting (again) in Turbo Pascal (hey, 4 kb output isn't too bloated, heheh).

Code:
; BYTEFIX.ASM -- change a hex byte in a binary file
;
; rugxulo _AT_ gmail
;
; public domain, free for any use, nenies proprajho, "Christus Rex!"


ARGC=80h
ARGV=81h
BYTE1=ARGV+1+8+1
CR=13
FILENAME=ARGV+1+8+1+2+1+2+1
LF=10
MINARGC=15+1

CLOSEFILE=3Eh
GOODBYE=4Ch
OPENFILERW=3D02h
READFILE=3Fh
SEEKSETFILE=4200h
WRITEFILE=40h
WRITESTR=9

DOS equ int 21h

b equ byte
w equ word

macro proc name {
  if used name
name:
}

end fix end if

macro endp {
.ret:
  ret
  end
}

macro charargs {
  push w[BYTE1+3]
  push w[BYTE1]
  mov di,charbyte1
  rept 2 \{
    pop ax
    stosw
  \}
}

macro stripblanks {
  mov di,ARGV
  lea si,[di+1]
  inc cx
  mov al,' '
  rep scasb
  dec di
  mov [ARGC],cl
  xchg si,di
  push cx cx
  rep movsb
  pop cx di
  add di,ARGV
  mov al,' '
  std
  rep scasb
  cld
  inc cx
  mov b[di+2],0 ; NUL (for ASCIIZ filename)
  mov [ARGC],cl
}

macro fixargs {
  local .loop,.bye

  xor ch,ch
  mov cl,[ARGC]
  jcxz .bye
  push cx
  stripblanks
  pop cx
  mov si,ARGV+1
  mov di,si
.loop:
  lodsb
  push cx
  mov bx,'az'
  push bx
  call rangecheck
  pop cx
  sbb bl,bl
  or bl,not ' '
  and al,bl
  stosb
  loop .loop
.bye:
}

macro scanofs {
  local .load,.adjust,.shift,.add,.continue,.bye

  mov cx,8
.load:
  lodsb
  push cx
  call ishex
  pop cx
  jc .bye
  push cx
  call asc2hex
  pop cx
.adjust:
  push ax cx
  mov cl,4
.shift:
  rcl w[ofs],1
  rcl w[ofs+2],1
  loop .shift
  pop cx
.add:
  pop ax
  xor ah,ah
  add w[ofs],ax
  adc w[ofs+2],0
.continue:
  loop .load
.bye:
}

; --------------------------------------------------------------------
; section .text
org 100h
Main:
  charargs
  fixargs
  cmp b[ARGC],MINARGC
  jae .okay
  mov ah,WRITESTR
  mov dx,usagemsg
  DOS
  neg [errlvl]
  jmp Fino
.okay:
  mov si,ARGV+1
  scanofs
  jc Fino
  call fixbyte
Fino:
  mov ax,w[errlvl]
  DOS
; end Main
; --------------------------------------------------------------------


proc fixbyte
  cmp b[BYTE1],'?'
  jz .open
  call getbytes
  jc .ret
.issame:
  mov al,[oldbyte]
  cmp al,[newbyte]
  jz .ret
.open:
  mov ax,OPENFILERW
  mov dx,FILENAME
  DOS
  jc .ret
  mov [handle],ax
.seek:
  call seekbyte
  jc .close
.read:
  mov ah,READFILE
  mov bx,[handle]
  mov cx,1
  mov dx,foundbyte
  DOS
  jc .close
.query:
  cmp b[BYTE1],'?'
  jnz .compare
  mov al,[foundbyte]
  mov [errlvl],al
  jmp .close
.compare:
  cmp b[BYTE1],'!'
  jz .seekagain
.noforce:
  mov al,[foundbyte]
  cmp al,[oldbyte]
  jnz .close
.seekagain:
  call seekbyte
.write:
  mov ah,WRITEFILE
  mov bx,[handle]
  mov cx,1
  mov dx,newbyte
  DOS
  jc .close
  dec [errlvl]
.close:
  mov ah,CLOSEFILE
  mov bx,[handle]
  DOS
endp


proc seekbyte
  mov ax,SEEKSETFILE
  mov bx,[handle]
  mov cx,w[ofs+2]
  mov dx,w[ofs]
  DOS
endp


proc getbytes
  push ax
  mov di,oldbyte
  push di
  mov ax,[charbyte1]
  mov cx,[charbyte2]
  xchg al,ch
  cmp cx,''''''
  jnz .hex
  mov cx,2
.check:
  xchg ah,al
  mov dx,'!~'
  push dx
  call rangecheck
  jc .bye
  loop .check
  xchg ah,al
  stosw
  jmp .bye
.hex:
  mov si,BYTE1
  cmp b[si],'!'
  jnz .noforce
  add si,3
  inc di
  jmp .byte2
.noforce:
  call hexbyte
  jc .bye
.byte2:
  call hexbyte ; newbyte
.bye:
  pop di ax
endp


proc hexbyte
  lodsb
  call ishex
  jc .ret
  call asc2hex
  mov cl,4
  shl al,cl
  mov dl,al
  lodsb
  push dx
  call ishex
  pop dx
  jc .ret
  call asc2hex
  add dl,al
  inc si
  xchg ax,dx
  stosb
endp


proc asc2hex
  cmp al,40h
  adc al,69h
  daa
  mov ah,al
  and al,15
  mov cl,4
  shr ah,cl
  aad
endp


proc ishex
  mov cx,'09'
  push cx
  call rangecheck
  sbb bh,bh

; mov cx,'af'
; push cx
; call rangecheck
; sbb bl,bl
; and bh,bl

  mov cx,'AF'
  push cx
  call rangecheck
  sbb bl,bl
  and bh,bl

  shr bh,1  ; return CF if not valid hex char
endp


proc rangecheck
            ; in: (upper_limit shl 8) + lower_limit
  pop bp
  pop dx    ; mov dx,[sp+2]
  push bp
.check:
  cmp al,dl
  sbb ch,ch
  inc dh
  cmp al,dh
  cmc
  sbb dl,dl
  or dl,ch
  cmp dl,1  ; set CF if DL == 0
  cmc       ; return NC if AL within valid range
endp


; --------------------------------------------------------------------
; section .data
errlvl db 1, GOODBYE
ofs    dd 0
usagemsg db CR,LF,'Usage: BYTEFIX CaFe5150 Ed A1 my.dat',CR,LF,'$'
; --------------------------------------------------------------------


; --------------------------------------------------------------------
; section .bss
oldbyte rb 1
newbyte rb 1
foundbyte rb 1
charbyte1 rw 1
charbyte2 rw 1
handle rw 1
; --------------------------------------------------------------------

; EOF
    
Post 10 Aug 2017, 00:52
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.