News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Quick play with UASM

Started by hutch--, July 21, 2019, 07:04:08 PM

Previous topic - Next topic

aw27

The use of FRAME, which also exists in MASM, has never really been of interest to Assembly-Language-only programmers, although at a point in time everybody started using it with JWasm and successors, (including myself  :badgrin:). In addition, it was broken in its 64-bit version (Japeth heritage). After a lengthy discussion with Johnsa, which started probably here and continued in private I believe that UASM ended with a functional FRAME for people that wants to use it. As a spin-off of that discussion I ended up doing an article for CodeProject which is centered in MASM, but which applies to UASM as well (although I intend to update it and be more specific about its use with UASM).

hutch--

I went for a rat through my archives and found one of my own golden oldies, a template that I used for C code and it only has a "main" to start, no WinMain or CRT runtime module to start it. If you are old enough you will pick the K&R formatting. Its last build was with the VC 2003 toolkit.

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

      #include <windows.h>

      LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

void main()

    {
      HANDLE        hWnd;                       // local main window handle
      HICON         hIcon;                      // local icon handle
      LPMSG         pmsg;                       // pointer for MSG structure
      WNDCLASSEX *  pwc;                        // pointer to WNDCLASSEX structure
      MSG           msg;                        // local msg structure
      WNDCLASSEX    wc;                         // local structure for RegisterClassEx
      char          szTitle[]       = "Small C";
      char          szWindowClass[] = "SC_Class";

      /* ------------------------------------------
      Copy structure addresses to pointer variables
      ------------------------------------------ */
      pmsg = &msg;
      pwc  = &wc;

      hIcon = LoadIcon(NULL,IDI_APPLICATION);

      wc.cbSize           = sizeof(WNDCLASSEX);
      wc.style            = CS_BYTEALIGNWINDOW | CS_BYTEALIGNCLIENT;
      wc.lpfnWndProc      = (WNDPROC)WndProc;
      wc.cbClsExtra       = 0;
      wc.cbWndExtra       = 0;
      wc.hInstance        = (HINSTANCE)0x400000;
      wc.hIcon            = hIcon;
      wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
      wc.lpszMenuName     = NULL;
      wc.lpszClassName    = szWindowClass;
      wc.hIconSm          = hIcon;

      RegisterClassEx(pwc);     

      hWnd = CreateWindowEx(WS_EX_LEFT,
                            szWindowClass,
                            szTitle,
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT,0,
                            CW_USEDEFAULT,0,
                            NULL,NULL,(HINSTANCE)0x400000,NULL);

      ShowWindow(hWnd,SW_SHOW);
      UpdateWindow(hWnd);

      while (GetMessage(pmsg,NULL,0,0))
      {
        TranslateMessage(pmsg);
        DispatchMessage(pmsg);
      }

    }

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

LRESULT CALLBACK WndProc(HWND hWin,UINT uMsg,
                         WPARAM wParam,LPARAM lParam)

    {
      switch (uMsg)
      {
        case WM_DESTROY:
        {
          PostQuitMessage(0);
          break;
        }
      }

    return DefWindowProc(hWin,uMsg,wParam,lParam);

    }

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

Built with,

@echo off

set lib=h:\vctoolkit\lib\
set include=h:\vctoolkit\include\

if exist project2.exe del project2.exe
if exist project2.obj del project2.obj

h:\vctoolkit\bin\cl /c /G7 /O2 /Ot /GA /TC /W3 /FA project2.c
h:\vctoolkit\bin\Link /SUBSYSTEM:WINDOWS /entry:main /libpath:h:\msc6\lib @project2.rsp

dir project2.*

pause

Vortex

Hi Hutch,

Same C example built with Manos' pcc32 compiler. The executable is 2560 bytes.

aw27

