News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

MasmBasic

Started by jj2007, October 06, 2009, 08:24:57 PM

Previous topic - Next topic

jcfuller

Quote from: MusicalMike on December 23, 2010, 07:54:06 PM
It would be really cool if this macro system could be ported to nasm, or fasm. (gas is probably a lost cause for this)

Or JWASM (Linux)

James

jj2007

Thanks, folks. MB code assembles fine with JWasm, although there is a minor problem with macros expanding inside quoted strings.

The bigger problem with Linux is that MB uses quite a number of Windows APIs. I have zero experience with Linux, so I can only guess that Wine might be a solution... otherwise, one would have to go through the 100+ routines and check how to replace them with native Linux equivalents. If you are curious, see attached list of used Windows API calls.

frktons

Hi Jochen, I'd like to ask you a beginner question.
I tried to assemble and run your TestMasmbasic example for testing JWASM, but I used MASM32 package
and I get the following error during the execution.
What did I miss?

Mind is like a parachute. You know what to do in order to use it :-)

jj2007

Hi Frank,
The error is mine, and it's a simple one:

   test eax, FilesDiffer("Windows.inc", "\masm32\include\windows.inc", 1)   ; 1=case-insensitive
   .if Zero?
      Print "Files are equal, case-insensitive", CrLf$
   .else
      mov MyPos, edx   ; edx trashed by first Str$
      Print Str$("Files differ by %i", eax), Str$(" at pos %i, case-sensitive\n", MyPos)
   .endif

In my installation, I have a copy of windows.inc in the same folder as TestMasmBasic.asc, so it finds a file to compare with the second file. Copy \masm32\include\windows.inc to the current folder, and it will work (hopefully). Sorry for that.

frktons

OK Jochen. Moving the include file to the current folder solved the problem.
I guess you're going to fix it in the next release anyway, don't you?  :bg
Mind is like a parachute. You know what to do in order to use it :-)

jj2007

Quote from: frktons on December 23, 2010, 10:00:43 PM
OK Jochen. Moving the include file to the current folder solved the problem.
I guess you're going to fix it in the next release anyway, don't you?  :bg

I might introduce something like this:
   Let esi=FileRead$("\masm32\include\windows.inc")
   Let Mid$(esi, Instr_(esi, "echo WARNING"))="echo Warning"
   FileWrite "MyCopyOfWindows.inc", esi
   test eax, FilesDiffer("MyCopyOfWindows.inc", "\masm32\include\windows.inc", 1)   ; 1=case-insensitive
:bg
So it would not contradict the two lines before the FilesDiffer:
   ; compare a local copy of windows.inc against the original; only difference,
   ; at the very end: "echo Warning" instead of "echo WARNING"

frktons

Quote from: jj2007 on December 23, 2010, 10:19:44 PM
Quote from: frktons on December 23, 2010, 10:00:43 PM
OK Jochen. Moving the include file to the current folder solved the problem.
I guess you're going to fix it in the next release anyway, don't you?  :bg

I might introduce something like this:
   Let esi=FileRead$("\masm32\include\windows.inc")
   Let Mid$(esi, Instr_(esi, "echo WARNING"))="echo Warning"
   FileWrite "MyCopyOfWindows.inc", esi
   test eax, FilesDiffer("MyCopyOfWindows.inc", "\masm32\include\windows.inc", 1)   ; 1=case-insensitive
:bg
So it would not contradict the two lines before the FilesDiffer:
   ; compare a local copy of windows.inc against the original; only difference,
   ; at the very end: "echo Warning" instead of "echo WARNING"


Well, a better idea could be to copy the windows.inc to all the folders on the disk, just
to be sure none of them misses it  :lol
Mind is like a parachute. You know what to do in order to use it :-)

jj2007

Update 26.12. (attached on top of thread):
- bug in RichMasm editor fixed
- bug in FilesDiffer fixed (falsely returned "files are equal" if they were equal but of different length)
- fixed: Timer, Time$, Date$ used to expand multiple times; while this had no impact on the output, it generated unnecessary extra code.
- there is a remaining problem with JWasm: Print "This is Files$(0): ", Files$(0) expands the macro within the quoted text.
  Workaround: use offset txThisIsFiles or similar in .data section (or wait for a new version of JWasm).

