delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1997/09/14/22:04:39

Date: Mon, 15 Sep 1997 13:58:35 +1100
From: Bill Currie <billc AT blackmagic DOT tait DOT co DOT nz>
Subject: Transfer buffer device driver
To: djgpp-workers AT delorie DOT com
Message-id: <199709150203.OAA17313@teleng1.tait.co.nz gatekeeper.tait.co.nz>
Organization: Tait Electronics Limited
MIME-version: 1.0
Comments: Authenticated sender is <billc AT blackmagic DOT tait DOT co DOT nz>

--Message-Boundary-9630
Content-type: text/plain; charset=US-ASCII
Content-transfer-encoding: 7BIT
Content-description: Mail message body

I suggested on Friday (nz) that maybe a device driver could be used 
to provide a common transfer buffer for djgpp programs.

Well, as soon as I posted my message, I went off and implemented it 
and the required changes to stub.asm.  The best (IMHO) feature of 
this mechanism is that ALL djgpp programs from 2.01 on get the 
benifit as there are NO changes to the library code needed* to use 
this feature (everything is handled automagically by the stub and 
driver). *-> the driver tb can be much larger than 64k and a slightly 
different call needs to be made to find out the true size of the tb 
(the driver maxes out the stub call to 0xfe00).

Even if my driver (and the stub mods) are not used, I did manage to 
make it so there was 40 bytes free in the stub. I found a couple of 
optimisations and I modified djasm to use sign extention whenever 
possible for offsets (eg mov ax,[bp+4]) and math instuctions (add 
bx,5). I also changed a `cannot' to a `can't' in the error messages.

Here's the source for the device driver (I called it djgpp.sys, but 
I'm not strongly attached to the name :) The patches for the stub 
will be in separate postings.

Enjoy

Bill
--
Leave others their otherness.


--Message-Boundary-9630
Content-type: text/plain; charset=US-ASCII
Content-transfer-encoding: 7BIT
Content-description: Text from file 'djgpp.asm'

		.type	"sys"

		.struct	FARPTR
offs	.dw
seg		.dw
		.ends

device_header:
dh.next_driver:
		.dd		0xffffffff
dh.attributes:
		.dw		0xc800					; just a `useless' :) character device
dh.strategy:
		.dw		strategy
dh.interrupt:
		.dw		interrupt
dh.name:
		.db		"DJGPP TB"				; dos has always allowed spaces, just not the utils
DRB		.struct FARPTR
inited:									; was initialization successfull?
		.db		0
tb_size:								; size of transfer buffer in paragraphs
		.dw		1024					; default to 16k
tb_segment:								; segment of transfer buffer
		.dw		0

strategy:
		mov		[cs:DRB.offs],bx
		mov		[cs:DRB.seg],es
		retf

		.struct	drb
plen	.db
unum	.db
cmd		.db
status	.dw
		.db 8 .dup
		.ends
interrupt:
		push	es
		push	bx
		les		bx,[cs:DRB]
		cmpb	[es:bx+drb.cmd],0x00		; init
		jne		?not_init
		call	init
		jmp		?exit
?not_init:
		testb	[cs:inited],0xff
		jz		?invalid_function
		cmpb	[es:bx+drb.cmd],0x0d		; open
		jne		?not_open
		call	open
		jmp		?exit
?not_open:
		cmpb	[es:bx+drb.cmd],0x0e		; close
		jne		?not_close
		call	close
		jmp		?exit
?not_close:
		cmpb	[es:bx+drb.cmd],0x03		; ioctl input(read)
		jne		?not_ioctl_read
		call	ioctl_read
		jmp		?exit
?not_ioctl_read:
		cmpb	[es:bx+drb.cmd],0x07		; input stuff (4-7)
		ja		?not_input_stuff
		cmpb	[es:bx+drb.cmd],0x04
		jb		?not_input_stuff
		call	input_stuff
		jmp		?exit
?not_input_stuff:
		; room for expansion
?invalid_function:
		movw	[es:bx+drb.status],0x8103
?exit:	; does not assume es:bx has survived to this point. if needed,
		; it can always be retrieved from DRB
		pop		bx
		pop		es
		retf

open:
close:
input_stuff:
		movw	[es:bx+drb.status],0x100	; done
		ret

		.struct	drbr
		.db drb .dup
		.db
buffer	.struct	FARPTR
count	.dw
		.ends

ioctl_read:
		; NOTE!!! Even when 4 bytes are requested, a 6 (SIX!!) byte buffer is
		; REQUIRED. However only 4 bytes will be modified (0,1 and 4,5) this
		; is to minimize the amount of code needed in the stub to use this
		; driver's service. The formats of the return buffer are:
		;	4 byte read:
		;		0	dw	transfer buffer size (maxed to 0xfe00)
		;		2	dw	untouched. data in this location will survive the
		;				IOCTL read call.
		;		4	dw	transfer buffer segment.
		;	6 byte read:
		;		0	dd	transfer buffer size
		;		4	dw	transfer buffer segment.
		;
		; Any other read size in invalid and will result in an error (?) or at
		; least a nop. (I'm not sure if DOS error checkes IOCTL calls).
		; Actually, only the low byte is checked for 4 byte reads (another
		; concession to byte pinching in the stub). However, the 6 byte read
		; must truely be 6 bytes.
		cmpb	[es:bx+drbr.count],4		; byte count must be 0xXX04
		je		?read_size_ok
		cmpw	[es:bx+drbr.count],6		; or 6 (to obtain true, 32 bit size)
		je		?read_size_ok
		movw	[es:bx+drb.status],0x810b	; read fault
		ret
?read_size_ok:
		movw	[es:bx+drb.status],0x0100	; done
		push	eax
		push	cx
		mov		cx,[es:bx+drbr.count]
		les		bx,[es:bx+drbr.buffer]		; transfer address
		; copy the segment to offset 4 nomatter what. This means that a 6 byte
		; buffer rather than a 4 byte buffer is required, but it makes things
		; easier when it comes to coping with the stub.  This is because
		; stubinfo_minkeep and stubinfo_ds_segment are separated by
		; stubinfo_ds_selector.  A selector can't be returned because dos
		; device drivers run in real mode, and there is no way we can call
		; dpmi services from real mode (int 0x31 is only available in
		; protected mode) and anyway, dpmi might no be the server in use and
		; the services of this driver are available to ALL programs, not just
		; djgpp.  Mind you, only djgpp programmers will actually know how to
		; use this driver (initially).
		mov		ax,[cs:tb_segment]
		mov		[es:bx+4],ax
		; Now return the size of the transfer buffer. If 4 bytes are being
		; read, it's the stub calling, so limit the size of the transfer
		; buffer and only write with ax, otherwise return the full size (only
		; 4 and 6 byte reads are valid) and write with eax
		xor		eax,eax
		mov		ax,[cs:tb_size]
		shl		eax,4
		cmp		cx,6
		je		?return_full_size
		; to make sure the tb size is `valid'. the stub and libc cannot handle
		; anything over 65535 bytes and 65024 (0xfe00) is a multiple of 512
		; which is best for optimising dos file transfers.
		cmp		eax,0xfe00
		jbe		?tb_size_legal
		; Lie about the size of the transfer buffer.  The application can
		; always use a six byte read to get the full size.
		mov		ax,0xfe00
?tb_size_legal:
		mov		[es:bx],ax
		les		bx,[cs:DRB]
		; Dos returns the number of bytes read in ax.  Lie and set ah to 0x3e
		; (close handle) so another 2 bytes can be saved in the stub. Pitty we
		; can't close the file in the driver (dos would crash :( )
		movb	[es:bx+drbr.count+1],0x3e
		pop		cx
		pop		eax
		ret
?return_full_size:
		mov		[es:bx],eax
		pop		cx
		pop		eax
		ret

		.align	16,0x90						; align to paragraph boundary using NOPs
		.struct drbi
		.db drb .dup
