I wanted to get a feel for preemptive multi-tasking. The easiest way I could think of was to write Real Mode boot code.
To compile: nasm bt.asm -o bt.img
I ran this by loading bt.img as a floppy image under VirtualBox. Not sure if it'd work with other emulators.
The code below works, but only the main loop runs (the main loop, task1 loop, and task2 loop are all supposed to run). What am I doing wrong?
BITS 16
ORG 0x7C00
cli
; Save data segment
mov [data], ds
; Get flags
pushf
pop ax
mov bx, sp
; Save main loop SS:SP
mov [stacks + (0 * 4) + 0], bx
mov [stacks + (0 * 4) + 2], ss
; Initialize task1 loop SS:SP
sub bx, 128
mov [stacks + (1 * 4) + 0], bx
mov [stacks + (1 * 4) + 2], ss
; Initialize task2 loop SS:SP
sub bx, 128
mov [stacks + (2 * 4) + 0], bx
mov [stacks + (2 * 4) + 2], ss
; Save old timer handler
xor ax, ax
mov es, ax
mov di, [es:0x08 * 4 + 0]
mov ax, [es:0x08 * 4 + 2]
mov [old_timer + 0], di
mov [old_timer + 2], ax
; Set timer handler
mov word [es:0x08 * 4 + 0], timer
mov [es:0x08 * 4 + 2], cs
sti
; Init common register values
mov ax, 0xB800
mov es, ax
mov di, (80 * 2) * 10
main:
mov bx, 20 * 2
inc byte [es:di + bx]
jmp main
task1:
mov bx, 40 * 2
inc byte [es:di + bx]
jmp task1
task2:
mov bx, 60 * 2
inc byte [es:di + bx]
jmp task2
timer:
; Save registers to current stack
push ds
push es
pusha
; Restore data segment
mov ax, [cs:data]
mov ds, ax
; Initalize current task and next task SS:SP indices
xor bx, bx
xor si, si
cmp word [ntasks], 0
je SkipSwitch
; If more than 1 task is running, set task indices
mov ax, [curtask]
mov bx, ax
inc ax
xor dx, dx
div word [ntasks]
mov si, ax
mov [curtask], ax
shl bx, 2 ; current task
shl si, 2 ; next task
SkipSwitch:
; Save current SS:SP
mov [bx + stacks + 0], sp
mov [bx + stacks + 2], ss
; Restore next SS:SP
mov sp, [si + stacks + 0]
mov ss, [si + stacks + 2]
; Restore registers
popa
pop es
pop ds
; Chain to old timer handler
jmp far [old_timer]
old_timer: dd 0
data: dw 0
stacks: times 4 dd 0
curtask: dw 0
ntasks: dw 2
times 512 - ($ - $$) db 0