;=============================================================================
;
;=============================================================================

MAX_TASKS	equ 8
MAX_STACK	equ 2*512

TS_FREE 	equ 0
TS_READY	equ 1
TS_SEND 	equ 2
TS_RECV 	equ 3

SV_NONE 	equ 0
SV_GETC 	equ 1
SV_PUTC 	equ 2

OP_JMPF 	equ 0eah
OP_IRET 	equ 0cfh

CPU_HOG 	equ 0
REAL_86 	equ 0	;help me!

SP_HACK 	equ (REAL_86*2)

;=============================================================================
;
;=============================================================================

use16
org 7c00h

;7c00h - 600h = 30208 bytes
;
;rb MAX_STACK*MAX_TASKS
;rb MAX_STACK

if (7c00h-MAX_STACK-(MAX_STACK*MAX_TASKS)) < 600h
 display "out of stack space"
 err
end if

kstack:
 cli
 xor ax,ax
 mov ds,ax
 mov es,ax
 mov ss,ax
 mov sp,kstack-SP_HACK

hot_code:
 db OP_JMPF
 dw _purge
 dw 0
_purge:

 mov di,bss_data
 mov cx,bss_data2-bss_data
;mov al,0
 rep stosb

 mov si,4*8
 mov di,hot_code+1
 push si
 movsw
 movsw
 pop di
 mov ax,irq0
 stosw
 mov ax,cs
 stosw

 mov si,server_idle
 call sys_exec

 mov si,server_console
 call sys_exec

 mov sp,kstack-MAX_STACK-SP_HACK
 sti
 jmp server_idle+1

;=============================================================================
; system servers
;=============================================================================

server_idle:
 db SV_NONE
.main:
if CPU_HOG
 call sys_yield
else
 hlt
end if
 jmp .main

;-----------------------------------------------------------------------------

server_stdin:
 db SV_GETC
.main:
 call sys_recv
;cmp ah,SV_GETC
;je .on_getc
;...
;.on_getc:
 mov ah,01h
 int 16h
 mov ax,00h
 jz .send
 int 16h
.send:
;mov ah,SV_GETC
 call sys_send
 jmp .main

;-----------------------------------------------------------------------------

server_stdout:
 db SV_PUTC
.main:
 call sys_recv
;cmp ah,SV_PUTC
;je .on_putc
;...
;.on_putc:
 mov ah,0eh
 mov bx,0007h
 int 10h
 cmp al,8
 jne .main
 mov al,32
 int 10h
 mov al,8
 int 10h
 jmp .main

;-----------------------------------------------------------------------------

server_console:
 db SV_NONE
.main:
;.exec_stdin:
 mov si,server_stdin
 call sys_exec
;or di,di
;js .exec_stdin
 push di
;.exec_stdout:
 mov si,server_stdout
 call sys_exec
;or di,di
;js .exec_stdout
 push di
.exec_hello:
 mov si,server_hello
 call sys_exec
;or di,di
;js .exec_hello
.idle:
 mov bp,sp
 mov di,[ss:bp+2+SP_HACK]
 mov ah,SV_GETC
 call sys_send
;cmp di,[ss:bp++2+SP_HACK]
;jne ...
;cmp ah,SV_GETC
;jne ...
 call sys_recv
;cmp di,[ss:bp++2+SP_HACK]
;jne ...
;cmp ah,SV_GETC
;jne ...
 test al,al
 jz .idle
 cmp al,'H'
 je .exec_hello
 mov di,[ss:bp+0+SP_HACK]
 mov ah,SV_PUTC
.more:
 call sys_send
;cmp di,[ss:bp+0+SP_HACK]
;jne ...
;cmp ah,SV_PUTC
;jne ...
 cmp al,13
 jne .idle
 mov al,10
 jmp .more

;-----------------------------------------------------------------------------

server_hello:
 db SV_NONE
;.main:
 mov di,-2
 mov ah,SV_PUTC
 mov si,.msg
.next:
 lodsb
 test al,al
 jz .exit
 call sys_send
 or di,di
 jns .next
.exit:
 ret	;call sys_exit
.msg:
 db 'Hello World!',0

;=============================================================================
; system api
;=============================================================================

sys_exit:
;in: no parameters
;out: never returns
;uses: di
 cli
 mov di,[ds:active_task]
 mov [ds:di+task_states],TS_FREE
 mov [ds:di+ipc_services],SV_NONE
 call sys_yield

;-----------------------------------------------------------------------------

sys_exec:
;in: si = task code
;out: di = task handle (-2 = no free slots)
;uses: ax, si, bp
 pushf
 cli
 mov di,MAX_TASKS*2
 mov bp,kstack-28
