[comp.os.minix] Reposting of bios_wini.c; BITNET ate the original

ast@cs.vu.nl (Andy Tanenbaum) (11/16/88)

At long last here is a routine I should have written two years ago: a hard
disk driver that uses the BIOS.  As readers of this group are all too well
aware, we have had endless discussions about how to deal with all manner of
non-IBM compatible disks.  This problem arises because xt_wini.c and at_wini.c
directly address the hardware, and a lot of hardware around is different
from IBM's.

The new file bios_wini.c can be copied to wini.c and used as an alternative to
xt_wini.c and at_wini.c.  It makes 0x13 BIOS calls, so it should work on just
about any machine claimed to be IBM compatible.  Although many vendors make no
attempt to have the hardware compatible, most of them do attempt to get the
BIOS right.  The 0x13 BIOS call asks for a block, and then just waits.  It is
not interrupt driven.  While this is not optimal, especially when there are
background jobs running, it should be highly portable.

I had to change three other kernel files: glo.h, main.c and klib88.s.
Below are cdiffs for glo.h, main.c and klib88.s.  These will be in the next 
release, so you might as well install them now.  They also work fine with
xt_wini.c and at_wini.c.  The crcs for glo.h main.c and klib88.s after patching
and bios_wini.c are:


27591  16189 bios_wini.c
58375    973 glo.h
19767  26396 klib88.s
52671  11169 main.c

The value of bios_wini.c is that it is supposed to work on all machines.
So far all I have tested it on is a Zenith Z-248 and an IBM XT.  I would
greatly appreciate it if LOTS of people would test it (even if your current
disk driver works fine) and mail me results.    Describe your machine, disk,
and tell if it worked.  A short message is fine.  If it works on (almost) all
machines, I will use it in the boot diskette in the next release, thus
eliminating the difference between the XT and AT version.  This will make
life much simpler for everyone.