Let me see if I get this right (for a Windows program):
1) If you don't use the /Entry switch in the linker, the entry point for a Windows program will be WinMainCRTStartup.
For this case, on a ASM program you must have a procedure called WinMainCRTStartup.
On a C program WinMainCRTStartup is part of the runtime (it is possible to override it and save a few KBs), it runs first and then calls WinMain which is what you see on a normal C program.
2) If you use the /Entry switch, this will be called first either in ASM or in C. For an ASM program the Microsoft Linker will not even require you to specify /SUBSYSTEM:WINDOWS. If you don't specify /SUBSYSTEM:WINDOWS, the POLINK guy will tell you that it is assuming CONSOLE but that does not really matter, it will swallow whatever comes after the entry point.

So Hutch's old Windows application complies.  :biggrin:

hutch--

I have found a funny effect prototyping API functions for UASM so far.

Change this,
SendMessageA PROTO :QWORD,:QWORD,:QWORD,:QWORD
IFNDEF __UNICODE__
  SendMessage equ <SendMessageA>
ENDIF

To

SendMessageA PROTO :PTR,:PTR,:PTR,:PTR
IFNDEF __UNICODE__
  SendMessage equ <SendMessageA>
ENDIF

And it seems to work fine with the OPTION LITERALS:ON as well as any other 64 bit argumernt.





TimoVJL

.drectve  section is useful for some linker options
OPTION DOTNAME
.drectve SEGMENT INFO
db '-subsystem:windows,5.2 -entry:start',0
.drectve ENDS
public start ; for ml64.exe
.code
start:
;WinMainCRTStartup proc
mov eax, 0
ret
;WinMainCRTStartup endp
end ;start ; ml64 don't accept entry point
May the source be with you

aw27

Prototypes were invented to guide our hot little hands  :thumbsup:. We can do without them but is painful and will never be a recommended practice.

One size fits all prototyping has caused the need for new Masm SDK 64-bit include files. Microsoft had been alerting for decades that people should not rely on pointers being dwords. Nobody listened.

hutch--

 :biggrin:

I guess it depends on if the programmer knows what the pointer size is for the OS being used. DOS was 16 bit, win32 was 32 bit and win64 is 64 bit. If a programmer does not know this, they should stick to server side scripting.

UASM seems to work well once you can get it up and going, full window app comes easy, about the only real complaint after using 64 bit MASM is all the hassle producing fixed prototypes, something I left behind in 32 bit. It could do with the OPTION DOTNAME as this solves many naming problems, the need to specify PTR for literal text is a pest and the work around was to make all include file data types PTR instead of QWORD which is nonsensical but it works OK. This needs to be ditched and leave open the base datatype, not some notion of strong typing.

Vortex

Example built with MS VC Toolkit 2003 :

#include <stdio.h>

void __stdcall ExitProcess(unsigned int uExitCode);

__declspec(naked) void proc()
{
static char name[32];

printf("What's your name?\n");
scanf("%s",name);

printf("Nice to meet you %s\n",name);

ExitProcess(0);
}


Set vc2003=C:\MSVCTK2003

Set PATH=%vc2003%\bin;%PATH%
Set INCLUDE=%vc2003%\include;%INCLUDE%
Set LIB=%vc2003%\lib;%LIB%

cl /c /Zl Project.c
link /SUBSYSTEM:CONSOLE /ENTRY:proc Project.obj kernel32.lib H:\masm32\lib\msvcrt.lib


TimoVJL

as msvcrt.dll was used, exit() is usable too.void __cdecl mainCRTStartup(void) {exit(main());}
May the source be with you

hutch--

A UASM window with no Microsoft components used in its build.

UASM assembler
POLINK linker
PORC resource compiler

The source is attached but it cannot be directly built as it assumes bits and pieces from all over my DEV drive but the exe runs correctly, it has an icon, manifest and version control block.
The "sauce".

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \uasm\include\uasm64rt.inc

    button  PROTO :PTR,:PTR,:PTR,:PTR,:PTR,:PTR,:PTR,:PTR
    iMsgbox PROTO :PTR,:PTR,:PTR,:PTR,:PTR

    .data?
      hInstance dq ?
      hWnd      dq ?
      hIcon     dq ?
      hCursor   dq ?
      sWid      dq ?
      sHgt      dq ?
      hBrush    dq ?
      butn1     dq ?
      butn2     dq ?

    .data
      classname db "template_class",0
      caption db "UASM Template",0

    .code

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

