Hello.. I am a complete newb when it comes to assembly programming and all this windows creation stuff is a little over my head at the moment..

I have a couple of questions tho, hope you can help :)

1. I want to be able to output in a simple manner.. how do I make the program output into the command line instead of having to create a whole window and so on to be able to get some output... something similar to C's cout function.

2. Is it impossible to use dos interrupts under windows xp? Most of the tutorials I have looked at on the web deal with them (like int 21h) but I cant seem to be able to run them under windows.. is this a limitation or are they just overall not needed anymore?

3. Do you know some site with useful beginner knowledge.. The sites I have checked out all have the same info (how to count in boolean and hex, how the memory works, a couple of opcodes) but what about the rest.. like macros? (what the hell are they anyway :P) Procedures? and so on..

Hope for lots of replys, peace out. 8)
Posted on 2006-06-15 08:03:25 by talmir
Hi,

1) If using MASM32, use the StdOut function in it's library. If not, you can use the GetStdHandle and WriteFile API functions.

2) No, interrupts are off limits under all 32-bit versions of windows (for 32-bit code). 16-bit code used them under DOS, but that is a long time ago now. They are not needed anymore and this is a blessing as the Windows API is much easier to use and much more powerful.

3) I reccommend Iczelion's tutorials for most of your needs: http://win32assembly.online.fr/tutorials.html

If you have any more questions, people here will be happy to help, I'm sure.

Ossa
Posted on 2006-06-15 08:17:05 by Ossa
Welcome onboard! :)


#1: what you want to make is a "console mode" application. You can then use ReadFile and WriteFile on the console, like you could use similar functions in C/C++. First, you need to call GetStdHandle to obtain STD_INPUT_HANDLE (for read) and STD_OUTPUT_HANDLE (for write).

ReadFile and WriteFile take a lot of arguments, so it's a good idea to write some wrappers that are simpler to call.

#2: yes, you cannot use dos interrupts from a windows application. You can use them from a DOS application running under windows, but I assume you're interested in writing native windows applications. So, unfortunately, you have to dismiss most of the stuff from the 16bit days.

#3: http://madwizard.org/view.php?page=tutorials.contents for a quick introduction to 32bit assembly under windows. http://win32asm.cjb.net/ for Izelion's classical "how to write windows applications in assembly" tutorials (note that these focus on GUI, not console).

Macros... are a feature of your assembly. Very basic macros are just text substitution, but there's a lot more complex uses.
Posted on 2006-06-15 08:21:54 by f0dder
Ok, I'm trying to make do with the WriteConsole function and so far the code looks like this (unfinished)

ConOut MACRO msg
LOCAL @msg
.data
@Message db msg
ConsoleHandle dd ?
CharCount db ?
.code
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov ,eax
invoke WriteConsole,,addr @Message,NumChars,CharCount,0
ENDM

The problem I have is that Macros dont seem to allow two variables to be declared... Is there another way to count the characters in the string that I input?
Posted on 2006-06-17 11:45:23 by talmir


ConOut MACRO msg
LOCAL @msg
.data
  @Message db msg
  ConsoleHandle dd ?
  CharCount db ?
.code
  invoke GetStdHandle, STD_OUTPUT_HANDLE
  mov ,eax
  invoke lstrlen,addr @Message
  mov ,eax
  invoke WriteConsole,,addr @Message,NumChars,CharCount,0
ENDM



Now I don't use anything like WriteConsole and so I don't know why there is a NumChars and CharCount
as those seem to be the same to me. But who knows, lstrlen is your string count API you can use
to get the count of a String.

Zcoder....
Posted on 2006-06-17 11:56:09 by Zcoder
This api is similar to a disk write or a socket write, it MAY NOT write as much as you told it to..
The second-last param is meant to be a pointer to a dword variable which receives the #chars that actually got written.
You can compare this to the #chars you intended to write, and make sure that winbloze actually wrote as much as you told it to.
I don't know why you insist on wrapping this as a macro, it makes more sense to wrap it as a proc.


ConOut MACRO msg
LOCAL @msg
.data
  @Message db msg
  pMessage dd offset @Message
  ConsoleHandle dd ?
  NumChars db ?
  DidWrite dd ?
.code
  invoke GetStdHandle, STD_OUTPUT_HANDLE
  mov ,eax
  invoke lstrlen,addr @Message
  mov ,eax
@@: ;re-entry point for incomplete writes (see below)
  invoke WriteConsole,,pMessage,NumChars,addr DidWrite,0
  .if eax==TRUE ;successful api call, but was all data written?
    mov eax,NumChars ;we better check to make sure...
    .if eax>DidWrite ;incomplete write !!!
          mov edx,DidWrite
          add ,edx ;move pointer to unwritten data
          sub eax,edx          ;calculate #bytes remaining to write
          mov ,eax
          jmp @B  ;go write some more
    .else
        mov eax,TRUE
    .endif
.endif
ENDM


If our macro completes with eax=TRUE, all is well.
If eax=FALSE, something went horribly wrong :P
Posted on 2006-06-17 14:03:26 by Homer
I dont know how to make a proc so I went in the macro direction :P
Posted on 2006-06-17 14:18:40 by talmir
For generic purposes, I would recommend using WriteFile instead of WriteConsole. Quoting MSDN:

