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

Main Menu


Started by jj2007, May 23, 2012, 10:16:07 PM

Previous topic - Next topic


I have downloaded the SDK from

Mary & Mike don't work, and "Sample TTS voice" works but says most of the time "bla" (really :tongue:)


Creative coders use backward thinking techniques as a strategy.


A test program for SAPI 5.x
#include <windows.h>
#include <sapi.h>

#pragma comment(lib, "ole32.lib")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
ISpVoice * pVoice = NULL;
HRESULT hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_ALL, &IID_ISpVoice, (void **)&pVoice);
if (SUCCEEDED(hr))
hr = pVoice->lpVtbl->Speak(pVoice, L"<voice optional='Gender=Male;'>Hello world", 0, NULL);
//hr = pVoice->lpVtbl->Speak(pVoice, L"<voice required='Gender = Male;'>Hello world", 0, NULL);
//if (hr) hr = pVoice->lpVtbl->Speak(pVoice, L"<voice required='Gender = Female;'>Hello world", 0, NULL);
pVoice = NULL;
return 0;
May the source be with you


Very interesting, Timo - I didn't know that you can specify options with XML tags:
hr = pVoice->lpVtbl->Speak(pVoice, L"<voice optional='Gender=Male;'>Hello world, I am a man", 0, NULL);
if (1)
hr = pVoice->lpVtbl->Speak(pVoice, L"<voice required='Gender=Female;'>Hello world, I am a woman", 0, NULL);

With this modification, I hear both strings spoken by the same female voice when built as X64, but only "I am a woman" when built as x86  :sad:

With MasmBasic:

Say wChr$("<voice optional='Gender=lgbt;'>Good morning")

For Gender=, everything works fine except Gender=male, which is mute :rolleyes:


Update 28 July 2020 (in response to fast median thread and timings)

