masm2wasm translates a broad MASM32/Irvine32 classroom subset into standalone WASI WebAssembly console programs.
GitHub releases ship a single user-facing binary, also named masm2wasm, which can both build and run projects.
It is built around a MASM-like interpreter runtime:
internal\masmparses MASM/Irvine source into IR.vmprovides the register file, memory model, flags, stack, Irvine-style shims, and file I/O.cmd\vmtemplateis a generic WASI runtime module with an embedded payload slot.internal\generatorpatches that template with the translated program IR and emits a runnable.wasm.cmd\wasmrunremains as a thin developer wrapper around the same runtime path used bymasm2wasm run.
This design means:
- the generated executables are WebAssembly
- the translator itself is also compilable to WebAssembly
- the translator can run as WebAssembly without spawning a host compiler
This project is now aimed at real Irvine-style student console programs, not just the bundled Easy-MASM samples.
- Core MASM data/code parsing for console programs
- Procedures with
PROTO,PROC,USES,LOCAL, named parameters/locals, user-definedINVOKE, andret n - High-level MASM conditionals and loops:
.IF,.ELSEIF,.ELSE,.ENDIF,.WHILE,.ENDW,.REPEAT,.UNTIL - Common compile-time expressions and directives:
OFFSET,ADDR,TYPE,LENGTHOF/LENGTH,SIZEOF/SIZE, constants with=/EQU, practical text aliases withTEXTEQU, top-level compile-timeIF/ELSE/ENDIF, and data-generatingREPT/REPEAT, compile-timeWHILE,FOR, andFORC COMMENTblock handling plus common conditional-jump aliases such asjng,jnl,jnge,jnle,jna,jnb, andjnbe, along with MASM-style textual condition operators likeEQ,NE,LT,LE,GT,GE,AND,OR, andNOT- Classroom-style addressing like
[esi],[ebp+8],array[esi*4],[ebx + esi*TYPE table], and symbol-plus-displacement forms - Procedure tables and indirect calls through memory such as
call NEAR PTR [ebx+1] - Practical MASM data/type features including
LABEL, data/structALIGN, anonymous adjacent data/aggregate declarations,DB/DW/DD/DQaliases, user-definedSTRUCT/UNION, nested aggregate fields, aggregate initializers, and typed field access such asworker.Yearsand(Rectangle PTR [esi]).UpperLeft.Y - Interactive console programs using
ReadString,ReadInt,ReadDec,ReadHex,ReadChar, and line-bufferedReadKey - Irvine string routines, file I/O, and REP/string-instruction style algorithms
REAL4/REAL8data, practicalREAL10acceptance for load/store-style examples, a practical x87 stack model, and the floating-point chapter-12 style console examples- Practical C-style interop for common classroom examples using
PROTO C/PROC Cwithprintf,scanf,system,fopen, andfclose - Built-in Irvine macros plus a practical user-defined
MACRO/ENDMsubset
mov,leaadd,sub,adc,sbb,inc,dec,negxor,and,or,not,testcmpmul,imul,div,idiv,cdqcbw,cwd,cwdemovzx,movsx,xchgshl,shr,sar,sal,shld,shrd,rol,ror,rcl,rcrpush,poppushad,popad,pusha,popa,pushfd,popfd,pushf,popf,leave,entercld,std,clc,stc,cmcaaa,aas,daa,das,aad,aamlahf,sahflodsb,lodsw,lodsdstosb,stosw,stosdmovsb,movsw,movsdcmpsb,cmpsw,cmpsdscasb,scasw,scasdxlatrep,repe/repz,repne/repnzon supported string instructionscmovcc(all 16 condition codes:cmova,cmovae,cmovb,cmovbe,cmovc,cmove,cmovg,cmovge,cmovl,cmovle,cmovna,cmovnae,cmovnb,cmovnbe,cmovnc,cmovne,cmovng,cmovnge,cmovnl,cmovnle,cmovno,cmovnp,cmovns,cmovnz,cmovo,cmovp,cmovpe,cmovpo,cmovs,cmovz)setcc(all 16 condition codes:seta,setae,setb,setbe,setc,sete,setg,setge,setl,setle,setna,setnae,setnb,setnbe,setnc,setne,setng,setnge,setnl,setnle,setno,setnp,setns,setnz,seto,setp,setpe,setpo,sets,setz)bt,bts,btr,btc,bsf,bsrbswapxadd,cmpxchgfinit,fld,fld1,fldz,fildfldpi,fldl2e,fldl2t,fldln2,fldlg2fiadd,fisub,fisubr,fimul,fidiv,fidivrfst,fstp,fstcw,fstsw,fldcw,fnstsw,fist,fistpfadd,fsub,fsubr,fmul,fdiv,fdivrfaddp,fsubp,fsubrp,fmulp,fdivp,fdivrpfabs,fchs,fsqrt,f2xm1,fyl2x,frndint,ftstfsin,fcos,fsincos,fptan,fpatanfprem,fprem1,fscale,fxtractfcom,fcomi,fcomp,fcomip,fcomppfucom,fucomp,fucomi,fucomip,fucomppfxchfclex,fwait,fincstp,fdecstp,fnop,ffreefstenv,fnstenv,fldenvjmpje,jz,jne,jnzjl,jle,jg,jge,jng,jnl,jnge,jnlejb,jbe,ja,jae,jc,jnc,jnb,jna,jnbe,jnaejs,jns,jo,jnojcxz,jecxz,loop,loopz/loope,loopnz/loopnecall,ret,exitnop
WriteStringCrlfWriteIntWriteDecWriteCharWriteHexWriteHexBWriteBinWriteBinBDumpRegsDumpMemClrscrWaitMsgGotoxySetTextColorGetTextColorGetMaxXYDelayGetMsecondsGetCommandTailReadKeyReadKeyFlushGetLastErrorviaINVOKEMsgBoxMsgBoxAskIsDigitWriteStackFrameWriteStackFrameNameGetDateTime
ReadIntReadDecReadHexReadStringReadCharGetCharReadFloatParseInteger32ParseDecimal32
StrLengthStr_lengthStr_copyStr_compareStr_trimStr_ucase
ReadFloatWriteFloatShowFPUStack
RandomizeRandom32RandomRangeGetTickCountviaINVOKESleepviaINVOKE
GetProcessHeapviaINVOKEHeapAllocviaINVOKEHeapFreeviaINVOKEHeapCreateviaINVOKEHeapDestroyviaINVOKEHeapSizeviaINVOKE
CreateOutputFileOpenInputFileCloseFileReadFromFileWriteToFileWriteWindowsMsgCreateFileviaINVOKEReadFileviaINVOKEWriteFileviaINVOKECloseHandleviaINVOKESetFilePointerviaINVOKE
printfviacallandINVOKEscanfviacallandINVOKEsystemviacallandINVOKEfopenviacallandINVOKEfcloseviacallandINVOKEMessageBox/MessageBoxAviacallandINVOKEFormatMessage/FormatMessageAviacallandINVOKELocalFreeviacallandINVOKEGetCommandLineA/GetCommandLineviacallandINVOKEwsprintfA/wsprintfviacallandINVOKE
GetStdHandleGetConsoleModeSetConsoleModeSetConsoleTextAttributeSetConsoleOutputCPGetConsoleOutputCPSetConsoleCPGetConsoleCPWriteConsoleWriteConsoleOutputCharacterWriteConsoleOutputAttributeReadConsoleFlushConsoleInputBufferPeekConsoleInputReadConsoleInputGetNumberOfConsoleInputEventsGetKeyStateSetConsoleCursorPositionSetConsoleWindowInfoGetConsoleCursorInfoSetConsoleCursorInfoSetConsoleScreenBufferSizeGetConsoleScreenBufferInfoSetConsoleTitleGetLocalTimeGetSystemTimeExitProcessviaINVOKEandcall
mWritemWriteLnmWriteStringmWriteSpacemReadStringmGotoxymDumpmDumpMemmShowmShowRegisterexitStartup- user-defined
MACRO/ENDMwith positional parameters,:REQ,:=defaults, nested expansion,LOCALlabels, and bare-name /¶m/¶m&substitution - compile-time conditionals
IF,IFDEF,IFNDEF,IFB,IFNB,IFIDNI,ELSE, andENDIF - compile-time data-generation directives
REPT/REPEAT,WHILE,FOR, andFORC
This is not full ML.EXE/LINK.EXE compatibility yet.
- Full MASM compile-time macro language (
IFB,IFIDNI,EXITM, macro-time conditionals, advancedTEXTEQUmetaprogramming, and similar directives beyond the currently implementedREPT/REPEAT,WHILE,FOR, andFORCsubset) - Full Win32 API coverage
- Exact 80-bit
REAL10arithmetic fidelity; current support accepts/parsesREAL10and treats it as a practical float-backed approximation for the book's load/store examples - Full MASM type system, records, anonymous field promotion, and the more advanced structure/union directives beyond the currently supported classroom subset
- The broader typed pointer/cast surface used by the more advanced struct-heavy examples
ReadKeyworks, but in WASI environments it is line-buffered rather than true raw keyboard polling.- Cursor movement and colors are implemented with ANSI escape sequences; behavior depends on the host terminal.
- Console output-buffer APIs such as
WriteConsoleOutputCharacter/WriteConsoleOutputAttributeupdate a pragmatic sparse screen-buffer model rather than emulating the full Win32 console subsystem. - Console text output now honors a practical set of OEM/Windows code pages for
WriteChar,WriteString, andWriteConsole(including CP437, CP850, CP852, CP858, CP866, CP874, and Windows-1250 through Windows-1258), but it is still a terminal-facing compatibility layer rather than a full Windows console renderer. - The
printf/scanfcompatibility layer is intentionally pragmatic and currently targets the classroom-heavy subset (%s,%d/%i,%u,%x,%X,%c,%f,%lf) rather than full libc formatting/parsing. MsgBox,MsgBoxAsk, andMessageBoxare implemented as pragmatic console-visible shims: they print the caption/text to stdout and return classroom-useful button IDs, honoring simple buffered yes/no-style input when present and otherwise falling back to the dialog's default button.- Low memory is reserved so real data pointers no longer collide with
NULL, which keeps first-declared strings and buffers compatible with Win32/Irvine APIs that treat0as a null pointer. - Win32-style file APIs now keep a pragmatic
GetLastError/FormatMessagepath for common classroom failures such as missing files, invalid handles, permission errors, and basic seek failures rather than reproducing the entire Windows error surface. cmd\wasmrunnow supplies real wall-clock and monotonic time to generated WASI modules, so timing/date examples behave more like a native run.- Relative file paths resolve against the current directory exposed to the WASI runner.
- The latest chapter-10 through chapter-15 survey translated 60 of 66
.asmfiles directly; the remaining 6 are 64-bit listings, Visual C++-generated segment-heavy listings, or a 16-bit DOS sector reader outside the main MASM32/Irvine32 console target.
- The CLI version follows semantic versioning.
- The repository starts at
0.1.0. masm2wasm versionandmasm2wasm --versionshow the version embedded into the binary.- GitHub releases are created by the manually triggered workflow in
.github\workflows\release.yml.
The release workflow:
- validates the requested semantic version
- cross-compiles
masm2wasmfor Linux, Windows, and macOS onamd64andarm64, plus a standalone WASI WebAssembly build - stamps the release version, commit, and build date into each binary
- creates a new GitHub release and uploads the binaries plus
SHA256SUMS.txt
After you initialize the repository on GitHub, trigger it from Actions -> Release -> Run workflow.
Users only need the masm2wasm binary.
go build -o .\dist\masm2wasm.exe .\cmd\masm2wasmDo this after changing vm\ or cmd\vmtemplate\.
$env:GOOS='wasip1'
$env:GOARCH='wasm'
$env:CGO_ENABLED='0'
go build -trimpath -ldflags='-s -w' -o .\internal\generator\vmtemplate.wasm .\cmd\vmtemplate
Remove-Item Env:GOOS,Env:GOARCH,Env:CGO_ENABLED$env:GOOS='wasip1'
$env:GOARCH='wasm'
$env:CGO_ENABLED='0'
go build -trimpath -ldflags='-s -w' -o .\masm2wasm.wasm .\cmd\masm2wasm
Remove-Item Env:GOOS,Env:GOARCH,Env:CGO_ENABLEDmasm2wasm build [options] <source.asm>
masm2wasm run [options] <program.wasm|source.asm> [module args...]
masm2wasm version
If no subcommand is provided, masm2wasm <source.asm> still behaves like masm2wasm build <source.asm>.
.\masm2wasm.exe build .\program.asm
.\masm2wasm.exe build .\program.asm -o .\artifacts\program.wasm.\masm2wasm.exe run .\program.wasm
.\masm2wasm.exe run .\program.wasm -stdin "5`n"
.\masm2wasm.exe run .\program.wasm -stdin-file .\input.txt.\masm2wasm.exe run .\factorial.asm
.\masm2wasm.exe run .\factorial.asm -stdin "5`n"
.\masm2wasm.exe run .\factorial.asm -o .\factorial.wasm.\masm2wasm.exe version
.\masm2wasm.exe --versionThis is the wasm-hosted path. The translator reads MASM source from stdin and writes the produced .wasm to stdout.
.\masm2wasm.exe run `
.\masm2wasm.wasm `
-arg -input -arg - `
-arg -output -arg - `
-stdin-file .\program.asm `
-stdout-file .\helloworld.wasmThen run the generated program:
.\masm2wasm.exe run .\helloworld.wasmmasm2wasm run mounts the current host directory into the guest filesystem, so relative file paths like output.txt work when you launch the runner from the directory where you want the files to live.
Example:
.\masm2wasm.exe build .\somefile.asm -o .\somefile.wasm
.\masm2wasm.exe run .\somefile.wasmIf the program opens output.txt, it will create or read that file in the current directory.
The implementation has been exercised with:
- the Easy-MASM
source.asmsample - the Easy-MASM console samples for hello world, arithmetic, loops, arrays, input/output, and factorial
- Irvine example programs including
Params.asm,proc.asm,ArrayFill.asm,Flowchart.asm,Base-Index.asm,Mult.asm,Cmpsb.asm,Macro1.asm,Macro2.asm,Macro3.asm,RowSum.asm,floatTest32.asm,MixedMode.asm,FCompare.asm,Expr.asm,LoadAndStore.asm,LossOfPrecision.asm,IfStatements.asm,Struct2.asm,Union.asm,ShowTime.asm,Console1.asm,Console2.asm,ReadConsole.asm,TimingLoop.asm,Test_WriteStackFrame.asm,PeekInput.asm,Keybd.asm, andTestReadkey.asm - additional real-source translation checks for
Fibon.asm,List.asm,Repeat.asm,IndexOf_asm\indexof.asm, andDirectoryListing\asmMain.asm - additional Go tests covering procedures,
PROTO,USES,LOCAL, user-definedINVOKE, indexed addressing, high-level loops, compile-timeREPT/WHILE/FOR/FORC, string / REP instructions, user-defined macros, struct/union parsing, Win32-style console shims, text aliases, float/x87 execution, comment blocks, jump aliases, file I/O, and generated wasm execution through Wazero - release-oriented CLI checks covering
masm2wasm version,masm2wasm build,masm2wasm run <program.wasm>, and directmasm2wasm run <source.asm> - local release-style cross-compilation for Windows, Linux, and macOS on
amd64andarm64, including version-stamped binaries