User:Johnburger/Demo/Ints/Debug/Key
From OSDev Wiki
< User:Johnburger | Demo
The keyboard handler just has to test against the different keypresses that we want to heed, to affect the current display on the screen. From easiest to hardest:
- <Esc>
Just leave the debugger; - <Del>
Swap between displaying Bytes and DWords by changing theEBP
register (it holds the address of the function toCALL
). - <Up> or <Down> arrow
Subtract or Add (respectively)10h
toESI
(the current memory pointer) unless it's already outside the current Segment's limits. - <PgUp> or <PgDn>
Call the <Up> or <Down> (respectively) functionDebug.Show.Height - 1
times. Why- 1
? It allows a measure of overlap for the user to maintain synchronization with the scrolling. Note that there's no attempt to break out of the loop if the Segment limits have been reached: the code simply ignores the attempt, so it can ignore it repeatedly! - <Left> or <Right> arrow
These are the most complex functions. They Subtract or Add (respectively) the size of a Descriptor to the current value of the currently-displayed Selector, and then the result checked for readability with the CPU'sVERR
(VERify Read) instruction. (That hasn't had a lot of use in your code before, I'll bet!) If the new value is a readable Segment, it becomes the new current Segment. Otherwise, we loop around doing the Subtraction or Addition again. And if the ends of the Descriptor Table are reached, the Table Indicator bit inside the Selector is toggled to access the LDT or GDT, and the loop is continued.
The final keypresses above call the .Check
routine to determine if the Selector is readable, and to toggle the TI bit if necessary. If the new Selector isn't readable, it just returns. If it is, it establishes the new Minimum and Maximum values for the Segment, taking into account if the Descriptor is marked Expand Down.
Demo/Ints/Debug/Key.inc
; ; Ints/Debug/Key.inc ; ; This module handles the various keyboard actions. Ints.Debug.Key: HLT ; Wait for interrupt MOV AL,0 ; Consume any keypress XCHG [FS:Data.Key.Code],AL ; Avoid repeats CMP AL,0 ; Anything there? JE .End ; No, so leave CMP AL,Debug.Key.Quit JE .End ; Quit? CMP AL,Debug.Key.Format JE .Format CMP AL,Debug.Key.Up JE .Up CMP AL,Debug.Key.Down JE .Down CMP AL,Debug.Key.PgUp JE .PgUp CMP AL,Debug.Key.PgDn JE .PgDn CMP AL,Debug.Key.Prev JE Ints.Debug.Desc.Prev CMP AL,Debug.Key.Next JE Ints.Debug.Desc.Next .End: RET ;............................................................................... Ints.Debug.Key.Format: CMP EBP,Ints.Debug.Show.DWords JE .Bytes MOV EBP,Ints.Debug.Show.DWords JMP .End .Bytes: MOV EBP,Ints.Debug.Show.Bytes .End: RET ;............................................................................... Ints.Debug.Key.Up: CMP ESI,EBX ; Reached Minimum? JBE .End ; Yep! Do nothing. SUB ESI,Debug.BytesPerRow ; Move earlier in memory .End: RET ;............................................................................... Ints.Debug.Key.Down: MOV EAX,ESI ; Try some calculations ADD EAX,Debug.Show.Height*Debug.BytesPerRow ; Add in bottom of Screen JO .End ; Overflow means too far! DEC EAX ; Need to, since Limit has -1 CMP EAX,EDX ; Reached Maximum? JAE .End ; Yep, so too far ADD ESI,Debug.BytesPerRow ; Move later in memory .End: RET ;............................................................................... Ints.Debug.Key.PgUp: MOV CL,Debug.Show.Height-1 ; Leave one row the same .Loop: CALL Ints.Debug.Key.Up LOOP .Loop RET ;............................................................................... Ints.Debug.Key.PgDn: MOV CL,Debug.Show.Height-1 ; Leave one row the same .Loop: CALL Ints.Debug.Key.Down LOOP .Loop RET ;............................................................................... Ints.Debug.Desc.Next: MOV EAX,DS ; Get current Descriptor .Loop: ADD AX,x86.Desc_size ; Go to next Descriptor CALL Ints.Debug.Desc.Check JNE .Loop RET Ints.Debug.Desc.Prev: MOV EAX,DS ; Get current Descriptor .Loop: SUB AX,x86.Desc_size ; Go to previous Descriptor CALL Ints.Debug.Desc.Check JNE .Loop RET Ints.Debug.Desc.Check: ; This checks the current Descriptor for readability. If not, it simply returns. ; If it is readable, it sets up the necessary registers; in particular the ; values for Minimum and Maximum. ; One step it does take is to check the result of the just-completed Next or ; Prev. If that over- or under-flowed, it swaps to the other Descriptor Table ; (GDT or LDT). Also note that since there's no easy way to determine the size ; of the current LDT, or even the GDT, it is easiest to simply check all the ; Descriptors. JNC .Readable ; Didn't overflow: keep going XOR EAX,x86.Seg.TI ; Use other Descriptor Table .Readable: VERR AX ; Can I read it? JNZ .End ; Nope. Keep looking! MOV DS,EAX ; Found! LSL EDX,EAX ; Maximum address XOR EBX,EBX ; Minimum address MOV ESI,EBX ; Start at Minimum LAR EAX,EAX ; One more thing... TEST AH,x86.Desc.Mem.Type.Code ; Is it Code? JNZ .Found ; OK, good TEST AH,x86.Desc.Mem.Type.Stack ; No. Is it a Stack? JZ .Found ; No! Phew! XCHG EBX,EDX ; Yes, so everything's upside down! INC EBX ; Or off-by-one NOT EDX ; Or inverted! ; Start at "top" of Segment, less a screenful MOV ESI,0-(Debug.Show.Height*Debug.BytesPerRow) ; Screenful SHR EAX,16 ; Also need to check Gran byte TEST AL,x86.Desc.Mem.Gran.Big ; 32-bit? JNZ .Found ; Yes. MOV AX,0FFFFh ; No. Need to mask off high word AND EDX,EAX ; Maximum address AND ESI,EAX ; Starting address .Found: XOR EAX,EAX ; Set Z flag - Found! .End: RET