News:

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

Main Menu

Contextual help project

Started by mywan, July 18, 2012, 07:31:02 AM

Previous topic - Next topic

mywan

The + key seems to work quiet well also. I'll get around to playing with the MasmBasic library sometime also. Right now I'm just trying to gain some proficiency at dealing with various procedures using the basic instruction set. I still want to have some idea of what these libraries consist of before starting to depend on them too much.

jj2007

Quote from: mywan on August 05, 2012, 04:28:48 AMI still want to have some idea of what these libraries consist of before starting to depend on them too much.

That is not a trivial point, of course. There is kind of a hierarchy there:

- 68 00204000 (the x86 CPU knows what it is, be it on Windows or Linux or a Mac)
- push offset mystring + call StdOut (StdOut is part of the Masm32 library, it uses WriteFile, which is part of the Windows library)
- invoke StdOut, addr mystring (same but it uses the invoke feature of Microsoft Macro Assembler)
- print "Hello World" (the Masm32 library, you will depend on the goodwill of Hutch :P)
- Print "Hello World", CrLf$, "Today is the ", Date$ (the MasmBasic library, depends on the Masm32 installation, on Windows, and on the goodwill of jj)

You need growing levels of confidence to use each of these features ;)
As the author, I have full confidence in MB; but I still designed it in a way that I can use it in parallel to Masm32 code, and with ML (6.15...9.0) and JWasm. Most if not all of the code in \masm32\examples works just fine if you replace the whole .model, .586, include whatever stuff with one single line, include \masm32\MasmBasic\MasmBasic.inc
I see MB as "inline Basic", in analogy to "inline assembler" in C code. Its main advantage is a productivity gain from not having to repeat boring stuff all the time:

   Let esi=FileRead$("MyCrFile.txt")
   push esi
   .Repeat
      lodsb
      .if al==13
         mov byte ptr [esi-1], 10
      .endif
   .Until !al
   pop esi
   FileWrite "MyLfFile.txt", esi

Masm32 can do almost the same in a few steps more, but MB is designed to do the trivial file I/O and string manipulation etc stuff easily and fast, and that's how I mostly use it. My bigger sources are nowadays about 10% MasmBasic and 90% "true" assembler. But those 10%, if expanded to "true" assembler, would eat a disproportionate amount of my mental resources.

Here in the forum, you will find a fair share of "purist macro haters" and an equally fair share of pragmatic programmers who do not feel ashamed to show a print "hello world" instead of the real stuff. It's an ideologic debate, sometimes a religious one, but try to get a feeling which side is more productive :biggrin:

mywan

I'm not entirely a purist when it comes to code conventions. I am a purist in the sense that I want to know the cost/benefit ratio as well as the underlying mechanics of the pure assembly when I use these libraries. So even when these macros come at effectively no machine cost and significant ease of use I still want to know what makes them tick. I don't even need to be especially proficient at straight assembly versions of those macros, as long as I can fully comprehend what the purist versions entail.

Basic string handling is what I'm experimenting with the most at the moment. One of my near term goals is to write an ini file parser. The string macros that operate by modifying existing strings is a very nice and powerful feature but also gets in the way sometimes. I also still have various types of arrays to experiment with implementing, dynamic buffer allocations, etc. I have got switch and select type constructs down very nicely in pure asm, without macros. I love the level of fine tuned control I have with these that higher level languages often don't provide.

My first operational assembly programs will be a small app (Browser.exe), that I believe I presently have sufficient skills to implement all the primary functionality of. It will drop itself into the same folder as either Firefox.exe or chrome.exe and copy all profile data from %appdata%\<sunbdirectory> to a profile subdirectory under Browser.exe such that your existing Firefox/Chrome installation, settings, addons, etc., becomes a standalone application. At that point you can merely copy or move your Firefox/Chrome folder from 'Program Files' to any USB, removable drive, or just a different location on the same drive. Then it can run from anywhere or any computer, with all the same settings, bookmarks, plugins. etc., and also act as a profile manager replacement. No tracks, cache, registry entries, etc., would be left on any machine it was run on. Except maybe supercookies, depending on settings, through flash and silverlight. I may include a mechanism to clean that up also, through caching the original state. It can also be used to back up and restore any existing Firefox/Chrome installation and settings without needing a new installation. My computer no longer even has a default browser, but rather defaults to a browser/source editor/settings selection menu. The one I'm working up to is a rewrite of my Drawer.exe app in assembly. For this I will likely make good use of your macro library :icon_mrgreen:


