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