| Index | Manual | Download | Source | Macros | Embedded word Case ins.
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
  Shortcut | Mask | Value
  --- | --- | ---
  NT | 4000h | Nested Task flag
  PL | 3000h | I/O Privilege Level 0..3
  OF | 0800h | Overflow Flag
  DF | 0400h | Direction Flag
  IF | 0200h | Interrupt enable Flag
  TF | 0100h | Trap Flag
  SF | 0080h | Sign Flag
  ZF | 0040h | Zero Flag
  AF | 0010h | Auxiliary carry Flag
  PF | 0004h | Parity Flag
  CF | 0001h | Carry Flag

  FPU environment registers
  Shortcut | Offset | Value
  --- | --- | ---
  Width=16 | Width=32|64
  FCW | 0 | 0 | FPU Control Word
  FSW | 2 | 4 | FPU Status Word
  FTW | 4 | 8 | FPU Tag Word
  FIO | 6 | 12 | FPU Instruction pointer Offset
  FIS | 8 | 16 | FPU Instruction pointer Selector
  FDO | 10 | 20 | FPU Data pointer Offset
  FDS | 12 | 24 | FPU Data pointer Selector

  FPU register contents-tags and spec.values
  Shortcut | Tag | Value
  --- | --- | ---
  VAL | 00b | Valid FP value
  NUL | 01b | #ZERO FP value
  NAN | 10b | #NAN or #INF or denormal FP special value
  EMP | 11b | Empty, STx was not used since FINIT.

  FPU and SIMD exceptions
  Shortcut | Offset | Value
  --- | --- | ---
  PREC | 100000b | Precision lost
  UNF | 010000b | UnderFlow
  OVF | 001000b | OverFlow
  DIV0 | 000100b | Divide by 0
  DEN | 000010b | Denormalized operand
  INV | 000001b | Invalid operation
  NONE | 000000b | No 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.
    defined  %IF %^PASS > 1 && TYPE#(%CallbackProc) = '?'
               %ERROR Debug callback procedure "%CallbackProc" was not defined.
               %EXITMACRO Debug
             %ENDIF defined

             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▲