code: purgatorio

Download patch

ref: 82a3f55c5fb7b9f7a82449e4eb943c535ec3e491
parent: 8a788aea84aad3bfbd5b39d78c1925654f3b9e14
author: henesy <devnull@localhost>
date: Wed Mar 13 02:19:12 EDT 2019

add missing files ;; add pete whois/tac

--- /dev/null
+++ b/appl/cmd/disdep.b
@@ -1,0 +1,250 @@
+implement Disdep;
+
+#
+# Copyright  © 2000 Vita Nuova Limited.  All rights reserved.
+#
+
+include "sys.m";
+	sys: Sys;
+	print, sprint: import sys;
+
+include "bufio.m";
+	bufio: Bufio;
+	Iobuf: import bufio;
+
+include "draw.m";
+
+include "string.m";
+	str: String;
+
+include "arg.m";
+	arg: Arg;
+
+include "dis.m";
+	dis: Dis;
+	Mod: import dis;
+
+include "hash.m";
+	hash: Hash;
+	HashTable, HashVal: import hash;
+
+Disdep: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Item: adt {
+	name:	string;
+	needs:	cyclic list of ref Item;
+	visited:	int;
+
+	find:		fn(s: string): ref Item;
+};
+
+bout: ref Iobuf;
+pending: list of ref Item;
+roots: list of ref Item;
+tab: ref HashTable;
+aflag := 0;		# display all non-recursive dependencies
+oflag := 0;		# only list the immediate (outer) dependencies
+sflag := 0;		# include $system modules
+pflag := 0;		# show dependency sets as pairs, one per line
+showdepth := 0;	# indent to show the dependency structure
+
+noload(mod: string)
+{
+	sys->fprint(sys->fildes(2), "disdep: can't load %s: %r\n", mod);
+	raise "fail:load";
+}
+
+usage()
+{
+	sys->fprint(sys->fildes(2), "Usage: disdep [-a] [-d] [-o] [-p] [-s] file.dis ...\n");
+	raise "fail:usage";
+}
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+	sys = load Sys Sys->PATH;
+	bufio = load Bufio Bufio->PATH;
+	if(bufio == nil)
+		noload(Bufio->PATH);
+
+	str = load String String->PATH;
+	if(str == nil)
+		noload(String->PATH);
+
+	hash = load Hash Hash->PATH;
+	if(hash == nil)
+		noload(Hash->PATH);
+
+	arg = load Arg Arg->PATH;
+	if(arg == nil)
+		noload(Arg->PATH);
+
+	dis = load Dis Dis->PATH;
+	if(dis == nil)
+		noload(Dis->PATH);
+	dis->init();
+
+	arg->init(argv);
+	while((opt := arg->opt()) != 0)
+		case opt {
+		'a' => aflag = 1; showdepth = 1;
+		'o' => oflag = 1;
+		's' => sflag = 1;
+		'd' => showdepth = 1;
+		'p' => pflag = 1;
+		* => usage();
+		}
+
+	argv = arg->argv();
+	if(argv == nil)
+		usage();
+
+	tab = hash->new(521);
+
+	bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+	for(l := rev(argv); l != nil; l = tl l)
+		roots = Item.find(hd l) :: roots;
+	pending = roots;
+	while(pending != nil){
+		f := hd pending;
+		pending = tl pending;
+		(m, s) := dis->loadobj(f.name);
+		if(s != nil){
+			sys->fprint(sys->fildes(2), "disdep: can't open %s: %s\n", f.name, s);
+			continue;
+		}
+		f.needs = disfind(m);
+		for(nl := f.needs; nl != nil; nl = tl nl){
+			n := hd nl;
+			if(!n.visited){
+				n.visited = 1;
+				if(!oflag && !isdol(n.name))
+					pending = n :: pending;
+			}
+		}
+	}
+
+	if(pflag){
+		for(i := 0; i < nextitem; i++){
+			f := items[i];
+			if(f.needs != nil){
+				for(nl := f.needs; nl != nil; nl = tl nl){
+					bout.puts(f.name);
+					bout.putc(' ');
+					bout.puts((hd nl).name);
+					bout.putc('\n');
+				}
+			}else{
+				bout.puts(f.name);
+				bout.putc('\n');
+			}
+		}
+	}else{
+		unvisited();
+		for(; roots != nil; roots = tl roots){
+			if(aflag)
+				unvisited();
+			f := hd roots;
+			depth := 0;
+			if(showdepth){
+				bout.puts(f.name);
+				bout.putc('\n');
+				depth = 1;
+			}
+			prdep(hd roots, depth);
+		}
+	}
+	bout.flush();
+}
+
+disfind(m: ref Mod): list of ref Item
+{
+	needs: list of ref Item;
+	for(d := m.data; d != nil; d = tl d) {
+		pick dat := hd d {
+		String =>
+			if(isdisfile(dat.str) || sflag && isdol(dat.str))
+				needs = Item.find(dat.str) :: needs;
+		}
+	}
+	return rev(needs);
+}
+
+prdep(f: ref Item, depth: int)
+{
+	f.visited = 1;	# short-circuit self-reference
+	for(nl := f.needs; nl != nil; nl = tl nl){
+		n := hd nl;
+		if(!n.visited){
+			n.visited = 1;
+			name(n.name, depth);
+			prdep(n, depth+1);
+		}else if(aflag)
+			name(n.name, depth);
+	}
+}
+			
+items := array[100] of ref Item;
+nextitem := 0;
+
+Item.find(name: string): ref Item
+{
+	k := tab.find(name);
+	if(k != nil)
+		return items[k.i];
+	if(nextitem >= len items){
+		a := array[len items + 100] of ref Item;
+		a[0:] = items;
+		items = a;
+	}
+	f := ref Item;
+	f.name = name;
+	f.visited = 0;
+	items[nextitem] = f;
+	tab.insert(name, HashVal(nextitem, 0.0, nil));
+	nextitem++;
+	return f;
+}
+
+unvisited()
+{
+	for(i := 0; i < nextitem; i++)
+		items[i].visited = 0;
+}
+
+name(s: string, depth: int)
+{
+	if(showdepth)
+		for(i:=0; i<depth; i++)
+			bout.putc('\t');
+	bout.puts(s);
+	bout.putc('\n');
+}
+
+isdisfile(s: string): int
+{
+	if(len s > 4 && s[len s-4:]==".dis"){	# worth a look
+		for(i := 0; i < len s; i++)
+			if(s[i] <= ' ' || s[i] == '%')
+				return 0;
+		return 1;
+	}
+	return 0;
+}
+
+isdol(s: string): int
+{
+	return len s > 1 && s[0] == '$' && s[1]>='A' && s[1]<='Z';	# reasonable guess
+}
+
+rev[T](l: list of T): list of T
+{
+	t: list of T;
+	for(; l != nil; l = tl l)
+		t = hd l :: t;
+	return t;
+}
+
--- /dev/null
+++ b/appl/cmd/disdump.b
@@ -1,0 +1,52 @@
+implement Disdump;
+
+include "sys.m";
+	sys: Sys;
+include "draw.m";
+include "dis.m";
+	dis: Dis;
+include "bufio.m";
+	bufio: Bufio;
+	Iobuf: import bufio;
+
+Disdump: module {
+	init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+	sys = load Sys Sys->PATH;
+	stderr := sys->fildes(2);
+	bufio = load Bufio Bufio->PATH;
+	if (bufio == nil) {
+		sys->fprint(stderr, "dis: cannot load %s: %r\n", Bufio->PATH);
+		raise "fail:bad module";
+	}
+
+	dis = load Dis Dis->PATH;
+	if (dis == nil) {
+		sys->fprint(stderr, "dis: cannot load %s: %r\n", Dis->PATH);
+		raise "fail:bad module";
+	}
+
+	if (len argv < 2) {
+		sys->fprint(stderr, "usage: dis module...\n");
+		raise "fail:usage";
+	}
+	dis->init();
+	out := bufio->fopen(sys->fildes(1), Sys->OWRITE);
+	errs := 0;
+	for (argv = tl argv; argv != nil; argv = tl argv) {
+		(mod, err) := dis->loadobj(hd argv);
+		if (mod == nil) {
+			sys->fprint(stderr, "dis: failed to load %s: %s\n", hd argv, err);
+			errs++;
+			continue;
+		}
+		for (i := 0; i < len mod.inst; i++)
+			out.puts(dis->inst2s(mod.inst[i])+"\n");
+	}
+	out.close();
+	if (errs)
+		raise "fail:errors";
+}
--- /dev/null
+++ b/appl/cmd/limbo/dis.b
@@ -1,0 +1,560 @@
+
+NAMELEN:	con 28;
+
+cache:		array of byte;
+ncached:	int;
+ndatum:		int;
+startoff:	int;
+lastoff:	int;
+lastkind:	int;
+
+discon(val: int)
+{
+	if(val >= -64 && val <= 63){
+		bout.putb(byte (val & ~16r80));
+		return;
+	}
+	if(val >= -8192 && val <= 8191){
+		bout.putb(byte ((val>>8) & ~16rC0 | 16r80));
+		bout.putb(byte val);
+		return;
+	}
+	if(val < 0 && ((val >> 29) & 7) != 7
+	|| val > 0 && (val >> 29) != 0)
+		fatal("overflow in constant 16r"+hex(val, 0));
+	bout.putb(byte(val>>24 | 16rC0));
+	bout.putb(byte(val>>16));
+	bout.putb(byte(val>>8));
+	bout.putb(byte val);
+}
+
+disword(w: int)
+{
+	bout.putb(byte(w >> 24));
+	bout.putb(byte(w >> 16));
+	bout.putb(byte(w >> 8));
+	bout.putb(byte w);
+}
+
+disdata(kind, n: int)
+{
+	if(n < DMAX && n != 0)
+		bout.putb(byte((kind << DBYTE) | n));
+	else{
+		bout.putb(byte kind << DBYTE);
+		discon(n);
+	}
+}
+
+dismod(m: ref Decl)
+{
+	fileoff := bout.seek(big 0, 1);
+	name := array of byte m.sym.name;
+	n := len name;
+	if(n > NAMELEN-1)
+		n = NAMELEN-1;
+	bout.write(name, n);
+	bout.putb(byte 0);
+	for(m = m.ty.tof.ids; m != nil; m = m.next){
+		case m.store{
+		Dglobal =>
+			discon(-1);
+			discon(-1);
+			disword(sign(m));
+			bout.puts(".mp");
+			bout.putb(byte 0);
+		Dfn =>
+			discon(m.pc.pc);
+			discon(m.desc.id);
+			disword(sign(m));
+			if(m.dot.ty.kind == Tadt){
+				bout.puts(m.dot.sym.name);
+				bout.putb(byte '.');
+			}
+			bout.puts(m.sym.name);
+			bout.putb(byte 0);
+		* =>
+			fatal("unknown kind in dismod: "+declconv(m));
+		}
+	}
+	if(debug['s'])
+		print("%bd linkage bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+dispath()
+{
+	sp := array of byte srcpath();
+	bout.write(sp, len sp);
+	bout.putb(byte 0);
+}
+
+disentry(e: ref Decl)
+{
+	if(e == nil){
+		discon(-1);
+		discon(-1);
+		return;
+	}
+	discon(e.pc.pc);
+	discon(e.desc.id);
+}
+
+disdesc(d: ref Desc)
+{
+	fileoff := bout.seek(big 0, 1);
+	for(; d != nil; d = d.next){
+		discon(d.id);
+		discon(d.size);
+		discon(d.nmap);
+		bout.write(d.map, d.nmap);
+	}
+	if(debug['s'])
+		print("%bd type descriptor bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disvar(nil: int, ids: ref Decl)
+{
+	fileoff := bout.seek(big 0, 1);
+	lastkind = -1;
+	ncached = 0;
+	ndatum = 0;
+
+	for(d := ids; d != nil; d = d.next)
+		if(d.store == Dglobal && d.init != nil)
+			disdatum(d.offset, d.init);
+
+	disflush(-1, -1, 0);
+
+	bout.putb(byte 0);
+
+	if(debug['s'])
+		print("%bd data bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disldt(size: int, ds: ref Decl)
+{
+	if(0){
+		discon(size);
+		disvar(size, ds);
+		return;
+	}
+
+	m := 0;
+	for(d := ds; d != nil; d = d.next)
+		if(d.store == Dglobal && d.init != nil)
+			m++;
+	discon(m);
+	n: ref Node;
+	for(d = ds; d != nil; d = d.next){
+		if(d.store == Dglobal && d.init != nil){
+			n = d.init;
+			if(n.ty.kind != Tiface)
+				nerror(n, "disldt: not Tiface");
+			discon(int n.c.val);
+			for(id := n.decl.ty.ids; id != nil; id = id.next){
+				disword(sign(id));
+				if(id.dot.ty.kind == Tadt){
+					s := array of byte id.dot.sym.name;
+					bout.write(s, len s);
+					bout.putb(byte '.');
+				}
+				s := array of byte id.sym.name;
+				bout.write(s, len s);
+				bout.putb(byte 0);
+			}
+		}
+	}
+	discon(0);
+}
+
+disdatum(offset: int, n: ref Node)
+{
+	c: ref Case;
+	lab: Label;
+	id: ref Decl;
+	wild: ref Node;
+	i, e: int;
+
+	case n.ty.kind{
+	Tbyte =>
+		disbyte(offset, byte n.c.val);
+	Tint or
+	Tfix =>
+		disint(offset, int n.c.val);
+	Tbig =>
+		disbig(offset, n.c.val);
+	Tstring =>
+		disstring(offset, n.decl.sym);
+	Treal =>
+		disreal(offset, n.c.rval);
+	Tadt or
+	Tadtpick or
+	Ttuple =>
+		id = n.ty.ids;
+		for(n = n.left; n != nil; n = n.right){
+			disdatum(offset + id.offset, n.left);
+			id = id.next;
+		}
+	Tany =>
+		break;
+	Tcase =>
+		c = n.ty.cse;
+		disint(offset, c.nlab);
+		offset += IBY2WD;
+		for(i = 0; i < c.nlab; i++){
+			lab = c.labs[i];
+			disint(offset, int lab.start.c.val);
+			offset += IBY2WD;
+			disint(offset, int lab.stop.c.val+1);
+			offset += IBY2WD;
+			disint(offset, lab.inst.pc);
+			offset += IBY2WD;
+		}
+		if(c.iwild != nil)
+			disint(offset, c.iwild.pc);
+		else
+			disint(offset, -1);
+	Tcasel =>
+		c = n.ty.cse;
+		disint(offset, c.nlab);
+		offset += 2*IBY2WD;
+		for(i = 0; i < c.nlab; i++){
+			lab = c.labs[i];
+			disbig(offset, lab.start.c.val);
+			offset += IBY2LG;
+			disbig(offset, lab.stop.c.val+big 1);
+			offset += IBY2LG;
+			disint(offset, lab.inst.pc);
+			offset += 2*IBY2WD;
+		}
+		if(c.iwild != nil)
+			disint(offset, c.iwild.pc);
+		else
+			disint(offset, -1);
+	Tcasec =>
+		c = n.ty.cse;
+		disint(offset, c.nlab);
+		offset += IBY2WD;
+		for(i = 0; i < c.nlab; i++){
+			lab = c.labs[i];
+			disstring(offset, lab.start.decl.sym);
+			offset += IBY2WD;
+			if(lab.stop != lab.start)
+				disstring(offset, lab.stop.decl.sym);
+			offset += IBY2WD;
+			disint(offset, lab.inst.pc);
+			offset += IBY2WD;
+		}
+		if(c.iwild != nil)
+			disint(offset, c.iwild.pc);
+		else
+			disint(offset, -1);
+	Tgoto =>
+		c = n.ty.cse;
+		disint(offset, n.ty.size/IBY2WD-1);
+		offset += IBY2WD;
+		for(i = 0; i < c.nlab; i++){
+			disint(offset, c.labs[i].inst.pc);
+			offset += IBY2WD;
+		}
+		if(c.iwild != nil)
+			disint(offset, c.iwild.pc);
+	Tarray =>
+		disflush(-1, -1, 0);
+		disdata(DEFA, 1);		# 1 is ignored
+		discon(offset);
+		disword(n.ty.tof.decl.desc.id);
+		disword(int n.left.c.val);
+
+		if(n.right == nil)
+			break;
+
+		disdata(DIND, 1);		# 1 is ignored
+		discon(offset);
+		disword(0);
+
+		c = n.right.ty.cse;
+		wild = nil;
+		if(c.wild != nil)
+			wild = c.wild.right;
+		last := 0;
+		esz := n.ty.tof.size;
+		for(i = 0; i < c.nlab; i++){
+			e = int c.labs[i].start.c.val;
+			if(wild != nil){
+				for(; last < e; last++)
+					disdatum(esz * last, wild);
+			}
+			last = e;
+			e = int c.labs[i].stop.c.val;
+			elem := c.labs[i].node.right;
+			for(; last <= e; last++)
+				disdatum(esz * last, elem);
+		}
+		if(wild != nil)
+			for(e = int n.left.c.val; last < e; last++)
+				disdatum(esz * last, wild);
+
+		disflush(-1, -1, 0);
+		disdata(DAPOP, 1);		# 1 is ignored
+		discon(0);
+	Tiface =>
+		disint(offset, int n.c.val);
+		offset += IBY2WD;
+		for(id = n.decl.ty.ids; id != nil; id = id.next){
+			offset = align(offset, IBY2WD);
+			disint(offset, sign(id));
+			offset += IBY2WD;
+
+			name: array of byte;
+			if(id.dot.ty.kind == Tadt){
+				name = array of byte id.dot.sym.name;
+				disbytes(offset, name);
+				offset += len name;
+				disbyte(offset, byte '.');
+				offset++;
+			}
+			name = array of byte id.sym.name;
+			disbytes(offset, name);
+			offset += len name;
+			disbyte(offset, byte 0);
+			offset++;
+		}
+	* =>
+		fatal("can't gen global "+nodeconv(n));
+	}
+}
+
+disexc(es: ref Except)
+{
+	e: ref Except;
+
+	n := 0;
+	for(e = es; e != nil; e = e.next)
+		if(int e.p1.reach || int e.p2.reach)
+			n++;
+	discon(n);
+	for(e = es; e != nil; e = e.next){
+		if(!int e.p1.reach && !int e.p2.reach)
+			continue;
+		c := e.c;
+		discon(e.d.offset);
+		discon(getpc(e.p1));
+		discon(getpc(e.p2));
+		if(e.desc != nil)
+			discon(e.desc.id);
+		else
+			discon(-1);
+		discon(c.nlab|(e.ne<<16));
+		for(i := 0; i < c.nlab; i++){
+			lab := c.labs[i];
+			d := lab.start.decl;
+			if(lab.start.ty.kind == Texception)
+				d = d.init.decl;
+			bout.puts(d.sym.name);
+			bout.putb(byte 0);
+			discon(lab.inst.pc);
+		}
+		if(c.iwild == nil)
+			discon(-1);
+		else
+			discon(c.iwild.pc);
+	}
+	discon(0);
+}
+
+disbyte(off: int, v: byte)
+{
+	disflush(DEFB, off, 1);
+	cache[ncached++] = v;
+	ndatum++;
+}
+
+disbytes(off: int, v: array of byte)
+{
+	n := len v;
+	disflush(DEFB, off, n);
+	cache[ncached:] = v;
+	ncached += n;
+	ndatum += n;
+}
+
+disint(off, v: int)
+{
+	disflush(DEFW, off, IBY2WD);
+	cache[ncached++] = byte(v >> 24);
+	cache[ncached++] = byte(v >> 16);
+	cache[ncached++] = byte(v >> 8);
+	cache[ncached++] = byte(v);
+	ndatum++;
+}
+
+disbig(off: int, v: big)
+{
+	disflush(DEFL, off, IBY2LG);
+	iv := int(v >> 32);
+	cache[ncached++] = byte(iv >> 24);
+	cache[ncached++] = byte(iv >> 16);
+	cache[ncached++] = byte(iv >> 8);
+	cache[ncached++] = byte(iv);
+	iv = int v;
+	cache[ncached++] = byte(iv >> 24);
+	cache[ncached++] = byte(iv >> 16);
+	cache[ncached++] = byte(iv >> 8);
+	cache[ncached++] = byte(iv);
+	ndatum++;
+}
+
+disreal(off: int, v: real)
+{
+	disflush(DEFF, off, IBY2LG);
+	export_real(cache[ncached:ncached+8], array[] of {v});
+	ncached += IBY2LG;
+	ndatum++;
+}
+
+disstring(offset: int, sym: ref Sym)
+{
+	disflush(-1, -1, 0);
+	d := array of byte sym.name;
+	disdata(DEFS, len d);
+	discon(offset);
+	bout.write(d, len d);
+}
+
+disflush(kind, off, size: int)
+{
+	if(kind != lastkind || off != lastoff){
+		if(lastkind != -1 && ncached){
+			disdata(lastkind, ndatum);
+			discon(startoff);
+			bout.write(cache, ncached);
+		}
+		startoff = off;
+		lastkind = kind;
+		ncached = 0;
+		ndatum = 0;
+	}
+	lastoff = off + size;
+	while(kind >= 0 && ncached + size >= len cache){
+		c := array[ncached + 1024] of byte;
+		c[0:] = cache;
+		cache = c;
+	}
+}
+
+dismode := array[int Aend] of
+{
+	int Aimm =>	byte AIMM,
+	int Amp =>	byte AMP,
+	int Ampind =>	byte(AMP|AIND),
+	int Afp =>	byte AFP,
+	int Afpind =>	byte(AFP|AIND),
+	int Apc =>	byte AIMM,
+	int Adesc =>	byte AIMM,
+	int Aoff =>	byte AIMM,
+	int Anoff =>	byte AIMM,
+	int Aerr =>	byte AXXX,
+	int Anone =>	byte AXXX,
+	int Aldt =>	byte AIMM,
+};
+
+disregmode := array[int Aend] of
+{
+	int Aimm =>	byte AXIMM,
+	int Amp =>	byte AXINM,
+	int Ampind =>	byte AXNON,
+	int Afp =>	byte AXINF,
+	int Afpind =>	byte AXNON,
+	int Apc =>	byte AXIMM,
+	int Adesc =>	byte AXIMM,
+	int Aoff =>	byte AXIMM,
+	int Anoff =>	byte AXIMM,
+	int Aerr =>	byte AXNON,
+	int Anone =>	byte AXNON,
+	int Aldt =>	byte AXIMM,
+};
+
+MAXCON: con 4;
+MAXADDR: con 2*MAXCON;
+MAXINST: con 3*MAXADDR+2;
+NIBUF: con 1024;
+
+ibuf:	array of byte;
+nibuf:	int;
+
+disinst(in: ref Inst)
+{
+	fileoff := bout.seek(big 0, 1);
+	ibuf = array[NIBUF] of byte;
+	nibuf = 0;
+	for(; in != nil; in = in.next){
+		if(in.op == INOOP)
+			continue;
+		if(nibuf >= NIBUF-MAXINST){
+			bout.write(ibuf, nibuf);
+			nibuf = 0;
+		}
+		ibuf[nibuf++] = byte in.op;
+		o := dismode[int in.sm] << SRC;
+		o |= dismode[int in.dm] << DST;
+		o |= disregmode[int in.mm];
+		ibuf[nibuf++] = o;
+		if(in.mm != Anone)
+			disaddr(in.mm, in.m);
+		if(in.sm != Anone)
+			disaddr(in.sm, in.s);
+		if(in.dm != Anone)
+			disaddr(in.dm, in.d);
+	}
+	if(nibuf > 0)
+		bout.write(ibuf, nibuf);
+	ibuf = nil;
+
+	if(debug['s'])
+		print("%bd instruction bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disaddr(m: byte, a: Addr)
+{
+	val := 0;
+	case int m{
+	int Aimm or
+	int Apc or
+	int Adesc =>
+		val = a.offset;
+	int Aoff =>
+		val = a.decl.iface.offset;
+	int Anoff =>
+		val = -(a.decl.iface.offset+1);
+	int Afp or
+	int Amp or
+	int Aldt =>
+		val = a.reg;
+	int Afpind or
+	int Ampind =>
+		disbcon(a.reg);
+		val = a.offset;
+	}
+	disbcon(val);
+}
+
+disbcon(val: int)
+{
+	if(val >= -64 && val <= 63){
+		ibuf[nibuf++] = byte(val & ~16r80);
+		return;
+	}
+	if(val >= -8192 && val <= 8191){
+		ibuf[nibuf++] = byte(val>>8 & ~16rC0 | 16r80);
+		ibuf[nibuf++] = byte val;
+		return;
+	}
+	if(val < 0 && ((val >> 29) & 7) != 7
+	|| val > 0 && (val >> 29) != 0)
+		fatal("overflow in constant 16r"+hex(val, 0));
+	ibuf[nibuf++] = byte(val>>24 | 16rC0);
+	ibuf[nibuf++] = byte(val>>16);
+	ibuf[nibuf++] = byte(val>>8);
+	ibuf[nibuf++] = byte val;
+}
--- /dev/null
+++ b/appl/cmd/limbo/disoptab.m
@@ -1,0 +1,355 @@
+movetab:= array [Mend]of
+{
+	Mas => array[Tend] of
+	{
+		Tadt =>		IMOVM,
+		Tadtpick =>	IMOVM,
+		Tarray =>	IMOVP,
+		Tbig =>		IMOVL,
+		Tbyte =>	IMOVB,
+		Tchan =>	IMOVP,
+		Treal =>	IMOVF,
+		Tint =>		IMOVW,
+		Tlist =>	IMOVP,
+		Tmodule =>	IMOVP,
+		Tref =>		IMOVP,
+		Tstring =>	IMOVP,
+		Ttuple =>	IMOVM,
+		Texception => IMOVM,
+		Tfix =>	IMOVW,
+		Tpoly =>	IMOVP,
+
+		Tany =>		IMOVP,
+
+		* =>		0
+	},
+	Mcons => array[Tend] of
+	{
+		Tadt =>		ICONSM,
+		Tadtpick =>	0,
+		Tarray =>	ICONSP,
+		Tbig =>		ICONSL,
+		Tbyte =>	ICONSB,
+		Tchan =>	ICONSP,
+		Treal =>	ICONSF,
+		Tint =>		ICONSW,
+		Tlist =>	ICONSP,
+		Tmodule =>	ICONSP,
+		Tref =>		ICONSP,
+		Tstring =>	ICONSP,
+		Ttuple =>	ICONSM,
+		Texception => ICONSM,
+		Tfix =>	ICONSW,
+		Tpoly =>	ICONSP,
+
+		Tany =>		ICONSP,
+
+		* =>		0
+	},
+	Mhd => array[Tend] of
+	{
+		Tadt =>		IHEADM,
+		Tadtpick =>	0,
+		Tarray =>	IHEADP,
+		Tbig =>		IHEADL,
+		Tbyte =>	IHEADB,
+		Tchan =>	IHEADP,
+		Treal =>	IHEADF,
+		Tint =>		IHEADW,
+		Tlist =>	IHEADP,
+		Tmodule =>	IHEADP,
+		Tref =>		IHEADP,
+		Tstring =>	IHEADP,
+		Ttuple =>	IHEADM,
+		Texception => IHEADM,
+		Tfix =>	IHEADW,
+		Tpoly =>	IHEADP,
+
+		Tany =>		IHEADP,
+
+		* =>		0
+	},
+	Mtl  => array[Tend] of
+	{
+		Tlist =>	ITAIL,
+
+		* =>		0
+	},
+};
+
+chantab := array[Tend] of
+{
+	Tadt =>		INEWCM,
+	Tadtpick =>	0,
+	Tarray =>	INEWCP,
+	Tbig =>		INEWCL,
+	Tbyte =>	INEWCB,
+	Tchan =>	INEWCP,
+	Treal =>	INEWCF,
+	Tint =>		INEWCW,
+	Tlist =>	INEWCP,
+	Tmodule =>	INEWCP,
+	Tref =>		INEWCP,
+	Tstring =>	INEWCP,
+	Ttuple =>	INEWCM,
+	Texception => INEWCM,
+	Tfix =>	INEWCW,
+	Tpoly =>	INEWCP,
+
+	Tany =>		INEWCP,
+
+	* =>	0
+};
+
+opind := array[Tend] of
+{
+	Tbyte =>	1,
+	Tint =>		2,
+	Tbig =>		3,
+	Treal =>	4,
+	Tstring =>	5,
+	Tfix => 6,
+
+	* =>		0
+};
+
+disoptab := array[Oend+1] of
+{
+	# opcode			default	byte	word	big	real	string	fixed
+	Oadd =>		array[7] of	{0,	IADDB,	IADDW,	IADDL,	IADDF,	IADDC,	IADDW,},
+	Oaddas =>	array[7] of	{0,	IADDB,	IADDW,	IADDL,	IADDF,	IADDC,	IADDW,},
+	Oand =>		array[7] of	{0,	IANDB,	IANDW,	IANDL,	0,	0,	0,},
+	Oandas =>	array[7] of	{0,	IANDB,	IANDW,	IANDL,	0, 	0,	0,},
+	Odec =>		array[7] of	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	Odiv =>		array[7] of	{0,	IDIVB,	IDIVW,	IDIVL,	IDIVF,	0,	IDIVX,},
+	Odivas =>	array[7] of	{0,	IDIVB,	IDIVW,	IDIVL,	IDIVF,	0,	IDIVX,},
+	Oeq =>		array[7] of	{IBEQW,	IBEQB,	IBEQW,	IBEQL,	IBEQF,	IBEQC,	IBEQW,},
+	Oexp =>	array[7] of	{0,	0,	IEXPW,	IEXPL,	IEXPF,	0,	0,},
+	Oexpas =>	array[7] of	{0,	0,	IEXPW,	IEXPL,	IEXPF,	0,	0,},
+	Ogeq =>		array[7] of	{0,	IBGEB,	IBGEW,	IBGEL,	IBGEF,	IBGEC,	IBGEW,},
+	Ogt =>		array[7] of	{0,	IBGTB,	IBGTW,	IBGTL,	IBGTF,	IBGTC,	IBGTW,},
+	Oinc =>		array[7] of	{0,	IADDB,	IADDW,	IADDL,	IADDF,	0,	IADDW,},
+	Oinds =>	array[7] of	{0,	0,	IINDC,	0,	0,	0,	0,},
+	Oindx =>	array[7] of	{0,	0,	IINDX,	0,	0,	0,	0,},
+	Olen =>		array[7] of	{ILENA,	0,	0,	0,	0,	ILENC,	0,},
+	Oleq =>		array[7] of	{0,	IBLEB,	IBLEW,	IBLEL,	IBLEF,	IBLEC,	IBLEW,},
+	Olsh =>		array[7] of	{0,	ISHLB,	ISHLW,	ISHLL,	0,	0,	0,},
+	Olshas =>	array[7] of	{0,	ISHLB,	ISHLW,	ISHLL,	0,	0,	0,},
+	Olt =>		array[7] of	{0,	IBLTB,	IBLTW,	IBLTL,	IBLTF,	IBLTC,	IBLTW,},
+	Omod =>		array[7] of	{0,	IMODB,	IMODW,	IMODL,	0,	0,	0,},
+	Omodas =>	array[7] of	{0,	IMODB,	IMODW,	IMODL,	0,	0,	0,},
+	Omul =>		array[7] of	{0,	IMULB,	IMULW,	IMULL,	IMULF,	0,	IMULX,},
+	Omulas =>	array[7] of	{0,	IMULB,	IMULW,	IMULL,	IMULF,	0,	IMULX,},
+	Oneg =>		array[7] of	{0,	0,	0,	0,	INEGF,	0,	0, },
+	Oneq =>		array[7] of	{IBNEW,	IBNEB,	IBNEW,	IBNEL,	IBNEF,	IBNEC,	IBNEW,},
+	Oor =>		array[7] of	{0,	IORB,	IORW,	IORL,	0,	0,	0,},
+	Ooras =>	array[7] of	{0,	IORB,	IORW,	IORL,	0,	0,	0,},
+	Orsh =>		array[7] of	{0,	ISHRB,	ISHRW,	ISHRL,	0,	0,	0,},
+	Orshas =>	array[7] of	{0,	ISHRB,	ISHRW,	ISHRL,	0,	0,	0,},
+	Oslice =>	array[7] of	{ISLICEA,0,	0,	0,	0,	ISLICEC,	0,},
+	Osub =>		array[7] of	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	Osubas =>	array[7] of	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	Oxor =>		array[7] of	{0,	IXORB,	IXORW,	IXORL,	0,	0,	0,},
+	Oxoras =>	array[7] of	{0,	IXORB,	IXORW,	IXORL,	0,	0,	0,},
+};
+
+isbyteinst := array [256] of
+{
+	IMULB	=> 1,
+	ISUBB	=> 1,
+	IADDB	=> 1,
+	IDIVB	=> 1,
+	IORB	=> 1,
+	IXORB	=> 1,
+	ISHLB	=> 1,
+	ISHRB	=> 1,
+	IMODB	=> 1,
+	IANDB	=> 1,
+	IBEQB	=> 1,
+	IBNEB	=> 1,
+	IBLTB	=> 1,
+	IBLEB	=> 1,
+	IBGTB	=> 1,
+	IBGEB	=> 1,
+
+	*	=> 0,
+};
+
+instname := array[256] of
+{
+	"nop",
+	"alt",
+	"nbalt",
+	"goto",
+	"call",
+	"frame",
+	"spawn",
+	"runt",
+	"load",
+	"mcall",
+	"mspawn",
+	"mframe",
+	"ret",
+	"jmp",
+	"case",
+	"exit",
+	"new",
+	"newa",
+	"newcb",
+	"newcw",
+	"newcf",
+	"newcp",
+	"newcm",
+	"newcmp",
+	"send",
+	"recv",
+	"consb",
+	"consw",
+	"consp",
+	"consf",
+	"consm",
+	"consmp",
+	"headb",
+	"headw",
+	"headp",
+	"headf",
+	"headm",
+	"headmp",
+	"tail",
+	"lea",
+	"indx",
+	"movp",
+	"movm",
+	"movmp",
+	"movb",
+	"movw",
+	"movf",
+	"cvtbw",
+	"cvtwb",
+	"cvtfw",
+	"cvtwf",
+	"cvtca",
+	"cvtac",
+	"cvtwc",
+	"cvtcw",
+	"cvtfc",
+	"cvtcf",
+	"addb",
+	"addw",
+	"addf",
+	"subb",
+	"subw",
+	"subf",
+	"mulb",
+	"mulw",
+	"mulf",
+	"divb",
+	"divw",
+	"divf",
+	"modw",
+	"modb",
+	"andb",
+	"andw",
+	"orb",
+	"orw",
+	"xorb",
+	"xorw",
+	"shlb",
+	"shlw",
+	"shrb",
+	"shrw",
+	"insc",
+	"indc",
+	"addc",
+	"lenc",
+	"lena",
+	"lenl",
+	"beqb",
+	"bneb",
+	"bltb",
+	"bleb",
+	"bgtb",
+	"bgeb",
+	"beqw",
+	"bnew",
+	"bltw",
+	"blew",
+	"bgtw",
+	"bgew",
+	"beqf",
+	"bnef",
+	"bltf",
+	"blef",
+	"bgtf",
+	"bgef",
+	"beqc",
+	"bnec",
+	"bltc",
+	"blec",
+	"bgtc",
+	"bgec",
+	"slicea",
+	"slicela",
+	"slicec",
+	"indw",
+	"indf",
+	"indb",
+	"negf",
+	"movl",
+	"addl",
+	"subl",
+	"divl",
+	"modl",
+	"mull",
+	"andl",
+	"orl",
+	"xorl",
+	"shll",
+	"shrl",
+	"bnel",
+	"bltl",
+	"blel",
+	"bgtl",
+	"bgel",
+	"beql",
+	"cvtlf",
+	"cvtfl",
+	"cvtlw",
+	"cvtwl",
+	"cvtlc",
+	"cvtcl",
+	"headl",
+	"consl",
+	"newcl",
+	"casec",
+	"indl",
+	"movpc",
+	"tcmp",
+	"mnewz",
+	"cvtrf",
+	"cvtfr",
+	"cvtws",
+	"cvtsw",
+	"lsrw",
+	"lsrl",
+	"eclr",
+	"newz",
+	"newaz",
+	"raise",
+	"casel",
+	"mulx",
+	"divx",
+	"cvtxx",
+	"mulx0",
+	"divx0",
+	"cvtxx0",
+	"mulx1",
+	"divx1",
+	"cvtxx1",
+	"cvtfx",
+	"cvtxf",
+	"expw",
+	"expl",
+	"expf",
+	"self",
+};
--- /dev/null
+++ b/appl/cmd/limbo/sbl.b
@@ -1,0 +1,397 @@
+
+sbltname := array[Tend] of
+{
+	Tnone =>	byte 'n',
+	Tadt =>		byte 'a',
+	Tadtpick =>	byte 'a',
+	Tarray =>	byte 'A',
+	Tbig =>		byte 'B',
+	Tbyte =>	byte 'b',
+	Tchan =>	byte 'C',
+	Treal =>	byte 'f',
+	Tfn =>		byte 'F',
+	Tint =>		byte 'i',
+	Tlist =>	byte 'L',
+	Tmodule =>	byte 'm',
+	Tref =>		byte 'R',
+	Tstring =>	byte 's',
+	Ttuple =>	byte 't',
+	Texception => byte 't',
+	Tfix => byte 'i',
+	Tpoly => byte 'P',
+
+	Tainit =>	byte '?',
+	Talt =>		byte '?',
+	Tany =>		byte 'N',
+	Tarrow =>	byte '?',
+	Tcase =>	byte '?',
+	Tcasel =>	byte '?',
+	Tcasec =>	byte '?',
+	Tdot =>		byte '?',
+	Terror =>	byte '?',
+	Tgoto =>	byte '?',
+	Tid =>		byte '?',
+	Tiface =>	byte '?',
+	Texcept => byte '?',
+	Tinst => byte '?',
+};
+sbltadtpick:	con byte 'p';
+
+sfiles:		ref Sym;
+ftail:		ref Sym;
+nsfiles:	int;
+blockid:	int;
+lastf:		int;
+lastline:	int;
+
+MAXSBLINT:	con 12;
+MAXSBLSRC:	con 6*(MAXSBLINT+1);
+
+sblmod(m: ref Decl)
+{
+	bsym.puts("limbo .sbl 2.1\n");
+	bsym.puts(m.sym.name);
+	bsym.putb(byte '\n');
+
+	blockid = 0;
+	nsfiles = 0;
+	sfiles = ftail = nil;
+	lastf = 0;
+	lastline = 0;
+}
+
+sblfile(name: string): int
+{
+	i := 0;
+	for(s := sfiles; s != nil; s = s.next){
+		if(s.name == name)
+			return i;
+		i++;
+	}
+	s = ref Sym;
+	s.name = name;
+	s.next = nil;
+	if(sfiles == nil)
+		sfiles = s;
+	else
+		ftail.next = s;
+	ftail = s;
+	nsfiles = i + 1;
+	return i;
+}
+
+filename(s: string): string
+{
+	(nil, file) := str->splitr(s, "/ \\");
+	return file;
+}
+
+sblfiles()
+{
+	for(i := 0; i < nfiles; i++)
+		files[i].sbl = sblfile(files[i].name);
+	bsym.puts(string nsfiles);
+	bsym.putb(byte '\n');
+	for(s := sfiles; s != nil; s = s.next){
+		bsym.puts(filename(s.name));
+		bsym.putb(byte '\n');
+	}
+}
+
+sblint(buf: array of byte, off, v: int): int
+{
+	if(v == 0){
+		buf[off++] = byte '0';
+		return off;
+	}
+	stop := off + MAXSBLINT;
+	if(v < 0){
+		buf[off++] = byte '-';
+		v = -v;
+	}
+	n := stop;
+	while(v > 0){
+		buf[n -= 1] = byte(v % 10 + '0');
+		v = v / 10;
+	}
+	while(n < stop)
+		buf[off++] = buf[n++];
+	return off;
+}
+
+sblsrcconvb(buf: array of byte, off: int, src: Src): int
+{
+	(startf, startl) := fline(src.start >> PosBits);
+	(stopf, stopl) := fline(src.stop >> PosBits);
+	if(lastf != startf.sbl){
+		off = sblint(buf, off, startf.sbl);
+		buf[off++] = byte ':';
+	}
+	if(lastline != startl){
+		off = sblint(buf, off, startl);
+		buf[off++] = byte '.';
+	}
+	off = sblint(buf, off, (src.start & PosMask));
+	buf[off++] = byte ',';
+	if(startf.sbl != stopf.sbl){
+		off = sblint(buf, off, stopf.sbl);
+		buf[off++] = byte ':';
+	}
+	if(startl != stopl){
+		off = sblint(buf, off, stopl);
+		buf[off++] = byte '.';
+	}
+	off = sblint(buf, off, (src.stop & PosMask));
+	buf[off++] = byte ' ';
+	lastf = stopf.sbl;
+	lastline = stopl;
+	return off;
+}
+
+sblsrcconv(src: Src): string
+{
+	s := "";
+	(startf, startl) := fline(src.start >> PosBits);
+	(stopf, stopl) := fline(src.stop >> PosBits);
+	if(lastf != startf.sbl){
+		s += string startf.sbl;
+		s[len s] = ':';
+	}
+	if(lastline != startl){
+		s += string startl;
+		s[len s] = '.';
+	}
+	s += string (src.start & PosMask);
+	s[len s] = ',';
+	if(startf.sbl != stopf.sbl){
+		s += string stopf.sbl;
+		s[len s] = ':';
+	}
+	if(startl != stopl){
+		s += string stopl;
+		s[len s] = '.';
+	}
+	s += string (src.stop & PosMask);
+	s[len s] = ' ';
+	lastf = stopf.sbl;
+	lastline = stopl;
+	return s;
+}
+
+isnilsrc(s: Src): int
+{
+	return s.start == 0 && s.stop == 0;
+}
+
+isnilstopsrc(s: Src): int
+{
+	return s.stop == 0;
+}
+
+sblinst(in: ref Inst, ninst: int)
+{
+	src: Src;
+
+	MAXSBL:	con 8*1024;
+	buf := array[MAXSBL] of byte;
+	n := 0;
+	bsym.puts(string ninst);
+	bsym.putb(byte '\n');
+	sblblocks := array[nblocks] of {* => -1};
+	for(; in != nil; in = in.next){
+		if(in.op == INOOP)
+			continue;
+		if(in.src.start < 0)
+			fatal("no file specified for "+instconv(in));
+		if(n >= (MAXSBL - MAXSBLSRC - MAXSBLINT - 1)){
+			bsym.write(buf, n);
+			n = 0;
+		}
+		if(isnilsrc(in.src))
+			in.src = src;
+		else if(isnilstopsrc(in.src)){	# how does this happen ?
+			in.src.stop = in.src.start;
+			in.src.stop++;
+		}
+		n = sblsrcconvb(buf, n, in.src);
+		src = in.src;
+		b := sblblocks[in.block];
+		if(b < 0)
+			sblblocks[in.block] = b = blockid++;
+		n = sblint(buf, n, b);
+		buf[n++] = byte '\n';
+	}
+	if(n > 0)
+		bsym.write(buf, n);
+}
+
+sblty(tys: array of ref Decl, ntys: int)
+{
+	bsym.puts(string ntys);
+	bsym.putb(byte '\n');
+	for(i := 0; i < ntys; i++){
+		d := tys[i];
+		d.ty.sbl = i;
+	}
+	for(i = 0; i < ntys; i++){
+		d := tys[i];
+		sbltype(d.ty, 1);
+	}
+}
+
+sblfn(fns: array of ref Decl, nfns: int)
+{
+	bsym.puts(string nfns);
+	bsym.putb(byte '\n');
+	for(i := 0; i < nfns; i++){
+		f := fns[i];
+		if(ispoly(f))
+			rmfnptrs(f);
+		bsym.puts(string f.pc.pc);
+		bsym.putb(byte ':');
+		if(f.dot != nil && f.dot.ty.kind == Tadt){
+			bsym.puts(f.dot.sym.name);
+			bsym.putb(byte '.');
+		}
+		bsym.puts(f.sym.name);
+		bsym.putb(byte '\n');
+		sbldecl(f.ty.ids, Darg);
+		sbldecl(f.locals, Dlocal);
+		sbltype(f.ty.tof, 0);
+	}
+}
+
+sblvar(vars: ref Decl)
+{
+	sbldecl(vars, Dglobal);
+}
+
+isvis(id: ref Decl): int
+{
+	if(!tattr[id.ty.kind].vis
+	|| id.sym == nil
+	|| id.sym.name == ""
+	|| id.sym.name[0] == '.')
+		return 0;
+	if(id.ty == tstring && id.init != nil && id.init.op == Oconst)
+		return 0;
+	if(id.src.start < 0 || id.src.stop < 0)
+		return 0;
+	return 1;
+}
+
+sbldecl(ids: ref Decl, store: int)
+{
+	n := 0;
+	for(id := ids; id != nil; id = id.next){
+		if(id.store != store || !isvis(id))
+			continue;
+		n++;
+	}
+	bsym.puts(string n);
+	bsym.putb(byte '\n');
+	for(id = ids; id != nil; id = id.next){
+		if(id.store != store || !isvis(id))
+			continue;
+		bsym.puts(string id.offset);
+		bsym.putb(byte ':');
+		bsym.puts(id.sym.name);
+		bsym.putb(byte ':');
+		bsym.puts(sblsrcconv(id.src));
+		sbltype(id.ty, 0);
+		bsym.putb(byte '\n');
+	}
+}
+
+sbltype(t: ref Type, force: int)
+{
+	if(t.kind == Tadtpick)
+		t = t.decl.dot.ty;
+
+	d := t.decl;
+	if(!force && d != nil && d.ty.sbl >= 0){
+		bsym.putb(byte '@');
+		bsym.puts(string d.ty.sbl);
+		bsym.putb(byte '\n');
+		return;
+	}
+
+	if(t.rec != byte 0)
+		fatal("recursive sbl type: "+typeconv(t));
+
+	t.rec = byte 1;
+	case t.kind{
+	* =>
+		fatal("bad type in sbltype: "+typeconv(t));
+	Tnone or
+	Tany or
+	Tint or
+	Tbig or
+	Tbyte or
+	Treal or
+	Tstring or
+	Tfix or
+	Tpoly =>
+		bsym.putb(sbltname[t.kind]);
+	Tfn =>
+		bsym.putb(sbltname[t.kind]);
+		sbldecl(t.ids, Darg);
+		sbltype(t.tof, 0);
+	Tarray or
+	Tlist or
+	Tchan or
+	Tref =>
+		bsym.putb(sbltname[t.kind]);
+		if(t.kind == Tref && t.tof.kind == Tfn){
+			tattr[Tany].vis = 1;
+			sbltype(tfnptr, 0);
+			tattr[Tany].vis = 0;
+		}
+		else
+			sbltype(t.tof, 0);
+	Ttuple or
+	Texception =>
+		bsym.putb(sbltname[t.kind]);
+		bsym.puts(string t.size);
+		bsym.putb(byte '.');
+		sbldecl(t.ids, Dfield);
+	Tadt =>
+		if(t.tags != nil)
+			bsym.putb(sbltadtpick);
+		else
+			bsym.putb(sbltname[t.kind]);
+		if(d.dot != nil && !isimpmod(d.dot.sym))
+			bsym.puts(d.dot.sym.name + "->");
+		bsym.puts(d.sym.name);
+		bsym.putb(byte ' ');
+		bsym.puts(sblsrcconv(d.src));
+		bsym.puts(string d.ty.size);
+		bsym.putb(byte '\n');
+		sbldecl(t.ids, Dfield);
+		if(t.tags != nil){
+			bsym.puts(string t.decl.tag);
+			bsym.putb(byte '\n');
+			lastt : ref Type = nil;
+			for(tg := t.tags; tg != nil; tg = tg.next){
+				bsym.puts(tg.sym.name);
+				bsym.putb(byte ':');
+				bsym.puts(sblsrcconv(tg.src));
+				if(lastt == tg.ty){
+					bsym.putb(byte '\n');
+				}else{
+					bsym.puts(string tg.ty.size);
+					bsym.putb(byte '\n');
+					sbldecl(tg.ty.ids, Dfield);
+				}
+				lastt = tg.ty;
+			}
+		}
+	Tmodule =>
+		bsym.putb(sbltname[t.kind]);
+		bsym.puts(d.sym.name);
+		bsym.putb(byte '\n');
+		bsym.puts(sblsrcconv(d.src));
+		sbldecl(t.ids, Dglobal);
+	}
+	t.rec = byte 0;
+}
--- /dev/null
+++ b/appl/cmd/tac.b
@@ -1,0 +1,58 @@
+implement Tac;
+
+include "sys.m"; sys: Sys;
+include "bufio.m"; bufio: Bufio; Iobuf: import bufio;
+include "draw.m";
+
+Tac: module {
+	init: fn(nil: ref Draw->Context, args: list of string);
+};
+
+
+stdout: ref Sys->FD;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+	sys = load Sys Sys->PATH;
+	bufio = load Bufio Bufio->PATH;
+	stdout = sys->fildes(1);
+	args = tl args;
+
+	if(args == nil)
+		args = "-" :: nil;
+
+	for(; args != nil; args = tl args) {
+		file := hd args;
+		if(file != "-") {
+			fd := sys->open(file, Sys->OREAD);
+			if(fd == nil){
+				sys->fprint(sys->fildes(2), "tac: cannot open %s: %r\n", file);
+				raise "fail:bad open";
+			}
+			tac(fd, file);
+		} else {
+			tac(sys->fildes(0), "<stdin>");
+		}
+	}
+}
+
+tac(fd: ref Sys->FD, file: string)
+{
+	lines: list of string = nil;
+	line: string;
+
+	fio := bufio->fopen(fd, bufio->OREAD);
+	if(fio == nil) {
+		sys->fprint(sys->fildes(2), "tac: Couldn't create a bufio for %s:  %r\n", file);
+		raise "fail:bufio";
+	}
+
+	while((line = fio.gets('\n')) != nil)
+		lines = line :: lines;
+
+	while(lines != nil) {
+		buf := array of byte (hd lines);
+		sys->write(stdout, buf, len buf);
+		lines = tl lines;
+	}
+}
--- /dev/null
+++ b/appl/cmd/whois.b
@@ -1,0 +1,89 @@
+implement Whois;
+
+include "sys.m"; sys: Sys;
+include "draw.m";
+include "dial.m"; dial: Dial;
+include "arg.m";
+
+Whois: module {
+	init: fn(nil: ref Draw->Context, args: list of string);
+};
+
+init(nil: ref Draw->Context, args: list of string) {
+	sys = load Sys Sys->PATH;
+	dial = load Dial Dial->PATH;
+	arg := load Arg Arg->PATH;
+
+	addr := "tcp!whois.iana.org!43";
+	expect_refer := 5;
+
+	arg->init(args);
+	arg->setusage("whois [-a addr] [-n] host");
+	while((opt := arg->opt()) != 0) {
+		case opt {
+		'a' =>
+			addr = dial->netmkaddr(arg->earg(), "tcp", "43");
+		'n' =>
+			expect_refer = 0;
+		* =>
+			arg->usage();
+		}
+	}
+
+	args = arg->argv();
+	if(len args != 1)
+		arg->usage();
+
+	host := hd args;
+	fd := whois(addr, host);
+
+	buf := array[512] of byte;
+	stdout := sys->fildes(1);
+	while((i := sys->read(fd, buf, len buf)) > 0) {
+		if(expect_refer) {
+			ls := sys->tokenize(string buf[0:i], "\n").t1;
+			newaddr: string = nil;
+			while(ls != nil) {
+				l := hd ls;
+				(n, rs) := sys->tokenize(l, " \t");
+				if(n == 2 && hd rs == "refer:") {
+					newaddr = dial->netmkaddr(hd tl rs, "tcp", "43");
+					break;
+				} else if(n == 3 && hd rs == "Whois" && hd tl rs == "Server:") {
+					newaddr = dial->netmkaddr(hd tl tl rs, "tcp", "43");
+					break;
+				}
+				ls = tl ls;
+			}
+			if(newaddr != nil) {
+				fd = whois(newaddr, host);
+				expect_refer--;
+				continue;
+			}
+		}
+		sys->write(stdout, buf, i);
+	}
+	if(i < 0) {
+		sys->fprint(sys->fildes(2), "whois: reading info: %r\n");
+		raise "fail:errors";
+	}
+}
+
+whois(addr: string, host: string): ref Sys->FD
+{
+	sys->print("[using server %s]\n", addr);
+	conn := dial->dial(addr, nil);
+	if(conn == nil) {
+		sys->fprint(sys->fildes(2), "whois: dialing %s: %r\n", addr);
+		raise "fail:errors";
+	}
+
+	fd := conn.dfd;
+	i := sys->fprint(fd, "%s\r\n", host);
+	if(i != len host + 2) {
+		sys->fprint(sys->fildes(2), "whois: sending name: %r\n");
+		raise "fail:errors";
+	}
+
+	return fd;
+}
--- /dev/null
+++ b/appl/lib/dis.b
@@ -1,0 +1,609 @@
+implement Dis;
+
+#
+# Derived by Vita Nuova Limited 1998 from /appl/wm/rt.b, which is
+# Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+#
+
+include "sys.m";
+	sys: Sys;
+	sprint: import sys;
+
+include "math.m";
+	math: Math;
+
+include "dis.m";
+
+disptr: int;
+disobj: array of byte;
+
+optab := array[] of {
+	"nop",
+	"alt",
+	"nbalt",
+	"goto",
+	"call",
+	"frame",
+	"spawn",
+	"runt",
+	"load",
+	"mcall",
+	"mspawn",
+	"mframe",
+	"ret",
+	"jmp",
+	"case",
+	"exit",
+	"new",
+	"newa",
+	"newcb",
+	"newcw",
+	"newcf",
+	"newcp",
+	"newcm",
+	"newcmp",
+	"send",
+	"recv",
+	"consb",
+	"consw",
+	"consp",
+	"consf",
+	"consm",
+	"consmp",
+	"headb",
+	"headw",
+	"headp",
+	"headf",
+	"headm",
+	"headmp",
+	"tail",
+	"lea",
+	"indx",
+	"movp",
+	"movm",
+	"movmp",
+	"movb",
+	"movw",
+	"movf",
+	"cvtbw",
+	"cvtwb",
+	"cvtfw",
+	"cvtwf",
+	"cvtca",
+	"cvtac",
+	"cvtwc",
+	"cvtcw",
+	"cvtfc",
+	"cvtcf",
+	"addb",
+	"addw",
+	"addf",
+	"subb",
+	"subw",
+	"subf",
+	"mulb",
+	"mulw",
+	"mulf",
+	"divb",
+	"divw",
+	"divf",
+	"modw",
+	"modb",
+	"andb",
+	"andw",
+	"orb",
+	"orw",
+	"xorb",
+	"xorw",
+	"shlb",
+	"shlw",
+	"shrb",
+	"shrw",
+	"insc",
+	"indc",
+	"addc",
+	"lenc",
+	"lena",
+	"lenl",
+	"beqb",
+	"bneb",
+	"bltb",
+	"bleb",
+	"bgtb",
+	"bgeb",
+	"beqw",
+	"bnew",
+	"bltw",
+	"blew",
+	"bgtw",
+	"bgew",
+	"beqf",
+	"bnef",
+	"bltf",
+	"blef",
+	"bgtf",
+	"bgef",
+	"beqc",
+	"bnec",
+	"bltc",
+	"blec",
+	"bgtc",
+	"bgec",
+	"slicea",
+	"slicela",
+	"slicec",
+	"indw",
+	"indf",
+	"indb",
+	"negf",
+	"movl",
+	"addl",
+	"subl",
+	"divl",
+	"modl",
+	"mull",
+	"andl",
+	"orl",
+	"xorl",
+	"shll",
+	"shrl",
+	"bnel",
+	"bltl",
+	"blel",
+	"bgtl",
+	"bgel",
+	"beql",
+	"cvtlf",
+	"cvtfl",
+	"cvtlw",
+	"cvtwl",
+	"cvtlc",
+	"cvtcl",
+	"headl",
+	"consl",
+	"newcl",
+	"casec",
+	"indl",
+	"movpc",
+	"tcmp",
+	"mnewz",
+	"cvtrf",
+	"cvtfr",
+	"cvtws",
+	"cvtsw",
+	"lsrw",
+	"lsrl",
+	"eclr",
+	"newz",
+	"newaz",
+	"raise",
+	"casel",
+	"mulx",
+	"divx",
+	"cvtxx",
+	"mulx0",
+	"divx0",
+	"cvtxx0",
+	"mulx1",
+	"divx1",
+	"cvtxx1",
+	"cvtfx",
+	"cvtxf",
+	"expw",
+	"expl",
+	"expf",
+	"self",
+};
+
+init()
+{
+	sys = load Sys  Sys->PATH;
+	math = load Math Math->PATH;	# optional
+}
+
+loadobj(disfile: string): (ref Mod, string)
+{
+	fd := sys->open(disfile, sys->OREAD);
+	if(fd == nil)
+		return (nil, "open failed: "+sprint("%r"));
+
+	(ok, d) := sys->fstat(fd);
+	if(ok < 0)
+		return (nil, "stat failed: "+sprint("%r"));
+
+	objlen := int d.length;
+	disobj = array[objlen] of byte;
+
+	if(sys->read(fd, disobj, objlen) != objlen){
+		disobj = nil;
+		return (nil, "read failed: "+sprint("%r"));
+	}
+
+	disptr = 0;
+	m := ref Mod;
+	m.magic = operand();
+	if(m.magic == SMAGIC) {
+		n := operand();
+		m.sign = disobj[disptr:disptr+n];
+		disptr += n;
+		m.magic = operand();
+	}
+	if(m.magic != XMAGIC){
+		disobj = nil;
+		return (nil, "bad magic number");
+	}
+
+	m.rt = operand();
+	m.ssize = operand();
+	m.isize = operand();
+	m.dsize = operand();
+	m.tsize = operand();
+	m.lsize = operand();
+	m.entry = operand();
+	m.entryt = operand();
+
+	m.inst = array[m.isize] of ref Inst;
+	for(i := 0; i < m.isize; i++) {
+		o := ref Inst;
+		o.op = int disobj[disptr++];
+		o.addr = int disobj[disptr++];
+		case o.addr & ARM {
+		AXIMM or
+		AXINF or
+		AXINM =>
+			o.mid = operand();
+		}
+
+		case (o.addr>>3) & 7 {
+		AFP or
+		AMP or
+		AIMM =>
+			o.src = operand();
+		AIND|AFP or
+		AIND|AMP =>
+			o.src = operand()<<16;
+			o.src |= operand();
+		}
+
+		case o.addr & 7	 {
+		AFP or
+		AMP or
+		AIMM =>
+			o.dst = operand();
+		AIND|AFP or
+		AIND|AMP =>
+			o.dst = operand()<<16;
+			o.dst |= operand();
+		}
+		m.inst[i] = o;
+	}
+
+	m.types = array[m.tsize] of ref Type;
+	for(i = 0; i < m.tsize; i++) {
+		h := ref Type;
+		id := operand();
+		h.size = operand();
+		h.np = operand();
+		h.map = disobj[disptr:disptr+h.np];
+		disptr += h.np;
+		m.types[i] = h;
+	}
+
+	for(;;) {
+		op := int disobj[disptr++];
+		if(op == 0)
+			break;
+
+		n := op & (DMAX-1);
+		if(n == 0)
+			n = operand();
+
+		offset := operand();
+
+		dat: ref Data;
+		case op>>4 {
+		DEFB =>
+			dat = ref Data.Bytes(op, n, offset, disobj[disptr:disptr+n]);
+			disptr += n;
+		DEFW =>
+			words := array[n] of int;
+			for(i = 0; i < n; i++)
+				words[i] = getw();
+			dat = ref Data.Words(op, n, offset, words);
+		DEFS =>
+			dat = ref Data.String(op, n, offset, string disobj[disptr:disptr+n]);
+			disptr += n;
+		DEFF =>
+			if(math != nil){
+				reals := array[n] of real;
+				for(i = 0; i < n; i++)
+					reals[i] = math->bits64real(getl());
+				dat = ref Data.Reals(op, n, offset, reals);
+			} else {
+				disptr += 8*n;	# skip it
+				dat = ref Data.Reals(op, n, offset, nil);
+			}
+			break;
+		DEFA =>
+			typex := getw();
+			length := getw();
+			dat = ref Data.Array(op, n, offset, typex, length);
+		DIND =>
+			dat = ref Data.Aindex(op, n, offset, getw());
+		DAPOP =>
+			dat = ref Data.Arestore(op, n, offset);
+		DEFL =>
+			bigs := array[n] of big;
+			for(i = 0; i < n; i++)
+				bigs[i] = getl();
+			dat = ref Data.Bigs(op, n, offset, bigs);
+		* =>
+			dat = ref Data.Zero(op, n, offset);
+		}
+		m.data = dat :: m.data;
+	}
+
+	m.data = revdat(m.data);
+
+	m.name = gets();
+
+	m.links = array[m.lsize] of ref Link;
+	for(i = 0; i < m.lsize; i++) {
+		l := ref Link;
+		l.pc = operand();
+		l.desc = operand();
+		l.sig = getw();
+		l.name = gets();
+
+		m.links[i] = l;
+	}
+
+	if(m.rt & Dis->HASLDT0)
+		raise "obsolete dis";
+
+	if(m.rt & Dis->HASLDT){
+		nl := operand();
+		imps := array[nl] of array of ref Import;
+		for(i = 0; i < nl; i++){
+			n := operand();
+			imps[i] = array[n] of ref Import;
+			for(j := 0; j < n; j++){
+				imps[i][j] = im := ref Import;
+				im.sig = getw();
+				im.name = gets();
+			}
+		}
+		disptr++;
+		m.imports = imps;
+	}
+
+	if(m.rt & Dis->HASEXCEPT){
+		nh := operand();	# number of handlers
+		hs := array[nh] of ref Handler;
+		for(i = 0; i < nh; i++){
+			h := hs[i] = ref Handler;
+			h.eoff = operand();
+			h.pc1 = operand();
+			h.pc2 = operand();
+			t := operand();
+			if(t >= 0)
+				h.t = m.types[t];
+			n := operand();	
+			h.ne = n>>16;
+			n &= 16rffff;	# number of cases
+			h.etab = array[n+1] of ref Except;
+			for(j := 0; j < n; j++){
+				e := h.etab[j] = ref Except;
+				k := disptr;
+				while(int disobj[disptr++])	# pattern
+					;
+				e.s = string disobj[k: disptr-1];
+				e.pc = operand();
+			}
+			e := h.etab[j] = ref Except;
+			e.pc = operand();	# * pc
+		}
+		disptr++;	# 0 byte
+		m.handlers = hs;
+	}
+
+	m.srcpath = gets();
+
+	disobj = nil;
+	return (m, nil);
+}
+
+operand(): int
+{
+	if(disptr >= len disobj)
+		return -1;
+
+	b := int disobj[disptr++];
+
+	case b & 16rC0 {
+	16r00 =>
+		return b;
+	16r40 =>
+		return b | ~16r7F;
+	16r80 =>
+		if(disptr >= len disobj)
+			return -1;
+		if(b & 16r20)
+			b |= ~16r3F;
+		else
+			b &= 16r3F;
+		return (b<<8) | int disobj[disptr++];
+	16rC0 =>
+		if(disptr+2 >= len disobj)
+			return -1;
+		if(b & 16r20)
+			b |= ~16r3F;
+		else
+			b &= 16r3F;
+		b = b<<24 |
+			(int disobj[disptr]<<16) |
+		    	(int disobj[disptr+1]<<8)|
+		    	int disobj[disptr+2];
+		disptr += 3;
+		return b;
+	}
+	return 0;
+}
+
+get4(a: array of byte, i: int): int
+{
+	return (int a[i+0] << 24) | (int a[i+1] << 16) | (int a[i+2] << 8) | int a[i+3];
+}
+
+getw(): int
+{
+	if(disptr+3 >= len disobj)
+		return -1;
+	i := (int disobj[disptr+0]<<24) |
+	     (int disobj[disptr+1]<<16) |
+	     (int disobj[disptr+2]<<8) |
+	      int disobj[disptr+3];
+
+	disptr += 4;
+	return i;
+}
+
+getl(): big
+{
+	if(disptr+7 >= len disobj)
+		return big -1;
+	i := (big disobj[disptr+0]<<56) |
+	     (big disobj[disptr+1]<<48) |
+	     (big disobj[disptr+2]<<40) |
+	     (big disobj[disptr+3]<<32) |
+	     (big disobj[disptr+4]<<24) |
+	     (big disobj[disptr+5]<<16) |
+	     (big disobj[disptr+6]<<8) |
+	      big disobj[disptr+7];
+
+	disptr += 8;
+	return i;
+}
+
+gets(): string
+{
+	s := disptr;
+	while(disptr < len disobj && disobj[disptr] != byte 0)
+		disptr++;
+
+	v := string disobj[s:disptr];
+	disptr++;
+	return v;
+}
+
+revdat(d: list of ref Data): list of ref Data
+{
+	t: list of ref Data;
+
+	while(d != nil) {
+		t = hd d :: t;
+		d = tl d;
+	}
+	return t;
+}
+
+op2s(op: int): string
+{
+	if(op < 0 || op >= len optab)
+		return sys->sprint("OP%d", op);
+	return optab[op];
+}
+
+inst2s(o: ref Inst): string
+{
+	fi := 0;
+	si := 0;
+	s := sprint("%-10s", optab[o.op]);
+	src := "";
+	dst := "";
+	mid := "";
+	case (o.addr>>3) & 7 {
+	AFP =>
+		src = sprint("%d(fp)", o.src);
+	AMP =>
+		src = sprint("%d(mp)", o.src);
+	AIMM =>
+		src = sprint("$%d", o.src);
+	AIND|AFP =>
+		fi = (o.src>>16) & 16rFFFF;
+		si = o.src & 16rFFFF;
+		src = sprint("%d(%d(fp))", si, fi);
+	AIND|AMP =>
+		fi = (o.src>>16) & 16rFFFF;
+		si = o.src & 16rFFFF;
+		src = sprint("%d(%d(mp))", si, fi);
+	}
+
+	case o.addr & ARM {
+	AXIMM =>
+		mid = sprint("$%d", o.mid);
+	AXINF =>
+		mid = sprint("%d(fp)", o.mid);
+	AXINM =>
+		mid = sprint("%d(mp)", o.mid);
+	}
+
+	case o.addr & 7 {
+	AFP =>
+		dst = sprint("%d(fp)", o.dst);
+	AMP =>
+		dst = sprint("%d(mp)", o.dst);
+	AIMM =>
+		dst = sprint("$%d", o.dst);
+	AIND|AFP =>
+		fi = (o.dst>>16) & 16rFFFF;
+		si = o.dst & 16rFFFF;
+		dst = sprint("%d(%d(fp))", si, fi);
+	AIND|AMP =>
+		fi = (o.dst>>16) & 16rFFFF;
+		si = o.dst & 16rFFFF;
+		dst = sprint("%d(%d(mp))", si, fi);
+	}
+	if(mid == "") {
+		if(src == "")
+			s += sprint("%s", dst);
+		else if(dst == "")
+			s += sprint("%s", src);
+		else
+			s += sprint("%s, %s", src, dst);
+	}
+	else
+		s += sprint("%s, %s, %s", src, mid, dst);
+
+	return s;
+}
+
+getsb(fd: ref Sys->FD, o: int): (string, int)
+{
+	b := array[1] of byte;
+	buf := array[8192] of byte;
+	p := len buf;
+	for( ; ; o++){
+		sys->seek(fd, big -o, Sys->SEEKEND);
+		if(sys->read(fd, b, 1) != 1)
+			return (nil, 0);
+		if(b[0] == byte 0){
+			if(p < len buf)
+				break;
+		}
+		else if(p > 0)
+			buf[--p] = b[0];
+	}
+	return (string buf[p: ], o);
+}
+
+src(disf: string): string
+{
+	fd := sys->open(disf, sys->OREAD);
+	if(fd == nil)
+		return nil;
+	(s, nil) := getsb(fd, 1);
+	if(s != nil && s[0] == '/')
+		return s;
+	return nil;
+}
--- /dev/null
+++ b/appl/lib/diskblocks.b
@@ -1,0 +1,122 @@
+implement Diskblocks;
+
+#
+# adapted from Acme's disk.b
+#
+
+include "sys.m";
+	sys: Sys;
+
+include "diskblocks.m";
+
+init()
+{
+	sys = load Sys Sys->PATH;
+}
+
+tempfile(): ref Sys->FD
+{
+	user := "inferno";
+	fd := sys->open("/dev/user", Sys->OREAD);
+	if(fd != nil){
+		b := array[Sys->NAMEMAX] of byte;
+		n := sys->read(fd, b, len b);
+		if(n > 0)
+			user = string b[0:n];
+	}
+	fd = nil;
+	buf := sys->sprint("/tmp/X%d.%.4sblks", sys->pctl(0, nil), user);
+	for(i:='A'; i<='Z'; i++){
+		buf[5] = i;
+		if(sys->stat(buf).t0 == 0)
+			continue;
+		fd = sys->create(buf, Sys->ORDWR|Sys->ORCLOSE|Sys->OEXCL, 8r600);
+		if(fd != nil)
+			return fd;
+	}
+	return nil;
+}
+
+Disk.init(fd: ref Sys->FD, gran: int, maxblock: int): ref Disk
+{
+	d := ref Disk;
+	if(gran == 0 || maxblock%gran != 0)
+		return nil;
+	d.maxblock = maxblock;
+	d.gran = gran;
+	d.free = array[maxblock/gran+1] of list of ref Block;
+	d.addr = big 0;
+	d.fd = fd;
+	d.lock = chan[1] of int;
+	return d;
+}
+
+ntosize(d: ref Disk, n: int): (int, int)
+{
+	if (n > d.maxblock)
+		return (-1, -1);
+	size := n;
+	if((size % d.gran) != 0)
+		size += d.gran - size%d.gran;
+	# last bucket holds blocks of exactly d.maxblock
+	return (size, size/d.gran);
+}
+
+Disk.new(d: self ref Disk, n: int): ref Block
+{
+	(size, i) := ntosize(d, n);
+	if(i < 0){
+		sys->werrstr("illegal Disk allocation");
+		return nil;
+	}
+	b: ref Block;
+	d.lock <-= 1;
+	if(d.free[i] != nil){
+		b = hd d.free[i];
+		d.free[i] = tl d.free[i];
+	}else{
+		b = ref Block(d.addr, 0);
+		d.addr += big size;
+	}
+	<-d.lock;
+	b.n = n;
+	return b;
+}
+
+Disk.release(d: self ref Disk, b: ref Block)
+{
+	(nil, i) := ntosize(d, b.n);
+	d.lock <-= 1;
+	d.free[i] = b :: d.free[i];
+	<-d.lock;
+}
+
+Disk.write(d: self ref Disk, b: ref Block, a: array of byte, n: int): ref Block
+{
+	if(b != nil){
+		(size, nil) := ntosize(d, b.n);
+		(nsize, nil) := ntosize(d, n);
+		if(size != nsize){
+			d.release(b);
+			b = d.new(n);
+		}
+	}else
+		b = d.new(n);
+	if(b == nil)
+		return nil;
+	if(sys->pwrite(d.fd, a, n, b.addr) != n){
+		sys->werrstr(sys->sprint("Disk write error: %r"));
+		return nil;
+	}
+	b.n = n;
+	return b;
+}
+
+Disk.read(d: self ref Disk, b: ref Block, a: array of byte, n: int): int
+{
+	if(b == nil || n > b.n){
+		sys->werrstr("read request bigger than block");
+		return -1;
+	}
+	return sys->pread(d.fd, a, n, b.addr);
+}
--- /dev/null
+++ b/appl/lib/disks.b
@@ -1,0 +1,363 @@
+implement Disks;
+
+# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02
+
+include "sys.m";
+	sys: Sys;
+
+include "bufio.m";
+	bufio: Bufio;
+	Iobuf: import bufio;
+
+include "disks.m";
+
+scsiverbose := 0;
+
+Codefile: con "/lib/scsicodes";
+
+Code: adt {
+	v:	int;	# (asc<<8) | ascq
+	s:	string;
+};
+codes: array of Code;
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	bufio = load Bufio Bufio->PATH;
+}
+
+#
+# Discover the disk geometry by various sleazeful means.
+# 
+# First, if there is a partition table in sector 0,
+# see if all the partitions have the same end head
+# and sector; if so, we'll assume that that's the 
+# right count.
+# 
+# If that fails, we'll try looking at the geometry that the ATA
+# driver supplied, if any, and translate that as a
+# BIOS might. 
+# 
+# If that too fails, which should only happen on a SCSI
+# disk with no currently defined partitions, we'll try
+# various common (h, s) pairs used by BIOSes when faking
+# the geometries.
+#
+
+# table entry:
+	Oactive,			# active flag
+	Ostarth,			# starting head
+	Ostarts,			# starting sector
+	Ostartc,			# starting cylinder
+	Otype,			# partition type
+	Oendh,			# ending head
+	Oends,			# ending sector
+	Oendc: con iota;		# ending cylinder
+	Oxlba: con Oendc+1;	# starting LBA from start of disc or partition [4]
+	Oxsize: con Oxlba+4;	# size in sectors[4]
+
+# Table: entry[NTentry][TentrySize] magic[2]
+Omagic: con NTentry*TentrySize;
+
+partitiongeometry(d: ref Disk): int
+{
+	buf := array[512] of byte;
+	t := buf[Toffset:];
+
+	#
+	# look for an MBR first in the /dev/sdXX/data partition, otherwise
+	# attempt to fall back on the current partition.
+	#
+	rawfd := sys->open(d.prefix+"data", Sys->OREAD);
+	if(rawfd != nil
+	&& sys->seek(rawfd, big 0, 0) == big 0
+	&& sys->readn(rawfd, buf, 512) == 512
+	&& int t[Omagic] == Magic0
+	&& int t[Omagic+1] == Magic1) {
+		rawfd = nil;
+	}else{
+		rawfd = nil;
+		if(sys->seek(d.fd, big 0, 0) < big 0
+		|| sys->readn(d.fd, buf, 512) != 512
+		|| int t[Omagic] != Magic0
+		|| int t[Omagic+1] != Magic1)
+			return -1;
+	}
+
+	h := s := -1;
+	for(i:=0; i<NTentry*TentrySize; i += TentrySize) {
+		if(t[i+Otype] == byte 0)
+			continue;
+
+		t[i+Oends] &= byte 63;
+		if(h == -1) {
+			h = int t[i+Oendh];
+			s = int t[i+Oends];
+		} else {
+			#
+			# Only accept the partition info if every
+			# partition is consistent.
+			#
+			if(h != int t[i+Oendh] || s != int t[i+Oends])
+				return -1;
+		}
+	}
+
+	if(h == -1)
+		return -1;
+
+	d.h = h+1;	# heads count from 0
+	d.s = s;	# sectors count from 1
+	d.c = int (d.secs / big (d.h*d.s));
+	d.chssrc = "part";
+	return 0;
+}
+
+#
+# If there is ATA geometry, use it, perhaps massaged
+#
+drivergeometry(d: ref Disk): int
+{
+	if(d.c == 0 || d.h == 0 || d.s == 0)
+		return -1;
+
+	d.chssrc = "disk";
+	if(d.c < 1024)
+		return 0;
+
+	case d.h {
+	15 =>
+		d.h = 255;
+		d.c /= 17;
+
+	* =>
+		for(m := 2; m*d.h < 256; m *= 2) {
+			if(d.c/m < 1024) {
+				d.c /= m;
+				d.h *= m;
+				return 0;
+			}
+		}
+
+		# set to 255, 63 and be done with it
+		d.h = 255;
+		d.s = 63;
+		d.c = int (d.secs / big(d.h * d.s));
+	}
+	return 0;
+}
+
+#
+# There's no ATA geometry and no partitions.
+# Our guess is as good as anyone's.
+#
+Guess: adt {
+	h:	int;
+	s:	int;
+};
+guess: array of Guess = array[] of {
+	(64, 32),
+	(64, 63),
+	(128, 63),
+	(255, 63),
+};
+
+guessgeometry(d: ref Disk)
+{
+	d.chssrc = "guess";
+	c := 1024;
+	for(i:=0; i<len guess; i++)
+		if(big(c*guess[i].h*guess[i].s) >= d.secs) {
+			d.h = guess[i].h;
+			d.s = guess[i].s;
+			d.c = int(d.secs / big(d.h * d.s));
+			return;
+		}
+
+	# use maximum values
+	d.h = 255;
+	d.s = 63;
+	d.c = int(d.secs / big(d.h * d.s));
+}
+
+findgeometry(disk: ref Disk)
+{
+	disk.h = disk.s = disk.c = 0;
+	if(partitiongeometry(disk) < 0 &&
+	   drivergeometry(disk) < 0)
+		guessgeometry(disk);
+}
+
+openfile(d: ref Disk): ref Disk
+{
+	(ok, db) := sys->fstat(d.fd);
+	if(ok < 0)
+		return nil;
+
+	d.secsize = 512;
+	d.size = db.length;
+	d.secs = d.size / big d.secsize;
+	d.offset = big 0;
+
+	findgeometry(d);
+	return mkwidth(d);
+}
+
+opensd(d: ref Disk): ref Disk
+{
+	b := bufio->fopen(d.ctlfd, Bufio->OREAD);
+	while((p := b.gets('\n')) != nil){
+		p = p[0:len p - 1];
+		(nf, f) := sys->tokenize(p, " \t");	# might need str->unquote
+		if(nf >= 3 && hd f == "geometry") {
+			d.secsize = int hd tl tl f;
+			if(nf >= 6) {
+				d.c = int hd tl tl tl f;
+				d.h = int hd tl tl tl tl f;
+				d.s = int hd tl tl tl tl tl f;
+			}
+		}
+		if(nf >= 4 && hd f == "part" && hd tl f == d.part) {
+			d.offset = big hd tl tl f;
+			d.secs = big hd tl tl tl f - d.offset;
+		}
+	}
+
+	
+	d.size = d.secs * big d.secsize;
+	if(d.size <= big 0) {
+		d.part = "";
+		d.dtype = "file";
+		return openfile(d);
+	}
+
+	findgeometry(d);
+	return mkwidth(d);
+}
+
+Disk.open(name: string, mode: int, noctl: int): ref Disk
+{
+	d := ref Disk;
+	d.rdonly = mode == Sys->OREAD;
+	d.fd = sys->open(name, Sys->OREAD);
+	if(d.fd == nil)
+		return nil;
+
+	if(mode != Sys->OREAD){
+		d.wfd = sys->open(name, Sys->OWRITE);
+		if(d.wfd == nil)
+			d.rdonly = 1;
+	}
+
+	if(noctl)
+		return openfile(d);
+
+	# check for floppy(3) disk
+	if(len name >= 7) {
+		q := name[len name-7:];
+		if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && q[3:] == "disk") {
+			if((d.ctlfd = sys->open(name[0:len name-4]+"ctl", Sys->ORDWR)) != nil) {
+				d.prefix = name[0:len name-4];	# fdN (unlike Plan 9)
+				d.dtype = "floppy";
+				return openfile(d);
+			}
+		}
+	}
+
+	# attempt to find sd(3) disk or partition
+	d.prefix = name;
+	for(i := len name; --i >= 0;)
+		if(name[i] == '/'){
+			d.prefix = name[0:i+1];
+			break;
+		}
+
+	if((d.ctlfd = sys->open(d.prefix+"ctl", Sys->ORDWR)) != nil) {
+		d.dtype = "sd";
+		d.part = name[len d.prefix:];
+		return opensd(d);
+	}
+
+	# assume we have just a normal file
+	d.dtype = "file";
+	return openfile(d);
+}
+
+mkwidth(d: ref Disk): ref Disk
+{
+	d.width = len sys->sprint("%bd", d.size);
+	return d;
+}
+
+isdigit(c: int): int
+{
+	return c >= '0' && c <= '9';
+}
+
+putchs(d: ref Disk, p: array of byte, lba: big)
+{
+	s := int (lba % big d.s);
+	h := int (lba / big d.s % big d.h);
+	c := int (lba / (big d.s * big d.h));
+
+	if(c >= 1024) {
+		c = 1023;
+		h = d.h - 1;
+		s = d.s - 1;
+	}
+
+	p[0] = byte h;
+	p[1] = byte (((s+1) & 16r3F) | ((c>>2) & 16rC0));
+	p[2] = byte c;
+}
+
+PCpart.bytes(p: self PCpart, d: ref Disk): array of byte
+{
+	a := array[TentrySize] of byte;
+	a[Oactive] = byte p.active;
+	a[Otype] = byte p.ptype;
+	putchs(d, a[Ostarth:], p.base+p.offset);
+	putchs(d, a[Oendh:], p.base+p.offset+p.size-big 1);
+	putle32(a[Oxlba:], p.offset);
+	putle32(a[Oxsize:], p.size);
+	return a;
+}
+
+PCpart.extract(a: array of byte, nil: ref Disk): PCpart
+{
+	p: PCpart;
+	p.active = int a[Oactive];
+	p.ptype = int a[Otype];
+	p.base = big 0;
+	p.offset = getle32(a[Oxlba:]);
+	p.size = getle32(a[Oxsize:]);
+	return p;
+}
+
+getle32(p: array of byte): big
+{
+	return (big p[3]<<24) | (big p[2]<<16) | (big p[1] << 8) | big p[0];
+}
+
+putle32(p: array of byte, i: big)
+{
+	p[0] = byte i;
+	p[1] = byte (i>>8);
+	p[2] = byte (i>>16);
+	p[3] = byte (i>>24);
+}
+
+Disk.readn(d: self ref Disk, buf: array of byte, nb: int): int
+{
+	return sys->readn(d.fd, buf, nb);
+}
+
+chstext(p: array of byte): string
+{
+	h := int p[0];
+	c := int p[2];
+	c |= (int p[1]&16rC0)<<2;
+	s := (int p[1] & 16r3F);
+	return sys->sprint("%d/%d/%d", c, h, s);
+}
binary files /dev/null b/appl/lib/ida/idatab.dist differ