changeset 652:f822d9216968

Merge
author Michael Pavone <pavone@retrodev.com>
date Tue, 30 Dec 2014 19:11:34 -0800
parents 9d6fed6501ba (current diff) 103d5cabbe14 (diff)
children a18e3923481e
files .hgignore 68kinst.c Makefile blastem.c debug.c dis.c z80_to_x86.c z80_to_x86.h
diffstat 23 files changed, 3590 insertions(+), 813 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Dec 29 23:08:39 2014 -0800
+++ b/.hgignore	Tue Dec 30 19:11:34 2014 -0800
@@ -17,6 +17,7 @@
 generated_tests/*
 ztests/*
 *.o
+*.list
 blastem
 dis
 stateview
--- a/68kinst.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/68kinst.c	Tue Dec 30 19:11:34 2014 -0800
@@ -19,7 +19,7 @@
 
 uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t size, m68k_op_info *dst)
 {
-	uint16_t ext;
+	uint16_t ext, tmp;
 	dst->addr_mode = mode;
 	switch(mode)
 	{
@@ -36,15 +36,95 @@
 		dst->params.regs.displacement = sign_extend16(ext);
 		break;
 	case MODE_AREG_INDEX_MEM:
-		#ifdef M68020
-			//TODO: implement me for M68020+ support
-		#else
+		dst->params.regs.pri = reg;
+		ext = *(++cur);
+		dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit
+#ifdef M68020
+		dst->params.regs.scale = ext >> 9 & 3;
+		if (ext & 0x100)
+		{
+			dst->params.regs.disp_sizes = ext >> 4 & 3;
+			switch (dst->params.regs.disp_sizes)
+			{
+			case 0:
+				//reserved
+				return NULL;
+			case 1:
+				dst->params.regs.displacement = 0;
+				break;
+			case 2:
+				dst->params.regs.displacement = sign_extend16(*(cur++));
+				break;
+			case 3:
+				tmp = *(cur++);
+				dst->params.regs.displacement = tmp << 16 | *(cur++);
+				break;
+			}
+			if (ext & 0x3)
+			{
+				//memory indirect
+				switch (ext & 0xC4)
+				{
+				case 0x00:
+					dst->addr_mode = MODE_AREG_PREINDEX;
+					break;
+				case 0x04:
+					dst->addr_mode = MODE_AREG_POSTINDEX;
+					break;
+				case 0x40:
+					dst->addr_mode = MODE_AREG_MEM_INDIRECT;
+					break;
+				case 0x80:
+					dst->addr_mode = MODE_PREINDEX;
+					break;
+				case 0x84:
+					dst->addr_mode = MODE_POSTINDEX;
+					break;
+				case 0xC0:
+					dst->addr_mode = MODE_MEM_INDIRECT;
+					break;
+				}
+				dst->params.regs.disp_sizes |= ext << 4 & 0x30;
+				switch (ext & 0x3)
+				{
+				case 0:
+					//reserved
+					return NULL;
+				case 1:
+					dst->params.regs.outer_disp = 0;
+					break;
+				case 2:
+					dst->params.regs.outer_disp = sign_extend16(*(cur++));
+					break;
+				case 3:
+					tmp = *(cur++);
+					dst->params.regs.outer_disp = tmp << 16 | *(cur++);
+					break;
+				}
+			} else {
+				switch (ext >> 6 & 3)
+				{
+				case 0:
+					dst->addr_mode = MODE_AREG_INDEX_BASE_DISP;
+					break;
+				case 1:
+					dst->addr_mode = MODE_AREG_BASE_DISP;
+					break;
+				case 2:
+					dst->addr_mode = MODE_INDEX_BASE_DISP;
+					break;
+				case 3:
+					dst->addr_mode = MODE_BASE_DISP;
+					break;
+				}
+			}
+		} else {
+#endif
 			dst->addr_mode = MODE_AREG_INDEX_DISP8;
-			dst->params.regs.pri = reg;
-			ext = *(++cur);
-			dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit
 			dst->params.regs.displacement = sign_extend8(ext&0xFF);
-		#endif
+#ifdef M68020
+		}
+#endif
 		break;
 	case MODE_PC_INDIRECT_ABS_IMMED:
 		switch(reg)
@@ -60,13 +140,93 @@
 			dst->params.immed = ext << 16 | *(++cur);
 			break;
 		case 3:
-#ifdef M68020
-			//TODO: Implement me for M68020+ support;
-#else
-			dst->addr_mode = MODE_PC_INDEX_DISP8;
 			ext = *(++cur);
 			dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit
-			dst->params.regs.displacement = sign_extend8(ext&0xFF);
+#ifdef M68020
+			dst->params.regs.scale = ext >> 9 & 3;
+			if (ext & 0x100)
+			{
+				dst->params.regs.disp_sizes = ext >> 4 & 3;
+				switch (dst->params.regs.disp_sizes)
+				{
+				case 0:
+					//reserved
+					return NULL;
+				case 1:
+					dst->params.regs.displacement = 0;
+					break;
+				case 2:
+					dst->params.regs.displacement = sign_extend16(*(cur++));
+					break;
+				case 3:
+					tmp = *(cur++);
+					dst->params.regs.displacement = tmp << 16 | *(cur++);
+					break;
+				}
+				if (ext & 0x3)
+				{
+					//memory indirect
+					switch (ext & 0xC4)
+					{
+					case 0x00:
+						dst->addr_mode = MODE_PC_PREINDEX;
+						break;
+					case 0x04:
+						dst->addr_mode = MODE_PC_POSTINDEX;
+						break;
+					case 0x40:
+						dst->addr_mode = MODE_PC_MEM_INDIRECT;
+						break;
+					case 0x80:
+						dst->addr_mode = MODE_ZPC_PREINDEX;
+						break;
+					case 0x84:
+						dst->addr_mode = MODE_ZPC_POSTINDEX;
+						break;
+					case 0xC0:
+						dst->addr_mode = MODE_ZPC_MEM_INDIRECT;
+						break;
+					}
+					dst->params.regs.disp_sizes |= ext << 4 & 0x30;
+					switch (ext & 0x3)
+					{
+					case 0:
+						//reserved
+						return NULL;
+					case 1:
+						dst->params.regs.outer_disp = 0;
+						break;
+					case 2:
+						dst->params.regs.outer_disp = sign_extend16(*(cur++));
+						break;
+					case 3:
+						tmp = *(cur++);
+						dst->params.regs.outer_disp = tmp << 16 | *(cur++);
+						break;
+					}
+				} else {
+					switch (ext >> 6 & 3)
+					{
+					case 0:
+						dst->addr_mode = MODE_PC_INDEX_BASE_DISP;
+						break;
+					case 1:
+						dst->addr_mode = MODE_PC_BASE_DISP;
+						break;
+					case 2:
+						dst->addr_mode = MODE_ZPC_INDEX_BASE_DISP;
+						break;
+					case 3:
+						dst->addr_mode = MODE_ZPC_BASE_DISP;
+						break;
+					}
+				}
+			} else {
+#endif
+				dst->addr_mode = MODE_PC_INDEX_DISP8;
+				dst->params.regs.displacement = sign_extend8(ext&0xFF);
+#ifdef M68020
+			}
 #endif
 			break;
 		case 2:
@@ -172,7 +332,7 @@
 			istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 			if (decoded->dst.addr_mode == MODE_REG) {
 				decoded->extra.size = OPSIZE_LONG;
@@ -202,7 +362,7 @@
 			istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 			if (decoded->dst.addr_mode == MODE_REG) {
 				decoded->extra.size = OPSIZE_LONG;
@@ -248,7 +408,7 @@
 					istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				}
 				break;
@@ -287,7 +447,7 @@
 					istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				}
 				break;
@@ -314,7 +474,7 @@
 				istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			case 3:
@@ -340,7 +500,7 @@
 				istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			case 4:
@@ -365,7 +525,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			case 5:
@@ -403,7 +563,7 @@
 					istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				}
 				break;
@@ -427,15 +587,29 @@
 					decoded->src.params.immed = (immed << 16) | *(++istream);
 					break;
 				}
-				istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst));
+				istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			case 7:
-
-
+#ifdef M68010
+				decoded->op = M68K_MOVES;
+				decoded->extra.size = *istream >> 6 & 0x3;
+				immed = *(++istream);
+				reg = immed  >> 12 & 0x7;
+				opmode = immed & 0x8000 ? MODE_AREG : MODE_REG;
+				if (immed & 0x800) {
+					decoded->src.addr_mode = opmode;
+					decoded->src.params.regs.pri = reg;
+					m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->dst));
+				} else {
+					m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->src));
+					decoded->dst.addr_mode = opmode;
+					decoded->dst.params.regs.pri = reg;
+				}
+#endif
 				break;
 			}
 		}
@@ -450,12 +624,12 @@
 		istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 		if (!istream) {
 			decoded->op = M68K_INVALID;
-			return start+1;
+			break;
 		}
 		istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst));
 		if (!istream || decoded->dst.addr_mode == MODE_IMMEDIATE) {
 			decoded->op = M68K_INVALID;
-			return start+1;
+			break;
 		}
 		break;
 	case MISC:
@@ -468,7 +642,7 @@
 			istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		} else {
 			if (*istream & 0x100) {
@@ -489,7 +663,7 @@
 				istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			} else {
 				opmode = (*istream >> 3) & 0x7;
@@ -504,7 +678,7 @@
 						istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->src));
 						if (!istream) {
 							decoded->op = M68K_INVALID;
-							return start+1;
+							break;
 						}
 						if (decoded->src.addr_mode == MODE_PC_DISPLACE || decoded->src.addr_mode == MODE_PC_INDEX_DISP8) {
 							//adjust displacement to account for extra instruction word
@@ -516,7 +690,7 @@
 						istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst));
 						if (!istream) {
 							decoded->op = M68K_INVALID;
-							return start+1;
+							break;
 						}
 					}
 				} else {
@@ -536,7 +710,7 @@
 						istream= m68k_decode_op(istream, size, &(decoded->dst));
 						if (!istream) {
 							decoded->op = M68K_INVALID;
-							return start+1;
+							break;
 						}
 						break;
 					case 1:
@@ -546,7 +720,7 @@
 							decoded->op = M68K_MOVE_FROM_CCR;
 							size = OPSIZE_WORD;
 #else
-							return istream+1;
+							break;
 #endif
 						} else {
 							decoded->op = M68K_CLR;
@@ -555,7 +729,7 @@
 						istream= m68k_decode_op(istream, size, &(decoded->dst));
 						if (!istream) {
 							decoded->op = M68K_INVALID;
-							return start+1;
+							break;
 						}
 						break;
 					case 2:
@@ -566,14 +740,14 @@
 							istream= m68k_decode_op(istream, size, &(decoded->src));
 							if (!istream) {
 								decoded->op = M68K_INVALID;
-								return start+1;
+								break;
 							}
 						} else {
 							decoded->op = M68K_NEG;
 							istream= m68k_decode_op(istream, size, &(decoded->dst));
 							if (!istream) {
 								decoded->op = M68K_INVALID;
-								return start+1;
+								break;
 							}
 						}
 						decoded->extra.size = size;
@@ -586,14 +760,14 @@
 							istream= m68k_decode_op(istream, size, &(decoded->src));
 							if (!istream) {
 								decoded->op = M68K_INVALID;
-								return start+1;
+								break;
 							}
 						} else {
 							decoded->op = M68K_NOT;
 							istream= m68k_decode_op(istream, size, &(decoded->dst));
 							if (!istream) {
 								decoded->op = M68K_INVALID;
-								return start+1;
+								break;
 							}
 						}
 						decoded->extra.size = size;
@@ -648,7 +822,7 @@
 								istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst));
 								if (!istream) {
 									decoded->op = M68K_INVALID;
-									return start+1;
+									break;
 								}
 							} else if((*istream & 0x1C0) == 0x40) {
 								decoded->op = M68K_PEA;
@@ -656,7 +830,7 @@
 								istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src));
 								if (!istream) {
 									decoded->op = M68K_INVALID;
-									return start+1;
+									break;
 								}
 							}
 						}
@@ -678,7 +852,7 @@
 								istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 								if (!istream) {
 									decoded->op = M68K_INVALID;
-									return start+1;
+									break;
 								}
 							}
 						}
@@ -702,7 +876,7 @@
 							istream = m68k_decode_op(istream, OPSIZE_UNSIZED, &(decoded->src));
 							if (!istream) {
 								decoded->op = M68K_INVALID;
-								return start+1;
+								break;
 							}
 						} else {
 							//it would appear bit 6 needs to be set for it to be a valid instruction here
@@ -783,6 +957,33 @@
 							case 7:
 								//MOVEC
 #ifdef M68010
+								decoded->op = M68K_MOVEC;
+								immed = *(++istream);
+								reg = immed >> 12 & 0x7;
+								opmode = immed & 0x8000 ? MODE_AREG : MODE_REG;
+								immed &= 0xFFF;
+								if (immed & 0x800) {
+									if (immed > MAX_HIGH_CR) {
+										decoded->op = M68K_INVALID;
+										break;
+									} else {
+										immed = immed - 0x800 + CR_USP;
+									}
+								} else {
+									if (immed > MAX_LOW_CR) {
+										decoded->op = M68K_INVALID;
+										break;
+									}
+								}
+								if (*start & 1) {
+									decoded->src.addr_mode = opmode;
+									decoded->src.params.regs.pri = reg;
+									decoded->dst.params.immed = immed;
+								} else {
+									decoded->dst.addr_mode = opmode;
+									decoded->dst.params.regs.pri = reg;
+									decoded->src.params.immed = immed;
+								}
 #endif
 								break;
 							}
@@ -816,7 +1017,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			}
 		} else {
@@ -837,7 +1038,7 @@
 			istream = m68k_decode_op(istream, size, &(decoded->dst));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		}
 		break;
@@ -865,7 +1066,7 @@
 	case MOVEQ:
 		if (*istream & 0x100) {
 			decoded->op = M68K_INVALID;
-			return start+1;
+			break;
 		}
 		decoded->op = M68K_MOVE;
 		decoded->variant = VAR_QUICK;
@@ -891,7 +1092,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src));
 				if (!istream || decoded->src.addr_mode == MODE_AREG) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			case 4:
@@ -917,7 +1118,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src));
 				if (!istream || decoded->src.addr_mode == MODE_AREG) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				break;
 			}
@@ -930,7 +1131,7 @@
 				istream = m68k_decode_op(istream, size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			} else {
 				decoded->dst.addr_mode = MODE_REG;
@@ -938,7 +1139,7 @@
 				istream = m68k_decode_op(istream, size, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			}
 		}
@@ -957,7 +1158,7 @@
 					istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				} else {
 					decoded->extra.size = size;
@@ -966,7 +1167,7 @@
 					istream = m68k_decode_op(istream, size, &(decoded->dst));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				}
 			} else {
@@ -994,7 +1195,7 @@
 			istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		}
 		break;
@@ -1012,14 +1213,14 @@
 				istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			} else {
 				reg = m68k_reg_quick_field(*istream);
 				istream = m68k_decode_op(istream, size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 				decoded->extra.size = size;
 				if (decoded->dst.addr_mode == MODE_AREG) {
@@ -1047,7 +1248,7 @@
 			istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		}
 		break;
@@ -1070,7 +1271,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			} else if(!(*istream & 0xF0)) {
 				decoded->op = M68K_ABCD;
@@ -1101,7 +1302,7 @@
 				istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			}
 		} else {
@@ -1113,7 +1314,7 @@
 				istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			} else {
 				decoded->op = M68K_AND;
@@ -1123,7 +1324,7 @@
 				istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
-					return start+1;
+					break;
 				}
 			}
 		}
@@ -1142,7 +1343,7 @@
 					istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				} else {
 					decoded->extra.size = size;
@@ -1151,7 +1352,7 @@
 					istream = m68k_decode_op(istream, size, &(decoded->dst));
 					if (!istream) {
 						decoded->op = M68K_INVALID;
-						return start+1;
+						break;
 					}
 				}
 			} else {
@@ -1179,7 +1380,7 @@
 			istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		}
 		break;
@@ -1216,7 +1417,7 @@
 			istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->dst));
 			if (!istream) {
 				decoded->op = M68K_INVALID;
-				return start+1;
+				break;
 			}
 		} else if((*istream & 0xC0) != 0xC0) {
 			switch(((*istream >> 2) & 0x6) | ((*istream >> 8) & 1))
@@ -1264,6 +1465,56 @@
 		} else {
 #ifdef M68020
 			//TODO: Implement bitfield instructions for M68020+ support
+			switch (*istream >> 8 & 7)
+			{
+			case 0:
+				decoded->op = M68K_BFTST; //<ea>
+				break;
+			case 1:
+				decoded->op = M68K_BFEXTU; //<ea>, Dn
+				break;
+			case 2:
+				decoded->op = M68K_BFCHG; //<ea>
+				break;
+			case 3:
+				decoded->op = M68K_BFEXTS; //<ea>, Dn
+				break;
+			case 4:
+				decoded->op = M68K_BFCLR; //<ea>
+				break;
+			case 5:
+				decoded->op = M68K_BFFFO; //<ea>, Dn
+				break;
+			case 6:
+				decoded->op = M68K_BFSET; //<ea>
+				break;
+			case 7:
+				decoded->op = M68K_BFINS; //Dn, <ea>
+				break;
+			}
+			opmode = *istream >> 3 & 0x7;
+			reg = *istream & 0x7;
+			m68k_op_info *ea, *other;
+			if (decoded->op == M68K_BFEXTU || decoded->op == M68K_BFEXTS || decoded->op == M68K_BFFFO)
+			{
+				ea = &(decoded->src);
+				other = &(decoded->dst);
+			} else {
+				ea = &(decoded->dst);
+				other = &(decoded->dst);
+			}
+			if (*istream & 0x100)
+			{
+				immed = *(istream++);
+				other->addr_mode = MODE_REG;
+				other->params.regs.pri = immed >> 12 & 0x7;
+			} else {
+				immed = *(istream++);
+			}
+			decoded->extra.size = OPSIZE_UNSIZED;
+			istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, ea);
+			ea->addr_mode |= M68K_FLAG_BITFIELD;
+			ea->bitfield = immed & 0xFFF;
 #endif
 		}
 		break;
@@ -1271,6 +1522,10 @@
 		//TODO: Implement me
 		break;
 	}
+	if (decoded->op == M68K_INVALID) {
+		decoded->src.params.immed = *start;
+		return start + 1;
+	}
 	return istream+1;
 }
 
@@ -1417,7 +1672,43 @@
 	"trapv",
 	"tst",
 	"unlk",
-	"invalid"
+	"invalid",
+#ifdef M68010
+	"bkpt",
+	"move", //from ccr
+	"movec",
+	"moves",
+	"rtd",
+#endif
+#ifdef M68020
+	"bfchg",
+	"bfclr",
+	"bfexts",
+	"bfextu",
+	"bfffo",
+	"bfins",
+	"bfset",
+	"bftst",
+	"callm",
+	"cas",
+	"cas2",
+	"chk2",
+	"cmp2",
+	"cpbcc",
+	"cpdbcc",
+	"cpgen",
+	"cprestore",
+	"cpsave",
+	"cpscc",
+	"cptrapcc",
+	"divsl",
+	"divul",
+	"extb",
+	"pack",
+	"rtm",
+	"trapcc",
+	"unpk"
+#endif
 };
 
 char * cond_mnem[] = {
@@ -1438,55 +1729,651 @@
 	"gt",
 	"le"
 };
+#ifdef M68010
+char * cr_mnem[] = {
+	"SFC",
+	"DFC",
+#ifdef M68020
+	"CACR",
+#endif
+	"USP",
+	"VBR",
+#ifdef M68020
+	"CAAR",
+	"MSP",
+	"ISP"
+#endif
+};
+#endif
 
-int m68k_disasm_op(m68k_op_info *decoded, char *dst, int need_comma, uint8_t labels, uint32_t address)
+int m68k_disasm_op(m68k_op_info *decoded, char *dst, int need_comma, uint8_t labels, uint32_t address, format_label_fun label_fun, void * data)
 {
 	char * c = need_comma ? "," : "";
-	switch(decoded->addr_mode)
+	int ret = 0;
+#ifdef M68020
+	uint8_t addr_mode = decoded->addr_mode & (~M68K_FLAG_BITFIELD);
+#else
+	uint8_t addr_mode = decoded->addr_mode;
+#endif
+	switch(addr_mode)
 	{
 	case MODE_REG:
-		return sprintf(dst, "%s d%d", c, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s d%d", c, decoded->params.regs.pri);
+		break;
 	case MODE_AREG:
-		return sprintf(dst, "%s a%d", c, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s a%d", c, decoded->params.regs.pri);
+		break;
 	case MODE_AREG_INDIRECT:
-		return sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri);
+		break;
 	case MODE_AREG_POSTINC:
-		return sprintf(dst, "%s (a%d)+", c, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s (a%d)+", c, decoded->params.regs.pri);
+		break;
 	case MODE_AREG_PREDEC:
-		return sprintf(dst, "%s -(a%d)", c, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s -(a%d)", c, decoded->params.regs.pri);
+		break;
 	case MODE_AREG_DISPLACE:
-		return sprintf(dst, "%s (%d, a%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri);
+		ret = sprintf(dst, "%s (%d, a%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri);
+		break;
 	case MODE_AREG_INDEX_DISP8:
-		return sprintf(dst, "%s (%d, a%d, %c%d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w');
+#ifdef M68020
+		if (decoded->params.regs.scale)
+		{
+			ret = sprintf(dst, "%s (%d, a%d, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+#endif
+			ret = sprintf(dst, "%s (%d, a%d, %c%d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w');
+#ifdef M68020
+		}
+#endif
+		break;
+#ifdef M68020
+	case MODE_AREG_INDEX_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, a%d, %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+			ret = sprintf(dst, "%s (a%d, %c%d.%c*%d)", c, decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		}
+		break;
+	case MODE_AREG_PREINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([a%d, %c%d.%c*%d])", c, decoded->params.regs.pri,
+						  (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+						  (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, a%d, %c%d.%c*%d])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([a%d, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, a%d, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_AREG_POSTINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([a%d], %c%d.%c*%d)", c, decoded->params.regs.pri,
+						  (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+						  (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, a%d], %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([a%d], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, a%d], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_AREG_MEM_INDIRECT:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([a%d])", c, decoded->params.regs.pri);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, a%d])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([a%d], %d.%c)", c, decoded->params.regs.pri, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, a%d], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_AREG_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, a%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', decoded->params.regs.pri);
+		} else {
+			//this is a lossy representation of the encoded instruction
+			//not sure if there's a better way to print it though
+			ret = sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri);
+		}
+		break;
+	case MODE_INDEX_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+			ret = sprintf(dst, "%s (%c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+		}
+		break;
+	case MODE_PREINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([%c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, %c%d.%c*%d])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([%c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_POSTINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c], %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_MEM_INDIRECT:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([])", c);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([], %d.%c)", c, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+		} else {
+			ret = sprintf(dst, "%s ()", c);
+		}
+		break;
+#endif
 	case MODE_IMMEDIATE:
 	case MODE_IMMEDIATE_WORD:
-		return sprintf(dst, (decoded->params.immed <= 128 ? "%s #%d" : "%s #$%X"), c, decoded->params.immed);
+		ret = sprintf(dst, (decoded->params.immed <= 128 ? "%s #%d" : "%s #$%X"), c, decoded->params.immed);
+		break;
 	case MODE_ABSOLUTE_SHORT:
 		if (labels) {
-			return sprintf(dst, "%s ADR_%X.w", c, decoded->params.immed);
+			ret = sprintf(dst, "%s ", c);
+			ret += label_fun(dst+ret, decoded->params.immed, data);
+			strcat(dst+ret, ".w");
+			ret = ret + 2;
 		} else {
-			return sprintf(dst, "%s $%X.w", c, decoded->params.immed);
+			ret = sprintf(dst, "%s $%X.w", c, decoded->params.immed);
 		}
+		break;
 	case MODE_ABSOLUTE:
 		if (labels) {
-			return sprintf(dst, "%s ADR_%X.l", c, decoded->params.immed);
+			ret = sprintf(dst, "%s ", c);
+			ret += label_fun(dst+ret, decoded->params.immed, data);
+			strcat(dst+ret, ".l");
+			ret = ret + 2;
 		} else {
-			return sprintf(dst, "%s $%X", c, decoded->params.immed);
+			ret = sprintf(dst, "%s $%X", c, decoded->params.immed);
 		}
+		break;
 	case MODE_PC_DISPLACE:
 		if (labels) {
-			return sprintf(dst, "%s ADR_%X(pc)", c, address + 2 + decoded->params.regs.displacement);
+			ret = sprintf(dst, "%s ", c);
+			ret += label_fun(dst+ret, address + 2 + decoded->params.regs.displacement, data);
+			strcat(dst+ret, "(pc)");
+			ret = ret + 4;
 		} else {
-			return sprintf(dst, "%s (%d, pc)", c, decoded->params.regs.displacement);
+			ret = sprintf(dst, "%s (%d, pc)", c, decoded->params.regs.displacement);
 		}
+		break;
 	case MODE_PC_INDEX_DISP8:
-		return sprintf(dst, "%s (%d, pc, %c%d.%c)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w');
+#ifdef M68020
+		if (decoded->params.regs.scale)
+		{
+			ret = sprintf(dst, "%s (%d, pc, %c%d.%c*%d)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+#endif
+			ret = sprintf(dst, "%s (%d, pc, %c%d.%c)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w');
+#ifdef M68020
+		}
+#endif
+		break;
+#ifdef M68020
+	case MODE_PC_INDEX_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, pc, %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+			ret = sprintf(dst, "%s (pc, %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+		}
+		break;
+	case MODE_PC_PREINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([pc, %c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, pc, %c%d.%c*%d])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([pc, %c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, pc, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_PC_POSTINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([pc], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, pc], %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([pc], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, pc], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_PC_MEM_INDIRECT:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([pc])", c);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, pc])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([pc], %d.%c)", c, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, pc], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_PC_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, pc)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+		} else {
+			ret = sprintf(dst, "%s (pc)", c);
+		}
+		break;
+	case MODE_ZPC_INDEX_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, zpc, %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+		} else {
+			ret = sprintf(dst, "%s (zpc, %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+		}
+		break;
+	case MODE_ZPC_PREINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([zpc, %c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, zpc, %c%d.%c*%d])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([zpc, %c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, zpc, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_ZPC_POSTINDEX:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([zpc], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, zpc], %c%d.%c*%d)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale);
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([zpc], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd',
+			              (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w',
+			              1 << decoded->params.regs.scale, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, zpc], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l',
+			              (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7,
+			              (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale,
+			              decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_ZPC_MEM_INDIRECT:
+		switch (decoded->params.regs.disp_sizes)
+		{
+		case 0x11:
+			//no base displacement or outer displacement
+			ret = sprintf(dst, "%s ([zpc])", c);
+			break;
+		case 0x12:
+		case 0x13:
+			//base displacement only
+			ret = sprintf(dst, "%s ([%d.%c, zpc])", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+			break;
+		case 0x21:
+		case 0x31:
+			//outer displacement only
+			ret = sprintf(dst, "%s ([zpc], %d.%c)", c, decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		case 0x22:
+		case 0x23:
+		case 0x32:
+		case 0x33:
+			//both outer and inner displacement
+			ret = sprintf(dst, "%s ([%d.%c, zpc], %d.%c)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp,
+			              decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l');
+			break;
+		}
+		break;
+	case MODE_ZPC_BASE_DISP:
+		if (decoded->params.regs.disp_sizes > 1)
+		{
+			ret = sprintf(dst, "%s (%d.%c, zpc)", c, decoded->params.regs.displacement,
+			              decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l');
+		} else {
+			ret = sprintf(dst, "%s (zpc)", c);
+		}
+		break;
+#endif
 	default:
-		return 0;
+		ret = 0;
 	}
+#ifdef M68020
+	if (decoded->addr_mode & M68K_FLAG_BITFIELD)
+	{
+		switch (decoded->bitfield & 0x820)
+		{
+		case 0:
+			return ret + sprintf(dst+ret, " {$%X:%d}", decoded->bitfield >> 6 & 0x1F, decoded->bitfield & 0x1F ? decoded->bitfield & 0x1F : 32);
+		case 0x20:
+			return ret + sprintf(dst+ret, " {$%X:d%d}", decoded->bitfield >> 6 & 0x1F, decoded->bitfield & 0x7);
+		case 0x800:
+			return ret + sprintf(dst+ret, " {d%d:%d}", decoded->bitfield >> 6 & 0x7, decoded->bitfield & 0x1F ? decoded->bitfield & 0x1F : 32);
+		case 0x820:
+			return ret + sprintf(dst+ret, " {d%d:d%d}", decoded->bitfield >> 6 & 0x7, decoded->bitfield & 0x7);
+		}
+	}
+#endif
+	return ret;
 }
 
-int m68k_disasm_movem_op(m68k_op_info *decoded, m68k_op_info *other, char *dst, int need_comma, uint8_t labels, uint32_t address)
+int m68k_disasm_movem_op(m68k_op_info *decoded, m68k_op_info *other, char *dst, int need_comma, uint8_t labels, uint32_t address, format_label_fun label_fun, void * data)
 {
 	int8_t dir, reg, bit, regnum, last=-1, lastreg, first=-1;
 	char *rtype, *last_rtype;
@@ -1540,11 +2427,16 @@
 		}
 		return oplen;
 	} else {
-		return m68k_disasm_op(decoded, dst, need_comma, labels, address);
+		return m68k_disasm_op(decoded, dst, need_comma, labels, address, label_fun, data);
 	}
 }
 
-int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels)
+int m68k_default_label_fun(char * dst, uint32_t address, void * data)
+{
+	return sprintf(dst, "ADR_%X", address);
+}
+
+int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels, format_label_fun label_fun, void * data)
 {
 	int ret,op1len;
 	uint8_t size;
@@ -1562,9 +2454,11 @@
 		if (decoded->op != M68K_SCC) {
 			if (labels) {
 				if (decoded->op == M68K_DBCC) {
-					ret += sprintf(dst+ret, " d%d, ADR_%X", decoded->dst.params.regs.pri, decoded->address + 2 + decoded->src.params.immed);
+					ret += sprintf(dst+ret, " d%d, ", decoded->dst.params.regs.pri);
+					ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data);
 				} else {
-					ret += sprintf(dst+ret, " ADR_%X", decoded->address + 2 + decoded->src.params.immed);
+					dst[ret++] = ' ';
+					ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data);
 				}
 			} else {
 				if (decoded->op == M68K_DBCC) {
@@ -1578,8 +2472,8 @@
 		break;
 	case M68K_BSR:
 		if (labels) {
-			ret = sprintf(dst, "bsr%s ADR_%X", decoded->variant == VAR_BYTE ? ".s" : "",
-			decoded->address + 2 + decoded->src.params.immed);
+			ret = sprintf(dst, "bsr%s ", decoded->variant == VAR_BYTE ? ".s" : "");
+			ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data);
 		} else {
 			ret = sprintf(dst, "bsr%s #%d <%X>", decoded->variant == VAR_BYTE ? ".s" : "", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed);
 		}
@@ -1587,7 +2481,7 @@
 	case M68K_MOVE_FROM_SR:
 		ret = sprintf(dst, "%s", mnemonics[decoded->op]);
 		ret += sprintf(dst + ret, " SR");
-		ret += m68k_disasm_op(&(decoded->dst), dst + ret, 1, labels, decoded->address);
+		ret += m68k_disasm_op(&(decoded->dst), dst + ret, 1, labels, decoded->address, label_fun, data);
 		return ret;
 	case M68K_ANDI_SR:
 	case M68K_EORI_SR:
@@ -1599,19 +2493,34 @@
 	case M68K_MOVE_CCR:
 	case M68K_ORI_CCR:
 		ret = sprintf(dst, "%s", mnemonics[decoded->op]);
-		ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address);
+		ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data);
 		ret += sprintf(dst + ret, ", %s", special_op);
 		return ret;
 	case M68K_MOVE_USP:
 		ret = sprintf(dst, "%s", mnemonics[decoded->op]);
 		if (decoded->src.addr_mode != MODE_UNUSED) {
-			ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address);
+			ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data);
 			ret += sprintf(dst + ret, ", USP");
 		} else {
 			ret += sprintf(dst + ret, "USP, ");
-			ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address);
+			ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data);
 		}
 		return ret;
+	case M68K_INVALID:
+		ret = sprintf(dst, "dc.w $%X", decoded->src.params.immed);
+		return ret;
+#ifdef M68010
+	case M68K_MOVEC:
+		ret = sprintf(dst, "%s ", mnemonics[decoded->op]);
+		if (decoded->src.addr_mode == MODE_UNUSED) {
+			ret += sprintf(dst + ret, "%s, ", cr_mnem[decoded->src.params.immed]);
+			ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data);
+		} else {
+			ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data);
+			ret += sprintf(dst + ret, ", %s", cr_mnem[decoded->dst.params.immed]);
+		}
+		return ret;
+#endif
 	default:
 		size = decoded->extra.size;
 		ret = sprintf(dst, "%s%s%s",
@@ -1620,23 +2529,27 @@
 				size == OPSIZE_BYTE ? ".b" : (size == OPSIZE_WORD ? ".w" : (size == OPSIZE_LONG ? ".l" : "")));
 	}
 	if (decoded->op == M68K_MOVEM) {
-		op1len = m68k_disasm_movem_op(&(decoded->src), &(decoded->dst), dst + ret, 0, labels, decoded->address);
+		op1len = m68k_disasm_movem_op(&(decoded->src), &(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data);
 		ret += op1len;
-		ret += m68k_disasm_movem_op(&(decoded->dst), &(decoded->src), dst + ret, op1len, labels, decoded->address);
+		ret += m68k_disasm_movem_op(&(decoded->dst), &(decoded->src), dst + ret, op1len, labels, decoded->address, label_fun, data);
 	} else {
-		op1len = m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address);
+		op1len = m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data);
 		ret += op1len;
-		ret += m68k_disasm_op(&(decoded->dst), dst + ret, op1len, labels, decoded->address);
+		ret += m68k_disasm_op(&(decoded->dst), dst + ret, op1len, labels, decoded->address, label_fun, data);
 	}
 	return ret;
 }
 
 int m68k_disasm(m68kinst * decoded, char * dst)
 {
-	return m68k_disasm_ex(decoded, dst, 0);
+	return m68k_disasm_ex(decoded, dst, 0, NULL, NULL);
 }
 
-int m68k_disasm_labels(m68kinst * decoded, char * dst)
+int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data)
 {
-	return m68k_disasm_ex(decoded, dst, 1);
+	if (!label_fun)
+	{
+		label_fun = m68k_default_label_fun;
+	}
+	return m68k_disasm_ex(decoded, dst, 1, label_fun, data);
 }
--- a/68kinst.h	Mon Dec 29 23:08:39 2014 -0800
+++ b/68kinst.h	Tue Dec 30 19:11:34 2014 -0800
@@ -8,6 +8,13 @@
 
 #include <stdint.h>
 
+#ifdef M68030
+#define M68020
+#endif
+#ifdef M68020
+#define M68010
+#endif
+
 typedef enum {
 	BIT_MOVEP_IMMED = 0,
 	MOVE_BYTE,
@@ -97,7 +104,43 @@
 	M68K_TRAPV,
 	M68K_TST,
 	M68K_UNLK,
-	M68K_INVALID
+	M68K_INVALID,
+#ifdef M68010
+	M68K_BKPT,
+	M68K_MOVE_FROM_CCR,
+	M68K_MOVEC,
+	M68K_MOVES,
+	M68K_RTD,
+#endif
+#ifdef M68020
+	M68K_BFCHG,
+	M68K_BFCLR,
+	M68K_BFEXTS,
+	M68K_BFEXTU,
+	M68K_BFFFO,
+	M68K_BFINS,
+	M68K_BFSET,
+	M68K_BFTST,
+	M68K_CALLM,
+	M68K_CAS,
+	M68K_CAS2,
+	M68K_CHK2,
+	M68K_CMP2,
+	M68K_CP_BCC,
+	M68K_CP_DBCC,
+	M68K_CP_GEN,
+	M68K_CP_RESTORE,
+	M68K_CP_SAVE,
+	M68K_CP_SCC,
+	M68K_CP_TRAPCC,
+	M68K_DIVSL,
+	M68K_DIVUL,
+	M68K_EXTB,
+	M68K_PACK,
+	M68K_RTM,
+	M68K_TRAPCC,
+	M68K_UNPK,
+#endif
 } m68K_op;
 
 typedef enum {
@@ -130,19 +173,40 @@
 //expanded values
 	MODE_AREG_INDEX_DISP8,
 #ifdef M68020
-	MODE_AREG_INDEX_DISP32,
+	MODE_AREG_INDEX_BASE_DISP,
+	MODE_AREG_PREINDEX,
+	MODE_AREG_POSTINDEX,
+	MODE_AREG_MEM_INDIRECT,
+	MODE_AREG_BASE_DISP,
+	MODE_INDEX_BASE_DISP,
+	MODE_PREINDEX,
+	MODE_POSTINDEX,
+	MODE_MEM_INDIRECT,
+	MODE_BASE_DISP,
 #endif
 	MODE_ABSOLUTE_SHORT,
 	MODE_ABSOLUTE,
 	MODE_PC_DISPLACE,
 	MODE_PC_INDEX_DISP8,
 #ifdef M68020
-	MODE_PC_INDEX_DISP32,
+	MODE_PC_INDEX_BASE_DISP,
+	MODE_PC_PREINDEX,
+	MODE_PC_POSTINDEX,
+	MODE_PC_MEM_INDIRECT,
+	MODE_PC_BASE_DISP,
+	MODE_ZPC_INDEX_BASE_DISP,
+	MODE_ZPC_PREINDEX,
+	MODE_ZPC_POSTINDEX,
+	MODE_ZPC_MEM_INDIRECT,
+	MODE_ZPC_BASE_DISP,
 #endif
 	MODE_IMMEDIATE,
 	MODE_IMMEDIATE_WORD,//used to indicate an immediate operand that only uses a single extension word even for a long operation
 	MODE_UNUSED
 } m68k_addr_modes;
+#ifdef M68020
+#define M68K_FLAG_BITFIELD 0x80
+#endif
 
 typedef enum {
 	COND_TRUE,
@@ -163,13 +227,49 @@
 	COND_LESS_EQ
 } m68K_condition;
 
+#ifdef M68010
+typedef enum {
+	CR_SFC,
+	CR_DFC,
+#ifdef M68020
+	CR_CACR,
+#endif
+	CR_USP,
+	CR_VBR,
+#ifdef M68020
+	CR_CAAR,
+	CR_MSP,
+	CR_ISP
+#endif
+} m68k_control_reg;
+
+#ifdef M68020
+#define MAX_HIGH_CR 0x804
+#define MAX_LOW_CR 0x002
+#else
+#define MAX_HIGH_CR 0x801
+#define MAX_LOW_CR 0x001
+#endif
+
+#endif
+
 typedef struct {
-	uint8_t addr_mode;
+#ifdef M68020
+	uint16_t bitfield;
+#endif
+	uint8_t  addr_mode;
 	union {
 		struct {
 			uint8_t pri;
 			uint8_t sec;
+#ifdef M68020
+			uint8_t scale;
+			uint8_t disp_sizes;
+#endif
 			int32_t displacement;
+#ifdef M68020
+			int32_t outer_disp;
+#endif
 		} regs;
 		uint32_t immed;
 	} params;
@@ -229,12 +329,15 @@
 	VECTOR_TRAP_15
 } m68k_vector;
 
+typedef int (*format_label_fun)(char * dst, uint32_t address, void * data);
+
 uint16_t * m68k_decode(uint16_t * istream, m68kinst * dst, uint32_t address);
 uint32_t m68k_branch_target(m68kinst * inst, uint32_t *dregs, uint32_t *aregs);
 uint8_t m68k_is_branch(m68kinst * inst);
 uint8_t m68k_is_noncall_branch(m68kinst * inst);
 int m68k_disasm(m68kinst * decoded, char * dst);
-int m68k_disasm_labels(m68kinst * decoded, char * dst);
+int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data);
+int m68k_default_label_fun(char * dst, uint32_t address, void * data);
 
 #endif
 
--- a/Makefile	Mon Dec 29 23:08:39 2014 -0800
+++ b/Makefile	Tue Dec 30 19:11:34 2014 -0800
@@ -19,6 +19,16 @@
 CFLAGS+= -DDISABLE_OPENGL
 endif
 
+ifdef M68030
+CFLAGS+= -DM68030
+endif
+ifdef M68020
+CFLAGS+= -DM68020
+endif
+ifdef M68010
+CFLAGS+= -DM68010
+endif
+
 ifndef CPU
 CPU:=$(shell uname -m)
 endif
@@ -64,8 +74,8 @@
 blastem : $(MAINOBJS)
 	$(CC) -o blastem $(MAINOBJS) $(LDFLAGS)
 
-dis : dis.o 68kinst.o
-	$(CC) -o dis dis.o 68kinst.o
+dis : dis.o 68kinst.o tern.o vos_program_module.o
+	$(CC) -o dis dis.o 68kinst.o tern.o vos_program_module.o
 
 zdis : zdis.o z80inst.o
 	$(CC) -o zdis zdis.o z80inst.o
@@ -106,6 +116,9 @@
 offsets : offsets.c z80_to_x86.h m68k_core.h
 	$(CC) -o offsets offsets.c
 
+vos_prog_info : vos_prog_info.o vos_program_module.o
+	$(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o
+
 %.o : %.S
 	$(CC) -c -o $@ $<
 
@@ -113,7 +126,7 @@
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 %.bin : %.s68
-	vasmm68k_mot -Fbin -m68000 -no-opt -spaces -o $@ $<
+	vasmm68k_mot -Fbin -m68000 -no-opt -spaces -o $@ -L $@.list $<
 
 %.bin : %.sz8
 	vasmz80_mot -Fbin -spaces -o $@ $<
--- a/blastem.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/blastem.c	Tue Dec 30 19:11:34 2014 -0800
@@ -33,7 +33,7 @@
 
 #define MAX_SOUND_CYCLES 100000
 
-uint32_t mclks_per_frame = MCLKS_LINE*LINES_NTSC;
+uint32_t mclk_target = 0;
 
 uint16_t cart[CARTRIDGE_WORDS];
 uint16_t ram[RAM_WORDS];
@@ -191,15 +191,19 @@
 				z80_reset(z_context);
 				need_reset = 0;
 			}
-			uint32_t vint_cycle = vdp_next_vint_z80(gen->vdp) / MCLKS_PER_Z80;
+
 			while (z_context->current_cycle < z_context->sync_cycle) {
-				if (z_context->iff1 && z_context->current_cycle < (vint_cycle + Z80_VINT_DURATION)) {
-					z_context->int_cycle = vint_cycle < z_context->int_enable_cycle ? z_context->int_enable_cycle : vint_cycle;
+				if (z_context->int_pulse_end < z_context->current_cycle || z_context->int_pulse_end == CYCLE_NEVER) {
+					z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp) / MCLKS_PER_Z80;
+					z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION;
+				}
+				if (z_context->iff1) {
+					z_context->int_cycle = z_context->int_pulse_start < z_context->int_enable_cycle ? z_context->int_enable_cycle : z_context->int_pulse_start;
 				} else {
 					z_context->int_cycle = CYCLE_NEVER;
 				}
 				z_context->target_cycle = z_context->sync_cycle < z_context->int_cycle ? z_context->sync_cycle : z_context->int_cycle;
-				dprintf("Running Z80 from cycle %d to cycle %d. Native PC: %p\n", z_context->current_cycle, z_context->sync_cycle, z_context->native_pc);
+				dprintf("Running Z80 from cycle %d to cycle %d. Int cycle: %d\n", z_context->current_cycle, z_context->sync_cycle, z_context->int_cycle);
 				z_context->run(z_context);
 				dprintf("Z80 ran to cycle %d\n", z_context->current_cycle);
 			}
@@ -236,15 +240,15 @@
 	z80_context * z_context = gen->z80;
 	uint32_t mclks = context->current_cycle * MCLKS_PER_68K;
 	sync_z80(z_context, mclks);
-	if (mclks >= mclks_per_frame) {
+	if (mclks >= mclk_target) {
 		sync_sound(gen, mclks);
-		gen->ym->current_cycle -= mclks_per_frame;
-		gen->psg->cycles -= mclks_per_frame;
+		gen->ym->current_cycle -= mclk_target;
+		gen->psg->cycles -= mclk_target;
 		if (gen->ym->write_cycle != CYCLE_NEVER) {
-			gen->ym->write_cycle = gen->ym->write_cycle >= mclks_per_frame/MCLKS_PER_68K ? gen->ym->write_cycle - mclks_per_frame/MCLKS_PER_68K : 0;
+			gen->ym->write_cycle = gen->ym->write_cycle >= mclk_target/MCLKS_PER_68K ? gen->ym->write_cycle - mclk_target/MCLKS_PER_68K : 0;
 		}
 		//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks);
-		vdp_run_context(v_context, mclks_per_frame);
+		vdp_run_context(v_context, mclk_target);
 
 		if (!headless) {
 			break_on_sync |= wait_render_frame(v_context, frame_limit);
@@ -255,28 +259,54 @@
 			}
 		}
 		frame++;
-		mclks -= mclks_per_frame;
-		vdp_adjust_cycles(v_context, mclks_per_frame);
-		io_adjust_cycles(gen->ports, context->current_cycle, mclks_per_frame/MCLKS_PER_68K);
-		io_adjust_cycles(gen->ports+1, context->current_cycle, mclks_per_frame/MCLKS_PER_68K);
-		io_adjust_cycles(gen->ports+2, context->current_cycle, mclks_per_frame/MCLKS_PER_68K);
+		mclks -= mclk_target;
+		vdp_adjust_cycles(v_context, mclk_target);
+		io_adjust_cycles(gen->ports, context->current_cycle, mclk_target/MCLKS_PER_68K);
+		io_adjust_cycles(gen->ports+1, context->current_cycle, mclk_target/MCLKS_PER_68K);
+		io_adjust_cycles(gen->ports+2, context->current_cycle, mclk_target/MCLKS_PER_68K);
 		if (busack_cycle != CYCLE_NEVER) {
-			if (busack_cycle > mclks_per_frame/MCLKS_PER_68K) {
-				busack_cycle -= mclks_per_frame/MCLKS_PER_68K;
+			if (busack_cycle > mclk_target/MCLKS_PER_68K) {
+				busack_cycle -= mclk_target/MCLKS_PER_68K;
 			} else {
 				busack_cycle = CYCLE_NEVER;
 				busack = new_busack;
 			}
 		}
-		context->current_cycle -= mclks_per_frame/MCLKS_PER_68K;
-		if (z_context->current_cycle >= mclks_per_frame/MCLKS_PER_Z80) {
-			z_context->current_cycle -= mclks_per_frame/MCLKS_PER_Z80;
+		context->current_cycle -= mclk_target/MCLKS_PER_68K;
+		if (z_context->current_cycle >= mclk_target/MCLKS_PER_Z80) {
+			z_context->current_cycle -= mclk_target/MCLKS_PER_Z80;
 		} else {
 			z_context->current_cycle = 0;
 		}
+		if (z_context->int_cycle != CYCLE_NEVER) {
+			if (z_context->int_cycle >= mclk_target/MCLKS_PER_Z80) {
+				z_context->int_cycle -= mclk_target/MCLKS_PER_Z80;
+			} else {
+				z_context->int_cycle = 0;
+			}
+		}
+		if (z_context->int_pulse_start != CYCLE_NEVER) {
+			if (z_context->int_pulse_end >= mclk_target/MCLKS_PER_Z80) {
+				z_context->int_pulse_end -= mclk_target/MCLKS_PER_Z80;
+				if (z_context->int_pulse_start >= mclk_target/MCLKS_PER_Z80) {
+					z_context->int_pulse_start -= mclk_target/MCLKS_PER_Z80;
+				} else {
+					z_context->int_pulse_start = 0;
+				}
+			}
+		} else {
+			z_context->int_pulse_start = CYCLE_NEVER;
+			z_context->int_pulse_end = CYCLE_NEVER;
+		}
+		if (z_context->int_enable_cycle >= mclk_target/MCLKS_PER_Z80) {
+			z_context->int_enable_cycle -= mclk_target/MCLKS_PER_Z80;
+		} else {
+			z_context->int_enable_cycle = 0;
+		}
 		if (mclks) {
 			vdp_run_context(v_context, mclks);
 		}
+		mclk_target = vdp_cycles_to_frame_end(v_context);
 	} else {
 		//printf("running VDP for %d cycles\n", mclks - v_context->cycles);
 		vdp_run_context(v_context, mclks);
@@ -322,10 +352,10 @@
 			gen->bus_busy = 1;
 			while (vdp_data_port_write(v_context, value) < 0) {
 				while(v_context->flags & FLAG_DMA_RUN) {
-					vdp_run_dma_done(v_context, mclks_per_frame);
-					if (v_context->cycles >= mclks_per_frame) {
+					vdp_run_dma_done(v_context, mclk_target);
+					if (v_context->cycles >= mclk_target) {
 						context->current_cycle = v_context->cycles / MCLKS_PER_68K;
-						if (context->current_cycle * MCLKS_PER_68K < mclks_per_frame) {
+						if (context->current_cycle * MCLKS_PER_68K < mclk_target) {
 							++context->current_cycle;
 						}
 						sync_components(context, 0);
@@ -339,10 +369,10 @@
 			if (blocked) {
 				while (blocked) {
 					while(v_context->flags & FLAG_DMA_RUN) {
-						vdp_run_dma_done(v_context, mclks_per_frame);
-						if (v_context->cycles >= mclks_per_frame) {
+						vdp_run_dma_done(v_context, mclk_target);
+						if (v_context->cycles >= mclk_target) {
 							context->current_cycle = v_context->cycles / MCLKS_PER_68K;
-							if (context->current_cycle * MCLKS_PER_68K < mclks_per_frame) {
+							if (context->current_cycle * MCLKS_PER_68K < mclk_target) {
 								++context->current_cycle;
 							}
 							sync_components(context, 0);
@@ -1045,7 +1075,7 @@
 	context.system = gen;
 	//cartridge ROM
 	context.mem_pointers[0] = cart;
-	context.target_cycle = context.sync_cycle = mclks_per_frame/MCLKS_PER_68K;
+	context.target_cycle = context.sync_cycle = mclk_target/MCLKS_PER_68K;
 	//work RAM
 	context.mem_pointers[1] = ram;
 	//save RAM/map
@@ -1300,7 +1330,6 @@
 	height = height < 240 ? (width/320) * 240 : height;
 	uint32_t fps = 60;
 	if (version_reg & 0x40) {
-		mclks_per_frame = MCLKS_LINE * LINES_PAL;
 		fps = 50;
 	}
 	if (!headless) {
@@ -1311,7 +1340,8 @@
 	memset(&gen, 0, sizeof(gen));
 	gen.master_clock = gen.normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL;
 
-	init_vdp_context(&v_context);
+	init_vdp_context(&v_context, version_reg & 0x40);
+	mclk_target = vdp_cycles_to_frame_end(&v_context);
 
 	ym2612_context y_context;
 	ym_init(&y_context, render_sample_rate(), gen.master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0);
@@ -1328,7 +1358,7 @@
 
 	z_context.system = &gen;
 	z_context.mem_pointers[0] = z80_ram;
-	z_context.sync_cycle = z_context.target_cycle = mclks_per_frame/MCLKS_PER_Z80;
+	z_context.sync_cycle = z_context.target_cycle = mclk_target/MCLKS_PER_Z80;
 	z_context.int_cycle = CYCLE_NEVER;
 	z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart;
 
@@ -1351,7 +1381,7 @@
 	if (i < 0) {
 		strcpy(sram_filename + fname_size, ".sram");
 	}
-	set_keybindings();
+	set_keybindings(gen.ports);
 
 	init_run_cpu(&gen, address_log, statefile, debuggerfun);
 	return 0;
--- a/debug.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/debug.c	Tue Dec 30 19:11:34 2014 -0800
@@ -632,9 +632,14 @@
 					}
 				} else if(param[0] == 'c') {
 					value = context->current_cycle;
-				} else if (param[0] == '0' && param[1] == 'x') {
-					uint32_t p_addr = strtol(param+2, NULL, 16);
-					value = read_dma_value(p_addr/2);
+				} else if ((param[0] == '0' && param[1] == 'x') || param[0] == '$') {
+					uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), NULL, 16);
+					if ((p_addr & 0xFFFFFF) == 0xC00004) {
+						genesis_context * gen = context->system;
+						value = vdp_hv_counter_read(gen->vdp);
+					} else {
+						value = read_dma_value(p_addr/2);
+					}
 				} else {
 					fprintf(stderr, "Unrecognized parameter to p: %s\n", param);
 					break;
--- a/default.cfg	Mon Dec 29 23:08:39 2014 -0800
+++ b/default.cfg	Tue Dec 30 19:11:34 2014 -0800
@@ -54,6 +54,13 @@
 	}
 }
 
+io {
+	devices {
+		1 gamepad6.1
+		2 gamepad6.2
+	}
+}
+
 video {
 	width 640
 	vertex_shader default.v.glsl
--- a/dis.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/dis.c	Tue Dec 30 19:11:34 2014 -0800
@@ -6,9 +6,12 @@
 #include "68kinst.h"
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include "vos_program_module.h"
+#include "tern.h"
 
 uint8_t visited[(16*1024*1024)/16];
-uint8_t label[(16*1024*1024)/8];
+uint16_t label[(16*1024*1024)/8];
 
 void visit(uint32_t address)
 {
@@ -20,7 +23,7 @@
 {
 	address &= 0xFFFFFF;
 	//printf("referenced: %X\n", address);
-	label[address/16] |= 1 << (address % 8);
+	label[address/16] |= 1 << (address % 16);
 }
 
 uint8_t is_visited(uint32_t address)
@@ -29,10 +32,40 @@
 	return visited[address/16] & (1 << ((address / 2) % 8));
 }
 
-uint8_t is_label(uint32_t address)
+uint16_t is_label(uint32_t address)
 {
 	address &= 0xFFFFFF;
-	return label[address/16] & (1 << (address % 8));
+	return label[address/16] & (1 << (address % 16));
+}
+
+typedef struct {
+	uint32_t num_labels;
+	uint32_t storage;
+	char     *labels[];
+} label_names;
+
+tern_node * add_label(tern_node * head, char * name, uint32_t address)
+{
+	char key[MAX_INT_KEY_SIZE];
+	address &= 0xFFFFFF;
+	reference(address);
+	tern_int_key(address, key);
+	label_names * names = tern_find_ptr(head, key);
+	if (names)
+	{
+		if (names->num_labels == names->storage)
+		{
+			names->storage = names->storage + (names->storage >> 1);
+			names = realloc(names, sizeof(label_names) + names->storage * sizeof(char *));
+		}
+	} else {
+		names = malloc(sizeof(label_names) + 4 * sizeof(char *));
+		names->num_labels = 0;
+		names->storage = 4;
+		head = tern_insert_ptr(head, key, names);
+	}
+	names->labels[names->num_labels++] = strdup(name);
+	return head;
 }
 
 typedef struct deferred {
@@ -42,7 +75,7 @@
 
 deferred * defer(uint32_t address, deferred * next)
 {
-	if (is_visited(address)) {
+	if (is_visited(address) || address & 1) {
 		return next;
 	}
 	//printf("deferring %X\n", address);
@@ -66,9 +99,18 @@
 	}
 }
 
-uint8_t labels = 0;
-uint8_t addr = 0;
-uint8_t only = 0;
+int label_fun(char *dst, uint32_t address, void * data)
+{
+	tern_node * labels = data;
+	char key[MAX_INT_KEY_SIZE];
+	label_names * names = tern_find_ptr(labels, tern_int_key(address & 0xFFFFFF, key));
+	if (names)
+	{
+		return sprintf(dst, "%s", names->labels[0]);
+	} else {
+		return m68k_default_label_fun(dst, address, NULL);
+	}
+}
 
 int main(int argc, char ** argv)
 {
@@ -77,14 +119,10 @@
 	char disbuf[1024];
 	m68kinst instbuf;
 	unsigned short * cur;
-	FILE * f = fopen(argv[1], "rb");
-	fseek(f, 0, SEEK_END);
-	filesize = ftell(f);
-	fseek(f, 0, SEEK_SET);
-	filebuf = malloc(filesize);
-	fread(filebuf, 2, filesize/2, f);
-	fclose(f);
 	deferred *def = NULL, *tmpd;
+
+	uint8_t labels = 0, addr = 0, only = 0, vos = 0, reset = 0;
+
 	for(uint8_t opt = 2; opt < argc; ++opt) {
 		if (argv[opt][0] == '-') {
 			FILE * address_log;
@@ -99,6 +137,12 @@
 			case 'o':
 				only = 1;
 				break;
+			case 'v':
+				vos = 1;
+				break;
+			case 'r':
+				reset = 1;
+				break;
 			case 'f':
 				opt++;
 				if (opt >= argc) {
@@ -126,29 +170,85 @@
 			reference(address);
 		}
 	}
-	for(cur = filebuf; cur - filebuf < (filesize/2); ++cur)
+
+	FILE * f = fopen(argv[1], "rb");
+	fseek(f, 0, SEEK_END);
+	filesize = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+	tern_node * named_labels = NULL;
+	char int_key[MAX_INT_KEY_SIZE];
+	uint32_t address_off, address_end;
+	if (vos)
 	{
-		*cur = (*cur >> 8) | (*cur << 8);
+		vos_program_module header;
+		vos_read_header(f, &header);
+		vos_read_alloc_module_map(f, &header);
+		address_off = header.user_boundary;
+		address_end = address_off + filesize - 0x1000;
+		def = defer(header.main_entry_link.code_address, def);
+		named_labels = add_label(named_labels, "main_entry_link", header.main_entry_link.code_address);
+		for (int i = 0; i < header.n_modules; i++)
+		{
+			if (!reset || header.module_map_entries[i].code_address != header.user_boundary)
+			{
+				def = defer(header.module_map_entries[i].code_address, def);
+			}
+			named_labels = add_label(named_labels, header.module_map_entries[i].name.str, header.module_map_entries[i].code_address);
+		}
+		fseek(f, 0x1000, SEEK_SET);
+		filebuf = malloc(filesize - 0x1000);
+		if (fread(filebuf, 2, (filesize - 0x1000)/2, f) != (filesize - 0x1000)/2)
+		{
+			fprintf(stderr, "Failure while reading file %s\n", argv[1]);
+		}
+		fclose(f);
+		for(cur = filebuf; cur - filebuf < ((filesize - 0x1000)/2); ++cur)
+		{
+			*cur = (*cur >> 8) | (*cur << 8);
+		}
+		if (reset)
+		{
+			def = defer(filebuf[2] << 16 | filebuf[3], def);
+			named_labels = add_label(named_labels, "reset", filebuf[2] << 16 | filebuf[3]);
+		}
+	} else {
+		address_off = 0;
+		address_end = filesize;
+		filebuf = malloc(filesize);
+		if (fread(filebuf, 2, filesize/2, f) != filesize/2)
+		{
+			fprintf(stderr, "Failure while reading file %s\n", argv[1]);
+		}
+		fclose(f);
+		for(cur = filebuf; cur - filebuf < (filesize/2); ++cur)
+		{
+			*cur = (*cur >> 8) | (*cur << 8);
+		}
+		uint32_t start = filebuf[2] << 16 | filebuf[3];
+		uint32_t int_2 = filebuf[0x68/2] << 16 | filebuf[0x6A/2];
+		uint32_t int_4 = filebuf[0x70/2] << 16 | filebuf[0x72/2];
+		uint32_t int_6 = filebuf[0x78/2] << 16 | filebuf[0x7A/2];
+		named_labels = add_label(named_labels, "start", start);
+		named_labels = add_label(named_labels, "int_2", int_2);
+		named_labels = add_label(named_labels, "int_4", int_4);
+		named_labels = add_label(named_labels, "int_6", int_6);
+		if (!def || !only) {
+			def = defer(start, def);
+			def = defer(int_2, def);
+			def = defer(int_4, def);
+			def = defer(int_6, def);
+		}
 	}
-	uint32_t start = filebuf[2] << 16 | filebuf[3], tmp_addr;
-	uint32_t int_2 = filebuf[0x68/2] << 16 | filebuf[0x6A/2];
-	uint32_t int_4 = filebuf[0x70/2] << 16 | filebuf[0x72/2];
-	uint32_t int_6 = filebuf[0x78/2] << 16 | filebuf[0x7A/2];
 	uint16_t *encoded, *next;
-	uint32_t size;
-	if (!def || !only) {
-		def = defer(start, def);
-		def = defer(int_2, def);
-		def = defer(int_4, def);
-		def = defer(int_6, def);
-	}
+	uint32_t size, tmp_addr;
 	uint32_t address;
 	while(def) {
 		do {
 			encoded = NULL;
 			address = def->address;
 			if (!is_visited(address)) {
-				encoded = filebuf + address/2;
+				encoded = filebuf + (address - address_off)/2;
 			}
 			tmpd = def;
 			def = def->next;
@@ -158,7 +258,7 @@
 			break;
 		}
 		for(;;) {
-			if (address > filesize) {
+			if (address > address_end || address < address_off) {
 				break;
 			}
 			visit(address);
@@ -175,7 +275,7 @@
 			if (instbuf.op == M68K_BCC || instbuf.op == M68K_DBCC || instbuf.op == M68K_BSR) {
 				if (instbuf.op == M68K_BCC && instbuf.extra.cond == COND_TRUE) {
 					address = instbuf.address + 2 + instbuf.src.params.immed;
-					encoded = filebuf + address/2;
+					encoded = filebuf + (address - address_off)/2;
 					reference(address);
 					if (is_visited(address)) {
 						break;
@@ -188,13 +288,13 @@
 			} else if(instbuf.op == M68K_JMP) {
 				if (instbuf.src.addr_mode == MODE_ABSOLUTE || instbuf.src.addr_mode == MODE_ABSOLUTE_SHORT) {
 					address = instbuf.src.params.immed;
-					encoded = filebuf + address/2;
+					encoded = filebuf + (address - address_off)/2;
 					if (is_visited(address)) {
 						break;
 					}
 				} else if (instbuf.src.addr_mode == MODE_PC_DISPLACE) {
 					address = instbuf.src.params.regs.displacement + instbuf.address + 2;
-					encoded = filebuf + address/2;
+					encoded = filebuf + (address - address_off)/2;
 					if (is_visited(address)) {
 						break;
 					}
@@ -211,6 +311,11 @@
 		}
 	}
 	if (labels) {
+		for (address = 0; address < address_off; address++) {
+			if (is_label(address)) {
+				printf("ADR_%X equ $%X\n", address, address);
+			}
+		}
 		for (address = filesize; address < (16*1024*1024); address++) {
 			if (is_label(address)) {
 				printf("ADR_%X equ $%X\n", address, address);
@@ -218,25 +323,21 @@
 		}
 		puts("");
 	}
-	for (address = 0; address < filesize; address+=2) {
+	for (address = address_off; address < address_end; address+=2) {
 		if (is_visited(address)) {
-			encoded = filebuf + address/2;
+			encoded = filebuf + (address-address_off)/2;
 			m68k_decode(encoded, &instbuf, address);
 			if (labels) {
-				m68k_disasm_labels(&instbuf, disbuf);
-				if (address == start) {
-					puts("start:");
-				}
-				if(address == int_2) {
-					puts("int_2:");
-				}
-				if(address == int_4) {
-					puts("int_4:");
-				}
-				if(address == int_6) {
-					puts("int_6:");
-				}
-				if (is_label(instbuf.address)) {
+				m68k_disasm_labels(&instbuf, disbuf, label_fun, named_labels);
+				char keybuf[MAX_INT_KEY_SIZE];
+				label_names * names = tern_find_ptr(named_labels, tern_int_key(address, keybuf));
+				if (names)
+				{
+					for (int i = 0; i < names->num_labels; i++)
+					{
+						printf("%s:\n", names->labels[i]);
+					}
+				} else if (is_label(instbuf.address)) {
 					printf("ADR_%X:\n", instbuf.address);
 				}
 				if (addr) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gen_test_hv.s68	Tue Dec 30 19:11:34 2014 -0800
@@ -0,0 +1,631 @@
+	dc.l $0, start
+	dc.l empty_handler
+	dc.l empty_handler
+	;$10
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$20
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$30
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$40
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$50
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$60
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$70
+	dc.l int_4
+	dc.l empty_handler
+	dc.l int_6
+	dc.l empty_handler
+	;$80
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$90
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$A0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$B0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$C0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$D0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$E0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	;$F0
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.l empty_handler
+	dc.b "SEGA"
+empty_handler:
+int_6:
+	rte
+int_4:
+	move.w (a2), d0
+	ori.w #$8000, d0
+	move.w d0, (a4)+
+	rte
+
+start:
+	lea $C00000, a0
+	lea $C00004, a1
+	move.w #$8104, (a1) ;Mode 5, everything turned off
+	move.w #$8004, (a1)
+	move.w #$8220, (a1) ;Scroll a table $8000
+	move.w #$8404, (a1) ;Scroll b table $8000
+	move.w #$8560, (a1) ;SAT table $C000
+	move.w #$8700, (a1) ;backdrop color 0
+	move.w #$8B00, (a1) ;full screen scroll
+	move.w #$8C81, (a1) ;40 cell mode, no interlace
+	move.w #$8C81, (mode).w
+	move.w #$8D00, (a1) ;hscroll table at 0
+	move.w #$8F02, (a1) ;autoinc 2
+	move.w #$9011, (a1) ;64x64 scroll size
+	move.l #$C0000000, (a1)
+	move.w #$000, (a0)
+	move.w #$EEE, (a0)
+
+	;clear scroll table
+	move.l #$40000000, (a1)
+	move.l #0, (a0)
+
+	;load tiles
+	move.l #$44000000, (a1)
+	lea font(pc), a2
+	move.w #((fontend-font)/4 - 1), d0
+tloop:
+	move.l (a2)+, (a0)
+	dbra d0, tloop
+
+
+
+	;clear name table
+	move.l #$40000002, (a1)
+	moveq #32, d0
+	move.w #(64*64-1), d1
+ploop:
+	move.w d0, (a0)
+	dbra d1, ploop
+
+
+	lea $FF0000, a4
+	move.b #$40, (a4, 6)
+	move.w #$8144, (a1) ;enable display
+	move #$2300, sr
+
+	lea (4, a1), a2 ;hv counter line address
+	lea (2, a1), a3 ;second contro/status address
+
+	move.b #254, d0
+init_wait:
+	cmp.b (a2), d0
+	beq init_wait
+
+top:
+	move.b #254, d0
+	lea $FF0000, a4
+	move.w #$8F00, (a1)     ;autoinc of 0
+	move.l #$40040000, (a1) ;unused VRAM address
+wait_active:
+	cmp.b (a2), d0
+	bne.s wait_active
+
+	move.l #$8A718014, (a1) ;enable Hints
+
+	;sync to VDP by attempting to fill FIFO
+	;being in vblank makes this a bit difficult
+
+	rept 8
+	move.l d0, (a0)
+	endr
+
+	;sample data for vblank flag off
+	rept 82 ;two lines worth of move.l
+	move.l (a3), (a4)+
+	endr
+
+	move.l a4, a5 ;save end of first buffer
+
+	move.b (a2), d0
+wait_new_line:
+	cmp.b (a2), d0
+	beq.s wait_new_line
+
+	;sync to VDP by filling FIFO
+	move.l d0, (a0)
+	move.l d0, (a0)
+	move.w d0, (a0)
+
+	;sample data for line change HV value
+	rept 45 ;one line worth of move.l
+	move.l (a2), (a4)+
+	endr
+
+	move.l a4, usp ;save end of second buffer
+
+	moveq #$70, d0
+wait_hint_line:
+	cmp.b (a2), d0
+	bne.s wait_hint_line
+
+	;sample data for line change HV value
+	rept 45 ;one line worth of move.l
+	move.l (a2), (a4)+
+	endr
+
+	move.l a4, a6
+
+	move.b #223, d0
+wait_inactive:
+	cmp.b (a2), d0
+	bne.s wait_inactive
+
+	;sync to VDP by filling FIFO
+	move.l d0, (a0)
+	move.l d0, (a0)
+	move.w d0, (a0)
+
+	;sample data for vblank on
+	rept 82 ;two lines worth of move.l
+	move.l (a3), (a4)+
+	endr
+
+	move.l #$8AFF8004, (a1) ;disable Hints
+
+	rsset $FFFF8000
+vblank_start_min rs.w 1
+vblank_start_max rs.w 1
+vblank_end_min   rs.w 1
+vblank_end_max   rs.w 1
+hblank_start_min rs.w 1
+hblank_start_max rs.w 1
+hblank_end_min   rs.w 1
+hblank_end_max   rs.w 1
+line_change_min  rs.w 1
+line_change_max  rs.w 1
+hint_min         rs.w 1
+hint_max         rs.w 1
+mode             rs.w 1
+printed_hv_dump  rs.b 1
+button_state     rs.b 1
+
+	lea $FF0001, a4
+.loop:
+	btst.b #3, (a4)
+	beq.s found_vblank_off
+	move.w 1(a4), d6
+	addq #4, a4
+	bra.s .loop
+found_vblank_off:
+
+	move.w (vblank_end_max).w, d0
+	beq .new_max
+	cmp.w d0, d6
+	blo .no_new_max
+.new_max
+	move.w d6, (vblank_end_max).w
+.no_new_max:
+
+
+	move.w 1(a4), d6
+
+	move.w (vblank_end_min).w, d0
+	beq .new_min
+	cmp.w d0, d6
+	bhi .no_new_min
+.new_min
+	move.w d6, (vblank_end_min).w
+.no_new_min:
+
+	lea $FF0001, a4
+;first find a point where HBLANK is not set
+	bra.s .start
+.loop:
+	addq #4, a4
+.start
+	btst.b #2, (a4)
+	bne.s .loop
+
+;then find a point after that where it switches to on
+.loop2:
+	btst.b #2, (a4)
+	bne.s found_hblank_on
+	move.w 1(a4), d5
+	addq #4, a4
+	bra.s .loop2
+found_hblank_on:
+
+	move.w (hblank_start_max).w, d0
+	beq .new_max
+	cmp.w d0, d5
+	blo .no_new_max
+.new_max
+	move.w d5, (hblank_start_max).w
+.no_new_max:
+
+
+	move.w 1(a4), d5
+
+	move.w (hblank_start_min).w, d0
+	beq .new_min
+	cmp.w d0, d5
+	bhi .no_new_min
+.new_min
+	move.w d5, (hblank_start_min).w
+.no_new_min:
+
+;finally find a point after that where it switches back off
+.loop2:
+	btst.b #2, (a4)
+	beq.s found_hblank_off
+	move.w 1(a4), d5
+	addq #4, a4
+	bra.s .loop2
+found_hblank_off:
+
+	move.w (hblank_end_max).w, d0
+	beq .new_max
+	cmp.w d0, d5
+	blo .no_new_max
+.new_max
+	move.w d5, (hblank_end_max).w
+.no_new_max:
+
+
+	move.w 1(a4), d5
+
+	move.w (hblank_end_min).w, d0
+	beq .new_min
+	cmp.w d0, d5
+	bhi .no_new_min
+.new_min
+	move.w d5, (hblank_end_min).w
+.no_new_min:
+
+	move.l a5, a4 ;save line change buffer for later
+	move.b (a5), d0
+.loop
+	move.w (a5), d7
+	addq #2, a5
+	cmp.b (a5), d0
+	beq .loop
+found_line_change:
+
+	move.w (line_change_max).w, d0
+	beq .new_max
+	cmp.w d0, d7
+	blo .no_new_max
+.new_max
+	move.w d7, (line_change_max).w
+.no_new_max:
+
+	move.w (a5), d7
+
+	move.w (line_change_min).w, d0
+	beq .new_min
+	cmp.w d0, d7
+	bhi .no_new_min
+.new_min
+	move.w d7, (line_change_min).w
+.no_new_min:
+
+	addq #1, a6
+.loop:
+	btst.b #3, (a6)
+	bne.s found_vblank_on
+	move.w 1(a6), d5
+	addq #4, a6
+	bra.s .loop
+found_vblank_on:
+
+	move.w (vblank_start_max).w, d0
+	beq .new_max
+	cmp.w d0, d5
+	blo .no_new_max
+.new_max
+	move.w d5, (vblank_start_max).w
+.no_new_max:
+
+	move.w 1(a6), d5
+
+	move.w (vblank_start_min).w, d0
+	beq .new_min
+	cmp.b d0, d5
+	bhi .no_new_min
+.new_min
+	move.w d5, (vblank_start_min).w
+.no_new_min:
+
+	move usp, a5
+.loop:
+	btst.b #7, (a5)
+	bne.s found_hint
+	move.w (a5), d1
+	addq #2, a5
+	bra.s .loop
+found_hint:
+
+	move.w (hint_max).w, d0
+	beq .new_max
+	cmp.w d0, d1
+	blo .no_new_max
+.new_max
+	move.w d1, (hint_max).w
+.no_new_max:
+
+	move.w (a5), d1
+	and.w #$7FFF, d1
+
+	move.w (hint_min).w, d0
+	beq .new_min
+	cmp.b d0, d1
+	bhi .no_new_min
+.new_min
+	move.w d1, (hint_min).w
+.no_new_min:
+
+draw_info:
+	;draw data
+	move.w #$8F02, (a1)     ;autoinc of 2
+	move.l #$40840002, (a1)
+
+	moveq #0, d0
+	lea VBlankStart(pc), a6
+	bsr print_string
+
+
+	move.w (vblank_start_max), d0
+	moveq #0, d1
+	bsr print_hexw
+
+	move.w #32, (a0)
+	move.w d5, d0
+	bsr print_hexw
+
+	move.w #32, (a0)
+	move.w (vblank_start_min), d0
+	bsr print_hexw
+
+	moveq #0, d0
+	move.l #$41040002, (a1)
+	lea VBlankEnd(pc), a6
+	bsr print_string
+
+	;max value before vblank end
+	moveq #0, d1
+	move.w (vblank_end_max), d0
+	bsr print_hexw
+
+	move.w #32, (a0)
+	move.w d6, d0
+	bsr print_hexw
+
+	;min value after vblank end
+	move.w (vblank_end_min), d0
+	move.w #32, (a0)
+	bsr print_hexw
+
+	moveq #0, d0
+	move.l #$41840002, (a1)
+	lea LineChange(pc), a6
+	bsr print_string
+
+	move.w (line_change_max), d0
+	moveq #0, d1
+	bsr print_hexw
+
+	move.w #32, (a0)
+	move.w d7, d0
+	bsr print_hexw
+
+	move.w (line_change_min), d0
+	move.w #32, (a0)
+	bsr print_hexw
+
+	moveq #0, d0
+	move.l #$42040002, (a1)
+	lea HBlankStart(pc), a6
+	bsr print_string
+
+	move.w (hblank_start_max), d0
+	moveq #0, d1
+	bsr print_hexw
+
+	move.w (hblank_start_min), d0
+	move.w #32, (a0)
+	bsr print_hexw
+
+	moveq #0, d0
+	move.l #$42840002, (a1)
+	lea HBlankEnd(pc), a6
+	bsr print_string
+
+	move.w (hblank_end_max), d0
+	moveq #0, d1
+	bsr print_hexw
+
+	move.w (hblank_end_min), d0
+	move.w #32, (a0)
+	bsr print_hexw
+
+	moveq #0, d0
+	move.l #$43040002, (a1)
+	lea HInterrupt(pc), a6
+	bsr print_string
+
+	move.w (hint_max), d0
+	moveq #0, d1
+	bsr print_hexw
+
+	move.w (hint_min), d0
+	move.w #32, (a0)
+	bsr print_hexw
+
+	;read pad
+	move.b #$40, $A10003
+	move.b $A10003, d0
+	move.b #$00, $A10003
+	and.b #$3f, d0
+	move.b $A10003, d1
+	and.b #$30, d1
+	lsl.b #2, d1
+	or.b d1, d0
+	not.b d0
+	move.b (button_state).w, d2
+	eor.b d0, d2
+	and.b d0, d2
+	move.b d2, d3 ;d3 contains newly pressed buttons, SACBRLDU
+	move.b d0, (button_state).w
+
+	btst.l #7, d3
+	beq not_pressed
+
+	moveq #0, d0
+	move.l d0, (vblank_start_min).w
+	move.l d0, (vblank_end_min).w
+	move.l d0, (hblank_start_min).w
+	move.l d0, (hblank_end_min).w
+	move.l d0, (line_change_min).w
+	move.l d0, (hint_min).w
+	move.b d0, (printed_hv_dump).w
+	move.w (mode).w, d0
+	eor.w  #$81, d0
+	move.w d0, (mode).w
+	move.w d0, (a1)
+	bra top
+
+not_pressed
+
+	move.b (printed_hv_dump).w, d0
+	bne top
+	move.b #1, (printed_hv_dump).w
+
+	moveq #0, d1
+	moveq #89, d4
+	moveq #6, d5
+	move.l #$45820002, d6
+	move.l d6, (a1)
+
+print_loop:
+	dbra d5, .no_line_change
+		 ;#$45820002
+	add.l #$00800000, d6
+	move.l d6, (a1)
+	moveq #5, d5
+.no_line_change
+	move.w #32, (a0)
+	move.w (a4)+, d0
+	bsr print_hexw
+	dbra d4, print_loop
+
+	add.l #$01020000, d6
+	move.l d6, (a1)
+	moveq #0, d0
+	lea Instructions(pc), a6
+	bsr print_string
+
+	bra top
+
+VBlankStart:
+	dc.b "VBlank Start: ", 0
+VBlankEnd:
+	dc.b "VBlank End:   ", 0
+LineChange:
+	dc.b "Line Change:  ", 0
+HBlankStart:
+	dc.b "HBlank Start: ", 0
+HBlankEnd:
+	dc.b "HBlank End:   ", 0
+HInterrupt:
+	dc.b "HInterrupt:   ", 0
+Instructions:
+	dc.b "Press Start to switch modes", 0
+
+	align 1
+;Prints a number in hex format
+;d0.w - number to print
+;d1.w - base tile attribute
+;a0 - VDP data port
+;
+;Clobbers: d2.l, d3.l
+;
+print_hexw:
+	moveq #3, d3
+.digitloop
+	rol.w #4, d0
+	moveq #$F, d2
+	and.b d0, d2
+	cmp.b #$A, d2
+	bge .hex
+	add.w #$30, d2
+	bra .makeattrib
+.hex
+	add.w #($41-$A), d2
+.makeattrib
+	add.w d1, d2
+	move.w d2, (a0)
+	dbra d3, .digitloop
+	rts
+
+;Prints a null terminated string
+;a6 - pointer to string
+;a0 - VDP data port
+;d0 - base tile attribute
+;
+;Clobbers: d1.w
+print_string:
+.loop
+	moveq #0, d1
+	move.b (a6)+, d1
+	beq .end
+	add.w d0, d1
+	move.w d1, (a0)
+	bra .loop
+.end
+	rts
+
+	align 1
+font:
+	incbin font.tiles
+fontend
+
--- a/io.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/io.c	Tue Dec 30 19:11:34 2014 -0800
@@ -3,15 +3,44 @@
  This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+
 #include "io.h"
 #include "blastem.h"
 #include "render.h"
 
+const char * device_type_names[] = {
+	"3-button gamepad",
+	"6-button gamepad",
+	"Mega Mouse",
+	"Menacer",
+	"Justifier",
+	"Sega multi-tap",
+	"EA 4-way Play cable A",
+	"EA 4-way Play cable B",
+	"Sega Parallel Transfer Board",
+	"Generic Device",
+	"None"
+};
+
 enum {
 	BIND_NONE,
+	BIND_UI,
 	BIND_GAMEPAD1,
 	BIND_GAMEPAD2,
-	BIND_UI
+	BIND_GAMEPAD3,
+	BIND_GAMEPAD4,
+	BIND_GAMEPAD5,
+	BIND_GAMEPAD6,
+	BIND_GAMEPAD7,
+	BIND_GAMEPAD8
 };
 
 typedef enum {
@@ -26,6 +55,7 @@
 } ui_action;
 
 typedef struct {
+	io_port *port;
 	uint8_t bind_type;
 	uint8_t subtype_a;
 	uint8_t subtype_b;
@@ -117,7 +147,7 @@
 void bind_gamepad(int keycode, int gamepadnum, int button)
 {
 
-	if (gamepadnum < 1 || gamepadnum > 2) {
+	if (gamepadnum < 1 || gamepadnum > 8) {
 		return;
 	}
 	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
@@ -126,7 +156,7 @@
 
 void bind_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbutton)
 {
-	if (gamepadnum < 1 || gamepadnum > 2) {
+	if (gamepadnum < 1 || gamepadnum > 8) {
 		return;
 	}
 	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
@@ -135,7 +165,7 @@
 
 void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum, int button)
 {
-	if (gamepadnum < 1 || gamepadnum > 2) {
+	if (gamepadnum < 1 || gamepadnum > 8) {
 		return;
 	}
 	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
@@ -159,17 +189,14 @@
 
 void handle_binding_down(keybinding * binding)
 {
-	switch(binding->bind_type)
+	if (binding->bind_type >= BIND_GAMEPAD1)
 	{
-	case BIND_GAMEPAD1:
-	case BIND_GAMEPAD2:
-		if (binding->subtype_a <= GAMEPAD_EXTRA) {
-			genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] |= binding->value;
+		if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) {
+			binding->port->input[binding->subtype_a] |= binding->value;
 		}
-		if (binding->subtype_b <= GAMEPAD_EXTRA) {
-			genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] |= binding->value;
+		if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) {
+			binding->port->input[binding->subtype_b] |= binding->value;
 		}
-		break;
 	}
 }
 
@@ -206,11 +233,11 @@
 	{
 	case BIND_GAMEPAD1:
 	case BIND_GAMEPAD2:
-		if (binding->subtype_a <= GAMEPAD_EXTRA) {
-			genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] &= ~binding->value;
+		if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) {
+			binding->port->input[binding->subtype_a] &= ~binding->value;
 		}
-		if (binding->subtype_b <= GAMEPAD_EXTRA) {
-			genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] &= ~binding->value;
+		if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) {
+			binding->port->input[binding->subtype_b] &= ~binding->value;
 		}
 		break;
 	case BIND_UI:
@@ -447,7 +474,169 @@
 	}
 }
 
-void set_keybindings()
+void process_device(char * device_type, io_port * port)
+{
+	port->device_type = IO_NONE;
+	if (!device_type)
+	{
+		return;
+	}
+
+	const int gamepad_len = strlen("gamepad");
+	if (!memcmp(device_type, "gamepad", gamepad_len))
+	{
+		if (
+			(device_type[gamepad_len] != '3' && device_type[gamepad_len] != '6')
+			|| device_type[gamepad_len+1] != '.' || device_type[gamepad_len+2] < '1'
+			|| device_type[gamepad_len+2] > '8' || device_type[gamepad_len+3] != 0
+		)
+		{
+			fprintf(stderr, "%s is not a valid gamepad type\n", device_type);
+		} else if (device_type[gamepad_len] == '3')
+		{
+			port->device_type = IO_GAMEPAD3;
+		} else {
+			port->device_type = IO_GAMEPAD6;
+		}
+		port->device.pad.gamepad_num = device_type[gamepad_len+2] - '1';
+	} else if(!strcmp(device_type, "sega_parallel")) {
+		port->device_type = IO_SEGA_PARALLEL;
+		port->device.stream.data_fd = -1;
+		port->device.stream.listen_fd = -1;
+	} else if(!strcmp(device_type, "generic")) {
+		port->device_type = IO_GENERIC;
+		port->device.stream.data_fd = -1;
+		port->device.stream.listen_fd = -1;
+	}
+}
+
+char * io_name(int i)
+{
+	switch (i)
+	{
+	case 0:
+		return "1";
+	case 1:
+		return "2";
+	case 2:
+		return "EXT";
+	default:
+		return "invalid";
+	}
+}
+
+static char * sockfile_name;
+static void cleanup_sockfile()
+{
+	unlink(sockfile_name);
+}
+
+void setup_io_devices(tern_node * config, io_port * ports)
+{
+	tern_node *io_nodes = tern_find_prefix(config, "iodevices");
+	char * io_1 = tern_find_ptr(io_nodes, "1");
+	char * io_2 = tern_find_ptr(io_nodes, "2");
+	char * io_ext = tern_find_ptr(io_nodes, "ext");
+
+	process_device(io_1, ports);
+	process_device(io_2, ports+1);
+	process_device(io_ext, ports+2);
+
+	for (int i = 0; i < 3; i++)
+	{
+
+		if (ports[i].device_type == IO_SEGA_PARALLEL)
+		{
+			char *pipe_name = tern_find_ptr(config, "ioparallel_pipe");
+			if (!pipe_name)
+			{
+				fprintf(stderr, "IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i));
+				ports[i].device_type = IO_NONE;
+			} else {
+				printf("IO port: %s connected to device '%s' with pipe name: %s\n", io_name(i), device_type_names[ports[i].device_type], pipe_name);
+				if (!strcmp("stdin", pipe_name))
+				{
+					ports[i].device.stream.data_fd = STDIN_FILENO;
+				} else {
+					if (mkfifo(pipe_name, 0666) && errno != EEXIST)
+					{
+						fprintf(stderr, "Failed to create fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno));
+						ports[i].device_type = IO_NONE;
+					} else {
+						ports[i].device.stream.data_fd = open(pipe_name, O_NONBLOCK | O_RDONLY);
+						if (ports[i].device.stream.data_fd == -1)
+						{
+							fprintf(stderr, "Failed to open fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno));
+							ports[i].device_type = IO_NONE;
+						}
+					}
+				}
+			}
+		} else if (ports[i].device_type == IO_GENERIC) {
+			char *sock_name = tern_find_ptr(config, "iosocket");
+			if (!sock_name)
+			{
+				fprintf(stderr, "IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i));
+				ports[i].device_type = IO_NONE;
+			} else {
+				printf("IO port: %s connected to device '%s' with socket name: %s\n", io_name(i), device_type_names[ports[i].device_type], sock_name);
+				ports[i].device.stream.data_fd = -1;
+				ports[i].device.stream.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+				size_t pathlen = strlen(sock_name);
+				size_t addrlen = offsetof(struct sockaddr_un, sun_path) + pathlen + 1;
+				struct sockaddr_un *saddr = malloc(addrlen);
+				saddr->sun_family = AF_UNIX;
+				memcpy(saddr->sun_path, sock_name, pathlen+1);
+				if (bind(ports[i].device.stream.listen_fd, (struct sockaddr *)saddr, addrlen))
+				{
+					fprintf(stderr, "Failed to bind socket for IO Port %s to path %s: %d %s\n", io_name(i), sock_name, errno, strerror(errno));
+					goto cleanup_sock;
+				}
+				if (listen(ports[i].device.stream.listen_fd, 1))
+				{
+					fprintf(stderr, "Failed to listen on socket for IO Port %s: %d %s\n", io_name(i), errno, strerror(errno));
+					goto cleanup_sockfile;
+				}
+				sockfile_name = sock_name;
+				atexit(cleanup_sockfile);
+				continue;
+cleanup_sockfile:
+				unlink(sock_name);
+cleanup_sock:
+				close(ports[i].device.stream.listen_fd);
+				ports[i].device_type = IO_NONE;
+			}
+		} else if (ports[i].device_type == IO_GAMEPAD3 || ports[i].device_type == IO_GAMEPAD6) {
+			printf("IO port %s connected to gamepad #%d with type '%s'\n", io_name(i), ports[i].device.pad.gamepad_num + 1, device_type_names[ports[i].device_type]);
+		} else {
+			printf("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]);
+		}
+	}
+}
+
+void map_bindings(io_port *ports, keybinding *bindings, int numbindings)
+{
+	for (int i = 0; i < numbindings; i++)
+	{
+		if (bindings[i].bind_type >= BIND_GAMEPAD1)
+		{
+			int num = bindings[i].bind_type - BIND_GAMEPAD1;
+			for (int j = 0; j < 3; j++)
+			{
+				if ((ports[j].device_type == IO_GAMEPAD3
+					 || ports[j].device_type ==IO_GAMEPAD6)
+					 && ports[j].device.pad.gamepad_num == num
+				)
+				{
+					bindings[i].port = ports + j;
+					break;
+				}
+			}
+		}
+	}
+}
+
+void set_keybindings(io_port *ports)
 {
 	tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP);
 	special = tern_insert_int(special, "down", RENDERKEY_DOWN);
@@ -532,76 +721,245 @@
 	speeds = malloc(sizeof(uint32_t));
 	speeds[0] = 100;
 	process_speeds(speed_nodes, NULL);
-	for (int i = 0; i < num_speeds; i++) {
+	for (int i = 0; i < num_speeds; i++)
+	{
 		if (!speeds[i]) {
 			fprintf(stderr, "Speed index %d was not set to a valid percentage!", i);
 			speeds[i] = 100;
 		}
 	}
+	for (int bucket = 0; bucket < 256; bucket++)
+	{
+		if (bindings[bucket])
+		{
+			map_bindings(ports, bindings[bucket], 256);
+		}
+	}
+	for (int stick = 0; stick < MAX_JOYSTICKS; stick++)
+	{
+		if (joybindings[stick])
+		{
+			int numbuttons = render_joystick_num_buttons(stick);
+			map_bindings(ports, joybindings[stick], render_joystick_num_buttons(stick));
+		}
+		if (joydpads[stick])
+		{
+			map_bindings(ports, joydpads[stick]->bindings, 4);
+		}
+	}
 }
 
 #define TH 0x40
 #define TH_TIMEOUT 8000
 
-void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction)
+void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction)
 {
 	/*uint8_t control = pad->control | 0x80;
 	uint8_t th = control & pad->output;
 	if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) {
 		printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle);
 	}*/
