; This is a mini-tutorial showing how to handle exceptions and runtime errors in Masm32 using MasmBasic.
;
; The include below adds the MasmBasic library to the Masm32 SDK. Most if not all examples in
; the folder
\masm32\examples work just fine if you replace the header lines or the default header
; (
include \masm32\include\masm32rt.inc ) with this single line:
include \masm32\MasmBasic\
MasmBasic.inc ;
download.data
dummy db "data lines before the Init statement are allowed", 0
; the next line initialises the library and the error handlers. Use instead of .code and start: label.
Init tc, con[/b]
; install the try/catch handler, output to console; see TryCa.,., dungu .,.,.,d at the end; the
con is optional; no arg or
box would show MessageBoxes for each error, while
key would wait for a keypress after each error.
; the
ErrLines macro adds some extra code in case you want to know which line triggered a runtime error. It is not needed for exceptions.
ErrLines ; blank or on means use them, off means don't; we have a suspicion that the next para might be somewhat illegal. Let's
try nonetheless:
Try ; set the start label
xor ecx, ecx
; you should go to jail for accessing address zero, but we'll be kind inc dword ptr [ecx] ; RichMasm will select this line after exiting the program Catch ; set the end label PrintLine "Incrementing (0) is illegal, but nobody can stop us!!", CrLf$
; so exceptions are no problem - what about runtime errors?
call CatchMyRunTimeError
; let's test the TryRTE macro, too; once again, an exception, but this time we add code that is triggered
only in case of an exception:
Try ; set the start label mov ecx, 4
.Repeat
Print Str$("44444444/%i = ", ecx)
mov eax, 44444444
cdq
; extend the sign of eax to edx ->edx=0 SetErrLine div ecx PrintLine Str$(eax)
dec ecx
.Until Sign?
Catch only ; "only" means skip this section if there are no problems mov eax, 31415926
; ... otherwise, change your results here... PrintLine "undefined"
; ... or issue a warning, etc. Finally ; needed if "only" was used above
.if
LastEx(code)
; we better give some extra info PrintLine Str$("\nOuch, we had an exception in source line %i at\nAddress\t",
LastEx(line)),
Hex$(
LastEx(addr)), CrLf$, "Code", Tb$,
Hex$(LastEx(code)), CrLf$
PrintLine "The OS reports:", CrLf$,
LastEx(info)
.else
PrintLine Err$()
; no exceptions, but maybe a Windows error? .endif
PrintLine CrLf$, "That was cute, now we crash the FPU:"
FpuSet MbNear64, exall
; set rounding=near, 64 bits precision, all flags set except precision flag; one more exception, now with coder-defined into:
Try "fdiv zero is not a good idea"
; you can pass your own message with try fldpi ; push 3.14 fldz ; push 0; the deb macro can display all kinds of numerical values (reg8/16/32, immediate constants, xmm regs, fpu regs etc
; you can use it with the console (deb 4), with messageboxes (deb 1-3) and even with logfiles (deb 5)
deb 4, "First, let's have a look at the FPU:", ST(0), ST(1)
PrintLine CrLf$, "And now the illegal
fdiv instruction:"
fdiv ; 3.1415/0 -> sets Z flag but does not yet trigger the exception nop
; line 46 nop
; line 47 fstp st ; triggers exception (note this is line 69, the OS rightly says the fault happened in line 66)
Catch only
PrintLine "LastEx= ", Tb$,
Hex$(
LastEx(code))
PrintLine "Address= ", Tb$,
Hex$(
LastEx(addr))
PrintLine Str$("Source line=\t%i",
LastEx(line))
.if
LastEx(user)
PrintLine "Your coder says ", eax
.endif
PrintLine CrLf$, "The OS reports:", CrLf$,
LastEx(info)
Finally ; this line will always be executed; OK, game over - the last exception will be fatal. All files opened with
Open "x", #1 etc will be closed before exiting.
Print CrLf$, "Let's try one more, outside the Try/Catch block:", CrLf$
mov ecx, 1234h
; you won't be allowed to write to that address inc dword ptr [ecx] ; this exception not handled by Try/Catch, so the program stops here
Inkey "Can you see this text?"
; no, you can't... we have a non-continuable exception before ;-)
Exit ; this won't be used, but the last crash will provide for an orderly Exit
CatchMyRunTimeError proc
TryRTE RecallFailed ; options: con
means errors to console, key
= con
+wait for a keypress; default is box
Let esi=
FileRead$("NoSuchFile.dat")
; trigger a runtime error
Recall "NoSuchFile.dat", MyRec
$() ; try the impossible... TryRTE off
Print Str$("%i strings read\n", eax
) ; in case Recall succeeds ;-) ; xor edx, edx ; if there is a chance that edx could equal the label, zero it before the labelRecallFailed: cmp edx, $
; simple error check .if Zero?
Print CrLf$, "There was a runtime error, check your code or your files", CrLf$, CrLf$
.else
PrintLine "Rec 0=", MyRec$(0)
.endif
ret
CatchMyRunTimeError endp
TryCa.,., dungu .,.,.,d ; activate the handler - must be the last instruction before "end start"
end start
MasmBasic's preferred editor RichMasm will select the source line that triggered the first exception, provided you use the
right assembler and linker as shown below:
OPT_Assembler mlv615
; only ml.exe 6.15 and JWasm work ok (ml 8+ don't do /Zd any more) OPT_DebugA /Zd
; generate info for mapinfo:lines OPT_DebugL /map /mapinfo:lines
; recent ml.exe and link.exe versions can't do that
OPT_Linker link
; you need the Masm32 version, i.e. the old 1998 link.exe OPT_Wait 0
; optional: don't wait for a key after exiting (i.e. you need an Inkey somewhere)And here is the complete output of the proggie. It is partly Italian because my NtDll.dll is Italian. If you want to see it in your language: source and executable are attached.
Incrementing (0) is illegal, but nobody can stop us
Could not open
NoSuchFile.dat
for Recall, FileRead$ etc.
There was a runtime error, check your code or your files
44444444/4 = 11111111
44444444/3 = 14814814
44444444/2 = 22222222
44444444/1 = 44444444
44444444/0 = undefined
Ouch, we had an exception in source line 36 at
Address 0040108D
Code C0000094
The OS reports:
{ERRORE DI EXCEPTION}
Divisione intera per zero.
EIP 0040108D
Code C0000094
That was cute, now we crash the FPU:
First, let's have a look at the FPU:
ST(0) 0.0
ST(1) 3.14159265358979324
And now the illegal fdiv instruction:
LastEx= C000008E
Address= 00401460
Source line= 63
Your coder says fdiv zero is not a good idea
The OS reports:
{ERRORE DI EXCEPTION}
Divisione a virgola mobile per zero.
EIP 00401460
Code C000008E
Let's try one more, outside the Try/Catch block:
L'istruzione a "00401696" ha fatto riferimento alla memoria a "00001234". La memoria non poteva essere "written".