: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'bios_wini.c'
sed 's/^X//' > 'bios_wini.c' << '+ END-OF-FILE ''bios_wini.c'
X/* This file contains a hard disk driver that uses the ROM BIOS.  It makes
X * a call and just waits for the transfer to happen.  It is not interrupt
X * driven and thus will have poor performance.  The advantage is that it should
X * work on virtually any PC, XT, 386, PS/2 or clone.  The generic boot
X * diskette uses this driver.  It is suggested that all MINIX users try the
X * other drivers, and use this one only as a last resort, if all else fails.
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 */
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/* Error codes */
X#define ERR		  -1	/* general error */
X
X/* Parameters for the disk drive. */
X#define MAX_DRIVES         1
X#define NR_DEVICES        10	/* this driver supports 2 drives (hd0 - hd9)*/
X#define SECTOR_SIZE	 512	/* physical sector size in bytes */
X#define NR_SECTORS	  17	/* number of sectors per track */
X#define BASE            1536	/* base address of kernel */
X
X/* BIOS parameters */
X#define BIOS_ASK        0x08	/* opcode for asking BIOS for parameters */
X#define BIOS_RESET      0x00	/* opcode for resetting disk BIOS */
X#define BIOS_READ       0x02	/* opcode for BIOS read */
X#define BIOS_WRITE      0x03	/* opcode for BIOS write */
X#define DRIVE           0x80	/* BIOS code for drive 0 */
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#define WIN_SELECT     0x322	/* XT winchester disk controller select port */
X
XPRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
XPRIVATE message w_mess;		/* message buffer for in and out */
XPRIVATE message w_extra;	/* message buffer for reading partition table*/
XPRIVATE int initialized[2] = {FALSE, FALSE};
XPRIVATE putback;
X
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_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  int wn_maxsec;		/* maximum number of sectors per track */
X  long wn_low;			/* lowest cylinder of partition */
X  long wn_size;			/* size of partition in sectors */
X  int wn_count;			/* byte count */
X  int wn_drive;			/* 0x80 or 0x81 */
X  vir_bytes wn_address;		/* user virtual address */
X} wini[NR_DEVICES];
X
XPRIVATE struct param {
X	int nr_cyl;		/* Number of cylinders */
X	int nr_heads;		/* Number of heads */
X} param0, param1;
X
Xint nr_drives;
Xextern int vec_table[];
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, drive;
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\n",w_mess.m_source);
X		continue;
X	}
X
X	caller = w_mess.m_source;
X	proc_nr = w_mess.PROC_NR;
X	drive = w_mess.DEVICE/5;
X
X	/* Initialize each drive at the moment it is first used. */
X	if (initialized[drive] == FALSE) {
X		if (putback++ == 0) replace();
X		if (pc_at || ps)
X			at_init_params(drive);
X		else
X			xt_init_params(drive);
X		initialized[drive] = TRUE;
X	}
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  struct proc *rp;
X  vir_bytes vir, ct;
X  unsigned locyl, hicyl, c1, c2, c3;
X  int r, device, errors = 0;
X  long sector;
X  phys_bytes user_phys;
X  extern phys_bytes umap();
X
X  /* Decode the w_message parameters. */
X  device = m_ptr->DEVICE;	/* minor device #.  1-4 are partitions */
X  if (device < 0 || device >= NR_DEVICES) return(EIO);
X  if (m_ptr->COUNT != BLOCK_SIZE) return(EINVAL);
X  wn = &wini[device];		/* 'wn' points to entry for this drive */
X
X  /* Set opcode to BIOS_READ or BIOS_WRITE. Check for bad starting addr. */
X  wn->wn_opcode = (m_ptr->m_type == DISK_WRITE ? BIOS_WRITE : BIOS_READ);
X  if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL);
X
X  /* Calculate the physical parameters */
X  sector = m_ptr->POSITION/SECTOR_SIZE;	/* relative sector within partition */
X  if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size) return(EOF);
X  sector += wn->wn_low;		/* absolute sector number */
X  wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec);
X  wn->wn_sector =  (sector % wn->wn_maxsec);
X  wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec;
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  /* Do the transfer */
X  rp = proc_addr(wn->wn_procnr);
X  vir = (vir_bytes) wn->wn_address;
X  ct = (vir_bytes) wn->wn_count;
X  user_phys = umap(rp, D, vir, ct);
X  Ax = (wn->wn_opcode << 8) | 2;		/* read or write 2 sectors */
X  Bx = (unsigned) (user_phys & 0xF);	/* bx = low order 4 bits */
X  Es = (unsigned) ((user_phys >> 4) & 0xFFFF);
X  hicyl = (wn->wn_cylinder >> 8) & 03;	/* two high-order bits */
X  locyl = (wn->wn_cylinder & 0xFF);	/* 8 low-order bits */
X  c1 = (locyl<<8);
X  c2 = (hicyl<<6);
X  c3 = ((unsigned) wn->wn_sector) + 1;
X  Cx = c1 | c2 | c3;
X  Dx = (wn->wn_head<<8) | wn->wn_drive;
X  bios13();
X  r = (Ax >> 8) & 0xFF;
X  return(r == 0 ? BLOCK_SIZE : EIO);
X}
X
X
X/*===========================================================================*
X *				at_init_params				     *
X *===========================================================================*/
XPRIVATE at_init_params(drive)
Xint drive;			/* 0 or 1 */
X{
X/* This routine is called to initialize the partition table, etc. */
X
X  unsigned int i, segment, offset;
X  phys_bytes address;
X  extern phys_bytes umap();
X
X  /* Copy the parameter vector from the saved vector table */
X  offset = vec_table[2 * (0x41 + 5*drive)];
X  segment = vec_table[2 * (0x41 + 5*drive) + 1];
X  address = ((long)segment << 4) + offset;
X  phys_copy(address, umap(proc_addr(WINCHESTER), D, (vir_bytes)buf, 16), 16L);
X  at_copy_params(buf, &wini[5*drive]);
X
X  /* Set the parameters in the drive  */
X  wini[0].wn_low = wini[5].wn_low = 0L;
X  for (i = 0; i < NR_DEVICES; i++) wini[i].wn_drive = DRIVE + i/5;
X
X  /* Read the partition table for this drive and save it */
X  w_extra.DEVICE = drive * 5;
X  w_extra.POSITION = 0L;
X  w_extra.COUNT = BLOCK_SIZE;
X  w_extra.ADDRESS = (char *) buf;
X  w_extra.PROC_NR = WINCHESTER;
X  w_extra.m_type = DISK_READ;
X  if (w_do_rdwt(&w_extra) != BLOCK_SIZE) {
X  	printf("Can't read partition table on winchester %d\n",drive);
X  	delay();
X	return;
X  }
X  if (buf[510] != 0x55 || buf[511] != 0xAA) {
X  	printf("Invalid partition table on winchester %d\n",drive);
X  	delay();
X	return;
X  }
X  copy_prt((int) drive*5);
X}
X
X/*===========================================================================*
X *				at_copy_params				     *
X *===========================================================================*/
XPRIVATE at_copy_params(src, dest)
Xregister unsigned char *src;
Xregister struct wini *dest;
X{
X/* This routine copies the parameters from src to dest
X * and sets the parameters for partition 0 and 5
X */
X  register int i;
X  long cyl, heads, sectors;
X
X  for (i=0; i<5; i++) {
X	dest[i].wn_heads = (int)src[2];
X	dest[i].wn_maxsec = (int)src[14];
X  }
X  cyl = (long)(*(int *)src);
X  heads = (long)dest[0].wn_heads;
X  sectors = (long)dest[0].wn_maxsec;
X  dest[0].wn_size = cyl * heads * sectors;
X}
X
X/*============================================================================*
X *				xt_init_params				      *
X *===========================================================================*/
XPRIVATE xt_init_params(drive)
Xint drive;			/* for future use; at present only 1 drive */
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
X  /* Get the number of drives from the bios */
X  phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L);
X  nr_drives = (int) *buf > MAX_DRIVES ? MAX_DRIVES : (int) *buf;
X
X  /* Read the switches from the controller */
X  port_in(WIN_SELECT, &i);
X
X#if AUTO_BIOS
X  /* Get the drive parameters from sector zero of the drive if the */
X  /* autoconfig mode of the controller has been selected */
X
X  if(i & AUTO_ENABLE) {
X
X	/* Set up phony parameters so that we can read the first sector
X	 * from winchester. All drives will have one cylinder and one head
X	 * but set up initially to the mini scribe drives from IBM,
X	 */
X	param1.nr_cyl = param0.nr_cyl = AUTO_CYLS;
X	param1.nr_heads = param0.nr_heads = AUTO_HEADS;
X	wini[DEV_PER_DRIVE].wn_heads = wini[0].wn_heads = param0.nr_heads;
X	wini[DEV_PER_DRIVE].wn_low = wini[0].wn_low = 0L;
X	wini[DEV_PER_DRIVE].wn_size = wini[0].wn_size
X		   = (long)AUTO_CYLS * (long)AUTO_HEADS * (long)NR_SECTORS;
X
X	if (nr_drives > 1) {
X	  /* Generate request to read the first sector from the winchester */
X	  w_extra.DEVICE = DEV_PER_DRIVE;
X	  w_extra.POSITION = 0L;
X	  w_extra.COUNT = BLOCK_SIZE;
X	  w_extra.ADDRESS = (char *) buf;
X	  w_extra.PROC_NR = WINCHESTER;
X	  w_extra.m_type = DISK_READ;
X	  if(w_do_rdwt(&w_extra) != BLOCK_SIZE)
X		panic("cannot read drive parameters from winchester",DEV_PER_DRIVE);
X
X	  /* copy the parameter tables into the structures for later use */
X	  xt_copy_params(&buf[AUTO_PARAM], &param1);
X
X	}
X
X	/* Generate the request to read the first sector from the winchester.*/
X	w_extra.DEVICE = 0;
X	w_extra.POSITION = 0L;
X	w_extra.COUNT = BLOCK_SIZE;
X	w_extra.ADDRESS = (char *) buf;
X	w_extra.PROC_NR = WINCHESTER;
X	w_extra.m_type = DISK_READ;
X	if(w_do_rdwt(&w_extra) != BLOCK_SIZE)
X	  panic("cannot read drive parameters from winchester", 0);
X
X	/* Copy the parameter tables into the structures for later use. */
X	xt_copy_params(&buf[AUTO_PARAM], &param0);
X
X	   
X	/* Whoever compiled the kernel wanted auto bios code included. if it
X	 * turns out that the tables should be read from the rom, then handle
X	 * this case the regular way.
X	 */
X  } else {
X#endif
X
X  /* Calculate the drive types */
X  type_0 = i & 3;
X  type_1 = (i >> 2) & 3;
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 = ((phys_bytes)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#if AUTO_BIOS
X  /* close up the code to be executed when the controller has not been
X   * set up to for auto configuration */
X  }
X#endif
X
X  /* Set the parameters in the drive structure */
X  for (i = 0; i < DEV_PER_DRIVE; i++) {
X	wini[i].wn_heads = param0.nr_heads;
X	wini[i].wn_drive = DRIVE + i/5;
X  }
X
X  wini[0].wn_low = wini[DEV_PER_DRIVE].wn_low = 0L;
X  wini[0].wn_size = (long)((long)param0.nr_cyl * 
X				   (long)param0.nr_heads * (long)NR_SECTORS);
X
X  for (i = DEV_PER_DRIVE; i < (2*DEV_PER_DRIVE); i++) {
X	wini[i].wn_heads = param1.nr_heads;
X	wini[i].wn_drive = DRIVE + i/5;
X  }
X  wini[DEV_PER_DRIVE].wn_size =
X	(long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS);
X
X  /* Read the partition table for this drive and save it. */
X  w_extra.DEVICE = drive * DEV_PER_DRIVE;
X  w_extra.POSITION = 0L;
X  w_extra.COUNT = BLOCK_SIZE;
X  w_extra.ADDRESS = (char *) buf;
X  w_extra.PROC_NR = WINCHESTER;
X  w_extra.m_type = DISK_READ;
X  if (w_do_rdwt(&w_extra) != BLOCK_SIZE) {
X	printf("Can't read partition table of winchester %d\n", drive);
X	delay();
X	return;
X  }
X  copy_prt(drive * DEV_PER_DRIVE);
X}
X
X/*==========================================================================*
X *				xt_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 DEV_PER_DRIVE
X*/
X
X  int i;
X
X  for (i= 0; i < NR_DEVICES; i++) wini[i].wn_maxsec = NR_SECTORS;
X
X  dest->nr_cyl = *(int *)src;
X  dest->nr_heads = (int)src[2];
X}
X
X/*===========================================================================*
X *				copy_prt				     *
X *===========================================================================*/
XPRIVATE copy_prt(drdev)
Xint drdev;			/* 5 * drive */
X{
X/* This routine copies the partition table for the selected drive/dev 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 + drdev + 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[drdev + 1]);
X}
X
X/*===========================================================================*
X *				sort					     *
X *===========================================================================*/
XPRIVATE sort(wn)
Xregister struct wini wn[];
X{
X  register int i,j;
X  struct wini tmp;
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		    (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)) {
X			tmp = wn[j];
X			wn[j] = wn[j+1];
X			wn[j+1] = tmp;
X		}
X}
X
X/*===========================================================================*
X *				delay					     *
X *===========================================================================*/
XPRIVATE delay()
X{
X  int i,j;
X
X  for (i = 0; i < 2000; i++)
X	for (j = 0; j < 1000; j ++)
X		;
X}
X
X
X/*===========================================================================*
X *				replace					     *
X *===========================================================================*/
XPRIVATE replace()
X{
X/* Restore the interrupt vectors > 16 to their original state, except 32. */
X
X  phys_bytes phys_b;
X  extern phys_bytes umap();
X  phys_clicks base_click;
X  extern int s_call();
X
X  base_click = BASE >> CLICK_SHIFT;
X  phys_b = umap(proc_addr(HARDWARE), D, (vir_bytes) vec_table, VECTOR_BYTES);
X  phys_copy(phys_b+64L, 64L, (long) VECTOR_BYTES-64L);	/* restore vectors */
X  phys_copy(phys_b+4L*XT_WINI_VECTOR, 4L*XT_WINI_VECTOR, 4L);
X  set_vec(SYS_VECTOR, s_call, base_click);
X}
+ END-OF-FILE bios_wini.c
chmod 'u=rw,g=r,o=r' 'bios_wini.c'
set `wc -c 'bios_wini.c'`
count=$1
case $count in
16189)	:;;
*)	echo 'Bad character count in ''bios_wini.c' >&2
		echo 'Count should be 16189' >&2
