flat assembler
Message board for the users of flat assembler.

Index > DOS > Quasi-multitasking in DOS

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
me239



Joined: 06 Jan 2011
Posts: 200
me239
Hey guys, here is an example DOS TSR that produces a window with text, hooks int 08h, and initiates the mouse. This program is an example of a simple form of multitasking in DOS w/o setting up a 3rd party multitasking environment, but rather having a self contained environment that can run alongside other legacy DOS applications. Screenshot: Image
code:
Code:
org 100h
start:
        push    0
        pop     ds
        mov     ax, word[ds:0008h*4]
        mov     bx, word[ds:0008h*4+2]
        mov     word[cs:int08], ax
        mov     word[cs:int08+2], bx
        push    cs cs
        pop     ds es
        push    ds
        mov     bx, ds
        dec     bx
        mov     ds, bx
        mov     bx, [ds:03]
        sub     bx, (vend-task+15)/16+1
        pop     ds
        mov     ah, 4ah
        int     21h
        mov     ah, 48h
        mov     bx, (vend-task+15)/16
        int     21h
        mov     es, ax
        push    cs
        pop     ds
        xor     di, di
        mov     si, task
        mov     cx, vend-task
        rep     movsb
        mov     ax, es
        dec     ax
        mov     es, ax
        mov     word[es:01], 8
        push    0
        pop     ds
        cli
        inc     ax
        mov     word[ds:0008h*4], 0
        mov     word[ds:0008h*4+2], ax
        sti
        ret
task:
        pushf
        push    ax bx cx dx ds es di si
        push    0b800h
        pop     es
        mov     di, 60*2
        mov     ah, 4fh
        mov     al, 0c9h
        stosw
        mov     al, 0cdh
        mov     cx, 16
        rep     stosw
        mov     al, '[';0bbh
        stosw
        mov     al, 'X'
        stosw
        mov     al, ']'
        stosw
        mov     di, ((80*1)*2)+(60*2)
        mov     al, 0bah
        stosw  
        mov     al, 20h
        mov     cx, 7
        rep     stosw
        mov     ah, 4fh
        mov     al, 'T'
        stosw
        mov     al, 'A'
        stosw
        mov     al, 'S'
        stosw
        mov     al, 'K'
        stosw
        mov     al, ' '
        stosw
        mov     al, '1'
        stosw
        mov     cx, 5
        mov     al, 20h
        rep     stosw
        mov     al, 0bah
        stosw
        mov     di, ((80*2)*2)+(60*2);(80*3)+(60*3)+20
        mov     al, 0bah
        stosw
        mov     al, 20h
        mov     cx, 6
        rep     stosw
        mov     ah, 4fh
        mov     al, 'W'
        stosw
        mov     al, 'I'
        stosw
        mov     al, 'N'
        stosw
        mov     al, 'D'
        stosw
        mov     al, 'O'
        stosw
        mov     al, 'W'
        stosw
        mov     al, ' '
        stosw
        mov     al, '1'
        stosw
        mov     al, '!'
        stosw
        mov     cx, 3
        mov     al, 20h
        rep     stosw
        mov     al, 0bah
        stosw
        mov     di, ((80*3)*2)+(60*2)
        mov     al, 0c8h
        stosw
        mov     cx, 18
        mov     al, 0cdh
        rep     stosw
        mov     al, 0bch
        stosw 
        mov     ax, 0001h
        int     33h
        mov     ax, 0003h
        int     33h
        cmp     cx, 616
        jb      nomouse
        cmp     cx, 640
        ja      nomouse
        cmp     dx, 8
        ja      nomouse
        cmp     bx, 1
        jnz     nomouse
        jmp     killint
nomouse:
        pop     si di es ds dx cx bx ax
        popf
int08old:
db      0eah
int08   dw      0, 0
killint:
        mov     ax, 0002h
        int     33h
        pop     si di es ds dx cx bx ax
        push    ax bx ds
        call    cls
        mov     ax, word[cs:int08-task]
        mov     bx, word[cs: (int08-task)+2]
        cli
        push    0
        pop     ds
        mov     word[ds:0008h*4], ax
        mov     word[ds:0008h*4+2], bx
        sti
        pop     ds bx ax
        popf
        jmp     int08old
