diff --git a/cMIPS/include/handlers.s b/cMIPS/include/handlers.s
index ebb702a1cd4f10fb413507147a97da07a11324ad..f5f67a6b33f98b80c7341da4e4db0cbe8a89e194 100644
--- a/cMIPS/include/handlers.s
+++ b/cMIPS/include/handlers.s
@@ -1,18 +1,18 @@
 	# interrupt handlers
 	.include "cMIPS.s"
 	.text
-	.set noreorder
+	.set noreorder	# do not reorder instructions
+	.set noat	# do not use register $1 as $at
         .align 2
 
 	.set M_StatusIEn,0x0000ff11     # STATUS.intEn=1, user mode
-	
-	#----------------------------------------------------------------
+
+	#================================================================
 	# interrupt handler for external counter attached to IP5=HW3
 	# for extCounter address see vhdl/packageMemory.vhd
 
 	.bss
 	.align  2
-	.set noreorder
 	.global _counter_val             # accumulate number of interrupts
 	.comm   _counter_val 4
 	.comm   _counter_saves 8*4       # area to save up to 8 registers
@@ -67,12 +67,11 @@ extCounter:
 	#----------------------------------------------------------------
 
 	
-	#----------------------------------------------------------------
+	#================================================================
 	# interrupt handler for UART attached to IP6=HW4
 
 	.bss 
         .align  2
-	.set noreorder
 	.global Ud
 Ud:	.comm   rx_queue 16       # reception queue and pointers
 	.comm   rx_hd 4
@@ -138,15 +137,16 @@ UARTret:
 	#----------------------------------------------------------------
 
 	
-	#----------------------------------------------------------------
+	#================================================================
 	# handler for COUNT-COMPARE registers -- IP7=HW5
 	.text
 	.set    noreorder
+	.equ	num_cycles, 64
 	.global countCompare
 	.ent    countCompare
 countCompare:	
 	mfc0  $k1,c0_count    	# read COMPARE and clear IRQ
-	addiu $k1,$k1,64	# set next interrupt in 64 ticks
+	addiu $k1,$k1,num_cycles # set next interrupt in so many ticks
 	mtc0  $k1,c0_compare  
 
 	mfc0 $k0, c0_status	# Read STATUS register
@@ -160,7 +160,7 @@ countCompare:
 	#----------------------------------------------------------------
 
 	
-	#----------------------------------------------------------------
+	#================================================================
 	# functions to enable and disable interrupts, both return STATUS
 	.text
 	.set    noreorder
@@ -188,7 +188,234 @@ disableInterr:
 	#----------------------------------------------------------------
 
 
