diff --git a/Config/Games.xml b/Config/Games.xml
index c7c9281..6bf5292 100644
--- a/Config/Games.xml
+++ b/Config/Games.xml
@@ -1899,10 +1899,18 @@
     </hardware>
     <roms>
       <patches>
-        <!-- Unemulated JTAG stuff -->
+        <!-- Old patch to bypass JTAG checks, breaks linked mode -->
+        <!--
         <patch region="crom" bits="32" offset="0x7c0c4" value="0x60000000" />
         <patch region="crom" bits="32" offset="0x7c0c8" value="0x60000000" />
         <patch region="crom" bits="32" offset="0x7c0cc" value="0x60000000" />
+        -->
+        <!-- New patch to bypass JTAG checks -->
+        <patch region="crom" bits="32" offset="0x7b348" value="0x60000000" />
+        <patch region="crom" bits="32" offset="0x7b568" value="0x60000000" />
+        <patch region="crom" bits="32" offset="0x7b6b4" value="0x60000000" />
+        <patch region="crom" bits="32" offset="0x7b7b0" value="0x60000000" />
+        <patch region="crom" bits="32" offset="0x7b8a8" value="0x60000000" />
       </patches>
       <region name="crom" stride="8" chunk_size="2" byte_swap="true">
         <file offset="0" name="epr-20635.20" crc32="0x7937473F" />
diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp
index b19571e..4d9bdeb 100644
--- a/Src/Model3/Model3.cpp
+++ b/Src/Model3/Model3.cpp
@@ -1021,7 +1021,8 @@ UINT8 CModel3::Read8(UINT32 addr)
         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL);
       }
       //printf("R8 ioreg @%x=%x\n", (addr & 0x1FF), netBuffer[0x10000 + ((addr & 0x1FF) / 2)]);
-      return netBuffer[0x10000 + ((addr & 0x1FF) / 2)];
+      //return netBuffer[0x10000 + ((addr & 0x1FF) / 2)];
+      return (UINT8)NetBoard->ReadIORegister((addr & 0x1FF) / 2);
 
     case 2:
     case 3:
@@ -1316,43 +1317,28 @@ UINT32 CModel3::Read32(UINT32 addr)
       switch ((addr & 0x3ffff) >> 16)
       {
       case 0:
-        //printf("R32 netbuffer @%x=%x\n", (addr & 0xFFFF), FLIPENDIAN32(*(UINT32 *)&netBuffer[(addr & 0xFFFF)]));
         result = *(UINT32 *)&netBuffer[(addr & 0xFFFF)];
         result = FLIPENDIAN32(result);
         return ((result << 16) | (result >> 16));
-        //return FLIPENDIAN32(result); // result
 
       case 1: // ioreg 32bits access to 16bits range
-        //printf("R32 ioreg @%x=%x\n", (addr & 0x1FF), FLIPENDIAN32(*(UINT32 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)]));
         if (addr > 0xc00101ff)
         {
           printf("R32 ATTENTION OUT OF RANGE\n");
         }
 
-        UINT32 test;
-        test = (*(UINT32 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)]);
-        /*if (((FLIPENDIAN32(test) & 0x00ff0000) != 0x00900000) && ((FLIPENDIAN32(test) & 0x00ff0000) != 0x00a00000) && ((FLIPENDIAN32(test) & 0x00ff0000) != 0x00b00000) && ((FLIPENDIAN32(test) & 0x00ff0000) != 0x00800000) && ((FLIPENDIAN32(test) & 0x00ff0000) != 0x00f00000))
-        {
-          printf("R32 ioreg @%x=%04x\n", (addr), FLIPENDIAN32(test) >> 16);
-        }*/
-        result = (*(UINT32 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)]) & 0x0000ffff;
+        result = NetBoard->ReadIORegister((addr & 0x1FF) / 2);
         return FLIPENDIAN32(result);
 
       case 2:
       case 3:
-        //printf("R32 netram @%x=%x\n", (addr & 0x1FFFF), FLIPENDIAN32(*(UINT32 *)&netBuffer[(addr & 0x1FFFF)]));
-
-        if (addr > 0xc002ffff)
+        if (addr > 0xc003ffff)
         {
           printf("R32 ATTENTION OUT OF RANGE\n");
         }
 
         result = (*(UINT32 *)&netRAM[((addr & 0x1FFFF) / 2)]) & 0x0000ffff;
         return FLIPENDIAN32(result); // result
-      /*case 3:
-        //printf("R32 netram @%x=%x\n", (addr & 0x1FFFF), FLIPENDIAN32(*(UINT32 *)&netBuffer[(addr & 0x1FFFF)]));
-        result = (*(UINT32 *)&netRAM[((addr & 0x1FFFF) / 2)]) & 0x0000ffff;
-        return FLIPENDIAN32(result); // result*/
 
       default:
         printf("R32 ATTENTION OUT OF RANGE\n");
@@ -1493,7 +1479,6 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
       switch ((addr & 0x3ffff) >> 16)
       {
       case 0:
-        //printf("W8 netbuffer @%x<-%x\n", (addr & 0xFFFF), data);
         *(UINT8 *)&netBuffer[(addr & 0xFFFF) ^ 2] = data;
         break;
 
@@ -1503,10 +1488,7 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
           printf("W8 ATTENTION OUT OF RANGE\n");
         }
 
-        //printf("W8 ioreg @%x<-%x\n", (addr & 0x1FF), data);
-        if (((addr & 0x1FF) == 0x180) && (data == 0x00))
-            NetBoard->Reset();
-        *(UINT8 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)] = data;
+        NetBoard->WriteIORegister((addr & 0x1FF) / 2, data);
         break;
 
       case 2:
@@ -1516,13 +1498,8 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
           printf("W8 ATTENTION OUT OF RANGE\n");
         }
 
-        //printf("W8 netram @%x<-%x\n", (addr & 0x1FFFF), data);
         *(UINT8 *)&netRAM[(addr & 0x1FFFF)/2] = data;
         break;
-      /*case 3:
-        //printf("W8 netram @%x<-%x\n", (addr & 0x1FFFF), data);
-        *(UINT8 *)&netRAM[(addr & 0x1FFFF) / 2] = data;
-        break;*/
 
       default:
         printf("W8 ATTENTION OUT OF RANGE\n");
@@ -1623,7 +1600,6 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
     switch ((addr & 0x3ffff) >> 16)
     {
     case 0:
-      //printf("W16 netbuffer @%x<-%x\n", (addr & 0xFFFF), data);
       *(UINT16 *)&netBuffer[(addr & 0xFFFF) ^ 2] = FLIPENDIAN16(data);
       break;
 
@@ -1829,8 +1805,6 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
       switch ((addr & 0x3ffff) >> 16)
       {
       case 0:
-        //printf("W32 netbuffer @%x<-%x\n", (addr & 0xFFFF), data);
-        //*(UINT32 *)&netBuffer[(addr & 0xFFFF)] = FLIPENDIAN32(data);
         temp = FLIPENDIAN32(data);
         *(UINT32 *)&netBuffer[(addr & 0xFFFF)] = (temp << 16) | (temp >> 16);
         break;
@@ -1841,26 +1815,19 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
           printf("W32 ATTENTION OUT OF RANGE\n");
         }
 
-        //printf("W32 ioreg @%x<-%04x\n", (addr /*& 0x1FF*/), data>>16);
-        if (((addr & 0x1FF) == 0x180) && ((data >> 16) == 0x0000))
-            NetBoard->Reset();
-        *(UINT16 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)] = FLIPENDIAN16(data >> 16);
+        NetBoard->WriteIORegister((addr & 0x1FF) / 2, FLIPENDIAN16(data >> 16));
         break;
 
       case 2:
       case 3:
-        if (addr > 0xc002ffff)
+        if (addr > 0xc003ffff)
         {
           printf("W32 ATTENTION OUT OF RANGE\n");
         }
 
-        //printf("W32 netram @%x<-%x\n", (addr & 0x1FFFF), data);
         *(UINT16 *)&netRAM[((addr & 0x1FFFF) / 2)] = FLIPENDIAN16(data >> 16);
         break;
