News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Segment attributes cannot change

Started by kkurkiewicz, August 15, 2022, 04:41:45 AM

Previous topic - Next topic

kkurkiewicz

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
Kamil

quarantined

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

kkurkiewicz

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

quarantined

If there are any issues installing Masm32, let us know.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

Vortex

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.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

Vortex

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

quarantined

The question everyone needs to know...
Have you installed Masm32 yet? We can help you much better if you have that installed.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

jj2007

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.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

kkurkiewicz

#12
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.
Kamil

hutch--

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.

NoCforMe

#14
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.
Assembly language programming should be fun. That's why I do it.