-	#----------------------------------------------------------------	
+	#================================================================
+	## TLB handlers
+	## page table entry is { EntryLo0 int0 EntryLo1 int1 }
+	## int{0,1} is
+	## { fill_31..6, Modified_5, Used_4, Writable_3, eXecutable_2,
+	##    Status_10 },
+	## Status is 00=unmapped, 01=mapped, 10=secondary_storage
+	#================================================================
+	
+
+	#================================================================
+	# handle TLB Modified exception (store to page with bit dirty=0)
+	#
+	.global _excp_saves
+	.global _excp_0180ret
+	.global handle_Mod
+	.set noreorder
+	
+	.ent handle_Mod
+handle_Mod:			# EntryHi points to offending TLB entry
+	tlbp			# what is the offender's index?
+	lui  $k1, %hi(_excp_saves)
+        ori  $k1, $k1, %lo(_excp_saves)
+	sw   $a0,  9*4($k1)	# save registers
+	sw   $a1, 10*4($k1)
+	sw   $a2, 11*4($k1)
+
+	mfc0 $a0, c0_badvaddr
+	andi $a0, $a0, 0x1000	# check if even or odd page
+	beq  $a0, $zero, M_even
+	mfc0 $a0, c0_context
+
+M_odd:	addi $a2, $a0, 12	# address for odd entry
+	mfc0 $k0, c0_entrylo1
+	ori  $k0, $k0, 0x0004	# mark TLB entry as dirty/writable
+	j    M_test
+	mtc0 $k0, c0_entrylo1
+	
+M_even: addi $a2, $a0, 4	# address for even entry
+	mfc0 $k0, c0_entrylo0
+	ori  $k0, $k0, 0x0004	# mark TLB entry as dirty/writable
+	mtc0 $k0, c0_entrylo0
+
+M_test:	lw   $a1, 0($a2)	# read PT[badVAddr]
+	mfc0 $k0, c0_badvaddr	# get faulting address
+	andi $a0, $a1, 0x0003	# check if page is mapped
+	beq  $a0, $zero, M_seg_fault	# no, abort simulation
+	nop
+
+	andi $a0, $a1, 0x0008	# check if page is writable
+	beq  $a0, $zero, M_prot_viol	# no, abort simulation
+	nop
+
+	andi $a0, $a1, 0x0002	# check if page is in secondary memory
+	bne  $a0, $zero, M_sec_mem
+	nop
+
+	mfc0 $a0, c0_epc	# check if fault is on an instruction
+	beq  $a0, $k0, M_prot_viol	# k0 is badVAddr
+	nop
+
+	ori  $a1, $a1, 0x0030	# mark PT entry as modified, used
+	sw   $a1, 0($a2)
+
+	tlbwi			# write entry with dirty bit=1 back to TLB
+	
+	lw   $a0,  9*4($k1)	# restore saved registers and return
+	lw   $a1, 10*4($k1)
+	j    _excp_0180ret
+	lw   $a2, 11*4($k1)
+
+M_seg_fault:	# print message and abort simulation
+	la   $k1, x_IO_BASE_ADDR
+	sw   $k0, 0($k1)
+	jal  cmips_kmsg
+	la   $k1, 3		# segmentation fault
+	nop
+	nop
+	nop
+	wait 0x41
+	
+M_prot_viol:	# print message and abort simulation
+	la   $k1, x_IO_BASE_ADDR
+	sw   $k0, 0($k1)
+	jal  cmips_kmsg
+	la   $k1, 2		# protection violation
+	nop
+	nop
+	nop
+	wait 0x41
+
+M_sec_mem:	# print message and abort simulation
+	la   $k1, x_IO_BASE_ADDR
+	sw   $k0, 0($k1)
+	jal  cmips_kmsg
+	la   $k1, 4		# secondary memory
+	nop
+	nop
+	nop
+	wait 0x41
+	
+	.end handle_Mod
+	#----------------------------------------------------------------
+
+
+	#================================================================
+	# handle TLB Load exception: double-fault caused by a TLB miss
+	#   to the Page Table -- mapping pointing to PT is not on TLB
+	#   so, fix the TLB by storing mapping onto TLB[4]
+	.global _excp_saves
+	.global _excp_0180ret
+	.global handle_TLBL
+	.global _PT
+        .set MIDDLE_RAM, (x_DATA_BASE_ADDR + (x_DATA_MEM_SZ/2))
+
+	.ent handle_TLBL
+handle_TLBL:			# EntryHi points to offending TLB entry
+	tlbp			# what is the offender's index?
+	lui  $k1, %hi(_excp_saves)
+        ori  $k1, $k1, %lo(_excp_saves)
+	sw   $a0,  9*4($k1)
+	sw   $a1, 10*4($k1)
+	sw   $a2, 11*4($k1)
+
+	mfc0 $a0, c0_badvaddr
+	la   $a1, (_PT + (x_INST_BASE_ADDR >>13)*16)
+
+	slt  $a2, $a0, $a1	# a2 <- badVAddr <= PageTable
+	bne  $a2, $zero, L_chks
+	nop
+
+	# this is same code as in start.s
+        # get physical page number for two pages at the bottom of PageTable
+        la    $a0, ( MIDDLE_RAM >>13 )<<13
+        mtc0  $a0, c0_entryhi           # tag for bottom double-page
+
+        la    $a0, ( (MIDDLE_RAM + 0*4096) >>12 )<<6
+        ori   $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
+        mtc0  $a1, c0_entrylo0          # bottom page (even)
+
+        la    $a0, ( (MIDDLE_RAM + 1*4096) >>12 )<<6
+        ori   $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
+        mtc0  $a1, c0_entrylo1          # bottom page + 1 (odd)
+
+        # and write it to TLB[4]
+        li    $k0, 4
+        mtc0  $k0, c0_index
+        tlbwi
+	j     L_ret		# all work done, return
+	nop
+
+L_chks: andi $a0, $a0, 0x1000	# check if even or odd page
+	beq  $a0, $zero, L_even
+	mfc0 $a0, c0_context
+
+L_odd:	j    L_test
+	addi $a2, $a0, 12	# address for odd entry
+	
+L_even: addi $a2, $a0, 4	# address for even entry
+
+L_test:	lw   $a1, 0($a2)
+	mfc0 $k0, c0_badvaddr	# get faulting address
+	andi $a0, $a1, 0x0003	# check if page is mapped
+	beq  $a0, $zero, M_seg_fault
+	nop
+
+	andi $a0, $a1, 0x0002	# check if page is in secondary memory
+	bne  $a0, $zero, M_sec_mem
+	nop
+
+	ori  $a1, $a1, 0x0010	# mark PT entry as used
+	sw   $a1, 0($a2)
+
+L_ret:	lw   $a0,  9*4($k1)	# nothing else to do, return??
+	lw   $a1, 10*4($k1)
+	j    _excp_0180ret
+	lw   $a2, 11*4($k1)
+
+	.end handle_TLBL
+	#----------------------------------------------------------------
+
+
+	#================================================================
+	# purge an entry from the TLB
+	# int TLB_purge(void *V_addr)
+	#   returns 0 if V_addr purged, 1 if V_addr not in TLB (probe failure)
+	#
+	.text
+	.global TLB_purge
+
+	.set noreorder
+	.ent TLB_purge
+TLB_purge:
+	srl  $a0, $a0, 13	# clear out in-page address bits
+	sll  $a0, $a0, 13	# 
+	mtc0 $a0, c0_entryhi
+	nop
+	tlbp			# probe the TLB
+	nop
+	mfc0 $a0, c0_index	# check for hit
+	srl  $a0, $a0, 31	# keeo only MSbit
+	bne  $a0, $zero, pu_miss # address not in TLB
+	move $v0, $a0		# V_addr not in TLB
+
+	tlbr			# read the entry
+	li   $a0, -8192		# -8192 = 0xffff.e000
+	mtc0 $a0, c0_entryhi	# and write an un-mapped address to tag
+
+	addi $v0, $zero, -3	# -3 = 0xffff.fffd to clear valid bit
+	mfc0 $a0, c0_entrylo0	# invalidate the mappings
+	and  $a0, $v0, $a0
+	mtc0 $a0, c0_entrylo0
+
+	mfc0 $a0, c0_entrylo1
+	and  $a0, $v0, $a0
+	mtc0 $a0, c0_entrylo1
+	move $v0, $zero		# V_addr was purged from TLB
+
+	tlbwi			# write invalid mappings to TLB
+	
+pu_miss: jr  $ra
+	nop
+	.end TLB_purge
+	##---------------------------------------------------------------
+
+
+	
+	#================================================================	
 	# delays processing by approx 4*$a0 processor cycles
 	.text
 	.set    noreorder
