4.1 Convenzioni adottate nella sezione Assembly Windows 32bit
In tutti gli esempi presentati nella sezione Assembly Windows 32bit, si suppone che l'utente abbia installato il MASM nella cartella:C:\MASM32in questo caso, tutti gli include files di MASM si troveranno nella cartella:
C:\MASM32\INCLUDEmentre le librerie di MASM si troveranno nella cartella:
C:\MASM32\LIBCome al solito è importante crearsi una cartella di lavoro dove sistemare i files relativi ai vari esempi; in tutti gli esempi presentati nella sezione Assembly Windows32bit, si fa riferimento ad una cartella di lavoro chiamata win32asm. Se si utilizza MASM questa cartella può trovarsi in:
C:\WIN32ASMRicordiamoci che a differenza di quanto accade in ambiente Unix, in ambiente DOS/Windows il SO non distingue tra lettere maiuscole e minuscole utilizzate per i nomi e per i percorsi dei vari files; non esiste nessuna differenza quindi tra c:\masm32 e C:\MASM32.
4.2 Il primo programma Assembly per Win32
Per illustrare le fasi di assembling e di linking di un programma Assembly per Win32 utilizzeremo in questo capitolo alcuni esempi molto semplici; si tratta di piccolissime applicazioni Win32 ridotte quasi al minimo indispensabile. La Figura 4.1 mostra il primo esempio che utilizzeremo in questo capitolo; si tratta della versione MASM di un programma che viene chiamato PRIMO.ASM. Come si può notare, rispetto al template presentato nel precedente capitolo sono state rimosse le direttive INCLUDE; questo procedimento si rende necessario perché in seguito, in fase di assemblaggio dovremo generare il listing file. In presenza dei vari include files, l'assembler genera un listing file gigantesco contenente una marea di simboli che non dobbiamo utilizzare; per evitare questo inconveniente, rimuoviamo le direttive INCLUDE e inseriamo nel nostro programma solamente le costanti e i prototipi di procedure di cui abbiamo bisogno. La Figura 4.1 mostra appunto la presenza nel nostro programma di due sezioni contenenti queste informazioni copiate direttamente dai vari include files; il contenuto di queste due sezioni viene descritto più avanti.int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);Per una descrizione dettagliata delle caratteristiche di MessageBox è necessario consultare il solito Win32 Programmer's Reference; analizziamo invece gli aspetti di MessageBox che interessano a noi programmatori Assembly. Chiamando la procedura MessageBox viene visualizzata sullo schermo una finestra predefinita chiamata appunto message box (finestra dei messaggi); nella sua forma più semplice questa procedura richiede quattro parametri e quando termina restituisce un valore intero a 32 bit (tipo int del linguaggio C). Le convenzioni seguite dai linguaggi di alto livello per la restituzione di valori da parte delle procedure, vengono dettagliatamente descritte nella sezione Assembly Base e sono valide anche per Win32; anche in questo caso quindi i valori interi a 8 bit vengono restituiti in AL, i valori interi a 16 bit vengono restituiti in AX, i valori interi a 32 bit vengono restituiti in EAX, i valori in virgola mobile vengono restituiti nel registro ST(0) della FPU, etc. Il valore intero a 32 bit restituito in EAX da MessageBox codifica una determinata azione compiuta dall'utente; nella gran parte dei casi l'azione consiste nella pressione del bottone 'OK' presente nella finestra dei messaggi. Il bottone 'OK' può essere premuto con il pulsante sinistro del mouse oppure attraverso il tasto [Invio]; il codice associato alla pressione del bottone 'OK' è rappresentato dal valore 00000001h ed è dichiarato in windows.inc come:
IDOK = 00000001hI codici associati ad altre azioni verranno esaminati al momento opportuno; analizziamo ora i parametri richiesti dalla procedura MessageBox. Il primo parametro (hWnd) consente di passare alla finestra dei messaggi il codice numerico della finestra principale che ha effettuato la chiamata di MessageBox; come è stato spiegato in un precedente capitolo, normalmente ogni applicazione Windows è dotata di una finestra chiamata main window (finestra principale). La finestra principale viene individuata da Windows attraverso un codice identificativo chiamato handle to window o hwnd (handle = maniglia); quando la finestra principale chiama una finestra che potremmo definire "finestra figlia", deve passarle il suo hwnd. Se, come nel nostro caso, non esiste una finestra principale, il parametro hwnd da passare alla finestra figlia (che in questo caso è la MessageBox) deve valere zero; come al solito, invece di utilizzare valori numerici espliciti, conviene sempre servirsi di costanti simboliche. Nel nostro caso utilizziamo la costante simbolica NULL che rappresenta appunto un valore nullo; la costante NULL presente in Figura 4.1 viene dichiarata anche in windows.inc. In sostanza, il tipo HWND indica un tipo di dato intero a 32 bit che corrisponde quindi al tipo DWORD dell'Assembly; se consultiamo infatti il file windows.inc, troviamo la dichiarazione:
HWND TYPEDEF DWORDIl secondo parametro richiesto dalla procedura MessageBox è l'indirizzo di una stringa C chiamata lpText; questa stringa deve contenere il messaggio che verrà mostrato dalla message box. Come si nota dal prototipo di MessageBox, il parametro lpText viene dichiarato di tipo LPCTSTR; questo mnemonico sta per Long Pointer to C Text STRing (puntatore FAR ad una stringa di testo terminata da uno zero). I puntatori NEAR e FAR esistono anche in Win32 per garantire la compatibilità con i vecchi programmi scritti per Win16; siccome noi stiamo scrivendo una applicazione "pura" a 32 bit per Win32, dobbiamo ignorare totalmente questa distinzione tra diversi tipi di puntatori che riguarda esclusivamente Win16. Come già sappiamo, in Win32 un qualsiasi indirizzo di memoria è rappresentato da un offset a 32 bit, e cioè da un numero intero senza segno che può andare da 00000000h a FFFFFFFFh; possiamo dire quindi che dal punto di vista dell'Assembly, il tipo LPCTSTR è perfettamente equivalente al tipo DWORD. Anche in questo caso, consultando il file windows.inc troviamo proprio la dichiarazione:
LPCTSTR TYPEDEF DWORDIl terzo parametro richiesto da MessageBox viene chiamato lpCaption ed è anch'esso di tipo LPCTSTR; si tratta quindi dell'indirizzo a 32 bit di un'altra stringa C. Questa stringa verrà visualizzata nell'area riservata al titolo della finestra dei messaggi; nel gergo di Windows l'area riservata al titolo di una finestra viene chiamata Title Bar (barra del titolo).
MB_OK OR MB_ICONINFORMATIONQuesta situazione viene mostrata proprio nel listato di Figura 4.1.
00000000h OR 00000040h = 00000040h(gli operatori dell'Assembly vengono trattati in dettaglio nella sezione Assembly Base).
UINT TYPEDEF DWORDIn sostanza, la procedura MessageBox richiede quattro parametri di tipo DWORD e quando termina restituisce in EAX un valore di tipo DWORD; in base a tutte queste considerazioni, il prototipo di MessageBox mostrato in Figura 4.1 è proprio:
MessageBoxA PROTO :DWORD, :DWORD, :DWORD, :DWORDNaturalmente, sfruttando le dichiarazioni presenti in windows.inc possiamo anche scrivere:
MessageBoxA PROTO :HWND, :LPCTSTR, :LPCTSTR, :UINTIn questo modo rendiamo più esplicite le caratteristiche dei vari parametri.
4.3 La fase di assembling con MASM
Vediamo ora come si deve procedere per convertire PRIMO.ASM in formato oggetto; cominciamo dal caso in cui l'utente voglia utilizzare MASM. Visto e considerato che la versione più recente di MASM è scaricabile gratuitamente da Internet, non avrebbe senso pretendere di utilizzare una vecchia versione di questo assembler; faremo riferimento quindi alla versione 6.x o superiore. Installando il MASM nella cartella:C:\MASM32tutti gli strumenti di sviluppo vengono a trovarsi nella cartella:
C:\MASM32\BINl'assemblatore fornito dal MASM si chiama ML.EXE. Prima di tutto è necessario che il file PRIMO.ASM si trovi nella cartella di lavoro:
c:\win32asmposizionandoci in questa cartella, dal prompt del DOS dobbiamo digitare:
c:\masm32\bin\ml /c /coff /I c:\masm32\include /Fl primo.asmPremendo ora il tasto [Invio] parte la fase di assemblaggio del programma; se non vengono trovati errori, viene generato il file PRIMO.OBJ che contiene il codice oggetto del nostro programma. Il parametro /c dice all'assembler di limitarsi alla sola fase di assemblaggio; in assenza di questo parametro, parte automaticamente anche la fase di linking con la generazione dell'eseguibile. Il parametro /coff dice all'assembler di generare il file PRIMO.OBJ nel formato COFF (Common Object File Format); questo è il formato oggetto standard utilizzato da molti strumenti di sviluppo Microsoft in ambiente Windows. Il formato COFF è incompatibile con il formato OMF (Object Module Format) utilizzato invece dagli strumenti di sviluppo della Borland; proprio per questo motivo, non è possibile utilizzare il TASM per creare applicazioni per Win32. Il parametro /I c:\masm32\include dice all'assembler dove cercare gli include files per risolvere i simboli e i prototipi di procedure utilizzati dal programma; il parametro /Fl infine dice all'assembler di generare il listing file; in assenza di altre indicazioni da parte dell'utente, questo file verrà chiamato PRIMO.LST.
4.4 Il listing file
Utilizzando il parametro /Fl con il MASM, abbiamo chiesto all'assembler di generare il listing file del nostro programma; dopo la fase di assemblaggio quindi, abbiamo a disposizione non solo l'object file PRIMO.OBJ ma anche il listing file PRIMO.LST. Questo file ci permette di analizzare in dettaglio il lavoro svolto dall'assembler; la Figura 4.2 mostra in particolare il listing file prodotto da MASM. Come è stato già spiegato nella sezione Assembly Base, la parte iniziale del listing file mostra sulla destra il listato del nostro programma, e sulla sinistra una serie di informazioni inserite dall'assembler e comprendenti anche il codice macchina; la colonna più a sinistra contiene la numerazione delle linee che formano il listato del programma. La seconda colonna a partire da sinistra contiene gli offset relativi al contenuto di ciascun segmento di programma; come si può notare, questa volta gli offset sono a 32 bit. La terza colonna contiene la conversione del nostro programma in codice macchina più altre informazioni inserite dall'assembler; in particolare, notiamo che l'assembler prende nota dei valori numerici espliciti associati a ciascuna costante dichiarata nel programma. Il blocco dati inizializzati _DATA inizia con la stringa strTitolo che si trova ovviamente all'offset 00000000h, cioè a 0 byte di distanza dall'inizio di _DATA; questa stringa occupa 23 byte (17h) per cui l'assembler a partire dall'offset 00000000h crea un vettore di 23 byte che vengono inizializzati con i codici ASCII dei caratteri che formano la stringa stessa. La stringa strTitolo occupa tutti gli offset che vanno da 00000000h a 00000016h, per cui la stringa successiva (strMessaggio) parte dall'offset 00000017h; questa seconda stringa occupa 48 byte (30h), per cui l'assembler a partire dall'offset 00000017h crea un vettore di 48 byte che vengono inizializzati con i codici ASCII dei caratteri che formano la stringa stessa. Complessivamente il segmento _DATA occupa in tutto 71 byte (47h); infatti, come si vede in Figura 4.2 il blocco _DATA termina all'offset 00000047h.MB_OK OR MB_ICONINFORMATIONL'assembler converte quest'espressione nel valore esplicito 40h che occupa un solo byte, contro i quattro byte occupati da 00000040h; l'assembler pone s=1 e ottiene il codice macchina:
6A 40hformato da due soli byte; quando la CPU incontra questo codice macchina, capisce che deve ampliare 40h a 32 bit con estensione del bit di segno. Siccome 40h=01000000b, la CPU converte 40h in 00000040h, decrementa ESP di 4 byte e inserisce 00000040h nello stack. Il secondo parametro da inserire nello stack è l'offset 00000000h di srtTitolo; trattandosi di un numero positivo a 32 bit, l'assembler pone s=0 e ottiene il codice macchina:
68 00000000hQuando la CPU incontra questo codice macchina, decrementa ESP di 4 byte e inserisce 00000000h nello stack; lo stesso procedimento viene seguito per l'offset 0000000Fh di strMessaggio (terzo parametro). Il quarto parametro da inserire nello stack è il valore immediato 00000000h (NULL); con lo stesso procedimento usato per il primo parametro, l'assembler pone s=1 e ottiene il codice macchina:
6A 00hQuando la CPU incontra questo codice macchina, decrementa ESP di 4 byte, converte 00h in 00000000h e inserisce questo valore nello stack; a questo punto tutti i parametri sono stati inseriti nello stack per cui si può procedere alla chiamata di MessageBoxA che provvede a visualizzare la stringa strMessaggio. Dopo la chiamata di MessageBoxA troviamo il codice macchina relativo alla chiamata di ExitProcess; come si può notare, per entrambe le chiamate l'assembler utilizza l'opcode E8h che come sappiamo è relativo all'istruzione CALL direct within segment (chiamata diretta all'interno del segmento). Questo aspetto è molto importante in quanto ci fa capire bene il concetto di modello di memoria FLAT; in sostanza, al momento di avviare la fase di esecuzione, Win32 carica in memoria il nostro programma inserendolo in un "supersegmento" virtuale da 4 Gib. All'interno di questo supersegmento vengono caricate anche le librerie dinamiche (DLL) contenenti i servizi del SO richiesti dal nostro programma (come MessageBoxA, ExitProcess, etc); tutto ciò che è presente in questo supersegmento (codice, dati, stack del programma, librerie dinamiche, etc) è indirizzabile in modo lineare attraverso un semplice offset a 32 bit.
4.5 La fase di linking con MASM
Dopo aver esaminato il listing file PRIMO.LST, possiamo passare alla fase di linking che ci consentirà di ottenere l'eseguibile finale chiamato PRIMO.EXE; nella fase di linking viene anche effettuato il collegamento tra PRIMO.OBJ e le necessarie librerie di Win32. Le librerie da collegare sono USER32.LIB (che contiene la procedura MessageBoxA) e KERNEL32.LIB (che contiene la procedura ExitProcess); come si può vedere in Figura 4.1, con MASM queste librerie possono essere specificate direttamente nel codice sorgente attraverso le direttive INCLUDELIB.c:\win32asmdove deve essere presente l'object file PRIMO.OBJ; dal prompt del DOS dobbiamo digitare:
c:\masm32\bin\link /subsystem:windows /libpath:c:\masm32\lib /map primo.objPremendo ora il tasto [Invio] parte la fase di linking di PRIMO.OBJ; se non vengono trovati errori, viene generato il file PRIMO.EXE che contiene il codice eseguibile del nostro programma. Il parametro /subsystem:windows dice al linker di generare un eseguibile in formato PE per Win32; il parametro /libpath:c:\masm32\lib dice all'assembler dove cercare le librerie necessarie per collegare le procedure dei servizi di Windows utilizzate dal programma. Il parametro /map dice al linker di generare il map file che in assenza di diverse indicazioni da parte dell'utente, viene chiamato PRIMO.MAP.
4.6 Il Map File
Nella fase di linking del nostro programma, abbiamo chiesto al linker di generare il Map File; nel caso di MASM si utilizza l'opzione /map. In assenza di diverse indicazioni da parte del programmatore, viene generato un map file chiamato PRIMO.MAP; la Figura 4.3 mostra il map file generato dal MASM. Con l'ausilio del map file possiamo analizzare il lavoro svolto dal linker; come già sappiamo, questo lavoro consiste principalmente nella determinazione di tutte le caratteristiche dei vari segmenti che formano il nostro programma e nella verifica di tutti i riferimenti a simboli definiti in altri moduli. I segmenti di programma vengono poi fusi con i corrispondenti segmenti predisposti dal SO; infine il linker provvede ad ordinare i vari segmenti secondo le convenzioni stabilite sempre dal SO. In Figura 4.3 vediamo che il map file prodotto dal MASM stabilisce che l'applicazione verrà caricata in memoria preferibilmente all'indirizzo lineare 400000h (4 Mib); successivamente troviamo una serie di informazioni relative alle caratteristiche dei vari segmenti di programma, come ad esempio l'indirizzo iniziale, la dimensione in byte, il tipo di segmento, la classe del segmento, etc. Il valore a 16 bit presente nel campo Start (indirizzo iniziale), rappresenta simbolicamente il selettore di quel blocco di programma; oltre ai segmenti appartenenti al nostro programma, notiamo la presenza di altri segmenti appartenenti al SO. Il blocco successivo contiene informazioni relative alla posizione dei vari simboli nei rispettivi segmenti di programma; come possiamo notare, sono presenti anche le informazioni relative ai simboli MessageBoxA e ExitProcess. L'ultima informazione del map file si riferisce all'entry point del nostro programma, rappresentato dall'indirizzo 0001:00000000h; il valore 0001h si riferisce simbolicamente al selettore del blocco codice _TEXT.4.7 La fase di esecuzione di PRIMO.EXE
Per eseguire PRIMO.EXE possiamo digitare PRIMO dal prompt del DOS premendo successivamente [Invio], oppure possiamo fare doppio click con il pulsante sinistro del mouse sull'icona associata al file eseguibile; a questo punto entra in gioco il loader del SO che esegue tutte le necessarie inizializzazioni. In particolare analizziamo il lavoro svolto dal loader sui registri di segmento della CPU.4.8 La procedura wsprintf
Per verificare in pratica le considerazioni appena esposte, scriviamo un apposito programma che rappresenta il secondo esempio di questo capitolo; il programma, che si chiama REGVAL.ASM, mostra in una message box l'indirizzo di memoria da cui parte l'applicazione REGVAL.EXE e il contenuto dei principali registri della CPU, nella figura 4.4 viene mostrato il listato del programma regval.asm. A tale proposito, utilizziamo due nuove procedure chiamate GetModuleHandle e wsprintf; entrambe le procedure maneggiano stringhe, per cui esistono come al solito sia in versione ASCII sia in versione UNICODE. La procedura GetModuleHandle fa parte della libreria KERNEL32.LIB ed è dichiarata nell'API di Windows come:HMODULE GetModuleHandle(LPCTSTR lpModuleName)Questa procedura restituisce in EAX l'handle di un modulo EXE o DLL il cui nome (completo di estensione) è contenuto nella stringa C lpModuleName. In Win16, ad ogni applicazione in esecuzione viene assegnato un codice numerico univoco chiamato module handle; se si eseguono più copie (istanze) della stessa applicazione, ad ogni copia viene associato un codice numerico univoco chiamato instance handle. Questi accorgimenti sono necessari in quanto in Win16 le varie applicazioni condividono tutte lo stesso spazio di indirizzamento; attraverso i module handle è possibile distinguere le diverse applicazioni in esecuzione, mentre attraverso gli instance handle è possibile distinguere le varie istanze di una applicazione in esecuzione. Se vogliamo ottenere ad esempio l'handle del modulo REGVAL.EXE, possiamo definire la stringa:
strName db 'REGVAL.EXE', 0(si possono usare indifferentemente le maiuscole o le minuscole). A questo punto possiamo effettuare la chiamata:
invoke GetModuleHandleA, offset strNameSe REGVAL.EXE è in esecuzione, otteniamo in EAX il suo module handle, in caso contrario la procedura GetModuleHandle restituisce in EAX il valore zero (NULL). Possiamo dire quindi che il valore restituito da GetModuleHandle è di tipo DWORD; infatti in windos.inc è presente la dichiarazione:
HMODULE TYPEDEF DWORD(se questa dichiarazione non è presente, possiamo sempre inserirla noi).
invoke GetModuleHandleA, NULLL'informazione desiderata viene restituita nel registro EAX e viene quindi copiata nella variabile a 32 bit hInstance; questa variabile non ha un valore iniziale per cui viene definita nel segmento dati non inizializzati (_BSS).
int wsprintf(LPTSTR lpOut, LPCTSTR lpFmt, ...);Il primo parametro lpOut è una stringa destinata a ricevere l'output prodotto da wsprintf; come si può notare, questo parametro è di tipo LPTSTR (puntatore FAR ad una generica stringa di testo) per indicare il fatto che non è necessario lo zero finale. Il secondo parametro lpFmt è una stringa C chiamata stringa di formato, contenente le direttive per la formattazione dell'output; in base a queste direttive wsprintf determina l'output da inviare a lpOut. Al posto del terzo parametro troviamo tre puntini ... che nel linguaggio C indicano il fatto che wsprintf accetta un numero variabile di altri argomenti; la procedura wsprintf quando termina restituisce in EAX un valore intero che (in assenza di errori) rappresenta il numero di caratteri inseriti in lpOut.
strBuffer db 40 dup (0)Bisogna prestare particolare attenzione al fatto che questa stringa deve essere in grado di contenere tutto l'output prodotto da wsprintf. La stringa di formato può essere definita come:
strFormat db 'EAX = %.8Xh', 0Attraverso la stringa di formato, stiamo dicendo a wsprintf di inserire in strBuffer la stringa:
'EAX = 'seguita da un valore esadecimale formato da almeno 8 cifre. Gli eventuali posti vuoti alla sinistra del valore esadecimale devono essere riempiti con degli zeri; subito dopo il valore esadecimale deve essere inserita la lettera h. A questo punto possiamo procedere con la chiamata:
invoke wsprinfA, offset strBuffer, offset strFormat, eaxCome si può notare, il valore da inserire nella stringa di output viene passato in EAX come terzo parametro; è importantissimo che ci sia un perfetto equilibrio tra il numero di direttive inserite nella stringa di formato e il numero di parametri aggiuntivi passati a wsprintf. Se tutto fila liscio, la procedura wsprintf restituisce in strBuffer l'output:
'EAX = 0F4AB000h', 0Come si può notare, wsprintf ha aggiunto a strBuffer lo zero finale. Se vogliamo visualizzare questa stringa possiamo usare la solita message box con la chiamata:
invoke MessageBoxA, NULL, strBuffer, strTitolo, MB_OKLa procedura wsprintf è una delle rarissime procedure di Win32 che seguono le convenzioni del linguaggio C; in sostanza, i parametri vengono passati a wsprintf a partire dall'ultimo, e lo stack viene ripulito da chi ha chiamato la procedura. Nel caso dell'esempio precedente, l'assembler espande l'istruzione:
invoke wsprinfA, offset strBuffer, offset strFormat, eaxnella sequenza di istruzioni: Come si può notare, subito dopo la chiamata di wsprintf l'assembler ha aggiunto un'istruzione che somma il valore 0Ch = 12d al registro ESP; infatti, prima della chiamata di wsprintf abbiamo inserito nello stack tre parametri da 4 byte ciascuno. Come fa l'assembler a sapere che wsprintf è una procedura C e non STDCALL e che richiede un numero variabile di argomenti? Questa informazione gliela dobbiamo dare noi attraverso il prototipo della procedura; con il MASM bisogna scrivere:
wsprintfA PROTO C :DWORD, :DWORD, :VARARGAlternativamente, è sempre possibile l'utilizzo della dichiarazione in vecchio stile Assembly:
EXTRN wsprintfA: PROCIn questo modo però non possiamo utilizzare le direttive avanzate INVOKE.
strBuffer db 256 dup (0)In questo modo siamo sicuri che strBuffer riuscirà a contenere tutto l'output prodotto da wsprintf.
0063FE3Ch - 00400000h = 0023FE3Ch = 2358844 byteIl programma REGVAL.EXE utilizza le procedure wsprintfA, MessageBoxA, GetModuleHandleA e ExitProcess; le prime due procedure vengono definite nella libreria USER32, mentre le altre due procedure vengono definite nella libreria KERNEL32. Quando REGVAL.EXE chiama una di queste procedure non sta facendo altro che richiedere un servizio al SO; di conseguenza, al momento di eseguire REGVAL.EXE, il SO carica in memoria anche queste due librerie che devono fornire i servizi richiesti dal nostro programma. In sostanza, le librerie di Win32 vengono collegate dinamicamente alle applicazioni che le utilizzano e per questo motivo si parla anche di librerie a collegamento dinamico o DLL; questi concetti sono molto importanti per capire il meccanismo attraverso il quale un'applicazione Win32 si interfaccia al SO.
4.9 L'editor QEDITOR.EXE del MASM32
Con le vecchie versioni del MASM vengono largamente utilizzati i makefile; in questo caso, l'interprete dei makefiles si chiama NMAKE.EXE. In MASM32 questo programma non è presente perché al posto dei makefiles vengono utilizzati i batch files già illustrati nella sezione Assembly Base; MASM32 installa una serie di batch files predefiniti che vengono utilizzati da un potente editor fornito in dotazione e chiamato QEDITOR.EXE. Questo editor che si trova nella cartella c:\masm32, permette di gestire dal suo interno tutte le fasi di assembling e di linking; per poterlo sfruttare al massimo, dobbiamo adottare una serie di accorgimenti. La prima cosa da fare consiste nell'inserire il percorso c:\masm32 nel file c:\autoexec.bat che viene eseguito all'avvio del computer; all'interno di questo file è presente una riga del tipo:SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND ...Questa riga permette di specificare una serie di cartelle con "visibilità globale"; alla fine di questa riga dobbiamo aggiungere la cartella C:\MASM32 (le varie cartelle sono separate tra loro da un punto e virgola). Per rendere attiva questa modifica, dobbiamo salvare il file autoexec.bat e riavviare il computer; a questo punto, dal prompt del DOS possiamo eseguire QEDITOR.EXE da qualunque altra cartella.
c:\win32asmdigitiamo qeditor e premiamo [Invio]. Dall'interno dell'editor selezioniamo il menu File + Open e carichiamo il programma Assembly desiderato; selezioniamo quindi il menu Project + Build All. A questo punto partono le fasi di assemblig e di linking che portano alla generazione dell'eseguibile; per poter lanciare questo eseguibile selezioniamo il menu Project + Run Program.