News:

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

Main Menu

Using _beginthreadex

Started by TBRANSO1, January 11, 2019, 06:05:16 AM

Previous topic - Next topic

TBRANSO1

Hello all,

I have been learning, studying and practicing both the 32-bit and 64-bit MASM assembly programming.  I have a lot of questions, but limiting this question to the above-mentioned subject.

I am practicing multithreading in assembly.  I have created a few scripts with CreateThread using the win32 library and in both 32 and 64 bit forms.  However, I have struggled a bit on correctly linking the c runtime library with _beginthreadex / _endthreadex.

As a note, I am trying to create the scripts without the use of the masm32 libraries, just because I like making things hard on myself... LOL.

One of the scripts that I am working on uses the CreateThread / ExitThread functions.

I assemble and link as such:
ml threadEx.asm /Zi /c /coff
link threadEx kernel32.lib user32.lib /subsystem:console /entry:main

here is the script:



.686
.XMM
.MODEL flat
; re: .MODEL directive
; if not indicated, all function parameters are passed in right to left
; and ret <size of call parameter stack> is required
; if stdcall is used, then add esp/rsp, <size of call parameter stack>

extern _ExitProcess@4:NEAR
extern _MessageBoxA@16:NEAR
extern _CreateThread@24:NEAR
extern _WaitForSingleObject@8:NEAR
extern _WaitForMultipleObjects@16:NEAR
extern _CloseHandle@4:NEAR
extern _GetStdHandle@4:NEAR
extern _WriteConsoleA@20:NEAR
extern _ExitThread@4:NEAR
;extern _beginthreadex@24:NEAR
;extern _endthreadex@4:NEAR

func_ptr typedef PTR BOOL

public _main

.CONST
INFINITY EQU -1
STDOUT EQU -11

.DATA
counter SDWORD 0
message BYTE "In the main using Assembly", 0Ah, 0
len_msg EQU $-message
t_msg1 BYTE 0Dh, "Creating second thread . . .", 0Ah, 0
len_msg1 EQU $-t_msg1
t_msg2 BYTE "Counter should equal 2000000000: ", 0
t_msg3 BYTE 0Dh, "In second thread . . . ", 0Ah, 0
len_msg3 EQU $-t_msg3
terr_caption BYTE "ERROR!!!!", 0
terr_msg BYTE "CreateThread failed, exiting . . .", 0

.DATA?
string BYTE 10 dup(?)
handle DWORD ?
bytesWritten DWORD ?
hThread1 DWORD ?
hThread2 DWORD ?
thread_ID SDWORD ?
str_len DWORD ?

.CODE
_main PROC
push STDOUT
call _GetStdHandle@4
add esp, 4d
mov handle, eax

push 0
push offset bytesWritten
mov eax, len_msg
mov str_len, eax
push str_len
push offset message
push handle
call _WriteConsoleA@20
add esp, 20d

push offset thread_ID
push 0
push 0
push offset _secondThreadFunc
push 0
push 0
call _CreateThread@24
add esp, 24d
mov hThread1, eax

.if hThread1 == 0
push 0
push offset terr_caption
push offset terr_msg
push 0
call _MessageBoxA@16
add esp, 16d

mov edx, hThread1
push edx
call _CloseHandle@4
jmp _EXIT
.endif

push offset thread_ID
push 0
push 0
push offset _secondThreadFunc
push 0
push 0
call _CreateThread@24
add esp, 24d
mov hThread2, eax

.if hThread2 == 0
push 0
push offset terr_caption
push offset terr_msg
push 0
call _MessageBoxA@16
add esp, 16d

mov edx, hThread2
push edx
call _CloseHandle@4
jmp _EXIT
.endif

push INFINITY
push hThread1
call _WaitForSingleObject@8
add esp, 8d

push INFINITY
push hThread2
call _WaitForSingleObject@8
add esp, 8d

mov esi, offset string
push esi
call _toString ; convert int to string

push 0
push offset t_msg2
push offset string
push 0
call _MessageBoxA@16
add esp, 16d