@@ -205,7 +432,7 @@ cmips_delay:
 	#----------------------------------------------------------------
 
 
-	#----------------------------------------------------------------	
+	#================================================================	
 	# print a message from within "the kernel"
 	#   void cmips_kmsg( $k1 )
 	# this function preserves registers other than k0,k1
@@ -219,10 +446,10 @@ cmips_delay:
 	.align  2
 	.set    noreorder
 	.set    noat
-	.global cmips_kmsg
-	.ent    cmips_kmsg
 	.equ	stdout,(x_IO_BASE_ADDR + 1*x_IO_ADDR_RANGE);
 
+	.global cmips_kmsg
+	.ent    cmips_kmsg
 cmips_kmsg:
 	lui   $k0, %hi(_kmsg_saves)
 	ori   $k0, $k0, %lo(_kmsg_saves)
@@ -257,13 +484,18 @@ k_for:	lbu   $a0, 0($a1)
 
 	.data
         .align  2
-_kmsg_interr:	.asciiz "\n\tinterrupt\n\n"
-_kmsg_excep:	.asciiz "\n\texception\n\n"
+_kmsg_interr:	.asciiz "\n\t00 - interrupt\n\n"
+_kmsg_excep:	.asciiz "\n\t01 - exception\n\n"
+_kmsg_prot_viol:	.asciiz "\n\t02 - protection violation\n\n"
+_kmsg_seg_fault: 	.asciiz "\n\t03 - segmentation fault\n\n"
+_kmsg_sec_mem: 		.asciiz "\n\t04 - in secondary memory\n\n"
 
 	.global _kmsg_list
 	.data
