The MASM Forum

General => The Campus => Topic started by: TBRANSO1 on January 11, 2019, 06:05:16 AM

Title: Using _beginthreadex
Post by: TBRANSO1 on January 11, 2019, 06:05:16 AM
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
Title: Re: Using _beginthreadex
Post by: TimoVJL on January 11, 2019, 08:38:40 AM
crt use cdecl
extern C _beginthreadex :proc
Title: Re: Using _beginthreadex
Post by: Raistlin on January 11, 2019, 07:27:39 PM
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

Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 12, 2019, 02:28:57 AM
@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.

Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 12, 2019, 03:02:17 AM
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/

Title: Re: Using _beginthreadex
Post by: 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.

Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 12, 2019, 04:51:10 AM
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
Title: Re: Using _beginthreadex
Post by: Raistlin on January 12, 2019, 05:34:23 AM
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]
Title: Re: Using _beginthreadex
Post by: TimoVJL on January 12, 2019, 06:00:14 AM
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
Title: Re: Using _beginthreadex
Post by: 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.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 12, 2019, 09:50:26 AM
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.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 12, 2019, 11:37:01 AM
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?

Title: Re: Using _beginthreadex
Post by: hutch-- on January 12, 2019, 11:59:32 AM
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.
Title: Re: Using _beginthreadex
Post by: Raistlin on January 12, 2019, 05:08:02 PM
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.
Title: Re: Using _beginthreadex
Post by: daydreamer on January 12, 2019, 07:22:47 PM
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
Title: Re: Using _beginthreadex
Post by: hutch-- on January 12, 2019, 09:30:24 PM
Magnus,

Thread behaviour is not related to instruction sets, it is related to an OS capacity to run different threads on different cores if they are available. The example tests 1 thread, then 2 threads then 4 threads. If you run the test on an old single core machine you will get very different timings as the single code has to time slice to distribute the processing across multiple threads.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 03:01:15 AM
Quote from: Raistlin on January 12, 2019, 05:08:02 PM
Please have a look at the excellent threading examples in your MASM32 directory
C:\masm32\examples\threads\mprocasm

Got it

the only feeling that I get from using invoke, just pick a function and invoke it automagically, kind of feels high level language-ish. This is something that I kind of want to take a break from.  I enjoy the feeling of setting up the registers and moving data around myself, and get the program to work.  It's just doesn't feel like "assembly" should, that's how I feel at the moment.  8)

I have actually googled for it but haven't found a source yet, but where can I find the actual macro code of the invoke statement?

Title: Re: Using _beginthreadex
Post by: TimoVJL on January 13, 2019, 03:18:03 AM
In ml.exe INVOKE is a directive / pseudo-instruction, not an actual MACRO
Title: Re: Using _beginthreadex
Post by: hutch-- on January 13, 2019, 03:23:27 AM
> where can I find the actual macro code of the invoke statement?

You won't, its built into ML.EXE.

What does "invoke" do ?

1. First you define a prototype.
2. When you "invoke" a procedure, ML.EXE checks the arguments against the prototype.
3. If your procedure call matches the prototype, invoke then makes the call according to the calling convention.

invoke MessageBox,hParent,pMsg,pTitle,MB_OK

ML.EXE converts this into the following.

push MB_OK
push pTitle
push pMsg
push hParent
call MessageBox

You can do this manually but it comes back to bite you when dealing with complexity. In a single simple instance it works fine but try and code a complex WndProc() and it will turn into a nightmare. All you need to do is know what "invoke" does and you will no longer have to code procedure calls manually.

Just as an example, the lowest level you can code assembler in MASM with is direct DB sequences. There are some instances when you actually DO code DB sequences but if you tried to write an app that way you may finish it in time for Win3000.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 03:36:25 AM
Quote from: hutch-- on January 13, 2019, 03:23:27 AM
> where can I find the actual macro code of the invoke statement?

You won't, its built into ML.EXE.

What does "invoke" do ?

1. First you define a prototype.
2. When you "invoke" a procedure, ML.EXE checks the arguments against the prototype.
3. If your procedure call matches the prototype, invoke then makes the call according to the calling convention.

invoke MessageBox,hParent,pMsg,pTitle,MB_OK

ML.EXE converts this into the following.

push MB_OK
push pTitle
push pMsg
push hParent
call MessageBox

You can do this manually but it comes back to bite you when dealing with complexity. In a single simple instance it works fine but try and code a complex WndProc() and it will turn into a nightmare. All you need to do is know what "invoke" does and you will no longer have to code procedure calls manually.

Just as an example, the lowest level you can code assembler in MASM with is direct DB sequences. There are some instances when you actually DO code DB sequences but if you tried to write an app that way you may finish it in time for Win3000.

Cool

I just found this bit in the thread examples

OMG, that's crazy, in the masm32rt.inc... you've put all the dependencies the boilerplate intro and everything.

I think this has confused me when I have looked at so many different example on the internet, that when someone is using or not using certain platforms, it is throwing me off and why I see so many differences in the code.  Now, I see the light.   I just need to read through everything.

mov thread1, rv(CreateThread,NULL,NULL,ADDR test_thread,[esi],NULL,ADDR var)

I assume this means, "return value"?...

Don't take this the wrong way, please... but when I see this... it just reminds of OOP, how many clock cycles is this macro vs. just the standard mov thread1, eax?

