;From: bobs@access.digex.net (Bob Smith)
;Newsgroups: alt.lang.asm
;Subject: Re: Assembling to SYS

comment ^
In fact it is possible to have a device driver in .COM format (meaning that
the first two bytes are not 'MZ') which can execute from the DOS command
line as well.  For the sake of completeness, I'll explain how, but I suspect
you won't want to use this approach.

Note that the first four bytes of a device driver header are used as a link 
field to the next device driver in sequence.  However, DOS doesn't examine 
these bytes until the DD returns from its initialization call, so they can 
be set initially to whatever you like just as long as they are valid after 
the initialization call.  Of course, unless you have a nested DD, the first 
word should be set to -1.

Thus the first few bytes of the DD header can be a near (three byte) or 
short (two byte) jump over the rest of the DD header.  The disadvantage 
(and it's not a small one) of this combination is that all immediate 
displacement data references in the DD part of the file must be 
artificially reduced by 100h (the difference between the two file format's 
origins).  This makes it awkward (but not impossible) to share code between 
the two file formats (.COM file and DD).  Because of this complication, 
this mixed format beast is pretty much relegated to being written in 
assembler language.  All in all, it's much easier to write a mixed format 
program as an .EXE file.  So what if it won't run on DOS 2.x?

At the risk of overlengthening this message (and boring the socks off you), 
here's some sample code which creates a combination .COM file and device 
driver:
==================================================^
.286

DD_STR	struc

DD_NEXT  dd	-1		; Pointer to next device drive in chain
DD_ATTR  dw	?		; Attributes
DD_STRA  dw	?		; Offset of strategy routine
DD_INTR  dw	?		; Offset of interrupt routine
DD_NAME  db	'        '      ; Char device[0-7] = name,
				; Block device[0] = # units,
				;             [1-7] = undefined

DD_STR	 ends


SRH_STR  struc			; Status request header

SRH_LEN  db	?		; 00-00:  Length
SRH_UCD  db	?		; 01-01:  Unit code
SRH_CCD  db	?		; 02-02:  Command code
SRH_STA  dw	?		; 03-04:  Status
SRH_RES  db	8 dup (?)	; 05-0C:  Reserved area

SRH_STR  ends


VECTOR	 struc

VOFF	 dw	 ?		; Offset
VSEG	 dw	 ?		; Segment

VECTOR	 ends



CODE	segment dword public 'code'
	assume	cs:CODE,ds:CODE

	org	100h
START:
DRV_ATTR_CHAR equ     8000h	; Character device
DDHDR	DD_STR	<, DRV_ATTR_CHAR, \
		ENTER_STRA-100h,  \
		ENTER_INTR-100h,  \
		'FOO'>
L1:
	org	START

	jmp	short L1

	org	L1

; Put .COM file entry point code here.




	ret			; Return to DOS


	align	4
RH_VEC	VECTOR	<>

	assume	ds:nothing
ENTER_STRA:

; Put DD strategy entry point code here.
; For the DD code, all immediate displacement data references must
; be reduced by 100h as demonstrated by the next two instructions.

	mov	RH_VEC.VSEG-100h,es ; Save for later use
	mov	RH_VEC.VOFF-100h,bx ; ...

	retf			; Return to caller


ENTER_INTR:
	assume	ds:nothing

; Put DD interrupt entry point code here.

	pusha			; Save for a moment
	push	es		; ...

	les	bx,RH_VEC-100h	; ES:BX ==> request header
	assume	es:nothing	; Tell the assembler about it

	cmp	es:[bx].SRH_CCD,0 ; Izit initialization time?
	jne	short XINIT	; Jump if not

	mov	DDHDR.DD_NEXT.VOFF-100h,-1 ; Set to non-nested value
	mov	DDHDR.DD_NEXT.VSEG-100h,-1 ; ...

; Put DD initialization code here.




	jmp	EXIT

XINIT:

; Put DD non-initialization code here.




EXIT:
	pop	es		; Restore
	popa			; ...

	retf			; Return to caller

CODE	ends

	end	START


