1. spencercw
  2. gb_emulator

Commits

spencercw  committed 2f8897c

#33 Add infrastructure to support serial communications implementation.

  • Participants
  • Parent commits 082fbce
  • Branches default

Comments (0)

Files changed (8)

File gb_emulator/gb_emulator.vcxproj

View file
  • Ignore whitespace
     <ClInclude Include="include\gb_emulator\constants.hpp" />
     <ClInclude Include="include\gb_emulator\defs.hpp" />
     <ClInclude Include="include\gb_emulator\gb.hpp" />
+    <ClInclude Include="include\gb_emulator\gb_comms_serial.h" />
     <ClInclude Include="include\gb_emulator\gb_config.h" />
     <ClInclude Include="include\gb_emulator\gb_cpu.hpp" />
     <ClInclude Include="include\gb_emulator\gb_debugger.h" />
     </ClCompile>
     <ClCompile Include="src\cdb_file.cpp" />
     <ClCompile Include="src\gb.cpp" />
+    <ClCompile Include="src\gb_comms_serial.cpp" />
     <ClCompile Include="src\gb_cpu.cpp" />
     <ClCompile Include="src\gb_debugger.cpp" />
     <ClCompile Include="src\gb_disassembler.cpp" />

File gb_emulator/gb_emulator.vcxproj.filters

View file
  • Ignore whitespace
     <ClInclude Include="include\gb_emulator\gb_config.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\gb_emulator\gb_comms_serial.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="gb.pb.cc">
     <ClCompile Include="src\gb_video_d3d11.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\gb_comms_serial.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="gb.proto">

File gb_emulator/include/gb_emulator/constants.hpp

View file
  • Ignore whitespace
 
 /* I/O ports */
 const uint8_t  P1                       = 0x00;          /* Joypad */
-const uint8_t  SB                       = 0x01;          /* Unknown */
-const uint8_t  SC                       = 0x02;          /* Unknown */
+const uint8_t  SB                       = 0x01;          /* Serial transfer data */
+const uint8_t  SC                       = 0x02;          /* Serial transfer control */
 const uint8_t  DIV                      = 0x04;          /* Divider */
 const uint8_t  TIMA                     = 0x05;          /* Timer counter */
 const uint8_t  TMA                      = 0x06;          /* Timer modulo */
 const int TIMER2_CYCLES                 = 16;
 const int TIMER3_CYCLES                 = 64;
 
+/* Serial comms clock durations
+ * This is the number of cycles per *byte*, not per bit as the real Game Boy operates. SB is
+ * inaccessible during transfers so emulating the bit shifting is unnecessary. */
+const int SERIAL1_CYCLES                = 1024;  /* 1024 Hz (8192 Hz) */
+const int SERIAL2_CYCLES                = 32;    /* 32 768 Hz (262 144 Hz) */
+
 #endif

File gb_emulator/include/gb_emulator/gb.hpp

View file
  • Ignore whitespace
 #include <gb_emulator/gb_debugger_module.h>
 #include <gb_emulator/gb_timers.hpp>
 
+class GbCommsSerial;
 class GbConfig;
 class GbDebugger;
 class GbInput;
  */
 class GB_EMULATOR_API Gb
 {
+	friend class GbCommsSerial;
 	friend class GbCpu;
 	friend class GbTimers;
 	friend class GbInput;
 	boost::scoped_ptr<GbSound> sound_;
 	boost::scoped_ptr<GbVideo> video_;
 	boost::scoped_ptr<GbMemory> mem_;
+	boost::scoped_ptr<GbCommsSerial> serial_;
 	GbDebugger debugger_;
 	bool gbc_;
 

File gb_emulator/include/gb_emulator/gb_comms_serial.h

View file
  • Ignore whitespace
+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_COMMS_SERIAL_H_1F334C00_1F91_11E1_B524_0002A5D5C51B
+#define GB_COMMS_SERIAL_H_1F334C00_1F91_11E1_B524_0002A5D5C51B
+
+class Gb;
+
+//! Game Boy serial communications emulator.
+class GbCommsSerial
+{
+public:
+	//! Constructor; sets the associated emulator container.
+	GbCommsSerial(Gb &gb);
+
+	//! Performs a single transfer.
+	/**
+	 * This should be called when bit 7 of the SC register is set to one and cycle counter for this
+	 * class has reached zero.
+	 * \return The number of CPU cycles to execute before calling this function again. If zero is
+	 * returned then this should not be called until a transfer is next initiated.
+	 */
+	int poll();
+
+private:
+	// The emulator context
+	Gb &gb_;
+
+	// Whether a transfer has been started but not completed
+	bool transferStarted_;
+
+	// Disabled operations
+	GbCommsSerial(const GbCommsSerial &);
+	GbCommsSerial & operator=(const GbCommsSerial &);
+};
+
+#endif

File gb_emulator/src/gb.cpp

View file
  • Ignore whitespace
 
 #include <gb_emulator/constants.hpp>
 #include <gb_emulator/gb.hpp>
+#include <gb_emulator/gb_comms_serial.h>
 #include <gb_emulator/gb_config.h>
 #include <gb_emulator/gb_input.hpp>
 #include <gb_emulator/gb_memory.hpp>
 		assert(!"unknown video driver");
 	}
 
