Discussion:
itoa assembly version
(too old to reply)
Liys
2006-05-29 15:14:35 UTC
Permalink
Hi all:
The following is the code snippet to realize the C version of itoa,
basically the code use the remain from divides integer by 10 to extract the
last digit, then add it with 30H to convert it to ASCII, ie.,to turn 123
into ASCII: 123 / 10 with remain 3, then add it with 30H, the ASCII '3' was
generated.... But the problem that I experience is that I got a "Divide
overflow" with 12 / 10(ax/bx).

// convert the data in 'data' seg into ASCII and store it in 'display' seg.
assume cs:code,ds:data

data segment
dw 123, 12666, 1, 8, 3, 38
data ends

display segment
db '0000000000000', 0
display ends

code segment

start:

mov ax, data
mov es, ax
mov ax, display
mov ds, ax
call itoa
mov ax,4c00h
int 21h

itoa:
mov cx, 6
s:
mov si, 0
mov di, 0
mov ax, es:[di]
mov bx, 0AH

s0:
div bx // cause 'Divide overflow' with 0c/0a
mov cx, ax
jcxz ok
add dl, 30h
mov byte ptr ds:[si], dl
inc si
jmp short s0
add di, 2

ok:add dl, 30h
mov byte ptr ds:[si], dl
loop s
ret


code ends
end start
Frank Kotler
2006-05-29 16:01:13 UTC
Permalink
Liys wrote:

...
Post by Liys
.... But the problem that I experience is that I got a "Divide
overflow" with 12 / 10(ax/bx).
"div bx" divides dx:ax by bx, not just ax. If the result won't fit in
ax, which it won't if dx>=bx, it causes an exception.

Most common newbie error in existance!
Post by Liys
// convert the data in 'data' seg into ASCII and store it in 'display' seg.
assume cs:code,ds:data
data segment
dw 123, 12666, 1, 8, 3, 38
data ends
display segment
db '0000000000000', 0
display ends
code segment
mov ax, data
mov es, ax
mov ax, display
mov ds, ax
call itoa
mov ax,4c00h
int 21h
mov cx, 6
mov si, 0
mov di, 0
mov ax, es:[di]
mov bx, 0AH
xor dx, dx
Post by Liys
div bx // cause 'Divide overflow' with 0c/0a
mov cx, ax
jcxz ok
Isn't this going to interfere with your "loop"?
Post by Liys
add dl, 30h
mov byte ptr ds:[si], dl
inc si
Since the remainders are your digits, but in the "wrong" order, you
might want to start with si at the "end" of the buffer and "dec" it here.
Post by Liys
jmp short s0
add di, 2
When does this get executed?
Post by Liys
ok:add dl, 30h
mov byte ptr ds:[si], dl
loop s
ret
code ends
end start
This doesn't quite implement any "itoa" that I've ever seen, but you're
on the right track. You probably want to process *one* number per call
to "itoa", and loop through the six numbers (and probably display each
one) in your main routine, no?

Best,
Frank
l***@gmail.com
2006-05-30 15:59:58 UTC
Permalink
Frank Kotler 写道:
Post by Frank Kotler
"div bx" divides dx:ax by bx, not just ax. If the result won't fit in
ax, which it won't if dx>=bx, it causes an exception.
Most common newbie error in existance!
I have set the dx to zero before divide operation. So "div bx" turn out
to be divide 0000:ax by bx. And I figure that will not cause any
"divide overflow"

And the code changed to:

assume cs:code,ds:data

data segment
dw 12, 23, 34, 45, 56, 12666
data ends
display segment
db '000000000000000', 0
display ends

stack segment
db '0000000000'
stack ends

code segment

start:

mov ax, data
mov es, ax
mov ax, display
mov ds, ax
call dtoc

mov si, 0
mov dh, 8
mov dl, 3
mov cl, 2
call show_str

mov ax,4c00h
int 21h
dtoc:
mov di, 0
mov cx, 6
s:
push cx
mov ax, es:[di]
mov bx, 0AH
s0: div bx
mov cx, ax
jcxz ok
add dl, 30h
mov byte ptr ds:[si], dl
mov dx, 0
inc si
jmp short s0