jj2007

Quote from: mywan on August 05, 2012, 12:25:26 PM
One of my near term goals is to write an ini file parser.

Here is one, written in less than ten minutes between fully waking up and having my first coffee ;-)

include \masm32\MasmBasic\MasmBasic.inc   ; download
  Init
  Recall "\Masm32\menus.ini", qEdMenu$()

; put you resource
editor here
; -----------------------------
;
&Microsoft Image Editor,\MASM32\BIN\Imagedit.exe

  push eax
  xor ebx, ebx
  Let edi="Editors used in your qEditor installation:"
  .Repeat
   mov esi, qEdMenu$(ebx)
   .if Instr_(esi, "edit", 1)
      .if Instr_(esi, ".exe", 1)
         Let edi=edi+CrLf$+Replace$(Left$(esi, Rinstr(esi, ",")-1), "&", "")
      .endif
   .endif
   inc ebx
  .Until ebx>stack
  pop eax
  Inkey edi
  Exit
end start

Keep me posted :t

mywan

Got side tracked momentarily on some EPR related algorithms (EPR paradox). Can't let crazy ideas go untested.

I'm not recognizing how your code snippet above is structured, or even what the majority of keywords refer to.
I see Recall in MasmBasic.inc but not making much from it either.
Can't find any other reference to the qEdMenu$ function.
Let looks incredibly useful from what I can make from MbGuide.rtf, but I'm very short on details. The rich text is also very difficult for me to parse, due to extreme variations in text sizes and limited display width.
With Rinstr again I am forced to look through cross-branched trees of unfamiliar macros to try to figure out what the initial macro actually does. Can only guess from context.
I also don't know what the relavance of "Editors used in your qEditor installation:" was. It didn't appear to be associated with the content of menus.ini. PushString (from MasmBasic.inc) seems like it's closely related to Let.

Anyway, trying to work through even that short code snippet blocked me at almost every turn while trying to work out the details of the logical flow. Some of those macros you use look like something I would have a difficult time living without, but at present I need to learn a lot of basics and include new macros piecemeal, one by one, as I gain better incite into their details.

I'm particularly interested in the Let macro atm. From what I gather it looks like something that is well suited for present issues I am working out. Recall also looks interesting. Where is the best place to get basic descriptions of these macros without the issues described?
-----
Noticed something about xhelp. When I search "instr" the only find is from REP.htm in opcodes.chm. Yet a search for "instring" returned 3 hits from masmlib.chm. Makes fishing for useful unknown macros, based on guesses, and such a bit more difficult. Full strings searches are understandable but this isn't always the case, like: "GetFiles" finds "GetFileSize()" in hlhelp.chm::/fsize.htm.

jj2007

Quote from: mywan on August 06, 2012, 06:10:14 PM
I'm not recognizing how your code snippet above is structured, or even what the majority of keywords refer to.
I see Recall in MasmBasic.inc but not making much from it either.
Can't find any other reference to the qEdMenu$ function.
...
I'm particularly interested in the Let macro atm. From what I gather it looks like something that is well suited for present issues I am working out. Recall also looks interesting. Where is the best place to get basic descriptions of these macros without the issues described?
-----
Noticed something about xhelp. When I search "instr" the only find is from REP.htm in opcodes.chm. Yet a search for "instring" returned 3 hits from masmlib.chm. Makes fishing for useful unknown macros, based on guesses, and such a bit more difficult. Full strings searches are understandable but this isn't always the case, like: "GetFiles" finds "GetFileSize()" in hlhelp.chm::/fsize.htm.

Thanks for the feedback. It seems I simply assume too much :redface:
Recall translates a file from disk into a string array; qEdMenu$ is not function but rather the array created by Recall. You could use Print qEdMenu$(1), for example, or use it both ways in Let statements:

Let esi="String1=["+qEdMenu$(1)+"]"
Let qEdMenu$(0)="I decided to change the first array element"

Instr_ and Rinstr are BASIC keywords that find matches in strings, where the "R" stands for "from the right end of the string".

HTH - right now I have little time to write, but MbGuide.rtf is a good source, as well as the MasmBasic Quick Reference

mywan

MasmBasicQuickReference seems to be a good start on what I need but leaves a lot to be desired. Looking at some of the string handling there it looks functionally quiet similar to functions I am quiet familiar with, just with differing syntax and details.

Your assumptions are understandable and a necessary part of being proficient. We all make similar presumptions in areas we understand, and it's not always easy to recognize just how many we make. Neither is it your job to hold my hand through this kind of stuff. I'll handle my own deficiencies to my own satisfaction in time. I appreciate what you can offer though :bgrin:

My string handling is still just a bunch of experimental attempts chopping and splicing strings where I can watch each step of what happens to different strings. One thing I still fall short on is efficient methods of buffering the string segments I want to store for later use. Haven't even got to arrays yet. Below is a couple of code snippets I wrote that I find to be the simplest and easiest to fully comprehend. All the rest of the code I've personally written with asm is just experimental snippets to see the output. Yes, highly limited experience here with assembly, but I have a fairly well defined sequence of practice problems (geared to my needs) to get better.

This sets the parameters to create a window with the bottom left corner at the mouse, and restricts the window inside the top and right of the screen (working code).
invoke GetCursorPos,addr pt
mov WinH,300
mov WinW,200
mov DskX,FUNC(GetSystemMetrics,SM_CXSCREEN)
mov DskY,FUNC(GetSystemMetrics,SM_CYSCREEN)
mov eax,pt.x
add eax,WinW
cmp DskX,eax
jg short pWinX
mov eax,DskX
sub eax,WinW
mov pt.x,eax
pWinX: mov eax,pt.y
sub eax,WinH
cmp eax,0
jg short pWinY
mov eax,WinH
mov pt.y,eax
pWinY: mov eax,WinH
sub pt.y,eax


Later on WinW and WinH will be defined as a multiple of the number of entries to display from a config file.

This is the most compact and comprehensible working WndProc I've written. It's far simpler and makes much more sense to me than nested .If statements I replaced. Still has a few comment lines from me playing with various stuff. Not sure why the endprc: is need, except maybe caution, but it seems to be part of all the samples I see.

WndProc PROC hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
cmp wParam,VK_ESCAPE
jne @F
invoke DestroyWindow,hWnd
jmp EnSw
@@: cmp wParam,VK_F1
jne @F
;mov MyArgs,len(rv(GetCommandLine))
;invoke ShellExecute,0,chr$("open"),CmdLine,NULL,NULL,SW_SHOW
;MsgBox hWnd, CmdArgs,"Command",MB_OK
jmp EnSw
@@: cmp uMsg,WM_ACTIVATEAPP
jne @F
cmp wParam,0
jne @F
invoke DestroyWindow,hWnd
jmp short EnSw
@@: cmp uMsg,WM_DESTROY
jne @F
invoke PostQuitMessage,0
jmp EnSw
@@: cmp uMsg,WM_LBUTTONDOWN
jne EnSw
MsgBox hWnd, "lpReturnedString","Command",MB_OK
EnSw: invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
endprc: xor eax, eax
ret
WndProc ENDP


jj2007

Quote from: mywan on August 06, 2012, 08:24:35 PM
MasmBasicQuickReference seems to be a good start on what I need but leaves a lot to be desired.
I know ;-)

QuoteThis is the most compact and comprehensible working WndProc I've written. It's far simpler and makes much more sense to me than nested .If statements I replaced. Still has a few comment lines from me playing with various stuff. Not sure why the endprc: is need, except maybe caution, but it seems to be part of all the samples I see.