-	if (current_cycle >= pad->timeout_cycle) {
-		pad->th_counter = 0;
-	} else {
-		pad->timeout_cycle -= deduction;
+	if (port->device_type == IO_GAMEPAD6)
+	{
+		if (current_cycle >= port->device.pad.timeout_cycle)
+		{
+			port->device.pad.th_counter = 0;
+		} else {
+			port->device.pad.timeout_cycle -= deduction;
+		}
+	}
+}
+
+static void wait_for_connection(io_port * port)
+{
+	if (port->device.stream.data_fd == -1)
+	{
+		puts("Waiting for socket connection...");
+		port->device.stream.data_fd = accept(port->device.stream.listen_fd, NULL, NULL);
+		fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR);
+	}
+}
+
+static void service_pipe(io_port * port)
+{
+	uint8_t value;
+	int numRead = read(port->device.stream.data_fd, &value, sizeof(value));
+	if (numRead > 0)
+	{
+		port->input[IO_TH0] = (value & 0xF) | 0x10;
+		port->input[IO_TH1] = (value >> 4) | 0x10;
+	} else if(numRead == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
+		fprintf(stderr, "Error reading pipe for IO port: %d %s\n", errno, strerror(errno));
 	}
 }
 
-void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle)
+static void service_socket(io_port *port)
 {
-	if (pad->control & TH) {
-		//check if TH has changed
-		if ((pad->output & TH) ^ (value & TH)) {
-			if (current_cycle >= pad->timeout_cycle) {
-				pad->th_counter = 0;
+	uint8_t buf[32];
+	uint8_t blocking = 0;
+	int numRead = 0;
+	while (numRead <= 0)
+	{
+		numRead = recv(port->device.stream.data_fd, buf, sizeof(buf), 0);
+		if (numRead > 0)
+		{
+			port->input[IO_TH0] = buf[numRead-1];
+			if (port->input[IO_STATE] == IO_READ_PENDING)
+			{
+				port->input[IO_STATE] = IO_READ;
+				if (blocking)
+				{
+					//pending read satisfied, back to non-blocking mode
+					fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK);
+				}
+			} else if (port->input[IO_STATE] == IO_WRITTEN) {
+				port->input[IO_STATE] = IO_READ;
 			}
-			if (!(value & TH)) {
-				pad->th_counter++;
+		} else if (numRead == 0) {
+			port->device.stream.data_fd = -1;
+			wait_for_connection(port);
+		} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+			fprintf(stderr, "Error reading from socket for IO port: %d %s\n", errno, strerror(errno));
+			close(port->device.stream.data_fd);
+			wait_for_connection(port);
+		} else if (port->input[IO_STATE] == IO_READ_PENDING) {
+			//clear the nonblocking flag so the next read will block
+			if (!blocking)
+			{
+				fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR);
+				blocking = 1;
 			}
-			pad->timeout_cycle = current_cycle + TH_TIMEOUT;
+		} else {
+			//no new data, but that's ok
+			break;
 		}
 	}
-	pad->output = value;
+
+	if (port->input[IO_STATE] == IO_WRITE_PENDING)
+	{
+		uint8_t value = port->output & port->control;
+		int written = 0;
+		blocking = 0;
+		while (written <= 0)
+		{
+			send(port->device.stream.data_fd, &value, sizeof(value), 0);
+			if (written > 0)
+			{
+				port->input[IO_STATE] = IO_WRITTEN;
+				if (blocking)
+				{
+					//pending write satisfied, back to non-blocking mode
+					fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK);
+				}
+			} else if (written == 0) {
+				port->device.stream.data_fd = -1;
+				wait_for_connection(port);
+			} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+				fprintf(stderr, "Error writing to socket for IO port: %d %s\n", errno, strerror(errno));
+				close(port->device.stream.data_fd);
+				wait_for_connection(port);
+			} else {
+				//clear the nonblocking flag so the next write will block
+				if (!blocking)
+				{
+					fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR);
+					blocking = 1;
+				}
+			}
+		}
+	}
 }
 
