Hi there,
I am back with these macros and this time with the vengeance
this time I can assure you that they work as expected
take a look at this:
xmemcpy PROC USES edi esi dest:DWORD,src:DWORD,count:DWORD
mov eax,dest
.if (src != eax)
.if (count)
.for (esi=src,edi=dest,ecx=count:ecx:al=[esi],[edi]=al,esi++,edi++,ecx--)
.endfor
.endif
.endif
mov eax,dest
ret
xmemcpy ENDP
;--------------------------------------------------
xmemcpy:
004011ee 55 push ebp
004011ef 8bec mov ebp,esp
004011f1 57 push edi
004011f2 56 push esi
004011f3 8b4508 mov eax,dword ptr [ebp+8]
004011f6 39450c cmp dword ptr [ebp+0Ch],eax
004011f9 7424 je xmemcpy+0x31 (0040121f)
004011fb 837d1000 cmp dword ptr [ebp+10h],0
004011ff 741e je xmemcpy+0x31 (0040121f)
00401201 8b750c mov esi,dword ptr [ebp+0Ch]
00401204 8b7d08 mov edi,dword ptr [ebp+8]
00401207 8b4d10 mov ecx,dword ptr [ebp+10h]
0040120a eb0d jmp xmemcpy+0x2b (00401219)
0040120c 8a06 mov al,byte ptr [esi]
0040120e 8807 mov byte ptr [edi],al
00401210 83c601 add esi,1
00401213 83c701 add edi,1
00401216 83e901 sub ecx,1
00401219 23c9 and ecx,ecx
0040121b 7402 je xmemcpy+0x31 (0040121f)
0040121d ebed jmp xmemcpy+0x1e (0040120c)
0040121f 8b4508 mov eax,dword ptr [ebp+8]
00401222 5e pop esi
00401223 5f pop edi
00401224 c9 leave
00401225 c20c00 ret 0Ch
;--------------------------------------------------
;and this would be in x64:
xmemcpy PROC FRAME dest:QWORD,src :QWORD, count:UINT_PTR
.if (rcx != rdx)
.if (r8)
.for (:r8:al=[rdx],[rcx]=al,rcx++,rdx++,r8--)
.endfor
.endif
.endif
mov rax,dest
ret
xmemcpy ENDP
;--------------------------------------------------
xmemcpy PROC FRAME dest:QWORD,src :QWORD, count:UINT_PTR
0000000000493496 mov qword ptr [rsp+8],rcx
000000000049349B push rbp
000000000049349C mov rbp,rsp
000000000049349F cmp rcx,rdx
00000000004934A2 je xmemcpy+25h (4934BBh)
00000000004934A4 and r8,r8
00000000004934A7 je xmemcpy+25h (4934BBh)
00000000004934A9 mov al,byte ptr [rdx]
00000000004934AB mov byte ptr [rcx],al
00000000004934AD add rcx,1
00000000004934B1 add rdx,1
00000000004934B5 sub r8,1
00000000004934B9 jmp xmemcpy+13h (4934A9h)
00000000004934BB mov rax,qword ptr [rbp+10h]
00000000004934BF add rsp,0
00000000004934C3 pop rbp
00000000004934C4 ret
xmemcpy ENDP
;--------------------------------------------------
and this time I don't care if you like it or not because I am happy with it
we're happy if you're happy :biggrin:
speak for yourself!!! ;)
Quote from: habran on June 11, 2012, 09:30:45 AMand this time I don't care if you like it or not because I am happy with it
what kind of response do you expect ?
i might make suggestions, but it seems you aren't interested
I want you to say that it is a masterpiece and that you love it
i am not crazy about the loop structure in the expanded code
but - the macro looks nice and clean :biggrin:
i prefer to write code "old style"
i am not too fond of .if/.else or .while/.endw stuff
but - the members don't like it when i write code that way - lol
so - to make code that i like - and that makes them happy,
i have had to learn how the statements work - in reverse
in other words, i write some code, disassemble it, and learn what code is generated
then - i write code they like - that more or less does what i wanted, to begin with
here is a good example
many members will write a message loop like this
.while TRUE
INVOKE GetMessage,ebp,edi,edi,edi
.break .if !eax
INVOKE TranslateMessage,ebp
INVOKE DispatchMessage,ebp
.endw
it looks nice - but it generates some fugly code
here is how i prefer to write it
jmp short MLoop1
MLoop0: INVOKE Translate Message,ebp
INVOKE DispatchMessage,ebp
MLoop1: INVOKE GetMessage,ebp,edi,edi,edi
inc eax
shr eax,1
jnz MLoop0
it might not be pretty, but it is efficient
well - to appease the others - and get what i want, i found a comprimise :biggrin:
jmp short mEntry
.while !(ZERO?)
INVOKE TranslateMessage,ebp
INVOKE DispatchMessage,ebp
mEntry: INVOKE GetMessage,ebp,edi,edi,edi
inc eax ;exit only if 0 or -1
shr eax,1
.endw
the code is amost what i want
it generates an extra JMP that is not executed - but, oh well
Quote from: dedndave on June 11, 2012, 11:31:41 AMwell - to appease the others - and get what i want, i found a comprimise :biggrin:
jmp short mEntry
.while !(ZERO?)
INVOKE TranslateMessage,ebp
INVOKE DispatchMessage,ebp
mEntry: INVOKE GetMessage,ebp,edi,edi,edi
inc eax ;exit only if 0 or -1
shr eax,1
.endw
the code is amost what i want
it generates an extra JMP that is not executed - but, oh well
OMG :biggrin:
(http://l.yimg.com/us.yimg.com/i/mesg/emoticons7/24.gif)
qWord is great !!!
ok - maybe that's not the best example - lol
but - the idea is - i had to learn how to use the loop structure in reverse
Quote from: habran on June 11, 2012, 10:58:35 AM
I want you to say that it is a masterpiece and that you love it
It
is a masterpiece but it looks like C :(
.for (esi=src,edi=dest,ecx=count:ecx:al=[esi],[edi]=al,esi++,edi++,ecx--)
.endfor
i am a BASIC guy :biggrin:
For_ ebx=0 To eax-1
nop
Next
habran,
I had a look at your macros and you have done some good work there.
i wonder if we can do something like this ?
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
.data?
value dd ?
.data
item dd 0
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL eax_ :DWORD
LOCAL ebx_ :DWORD
LOCAL ecx_ :DWORD
LOCAL edx_ :DWORD
LOCAL cntr :DWORD
mov cntr, 1
mov eax, -10
mov ebx, -11
mov ecx, -12
mov edx, -13
lbl0:
add eax, 1
add ebx, 1
add ecx, 1
add edx, 1
.if eax==1 || ebx == 2 || ecx == 3 || edx == 4
mov eax_, eax
mov ebx_, ebx
mov ecx_, ecx
mov edx_, edx
pushad
print ustr$(eax_),13,10
print ustr$(ebx_),13,10
print ustr$(ecx_),13,10
print ustr$(edx_),13,10
popad
.else
pushad
print "iteration "
print ustr$(cntr),13,10
add cntr, 1
popad
jmp lbl0
.endif
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
JJ, :t It really WAS a typo.
thanks huch
if we put this statement in brackets
.if eax==1 || ebx == 2 || ecx == 3 || edx == 4
.if (eax==1 || ebx == 2 || ecx == 3 || edx == 4)
it will work fine
the rest is a chicken shit
best regards
jj2007
c like forloop can not look like BASIC FOR loop
but thank you for appreciating it
thanks to all of you for looking in to it
:biggrin:
> it will work fine
It works fine without the brackets. :P
Is there any gain to be had anymore from separating small memory copy operations from larger ones and using different methods such as SSE2, FPU, loading and incrementing pointers like in these macro examples or even the older REP MOVSx operands?
If so, you should be able to add conditional statements to only assemble the required method for the size given as a macro parameter shouldn't you?
Interesting, i'm going to look at this myself for curiousity's sake.
HR,
Ghandi
Mea culpa, huch :icon_mrgreen:
I was in hurry and still my mind was in the .for macro
however, it looks much better when in brackets, you have to admit that
So, what is actually that you are concerned with in that example?
dedndave,
>i prefer to write code "old style"
I appreciate your opinion
however, sometimes readability of program is more important then speed
ml64 is out of game because of the "old style"
best regards
Quote from: habran on June 11, 2012, 09:10:47 PM
however, it looks much better when in brackets, you have to admit that
There is room for improvement then:
.if eax
nop
.endif
can become
.if (eax!=NULL);
;{
nop;
;}
.endif
;)
:eusa_clap: jj2007, I like your cinizam
I take it as a great sense of humor
that is a masterpiece jock, I see it first time in my life
you should publish it somewhere
BTW
I used to write in BSIC while using commodore 64 but just to transfer machine code to assembler or opposite
I like C more than BASIC but my favorite is ASM, nothing can compensate for it
For-loops are fine and I've also had the idea to create such a macro, but finial I end up that the resulting syntax doesn't look as nice as in C. Currently I can only see a change if it is directly implemented in the assembler: jWasm.
For your macros, I see the problem of the used none-ASCII-characters (<<,>>), which are only visible, if the editor use the correct code page.
qWord, in this case you can use LT, GT, EQ, LE, NE, GE
>directly implemented in the assembler: jWasm.
I have worked on it and already build the skeleton of it
but I did not have enough time to study the code necessary to translate operators into code
now I am planing to look at it
Quote from: habran on June 11, 2012, 10:28:04 PM
I used to write in BASIC while using commodore 64 but just to transfer machine code to assembler or opposite
I like C more than BASIC but my favorite is ASM, nothing can compensate for it
I see we are on the same wavelength :biggrin:
My "coding career" was always a mix of Basic (i.e. Gfa on the Atari ST - almost a Pascal dialect) and assembler (68k, later x86). So I am probably just too old for C - and anyway, in Olly they look remarkably similar ;-)
Excuse my ignorance: What is the syntax for an extra simple loop as shown below?
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
Init
PrintLine "MasmBasic For...Next loop:"
mov eax, 5
For_ ecx=0 To eax-1
Print Str$("ecx=%i ", ecx)
Next
PrintLine CrLf$, "Now Habran's macro:"
; .FOR (
Inkey "ok"
Exit
end start
Output:
MasmBasic For...Next loop:
ecx=0 ecx=1 ecx=2 ecx=3 ecx=4
Now Habran's macro:
okP.S.: Any news from JWasm? 2.07 pre is fine for most purposes but there are minor quirks to fix...
jj2007
wavelength? you mean we remember things happened deep in the last millennium?
no mobile, no internet, no computer no nothing except having sex 28 hour a day!!! 8)
try this because I did not try it:
Init
PrintLine "MasmBasic For...Next loop:"
.for (eax=5,ecx=0:SDWORD PTR eax GE 0:eax--)
Print Str$("ecx=%i ", ecx)
PrintLine CrLf$, "Now Habran's macro:"
; .FOR (
Inkey "ok"
.endfor
Exit
end start
best regards neighbor
P.S.: "0" (JWasm news)
There is nothing wrong with the analog world, it keeps running when the digital world's batteries go flat. :biggrin:
shouldn't ".endfor" be ".next" :lol:
Quote from: habran on June 12, 2012, 07:18:24 AM
wavelength? you mean we remember things happened deep in the last millennium?
no mobile, no internet, no computer no nothing except having sex 28 hour a day!!! 8)
Hutch, alarm!! We have a Martian on board - earthlings had only 24 hours available for this ;-)
Tried your code but no luck, it throws lots of error messages, see attachment...
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
include FOR_M.inc ; slightly modified
Init
PrintLine "MasmBasic For...Next loop:"
mov eax, 5
For_ ecx=0 To eax-1
Print Str$("ecx=%i ", ecx)
Next
PrintLine CrLf$, "Now Habran's macro:"
.for (eax=5:ecx=0:SDWORD PTR eax GE 0:eax--)
nop
.endfor
Inkey "ok"
Exit
end start
jj2007
sorry it was supposed to be with comma not with column
like this: .for (eax=5,ecx=0:SDWORD PTR eax GE 0:eax--)
and I am not from Mars, I am your neighbor fro former Yugoslavia
but I am already 20 years in South Australia
:biggrin: Yeah hutch: analog sex 8)
Quote from: habran on June 12, 2012, 02:58:14 PM
jj2007
sorry it was supposed to be with comma not with column
like this: .for (eax=5,ecx=0:SDWORD PTR eax GE 0:eax--)
assembles fine now but doesn't do anything ::)
But I got it working in the end:
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
include FOR_M.inc ; slightly modified
Init
PrintLine "MasmBasic For...Next loop:"
mov eax, 5
For_ ecx=0 To eax-1
Print Str$("ecx=%i ", ecx)
Next
PrintLine CrLf$, CrLf$, "Now Habran's macro: "
; mov eax, 4
.for (ecx=0 :SDWORD PTR ecx GE 4 : ecx++)
Print Str$("ecx=%i ", ecx)
.endfor
Inkey CrLf$, "ok"
Exit
end start
Output:MasmBasic For...Next loop:
ecx=0 ecx=1 ecx=2 ecx=3 ecx=4
Now Habran's macro:
ecx=0 ecx=1 ecx=2 ecx=3 ecx=4Two minor issues: mov eax, 4
.for (ecx=0 : SDWORD PTR ecx GE eax : ecx++) ; reg32 instead of immediate: endless loop, eax gets trashed somewhere
.for (ecx=0 : SDWORD PTR ecx GE 4 : ecx++) ; blank before SDWORD: assembly hangs
Quoteand I am not from Mars, I am your neighbor fro former Yugoslavia
but I am already 20 years in South Australia
cosmically speaking, Mars is a close neighbour, too :biggrin:
by the way, if I remember well, even a century back that 24-hour activity was already digital ;)
jj2007
I will look at it tonigh :eusa_clap:
are you sure that we are from the same century? :lol:
Quote from: habran on June 12, 2012, 05:10:39 PM
are you sure that we are from the same century? :lol:
Pretty sure. I am uncertain about the location though - Mars is the only planet with a more than 24 hours day ::)
OK, when I said 28 hours a day I meant more than I needed
that means that we exaggerated in a pleasure
And for your information, Mars is not inhabited!!! (Except Arnold Schwarzenegger :badgrin:)
Not any more, they died of boredom because they did not have internet
If you don't believe me ask other members on this board
best regards
Hi,
QuoteMars is the only planet with a more than 24 hours day
Only if you ignore Mercury, Venus, and optionally Pluto.
Cheers,
Steve N.
i think Maria Shriver is from Pluto :redface:
Quote from: FORTRANS on June 12, 2012, 11:58:52 PM
QuoteMars is the only planet with a more than 24 hours day
Only if you ignore Mercury, Venus, and optionally Pluto.
Steve,
We only considered those which are appropriate for digital activity (hey, did anybody notice we are in the Lab here? we'll soon be banned :bgrin:)
Hi jj,
Well, okay. I'll quiet up after mentioning that Messenger
is now in orbit around Mercury.
Cheers,
Steve N.
thank you jj2007 for finding the bug in the macro :t
it was a proof that I am just a human (and not a Martian) :eusa_boohoo:
the bug was there because I was lazy to type so I copied and pasted the same routine but missed
to change 1 number
it was here:
:Lop3
cspace TEXTEQU @SubStr(<params>,temp,1)
IFIDN cspace,< >
temp = temp+1
space1 = space1+1
goto Lop2 ;bug was here, it supposed to be goto Lop3
ENDIF
I have fixed it an upload it in the beginning of this tread
and now it works like a charm :eusa_clap:
thanks again
Quote from: dedndave on June 11, 2012, 11:31:41 AM
here is how i prefer to write it
jmp short MLoop1
MLoop0: INVOKE Translate Message,ebp
INVOKE DispatchMessage,ebp
MLoop1: INVOKE GetMessage,ebp,edi,edi,edi
inc eax
shr eax,1
jnz MLoop0
it might not be pretty, but it is efficient
Not sure what's not pretty about it. One of the things that worried me when deciding to take on learning asm was loops and switches. To me the quoted snippet was the easiest to comprehend with the least effort of the 3 you provided. I have no issues at all with higher level syntax styles when it adds clarity, ease of use, or comprehensibility. Even paying some mild overhead cost for it is not out of the question in most circumstances. I'm just not personally seeing any of that in the examples given.
I hope if you contribute to my questions in the future you can offer your "old style" perspective as well. It's valuable even when alternative approaches are chosen in a given instance.
i feel better, now
some of the guys in here can't read code unless it is surrounded by ".IF/.ENDIF" :lol:
i hate that stuff, but i want to use it so that the newbies are accustomed to it
Dave, any reason why you use inc and shr instead of cmp?
Quote from: Ryan on July 18, 2012, 01:59:40 AM
Dave, any reason why you use inc and shr instead of cmp?
Loop exits for msg=0 and msg=-1 ;-)
to expand on what Jochen said...
by using INC and SHR, we get to use only 1 branch instruction
INC reg32 is a single-byte instruction
SHR reg32,1 is a two-byte instruction
JNZ by a reverse distance of 128 bytes or less is 2 bytes
inc eax
shr eax,1
jnz MLoop0
5 bytes total
we want to exit the program if the returned value is either 0 or -1
for all other values, we want to loop
Yeah, I guess .break .if !eax
would fail to exit if -1 is returned.
Good guess :biggrin:
Dave's code is one cycle faster and 4 bytes shorter.
The irony is the template that Microsoft uses for new C++ programs doesn't account for -1. I wish they would elaborate on what exactly would cause it to fail. They mention as examples an invalid hWnd or an invalid message pointer. Passing hWnd would overlook WM_QUIT, so that's definitely not recommended in a primary message loop. If someone passes an invalid message pointer, it would certainly be caught on the first run.
optimizing loops with blocking functions is like makeup for dogs :biggrin:
Quote from: dedndave on July 17, 2012, 08:55:11 PM
i feel better, now
some of the guys in here can't read code unless it is surrounded by ".IF/.ENDIF" :lol:
i hate that stuff, but i want to use it so that the newbies are accustomed to it
While trying to learn from reading source basic ".IF/.ENDIF" is not a problem. But when it has a lots of nested ".ELSE/.ELSEIF" I find myself messing with the white space to try and make sense of it. It seems a lot cleaner to me to see jnz. That tells me immediately where it is going and why without tracking back up through the page to see what other conditionals are contained in ".IF/.ELSEIF/.IF/.ELSEIF/.ENDIF/.ELSEIF/.ENDIF". No matter how many times I get it straight I still have to rework the logical blocks through my mind when I go back.
Edit: Somehow double quoted the same quote.
mywan - my point, exactly :t
qword - what blocking function ?
Quoteoptimizing loops with blocking functions is like makeup for dogs
some dogs are just plain ugly - lol
you and Hutch can both write bloatware for MS
my approach has always been:
1) if speed matters, make it fast
2) if speed doesn't matter, make it small
in general, i try to develop habits that are aligned with that approach
so - even if it isn't critical, i am developing the habits that i aim for
Ryan -
QuoteI wish they would elaborate on what exactly would cause it to fail.
a sentiment that has been expressed by more than one other member :biggrin:
as for the errors that MSDN says may be encountered
1) if it has an incorrect hWnd, it should never get into the queue - DOH !
2) invalid message pointer :dazzled:
again - if there is an invalid pointer in a message parm, the message should return an error
at any rate, i have never seen this error occur
and - if it did, there may be better ways to handle it than exiting the program
for example - you could just pull the offending message out of the queue and move on
but - i haven't seen anyone's code do ths :P
i figure exiting the program is safer than dispatching an erroneous message
Quote from: dedndave on July 18, 2012, 04:06:20 AMqword - what blocking function ?
functions that get into a wait-state (GetMessage()).
IMO it is most important to make code readable and understandable. Optimize code is worthless, if no one understand it. Even, if you do not share the code with others, the question is if you will understand the code in some years. This "spaghetti"-style (no .if/.while,...) requires a lot more comments and I don't think these will increase the transparency in any case.
My strategy is: if (Speed critically) then (optimize as much as possible) else (make code readable)
It's not speed, of course - but saving 4 bytes is always nice ;)
Quote from: qWord on July 18, 2012, 04:34:10 AMthe question is if you will understand the code in some years. This "spaghetti"-style (no .if/.while,...) requires a lot more comments
True. It doesn't matter when you have 20 lines of code, but for anything more complex the HLL constructs help a lot - there is a reason why GOTO was banned from modern languages.
I use both versions, actually, and I couldn't really explain why I sometimes prefer a jnz...
But I definitely need colour:
push ebx
push esi
... 20 lines of code
pop esi
... 20 lines of code
pop ebx
Dave, under which circumstances can you produce -1? My windows always exit with zero...
Quote from: qWord on July 18, 2012, 04:34:10 AM
functions that get into a wait-state (GetMessage()).
IMO it is most important to make code readable and understandable. Optimize code is worthless, if no one understand it. Even, if you do not share the code with others, the question is if you will understand the code in some years. This "spaghetti"-style (no .if/.while,...) requires a lot more comments and I don't think these will increase the transparency in any case.
My strategy is: if (Speed critically) then (optimize as much as possible) else (make code readable)
As someone just getting started with asm I'm finding the "no .if/.while,..." structures quicker to comprehend than with them, even after already figuring out the logical blocks of the ".IF/.THEN", and this is other peoples uncommented code. Hence the notion that it will create issues for me to understand my own code after some years seems unlikely. Absolutely love the "invoke" for API calls though. I am absolute not against macros and higher level constructs, but the assumption that these constructs always make code more readable just doesn't compute in some minds.
Coming from a nooby position the most valuable _missing_ feature is context sensitive help files. When I can't remember which jump "jnz", etc., refers to my editor is perfectly capable of going directly to "\MASM32\HELP\help.chm::/jnz.htm" simply by hitting F1 with the cursor over "jnz". Neither are the help files searchable. This for me, in both cases, is a lot worse than any lack of comments. It is, imo, well worth creating a project just to remedy this.
Quote from: mywan on July 18, 2012, 05:24:13 AMthe most valuable _missing_ feature is context sensitive help files. When I can't remember which jump "jnz", etc., refers to my editor is perfectly capable of going directly to "\MASM32\HELP\help.chm::/jnz.htm" simply by hitting F1 with the cursor over "jnz".
Good idea, but Microsoft makes it very difficult. In my own editor, I have chained two help files, so that F1 on WinByTitle opens the MasmBasic help file, while F1 on SetForegroundWindow opens the page in Win32.hlp (because it can't find SetForegroundWindow in the MasmBasic help). The same with *.chm files is a lot more difficult. If the bloody chm does not contain an exact match SetForegroundWindow.htm, you just get a boring "page not found" - and most of the time it's not even searchable. Stupid, very stupid :(
I just created a thread asking about interest in a help file project like this. I have seen it done very effectively with some scripting languages. Initially it needs to be done as <keyword>.htm all in the same folder. From there it can easily be compiled into an effective integratable .chm file. Certain standards should be set on the structure of the .htm files.