Author Topic: Dual 64- and 32-bit Assembly  (Read 1134 times)

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
Dual 64- and 32-bit Assembly
« on: March 30, 2021, 02:08:44 AM »
Attached the installer of the JBasic library. JBasic sources assemble either in 64- or in 32-bit, depending on the OPT_64 setting:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
Init ; OPT_64 0 ; put 0 for 32 bit, 1 for 64 bit assembly
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
EndOfCode

With different OPT_64 settings and assemblers:
Code: [Select]
This program was assembled with UAsm64 in 64-bit format.
This program was assembled with ml64 in 64-bit format.
This program was assembled with ml in 32-bit format.
This program was assembled with AsmC in 32-bit format.
This program was assembled with AsmC in 64-bit format.
This program was assembled with UAsm64 in 32-bit format.

Currently, about 70 macros are available, as a subset of the almost 500 MasmBasic macros (e.g. Open, Print, FileRead$(), Chr$, Hex$, Str$, Len...). Inter alia, the deb macro can be used in 64-bit code.

System requirements:
- a 64-bit flavour of Windows
- the Masm32 SDK
- UAsm64

To use AsmC instead of UAsm64, change the *.bat files accordingly.

It is possible (but not recommended) to use ML64. High level syntax such as .if ... .else ... .endif will not work with the 64-bit version of MASM.

Please report any installation or usage problems below this post.

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #1 on: March 30, 2021, 07:48:26 AM »
A simple program that writes some strings to a file, then opens the file for input and prints the content to the console. Afterwards, the first 1000 bytes of \Masm32\include\Windows.inc are shown in a MessageBox:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
Init ; OPT_64 1 ; 0 for 32 bit, 1 for 64 bit assembly
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
  Open "O", #1, "TestO.txt"
  Print "Hello world", CrLf$, "take it easy", Str$(" \nThe file handle is %i\n\n", rax)
  Print #1, "hello world", CrLf$, "written to a file", Str$(" \nThe value of rax is %i", rax)
  Close #1
  PrintLine "The content of the file: ", CrLf$, "[", FileRead$("TestO.txt"), "]"
  Kill "TestO.txt" ; cleanup
  mov rsi, FileRead$("\Masm32\include\Windows.inc")
  mov byte ptr [rsi+1000], 0 ; don't overload the MessageBox ;-)
  MsgBox 0, rsi, "Hi", MB_OK
EndOfCode

Output (without MsgBox):
Code: [Select]
This program was assembled with UAsm64 in 64-bit format.
Hello world
take it easy
The file handle is 60

The content of the file:
[hello world
written to a file
The value of rax is 1]

Source and executables attached.

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #2 on: March 30, 2021, 08:28:24 AM »
A variant of the previous example, with a Mid$() macro:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc
Mid$ MACRO pString, mStart, mBytes
  mov rdx, repargA(pString)
  mov rax, mStart
  add rax, rdx
  mov rdx, mBytes
  and byte ptr [rax+rdx], 0
  EXITM <rax>
ENDM
Init ; OPT_64 1 ; 0 for 32 bit, 1 for 64 bit assembly
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
  Open "A", #1, "TestO.txt" ; append to existing file
  Print "Hello world", CrLf$, "take it easy", Str$(" \nThe file handle is %i\n\n", rax)
  Print #1, "Strings", CrLf$, "written to a file:", CrLf$, "the entry point is at ", Hex$(offset start), CrLf$
  Close #1
  PrintLine "The content of the file: ", CrLf$, "[", FileRead$("TestO.txt"), "]"
  MsgBox 0, Mid$(FileRead$("\Masm32\include\Windows.inc"), 92, 1036), "Hi", MB_OK
EndOfCode

Source and executables attached. You may note that the 64-bit executable is somewhat larger (4096 bytes) than the 32-bit exe (2560 bytes).

LiaoMi

  • Member
  • ****
  • Posts: 922
