AUSAM/source/mdec/rkdfc.c
#
/* This program created by Ian J. July '76
*
* Performs certain necessary i/o functions for rk05 disks:
*
* h for help
* c for copy function
* f for format function
* k for compare function
* r for read function
*/
/* Modified by Kevin Hill Sep-Oct 78
*
* 1. Corrected a bug which caused `rkdf' to lie about block nrs.
* 2. Extra commands added:
*
* `a' for assisting with drive alignment;
* `b' for reboot (asks for a drive number);
* `d' for the J. Rottman `dcopy' program (as modified
* by C. Maltby / K. Hill).
*
* 3. Altered code slightly so that `help' is printed out at start.
* 4. Rewrote `gdrivnum' and `rkio' to avoid use of goto's.
* 5. Included a smaller `printf' - only `%d', `%o', and `%s' supported.
* 6. Added DELETE facility.
* 7. Added checks to detect:
*
* if selected drive is active;
* if drive selected to be written on is write-protected;
* if same drive numbers for compare (==> exit) or copy
* (==> copy-in-situ on a cylinder-by-cylinder basis).
*/
#define DELETE 0177
#define NRETRYS 10 /* number of attempts at disk i/o before collapsing in a heap */
#define SSR0 0177572
#define KISA0 0172340
#define KISA6 0172354
#define KISD0 0172300
#define SWREG 0177570
struct { int i; }; /* structure to access integers */
#define TT 0177560
#define DONE 0200
struct
{
int csr;
int csb;
int tsr;
int tsb;
};
#define RK 0177400
#define NCYL 203
#define BLKPERTRK 12
#define NTRACK 2
#define BLKPERCYL NTRACK * BLKPERTRK
#define WDSPERCYL 256 * BLKPERCYL
#define BLKPERDSK NCYL * BLKPERCYL /* 4872 */
struct
{
int rkds;
int rker;
int rkcs;
int rkwc;
int rkba;
int rkda;
};
/* rk i/o requests */
#define CNTRLR 01 /* control reset */
#define WRITE 03
#define READ 05
#define WRITECHK 07
#define SEEK 011
#define READCHK 013
#define WRITEPROT 017
#define SSE 0400 /* stop on soft error */
#define FMTW 02003 /* format write */
#define IBA 04000 /* inhibit incrementing RKBA */
#define WTPROT 040 /* write protect bit */
#define DRY 0200 /* drive ready bit */
char *bigbuf; /* pointer to buffer area */
int iob; /* number of blocks per big i/o */
int nio; /* number of i/o for these blocks */
int szio; /* number of words for each of above i/os */
int iov; /* number of blocks left after the big ones */
int szov; /* number of words for this last i/o */
int in, out; /* drives for current input/output operations */
int inform, outform; /* optimised-format flags - for dcopy */
int base; /* base of number to be printed out */
int kludge; /* prints out help first time only */
main()
{
register i;
setexit();
if (kludge++ == 0) help();
for (;;)
{
printf("\007\nWhich function is desired? \007");
i = getchar();
putchar('\n');
switch(i)
{
case 'a': in = gdrivnum("Drive# ", 0);
wrlock(in);
align(); /* no return */
case 'b': printf("Switches = %o - ", SWREG->i);
base = gdrivnum("Boot from drive# ", 0) << 13;
putchar(0); putchar(0); /* flush */
reboot(); /* no return */
case 'c': in = gdrivnum("Read from drive# ", 0);
out = gdrivnum(" Write to drive# ", 1);
if (in == out)
{
if (iob >= BLKPERCYL)
{
reed(in);
cpyinsitu();
break;
}
printf("\007Not enough core to copy-in-situ.\n");
break;
}
wrlock(in);
reed(in);
format();
copy();
check();
break;
case 'd': in = gdrivnum("Read from drive# ", 0);
inform = formtype();
out = gdrivnum("Write to drive# ", 1);
if (in == out)
{
printf("Same drives (?)\n");
break;
}
outform = formtype();
wrlock(in);
dcopy(); /* does the reed and format */
break;
case 'f': out = gdrivnum("Format drive# ", 1);
format();
reed(out);
break;
case 'k': in = gdrivnum(" Read from drive# ", 0);
out = gdrivnum("Compare with drive# ", 0);
if (in == out) printf("\007Same drives.\n");
else check();
break;
case 'r': in = gdrivnum("Read from drive# ", 0);
wrlock(in);
reed(in);
break;
default: help();
}
}
}
help()
{
printf("\nYou are reqd. to enter a char\n");
printf("to indicate the function you want\n");
printf(" a -- align\n");
printf(" b -- reboot\n");
printf(" c -- copy\n");
printf(" d -- dcopy\n");
printf(" f -- format\n");
printf(" h -- help\n");
printf(" k -- compare\n");
printf(" r -- read\n");
}
wrlock(unit)
register unit;
{
rkio(unit, 0, 0, WRITEPROT, 0);
printf("Drive %d write protected.\n", unit);
}
format()
{
register i, unit;
bigbuf[0] = 0;
printf("Begin formatting drive %d.\n", unit = out);
for(i = 0; i < nio; i++) rkio(unit, bigbuf, iob * i, FMTW | IBA, szio);
rkio(unit, bigbuf, iob * i, FMTW | IBA, szov);
printf("Formatting complete.\n");
}
cpyinsitu()
{
register i, unit;
printf("Begin copying drive %d in situ.\n", unit = in);
for (i = 0; i < NCYL; i++)
{
rkio(unit, bigbuf, i * BLKPERCYL, READ | SSE, WDSPERCYL);
rkio(unit, bigbuf, i * BLKPERCYL, FMTW, WDSPERCYL);
rkio(unit, bigbuf, i * BLKPERCYL, WRITECHK | SSE, WDSPERCYL);
}
wrlock(unit);
printf("Copying complete.\n");
}
copy()
{
register i, iunit, ounit;
printf("Begin copying drive %d to drive %d.\n", iunit = in, ounit = out);
for (i = 0; i < nio; i++)
{
rkio(iunit, bigbuf, i * iob, READ | SSE, szio);
rkio(ounit, bigbuf, i * iob, WRITE | SSE, szio);
rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szio);
}
rkio(iunit, bigbuf, i * iob, READ | SSE, szov);
rkio(ounit, bigbuf, i * iob, WRITE | SSE, szov);
rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szov);
printf("Copy complete.\n");
}
check()
{
register i, iunit, ounit;
wrlock(iunit = in);
wrlock(ounit = out);
printf("Begin comparing drive %d to drive %d.\n", iunit, ounit);
for (i = 0; i < nio; i++)
{
rkio(iunit, bigbuf, i * iob, READ | SSE, szio);
rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szio);
}
rkio(iunit, bigbuf, i * iob, READ | SSE, szov);
rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szov);
printf("Compare complete.\n");
}
reed(unit)
register unit;
{
register i;
printf("Begin reading drive %d.\n", unit);
for (i = 0; i < nio; i++) rkio(unit, bigbuf, i * iob, READCHK | SSE, szio);
rkio(unit, bigbuf, i * iob, READCHK | SSE, szov);
printf("Read complete.\n");
}
align()
{
register block, unit;
register char c;
unit = in;
for (;;)
{
printf("Cylinder# ");
block = getnum(0, NCYL, 0) * BLKPERCYL;
printf("0 = top head; 1 = bottom head; 'c' for new cyl; DEL to exit.\n");
for (;;)
{
printf("Head# ");
c = getchar();
putchar('\n');
if (c == 'c') break;
if (c != '0' && c != '1')
{
printf("\007Illegal head.\n");
continue;
}
rkio(unit, 0, 0, WRITEPROT, 0); /* safety first! */
rkio(unit, 0, c == '1' ? block + BLKPERTRK : block, SEEK | SSE, 0);
}
}
}
rkio(unit, buffer, block, cmd, wc)
register unit, block, wc;
{
int nretry;
for (nretry = 0; nretry < NRETRYS; nretry++)
{
while ((RK->rkcs & DONE) == 0);
RK->rkda = ((block / 12) << 4) | (block % 12) | (unit << 13);
RK->rkba = buffer;
RK->rkwc = -wc;
RK->rkcs = cmd;
while ((RK->rkcs & DONE) == 0);
if (RK->rkcs >= 0) return;
printf("\007Error on drive %d block=%d. -- ds=%o da=%o cs=%o er=%o\n",
(RK->rkda >> 13) & 07,
12 * ((RK->rkda & 017760) >> 4) + (RK->rkda & 017),
RK->rkds, RK->rkda, RK->rkcs, RK->rker);
RK->rkcs = CNTRLR;
}
printf("\nRecovery impossible.\n");
putchar(0); putchar(0); /* flush */
restart(); /* no return */
}
/********************************************************/
getnum(lower, upper, dflt) /* returns lower <= num < upper */
register unsigned upper;
{
register unsigned n;
register c;
for (;;)
{
n = 0;
if ((c = getchar()) == '\r')
{
if (dflt < upper) n = dflt;
}
else while (c >= '0' && c <= '9' && (n = n * 10 + c - '0') < upper) c = getchar();
putchar('\n');
if (c == '\r' && n >= lower) return(n);
printf("\007Illegal entry - try again ");
}
}
formtype()
{
register c;
printf("Optimised format? (y-n) ");
c = (getchar() == 'y');
putchar('\n');
return(c);
}
gdrivnum(s, chkwrprt)
register char *s;
register chkwrprt;
{
register c;
for (;;)
{
printf(s);
c = getchar() - '0';
putchar('\n');
if (c < 0 || c > 7)
{
printf("\007Illegal drive.\n");
continue;
}
RK->rkda = c << 13;
if ((RK->rkds & DRY) == 0)
{
printf("\007Drive not active.\n");
continue;
}
if (chkwrprt && (RK->rkds & WTPROT))
{
printf("\007Drive write protected.\n");
continue;
}
return(c);
}
}
printf(s, a)
register char *s;
{
register c, *p;
p = &a;
while (c = *s++)
{
if (c == '%')
switch (c = *s++)
{
case 's': printf(*p++); /* CARE! */
continue;
case 'o': base = 8;
printl(*p++);
continue;
case 'd': base = 10;
printl(*p++);
continue;
case 0: return;
}
putchar(c);
}
}
printl(n)
register unsigned n;
{
register i;
i = n % base;
if (n =/ base) printl(n);
putchar(i + '0');
}
putchar(c)
register char c;
{
while ((TT->tsr & DONE) == 0);
TT->tsb = c;
if (c == '\n') putchar('\r');
}
getchar()
{
register c;
while ((TT->csr & DONE) == 0);
if ((c = TT->csb & 0177) == DELETE)
{
putchar('\n');
reset();
}
putchar(c);
if (c >= 'A' && c <= 'Z') c =| 040;
return(c);
}
/********************************************************/
#include <local-system>
#include <defines.h>
#include <param.h>
#include <filsys.h>
#include <ino.h>
#define INTL 3 /* magic interleaving number */
#define IN_NODE sizeof *superblk
#define OUT_NODE IN_NODE + 16 * sizeof *in_node
#define GENERAL OUT_NODE + 16 * sizeof *out_node
#define IND0 GENERAL + 512
#define IND1 IND0 + 512
#define INDX IND1 + 512
struct buffer
{
int bn; /* block number */
int bb[256]; /* block */
} *startbuff, *currbuff;
struct filsys *superblk; /* target super block */
struct inode *in_node; /* inode blocks from source */
struct inode *out_node; /* inode blocks to target */
int *general; /* general usage */
int *ind0; /* first indirect block */
int *ind1; /* second indirect block */
int *indx; /* start of dynamic array area */
int *iindex; /* used with indx */
char adr[BLKPERTRK]; /* interleaving array */
int nbuf; /* number of buffers in the pool */
int inpool; /* number of used buffers */
struct inode *node; /* the current inode being considered */
int isize_sav; /* i-list size from the source super block */
long time_sav; /* time from the source super block */
int form; /* format of current input unit */
int upto; /* where up to when compacting directories */
unsigned dirent; /* counts the new directory entries */
int *diraddr; /* where up to in the current inode */
int *dirind; /* where up to in indirect block */
int inext;
int inum, iblk, onum, oblk;
int lastblock, cyl, sector, track;
dcopy()
{
register char *ptr1;
register *ptr2;
register icount;
int isize1;
superblk = bigbuf;
in_node = &bigbuf[IN_NODE];
out_node = &bigbuf[OUT_NODE];
general = &bigbuf[GENERAL];
ind0 = &bigbuf[IND0];
ind1 = &bigbuf[IND1];
indx = &bigbuf[INDX];
iindex = indx - 1;
inum = 16;
iblk = 1;
onum = 0;
oblk = 2;
inpool = 0;
cyl = sector = track = 0;
form = inform;
getblk(in, superblk, 1); /* source super block */
isize_sav = superblk->s_isize;
time_sav = superblk->s_time;
/* Test to see if there is room for the program plus reserved buffers
* plus at least 8 buffers in the pool, in the program's address space,
* and in the machine's core.
*/
ptr1 = &bigbuf[INDX] + superblk->s_isize * 2 * 16;
inext = 8 * sizeof *startbuff;
if (ptr1 + inext < &bigbuf[INDX] || ((ptr1 + inext + 077) >> 6) > (icount = KISA6->i))
{
printf("Not enough core.\n");
return;
}
inext = 1;
if (icount > 01600) icount = 01600; /* i/o page */
icount = (icount << 6) - ptr1;
currbuff = startbuff = ptr1;
nbuf = ldiv(0, icount, sizeof *startbuff);
printf("%d buffers in pool.\n\n", nbuf);
printf("Source: size = %d blks (%d in use); i-list = %d blks (%d in use).\n",
superblk->s_fsize, superblk->s_fsize - (ptr1 = countfree()), superblk->s_isize, ptr2 = counti());
for (;;)
{
printf("Enter size (blocks) of target volume (%d less swap space) ", BLKPERDSK);
icount = getnum(ptr2, BLKPERDSK + 1, superblk->s_fsize);
printf("Enter size (blocks) of target i-list (16 inodes per block) ");
isize1 = getnum(ptr2, icount, superblk->s_isize);
if ((superblk->s_fsize - (superblk->s_isize + ptr1)) <= (icount - isize1)) break;
printf("Insufficient file space.\n");
}
reed(in);
format(); /* secondary effect is to clear the inodes */
for (ptr2 = superblk; ptr2 < &superblk[1]; *ptr2++ = 0); /* clear target super block */
superblk->s_fsize = icount;
superblk->s_isize = isize1;
superblk->s_time = time_sav;
ptr1 = "optimised ";
printf("Begin dcopy from %sdrive %d to %sdrive %d.\n",
inform ? ptr1 : "", in, outform ? ptr1 : "", out);
getblk(in, grablock(0), 0); /* bootstrap block */
for (ptr2 = general; ptr2 < &general[BLKPERTRK]; *ptr2++ = 0);
icount = 0;
for (ptr1 = adr; ptr1 < &adr[BLKPERTRK]; ) /* set up interleaving template */
{
while (general[icount]) icount = (icount + 1) % BLKPERTRK;
*ptr1++ = icount;
general[icount]++;
icount = (icount + INTL) % BLKPERTRK;
}
printf("Copy files.\n");
icount = 16 * isize_sav;
do
{
geti();
if (node->i_mode & IALLOC)
{
copyi();
*indx++ = inext++;
}
else *indx++ = 0;
} while (--icount);
printf("Complete i-list.\n");
if (onum)
{
ptr1 = &out_node[onum];
icount = 32 * (16 - onum);
do *ptr1++ = 0; while (--icount);
putblk(out_node, oblk);
}
purgepool(); /* remaining inodes zero due to the format */
printf("Correct directory entries.\n");
iblk = 1;
inum = 16;
in = out; /* so `geti' gets from target */
form = outform;
for (icount = 0; icount < inext; icount++)
{
geti();
if ((node->i_mode & IFMT) == IFDIR) reldir();
}
printf("Construct the free list.\n");
ptr1 = 16 * isize1;
icount = inext;
while (icount <= ptr1 && superblk->s_ninode != 100)
superblk->s_inode[superblk->s_ninode++] = icount++;
ptr2 = &general[100];
for (icount = 100; icount < 256; icount++) *ptr2++ = 0;
freelist();
putblk(superblk, 1);
purgepool();
wrlock(out);
printf("Dcopy complete.\n");
}
getblk(unit, buff, block)
register unit, buff, block;
{
rkio(unit, buff, map(form, block), READ | SSE, 256);
}
grablock(n) /* grabs the next free block from the pool */
register n;
{
if (inpool == nbuf) purgepool();
inpool++;
currbuff->bn = map(outform, n);
return((currbuff++)->bb);
}
putblk(buff, block)
register int *buff;
{
register int *w, q;
w = grablock(block);
q = 256;
do *w++ = *buff++; while (--q);
}
purgepool()
{
register int i;
currbuff = startbuff;
for (i = 0; i < inpool; i++)
{
rkio(out, currbuff->bb, currbuff->bn, WRITE | SSE, 256);
currbuff++;
}
currbuff = startbuff;
inpool = 0;
}
map(fmt, block) /* translates optimised to equivalent unoptimised */
register fmt, block;
{
if (! fmt || ! block) return(block);
return(block > 2436 ? block - 2436 : block + 2435);
}
geti()
{
if (inum++ < 16) node++;
else
{
getblk(in, node = in_node, ++iblk);
printf("%d\r", iblk); /* keeps the user awake */
inum = 1;
}
}
puti()
{
register *r, *w, q;
w = &out_node[onum];
r = node;
q = 16;
do *w++ = *r++; while (--q);
if (++onum == 16)
{
putblk(out_node, oblk++);
onum = 0;
}
}
nxtblk()
{
register block;
do
{
block = (cyl * NTRACK + track) * BLKPERTRK + adr[sector];
if (++sector >= BLKPERTRK)
{
sector = 0;
if (++track >= NTRACK)
{
cyl++;
track = 0;
}
}
} while (block < 2 + superblk->s_isize);
return(lastblock = block);
}
freelist()
{
register rcyl, rsector, rtrack;
extern int ldivr;
unsigned block;
rcyl = ldiv(0, superblk->s_fsize, NTRACK * BLKPERTRK);
rtrack = ldivr / BLKPERTRK;
rsector = ldivr % BLKPERTRK; /* first unusable block */
bfree(0); /* end-of-list sentinel */
while (rsector) bfree ((rcyl * NTRACK + rtrack) * BLKPERTRK + --rsector);
for (;;)
{
if (rsector-- == 0)
{
rsector =+ BLKPERTRK;
if (rtrack-- == 0)
{
rtrack =+ NTRACK;
rcyl--;
}
}
block = (rcyl * NTRACK + rtrack) * BLKPERTRK + adr[rsector];
if (block == lastblock) return;
if (block >= 2 + superblk->s_isize) bfree(block);
}
}
bfree(b)
{
register *r, *q, w;
if (superblk->s_nfree == 100)
{
r = general;
*r++ = superblk->s_nfree;
q = superblk->s_free;
w = 100;
do *r++ = *q++; while (--w);
putblk(general, b);
superblk->s_nfree = 0;
}
superblk->s_free[superblk->s_nfree++] = b;
}
copyi()
{
register *p, *pp, md;
if (md = node->i_mode & IFMT)
{
if (md == IFDIR) copydir();
puti();
return;
}
if ((node->i_mode & ILARG) == 0)
{
for (p = node->i_addr; p < &node->i_addr[8]; p++)
if (*p) getblk(in, grablock(*p = nxtblk()), *p); /* stacking-dependent */
puti();
return;
}
for (p = node->i_addr; p < &node->i_addr[7]; p++)
if (*p)
{
getblk(in, ind0, *p);
*p = nxtblk();
for (pp = ind0; pp < &ind0[256]; pp++)
if (*pp) getblk(in, grablock(*pp = nxtblk()), *pp);
putblk(ind0, *p);
}
if (node->i_addr[7])
{
getblk(in, ind0, node->i_addr[7]);
node->i_addr[7] = nxtblk();
for (p = ind0; p < &ind0[256]; p++)
if (*p)
{
getblk(in, ind1, *p);
*p = nxtblk();
for (pp = ind1; pp < &ind1[256]; pp++)
if (*pp) getblk(in, grablock(*pp = nxtblk()), *pp);
putblk(ind1, *p);
}
putblk(ind0, node->i_addr[7]);
}
puti();
}
putdir(blk)
{
register *to, *from, cntr;
int count;
to = upto;
getblk(in, from = general, blk);
for (count = 0; count < 32; count++)
{
if (! *from)
{
from =+ 8;
continue;
}
if ((dirent & 037) == 0)
{
if (diraddr == 0 && dirent == 256) /* convert to large file */
{
node->i_mode =| ILARG;
*(diraddr = node->i_addr) = *ind1;
for (cntr = 0; cntr < 7; cntr++) /* at least 8 buffers have been guaranteed */
{
ind1[cntr] = ind1[cntr + 1];
currbuff[cntr - 8].bn = currbuff[cntr - 7].bn;
}
currbuff[-1].bn = map(outform, ind1[7] = nxtblk());
}
else if (diraddr && (dirent & 017777) == 0)
{
putblk(ind1, *diraddr);
*++diraddr = nxtblk();
dirind = ind1;
}
to = grablock(*dirind++ = nxtblk());
}
for (cntr = 0; cntr < 8; cntr++) *to++ = *from++;
dirent++;
}
upto = to;
}
copydir()
{
register *p, *pp;
dirent = diraddr = 0;
dirind = ind1;
if ((node->i_mode & ILARG) == 0)
{
for (p = node->i_addr; p < &node->i_addr[8]; p++)
if (*p) putdir(*p);
}
else
{
if (nbuf - inpool < 8) purgepool(); /* guarantees 8 buffers - see `putdir' */
node->i_mode =& ~ILARG;
for (p = node->i_addr; p < &node->i_addr[7]; p++)
if (*p)
{
getblk(in, ind0, *p);
for (pp = ind0; pp < &ind0[256]; pp++)
if (*pp) putdir(*pp);
}
if (diraddr) while (dirind < &ind1[256]) *dirind++ = 0; /* clear rest of pointers */
}
node->i_size0 = (dirent >> 12) & 017;
node->i_size1 = dirent << 4;
if (diraddr == 0)
{
p = &node->i_addr[-1];
for (pp = ind1; pp < dirind; *++p = *pp++);
}
for (pp = upto; pp < currbuff; *pp++ = 0); /* clear rest of directory */
if (diraddr) putblk(ind1, *(p = diraddr));
while (p < &node->i_addr[7]) *++p = 0;
}
reldir()
{
register i, j;
if ((node->i_mode & ILARG) == 0)
{
for (i = 0; i < 8; i++) relx(node->i_addr[i]);
return;
}
for (i = 0; i < 7; i++)
if (node->i_addr[i])
{
getblk(out, ind0, node->i_addr[i]);
for (j = 0; j < 256; j++) relx(ind0[j]);
}
}
relx(b)
register b;
{
register *p;
register i;
if (b == 0) return;
getblk(out, p = grablock(b), b);
i = 32;
do
{
if (*p) *p = iindex[*p];
p =+ 8; /* 8 words per directory entry */
} while (--i);
}
counti()
{
register i, j, n;
n = 0;
for (i = 0; i < superblk->s_isize; i++)
{
getblk(in, in_node, 2+i);
for (j = 0; j < 16; j++)
if (in_node[j].i_mode & IALLOC) n++;
}
return((n + 15) / 16);
}
countfree()
{
register n, m;
n = superblk->s_nfree;
if (n)
{
m = superblk->s_free[0];
while (m)
{
getblk(in, general, m);
n =+ general[0];
m = general[1];
}
}
return(n);
}
/********************************************************/
struct { unsigned u; };
initial()
{
register j, *a, *d;
extern char end; /* identify the end of the bss */
bigbuf = (&end + 077) & 0177700; /* start on 32w boundary */
a = KISA0;
d = KISD0;
for (j = 0; j < 01600; j =+ 0200)
{
*a++ = j;
*d++ = 077406; /* 4kw r/w */
}
*d = 077406;
*a-- = 07600; /* i/o page */
SSR0->i++; /* start mem-mngmnt */
for (*a = (bigbuf >> 6); *a < 07600; (*a)++)
for (d = 0140000; d.u < 0140100; *d++ = 0); /* 32 w */
}
initiam()
{
int register j;
SSR0->i = 0; /* stop mem-mngmnt */
printf("\n\nMem = %dkw\n", (j = KISA6->i) >> 5);
iob = ((--j) - (bigbuf >> 6)) >> 3;
if (iob > 256) iob = 256; /* Max. disk i/o size */
nio = BLKPERDSK / iob;
szio = iob * 256;
iov = BLKPERDSK - (nio * iob);
szov = iov * 256;
printf("%d big i/o's (each %d blocks)", nio, iob);
if (iov) printf(" + one at %d blocks", iov);
printf("\n\n");
}