this routine hooks interrupt 8h, which can't be pre-empted by any other interrupts. it is the highest priority level interrupt. read that as: DO NOT TRY TO USE INT 1Ch instead! that will kinda work at first, but it's very unrealiable. and it WILL inevitably cause problems. this code has proven very solid for me so far. no problems yet. it took some trial and error to get it right. hopefully this can be of use to somebody. before somebody freaks out over my individual pushes instead of pusha/popa, keep in mind i'd like this to work even on an 8086 or 8088.
Code: Select all
;this is the heart of the RetrOS task/context switching. this routine hooks interrupt 8h
;and when it's done, performs a far jmp to the old handler.
jmp entry
curproc dw 0
maxproc dw 100
hookbusy db 0
count dw 0
entry:
; the FIRST thing we do is make sure no other interrupts are already active that we've pre-empted.
; if there are, and we try to swap registers around in the middle of them your system is going to
; crash and burn hard. this involves reading the in-service register on the i8259 chip.
pushf
push ax
mov al, 0bh ;talk to the 8259 on port 20h
out 20h, al ;ask it what interrupts are in service
in al, 20h ;get our answer
test al, 11111110b ; check to make sure that interrupts 1-7 aren't active
pop ax
jnz reallyfinishedandpopf ; are they? if so, just jump to the end and then we'll far jmp to the old 8h handler
popf
cmp cs:hookbusy, 1 ;has the kernel set the busy flag? for some reason it doesnt want us to taskswitch now?
jz reallyfinished ;then let's bail out because that would be bad
mov cs:hookbusy, 1 ;no, so lets set the busy flag. probably dont need to do this since we can't be re-entrant
inc cs:count ; if we're on int 8h, but meh..
push bp
push di
push si
push ds
push es
push dx
push cx
push bx
push ax
mov ax, 0100h ;set ES to RAM area for the process array
mov es, ax
mov ax, cs:curproc
mov bl, 8
mul bl
mov si, ax
add si, 2 ;we need to save the task's SS:SP
mov es:[si], sp
add si, 2
mov es:[si], ss
findnext:
mov ax, cs:curproc
cmp ax, cs:maxproc
jz resetloop
inc ax
jmp continue
resetloop:
mov ax, 0
continue:
mov cs:curproc, ax
mov bl, 8
mul bl
mov si, ax
mov ax, es:[si]
cmp ax, 1 ;is the process curproc points to runnable?
jnz findnext ;if not, check go back and increment curproc
add si, 2 ;we need to retrieve the task's SS:SP
mov ax, es:[si]
mov sp, ax
add si, 2
mov ax, es:[si]
mov ss, ax
finished:
pop ax ;load registers back from the process' stack
pop bx
pop cx
pop dx
pop es
pop ds
pop si
pop di
pop bp
push ax ;next few lines clear our busy flag
mov al, 0
mov cs:hookbusy, al
pop ax
jmp reallyfinished ;skipping the popf
reallyfinishedandpopf:
popf
reallyfinished:
push ax
push bx
push es
mov ax, 0D00h ;the next few lines dump the old CS and IP of int 8h where they need to go from our vector array.
mov es, ax ;the D00h is where the C kernel keeps an array of vectors. 8h is stored before it hooks this new routine.
mov ax, es:32 ;8 * 4 = 32
mov bx, es:34 ;8 * 4 + 2 = 34
mov cs:jmpip, ax
mov cs:jmpcs, bx
pop es
pop bx
pop ax
db 0EAh ;going to far jump to the original 8h handler
jmpip dw 0
jmpcs dw 0
and this is the array structure my C kernel sets up at segment 100h:
Code: Select all
struct structpid {
unsigned isvalid; //so the task-switcher ISR knows if it should switch into a process, this will be 1 if okay.
unsigned sp;
unsigned ss;
unsigned proctty; //which vtty does this PID use for stdin/stdout?
} far *proc = (void far *)MK_FP(0x100, 0x0);
unsigned far *curproc = (void far *)MK_FP(0xE00, 0x2); //my int 8h routine is at E00:0h, and we will keep track of it's curproc so the kernel can always know what the current running PID is when a system call is made.