mov edx, hThread1
push edx
call _CloseHandle@4

mov edx, hThread2
push edx
call _CloseHandle@4

_EXIT:
xor eax, eax
push eax
call _ExitProcess@4
_main ENDP

_secondThreadFunc PROC
push ebp
mov ebp, esp
push STDOUT
call _GetStdHandle@4
add esp, 4
mov handle, eax
mov bytesWritten, 0
push 0
push offset bytesWritten
mov eax, len_msg3
mov str_len, eax
push str_len
push offset t_msg3
push handle
call _WriteConsoleA@20
add esp, 20d
mov ecx, 0
    L_COUNTER:
inc ecx
inc counter
cmp ecx, 2000000000d
jl L_COUNTER
push 0
call _ExitThread@4
mov esp, ebp
pop ebp
ret
_secondThreadFunc ENDP

_toString PROC
pushad
sub esp, 10h
mov edx, dword ptr [ebp+36d] ; string base ptr
mov dword ptr [esp+12d], edx ; string base ptr
mov dword ptr [esp+4d], 0 ; length
mov ecx, counter ; num
mov dword ptr [esp+8d], ecx ; num
test ecx, ecx ; num
jz L_EXIT
mov eax, dword ptr [esp+8d] ; numerator = num
mov ecx, 10d
    L_GETLEN:
inc dword ptr [esp+4d] ; len
xor edx, edx
div ecx
test eax, eax
jnz L_GETLEN
sub edi, edi
mov ebx, 0  ; i
mov eax, dword ptr [esp+8d] ; num
mov edi, dword ptr [esp+4d] ; length
mov esi, dword ptr [esp+12d] ; string base ptr
    L_STRINGIFY:
xor edx, edx
div ecx ; divide by 10, remainder in edx
sub edi, 1 ; len - (i + 1)
add dl, 48d ; rem + 0x30
mov byte ptr [esi+edi], dl ; string[len - (i + 1)] = rem + 0x30
inc ebx
cmp ebx, dword ptr [esp+4d] ; compare to length
jle L_STRINGIFY
    L_EXIT:
mov edi, dword ptr [esp+4d] ; length
mov byte ptr [esi+edi], 0 ; string[len] = '\0'
xor eax, eax
add esp, 10h
popad
ret 4
_toString ENDP
end _main


BTW, I know I have a race condition, I built it that way.. after getting this _beginthreadex issue resolved, I am going to start using the synchronization tools.  :greenclp:

I would like to substitute the CreateThread with the _beginthreadex routines, but am struggling with linking.  I know that it should be linked with msvcrt.lib, but it shows the LINK2001 error undefined references.  I have changed the way I declare the functions several different ways:
extern _beginthreadex@24:NEAR
extern _beginthreadex : PROC
_beginthreadex PROTO, <args x 6>..... (not extended on purpose, just lazy)

Nothing seems to work, I have linked to masm32/lib/msvcrt.lib and I see that seems to link, but I don't want to call the function with INVOKE, I want to use "call" and practice preparing the stack myself.

Funny, if I put "stdcall" in the .MODEL directive, it works, but causes all the other functions to not link, WTHeck???!!!

This leads to another question, however...   I am used to highlevel languages, and setting up the stack is not necessary, so I have been reading up on calling conventions, what does the 32 bit MASM typically use as the calling convention? fastcall or stdcall?  because I am kind of struggling to understand when to use what over the other?  I understand the differences after studying them for the past week.

What calling convention does CreateThread use?  I guess that _beginthreadex uses _stdcall??, as that is the type of secondary function that it requires.  I can see why that there is a lot of grey hairs in the industry over the older win32 api stuff, just a bunch of confusing weirdness that I don't understand completely.

I am also confused about the first processor type directive, I see the older code uses .386, but I have seen all the others used to...I have used them all in this program (.386, .486, .586. .686), they all work??.. if using a modern Intel processor, which number do we use for using win32 MASM?

Another confusing thing that I have encountered reading MASM assembly on the web done by others.  Either some code is wrong, or I just don't understand completely yet, but if the calling conventions say that parameters are entered right to left, I have seen in both 32 and 64 bit, parameters entered left to right (such as calling WriteFile or ReadFile, or WriteConsole) and using the registers (what I thought) was backwards.  I have seen 64 bit code with them going left to right, right to left, or all mixed up??  Either they're wrong or I'm wrong??

Anyways, I just haven't been able to find concrete answers to all my googling, so I am turning to y'all for some help.

Cheers

TimoVJL

crt use cdecl
extern C _beginthreadex :proc
May the source be with you

Raistlin

QuoteI am also confused about the first processor type directive, I see the older code uses .386, but I have seen all the others used to...I have used them all in this program (.386, .486, .586. .686), they all work??.. if using a modern Intel processor, which number do we use for using win32 MASM?

I have'nt helped out in the campus for awhile - so please excuse my rusty response. The answer seems
to be = it's all about the supported instruction set - you wish the assembler to enforce at the time of the
code segment declare. You can for example start off with .386 then shift to .586, and later .686 once you
know the CPU hardware you are using supports such. Most posts of code-snippets you will come-across ignores
such and just uses the .686 directive to indicate full instruction set support for anything since the
32-bit Intel Pentium Pro (circa 1995) / AMD Athlon (circa 1999) Re: From MSDN "Enables assembly of
non-privileged instructions for the Pentium Pro processor"


.686
.model flat, stdcall
option casemap :none   ; case sensitive


.386 = the base 32 bit instruction-set non-privileged introduced on the Intel 386 CPU including Floating Point Instructions (FPU)
(.386 is also the minimum practical instruction set declare for Console based applications - note you need to detect the FPU as certain
386 CPU's [SX variants] didn't have the co-processor)
.486 = the base 32 bit instruction-set non-privileged introduced on the Intel 486 CPU including FPU
(.486 is also the minimum practical instruction set declare for Windows based applications - note you need to detect the FPU as certain
486 CPU's [SX variants] didn't have the co-processor))
.586 = the base 32 bit instruction-set non-privileged introduced on the Intel 586 CPU including FPU (ex. Pentium I)
.686 = the base 32 bit instruction-set non-privileged introduced on the Intel Pentium Pro CPU including FPU (ex. Pentium Pro/II)

The privileged variants have the "P" at the back of the declare, example ".686P" - here privileged means CPU instructions
for input/output and memory management that are not normally available, also known as going into supervisor mode.
Originally the typical usage for privileged instructions included programming a driver or operating system.

Now however, most MASM compatible assemblers require the ".686P" to indicate enhanced instruction set support
<- a blanket statement you want to be able to use any and all instructions, the assembler supports, such as 64-bits.

Other declares that come after the above may include:
.MMX - enables instructions for the Multimedia Extensions [*note: AMD and Intel have differing instruction set implementations]
.XMM - enables instructions for streaming SIMD [single instruction, multiple data] and dependent on the assembler used, also
Advanced Vector Extensions (AVX)

Hope this clarifies somewhat, regards
Raistlin

Are you pondering what I'm pondering? It's time to take over the world ! - let's use ASSEMBLY...

TBRANSO1