entry_point proc

    mov hInstance, rv(GetModuleHandle,0)
    mov hIcon,     rv(LoadIcon,hInstance,10)
    mov hCursor,   rv(LoadCursor,0,IDC_ARROW)
    mov sWid,      rv(GetSystemMetrics,SM_CXSCREEN)
    mov sHgt,      rv(GetSystemMetrics,SM_CYSCREEN)
    mov hBrush,    rv(CreateSolidBrush,00666666h)

    call main

    close

entry_point endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL wc      :WNDCLASSEX
    LOCAL lft     :QWORD
    LOCAL top     :QWORD
    LOCAL wid     :QWORD
    LOCAL hgt     :QWORD

    mov wc.cbSize,         SIZEOF WNDCLASSEX
    mov wc.style,          CS_BYTEALIGNCLIENT or CS_BYTEALIGNWINDOW
    mov wc.lpfnWndProc,    ptr$(WndProc)
    mov wc.cbClsExtra,     0
    mov wc.cbWndExtra,     0
    mrm wc.hInstance,      hInstance
    mrm wc.hIcon,          hIcon
    mrm wc.hCursor,        hCursor
    mrm wc.hbrBackground,  hBrush
    mov wc.lpszMenuName,   0
    mov wc.lpszClassName,  ptr$(classname)
    mrm wc.hIconSm,        hIcon

    invoke RegisterClassEx,ADDR wc

    mov wid, 800
    mov hgt, 450

    mov rax, sWid                           ; calculate offset from left side
    sub rax, wid
    shr rax, 1
    mov lft, rax

    mov rax, sHgt                           ; calculate offset from top edge
    sub rax, hgt
    shr rax, 1
    mov top, rax

    invoke CreateWindowEx,WS_EX_LEFT or WS_EX_ACCEPTFILES, \
                          ADDR classname,ADDR caption, \
                          WS_OVERLAPPEDWINDOW or WS_VISIBLE,\
                          lft,top,wid,hgt,0,0,hInstance,0
    mov hWnd, rax
    call msgloop
    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

msgloop proc

    LOCAL msg    :MSG
    LOCAL pmsg   :QWORD

    mov pmsg, ptr$(msg)                     ; get the msg structure address
    jmp gmsg                                ; jump directly to GetMessage()

  mloop:
    invoke TranslateMessage,pmsg
    invoke DispatchMessage,pmsg
  gmsg:
    test rax, rv(GetMessage,pmsg,0,0,0)     ; loop until GetMessage returns zero
    jnz mloop

    ret

msgloop endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

WndProc proc hWin:PTR,uMsg:PTR,wParam:PTR,lParam:PTR

    .if uMsg == WM_COMMAND
      .if wParam == 100
        invoke iMsgbox,hWin,"Button 1 was pressed","Information",MB_OK,10
      .elseif wParam == 200
        jmp bye
      .elseif wParam == 300
        Invoke iMsgbox,hWin,"UASM 64 Bit Template","About",MB_OK,10
      .endif

    .elseif uMsg == WM_CREATE
      invoke LoadMenu,hInstance,100
      invoke SetMenu,hWin,rax
      mov butn1, rv(button,hInstance,hWin,"Button 1",50,40,100,20,100)
      mov butn2, rv(button,hInstance,hWin,"Close",50,65,100,20,200)
      xor rax, rax
      ret

    .elseif uMsg == WM_CLOSE
      bye:
      invoke SendMessage,hWin,WM_DESTROY,0,0

    .elseif uMsg == WM_DESTROY
      invoke PostQuitMessage,NULL

    .endif

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

    ret

