tags:

views:

59

answers:

1

I currently have an assembly program which is modeled after the hexdump system function in Linux. Essentially, it prints the current line number, converts the binary values to hexadecimal strings and also shows the current ASCII associated with the hexadecimal string.

I'm experiencing a problem with my system of printing line numbers. The function will only work when other parts of the code are commented out -- otherwise it produces incorrect results. However, I don't see why the two parts of code should have any effect on each other, since the registers utilized are cleared.

When the PrintLineNum function works, it prints the current line number to the left of the line

000000E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |r Linux, using N.| 
000000F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |ASM 2.05,.;    d.| 
0000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |emonstrating the.| 
0000011 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | conversion of b.| 

However, when functionality for printing the hexadecimal string is re-enabled, it begins to "skip".

000000E 72 20 4C 69 6E 75 78 2C 20 75 73 69 6E 67 20 4E  |r Linux, using N.| 
000000F 40 53 4D 20 32 2E 30 35 2C 0A 3B 20 20 20 20 64  |ASM 2.05,.;    d.| 
0000000 65 6D 6F 6E 73 74 72 60 74 69 6E 67 20 74 68 65  |emonstrating the.| 
0000000 20 63 6F 6E 76 65 72 73 69 6F 6E 20 6F 66 20 62  | conversion of b.| 
0000002 69 6E 60 72 79 20 76 60 6C 75 65 73 20 74 6F 20  |inary values to .| 
0000003 68 65 78 60 64 65 63 69 6D 60 6C 20 73 74 72 69  |hexadecimal stri.| 
0000004 6E 67 73 2E 0A 3B 20 20 20 20 49 74 20 60 63 74  |ngs..;    It act.

I'm not sure why printing the hexadecimal string affects the line count -- the two operations are independent as far as I can tell. Any advice, suggestions or improvements would help. Please note that some of this code is from Duntemann's "Assembly Language - Step by Step". I have just added the line numbers and ASCII printout. I have marked the problematic section of code below.

Thanks for any assistance!

SECTION .bss                    ; Section containing uninitialized data

        BUFFLEN equ 16          ; We read the file 16 bytes at a time
        Buff:   resb BUFFLEN    ; Text buffer itself

SECTION .data                   ; Section containing initialised data

        ; storage location for line number
        LineNStr: dd "000001"
        LINNLEN equ $-LineNStr

        LineNum: dd 1
        LINLEN equ $-LineNum

        ; storage location for ASCII string
        TextStr:        db " |................ | ",10
        TEXTLEN equ $-TextStr

        ; storage location for hex string
        HexStr: db " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
        HEXLEN equ $-HexStr

        ; conversion tables
        Digits: db "0123456789ABCDEF"

        Ascii:  
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h,2Ah,2Bh,2Ch,2Dh,2Eh,2Fh
                db 30h,31h,32h,33h,34h,35h,36h,37h,38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh
                db 40h,41h,42h,43h,44h,45h,46h,47h,48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh
                db 50h,51h,52h,53h,54h,55h,56h,57h,58h,59h,5Ah,5Bh,5Ch,5Dh,5Eh,5Fh
                db 60h,61h,62h,63h,64h,65h,66h,67h,68h,69h,6Ah,6Bh,6Ch,6Dh,6Eh,6Fh
                db 70h,71h,72h,73h,74h,75h,76h,77h,78h,79h,7Ah,7Bh,7Ch,7Dh,7Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
                db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh

SECTION .text                   ; Section containing code

; All done! Let's end this party:
Done:
        mov eax,1               ; Code for Exit Syscall
        mov ebx,0               ; Return a code of zero
        int 80H                 ; Make kernel call

global  _start                  ; Linker needs this to find the entry point!

_start:
        nop                     ; This no-op keeps gdb happy...

; Read a buffer full of text from stdin:
Read:
        mov eax,3               ; Specify sys_read call
        mov ebx,0               ; Specify File Descriptor 0: Standard Input
        mov ecx,Buff            ; Pass offset of the buffer to read to
        mov edx,BUFFLEN         ; Pass number of bytes to read at one pass
        int 80h                 ; Call sys_read to fill the buffer
        mov ebp,eax             ; Save # of bytes read from file for later
        cmp eax,0               ; If eax=0, sys_read reached EOF on stdin
        je Done                 ; Jump If Equal (to 0, from compare)

; Set up the registers for the process buffer step:
        mov esi,Buff            ; Place address of file buffer into esi
        mov edi,HexStr          ; Place address of line string into edi
        xor ecx,ecx             ; Clear line string pointer to 0

; Go through the buffer and convert binary values to hex digits:
Scan:
        xor eax,eax             ; Clear eax to 0

