Author Topic: about setting buffers, and div value in edx.  (Read 12766 times)

Offline Proudhonite

  • Jr. Member
  • *
  • Posts: 2
about setting buffers, and div value in edx.
« on: September 30, 2012, 06:45:12 AM »
hello there, i would greatly appreciate help with two issues.
i am creating a code to convert integers to ascii, i managed to succesfully convert a single integer, but now i am trying with a multiple digit number, in the process i ran into one doubt and one problem, but firstly, here is my code:

Code: [Select]
section .data
num1   dw    4950;
break  db    "breaking point",0xa;
brelen equ   $-break;

section .bss
buff1 resw 5;
buff2 resw 5;
buff3 resw 5;
buff4 resw 5;

section .txt
 global _start
_start:
mov  eax,      [num1];
mov  ebx,              0;
push               ebx;putting 0 a the top of the stack

.loop:
call         division;
xor ebx,          ebx;
pop               ebx;
push              eax;
mov  eax,         48d; 48 is 0 decimal value
add  eax,         edx; the remainder of the division is in edx
inc               ebx; adding one to the counter
cmp  ebx,           1;if it is the first iteration
je           .1buffer;move the result to the first buffer
cmp  ebx,           2;if it is the second loop...you know...
je           .2buffer;
cmp  ebx,           3;
je           .3buffer;
cmp  ebx,           4;
je           .4buffer;

.1buffer:
mov  [buff1+ecx], eax;my first doubt, i know that it is saving the value in the buffer, pu i do not understand what is ecx doing there
jmp           .prediv; to do necesary things before the next iteration
.2buffer:
mov [buff2+ecx],  eax;
jmp           .prediv;
.3buffer:
mov [buff3+ecx],  eax;
jmp           .prediv;
.4buffer:
mov [buff4+ecx],  eax;
jmp              .fin;

.prediv:
pop               eax; returning the remaining digits to eax
push              ebx; saving the counter in stack
jmp              .loop; returning to the division

.fin:
mov ecx,        buff4;to print numbers
mov edx,            5;
call             cout;
mov ecx,        buff3;
mov edx,            5;
call             cout;
mov ecx,        buff2;
mov edx,            5;
mov ecx,        buff1;
mov edx,            5;
call             cout;
call             cout;
mov ebx,            0;closing
mov eax,            1;
int 80h              ;

cout:
mov eax,            4;
mov ebx,            1;
int 80h              ;
ret                  ;

division:
xor edx,          edx;
xor ebx,          ebx;
mov ebx,           10;
div               ebx;
ret                  ;

what this prints is "7300" instead of "4950" and i have no idea why, could someone help me with this? that is the problem i have ran too.
and besides that, i need to understand, what is the meaning of "mov [buff1+ecx], eax" i know that it is storing the ascii value of the number in the buffer, but i do not understand what the ecx is doing there, summing to the buffer, and what other applications does that have.

help would be greatly appreciated, so thanks a lo beforehand!

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: about setting buffers, and div value in edx.
« Reply #1 on: September 30, 2012, 07:26:13 AM »
Very first problem I see is that "num1" wants to be "dd", not "dw". When you move a "dw" variable into eax, it gets the two bytes of your number and also picks up the first two bytes of your next variable - "br" in this case.

That still isn't right - prints 4900 now. I'll have to study it, and see if I can address your questions. I need some sleep before I tackle that. Remind me if I forget!

Best,
Frank


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: about setting buffers, and div value in edx.
« Reply #2 on: September 30, 2012, 05:23:30 PM »
Okay, where was I? Oh, yeah...
Code: [Select]
; nasm -f elf32 myfile.asm
; ld -o myfile myfile.o (-m elf_i386 for 64-bit system)

section .data
;num1   dw    4950;
; you want dd = data dword, not dw = data word
num1   dd    4950;
break  db    "breaking point",0xa;
brelen equ   $-break;

section .bss
buff1 resw 5;
buff2 resw 5;
buff3 resw 5;
buff4 resw 5;

;section .txt
section .text ; this is the "known" name
 global _start
_start:
mov  eax,      [num1];
mov  ebx,              0;
push               ebx;putting 0 a the top of the stack

.loop:
call         division;
xor ebx,          ebx;
pop               ebx;
push              eax;
mov  eax,         48d; 48 is 0 decimal value
add  eax,         edx; the remainder of the division is in edx
inc               ebx; adding one to the counter
cmp  ebx,           1;if it is the first iteration
je           .1buffer;move the result to the first buffer
cmp  ebx,           2;if it is the second loop...you know...
je           .2buffer;
cmp  ebx,           3;
je           .3buffer;
cmp  ebx,           4;
je           .4buffer;