ok:add dl, 30h
mov byte ptr ds:[si], dl
inc si
add di, 2
mov dx, 0
pop cx
loop s
ret

I have debugged it in protected mode and found that
ds:0 contains 213243546566621 which is quite what I want, though in
reverse order, I could use stack to implement that. But the question is
when I run this program in real mode(because I want it to displayon
screen), I still experience "divide overflow", how this could be?
Frank Kotler
2006-05-30 20:21:32 UTC
Permalink
Frank Kotler <characters my browser doesn't like :)>
Post by Frank Kotler
"div bx" divides dx:ax by bx, not just ax. If the result won't fit in
ax, which it won't if dx>=bx, it causes an exception.
Most common newbie error in existance!
I have set the dx to zero before divide operation. So "div bx" turn out
to be divide 0000:ax by bx. And I figure that will not cause any
"divide overflow"
assume cs:code,ds:data
data segment
dw 12, 23, 34, 45, 56, 12666
data ends
display segment
db '000000000000000', 0
display ends
stack segment
db '0000000000'
stack ends
code segment
mov ax, data
mov es, ax
mov ax, display
mov ds, ax
call dtoc
mov si, 0
mov dh, 8
mov dl, 3
mov cl, 2
call show_str
mov ax,4c00h
int 21h
mov di, 0
mov cx, 6
push cx
mov ax, es:[di]
mov bx, 0AH
s0: div bx
mov cx, ax
jcxz ok
add dl, 30h
mov byte ptr ds:[si], dl
mov dx, 0
inc si
jmp short s0
ok:add dl, 30h
mov byte ptr ds:[si], dl
inc si
add di, 2
mov dx, 0
pop cx
loop s
ret
I have debugged it in protected mode and found that
ds:0 contains 213243546566621 which is quite what I want, though in
reverse order, I could use stack to implement that. But the question is
when I run this program in real mode(because I want it to displayon
screen), I still experience "divide overflow", how this could be?
The only thing I see (without testing it) is that you zero dx at the
*end* of your loop. What's dx on the *first* iteration? Possibly
different in "protected mode" - I assume you mean v86 mode - and real
mode? That's what first comes to mind... try moving it and see if it helps.

What's the advantage of moving ax to cx and then testing cx for zero,
compared to just testing ax for zero?

You'd have a more "reuseable" subroutine if you called it six times,
rather than hard-coding "six numbers" into the subroutine. Your method
of using a separate segment for the output buffer, and hard-coding
offset 0 for source and destination is ... uhhh... unusual - and not
very "maintainable". Your stack seems awfully small - remember that
interrupts use your stack (in rmode - in pmode, they usually switch
stacks), in addition to any use your code makes of it. I'd go with a
minimum of 256 bytes or so. (you're getting 16 bytes anyway, I suspect,
but why make it so small?)

