Author Topic: dividing in NASM  (Read 27337 times)

Offline samqo

  • New Member
  • Posts: 1
dividing in NASM
« on: May 08, 2010, 09:26:36 PM »
Hi ... I am begining with learnin assembly language and I have problem ... I just want to divide the value of register ... example 1230 div 10 = 123 ... just a simple thing, but doesnt work ... windows shows error message after I put values and press Enter on keyboard ... here is my simple source code:

Code: [Select]

%include "asm_io.inc"
segment .data
text1 db  "Put decimal number: ",0
text2 db  "This decimal number ",0
;text3 db  "(which value  in decimal is  ",0
text4 db  ")",0
text5 db  " has an octal value (on 32 bits) ",0
error db  "Wrong number!", 0

segment .text
global _asm_main
_asm_main:
xor ECX, ECX
xor EBX, EBX ;result value
mov EAX, text1
call print_string
call print_nl

main:
call read_char
cmp ECX, 9
je fault

cmp EAX, 10
je end
cmp EAX, 13
je end

cmp EAX, '0'
jnge fault

cmp     EAX, '9'
jle number

number:
sub EAX,'0'

cmp     EAX, 0
je      adding_zero
jne     adding_number

adding_zero:

imul    EBX, 10
jmp main


adding_number:

add EBX, EAX
imul    EBX, 10
inc ECX
jmp main

fault:
mov EAX, error
call print_string
popa
leave
ret

end:

mov EAX,EBX
mov EBX,10
div EBX
mov EBX,EAX
jmp     end


mov EAX, text2
call print_string
call print_nl

mov EAX, EBX
call print_int
;mov EAX, text4
;call print_string
;call print_nl
mov EAX, text5
call print_string
call print_nl

mov EAX, EBX
shr EAX, 30
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 38000000h
shr EAX, 27
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 7000000h
shr EAX, 24
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 0e00000h
shr EAX, 21
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 1C0000h
shr EAX, 18
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 38000h
shr EAX, 15
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 7000h
shr EAX, 12
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 0e00h
shr EAX, 9
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 1C0h
shr EAX, 6
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 38h
shr EAX, 3
add EAX, '0'
call print_char
mov EAX, 32
call print_char

mov EAX, EBX
and EAX, 7h
add EAX, '0'
call print_char

ret[font=Verdana][/font]

I really do not know wheres the mistake ... sory for so sily question ... but I am beginner ...  ???

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: dividing in NASM
« Reply #1 on: May 09, 2010, 02:46:05 AM »
Don't feel bad about it - it is a *very* common error. I knew the answer before even looking at your code: "you didn't zero edx!"

Look closely at the description of the "div" instruction. What does it divide by what? Not eax (in 32-bit size), but edx:eax!!! (that is, edx * 4G + eax) We're only interested in eax, but the CPU cares about edx. If the result of the division won't fit in a 32-bit register (in this case), the CPU raises an exception. Back in the old dos days, this was reported as "divide by zero error". Huh? I didn't divide by zero! In Linux, it reports this exception as "floating point error". Huh? I didn't use any FPU! I don't know what Windows says about it, but I'll bet it isn't "result of divide won't fit in 32 bits", so we remain mystified.

Note that "div" leaves the quotient in eax and the remainder in edx, so edx changes "behind our back", so we have to do this each time through the loop. You might just want to do something with this remainder, while you've got it. :)

Your code has some other errors - the most serious one, probably, is if you get to "fault:", you use the "usual epilog" popa/leave/ret... but you didn't put the usual prolog - enter 0, 0/pusha - at "asm_main:"! This will surely crash.

Have you verified that that your "get number" routine gives correct results? It looks to me like you add the digit you've found to "result so far", and then multiply by ten. I think you want to do that the other-way-around. You've got separate code for if the digit is zero. What would happen if you just added it to "result so far" like any other digit? It doesn't hurt to treat it separately, but I think it's masking an error when your input ends in zero like "1230" - does it work for "1234"?

I haven't gotten into your code after the "div loop" (which has no exit, at present). I guess it's supposed to print the number in octal? Looks like it might work. You might be able to do this in a "loop", too. In fact, I think the exact same procedure you use for decimal would work, if you divide by 8 instead of 10. (get the decimal routine working first, of course - keep your eye on that remainder!) But, as you've figured out, we can divide by 8 with a shift. Probably much faster...

A minor nit: jnge and jle, which you use in validating the digits entered, are signed conditionals - jnae and jbe would be the unsigned instructions. Since you probably won't encounter a negative number here, it shouldn't ever cause a problem, but it isn't "right". (IMHO)

Note that Intel has given the same opcode multiple names. "jnge", for example, is the same opcode as "jl". "jle"="jng". "je"="jz" - I like to use "jz" if I'm testing for zero, and "je" if I'm testing for equality, "for clarity" - even though they're the same thing.

You do "jle number". What happens if the jump isn't taken? It "falls through" to "number:" anyway! That's not right! What you really want there is "jb fault" and "ja fault", I think... (incidentally, "jb"="jc" - I'd use "below" for clarity)

While I'm pickin' nits... "main" and "end" are horrible names for labels in the middle of your code. Neither of 'em mean anything to Nasm, so it'll work alright, but "end" is a directive to some assemblers - everything after it is a comment! And "main"... well it means something to C. Both are likely to confuse humans! Try to find "meaningful names" for your labels (and variables) - it will help make your code "self-documenting", and may help *you* keep track of what you're doing, as well as other people reading your code.

Well, you've got serious bugs to fix... zero edx before the "div" - do something with the remainder(s) - your "exit condition" from that loop, BTW, is when the quotient becomes zero (dividing 0 by 10 is not an error, but is pointless). Make the "prolog" and "epilog" match - very important! Mmmm, driver.c does not preserve the registers C requires (ebx, esi, edi, ebp... and "of course" esp!), so you need both, not neither... Then go back and make sure your "get number" routine is working before you proceed. You'll *never* find the bugs if the number you're processing isn't the number you think you're processing! :)

Despite several errors, you seem to have the "general idea" of what you want to do, so you're off to a good start. Don't feel bad - the "average person" can't do asm at all, so you're way above average already! :)

Best,
Frank