New function: Let My$=Input$ reads a line from the console and assigns it to a string variable.
QuoteInput$
   Let esi="["+Input$("Type something and hit Enter: ")+"]"
   Print "You typed ", esi
   Print "You typed [", Input$("Type something and hit Enter: "), "]"
   Let My$=Input$()
   PrintLine "You typed: ", My$

Use with mov is possible:
   mov My$, Input$()
   Print My$
... but be aware that you get a pointer to a circular buffer that will eventually be overwritten after a bunch of other functions have used it. If you need the text permanently, use Let (which will give you a string on the heap).

jj2007

Update 27.12. introduces a minor feature with Store (marked in red):

QuoteStore
   Store "MyWin1.inc", MyRec$()      ; store the whole array excluding trailing empty strings
   Store "MyWin1.inc", MyRec$(), 1000   ; store the first 1000 strings of the array

   Open "O", #1, MyFileName$
   Store #1, MyRec$(), 20      ; store the first 20 strings
   Store #1, MyOtherRec$()      ; add the complete array not including trailing empty strings
   Close #1
Rem[/color]   - will trigger runtime error if file cannot be opened
   - if a file name is being used, the file will be closed after writing the strings
   - for #n, the file remains open for writing, allowing e.g. to write several arrays to the same file
[/b]

The following app demonstrates its usage:
Quoteinclude \masm32\MasmBasic\MasmBasic.inc
   Init
   
push Timer
[/size]   Recall "\Masm32\include\Windows.inc", L$()
   deb
4, "Windows.inc", edx
   Recall "\Masm32\include\WinExtra.inc", E$()
   deb
4, "WinExtra.inc", edx
   Open "O", #1, "WinAll.inc"
   Store #1, L$()   ; write complete arrays to disk,
   Store #1, E$()   ; not including trailing empty lines
   Close
   deb 4, "WinAll.inc  ", Lof("WinAll.inc")   ; deb 4 writes debug info to console
   
void Timer
   pop edx
   Inkey Str$("\nThat was fast: %i ms", eax-edx)
   
Exit
end start

Output:
Windows.inc     edx             849788
WinExtra.inc    edx             807877
WinAll.inc      eax             1657665

That was fast: 32 ms

jj2007

Update 3 January fixes the problem with printing DWORD...BYTE array elements, plus a minor bug with MovVal (could fail if the FPU was already full).

include \masm32\MasmBasic\MasmBasic.inc
Init
Dim MyDWords(9) As DWORD
Dim MyWords(9) As WORD
Dim MyBytes(9) As BYTE
For_ n=0 To 9
m2m MyDWords(n), n ; mem to mem
m2m MyWords(n), n ; mem to mem for words...
m2m MyBytes(n), n ; ... and bytes
shl MyDWords(n), 1 ; shift left, i.e. *2
shl MyWords(n), 1
shl MyBytes(n), 1
Next
For_ n=0 To 9
Print Str$("dw=%i\t", MyDWords(n)), Str$("w=%i\t", MyWords(n)), Str$("b=%i\n", MyBytes(n))
Next
Inkey "OK"
Exit
end start

Output:
dw=0    w=0     b=0
dw=2    w=2     b=2
dw=4    w=4     b=4
dw=6    w=6     b=6
dw=8    w=8     b=8
dw=10   w=10    b=10
dw=12   w=12    b=12
dw=14   w=14    b=14
dw=16   w=16    b=16
dw=18   w=18    b=18

New archive on top of thread.

Farabi

Pretty cool jochen.  :U Awesome
Those who had universe knowledges can control the world by a micro processor.
http://www.wix.com/farabio/firstpage

"Etos siperi elegi"

jj2007

What's new in version 16 January 2011 attached on top of this thread?

1. ToolTips:
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
SWITCH uMsg
CASE WM_CREATE
... (create controls)
ToolTips TTS_BALLOON
ToolTips hButton1, "Press this button"
ToolTips hButton2, wRes$(103) ; Unicode from resource file, e.g. russian or chinese
ToolTips hEdit, wRes$(104) ; "type some text"
ToolTips end


