In The Assembly Programming Master Book, there is a listing showing an example of a simple Windows desktop application that displays a plain GUI window with an "Exit" button (see below). The code shown is clear enough, but I cannot get it to compile unless I change the align parameter in the segment declarations from "DWORD" to "PARA" or simply write "_DATA SEGMENT" and "_TEXT SEGMENT". Without that, I keep getting the "Segment attributes cannot change" error and the only help I found so far is the reference documentation of the Asmc Macro Assembler explaining that "when a SEGMENT directive opens a previously defined segment, the newly opened segment inherits the attributes the segment was defined with". Could you please explain to me why ml thinks that I'm trying to reopen the segments instead of just opening them, or, if it's normal, when they were opened originally?
; button.inc
; Constants
; The message that arrives when the window is closed
WM_DESTROY EQU 2
; The message that arrives when the window is created
WM_CREATE EQU 1
; The message that arrives if the left mouse button is clicked
; somewhere in the window area
WM_LBUTTONDOWN EQU 201h
WM_COMMAND EQU 111h
; Window properties
CS_VREDRAW EQU 1h
CS_HREDRAW EQU 2h
CS_GLOBALCLASS EQU 4000h
WS_OVERLAPPEDWINDOW EQU 000CF0000H
STYLE EQU CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS
BS_DEFPUSHBUTTON EQU 1h
WS_VISIBLE EQU 10000000h
WS_CHILD EQU 40000000h
STYLBTN EQU WS_CHILD+BS_DEFPUSHBUTTON+WS_VISIBLE
; Standard icon identifier
IDI_APPLICATION EQU 32512
; Cursor identifier
IDC_ARROW EQU 32512
; Normal mode of displaying the window
SW_SHOWNORMAL EQU 1
; Prototypes of external procedures
EXTERN MessageBoxA@16:NEAR
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
; Structures
; Message structure
MSGSTRUCT STRUC
MSHWND DD ?
MSMESSAGE DD ?
MSWPARAM DD ?
MSLPARAM DD ?
MSTIME DD ?
MSPT DD ?
MSGSTRUCT ENDS
;---- Window class structure
WNDCLASS STRUC
CLSSTYLE DD ?
CLWNDPROC DD ?
CLSCBCLSEX DD ?
CLSCBWNDEX DD ?
CLSHINST DD ?
CLSHICON DD ?
CLSHCURSOR DD ?
CLBKGROUND DD ?
CLMENNAME DD ?
CLNAME DD ?
WNDCLASS ENDS
; button.asm
.586P
; Flat memory model
.MODEL FLAT, stdcall
include button.inc
; Directives for the linker to link libraries
includelib user32.lib
includelib kernel32.lib
;------------------------------------------
; Data segment
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
NEWHWND DD 0
MSG MSGSTRUCT <?>
WC WNDCLASS <?>
HINST DD 0 ; Application descriptor
TITLENAME DB 'The example - Exit button', 0
CLASSNAME DB 'CLASS32', 0
CPBUT DB 'Exit', 0 ; Exit
CLSBUTN DB 'BUTTON', 0
HWNDBTN DWORD 0
CAP DB 'Message', 0
MES DB 'Program termination', 0
_DATA ENDS
; Code segment
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
; Get the application descriptor
PUSH 0
CALL GetModuleHandleA@4
MOV [HINST], EAX
REG_CLASS:
; Fill the window structure
; Style
MOV [WC.CLSSTYLE], STYLE
; Message handling procedure
MOV [WC.CLWNDPROC], OFFSET WNDPROC
MOV [WC.CLSCBCLSEX], 0
MOV [WC.CLSCBWNDEX], 0
MOV EAX, [HINST]
MOV [WC.CLSHINST], EAX
;----------Window icon
PUSH IDI_APPLICATION
PUSH 0
CALL LoadIconA@8
MOV [WC.CLSHICON], EAX
;----------Window cursor
PUSH IDC_ARROW
PUSH 0
CALL LoadCursorA@8
MOV [WC.CLSHCURSOR], EAX
;----------
MOV [WC.CLBKGROUND], 17 ; Window color
MOV DWORD PTR [WC.CLMENNAME], 0
MOV DWORD PTR [WC.CLNAME], OFFSET CLASSNAME
PUSH OFFSET WC
CALL RegisterClassA@4
; Create a window of the registered class
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH 400 ; DY - Window height
PUSH 400 ; DX - Window width
PUSH 100 ; Y - Coordinate of the top left corner
PUSH 100 ; X - Coordinate of the top right corner
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME ; Window name
PUSH OFFSET CLASSNAME ; Class name
PUSH 0
CALL CreateWindowExA@48
; Check for error
CMP EAX, 0
JZ _ERR
MOV [NEWHWND], EAX ; Window descriptor
;------------------------------------------
PUSH SW_SHOWNORMAL
PUSH [NEWHWND]
CALL ShowWindow@8 ; Show newly-created window
;------------------------------------------
PUSH [NEWHWND]
CALL UpdateWindow@4 ; The command for redrawing the visible
; part of the window, using the WM_PAINT message
; Message-processing loop
MSG_LOOP:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG
CALL GetMessageA@16
CMP EAX, 0
JE END_LOOP
PUSH OFFSET MSG
CALL TranslateMessage@4
PUSH OFFSET MSG
CALL DispatchMessageA@4
JMP MSG_LOOP
END_LOOP:
; Exit the program (close the process)
PUSH [MSG.MSWPARAM]
CALL ExitProcess@4
_ERR:
JMP END_LOOP
;------------------------------------------------------Window procedure
; Position of parameters in the stack
; [EBP+14H] ; LPARAM
; [EBP+10H] ; WAPARAM
; [EBP+0CH] ; MES
; [EBP+8] ; HWND
WNDPROC PROC
PUSH EBP
MOV EBP, ESP
PUSH EBX
PUSH ESI
PUSH EDI
CMP DWORD PTR [EBP+0CH], WM_DESTROY
JE WMDESTROY
CMP DWORD PTR [EBP+0CH], WM_CREATE
JE WMCREATE
CMP DWORD PTR [EBP+0CH], WM_COMMAND
JE WMCOMMND
JMP DEFWNDPROC
WMCOMMND:
MOV EAX, HWNDBTN
CMP DWORD PTR [EBP+14H], EAX ; Hasn't the user clicked the button?
JE WMDESTROY
MOV EAX, 0
JMP FINISH
WMCREATE:
; Create the button window
PUSH 0
PUSH [HINST]
PUSH 0
PUSH DWORD PTR [EBP+08H]
PUSH 20 ; DY
PUSH 60 ; DX
PUSH 10 ; Y
PUSH 10 ; X
PUSH STYLBTN
PUSH OFFSET CPBUT ; Window name
PUSH OFFSET CLSBUTN ; Window class
PUSH 0
CALL CreateWindowExA@48
MOV HWNDBTN, EAX ; Save the button descriptor
MOV EAX, 0
JMP FINISH
DEFWNDPROC:
PUSH DWORD PTR [EBP+14H]
PUSH DWORD PTR [EBP+10H]
PUSH DWORD PTR [EBP+0CH]
PUSH DWORD PTR [EBP+08H]
CALL DefWindowProcA@16
JMP FINISH
WMDESTROY:
PUSH 0 ; MB_OK
PUSH OFFSET CAP
PUSH OFFSET MES
PUSH DWORD PTR [EBP+08H] ; Window descriptor
CALL MessageBoxA@16
PUSH 0
CALL PostQuitMessage@4 ; WM_QUIT message
MOV EAX, 0
FINISH:
POP EDI
POP ESI
POP EBX
POP EBP
RET 16
WNDPROC ENDP
_TEXT ENDS
END START
First, welcome to the forum.
Second, do you have the Masm32 SDK installed? And are you using ml.exe to assemble it?
If not, you should install it. The way you have written your code is all wrong...
Format should look something like this
; #########################################################################
.386
.model flat, stdcall
option casemap :none ; case sensitive
; #########################################################################
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
; #########################################################################
.code
start:
jmp @F
szDlgTitle db "Minimum MASM",0
szMsg db " --- Assembler Pure and Simple --- ",0
@@:
push MB_OK
push offset szDlgTitle
push offset szMsg
push 0
call MessageBox
push 0
call ExitProcess
end start
That is an example from the Masm32 SDK, in the examples folder
On the upper right of the home screen there is a link "Masm32 Downloads" to install the Masm32 SDK
Attached below is the same source code and batch file to assemble it
Any further questions can be posted here. Someone will be here to help you further
Edit==typo
The code is taken from the book and I'm using Visual Studio 2019. Until now, however, I didn't have any problems, so I don't think it's the environment. After modifying the two segment declarations as per my first post, I can successfully run the program either directly in Visual Studio or by manually running ml and link. I'd just like to know why one would want to use any specific alignment here. I'll try installing the SDK and let you know if the problem persists. Thanks for the reply.
If there are any issues installing Masm32, let us know.
Quote from: kkurkiewicz on August 15, 2022, 05:29:13 AM
The code is taken from the book and I'm using Visual Studio 2019.
Maybe you'd like to try writing something in "pure" MASM. Around here we don't use IDEs like Visual Studio, which after all are really made for use with C, not assembly language. (Well, there are some IDEs, like for JJ's MasmBasic, but they're made for assembler.)
If you do, maybe try something simple like "hello world".
One thing about MASM: you should never even have to think about segments. That's so DOS/16-bit assembly old school. With MASM32, you get nice flat 32-bit address spaces and no need to ever use the segment registers, or set the attributes of a segment.
Hi kkurkiewicz,
Welcome to the forum. The book you are reading is outdated by today's standards. In the modern practice of the assembly language, you don't define segments but specify simplified section names.
Yes:
- .code for code
- .data for (initialized) data
- .data? for uninitialized data
And no need to even think about a stack segment; that's given to you on a silver platter.
Hi kkurkiewicz,
You need to modify the lines below in your code :
Line 10 :
_DATA SEGMENT PUBLIC USE32 'DATA'
Line 24 :
_TEXT SEGMENT PUBLIC USE32 'CODE'
Removing the statement DWORD from those lines is solving the problem.
Building the executable :
\masm32\bin\ml /c /coff button.asm
\masm32\bin\polink.exe /SUBSYSTEM:WINDOWS /LIBPATH:\masm32\lib button.obj
The question everyone needs to know...
Have you installed Masm32 yet? We can help you much better if you have that installed.
Yes. And once you have it installed (and by "installed" all we mean is "files copied to your \MASM32 folder"; nothing gets put in the registry), you can make your first real assembler program.
At the risk of insulting your intelligence--you may already know this--here's the "workflow":
- Create your source file with the text editor of your choice
- Use a batch file to assemble and link your program (look for files named make.bat or makeit.bat in various places in \MASM32)
- Run your program (assuming no assemble or link errors)
The batch file will invoke two (or possibly more if you use resources, but that's an advanced topic) programs,
ml.exe to assemble your code and
link.exe to link it into an executable. (You could type the
ml and
link command lines manually, but the batch file saves you the trouble of remembering all those command-line switches.) All these things are included in the MASM32 package.
Furthermore, in case you don't know, 2 basic types of Windows programs, your choice:
- GUI programs that run on the desktop and present windows and graphic elements for the user to interact with
- "console mode" programs that run in a Windows "command window", like DOS command window of old
The console programs operate in text mode only, accepting text input from the user and displaying text output in the command window. There are examples of both in the MASM32 package.
==================================================================
Hmm; after looking through the MASM32 stuff, I should warn you that a lot of the
make.bat files are
not ones that you can use to make your program (most of them are used to create the MASM32 libraries). Here's the batch file I use:
@echo off
:ml /c /coff /Fl /Sg /Sn /Zi %1.asm
ml /c /coff /Fl /Sg /Sn %1.asm
if errorlevel 1 goto errasm
:link /SUBSYSTEM:WINDOWS /debug %1.obj
link /SUBSYSTEM:WINDOWS %1
if errorlevel 1 goto errlink
goto TheEnd
:errlink
echo _
echo Link error
goto TheEnd
:errasm
echo _
echo Assembly Error
goto TheEnd
:TheEnd
This is a general-purpose batch file to make any program by putting the name of the source (minus ".asm") on the command line:
make yoursource
This batch file makes GUI programs. To make a console program, replace the
SUBSYTEM:WINDOWS with
SUBSYSTEM:CONSOLE.
The "commented-out" (prefixed with "
:") lines are used to make a debuggable version of the program so you can run a debugger (like Olly Debug) to see what's going on in there.
Quote from: Vortex on August 15, 2022, 05:55:47 AM
Removing the statement DWORD from those lines is solving the problem.
Hi kkurkiewicz,
As Vortex rightly suggested, removing the two DWORDs does indeed solve your immediate problem.
But it doesn't solve the problem that what you posted is stone age assembly code. This is not meant to insult you - the code you posted is not even yours. But the community has moved on in the last 20 years. You wouldn't code a C program in original Kernighan & Ritchie style either, right?
Compare the attached source to yours. It's less than half the lines, but it does a bit more, and the style is much closer to the MSDN documentation.
Ackshooly, looking over your code (addressing the OP here), there's nothing really "wrong" with it (except of course for those erroneous segment directives). But as JJ said, it's out of date in terms of assembler style. Couple things: the call to create a window
; Create a window of the registered class
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH 400 ; DY - Window height
PUSH 400 ; DX - Window width
PUSH 100 ; Y - Coordinate of the top left corner
PUSH 100 ; X - Coordinate of the top right corner
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME ; Window name
PUSH OFFSET CLASSNAME ; Class name
PUSH 0
CALL CreateWindowExA@48
now looks like this:
INVOKE CreateWindowEx, 0, OFFSET CLASSNAME, OFFSET TITLENAME, WS_OVERLAPPEDWINDOW,
100, 100, 400, 400, 0, 0, HINST, 0
or if you prefer,
INVOKE CreateWindowEx, 0, ;Extended window style
OFFSET CLASSNAME, ;Window class name
OFFSET TITLENAME, ;Window title text
WS_OVERLAPPEDWINDOW, ;Window style
100, ;X-pos
100, ;Y-pos
400, ;Width
400, ;Height
0, ;Parent window
0, ;Menu or ID
HINST, ;Instance handle
0 ;lParam value
Does exactly the same thing, but is much more readable and maintainable. No need to tell the linker the size of the argument list in bytes (that's the @48 at the end of the function name): the assembler & linker are now smart enough to do this for you. Also, while you can do the "push-push-call" thing (which you have to do in reverse order of the parameters), INVOKE takes care of that for you.
Where your code uses the most obscure way to address function parameters and stack variables:
;------------------------------------------------------Window procedure
; Position of parameters in the stack
; [EBP+14H] ; LPARAM
; [EBP+10H] ; WAPARAM
; [EBP+0CH] ; MES
; [EBP+8] ; HWND
WNDPROC PROC
CMP DWORD PTR [EBP+0CH], WM_DESTROY
JE WMDESTROY
the modern assembler lets you assign names to these things. What you're looking at here is the uMsg parameter to your window procedure, so it would now look like this:
WindowProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
CMP uMsg, WM_DESTROY
JE WMDESTROY
again making things much more readable.
Sorry for not replying earlier, but I couldn't install the SDK right away. Now, however, I can confirm that the problem was not the environment as the code compiles only after the alignment is set to "PARA" (either explicitly, or implicitly by removing the "DWORD" specifier as suggested by Vortex). I know the book is a little obsolete, but I'm most interested in the chapters describing resources and DLLs. I don't treat it as a textbook for learning the language. Maybe I'll try finding something newer, but for now it still seems quite relevant.
Hi Kamil,
The problem with using a very old reference book is the architecture is out of date and of little use to you. Segment - offset architecture is based off the 16 bit limitation of 64k addressing range and having to change segments to address another 64k address range.
Win32 solves this problem by using what is called a FLAT memory model and it has an address range of 4 gigabytes so you get far more range and a much simpler code for shifting to 32 bit.
You can routinely write both executable and DLL files in 32 bit and they are much simpler than the old 16 bit code.
Quote from: kkurkiewicz on August 15, 2022, 09:48:03 AM
Maybe I'll try finding something newer, but for now it still seems quite relevant.
I can't recommend enough Charles Petzold's book,
Programming Windows: The definitive guide to the Win32 API. I got mine for dirt cheap from some online seller (you can probably pick one up for less than $8 total). It tells the story from the point of view of a C programmer, of course, but it has all the info you need to get started. (It also doubles as a doorstop; it's huge.) This is old Windows, so only covers "classic" controls like edit, listbox, button, etc., but that's plenty to get you going.
Very easy to translate the C code to assembly language. Oh, and if you didn't know, Petzold was a Microsoft guy who won the "Windows Pioneer Award", whatever that is.
Quote from: kkurkiewicz on August 15, 2022, 04:41:45 AM
Could you please explain to me why ml thinks that I'm trying to reopen the segments instead of just opening them, or, if it's normal, when they were opened originally?
AFAICS, noone really answered your question - instead, you had to swallow a lot of propaganda about what's "modern".
Your segment definitions "reopened" the segments because the
.model directive at the top of your program defines - under the hood - two segments: _TEXT and _DATA with certain attributes, and those atributes cannot be changed later.
What attributes are actually used depends on the .model that is defined and also to some degree on the cpu directive (.386, .486, .586,...). If you feed masm with the -Fl option, it will generate a listing, where you can see what segment attributes masm has actually used.
:biggrin:
Perhaps you could have answered the question by advising that the sections,
.data?
.../.
.data
.../.
.code
Were the "modern" way to write 32 bit MASM code instead of a diatribe of ancient junk.
for this masm don't have modern shortcut
.drectve SEGMENT INFO DISCARD
.drectve ENDS
but luckily very low usage, as it don't used for linkable code
welcome kkurkiewicz :thumbsup:
check included examples in masm32 package
ichelionz tutorials,search MS online for winapi documentation
any texteditor will do
whatever code style is welcome here,everything from "bare metal code" style to loads of macros style
Quote from: TimoVJL on August 15, 2022, 03:26:53 PM.drectve SEGMENT INFO DISCARD
More (http://masm32.com/board/index.php?topic=7545.0)