-uint8_t io_data_read(io_port * pad, uint32_t current_cycle)
+void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle)
 {
-	uint8_t control = pad->control | 0x80;
-	uint8_t th = control & pad->output;
+	switch (port->device_type)
+	{
+	case IO_GAMEPAD6:
+		if (port->control & TH) {
+			//check if TH has changed
+			if ((port->output & TH) ^ (value & TH)) {
+				if (current_cycle >= port->device.pad.timeout_cycle) {
+					port->device.pad.th_counter = 0;
+				}
+				if (!(value & TH)) {
+					port->device.pad.th_counter++;
+				}
+				port->device.pad.timeout_cycle = current_cycle + TH_TIMEOUT;
+			}
+		}
+		port->output = value;
+		break;
+	case IO_GENERIC:
+		wait_for_connection(port);
+		port->input[IO_STATE] = IO_WRITE_PENDING;
+		port->output = value;
+		service_socket(port);
+		break;
+	default:
+		port->output = value;
+	}
+
+}
+
+uint8_t io_data_read(io_port * port, uint32_t current_cycle)
+{
+	uint8_t control = port->control | 0x80;
+	uint8_t th = control & port->output & 0x40;
 	uint8_t input;
-	if (current_cycle >= pad->timeout_cycle) {
-		pad->th_counter = 0;
+	switch (port->device_type)
+	{
+	case IO_GAMEPAD3:
+	{
+		input = port->input[th ? GAMEPAD_TH1 : GAMEPAD_TH0];
+		break;
 	}
-	/*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) {
-		printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, context->current_cycle);
-	}*/
-	if (th) {
-		if (pad->th_counter == 3) {
-			input = pad->input[GAMEPAD_EXTRA];
+	case IO_GAMEPAD6:
+	{
+		if (current_cycle >= port->device.pad.timeout_cycle) {
+			port->device.pad.th_counter = 0;
+		}
+		/*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) {
+			printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, port->input[GAMEPAD_TH0], port->input[GAMEPAD_TH1], port->th_counter,port->timeout_cycle, context->current_cycle);
+		}*/
+		if (th) {
+			if (port->device.pad.th_counter == 3) {
+				input = port->input[GAMEPAD_EXTRA];
+			} else {
+				input = port->input[GAMEPAD_TH1];
+			}
 		} else {
-			input = pad->input[GAMEPAD_TH1];
+			if (port->device.pad.th_counter == 3) {
+				input = port->input[GAMEPAD_TH0] | 0xF;
+			} else if(port->device.pad.th_counter == 4) {
+				input = port->input[GAMEPAD_TH0]  & 0x30;
+			} else {
+				input = port->input[GAMEPAD_TH0] | 0xC;
+			}
 		}
-	} else {
-		if (pad->th_counter == 3) {
-			input = pad->input[GAMEPAD_TH0] | 0xF;
-		} else if(pad->th_counter == 4) {
-			input = pad->input[GAMEPAD_TH0]  & 0x30;
-		} else {
-			input = pad->input[GAMEPAD_TH0] | 0xC;
+		break;
+	}
+	case IO_SEGA_PARALLEL:
+		if (!th)
+		{
+			service_pipe(port);
 		}
+		input = ~port->input[th ? IO_TH1 : IO_TH0];
+		break;
+	case IO_GENERIC:
+		if (port->input[IO_TH0] & 0x80 && port->input[IO_STATE] == IO_WRITTEN)
+		{
+			//device requested a blocking read after writes
+			port->input[IO_STATE] = IO_READ_PENDING;
+		}
+		service_socket(port);
+		input = ~port->input[IO_TH0];
+		break;
+	default:
+		input = 0;
+		break;
 	}
-	uint8_t value = ((~input) & (~control)) | (pad->output & control);
-	/*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) {
+	uint8_t value = ((~input) & (~control)) | (port->output & control);
+	/*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) {
 		printf ("value: %X\n", value);
 	}*/
 	return value;
--- a/io.h	Mon Dec 29 23:08:39 2014 -0800
+++ b/io.h	Tue Dec 30 19:11:34 2014 -0800
@@ -1,18 +1,43 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #ifndef IO_H_
 #define IO_H_
 #include <stdint.h>
+#include "tern.h"
+
+enum {
+	IO_GAMEPAD3,
+	IO_GAMEPAD6,
+	IO_MOUSE,
+	IO_MENACER,
+	IO_JUSTIFIER,
+	IO_SEGA_MULTI,
+	IO_EA_MULTI_A,
+	IO_EA_MULTI_B,
+	IO_SEGA_PARALLEL,
+	IO_GENERIC,
+	IO_NONE
+};
 
 typedef struct {
-	uint32_t th_counter;
-	uint32_t timeout_cycle;
-	uint8_t output;
-	uint8_t control;
-	uint8_t input[3];
+	union {
+		struct {
+			uint32_t timeout_cycle;
+			uint16_t th_counter;
+			uint16_t gamepad_num;
+		} pad;
+		struct {
+			int data_fd;
+			int listen_fd;
+		} stream;
+	} device;
+	uint8_t  output;
+	uint8_t  control;
+	uint8_t  input[3];
+	uint8_t  device_type;
 } io_port;
 
 #define GAMEPAD_TH0 0
@@ -20,7 +45,19 @@
 #define GAMEPAD_EXTRA 2
 #define GAMEPAD_NONE 0xF
 
-void set_keybindings();
+#define IO_TH0 0
+#define IO_TH1 1
+#define IO_STATE 2
+
+enum {
+	IO_WRITE_PENDING,
+	IO_WRITTEN,
+	IO_READ_PENDING,
+	IO_READ
+};
+
+void set_keybindings(io_port *ports);
+void setup_io_devices(tern_node * config, io_port * ports);
 void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction);
 void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle);
 uint8_t io_data_read(io_port * pad, uint32_t current_cycle);
--- a/render_sdl.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/render_sdl.c	Tue Dec 30 19:11:34 2014 -0800
@@ -364,7 +364,7 @@
 	glBindTexture(GL_TEXTURE_2D, (context->regs[REG_MODE_4] & BIT_INTERLACE) ? textures[1] : textures[2]);
 	glUniform1i(un_textures[1], 1);
 
-	glUniform1f(un_width, context->latched_mode & BIT_H40 ? 320.0f : 256.0f);
+	glUniform1f(un_width, context->regs[REG_MODE_4] & BIT_H40 ? 320.0f : 256.0f);
 
 	glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
 	glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
--- a/tern.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/tern.c	Tue Dec 30 19:11:34 2014 -0800
@@ -122,4 +122,14 @@
 	return tern_insert(head, key, val);
 }
 
-
+char * tern_int_key(uint32_t key, char * buf)
+{
+	char * cur = buf;
+	while (key)
+	{
+		*(cur++) = (key & 0x7F) + 1;
+		key >>= 7;
+	}
+	*cur = 0;
+	return buf;
+}
--- a/tern.h	Mon Dec 29 23:08:39 2014 -0800
+++ b/tern.h	Tue Dec 30 19:11:34 2014 -0800
@@ -8,6 +8,8 @@
 
 #include <stdint.h>
 
+#define MAX_INT_KEY_SIZE (sizeof(uint32_t) + 2)
+
 typedef union {
 	void     *ptrval;
 	intptr_t intval;
@@ -31,5 +33,6 @@
 void * tern_find_ptr_default(tern_node * head, char * key, void * def);
 void * tern_find_ptr(tern_node * head, char * key);
 tern_node * tern_insert_ptr(tern_node * head, char * key, void * value);
+char * tern_int_key(uint32_t key, char * buf);
 
 #endif //TERN_H_
--- a/vdp.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/vdp.c	Tue Dec 30 19:11:34 2014 -0800
@@ -9,8 +9,8 @@
 #include <string.h>
 #include "render.h"
 
-#define NTSC_ACTIVE 225
-#define PAL_ACTIVE 241
+#define NTSC_INACTIVE_START 224
+#define PAL_INACTIVE_START 240
 #define BUF_BIT_PRIORITY 0x40
 #define MAP_BIT_PRIORITY 0x8000
 #define MAP_BIT_H_FLIP 0x800
@@ -22,14 +22,17 @@
 
 #define MCLKS_SLOT_H40  16
 #define MCLKS_SLOT_H32  20
-#define VINT_CYCLE_H40  (21*MCLKS_SLOT_H40+332+9*MCLKS_SLOT_H40) //21 slots before HSYNC, 16 during, 10 after
-#define VINT_CYCLE_H32  ((33+20+7)*MCLKS_SLOT_H32)  //33 slots before HSYNC, 20 during, 7 after  TODO: confirm final number
-#define HSYNC_SLOT_H40  21
-#define MCLK_WEIRD_END  (HSYNC_SLOT_H40*MCLKS_SLOT_H40 + 332)
-#define SLOT_WEIRD_END  (HSYNC_SLOT_H40+17)
+#define VINT_SLOT_H40  4 //21 slots before HSYNC, 16 during, 10 after
+#define VINT_SLOT_H32  23  //33 slots before HSYNC, 20 during, 7 after  TODO: confirm final number
+#define HSYNC_SLOT_H40  240
+#define HSYNC_END_H40  (240+17)
 #define HSYNC_END_H32   (33 * MCLKS_SLOT_H32)
-#define HBLANK_CLEAR_H40 (MCLK_WEIRD_END+61*4)
-#define HBLANK_CLEAR_H32 (HSYNC_END_H32 + 46*5)
+#define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results
+#define HBLANK_END_H40  0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results
+#define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result
+#define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results
+#define LINE_CHANGE_H40 165
+#define LINE_CHANGE_H32 132
 #define FIFO_LATENCY    3
 
 int32_t color_map[1 << 12];
@@ -45,7 +48,7 @@
 
 uint8_t color_map_init_done;
 
-void init_vdp_context(vdp_context * context)
+void init_vdp_context(vdp_context * context, uint8_t region_pal)
 {
 	memset(context, 0, sizeof(*context));
 	context->vdpmem = malloc(VRAM_SIZE);
@@ -132,18 +135,21 @@
 			context->debugcolors[color] = render_map_color(r, g, b);
 		}
 	}
+	if (region_pal) {
+		context->flags2 |= FLAG2_REGION_PAL;
+	}
 }
 
 int is_refresh(vdp_context * context, uint32_t slot)
 {
-	if (context->latched_mode & BIT_H40) {
-		return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210);
+	if (context->regs[REG_MODE_4] & BIT_H40) {
+		return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154;
 	} else {
 		//TODO: Figure out which slots are refresh when display is off in 32-cell mode
 		//These numbers are guesses based on H40 numbers
-		return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152);
+		return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115;
 		//The numbers below are the refresh slots during active display
-		//return (slot == 66 || slot == 98 || slot == 130 || slot == 162);
+		//return (slot == 29 || slot == 61 || slot == 93 || slot == 125);
 	}
 }
 
