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

jj2007

Quote from: dedndave on November 02, 2011, 09:55:40 AM
thanks, Jochen   :bg

i had not thought of using up the neg offsets   :U

Actually, for full performance, one should use HeapAlloc in combination with a memalign that matches the cache line size, which is 64 bytes*) according to Intel:

   PrintLine "Same approach using HeapAlloc:"
.data?
   pHeap   dd ?
   Ebx64   dd ?
.code
   invoke HeapAlloc, rv(GetProcessHeap), HEAP_ZERO_MEMORY, GlobalVars
   mov pHeap, eax
   xchg eax, ebx      ; in the interest of performance, we will align the pointer ;-)
   memalign ebx, 64    ; see Intel: The cache line size is the same in all three cases: 64 Bytes
   sub ebx, -128      ; use the negative offsets, too
   mov Ebx64, ebx     ; keep a global copy e.g. for assigning to ebx at the top of WndProc
   mov gv.gsB, 123
   debExpand=0
   deb 4, "short", gv.gsB
   debExpand=1
   deb 4, "expanded", gv.gsB
   deb 4, "long form", [ebx.GlobalVars.gsB-128]
   PrintLine Str$("gv=%i", [ebx.GlobalVars.gsB-128]/gv.gsB)
   PrintLine Str$("gv.gsB/1.23=%i", gv.gsB/1.23)
   invoke HeapFree, rv(GetProcessHeap), 0, pHeap
   deb 4, "HeapFree returned", eax
   Inkey "ok"

Output:
Same approach using HeapAlloc:
short   gv.gsB          123
expanded        [ebx.GlobalVars-128].gsB        123
long form       [ebx.GlobalVars.gsB-128]        123
gv=1
gv.gsB/1.23=100
HeapFree returned       eax             1

*)
QuoteIntel:
You are right that on modern IA-32 hardware, the cache line size is 64.  The value 128 is a legacy of the Intel Netburst Microarchitecture
(e.g. Intel Pentium D) where 64-byte lines are paired into 128-byte sectors.   When a line in a sector is fetched, the hardware automatically
fetches the other line in the sector too.  So from a false sharing perspective, the effective line size is 128 bytes on the Netburst processors
[/b]

P.S.: I had to upload a new version because wCL$() (Unicode commandline, uses GetCommandLineW) had a minor bug. This is now valid code:

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   
xor ebx, ebx
   wPrint wChr$("Checking the commandline:")
@@:   inc ebx
   mov ecx, wCL$(ebx)
   jecxz @F
   wPrint wCrLf$, wStr$("arg %i\t", ebx), ecx
   .if wInstr_(ecx, wChr$("/"))
      wPrint wChr$(9, "valid options have a slash")
   .endif
   jmp @B
@@:   wInkey wChr$(13, 10, 10, "-- bye --")
   Exit
end start

Sample output with this batch file and a cyrillic file dragged over the bat:
@echo off
\masm32\RichMasm\CmdLineW.exe /read /write /dump %1 \and \more \args
pause

Checking the commandline:
arg 1   /read   valid options have a slash
arg 2   /write  valid options have a slash
arg 3   /dump   valid options have a slash
arg 4   D:\masm32\RichMasm\Добро пожаловать.txt
arg 5   \and
arg 6   \more
arg 7   \args

jj2007

Here is a little tool to embed binary data into your executable, similar to JWasm's IncBin directive. Its usage is very simple:

   drag the file to embed over CreateIncBin.exe
   insert
include FileName.dat into the .data section
   use
offset filename as a pointer to the embedded data

Example:
include \masm32\include\masm32rt.inc

.data
include SayHello.dat ; generated with CreateIncBin.exe
db 0 ; for displaying embedded text
.code
start: inkey offset SayHello, 13, 10, 10, 10, "Masm is fun!"
exit
end start


Source and demo attached.

jj2007

Inspired by Edgar's fast file time thread, the next edition of MasmBasic will feature a NanoTimer macro. This is a pre-release in case you need one urgently ;-)

include \masm32\MasmBasic\MasmBasic.inc   ; download
include NanoTimer.mac   ; attached

   Init
   SleepMs = 250   ; 125/250/500 yield exact results

   NanoTimer()   ; start the timer
   Delay SleepMs   ; sleep some milliseconds
   Print Str$("%i milliseconds = ",
NanoTimer(ms))
   
   NanoTimer()
   Delay SleepMs
   Print Str$("%i microseconds = ",
NanoTimer(µs))
   
   NanoTimer()
   Delay SleepMs
   Inkey Str$("%i nanoseconds",
NanoTimer())

   Exit
