Commits

spencercw committed 7723a09

#14 Load the debugger DLL dynamically at runtime instead of statically linking so it is possible to build the project cleanly.

Comments (0)

Files changed (6)

gb_debugger_msvs/gb_debugger_msvs.cpp

 #include "program.h"
 
 using boost::shared_ptr;
+using std::nothrow;
 
 class CGbDebuggerMsvsModule:
 	public ATL::CAtlExeModuleT<CGbDebuggerMsvsModule>
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
+GbDebuggerModule * getDebuggerInstance()
+{
+	return new (nothrow) GbDebuggerMsvs();
+}
+
+void freeDebuggerInstance(GbDebuggerModule *debugger)
+{
+	delete debugger;
+}
+
 class GbDebuggerMsvs::Impl:
 	public GbDebugger::Listener
 {

gb_emulator/gb_emulator.vcxproj

       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <TreatWarningAsError>true</TreatWarningAsError>
-      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)gb_debugger_msvs;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>DEBUG;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(BOOST_ROOT)\lib</AdditionalLibraryDirectories>
-      <AdditionalDependencies>gb_debugger_msvs.lib;hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
   </ItemDefinitionGroup>
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <TreatWarningAsError>true</TreatWarningAsError>
-      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)gb_debugger_msvs;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>DEBUG;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(BOOST_ROOT)\lib64</AdditionalLibraryDirectories>
-      <AdditionalDependencies>gb_debugger_msvs.lib;hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
   </ItemDefinitionGroup>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <TreatWarningAsError>true</TreatWarningAsError>
-      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)gb_debugger_msvs;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <OptimizeReferences>true</OptimizeReferences>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(BOOST_ROOT)\lib</AdditionalLibraryDirectories>
-      <AdditionalDependencies>gb_debugger_msvs.lib;hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
   </ItemDefinitionGroup>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <TreatWarningAsError>true</TreatWarningAsError>
-      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)gb_debugger_msvs;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <OptimizeReferences>true</OptimizeReferences>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(BOOST_ROOT)\lib64</AdditionalLibraryDirectories>
-      <AdditionalDependencies>gb_debugger_msvs.lib;hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>hqx.lib;libprotobuf.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
   </ItemDefinitionGroup>

gb_emulator/include/gb_emulator/gb.hpp

 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include <boost/filesystem/path.hpp>
+#include <boost/shared_ptr.hpp>
 
 #ifdef _WIN32
 #include <Windows.h>
 #include <gb_emulator/defs.hpp>
 #include <gb_emulator/gb_cpu.hpp>
 #include <gb_emulator/gb_debugger.h>
+#include <gb_emulator/gb_debugger_module.h>
 #include <gb_emulator/gb_input.hpp>
 #include <gb_emulator/gb_memory.hpp>
 #include <gb_emulator/gb_sound.hpp>
 	void load();
 
 private:
+#ifdef _WIN32
+	// Loaded plugin modules. The values in this vector are not actually used; they are only saved
+	// so they are automatically freed when the class is destructed. Note it is important that these
+	// are freed after the values in debuggers_.
+	std::vector<boost::shared_ptr<HINSTANCE__> > debuggerModules_;
+#endif
+	// Loaded debuggers
+	std::vector<boost::shared_ptr<GbDebuggerModule> > debuggers_;
+
 	// Emulator state
 	std::basic_string<uint8_t> bios_;
 	std::basic_string<uint8_t> rom_;
 	// Common initialisation code
 	void init(const boost::filesystem::path &bios);
 
+	// Loads any debugger plugins
+	void loadDebuggers();
+
 	// Spins to get the correct frame timing
 	void spin();
 

gb_emulator/include/gb_emulator/gb_debugger_module.h

 	virtual void stop() = 0;
 };
 
+extern "C"
+{
+
+	GbDebuggerModule GB_DEBUGGER_API * getDebuggerInstance();
+	void GB_DEBUGGER_API freeDebuggerInstance(GbDebuggerModule *debugger);
+
+}
+
 #endif

gb_emulator/src/gb.cpp

 #endif
 
 #include <algorithm>
+#include <boost/algorithm/string/predicate.hpp>
 #include <boost/filesystem/fstream.hpp>
 #include <boost/filesystem/operations.hpp>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #pragma warning(pop)
 #endif
 
-#include <gb_debugger_msvs.h>
-
 #include <gb_emulator/common.hpp>
 #include <gb_emulator/constants.hpp>
 #include <gb_emulator/gb.hpp>
 
 namespace fs = boost::filesystem;
 namespace pb = google::protobuf;
+using boost::ends_with;
+using boost::shared_ptr;
+using boost::starts_with;
 using std::clog;
 using std::hex;
 using std::ios_base;
 using std::setfill;
 using std::setw;
 using std::string;