@@ -227,8 +233,8 @@
 	       context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10,
 	       context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10,
 	       context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13,
-	       context->regs[REG_SAT], (context->regs[REG_SAT] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3E : 0x3F)) << 9,
-	       context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x1F) << 10);
+	       context->regs[REG_SAT], (context->regs[REG_SAT] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x7E : 0x7F)) << 9,
+	       context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10);
 	char * sizes[] = {"32", "64", "invalid", "128"};
 	printf("\n**Misc Group**\n"
 	       "07: %.2X | Backdrop Color: $%X\n"
@@ -239,11 +245,28 @@
 	       context->regs[REG_HINT], context->regs[REG_HINT],
 	       context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
 	       context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
+	char * src_types[] = {"68K", "68K", "Copy", "Fill"};
+	printf("\n**DMA Group**\n"
+	       "13: %.2X |\n"
+		   "14: %.2X | DMA Length: $%.4X words\n"
+		   "15: %.2X |\n"
+		   "16: %.2X |\n"
+		   "17: %.2X | DMA Source Address: $%.6X, Type: %s\n",
+		   context->regs[REG_DMALEN_L],
+		   context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L],
+		   context->regs[REG_DMASRC_L],
+		   context->regs[REG_DMASRC_M],
+		   context->regs[REG_DMASRC_H],
+		       context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
+			   src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
 	printf("\n**Internal Group**\n"
 	       "Address: %X\n"
 	       "CD:      %X\n"
-	       "Pending: %s\n",
-	       context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false");
+	       "Pending: %s\n"
+		   "VCounter: %d\n"
+		   "HCounter: %d\n",
+	       context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false",
+		   context->vcounter, context->hslot*2);
 
 	//TODO: Window Group, DMA Group
 }
