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