; multitasking kernel as a TSR
[org 0x0100]
              jmp start

; PCB layout:
; ax,bx,cx,dx,si,di,bp,sp,ip,cs,ds,ss,es,flags,next,processNo
;  0, 2, 4, 6, 8,10,12,14,16,18,20,22,24,  26 , 28 ,  30

pcb:          times 32*16 dw 0        ; space for 32 PCBs
stack:        times 32*256 dw 0       ; space for 32 512 byte stacks
current:      dw   0                  ; index of current pcb

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to print a number on screen
; takes the row no, column no, and number to be printed as parameters
printnum:     push bp
              mov  bp, sp
              push es
              push ax
              push bx
              push cx
              push dx
              push di

              mov  di, 80              ; load di with columns per row
              mov  ax, [bp+8]          ; load ax with row number
              mul  di                  ; multiply with columns per row
              mov  di, ax              ; save result in di
              add  di, [bp+6]          ; add column number
              shl  di, 1               ; turn into byte count
              add  di, 8               ; to end of number location

              mov  ax, 0xb800
              mov  es, ax              ; point es to video base
              mov  ax, [bp+4]          ; load number in ax
              mov  bx, 16              ; use base 16 for division
              mov  cx, 4               ; initialize count of digits
                                      
nextdigit:    mov  dx, 0               ; zero upper half of dividend
              div  bx                  ; divide by 10
              add  dl, 0x30            ; convert digit into ascii value
              cmp  dl, 0x39            ; is the digit an alphabet
              jbe  skipalpha           ; no, skip addition
              add  dl, 7               ; yes, make in alphabet code
skipalpha:    mov  dh, 0x07            ; attach normal attribute
              mov  [es:di], dx         ; print char on screen
              sub  di, 2               ; to previous screen location
              loop nextdigit           ; repeat for next digit

              pop  di
              pop  dx
              pop  cx
              pop  bx
              pop  ax
              pop  es
              pop  bp
              ret  6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; timer interrupt service routine
timer:        push ds
              push bx

              push cs
              pop  ds                 ; initialize ds to data segment

              mov  bx, [current]      ; read index of current in bx
              shl  bx, 1
              shl  bx, 1
              shl  bx, 1
              shl  bx, 1
              shl  bx, 1              ; multiply by 32 for pcb start
              mov  [pcb+bx+0], ax     ; save ax in current pcb
              mov  [pcb+bx+4], cx     ; save cx in current pcb
              mov  [pcb+bx+6], dx     ; save dx in current pcb
              mov  [pcb+bx+8], si     ; save si in current pcb
              mov  [pcb+bx+10], di    ; save di in current pcb
              mov  [pcb+bx+12], bp    ; save bp in current pcb
              mov  [pcb+bx+24], es    ; save es in current pcb

              pop  ax                 ; read original bx from stack
              mov  [pcb+bx+2], ax     ; save bx in current pcb
              pop  ax                 ; read original ds from stack
              mov  [pcb+bx+20], ax    ; save ds in current pcb
              pop  ax                 ; read original ip from stack
              mov  [pcb+bx+16], ax    ; save ip in current pcb
              pop  ax                 ; read original cs from stack
              mov  [pcb+bx+18], ax    ; save cs in current pcb
              pop  ax                 ; read original flags from stack
              mov  [pcb+bx+26], ax    ; save cs in current pcb
              mov  [pcb+bx+22], ss    ; save ss in current pcb
              mov  [pcb+bx+14], sp    ; save sp in current pcb

              mov  bx, [pcb+bx+28]    ; read next pcb of this pcb
              mov  [current], bx      ; update current to new pcb
              mov  cl, 5             
              shl  bx, cl             ; multiply by 32 for pcb start

              mov  cx, [pcb+bx+4]     ; read cx of new process
              mov  dx, [pcb+bx+6]     ; read dx of new process
              mov  si, [pcb+bx+8]     ; read si of new process
              mov  di, [pcb+bx+10]    ; read diof new process
              mov  bp, [pcb+bx+12]    ; read bp of new process
              mov  es, [pcb+bx+24]    ; read es of new process
              mov  ss, [pcb+bx+22]    ; read ss of new process
              mov  sp, [pcb+bx+14]    ; read sp of new process

              push word [pcb+bx+26]   ; push flags of new process
              push word [pcb+bx+18]   ; push cs of new process
              push word [pcb+bx+16]   ; push ip of new process
              push word [pcb+bx+20]   ; push ds of new process

              mov  al, 0x20
              out  0x20, al           ; send EOI to PIC

              mov  ax, [pcb+bx+0]     ; read ax of new process
              mov  bx, [pcb+bx+2]     ; read bx of new process
              pop  ds                 ; read ds of new process
              iret                    ; return to new process
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;routine to terminate the current thread
termthread:  push bp
             mov  bp,sp

             push bx
             push di
             push cx

             xor  bx,bx
             mov  di,[cs:pcb+bx+28]
             mov  cl,5
             shl  di,cl                 ; di points to process next of 0th

             mov  ax,[cs:current]