end start


Output:
250 milliseconds = 250000 microseconds = 250000000 nanoseconds

I hope the syntax is self-explanatory :bg

P.S. The resolution is 100 ns.

MichaelW

#288
The problem I see with this is that (in my tests under Windows 2000) the effective resolution of the interrupt time defaults to 10ms, although you can use timeBeginPeriod to set it to 1ms. With an effective resolution of a few microseconds the high-resolution performance counter would be a better choice for a precision timer.

I just got reminded of a related function:

;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================

printf MACRO format:REQ, args:VARARG
    IFNB <args>
        invoke crt_printf, cfm$(format), args
    ELSE
        invoke crt_printf, cfm$(format)
    ENDIF
    EXITM <>
ENDM

;==============================================================================
    .data
        timeAdjustment          dd 0
        timeIncrement           dd 0
        timeAdjustmentDisabled  dd 0
    .code
;==============================================================================
start:
;==============================================================================

    invoke GetSystemTimeAdjustment, ADDR timeAdjustment,
                                    ADDR timeIncrement,
                                    ADDR timeAdjustmentDisabled

    printf("%d\n%d\n\n",timeAdjustment,timeIncrement)

    inkey "Press any key to exit..."
    exit
;==============================================================================
end start


The (somewhat strange) results running on my Windows 2000 system:

100144
100144

eschew obfuscation

jj2007

Michael,
You are right - I had not yet seen this granularity, quite familiar from GetTickCount. No miracles under the sun :(
But the QPC seems to work:
100     ms delay: 98.000 s
200     ms delay: 203.00 s
300     ms delay: 312.00 s
400     ms delay: 406.00 s
500     ms delay: 500.00 s
600     ms delay: 608.00 s
700     ms delay: 703.00 s
800     ms delay: 812.00 s
900     ms delay: 906.00 s
1000    ms delay: 1000.00 s


Same syntax as before, macros attached.

include \masm32\MasmBasic\MasmBasic.inc   ; download
include NanoTimer.mac

   Init
   SleepMs = 250   ; 125/250/500 yield exact results
   NanoTimer()   ; start the timer
   Delay SleepMs   ; sleep some milliseconds
   Print Str$("%3f seconds = ",
NanoTimer(s))
   
   NanoTimer()
   Delay SleepMs
   Print Str$("%i milliseconds = ",
NanoTimer(ms))
   
   NanoTimer()
   Delay SleepMs
   Inkey Str$("%i microseconds",
NanoTimer(µs))
   Exit
end start

Output: 0.241 seconds = 250 milliseconds = 249795 microseconds

jj2007

#290
I haven't updated MasmBasic since 3 November, but I was working a lot with MasmBasic, and there are some by-products of this activity that I could share with you. A rough count says it has now about 140 commands, plus 28 Unicode versions :bg

The 28 Jan 2012 version is ready for download here. Incomplete list of changes:

- fully compatible with 1. Masm32 version 11, 2. all Masm versions starting from ML 6.15, 3. JWasm
- updated the C++ example at \masm32\MasmBasic\MB2C\CalcTest.asc
- SetClip$:
   SetClip$ "Today is the "+Date$+", and the time is "+Time$
   wSetClip$ "Today is the "+wDate$+", and the time is "+wTime$
   SetClip$ hBmp, CF_BITMAP
- Rand() improved, e.g. Rand(1, 7, MyArray(ebx))      ; put random number directly into the (byte, word, dword, qword, r4, r8) array
- crt_xxx dependencies eliminated except for QSort
- wChr$(eax) works
- NanoTimer uses QPC
- Launch (aka ShellExecute) can now passdata to consoles ->ParentData$()
  Launch "my.exe", passdata, esi [, Len(esi)]
- IncBin function here
- solved LNK4078 warning problem
- interface to Kip Irvine's library
- DosBasic
- Inkey and wInkey return Chr$/wChr$ compatible code, i.e. Windows keys (VK_LEFT...) instead of scan codes
- combining Left$ and Trim$ works now:
   Let edi="       badly_formatted_equate     EQU 123"      ; desired output: [badly_formatted_equate]
   Let edi=Trim$(Left$(edi, Instr_(edi, "equ", 5)-1))      ; Instr 5=case-insensitive, whole word
   Print "[", edi, "]"
- fixed a problem with the DLL examples (\masm32\RichMasm\Res\MbMicroDll.asc) and RichMasm

The new functions are documented, as usual, in \masm32\MasmBasic\MbGuide.rtf

Some examples are attached, including TestMasmBasic.asc, my internal testbed.
The second attachment contains the two templates (spreadsheet, FPU) that are marked in red when you click "New Masm source" in RichMasm.

jj2007

I hesitated a lot to put the 1 Feb version online, because it has some potential for abuse; however, it seems that a simple firewall would inform the user that there is a download attempt, so here it is:

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   ; read a file from the Internet, strip all HTML tags, scripts and styles, and display it on the screen:
   Inkey NoTag$(FileRead$("http://www.masm32.com"))
   Exit
end start

I am curious if any AV would trigger an alert ::)

