1
0
mirror of synced 2026-01-13 15:27:51 +00:00
Matthieu Bucchianeri 53caec7569
Improvements to the SDPP driver. (#30)
* Mask unimplemented IOCTL message.

* Add build for 286+.

* Optimized IO for 8086.

* Adding pre-build drivers.
2025-01-03 18:40:38 -08:00

248 lines
9.4 KiB
NASM

PAGE 60, 132
TITLE HEADER - Interface for C Device Drivers
SUBTTL Bob Armstrong [23-Jul-94]
; header.asm - MSDOS device driver header...
;
; Copyright (C) 1994 by Robert Armstrong
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
; ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
; Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, visit the website of the Free
; Software Foundation, Inc., www.gnu.org.
; This module receives the DOS device driver calls (there are only two!)
; and sets up an environment suitable for executing C code. The original
; DOS device driver request is then handed off to the C code.
;
; There are two requirements that drive the memory layout of a device
; driver: (1) DOS requires the device header to be the first thing in the
; memory image, and (2) it is desirable to free the memory allocated to
; startup code once the driver has been initialized. These requirements
; force a memory layout like this diagram:
;
; DS, SS -> +----------------------+ <- driver load address
; | |
; | (device header) |
; | |
; \ _DATA \
; | |
; |----------------------|
; | |
; \ _BSS \
; | |
; | (stack) | <- C language stack
; | |
; CS -> |----------------------|
; | |
; \ _TEXT \
; | | <- driver break address
; | |
; \ (startup code) \
; | |
; +----------------------+
;
; This is very similar to the TINY (.COM file) model _except_ that the
; code is at the end of the memory image rather than the start. This
; turns out to be a major problem because the code generated by TURBO
; assumes that the CS register contains the start of the _TEXT segment
; and NOT the DGROUP. This is contrary to the documentation in the
; Borland manual and _I_ think it's a bug. Note that this bug is
; asymptomatic in .COM files because the _TEXT segment is normally the
; first thing in the DGROUP.
;
; To get around this problem we use the SMALL model (i.e. CS != DS).
; Trouble is, when this driver is loaded the only thing we know is the
; address of the start of the driver and that's in the CS register.
; This means that we have to calculate an appropriate CS value to use
; in the C code.
;
; Another unrelated issue is the stack size. The stack DOS uses when
; a driver is called has room for about 20 PUSHes. This isn't enough
; for any serious C code, so we only use the DOS stack to save all the
; registers. After that we switch to our own stack, which is located
; in the _BSS segment. Of course we have to put the DOS stack pointer
; back before returning.
;
; In order to ensure that the device header appears first in the load
; image it must be defined in this module. We also define the BIOS
; Parameter Block (BPB) and the table of BPB pointers in this module.
; These things are unfortunate because those parts of this file must be
; modified for each different driver.
;
; The only interface between this module and the C code is a single
; far pointer which must be exported by the C code. This pointer would
; be declared as:
;
; void (far *c_driver) (rh_t far *);
;
; The offset part (first word!) of this pointer must be initialized at
; link time with the offset (relative to _TEXT) of the routine that
; will handle DOS driver calls. The segment part of this pointer is
; filled in by this module with the CS value we compute before the
; first call. The C driver routine is passed a far pointer to the
; DOS request header. Everything else is up to the C code.
;
; Bob Armstrong [22-July-1994]
.8086
; This is the size of the new stack we create for the C code...
STACKSIZE EQU 256 ; use whatever seems appropriate...
; This DGROUP, and the order the segments appear in this module, will
; force the load order to be as described above!
DGROUP GROUP _DATA, _BSS, _TEXT
; The _DATA segment (initialized data) for this module contains the
; MSDOS device header and the BIOS parameter blocks...
_DATA SEGMENT WORD PUBLIC 'DATA'
PUBLIC _header, _bpb, _bpbtbl
; Header attribute bits for block devices:
;
; 0002H (B1) - 32 bit sector addresses
; 0040H (B6) - Generic IOCTL, Get/Set logical device
; 0080H (B7) - IOCTL Query
; 0800H (B11) - Open/Close device, Removable media
; 2000H (B13) - IBM format
; 4000H (B14) - IOCTL read/write
; 8000H (B15) - zero for block device!
; The DOS device header...
_header DD -1 ; link to the next device
DW 00C0H ; block device, non-IBM format, generic IOCTL
DW DGROUP:STRATEGY ; address of the strategy routine
DW DGROUP:INTERRUPT; " " " interrupt "
DB 1 ; number of drives
DB 'SDCDv12' ; DOS doesn't really use these bytes
; The geometry (sectors/track, tracks/cylinder) defined in the BPB is rather
; arbitrary in the case of the TU58, but there are things to watch out for.
; First, the DOS FORMAT program has a bug which causes it to crash or worse
; yet, exit silently without doing anything for devices with large numbers of
; sectors per track. Experiments show that 64 sectors/track is safe (at least
; for DOS v5) but 128 is not. Second, the DRIVER.C module must calculate the
; number of cylinders by cylinders = device_size / (sectors * heads). This
; value must always come out to an integer.
; The BIOS Parameter Block...
_bpb DW 512 ; sector size
DB 1 ; cluster size
DW 1 ; number of reserved sectors
DB 2 ; number of FAT copies
DW 48 ; number of files in root directory
DW 512 ; total number of sectors
DB 0F0H ; media descriptor
DW 2 ; number of sectors per FAT
DW 64 ; sectors per track
DW 2 ; number of heads
DD 0 ; number of hidden sectors
DD 0 ; large sector count
; This table contains one BPB pointer for each physical drive...
_bpbtbl DW DGROUP:_bpb ; the BPB for unit 0
; DW DGROUP:_bpb ; " " " " 1
; This doubleword points to the entry point of the C driver code...
EXTRN _c_driver:WORD
_DATA ENDS
; The _BSS segment contains uninitialized static data...
_BSS SEGMENT WORD PUBLIC 'BSS'
PUBLIC _STACK
; Local variables for this module...
RHPTR DW 2 DUP (?) ; offset and segment of the request header
OLDSTK DW 2 DUP (?) ; original stack offset and segment
; This is the stack while the C code is running...
DB STACKSIZE DUP (?)
_STACK LABEL WORD ; the address of a stack is at its _top_ !
_BSS ENDS
; WARNING! The _TEXT segment must be paragraph aligned!
_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:NOTHING
; These words give the offsets of the _TEXT segment and the stack, both
; relative to the DGROUP. Note that you can't define use "DGROUP:_TEXT"
; as this generates a segment relocation record in the .EXE file which
; will prevent you from converting to a .COM file. The way its written
; works, but assumes that it is the first thing in this module.
CSOFF DW DGROUP:CSOFF
NEWSTK DW DGROUP:_STACK
; This is the entry for the STRATEGY procedure. DOS calls this routine
; with the address of the request header, but all the work is expected
; to occur in the INTERRUPT procedure. All we do here is to save the
; address of the request for later processing. Since this function is
; trivial, we never call any C code...
PUBLIC STRATEGY
STRATEGY PROC FAR
MOV CS:RHPTR,BX ; save the request header offset
MOV CS:RHPTR+2,ES ; and its segment
RET ; that's all there is to do
STRATEGY ENDP
; And now the INTERRUPT routine. This routine has to save all the
; registers, switch to the new stack, set up the segment registers,
; and call the C language driver routine while passing it the address
; of the request header (saved by the strategy routine).
PUBLIC INTERRUPT
INTERRUPT PROC FAR
PUSH DS ES AX BX CX DX DI SI BP
CLI ; interrupts OFF while the stack is unsafe!
MOV CS:OLDSTK+2,SS ; save the current stack pointer
MOV CS:OLDSTK,SP ; ...
MOV BX,CS ; then setup the new stack
MOV SS,BX ; ...
MOV SP,NEWSTK ; ...
STI ; interrupts are safe once again
MOV AX,CSOFF ; compute the correct code segment address
SHR AX,4 ; ... for the _TEXT segment
ADD AX,BX ; ...
MOV _c_driver+2,AX ; fix the pointer appropriately
MOV DS,BX ; setup DS for the C code
CLD ; the C code will assume this state
PUSH CS:RHPTR+2 ; pass the request header
PUSH CS:RHPTR ; address as a parameter
CALL DWORD PTR _c_driver; call the C entry point
CLI ; interrupts OFF once again
MOV SS,CS:OLDSTK+2 ; and restore the original stack
MOV SP,CS:OLDSTK ; ...
STI ; ...
POP BP SI DI DX CX BX AX ES DS
RET
INTERRUPT ENDP
_TEXT ENDS
END