-	// Memory
+	// Others
 	mem_.reset(new GbMemory(*this));
+	serial_.reset(new GbCommsSerial(*this));
 
 	// Load the BIOS
 	biosFilename_ = installDir_ / "bios.bin";
 	int videoCycles = HBLANK_CYCLES;
 	int timerCycles = DIVIDER_CYCLES;
 	double soundCycles = 0;
+	int serialCycles = 0;
 	int frameCycles = CYCLES_PER_FRAME;
 	for (;;)
 	{
 			soundCycles += cycles;
 		}
 
+		// Update the serial comms
+		if ((mem_->ioPorts[SC] & 0x80) && serialCycles <= 0)
+		{
+			int tmpCycles = serial_->poll();
+			if (tmpCycles)
+			{
+				serialCycles += tmpCycles;
+			}
+			else
+			{
+				serialCycles = 0;
+			}
+		}
+
 		// Wait for the sound timer to signal we are ok to continue emulation
 		if (frameCycles <= 0)
 		{
 		if (timerCycles < cycles) cycles = timerCycles;
 		if (soundCycles < cycles) cycles = soundCycles;
 		if (frameCycles < cycles) cycles = frameCycles;
+		if ((mem_->ioPorts[SC] & 0x80) && serialCycles < cycles) cycles = serialCycles;
 
 		bool speedSwitch = false;
 		int cyclesExecuted = cpu_.poll(static_cast<int>(ceil(cycles)), speedSwitch);
 		videoCycles -= cyclesExecuted;
 		soundCycles -= cyclesExecuted;
 		frameCycles -= cyclesExecuted;
+		if (mem_->ioPorts[SC] & 0x80 && serialCycles > 0)
+		{
+			serialCycles -= cyclesExecuted;
+		}
 
 		// Adjust the cycle counts if the CPU speed has switched
 		if (speedSwitch)

File gb_emulator/src/gb_comms_serial.cpp

View file
  • Ignore whitespace
+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <gb_emulator/gb_comms_serial.h>
+
+#include <assert.h>
+
+#include <gb_emulator/constants.hpp>
+#include <gb_emulator/gb.hpp>
+#include <gb_emulator/gb_memory.hpp>
+
+GbCommsSerial::GbCommsSerial(Gb &gb):
+gb_(gb),
+transferStarted_(false)
+{
+}
+
+int GbCommsSerial::poll()
+{
+	assert(gb_.mem_->ioPorts[SC] & 0x80);
+
+	if (!transferStarted_)
+	{
+		transferStarted_ = true;
+
+		// Return the duration of the transfer. If the external clock is used just assumed it is the
+		// standard 8192 Hz clock.
+		if (gb_.gbc_ && (gb_.mem_->ioPorts[SC] & 0x03) == 0x03)
+		{
+			return SERIAL2_CYCLES;
+		}
+		else
+		{
+			return SERIAL1_CYCLES;
+		}
+	}
+	else
+	{
+		// Mark the transfer as completed. Fill SB with 0xff to indicate there is no connection
+		transferStarted_ = false;
+		gb_.mem_->ioPorts[SB]  = 0xff;
+		gb_.mem_->ioPorts[SC] &= ~0x80;
+		return 0;
+	}
+}

File gb_emulator/src/gb_memory.cpp

View file
  • Ignore whitespace
 #endif
 
 #include <gb_emulator/gb.hpp>
+#include <gb_emulator/gb_comms_serial.h>
 #include <gb_emulator/gb_input.hpp>
 #include <gb_emulator/gb_memory.hpp>
 #include <gb_emulator/gb_sound.hpp>
 {
 	switch (ptr)
 	{
+	// Serial transfer data
+	case SB:
+		// Access is only permitted when a transfer is not in progress
+		if (ioPorts[SC] & 0x80)
+		{
+			return 0;
+		}
+		return ioPorts[ptr];
+
 	// Sound modes
 	case NR14:
 	case NR24:
 		gb_.input_->translate();
 		break;
 
+	// Serial transfer data
+	case SB:
+		// Access is only permitted when a transfer is not in progress
+		if (!(ioPorts[SC] & 0x80))
+		{
+			ioPorts[ptr] = val;
+		}
+		break;
+
+	// Serial transfer control
+	case SC:
+		// Don't do anything if a transfer is in progress. The documentation doesn't say anything
+		// about this and it just makes the implementation simpler.
+		if (ioPorts[ptr] & 0x80)
+		{
+			return;
+		}
+
+		// Bit 1 is not accessible in DMG mode
+		if (gb_.gbc_)
+		{
+			ioPorts[ptr] = val & 0x83;
+		}
+		else
+		{
+			ioPorts[ptr] = val & 0x81;
+		}
+		break;
+
 	// Divider
 	case DIV:
 		ioPorts[ptr] = 0;
 
 			// Check if we are using a GBC ROM
 			if (0x143 < gb_.rom_.length() && !(gb_.rom_[0x143] & 0x80))
+			{
 				gb_.gbc_ = false;
+
+				// Bit 1 of the SC register (fast speed mode) is always set to 1 in DMG mode, even
+				// though it doesn't do anything
+				ioPorts[SC] |= 0x02;
+			}
 		}
 		break;