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 macroinstructionDebugneeds 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.
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 %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 %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 %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