cagain:      cmp  ax,[cs:pcb+di+30]     ; is it the process to be terminated?
             je   found                 ; yes, goto found
             mov  bx,di                 ; no, set bx to di(bx contains previous process)
             mov  di,[cs:pcb+di+28]     ; next of di in di
             mov  cl,5                     
             shl  di,cl                 ; di points to next process now
             jmp  cagain                ; oterwise repeat

found:       mov  cx, [cs:pcb+di+28]    ; set next of prev(bx), to next of curr(di)
             mov  word[cs:pcb+bx+28],cx
             mov  word[cs:pcb+di+30],0xffff ;set the process no of deleted pcb to -1 for reuse
                                        ; the process is removed from list now
             jmp  $                     ; wait for a timer int, to switch to next process

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;return a free pcb block number in ax
getfree:   push bx
           push cx

           mov  ax,1
cnextblk:  mov  cl,5
           mov  bx,ax
           shl  bx,cl
           cmp  word[cs:pcb+bx+30],0xffff
           je   over
           inc  ax
           cmp  ax,32
           jne  cnextblk

over:      pop  cx
           pop  bx
           ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; software interrupt to register a new thread
; takes parameter block in ds:si
; parameter block has cs, ip, ds, es, and param in this order
initpcb:      push ax
              push bx
              push cx
              push di

              call getfree
              push ax                 ; save this process number on stack
              mov  bx, ax             ; read next available pcb index
              cmp  ax, 32             ; are all PCBs used
              je   exit               ; yes, exit
              
              mov  cl, 5              
              shl  bx, cl             ; multiply by 32 for pcb start

              mov  [cs:pcb+bx+30], ax ; set the process number of new pcb
              mov  ax, [si+0]         ; read code segment parameter
              mov  [cs:pcb+bx+18], ax ; save in pcb space for cs 
              mov  ax, [si+2]         ; read offset parameter
              mov  [cs:pcb+bx+16], ax ; save in pcb space for ip
              mov  ax, [si+4]         ; read data segment parameter
              mov  [cs:pcb+bx+20], ax ; save in pcb space for ds 
              mov  ax, [si+6]         ; read extra segment parameter
              mov  [cs:pcb+bx+24], ax ; save in pcb space for es

              mov  [cs:pcb+bx+22], cs ; set stack to our segment
              pop  di                 ; read this pcb's index, saved earlier on stack
              push di                 ; save it again on stack
              mov  cl, 9              
              shl  di, cl             ; multiply by 512 
              add  di, 256*2+stack    ; end of stack for this thread
              mov  ax, [si+8]         ; read parameter for subroutine
              sub  di, 2              ; decrement thread stack pointer
              mov  [cs:di], ax        ; pushing param on thread stack
              mov  ax,cs              ; read segment for terminating routine
              sub  di,2               ; decrement thread stack pointer
              mov  [cs:di],ax         ; push segment of terminating routine on thread stack
              mov  ax,termthread      ; ax=offset of terminating routine
              sub  di,2               ; decrement thread stack pointer
              mov  [cs:di],ax         ; push offset of  terminating routine on thread stack
              mov  [cs:pcb+bx+14], di ; save di in pcb space for sp

              mov  word [cs:pcb+bx+26], 0x0200 ; initialize flags
              mov  ax, [cs:pcb+28]    ; read next of 0th thread in ax
              mov  [cs:pcb+bx+28], ax ; set as next of new thread
              pop  ax                 ; read new thread index saved earlier on stack  
              mov  [cs:pcb+28], ax    ; set as next of 0th thread
          

exit:         pop  di
              pop  cx
              pop  bx
              pop  ax
              iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:        mov cx,31
              xor bx,bx
              add bx,32

ag:           mov word[cs:pcb+bx+30],0xffff   ;set process nos of all pcb's to -1
              add bx,32
              loop ag

              xor ax, ax
              mov es, ax              ; point es to IVT base

              mov word [es:0x80*4], initpcb
              mov [es:0x80*4+2], cs   ; hook software int 80
              cli
              mov word [es:0x08*4], timer
              mov [es:0x08*4+2], cs   ; hook timer interrupt
              sti

              mov dx, start
              add dx, 15
              mov cl, 4
              shr dx, cl

              mov ax, 0x3100          ; terminate and stay resident
              int 0x21
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; another multitasking TSR caller
[org 0x0100]
              jmp start

; parameter block layout:
; cs,ip,ds,es,param
;  0, 2, 4, 6,  8

paramblock:   times 5 dw 0            ; space for parameters 
lineno:       dw   0                  ; line number for next thread
chars:        db '\|/-'               ; chracters for rotating bar
message:      db 'moving hello'       ; moving string
message2:     db '            '       ; to erase previous string
messagelen:   dw 12                   ; length of above strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to print a number on screen
; takes the row no, column no, and number to be printed as parameters
printnum:     push bp
              mov  bp, sp
              push es
              push ax
              push bx
              push cx
              push dx
              push di

              mov  di, 80             ; load di with columns per row
              mov  ax, [bp+8]         ; load ax with row number
              mul  di                 ; multiply with columns per row
              mov  di, ax             ; save result in di
              add  di, [bp+6]         ; add column number
              shl  di, 1              ; turn into byte count
              add  di, 8              ; to end of number location

              mov  ax, 0xb800
              mov  es, ax             ; point es to video base
              mov  ax, [bp+4]         ; load number in ax
              mov  bx, 16             ; use base 16 for division
              mov  cx, 4              ; initialize count of digits

nextdigit:    mov  dx, 0              ; zero upper half of dividend
              div  bx                 ; divide by 10
              add  dl, 0x30           ; convert digit into ascii value
              cmp  dl, 0x39           ; is the digit an alphabet
              jbe  skipalpha          ; no, skip addition
              add  dl, 7              ; yes, make in alphabet code
skipalpha:    mov  dh, 0x07           ; attach normal attribute
              mov [es:di], dx         ; print char on screen
              sub  di, 2              ; to previous screen location
              loop nextdigit          ; repeat for next digit

              pop  di
              pop  dx
              pop  cx
              pop  bx
              pop  ax
              pop  es
              pop  bp
              ret  6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to print a string 
; takes row no, column no, address of string, and its length 
; as parameters
printstr:     push bp
              mov bp, sp
              push es
              push ax
              push bx
              push cx
              push dx
              push si
              push di

              mov  ax, 0xb800
              mov  es, ax             ; point es to video base

              mov  di, 80             ; load di with columns per row
              mov  ax, [bp+10]         ; load ax with row number
              mul  di                 ; multiply with columns per row
              mov  di, ax             ; save result in di
              add  di, [bp+8]         ; add column number
              shl  di, 1              ; turn into byte count

              mov  si, [bp+6]         ; string to be printed
              mov  cx, [bp+4]         ; length of string
              mov  ah, 0x07           ; normal attribute is fixed

