I feel a little disappointed, trying do write a very simple
routine in MASM32 and getting a lot of errors during assembling.
Here is the code. How many errors did I make?
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Dividing a number by ten and display result and remainder
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
.data
dd div_result 0
dd remain 0
dd ten 10
dd num 17345
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
xor edx, edx
mov eax, num
mov ecx, ten
idiv ecx
mov div_result, eax
mov remain, edx
mov ecx, num
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; display results
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
print "Original number: ", str$(ecx), 13, 10, 13, 10
print "Number after division by ten: ",str$(eax),13,10,13,10
print "Remainder: ",str$(edx),13,10
inkey "Press any key to close..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
For starters, your data should be in this format.
Name size Value
ex.
Number dw 1234
Quote from: Magnum on July 22, 2010, 11:28:19 PM
For starters, your data should be in this format.
Name size Value
ex.
Number dw 1234
OK thanks, I changed in this way:
div_result dw 0
remain dw 0
ten dw 10
num dw 17345
Still a lot of errors, maybe less than before. :P
What else I made wrong?
Well according to masm I have to define the variables in this way:
div_result dd 0
remain dd 0
ten dd 10
num dd 17345
And the errors are in the use of the macro
print.
I don't believe idiv gives you a remainder, just the quotient, but I do not use idiv so I may be wrong.
Quote from: KeepingRealBusy on July 22, 2010, 11:38:49 PM
I don't believe idiv gives you a remainder, just the quotient, but I do not use idiv so I may be wrong.
The error is in the use of
print macro the remain of the code
should be fine now.
I looked it up, idiv does return a remainder.
Dave.
Quote from: KeepingRealBusy on July 22, 2010, 11:44:06 PM
I looked it up, idiv does return a remainder.
Dave.
Yes Dave, That is correct. I have only to understand what I did
wrong with the
print macro.
print expects dwords, i.e. dd, not dw
in addition, you can't use literal text followed by str$. Split it as follows:
print "Original number: "
print str$(ecx), 13, 10, 13, 10
print "Number after division by ten: "
print str$(eax),13,10,13,10
print "Remainder: "
print str$(edx),13,10
inkey "Press any key to close..."
Quote from: jj2007 on July 22, 2010, 11:46:24 PM
print expects dwords, i.e. dd, not dw
in addition, you can't use literal text followed by str$. Split it as follows:
print "Original number: "
print str$(ecx), 13, 10, 13, 10
print "Number after division by ten: "
print str$(eax),13,10,13,10
print "Remainder: "
print str$(edx),13,10
inkey "Press any key to close..."
Great, finally it assembled! but the result is very poor:
Original number: -1040318464
Number after division by ten: 30
Remainder: 1170728
Press any key to close...
Something is still wrong. ::)
OK I found the error, the correct version is:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Dividing a number by ten and display result and remainder
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
.data
div_result dd 0
remain dd 0
ten dd 10
num dd 17345
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
xor edx, edx
mov eax, num
mov ecx, ten
idiv ecx
mov div_result, eax
mov remain, edx
mov ecx, num
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; display results
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
print "Original number: "
print sstr$(num), 13, 10, 13, 10
print "Number after division by ten: "
print sstr$(div_result),13,10,13,10
print "Remainder: "
print sstr$(remain),13,10,13,10
inkey "Press any key to close..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Now let's move a step further, I'm still very confused about declaring and
calling procedures in MASM.
I know we can push the parameters to pass, and pop them, or we can pass
them directly in the calling.
But I am not able to do either of them.
Could someone please use this code and make a procedure with the
instructions to divide, and the call to it passing the parameters?
Thanks
you can combine the print and chr$ macros to handle literal strings
it is convenient, but you can't make multiple use of the string
print chr$('Hello',32,'World',13),10
i tried to show different ways to combine ASCII numerics, as well
the chr$() macro actually creates the null-terminated string and returns its' address for the print macro
here is one way to make the proc...
IDivide PROTO :DWORD,:DWORD
IDivide PROC dwDividend:DWORD,dwDivisor:DWORD
;Returns: EAX = Quotient
; EDX = Remainder
mov eax,dwDividend
xor edx,edx
idiv dword ptr dwDivisor
ret
IDivide ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Dividing a number by ten and display result and remainder
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
IDivide PROTO :DWORD,:DWORD
INCLUDE \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Data Area
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.DATA
num dd 17345
.DATA?
div_result dd ?
remain dd ?
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Code Area
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.CODE
_main PROC
;divide "num" by 10
INVOKE IDivide,num,10
mov div_result,eax
mov remain,edx
; display results
print chr$('Dividend: ')
mov eax,num
print str$(eax),13,10,13,10
print chr$('Quotient: ')
mov eax,div_result
print str$(eax),13,10,13,10
print chr$('Remainder: ')
mov eax,remain
print str$(eax),13,10,13,10
;wait for a keypress and terminate
inkey "Press any key to close..."
exit
_main ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Signed Integer Division Routine
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
IDivide PROC dwDividend:DWORD,dwDivisor:DWORD
;Returns: EAX = Quotient
; EDX = Remainder
mov eax,dwDividend
xor edx,edx
idiv dword ptr dwDivisor
ret
IDivide ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
END _main
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Thanks Dave, it looks pretty clear :U
So when I declare the procedure prototype I only declare the generic
data type to be passed:
IDivide PROTO :DWORD,:DWORD
Any reason it is before the include:
IDivide PROTO :DWORD,:DWORD
INCLUDE \masm32\include\masm32rt.inc
or it is the same?
When I call the procedure I just use the names of the variables:
INVOKE IDivide,num,10
And the procedure is defined with both data type and name of variables:
IDivide PROC dwDividend:DWORD,dwDivisor:DWORD
......
IDivide ENDP
Till now it looks pretty much like the C way of doing the same thing,
except I don't know if you can declare a return value as well or you just
take care of the address, registers to pass.
Well, now a little doubt about the difference between call and invoke.
Are they the same or they use different syntax and are used in different ways
for different purposes?
And the final doubt: if I use the old way of pushing and popping the
data between the calls by myself, how would I do that in this specific case? ::)
Thanks for your patience.
Edit: a problem during assemblying time:
Microsoft (R) Macro Assembler Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Assembling: C:\masm32\examples\dividebythe_dave.asm
C:\masm32\examples\dividebythe_dave.asm(5) : error A2119:language type must be s
pecified
C:\masm32\examples\dividebythe_dave.asm(62) : error A2112:PROC and prototype cal
ling conventions conflict
_
Assembly Error
. . .
Well I had to change the order of include and proc prototype
in order to have it compiling successfully:
INCLUDE \masm32\include\masm32rt.inc
IDivide PROTO :DWORD,:DWORD
And now it works fine:
Dividend: 17345
Quotient: 1734
Remainder: 5
Press any key to close...
I discovered a thread from 2004 that deals with call and invoke
better if I have a look:
call vs invoke (http://www.masm32.com/board/index.php?topic=239.0")
:P
yes - i should have caught that :bg
but, i am pretty sure the PROTO must come before the INVOKE
PROTO is the difference between CALL and INVOKE (other than how the parms are pushed on the stack)
for INVOKE, the name must be pre-defined - not so for CALL
i used the constant 10, only because it is always the same value in this case
and, i wanted to demonstrate that a constant or a variable can be used as a parm
Quote from: dedndave on July 23, 2010, 06:54:54 AM
yes - i should have caught that :bg
but, i am pretty sure the PROTO must come before the INVOKE
PROTO is the difference between CALL and INVOKE (other than how the parms are pushed on the stack)
for INVOKE, the name must be pre-defined - not so for CALL
i used the constant 10, only because it is always the same value in this case
and, i wanted to demonstrate that a constant or a variable can be used as a parm
Thanks Dave. Your example was so clean, beginner friendly and somewhat
perfect that inverting the position of
include and
proto was
negligible stuff. :P
here is an exercise for you
the results display is essentially the same code, repeated 3 times
you could put that code in a PROC, and use INVOKE with the address of the string and the value to display
or, you could just make a simple loop to display the 3 lines
you could do both :bg
although, if you put it in a loop, there may not be much advantage to making a PROC, also
Quote from: dedndave on July 23, 2010, 02:29:01 PM
here is an exercise for you
the results display is essentially the same code, repeated 3 times
you could put that code in a PROC, and use INVOKE with the address of the string and the value to display
or, you could just make a simple loop to display the 3 lines
you could do both :bg
although, if you put it in a loop, there may not be much advantage to making a PROC, also
Good suggestion Dave, as I find some free time and neurons in my disconnected brain
I'm goint to give it a try. :P
you make a C proc like so
whatever proto C :DWORD
whatever proc C val1:DWORD
ret
whatever endp
good thing about about the c calling convention(cdecl) is the caller is the one who adjusts the stack instead of the function itself like stdcall(what the regular procs use), meaning you can specify unlimited parameters. All windows api is stdcall minus wsprintf, i'm not sure why windows went with stdcall in 32bit, for 16bit they used pascal calling convention which is the same thing as cdecl only the params are pushed the opposite direction.
Quote from: E^cube on July 23, 2010, 02:42:39 PM
you make a C proc like so
whatever proto C :DWORD
whatever proc C val1:DWORD
ret
whatever endp
good thing about about the c calling convention(cdecl) is the caller is the one who adjusts the stack instead of the function itself like stdcall(what the regular procs use), meaning you can specify unlimited parameters. All windows api is stdcall minus wsprintf, i'm not sure why windows went with stdcall in 32bit, for 16bit they used pascal calling convention which is the same thing as cdecl only the params are pushed the opposite direction.
Thanks E^cube.
It would be better if you add some more lines of asm code in order to make
me understand what you mean, I'm not that smart for the time being. :P
a cdecl function looks like this
push p3
push p2
push p1
call myfunc
add esp, 12 <---see the stack is adjust after the call
myfunc proc C p1,p2,p3
ret
myfunc endp
a stdcall function looks like
push p3
push p2
push p1
call myfunc
myfunc proc p1,p2,p3
;whatever
retn 12 <---see the stack is adjusted inside, u don't need to do this yourself, unless u turn off the epilog, as masm does it for u
myfunc endp
Quote from: E^cube on July 23, 2010, 02:54:29 PM
a cdecl function looks like this
push p3
push p2
push p1
call myfunc
add esp, 12 <---see the stack is adjust after the call
myfunc proc C p1,p2,p3
ret
myfunc endp
a stdcall function looks like
push p3
push p2
push p1
call myfunc
myfunc proc p1,p2,p3
;whatever
retn 12 <---see the stack is adjusted inside, u don't need to do this yourself, unless u turn off the epilog, as masm does it for u
myfunc endp
This is a way of using something C-like inside MASM you mean.
OK. Thanks.
And here we have the exercise my assembly master Dave
gave me:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Dividing a number by ten and display result and remainder
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
INCLUDE \masm32\include\masm32rt.inc
IDivide PROTO :DWORD,:DWORD
Display_num PROTO :DWORD,:DWORD
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Data Area
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.DATA
num dd 17345
str1 db "Dividend: ",0
pstr1 dd str1
str2 db "Quotient: ",0
pstr2 dd str2
str3 db "Remainder: ",0
pstr3 dd str3
.DATA?
div_result dd ?
remain dd ?
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Code Area
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.CODE
_main PROC
;divide "num" by 10
INVOKE IDivide,num,10
mov div_result,eax
mov remain,edx
Invoke Display_num,num,pstr1
Invoke Display_num,div_result,pstr2
Invoke Display_num,remain,pstr3
;wait for a keypress and terminate
inkey "Press any key to close..."
exit
_main ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; ; display results
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Display_num PROC numx:DWORD,ptrx:DWORD
print ptrx
mov eax,numx
print str$(eax),13,10,13,10
ret
Display_num ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Signed Integer Division Routine
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
IDivide PROC dwDividend:DWORD,dwDivisor:DWORD
;Returns: EAX = Quotient
; EDX = Remainder
mov eax,dwDividend
xor edx,edx
idiv dword ptr dwDivisor
ret
IDivide ENDP
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
END _main
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
and strange enough it seems to work :P
very nice, Frank :U
don't need the pointers, though...
str1 db "Dividend: ",0
str2 db "Quotient: ",0
str3 db "Remainder: ",0
;
;
;
Invoke Display_num,num,offset str1
Invoke Display_num,div_result,offset str2
Invoke Display_num,remain,offset str3
if you were to use the loop method, then you might want the pointers in a table
Quote from: dedndave on July 23, 2010, 11:00:18 PM
don't need the pointers, though...
str1 db "Dividend: ",0
str2 db "Quotient: ",0
str3 db "Remainder: ",0
;
;
;
Invoke Display_num,num,offset str1
Invoke Display_num,div_result,offset str2
Invoke Display_num,remain,offset str3
if you were to use the loop method, then you might want the pointers in a table
Well That is an improvement as well. :U and a new thing to keep in
mind. I was thinking about the loop and the table for the pointers, but
I'm not sure how to do it.
Could you show me how to apply that modification to the code?
Thanks
here is one way...
DsplyTbl dd str1
num dd 17345
dd str2
div_result dd ?
dd str3
remain dd ?
str1 db "Dividend: ",0
str2 db "Quotient: ",0
str3 db "Remainder: ",0
;
;
;
mov edx,offset DsplyTbl
mov ecx,3
Dsply: push ecx
push edx
mov eax,[edx]
print eax
mov edx,[esp]
mov eax,[edx+4]
print str$(eax),13,10,13,10
pop edx
pop ecx
add edx,8
dec ecx
jnz Dsply
after looking at it, the INVOKE method looks a lot cleaner :lol
Quote from: dedndave on July 23, 2010, 11:14:59 PM
here is one way...
DsplyTbl dd str1
num dd 17345
dd str2
div_result dd ?
dd str3
remain dd ?
str1 db "Dividend: ",0
str2 db "Quotient: ",0
str3 db "Remainder: ",0
;
;
;
mov edx,offset DsplyTbl
mov ecx,3
Dsply: push ecx
push edx
mov eax,[edx]
print eax
mov edx,[esp]
mov eax,[edx+4]
print str$(eax),13,10,13,10
pop edx
pop ecx
add edx,8
dec ecx
jnz Dsply
after looking at it, the INVOKE method looks a lot cleaner :lol
Yeah, the procs tend to organize the code in a better fashion, but
it is good to know there are less elegant ways too. :P
Thanks for this example. :U
Is there any reason you intermixed the pointers with the
numbers putting them before the numbers themselves?
DsplyTbl dd str1
num dd 17345
dd str2
div_result dd ?
dd str3
remain dd ?
Would it be the same if the pointers are put after the numeric variables?
in the loop - the string pointer is used first, is all
they could be swapped with no real difference
the loop would need to be modified slightly - so the string pointer is [edx+4] and the value is [edx]
Quote from: dedndave on July 23, 2010, 11:25:27 PM
in the loop - the string pointer is used first, is all
they could be swapped with no real difference
the loop would need to be modified slightly - so the string pointer is [edx+4] and the value is [edx]
Well it is a question of correctly addressing the locations of memory
we need, so if we change the position, the offset changes accordingly.
Good - thanks
your INVOKE code is still cleanest - lol
Quote from: dedndave on July 23, 2010, 11:34:00 PM
your INVOKE code is still cleanest - lol
That is because I don't understand it otherwise :lol