News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Using _beginthreadex

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

Previous topic - Next topic

hutch--

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.

TBRANSO1

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?


TimoVJL

In ml.exe INVOKE is a directive / pseudo-instruction, not an actual MACRO
May the source be with you

hutch--

> 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.

TBRANSO1

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.





jj2007

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).

hutch--

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.

TBRANSO1

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.

TimoVJL

#23
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
May the source be with you

TBRANSO1

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

TBRANSO1

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

hutch--

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.

jj2007

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. How you use it is up to you - with RichMasm 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  :bgrin:

TBRANSO1

Quote from: jj2007 on January 13, 2019, 12:56:53 PM
Have a look  :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

jj2007

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 for "most frequent noob error" ;-)