This is why I don't find the offshoots of assembly like HLA, FASM and others too appealing, it's just too hard to read, I have to search out everything in a multitude of files to track down code, just seems like trying to hard to make it into an OOP-lite.  I guess that I am a little burnt out on OOP at the moment and find solace in the bare-bones basics.  I just feel that OOP with all the great intentions from the original minds has turned into a monstrosity of enormous code... I understand that it is necessary, but I think there are situations where it has now become so verbose, so big, so abstract, it's almost impossible to read and maintain, further compounded by the fact that there are a lot of novices (like myself), that don't understand the interrelationship of dependencies (such as using Python/Ruby/or even Java), whereby using too many gems or packages of duplicate code and installing new gems/packages on top of each other, just because a programmer thinks that they need some little functionality out of that library, when in reality it is much easier to figure out how to code it themselves without the extra baggage.  For example, in a popular Ruby concurrency library, it is so bloated with code that when you need something atomic and you look at the file, it uses the Ruby mutex and condition variables that call to the C-Ruby source that call to the C library that call to the Kernel, further exacerbated by wrapping everything in a Job Factory and Delegators for just a freakin' atomic integer... I haven't measured but I'd assume that at least 1 mb of heap is created for this stupid little integer.  I love Ruby, don't get me wrong, but this is now almost required thinking in Ruby, just bloat it up... and then when everything breaks... OMG... the backtrack is ridiculous trying to track down what happened.

Sorry for my rant...  :icon_redface:

and I've seen the LOCAL directive, and read the description on MSDN... this is like declaring a local variable inside a function? so basically it's an easier alternative to use something like stack or base pointer reference like [esp/rsp+offset] or [ebp/rbp-offset]?

Hutch, on a side note.

If I am using the VS Studio IDE, I get duplication description errors if I include your files.  Should I remove the references in the linker tab to the MS lib(s)? and just use your "includes"?

On another note, I created my first Windows hello world app... not as scary as I first thought, after starting to read the MSDN windows docs, I see how the pieces fit together.  It's similar to using QT or Py GTK and stuff, just gotta learn how to do it in assembly... Oh, this great... one step closer to getting started on doing my project application.  I am going to a UML style diagram to start working it out, and if when I have a simple working model, you all are the guinea pigs.  :lol:

A hint to what I am thinking... there was this gui application created by a University professor 20 to 25 years ago that demonstrated multi threading to students, and showed a lot of visual ways how deadlocks, race conditions take place, and synchronization... it appears to have been abandoned, but was written in circa 95 to 99 windows, it seemed to get updates for while, but then looks like it was just abandoned in the early-mid 2000's... as I am working toward a master's degree at the moment, I would like to redo this program for a project and reintroduce it.  I like the educational aspect and it seemed to be so unique that I haven't seen anything else like it since... I am using it as a model for bringing it up to the 21st century and try to reintroduce it as an application to learn about threading in education settings... I would like to use as much assembly based programming as possible.  I could probably do it a lot quicker in Visual C++ or QT, but it's a practical exercise for myself as well as others I hope.




Title: Re: Using _beginthreadex
Post by: jj2007 on January 13, 2019, 04:57:44 AM
Quote from: TBRANSO1 on January 13, 2019, 03:36:25 AMmov thread1, rv(CreateThread,NULL,NULL,ADDR test_thread,[esi],NULL,ADDR var)

I assume this means, "return value"?...

Don't take this the wrong way, please... but when I see this... it just reminds of OOP, how many clock cycles is this macro vs. just the standard mov thread1, eax?

Exactly the same cycles count. Try doing the same once "by hand", then with the rv() macro, then insert an int 3 before the two versions and launch a debugger like Olly. You will realise that the same identical code is being produced.

The same is true for .Repeat ... .Until and friends (.While ... .Endw, .if ... .else ... .endif, Switch ... Case ... Endsw). These macros produce the same efficient code that you would generate "by hand", except that the source is much more readable.

Re LOCAL: For more complex subs, it is more efficient under the hood than messing around with [esp+x]. Remember that locals may need initialisation (see also http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm - you can ignore the green stuff; the Iczelion bit may interest you).
Title: Re: Using _beginthreadex
Post by: hutch-- on January 13, 2019, 05:27:03 AM
I don't undertake to convince anyone but try and help them negotiate the complexities of writing assembler for Windows in both 32 and 64 bit. To help you comprehend the difference, have a look at 2 examples in the "exampl07" directory.

"slickhuh.asm" and "masm1k.asm". The first is unreadable to all but old timers who could write this stuff, the second is clear, clean code and they both build at 1024 bytes.

The pre-processor in MASM is a cantankerous old pig but its also very powerful and as you write pre-processor code with MASM, when you run a macro, you are running assembler. Give yourself a few years of practice and you can write garbage like "slickhuh" but if you want to write reliable production code for applications, you will distinguish between the high level code like APIs and C runtime code versus pure mnemonic code for algorithms where it actually matters.

Put you time into what matters, high level code is like shovelling chyte, the real deal with assembler code in algorithms as you can get real performance gains if you write it properly.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 05:37:27 AM
Quote from: jj2007 on January 13, 2019, 04:57:44 AM
(see also http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm - you can ignore the green stuff; the Iczelion bit may interest you).

I don't see a masm32/RichMasm folder?  is that from the MasmBasic package?... where would I install the help files without the RichMasm folder?

To Hutch:

What is the accepted way to start a program?

Let's see if I can explain:

I see this:

.CODE

start:

call main

main PROC ....
    ; code here
main ENDP

end start

OR

.CODE

start:

main PROC ....
    ; code here
main ENDP

end start

Either way, since I have tried both, either works, but what is most recommended by industry veterans? or is there really any difference between the 2?