WndProc PROC hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
cmp wParam,VK_ESCAPE
jne @F
invoke DestroyWindow,hWnd
jmp EnSw
@@: cmp wParam,VK_F1
jne @F
;mov MyArgs,len(rv(GetCommandLine))
;invoke ShellExecute,0,chr$("open"),CmdLine,NULL,NULL,SW_SHOW
;MsgBox hWnd, CmdArgs,"Command",MB_OK
jmp EnSw
@@: cmp uMsg,WM_ACTIVATEAPP
jne @F
cmp wParam,0
jne @F
invoke DestroyWindow,hWnd
jmp short EnSw
@@: cmp uMsg,WM_DESTROY
jne @F
invoke PostQuitMessage,0
jmp EnSw
@@: cmp uMsg,WM_LBUTTONDOWN
jne EnSw
MsgBox hWnd, "lpReturnedString","Command",MB_OK
EnSw: invoke DefWindowProc,hwnd,uMsg,wParam,lParam
ret
endprc: xor eax, eax
ret
WndProc ENDP


Caution: There are many Windows messages around, and some could have VK_ESCAPE or VK_F1 in their wParams with an entirely different meaning. The correct and safe way is to check for the uMsg first:
WndProc PROC hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
SWITCH uMsg
CASE WM_KEYUP
cmp wParam,VK_ESCAPE
jne @F
MsgBox hWnd, "Esc","Command",MB_OK
invoke DestroyWindow,hWnd
jmp EnSw
@@: cmp wParam,VK_F1
jne @F
MsgBox hWnd, "F1","Command",MB_OK
jmp EnSw
CASE WM_CREATE
...
ENDSW


mywan

Thanks for the heads up, I'll fix that. However, if you will notice the window exits if it merely loses focus via WM_ACTIVATEAPP.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms632614%28v=vs.85%29.aspx
QuoteSent when a window belonging to a different application than the active window is about to be activated. The message is sent to the application whose window is being activated and to the application whose window is being deactivated.

This is by design as the window is there to simply make a choice, and automatically go away if the user either makes that choice or decides to do something else entirely. The worst it could do is accidentally make the window do what it was supposed to.

jj2007

Quote from: mywan on August 07, 2012, 12:43:25 AMyou will notice the window exits if it merely loses focus via WM_ACTIVATEAPP

That part was ok anyway, because you checked uMsg. Interesting idea, by the way - your users like it? I suppose the first two, three times, they might be a bit surprised not to find the window any more :biggrin:

jj2007

#100
Quote from: mywan on August 06, 2012, 08:24:35 PMMy string handling is still just a bunch of experimental attempts chopping and splicing strings where I can watch each step of what happens to different strings.

You might use the deb macro, it is excellent for such testing:

include \masm32\MasmBasic\MasmBasic.inc   ; download
.data
lpBuffer   dd Buffer
Buffer   db 1000 dup(?)

   Init
   mov ebx, 3
   .Repeat
      mov lpBuffer, cat$(lpBuffer, chr$("test "))   ; Masm32 library
      deb 4, "Loop", ebx, $lpBuffer   ; prefix $ means "show the string content"
      dec ebx
   .Until Zero?
   Inkey "ok"
   Exit
end start

QuoteOne thing I still fall short on is efficient methods of buffering the string segments I want to store for later use.

MasmBasic uses a circular buffer for producing e.g.

   Print "Today is the ", Date$, ", ", Time$, Str$(", and your mouse is at position X=%i and Y=", MouseX), Str$(MouseY), CrLf$

Note the two Str$() - not so easy with Masm32 str$() ;)

deb comes with an integer:
  deb 1, "1,2,3 means show in a MsgBox", eax
  deb 4, "Four means show in console window", eax, Xmm0, ST(1), $esi
  deb 5, "Five means write to logfile", eax, $esi
  deb 6, "Six and higher means show the first n results", ebx, $lpBuffer

Test this with the example above:
   mov ebx, 200
   .Repeat
      mov lpBuffer, cat$(lpBuffer, chr$("test "))   ; Masm32 library
      deb 6, "Loop", ebx, $lpBuffer   ; prefix $ means "show the string content"
      dec ebx
   .Until Zero?

The MsgBoxes of deb 1/2/3 can be cancelled independently, so that you can debug several loops in one go.