Although WriteConsole can be used only with a console screen buffer handle, WriteFile can be used with other handles (such as files or pipes). WriteConsole fails if used with a standard handle that has been redirected to be something other than a console handle.


So, if you use WriteConsole, you won't be able to "myprog.exe > output.txt", which is quite useful when dealing with command-line programs.

If you're writing a "full-screen text mode" program (think HIEW, old Borland IDEs, ...), take a look at WriteConsoleOutput.
Posted on 2006-06-17 15:32:10 by f0dder
Writing a proc (procedure) is easy !!!
Let's do it :)
Note : the following procedure assumes that you already obtained 'standard console output handle' (hConsole) since we only ever need to do that one time..


ConOut proc pMessage:ptr BYTE
local NumChars:DWORD
local DidWrite:DWORD

  invoke lstrlen,pMessage
  mov NumChars,eax
@@: ;re-entry point for incomplete writes (see below)
  invoke WriteConsole,hConsole,pMessage,NumChars,addr DidWrite,0
  .if eax==TRUE ;successful api call, but was all data written?
    mov eax,NumChars ;we better check to make sure...
    .if eax>DidWrite ;incomplete write !!!
          mov edx,DidWrite
          add pMessage,edx ;move pointer to unwritten data
          sub eax,edx          ;calculate #bytes remaining to write
          mov NumChars,eax
          jmp @B  ;go write some more
    .else
        mov eax,TRUE
    .endif
.endif
ret
ConOut endp


The most important thing when writing a procedure is to remember that it must end with a RET statement (return to caller).
By writing the code as a procedure, we can use it as many times as we like, yet there's only one copy of the code in our executable.
If we use a macro multiple times, we have N copies of the code in our executable, which is kinda crappy..

As I was saying, we can now call our proc any number of times, handing it different strings to print to the console..
Note that I included some CRLF (13,10) at the end of each string..
I'm not sure its necessary, but basically its like pressing Enter, it forces each string to appear on a line of its own.


.data
hConsole DWORD 0
szString1 db "ConsolePrintThingy v1.0 by talmir",13,10,0
szString2 db "Heya, can you see me?",13,10,0
szString3 db "Gee, I hope this works!",13,10,0

.code
start:
  invoke GetStdHandle, STD_OUTPUT_HANDLE
  mov hConsole,eax
  invoke ConOut,addr szString1
  invoke ConOut,addr szString2
  invoke ConOut,addr szString3
  invoke Sleep,5000
  invoke ExitProcess,0
end start


Heh, writing procedures is a LOT like writing macros, isn't it? :)
Posted on 2006-06-18 00:13:55 by Homer
Ok, This is working for me so far when writing to console.. I took your advice and used WriteFile wrapped up as a macro...


kWrite PROC pMessage:ptr BYTE
local NumChars:DWORD
invoke lstrlen,pMessage
invoke WriteFile,hConsole,pMessage,eax,NumChars,NULL
ret
kWrite endp


However I dont seem to be able to put it into a .inc file like a normal macro... Any way to do that?
Posted on 2006-06-18 10:20:40 by talmir
You are confused, thats ok :)

Any files that your INCLUDE in your source are seen as a once-off macro, that is to say, when the assembler sees the INCLUDE statement, it switches to that file, and continues assembling until it runs out of data, then resumes from where it left off.

You can put macros inside an include file, if you want to.
Posted on 2006-06-18 10:44:39 by Homer
Hehe.. Im back with another question :) Im so helpless in this its not funny.. but believe me.. I do try and search for answers in many places before asking questions here.. Thanks for being so helpful and patient to a newb :)

Anywho.. my code is like this at the moment


;====
; Playsound
;====

.586
.model flat, stdcall
option casemap:none

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\winmm.lib
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
include \masm32\include\windows.inc
include \masm32\include\winmm.inc

.data
Soundfile db "Enchant.wav"

.code
start:
invoke PlaySound,addr Soundfile,NULL,SND_FILENAME
invoke ExitProcess,0
end start


and everything works great.. I go to the command prompt.. write Playsound.exe and I hear the sound being played.. Now to be more ambitious I thought "Why not make a text appear that tells you the program is playing a text?".. Easy, right?

So I rallied my current knowledge and wrote this:

Playsound.asm

;====
; Playsound
;====

.586
.model flat, stdcall
option casemap:none

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\winmm.lib
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
include \masm32\include\windows.inc
include \masm32\include\winmm.inc

.data
hConsole DWORD 0
Message db "Playing soundfile."
Soundfile db "Enchant.wav"

.code
start:
include cmdline.inc

invoke GetStdHandle,STD_OUTPUT_HANDLE
mov hConsole,eax
invoke kWrite,addr Message
invoke PlaySound,addr Soundfile,NULL,SND_FILENAME
invoke ExitProcess,0
end start


cmdline.inc