esac
echo Extracting 'glo.h.cdif'
sed 's/^X//' > 'glo.h.cdif' << '+ END-OF-FILE ''glo.h.cdif'
X*** /local/ast/minix/tape3/kernel/glo.h	Thu Oct 13 16:23:37 1988
X--- /local/ast/minix/tape4/kernel/glo.h	Fri Nov 11 23:55:01 1988
X***************
X*** 19,21 ****
X--- 19,24 ----
X  EXTERN int color;		/* 1 if console is color, 0 if it is mono */
X  EXTERN int ega;			/* 1 if console is EGA, 0 if not */
X  EXTERN int scan_code;		/* scan code of key pressed to start minix */
X+ 
X+ /* Miscellaneous */
X+ EXTERN Ax, Bx, Cx, Dx, Es;	/* used to hold registers for BIOS calls */
+ END-OF-FILE glo.h.cdif
chmod 'u=rw,g=r,o=r' 'glo.h.cdif'
set `wc -c 'glo.h.cdif'`
count=$1
case $count in
467)	:;;
*)	echo 'Bad character count in ''glo.h.cdif' >&2
		echo 'Count should be 467' >&2
esac
echo Extracting 'klib88.s.cdif'
sed 's/^X//' > 'klib88.s.cdif' << '+ END-OF-FILE ''klib88.s.cdif'
X*** /local/ast/minix/tape3/kernel/klib88.s	Thu Oct 13 16:28:14 1988
X--- /local/ast/minix/tape4/kernel/klib88.s	Fri Nov 11 23:53:01 1988
X***************
X*** 21,33 ****
X  |   em_xfer:	read or write AT extended memory using the BIOS
X  |   wait_retrace: waits for retrace interval, and returns int disabled
X  |   ack_char:	acknowledge character from keyboard
X! |   save_tty_vec: save tty interrupt vector 0x71 for PS/2
X  
X  | The following procedures are defined in this file and called from outside it.
X  .globl _phys_copy, _cp_mess, _lock, _restore
X  .globl _build_sig, csv, cret, _get_chrome, _vid_copy, _get_byte, _reboot
X  .globl _wreboot, _dma_read, _dma_write, _em_xfer, _scr_up, _scr_down
X! .globl _ack_char, _save_tty_vec, _get_ega, _wait_retrace
X  
X  
X  | The following external procedures are called in this file.
X--- 21,33 ----
X  |   em_xfer:	read or write AT extended memory using the BIOS
X  |   wait_retrace: waits for retrace interval, and returns int disabled
X  |   ack_char:	acknowledge character from keyboard
X! |   bios13: make a 0x13 BIOS call to do disk I/O
X  
X  | The following procedures are defined in this file and called from outside it.
X  .globl _phys_copy, _cp_mess, _lock, _restore
X  .globl _build_sig, csv, cret, _get_chrome, _vid_copy, _get_byte, _reboot
X  .globl _wreboot, _dma_read, _dma_write, _em_xfer, _scr_up, _scr_down
X! .globl _ack_char,  _get_ega, _wait_retrace, _bios13
X  
X  
X  | The following external procedures are called in this file.
X***************
X*** 36,41 ****
X--- 36,42 ----
X  | Variables and data structures
X  .globl _color, _cur_proc, _proc_ptr, splimit
X  .globl _port_65, _ps, _vec_table, _vid_mask, _vid_port
X+ .globl _Ax, _Bx, _Cx, _Dx, _Es
X  
X  |*===========================================================================*
X  |*				phys_copy				     *
X***************
X*** 740,761 ****
X  
X  
X  |*===========================================================================*
X! |*				save_tty_vec				     *
X  |*===========================================================================*
X! | Save the tty vector 0x71 (PS/2)
X! _save_tty_vec:
X  	push es
X! 	xor ax,ax
X! 	mov es,ax
X! 	seg es
X! 	mov ax,452
X! 	mov tty_vec1,ax
X! 	seg es
X! 	mov ax,454
X! 	mov tty_vec2,ax
X  	pop es
X  	ret
X  
X  |*===========================================================================*
X  |*				reboot & wreboot			     *
X  |*===========================================================================*
X--- 741,773 ----
X  
X  
X  |*===========================================================================*
X! |*				bios13					     *
X  |*===========================================================================*
X! _bios13:			| make a BIOS 0x13 call for disk I/O
X! 	push ax			| save the registers
X! 	push bx
X! 	push cx
X! 	push dx
X  	push es
X! 	mov ax,_Ax		| load parameters
X! 	mov bx,_Bx
X! 	mov cx,_Cx
X! 	mov dx,_Dx
X! 	mov es,_Es
X! 	int 0x13		| make the BIOS call
X! 	mov _Ax,ax		| save results
X! 	mov _Bx,bx
X! 	mov _Cx,cx
X! 	mov _Dx,dx
X  	pop es
X+ 	pop dx
X+ 	pop cx
X+ 	pop bx
X+ 	pop ax
X  	ret
X  
X+ 
X+ 
X  |*===========================================================================*
X  |*				reboot & wreboot			     *
X  |*===========================================================================*
X***************
X*** 828,847 ****
X  
X  | Restore the interrupt vectors in low core.
X  resvec:	cld
X! 	mov cx,#2*71
X  	mov si,#_vec_table
X  	xor di,di
X  	mov es,di
X  	rep
X  	movw
X- 
X- 	mov ax,tty_vec1		| Restore keyboard interrupt vector for PS/2
X- 	seg es
X- 	mov 452,ax
X- 	mov ax,tty_vec2
X- 	seg es
X- 	mov 454,ax
X- 
X  	ret
X  
X  | Some library routines use exit, so this label is needed.
X--- 840,851 ----
X  
X  | Restore the interrupt vectors in low core.
X  resvec:	cld
X! 	mov cx,#2*128
X  	mov si,#_vec_table
X  	xor di,di
X  	mov es,di
X  	rep
X  	movw
X  	ret
X  
X  | Some library routines use exit, so this label is needed.
X***************
X*** 855,860 ****
X  splimit:	.word 0		| stack limit for current task (kernel only)
X  tmp:		.word 0		| count of bytes already copied
X  stkoverrun:	.asciz "Kernel stack overrun, task = "
X- _vec_table:	.zerow 142	| storage for interrupt vectors
X- tty_vec1:	.word 0		| sorage for vector 0x71 (offset)
X- tty_vec2:	.word 0		| sorage for vector 0x71 (segment)
X--- 859,861 ----
+ END-OF-FILE klib88.s.cdif
chmod 'u=rw,g=r,o=r' 'klib88.s.cdif'
set `wc -c 'klib88.s.cdif'`
count=$1
case $count in
4227)	:;;
*)	echo 'Bad character count in ''klib88.s.cdif' >&2
		echo 'Count should be 4227' >&2