include \masm32\MasmBasic\
  Dim MyArray() As REAL4        ; can be DWORD, REAL4 or REAL8
  For_ ecx=0 To 99
        Rand(-888.8, 999.9, MyArray(ecx))       ; put random numbers into the array
  PrintLine Str$("The elements:\t%i", MyArray(?))
  PrintLine Str$("The columns:\t%i", MyArray(?cols))    ; will return 0: this array is one-dimensional
  PrintLine Str$("The minimum:\t%f", MyArray(?min)v)    ; the functions marked with v return the
  PrintLine Str$("The maximum:\t%9f", MyArray(?max)v)   ; value in ST(0); the v tells Str$() to discard
  PrintLine Str$("The median:\t%f", MyArray(?median)v; ST(0) with fstp st after printing
  PrintLine Str$("The mean:\t%9f", MyArray(?mean)v)     ; ?average does the same as ?mean
  PrintLine Str$("The sum: \t%9f", MyArray(?sum)v)      ; sum of all elements

The elements:   100
The columns:    0
The minimum:    -884.9537
The maximum:    994.308716
The median:     -8.574001
The mean:       35.9553806
The sum:        3595.53806


Cool, all in one.  :cool:
Creative coders use backward thinking techniques as a strategy.


Great work, JJ

One question about the median function.

  fld st
  faddp st(2), st
  fsub arrInf.yMin ; make positive
  fmul arrInf.yRange ; normalise 0...99999
  ; fadd FP4(0.45)
  fistp arrInf.yIndex ; we need a
  mov eax, arrInf.yIndex ; reg32
  inc dword ptr [edi+4*eax] ; Populate the Median buffer with the values as index numbers and keep track for duplicates in one go
  dec ecx
  jns aiPop ; 0 is still ok

the size of the Table (MedBuffer) used in edi must be the double when we are dealing with negative values, right ?

I mean, say i have limits from -255 to 255, then the total size of the table should be (256*4*2) ?

Also, since it will be subtracting the negative values to set onto the proper address of the table, should it calculate the median and preserve the sign in case the resultant median is also negative ?

I previously normalized the inputed data to -1 to 1, so i divided each value with 255. On your version, at that routine, i made the following port:

; [Med_MinVal_Normalized: R$ -1.0] = < --- The minimum value as set in arrInf.yMin already normalized

        fld F$esi | fsub R$Med_MinVal_Normalized | fmul R$Float255 | fistp D@Index | mov eax D@Index ; Multiplied the input with 255 to it points to the proper address on the table.
        inc D$edi+eax*4   ; Populate the Median buffer with the values as index numbers
        add esi ebx       ; and keep track for duplicates in one go. And skip positions if needed
    Repeat_Until_Zero ecx

Is this correct ? Will it result a negative median (thus, preserving the sign) if the median is, in fact, negative ?
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:


Hi Guga,
In the example above the range of values is determined by the Rand(-888.8, 999.9, destination). The normalisation takes care of negative numbers. In fact, the median for this dataset is slightly negative.
The minimum:    -884.9537
The maximum:    994.308716
The median:     -8.574001
The mean:       35.9553806


Hi JJ, ok, but how the normalization works ? The math operation, i mean.

I saw it takes the subtraction of the Maxvalue to the MinValue, but it will divide by what ?

Fraction = (Max-Min)/ WhatData ?

What Data is suppose to be divided with ? If we have negative numbers, it means we have double the amount of values, right ? ex, if the limits to be settled are -255 to 255, it means we have, in fact a limit of 255*2 data to load. So, if our Minimum value found is, let´s say -200 and Max is 185, then to apply the normalization, is correct to do this ?

Fraction = (185-(-200))(2*255) = (185+200)/510 = 385/510 = 0.754901961

So, our Fraction (arrInf.yRange) would be 0.754901961, right ?

And to recover the proper median at the end of the function, we do this ?

New Value = ValueFound/Fraction - Minimum Value ? = ValueFound/0.75490191 - (-200) ?

Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:


Quote from: guga on July 30, 2020, 12:11:06 PM
Hi JJ, ok, but how the normalization works ? The math operation, i mean.

Search for arrInf.yRange - it should be self-explanatory :cool:


New version online

There are numerous little improvements under the hood. One new feature: RichMasm knows a new option called "Close" (see the Capitals of Europe thread for an example).

OPT_Close Capitals of Europe ; close the window before building

When testing a Windows program, I often forget to close it before hitting F6 (the build all key). Inevitably, the linker chokes because the exe is still running. Now with OPT_Close <the window title> RichMasm sends a WM_CLOSE message to the running exe, and it closes (unless there are data to be saved, of course). Problem solved :tongue:

Another new feature: the canvas control (for plots, images and maps, see e.g. GuiImage) has now a right-click context menu for saving its content to the clipboard or to a file. It can also be zoomed to fill the entire client area.

The one-line If_ accepts now also Exist(), FileOpen$() etc args:
include \masm32\MasmBasic\
  If_ Exist("*.bmp") Then PrintLine "yeah, ", Exist$, " exists"
  If_ not Exist("no.bmp") Then Print "Nope, it does not exist"

Btw did you know the difference between these two?
a) ; int 3
b) int 3

With a), hitting F6 builds and launches the executable
With b), hitting F6 builds and launches \Masm32\OllyDbg\ollydbg.exe <executable path>

Greetings from a lazy programmer :tongue:


New version online

Here is an attempt to use an I/O library to communicate with ports, inspired by this FreeBasic thread:

include \masm32\MasmBasic\         ; download
  if 0
        Dll "InpOut32"          ; WikiDll; first three functions
        Dll "DlPortIO"          ; Scientific Software Tools at WikiDll ; next four
  Declare void Out32, 2         ; ushort port, data
  Declare void Inp32, 2         ; ushort port, data
  Declare IsInpOutDriverOpen    ; no args
  Declare DlPortReadPortUshort, 1       ; ushort port
  Declare DlPortReadPortUlong, 1       ; int port
  Declare DlPortWritePortUshort, 2     ; int port, data
  Declare DlPortWritePortUlong, 2      ; int port, data
  PrintLine Str$("Out32: %i", Out32(?))        ; prints the address or zero if the function was not found
  If_ IsInpOutDriverOpen(?) Then <Print Str$("driver open: %i\n", IsInpOutDriverOpen())>        ; crashes
  If_ Out32(?) Then <Out32(111h, 222h)>        ; crashes
  If_ DlPortReadPortUshort(?) Then <Print Str$("read port: %i\n", DlPortReadPortUshort(99))> ; error message
  PrintLine "done"

