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

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • 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.
« Last Edit: July 08, 2022, 03:03:26 AM by jj2007 »

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • 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: 12982
  • 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: 1031
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: 12982
  • 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: 12982
  • 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: 12982
  • 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)

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #7 on: July 08, 2022, 03:02:50 AM »
JBasic updated, see first post in this thread. Inter alia, I added a simplified version of the Instr_() macro (pHaystack and pNeedle only, case-sensitive). It's over ten times faster than CRT strstr :cool:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## console demo, builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
Init ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
  Print Chr$(10, 10, "This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format")
  Print Str$(", and the needle is at pos %i\n", Instr_("The haystack: does it really contain a needle?", "needle"))
  Inkey Str$("'Duplicate' at pos %i in Windows.inc", Instr_(FileRead$("\Masm32\include\Windows.inc"), "WARNING Duplicate"))
EndOfCode

The exe is 2048 bytes in 32-bit and 2560 bytes in 64-bit mode, see attachment. Output:

Code: [Select]
This program was assembled with UAsm64 in 64-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

This program was assembled with ml64 in 64-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

This program was assembled with AsmC in 64-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

This program was assembled with UAsm64 in 32-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

This program was assembled with ML in 32-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

This program was assembled with AsmC in 32-bit format, and the needle is at pos 40
'Duplicate' at pos 977313 in Windows.inc

HSE

  • Member
  • *****
  • Posts: 2196
  • AMD 7-32 / i3 10-64
Re: Dual 64- and 32-bit Assembly
« Reply #8 on: July 09, 2022, 12:49:14 AM »
Hi JJ!!
 
:biggrin: This is not for beginners.

There is no pt.inc. Not easy at all to know that GetPT.exe fails. 
Equations in Assembly: SmplMath

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #9 on: July 09, 2022, 12:58:33 AM »
Hi Hector,

Thanks for your feedback. Can you give me a hint why GetPT.exe fails?
Did you try to assemble it "by hand" or from QEditor? That would certainly file if pt.inc is not yet installed.
Does it fail if you open the file in RichMasm and hit F6? If so, I will have to specify that requirement.

HSE

  • Member
  • *****
  • Posts: 2196
  • AMD 7-32 / i3 10-64
Re: Dual 64- and 32-bit Assembly
« Reply #10 on: July 09, 2022, 01:14:57 AM »
I see this making last June MB installation. But this is executing GetPT from explorer.
Equations in Assembly: SmplMath

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #11 on: July 09, 2022, 01:32:03 AM »
Mysterious, this message is not mine.

If RichMasm detects an OPT_64 1 in a source, and doesn't find \Masm32\MasmBasic\Res\DualWin.inc, then it launches GetPT.bat to build (from GetPT.asm) GetPT.exe. The latter creates pt.inc and DualWin.inc in \Masm32\MasmBasic\Res\

That works fine on my machine, so I wonder why you get this debug message :rolleyes:

Also I wonder why it says registration error. There is nothing being registered, no DLL, no registry entry, absolutely nothing...

I see that your screenshot ends with "Storing...". Mine shows a bit more:

Code: [Select]
No match for XMM_SAVE_AREA32
No match for ; SID_AND_ATTRIBUTES_HASH
Storing DualWin.inc

14525 API calls in 150 DLLs processed
# bye #

HSE

  • Member
  • *****
  • Posts: 2196
  • AMD 7-32 / i3 10-64
Re: Dual 64- and 32-bit Assembly
« Reply #12 on: July 09, 2022, 01:41:33 AM »
Perhaps an exception  :rolleyes:
Equations in Assembly: SmplMath

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 12982
  • Assembler is fun ;-)
    • MasmBasic
Re: Dual 64- and 32-bit Assembly
« Reply #13 on: July 09, 2022, 01:51:06 AM »
Can you give it a try, please?

HSE

  • Member
  • *****
  • Posts: 2196
  • AMD 7-32 / i3 10-64
Re: Dual 64- and 32-bit Assembly
« Reply #14 on: July 09, 2022, 02:05:56 AM »
Code: [Select]
00406B00 | 83 E6 F0                 | and esi, FFFFFFF0                       |
00406B03 | 89 55 F4                 | mov dword ptr ss:[ebp - C], edx         |
00406B06 | 3B 75 F4                 | cmp esi, dword ptr ss:[ebp - C]         |
00406B09 | 73 3C                    | jae getpt.406B47                        |
00406B0B | 0F 28 06                 | movaps xmm0, dqword ptr ds:[esi]        |<<<<<<<<
00406B0E | 66 0F 74 C1              | pcmpeqb xmm0, xmm1                      |
00406B12 | 66 0F D7 D0              | pmovmskb edx, xmm0                      |
00406B16 | 8D 76 10                 | lea esi, dword ptr ds:[esi + 10]        |
00406B19 | 85 D2                    | test edx, edx                           |
00406B1B | 74 E9                    | je getpt.406B06                         |
00406B1D | 2B CE                    | sub ecx, esi                            |
00406B1F | 74 0F                    | je getpt.406B30                         |

Exception C00..05     [esi]=????   (esi = 006EF000)
Equations in Assembly: SmplMath