2. New Loc function, see Seek, Loc and Lof thread
3. The Dim command works now fine for structures and "type" arrays, e.g. Dim MyWords(99) As WORD
4. Store and Recall work now for structure and "type" arrays.

Quoteinclude \masm32\MasmBasic\MasmBasic.inc

FLOATSTRUC STRUCT   ; a colourful mix of formats
f4       REAL4 ?
f8       REAL8 ?
f10      REAL10 ?
fsrect   RECT <?>
filler   WORD ?   ; 40 bytes
FLOATSTRUC ENDS


   
Init[/color]
   Dim MyFS(99) As FLOATSTRUC   ; 100 strucs à 40 bytes = 4000 bytes
   
Dim L$(99)    ; same # of strings
   
For_ n=0 To 99
      Let L$(n)=Str$("String %i",n)   ; set the strings
   
   Let esi=Str$(n+4000)   ; create the strings "4000", "4001"
      MovVal MyFS(n, f4), esi   ; load the REAL4 member of the structure
   
   Let esi=Str$(n+8000)   ; create the strings "8000", "8001"
      MovVal f:xmm0, esi   ; load xmm0 as a float and...
      movlps MyFS(n, f8), xmm0   ; ...just for fun, shove it into the REAL8 member
      ffree st(7)   ; don't forget the FPU
      
fldpi
      fimul n
      fstp MyFS(n, f10)   ; PI*n
      mov eax, n
      mov MyFS(n, fsrect.left), eax
      add eax, 100
      mov MyFS(n, fsrect.top), eax
      add eax, 100
      mov MyFS(n, fsrect.right), eax
      add eax, 100
      mov MyFS(n, fsrect.bottom), eax
      add eax, 100
      mov MyFS(n, filler), ax   ; filler is word sized
   Next
   Open "O", #7, "StrucStore.dat"   ; open a file for output
   Store #7, MyFS()   ; write all array elements
   Store #7, L$()   ; append the strings, too
   Close #7
; variants:
;   Store #7, MyFS(20)   ; write element 20 only
;   Store #7, MyFS(21), 9   ; write element 21-29

   Dim MyReadFS(99) As FLOATSTRUC   ; 100 strucs * 40 bytes = 4000 bytes
   
Open "I", #1, "StrucStore.dat"
   Recall #1, MyReadFS()
   Print Str$("The file pointer is now at byte %i\n", eax)
   Recall #1, MyRead$()
   Close
   For_ n=0 To MyReadFS(?)-1
         .if n<5 || n>99-5
         
   Print Str$("\n%i\t", n)      ; \n is C style CrLf
         
   Print Str$("%i\t", MyReadFS(n, f4))
            Print Str$("%i\t", MyReadFS(n, f8))
            Print Str$("%5f\t", MyReadFS(n, f10))
            Print Str$("%i\t", MyReadFS(n, fsrect.left))
            Print Str$("%i\t", MyReadFS(n, fsrect.right))
            Print Str$("%i\t", MyReadFS(n, filler))
         .elseif n==50
            Print CrLf$, "..."
         .endif
   Next
   PrintLine CrLf$
   For_ n=0 To MyRead$(?)-1   ; struct(?) yields # of elements
         .if n<5 || n>99-5
         
   PrintLine Str$(n), Tb$, "[", MyRead$(n), "]"
         .elseif n==50
            PrintLine "..."
         .endif
   Next
      Inkey
"-- ok --"
   
Exit
end start

Output:
The file pointer is now at byte 4000

0       4000    8000    0.0     0       200     400
1       4001    8001    3.1416  1       201     401
2       4002    8002    6.2832  2       202     402
3       4003    8003    9.4248  3       203     403
4       4004    8004    12.566  4       204     404
...
95      4095    8095    298.45  95      295     495
96      4096    8096    301.59  96      296     496
97      4097    8097    304.73  97      297     497
98      4098    8098    307.88  98      298     498
99      4099    8099    311.02  99      299     499

0       [String 0]
1       [String 1]
2       [String 2]
3       [String 3]
4       [String 4]
...
95      [String 95]
96      [String 96]
97      [String 97]
98      [String 98]
99      [String 99]