@Raistlin  - that's helpful, at least it gives a little better understanding.  Although, I still haven't found out which type of processor the i5/i7 series is: 80386 /80486/80686?  I realize that all modern cpu(s) have backwards compatibility with the original 80386, and from what I read the 80586 was the ill fated Itanium processor, but it does say for the "Pentium" in the MSDN... I noticed now that I am more aware of these things, I see that the MS or GCC compilers use 686/XMM (still don't know what that means)... when I disassemble C++ programs.

I find it easier to use the x64 platform since there are no more directives to setup like that, we just have to worry about stack alignment, which I am getting more comfortable doing with x64... it's when I'm using the x86 win32 or NASM that it seems so perplexing... and stiff learning curve.

For example, I use Windows Subsytem on Linux for my Linux programming environment.  It allows a pretty close to perfect Linux environment, except a few things that aren't possible.  I can't use int 0x80 calls, but can use syscalls, b/c the way I understand it is that the Linux is wrapped on top of the Windows OS, thus a syscall to read, for example, maps to a Windows API.  I also learned that when assembling on the Linux subsystem, it uses the x64 rules with the registers, so the older EAX, EBX, ECX, EDX stuff seems to not be valid, and if I use 32bit registers to pass parameters to functions, I have to use ecx, edx, r8d, r9d... where as if I use the Windows OS to write/assemble/link, I can use either older 32-bit rules or the new 64-bit rules.  It's this kind of stuff that just makes a little bit more of a challenge, and really solidified in my brain as to why assembly programming is not portable without a lot of effort and strain.

I guess that I will look into the privilege part a little deeper, while I am not doing any privileged instruction stuff now.. I see that it allows use of certain instructions.  I guess if I get brave enough to write drivers or something later on I will use that.


TBRANSO1

A little OT, but here's a nice post about privileged instructions, it's aimed at Linux OS, but I got a better grasp from it: http://web.cs.ucla.edu/classes/winter13/cs111/scribe/4a/


Raistlin

Ouch - OK, in very general terms, any CPU
instruction support after the year 2000
should be using a .686P followed by
.MMX, followed by .XMM for modern
coding purposes. The only issue being
that the instructions you use within the
assembler would compile BUT not necessarily
execute on the all platforms - regardless of age.
The point is = you must check for a valid platform
before using the instruction set OR face a crash.
The end.

On another topic you asked about, my advice would
be, at introductory level = always use INVOKE. There's
too many reasons for this, I just don't know where to start.
If you write your own Procedures that pass parameters -
just PROTO it. For built-in kernel APIs this is a no brain-er.
Trust me, only after your code works = logic, can you start
looking at fastcall etc.

Are you pondering what I'm pondering? It's time to take over the world ! - let's use ASSEMBLY...

TBRANSO1

Quote from: Raistlin on January 12, 2019, 04:12:04 AM
Ouch - OK, in very general terms, any CPU
instruction support after the year 2000
should be using a .686P followed by
.MMX, followed by .XMM for modern
coding purposes. The only issue being
that the instructions you use within the
assembler would compile BUT not necessarily
execute on the all platforms - regardless of age.
The point is = you must check for a valid platform
before using the instruction set OR face a crash.
The end.

On another topic you asked about, my advice would
be, at introductory level = always use INVOKE. There's
too many reasons for this, I just don't know where to start.
If you write your own Procedures that pass parameters -
just PROTO it. For built-in kernel APIs this is a no brain-er.
Trust me, only after your code works = logic, can you start
looking at fastcall etc.

Excellent points... thanks, I think that I'll take your advice and back trace a little before I dive deeper into the abyss.

I went and changed up the directives and declarations at the top of the script, as shown above in original post.

It now looks like:

I made the following changes, added stdcall and then changed all the function declarations with the PROTO syntax, add the case sensative directive
and the dang thing worked! as before...

And this is why I am just a learner, b/c I don't understand how or why it worked vs. the first example (the first example worked, I just couldn't figure out how to link and use _beginthreadex)

EDIT: I spoke too soon, it compiles and links as shown below, but can't call or invoke, throws up link errors.

threadEx.obj : error LNK2001: unresolved external symbol __beginthreadex@24
threadEx.obj : error LNK2001: unresolved external symbol __endthreadex@4
threadEx.exe : fatal error LNK1120: 2 unresolved externals


; link threadEx kernel32.lib user32.lib /subsystem:console /entry:main
; ml threadEx.asm /c /coff /Zi

.686
.XMM
.MODEL flat, stdcall
option casemap: none
.stack 4096

; re: .MODEL directive
; if not indicated, all function parameters are passed in right to left
; and ret <size of call parameter stack> is required
; if stdcall is used, then add esp/rsp, <size of call parameter stack>

