diff --git a/ROM2/Makefile b/ROM2/Makefile
index 49c8f89e88999e9fc99cc92b31db6f7000625ce7..f3cfd3bf642d8ccdb0f90ad59b784e9b57310c13 100644
--- a/ROM2/Makefile
+++ b/ROM2/Makefile
@@ -7,7 +7,7 @@ OBJS = \
 INCLUDES = vend.h keypad.h chime.h asm.h display_basic.h ports.h types.h
 # debugging doesn't get compiled into the ROM image
 CFLAGS = -m68hc11 -mshort -Wall -O1 \
-	-msoft-reg-count=0 -ffixed-z -g #-fomit-frame-pointer
+	-msoft-reg-count=0 -ffixed-z -g -fomit-frame-pointer
 
 LDFLAGS = -m68hc11 -mshort -Wl,-m,m68hc11elfb \
 	  -nostartfiles \
@@ -56,7 +56,7 @@ size:   rom2.s19
 
 rom.tar.bz2:
 	rm -f romsrc.s crctab.h
-	tar cjf rom.tar.bz2 README Makefile gdbsimrc *.pl *.c *.h *.s *.x
+	tar c README Makefile gdbsimrc *.pl *.c *.h *.s *.x | bzip2 -c -9 > $@
 
 romsrc.s: rom.tar.bz2 src2asm.pl
 	perl -w src2asm.pl < $< > $@
@@ -69,13 +69,6 @@ gencrctab: gencrctab.c
 crctab.h: gencrctab
 	./gencrctab > $@
 
-#
-# Implicit rules
-#
-# .elf is for the simulator and gdb
-# .s19 is for some downloader and the simulator
-# .b   is a binary dump
-#
 .SUFFIXES: .elf .s19 .b
 
 .elf.s19:
diff --git a/ROM2/README b/ROM2/README
index 3bfc65376de3e969678165b3c4e108284f1d3564..653d5610658d1e3efd642fcdf28f27517de0c849 100644
--- a/ROM2/README
+++ b/ROM2/README
@@ -1,14 +1,3 @@
 This is the source code for the Snack Machine's new ROM at the University
-Computer Club.
-
-To build, you'll require the gcc-m68hc1x & binutils-m68hc1x packages.
-
-You can also run it in a simulator using gdb-m68hc1x. Except to do so, you'll
-need to use a local copy generated as such:
-
-sed -e 's|m68hc11eepr/reg 0xb000 512|m68hc11eepr/reg 0x4000 1  |' < /usr/bin/m68hc11-gdb > my-m68hc11-gdb
-
-This is in order to move the internal eprom out of the way in the simulator that
-would otherwise cause the simulator to fail when loading the ROM.
-
-
+Computer Club. To build, you'll require the gcc-m68hc1x & binutils-m68hc1x
+packages.
diff --git a/ROM2/helpers.c b/ROM2/helpers.c
index f7bb4062fe0b5786aa5888c8c5f1a275e9558aa2..13c2d36360a8484317a94f087db8e39e3678ec90 100644
--- a/ROM2/helpers.c
+++ b/ROM2/helpers.c
@@ -2,18 +2,9 @@
 #include "vend.h"
 
 void delay(u16 ms) {
-	/* delay routine written in assembly so we know what we're really getting.
-	 * each inner loop should take ~1 ms to execute.
-	 * 15 cycles * (1/4.9152Mhz) * 327 = 0.997 ms + a little bit on the fringes.
-	 *
-	 * XXX - how do we know gcc isn't optimising this? it seems to optimise after
-	 * parsing C -> asm, but before asm -> machine code.
-	 */
-	//asm volatile ("pshx\npsha\npshb\n"); /* save registers */
 	asm volatile ("ldx %0\n" :: "m" (ms) : "x");
 	asm volatile (
 		"delay_loop:\n"
-		//"	ldd #327\n"                   /* 3 */
 		"	ldd #150\n"                   /* 3 */
 		"delay_inner_loop:\n" /* 15 cycles each */
 		"	cpd #0x0000\n"                /* 5 */
@@ -25,9 +16,6 @@ void delay(u16 ms) {
 		"	beq delay_out\n"              /* 3 */
 		"	bra delay_loop\n"             /* 3 */
 		"delay_out:\n" ::: "x", "d");
-		/*"	pulb\n"
-		"	pula\n"
-		"	pulx\n");*/
 }
 
 u8 my_strlen(char* s) {
@@ -87,3 +75,10 @@ char* u82hex(u8 a) {
 	return hexconv_buf;
 }
 
+bool ishex(char b) {
+	if (b >= '0' && b <= '9') return 1;
+	if (b >= 'a' && b <= 'f') return 1;
+	if (b >= 'A' && b <= 'F') return 1;
+	return 0;
+}
+
diff --git a/ROM2/main_basic.c b/ROM2/main_basic.c
index a6a8a155b576331c09034060224be66d75dcd97d..57973188a6dff57960d279ffe69ea13e045f2eb8 100644
--- a/ROM2/main_basic.c
+++ b/ROM2/main_basic.c
@@ -4,7 +4,7 @@
  * and snacks.
  */
 
-#define VERSION_STRING "R 20040622"
+#define VERSION_STRING "V 20040624"
 
 #include "display_basic.h"
 #include "keypad.h"
@@ -34,6 +34,14 @@ bool check_standalone() {
 	return 0;
 }
 
+bool check_badpoke() {
+	if (cant_poke()) {
+		send_string("099 Can't poke without flipping DIP SW 3." CRLF);
+		return 1;
+	}
+	return 0;
+}
+
 void unknown_command() {
 	send_string("012 Unknown command. Type HELP for help." CRLF);
 }
@@ -252,6 +260,86 @@ void ping_pong() {
 	send_string("000 PONG!" CRLF);
 }
 
+u16 hex2addr(char* addrptr) {
+	u16 v;
+	v = hex2u8(addrptr[0], addrptr[1]) << 8;
+	v |= hex2u8(addrptr[2], addrptr[3]);
+	return v;
+}
+
+void peek() {
+	if (!my_strncmp("EEK", (char*)sci_rx_buf+1, 3)) {
+		unknown_command();
+		return;
+	}
+	if (check_badpoke()) return;
+	if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
+			ishex(sci_rx_buf[7]) && sci_rx_buf[8] == '\0') {
+		u16 v = hex2addr((char*)(sci_rx_buf+4));
+		v = *((u8*)v);
+		send_string("090 ");
+		send_string(u82hex(v));
+		send_string(CRLF);
+		return;
+	}
+	send_string("091 Invalid location given." CRLF);
+}
+
+void poke() {
+	if (!my_strncmp("OKE", (char*)sci_rx_buf+1, 3)) {
+		unknown_command();
+		return;
+	}
+	if (check_badpoke()) return;
+	if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
+			ishex(sci_rx_buf[7]) && ishex(sci_rx_buf[8]) && ishex(sci_rx_buf[9])
+			&& sci_rx_buf[10] == '\0') {
+		u16 v;
+		v = hex2addr((char*)(sci_rx_buf+4));
+		*(u8*)v = hex2u8(sci_rx_buf[8], sci_rx_buf[9]);
+		send_string("080 Written." CRLF);
+		return;
+	}
+	send_string("081 Invalid location or byte given." CRLF);
+}
+
+void jump() {
+	if (!my_strncmp("UMP", (char*)sci_rx_buf+1, 3)) {
+		unknown_command();
+		return;
+	}
+	if (check_badpoke()) return;
+	if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
+			ishex(sci_rx_buf[7]) && sci_rx_buf[8] == '\0') {
+		u16 v = hex2addr((char*)(sci_rx_buf+4));
+		send_string("070 Jumping now." CRLF);
+		asm volatile ("jsr %0" : : "m"(*(u16*)v) : "d");
+		send_string("071 And back." CRLF);
+		return;
+	}
+	send_string("079 Invalid location given." CRLF);
+}
+
+void do_set_password() {
+	u8 i;
+	bool good = 1;
+	if (check_badpoke()) return;
+	for (i=1; i < 17; i++) {
+		if (sci_rx_buf[i] == '\0') {
+			good = 0;
+			break;
+		}
+	}
+	if (good && sci_rx_buf[17] != '\0') good = 0;
+	if (!good) {
+		send_string("061 Password must be exactly 16 characters." CRLF);
+		return;
+	}
+
+	mic_set_secret((char*)(sci_rx_buf+1));
+	send_string("060 Password has been set." CRLF);
+}
+
 void send_prompt() {
 	if (must_verify()) {
 		send_string(u82hex(mic_challenge >> 8));
@@ -322,16 +410,26 @@ void help() {
 	send_string(
 		"Valid commands are:" CRLF
 		" ABOUT         ROM information" CRLF
-		" PING          pongs" CRLF
+		" B[S][nn]      beep [synchronously] for a duration nn (optional)" CRLF
+		" C[S][nn]      silence [synchronously] for a duration nn (optional)" CRLF
+		" Dxxxxxxxxxx   show a message on the display" CRLF
 		" ECHO {ON|OFF} turn echo on or off" CRLF
-		" Vnn           vend an item" CRLF
-		" VALL          vend all items" CRLF
-		" DXXXXXXXXXX   show a message on the display" CRLF
-		" B[nn]         beep for a duration nn (optional)" CRLF
-		" S[...]        query all internal switch states" CRLF
-		" H[...]        this help screen" CRLF
 		" GETROM        download the ROM source code using xmodem" CRLF
-		"Comments start with a #" CRLF
+		" H[...]        this help screen" CRLF
+		"*JUMPxxxx      jumps to a subroute at location xxxx" CRLF
+		"*PEEKxxxx      returns the value of the byte at location xxxx" CRLF
+		"*POKExxxxyy    sets the value of location xxxx to yy" CRLF
+		" PING          pongs" CRLF
+		" S[...]        query all internal switch states" CRLF
+		"+Vnn           vend an item" CRLF
+		"+VALL          vend all items" CRLF
+		"*Wxxxxxxxxxxxx set a new password for authenticated vends. xxx=16 chars" CRLF
+		"" CRLF
+		"Very few functions are available when the machine is in standalone " CRLF
+		"mode (DIP SW 1 is set)" CRLF
+		"+ denotes that this item requires authentication if DIP SW 2 is set" CRLF
+		"* denotes that DIP SW 3 must be set to use these" CRLF
+		"Commands starting with # are ignored (comments)" CRLF
 	);
 }
 
@@ -404,9 +502,6 @@ void quit() {
 		unknown_command();
 }
 
-//SHA1_CTX ctx;
-//u8 sha1_digest[SHA1_SIGNATURE_SIZE];
-
 int main() {
 	u8 i;
 	for (i = 0; i < 11; i++)
@@ -444,7 +539,6 @@ int main() {
 	send_string("5N4X0RZ R US" CRLF);
 
 	mic_challenge = 0;
-	mic_set_secret("ABCDEFGH12345678");
 	
 	last_standalone = is_standalone();
 	if (last_standalone)
@@ -494,17 +588,8 @@ int main() {
 				case '#':
 					send_string(CRLF);
 					break;
-				case 'E':
-					set_echo();
-					break;
-				case 'H':
-					help();
-					break;
-				case 'V':
-					dispense_something();
-					break;
-				case 'D':
-					write_to_display();
+				case 'A':
+					about();
 					break;
 				case 'B': 
 					do_chime();
@@ -512,23 +597,43 @@ int main() {
 				case 'C': 
 					do_silence();
 					break;
-				case 'P':
-					ping_pong();
+				case 'D':
+					write_to_display();
 					break;
-				case 'A':
-					about();
+				case 'E':
+					set_echo();
 					break;
-				case 'S':
-					print_switches(1);
+				case 'G':
+					getrom();
+					break;
+				case 'H':
+					help();
 					break;
 				case 'M':
 					moo();
 					break;
+				case 'P':
+					if (sci_rx_buf[1] == 'I')
+						ping_pong();
+					else if (sci_rx_buf[1] == 'O')
+						poke();
+					else if (sci_rx_buf[1] == 'E')
+						peek();
+					break;
+				case 'J':
+					jump();
+					break;
 				case 'Q':
 					quit();
 					break;
-				case 'G':
-					getrom();
+				case 'S':
+					print_switches(1);
+					break;
+				case 'V':
+					dispense_something();
+					break;
+				case 'W':
+					do_set_password();
 					break;
 				default:
 					// shurg
diff --git a/ROM2/src2asm.pl b/ROM2/src2asm.pl
index 2d380154adf9dc11cf95d84eabf9ccce46f5b6d3..b723c6dedcd56e2f1ba61d5a4ca740def877e7c1 100644
--- a/ROM2/src2asm.pl
+++ b/ROM2/src2asm.pl
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -w
 
 # keep the format of this next line the same to match regex in check-romsrc.pl
-$origin = 0x9d80;
+$origin = 0x9c80;
 $hole_start = 0xb600;
 $hole_size = 0x0200;
 
diff --git a/ROM2/vend.h b/ROM2/vend.h
index 5d4f5ac9931a20064f73e973e7c5575e3f58640b..f2f710647e236f2d37378f4383bf09530a569437 100644
--- a/ROM2/vend.h
+++ b/ROM2/vend.h
@@ -27,11 +27,13 @@ extern volatile u8 _home_sensors;
 #define home_sensors _home_sensors
 
 #define is_standalone() (misc_input & 0x01) /* DIP sw 1 */
-#define must_verify()   !(misc_input & 0x02) /* DIP sw 2 */
+#define must_verify()   (misc_input & 0x02) /* DIP sw 2 */
+#define cant_poke()     (misc_input & 0x04) /* DIP sw 3 */
 
 extern u16 _stack;
 extern char _nvram[]; /* 2048 bytes of nvram! */
-// uncomment for testing. char _nvram[20]; /* 2048 bytes of nvram! */
+// uncomment for testing.
+//char _nvram[20];
 /* NVRAM map:
  *
  * START LEN
@@ -51,6 +53,7 @@ char* u82hex(u8 a);
 u8 hex2u8(char msb, char lsb);
 char nibble2hexchar(u8 b);
 u8 hexchar2u8(char b);
+bool ishex(char b);
 
 /******** Some meaningful bits ******/
 #define PORTA_CHIME         0x10 /* chime is on when set */