-      /*case 3:
-        //printf("W32 netram @%x<-%x\n", (addr & 0x1FFFF), data);
-        *(UINT16 *)&netRAM[((addr & 0x1FFFF) / 2)] = FLIPENDIAN16(data >> 16);
-        break;*/
+
       default:
         printf("W32 ATTENTION OUT OF RANGE\n");
         break;
@@ -3153,7 +3120,7 @@ const static int DSBPROGROM_SIZE	= 0x20000;		//128KB
 const static int DSBMPEGROM_SIZE	= 0x1000000;	//16MB
 const static int DRIVEROM_SIZE		= 0x10000;		//64KB
 const static int NETBUFFER_SIZE		= 0x20000;		//128KB
-const static int NETRAM_SIZE		= 0x20000;		//128KB
+const static int NETRAM_SIZE		= 0x10000;		//64KB
 
 const static int MEM_POOL_SIZE		= RAM_SIZE + CROM_SIZE +
                                         CROMxx_SIZE + VROM_SIZE +
diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h
index 302bd57..deed28b 100644
--- a/Src/Model3/Model3.h
+++ b/Src/Model3/Model3.h
@@ -251,8 +251,8 @@ private:
   UINT8   *backupRAM;   // 128 KB Backup RAM (battery backed)
   UINT8   *securityRAM; // 128 KB Security Board RAM
   UINT8   *driveROM;    // 32 KB drive board ROM (Z80 program) (optional)
-  UINT8   *netRAM;		// 128KB RAM
-  UINT8	  *netBuffer;	// 128KB buffer
+  UINT8   *netRAM;		// 64 KB RAM
+  UINT8	  *netBuffer;	// 128 KB buffer
 
   // Banked CROM
   UINT8     *cromBank;    // currently mapped in CROM bank
diff --git a/Src/Network/INetBoard.h b/Src/Network/INetBoard.h
index b1d98f3..41b9f00 100644
--- a/Src/Network/INetBoard.h
+++ b/Src/Network/INetBoard.h
@@ -42,6 +42,9 @@ public:
 	virtual bool Init(UINT8* netRAMPtr, UINT8* netBufferPtr) = 0;
 
 	virtual void GetGame(Game) = 0;
+
+	virtual UINT16 ReadIORegister(unsigned reg) = 0;
+	virtual void WriteIORegister(unsigned reg, UINT16 data) = 0;
 };
 
 #endif
\ No newline at end of file
diff --git a/Src/Network/NetBoard.cpp b/Src/Network/NetBoard.cpp
index fb777bc..12b3925 100644
--- a/Src/Network/NetBoard.cpp
+++ b/Src/Network/NetBoard.cpp
@@ -64,7 +64,7 @@
 // Network=1
 // PortIn = 1970
 // PortOut = 1971
-// addr_out = "127.0.0.1"
+// AddressOut = "127.0.0.1"
 //
 // add for slave
 // Network=1
@@ -78,19 +78,19 @@
 // Network=1
 // PortIn = 1970
 // PortOut = 1971
-// addr_out = "127.0.0.1"
+// AddressOut = "127.0.0.1"
 //
 // add for slave1
 // Network=1
 // PortIn = 1971
 // PortOut = 1972
-// addr_out = "127.0.0.1"
+// AddressOut = "127.0.0.1"
 //
 // add for slave2
 // Network=1
 // PortIn = 1972
 // PortOut = 1970
-// addr_out = "127.0.0.1"
+// AddressOut = "127.0.0.1"
 
 //#define NET_DEBUG
 
@@ -1306,7 +1306,7 @@ bool CNetBoard::IsAttached(void)
 
 bool CNetBoard::IsRunning(void)
 {
-	return m_attached && ((ioreg[0xc0] == 0xff) || (ioreg[0xc0] == 0x01));
+	return m_attached && (ioreg[0xc0] != 0);
 }
 
 void CNetBoard::GetGame(Game gameinfo)
@@ -1314,3 +1314,18 @@ void CNetBoard::GetGame(Game gameinfo)
 	Gameinfo = gameinfo;
 }
 