@@ -269,7 +292,7 @@
 			height_mult = 8;
 		}
 		context->sprite_index &= 0x7F;
-		if (context->latched_mode & BIT_H40) {
+		if (context->regs[REG_MODE_4] & BIT_H40) {
 			if (context->sprite_index >= MAX_SPRITES_FRAME) {
 				context->sprite_index = 0;
 				return;
@@ -472,7 +495,7 @@
 	case 0x40:
 		if (!slot || !is_refresh(context, slot-1)) {
 			cur = context->fifo + context->fifo_write;
-			cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
+			cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
 			cur->address = context->address;
 			cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
 			cur->cd = context->cd;
@@ -567,7 +590,7 @@
 		if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) {
 			uint16_t address = context->regs[REG_WINDOW] << 10;
 			uint16_t line_offset, offset, mask;
-			if (context->latched_mode & BIT_H40) {
+			if (context->regs[REG_MODE_4] & BIT_H40) {
 				address &= 0xF000;
 				line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
 				mask = 0x7F;
@@ -893,13 +916,15 @@
 	uint32_t mask;
 	switch(linecyc)
 	{
+	case 165:
+	case 166:
+		external_slot(context);
+		break;
 	//sprite render to line buffer starts
-	case 0:
-		context->cur_slot = MAX_DRAWS-1;
-		memset(context->linebuf, 0, LINEBUF_SIZE);
-	case 1:
-	case 2:
-	case 3:
+	case 167:
+	case 168:
+	case 169:
+	case 170:
 		if (line == 0xFF) {
 			external_slot(context);
 		} else {
@@ -907,52 +932,50 @@
 		}
 		break;
 	//sprite attribute table scan starts
-	case 4:
+	case 171:
 		render_sprite_cells( context);
-		context->sprite_index = 0x80;
-		context->slot_counter = MAX_SPRITES_LINE;
 		scan_sprite_table(line, context);
 		break;
-	case 5:
-	case 6:
-	case 7:
-	case 8:
-	case 9:
-	case 10:
-	case 11:
-	case 12:
-	case 13:
-	case 14:
-	case 15:
-	case 16:
-	case 17:
-	case 18:
-	case 19:
-	case 20:
+	case 172:
+	case 173:
+	case 174:
+	case 175:
+	case 176:
+	case 177:
+	case 178:
+	case 179:
+	case 180:
+	case 181:
+	case 182:
+	case 229:
+	case 230:
+	case 231:
+	case 232:
+	case 233:
 	//!HSYNC asserted
-	case 21:
-	case 22:
+	case 234:
+	case 235:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 23:
+	case 236:
 		external_slot(context);
 		break;
-	case 24:
-	case 25:
-	case 26:
-	case 27:
-	case 28:
-	case 29:
-	case 30:
-	case 31:
-	case 32:
-	case 33:
-	case 34:
+	case 237:
+	case 238:
+	case 239:
+	case 240:
+	case 241:
+	case 242:
+	case 243:
+	case 244:
+	case 245:
+	case 246:
+	case 247:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 35:
+	case 248:
 		address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
 		mask = 0;
 		if (context->regs[REG_MODE_3] & 0x2) {
@@ -967,41 +990,41 @@
 		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
 		//printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
 		break;
-	case 36:
+	case 249:
 	//!HSYNC high
-	case 37:
-	case 38:
-	case 39:
+	case 250:
+	case 251:
+	case 252:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 40:
+	case 253:
 		read_map_scroll_a(0, line, context);
 		break;
-	case 41:
+	case 254:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 42:
+	case 255:
 		render_map_1(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 43:
+	case 0:
 		render_map_2(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 44:
+	case 1:
 		read_map_scroll_b(0, line, context);
 		break;
-	case 45:
+	case 2:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 46:
+	case 3:
 		render_map_3(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 47:
+	case 4:
 		render_map_output(line, 0, context);
 		scan_sprite_table(line, context);//Just a guess
 		//reverse context slot counter so it counts the number of sprite slots
@@ -1011,33 +1034,26 @@
 		context->sprite_draws = MAX_DRAWS;
 		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
 		break;
-	COLUMN_RENDER_BLOCK(2, 48)
-	COLUMN_RENDER_BLOCK(4, 56)
-	COLUMN_RENDER_BLOCK(6, 64)
-	COLUMN_RENDER_BLOCK_REFRESH(8, 72)
-	COLUMN_RENDER_BLOCK(10, 80)
-	COLUMN_RENDER_BLOCK(12, 88)
-	COLUMN_RENDER_BLOCK(14, 96)
-	COLUMN_RENDER_BLOCK_REFRESH(16, 104)
-	COLUMN_RENDER_BLOCK(18, 112)
-	COLUMN_RENDER_BLOCK(20, 120)
-	COLUMN_RENDER_BLOCK(22, 128)
-	COLUMN_RENDER_BLOCK_REFRESH(24, 136)
-	COLUMN_RENDER_BLOCK(26, 144)
-	COLUMN_RENDER_BLOCK(28, 152)
-	COLUMN_RENDER_BLOCK(30, 160)
-	COLUMN_RENDER_BLOCK_REFRESH(32, 168)
-	COLUMN_RENDER_BLOCK(34, 176)
-	COLUMN_RENDER_BLOCK(36, 184)
-	COLUMN_RENDER_BLOCK(38, 192)
-	COLUMN_RENDER_BLOCK_REFRESH(40, 200)
-	case 208:
-	case 209:
-		external_slot(context);
-		break;
-	default:
-		//leftovers from HSYNC clock change nonsense
-		break;
+	COLUMN_RENDER_BLOCK(2, 5)
+	COLUMN_RENDER_BLOCK(4, 13)
+	COLUMN_RENDER_BLOCK(6, 21)
+	COLUMN_RENDER_BLOCK_REFRESH(8, 29)
+	COLUMN_RENDER_BLOCK(10, 37)
+	COLUMN_RENDER_BLOCK(12, 45)
+	COLUMN_RENDER_BLOCK(14, 53)
+	COLUMN_RENDER_BLOCK_REFRESH(16, 61)
+	COLUMN_RENDER_BLOCK(18, 69)
+	COLUMN_RENDER_BLOCK(20, 77)
+	COLUMN_RENDER_BLOCK(22, 85)
+	COLUMN_RENDER_BLOCK_REFRESH(24, 93)
+	COLUMN_RENDER_BLOCK(26, 101)
+	COLUMN_RENDER_BLOCK(28, 109)
+	COLUMN_RENDER_BLOCK(30, 117)
+	COLUMN_RENDER_BLOCK_REFRESH(32, 125)
+	COLUMN_RENDER_BLOCK(34, 133)
+	COLUMN_RENDER_BLOCK(36, 141)
+	COLUMN_RENDER_BLOCK(38, 149)
+	COLUMN_RENDER_BLOCK_REFRESH(40, 157)
 	}
 }
 
@@ -1047,13 +1063,15 @@
 	uint32_t mask;
 	switch(linecyc)
 	{
+	case 132:
+	case 133:
+		external_slot(context);
+		break;
 	//sprite render to line buffer starts
-	case 0:
-		context->cur_slot = MAX_DRAWS_H32-1;
-		memset(context->linebuf, 0, LINEBUF_SIZE);
-	case 1:
-	case 2:
-	case 3:
+	case 134:
+	case 135:
+	case 136:
+	case 137:
 		if (line == 0xFF) {
 			external_slot(context);
 		} else {
@@ -1061,46 +1079,44 @@
 		}
 		break;
 	//sprite attribute table scan starts
-	case 4:
+	case 138:
 		render_sprite_cells( context);
-		context->sprite_index = 0x80;
-		context->slot_counter = MAX_SPRITES_LINE_H32;
 		scan_sprite_table(line, context);
 		break;
-	case 5:
-	case 6:
-	case 7:
-	case 8:
-	case 9:
-	case 10:
-	case 11:
-	case 12:
-	case 13:
+	case 139:
+	case 140:
+	case 141:
+	case 142:
+	case 143:
+	case 144:
+	case 145:
+	case 146:
+	case 147:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
-	case 14:
+	case 233:
 		external_slot(context);
 		break;
-	case 15:
-	case 16:
-	case 17:
-	case 18:
-	case 19:
+	case 234:
+	case 235:
+	case 236:
+	case 237:
+	case 238:
 	//HSYNC start
-	case 20:
-	case 21:
-	case 22:
-	case 23:
-	case 24:
-	case 25:
-	case 26:
+	case 239:
+	case 240:
+	case 241:
+	case 242:
+	case 243:
+	case 244:
+	case 245:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 27:
+	case 246:
 		external_slot(context);
 		break;
-	case 28:
+	case 247:
 		address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
 		mask = 0;
 		if (context->regs[REG_MODE_3] & 0x2) {
@@ -1115,41 +1131,41 @@
 		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
 		//printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
 		break;
-	case 29:
-	case 30:
-	case 31:
-	case 32:
+	case 248:
+	case 249:
+	case 250:
+	case 251:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
 	//!HSYNC high
-	case 33:
+	case 252:
 		read_map_scroll_a(0, line, context);
 		break;
-	case 34:
+	case 253:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 35:
+	case 254:
 		render_map_1(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 36:
+	case 255:
 		render_map_2(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 37:
+	case 0:
 		read_map_scroll_b(0, line, context);
 		break;
-	case 38:
+	case 1:
 		render_sprite_cells(context);
 		scan_sprite_table(line, context);
 		break;
-	case 39:
+	case 2:
 		render_map_3(context);
 		scan_sprite_table(line, context);//Just a guess
 		break;
-	case 40:
+	case 3:
 		render_map_output(line, 0, context);
 		scan_sprite_table(line, context);//Just a guess
 		//reverse context slot counter so it counts the number of sprite slots
@@ -1159,26 +1175,22 @@
 		context->sprite_draws = MAX_DRAWS_H32;
 		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
 		break;
-	COLUMN_RENDER_BLOCK(2, 41)
-	COLUMN_RENDER_BLOCK(4, 49)
-	COLUMN_RENDER_BLOCK(6, 57)
-	COLUMN_RENDER_BLOCK_REFRESH(8, 65)
-	COLUMN_RENDER_BLOCK(10, 73)
-	COLUMN_RENDER_BLOCK(12, 81)
-	COLUMN_RENDER_BLOCK(14, 89)
-	COLUMN_RENDER_BLOCK_REFRESH(16, 97)
-	COLUMN_RENDER_BLOCK(18, 105)
-	COLUMN_RENDER_BLOCK(20, 113)
-	COLUMN_RENDER_BLOCK(22, 121)
-	COLUMN_RENDER_BLOCK_REFRESH(24, 129)
-	COLUMN_RENDER_BLOCK(26, 137)
-	COLUMN_RENDER_BLOCK(28, 145)
-	COLUMN_RENDER_BLOCK(30, 153)
-	COLUMN_RENDER_BLOCK_REFRESH(32, 161)
-	case 169:
-	case 170:
-		external_slot(context);
-		break;
+	COLUMN_RENDER_BLOCK(2, 4)
+	COLUMN_RENDER_BLOCK(4, 12)
+	COLUMN_RENDER_BLOCK(6, 20)
+	COLUMN_RENDER_BLOCK_REFRESH(8, 28)
+	COLUMN_RENDER_BLOCK(10, 36)
+	COLUMN_RENDER_BLOCK(12, 44)
+	COLUMN_RENDER_BLOCK(14, 52)
+	COLUMN_RENDER_BLOCK_REFRESH(16, 60)
+	COLUMN_RENDER_BLOCK(18, 68)
+	COLUMN_RENDER_BLOCK(20, 76)
+	COLUMN_RENDER_BLOCK(22, 84)
+	COLUMN_RENDER_BLOCK_REFRESH(24, 92)
+	COLUMN_RENDER_BLOCK(26, 100)
+	COLUMN_RENDER_BLOCK(28, 108)
+	COLUMN_RENDER_BLOCK(30, 116)
+	COLUMN_RENDER_BLOCK_REFRESH(32, 124)
 	}
 }
 
@@ -1203,6 +1215,14 @@
 		if (context->flags & FLAG_DMA_RUN) {
 			run_dma_src(context, 0);
 		}
+		external_slot(context);
+		if (context->flags & FLAG_DMA_RUN) {
+			run_dma_src(context, 0);
+		}
+		external_slot(context);
+		if (context->flags & FLAG_DMA_RUN) {
+			run_dma_src(context, 0);
+		}
 		for (int i = 0; i < 19; i++)
 		{
 			scan_sprite_table(line, context);
@@ -1240,13 +1260,17 @@
 
 			read_sprite_x(line, context);
 		}
-		external_slot(context);
-		if (context->flags & FLAG_DMA_RUN) {
-			run_dma_src(context, 0);
-		}
-		external_slot(context);
+
 		return;
 	}
+	external_slot(context);
+	if (context->flags & FLAG_DMA_RUN) {
+		run_dma_src(context, 0);
+	}
+	external_slot(context);
+	if (context->flags & FLAG_DMA_RUN) {
+		run_dma_src(context, 0);
+	}
 
 	render_sprite_cells(context);
 	render_sprite_cells(context);
@@ -1356,73 +1380,81 @@
 		render_map_3(context);
 		render_map_output(line, column, context);
 	}
-	external_slot(context);
-	if (context->flags & FLAG_DMA_RUN) {
-		run_dma_src(context, 0);
-	}
-	external_slot(context);
 }
 
 void latch_mode(vdp_context * context)
 {
-	context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
+	context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL;
 }
 
 void check_render_bg(vdp_context * context, int32_t line, uint32_t slot)
 {
-	if (line > 0) {
-		line -= 1;
-		int starti = -1;
-		if (context->latched_mode & BIT_H40) {
-			if (slot >= 55 && slot < 210) {
-				uint32_t x = (slot-55)*2;
-				starti = line * 320 + x;
-			} else if (slot < 5) {
-				uint32_t x = (slot + 155)*2;
-				starti = (line-1)*320 + x;
+	int starti = -1;
+	if (context->regs[REG_MODE_4] & BIT_H40) {
+		if (slot >= 12 && slot < 172) {
+			uint32_t x = (slot-12)*2;
+			starti = line * 320 + x;
+		}
+	} else {
+		if (slot >= 11 && slot < 139) {
+			uint32_t x = (slot-11)*2;
+			starti = line * 320 + x;
+		}
+	}
+	if (starti >= 0) {
+		if (context->b32) {
+			uint32_t color = context->colors[context->regs[REG_BG_COLOR]];
+			uint32_t * start = context->framebuf;
+			start += starti;
+			for (int i = 0; i < 2; i++) {
+				*(start++) = color;
 			}
 		} else {
-			if (slot >= 48 && slot < 171) {
-				uint32_t x = (slot-48)*2;
-				starti = line * 320 + x;
-			} else if (slot < 5) {
-				uint32_t x = (slot + 123)*2;
-				starti = (line-1)*320 + x;
-			}
-		}
-		if (starti >= 0) {
-			if (context->b32) {
-				uint32_t color = context->colors[context->regs[REG_BG_COLOR]];
-				uint32_t * start = context->framebuf;
-				start += starti;
-				for (int i = 0; i < 2; i++) {
-					*(start++) = color;
-				}
-			} else {
-				uint16_t color = context->colors[context->regs[REG_BG_COLOR]];
-				uint16_t * start = context->framebuf;
-				start += starti;
-				for (int i = 0; i < 2; i++) {
-					*(start++) = color;
-				}
+			uint16_t color = context->colors[context->regs[REG_BG_COLOR]];
+			uint16_t * start = context->framebuf;
+			start += starti;
+			for (int i = 0; i < 2; i++) {
+				*(start++) = color;
 			}
 		}
 	}
 }
 
+uint32_t h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19};
+
 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
 {
 	while(context->cycles < target_cycles)
 	{
 		context->flags &= ~FLAG_UNUSED_SLOT;
-		uint32_t line = context->cycles / MCLKS_LINE;
-		uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
-		if (!context->cycles) {
+		uint32_t line = context->vcounter;
+		uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
+		uint32_t slot = context->hslot;
+		//TODO: Figure out when this actually happens
+		if (!line && !slot) {
 			latch_mode(context);
 		}
-		uint32_t linecyc = context->cycles % MCLKS_LINE;
-		if (linecyc == 0) {
-			if (line <= 1 || line >= active_lines) {
+
+		uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
+		if (is_h40) {
+			if (slot == 167) {
+				context->cur_slot = MAX_DRAWS-1;
+				memset(context->linebuf, 0, LINEBUF_SIZE);
+			} else if (slot == 171) {
+				context->sprite_index = 0x80;
+				context->slot_counter = MAX_SPRITES_LINE;
+			}
+		} else {
+			if (slot == 134) {
+				context->cur_slot = MAX_DRAWS_H32-1;
+				memset(context->linebuf, 0, LINEBUF_SIZE);
+			} else if (slot == 138) {
+				context->sprite_index = 0x80;
+				context->slot_counter = MAX_SPRITES_LINE_H32;
+			}
+		}
+		if (is_h40 && slot == LINE_CHANGE_H40 || !is_h40 && slot == LINE_CHANGE_H32) {
+			if (line >= inactive_start) {
 				context->hint_counter = context->regs[REG_HINT];
 			} else if (context->hint_counter) {
 				context->hint_counter--;
@@ -1430,111 +1462,41 @@
 				context->flags2 |= FLAG2_HINT_PENDING;
 				context->hint_counter = context->regs[REG_HINT];
 			}
-		} else if(line == active_lines) {
-			uint32_t intcyc = context->latched_mode & BIT_H40 ? VINT_CYCLE_H40 :  VINT_CYCLE_H32;
-			if (linecyc == intcyc) {
+		} else if(line == inactive_start) {
+			uint32_t intslot = context->regs[REG_MODE_4] & BIT_H40 ? VINT_SLOT_H40 :  VINT_SLOT_H32;
+			if (slot == intslot) {
 				context->flags2 |= FLAG2_VINT_PENDING;
 			}
 		}
-		uint32_t inccycles, slot;
-		if (context->latched_mode & BIT_H40){
-			if (linecyc < MCLKS_SLOT_H40*HSYNC_SLOT_H40) {
-				slot = linecyc/MCLKS_SLOT_H40;
+		uint32_t inccycles;
+		//line 0x1FF is basically active even though it's not displayed
+		uint8_t active_slot = line < inactive_start || line == 0x1FF;
+		if (is_h40) {
+			if (slot < HSYNC_SLOT_H40 || slot >= HSYNC_END_H40) {
 				inccycles = MCLKS_SLOT_H40;
-			} else if(linecyc < MCLK_WEIRD_END) {
-				switch(linecyc-(MCLKS_SLOT_H40*HSYNC_SLOT_H40))
-				{
-				case 0:
-					inccycles = 19;
-					slot = 0;
-					break;
-				case 19:
-					slot = 1;
-					inccycles = 20;
-					break;
-				case 39:
-					slot = 2;
-					inccycles = 20;
-					break;
-				case 59:
-					slot = 3;
-					inccycles = 20;
-					break;
-				case 79:
-					slot = 4;
-					inccycles = 18;
-					break;
-				case 97:
-					slot = 5;
-					inccycles = 20;
-					break;
-				case 117:
-					slot = 6;
-					inccycles = 20;
-					break;
-				case 137:
-					slot = 7;
-					inccycles = 20;
-					break;
-				case 157:
-					slot = 8;
-					inccycles = 18;
-					break;
-				case 175:
-					slot = 9;
-					inccycles = 20;
-					break;
-				case 195:
-					slot = 10;
-					inccycles = 20;
-					break;
-				case 215:
-					slot = 11;
-					inccycles = 20;
-					break;
-				case 235:
-					slot = 12;
-					inccycles = 18;
-					break;
-				case 253:
-					slot = 13;
-					inccycles = 20;
-					break;
-				case 273:
-					slot = 14;
-					inccycles = 20;
-					break;
-				case 293:
-					slot = 15;
-					inccycles = 20;
-					break;
-				case 313:
-					slot = 16;
-					inccycles = 19;
-					break;
-				default:
-					fprintf(stderr, "cycles after weirdness %d\n", linecyc-(MCLKS_SLOT_H40*HSYNC_SLOT_H40));
-					exit(1);
-				}
-				slot += HSYNC_SLOT_H40;
 			} else {
-				slot = (linecyc-MCLK_WEIRD_END)/MCLKS_SLOT_H40 + SLOT_WEIRD_END;
-				inccycles = MCLKS_SLOT_H40;
+				inccycles = h40_hsync_cycles[slot-HSYNC_SLOT_H40];
+			}
+			//the first inactive line behaves as an active one for the first 4 slots
+			if (line == inactive_start && slot > 166 && slot < 171) {
+				active_slot = 1;
 			}
 		} else {
 			inccycles = MCLKS_SLOT_H32;
-			slot = linecyc/MCLKS_SLOT_H32;
+			//the first inactive line behaves as an active one for the first 4 slots
+			if (line == inactive_start && slot > 166 && slot < 171) {
+				active_slot = 1;
+			}
 		}
-		if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) {
-			//first sort-of active line is treated as 255 internally
-			//it's used for gathering sprite info for line
-			line = (line - 1) & 0xFF;
-
-			//Convert to slot number
-			if (context->latched_mode & BIT_H40){
-				if (!slot && line != (active_lines-1) && (target_cycles - context->cycles) >= MCLKS_LINE) {
+		uint8_t inc_slot = 1;
+		if (context->regs[REG_MODE_2] & DISPLAY_ENABLE && active_slot) {
+			//run VDP rendering for a slot or a line
+			if (is_h40) {
+				if (slot == LINE_CHANGE_H40 && line < inactive_start && (target_cycles - context->cycles) >= MCLKS_LINE) {
 					vdp_h40_line(line, context);
 					inccycles = MCLKS_LINE;
+					context->vcounter++;
+					inc_slot = 0;
 				} else {
 					vdp_h40(line, slot, context);
 				}
@@ -1545,20 +1507,50 @@
 			if (!is_refresh(context, slot)) {
 				external_slot(context);
 			}
-			if (line < active_lines) {
+			if (line < inactive_start) {
 				check_render_bg(context, line, slot);
 			}
 		}
 		if (context->flags & FLAG_DMA_RUN && !is_refresh(context, slot)) {
 			run_dma_src(context, slot);
 		}
+		if (inc_slot) {
+			context->hslot++;
+			context->hslot &= 0xFF;
+			if (is_h40) {
+				if (context->hslot == LINE_CHANGE_H40) {
+					context->vcounter++;
+				} else if (context->hslot == 183) {
+					context->hslot = 229;
+				}
+			} else {
+				if (context->hslot == LINE_CHANGE_H32) {
+					context->vcounter++;
+				} else if (context->hslot == 148) {
+					context->hslot = 233;
+				}
+			}
+
+		}
+		context->vcounter &= 0x1FF;
+		if (context->flags2 & FLAG2_REGION_PAL) {
+			if (context->latched_mode & BIT_PAL) {
+				if (context->vcounter == 0x10B) {
+					context->vcounter = 0x1D2;
+				}
+			} else if (context->vcounter == 0x103){
+				context->vcounter = 0x1CA;
+			}
+		} else if (!(context->latched_mode & BIT_PAL) &&  context->vcounter == 0xEB) {
+			context->vcounter = 0x1E5;
+		}
 		context->cycles += inccycles;
 	}
 }
 
 uint32_t vdp_run_to_vblank(vdp_context * context)
 {
-	uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE;
+	uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_INACTIVE_START : NTSC_INACTIVE_START) * MCLKS_LINE;
 	vdp_run_context(context, target_cycles);
 	return context->cycles;
 }
@@ -1570,7 +1562,7 @@
 		if (!dmalen) {
 			dmalen = 0x10000;
 		}
-		uint32_t min_dma_complete = dmalen * (context->latched_mode & BIT_H40 ? 16 : 20);
+		uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
 		if ((context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 || (context->cd & 0xF) == VRAM_WRITE) {
 			//DMA copies take twice as long to complete since they require a read and a write
 			//DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
@@ -1661,10 +1653,10 @@
 		context->flags &= ~FLAG_DMA_RUN;
 	}
 	while (context->fifo_write == context->fifo_read) {
-		vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+		vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
 	}
 	fifo_entry * cur = context->fifo + context->fifo_write;
-	cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
+	cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
 	cur->address = context->address;
 	cur->value = value;
 	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
@@ -1709,13 +1701,25 @@
 	if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && context->framebuf == context->oddbuf) {
 		value |= 0x10;
 	}
-	uint32_t line= context->cycles / MCLKS_LINE;
-	uint32_t linecyc = context->cycles % MCLKS_LINE;
-	if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) {
+	uint32_t line= context->vcounter;
+	uint32_t slot = context->hslot;
+	if (
+		(
+			line >= (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)
+			&& line < 0x1FF
+		)
+		|| !(context->regs[REG_MODE_2] & BIT_DISP_EN)
+	) {
 		value |= 0x8;
 	}
-	if (linecyc < (context->latched_mode & BIT_H40 ? HBLANK_CLEAR_H40 : HBLANK_CLEAR_H32)) {
-		value |= 0x4;
+	if (context->regs[REG_MODE_4] & BIT_H40) {
+		if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) {
+			value |= 0x4;
+		}
+	} else {
+		if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) {
+			value |= 0x4;
+		}
 	}
 	if (context->flags & FLAG_DMA_RUN) {
 		value |= 0x2;
@@ -1741,7 +1745,7 @@
 	context->flags &= ~FLAG_UNUSED_SLOT;
 	//context->flags2 |= FLAG2_READ_PENDING;
 	while (!(context->flags & FLAG_UNUSED_SLOT)) {
-		vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+		vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
 	}
 	uint16_t value = 0;
 	switch (context->cd & 0xF)
@@ -1751,7 +1755,7 @@
 		context->flags &= ~FLAG_UNUSED_SLOT;
 		context->flags2 |= FLAG2_READ_PENDING;
 		while (!(context->flags & FLAG_UNUSED_SLOT)) {
-			vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+			vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
 		}
 		value |= context->vdpmem[context->address | 1];
 		break;
@@ -1782,102 +1786,8 @@
 	if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) {
 		return context->hv_latch;
 	}
-	uint32_t line= context->cycles / MCLKS_LINE;
-	if (!line) {
-		line = 0xFF;
-	} else {
-		line--;
-		if (line > 0xEA) {
-			line = (line + 0xFA) & 0xFF;
-		}
-	}
-	uint32_t linecyc = context->cycles % MCLKS_LINE;
-	if (context->latched_mode & BIT_H40) {
-		uint32_t slot;
-		if (linecyc < MCLKS_SLOT_H40*HSYNC_SLOT_H40) {
-			slot = linecyc/MCLKS_SLOT_H40;
-		} else if(linecyc < MCLK_WEIRD_END) {
-			switch(linecyc-(MCLKS_SLOT_H40*HSYNC_SLOT_H40))
-			{
-			case 0:
-				slot = 0;
-				break;
-			case 19:
-				slot = 1;
-				break;
-			case 39:
-				slot = 2;
-				break;
-			case 59:
-				slot = 2;
-				break;
-			case 79:
-				slot = 3;
-				break;
-			case 97:
-				slot = 4;
-				break;
-			case 117:
-				slot = 5;
-				break;
-			case 137:
-				slot = 6;
-				break;
-			case 157:
-				slot = 7;
-				break;
-			case 175:
-				slot = 8;
-				break;
-			case 195:
-				slot = 9;
-				break;
-			case 215:
-				slot = 11;
-				break;
-			case 235:
-				slot = 12;
-				break;
-			case 253:
-				slot = 13;
-				break;
-			case 273:
-				slot = 14;
-				break;
-			case 293:
-				slot = 15;
-				break;
-			case 313:
-				slot = 16;
-				break;
-			default:
-				fprintf(stderr, "cycles after weirdness %d\n", linecyc-(MCLKS_SLOT_H40*HSYNC_SLOT_H40));
-				exit(1);
-			}
-			slot += HSYNC_SLOT_H40;
-		} else {
-			slot = (linecyc-MCLK_WEIRD_END)/MCLKS_SLOT_H40 + SLOT_WEIRD_END;
-		}
-		linecyc = slot * 2;
-		if (linecyc >= 86) {
-			linecyc -= 86;
-		} else {
-			linecyc += 334;
-		}
-		if (linecyc > 0x16C) {
-			linecyc += 92;
-		}
-	} else {
-		linecyc /= 10;
-		if (linecyc >= 74) {
-			linecyc -= 74;
-		} else {
-			linecyc += 268;
-		}
-		if (linecyc > 0x127) {
-			linecyc += 170;
-		}
-	}
+	uint32_t line= context->vcounter & 0xFF;
+	uint32_t linecyc = context->hslot;
 	linecyc &= 0xFF;
 	if (context->double_res) {
 		line <<= 1;
@@ -1910,6 +1820,88 @@
 	}
 }
 
+uint32_t vdp_cycles_next_line(vdp_context * context)
+{
+	if (context->regs[REG_MODE_4] & BIT_H40) {
+		if (context->hslot < LINE_CHANGE_H40) {
+			return (HBLANK_START_H40 - context->hslot) * MCLKS_SLOT_H40;
+		} else if (context->hslot < 183) {
+			return MCLKS_LINE - (context->hslot - LINE_CHANGE_H40) * MCLKS_SLOT_H40;
+		} else {
+			return (256-context->hslot + LINE_CHANGE_H40) * MCLKS_SLOT_H40;
+		}
+	} else {
+		if (context->hslot < LINE_CHANGE_H32) {
+			return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32;
+		} else if (context->hslot < 148) {
+			return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
+		} else {
+			return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32;
+		}
+	}
+}
+
+uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target)
+{
+	uint32_t jump_start, jump_dst;
+	if (context->flags2 & FLAG2_REGION_PAL) {
+		if (context->latched_mode & BIT_PAL) {
+			jump_start = 0x10B;
+			jump_dst = 0x1D2;
+		} else {
+			jump_start = 0x103;
+			jump_dst = 0x1CA;
+		}
+	} else {
+		if (context->latched_mode & BIT_PAL) {
+			jump_start = 0;
+			jump_dst = 0;
+		} else {
+			jump_start = 0xEB;
+			jump_dst = 0x1E5;
+		}
+	}
+	uint32_t lines;
+	if (context->vcounter < target) {
+		if (target < jump_start) {
+			lines = target - context->vcounter;
+		} else {
+			lines = jump_start - context->vcounter + target - jump_dst;
+		}
+	} else {
+		if (context->vcounter < jump_start) {
+			lines = jump_start - context->vcounter + 512 - jump_dst;
+		} else {
+			lines = 512 - context->vcounter;
+		}
+		if (target < jump_start) {
+			lines += target;
+		} else {
+			lines += jump_start + target - jump_dst;
+		}
+	}
+	return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context);
+}
+
+uint32_t vdp_cycles_to_frame_end(vdp_context * context)
+{
+	uint32_t frame_end;
+	if (context->flags2 & FLAG2_REGION_PAL) {
+		if (context->latched_mode & BIT_PAL) {
+			frame_end = PAL_INACTIVE_START + 8;
+		} else {
+			frame_end = NTSC_INACTIVE_START + 8;
+		}
+	} else {
+		if (context->latched_mode & BIT_PAL) {
+			frame_end = 512;
+		} else {
+			frame_end = NTSC_INACTIVE_START + 8;
+		}
+	}
+	return context->cycles + vdp_cycles_to_line(context, frame_end);
+}
+
 uint32_t vdp_next_hint(vdp_context * context)
 {
 	if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) {
@@ -1918,17 +1910,15 @@
 	if (context->flags2 & FLAG2_HINT_PENDING) {
 		return context->cycles;
 	}
-	uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
-	uint32_t line = context->cycles / MCLKS_LINE;
-	if (line >= active_lines) {
-		return 0xFFFFFFFF;
+	uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
+	uint32_t hint_line;
+	if (context->vcounter >= inactive_start) {
+		hint_line = context->regs[REG_HINT];
+	} else {
+		hint_line = context->vcounter + context->hint_counter + 1;
 	}
-	uint32_t linecyc = context->cycles % MCLKS_LINE;
-	uint32_t hcycle = context->cycles + context->hint_counter * MCLKS_LINE + MCLKS_LINE - linecyc;
-	if (!line) {
-		hcycle += MCLKS_LINE;
-	}
-	return hcycle;
+
+	return context->cycles + vdp_cycles_to_line(context, hint_line);
 }
 
 uint32_t vdp_next_vint(vdp_context * context)
@@ -1939,29 +1929,44 @@
 	if (context->flags2 & FLAG2_VINT_PENDING) {
 		return context->cycles;
 	}
-	uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
-	uint32_t vcycle =  MCLKS_LINE * active_lines;
-	if (context->latched_mode & BIT_H40) {
-		vcycle += VINT_CYCLE_H40;
-	} else {
-		vcycle += VINT_CYCLE_H32;
-	}
-	if (vcycle < context->cycles) {
-		return 0xFFFFFFFF;
-	}
-	return vcycle;
+
+
+	return vdp_next_vint_z80(context);
 }
 
 uint32_t vdp_next_vint_z80(vdp_context * context)
 {
-	uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
-	uint32_t vcycle =  MCLKS_LINE * active_lines;
-	if (context->latched_mode & BIT_H40) {
-		vcycle += VINT_CYCLE_H40;
+	uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
+	if (context->vcounter == inactive_start) {
+		if (context->regs[REG_MODE_4] & BIT_H40) {
+			if (context->hslot >= HBLANK_START_H40) {
+				if (context->hslot < 183) {
+					return context->cycles + (VINT_SLOT_H40 + 183 - context->hslot + 256 - 229) * MCLKS_SLOT_H40;
+				} else {
+					return context->cycles + (VINT_SLOT_H40 + 256 - context->hslot) * MCLKS_SLOT_H40;
+				}
+			} else if (context->hslot < VINT_SLOT_H40) {
+				return context->cycles + (VINT_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40;
+			}
+		} else {
+			if (context->hslot >= HBLANK_START_H32) {
+				if (context->hslot < 148) {
+					return context->cycles + (VINT_SLOT_H32 + 148 - context->hslot + 256 - 233) * MCLKS_SLOT_H32;
+				} else {
+					return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32;
+				}
+			} else if (context->hslot < VINT_SLOT_H32) {
+				return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32;
+			}
+		}
+	}
+	int32_t cycles_to_vint = vdp_cycles_to_line(context, inactive_start);
+	if (context->regs[REG_MODE_4] & BIT_H40) {
+		cycles_to_vint += (VINT_SLOT_H40 + 183 - HBLANK_START_H40 + 256 - 229) * MCLKS_SLOT_H40;
 	} else {
-		vcycle += VINT_CYCLE_H32;
+		cycles_to_vint += (VINT_SLOT_H32 + 148 - HBLANK_START_H32 + 256 - 233) * MCLKS_SLOT_H32;
 	}
-	return vcycle;
+	return context->cycles + cycles_to_vint;
 }
 
 void vdp_int_ack(vdp_context * context, uint16_t int_num)
--- a/vdp.h	Mon Dec 29 23:08:39 2014 -0800
+++ b/vdp.h	Tue Dec 30 19:11:34 2014 -0800
@@ -49,6 +49,7 @@
 #define FLAG2_HINT_PENDING   0x02
 #define FLAG2_READ_PENDING   0x04
 #define FLAG2_SPRITE_COLLIDE 0x08
+#define FLAG2_REGION_PAL     0x10
 
 #define DISPLAY_ENABLE 0x40
 
@@ -142,9 +143,11 @@
 	uint32_t    colors[CRAM_SIZE*3];
 	uint32_t    debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight
 	uint16_t    vsram[VSRAM_SIZE];
-	uint8_t     latched_mode;
+	uint16_t    vcounter;
+	uint16_t    hslot; //hcounter/2
 	uint16_t    hscroll_a;
 	uint16_t    hscroll_b;
+	uint8_t     latched_mode;
 	uint8_t	    sprite_index;
 	uint8_t     sprite_draws;
 	int8_t      slot_counter;
@@ -167,7 +170,7 @@
 	uint8_t     *tmp_buf_b;
 } vdp_context;
 
-void init_vdp_context(vdp_context * context);
+void init_vdp_context(vdp_context * context, uint8_t region_pal);
 void vdp_run_context(vdp_context * context, uint32_t target_cycles);
 //runs from current cycle count to VBLANK for the current mode, returns ending cycle count
 uint32_t vdp_run_to_vblank(vdp_context * context);
@@ -190,6 +193,7 @@
 void vdp_print_sprite_table(vdp_context * context);
 void vdp_print_reg_explain(vdp_context * context);
 void latch_mode(vdp_context * context);
+uint32_t vdp_cycles_to_frame_end(vdp_context * context);
 
 extern int32_t color_map[1 << 12];
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vos_prog_info.c	Tue Dec 30 19:11:34 2014 -0800
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include "vos_program_module.h"
+
+int main(int argc, char ** argv)
+{
+	vos_program_module header;
+	FILE * f = fopen(argv[1], "rb");
+	vos_read_header(f, &header);
+	vos_read_alloc_module_map(f, &header);
+	vos_read_alloc_external_vars(f, &header);
+
+	printf("Version: %d\n", header.version);
+	printf("Binder Version: %s\n", header.binder_version.str);
+	printf("Binder Options: %s\n", header.binder_options.str);
+	printf("System name: %s\n", header.system_name.str);
+	printf("User name: %s\n", header.user_name.str);
+	printf("Date bound: %d\n", header.date_bound);
+	printf("Code addresss: 0x%X, Static address: 0x%X\n",
+	       header.main_entry_link.code_address, header.main_entry_link.static_address);
+	printf("User boundary: 0x%X\n", header.user_boundary);
+	printf("Num modules: %d\n", header.n_modules);
+	printf("Num extern vars: %d\n", header.n_external_vars);
+	printf("Num link names: %d\n", header.n_link_names);
+	printf("Num unsapped links: %d\n", header.n_unsnapped_links);
+	printf("Num VM pages: %d\n", header.n_vm_pages);
+	printf("Num header pages: %d\n", header.n_header_pages);
+	for (int i = 0; i < 3; i++) {
+		for (int j = 0; j < 4; j++) {
+			printf("Info %d:%d\n\tAddress: 0x%X\n\tLength: 0x%X\n",
+			       i, j, header.info[i][j].address, header.info[i][j].len);
+		}
+	}
+	printf("Module map address: 0x%X\n", header.module_map_address);
+	printf("Module map length: 0x%X\n", header.module_map_len);
+	printf("External vars map address: 0x%X\n", header.external_vars_map_address);
+	printf("External vars map length: 0x%X\n", header.external_vars_map_len);
+	printf("Link names map address: 0x%X\n", header.link_names_map_address);
+	printf("Link names map length: 0x%X\n", header.link_names_map_len);
+	printf("Header address: 0x%X\n", header.header_address);
+	printf("Header length: 0x%X\n", header.header_len);
+	//printf("Access Info: 0x%X\n", header.header_address);
+	printf("Flags: 0x%X\n", header.flags);
+	printf("Num tasks: %d\n", header.n_tasks);
+	printf("Stack Size: 0x%X\n", header.stack_len);
+	printf("Num entries: %d\n", header.n_entries);
+	printf("Entry map address: 0x%X\n", header.entry_map_address);
+	printf("Entry map length: 0x%X\n", header.entry_map_len);
+	printf("Pop Version: %d\n", header.pop_version);
+	printf("Processor: %d\n", header.processor);
+	printf("Processor family: %d\n", header.processor_family);
+	printf("Release name: %s\n", header.release_name.str);
+	printf("Relocation info:\n\tMap Addres: 0x%X\n\tMap Length: 0x%X\n\tNum Relocations: %d\n",
+	       header.relocation_info.map_address, header.relocation_info.map_len,
+		   header.relocation_info.n_relocations);
+	printf("High water mark: 0x%X\n", header.high_water_mark);
+	printf("Copyright notice: %s\n", header.program_name.str);
+	printf("String pool address: 0x%X\n", header.string_pool_address);
+	printf("String pool length: 0x%X\n", header.string_pool_len);
+	printf("Object dir map address: 0x%X\n", header.obj_dir_map_address);
+	printf("Object dir map length: 0x%X\n", header.obj_dir_map_len);
+	puts("Global offset table addresses:");
+	for (int i = 0; i < 3; i++) {
+		printf("\t%d: 0x%X\n", i, header.global_offset_table_address[i]);
+	}
+	for (int i = 0; i < 3; i++) {
+		printf("Block map info %d\n\tAddress: 0x%X\n\tLength: 0x%X\n",
+			   i, header.block_map_info[i].address, header.block_map_info[i].len);
+	}
+	printf("Secton map file address: 0x%X\n", header.section_map_file_address);
+	printf("Secton map address: 0x%X\n", header.section_map_address);
+	printf("Secton map length: 0x%X\n", header.section_map_len);
+	printf("Num sections: %d\n", header.n_sections);
+	printf("Max heap size: 0x%X\n", header.max_heap_size);
+	printf("Max program size: 0x%X\n", header.max_program_size);
+	printf("Max stack size: 0x%X\n", header.max_stack_size);
+	printf("Stack fence size: 0x%X\n", header.stack_fence_size);
+
+	puts("\nModules");
+	for (int i = 0; i < header.n_modules; i++) {
+		printf("\t%s:\n\t\tCode Address: 0x%X, Length: 0x%X\n",
+			   header.module_map_entries[i].name.str,
+			   header.module_map_entries[i].code_address,
+			   header.module_map_entries[i].code_length);
+		printf("\t\tFoo Address: 0x%X, Length: 0x%X\n",
+		       header.module_map_entries[i].foo_address,
+			   header.module_map_entries[i].foo_length);
+		printf("\t\tBar Address: 0x%X, Length: 0x%X\n",
+		       header.module_map_entries[i].bar_address,
+			   header.module_map_entries[i].bar_length);
+	}
+
+	puts("\nExtrnal Vars");
+	for (int i = 0; i < header.n_external_vars; i++) {
+		printf("\t%s: 0x%X\n",
+		       header.external_vars[i].name.str, header.external_vars[i].address);
+	}
+
+	vos_header_cleanup(&header);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vos_program_module.c	Tue Dec 30 19:11:34 2014 -0800
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include "vos_program_module.h"
+
+static uint16_t big16(uint8_t ** src)
+{
+	uint16_t ret = *((*src)++) << 8;
+	ret |= *((*src)++);
+	return ret;
+}
+
+static uint32_t big32(uint8_t ** src)
+{
+	uint32_t ret = *((*src)++) << 24;
+	ret |= *((*src)++) << 16;
+	ret |= *((*src)++) << 8;
+	ret |= *((*src)++);
+	return ret;
+}
+
+static void string_(uint8_t ** src, uint16_t *len, char * str, uint32_t storage)
+{
+	*len = big16(src);
+	memcpy(str, *src, storage);
+	*src += storage;
+	if (*len >= storage)
+	{
+		*len = storage;
+	} else {
+		str[*len] = 0;
+	}
+	if (storage & 1)
+	{
+		(*src)++;
+	}
+}
+
+#define string(src, field) string_(src, &(field).len, (field).str, sizeof((field).str))
+
+
+int vos_read_header(FILE * f, vos_program_module *out)
+{
+	uint8_t buffer[4096];
+	if (fread(buffer, 1, sizeof(buffer), f) != sizeof(buffer))
+	{
+		return 0;
+	}
+	uint8_t *cur = buffer;
+	out->version = big16(&cur);
+	string(&cur, out->binder_version);
+	string(&cur, out->binder_options);
+	string(&cur, out->system_name);
+	string(&cur, out->user_name);
+	out->date_bound = big32(&cur);
+	out->main_entry_link.code_address = big32(&cur);
+	out->main_entry_link.static_address = big32(&cur);
+	out->user_boundary = big32(&cur);
+	out->n_modules = big16(&cur);
+	out->n_external_vars = big16(&cur);
+	out->n_link_names = big16(&cur);
+	out->n_unsnapped_links = big16(&cur);
+	out->n_vm_pages = big16(&cur);
+	out->n_header_pages = big16(&cur);
+	for (int i = 0; i < 3; i++)
+	{
+		for (int j = 0; j < 4; j++)
+		{
+			out->info[i][j].address = big32(&cur);
+			out->info[i][j].len = big32(&cur);
+		}
+	}
+	out->module_map_address = big32(&cur);
+	out->module_map_len = big32(&cur);
+	out->external_vars_map_address = big32(&cur);
+	out->external_vars_map_len = big32(&cur);
+	out->link_names_map_address = big32(&cur);
+	out->link_names_map_len = big32(&cur);
+	out->link_map_address = big32(&cur);
+	out->link_map_len = big32(&cur);
+	out->header_address = big32(&cur);
+	out->header_len = big32(&cur);
+	memcpy(out->access_info, cur, sizeof(out->access_info));
+	cur += sizeof(out->access_info);
+	out->flags = big32(&cur);
+	out->n_tasks = big16(&cur);
+	for (int i = 0; i < 3; i++)
+	{
+		out->task_static_len[i] = big32(&cur);
+	}
+	out->stack_len = big32(&cur);
+	out->n_entries = big16(&cur);
+	out->entry_map_address = big32(&cur);
+	out->entry_map_len = big32(&cur);
+	out->pop_version = big16(&cur);
+	out->processor = big16(&cur);
+	string(&cur, out->release_name);
+	out->relocation_info.map_address = big32(&cur);
+	out->relocation_info.map_len = big32(&cur);
+	out->relocation_info.n_relocations = big32(&cur);
+	out->high_water_mark = big32(&cur);
+	string(&cur, out->copyright_notice);
+	for (int i = 0; i < 14; i++)
+	{
+		out->module_origins[i] = big32(&cur);
+	}
+	out->processor_family = big16(&cur);
+	string(&cur, out->program_name);
+	out->string_pool_address = big32(&cur);
+	out->string_pool_len = big32(&cur);
+	out->obj_dir_map_address = big32(&cur);
+	out->obj_dir_map_len = big32(&cur);
+	for (int i = 0; i < 3; i++)
+	{
+		out->global_offset_table_address[i] = big32(&cur);
+	}
+	for (int i = 0; i < 3; i++)
+	{
+		out->block_map_info[i].address = big32(&cur);
+		out->block_map_info[i].len = big32(&cur);
+	}
+	out->section_map_file_address = big32(&cur);
+	out->section_map_address = big32(&cur);
+	out->section_map_len = big32(&cur);
+	out->n_sections = big16(&cur);
+	out->max_heap_size = big32(&cur);
+	out->max_program_size = big32(&cur);
+	out->max_stack_size = big32(&cur);
+	out->stack_fence_size = big32(&cur);
+
+	out->module_map_entries = NULL;
+	out->external_vars = NULL;
+	return 1;
+}
+
+#define MODULE_MAP_ENTRY_SIZE 74
+
+int vos_read_alloc_module_map(FILE * f, vos_program_module *header)
+{
+	if (header->module_map_len != header->n_modules * MODULE_MAP_ENTRY_SIZE)
+	{
+		return 0;
+	}
+	uint8_t * buf = malloc(header->module_map_len);
+	fseek(f, header->module_map_address + 0x1000 - header->user_boundary, SEEK_SET);
+	if (fread(buf, 1, header->module_map_len, f) != header->module_map_len)
+	{
+		free(buf);
+		return 0;
+	}
+	uint8_t * cur = buf;
+	header->module_map_entries = malloc(sizeof(vos_module_map_entry) * header->n_modules);
+	for (int i = 0; i < header->n_modules; i++)
+	{
+		string(&cur, header->module_map_entries[i].name);
+		for (int j = 0; j < 5; j++)
+		{
+			header->module_map_entries[i].unknown[j] = big16(&cur);
+		}
+		header->module_map_entries[i].code_address = big32(&cur);
+		header->module_map_entries[i].code_length = big32(&cur);
+		header->module_map_entries[i].foo_address = big32(&cur);
+		header->module_map_entries[i].foo_length = big32(&cur);
+		header->module_map_entries[i].bar_address = big32(&cur);
+		header->module_map_entries[i].bar_length = big32(&cur);
+		for (int j = 0; j < 3; j++)
+		{
+			header->module_map_entries[i].unknown2[j] = big16(&cur);
+		}
+	}
+	return 1;
+}
+
+#define EXTERNAL_VAR_ENTRY_SIZE 44
+
+int vos_read_alloc_external_vars(FILE * f, vos_program_module *header)
+{
+	if (header->external_vars_map_len != header->n_external_vars * EXTERNAL_VAR_ENTRY_SIZE)
+	{
+		return 0;
+	}
+	uint8_t * buf = malloc(header->external_vars_map_len);
+	fseek(f, header->external_vars_map_address + 0x1000 - header->user_boundary, SEEK_SET);
+	if (fread(buf, 1, header->external_vars_map_len, f) != header->external_vars_map_len)
+	{
+		free(buf);
+		return 0;
+	}
+	uint8_t * cur = buf;
+	header->external_vars = malloc(sizeof(vos_external_var_entry) * header->n_external_vars);
+	for (int i = 0; i < header->n_external_vars; i++)
+	{
+		string(&cur, header->external_vars[i].name);
+		header->external_vars[i].address = big32(&cur);
+		for (int j = 0; j < 3; j++)
+		{
+			header->external_vars[i].unknown[j] = big16(&cur);
+		}
+	}
+	return 1;
+}
+
+void vos_header_cleanup(vos_program_module *header)
+{
+	free(header->module_map_entries);
+	free(header->external_vars);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vos_program_module.h	Tue Dec 30 19:11:34 2014 -0800
@@ -0,0 +1,134 @@
+#ifndef VOS_PROGRAM_MODULE_H_
+#define VOS_PROGRAM_MODULE_H_
+
+#include <stdint.h>
+
+typedef struct
+{
+	struct {
+		uint16_t len;
+		char     str[32];
+	} name;
+	uint16_t unknown[5];
+	uint32_t code_address;
+	uint32_t code_length;
+	uint32_t foo_address;
+	uint32_t foo_length;
+	uint32_t bar_address;
+	uint32_t bar_length;
+	uint16_t unknown2[3];
+} vos_module_map_entry;
+
+typedef struct
+{
+	struct {
+		uint16_t len;
+		char     str[32];
+	} name;
+	uint32_t address;
+	uint16_t unknown[3];
+} vos_external_var_entry;
+
+typedef struct
+{
+	uint16_t version;
+	struct {
+		uint16_t len;
+		char     str[32];
+	} binder_version;
+	struct {
+		uint16_t len;
+		char     str[32];
+	} binder_options;
+	struct {
+		uint16_t len;
+		char     str[32];
+	} system_name;
+	struct {
+		uint16_t len;
+		char     str[65];
+	} user_name;
+	uint32_t date_bound;
+	struct {
+		uint32_t code_address;
+		uint32_t static_address;
+	} main_entry_link;
+	uint32_t user_boundary;
+	uint16_t n_modules;
+	uint16_t n_external_vars;
+	uint16_t n_link_names;
+	uint16_t n_unsnapped_links;
+	uint16_t n_vm_pages;
+	uint16_t n_header_pages;
+	struct {
+		uint32_t address;
+		uint32_t len;
+	} info[3][4];
+	uint32_t module_map_address;
+	uint32_t module_map_len;
+	uint32_t external_vars_map_address;
+	uint32_t external_vars_map_len;
+	uint32_t link_names_map_address;
+	uint32_t link_names_map_len;
+	uint32_t link_map_address;
+	uint32_t link_map_len;
+	uint32_t header_address;
+	uint32_t header_len;
+	uint8_t  access_info[2048];
+	uint32_t flags;
+	uint16_t n_tasks;
+	uint32_t task_static_len[3];
+	uint32_t stack_len;
+	uint16_t n_entries;
+	uint32_t entry_map_address;
+	uint32_t entry_map_len;
+	uint16_t pop_version;
+	uint16_t processor;
+	struct {
+		uint16_t len;
+		char     str[32];
+	} release_name;
+	struct {
+		uint32_t map_address;
+		uint32_t map_len;
+		uint32_t n_relocations;
+	} relocation_info;
+	uint32_t high_water_mark;
+	struct {
+		uint16_t len;
+		char     str[256];
+	} copyright_notice;
+	uint32_t module_origins[14];
+	uint16_t processor_family;
+	struct {
+		uint16_t len;
+		char     str[32];
+	} program_name;
+	uint32_t string_pool_address;
+	uint32_t string_pool_len;
+	uint32_t obj_dir_map_address;
+	uint32_t obj_dir_map_len;
+	uint32_t global_offset_table_address[3];
+	struct {
+		uint32_t address;
+		uint32_t len;
+	} block_map_info[3];
+	uint32_t section_map_file_address;
+	uint32_t section_map_address;
+	uint32_t section_map_len;
+	uint16_t n_sections;
+	uint32_t max_heap_size;
+	uint32_t max_program_size;
+	uint32_t max_stack_size;
+	uint32_t stack_fence_size;
+
+	vos_module_map_entry   *module_map_entries;
+	vos_external_var_entry *external_vars;
+} vos_program_module;
+
+int vos_read_header(FILE * f, vos_program_module *out);
+int vos_read_alloc_module_map(FILE * f, vos_program_module *header);
+int vos_read_alloc_external_vars(FILE * f, vos_program_module *header);
+void vos_header_cleanup(vos_program_module *header);
+
+#endif //VOS_PROGRAM_MODULE_H_
--- a/ym2612.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/ym2612.c	Tue Dec 30 19:11:34 2014 -0800
@@ -521,6 +521,7 @@
 	context->selected_part = 0;
 	context->write_cycle = context->current_cycle;
 	context->busy_cycles = BUSY_CYCLES_ADDRESS;
+	context->status |= 0x80;
 }
 
 void ym_address_write_part2(ym2612_context * context, uint8_t address)
@@ -530,6 +531,7 @@
 	context->selected_part = 1;
 	context->write_cycle = context->current_cycle;
 	context->busy_cycles = BUSY_CYCLES_ADDRESS;
+	context->status |= 0x80;
 }
 
 uint8_t fnum_to_keycode[] = {
--- a/z80_to_x86.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/z80_to_x86.c	Tue Dec 30 19:11:34 2014 -0800
@@ -28,6 +28,8 @@
 #define dprintf
 #endif
 
+uint32_t zbreakpoint_patch(z80_context * context, uint16_t address, code_ptr dst);
+
 uint8_t z80_size(z80inst * inst)
 {
 	uint8_t reg = (inst->reg & 0x1F);
@@ -124,7 +126,7 @@
 				ea->base = opts->regs[Z80_IYL];
 				ror_ir(code, 8, opts->regs[Z80_IY], SZ_W);
 			}
-		} else {
+		} else if(opts->regs[inst->ea_reg] >= 0) {
 			ea->base = opts->regs[inst->ea_reg];
 			if (ea->base >= AH && ea->base <= BH && inst->reg != Z80_UNUSED && inst->reg != Z80_USE_IMMED) {
 				uint8_t other_reg = opts->regs[inst->reg];
@@ -134,6 +136,10 @@
 					ror_ir(code, 8, ea->base, SZ_W);
 				}
 			}
+		} else {
+			ea->mode = MODE_REG_DISPLACE8;
+			ea->base = CONTEXT;
+			ea->disp = offsetof(z80_context, regs) + inst->ea_reg;
 		}
 		break;
 	case Z80_REG_INDIRECT:
@@ -292,7 +298,7 @@
 	exit(0);
 }
 
-void translate_z80inst(z80inst * inst, z80_context * context, uint16_t address)
+void translate_z80inst(z80inst * inst, z80_context * context, uint16_t address, uint8_t interp)
 {
 	uint32_t num_cycles;
 	host_ea src_op, dst_op;
@@ -300,7 +306,12 @@
 	z80_options *opts = context->options;
 	uint8_t * start = opts->gen.code.cur;
 	code_info *code = &opts->gen.code;
-	check_cycles_int(&opts->gen, address);
+	if (!interp) {
+		check_cycles_int(&opts->gen, address);
+		if (context->breakpoint_flags[address / sizeof(uint8_t)] & (1 << (address % sizeof(uint8_t)))) {
+			zbreakpoint_patch(context, address, start);
+		}
+	}
 	switch(inst->op)
 	{
 	case Z80_LD:
@@ -350,6 +361,16 @@
 		} else {
 			mov_rdispr(code, src_op.base, src_op.disp, dst_op.base, size);
 		}
+		if (inst->ea_reg == Z80_I && inst->addr_mode == Z80_REG) {
+			//ld a, i sets some flags
+			//TODO: Implement half-carry flag
+			cmp_ir(code, 0, dst_op.base, SZ_B);
+			setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
+			setcc_rdisp(code, CC_S, opts->gen.context_reg, zf_off(ZF_S));
+			mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);;
+			mov_rdispr(code, opts->gen.context_reg, offsetof(z80_context, iff2), SCRATCH1, SZ_B);
+			mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, zf_off(ZF_PV), SZ_B);
+		}
 		z80_save_reg(inst, opts);
 		z80_save_ea(code, inst, opts);
 		if (inst->addr_mode & Z80_DIR) {
@@ -915,10 +936,13 @@
 		setcc_rdisp(code, CC_C, opts->gen.context_reg, zf_off(ZF_C));
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		//TODO: Implement half-carry flag
+		if (inst->immed) {
+			//rlca does not set these flags
 		cmp_ir(code, 0, dst_op.base, SZ_B);
 		setcc_rdisp(code, CC_P, opts->gen.context_reg, zf_off(ZF_PV));
 		setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
 		setcc_rdisp(code, CC_S, opts->gen.context_reg, zf_off(ZF_S));
+		}
 		if (inst->addr_mode != Z80_UNUSED) {
 			z80_save_result(opts, inst);
 			if (src_op.mode != MODE_UNUSED) {
@@ -947,10 +971,13 @@
 		setcc_rdisp(code, CC_C, opts->gen.context_reg, zf_off(ZF_C));
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		//TODO: Implement half-carry flag
+		if (inst->immed) {
+			//rla does not set these flags
 		cmp_ir(code, 0, dst_op.base, SZ_B);
 		setcc_rdisp(code, CC_P, opts->gen.context_reg, zf_off(ZF_PV));
 		setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
 		setcc_rdisp(code, CC_S, opts->gen.context_reg, zf_off(ZF_S));
+		}
 		if (inst->addr_mode != Z80_UNUSED) {
 			z80_save_result(opts, inst);
 			if (src_op.mode != MODE_UNUSED) {
@@ -978,10 +1005,13 @@
 		setcc_rdisp(code, CC_C, opts->gen.context_reg, zf_off(ZF_C));
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		//TODO: Implement half-carry flag
+		if (inst->immed) {
+			//rrca does not set these flags
 		cmp_ir(code, 0, dst_op.base, SZ_B);
 		setcc_rdisp(code, CC_P, opts->gen.context_reg, zf_off(ZF_PV));
 		setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
 		setcc_rdisp(code, CC_S, opts->gen.context_reg, zf_off(ZF_S));
+		}
 		if (inst->addr_mode != Z80_UNUSED) {
 			z80_save_result(opts, inst);
 			if (src_op.mode != MODE_UNUSED) {
@@ -1010,10 +1040,13 @@
 		setcc_rdisp(code, CC_C, opts->gen.context_reg, zf_off(ZF_C));
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		//TODO: Implement half-carry flag
+		if (inst->immed) {
+			//rra does not set these flags
 		cmp_ir(code, 0, dst_op.base, SZ_B);
 		setcc_rdisp(code, CC_P, opts->gen.context_reg, zf_off(ZF_PV));
 		setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
 		setcc_rdisp(code, CC_S, opts->gen.context_reg, zf_off(ZF_S));
+		}
 		if (inst->addr_mode != Z80_UNUSED) {
 			z80_save_result(opts, inst);
 			if (src_op.mode != MODE_UNUSED) {
@@ -1631,18 +1664,73 @@
 	}
 }
 
+uint8_t * z80_interp_handler(uint8_t opcode, z80_context * context)
+{
+	if (!context->interp_code[opcode]) {
+		if (opcode == 0xCB || (opcode >= 0xDD && opcode & 0xF == 0xD)) {
+			fprintf(stderr, "Encountered prefix byte %X at address %X. Z80 interpeter doesn't support those yet.", opcode, context->pc);
+			exit(1);
+		}
+		uint8_t codebuf[8];
+		memset(codebuf, 0, sizeof(codebuf));
+		codebuf[0] = opcode;
+		z80inst inst;
+		uint8_t * after = z80_decode(codebuf, &inst);
+		if (after - codebuf > 1) {
+			fprintf(stderr, "Encountered multi-byte Z80 instruction at %X. Z80 interpeter doesn't support those yet.", context->pc);
+			exit(1);
+		}
+
+		z80_options * opts = context->options;
+		code_info *code = &opts->gen.code;
+		check_alloc_code(code, ZMAX_NATIVE_SIZE);
+		context->interp_code[opcode] = code->cur;
+		translate_z80inst(&inst, context, 0, 1);
+		mov_rdispr(code, opts->gen.context_reg, offsetof(z80_context, pc), opts->gen.scratch1, SZ_W);
+		add_ir(code, after - codebuf, opts->gen.scratch1, SZ_W);
+		call(code, opts->native_addr);
+		jmp_r(code, opts->gen.scratch1);
+	}
+	return context->interp_code[opcode];
+}
+
+code_info z80_make_interp_stub(z80_context * context, uint16_t address)
+{
+	z80_options *opts = context->options;
+	code_info * code = &opts->gen.code;
+	check_alloc_code(code, 32);
+	code_info stub = {code->cur, NULL};
+	//TODO: make this play well with the breakpoint code
+	mov_ir(code, address, opts->gen.scratch1, SZ_W);
+	call(code, opts->read_8);
+	//normal opcode fetch is already factored into instruction timing
+	//back out the base 3 cycles from a read here
+	//not quite perfect, but it will have to do for now
+	cycles(&opts->gen, -3);
+	check_cycles_int(&opts->gen, address);
+	call(code, opts->gen.save_context);
+	mov_rr(code, opts->gen.scratch1, RDI, SZ_B);
+	mov_irdisp(code, address, opts->gen.context_reg, offsetof(z80_context, pc), SZ_W);
+	push_r(code, opts->gen.context_reg);
+	call(code, (code_ptr)z80_interp_handler);
+	mov_rr(code, RAX, opts->gen.scratch1, SZ_Q);
+	pop_r(code, opts->gen.context_reg);
+	call(code, opts->gen.load_context);
+	jmp_r(code, opts->gen.scratch1);
+	stub.last = code->cur;
+	return stub;
+}
+
+
 uint8_t * z80_get_native_address(z80_context * context, uint32_t address)
 {
 	native_map_slot *map;
 	if (address < 0x4000) {
 		address &= 0x1FFF;
 		map = context->static_code_map;
-	} else if (address >= 0x8000) {
-		address &= 0x7FFF;
-		map = context->banked_code_map + context->bank_reg;
 	} else {
-		//dprintf("z80_get_native_address: %X NULL\n", address);
-		return NULL;
+		address -= 0x4000;
+		map = context->banked_code_map;
 	}
 	if (!map->base || !map->offsets || map->offsets[address] == INVALID_OFFSET || map->offsets[address] == EXTENSION_WORD) {
 		//dprintf("z80_get_native_address: %X NULL\n", address);
@@ -1654,6 +1742,7 @@
 
 uint8_t z80_get_native_inst_size(z80_options * opts, uint32_t address)
 {
+	//TODO: Fix for addresses >= 0x4000
 	if (address >= 0x4000) {
 		return 0;
 	}
@@ -1671,15 +1760,14 @@
 		opts->gen.ram_inst_sizes[0][address] = native_size;
 		context->ram_code_flags[(address & 0x1C00) >> 10] |= 1 << ((address & 0x380) >> 7);
 		context->ram_code_flags[((address + size) & 0x1C00) >> 10] |= 1 << (((address + size) & 0x380) >> 7);
-	} else if (address >= 0x8000) {
-		address &= 0x7FFF;
-		map = context->banked_code_map + context->bank_reg;
+	} else {
+		//HERE
+		address -= 0x4000;
+		map = context->banked_code_map;
 		if (!map->offsets) {
-			map->offsets = malloc(sizeof(int32_t) * 0x8000);
-			memset(map->offsets, 0xFF, sizeof(int32_t) * 0x8000);
+			map->offsets = malloc(sizeof(int32_t) * 0xC000);
+			memset(map->offsets, 0xFF, sizeof(int32_t) * 0xC000);
 		}
-	} else {
-		return;
 	}
 	if (!map->base) {
 		map->base = native_address;
@@ -1690,15 +1778,13 @@
 		if (address < 0x4000) {
 			address &= 0x1FFF;
 			map = context->static_code_map;
-		} else if (address >= 0x8000) {
-			address &= 0x7FFF;
-			map = context->banked_code_map + context->bank_reg;
 		} else {
-			return;
+			address -= 0x4000;
+			map = context->banked_code_map;
 		}
 		if (!map->offsets) {
-			map->offsets = malloc(sizeof(int32_t) * 0x8000);
-			memset(map->offsets, 0xFF, sizeof(int32_t) * 0x8000);
+			map->offsets = malloc(sizeof(int32_t) * 0xC000);
+			memset(map->offsets, 0xFF, sizeof(int32_t) * 0xC000);
 		}
 		map->offsets[address] = EXTENSION_WORD;
 	}
@@ -1708,6 +1794,7 @@
 
 uint32_t z80_get_instruction_start(native_map_slot * static_code_map, uint32_t address)
 {
+	//TODO: Fixme for address >= 0x4000
 	if (!static_code_map->base || address >= 0x4000) {
 		return INVALID_INSTRUCTION_START;
 	}
@@ -1782,13 +1869,13 @@
 		check_alloc_code(code, ZMAX_NATIVE_SIZE);
 		code_ptr start = code->cur;
 		deferred_addr * orig_deferred = opts->gen.deferred;
-		translate_z80inst(&instbuf, context, address);
+		translate_z80inst(&instbuf, context, address, 0);
 		/*
 		if ((native_end - dst) <= orig_size) {
 			uint8_t * native_next = z80_get_native_address(context, address + after-inst);
 			if (native_next && ((native_next == orig_start + orig_size) || (orig_size - (native_end - dst)) > 5)) {
 				remove_deferred_until(&opts->gen.deferred, orig_deferred);
-				native_end = translate_z80inst(&instbuf, orig_start, context, address);
+				native_end = translate_z80inst(&instbuf, orig_start, context, address, 0);
 				if (native_next == orig_start + orig_size && (native_next-native_end) < 2) {
 					while (native_end < orig_start + orig_size) {
 						*(native_end++) = 0x90; //NOP
@@ -1814,11 +1901,11 @@
 		code_info tmp_code = *code;
 		code->cur = orig_start;
 		code->last = orig_start + ZMAX_NATIVE_SIZE;
-		translate_z80inst(&instbuf, context, address);
+		translate_z80inst(&instbuf, context, address, 0);
 		code_info tmp2 = *code;
 		*code = tmp_code;
 		if (!z80_is_terminal(&instbuf)) {
-			
+
 			jmp(&tmp2, z80_get_native_address_trans(context, address + after-inst));
 		}
 		z80_handle_deferred(context);
@@ -1837,19 +1924,16 @@
 	uint8_t * encoded = NULL, *next;
 	if (address < 0x4000) {
 		encoded = context->mem_pointers[0] + (address & 0x1FFF);
-	} else if(address >= 0x8000 && context->mem_pointers[1]) {
-		printf("attempt to translate Z80 code from banked area at address %X\n", address);
-		exit(1);
-		//encoded = context->mem_pointers[1] + (address & 0x7FFF);
 	}
-	while (encoded != NULL)
+
+	while (encoded != NULL || address >= 0x4000)
 	{
 		z80inst inst;
 		dprintf("translating Z80 code at address %X\n", address);
 		do {
-			if (address > 0x4000 && address < 0x8000) {
-				xor_rr(&opts->gen.code, RDI, RDI, SZ_D);
-				call(&opts->gen.code, (uint8_t *)exit);
+			if (address >= 0x4000) {
+				code_info stub = z80_make_interp_stub(context, address);
+				z80_map_native_address(context, address, stub.cur, 1, stub.last - stub.cur);
 				break;
 			}
 			uint8_t * existing = z80_get_native_address(context, address);
@@ -1869,7 +1953,7 @@
 			}
 			#endif
 			code_ptr start = opts->gen.code.cur;
-			translate_z80inst(&inst, context, address);
+			translate_z80inst(&inst, context, address, 0);
 			z80_map_native_address(context, address, start, next-encoded, opts->gen.code.cur - start);
 			address += next-encoded;
 			if (address > 0xFFFF) {
@@ -1885,14 +1969,12 @@
 			dprintf("defferred address: %X\n", address);
 			if (address < 0x4000) {
 				encoded = context->mem_pointers[0] + (address & 0x1FFF);
-			} else if (address > 0x8000 && context->mem_pointers[1]) {
-				encoded = context->mem_pointers[1] + (address  & 0x7FFF);
 			} else {
-				printf("attempt to translate non-memory address: %X\n", address);
-				exit(1);
+				encoded = NULL;
 			}
 		} else {
 			encoded = NULL;
+			address = 0;
 		}
 	}
 }
@@ -1965,7 +2047,7 @@
 		} else {
 			reg = i;
 			size = SZ_B;
-		}
+}
 		if (options->regs[reg] >= 0) {
 			mov_rrdisp(code, options->regs[reg], options->gen.context_reg, offsetof(z80_context, regs) + i, size);
 		}
@@ -2045,7 +2127,7 @@
 	*no_sync = code->cur - (no_sync + 1);
 	//return to caller of z80_run
 	retn(code);
-	
+
 	options->gen.handle_code_write = (code_ptr)z80_handle_code_write;
 
 	options->read_8 = gen_mem_fun(&options->gen, chunks, num_chunks, READ_8, &options->read_8_noinc);
@@ -2117,7 +2199,7 @@
 	check_cycles(&options->gen);
 	cycles(&options->gen, 4);
 	retn(code);
-	
+
 	options->read_16 = code->cur;
 	cycles(&options->gen, 3);
 	check_cycles(&options->gen);
@@ -2134,7 +2216,7 @@
 	shl_ir(code, 8, options->gen.scratch1, SZ_W);
 	mov_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_B);
 	retn(code);
-	
+
 	options->write_16_highfirst = code->cur;
 	cycles(&options->gen, 3);
 	check_cycles(&options->gen);
@@ -2150,7 +2232,7 @@
 	//TODO: Check if we can get away with TCO here
 	call(code, options->write_8_noinc);
 	retn(code);
-	
+
 	options->write_16_lowfirst = code->cur;
 	cycles(&options->gen, 3);
 	check_cycles(&options->gen);
@@ -2215,9 +2297,12 @@
 	context->static_code_map->base = NULL;
 	context->static_code_map->offsets = malloc(sizeof(int32_t) * 0x2000);
 	memset(context->static_code_map->offsets, 0xFF, sizeof(int32_t) * 0x2000);
-	context->banked_code_map = malloc(sizeof(native_map_slot) * (1 << 9));
-	memset(context->banked_code_map, 0, sizeof(native_map_slot) * (1 << 9));
+	context->banked_code_map = malloc(sizeof(native_map_slot));
+	memset(context->banked_code_map, 0, sizeof(native_map_slot));
 	context->options = options;
+	context->int_cycle = 0xFFFFFFFF;
+	context->int_pulse_start = 0xFFFFFFFF;
+	context->int_pulse_end = 0xFFFFFFFF;
 	context->run = options->run;
 }
 
@@ -2229,61 +2314,81 @@
 	context->extra_pc = NULL;
 }
 
+uint32_t zbreakpoint_patch(z80_context * context, uint16_t address, code_ptr dst)
+{
+	code_info code = {dst, dst+16};
+	mov_ir(&code, address, SCRATCH1, SZ_W);
+	call(&code, context->bp_stub);
+	return code.cur-dst;
+}
+
+void zcreate_stub(z80_context * context)
+{
+	z80_options * opts = context->options;
+	code_info *code = &opts->gen.code;
+	check_code_prologue(code);
+	context->bp_stub = code->cur;
+
+	//Calculate length of prologue
+	check_cycles_int(&opts->gen, 0);
+	int check_int_size = code->cur-context->bp_stub;
+	code->cur = context->bp_stub;
+
+	//Calculate length of patch
+	int patch_size = zbreakpoint_patch(context, 0, code->cur);
+
+	//Save context and call breakpoint handler
+	call(code, opts->gen.save_context);
+	push_r(code, opts->gen.scratch1);
+	mov_rr(code, opts->gen.context_reg, RDI, SZ_Q);
+	mov_rr(code, opts->gen.scratch1, RSI, SZ_W);
+	call(code, context->bp_handler);
+	mov_rr(code, RAX, opts->gen.context_reg, SZ_Q);
+	//Restore context
+	call(code, opts->gen.load_context);
+	pop_r(code, opts->gen.scratch1);
+	//do prologue stuff
+	cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
+	uint8_t * jmp_off = code->cur+1;
+	jcc(code, CC_NC, code->cur + 7);
+	pop_r(code, opts->gen.scratch1);
+	add_ir(code, check_int_size - patch_size, opts->gen.scratch1, SZ_Q);
+	push_r(code, opts->gen.scratch1);
+	jmp(code, opts->gen.handle_cycle_limit_int);
+	*jmp_off = code->cur - (jmp_off+1);
+	//jump back to body of translated instruction
+	pop_r(code, opts->gen.scratch1);
+	add_ir(code, check_int_size - patch_size, opts->gen.scratch1, SZ_Q);
+	jmp_r(code, opts->gen.scratch1);
+}
+
 void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler)
 {
-	static uint8_t * bp_stub = NULL;
-	z80_options * opts = context->options;
-	code_ptr native = z80_get_native_address_trans(context, address);
-	code_info tmp_code = {native, native+16};
-	mov_ir(&tmp_code, address, opts->gen.scratch1, SZ_W);
-	if (!bp_stub) {
-		code_info *code = &opts->gen.code;
-		check_code_prologue(code);
-		bp_stub = code->cur;
-		call(&tmp_code, bp_stub);
-
-		//Calculate length of prologue
-		check_cycles_int(&opts->gen, address);
-		int check_int_size = code->cur-bp_stub;
-		code->cur = bp_stub;
-
-		//Save context and call breakpoint handler
-		call(code, opts->gen.save_context);
-		push_r(code, opts->gen.scratch1);
-		mov_rr(code, opts->gen.context_reg, RDI, SZ_Q);
-		mov_rr(code, opts->gen.scratch1, RSI, SZ_W);
-		call(code, bp_handler);
-		mov_rr(code, RAX, opts->gen.context_reg, SZ_Q);
-		//Restore context
-		call(code, opts->gen.load_context);
-		pop_r(code, opts->gen.scratch1);
-		//do prologue stuff
-		cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
-		uint8_t * jmp_off = code->cur+1;
-		jcc(code, CC_NC, code->cur + 7);
-		pop_r(code, opts->gen.scratch1);
-		add_ir(code, check_int_size - (tmp_code.cur-native), opts->gen.scratch1, SZ_Q);
-		push_r(code, opts->gen.scratch1);
-		jmp(code, opts->gen.handle_cycle_limit_int);
-		*jmp_off = code->cur - (jmp_off+1);
-		//jump back to body of translated instruction
-		pop_r(code, opts->gen.scratch1);
-		add_ir(code, check_int_size - (tmp_code.cur-native), opts->gen.scratch1, SZ_Q);
-		jmp_r(code, opts->gen.scratch1);
-	} else {
-		call(&tmp_code, bp_stub);
+	context->bp_handler = bp_handler;
+	uint8_t bit = 1 << (address % sizeof(uint8_t));
+	if (!(bit & context->breakpoint_flags[address / sizeof(uint8_t)])) {
+		context->breakpoint_flags[address / sizeof(uint8_t)] |= bit;
+		if (!context->bp_stub) {
+			zcreate_stub(context);
+		}
+		uint8_t * native = z80_get_native_address(context, address);
+		if (native) {
+			zbreakpoint_patch(context, address, native);
+		}
 	}
 }
 
 void zremove_breakpoint(z80_context * context, uint16_t address)
 {
+	context->breakpoint_flags[address / sizeof(uint8_t)] &= ~(1 << (address % sizeof(uint8_t)));
 	uint8_t * native = z80_get_native_address(context, address);
-	z80_options * opts = context->options;
-	code_info tmp_code = opts->gen.code;
-	opts->gen.code.cur = native;
-	opts->gen.code.last = native + 16;
-	check_cycles_int(&opts->gen, address);
-	opts->gen.code = tmp_code;
+	if (native) {
+		z80_options * opts = context->options;
+		code_info tmp_code = opts->gen.code;
+		opts->gen.code.cur = native;
+		opts->gen.code.last = native + 16;
+		check_cycles_int(&opts->gen, address);
+		opts->gen.code = tmp_code;
+	}
 }
 
-
--- a/z80_to_x86.h	Mon Dec 29 23:08:39 2014 -0800
+++ b/z80_to_x86.h	Tue Dec 30 19:11:34 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm.
+ This file is part of BlastEm. 
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #ifndef Z80_TO_X86_H_
@@ -74,6 +74,13 @@
 	uint32_t          int_enable_cycle;
 	z80_run_fun       run;
   uint16_t          pc;
+	uint32_t          int_pulse_start;
+	uint32_t          int_pulse_end;
+	uint8_t           breakpoint_flags[(16 * 1024)/sizeof(uint8_t)];
+	uint8_t *         bp_handler;
+	uint8_t *         bp_stub;
+	uint8_t *         interp_code[256];
+
 } z80_context;
 
 void translate_z80_stream(z80_context * context, uint32_t address);
--- a/z80inst.c	Mon Dec 29 23:08:39 2014 -0800
+++ b/z80inst.c	Tue Dec 30 19:11:34 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "z80inst.h"
@@ -433,7 +433,7 @@
 	{op, Z80_L, Z80_UNUSED, Z80_UNUSED, 1},\
 	{op, Z80_UNUSED, Z80_REG_INDIRECT, Z80_HL, 1},\
 	{op, Z80_A, Z80_UNUSED, Z80_UNUSED, 1}
-	
+
 #define BIT_BLOCK(op, bit) \
 	{op, Z80_USE_IMMED, Z80_REG, Z80_B, bit},\
 	{op, Z80_USE_IMMED, Z80_REG, Z80_C, bit},\
@@ -771,14 +771,14 @@
 };
 
 #define SHIFT_BLOCK_IX(op) \
-	{op, Z80_B, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_C, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_D, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_E, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_H, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_L, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_UNUSED, Z80_IX_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_A, Z80_IX_DISPLACE | Z80_DIR, 0, 0}
+	{op, Z80_B, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_C, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_D, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_E, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_H, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_L, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_UNUSED, Z80_IX_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_A, Z80_IX_DISPLACE | Z80_DIR, 0, 1}
 
 #define BIT_BLOCK_IX(bit) \
 	{Z80_BIT, Z80_USE_IMMED, Z80_IX_DISPLACE, 0, bit},\
@@ -1129,14 +1129,14 @@
 };
 
 #define SHIFT_BLOCK_IY(op) \
-	{op, Z80_B, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_C, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_D, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_E, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_H, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_L, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_UNUSED, Z80_IY_DISPLACE | Z80_DIR, 0, 0},\
-	{op, Z80_A, Z80_IY_DISPLACE | Z80_DIR, 0, 0}
+	{op, Z80_B, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_C, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_D, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_E, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_H, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_L, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_UNUSED, Z80_IY_DISPLACE | Z80_DIR, 0, 1},\
+	{op, Z80_A, Z80_IY_DISPLACE | Z80_DIR, 0, 1}
 
 #define BIT_BLOCK_IY(bit) \
 	{Z80_BIT, Z80_USE_IMMED, Z80_IY_DISPLACE, 0, bit},\
@@ -1250,7 +1250,7 @@
 		}
 	} else {
 		memcpy(decoded, z80_tbl_a + *istream, sizeof(z80inst));
-		
+
 	}
 	if ((decoded->addr_mode & 0x1F) == Z80_IMMED && decoded->op != Z80_RST && decoded->op != Z80_IM) {
 		decoded->immed = *(++istream);