home *** CD-ROM | disk | FTP | other *** search
- COMMENT %
- PRIM.ASM Copyright 1989 by Dlugosz Software
-
- This is the assembly language primitives needed by the C++ multitasking
- system. It is written for Zortech C++ large model, to assemble under
- MASM 5.1
-
- %
-
- .MODEL LARGE
-
- PUBLIC _from_scheduler,__DS, _tasker_install, _tasker_remove
- PUBLIC _preempt_off, _preempt_on, _task_yield
- ;for debugging...
- PUBLIC to_scheduler, new_timer_tick
-
- .DATA
- __DS dw SEG DGROUP
-
- .CODE
-
- old_timer LABEL DWORD
- old_timer_ofs dw ?
- old_timer_seg dw ?
-
- preempt_inhibit dw 1 ;starts out inhibited
-
- ;------------------------------------------------
- ; void preempt_off();
- ; void preempt_on();
- ; these functions change the state of the preempt_inhibit flag. They
- ; are in here for two reasons: 1)you must change the state in an
- ; uninterruptable cycle, and I don't want to trust the compiler to
- ; do so. 2)Zortech cannot access a FAR variable, and this flag is not
- ; in DGROUP. It must be in the CSEG of the timer interrupt.
-
- _preempt_off PROC FAR
- inc CS:preempt_inhibit
- ret
- _preempt_off ENDP
-
- _preempt_on PROC FAR
- dec CS:preempt_inhibit
- ret
- _preempt_on ENDP
-
-
- ;------------------------------------------------
- ; void* from_scheduler(unsigned state[]);
- ; This is where the scheduler gives up control to another
- ; task, and where another task returns to the scheduler.
-
- stack_save_SP dw ?
- stack_save_SS dw ?
-
- taskSS dw ?
- taskSP dw ?
-
- _from_scheduler PROC FAR
- push BP
- mov BP,SP
- ;The only thing restored when control is transferd back is the CS:IP
- ;which is obveously required since that tells where code is executing.
- ;It can restore the SS:SP from code segment variables, and than restore
- ;other registers.
-
- ;Zortech manual, page 233: Functions must preserve SI,DI,BP,SP,SS,CS,DS.
- ;functions can alter AX,BX,CX,DX,ES. Direction flag must be set to
- ;forward. I reset the D flag in case the interrupted function was in the
- ;middle of a string operation. Preserving SI and DI is pretty standard,
- ;but ES varies on the compiler. You might add it to the list if the
- ;compiler used on the C++ end of the scheduler needs it, and you can
- ;likewise remove SI & DI if the C++ end will not need them.
- push SI
- push DI
- push DS
- ;after all pushes are done, store stack pointer somewhere safe.
- mov CS:stack_save_SS,SS
- mov CS:stack_save_SP,SP
- ;I am now done saving the required state of the scheduler. I can now
- ;resume the state of the interrupted task.
- lds BX,[BP+6] ;should be SS:SP
- mov AX,DS
- mov SS,AX ;SS and SP
- mov SP,BX ;must be loaded consecutivly
- pop AX
- pop BX
- pop CX
- pop DX
- pop SI
- pop DI
- pop BP
- pop ES
- pop DS
- dec CS:preempt_inhibit
- iret ;pops Flags,IP,CS
- _from_scheduler ENDP
- ; This is a function sawed in half
- to_scheduler PROC FAR
- ;this restores the stack and returns to the scheduler.
- ;an interrupt jumps to this point.
- mov SS, CS:stack_save_SS
- mov SP, CS:stack_save_SP ;SS and SP must be loaded consecutively
- pop DS
- pop DI
- pop SI
- pop BP
- cld
- mov DX,CS:taskSS
- mov AX,CS:taskSP
- ret
- to_scheduler ENDP
-
- ;------------------------------------------------
- ; void task_yield()
- ; give up control to the scheduler
-
- _task_yield PROC FAR
- inc CS:preempt_inhibit
- ;CS,IP,and already on stack. I don't need to save the general purpose
- ; registers, and I don't need to save the flags but I need to get the
- ; proper stack setup for returning. I need to insert the flags over
- ; the return address.
- pop AX
- pop BX
- pushf ;the conditional flags don't matter, but the system flags
- ; need to be correct.
- push BX
- push AX
- ; now save the other regs
- push DS
- push ES ;don't really need to save this one, but no shortcut
- push BP
- push DI
- push SI
- ;don't need to save AX,BX,CX,DX
- sub SP,8 ;leave room on the stack for them
- mov CS:taskSS,SS
- mov CS:taskSP,SP
- jmp to_scheduler
- _task_yield ENDP
-
-
-
- ;------------------------------------------------
- ; new timer interrupt
- ; this saves the state, and then alters the return address so that
- ; the interrupt will return to the scheduler. Then the normal service
- ; routine is jumped to.
-
- new_timer_tick PROC FAR ;actually, its an INTERRUPT
- cmp CS:preempt_inhibit,0
- jne skip_switch ;don't do any of this
- inc CS:preempt_inhibit
- ;CS,IP,and Fl already on stack.
- push DS
- push ES
- push BP
- push DI
- push SI
- push DX
- push CX
- push BX
- push AX
- mov CS:taskSS,SS
- mov CS:taskSP,SP
- ; now put the new return address on the stack
- mov AX, 246h
- push AX ;flags upon returning to switcher
- push CS ;same segment as me
- mov AX, OFFSET CS:to_scheduler
- push AX
- ;operation complete. Do the real timer stuff.
- skip_switch:
- jmp old_timer
- new_timer_tick ENDP
-
-
- ;------------------------------------------------
- ; void tasker_install();
- ; install switcher as front end to timer tick
- _tasker_install PROC FAR
- push DS
- mov AX,3508h
- int 21h ;old timer int in ES:BX
- mov old_timer_seg, ES
- mov old_timer_ofs, BX
- mov AX, SEG new_timer_tick
- mov DS,AX
- mov DX, OFFSET new_timer_tick
- mov AX,2508h
- int 21h ;new timer in place. We're off!
- pop DS
- ret
- _tasker_install ENDP
-
- ;------------------------------------------------
- ; void tasker_remove();
- ; restore the timer to its normal state
- _tasker_remove PROC FAR
- push DS
- mov AX,2508h
- lds DX, old_timer
- int 21h
- pop DS
- ;return task's stack value
- ret
- _tasker_remove ENDP
-
-
- END
-