### Author Topic: I try to learn about esp and ebp  (Read 6309 times)

#### grimoire

• Jr. Member
• Posts: 6
##### I try to learn about esp and ebp
« on: August 15, 2013, 12:45:36 AM »
hi

I try to learn how use ebp and esp, but I've a problem with ascii and decimal

This is my code using nasm, is a calculator and everything works fine doing this.

Code: (asm) [Select]
`[Bits 32]global _startsection .datatit: db "Inicio de calculadora",10,10,0in: db "Escribe el nombre de la funcion",10,0buf1: times 24 db (0)section .textsumar:push ebpmov ebp, espsub esp, 8mov eax, 4mov ebx, 1lea ecx, [ebp + 8]mov edx, 4int 0x80mov eax, 3mov ebx, 1lea ecx, [ebp - 8]mov edx, 5int 0x80mov eax, 4mov ebx, 1lea ecx, [ebp - 8]mov edx, 5int 0x80mov esp, ebppop ebpret 8_start:mov eax, 4mov ebx, 1lea ecx, [tit]mov edx, 23int 0x80mov eax, 4mov ebx, 1lea ecx, [in]mov edx, 32int 0x80mov eax, 3mov ebx, 1lea ecx, [buf1]mov edx, 8int 0x80cmp dword[buf1], "suma"je sumajne finsuma:push dword[buf1]call sumarfin:mov eax, 1int 0x80`
But I tried to convert ascii to decimal with those lines.

Code: (asm) [Select]
`xor eax, eaxstrtodec:mov al, byte[ebp - 8]cmp al, 10je consub al, 48mov byte[buf2], alinc byte[ebp - 8]inc byte[buf2]jmp strtodec`

I tried to move 1 byte to [al], and check if is equal to 10 (NULL in ascii)
if is equal go to write, if not... sub 48 in [al] to convert to decimal

later move the decimal to [buf2] and incrementing after  [ebp] and [buf] to the next byte and repeat

when [al] is (null) go to write

but seems it doesn't work,  can somebody explain me how should I fix it?

Leave the code

Code: (asm) [Select]
`[Bits 32]global _startsection .datatit: db "Inicio de calculadora",10,10,0in: db "Escribe el nombre de la funcion",10,0buf1: times 24 db (0)buf2: times 20 db (0)section .textsumar:push ebpmov ebp, espsub esp, 8mov eax, 4mov ebx, 1lea ecx, [ebp + 8]mov edx, 4int 0x80mov eax, 3mov ebx, 1lea ecx, [ebp - 8]mov edx, 5int 0x80xor eax, eaxstrtodec:mov al, byte[ebp - 8]cmp al, 10je consub al, 48mov byte[buf2], alinc byte[ebp - 8]inc byte[buf2]jmp strtodeccon:mov eax, 4mov ebx, 1lea ecx, [buf2]mov edx, 5int 0x80mov esp, ebppop ebpret 8_start:mov eax, 4mov ebx, 1lea ecx, [tit]mov edx, 23int 0x80mov eax, 4mov ebx, 1lea ecx, [in]mov edx, 32int 0x80mov eax, 3mov ebx, 1lea ecx, [buf1]mov edx, 8int 0x80cmp dword[buf1], "suma"je sumajne finsuma:push dword[buf1]call sumarfin:mov eax, 1int 0x80`

#### Frank Kotler

• NASM Developer
• Hero Member
• Posts: 2667
• Country:
##### Re: I try to learn about esp and ebp
« Reply #1 on: August 15, 2013, 08:04:31 AM »
Hi Grimoire,

You seem to be using esp and ebp correctly, so I don't know what I can tell you about that. If you have questions, ask again... but I don't think that's a problem.

Your routine to convert ascii to decimal starts out well, but then I get confused...
Code: [Select]
`; ...sumar:push ebp ; save caller's ebpmov ebp, esp ; create a "stack frame"sub esp, 8 ; make a buffer on the stack; this just repeats "suma"; I don't know why, but it does no harmmov eax, 4mov ebx, 1lea ecx, [ebp + 8]mov edx, 4int 0x80; read some ascii into our buffermov eax, 3mov ebx, 1 ; should be 0 - stdin?lea ecx, [ebp - 8]mov edx, 5int 0x80xor eax, eaxstrtodec:mov al, byte[ebp - 8] ; get a charactercmp al, 10 ; linefeed - not really "NULL"je consub al, 48 ; convert a character to a number; this is fine unless the pesky user types; something other than '0' to '9'; you might want to check that we have; a "valid" character at this point, but okay...mov byte[buf2], al ; store the number in another bufferinc byte[ebp - 8]inc byte[buf2]`
Here's where I get confused. Suppose I typed "123". You get the character '1', convert it to the number 1, and store it in buf2. Then you increment the character '1' to '2', and increment the number 1 to 2. I think you want to move to the next character in the buffer. That would be at "[ebp - 7]", but I think you're better off to put the address of "[ebp - 8]" into a register, and increment that. Then, once we've got another valid character - but probably not before - we want to multiply the "result so far" by 10, and add in the next character (converted to a number, of course!). So the "result so far" is 12. Then when we get the '3', we convert it to 3, multiply the "result so far" by 10 again, and add in the 3. At this point we find the linefeed (also 10, by chance) so we're done and the result is 123.