Re: Dual 64- and 32-bit Assembly
« Reply #3 on: March 30, 2021, 07:33:14 PM »
Hi jj2007,

what are the differences from MasmBasic, without taking into account the x64 support  :icon_idea:

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #4 on: March 30, 2021, 07:47:12 PM »
Hi jj2007,

what are the differences from MasmBasic, without taking into account the x64 support  :icon_idea:

MasmBasic has over 700 macros, of which 488 are documented. Inter alia, it has strong support for string and numeric arrays (see Recall, Insert & Delete, QSort, etc). Mainly because of the array and string engines, a "Hello World" in MasmBasic is 27.5kBytes, far too much for the taste of the average assembly programmer ;-)

In contrast, JBasic just offers some basic services, such as reading a file into memory, printing, input, etc.; the executables are tiny - for example, 4608 bytes for a full-fledged 64-bit Gui application with a menu and an edit control, see below. However, it does have the deb macro, which I consider essential for any coding, and which IMHO can speed up the learning of a novice.

And it comes with a simple 100kB installer that gives you jinvoke access to 13,000+ WinAPI calls... isn't that cute?  :tongue:

Btw jinvoke does count and check arguments, so a n00b fighting with CreateWindowEx may see forced error: ## too many arguments for CreateWindowEx ## or forced error: ## not enough arguments for CreateWindowEx ## (I strongly believe in holding the hot little hands of n00bs; google site:masm32.com "hot little" in case you don't get the joke)

This is the Gui demo (see ?:\Masm32\MasmBasic\Res\SkelDualGUI.asm). It assembles fine with UAsm, AsmC and ML64:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; hit F6 to build this program
wcx WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW or CS_OWNDC, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, SIZE_P>
txClass db "JBasicGUI", 0 ; class name, will be registered below
lastMenu DWORD ? ; OPT_64 1 ; 0=32-bit, 1=64-bit assembly
hEdit SIZE_P ? ; the handle to the edit control is a pointer, 32 or 64 bits wide
Init
WinMain proc
LOCAL msg:MSG
  wc equ [rbx.WNDCLASSEX] ; we use an equate for better readability
  lea rbx, wcx
  mov wc.hInstance, rv(GetModuleHandle, 0)
  mov wc.hIcon, rv(LoadIcon, rax, IDI_APPLICATION) ; click on the first Rsrc bookmark to change the icon
  mov wc.hIconSm, rax ; the rv macro returns results in rax
  mov wc.hCursor, rv(LoadCursor, NULL, IDC_ARROW) ; get a cursor
  jinvoke RegisterClassEx, rbx ; the window class needs to be registered
  wsStyle=WS_OVERLAPPEDWINDOW or WS_VISIBLE or WS_CLIPCHILDREN
  jinvoke CreateWindowEx, 0, wc.lpszClassName, Chr$("Hello World"), wsStyle, 300+320*@64, 127, 300, 200, NULL, rv(LoadMenu, wc.hInstance, 100), wc.hInstance, NULL
  msgLoop: jinvoke TranslateMessage, addr msg ; translates virtual-key messages into character messages
jinvoke DispatchMessage, addr msg ; dispatches a message to a window procedure
jinvoke GetMessage, addr msg, 0, 0, 0 ; retrieve a message from the queue, and return a BOOL
mcs inc eax : shr eax, 1 : jne msgLoop ; quit if GetMessage returned 0 (exit OK) or -1 (error)
  jinvoke ExitProcess, msg.wParam
WinMain endp
WndProc proc <cb cs> uses rsi rdi rbx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL ps:PAINTSTRUCT, MyR4:REAL4
  Switch_ uMsg
  Case_ WM_CREATE
