gw@sickkids.UUCP (CFI/Graham Wilson ) (08/31/88)
A while back I posted a request for information on catching DOS device errors. There were a few comments about interrupt 0x24. Thanks all for the leads. My investigations shall now lead down that path. Meanwhile, I made the silly mistake of mentioning that I have assembler code to catch disk errors via BIOS calls. A lot of requests for this code came through. So here it is... ********************************************************************** ********************************************************************** ;====================================================================== ; ; DISK ERROR CATCHING ASSEMBLER ROUTINES ; ;====================================================================== public _diskerrinit public _diskerrterm dgroup group _STACK, _DATA assume ss:dgroup, ds:dgroup _STACK segment word stack 'STACK' _STACK ends _DATA segment word public 'DATA' _DATA ends _TEXT segment byte public 'CODE' assume cs:_TEXT handler_ds DW ? handler_es DW ? save_ss DW ? save_sp DW ? local_sp DW ? return_ax DW ? handler_off DW ? handler_seg DW ? int13_off DW ? int13_seg DW ? parm_ax DW ? parm_bx DW ? parm_cx DW ? parm_dx DW ? parm_es DW ? ;====================================================================== ; ; Interrupt handler. Calls the handler function as: ; ; func(error, serv, drive, head, track, sector, nsects, farbuf) ; int error; /* returned error code */ ; int serv; /* service (2 or 3) */ ; int drive; /* 0 for A, 1 for B */ ; int head; ; int track; ; int sector; ; int nsects; ; char far *farbuf; ; ;====================================================================== diskioerr proc far sti ; ; If not floppy or not read or write op, skip handler ; cmp dl,2 jge disk_skip ; jmp if not floppy cmp ah,2 jl disk_skip cmp ah,3 jle disk_init ; jmp if read or write ; ; Skip handler ; disk_skip: pushf call dword ptr cs:int13_off ret 2 ; ; Set up for handler - save parameters ; disk_init: mov cs:parm_ax,ax mov cs:parm_bx,bx mov cs:parm_cx,cx mov cs:parm_dx,dx mov cs:parm_es,es ; ; Call disk I/O routine ; disk_int13: pushf ; simulate int call dword ptr cs:int13_off jc disk_handler ; jmp if error ret 2 ; no error - return ; ; Set up segments for call to handler ; disk_handler: mov cs:return_ax,ax push ds push es mov cs:save_ss,ss mov cs:save_sp,sp mov ds,cs:handler_ds mov es,cs:handler_es mov ss,cs:handler_ds mov sp,cs:local_sp ; ; Set up stack for call to handler ; push cs:parm_bx ; buf offset push cs:parm_es ; buf segment mov ax,cs:parm_ax mov cl,ah xor ah,ah push ax ; nsects mov ax,cs:parm_cx mov bl,ah xor ah,ah push ax ; sector xor bh,bh push bx ; track mov ax,cs:parm_dx mov bl,ah xor bh,bh push bx ; head xor ah,ah push ax ; drive xor ch,ch push cx ; service mov ax,cs:return_ax mov al,ah xor ah,ah push ax ; error ; ; Call handler ; Return codes (AX): 0 - try again ; 1 - ignore ; 2 - return with error ; call dword ptr cs:handler_off ; ; Restore segments ; mov sp,cs:save_sp mov ss,cs:save_ss pop es pop ds or al,al je disk_again ; jmp if try again dec al je disk_ignore ; jmp if ignore error ; ; Return with error ; mov ax,cs:return_ax stc ; error ret 2 ; simulate iret ; ; Ignore error ; disk_ignore: xor ah,ah clc ; no error ret 2 ; simulate iret ; ; Reset drive and try again ; disk_again: mov ax,cs:parm_ax ; get drive mov ah,0 ; service 0 mov dx,cs:parm_dx pushf ; simulate ret call dword ptr cs:int13_off mov ax,cs:parm_ax mov bx,cs:parm_bx mov cx,cs:parm_cx mov dx,cs:parm_dx mov es,cs:parm_es jmp disk_int13 diskioerr endp ;====================================================================== ; ; void diskerrinit(func, sp) ; int (far * func)(); ; char *sp; ; ; This routine initializes the BIOS disk I/O error catcher. It passes ; a far pointer to a function to execute. The second parameter points ; to the top of the secondary stack. ; ;====================================================================== _diskerrinit proc push bp mov bp,sp ; ; Save handler address & data segment pointer ; mov ax,[bp+argbase] mov cs:handler_off,ax mov ax,[bp+argbase+2] mov cs:handler_seg,ax mov ax,[bp+argbase+4] mov cs:local_sp,ax ; mov cs:handler_ds,ds mov cs:handler_es,es ; ; Get BIOS disk I/O interrupt ; mov al,13h mov ah,35h ; DOS service int 21h ; get interrupt vector mov cs:int13_seg,es ; save mov cs:int13_off,bx ; ; Set BIOS disk I/O interrupt ; push ds ; save data seg mov ax,cs mov ds,ax ; set ds == cs mov al,13h mov ah,25h ; DOS service mov dx,offset diskioerr int 21h ; set interrupt vector pop ds mov sp,bp pop bp ret _diskerrinit endp ;====================================================================== ; ; void diskerrterm() ; ; This routine un-initializes the BIOS disk I/O error catcher. ; ;====================================================================== _diskerrterm proc push bp mov bp,sp ; ; Reset BIOS disk I/O interrupt ; push ds ; save data seg mov al,13h mov ah,25h ; DOS service mov ds,cs:int13_seg mov dx,cs:int13_off int 21h ; reset interrupt vector pop ds mov sp,bp pop bp ret _diskerrterm endp _TEXT ends end ********************************************************************** ********************************************************************** The following sample C program shows how it would be used: ------------------------------------------------------------ main() { int far errhandler(); char *sp; FILE *fp; sp = alloca(1000) + 1000; /* sp points to TOP of stack */ diskerrinit(errhandler,sp); fp = fopen("testfile","w"); fprintf(fp,"this is data\n"); fclose(fp); diskerrterm(); } errhandler() { bios_print("Disk error - fix and press key\n"); bios_get_key(); return( 0 ); /* try again */ } ------------------------------------------------------------ Some comments and explanations are in order. The assembler code is MASM and the C code is MSC 4.0. The alloca() library routine is standard in MSC 4.0 and allocates memory FROM THE STACK. The block is deallocated when the routine which called alloca() exits [in this case main()]. So the block of memory is used as a stack by an error handler without having to worry about "error 2000 Stack Overflow". The routines bios_print() and bios_get_key() are fictional routines which print and get a key using BIOS system calls. The errors occur while DOS is busy, so calling DOS while handling an error will most likely hang the system. BE WARNED!!!! There are no known bugs, and it has worked flawlessly for many months. The error code passed to the error handler is the same as that returned by the BIOS int 13h. If anyone has an intelligent question, send me e-mail and I'll try to respond. Graham Wilson | watmath allegra decvax CyberFluor Inc | | | | 179 John St, #400 | uunet!utai!utgpu!utzoo!sickkids!robot!limbo!graham Toronto, Ont, M5T 1X4 | | | | (416) 977-5450 | ubc-vision linus research