Hello everyone. I'm glad i can join this community. (first post :p)
I would like to have some help tho. (Sorry for the first post here, but i guess we have to start somewhere)
Well, i learned about 16-bit ASM basics 2 years ago (was forced to...), but I can see that 16-bit ASM is pretty useless nowadays on windows. So I'm trying to re-learn everything I knew, and of course learn new things.
I faced myself with some problems, some of them i was able to solve by searching the web, this forum, etc. Others.. not really (Beginner).
So heres the problem.
I have been reading about registers (EAX,EBX,ECX,etc..) sizes and their main use, and variables (byte,word,dword).
I know a byte is 8 bits(byte), a word 16 bits(2bytes), and a dword 32bits (4bytes). And that these registers(EAX,etc..) have the total size of 4bytes (Where AX if the bottom 2bytes (or top?), which can be also divided into AL and AH, lower and higher Bytes of AX)
I'm trying to understand the basics of all this first, before anything else.
Okay i got this to print a simple string.
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
.data
messageStr db "Test",0
hStd dd ?
bytesOut dd ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
push 0
push offset bytesOut
push 10
push offset messageStr
push hStd
call WriteConsole
push 0
call ExitProcess
END START
Let's say I want to print the value in EDX instead of the value on the variable(byte) named messageStr.
My first thought would be:
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
.data
messageStr db "Test",0
hStd dd ?
bytesOut dd ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
MOV EDX, 84 ; 'T'
push 0
push offset bytesOut
push 10
push EDX
push hStd
call WriteConsole
push 0
call ExitProcess
END START
But i realized fast that it wouldn't work, since edx would have stored a decimal value ("T" in ASCII), instead of the address that the WinAPI needs as parameter.
Now i ask, how would i send the address of EDX instead of the value inside? (even to print a simple character)
I tried something like: DWORD PTR EDX, but it seems to fail. (Probably for obvious reason, but since I'm way too fresh on asm... you could understand :p)
PS: Please don't tell me to use MASM macros yet. I'm really trying to avoid them at the very beginning. As i want to learn as much as I can before I use any kind of "shortcut".
Thanks in the Advance. (Please keep in mind that i also seek some theory on how this works, so i ask you to correct anything I said that could be wrong)
You need to store edx somewhere (another variable) and then use the offset of that variable - the .data section is simplest.
Quote from: Tedd on April 01, 2011, 05:28:07 PM
You need to store edx somewhere (another variable) and then use the offset of that variable - the .data section is simplest.
Thanks for your reply, so i guess something like this would work.
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
.data
messageStr db "Test",0
character db ?
hStd dd ?
bytesOut dd ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
MOV DL, 84
MOV character, DL
push 0
push offset bytesOut
push 10
push offset character
push hStd
call WriteConsole
push 0
call ExitProcess
END START
I tried to save the character to the whole EDX, and realized that I couldn't reach it. Is it because it is saved on the the other 2 bytes, other that it's not DX? (DL,DH)
well - you also need to convert the binary integer in EDX into an ASCII decimal string so that it makes sense
and, i understand not wanting to use the macros in the beginning - it's a good way to learn
however, for a beginner, conversion is a little bit advanced
it requires knowledge of registers, routines, and so on, that a beginner may not have yet
you can use a masm32 library function or macro to perform this operation until you get your feet wet :bg
another good way to learn is to use the macro, but open the macros.asm file and the functions that it uses to see what they do
there are a few other things i noticed in your code that will slow you down
first, you are missing the msvcrt import library - some of the masm32 functions require it
also, you have a lot of typing at the beginning - that stuff is good to understand - not good to type
have a look at the file \masm32\include\masm32rt.inc
that file has most of the stuff you need already typed for you
so, at the beginning of the program...
INCLUDE \masm32\include\masm32rt.inc
and that will get you going
another thing that hit me right off...
push -11
call GetStdHandle
MOV hStd, EAX
it is easier to read if you...
INVOKE GetStdHandle,STD_OUTPUT_HANDLE
mov hStdOut,eax
oh - one more thing
i see that you have masm32 installed on the E: drive
the package is set up to work from the root of the current drive
so, if it is installed on E:, create your ASM files somewhere on the same drive
then use "\masm32\......."
QuoteI tried to save the character to the whole EDX, and realized that I couldn't reach it. Is it because it is saved on the the other 2 bytes, other that it's not DX? (DL,DH)
If you had declared the "character' variable as a dword (instead of a byte), you could then have saved the entire content of EDX in that variable.
Dave seems to have misunderstood the meaning of the value in the DL register (the ascii value of "T").
One thing you should learn is to understand the requirements of the parameters when you call one of the API functions (such as WriteConsole). If you don't have it already, get yourself a copy of the Win32 Programmer's Reference (WIN32.HLP).
The 3rd parameter for the WriteConsole function is the number of characters to be written to the console. When your test string is "Test", the passed parameter should be 4. When you store the value of DL in the "character" variable, the passed parameter should then be 1 if you want to print that single character. You should also realize that the offset of the string you pass as a parameter is strictly an address where the function will retrieve the required characters for display. It doesn't matter to the function how the data at that address was declared initially. For example,
mov edx,"tseT"
mov character,edx
and later writing 4 characters to console from the
character variable would produce "Test" on the console.
@raymond
Let me say that I'm pretty familiar with WinAPIs that windows has.(Using them at C/C++) I know that the 3rd param would be bytes to write. I have it 10 just for testing, actually it could be easier to count the size of the string and pass it as parameter, but i really didn't mind much for this kind of testing.
MSDN pretty much says everything i need about these APIs tho.
About the DWORD, yea it makes sense indeed. Stupid of me for not remembering it. =P
Yea i realized that it was the address (pointer as msdn says) but the initial problem was just to get the address of EDX (yea prob stupid, since it isn't actually a variable)
Thanks for the input Raymond. I appreciate it.
@dedndave
Thanks about masm32rt.inc, didn't know it =]
About the invoke and push, well since i learned to use push and call at 16bits, i guess that it was what made me use it also here. Plus i used -11 on the parameter, because on MSDN the param STD_OUTPUT_HANDLE is equivalent to -11. ^^
I agree that i shouldn't include the drive on the includes list because it will be harder for others or even me to compile it somewhere else (not hard, but i would need to change these.. And it could be painful if there were many)
Thanks too for the Input, I appreciate it.
PS: Sorry for not writing perfect English, but since my natural language ain't English, i can't do it perfectly yet ^^
Hello Sr Brinuz, and welcome.
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
.data
hStd dd ?
bytesOut dd ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
jmp that
here:
db "test",00h
here_size = $- here
that:
push 0
push offset bytesOut
push here_size
push offset here
push hStd
call WriteConsole
push 0
call ExitProcess
END START
with this in mind, you can write :
here:
MOV AL, "T" ;double sense, we are searching for the "T" and puting "T" in .code section, but the sizeof is static.
here_size = $ - here
mov ecx,here_size ;how many bytes we are search for
mov edi,offset here
repne scasb ;search for "AL" in offset EDI
dec edi
push 0
push offset bytesOut
push 1
push EDI
push hStd
call WriteConsole
you can also use the stack as a temporary data area
.XCREF
.NOLIST
INCLUDE \masm32\include\masm32rt.inc
.LIST
.DATA?
hStdOut dd ?
.CODE
_main PROC
INVOKE GetStdHandle,STD_OUTPUT_HANDLE
mov hStdOut,eax
push eax ;create a place on the stack for NumberOfCharsWritten
mov eax,esp ;EAX = address of NumberOfCharsWritten
mov edx,'T' ;EDX = ASCII 'T'
push edx ;put it on the stack
mov ecx,esp ;ECX = address of ASCII 'T' on stack
INVOKE WriteConsole,hStdOut,ecx,1,eax,0
pop edx ;recall the char
pop ecx ;recover NumberOfCharsWritten, balancing the stack
INVOKE ExitProcess,0
_main ENDP
END _main
the value need not be in EDX to push it
and, of course, you can output more than one character at a time
in this example, we display 'T', with a carriage return and line feed
also, the handle is still in EAX, so we use it
push eax ;create a place on the stack for NumberOfCharsWritten
mov edx,esp ;EDX = address of NumberOfCharsWritten
push 'T' or 0A0D00h ;ASCII 'T',13,10 on the stack
mov ecx,esp ;ECX = address of ASCII 'T' on stack
INVOKE WriteConsole,eax,ecx,3,edx,0
pop edx ;recall the chars
pop ecx ;recover NumberOfCharsWritten, balancing the stack
Once again, Thanks for the useful information. Will take a deeper look into it and play around with it.
Thanks guys.
Erm, not sure if I can double post, but there we go.
Ok, i have been trying to Convert a number into ascii, I guess the main "problem" is solved tho. A lil bit of search and I managed to get the logic for this conversion.
(Divide by 10, get the reminder, add 48 decimal to it and there we go, I have the correct ASCII Code of the number)
Well this is not the problem.
I created a loop to check each number, save it on the stack, for later use. Thing is I'm not sure how to save it from the stack into somewhere else. Here's the code I tried:
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
.data
messageStr dd 1337,0
number db ?
lenght dd ?
hStd dd ?
bytesOut dd ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
MOV ECX, 10
MOV EAX, messageStr
MOV EBX, 0
convertNum:
MOV EDX, 0
DIV ECX
CMP EDX, 0
JE convertEnd
push EDX
INC EBX
JMP convertNum
convertEnd:
;This will now have the number on the stack (1,3,3,7,...)
;EBX will have the number lengh
MOV lenght, EBX
MOV EDX, offset number
saveNumber:
POP EAX ;recover one number from the stack to EAX and then save it
ADD EAX, 48 ;To convert it into the correct ASCII code
MOV BYTE PTR [EDX], EAX ;Save the number to the byte pointed by EDX
INC EDX ;Next byte to save the next number
DEC EBX ;Counter--
CMP EBX, 0
JNE saveNumber
saveEnd:
push 0
push offset bytesOut
push lenght
push offset number
push hStd
call WriteConsole
push 0
call ExitProcess
END START
Well, i get an error at the line (Invalid Instruction):
MOV BYTE PTR [EDX], EAX ;Save the number to the byte pointed by EDX
But since i can't do this to save into a place in memory (variable) the value in EAX (which is one of the characters from the whole number which was in the stack)
How could I do this?
Once again, Thanks in advance, and sorry for the spelling mistakes.
EAX is a dword register
you might try...
mov [edx],al
Quote from: dedndave on April 04, 2011, 02:03:25 PM
EAX is a dword register
you might try...
mov [edx],al
Why do i always miss the most obvious stuff. Of course. Thanks again ._.
Can't get 4Bytes into 1byte..
EDIT: And just saw another error, I'm writting to number plus the address's (EDX+1) on front of it. And these are actually being used (Handle,Lenght,BytesWritten).
Okay, here's the working "solution" that i got into
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
.data
messageStr dd 1337,0
hStd dd ?
bytesOut dd ?
number db ?
.code
START:
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
MOV ECX, 10
MOV EAX, messageStr
MOV EBX, 0
convertNum:
MOV EDX, 0
DIV ECX
CMP EDX, 0
JE convertEnd
push EDX
INC EBX
JMP convertNum
convertEnd:
;This will now have the number on the stack (1,3,3,7,...)
;EBX will have the number lengh
MOV ECX, EBX
MOV EDX, offset number
saveNumber:
POP EAX ;recover one number from the stack to EAX and then save it
ADD EAX, 48 ;To convert it into the correct ASCII code
MOV BYTE PTR [EDX], AL ;Save the number to the byte pointed by EDX
INC EDX ;Next byte to save the next number
DEC EBX ;Counter--
CMP EBX, 0
JNE saveNumber
saveEnd:
push 0
push offset bytesOut
push ECX
push offset number
push hStd
call WriteConsole
push 0
call ExitProcess
END START
You should be testing eax for zero, not edx, and you should really push edx and inc ebx before testing otherwise you will pop something that wasn't pushed (e.g. eax=0)
@@:
sub edx,edx
div ecx
push edx
inc ebx
test eax,eax
jz @b
@@:
pop eax
...
dec ebx
jnz @b
Quote from: sinsi on April 04, 2011, 02:25:17 PM
You should be testing eax for zero, not edx, and you should really push edx and inc ebx before testing otherwise you will pop something that wasn't pushed (e.g. eax=0)
@@:
sub edx,edx
div ecx
push edx
inc ebx
test eax,eax
jz @b
@@:
pop eax
...
dec ebx
jnz @b
indeed I changed it, after I realized that it wouldn't accept 0's correctly. And figured that out ^^ Thanks tho.
convertNum:
MOV EDX, 0
DIV ECX
push EDX
INC EBX
CMP EAX, 0
JE convertEnd
JMP convertNum
convertEnd:
or I could use JNE there instead of these 2.. ^^
probably without knowing it, you have applied Horner's Rule (or what i like to call Ling Long Kai Fang)
some time ago, i wrote a set of functions that will convert integers of any practical size to decimal string
http://www.masm32.com/board/index.php?topic=12363.msg94779#msg94779
LLKF9_1 has three procs, one for signed, one for unsigned, and one that is mode selectable
it performs base conversion on up to 9 digits at a time, then converts them to ASCII, 4 digits at a time
@dedndave
Yea i didn't know it =o I will give a look at your code and see what I can learn from it ^^
@
Okay after learning a bit in this topic. I decided to create one "app" that will output every friday 13th from the year 1900 to the year 2000.
I know that the code isn't nearly to perfect. But that's why I'm posting it here. In search of Tips on what to improve and why. :p
.386
.model flat, stdcall
option casemap:none
include E:\masm32\include\windows.inc
include E:\masm32\include\kernel32.inc
include E:\masm32\include\masm32.inc
include E:\masm32\include\user32.inc
includelib E:\masm32\lib\user32.lib
includelib E:\masm32\lib\kernel32.lib
includelib E:\masm32\lib\masm32.lib
GetMonthMax PROTO month:DWORD,year:DWORD
.data
hStd dd ?
bytesOut dd ?
monthsTOne dd 1,3,5,7,8,10,12,0
monthsTZero dd 4,6,9,11,0
currentDay dd ?
currentMonth dd ?
currentYear dd ?
total dd 0
number dd ?
.code
;EAX will have the days of the given month on the given year.
GetMonthMax PROC month:DWORD,year:DWORD
MOV EDX, month
;Check if it is 31 days
MOV EBX, offset monthsTOne
TOne:
CMP DWORD PTR [EBX], EDX
JE endTOne
ADD EBX, 4
CMP DWORD PTR [EBX], 0
JE continueTOne
JMP TOne
endTOne:
MOV EAX, 31
JMP endProc
continueTOne:
; -------------------------------------------------------------------------
;Check if it is 30 days
MOV EBX, offset monthsTZero
TZero:
CMP DWORD PTR [EBX], EDX
JE endTZero
ADD EBX, 4
CMP DWORD PTR [EBX], 0
JE continueTZero
JMP TZero
endTZero:
MOV EAX, 30
JMP endProc
continueTZero:
; -------------------------------------------------------------------------
;Check if it is 28/29 days
MOV EAX, year
MOV ECX, 4
DIV ECX
CMP EDX, 0 ;Check if Year % 4 Equals 0
JNE endTFeb28
MOV EAX, year
MOV ECX, 100
DIV ECX
CMP EDX, 0 ;Check if Year % 100 Equals 0
JNE endTFeb
MOV EAX, year
MOV ECX, 400
DIV ECX
CMP EDX, 0 ;Check if Year % 400 Equals 0
JE endTFeb28
endTFeb:
MOV EAX, 29
JMP endProc
endTFeb28:
MOV EAX, 28
endProc:
ret
GetMonthMax ENDP
START:
MOV EDX, 5 ;Starting day of 1900 year
MOV ECX, 1900 ;Years - Starts at 1900, Ends at 2000
yearsLoop:
MOV EBX, 1 ;Months - Starts at 1, Ends at 12
monthsLoop:
infLoop:
CMP EDX, 13
JNE continueInfLoop
push EAX ;Uses EAX as a temporary store, saving the current value of it in the stack
MOV EAX, total
INC EAX
MOV total, EAX
POP EAX ;Sets EAX value back from the stack
continueInfLoop:
ADD EDX, 7 ;Adds 7 days to the current day
MOV currentDay, EDX ;saves the current day
MOV currentMonth, ECX ;saves the current month
MOV currentYear, EBX ;saves the current year
push ECX
push EBX
call GetMonthMax
MOV ECX, currentMonth ;Restore from the variable
MOV EBX, currentYear ;Restore from the variable
MOV EDX, currentDay ;Restore from the variable
CMP EDX, EAX
JL infLoop
SUB EDX, EAX
endInfLoop:
INC EBX ;Next month
CMP EBX, 13
JNE monthsLoop
endMonthsLoop:
INC ECX ;Next year
CMP ECX, 2001
JNE yearsLoop
endYearsLoop:
; -------------------------------------------------------------------------
;OUTPUT
push -11
call GetStdHandle
MOV hStd, EAX
MOV ECX, 10
MOV EAX, total
MOV EBX, 0
;Convert from Decimal to ASCII
convertNum:
MOV EDX, 0
DIV ECX
push EDX
INC EBX
CMP EAX, 0
JNE convertNum
convertEnd:
;Stack stores the number now in the correct order
;EBX will have the number lengh
MOV ECX, EBX
MOV EDX, offset number
saveNumber:
POP EAX ;recover one number from the stack to EAX and then save it
ADD EAX, 48 ;To convert it into the correct ASCII code
MOV BYTE PTR [EDX], AL ;Save the number to the byte pointed by EDX
INC EDX ;Next byte to save the next number
DEC EBX ;Counter--
CMP EBX, 0
JNE saveNumber
saveEnd:
;Write to Console
push 0
push offset bytesOut
push ECX
push offset number
push hStd
call WriteConsole
;Exit Process
push 0
call ExitProcess
END START
Mainly i want a tip on how to avoid the need to save every register on a variable before using them on the procedure.
continueInfLoop:
ADD EDX, 7 ;Adds 7 days to the current day
MOV currentDay, EDX ;saves the current day
MOV currentMonth, ECX ;saves the current month
MOV currentYear, EBX ;saves the current year
push ECX
push EBX
call GetMonthMax
MOV ECX, currentMonth ;Restore from the variable
MOV EBX, currentYear ;Restore from the variable
MOV EDX, currentDay ;Restore from the variable
CMP EDX, EAX
JL infLoop
SUB EDX, EAX
endInfLoop:
well - i dunno if my code is a good example :P
whenever i post anything in here, they seem to tell me i am stupid
Hi Brinuz,
replace this:
MOV EAX, year
by this:
xor edx, edx
MOV EAX, year
because «div ECX» divide EDX:EAX by ECX and not only EAX by ECX !!! Ok ?
Quote from: dedndave on April 08, 2011, 11:40:03 AM
well - i dunno if my code is a good example :P
whenever i post anything in here, they seem to tell me i am stupid
i'm pretty sure that for me it will be i guess? ;)
Quote from: RuiLoureiro on April 08, 2011, 11:41:45 AM
Hi Brinuz,
replace this:
MOV EAX, year
by this:
xor edx, edx
MOV EAX, year
because «div ECX» divide EDX:EAX by ECX and not only EAX by ECX !!! Ok ?
I see, because of the 64 bits / 32 bits division. Thank you. Sure it is something i should do.
«Mainly i want a tip on how to avoid the need to save every register on a
variable before using them on the procedure.»
Replace this:
continueInfLoop:
ADD EDX, 7 ;Adds 7 days to the current day
MOV currentDay, EDX ;saves the current day
MOV currentMonth, ECX ;saves the current month
MOV currentYear, EBX ;saves the current year
push ECX
push EBX
call GetMonthMax
MOV ECX, currentMonth ;Restore from the variable
MOV EBX, currentYear ;Restore from the variable
MOV EDX, currentDay ;Restore from the variable
CMP EDX, EAX
JL infLoop
SUB EDX, EAX
endInfLoop:
By this:
continueInfLoop:
ADD EDX, 7 ;Adds 7 days to the current day
;MOV currentDay, EDX ;saves the current day
;MOV currentMonth, ECX ;saves the current month
;MOV currentYear, EBX ;saves the current year
push edx
push ecx
push ebx
push ECX
push EBX
call GetMonthMax
;MOV ECX, currentMonth ;Restore from the variable
;MOV EBX, currentYear ;Restore from the variable
;MOV EDX, currentDay ;Restore from the variable
pop ebx
pop ecx
pop edx
CMP EDX, EAX
JL infLoop
SUB EDX, EAX
endInfLoop:
AND
replace this:
endTFeb:
MOV EAX, 29
JMP endProc
by this:
endTFeb:
MOV EAX, 29
ret
I was kinda afraid of doing that, because i could probably mess up with the call.
OR
replace this:
GetMonthMax PROC month:DWORD,year:DWORD
by this
GetMonthMax PROC month:DWORD,year:DWORD
push edx
push ecx
push ebx
AND
replace this:
endProc:
ret
GetMonthMax ENDP
by this:
endProc:
pop ebx
pop ecx
pop edx
ret
GetMonthMax ENDP
Ok I understand the logic there. It could work I guess ;D
Thank you once again. Tips to improve are always welcome
Quote from: Brinuz on April 08, 2011, 01:39:59 PM
Ok I understand the logic there. It could work I guess ;D
Thank you once again. Tips to improve are always welcome
Ok, using the last hint GetMonthMax doesnt destroy EDX, ECX and EBX
and we dont need to save on a variable
Hi Brinuz,
There is an error here:
.data
...
number dd ? <<<---- here
----------------------------------
Replace this:
number dd ?
By this:
number db 20 dup (0)
Why ? number is a buffer for ascii digits