Κυριακή 16 Αυγούστου 2026

Revision 25 Version 15 Assembler x86 Upgrade

This is an example from the new assembler:

The first declare ..lib was not part of assembler. It is a way for M2000 to load and use a function from a dll. I made a new variant as declare ...code. The new variant get an address (the calling address). The old one get a dll name and a name for function or a cardinal number of function using #).

So the first declare make the function MessageBox() as M2000 function plus the name MessageBox as internal name (read only), for the address of the function. For this example we would like to call this function from the machine code. We need the address and the Hwnd value (the handler of current window, because we need the message box to be as a child window of our window). So @Hwnd and @MessageBox now can be used from assembler - Earlier version need a local variable to be used for each of these two. Names that are not known from assembler are looked as local variables. Also Assembler object need to be used after the call to obtain pointers from labels. See that Execute nee lassembler=>labeloffset( ), but Declare Code need assembler=>labelptr( )

Another difference from Execute is that functions can be pass any number of parameters. Execute get zero to 4  (always pass 4 long values), using the WindowProc internal function. Declare lib/code made functions called by DispCallFunc.

Function x86 called using Call Local. This place the caller scope as the callee scope. We need that because we have some local variables from the caller to be used as values from assembler.

You can see declare lib in SQLITE3 module in INFO which use c calls to handle sqlite3.dll. We can define the output variable (see the example which we define long long (int64)).

Although we make a signature for parameters, we can use the three points "..." for any number of parameters - c call. See Help Declare for this (Declare Global MyPrint Lib C "msvcrt.swprintf" { &sBuf$,  sFmt$, ... })

See also ASM4 in INFO file which saw the use of local variables in assembly plus the calling of M2000 code from the machine code.


Declare MessageBox Lib "user32.MessageBoxW" {long alfa, lptext$, lpcaption$, long type}
ASM_TEST = {    
start_code3:
push dword 2 | push dword mCaption | push dword mText |  push dword @HWND
Call @MessageBox
ret    
mText:          dw "HELLO THERE", 0
mCaption:       dw "GEORGE", 0
start_code4:  ; C call then StdCall
push dword [esp+16] | push dword [esp+16]
push dword [esp+16] | push dword [esp+16] ; copy arguments
Call @MessageBox
ret
start_code5:  ; StdCall -> StdCall
push dword [esp+16] | push dword [esp+16]
push dword [esp+16] | push dword [esp+16] ; copy arguments
Call @MessageBox
ret 16
}


Assembler=getobject("","m2000.x86")
function x86 (b as string, &outbuffer, useprep as boolean=false) {
if Assembler=>assemble(b, true) then
local OutPutSize=Assembler=>OutputSize
buffer code outbuffer as byte*OutputSize
Assembler=>BaseAddress = outbuffer(0)
if Assembler=>assemble(b) then
outbuffer=>FillDataFromMem Assembler=>GetOutPtr
else
error "x86 fault 2"
end if
else
error "x86 fault 1"
end if
}
var example1
call local x86(ASM_TEST, &example1)
Declare CallCode code c example1(0) As Long
Print CallCode()
' c call - by default ret value is long but here we place the type.
Declare MsgBox code c assembler=>labelptr("start_code4") {long alfa, lptext$, lpcaption$, long type} as long
Print MsgBox(hwnd, "This is the text", "This is the Caption", 2&)
wait 300
' stdcall
Declare MsgBox2 code assembler=>labelptr("start_code5") {long alfa, lptext$, lpcaption$, long type}
Print MsgBox2(hwnd, "This is the text", "This is the Caption", 2&)