The DLLs are available at various places, see links above, but it is unclear how they actually work. Inp32 and Out32 are actually straightforward, under the hood you see in al, dx and out dx, al, but the rest is badly documented.

What's new in MasmBasic then?
- SomeFunction(?) checks if the function has been loaded - see IsInpOutDriverOpen(?) above
- in console mode, you get feedback on Declare:
not found (line 9):     Out32
not found (line 10):    Inp32
not found (line 11):    IsInpOutDriverOpen

Unrelated: $Data accepts now empty strings. This was inspired by Guga's Resource String Table thread. If, for example, you copy \Masm32\include\ into a source (to display the strings, whatever), there are some empty strings, and they used to choke with $Data. This little problem has been solved*)

In case you want a huge string table copied from a text file:
- File/New/Masm source: pick the Console example
- between the include line and Init, paste the lines from the text file
- select the whole block
- press Alt R
- insert $Data ]#[ into the replace edit control, hit Return and confirm
- insert these three lines under Init:
  Read my$()
  For_ ecx=0 To eax-1
PrintLine my$(ecx)

*) at least for UAsm and AsmC - M$ MASM misbehaves on this one, and for ML there is no workaround other than deleting the offending empty line; otherwise, the whole MasmBasic library is fully compatible with ML versions 6.15 ... 14 or so, but attention, some recent ML versions have ugly bugs. I strongly recommend to use UAsm.


Version 12 October is online

- version 3 of Capitals of Europe will build with the latest MasmBasic package (maps with flicker-free Unicode tooltips)
- new macro LineNumber(buffer, search$):

include \masm32\MasmBasic\
Print Str$("The string 'Duplicate' is in line %i of", LineNumber(FileRead$("\Masm32\include\"), "Duplicate")+1)

The string 'Duplicate' is in line 26900 of

Optional 3rd argument: mode as for Instr_(), i.e. 1=case-insensitive, 2=first char of match case-insensitive, 4=full word...
Optional 4th argument: line delimiter char, e.g. 13 (default is 10=linefeed)

LineNumber() is not particularly fast, but it returns the #lines of Duplicate in in under 3ms on my trusty old Core i5 - reading the file included :cool:


Update 16 October 2020 is online and features a new StringBuild macro:

StringBuild edi ; ---------- start building the string
For_ ecx=1 To 70000
Let edi=CrLf$+"string "+Str$("#%i", ecx)
StringBuild ; ---------- stop string building

For a 1MB string, the speed gain compared to Let my$=my$+"xx" is roughly a factor 1,000 :cool:

With e.g. StringBuild some$, 2000000 the default 1MB allocation can be adjusted if needed. For usedeb=1 (default), overflow will trigger a MsgBox; this costs a few bytes more but has practically no influence on performance.


GuiParas equ "Plotting a linear regression", w600, h300, col Pink, icon Plot, b RgbCol(0, 0, 0)
include \masm32\MasmBasic\Res\MbGui.asm
  ArrayRead x() As REAL8, 90    ; get x+y arrays
  ArrayRead y() As REAL8, 91    ; from resources
  Dim y2() As REAL8             ; this array will hold the regression line
  GetLinReg x(), y()            ; set the A and B coefficients
  For_ ecx=0 To eax-1           ; #elements in eax
        SetFloat <y2(ecx)>=LinRegY(x(ecx))      ; <destination> brackets needed here
Event Paint
  ArraySet MyRange() As REAL4=-0.5, 0.0, 3.2, 50.0
  ArrayPlot MyRange(XY), setrange
  ArrayPlot x():y(), RgbCol(255, 255, 255, 0), 5
  ArrayPlot x():y2(), RgbCol(160, 255, 0, 0), lines=3
  ArrayPlot exit, "Linear regression"
90 RCDATA       "RegressionX.dat"
91 RCDATA       "RegressionY.dat"

Source, exe and data files attached - building requires MasmBasic version 25 October 2020