esac
echo Extracting 'main.c.cdif'
sed 's/^X//' > 'main.c.cdif' << '+ END-OF-FILE ''main.c.cdif'
X*** /local/ast/minix/tape3/kernel/main.c	Tue Sep 27 08:51:13 1988
X--- /local/ast/minix/tape4/kernel/main.c	Fri Nov 11 23:37:46 1988
X***************
X*** 42,47 ****
X--- 42,48 ----
X  int (*int_vec[HIGH_INT])() = {int00, int01, int02, int03, int04, int05, int06,
X      int07, int08, int09, int10, int11, int12, int13, int14, int15};     
X  
X+ PUBLIC int vec_table[VECTOR_BYTES/2];
X  
X  /*===========================================================================*
X   *                                   main                                    * 
X***************
X*** 59,65 ****
X    int	stack_size;
X    int * ktsb;			/* kernel task stack base */
X    extern unsigned sizes[8];	/* table filled in by build */
X!   extern int port_65, ega, color, vec_table[], get_chrome();
X    extern int s_call(), disk_int(), tty_int(), clock_int(), disk_int();
X    extern int wini_int(), lpr_int(), trp(), rs232_int(), secondary_int();
X    extern phys_bytes umap();
X--- 60,66 ----
X    int	stack_size;
X    int * ktsb;			/* kernel task stack base */
X    extern unsigned sizes[8];	/* table filled in by build */
X!   extern int port_65, ega, color, get_chrome();
X    extern int s_call(), disk_int(), tty_int(), clock_int(), disk_int();
X    extern int wini_int(), lpr_int(), trp(), rs232_int(), secondary_int();
X    extern phys_bytes umap();
X***************
X*** 151,162 ****
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-   if (ps) save_tty_vec();	/* save tty vector 0x71 for reboot() */
X  
X!   /* Set up the new interrupt vectors. */
X    for (t = 0; t < HIGH_INT; t++) set_vec(t, int_vec[t], base_click);
X!   for (t = HIGH_INT; t < 256; t++) set_vec(t, trp, base_click);
X  
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--- 152,163 ----
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!   /* Overwrite unused vectors.  The holes in this list are needed by the BIOS*/
X    for (t = 0; t < HIGH_INT; t++) set_vec(t, int_vec[t], base_click);
X!   for (t = 16; t < 256; t++) set_vec(t, trp, base_click);
X  
X+   /* Set up the new interrupt vectors. */
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***************
X*** 164,177 ****
X    set_vec(RS232_VECTOR, rs232_int, base_click);
X    set_vec(FLOPPY_VECTOR, disk_int, base_click);
X    set_vec(PRINTER_VECTOR, lpr_int, base_click);
X  #ifdef AM_KERNEL
X  #ifndef NONET
X    set_vec(ETHER_VECTOR, eth_int, base_click);	/* overwrites RS232 port 2 */
X  #endif
X  #endif
X    if (pc_at) {
X  	set_vec(AT_WINI_VECTOR, wini_int, base_click);
X! 	phys_copy(phys_b + 4L*EM_VEC, 4L*EM_VEC, 4L);	/* extended mem vec */
X    } else
X  	set_vec(XT_WINI_VECTOR, wini_int, base_click);
X  
X--- 165,181 ----
X    set_vec(RS232_VECTOR, rs232_int, base_click);
X    set_vec(FLOPPY_VECTOR, disk_int, base_click);
X    set_vec(PRINTER_VECTOR, lpr_int, base_click);
X+ 
X+ 
X  #ifdef AM_KERNEL
X  #ifndef NONET
X    set_vec(ETHER_VECTOR, eth_int, base_click);	/* overwrites RS232 port 2 */
X  #endif
X  #endif
X+ 
X    if (pc_at) {
X  	set_vec(AT_WINI_VECTOR, wini_int, base_click);
X! 	phys_copy(phys_b + 4L*EM_VEC, 4L*EM_VEC, 4L);
X    } else
X  	set_vec(XT_WINI_VECTOR, wini_int, base_click);
X  
X***************
X*** 261,267 ****
X  /*===========================================================================*
X   *                                   set_vec                                 * 
X   *===========================================================================*/
X! PRIVATE set_vec(vec_nr, addr, base_click)
X  int vec_nr;			/* which vector */
X  int (*addr)();			/* where to start */
X  phys_clicks base_click;		/* click where kernel sits in memory */
X--- 265,271 ----
X  /*===========================================================================*
X   *                                   set_vec                                 * 
X   *===========================================================================*/
X! PUBLIC set_vec(vec_nr, addr, base_click)
X  int vec_nr;			/* which vector */
X  int (*addr)();			/* where to start */
X  phys_clicks base_click;		/* click where kernel sits in memory */
X***************
X*** 295,297 ****
X--- 299,302 ----
X  PUBLIC eth_stp() {}			/* stop the ethernet upon reboot */
X  PUBLIC dp8390_int(){}			/* Ethernet interrupt */
X  #endif
X+ 
+ END-OF-FILE main.c.cdif
chmod 'u=rw,g=r,o=r' 'main.c.cdif'
set `wc -c 'main.c.cdif'`
count=$1
case $count in
4656)	:;;
*)	echo 'Bad character count in ''main.c.cdif' >&2
		echo 'Count should be 4656' >&2
esac
exit 0