Just for fun, here an example how a Split$() macro could be implemented:

include \masm32\MasmBasic\MasmBasic.inc   ; download

Split$ MACRO src, array, delimiter:=<" ">   ; splits strings using a delimiter, by default: a space
  StringToArray Replace$(src, delimiter, CrLf$), array
ENDM

   Init

splitcode_s:
   Split$ "This is a test for Split$()", MySplit$()   ; create the string array MySplit$() using spaces as delimiter
   ; Split$ "This@_#is@_#a@_#test@_#for@_#Split$()", MySplit$(), "@_#"   ; why simple if you can complicate things ;-)
splitcode_endp:
   push eax

   xor ecx, ecx
   PrintLine 'Result of splitting the string "This is a test for Split$()":'
   .Repeat
      Print Str$("\nString %i\t", ecx), MySplit$(ecx)
      inc ecx
   .Until ecx>=stack

   pop eax
   Print String$(5, CrLf$)
   CodeSize splitcode   ; 58 bytes needed for using Split$()
   Inkey
   Exit
end start

P.S.:
   Split$ Cat$(FileRead$("\Masm32\include\Windows.inc")), MySplit$(), CrLf$   ; works fine but is slow - about 10 ms on my Celeron M
   Recall "\Masm32\include\Windows.inc", MySplit$()   ; Recall is much faster, less than 3 ms; however, you cannot use Recall for merging two files to an array, as shown below:

   Split$ Cat$(FileRead$("\Masm32\include\Windows.inc")+FileRead$("\Masm32\include\WinExtra.inc")), MySplit$(), CrLf$
   Store "WinBoth.inc", MySplit$()   ; test the result

mywan

I really do need to experiment with your macros. One of my quandaries was how to best go about creating something similar to a circular buffer, to hold various string segments associated with my Drawer app. I have got to the point where I can chop the strings into the parts I need, but I haven't dealt with 'efficient' persistent buffer storage for them yet. Neither have I dealt with retrieving the initial strings from a configuration file yet. As you noted, the Str$() functions are not so straight forward in Masm32 as you make it look. The ones that operate by modifying the existing string is very cool, but sometimes not well suited in some circumstances.

I would still prefer addons like MasmBasic to be more modular in its approach, at least while I'm on such a steep learning curve.

Quote from: jj2007 on August 07, 2012, 02:23:37 AMThat part was ok anyway, because you checked uMsg. Interesting idea, by the way - your users like it? I suppose the first two, three times, they might be a bit surprised not to find the window any more :biggrin:
Everybody that has used it so far, though that number is limited, prefers it over anything else they have seen. The only version I have is an AutoIt script version written primarily as just a proof of concept demonstration. Even so, it turned out to be more than sufficient for me to go ahead and replace all my windows file association with it.

Those code snippets is the beginnings of the asm rewrite for Drawer.exe. The only surprise would be if the choice made failed. Basically if you double click a file type associated with the Drawer, like Drawer.asm, it provides a set of choices such as "Edit with Qeditor", "Edit With SciTe", "BuildAll (Console)", "BuildAll (Window)", "Execute", etc. As many options as the user wishes to include. If this is called from a source editor it can be instructed to pipe output back to that editor, even through a daisy chain of Drawers. If it's opened as a Drawer, such as a shortcut on the Quick Launch bar, it passes a user defines argument to identify which Drawer of options/apps to display. For instance, I have an "Internet" Drawer that contains all my browsers, Internet connection settings, etc., and can also include options to open different user profiles with the same browser. This Choose and die or die approach, like a context menu or start menu, means the Drawers are effectively reentrant, so choices can be cascaded like subfolders of choices without losing track of the reference file or drawer which initiated the cascade. In practice tracking the initial object is not usually required, and merely involves selecting a new Drawer like "Internet Settings" opening a new Drawer of options from the "Internet" Drawer.