-        .align  2	
-_kmsg_list: .word _kmsg_interr,_kmsg_excep
+        .align  2
+_kmsg_list:
+	.word _kmsg_interr,_kmsg_excep, _kmsg_prot_viol, _kmsg_seg_fault
+	.word _kmsg_sec_mem
 
 	#----------------------------------------------------------------
 
diff --git a/cMIPS/include/start.s b/cMIPS/include/start.s
index 9652e4e38efce0ce15d93c5068ff19bb0cf4117c..1d96eb4fa880ad140d71275ba5dd076f98d1d7fd 100644
--- a/cMIPS/include/start.s
+++ b/cMIPS/include/start.s
@@ -155,12 +155,13 @@ _excp_0000:
 	.set noat
 
 	mfc0 $k1, c0_context
-	lw   $k0, 0($k1)           # k0 <- TP[context.lo]
-	lw   $k1, 8($k1)           # k1 <- TP[context.hi]
-	mtc0 $k0, c0_entrylo0      # EntryLo0 <- k0 = even element
-	mtc0 $k1, c0_entrylo1      # EntryLo1 <- k1 = odd element
+	lw   $k0, 0($k1)      	# k0 <- TP[context.lo]
+	lw   $k1, 8($k1)        # k1 <- TP[context.hi]
+	mtc0 $k0, c0_entrylo0   # EntryLo0 <- k0 = even element
+	mtc0 $k1, c0_entrylo1   # EntryLo1 <- k1 = odd element
 	ehb
-	tlbwr	                   # update TLB
+	tlbwr	                # update TLB
+	mfc0 $zero, c0_cause	# clear excCode in Cause
 	eret	
 	.end _excp_0000
 
@@ -201,6 +202,9 @@ _excp_0100:
         .text
         .set noreorder
 	.set noat
+	.global _excp_saves, _excp_0180ret
+	.global handle_Mod, handle_TLBL, handle_TLBS
+	
 	.org x_EXCEPTION_0180,0  # exception vector_180
 	.ent _excp_0180
 _excp_0180:
@@ -223,10 +227,10 @@ excp_tbl: # see Table 8-25, pg 95,96
 	wait 0x02  # interrupt, should never get here, abort simulation
 	nop
 
-	j h_Mod  # 1
+	j handle_Mod  # 1
 	nop
 
-	j h_TLBL # 2
+	j handle_TLBL # 2
 	nop
 
 	j h_TLBS # 3
@@ -266,8 +270,6 @@ excp_tbl: # see Table 8-25, pg 95,96
 	nop
 
 
-h_Mod:
-h_TLBL:
 h_TLBS:	
 h_syscall:
 h_breakpoint:
@@ -275,7 +277,7 @@ h_RI:
 h_CpU:
 h_Ov:
 
-excp_0180ret:
+_excp_0180ret:
 	lui  $k1, %hi(_excp_saves) # Read previous contents of STATUS
 	ori  $k1, $k1, %lo(_excp_saves)
 	lw   $k0, 1*4($k1)
@@ -390,75 +392,103 @@ _excp_BFC0:
 	##---------------------------------------------------------------
 	
 
