Date: Mon, 15 Sep 1997 13:58:35 +1100 From: Bill Currie 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 Content-type: Multipart/Mixed; boundary=Message-Boundary-9630 Comments: Authenticated sender is Precedence: bulk --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--