home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
ddjmag
/
ddj8709.arc
/
KLEINLST.SEP
< prev
next >
Wrap
Text File
|
1987-08-13
|
16KB
|
618 lines
Listing One
; prndrv.asm
; printer driver startup code
; ms/pc-dos 2.x, 3.x installable device driver
;copyright (c) Andy Klein 1987
PRIVATE_STACK_SIZ equ 256 ;private stack size, probably much larger
; than necessary
IS_CHAR_DEV equ 32768 ;bit to set for this driver
codeseg segment para public 'CODE'
codeseg ends
dataseg segment para public 'DATA' ; define first
dataseg ends
assume cs:codeseg, ds:dataseg, es:dataseg, ss:dataseg
codeseg segment para public 'CODE'
org 0 ;drivers are always org'ed at 0
; with the device header first
public prn_driver, dev_strategy_, dev_interrupt_
prn_driver proc far ;drivers invoked as far calls by DOS
;device header starts here
next_dev db 4 dup(255) ;no next driver in this file, need a
;(long)-1 which is 4 bytes of 255
attribute dw IS_CHAR_DEV
strategy dw dev_strategy_ ;device strategy entry point
interrupt dw dev_interrupt_ ;device interrupt entry point
dev_name db 'PRN '
;end of the device header
;code segment variables, these will be addressable before we setup
; our data and stack segment
req_hdr_seg dw (0) ; pointer to request header, segment part
req_hdr_off dw (0) ; pointer to request header, offset part
caller_ss dw (0) ; caller's ss
caller_sp dw (0) ; caller's sp
;strategy - this strategy function saves a long pointer to a
; request header in code segment variables req_hdr_seg and
; req_hdr_off.
; The address of the request header is passed in es:bx
dev_strategy_:
mov word ptr cs:req_hdr_off,bx ;save request header pointer
mov word ptr cs:req_hdr_seg,es ; in code segment variables
ret ; ...and back to dos
public $cswt, $begin ;keep Aztec linker happy, all Aztec C compiled
; functions have a reference to these 2 functions
; which normally drag in the startup code
$cswt proc near
$cswt endp
$begin proc near
$begin endp
extrn driver_functions_:near
;interrupt - not a true interrupt handler (it ends with a ret as opposed
; to an iret), this function recieves no parameters. Instead it
; uses the information stored in the request header we recieved and
; saved a pointer to in the strategy function to determine what to do.
dev_interrupt_:
;STEP 0
; Preserve machine state
cli ; no interrupts when dealing with machine state
push ds ; save machine state on caller's stack
push es
push ax
push bx
push cx
push dx
push si
push di
push bp
pushf
mov cs:caller_ss,ss ; save caller stack frame (ss and sp)
mov cs:caller_sp,sp
sti ;interrupts OK
;Now the real work starts ...
;STEP 1
; Get the driver's segment into the ax register
; This begins our task of establishing addressability
mov ax,cs
;STEP 2
; Copy the request header stored at req_hdr_seg:req_hdr_off
; into our private and C addressable request header
mov si,cs:req_hdr_off ;load up the source string segment:offset
mov bx,cs:req_hdr_seg ; into ds:si registers
mov ds,bx
mov es,ax ;load up the destination string segment:offset
mov di,offset cs:driver_rh_ ; into es:di registers
cld ;set direction flag to increment si and di
xor cx,cx ;clear out cx
mov cl,[si] ;first byte of request header is its length
rep movsb ; ... and copy it ...
;STEP 3
; This step establishes addressability of data segment
; and sets up driver's stack frame.
; It also sets register bp equal to sp, a state required
; for calling the first C function
; Remember that register ax contains the driver segment
; We will set ds, es, ss all equal to cs ... 8080 model??
mov bx,offset c_stack_top ;this will go into sp
cli ;no interrupts while we mess with these registers
mov ds,ax
mov es,ax
mov ss,ax ;now establish our stack frame
mov sp,bx
mov bp,bx ;bp = sp this is critical for C
sti ;ok for an interrupt to occur
;now we have set up the environment for C
;STEP 4
; Call our first C function,
; passing the command code from the request header as
; a parameter. (void)driver_functions(cmd_code);
xor ax,ax ;clear out ax
mov bx,offset driver_rh_ ;bx points to our request header
mov al,byte ptr [bx + 2] ;3rd byte of request header is command code
push ax ;to pass parameter(s) to a C function,
call driver_functions_ ; push it(them) on stack and call function
pop ax ;caller must remove parameter(s) from stack
;STEP 5
; At this point we are done with the function our driver
; was to perform. Now we must copy our private request header
; back into the original request header DOS gave us a pointer
; to back in the strategy function.
mov es,cs:req_hdr_seg ;load address for orig RH into es:di
mov di,cs:req_hdr_off
mov si,offset driver_rh_ ;ds:si our (updated) copy
cld ;set direction to increment
xor cx,cx ;clear out cx
mov cl,[si] ;length is at first byte
rep movsb ; ... and copy it
;STEP 6
; Restore machine state saved in STEP 0
; and return to DOS
cli ;machine state restore should not be interrupted
mov ax,cs:caller_ss ;switch to caller's stack frame
mov ss,ax
mov ax,cs:caller_sp
mov sp,ax
popf ; ... pop all of it ...
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
sti ;enable interrrupts
ret ; ... back to dos and bye bye
prn_driver endp ;end of driver code
codeseg ends
;Data segment, contains our local stack space
; and C addressable copy of request header
dataseg segment para public 'DATA'
public c_stack_top
db PRIVATE_STACK_SIZ dup (0)
c_stack_top label word
public driver_rh_
driver_rh_ db 32 dup (0) ;C addressable copy of request header
dataseg ends
END
Listing Two
/** rh.h
Device Driver Request Header structure definition
and status word #define's
copyright (c) 1987 Andy Klein
**/
typedef struct
{
unsigned char length, /* length of header */
unit, /* unit code ... which unit to use */
cmd; /* command to execute */
unsigned int status; /* status of operation */
unsigned char reserved[8],
media_type; /* media descriptor byte, block dev only */
unsigned int xfer_buf_offset,
xfer_buf_segment,
xfer_count;
char dummy[32 - 20];
/* data for operation */
} request_hdr;
#define ERROR_MASK 32768
#define BUSY_MASK 1024
#define DONE_MASK 512
#define WRITE_PROTECTED 0x00
#define UNKNOWN_UNIT 0x01
#define DEV_NOT_READY 0x02
#define UNKNOWN_CMD 0x03
#define CRC_ERROR 0x04
#define BAD_DRIVE_REQ_LEN 0x05
#define SEEK_ERROR 0x06
#define UNKOWN_MEDIA 0x07
#define SECTOR_NOT_FOUND 0x08
#define PRN_NO_PAPER 0x09
#define WRITE_FAULT 0x0A
#define READ_FAULT 0x0B
#define GENERAL_FAILURE 0x0C
#define INVALID_DISK_CHG 0x0F
Listing Three
/** drvfunc.c
Device Driver for DOS 2.x, 3.x
This is the function table that is called once
the interrupt function has established addressability
copyright (c) 1987 Andy Klein
**/
#define LAST_FUNCTION 15
#define Q_FUNCTIONS LAST_FUNCTION + 1
extern void init(), output_status(), output(), output_flush(),
output_verf(), bad_cmd();
/**
function_table is an array of pointers to functions
returning nothing (void). It is analogous to a jump table
in assembler.
**/
void (* function_table[Q_FUNCTIONS])() = {
/* 0 */ init,
/* 1 */ bad_cmd, /* media check */
/* 2 */ bad_cmd, /* build bpb */
/* 3 */ bad_cmd, /* ioctl input */
/* 4 */ bad_cmd, /* input */
/* 5 */ bad_cmd, /* input no wait */
/* 6 */ bad_cmd, /* input status */
/* 7 */ bad_cmd, /* input_flush */
/* 8 */ output,
/* 9 */ output_verf,
/* 10 */ output_status,
/* 11 */ output_flush,
/* 12 */ bad_cmd, /* ioctl output */
/* 13 */ bad_cmd, /* open device */
/* 14 */ bad_cmd, /* close device */
/* 15 */ bad_cmd /* removable_media */
};
void
driver_functions(cmd)
int cmd;
{
if ( cmd > LAST_FUNCTION )
(void) bad_cmd();
else
(void) (* function_table[cmd])();
}/* driver_functions() */
Listing Four
/** error.c
Error handler for printer driver
copyright (c) 1987 Andy Klein
**/
#include "rh.h"
extern request_hdr driver_rh;
void
bad_cmd()
{
driver_rh.status = ERROR_MASK | UNKNOWN_CMD;
}
#define TIME_OUT 1
#define IO_ERR 8
#define NO_PAPER 32
#define BUSY 128
void
driver_error(stat)
int stat;
{
int err_code;
if ( stat & ERROR_MASK )
stat ^= ERROR_MASK;
switch ( stat )
{
case TIME_OUT: err_code = DEV_NOT_READY;
break;
case IO_ERR: err_code = GENERAL_FAILURE;
break;
case NO_PAPER: err_code = PRN_NO_PAPER;
break;
case BUSY: err_code = DEV_NOT_READY;
break;
default: err_code = GENERAL_FAILURE;
break;
}
driver_rh.status = ERROR_MASK | err_code;
}/* driver_error() */
Listing Five
/** init.c
Printer driver initialization
copyright (c) 1987 Andy Klein
**/
#include "rh.h"
extern request_hdr driver_rh;
char *title = "\nPrinter Device Driver for Okidata 92";
char *copyw = "copyright 1987 (c) Andy Klein\n";
void
init()
{
extern unsigned _Uend;
/* _Uend is inserted by the Aztec linker */
unsigned show_cs();
void reset_printer(), initialize_oki92();
puts(title);
puts(copyw);
(void) reset_printer();
(void) initialize_oki92();
/* set ending address of driver, set status word */
driver_rh.xfer_buf_segment = show_cs();
driver_rh.xfer_buf_offset = (unsigned int)&_Uend;
driver_rh.status = DONE_MASK;
} /* init() */
#define ELITE_FONT 28
void
initialize_oki92()
{
char_2_prn(ELITE_FONT);
}/* initialize_oki92() */
Listing Six
; show_cs.asm
; c call is (unsigned) show_cs();
; Returns contents of cs register
;copyright (c) 1987 Andy Klein
include lmacros.h
procdef show_cs
mov ax, cs
pret
pend show_cs
finish
Listing Seven
/** output.c
Printer driver output functions
copyright (c) 1987 Andy Klein
**/
#include "rh.h"
extern request_hdr driver_rh;
void
output()
{
int stat;
unsigned xfer_off, bytes_xferd;
register char outch;
char fetch_char();
xfer_off = driver_rh.xfer_buf_offset;
for ( bytes_xferd = 0; bytes_xferd < driver_rh.xfer_count;
++bytes_xferd, ++xfer_off )
{
outch = fetch_char(driver_rh.xfer_buf_segment, xfer_off);
if ( (stat = char_2_prn(outch)) )
{
(void) driver_error(stat);
break;
}
}
driver_rh.xfer_count = bytes_xferd;
if ( ! stat )
driver_rh.status = DONE_MASK;
else
driver_rh.status = stat;
}/* output() */
void
output_verf()
{
(void) output();
}/* out_verf() */
void
output_status()
{
/**
if device is currently doing an operation,
driver_rh.status = BUSY_MASK & DONE_MASK;
else if a write can begin immediately,
driver_rh.status = 0 & DONE_MASK;
**/
if ( printer_busy() )
driver_rh.status = BUSY_MASK & DONE_MASK;
else
driver_rh.status = DONE_MASK;
}/* output_status() */
void
output_flush()
{
/**
terminate all pending requests,
meaningless for this device
**/
driver_rh.status = DONE_MASK;
}/* output_flush() */
Listing Eight
; prlport.asm
; low level parallel port output functions
;copyright (c) Andy Klein 1987
include lmacros.h ;Aztec C macros for assembly language functions
; for other environments replace with calling
; conventions for your complier
ERROR_MASK equ 32768
PRINTER_INT equ 017H
PRINTER_ID equ 0 ;lpt1 = 0, lpt2 = 1, lpt3 = 2
;printer commands, put into ah
PRINT_CHAR equ 0
INIT_PRN equ 1
READ_STAT equ 2
;printer status byte error codes, returned in ah
TIME_OUT equ 1
IO_ERR equ 8
NO_PAPER equ 32
BUSY equ 128
PRINTER_FAILURE equ TIME_OUT+IO_ERR+NO_PAPER
;C call is (void) reset_printer();
;no value returned
procdef reset_printer
mov ah,INIT_PRN
mov dx,PRINTER_ID
sti
int PRINTER_INT
xor ax,ax
pret
pend reset_printer
;C call is printer_busy();
;returns 0 (FALSE) if not busy, nonzero (TRUE) if busy
procdef printer_busy
mov ah,READ_STAT
mov dx,PRINTER_ID
sti
int PRINTER_INT
and ah,BUSY
jz printer_is_busy
xor ax,ax
pret
printer_is_busy:
mov ax,1
pret
pend printer_busy
; C call is (char) fetch_char(segm, offs);
; gets the character at segm:offs and
; returns it
procdef fetch_char,<<segm,word>,<offs,word>>
push ds
mov bx,offs
mov ds,segm
mov al,byte ptr ds:[bx]
and ax,0ffH
pop ds
pret
pend fetch_char
;C call is char_2_prn(outch);
; prints outch on PRINTER_ID
; returns 0 if ok, error code
procdef char_2_prn,<<outch,byte>>
mov al,outch
mov ah,PRINT_CHAR
mov dx,PRINTER_ID
sti
int PRINTER_INT
test ah,PRINTER_FAILURE
jnz printer_has_failed
xor ax,ax
pret
printer_has_failed:
mov al,ah ;result code in ah, mov it to al
and ax,PRINTER_FAILURE
pret
pend char_2_prn
finish