The obvious way to "multiply by 10" is to use the "mul" instruction. This multiplies eax by the operand you provide - either a register or memory - and puts the result in edx:eax. No choice of the registers eax and edx!

The "imul" instruction is more complicated. With one operand, it is the same as "mul", only it handles signed numbers (could be negative). With three operands, it multiplies the second by the third, and puts the result in the first. This is a 32-bit x 32-bit = 32- bit operation, unlike "mul" and the one-operand "imul" which do 32 x 32 = 64. The two-operand form is just a special case of the three-operand form, in which the first and second operands are the same. Also, the third operand (or second) can be an immediate (signed byte), which we can't do with "mul".
Code: [Select]
`imul ebx, 10`will work, and may be just what you want.

When it comes to "add in the next digit", there may be a complication. We can't:
Code: [Select]
`add ebx, al`no matter how much we'd like to! We need to have the "next digit" in a 32-bit register, even though it would fit in a byte. It isn't a "problem", but we need to make sure that the upper bits are zero.

Besides "mul" and "imul" there's a "trick" using lea:
Code: [Select]
`lea ebx, [ebx + ebx * 4] ; ebx = ebx * 5lea ebx, [ebx * 2] ; multiplied by ten!`We can add in the "next digit" while we're at it:
Code: [Select]
`lea ebx, [ebx + ebx * 4]lea ebx, [eax + ebx * 2]`We can even do the "convert character to number" part:
Code: [Select]
`lea ebx, [ebx * 5] ; we can write it this way, toolea ebx, [eax + ebx * 2 - '0']`(I think writing it as '0' rather than 48 makes it clearer what the "purpose" of the number is - either will work). One disadvantage to this method is that there's no way to check for overflow using "lea". It's what I'm currently using, mostly because I think it's "cute". It was shown to me by Herbert Kleebauer, and was supposedly originally generated by a C compiler! Clever, those compilers (their authors, actually)!

So you can probably get that part working, one way or another. Then you go on to:
Code: [Select]
`; ...con:; this is going to print the decimal, not ascii; probably not useful!mov eax, 4mov ebx, 1lea ecx, [buf2]mov edx, 5int 0x80mov esp, ebp ; undo the "stack frame" pop ebp ; resore caller's ebpret 8`The "ret 8" is not correct! That will "remove" two parameter's from the stack, but we only pushed one! You could do "ret 4" here - that would make it a "stdcall" calling convention. Windows APIs use this, and if you were using Windows you'd be wise to do it the same way. Linux more commonly uses the "cdecl" calling convention, in which the caller removes the parameters. You might want to stick with that (either will work) End your subroutine with just "ret", and where you call it:
Code: [Select]
`push dword [buf1]call sumaradd esp, 4 ; "remove" the parameter;... `You could also "pop" a dummy register. Since you just sys_exit after this, it doesn't matter if you clobber the stack, but it isn't "right"!

I mentioned:
Code: [Select]
`mov eax, 3 ; sys_writemov ebx, 1 ; read from stdout?`
I once caught Randy Hyde doing this, and pointed it out to him. He explained that with both Dos and Unix, you can use either stdin or stdout with a read or write, and it'll do the right thing. I don't know why this works, but it does. He agreed that the code would be clearer if he used stdin (0) to read from. You might want to do the same (couple places). Not a big deal!

When you've got your number(s) and done a calculation, you need to convert back to ascii before you can display the result. It won't work putting the number in ecx, nor putting the address of a buffer containing the number in ecx. You'll need to add that 48 (or '0') back in... for each digit. There should be a post nearby called something like "integer in, integer out" that has an example, if you need it. That post will also point out that you can "just call scanf" and "just call printf" to do this.

I could attempt a working version of your code, but I'm not sure why you're passing "suma" to your subroutine. You probably have doing something further in mind...

Best,
Frank

#### grimoire

• Jr. Member
• Posts: 6
##### Re: I try to learn about esp and ebp
« Reply #2 on: August 16, 2013, 04:15:38 AM »
Ok, thank you Frank

I think I'm very accustomed still to programming in C, I will study more about convert bytes, is very similar to strcmp.

Meanwhile I used scanf and printf and it's working fine.

regards