; ; --- Version 2.2 90-10-12 15:33 --- ; ; CTask - Local Stack Switch handler module. ; ; Public Domain Software written by ; Thomas Wagner ; Ferrari electronic Gmbh ; Beusselstrasse 27 ; D-1000 Berlin 21 ; Germany ; ; This file is new with version 1.2. ; ; In this version, all Hard- and Software INT handlers switch ; to a local stack (INT 16 for AH=0 only). Stack overruns have ; been observed, especially in conjunction with VDISK and the ; Microsoft Editor. Some Microsoft products (and some other TSR's) ; link into interrupts without switching to local stacks. This ; can lead to situations where just a few words are available ; for use by the CTask handlers. In the abovementioned combination, ; a keyboard interrupt occurring while VDISK is active leaves ; four (!) words of stack space for INT 16 execution. ; Since most of the interrupts should be reentrant, a number ; of stacks are maintained (constant NUM_STACKS), and the call ; is passed without a stack switch if all local stacks are in use. ; ; To avoid duplication of the stack switch and register save code ; in all handlers, a global stack switcher is implemented here. ; It pushes the address of a back-switch routine on the new stack, ; so the calling routine doesn't have to care about explicitly ; calling the restore routine. ; ; No registers may be pushed prior to the call to switch_stack if ; the auto-switchback is to be used, since switch_stack assumes ; the old stack to contain the callers flags at offset 4. ; ; When old_stack is called to explicitly switch back, stack ; contents prior to the call to switch_stack are insignificant. ; ; Switch_stack will set up registers as follows: ; BP = base of register save area on stack ; DS,ES = DGROUP ; Direction flag clear ; Interrupts enabled ; All other registers are unchanged. ; ; To allow preservation of the flag state on entry to switch_stack, ; the flags are pushed on the save area, but not automatically ; restored. Copy the entry_flags into caller_flags to restore ; the entry flags on return. ; ; old_stack will not restore flags from the stack, it will preserve ; the flag state. ; ; Version 2.1 slightly changes the handling of flag restoration ; on auto-backswitch. The old algorithm restored the caller flags ; on entry to the backswitch routine, which caused problems with ; debuggers if the interrupted code was being single-stepped. ; Now the original flags always will be restored on exit of the ; back-switch, regardless of the return used (IRET or RET 2). ; To modify the flags returned, you have to explicitly modify ; the caller_flags field in the saved register area. ; name tskstck ; include tsk.mac include tskdeb.h ; .tsk_model ; Pubfunc tsk_switch_stack Pubfunc tsk_old_stack ; IF DEBUG AND DEB_FLASHERS Locext tsk_inccdis extrn tsk_debflash: word extrn tsk_dtposn: dword ENDIF ; ; STACKSIZE = 256 ; local stack size (bytes) NUM_STACKS = 8 ; Number of local stacks ; IF FAR_STACKS ctask_stacks segment word public 'CTASK_STACKS' ELSE .tsk_data ENDIF ; lstacks label word db NUM_STACKS * STACKSIZE dup (?) ; IF FAR_STACKS ctask_stacks ends .tsk_data ENDIF ; IF NOT ROM_CODE ; .tsk_edata .tsk_code ; sseg equ ENDIF ; stack_full dw 0 ; count 'all stacks in use' events IF DEBUG AND DEB_FLASHERS usecnt db 0 ENDIF EVEN ; ; IF ROM_CODE r_ds dw ? ENDIF r_ss dw ? r_sp dw ? r_bx dw ? r_cx dw ? r_bp dw ? call_ip dw ? call_cs dw ? retaoff dw ? IF LOCALS_FAR retaseg dw ? ENDIF sflags dw ? ; ; Caution: Since a zero slot offset is used to detect ; that no stack was allocated, the "stacks" array may not ; be located at offset 0 in the segment. ; stacks label word xst = offset lstacks + STACKSIZE rept NUM_STACKS dw xst xst = xst + STACKSIZE endm ; IF ROM_CODE ; .tsk_edata .tsk_code ; sseg equ ENDIF ; tsk_dgroup dw @CTASK_DATA ; IF FAR_STACKS stackseg dw SEG ctask_stacks ELSE stackseg equ ENDIF ; ; Localfunc tsk_switch_stack ; IF ROM_CODE push ds mov ds,cs:tsk_dgroup pop r_ds ENDIF pushf cli pop sseg:sflags pop sseg:retaoff ; return address IF LOCALS_FAR pop sseg:retaseg ENDIF mov sseg:r_bx,bx ; save bx & cx in CS mov sseg:r_cx,cx mov sseg:r_bp,bp ; save BP mov bp,sp mov bx,[bp] ; get caller's IP mov sseg:call_ip,bx mov bx,2[bp] ; get caller's CS mov sseg:call_cs,bx mov bp,4[bp] ; get caller's flags into BP ; mov cx,NUM_STACKS ; total number of stacks mov bx,offset stacks ; stack table stlp: cmp word ptr sseg:[bx],0 ; in use ? jne st_found ; jump if free inc bx inc bx loop stlp ; ; No unused stack, continue on caller's stack ; IF DEBUG AND DEB_FLASHERS push ds mov ds,cs:tsk_dgroup cmp tsk_debflash,0 je debdd0 push ax mov ax,DEBP_CNTSTOFL call tsk_inccdis pop ax debdd0: pop ds ENDIF inc sseg:stack_full xor bx,bx jmp short st_old ; ; Stack found, perform switch ; st_found: mov sseg:r_ss,ss ; save SS/SP mov sseg:r_sp,sp mov ss,cs:stackseg ; load new SS/SP mov sp,sseg:[bx] mov cx,sp ; save new SP value mov word ptr sseg:[bx],0 ; mark stack in use push sseg:r_ss ; push old SS/SP push sseg:r_sp st_old: push sseg:r_bx push sseg:r_cx push bx ; push stack slot index push cx ; push new SP ; push ax ; push remaining regs push dx push si push di push sseg:r_bp IF ROM_CODE push r_ds ELSE push ds ENDIF push es ; push bp ; push caller's flags push sseg:call_cs ; push caller's CS push sseg:call_ip ; push caller's IP ; push sseg:sflags ; push entry flags push cs ; and special return mov cx,offset @stsw_retn push cx mov bx,sseg:r_bx ; restore regs bx,cx mov cx,sseg:r_cx mov bp,sp ; set BP to stack bottom IF LOCALS_FAR push sseg:retaseg ENDIF push sseg:retaoff ; push retaddr on new stack cld sti mov ds,cs:tsk_dgroup mov es,cs:tsk_dgroup IF DEBUG AND DEB_FLASHERS cmp slot_idx[bp],0 je debdd1 cmp tsk_debflash,0 je debdd1 inc sseg:usecnt push ax push ds push si mov al,sseg:usecnt add al,'0' lds si,tsk_dtposn mov DEBP_STACKNUM[si],al pop si pop ds pop ax debdd1: ENDIF ret ; restore flags & return ; tsk_switch_stack endp ; ; Localfunc tsk_old_stack ; pushf ; save flags cli IF ROM_CODE mov ds,cs:tsk_dgroup ENDIF pop sseg:sflags pop sseg:retaoff ; return address IF LOCALS_FAR pop sseg:retaseg ENDIF add sp,12 ; discard dummy return & flags ; IF DEBUG AND DEB_FLASHERS mov ds,cs:tsk_dgroup cmp slot_idx[bp],0 je debdd2 cmp tsk_debflash,0 je debdd2 dec sseg:usecnt mov al,sseg:usecnt add al,'0' lds si,tsk_dtposn mov DEBP_STACKNUM[si],al IF ROM_CODE mov ds,cs:tsk_dgroup ENDIF debdd2: ENDIF ; pop es ; restore regs IF ROM_CODE pop r_ds ELSE pop ds ENDIF pop bp pop di pop si pop dx pop ax ; pop cx ; new SP pop bx ; stack slot index or bx,bx ; no new stack? jnz old_sw pop cx pop bx jmp short old_ret old_sw: mov sseg:[bx],cx ; reset stack slot pop cx pop bx pop sseg:r_sp pop ss ; old SS mov sp,sseg:r_sp old_ret: IF LOCALS_FAR push sseg:retaseg ENDIF push sseg:retaoff ; push retaddr on old stack push sseg:sflags IF ROM_CODE mov ds,r_ds ENDIF popf ; restore flags ret ; and return ; tsk_old_stack endp ; ; stsw_retn is where the call returns to when there is no explicit ; call to old_stack. ; @stsw_retn proc far ; cli add sp,4 ; discard caller CS/IP IF ROM_CODE mov ds,cs:tsk_dgroup ENDIF pop sseg:sflags ; store caller's flags in CS ; IF DEBUG AND DEB_FLASHERS mov ds,cs:tsk_dgroup cmp slot_idx[bp],0 je debdd3 cmp tsk_debflash,0 je debdd3 dec sseg:usecnt mov al,sseg:usecnt add al,'0' lds si,tsk_dtposn mov DEBP_STACKNUM[si],al IF ROM_CODE mov ds,cs:tsk_dgroup ENDIF debdd3: ENDIF ; pop es ; restore regs IF ROM_CODE pop r_ds ELSE pop ds ENDIF pop bp pop di pop si pop dx pop ax ; pop cx ; new SP pop bx ; stack slot index or bx,bx ; no new stack? jnz retn_sw pop cx pop bx jmp short retn_ret retn_sw: mov sseg:[bx],cx ; reset stack slot pop cx pop bx pop sseg:r_sp pop ss ; old SS mov sp,sseg:r_sp retn_ret: push bp mov bp,sp push ax mov ax,sseg:sflags ; push flags on old stack mov 6[bp],ax pop ax pop bp IF ROM_CODE mov ds,r_ds ENDIF iret ; @stsw_retn endp ; .tsk_ecode end