kWrite PROC pMessage:ptr BYTE
local NumChars:DWORD
invoke lstrlen,pMessage
invoke WriteFile,hConsole,pMessage,eax,NumChars,NULL
ret
kWrite endp


and it does absolutely, positively, andoodiliderridy nothing at all... :(

What am I doing wrong?

p.s. and a quick question.. is it hard retrieving command line parameters.. f.x "playsound.exe -p ping.wav" ?
Posted on 2006-06-18 14:26:05 by talmir
Are you linking your app as a GUI app? In that case, if I remember correctly, you don't have a default output handle.

Next, you need to 0-terminate your strings - the only reason you are able to play the sound file is that it's at the end of your data section, which has zeroes padding it up to 4096 bytes.

Ie., you need to do this:

Message db "Playing soundfile.", 0
Soundfile db "Enchant.wav", 0


EDIT:
It's easy enough to get at the commandline - it's also pretty easy to parse it. You could use "GetCL" from the masm32 library, unfortunately I have to advise against this. If a user passes a command line that's too long, your application will crash with a buffer overflow.
Posted on 2006-06-18 14:30:45 by f0dder
this is my make file :P dont think I'm linking it as a gui


cls
ml /c /coff PlaySound.asm
link /subsystem:console PlaySound.obj
Posted on 2006-06-18 14:35:09 by talmir
Several things,

First let me show you a working method, then explain why.

;====
; Playsound
;====

.586
.model flat, stdcall
option casemap:none

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\winmm.lib
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc
include \masm32\include\windows.inc
include \masm32\include\winmm.inc

.data
Soundfile db "Enchant.wav",0
Message db "Playing soundfile.",0

.code
start:
invoke MessageBox, NULL, addr Message, addr Message, MB_OK
invoke PlaySound,addr Soundfile,NULL,SND_FILENAME
invoke ExitProcess,0
end start


and the batch file ...

cls
\masm32\bin\ml /c /coff PlaySound.asm
\masm32\bin\link /SUBSYSTEM:WINDOWS PlaySound.obj

pause


talmir,
This is what I think is happening.?  In the beginning you get away with two things, one is no zero after the name of the song.?  Two, the batch file is set to console.?  As long as the console window is not used and there is no other data in the assembly you get away with both.?  As soon as you try to write to the console, PlaySound crashes because it is a windows app.

Using a Message Box instead solves the assembly problem and making it a windows program finishes the job and everything works.

Remember, though, that PlaySound is meant for short wave files.?  Do not try to play songs.

Some of those libs and includes are not used but including them does not hurt anything.  The output from the batch file will give you a warning about one.  You can either delete the reference from the asm file or ignore the warning.  The linker does an adequate fixup.

hth,
Paul
Posted on 2006-06-18 19:42:18 by PBrennick
first, the batch file:

@echo off
ml /nologo /c /coff /Ic:\masm32\include PlaySound.asm
link /nologo /subsystem:console /libpath c:\masm32\lib PlaySound.obj


Next, the fixed up code:

.586
.model flat,stdcall
option casemap:none
option proc:private

include <windows.inc>
include <kernel32.inc>
include <winmm.inc>

includelib <kernel32.lib>
includelib <winmm.lib>

.data?
hStdOut DWORD ?

.data
Soundfile BYTE "Enchant.wav", 0
Message BYTE "Playing soundfile.", 0

.code
kWrite PROC pMessage:ptr BYTE
local NumChars:DWORD

invoke lstrlen, pMessage
mov ecx, eax
invoke WriteFile, hStdOut, pMessage, ecx, addr NumChars, 0 ; <-- addr is very important there!
ret
kWrite endp

start:
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov , eax

invoke kWrite, addr Message
invoke PlaySound, addr Soundfile, 0, SND_FILENAME or SND_SYNC
invoke ExitProcess, 0

end start


I added SND_SYNC to make sure the application doesn't exit until the sound is done playing. Also, your kWrite had the error that you didn't use "addr".
Posted on 2006-06-19 07:20:39 by f0dder
f0dder,
What is with the brackets around the includes and libs?  I had to remove them to assemble the project.

Paul
Posted on 2006-06-19 08:06:19 by PBrennick

f0dder,
What is with the brackets around the includes and libs?  I had to remove them to assemble the project.

Paul



Afaik the <brackets> are a way to pass literals in masm. It's a bit silly using them like I do, but I think it looks pretty :). Assemble errors, though? Which MASM version? It assembles fine here with ML 6.14.8444 (from the MASM32 package), as well as ML 7.10.3077 from vs.net 2003.
Posted on 2006-06-19 08:11:27 by f0dder
Another question from da noob :)

I have so far managed to create a window with controls (one line edit box and an exit button that actually exits, Im oh so proud of myself now :P)

but my question is:

Lets say I have three numbers, 222, 444 and 666... how would I go about combining them so they form 222444666?
Posted on 2006-06-19 12:53:34 by talmir

Lets say I have three numbers, 222, 444 and 666... how would I go about combining them so they form 222444666?

Depends on what format you have the numbers in. If it's strings, you can do string concatention. If it's in integers, you can do MUL and ADD :)
Posted on 2006-06-19 16:36:30 by f0dder