home *** CD-ROM | disk | FTP | other *** search
- PAGE 60,132
- TITLE Ram-Resident Program Shell, Preliminary Version (Lesson 2)
- SUBTTL Date 02-02-87 -- Tutorial for Microsoft Forum Version 0.03
- ;-----------------------------------------------------------------------------
- ; Ram-Resident Program Shell
- ;
- ;
- ;The software, documentation and source code are:
- ;
- ; (C) Copyright 1986, 1987
- ; Chip Rabinowitz
- ; All Rights Reserved
- ;
- ; 51 East Rogues Path
- ; Huntington Station NY 11746
- ; Assistant SysOp, Microsoft Forum, Compuserve PPN 76703,350
- ;
- ;
- ;COPYRIGHT NOTICE AND WARRANTY INFORMATION
- ;-----------------------------------------
- ;
- ;This document ("the source code"), other accompanying written and disk-based
- ;notes and specifications ("the documentation"), and all referenced and related
- ;program files accompanying the source code and the documentation ("the
- ;software") are copyrighted by Chip Rabinowitz.
- ;
- ;This program has been uploaded to Compuserve as part of a Tutorial in how
- ;to write Terminate-But-Stay-Resident programs, being conducted on
- ;Compuserve's Microsoft Special Interest Group during early 1987/
- ;
- ;This code (and all other code uploaded by the Copyright Holder as a
- ;part of this TSR Tutorial) may be used with the following restrictions:
- ;
- ;(1) If all or part of this code is used as part of a software package of
- ;ANY kind, the following acknowledgement must be used in the Documentation
- ;accompanying said package:
- ;
- ; 'Parts of the Resident Program Management for this Product are'
- ; 'Copyright (C) 1986, 1987 by Chip Rabinowitz, and are incorporated'
- ; 'into this product courtesy of the Ringmaster Development Team'
- ;
- ;(2) If all or part of this code is used as part of a software package that
- ;is placed in the public domain, no further restrictions apply.
- ;
- ;(3) If all or part of this code is used as part of a software package that
- ;is being distributed as 'shareware', a one-time-only donation of $10 will be
- ;accepted for the express purpose of continuing research into TSR standards.
- ;Note that this is a voluntary contribution, and should be sent to the
- ;Ringmaster Development Team at the address printed above.
- ;
- ;(4) If all or part of this code is used as part of a software package that
- ;will be distributed as a commercial product, a one-time-only payment of
- ;$25 will be accepted the express purpose of continuing research into TSR
- ;standards. Note that in THIS CASE ONLY, this is not a voluntary
- ;contribution. Commercial Developers are REQUIRED to make payment prior to
- ;incorporating these routines into their product.
- ;
- ;No copy of the source code may be distributed or given away without the
- ;accompanying documentation; and this notice must not be removed.
- ;
- ;There is no warranty of any kind associated with this software, and the
- ;copyright owner is not liable for damages of any kind. By using this
- ;software, you agree to this.
- ;
- ;
-
- XON equ 11h
- XOFF equ 13h
- TRUE equ 1
- FALSE equ 0
- CR equ 0dh
- LF equ 0ah
- TAB equ 9
-
-
- HOTKEYON equ 01h ;hot key pressed
- SHIFTSON equ 02h ;shift states match
- TSRACTIVE equ 04h ;tsr is running in foreground
- INT28ACTIVE equ 08h ;INT28 routine is running in background
-
- KB_DATA equ 60h
- KB_CTL equ 61h
- KB_FLG1 equ 17h
- KB_FLG2 equ 18h
- BIOS_DATA equ 40h
-
- code segment para public 'CODE'
- org 100h
- assume cs:code, ds:code, es:code
- start: jmp begin
-
- db 1bh,'[2J'
- cpyrt db 'Ram-Resident Program Shell, Preliminary Version (Lesson 2)',
- db CR,LF,LF
- db 'Copyright (C) 1986, 1987 Chip Rabinowitz',
- db CR,LF,'All Rights Reserved',CR,LF,LF,'$'
- hello db 'Hello World!',CR,LF,'$'
- bad_dos_msg db 'Incompatible DOS version .... Aborting!',07h,CR,LF,'$'
-
- tsr_parms:
- PSP dw ? ;PSP of TSR
- OLDPSP dw ? ;location to save current PSP
- DTA dd ? ;DTA of TSR
- OLDDTA dd ? ;location to save current DTA
- DSEG dw ? ;TSR's Data Segment
- SSEG dw ? ;TSR's Stack Segment ....
- SPTR dw 0fffeh ;.... and Stack Pointer
- ISSEG dw 0 ;interrupted SS ...
- ISPTR dw 0 ;..... and SP
- SHIFTST db 0 ;Shift State to Activate
- HOTKEY db 0ffh ;scan code to activate
- STATUS dw 0 ;current status words
- POPUP dd 0 ;far pointer to popup routine
-
- intflags db 0
- ININT13 equ 04h ;Bios disk interrupt
- ININT21 equ 08h ;DOS interrupt
-
- dosversion db 0 ;current version of DOS
- waitcount db 0 ;wait to activate count
-
- indosflag dd 0 ;Pointer to INDOS flag
- doscriterr dd 0 ;Pointer to DOS Critical Error Flag
-
- dossseg dw 0 ;pointer to dos stack segment
- dossptr dw 0 ;pointer to dos stack pointer
- dossize dw 0 ;dos stack size
-
- dos21funcs label byte
- db 0, 0, 0, 0, 0, 0, 0, 0 ;0-7
- db 0, 0, 0, 0, 0, 0ffh, 0ffh, 0ffh ;8-f
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;10-17
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;18-1f
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0, 0ffh ;20-27
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0 ;28-2f
- db 0ffh, 0, 0, 0, 0, 0, 0ffh, 0ffh ;30-37
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;38-3f
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;40-47
- db 0, 0, 0, 0, 0ffh, 0, 0ffh, 0ffh ;48-4f **
- db 0, 0, 0, 0, 0ffh, 0, 0ffh, 0ffh ;50-57
- db 0, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0, 0 ;58-5f
- db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh ;60-67
-
- ;
- ; ***************************************************************************
- ; These are the bios interrupt vectors
- ;
-
- BIOSI8 equ 08h ;Bios Timer interrupt
- BIOSI9 equ 09h ;BIOS Hardware Keyboard interrupt
- BIOSIB equ 0Bh
- BIOSIC equ 0Ch
- BIOSI10 equ 10h
- BIOSI13 equ 13h ;Bios Disk interrupt
- BIOSI14 equ 14h
- BIOSI15 equ 15h
- BIOSI16 equ 16h ;Bios Keyboard interrupt
- BIOSI17 equ 17h
- BIOSI1C equ 1Ch
- DOSI21 equ 21h ;DOS service router interrupt
- DOSI28 equ 28h ;DOS Idle interrupt
- DOSI33 equ 33h ;mouse interrupt
- BIOSI1B equ 1Bh
- DOSI23 equ 23h
- DOSI24 equ 24h
- ;
- oldint8 dd 0 ;BIOS Hardware Timer Interrupt
- db BIOSI8
- dw offset newint8 ;replacement vector
- oldint9 dd 0 ;BIOS Hardware Keyboard Interrupt
- db BIOSI9
- dw offset newint9 ;replacement vector
- oldint13 dd 0 ;BIOS Disk Interrupt
- db BIOSI13
- dw offset newint13 ;replacement vector
- oldint16 dd 0 ;BIOS Software Keyboard Interrupt
- db BIOSI16
- dw offset newint16 ;replacement vector
- oldint21 dd 0 ;DOS Services Interrupt
- db DOSI21
- dw offset newint21 ;replacement vector
- oldint28 dd 0 ;DOS Idle Interrupt
- db DOSI28
- dw offset newint28 ;replacement vector
- oldint1B dd 0 ;control-C vector
- db BIOSI1B
- dw offset intret ;replacement vector
- oldint23 dd 0 ;control-C vector
- db DOSI23
- dw offset intret ;replacement vector
- oldint24 dd 0 ;critical error vector
- db DOSI24
- dw offset newint24 ;replacement vector
-
- ;
- ; ***************************************************************************
- ; Function to restore interrupts.
- ;
- ; If AX=0, this function does popup-interrupts (1B, 23 and 24)
- ; If AX=anything else, this function does initial interrupts
- ;
- restore_int proc near
- push ds
- push ax
- push dx
- push si
- push di
- push cx
- mov di,7 ;7 bytes per structure
-
- or ax,ax
- jnz restore1
-
- mov cx,3 ;three times through
- mov si,offset oldint1B ;start at table beginning
- jmp restore_loop
- restore1:
- mov cx,06h ;six interrupts!
- mov si,offset oldint8 ;top of table
-
- restore_loop:
- push cx
- push di ;save structure size
- push ds
- mov al,4[si] ;interrupt number
- mov dx,[si] ;IP of interrupt vector
- mov ds,2[si] ;CS of interrupt vector
- mov ah,25h ;set interrupt vector function
- int 21h
- pop ds
- xor ax,ax ;clear this entry from table
- mov [si],ax ;so it shows not used
- mov 2[si],ax
- pop di ;get size back
- add si,di ;do it again
- pop cx
- loop restore_loop
-
- pop cx
- pop di
- pop si
- pop dx
- pop ax
- pop ds
- ret
- restore_int endp
- ;
- ; ***************************************************************************
- ; Function to set interrupts.
- ;
- ; If AX=0, this function does popup-interrupts (1B, 23 and 24)
- ; If AX=anything else, this function does initial interrupts
- ;
- setup_int proc near
- push es
- push ax
- push bx
- push dx
- push di
- push si
- push cx
- mov di,7 ;7 bytes per structure
-
- or ax,ax
- jnz setup1
-
- mov cx,3 ;three times through
- mov si,offset oldint1B ;start at table beginning
- jmp setup_loop
- setup1:
- mov cx,06h ;six interrupts!
- mov si,offset oldint8 ;top of table
-
- setup_loop:
- push cx
- push di ;save structure size
- ; push ds
-
- mov ax,[si] ;check for value
- or ax,ax ;if not zero, next one
- jnz setup2
- mov al,4[si] ;interrupt number
- mov ah,35h ;get interrupt vector function
-
- int 21h
- ; pop ds
- mov [si],bx ;save IP
- mov 2[si],es ;save CS
-
- mov al,4[si] ;interrupt number
- ;DS already contains correct segment
- mov dx,5[si] ;IP of interrupt vector
- mov ah,25h ;set interrupt vector function
- int 21h
-
- setup2:
- pop di ;get size back
- add si,di ;do it again
- pop cx
- loop setup_loop
- ex_set:
- pop cx
- pop si
- pop di
- pop dx
- pop bx
- pop ax
- pop es
- ret
- setup_int endp
-
- setdta macro ddptr
- push ds
- mov ax,1a00h ;Function used to get current DTA address }
- mov dx,word ptr ddptr ;Offset of DTA returned }
- mov ds,word ptr ddptr+2 ;Segment of DTA returned by DOS }
- int 21h ;Execute MSDos function request }
- pop ds
- endm
-
- getdta macro ddptr
- push es
- mov ax,2f00h ;Function used to get current DTA address }
- int 21h ;Execute MSDos function request }
- mov word ptr ddptr+2,es ;Segment of DTA returned by DOS }
- mov word ptr ddptr,bx ;Offset of DTA returned }
- pop es
- endm
-
- ;
- ; ***************************************************************************
- ; S E T P S P
- ;
- ; A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack }
- ; Then the PSP get/set functions are issued at the DOS prompt. The }
- ; following checks are made, forcing DOS to use the "critical" }
- ; stack when the TSR enters at the INDOS level. }
- ;
- ; BX contains the PSP segment to set
- ;
- setpsp proc near
- call checkindos ;If Version < 3, and INDOS,...
- jz do_set_psp
- mov byte ptr es:[di],0ffh ;set critical flag
-
-
- do_set_psp:
- mov ax,5000h ;set PSP* function
- ;BX already holds segment
- int 21h ;DOS function request
-
- call checkindos ;If Version < 3, and INDOS,...
- jz set_out
- mov byte ptr es:[di],0h ;set critical flag
-
- set_out:
- ret
-
- setpsp endp
-
- ;
- ; ***************************************************************************
- ; G E T P S P
- ;
- ; A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack }
- ; Then the PSP get/set functions are issued at the DOS prompt. The }
- ; following checks are made, forcing DOS to use the "critical" }
- ; stack when the TSR enters at the INDOS level. }
- ;
- ; The PSP Segment is returned in BX
- ;
- getpsp proc near
- call checkindos ;If Version < 3, and INDOS,...
- jz do_get_psp
- mov byte ptr es:[di],0ffh ;set critical flag
-
- do_get_psp:
- mov ax,5100h ;get PSP function
- int 21h ;DOS function request
- ;BX now holds PSP segment
- call checkindos ;If Version < 3, and INDOS,...
- jz get_out
- mov byte ptr es:[di],0h ;clear critical flag
-
- get_out:
- ret
-
- getpsp endp
-
- ;
- ; ***************************************************************************
- ; This routine checks the version of DOS in use, and returns the
- ; ZF=0 (Not zero) if version < 3.0, and INDOS is set. Internal function
- ; is called ONLY by getpsp and setpsp.
- ;
- checkindos proc near
- mov al,dosversion
- cmp al,3 ;If Version is less than 3.0 ...
- jge ok_ret
- mov es,word ptr indosflag+2 ; ... and ...
- mov di,word ptr indosflag
- mov al,byte ptr es:[di]
- or al,al ;INDOS is set ...
- jz ok_ret
- mov es,word ptr doscriterr+2; ... then ...
- mov di,word ptr doscriterr
- jmp fixit ;exit with not zero
- ok_ret:
- xor al,al ;set zero flag
- fixit:
- ret
-
- checkindos endp
-
- ;
- ; ***************************************************************************
- ; Internal routine to establish addressibility of the data segment
- ;
- dds proc near
- push cs
- pop ds
- ret
- dds endp
-
- newint9 proc far
-
- ;NOTE: None of this pusing/popping is really necessary for THIS particular
- ;TSR, so I;ve commented it out.
- ;
- ; pushf
- ; cli
- ; push ds
- ; push es
- ; push ax
- ; push bx
- ; push cx
- ; push dx
- ; push di
- ; push si
-
- ; push cs
- ; pop ds
-
- ;internal INT9 processing here
-
- ;getkey_out:
- ; pop si
- ; pop di
- ; pop dx
- ; pop cx
- ; pop bx
- ; pop ax
- ; pop es
- ; pop ds
- ;out_9:
- ; popf
-
- ; cli
- jmp dword ptr cs:oldint9
-
-
- ;
- ; ***************************************************************************
- ; This is the int return (IRET) dummy pointer
- ;
- intret:
- iret
-
- ;This routine will be used at some future point ... commented out for now.
- ;
- ;ignore_9:
- ; in al,60h
- ; push ax
- ; pop ax
- ; in al,61h
- ; mov ah,al
- ; or al,80h
- ; out 61h,al
- ; xchg ah,al
- ; out 61h,al
- ; mov al,20h
- ; out 20h,al
- ; pop ax
- ; jmp short out_9
-
- newint9 endp
-
- new_bios_flag db 0
-
- newint16 proc far
- start_again:
- cmp ah,00 ;if char request,
- je func00 ;loop for character
- cmp ah,01 ;if character availability test
- je func01 ;go check for char
- push ax
- and ah,10h ;test for extra bios
- pop ax
- jz gobios16
- mov cs:new_bios_flag,10h
- and ah,3
- jmp short start_again
- gobios16:
- or ah,cs:new_bios_flag
- jmp dword ptr cs:[oldint16]
- ;go to bios interrupt 16
- func01:
- push ds
- push si
- push di
- call dds
- func01A:
- call keystat ;look at key buffer
- pushf ;save return flags
- jz fret01 ;return if no key
- call hotcheck ;test for hot key
- jnz fret01 ;not there
- popf ;flags back for loop
- jmp func01A ;it was a hot key -- skip it!
- fret01:
- mov new_bios_flag,0
- popf ;flags back
- pop di
- pop si
- pop ds
- ret 2 ;return to user
- func00:
- push ds
- push si
- push di
- call dds
- func00A:
- call keystat ;wait until character available
- jz func00A ;loop it
- call hotcheck ;test for a hot key
- jz func00A ;it sure was -- start loop again
-
- b_func0:
- mov ah,0 ;get the next user key
- or ah,new_bios_flag
- pushf ;simulate it ...
- call dword ptr cs:oldint16 ;do bios
-
- func_out:
- pushf ;save return flags
- jmp fret01 ;restore and return
-
- newint16 endp
-
- ;
- ; call the background task if no key is available
- ;
- keystat proc near
- ;preserves all registers except
- ;AX,SI,DI,DS & flags
- push cx
- push bx
-
- push ax
- push ds
- cld
-
- mov ax,BIOS_DATA
- mov ds,ax ;set data segment
- mov si,KB_FLG1 ;set source
-
- lodsw ;get the high and low bytes
- pop ds
- mov bx,ax ;save it away
- pop ax
-
-
- mov al,SHIFTST ;shift state check
-
- or al,al ;is it zero?
- jz sim_16 ;yes -- set flag for simulation
-
- push bx ;save current flags
- and bl,al ;mask bits
- cmp al,bl ;are we the same?
- pop bx
- jne skip_16A ;nope -- next one
- sim_16:
- or word ptr STATUS,SHIFTSON ;set the flag
-
- mov al,HOTKEY ;is this control block the last one?
-
- or al,al
- jnz short skip_16
- or word ptr STATUS,HOTKEYON ;turn on hotkey flag
- jmp short skip_16
- skip_16A:
- and word ptr STATUS,NOT SHIFTSON ;set the flag
-
- skip_16:
- mov ah,1 ;see if character is available
- or ah,new_bios_flag
- pushf ;simulate interrupt
- call dword ptr cs:oldint16
-
- jnz exit_161 ;got one
-
- chkdos_161:
- cld
- push ds ;check if dos critical error in effect
- push si
- lds si,cs:doscriterr ;zero says dos is interruptable
- lodsb ;$ff says dos is in a critical state
- lds si,cs:indosflag ;if indos then int $28 issued by dos
- or al,[si] ;so we dont have to.
- ;account for active interrupts
- or al,cs:intflags ;any flags says we dont issue call
- pop si ;to the background.
- pop ds
- cmp al,01 ;must be indos flag only
- jg skip28 ;dos cannot take an interrupt yet
- int 28h ;call dos idle function (background dispatch).
- skip28:
- xor ax,ax ;show no keycode available
- exit_161:
- pop bx
- pop cx
- ret
-
- keystat endp
-
- hotcheck proc near ;DI points to TSR Block character is from
- ;SI holds ID of TSR routine
-
- push cx
- push ax ;save character
- push di
-
- mov al,HOTKEY
-
- or al,al
- jz only_shift ;special case for shift-state only
-
- cmp al,ah ;do scan codes match?
- jne again_16H
-
- only_shift:
- push ax
- mov ax,STATUS ;get TSR status flags
- test ax,SHIFTSON ;are shift codes set?
- jz again_16H1 ;nope -- try next one
-
- or ax,HOTKEYON ;turn on hotkey flag
- mov STATUS,ax ;put new flags back
-
- pop ax
- or al,al ;test for special case
- pop di ;get back Block pointer
- pop ax ;throwing this away
-
- jz hot_out ;special case for flags only
-
- b_func1:
- mov ah,0 ;get rid of the character
- or ah,new_bios_flag
- pushf ;simulate interrupt
- call dword ptr cs:oldint16 ;do bios
-
- hot_out:
- xor ax,ax ;set zero flag
- jmp got_hot
-
- again_16H1:
- pop ax ;get rid of flag
- again_16H:
- mov al,0ffh
- or al,al ;clear zero flag if set
- pop di
- pop ax ;get back keystroke
- got_hot:
- pop cx ;get back cx register
- ret
-
- hotcheck endp
-
- newint8 proc far
-
- pushf
- push ds
- push di
- push cx ;save regs
- push ax
- call dds
-
- mov ax,STATUS
- test ax,HOTKEYON ;is hot-key flag set?
- jz again_8_2
- test ax,TSRACTIVE ;are we already running?
- jnz again_8_2 ;yep -- next one
-
- wait_8:
- cmp waitcount,0 ;if waiting, check time
- jnz wait2_8 ;decrement count
-
- chkdos_8:
- cld
- push ds ;check if dos critical error in effect
- push si
- lds si,cs:doscriterr ;zero says dos is interruptable
- lodsb ;$ff says dos is in a critical state
- lds si,cs:indosflag ;add in second status byte
- or al,[si]
- or al,cs:intflags ;any flags says we're busy
- pop si
- pop ds
- jnz wait1_8 ;we're busy -- set count & EXIT
- call dopopup ;call the popup stuff
- jmp end_8 ;and out we go
- wait1_8:
- mov waitcount,10h ;set the count
- wait2_8:
- dec waitcount
- jz chkdos_8
-
- again_8_2:
- end_8:
- pushf ;simulate interrupt ...
- call dword ptr oldint8 ;do the original
-
- pop ax ;get back registers
- pop cx
- pop di
- pop ds
- popf
-
- exit_8:
- iret
-
- newint8 endp
-
- newint28 proc far
-
- pushf ;simulate interrupt ...
- call dword ptr cs:oldint28 ;do the original
-
- push ds
- push di
- push cx ;save regs
- push ax
-
- push cs
- pop ds
-
- again_28:
- mov ax,STATUS ;get TSR status flags
- test ax,HOTKEYON ;is hot-key flag set?
- jz again_28_2
- test ax,TSRACTIVE ;are we already running?
- jnz again_28_2 ;yep -- next one
-
- chkdos_28:
- cld
- push ds ;check if dos critical error in effect
- push si
- lds si,cs:doscriterr ;zero says dos is interruptable
- lodsb ;$ff says dos is in a critical state
- or al,cs:intflags ;any flags says we're busy
- ;NOTE: Indos flag is always set here!
- ; ... so don't have to check it!
- pop si
- pop ds
- jnz end_28 ;we're busy-EXIT & try again next time
-
- mov waitcount,0 ;clear waiting count and ...
- call dopopup ;... call the popup stuff
- jmp end_28 ;and out we go
-
- again_28_2:
- end_28:
- pop ax ;get back registers
- pop cx
- pop di
- pop ds
-
- exit_28:
- iret
-
- newint28 endp
-
- di28 dw 0
- ds28 dw 0
-
- dopopup proc near
-
- cli ;no interrupts now!!!
-
- or word ptr STATUS,TSRACTIVE ;set active bit
-
- mov ISSEG,ss ;save user's stack segment
- mov ISPTR,sp ;... and stack pointer
- ;DS was saved in INT8 or INT28
-
- mov ss,SSEG ;load TSR stack segment ...
- mov sp,SPTR ;... and stack pointer
-
- push bp ;save all registers except for
- push bx ;...those saved by calling routine,
- push dx ;...DS,DI,CX,AX are restored on exit
- push si ;...from this procedure
- push es
-
- mov [di28],di ;save TSR pointer until after saving
- mov [ds28],ds ;...Indos stack
-
- mov di,sp ;destination is our stack
- dec di
- dec di ;back off current word
- mov ax,ss ;move stack segment
- mov es,ax ;...to extra segment
-
- mov si,cs:dossptr ;source is DOS Indos Primary Stack
- mov ds,cs:dossseg ;...and segment, of course
- dec si ;point to last word on DOS stack
- dec si
-
- mov cx,40h ;stack size to save
-
- mov ax,sp ;save current stack pointer ...
- sub ax,cx ;...and make room to avoid
- sub ax,cx ;...overwriting during REP
- mov sp,ax ;...and put it back
-
- std ;set the direction flag ...
- rep movsw ;and move the 40 word stack
- mov sp,di
- cld ;direction flag back
-
- mov di,cs:di28 ;get TSR pointer back
- mov ds,cs:ds28
-
- sti ;interrupts back on!!
-
- getdta OLDDTA ;save current DTA
-
- call getpsp ;get current PSP segment
- mov OLDPSP,bx ;...returned in BX
-
- mov bx,PSP ;set TSR's PSP in BX
- call setpsp ;...and do it
-
- setdta DTA
-
- xor ax,ax
- call setup_int ;replace with our handler
-
- push ds ;save DS:DI for after popup
- push di
- mov ds,DSEG ;load user's data segment
-
-
- call dword ptr cs:POPUP ;call the user routine!!
-
-
- pop di ;restore ds:di for addressibility
- pop ds
-
- mov bx,OLDPSP ;replace interrupted PSP
- call setpsp
-
- setdta OLDDTA ;restore interrupted DTA
-
- xor ax,ax
- call restore_int ;restore interrupts as well
-
- ;terminate checking here!
-
- cli ;no interrupts while manipulating stack
-
- mov cx,40h ;stack size here
-
- mov di28,di ;save TSR pointer until after saving
- mov ds28,ds ;...Indos stack
-
- mov ax,ss ;source is current stack
- mov ds,ax
- mov si,sp ;point to last word on our stack
- inc si
- inc si
-
- mov es,cs:dossseg ;destination is dos stack
- mov di,cs:dossptr
- sub di,cx ;point backward to last used word
- sub di,cx ;...on dos stack
- cld
- rep movsw
-
- mov sp,si ;skip over moved words
-
- mov di,cs:[di28] ;get TSR pointer back
- mov ds,cs:[ds28]
-
- pop es
- pop si
- pop dx
- pop bx
- pop bp
-
- ;
- ; ***************************************************************************
- ;
- ; CAUTION!!!! THE USER ROUTINE MUST RETURN SS:SP WITH THE SAME
- ; VALUES AS ON ENTRY. OTHER REGISTERS WILL BE RESTORED
- ; BY THIS ROUTINE.
- ;
- ; ***************************************************************************
- ;
- back_popup:
-
- mov ss,ISSEG ;restore user's stack segment
- mov sp,ISPTR ;... and stack pointer
- ;DS will be restored in INT8 or INT28
- and STATUS,NOT HOTKEYON+TSRACTIVE
- ;clear hot-key and inuse bits
-
- sti ;enable interrupts again
- dopopup_end:
- ret
-
- dopopup endp
-
- newint13 proc far
-
- pushf
- or cs:intflags,ININT13
- popf ;get back original flags
-
- pushf ;simulate interrupt ...
- call dword ptr cs:oldint13 ;do the original
-
- pushf
- and cs:intflags,NOT ININT13
- popf
-
- ret 2
-
- newint13 endp
-
- newint21 proc far
- pushf
- sti ;allow interrupts
-
- cmp ah,63h ;max number
- jg notme
- push ax ;some INT21 functions must be left
- push bx ;...alone. They either never return,
- mov bx,offset dos21funcs ;...grab parameters from the stack
- mov al,ah ;...or can be interrupted.
- xlat cs:dos21funcs ;test function against table
- or al,al ;wait for functions marked zero
- pop bx ;these ,ust be left alone
- pop ax
- jz notme ;jump to original INT21
- jmp short set_21
- notme:
- popf
- jmp dword ptr cs:oldint21
-
- set_21:
- or cs:intflags,ININT21 ;say int21 is active
- popf ;get original flags back
- pushf ;save flags and all regs that
-
- call dword ptr cs:oldint21 ;do the interrupt
- sti
-
- pushf ;save return registers
- and cs:intflags,NOT ININT21 ;not active any more
- popf ;they're back
-
- ret 2 ;return with flags set
-
- newint21 endp
-
- error24 dw 0
-
- newint24 proc far
- pushf
- mov cs:error24,di ;save the error
- mov al,0
- iret
- newint24 endp
-
- pop_routine proc far ;just prints a msg and gets out
- lea dx,hello
- mov ah,9
- int 21h ; display copyright notice
- ret
- pop_routine endp
-
-
- TSR_stack db 512 dup(0)
- TSR_stack_end:
-
- res_part: ; this is the end of my resident code so I can tell DOS
- ; how big I am
- ;
- ;
- ; ***************************************************************************
- ;
- ; Initialization Code -- load routine and point new int14
- ;
- second_time db 0 ;flag used to test for odd-byte check
-
- begin:
- lea dx,cpyrt
- mov ah,9
- int 21h ; display copyright notice
-
- call getpsp ;Current PSP returned in BX
- mov PSP,bx
- getdta DTA ;macro to get Disk Transfer Address
-
- mov ax,ds
- mov DSEG,ax ;save it
- mov SSEG,ax ;save it here too!
- mov word ptr POPUP+2,ax ;save it here three!
- mov ax,offset TSR_stack_end
- sub ax,2
- mov SPTR,ax
-
- mov SHIFTST,08h ;ALT-Key + ...
- mov HOTKEY,2dh ;...'X' to pop up
-
- mov word ptr POPUP,offset pop_routine
-
- mov ax,3400h ;find the DOS Indos Byte
- int 21h
- ;IF SOMEBODY HAS AT&T DOS 2.11, would you please try something?
- ;
- ;Move the label 'crit2' to the second location (where it is currently)
- ;commented out, and see how it works. There is still some dissension
- ;about where the critical byte actually is in that version of DOS!
-
- crit2:
- mov word ptr indosflag,bx ;offset returned in BX
- mov word ptr indosflag+2,es ;segment returned in ES
- mov dossseg,es ;this is also the DOS stack segment
- mov word ptr doscriterr+2,es;as well as the segment for the critical flag
- ;crit2:
- mov cx,2000h ;search for instruction: CMP [crit flag],00
- mov ax,3e80h ;3e80 is opcode to search for
- mov di,bx ;start search here
- crit3:
- repnz scasw ;search until a match is found
- jnz no_crit_found ;couldn't find critical byte
- ;we're going to search for the
- ;address of the critical flag
- ;es:[di-2] now points to:
- ; CMP [crit flag],00
- ; JNZ ...
- ; MOV SP,indos stack address
- mov al,byte ptr es:[di+5] ;MOV SP,xxxx
- cmp al,0bch ;opcode for MOV SP
- jne crit3 ;bad place. Sorry
- mov ax,word ptr es:[di] ;here's the critical byte address
- mov word ptr doscriterr,ax
- mov ax,word ptr es:[di+6] ;...and here's the stack pointer
- mov word ptr dossptr,ax
- mov ax,1
- jmp no_error
- no_crit_found:
- mov al,second_time ;did we already try odd bytes?
- or al,al
- jz crit_again ;nope ...
- jmp bad_dos_version ;we tried!
- crit_again:
- mov second_time,1 ;set the flag
- inc bx ;increment pointer (needed for AT&T
- jmp crit2 ;DOS 2.11 and some others
-
- no_error:
- mov ax,1
- call setup_int
-
- lea dx,res_part ; load offset of top of code
- add dx,15 ;round up
- shr dx,1
- shr dx,1
- shr dx,1
- shr dx,1 ; divide by 16 to get paragraphs
- xor al,al ; no errors
- mov ah,31h ; load terminate & stay resident code
- int 21h
-
- bad_dos_version:
- lea dx,bad_dos_msg
- mov ah,9
- int 21h ; display error message
- mov ax,4cffh ;exit with -1 return code
- int 21h
-
- code ends
-
- end start