cls:
        mov     di, (80*5)+(60*3)+20
        call    drawline
        mov     di, (80*3)+(60*3)+20
        call    drawline
        mov     di, (60*2)+(80*2)
        call    drawline
        mov     di, (60*2)
        call    drawline
        ret
drawline:
        push    es
        push    0b800h
        pop     es
        mov     cx, 20
        mov     ax, 0700h
        rep     stosw
        pop     es
        ret
vend:
    


Last edited by me239 on 26 Sep 2011, 21:19; edited 1 time in total
Post 26 Sep 2011, 04:42
View user's profile Send private message Reply with quote
Dex4u



Joined: 08 Feb 2005
Posts: 1601
Location: web
Dex4u
Nice work, but TSR is multi-tasking Wink
Post 26 Sep 2011, 17:33
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
Dex4u wrote:
Nice work, but TSR is multi-tasking Wink
I would consider mine more like multitasking because it hooks the timer interrupt and is independent of other non-multitasking code. I don't really consider TSRs that hook say file operations in DOS multitasking, but rather a re-route since the program is dependent on the user to initiate the program. The only reason I don't call mine full blown multitasking is because it doesn't properly re-route int 08h. For example, say two programs are run, program 1 and program 2 respectively. If one were to close program 1 before program 2, int 08 would be returned to it's original vector rather than program 2, thus destroying the chain of tasks. I still have yet to figure out a workaround.
Post 26 Sep 2011, 20:19
View user's profile Send private message Reply with quote
addes3



Joined: 09 May 2011
Posts: 29
addes3
This doesn't compile. csSadint08 does not exist, though it is used in line 150. May be a misspell.
Post 26 Sep 2011, 20:47
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
addes3 wrote:
This doesn't compile. csSadint08 does not exist, though it is used in line 150. May be a misspell.
LOL, the actual code was a "cs: " and an "(", but that also is the emoticon for Sad
Post 26 Sep 2011, 21:18
View user's profile Send private message Reply with quote
addes3



Joined: 09 May 2011
Posts: 29
addes3
Ohhhhhhh okay.
Post 26 Sep 2011, 21:46
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2913
Location: 0x77760000
typedef
Doesn't do anything on my PC. Just pops up a console and quits. I tried running it from CMD but it does the same thing. It doesn't show anything either.

OS: XP Home Edition
SP 3
Post 27 Sep 2011, 00:38
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
typedef wrote:
Doesn't do anything on my PC. Just pops up a console and quits. I tried running it from CMD but it does the same thing. It doesn't show anything either.

OS: XP Home Edition
SP 3
I had some problems with it using DOSEmu on my Linux box, but it ran fine under DOSBox. I think the problem is with the mouse driver under MS-DOS. Oh and remember, running it straight from Explorer won't do anything since it's a TSR.
Post 27 Sep 2011, 00:46
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 2796
Location: dank orb
bitRAKE
me239 wrote:
Say two programs are run, program 1 and program 2 respectively. If one were to close program 1 before program 2, int 08 would be returned to it's original vector rather than program 2, thus destroying the chain of tasks. I still have yet to figure out a workaround.
I've seen a vector chain used - where the old vector value is always the two words before the task address. The INT08 vector would always point to the head of the chain. Just like removing a value from a single-linked list: if the head is removed then INT08 is updated to old value, else list is traversed to replace task address with [int08].
Code:
int08old:
        db $EA
int08   dw 0,0

task:

...

        jmp int08old    
p.s. Often a key value is used to indicate this model is used, or at least $EA is found at [task-5]. Otherwise the end of the list is ambiguous. (Only a problem when non-model tasks are replacing INT08.)

