User:Johnburger/Demo/Ints/Debug/Show
The .Show
routine is mostly just a lot of loading of memory addresses and writing Hex to the screen. There is the main loop, and three support functions:
.Address
displays the current Address (always a multiple of 10h);.Show.Bytes
displays the current row in Bytes, with an ASCII display on the right hand side;.Show.DWords
displays the current row in DWords.
The hard part is actually near the edge of the current Segment's limits. The rest of the code manages discovering the limits and changing the display address in response to keypresses: this code just has to not blow those limits. The rest of the code has already set up the input parameters to handle Expand Down Segments. All this code has to do is compare the current Segment memory pointer (ESI
) against the Minimum (EBX
) and Maximum (EDX
) values, and display spaces if it's outside of those limits.
And the hardest part of that was the .Show.DWords
displayer, since it usually uses LODSD
to read the DWords from the current Data segment. Close to the limit of the Segment, though, this will cause a GPF - no part of the access can breach the Limit. So in that case it devolves to multiple Byte accesses to form as much of the DWord as it can assemble.
Demo/Ints/Debug/Show.inc
;
; Ints/Debug/Show.inc
;
; This module has the various Display functions to show the current Segment.
;-------------------------------------------------------------------------------
; This function shows a screenful of memory, without violating any Segment
; limits. It displays the memory in either Byte (with ASCII) or DWord form.
; Input: DS = Segment to display
; ES = Screen Segment
; EBX = Minimum displayable address (0000_0000h or Limit)
; EDX = Maximum displayable address (Limit or FFFF_FFFFh)
; ESI = Current address to display from
; EBP = .Byte or .DWord function pointer
; Output: EAX, ECX, EDI modified
Ints.Debug.Show:
PUSH ESI
MOV ECX,Debug.Show.Height
MOV EDI,(Debug.VGA.Top*VGA.Cols+Debug.VGA.Left)*2
.Row:
PUSH ECX ; Need this later
CMP ESI,EDX ; Past Maximum?
JA .Row.Blank ; Yes, so blank row
CMP ESI,EBX ; At least Minimum?
JAE .Row.Show ; Yes, so show row normally
LEA EAX,[EBX-Debug.BytesPerRow] ; Is it a partial row?
CMP ESI,EAX ; Below even this?
JA .Row.Show ; No, so show partial row
.Row.Blank:
MOV AH,Debug.Colour.Blank
MOV AL,' ' ; Blank
MOV ECX,Debug.VGA.Width ; Entire row
ADD ESI,Debug.BytesPerRow ; Catching up?
REP STOSW
JMP .Row.Next ; Next row
.Row.Show:
PUSH EDX ; Need this later
CALL .Address
POP EDX
CALL EBP ; Call current Row function
.Row.Next:
ADD EDI,(VGA.Cols-Debug.VGA.Width)*2
POP ECX ; More rows?
LOOP .Row
POP ESI ; Back to top
RET
;...............................................................................
.Address:
MOV AH,Debug.Colour.Addr
MOV CL,4 ; Size of Selector
MOV DX,DS ; Selector to display
CALL Ints.Hex
MOV AL,':' ; Separator
STOSW
MOV CL,8
MOV EDX,ESI ; Address
CALL Ints.Hex
MOV AL,' '
STOSW
STOSW
STOSW
RET
;...............................................................................
; This function displays one row of .BytesPerRow Bytes (if inside Limits), plus
; an ASCII representation on the side.
Ints.Debug.Show.Bytes:
MOV AH,Debug.Colour.Bytes
MOV CL,Debug.BytesPerRow ; Number of Bytes
.Loop:
PUSH ECX
MOV AH,Debug.Colour.ASCII
LEA ECX,[Debug.BytesPerRow+ECX*2] ; Offset to ASCII field
CMP ESI,EBX ; Less than Minimum?
JB .NoByte ; Yes, so skip
CMP ESI,EDX ; More than Maximum?
JA .NoByte ; Yes, so skip
LODSB ; Byte to display
MOV [ES:EDI+ECX*2],AX ; Store ASCII
PUSH EDX
MOV AH,Debug.Colour.Bytes
MOV CL,2 ; Number of nybbles
MOV DL,AL
CALL Ints.Hex
POP EDX
MOV AL,' '
CMP BYTE [ESP],4 ; Don't show in last position
JB .Separator
TEST ESI,0003h
JNZ .Separator
MOV AL,'-'
.Separator:
STOSW
JMP .Next
.NoByte:
MOV AL,' '
INC ESI ; Catching up?
MOV [ES:EDI+ECX*2],AX ; Store ASCII
MOV AH,Debug.Colour.Bytes
MOV ECX,3
REP STOSW
.Next:
POP ECX
LOOP .Loop
; Now blank out any area to the right of the ASCII field
MOV AL,' '
MOV AH,Debug.Colour.ASCII
MOV ECX,Debug.VGA.Width-Debug.Width.Addr-Debug.Width.Byte
ADD EDI,Debug.BytesPerRow*2 ; Go past ASCII field
REP STOSW
RET
;...............................................................................
; This function displays one row of 4 DWords (if inside Limits). There's some
; hairy code about trying to display part-words at the edge of the Limits...
Ints.Debug.Show.DWords:
MOV CL,Debug.BytesPerRow/4 ; Number of DWords
.Loop:
PUSH ECX
CMP ESI,EDX ; More than Maximum?
JA .NoDWord ; Yes, so skip
CMP ESI,EBX ; Less than Minimum?
JAE .TestHi ; No, so TestHi
LEA EAX,[EBX-4] ; Check how FAR less we are
CMP ESI,EAX ; MUCH less?
JBE .NoDWord ; Yes
JMP .Partial ; Get part-DWord
.TestHi:
LEA EAX,[EDX-3] ; Test how close to Maximum
CMP ESI,EAX
JBE .Normal ; Safe to proceed normally
.Partial:
XOR EAX,EAX ; Start with zero
MOV CL,4 ; Number of bytes
.Build:
SHL EAX,8 ; One more byte
CMP ESI,EBX ; Still too low?
JB .Continue ; Yep!
CMP ESI,EDX ; Now too high?
JA .Continue ; Yep!
MOV AL,[ESI] ; Nope, so safe!
.Continue:
INC ESI ; Keep looking
LOOP .Build ; Build DWord
JMP .Show
.Normal:
LODSD ; DWord to display
.Show:
PUSH EDX
MOV CL,8 ; Number of nybbles
MOV EDX,EAX
MOV AH,Debug.Colour.DWords
CALL Ints.Hex
POP EDX
JMP .Separator
.NoDWord:
MOV AH,Debug.Colour.DWords
MOV AL,' '
MOV ECX,8
ADD ESI,4 ; Catching up?
REP STOSW
.Separator:
MOV AL,' '
STOSW
STOSW
.Next:
POP ECX
LOOP .Loop
; Now blank out any area to the right of the Hex field
MOV AL,' '
MOV ECX,Debug.VGA.Width-Debug.Width.Addr-Debug.Width.DWord
REP STOSW
RET