more 386 runtime fixes.
can pass many tests;
current stumbling block is an 8l bug.

R=r
DELTA=122  (83 added, 8 deleted, 31 changed)
OCL=29872
CL=29876
diff --git a/src/cmd/clean.bash b/src/cmd/clean.bash
index 81cbbec..262bcca 100644
--- a/src/cmd/clean.bash
+++ b/src/cmd/clean.bash
@@ -3,7 +3,7 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-for i in cc 6l 6a 6c 8l 8a 8c 5l 5a 5c gc 6g ar db nm acid cov gobuild godefs prof gotest
+for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g ar db nm acid cov gobuild godefs prof gotest
 do
 	cd $i
 	make clean
diff --git a/src/cmd/gobuild/Makefile b/src/cmd/gobuild/Makefile
index 8c0eb52..cb326c2 100644
--- a/src/cmd/gobuild/Makefile
+++ b/src/cmd/gobuild/Makefile
@@ -2,10 +2,14 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-# sadly, not auto-generated
+# ironically, not auto-generated
 
-O=6
-OS=568vqo
+O_arm=5
+O_amd64=6
+O_386=8
+OS=568vq
+
+O=$(O_$(GOARCH))
 GC=$(O)g
 CC=$(O)c -FVw
 AS=$(O)a
diff --git a/src/cmd/make.bash b/src/cmd/make.bash
index 08cffbb..f406e35 100644
--- a/src/cmd/make.bash
+++ b/src/cmd/make.bash
@@ -7,12 +7,21 @@
 
 bash clean.bash
 
-cd 6l
+case "$GOARCH" in
+386)	O=8;;
+amd64)	O=6;;
+arm)	O=5;;
+*)
+	echo 'unknown $GOARCH' 1>&2
+	exit 1
+esac
+
+cd ${O}l
 bash mkenam
 make enam.o
 cd ..
 
-for i in cc 6l 6a 6c gc 6g ar db nm acid cov godefs prof gotest
+for i in cc ${O}l ${O}a ${O}c gc ${O}g ar db nm acid cov godefs prof gotest
 do
 	echo; echo; echo %%%% making $i %%%%; echo
 	cd $i
diff --git a/src/lib/net/fd_darwin.go b/src/lib/net/fd_darwin.go
index 3d1025d..42bf512 100644
--- a/src/lib/net/fd_darwin.go
+++ b/src/lib/net/fd_darwin.go
@@ -31,7 +31,7 @@
 }
 
 func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
-	var kmode int16;
+	var kmode int;
 	if mode == 'r' {
 		kmode = syscall.EVFILT_READ
 	} else {
@@ -39,23 +39,21 @@
 	}
 	var events [1]syscall.Kevent_t;
 	ev := &events[0];
-	ev.Ident = uint64(fd);
-	ev.Filter = kmode;
-
 	// EV_ADD - add event to kqueue list
 	// EV_RECEIPT - generate fake EV_ERROR as result of add,
 	//	rather than waiting for real event
 	// EV_ONESHOT - delete the event the first time it triggers
-	ev.Flags = syscall.EV_ADD | syscall.EV_RECEIPT;
+	flags := syscall.EV_ADD | syscall.EV_RECEIPT;
 	if !repeat {
-		ev.Flags |= syscall.EV_ONESHOT
+		flags |= syscall.EV_ONESHOT
 	}
+	syscall.SetKevent(ev, fd, kmode, flags);
 
 	n, e := syscall.Kevent(p.kq, &events, &events, nil);
 	if e != 0 {
 		return os.ErrnoToError(e)
 	}
-	if n != 1 || (ev.Flags & syscall.EV_ERROR) == 0 || ev.Ident != uint64(fd) || ev.Filter != kmode {
+	if n != 1 || (ev.Flags & syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
 		return kqueuePhaseError
 	}
 	if ev.Data != 0 {
@@ -65,7 +63,7 @@
 }
 
 func (p *pollster) DelFD(fd int, mode int) {
-	var kmode int16;
+	var kmode int;
 	if mode == 'r' {
 		kmode = syscall.EVFILT_READ
 	} else {
@@ -73,13 +71,10 @@
 	}
 	var events [1]syscall.Kevent_t;
 	ev := &events[0];
-	ev.Ident = uint64(fd);
-	ev.Filter = kmode;
-
 	// EV_DELETE - delete event from kqueue list
 	// EV_RECEIPT - generate fake EV_ERROR as result of add,
 	//	rather than waiting for real event
-	ev.Flags = syscall.EV_DELETE | syscall.EV_RECEIPT;
+	syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE | syscall.EV_RECEIPT);
 	syscall.Kevent(p.kq, &events, &events, nil);
 }
 