-	##
+	#================================================================
+	# modify the page table:
+	# void PT_update(void *V_addr, int component, int new_value)
+	#   component is in {0=entrylo0, 1-int0, 2=entrylo1, 3=int1}
+	.text
+	.global PT_update
+
+	.set noreorder
+	.ent PT_update
+PT_update:
+	srl  $a0, $a0, 9	# (_PT + (V_addr >>13)*16)
+	la   $v0, PTbase
+	add  $a0, $v0, $a0
+	andi $a1, $a1, 0x0003	# make sure component is in range
+	sll  $a1, $a1, 2	# component * 4
+	add  $a0, $a0, $a1	# (_PT + (V_addr >>13)*16).component
+	jr   $ra
+	sw   $a2, 0($a0)	# write to PT[V_addr].component
+	.end PT_update
+	##---------------------------------------------------------------
+
+
+	
+
+	
 	##===============================================================
 	## Page Table
 	##
 	## See EntryLo, pg 63
 	##
+	## intLo0 and intLo1 are:
+	## nil_31..6 Modified_5 Used_4  Writable_3  eXecutable_2 Status_1,0
+	## Status: 00=unmapped, 01=mapped, 10=in_secondary_storage, 11=undef
+	##
 	.section .PT,"aw",@progbits
-
+	.global _PT
+	
 	## ( ( (x_INST_BASE_ADDR + n*4096) >>12 )<<6 ) || 0b000011  d,v,g
-
 _PT:
 	##
-	## ROM mappings
+	## ROM mappings, intLo{01} = U=M=W=0, X=1, S=10 (or 00) = 6 (or 4)
 	##
 	.org (_PT + (x_INST_BASE_ADDR >>13)*16)
 
 	# PT[0], ROM