_________________
¯\(°_o)/¯ unlicense.org
Post 27 Sep 2011, 04:56
View user's profile Send private message Visit poster's website Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
bitRAKE wrote:
me239 wrote:
Say two programs are run, program 1 and program 2 respectively. If one were to close program 1 before program 2, int 08 would be returned to it's original vector rather than program 2, thus destroying the chain of tasks. I still have yet to figure out a workaround.
I've seen a vector chain used - where the old vector value is always the two words before the task address. The INT08 vector would always point to the head of the chain. Just like removing a value from a single-linked list: if the head is removed then INT08 is updated to old value, else list is traversed to replace task address with [int08].
Code:
int08old:
        db $EA
int08   dw 0,0

task:

...

        jmp int08old    
p.s. Often a key value is used to indicate this model is used, or at least $EA is found at [task-5]. Otherwise the end of the list is ambiguous. (Only a problem when non-model tasks are replacing INT08.)
All I could think of is actually overwriting part of the code so that it jumps to a db 0eah instead the code. Could you elaborate a little Very Happy thanks.
Post 27 Sep 2011, 05:20
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 2796
Location: dank orb
bitRAKE
(0) First INT08 looks like this:

INT08: dd org_val ; segment:offset (of course)

(1) Next a task is installed:
Code:
INT08: dd task_A
...
  db $EA
  dd org_val
task_A:    
(2) Next another task is installed:
Code:
INT08: dd task_B
...
  db $EA
  dd org_val
task_A:
...
  db $EA
  dd task_A
task_B:    
(3) Finally, we'd like to kill task_A:
Code:
INT08: dd task_B
...
  db $EA
  org_val
task_B:    
Explaination:(0) thru (2) only data order differs. (3) is accomplished by loading the list head, [INT08] and looking for task_A value. To get next task in list, just load the pointer at [task-4] --> task.
Code:
old = [task_A-4]
val = [INT08]
if val <> task_A
  while [val-4] <> task_A ; while not chained to removed task
    val = [val-4]         ; get next task in chain
  end while
  [val-4] = old           ; set forward link
else
  [INT08] = old
end if    
...hope this isn't more confusing. Wink
Post 27 Sep 2011, 07:14
View user's profile Send private message Visit poster's website Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
bitRAKE wrote:
(0) First INT08 looks like this:

INT08: dd org_val ; segment:offset (of course)

(1) Next a task is installed:
Code:
INT08: dd task_A
...
  db $EA
  dd org_val
task_A:    
(2) Next another task is installed:
Code:
INT08: dd task_B
...
  db $EA
  dd org_val
task_A:
...
  db $EA
  dd task_A
task_B:    
(3) Finally, we'd like to kill task_A:
Code:
INT08: dd task_B
...
  db $EA
  org_val
task_B:    
Explaination:(0) thru (2) only data order differs. (3) is accomplished by loading the list head, [INT08] and looking for task_A value. To get next task in list, just load the pointer at [task-4] --> task.
Code:
old = [task_A-4]
val = [INT08]
if val <> task_A
  while [val-4] <> task_A ; while not chained to removed task
    val = [val-4]         ; get next task in chain
  end while
  [val-4] = old           ; set forward link
else
  [INT08] = old
