; SCCSID = @(#)ctrlc.asm 1.4 85/08/16 ; Low level routines for detecting special characters on CON input, ; the ^C exit/int code, the Hard error INT 24 code, the ; process termination code, and the INT 0 divide overflow handler. ; ; FATAL ; FATAL1 ; reset_environment ; DSKSTATCHK ; SPOOLINT ; STATCHK ; CNTCHAND ; DIVOV ; CHARHARD ; HardErr ; ; Revision history: ; ; AN000 version 4.0 Jan 1988 ; A002 PTM -- dir >lpt3 hangs ; A003 PTM 3957- fake version for IBMCAHE.COM ; ; get the appropriate segment definitions ; .xlist include dosseg.asm CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME SS:DOSGROUP,CS:DOSGROUP .xcref INCLUDE DOSSYM.INC INCLUDE DEVSYM.INC include version.inc .cref .list I_need SFN,WORD I_NEED pJFN,DWORD i_need DevIOBuf,BYTE i_need DidCTRLC,BYTE i_need INDOS,BYTE i_need DSKSTCOM,BYTE i_need DSKSTCALL,BYTE i_need DSKSTST,WORD i_need BCON,DWORD i_need DSKCHRET,BYTE i_need DSKSTCNT,WORD i_need IDLEINT,BYTE i_need CONSWAP,BYTE i_need user_SS,WORD i_need user_SP,WORD i_need User_In_AX,WORD i_need ERRORMODE,BYTE i_need ConC_spsave,WORD i_need Exit_type,BYTE i_need PFLAG,BYTE i_need ExitHold,DWORD i_need WPErr,BYTE i_need ReadOp,BYTE i_need CONTSTK,WORD i_need Exit_Code,WORD i_need CurrentPDB,WORD i_need DIVMES,BYTE i_need DivMesLen,WORD i_need ALLOWED,BYTE i_need FAILERR,BYTE i_need EXTERR,WORD i_need ERR_TABLE_24,BYTE I_need ErrMap24,BYTE I_need ErrMap24End,BYTE I_need fAborting,BYTE I_need AUXStack,BYTE I_need SCAN_FLAG,BYTE I_need EXTOPEN_ON,BYTE ;AN000; DOS 4.0 I_need InterCon,BYTE ;AN000; DOS 4.0 I_need DOS34_FLAG,WORD ;AN000; DOS 4.0 I_need ACT_PAGE,WORD ;AN000; DOS 4.0 I_need Special_Version,WORD ;AN007; DOS 4.0 if debug I_need BugLev,WORD I_need BugTyp,WORD include bugtyp.asm endif IF BUFFERFLAG extrn restore_user_map:near ENDIF Break ASSUME DS:NOTHING,ES:NOTHING procedure DSKSTATCHK,NEAR ; Check for ^C if only one level in CMP BYTE PTR [INDOS],1 retnz ; Do NOTHING PUSH CX PUSH ES PUSH BX PUSH DS PUSH SI PUSH CS POP ES Context DS DOSAssume CS,,"DskStatChk" MOV BYTE PTR [DSKSTCOM],DEVRDND MOV BYTE PTR [DSKSTCALL],DRDNDHL MOV [DSKSTST],0 IF DBCS ;AN000; MOV AL, [InterCon] ;AN000;get type of status read 2/13/KK MOV BYTE PTR [DSKCHRET],AL ;AN000; load interim flag into packet ENDIF ;AN000; MOV BX,OFFSET DOSGROUP:DSKSTCALL LDS SI,[BCON] ASSUME DS:NOTHING invoke DEVIOCALL2 TEST [DSKSTST],STBUI JZ GotCh ; No characters available XOR AL,AL ; Set zero RET36: POP SI POP DS POP BX POP ES POP CX return GotCh: MOV AL,BYTE PTR [DSKCHRET] DSK1: CMP AL,"C"-"@" JNZ RET36 MOV BYTE PTR [DSKSTCOM],DEVRD MOV BYTE PTR [DSKSTCALL],DRDWRHL MOV BYTE PTR [DSKCHRET],CL MOV [DSKSTST],0 MOV [DSKSTCNT],1 invoke DEVIOCALL2 ; Eat the ^C POP SI POP DS POP BX ; Clean stack POP ES POP CX JMP CNTCHAND NOSTOP: CMP AL,"P"-"@" JNZ check_next CMP BYTE PTR [SCAN_FLAG],0 ; ALT_Q ? JZ INCHKJ ; no return check_next: IF NOT TOGLPRN CMP AL,"N"-"@" JZ INCHKJ ENDIF CMP AL,"C"-"@" JZ INCHKJ check_end: return INCHKJ: JMP INCHK EndProc DSKSTATCHK ; ; SpoolInt - signal processes that the DOS is truly idle. We are allowed to ; do this ONLY if we are working on a 1-12 system call AND if we are not in ; the middle of an INT 24. ; procedure SPOOLINT,NEAR PUSHF test IdleInt,-1 jz POPFRet test ErrorMode,-1 jnz POPFRet ; ; Note that we are going to allow an external program to issue system calls ; at this time. We MUST preserve IdleInt across this. ; PUSH WORD PTR IdleInt INT int_spooler POP WORD PTR IdleInt POPFRET: POPF return EndProc SPOOLINT procedure STATCHK,NEAR invoke DSKSTATCHK ; Allows ^C to be detected under ; input redirection PUSH BX XOR BX,BX invoke GET_IO_SFT POP BX retc MOV AH,1 invoke IOFUNC JZ SPOOLINT CMP AL,"S"-"@" JNZ NOSTOP CMP BYTE PTR [SCAN_FLAG],0 ;AN000; ALT_R ? JNZ check_end ;AN000; yes XOR AH,AH invoke IOFUNC ; Eat Cntrl-S JMP SHORT PAUSOSTRT PRINTOFF: PRINTON: NOT BYTE PTR [PFLAG] PUSH BX MOV BX,4 invoke GET_IO_SFT POP BX retc PUSH ES PUSH DI PUSH DS POP ES MOV DI,SI ; ES:DI -> SFT TEST ES:[DI.sf_flags],sf_net_spool JZ NORM_PR ; Not redirected, echo is OK Callinstall NetSpoolEchoCheck,MultNet,38,, ; See if allowed JNC NORM_PR ; Echo is OK MOV BYTE PTR [PFLAG],0 ; If not allowed, disable echo Callinstall NetSpoolClose,MultNet,36,, ; and close JMP SHORT RETP6 NORM_PR: CMP BYTE PTR [PFLAG],0 JNZ PRNOPN invoke DEV_CLOSE_SFT JMP SHORT RETP6 PRNOPN: invoke DEV_OPEN_SFT RETP6: POP DI POP ES return PAUSOLP: CALL SPOOLINT PAUSOSTRT: MOV AH,1 invoke IOFUNC JZ PAUSOLP INCHK: PUSH BX XOR BX,BX invoke GET_IO_SFT POP BX retc XOR AH,AH invoke IOFUNC CMP AL,"P"-"@" ;;;;; 7/14/86 ALT_Q key fix JZ PRINTON ; no! must be CTRL_P NOPRINT: ;;;;; 7/14/86 ALT_Q key fix IF NOT TOGLPRN CMP AL,"N"-"@" JZ PRINTOFF ENDIF CMP AL,"C"-"@" retnz EndProc STATCHK procedure CNTCHAND,NEAR ; Ctrl-C handler. ; "^C" and CR/LF is printed. Then the user registers are restored and the ; user CTRL-C handler is executed. At this point the top of the stack has 1) ; the interrupt return address should the user CTRL-C handler wish to allow ; processing to continue; 2) the original interrupt return address to the code ; that performed the function call in the first place. If the user CTRL-C ; handler wishes to continue, it must leave all registers unchanged and RET ; (not IRET) with carry CLEAR. If carry is SET then an terminate system call ; is simulated. TEST [DOS34_FLAG],CTRL_BREAK_FLAG ;AN002; from RAWOUT JNZ around_deadlock ;AN002; MOV AL,3 ; Display "^C" invoke BUFOUT invoke CRLF around_deadlock: ;AN002; Context DS CMP BYTE PTR [CONSWAP],0 JZ NOSWAP invoke SWAPBACK NOSWAP: CLI ; Prepare to play with stack MOV SS,[user_SS] ; User stack now restored ASSUME SS:NOTHING MOV SP,[user_SP] invoke restore_world ; User registers now restored ASSUME DS:NOTHING MOV BYTE PTR [INDOS],0 ; Go to known state MOV BYTE PTR [ERRORMODE],0 MOV [ConC_spsave],SP ; save his SP CLC INT int_ctrl_c ; Execute user Ctrl-C handler ; ; The user has returned to us. The circumstances we allow are: ; ; IRET We retry the operation by redispatching the system call ; CLC/RETF POP the stack and retry ; ... Exit the current process with ^C exit ; ; User's may RETURN to us and leave interrupts on. Turn 'em off just to be ; sure ; CLI MOV [user_IN_AX],ax ; save the AX PUSHF ; and the flags (maybe new call) POP AX ; ; See if the input stack is identical to the output stack ; CMP SP,[ConC_spsave] JNZ ctrlc_try_new ; current SP not the same as saved SP ; ; Repeat the operation by redispatching the system call. ; ctrlc_repeat: MOV AX,User_In_AX transfer COMMAND ; ; The current SP is NOT the same as the input SP. Presume that he RETF'd ; leaving some flags on the stack and examine the input ; ctrlc_try_new: ADD SP,2 ; pop those flags TEST AX,f_carry ; did he return with carry? JZ Ctrlc_Repeat ; no carry set, just retry ; ; Well... time to abort the user. Signal a ^C exit and use the EXIT system ; call.. ; ctrlc_abort: MOV AX,(EXIT SHL 8) + 0 MOV DidCTRLC,-1 transfer COMMAND ; give up by faking $EXIT EndProc CNTCHAND Break ; Default handler for division overflow trap procedure DIVOV,NEAR ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING MOV SI,OFFSET DOSGROUP:DIVMES MOV BX,DivMesLen MOV AX,CS MOV SS,AX MOV SP,OFFSET DOSGROUP:AUXSTACK ; Enough stack for interrupts CALL OutMes JMP ctrlc_abort ; Use Ctrl-C abort on divide overflow EndProc DIVOV ; ; OutMes: perform message output ; Inputs: SS:SI points to message ; BX has message length ; Outputs: message to BCON ; procedure OutMes,NEAR Context ES ; get ES addressability Context DS ; get DS addressability MOV BYTE PTR [DskStCom],DevWrt MOV BYTE PTR [DskStCall],DRdWrHL MOV [DskSTST],0 MOV [DskStCnt],BX MOV BX,OFFSET DOSGROUP:DskStCall MOV WORD PTR [DskChRet+1],SI ; transfer address (need an EQU) LDS SI,[BCON] ASSUME DS:NOTHING invoke DEVIOCALL2 MOV WORD PTR [DskChRet+1],OFFSET DOSGROUP:DevIOBuf MOV [DskStCnt],1 return EndProc OutMes Break procedure CHARHARD,NEAR ASSUME DS:NOTHING,ES:NOTHING,SS:DOSGROUP ; Character device error handler ; Same function as HARDERR OR AH,allowed_FAIL + allowed_IGNORE + allowed_RETRY MOV Allowed,AH MOV WORD PTR [EXITHOLD+2],ES MOV WORD PTR [EXITHOLD],BP PUSH SI AND DI,STECODE MOV BP,DS ; Device pointer is BP:SI CALL FATALC POP SI return EndProc CHARHARD ; Hard disk error handler. Entry conditions: ; DS:BX = Original disk transfer address ; DX = Original logical sector number ; CX = Number of sectors to go (first one gave the error) ; AX = Hardware error code ; DI = Original sector transfer count ; ES:BP = Base of drive parameters ; [READOP] = 0 for read, 1 for write ; [ALLOWED] Set with allowed responses to this error (other bits MUST BE 0) ; Output: ; [FAILERR] will be set if user responded FAIL procedure HardErr,NEAR ASSUME DS:NOTHING,ES:NOTHING XCHG AX,DI ; Error code in DI, count in AX AND DI,STECODE ; And off status bits CMP DI,error_I24_write_protect ; Write Protect Error? JNZ NOSETWRPERR PUSH AX MOV AL,ES:[BP.dpb_drive] MOV BYTE PTR [WPERR],AL ; Flag drive with WP error POP AX NOSETWRPERR: SUB AX,CX ; Number of sectors successfully transferred ADD DX,AX ; First sector number to retry PUSH DX MUL ES:[BP.dpb_sector_size] ; Number of bytes transferred POP DX ADD BX,AX ; First address for retry XOR AH,AH ; Flag disk section in error CMP DX,ES:[BP.dpb_first_FAT] ; In reserved area? JB ERRINT INC AH ; Flag for FAT CMP DX,ES:[BP.dpb_dir_sector] ; In FAT? JAE TESTDIR ; No MOV ES:[BP.dpb_free_cnt],-1 ; Err in FAT must force recomp of freespace JMP SHORT ERRINT TESTDIR: INC AH CMP DX,ES:[BP.dpb_first_sector] ; In directory? JB ERRINT INC AH ; Must be in data area ERRINT: SHL AH,1 ; Make room for read/write bit OR AH,BYTE PTR [READOP] ; Set bit 0 ; If we have a write protect error when writing on a critical area on disk, ; do not allow a retry as this may write out garbage on any subsequent disk. ;test ah,1 ;jz Not_Crit ;cmp ah,5 ;ja Not_Crit ;and [ALLOWED],NOT Allowed_RETRY Not_Crit: OR AH,[ALLOWED] ; Set the allowed_ bits entry FATAL MOV AL,ES:[BP.dpb_drive] ; Get drive number entry FATAL1 MOV WORD PTR [EXITHOLD+2],ES MOV WORD PTR [EXITHOLD],BP ; The only things we preserve LES SI,ES:[BP.dpb_driver_addr] MOV BP,ES ; BP:SI points to the device involved ; ; DI has the INT-24-style extended error. We now map the error code for this ; into the normalized get extended error set by using the ErrMap24 table as an ; translate table. Note that we translate ONLY the device returned codes and ; leave all others beyond the look up table alone. ; FATALC: call SET_I24_EXTENDED_ERROR CMP DI,error_I24_gen_failure JBE GOT_RIGHT_CODE ; Error codes above gen_failure get MOV DI,error_I24_gen_failure ; mapped to gen_failure. Real codes ; Only come via GetExtendedError entry NET_I24_ENTRY ; Entry point used by REDIRector on Network I 24 errors. ; ; ASSUME DS:NOTHING,ES:NOTHING,SS:DOSGROUP ; ; ALL I 24 regs set up. ALL Extended error info SET. ALLOWED Set. ; EXITHOLD set for restore of ES:BP. GOT_RIGHT_CODE: CMP BYTE PTR [ERRORMODE],0 ; No INT 24s if already INT 24 JZ NoSetFail MOV AL,3 JMP FailRet NoSetFail: IF BUFFERFLAG invoke RESTORE_USER_MAP ;AN000;LB. restore user's EMS map ENDIF MOV [CONTSTK],SP Context ES fmt TypINT24,LevLog,<"INT 24: AX = $x DI = $x\n">, ; ; Wango!!! We may need to free some user state info... In particular, we ; may have locked down a JFN for a user and he may NEVER return to us. Thus, ; we need to free it here and then reallocate it when we come back. ; CMP SFN,-1 JZ NoFree SaveReg LDS SI,pJFN MOV BYTE PTR [SI],0FFH RestoreReg NoFree: CLI ; Prepare to play with stack INC BYTE PTR [ERRORMODE] ; Flag INT 24 in progress DEC BYTE PTR [INDOS] ; INT 24 handler might not return ;; Extneded Open hooks TEST [DOS34_FLAG],Force_I24_Fail ;AN000;IFS. form IFS Call Back ;AN000; JNZ faili24 ;AN000;IFS. ;AN000; TEST [EXTOPEN_ON],EXT_OPEN_I24_OFF ;AN000;IFS.I24 error disabled ;AN000; JZ i24yes ;AN000;IFS.no ;AN000; faili24: ;AN000; MOV AL,3 ;AN000;IFS.fake fail ;AN000; JMP passi24 ;AN000;IFS.exit ;AN000; i24yes: ;AN000; ;; Extended Open hooks MOV SS,[user_SS] ASSUME SS:NOTHING MOV SP,ES:[user_SP] ; User stack pointer restored INT int_fatal_abort ; Fatal error interrupt vector, must preserve ES MOV ES:[user_SP],SP ; restore our stack MOV ES:[user_SS],SS MOV BP,ES MOV SS,BP ASSUME SS:DOSGROUP passi24: ;AN000; MOV SP,[CONTSTK] INC BYTE PTR [INDOS] ; Back in the DOS MOV BYTE PTR [ERRORMODE],0 ; Back from INT 24 STI ;; MOV [ACT_PAGE],-1 ;LB. invalidate DOS active page ;AN000; ;; invoke SAVE_MAP ;LB. save user's EMS map ;AN000; fmt TypINT24,LevLog,<"INT 24: User reply = $x\n">, FAILRET: LES BP,[EXITHOLD] ASSUME ES:NOTHING ; ; Triage the user's reply. ; CMP AL,1 JB CheckIgnore ; 0 => ignore JZ CheckRetry ; 1 => retry CMP AL,3 ; 3 => fail JNZ DoAbort ; 2, invalid => abort ; ; The reply was fail. See if we are allowed to fail. ; TEST [ALLOWED],allowed_FAIL ; Can we? JZ DoAbort ; No, do abort DoFail: MOV AL,3 ; just in case... TEST [EXTOPEN_ON],EXT_OPEN_I24_OFF ;AN000;EO. I24 error disabled JNZ cleanup ;AN000;EO. no INC [FAILERR] ; Tell everybody CleanUp: MOV WpErr,-1 CMP SFN,-1 retz SaveReg MOV AX,SFN LDS SI,pJFN MOV [SI],AL RestoreReg return ; ; The reply was IGNORE. See if we are allowed to ignore. ; CheckIgnore: TEST [ALLOWED],allowed_IGNORE ; Can we? JZ DoFail ; No, do fail JMP CleanUp ; ; The reply was RETRY. See if we are allowed to retry. ; CheckRetry: TEST [ALLOWED],allowed_RETRY ; Can we? JZ DoFail ; No, do fail JMP CleanUp ; ; The reply was ABORT. ; DoAbort: Context DS CMP BYTE PTR [CONSWAP],0 JZ NOSWAP2 invoke SWAPBACK NOSWAP2: ; ; See if we are to truly abort. If we are in the process of aborting, turn ; this abort into a fail. ; TEST fAborting,-1 JNZ DoFail ; ; Set return code ; MOV BYTE PTR [exit_Type],Exit_hard_error XOR AL,AL ; ; we are truly aborting the process. Go restore information from the PDB as ; necessary. ; Transfer exit_inner ; ; reset_environment checks the DS value against the CurrentPDB. If they are ; different, then an old-style return is performed. If they are the same, ; then we release jfns and restore to parent. We still use the PDB at DS:0 as ; the source of the terminate addresses. ; ; Some subtlety: We are about to issue a bunch of calls that *may* generate ; INT 24s. We *cannot* allow the user to restart the abort process; we may ; end up aborting the wrong process or turn a terminate/stay/resident into a ; normal abort and leave interrupt handlers around. What we do is to set a ; flag that will indicate that if any abort code is seen, we just continue the ; operation. In essence, we dis-allow the abort response. ; ; output: none. ; entry reset_environment ASSUME DS:NOTHING,ES:NOTHING invoke Reset_Version ;AN007;MS. reset version number PUSH DS ; save PDB of process ; ; There are no critical sections in force. Although we may enter here with ; critical sections locked down, they are no longer relevant. We may safely ; free all allocated resources. ; MOV AH,82h INT int_IBM MOV fAborting,-1 ; signal abort in progress CallInstall NetResetEnvironment, multNet, 34 ;DOS 4.00 doesn't need it ; Allow REDIR to clear some stuff ; On process exit. MOV AL,int_Terminate invoke $Get_interrupt_vector ; and who to go to POP CX ; get ThisPDB SaveReg ; save return address MOV BX,[CurrentPDB] ; get currentPDB MOV DS,BX MOV AX,DS:[PDB_Parent_PID] ; get parentPDB ; ; AX = parentPDB, BX = CurrentPDB, CX = ThisPDB ; Only free handles if AX <> BX and BX = CX and [exit_code].upper is not ; Exit_keep_process ; CMP AX,BX JZ reset_return ; parentPDB = CurrentPDB CMP BX,CX JNZ reset_return ; CurrentPDB <> ThisPDB PUSH AX ; save parent CMP BYTE PTR [exit_type],Exit_keep_process JZ reset_to_parent ; keeping this process ; ; We are truly removing a process. Free all allocation blocks belonging to ; this PDB ; invoke arena_free_process ; ; Kill off remainder of this process. Close file handles and signal to ; relevant network folks that this process is dead. Remember that CurrentPDB ; is STILL the current process! ; invoke DOS_ABORT reset_to_parent: POP [CurrentPDB] ; set up process as parent reset_return: ; come here for normal return PUSH CS POP DS ASSUME DS:DOSGROUP MOV AL,-1 ; ; make sure that everything is clean In this case ignore any errors, we cannot ; "FAIL" the abort, the program being aborted is dead. ; EnterCrit critDisk invoke FLUSHBUF LeaveCrit critDisk ; ; Decrement open ref. count if we had done a virtual open earlier. ; invoke CHECK_VIRT_OPEN IF BUFFERFLAG invoke RESTORE_USER_MAP ;AN000;LB. restore user's EMS map ENDIF CLI MOV BYTE PTR [INDOS],0 ; Go to known state MOV BYTE PTR [WPERR],-1 ; Forget about WP error MOV fAborting,0 ; let aborts occur POP WORD PTR ExitHold POP WORD PTR ExitHold+2 ; ; Snake into multitasking... Get stack from CurrentPDB person ; MOV DS,[CurrentPDB] ASSUME DS:NOTHING MOV SS,WORD PTR DS:[PDB_user_stack+2] MOV SP,WORD PTR DS:[PDB_user_stack] ASSUME SS:NOTHING invoke restore_world ASSUME ES:NOTHING MOV User_SP,AX POP AX ; suck off CS:IP of interrupt... POP AX POP AX MOV AX,0F202h ; STI PUSH AX PUSH WORD PTR [EXITHOLD+2] PUSH WORD PTR [EXITHOLD] MOV AX,User_SP IRET ; Long return back to user terminate address EndProc HardErr ; ; This routine handles extended error codes. ; Input : DI = error code from device ; Output: All EXTERR fields are set ; Procedure SET_I24_EXTENDED_ERROR,NEAR PUSH AX MOV AX,OFFSET DOSGroup:ErrMap24End SUB AX,OFFSET DOSGroup:ErrMap24 ; ; AX is the index of the first unavailable error. Do not translate if ; greater or equal to AX. ; CMP DI,AX MOV AX,DI JAE NoTrans MOV AL,ErrMap24[DI] XOR AH,AH NoTrans: MOV [EXTERR],AX POP AX ; ; Now Extended error is set correctly. Translate it to get correct error ; locus class and recommended action. ; PUSH SI MOV SI,OFFSET DOSGROUP:ERR_TABLE_24 invoke CAL_LK ; Set other extended error fields POP SI ret EndProc SET_I24_EXTENDED_ERROR CODE ENDS END