EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

debug.htm
Debug
16bit runtime
32bit runtime
64bit runtime

This OS-independent macrolibrary can be included to programs written in EuroAssembler.

The library contains macroinstruction which helps to find bug in the program at run-time.


debug HEAD
Debug DumpAddress, Size=16, CallbackProc=DebugOutput, \
              Width=%^WIDTH, PRIV=%^PRIV, FPU=%^FPU, MMX=%^MMX, SIMD=%^SIMD

Debug is a macro for noninteractive debugging of EuroAssembler programs at run-time. It allows to display CPU state (contents of registers and flags) and memory contents when instruction pointer is at the position in program, where the macro was inserted. Investigated registers, flags and memory are not affected.

Disadvantage of this debugging method is that the macroinstruction Debug needs to be inserted beforehand into source code at places of interest (breakpoints) and the whole program recompiled and run.
It may be useful when more comfortable interactive debugger is not available or when it doesn't display the contents of all important registers.

Debug keywords Width=, Priv=, FPU=, MMX=, SIMD= specify which register family should be displayed. By default are those keywords copying EuroAssembler options specified in configuration file euroasm.ini or by directives PROGRAM WIDTH= and EUROASM PRIV=, FPU=, MMX=, SIMD= but this can be changed ad hoc with this macro keyword operand. For instance 64bit programs often have option EUROASM SIMD=On (which is required by WinABI) but if programmer is not interested in SIMD registers, invokation Debug SIMD=no will disable unnecessary display of this family.

When Width=16, Priv=no the macro code uses only 16bit registers and instructions, so it should be able to execute on old PC/XT with CPU=8086.

When ordinal parametr DumpAddress and its keyword parameter Size= is used, Debug also displays the contents of memory at specified address.

All working space is allocated on machine stack only. Using macro Debug requires at least 5 KB of stack space (9 KB in 64bit mode), no matter how many times is the macro expanded in program.
Default stack size in MZ programs is 8 KB, it should not be decreased when Debug is used.

Macro Debug is ignored when the option EUROASM DEBUG=Disabled . This allows to left the release version of original source program peppered with abandoned Debug statements for the possible future debugging.

Macro Debug does not depend on current program width and operating system because it does not write the formated output to any device. Debug uses callback function instead, which has to be defined in the debugged program, and which is responsible for the output presentation. Usually it only calls StdOutput to display the formated output on console, but it could log the output to a log file as well, use MessageBox or display it using BIOS INT 0x10 or direct video-RAM write when OS is not available.

Input
DumpAddress is optional parameter which specifies address of dumped memory. It can be immediate offset, name of memory variable, such as MyCounter:, or GP register, e.g. RSI. In 16bit mode this parameter can be prefixed with segment specification separated with colon :, for instance 0x0040:0x0060 or ES:MyVar.
Size=16 specifies size of dumped memory. This must be immediate number 1..256.
Width=%^WIDTH specifies the width of displayed registers. It may be 16,32 or 64, by default this operand copies program width. This should also be the width of CallbackProc.
PRIV=%^PRIV is boolean value which specifies if Debug will show the contents of 32bit Control and Debug registers. Those registers can be displayed only when program runs in Ring 0. CR8 is displayed only if Width=64.
FPU=%^FPU is boolean value which specifies if Debug will display the contents of registers ST*.
MMX=%^MMX is boolean value which specifies if Debug will display the contents of registers MM*.
SIMD=%^SIMD is boolean value which specifies if Debug will display the contents of registers ZMM/YMM/XMM*.
Mask registers K* and ZMM* are displayed only if SIMD=AVX512 and if CPU supports if, of course.
Registers YMM* are displayed only if SIMD=AVX and registers XMM* only if SIMD=SSE or higher.
CallbackProc=DebugOutput: is the name of procedure, which should display or log the plain-text output of macro Debug. It is invoked as a callback from macro Debug and it gets the formated text in the form of a single string pointed to by DS:rSI, whose size is in rCX.
When Width=16, CallbackProc should be declared as FAR regardless of memory model. It may clobber ES,DS and any GP register. Example of a simple output procedure in 16bit mode:
%IF %^Debug DebugOutput:PROC DIST=FAR ; Write string DS:SI,CX on console. StdOutput SI,Size=CX RETF ENDP DebugOutput: %ENDIF
When Width=32 or Width=64, DebugOutput is invoked as DIST=NEAR. The procedure may clobber rAX, rBX, rCX, rDX, rBP, rSI, rDI. Dump of privileged registers CR and DR will not be available ( N/A) in protected mode. Dump of SIMD registers will not be available when it is not supported by CPUID at run-time.
Example of a simple output procedure in 32bit mode:
%IF %^Debug DebugOutput:PROC ; Write string ESI,ECX on console. StdOutput ESI,Size=ECX RET ENDP DebugOutput: %ENDIF
Example of a simple output procedure in 64bit mode:
%IF %^Debug DebugOutput:PROC ; Write string RSI,RCX on console. StdOutput RSI,Size=RCX RET ENDP DebugOutput: %ENDIF
Instead of writing to console you may prefer to store formated ASCIIZ string rSI,rCX into a log file, line printer, serial port etc. When operating system is not available, DebugOutput should write to console using BiosAPI INT 0x10 or write characters directly to videoRAM.
Output
Inserting macro Debug does not disrupt current CPU state, all flags and registers are preserved. Their contents were formated and sent to CallbackProc.
Format

Contents of all registers is presented in hexadecimal notation, starting with the most significant byte. Line length of formated output will not exceed 78 characters. General-purpose registers and flags are displayed always, other families only when the corresponding option PRIV=, FPU=, MMX=, SIMD= is enabled.

Beside the standard register names the displayed output uses following abbreviations:

Flag names
ShortcutMaskValue
NT4000hNested Task flag
PL3000hI/O Privilege Level 0..3
OF0800hOverflow Flag
DF0400hDirection Flag
IF0200hInterrupt enable Flag
TF0100hTrap Flag
SF0080hSign Flag
ZF0040hZero Flag
AF0010hAuxiliary carry Flag
PF0004hParity Flag
CF0001hCarry Flag
FPU environment registers
ShortcutOffsetValue
Width=16Width=32|64
FCW00FPU Control Word
FSW24FPU Status Word
FTW48FPU Tag Word
FIO612FPU Instruction pointer Offset
FIS816FPU Instruction pointer Selector
FDO1020FPU Data pointer Offset
FDS1224FPU Data pointer Selector
FPU register contents-tags and spec.values
ShortcutTagValue
VAL00bValid FP value
NUL01b#ZERO FP value
NAN10b#NAN or #INF or denormal FP special value
EMP11bEmpty, STx was not used since FINIT.
FPU and SIMD exceptions
ShortcutOffsetValue
PREC100000bPrecision lost
UNF 010000bUnderFlow
OVF 001000bOverFlow
DIV0000100bDivide by 0
DEN 000010bDenormalized operand
INV 000001bInvalid operation
NONE000000bNo exception pending
Debug layout
111111111122222222223333333333444444444455555555556666666666777777777 123456789012345678901234567890123456789012345678901234567890123456789012345678 Layout of GPR Debug output when Width=16: #### Debug at "filename.ext"{1234} CS=0000 DS=0000 ES=0000 SS=0000 GPR: FL=0000 NT=0 PL=0 OF=0 DF=0 IF=0 TF=0 SF=0 ZF=0 AF=0 PF=0 CF=0 AX=0000 CX=0000 DX=0000 BX=0000 SP=0000 BP=0000 SI=0000 DI=0000 IP=0000 Layout of GPR Debug output when Width=32: #### Debug at "filename.ext"{1234) CS=0000 DS=0000 ES=0000 SS=0000 GPR: FL=0000_0000 NT=0 PL=0 OF=0 DF=0 IF=0 TF=0 SF=0 ZF=0 AF=0 PF=0 CF=0 EAX=0000_0000 ECX=0000_0000 EDX=0000_0000 EBX=0000_0000 FS=0000 GS=0000 ESP=0000_0000 EBP=0000_0000 ESI=0000_0000 EDI=0000_0000 EIP=0000_0000 Layout of GPR Debug output when Width=64: #### Debug at "filename.ext"{1234} FS=0000 GS=0000 GPR: FL=0000_0000_0000_0000 OF=0 DF=0 IF=0 TF=0 SF=0 ZF=0 AF=0 PF=0 CF=0 RAX=0000_0000_0000_0000 RCX=0000_0000_0000_0000 RDX=0000_0000_0000_0000 RBX=0000_0000_0000_0000 RSP=0000_0000_0000_0000 RBP=0000_0000_0000_0000 RSI=0000_0000_0000_0000 RDI=0000_0000_0000_0000 RIP=0000_0000_0000_0000 R8=0000_0000_0000_0000 R9=0000_0000_0000_0000 R10=0000_0000_0000_0000 R11=0000_0000_0000_0000 R12=0000_0000_0000_0000 R13=0000_0000_0000_0000 R14=0000_0000_0000_0000 R15=0000_0000_0000_0000 Layout of CR+DR Debug output when PRIV=Enabled: PRIV: DR0=0000_0000 DR3=0000_0000 CR0=0000_0000 CR4=0000_0000 DR1=0000_0000 DR6=0000_0000 CR2=0000_0000 CR8=0000_0000 DR2=0000_0000 DR7=0000_0000 CR3=0000_0000 Layout of FPU Debug output when FPU=Enabled, Width=16: FPU: FCW=0000 FSW=0000 FTW=0000 FIO=0000 FIS=0000 FDO=0000 FDS=0000 C0(CF)=0 C1=0 C2(PF)=0 C3(ZF)=0 TOP=0 ROUND=NEAR PREC=TBYTE EXC=NONE ST0=EMP:0000_0000_0000_0000_0000 ST4=EMP:0000_0000_0000_0000_0000 ST1=EMP:0000_0000_0000_0000_0000 ST5=EMP:0000_0000_0000_0000_0000 ST2=EMP:0000_0000_0000_0000_0000 ST6=EMP:0000_0000_0000_0000_0000 ST3=EMP:0000_0000_0000_0000_0000 ST7=EMP:0000_0000_0000_0000_0000 Layout of FPU Debug output when FPU=Enabled, Width=32|64: FPU: FCW=0000_0000 FSW=0000_0000 FTW=0000_0000 FIO=0000_0000 FIS=0000_0000 FDO=0000_0000 FDS=0000_0000 C0(CF)=0 C1=0 C2(PF)=0 C3(ZF)=0 TOP=0 ROUND=NEAR PREC=TBYTE EXC=NONE ST0=EMP:0000_0000_0000_0000_0000 ST4=EMP:0000_0000_0000_0000_0000 ST1=EMP:0000_0000_0000_0000_0000 ST5=EMP:0000_0000_0000_0000_0000 ST2=EMP:0000_0000_0000_0000_0000 ST6=EMP:0000_0000_0000_0000_0000 ST3=EMP:0000_0000_0000_0000_0000 ST7=EMP:0000_0000_0000_0000_0000 Layout of MMX Debug output when MMX=Enabled: MMX: MM0=0000_0000_0000_0000 MM4=0000_0000_0000_0000 MM1=0000_0000_0000_0000 MM5=0000_0000_0000_0000 MM2=0000_0000_0000_0000 MM6=0000_0000_0000_0000 MM3=0000_0000_0000_0000 MM7=0000_0000_0000_0000 Layout of SIMD Debug output when SIMD=SSE SSE: MXCSR=0000_0000 FZ=0 DZ=0 ROUND=NEAR EXC=NONE XMM0=00000000_00000000_00000000_00000000 XMM1=00000000_00000000_00000000_00000000 XMM2=00000000_00000000_00000000_00000000 XMM3=00000000_00000000_00000000_00000000 XMM4=00000000_00000000_00000000_00000000 XMM5=00000000_00000000_00000000_00000000 XMM6=00000000_00000000_00000000_00000000 XMM7=00000000_00000000_00000000_00000000 Layout of SIMD Debug output when SIMD=AVX, Width=16|32 AVX: MXCSR=0000_0000 FZ=0 DZ=0 ROUND=NEAR EXC=NONE YMM0=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM1=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM2=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM3=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM4=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM5=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM6=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM7=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 Layout of SIMD Debug output when SIMD=AVX, Width=64 AVX: MXCSR=0000_0000 FZ=0 DZ=0 ROUND=NEAR EXC=NONE YMM0=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM1=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM2=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM3=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM4=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM5=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM6=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM7=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM8=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM9=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM10=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM11=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM12=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM13=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM14=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 YMM15=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 Layout of SIMD Debug output when SIMD=AVX512, Width=64 AVX512: MXCSR=0000_0000 FZ=0 DZ=0 ROUND=NEAR EXC=NONE K0=00000000_00000000 K4=00000000_00000000 K1=00000000_00000000 K5=00000000_00000000 K2=00000000_00000000 K6=00000000_00000000 K3=00000000_00000000 K7=00000000_00000000 ZMM0=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM0=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM1=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM1=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM2=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM2=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM3=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM3=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM4=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM4=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM5=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM5=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM6=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM6=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM7=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM7=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM8=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM8=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM9=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM9=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM10=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM10=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM11=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM11=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM12=00000000_00000000_00000000_00000000_00000000_00000000_00000100_00000000_ YMM12=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM13=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM13=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM14=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM14=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM15=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM15=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM16=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM16=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM17=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM17=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM18=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM18=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM19=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM19=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM20=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM20=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM21=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM21=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM22=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM22=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM23=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM23=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM24=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM24=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM25=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM25=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM26=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM26=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM27=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM27=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM28=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM28=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM29=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM29=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM30=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM30=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 ZMM31=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_ YMM31=00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 Layout of hexadecimal dump when Width=16, Size=16: Dump 16 bytes at 0000:0000: 0000:0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Layout of hexadecimal dump when Width=32|64, Size=16: Dump 16 bytes at 0000_0000_0000_0000: 0000_0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Debug stores on machine stack the source filename and line number of statement where the macro is inserted. All PUSH instructions must be assembled with known size in order that Debug could calculate rIP value at the macro entry. That is why those instructions have modifier PUSH IMM=.
Stack frame of macro Debug Width=16 Width=32 Width=64 ┌─────────┐ ┐ ┌───────────┐ ┌─────────────┐ BP+50│ 11..12 │ │ EBP+64│ 09..12 │ RBP+120│ 09..12 │ ├─────────┤ │ ├───────────┤ ├─────────────┤ BP+48│ 09..10 │ │ EBP+60│ 05..08 │ RBP+112│ 05..08 │ ├─────────┤ │ ├───────────┤ ├─────────────┤ BP+46│ 07..08 │ │Source EBP+56│ 01..04 │ RBP+104│ 01..04 │ ├─────────┤ │ name ├───────────┤ ├─────────────┤ BP+44│ 05..06 │ │ EBP+52│SourceLine │ RBP+096│ SourceLine │ ├─────────┤ │ ├───────────┤ ├─────────────┤ BP+42│ 03..04 │ │ EBP+48│Dump addr │ RBP+088│ Dump addr │ ├─────────┤ │ ├───────────┤ ├─────────────┤ BP+40│ 01..02 │ ┘ EBP+44│DebugStatus│ RBP+080│ DebugStatus │ ├─────────┤ ┐ ├───────────┤ ├─────────────┤ BP+38│ Line/64 │ │Source EBP+40│Callback │ RBP+072│ Callback │ ├─────────┤ │ line ├───────────┤ ├─────────────┤ BP+36│ Line\64 │ │ EBP+36│ RT return │ RBP+064│ RT return │ ├─────────┤ ┘ ├───────────┤ ├─────────────┤ BP+34│PARA#Dump│ ┐ EBP+32│ Eflags │ RBP+056│ Rflags │ ├─────────┤ │Dump ├───────────┤ ├─────────────┤ BP+32│OFFS#Dump│ │address EBP+28│ EAX │ RBP+048│ RAX │ ├─────────┤ ┘ ├───────────┤ ├─────────────┤ BP+30│DebugFg │ status EBP+24│ ECX │ RBP+040│ RCX │ ├─────────┤ ┐ ├───────────┤ ├─────────────┤ BP+28│PARA# CB │ │Callback EBP+20│ EDX │ RBP+032│ RDX │ ├─────────┤ │ address ├───────────┤ ├─────────────┤ BP+26│OFFS# CB │ ┘ EBP+16│ EBX │ RBP+024│ RBX │ ├─────────┤ ┐ ├───────────┤ ├─────────────┤ BP+24│ RET CS │ │runtime EBP+12│ ESP │ RBP+016│ RBP │ ├─────────┤ │ return ├───────────┤ ├─────────────┤ BP+22│ RET IP │ │ EBP+08│ EBP │ RBP+008│ RSI │ ├─────────┤ ┘ ├───────────┤ ├─────────────┤ BP+20│ Flags │ CPU flags EBP+04│ ESI │ RBP+000│ RDI │ ├─────────┤ ├───────────┤ ├─────────────┤ BP+18│ DS │ EBP+00│ EDI │ │ │ ├─────────┤ ├───────────┤ │ %DebugWA │ BP+16│ ES │ │ │ │ workarea │ ├─────────┤ ┐ │ %DebugWA │ RBP-128│ │ BP+14│ AX │ │ │ workarea │ ├─────────────┤ ├─────────┤ │ EBP-128│ │ │ │ BP+12│ CX │ │ ├───────────┤ │ │ ├─────────┤ │ │ │ │ │ BP+10│ DX │ │ │ │ │ │ ├─────────┤ │ │ │ │ │ BP+08│ BX │ │PUSHAW │ │ │ │ ├─────────┤ │ │ │ │ │ BP+06│ SP │ │ │ │ │ │ ├─────────┤ │ │ │ │ │ BP+04│ BP │ │ │ │ │ │ ├─────────┤ │ │ │ │ │ BP+02│ SI │ │ │ Output │ │ Output │ ├─────────┤ │ │ printarea │ │ printarea │ BP+00│ DI │ │ │ │ │ │ ├─────────┤ ┘ │ │ │ │ │ │ │ │ │ │ │%DebugWA │ │ │ │ │ │ workarea│ │ │ │ │ │ │ │ │ │ │ BP-128│ │ │ │ │ │ ├─────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Output │ │ │ │ │ │printarea│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────┘ └───────────┘ └─────────────┘
Debug %MACRO DumpAddress, Size=16, CallbackProc=DebugOutput::, Width=%^WIDTH, \
             PRIV=%^PRIV, FPU=%^FPU, MMX=%^MMX, SIMD=%^SIMD
enabled %IF %^DEBUG  ; Ignore this macro when debugging is not enabled by EUROASM DEBUG=ON.

%comment ; todo: uncomment after release

defined  %IF %^PASS > 1 && TYPE#(%CallbackProc) = '?'
           %ERROR Debug callback procedure "%CallbackProc" was not defined.
           %EXITMACRO Debug
         %ENDIF defined
%endcomment

         EUROASM PUSH,NOWARN=2340; Do not complain of missing €ASM CPU= | SIMD= options.
%DebugPRIV   %SETA 0x01          ; Declare names of Debug internal status flags.
%DebugFPU    %SETA 0x02
%DebugMMX    %SETA 0x04
%DebugSSE    %SETA 0x08
%DebugAVX    %SETA 0x10
%DebugAVX512 %SETA 0x20
%DebugDump   %SETA 0x80
%DumpSize    %SET  %Size
    %IF TYPE# %DumpSize != 'N'
      %ERROR ID=5711, 'Required dump Size= must be specified as a plain number.'
      %DumpSize %SETA 16
    %ENDIF
    %IF %DumpSize #<= 0          ;>Saturate to minimum 1.
      %DumpSize %SETA 1
    %ENDIF
    %IF %DumpSize #>= 256        ; Saturate to maximum 256.
       %DumpSize %SETA 256
    %ENDIF ; Dump size 1..256 will be decremented and encoded in %DebugFg bits 8..15.
%DebugFg %SETA (%DumpSize-1)<<8  ;>Decrement and encode in high byte.
             %IF %#              ; Is ordinal parameter (dump address) present?
%DebugFg       %SETA %DebugFg | %DebugDump
%DumpAddr      %SET %DumpAddress
             %ELSE
%DumpAddr      %SET 0
             %ENDIF
             %IF %PRIV           ; Is display of privileged registers DR*,CR* required?
%DebugFg       %SETA %DebugFg | %DebugPRIV
             %ENDIF
             %IF %FPU            ; Is display of floating-point registers ST* required?
%DebugFg       %SETA %DebugFg | %DebugFPU
             %ENDIF
             %IF %MMX            ; Is display of multimedia registers MM* required?
%DebugFg       %SETA %DebugFg | %DebugMMX
             %ENDIF
             %IF "%SIMD" == "AVX512"    ; Is display of SIMD registers ZMM* required?
%DebugFg       %SETA %DebugFg | %DebugAVX512 | %DebugAVX | %DebugSSE
             %ENDIF
             %IF "%SIMD[1..3]" == "AVX" ; Is display of SIMD registers YMM* required?
%DebugFg       %SETA %DebugFg | %DebugAVX | %DebugSSE
             %ENDIF
             %IF "%SIMD[1..3]" == "SSE" ; Is display of SIMD registers XMM* required?
%DebugFg       %SETA %DebugFg | %DebugSSE
             %ENDIF
%DebugSN %SET %^SourceName%^SourceExt   ; Debugged source name will be passed to @RT.
%DebugSS %SETS %DebugSN                 ; Number of characters in the source file name.
TooLong? %IF %DebugSS >12               ; Name longer than 8.3 will be shortened.
%DebugSN   %SET %DebugSN[1..5]~~%DebugSN[%&-4..%&] ; Replace the middle with two tildas.
         %ENDIF TooLong?
Debug16@RT
Debug16  %IF %Width=16                  ; 16bit mode.
n         %FOR 12..2, STEP= -2
            %IF "%DebugSN[%n-1..%n]" === ""
              PUSHW 0, IMM=WORD
            %ELSE
              PUSHW "%DebugSN[%n-1..%n]",IMM=WORD
            %ENDIF
          %ENDFOR n
          PUSHW %^SourceLine / 64K,   IMM=WORD
          PUSHW %^SourceLine \ 64K,   IMM=WORD
          %IF %#
    %ColonPos %SETA 0      ; Nonzero if : is present in DumpAddress.
    %nrChars  %SETS %DumpAddress ; Number of characters in %1.
           n %FOR 1..%nrChars
               %IF "%DumpAddress[%n]" === ":"
                %ColonPos %SETA %n
                %EXITFOR n
              %ENDIF
             %ENDFOR n
             %IF %ColonPos ; Colon is present in DumpAddress notation, e.g. ES:123 or 123:456
               %IF TYPE# %DumpAddress[1..%ColonPos-1] = 'N'
                 PUSHW %DumpAddress[1..%ColonPos-1], IMM=WORD ; The segment part as immediate number.
               %ELSE
                 NOP
                 NOP                                          ; Following PUSH size is padded to 3.
                 PUSHW %DumpAddress[1..%ColonPos-1]           ; The segment part is segm.register.
               %ENDIF
               PUSHW %DumpAddress[%ColonPos+1..%&],IMM=WORD ; The offset part.
             %ELSE         ; DumpAddress is a symbol, e.g. aBuffer
               PUSHW PARA# %DumpAddress,  IMM=WORD
               PUSHW OFFSET# %DumpAddress,IMM=WORD
             %ENDIF
          %ELSE            ; No dump is required.
            PUSHW 0,0, IMM=WORD
          %ENDIF
          PUSHW %DebugFg, IMM=WORD
          %IF "%^Format"=="MZ"
            PUSHW PARA#   %CallbackProc,IMM=WORD
            PUSHW OFFSET# %CallbackProc,IMM=WORD
            CALLF Debug16@RT::
          %ELSE  ; Loader of format COM doesn't update image base, cannot CALLF.
            NOP
            NOP
            PUSH CS ; Instruction size was padded to 3, same as PUSH IMM=WORD.
            PUSHW OFFSET# %CallbackProc,IMM=WORD
            PUSH CS            ; FAR runtime procedure is called as NEAR in COM program.
            NOP
            CALLN Debug16@RT:: ; PUSH CS+CALLN size was padded to 5, same as CALLF.
          %ENDIF               ; Runtime procedure is emitted once in a program
Debug16@RT::PROC1              ;  and called from each Debug expansion.
     PUSHF
     PUSH DS,ES
     PUSHAW
     MOV BP,SP
     CLD
%DebugSt %SET BP+30        ; Macro Debug internal status flags.
%DebugWA %SET BP-128       ; Working area 128 bytes for dump of registers and FPU environment.
     SUB SP,128+4400       ; Allocate room on stack for %DebugWA and 55 lines of formated output.
     MOV DI,SP             ; The first byte of formated output.
     MOV AX,SS             ; Use small memory model in 16bit Debug runtime.
     MOV DS,AX
     MOV ES,AX
     CALL .Eol:
     MOV AX,'##'           ; Display the marker and source position.
     STOSW
     STOSW
     MOV AX,' D'
     STOSW
     MOV AX,'eb'
     STOSW
     MOV AX,'ug'
     STOSW
     MOV AX,' a'
     STOSW
     MOV AX,'t '
     STOSW
     MOV AL,'"'
     STOSB
     LEA SI,[BP+40]        ; Display %^SourceName.
     MOV CX,12
.G1: LODSB
     STOSB
     CMP AL,0
     LOOPNZ .G1:
     JNZ .G2:
     DEC DI                ; Omit the terminating 0.
.G2: MOV AX,'"{'
     STOSW
     MOV CX,[BP+36]        ; Display %^SourceLine.
     MOV DX,[BP+38]        ; DX:CX is binary line number.
     CALL .StoDD:          ; Conversion to decimal number at ES:DI {simplified StoDD).
     MOV AL,'}'
     STOSB
     MOV CX,SP
     ADD CX,41             ; Continue at 41st column of output line.
     SUB CX,DI
     JBE .G4:
     MOV AL,' '
     REP STOSB
.G4: MOV AX,'CS'           ; Display segment registers.
     XOR DX,DX
     LEA SI,[BP+24+1]
     CALL .r16:
     MOV AX,'DS'
     LEA SI,[BP+18+1]
     CALL .r16:
     MOV AX,'ES'
     CALL .r16:
     MOV AX,'SS'
     MOV BX,SS
     MOV [%DebugWA],BX
     LEA SI,[%DebugWA+1]
     CALL .r16:
     CALL .Eol1:
     MOV AX,'GP'
     STOSW
     MOV AX,'R:'
     STOSW
     MOV AL,' '
     STOSB
     MOV AX,'FL'
     LEA SI,[BP+20+1]      ; Display flags.
     CALL .r16:
     MOV AH,5
     CALL .AHsp:
     MOV AX,'NT'
     MOV CX,0x4000
     CALL .Flag:           ; Display IOPL.
     MOV AX,'PL'
     STOSW
     MOV AL,'='
     STOSB
     MOV AX,'0 '
     MOV CX,[BP+20]
     SHR CX,12
     AND CL,11b
     ADD AL,CL
     STOSW
     MOV AX,'OF'
     MOV CX,0x0800
     CALL .Flag:
     MOV AX,'DF'
     SHR CX,1
     CALL .Flag:
     MOV AX,'IF'
     SHR CX,1
     CALL .Flag:
     MOV AX,'TF'
     SHR CX,1
     CALL .Flag:
     MOV AX,'SF'
     SHR CX,1
     CALL .Flag:
     MOV AX,'ZF'
     SHR CX,1
     CALL .Flag:
     MOV AX,'AF'
     MOV CX,0x0010
     CALL .Flag:
     MOV AX,'PF'
     MOV CX,0x0004
     CALL .Flag:
     MOV AX,'CF'
     MOV CX,0x0001
     CALL .Flag:
     CALL .Eol1:
     MOV AL,' '                ; Display general-purpose registers.
     STOSB
     MOV AX,'AX'
     XOR DX,DX
     LEA SI,[BP+14+1]
     CALL .r16:
     MOV AX,'CX'
     CALL .r16:
     MOV AX,'DX'
     CALL .r16:
     MOV AX,'BX'
     CALL .r16:
     LEA AX,[BP+52]            ; Original SP.
     MOV [%DebugWA],AX
     LEA SI,[%DebugWA+1]
     MOV AX,'SP'
     CALL .r16:
     MOV AX,'BP'
     LEA SI,[BP+4+1]
     CALL .r16:
     MOV AX,'SI'
     CALL .r16:
     MOV AX,'DI'
     CALL .r16:
     MOV AX,[BP+22]
     SUB AX,13*3+5    ; Original IP was 13 instructions PUSHW IMM=W and CALLF above return.
     MOV [%DebugWA],AX
     LEA SI,[%DebugWA+1]
     MOV AX,'IP'
     CALL .r16:
     CALL .Eol1:

.PRIV:TESTW [%DebugSt],%DebugPRIV
     JZ .FPU:
     CALL .TestPRIV:
     JC .FPU:
     MOV AX,'PR'
     STOSW
     MOV AX,'IV'
     STOSW
     MOV AL,':'
     STOSB
     CALL .Test386:
     JC .FPU:
     MOV EAX,DR0                 ; Display privileged registers CR,DR. Expects real/V8086 mode.
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR3
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'3'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR0
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'CR'
     MOV DL,'0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,CR4
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'CR'
     MOV DL,'4'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV EAX,DR1
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'1'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR6
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'6'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR2
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'CR'
     MOV DL,'2'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV EAX,DR2
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'2'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR7
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'DR'
     MOV DL,'7'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR3
     MOV [%DebugWA],EAX
     LEA SI,[%DebugWA+3]
     MOV AX,'CR'
     MOV DL,'3'
     CALL .r32:
     CALL .Eol1:

.FPU:TESTW [%DebugSt],%DebugFPU
     JZ .MMX:
     MOV AX,'FP'             ; Display FPU in 16bit mode.
     STOSW
     MOV AX,'U:'
     STOSW
     MOV AX,'  '
     STOSW
     STOSB
.F1: FNSAVE [%DebugWA]       ; Area for FPU registers and FPU environment.
     MOV AX,'FC'             ; Debug FPU environment as words.
     MOV DL,'W'
     LEA SI,[%DebugWA+00+1]
     CALL .r16:
     MOV AX,'FS'
     LEA SI,[%DebugWA+02+1]
     CALL .r16:
     MOV AX,'FT'
     LEA SI,[%DebugWA+04+1]
     CALL .r16:
     MOV AX,'FI'
     MOV DL,'O'
     LEA SI,[%DebugWA+06+1]
     CALL .r16:
     MOV AX,'FI'
     MOV DL,'S'
     LEA SI,[%DebugWA+08+1]
     CALL .r16:
     MOV AX,'FD'
     MOV DL,'O'
     LEA SI,[%DebugWA+10+1]
     CALL .r16:
     MOV AX,'FD'
     MOV DL,'S'
     LEA SI,[%DebugWA+12+1]
     CALL .r16:
     CALL .Eol1:
     MOV CX,[%DebugWA+2]         ; Display FPU environment as bitfields.
     MOV AL,' '
     STOSB
     MOV AX,'C0'
     STOSW
     MOV AX,'(C'
     STOSW
     MOV AX,'F)'
     STOSW
     MOV AX,'=0'
     SHR CH,1
     ADC AH,0
     STOSW
     MOV AX,' C'
     STOSW
     MOV AX,'1='
     STOSW
     MOV AX,'0 '
     SHR CH,1
     ADC AL,0
     STOSW
     MOV AX,'C2'
     STOSW
     MOV AX,'(P'
     STOSW
     MOV AX,'F)'
     STOSW
     MOV AX,'=0'
     SHR CH,1
     ADC AH,0
     STOSW
     MOV AX,' C'
     STOSW
     MOV AX,'3('
     STOSW
     MOV AX,'ZF'
     STOSW
     MOV AX,')='
     STOSW
     MOV AX,'0 '
     MOV DL,CH                 ; Save TOP to DL, bits 0..2.
     SHR CH,4
     ADC AL,0
     STOSW
     MOV AX,'TO'
     STOSW
     MOV AX,'P='
     STOSW
     AND DL,111b
     MOV AX,'0 '
     OR AL,DL
     STOSW
     MOV CL,[%DebugWA+1]       ; FCW MSB.
     SHR CL,2
     CALL .ROUND:
     MOV AX,' P'
     STOSW
     MOV AX,'RE'
     STOSW
     MOV AX,'C='
     STOSW
     MOV CL,[%DebugWA+1]        ; FCW MSB.
     MOV AX,'DW'
     MOV DX,'OR'
     MOV BX,'D '
     AND CX,11b
     JZ .F4:
     DEC CX
     MOV BX,'? '
     JZ .F5:
     DEC CX
     MOV AL,'Q'
     JZ .F4:
     MOV AX,'TB'
     MOV DX,'YT'
     MOV BX,'E '
.F4: STOSW
     MOV AX,DX
     STOSW
.F5: XCHG AX,BX
     STOSW
     MOV CL,[%DebugWA+2]     ; FSW LSB.
     CALL .EXC:
     CALL .Eol:              ; Display 8 FPU registers.
     MOV DX,[%DebugWA+4]     ; FPU Tag word, each two bits specify ST property.
     MOV CL,[%DebugWA+3]     ; FPU Status word MSB.
     AND CL,00111000b        ; Which register is currently ST0 (top of FPU stack).
     SHR CL,2
     ROR DX,CL               ; Property of ST0 is now in two LSbits of DX.
     SUB CX,CX               ; CH=ordinal of ST register (0..7).
.F6: MOV AL,' '
     STOSB
     CALL .r80:              ; Display the property and contents of one FPU register.
     MOV AH,7
     CALL .AHsp:
     ADD CH,4
     ROR DX,4*2
     CALL .r80:
     CALL .Eol:
     SUB CH,3
     ROL DX,3*2
     CMP CH,4
     JNE .F6:
     FRSTOR [%DebugWA]

.MMX:TESTW [%DebugSt],%DebugMMX
     JZ .AVX512:
     MOV AX,'MM'
     STOSW
     MOV AX,'X:'
     STOSW
     CALL .TestMMX:
     JC  .AVX512:
.M1: MOV AH,6                             ; Display MMX.
     CALL .AHsp:
m    %FOR 0..3
       MOV DL,%m               ; %m=MMx register ordinal, left column.
       MOVD [%DebugWA+0],MM%m
       %m4 %SETA %m+4          ; %m4=MMx register ordinal, right column.
       MOVD [%DebugWA+8],MM%m4
       CALL .R2MM:             ; Debug two MMx registers.
     %ENDFOR m
     SUB DI,10

.AVX512:
      TESTW [%DebugSt],%DebugAVX512
      JZ .AVX:
      MOV AX,'AV'
      STOSW
      MOV AX,'X5'
      STOSW
      MOV AX,'12'
      STOSW
      MOV AL,':'
      STOSB
      CALL .TestAVX512:
      JC  .AVX:
      CALL .MXCSR:
k     %FOR 0..3
        MOV DL,%k
        KMOVQ [%DebugWA+0],K%k
        %k4 %SETA %k+4
        KMOVQ [%DebugWA+8],K%k4
        CALL .R2K:
      %ENDFOR k
z     %FOR 0..7
        VMOVUPD [%DebugWA],ZMM%z
        MOV DL,%z
        CALL .ZMM:
      %ENDFOR z
      JMP .Dump:

.AVX: TESTW [%DebugSt],%DebugAVX
      JZ .SSE:
      MOV AX,'AV'
      STOSW
      MOV AX,'X:'
      STOSW
      MOV AX,'  '
      STOSW
      STOSB
      CALL .TestAVX:
      JC  .SSE:
      CALL .MXCSR:                       ; Display AVX.
m     %FOR 0..7
        VMOVUPS [%DebugWA],YMM%m
        MOV CL,%m
        CALL .YMM:
      %ENDFOR m
      JMP .Dump:

.SSE: TESTW [%DebugSt],%DebugSSE
      JZ .Dump:
      MOV AX,'SS'
      STOSW
      MOV AX,'E:'
      STOSW
      MOV AX,'  '
      STOSW
      STOSB
      CALL .TestSSE:
      JC  .End:
      MOV AL,' '
      STOSB
      CALL .MXCSR:                         ; Display SSE.
m     %FOR 0..7
        MOVDQU [%DebugWA],XMM%m
        MOV CL,%m
        CALL .XMM:
      %ENDFOR m
    ; JMP .Dump:

.Dump:TESTW [%DebugSt],%DebugDump
      JZ .End:
      MOV AX,'Du'
      STOSW
      MOV AX,'mp'
      STOSW
      MOV AL,' '
      STOSB
      MOV AX,[%DebugSt]
      MOV CL,8
      SHR AX,CL
      INC AX
      MOV CX,AX
      SUB DX,DX
      CALL .StoDD:          ; Output decimal number DX:CX (netto dump size).
      ADD CX,[BP+32]        ; End of dumped area
      MOV [%DebugSt],CX     ; %DebugSt is now used as the end offset of dumped area.
      MOV AX,' b'
      STOSW
      MOV AX,'yt'
      STOSW
      MOV AX,'es'
      STOSW
      MOV AX,' a'
      STOSW
      MOV AX,'t '
      STOSW
      LEA SI,[BP+34+1]
      CALL .StoSI2:
      MOV AL,':'
      STOSB
      CALL .StoSI2:
      CALL .Eol:
      MOV DX,[BP+32]
      MOV AX,0xFFF0
      AND AX,DX
      MOV [BP+32],AX
.D2:  ; Print dumped address.
      LEA SI,[BP+34+1]      ; Start offset of dumped area rounded down.
      CALL .StoSI2:
      MOV AL,':'
      STOSB
      CALL .StoSI2:
      MOV AX,': '
      STOSW
      LEA BX,[DI+60-12]     ; SS:BX points to the first char-dump column.
      MOV CX,16             ; Number of bytes dumped in one line.
.D3:  CMP DX,[BP+32]
      JA .D5:
      CMP DX,[%DebugSt]     ; Test if above dumped area.
      JAE .D5:
      PUSH DS
        LDS SI,[BP+32]      ; Offset DX is withing dumped area.
        LODSB               ; One dumped character.
      POP DS
      MOV AH,AL
      CMP AL,' '
      JNB .D4:
      MOV AL,'.'            ; Replace controls with fullstop.
.D4:  MOV [SS:BX],AL
      INC BX
      MOV AL,AH
      CALL .StoAL:
      MOV AL,' '
      STOSB
      INC DX
      JMP .D7:
.D5:
      MOV AX,'__'           ; When offset DX is outside dumped area, use underscores.
      MOV [SS:BX],AL
      INC BX
      STOSW
      MOV AL,' '
      STOSB
.D7:  INCW [BP+32]
      LOOP .D3:
      ADD DI,16             ; Skip the already written char column.
      CALL .Eol:
      CMP DX,[%DebugSt]     ; Test if above dumped area.
      JB .D2:
.End:MOV CX,DI              ; Output text is created at ES:SP..DI.
     MOV SI,SP
     SUB AX,AX
     STOSB                  ; Zero-terminate the output text.
     SUB CX,SI
     PUSH BP
      CALLF [BP+26]         ; Perform the callback with string DS:SI,CX.
     POP BP
     MOV SP,BP
     POPAW
     POP ES,DS
     POPFW
     RETF 13*2

.Test386:PROC1 ; Returns CF=1 if CPU is older than 386. Destroys AX.
       EUROASM PUSH,WARN=2340,CPU=086 ; Allow only 086 instructions in this test.
       XOR AX,AX
       PUSH AX
       POPFW
       PUSHFW
       POP AX
       AND AH,11110000b
       CMP AH,11110000b     ; If flag bits 12..15 are auto-set, CPU=086.
       JE Debug16@RT.NonAvailable:
       OR  AH,11110000b
       PUSH AX
       POPFW
       PUSHFW
       POP AX
       AND AH,11110000b     ; If flag bits 12..15 are auto-reset, CPU=286.
       JZ Debug16@RT.NonAvailable:
       RET                  ; Otherwise CPU is at least 386, return CF=0.
       EUROASM POP
      ENDP1 .Test386:

.TestPRIV:PROC1  ; Returns CF=1 if not in ring 0.
     MOV CX,CS
     AND CX,11b             ; ECX is privilege level 0..3.
     JNZ Debug16@RT.NonAvailable:
     RET
    ENDP1 .TestPRIV:

.NonAvailable:PROC1         ; Displays " N/A" + EOL and returns with CF=1.
       MOV AX,' N'
       STOSW
       MOV AX,'/A'
       STOSW
       MOV AX,0x0A0D
       STOSW
       STC
       RET
      ENDP1 .NonAvailable:

.TestCPUID:PROC1 ; Returns CF=1 if CPU doesn't support CPUID. Destroys EAX,EBX,ECX,EDX.
       CALL Debug16@RT.Test386:
       JC .T9:
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0x0004_0000  ; Toggle flag AC.
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX          ; If AC cannot toggle, CPUID is N/A.
       JZ Debug16@RT.NonAvailable:
       MOV EAX,EDX
       XOR EAX,0x0020_0000  ; Toggle flag ID.
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX          ; If ID cannot toggle, CPU is 486 without CPUID.
       JZ Debug16@RT.NonAvailable:
.T9:   RET                  ; CPUID is available, return with CF=0.
      ENDP1 .TestCPUID:

.TestMMX:PROC1  ; Returns CF=1 if CPU doesn't support MMX. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug16@RT.TestCPUID:
       JC .T9:              ; If CPUID is not available, MMX is neither.
       MOV EAX,1
       CPUID
       BT EDX,23            ; CPUID.1:EDX.MMX
       JNC Debug16@RT.NonAvailable:
       CLC
.T9:   RET
      ENDP1 .TestMMX:

.TestSSE:PROC1  ; Returns CF=1 if CPU doesn't support SSE. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug16@RT.TestCPUID:
       JC .T9:              ; If CPUID is not available, SSE is neither.
       MOV EAX,1
       CPUID
       BT EDX,26            ; CPUID.1:EDX.SSE2 (required for MOVDQU instruction).
       JNC Debug16@RT.NonAvailable:
       CLC
.T9:   RET
      ENDP1 .TestSSE:

.TestAVX:PROC1  ; Returns CF=1 if CPU doesn't support AVX. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug16@RT.TestCPUID:
       JC .T9:              ; If CPUID is not available, AVX is neither.
       MOV EAX,1
       CPUID
       BT ECX,27            ; CPUID.1:ECX.OSXSAVE
       JNC Debug16@RT.NonAvailable: ; If OSXSAVE is not supported, AVX is neither.
       XOR ECX,ECX
       XGETBV               ; Read XCR0 to EDX:EAX.
       AND EAX,0000_0110b
       CMP EAX,0000_0110b
       JNE Debug16@RT.NonAvailable: ; XMM and YMM are not supported.
.T9:   RET
      ENDP1 .TestAVX:


.TestAVX512:PROC1 ; Returns CF=1 if CPU doesn't support AVX512. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug16@RT.TestCPUID:
       JC .T9:              ; If CPUID is not available, AVX512 is neither.
       MOV EAX,1
       CPUID
       BT ECX,27            ; CPUID.1:ECX.OSXSAVE
       JNC Debug16@RT.NonAvailable: ; If OSXSAVE is not supported, AVX512 is neither.
       XOR ECX,ECX
       XGETBV               ; Read XCR0 to EDX:EAX.
       AND EAX,11100110b
       CMP EAX,11100110b
       JNE Debug16@RT.NonAvailable: ; ZMM are not supported.
       MOV EAX,7
       XOR ECX,ECX
       CPUID                ; Is AVX512Foundation supported?
       BT EBX,16            ; CPUID.7.0:EBX.AVX512F
       JNC Debug16@RT.NonAvailable: ; Foundation not supported.
       CLC
.T9:   RET
      ENDP1 .TestAVX512:

.StoDD:PROC1  ; Store unsigned 32bit integer DX:CX as decimal string at ES:DI.
        XOR AX,AX      ; Returns AX,BX,SI=?, DI=incrementd.
        PUSH CX,DX
decim   %FOR 1_000_000_000, 100_000_000, 10_000_000, \
             1_000_000, 100_000, 10_000, 1_000, 100, 10, 1
          MOV SI,%decim /65536
          MOV BX,%decim \65536 ; SI:BX is divisor 1_000_000_000..1.
          CALL .1digit:
        %ENDFOR decim
        POP DX,CX
        RET
.1digit:PROC1
          SUB AL,AL
      .1A:SUB CX,BX    ; Use repeated subtraction instead of division.
          SBB DX,SI
          JB .1D:
          INC AL
          JMP .1A:
      .1D:ADD CX,BX    ; Rollback the last subtraction.
          ADC DX,SI
          TEST AX
          JZ .1Z:      ; Do not display unsignificant zeros.
          OR AX,0x0130 ; AH=flag that all following zeros are significant.
          STOSB        ; Store one significant decimal digit.
      .1Z:RET
        ENDP1 .1digit:
       ENDP1 .StoDD:

.Flag:  PROC1 ; Display flag named AX under mask CX as "CF=1 ". Returns DI=++, AX=?.
         STOSW
         MOV AL,'='
         STOSB
         MOV AX,'0 '
         TEST [BP+20],CX
         JZ .Flag0:
         INC AL
.Flag0:  STOSW
         RET
        ENDP1 .Flag:

.AHsp:  PROC1 ; Display AH spaces. Returns AX=0x0020, DI=+AH.
           MOV AL,' '
.sp:       STOSB
           DEC AH
           JNZ .sp:
           RET
        ENDP1 .AHsp:

.Eol1:  PROC1  ; Discard the last emited char, store CR+LF - make new line. Returns DI=+1, AX=?.
         DEC DI
        ENDP1 .Eol1:                 ; Continue through .Eol:.
.Eol:   PROC1  ; Store CR+LF - force EndOfLine. Returns DI=+2, AX=?.
         MOV AX,0x0A0D
         STOSW
         RET
        ENDP1 .Eol:

.r16:   PROC1 ; Store r16 from [SI](MSB),[SI-1](LSB) with name in AX,DL to ES:DI as "BP=CCCC ".
         STOSW                                          ; Returns SI=-2, DI=+8|9, AX=?.
         TEST DL
         JZ .r16a:
         MOV AX,DX
         STOSB
.r16a:   MOV AL,'='
         STOSB
         CALL Debug16@RT.StoSI2
         MOV AL,' '
         STOSB
         RET
        ENDP1 .r16:

.r32:   PROC1 ; Store r32 from [SI](MSB)..[SI-3](LSB) with name in AX,DL to ES:DI as "EBP=CCCC_DDDD ".
         STOSW                                     ; Returns SI=-4, DI=+14, AX=?.
         MOV AX,DX
         MOV AH,'='
         STOSW
        ENDP1 .r32:                                 ; Continue through .StoSI_4:
.StoSI_4:PROC1 ; Store [SI+0](MSB),[SI-1],[SI-2],[SI-3] as "00FF_FEFD " to ES:DI. Returns SI=-4, DI=+10, AX=?.
         CALL Debug16@RT.StoSI2:
         MOV AL,'_'
         STOSB
         CALL Debug16@RT.StoSI2:
         MOV AL,' '
         STOSB
         RET
         ENDP1 .StoSI_4

.r80:PROC1  ; Display the contents from %DebugWA of CH-th FPU register at DI.
      PUSH CX     ; DX=register property: 00b=VALid, 01b=NULl, 10b=NAN, 11b=EMPty
      MOV AX,'ST' ; Returns AX=?,SI=?, DI=+33.
      STOSW
      MOV AX,'0='
      OR AL,CH
      STOSW
      MOV CL,DL
      AND CL,11b
      MOV AX,'VA'
      MOV SI,'L:'
      DEC CL
      JS .8D:
      MOV AX,'NU'
      MOV SI,'L:'
      DEC CL
      JS .8D:
      MOV AX,'NA'
      MOV SI,'N:'
      DEC CL
      JS .8D:
      MOV AX,'EM'
      MOV SI,'P:'
  .8D:STOSW
      MOV AX,SI
      STOSW
      MOV SI,CX     ; CH is ST ordinal 0..7.
      SHR SI,8      ; SI=0,1,2,3,4,5,6,7.
      SAL SI,1
      MOV AX,SI     ; AX=0,2,4,6,8,10,12,14.
      SAL SI,2
      ADD SI,AX     ; SI=0,10,20,30,40,50,60,70.
      LEA SI,[%DebugWA+14+SI+9]
      MOV CX,5
  .8G:CALL Debug16@RT.StoSI2:
      MOV AL,'_'
      STOSB
      LOOP .8G:
      DEC DI        ; Omit the last underscore.
      POP CX
      RET
     ENDP1 .r80:

.StoSI4:PROC1 ; Store [SI+0](MSB),[SI-1],[SI-2],[SI-3] as "00FFFEFD" to ES:DI. Returns SI=-4, DI=+8, AX=?.
         CALL Debug16@RT.StoSI2:
        ENDP1 .StoSI4:  ; Continue through .StoSI2:
.StoSI2:PROC1 ; Store [SI+0](MSB),[SI-1](LSB) as "0000" to ES:DI. Returns SI=-2, DI=+4, AX=?.
         LODSB
         DEC SI,SI
         CALL Debug16@RT.StoAL:
         LODSB
         DEC SI,SI
        ENDP1 .StoSI2: ; Continue through .StoAL:
.StoAL:PROC1 ; Store AL as 2 hexa chars "FF" to ES:DI. Returns DI=+2, AX=?.
        MOV AH,AL
        SHR AL,4
        ADD AL,0x90
        DAA
        ADC AL,0x40
        DAA
        STOSB
        MOV AL,AH
        AND AL,0x0F
        ADD AL,0x90
        DAA
        ADC AL,0x40
        DAA
        STOSB
        RET
       ENDP1 .StoAL:

.ROUND:PROC1 ; Display "ROUND=NEAR" from bits 0..1 of CL. Returns AX,CX,DX,DI changed.
        MOV AX,'RO'
        STOSW
        MOV AX,'UN'
        STOSW
        MOV AX,'D='
        STOSW
        MOV AX,'NE'
        MOV DX,'AR'
        AND CX,11b
        JZ .R9:
        DEC CX
        MOV AX,'DO'
        MOV DX,'WN'
        JZ .R9:
        DEC CX
        MOV AX,'UP'
        MOV DX,'  '
        JZ .R9:
        MOV AX,'ZE'
        MOV DX,'RO'
.R9:    STOSW
        XCHG AX,DX
        STOSW
        RET
       ENDP1 .ROUND:

.EXC:PROC1 ; Display exceptions from bits 5..0 of CL. Returns AX,CX,DI changed.
     MOV AX,'EX'
     STOSW
     MOV AX,'C='
     STOSW
     AND CL,0x3F
     JNZ .E0:
     MOV AX,'NO'
     STOSW
     MOV AX,'NE'
     STOSW
     JMP .E9:
.E0: SHR CL,1
     JNC .E1:
     MOV AX,'IN'
     STOSW
     MOV AX,'V+'
     STOSW
.E1: SHR CL,1
     JNC .E2:
     MOV AX,'DE'
     STOSW
     MOV AX,'N+'
     STOSW
.E2: SHR CL,1
     JNC .E3:
     MOV AX,'DI'
     STOSW
     MOV AX,'V0'
     STOSW
     MOV AL,'+'
     STOSB
.E3: SHR CL,1
     JNC .E4:
     MOV AX,'OV'
     STOSW
     MOV AX,'F+'
     STOSW
.E4: SHR CL,1
     JNC .E5:
     MOV AX,'UN'
     STOSW
     MOV AX,'F+'
     STOSW
.E5: SHR CL,1
     JNC .E8:
     MOV AX,'PR'
     STOSW
     MOV AX,'EC'
     STOSW
     INC DI
.E8: DEC DI          ; Dismiss the last '+'.
.E9: RET
    ENDP1 .EXC:

.R2MM:PROC1           ; Display two MMx with ordinals DL and DL+4 stored at SI,SI+8.
       MOV AX,'MM'    ; Returns AX,CX,SI=?, DI=+23.
       STOSW
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA SI,[%DebugWA+7]
       MOV CX,4
.R2MM4:CALL Debug16@RT.StoSI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM4:
       DEC DI
       MOV AH,16
       CALL Debug16@RT.AHsp:
       MOV AX,'MM'
       STOSW
       MOV AX,'4='
       OR AL,DL
       STOSW
       ADD SI,8+8
       MOV CL,4
.R2MM8:CALL Debug16@RT.StoSI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM8:
       CALL Debug16@RT.Eol1:
       MOV AH,10
       JMPN Debug16@RT.AHsp:
     ENDP1 .R2MM:

.MXCSR:PROC1  ; Store MXCSR as hexa and as bitfields FZ=,DZ=,ROUND=,EXC=.
        MOV AX,'MX'
        STOSW
        MOV AX,'CS'
        STOSW
        MOV AX,'R='
        STOSW
        LEA SI,[%DebugWA]
        STMXCSR [SI]
        ADD SI,3
        CALL Debug16@RT.StoSI2:
        MOV AL,'_'
        STOSB
        CALL Debug16@RT.StoSI2:
        MOV AX,' F'
        STOSW
        MOV AX,'Z='
        STOSW
        MOV AL,[%DebugWA+1]
        SHR AL,7
        OR AL,'0'
        STOSB
        MOV AX,' D'
        STOSW
        MOV AX,'Z='
        STOSW
        MOV AL,[%DebugWA+0]
        SHR AL,6
        AND AL,1
        OR AL,'0'
        STOSB
        MOV AL,' '
        STOSB
        MOV CL,[%DebugWA+1]
        SHR CX,13
        CALL Debug16@RT.ROUND:
        MOV AL,' '
        STOSB
        MOV CL,[%DebugWA+0]
        CALL Debug16@RT.EXC:
        JMPN Debug16@RT.Eol:
       ENDP1 .MXCSR:

.XMM:PROC1  ; Display CL-th register XMM.
      MOV AX,' X'
      STOSW
      MOV AX,'MM'
      STOSW
      MOV AX,'0='
      OR AL,CL
      STOSW
      LEA SI,[%DebugWA+15]
      MOV CX,4
.XMMB:CALL Debug16@RT.StoSI4:
      MOV AL,'_'
      STOSB
      LOOP .XMMB
      JMP Debug16@RT.Eol1:
     ENDP1 .XMM:

.YMM:PROC1  ; Display CL-th register YMM.
      MOV AX,' Y'
      STOSW
      MOV AX,'MM'
      STOSW
      MOV AX,'0='
      OR AL,CL
      STOSW
      LEA SI,[%DebugWA+31]
      MOV CX,8
.YMMB:CALL Debug16@RT.StoSI4:
      MOV AL,'_'
      STOSB
      LOOP .YMMB
      JMP Debug16@RT.Eol1:
     ENDP1 .YMM:

.ZMM:PROC1 ; Display DL-th register ZMM. CL=0..7 (16bit version).
     MOV AX,' Z'
     STOSW
     MOV AX,'MM'
     STOSW
     MOV AX,'0='
     OR AL,DL
     STOSW
     LEA SI,[%DebugWA+64-1]
     MOV CX,8
.ZMM1:CALL Debug16@RT.StoSI4:
     MOV AL,'_'
     STOSB
     LOOP .ZMM1:
     CALL Debug16@RT.Eol:
     MOV AX,' Y'
     STOSW
     MOV AX,'MM'
     STOSW
     MOV AX,'0='
     OR AL,DL
     STOSW
     MOV CL,8
.ZMM2:CALL Debug16@RT.StoSI4:
     MOV AL,'_'
     STOSB
     LOOP .ZMM2:
     JMP Debug16@RT.Eol1:
    ENDP1 .ZMM:

.R2K: PROC1          ; Display two Kx with ordinals DL and DL+4 stored in %DebugWA.
       MOV AX,'  '; Returns EAX,ECX,ESI=?, EDI=+23.
       STOSW
       MOV AX,' K'
       STOSW
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA SI,[%DebugWA+8-1]
       CALL Debug16@RT.StoSI4:
       MOV AL,'_'
       STOSB
       CALL Debug16@RT.StoSI4:
       MOV AH,16
       CALL Debug16@RT.AHsp:
       MOV AL,'K'
       STOSB
       MOV AX,'4='
       OR AL,DL
       STOSW
       LEA SI,[%DebugWA+16-1]
       CALL Debug16@RT.StoSI4:
       MOV AL,'_'
       STOSB
       CALL Debug16@RT.StoSI4:
       JMP Debug16@RT.Eol:
      ENDP1 .R2K:

    ENDP1 Debug16@RT::
   %ENDIF Debug16
Debug32@RT
Debug32  %IF %Width=32       ; 32bit mode.
n         %FOR 12..4, STEP= -4
            %IF "%DebugSN[%n-3..%n]" === ""
              PUSHD 0, IMM=DWORD
            %ELSE
              PUSHD "%DebugSN[%n-3..%n]",IMM=DWORD
            %ENDIF
          %ENDFOR n
          PUSHD %^SourceLine, IMM=DWORD
          %IF REGTYPE# %DumpAddr = 'D'
            PUSH %DumpAddr
          %ELSE
            PUSHD %DumpAddr,  IMM=DWORD
          %ENDIF
          PUSHD %DebugFg,     IMM=DWORD
          PUSHD %CallbackProc,IMM=DWORD
          CALL Debug32@RT
Debug32@RT:PROC1
     PUSHFD
     PUSHAD
     MOV EBP,ESP
     CLD
%DebugSt %SET EBP+44         ; Macro Debug internal status flags.
%DebugWA %SET EBP-128        ; Working area 128 bytes for registers and FPU environment.
     SUB ESP,128+4400        ; Allocate room on stack for %DebugWA and 55 lines of formated output.
     MOV EDI,ESP
     CALL .Eol:
     MOV EAX,'####'
     STOSD
     MOV EAX,' Deb'
     STOSD
     MOV EAX,'ug a'
     STOSD
     MOV EAX,'t " '
     STOSD
     DEC EDI
     LEA ESI,[EBP+56]        ; Display %^SourceName.
     MOV ECX,12
 .G1:LODSB
     STOSB
     CMP AL,0
     LOOPNZ .G1:
     JNZ .G2:
     DEC EDI
 .G2:MOV AX,'"{'
     STOSW
     MOV ECX,[EBP+52]        ; Display %^SourceLine.
     CALL .StoDD:            ; Conversion to decimal number at EDI {simplified StoD).
     MOV AL,'}'
     STOSB
     MOV ECX,ESP
     ADD ECX,41              ; Continue at 41st column of output line.
     SUB ECX,EDI
     JBE .G4:
     MOV AL,' '
     REP STOSB
 .G4:                        ; Display segment registers.
seg  %FOR CS,DS,ES,SS
       MOV EAX,%seg
       MOV [%DebugWA],EAX
       MOV EAX,'%seg'
       LEA ESI,[%DebugWA+1]
       CALL .r16:
     %ENDFOR seg
     CALL .Eol1:
     MOV EAX,'GPR:'
     STOSD
     MOV AL,' '
     STOSB
     MOV EAX,'FL'
     LEA ESI,[EBP+32+3]      ; Display flags.
     CALL .r32:
     MOV AX,'NT'
     MOV ECX,0x4000
     CALL .Flag:
     MOV EAX,'PL='           ; Display IOPL.
     STOSD
     DEC EDI
     MOV AX,'0 '
     MOV ECX,[EBP+32]
     SHR CX,12
     AND CL,11b
     ADD AL,CL
     STOSW
     MOV AX,'OF'
     MOV CX,0x0800
     CALL .Flag:
     MOV AX,'DF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'IF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'TF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'SF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'ZF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'AF'
     SHR ECX,2
     CALL .Flag:
     MOV AX,'PF'
     SHR ECX,2
     CALL .Flag:
     MOV AX,'CF'
     SHR ECX,2
     CALL .Flag:
     CALL .Eol1:
     MOV AL,' '              ; Display general-purpose registers.
     STOSB
     MOV EAX,'EAX'
     LEA ESI,[EBP+28+3]
     CALL .r32:
     MOV EAX,'ECX'
     CALL .r32:
     MOV EAX,'EDX'
     CALL .r32:
     MOV EAX,'EBX'
     CALL .r32:
     MOV EAX,FS
     LEA ESI,[%DebugWA]
     MOV [ESI],EAX
     MOV EAX,'FS'
     INC ESI
     CALL .r16:
     MOV EAX,GS
     LEA ESI,[%DebugWA]
     MOV [ESI],EAX
     MOV EAX,'GS'
     INC ESI
     CALL .r16:
     CALL .Eol1:
     LEA EAX,[EBP+68]        ; Original ESP.
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,' ESP'
     CALL .r32:
     MOV EAX,'EBP'
     LEA ESI,[EBP+8+3]
     CALL .r32:
     MOV EAX,'ESI'
     CALL .r32:
     MOV EAX,'EDI'
     CALL .r32:
     MOV AX,'  '
     STOSW
     MOV EAX,[EBP+36]        ; Return-address.
     SUB EAX,7*5+1*5         ; Original EIP was 7 instructions PUSHD IMM=D and 1 CALLN above return.
     MOV [%DebugWA],EAX
     MOV EAX,'EIP'
     LEA ESI,[%DebugWA+3]
     CALL .r32:
     CALL .Eol1:
     TESTD [%DebugSt],%DebugPRIV
     JZ .FPU:
.PRIV:MOV EAX,'PRIV'         ; Display privileged registers CR,DR.
     STOSD
     MOV EAX,':   '
     STOSD
     CALL .TestPRIV:
     JC .FPU:
     MOV EAX,DR0
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR3
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR3'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR0
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'CR0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,CR4
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'CR4'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV EAX,DR1
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR1'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR6
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR6'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR2
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'CR2'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,CR4
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'CR4'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV EAX,DR2
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR2'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV EAX,DR7
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'DR7'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV EAX,CR3
     MOV [%DebugWA],EAX
     LEA ESI,[%DebugWA+3]
     MOV EAX,'CR3'
     CALL .r32:
     CALL .Eol1:
.FPU:TESTD [%DebugSt],%DebugFPU
     JZ .MMX:
     MOV EAX,'FPU:'          ; Display FPU in 32bit mode.
     STOSD
     MOV AH,17
     CALL .AHsp:
     FNSAVE [%DebugWA]       ; Area for FPU registers and FPU environment.
     MOV EAX,'FCW'           ; Display FPU environment as dwords.
     LEA ESI,[%DebugWA+00+3]
     CALL .r32:
     MOV EAX,'FSW'
     LEA ESI,[%DebugWA+04+3]
     CALL .r32:
     MOV EAX,'FTW'
     LEA ESI,[%DebugWA+08+3]
     CALL .r32:
     CALL .Eol1:
     MOV AH,7
     CALL .AHsp:
     MOV EAX,'FIO'
     LEA ESI,[%DebugWA+12+3]
     CALL .r32:
     MOV EAX,'FIS'
     LEA ESI,[%DebugWA+16+3]
     CALL .r32:
     MOV EAX,'FDO'
     LEA ESI,[%DebugWA+20+3]
     CALL .r32:
     MOV EAX,'FDS'
     LEA ESI,[%DebugWA+24+3]
     CALL .r32:
     CALL .Eol1:
     MOV ECX,[%DebugWA+04]   ; FSW.
     MOV AL,' '              ; Display FPU environment as bitfields.
     STOSB
     MOV EAX,'C0(C'
     STOSD
     MOV EAX,'F)=0'
     STOSD
     DEC EDI
     MOV AL,'0'
     SHR CH,1
     ADC AL,0
     STOSB
     MOV EAX,' C1='
     STOSD
     MOV EAX,'0 C2'
     SHR CH,1
     ADC AL,0
     STOSD
     MOV EAX,'(PF)'
     STOSD
     MOV EAX,'=0 C'
     SHR CH,1
     ADC AH,0
     STOSD
     MOV EAX,'3(ZF'
     STOSD
     MOV AL,')'
     STOSB
     MOV DL,CH               ; Save TOP to EDX, bits 0..2.
     MOV EAX,'=0 T'
     SHR CH,4
     ADC AH,0
     STOSD
     MOV EAX,'OP=0'
     AND DL,111b
     SHL EDX,24
     ADD EAX,EDX
     STOSD
     MOV AL,' '
     STOSB
     MOV ECX,[%DebugWA+00]   ; FCW.
     SHR ECX,10
     CALL .ROUND:
     MOV EAX,' PRE'
     STOSD
     MOV AX,'C='
     STOSW
     MOV ECX,[%DebugWA+00]   ; FCW.
     SHR ECX,8
     MOV EAX,'DWOR'
     MOV DL,'D'
     AND CL,11b
     JZ .F4:
     DEC CL
     MOV EAX,'?   '
     MOV DL,' '
     JZ .F4:
     DEC CL
     MOV EAX,'QWOR'
     MOV DL,'D'
     JZ .F4:
     MOV EAX,'TBYT'
     MOV DL,'E'
.F4: STOSD
     MOV EAX,EDX
     STOSB
     MOV AL,' '
     STOSB
     MOV ECX,[%DebugWA+04]   ; FSW.
     CALL .EXC:
     CALL .Eol:              ; Display 8 FPU registers.
     MOV EDX,[%DebugWA+08]   ; FTW, each two bits specify ST property.
     MOV ECX,[%DebugWA+04]   ; FSW.
     AND CH,00111000b        ; Which register is currently ST0 (top of FPU stack).
     SHR ECX,10
     ROR DX,CL               ; Property of ST0 is now in two LSbits of DX.
     SUB ECX,ECX             ; CH=ordinal of ST register (0..7).
.F1: MOV AL,' '
     STOSB
     CALL .r80:              ; Display the property and contents of one FPU register.
     MOV AH,7
     CALL .AHsp:
     ADD CH,4
     ROR DX,4*2
     CALL .r80:
     CALL .Eol:
     SUB CH,3
     ROL DX,3*2
     CMP CH,4
     JNE .F1:
     FRSTOR [%DebugWA]

.MMX:TESTD [%DebugSt],%DebugMMX
     JZ .AVX512:
     MOV EAX,'MMX:'          ; Display MMX
     STOSD
     CALL .TestMMX:
     JC .AVX512:
     MOV AH,6
     CALL .AHsp:
m    %FOR 0..3
       MOV DL,%m             ; %m=MMx register ordinal, left column.
       MOVD [%DebugWA+0],MM%m
       %m4 %SETA %m+4        ; %m4=MMx register ordinal, right column.
       MOVD [%DebugWA+8],MM%m4
       CALL .R2MM:           ; Display two MMx registers.
     %ENDFOR m
     SUB EDI,10

.AVX512:TESTD  [%DebugSt],%DebugAVX512
     JZ .AVX:
     MOV EAX,'AVX5'
     STOSD
     MOV EAX,'12: '
     STOSD
     DEC EDI
     CALL .TestAVX512:
     JC .AVX:
     INC EDI
     CALL .MXCSR:
k    %FOR 0..3
       MOV DL,%k
       KMOVQ [%DebugWA+0],K%k
       %k4 %SETA %k+4
       KMOVQ [%DebugWA+8],K%k4
       CALL .R2K:
     %ENDFOR k
z    %FOR 0..7
        MOV DL,%z
        VMOVUPD [%DebugWA],ZMM%z
        CALL .ZMM:
     %ENDFOR
     JMP .End:

.AVX:TESTD [%DebugSt],%DebugAVX
     JZ .SSE:
     MOV EAX,'AVX:'
     STOSD
     MOV EAX,'    '
     STOSD
     DEC EDI
     CALL .TestAVX:
     JC .SSE:
     MOV AL,' '
     STOSB
     CALL .MXCSR:
y    %FOR 0..7
       VMOVUPS [%DebugWA],YMM%y
       MOV CL,%y
       CALL .YMM:
     %ENDFOR y
     JMP .End:

.SSE:TESTD [%DebugSt],%DebugSSE
     JZ .Dump:
     MOV EAX,'SSE:'            ; Display SSE.
     STOSD
     CALL .TestSSE:
     JC .End:
     MOV EAX,'    '
     STOSD
     CALL .MXCSR:
x    %FOR 0..7
       MOVDQU [%DebugWA],XMM%x
       MOV CL,%x
       CALL .XMM:
     %ENDFOR x

.Dump:MOV ECX,[%DebugSt]
     TEST ECX,%DebugDump
     JZ .End:
     MOV EAX,'Dump'            ; Display hexadecimal and character dump.
     STOSD
     MOV AL,' '
     STOSB
     MOVZX ECX,CH
     INC ECX                   ; Dump size.
     MOV EAX,[EBP+48]          ; Dump address.
     ADD EAX,ECX
     MOV [%DebugSt],EAX        ; %DebugSt is now used as the end offset of dumped area.
     CALL .StoDD:              ; Number of ECX dumped bytes.
     MOV EAX,' byt'
     STOSD
     MOV EAX,'es a'
     STOSD
     MOV AX,'t '
     STOSW
     LEA ESI,[EBP+48+3]        ; Dumped address.
     CALL .StoESI_4:
     DEC EDI
     MOV AL,':'
     STOSB
     CALL .Eol:
     MOV EDX,[EBP+48]          ; Dumped address.
     MOV EAX,EDX
     AND AL,0xF0
     MOV [EBP+48],EAX          ; Start offset of dumped area rounded down.
.D2: ; Print dumped address.
     LEA ESI,[EBP+48+3]
     CALL .StoESI_4:
     DEC EDI
     MOV AX,': '
     STOSW
     LEA EBX,[EDI+60-12]       ; EBX points to the first char-dump column.
     MOV ECX,16                ; Number of bytes dumped in one line.
.D3: CMP EDX,[EBP+48]
     JA .D5:
     CMP EDX,[%DebugSt]        ; Test if above dumped area.
     JAE .D5:
     MOV AL,[EDX]
     MOV AH,AL
     CMP AL,' '
     JNB .D4:
     MOV AL,'.'                ; Replace controls with fullstop.
.D4: MOV [EBX],AL
     INC EBX
     MOV AL,AH
     CALL .StoAL:
     MOV AL,' '
     STOSB
     INC EDX
     JMP .D7:
.D5: MOV AX,'__'               ; When offset DX is outside dumped area, use underscores.
     MOV [EBX],AL
     INC EBX
     STOSW
     MOV AL,' '
     STOSB
.D7: INCD [EBP+48]
     LOOP .D3:
     ADD EDI,16                ; Skip the already written char column.
     CALL .Eol:
     CMP EDX,[%DebugSt]        ; Test if above dumped area.
     JB .D2:

.End:MOV ECX,EDI               ; Output text is created at ESP..EDI.
     MOV ESI,ESP
     SUB EAX,EAX
     STOSB                     ; Zero-terminate the output text.
     SUB ECX,ESI
     PUSH EBP
      CALL [EBP+40]            ; Perform the callback with string ESI,ECX.
     POP EBP
     MOV ESP,EBP
     POPAD
     POPFD
     RET 7*4

.NonAvailable:PROC1            ; Store " N/A" and end-of-line.
     MOV EAX,' N/A'
     STOSD
     MOV AX,0x0A0D
     STOSW
     STC
     RET
    ENDP1 .NonAvailable:

.TestCPUID:PROC1 ; Returns via .NonAvailable when CPU doesn't support CPUID. Destroys EAX,EBX,ECX,EDX.
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0x0004_0000     ; Toggle flag AC.
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX             ; If AC cannot toggle, CPUID is N/A.
       JZ Debug32@RT.NonAvailable:
       MOV EAX,EDX
       XOR EAX,0x0020_0000     ; Toggle flag ID.
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX             ; If ID cannot toggle, CPU is 486 without CPUID.
       JZ Debug32@RT.NonAvailable:
.T9:   RET                     ; CPUID is available, return with CF=0.
      ENDP1 .TestCPUID:

.TestPRIV:PROC1 ; Returns CF via .NonAvailable when CPU is not in ring 0.
     MOV ECX,CS
     AND ECX,11b               ; ECX is privilege level 0..3.
     JNZ Debug32@RT.NonAvailable:
     RET
    ENDP1 .TestPRIV:

.TestMMX:PROC1  ; Returns CF via .NonAvailable when CPU doesn't support MMX. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug32@RT.TestCPUID:
       JC .T9:                 ; If CPUID is not available, MMX is neither.
       MOV EAX,1
       CPUID
       BT EDX,23               ; CPUID.1:EDX.MMX
       JNC Debug32@RT.NonAvailable:
       CLC
.T9:   RET
      ENDP1 .TestMMX:

.TestSSE:PROC1  ; Returns CF=1 if CPU doesn't support SSE2. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug32@RT.TestCPUID:
       JC .T9:                  ; If CPUID is not available, SSE is neither.
       MOV EAX,1
       CPUID
       BT EDX,26                ; CPUID.1:EDX.SSE2 (required for MOVDQU instruction).
       JNC Debug32@RT.NonAvailable:
       CLC
.T9:   RET
      ENDP1 .TestSSE:

.TestAVX:PROC1  ; Returns CF=1 if CPU doesn't support AVX. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug32@RT.TestCPUID:
       JC .T9:                  ; If CPUID is not available, AVX is neither.
       MOV EAX,1
       CPUID
       BT ECX,27                ; CPUID.1:ECX.OSXSAVE
       JNC Debug32@RT.NonAvailable: ; If OSXSAVE is not supported, AVX is neither.
       XOR ECX,ECX
       XGETBV                   ; Read XCR0 to EDX:EAX.
       AND EAX,0000_0110b
       CMP EAX,0000_0110b
       JNE Debug32@RT.NonAvailable: ; XMM and YMM are not supported.
.T9:   RET
      ENDP1 .TestAVX:

.TestAVX512:PROC1 ; Returns CF=1 if CPU doesn't support AVX512. Expects CPU=386. Destroys EAX,EBX,ECX,EDX.
       CALL Debug32@RT.TestCPUID:
       JC .T9:                  ; If CPUID is not available, AVX512 is neither.
       MOV EAX,1
       CPUID
       BT ECX,27                ; CPUID.1:ECX.OSXSAVE
       JNC Debug32@RT.NonAvailable: ; If OSXSAVE is not supported, AVX512 is neither.
       XOR ECX,ECX
       XGETBV                   ; Read XCR0 to EDX:EAX.
       AND EAX,11100110b
       CMP EAX,11100110b
       JNE Debug32@RT.NonAvailable: ; ZMM are not supported.
       MOV EAX,7
       XOR ECX,ECX
       CPUID                    ; Is AVX512Foundation supported?
       BT EBX,16                ; CPUID.7.0:EBX.AVX512F
       JNC Debug32@RT.NonAvailable: ; Foundation not supported.
       CLC
.T9:   RET
      ENDP1 .TestAVX512:

.XMM:PROC1  ; Display CL-th register XMM. Changes EAX,ECX,ESI,EDI.
      MOV EAX,' XMM'
      STOSD
      MOV AX,'0='
      OR AL,CL
      STOSW
      LEA ESI,[%DebugWA+15]
      MOV ECX,4
.XMMB:CALL Debug32@RT.StoESI4:
      MOV AL,'_'
      STOSB
      LOOP .XMMB
      JMP Debug32@RT.Eol1:
     ENDP1 .XMM:

.YMM:PROC1  ; Display CL-th register YMM.  Changes EAX,ECX,ESI,EDI.
      MOV EAX,' YMM'
      STOSD
      MOV AX,'0='
      OR AL,CL
      STOSW
      LEA ESI,[%DebugWA+31]
      MOV ECX,8
.YMMB:CALL Debug32@RT.StoESI4:
      MOV AL,'_'
      STOSB
      LOOP .YMMB:
      JMP Debug32@RT.Eol1:
     ENDP1 .YMM:

.ZMM:PROC1  ; Display DL-th register ZMM. DL=0..7 (32bit version).
       MOV EAX,' ZMM'
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA ESI,[%DebugWA+64-1]
       MOV ECX,8
.ZMM1: CALL Debug32@RT.StoESI4:
       MOV AL,'_'
       STOSB
       LOOP .ZMM1:
       CALL Debug32@RT.Eol:
       MOV EAX,' YMM'
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       MOV CL,8
.ZMM2: CALL Debug32@RT.StoESI4:
       MOV AL,'_'
       STOSB
       LOOP .ZMM2:
       JMP Debug32@RT.Eol1:
      ENDP1 .ZMM:

.MXCSR:PROC1  ; Display MXCSR as hexa and as bitfields FZ=,DZ=,ROUND=,EXC=.
        MOV EAX,'MXCS'
        STOSD
        MOV AX,'R='
        STOSW
        LEA ESI,[%DebugWA]
        STMXCSR [ESI]
        ADD ESI,3
        CALL Debug32@RT.StoESI2:
        MOV AL,'_'
        STOSB
        CALL Debug32@RT.StoESI2:
        MOV EAX,' FZ='
        STOSD
        MOV AL,[%DebugWA+1]
        SHR AL,7
        OR AL,'0'
        STOSB
        MOV EAX,' DZ='
        STOSD
        MOV AL,[%DebugWA+0]
        SHR AL,6
        AND AL,1
        OR AL,'0'
        STOSB
        MOV AL,' '
        STOSB
        MOV ECX,[%DebugWA]
        SHR CX,13
        CALL Debug32@RT.ROUND:
        MOV AL,' '
        STOSB
        MOV ECX,[%DebugWA]
        CALL Debug32@RT.EXC:
        JMPN Debug32@RT.Eol:
       ENDP1 .MXCSR:

.R2K: PROC1          ; Display two Kx with ordinals DL and DL+4 stored in %DebugWA.
       MOV EAX,'   K'; Returns EAX,ECX,ESI=?, EDI=+23.
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA ESI,[%DebugWA+8-1]
       CALL Debug32@RT.StoESI4:
       MOV AL,'_'
       STOSB
       CALL Debug32@RT.StoESI4:
       MOV AH,16
       CALL Debug32@RT.AHsp:
       MOV AL,'K'
       STOSB
       MOV AX,'4='
       OR AL,DL
       STOSW
       LEA ESI,[%DebugWA+16-1]
       CALL Debug32@RT.StoESI4:
       MOV AL,'_'
       STOSB
       CALL Debug32@RT.StoESI4:
       JMP Debug32@RT.Eol:
      ENDP1 .R2K:

.R2MM:PROC1          ; Display two MMx with ordinals DL and DL+4 stored in %DebugWA.
       MOV AX,'MM'   ; Returns EAX,ECX,ESI=?, EDI=+23.
       STOSW
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA ESI,[%DebugWA+7]
       MOV ECX,4
.R2MM4:CALL Debug32@RT.StoESI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM4:
       DEC EDI
       MOV AH,16
       CALL Debug32@RT.AHsp:
       MOV AX,'MM'
       STOSW
       MOV AX,'4='
       OR AL,DL
       STOSW
       ADD ESI,8+8
       MOV CL,4
.R2MM8:CALL Debug32@RT.StoESI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM8:
       CALL Debug32@RT.Eol1:
       MOV AH,10
       JMPN Debug32@RT.AHsp:
     ENDP1 .R2MM:

.r80:PROC1  ; Display the contents from %DebugWA of CH-th FPU register at EDI.
      PUSH ECX        ; DX=register property: 00b=VALid, 01b=NULl, 10b=NAN, 11b=EMPty
      MOV AX,'ST'     ; Returns EAX=?,ESI=?, EDI=+33.  ECX is unchanged.
      STOSW
      MOV AX,'0='
      OR AL,CH
      STOSW
      MOV CL,DL
      AND CL,11b
      MOV EAX,'VAL:'
      DEC CL
      JS .8D:
      MOV EAX,'NUL:'
      DEC CL
      JS .8D:
      MOV EAX,'NAN:'
      DEC CL
      JS .8D:
      MOV EAX,'EMP:'
  .8D:STOSD
      MOV ESI,ECX     ; CH is ST ordinal 0..7.
      SHR ESI,8       ; ESI=0,1,2,3,4,5,6,7.
      SAL ESI,1
      MOV EAX,ESI     ; EAX=0,2,4,6,8,10,12,14.
      SAL ESI,2
      ADD ESI,EAX     ; ESI=0,10,20,30,40,50,60,70.
      LEA ESI,[%DebugWA+28+ESI+9]
      MOV ECX,5
  .8G:CALL Debug32@RT.StoESI2:
      MOV AL,'_'
      STOSB
      LOOP .8G:
      DEC EDI        ; Omit the last underscore.
      POP ECX
      RET
     ENDP1 .r80:

.EXC:PROC1 ; Display exceptions from bits 5..0 of CL. Returns EAX,ECX,EDI changed.
     MOV EAX,'EXC='
     STOSD
     AND CL,0x3F
     JNZ .E0:
     MOV EAX,'NONE'
     STOSD
     JMP .E9:
.E0: SHR CL,1
     JNC .E1:
     MOV EAX,'INV+'
     STOSD
.E1: SHR CL,1
     JNC .E2:
     MOV EAX,'DEN+'
     STOSD
.E2: SHR CL,1
     JNC .E3:
     MOV EAX,'DIV0'
     STOSD
     MOV AL,'+'
     STOSB
.E3: SHR CL,1
     JNC .E4:
     MOV EAX,'OVF+'
     STOSD
.E4: SHR CL,1
     JNC .E5:
     MOV EAX,'UNF+'
     STOSD
.E5: SHR CL,1
     JNC .E8:
     MOV EAX,'PREC'
     STOSD
     INC EDI
.E8: DEC EDI          ; Dismiss the last '+'.
.E9: RET
    ENDP1 .EXC:

.ROUND:PROC1 ; Display "ROUND=NEAR" from bits 0..1 of CL. Returns EAX,ECX,EDI changed.
        MOV EAX,'ROUN'
        STOSD
        MOV AX,'D='
        STOSW
        MOV EAX,'NEAR'
        AND CL,11b
        JZ .R9:
        DEC CX
        MOV EAX,'DOWN'
        JZ .R9:
        DEC CX
        MOV EAX,'UP  '
        JZ .R9:
        MOV EAX,'ZERO'
.R9:    STOSD
        RET
       ENDP1 .ROUND:

.Flag:  PROC1 ; Display flag named AX under mask ECX as "CF=1 ". Returns EDI=++, AX=?.
         STOSW
         MOV AL,'='
         STOSB
         MOV AX,'0 '
         TEST [EBP+32],ECX
         JZ .Flag0:
         INC AL
.Flag0:  STOSW
         RET
        ENDP1 .Flag:

.AHsp:PROC1 ; Display AH spaces. Returns AX=0x0020, EDI=+AH.
       MOV AL,' '
.sp:   STOSB
       DEC AH
       JNZ .sp:
       RET
      ENDP1 .AHsp:

.Eol1:PROC1  ; Discard the last emited char, store CR+LF - make new line. Returns EDI=+1, AX=0x0A0D.
       DEC DI
      ENDP1 .Eol1:                 ; Continue through .Eol:.
.Eol: PROC1  ; Store CR+LF - force EndOfLine. Returns DI=+2, AX=?.
       MOV AX,0x0A0D
       STOSW
       RET
      ENDP1 .Eol:

.r32: PROC1 ; Store r32 from [ESI](MSB)..[ESI-3](LSB) with name in EAX to EDI as "EBP=CCCC_DDDD ".
       STOSD                                     ; Returns ESI=-4, EDI=+14, EAX=?.
.r32a: ROL EAX,8
       TEST AL
       JNZ .r32b:
       DEC EDI
       JMP .r32a:
.r32b: MOV AL,'='
       STOSB
      ENDP1 .r32:                                 ; Continue through .StoSI_4:

.StoESI_4:PROC1 ; Store [ESI+0](MSB),[ESI-1],[ESI-2],[ESI-3] as "00FF_FEFD " to EDI. Returns ESI=-4, EDI=+10, EAX=?.
         CALL Debug32@RT.StoESI2:
         MOV AL,'_'
         STOSB
         CALL Debug32@RT.StoESI2:
         MOV AL,' '
         STOSB
         RET
         ENDP1 .StoESI_4

.r16: PROC1 ; Store r16 from [ESI](MSB),[ESI-1](LSB) with name in EAX to EDI as "CS=CCCC ".
       STOSD                                          ; Returns ESI=-2, EDI=+8..10, AX=?.
.r16a: ROL EAX,8
       TEST AL
       JNZ .r16b:
       DEC EDI
       JMP .r16a:
.r16b: MOV AL,'='
       STOSB
       CALL Debug32@RT.StoESI2
       MOV AL,' '
       STOSB
       RET
     ENDP1 .r16:

.StoESI4:PROC1 ; Store [ESI+0](MSB),[ESI-1],[ESI-2],[ESI-3] as "00FFFEFD" to EDI. Returns ESI=-4, EDI=+8, EAX=?.
         CALL Debug32@RT.StoESI2:
        ENDP1 .StoESI4:  ; Continue through .StoESI2:
.StoESI2:PROC1 ; Store [ESI+0](MSB),[ESI-1](LSB) as "0000" to EDI. Returns ESI=-2, EDI=+4, EAX=?.
         LODSB
         DEC ESI,ESI
         CALL Debug32@RT.StoAL:
         LODSB
         DEC ESI,ESI
        ENDP1 .StoESI2: ; Continue through .StoAL:
.StoAL:PROC1 ; Store AL as 2 hexa chars "FF" to EDI. Returns EDI=+2, EAX=?.
        MOV AH,AL
        SHR AL,4
        ADD AL,0x90
        DAA
        ADC AL,0x40
        DAA
        STOSB
        MOV AL,AH
        AND AL,0x0F
        ADD AL,0x90
        DAA
        ADC AL,0x40
        DAA
        STOSB
        RET
       ENDP1 .StoAL:

.StoDD:PROC1  ; Store unsigned 32bit integer ECX as decimal string at EDI.
        XOR EAX,EAX     ; Clobbers EAX,EBX,ECX,EDX,ESI. EDI=incremented.
decim   %FOR 1_000_000_000, 100_000_000, 10_000_000, \
             1_000_000, 100_000, 10_000, 1_000, 100, 10, 1
          MOV EBX,%decim ; EBX is divisor 1_000_000_000..1.
          CALL .1digit:
        %ENDFOR decim
          RET
.1digit:PROC1
          SUB AL,AL
      .1A:SUB ECX,EBX    ; Use repeated subtraction instead of division.
          JB .1D:
          INC AL
          JMP .1A:
      .1D:ADD ECX,EBX    ; Rollback the last subtraction.
          TEST AX
          JZ .1Z:        ; Do not display unsignificant zeros.
          OR AX,0x0130   ; AH=flag that all following zeros are significant.
          STOSB          ; Store one significant decimal digit.
      .1Z:RET
        ENDP1 .1digit:
       ENDP1 .StoDD:

     ENDP1 Debug32@RT
    %ENDIF Debug32
Debug64@RT
Debug64  %IF %Width=64      ; 64bit mode.
n         %FOR 12..4, STEP= -4
            %IF "%DebugSN[%n-3..%n]" === ""
              PUSHQ 0, IMM=DWORD
            %ELSE
              PUSHQ "%DebugSN[%n-3..%n]",IMM=DWORD
            %ENDIF
          %ENDFOR n
          PUSHQ %^SourceLine, IMM=DWORD
          %IF REGTYPE# %DumpAddr = 'Q'
            PUSH %DumpAddr
          %ELSE
            PUSHQ %DumpAddr,  IMM=DWORD ; Macro Debug Imm can dump in lower 2 GB only.
          %ENDIF
          PUSHQ %DebugFg,     IMM=DWORD
          PUSHQ %CallbackProc,IMM=DWORD
          CALL Debug64@RT::
Debug64@RT::PROC1
     PUSHFQ
     PUSH RAX,RCX,RDX,RBX,RBP,RSI,RDI
     MOV RBP,RSP
     CLD
%DebugSt %SET RBP+80        ; Macro Debug internal status flags.
%DebugWA %SET RBP-128       ; Working area 128 bytes for registers and FPU environment.
     SUB RSP,128+8720       ; Allocate room on stack for %DebugWA and 109 lines of formated output.
     MOV RDI,RSP
     CALL .Eol:
     MOV RAX,'#### Deb'
     STOSQ
     MOV RAX,'ug at " '
     STOSQ
     DEC RDI
     LEA RSI,[RBP+104]      ; Display %^SourceName.
     MOV ECX,24
 .G1:LODSB
     CMP AL,0
     JE .G2:
     CMP AL,-1
     JE .G2:
     STOSB
 .G2:LOOP .G1:
     MOV AX,'"{'
     STOSW
     MOV ECX,[RBP+96]        ; Display %^SourceLine.
     CALL .StoDD:            ; Conversion to decimal number at RDI {simplified StoD).
     MOV AL,'}'
     STOSB
     MOV RCX,RSP
     ADD RCX,57              ; Continue at 57th column of output line.
     SUB RCX,RDI
     JBE .G4:
     MOV AL,' '
     REP STOSB
 .G4:                        ; Display segment registers.
seg  %FOR FS,GS
       MOV EAX,%seg
       MOV [%DebugWA],EAX
       MOV EAX,'%seg'
       LEA RSI,[%DebugWA+1]
       CALL .r16:
     %ENDFOR seg
     CALL .Eol1:
     MOV RAX,'GPR: FL='
     STOSQ
     LEA RSI,[RBP+56+7]      ; Display flags.
     CALL .StoRSI_8:
     MOV EAX,'OF'
     MOV CX,0x0800
     CALL .Flag:
     MOV AX,'DF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'IF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'TF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'SF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'ZF'
     SHR ECX,1
     CALL .Flag:
     MOV AX,'AF'
     SHR ECX,2
     CALL .Flag:
     MOV AX,'PF'
     SHR ECX,2
     CALL .Flag:
     MOV AX,'CF'
     SHR ECX,2
     CALL .Flag:
     CALL .Eol1:
     MOV EAX,' RAX'            ; Display general-purpose registers.
     LEA RSI,[RBP+48+7]
     CALL .r64:
     MOV EAX,'RCX'
     CALL .r64:
     MOV EAX,'RDX'
     CALL .r64:
     CALL .Eol1:
     MOV EAX,' RBX'
     CALL .r64:
     LEA RAX,[RBP+128]         ; Original RSP.
     MOV [%DebugWA],RAX
     LEA RSI,[%DebugWA+7]
     MOV EAX,'RSP'
     CALL .r64:
     MOV EAX,'RBP'
     LEA RSI,[RBP+16+7]
     CALL .r64:
     CALL .Eol1:
     MOV EAX,' RSI'
     CALL .r64:
     MOV EAX,'RDI'
     CALL .r64:
     MOV RAX,[RBP+64]
     SUB EAX,7*5+1*5   ; Original RIP was 7 instructions PUSHQ IMM=D and 1 CALLN above return.
     MOV [%DebugWA],RAX
     MOV EAX,'RIP'
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     CALL .Eol1:
     MOV EAX,'  R8'
     MOV [%DebugWA],R8
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     MOV EAX,' R9'
     MOV [%DebugWA],R9
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     MOV EAX,'R10'
     MOV [%DebugWA],R10
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     CALL .Eol1:
     MOV EAX,' R11'
     MOV [%DebugWA],R11
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     MOV EAX,'R12'
     MOV [%DebugWA],R12
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     MOV EAX,'R13'
     MOV [%DebugWA],R13
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     CALL .Eol1:
     MOV EAX,' R14'
     MOV [%DebugWA],R14
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     MOV EAX,'R15'
     MOV [%DebugWA],R15
     LEA RSI,[%DebugWA+7]
     CALL .r64:
     CALL .Eol1:
     TESTD [%DebugSt],%DebugPRIV
     JZ .FPU:
.PRIV:MOV RAX,'PRIV:   '           ; Display privileged registers CR,DR.
     STOSQ
     CALL .TestPRIV:
     JC .FPU:
     MOV RAX,DR0
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,DR3
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR3'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV RAX,CR0
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR0'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,CR4
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR4'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV RAX,DR1
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR1'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,DR6
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR6'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV RAX,CR2
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR2'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,CR4
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR4'
     CALL .r32:
     CALL .Eol1:
     MOV AH,5
     CALL .AHsp:
     MOV RAX,DR2
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR2'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,DR7
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'DR7'
     CALL .r32:
     MOV AH,10
     CALL .AHsp:
     MOV RAX,CR3
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR3'
     CALL .r32:
     MOV AL,' '
     STOSB
     MOV RAX,CR8
     MOV [%DebugWA],EAX
     LEA RSI,[%DebugWA+3]
     MOV EAX,'CR8'
     CALL .r32:
     CALL .Eol1:
.FPU:TESTD [%DebugSt],%DebugFPU
     JZ .MMX:
     MOV EAX,'FPU:'        ; Display FPU in 64bit mode.
     STOSD
     MOV AH,17
     CALL .AHsp:
     FNSAVE [%DebugWA]     ; Area for FPU registers and FPU environment.
     MOV EAX,'FCW'         ; Display FPU environment as dwords.
     LEA RSI,[%DebugWA+00+3]
     CALL .r32:
     MOV EAX,'FSW'
     LEA RSI,[%DebugWA+04+3]
     CALL .r32:
     MOV EAX,'FTW'
     LEA RSI,[%DebugWA+08+3]
     CALL .r32:
     CALL .Eol1:
     MOV AH,7
     CALL .AHsp:
     MOV EAX,'FIO'
     LEA RSI,[%DebugWA+12+3]
     CALL .r32:
     MOV EAX,'FIS'
     LEA RSI,[%DebugWA+16+3]
     CALL .r32:
     MOV EAX,'FDO'
     LEA RSI,[%DebugWA+20+3]
     CALL .r32:
     MOV EAX,'FDS'
     LEA RSI,[%DebugWA+24+3]
     CALL .r32:
     CALL .Eol1:
     MOV ECX,[%DebugWA+04]  ; FSW.
     MOV AL,' '             ; Display FPU environment as bitfields.
     STOSB
     MOV EAX,'C0(C'
     STOSD
     MOV EAX,'F)=0'
     STOSD
     DEC EDI
     MOV AL,'0'
     SHR CH,1
     ADC AL,0
     STOSB
     MOV EAX,' C1='
     STOSD
     MOV EAX,'0 C2'
     SHR CH,1
     ADC AL,0
     STOSD
     MOV EAX,'(PF)'
     STOSD
     MOV EAX,'=0 C'
     SHR CH,1
     ADC AH,0
     STOSD
     MOV EAX,'3(ZF'
     STOSD
     MOV AL,')'
     STOSB
     MOV DL,CH             ; Save TOP to EDX, bits 0..2.
     MOV EAX,'=0 T'
     SHR CH,4
     ADC AH,0
     STOSD
     MOV EAX,'OP=0'
     AND DL,111b
     SHL EDX,24
     ADD EAX,EDX
     STOSD
     MOV AL,' '
     STOSB
     MOV ECX,[%DebugWA+00]  ; FCW.
     SHR ECX,10
     CALL .ROUND:
     MOV EAX,' PRE'
     STOSD
     MOV AX,'C='
     STOSW
     MOV ECX,[%DebugWA+00]  ; FCW.
     SHR ECX,8
     MOV EAX,'DWOR'
     MOV DL,'D'
     AND CL,11b
     JZ .F4:
     DEC CL
     MOV EAX,'?   '
     MOV DL,' '
     JZ .F4:
     DEC CL
     MOV EAX,'QWOR'
     MOV DL,'D'
     JZ .F4:
     MOV EAX,'TBYT'
     MOV DL,'E'
.F4: STOSD
     MOV EAX,EDX
     STOSB
     MOV AL,' '
     STOSB
     MOV ECX,[%DebugWA+04] ; FSW.
     CALL .EXC:
     CALL .Eol:            ; Display 8 FPU registers.
     MOV EDX,[%DebugWA+08] ; FTW, each two bits specify ST property.
     MOV ECX,[%DebugWA+04] ; FSW.
     AND CH,00111000b      ; Which register is currently ST0 (top of FPU stack).
     SHR ECX,10
     ROR DX,CL             ; Property of ST0 is now in two LSbits of DX.
     SUB ECX,ECX           ; CH=ordinal of ST register (0..7).
.F1: MOV AL,' '
     STOSB
     CALL .r80:            ; Display the property and contents of one FPU register.
     MOV AH,7
     CALL .AHsp:
     ADD CH,4
     ROR DX,4*2
     CALL .r80:
     CALL .Eol:
     SUB CH,3
     ROL DX,3*2
     CMP CH,4
     JNE .F1:
     FRSTOR [%DebugWA]
.MMX:TESTD [%DebugSt],%DebugMMX
     JZ .AVX512:
     MOV EAX,'MMX:'            ; Display MMX
     STOSD
     MOV AH,6
     CALL .AHsp:
m    %FOR 0..3
       MOV DL,%m               ; %m=MMx register ordinal, left column.
       MOVD [%DebugWA+0],MM%m
       %m4 %SETA %m+4          ; %m4=MMx register ordinal, right column.
       MOVD [%DebugWA+8],MM%m4
       CALL .R2MM:             ; Display two MMx registers.
     %ENDFOR m
     SUB RDI,10

.AVX512:TESTD  [%DebugSt],%DebugAVX512
     JZ .AVX:
     MOV RAX,'AVX512: '
     STOSQ
     DEC RDI
     CALL .TestAVX512:
     JC .AVX:
     INC RDI
     CALL .MXCSR:
k    %FOR 0..3
       MOV DL,%k
       KMOVQ [%DebugWA+0],K%k
       %k4 %SETA %k+4
       KMOVQ [%DebugWA+8],K%k4
       CALL .R2K:
     %ENDFOR k
z    %FOR 0..7
        MOV DL,%z
        VMOVUPD [%DebugWA],ZMM%z
        CALL .ZMM:
     %ENDFOR
     JMP .End:

.AVX:TESTD [%DebugSt],%DebugAVX
     JZ .SSE:
     MOV RAX,'AVX:    '
     STOSQ
     DEC RDI
     CALL .TestAVX:
     JC .SSE:
     MOV AL,' '
     STOSB
     CALL .MXCSR:
y    %FOR 0..15
       VMOVUPS [%DebugWA],YMM%y
       MOV CL,%y
       CALL .YMM:
     %ENDFOR y
     JMP .End:

.SSE:TESTD [%DebugSt],%DebugSSE
     JZ .Dump:
     MOV EAX,'SSE:'             ; Display SSE.
     STOSD
     CALL .TestSSE:
     JC .End:
     MOV EAX,'    '
     STOSD
     CALL .MXCSR:
x    %FOR 0..15
       MOVDQU [%DebugWA],XMM%x
       MOV CL,%x
       CALL .XMM:
     %ENDFOR x

.Dump:MOV RCX,[%DebugSt]
     TEST CL,%DebugDump
     JZ .End:
     MOV EAX,'Dump'            ; Display hexadecimal and character dump.
     STOSD
     MOV AL,' '
     STOSB
     MOVZX ECX,CH
     INC ECX                   ; Dump size.
     MOV RAX,[RBP+88]          ; Dump address.
     ADD RAX,RCX
     MOV [%DebugSt],RAX        ; %DebugSt is now used as the end offset of dumped area.
     CALL .StoDD:              ; Number of ECX dumped bytes.
     MOV RAX,' bytes a'
     STOSQ
     MOV AX,'t '
     STOSW
     LEA RSI,[RBP+88+7]        ; Dumped address.
     CALL .StoRSI_4:
     MOVB [RDI-1],'_'
     CALL .StoRSI_4:
     DEC RDI
     MOV AL,':'
     STOSB
     CALL .Eol:
     MOV RDX,[RBP+88]          ; Dumped address.
     MOV RAX,RDX
     AND AL,0xF0
     MOV [RBP+88],RAX          ; Start offset of dumped area rounded down.
.D2: ; Print dumped address lower DWORD.
     LEA RSI,[RBP+88+3]
     CALL .StoRSI_4:
     DEC RDI
     MOV AX,': '
     STOSW
     LEA RBX,[RDI+60-12]       ; RBX points to the first char-dump column.
     MOV ECX,16                ; Number of bytes dumped in one line.
.D3: CMP RDX,[RBP+88]          ; DumpAddress.
     JA .D5:
     CMP RDX,[%DebugSt]        ; Test if above dumped area.
     JAE .D5:
     MOV AL,[RDX]              ; Inside dumped area.
     MOV AH,AL
     CMP AL,' '
     JNB .D4:
     MOV AL,'.'                ; Replace controls with fullstop.
.D4: MOV [RBX],AL
     INC RBX
     MOV AL,AH
     CALL .StoAL:
     MOV AL,' '
     STOSB
     INC RDX
     JMP .D7:
.D5: MOV AX,'__'               ; When offset RDX is outside dumped area, use underscores.
     MOV [RBX],AL
     INC RBX
     STOSW
     MOV AL,' '
     STOSB
.D7: INCQ [RBP+88]
     LOOP .D3:
     ADD RDI,16                ; Skip the already written char column.
     CALL .Eol:
     CMP RDX,[%DebugSt]        ; Test if above dumped area.
     JB .D2:

.End:MOV RCX,RDI                ; Output text is created at RSP..RDI.
     MOV RSI,RSP
     SUB EAX,EAX
     STOSB                      ; Zero-terminate the output text.
     SUB RCX,RSI
     PUSH RBP
      CALL [RBP+72]             ; Perform the callback with string RSI,RCX.
     POP RBP
     MOV RSP,RBP
     POP RDI,RSI,RBP,RBX,RDX,RCX,RAX
     POPFQ
     RET 7*8

.NonAvailable:PROC1              ; Store " N/A" and end-of-line.
     MOV EAX,' N/A'
     STOSD
     MOV AX,0x0A0D
     STOSW
     STC
     RET
    ENDP1 .NonAvailable:

.TestPRIV:PROC1  ; Returns CF=1 if not in ring 0.
     MOV ECX,CS
     AND ECX,11b                   ; ECX is privilege level 0..3.
     JNZ Debug64@RT.NonAvailable:
     RET
    ENDP1 .TestPRIV:

;.TestMMX:PROC1  ; MMX is always available in 64bit CPU.

.TestSSE:PROC1  ; Returns CF=1 if CPU doesn't support SSE2. Destroys EAX,EBX,ECX,EDX.
       MOV EAX,1
       CPUID
       BT EDX,26                ; CPUID.1:EDX.SSE2 (required for MOVDQU instruction).
       JNC Debug64@RT.NonAvailable:
       CLC
.T9:   RET
      ENDP1 .TestSSE:

.TestAVX:PROC1  ; Returns CF=1 if CPU doesn't support AVX. Destroys EAX,EBX,ECX,EDX.
       MOV EAX,1
       CPUID
       BT ECX,27                ; CPUID.1:ECX.OSXSAVE
       JNC Debug64@RT.NonAvailable: ; If OSXSAVE is not supported, AVX is neither.
       XOR ECX,ECX
       XGETBV                   ; Read XCR0 to EDX:EAX.
       AND EAX,0000_0110b
       CMP EAX,0000_0110b
       JNE Debug64@RT.NonAvailable: ; XMM and YMM are not supported.
.T9:   RET
      ENDP1 .TestAVX:

.TestAVX512:PROC1 ; Returns CF=1 if CPU doesn't support AVX512. Destroys EAX,EBX,ECX,EDX.
       MOV EAX,1
       CPUID
       BT ECX,27                ; CPUID.1:ECX.OSXSAVE
       JNC Debug64@RT.NonAvailable: ; If OSXSAVE is not supported, AVX512 is neither.
       XOR ECX,ECX
       XGETBV                   ; Read XCR0 to EDX:EAX.
       AND EAX,11100110b
       CMP EAX,11100110b
       JNE Debug64@RT.NonAvailable: ; ZMM are not supported.
       MOV EAX,7
       XOR ECX,ECX
       CPUID                    ; Is AVX512Foundation supported?
       BT EBX,16                ; CPUID.7.0:EBX.AVX512F
       JNC Debug64@RT.NonAvailable: ; Foundation not supported.
       CLC
.T9:   RET
      ENDP1 .TestAVX512:


.XMM:PROC1  ; Display CL-th register XMM. Changes RAX,RCX,RSI,RDI.
      CMP CL,9
      JA .XMM9:
      MOV EAX,' XMM'
      STOSD
      MOV AX,'0='
      OR AL,CL
      STOSW
      JMP .XMMA:
.XMM9:MOV EAX,'XMM '
      STOSD
      DEC RDI
      CALL Debug64@RT.StoDD:
      MOV AL,'='
      STOSB
.XMMA:LEA RSI,[%DebugWA+15]
      MOV ECX,4
.XMMB:CALL Debug64@RT.StoRSI4:
      MOV AL,'_'
      STOSB
      LOOP .XMMB
      JMP Debug64@RT.Eol1:
     ENDP1 .XMM:

.YMM:PROC1  ; Display CL-th register YMM.  Changes RAX,RCX,RSI,RDI.
      CMP CL,9
      JA .YMM9:
      MOV EAX,' YMM'
      STOSD
      MOV AX,'0='
      OR AL,CL
      STOSW
      JMP .YMMA:
.YMM9:MOV EAX,'YMM '
      STOSD
      DEC RDI
      CALL Debug64@RT.StoDD:
      MOV AL,'='
      STOSB
.YMMA:LEA RSI,[%DebugWA+31]
      MOV ECX,8
.YMMB:CALL Debug64@RT.StoRSI4:
      MOV AL,'_'
      STOSB
      LOOP .YMMB:
      JMP Debug64@RT.Eol1:
     ENDP1 .YMM:

.ZMM:PROC1  ; Display DL-th register ZMM. DL=0..7 (32bit version).
       CMP DL,9
       JA .ZMM9:
       MOV EAX,' ZMM'
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       JMP .ZMMA:
.ZMM9: MOV EAX,'ZMM '
       STOSD
       DEC RDI
       MOVZX ECX,DL
       CALL Debug64@RT.StoDD:
       MOV AL,'='
       STOSB
.ZMMA: LEA ESI,[%DebugWA+64-1]
       MOV ECX,8
.ZMMB: CALL Debug64@RT.StoRSI4:
       MOV AL,'_'
       STOSB
       LOOP .ZMMB:
       CALL Debug64@RT.Eol:
       CMP DL,9
       JA .YMM9:
       MOV EAX,' YMM'
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       JMP .YMMA:
.YMM9: MOV EAX,'YMM '
       STOSD
       DEC RDI
       MOVZX ECX,DL
       CALL Debug64@RT.StoDD:
       MOV AL,'='
       STOSB
.YMMA: MOV CL,8
.YMMB: CALL Debug64@RT.StoRSI4:
       MOV AL,'_'
       STOSB
       LOOP .YMMB:
       JMP Debug64@RT.Eol1:
      ENDP1 .ZMM:

.MXCSR:PROC1  ; Display MXCSR as hexa and as bitfields FZ=,DZ=,ROUND=,EXC=.
        MOV EAX,'MXCS'
        STOSD
        MOV AX,'R='
        STOSW
        LEA RSI,[%DebugWA]
        STMXCSR [RSI]
        ADD RSI,3
        CALL Debug64@RT.StoRSI2:
        MOV AL,'_'
        STOSB
        CALL Debug64@RT.StoRSI2:
        MOV EAX,' FZ='
        STOSD
        MOV AL,[%DebugWA+1]
        SHR AL,7
        OR AL,'0'
        STOSB
        MOV EAX,' DZ='
        STOSD
        MOV AL,[%DebugWA+0]
        SHR AL,6
        AND AL,1
        OR AL,'0'
        STOSB
        MOV AL,' '
        STOSB
        MOV ECX,[%DebugWA]
        SHR CX,13
        CALL Debug64@RT.ROUND:
        MOV AL,' '
        STOSB
        MOV ECX,[%DebugWA]
        CALL Debug64@RT.EXC:
        JMPN Debug64@RT.Eol:
       ENDP1 .MXCSR:

.R2K: PROC1          ; Display two Kx with ordinals DL and DL+4 stored in %DebugWA.
       MOV EAX,'   K'; Returns EAX,ECX,ESI=?, EDI=+23.
       STOSD
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA RSI,[%DebugWA+8-1]
       CALL Debug64@RT.StoRSI4:
       MOV AL,'_'
       STOSB
       CALL Debug64@RT.StoRSI4:
       MOV AH,16
       CALL Debug64@RT.AHsp:
       MOV AL,'K'
       STOSB
       MOV AX,'4='
       OR AL,DL
       STOSW
       LEA RSI,[%DebugWA+16-1]
       CALL Debug64@RT.StoRSI4:
       MOV AL,'_'
       STOSB
       CALL Debug64@RT.StoRSI4:
       JMP Debug64@RT.Eol:
      ENDP1 .R2K:

.R2MM:PROC1          ; Display two MMx with ordinals DL and DL+4 stored in %DebugWA.
       MOV AX,'MM'   ; Returns EAX,ECX,ESI=?, EDI=+23.
       STOSW
       MOV AX,'0='
       OR AL,DL
       STOSW
       LEA RSI,[%DebugWA+7]
       MOV ECX,4
.R2MM4:CALL Debug64@RT.StoRSI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM4:
       DEC RDI
       MOV AH,16
       CALL Debug64@RT.AHsp:
       MOV AX,'MM'
       STOSW
       MOV AX,'4='
       OR AL,DL
       STOSW
       ADD RSI,8+8
       MOV CL,4
.R2MM8:CALL Debug64@RT.StoRSI2:
       MOV AL,'_'
       STOSB
       LOOP .R2MM8:
       CALL Debug64@RT.Eol1:
       MOV AH,10
       JMP  Debug64@RT.AHsp:
     ENDP1 .R2MM:

.r80:PROC1  ; Display the contents from %DebugWA of CH-th FPU register at EDI.
      PUSH RCX        ; DX=register property: 00b=VALid, 01b=NULl, 10b=NAN, 11b=EMPty
      MOV AX,'ST'     ; Returns RAX=?,RSI=?, RDI=+33.  RCX is unchanged.
      STOSW
      MOV AX,'0='
      OR AL,CH
      STOSW
      MOV CL,DL
      AND CL,11b
      MOV EAX,'VAL:'
      DEC CL
      JS .8D:
      MOV EAX,'NUL:'
      DEC CL
      JS .8D:
      MOV EAX,'NAN:'
      DEC CL
      JS .8D:
      MOV EAX,'EMP:'
  .8D:STOSD
      MOV ESI,ECX     ; CH is ST ordinal 0..7.
      SHR ESI,8       ; ESI=0,1,2,3,4,5,6,7.
      SAL ESI,1
      MOV EAX,ESI     ; EAX=0,2,4,6,8,10,12,14.
      SAL ESI,2
      ADD ESI,EAX     ; ESI=0,10,20,30,40,50,60,70.
      LEA RSI,[%DebugWA+28+RSI+9]
      MOV ECX,5
  .8G:CALL Debug64@RT.StoRSI2:
      MOV AL,'_'
      STOSB
      LOOP .8G:
      DEC RDI        ; Omit the last underscore.
      POP RCX
      RET
     ENDP1 .r80:

.EXC:PROC1 ; Display exceptions from bits 5..0 of CL. Returns RAX,RCX,RDI changed.
     MOV EAX,'EXC='
     STOSD
     AND CL,0x3F
     JNZ .E0:
     MOV EAX,'NONE'
     STOSD
     JMP .E9:
.E0: SHR CL,1
     JNC .E1:
     MOV EAX,'INV+'
     STOSD
.E1: SHR CL,1
     JNC .E2:
     MOV EAX,'DEN+'
     STOSD
.E2: SHR CL,1
     JNC .E3:
     MOV EAX,'DIV0'
     STOSD
     MOV AL,'+'
     STOSB
.E3: SHR CL,1
     JNC .E4:
     MOV EAX,'OVF+'
     STOSD
.E4: SHR CL,1
     JNC .E5:
     MOV EAX,'UNF+'
     STOSD
.E5: SHR CL,1
     JNC .E8:
     MOV EAX,'PREC'
     STOSD
     INC RDI
.E8: DEC RDI          ; Dismiss the last '+'.
.E9: RET
    ENDP1 .EXC:

.ROUND:PROC1 ; Display "ROUND=NEAR" from bits 0..1 of CL. Returns RAX,RCX,RDI changed.
        MOV EAX,'ROUN'
        STOSD
        MOV AX,'D='
        STOSW
        MOV EAX,'NEAR'
        AND CL,11b
        JZ .R9:
        DEC CL
        MOV EAX,'DOWN'
        JZ .R9:
        DEC CL
        MOV EAX,'UP  '
        JZ .R9:
        MOV EAX,'ZERO'
.R9:    STOSD
        RET
       ENDP1 .ROUND:

.Flag:  PROC1 ; Display flag named AX under mask ECX as "CF=1 ". Returns RDI=+5, AX=?.
         STOSW
         MOV AL,'='
         STOSB
         MOV AX,'0 '
         TEST [RBP+56],ECX
         JZ .Flag0:
         INC AL
.Flag0:  STOSW
         RET
        ENDP1 .Flag:

.AHsp:PROC1 ; Display AH spaces. Returns AX=0x0020, RDI=+AH.
       MOV AL,' '
.sp:   STOSB
       DEC AH
       JNZ .sp:
       RET
      ENDP1 .AHsp:

.Eol1:PROC1  ; Discard the last emited char, store CR+LF - make new line. Returns RDI=+1, AX=0x0A0D.
       DEC RDI
      ENDP1 .Eol1:                 ; Continue through .Eol:.
.Eol: PROC1  ; Store CR+LF - force EndOfLine. Returns RDI=+2, AX=?.
       MOV AX,0x0A0D
       STOSW
       RET
      ENDP1 .Eol:

.r64: PROC1 ; Store r64 from [RSI](MSB)..[RSI-7](LSB) with name in EAX to RDI as "RBP=CCCC_DDDD_EEEE_FFFF ".
       STOSD                                     ; Returns RSI=-8, RDI=+24, RAX=?.
.r64a: ROL EAX,8
       TEST AL
       JNZ .r64b:
       DEC RDI
       JMP .r64a:
.r64b: MOV AL,'='
       STOSB
      ENDP1 .r64:                                 ; Continue through .StoRSI_8:

.StoRSI_8:PROC1 ; Store [RSI+0](MSB),[RSI-1]..[RSI-7] as "AAAA_BBBB_CCCC_DDDD " to RDI. Returns RSI=-8, RDI=+20, RAX=?.
       CALL Debug64@RT.StoRSI_4:
       DEC RDI
       MOV AL,'_'
       STOSB
       JMP  Debug64@RT.StoRSI_4:
      ENDP1 .StoRSI_8:

.r32: PROC1 ; Store r32 from [RSI](MSB)..[RSI-3](LSB) with name in EAX to RDI as "EBP=CCCC_DDDD ".
       STOSD                                     ; Returns RSI=-4, RDI=+14, RAX=?.
.r32a: ROL EAX,8
       TEST AL
       JNZ .r32b:
       DEC RDI
       JMP .r32a:
.r32b: MOV AL,'='
       STOSB
      ENDP1 .r32:                                 ; Continue through .StoRSI_4:

.StoRSI_4:PROC1 ; Store [RSI+0](MSB),[RSI-1],[RSI-2],[RSI-3] as "00FF_FEFD " to RDI. Returns RSI=-4, RDI=+10, RAX=?.
         CALL Debug64@RT.StoRSI2:
         MOV AL,'_'
         STOSB
         CALL Debug64@RT.StoRSI2:
         MOV AL,' '
         STOSB
         RET
         ENDP1 .StoRSI_4

.r16: PROC1 ; Store r16 from [RSI](MSB),[RSI-1](LSB) with name in EAX to RDI as "CS=CCCC ".
       STOSD                                          ; Returns RSI=-2, RDI=+8..10, AX=?.
.r16a: ROL EAX,8
       TEST AL
       JNZ .r16b:
       DEC RDI
       JMP .r16a:
.r16b: MOV AL,'='
       STOSB
       CALL Debug64@RT.StoRSI2
       MOV AL,' '
       STOSB
       RET
     ENDP1 .r16:

.StoRSI4:PROC1 ; Store [RSI+0](MSB),[RSI-1],[RSI-2],[RSI-3] as "00FFFEFD" to RDI. Returns RSI=-4, RDI=+8, RAX=?.
         CALL Debug64@RT.StoRSI2:
        ENDP1 .StoRSI4:  ; Continue through .StoESI2:
.StoRSI2:PROC1 ; Store [RSI+0](MSB),[RSI-1](LSB) as "0000" to RDI. Returns RSI=-2, RDI=+4, RAX=?.
         LODSB
         DEC RSI,RSI
         CALL Debug64@RT.StoAL:
         LODSB
         DEC RSI,RSI
        ENDP1 .StoRSI2: ; Continue through .StoAL:
.StoAL:PROC1 ; Store AL as 2 hexa chars "FF" to RDI. Returns RDI=+2, RAX=?.
        MOV AH,AL
        SHR AL,4
        ADD AL,'0'
        CMP AL,'9'
        JNA .SAL1:
        ADD AL,'A'-'0'-10
  .SAL1:STOSB
        MOV AL,AH
        AND AL,0x0F
        ADD AL,'0'
        CMP AL,'9'
        JNA .SAL2:
        ADD AL,'A'-'0'-10
  .SAL2:STOSB
        RET
       ENDP1 .StoAL:

.StoDD:PROC1  ; Store unsigned 32bit integer in RCX as decimal string at RDI.
        XOR EAX,EAX     ; Returns RAX,RBX,RCX=?, RDI=incremented.
decim   %FOR 1_000_000_000, 100_000_000, 10_000_000, \
             1_000_000, 100_000, 10_000, 1_000, 100, 10, 1
          MOV EBX,%decim ; RBX is divisor 1_000_000_000..1.
          CALL .1digit:
        %ENDFOR decim
        RET
.1digit:PROC1
          SUB AL,AL
      .1A:SUB RCX,RBX    ; Use repeated subtraction instead of division.
          JB .1D:
          INC AL
          JMP .1A:
      .1D:ADD RCX,RBX    ; Rollback the last subtraction.
          TEST EAX
          JZ .1Z:        ; Do not display unsignificant zeros.
          OR AX,0x0130   ; AH=flag that all following zeros are significant.
          STOSB          ; Store one significant decimal digit.
      .1Z:RET
        ENDP1 .1digit:
       ENDP1 .StoDD:

     ENDP1 Debug64@RT::
    %ENDIF Debug64
    EUROASM POP          ; Restore W2340.
  %ENDIF enabled
 %ENDMACRO Debug
 ENDHEAD debug

▲Back to the top▲