Code: Select all
;
; ==============
; Nano OS v2.1 copyright by Viktor Peter Kovacs (KVP)
; ============== for the fasm 512 byte os contest of 2004
;
; This is a demo os, written to show some os design
; methods, and to proove that it's possible to put
; a simple multitasking os into 512 bytes...
;
; ---- constants ----
TIMER_IRQN equ (8) ; hw. timer interrupt number
IRQ_BIT equ (512) ; 'irq enable' bit in the flags register
TS_FREE equ (0) ; task states
TS_READY equ (1)
TS_SEND equ (2)
TS_RECV equ (3)
TS_PUTC equ (4)
TS_GETC equ (5)
E_TIDLIMIT equ (0ffffh) ; error code for task resource limit error
TID_NUM equ (8) ; maximal number of tasks allowed
TID_STACK equ (512) ; default task stack size
; ---- startup code ----
start0: ; needed for compatibility
jmp start1
nop
start1:
jmp 07c0h:start2 ; code segment alignement
start2:
cli
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, stack_top
cld
mov di, _bss_start ; zero the bss
mov cx, _bss_end-_bss_start
xor ax, ax
rep stosb
push es ; change the irq vector table
xor bx, bx
mov es, bx
mov ax, [es:TIMER_IRQN*4]
mov [timer0l], ax
mov ax, [es:TIMER_IRQN*4+2]
mov [timer0h], ax
mov ax, timer_irq
mov [es:TIMER_IRQN*4], ax
mov ax, cs
mov [es:TIMER_IRQN*4+2], ax
pop es
mov cx, idle_task ; start idle task
call sys_exec
mov cx, prg1 ; start program 1 (rpc demo - data source)
call sys_exec
mov cx, prg3 ; start program 3 (hello world)
call sys_exec
mov ax, (TID_NUM-1)*2 ; setup and enable the scheduler
mov [ctid], ax
sti
idle_task:
jmp idle_task ; the idle task
; ---- kernel code ----
timer_irq:
push bp ; save task context
push di
push si
push dx
push cx
push bx
push ax
mov ax, [irqflg] ; kernel lock check
or ax, ax
jz timer_irq1
timer_irq0:
pop ax ; restore task context
pop bx
pop cx
pop dx
pop si
pop di
pop bp
db 0eah ; jmp far to old handler (for compatibility)
timer0l dw 00000h
timer0h dw 00000h
timer_irq1:
mov ax, 1 ; lock kernel
mov [irqflg], ax
mov bx, [ctid]
mov ax, [tstate+bx]
timer_cmd1:
cmp ax, TS_SEND ; ipc message send (blocking ipc)
jnz timer_cmd2
mov si, [msgtid+bx]
mov ax, [tstate+si]
cmp ax, TS_RECV
jnz timer_next
mov ax, [msgdat+bx]
mov [msgdat+si], ax
mov [msgtid+si], bx
mov ax, TS_READY
mov [tstate+si], ax
jmp timer_cmd_ok
timer_cmd2:
cmp ax, TS_PUTC ; terminal write
jnz timer_cmd3
mov ax, [msgdat+bx]
call bios_putc
jmp timer_cmd_ok
timer_cmd3:
cmp ax, TS_GETC ; terminal read
jnz timer_next
call bios_getc
mov [msgdat+bx], ax
or ax, ax
jz timer_next
timer_cmd_ok: ; resume task
mov ax, TS_READY
mov [tstate+bx], ax
timer_next:
;; prints task states (see:TS_* constants; red=current task)
;; (used for testing, removed to save code space)
; push es
; mov ax, 0b800h
; mov es, ax
; xor di, di
; mov si, tstate
;debug_print:
; lodsw
; mov ah, 15
; add al, '0'
; stosw
; cmp si, tstate+TID_NUM*2
; jnz debug_print
; mov di, [ctid]
; inc di
; mov al, 12
; stosb
; pop es
timer_next0:
mov bx, [ctid] ; hard realtime round robin scheduler
mov [tstack+bx], sp
timer_next1:
add bx, 2
and bx, (TID_NUM-1)*2
mov [ctid], bx
mov ax, [tstate+bx]
cmp ax, TS_FREE
jz timer_next1
mov sp, [tstack+bx]
xor ax, ax ; unlock kernel
mov [irqflg], ax
jmp timer_irq0
;
; <-
; ax : char
;
bios_putc: ; device i/o function (called internally)
push bp
push bx
mov ah, 00eh
mov bx, 00007h
int 010h
cmp al, 13
jnz bios_putc1
mov al, 10
int 010h
bios_putc1:
pop bx
pop bp
ret
;
; ->
; ax : char (0=no char)
;
bios_getc: ; device i/o function (called internally)
mov ax, 00100h
int 016h
jnz bios_getc1
xor ax, ax
ret
bios_getc1:
mov ax, 00000h
int 016h
xor ah, ah
ret
; ---- standard library ----
;
; EXEC: Starts a new task
;
; <-
; cx : code offset
; (ax, si)
; ->
; bx : new tid (E_TIDLIMIT=out of task slots)
;
sys_exec:
pushf ; task table resource lock
cli
xor bx, bx
sys_exec1:
mov ax, [tstate+bx] ; looks for a free task slot
cmp ax, TS_FREE
jz sys_exec2
add bx, 2
cmp bx, TID_NUM*2
jnz sys_exec1
mov bx, E_TIDLIMIT ; return error code
popf
ret
sys_exec2:
mov dx, bx
mov dh, dl
xor dl, dl
mov si, tstacks+TID_STACK-10*2 ; allocate task stack
add si, dx
mov [tstack+bx], si
mov [si+7*2], cx ; set task startup context (cs:ip,flg)
mov ax, cs
mov [si+8*2], ax
pushf
pop ax
or ax, IRQ_BIT
mov [si+9*2], ax
mov ax, TS_READY
mov [tstate+bx], ax ; activate new task
shr bx, 1
popf ; release task table lock
ret
;
; EXIT: Exits a task
;
sys_exit:
mov bx, [ctid]
mov ax, TS_FREE
mov [tstate+bx], ax
jmp $
;
; SEND: Sends an ipc message
;
; <-
; ax : msg
; bx : dst
; (cx, bp)
;
sys_send:
shl bx, 1
mov cx, TS_SEND
sys_send0:
mov bp, [ctid]
mov [msgdat+bp], ax
mov [msgtid+bp], bx
mov [tstate+bp], cx
sys_send1:
mov ax, [tstate+bp]
cmp ax, TS_READY
jnz sys_send1
ret
;
; RECV: Receives an ipc message
;
; ->
; ax : msg
; bx : dst
; (cx, bp)
;
sys_recv:
mov cx, TS_RECV
sys_recv0:
mov bp, [ctid]
mov [tstate+bp], cx
sys_recv1:
mov ax, [tstate+bp]
cmp ax, TS_READY
jnz sys_recv1
mov ax, [msgdat+bp]
mov bx, [msgtid+bp]
shr bx, 1
ret
;
; PUTC: Outputs a character to the console
;
; <-
; ax : chr
; (bx, cx, bp)
;
sys_putc:
mov cx, TS_PUTC
jmp sys_send0
;
; GETC: Inputs a character from the console
;
; (bx, cx, bp)
; ->
; ax : chr (0=no char)
;
sys_getc:
mov cx, TS_GETC
jmp sys_recv0
; ---- application code ----
;; prg1 v1.0
;prg1:
; call sys_getc
; or ax, ax
; jz prg1
; cmp al, 'H'
; je prg1_1
; call sys_putc
; jmp prg1
;prg1_1:
; mov cx, prg2
; call sys_exec
; jmp prg1
;
; prg1 v2.0
; -on startup, starts a listerner task
; -gets a character from the console and sends it via ipc
; -on the 'H' key, starts a new 'hello world' process
;
prg1:
mov cx, prg2
call sys_exec
mov di, bx
prg1_0:
call sys_getc
or ax, ax
jz prg1_0
cmp al, 'H'
je prg1_1
mov bx, di
call sys_send
jmp prg1_0
prg1_1:
mov cx, prg3
call sys_exec
jmp prg1_0
;
; prg2 v1.0
; -waits for incoming messages via ipc
; -prints the message content on the console
;
prg2:
call sys_recv
call sys_putc
jmp prg2
;
; hello world v1.1
; -prints the text 'Hello World!' on the console
; -exits on completition
;
prg3:
mov si, prg3_str_hello
prg3_1:
lodsb
or al, al
jz prg3_3
call sys_putc
jmp prg3_1
prg3_3:
call sys_exit
prg3_str_hello db 'Hello World!',13,0
; ---- boot flags ----
rb 510-$
dw 0aa55h
; ---- boot stack ----
rb 512
stack_top:
_bss_start: ; uninitialized data area (cleared to 0 on start)
; ---- kernel data ----
irqflg rw 1 ; kernel lock
ctid rw 1 ; current task
tstate rw TID_NUM ; task states
tstack rw TID_NUM ; task stack pointers
msgtid rw TID_NUM ; message destination task ids
msgdat rw TID_NUM ; message data
; ---- application data ----
; ---- task stacks ----
rb (512*4)-$
tstacks:
rb (TID_STACK*TID_NUM)
_bss_end: