changeset 80:4438054a183c

Merge
author Michael Pavone <pavone@retrodev.com>
date Mon, 28 Jul 2014 03:25:05 -0700
parents dcfa97976071 (diff) 8f6ade456edf (current diff)
children 4251797af36b 3e5de539a676
files
diffstat 5 files changed, 791 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/code/dotScanner.lm	Mon Jul 28 03:14:50 2014 -0700
+++ b/code/dotScanner.lm	Mon Jul 28 03:25:05 2014 -0700
@@ -29,11 +29,13 @@
 		gridMaxY
 		gridArea
 		grid:inBounds?
-		grid:get:withDefault
 		calcPos
 	] from: (module: "grid.lm")
 
 	visited <- 0
+	badGhostCount <- 0
+	edibleGhosts <- 0
+	totalGhosts <- 0
 
 	advancer <- :continuations {
 		notdone <- 1
@@ -65,6 +67,11 @@
 		}
 		continuations
 	}
+	
+	target0 <- 2
+	target1 <- 3
+	target2 <- 4
+	target3 <- 7
 
 	makeContClos <- :grid myLoc path {
 		{
@@ -75,7 +82,7 @@
 				if: (grid: visited get: myLoc) {
 				} else: {
 					atpos <- grid: grid get: myLoc
-					if: (atpos = 2) + (atpos = 3) + (atpos = 4) {
+					if: (atpos = target0) + (atpos = target1) + (atpos = target2) + (atpos = target3) {
 						//pellet, power pellet, fruit
 						ret <- #[1 (reverse: path)]
 					} else: {
@@ -99,6 +106,7 @@
 
 	step <- :myState world {
 		lmState <- (world tail) value
+		myVitality <- lmState value
 		myLoc <- (lmState tail) value
 		ghostState <- ((world tail) tail) value
 		fruitState <- ((world tail) tail) tail
@@ -116,23 +124,49 @@
 			}
 			makeTree: row 
 		})
+		badGhostCount <- 0
+		edibleGhosts <- 0
+		totalGhosts <- 0
 		grid <- fold: ghostState grid with: :acc ghost {
+			totalGhosts <- totalGhosts + 1
 			vitality <- ghost value
 			loc <- (ghost tail) value
 			dir <- (ghost tail) tail
 			nextloc <- 0
 			move <- 0
+			manHatDist <- 0
 			if: vitality = 1 {
-				//treat fright mode ghosts as a pellet for now
-				acc <- grid: acc set: loc to: 2
+				
+				if: (myLoc value) > (loc value) {
+					manHatDist <- (myLoc value) - (loc value)
+				} else: {
+					manHatDist <- (loc value) - (myLoc value)
+				}
+				if: (myLoc tail) > (loc tail) {
+					manHatDist <- manHatDist + (myLoc tail) - (loc tail)
+				} else: {
+					manHatDist <- manHatDist + (loc tail) - (myLoc tail)
+				}
+				if: myVitality > (manHatDist * 254) {
+					edibleGhosts <- edibleGhosts + 1	
+				} else: {}
+				//mark fright mode ghosts
+				acc <- grid: acc set: loc to: 7
 			} else: {
 				if: vitality = 0 {
+					badGhostCount <- badGhostCount + 1
 					//treat normal mode ghosts as a wall for now
 					acc <- grid: acc set: loc to: 0
 					while: { move < 4 } do: {
 						nextloc <- calcPos: move loc
 						if: (grid: acc inBounds?: nextloc) {
-							acc <- grid: acc set: nextloc to: 0
+							acc <- grid: acc update: nextloc with: :oldval {
+								if: oldval = 3 {
+								} else: {
+									oldval <- 0
+								}
+								oldval
+							}
 						} else: {}
 						move <- move + 1
 					}
@@ -140,6 +174,24 @@
 			}
 			acc
 		}
+		//default behavior, target all yummy things
+		target0 <- 2
+		target1 <- 3
+		target2 <- 4
+		target3 <- 7
+		if: badGhostCount > 0 {
+		} else: {
+			if: totalGhosts > 0 {
+				//ignore power pellets when there are no ghosts in normal mode
+				target1 <- 2
+				if: edibleGhosts > 0 {
+					//ignore anything except edible ghosts when some are in range
+					target0 <- 7
+					target1 <- 7
+					target2 <- 7
+				} else: {}
+			} else: {}
+		}
 		//make sure my location is marked clear even if there is a ghost nearby
 		grid <- grid: grid set: myLoc to: 1
 		visited <- treeMap: grid :row {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/code/ghost0.gq	Mon Jul 28 03:25:05 2014 -0700
@@ -0,0 +1,140 @@
+#{
+	up <- 0
+	right <- 1
+	down <- 2
+	left <- 3
+	
+	getDirX <- :dir startX {
+		if: dir = right {
+			startX <- startX + 1
+		} else: {
+			if: dir = left {
+				startX <- startX - 1 
+			}
+		}
+		startX
+	}
+	
+	getDirY <- :dir startY {
+		if: dir = up {
+			startY <- startY - 1
+		} else: {
+			if: dir = down {
+				startY <- startY + 1 
+			}
+		}
+		startY
+	}
+	
+	opDir <- :dir {
+		if: dir < 2 {
+			dir <- dir + 2
+		} else: {
+			dir <- dir - 2
+		}
+		dir
+	}
+
+	goTowardsX:Y <- :targetX targetY {
+		myIdx <- (me: )
+		myX <- ghostPos: myIdx
+		myY <- yCoord
+		myVit <- ghostStatus: myIdx
+		myDir <- direction
+		
+		
+		firstChoice <- 0
+		secondChoice <- 0
+		
+		if: myX > targetX {
+			//ghost is to the right of target
+			if: myY > targetY {
+				//ghost is below target
+				if: (myX - targetX) > (myY - targetY) {
+					//target is more left than up
+					firstChoice <- left
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- left
+				}
+			} else: {
+				//ghost is above or directly to the right of target
+				if: (myX - targetX) > (targetY - myY) {
+					//target is more left than down
+					firstChoice <- left
+					secondChoice <- down
+				} else: {
+					//target is more down than left
+					firstChoice <- down
+					secondChoice <- left
+				}
+			}
+		} else: {
+			//ghost is to the left of or directly above/below target
+			if: myY > targetY {
+				//ghost is below target
+				if: (targetX - myX) > (myY - targetY) {
+					//target is more right than up
+					firstChoice <- right
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- right
+				}
+			} else: {
+				//ghost is above or directly to the left of target
+				if: (targetX - myX) > (targetY - myY) {
+					//target is more right than down
+					firstChoice <- right
+					secondChoice <- down
+				} else: {
+					//target is more down than right
+					firstChoice <- down
+					secondChoice <- right
+				}
+			}
+		}
+		if: myVit = 1 {
+			//currently in fright mode, try to run away
+			firstChoice <- opDir: firstChoice
+			secondChoice <- opDir: secondChoice
+		}
+		
+		
+		
+		tmp <- 0
+		i <- 0
+		while: { i < 3} do: {
+			targetX <- getDirX: firstChoice myX
+			targetY <- getDirY: firstChoice myY
+		
+			if: (mapContentsAt: targetX targetY) - 1 > 4 {
+				//first choice is a wall or ghost start pos
+				tmp <- firstChoice
+				firstChoice <- secondChoice
+				secondChoice <- opDir: firstChoice
+				i <- i + 1
+			} else: {
+				if: firstChoice = (opDir: myDir) {
+					//first choice is backwards
+					tmp <- firstChoice
+					firstChoice <- secondChoice
+					secondChoice <- opDir: firstChoice
+					i <- i + 1
+				} else: {
+					i <- 3
+				}
+			}
+		}
+		direction!: firstChoice
+
+		0
+	}
+
+	//chases lambda man
+	main <- {
+		lambdamanPos:
+		goTowardsX: xCoord Y: yCoord
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/code/ghost1.gq	Mon Jul 28 03:25:05 2014 -0700
@@ -0,0 +1,158 @@
+#{
+	up <- 0
+	right <- 1
+	down <- 2
+	left <- 3
+	lastX <- 0
+	lastY <- 0
+	
+	getDirX <- :dir startX {
+		if: dir = right {
+			startX <- startX + 1
+		} else: {
+			if: dir = left {
+				startX <- startX - 1 
+			}
+		}
+		startX
+	}
+	
+	getDirY <- :dir startY {
+		if: dir = up {
+			startY <- startY - 1
+		} else: {
+			if: dir = down {
+				startY <- startY + 1 
+			}
+		}
+		startY
+	}
+	
+	opDir <- :dir {
+		if: dir < 2 {
+			dir <- dir + 2
+		} else: {
+			dir <- dir - 2
+		}
+		dir
+	}
+
+	goTowardsX:Y <- :targetX targetY {
+		myIdx <- (me: )
+		myX <- ghostPos: myIdx
+		myY <- yCoord
+		myVit <- ghostStatus: myIdx
+		myDir <- direction
+		
+		
+		firstChoice <- 0
+		secondChoice <- 0
+		
+		if: myX > targetX {
+			//ghost is to the right of target
+			if: myY > targetY {
+				//ghost is below target
+				if: (myX - targetX) > (myY - targetY) {
+					//target is more left than up
+					firstChoice <- left
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- left
+				}
+			} else: {
+				//ghost is above or directly to the right of target
+				if: (myX - targetX) > (targetY - myY) {
+					//target is more left than down
+					firstChoice <- left
+					secondChoice <- down
+				} else: {
+					//target is more down than left
+					firstChoice <- down
+					secondChoice <- left
+				}
+			}
+		} else: {
+			//ghost is to the left of or directly above/below target
+			if: myY > targetY {
+				//ghost is below target
+				if: (targetX - myX) > (myY - targetY) {
+					//target is more right than up
+					firstChoice <- right
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- right
+				}
+			} else: {
+				//ghost is above or directly to the left of target
+				if: (targetX - myX) > (targetY - myY) {
+					//target is more right than down
+					firstChoice <- right
+					secondChoice <- down
+				} else: {
+					//target is more down than right
+					firstChoice <- down
+					secondChoice <- right
+				}
+			}
+		}
+		if: myVit = 1 {
+			//currently in fright mode, try to run away
+			firstChoice <- opDir: firstChoice
+			secondChoice <- opDir: secondChoice
+		}
+		
+		
+		
+		tmp <- 0
+		i <- 0
+		while: { i < 3} do: {
+			targetX <- getDirX: firstChoice myX
+			targetY <- getDirY: firstChoice myY
+		
+			if: (mapContentsAt: targetX targetY) - 1 > 4 {
+				//first choice is a wall or ghost start pos
+				tmp <- firstChoice
+				firstChoice <- secondChoice
+				secondChoice <- opDir: firstChoice
+				i <- i + 1
+			} else: {
+				if: firstChoice = (opDir: myDir) {
+					//first choice is backwards
+					tmp <- firstChoice
+					firstChoice <- secondChoice
+					secondChoice <- opDir: firstChoice
+					i <- i + 1
+				} else: {
+					i <- 3
+				}
+			}
+		}
+		direction!: firstChoice
+
+		0
+	}
+
+	//tries to intercept lambda man by targeting a
+	//position two cells ahead of his current position
+	main <- {
+		lmX <- lambdamanPos:
+		lmY <- yCoord
+		diffX <- 0
+		diffY <- 0
+		
+		if: notFirst? = 1 {
+			diffX <- lmX - lastX
+			diffY <- lmY - lastY
+		}
+		lastX <- lmX
+		lastY <- lmY
+		
+		lmX <- (lmX + diffX) + diffX
+		lmY <- (lmY + diffY) + diffY
+		
+		goTowardsX: lmX Y: lmY
+		
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/code/ghost2.gq	Mon Jul 28 03:25:05 2014 -0700
@@ -0,0 +1,177 @@
+#{
+	up <- 0
+	right <- 1
+	down <- 2
+	left <- 3
+	myIdx <- (me: )
+	lastX <- 0
+	lastY <- 0
+	myX <- 0
+	myY <- 0
+	dist <- 0
+	distX <- 0
+	distY <- 0
+	
+	getDirX <- :dir startX {
+		if: dir = right {
+			startX <- startX + 1
+		} else: {
+			if: dir = left {
+				startX <- startX - 1 
+			}
+		}
+		startX
+	}
+	
+	getDirY <- :dir startY {
+		if: dir = up {
+			startY <- startY - 1
+		} else: {
+			if: dir = down {
+				startY <- startY + 1 
+			}
+		}
+		startY
+	}
+	
+	/*
+	opDir <- :dir {
+		(dir + 2) and: 3
+	}
+	*/
+
+	goTowardsX:Y <- :targetX targetY {
+		myVit <- ghostStatus: myIdx
+		myDir <- direction
+		
+		
+		firstChoice <- 0
+		secondChoice <- 0
+		
+		if: myX > targetX {
+			//ghost is to the right of target
+			if: myY > targetY {
+				//ghost is below target
+				if: distX > distY {
+					//target is more left than up
+					firstChoice <- left
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- left
+				}
+			} else: {
+				//ghost is above or directly to the right of target
+				if: distX > distY {
+					//target is more left than down
+					firstChoice <- left
+					secondChoice <- down
+				} else: {
+					//target is more down than left
+					firstChoice <- down
+					secondChoice <- left
+				}
+			}
+		} else: {
+			//ghost is to the left of or directly above/below target
+			if: myY > targetY {
+				//ghost is below target
+				if: distX > distY {
+					//target is more right than up
+					firstChoice <- right
+					secondChoice <- up
+				} else: {
+					firstChoice <- up
+					secondChoice <- right
+				}
+			} else: {
+				//ghost is above or directly to the left of target
+				if: distX > distY {
+					//target is more right than down
+					firstChoice <- right
+					secondChoice <- down
+				} else: {
+					//target is more down than right
+					firstChoice <- down
+					secondChoice <- right
+				}
+			}
+		}
+		if: myVit = 1 {
+			//currently in fright mode, try to run away if we are close to LM
+			if: dist < 16 {				
+				firstChoice <- (firstChoice + 2) and: 3 //opDir: firstChoice
+				secondChoice <- (secondChoice + 2) and: 3 //opDir: secondChoice
+			}
+		}
+		
+		
+		
+		tmp <- 0
+		i <- 0
+		fail <- 0
+		while: { i < 3} do: {
+			targetX <- getDirX: firstChoice myX
+			targetY <- getDirY: firstChoice myY
+		
+			if: (mapContentsAt: targetX targetY) - 1 > 4 {
+				//first choice is a wall or ghost start pos
+				tmp <- firstChoice
+				firstChoice <- secondChoice
+				secondChoice <- (firstChoice + 2) and: 3 //opDir: firstChoice
+				i <- i + 1
+			} else: {
+				if: firstChoice = ( (myDir + 2) and: 3 /*opDir: myDir*/) {
+					//first choice is backwards
+					tmp <- firstChoice
+					firstChoice <- secondChoice
+					secondChoice <- (firstChoice + 2) and: 3 //opDir: firstChoice
+					i <- i + 1
+				} else: {
+					i <- 3
+				}
+			}
+		}
+		direction!: firstChoice
+
+		0
+	}
+
+	//"wingman" - tries to surround lambda man
+	//by picking a position sideways of his current direction
+	main <- {
+		lmX <- lambdamanPos:
+		lmY <- yCoord
+		myX <- ghostPos: myIdx
+		myY <- yCoord
+		diffX <- 0
+		diffY <- 0
+		
+		if: notFirst? = 1 {
+			diffX <- lmX - lastX
+			diffY <- lmY - lastY
+		}
+		lastX <- lmX
+		lastY <- lmY
+		
+		distX <- myX - lmX
+		if: distX < 0 {
+			distX <- 0 - distX
+		}
+		distY <- myY - lmY
+		if: distY < 0 {
+			distY <- 0 - distX
+		}
+		dist <- distX + distY
+		if: dist < 8 {
+			diffX <- 0
+			diffY <- 0	
+		}
+		
+		lmX <- (lmX + diffY) + diffY
+		lmY <- (lmY + diffX) + diffX
+		
+		goTowardsX: lmX Y: lmY
+		
+	}
+}
\ No newline at end of file
--- a/code/gqc.tp	Mon Jul 28 03:14:50 2014 -0700
+++ b/code/gqc.tp	Mon Jul 28 03:25:05 2014 -0700
@@ -1,14 +1,20 @@
 {
+	reg? <- :val {
+		(object does: val understand?: "isReg?") && (val isReg?)
+	}
+	mem? <- :val {
+		(object does: val understand?: "isMem?") && (val isMem?)
+	}
+	
 	mem <- :_addr {
 		#{
 			addr <- { _addr }
 			string <- { "[" . _addr . "]" }
 			isReg? <- { false }
+			!= <- :other { (not: (mem?: other)) || _addr != (other addr) }
+			= <- :other { (mem?: other) && _addr = (other addr) }
 		}
 	}
-	reg? <- :val {
-		(object does: val understand?: "isReg?") && (val isReg?)
-	}
 	reg <- :_num {
 		#{
 			num <- { _num }
@@ -82,7 +88,8 @@
 	error <- :msg {
 		(file stderr) write: "Error - " . msg . "\n"
 	}
-	_nextVar <- 0
+	//0 is used for the special notFirst? variable
+	_nextVar <- 1
 	//a and b are reserved for int/return values
 	//h is reserved as a stack pointer
 	_allTemp <- [
@@ -94,6 +101,26 @@
 	]
 	_tempRegs <- _allTemp
 	
+	getTemp <- {
+		if: (_tempRegs empty?) {
+			//out of regs, use memory
+			loc <- _nextVar
+			_nextVar <- _nextVar + 1
+			mem: loc
+		} else: {
+			r <- _tempRegs value
+			_tempRegs <- _tempRegs tail
+			r
+		}
+	}
+	
+	preserveTemps <- :fun {
+		saveTempRegs <- _tempRegs
+		res <- fun:
+		_tempRegs <- saveTempRegs
+		res
+	}
+	
 	_exprHandlers <- dict hash
 	
 	compileExpr:syms <- :expr :syms {
@@ -118,19 +145,22 @@
 	_opNames set: "xor" "XOR"
 	
 	_exprHandlers set: (ast binary) :expr syms {
-		startTempRegs <- _tempRegs
-		l <- compileExpr: (expr left) syms: syms
-		r <- compileExpr: (expr right) syms: syms
+		l <- 0
+		r <- preserveTemps: {
+			l <- compileExpr: (expr left) syms: syms
+			compileExpr: (expr right) syms: syms
+		}
 		dest <- l
 		if: (reg?: l) {
-			_tempRegs <- startTempRegs filter: :r { r != l }
+			//reallocate temp register used by l
+			//not always safe, needs work
+			_tempRegs <- _tempRegs filter: :r { r != l }
 		} else: {		
-			dest <- startTempRegs value
+			dest <- getTemp:
 			prog add: (inst: "MOV" #[
 				dest
 				l
 			])
-			_tempRegs <- startTempRegs tail
 		}
 		_opNames ifget: (expr op) :i {
 			prog add: (inst: i #[
@@ -161,14 +191,16 @@
 		info <- syms find: (sym name) else: {
 			error: "this should never happen!"
 		}
-		startTempRegs <- _tempRegs
-		v <- compileExpr: (expr assign) syms: syms
-		_tempRegs <- startTempRegs
+		v <- preserveTemps: {
+			compileExpr: (expr assign) syms: syms
+		}
 		dest <- info def
-		prog add: (inst: "MOV" #[
-			dest
-			v
-		])
+		if: dest != v {
+			prog add: (inst: "MOV" #[
+				dest
+				v
+			])
+		}
 		dest
 	}
 	
@@ -180,9 +212,9 @@
 	}
 	_funHandlers set: "direction!" :args syms {
 		dir <- args value
-		startTempRegs <- _tempRegs
-		v <- compileExpr: dir syms: syms
-		_tempRegs <- startTempRegs
+		v <- preserveTemps: {
+			compileExpr: dir syms: syms
+		}
 		if: (reg: 0) != v {	
 			prog add: (inst: "MOV" #[
 				reg: 0
@@ -208,9 +240,9 @@
 		intNum <- idx + 4
 		_funHandlers set: name :args syms {
 			ghostIdx <- args value
-			startTempRegs <- _tempRegs
-			v <- compileExpr: ghostIdx syms: syms
-			_tempRegs <- startTempRegs
+			v <- preserveTemps: {
+				compileExpr: ghostIdx syms: syms
+			}
 			if: (reg: 0) != v {	
 				prog add: (inst: "MOV" #[
 					reg: 0
@@ -224,10 +256,10 @@
 	_funHandlers set: "mapContentsAt" :args syms {
 		x <- args value
 		y <- (args tail) value
-		startTempRegs <- _tempRegs
-		x <- compileExpr: x syms: syms
-		y <- compileExpr: y syms: syms
-		_tempRegs <- startTempRegs
+		preserveTemps: {
+			x <- compileExpr: x syms: syms
+			y <- compileExpr: y syms: syms
+		}
 		if: (reg: 0) != x {
 			prog add: (inst: "MOV" #[	
 				reg: 0
@@ -243,12 +275,33 @@
 		prog add: (inst: "INT" #[7])
 		reg: 0
 	}
+	//new Quiche parser doesn't support and/or/xor operators yet :(
+	_funHandlers set: "and" :args syms {
+		l <- 0
+		r <- preserveTemps: {
+			l <- compileExpr: (args value) syms: syms
+			compileExpr: ((args tail) value) syms: syms
+		}
+		dest <- getTemp:
+		if: dest != l {
+			prog add: (inst: "MOV" #[
+				dest
+				l
+			])
+		}
+		prog add: (inst: "AND" #[
+			dest
+			r
+		])
+		dest
+	}
 	
 	//allow access to raw instructions
 	foreach: #["MOV" "INC" "DEC" "ADD" "SUB" "MUL" "DIV" "AND" "OR" "XOR" "JLT" "JEQ" "JGT" "HLT"] :idx instName {
 		_funHandlers set: instName :args syms {
-			saveTempRegs <- _tempRegs
-			args <- args map: :arg { compileExpr: arg syms: syms }
+			preserveTemps: {
+				args <- args map: :arg { compileExpr: arg syms: syms }
+			}
 			prog add: (inst: instName args)
 		}
 	}
@@ -262,10 +315,11 @@
 			end <- prog makeLabel: "loop_end"
 			prog setLabel: top
 			
-			saveTempRegs <- _tempRegs
-			l <- compileExpr: (cond left) syms: syms
-			r <- compileExpr: (cond right) syms: syms
-			_tempRegs <- saveTempRegs
+			l <- 0
+			r <- preserveTemps: {
+				l <- compileExpr: (cond left) syms: syms
+				compileExpr: (cond right) syms: syms
+			}
 			
 			ok <- true
 			//we need the inverse check in the instruction since a true condition
@@ -347,9 +401,9 @@
 						prog setLabel: lbl
 						syms define: (expr name) lbl
 					} else: {
-						saveTempRegsExpr <- _tempRegs
-						v <- compileExpr: expr syms: syms
-						_tempRegs <- saveTempRegsExpr
+						v <- preserveTemps: {
+							compileExpr: expr syms: syms
+						}
 					}
 				}
 				prog add: (inst: "MOV" #[
@@ -375,10 +429,11 @@
 			falseLbl <- prog makeLabel: "false"
 			endLbl <- prog makeLabel: "end"
 			
-			saveTempRegs <- _tempRegs
-			l <- compileExpr: (cond left) syms: syms
-			r <- compileExpr: (cond right) syms: syms
-			_tempRegs <- saveTempRegs
+			l <- 0
+			r <- preserveTemps: {
+				l <- compileExpr: (cond left) syms: syms
+				compileExpr: (cond right) syms: syms
+			}
 			
 			ok <- true
 			
@@ -454,9 +509,9 @@
 						prog setLabel: lbl
 						syms define: (expr name) lbl
 					} else: {
-						saveTempRegsExpr <- _tempRegs
-						v <- compileExpr: expr syms: syms
-						_tempRegs <- saveTempRegsExpr
+						v <- preserveTemps: {
+							compileExpr: expr syms: syms
+						}
 					}
 				}
 				prog add: (inst: "MOV" #[
@@ -472,9 +527,9 @@
 						prog setLabel: lbl
 						syms define: (expr name) lbl
 					} else: {
-						saveTempRegsExpr <- _tempRegs
-						v <- compileExpr: expr syms: syms
-						_tempRegs <- saveTempRegsExpr
+						v <- preserveTemps: {
+							compileExpr: expr syms: syms
+						}
 					}
 				}
 				prog setLabel: endLbl
@@ -486,6 +541,108 @@
 		}
 	}
 	
+	_funHandlers set: "if" :args syms {
+		cond <- (args value)
+		trueBody <- ((args tail) value) expressions
+		
+		if: (cond nodeType) = (ast binary) {
+			trueLbl <- prog makeLabel: "true"
+			endLbl <- prog makeLabel: "end"
+			
+			l <- 0
+			r <- preserveTemps: {
+				l <- compileExpr: (cond left) syms: syms
+				compileExpr: (cond right) syms: syms
+			}
+			
+			ok <- true
+			
+			if: (cond op) = ">=" {
+				prog add: (inst: "JLT" #[
+					endLbl
+					l
+					r
+				])
+			} else: {
+				if: (cond op) = "<=" {
+					prog add: (inst: "JGT" #[
+						endLbl
+						l
+						r
+					])
+				} else: {
+					if: (cond op) = "!=" {
+						prog add: (inst: "JEQ" #[
+							endLbl
+							l
+							r
+						])
+					} else: {
+						if: (cond op) = ">" {
+							prog add: (inst: "JGT" #[
+								trueLbl
+								l
+								r
+							])
+							prog add: (inst: "MOV" #[
+								reg: 8
+								endLbl
+							])
+						} else: {
+							if: (cond op) = "<" {
+								prog add: (inst: "JLT" #[
+									trueLbl
+									l
+									r
+								])
+								prog add: (inst: "MOV" #[
+									reg: 8
+									endLbl
+								])
+							}  else: {
+								bodyLbl <- prog makeLabel: "loop_body"
+								if: (cond op) = "=" {
+									prog add: (inst: "JEQ" #[
+										trueLbl
+										l
+										r
+									])
+									prog add: (inst: "MOV" #[
+										reg: 8
+										endLbl
+									])
+								} else: {
+									ok <- false
+								}
+							}
+						}
+					}
+				}
+			}
+			if: ok {
+				prog setLabel: trueLbl
+				//TODO: do 2 passes for labels to allow forward references
+				foreach: trueBody :idx expr {
+					if: (expr nodeType) = (ast sym) {
+						//allow using bare symbols to define labels
+						lbl <- prog makeLabel: (expr name)
+						prog setLabel: lbl
+						syms define: (expr name) lbl
+					} else: {
+						v <- preserveTemps: {
+							compileExpr: expr syms: syms
+						}
+					}
+				}
+				prog setLabel: endLbl
+			} else: {
+				error: "Condition parameter to if must be a comparison operator expression"
+			}
+		} else: {
+			error: "Condition parameter to if must be a comparison operator expression"
+		}
+	}
+	
 	_exprHandlers set: (ast call) :expr syms {
 		tc <- (expr tocall)
 		if: (tc nodeType) = (ast sym) {
@@ -493,9 +650,9 @@
 				handler: (expr args) syms
 			} else: {
 				syms ifDefined: (tc name) :info {
-					saveTempRegs <- _tempRegs
-					funArgs <- (expr args) map: :arg { compileExpr: arg syms: syms}
-					_tempRegs <- saveTempRegs
+					funArgs <- preserveTemps: {
+						(expr args) map: :arg { compileExpr: arg syms: syms}
+					}
 				
 					//save registers that need it
 					needSave <- _allTemp filter: :r {
@@ -562,41 +719,40 @@
 	_compileFun <- :fName fun globsyms {
 		syms <- symbols tableWithParent: globsyms
 		
-		saveTempRegs <- _tempRegs
-		foreach: (fun args) :idx arg {
-			argname <- (if: (arg startsWith?: ":") { arg from: 1 } else: { arg })
-			r <- _tempRegs value
-			_tempRegs <- _tempRegs tail
-			syms define: argname r
-		}
-		
-		lastexpr <- ((fun expressions) length) - 1
-		
-		//TODO: do 2 passes for labels to allow forward references
-		foreach: (fun expressions) :idx expr {
-			if: idx != lastexpr && (expr nodeType) = (ast sym) {
-				//allow using bare symbols to define labels
-				prog setLabel: (expr name)
-				syms define: (expr name) (expr name)
-			} else: {
-				saveTempRegsExpr <- _tempRegs
-				v <- compileExpr: expr syms: syms
-				_tempRegs <- saveTempRegsExpr
-				if: idx = lastexpr && (fName != "main") {
-					//move result to a register
-					prog add: (inst: "MOV" #[
-						reg: 0
-						v
-					])
-					//return instruction
-					prog add: (inst: "MOV" #[
-						reg: 8
-						mem: (reg: 7)
-					])
+		preserveTemps: {
+			foreach: (fun args) :idx arg {
+				argname <- (if: (arg startsWith?: ":") { arg from: 1 } else: { arg })
+				r <- getTemp:
+				syms define: argname r
+			}
+			
+			lastexpr <- ((fun expressions) length) - 1
+			
+			//TODO: do 2 passes for labels to allow forward references
+			foreach: (fun expressions) :idx expr {
+				if: idx != lastexpr && (expr nodeType) = (ast sym) {
+					//allow using bare symbols to define labels
+					prog setLabel: (expr name)
+					syms define: (expr name) (expr name)
+				} else: {
+					v <- preserveTemps: {
+						compileExpr: expr syms: syms
+					}
+					if: idx = lastexpr && (fName != "main") {
+						//move result to a register
+						prog add: (inst: "MOV" #[
+							reg: 0
+							v
+						])
+						//return instruction
+						prog add: (inst: "MOV" #[
+							reg: 8
+							mem: (reg: 7)
+						])
+					}
 				}
 			}
 		}
-		saveTempRegs <- _tempRegs
 	}
 	
 	#{
@@ -606,6 +762,20 @@
 				outer <- res yield
 				functions <- dict hash
 				syms <- symbols table
+				
+				//define symbols for the special notFirst? variable
+				syms define: "notFirst?" (mem: 0)
+				//use it to skip global init on subsequent runthroughs
+				prog add: (inst: "JEQ" #[
+					"main"
+					(mem: 0)
+					1
+				])
+				prog add: (inst: "MOV" #[
+					(mem: 0)
+					1
+				])
+				
 				//define symbols for all registers
 				//for low level shenanigans
 				i <- 0
@@ -630,7 +800,14 @@
 							functions set: sym def
 							syms define: sym sym
 						} else: {
-							compileExpr: msg syms: syms
+							if: (def nodeType) != (ast intlit) || (def val) != 0{
+								preserveTemps: {
+									compileExpr: msg syms: syms
+								}
+							} else: {
+								syms define: sym (mem: _nextVar)
+								_nextVar <- _nextVar + 1
+							}
 						}
 					} else: {
 						error: "Only assignments are allowed at the top level"