reStyle=WS_VISIBLE or WS_CHILD or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_NOHIDESEL
jinvoke LoadLibrary, Chr$("RichEd20") ; we want to add a RichEdit control
ID_EDIT=111
jinvoke CreateWindowEx, WS_EX_CLIENTEDGE, Chr$("RichEdit20A"), NULL, reStyle, 0, 0, 1, 1, hWnd, ID_EDIT, wcx.hInstance, NULL
mov hEdit, rax ; you may need this global variable for further processing
xchg rax, rbx ; use a persistent register for the handle
and MyR4, 0 ; just for fun:
jinvoke SendMessage, rbx, WM_SETTEXT, 0, MyR4 ; ** Warning, line 35: passing a REAL4 may not work **
jinvoke SendMessage, rbx, EM_SETTARGETDEVICE, 0, 0 ; word wrap on
jinvoke SendMessage, rbx, EM_EXLIMITTEXT, 0, -1 ; no limit
jinvoke SendMessage, rbx, WM_SETFONT, rv(GetStockObject, ANSI_FIXED_FONT), 0
jinvoke SetWindowText, rbx, Str$(Chr$("This template was built with", 13, 10, @AsmUsed$(1), " in %i-bit mode", 13, 10), jBits)
  Case_ WM_KEYDOWN
cmp wParam, VK_ESCAPE
jne @F
forceClose: jinvoke SendMessage, hWnd, WM_CLOSE, 0, 0
@@:
  Case_ WM_COMMAND
movsx eax, word ptr wParam
mov lastMenu, eax
jinvoke InvalidateRect, hWnd, 0, 1
cmp word ptr wParam, 106
je forceClose
  Case_ WM_PAINT
jinvoke BeginPaint, hWnd, addr ps
xchg rax, rsi
PtDC equ rsi
jinvoke SetTextColor, PtDC, RgbCol(160, 0, 0)
cmp lastMenu, 107
jne @F
jinvoke SetBkColor, PtDC, RgbCol(255, 255, 240)
jinvoke TextOut, PtDC, 7, 2, Str$(Chr$("Assembled with ", @AsmUsed$(1), " in %i-bit mode", 13, 10), jBits), s$Len
jmp EndPt
  @@: jinvoke SetBkColor, PtDC, RgbCol(204, 255, 240)
jinvoke TextOut, PtDC, 7, 2, Str$("Command or menu clicked: %i", lastMenu), s$Len ; Str$() and Hex$() return a special length variable s$Len
  EndPt: jinvoke EndPaint, hWnd, addr ps
  Case_ WM_SIZE ; adjust shape of edit control to main window
movzx eax, word ptr lParam ; width of client area
movzx edx, word ptr lParam+2 ; height
sub eax, 17 ; these 32-bit instructions sign-extend the upper 32 bits
sub edx, 37 ; of the 64-bit registers and are one byte shorter
jinvoke MoveWindow, hEdit, 7, 30, rax, rdx, 1
  Case_ WM_DESTROY
jinvoke PostQuitMessage, NULL ; quit after WM_CLOSE
  Endsw_
  jinvoke DefWindowProc, hWnd, uMsg, wParam, lParam ; default processing
  ret
WndProc endp
EndOfCode

Current JBasic macros:
Chr$
FP4
FP8
FP10
CodeSize
wMsgBox
RgbCol
MsgBox
Hex$
Str$
Input$
Len
wLen
Val
PrintLine
Inkey
Print
Open
Close
Kill
FileRead$
@AsmUsed$
ComCtl32$
nops
mPush
jinvoke
void
Err$
deb
Init
j@end
Cls
SetProto
_Regs
Switch_
Case_
Default_
Endsw_
CL$
Oword16
PushX
PopX
mcs
Switch_
Case_
Default_
Endsw_
Link$
Rand64
Rand
GuidFromString

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #5 on: March 30, 2021, 09:02:50 PM »
Re jinvoke: take the console demo at ?:\Masm32\MasmBasic\Res\SkelDualConsole.asm and add the following:
Code: [Select]
  CodeSize callSayHi
  int 3
  callSayHi_s:
  jinvoke SayHi, Chr$("Wow, it works!!!!"), 222, 333, 444, 555
  callSayHi_endp:

Output:
The locals are NOT cleared:
38      bytes for callSayHi
** on entry to the proc **

Now take away the "j" from the code above, so that it uses (with UAsm and AsmC) the built-in invoke macro...

Under the hood, JBasic jinvoke:
Code: [Select]
000000014000250C  | CC                      | int3                            |
000000014000250D  | 48 C7 44 24 20 2B 02 00 | mov qword ptr ss:[rsp+20],22B   | 555
0000000140002516  | 41 B9 BC 01 00 00       | mov r9d,1BC                     | 444
000000014000251C  | 41 B8 4D 01 00 00       | mov r8d,14D                     | 333
0000000140002522  | BA DE 00 00 00          | mov edx,DE                      | 222
0000000140002527  | 48 8D 0D 90 1D 00 00    | lea rcx,qword ptr ds:[1400042BE | 1400042BE:"Wow, it works!!!!"
000000014000252E  | E8 CE EA FF FF          | call 140001001                  | SayHi

Under the hood, UAsm invoke:
Code: [Select]
000000014000250C  | CC                      | int3                            |
000000014000250D  | 48 83 EC 30             | sub rsp,30                      | UAsm fumbles the stack
0000000140002511  | 48 B9 BE 42 00 40 01 00 | movabs rcx,skeldualconsole64.14 | 1400042BE:"Wow, it works!!!!"
000000014000251B  | BA DE 00 00 00          | mov edx,DE                      |
0000000140002520  | 41 B8 4D 01 00 00       | mov r8d,14D                     |
0000000140002526  | 41 B9 BC 01 00 00       | mov r9d,1BC                     |
000000014000252C  | C7 44 24 20 2B 02 00 00 | mov dword ptr ss:[rsp+20],22B   |
0000000140002534  | E8 C8 EA FF FF          | call 140001001                  |
0000000140002539  | 48 83 C4 30             | add rsp,30                      | UAsm fumbles the stack

Note that UAsm and AsmC wil fumble the stack three times if you use this (see Shadow space in 64-bit programming):
Code: [Select]
  invoke SayHi, Chr$("Wow, it works!!!!"), 222, 333, 444, 555
  invoke SayHi, Chr$("Wow, it works!!!!"), 222, 333, 444, 555
  invoke SayHi, Chr$("Wow, it works!!!!"), 222, 333, 444, 555

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 11552
  • Assembler is fun ;-)
    • MasmBasic
64-bit OpenFileName
« Reply #6 on: May 07, 2021, 07:14:35 AM »
Just for fun :tongue:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## This demo builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
SetGlobals ofn:OPENFILENAME, buffer[MAX_PATH]:BYTE
Init ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
  Print ComCtl32$("It uses common controls version %i.%i\n\n") ; see EndOfCode below
  mov ofn.lStructSize, OPENFILENAME
  mov ofn.hwndOwner, rv(GetForegroundWindow)
  mov rax, Chr$("Sources", 0, "*.as?", 0, "Resources", 0, "*.rc", 0, "Includes", 0, "*.inc", 0, "All files", 0, "*.*", 0, 0)
  mov ofn.lpstrFilter, rax
  lea rdi, buffer
  jinvoke lstrcpy, rdi, Chr$("Testme.asm")
  mov ofn.lpstrFile, rdi
  mov ofn.nMaxFile, MAX_PATH
  mov rax, Chr$("Grab a file:")
  mov ofn.lpstrTitle, rax
  mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_EXPLORER or OFN_NOCHANGEDIR
  mov rax, Chr$("asm")
  mov ofn.lpstrDefExt, rax
  jinvoke GetOpenFileName, addr ofn
  lea rdx, buffer
  jinvoke MessageBox, 0, addr buffer, Chr$("Your choice:"), MB_OK or MB_SETFOREGROUND
EndOfCode XP ; XP adds a manifest (UAsm only)