end if    
...hope this isn't more confusing. Wink
I think I see what you're saying. So if I got task 1 to rewrite the value at task1-4 and set it to the original int 08, it would keep task 2 working and jumping. The only problem I see, I could be reading this wrong, is that the system still has to jump to old closed tasks.
Post 27 Sep 2011, 21:37
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 759
Location: Massachusetts, USA
bitshifter
Have you seen my 512 byte multitasking OS demo?
http://board.flatassembler.net/topic.php?t=13063
I thought about making some changes to run on DOS
but i am too damn lazy right now...
Maybe you could learn something from it?
Post 27 Sep 2011, 23:39
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
bitshifter wrote:
Have you seen my 512 byte multitasking OS demo?
http://board.flatassembler.net/topic.php?t=13063
I thought about making some changes to run on DOS
but i am too damn lazy right now...
Maybe you could learn something from it?
I've seen your example, and it's predecessor, and I must say I was impressed, but it seems like quite a task porting to DOS. One question I have though is why is it so slow? The timer goes off 18.2 times every second, but I can still see the text being slowly typed.
Post 28 Sep 2011, 00:20
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 759
Location: Massachusetts, USA
bitshifter
It is slow because of the IPC indirection (which the KVP version doesnt have)
His had done IO within the timer IRQ wheras mine just sets flags
and routes things later through the console.
It also explains why his text merges better than mine.
Hope that makes sense...
Post 28 Sep 2011, 01:53
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
bitshifter wrote:
It is slow because of the IPC indirection (which the KVP version doesnt have)
His had done IO within the timer IRQ wheras mine just sets flags
and routes things later through the console.
It also explains why his text merges better than mine.
Hope that makes sense...
So all the functions like printf and everything are controlled by the int 08h vector?
Post 28 Sep 2011, 02:21
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 759
Location: Massachusetts, USA
bitshifter
In my demo the INT 08h vector only sets flags and then re-schedules.
The tasks have message-boxes and data-packets to communicate via IPC.
It may take you a few hours or days to really understand the code.
I recommend studying it until you understand every single byte.
It took me about a week to understand it good enough to write my own
version completely from scratch.
Notice my version was inspired by KVP's version but shares no
code which was copy and paste.
Post 28 Sep 2011, 02:34
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 759
Location: Massachusetts, USA
bitshifter
Assume CS = DS = ES = SS = whatever
Uses BIOS keyboard and video interrupts.
ESC quits console server and exits to DOS.

I have not debugged this code.
There is things i need to know:
Does hardware interrupt and software interrupt push same 3 regs?
Is it ok to yield by issuing software interrupt?

Code:
;======================================
;              smt4dos
;======================================

;---------------;
; max constants ;
;---------------;

MAX_STACK       equ 512 ;number of words in each stack
MAX_TASKS       equ 8

;-------------;
; task states ;
;-------------;

STATE_FREE      equ 0 ;dead, available task slot
STATE_READY     equ 1 ;running, no ipc activity
STATE_SEND      equ 2 ;sending an ipc message
STATE_RECV      equ 3 ;awaiting an ipc message
STATE_SREQ      equ 4 ;requesting an ipc service

;------------;
; task stack ;
;------------;

TSS_SIZE        equ 26 ;structure size (not on stack)
TSS_FLAGS       equ 24 ;int/iret
TSS_CS          equ 22 ;...
TSS_IP          equ 20 ;...
TSS_AX          equ 18 ;pusha/popa
TSS_CX          equ 16 ;...
TSS_DX          equ 14 ;...
TSS_BX          equ 12 ;...
TSS_SP          equ 10 ;...
TSS_BP          equ 8  ;...
TSS_SI          equ 6  ;...
TSS_DI          equ 4  ;...
TSS_DS          equ 2  ;push/pop ds
TSS_ES          equ 0  ;push/pop es

;---------------;
; service types ;
;---------------;

SERVICE_NONE    equ 0 ;intermediate service (Ex: idle,console,hello)
SERVICE_GETC    equ 1 ;character input service (Ex: stdin)
SERVICE_PUTC    equ 2 ;character output service (Ex: stdout)

;-------------;
; irq offsets ;
;-------------;

IRQ0_IP         equ (08h*4)
IRQ0_CS         equ (08h*4+2)

;=======================================
; program entry point
;=======================================

use16
org 0100h

        ; assume cs = ds = es = ss = shared

        ;--------------------;
        ; disable interrupts ;
        ;--------------------;

        cli

        ;---------------------;
        ; init kernel context ;
        ;---------------------;