ExitProcess PROTO, dwExitCode:DWORD
GetStdHandle PROTO, handle:DWORD
MessageBoxA PROTO, hInstance:DWORD, captionPtr:PTR DWORD, messagePtr:PTR DWORD, button:DWORD
WriteConsoleA PROTO, handle:DWORD, messagePtr:PTR DWORD, buflen:DWORD, bytes:PTR DWORD, attr:DWORD
CloseHandle PROTO, handle:DWORD
CreateThread PROTO, secattr:PTR DWORD, ssize:DWORD, funPtr:PTR DWORD, lpParam:PTR DWORD, dwCrFlgs:DWORD, lpTidPtr:PTR DWORD
WaitForSingleObject PROTO, thread:DWORD, interval:SDWORD
_beginthreadex PROTO, secattr:PTR DWORD, ssize:DWORD, funPtr:PTR DWORD, lpParam:PTR DWORD, dwCrFlgs:DWORD, lpTidPtr:PTR DWORD
_endthreadex PROTO, dwExitCode:DWORD
ExitThread PROTO, dwExitCode:DWORD

;extern _ExitProcess@4:NEAR
;extern _MessageBoxA@16:NEAR
;extern _CreateThread@24:NEAR
;extern _WaitForSingleObject@8:NEAR
;extern _WaitForMultipleObjects@16:NEAR
;extern _CloseHandle@4:NEAR
;extern _GetStdHandle@4:NEAR
;extern _WriteConsoleA@20:NEAR
;extern _ExitThread@4:NEAR

func_ptr typedef PTR BOOL

public _main

.CONST
INFINITY EQU -1
STDOUT EQU -11

.DATA
counter SDWORD 0
message BYTE "Inside main with Assembly", 0Dh, 0Ah
len_msg EQU $-message
t_msg1 BYTE "Creating second thread . . .", 0Ah, 0
len_msg1 EQU $-t_msg1
t_msg2 BYTE "Counter should equal 2000000000: ", 0
t_msg3 BYTE "In second thread . . . ", 0Dh, 0Ah
len_msg3 EQU $-t_msg3
terr_caption BYTE "ERROR!!!!", 0
terr_msg BYTE "CreateThread failed, exiting . . .", 0

.DATA?
string BYTE 10 dup(?)
handle DWORD ?
bytesWritten DWORD ?
hThread1 DWORD ?
hThread2 DWORD ?
thread_ID SDWORD ?
str_len DWORD ?

.CODE
_main PROC
push STDOUT
call GetStdHandle
;call _GetStdHandle@4
add esp, 4d
mov handle, eax

push 0
push offset bytesWritten
mov eax, len_msg
mov str_len, eax
push str_len
push offset message
push handle
call WriteConsoleA
;call _WriteConsoleA@20
add esp, 20d

push offset thread_ID
push 0
push 0
push offset _secondThreadFunc
push 0
push 0
call CreateThread
;call _CreateThread@24
add esp, 24d
mov hThread1, eax

.if hThread1 == 0
push 0
push offset terr_caption
push offset terr_msg
push 0
call MessageBoxA
;call _MessageBoxA@16
add esp, 16d

mov edx, hThread1
push edx
call CloseHandle
;call _CloseHandle@4
jmp _EXIT
.endif

push offset thread_ID
push 0
push 0
push offset _secondThreadFunc
push 0
push 0
call CreateThread
;call _CreateThread@24
add esp, 24d
mov hThread2, eax

.if hThread2 == 0
push 0
push offset terr_caption
push offset terr_msg
push 0
call MessageBoxA
;call _MessageBoxA@16
add esp, 16d

mov edx, hThread2
push edx
call CloseHandle
;call _CloseHandle@4
jmp _EXIT
.endif

push INFINITY
push hThread1
call WaitForSingleObject
;call _WaitForSingleObject@8
add esp, 8d

push INFINITY
push hThread2
call WaitForSingleObject
;call _WaitForSingleObject@8
add esp, 8d