; Convert text into ASCII string:
        mov al,byte [esi+ecx]           ; Get current location into al
        mov bl,byte [Ascii+eax]         ; Use lookup table to perform conversions
        mov byte [TextStr+ecx+2],bl     ; Write to ASCII text block

; Here we calculate the offset into the line string, which is ecx X 3
        mov edx,ecx             ; Copy the pointer into line string into edx
;       shl edx,1               ; Multiply pointer by 2 using left shift
;       add edx,ecx             ; Complete the multiplication X3
        lea edx,[edx*2+edx]     ; The lea operation performs a combination of the two operations above

; Get a character from the buffer and put it in both eax and ebx:
        mov al,byte [esi+ecx]   ; Put a byte from the input buffer into al
        mov ebx,eax             ; Duplicate the byte in bl for second nybble

;;;;;;;;;;;;;;;;;; When this section is commented out, the line printout works properly
; Look up low nybble character and insert it into the string:
        and al,0Fh                 ; Mask out all but the low nybble
        mov al,byte [Digits+eax]   ; Look up the char equivalent of nybble
        mov byte [HexStr+edx+2],al ; Write the char equivalent to line string

; Look up high nybble character and insert it into the string:
        shr bl,4                ; Shift high 4 bits of char into low 4 bits
        mov bl,byte [Digits+ebx] ; Look up char equivalent of nybble
        mov byte [HexStr+edx+1],bl ; Write the char equivalent to line string
;;;;;;;;;;;;;;;;;; When this section is commented out, the line printout works properly

; Bump the buffer pointer to the next character and see if we're done:
        inc ecx         ; Increment line string pointer
        cmp ecx,ebp     ; Compare to the number of characters in the buffer
        jna Scan        ; Loop back if ecx is <= number of chars in buffer

; Print the current line number prior to printing any other information
        Call PrintLineNum ; print line number function

; Write the line of hexadecimal values to stdout:
        mov eax,4               ; Specify sys_write call
        mov ebx,1               ; Specify File Descriptor 1: Standard output
        mov ecx,HexStr          ; Pass offset of line string
        mov edx,HEXLEN          ; Pass size of the line string
        int 80h                 ; Make kernel call to display line string

; Write the line of ASCII values to stdout:
        mov eax,4               ; Specify sys_write call
        mov ebx,1               ; Specify File Descriptor 1: Standard output
        mov ecx,TextStr         ; Pass offset of line string
        mov edx,TEXTLEN         ; Pass size of the line string
        int 80h                 ; Make kernel call to display line string
        jmp Read                ; Loop back and load file buffer again

PrintLineNum:
; Clear out the registers
        xor eax,eax
        xor ebx,ebx

; Get data into registers
        mov al,byte [LineNum]   ; Put a byte from the input buffer into al
        mov ebx,eax             ; Duplicate the byte in bl for second nybble

; Look up low nybble character and insert it into the string:
        and al,0Fh                 ; Mask out all but the low nybble
        mov al,byte [Digits+eax]   ; Look up the char equivalent of nybble
        mov byte [LineNStr+6],al ; Write the char equivalent to line string

; Look up high nybble character and insert it into the string:
        shr bl,4                ; Shift high 4 bits of char into low 4 bits
        mov bl,byte [Digits+ebx] ; Look up char equivalent of nybble
        mov byte [LineNStr+5],bl ; Write the char equivalent to line string

; Increment line number
        mov eax,[LineNum]
        inc eax
        mov [LineNum],eax

; Write the line number to stdout:
        mov eax,4               ; Specify sys_write call
        mov ebx,1               ; Specify File Descriptor 1: Standard output
        mov ecx,LineNStr        ; Pass offset of line string
        mov edx,LINNLEN
        int 80h                 ; Make kernel call to display line string
        ret
+2  A: 

You have an off-by-one error in your code.

The problem is on this line:

    jna Scan        ; Loop back if ecx is <= number of chars in buffer

which means that you will go round the loop 17 times, rather than 16. This is hinted at by ruslik's comment (the original TextStr string is 16 dots followed by a space, so why does the space gets replaced?).

The reason it breaks the line numbering is that mov byte [HexStr+edx+2],al in the marked section overflows HexStr on the 17th iteration, and writes into the Digits table. It breaks the hex dump as well (look at the first broken line: the a of demonstrating has been dumped as 60, not 61).

Try:

    jb  Scan        ; Loop back if ecx is < number of chars in buffer

instead.

Matthew Slattery
Yep, this is absolutely correct. It is actually not my own code with the error -- the error comes from the code the author included with the book.
BSchlinker