home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sampler0
/
parchk.asm
< prev
next >
Wrap
Assembly Source File
|
1979-12-31
|
31KB
|
580 lines
page ,132
title PARCHK.ASM -- Replacement for IBM-PC ROM BIOS parity error routines
;*****************************************************************************
;*
;* copyright(C) 1984 Skip Gilbrech (CIS 71445,534)
;* 90 Lexington Ave. #10-G
;* New York, NY 10016
;* 212-685-0551
;*
;* This program may be freely copied/altered for any non-commercial
;* purpose but may not be sold or used in any way as part of any
;* profit-making venture without permission of the author.
;*
;* author = skip gilbrech
;* date written = 02/19/84
;*
;* environment:
;* system = ibm pc (dos 2.0 - but should work on any version)
;* processor = microsoft 8086 macro assembler
;*
;* USAGE: PARCHK /R (or) /D
;* /R = Report all errors which occur
;* /D = Disable reporting after an error
;*
;* PARCHK is a resident program which replaces the ibm pc rom-bios NMI
;* (non-maskable interrupt) handler.
;*
;* It will report any memory parity errors to the operator, but will allow
;* the system to continue running. An installation option lets you choose
;* whether or not to receive continued reports after the first error for
;* a particular channel (system memory or i/o channel). Regardless of the
;* option chosen, however, the first error for each channel will always be
;* reported.
;*
;* I wrote PARCHK mostly because of the frustration I felt a few days ago
;* when the message 'PARITY CHECK 2' appeared suddenly on my pc's screen.
;* I had seen the message a couple of times before, but never at a time
;* like this: The machine contained over 3 hours of unsaved work, and I
;* knew there was not a thing I could do about it, since the parity error
;* handler in the rom-bios simply disables interrupts and issues a HLT
;* instruction to the cpu.
;*
;* I have also read a number of messages on the Compuserve IBM-PC SIG
;* which indicate that I'm not the only one to have had this experience.
;*
;* IBM's approach in the rom-bios makes some sense. The condition of
;* system ram is unknown following a parity error, and continued operation
;* MIGHT result in all sorts of horrible consequences, i.e., hopelessly
;* corrupted data on disk, etc.
;*
;* On my system, though, memory parity errors have been extremely rare,
;* and have probably resulted from a slight glitch on the power line,
;* static, etc. I really have no idea what would have happened if I
;* had been able to continue operation in those cases. However, I have
;* crashed the system often enough (most of the time probably executing
;* random data or instructions somewhere) to have some confidence that the
;* most likely, if not the only possible, outcome of a processor gone wild
;* is simply a dead machine, i.e. interrupt vectors wiped out, etc., which
;* must be powered-down and up.
;*
;* So, for me, the risks of continued operation -- at least until important
;* data in ram are saved -- seem relatively small when balanced against
;* the CERTAINTY of lost data when the rom-bios routine gets control: If
;* data HAS been corrupted, at least there remains the chance of later
;* being able to examine and possibly fix it.
;*
;* Continued operation still DOES represent a risk, however, especially
;* if parity errors occur often, since that probably indicates a serious
;* hardware problem somewhere in the system.
;*
;* If you don't want to take that risk, please don't use this program,
;* as I can't, of course, take responsibility for any damage, real or
;* otherwise, it may cause.
;*
;* On the other hand, if PARCHK is ever responsible for saving a multi-
;* million dollar oil deal which would otherwise have fallen victim to
;* an unruly parity bit...... (suffice it to say that I would deem it
;* an honor to allow you to express your gratitude)
;*
;* The resident part of this code, by the way, uses up a little over 1K
;* of ram. Most of that space is taken up by routines which save the
;* current screen, write the error message, and then restore the screen.
;* I made no particular effort to make that code as compact as possible,
;* so, if space is at a premium, and/or you like doing such things, please
;* feel free to squeeze and optimize to your heart's content... As an
;* alternative, if you don't mind junk on your screen, it would be fairly
;* easy to replace most of the error message code with a short routine which
;* uses the rom-bios 'write teletype' function to print a message, but
;* doesn't waste any memory by saving the screen. It's probably best not
;* to use dos functions here, since a parity error can be reported at any
;* time, even with interrupts disabled and/or while in the middle of a dos
;* routine -- and pc/ms-dos is notoriously non-reentrant.
;*
;**************************************************************************
page
;**************************************************************************
;*
;* constants
;*
;**************************************************************************
; general equates:
false equ 0
true equ not false
cr equ 0dh ; carriage return
lf equ 0ah ; line feed
bell equ 7 ; ascii bell
spc equ ' ' ; ascii space, blank
tab equ 9 ; ascii horizontal tab
; program equates:
dosint equ 21h ; interrupt number for dos functions
prt_str equ 9 ; dos print string function
get_vers equ 30h ; dos get version number function
get_int_vec equ 35h ; dos 2.0 get interrupt vector function
set_int_vec equ 25h ; dos set interrupt vector function
vidint equ 10h ; bios video interrupt number
vid_state equ 15 ; bios - get video state
read_curs_pos equ 3 ; bios - read cursor position
set_curs_pos equ 2 ; bios - set cursor position
set_curs_type equ 1 ; bios - set cursor type
write_teletype equ 14 ; bios - write teletype to display
read_ac equ 8 ; bios - read att/char at curs. position
write_ac equ 9 ; bios - write att/char at curs. position
disp_row equ 10 ; row to display error msg
disp_col equ 25 ; col to display error msg
num_lines equ 3 ; number of text lines to display
normal equ 7 ; normal (white on black) vid. attrib.
reverse_blink equ 0f0h ; reverse-video blinking vid. attrib.
reverse equ 70h ; reverse-video (no blink)
; hardware specific equates:
port_a equ 60h ; system board 8255 port a address
port_b equ 61h ; system board 8255 port b address
port_c equ 62h ; system board 8255 port c address
par_err_mask equ 0c0h ; mask to test for any parity error
par_ch1_mask equ 10000000b ; mask to test for system board parity error
par_ch2_mask equ 01000000b ; mask to test for i/o channel parity error
disa_ch1_mask equ 00010000b ; mask to disable system board parity checking
enab_ch1_mask equ 11101111b ; mask to enable system board parity checking
disa_ch2_mask equ 00100000b ; mask to disable i/o channel parity checking
enab_ch2_mask equ 11011111b ; mask to enable i/o channel parity checking
nmi_int_no equ 2 ; NMI interrupt number
nmi_port equ 0a0h ; NMI control port
enable_nmi equ 80h ; value to output to enable NMI
disable_nmi equ 0 ; value to output to disable NMI
;**************************************************************************
int_vecs segment at 0
int_vecs ends
page
;**************************************************************************
cseg segment
assume cs:cseg,ds:cseg
org 80h ; for processing command line parms.
cmd_ct label byte ; number of chars. in command line
org 100h ; for .COM file
entry:
jmp init ; ck for residency, init if not resident
reenable_flag db false ; enable continued checking after error?
active_page db ? ; current active page
curs_pos dw ? ; current cursor position
orig_nmi_int_vec dd ? ; pointer to orig. NMI service routine
start_row_1 equ $
par_ch1_msg db " PARITY CHECK 1 (System Board) "
length_row equ $ - offset start_row_1
par_ch2_msg db " PARITY CHECK 2 (I/O channel) "
msg_1 db " Parity error reporting for "
msg_1a db " this channel will be disabled "
msg_1b db " this channel will continue "
scn_storage dw (length_row * 5) dup (?) ; storage for current screen
; display
page
;**************************************************************************
;* This is the start of the new NMI interrupt handler code.
;*
;* 1. Check system board 8255 to determine if a parity error has occurred.
;* 2. If not, pass control to the original NMI vector.
;* 3. If a parity error has occurred:
;* a. Disable the NMI temporarily.
;* b. Determine which channel was affected and disable parity reporting.
;* c. Determine whether error reporting will continue, depending on
;* which installation option was selected.
;* d. Print appropriate message and loop for about 10 seconds.
;* e. Re-enable reporting on the error channel if the /R option was selected.
;* f. Re-enable the NMI.
nmi_int_hdlr proc
pushf ; if we jump to orig. vector, arrive in same condition
push ax ;
in al,port_c ; read 8255 port
test al,par_err_mask ; has a parity error occurred?
jnz nmi_1 ; yes, jump to error routine
pop ax ; else, restore entering machine state
popf ;
jmp cs:orig_nmi_int_vec ; and pass control to original vector
nmi_1:
push ax ; save value read from port c
mov al,disable_nmi ; disable NMI
out nmi_port,al ;
pop ax ; restore port c value
sti ; allow further interrupts
push bx ; save possibly affected registers
push cx ;
push dx ;
push si ;
push di ;
push bp ;
push ds ;
push es ;
push ax ; save port value again
mov ax,cs ; set DS and ES to CS
mov ds,ax ;
mov es,ax ;
call save_screen ; save contents of current display
pop ax ; restore port value
test al,par_ch2_mask ; test for i/o channel error
in al,port_b ; get value at 8255 port b
jnz nmi_2 ; jump if i/o channel error
mov ah,disa_ch1_mask ; else, put planar disabling mask in AH
mov si,offset par_ch1_msg ; put adr of system board msg in SI
jmp short nmi_3 ; and continue
nmi_2:
mov ah,disa_ch2_mask ; put i/o channel disabling mask in AH
mov si,offset par_ch2_msg ; put adr of i/o channel msg in SI
nmi_3:
or al,ah ; turn on proper bit
out port_b,al ; disable appropriate channel to reset
push ax ; save mask value in AH
mov ax,offset msg_1 ; put adr of 2nd line of message in AX
mov bx,offset msg_1a ; put adr of 'remain disabled' msg in BX
cmp reenable_flag,false ; is continued checking desired?
pushf ; save result of comparison
je nmi_4 ; no, continue
mov bx,offset msg_1b ; else, put 'reenable' msg adr in BX
nmi_4:
call print_msg ; display our message
; -- loop allow reading the message
mov cx,15 ; trying to hit 10 seconds...
nmi_5:
mov bx,cx ; save outer loop count
sub cx,cx ; do each inner loop 65536 times
nmi_6:
loop nmi_6 ; spin wheels...
nmi_7:
loop nmi_7 ; ...
mov cx,bx ; restore outer loop count
loop nmi_5 ; and spin some more until done...
call restore_screen ; restore original display
popf ; flags show if reenabling desired
pop ax ; restore mask value in AH
je nmi_8 ; continue if reenabling not wanted
not ah ; else, flip bits in AH
in al,port_b ; get 8255 port b value
and al,ah ; turn masked bit off again
out port_b,al ; and reenable the channel
nmi_8:
pop es ; restore machine state
pop ds ;
pop bp ;
pop di ;
pop si ;
pop dx ;
pop cx ;
pop bx ;
mov al,enable_nmi ; reenable NMI
out nmi_port,al ;
pop ax ;
popf ;
iret
nmi_int_hdlr endp
page
;**************************************************************************
; Save current contents of the portion of the screen we will overwrite.
; Also save the current active page and cursor position.
; Registers not preserved.
save_screen proc near
mov ah,vid_state ; get active page
int vidint ; bios video interrupt
mov active_page,bh ; store it for others..
mov ah,read_curs_pos ; get cursor position and type
int vidint ;
mov curs_pos,dx ; store data
mov dx,(disp_row * 256 + disp_col) ; cursor at start of our msg
mov cx,num_lines + 2 ; save enough for top & bot. blank
; lines, and our message
mov di,offset scn_storage ; point DI to buffer
ss_1:
push dx ; save cursor position
push cx ; save outer loop count
mov cx,length_row ; get number of chars. per row
ss_2:
push di ; save buffer ptr
mov ah,set_curs_pos ; set cursor pos.
int vidint ;
mov ah,read_ac ; get attr/char at curs. pos
int vidint ;
pop di ; restore buffer ptr.
stosw ; store attr/char in buffer
inc dl ; bump DL to next column
loop ss_2 ; do for the entire row
pop cx ; restore outer loop count
pop dx ; restore cursor position
inc dh ; bump DH to next row
loop ss_1 ; do for required number of rows
ret
save_screen endp
page
;**************************************************************************
; Restore original contents of the screen.
; Also restore the current cursor position.
restore_screen proc near
mov bh,active_page ; get active page in BH
mov dx,(disp_row * 256 + disp_col) ; cursor at start of our msg
mov cx,num_lines + 2 ; restore amount saved...
mov si,offset scn_storage ; point DI to buffer
rs_1:
push dx ; save cursor pos.
push cx ; save outer loop count
mov cx,length_row ; get number of chars. per row
push si ; save buffer ptr
rs_2:
mov ah,set_curs_pos ; set cursor pos.
int vidint ;
pop si ; restore buffer ptr
lodsw ; get the attr/char in AX, bump SI
push si ; save it...
mov bl,ah ; put attr. where bios wants it
mov ah,write_ac ; write attr/char at curs. pos
push cx ; save row chars remaining
mov cx,1 ; tell bios to write 1 char.
int vidint ;
pop cx ; restore row chars remaining
inc dl ; bump DL to next column
loop rs_2 ; do for the entire row
pop si ; keep stack in order....
pop cx ; restore outer loop count
pop dx
inc dh ; bump DH to next row
loop rs_1 ; do for required number of rows
mov dx,curs_pos ; restore original cursor pos
mov ah,set_curs_pos ;
int vidint ;
ret
restore_screen endp
page
;**************************************************************************
; Print message to console
; 1st and last line blanks - start at current curs. pos.
; SI has adr. of 1st string, AX has second, and BX has third.
print_msg proc near
push bx ; push string addresses
push ax ;
push si ;
mov al,bell ; first put out a beep
mov ah,write_teletype ;
int vidint ;
mov bl,reverse_blink ; reverse-video blinking vid. attrib.
mov bh,active_page ; get active page in BH
mov cx,length_row ; length of a row
mov dx,(disp_row * 256 + disp_col) ; cursor at start of our msg
call blank_line ; put out the first blank line
pop si ; address of 1st line
call char_line ;
pop si ; ...2nd line
mov bl,reverse ; don't blink for the rest of msg
call char_line ;
pop si ; ...3rd line
call char_line ;
call blank_line ; ... and last blank line
mov dx,curs_pos ; restore original cursor pos
mov ah,set_curs_pos ;
int vidint ;
ret
blank_line: ; write CX blanks, bump row position
mov ah,set_curs_pos ; set cursor pos.
int vidint ;
mov ah,write_ac ; write attr/char at curs. pos
mov al,' ' ; write blanks
int vidint ;
inc dh ; bump row position
ret
char_line:
; -- attrib. in BL, active page in BH, count in CX, starting pos. in DX
; get chars. starting at DS:SI
push cx ; save count of chars. in line
push dx ; save starting curs. pos.
push si ; save buffer ptr
cl_1:
mov ah,set_curs_pos ; set cursor pos.
int vidint ;
pop si ; restore buffer ptr
lodsb ; get char. from message string in AL
push si ; save bumped string ptr.
push cx ; save chars. remaining in line
mov cx,1 ; so bios will write one char.
mov ah,write_ac ; write attr/char at curs. pos
int vidint ;
pop cx ; restore chars. remaining in line
inc dl ; bump DL to next column
loop cl_1 ; do for the entire row
pop si ; keep stack in order....
pop dx ; restore starting curs. pos.
inc dh ; bump DH to next row
pop cx ; restore count of chars. in line
ret
print_msg endp
; -- This is the end of the code which will be made permanently resident.
end_nmi_int_hdlr equ $
len_nmi_int_hdlr equ (end_nmi_int_hdlr - nmi_int_hdlr)
page
;**************************************************************************
install_msg db cr,lf,"PARCHK (v 1.0) installed -- Copyright (C) 1984 Skip Gilbrech",cr,lf,'$'
resident_msg db cr,lf,bell,"PARCHK (v 1.0) already resident",cr,lf,'$'
usage_msg db cr,lf
db "PARCHK (v 1.0) -- Copyright (C) 1984 Skip Gilbrech",cr,lf,cr,lf
db "USAGE: PARCHK /R (or) /D",cr,lf
db "/R = Report all errors which occur",cr,lf
db "/D = Disable reporting after an error",cr,lf,cr,lf
db "PARCHK replaces the ibm pc rom-bios NMI (non-maskable interrupt) handler.",cr,lf
db "It will report any memory parity errors to the operator, but will allow",cr,lf
db "the system to continue running. An installation option lets you choose",cr,lf
db "whether or not to receive continued reports after the first error for",cr,lf
db "a particular channel (system memory or i/o channel). Regardless of the",cr,lf
db "option chosen, however, the first error for each channel will always be",cr,lf
db "reported. The source file, PARCHK.ASM, contains some cautions which",cr,lf
db "you should read before using this program.",cr,lf,cr,lf,'$'
page
;**************************************************************************
;* Initialization procedure:
;*
;* 1. Check whether the routine is already resident by getting the current
;* interrupt vector and checking whether our code is servicing it.
;* If so, just print message and exit without installing
;* 2. Check command line to see if continued checking is wanted after
;* error and set flag accordingly. If no parms or unknown parms,
;* print informational message and exit without installing.
;* 3. Otherwise, change vector to point to our handler, print installation
;* message, and execute dos 'terminate but stay resident' interrupt,
;* installing only the code which will actually be used.
;*
;* The idea, and much of the code, for the 'testresident' routine, came
;* from Tony A. Rhea's KEYSTAT program. His copyright notice is reproduced
;* below:
;*
;* KEYSTAT -- Copyright (C) 1983 Tony A. Rhea
;* This program may be copied for non-commercial use.
;* Adapted from KEYFLAGS (PC-World, Oct. 83, page 266) by Morton Kaplon
;*
init proc near
call testresident ; see if handler already resident,
jz init_1 ; ZF set = already resident -- print msg and exit
call get_parms ; see if reenabling NMI desired, set flag
jc init_2 ; if parm error, print usage message
; else, install the handler
mov al,nmi_int_no ; interrupt number in AL
mov ah,set_int_vec ; funct. number for set int. vector
mov dx,offset nmi_int_hdlr ; offset of resident code
int dosint ; set the vector
mov dx,offset install_msg ; print install message
mov ah,prt_str ;
int dosint ;
mov dx,offset end_nmi_int_hdlr ; set DX to end of resident code
int 27h ; dos terminate but stay resident func.
init_1:
mov dx,offset resident_msg ; msg showing already installed
jmp short init_3 ; print it and exit
init_2:
mov dx,offset usage_msg ; general info. msg.
init_3:
mov ah,prt_str ;
int dosint ;
int 20h ; return to dos (nothing made resident)
init endp
page
;**************************************************************************
;* see if our handler is already resident -- zero flag set if it is.
;*
;* AX, BX, CX, SI, DI changed, ES preserved
testresident proc near
push es ;
mov al,nmi_int_no ; interrupt number in AL
call get_vec ; interrupt vector returned in ES:BX
mov orig_nmi_int_vec,bx ; store IP of current servicer
mov orig_nmi_int_vec[2],es ; store CS of current servicer
mov cx,len_nmi_int_hdlr ; number of bytes to match
mov si,offset nmi_int_hdlr ; point DS:SI to our code
mov di,bx ; make ES:DI point to current handler
cld ; auto increment
repe cmpsb ; compare while equal - zero flag set on exit
; if all bytes matched (already resident)
pop es ;
ret
testresident endp
page
;**************************************************************************
;* Get interrupt vector for interrupt number in AL. On exit, ES:BX contain
;* CS:IP for vector.
;*
;* Only ES and BX changed
get_vec proc near
push ax ; save entering value
push ax ; save interrupt number requested
mov ah,get_vers ; dos get version function
int dosint ; major version returned in AL, minor in AH
pop bx ; restore int. number requested
cmp al,2 ; less than 2 = pre dos 2.0
jb gv_direct ; get vector directly
mov al,bl ; move requested interrupt vector into AL
mov ah,get_int_vec ; dos get int. vector function
int dosint ; CS:IP returned in ES:BX
pop ax ;
ret
gv_direct: ; get vector directly
; first, convert number in BL to offset in BX
xor bh,bh ; make sure BH = 0
shl bx,1 ; multiply by 4
shl bx,1 ;
mov ax,int_vecs ; point ES to segment for int. vectors
mov es,ax ;
mov ax,es:[bx+2] ; get CS value into AX
mov bx,es:[bx] ; get IP value into BX
mov es,ax ; mov CS value into ES
pop ax ;
ret
get_vec endp
page
;**************************************************************************
;* Scan command line for /R or /D installation option -- carry set
;* on exit if no valid parm.
;*
;* AX,BX,SI changed
get_parms proc near
mov bx,offset cmd_ct ; BX pts to number of cmd line chars
mov si,[bx] ; get count
inc bx ; point BX to 1st char
and si,0ffh ; max. 127 chars. - set flags
jz gp_err_exit ; no parms, skip the rest
mov byte ptr [bx][si],0 ; make command line null terminated
gp_top_loop:
mov al,[bx] ; get the char
or al,al ; null?
je gp_err_exit ; if so, return error
cmp al,spc ; space?
je gp_bot_loop ; yes, get another char
cmp al,tab ; tab?
je gp_bot_loop ; yes, get another char
cmp al,'/' ; see if possible /R or /D switch
jne gp_err_exit ; no, return error
call ck_switches ; check parm, carry set on error
jc gp_err_exit ; carry set, return error
jmp gp_exit ;
gp_bot_loop:
inc bx ; point to next char
jmp gp_top_loop ; go get it
gp_err_exit:
stc ; set carry to show error
gp_exit: ; if got here by jump, carry is reset
ret
ck_switches: ; ck parms ( BX pts to '/' )
inc bx ; point to possible switch
mov al,[bx] ; get char
cmp al,'Z' ; check if possibly upper-case
jbe ck_sw1 ; no? continue
sub al, 'a' - 'A' ; make upper-case
ck_sw1:
cmp al,'R' ; reenable checking after parity error?
jne ck_sw2 ; no? continue
mov reenable_flag,true ; else, set flag
ret ; and return
ck_sw2:
cmp al,'D' ; disable checking after error?
je ck_sw_exit ; yes, return ok
stc ; else, set carry to show error
ck_sw_exit:
ret
get_parms endp
cseg ends
end entry
XA 6: ; else, set carry to show error
ck_sw_exit:
ret
get_parms endp
cseg e