User:Johnburger/Demo/Ints/Util

From OSDev Wiki
Jump to navigation Jump to search

There are four utility functions and a useful table that different interrupt handlers may want to use.

Ints.Switch

This function switches to the next available Task. It does it by getting the current Task Register, then looking past that one in the GDT for the next available TSS. If it reaches the end of the GDT it starts from the beginning again. If it finds a different one, it JMPs to it, invoking a Task Switch. Otherwise, it simply returns.

Note that there are some TSSes that should NOT be switched to: in particular, the ones that are waiting around to handle any special Fault exceptions. To flag those as special, I make use of the x86.Desc.Gran.Avail bit in the Descriptor: Intel reserved that bit for system use, and that is what I want to use it for - with TSSes, at least.

For that matter, special TSSes shouldn't be switched away from either. If they were, there'd be bouncing balls all over the carefully-crafted Hex screen contents...

Ints.System

I use the System.TSS flag to mark a TSS as being "special", as described above. But I also need to be able to temporarily mark a normal TSS as special so that I can debug it. These two routines handle this.

.Init

This routine marks the current Task (as indicated in the TR) as a System.TSS - unless it already is one. It returns a flag to indicate what it did, which needs to be saved and passed back to .Done below when the TSS is finished with.

.Done

This routine undoes whatever .Init above did.

Ints.Hex

This routine takes a number of parameters:

  • Value to display;
  • Number of Hex digits to display;
  • Where on the screen to display it;
  • What colour to use;

and displays the Hex representation of the Value, updating the screen position.

Ints.Name

I wanted both the Fault handler and the Debugger to display register contents, but I wanted a minimal overhead. So I designed a simple table with five fields:

  • A one-byte screen location for the Debug window (the Fault window just displays them one below the other);
  • A four-character register name, with internal padding for alignment:
  • A one-byte number of Hex digits to display;
  • A one-byte offset on the stack for the register's location;
  • A one-byte offset within a TSS for the register's location.

It turns out that even when displaying a TSS' contents, some of the values of interest are actually still on the stack. The code can distinguish those, though: the low-down offsets are stack offsets rather than TSS ones.

Demo/Ints/Util.inc

;
; Ints/Util.inc
;

; This module holds Utility functions for various Fault handlers to use

;...............................................................................
; This function switches to the next available Task. Note that it assumes that
; interrupts are OFF!
;
; It cycles through the Global Descriptors looking for Idle TSSs. When it finds
; one, it JMPs to it - stopping the current Task, making it Idle for next time.
;
; Note that I use x86.Desc.Mem.Gran.Avail to flag a System TSS: it may be Idle
; now, but that'd be because nothing's using it yet -  a Fault handler is a
; good example of a System TSS waiting to be used.
;
; Also note that if the TSS that is currently running is a System TSS, I won't
; switch away from it either!

Ints.Switch:

.Type.Mask      EQU             x86.Desc.Type.Present | x86.Desc.Type.Sys | x86.Desc.Type.Type
.IdleTSS        EQU             Type.Sys(TSS, DPL0, 386) ; Target!

                STR             EAX             ; Get current TSS
                TEST            EAX,EAX         ; Switching Tasks yet?
                JZ              .End            ; No, so leave

                PUSH            EBX             ; Need some more registers
                PUSH            DS              ; And a Segment register

                MOV             EBX,EAX
                MOV     WORD    AX,Selector(GDT.Alias, GDT, RPL0)
                MOV             DS,AX           ; To point to the GDT

                ; I know that EAX holds a TSS - but is it a System TSS?
                TEST    BYTE    [EBX+x86.Desc.Mem.Gran],System.TSS
                JNZ             .Kill           ; Yes! Abort!

                PUSH            EDX             ; Now need this

                MOV             EDX,EBX         ; Starting point
.Loop:
                ADD             BX,x86.Desc_size     ; Look at next descriptor
                JZ              .Restart             ; Overflow!
                CMP             BX,[GDT.Alloc.Limit] ; Too far?
                JB              .Continue            ; Not yet...
.Restart:
                MOV             BX,GDT.Limit+1       ; Yes. Start at original Limit
.Continue:
                CMP             EBX,EDX         ; Looped back to here again?
                JE              .Finish         ; Yes, so nothing to switch to

                MOV             AL,[EBX+x86.Desc.Mem.Type] ; Get Descriptor Type
                AND             AL,.Type.Mask   ; Isolate interesting bits
                CMP             AL,.IdleTSS     ; Is it an Idle TSS?
                JNE             .Loop           ; No. Keep looking

                TEST    BYTE    [EBX+x86.Desc.Mem.Gran],System.TSS
                JNZ             .Loop           ; Ignore System TSSs too

                PUSH            EBX             ; Found! So JMP to it!
                PUSH            0               ; (Note that Offset is ignored)
                JMP     FAR     [ESP]           ; Invokes Task Switch
                ADD             ESP,8           ; When JMPs back, continues here