@@ -90,9 +85,7 @@
 			if t == nil {
 				t = new(syscall.Timespec);
 			}
-			t.Sec = nsec / 1e9;
-			t.Nsec = int64(nsec % 1e9);
-//			*t = syscall.NsecToTimespec(nsec);
+			*t = syscall.NsecToTimespec(nsec);
 		}
 		nn, e := syscall.Kevent(p.kq, nil, &p.eventbuf, t);
 		if e != 0 {
diff --git a/src/lib/syscall/asm_darwin_386.s b/src/lib/syscall/asm_darwin_386.s
index 7fb90c2..a8ec5b0 100644
--- a/src/lib/syscall/asm_darwin_386.s
+++ b/src/lib/syscall/asm_darwin_386.s
@@ -48,7 +48,7 @@
 	MOVSL
 	MOVSL
 	INT	$0x80
-	JAE	ok
+	JAE	ok6
 	MOVL	$-1, 32(SP)	// r1
 	MOVL	$-1, 36(SP)	// r2
 	MOVL	AX, 40(SP)		// errno
diff --git a/src/lib/syscall/syscall_darwin_386.go b/src/lib/syscall/syscall_darwin_386.go
index 6f82e04..5633d7c 100644
--- a/src/lib/syscall/syscall_darwin_386.go
+++ b/src/lib/syscall/syscall_darwin_386.go
@@ -6,6 +6,10 @@
 
 import "syscall"
 
+func Getpagesize() int {
+	return 4096
+}
+
 func TimespecToNsec(ts Timespec) int64 {
 	return int64(ts.Sec)*1e9 + int64(ts.Nsec);
 }
@@ -38,3 +42,8 @@
 	return err;
 }
 
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+	k.Ident = uint32(fd);
+	k.Filter = int16(mode);
+	k.Flags = uint16(flags);
+}
diff --git a/src/lib/syscall/syscall_darwin_amd64.go b/src/lib/syscall/syscall_darwin_amd64.go
index 8620232..f7a93f1 100644
--- a/src/lib/syscall/syscall_darwin_amd64.go
+++ b/src/lib/syscall/syscall_darwin_amd64.go
@@ -42,3 +42,8 @@
 	return err;
 }
 
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+	k.Ident = uint64(fd);
+	k.Filter = int16(mode);
+	k.Flags = uint16(flags);
+}
diff --git a/src/runtime/darwin/386/sys.s b/src/runtime/darwin/386/sys.s
index 1ad6d2a..bbcb622 100644
--- a/src/runtime/darwin/386/sys.s
+++ b/src/runtime/darwin/386/sys.s
@@ -88,25 +88,76 @@
 	CALL	notok(SB)
 	RET
 
-TEXT bsdthread_create(SB),7,$0
+// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void))
+// System call args are: func arg stack pthread flags.
+TEXT bsdthread_create(SB),7,$32
 	MOVL	$360, AX
+	// 0(SP) is where the caller PC would be; kernel skips it
+	MOVL	func+12(FP), BX
+	MOVL	BX, 4(SP)	// func
+	MOVL	m+4(FP), BX
+	MOVL	BX, 8(SP)	// arg
+	MOVL	stk+0(FP), BX
+	MOVL	BX, 12(SP)	// stack
+	MOVL	g+8(FP), BX
+	MOVL	BX, 16(SP)	// pthread
+	MOVL	$0x1000000, 20(SP)	// flags = PTHREAD_START_CUSTOM
 	INT	$0x80
 	JAE	2(PC)
 	CALL	notok(SB)
 	RET
 
+// The thread that bsdthread_create creates starts executing here,
+// because we registered this function using bsdthread_register
+// at startup.
+//	AX = "pthread" (= g)
+//	BX = mach thread port
+//	CX = "func" (= fn)
+//	DX = "arg" (= m)
+//	DI = stack top
+//	SI = flags (= 0x1000000)
+//	SP = stack - C_32_STK_ALIGN
 TEXT bsdthread_start(SB),7,$0