+UINT16 CNetBoard::ReadIORegister(unsigned reg)
+{
+	if (!IsRunning())
+		return 0;
+
+	return *(UINT16*)&ioreg[reg];
+}
+
+void CNetBoard::WriteIORegister(unsigned reg, UINT16 data)
+{
+	if (reg == 0xc0 && !(data != 0 && IsRunning()))
+		Reset();	// don't reset if we are activating the netboard but it is already activated
+
+	*(UINT16*)&ioreg[reg] = data;
+}
\ No newline at end of file
diff --git a/Src/Network/NetBoard.h b/Src/Network/NetBoard.h
index 6f99a70..fb8f48a 100644
--- a/Src/Network/NetBoard.h
+++ b/Src/Network/NetBoard.h
@@ -56,12 +56,14 @@ public:
 	M68KCtx *GetM68K(void);
 	bool IsAttached(void);
 	bool IsRunning(void);
-	bool CodeReady;
 
 	bool Init(UINT8 *netRAMPtr, UINT8 *netBufferPtr);
 
 	void GetGame(Game);
 
+	UINT16 ReadIORegister(unsigned reg);
+	void WriteIORegister(unsigned reg, UINT16 data);
+
 	CNetBoard(const Util::Config::Node &config);
 	~CNetBoard(void);
 
@@ -72,7 +74,7 @@ private:
 	M68KCtx		M68K;
 
 	// Sound board memory
-	UINT8		*netRAM;		// 128Kb RAM (passed in from parent object)
+	UINT8		*netRAM;		// 64Kb RAM (passed in from parent object)
 	UINT8		*netBuffer;		// 128kb (passed in from parent object)
 	UINT8		*memoryPool;	// single allocated region for all net board RAM
 	UINT8		*CommRAM;
diff --git a/Src/Network/SimNetBoard.cpp b/Src/Network/SimNetBoard.cpp
index 8bff64b..0c85ca8 100644
--- a/Src/Network/SimNetBoard.cpp
+++ b/Src/Network/SimNetBoard.cpp
@@ -28,7 +28,6 @@
  // these make 16-bit read/writes much neater
 #define RAM16 *(uint16_t*)&RAM
 #define CommRAM16 *(uint16_t*)&CommRAM
-#define ioreg16 *(uint16_t*)&ioreg
 
 static const uint64_t netGUID = 0x5bf177da34872;
 