This basically means I have literally hundreds of apps 2 clicks away from my Quick Launch bar with only a handful of icons and no need of a start menu. It basically puts a start menu on steroids on every icon on the quick launch bar. Really cool in Linux also with a minor addition of Linux file type support. Though the Xdesktop developers royally screwed Linux, especially given that this is how Linux is designed at the core to operate. Nobody is surprised when the start menu closes after a choice or no choice. Same here. The same Drawer.exe is shelled to from all my source code editors, with output piped back to the editor, for compile/edit/run/etc. options. Since this Drawer is also associated with every file type on my machine a separate drawer is defined for each file type. Even executables and folders have their own drawer configurations.

There is no background process or configurations outside the app folder, so it can be fully demonstrated (including Quick Launch) without making any changes to a machine that can't be undone simply by closing the new toolbar and deleting a shortcut in the "%APPDATA%\Microsoft\Windows\SendTo" folder. Unless of course you decide to make it the default open with for everything, or some subset of file types. SendTo is only useful for demonstrations to avoid system config changes.

That is my primary target to accomplish with assembly, though there are a few others targets in the interim. When I am proficient enough to rewrite my Drawer app in assembly I'll pretty much have the full range of skills I need for my personal needs.

jj2007

Quote from: mywan on August 07, 2012, 03:09:02 PM
I really do need to experiment with your macros. One of my quandaries was how to best go about creating something similar to a circular buffer, to hold various string segments associated with my Drawer app. ... I would still prefer addons like MasmBasic to be more modular in its approach, at least while I'm on such a steep learning curve.

Perhaps you simply stick to a string array? The Recall/Store pair is designed to do that, see example below (source is attached).

Re modular: What exactly do you mean? As it stands, you replace one line at the top of your file with another one:
include \masm32\include\masm32rt.inc
include \masm32\MasmBasic\MasmBasic.inc
... and you start adding the bits and pieces you need. You are free not to use any of the MB "modules" - most sources in \Masm32\examples do not complain if you replace the model etc and include headers with that magic line. Often, I do that with other members' code for exactly one purpose: to use the deb macro when debugging the code (Olly is a brilliant tool, but for most purposes a deb 4 does the job, too).

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   Recall "Drawer.ini", dri$()   ; create the dri$ array from a disk file
   push eax   ; eax is string count

   xor ecx, ecx
   Print "The old file:"
   .Repeat
      Print Str$("\nLine %i\t", ecx), dri$(ecx)
      inc ecx
   .Until ecx>=stack

   ; do useful things - freely invented:
   mov esi, Chr$("asm", 9)   ; we delete this one
   or ecx, -1   ; same as mov ecx, -1 but shorter
   .Repeat
      inc ecx
   .Until ecx>=stack || Instr_(dri$(ecx), esi)
   Delete dri$(ecx)   ; delete the asm file association string
   dec ecx
   .Repeat   ; replacing would be much easier than delete+append ;-)
      inc ecx
   .Until ecx>=stack || Len(dri$(ecx))==0
   Let dri$(ecx)="asm"+Tb$+"C:\Masm32\RichMasm\RichMasm.exe"   ; append a new one
   Store "DrawerNew.ini", dri$()   ; save your work to disk

   Inkey CrLf$, CrLf$, "The new file:", CrLf$, FileRead$("DrawerNew.ini")
   pop eax
   Exit
end start

mywan

The modularity issue is more about easing my capacity to deconstruct the internal logic of the macros than it is about to what degree I choose to make use of them. As such it's really my responsibility to deal with, not your responsibility to 'fix' in some way. That fact would make going over the details of what I personally would prefer, to soften my learning curve, a moot issue.

I will definitely get to the point of deconstructing the logic of your code snippets, though it might take me some time. I have learned a lot from you, even if my comprehension remains somewhat limited at the moment. The easiest stuff for me to fully understand are the core directives, and I'll have to work out from there.


jj2007

I have updated xHelp in the MasmBasic package. The source (xHelp.asc) is also included.
The only major change is under the hood:

      mov esi, Files$(ecx)
      Let HH$(ecx)=FileRead$(esi)   ; read the full content of e.g. \Masm32\help\xHelp\MASM_Programmers_Guide\Chap_04.htm
      ...
      Let HH$(ecx)=NoTag$(HH$(ecx))

The NoTag$ macro strips a string from all HTML tags. With the old xHelp, typing body would result in many matches - guess why :P