4. Creating your project

This section guides you through setting up VC++ to work with assembly. You'll create a workspace and project. You'll add a .asm and .mak file to the project. When you've completed this section you'll be ready to compile an assembly program.

Start by opening Visual C++. Use FILE->NEW to create a new blank project called 'helloworld'. (see figure 1)

Create a new blank workspace

Figure 1

Now we'll add a make file project to this workspace. When the new workspace is open, right click it in the file view and select "Add New Project To Workspace..." (see figure 2)

Add a new project to this workspace

Figure 2

Choose "Makefile" and set the name to "helloworld". Use the same directory as for your workspace. If your skilled in VC++ do what you want. Change the radio buttons to read "Add to current workspace..." (see figure 3). Accept the defaults on the wizard that follows.

New makefile project

Figure 3

Before we go any further you'll need to have a sample assembly program file and a make file for your project. Code listings and links follow. Save these files to you Workspace/Project folder.

helloworld.asm


; This is a very simple 32 bit assembly program
; that uses the Win32 API to display hello world on the
; console.

TITLE Hello World in Win32 ASM       (helloworld.asm)

.386
.MODEL flat, stdcall
.STACK 4096

; ----------------------------------------------------------------------------
; These are prototypes for functions that we use
; from the Microsoft library Kernel32.lib.
; ----------------------------------------------------------------------------

; Win32 Console handle
STD_OUTPUT_HANDLE EQU -11		        ; predefined Win API constant (magic)

GetStdHandle PROTO,                 ; get standard handle
	nStdHandle:DWORD  		             ; type of console handle

WriteConsole EQU <WriteConsoleA>    ; alias

WriteConsole PROTO,		              ; write a buffer to the console
	handle:DWORD,		                   ; output handle
	lpBuffer:PTR BYTE,		             ; pointer to buffer
	nNumberOfBytesToWrite:DWORD, 	     ; size of buffer
	lpNumberOfBytesWritten:PTR DWORD,  ; num bytes written
	lpReserved:DWORD		               ; (not used)

ExitProcess PROTO,                  ; exit program
	dwExitCode:DWORD		               ; return code
	
; ----------------------------------------------------------------------------




; ----------------------------------------------------------------------------
; global data
; ----------------------------------------------------------------------------

.data
consoleOutHandle dd ?     	      ; DWORD: handle to standard output device
bytesWritten     dd ?     	      ; DWORD: number of bytes written
message db "Hello World",13,10,0  ; BYTE: string, with \r, \n, \0 at the end

; ----------------------------------------------------------------------------




.code

; ----------------------------------------------------------------------------
procStrLength PROC USES edi,
	ptrString:PTR BYTE	; pointer to string
;
; walk the null terminated string at ptrString
; incrementing eax. The value in eax is the string length 
;
; parameters: ptrString - a string pointer
; returns: EAX = length of string prtString
; ----------------------------------------------------------------------------
	mov edi,ptrString
	mov eax,0     	            ; character count
L1:                             ; loop
	cmp byte ptr [edi],0	     ; found the null end of string?
	je  L2	                     ; yes: jump to L2 and return
	inc edi	                     ; no: increment to next byte
	inc eax	                     ; increment counter
	jmp L1                       ; next iteration of loop
L2: ret                         ; jump here to return
procStrLength ENDP
; ----------------------------------------------------------------------------




; ----------------------------------------------------------------------------
procWriteString proc
;
; Writes a null-terminated string pointed to by EDX to standard
; output using windows calls.
; ----------------------------------------------------------------------------
	pushad

	INVOKE procStrLength,edx   	   ; return length of string in EAX
	cld                            ; clear the direction flag
	                               ; must do this before WriteConsole

	INVOKE WriteConsole,
	    consoleOutHandle,     	   ; console output handle
	    edx,	                   ; points to string
	    eax,	                   ; string length
	    offset bytesWritten,  	   ; returns number of bytes written
	    0

	popad
	ret
procWriteString endp
; ----------------------------------------------------------------------------




; ----------------------------------------------------------------------------
main PROC
;
; Main procedure. Just initializes stdout, dumps the string, and exits.
; ----------------------------------------------------------------------------
	INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; use Win32 to put 
	                                        ; stdout handle in EAX
	                                        
	mov [consoleOutHandle],eax             ; Put the address of the handle in 
	                                        ; our variable

	mov edx,offset message                 ; load the address of the message 
	                                        ; into edx for procWriteString
	
	INVOKE procWriteString                 ; invoke our write string method. 
	                                        ; It'll check EDX

	INVOKE ExitProcess,0                   ; Windows method to quit

main ENDP
; ----------------------------------------------------------------------------

END main

helloworld.mak


# A very simple make file for a windows 32 bit assembly console program
# it assembles and links

# nmake help is online at:
#  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcug98/html/_asug_macros_and_nmake.asp

# Assemble the code into coff format producing map and listing files, 
# including symbolic debugging info. Try "ml /?" for more options 
# and descriptions

# 32 bit link our .obj file with the kernel32.lib file and create an exe file

all: helloworld.exe

helloworld.exe: helloworld.asm
	ml /nologo /coff /c /Zi /Fl /Fm  $?
	link32 /nologo /DEBUG /incremental:no /subsystem:console /entry:main /out:debug\helloworld.exe helloworld.obj kernel32.lib

The next step is to add downloaded files to the project. You'll add our helloworld.asm assembly program. You'll also add an nmake makefile, which is similar so gmake. Right click your project and click "add files.." (see figure 4)

Add files to workspace.

Figure 4

Change the file type to "All files" and select helloworld.asm and helloworld.mak (see figure 5).

Select asm and mak file

Figure 5

Now your workspace should look something like figure 6.

Select asm and mak file

Figure 6

Our final step before we compile is to add paths for executable, library, and include files. This is accomplished via (TOOLS->OPTIONS- >DIRECTORIES). First add the C:\MASM615 path to the executable list (figure 7). Then add C:\MASM615\INCLUDE to the include path (figure 8). Finally add C:\MASM615\LIB to the library path (figure 9). We won't use the include path for this program, but still set it up while we are there.

Add executable path

Figure 7

Add include path

Figure 8

Add lib path

Figure 9

Great! You've successfully established a framework in which to compile and debug your assembly programs. Now we'll continue to the next section to discuss compilation.