;        mov ax,cs
;        mov ds,ax
;        mov es,ax
;        mov ss,ax
;        mov sp,0fffeh

        ;----------------;
        ; zero .bss data ;
        ;----------------;

        cld
        mov di,BSS_START
        mov cx,BSS_SIZE
        xor ax,ax       ;zero high-bits for later
        rep stosb

        ;-----------------;
        ; chain into irq0 ;
        ;-----------------;

        push es
        push ax ;zero
        pop es

        mov ax,[es:IRQ0_IP]             ;get old ip
        mov [ds:old_irq0_ip],ax         ;store old ip
        mov [es:IRQ0_IP],word irq0      ;write new ip

        mov ax,[es:IRQ0_CS]             ;get old cs
        mov [ds:old_irq0_cs],ax         ;store old cs
        mov [es:IRQ0_CS],cs             ;set new cs

        pop es

        ;--------------------;
        ; start system tasks ;
        ;--------------------;

        mov ax,server_idle      ;task entry point offset
        mov cl,SERVICE_NONE     ;services the task can provide
        call sys_exec           ;input: ax = offset, cl = services
                                ;output: bx = handle

        mov ax,server_console   ;task entry point offset
        mov cl,SERVICE_NONE     ;services the task can provide
        call sys_exec           ;input: ax = offset, cl = services
                                ;output: bx = handle

        ;---------------------------------;
        ; map kernel into idle task space ;
        ;---------------------------------;

        mov [ds:active_task],MAX_TASKS*2-2 ;first task handle
        mov sp,MAX_STACK*2*MAX_TASKS+task_stacks

        sti ;enable scheduler
;       jmp server_idle

;===============================
; system servers
;===============================

;----------------------------------

server_idle:

        int 08h         ;yield
        jmp server_idle

;-----------------------------------

server_video:

        ;-----------------------------;
        ; await video service request ;
        ;-----------------------------;

  .await_request:

        call sys_recv           ;out: bx = handle, dx = message, ax = packet

        ;------------------------------;
        ; handle video service request ;
        ;------------------------------;

        cmp dx,SERVICE_PUTC     ;only service we provide
        jne .await_request      ;go wait for more work

        ;---------------------------------------;
        ; provide teletype video output service ;
        ;---------------------------------------;

  .on_putc:

        mov ah,0eh              ;teletype output
        mov bh,0                ;video page number

        ;handle backspace characters properly

        cmp al,8
        jne .do_it
        int 10h                 ;BIOS video service
        mov al,32               ;print a space char
        int 10h                 ;BIOS video service
        mov al,8

  .do_it:

        int 10h                 ;BIOS video service

        ;compliment carriage-return with line-feed

        sub al,3
        cmp al,10
        je .do_it

        jmp .await_request      ;go wait for more work

;---------------------------------------------

server_keyboard:

        ;--------------------------------;
        ; await keyboard service request ;
        ;--------------------------------;

  .await_request:

        call sys_recv           ;out: bx = handle, dx = message, ax = packet

        ;---------------------------------;
        ; handle keyboard service request ;
        ;---------------------------------;

        cmp dx,SERVICE_GETC     ;only service we provide
        jne .await_request      ;go wait for more work

        ;--------------------------------;
        ; provide keyboard input service ;
        ;--------------------------------;

  .on_getc:

        ;check keyboard buffer for keystroke

        mov ah,01h              ;check keystroke
        int 16h                 ;BIOS keyboard service

        ;if there is no keystroke available we wait for one

        jz .on_getc_wait

        ;remove keystroke from keyboard buffer

        xor ah,ah               ;get keystroke
        int 16h                 ;BIOS keyboard service

        ;send service request reply with data packet

        call sys_send           ;in: bx = handle, dx = message, ax = packet
        jmp .await_request      ;go wait for more work

  .on_getc_wait:

        ;an input request is still pending.
        ;dont continuosly poll via yield.

        int 08h         ;yield
        jmp .on_getc    ;try again

;----------------------------------------