@@ -39,12 +38,11 @@ inline bool CSimNetBoard::IsGame(const char* gameName)
 
 CSimNetBoard::CSimNetBoard(const Util::Config::Node& config) : m_config(config)
 {
-	m_running = true;
 }
 
 CSimNetBoard::~CSimNetBoard(void)
 {
-	m_running = false;
+	m_quit = true;
 
 	if (m_connectThread.joinable())
 		m_connectThread.join();
@@ -64,7 +62,6 @@ bool CSimNetBoard::Init(uint8_t* netRAMPtr, uint8_t* netBufferPtr)
 {
 	RAM = netRAMPtr;
 	CommRAM = netBufferPtr;
-	ioreg = netBufferPtr + 0x10000;
 
 	m_attached = m_gameInfo.netboard_present && m_config["Network"].ValueAs<bool>();
 
@@ -102,8 +99,8 @@ void CSimNetBoard::RunFrame(void)
 	case State::start:
 		if (!m_connected && !m_connectThread.joinable())
 			m_connectThread = std::thread(&CSimNetBoard::ConnectProc, this);
-		ioreg16[0x88] = 0;
-		ioreg16[0x8a] = IsGame("dirtdvls") ? 0x4004 : 0xe000;
+		m_status0 = 0;
+		m_status1 = IsGame("dirtdvls") ? 0x4004 : 0xe000;
 		m_state = State::init;
 		break;
 
@@ -111,23 +108,23 @@ void CSimNetBoard::RunFrame(void)
 		memset(CommRAM, 0, 0x10000);
 		if (m_gameType == GameType::one)
 		{
-			if (ioreg16[0x88] & 0x8000)				// has main board changed this register?
+			if (m_status0 & 0x8000)				// has main board changed this register?
 			{
-				ioreg[0] |= 0x01;					// simulate IRQ 2 ack
-				if (ioreg16[0x88] == 0xf000)
+				m_IRQ2ack |= 0x01;				// simulate IRQ 2 ack
+				if (m_status0 == 0xf000)
 				{
 					// initialization complete
-					ioreg16[0x8a] = 0;
+					m_status1 = 0;
 					CommRAM16[0x72] = FLIPENDIAN16(0x1); // is this necessary?
 					m_state = State::testing;
 				}
-				ioreg16[0x88] = 0;					// 0 should work for all init subroutines
+				m_status0 = 0;					// 0 should work for all init subroutines
 			}
 		}
 		else
 		{
 			// type 2 performs initialization on its own
-			ioreg16[0x8a] = 0;
+			m_status1 = 0;
 			m_state = State::testing;
 			m_counter = 0;
 		}
@@ -136,7 +133,7 @@ void CSimNetBoard::RunFrame(void)
 	case State::testing:
 		if (m_gameType == GameType::one)
 		{
-			ioreg16[0x88] += 1; // type 1 games require this to be incremented every frame
+			m_status0 += 1; // type 1 games require this to be incremented every frame
 
 			if (!m_connected)
 				break;
@@ -240,8 +237,8 @@ void CSimNetBoard::RunFrame(void)
 
 			m_numMachines = numMachines + 1;
 
-			ioreg16[0x88] = 0;		// supposed to cycle between 0 and 1 (also 2 for Daytona 2); doesn't seem to matter
-			ioreg16[0x8a] = 0x2021 + (numMachines * 0x20) + machineIndex;
+			m_status0 = 0;		// supposed to cycle between 0 and 1 (also 2 for Daytona 2); doesn't seem to matter
+			m_status1 = 0x2021 + (numMachines * 0x20) + machineIndex;
 
 			CommRAM16[0x0] = RAM16[0x400];	// 0 if master, 1 if slave
 			CommRAM16[0x2] = numMachines;
@@ -421,18 +418,18 @@ void CSimNetBoard::RunFrame(void)
 			{
 				ErrorLog("no slave machines detected. Make sure only one machine is set to master!");
 				if (IsGame("dirtdvls"))
-					ioreg16[0x8a] = 0x8085;	// seems like the netboard code writers really liked their CPU model numbers
+					m_status1 = 0x8085;	// seems like the netboard code writers really liked their CPU model numbers
 				m_state = State::error;
 				break;
 			}
 
 			m_numMachines = numMachines.total + 1;
 
-			ioreg16[0x88] = 5;			// probably not necessary
+			m_status0 = 5;			// probably not necessary
 			if (IsGame("dirtdvls"))
-				ioreg16[0x8a] = (numMachines.playable << 4) | machineIndex.playable | 0x7400;
+				m_status1 = (numMachines.playable << 4) | machineIndex.playable | 0x7400;
 			else
-				ioreg16[0x8a] = (numMachines.playable << 8) | machineIndex.playable;
+				m_status1 = (numMachines.playable << 8) | machineIndex.playable;
 
 			CommRAM16[0x0] = RAM16[0x200];	// master/slave/relay status
 			CommRAM16[0x2] = (numMachines.playable << 8) | numMachines.total;
@@ -466,7 +463,7 @@ void CSimNetBoard::RunFrame(void)
 				// link broken - send an "empty" packet to alert other machines
 				nets->Send(nullptr, 0);
 				m_state = State::error;
-				ioreg16[0x8a] = 0x40;		// send "link broken" message to mainboard
+				m_status1 = 0x40;		// send "link broken" message to mainboard
 				break;
 			}
 			memcpy(CommRAM + 0x100 + m_segmentSize, recv_data.data(), recv_data.size());
@@ -485,7 +482,7 @@ void CSimNetBoard::RunFrame(void)
 					nets->Send(nullptr, 0);
 					m_state = State::error;
 					if (m_gameType == GameType::one)
-						ioreg16[0x8a] = 0x40;			// send "link broken" message to mainboard
+						m_status1 = 0x40;			// send "link broken" message to mainboard
 					break;
 				}
 				memcpy(CommRAM + 0x100 + (i + 1) * m_segmentSize, recv_data.data(), recv_data.size());
@@ -509,7 +506,7 @@ void CSimNetBoard::Reset(void)
 		netr->Receive();
 	}
 
