home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mega CD-ROM 1
/
megacd_rom_1.zip
/
megacd_rom_1
/
MAGAZINE
/
DDJMAG
/
DDJ8910.ZIP
/
MISCHEL.LST
< prev
next >
Wrap
File List
|
1989-09-07
|
22KB
|
757 lines
_A Global Variable Device Driver for MS-DOS_
by Jim Mischel
[LISTING ONE]
name globals
page ,132
title 'GLOVAR - global variable device driver
;
; GLOVAR.ASM
;
; Copyright 1989, Jim Mischel
;
; This MS-DOS device driver provides true global variables to application
; programs.
;
; Sample make file:
; #
; # glovar.mak - build global variable device driver glovar.sys.
; #
; glovar.sys: glovar.asm
; tasm glovar
; link glovar;
; exe2bin glovar.exe glovar.sys
;
ideal
locals
model tiny,pascal
CodeSeg
org 0 ;device drivers start at offset 0
max_cmd equ 12 ;only 12 functions recognized by the driver
cr equ 0dh
lf equ 0ah
buff_size equ 1024 ;buffer size in bytes
TRUE equ -1
FALSE equ 0
;
; Device Driver Header
;
header dd -1 ;link to next device, -1 = end of list
dw 8000h ;simple character device driver
dw strat ;device Strategy entry point
dw intr ;device Interrupt entry point
db '$$GVAR$$' ;Device name
rh_ptr dd ? ;saved request header pointer
;
; Command codes dispatch table. Only functions 0 (initialize) and 4 (read)
; are supported. Other functions go to a stub routine that simple returns
; a non-error status.
;
dispatch:
dw init ; 0 = initialize driver
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw read ; 4 = read from device
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
dw not_imp ; not implemented
;
; MS-DOS Request Header structure definition
;
struc request
rlength db ? ;length of request header
unit db ? ;unit number for this request (block devices)
command db ? ;request header's command code
status dw ? ;driver's return status word
reserve db 8 dup (?) ;reserved area
media db ? ;media descriptor byte (block devices)
address dd ? ;memory address for transfer
count dw ? ;byte/sector count value
sector dw ? ;starting sector value (block devices)
ends request ;end of request header template
; Status codes returned by routines in AX
error equ 8000h
busy equ 0200h
done equ 0100h
unknown_command equ 3
;
; Device strategy routine -- save address of request header.
;
strat: mov [word ptr cs:rh_ptr],bx
mov [word ptr cs:rh_ptr+2],es
retf
;
; Interrupt routine. Dispatch to the proper routine.
;
proc intr far uses ax bx cx dx ds es di si
pushf
push cs
pop ds ;point to local data
les di,[rh_ptr] ;ES:DI = Request Header
mov bl,[es:di+request.command] ;bx = command code
xor bh,bh
cmp bx,max_cmd ;make sure it's legal
jle intr1
mov ax,error+unknown_command ;Error: unknown command
jmp short intr2
intr1: shl bx,1 ;form index to dispatch table
call [word ptr bx+dispatch] ;and branch to driver routine
les di,[rh_ptr] ;ES:DI = request header
intr2: or ax,done ;set Done bit
mov [es:di+request.status],ax ;store status in request header
popf
ret
endp intr
;
; 'stub' routine for un-implemented functions.
;
not_imp:
xor ax,ax ;set good status
retn
;
; Read -- return address of 'global_table'
;
; This routine assumes that the application program asked for 4 (no less!)
; bytes (the size of a far pointer).
;
; This function is called with ES:DI pointing to the MS-DOS request header.
;
read: lds si,[dword ptr es:di+request.address] ;DS:SI is buffer address
mov [word ptr ds:si],offset global_table ;store offset
mov [word ptr ds:si+2],cs ;and segment
mov [word ptr es:di+request.count],4 ;4 bytes transferred
xor ax,ax ;return good status
ret
;
; The following three routines (set_var, get_var, flush_vars) are accessed
; by applications programs through FAR calls. The addresses of these
; functions are provided in the 'global_table' structure, the address of
; which is passed to the application when it does a read on the $$GVAR$$
; device.
;
;
; set_var -- assign a value to the global variable pointed to by the
; 'varname' parameter. If the 'vardef' parameter points to the null string,
; the variable is removed from the table.
;
; Returns TRUE (-1) if successful, FALSE (0) if variable table overflows.
;
proc set_var far uses si di ds, varname:far ptr, vardef:far ptr
lds si,[varname]
call near ptr remove_var ;try to remove variable
les di,[vardef]
cmp [byte ptr es:di],0 ;check for NULL string
jnz do_set
mov ax,-1 ;and if so, just call it quits
jmp short set_done
do_set: lds si,[varname] ;otherwise,
call near ptr install_var ;install the new def
set_done:
ret
endp set_var
;
; Return the definition of the variable pointed to by 'varname' in the
; space pointed to by 'vardef'. Returns -1 if the variable exists, 0 if
; not. If the variable does not exist the null string is returned in 'vardef'.
;
proc get_var far uses si di ds, varname:far ptr, vardef:far ptr
lds si,[varname]
call find_var
jnc get1 ;if found, continue
les di,[vardef] ;otherwise,
mov [byte ptr es:di],0 ;store NULL string
xor ax,ax ;set failure status
jmp short @@done ;and exit
get1: cld ;variable found, now find '='
mov al,'='
mov cx,-1
repnz scasb
mov si,di
push es
pop ds ;DS:SI now points to first character of definition
les di,[vardef] ;ES:DI points to return vardef area
@@loop: lodsb ;copy the definition to
stosb ;the 'vardef' string
or al,al
jnz @@loop
mov ax,TRUE ;set status to TRUE
@@done: ret
endp get_var
;
; flush all variables from the global variable table. This is done by placing
; a NULL byte at the start of the table and updating the 'next_mem' variable
; so that it also points to the head of the table.
;
flush_vars:
mov [word ptr cs:glovars],0
mov ax,offset glovars
mov [word ptr cs:next_mem],ax
retf
;
; Support routines.
;
; Remove the variable (name pointed to by ds:si) and its definition from
; the global variables table.
;
remove_var:
call find_var ;find the variable
jc @@done
cld
mov si,di ;SI is start of variable name
mov al,0
mov cx,-1
repnz scasb ;move past definition
add [word ptr cs:next_mem],cx ;update next_mem pointer
inc [word ptr cs:next_mem]
xchg si,di ;si=next variable, di=old var
push cs
pop ds
mov cx,offset end_buff
sub cx,si ;cx is byte count
rep movsb ;variable has been erased
@@done: ret
;
; Called with ds:si pointing to variable name, es:di pointing to definition.
;
install_var:
push es
push di ;save 'vardef' address
cld
mov al,0
mov cx,-1
repnz scasb ;get length of definition
not cx
mov bx,cx ;and save
push bx ;will be used to copy the definition
push ds
pop es
mov di,si
mov cx,-1
repnz scasb ;get length of variable name
not cx
push cx ;will be used to copy the variable name
add bx,cx ;bx=strlen(varname)+strlen(vardef)+2
mov ax,buff_size + offset glovars
sub ax,[word ptr cs:next_mem]
cmp ax,bx
jnc inst1 ;if not enough room
xor ax,ax ;then fail
add sp,8 ;clean up stack
ret
inst1: push cs
pop es
mov di,[word ptr cs:next_mem] ;ES:DI points to buffer
pop cx ;restore varname length
dec cx ;don't want null terminator
@@loop1:
lodsb
call toupper ;convert char to upper_case
stosb ;and store
loop @@loop1
inst2: mov al,'='
stosb ;store the '=' separator
pop cx ;restore definition length
pop si ;restore 'vardef' address
pop ds
rep movsb ;and copy definition to buffer
mov [word ptr cs:next_mem],di
stosb ;store extra null byte for safe keeping
mov ax,TRUE
ret
;
; find_var -- attempt to find the variable pointed to by DS:SI in the
; global variables buffer.
; On success, AX = TRUE (-1), carry is cleared, and ES:DI points to the first
; character of the variable name in the buffer.
; If the variable is not found, AX = FALSE (0), carry flag is set, and ES:DI
; is undefined.
;
find_var:
cld
push cs
pop es
mov di,offset glovars
mov dx,si ;save name starting address
find0: mov bx,di ;and variable starting address
mov si,dx
dec di
find1: lodsb
call toupper
inc di
cmp al,[byte ptr es:di]
jz find1
or al,al ;didn't match, at end of string?
jnz get_next_var ;if not, go for next variable
cmp [byte ptr es:di],'=' ;at end of varname in buffer?
jnz get_next_var ;if not, do next variable
mov di,bx ;variable found
mov ax,TRUE
ret
get_next_var:
mov al,0
mov cx,-1
repnz scasb ;get start of next variable
cmp [byte ptr es:di],0 ;if not at end of buffer
jnz find0 ;then do next variable
xor ax,ax
stc ;the variable wasn't found
ret
;
; toupper - convert the character in AL to upper-case.
;
toupper:
cmp al,'a'
jc tod
cmp al,'z'+1
jnc tod
sub al,32
tod: ret
even ;might as well
;
; The address of this table is passed back to the application whenever
; a 'read' call is made on the device.
; The application uses this table to access the memory buffer and the
; driver functions.
;
global_table:
dd ? ;address of memory buffer
dd ? ;address of set_var routine
dd ? ;address of get_var routine
dd ? ;address of flush_vars routine
next_mem dw offset glovars ;pointer to end of defined variables
glovars: ;start of glovar buffer
;
; Initialization code.
;
; Initialize the function pointers table and the global data buffer, and
; return the address of the first byte of free memory after all the device
; driver code. The global variables buffer overlays this initialization code.
;
; Upon entry, ES:DI points to the MS-DOS request header. DS contains the
; local data segment (same as CS). This routine makes no attempt to save
; any registers.
;
init: mov ax,cs
mov es,ax
mov di,offset dhaddr
call hexasc ;convert load segment address to ASCII
mov ax,cs
mov es,ax
mov di,offset mbaddr
call hexasc
push cs
pop es
mov ax,offset glovars
mov di,offset mbaddr+5
call hexasc
mov ah,9 ;print sign-on message and
mov dx,offset ident ;driver load address
int 21h
les di,[rh_ptr] ;request header address in ES:DI
;store first usable memory address in request header
mov [word ptr es:di+request.address],offset end_buff
mov [word ptr es:di+2+request.address],cs
;
; Now set up the 'global_table' structure with proper memory addresses.
;
mov [word ptr global_table],offset glovars
mov [word ptr global_table+2],cs
mov [word ptr global_table+4],offset set_var
mov [word ptr global_table+6],cs
mov [word ptr global_table+8],offset get_var
mov [word ptr global_table+10],cs
mov [word ptr global_table+12],offset flush_vars
mov [word ptr global_table+14],cs
xor ax,ax ;return good status
mov [word ptr glovars],ax ;and clear glovars buffer
ret
ident db cr,lf,lf
db 'Global variable device driver version 1.0',cr,lf
db 'Copyright 1989, Jim Mischel',cr,lf
db 'Device Header at '
dhaddr db 'XXXX:0000. '
db 'Memory buffer at '
mbaddr db 'SSSS:OOOO',cr,lf,lf,'$'
;
; hexasc - converts a binary 16-bit number into a hex ASCII string
;
; call with AX = value to convert, ES:DI = address to store 4-character
; string.
;
hexasc: mov bx,ax ;save value here
mov cx,4 ;initialize character counter
mov dx,cx
hexasc1:
xchg cx,dx
rol bx,cl ;isolate next 4 bits
mov al,bl
and al,0fh
add al,'0' ;convert to ASCII
cmp al,'9' ;if 0-9
jbe hexasc2 ;then jump
add al,'A'-'9'-1 ;otherwise add offset for A-F
hexasc2:
stosb
xchg cx,dx
loop hexasc1
ret
; reserve space for rest of global variable buffer.
db buff_size - (offset $ - offset glovars) + 1 dup (0)
even
end_buff:
end
[LISTING TWO]
/*
* GVAR.C - program to test global device driver interface.
* Required structure and function definitions are in the file GVAR.H
*
* Sample make file.
*
* #
* # tcgvar.mak - make C gvar test program
* #
* gvar.exe: gvar.c gvar.h
* tcc -ms gvar
*
*/
#include <stdio.h>
#include <conio.h>
#include <fcntl.h>
#include <io.h>
#include "gvar.h"
void main (void) {
char buff[1024];
int fhandle;
if ((fhandle = _open ("$$GVAR$$", O_RDONLY)) == -1) {
puts ("Error: can't open global variables file");
return;
}
read (fhandle, &gvars, sizeof (char far *));
printf ("global table at %Fp\n", gvars);
_close (fhandle);
if (!set_gvar ("JIMSVAR", "Hi jim.!"))
puts ("Error setting variable JIMSVAR");
if (!get_gvar ("jimsvar", buff))
puts ("JIMSVAR not defined");
else
printf ("JIMSVAR=%s\n", buff);
set_gvar ("jimsvar","");
if (!get_gvar ("JIMSVAR", buff))
puts ("JIMSVAR not defined");
else
printf ("JIMSVAR=%s\n", buff);
}
[LISTING THREE]
/*
* gvar.h -- required definitions for C interface to global variable
* device driver.
*
* Copyright 1989, Jim Mischel
*/
/*
* structure of table returned by glovar read call.
*/
typedef struct {
void far *mem_buff; /* pointer to memory buffer */
/* function pointers */
int far pascal (far *set_var) (char far *, char far *);
int far pascal (far *get_var) (char far *, char far *);
void far pascal (far *flush_vars) ();
} GVAR_DEF;
GVAR_DEF far *gvars; /* global pointer to global variables structure */
/*
* function macros
*/
#define set_gvar (*gvars->set_var)
#define get_gvar (*gvars->get_var)
#define flush_gvars (*gvars->flush_vars)
[LISTING FOUR]
{*
* GVAR.PAS - Test global variable device driver.
*
* This program depends on the routines in TPGVAR.ASM to provide the interface
* to the device driver.
*
* Make sure the file tpgvar.obj is in the current directory
* (or Turbo's /Object directory) before compiling this program.
*
* Sample make file for compiling this program:
* #
* # tpgvar.mak - make pascal gvar test program
* #
* gvar.exe: gvar.pas tpgvar.obj
* tpc gvar
*
* tpgvar.obj: tpgvar.asm
* tasm tpgvar
*
*}
{$F+} { asm routines require far calls }
{$L tpgvar}
program test_globals;
type
varstr = string[255];
var
buff : varstr;
function gvar_init : boolean; external;
function set_gvar (varname : varstr; vardef : varstr) : boolean; external;
function get_gvar (varname : varstr; var vardef : varstr) : boolean; external;
procedure flush_gvars; external;
begin
if (not gvar_init) then begin
writeln ('Can''t open gvars file. Program aborted.');
halt;
end;
if (not set_gvar ('JIMSVAR', 'HELLO THERE'))
writeln ('Error setting variable JIMSVAR');
if (not get_gvar ('jimsvar', buff))
writeln ('JIMSVAR not defined')
else
writeln ('JIMSVAR=', buff);
trash := set_gvar ('jimsvar','');
if (not get_gvar ('jimsvar', buff))
writen ('JIMSVAR not defined')
else
writeln ('JIMSVAR=', buff);
end.
[LISTING FIVE]
page ,132
name tpgvar
title 'TPGVAR -- Turbo Pascal global variable interface routines'
;
; TPGVAR.ASM
; These routines provide the Turbo Pascal interface to the global variable
; device driver.
;
; The routines provided are:
;
; GVAR_INIT
; function gvar_init : boolean; external;
; Returns TRUE if successful, FALSE otherwise. MUST be called before using
; any of the other functions.
;
; SET_GVAR
; function set_gvar (varname : varstr; vardef : varstr) : boolean; external;
; Defines the variable 'varname' with the value 'varstr'. Returns FALSE
; if inserting this variable will over-run the global variables buffer.
;
; GET_GVAR
; function get_gvar (varname : varstr; var vardef : varstr) : boolean; external;
; Return the definition of the variable 'varname' in the string 'vardef'.
; Returns FALSE if 'varname' doesn't exist.
;
; FLUSH_GVARS procedure flush_gvars; external;
; Removes all global variables from the buffer.
;
; Copyright 1989, Jim Mischel
;
model tpascal
ideal
locals
;
; global variable interface structure
; A pointer to this type of structure is returned by the $$GVAR$$ device
; in response to a read call.
;
struc gvars
membuff_ptr dd ?
set_var_ptr dd ?
get_var_ptr dd ?
flush_vars_ptr dd ?
ends
CodeSeg
;
; This has to go into the code segment because 'TPascal' model won't allow
; initialization in the data segment.
;
gvar_filename db '$$GVAR$$',0 ;global variable device name
gvar_ptr dd ? ;pointer to global variables structure
glovar_name db 256 dup (?) ;passed to driver routines
glovar_def db 256 dup (?)
public GVAR_INIT
public SET_GVAR
public GET_GVAR
public FLUSH_GVARS
;
; open the $$GVAR$$ file and read the address of the header into gvar_ptr
; returns TRUE if successful, FALSE if not.
;
proc gvar_init uses ds
push cs
pop ds
mov dx,offset gvar_filename
mov ax,3d00h
int 21h ;open the file
jc @@error ;error opening file
push ax ;save file handle
mov bx,ax ;file handle in BX
mov dx,offset gvar_ptr
mov ah,3fh
mov cx,4
int 21h ;read address of gvar structure
pop bx ;file handle in BX
mov ah,3eh
int 21h ;close the file
mov ax,-1
jmp short @@done
@@error:
xor ax,ax
@@done: ret
endp gvar_init
;
; This macro converts the Turbo Pascal string in the 'from' parameter to an
; ASCIIZ string in the 'to' parameter.
;
macro @make_asciiz from, to
lds si,[&from&]
push cs
pop es
mov di,offset &to&
push cs ;these values are pushed now, but not
push di ;used until the gvar call below
call near ptr make_asciiz
endm
;
; Macro calls the specified routine.
;
macro @gvar_call routine
les bx,[cs:gvar_ptr]
call [dword ptr es:bx+gvars.&routine&]
endm
;
; Define 'varname' with value 'vardef'. Returns TRUE if successful, FALSE
; if the global variables buffer overflows.
;
proc set_gvar uses ds, varname:far ptr, vardef:far ptr
@make_asciiz varname, glovar_name
@make_asciiz vardef, glovar_def
@gvar_call set_var_ptr
ret
endp set_gvar
;
; Return definition of 'varname' in 'vardef'. Returns TRUE if successful,
; FALSE if 'varname' does not exist. If 'varname' does not exist, 'vardef'
; is set to the null string.
;
; CAUTION: if the returned variable definition is longer than 255 characters,
; many strange and not-so-wonderful things will happen.
;
proc get_gvar uses ds, varname:far ptr, vardef:far ptr
@make_asciiz varname, glovar_name
push cs
mov ax,offset glovar_def
push ax
@gvar_call get_var_ptr ;get the variable
push ax ;save return status
push cs ;now convert definition to TP string
pop ds
mov si,offset glovar_def
les di,[vardef]
push di ;save to set length
inc di ;start string at second byte
xor cx,cx ;cx is byte count
cld ;better safe than sorry
@@loop: lodsb
or al,al
jz @@done
stosb
inc cx ;bump length
jmp short @@loop
@@done: pop di ;restore pointer to length byte
mov [byte ptr es:di],cl ;and store length there
pop ax ;restore return status
ret
endp get_gvar
;
; flush all global variables
proc flush_gvars
@gvar_call flush_vars_ptr
ret
endp flush_gvars
;
; convert Turbo Pascal string into ASCIIZ string.
; Call with ds:si pointing to source string (TP format)
; es:di pointing to destination buffer
; Make sure this is accessed through a near call.
;
make_asciiz:
lodsb ;get length
mov cl,al
xor ch,ch ;in CX
cld
rep movsb ;move the string
mov [byte ptr es:di],0 ;and place the null terminator
retn
end