+using std::vector;
 
 Gb::Gb(const fs::path &bios):
 	cpu_(*this),
 		throw runtime_error("failed to get system time");
 	}
 
-	// Start the debugger
-	debugger_.init();
-	GbDebuggerMsvs debuggerMsvs;
-	debuggerMsvs.start(debugger_);
+	// Start any debugger plugins
+	loadDebuggers();
 
 	// Enter the main loop
 	int videoCycles = HBLANK_CYCLES;
 	int timerCycles = DIVIDER_CYCLES;
 	for (;;)
 	{
-		debugger_.poll();
+		// Update the debugger
+		if (!debuggers_.empty())
+		{
+			debugger_.poll();
+		}
 
+		// Execute an emulation tick
 		int cyclesExecuted = cpu_.poll((min)(videoCycles, timerCycles));
 		videoCycles -= cyclesExecuted;
 		timerCycles -= cyclesExecuted;
 	assert(f);
 }
 
+void Gb::loadDebuggers()
+{
+#ifndef _WIN32
+	// To be implemented...
+#else
+	// Find any debugger plugins in this directory
+	vector<fs::path> pluginFiles;
+	for (fs::directory_iterator it(L"."), end; it != end; ++it)
+	{
+		fs::path filename = it->path().filename();
+		if (starts_with(filename.native(), L"gb_debugger_") &&
+			ends_with(filename.native(), L".dll"))
+		{
+			pluginFiles.push_back(filename);
+		}
+	}
+
+	for (vector<fs::path>::const_iterator file = pluginFiles.begin(), end = pluginFiles.end();
+		file != end; ++file)
+	{
+		clog << "loading debugger " << *file << "...";
+
+		// Load the file
+		fs::path path = fs::absolute(*file);
+		HMODULE theModule = LoadLibrary(path.native().c_str());
+		if (!theModule)
+		{
+			clog << "failed to load library: " << GetLastError() << "\n";
+			continue;
+		}
+		shared_ptr<HINSTANCE__> module(theModule, FreeLibrary);
+
+		// Get the access function addresses
+		FARPROC getDebuggerFunction = GetProcAddress(module.get(), "getDebuggerInstance");
+		if (!getDebuggerFunction)
+		{
+			clog << "failed to locate getDebuggerInstance() in library: " << GetLastError() << "\n";
+			continue;
+		}
+		FARPROC freeDebuggerFunction = GetProcAddress(module.get(), "freeDebuggerInstance");
+		if (!freeDebuggerFunction)
+		{
+			clog << "failed to locate freeDebuggerInstance() in library: " << GetLastError() << "\n";
+			continue;
+		}
+
+		// Create an instance of the debugger
+		GbDebuggerModule * (*getDebuggerInstance)() =
+			reinterpret_cast<GbDebuggerModule * (*)()>(getDebuggerFunction);
+		void (*freeDebuggerInstance)(GbDebuggerModule *) =
+			reinterpret_cast<void (*)(GbDebuggerModule *)>(getDebuggerFunction);
+		shared_ptr<GbDebuggerModule> debugger(getDebuggerInstance(), freeDebuggerInstance);
+		if (!debugger)
+		{
+			clog << "failed to initialize debugger library\n";
+			continue;
+		}
+
+		// Library loaded; add it to the list
+		debuggerModules_.push_back(module);
+		debuggers_.push_back(debugger);
+	}
+#endif
+
+	// Start the loaded debuggers
+	if (!debuggers_.empty())
+	{
+		debugger_.init();
+		for (vector<shared_ptr<GbDebuggerModule> >::const_iterator debugger = debuggers_.begin(),
+			end = debuggers_.end(); debugger != end; ++debugger)
+		{
+			(*debugger)->start(debugger_);
+		}
+	}
+}
+
 #ifndef _WIN32
 // Subtract timespec structs. Returns 1 if the result is negative. This is adapted from the code
 // available here: http://www.gnu.org/s/libc/manual/html_node/Elapsed-Time.html

gb_emulator/src/gb_cpu.cpp

 	while (cycles > 0)
 	{
 		// Update the debugger and stop execution if requested
-		if (!gb_.debugger_.cpuTick())
+		if (!gb_.debuggers_.empty() && !gb_.debugger_.cpuTick())
 		{
 			break;
 		}
 		if (interrupts & 0x1f)
 		{
 			// Notify the debugger
-			gb_.debugger_.interrupt(interrupts);
+			if (!gb_.debuggers_.empty())
+			{
+				gb_.debugger_.interrupt(interrupts);
+			}
 
 			/* Disable further interrupts */
 			ime = IME_DISABLED;