.1buffer:
mov  [buff1+ecx], eax;my first doubt, i know that it is saving the value in the buffer, pu i do not understand what is ecx doing there
jmp           .prediv; to do necesary things before the next iteration
.2buffer:
mov [buff2+ecx],  eax;
jmp           .prediv;
.3buffer:
mov [buff3+ecx],  eax;
jmp           .prediv;
.4buffer:
mov [buff4+ecx],  eax;
jmp              .fin;

.prediv:
pop               eax; returning the remaining digits to eax
push              ebx; saving the counter in stack
jmp              .loop; returning to the division

.fin:
mov ecx,        buff4;to print numbers
mov edx,            5;
call             cout;
mov ecx,        buff3;
mov edx,            5;
call             cout;
mov ecx,        buff2;
mov edx,            5;
; you didn't call cout here!
call cout

mov ecx,        buff1;
mov edx,            5;
call             cout;
; ...and you only want to call cout once here
;call             cout;
mov ebx,            0;closing
mov eax,            1;
int 80h              ;

cout:
mov eax,            4;
mov ebx,            1;
int 80h              ;
ret                  ;

division:
xor edx,          edx;
xor ebx,          ebx;
mov ebx,           10;
div               ebx;
ret                  ;
This actually works, with the couple of minor changes as commented. Good job... but it's more complicated than it needs to be (would be good with some tomato sauce and garlic :) ), and doesn't really do what you want in a couple places, even though it "works".
Code: [Select]
mov  [buff1+ecx], eax;my first doubt, i know that it is saving the value in the buffer, pu i do not understand what is ecx doing there
Good question. What IS it doing there? :) It adds the value in ecx to the address of buf1. Since at this point in your program (by luck) the value of ecx is zero, it doesn't do anything at all. If you had, say, the value of "counter" in ecx, it might do something "useful"...

By using eax here, you're putting four bytes into the buffer at once. You really only want one - the "character representing a digit" that you've calculated. Just use al. This doesn't do any harm since you're putting eax first in the buffer each time. If you put eax near the end of the buffer, it would run off the end of the buffer and scribble on the next variable, which would not be good!

You're using a separate buffer for each character... plus three bytes from eax which happen to be zero. Then you print 5 characters (in edx) with sys_write. Since printing zeros doesn't do anything (this is NOT a zero-terminated string like C!) this "works" okay, but probably isn't what you really want to do. You can put all of your characters in a single buffer, as long as you keep track of what goes where (and how many), and print 'em all with a single sys_write.

You're using the "div" instruction correctly. I don't see much advantage to making it a subroutine - doesn't hurt. Since "div" with a 32-bit operand divides edx:eax by the operand, we need to make edx zero - or some intended value - before the "div". Since edx will hold the remainder after the "div", we need to re-zero it every time, if we're doing it in a loop. You've dealt with that nicely. You don't really need to "xor ebx, ebx", the "mov" overwrites the entire register - doesn't hurt.

As you've coped with, repeated "div"s give us the digits we want to convert to a character and print, but in the opposite order than we want to print 'em. There are a number of ways to deal with this. One way, which I usually use, is to push digits on the stack and pop 'em off in the order I want 'em, converting to number before or after. Another way is to put the digits/characters in the buffer as we get 'em, and reverse the buffer at the end, before printing. Still another way is to start at the end of the buffer and work towards the front. This may not get you to the start of the buffer. You could pad with spaces to the start of the buffer, for "right justified" numbers - look nice printed in a column. Something I sometimes do is return both the address in the buffer where the characters actually start, and the length - in ecx and edx respectively is convenient... all ready for "cout". :) This doesn't conform to any "calling convention" so might not be the best idea. But if we stick to asm, we can return more than one thing, and in a register other than eax... if we want to.

Then there's the way you did it, which "kinda works" (for 4 digits), but seems more complicated than it needs to be. I'd give it another shot and see if you can simplify it - less pushing and popping of "counter" and "quotient", perhaps... You're on the right track!

Best,
Frank


Offline Proudhonite

  • Jr. Member
  • *
  • Posts: 2
Re: about setting buffers, and div value in edx.
« Reply #3 on: September 30, 2012, 08:12:29 PM »
Thank you a lot! i managed to get the correct output just fixing the size of the bytes as you said so, i really thought that determining the size of the data was more about making the code more compact or use more resources, but now i know understand that it really afects the organization of data (if, however, you could explain a little more of why this is like this, or give me some kind of place to inform myself better, i would appreciate it a lot!).
and i don't know how i did not see what the "mov [buff1+ecx], eax" meant either! thank you for that too, the first time i saw how to allocate memory in buffer (in a example about printing user imputs) i though the "+ecx" part had to to with putting the whole buffer in some kind of "correct place" near ecx (like moving in some form the whole physical space of the buffer to ecx) but now i understand that i was just moving the bytes directly to the buffer, and that ecx was probably part of some kind of logic in the "cin" project.
Thank you a lot, you have been of massive help, but i have one more request and another question, if it is not a problem, how could i save multiple data in a single buffer, as you suggested, to directly print with only one cout the number? and, i understand that my code seems unnecessarly complex, however, i used all those sub rutines to reduce its size, and expecting it to use less procesing resources, does the code manage any of that, or is it just completely unncecesary to make it like this?
« Last Edit: September 30, 2012, 08:18:14 PM by Proudhonite »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: about setting buffers, and div value in edx.
« Reply #4 on: October 02, 2012, 02:47:41 PM »
Well, I dunno. The general rule is that if you allocate more space than you need, your code might be "bloated". If you allocate less than you need, it's a bug.