The example creates an array of structures and an associated array of strings, fills both arrays and writes them sequentially to a file.
Then, a new structure with the same # of elements is being created, and Recall fills it from file; while the file is still open, Recall loads also the strings.
The Recall for the structures requires that the array exists and matches the amount of bytes stored before.
The Recall for the strings creates the new string array automatically, and the # of elements depends on the disk file.
See also MbGuide.rtf for further detail.

The disassembly will show that filling the structure is not very efficient, because the offset into memory is loaded every time. This can be shortened by the equivalent to ASSUME:

MyFS(ebx:n) ;   element n now relative to ebx

Quote      MyFS(ebx:n)
      movlps MyFS(f8), xmm0   ; ...just for fun, shove it into the REAL8 member
      ffree st(7)   ; don't forget the FPU
      
fldpi
      fimul n
      fstp MyFS(f10)   ; PI*n
      mov eax, n
      mov MyFS(fsrect.left), eax

The full example is attached.

jj2007

The typical implementation of chr$("abc") tends to create some superfluous data that may bloat the executable; superfluous because in "proper" assembler we would rather create once the MyAbc db "abc", 0 and reuse offset MyAbc as often as we need this.
Readability is better if we can see the "abc", though, therefore MasmBasic now reuses already created strings automatically. In practice, this will occasionally save a few bytes, unless you have a highly repetitive database or whatever code.

Quoteinclude \masm32\MasmBasic\MasmBasic.inc
  ; ramax = 0      ; uncomment, i.e. set to zero to test the repeated strings feature
  Init
  Dim
rc(3) As RECT
  .if Exist("StoredRect.dat")
   Open "I", #1, "StoredRect.dat"
   Recall #1, rc()   ; get array values from file
   PrintLine "Data loaded from file"
  .else
   xor ebx, ebx
   .Repeat
      rc(esi:ebx)   ; assign esi to element ebx
      
lea eax, [100+ebx*8]   ; load test values into the array
      
mov rc(left), eax
      mov rc(top), eax
      add eax, 120
      mov rc(right), eax
      mov rc(bottom), eax
      inc ebx
   .Until ebx>3
   Open "O", #1, "StoredRect.dat"
   Store #1, rc()
   Close #1
   PrintLine "Data saved to file"
  .endif
  xor ebx, ebx
  .Repeat
   rc(esi:ebx)   ; assign esi to element ebx
   Print Str$("\nElement %i.", ebx), Str$("left=  \t%i", rc(left))
   Print Str$("\nElement %i.", ebx), Str$("top=   \t%i", rc(top))
   Print Str$("\nElement %i.", ebx), Str$("right=\t%i", rc(right))
   Print Str$("\nElement %i.", ebx), Str$("bottom=\t%i", rc(bottom)), CrLf$
   inc ebx
  .Until ebx>3
  Inkey Str$("\n%i strings created, ", ract), Str$("%i repeated strings found", found)
  Exit
end start

Output:
...
Element 3.left=         124
Element 3.top=          124
Element 3.right=        244
Element 3.bottom=       244

10 strings created, 5 repeated strings found

dedndave

if you modify the string ?
maybe give them a way to create a unique string, if desired ?

jj2007

Quote from: dedndave on January 16, 2011, 06:09:57 PM
if you modify the string ?
Then it's no longer the same string, and you'll create a new one. The macro is case sensitive.

Quote
maybe give them a way to create a unique string, if desired ?
Honestly, I don't see a need to have the same static string twice in memory, but you can either use Masm32 chr$(), or use a .data entry:
Quote  .if Exist(chr$("StoredRect.dat"))
   .data
   MyUniqueFile   db "StoredRect.dat", 0
   
.code
   Open "I", #1, offset MyUniqueFile
   Recall #1, rc()   ; get array values from file

Both do, imho, not add any advantage over the simple
Quote  .if Exist("StoredRect.dat")
   Open "I", #1, "StoredRect.dat"
   Recall #1, rc()   ; get array values from file

Even more efficient:
Quote  Dim rc(3) As RECT
  mov esi, Chr$("StoredRect.dat")
  .if Exist(esi)
   Open "I", #1, esi
   Recall #1, rc()   ; get array values from file