Output:
MASM32 Home Page
The MASM32 SDK
Index Download Installation Forum License History Myths Why
The MASM32 SDK Version 11
Description The MASM32 SDK version 11 is a working development environment for p
rogrammers who are interested in either learning or writing 32 bit Microsoft ass
embler (MASM). The installation is an automated process that installs the correc
t directory tree structure on the local drive of your choice.
...
Not for the faint of heart. If MASM is beyond you, take up server side scripting
.
Copyright (c) The MASM32 SDK 1998 - 2012 All Rights Reserved

dedndave

Smith & Wesson sells guns - that doesn't make them killers - lol

reminds me of a hoax article i saw about an interview with an Aussie General...
http://www.hoax-slayer.com/cosgrove-female-interviewer.shtml

QuoteWell, Ma'am, you're equipped to be a prostitute, but you're not one, are you?

jj2007

The Feb 9 2012 version of MasmBasic features a new Fcmp (more):

Fcmp
MyPI_hi   REAL4   3.14160
   ...
   Fcmp MyPI_hi, PI, medium
   .if FcmpLesser
      Print Str$("MyPI_hi at %f is lower than the real PI\n", MyPI_hi)
   .elseif Zero?
      Print Str$("MyPI_hi at %f is exact\n", MyPI_hi)
   .else
      Print Str$("MyPI_hi at %f is higher than the real PI\n", MyPI_hi)
   .endif
Rem   - returns Zero? and Sign? flags: Sign? means "first arg below second arg"
   - you may use FcmpGreater and FcmpLess (aka !Sign? and Sign?)
   - single arg, e.g. Fcmp xmm1, tests for zero
   - almost any number formats can be compared, including xmm registers etc

Example:
   m2m eax, 123
   movd xmm0, eax
   Print Str$("Xmm0 at %i is ", xmm0)
   Fcmp xmm0, FP10(123.1)
   .if Sign?
      PrintLine "lower"
   .elseif Zero?
      PrintLine "equal"
   .else
      PrintLine "higher"
   .endif

For Fcmp xmm0, FP10(123.1), low the result would be "equal".

jj2007

Update 17 March (attached here):

- SendControlKey hWin, VK_xxx: send e.g. a paste key to Notepad
- [Set]HtmlClip$ handles the format used by Microsoft Office as well as Thunderbird and many others
- GetFiles companions: GfSize, GfDate$, GfTime$, GfAgeHours, GfAgeMinutes, GfAgeMs return file properties:

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   GetFiles *.asm   ; get all sources in the current folder and its subfolders
   xchg eax, ecx   ; save #files
   xor edi, edi      ; counter for the filter
   For_ ebx=0 To ecx-1
      .if GfAgeHours(ebx)<=24   ; pick your latest code
         call PrintFileInfo
      .endif
   Next
   Print Str$("\n%i files younger than 24 hours\n", edi)

   GetFiles *.hlp|*.exe   ; help files or executables
   xchg eax, ecx   ; save #files
   xor edi, edi      ; counter for the filter
   For_ ebx=0 To ecx-1
      .if !Instr_(7, GfDate$(ebx), "20")   ; pre-2000 - your date format might differ
         call PrintFileInfo
      .endif
   Next
   Print Str$("\n%i files from the previous millennium", edi)
   Inkey CrLf$, "ok"
   Exit