.Finish:
                POP             EDX
.Kill:
                POP             DS
                POP             EBX
.End:
                RET
;...............................................................................
; This function sets the current TSS to be a System TSS.
; Output: EAX = whether it modified the Task. Pass this in EBX to .Done
;         GS  = GDT
Ints.System.Init:
                MOV             AX,Selector(GDT.Alias, GDT, RPL0)
                MOV             GS,AX           ; Point to GDT

                STR             EAX             ; Get current Task
                TEST            EAX,EAX         ; Is there one?
                JZ              .End            ; No, so nothing to do
                TEST    BYTE    [GS:EAX+x86.Desc.Mem.Gran],System.TSS
                JNZ             .System         ; Already a System TSS!
                OR      BYTE    [GS:EAX+x86.Desc.Mem.Gran],System.TSS
                JMP             .End            ; Make it one temporarily
.System:
                XOR             EAX,EAX         ; Already a System TSS! Don't touch!
.End:
                RET
;...............................................................................
; This function reverts any change made by .Init (above)
; Input:  EBX = EAX output from .Init
;         GS  = GDT

Ints.System.Done:
                TEST            EBX,EBX         ; Was current TSS modified?
                JZ              .End            ; No, so just leave
                AND     BYTE    [GS:EBX+x86.Desc.Mem.Gran],~System.TSS
.End:
                RET
;...............................................................................
; This function displays a Hex value on the screen.
; Input:  AH  = Colour to use
;         ECX = Number of digits to display
;         EDX = Value to display
;         EDI = Screen position to display
;         ES  = Screen segment
; Output: AL, EDX modified. ECX zeroed. EDI preserved
Ints.Hex:
                STD                             ; Work backwards
                LEA             EDI,[EDI+ECX*2-2] ; Point to end of number
                PUSH            EDI             ; Save for end
.Loop:
                MOV             AL,DL           ; Get lowest byte
                AND             AL,0Fh          ; Isolate low nybble
                ADD             AL,'0'          ; Turn into ASCII
                CMP             AL,'9'          ; Too far?
                JBE             .NotHex         ; No
                ADD             AL,'A'-'9'-1    ; Yes, so turn into Hex
.NotHex:
                STOSW                           ; Store
                SHR             EDX,4           ; Shift in next nybble
                LOOP            .Loop           ; Loop
                CLD                             ; Assume forwards
                POP             EDI             ; Saved end
                ADD             EDI,2           ; Past last digit
                RET                             ; And return
;-------------------------------------------------------------------------------
; The following table lists the information for each register to show:
; Debug offset, Label, Num Hex chars, Stack offset, TSS offset

Ints.Names:
.Label          EQU             4  ; Width of label
;                              Debug,  Name , W,Stk,TSS
                DB                 0, ' Int', 2, 68, 20 ; On stack
                DB                 0, ' Err', 4, 72, 24 ; On stack
.Regs: ; Only registers from here
                DB              002h, ' EAX', 8, 64, 40
                DB              011h, ' EBX', 8, 52, 52
                DB              020h, ' ECX', 8, 60, 44
                DB              02Fh, ' EDX', 8, 56, 48
                DB              042h, ' ESI', 8, 40, 64
                DB              051h, ' EDI', 8, 36, 68
                DB              060h, ' EBP', 8, 44, 60
                DB              0DAh, ' ESP', 8, 48, 56
                DB              0C7h, ' EIP', 8, 76, 32
                DB              0C2h, '  CS', 4, 80, 76
                DB              082h, '  DS', 4, 32, 84
                DB              08Dh, '  ES', 4, 28, 72
                DB              098h, '  FS', 4, 24, 88
                DB              0A3h, '  GS', 4, 20, 92
                DB              0D5h, '  SS', 4, 16, 80
                DB              06Fh, 'Flag', 8, 84, 36
                DB              0AFh, 'PDBR', 8,  4, 28 ; Minimum NOT on stack
                DB              0F3h, 'Task', 4, 12, 12 ; On stack
                DB              0E9h, ' LDT', 4,  8, 96
.End            EQU             $
;    0000000000000000111111111111111122222222222222223333333333333333
;    0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
;000:  _EAX:########  _EBX:########  _ECX:########  _EDX:########
;040:  _ESI:########  _EDI:########  _EBP:########  Flag:########
;080:  __DS:####  __ES:####  __FS:####  __GS:####   PDBR:########
;0C0:  __CS:####:######## __SS:####:######### _LDT:#### Task:####