diff --git a/Makefile b/Makefile
index 1084c20..b7828dd 100644
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,7 @@ PRINT =	\
 	string.c\
 
 print: $(PRINT)
-//	~/src/lgrind/source/lgrind -d ~/src/lgrind/lgrindef $(PRINT) > xv6.tex
+	//~/src/lgrind/source/lgrind -d ~/src/lgrind/lgrindef $(PRINT) > xv6.tex
 	lgrind $(PRINT) > xv6.tex
 	latex xv6.tex
 	dvips -o xv61.ps xv6.dvi
diff --git a/asm.h b/asm.h
index 5986ec7..e02f050 100644
--- a/asm.h
+++ b/asm.h
@@ -1,3 +1,8 @@
+//
+// macros to create x86 segments from assembler
+// from JOS
+//
+
 #define SEG_NULL						\
 	.word 0, 0;						\
 	.byte 0, 0, 0, 0
diff --git a/bootasm.S b/bootasm.S
index c2a3c3e..dda415d 100644
--- a/bootasm.S
+++ b/bootasm.S
@@ -1,3 +1,7 @@
+#
+# from JOS
+#
+        
 #include "asm.h"
 	
 .set PROT_MODE_CSEG,0x8		# code segment selector
diff --git a/elf.h b/elf.h
index 8eb4074..e53051d 100644
--- a/elf.h
+++ b/elf.h
@@ -1,3 +1,8 @@
+//
+// format of an ELF executable file
+// from JOS
+//
+
 #define ELF_MAGIC 0x464C457FU	/* "\x7FELF" in little endian */
 
 struct elfhdr {
diff --git a/init.c b/init.c
index 5e387a8..359f1fb 100644
--- a/init.c
+++ b/init.c
@@ -13,20 +13,25 @@ main(void)
 {
   int pid;
   
-  if(open("console", 0) < 0){
+  if(open("console", O_RDWR) < 0){
     mknod("console", T_DEV, 1, 1);
-    open("console", 0);
+    open("console", O_RDWR);
   }
-  open("console", 1);
-  open("console", 1);
+  dup(0);
+  dup(0);
 
   while(1){
     pid = fork();
-    if(pid == 0){
-      exec("sh", sh_args);
+    if(pid < 0){
+      puts("init: fork failed\n");
       exit();
     }
-    if(pid > 0)
+    if(pid == 0){
+      exec("sh", sh_args);
+      puts("init: exec sh failed\n");
+      exit();
+    } else {
       wait();
+    }
   }
 }
diff --git a/lapic.c b/lapic.c
index 0d389f1..3b7c629 100644
--- a/lapic.c
+++ b/lapic.c
@@ -110,7 +110,6 @@ lapic_write(int r, int data)
 void
 lapic_timerinit(void)
 {
-  cprintf("cpu%d: init timer\n", cpu());
   lapic_write(LAPIC_TDCR, LAPIC_X1);
   lapic_write(LAPIC_TIMER, LAPIC_CLKIN | LAPIC_PERIODIC | (IRQ_OFFSET + IRQ_TIMER));
   lapic_write(LAPIC_TCCR, 10000000);
@@ -129,8 +128,6 @@ lapic_init(int c)
 {
   uint r, lvt;
 
-  cprintf("cpu%d: lapic_init %d\n", c);
-
   lapic_write(LAPIC_DFR, 0xFFFFFFFF); // set destination format register
   r = (lapic_read(LAPIC_ID)>>24) & 0xFF; // read APIC ID
   lapic_write(LAPIC_LDR, (1<<r)<<24);  // set logical destination register to r
@@ -157,8 +154,6 @@ lapic_init(int c)
   lapic_write(LAPIC_ICRLO, LAPIC_ALLINC|APIC_LEVEL|LAPIC_DEASSERT|APIC_INIT);
   while(lapic_read(LAPIC_ICRLO) & APIC_DELIVS)
     ;
-
-  cprintf("cpu%d: apic init done\n", cpu());
 }
 
 void
@@ -204,7 +199,7 @@ lapic_startap(uchar apicid, int v)
   // in p9 code, this was i < 2, which is what the spec says on page B-3
   for(i = 0; i < 1; i++){
     lapic_write(LAPIC_ICRHI, crhi);
-    lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_EDGE|APIC_STARTUP|(v/PGSIZE));
+    lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_EDGE|APIC_STARTUP|(v/4096));
     while (j++ < 100000) {;}
   }
 }
diff --git a/main.c b/main.c
index 5470e6a..c2b9b3f 100644
--- a/main.c
+++ b/main.c
@@ -13,7 +13,7 @@
 extern char edata[], end[];
 extern uchar _binary_init_start[], _binary_init_size[];
 
-void main00();
+void process0();
 
 // CPU 0 starts running C code here.
 // This is called main0 not main so that it can have
@@ -32,43 +32,33 @@ main0(void)
   asm volatile("movl %0, %%esp" : : "r" (cpus[0].mpstack + MPSTACK - 32));
   asm volatile("movl %0, %%ebp" : : "r" (cpus[0].mpstack + MPSTACK));
 
-  // Make sure interrupts stay disabled on all processors
-  // until each signals it is ready, by pretending to hold
-  // an extra lock.
-  // xxx maybe replace w/ acquire remembering if FL_IF was already clear
-  for(i=0; i<NCPU; i++){
-    cpus[i].nlock++;
-    cpus[i].guard1 = 0xdeadbeef;
-    cpus[i].guard2 = 0xdeadbeef;
-  }
+  // Prevent release() from enabling interrupts.
+  for(i=0; i<NCPU; i++)
+    cpus[i].nlock = 1;
 
   mp_init(); // collect info about this machine
 
   lapic_init(mp_bcpu());
 
-  cprintf("\n\ncpu%d: booting xv6\n\n", cpu());
+  cprintf("\ncpu%d: starting xv6\n\n", cpu());
 
-  pinit();
-  binit();
-  pic_init(); // initialize PIC
+  pinit(); // process table
+  binit(); // buffer cache
+  pic_init();
   ioapic_init();
   kinit(); // physical memory allocator
   tvinit(); // trap vectors
-  idtinit(); // this CPU's idt register
+  idtinit(); // this CPU's interrupt descriptor table
   fd_init();
-  iinit();
+  iinit(); // i-node table
 
   // initialize process 0
   p = &proc[0];
   p->state = RUNNABLE;
-  p->sz = 4 * PAGE;
-  p->mem = kalloc(p->sz);
-  memset(p->mem, 0, p->sz);
   p->kstack = kalloc(KSTACKSIZE);
 
-  // cause proc[0] to start in kernel at main00
-  memset(&p->jmpbuf, 0, sizeof p->jmpbuf);
-  p->jmpbuf.eip = (uint)main00;
+  // cause proc[0] to start in kernel at process0
+  p->jmpbuf.eip = (uint) process0;
   p->jmpbuf.esp = (uint) (p->kstack + KSTACKSIZE - 4);
 
   // make sure there's a TSS
@@ -78,6 +68,7 @@ main0(void)
   console_init();
   ide_init(); 
 
+  // start other CPUs
   mp_startthem();
 
   // turn on timer and enable interrupts on the local APIC
@@ -118,7 +109,7 @@ mpmain(void)
 
 // proc[0] starts here, called by scheduler() in the ordinary way.
 void
-main00()
+process0()
 {
   struct proc *p0 = &proc[0];
   struct proc *p1;
@@ -130,10 +121,13 @@ main00()
   p0->cwd = iget(rootdev, 1);
   iunlock(p0->cwd);
 
+  // dummy user memory to make copyproc() happy
+  p0->sz = 4 * PAGE;
+  p0->mem = kalloc(p0->sz);
+
   // fake a trap frame as if a user process had made a system
   // call, so that copyproc will have a place for the new
   // process to return to.
-  p0 = &proc[0];
   p0->tf = &tf;
   memset(p0->tf, 0, sizeof(struct trapframe));
   p0->tf->es = p0->tf->ds = p0->tf->ss = (SEG_UDATA << 3) | 3;
@@ -141,7 +135,7 @@ main00()
   p0->tf->eflags = FL_IF;
   p0->tf->esp = p0->sz;
 
-  p1 = copyproc(&proc[0]);
+  p1 = copyproc(p0);
   
   load_icode(p1, _binary_init_start, (uint) _binary_init_size);
   p1->state = RUNNABLE;
@@ -157,7 +151,6 @@ load_icode(struct proc *p, uchar *binary, uint size)
   struct elfhdr *elf;
   struct proghdr *ph;
 
-  // Check magic number on binary
   elf = (struct elfhdr*) binary;
   if (elf->magic != ELF_MAGIC)
     panic("load_icode: not an ELF binary");
diff --git a/mmu.h b/mmu.h
index 0cd7944..8b30534 100644
--- a/mmu.h
+++ b/mmu.h
@@ -1,15 +1,8 @@
 /*
- * This file contains definitions for the x86 memory management unit (MMU),
- * including paging- and segmentation-related data structures and constants,
- * the %cr0, %cr4, and %eflags registers, and traps.
+ * This file contains definitions for the x86 memory management unit (MMU).
+ * from JOS.
  */
 
-/*
- *	Register flags and fundamental constants.
- */
-
-#define PGSIZE		4096		// bytes mapped by a page
-
 // Eflags register
 #define FL_CF		0x00000001	// Carry Flag
 #define FL_PF		0x00000004	// Parity Flag
@@ -33,33 +26,7 @@
 #define FL_VIP		0x00100000	// Virtual Interrupt Pending
 #define FL_ID		0x00200000	// ID flag
 
-// Page fault error codes
-#define FEC_PR		0x1	// Page fault caused by protection violation
-#define FEC_WR		0x2	// Page fault caused by a write
-#define FEC_U		0x4	// Page fault occured while in user mode
-
-/*
- *
- *	Segmentation data structures and constants.
- *
- */
-
-#ifdef __ASSEMBLER__
-
-/*
- * Macros to build GDT entries in assembly.
- */
-#define SEG_NULL						\
-	.word 0, 0;						\
-	.byte 0, 0, 0, 0
-#define SEG(type,base,lim)					\
-	.word (((lim) >> 12) & 0xffff), ((base) & 0xffff);	\
-	.byte (((base) >> 16) & 0xff), (0x90 | (type)),		\
-		(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
-
-#else	// not __ASSEMBLER__
-
-// Segment Descriptors
+// Segment Descriptor
 struct segdesc {
 	uint lim_15_0 : 16;  // Low bits of segment limit
 	uint base_15_0 : 16; // Low bits of segment base address
@@ -75,22 +42,21 @@ struct segdesc {
 	uint g : 1;          // Granularity: limit scaled by 4K when set
 	uint base_31_24 : 8; // High bits of segment base address
 };
+
 // Null segment
 #define SEG_NULL	(struct segdesc){ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-// Segment that is loadable but faults when used
-#define SEG_FAULT	(struct segdesc){ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }
+
 // Normal segment
 #define SEG(type, base, lim, dpl) (struct segdesc)			\
 { ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,	\
     type, 1, dpl, 1, (uint) (lim) >> 28, 0, 0, 1, 1,		\
     (uint) (base) >> 24 }
+
 #define SEG16(type, base, lim, dpl) (struct segdesc)			\
 { (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,		\
     type, 1, dpl, 1, (uint) (lim) >> 16, 0, 0, 1, 0,		\
     (uint) (base) >> 24 }
 
-#endif /* !__ASSEMBLER__ */
-
 // Application segment type bits
 #define STA_X		0x8	    // Executable segment
 #define STA_E		0x4	    // Expand down (non-executable segments)
@@ -113,16 +79,7 @@ struct segdesc {
 #define STS_IG32	0xE	    // 32-bit Interrupt Gate
 #define STS_TG32	0xF	    // 32-bit Trap Gate
 
-
-/*
- *
- *	Traps.
- *
- */
-
-#ifndef __ASSEMBLER__
-
-// Task state segment format (as described by the Pentium architecture book)
+// Task state segment format
 struct taskstate {
 	uint link;	// Old ts selector
 	uint esp0;	// Stack pointers and segment selectors
@@ -197,19 +154,3 @@ struct gatedesc {
 	(gate).off_31_16 = (uint) (off) >> 16;		\
 }
 
-// Set up a call gate descriptor.
-#define SETCALLGATE(gate, ss, off, d)           	        \
-{								\
-	(gate).off_15_0 = (uint) (off) & 0xffff;		\
-	(gate).ss = (ss);					\
-	(gate).args = 0;					\
-	(gate).rsv1 = 0;					\
-	(gate).type = STS_CG32;				\
-	(gate).s = 0;					\
-	(gate).dpl = (d);					\
-	(gate).p = 1;					\
-	(gate).off_31_16 = (uint) (off) >> 16;		\
-}
-
-#endif /* !__ASSEMBLER__ */
-
diff --git a/mp.c b/mp.c
index 341b545..5029f12 100644
--- a/mp.c
+++ b/mp.c
@@ -42,7 +42,6 @@ mp_scan(uchar *addr, int len)
   uchar *e, *p, sum;
   int i;
 
-  cprintf("scanning: 0x%x\n", (uint)addr);
   e = addr+len;
   for(p = addr; p < e; p += sizeof(struct mp)){
     if(memcmp(p, "_MP_", 4))
@@ -131,8 +130,6 @@ mp_init(void)
   ncpu = 0;
   if ((r = mp_detect()) != 0) return;
 
-  cprintf("Mp spec rev #: %x imcrp 0x%x\n", mp->specrev, mp->imcrp);
-
   /*
    * Run through the table saving information needed for starting
    * application processors and initialising any I/O APICs. The table
@@ -140,7 +137,6 @@ mp_init(void)
    */
   mpctb = (struct mpctb *) mp->physaddr;
   lapicaddr = (uint *) mpctb->lapicaddr;
-  cprintf("apicaddr: %x\n", lapicaddr);
   p = ((uchar*)mpctb)+sizeof(struct mpctb);
   e = ((uchar*)mpctb)+mpctb->length;
 
@@ -149,7 +145,6 @@ mp_init(void)
     case MPPROCESSOR:
       proc = (struct mppe *) p;
       cpus[ncpu].apicid = proc->apicid;
-      cprintf("a processor %x\n", cpus[ncpu].apicid);
       if (proc->flags & MPBP) {
 	bcpu = &cpus[ncpu];
       }
@@ -162,18 +157,15 @@ mp_init(void)
 	if(strncmp(buses[i], bus->string, sizeof(bus->string)) == 0)
 	  break;
       }
-      cprintf("a bus %d\n", i);
       p += sizeof(struct mpbe);
       continue;
     case MPIOAPIC:
       ioapic = (struct mpioapic *) p;
-      cprintf("an I/O APIC: id %d %x\n", ioapic->apicno, ioapic->flags);
       ioapic_id = ioapic->apicno;
       p += sizeof(struct mpioapic);
       continue;
     case MPIOINTR:
       intr = (struct mpie *) p;
-      // cprintf("an I/O intr: type %d flags 0x%x bus %d souce bus irq %d dest ioapic id %d dest ioapic intin %d\n", intr->intr, intr->flags, intr->busno, intr->irq, intr->apicno, intr->intin);
       p += sizeof(struct mpie);
       continue;
     default:
@@ -192,8 +184,6 @@ mp_init(void)
     byte |= 0x01;	/* mask external INTR */
     outb(0x23, byte);	/* disconnect 8259s/NMI */
   }
-
-  cprintf("ncpu: %d boot %d\n", ncpu, bcpu-cpus);
 }
 
 
@@ -219,7 +209,6 @@ mp_startthem(void)
 
   for(c = 0; c < ncpu; c++){
     if (c == cpu()) continue;
-    cprintf ("cpu%d: starting processor %d\n", cpu(), c);
     *(uint *)(APBOOTCODE-4) = (uint) (cpus[c].mpstack) + MPSTACK; // tell it what to use for %esp
     *(uint *)(APBOOTCODE-8) = (uint)mpmain; // tell it where to jump to
     lapic_startap(cpus[c].apicid, (uint) APBOOTCODE);
diff --git a/proc.c b/proc.c
index 1d0917f..01f6aa4 100644
--- a/proc.c
+++ b/proc.c
@@ -169,18 +169,11 @@ scheduler(void)
   struct proc *p;
   int i;
 
-  if(cpus[cpu()].nlock != 0){
-    cprintf("la %x lr %x\n", cpus[cpu()].lastacquire, cpus[cpu()].lastrelease   );
-    panic("holding locks at first entry to scheduler");
-  }
-
   for(;;){
     // Loop over process table looking for process to run.
     acquire(&proc_table_lock);
+
     for(i = 0; i < NPROC; i++){
-      if(cpus[cpu()].guard1 != 0xdeadbeef || 
-         cpus[cpu()].guard2 != 0xdeadbeef)
-        panic("cpu guard");
       p = &proc[i];
       if(p->state != RUNNABLE)
         continue;
@@ -198,31 +191,11 @@ scheduler(void)
       // Process is done running for now. 
       // It should have changed its p->state before coming back.
       curproc[cpu()] = 0;
-      if(p->state == RUNNING)
-        panic("swtch to scheduler with state=RUNNING");
       
-      if(!holding(&proc_table_lock)){
-        cprintf("back to scheduler without proc_table_lock (pid=%d state=%d)", p->pid, p->state);
-        panic("scheduler lock");
-      }
-      if(cpus[cpu()].nlock != 1){
-        cprintf("holding %d locks in scheduler (pid=%d state=%d)\n", cpus[cpu()].nlock, p->pid, p->state);
-        panic("scheduler lock");
-      }
-
       setupsegs(0);
     }
 
     release(&proc_table_lock);
-    
-    if(cpus[cpu()].nlock != 0)
-      panic("holding locks in scheduler");
-
-    // With proc_table_lock released, there are no 
-    // locks held on this cpu, so interrupts are enabled.
-    // Hardware interrupts can happen here.
-    // Also, releasing the lock here lets the other CPUs
-    // look for runnable processes too.
   }
 }
 
diff --git a/proc.h b/proc.h
index 64e979a..5689bbc 100644
--- a/proc.h
+++ b/proc.h
@@ -66,13 +66,9 @@ struct cpu {
   struct jmpbuf jmpbuf;
   struct taskstate ts;  // only to give cpu address of kernel stack
   struct segdesc gdt[NSEGS];
-  int guard1;
   char mpstack[MPSTACK]; // per-cpu start-up stack
-  int guard2;
   volatile int booted;
   int nlock; // # of locks currently held
-  struct spinlock *lastacquire; // xxx debug
-  struct spinlock *lastrelease; // xxx debug
 };
 
 extern struct cpu cpus[NCPU];
diff --git a/spinlock.c b/spinlock.c
index c77e444..7285748 100644
--- a/spinlock.c
+++ b/spinlock.c
@@ -43,7 +43,6 @@ acquire(struct spinlock * lock)
   cpuid(0, 0, 0, 0, 0);	// memory barrier
   getcallerpcs(&lock, lock->pcs);
   lock->cpu = cpu() + 10;
-  cpus[cpu()].lastacquire = lock;
 }
 
 void
@@ -53,7 +52,6 @@ release(struct spinlock * lock)
   if(!holding(lock))
     panic("release");
 
-  cpus[cpu()].lastrelease = lock;
   lock->pcs[0] = 0;
   lock->cpu = 0xffffffff;
   cpuid(0, 0, 0, 0, 0);	// memory barrier
diff --git a/syscall.c b/syscall.c
index eaf13c5..5a7a60d 100644
--- a/syscall.c
+++ b/syscall.c
@@ -473,7 +473,7 @@ sys_getpid(void)
 int
 sys_sbrk(void)
 {
-  unsigned addr;
+  uint addr;
   int n;
   struct proc *cp = curproc[cpu()];
 
diff --git a/trapasm.S b/trapasm.S
index 3e0f375..ca5b42a 100644
--- a/trapasm.S
+++ b/trapasm.S
@@ -1,5 +1,3 @@
-#include "mmu.h"
-        
 	.text
 .globl trap
 .globl trapret1
diff --git a/usertests.c b/usertests.c
index badaa1c..54cebbd 100644
--- a/usertests.c
+++ b/usertests.c
@@ -13,7 +13,10 @@ pipe1(void)
   int fds[2], pid;
   int seq = 0, i, n, cc, total;
 
-  pipe(fds);
+  if(pipe(fds) != 0){
+    puts("pipe() failed\n");
+    exit();
+  }
   pid = fork();
   if(pid == 0){
     close(fds[0]);
@@ -26,7 +29,7 @@ pipe1(void)
       }
     }
     exit();
-  } else {
+  } else if(pid > 0){
     close(fds[1]);
     total = 0;
     cc = 1;
@@ -43,9 +46,12 @@ pipe1(void)
         cc = sizeof(buf);
     }
     if(total != 5 * 1033)
-      printf(1, "pipe1 oops 3\n");
+      printf(1, "pipe1 oops 3 total %d\n", total);
     close(fds[0]);
     wait();
+  } else {
+    puts("fork() failed\n");
+    exit();
   }
   puts("pipe1 ok\n");
 }
@@ -121,26 +127,30 @@ void
 mem(void)
 {
   void *m1, *m2;
+  int pid;
 
-  m1 = 0;
-  while ((m2 = malloc(1024)) != 0) {
-    printf(1, "malloc %x\n", m2);
-    *(char **) m2 = m1;
-    m1 = m2;
-  }
-  while (m1) {
-    m2 = *(char **)m1;
+  if((pid = fork()) == 0){
+    m1 = 0;
+    while ((m2 = malloc(10001)) != 0) {
+      *(char **) m2 = m1;
+      m1 = m2;
+    }
+    while (m1) {
+      m2 = *(char **)m1;
+      free(m1);
+      m1 = m2;
+    }
+    m1 = malloc(1024*20);
+    if (m1 == 0) {
+      puts("couldn't allocate mem?!!\n");
+      exit();
+    }
     free(m1);
-    m1 = m2;
-  }
-  m1 = malloc(1024*20);
-  if (m1 == 0) {
-    puts("couldn't allocate mem?!!\n");
+    printf(1, "mem ok\n");
     exit();
+  } else {
+    wait();
   }
-  free(m1);
-
-  printf(1, "mem ok\n");
 }
 
 int