Here are a couple examples, one without any subroutines, one with...

Code: [Select]
; convert one number to ascii and print it
; nasm -f elf32 myfile.asm
; ld -o myfile myfile.o (-melf_i386 for 64-bit systems)

global _start

section .bss
    buffer resb 16 ; nice round number

section .text
_start:
    mov eax, 1234567 ; our number

    mov ebx, 10 ; divisor
    xor ecx, ecx ; counter
top:
    xor edx, edx ; zero edx for the div
    div ebx ; quotient in eax, remainder in edx
    push edx ; our digit
    inc ecx  ; count 'em
    test eax, eax ; is quotient zero yet?
    jnz top ; no? do more

    mov edx, ecx ; we're done with edx - save the length
    mov edi, buffer
poploop:
    pop eax
    add eax, '0' ; convert digit to character
    stosb
    loop poploop

    mov byte [edi], 10 ; throw in a linefeed for neater display
    inc edx ; and add it to the length
    mov ecx, buffer
    mov ebx, 1 ; stdout
    mov eax, 4 ; sys_write
    int 80h
exit:
    xor ebx, ebx ; exitcode zero
    mov eax, 1 ; sys_exit
    int 80h

Code: [Select]
; convert several numbers to ascii (signed, right-justified)
; nasm -f elf32 myfile.asm
; ld -o myfile myfile.o (-melf_i386 for 64-bit syatems)

global _start

FIELD_WIDTH equ 12 ; 10 digits and a minus sign... rounded up :)

section .data
    num1 dd 123456
    num2 dd -123414
   
section .bss
    buf1 resb FIELD_WIDTH
    buf2 resb FIELD_WIDTH
    bufres resb FIELD_WIDTH

section .text
_start:
    mov eax, [num1]
    mov esi, buf1
    mov ecx, FIELD_WIDTH
    call binasc

    mov eax, [num2]
    mov esi, buf2
    mov ecx, FIELD_WIDTH
    call binasc
   
; add 'em togtether...
    mov eax, [num1]
    add eax, [num2]
    mov esi, bufres
    mov ecx, FIELD_WIDTH
    call binasc

; now print 'em out
    mov ecx, buf1
    mov edx, FIELD_WIDTH
    call write_stdout

    call linefeed

    mov ecx, buf2
    mov edx, FIELD_WIDTH
    call write_stdout

    call linefeed

    mov ecx, bufres
    mov edx, FIELD_WIDTH
    call write_stdout
   
    call linefeed
exit:
    xor ebx, ebx
    mov eax, 1
    int 80h
;-------------------

;--------------------
; from Chuck Crayne - RIP, Chuck.
;convert binary to ascii
;call with eax = signed binary number
; esi = address of output string
; ecx = length of output string
;returns esi = 1st printed digit
; ecx = no of digits printed (includes sign if any)
; other registers preserved
binasc: push edx
push ebx
push edi
push eax
mov edi,esi ;save start of string
ba1: mov byte [esi],' ' ;fill string with blanks
inc esi
loop ba1
mov ebx,10 ;initialize divisor
or eax,eax ;value negative?
jns ba2 ;no problem
neg eax ;make it positive
ba2: xor edx,edx ;clear high part of dividend
div ebx ;divide by 10
add dl,'0' ;convert to ascii digit
dec esi ;step backwards through buffer
mov [esi],dl ;store digit
inc ecx
cmp esi,edi ;out of space
jz ba4 ;yes - quit
or eax,eax ;all digits printed?
jnz ba2 ;no - keep trucking
pop eax ;get original value
or eax,eax ;negative?
jns ba3 ;no - quit
dec esi ;place for sign
mov byte [esi],'-'
inc ecx ;add to char count
ba3: pop edi
pop ebx
pop edx
ret
ba4: pop eax
jmp ba3
;-------------------

;--------------------
write_stdout:
; call with ecx = buffer, edx = lemgth
; returns length
    push ebx
    mov ebx, 1
    mov eax, 4
    int 80h
    pop ebx
    ret
;--------------------

;----------------
linefeed:
    push eax
    push ecx
    push edx
    push 10 ; linefeed
    mov ecx, esp ; my stack is my buffer
    mov edx, 1 ; length
    call write_stdout
    pop edx ; dummy pop to free the LF
    pop edx
    pop ecx
    pop eax
    ret
;---------------------

They do different things, so it's kinda comparing apples with oranges...

Best,
Frank