[comp.os.minix] MINIX on Olivetti M24

sch@oce.nl (Jakob Schripsema) (04/27/87)

echo x - f_make.
sed 's/^X//' >f_make. <<'*-*-END-of-f_make.-*-*'
XCFLAGS= -Di8088 -w -F -T.
Xh=../h
Xl=/usr/lib
X
Xobj =	main.s open.s read.s write.s pipe.s device.s \
X	path.s mount.s link.s super.s inode.s cache.s filedes.s \
X	stadir.s protect.s time.s misc.s utility.s table.s putc.s
X
Xfs:	makefile   $l/head.s $(obj) $l/libc.a $l/end.s
X#	@echo "Start linking FS.  /lib/cem will be removed to make space on RAM disk"
X#	@rm -f /lib/cem /tmp/*
X#	asld -o fs $l/head.s $(obj) $l/libc.a $l/end.s
X#	@echo "FS done.  Please restore /lib/cem manually"
X	@echo "Start linking FS.
X	@asld -o fs -T. $l/head.s $(obj) $l/libc.a $l/end.s
X	@echo "FS done.
X
Xcache.s:	const.h type.h $h/const.h $h/type.h
Xcache.s:	$h/error.h
Xcache.s:	buf.h
Xcache.s:	file.h
Xcache.s:	fproc.h
Xcache.s:	glo.h
Xcache.s:	inode.h
Xcache.s:	super.h
X
Xdevice.s:	const.h type.h $h/const.h $h/type.h
Xdevice.s:	$h/com.h
Xdevice.s:	$h/error.h
Xdevice.s:	dev.h
Xdevice.s:	file.h
Xdevice.s:	fproc.h
Xdevice.s:	glo.h
Xdevice.s:	inode.h
Xdevice.s:	param.h
X
Xfiledes.s:	const.h type.h $h/const.h $h/type.h
Xfiledes.s:	$h/error.h
Xfiledes.s:	file.h
Xfiledes.s:	fproc.h
Xfiledes.s:	glo.h
Xfiledes.s:	inode.h
X
Xinode.s:	const.h type.h $h/const.h $h/type.h
Xinode.s:	$h/error.h
Xinode.s:	buf.h
Xinode.s:	file.h
Xinode.s:	fproc.h
Xinode.s:	glo.h
Xinode.s:	inode.h
Xinode.s:	super.h
X
Xlink.s:		const.h type.h $h/const.h $h/type.h
Xlink.s:		$h/error.h
Xlink.s:		buf.h
Xlink.s:		file.h
Xlink.s:		fproc.h
Xlink.s:		glo.h
Xlink.s:		inode.h
Xlink.s:		param.h
X
Xmain.s:		const.h type.h $h/const.h $h/type.h
Xmain.s:		$h/callnr.h
Xmain.s:		$h/com.h
Xmain.s:		$h/error.h
Xmain.s:		buf.h
Xmain.s:		file.h
Xmain.s:		fproc.h
Xmain.s:		glo.h
Xmain.s:		inode.h
Xmain.s:		param.h
Xmain.s:		super.h
X
Xmisc.s:		const.h type.h $h/const.h $h/type.h
Xmisc.s:		$h/callnr.h
Xmisc.s:		$h/com.h
Xmisc.s:		$h/error.h
Xmisc.s:		buf.h
Xmisc.s:		file.h
Xmisc.s:		fproc.h
Xmisc.s:		glo.h
Xmisc.s:		inode.h
Xmisc.s:		param.h
Xmisc.s:		super.h
X
Xmount.s:	const.h type.h $h/const.h $h/type.h
Xmount.s:	$h/error.h
Xmount.s:	buf.h
Xmount.s:	file.h
Xmount.s:	fproc.h
Xmount.s:	glo.h
Xmount.s:	inode.h
Xmount.s:	param.h
Xmount.s:	super.h
X
Xopen.s:		const.h type.h $h/const.h $h/type.h
Xopen.s:		$h/callnr.h
Xopen.s:		$h/error.h
Xopen.s:		buf.h
Xopen.s:		file.h
Xopen.s:		fproc.h
Xopen.s:		glo.h
Xopen.s:		inode.h
Xopen.s:		param.h
X
Xpath.s:		const.h type.h $h/const.h $h/type.h
Xpath.s:		$h/error.h
Xpath.s:		buf.h
Xpath.s:		file.h
Xpath.s:		fproc.h
Xpath.s:		glo.h
Xpath.s:		inode.h
Xpath.s:		super.h
X
Xpipe.s:		const.h type.h $h/const.h $h/type.h
Xpipe.s:		$h/callnr.h
Xpipe.s:		$h/com.h
Xpipe.s:		$h/error.h
Xpipe.s:		$h/signal.h
Xpipe.s:		file.h
Xpipe.s:		fproc.h
Xpipe.s:		glo.h
Xpipe.s:		inode.h
Xpipe.s:		param.h
X
Xprotect.s:	const.h type.h $h/const.h $h/type.h
Xprotect.s:	$h/error.h
Xprotect.s:	buf.h
Xprotect.s:	file.h
Xprotect.s:	fproc.h
Xprotect.s:	glo.h
Xprotect.s:	inode.h
Xprotect.s:	param.h
Xprotect.s:	super.h
X
Xputc.s:		const.h type.h $h/const.h $h/type.h
Xputc.s:		$h/com.h
X
Xread.s:		const.h type.h $h/const.h $h/type.h
Xread.s:		$h/com.h
Xread.s:		$h/error.h
Xread.s:		buf.h
Xread.s:		file.h
Xread.s:		fproc.h
Xread.s:		glo.h
Xread.s:		inode.h
Xread.s:		param.h
Xread.s:		super.h
X
Xstadir.s:	const.h type.h $h/const.h $h/type.h
Xstadir.s:	$h/error.h
Xstadir.s:	$h/stat.h
Xstadir.s:	file.h
Xstadir.s:	fproc.h
Xstadir.s:	glo.h
Xstadir.s:	inode.h
Xstadir.s:	param.h
X
Xsuper.s:	const.h type.h $h/const.h $h/type.h
Xsuper.s:	$h/error.h
Xsuper.s:	buf.h
Xsuper.s:	inode.h
Xsuper.s:	super.h
X
Xtable.s:	const.h type.h $h/const.h $h/type.h
Xtable.s:	$h/com.h
Xtable.s:	$h/callnr.h
Xtable.s:	$h/error.h
Xtable.s:	$h/stat.h
Xtable.s:	buf.h
Xtable.s:	dev.h
Xtable.s:	file.h
Xtable.s:	fproc.h
Xtable.s:	glo.h
Xtable.s:	inode.h
Xtable.s:	super.h
X
Xtime.s:		const.h type.h $h/const.h $h/type.h
Xtime.s:		$h/callnr.h
Xtime.s:		$h/com.h
Xtime.s:		$h/error.h
Xtime.s:		file.h
Xtime.s:		fproc.h
Xtime.s:		glo.h
Xtime.s:		inode.h
Xtime.s:		param.h
X
Xutility.s:	const.h type.h $h/const.h $h/type.h
Xutility.s:	$h/com.h
Xutility.s:	$h/error.h
Xutility.s:	buf.h
Xutility.s:	file.h
Xutility.s:	fproc.h
Xutility.s:	glo.h
Xutility.s:	inode.h
Xutility.s:	param.h
Xutility.s:	super.h
X
Xwrite.s:	const.h type.h $h/const.h $h/type.h
Xwrite.s:	$h/error.h
Xwrite.s:	buf.h
Xwrite.s:	file.h
Xwrite.s:	fproc.h
Xwrite.s:	glo.h
Xwrite.s:	inode.h
Xwrite.s:	super.h
*-*-END-of-f_make.-*-*
echo x - h_const.h
sed 's/^X//' >h_const.h <<'*-*-END-of-h_const.h-*-*'
X/* Copyright (C) 1987 by Prentice-Hall, Inc.  Permission is hereby granted to
X * private individuals and educational institutions to modify and
X * redistribute the binary and source programs of this system to other
X * private individuals and educational institutions for educational and
X * research purposes.  For corporate or commercial use, permission from
X * Prentice-Hall is required.  In general, such permission will be granted,
X * subject to a few conditions.
X */
X
X#define EXTERN        extern	/* used in *.h files */
X#define PRIVATE       static	/* PRIVATE x limits the scope of x */
X#define PUBLIC			/* PUBLIC is the opposite of PRIVATE */
X#define FORWARD 		/* some compilers require this to be 'static' */
X
X#define TRUE               1	/* used for turning integers into Booleans */
X#define FALSE              0	/* used for turning integers into Booleans */
X
X#define HZ	          60	/* clock freq (software settable on IBM-PC) */
X#define BLOCK_SIZE      1024	/* # bytes in a disk block */
X#define SUPER_USER   (uid) 0	/* uid of superuser */
X
X#define MAJOR	           8	/* major device = (dev>>MAJOR) & 0377 */
X#define MINOR	           0	/* minor device = (dev>>MINOR) & 0377 */
X
X#define NR_TASKS           8	/* number of tasks in the transfer vector */
X#define NR_PROCS          16	/* number of slots in proc table */
X#define NR_SEGS            3	/* # segments per process */
X#define T                  0	/* proc[i].mem_map[T] is for text */
X#define D                  1	/* proc[i].mem_map[D] is for data */
X#define S                  2	/* proc[i].mem_map[S] is for stack */
X
X#define MAX_P_LONG  2147483647	/* maximum positive long, i.e. 2**31 - 1 */
X
X/* Memory is allocated in clicks. */
X#define CLICK_SIZE      0020	/* unit in which memory is allocated */
X#define CLICK_SHIFT        4	/* log2 of CLICK_SIZE */
X
X/* Process numbers of some important processes */
X#define MM_PROC_NR         0	/* process number of memory manager */
X#define FS_PROC_NR         1	/* process number of file system */
X#define INIT_PROC_NR       2	/* init -- the process that goes multiuser */
X#define LOW_USER           2	/* first user not part of operating system */
X
X/* Miscellaneous */
X#define BYTE            0377	/* mask for 8 bits */
X#define TO_USER            0	/* flag telling to copy from fs to user */
X#define FROM_USER          1	/* flag telling to copy from user to fs */
X#define READING            0	/* copy data to user */
X#define WRITING            1	/* copy data from user */
X#define ABS             -999	/* this process means absolute memory */
X
X#define WORD_SIZE          2		/* number of bytes per word */
X
X#define NIL_PTR   (char *) 0	/* generally useful expression */
X
X#define NO_NUM           0x8000	/* used as numerical argument to panic() */
X#define MAX_PATH            128	/* max length of path names */
X#define SIG_PUSH_BYTES	      8	/* how many bytes pushed by signal */
X#define MAX_ISTACK_BYTES   1024	/* maximum initial stack size for EXEC */
X
X/* Device numbers of root (RAM) and boot (fd0) devices. */
X#define ROOT_DEV (dev_nr)   256	/* major-minor device number of root dev */
X/* bootdev fd0 : 512, hd2 : 770 */
X#define BOOT_DEV (dev_nr)   770	/* major-minor device number of boot diskette */
X
X/* Flag bits for i_mode in the inode. */
X#define I_TYPE          0170000	/* this field gives inode type */
X#define I_REGULAR       0100000	/* regular file, not dir or special */
X#define I_BLOCK_SPECIAL 0060000	/* block special file */
X#define I_DIRECTORY     0040000	/* file is a directory */
X#define I_CHAR_SPECIAL  0020000	/* character special file */
X#define I_SET_UID_BIT   0004000	/* set effective uid on exec */
X#define I_SET_GID_BIT   0002000	/* set effective gid on exec */
X#define ALL_MODES       0006777	/* all bits for user, group and others */
X#define RWX_MODES       0000777	/* mode bits for RWX only */
X#define R_BIT           0000004	/* Rwx protection bit */
X#define W_BIT           0000002	/* rWx protection bit */
X#define X_BIT           0000001	/* rwX protection bit */
X#define I_NOT_ALLOC     0000000	/* this inode is free */
*-*-END-of-h_const.h-*-*
echo x - k_const.h
sed 's/^X//' >k_const.h <<'*-*-END-of-k_const.h-*-*'
X/* General constants used by the kernel. */
X
X#ifdef i8088
X/* p_reg contains: ax, bx, cx, dx, si, di, bp, es, ds, cs, ss in that order. */
X#define NR_REGS           11	/* number of general regs in each proc slot */
X#define INIT_PSW      0x0200	/* initial psw */
X#define INIT_SP (int*)0x0010	/* initial sp: 3 words pushed by kernel */
X
X/* The following values are used in the assembly code.  Do not change the
X * values of 'ES_REG', 'DS_REG', 'CS_REG', or 'SS_REG' without making the 
X * corresponding changes in the assembly code.
X */
X#define ES_REG             7	/* proc[i].p_reg[ESREG] is saved es */
X#define DS_REG             8	/* proc[i].p_reg[DSREG] is saved ds */
X#define CS_REG             9	/* proc[i].p_reg[CSREG] is saved cs */
X#define SS_REG            10	/* proc[i].p_reg[SSREG] is saved ss */
X
X#define VECTOR_BYTES     284	/* bytes of interrupt vectors to save */
X#define MEM_BYTES    655360L	/* memory size for /dev/mem */
X
X/* Interrupt vectors */
X#define DIVIDE_VECTOR      0	/* divide interrupt vector */
X#define CLOCK_VECTOR       8	/* clock interrupt vector */
X#define KEYBOARD_VECTOR    9	/* keyboard interrupt vector */
X#define XT_WINI_VECTOR	  13	/* xt winchester interrupt vector */
X#define FLOPPY_VECTOR     14	/* floppy disk interrupt vector */
X#define PRINTER_VECTOR    15	/* line printer interrupt vector */
X#define SYS_VECTOR        32	/* system calls are made with int SYSVEC */
X#define AT_WINI_VECTOR	 118	/* at winchester interrupt vector */
X
X/* The 8259A interrupt controller has to be re-enabled after each interrupt. */
X#define INT_CTL         0x20	/* I/O port for interrupt controller */
X#define INT_CTLMASK     0x21	/* setting bits in this port disables ints */
X#define INT2_CTL	0xA0	/* I/O port for second interrupt controller */
X#define INT2_MASK	0xA1	/* setting bits in this port disables ints */
X#define ENABLE          0x20	/* code used to re-enable after an interrupt */
X
X/* Hardware IDs */
X#define	UNKNOWN	0	/* OS could not detect hrdware type */
X#define	IBMXT		1	/* IBM XT */
X#define	IBMAT		2	/* IBM AT */
X#define	M24		3	/* Olivetti M24, AT&T 6300 */
X#endif
X
X#define TASK_STACK_BYTES 256	/* how many bytes for each task stack */
X#define K_STACK_BYTES    256	/* how many bytes for the kernel stack */
X
X#define RET_REG            0	/* system call return codes go in this reg */
X#define IDLE            -999	/* 'cur_proc' = IDLE means nobody is running */
X
X/* The following items pertain to the 3 scheduling queues. */
X#define NQ                 3	/* # of scheduling queues */
X#define TASK_Q             0	/* ready tasks are scheduled via queue 0 */
X#define SERVER_Q           1	/* ready servers are scheduled via queue 1 */
X#define USER_Q             2	/* ready users are scheduled via queue 2 */
X
X#define printf        printk	/* the kernel really uses printk, not printf */
*-*-END-of-k_const.h-*-*
echo x - k_floppy.c
sed 's/^X//' >k_floppy.c <<'*-*-END-of-k_floppy.c-*-*'
X/* This file contains a driver for a Floppy Disk Controller (FDC) using the
X * NEC PD765 chip.  The driver supports two operations: read a block and
X * write a block.  It accepts two messages, one for reading and one for
X * writing, both using message format m2 and with the same parameters:
X *
X *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
X * ----------------------------------------------------------------
X * |  DISK_READ | device  | proc nr |  bytes  |  offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | DISK_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
X * ----------------------------------------------------------------
X *
X * The file contains one entry point:
X *
X *   floppy_task:	main entry when system is brought up
X *
X *  Changes:
X *	27 october 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz
X *	2 februari 1987 by Jakob Schripsema: added timeout() routine
X *						& reset counter
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X/* I/O Ports used by floppy disk task. */
X#define DOR            0x3F2	/* motor drive control bits */
X#define FDC_STATUS     0x3F4	/* floppy disk controller status register */
X#define FDC_DATA       0x3F5	/* floppy disk controller data register */
X#define FDC_RATE       0x3F7	/* transfer rate register */
X#define DMA_ADDR       0x004	/* port for low 16 bits of DMA address */
X#define DMA_TOP        0x081	/* port for top 4 bits of 20-bit DMA addr */
X#define DMA_COUNT      0x005	/* port for DMA count (count =  bytes - 1) */
X#define DMA_M2         0x00C	/* DMA status port */
X#define DMA_M1         0x00B	/* DMA status port */
X#define DMA_INIT       0x00A	/* DMA init port */
X
X/* Status registers returned as result of operation. */
X#define ST0             0x00	/* status register 0 */
X#define ST1             0x01	/* status register 1 */
X#define ST2             0x02	/* status register 2 */
X#define ST3             0x00	/* status register 3 (return by DRIVE_SENSE) */
X#define ST_CYL          0x03	/* slot where controller reports cylinder */
X#define ST_HEAD         0x04	/* slot where controller reports head */
X#define ST_SEC          0x05	/* slot where controller reports sector */
X#define ST_PCN          0x01	/* slot where controller reports present cyl */
X
X/* Fields within the I/O ports. */
X#define MASTER          0x80	/* used to see who is master */
X#define DIRECTION       0x40	/* is FDC trying to read or write? */
X#define CTL_BUSY        0x10	/* used to see when controller is busy */
X#define CTL_ACCEPTING   0x80	/* bit pattern FDC gives when idle */
X#define MOTOR_MASK      0xF0	/* these bits control the motors in DOR */
X#define ENABLE_INT      0x0C	/* used for setting DOR port */
X#define ST0_BITS        0xF8	/* check top 5 bits of seek status */
X#define ST3_FAULT       0x80	/* if this bit is set, drive is sick */
X#define ST3_WR_PROTECT  0x40	/* set when diskette is write protected */
X#define ST3_READY       0x20	/* set when drive is ready */
X#define TRANS_ST0       0x00	/* top 5 bits of ST0 for READ/WRITE */
X#define SEEK_ST0        0x20	/* top 5 bits of ST0 for SEEK */
X#define BAD_SECTOR      0x05	/* if these bits are set in ST1, recalibrate */
X#define BAD_CYL         0x1F	/* if any of these bits are set, recalibrate */
X#define WRITE_PROTECT   0x02	/* bit is set if diskette is write protected */
X#define CHANGE          0xC0	/* value returned by FDC after reset */
X
X/* Floppy disk controller command bytes. */
X#define FDC_SEEK        0x0F	/* command the drive to seek */
X#define FDC_READ        0xE6	/* command the drive to read */
X#define FDC_WRITE       0xC5	/* command the drive to write */
X#define FDC_SENSE       0x08	/* command the controller to tell its status */
X#define FDC_RECALIBRATE 0x07	/* command the drive to go to cyl 0 */
X#define FDC_SPECIFY     0x03	/* command the drive to accept params */
X
X/* DMA channel commands. */
X#define DMA_READ        0x46	/* DMA read opcode */
X#define DMA_WRITE       0x4A	/* DMA write opcode */
X
X/* Parameters for the disk drive. */
X#define SECTOR_SIZE      512	/* physical sector size in bytes */
X#define HC_SIZE         2400	/* # sectors on a high-capacity (1.2M) disk */
X#define NR_HEADS        0x02	/* two heads (i.e., two tracks/cylinder) */
X#define DTL             0xFF	/* determines data length (sector size) */
X#define SPEC1           0xDF	/* first parameter to SPECIFY */
X#define SPEC2           0x02	/* second parameter to SPECIFY */
X
X#define MOTOR_OFF       3*HZ	/* how long to wait before stopping motor */
X#define	TIMEOUT		2*HZ	/* how long to wait before aborting transfer */
X
X/* Error codes */
X#define ERR_SEEK          -1	/* bad seek */
X#define ERR_TRANSFER      -2	/* bad transfer */
X#define ERR_STATUS        -3	/* something wrong when getting status */
X#define ERR_RECALIBRATE   -4	/* recalibrate didn't work properly */
X#define ERR_WR_PROTECT    -5	/* diskette is write protected */
X#define ERR_DRIVE         -6	/* something wrong with a drive */
X#define	ERR_TIMEOUT	  -7	/* timeout, drive not ready */
X
X/* Miscellaneous. */
X#define MOTOR_RUNNING   0xFF	/* message type for clock interrupt */
X#define MAX_ERRORS        20	/* how often to try rd/wt before quitting */
X#define	MAX_RESETS	   2	/* max number of resets for 1 job */
X#define MAX_RESULTS        8	/* max number of bytes controller returns */
X#define NR_DRIVES          2	/* maximum number of drives */
X#define DIVISOR          128	/* used for sector size encoding */
X#define MAX_FDC_RETRY    100	/* max # times to try to output to FDC */
X#define NT                 4	/* number of diskette/drive combinations */
X
X/* Variables. */
XPRIVATE struct floppy {		/* main drive struct, one entry per drive */
X  int fl_opcode;		/* DISK_READ or DISK_WRITE */
X  int fl_curcyl;		/* current cylinder */
X  int fl_procnr;		/* which proc wanted this operation? */
X  int fl_drive;			/* drive number addressed */
X  int fl_cylinder;		/* cylinder number addressed */
X  int fl_sector;		/* sector addressed */
X  int fl_head;			/* head number addressed */
X  int fl_count;			/* byte count */
X  vir_bytes fl_address;		/* user virtual address */
X  char fl_results[MAX_RESULTS];	/* the controller can give lots of output */
X  char fl_calibration;		/* CALIBRATED or UNCALIBRATED */
X  char fl_density;		/* 0 = 360K/360K; 1 = 360K/1.2M; 2= 1.2M/1.2M */
X} floppy[NR_DRIVES];
X
X#define UNCALIBRATED       0	/* drive needs to be calibrated at next use */
X#define CALIBRATED         1	/* no calibration needed */
X
XPRIVATE int motor_status;	/* current motor status is in 4 high bits */
XPRIVATE int motor_goal;		/* desired motor status is in 4 high bits */
XPRIVATE int prev_motor;		/* which motor was started last */
XPRIVATE int need_reset;		/* set to 1 when controller must be reset */
XPRIVATE	int nresets;		/* # of resets during 1 'job' */
XPRIVATE int initialized;	/* set to 1 after first successful transfer */
XPRIVATE int d;			/* diskette/drive combination */
XPRIVATE	int active_timeout;	/* used to detect timeout in transfer */
X
XPRIVATE message mess;		/* message buffer for in and out */
X
XPRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4};
XPRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
X
X/* Four combinations of diskette/drive are supported:
X * # Drive  diskette  Sectors  Tracks  Rotation Data-rate  Comment
X * 0  360K    360K      9       40     300 RPM  250 kbps   Standard PC DSDD
X * 1  720K    360K      9       40     300 RPM  250 kbps   Quad density PC
X * 2  1.2M    360K      9       40     360 RPM  300 kbps   PC disk in AT drive
X * 3  1.2M    1.2M     15       80     360 RPM  500 kbps   AT disk in AT drive
X */
XPRIVATE int gap[NT]           = {0x2A, 0x2A, 0x23, 0x1B}; /* gap size */
XPRIVATE int rate[NT]          = {0x02, 0x02, 0x01, 0x00}; /* 250,300,500 kbps*/
XPRIVATE int nr_sectors[NT]    = {9,    9,    9,    15};   /* sectors/track */
XPRIVATE int nr_blocks[NT]     = {720,  720,  720,  2400}; /* sectors/diskette*/
XPRIVATE int steps_per_cyl[NT] = {1,    2,    2,    1};	  /* 2 = dbl step */
XPRIVATE int mtr_setup[NT]     = {HZ/4,HZ/4,3*HZ/4,3*HZ/4};/* in ticks */
X
X/*===========================================================================*
X *				floppy_task				     * 
X *===========================================================================*/
XPUBLIC floppy_task()
X{
X/* Main program of the floppy disk driver task. */
X
X  int r, caller, proc_nr;
X  extern int hw_id;
X
X  if (hw_id == M24) mtr_setup[0] = 3*HZ/4;
X
X  /* Here is the main loop of the disk task.  It waits for a message, carries
X   * it out, and sends a reply.
X   */
X  while (TRUE) {
X	/* First wait for a request to read or write a disk block. */
X	receive(ANY, &mess);	/* get a request to do some work */
X	if (mess.m_source < 0)
X		panic("disk task got message from ", mess.m_source);
X	caller = mess.m_source;
X	proc_nr = mess.PROC_NR;
X
X	/* Now carry out the work. */
X	switch(mess.m_type) {
X	    case DISK_READ:	r = do_rdwt(&mess);	break;
X	    case DISK_WRITE:	r = do_rdwt(&mess);	break;
X	    default:		r = EINVAL;		break;
X	}
X
X	/* Finally, prepare and send the reply message. */
X	mess.m_type = TASK_REPLY;	
X	mess.REP_PROC_NR = proc_nr;
X	mess.REP_STATUS = r;	/* # of bytes transferred or error code */
X	send(caller, &mess);	/* send reply to caller */
X  }
X}
X
X
X/*===========================================================================*
X *				do_rdwt					     * 
X *===========================================================================*/
XPRIVATE int do_rdwt(m_ptr)
Xmessage *m_ptr;			/* pointer to read or write message */
X{
X/* Carry out a read or write request from the disk. */
X  register struct floppy *fp;
X  int r, drive, errors, stop_motor();
X  long block;
X
X  /* Decode the message parameters. */
X  drive = m_ptr->DEVICE;
X  if (drive < 0 || drive >= NR_DRIVES) return(EIO);
X  fp = &floppy[drive];		/* 'fp' points to entry for this drive */
X  fp->fl_drive = drive;		/* save drive number explicitly */
X  fp->fl_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
X  if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);
X  block = m_ptr->POSITION/SECTOR_SIZE;
X  if (block >= HC_SIZE) return(EOF);	/* sector is beyond end of 1.2M disk */
X  d = fp->fl_density;		/* diskette/drive combination */
X  fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d]));
X  fp->fl_sector = (int) interleave[block % nr_sectors[d]];
X  fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d];
X  fp->fl_count = m_ptr->COUNT;
X  fp->fl_address = (vir_bytes) m_ptr->ADDRESS;
X  fp->fl_procnr = m_ptr->PROC_NR;
X  if (fp->fl_count != BLOCK_SIZE) return(EINVAL);
X
X  errors = 0;
X  nresets = 0;
X
X  /* This loop allows a failed operation to be repeated. */
X  while (errors <= MAX_ERRORS) {
X
X	/* If a lot of errors occur when 'initialized' is 0, it probably
X	 * means that we are trying at the wrong density.  Try another one.
X	 * Increment 'errors' here since loop is aborted on error.
X	 */
X	errors++;		/* increment count once per loop cycle */
X 	if (errors % (MAX_ERRORS/NT) == 0) {
X 		d = (d + 1) % NT;	/* try next density */
X 		fp->fl_density = d;
X 		need_reset = 1;
X	}
X  	if (block >= nr_blocks[d]) continue;
X
X	/* First check to see if a reset is needed. */
X	if (need_reset) reset();
X	if (nresets > MAX_RESETS) return(ERR_DRIVE);
X
X	/* Now set up the DMA chip. */
X	dma_setup(fp);
X
X	/* See if motor is running; if not, turn it on and wait */
X	start_motor(fp);
X
X	/* If we are going to a new cylinder, perform a seek. */
X	r = seek(fp);
X	if (r != OK) continue;	/* if error, try again */
X
X	/* Perform the transfer. */
X	r = transfer(fp);
X	if (r == OK) break;	/* if successful, exit loop */
X	if (r == ERR_WR_PROTECT || r == ERR_TIMEOUT) break;	/* retries won't help */
X
X  }
X
X  /* Start watch_dog timer to turn motor off in a few seconds */
X  motor_goal = ENABLE_INT;	/* when timer goes off, kill all motors */
X  clock_mess(MOTOR_OFF, stop_motor);
X  if (r == OK && fp->fl_cylinder > 0) initialized = 1;	/* seek works */
X  return(r == OK ? BLOCK_SIZE : EIO);
X}
X
X/*===========================================================================*
X *				dma_setup				     * 
X *===========================================================================*/
XPRIVATE dma_setup(fp)
Xstruct floppy *fp;		/* pointer to the drive struct */
X{
X/* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
X * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
X * to be read from or written to, the byte count minus 1, and a read or write
X * opcode.  This routine sets up the DMA chip.  Note that the chip is not
X * capable of doing a DMA across a 64K boundary (e.g., you can't read a 
X * 512-byte block starting at physical address 65520).
X */
X
X  int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end;
X  vir_bytes vir, ct;
X  phys_bytes user_phys;
X  extern phys_bytes umap();
X
X  mode = (fp->fl_opcode == DISK_READ ? DMA_READ : DMA_WRITE);
X  vir = (vir_bytes) fp->fl_address;
X  ct = (vir_bytes) fp->fl_count;
X  user_phys = umap(proc_addr(fp->fl_procnr), D, vir, ct);
X  low_addr  = (int) (user_phys >>  0) & BYTE;
X  high_addr = (int) (user_phys >>  8) & BYTE;
X  top_addr  = (int) (user_phys >> 16) & BYTE;
X  low_ct  = (int) ( (ct - 1) >> 0) & BYTE;
X  high_ct = (int) ( (ct - 1) >> 8) & BYTE;
X
X  /* Check to see if the transfer will require the DMA address counter to
X   * go from one 64K segment to another.  If so, do not even start it, since
X   * the hardware does not carry from bit 15 to bit 16 of the DMA address.
X   * Also check for bad buffer address.  These errors mean FS contains a bug.
X   */
X  if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir);
X  top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
X  if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr);
X
X  /* Now set up the DMA registers. */
X  lock();
X  port_out(DMA_M2, mode);	/* set the DMA mode */
X  port_out(DMA_M1, mode);	/* set it again */
X  port_out(DMA_ADDR, low_addr);	/* output low-order 8 bits */
X  port_out(DMA_ADDR, high_addr);/* output next 8 bits */
X  port_out(DMA_TOP, top_addr);	/* output highest 4 bits */
X  port_out(DMA_COUNT, low_ct);	/* output low 8 bits of count - 1 */
X  port_out(DMA_COUNT, high_ct);	/* output high 8 bits of count - 1 */
X  unlock();
X  port_out(DMA_INIT, 2);	/* initialize DMA */
X}
X
X
X/*===========================================================================*
X *				start_motor				     * 
X *===========================================================================*/
XPRIVATE start_motor(fp)
Xstruct floppy *fp;		/* pointer to the drive struct */
X{
X/* Control of the floppy disk motors is a big pain.  If a motor is off, you
X * have to turn it on first, which takes 1/2 second.  You can't leave it on
X * all the time, since that would wear out the diskette.  However, if you turn
X * the motor off after each operation, the system performance will be awful.
X * The compromise used here is to leave it on for a few seconds after each
X * operation.  If a new operation is started in that interval, it need not be
X * turned on again.  If no new operation is started, a timer goes off and the
X * motor is turned off.  I/O port DOR has bits to control each of 4 drives.
X * Interrupts must be disabled temporarily to prevent clock interrupt from
X * turning off motors while we are testing the bits.
X */
X
X  int motor_bit, running, send_mess();
X
X  lock();			/* no interrupts while checking out motor */
X  motor_bit = 1 << (fp->fl_drive + 4);	/* bit mask for this drive */
X  motor_goal = motor_bit | ENABLE_INT | fp->fl_drive;
X  if (motor_status & prev_motor) motor_goal |= prev_motor;
X  running = motor_status & motor_bit;	/* nonzero if this motor is running */
X  port_out(DOR, motor_goal);
X  motor_status = motor_goal;
X  prev_motor = motor_bit;	/* record motor started for next time */
X  unlock();
X
X  /* If the motor was already running, we don't have to wait for it. */
X  if (running) return;			/* motor was already running */
X  clock_mess(mtr_setup[d], send_mess);	/* motor was not running */
X  receive(CLOCK, &mess);		/* wait for clock interrupt */
X}
X
X
X/*===========================================================================*
X *				stop_motor				     * 
X *===========================================================================*/
XPRIVATE stop_motor()
X{
X/* This routine is called by the clock interrupt after several seconds have
X * elapsed with no floppy disk activity.  It checks to see if any drives are
X * supposed to be turned off, and if so, turns them off.
X */
X
X  if ( (motor_goal & MOTOR_MASK) != (motor_status & MOTOR_MASK) ) {
X	port_out(DOR, motor_goal);
X	motor_status = motor_goal;
X  }
X}
X
X
X/*===========================================================================*
X *				seek					     * 
X *===========================================================================*/
XPRIVATE int seek(fp)
Xstruct floppy *fp;		/* pointer to the drive struct */
X{
X/* Issue a SEEK command on the indicated drive unless the arm is already 
X * positioned on the correct cylinder.
X */
X
X  int r;
X  extern int timeout();
X
X  /* Are we already on the correct cylinder? */
X  if (fp->fl_calibration == UNCALIBRATED)
X	if (recalibrate(fp) != OK) return(ERR_SEEK);
X  if (fp->fl_curcyl == fp->fl_cylinder) return(OK);
X
X  /* No.  Wrong cylinder.  Issue a SEEK and wait for interrupt. */
X  fdc_out(FDC_SEEK);		/* start issuing the SEEK command */
X  fdc_out( (fp->fl_head << 2) | fp->fl_drive);
X  fdc_out(fp->fl_cylinder * steps_per_cyl[d]);
X  if (need_reset) return(ERR_SEEK);	/* if controller is sick, abort seek */
X  receive(HARDWARE, &mess);
X
X  /* Interrupt has been received.  Check drive status. */
X  fdc_out(FDC_SENSE);		/* probe FDC to make it return status */
X  r = fdc_results(fp);		/* get controller status bytes */
X  if ( (fp->fl_results[ST0] & ST0_BITS) != SEEK_ST0) r = ERR_SEEK;
X  if (fp->fl_results[ST1] != fp->fl_cylinder * steps_per_cyl[d]) r = ERR_SEEK;
X  if (r != OK) 
X	if (recalibrate(fp) != OK) return(ERR_SEEK);
X  return(r);
X}
X
X
X/*===========================================================================*
X *				transfer				     * 
X *===========================================================================*/
XPRIVATE int transfer(fp)
Xregister struct floppy *fp;	/* pointer to the drive struct */
X{
X/* The drive is now on the proper cylinder.  Read or write 1 block. */
X
X  int r, s, op;
X  extern int olivetti;
X  extern int timeout();
X
X  /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
X  if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
X  if ( ( (motor_status>>(fp->fl_drive+4)) & 1) == 0) return(ERR_TRANSFER);
X
X  /* The PC-AT requires the date rate to be set to 250 or 500 kbps */
X  if (hw_id == IBMAT) port_out(FDC_RATE, rate[d]);
X
X  /* The command is issued by outputing 9 bytes to the controller chip. */
X  op = (fp->fl_opcode == DISK_READ ? FDC_READ : FDC_WRITE);
X  fdc_out(op);			/* issue the read or write command */
X  fdc_out( (fp->fl_head << 2) | fp->fl_drive);
X  fdc_out(fp->fl_cylinder);	/* tell controller which cylinder */
X  fdc_out(fp->fl_head);		/* tell controller which head */
X  fdc_out(fp->fl_sector);	/* tell controller which sector */
X  fdc_out( (int) len[SECTOR_SIZE/DIVISOR]);	/* sector size */
X  fdc_out(nr_sectors[d]);	/* tell controller how big a track is */
X  fdc_out(gap[d]);		/* tell controller how big sector gap is */
X  fdc_out(DTL);			/* tell controller about data length */
X
X  /* Block, waiting for disk interrupt. */
X  if (need_reset) return(ERR_TRANSFER);	/* if controller is sick, abort op */
X  clock_mess(TIMEOUT,timeout); /* start timer */
X  active_timeout = TRUE; /* and set flag to remember this */
X
X  receive(HARDWARE, &mess);
X
X  /* Check for timeout */
X  if (active_timeout == FALSE) return(ERR_TIMEOUT);
X  active_timeout = FALSE; /* all done, forget about timeout */
X
X  /* Get controller status and check for errors. */
X  r = fdc_results(fp);
X  if (r != OK) return(r);
X  if ( (fp->fl_results[ST1] & BAD_SECTOR) || (fp->fl_results[ST2] & BAD_CYL) )
X	fp->fl_calibration = UNCALIBRATED;
X  if (fp->fl_results[ST1] & WRITE_PROTECT) {
X	printf("Diskette in drive %d is write protected.\n", fp->fl_drive);
X	return(ERR_WR_PROTECT);
X  }
X  if ((fp->fl_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER);
X  if (fp->fl_results[ST1] | fp->fl_results[ST2]) return(ERR_TRANSFER);
X
X  /* Compare actual numbers of sectors transferred with expected number. */
X  s =  (fp->fl_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * nr_sectors[d];
X  s += (fp->fl_results[ST_HEAD] - fp->fl_head) * nr_sectors[d];
X  s += (fp->fl_results[ST_SEC] - fp->fl_sector);
X  if (s * SECTOR_SIZE != fp->fl_count) return(ERR_TRANSFER);
X  return(OK);
X}
X
X/*===========================================================================*
X *				timeout					     * 
X *===========================================================================*/
XPRIVATE int timeout()
X{
X/* The NEC 765 fdc has its ready line tied to Vcc. So it's impossible
X * to check a drives ready state. If a drive isn't ready and a read or
X * write is started in transfer(), the fdc waits until the drive gets ready.
X * This blocks both the floppy task and FS until the drive gets ready.
X * Solution :
X * Before transfer() does a receive() it starts a timeout.
X * If the timer runs out this routine is called. When no interrupt has
X * occured in the mean time (active_timeout still TRUE) the disk probably
X * isn't ready. To get the floppy task out of its deadlock the fdc is reset.
X * After a reset the fdc detects 'drive-ready' (ready line still tied to
X * Vcc !!) and generates a 'drive-status-changed' interrupt (see 765 spec.)
X * The interrupt routine sends a message to the floppy task which readies
X * the floppy task.
X * As usual the problem is with the IBM PC hardware.
X */
X
X  if (active_timeout == FALSE) return; /* no work to do */
X
X  /* Start fdc reset */
X  lock();
X  port_out(DOR, 0);		/* strobe reset bit low */
X  port_out(DOR, ENABLE_INT);	/* strobe it high again */
X  unlock();			/* interrupts allowed again */
X
X  need_reset = TRUE;
X  active_timeout = FALSE;
X}
X
X/*===========================================================================*
X *				fdc_results				     * 
X *===========================================================================*/
XPRIVATE int fdc_results(fp)
Xregister struct floppy *fp;	/* pointer to the drive struct */
X{
X/* Extract results from the controller after an operation. */
X
X  int i, j, status, ready;
X
X  /* Loop, extracting bytes from FDC until it says it has no more. */
X  for (i = 0; i < MAX_RESULTS; i++) {
X	ready = FALSE;
X	for (j = 0; j < MAX_FDC_RETRY; j++) {
X		port_in(FDC_STATUS, &status);
X		if (status & MASTER) {
X			ready = TRUE;
X			break;
X		}
X	}
X	if (ready == FALSE) return(ERR_STATUS);	/* time out */
X
X	if ((status & CTL_BUSY) == 0) return(OK);
X	if ((status & DIRECTION) == 0) return(ERR_STATUS);
X	port_in(FDC_DATA, &status);
X	fp->fl_results[i] = status & BYTE;
X  }
X
X  /* FDC is giving back too many results. */
X  need_reset = TRUE;		/* controller chip must be reset */
X  return(ERR_STATUS);
X}
X
X
X/*===========================================================================*
X *				fdc_out					     * 
X *===========================================================================*/
XPRIVATE fdc_out(val)
Xint val;			/* write this byte to floppy disk controller */
X{
X/* Output a byte to the controller.  This is not entirely trivial, since you
X * can only write to it when it is listening, and it decides when to listen.
X * If the controller refuses to listen, the FDC chip is given a hard reset.
X */
X
X  int retries, r;
X
X  if (need_reset) return;	/* if controller is not listening, return */
X  retries = MAX_FDC_RETRY;
X
X  /* It may take several tries to get the FDC to accept a command. */
X  while (retries-- > 0) {
X	port_in(FDC_STATUS, &r);
X	r &= (MASTER | DIRECTION);	/* just look at bits 2 and 3 */
X	if (r != CTL_ACCEPTING) continue;	/* FDC is not listening */
X	port_out(FDC_DATA, val);
X	return;
X  }
X
X  /* Controller is not listening.  Hit it over the head with a hammer. */
X  need_reset = TRUE;
X}
X
X
X/*===========================================================================*
X *				recalibrate				     * 
X *===========================================================================*/
XPRIVATE int recalibrate(fp)
Xregister struct floppy *fp;	/* pointer tot he drive struct */
X{
X/* The floppy disk controller has no way of determining its absolute arm
X * position (cylinder).  Instead, it steps the arm a cylinder at a time and
X * keeps track of where it thinks it is (in software).  However, after a
X * SEEK, the hardware reads information from the diskette telling where the
X * arm actually is.  If the arm is in the wrong place, a recalibration is done,
X * which forces the arm to cylinder 0.  This way the controller can get back
X * into sync with reality.
X */
X
X  int r;
X  /* Issue the RECALIBRATE command and wait for the interrupt. */
X  start_motor(fp);		/* can't recalibrate with motor off */
X  fdc_out(FDC_RECALIBRATE);	/* tell drive to recalibrate itself */
X  fdc_out(fp->fl_drive);	/* specify drive */
X  if (need_reset) return(ERR_SEEK);	/* don't wait if controller is sick */
X  receive(HARDWARE, &mess);	/* wait for interrupt message */
X
X  /* Determine if the recalibration succeeded. */
X  fdc_out(FDC_SENSE);		/* issue SENSE command to see where we are */
X  r = fdc_results(fp);		/* get results of the SENSE command */
X  fp->fl_curcyl = -1;		/* force a SEEK next time */
X  if (r != OK ||		/* controller would not respond */
X     (fp->fl_results[ST0]&ST0_BITS) != SEEK_ST0 || fp->fl_results[ST_PCN] !=0){
X	/* Recalibration failed.  FDC must be reset. */
X	need_reset = TRUE;
X	fp->fl_calibration = UNCALIBRATED;
X	return(ERR_RECALIBRATE);
X  } else {
X	/* Recalibration succeeded. */
X	fp->fl_calibration = CALIBRATED;
X	return(OK);
X  }
X}
X
X/*===========================================================================*
X *				reset					     * 
X *===========================================================================*/
XPRIVATE reset()
X{
X/* Issue a reset to the controller.  This is done after any catastrophe,
X * like the controller refusing to respond.
X */
X
X  int i, r, status;
X  register struct floppy *fp;
X
X  /* Disable interrupts and strobe reset bit low. */
X  need_reset = FALSE;
X  lock();
X  motor_status = 0;
X  motor_goal = 0;
X  port_out(DOR, 0);		/* strobe reset bit low */
X  port_out(DOR, ENABLE_INT);	/* strobe it high again */
X  unlock();			/* interrupts allowed again */
X  receive(HARDWARE, &mess);	/* collect the RESET interrupt */
X
X  /* Interrupt from the reset has been received.  Continue resetting. */
X  fp = &floppy[0];		/* use floppy[0] for scratch */
X  fp->fl_results[0] = 0;	/* this byte will be checked shortly */
X  fdc_out(FDC_SENSE);		/* did it work? */
X  r = fdc_results(fp);		/* get results */
X  status = fp->fl_results[0] & BYTE;
X
X  /* Tell FDC drive parameters. */
X  fdc_out(FDC_SPECIFY);		/* specify some timing parameters */
X  fdc_out(SPEC1);		/* step-rate and head-unload-time */
X  fdc_out(SPEC2);		/* head-load-time and non-dma */
X
X  for (i = 0; i < NR_DRIVES; i++) floppy[i].fl_calibration = UNCALIBRATED;
X
X  nresets++;	/* count this reset */
X}
X
X
X/*===========================================================================*
X *				clock_mess				     * 
X *===========================================================================*/
XPRIVATE clock_mess(ticks, func)
Xint ticks;			/* how many clock ticks to wait */
Xint (*func)();			/* function to call upon time out */
X{
X/* Send the clock task a message. */
X
X  mess.m_type = SET_ALARM;
X  mess.CLOCK_PROC_NR = FLOPPY;
X  mess.DELTA_TICKS = ticks;
X  mess.FUNC_TO_CALL = func;
X  sendrec(CLOCK, &mess);
X}
X
X
X/*===========================================================================*
X *				send_mess				     * 
X *===========================================================================*/
XPRIVATE send_mess()
X{
X/* This routine is called when the clock task has timed out on motor startup.*/
X
X  mess.m_type = MOTOR_RUNNING;
X  send(FLOPPY, &mess);
X}
*-*-END-of-k_floppy.c-*-*
echo x - k_glo.h
sed 's/^X//' >k_glo.h <<'*-*-END-of-k_glo.h-*-*'
X/* Global variables used in the kernel. */
X
X/* Clocks and timers */
XEXTERN real_time realtime;	/* real time clock */
XEXTERN int lost_ticks;		/* incremented when clock int can't send mess*/
X
X/* Processes, signals, and messages. */
XEXTERN int cur_proc;		/* current process */
XEXTERN int prev_proc;		/* previous process */
XEXTERN int sig_procs;		/* number of procs with p_pending != 0 */
XEXTERN message int_mess;	/* interrupt routines build message here */
X
X/* CPU type. */
XEXTERN int olivetti;		/* TRUE for Olivetti-style keyboard */
XEXTERN int hw_id;		/* identification of hardware */
X
X/* The kernel and task stacks. */
XEXTERN struct t_stack {
X  int stk[TASK_STACK_BYTES/sizeof(int)];
X} t_stack[NR_TASKS - 1];	/* task stacks; task = -1 never really runs */
X
XEXTERN char k_stack[K_STACK_BYTES];	/* The kernel stack. */
*-*-END-of-k_glo.h-*-*
echo x - k_main.c
sed 's/^X//' >k_main.c <<'*-*-END-of-k_main.c-*-*'
X/* This file contains the main program of MINIX.  The routine main()
X * initializes the system and starts the ball rolling by setting up the proc
X * table, interrupt vectors, and scheduling each task to run to initialize
X * itself.
X * 
X * The entries into this file are:
X *   main:		MINIX main program
X *   unexpected_int:	called when an interrupt to an unused vector < 16 occurs
X *   trap:		called when an unexpected trap to a vector >= 16 occurs
X *   panic:		abort MINIX due to a fatal error
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X#define SAFETY             8	/* margin of safety for stack overflow (ints)*/
X#define VERY_BIG       39328	/* must be bigger than kernel size (clicks) */
X#define BASE            1536	/* address where MINIX starts in memory */
X#define SIZES              8	/* sizes array has 8 entries */
X#define CPU_TY1       0xFFFF	/* BIOS segment that tells CPU type */
X#define CPU_TY2       0x000E	/* BIOS offset that tells CPU type */
X#define PC_AT           0xFC	/* IBM code for PC-AT (in BIOS at 0xFFFFE) */
X
X/*===========================================================================*
X *                                   main                                    * 
X *===========================================================================*/
XPUBLIC main()
X{
X/* Start the ball rolling. */
X
X  register struct proc *rp;
X  register int t;
X  vir_clicks size;
X  phys_clicks base_click, mm_base, previous_base;
X  phys_bytes phys_b;
X  extern unsigned sizes[8];	/* table filled in by build */
X  extern int color, vec_table[], get_chrome(), (*task[])();
X  extern int s_call(), disk_int(), tty_int(), clock_int(), disk_int();
X  extern int wini_int(), lpr_int(), surprise(), trp(), divide();
X  extern phys_bytes umap();
X
X  /* Set up proc table entry for user processes.  Be very careful about
X   * sp, since the 3 words prior to it will be clobbered when the kernel pushes
X   * pc, cs, and psw onto the USER's stack when starting the user the first
X   * time.  This means that with initial sp = 0x10, user programs must leave 
X   * the words at 0x000A, 0x000C, and 0x000E free.
X   */
X
X  lock();			/* we can't handle interrupts yet */
X  base_click = BASE >> CLICK_SHIFT;
X  size = sizes[0] + sizes[1];	/* kernel text + data size in clicks */
X  mm_base = base_click + size;	/* place where MM starts (in clicks) */
X
X  for (rp = &proc[0]; rp <= &proc[NR_TASKS+LOW_USER]; rp++) {
X	for (t=0; t< NR_REGS; t++) rp->p_reg[t] = 0100*t;	/* debugging */
X	t = rp - proc - NR_TASKS;	/* task number */
X	rp->p_sp = (rp < &proc[NR_TASKS] ? t_stack[NR_TASKS+t+1].stk : INIT_SP);
X	rp->p_splimit = rp->p_sp;
X	if (rp->p_splimit != INIT_SP)
X		rp->p_splimit -= (TASK_STACK_BYTES - SAFETY)/sizeof(int);
X	rp->p_pcpsw.pc = task[t + NR_TASKS];
X	if (rp->p_pcpsw.pc != 0 || t >= 0) ready(rp);
X	rp->p_pcpsw.psw = INIT_PSW;
X	rp->p_flags = 0;
X
X	/* Set up memory map for tasks and MM, FS, INIT. */
X	if (t < 0) {
X		/* I/O tasks. */
X		rp->p_map[T].mem_len  = VERY_BIG; 
X		rp->p_map[T].mem_phys = base_click;
X		rp->p_map[D].mem_len  = VERY_BIG; 
X		rp->p_map[D].mem_phys = base_click + sizes[0];
X		rp->p_map[S].mem_len  = VERY_BIG; 
X		rp->p_map[S].mem_phys = base_click + sizes[0] + sizes[1];
X		rp->p_map[S].mem_vir = sizes[0] + sizes[1];
X	} else {
X		/* MM, FS, and INIT. */
X		previous_base = proc[NR_TASKS + t - 1].p_map[S].mem_phys;
X		rp->p_map[T].mem_len  = sizes[2*t + 2];
X		rp->p_map[T].mem_phys = (t == 0 ? mm_base : previous_base);
X		rp->p_map[D].mem_len  = sizes[2*t + 3];
X		rp->p_map[D].mem_phys = rp->p_map[T].mem_phys + sizes[2*t + 2];
X		rp->p_map[S].mem_vir  = sizes[2*t + 3];
X		rp->p_map[S].mem_phys = rp->p_map[D].mem_phys + sizes[2*t + 3];
X	}
X
X#ifdef i8088
X	rp->p_reg[CS_REG] = rp->p_map[T].mem_phys;
X	rp->p_reg[DS_REG] = rp->p_map[D].mem_phys;
X	rp->p_reg[SS_REG] = rp->p_map[D].mem_phys;
X	rp->p_reg[ES_REG] = rp->p_map[D].mem_phys;
X#endif
X  }
X
X  proc[NR_TASKS+(HARDWARE)].p_sp = (int *) k_stack;
X  proc[NR_TASKS+(HARDWARE)].p_sp += K_STACK_BYTES/2;
X  proc[NR_TASKS+(HARDWARE)].p_splimit = (int *) k_stack;
X  proc[NR_TASKS+(HARDWARE)].p_splimit += SAFETY/2;
X
X  for (rp = proc_addr(LOW_USER+1); rp < proc_addr(NR_PROCS); rp++)
X	rp->p_flags = P_SLOT_FREE;
X
X  /* Determine if display is color or monochrome and CPU type (from BIOS). */
X  color = get_chrome();		/* 0 = mono, 1 = color */
X  hw_id = UNKNOWN;
X  t = get_byte(CPU_TY1, CPU_TY2);	/* is this PC, XT, AT ... ? */
X  if (t == PC_AT) hw_id = IBMAT;
X  if (hw_id == UNKNOWN && is_m24() == OK) hw_id = M24;
X
X  /* Save the old interrupt vectors. */
X  phys_b = umap(proc_addr(HARDWARE), D, (vir_bytes) vec_table, VECTOR_BYTES);
X  phys_copy(0L, phys_b, (long) VECTOR_BYTES);	/* save all the vectors */
X
X  /* Set up the new interrupt vectors. */
X  for (t = 0; t < 16; t++) set_vec(t, surprise, base_click);
X  for (t = 16; t < 256; t++) set_vec(t, trp, base_click);
X  set_vec(DIVIDE_VECTOR, divide, base_click);
X  set_vec(SYS_VECTOR, s_call, base_click);
X  set_vec(CLOCK_VECTOR, clock_int, base_click);
X  set_vec(KEYBOARD_VECTOR, tty_int, base_click);
X  set_vec(FLOPPY_VECTOR, disk_int, base_click);
X  set_vec(PRINTER_VECTOR, lpr_int, base_click);
X  if (hw_id == IBMAT)
X	  set_vec(AT_WINI_VECTOR, wini_int, base_click);
X  else
X	  set_vec(XT_WINI_VECTOR, wini_int, base_click);
X
X  /* Put a ptr to proc table in a known place so it can be found in /dev/mem */
X  set_vec( (BASE - 4)/4, proc, (phys_clicks) 0);
X
X  bill_ptr = proc_addr(HARDWARE);	/* it has to point somewhere */
X  pick_proc();
X
X  /* Now go to the assembly code to start running the current process. */
X  port_out(INT_CTLMASK, 0);	/* do not mask out any interrupts in 8259A */
X  port_out(INT2_MASK, 0);	/* same for second interrupt controller */
X  restart();
X}
X
X
X/*===========================================================================*
X *                                   unexpected_int                          * 
X *===========================================================================*/
XPUBLIC unexpected_int()
X{
X/* A trap or interrupt has occurred that was not expected. */
X
X  printf("Unexpected trap: vector < 16\n");
X  printf("pc = 0x%x    text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc,
X					proc_ptr->p_map[D].mem_len<<4);
X}
X
X
X/*===========================================================================*
X *                                   trap                                    * 
X *===========================================================================*/
XPUBLIC trap()
X{
X/* A trap (vector >= 16) has occurred.  It was not expected. */
X
X  printf("\nUnexpected trap: vector >= 16 ");
X  printf("This may be due to accidentally including\n");
X  printf("a non-MINIX library routine that is trying to make a system call.\n");
X  printf("pc = 0x%x    text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc,
X					proc_ptr->p_map[D].mem_len<<4);
X}
X
X
X/*===========================================================================*
X *                                   div_trap                                * 
X *===========================================================================*/
XPUBLIC div_trap()
X{
X/* The divide intruction traps to vector 0 upon overflow. */
X
X  printf("Trap to vector 0: divide overflow.  ");
X  printf("pc = 0x%x    text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc,
X					proc_ptr->p_map[D].mem_len<<4);
X}
X
X
X/*===========================================================================*
X *                                   panic                                   * 
X *===========================================================================*/
XPUBLIC panic(s,n)
Xchar *s;
Xint n; 
X{
X/* The system has run aground of a fatal error.  Terminate execution.
X * If the panic originated in MM or FS, the string will be empty and the
X * file system already syncked.  If the panic originates in the kernel, we are
X * kind of stuck. 
X */
X
X  if (*s != 0) {
X	printf("\nKernel panic: %s",s); 
X	if (n != NO_NUM) printf(" %d", n);
X	printf("\n");
X  }
X  printf("\nType space to reboot\n");
X  wreboot();
X
X}
X
X#ifdef i8088
X/*===========================================================================*
X *                                   set_vec                                 * 
X *===========================================================================*/
XPRIVATE set_vec(vec_nr, addr, base_click)
Xint vec_nr;			/* which vector */
Xint (*addr)();			/* where to start */
Xphys_clicks base_click;		/* click where kernel sits in memory */
X{
X/* Set up an interrupt vector. */
X
X  unsigned vec[2];
X  unsigned u;
X  phys_bytes phys_b;
X  extern unsigned sizes[8];
X
X  /* Build the vector in the array 'vec'. */
X  vec[0] = (unsigned) addr;
X  vec[1] = (unsigned) base_click;
X  u = (unsigned) vec;
X
X  /* Copy the vector into place. */
X  phys_b = ( (phys_bytes) base_click + (phys_bytes) sizes[0]) << CLICK_SHIFT;
X  phys_b += u;
X  phys_copy(phys_b, (phys_bytes) 4*vec_nr, (phys_bytes) 4);
X}
X#endif
X
X/*===========================================================================*
X *                                   is_m24                                  * 
X *===========================================================================*/
XPRIVATE is_m24()
X{
X/* Reads the BIOS to to check is the hardware is a Olivetti M24
X * This routine works for BIOS versions 1.1, 1.21 and 1.36
X */
X
X  char	buffer[9];
X  phys_bytes buffaddr;
X
X  /* read copyright string at FC00:50 */
X  buffaddr = umap(proc_addr(HARDWARE), D, (vir_bytes) buffer, 8);
X  phys_copy(0xFC050L, buffaddr, 8L);
X
X  /* test string */
X  buffer[8] = '\0';
X  if(strcmp(buffer,"OLIVETTI") == 0)
X	return(TRUE);
X  return(FALSE);
X}
*-*-END-of-k_main.c-*-*
echo x - k_make.
sed 's/^X//' >k_make. <<'*-*-END-of-k_make.-*-*'
X# The kernel directory contains files xt_wini.c and at_wini.c.  Before running
X# make you must copy one of these to wini.c, depending on whether you have a
X# PC or an AT.  You must do this even if you do not have a hard disk..
XCFLAGS= -Di8088 -w -F -T.
Xh=../h
Xl=/usr/lib
X
Xobj = mpx88.s main.s proc.s system.s tty.s clock.s memory.s floppy.s wini.s  \
X      printer.s table.s klib88.s dmp.s 
X
Xkernel:	makefile $(obj) $l/libc.a
X#	@echo "Start linking Kernel.  /lib/* will be removed to make space on RAM disk"
X#	@rm -f /lib/cem /lib/cpp /tmp/*
X#	@asld -o kernel  $(obj) $l/libc.a $l/end.s
X#	@echo "Kernel done.  Please restore /lib/cem and /lib/cpp manually"
X	@echo "Start linking Kernel."
X	@asld -o kernel -T. $(obj) $l/libc.a $l/end.s
X	@echo "Kernel done."
X
Xclock.s:	const.h type.h $h/const.h $h/type.h
Xclock.s:	$h/callnr.h
Xclock.s:	$h/com.h
Xclock.s:	$h/error.h
Xclock.s:	$h/signal.h
Xclock.s:	glo.h
Xclock.s:	proc.h
X
Xfloppy.s:	const.h type.h $h/const.h $h/type.h
Xfloppy.s:	$h/callnr.h
Xfloppy.s:	$h/com.h
Xfloppy.s:	$h/error.h
Xfloppy.s:	glo.h
Xfloppy.s:	proc.h
X
X
Xdmp.s:		const.h type.h $h/const.h $h/type.h
Xdmp.s:		$h/callnr.h
Xdmp.s:		$h/com.h
Xdmp.s:		$h/error.h
Xdmp.s:		glo.h
Xdmp.s:		proc.h
X
Xmain.s:		const.h type.h $h/const.h $h/type.h
Xmain.s:		$h/callnr.h
Xmain.s:		$h/com.h
Xmain.s:		$h/error.h
Xmain.s:		glo.h
Xmain.s:		proc.h
X
Xmemory.s:	const.h type.h $h/const.h $h/type.h
Xmemory.s:	$h/callnr.h
Xmemory.s:	$h/com.h
Xmemory.s:	$h/error.h
Xmemory.s:	proc.h
X
Xprinter.s:	const.h type.h $h/const.h $h/type.h
Xprinter.s:	$h/callnr.h
Xprinter.s:	$h/com.h
Xprinter.s:	$h/error.h
X
Xproc.s:		const.h type.h $h/const.h $h/type.h
Xproc.s:		$h/callnr.h
Xproc.s:		$h/com.h
Xproc.s:		$h/error.h
Xproc.s:		glo.h
Xproc.s:		proc.h
X
Xsystem.s:	const.h type.h $h/const.h $h/type.h
Xsystem.s:	$h/callnr.h
Xsystem.s:	$h/com.h
Xsystem.s:	$h/error.h
Xsystem.s:	$h/signal.h
Xsystem.s:	glo.h
Xsystem.s:	proc.h
X
Xtable.s:	const.h type.h $h/const.h $h/type.h
Xtable.s:	glo.h
Xtable.s:	proc.h
X
Xtty.s:	const.h type.h $h/const.h $h/type.h
Xtty.s:	$h/callnr.h
Xtty.s:	$h/com.h
Xtty.s:	$h/error.h
Xtty.s:	$h/sgtty.h
Xtty.s:	$h/signal.h
Xtty.s:	glo.h
Xtty.s:	proc.h
X
Xwini.s:	const.h type.h $h/const.h $h/type.h
Xwini.s:	$h/callnr.h
Xwini.s:	$h/com.h
Xwini.s:	$h/error.h
Xwini.s:	proc.h
*-*-END-of-k_makmmmb d;		/* 

sch@oce.nl (Jakob Schripsema) (04/27/87)

echo x - k_proc.c
sed 's/^X//' >k_proc.c <<'*-*-END-of-k_proc.c-*-*'
X/* This file contains essentially all of the process and message handling.
X * It has two main entry points from the outside:
X *
X *   sys_call:   called when a process or task does SEND, RECEIVE or SENDREC
X *   interrupt:	called by interrupt routines to send a message to task
X *
X * It also has five minor entry points:
X *
X *   ready:	put a process on one of the ready queues so it can be run
X *   unready:	remove a process from the ready queues
X *   sched:	a process has run too long; schedule another one
X *   mini_send:	send a message (used by interrupt signals, etc.)
X *   pick_proc:	pick a process to run (used by system initialization)
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X/*===========================================================================*
X *				interrupt				     * 
X *===========================================================================*/
XPUBLIC interrupt(task, m_ptr)
Xint task;			/* number of task to be started */
Xmessage *m_ptr;			/* interrupt message to send to the task */
X{
X/* An interrupt has occurred.  Schedule the task that handles it. */
X
X  int i, n, old_map, this_bit;
X
X#ifdef i8088
X  /* Re-enable the 8259A interrupt controller. */
X  port_out(INT_CTL, ENABLE);	/* this re-enables the 8259A controller chip */
X  if (hw_id == IBMAT) port_out(INT2_CTL, ENABLE);	/* re-enable second 8259A */
X#endif
X
X  /* Try to send the interrupt message to the indicated task. */
X  this_bit = 1 << (-task);
X  if (mini_send(HARDWARE, task, m_ptr) != OK) {
X	/* The message could not be sent to the task; it was not waiting. */
X	old_map = busy_map;	/* save original map of busy tasks */
X	if (task == CLOCK) {
X		lost_ticks++;
X	} else {
X		busy_map |= this_bit;		/* mark task as busy */
X		task_mess[-task] = m_ptr;	/* record message pointer */
X	}
X  } else {
X	/* Hardware interrupt was successfully sent as a message. */
X	busy_map &= ~this_bit;	/* turn off the bit in case it was on */
X	old_map = busy_map;
X  }
X
X  /* See if any tasks that were previously busy are now listening for msgs. */
X  if (old_map != 0) {
X	for (i = 2; i <= NR_TASKS; i++) {
X		/* Check each task looking for one with a pending interrupt. */
X		if ( (old_map>>i) & 1) {
X			/* Task 'i' has a pending interrupt. */
X			n = mini_send(HARDWARE, -i, task_mess[i]);
X			if (n == OK) busy_map &= ~(1 << i);
X		}
X	}
X  }
X
X  /* If a task has just been readied and a user is running, run the task. */
X  if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE))
X	pick_proc();
X}
X
X
X/*===========================================================================*
X *				sys_call				     * 
X *===========================================================================*/
XPUBLIC sys_call(function, caller, src_dest, m_ptr)
Xint function;			/* SEND, RECEIVE, or BOTH */
Xint caller;			/* who is making this call */
Xint src_dest;			/* source to receive from or dest to send to */
Xmessage *m_ptr;			/* pointer to message */
X{
X/* The only system calls that exist in MINIX are sending and receiving
X * messages.  These are done by trapping to the kernel with an INT instruction.
X * The trap is caught and sys_call() is called to send or receive a message (or
X * both).
X */
X
X  register struct proc *rp;
X  int n;
X
X  /* Check for bad system call parameters. */
X  rp = proc_addr(caller);
X  if (src_dest < -NR_TASKS || (src_dest >= NR_PROCS && src_dest != ANY) ) {
X	rp->p_reg[RET_REG] = E_BAD_SRC;
X	return;
X  }
X  if (function != BOTH && caller >= LOW_USER) {
X	rp->p_reg[RET_REG] = E_NO_PERM;	/* users only do BOTH */
X	return;
X  }
X
X  /* The parameters are ok. Do the call. */
X  if (function & SEND) {
X	n = mini_send(caller, src_dest, m_ptr);	/* func = SEND or BOTH */
X	if (function == SEND || n != OK) rp->p_reg[RET_REG] = n;
X	if (n != OK) return;	/* SEND failed */
X  }
X
X  if (function & RECEIVE) {
X	n = mini_rec(caller, src_dest, m_ptr);      /* func = RECEIVE or BOTH */
X	rp->p_reg[RET_REG] = n;
X  }
X}
X
X/*===========================================================================*
X *				mini_send				     * 
X *===========================================================================*/
XPUBLIC int mini_send(caller, dest, m_ptr)
Xint caller;			/* who is trying to send a message? */
Xint dest;			/* to whom is message being sent? */
Xmessage *m_ptr;			/* pointer to message buffer */
X{
X/* Send a message from 'caller' to 'dest'.  If 'dest' is blocked waiting for
X * this message, copy the message to it and unblock 'dest'.  If 'dest' is not
X * waiting at all, or is waiting for another source, queue 'caller'.
X */
X
X  register struct proc *caller_ptr, *dest_ptr, *next_ptr;
X  vir_bytes vb;			/* message buffer pointer as vir_bytes */
X  vir_clicks vlo, vhi;		/* virtual clicks containing message to send */
X  vir_clicks len;		/* length of data segment in clicks */
X
X  /* User processes are only allowed to send to FS and MM.  Check for this. */
X  if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR))
X	return(E_BAD_DEST);
X  caller_ptr = proc_addr(caller);	/* pointer to source's proc entry */
X  dest_ptr = proc_addr(dest);	/* pointer to destination's proc entry */
X  if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST);	/* dead dest */
X
X  /* Check for messages wrapping around top of memory or outside data seg. */
X  len = caller_ptr->p_map[D].mem_len;
X  vb = (vir_bytes) m_ptr;
X  vlo = vb >> CLICK_SHIFT;	/* vir click for bottom of message */
X  vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT;	/* vir click for top of message */
X  if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR);
X
X  /* Check to see if 'dest' is blocked waiting for this message. */
X  if ( (dest_ptr->p_flags & RECEIVING) &&
X		(dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {
X	/* Destination is indeed waiting for this message. */
X	cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, 
X				dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
X	dest_ptr->p_flags &= ~RECEIVING;	/* deblock destination */
X	if (dest_ptr->p_flags == 0) ready(dest_ptr);
X  } else {
X	/* Destination is not waiting.  Block and queue caller. */
X	if (caller == HARDWARE) return(E_OVERRUN);
X	caller_ptr->p_messbuf = m_ptr;
X	caller_ptr->p_flags |= SENDING;
X	unready(caller_ptr);
X
X	/* Process is now blocked.  Put in on the destination's queue. */
X	if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) {
X		dest_ptr->p_callerq = caller_ptr;
X	} else {
X		while (next_ptr->p_sendlink != NIL_PROC)
X			next_ptr = next_ptr->p_sendlink;
X		next_ptr->p_sendlink = caller_ptr;
X	}
X	caller_ptr->p_sendlink = NIL_PROC;
X  }
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				mini_rec				     * 
X *===========================================================================*/
XPRIVATE int mini_rec(caller, src, m_ptr)
Xint caller;			/* process trying to get message */
Xint src;			/* which message source is wanted (or ANY) */
Xmessage *m_ptr;			/* pointer to message buffer */
X{
X/* A process or task wants to get a message.  If one is already queued,
X * acquire it and deblock the sender.  If no message from the desired source
X * is available, block the caller.  No need to check parameters for validity.
X * Users calls are always sendrec(), and mini_send() has checked already.  
X * Calls from the tasks, MM, and FS are trusted.
X */
X
X  register struct proc *caller_ptr, *sender_ptr, *prev_ptr;
X  int sender;
X
X  caller_ptr = proc_addr(caller);	/* pointer to caller's proc structure */
X
X  /* Check to see if a message from desired source is already available. */
X  sender_ptr = caller_ptr->p_callerq;
X  while (sender_ptr != NIL_PROC) {
X	sender = sender_ptr - proc - NR_TASKS;
X	if (src == ANY || src == sender) {
X		/* An acceptable message has been found. */
X		cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf,
X					caller_ptr->p_map[D].mem_phys, m_ptr);
X		sender_ptr->p_flags &= ~SENDING;	/* deblock sender */
X		if (sender_ptr->p_flags == 0) ready(sender_ptr);
X		if (sender_ptr == caller_ptr->p_callerq)
X			caller_ptr->p_callerq = sender_ptr->p_sendlink;
X		else
X			prev_ptr->p_sendlink = sender_ptr->p_sendlink;
X		return(OK);
X	}
X	prev_ptr = sender_ptr;
X	sender_ptr = sender_ptr->p_sendlink;
X  }
X
X  /* No suitable message is available.  Block the process trying to receive. */
X  caller_ptr->p_getfrom = src;
X  caller_ptr->p_messbuf = m_ptr;
X  caller_ptr->p_flags |= RECEIVING;
X  unready(caller_ptr);
X
X  /* If MM has just blocked and there are kernel signals pending, now is the
X   * time to tell MM about them, since it will be able to accept the message.
X   */
X  if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR);
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				pick_proc				     * 
X *===========================================================================*/
XPUBLIC pick_proc()
X{
X/* Decide who to run now. */
X
X  register int q;		/* which queue to use */
X
X  if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q;
X  else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q;
X  else q = USER_Q;
X
X  /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a
X   * special value (IDLE), and set 'proc_ptr' to point to an unused proc table
X   * slot, namely, that of task -1 (HARDWARE), so save() will have somewhere to
X   * deposit the registers when a interrupt occurs on an idle machine.
X   * Record previous process so that when clock tick happens, the clock task
X   * can find out who was running just before it began to run.  (While the
X   * clock task is running, 'cur_proc' = CLOCKTASK. In addition, set 'bill_ptr'
X   * to always point to the process to be billed for CPU time.
X   */
X  prev_proc = cur_proc;
X  if (rdy_head[q] != NIL_PROC) {
X	/* Someone is runnable. */
X	cur_proc = rdy_head[q] - proc - NR_TASKS;
X	proc_ptr = rdy_head[q];
X	if (cur_proc >= LOW_USER) bill_ptr = proc_ptr;
X  } else {
X	/* No one is runnable. */
X	cur_proc = IDLE;
X	proc_ptr = proc_addr(HARDWARE);
X	bill_ptr = proc_ptr;
X  }
X}
X
X/*===========================================================================*
X *				ready					     * 
X *===========================================================================*/
XPUBLIC ready(rp)
Xregister struct proc *rp;	/* this process is now runnable */
X{
X/* Add 'rp' to the end of one of the queues of runnable processes. Three
X * queues are maintained:
X *   TASK_Q   - (highest priority) for runnable tasks
X *   SERVER_Q - (middle priority) for MM and FS only
X *   USER_Q   - (lowest priority) for user processes
X */
X
X  register int q;		/* TASK_Q, SERVER_Q, or USER_Q */
X  int r;
X
X  lock();			/* disable interrupts */
X  r = (rp - proc) - NR_TASKS;	/* task or proc number */
X  q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
X
X  /* See if the relevant queue is empty. */
X  if (rdy_head[q] == NIL_PROC)
X	rdy_head[q] = rp;	/* add to empty queue */
X  else
X	rdy_tail[q]->p_nextready = rp;	/* add to tail of nonempty queue */
X  rdy_tail[q] = rp;		/* new entry has no successor */
X  rp->p_nextready = NIL_PROC;
X  restore();			/* restore interrupts to previous state */
X}
X
X
X/*===========================================================================*
X *				unready					     * 
X *===========================================================================*/
XPUBLIC unready(rp)
Xregister struct proc *rp;	/* this process is no longer runnable */
X{
X/* A process has blocked. */
X
X  register struct proc *xp;
X  int r, q;
X
X  lock();			/* disable interrupts */
X  r = rp - proc - NR_TASKS;
X  q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
X  if ( (xp = rdy_head[q]) == NIL_PROC) return;
X  if (xp == rp) {
X	/* Remove head of queue */
X	rdy_head[q] = xp->p_nextready;
X	pick_proc();
X  } else {
X	/* Search body of queue.  A process can be made unready even if it is
X	 * not running by being sent a signal that kills it.
X	 */
X	while (xp->p_nextready != rp)
X		if ( (xp = xp->p_nextready) == NIL_PROC) return;
X	xp->p_nextready = xp->p_nextready->p_nextready;
X	while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready;
X	rdy_tail[q] = xp;
X  }
X  restore();			/* restore interrupts to previous state */
X}
X
X
X/*===========================================================================*
X *				sched					     * 
X *===========================================================================*/
XPUBLIC sched()
X{
X/* The current process has run too long.  If another low priority (user)
X * process is runnable, put the current process on the end of the user queue,
X * possibly promoting another user to head of the queue.
X */
X
X  lock();			/* disable interrupts */
X  if (rdy_head[USER_Q] == NIL_PROC) {
X	restore();		/* restore interrupts to previous state */
X	return;
X  }
X
X  /* One or more user processes queued. */
X  rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q];
X  rdy_tail[USER_Q] = rdy_head[USER_Q];
X  rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready;
X  rdy_tail[USER_Q]->p_nextready = NIL_PROC;
X  pick_proc();
X  restore();			/* restore interrupts to previous state */
X}
*-*-END-of-k_proc.c-*-*
echo x - k_tty.c
sed 's/^X//' >k_tty.c <<'*-*-END-of-k_tty.c-*-*'
X/*
X * Kernel debugging routine
X */
X
X/*===========================================================================*
X *				wfc					     *
X *===========================================================================*/
X
Xwfc()
X{
X/* routine to wait for a character from the console. used for kernel debugging */
X
X  tty_driver_buf[0] = 0;
X  while(tty_driver_buf[0] == 0);
X  tty_driver_buf[0] = 0;
X}
*-*-END-of-k_tty.c-*-*
echo x - k_wini.c
sed 's/^X//' >k_wini.c <<'*-*-END-of-k_wini.c-*-*'
X/* This file contains a driver for the IBM or DTC winchester controller.
X * It was written by Adri Koppes.
X *
X * The driver supports two operations: read a block and
X * write a block.  It accepts two messages, one for reading and one for
X * writing, both using message format m2 and with the same parameters:
X *
X *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
X * ----------------------------------------------------------------
X * |  DISK_READ | device  | proc nr |  bytes  |  offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | DISK_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
X * ----------------------------------------------------------------
X *
X * The file contains one entry point:
X *
X *   winchester_task:	main entry when system is brought up
X *
X * TODO :
X *	change struct param into struct disk
X *		struct disk should contain controller address of
X *		controller associated with each disk
X *	rewrite init routines from scratch
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "const.h"
X#include "type.h"
X#include "proc.h"
X
X/* I/O Ports used by winchester disk task. */
X#define WIN_DATA       0x320	/* winchester disk controller data register */
X#define WIN_STATUS     0x321	/* winchester disk controller status register */
X#define WIN_SELECT     0x322	/* winchester disk controller select port */
X#define WIN_DMA	       0x323	/* winchester disk controller dma register */
X#define DMA_ADDR       0x006	/* port for low 16 bits of DMA address */
X#define DMA_TOP        0x082	/* port for top 4 bits of 20-bit DMA addr */
X#define DMA_COUNT      0x007	/* port for DMA count (count =  bytes - 1) */
X#define DMA_M2         0x00C	/* DMA status port */
X#define DMA_M1         0x00B	/* DMA status port */
X#define DMA_INIT       0x00A	/* DMA init port */
X
X/* Winchester disk controller command bytes. */
X#define WIN_RECALIBRATE	0x01	/* command for the drive to recalibrate */
X#define WIN_SENSE       0x03	/* command for the controller to get its status */
X#define WIN_READ        0x08	/* command for the drive to read */
X#define WIN_WRITE       0x0a	/* command for the drive to write */
X#define WIN_SPECIFY     0x0C	/* command for the controller to accept params */
X#define WIN_ECC_READ	0x0D	/* command for the controller to read ecc length */
X
X#define DMA_INT		   3 /* Command with dma and interrupt */
X#define INT		   2	/* Command with interrupt, no dma */
X#define NO_DMA_INT	   0	/* Command without dma and interrupt */
X#define CTRL_BYTE	   5 /* Control byte for controller */
X
X/* DMA channel commands. */
X#define DMA_READ        0x47	/* DMA read opcode */
X#define DMA_WRITE       0x4B	/* DMA write opcode */
X
X/* Parameters for the disk drive. */
X#define SECTOR_SIZE      512	/* physical sector size in bytes */
X#define NR_SECTORS      0x11	/* number of sectors per track */
X
X/* Error codes */
X#define ERR		  -1	/* general error */
X
X/* Miscellaneous. */
X#define MAX_ERRORS         4	/* how often to try rd/wt before quitting */
X#define MAX_RESULTS        4	/* max number of bytes controller returns */
X#define NR_DEVICES        10	/* maximum number of drives */
X#define MAX_WIN_RETRY  10000	/* max # times to try to output to WIN */
X#define PART_TABLE     0x1C6	/* IBM partition table starts here in sect 0 */
X#define DEV_PER_DRIVE      5	/* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
X
X/* Variables. */
XPRIVATE struct wini {		/* main drive struct, one entry per drive */
X  int wn_opcode;		/* DISK_READ or DISK_WRITE */
X  int wn_procnr;		/* which proc wanted this operation? */
X  int wn_drive;			/* drive number addressed */
X  int wn_cylinder;		/* cylinder number addressed */
X  int wn_sector;		/* sector addressed */
X  int wn_head;			/* head number addressed */
X  int wn_heads;			/* maximum number of heads */
X  long wn_low;			/* lowest cylinder of partition */
X  long wn_size;			/* size of partition in blocks */
X  int wn_count;			/* byte count */
X  vir_bytes wn_address;		/* user virtual address */
X  char wn_results[MAX_RESULTS];	/* the controller can give lots of output */
X} wini[NR_DEVICES];
X
XPRIVATE int w_need_reset = FALSE;	 /* set to 1 when controller must be reset */
XPRIVATE int nr_drives;		 /* Number of drives */
X
XPRIVATE message w_mess;		/* message buffer for in and out */
X
XPRIVATE int command[6];		/* Common command block */
X
XPRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
X
XPRIVATE struct param {
X	int nr_cyl;		/* Number of cylinders */
X	int nr_heads;		/* Number of heads */
X	int reduced_wr;		/* First cylinder with reduced write current */
X	int wr_precomp;		/* First cylinder with write precompensation */
X	int max_ecc;		/* Maximum ECC burst length */
X} param0, param1;
X
X/*===========================================================================*
X *				winchester_task				     * 
X *===========================================================================*/
XPUBLIC winchester_task()
X{
X/* Main program of the winchester disk driver task. */
X
X  int r, caller, proc_nr;
X
X  /* First initialize the controller */
X  init_param();
X
X  /* Here is the main loop of the disk task.  It waits for a message, carries
X   * it out, and sends a reply.
X   */
X
X  while (TRUE) {
X	/* First wait for a request to read or write a disk block. */
X	receive(ANY, &w_mess);	/* get a request to do some work */
X	if (w_mess.m_source < 0) {
X		printf("winchester task got message from %d ", w_mess.m_source);
X		continue;
X	}
X	caller = w_mess.m_source;
X	proc_nr = w_mess.PROC_NR;
X
X	/* Now carry out the work. */
X	switch(w_mess.m_type) {
X	    case DISK_READ:
X	    case DISK_WRITE:	r = w_do_rdwt(&w_mess);	break;
X	    default:		r = EINVAL;		break;
X	}
X
X	/* Finally, prepare and send the reply message. */
X	w_mess.m_type = TASK_REPLY;	
X	w_mess.REP_PROC_NR = proc_nr;
X
X	w_mess.REP_STATUS = r;	/* # of bytes transferred or error code */
X	send(caller, &w_mess);	/* send reply to caller */
X  }
X}
X
X
X/*===========================================================================*
X *				w_do_rdwt					     * 
X *===========================================================================*/
XPRIVATE int w_do_rdwt(m_ptr)
Xmessage *m_ptr;			/* pointer to read or write w_message */
X{
X/* Carry out a read or write request from the disk. */
X  register struct wini *wn;
X  int r, device, errors = 0;
X  long sector;
X
X  /* Decode the w_message parameters. */
X  device = m_ptr->DEVICE;
X  if (device < 0 || device >= NR_DEVICES)
X	return(EIO);
X  if (m_ptr->COUNT != BLOCK_SIZE)
X	return(EINVAL);
X  wn = &wini[device];		/* 'wn' points to entry for this drive */
X  wn->wn_drive = device/DEV_PER_DRIVE;	/* save drive number */
X  if (wn->wn_drive >= nr_drives)
X	return(EIO);
X  wn->wn_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
X  if (m_ptr->POSITION % BLOCK_SIZE != 0)
X	return(EINVAL);
X  sector = m_ptr->POSITION/SECTOR_SIZE;
X  if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size)
X	return(EOF);
X  sector += wn->wn_low;
X  wn->wn_cylinder = sector / (wn->wn_heads * NR_SECTORS);
X  wn->wn_sector =  (sector % NR_SECTORS);
X  wn->wn_head = (sector % (wn->wn_heads * NR_SECTORS) )/NR_SECTORS;
X  wn->wn_count = m_ptr->COUNT;
X  wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
X  wn->wn_procnr = m_ptr->PROC_NR;
X
X  /* This loop allows a failed operation to be repeated. */
X  while (errors <= MAX_ERRORS) {
X	errors++;		/* increment count once per loop cycle */
X	if (errors >= MAX_ERRORS)
X		return(EIO);
X
X	/* First check to see if a reset is needed. */
X	if (w_need_reset) w_reset();
X
X	/* Now set up the DMA chip. */
X	w_dma_setup(wn);
X
X	/* Perform the transfer. */
X	r = w_transfer(wn);
X	if (r == OK) break;	/* if successful, exit loop */
X
X  }
X
X  return(r == OK ? BLOCK_SIZE : EIO);
X}
X
X
X/*===========================================================================*
X *				w_dma_setup				     * 
X *===========================================================================*/
XPRIVATE w_dma_setup(wn)
Xstruct wini *wn;		/* pointer to the drive struct */
X{
X/* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
X * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
X * to by read from or written to, the byte count minus 1, and a read or write
X * opcode.  This routine sets up the DMA chip.  Note that the chip is not
X * capable of doing a DMA across a 64K boundary (e.g., you can't read a 
X * 512-byte block starting at physical address 65520).
X */
X
X  int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end;
X  vir_bytes vir, ct;
X  phys_bytes user_phys;
X  extern phys_bytes umap();
X
X  mode = (wn->wn_opcode == DISK_READ ? DMA_READ : DMA_WRITE);
X  vir = (vir_bytes) wn->wn_address;
X  ct = (vir_bytes) wn->wn_count;
X  user_phys = umap(proc_addr(wn->wn_procnr), D, vir, ct);
X  low_addr  = (int) user_phys & BYTE;
X  high_addr = (int) (user_phys >>  8) & BYTE;
X  top_addr  = (int) (user_phys >> 16) & BYTE;
X  low_ct  = (int) (ct - 1) & BYTE;
X  high_ct = (int) ( (ct - 1) >> 8) & BYTE;
X
X  /* Check to see if the transfer will require the DMA address counter to
X   * go from one 64K segment to another.  If so, do not even start it, since
X   * the hardware does not carry from bit 15 to bit 16 of the DMA address.
X   * Also check for bad buffer address.  These errors mean FS contains a bug.
X   */
X  if (user_phys == 0) panic("FS gave winchester disk driver bad addr", (int) vir);
X  top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
X  if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr);
X
X  /* Now set up the DMA registers. */
X  lock();
X  port_out(DMA_M2, mode);	/* set the DMA mode */
X  port_out(DMA_M1, mode);	/* set it again */
X  port_out(DMA_ADDR, low_addr);	/* output low-order 8 bits */
X  port_out(DMA_ADDR, high_addr);/* output next 8 bits */
X  port_out(DMA_TOP, top_addr);	/* output highest 4 bits */
X  port_out(DMA_COUNT, low_ct);	/* output low 8 bits of count - 1 */
X  port_out(DMA_COUNT, high_ct);	/* output high 8 bits of count - 1 */
X  unlock();
X}
X
X/*===========================================================================*
X *				w_transfer				     * 
X *===========================================================================*/
XPRIVATE int w_transfer(wn)
Xregister struct wini *wn;	/* pointer to the drive struct */
X{
X/* The drive is now on the proper cylinder.  Read or write 1 block. */
X
X  /* The command is issued by outputing 6 bytes to the controller chip. */
X  command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
X  command[1] = (wn->wn_head | (wn->wn_drive << 5));
X  command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector);
X  command[3] = (wn->wn_cylinder & 0xFF);
X  command[4] = BLOCK_SIZE/SECTOR_SIZE;
X  command[5] = CTRL_BYTE;
X  if (com_out(DMA_INT) != OK)
X	return(ERR);
X
X  port_out(DMA_INIT, 3);	/* initialize DMA */
X  /* Block, waiting for disk interrupt. */
X  receive(HARDWARE, &w_mess);
X
X  /* Get controller status and check for errors. */
X  if (win_results(wn) == OK)
X	return(OK);
X  if ((wn->wn_results[0] & 63) == 24)
X	read_ecc();
X  else
X	w_need_reset = TRUE;
X  return(ERR);
X}
X
X
X/*===========================================================================*
X *				win_results				     * 
X *===========================================================================*/
XPRIVATE int win_results(wn)
Xregister struct wini *wn;	/* pointer to the drive struct */
X{
X/* Extract results from the controller after an operation. */
X
X  register int i;
X  int status;
X
X  port_in(WIN_DATA, &status);
X  port_out(WIN_DMA, 0);
X  if (!(status & 2))
X	return(OK);
X  command[0] = WIN_SENSE;
X  command[1] = (wn->wn_drive << 5);
X  if (com_out(NO_DMA_INT) != OK)
X	return(ERR);
X
X  /* Loop, extracting bytes from WIN */
X  for (i = 0; i < MAX_RESULTS; i++) {
X	if (hd_wait(1) != OK)
X		return(ERR);
X	port_in(WIN_DATA, &status);
X	wn->wn_results[i] = status & BYTE;
X  }
X  if (wn->wn_results[0] & 63)
X	return(ERR);
X  else
X	return(OK);
X}
X
X
X/*===========================================================================*
X *				win_out					     * 
X *===========================================================================*/
XPRIVATE win_out(val)
Xint val;			/* write this byte to winchester disk controller */
X{
X/* Output a byte to the controller.  This is not entirely trivial, since you
X * can only write to it when it is listening, and it decides when to listen.
X * If the controller refuses to listen, the WIN chip is given a hard reset.
X */
X
X  if (w_need_reset) return;	/* if controller is not listening, return */
X  if (hd_wait(1) == OK)
X	port_out(WIN_DATA, val);
X}
X
X/*===========================================================================*
X *				w_reset					     * 
X *===========================================================================*/
XPRIVATE w_reset()
X{
X/* Issue a reset to the controller.  This is done after any catastrophe,
X * like the controller refusing to respond.
X */
X
X  int r = 1, i;
X
X  /* Strobe reset bit low. */
X  port_out(WIN_STATUS, r);
X  for (i = 0; i < 10000; i++) {
X	port_in(WIN_STATUS, &r);
X	if ( (r&01) == 0)break;
X  }
X  if (r & 2) {
X	printf("Hard disk won't reset\n");
X	return(ERR);
X  }
X
X  /* Reset succeeded.  Tell WIN drive parameters. */
X  w_need_reset = FALSE;
X
X  return(win_init());
X}
X
X/*===========================================================================*
X *				win_init				     * 
X *===========================================================================*/
XPRIVATE win_init()
X{
X/* Routine to initialize the drive parameters after boot or reset */
X
X  register int i;
X
X  command[0] = WIN_SPECIFY;		/* Specify some parameters */
X  command[1] = 0;			/* Drive 0 */
X  if (com_out(NO_DMA_INT) != OK)	/* Output command block */
X	return(ERR);
X
X  lock();
X
X  /* No. of cylinders (high byte) */
X  win_out(param0.nr_cyl >> 8);
X
X  /* No. of cylinders (low byte) */
X  win_out(param0.nr_cyl & 0xFF);
X
X  /* No. of heads */
X  win_out(param0.nr_heads);
X
X  /* Start reduced write (high byte) */
X  win_out(param0.reduced_wr >> 8);
X
X  /* Start reduced write (low byte) */
X  win_out(param0.reduced_wr & 0xFF);
X
X  /* Start write precompensation (high byte) */
X  win_out(param0.wr_precomp >> 8);
X
X  /* Start write precompensation (low byte) */
X  win_out(param0.wr_precomp & 0xFF);
X
X  /* Ecc burst length */
X  win_out(param0.max_ecc);
X  unlock();
X
X  if (check_init() != OK) {	/* See if controller accepted parameters */
X	w_need_reset = TRUE;
X	return(ERR);
X  }
X
X  if (nr_drives > 1) {
X	command[1] = (1 << 5);			/* Drive 1 */
X	if (com_out(NO_DMA_INT) != OK)		/* Output command block */
X		return(ERR);
X	lock();
X
X	/* No. of cylinders (high byte) */
X	win_out(param1.nr_cyl >> 8);
X
X	/* No. of cylinders (low byte) */
X	win_out(param1.nr_cyl & 0xFF);
X
X	/* No. of heads */
X	win_out(param1.nr_heads);
X
X	/* Start reduced write (high byte) */
X	win_out(param1.reduced_wr >> 8);
X
X	/* Start reduced write (low byte) */
X	win_out(param1.reduced_wr & 0xFF);
X
X	/* Start write precompensation (high byte) */
X	win_out(param1.wr_precomp >> 8);
X
X	/* Start write precompensation (low byte) */
X	win_out(param1.wr_precomp & 0xFF);
X
X	/* Ecc burst length */
X	win_out(param1.max_ecc);
X	unlock();
X	if (check_init() != OK) {  /* See if controller accepted parameters */
X		w_need_reset = TRUE;
X		return(ERR);
X	}
X  }
X  for (i=0; i<nr_drives; i++) {
X	command[0] = WIN_RECALIBRATE;
X	command[1] = i << 5;
X	command[5] = CTRL_BYTE;
X	if (com_out(INT) != OK)
X		return(ERR);
X	receive(HARDWARE, &w_mess);
X	if (win_results() != OK) {
X		w_need_reset = TRUE;
X		return(ERR);
X	}
X  }
X  return(OK);
X}
X
X/*============================================================================*
X *				check_init				      *
X *============================================================================*/
XPRIVATE check_init()
X{
X/* Routine to check if controller accepted the parameters */
X  int r;
X
X  if (hd_wait(2) == OK) {
X	  port_in(WIN_DATA, &r);
X	  if (r & 2)
X		return(ERR);
X	  else
X		return(OK);
X  }
X}
X
X/*============================================================================*
X *				read_ecc				      *
X *============================================================================*/
XPRIVATE read_ecc()
X{
X/* Read the ecc burst-length and let the controller correct the data */
X
X  int r;
X
X  command[0] = WIN_ECC_READ;
X  if (com_out(NO_DMA_INT) == OK && hd_wait(1) == OK) {
X	port_in(WIN_DATA, &r);
X	if (hd_wait(1) == OK) {
X		port_in(WIN_DATA, &r);
X		if (r & 1)
X			w_need_reset = TRUE;
X	}
X  }
X  return(ERR);
X}
X
X/*============================================================================*
X *				hd_wait					      *
X *============================================================================*/
XPRIVATE hd_wait(bit)
Xregister int bit;
X{
X/* Wait until the controller is ready to receive a command or send status */
X
X  register int i = 0;
X  int r;
X
X  do {
X	port_in(WIN_STATUS, &r);
X	r &= bit;
X  } while ((i++ < MAX_WIN_RETRY) && !r);
X
X  if (i >= MAX_WIN_RETRY) {
X	w_need_reset = TRUE;
X	return(ERR);
X  } else
X	return(OK);
X}
X
X/*============================================================================*
X *				com_out					      *
X *============================================================================*/
XPRIVATE com_out(mode)
Xint mode;
X{
X/* Output the command block to the winchester controller and return status */
X
X	register int i = 0;
X	int r;
X
X	port_out(WIN_SELECT, mode);
X	port_out(WIN_DMA, mode);
X	for (i=0; i<MAX_WIN_RETRY; i++) {
X		port_in(WIN_STATUS, &r);
X		if ((r & 0x0F) == 0x0D)
X			break;
X	}
X	if (i == MAX_WIN_RETRY) {
X		w_need_reset = TRUE;
X		return(ERR);
X	}
X	lock();
X	for (i=0; i<6; i++) {
X		port_out(WIN_DATA, command[i]);
X		r = hd_wait(1);
X		if (r != OK) break;
X		port_in(WIN_STATUS,&r);
X		if ( (r&7) != 5) break;
X	}
X	unlock();
X	return(OK);
X}
X
X/*============================================================================*
X *				init_params				      *
X *============================================================================*/
XPRIVATE init_params()
X{
X/* This routine is called at startup to initialize the partition table,
X * the number of drives and the controller
X*/
X  unsigned int i, segment, offset;
X  int type_0, type_1;
X  phys_bytes address;
X  extern phys_bytes umap();
X  extern int vec_table[];
X
X  /* Read the switches from the controller */
X  port_in(WIN_SELECT, &i);
X
X  /* Calculate the drive types */
X  type_0 = ((i >> 2) & 3) + ((i>>4) & 0xc);
X  type_1 = (i & 3) + ((i>>2) & 0xc);
X
X  /* Copy the parameter vector from the saved vector table */
X  offset = vec_table[2 * 0x41];
X  segment = vec_table[2 * 0x41 + 1];
X
X  /* Calculate the address off the parameters and copy them to buf */
X  address = ((long)segment << 4) + offset;
X  phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 64), 64L);
X
X  /* Copy the parameters to the structures */
X  copy_param((&buf[type_0 * 16]), &param0);
X  copy_param((&buf[type_1 * 16]), &param1);
X
X  /* Get the nummer of drives from the bios */
X  phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L);
X  nr_drives = (int) *buf;
X
X  /* Set the parameters in the drive structure */
X  for (i=0; i<5; i++)
X	wini[i].wn_heads = param0.nr_heads;
X  wini[0].wn_low = wini[5].wn_low = 0L;
X  wini[0].wn_size = (long)((long)param0.nr_cyl * (long)param0.nr_heads * (long)NR_SECTORS);
X  for (i=5; i<10; i++)
X	wini[i].wn_heads = param1.nr_heads;
X  wini[5].wn_size = (long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS);
X
X
X  /* Initialize the controller */
X  if ((nr_drives > 0) && (win_init() != OK))
X		nr_drives = 0;
X
Xif (nr_drives == 0) {
X	printf("No winchester drive ?????\n");
X	wfc();
X}
X
X  /* Read the partition table for each drive and save them */
X  for (i = 0; i < nr_drives; i++) {
X	w_mess.DEVICE = i * 5;
X	w_mess.POSITION = 0L;
X	w_mess.COUNT = BLOCK_SIZE;
X	w_mess.ADDRESS = (char *) buf;
X	w_mess.PROC_NR = WINCHESTER;
X	w_mess.m_type = DISK_READ;
X	if (w_do_rdwt(&w_mess) != BLOCK_SIZE)
X		panic("Can't read partition table of winchester ", i);
X	copy_prt(i * 5);
X  }
X}
X
X/*============================================================================*
X *				copy_params				      *
X *============================================================================*/
XPRIVATE copy_params(src, dest)
Xregister unsigned char *src;
Xregister struct param *dest;
X{
X/* This routine copies the parameters from src to dest
X * and sets the parameters for partition 0 and 5
X*/
X
X  dest->nr_cyl = *(int *)src;
X  dest->nr_heads = (int)src[2];
X  dest->reduced_wr = *(int *)&src[3];
X  dest->wr_precomp = *(int *)&src[5];
X  dest->max_ecc = (int)src[7];
X}
X
X/*============================================================================*
X *				copy_prt				      *
X *============================================================================*/
XPRIVATE copy_prt(drive)
Xint drive;
X{
X/* This routine copies the partition table for the selected drive to
X * the variables wn_low and wn_size
X */
X
X  register int i, offset;
X  struct wini *wn;
X  long adjust;
X
X  for (i=0; i<4; i++) {
X	adjust = 0;
X	wn = &wini[i + drive + 1];
X	offset = PART_TABLE + i * 0x10;
X	wn->wn_low = *(long *)&buf[offset];
X	if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) {
X		adjust = wn->wn_low;
X		wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE);
X		adjust = wn->wn_low - adjust;
X	}
X	wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust;
X  }
X  sort(&wini[drive + 1]);
X}
X
Xsort(wn)
Xregister struct wini *wn;
X{
X  register int i,j;
X
X  for (i=0; i<4; i++)
X	for (j=0; j<3; j++)
X		if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0))
X			swap(&wn[j], &wn[j+1]);
X		else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)
X			swap(&wn[j], &wn[j+1]);
X}
X
Xswap(first, second)
Xregister struct wini *first, *second;
X{
X  register struct wini tmp;
X
X  tmp = *first;
X  *first = *second;
X  *second = tmp;
X}
*-*-END-of-k_wini.c-*-*
echo x - m_make.
sed 's/^X//' >m_make. <<'*-*-END-of-m_make.-*-*'
XCFLAGS = -Di8088 -w -F -T.
Xh=../h
Xl=/usr/lib
X
Xobj =	main.s forkexit.s break.s exec.s signal.s getset.s  \
X	alloc.s utility.s table.s putc.s 
X
Xmm:	makefile    $l/head.s $(obj) $l/libc.a $l/end.s
X#	@echo "Start linking MM.  /lib/cem will be removed to make space on RAM disk"
X#	@rm -f /lib/cem /tmp/*
X#	@asld -o mm $l/head.s $(obj) $l/libc.a $l/end.s
X#	@echo "MM done.  Please restore /lib/cem manually"
X	@echo "Start linking MM.
X	@asld -o mm -T. $l/head.s $(obj) $l/libc.a $l/end.s
X	@echo "MM done.
X
X
Xalloc.s:	const.h $h/const.h $h/type.h
X
Xbreak.s:	const.h $h/const.h $h/type.h
Xbreak.s:	$h/error.h
Xbreak.s:	$h/signal.h
Xbreak.s:	glo.h
Xbreak.s:	mproc.h
Xbreak.s:	param.h
X
Xexec.s:		const.h $h/const.h $h/type.h
Xexec.s:		$h/callnr.h
Xexec.s:		$h/error.h
Xexec.s:		$h/stat.h
Xexec.s:		glo.h
Xexec.s:		mproc.h
Xexec.s:		param.h
X
Xforkexit.s:	const.h $h/const.h $h/type.h
Xforkexit.s:	$h/callnr.h
Xforkexit.s:	$h/error.h
Xforkexit.s:	glo.h
Xforkexit.s:	mproc.h
Xforkexit.s:	param.h
X
Xgetset.s:	const.h $h/const.h $h/type.h
Xgetset.s:	$h/callnr.h
Xgetset.s:	$h/error.h
Xgetset.s:	glo.h
Xgetset.s:	mproc.h
Xgetset.s:	param.h
X
Xmain.s:		const.h $h/const.h $h/type.h
Xmain.s:		$h/callnr.h
Xmain.s:		$h/com.h
Xmain.s:		$h/error.h
Xmain.s:		glo.h
Xmain.s:		mproc.h
Xmain.s:		param.h
X
Xputc.s:		$h/const.h $h/type.h
Xputc.s:		$h/com.h
X
Xsignal.s:	const.h $h/const.h $h/type.h
Xsignal.s:	$h/callnr.h
Xsignal.s:	$h/com.h
Xsignal.s:	$h/error.h
Xsignal.s:	$h/signal.h
Xsignal.s:	$h/stat.h
Xsignal.s:	glo.h
Xsignal.s:	mproc.h
Xsignal.s:	param.h
X
Xtable.s:	const.h $h/const.h $h/type.h
Xtable.s:	$h/callnr.h
Xtable.s:	glo.h
Xtable.s:	mproc.h
Xtable.s:	param.h
X
Xutility.s:	const.h $h/const.h $h/type.h
Xutility.s:	$h/callnr.h
Xutility.s:	$h/com.h
Xutility.s:	$h/error.h
Xutility.s:	$h/stat.h
Xutility.s:	glo.h
Xutility.s:	mproc.h
*-*-END-of-m_make.-*-*
echo x - readme.txt
sed 's/^X//' >readme.txt <<'*-*-END-of-readme.txt-*-*'
X
XThis directorry should contain the following files :
X
X   file		to be placed in		decription
X   ----         ---------------         ----------
X
Xk_floppy.c	kernel/floppy.c		changed startup time for M24
X					added timeout
X					changes startup time if M24
X
Xk_wini.c	kernel/wini.c		changed com_out for M24
X
Xdiskpart.c	tools/diskpart.c	utility to partition harddisk
X
Xh_const.h	h/const.h		BOOT_DEV set to /dev/hd2
X
Xk_const.h	kernel/const.h		added hardware types
X
Xk_glo.h		kernel/glo.h		changed pc_at into hw_id
X
Xk_main.c	kernel/main.c		added m24 detection
X
Xk_proc.c	kernel/proc.c		changed test on pc_at
X
Xk_tty.c		kernel/tty.c		added wfc()
X
*-*-END-of-readme.txt-*-*
echo x - t_diskp.c
sed 's/^X//' >t_diskp.c <<'*-*-END-of-t_diskp.c-*-*'
X/*
X * Diskpart. Display and modify partition table
X *           Written by Jakob Schripsema
X *
X * First run the DOS utilities FDISK and FORMAT. FDISK
X * puts the boot code in sector 0.
X * Then run diskpart
X *
X *	diskpart /dev/hdx	(MINIX)
X *	diskpart x:		(DOS)
X *
X * Compiling
X *
X *	cc -o diskpart -DUNIX diskpart.c	(MINIX)
X *	cl -DDOS diskpart.c			(DOS with MS C compiler)
X */
X 
X#include <stdio.h>
X
X#ifdef DOS
X#include <dos.h>
X#endif
X
X/*
X * Constants
X */
X
X#define	NHEAD		4		/* # heads		*/
X#define	NSEC		17		/* sectors / track	*/
X#define SECSIZE		512		/* sector size		*/
X#define	OK		0
X#define	ERR		1
X#define	TABLEOFFSET	0x1be		/* offset in boot sector*/
X/*
X * Description of entry in partition table
X */
X
Xstruct part_entry {
X	char	bootind;	/* boot indicator 0/0x80	*/
X	char	start_head;	/* head value for first sector	*/
X	char	start_sec;	/* sector value for first sector*/
X	char	start_cyl;	/* track value for first sector	*/
X	char	sysind;		/* system indicator 00=?? 01=DOS*/
X	char	last_head;	/* head value for last sector	*/
X	char	last_sec;	/* sector value for last sector	*/
X	char	last_cyl;	/* track value for last sector	*/
X	long	lowsec;		/* logical first sector		*/
X	long	size;		/* size of partion in sectors	*/
X};
X	
X/*
X * Globals
X */
X
Xchar	secbuf[SECSIZE];
Xchar	*devname;
Xchar	*dosstr  = "  DOS  ";
Xchar	*ndosstr = "Non-DOS";
X
X#ifdef DOS
Xunion	REGS	regs;
Xstruct	SREGS	sregs;
Xint	drivenum;
X#endif
X
X#ifdef UNIX
Xint	devfd;
X#endif
X
Xmain(argc,argv)
Xint	argc;
Xchar	*argv[];
X{
X	char	ch;
X
X	/* init */
X
X	if (argc != 2) {
X		printf("Usage: diskpart drive\n");
X		exit(1);
X	}
X
X	devname = argv[1];
X	getboot(secbuf);	/* get boot sector	*/
X
X	do {
X		dpl_partitions();
X		printf("\nchange write hexdump quit (c/w/x/q) ? ");
X		ch = get_a_char();
X		putchar('\n');
X		switch (ch) {
X			case 'c' :
X				change_table();
X				break;
X			case 'w' :
X				if (chk_table() == OK) {
X					putboot(secbuf);
X					exit(0);
X				}
X				break;
X			case 'x' :
X				dump_table();
X				break;
X			case 'q' :
X				exit(0);
X			default :
X				printf(" %c ????\n",ch);
X		}
X	}
X	while (1);
X}
X
X/*
X * Read boot sector
X */
X
X#ifdef DOS
X
Xgetboot(buffer)
Xchar	*buffer;
X{
X	segread(&sregs);	/* get ds */
X
X	if (devname[1] != ':') {
X		printf("Invalid drive %s\n",devname);
X		exit(1);
X	}
X
X	if (*devname >= 'a')
X		*devname += 'A' - 'a';
X	drivenum = (*devname - 'C') & 0xff;
X	if (drivenum < 0 || drivenum > 7) {
X		printf("Funny drive number %d\n",drivenum);
X		exit(1);
X	}
Xprintf("Drive number %d\n",drivenum);
X	regs.x.ax = 0x201;	/* read 1 sectors	*/
X	regs.h.ch = 0;		/* track		*/
X	regs.h.cl = 1;		/* first sector = 1	*/
X	regs.h.dh = 0;		/* head = 0		*/
X	regs.h.dl = 0x80+drivenum;/* drive = 0		*/
X	sregs.es = sregs.ds;	/* buffer address	*/
X	regs.x.bx = (int)secbuf;
X
X	int86x(0x13,&regs,&regs,&sregs);
X	if (regs.x.cflag)
X	{
X		printf("Cannot read boot sector\n");
X		exit(1);
X	}
X}
X#endif
X
X#ifdef UNIX
X
Xgetboot(buffer)
Xchar	*buffer;
X{
X	devfd = open(devname,2);
X	if (devfd < 0) {
X		printf("Cannot open device %s\n",devname);
X		exit(1);
X	}
X	if (read(devfd,buffer,SECSIZE) != SECSIZE) {
X		printf("Cannot read boot sector\n");
X		exit(2);
X	}
X}
X#endif
X
X/*
X * Write boot sector
X */
X
X#ifdef DOS
X
Xputboot(buffer)
Xchar	*buffer;
X{
X	regs.x.ax = 0x301;	/* read 1 sectors	*/
X	regs.h.ch = 0;		/* track		*/
X	regs.h.cl = 1;		/* first sector = 1	*/
X	regs.h.dh = 0;		/* head = 0		*/
X	regs.h.dl = 0x80+drivenum;/* drive = 0		*/
X	sregs.es = sregs.ds;	/* buffer address	*/
X	regs.x.bx = (int)secbuf;
X
X	int86x(0x13,&regs,&regs,&sregs);
X	if (regs.x.cflag)
X	{
X		printf("Cannot write boot sector\n");
X		exit(1);
X	}
X}
X#endif
X
X#ifdef UNIX
X
Xputboot(buffer)
Xchar	*buffer;
X{
X	int r;
X
X	if (lseek(devfd,0L,0) < 0) {
X		printf("Seek error during write\n");
X		exit(1);
X	}
X	if (write(devfd,buffer,SECSIZE) != SECSIZE) {
X		printf("Write error\n");
X		exit(1);
X	}
X	sync();
X}
X#endif
X
X/*
X * Dump partition table
X */
X
Xdump_table()
X{
X	struct	part_entry	*pe;
X	int	i;
X
X	pe = (struct part_entry *)&secbuf[TABLEOFFSET];
X	printf("\n       --first---     ---last---\n");
X	printf("Prt ac hd sec cyl sys hd sec cyl    low     size\n");
X	for (i = 1 ; i < 5 ; i++) {
X		printf(" %x  %2x  %x  %2x  %2x  %2x  %x  %2x  %2x %6X%9X\n",
X			i,
X			pe->bootind & 0xff,
X			pe->start_head & 0xff,
X			pe->start_sec & 0xff,
X			pe->start_cyl & 0xff,
X			pe->sysind & 0xff,
X			pe->last_head & 0xff,
X			pe->last_sec & 0xff,
X			pe->last_cyl & 0xff,
X			pe->lowsec,
X			pe->size);
X		pe++;
X	}
X}
X/*
X * Display partition table
X */
X
Xdpl_partitions()
X{
X	struct	part_entry	*pe;
X	int	i;
X
X	printf("\nPartition      Type     Begin End  Active\n");
X	pe = (struct part_entry *)&secbuf[TABLEOFFSET];
X	for (i = 1 ; i <= 4 ; i++) {
X		dpl_entry(i,pe);
X		pe++;
X	}
X}
X
X/*
X * Display an entry
X */
X
Xdpl_entry(number,entry)
Xint	number;
Xstruct	part_entry	*entry;
X{
X	int	low_cyl,high_cyl,temp;
X	char	*typestring;
X	char	active;
X
X	if (entry->sysind == 0x01)
X		typestring = dosstr;
X	else
X		typestring = ndosstr;
X	printf("%5d         %s  ",number,typestring);
X	temp = entry->start_sec & 0xc0;
X	low_cyl = (entry->start_cyl & 0xff) + (temp << 2);
X	temp = entry->last_sec & 0xc0;
X	high_cyl = (entry->last_cyl & 0xff) + (temp << 2);
X	printf("%4d  %4d",low_cyl,high_cyl);
X	if ((entry->bootind & 0xff) == 0x80)
X		active = 'A';
X	else
X		active = 'N';
X	printf("     %c\n",active);
X}
X
X/*
X * Check partition table
X */
X
Xchk_table()
X{
X	struct part_entry *pe;
X	int i;
X	int active;
X	long limit;
X
X	pe = (struct part_entry *)&secbuf[TABLEOFFSET];
X	limit = 0L;
X	active = 0;
X	for (i = 1 ; i < 5 ; i++) {
X		if (pe->size == 0L)
X			return(OK);
X		if (pe->lowsec <= limit) {
X			printf("Overlap between part. %d and %d\n",i,i-1);
X			return(ERR);
X		}
X		limit = pe->lowsec + pe->size - 1L;
X		if (pe->bootind == 0x80)
X			active++;
X		pe++;
X	}
X	if (active > 1) {
X		printf("%d active partitions\n",active);
X		return(ERR);
X	}
X	return(OK);
X}
X/*
X * Check entry
X * head/sector/track info must match logical sector number
X * Used to check 'foreign' partition table during debugging
X */
X
Xchk_entry(entry)
Xstruct	part_entry	*entry;
X{
X	char	head;
X	char	sector;
X	char	track;
X
X	sec_to_hst(entry->lowsec,&head,&sector,&track);
X	if (	(head != entry->start_head) ||
X		(sector != entry->start_sec) ||
X		(track != entry->start_cyl))
X		return(ERR);
X	sec_to_hst(entry->lowsec + entry->size - 1L,&head,&sector,&track);
X	if (	(head != entry->last_head) ||
X		(sector != entry->last_sec) ||
X		(track != entry->last_cyl))
X		return(ERR);
X	return(OK);
X}
X
X/*
X * Convert a logical sector number to
X * head / sector / track
X */
X
Xsec_to_hst(logsec,hd,sec,cyl)
Xlong	logsec;
Xchar	*hd,*sec,*cyl;
X{
X	int	bigcyl;
X
X	bigcyl = logsec / (NHEAD * NSEC);
X	*sec = (logsec % NSEC) + 1 + ((bigcyl >> 2) & 0xc0);
X	*cyl = bigcyl & 0xff;
X	*hd = (logsec % (NHEAD * NSEC)) / NSEC;
X}
X
X/*
X * change partition table
X */
X
Xchange_table()
X{
X	struct	part_entry	*pe;
X	int	i,temp,low_cyl,high_cyl;
X	char	ch;
X
X	pe = (struct part_entry *)&secbuf[TABLEOFFSET];
X	for (i = 1 ; i <= 4 ; i++) {
X		temp = pe->start_sec & 0xc0;
X		low_cyl = (pe->start_cyl & 0xff) + (temp << 2);
X		temp = pe->last_sec & 0xc0;
X		high_cyl = (pe->last_cyl & 0xff) + (temp << 2);
X		printf("Partition %d from %d to %d. Change ? ",
X			i,low_cyl,high_cyl);
X		ch = get_a_char();
X		if (ch == 'y' || ch == 'Y')
X			get_partition(pe);
X		pe++;
X	}
X	putchar('\n');
X}
X
X/*
X * Get partition info : first & last cylinder
X */
X
Xget_partition(entry)
Xstruct part_entry *entry;
X{
X	char	buf[80];
X	int	first,last;
X	long	low,high;
X	char	ch;
X
X	printf("	First cylinder ? ");
X	gets(buf);
X	sscanf(buf,"%d",&first);
X	printf("	Last cylinder ? ");
X	gets(buf);
X	sscanf(buf,"%d",&last);;
X	if (first == 0 && last == 0) {
X		entry->bootind = 0;
X		entry->start_head = 0;
X		entry->start_sec = 0;
X		entry->start_cyl = 0;
X		entry->sysind = 0;
X		entry->last_head = 0;
X		entry->last_sec  = 0;
X		entry->last_cyl = 0;
X		entry->lowsec = 0L;
X		entry->size = 0L ;
X		return;
X	}
X	low = first & 0xffff;
X	low = low * NSEC * NHEAD;
X	if (low == 0)
X		low = 1; /* sec0 is master boot record */
X	high = last & 0xffff;
X	high = (high + 1)*NSEC*NHEAD - 1;
X	entry->lowsec = low;
X	entry->size = high - low + 1;
X	sec_to_hst(low,
X		&entry->start_head,
X		&entry->start_sec,
X		&entry->start_cyl);
X	sec_to_hst(high,
X		&entry->last_head,
X		&entry->last_sec,
X		&entry->last_cyl);
X	printf("	DOS partition ? ");
X	ch = get_a_char();
X	if (ch == 'y' || ch == 'Y')
X		entry->sysind = 1;
X	else
X		entry->sysind = 0;
X	printf("	Active partition ? ");
X	ch = get_a_char();
X	if (ch == 'y' || ch == 'Y')
X		entry->bootind = 0x80;
X	else
X		entry->bootind = 0;
X}
X
X/*
X * Read 1 character and discard rest of line
X */
X
Xget_a_char()
X{
X	char	ch;
X
X	ch = getchar();
X	if (ch != '\n')
X		while (getchar() != '\n');
X	return(ch);
X}
X
*-*-END-of-t_diskp.c-*-*
echo x - t_mkboot.
sed 's/^X//' >t_mkboot. <<'*-*-END-of-t_mkboot.-*-*'
Xbuild bootblok ../kernel/kernel ../mm/mm ../fs/fs init fsck /dev/fd0
*-*-END-of-t_mkboot.-*-*
exit