nunits	.db
end		.struct FARPTR
cmdln:
BPBptr	.struct FARPTR
drvnum	.db
errmsg	.db
		.ends

init:
		push	ax
		push	cx
		push	dx
		push	si
		push	ds

		; first of all, test to see if we're on a 386 or better, no point in
		; wasting memory that will never be used (DJGPP requires 386+).  Uses
		; the flags method of cpu detections. 8088/86 *ALWAYS* sets bits 12-15
		; of the flags register and the 286 *ALWAYS* clears them. Might fail
		; under certain memory managers/operating systems.
		pushf
		pop		ax
		and		ah,0x0f
		mov		ch,ah
		push	ax
		popf
		pushf
		pop		ax
		and		ah,0xf0
		cmp		ah,0xf0						; if all bits 12-15 set, 
		je		?not386						; oops, 8086/88
		mov		ah,ch
		or		ah,0xf0
		push	ax
		popf
		pushf
		pop		ax
		and		ah,0xf0						; if all bits 12-15 clear,
		jnz		?is386
?not386:									; oops, 286
		push	cs
		pop		ds
		mov		dx,not386
		mov		ah,9
		int		0x21
?error:
		movw	[es:bx+drb.status],0x810C	; general failure
		movb	[es:bx+drbi.nunits],0
		movw	[es:bx+drbi.end.offs],0
		mov		[es:bx+drbi.end.seg],cs
		movb	[es:bx+drbi.errmsg],1
		push	cs
		pop		ds
		mov		dx,errmsg
		mov		ah,9
		int		0x21
		jmpl	?exit
?is386:
		lds		si,[es:bx+drbi.cmdln]
		cld
?skip_name:
		lodsb
		cmp		al,'\r'
		je		?next_char
		cmp		al,'\n'
		je		?next_char
		cmp		al,0
		je		?setup
		cmp		al,' '
		jne		?skip_name
?next_char:
		lodsb
		cmp		al,'\r'
		je		?setup
		cmp		al,'\n'
		je		?setup
		cmp		al,0
		je		?setup
		cmp		al,' '
		je		?next_char
		cmp		al,'\t'
		je		?next_char
		cmp		al,'0'
		jb		?error
		cmp		al,'9'
		ja		?error
		mov		cx,ax
		mov		ax,[cs:tb_size]
		mov		dx,10
		mul		dx
		or		dx,dx
		jnz		?error
		and		cx,0xf
		add		ax,cx
		jc		?error
		mov		[cs:tb_size],ax
		jmp		?next_char
?setup:
		mov		ax,[cs:tb_size]
		mov		dx,cs
		mov		cx,0xa000
		cmp		dx,cx
		jb		?low_mem
		; These values aren't valid until msdos 5.0, but thats ok because
		; we won't ever be loaded above 640k until 5.0 (or equivalent).
		mov		cx,[es:bx+drbi.end.offs]
		add		cx,0xf
		shr		cx,4
		add		cx,[es:bx+drbi.end.seg]
?low_mem:
		add		dx,init/16		; init is paragraph aligned
		mov		[cs:tb_segment],dx
		add		dx,ax
		cmp		dx,cx
		jal		?error						; ok as 386 has been checked for
		movw	[es:bx+drb.status],0x0100	; done
		movb	[es:bx+drbi.nunits],1
		movw	[es:bx+drbi.end.offs],0
		mov		[es:bx+drbi.end.seg],dx
		movb	[es:bx+drbi.errmsg],0
		movb	[cs:inited],1
		push	cs
		pop		ds
		mov		dx,okmsg
		mov		ah,9
		int		0x21
		
?exit:
		movw	[es:bx+drbi.BPBptr.offs],0
		movw	[es:bx+drbi.BPBptr.seg],0
		pop		ds
		pop		si
		pop		dx
		pop		cx
		pop		ax
		ret

not386:
		.db		"386 or better needed.\r\n$"
errmsg:
		.db		"DJGPP transfer buffer not installed\r\n$"
okmsg:
		.db		"DJGPP transfer buffer installed\r\n$"

--Message-Boundary-9630--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019