-	CALL	notok(SB)
+	// set up ldt 7+id to point at m->tls.
+	// m->tls is at m+40.  newosproc left
+	// the m->id in tls[0].
+	LEAL	40(DX), BP
+	MOVL	0(BP), DI
+	ADDL	$7, DI	// m0 is LDT#7. count up.
+	// setldt(tls#, &tls, sizeof tls)
+	PUSHAL	// save registers
+	PUSHL	$32	// sizeof tls
+	PUSHL	BP	// &tls
+	PUSHL	DI	// tls #
+	CALL	setldt(SB)
+	POPL	AX
+	POPL	AX
+	POPL	AX
+	POPAL
+	SHLL	$3, DI	// segment# is ldt*8 + 7.
+	ADDL	$7, DI
+	MOVW	DI, FS
+
+	// Now segment is established.  Initialize m, g.
+	MOVL	AX, 0(FS)	// g
+	MOVL	DX, 4(FS)	// m
+	MOVL	BX, 20(DX)	// m->procid = thread port (for debuggers)
+	CALL	CX	// fn()
+	CALL	exit1(SB)
 	RET
 
+// void bsdthread_register(void)
+// registers callbacks for threadstart (see bsdthread_create above
+// and wqthread and pthsize (not used).  returns 0 on success.
 TEXT bsdthread_register(SB),7,$40
 	MOVL	$366, AX
-	MOVL	$bsdthread_start(SB), 0(SP)	// threadstart
-	MOVL	$0, 4(SP)	// wqthread, not used by us
-	MOVL	$0, 8(SP)	// pthsize, not used by us
-	MOVL	$0, 12(SP)	// paranoia
-	MOVL	$0, 16(SP)
+	// 0(SP) is where kernel expects caller PC; ignored
+	MOVL	$bsdthread_start(SB), 4(SP)	// threadstart
+	MOVL	$0, 8(SP)	// wqthread, not used by us
+	MOVL	$0, 12(SP)	// pthsize, not used by us
+	MOVL	$0, 16(SP)	// paranoia
 	MOVL	$0, 20(SP)
+	MOVL	$0, 24(SP)
 	INT	$0x80
 	JAE	2(PC)
 	CALL	notok(SB)
diff --git a/src/runtime/darwin/amd64/sys.s b/src/runtime/darwin/amd64/sys.s
index 4238cd1..b46c823 100644
--- a/src/runtime/darwin/amd64/sys.s
+++ b/src/runtime/darwin/amd64/sys.s
@@ -157,6 +157,7 @@
 	MOVQ	m+16(SP), SI	// "arg"
 	MOVQ	stk+8(SP), DX	// stack
 	MOVQ	g+24(SP), R10	// "pthread"
+// TODO(rsc): why do we get away with 0 flags here but not on 386?
 	MOVQ	$0, R8	// flags
 	MOVQ	$(0x2000000+360), AX	// bsdthread_create
 	SYSCALL
diff --git a/src/runtime/darwin/thread.c b/src/runtime/darwin/thread.c
index c780e16..7926708 100644
--- a/src/runtime/darwin/thread.c
+++ b/src/runtime/darwin/thread.c
@@ -144,13 +144,15 @@
 void
 osinit(void)
 {
-	// Register our thread-creation callback (see sys_amd64_darwin.s).
+	// Register our thread-creation callback (see {amd64,386}/sys.s).
 	bsdthread_register();
 }
 
 void
 newosproc(M *m, G *g, void *stk, void (*fn)(void))
 {
+	// printf("newosproc m=%p g=%p stk=%p fn=%p\n", m, g, stk, fn);
+	m->tls[0] = m->id;	// so 386 asm can find it
 	bsdthread_create(stk, m, g, fn);
 }
 
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 8b92c44..8c20c1d 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -168,7 +168,8 @@
 	uint64	procid;		// for debuggers - must not move
 	G*	gsignal;	// signal-handling G - must not move
 	G*	curg;		// current running goroutine - must not move
-	G*	lastg;		// last running goroutine - to emulate fifo
+	G*	lastg;		// last running goroutine - to emulate fifo - must not move
+	uint32	tls[8];		// thread-local storage (for 386 extern register) - must not move
 	Gobuf	sched;
 	Gobuf	morestack;
 	byte*	moresp;
diff --git a/test/env.go b/test/env.go
index 88cec45..db76ee4 100644
--- a/test/env.go
+++ b/test/env.go
@@ -14,7 +14,7 @@
 		print("$GOARCH: ", e0.String(), "\n");
 		os.Exit(1);
 	}
-	if ga != "amd64" {
+	if ga != "amd64" && ga != "386" && ga != "arm" {
 		print("$GOARCH=", ga, "\n");
 		os.Exit(1);
 	}