-	ioreg[0xc0] = 0;
+	m_running = false;
 	m_state = State::start;
 }
 
@@ -520,7 +517,7 @@ bool CSimNetBoard::IsAttached(void)
 
 bool CSimNetBoard::IsRunning(void)
 {
-	return (ioreg[0xc0] == 0xff) || (ioreg[0xc0] == 0x01);	// there's probably a better way of checking
+	return m_attached && m_running;
 }
 
 void CSimNetBoard::GetGame(Game gameInfo)
@@ -540,14 +537,14 @@ void CSimNetBoard::ConnectProc(void)
 	// wait until TCPSend has connected to the next machine
 	while (!nets->Connect())
 	{
-		if (!m_running)
+		if (m_quit)
 			return;
 	}
 
 	// wait until TCPReceive has accepted a connection from the previous machine
 	while (!netr->Connected())
 	{
-		if (!m_running)
+		if (m_quit)
 			return;
 		std::this_thread::sleep_for(1ms);
 	}
@@ -555,4 +552,46 @@ void CSimNetBoard::ConnectProc(void)
 	printf("Successfully connected.\n");
 
 	m_connected = true;
+}
+
+uint16_t CSimNetBoard::ReadIORegister(unsigned reg)
+{
+	if (!IsRunning())
+		return 0;
+
+	switch (reg)
+	{
+	case 0x00:
+		return m_IRQ2ack;
+	case 0x88:
+		return m_status0;
+	case 0x8a:
+		return m_status1;
+	default:
+		ErrorLog("read from unknown IO register 0x%02x", reg);
+		return 0;
+	}
+}
+
+void CSimNetBoard::WriteIORegister(unsigned reg, uint16_t data)
+{
+	switch (reg)
+	{
+	case 0x00:
+		m_IRQ2ack = data;
+		break;
+	case 0x88:
+		m_status0 = data;
+		break;
+	case 0x8a:
+		m_status1 = data;
+		break;
+	case 0xc0:
+		if (data == 0)
+			Reset();
+		m_running = (data != 0);
+		break;
+	default:
+		ErrorLog("write to unknown IO register 0x%02x", reg);
+	}
 }
\ No newline at end of file
diff --git a/Src/Network/SimNetBoard.h b/Src/Network/SimNetBoard.h
index f5b9ccc..b45c821 100644
--- a/Src/Network/SimNetBoard.h
+++ b/Src/Network/SimNetBoard.h
@@ -62,20 +62,22 @@ public:
 
 	void GetGame(Game gameInfo);
 
+	uint16_t ReadIORegister(unsigned reg);
+	void WriteIORegister(unsigned reg, uint16_t data);
+
 private:
 	// Config
 	const Util::Config::Node& m_config;
 
 	uint8_t* RAM = nullptr;
 	uint8_t* CommRAM = nullptr;
-	uint8_t* ioreg = nullptr;
 
 	// netsock
 	uint16_t port_in = 0;
 	uint16_t port_out = 0;
 	std::string addr_out = "";
 	std::thread m_connectThread;
-	std::atomic_bool m_running = false;
+	std::atomic_bool m_quit = false;
 	std::atomic_bool m_connected = false;
 
 	std::unique_ptr<TCPSend> nets = nullptr;
@@ -91,6 +93,11 @@ private:
 	uint16_t m_segmentSize = 0;
 
 	bool m_attached = false;
+	bool m_running = false;
+
+	uint16_t m_IRQ2ack = 0;
+	uint16_t m_status0 = 0;	// ioreg 0x88
+	uint16_t m_status1 = 0;	// ioreg 0x8a
 
 	inline bool IsGame(const char* gameName);
 	void ConnectProc(void);