server_console:

        ;--------------------;
        ; start video server ;
        ;--------------------;

        mov ax,server_video             ;code entry point offset
        mov cl,SERVICE_PUTC             ;services the task can provide
        call sys_exec                   ;in: ax = offset, cl = services
                                        ;out: bx = handle

        ;TODO: Verify task handle and try agin later if no slots available.
        ;      This should never happen since its the third task started.

        mov [ds:console_stdout],bx      ;store stdout handle

        ;-----------------------;
        ; start keyboard server ;
        ;-----------------------;

        mov ax,server_keyboard          ;code entry point offset
        mov cl,SERVICE_GETC             ;services the task can provide
        call sys_exec                   ;in: ax = offset, cl = services
                                        ;out: bx = handle

        ;TODO: Verify task handle and try agin later if no slots available.
        ;      This should never happen since its the fourth task started.

        mov [ds:console_stdin],bx       ;store stdin handle

        ;--------------------------;
        ; start hello world server ;
        ;--------------------------;

  .say_hello:

        mov ax,server_hello             ;task entry point offset
        mov cl,SERVICE_NONE             ;services the task can provide
        call sys_exec                   ;in: ax = offset, cl = services
                                        ;out: bx = handle

        ;TODO: Verify task handle and try again later if no slots available.
        ;      At the moment we just ignore the request and go on living.

        ;--------------------------------;
        ; acquire and process user input ;
        ;--------------------------------;

  .wait_input:

        ;send input service request message to keyboard server

        mov dx,SERVICE_GETC             ;service request
        mov bx,[ds:console_stdin]       ;send to stdin
        call sys_send                   ;in: bx = handle, dx = message

        ;wait for keyboard server to reply

        call sys_recv                   ;out: bx = handle, dx = message, ax = packet

        ;TODO: Verify reply service by testing handle in bx and message in dx.
        ;      (bx should be console_stdin and dx should be SERVICE_GETC)

        ;ESC character quits program

        cmp al,1bh
        je .quit

        ;'H' character spawns new hello world application

        cmp al,'H'
        je .say_hello

        ;any other data gets sent to stdout server

        mov dx,SERVICE_PUTC             ;service request
        mov bx,[ds:console_stdout]      ;send to stdout
        call sys_send                   ;in: bx = handle, dx = message, ax = packet

        jmp .wait_input ;go wait for more work

  .quit:

        cli     ; lock the scheduler

        ; If the stdin/stdout servers needed to be cleaned
        ; we would send each one a cleanup message first.

        ;restore IRQ vector

        push 0
        pop es

        mov ax,[ds:old_irq0_ip]
        mov [es:IRQ0_IP],ax

        mov ax,[ds:old_irq0_cs]
        mov [es:IRQ0_CS],ax

        int 20h
        jmp $

;---------------------------------------------

str_hello db 'Hello World!',0

server_hello:

        ;--------------------------------------;
        ; display hello world message and quit ;
        ;--------------------------------------;

        mov si,str_hello

  .print:

        lodsb

        cmp al,0
        je .done

        ;This task has no idea of who provides video services
        ;so we must ask the system to try and find a handler.

        mov dx,SERVICE_PUTC             ;service request
        call sys_sreq                   ;in: dx = message, ax = packet
                                        ;out: bx = handle, dx = message, ax = packet

        ;If no video server was found, quit this task.

        or bx,bx                        ;compare bx to -2
        jns .print

  .done:

        ;This task exits upon completion of displaying its message.

        call sys_exit   ;never returns

;==============================
; system api
;==============================

;--------------------------------------
; sys_exit -- exits a task
; in: no parameters
; out: never returns
; uses: bx
;--------------------------------------

sys_exit:

        pop ax ;discard return address

        mov bx,[ds:active_task]
        mov byte[ds:bx+task_states],STATE_FREE

  .dying:

        int 08h         ;yield
        jmp .dying

;--------------------------------------
; sys_exec -- starts a new task
; in: ax = task offset, cl = ipc services
; out: bx = task handle (-2 = out of task slots)
; uses: di,dx
; notes: If no test slots are available it is up to the caller to try again
;        later, rather than hanging it while waiting for an open slot.
;--------------------------------------

sys_exec:

        pushf   ;disable interrupts within this context
        cli

        mov bx,MAX_TASKS*2 ;allocate task handles from top down
        mov di,MAX_TASKS*2*MAX_STACK+task_stacks+MAX_STACK*2-TSS_SIZE ;allocate task stacks from top down

  .find_free_slot:

        sub di,MAX_STACK*2

        dec bx
        dec bx
        js .no_free_slot ;-2 = no task slots available

        cmp byte[ds:bx+task_states],STATE_FREE
        jne .find_free_slot

        mov [ds:bx+stack_ptrs],di

        pushf
        pop dx
        or dx,512 ;'IRQ enable' bit in the flags register

        mov [ds:di+TSS_FLAGS],dx
        mov [ds:di+TSS_CS],cs
        mov [ds:di+TSS_IP],ax
;       mov [ds:di+TSS_AX],0   ;no need to initialize these
;       mov [ds:di+TSS_CX],0
;       mov [ds:di+TSS_DX],0
;       mov [ds:di+TSS_BX],0
;       mov [ds:di+TSS_SP],0
;       mov [ds:di+TSS_BP],0
;       mov [ds:di+TSS_SI],0
;       mov [ds:di+TSS_DI],0
        mov [ds:di+TSS_DS],cs
        mov [ds:di+TSS_ES],cs

        mov byte[ds:bx+ipc_services],cl
        mov byte[ds:bx+task_states],STATE_READY

  .no_free_slot:

        popf
        ret

;---------------------------------------
; sys_send -- sends an ipc message
; in: bx = ipc target handle, dx = message to send, ax = data packet to send
; out: no parameters
; uses: bp,cl
;---------------------------------------

sys_send:

        mov cl,STATE_SEND
        jmp ipc_squak

;---------------------------------------
; sys_recv -- receives an ipc message
; in: no parameters
; out: bx = source handle, dx = message to recv, ax = data packet to recv
; uses: bp,cl
;---------------------------------------

sys_recv:

        mov cl,STATE_RECV
        jmp ipc_squak

;---------------------------------------
; sys_sreq -- requests an ipc service
; in: dx = message, ax = data packet
; out: bx = ipc service handle (-2 = no handler found)
; uses: bp,cl
;---------------------------------------

sys_sreq:

        mov cl,STATE_SREQ
;       jmp ipc_squak

;----------------------------------------
; ipc_squak -- setup an ipc connection
; in: bx = handle, dx = message, ax = packet, cl = new state
; out: bx = handle, dx = message, ax = packet
; uses: bp
;----------------------------------------

ipc_squak:

        mov bp,[ds:active_task]

        mov [ss:bp+ipc_links],bx
        mov [ss:bp+ipc_messages],dx
        mov [ss:bp+ipc_packets],ax

        mov byte[ss:bp+task_states],cl

  .wait:

        cmp byte[ss:bp+task_states],STATE_READY
        je .ready

;       int 08h         ;yield, DEBUG -- is broken?
        jmp .wait

  .ready:

        mov bx,[ss:bp+ipc_links]
        mov dx,[ss:bp+ipc_messages]
        mov ax,[ss:bp+ipc_packets]

        ret

;=====================================
; IRQ0 hook
;=====================================

; we can yield here but next task only gets what time remains

irq0:

        ;--------------------;
        ; disable interrupts ;
        ;--------------------;

        cli     ;DEBUG -- does the cpu clear this?

        ;----------------------;
        ; store active context ;
        ;----------------------;

        pusha
        push ds
        push es

        ;---------------------;
        ; load kernel context ;
        ;---------------------;

        mov ax,cs
        mov ds,ax
        mov es,ax