Well, that's enough complaints. :) You've got it "sorta working" so
far... now you need to reverse the order. You could use the stack (if
it's big enough) as you suggest. You could start at the "end" of the
buffer, and decrement the pointer as you go... this could leave
"garbage" in front of the string, if you're not careful - but works well
if you want your numbers "right justified", which looks a lot better if
you're printing a column of numbers. Or you could provide a "reverse
string" function which exchanges the first character with the last, then
second char with next to last, until it comes to the middle. I like the
stack method, myself... You'll want to either count the number of
characters pushed, or push a "guard" value first, and watch for it in
your "pop loop". I usually use the "count" method, but since you're
already using cx, the "guard" method might be better. Your "print"
routine apparently expects a zero-terminated string, so zero might make
a good guard value. If you were using int 21h/9, you could use '$'
conveniently... Using zero means you'll have to do the conversion from
number to ascii digit *before* pushing it...

Well, you're closing in on it. As Betov says - one time I do agree with
him - "Courage!"

Best,
Frank
l***@gmail.com
2006-05-31 01:38:00 UTC
Permalink
Post by Frank Kotler
The only thing I see (without testing it) is that you zero dx at the
*end* of your loop. What's dx on the *first* iteration? Possibly
different in "protected mode" - I assume you mean v86 mode - and real
mode? That's what first comes to mind... try moving it and see if it helps.
By *end* of your loop do you mean:
mov byte ptr ds:[si], dl
mov dx, 0
At the *first* iteration the dl holds the remainders. If that was
removed the divide operation will be dx:ax/bx again, I believe that's
the cause of "Divide Overflow"
If that's what you mean. But I really curious what make the program
"Divide Overflow" again?
By rmode I mean the real DOS, and by pmode I mean WinXP
Post by Frank Kotler
What's the advantage of moving ax to cx and then testing cx for zero,
compared to just testing ax for zero?
" jcxz ok" was also a guard against the last bit of a digit, I wish I
could compare ax against zero but I haven't learn it yet.
Post by Frank Kotler
Well, you're closing in on it. As Betov says - one time I do agree with
him - "Courage!"
Well, Thanks a lot, encouragement is really what I needed.
Frank Kotler
2006-05-31 02:44:16 UTC
Permalink
Post by Liys
Post by Frank Kotler
The only thing I see (without testing it) is that you zero dx at the
*end* of your loop. What's dx on the *first* iteration? Possibly
different in "protected mode" - I assume you mean v86 mode - and real
mode? That's what first comes to mind... try moving it and see if it helps.
mov byte ptr ds:[si], dl
mov dx, 0
Yep. Try it like:

s0: mov dx, 0 ; or "xor dx, dx"
div bx
mov cx, ax
cmp ax, 0 ; or "test ax, ax"
jz ok
add dl, 30h
mov byte ptr ds:[si], dl
inc si
jmp short s0
...

Actually, you probably want to check for "done" after you process the
last digit instead of before:

s0: mov dx, 0 ; or "xor dx, dx"
div bx
mov cx, ax
add dl, 30h
mov byte ptr ds:[si], dl
cmp ax, 0 ; or "test ax, ax"
jz ok
inc si
jmp short s0
...

Even better, if you do it "dead last"...

...
mov byte ptr ds:[si], dl
inc si
cmp ax, 0 ; or "test ax, ax"
jnz s0
ok: add di, 2
loop s

(since we no longer trash cx, we can eliminate the push/pop - both
please! - of cx)

That way, you won't have to duplicate the "add dl, 30h", etc. at the
"ok:" label to catch the last digit...
Post by Liys
At the *first* iteration the dl holds the remainders.
Yeah, *after* the "div". What's dx *before* the div?
Post by Liys
If that was
removed the divide operation will be dx:ax/bx again, I believe that's
the cause of "Divide Overflow"
If that's what you mean. But I really curious what make the program
"Divide Overflow" again?
The "div" operation is always dx:ax - the number we *want* to divide is
in ax - we need to make sure dx is zero, or we'll get false results even
if it doesn't overflow.
Post by Liys
By rmode I mean the real DOS, and by pmode I mean WinXP
Okay. Even real dos is in v86 mode if you've got "emm386.sys" or
something similar running. That really isn't the issue. I forget what
the "usual" state of dx is on program startup. (hell, this'll only take
a minute - be right back)

Mmmm, didn't really find anything definite - according to DEBUG, dx is
zero at startup in an .exe, but not in a .com... I haven't got XP to
check it. That's the only thing I can think of that would cause a divide
overflow exception - except actually dividing by zero, which you're not
doing...
Post by Liys
Post by Frank Kotler
What's the advantage of moving ax to cx and then testing cx for zero,
compared to just testing ax for zero?
" jcxz ok" was also a guard against the last bit of a digit, I wish I
could compare ax against zero but I haven't learn it yet.
Ah. Very complicated:

cmp ax, 0
jz ok ; "je" is the same instruction

That's not how I'd actually do it - "cmp ax, 0" takes two bytes to store
the zero - "test ax, ax" performs a "notional and" - sets flags as if
"and" had been executed, but doesn't store the result anywhere. ("cmp"
is a "notional subtract" - sets flags, but doesn't store the result).
Either of them will set the zero-flag if ax is zero (indicating we're done).
Post by Liys
Post by Frank Kotler
Well, you're closing in on it. As Betov says - one time I do agree with
him - "Courage!"
Well, Thanks a lot, encouragement is really what I needed.
Yeah, there are some really frustrating issues - even worse is when you
can't get the damn thing to assemble at all! But after a while, it gets
better... or you get used to it...

Best,
Frank
l***@gmail.com
2006-05-31 03:43:35 UTC
Permalink
Post by Frank Kotler
Post by Liys
Post by Frank Kotler
The only thing I see (without testing it) is that you zero dx at the
*end* of your loop. What's dx on the *first* iteration? Possibly
different in "protected mode" - I assume you mean v86 mode - and real
mode? That's what first comes to mind... try moving it and see if it helps.
mov byte ptr ds:[si], dl
mov dx, 0
s0: mov dx, 0 ; or "xor dx, dx"
div bx
mov cx, ax
cmp ax, 0 ; or "test ax, ax"
jz ok
add dl, 30h
mov byte ptr ds:[si], dl
inc si
jmp short s0
...
Actually, you probably want to check for "done" after you process the
s0: mov dx, 0 ; or "xor dx, dx"
div bx
mov cx, ax
add dl, 30h
mov byte ptr ds:[si], dl
cmp ax, 0 ; or "test ax, ax"
jz ok
inc si
jmp short s0
...
Even better, if you do it "dead last"...
...
mov byte ptr ds:[si], dl
inc si
cmp ax, 0 ; or "test ax, ax"
jnz s0
ok: add di, 2
loop s
(since we no longer trash cx, we can eliminate the push/pop - both
please! - of cx)
That way, you won't have to duplicate the "add dl, 30h", etc. at the
"ok:" label to catch the last digit...
Post by Liys
At the *first* iteration the dl holds the remainders.
Yeah, *after* the "div". What's dx *before* the div?
Post by Liys
If that was
removed the divide operation will be dx:ax/bx again, I believe that's
the cause of "Divide Overflow"
If that's what you mean. But I really curious what make the program
"Divide Overflow" again?
The "div" operation is always dx:ax - the number we *want* to divide is
in ax - we need to make sure dx is zero, or we'll get false results even
if it doesn't overflow.
Post by Liys
By rmode I mean the real DOS, and by pmode I mean WinXP
Okay. Even real dos is in v86 mode if you've got "emm386.sys" or
something similar running. That really isn't the issue. I forget what
the "usual" state of dx is on program startup. (hell, this'll only take
a minute - be right back)
Mmmm, didn't really find anything definite - according to DEBUG, dx is
zero at startup in an .exe, but not in a .com... I haven't got XP to
check it. That's the only thing I can think of that would cause a divide
overflow exception - except actually dividing by zero, which you're not
doing...
Post by Liys
Post by Frank Kotler
What's the advantage of moving ax to cx and then testing cx for zero,
compared to just testing ax for zero?
" jcxz ok" was also a guard against the last bit of a digit, I wish I
could compare ax against zero but I haven't learn it yet.
cmp ax, 0
jz ok ; "je" is the same instruction
That's not how I'd actually do it - "cmp ax, 0" takes two bytes to store
the zero - "test ax, ax" performs a "notional and" - sets flags as if
"and" had been executed, but doesn't store the result anywhere. ("cmp"
is a "notional subtract" - sets flags, but doesn't store the result).
Either of them will set the zero-flag if ax is zero (indicating we're done).
Post by Liys
Post by Frank Kotler
Well, you're closing in on it. As Betov says - one time I do agree with
him - "Courage!"
Well, Thanks a lot, encouragement is really what I needed.
Yeah, there are some really frustrating issues - even worse is when you
can't get the damn thing to assemble at all! But after a while, it gets
better... or you get used to it...
Best,
Frank
So the whole point is that I have to set the dx to zero before divide
operation. dx might have some initial value after 's0', that might
cause some problem? But what about the next execution, it also set by
initial value? In my original implementation the dx just set to zero in
each loop before program reaches to 's0'. isn't it right?
Sorry about that.

Continue reading on narkive:
Loading...