-	.word  ( (x_INST_BASE_ADDR +  0*4096) >>6) | 0b000011  
-	.word 0
+PTbase:	.word  ( (x_INST_BASE_ADDR +  0*4096) >>6) | 0b000011  
+	.word 0x00000006
 	.word  ( (x_INST_BASE_ADDR +  1*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000006
 
 	# PT[1]
 	.word  ( (x_INST_BASE_ADDR +  2*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000006
 	.word  ( (x_INST_BASE_ADDR +  3*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000006
 	
-	# PT[2]
+	# PT[2] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR +  4*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR +  5*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000004
 
-	# PT[3]
+	# PT[3] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR +  6*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR +  7*4096) >>6) | 0b000011  
-	.word 0
+	.word 0x00000004
 	
 	# PT[4] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR +  8*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR +  9*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 
 	# PT[5] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR + 10*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR + 11*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 	
 	# PT[6] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR + 12*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR + 13*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 
 	# PT[7] -- not mapped for simulation
 	.word  ( (x_INST_BASE_ADDR + 14*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 	.word  ( (x_INST_BASE_ADDR + 15*4096) >>6) | 0b000001
-	.word 0
+	.word 0x00000004
 
-	## all remaining ROM entries are invalid
+	## all remaining ROM entries are invalid and unmapped
 	
 	
 	##
-	## RAM mappings
+	## RAM mappings, intLo{01} = U=M=0, W=1, X=0, S=10 (or 00) = a (or 8)
 	##
 	.org (_PT + (x_DATA_BASE_ADDR >>13)*16)
 
@@ -466,54 +496,54 @@ _PT:
 	
 	# PT[ram+0], RAM
 	.word  ( (x_DATA_BASE_ADDR +  0*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR +  1*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 
 	# PT[ram+1]
 	.word  ( (x_DATA_BASE_ADDR +  2*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR +  3*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	
 	# PT[ram+2]
 	.word  ( (x_DATA_BASE_ADDR +  4*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR +  5*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 
 	# PT[ram+3]
 	.word  ( (x_DATA_BASE_ADDR +  6*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR +  7*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	
 	# PT[ram+4]
 	.word  ( (x_DATA_BASE_ADDR +  8*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR +  9*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 
 	# PT[ram+5]
 	.word  ( (x_DATA_BASE_ADDR + 10*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR + 11*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	
 	# PT[ram+6]
 	.word  ( (x_DATA_BASE_ADDR + 12*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR + 13*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 
 	# PT[ram+7]
 	.word  ( (x_DATA_BASE_ADDR + 14*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 	.word  ( (x_DATA_BASE_ADDR + 15*4096) >>6) | 0b000111  
-	.word 0
+	.word 0x0000000a
 
 	
-	## all remaining RAM entries are invalid
+	## all remaining RAM entries are invalid and unmapped
 	
 _endPT:
 
diff --git a/cMIPS/tests/pt_walk.c b/cMIPS/tests/pt_walk.c
index 3a24e13fc811888f127dbfa515703dba0c222fe6..69e583052220e3e27371b67bad53ce67208aaa0f 100644
--- a/cMIPS/tests/pt_walk.c
+++ b/cMIPS/tests/pt_walk.c
@@ -15,8 +15,24 @@
 // With this much memory, simulations run slowly.  No free lunch.
 
 
+//-----------------------------------------------------------------------
+// decide on the tests to run
+#define WALK_THE_PT  0
+#define TLB_MODIFIED 1
+#define DOUBLE_FAULT 0
+
+// these will abort the simulation when the fault is detected/handled
+#define PROT_VIOL 0
+#define SEG_FAULT 0
+//-----------------------------------------------------------------------
+
+
 #include "cMIPS.h"
 
+extern void PT_update(void *V_addr, int component, int new_value);
+extern  int TLB_purge(void *V_addr);
+static void print_str(char *);
+
 #define FALSE (0==1)
 #define TRUE  !FALSE
 
@@ -25,11 +41,19 @@
 #define NUM_RAM_PAGES  ( (x_DATA_MEM_SZ / 2) / 4096 )
 
 
+
+
 void main(void) {
-  int i;
+  int i, rsp, new_value;
   int *walker;
 
-  // write to the middle of all pages
+
+#if WALK_THE_PT
+  //-----------------------------------------------------------------
+  // write to the middle of all datapages
+  //   this will cause some TLB refill exceptions, which should be
+  //   handled smoothly by the handler at excp_0000 (include/start/s)
+  //-----------------------------------------------------------------
   walker = (int *)(x_DATA_BASE_ADDR + 1024);
 
   for( i = 0 ; i < NUM_RAM_PAGES; i++){
@@ -44,11 +68,180 @@ void main(void) {
     print( *walker );
     walker = (int *)((int)walker + 4096);
   }
-	
-  to_stdout('\n');
-  to_stdout('o');
-  to_stdout('k');
-  to_stdout('\n');
 
+  print_str("\twalked\n");
+#endif
+
+
+
+#define PG_NUM 10
+
+
+
+#if TLB_MODIFIED
+  //-------------------------------------------------------------------
+  // let's change a mapping to cause a TLB-Modified exception
+  //
+  // in fact, there will be TWO exceptions: 
+  // (1) a TLB-Refill will copu the write-protected mapping from the PT
+  //     and retry the instruction;
+  // (2) when the sw is retried, it will cause a TLB-Modified
+  //     exception, which checks PT's protection bits, then fixes the
+  //     dirty bit in the TLB, and retries again, succeeding this time.
+  //-------------------------------------------------------------------
+  // ( ( (x_DATA_BASE_ADDR + n*4096) >>12 )<<6 ) || 0b000111  d,v,g
+
+  walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096);
+
+  // first, remove V_addr from the TLB, to ensure the PT is searched
+  if ( TLB_purge((void *)walker) == 0 ) {
+    print_str("\n\tTLB entry purged\n");
+  } else {
+    print_str("\n\tTLB miss\n");
+  }
+
+  new_value = ( ((x_DATA_BASE_ADDR + PG_NUM*4096)>>12) <<6) | 0b000011; // d=0
+  PT_update( (int *)walker, 0, new_value);
+
+  new_value = 0x00000009;                    // writable, mapped
+  PT_update( (int *)walker, 1, new_value);
+
+  *walker = 0x99;              // cause a TLBrefill, then a TLBmod
+
+  if ( *walker == 0x99 ) {     // this load is optimized away by gcc
+    print( *walker );
+    print_str("\tMod ok\n");
+  } else {
+    print_str("\tMod err\n");
+ }
+#endif
+
+
+
+
+#if DOUBLE_FAULT
+  //--------------------------------------------------------------------
+  // let's remove a PT's mapping from the TLB and cause a double-fault:
+  //
+  // (1) on the 1st reference, TLB-refill does not find a mapping for
+  //     the PT on the TLB; this causes a TLBL (load) exception;
+  // (2) routine handle_TLBL writes a new mapping on the PT, refills
+  //     the TLB, then retries the reference, which succeeds this time.
+  //--------------------------------------------------------------------
+
+  // this is the base of the page table
+  walker = (int *)(x_DATA_BASE_ADDR + (x_DATA_MEM_SZ/2));
+
+  // remove the TLB entry which points to the PT
+  if ( TLB_purge((void *)walker) == 0 ) {
+    print_str("\n\tPT purged from TLB\n");
+  } else {
+    print_str("\n\twtf?\n");
+  }
+
+  // now reference a mapped page, to cause the double fault
+  walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096 + 1024);
+
+  *walker = 0x77;              // cause a TLBrefill then a TLBload
+
+  if ( *walker == 0x77 ) {     // this load is optimized away by gcc
+    print( *walker );
+    print_str("\tdouble ok\n");
+  } else {
+    print_str("\tdouble err\n");
+ }
+#endif
+
+
+
+
+#if PROT_VIOL
+  //----------------------------------------------------------------------
+  // let's cause a protection violation -- write to a write-protected page
+  //   this will abort the simulation
+  //----------------------------------------------------------------------
+
+  walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096);
+
+  // first, remove V_addr from the TLB, to ensure the PT is searched
+  if ( TLB_purge((void *)walker) == 0 ) {
+    print_str("\n\tpurged\n");
+  } else {
+    print_str("\t\tTLB miss\n");
+  }
+
+  // change a PT element so it is data, NON-writable, page is mapped in PT
+  new_value = ( ((x_DATA_BASE_ADDR + PG_NUM*4096)>>12) <<6) | 0b000011; // d=0
+  PT_update( (int *)walker, 0, new_value);
+  new_value = 0x00000001;                          // NOT-writable, mapped
+  PT_update( (int *)walker, 1, new_value);
+
+  *walker = 0x88;
+
+  // will never get here -- protection violation on the store
+  if ( *walker == 0x88 ) {    // this load is optimized away by gcc
+    print( *walker );
+    print_str("\tprot viol not ok\n");
+  } else {
+    print_str("\tprot viol err\n");
+ }
+#endif
+
+
+
+
+#if SEG_FAULT
+  //-----------------------------------------------------------------
+  // let's cause a segmentation fault -- reference to page not mapped
+  //   this will abort the simulation
+  //-----------------------------------------------------------------
+
+#define PG_UNMAPPED 20
+
+  // pick a page that is not mapped
+  walker = (int *)(x_DATA_BASE_ADDR + PG_UNMAPPED*4096); // page not mapped
+
+  // first, remove V_addr from the TLB, to ensure the PT will be searched
+  if ( TLB_purge((void *)walker) == 0 ) {
+    print_str("\n\tpurged\n");
+  } else {
+    print_str("\n\tTLB miss\n");
+  }
+
+  // add a new PT element for an address range with RAM but UN-mapped
+  //   this address is above the page table
+  new_value =
+    (((x_DATA_BASE_ADDR + PG_UNMAPPED*4096)>>12) <<6) | 0b000011; // d=0
+  PT_update( (int *)walker, 0, new_value);
+  PT_update( (int *)walker, 1, 0);              // mark as unmapped
+  new_value =
+    (((x_DATA_BASE_ADDR + (PG_UNMAPPED+1)*4096)>>12) <<6) | 0b000011; // d=0
+  PT_update( (int *)walker, 2, new_value);
+  PT_update( (int *)walker, 3, 0);              // mark as unmapped
+
+  *walker = 0x44;
+
+  // will never get here -- seg fault on the store
+  if ( *walker == 0x44 ) {    // this load is optimized away by gcc
+    print( *walker );
+    print_str("\tseg fault not ok\n");
+  } else {
+    print_str("\tseg fault err\n");
+ }
+#endif
+
+
+
+
+  to_stdout('\n');
 }
 
+
+void print_str(char *s) {
+  int i;
+  i = 0;
+  while (s[i] != '\0') {
+    to_stdout(s[i]);
+    i = i + 1;
+  }
+}