;       mov ss,ax

        ;----------------;
        ; load task info ;
        ;----------------;

        mov bx,[ds:active_task]
        mov ax,[ds:bx+task_states]
        mov di,[ds:bx+ipc_links]
        mov cx,[ds:bx+ipc_messages]
        mov dx,[ds:bx+ipc_packets]

        ;----------------------------;
        ; handle ipc service request ;
        ;----------------------------;

        cmp al,STATE_SREQ
        jne .not_sreq

        mov di,MAX_TASKS*2

  .find_service_handler:

        dec di
        dec di
        js .no_service_handler

        cmp [ds:di+ipc_services],cx ;can he handle this type of service request message
        jne .find_service_handler   ;keep looking for someone who can

        mov [ds:bx+ipc_links],di ;he can do it, so lets link ourself to him
        jmp .ipc_send            ;send package it to him

  .no_service_handler:

        mov [ds:bx+ipc_links],di ;no handler found so return -2
        jmp .task_done ;stop trying to post service request

  .not_sreq:

        ;----------------------------;
        ; handle ipc message passing ;
        ;----------------------------;

        cmp al,STATE_SEND
        jne .next_task

  .ipc_send:

        cmp byte[ds:di+task_states],STATE_RECV ;is target in recv mode?
        jne .next_task ;if not, try again later

  .ipc_copy:

        mov [ds:di+ipc_links],bx        ;tell target who sent it (us)
        mov [ds:di+ipc_messages],cx     ;put message in his mailbox
        mov [ds:di+ipc_packets],dx      ;put data in his mailbox

        mov byte[ds:di+task_states],STATE_READY ;take target out of recv mode (break his wait loop)

  .task_done:

        mov byte[ds:bx+task_states],STATE_READY ;take us out of send/sreq mode

  .next_task:

        ;------------------------;
        ; perform task sheduling ;
        ;------------------------;

        ;BX = active task handle

        ;The sheduler requires an idle task whith constant READY state to work properly.
        ;Otherwise we could end up in and endless loop trying to find a task to schedule.

        mov [ds:bx+stack_ptrs],sp

  .find_work:

        dec bx
        dec bx
        jns .check_state

        mov bx,MAX_TASKS*2-2 ;wrap around

  .check_state:

        cmp byte[ds:bx+task_states],STATE_FREE
        je .find_work ;dont schedule dead tasks

        cmp byte[ds:bx+task_states],STATE_RECV
        je .find_work ;dont schedule waiting tasks

        mov [ds:active_task],bx ;new active task handle

        mov sp,[ds:bx+stack_ptrs]

        ;-------------------;
        ; load task context ;
        ;-------------------;

        pop es
        pop ds
        popa

        ;---------------------------;
        ; jump far into old handler ;
        ;---------------------------;

            db 0eah  ;opcode: jmp far
old_irq0_ip dw 0000h ;hot-patch: ip
old_irq0_cs dw 0000h ;hot-patch: cs

;==================================
; uninitialized data
;==================================

BSS_START:

  active_task           rw 1
  task_stacks           rw MAX_TASKS*MAX_STACK
  stack_ptrs            rw MAX_TASKS
  task_states           rw MAX_TASKS ;only lobyte is used
  ipc_services          rw MAX_TASKS ;only lobyte is used
  ipc_messages          rw MAX_TASKS
  ipc_links             rw MAX_TASKS
  ipc_packets           rw MAX_TASKS
  ;console server private data
  console_stdin         rw 1
  console_stdout        rw 1

BSS_SIZE = $ - BSS_START

;===================================
    

_________________
Coding a 3D game engine with fasm is like trying to eat an elephant,
you just have to keep focused and take it one 'byte' at a time.


Last edited by bitshifter on 30 Sep 2011, 00:35; edited 3 times in total
Post 28 Sep 2011, 03:25
View user's profile Send private message Reply with quote
me239



Joined: 06 Jan 2011
Posts: 200
me239
bitshifter wrote:
Ok, this was EZ-4-ME so here it is ported to DOS as a COM program.

Assume CS = DS = ES = whatever
Uses BIOS keyboard interrupts.
No exit services, kill manually.

Note that this design is very slow by nature,
but is scaleable to a highly protected system.

Oh, and did i mention that it is only 488 bytes Smile

Now go beat it up!
WOW, that was quick. So is there ANYWAY to speed this up? It's absolutely painful.


Last edited by me239 on 29 Sep 2011, 03:58; edited 1 time in total
Post 28 Sep 2011, 04:30
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 759
Location: Massachusetts, USA
bitshifter
I updated the code posted above...
Post 28 Sep 2011, 08:42
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2019, Tomasz Grysztar.

Powered by rwasa.