home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR41
/
CTASK22D.ZIP
/
TSKTIM.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-06-08
|
14KB
|
665 lines
;
; --- Version 2.2 93-06-08 10:00 ---
;
; CTask - Timer interrupt handler (IBM specific)
;
; Public Domain Software written by
; Thomas Wagner
; Ferrari electronic Gmbh
; Beusselstrasse 27
; D-1000 Berlin 21
; Germany
;
; NOTE: Logic in this module has been changed to accommodate two
; different ways to handle chaining to the original INT 8
; entry in version 1.2.
;
; CAUTION: This module can only be installed in the primary kernel.
; It is not ROMable.
;
; For non-IBM applications, you have to completely replace
; this module and write your own.
; You should be able to use this module at least as a model
; by throwing out about 80%, mainly the initialization
; and interrupt chaining stuff. All you really need is the
; tsk_timer_counter increment, and the ticker processing,
; which can be left unchanged. The section enclosed by
; IF INT8_EARLY most likely is the one you should use as
; reference, since you will usuallly not have other routines
; chained to the timer tick in an embedded system.
;
name tsktim
;
include tsk.mac
include tskdeb.h
;
.tsk_model
;
Pubfunc tsk_install_timer
Pubfunc tsk_remove_timer
IF NOT INT8_EARLY
Pubfunc tsk_chain_timer
ENDIF
;
extrn sched_int: far
Globext inc_counter
Globext set_flag
;
extrn tsk_key_avail: byte
extrn tsk_glob_rec: byte
extrn tsk_timer_counter:word
extrn tsk_instflags: word
IF NOT INT8_EARLY
extrn tsk_int8_counter:word
ENDIF
;
IF DEBUG AND DEB_FLASHERS
Locext tsk_inccdis
extrn tsk_debflash: word
extrn tsk_dtposn: dword
ENDIF
;
;
STACKSIZE = 128 ; local stack size (words)
;
timer equ 40h ; 8253 timer base I/O address
inta00 equ 20h ; 8259 int controller base
eoi equ 20h ; unspecific EOI
;
intseg segment at 0
;
org 8*4
tintofs dw ? ; timer interrupt entry
tintseg dw ?
intseg ends
;
biosdataseg segment at 40h
org 1ah
kbdptr1 dw ?
kbdptr2 dw ?
biosdataseg ends
;
IF FAR_STACKS
ctask_stacks segment word public 'CTASK_STACKS'
ELSE
.tsk_data
ENDIF
;
dw STACKSIZE dup(?)
local_stack label word
;
IF FAR_STACKS
ctask_stacks ends
.tsk_data
ENDIF
;
divisor dw ?
timflag dw ?
timcnt dw ?
sys_ticks dw ?
;
.tsk_edata
.tsk_code
;
tsk_dgroup dw @CTASK_DATA
;
IF FAR_STACKS
stackseg dw SEG ctask_stacks
ELSE
stackseg equ <tsk_dgroup>
ENDIF
;
timer_save label dword ; in CSEG to allow addressing
tsofs dw ?
tsseg dw ?
;
r_ss dw ?
r_sp dw ?
in_timer db 0
;
;----------------------------------------------------------------------
;
IF NOT INT8_EARLY
;
; Timer interrupt handler, late INT 8 processing
;
; Normal timer tick. The tick counter is incremented, and
; the interrupt controller is checked for other pending interrupts.
; If the timer tick occurred during processing of another interrupt,
; we may not call the scheduler, since this would delay the
; interrupt handler.
;
; Note that an EOI is issued here to allow interrupts to occur
; during further processing. The original INT 8 handler will be
; chained to from a special task. The reason behind this is that
; some TSR's link into the timer interrupt and may do some lengthy
; processing there. To allow the TSR to be preempted, we must use
; a task for the INT 8 processing.
;
@timer_int_late proc far
;
; first, check for overrun. If we're already processing a tick,
; we simply ignore this one. That's not that great a way to
; react, but anything else would easily mess things up, and we're
; already in trouble anyway if this should happen.
;
cmp cs:in_timer,0
je timer_ok
push ax
mov al,eoi ; issue EOI
out inta00,al
pop ax
iret
;
; Not a second interrupt, continue processing. First, set the
; in_timer marker, and switch to our local stack.
;
timer_ok:
inc cs:in_timer
mov cs:r_ss,ss
mov cs:r_sp,sp
mov ss,cs:stackseg
mov sp,offset local_stack
sti
cld
push ax
push ds
mov ds,cs:tsk_dgroup
;
IF DEBUG AND DEB_FLASHERS
cmp tsk_debflash,0
je debdd0
mov ax,DEBP_CNTTICK
call tsk_inccdis
debdd0:
ENDIF
;
push es ; save other regs
push bx
push cx
push dx
push si
push di
push bp
callp inc_counter,<<ds,#tsk_timer_counter>> ; increase timer tick counter
;
; Now the timer counter is decremented. If it is zero,
; we must chain to the original INT 8, so the counter for
; the chain task is incremented.
;
dec timcnt ; decrement tick count
jnz no_pass ; pass on this int if zero
;
mov ax,sys_ticks
mov timcnt,ax ; re-init tick counter
;
callp inc_counter,<<ds,#tsk_int8_counter>>
;
; Now we decrement all installed tick counters
;
no_pass:
les bx,tsk_glob_rec.ticker_chain
;
count_dec:
mov ax,es
or ax,bx
jz tick_ready
mov ax,es:ticklo[bx]
or ax,es:tickhi[bx]
jz count_next
sub es:ticklo[bx],1
sbb es:tickhi[bx],0
count_next:
les bx,es:ticknext[bx]
jmp count_dec
;
; Now check the BIOS keyboard buffer.
; If there are chars in the buffer, set the tsk_key_avail flag
; to wake up tasks waiting for the keyboard. This check is required
; to find out about keyboard stuffers placing characters directly
; into the keyboard buffer.
;
tick_ready:
mov ax,SEG biosdataseg
mov es,ax
assume es:biosdataseg
mov ax,es:kbdptr1
cmp ax,es:kbdptr2
assume es:@CTASK_DATA
je tick_exit
callp set_flag,<<ds,#tsk_key_avail>>
;
tick_exit:
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop es
;
cli
mov al,eoi ; issue EOI
out inta00,al
mov al,0bh ; access int control reg
out inta00,al
in al,inta00 ; ints pending?
or al,al
jnz tim_retn ; don't schedule if other ints active
IF DEBUG AND DEB_FLASHERS
cmp tsk_debflash,0
je debdda1
push bx
lds bx,tsk_dtposn
mov byte ptr [bx+DEBP_CNTTICK+DEBFLASH_NDIGS*2],'+'
pop bx
debdda1:
ENDIF
pop ds
pop ax
mov ss,cs:r_ss
mov sp,cs:r_sp
dec cs:in_timer
jmp sched_int ; else schedule
;
tim_retn:
IF DEBUG AND DEB_FLASHERS
cmp tsk_debflash,0
je debdda2
push bx
lds bx,tsk_dtposn
mov byte ptr [bx+DEBP_CNTTICK+DEBFLASH_NDIGS*2],'-'
pop bx
debdda2:
ENDIF
pop ds
pop ax
mov ss,cs:r_ss
mov sp,cs:r_sp
dec cs:in_timer
iret
;
@timer_int_late endp
;
ENDIF
;
;----------------------------------------------------------------------
;
IF NOT INT8_LATE
;
; Timer interrupt handler, early INT 8 processing
;
; With this interrupt handler, the original INT 8 is chained to
; directly, before calling the scheduler.
;
; This avoids compatibility problems with TSR's that do strange
; things in the timer tick, but it may lead to problems within
; CTask, should the strange TSR decide to loop in the INT 8 handler,
; or even to never return. Oh well, you can't please all TSR's all
; of the time...
;
@timer_int_early proc far
;
cld
push ax
push ds
mov ds,cs:tsk_dgroup
;
IF DEBUG AND DEB_FLASHERS
cmp tsk_debflash,0
je debdd1
mov ax,DEBP_CNTTICK
call tsk_inccdis
debdd1:
ENDIF
;
; The timer counter is decremented. If it is zero,
; we chain directly to the original INT 8.
; Since the local stack is not yet in use,
; multiple entries into this part are possible.
;
xor ah,ah ; no delayed tick
dec timcnt ; decrement tick count
jnz eno_pass
;
mov ax,sys_ticks
mov timcnt,ax ; re-init tick counter
;
; Version 2.1 adds a check for scheduler active here.
; We may not call the original INT 8 if the scheduler is active,
; since this would crash the system when the old INT 8 causes
; a task_wait. See tskasm.asm for a more detailed explanation.
;
cmp tsk_glob_rec.in_sched,0
je call_oldint
;
mov ah,1 ; mark delayed tick
jmp short eno_pass
;
call_oldint:
sti
pop ds
pop ax
pushf
call cs:timer_save ; call original INT 8
push ax
push ds
mov ds,cs:tsk_dgroup
xor ah,ah ; no delayed tick
jmp short e_pass
;
; On return from INT 8 processing, or when INT 8 was not chained,
; processing is pretty much the same as in timer_int_late.
;
eno_pass:
mov al,eoi ; issue EOI when not chained
out inta00,al
;
e_pass:
cli
cmp cs:in_timer,0
je etimer_ok
pop ds
pop ax
iret
;
etimer_ok:
inc cs:in_timer
mov cs:r_ss,ss
mov cs:r_sp,sp
mov ss,cs:stackseg
mov sp,offset local_stack
sti
cld
;
push es ; save other regs
push bx
push cx
push dx
push si
push di
push bp
;
; If the tick was delayed, we have to notify the tsk_int8 to
; process an additional tick when there's time.
;
IF NOT INT8_EARLY
or ah,ah ; check delay flag
jz no_delay
callp inc_counter,<<ds,#tsk_int8_counter>>
ENDIF
;
no_delay:
callp inc_counter,<<ds,#tsk_timer_counter>> ; increase timer tick counter
;
; Now we decrement all installed tick counters
;
les bx,tsk_glob_rec.ticker_chain
;
ecount_dec:
mov ax,es
or ax,bx
jz etick_ready
mov ax,es:ticklo[bx]
or ax,es:tickhi[bx]
jz ecount_next
sub es:ticklo[bx],1
sbb es:tickhi[bx],0
ecount_next:
les bx,es:ticknext[bx]
jmp ecount_dec
;
; Now check the BIOS keyboard buffer.
; If there are chars in the buffer, set the tsk_key_avail flag
; to wake up tasks waiting for the keyboard. This check is required
; to find out about keyboard stuffers placing characters directly
; into the keyboard buffer.
;
etick_ready:
mov ax,SEG biosdataseg
mov es,ax
assume es:biosdataseg
mov ax,es:kbdptr1
cmp ax,es:kbdptr2
assume es:@CTASK_DATA
je etick_exit
callp set_flag,<<ds,#tsk_key_avail>>
;
etick_exit:
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop es
;
cli
mov al,0bh ; access int control reg
out inta00,al
in al,inta00 ; ints pending?
or al,al
jnz etim_retn ; don't schedule if other ints active
mov ss,cs:r_ss
mov sp,cs:r_sp
pop ds
pop ax
dec cs:in_timer
jmp sched_int ; else schedule
;
etim_retn:
mov tsk_glob_rec.pretick,1 ; mark that we missed a schedule
mov ss,cs:r_ss
mov sp,cs:r_sp
pop ds
pop ax
dec cs:in_timer
iret
;
@timer_int_early endp
;
ENDIF
;
;---------------------------------------------------------------------
;
; Install timer.
; The scheduler is not called on this first tick.
;
@tim_install proc far
;
push ax
push ds
push es
;
mov ds,cs:tsk_dgroup
;
mov timflag,0 ; signal init ready
mov ax,sys_ticks
mov timcnt,ax ; init tick counter
mov al,36h
out timer+3,al ; setup to load divisor
mov al,byte ptr divisor
out timer,al ; lsb
mov al,byte ptr divisor+1
out timer,al ; msb
;
xor ax,ax
mov es,ax
assume es:intseg
;
IF INT8_EARLY
mov ax,offset @timer_int_early
ELSE
mov ax,offset @timer_int_late
IF NOT INT8_LATE
test tsk_instflags,IFL_INT8_DIR
jz tim_inst_1
mov ax,offset @timer_int_early
ENDIF
ENDIF
;
tim_inst_1:
mov es:tintofs,ax
mov es:tintseg,cs
;
pop es
pop ds
pop ax
jmp cs:timer_save
;
@tim_install endp
;
;------------------------------------------------------------------------
;
; Un-Install timer (wait until system tick count reached).
; The scheduler is not called while waiting for the tick count.
;
@tim_uninstall proc far
;
push ax
push ds
;
mov ds,cs:tsk_dgroup
;
cli
dec timcnt ; decrement tick count
jz uninit ; go un-install if zero
mov al,eoi ; else just issue EOI
out inta00,al
pop ds
pop ax
iret
;
; Uninstall timer int handler
;
uninit:
mov timflag,0 ; mark un-install complete
mov al,36h ; setup to load divisor
out timer+3,al
mov al,0 ; divisor 0 means 65536
out timer,al ; lsb
out timer,al ; msb
;
push es
xor ax,ax
mov es,ax
assume es:intseg
mov ax,cs:tsofs ; restore vector
mov tintofs,ax
mov ax,cs:tsseg
mov tintseg,ax
pop es
pop ds
pop ax
jmp cs:timer_save ; pass on interrupt
;
@tim_uninstall endp
;
;----------------------------------------------------------------------
;
IF NOT INT8_EARLY
;
; void far tsk_chain_timer (void)
;
; Pass timer tick on to interrupt 8 chain.
;
Localfunc tsk_chain_timer
;
IF DEBUG AND DEB_FLASHERS
cmp tsk_debflash,0
je debdd2
mov ax,DEBP_CNTTCHAIN
call tsk_inccdis
debdd2:
ENDIF
pushf
cli
call cs:timer_save
ret
;
tsk_chain_timer endp
;
ENDIF
;
;
; void near tsk_install_timer (word divisor, word sys_ticks)
;
; This routine installs the timer tick int handler.
; The timer chip is reprogrammed on the next tick.
;
Localfunc tsk_install_timer,<pdivisor: word, psysticks: word>
;
IFDEF LOAD_DS
push ds
mov ds,cs:tsk_dgroup
ENDIF
;
mov ax,pdivisor
mov divisor,ax
mov ax,psysticks
mov sys_ticks,ax
mov timflag,1 ; set init-flag
xor ax,ax
mov es,ax ; establish addressing for intseg
assume es:intseg
;
mov ax,tintofs ; save old timer int addr
mov tsofs,ax
mov ax,tintseg
mov tsseg,ax
cli
mov tintofs,offset @tim_install ; set new timer int addr
mov tintseg,cs
sti
assume es:nothing
wait_set:
cmp timflag,0 ; wait until timer started
jne wait_set
IFDEF LOAD_DS
pop ds
ENDIF
ret
;
tsk_install_timer endp
;
;
; void far tsk_remove_timer (void)
;
; This routine un-installs the timer tick int handler.
; The timer chip is reprogrammed & the interrupt vector
; restored when the system tick count reaches zero.
;
Localfunc tsk_remove_timer
;
IFDEF LOAD_DS
push ds
mov ds,cs:tsk_dgroup
ENDIF
;
mov timflag,2 ; set un-init flag for timer
xor ax,ax
mov es,ax ; establish addressing for intseg
assume es:intseg
;
cli
mov tintofs,offset @tim_uninstall ; set new timer int addr
mov tintseg,cs
sti
assume es:nothing
wait_tim:
sti ; just to be safe
cmp timflag,0 ; wait until int un-installed
jne wait_tim
IFDEF LOAD_DS
pop ds
ENDIF
ret
;
tsk_remove_timer endp
;
.tsk_ecode
end