mov esi, offset string
push esi
call _toString ; convert int to string

push 0
push offset t_msg2
push offset string
push 0
call MessageBoxA
;call _MessageBoxA@16
add esp, 16d

mov edx, hThread1
push edx
call CloseHandle
;call _CloseHandle@4

mov edx, hThread2
push edx
call CloseHandle
;call _CloseHandle@4
    _EXIT:
xor eax, eax
push eax
call ExitProcess
;call _ExitProcess@4
_main ENDP

_secondThreadFunc PROC
push ebp
mov ebp, esp
push STDOUT
call GetStdHandle
;call _GetStdHandle@4
add esp, 4
mov handle, eax
mov bytesWritten, 0
push 0
push offset bytesWritten
mov eax, len_msg3
mov str_len, eax
push str_len
push offset t_msg3
push handle
call WriteConsoleA
;call _WriteConsoleA@20
add esp, 20d
mov ecx, 0
    L_COUNTER:
inc ecx
inc counter
cmp ecx, 2000000000d
jl L_COUNTER
push 0
call ExitThread
;call _ExitThread@4
mov esp, ebp
pop ebp
ret
_secondThreadFunc ENDP

_toString PROC
pushad
sub esp, 10h
mov edx, dword ptr [ebp+36d] ; string base ptr
mov dword ptr [esp+12d], edx ; string base ptr
mov dword ptr [esp+4d], 0 ; length
mov ecx, counter ; num
mov dword ptr [esp+8d], ecx ; num
test ecx, ecx ; num
jz L_EXIT
mov eax, dword ptr [esp+8d] ; numerator = num
mov ecx, 10d
    L_GETLEN:
inc dword ptr [esp+4d] ; len
xor edx, edx
div ecx
test eax, eax
jnz L_GETLEN
sub edi, edi
mov ebx, 0  ; i
mov eax, dword ptr [esp+8d] ; num
mov edi, dword ptr [esp+4d] ; length
mov esi, dword ptr [esp+12d] ; string base ptr
    L_STRINGIFY:
xor edx, edx
div ecx ; divide by 10, remainder in edx
sub edi, 1 ; len - (i + 1)
add dl, 48d ; rem + 0x30
mov byte ptr [esi+edi], dl ; string[len - (i + 1)] = rem + 0x30
inc ebx
cmp ebx, dword ptr [esp+4d] ; compare to length
jle L_STRINGIFY
    L_EXIT:
mov edi, dword ptr [esp+4d] ; length
mov byte ptr [esi+edi], 0 ; string[len] = '\0'
xor eax, eax
add esp, 10h
popad
ret 4
_toString ENDP
end _main

Raistlin

#7
Honestly I never looked at your code, only your questions.
Now that I have, the recommendations are:
1) INVOKE will make the code much smaller (text), readable
maintainable, reliable without speed loss = right off the bat.
2) Your love for an external C library calls has to go. The
MASM32 library is on average (understatement) faster
whilst containing better routines and provides more
flexibility for things like string manipulation and so much more.
3) Some of your API calls have native Kernel equivalents
which you are wrapping in C <- WHY ? You do realize the
C code is just calling the kernel API = thus loosing cycles,
for an API you could have called yourself directly ?

