/**************************************************************\*                                                              **      BIOS Hard Disk Controller for QNX                       **                                                              ** Copyright (c) Quantum Software Systems Ltd. 1988 UNPUBLISHED **                                                              *\**************************************************************//* * Define driver entry codes */#define READ_BLK		0#define WRITE_BLK		1#define FORMAT			2#define TYPE_HARD  		1#define HARD_DISK_EVENT	6/*-- Flag bits for extended disk table --*/#define DISK_LARGE_EXTENTS	0x0001#define DISK_LARGE_BLOCKS	0x0002#define DISK_LARGE_FSYS		0x0004#define DISK_NEEDS_FLUSH	0x0008#asm "TYPE_HARD 	= 1"/* *	Header Information (16 bytes)" *	NOTE: This must be the first file linked */#asm "		seg 1"#asm "		export <hard_disk>"#asm "<hard_disk>:"#asm "	w	data	TYPE_HARD"#asm "	w	data	<Disk_init>"#asm "	w	data	<Blk_read>"#asm "	w	data	<Blk_write>"#asm "	w	data	<Format>"#asm "	w	data	<Multi_sector>"#asm "	w	data	<Control>"#asm "		rmb 2"/* *	default disk table parameters (16 bytes)" */#asm "b data TYPE_HARD"#asm "b data 0"									/* physical drive */#asm "w data 0"									/* block offset */#asm "w data 0"									/* sectors/disk */#asm "w data 0"									/* sectors/cylinder */#asm "b data 0"									/* sectors/track */#asm "b data 1"									/* Sector base */#asm "w data 0"									/* I/O Port */#asm "b data 6"									/* Interrupt on slave 8259 */#asm "w data 0"									/* number of tracks */#asm "b data 0"									/* Control field *//* *	Extended default disk table parameters (16 more bytes)" */#asm "w data 0"				/* Top word of offset */#asm "w data 0"				/* Top word of size */#asm "w data 0"				/* write_precomp */#asm "w data 0"				/* Number of pages */#asm "w data 0"				/* Blks per page */#asm "w data 0"#asm "w data 000bh"			/* Flags, flush, request large blocks and large xtnts */#asm "w data 1234h"			/* Signature for extension */#asm " seg 3"/* * The next three structures are in fsys and must be referenced off the * extra segment. */struct disk_entry {	char disk_type;	char disk_drv;	unsigned blk_offset;	unsigned num_sctrs;	unsigned sctr_cyl;	char sctr_trk;	char sctr_base;	unsigned ctl_addr;	char ctl_int;	unsigned num_tracks;	char disk_filler[1];	} ;struct ext_disk_entry {	unsigned ext_blk_offset;	unsigned ext_num_sctrs;	unsigned write_precomp;	unsigned disk_arg[3];	unsigned disk_flags;	unsigned disk_entry_id;	} ;/* * A block relative to a QNX partition is decomposed into the * following structure. */struct bdata_entry {	long block;			/* Relative to start of disk 1...n		*/	unsigned drive;		/* Physical drive 0 or 1				*/	unsigned track;		/* Not needed by this controller		*/	unsigned head;		/* Not needed by this controller		*/	unsigned sector;	/* Not needed by this controller		*/	unsigned priority;	/* Priorty to wait while in BIOS		*/	} ;struct disk_size_entry {	unsigned sectors_per_cyl;	unsigned sectors_per_trk;	unsigned sector_base;	} ;/* * Two structures usefull for getting at pieces of 16/32 bit values. */struct {	unsigned char wlow;	unsigned char whigh;	} ;struct {	unsigned llow;	unsigned lhigh;	} ;struct disk_entry *disktab;			/* Pointer into FSYS off ES	*/struct ext_disk_entry *extdisktab;	/* Pointer into FSYS off ES	*/struct activity_entry *acttab;		/* Pointer into FSYS off ES	*/struct bdata_entry  bdata;			/* These can NOT be local variables	*/int large_fsys;char atp;char tempb;char initialized[2];struct disk_size_entry disk_size[2];/* Used to break BLK -> TRK, HEAD, SECTOR */unsigned  drive, track, head, sector;/* * Initialize the hard disk controller. * This is called when the disk is mounted.  It may be called again so * only initalize the hardware on the first call for a physical drive. * dtab		- Pointer to the base of fsys's disk table. * index	- Which entry in the table to use (QNX drive - 1). * edtab	- Pointer to the base of fsys's extended disk table. */disk_init( dtab, index, edtab, atab )	struct disk_entry *dtab;	unsigned index;	struct ext_disk_entry *edtab;	struct activity_entry *atab;	{	register struct disk_entry *dp;	register struct disk_param_table *tp;	struct ext_disk_entry *edp;	long l;	dp = disktab = dtab;		/* Save in disktab for later calls		*/	dp += index;	edp = extdisktab = edtab;	/* Save in extdisktab for later calls	*/	edp += index;	acttab = atab;				/* Save in acttab for later calls		*/	if(task_info(0, 6) >= 300)	/* Protected mode QNX 3.0	*/		return(-1);/* * If O/S does not support large FSYS, * turn off flag bits for large extents and large blocks as well * just in case some utilities don't check the large fsys bit first. */	if( !(task_info(0, 4) & 0x0080) ) {		large_fsys = 0;		edp-}disk_flags = 0;		}	else		large_fsys = 1;	if ( !initialized[0]  &&  !initialized[1] ) {		attach( HARD_DISK_EVENT );/*		init_int();*/		}	if ( initialized[ drive = dp-}disk_drv & 0x01 ] ) return( 0 );	/*	 * If the user does not specify h=,t=,n= when using MOUNT we will	 * ask the BIOS.	 */	if(dp-}sctr_cyl == 0) {		asm("mov ah,#0");		asm("mov dl,<drive>");		asm("or dl,#80h");		asm("int 13h");			/* Reset disk system		*/		asm("mov ah,#11h");		asm("mov dl,<drive>");		asm("or dl,#80h");		asm("int 13h");			/* Recalibrate				*/		asm("mov ah,#8");		asm("mov dl,<drive>");		asm("or dl,#80h");		asm("int 13h");			/* Get disk parameters		*/		asm("b mov <head>,dh");		asm("b mov <track>,ch");		asm("b mov <sector>,cl");		asm("b and <sector>,#3fh");		asm("shl cx");		asm("shl cx");		asm("and ch,#3");		asm("b mov <track>+1,ch");		++track;		++head;		dp-}sctr_trk = sector;		dp-}sctr_cyl = head * sector;		dp-}num_tracks = track;		}	if ( dp-}num_sctrs == 0  &&  edp-}ext_num_sctrs == 0 ) {		l = ((long) track) * head * sector;		dp-}num_sctrs = l.llow;		edp-}ext_num_sctrs = l.lhigh;		}	disk_size[drive].sectors_per_cyl = dp-}sctr_cyl;	disk_size[drive].sectors_per_trk = dp-}sctr_trk;	disk_size[drive].sector_base = dp-}sctr_base;	initialized[drive] = 1;	}/* * Read a block (512 bytes) from the disk. * drv	- QNX drive number 1 to 7. * blk	- Relative to partition start 1..n. * seg	- Segment to put the data. * off	- Offset within the segment. */blk_read( drv, blk, off, seg )	unsigned drv, off, seg;	long blk;	{	if(map_blk(drv, blk, &bdata))		return(-1);	map_thn(&bdata);	return(r_w_blk(READ_BLK, &bdata, off, seg, 1));	}/* * Write a block (512 bytes) from to disk. * drv	- QNX drive number 1 to 7. * blk	- Relative to partition start 1..n. * seg	- Segment to get the data. * off	- Offset within the segment. */blk_write( drv, blk, off, seg )	unsigned drv, off, seg;	long blk;	{	if(map_blk(drv, blk, &bdata))		return(-1);	map_thn(&bdata);	return(r_w_blk(WRITE_BLK, &bdata, off, seg, 1));	}/* * Transfer n blocks (512 bytes) from the disk. * drv		- QNX drive number 1 to 7. * blk		- Relative to partition start 1..n. * seg		- Segment to put the data. * off		- Offset within the segment. * rw		- Read=0/Write=1 transfer. * nblks	- Number of blocks to transfer */multi_sector(drv, blk, off, seg, rw, nblks)	unsigned drv, off, seg, rw, nblks;	long blk;	{	unsigned stat;	if(map_blk(drv, blk, &bdata))		return(-1);	map_thn(&bdata);	if(nblks > 255) /* limit I/O to 255 blocks */		nblks = 255;	stat = r_w_blk(rw, &bdata, off, seg, nblks);	if(stat == 0)		return(nblks);	return(-1);	}/* * Format a single track on the disk. * drv			- QNX drive number 1 to 7. * trk			- Track (same as cylinder) to format. * hd			- Head to format on this cylinder. * sctrs		- Number of sectors to lay down. * interleave	- Pointer to an array of sector numbers. */format( drv, trk, hd, sctrs, interleave )	unsigned drv, trk, hd, sctrs;	char *interleave;	{	/* We don't let you format the drive! */	return( -1 );	}/* * A control function to the driver. * func			- Function code. *					1 - Flush activity timeout. *					2 - Flush file. *					3 - Flush all. * drv			- QNX drive number 1 to 7. * off			- Offset of a data structure in the file system. */control(func, drv, off)unsigned func, drv, off;	{	return( -1 );	}r_w_blk( rw, bdata_ptr, off, seg, nblks )	unsigned rw, off, seg, nblks;	struct bdata_entry *bdata_ptr;	{	register struct bdata_entry *bp = bdata_ptr;	/* In register DI */	unsigned stat, trys = 2;	if(bp->priority)		set_priority(bp->priority);	do {		asm("push es");		asm("push bp");		asm("push di");		asm("mov dl,4[di]");		asm("or dl,#80h");		asm("mov dh,8[di]");		asm("mov ax,6[di]");		asm("mov ch,al");			/* Bottom 8 bits of track	*/		asm("shr ax");		asm("shr ax");		asm("and al,#0c0h");		asm("mov cl,10[di]");		asm("or cl,al");			/* Top 2 bits of track		*/		asm("mov al,22[bp]");		asm("mov es,20[bp]");		asm("mov bx,18[bp]");		asm("mov ah,14[bp]");		asm("add ah,#2");		asm("int 13h");		asm("pop di");		asm("pop bp");		asm("pop es");		asm("mov <tempb>,ah");		/* Return status			*/		stat = tempb;		} while( stat  &&  --trys );	if(bp->priority)		set_priority(3);	return( stat );	} /* * Block until a disk interrupt occurs. * When the interrupt occurs, return with status. */wait_results() {	await( HARD_DISK_EVENT );	return( 0 );	}/* * Convert block number relative to the start of a QNX partition * to a physical location on the disk. * * Rely on the fact that the extra-segment points to the * data segment of the file system. */map_blk( drv, blk, bdata_ptr )	long blk;	unsigned drv;	struct bdata_entry *bdata_ptr;	{	register struct bdata_entry *bp = bdata_ptr;	register struct disk_entry *dp;	register struct ext_disk_entry *edp;	long l;	--drv;	/* QNX drives start at one, table starts at zero */	dp = disktab + drv;	edp = extdisktab + drv;	if(dp-}disk_type != TYPE_HARD)	return(-1); 	if(large_fsys == 0) /* O/S with small fsys gives garbage in top word */		blk &= 0x0000ffffL;	/* Limit blk to lie within this partition */	l.llow = dp-}num_sctrs;	l.lhigh = edp-}ext_num_sctrs;	if(blk <= 0  ||  blk > l)		return(-1);	bp->block.llow = dp-}blk_offset;	bp->block.lhigh = edp-}ext_blk_offset;	bp->block += blk - 1;	bp->drive = dp-}disk_drv & 0x01;	bp->priority = edp-}disk_arg[0];	return(0);	}/* * Now that we have the physical block from the start of the disk * (blk 0) we can break it into a track,head and sector. */voidmap_thn( bdata_ptr )struct bdata_entry *bdata_ptr;	{	register struct bdata_entry *bp = bdata_ptr;					/* In DI */	register struct disk_size_entry *sp = &disk_size[bp->drive];	/* In SI */	asm("mov ax,0[di]");			/* Get absolute blk (from 0)		*/	asm("mov dx,2[di]");	asm("div 0[si]");				/* Divide by sectors/cylinder		*/	asm("mov 6[di],ax");			/* Save away track					*/	asm("mov ax,dx");				/* Now div remainder to get head	*/	asm("xor dx,dx");	asm("div 2[si]");				/* Divide by sectors/track			*/	asm("mov 8[di],ax");			/* Save away head					*/	asm("add dx,4[si]");			/* Add sector base to remainder		*/	asm("mov 10[di],dx");			/* Save away sector					*/	} /********************************************************** *              Support routines                           * **********************************************************/#asm"	seg 1"#asm "<Disk_init>:"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 22[bp]"#asm "		push 20[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <disk_init>"#asm "		add sp,#8"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "<Blk_write>:		"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 22[bp]"#asm "		push 20[bp]"#asm "		push 24[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <blk_write>"#asm "		add sp,#10"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "<Blk_read>:		"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 22[bp]"#asm "		push 20[bp]"#asm "		push 24[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <blk_read>"#asm "		add sp,#10"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "<Format>:		"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 24[bp]"#asm "		push 22[bp]"#asm "		push 20[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <format>"#asm "		add sp,#10"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "<Multi_sector>:"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 26[bp]"#asm "		push 24[bp]"#asm "		push 22[bp]"#asm "		push 20[bp]"#asm "		push 28[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <multi_sector>"#asm "		add sp,#14"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "<Control>:"#asm "		call	prologue"#asm "		push ds"#asm "		push es"#asm "		mov ax,ds"#asm "		mov es,ax"#asm "		mov ds,cs:0"#asm "		push 20[bp]"#asm "		push 28[bp]"#asm "		push 18[bp]"#asm "		push 16[bp]"#asm "		call <control>"#asm "		add sp,#8"#asm "		pop	es"#asm "		pop ds"#asm "		jmp	Epilogue"#asm "prologue:"#asm "		pop	ax		;save return address in ax"#asm "		push bp"#asm "		push bx"#asm "		push cx"#asm "		push dx"#asm "		push si"#asm "		push di"#asm "		mov	bp,sp"#asm "		push ax"#asm "		ret		;go back to the function with everything saved"#asm "Epilogue:"#asm "		mov	sp,bp"#asm "		pop	di"#asm "		pop	si"#asm "		pop	dx"#asm "		pop	cx"#asm "		pop	bx"#asm "		pop	bp"#asm "	abs	ret"#asm "epilogue:"#asm "		mov	sp,bp"#asm "		pop	di"#asm "		pop	si"#asm "		pop	dx"#asm "		pop	cx"#asm "		pop	bx"#asm "		pop	bp"#asm "		ret"#asm "<await>:"#asm "		mov ax,#6"#asm "		int 70h"#asm "		ret"#asm "<attach>:"#asm "		mov ax,#4"#asm "		int 70h"#asm "		ret"#asm "<set_priority>:"#asm "		mov ax,#19"#asm "		int 70h"#asm "		ret"#asm "<task_info>:"#asm "		mov ax,#14"#asm "		int 70h"#asm "		ret"#asm "<alloc_mem>:"#asm "		mov ax,#31"#asm "		int 70h"#asm "		ret"/*#asm "diskint:"#asm "		cmp ax,#9000h"#asm "		je waits"#asm "		cmp ax,#9001h"#asm "		je signal"#asm "	abs	jmp @cs:oldvec"#asm "oldvec:"#asm "	w	data	0, 0"#asm "waits:"#asm "		push ax"#asm "		mov ax,#6"#asm "		push ax"#asm "		call <await>"#asm "		add sp,#2"#asm "		pop ax"#asm "		iret"#asm "signal:"#asm "		push ax"#asm "		push bp"#asm "		push bx"#asm "		push cx"#asm "		push dx"#asm "		push si"#asm "		push di"#asm "		push es"#asm "		push ds"#asm "		mov ax,#6"#asm "		int 71h				; Signal will not return here"/* * Vector the post call (INT 15) to own interrupt handler.  We save * the old vector and jmp to it for calls not meant for us. */init_int() {	asm("cli");	asm("push es");	asm("mov ax,#0h");	asm("mov es,ax");	asm("mov ax,es:4*15h");	asm("mov cs:oldvec,ax");	asm("mov es:4*15h,#diskint");	asm("mov ax,es:4*15h+2");	asm("mov cs:oldvec+2,ax");	asm("mov es:4*15h+2,cs");	asm("pop es");	asm("sti");	}*/transfer(dst_seg, dst_off, src_seg, src_off) {	asm("	push es");	asm("	push ds");	asm("	mov es,14[bp]");	asm("	mov di,16[bp]");	asm("	mov ds,18[bp]");	asm("	mov si,20[bp]");	asm("	mov cx,#256");	asm("	cld");	asm("	rep movw");	asm("	pop ds");	asm("	pop es");	}/* * Replace with an #if 0 after debugging is finished. */#if 0int row = 0, col = 0;voiddisplay_msg( text )	char *text;	{	register char *sp, *dp;	sp = text;	asm("		push es");	while(*sp) {		if(*sp == '\n') {			++row;			col = 0;			}		else if(*sp == 0x0c)	row = col = 0;		else {			if(col >= 80) {				++row;				col = 0;				}			if(row >= 25)	row = 0;			dp = (row * 80 + col) * 2;			if(atp)				asm("mov ax,#28h");			else				asm("mov ax,#0b000h");			asm("mov es,ax");			@dp = *sp;			@(dp+1) = 0x0f;			if(atp)				asm("mov ax,#30h");			else				asm("mov ax,#0b800h");			asm("mov es,ax");			@dp = *sp;			@(dp+1) = 0x0c;			++col;			}		++sp;		}	asm("		pop es");	}		char buf1[6];voiddisplay_hex( value )	unsigned value;	{	register int i;	for(i = 3 ; i >= 0 ; --i, value >>= 4)		buf1[i] = "0123456789ABCDEF"[value & 0xf];	buf1[4] = ' ';	buf1[5] = '\0';	display_msg( buf1 );	}voiddisplay_pause( n )	unsigned n;	{	unsigned i;	while( n-- ) for( i = 0xffff ; i ; --i ) ;	}#endif#asm "		export TEMP"#asm "		export SAVE"#asm "		seg 3"#asm "TEMP:"#asm "		rmb 40"#asm "SAVE:"#asm "		rmb 2"