Method for communicating information between independently loaded, concurrently executing processes5175855Abstract A subprogram which operates as a MS/PC-DOS device driver provides a communication path between a data acquisition program which operates as an interrupt driven memory-resident program in background and a conventional data analysis program which operates in foreground. Information transfers are initiated by the foreground program which issues standard I/O requests to the DOS, identifying the device driver and the information storage area within the foreground address space from which or to which the information is to be communicated. The device driver subprogram relays the information to the background process by calling the background program at an entry point which is made available to the device driver at a predetermined interrupt vector location, enabling the device driver to pass the memory address of the foreground storage area, together with the size of the block of information to be communicated, to the background program. The background program operates in a selected one of several possible modes, one of which waits for data to supplied from an external data acquisition instrument before returning control to the foreground process so that the two processes can operate in synchronism. Claims What is claimed is: Description BRIEF SUMMARY OF THE INVENTION
__________________________________________________________________________
DEVICE DRIVER ASSEMBLY LANGUAGE LISTING ---
;-----------------------------
cr equ 0dh
1f equ 0ah
eom equ `$`
user.sub.-- int
equ 61h
vectors
segment
at 0H
org user.sub.-- int*
user.sub.-- int.sub.-- offset dw ?
user.sub.-- int.sub.-- segment dw ?
vectors
Ends
code segment
public `code`
driver
proc far
assume
cs:code,ds:code,es:code
org 0
max.sub.-- cmd
equ 12 ;driver command code max: 15 for dos 3.x
; DEVICE DRIVER HEADER
;
header
dd -1 ; link to next device
dw 8000h
; attribute word. bit 15=1
for character device
dw Strat
; "strategy" entry point
dw intr
; "interrupt" entry point
db `rta `
; device name
;
; LOCAL VARS FOR DRIVER
;
rh.sub.-- ptr
dd
? ; request header pointer
Channel
dw
0 ; channel (buffer number) for request
ident
db
cr,1f,1f
db `notebook rta device driver v.100`
db cr,1f,1f,eom
chk.sub.-- str
db
`Srid`
even
save.sub.-- ss
dw 3412h
save.sub.-- sp
dw 0aa5h
istack
db 512 dup("stk")
topstk
dw 0
astack
dw topstk
marker
db `0123456789`
;
; DRIVER COMMAND CODES DISPATCH TABLE
;
dispatch:
dw
inti ; 0 = init driver
dw
media.sub.-- chk ; 1 = media check on block device
dw
build.sub.-- bpb; 2 = build bios parameter block
dw
ioctl.sub.-- inp ; 3 = control read from device
dw
input ; 4 = normal destructive read
dw
nd.sub.-- input ; 5 = non-destructive read, no wait
dw
inp.sub.-- stat ; 6 = return current input status
dw
inp.sub.-- flush ; 7 = flush device input buffers
dw
output ; 8 = normal output to device
dw
output.sub.-- vfy ; 9 = output with verify
dw
outp.sub.-- stat ; 10 = return current output status
dw
outp.sub.-- flush ; 11 = flush output buffers
dw
ioctl.sub.-- outp ; 12 = i/o control output
dw
dev.sub.-- open ; 13 = device open (dos 3.x)
dw
Dev.sub.-- close ; 14 = device close (dos 3.x)
dw
Rem.sub.-- medial 15 = removeable media (dos 3.x)
;
; STRUCTURE DEFINITION FOR REQUEST HEADER
;
request
struc
rlength
db ?
Unit db ?
Command
db ?
Status dw ?
Reserve
db 8 dup(?)
Media db ?
Address
dd ?
Count dw ?
Sector dw ?
Request ends
;
; STRATEGY ROUTINE
;
; gets rh address in es:bx and saves it
;
strat
proc
far
mov
word ptr cs:[rh.sub.-- ptr],bx
mov
word ptr cs:[rh.sub.-- ptr+2],es
ret
strat
endp
;
; INTERRUPT ROUTINE
;
; dispatches to appropriate routine according to command code in
; request header. Command code routines responsible for setting
; up routine and passing control to error or exit as appropriate
;
intr
proc far
push
ax ; save as in caller' s stack
mov
cs:save.sub.-- sp,sp
mov
cs:save.sub.-- ss,ss
mov
ax,cs
mov
ss,ax
mov
sp,astack
push
bx ; save remaining regs in our stack
push
cx
push
dx
push
ds
push
es
push
di
push
si
push
bp
push
cs ; set up local addressing
pop
ds
les
di,[rh.sub.-- ptr]; es:di = request header
mov
bl,es;[di.Command]
xor
bh,bh
; copy the command to ah (like a function number)
mov
ah,bl
xor
al,al
cmp
bx,max.sub.-- cmd; check for code out of range
jg unk.sub.-- command
shl
bx,l ;set up to dispatch
jmp
word ptr[bx+dispatch]
; exits from driver
unk.sub.-- command:
mov a1,3
; set unknown command and done bits
error:
mov ah,8ah
; come here with a1=error code--sets
; error and done bits
jmp exit
done:
mov ah,1
; no error,set done bit only
exit:
; general purpose exit routine - enter
; with ax = returnstatus word for
; request header
lds
bx,cs:[rh.sub.-- ptr]
mov
ds:[bx.Status],ax
pop
bp
pop
si
pop
di
pop
es
pop
ds
pop
dx
pop
ex
pop
bx
mov
ss,save.sub.-- ss
mov
sp,save.sub.-- sp
pop
ax
ret
;
media.sub.-- chk:
; 1 = media check on block device
build.sub.-- bpb:
; 2 = build bios parameter block
ioctl.sub.-- inp:
; 3 = control read from device
;------------------------------
; NORMAL READ FROM NOTEBOOK USER READ
ROUTINE
;------------------------------
input: ; 4 = normal destructive read
nd.sub.-- input:
; 5 = non-destructive read, no wait
inp.sub.-- stat:
; 6 = return current input status
inp.sub.-- flush:
; 7 = flush device input buffers
output: ; 8 = normal output to device
output.sub.-- vfy:
; 9 = output with verify
outp.sub.-- stat:
; 10 = return current output status
putp.sub.-- flush:
; 11 = flush output buffers
ioctl.sub.-- outp:
; 12 = i/o control output
dev.sub.-- open:
; 13 = device open (dos 3.x)
Dev.sub.-- close:
The device driver as listed above functions as follows to enable a conventional program to communicate with the data acquisition program running in background. First, it should be noted that the DEVICE DRIVER HEADER as well as the STRATEGY ROUTINE and the INTERRUPT ROUTINE are mandatory parts of any installable device driver. The DEVICE DRIVER HEADER includes an ATTRIBUTE WORD which, as seen in the listing above, has been set to contain all zero's, except for bit 15, which is set to equal "1". DOS interprets this attribute word to mean that the device driver is a "character" (as opposed to a "block" device), that I/O Device Control is not used, nor are OPEN/CLOSE/REMOVABLE MEDIA calls. In addition, the ATTRIBUTE WORD 8000 Hex further indicates to DOS that this particular device driver does not have special attributes and is neither a CLOCK, NUL, nor STANDARD INPUT OR OUTPUT device. Although, as will be apparent to those skilled in the art, the device driver could be expanded to have even greater functionality, particularly by adding support for I/O Control (IOCTL) functions accessed via DOS function call 44h, most existing application programs are incapable of using IOCTL; accordingly, this additional capability has been intentionally omitted from the preferred form of device driver listed above. To initiate communication via a device driver, DOS passes a pointer to a data structure called the DRIVER REQUEST HEADER BLOCK which DOS builds to invoke the services of the driver. The pointer is passed in the ES:BX register pair to the STRATEGY ROUTINE, a FAR procedure, which saves it within its own code segment at RH.sub.-- PTR and returns control to DOS. DOS then calls the INTERRUPT ROUTINE, another FAR procedure, which performs the operation requested in the DRIVER HEADER REQUEST BLOCK. The INTERRUPT ROUTINE saves all registers, checks the command code (the third byte contained in the DRIVER REQUEST HEADER BLOCK as seen in the STRUCTURE DEFINITION FOR REQUEST HEADER) to insure that it is valid (less than or equal to MAX.sub.-- CMD, which is set to 12 in the listing above), and then branches to one of the routines whose address is contained in the DRIVER COMMAND CODES DISPATCH TABLE. This dispatch table represents a list of the 16 standard I/O commands which DOS uses to request services from any device driver. As seen from the assembly language listing for the driver, the dispatch table generates a branch to the same location for all commands except the first, INIT, a command which DOS issues only once, when the driver is first loaded by the system at boot time as a result of its being identified in the DOS configuration file CONFIG.SYS. This initial load is requested by the user by including a line in the file CONFIG.SYS (a DOS text file) having the form "DEVICE=RTA.SYS" where "RTA.SYS" is the DOS filename of the assembled and linked device driver code file. As seen in the device driver source listing above, the INIT routine uses INT 21h to display a "sign-on" message on the system's standard output device (usually the CRT screen) to indicate to the user that this particular device driver is being loaded and initialized. The request header address is then restored and the first useable memory address (break address) is set to the starting address of the INIT routine which, although it must be present in the device driver, will no longer be needed during the resident life of the driver once it has been initialized, and may hence be overwritten. The INIT routine concludes by branching to DONE which sets the appropriate status code into the AX register for handling by the general purpose exit routine EXIT. EXIT restores the general registers and concludes with a FAR return to the DOS process responsible for loading the device drivers listed in the CONFIG.SYS file. Thereafter, the device driver code is called by DOS to provide communication between an application program running in foreground and the data acquisition program running in background. This accomplished by the DISPATCH TABLE branch for all DOS command codes (other than INIT) to a common routine which simply transfers control to the resident data acquisition program at an entry point stored in a predetermined interrupt vector location (user interrupt vector location 61h in the example driver) which was previously set when the run time library acquisition program was loaded and initialized. Before passing control to the run time library code, however, the device driver does a validity check to insure that the needed code has indeed been properly loaded and initialized. It does this by checking a predetermined location in the run time library code (six bytes before the interrupt entry point) for a set of signature bytes CHK.sub.-- STR having the value `Srid`. If the signature check is valid, the run time library code is invoked by INT USER.sub.-- INT which is entered with the function humber in the AH register and the REQUEST HEADER address in the register pair ES:DI. The correct address of the entry point of the interface routise is installed by an initialization routine in the data acquisition program which uses the standard DOS function 25h (SET INTERRUPT VECTOR) to install the user interrupt. The installed interrupt vector points to code which, in the illustrative example, is an assembly language routine which provides a more convenient interface so that the data acquisition routines may be written in the high-level language C. As an illustration, the assembly language listing below defines the code at the interrupt handler entry point, together with certain additional data structures including the "signature bytes" noted earlier, as follows:
______________________________________
RTL INTERRUPT HANDLING INTERFACE CODE
IN ASSEMBLY LANGUAGE ---
; prologue/epilogue for interrupt handler written in C.
; assumptions: interrupt is issued from Lattice C large
model code - we
; need to preserve only bp, ds, and es
include ltn.mac
extrn bufrd.sub.-- int:far
PSEG
;-----------------------------------------------------------------------
; Data structures per Microsoft memory-resident standard
;------------------------------------------------------------------------
prog.sub.-- id db "PIDR"
dw 0003h ; BCD version number, 3.00
int.sub.-- list.sub.-- ptr dw offset int.sub.-- list
dw 0 ; startup shift keys
dw 0 ; startup key codes
dw 0 ; option area
db "LABTECH (R) RTA Notebook",0
int.sub.-- list db 08h,61h,0 ; list of interrupts used
user.sub.-- int.sub.-- record dd 0 ; space for down-pointer
dw offset prog.sub.-- id ; ptr to program id record
db 192 ; priority
db 1,2,3,4,5,6,7,8,0 ; function numbers
;-----------------------------------------------------------------------
; signature bytes for driver to check
;-----------------------------------------------------------------------
db "Srid"
dw offset user.sub.-- int.sub.-- record
;-----------------------------------------------------------------------
; gotint - user read interrupt entry
; on entry;
; AH integer function number
; (AL don't care)
; ES:DI pointer to request header
;------------------------------------------------------------------------
2
BEGIN gotint
; set our data segment
push ds
push ax
mov ax,DGROUP
mov ds,ax
pop ax
; make function number in ah into a regular integer
for C language
mov al,ah
xor ah,ah
sti
mov bp,sp
; arrange arguments to bufrd.sub.-- int in stack
push es
push di
push ax
cld
call bufrd.sub.-- int
; drop arguments from stack
mov sp,bp
cli
; restore from caller's stack
pop ds
iret
gotint endp
ENDPS
END
-- END OF RTL INTERRUPT HANDLING
INTERFACE ASSEMBLY LISTING---
______________________________________
As seen from the listing above and the prior discussion, at the entry point GOINT the AH register contains the function number and the ES:DI register pair contain the pointer to the REQUEST HEADER. The assembly language routine sets the data segment, converts the function number in the AH register into conventional C language integer format, puts the arguments to be passed to the background process in the stack, and makes a FAR call to the procedure BUFRD.sub.-- INT, the portion of the background program which handles data communications via the device driver. That procedure as written in C is listed below:
______________________________________
C LANGUAGE LISTING OF BUFRD.sub.-- INT
FUNCTION ---
/* structure used for holding characters preparatory to output
from the program */
*define MAX.sub.-- HB.sub.-- SIZE 512
struct hold.sub.-- struct
{
/* intermediate hold buffer */
int rent;
char *rptr;
char *ptr;
char *wptr;
char hold.sub.-- buffer[MAX.sub.-- HB.sub.-- SIZE];
}
struct hold.sub.-- struct h.sub.-- data;
/*-----------------------------------------------------------------------
* bufrd.sub.-- int - C function called from foreground using
software interrupt
* via gotint.asm interrupt catcher
*-----------------------------------------------------------------------
.
*/
#define STATUS.sub.-- DONE 0x0100
#define STATUS.sub.-- ERROR 0x8000
#define STATUS.sub.-- READ.sub.-- FAULT 0x800a
/* ASCII ctl-Z end of file character */
#define EOF.sub.-- CHAR 0x1a
/* variables to copy request header values into */
char rh.sub.-- cmd;
unsigned rh.sub.-- status;
char *rh.sub.-- transfer.sub.-- ptr;
unsigned rh.sub.-- count;
/* macro that can be used as an 1value to set the count
in the request header */
#define RH.sub.-- COUNT (*((unsigned *)(rh.sub.-- ptr + 0x12)))
unsigned
bufrd.sub.-- int(function, rh.sub.-- ptr)
int function;
char *rh.sub.-- ptr;
{
struct hold.sub.-- struct *h = &hdata;
int c;
int length = 0; /* number of bytes put in caller's buffer */
int went; /*bytes remaining in caller'buffer */
char *wptr; /* pointer for writing to user buffer*/
unsigned status = 0;
rh.sub.-- cmd = *(rh.sub.-- ptr + 2);
rh.sub.-- status = *((unsigned *)(rh.sub.-- ptr + 3));
rh.sub.-- transfer.sub.-- ptr = *((char **)(rh.sub.-- ptr + 0x0e));
rh.sub.-- count = *((unsigned *)(rh.sub.-- ptr + 0x12));
switch (function)
{
/* write request */
case 8:
buffer.sub.-- command(rh.sub.-- count, rh.sub.-- transfer.sub.--
ptr);
break;
/* read request */
case 4:
/* set write count and write pointer for
writing to user buffer */
went = rh.sub.-- count;
wptr = rh.sub.-- transfer.sub.-- ptr;
while(--went>=0)
{
if(--h->rent >=0)
c = *h->ptr++;
else
c = fill.sub.-- hold(h);
if(c == ERROR)
{
c = EOF.sub.-- CHAR;
break;
}
*wptr++ = c;
length++;
}
status = STATUS.sub.-- DONE;
RH.sub.-- COUNT = length;
break;
default;
break;
}
return (status);
}
-- END OF C LANGUAGE BUFRD.sub.-- INT
FUNCTION LISTING ---
______________________________________
As seen above, the device command number (expressed as the integer FUNCTION) and a pointer to the header (expressed as RH.sub.-- PTR) are passed to BUFRD.sub.-- INT. If the value of the device command was 8 indicating a request by DOS to write to the virtual device, BUFRD.sub.-- INT invokes the procedure BUFFER.sub.-- COMMAND, passing to it the number of bytes to be transferred RH.sub.-- COUNT and the address of the buffer area in memory containing these bytes RH.sub.-- TRANSFER.sub.-- PTR, both of these values being obtained from the ADDRESS and COUNT fields of the REQUEST HEADER supplied by DOS (see the STRUCTURE DEFINITION FOR REQUEST HEADER in the assembly language listing of the device driver code). The procedure BUFFER.sub.-- COMMAND can the perform whatever operation is indicated by the I/O request received from the foreground program. Information is transferred from the background program to the foreground program in response to a device command having the value 4, indicating a DOS input (read) operation. In that event, the procedure BUFRD.sub.-- INT listed above transfers up to RH.sub.-- COUNT bytes in the data holding area HDATA.HOLD.sub.-- BUFFER (which is loaded by means of conventional data acquisition methods as represented by the function FILL.sub.-- HOLD, the details of which are not described in this specification) to the destination in the address space of the foreground program as specified by the pointer RH.sub.-- TRANSFER.sub.-- PTR supplied by DOS. It may be noted that, if BUFRD.sub.-- INT encounters an error condition or the end of the available data before transferring RH.sub.-- COUNT bytes (as indicated by a data byte value=ERROR as set by the background program), a DOS end-of file character CONTROL-Z is placed in the transfer buffer and the BUFRD.sub.-- INT terminates. In accordance with the invention, an interface procedure of the type illustrated by BUFRD.sub.-- INT may be programmed to operate in a selected one of several modes of operation, and the device driver interface may be used to send commands from the foreground program to the background program selecting any of these modes. In one mode, all data accumulated in the data holding area HDATA.HOLD.sub.-- BUFFER since the last read request may be passed to the foreground program, assuring that no data will be skipped. In another mode, only the latest data point in the holding area is passed to the foreground program. Yet another command requests the transfer of a new data point regardless of when the last one had been collected and put in the holding area. Reading can also be set to return with no data if none has been collected since the last read command, or to wait for data to be entered from the external instrument. This last "wait for data" mode provides a simple way for the foreground program to synchronize itself to the data acquistion rate (or some other event or events known to the background program). As may be appreciated from the foregoing, the present invention provides a mechanism for constructing a variety of memory resident programs which can run in background to acquire data or perform other functions, and which can provide data and control information to, or receive data and/or control information from application programs running in foreground, and/or which can synchronize the operation of the foreground program to events known to the background program, even though those programs have no means of communicating other that via standard DOS files and devices. It is to be understood that the specific arrangement which has been described is merely illustrative of one application of the principles of the invention and that numerous modifications may be made by those skilled in the art without departing from the true spirit and scope of the invention.
|
Same subclass Same class Consider this |
||||||||||