WndProc endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

button proc instance:PTR,hparent:PTR,text:PTR,topx:PTR,topy:PTR,wid:PTR,hgt:PTR,idnum:PTR

    invoke CreateWindowEx,0,"BUTTON",text, \
                          WS_CHILD or WS_VISIBLE,\
                          topx,topy,wid,hgt,hparent,idnum,instance,0
    ret

button endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

iMsgbox proc hParent:PTR,pText:PTR,pTitle:PTR,mbStyle:PTR,IconID:PTR

    LOCAL mbp   :MSGBOXPARAMSxx

    or mbStyle, MB_USERICON

    mov mbp.cbSize,             SIZEOF mbp
    mrm mbp.hwndOwner,          hParent
    mov mbp.hInstance,          rv(GetModuleHandle,0)
    mrm mbp.lpszText,           pText
    mrm mbp.lpszCaption,        pTitle
    mov rax, mbStyle
    mov mbp.dwStyle,            eax
    mrm mbp.lpszIcon,           IconID
    mov mbp.dwContextHelpId,    NULL
    mov mbp.lpfnMsgBoxCallback, NULL
    mov mbp.dwLanguageId,       NULL

    invoke MessageBoxIndirect,ADDR mbp

    ret

iMsgbox endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    end

jj2007

button proc instance:PTR,hparent:PTR,text:PTR,topx:PTR,topy:PTR,wid:PTR,hgt:PTR,idnum:PTR
We all know how params are being passed in x64: rcx, rdx, r8, r9, then QWORDs on the stack. The receiving end will pick what it needs, mostly DWORDs in this case. So technically speaking, you can cheat the assembler in x64, passing only "pointers". But is it wise to do so?

hutch--

 :biggrin:

> But is it wise to do so?

No its not as it makes a fool of the pseudo strong typing. The alternative is to spend YEARS manually editing prototypes which no-one is willing to do. Auto formatting prototypes based on the actual data size is viable as a vast number of API functions use 64 bit arguments and even if they don't, the stack layout is done in 64 bit slots.

I would look for an option to turn it off while still being able to use the literal strings in invoke calls.

jj2007

If you would abandon for a moment your ideological "hot little hands" position, you might reflect on mechanisms to extract the size from C headers. I had started such an exercise, it took me a few days to get it 90% right, but then I completely lost interest in 64-bit coding and abandoned the project. I could dig it out, but of course, it's MasmBasic.

LiaoMi

Quote from: jj2007 on July 24, 2019, 04:09:02 PM
button proc instance:PTR,hparent:PTR,text:PTR,topx:PTR,topy:PTR,wid:PTR,hgt:PTR,idnum:PTR
We all know how params are being passed in x64: rcx, rdx, r8, r9, then QWORDs on the stack. The receiving end will pick what it needs, mostly DWORDs in this case. So technically speaking, you can cheat the assembler in x64, passing only "pointers". But is it wise to do so?

Hi jj2007,

Full SDK 10.0 translate for 64 and 32 bits - http://masm32.com/board/index.php?topic=563.0 - uses this PTR technique, on the forum it is discussed in the section of this sdk. I use it from 2016, during this time there were no problems.

Quote from: habran on December 24, 2014, 06:38:47 AM
I was unable to setup your SDK for use on my computer :(
I am curious if anybody else except you succeeded to set it up
I think that it requires to much work and I am wondering how someone new in programming can use it
I will stick to WinInc, it is simple to use and runs without flow :t
Why use XMASM
It is more clear like this in WinInc:

@DefProto WINOLEAPI, OleCreate, stdcall, ,<:REFCLSID,:REFIID,:DWORD,:LPFORMATETC,:LPOLECLIENTSITE,:LPSTORAGE,:ptr LPVOID>, 28

than this in SDK:
OleCreate PROTO :DWORD ,:DWORD ,:DWORD ,:XMASM ,:XMASM ,:XMASM ,:XMASM

XMASM = PTR