PrintFileInfo proc
  inc edi
  mov esi, Cat$(Files$(ebx)+Space$(20))   ; we simulate LSET
  Print Str$("\n#%i ", ebx+1), GfDate$(ebx), ", ", GfTime$(ebx), Spc2$
  Print Mid$(esi, Rinstr(esi, "\")+1, 20)
  Print Str$(" age: %ih", GfAgeHours(ebx))
Print Str$(" = %im", GfAgeMinutes(ebx))   ; age can be expressed in
Print Str$(" = %ims", GfAgeMs(ebx))   ; hours, minutes, milliseconds
  Print Str$(", size=%i", edx::GfSize(ebx))
  ret
PrintFileInfo endp

end start

Enjoy :U

jj2007

Update 18 March (attached here):

Just a stupid little bugfix: Fcmp whatever, MyDwordInMemory would load the dword as Real4. All other operands (reg32, xmm, qword, real4/8/10) worked already fine in the 17 March version.

jj2007

Update 28 March (attached here):

- Insert and Delete accept a counter as second arg:
      Insert L$(100)   ; insert one empty string at index 100
      Let L$(100)="brandnew"
      Delete L$(1100)   ; delete the string
      mov ecx, 1000   ; just a counter
      Insert L$(0), ecx   ; insert ecx items
      Delete L$(1111), ecx   ; delete ecx at another position

- the string engine has a new feature: when accessing the n+1th element of an array, automatic expansion is triggered. Example:

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   Dim My$(1)   ; a string array with 2 elements, 0 and 1
   ; Let My$(3)="This would trigger a runtime error"   ; no good
   For_ n=0 To 9
      Let My$(n)=Str$("This is string #%i", n)   ; but incremental assignments are OK
   Next
   For_ n=0 To 9
      PrintLine My$(n)
   Next

   Inkey "OK"
   Exit
end start

jj2007

Update 28 April (download from top of thread). Major changes:

- GetFiles got a companion, SortFiles:

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init
   Print "Oldest files in examples folder:", CrLf$
   GetFiles \Masm32\Examples\*.asm
   AddFiles \Masm32\Examples\*.inc
   SortFiles date, asc   ; first arg can be name, size, date; second arg asc or desc or blank=descending
   For_ ebx=0 To Min(9, eax-1)
      PrintLine Str$(GfSize(ebx)), Tb$, GfDate$(ebx), Spc2$, GfTime$(ebx), Tb$, Files$(ebx)
   Next
   Inkey
   Exit
end start

Oldest files in examples folder:
1250    20.01.1999  08:35:56    \Masm32\Examples\exampl01\minimum\minimum.asm
6560    21.01.1999  03:38:26    \Masm32\Examples\exampl01\dll\loaddll\loaddll.asm
6792    21.01.1999  03:39:02    \Masm32\Examples\exampl01\dll\calldll\calldll.asm
13798   27.01.1999  15:31:32    \Masm32\Examples\exampl01\generic\generic.asm
6561    12.04.1999  15:45:34    \Masm32\Examples\exampl01\oldstyle\oldstyle.asm
2463    30.04.1999  03:19:40    \Masm32\Examples\exampl01\dll\tstdll.asm
9806    03.05.1999  04:49:12    \Masm32\Examples\exampl01\regkey\regkey.asm
2104    17.05.1999  05:02:50    \Masm32\Examples\exampl01\mdidemo\filedlgs.asm
969     17.05.1999  05:02:50    \Masm32\Examples\exampl01\mdidemo\statusbr.asm
2065    17.05.1999  05:02:50    \Masm32\Examples\exampl01\mdidemo\tbmacros.asm


- ArraySort and ArrayMinMax can now handle QWORD and REAL8 arrays

- ArraySort has become faster, and beats Masm32 nrQsort in typical situations, see this thread (not to mention the horribly slow CRT version of QuickSort :bg)

The ultrafast algo is inspired by MarWin alias Marty Winkler's marvelous sorting algo tool - compliments to Germany :U


Enjoy :thumbu

jj2007

Addendum: The ArraySort macro has no option to set the sort order at runtime, but there is a "manual" workaround, see below. I attach the updated manual, including also docu of the SortFiles macro.

   ...
   ArraySort MyR4(-:123)      ; sort descending, first 123 elements only
   lea esi, KeyArr(0)      ; load start address of an array containing keys (e.g. original position)
   ArraySort MyR4(-), esi      ; sort Real4 array descending, keep key values with Real4 values
   ArraySort MyR4(+), KeyArr(), fill      ; use key array directly, fill with original unsorted order (0, 1, 2, ... n)
Rem   - returns number of sorted elements in eax
   - use for signed DWORD, signed QWORD, REAL4 and REAL8 arrays; for strings, see QSort
   - ArraySort sorts ascending if no - is found in the first argument. In case you need to determine the order based on
     a runtime parameter, you need to use the invoke syntax as follows:
      MbArrSort PROTO :DWORD, :DWORD, :DWORD, :DWORD
      invoke MbArrSort, ptr to first element, #elementsi, ptr to key array, mode
      with mode=size (4, 8) or (32 and real) or (64 and ascending) or (1 and MinMaxOnly) or (2 and fill the key)
   - uses a very fast algo inspired by Marwin's site, often faster than QuickSort (and much faster than the crt qsort)
   - Real4 and Dword use the same algo; the only difference is that the Real4 variant applies an extra
     pass to invert the order of negative elements. Speedwise there is no measurable difference

Little example:
include \masm32\MasmBasic\MasmBasic.inc   ; download
  Init
  StringToArray Clip$(), L$()   ; get something from the clipboard, e.g. from http://www.masm32.com/board/index.php?action=stats
  dec eax
  xchg eax, ebx         ; move linecount to permanent reg32
  Dim MyDw(ebx) As DWORD
  For_ ct=0 To ebx
   mov esi, L$(ct)
   .if Rinstr(esi, Tb$)   ; get rightmost tab char
      push edx
      inc eax
      mov MyDw(ct), Val(eax)
      pop ecx
      Let L$(ct)=Trim$(Left$(esi, ecx))
   .endif
  Next
  ArrayMinMax MyDw()
  xchg eax, ecx
  xchg ecx, edx   ; ascending sort...
  Print Str$("\nRange of values is %i to ", edx), Str$(ecx)
  Dim MyKey(ebx) As DWORD
  ArraySort MyDw(), MyKey(), fill   ; sort MyDw(), set reference to MyKey
  For_ ecx=0 To ebx
   Print Str$("\n#%i\t", ecx), Str$(MyDw(ecx)), Tb$, L$(MyKey(ecx))
  Next
  Inkey
  Exit
debug
end start

jj2007

Update 11 May 2012: Error handling with Try/Catch/Finally

In its simplest form, the syntax for a full console app is as follows:

Quoteinclude \masm32\MasmBasic\MasmBasic.inc   ; download
   Init tc   ; install the handler
  Try         ; set the start label
   xor ecx, ecx
   inc dword ptr [ecx]   ; you should go to jail for that, but we'll be kind
  Catch         ; set the end label
   Inkey "Incrementing 0 is illegal!!"
   Exit
   TryCatchEnd   
; activate the handler - must be last instruction before "end start"
end start

A more sophisticated example, a loop that ends up dividing by zero:

Quoteinclude \masm32\MasmBasic\MasmBasic.inc   ; download
   Init tc   ; install the handler
  Try         ; set the start label
     mov ecx, 4
     .Repeat
      Print Str$("44444444/%i = ", ecx)
      mov eax, 44444444
      cdq
      div ecx
      PrintLine Str$(eax)
      dec ecx
   .Until Sign?
  Catch only   ; "only" signals skip if no problems
     mov eax, 31415926   ; ... otherwise, change your results here...
     PrintLine "undefined"   ; ... or issue a warning, etc.
  Finally
   .if LastEx(code)   ; we better give some extra info
        PrintLine CrLf$, "Ouch, we had an exception at ", CrLf$, "address", Tb$,\
        Hex$(LastEx(addr)), CrLf$, "code", Tb$, Hex$(LastEx(code)), CrLf$, Err$()
   .endif
   Inkey
   Exit
   TryCatchEnd   
; activate the handler - must be last instruction before "end start"
end start

Output:
44444444/4 = 11111111
44444444/3 = 14814814
44444444/2 = 22222222
44444444/1 = 44444444
44444444/0 = undefined

Ouch, we had an exception at
address 00401064
code    C0000094


Different languages use different syntax. I have tried to stay as close as possible to "natural" wording (and i don't like "Finally", but it seems to be recognised Visual Basic jargon... feedback and opinions welcome :bg)