Re: Completely confused. Please send help. :)
- From: Frank Kotler <spamtrap@xxxxxxxxxx>
- Date: Tue, 28 Nov 2006 12:35:44 -0500
Shay wrote:
Ok, I have looked in books and PDFs but cannot find what is going on here. This is going back to my itoa() code I posted previously. I modified it to take parameters on the stack rather than via registers. I can get it to work, but I don't understand how it's working.
More precisely, I don't think I fully comprehend how the stack works (especially with indirect referencing and offsets).
If I have some code like so (and please correct me if I messed up):
sub esp, 11 ; buffer
This is an "awkward" number to subtract from the stack. Better to keep the stack aligned to a four-byte boundary (at least). This isn't any part of "the problem", but it will result in lousy performance. I'd make the buffer size 16 - a "nice round number" - 12 would be enough... 11's enough, but it's "odd".
push dword 11 ; size of buffer - itoa() changes this to length of converted number
lea ebx, [esp+15] ; pointer to buffer since it can be variable size
push ebx
Okay, you're passing the "end" (high in memory) of the buffer. That's what "itoa" expects, as it's written. It might be "better" to pass the "front" (low in memory) of the buffer, and "advance" to the "end" within "itoa" - just to save the caller(s) from having to do the calculation every time. But that isn't "the problem"... yet.
push eax ; This has a number in it
call itoa
pop eax
pop ebx ; get the address of the buffer back
mov ecx, ebx ; data to write
Here's "the problem". You've still got the "top" of the buffer, which is not where you want to begin your "write"! Since you're (incorrectly) using "inc edi" in itoa, you've put the characters where you're "write"ing from - yes, you're clobbering the stack, but there's nothing "vital" there... in this case - but "backwards". You want to be using "dec edi" in itoa... but since there's a variable number of characters, we don't want to start the "write" at the beginning of the buffer - we'll need to return the address at which to start our "write"...
mov edx, [esp] ; length of data to write
Since we *do* return the length, we can do a "quick fix" by:
sub ecx, edx
(in addition to changing "inc edi" to "dec edi" in itoa)
add esp, 15 ; get rid of buffer and buffer_size
I wouldn't get rid of it until after I'd written it, but it does no harm.
;; write_to_stdout call ...
I basically want a C call like:
void itoa(long number, char* buffer, int* buffer_size);
Stack should look like this:
--------------------
Buffer space
--------------------
11
--------------------
Buffer space addr
--------------------
number to convert
--------------------
Return address
--------------------
Now itoa() is the following (this is in its own file):
---------------- CODE --------------------------
section .text
global itoa
itoa:
push ebp
mov ebp, esp
;; parameters on stack (number, buffer*, buffer_size*)
%define number [ebp+8]
%define buf_addr [ebp+12]
%define buf_size [ebp+16]
push ebx
push edx
push edi
; Get address of where to store return data
mov edi, buf_addr
; Number to stringify
mov eax, number
; Do we have a positive number?
cmp eax, 0
jge .ASSIGN
; If not, make it so
neg eax
.ASSIGN:
; Set up divisor
mov ecx, 10
; Counter for length to make sure we don't
; go past buffer_size bytes
mov ebx, 0
%macro CHECK_BUFFER_SIZE 0
; Check to make sure we don't overflow
; the size of buffer
;
cmp ebx, buf_size
je .RET
%endmacro
.DO.WHILE:
mov edx, 0
; [eax] is always being changed to what we
; need so no need to reassign it here
div ecx
add edx, 48
; Add next number to the sequence
mov [edi], dl
inc edi ; This doesn't seem right to me since the buffer space "grows" down into memory
Correct. Should be "dec".
inc ebx
CHECK_BUFFER_SIZE
; Do we need to loop?
cmp eax, 10
jge .DO.WHILE
; Account for a zero being passed in so
; we don't end up with "00"
cmp eax, 0
je .CHECK.NEG
; Store final digit
add eax, 48
mov [edi], al
inc edi
"dec"
inc ebx
CHECK_BUFFER_SIZE
.CHECK.NEG:
cmp dword number, 0
jge .RET
; Add the sign for negative numbers
mov byte [edi], '-'
inc edi
"dec" (do we really need this?)
inc ebx
.RET:
; Modify the buffer size parameter to be the
; number of bytes we wrote to buffer
mov buf_size, ebx
; Restore stack and kill local variables
pop edi
pop edx
pop ebx
mov esp, ebp
pop ebp
ret
---------------- END CODE --------------------------
Now this works, but the number comes out backwards (i.e. 13 == 31).
If I change the 'inc edi' to 'dec edi', I only get the last digit (i.e. 13 == 3).
As noted, your string *is* there... but you're only writing part of it (and some additional garbage).
It seems I cannot figure out how to return everything on the stack. If I return via [eax], I can get it to work fine with only a few changes, but I don't know if it's working correctly (as in I'm not stomping all over the stack).
You could put a known value on the stack, and make sure it's still there. One way to do this would be to make a dummy subroutine that calls itoa - if it returns properly... you haven't trashed *that* part of the stack, at least.
I tried debugging it, which is how I got this far, but the debugger (ald in this case) is confusing me. When I 'examine' edi, I get from some address (where the buffer starts for instance) to an address up in memory (i.e. +X).
This is the confusing part for me... when I set up the call to itoa(), I am pushing items down into the stack; memory addresses should get lower yes? So when I attempt to modify those addresses within itoa() should I not get the start address and 'dec' into the buffer? For example, if I do:
mov edi, buf_addr
This is moving the address of where my buffer starts (yes?). So if I then do:
dec edi (10 times)
should I not then be to where my "buffer_size" is sitting on the stack?
Well... I guess so.
Thanks for any help you can give this poor soul. I must say, trying to learn assembly has aged me a year in the month or so I've been at it.
By the first of the year, you'll be as old as Yoda - and maybe as wise! :)
You seem to have the concepts straight. You just confused yourself by starting the "write" at the wrong place. Other than that, your code works with "dec edi" - as you understand it "should be" to get the digits in the right order. Frustrating? Yeah, unless you learn to think of it as "amusement" :)
Best,
Frank
.
- References:
- Completely confused. Please send help. :)
- From: Shay
- Completely confused. Please send help. :)
- Prev by Date: Re: how to override _m_pshufw
- Next by Date: Interrupt Service Routine (ISR) Vs Subroutine, Differences
- Previous by thread: Re: Completely confused. Please send help. :)
- Next by thread: tralnslate sintax from nasm to gas
- Index(es):
Relevant Pages
|
|