News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Hello and Help Request @ Basics

Started by Brinuz, April 01, 2011, 05:19:23 PM

Previous topic - Next topic

Brinuz

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)

Tedd

You need to store edx somewhere (another variable) and then use the offset of that variable - the .data section is simplest.
No snowflake in an avalanche feels responsible.

Brinuz

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)

dedndave

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\......."

raymond

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.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Brinuz

@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 ^^

mineiro

#6
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


dedndave

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

dedndave

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

Brinuz

Once again, Thanks for the useful information. Will take a deeper look into it and play around with it.
Thanks guys.

Brinuz

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.

dedndave

EAX is a dword register
you might try...
        mov     [edx],al

Brinuz

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

sinsi

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
Light travels faster than sound, that's why some people seem bright until you hear them.

Brinuz

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.. ^^