If I have time tomorrow I'll clean it up for you to demonstrate
the difference.
[Later Edit : hmm ok did 'nt realise the 64 bit bombshell as pointed out by hutch & co as I don't code in it. Didnt see a single 64bit register in your code either - how strange. Just haven't found the need for 64bit code yet myself.  I do all my multithread stuff in 32bit without these kinds of headaches]
Are you pondering what I'm pondering? It's time to take over the world ! - let's use ASSEMBLY...

TimoVJL

Quote from: TBRANSO1 on January 12, 2019, 04:51:10 AM
threadEx.obj : error LNK2001: unresolved external symbol __beginthreadex@24
threadEx.obj : error LNK2001: unresolved external symbol __endthreadex@4
threadEx.exe : fatal error LNK1120: 2 unresolved externals
are not a STDCALL functions, use
_beginthreadex PROTO C secattr:PTR DWORD, ssize:DWORD, funPtr:PTR DWORD, lpParam:PTR DWORD, dwCrFlgs:DWORD, lpTidPtr:PTR DWORD
_endthreadex PROTO C dwExitCode:DWORD
May the source be with you

hutch--

TBRANSO1,

A number of things, win32 uses a different calling convention to Win64, almost all API functions are STDCALL where a rare few API calls are C calling convention. With MSVCRT function calls they are normally C calling convention (you balance the stack after the call has been returned).

Win64 is a different animal, it uses a Microsoft specified FASTCALL which uses registers for the first 4 arguments and stack locations for any extra arguments. The first 4 stack locations are reserved for what is called "shadow space" and at higher addresses the stack is used at intervals of 8 bytes. If the stack is not aligned correctly the app just won't start.

This is why I bothered to write a variety of stackframe macros to save stack fiddling. It can be done manually but it is a cluttered mess that is very easy to get wrong. To write production code you don't need that level of clutter. The win with doing it this way is you can write win64 in much the same way as the 32 bit version.

In win32 its easy enough to write push/call procedure calls if you have a reason to do it but win64 is far more complicated. On the bright side, win64 is more efficient with lower calling overhead and twice as many registers so once you have your stackframes up and running, you can do more with all of the extra registers.

TBRANSO1

Quote from: Raistlin on January 12, 2019, 05:34:23 AM
2) Your love for an external C library calls has to go.

It's not a love, it's what I am most familiar with at the moment.

TBRANSO1

Quote from: hutch-- on January 12, 2019, 06:29:00 AM
TBRANSO1,

A number of things, win32 uses a different calling convention to Win64, almost all API functions are STDCALL where a rare few API calls are C calling convention. With MSVCRT function calls they are normally C calling convention (you balance the stack after the call has been returned).

Win64 is a different animal, it uses a Microsoft specified FASTCALL which uses registers for the first 4 arguments and stack locations for any extra arguments. The first 4 stack locations are reserved for what is called "shadow space" and at higher addresses the stack is used at intervals of 8 bytes. If the stack is not aligned correctly the app just won't start.

This is why I bothered to write a variety of stackframe macros to save stack fiddling. It can be done manually but it is a cluttered mess that is very easy to get wrong. To write production code you don't need that level of clutter. The win with doing it this way is you can write win64 in much the same way as the 32 bit version.

In win32 its easy enough to write push/call procedure calls if you have a reason to do it but win64 is far more complicated. On the bright side, win64 is more efficient with lower calling overhead and twice as many registers so once you have your stackframes up and running, you can do more with all of the extra registers.

I admit that I've been stubborn, and probably make things harder on myself.

can I use the functions with call or are they only done with invoke statements?


hutch--

Depends on if you are writing 32 or 64 bit code. In 32 bit, you can use either, the "invoke" notation is simpler and for someone who is new to this style of assembler, a lot safer.

In 64 bit if you want to code manually you will have to learn the win64 version of FASTCALL, stack balancing and the order of shadow space and stack locations for more than 4 arguments. It is advisable to use the high level calling macros, the name "invoke" is just a wrapper for a more general macro that performs call automation.

Raistlin

Please have a look at the excellent threading examples in your MASM32 directory
C:\masm32\examples\threads\mprocasm <- this one in particular is very close to
what you are trying to do TBRANSO1 - it also demonatrates the power of Invoke &
the MASM32 Lib macros - resulting in cleaner code.
Are you pondering what I'm pondering? It's time to take over the world ! - let's use ASSEMBLY...

daydreamer

I think if you forget .686 number completely you become very restricted,masm think you are coding for original 8086
if you need much processing power,I recommend you try 128bit(SSE)/256bit(AVX)/512bit(AVX2) SIMD solution too
good to process many floating point numbers simultanously,even many integer numbers
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding