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 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 macroinstructionDebug
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.
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
.
DS:rSI
, whose size is in rCX
.
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: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.
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:
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 |
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 |
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. |
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 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=
.
Debug %MACRO DumpAddress, Size=16, CallbackProc=DebugOutput::, Width=%^WIDTH, \
PRIV=%^PRIV, FPU=%^FPU, MMX=%^MMX, SIMD=%^SIMD
enabled %IF %^DEBUG ; Ignore this macro when debugging is not enabled by EUROASM DEBUG=ON
.
%comment ; todo: uncomment after release
defined %IF %^PASS > 1 && TYPE#(%CallbackProc) = '?'
%ERROR Debug callback procedure "%CallbackProc" was not defined.
%EXITMACRO Debug
%ENDIF defined
%endcomment
EUROASM PUSH,NOWARN=2340; Do not complain of missing €ASM CPU= | SIMD= options.
%DebugPRIV %SETA 0x01 ; Declare names of Debug internal status flags.
%DebugFPU %SETA 0x02
%DebugMMX %SETA 0x04
%DebugSSE %SETA 0x08
%DebugAVX %SETA 0x10
%DebugAVX512 %SETA 0x20
%DebugDump %SETA 0x80
%DumpSize %SET %Size
%IF TYPE# %DumpSize != 'N'
%ERROR ID=5711, 'Required dump Size= must be specified as a plain number.'
%DumpSize %SETA 16
%ENDIF
%IF %DumpSize #<= 0 ;>Saturate to minimum 1.
%DumpSize %SETA 1
%ENDIF
%IF %DumpSize #>= 256 ; Saturate to maximum 256.
%DumpSize %SETA 256
%ENDIF ; Dump size 1..256 will be decremented and encoded in %DebugFg bits 8..15.
%DebugFg %SETA (%DumpSize-1)<<8 ;>Decrement and encode in high byte.
%IF %# ; Is ordinal parameter (dump address) present?
%DebugFg %SETA %DebugFg | %DebugDump
%DumpAddr %SET %DumpAddress
%ELSE
%DumpAddr %SET 0
%ENDIF
%IF %PRIV ; Is display of privileged registers DR*,CR* required?
%DebugFg %SETA %DebugFg | %DebugPRIV
%ENDIF
%IF %FPU ; Is display of floating-point registers ST* required?
%DebugFg %SETA %DebugFg | %DebugFPU
%ENDIF
%IF %MMX ; Is display of multimedia registers MM* required?
%DebugFg %SETA %DebugFg | %DebugMMX
%ENDIF
%IF "%SIMD" == "AVX512" ; Is display of SIMD registers ZMM* required?
%DebugFg %SETA %DebugFg | %DebugAVX512 | %DebugAVX | %DebugSSE
%ENDIF
%IF "%SIMD[1..3]" == "AVX" ; Is display of SIMD registers YMM* required?
%DebugFg %SETA %DebugFg | %DebugAVX | %DebugSSE
%ENDIF
%IF "%SIMD[1..3]" == "SSE" ; Is display of SIMD registers XMM* required?
%DebugFg %SETA %DebugFg | %DebugSSE
%ENDIF
%DebugSN %SET %^SourceName%^SourceExt ; Debugged source name will be passed to @RT.
%DebugSS %SETS %DebugSN ; Number of characters in the source file name.
TooLong? %IF %DebugSS >12 ; Name longer than 8.3 will be shortened.
%DebugSN %SET %DebugSN[1..5]~~%DebugSN[%&-4..%&] ; Replace the middle with two tildas.
%ENDIF TooLong?
Debug16 %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
or123: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 %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 instructionsPUSHD IMM=D
and 1CALLN
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 %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 instructionsPUSHQ IMM=D
and 1CALLN
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