nextchar:     mov  al, [si]           ; load next char of string
              mov  [es:di], ax        ; show next char on screen
              add  di, 2              ; move to next screen location
              add  si, 1              ; move to next char
              loop nextchar           ; repeat the operation cx times

              pop  di
              pop  si
              pop  dx
              pop  cx
              pop  bx
              pop  ax
              pop  es
              pop  bp
              ret  8
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to run as first thread
mytask:       push bp
              mov  bp, sp
              sub  sp, 2              ; thread local variable
              push ax
              push bx

              xor  ax, ax             ; use line number 0
              mov  bx, 70             ; use column number 70
              mov  word [bp-2], 0     ; initialize local variable 

printagain:   push ax                 ; line number 
              push bx                 ; column number 
              push word [bp-2]        ; number to be printed
              call printnum           ; print the number 
              inc  word [bp-2]        ; increment the local variable
              jmp  printagain         ; infinitely print

              pop  bx
              pop  ax
              mov  sp, bp
              pop  bp
              retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to run as second thread
mytask2:      push ax
              push bx
              push es

              mov  ax, 0xb800
              mov  es, ax             ; point es to video base
              xor  bx, bx             ; initialize to use first shape
              mov  cx, 0xffff

rotateagain:  mov  al, [chars+bx]     ; read current shape
              mov  [es:40], al        ; print at specified place
              inc  bx                 ; update to next shape
              and  bx, 3              ; take modulus with 4
              loop rotateagain        ; repeat infinitely

              pop  es
              pop  bx
              pop  ax
              retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subroutine to run as third thread
mytask3:      push bp
              mov  bp, sp
              sub  sp, 2              ; thread local variable
              push ax
              push bx
              push cx

              mov  word [bp-2], 0     ; initialize line number to 0

nextline:     push word [bp-2]        ; line number
              mov  bx, 50             
              push bx                 ; column number 50
              mov  ax, message
              push ax                 ; offset of string
              push word [messagelen]  ; length of string
              call printstr           ; print the string

              mov  cx, 0x100           
waithere:     push cx                 ; save outer loop counter
              mov  cx, 0xffff          
              loop $                  ; repeat ffff times
              pop  cx                 ; restore outer loop counter
              loop waithere           ; repeat 0x100 times

              push word [bp-2]        ; line number 
              mov  bx, 50             ; column number 50
              push bx  
              mov  ax, message2    
              push ax                 ; offset of blank string
              push word [messagelen]  ; length of string
              call printstr           ; print the string 

              inc  word [bp-2]        ; update line number
              cmp  word [bp-2], 25    ; is this the last line
              jne  skipreset          ; no, proceed to draw
              jmp  endit              ; yes, reset line number to 0

skipreset:    jmp  nextline           ; proceed with next drawing

endit:        pop  cx
              pop  bx
              pop  ax
              mov  sp, bp
              pop  bp
              retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:        mov  [paramblock+0], cs ; code segment parameter
              mov  word [paramblock+2], mytask ; offset parameter
              mov  [paramblock+4], ds ; data segment parameter
              mov  [paramblock+6], es ; extra segment parameter
              mov  word [paramblock+8], 0 ; parameter for thread
              mov  si, paramblock     ; address of param block in si
              int  0x80               ; multitasking kernel interrupt
              
              mov  [paramblock+0], cs ; code segment parameter
              mov  word [paramblock+2], mytask2 ; offset parameter
              mov  [paramblock+4], ds ; data segment parameter
              mov  [paramblock+6], es ; extra segment parameter
              mov  word [paramblock+8], 0 ; parameter for thread
              mov  si, paramblock     ; address of param block in si
              int  0x80               ; multitasking kernel interrupt

              mov  [paramblock+0], cs ; code segment parameter
              mov  word [paramblock+2], mytask3 ; offset parameter
              mov  [paramblock+4], ds ; data segment parameter
              mov  [paramblock+6], es ; extra segment parameter
              mov  word [paramblock+8], 0 ; parameter for thread
              mov  si, paramblock     ; address of param block in si
              int  0x80               ; multitasking kernel interrupt

              mov dx,start                ; end of resident portion
              add dx,15                   ; round up to next para
              mov cl,4
              shr dx,cl                   ; number of paras

              mov ax,0x3100               ; terminate and stay resident
              int 0x21
             
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