.next:
 sub bp,MAX_STACK
 dec di
 dec di
 js .done
 cmp [ds:di+task_states],TS_FREE
 jne .next
 lodsb
 mov [ds:di+ipc_services],al
;mov [ds:di+ipc_packets],0
;mov [ds:di+ipc_links],0
 pushf
 pop ax
 or ax,512
 mov [ss:bp+26],word sys_exit	;ret
 mov [ss:bp+24],ax	;ff
 mov [ss:bp+22],cs	;cs
 mov [ss:bp+20],si	;ip
;mov [ss:bp+18],0       ;ax
;mov [ss:bp+16],0       ;cx
;mov [ss:bp+14],0       ;dx
;mov [ss:bp+12],0       ;bx
;mov [ss:bp+10],0       ;bp
;mov [ss:bp+8],0        ;si
;mov [ss:bp+6],0        ;di
 mov [ss:bp+4],cs	;ds
 mov [ss:bp+2],cs	;es
 mov [ss:bp+0],cs	;ss
if REAL_86
 dec bp
 dec bp
end if
 mov [ds:di+stack_ptrs],bp
 mov [ds:di+task_states],TS_READY
.done:
 popf
 ret

;-----------------------------------------------------------------------------

sys_send:
;in: di = task handle (-2 = service request)
;in: ax = ipc packet
;out: di = task handle (-2 = no service)
;uses: bx (dx)
 pushf
 cli
 mov bx,[ds:active_task]
;mov [ds:bx+task_states],TS_SEND
 or di,di
 jns .wait
 mov di,MAX_TASKS*2
.next:
 dec di
 dec di
 js .done
;cmp [ds:di+task_states],TS_FREE
;je .next
 test [ds:di+ipc_services],ah
 jz .next
.wait:
 call sys_yield
 cmp [ds:di+task_states],TS_RECV
 jne .wait
 mov [ds:di+ipc_links],bx
 mov [ds:di+ipc_packets],ax
 mov [ds:di+task_states],TS_READY
.done:
;mov [ds:bx+task_states],TS_READY
 popf
 ret

;-----------------------------------------------------------------------------

sys_recv:
;in: no parameters
;out: di = task handle
;out: ax = ipc packet
;uses: bx (dx)
 pushf
 cli
 mov bx,[ds:active_task]
 mov [ds:bx+task_states],TS_RECV
.wait:
 call sys_yield
 cmp [ds:bx+task_states],TS_READY
 jne .wait
 mov di,[ds:bx+ipc_links]
 mov ax,[ds:bx+ipc_packets]
 popf
 ret

;-----------------------------------------------------------------------------

sys_yield:
;in: no parameters
;out: no parameters
;uses: dx
 pop dx
 pushf
 cli
 push cs
 push dx
 mov [ds:hot_code],byte OP_IRET
 jmp schedule

;=============================================================================
;
;=============================================================================

irq0:
 mov [cs:hot_code],byte OP_JMPF
;jmp schedule

;-----------------------------------------------------------------------------

schedule:
 push ax
 push cx
 push dx
 push bx
 push bp
 push si
 push di
 push ds
 push es
 push ss

if 0
 mov ax,cs
 mov ds,ax
 mov es,ax
 mov ss,ax
else
 push cs
 pop ds
end if

 mov bx,[ds:active_task]
 mov [ds:bx+stack_ptrs],sp
;mov sp,kstack-SP_HACK

.next:
 dec bx
 dec bx
 jns .test
 mov bx,MAX_TASKS*2-2
.test:
 cmp [ds:bx+task_states],TS_FREE
 je .next

if 0
;push es
 mov ax,0b800h
 mov es,ax
 xor di,di
 mov si,task_states
 mov cx,MAX_TASKS
.print:
 lodsw
 mov ah,70h
 add al,'0'
 stosw
 loop .print
 mov byte[es:bx+1],7fh
;pop es
end if

 mov [ds:active_task],bx
 mov sp,[ds:bx+stack_ptrs]

 pop ss
 pop es
 pop ds
 pop di
 pop si
 pop bp
 pop bx
 pop dx
 pop cx
 pop ax
 jmp hot_code

;=============================================================================
; initialized data area
;=============================================================================

active_task dw MAX_TASKS*2-2

rb 510-($-7c00h)
dw 0aa55h

;=============================================================================
; uninitialized data area
;=============================================================================

bss_data:
;active_task    rw 1
stack_ptrs	rw MAX_TASKS
task_states	rb MAX_TASKS*2	;only lobyte is used
ipc_services	rb MAX_TASKS*2	;only lobyte is used
ipc_packets	rw MAX_TASKS	;hibyte = msg, lobyte = data
ipc_links	rw MAX_TASKS
bss_data2:

;=============================================================================