OT: I am amazed at something that we all take for granted like using a simple printf function is actually a complicated song and dance routine.
Title: Re: Using _beginthreadex
Post by: TimoVJL on January 13, 2019, 06:19:31 AM
Quote from: TBRANSO1 on January 13, 2019, 05:37:27 AM
OT: I am amazed at something that we all take for granted like using a simple printf function is actually a complicated song and dance routine.
:icon_confused:; ml.exe -coff hello.asm -link -subsystem:console
.386
.model flat
exit PROTO C :DWORD
printf PROTO C :PTR,:VARARG
INCLUDELIB msvcrt
.data
msg db "Hello ASM",10,0
.code
mainCRTStartup PROC C
invoke printf, ADDR msg
invoke exit,0
mainCRTStartup ENDP
END
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 06:24:18 AM
Quote from: TimoVJL on January 13, 2019, 06:19:31 AM
Quote from: TBRANSO1 on January 13, 2019, 05:37:27 AM
OT: I am amazed at something that we all take for granted like using a simple printf function is actually a complicated song and dance routine.
:icon_confused:; ml.exe -coff hello.asm -link -subsystem:console
.386
.model flat
exit PROTO C :DWORD
printf PROTO C :PTR,:VARARG
INCLUDELIB msvcrt
.data
msg db "Hello ASM",13,0
.code
mainCRTStartup PROC C
invoke printf, ADDR msg
invoke exit,0
mainCRTStartup ENDP
END


Thanks, but what I meant was if we wanted to do it ourselves every single time... it's awesome to be able to put any type of data (void, int, long, pointer, char, array... [list goes on]), and out the other side puts data on screen with just a simple printf... when if we were to have to do that ourselves, it requires lots of orchestration of converting bytes to ascii values, error checking, tokenizing and more.
[/list]

This is what's great about this forum... as opposed to others... you all are very patient for newbies... just like the best professors.  :icon_mrgreen:

It looks like I have a lot of reading to do... I just found the help files... as much as I want to code so badly... I guess that I have more reading to do  :P
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 08:13:17 AM
Hutch,

I just discovered the fn macro... and read the code... pretty clever, I didn't know there were enumerables in assembly... concatenating the invoke.  Is there a reason or recommendation for this over invoke?

Are there map or transpose macros in assembly too?

I guess on a broader scale, does anyone have links to production level applications that have widespread adoption that were written with masm x86 or x64 assembly?

I'd like to see the possibilities
Title: Re: Using _beginthreadex
Post by: hutch-- on January 13, 2019, 10:39:53 AM
The "fn" macro is there to add convenience and thus clarity for high level code like Windows API functions. The 32 bit version of MASM does not come with a default runtime library as a bare C compiler does but it does come with an ancient bad mannered pre-processor that is also very powerful which means you can configure MASM to be a lot easier and cleaner to produce code with. Its normal for someone with a C/C++ background to think of the C runtime library as the base line but you will find that the wide range of system API functions are better used directly rather than being filtered through the C runtime library.

With an assembler you draw the distinctions between high and low level code differently, API code is high level code and while its necessary for UI and OS access, the real action with an assembler is algorithm design where you have access to complete instruction sets and the architectural freedom to design algorithms for optimal performance.

If you bother to have a good look through the macro file, you will see that it is nearly all assembler apart from external function calls, if the architecture was not pure MASM you would not touch it with a barge pole.

As far as production level code, you would rarely find it published but to put this into perspective, the MASM32 SDK was downloaded in the millions before the year 2000 and many of our members over time have come from the professional end of the market. It depends what you are after as MASM can produce object modules for libraries, truly wicked DLLs as well as executable code but with enough practice EXE files are no big deal and they are better to design assembler algorithms with, even if you prefer to use the algos with a compiler.
Title: Re: Using _beginthreadex
Post by: jj2007 on January 13, 2019, 12:56:53 PM
Quote from: TBRANSO1 on January 13, 2019, 05:37:27 AMI don't see a masm32/RichMasm folder?  is that from the MasmBasic package?... where would I install the help files without the RichMasm folder?
Yes, RM is part of MB. You can find the old Win32 help file at this Modula page (https://www.modula2.org/win32tutor/references.php). How you use it is up to you - with RichMasm  (http://masm32.com/board/index.php?topic=5314.0)it is tied to the F1 key and the current selection, but every editor is slightly different. Remember this help file is 20 years old. I use it all the time because no other help system is that fast and convenient, but in some (rare) cases you won't find the API. Still, it's impressing that the core of Windows has not changed for 20 years ;-)

Quote from: TBRANSO1 on January 13, 2019, 08:13:17 AMI'd like to see the possibilities
Have a look (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1019)  :bgrin:
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 13, 2019, 03:27:41 PM
Quote from: jj2007 on January 13, 2019, 12:56:53 PM
Have a look (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1019)  :bgrin:

Oh, dear Gawd, that looks like compiler code.

Ok, I tasked it upon myself to make a macro map, aka Ruby like where the input array gets overwritten by the new array with the desired block.  I followed some examples, but it seems that I am missing an ingredient somewhere.  The code is not great, but gritty.

I s'pose that you guys can see what I am trying to do:


include \masm32\include\masm32rt.inc

ExitProcess PROTO dwExitCode:DWORD
GetTickCount PROTO

map MACRO operator, operand, array, length
    .IF operator EQ "mul"
        lea edi, array
        xor eax, eax
        mov eax, operand
        xor esi, esi
        mov ecx, length
    L1:
        mov edx, DWORD PTR [ebx]
        mul edx
        mov [edi+esi], eax
        add esi, TYPE DWORD
        loop L1
        mov [array], DWORD PTR ebx
        lea eax, array
    .ENDIF
    EXITM <eax>
ENDM

public start

.CONST
    UPPER_NUM     EQU 1000

.DATA
    num_array DWORD 10 dup(0)
    array_len EQU LENGTHOF num_array
    mnemonic BYTE "mul", 0

.DATA?
    operator DWORD ?

.CODE
start:

main PROC
    mov ebx, num_array
    mov ecx, array_len

  array_loop:
    call randInt
    mov esi, eax
    mov DWORD PTR [ebx], esi
    add ebx, 4
    loop array_loop
    mov operator, 5
    mov num_array, map(offset mnemonic, operator, offset num_array, array_len)
    push 0
    call ExitProcess
main ENDP

randInt PROC
    push ebp
    mov ebp, esp
    push ebx ; save array
    push ecx ; save count
    xor edx, edx
    mov edx, UPPER_NUM
    mov esi, edx
    xor ecx, ecx
    mov eax, ecx
    cmp eax, edx
    jl in_order
    xchg eax, edx
    mov ecx, eax

in_order:
    call GetTickCount
    and eax, 00FFh
    shr eax, 2
    mov ebx, esi
    add ebx, 1
    sub ebx, ecx
    cdq
    idiv ebx
    add esi, ecx
    mov eax, edx
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret
randInt ENDP

end start


So, it throws off this:


Microsoft (R) Macro Assembler Version 14.16.27026.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Assembling: map.asm

***********
ASCII build
***********

map.asm(53) : error A2001:immediate operand not allowed
map(13): Macro Called From
  map.asm(53): Main Line Code
map.asm(53) : error A2094:operand must be relocatable
map(1): Macro Called From
  map.asm(52): Main Line Code


I wish that I understood why you can do the mov thread1, rv(CreateThread,NULL,NULL,ADDR test_thread,[esi],NULL,ADDR var), but I can't reproduce the effect?

I swear this is it, after this I am going to chew on this thread for a while, secretly lurk around, and read the heck out of the help files and more... (as I grow a long hermit beard).

mucho abliged
Title: Re: Using _beginthreadex
Post by: jj2007 on January 13, 2019, 07:42:29 PM
Lurking around is not allowed here :P

Chewing is fine - go ahead:

main PROC
    mov ebx, offset num_array   ; yep, you want the address, not the value
    mov ecx, array_len

  array_loop:
    print "*"
    call randInt
    mov esi, eax
    print str$(esi), 13, 10
    mov DWORD PTR [ebx], esi
    add ebx, 4
    loop array_loop
    mov operator, 5
    print "go in"
    ; mov num_array, map(offset mnemonic, operator, offset num_array, array_len)
    print "- bye"
    push 0
    call ExitProcess
main ENDP


You must either insert a print "hi" every now and then, or use a debugger. The map macro will be the next step, first you must solve the mystery of that crash ("go in" is never reached"). Have fun, we are waiting :P

P.S., always useful for checking if your assumptions are right:
    mov ecx, array_len
    print str$(ecx), " is the len", 13, 10
    mov ecx, array_len ; reload ecx, it was trashed by the print


If you don't see what's happening, search my page (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm) for "most frequent noob error" ;-)
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 14, 2019, 09:01:16 AM
It was rough and crude, and since I couldn't get it to assemble, I couldn't begin to debug it.

Here it is:

A few things that I don't like:
1. the randInt... in Nasm it seems I can use the rdtsc directive, but it flags an error in MASM: error A2085:instruction or register not accepted in current CPU mode, so it works well in Nasm on linux, but using GetTickCount, no so well.
2. I would like to implement it as a MACRO, like the rv example, where I have Map(parameters).
3. It appears that in Nasm I can input a mnemonic as a parameter into a macro, can anything be done like that in Masm?  for example... Map(mnemonic, map_operand, array, length)?


include /masm32/include/masm32rt.inc

map MACRO arg1, arg2, arg3, arg4:VARARG
FOR var, <arg4>
.if var == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L1:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg3
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif var == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L2:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
add eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif var == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L3:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
sub eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.else
jmp _DONE
.endif
ENDM
_DONE:
;mov array, edi
;mov eax, array
    EXITM <eax>                           ;; EAX as the return value
ENDM

ExitProcess PROTO dwExitCode:DWORD
GetTickCount PROTO
Map PROTO operators:BYTE, operand:DWORD, array:PTR, ary_size:DWORD

public start

.CONST
    UPPER_NUM     EQU 10000

.DATA
    num_array DWORD 4 dup(0)
    array_len EQU LENGTHOF num_array
    mnemonic DB "m", 0
    whatever DB "msad", 0

.DATA?
    op DWORD ?

.CODE
start:
    call main

main PROC
    mov ecx, array_len
    mov esi, 0
  array_loop:
    push ecx
    push esi
    call randInt
    mov edi, eax
    print str$(edi), 13, 10
    pop esi
    mov DWORD PTR [num_array+esi], edi
    add esi, 4
    pop ecx
    loop array_loop
    mov op, 0FFh
    mov num_array, rv(Map, mnemonic, op, offset num_array, array_len)
    mov ecx, array_len
    mov edi, 0
display_loop:
    push ecx
    print str$([num_array+edi]), 13, 10
    add edi, 4
    pop ecx
    loop display_loop
    push 0
    call ExitProcess
main ENDP

randInt PROC
    push ebp
    mov ebp, esp
    xor edx, edx
    mov edx, UPPER_NUM
    mov esi, edx
    xor ecx, ecx
    mov eax, ecx
    cmp eax, edx
    jl in_order
    xchg eax, edx
    mov ecx, eax

in_order:
    call GetTickCount
    and eax, 0FFFh
    shr eax, 1
    mov ebx, esi
    add ebx, 1
    sub ebx, ecx
    cdq
    xor edx, edx
    idiv ebx
    add esi, ecx
    mov eax, edx
    mov esp, ebp
    pop ebp
    ret
randInt ENDP

Map PROC operator:BYTE, operand:DWORD, array:PTR, ary_size:DWORD
mov ebp, esp
sub esp, 10h
.if operator == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
L1:
mov edi, array
mov edx, DWORD PTR [edi+esi]
mov eax, operand
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif [operator] == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
L2:
mov edi, array
mov edx, DWORD PTR [edi+esi]
mov eax, operand
add eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif [operator] == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
L3:
mov edi, array
mov edx, DWORD PTR [edi+esi]
mov eax, operand
sub eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.else
jmp _DONE
.endif
_DONE:
;mov array, edi
;mov eax, array
add esp, 10h
mov esp, ebp
ret
Map ENDP
end start


EDIT/UPDATE:

Ok, I went through:
1. I have a better number generator using the rdtsc (as DedDave below notes), need to use the .686 directive
2. still outstanding issue
3. still outstanding issue


include /masm32/include/masm32rt.inc

ExitProcess PROTO :DWORD
GetTickCount PROTO
rand PROTO C :PTR
Map PROTO :BYTE, :DWORD, :PTR, :DWORD

map MACRO arg1, arg2, arg3, arg4:VARARG
FOR var, <arg4>
.if var == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L1:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg3
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif var == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L2:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
add eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif var == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L3:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
sub eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.else
jmp _DONE
.endif
ENDM
_DONE:
;mov array, edi
;mov eax, array
    EXITM <eax>                           ;; EAX as the return value
ENDM

public start

.CONST
    UPPER_NUM EQU 10000
    STDOUT EQU -11

.DATA
    num_array DWORD 5 dup(0)
    array_len EQU LENGTHOF num_array
    mnemonic DB "m", 0
    whatever DB "msad", 0
    message1 DB "Pre-mapped array: ", 13, 10
message2 DB "Post-mapped array: ", 13, 10
    msg_len EQU LENGTHOF message1
msg_len2 EQU LENGTHOF message2
    newline DB 13, 10

.DATA?
    handle DWORD ?
    bytesWritten DWORD ?
    op DWORD ?

.CODE
start:
    call main

main PROC
    mov handle, rv(GetStdHandle, STDOUT)
    INVOKE WriteConsoleA, handle, addr message1, msg_len, addr bytesWritten, 0
    mov ecx, array_len
    mov esi, 0
  array_loop:
    push ecx
    push esi
    call randInt
    mov edi, eax
    print str$(edi), 32
    pop esi
    mov DWORD PTR [num_array+esi], edi
    add esi, 4
    pop ecx
    loop array_loop
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr newline, 2, addr bytesWritten, 0
    mov op, 2d
    mov num_array, rv(Map, mnemonic, op, offset num_array, array_len)
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr message2, msg_len2, addr bytesWritten, 0
    mov ecx, array_len
    mov edi, 0
display_loop:
    push ecx
    print str$([num_array+edi]), 32
    add edi, 4
    pop ecx
    loop display_loop
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr newline, 2, addr bytesWritten, 0
    push 0
    call ExitProcess
main ENDP

randInt PROC
    push ebp
    mov ebp, esp
    xor edx, edx
    mov edx, UPPER_NUM
    mov esi, edx
    xor ecx, ecx
    mov eax, ecx
    cmp eax, edx
    jl in_order
    xchg eax, edx
    mov ecx, eax

in_order:
    rdtsc
    shr eax, 2
    mov ebx, esi
    add ebx, 1
    sub ebx, ecx
    cdq
    xor edx, edx
    idiv ebx
    add esi, ecx
    mov eax, edx
_EXIT:
    mov esp, ebp
    pop ebp
    ret
randInt ENDP

Map PROC operator:BYTE, operand:DWORD, array:PTR, ary_size:DWORD
mov ebp, esp
sub esp, 10h
.if operator == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L1:
mov edx, DWORD PTR [edi+esi]
mov eax, operand
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif [operator] == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
L2:
mov edi, array
mov edx, DWORD PTR [edi+esi]
mov eax, operand
add eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif [operator] == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L3:
mov edx, DWORD PTR [edi+esi]
mov eax, operand
sub edx, eax
xchg edx, eax
mov DWORD PTR [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.else
jmp _DONE
.endif
_DONE:
mov eax, DWORD PTR [edi]
add esp, 10h
mov esp, ebp
  ret
Map ENDP
end start
Title: Re: Using _beginthreadex
Post by: dedndave on January 14, 2019, 11:53:21 AM
if you use ".586" or ".686", you should be able to use RDTSC

.... or, you could hardcode it as
  DW 310Fh
or
  DB 0Fh,31h
Title: Re: Using _beginthreadex
Post by: jj2007 on January 14, 2019, 12:38:08 PM
Here is a skeleton for the macro. Usage: mov eax, map("m", op, offset num_array, array_len)

map MACRO arg1, arg2, arg3, arg4
Local tmp$
  ifidn <arg1>, <"m">
; echos appear in the output window
echo Yeah, it's the "m"
mov esi, arg2
mov edi, arg3
mov ecx, arg4
; ... more code ...
  else
tmp$ CATSTR <Your first arg is >, <arg1>, < - whatever that means>
% echo tmp$
  endif
  exitm <eax>
ENDM
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 14, 2019, 04:13:32 PM
Quote from: dedndave on January 14, 2019, 11:53:21 AM
if you use ".586" or ".686", you should be able to use RDTSC

.... or, you could hardcode it as
  DW 310Fh
or
  DB 0Fh,31h

Great!!!

Since I am using the MASM32 include of masm32rt.inc, and it was coded with a .486... I changed it to .686, and I'll be hornswaggled!

Bless ya, my snail.  :lol:

I now have my original and better random number generator back.  :t

I EDITED the above post with the 2nd code block to reflect updates.
Title: Re: Using _beginthreadex
Post by: hutch-- on January 14, 2019, 04:18:57 PM
Go the whole hog.

.686p
.mmx
.xmm

These size restrictions go back to the days when you could not run early Pentium code on an 8086. Turn everything on and you have escaped the old stuff.
Title: Re: Using _beginthreadex
Post by: Raistlin on January 14, 2019, 04:41:38 PM
QuoteTurn everything on and you have escaped the old stuff.
Yes, as initially suggested. Please add CPU detection to your code for production releases
to ensure run-time compatibility - if you use strange and wonderful instructions. On another
topic : macros. As pointed out by jj2007 and hutch the MASM32 library and macro calls typically
do not add significant cycles to your code [after-all the code is refactored to be extremely speedy
over years of use, by many many forum members and tireless hutch]
MASM = MacroASseMbler [it's in the name  :icon_eek:] macro's are just short hand notation for
code-lets. Thus produce the same processing cycles as the long-hand versions. They are convenient
and produce more readable/maintainable/consistent code.  But it's always a good idea to research
what exactly they do under the hood as to avoid register thrashing or unpredictable results.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 14, 2019, 05:08:56 PM
Okey dokey,

I am pretty much done with this assignment.

I am still working on the MACRO part.

This finally turned out decent.

TO: I was unsure about that rv macro, but that thing is cool, when I did the mov handle, rv(GetStdHandle, STDOUT), in there.

I have learned to embrace INVOKE for speed and brevity.

It took me a long time to figure out why the first index[0] was coming back with what I thought was garbage, but it was the pointer to the array... got that solved.

Thar she blows


.XCREF
.NoList
INCLUDE    \Masm32\Include\Masm32rt.inc
.686p
.MMX
.XMM
.List

HANDLE typedef DWORD

ExitProcess PROTO :DWORD
Map PROTO :BYTE, :DWORD, :PTR, :DWORD

map MACRO arg1, arg2, arg3, arg4:VARARG
FOR var, <arg4>
.if var == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L1:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg3
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif var == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L2:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
add eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif var == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg3
L3:
mov edi, arg2
mov edx, DWORD PTR [edi+esi]
mov eax, arg1
sub eax, edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.else
jmp _DONE
.endif
ENDM
_DONE:
;mov array, edi
;mov eax, array
    EXITM <eax>                           ;; EAX as the return value
ENDM

public start

.CONST
    UPPER_NUM EQU 10000
    STDOUT EQU -11

.DATA
    num_array DWORD 5 dup(0)
    array_len EQU LENGTHOF num_array
    mnemonic DB "m", 0
    whatever DB "msad", 0
    message1 DB "Pre-mapped array: ", 13, 10
    message2 DB "Post-mapped array: ", 13, 10
    msg_len EQU LENGTHOF message1
    msg_len2 EQU LENGTHOF message2
    newline DB 13, 10

.DATA?
    handle                HANDLE ?
    bytesWritten DWORD ?
    op DWORD ?

.CODE
start:
    call main

main PROC
    mov handle, rv(GetStdHandle, STDOUT)
    INVOKE WriteConsoleA, handle, addr message1, msg_len, addr bytesWritten, 0
    mov ecx, array_len
    mov esi, 0
array_loop:
    push ecx
    push esi
    call randInt
    mov edi, eax
    print str$(edi), 32
    pop esi
    mov DWORD PTR [num_array+esi], edi
    add esi, 4
    pop ecx
    loop array_loop
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr newline, 2, addr bytesWritten, 0
    mov op, 0Ah
    mov num_array, rv(Map, mnemonic, op, offset num_array, array_len)
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr message2, msg_len2, addr bytesWritten, 0
    mov ecx, array_len
    mov edi, 0
display_loop:
    push ecx
    print str$([num_array+edi]), 32
    add edi, 4
    pop ecx
    loop display_loop
    mov bytesWritten, 0
    INVOKE WriteConsoleA, handle, addr newline, 2, addr bytesWritten, 0
    push 0
    call ExitProcess
main ENDP

randInt PROC
    push ebp
    mov ebp, esp
    xor edx, edx
    mov edx, UPPER_NUM
    mov esi, edx
    xor ecx, ecx
    mov eax, ecx
    cmp eax, edx
    jl in_order
    xchg eax, edx
    mov ecx, eax

in_order:
    rdtsc
    shr eax, 2
    mov ebx, esi
    add ebx, 1
    sub ebx, ecx
    cdq
    xor edx, edx
    idiv ebx
    add esi, ecx
    mov eax, edx
_EXIT:
    mov esp, ebp
    pop ebp
    ret
randInt ENDP

Map PROC operator:BYTE, operand:DWORD, array:PTR, ary_size:DWORD
mov ebp, esp
sub esp, 10h
.if operator == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L1:
mov eax, operand
mul DWORD PTR [edi+esi]
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif operator == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L2:
mov eax, operand
add eax, DWORD PTR [edi+esi]
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif operator == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L3:
mov edx, DWORD PTR [edi+esi]
mov eax, operand
sub edx, eax
xchg edx, eax
mov DWORD PTR [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.elseif operator == "d"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, ary_size
mov edi, array
L4:
mov eax, DWORD PTR [edi+esi]
xor edx, edx
cdq
idiv operand
mov [edi+esi], eax
add esi, TYPE DWORD
loop L4
jmp _DONE
.endif
_DONE:
mov eax, DWORD PTR [edi]
add esp, 10h
mov esp, ebp
  ret
Map ENDP
end start


Well, all in all, this was a tough little assignment.  I learned a great deal.

I am going to work on making it prettier.

Thanks All

OUTPUT with multiply by 10:
Pre-mapped array:
2857 1164 5858 9389 227
Post-mapped array:
28570 11640 58580 93890 2270

OUTPUT with divide by 10:
Pre-mapped array:
2975 7228 644 9804 1872
Post-mapped array:
29 72 6 98 18

subtract and add work also
Title: Re: Using _beginthreadex
Post by: jj2007 on January 14, 2019, 06:41:24 PM
It works fine but your map macro is not being used and not operational. See my reply#32 above, you may have missed it.
Title: Re: Using _beginthreadex
Post by: dedndave on January 14, 2019, 08:01:20 PM
Quote from: TBRANSO1 on January 14, 2019, 04:13:32 PM
Since I am using the MASM32 include of masm32rt.inc, and it was coded with a .486... I changed it to .686....

it's not a good idea to alter the library file that way
now, when you post code that has "INCLUDE \masm32\include\masm32rt.inc",
other programmers will not be compatible

the CPU selection directive may be reassigned without a "duplicate" error
instead, do it like this...

INCLUDE \masm32\include\masm32rt.inc
.586


i start writing programs by using a template program
it starts like this

        .XCREF
        .NoList
        INCLUDE    \Masm32\Include\Masm32rt.inc
        .686p
        .MMX
        .XMM
        .List


also, RDTSC is not the only instruction like this
the same applies to using the CPUID instruction
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 15, 2019, 01:09:35 AM
Quote from: jj2007 on January 14, 2019, 06:41:24 PM
It works fine but your map macro is not being used and not operational. See my reply#32 above, you may have missed it.

Yes, this MACRO is a work in progress, I will work on this part now.

Quote from: dedndave on January 14, 2019, 06:41:24 PM
i start writing programs by using a template program
it starts like this

Code: [Select]

        .XCREF
        .NoList
        INCLUDE    \Masm32\Include\Masm32rt.inc
        .686p
        .MMX
        .XMM
        .List


also, RDTSC is not the only instruction like this
the same applies to using the CPUID instruction

I made those updates above.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 18, 2019, 04:49:59 AM
An update to the update:

I finished the map MACRO: works just like the map PROC:

now I can do either:
mov array, rv(map, <parameters>)

OR

mov array, map(<parameters>)

I like the latter due to it looking more like Ruby or Python...  :eusa_naughty:


map MACRO var:REQ, arg2, arg3, arg4
.if var == "m"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg4 ; len
mov edi, arg2 ; array
L1:
mov edx, DWORD PTR [edi+esi]
mov eax, arg3 ; operator
mul edx
mov [edi+esi], eax
add esi, TYPE DWORD
loop L1
jmp _DONE
.elseif var == "a"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg4
mov edi, arg2
L2:
mov eax, arg3
add eax, DWORD PTR [edi+esi]
mov [edi+esi], eax
add esi, TYPE DWORD
loop L2
jmp _DONE
.elseif var == "s"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg4
mov edi, arg2
L3:
mov edx, DWORD PTR [edi+esi]
mov eax, arg3
sub edx, eax
xchg edx, eax
mov DWORD PTR [edi+esi], eax
add esi, TYPE DWORD
loop L3
jmp _DONE
.elseif var == "d"
xor eax, eax
xor esi, esi
xor ecx, ecx
mov ecx, arg4
mov edi, arg2
L4:
mov eax, DWORD PTR [edi+esi]
xor edx, edx
cdq
idiv arg3 ; 3dcccccd
mov [edi+esi], eax
add esi, TYPE DWORD
loop L4
jmp _DONE
.endif
_DONE:
mov eax, DWORD PTR [edi]
    EXITM <eax>                          ;; EAX as the return value
ENDM


I have one more question:

Can we pass an actual mnemonic as a parameter in a macro?

It appears that it is possible in NASM.

i.e.
%macro map 4 (args count)
    // code here
   %1 eax, 4  // where mul, div, add, sub would replace the %1
   // rest of code
%endmacro

so a macro definition call would be like:

map(mul, <parameters>) in MASM

???
Title: Re: Using _beginthreadex
Post by: hutch-- on January 19, 2019, 03:11:53 PM
This should answer your question.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    addmacro MACRO mnemonic, register, value
      mnemonic register, value
    ENDM

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

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

main proc

    xor eax, eax                ; mnemonic

    addmacro add, eax, 1234     ; macro

    print str$(eax),13,10       ; display result

    ret

main endp

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

end start


Here is a simplified version.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    addmacro MACRO register, value
      add register, value
    ENDM

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

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

main proc

    xor eax, eax                ; mnemonic
    addmacro eax, 1234          ; macro
    print str$(eax),13,10       ; display result

    ret

main endp

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

end start

Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 20, 2019, 03:13:42 AM
Quote from: hutch-- on January 19, 2019, 03:11:53 PM
This should answer your question.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    addmacro MACRO mnemonic, register, value
      mnemonic register, value
    ENDM

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

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

main proc

    xor eax, eax                ; mnemonic

    addmacro add, eax, 1234     ; macro

    print str$(eax),13,10       ; display result

    ret

main endp

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

end start


Yep  :t

I have to refactor the macro now, as I try to differentiate between the different mnemonics instead of char literals.

@Hutch

I've been poking around the Masm32 includes and macros, reading help and MSDN docs... then using Select-String codelet in Powershell to find if the function or macro is available in MASM32.  I think that it has a 99% positive hit count, anything that didn't hit, I am adding.  Usually it's just small macros or defines... for example, I added the UNREFERENCED_PARAMETER macro, MSDN uses it a lot, not really neccessary, but it's an elegant way to stop the assembly warnings of unused parameter variable.

UNREFERENCED_PARAMETER MACRO param:REQ
   mov param, 0
ENDM


UNREFERENCED_PARAMETER(lpParam)


Anyways
Thanks
Title: Re: Using _beginthreadex
Post by: hutch-- on January 20, 2019, 07:52:26 AM
I am not sure how you are using this macro but you can use :VARARG in macros if a variable argument count is required.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 20, 2019, 08:04:44 AM
Quote from: hutch-- on January 20, 2019, 07:52:26 AM
I am not sure how you are using this macro but you can use :VARARG in macros if a variable argument count is required.

I find the Visual C++ unreferenced_parameter macro in a lot of MSDN examples.

BTW, I am curious to what the REQ means?  I see it a lot, and it seems to be used as a delimiter for the ( ) brackets?  But it's not in the MASM >= 14.0 docs, and I haven't found any full explanation for it yet.

Title: Re: Using _beginthreadex
Post by: hutch-- on January 20, 2019, 08:11:47 AM
It just means that the argument is required and if the arg is omitted you get an error.
Title: Re: Using _beginthreadex
Post by: jj2007 on January 20, 2019, 08:28:19 AM
REQ forces an error if the argument is not present. Here is a test piece showing some macro tricks.

include \masm32\include\masm32rt.inc

hi macro arg ; now add :req and see what it does
  print "hi ", arg
endm

hiwithdef macro arg:=<"folks"> ; you can use it without passing an argument but...
  print "hi ", arg
endm

test1 macro arg ; assemble time: generates code only for the relevant branch
  ifidn <arg>, <eax>
print "you passed eax", 13, 10
  else
print "you passed &arg", 13, 10
  endif
endm

test2 macro arg ; runtime: generates code for both branches
  .if arg==eax
print "you passed eax", 13, 10
  .else
print "you passed &arg", 13, 10
  .endif
endm

.code
start:
  hi "TBRANSO1"
  print chr$(13, 10)
  hi
  print chr$(13, 10)

  hiwithdef "TBRANSO1"
  print chr$(13, 10)
  hiwithdef
  print chr$(13, 10)


  test1 eax
  test1 something else

  test2 eax
  test2 ecx

  inkey "ok?"
  exit

end start
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 20, 2019, 08:49:49 AM
Quote from: jj2007 on January 20, 2019, 08:28:19 AM
REQ forces an error if the argument is not present. Here is a test piece showing some macro tricks.

Cool examples.

I am continually amazed at what MASM can do for lowly assembly that resembles stuff that I only thought was available in OOP languages.  I've placed C in Python or Ruby and it's a bitch and time consuming to do it, working with the original C-code bases and the FFI library. I may be prematurely saying this since I am new to this, but when folks say that it's not possible to do things in assembly except some number crunching here and there, obviously aren't aware of the capabilities you guys have designed along with the assemblers.  I've always said the smartest programmers aren't the guys doing Front End JS stuff, but the compiler, assembler writers.

I have another example that I made into a MACRO, and wondering what you thought of my implementation:
from MSDN there is


MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))


I realize that this make be absolutely unnecessary in assembly but I made this:
take the lower word of the parameter, cast it to double word, then cast it to LPSTR


MAKEINTRESOURCE MACRO param:REQ
LOCAL lpcStr
.data
lpcStr LPCSTR ?
.code
and param, 000FFh
push param
pop lpcStr
EXITM <lpcStr>
ENDM


What are your thoughts?

Title: Re: Using _beginthreadex
Post by: hutch-- on January 20, 2019, 09:37:18 AM
 :biggrin:

> What are your thoughts?

Looks like a C compiler.  :P
Title: Re: Using _beginthreadex
Post by: jj2007 on January 20, 2019, 11:08:44 AM
> and param, 000FFh

Are you sure it shouldn't be 0FFFFh?
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 20, 2019, 11:57:14 AM
Quote from: jj2007 on January 20, 2019, 11:08:44 AM
> and param, 000FFh

Are you sure it shouldn't be 0FFFFh?

You're right, I caught that afterward.  I had changed it in my base code already.

I had to change it a bit, since the assembler complained.


MAKEINTRESOURCE MACRO param:REQ
LOCAL lpStr
.data?
lpStr LPSTR ?
.code
xor eax, eax
mov eax, param
and eax, 0FFFFh
mov lpStr, eax
EXITM <lpStr>
ENDM

Title: Re: Using _beginthreadex
Post by: TimoVJL on January 20, 2019, 07:24:21 PM
In C MAKEINTRESOURCE macros don't generate code, only casts WORD value to LPSTR / LPWSTR.#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))

7: LPSTR lpStr = MAKEINTRESOURCEA(8001);
  [00000006] C745FC411F0000         mov               dword ptr [ebp-4],1F41h
8: LPWSTR lpWStr = MAKEINTRESOURCEW(8001);
  [0000000D] C745F8411F0000         mov               dword ptr [ebp-8],1F41h

UNREFERENCED_PARAMETER() macro works only with optimizing compilers, as they can remove an unused parameter.
Title: Re: Using _beginthreadex
Post by: TBRANSO1 on January 22, 2019, 06:54:17 AM
Thanks Timo

Yes, I know it's most likely unnecessary but it was an exercise and and excuse to practice macros.