Merge branch 'master' into wii-usb
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include "CoreAudioSoundStream.h"
|
||||
#include "OpenALStream.h"
|
||||
#include "PulseAudioStream.h"
|
||||
#include "../../Core/Src/Movie.h"
|
||||
|
||||
namespace AudioCommon
|
||||
{
|
||||
@@ -115,7 +116,31 @@ namespace AudioCommon
|
||||
return backends;
|
||||
}
|
||||
|
||||
bool UseJIT() {
|
||||
bool UseJIT()
|
||||
{
|
||||
if (!Movie::IsDSPHLE() && Movie::IsPlayingInput() && Movie::IsConfigSaved())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return ac_Config.m_EnableJIT;
|
||||
}
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (soundStream)
|
||||
{
|
||||
// audio typically doesn't maintain its own "paused" state
|
||||
// (that's already handled by the CPU and whatever else being paused)
|
||||
// so it should be good enough to only lock/unlock here.
|
||||
CMixer* pMixer = soundStream->GetMixer();
|
||||
if (pMixer)
|
||||
{
|
||||
std::mutex& csMixing = pMixer->MixerCritical();
|
||||
if (doLock)
|
||||
csMixing.lock();
|
||||
else
|
||||
csMixing.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace AudioCommon
|
||||
void ShutdownSoundStream();
|
||||
std::vector<std::string> GetSoundBackends();
|
||||
bool UseJIT();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
}
|
||||
|
||||
#endif // _AUDIO_COMMON_H_
|
||||
|
||||
@@ -31,7 +31,6 @@ void AudioCommonConfig::Load()
|
||||
IniFile file;
|
||||
file.Load(File::GetUserPath(F_DSPCONFIG_IDX));
|
||||
|
||||
file.Get("Config", "EnableDTKMusic", &m_EnableDTKMusic, true);
|
||||
file.Get("Config", "EnableJIT", &m_EnableJIT, true);
|
||||
file.Get("Config", "DumpAudio", &m_DumpAudio, false);
|
||||
#if defined __linux__ && HAVE_ALSA
|
||||
@@ -53,7 +52,6 @@ void AudioCommonConfig::SaveSettings()
|
||||
IniFile file;
|
||||
file.Load(File::GetUserPath(F_DSPCONFIG_IDX));
|
||||
|
||||
file.Set("Config", "EnableDTKMusic", m_EnableDTKMusic);
|
||||
file.Set("Config", "EnableJIT", m_EnableJIT);
|
||||
file.Set("Config", "DumpAudio", m_DumpAudio);
|
||||
file.Set("Config", "Backend", sBackend);
|
||||
@@ -67,7 +65,6 @@ void AudioCommonConfig::SaveSettings()
|
||||
void AudioCommonConfig::Update() {
|
||||
if (soundStream) {
|
||||
soundStream->GetMixer()->SetThrottle(SConfig::GetInstance().m_Framelimit == 2);
|
||||
soundStream->GetMixer()->SetDTKMusic(m_EnableDTKMusic);
|
||||
soundStream->SetVolume(m_Volume);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
struct AudioCommonConfig
|
||||
{
|
||||
bool m_EnableDTKMusic;
|
||||
bool m_EnableJIT;
|
||||
bool m_DumpAudio;
|
||||
int m_Volume;
|
||||
|
||||
@@ -37,7 +37,9 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples)
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
if (PowerPC::GetState() != 0)
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
memset(samples, 0, numSamples * 4);
|
||||
@@ -136,8 +138,7 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples)
|
||||
if (m_AIplaying) {
|
||||
Premix(samples, numLeft);
|
||||
|
||||
if (m_EnableDTKMusic)
|
||||
AudioInterface::Callback_GetStreaming(samples, numLeft, m_sampleRate);
|
||||
AudioInterface::Callback_GetStreaming(samples, numLeft, m_sampleRate);
|
||||
|
||||
g_wave_writer.AddStereoSamples(samples, numLeft);
|
||||
}
|
||||
@@ -147,11 +148,8 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples)
|
||||
Premix(samples, numSamples);
|
||||
|
||||
// Add the DTK Music
|
||||
if (m_EnableDTKMusic)
|
||||
{
|
||||
// Re-sampling is done inside
|
||||
AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate);
|
||||
}
|
||||
// Re-sampling is done inside
|
||||
AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +166,7 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||
while (num_samples + Common::AtomicLoad(m_numSamples) > MAX_SAMPLES)
|
||||
{
|
||||
if (*PowerPC::GetStatePtr() != 0)
|
||||
if (*PowerPC::GetStatePtr() != PowerPC::CPU_RUNNING || soundStream->IsMuted())
|
||||
break;
|
||||
// Shortcut key for Throttle Skipping
|
||||
if (Host_GetKeyState('\t'))
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define _MIXER_H_
|
||||
|
||||
#include "WaveFile.h"
|
||||
#include "StdMutex.h"
|
||||
|
||||
// 16 bit Stereo
|
||||
#define MAX_SAMPLES (1024 * 8)
|
||||
@@ -61,7 +62,6 @@ public:
|
||||
unsigned int GetSampleRate() {return m_sampleRate;}
|
||||
|
||||
void SetThrottle(bool use) { m_throttle = use;}
|
||||
void SetDTKMusic(bool use) { m_EnableDTKMusic = use;}
|
||||
|
||||
// TODO: do we need this
|
||||
bool IsHLEReady() { return m_HLEready;}
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex& MixerCritical() { return m_csMixing; }
|
||||
|
||||
protected:
|
||||
unsigned int m_sampleRate;
|
||||
@@ -103,7 +104,6 @@ protected:
|
||||
bool m_HLEready;
|
||||
bool m_logAudio;
|
||||
|
||||
bool m_EnableDTKMusic;
|
||||
bool m_throttle;
|
||||
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
@@ -112,7 +112,7 @@ protected:
|
||||
u32 m_indexR;
|
||||
|
||||
bool m_AIplaying;
|
||||
|
||||
std::mutex m_csMixing;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "AudioCommon.h"
|
||||
#include "NullSoundStream.h"
|
||||
#include "../../Core/Src/HW/SystemTimers.h"
|
||||
#include "../../Core/Src/HW/AudioInterface.h"
|
||||
|
||||
void NullSound::SoundLoop()
|
||||
{
|
||||
@@ -33,9 +35,14 @@ void NullSound::SetVolume(int volume)
|
||||
|
||||
void NullSound::Update()
|
||||
{
|
||||
// This should equal AUDIO_DMA_PERIOD. TODO: Fix after DSP merge
|
||||
int numBytesToRender = 32000 * 4 / 32;
|
||||
m_mixer->Mix(realtimeBuffer, numBytesToRender / 4);
|
||||
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
|
||||
const u32 stereo_16_bit_size = 4;
|
||||
const u32 dma_length = 32;
|
||||
const u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
|
||||
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
|
||||
const u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
|
||||
|
||||
m_mixer->Mix(realtimeBuffer, (unsigned int)num_samples_to_render);
|
||||
}
|
||||
|
||||
void NullSound::Clear(bool mute)
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
virtual void Stop() {}
|
||||
virtual void Update() {}
|
||||
virtual void Clear(bool mute) { m_muted = mute; }
|
||||
bool IsMuted() { return m_muted; }
|
||||
virtual void StartLogAudio(const char *filename) {
|
||||
if (! m_logAudio) {
|
||||
m_logAudio = true;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CDUtils.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include <memory> // for std::unique_ptr
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif __APPLE__
|
||||
|
||||
@@ -77,12 +77,6 @@ public:
|
||||
|
||||
template<class T>
|
||||
void Do(std::map<unsigned int, T> &x)
|
||||
{
|
||||
// TODO
|
||||
PanicAlert("Do(map<>) does not yet work.");
|
||||
}
|
||||
|
||||
void Do(std::map<unsigned int, std::string> &x)
|
||||
{
|
||||
unsigned int number = (unsigned int)x.size();
|
||||
Do(number);
|
||||
@@ -94,7 +88,7 @@ public:
|
||||
{
|
||||
unsigned int first = 0;
|
||||
Do(first);
|
||||
std::string second;
|
||||
T second;
|
||||
Do(second);
|
||||
x[first] = second;
|
||||
--number;
|
||||
@@ -105,7 +99,7 @@ public:
|
||||
case MODE_MEASURE:
|
||||
case MODE_VERIFY:
|
||||
{
|
||||
std::map<unsigned int, std::string>::iterator itr = x.begin();
|
||||
typename std::map<unsigned int, T>::iterator itr = x.begin();
|
||||
while (number > 0)
|
||||
{
|
||||
Do(itr->first);
|
||||
@@ -178,11 +172,74 @@ public:
|
||||
void Do(T &x) {
|
||||
DoVoid((void *)&x, sizeof(x));
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void DoLinkedList(LinkedListItem<T> **list_start) {
|
||||
// TODO
|
||||
PanicAlert("Do(linked list<>) does not yet work.");
|
||||
void DoPointer(T* &x, T*const base) {
|
||||
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range
|
||||
s32 offset = x - base;
|
||||
Do(offset);
|
||||
if (mode == MODE_READ)
|
||||
x = base + offset;
|
||||
}
|
||||
|
||||
template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)>
|
||||
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0)
|
||||
{
|
||||
LinkedListItem<T>* list_cur = list_start;
|
||||
LinkedListItem<T>* prev = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
u8 shouldExist = (list_cur ? 1 : 0);
|
||||
Do(shouldExist);
|
||||
if (shouldExist == 1)
|
||||
{
|
||||
LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
|
||||
TDo(*this, (T*)cur);
|
||||
if (!list_cur)
|
||||
{
|
||||
if (mode == MODE_READ)
|
||||
{
|
||||
cur->next = 0;
|
||||
list_cur = cur;
|
||||
if (prev)
|
||||
prev->next = cur;
|
||||
else
|
||||
list_start = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
TFree(cur);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == MODE_READ)
|
||||
{
|
||||
if (prev)
|
||||
prev->next = 0;
|
||||
if (list_end)
|
||||
*list_end = prev;
|
||||
if (list_cur)
|
||||
{
|
||||
if (list_start == list_cur)
|
||||
list_start = 0;
|
||||
do
|
||||
{
|
||||
LinkedListItem<T>* next = list_cur->next;
|
||||
TFree(list_cur);
|
||||
list_cur = next;
|
||||
}
|
||||
while (list_cur);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = list_cur;
|
||||
list_cur = list_cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
void DoMarker(const char* prevName, u32 arbitraryNumber=0x42)
|
||||
|
||||
@@ -31,7 +31,7 @@ const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF };
|
||||
|
||||
u32 Decode5A3(u16 val)
|
||||
{
|
||||
const u32 bg_color = 0x00000000;
|
||||
const u32 bg_color = 0xFFFFFFFF;
|
||||
|
||||
int r, g, b, a;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
// Files in the directory returned by GetUserPath(D_DUMP_IDX)
|
||||
#define RAM_DUMP "ram.raw"
|
||||
#define ARAM_DUMP "aram.raw"
|
||||
#define FAKEVMEM_DUMP "fakevmem.raw"
|
||||
|
||||
// Sys files
|
||||
#define TOTALDB "totaldb.dsy"
|
||||
@@ -121,6 +122,8 @@
|
||||
#define GC_MEMCARDA "MemoryCardA"
|
||||
#define GC_MEMCARDB "MemoryCardB"
|
||||
|
||||
#define WII_STATE "state.dat"
|
||||
|
||||
#define WII_SETTING "setting.txt"
|
||||
#define WII_EUR_SETTING "setting-eur.txt"
|
||||
#define WII_USA_SETTING "setting-usa.txt"
|
||||
|
||||
@@ -35,6 +35,9 @@ ConsoleListener::ConsoleListener()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
hConsole = NULL;
|
||||
bUseColor = true;
|
||||
#else
|
||||
bUseColor = isatty(fileno(stdout));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -299,7 +302,28 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
|
||||
SetConsoleTextAttribute(hConsole, Color);
|
||||
WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL);
|
||||
#else
|
||||
fprintf(stderr, "%s", Text);
|
||||
char ColorAttr[16] = "";
|
||||
char ResetAttr[16] = "";
|
||||
|
||||
if (bUseColor)
|
||||
{
|
||||
strcpy(ResetAttr, "\033[0m");
|
||||
switch (Level)
|
||||
{
|
||||
case NOTICE_LEVEL: // light green
|
||||
strcpy(ColorAttr, "\033[92m");
|
||||
break;
|
||||
case ERROR_LEVEL: // light red
|
||||
strcpy(ColorAttr, "\033[91m");
|
||||
break;
|
||||
case WARNING_LEVEL: // light yellow
|
||||
strcpy(ColorAttr, "\033[93m");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
|
||||
#endif
|
||||
}
|
||||
// Clear console screen
|
||||
|
||||
@@ -48,6 +48,7 @@ private:
|
||||
HWND GetHwnd(void);
|
||||
HANDLE hConsole;
|
||||
#endif
|
||||
bool bUseColor;
|
||||
};
|
||||
|
||||
#endif // _CONSOLELISTENER_H
|
||||
|
||||
@@ -682,6 +682,7 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath)
|
||||
paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF;
|
||||
paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP;
|
||||
paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP;
|
||||
paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP;
|
||||
paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ enum {
|
||||
F_WIISYSCONF_IDX,
|
||||
F_RAMDUMP_IDX,
|
||||
F_ARAMDUMP_IDX,
|
||||
F_FAKEVMEMDUMP_IDX,
|
||||
F_GCSRAM_IDX,
|
||||
NUM_PATH_INDICES
|
||||
};
|
||||
|
||||
@@ -115,11 +115,11 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ERROR_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) }
|
||||
#define WARN_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) }
|
||||
#define NOTICE_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) }
|
||||
#define INFO_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) }
|
||||
#define DEBUG_LOG(t,...) { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) }
|
||||
#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
|
||||
#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
|
||||
#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
|
||||
#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0)
|
||||
#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0)
|
||||
|
||||
#if MAX_LOGLEVEL >= DEBUG_LEVEL
|
||||
#define _dbg_assert_(_t_, _a_) \
|
||||
|
||||
@@ -161,6 +161,7 @@ void LoadDefaultSSEState();
|
||||
float MathFloatVectorSum(const std::vector<float>&);
|
||||
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define ROUND_DOWN(x, a) ((x) & ~((a) - 1))
|
||||
|
||||
|
||||
// Tiny matrix/vector library.
|
||||
|
||||
@@ -29,8 +29,10 @@
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#if defined(__APPLE__)
|
||||
static const char* ram_temp_file = "/tmp/gc_mem.tmp";
|
||||
#elif !defined(_WIN32) // non OSX unixes
|
||||
static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
|
||||
#endif
|
||||
|
||||
void MemArena::GrabLowMemSpace(size_t size)
|
||||
@@ -40,6 +42,7 @@ void MemArena::GrabLowMemSpace(size_t size)
|
||||
#else
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
fd = open(ram_temp_file, O_RDWR | O_CREAT, mode);
|
||||
unlink(ram_temp_file);
|
||||
ftruncate(fd, size);
|
||||
return;
|
||||
#endif
|
||||
@@ -53,27 +56,30 @@ void MemArena::ReleaseSpace()
|
||||
hMemoryMapping = 0;
|
||||
#else
|
||||
close(fd);
|
||||
unlink(ram_temp_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* MemArena::CreateView(s64 offset, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return MapViewOfFile(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size);
|
||||
#else
|
||||
return mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* MemArena::CreateViewAt(s64 offset, size_t size, void* base)
|
||||
void *MemArena::CreateView(s64 offset, size_t size, void *base)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
|
||||
#else
|
||||
return mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset);
|
||||
void *retval = mmap(
|
||||
base, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED),
|
||||
fd, offset);
|
||||
|
||||
if (retval == MAP_FAILED)
|
||||
{
|
||||
NOTICE_LOG(MEMMAP, "mmap on %s failed", ram_temp_file);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -112,8 +118,7 @@ u8* MemArena::Find4GBBase()
|
||||
}
|
||||
return base;
|
||||
#else
|
||||
void* base = mmap(0, 0x31000000, PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_SHARED, -1, 0);
|
||||
void* base = mmap(0, 0x31000000, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED) {
|
||||
PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno));
|
||||
return 0;
|
||||
@@ -160,14 +165,14 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
|
||||
goto bail;
|
||||
}
|
||||
#ifdef _M_X64
|
||||
*views[i].out_ptr = (u8*)arena->CreateViewAt(
|
||||
*views[i].out_ptr = (u8*)arena->CreateView(
|
||||
position, views[i].size, base + views[i].virtual_address);
|
||||
#else
|
||||
if (views[i].flags & MV_MIRROR_PREVIOUS) {
|
||||
// No need to create multiple identical views.
|
||||
*views[i].out_ptr = *views[i - 1].out_ptr;
|
||||
} else {
|
||||
*views[i].out_ptr = (u8*)arena->CreateViewAt(
|
||||
*views[i].out_ptr = (u8*)arena->CreateView(
|
||||
position, views[i].size, base + (views[i].virtual_address & 0x3FFFFFFF));
|
||||
if (!*views[i].out_ptr)
|
||||
goto bail;
|
||||
|
||||
@@ -33,12 +33,11 @@ class MemArena
|
||||
public:
|
||||
void GrabLowMemSpace(size_t size);
|
||||
void ReleaseSpace();
|
||||
void* CreateView(s64 offset, size_t size);
|
||||
void* CreateViewAt(s64 offset, size_t size, void* base);
|
||||
void ReleaseView(void* view, size_t size);
|
||||
void *CreateView(s64 offset, size_t size, void *base = nullptr);
|
||||
void ReleaseView(void *view, size_t size);
|
||||
|
||||
// This only finds 1 GB in 32-bit
|
||||
static u8* Find4GBBase();
|
||||
static u8 *Find4GBBase();
|
||||
private:
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace this_thread
|
||||
inline void yield()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Sleep(0);
|
||||
SwitchToThread();
|
||||
#else
|
||||
sleep(0);
|
||||
#endif
|
||||
|
||||
@@ -69,6 +69,9 @@ void VideoBackend::ClearList()
|
||||
|
||||
void VideoBackend::ActivateBackend(const std::string& name)
|
||||
{
|
||||
if (name.length() == 0) // If NULL, set it to the first one in the list. Expected behavior
|
||||
g_video_backend = g_available_video_backends.front();
|
||||
|
||||
for (std::vector<VideoBackend*>::const_iterator it = g_available_video_backends.begin(); it != g_available_video_backends.end(); ++it)
|
||||
if (name == (*it)->GetName())
|
||||
g_video_backend = *it;
|
||||
|
||||
@@ -93,8 +93,6 @@ public:
|
||||
|
||||
virtual bool Initialize(void *&) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
virtual void RunLoop(bool enable) = 0;
|
||||
|
||||
virtual std::string GetName() = 0;
|
||||
@@ -131,6 +129,14 @@ public:
|
||||
static void PopulateList();
|
||||
static void ClearList();
|
||||
static void ActivateBackend(const std::string& name);
|
||||
|
||||
// waits until is paused and fully idle, and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally unpauses.
|
||||
// calls must be balanced and non-recursive (once with doLock true, then once with doLock false).
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) = 0;
|
||||
|
||||
// the implementation needs not do synchronization logic, because calls to it are surrounded by PauseAndLock now
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
};
|
||||
|
||||
extern std::vector<VideoBackend*> g_available_video_backends;
|
||||
@@ -139,7 +145,6 @@ extern VideoBackend* g_video_backend;
|
||||
// inherited by dx9/dx11/ogl backends
|
||||
class VideoBackendHardware : public VideoBackend
|
||||
{
|
||||
void DoState(PointerWrap &p);
|
||||
void RunLoop(bool enable);
|
||||
bool Initialize(void *&) { InitializeShared(); return true; }
|
||||
|
||||
@@ -169,6 +174,9 @@ class VideoBackendHardware : public VideoBackend
|
||||
writeFn16 Video_PEWrite16();
|
||||
writeFn32 Video_PEWrite32();
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
protected:
|
||||
void InitializeShared();
|
||||
};
|
||||
|
||||
@@ -1321,7 +1321,7 @@ void XEmitter::MOVDDUP(X64Reg regOp, OpArg arg)
|
||||
{
|
||||
// Simulate this instruction with SSE2 instructions
|
||||
if (!arg.IsSimpleReg(regOp))
|
||||
MOVQ_xmm(regOp, arg); // MOVSD better?
|
||||
MOVSD(regOp, arg);
|
||||
UNPCKLPD(regOp, R(regOp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Common.h" // Common
|
||||
#include "StringUtil.h"
|
||||
#include "FileUtil.h"
|
||||
#include "MathUtil.h"
|
||||
|
||||
#include "../HLE/HLE.h" // Core
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
@@ -68,13 +69,13 @@ void CBoot::Load_FST(bool _bIsWii)
|
||||
u32 fstSize = VolumeHandler::Read32(0x0428) << shift;
|
||||
u32 maxFstSize = VolumeHandler::Read32(0x042c) << shift;
|
||||
|
||||
u32 arenaHigh = 0x817FFFF4 - maxFstSize;
|
||||
u32 arenaHigh = ROUND_DOWN(0x817FFFFF - maxFstSize, 0x20);
|
||||
Memory::Write_U32(arenaHigh, 0x00000034);
|
||||
|
||||
// load FST
|
||||
VolumeHandler::ReadToPtr(Memory::GetPointer(arenaHigh), fstOffset, fstSize);
|
||||
Memory::Write_U32(arenaHigh, 0x00000038);
|
||||
Memory::Write_U32(maxFstSize, 0x0000003c);
|
||||
Memory::Write_U32(maxFstSize, 0x0000003c);
|
||||
}
|
||||
|
||||
void CBoot::UpdateDebugger_MapLoaded(const char *_gameID)
|
||||
|
||||
@@ -34,12 +34,35 @@ bool CBoot::IsElfWii(const char *filename)
|
||||
File::IOFile f(filename, "rb");
|
||||
f.ReadBytes(mem, (size_t)filesize);
|
||||
}
|
||||
|
||||
ElfReader reader(mem);
|
||||
// TODO: Find a more reliable way to distinguish.
|
||||
bool isWii = reader.GetEntryPoint() >= 0x80004000;
|
||||
delete[] mem;
|
||||
|
||||
// Use the same method as the DOL loader uses: search for mfspr from HID4,
|
||||
// which should only be used in Wii ELFs.
|
||||
//
|
||||
// Likely to have some false positives/negatives, patches implementing a
|
||||
// better heuristic are welcome.
|
||||
|
||||
u32 HID4_pattern = 0x7c13fba6;
|
||||
u32 HID4_mask = 0xfc1fffff;
|
||||
ElfReader reader(mem);
|
||||
bool isWii = false;
|
||||
|
||||
for (int i = 0; i < reader.GetNumSections(); ++i)
|
||||
{
|
||||
if (reader.IsCodeSection(i))
|
||||
{
|
||||
for (unsigned int j = 0; j < reader.GetSectionSize(i) / sizeof (u32); ++j)
|
||||
{
|
||||
u32 word = Common::swap32(((u32*)reader.GetSectionDataPtr(i))[j]);
|
||||
if ((word & HID4_mask) == HID4_pattern)
|
||||
{
|
||||
isWii = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mem;
|
||||
return isWii;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,57 @@
|
||||
#include "VolumeCreator.h"
|
||||
#include "CommonPaths.h"
|
||||
|
||||
static u32 state_checksum(u32 *buf, int len)
|
||||
{
|
||||
u32 checksum = 0;
|
||||
len = len>>2;
|
||||
|
||||
for(int i=0; i<len; i++)
|
||||
{
|
||||
checksum += buf[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u32 checksum;
|
||||
u8 flags;
|
||||
u8 type;
|
||||
u8 discstate;
|
||||
u8 returnto;
|
||||
u32 unknown[6];
|
||||
} StateFlags;
|
||||
|
||||
bool CBoot::Boot_WiiWAD(const char* _pFilename)
|
||||
{
|
||||
|
||||
std::string state_filename(Common::GetTitleDataPath(TITLEID_SYSMENU) + WII_STATE);
|
||||
|
||||
if (File::Exists(state_filename))
|
||||
{
|
||||
File::IOFile state_file(state_filename, "r+b");
|
||||
StateFlags state;
|
||||
state_file.ReadBytes(&state, sizeof(StateFlags));
|
||||
|
||||
state.type = 0x03; // TYPE_RETURN
|
||||
state.checksum = state_checksum((u32*)&state.flags, sizeof(StateFlags)-4);
|
||||
|
||||
state_file.Seek(0, SEEK_SET);
|
||||
state_file.WriteBytes(&state, sizeof(StateFlags));
|
||||
}
|
||||
else
|
||||
{
|
||||
File::CreateFullPath(state_filename);
|
||||
File::IOFile state_file(state_filename, "a+b");
|
||||
StateFlags state;
|
||||
memset(&state,0,sizeof(StateFlags));
|
||||
state.type = 0x03; // TYPE_RETURN
|
||||
state.discstate = 0x01; // DISCSTATE_WII
|
||||
state.checksum = state_checksum((u32*)&state.flags, sizeof(StateFlags)-4);
|
||||
state_file.WriteBytes(&state, sizeof(StateFlags));
|
||||
}
|
||||
|
||||
const DiscIO::INANDContentLoader& ContentLoader = DiscIO::CNANDContentManager::Access().GetNANDLoader(_pFilename);
|
||||
if (!ContentLoader.IsValid())
|
||||
return false;
|
||||
|
||||
@@ -59,7 +59,6 @@ public:
|
||||
bool LoadInto(u32 vaddr);
|
||||
bool LoadSymbols();
|
||||
|
||||
private:
|
||||
int GetNumSegments() const { return (int)(header->e_phnum); }
|
||||
int GetNumSections() const { return (int)(header->e_shnum); }
|
||||
const u8 *GetPtr(int offset) const { return (u8*)base + offset; }
|
||||
@@ -73,6 +72,10 @@ private:
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
bool IsCodeSection(int section) const
|
||||
{
|
||||
return sections[section].sh_type == SHT_PROGBITS;
|
||||
}
|
||||
const u8 *GetSegmentPtr(int segment)
|
||||
{
|
||||
return GetPtr(segments[segment].p_offset);
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include "SysConf.h"
|
||||
#include "Core.h"
|
||||
#include "Host.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "Movie.h"
|
||||
|
||||
|
||||
namespace BootManager
|
||||
@@ -56,6 +58,7 @@ struct ConfigCache
|
||||
bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bMMUBAT,
|
||||
bVBeam, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bDisableWiimoteSpeaker;
|
||||
int iTLBHack;
|
||||
std::string strBackend;
|
||||
};
|
||||
static ConfigCache config_cache;
|
||||
|
||||
@@ -96,6 +99,7 @@ bool BootCore(const std::string& _rFilename)
|
||||
config_cache.bMergeBlocks = StartUp.bMergeBlocks;
|
||||
config_cache.bDSPHLE = StartUp.bDSPHLE;
|
||||
config_cache.bDisableWiimoteSpeaker = StartUp.bDisableWiimoteSpeaker;
|
||||
config_cache.strBackend = StartUp.m_strVideoBackend;
|
||||
|
||||
// General settings
|
||||
game_ini.Get("Core", "CPUThread", &StartUp.bCPUThread, StartUp.bCPUThread);
|
||||
@@ -109,7 +113,23 @@ bool BootCore(const std::string& _rFilename)
|
||||
game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks);
|
||||
game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE);
|
||||
game_ini.Get("Wii", "DisableWiimoteSpeaker",&StartUp.bDisableWiimoteSpeaker, StartUp.bDisableWiimoteSpeaker);
|
||||
game_ini.Get("Core", "GFXBackend", &StartUp.m_strVideoBackend, StartUp.m_strVideoBackend.c_str());
|
||||
VideoBackend::ActivateBackend(StartUp.m_strVideoBackend);
|
||||
|
||||
if (Movie::IsPlayingInput() && Movie::IsConfigSaved())
|
||||
{
|
||||
Movie::Init();
|
||||
StartUp.bCPUThread = Movie::IsDualCore();
|
||||
StartUp.bSkipIdle = Movie::IsSkipIdle();
|
||||
StartUp.bDSPHLE = Movie::IsDSPHLE();
|
||||
StartUp.bProgressive = Movie::IsProgressive();
|
||||
StartUp.bFastDiscSpeed = Movie::IsFastDiscSpeed();
|
||||
if (Movie::IsUsingMemcard() && Movie::IsBlankMemcard())
|
||||
{
|
||||
if (File::Exists("Movie.raw"))
|
||||
File::Delete("Movie.raw");
|
||||
}
|
||||
}
|
||||
// Wii settings
|
||||
if (StartUp.bWii)
|
||||
{
|
||||
@@ -149,6 +169,8 @@ void Stop()
|
||||
StartUp.bMergeBlocks = config_cache.bMergeBlocks;
|
||||
StartUp.bDSPHLE = config_cache.bDSPHLE;
|
||||
StartUp.bDisableWiimoteSpeaker = config_cache.bDisableWiimoteSpeaker;
|
||||
StartUp.m_strVideoBackend = config_cache.strBackend;
|
||||
VideoBackend::ActivateBackend(StartUp.m_strVideoBackend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ static const struct {
|
||||
const int DefaultModifier;
|
||||
} g_HKData[] = {
|
||||
#ifdef __APPLE__
|
||||
{ "Open", 79 /* 'O' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Open", 79 /* 'O' */, 2 /* wxMOD_CMD */ },
|
||||
{ "ChangeDisc", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "RefreshList", 0, 0 /* wxMOD_NONE */ },
|
||||
|
||||
{ "PlayPause", 80 /* 'P' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Stop", 87 /* 'W' */, 8 /* wxMOD_CMD */ },
|
||||
{ "PlayPause", 80 /* 'P' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Stop", 87 /* 'W' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Reset", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "FrameAdvance", 0, 0 /* wxMOD_NONE */ },
|
||||
|
||||
@@ -46,13 +46,13 @@ static const struct {
|
||||
{ "ExportRecording", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "Readonlymode", 0, 0 /* wxMOD_NONE */ },
|
||||
|
||||
{ "ToggleFullscreen", 70 /* 'F' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Screenshot", 83 /* 'S' */, 8 /* wxMOD_CMD */ },
|
||||
{ "ToggleFullscreen", 70 /* 'F' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Screenshot", 83 /* 'S' */, 2 /* wxMOD_CMD */ },
|
||||
|
||||
{ "Wiimote1Connect", 49 /* '1' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Wiimote2Connect", 50 /* '2' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Wiimote3Connect", 51 /* '3' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Wiimote4Connect", 52 /* '4' */, 8 /* wxMOD_CMD */ },
|
||||
{ "Wiimote1Connect", 49 /* '1' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Wiimote2Connect", 50 /* '2' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Wiimote3Connect", 51 /* '3' */, 2 /* wxMOD_CMD */ },
|
||||
{ "Wiimote4Connect", 52 /* '4' */, 2 /* wxMOD_CMD */ },
|
||||
#else
|
||||
{ "Open", 79 /* 'O' */, 2 /* wxMOD_CONTROL */},
|
||||
{ "ChangeDisc", 0, 0 /* wxMOD_NONE */ },
|
||||
@@ -190,6 +190,7 @@ void SConfig::SaveSettings()
|
||||
ini.Set("Display", "RenderWindowWidth", m_LocalCoreStartupParameter.iRenderWindowWidth);
|
||||
ini.Set("Display", "RenderWindowHeight", m_LocalCoreStartupParameter.iRenderWindowHeight);
|
||||
ini.Set("Display", "RenderWindowAutoSize", m_LocalCoreStartupParameter.bRenderWindowAutoSize);
|
||||
ini.Set("Display", "KeepWindowOnTop", m_LocalCoreStartupParameter.bKeepWindowOnTop);
|
||||
ini.Set("Display", "ProgressiveScan", m_LocalCoreStartupParameter.bProgressive);
|
||||
ini.Set("Display", "DisableScreenSaver", m_LocalCoreStartupParameter.bDisableScreenSaver);
|
||||
ini.Set("Display", "ForceNTSCJ", m_LocalCoreStartupParameter.bForceNTSCJ);
|
||||
@@ -319,6 +320,7 @@ void SConfig::LoadSettings()
|
||||
ini.Get("Display", "RenderWindowWidth", &m_LocalCoreStartupParameter.iRenderWindowWidth, 640);
|
||||
ini.Get("Display", "RenderWindowHeight", &m_LocalCoreStartupParameter.iRenderWindowHeight, 480);
|
||||
ini.Get("Display", "RenderWindowAutoSize", &m_LocalCoreStartupParameter.bRenderWindowAutoSize, false);
|
||||
ini.Get("Display", "KeepWindowOnTop", &m_LocalCoreStartupParameter.bKeepWindowOnTop, false);
|
||||
ini.Get("Display", "ProgressiveScan", &m_LocalCoreStartupParameter.bProgressive, false);
|
||||
ini.Get("Display", "DisableScreenSaver", &m_LocalCoreStartupParameter.bDisableScreenSaver, true);
|
||||
ini.Get("Display", "ForceNTSCJ", &m_LocalCoreStartupParameter.bForceNTSCJ, false);
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "HW/GPFifo.h"
|
||||
#include "HW/AudioInterface.h"
|
||||
#include "HW/VideoInterface.h"
|
||||
#include "HW/EXI.h"
|
||||
#include "HW/SystemTimers.h"
|
||||
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
@@ -58,6 +59,7 @@
|
||||
#include "DSPEmulator.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "AudioCommon.h"
|
||||
#include "OnScreenDisplay.h"
|
||||
#ifdef _WIN32
|
||||
#include "EmuWindow.h"
|
||||
@@ -95,13 +97,15 @@ void Stop();
|
||||
|
||||
bool g_bStopping = false;
|
||||
bool g_bHwInit = false;
|
||||
bool g_bStarted = false;
|
||||
bool g_bRealWiimote = false;
|
||||
void *g_pWindowHandle = NULL;
|
||||
std::string g_stateFileName;
|
||||
std::thread g_EmuThread;
|
||||
|
||||
static std::thread g_cpu_thread;
|
||||
static bool g_requestRefreshInfo;
|
||||
static bool g_requestRefreshInfo = false;
|
||||
static int g_pauseAndLockDepth = 0;
|
||||
|
||||
SCoreStartupParameter g_CoreStartupParameter;
|
||||
|
||||
@@ -160,16 +164,35 @@ bool IsRunning()
|
||||
return (GetState() != CORE_UNINITIALIZED) || g_bHwInit;
|
||||
}
|
||||
|
||||
bool IsRunningAndStarted()
|
||||
{
|
||||
return g_bStarted;
|
||||
}
|
||||
|
||||
bool IsRunningInCurrentThread()
|
||||
{
|
||||
return IsRunning() && ((!g_cpu_thread.joinable()) || g_cpu_thread.get_id() == std::this_thread::get_id());
|
||||
return IsRunning() && IsCPUThread();
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
{
|
||||
return ((!g_cpu_thread.joinable()) || g_cpu_thread.get_id() == std::this_thread::get_id());
|
||||
return (g_cpu_thread.joinable() ? (g_cpu_thread.get_id() == std::this_thread::get_id()) : !g_bStarted);
|
||||
}
|
||||
|
||||
bool IsGPUThread()
|
||||
{
|
||||
const SCoreStartupParameter& _CoreParameter =
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
if (_CoreParameter.bCPUThread)
|
||||
{
|
||||
return (g_EmuThread.joinable() && (g_EmuThread.get_id() == std::this_thread::get_id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsCPUThread();
|
||||
}
|
||||
}
|
||||
|
||||
// This is called from the GUI thread. See the booting call schedule in
|
||||
// BootManager.cpp
|
||||
bool Init()
|
||||
@@ -300,9 +323,13 @@ void CpuThread()
|
||||
if (!g_stateFileName.empty())
|
||||
State::LoadAs(g_stateFileName);
|
||||
|
||||
g_bStarted = true;
|
||||
|
||||
// Enter CPU run loop. When we leave it - we are done.
|
||||
CCPU::Run();
|
||||
|
||||
g_bStarted = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,6 +350,8 @@ void FifoPlayerThread()
|
||||
if (_CoreParameter.bLockThreads)
|
||||
Common::SetCurrentThreadAffinity(1); // Force to first core
|
||||
|
||||
g_bStarted = true;
|
||||
|
||||
// Enter CPU run loop. When we leave it - we are done.
|
||||
if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename))
|
||||
{
|
||||
@@ -330,6 +359,8 @@ void FifoPlayerThread()
|
||||
FifoPlayer::GetInstance().Close();
|
||||
}
|
||||
|
||||
g_bStarted = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -403,7 +434,7 @@ void EmuThread()
|
||||
CBoot::BootUp();
|
||||
|
||||
// Setup our core, but can't use dynarec if we are compare server
|
||||
if (_CoreParameter.iCPUCore && (!_CoreParameter.bRunCompareServer ||
|
||||
if (Movie::GetCPUMode() && (!_CoreParameter.bRunCompareServer ||
|
||||
_CoreParameter.bRunCompareClient))
|
||||
PowerPC::SetMode(PowerPC::MODE_JIT);
|
||||
else
|
||||
@@ -554,6 +585,26 @@ void RequestRefreshInfo()
|
||||
g_requestRefreshInfo = true;
|
||||
}
|
||||
|
||||
bool PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
// let's support recursive locking to simplify things on the caller's side,
|
||||
// and let's do it at this outer level in case the individual systems don't support it.
|
||||
if (doLock ? g_pauseAndLockDepth++ : --g_pauseAndLockDepth)
|
||||
return true;
|
||||
|
||||
// first pause or unpause the cpu
|
||||
bool wasUnpaused = CCPU::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
|
||||
// audio has to come after cpu, because cpu thread can wait for audio thread (m_throttle).
|
||||
AudioCommon::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
|
||||
// video has to come after cpu, because cpu thread can wait for video thread (s_efbAccessRequested).
|
||||
g_video_backend->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
return wasUnpaused;
|
||||
}
|
||||
|
||||
// Apply Frame Limit and Display FPS info
|
||||
// This should only be called from VI
|
||||
void VideoThrottle()
|
||||
@@ -583,9 +634,12 @@ void VideoThrottle()
|
||||
g_requestRefreshInfo = false;
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
if (ElapseTime == 0)
|
||||
ElapseTime = 1;
|
||||
|
||||
u32 FPS = Common::AtomicLoad(DrawnFrame) * 1000 / ElapseTime;
|
||||
u32 VPS = DrawnVideo * 1000 / ElapseTime;
|
||||
u32 Speed = VPS * 100 / VideoInterface::TargetRefreshRate;
|
||||
u32 Speed = DrawnVideo * (100 * 1000) / (VideoInterface::TargetRefreshRate * ElapseTime);
|
||||
|
||||
// Settings are shown the same for both extended and summary info
|
||||
std::string SSettings = StringFromFormat("%s %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC");
|
||||
|
||||
@@ -54,9 +54,11 @@ void Stop();
|
||||
std::string StopMessage(bool, std::string);
|
||||
|
||||
bool IsRunning();
|
||||
bool IsRunningAndStarted(); // is running and the cpu loop has been entered
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the cpu thread.
|
||||
bool IsCPUThread(); // this tells us whether we are the cpu thread.
|
||||
|
||||
bool IsGPUThread();
|
||||
|
||||
void SetState(EState _State);
|
||||
EState GetState();
|
||||
|
||||
@@ -87,6 +89,12 @@ bool ShouldSkipFrame(int skipped);
|
||||
void VideoThrottle();
|
||||
void RequestRefreshInfo();
|
||||
|
||||
// waits until all systems are paused and fully idle, and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally unpauses.
|
||||
// calls must be balanced (once with doLock true, then once with doLock false) but may be recursive.
|
||||
// the return value of the first call should be passed in as the second argument of the second call.
|
||||
bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
#ifdef RERECORDING
|
||||
|
||||
void FrameUpdate();
|
||||
|
||||
@@ -56,7 +56,7 @@ SCoreStartupParameter::SCoreStartupParameter()
|
||||
bAutoHideCursor(false), bUsePanicHandlers(true),
|
||||
iRenderWindowXPos(-1), iRenderWindowYPos(-1),
|
||||
iRenderWindowWidth(640), iRenderWindowHeight(480),
|
||||
bRenderWindowAutoSize(false),
|
||||
bRenderWindowAutoSize(false), bKeepWindowOnTop(false),
|
||||
bFullscreen(false), bRenderToMain(false),
|
||||
bProgressive(false), bDisableScreenSaver(false),
|
||||
iTheme(0),
|
||||
@@ -126,6 +126,7 @@ bool SCoreStartupParameter::AutoSetup(EBootBS2 _BootBS2)
|
||||
SplitPath(m_strFilename, NULL, NULL, &Extension);
|
||||
if (!strcasecmp(Extension.c_str(), ".gcm") ||
|
||||
!strcasecmp(Extension.c_str(), ".iso") ||
|
||||
!strcasecmp(Extension.c_str(), ".wbfs") ||
|
||||
!strcasecmp(Extension.c_str(), ".ciso") ||
|
||||
!strcasecmp(Extension.c_str(), ".gcz") ||
|
||||
bootDrive)
|
||||
|
||||
@@ -132,7 +132,7 @@ struct SCoreStartupParameter
|
||||
std::string strFullscreenResolution;
|
||||
int iRenderWindowXPos, iRenderWindowYPos;
|
||||
int iRenderWindowWidth, iRenderWindowHeight;
|
||||
bool bRenderWindowAutoSize;
|
||||
bool bRenderWindowAutoSize, bKeepWindowOnTop;
|
||||
bool bFullscreen, bRenderToMain;
|
||||
bool bProgressive, bDisableScreenSaver;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ struct BaseEvent
|
||||
|
||||
typedef LinkedListItem<BaseEvent> Event;
|
||||
|
||||
// STATE_TO_SAVE (how?)
|
||||
// STATE_TO_SAVE
|
||||
Event *first;
|
||||
Event *tsFirst;
|
||||
Event *tsLast;
|
||||
@@ -68,6 +68,8 @@ u64 fakeDecStartTicks;
|
||||
u64 fakeTBStartValue;
|
||||
u64 fakeTBStartTicks;
|
||||
|
||||
int ev_lost;
|
||||
|
||||
static std::recursive_mutex externalEventSection;
|
||||
|
||||
void (*advanceCallback)(int cyclesExecuted) = NULL;
|
||||
@@ -107,11 +109,28 @@ void FreeTsEvent(Event* ev)
|
||||
allocatedTsEvents--;
|
||||
}
|
||||
|
||||
static void EmptyTimedCallback(u64 userdata, int cyclesLate) {}
|
||||
|
||||
int RegisterEvent(const char *name, TimedCallback callback)
|
||||
{
|
||||
EventType type;
|
||||
type.name = name;
|
||||
type.callback = callback;
|
||||
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
for (int i = 0; i < event_types.size(); ++i)
|
||||
{
|
||||
if (!strcmp(name, event_types[i].name))
|
||||
{
|
||||
WARN_LOG(POWERPC, "Discarded old event type \"%s\" because a new type with the same name was registered.", name);
|
||||
// we don't know if someone might be holding on to the type index,
|
||||
// so we gut the old event type instead of actually removing it.
|
||||
event_types[i].name = "_discarded_event";
|
||||
event_types[i].callback = &EmptyTimedCallback;
|
||||
}
|
||||
}
|
||||
|
||||
event_types.push_back(type);
|
||||
return (int)event_types.size() - 1;
|
||||
}
|
||||
@@ -129,6 +148,8 @@ void Init()
|
||||
slicelength = maxSliceLength;
|
||||
globalTimer = 0;
|
||||
idledCycles = 0;
|
||||
|
||||
ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
@@ -153,6 +174,37 @@ void Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
void EventDoState(PointerWrap &p, BaseEvent* ev)
|
||||
{
|
||||
p.Do(ev->time);
|
||||
|
||||
// this is why we can't have (nice things) pointers as userdata
|
||||
p.Do(ev->userdata);
|
||||
|
||||
// we can't savestate ev->type directly because events might not get registered in the same order (or at all) every time.
|
||||
// so, we savestate the event's type's name, and derive ev->type from that when loading.
|
||||
std::string name = event_types[ev->type].name;
|
||||
p.Do(name);
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
bool foundMatch = false;
|
||||
for (int i = 0; i < event_types.size(); ++i)
|
||||
{
|
||||
if (!strcmp(name.c_str(), event_types[i].name))
|
||||
{
|
||||
ev->type = i;
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundMatch)
|
||||
{
|
||||
WARN_LOG(POWERPC, "Lost event from savestate because its type, \"%s\", has not been registered.", name.c_str());
|
||||
ev->type = ev_lost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(externalEventSection);
|
||||
@@ -165,53 +217,12 @@ void DoState(PointerWrap &p)
|
||||
p.Do(fakeTBStartValue);
|
||||
p.Do(fakeTBStartTicks);
|
||||
p.DoMarker("CoreTimingData");
|
||||
// OK, here we're gonna need to specialize depending on the mode.
|
||||
// Should do something generic to serialize linked lists.
|
||||
switch (p.GetMode()) {
|
||||
case PointerWrap::MODE_READ:
|
||||
{
|
||||
ClearPendingEvents();
|
||||
if (first)
|
||||
PanicAlertT("Clear failed.");
|
||||
int more_events = 0;
|
||||
Event *prev = 0;
|
||||
while (true) {
|
||||
p.Do(more_events);
|
||||
if (!more_events)
|
||||
break;
|
||||
Event *ev = GetNewEvent();
|
||||
if (!prev)
|
||||
first = ev;
|
||||
else
|
||||
prev->next = ev;
|
||||
p.Do(ev->time);
|
||||
p.Do(ev->type);
|
||||
p.Do(ev->userdata);
|
||||
ev->next = 0;
|
||||
prev = ev;
|
||||
ev = ev->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PointerWrap::MODE_MEASURE:
|
||||
case PointerWrap::MODE_VERIFY:
|
||||
case PointerWrap::MODE_WRITE:
|
||||
{
|
||||
Event *ev = first;
|
||||
int more_events = 1;
|
||||
while (ev) {
|
||||
p.Do(more_events);
|
||||
p.Do(ev->time);
|
||||
p.Do(ev->type);
|
||||
p.Do(ev->userdata);
|
||||
ev = ev->next;
|
||||
}
|
||||
more_events = 0;
|
||||
p.Do(more_events);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, EventDoState>(first);
|
||||
p.DoMarker("CoreTimingEvents");
|
||||
|
||||
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, EventDoState>(tsFirst, &tsLast);
|
||||
p.DoMarker("CoreTimingTsEvents");
|
||||
}
|
||||
|
||||
u64 GetTicks()
|
||||
@@ -468,8 +479,8 @@ void Advance()
|
||||
{
|
||||
if (first->time <= globalTimer)
|
||||
{
|
||||
// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ",
|
||||
// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
|
||||
// LOG(POWERPC, "[Scheduler] %s (%lld, %lld) ",
|
||||
// event_types[first->type].name ? event_types[first->type].name : "?", (u64)globalTimer, (u64)first->time);
|
||||
Event* evt = first;
|
||||
first = first->next;
|
||||
event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
|
||||
|
||||
@@ -50,7 +50,7 @@ u64 GetIdleTicks();
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
// Returns the event_type identifier.
|
||||
// Returns the event_type identifier. if name is not unique, an existing event_type will be discarded.
|
||||
int RegisterEvent(const char *name, TimedCallback callback);
|
||||
void UnregisterAllEvents();
|
||||
|
||||
|
||||
@@ -168,7 +168,8 @@ u16 dsp_read_accelerator()
|
||||
if (Address >= EndAddress)
|
||||
{
|
||||
// Set address back to start address.
|
||||
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
|
||||
if ((Address & ~0x1f) == (EndAddress & ~0x1f))
|
||||
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
|
||||
DSPCore_SetException(EXP_ACCOV);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) = 0;
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short) = 0;
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short) = 0;
|
||||
|
||||
@@ -35,7 +35,7 @@ void PPCDebugInterface::disasm(unsigned int address, char *dest, int max_size)
|
||||
|
||||
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||
{
|
||||
if (Memory::IsRAMAddress(address, true))
|
||||
if (Memory::IsRAMAddress(address, true, true))
|
||||
{
|
||||
u32 op = Memory::Read_Instruction(address);
|
||||
DisassembleGekko(op, address, dest, max_size);
|
||||
@@ -60,7 +60,7 @@ void PPCDebugInterface::getRawMemoryString(int memory, unsigned int address, cha
|
||||
{
|
||||
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||
{
|
||||
if (memory || Memory::IsRAMAddress(address, true))
|
||||
if (memory || Memory::IsRAMAddress(address, true, true))
|
||||
{
|
||||
snprintf(dest, max_size, "%08X%s", readExtraMemory(memory, address), memory ? " (ARAM)" : "");
|
||||
}
|
||||
@@ -175,7 +175,7 @@ void PPCDebugInterface::breakNow()
|
||||
// -------------
|
||||
int PPCDebugInterface::getColor(unsigned int address)
|
||||
{
|
||||
if (!Memory::IsRAMAddress(address, true))
|
||||
if (!Memory::IsRAMAddress(address, true, true))
|
||||
return 0xeeeeee;
|
||||
static const int colors[6] =
|
||||
{
|
||||
|
||||
@@ -153,17 +153,17 @@ void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory &cpMe
|
||||
const VAT &vtxAttr = cpMem.vtxAttr[vatIndex];
|
||||
|
||||
// Colors
|
||||
const int colDesc[2] = {vtxDesc.Color0, vtxDesc.Color1};
|
||||
const int colComp[2] = {vtxAttr.g0.Color0Comp, vtxAttr.g0.Color1Comp};
|
||||
const u32 colDesc[2] = {vtxDesc.Color0, vtxDesc.Color1};
|
||||
const u32 colComp[2] = {vtxAttr.g0.Color0Comp, vtxAttr.g0.Color1Comp};
|
||||
|
||||
const int tcElements[8] =
|
||||
const u32 tcElements[8] =
|
||||
{
|
||||
vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements, vtxAttr.g1.Tex2CoordElements,
|
||||
vtxAttr.g1.Tex3CoordElements, vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
|
||||
vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements
|
||||
};
|
||||
|
||||
const int tcFormat[8] =
|
||||
const u32 tcFormat[8] =
|
||||
{
|
||||
vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat, vtxAttr.g1.Tex2CoordFormat,
|
||||
vtxAttr.g1.Tex3CoordFormat, vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include "HW/GPFifo.h"
|
||||
#include "HW/Memmap.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "PowerPC/PowerPC.h"
|
||||
|
||||
#include "BPMemory.h"
|
||||
#include "CommandProcessor.h"
|
||||
|
||||
FifoPlayer::~FifoPlayer()
|
||||
{
|
||||
@@ -94,7 +96,40 @@ bool FifoPlayer::Play()
|
||||
if (m_EarlyMemoryUpdates && m_CurrentFrame == m_FrameRangeStart)
|
||||
WriteAllMemoryUpdates();
|
||||
|
||||
WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]);
|
||||
// Stop Fifo processing until we've written the new frame
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 0x10); // disable read & breakpoints, enable GP link
|
||||
|
||||
// Write frame data
|
||||
WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]);
|
||||
|
||||
// Enable frame processing and break when done
|
||||
u16 write_ptr_lo = ReadCP(CommandProcessor::FIFO_WRITE_POINTER_LO);
|
||||
u16 write_ptr_hi = ReadCP(CommandProcessor::FIFO_WRITE_POINTER_HI);
|
||||
WriteCP(CommandProcessor::FIFO_BP_LO, write_ptr_lo);
|
||||
WriteCP(CommandProcessor::FIFO_BP_HI, write_ptr_hi);
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 0x13); // enable read, breakpoints & GP link
|
||||
|
||||
// If necessary, wait until GP has reached the breakpoint to prevent fifo overflows
|
||||
// TODO: Can this be done any better? Dual core mode is slower than single core mode even with these conditions..
|
||||
if (m_CurrentFrame < m_FrameRangeEnd)
|
||||
{
|
||||
// Check if FIFO would be overflown when writing the next frame
|
||||
u32 CPRWDistance = (ReadCP(CommandProcessor::FIFO_RW_DISTANCE_HI)<<16) | ReadCP(CommandProcessor::FIFO_RW_DISTANCE_LO);
|
||||
CPRWDistance += m_File->GetFrame(m_CurrentFrame+1).fifoDataSize + CommandProcessor::GATHER_PIPE_SIZE;
|
||||
u32 CPFifoBase = (ReadCP(CommandProcessor::FIFO_BASE_HI)<<16) | ReadCP(CommandProcessor::FIFO_BASE_LO);
|
||||
u32 CPFifoEnd = (ReadCP(CommandProcessor::FIFO_END_HI)<<16) | ReadCP(CommandProcessor::FIFO_END_LO);
|
||||
|
||||
bool bWait = (CPRWDistance > CPFifoEnd - CPFifoBase);
|
||||
while (bWait && (ReadCP(CommandProcessor::FIFO_READ_POINTER_LO) != write_ptr_lo ||
|
||||
ReadCP(CommandProcessor::FIFO_READ_POINTER_HI) != write_ptr_hi))
|
||||
{
|
||||
Common::YieldCPU();
|
||||
CoreTiming::Advance(); // Process scheduled events (esp. PixelEngine::SetFinish!)
|
||||
|
||||
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++m_CurrentFrame;
|
||||
}
|
||||
@@ -387,6 +422,11 @@ void FifoPlayer::LoadMemory()
|
||||
FlushWGP();
|
||||
}
|
||||
|
||||
u16 FifoPlayer::ReadCP(u32 address)
|
||||
{
|
||||
return Memory::Read_U16(0xCC000000 | address);
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteCP(u32 address, u16 value)
|
||||
{
|
||||
Memory::Write_U16(value, 0xCC000000 | address);
|
||||
|
||||
@@ -87,6 +87,7 @@ private:
|
||||
|
||||
void LoadMemory();
|
||||
|
||||
u16 ReadCP(u32 address);
|
||||
void WriteCP(u32 address, u16 value);
|
||||
void WritePI(u32 address, u32 value);
|
||||
|
||||
|
||||
@@ -234,8 +234,8 @@ void Write32(const u32 _Value, const u32 _Address)
|
||||
m_Control.PSTAT = tmpAICtrl.PSTAT;
|
||||
g_LastCPUTime = CoreTiming::GetTicks();
|
||||
|
||||
// Tell Drive Interface to stop streaming
|
||||
if (!tmpAICtrl.PSTAT) DVDInterface::g_bStream = false;
|
||||
// Tell Drive Interface to start/stop streaming
|
||||
DVDInterface::g_bStream = tmpAICtrl.PSTAT;
|
||||
}
|
||||
|
||||
// AI Interrupt
|
||||
@@ -293,6 +293,11 @@ static void GenerateAudioInterrupt()
|
||||
UpdateInterrupts();
|
||||
}
|
||||
|
||||
void GenerateAISInterrupt()
|
||||
{
|
||||
GenerateAudioInterrupt();
|
||||
}
|
||||
|
||||
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate)
|
||||
{
|
||||
_AISampleRate = g_AISSampleRate;
|
||||
|
||||
@@ -43,6 +43,8 @@ void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
// Get the audio rates (48000 or 32000 only)
|
||||
unsigned int GetAIDSampleRate();
|
||||
|
||||
void GenerateAISInterrupt();
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,33 +15,38 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../Memmap.h"
|
||||
#include "../EXI_Device.h"
|
||||
#include "../EXI_DeviceEthernet.h"
|
||||
bool CEXIETHERNET::deactivate() {
|
||||
return true;
|
||||
}
|
||||
bool CEXIETHERNET::isActivated() {
|
||||
|
||||
bool CEXIETHERNET::Activate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::activate() {
|
||||
|
||||
void CEXIETHERNET::Deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::IsActivated()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::CheckRecieved() {
|
||||
|
||||
bool CEXIETHERNET::SendFrame(u8 *, u32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::resume() {
|
||||
|
||||
bool CEXIETHERNET::RecvInit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::startRecv() {
|
||||
|
||||
bool CEXIETHERNET::RecvStart()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size) {
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::handleRecvdPacket() {
|
||||
return false;
|
||||
}
|
||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size) {
|
||||
return false;
|
||||
|
||||
void CEXIETHERNET::RecvStop()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -15,305 +15,171 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "../Memmap.h"
|
||||
#include "StringUtil.h"
|
||||
#include "../EXI_Device.h"
|
||||
#include "../EXI_DeviceEthernet.h"
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/if_tun.h>
|
||||
#else
|
||||
#include <net/if_tun.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
int fd = -1;
|
||||
std::thread cpuThread;
|
||||
|
||||
bool CEXIETHERNET::deactivate()
|
||||
{
|
||||
close(fd);
|
||||
fd = -1;
|
||||
cpuThread.join();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::isActivated()
|
||||
{
|
||||
return fd != -1 ? true : false;
|
||||
}
|
||||
#define NOTIMPLEMENTED(Name) \
|
||||
NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name);
|
||||
|
||||
bool CEXIETHERNET::activate()
|
||||
bool CEXIETHERNET::Activate()
|
||||
{
|
||||
#ifdef __linux__
|
||||
if(isActivated())
|
||||
if (IsActivated())
|
||||
return true;
|
||||
if( (fd = open("/dev/net/tun", O_RDWR)) < 0)
|
||||
|
||||
// Assumes that there is a TAP device named "Dolphin" preconfigured for
|
||||
// bridge/NAT/whatever the user wants it configured.
|
||||
|
||||
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
|
||||
{
|
||||
INFO_LOG(SP1, "Couldn't Open device\n");
|
||||
ERROR_LOG(SP1, "Couldn't open /dev/net/tun, unable to init BBA");
|
||||
return false;
|
||||
}
|
||||
struct ifreq ifr;
|
||||
|
||||
int err;
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
|
||||
|
||||
|
||||
strncpy(ifr.ifr_name, "Dolphin", IFNAMSIZ);
|
||||
|
||||
if( (err = ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0)
|
||||
|
||||
int err;
|
||||
if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0)
|
||||
{
|
||||
close(fd);
|
||||
fd = -1;
|
||||
INFO_LOG(SP1, " Error with IOCTL: 0x%X\n", err);
|
||||
ERROR_LOG(SP1, "TUNSETIFF failed: err=%d", err);
|
||||
return false;
|
||||
}
|
||||
ioctl( fd, TUNSETNOCSUM, 1 );
|
||||
/*int flags;
|
||||
if ((flags = fcntl( fd, F_GETFL)) < 0)
|
||||
{
|
||||
INFO_LOG(SP1, "getflags on tun device: %s", strerror (errno));
|
||||
}
|
||||
flags |= O_NONBLOCK;
|
||||
if (fcntl( fd, F_SETFL, flags ) < 0)
|
||||
{
|
||||
INFO_LOG(SP1, "set tun device flags: %s", strerror (errno));
|
||||
}*/
|
||||
ioctl(fd, TUNSETNOCSUM, 1);
|
||||
|
||||
INFO_LOG(SP1, "Returned Socket name is: %s\n", ifr.ifr_name);
|
||||
system("brctl addif pan0 Dolphin");
|
||||
system("ifconfig Dolphin 0.0.0.0 promisc up");
|
||||
resume();
|
||||
readEnabled = false;
|
||||
|
||||
INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
|
||||
return true;
|
||||
#else
|
||||
NOTIMPLEMENTED("Activate");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CEXIETHERNET::Deactivate()
|
||||
{
|
||||
#ifdef __linux__
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
readEnabled = false;
|
||||
if (readThread.joinable())
|
||||
readThread.join();
|
||||
#else
|
||||
NOTIMPLEMENTED("Deactivate");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::IsActivated()
|
||||
{
|
||||
#ifdef __linux__
|
||||
return fd != -1 ? true : false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::CheckRecieved()
|
||||
bool CEXIETHERNET::SendFrame(u8* frame, u32 size)
|
||||
{
|
||||
if(!isActivated())
|
||||
#ifdef __linux__
|
||||
INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
|
||||
|
||||
int writtenBytes = write(fd, frame, size);
|
||||
if ((u32)writtenBytes != size)
|
||||
{
|
||||
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d",
|
||||
size, writtenBytes);
|
||||
return false;
|
||||
int maxfd;
|
||||
int retval;
|
||||
struct timeval tv;
|
||||
int timeout = 9999; // 3 seconds will kill him
|
||||
fd_set mask;
|
||||
|
||||
/* Find the largest file descriptor */
|
||||
maxfd = fd;
|
||||
|
||||
/* Check the file descriptors for available data */
|
||||
errno = 0;
|
||||
|
||||
/* Set up the mask of file descriptors */
|
||||
FD_ZERO(&mask);
|
||||
|
||||
FD_SET(fd, &mask);
|
||||
|
||||
/* Set up the timeout */
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout%1000)*1000;
|
||||
|
||||
/* Look! */
|
||||
retval = select(maxfd+1, &mask, NULL, NULL, &tv);
|
||||
|
||||
/* Mark all file descriptors ready that have data available */
|
||||
if ( retval > 0 ) {
|
||||
if ( FD_ISSET(fd, &mask) )
|
||||
{
|
||||
INFO_LOG(SP1, "\t\t\t\tWe have data!\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SendComplete();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
NOTIMPLEMENTED("SendFrame");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::resume()
|
||||
void ReadThreadHandler(CEXIETHERNET* self)
|
||||
{
|
||||
if(!isActivated())
|
||||
return true;
|
||||
INFO_LOG(SP1, "BBA resume\n");
|
||||
if(mBbaMem[BBA_NCRA] & NCRA_SR) {
|
||||
startRecv();
|
||||
}
|
||||
INFO_LOG(SP1, "BBA resume complete\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CpuThread(CEXIETHERNET *self)
|
||||
{
|
||||
while(1)
|
||||
while (true)
|
||||
{
|
||||
if(self->CheckRecieved())
|
||||
{
|
||||
u8 B[1514];
|
||||
self->mRecvBufferLength = read(fd, B, 1500);
|
||||
//INFO_LOG(SP1, "read return of 0x%x\n", self->mRecvBufferLength);
|
||||
if (self->mRecvBufferLength == 0xffffffff)
|
||||
{
|
||||
//Fail Boat
|
||||
continue;
|
||||
}
|
||||
else if(self->mRecvBufferLength > 0)
|
||||
{
|
||||
//mRecvBuffer.write(B, BytesRead);
|
||||
//strncat(mRecvBuffer.p(), B, BytesRead);
|
||||
memcpy(self->mRecvBuffer, B, self->mRecvBufferLength);
|
||||
}
|
||||
else if(self->mRecvBufferLength == -1U)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(SP1, "Unknown read return of 0x%x\n", self->mRecvBufferLength);
|
||||
exit(0);
|
||||
}
|
||||
INFO_LOG(SP1, "Received %d bytes of data\n", self->mRecvBufferLength);
|
||||
self->mWaiting = false;
|
||||
self->handleRecvdPacket();
|
||||
if (self->fd < 0)
|
||||
return;
|
||||
}
|
||||
//sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::startRecv()
|
||||
{
|
||||
INFO_LOG(SP1, "Start Receive!\n");
|
||||
//exit(0);
|
||||
INFO_LOG(SP1, "startRecv... ");
|
||||
if(mWaiting) {
|
||||
INFO_LOG(SP1, "already waiting\n");
|
||||
return true;
|
||||
}
|
||||
cpuThread = std::thread(CpuThread, this);
|
||||
mWaiting = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(self->fd, &rfds);
|
||||
|
||||
bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size)
|
||||
{
|
||||
if(!isActivated())
|
||||
return false;
|
||||
INFO_LOG(SP1, "Packet: 0x");
|
||||
for(int a = 0; a < size; ++a)
|
||||
{
|
||||
INFO_LOG(SP1, "%02X ", etherpckt[a]);
|
||||
}
|
||||
INFO_LOG(SP1, " : Size: %d\n", size);
|
||||
int numBytesWrit = write(fd, etherpckt, size);
|
||||
if(numBytesWrit != size)
|
||||
{
|
||||
INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!errno: %d\n", size, numBytesWrit, errno);
|
||||
return false;
|
||||
}
|
||||
recordSendComplete();
|
||||
return true;
|
||||
}
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 50000;
|
||||
if (select(self->fd + 1, &rfds, NULL, NULL, &timeout) <= 0)
|
||||
continue;
|
||||
|
||||
bool CEXIETHERNET::handleRecvdPacket()
|
||||
{
|
||||
int rbwpp = mCbw.p_write() + CB_OFFSET; //read buffer write page pointer
|
||||
u32 available_bytes_in_cb;
|
||||
if(rbwpp < mRBRPP)
|
||||
available_bytes_in_cb = mRBRPP - rbwpp;
|
||||
else if(rbwpp == mRBRPP)
|
||||
available_bytes_in_cb = mRBEmpty ? CB_SIZE : 0;
|
||||
else //rbwpp > mRBRPP
|
||||
available_bytes_in_cb = CB_SIZE - rbwpp + (mRBRPP - CB_OFFSET);
|
||||
|
||||
//DUMPWORD(rbwpp);
|
||||
//DUMPWORD(mRBRPP);
|
||||
//DUMPWORD(available_bytes_in_cb);
|
||||
|
||||
assert(available_bytes_in_cb <= CB_SIZE);
|
||||
if(available_bytes_in_cb != CB_SIZE)//< mRecvBufferLength + SIZEOF_RECV_DESCRIPTOR)
|
||||
return true;
|
||||
cbwriteDescriptor(mRecvBufferLength);
|
||||
mCbw.write(mRecvBuffer, mRecvBufferLength);
|
||||
mCbw.align();
|
||||
rbwpp = mCbw.p_write() + CB_OFFSET;
|
||||
//DUMPWORD(rbwpp);
|
||||
|
||||
//mPacketsRcvd++;
|
||||
mRecvBufferLength = 0;
|
||||
|
||||
if(mBbaMem[BBA_IMR] & INT_R)
|
||||
{
|
||||
if(!(mBbaMem[BBA_IR] & INT_R))
|
||||
int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE);
|
||||
if (readBytes < 0)
|
||||
{
|
||||
mBbaMem[BBA_IR] |= INT_R;
|
||||
INFO_LOG(SP1, "BBA Recv interrupt raised\n");
|
||||
m_bInterruptSet = true;
|
||||
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
|
||||
}
|
||||
else if (self->readEnabled)
|
||||
{
|
||||
INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str());
|
||||
self->mRecvBufferLength = readBytes;
|
||||
self->RecvHandlePacket();
|
||||
}
|
||||
}
|
||||
|
||||
if(mBbaMem[BBA_NCRA] & NCRA_SR)
|
||||
{
|
||||
startRecv();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
union bba_descr {
|
||||
struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
|
||||
u32 word;
|
||||
};
|
||||
|
||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
||||
bool CEXIETHERNET::RecvInit()
|
||||
{
|
||||
if(size < SIZEOF_ETH_HEADER)
|
||||
{
|
||||
INFO_LOG(SP1, "Packet too small: %i bytes\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
size += SIZEOF_RECV_DESCRIPTOR; //The descriptor supposed to include the size of itself
|
||||
|
||||
//We should probably not implement wraparound here,
|
||||
//since neither tmbinc, riptool.dol, or libogc does...
|
||||
if(mCbw.p_write() + SIZEOF_RECV_DESCRIPTOR >= CB_SIZE)
|
||||
{
|
||||
INFO_LOG(SP1, "The descriptor won't fit\n");
|
||||
return false;
|
||||
}
|
||||
if(size >= CB_SIZE)
|
||||
{
|
||||
INFO_LOG(SP1, "Packet too big: %i bytes\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
bba_descr descr;
|
||||
descr.word = 0;
|
||||
descr.packet_len = size;
|
||||
descr.status = 0;
|
||||
u32 npp;
|
||||
if(mCbw.p_write() + size < CB_SIZE)
|
||||
{
|
||||
npp = mCbw.p_write() + size + CB_OFFSET;
|
||||
}
|
||||
else
|
||||
{
|
||||
npp = mCbw.p_write() + size + CB_OFFSET - CB_SIZE;
|
||||
}
|
||||
npp = (npp + 0xff) & ~0xff;
|
||||
if(npp >= CB_SIZE + CB_OFFSET)
|
||||
npp -= CB_SIZE;
|
||||
descr.next_packet_ptr = npp >> 8;
|
||||
//DWORD swapped = swapw(descr.word);
|
||||
//next_packet_ptr:12, packet_len:12, status:8;
|
||||
INFO_LOG(SP1, "Writing descriptor 0x%08X @ 0x%04lX: next 0x%03X len 0x%03X status 0x%02X\n",
|
||||
descr.word, (unsigned long)mCbw.p_write() + CB_OFFSET, descr.next_packet_ptr,
|
||||
descr.packet_len, descr.status);
|
||||
mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
|
||||
|
||||
#ifdef __linux__
|
||||
readThread = std::thread(ReadThreadHandler, this);
|
||||
return true;
|
||||
#else
|
||||
NOTIMPLEMENTED("RecvInit");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::RecvStart()
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (!readThread.joinable())
|
||||
RecvInit();
|
||||
|
||||
readEnabled = true;
|
||||
return true;
|
||||
#else
|
||||
NOTIMPLEMENTED("RecvStart");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CEXIETHERNET::RecvStop()
|
||||
{
|
||||
#ifdef __linux__
|
||||
readEnabled = false;
|
||||
#else
|
||||
NOTIMPLEMENTED("RecvStop");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "StringUtil.h"
|
||||
#include "../Memmap.h"
|
||||
//#pragma optimize("",off)
|
||||
// GROSS CODE ALERT: headers need to be included in the following order
|
||||
#include "TAP_Win32.h"
|
||||
#include "../EXI_Device.h"
|
||||
@@ -187,66 +186,45 @@ bool OpenTAP(HANDLE& adapter, const std::string device_guid)
|
||||
|
||||
} // namespace Win32TAPHelper
|
||||
|
||||
bool CEXIETHERNET::deactivate()
|
||||
bool CEXIETHERNET::Activate()
|
||||
{
|
||||
INFO_LOG(SP1, "Deactivating BBA...");
|
||||
if (!isActivated())
|
||||
if (IsActivated())
|
||||
return true;
|
||||
CloseHandle(mHRecvEvent);
|
||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
||||
CloseHandle(mHAdapter);
|
||||
mHAdapter = INVALID_HANDLE_VALUE;
|
||||
INFO_LOG(SP1, "Success!");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::isActivated()
|
||||
{
|
||||
return mHAdapter != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::activate()
|
||||
{
|
||||
if (isActivated())
|
||||
return true;
|
||||
|
||||
INFO_LOG(SP1, "Activating BBA...");
|
||||
|
||||
DWORD len;
|
||||
std::vector<std::string> device_guids;
|
||||
|
||||
if (!Win32TAPHelper::GetGUIDs(device_guids))
|
||||
{
|
||||
INFO_LOG(SP1, "Failed to find a TAP GUID");
|
||||
ERROR_LOG(SP1, "Failed to find a TAP GUID");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < device_guids.size(); i++)
|
||||
for (size_t i = 0; i < device_guids.size(); i++)
|
||||
{
|
||||
if (Win32TAPHelper::OpenTAP(mHAdapter, device_guids.at(i)))
|
||||
{
|
||||
ERROR_LOG(SP1, "OPENED %s", device_guids.at(i).c_str());
|
||||
INFO_LOG(SP1, "OPENED %s", device_guids.at(i).c_str());
|
||||
i = device_guids.size();
|
||||
}
|
||||
}
|
||||
if (mHAdapter == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
INFO_LOG(SP1, "Failed to open any TAP");
|
||||
ERROR_LOG(SP1, "Failed to open any TAP");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get driver version info */
|
||||
ULONG info[3];
|
||||
if (DeviceIoControl (mHAdapter, TAP_IOCTL_GET_VERSION,
|
||||
&info, sizeof (info), &info, sizeof (info), &len, NULL))
|
||||
if (DeviceIoControl(mHAdapter, TAP_IOCTL_GET_VERSION,
|
||||
&info, sizeof(info), &info, sizeof(info), &len, NULL))
|
||||
{
|
||||
INFO_LOG(SP1, "TAP-Win32 Driver Version %d.%d %s",
|
||||
info[0], info[1], (info[2] ? "(DEBUG)" : ""));
|
||||
info[0], info[1], info[2] ? "(DEBUG)" : "");
|
||||
}
|
||||
if ( !(info[0] > TAP_WIN32_MIN_MAJOR
|
||||
|| (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)) )
|
||||
if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)))
|
||||
{
|
||||
PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver"
|
||||
PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver"
|
||||
" that is at least version %d.%d -- If you recently upgraded your Dolphin"
|
||||
" distribution, a reboot is probably required at this point to get"
|
||||
" Windows to see the new driver.",
|
||||
@@ -254,279 +232,124 @@ bool CEXIETHERNET::activate()
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get driver MTU */
|
||||
if (!DeviceIoControl(mHAdapter, TAP_IOCTL_GET_MTU,
|
||||
&mMtu, sizeof (mMtu), &mMtu, sizeof (mMtu), &len, NULL))
|
||||
{
|
||||
INFO_LOG(SP1, "Couldn't get device MTU");
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(SP1, "TAP-Win32 MTU=%d (ignored)", mMtu);
|
||||
|
||||
/* set driver media status to 'connected' */
|
||||
ULONG status = TRUE;
|
||||
if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS,
|
||||
&status, sizeof (status), &status, sizeof (status), &len, NULL))
|
||||
&status, sizeof(status), &status, sizeof(status), &len, NULL))
|
||||
{
|
||||
INFO_LOG(SP1, "WARNING: The TAP-Win32 driver rejected a"
|
||||
ERROR_LOG(SP1, "WARNING: The TAP-Win32 driver rejected a"
|
||||
"TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//set up recv event
|
||||
if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
|
||||
{
|
||||
INFO_LOG(SP1, "Failed to create recv event:%x", GetLastError());
|
||||
return false;
|
||||
}
|
||||
ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
|
||||
resume();
|
||||
|
||||
INFO_LOG(SP1, "Success!");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::CheckRecieved()
|
||||
void CEXIETHERNET::Deactivate()
|
||||
{
|
||||
if (!isActivated())
|
||||
return false;
|
||||
if (!IsActivated())
|
||||
return;
|
||||
|
||||
return false;
|
||||
RecvStop();
|
||||
|
||||
CloseHandle(mHAdapter);
|
||||
mHAdapter = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// Required for lwip...not sure why
|
||||
void fixup_ip_checksum(u16 *dataptr, u16 len)
|
||||
{
|
||||
u32 acc = 0;
|
||||
u16 *data = dataptr;
|
||||
u16 *chksum = &dataptr[5];
|
||||
|
||||
*chksum = 0;
|
||||
|
||||
while (len > 1) {
|
||||
u16 s = *data++;
|
||||
acc += Common::swap16(s);
|
||||
len -= 2;
|
||||
}
|
||||
acc = (acc >> 16) + (acc & 0xffff);
|
||||
acc += (acc >> 16);
|
||||
*chksum = Common::swap16(~acc);
|
||||
bool CEXIETHERNET::IsActivated()
|
||||
{
|
||||
return mHAdapter != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size)
|
||||
bool CEXIETHERNET::SendFrame(u8 *frame, u32 size)
|
||||
{
|
||||
fixup_ip_checksum((u16*)etherpckt + 7, 20);
|
||||
INFO_LOG(SP1, "Packet (%zu):\n%s", size, ArrayToString(etherpckt, size, 0, 16).c_str());
|
||||
DEBUG_LOG(SP1, "SendFrame %x\n%s",
|
||||
size, ArrayToString(frame, size, 0x10).c_str());
|
||||
|
||||
DWORD numBytesWrit;
|
||||
OVERLAPPED overlap;
|
||||
ZeroMemory(&overlap, sizeof(overlap));
|
||||
//overlap.hEvent = mHRecvEvent;
|
||||
if (!WriteFile(mHAdapter, etherpckt, size, &numBytesWrit, &overlap))
|
||||
|
||||
if (!WriteFile(mHAdapter, frame, size, &numBytesWrit, &overlap))
|
||||
{
|
||||
// Fail Boat
|
||||
DWORD res = GetLastError();
|
||||
INFO_LOG(SP1, "Failed to send packet with error 0x%X", res);
|
||||
ERROR_LOG(SP1, "Failed to send packet with error 0x%X", res);
|
||||
}
|
||||
|
||||
if (numBytesWrit != size)
|
||||
{
|
||||
INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!", size, numBytesWrit);
|
||||
return false;
|
||||
}
|
||||
recordSendComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::handleRecvdPacket()
|
||||
{
|
||||
static u32 mPacketsRecvd = 0;
|
||||
INFO_LOG(SP1, "handleRecvdPacket Packet %i (%i):\n%s", mPacketsRecvd, mRecvBufferLength,
|
||||
ArrayToString(mRecvBuffer, mRecvBufferLength, 0, 16).c_str());
|
||||
|
||||
static u8 broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
if (memcmp(mRecvBuffer, broadcast, 6) && memcmp(mRecvBuffer, mac_address, 6))
|
||||
{
|
||||
INFO_LOG(SP1, "dropping packet - not for us");
|
||||
goto wait_for_next;
|
||||
ERROR_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit);
|
||||
}
|
||||
|
||||
int rbwpp = (int)(mCbw.p_write() + CB_OFFSET); // read buffer write page pointer
|
||||
u32 available_bytes_in_cb;
|
||||
if (rbwpp < mRBRPP)
|
||||
available_bytes_in_cb = mRBRPP - rbwpp;
|
||||
else if (rbwpp == mRBRPP)
|
||||
available_bytes_in_cb = mRBEmpty ? CB_SIZE : 0;
|
||||
else //rbwpp > mRBRPP
|
||||
available_bytes_in_cb = CB_SIZE - rbwpp + (mRBRPP - CB_OFFSET);
|
||||
|
||||
INFO_LOG(SP1, "rbwpp %i mRBRPP %04x available_bytes_in_cb %i",
|
||||
rbwpp, mRBRPP, available_bytes_in_cb);
|
||||
|
||||
_dbg_assert_(SP1, available_bytes_in_cb <= CB_SIZE);
|
||||
if (available_bytes_in_cb != CB_SIZE)//< mRecvBufferLength + SIZEOF_RECV_DESCRIPTOR)
|
||||
return true;
|
||||
cbwriteDescriptor(mRecvBufferLength);
|
||||
mCbw.write(mRecvBuffer, mRecvBufferLength);
|
||||
mCbw.align();
|
||||
rbwpp = (int)(mCbw.p_write() + CB_OFFSET);
|
||||
// Always report the packet as being sent successfully, even though it might be a lie
|
||||
SendComplete();
|
||||
|
||||
INFO_LOG(SP1, "rbwpp %i", rbwpp);
|
||||
|
||||
mPacketsRecvd++;
|
||||
mRecvBufferLength = 0;
|
||||
|
||||
if ((mBbaMem[BBA_IMR] & INT_R) && !(mBbaMem[BBA_IR] & INT_R))
|
||||
{
|
||||
if (mBbaMem[2])
|
||||
{
|
||||
mBbaMem[BBA_IR] |= INT_R;
|
||||
INFO_LOG(SP1, "BBA Recv interrupt raised");
|
||||
m_bInterruptSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_next:
|
||||
if (mBbaMem[BBA_NCRA] & NCRA_SR)
|
||||
startRecv();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::resume()
|
||||
{
|
||||
if (!isActivated())
|
||||
return true;
|
||||
|
||||
INFO_LOG(SP1, "BBA resume");
|
||||
//mStop = false;
|
||||
|
||||
RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback,
|
||||
this, INFINITE, WT_EXECUTEDEFAULT);//WT_EXECUTEINWAITTHREAD
|
||||
|
||||
mReadOverlapped.hEvent = mHRecvEvent;
|
||||
|
||||
if (mBbaMem[BBA_NCRA] & NCRA_SR)
|
||||
startRecv();
|
||||
|
||||
INFO_LOG(SP1, "BBA resume complete");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::startRecv()
|
||||
{
|
||||
if (!isActivated())
|
||||
return false;// Should actually be an assert
|
||||
|
||||
INFO_LOG(SP1, "startRecv... ");
|
||||
|
||||
if (mWaiting)
|
||||
{
|
||||
INFO_LOG(SP1, "already waiting");
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE,
|
||||
&mRecvBufferLength, &mReadOverlapped);
|
||||
|
||||
if (res)
|
||||
{
|
||||
// Operation completed immediately
|
||||
INFO_LOG(SP1, "completed, res %i", res);
|
||||
mWaiting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = GetLastError();
|
||||
if (res == ERROR_IO_PENDING)
|
||||
{
|
||||
//'s ok :)
|
||||
INFO_LOG(SP1, "pending");
|
||||
// WaitCallback will be called
|
||||
mWaiting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error occurred
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired)
|
||||
{
|
||||
static int sNumber = 0;
|
||||
int cNumber = sNumber++;
|
||||
INFO_LOG(SP1, "WaitCallback %i", cNumber);
|
||||
__assume(!TimerFired);
|
||||
CEXIETHERNET* self = (CEXIETHERNET*)lpParameter;
|
||||
__assume(self->mHAdapter != INVALID_HANDLE_VALUE);
|
||||
|
||||
GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped,
|
||||
&self->mRecvBufferLength, false);
|
||||
self->mWaiting = false;
|
||||
self->handleRecvdPacket();
|
||||
INFO_LOG(SP1, "WaitCallback %i done", cNumber);
|
||||
(LPDWORD)&self->mRecvBufferLength, false);
|
||||
|
||||
self->RecvHandlePacket();
|
||||
}
|
||||
|
||||
union bba_descr
|
||||
bool CEXIETHERNET::RecvInit()
|
||||
{
|
||||
struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
|
||||
u32 word;
|
||||
};
|
||||
// Set up recv event
|
||||
|
||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
||||
{
|
||||
if (size < SIZEOF_ETH_HEADER)
|
||||
if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
|
||||
{
|
||||
INFO_LOG(SP1, "Packet too small: %i bytes", size);
|
||||
ERROR_LOG(SP1, "Failed to create recv event:%x", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// The descriptor supposed to include the size of itself
|
||||
size += SIZEOF_RECV_DESCRIPTOR;
|
||||
ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
|
||||
|
||||
//We should probably not implement wraparound here,
|
||||
//since neither tmbinc, riptool.dol, or libogc does...
|
||||
if (mCbw.p_write() + SIZEOF_RECV_DESCRIPTOR >= CB_SIZE)
|
||||
{
|
||||
INFO_LOG(SP1, "The descriptor won't fit");
|
||||
return false;
|
||||
}
|
||||
if (size >= CB_SIZE)
|
||||
{
|
||||
INFO_LOG(SP1, "Packet too big: %i bytes", size);
|
||||
return false;
|
||||
}
|
||||
RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback,
|
||||
this, INFINITE, WT_EXECUTEDEFAULT);
|
||||
|
||||
bba_descr descr;
|
||||
descr.word = 0;
|
||||
descr.packet_len = size;
|
||||
descr.status = 0;
|
||||
u32 npp;
|
||||
if (mCbw.p_write() + size < CB_SIZE)
|
||||
{
|
||||
npp = (u32)(mCbw.p_write() + size + CB_OFFSET);
|
||||
}
|
||||
else
|
||||
{
|
||||
npp = (u32)(mCbw.p_write() + size + CB_OFFSET - CB_SIZE);
|
||||
}
|
||||
|
||||
npp = (npp + 0xff) & ~0xff;
|
||||
|
||||
if (npp >= CB_SIZE + CB_OFFSET)
|
||||
npp -= CB_SIZE;
|
||||
|
||||
descr.next_packet_ptr = npp >> 8;
|
||||
//DWORD swapped = swapw(descr.word);
|
||||
//next_packet_ptr:12, packet_len:12, status:8;
|
||||
INFO_LOG(SP1, "Writing descriptor 0x%08X @ 0x%04X: next 0x%03X len 0x%03X status 0x%02X",
|
||||
descr.word, mCbw.p_write() + CB_OFFSET, descr.next_packet_ptr,
|
||||
descr.packet_len, descr.status);
|
||||
|
||||
mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
|
||||
mReadOverlapped.hEvent = mHRecvEvent;
|
||||
|
||||
return true;
|
||||
}
|
||||
//#pragma optimize("",on)
|
||||
|
||||
bool CEXIETHERNET::RecvStart()
|
||||
{
|
||||
if (!IsActivated())
|
||||
return false;
|
||||
|
||||
if (mHRecvEvent == INVALID_HANDLE_VALUE)
|
||||
RecvInit();
|
||||
|
||||
DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE,
|
||||
(LPDWORD)&mRecvBufferLength, &mReadOverlapped);
|
||||
|
||||
if (!res && (GetLastError() != ERROR_IO_PENDING))
|
||||
{
|
||||
// error occurred
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
// Completed immediately
|
||||
RecvHandlePacket();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::RecvStop()
|
||||
{
|
||||
if (!IsActivated())
|
||||
return;
|
||||
|
||||
UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE);
|
||||
|
||||
CloseHandle(mHRecvEvent);
|
||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "../Core.h"
|
||||
#include "CPU.h"
|
||||
#include "DSP.h"
|
||||
#include "Movie.h"
|
||||
|
||||
#include "VideoBackendBase.h"
|
||||
|
||||
@@ -31,10 +32,15 @@ namespace
|
||||
{
|
||||
static Common::Event m_StepEvent;
|
||||
static Common::Event *m_SyncEvent;
|
||||
static std::mutex m_csCpuOccupied;
|
||||
}
|
||||
|
||||
void CCPU::Init(int cpu_core)
|
||||
{
|
||||
if (Movie::IsPlayingInput() && Movie::IsConfigSaved())
|
||||
{
|
||||
cpu_core = Movie::GetCPUMode();
|
||||
}
|
||||
PowerPC::Init(cpu_core);
|
||||
m_SyncEvent = 0;
|
||||
}
|
||||
@@ -47,6 +53,7 @@ void CCPU::Shutdown()
|
||||
|
||||
void CCPU::Run()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_csCpuOccupied);
|
||||
Host_UpdateDisasmDialog();
|
||||
|
||||
while (true)
|
||||
@@ -60,8 +67,12 @@ reswitch:
|
||||
break;
|
||||
|
||||
case PowerPC::CPU_STEPPING:
|
||||
m_StepEvent.Wait();
|
||||
m_csCpuOccupied.unlock();
|
||||
|
||||
//1: wait for step command..
|
||||
m_StepEvent.Wait();
|
||||
|
||||
m_csCpuOccupied.lock();
|
||||
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN)
|
||||
return;
|
||||
if (PowerPC::GetState() != PowerPC::CPU_STEPPING)
|
||||
@@ -132,3 +143,26 @@ void CCPU::Break()
|
||||
{
|
||||
EnableStepping(true);
|
||||
}
|
||||
|
||||
bool CCPU::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
bool wasUnpaused = !IsStepping();
|
||||
if (doLock)
|
||||
{
|
||||
// we can't use EnableStepping, that would causes deadlocks with both audio and video
|
||||
PowerPC::Pause();
|
||||
if (!Core::IsCPUThread())
|
||||
m_csCpuOccupied.lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unpauseOnUnlock)
|
||||
{
|
||||
PowerPC::Start();
|
||||
m_StepEvent.Set();
|
||||
}
|
||||
if (!Core::IsCPUThread())
|
||||
m_csCpuOccupied.unlock();
|
||||
}
|
||||
return wasUnpaused;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ public:
|
||||
|
||||
// is stepping ?
|
||||
static bool IsStepping();
|
||||
|
||||
// waits until is stepping and is ready for a command (paused and fully idle), and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally re-disables stepping.
|
||||
// calls must be balanced and non-recursive (once with doLock true, then once with doLock false).
|
||||
// intended (but not required) to be called from another thread,
|
||||
// e.g. when the GUI thread wants to make sure everything is paused so that it can create a savestate.
|
||||
// the return value is whether the cpu was unpaused before the call.
|
||||
static bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -229,6 +229,7 @@ void DoState(PointerWrap &p)
|
||||
p.Do(g_ARAM_Info);
|
||||
p.Do(g_AR_MODE);
|
||||
p.Do(g_AR_REFRESH);
|
||||
p.Do(dsp_slice);
|
||||
|
||||
dsp_emulator->DoState(p);
|
||||
}
|
||||
@@ -354,7 +355,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
|
||||
// AI
|
||||
case AUDIO_DMA_BLOCKS_LEFT:
|
||||
_uReturnValue = g_audioDMA.BlocksLeft - 1; // AUDIO_DMA_BLOCKS_LEFT is zero based
|
||||
_uReturnValue = g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0; // AUDIO_DMA_BLOCKS_LEFT is zero based
|
||||
break;
|
||||
|
||||
case AUDIO_DMA_START_LO:
|
||||
@@ -435,10 +436,11 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||
if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0;
|
||||
if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0;
|
||||
|
||||
// g_ARAM
|
||||
g_dspState.DSPControl.DMAState = 0; // keep g_ARAM DMA State zero
|
||||
// Tracking DMAState fixes Knockout Kings 2003 in DSP HLE mode
|
||||
if (GetDSPEmulator()->IsLLE())
|
||||
g_dspState.DSPControl.DMAState = 0; // keep g_ARAM DMA State zero
|
||||
|
||||
// unknown
|
||||
// unknown
|
||||
g_dspState.DSPControl.unk3 = tmpControl.unk3;
|
||||
g_dspState.DSPControl.pad = tmpControl.pad;
|
||||
if (g_dspState.DSPControl.pad != 0)
|
||||
@@ -453,13 +455,30 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||
// ARAM
|
||||
// DMA back and forth between ARAM and RAM
|
||||
case AR_INFO:
|
||||
//PanicAlert("write %x %x", _Value,PowerPC::ppcState.pc);
|
||||
//PanicAlert("AR_INFO %x PC: %x", _Value, PowerPC::ppcState.pc);
|
||||
ERROR_LOG(DSPINTERFACE, "AR_INFO %x PC: %x", _Value, PowerPC::ppcState.pc);
|
||||
g_ARAM_Info.Hex = _Value;
|
||||
|
||||
// 0x43
|
||||
// Monster Hunter Tri, DKCR
|
||||
|
||||
// 0x43, 0x63:
|
||||
// Rebel Strike, Clone Wars, WWE DOR2, Mario Golf
|
||||
|
||||
// 0x43, 0x64, 0x63
|
||||
// Transworld Surf, Smashing Drive, SSBM, Cel Damage
|
||||
|
||||
// __OSInitAudioSystem sets to 0x43 -> expects 16bit adressing and mapping to dsp iram?
|
||||
// __OSCheckSize sets = 0x20 | 3 (keeps upper bits)
|
||||
// 0x23 -> Zelda standard mode (standard ARAM access ??)
|
||||
// 0x43 -> Set by Eternal Darkness and SSBB
|
||||
// 0x43 -> Set by __OSInitAudioSystem
|
||||
// 0x58 -> Transworld Surf, Cel Damage, SSBM
|
||||
// 0x60 -> Transworld Surf, Cel Damage, SSBM
|
||||
// 0x63 -> ARCheckSize Mode (access AR-registers ??) or no exception ??
|
||||
// 0x64 -> Transworld Surf, Cel Damage, SSBM
|
||||
|
||||
// 0x00 -> Switch to external ARAM
|
||||
// 0x04 -> Switch to internal ARAM
|
||||
break;
|
||||
|
||||
case AR_MODE:
|
||||
@@ -620,7 +639,7 @@ void GenerateDSPInterrupt(DSPInterruptType type, bool _bSet)
|
||||
switch (type)
|
||||
{
|
||||
case INT_DSP: g_dspState.DSPControl.DSP = _bSet ? 1 : 0; break;
|
||||
case INT_ARAM: g_dspState.DSPControl.ARAM = _bSet ? 1 : 0; break;
|
||||
case INT_ARAM: g_dspState.DSPControl.ARAM = _bSet ? 1 : 0; if (_bSet) g_dspState.DSPControl.DMAState = 0; break;
|
||||
case INT_AID: g_dspState.DSPControl.AID = _bSet ? 1 : 0; break;
|
||||
}
|
||||
|
||||
@@ -672,54 +691,104 @@ void UpdateAudioDMA()
|
||||
{
|
||||
// Send silence. Yeah, it's a bit of a waste to sample rate convert
|
||||
// silence. or hm. Maybe we shouldn't do this :)
|
||||
// dsp->DSP_SendAIBuffer(0, AudioInterface::GetDSPSampleRate());
|
||||
//dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate());
|
||||
}
|
||||
}
|
||||
|
||||
void Do_ARAM_DMA()
|
||||
{
|
||||
// Fake the DMA taking time to complete. The delay is not accurate, but
|
||||
// seems like a good estimate
|
||||
CoreTiming::ScheduleEvent_Threadsafe(g_arDMA.Cnt.count >> 1, et_GenerateDSPInterrupt, INT_ARAM | (1<<16));
|
||||
// Emulating the DMA wait time fixes Knockout Kings 2003 in DSP HLE mode
|
||||
if (!GetDSPEmulator()->IsLLE())
|
||||
g_dspState.DSPControl.DMAState = 1;
|
||||
|
||||
GenerateDSPInterrupt(INT_ARAM, true);
|
||||
|
||||
// Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
|
||||
if (g_arDMA.Cnt.dir)
|
||||
{
|
||||
// ARAM -> MRAM
|
||||
INFO_LOG(DSPINTERFACE, "DMA %08x bytes from ARAM %08x to MRAM %08x",
|
||||
g_arDMA.Cnt.count, g_arDMA.ARAddr, g_arDMA.MMAddr);
|
||||
INFO_LOG(DSPINTERFACE, "DMA %08x bytes from ARAM %08x to MRAM %08x PC: %08x",
|
||||
g_arDMA.Cnt.count, g_arDMA.ARAddr, g_arDMA.MMAddr, PC);
|
||||
|
||||
while (g_arDMA.Cnt.count)
|
||||
// Outgoing data from ARAM is mirrored every 64MB (verified on real HW)
|
||||
g_arDMA.ARAddr &= 0x3ffffff;
|
||||
g_arDMA.MMAddr &= 0x3ffffff;
|
||||
|
||||
if (g_arDMA.ARAddr < g_ARAM.size)
|
||||
{
|
||||
if (g_arDMA.ARAddr < g_ARAM.size)
|
||||
while (g_arDMA.Cnt.count)
|
||||
{
|
||||
Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr], g_arDMA.MMAddr);
|
||||
if ((g_ARAM_Info.Hex & 0xf) == 3)
|
||||
{
|
||||
Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
|
||||
}
|
||||
else if ((g_ARAM_Info.Hex & 0xf) == 4)
|
||||
{
|
||||
Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
|
||||
}
|
||||
else
|
||||
Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
|
||||
g_arDMA.MMAddr += 8;
|
||||
g_arDMA.ARAddr += 8;
|
||||
g_arDMA.Cnt.count -= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assuming no external ARAM installed; returns zeroes on out of bounds reads (verified on real HW)
|
||||
while (g_arDMA.Cnt.count)
|
||||
{
|
||||
Memory::Write_U64(0, g_arDMA.MMAddr);
|
||||
g_arDMA.MMAddr += 8;
|
||||
g_arDMA.ARAddr += 8;
|
||||
g_arDMA.Cnt.count -= 8;
|
||||
}
|
||||
g_arDMA.Cnt.count -= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// MRAM -> ARAM
|
||||
INFO_LOG(DSPINTERFACE, "DMA %08x bytes from MRAM %08x to ARAM %08x",
|
||||
g_arDMA.Cnt.count, g_arDMA.MMAddr, g_arDMA.ARAddr);
|
||||
INFO_LOG(DSPINTERFACE, "DMA %08x bytes from MRAM %08x to ARAM %08x PC: %08x",
|
||||
g_arDMA.Cnt.count, g_arDMA.MMAddr, g_arDMA.ARAddr, PC);
|
||||
|
||||
while (g_arDMA.Cnt.count)
|
||||
// Incoming data into ARAM is mirrored every 64MB (verified on real HW)
|
||||
g_arDMA.ARAddr &= 0x3ffffff;
|
||||
g_arDMA.MMAddr &= 0x3ffffff;
|
||||
|
||||
if (g_arDMA.ARAddr < g_ARAM.size)
|
||||
{
|
||||
if (g_arDMA.ARAddr < g_ARAM.size)
|
||||
while (g_arDMA.Cnt.count)
|
||||
{
|
||||
*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
|
||||
if ((g_ARAM_Info.Hex & 0xf) == 3)
|
||||
{
|
||||
*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
|
||||
}
|
||||
else if ((g_ARAM_Info.Hex & 0xf) == 4)
|
||||
{
|
||||
if (g_arDMA.ARAddr < 0x400000)
|
||||
{
|
||||
*(u64*)&g_ARAM.ptr[(g_arDMA.ARAddr + 0x400000) & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
|
||||
}
|
||||
*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
|
||||
}
|
||||
else
|
||||
*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
|
||||
|
||||
g_arDMA.MMAddr += 8;
|
||||
g_arDMA.ARAddr += 8;
|
||||
g_arDMA.Cnt.count -= 8;
|
||||
}
|
||||
g_arDMA.Cnt.count -= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assuming no external ARAM installed; writes nothing to ARAM when out of bounds (verified on real HW)
|
||||
g_arDMA.MMAddr += g_arDMA.Cnt.count;
|
||||
g_arDMA.ARAddr += g_arDMA.Cnt.count;
|
||||
g_arDMA.Cnt.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// (shuffle2) I still don't believe that this hack is actually needed... :(
|
||||
// Maybe the wii sports ucode is processed incorrectly?
|
||||
// (LM) It just means that dsp reads via '0xffdd' on WII can end up in EXRAM or main RAM
|
||||
|
||||
@@ -130,7 +130,24 @@ void DSPHLE::SwapUCode(u32 _crc)
|
||||
|
||||
void DSPHLE::DoState(PointerWrap &p)
|
||||
{
|
||||
bool prevInitMixer = m_InitMixer;
|
||||
p.Do(m_InitMixer);
|
||||
if (prevInitMixer != m_InitMixer && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
if (m_InitMixer)
|
||||
{
|
||||
InitMixer();
|
||||
AudioCommon::PauseAndLock(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioCommon::PauseAndLock(false);
|
||||
soundStream->Stop();
|
||||
delete soundStream;
|
||||
soundStream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
p.Do(m_DSPControl);
|
||||
p.Do(m_dspState);
|
||||
|
||||
@@ -230,6 +247,17 @@ void DSPHLE::DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short _Value)
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE::InitMixer()
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
soundStream = AudioCommon::InitSoundStream(new HLEMixer(this, AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
}
|
||||
|
||||
// Other DSP fuctions
|
||||
u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
@@ -238,14 +266,7 @@ u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
|
||||
soundStream = AudioCommon::InitSoundStream(
|
||||
new HLEMixer(this, AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
InitMixer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +316,9 @@ void DSPHLE::DSP_ClearAudioBuffer(bool mute)
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
void DSPHLE::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (doLock || unpauseOnUnlock)
|
||||
DSP_ClearAudioBuffer(doLock);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
virtual bool IsLLE() { return false; }
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
@@ -55,6 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
void SendMailToDSP(u32 _uMail);
|
||||
void InitMixer();
|
||||
|
||||
// Declarations and definitions
|
||||
void *m_hWnd;
|
||||
|
||||
@@ -465,5 +465,6 @@ void CUCode_AX::DoState(PointerWrap &p)
|
||||
p.Do(numPBaddr);
|
||||
p.Do(m_addressPBs);
|
||||
p.Do(PBaddr);
|
||||
p.Do(m_UploadSetupInProgress);
|
||||
|
||||
DoStateShared(p);
|
||||
}
|
||||
|
||||
@@ -261,5 +261,6 @@ void CUCode_AXWii::DoState(PointerWrap &p)
|
||||
|
||||
p.Do(m_addressPBs);
|
||||
p.Do(wiisportsHack);
|
||||
p.Do(m_UploadSetupInProgress);
|
||||
|
||||
DoStateShared(p);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ private:
|
||||
|
||||
// PBs
|
||||
u32 m_addressPBs;
|
||||
u32 _CRC;
|
||||
|
||||
bool wiisportsHack;
|
||||
|
||||
|
||||
@@ -236,7 +236,8 @@ inline void MixAddVoice(ParamBlockType &pb,
|
||||
{
|
||||
if (pb.audio_addr.looping == 1)
|
||||
{
|
||||
samplePos = loopPos;
|
||||
if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM))
|
||||
samplePos = loopPos;
|
||||
if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM))
|
||||
{
|
||||
pb.adpcm.yn1 = pb.adpcm_loop_info.yn1;
|
||||
|
||||
@@ -122,5 +122,7 @@ void CUCode_Rom::DoState(PointerWrap &p)
|
||||
p.Do(m_CurrentUCode);
|
||||
p.Do(m_BootTask_numSteps);
|
||||
p.Do(m_NextParameter);
|
||||
|
||||
DoStateShared(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -612,5 +612,5 @@ void CUCode_Zelda::DoState(PointerWrap &p)
|
||||
p.Do(m_PBAddress);
|
||||
p.Do(m_PBAddress2);
|
||||
|
||||
p.Do(m_UploadSetupInProgress);
|
||||
DoStateShared(p);
|
||||
}
|
||||
|
||||
@@ -182,3 +182,11 @@ void IUCode::PrepareBootUCode(u32 mail)
|
||||
m_DSPHLE->SwapUCode(ector_crc);
|
||||
}
|
||||
}
|
||||
|
||||
void IUCode::DoStateShared(PointerWrap &p)
|
||||
{
|
||||
p.Do(m_UploadSetupInProgress);
|
||||
p.Do(m_NextUCode);
|
||||
p.Do(m_NextUCode_steps);
|
||||
p.Do(m_NeedsResumeMail);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
virtual void Update(int cycles) = 0;
|
||||
virtual void MixAdd(short* buffer, int size) {}
|
||||
|
||||
virtual void DoState(PointerWrap &p) {}
|
||||
virtual void DoState(PointerWrap &p) { DoStateShared(p); }
|
||||
|
||||
static u32 GetCRC(IUCode* pUCode) { return pUCode ? pUCode->m_CRC : UCODE_NULL; }
|
||||
|
||||
@@ -105,6 +105,8 @@ protected:
|
||||
// The HLE can use this to
|
||||
bool NeedsResumeMail();
|
||||
|
||||
void DoStateShared(PointerWrap &p);
|
||||
|
||||
CMailHandler& m_rMailHandler;
|
||||
std::mutex m_csMix;
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "Thread.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "IniFile.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#include "DSPLLEGlobals.h" // Local
|
||||
#include "DSP/DSPInterpreter.h"
|
||||
@@ -77,6 +79,24 @@ void DSPLLE::DoState(PointerWrap &p)
|
||||
p.DoArray(g_dsp.dram, DSP_DRAM_SIZE);
|
||||
p.Do(cyclesLeft);
|
||||
p.Do(m_cycle_count);
|
||||
|
||||
bool prevInitMixer = m_InitMixer;
|
||||
p.Do(m_InitMixer);
|
||||
if (prevInitMixer != m_InitMixer && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
if (m_InitMixer)
|
||||
{
|
||||
InitMixer();
|
||||
AudioCommon::PauseAndLock(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioCommon::PauseAndLock(false);
|
||||
soundStream->Stop();
|
||||
delete soundStream;
|
||||
soundStream = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular thread
|
||||
@@ -84,10 +104,33 @@ void DSPLLE::dsp_thread(DSPLLE *dsp_lle)
|
||||
{
|
||||
Common::SetCurrentThreadName("DSP thread");
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bLockThreads)
|
||||
{
|
||||
if (cpu_info.num_cores > 3)
|
||||
{
|
||||
// HACK (delroth): there is no way to know where hyperthreads are in
|
||||
// the current Dolphin version.
|
||||
bool windows = false;
|
||||
#ifdef _WIN32
|
||||
windows = true;
|
||||
#endif
|
||||
|
||||
u8 core_id;
|
||||
if (windows && cpu_info.num_cores > 4) // Probably HT
|
||||
core_id = 5; // 3rd non HT core
|
||||
else
|
||||
core_id = 3; // 3rd core
|
||||
|
||||
Common::SetCurrentThreadAffinity(1 << (core_id - 1));
|
||||
}
|
||||
}
|
||||
|
||||
while (dsp_lle->m_bIsRunning)
|
||||
{
|
||||
int cycles = (int)dsp_lle->m_cycle_count;
|
||||
if (cycles > 0) {
|
||||
if (cycles > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(dsp_lle->m_csDSPThreadActive);
|
||||
if (dspjit)
|
||||
{
|
||||
DSPCore_RunCycles(cycles);
|
||||
@@ -156,6 +199,17 @@ void DSPLLE::Shutdown()
|
||||
DSPCore_Shutdown();
|
||||
}
|
||||
|
||||
void DSPLLE::InitMixer()
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
||||
{
|
||||
UDSPControl Temp(_uFlag);
|
||||
@@ -163,12 +217,7 @@ u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
||||
{
|
||||
if (!Temp.DSPHalt)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
InitMixer();
|
||||
}
|
||||
}
|
||||
DSPInterpreter::WriteCR(_uFlag);
|
||||
@@ -311,3 +360,15 @@ void DSPLLE::DSP_ClearAudioBuffer(bool mute)
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
void DSPLLE::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (doLock || unpauseOnUnlock)
|
||||
DSP_ClearAudioBuffer(doLock);
|
||||
|
||||
if (doLock)
|
||||
m_csDSPThreadActive.lock();
|
||||
else
|
||||
m_csDSPThreadActive.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual bool IsLLE() { return true; }
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
@@ -46,8 +47,10 @@ public:
|
||||
|
||||
private:
|
||||
static void dsp_thread(DSPLLE* lpParameter);
|
||||
void InitMixer();
|
||||
|
||||
std::thread m_hDSPThread;
|
||||
std::mutex m_csDSPThreadActive;
|
||||
bool m_InitMixer;
|
||||
void *m_hWnd;
|
||||
bool m_bWii;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "Thread.h"
|
||||
#include "Memmap.h"
|
||||
#include "../VolumeHandler.h"
|
||||
#include "AudioInterface.h"
|
||||
#include "../Movie.h"
|
||||
|
||||
// Disc transfer rate measured in bytes per second
|
||||
static const u32 DISC_TRANSFER_RATE_GC = 3125 * 1024;
|
||||
@@ -201,9 +203,11 @@ static UDICR m_DICR;
|
||||
static UDIIMMBUF m_DIIMMBUF;
|
||||
static UDICFG m_DICFG;
|
||||
|
||||
static u32 AudioStart;
|
||||
static u32 LoopStart;
|
||||
static u32 AudioPos;
|
||||
static u32 AudioLength;
|
||||
static u32 CurrentStart;
|
||||
static u32 LoopLength;
|
||||
static u32 CurrentLength;
|
||||
|
||||
u32 g_ErrorCode = 0;
|
||||
bool g_bDiscInside = false;
|
||||
@@ -238,12 +242,16 @@ void DoState(PointerWrap &p)
|
||||
p.Do(m_DIIMMBUF);
|
||||
p.Do(m_DICFG);
|
||||
|
||||
p.Do(AudioStart);
|
||||
p.Do(LoopStart);
|
||||
p.Do(AudioPos);
|
||||
p.Do(AudioLength);
|
||||
p.Do(LoopLength);
|
||||
|
||||
p.Do(g_ErrorCode);
|
||||
p.Do(g_bDiscInside);
|
||||
p.Do(g_bStream);
|
||||
|
||||
p.Do(CurrentStart);
|
||||
p.Do(CurrentLength);
|
||||
}
|
||||
|
||||
void TransferComplete(u64 userdata, int cyclesLate)
|
||||
@@ -266,9 +274,13 @@ void Init()
|
||||
m_DICFG.Hex = 0;
|
||||
m_DICFG.CONFIG = 1; // Disable bootrom descrambler
|
||||
|
||||
AudioStart = 0;
|
||||
AudioPos = 0;
|
||||
AudioLength = 0;
|
||||
AudioPos = 0;
|
||||
LoopStart = 0;
|
||||
LoopLength = 0;
|
||||
CurrentStart = 0;
|
||||
CurrentLength = 0;
|
||||
|
||||
g_bStream = false;
|
||||
|
||||
ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
||||
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
||||
@@ -323,6 +335,17 @@ void ChangeDisc(const char* _newFileName)
|
||||
std::string* _FileName = new std::string(_newFileName);
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, ejectDisc);
|
||||
CoreTiming::ScheduleEvent_Threadsafe(500000000, insertDisc, (u64)_FileName);
|
||||
if (Movie::IsRecordingInput())
|
||||
{
|
||||
Movie::g_bDiscChange = true;
|
||||
std::string fileName = _newFileName;
|
||||
int sizeofpath = fileName.find_last_of("/\\") + 1;
|
||||
if (fileName.substr(sizeofpath).length() > 40)
|
||||
{
|
||||
PanicAlert("Saving iso filename to .dtm failed; max file name length is 40 characters.");
|
||||
}
|
||||
Movie::g_discChange = fileName.substr(sizeofpath);
|
||||
}
|
||||
}
|
||||
|
||||
void SetLidOpen(bool _bOpen)
|
||||
@@ -351,33 +374,46 @@ bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength)
|
||||
|
||||
bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples)
|
||||
{
|
||||
_iNumSamples &= ~31;
|
||||
|
||||
if (AudioPos == 0)
|
||||
{
|
||||
//MessageBox(0,"DVD: Trying to stream from 0", "bah", 0);
|
||||
memset(_pDestBuffer, 0, _iNumSamples); // probably __AI_SRC_INIT :P
|
||||
return false;
|
||||
}
|
||||
_iNumSamples &= ~31;
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(dvdread_section);
|
||||
VolumeHandler::ReadToPtr(_pDestBuffer, AudioPos, _iNumSamples);
|
||||
std::lock_guard<std::mutex> lk(dvdread_section);
|
||||
VolumeHandler::ReadToPtr(_pDestBuffer, AudioPos, _iNumSamples);
|
||||
}
|
||||
|
||||
//
|
||||
// FIX THIS
|
||||
//
|
||||
// loop check
|
||||
//
|
||||
AudioPos += _iNumSamples;
|
||||
if (AudioPos >= AudioStart + AudioLength)
|
||||
if (g_bStream)
|
||||
{
|
||||
g_bStream = false; // Starfox Adventures
|
||||
AudioPos = AudioStart;
|
||||
NGCADPCM::InitFilter();
|
||||
}
|
||||
AudioPos += _iNumSamples;
|
||||
|
||||
//WARN_LOG(DVDINTERFACE,"ReadADPCM");
|
||||
return true;
|
||||
if (AudioPos >= CurrentStart + CurrentLength)
|
||||
{
|
||||
if (LoopStart == 0)
|
||||
{
|
||||
AudioPos = 0;
|
||||
CurrentStart = 0;
|
||||
CurrentLength = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioPos = LoopStart;
|
||||
CurrentStart = LoopStart;
|
||||
CurrentLength = LoopLength;
|
||||
}
|
||||
NGCADPCM::InitFilter();
|
||||
AudioInterface::GenerateAISInterrupt();
|
||||
}
|
||||
|
||||
//WARN_LOG(DVDINTERFACE,"ReadADPCM");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
@@ -651,7 +687,7 @@ void ExecuteCommand(UDICR& _DICR)
|
||||
_dbg_assert_(DVDINTERFACE, m_DILENGTH.Length == 0x20);
|
||||
if (!DVDRead(m_DICMDBUF[1].Hex, m_DIMAR.Address, m_DILENGTH.Length))
|
||||
PanicAlertT("Cant read from DVD_Plugin - DVD-Interface: Fatal Error");
|
||||
WARN_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(m_DIMAR.Address))
|
||||
WARN_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(m_DIMAR.Address));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -813,28 +849,69 @@ void ExecuteCommand(UDICR& _DICR)
|
||||
// m_DICMDBUF[2].Hex = Length of the stream
|
||||
case 0xE1:
|
||||
{
|
||||
// ugly hack to catch the disable command
|
||||
if (m_DICMDBUF[1].Hex != 0)
|
||||
u32 pos = m_DICMDBUF[1].Hex << 2;
|
||||
u32 length = m_DICMDBUF[2].Hex;
|
||||
|
||||
// Start playing
|
||||
if (!g_bStream && m_DICMDBUF[0].CMDBYTE1 == 0 && pos != 0 && length != 0)
|
||||
{
|
||||
AudioPos = m_DICMDBUF[1].Hex << 2;
|
||||
AudioStart = AudioPos;
|
||||
AudioLength = m_DICMDBUF[2].Hex;
|
||||
NGCADPCM::InitFilter();
|
||||
|
||||
AudioPos = pos;
|
||||
CurrentStart = pos;
|
||||
CurrentLength = length;
|
||||
NGCADPCM::InitFilter();
|
||||
g_bStream = true;
|
||||
|
||||
WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %02x offset = %08x length=%08x",
|
||||
m_DICMDBUF[0].CMDBYTE1, AudioPos, AudioLength);
|
||||
}
|
||||
else
|
||||
WARN_LOG(DVDINTERFACE, "(Audio) Off?");
|
||||
|
||||
LoopStart = pos;
|
||||
LoopLength = length;
|
||||
g_bStream = (m_DICMDBUF[0].CMDBYTE1 == 0); // This command can start/stop the stream
|
||||
|
||||
// Stop stream
|
||||
if (m_DICMDBUF[0].CMDBYTE1 == 1)
|
||||
{
|
||||
AudioPos = 0;
|
||||
LoopStart = 0;
|
||||
LoopLength = 0;
|
||||
CurrentStart = 0;
|
||||
CurrentLength = 0;
|
||||
}
|
||||
|
||||
WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %08x offset = %08x length=%08x",
|
||||
m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex << 2, m_DICMDBUF[2].Hex);
|
||||
}
|
||||
break;
|
||||
|
||||
// Request Audio Status (Immediate)
|
||||
case 0xE2:
|
||||
m_DIIMMBUF.Hex = g_bStream ? 1 : 0;
|
||||
//WARN_LOG(DVDINTERFACE, "(Audio): Request Audio status %s", g_bStream? "on":"off");
|
||||
{
|
||||
switch (m_DICMDBUF[0].CMDBYTE1)
|
||||
{
|
||||
case 0x00: // Returns streaming status
|
||||
m_DIIMMBUF.Hex = (AudioPos == 0) ? 0 : 1;
|
||||
break;
|
||||
case 0x01: // Returns the current offset
|
||||
if (g_bStream)
|
||||
m_DIIMMBUF.Hex = (AudioPos - CurrentStart) >> 2;
|
||||
else
|
||||
m_DIIMMBUF.Hex = 0;
|
||||
break;
|
||||
case 0x02: // Returns the start offset
|
||||
if (g_bStream)
|
||||
m_DIIMMBUF.Hex = CurrentStart >> 2;
|
||||
else
|
||||
m_DIIMMBUF.Hex = 0;
|
||||
break;
|
||||
case 0x03: // Returns the total length
|
||||
if (g_bStream)
|
||||
m_DIIMMBUF.Hex = CurrentLength;
|
||||
else
|
||||
m_DIIMMBUF.Hex = 0;
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", m_DICMDBUF[0].CMDBYTE1, g_bStream? "on":"off");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DVDLowStopMotor:
|
||||
|
||||
@@ -68,12 +68,13 @@ void DoState(PointerWrap &p)
|
||||
g_Channels[c]->DoState(p);
|
||||
}
|
||||
|
||||
void OnAfterLoad()
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
for (int c = 0; c < NUM_CHANNELS; ++c)
|
||||
g_Channels[c]->OnAfterLoad();
|
||||
g_Channels[c]->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
}
|
||||
|
||||
|
||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
u8 channel = (u8)(userdata >> 32);
|
||||
@@ -91,6 +92,17 @@ void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 devi
|
||||
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | ((u64)device_type << 16) | device_num);
|
||||
}
|
||||
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex)
|
||||
{
|
||||
for (int i = 0; i < NUM_CHANNELS; ++i)
|
||||
{
|
||||
IEXIDevice* device = g_Channels[i]->FindDevice(device_type, customIndex);
|
||||
if (device)
|
||||
return device;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Unused (?!)
|
||||
void Update()
|
||||
{
|
||||
|
||||
@@ -28,13 +28,14 @@ namespace ExpansionInterface
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
|
||||
|
||||
void Update();
|
||||
void UpdateInterrupts();
|
||||
|
||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate);
|
||||
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
|
||||
@@ -309,9 +309,19 @@ void CEXIChannel::DoState(PointerWrap &p)
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIChannel::OnAfterLoad()
|
||||
void CEXIChannel::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
for (int d = 0; d < NUM_DEVICES; ++d)
|
||||
m_pDevices[d]->OnAfterLoad();
|
||||
m_pDevices[d]->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
}
|
||||
|
||||
IEXIDevice* CEXIChannel::FindDevice(TEXIDevices device_type, int customIndex)
|
||||
{
|
||||
for (int d = 0; d < NUM_DEVICES; ++d)
|
||||
{
|
||||
IEXIDevice* device = m_pDevices[d]->FindDevice(device_type, customIndex);
|
||||
if (device)
|
||||
return device;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ private:
|
||||
public:
|
||||
// get device
|
||||
IEXIDevice* GetDevice(const u8 _CHIP_SELECT);
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
|
||||
|
||||
CEXIChannel(u32 ChannelId);
|
||||
~CEXIChannel();
|
||||
@@ -130,7 +131,7 @@ public:
|
||||
bool IsCausingInterrupt();
|
||||
void UpdateInterrupts();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
|
||||
|
||||
// This should only be used to transition interrupts from SP1 to Channel 2
|
||||
void SetEXIINT(bool exiint) { m_Status.EXIINT = !!exiint; }
|
||||
|
||||
@@ -129,7 +129,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
||||
break;
|
||||
|
||||
case EXIDEVICE_ETH:
|
||||
result = new CEXIETHERNET(SConfig::GetInstance().m_bba_mac);
|
||||
result = new CEXIETHERNET();
|
||||
break;
|
||||
|
||||
case EXIDEVICE_AM_BASEBOARD:
|
||||
|
||||
@@ -53,7 +53,8 @@ public:
|
||||
virtual bool IsPresent() {return false;}
|
||||
virtual void SetCS(int) {}
|
||||
virtual void DoState(PointerWrap&) {}
|
||||
virtual void OnAfterLoad() {}
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) {}
|
||||
virtual IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1) { return (device_type == m_deviceType) ? this : NULL; }
|
||||
|
||||
// Update
|
||||
virtual void Update() {}
|
||||
|
||||
@@ -16,87 +16,130 @@
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Memmap.h"
|
||||
//#pragma optimize("",off)
|
||||
#include "EXI_Device.h"
|
||||
#include "EXI_DeviceEthernet.h"
|
||||
#include "StringUtil.h"
|
||||
#include "../ConfigManager.h"
|
||||
|
||||
#define MAKE(type, arg) (*(type *)&(arg))
|
||||
// XXX: The BBA stores multi-byte elements as little endian.
|
||||
// Multiple parts of this implementation depend on dolphin
|
||||
// being compiled for a little endian host.
|
||||
|
||||
CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) :
|
||||
m_uPosition(0),
|
||||
m_uCommand(0),
|
||||
mWriteBuffer(2048),
|
||||
mCbw(mBbaMem + CB_OFFSET, CB_SIZE)
|
||||
|
||||
// TODO move this code into Common or something, for IOS to use
|
||||
|
||||
enum MACConsumer
|
||||
{
|
||||
const u8 mac_address_default[6] =
|
||||
{ 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 };
|
||||
BBA,
|
||||
IOS
|
||||
};
|
||||
|
||||
memset(mBbaMem, 0, BBA_MEM_SIZE);
|
||||
|
||||
int x = 0;
|
||||
u8 new_addr[6] = { 0 };
|
||||
for (int i = 0; i < (int)mac_addr.size() && x < 12; i++)
|
||||
void GenerateMAC(MACConsumer type, u8 (&mac)[6])
|
||||
{
|
||||
memset(mac, 0, 6);
|
||||
|
||||
u8 const oui_bba[] = { 0x00, 0x09, 0xbf };
|
||||
u8 const oui_ios[] = { 0x00, 0x17, 0xab };
|
||||
|
||||
switch (type)
|
||||
{
|
||||
char c = mac_addr.at(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
new_addr[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); x++;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
new_addr[x / 2] |= (c - 'A' + 10) << ((x & 1) ? 0 : 4); x++;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
new_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
|
||||
case BBA:
|
||||
memcpy(mac, oui_bba, 3);
|
||||
break;
|
||||
case IOS:
|
||||
memcpy(mac, oui_ios, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
srand((unsigned int)time(nullptr));
|
||||
|
||||
u8 id[3] =
|
||||
{
|
||||
(u8)rand(),
|
||||
(u8)rand(),
|
||||
(u8)rand()
|
||||
};
|
||||
|
||||
memcpy(&mac[3], id, 3);
|
||||
}
|
||||
|
||||
CEXIETHERNET::CEXIETHERNET()
|
||||
{
|
||||
tx_fifo = new u8[1518];
|
||||
mBbaMem = new u8[BBA_MEM_SIZE];
|
||||
|
||||
mRecvBuffer = new u8 [BBA_RECV_SIZE];
|
||||
mRecvBufferLength = 0;
|
||||
|
||||
MXHardReset();
|
||||
|
||||
// Parse MAC address from config, and generate a new one if it doesn't
|
||||
// exist or can't be parsed.
|
||||
|
||||
auto &mac_addr_setting = SConfig::GetInstance().m_bba_mac;
|
||||
bool mac_addr_valid = false;
|
||||
u8 mac_addr[6] = { 0 };
|
||||
|
||||
if (!mac_addr_setting.empty())
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
for (size_t i = 0; i < mac_addr_setting.size() && x < 12; i++)
|
||||
{
|
||||
char c = mac_addr_setting.at(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
mac_addr[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); x++;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
mac_addr[x / 2] |= (c - 'A' + 10) << ((x & 1) ? 0 : 4); x++;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
mac_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
|
||||
}
|
||||
}
|
||||
|
||||
if (x / 2 == 6)
|
||||
{
|
||||
memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_addr, 6);
|
||||
mac_addr_valid = true;
|
||||
}
|
||||
}
|
||||
if (x / 2 == 6)
|
||||
memcpy(mac_address, new_addr, 6);
|
||||
else
|
||||
memcpy(mac_address, mac_address_default, 6);
|
||||
|
||||
ERROR_LOG(SP1, "BBA MAC %x%x%x%x%x%x",
|
||||
mac_address[0], mac_address[1], mac_address[2],
|
||||
mac_address[3], mac_address[4], mac_address[5]);
|
||||
if (!mac_addr_valid)
|
||||
{
|
||||
GenerateMAC(BBA, mac_addr);
|
||||
|
||||
mWriteP = INVALID_P;
|
||||
mReadP = INVALID_P;
|
||||
mWaiting = false;
|
||||
mReadyToSend = false;
|
||||
Activated = false;
|
||||
m_bInterruptSet = false;
|
||||
mac_addr_setting = ArrayToString(mac_addr, 6, 10, false);
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
|
||||
#ifdef _WIN32
|
||||
memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_addr, 6);
|
||||
}
|
||||
|
||||
// hax .. fully established 100BASE-T link
|
||||
mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
|
||||
|
||||
#if defined(_WIN32)
|
||||
mHAdapter = INVALID_HANDLE_VALUE;
|
||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
||||
mHReadWait = INVALID_HANDLE_VALUE;
|
||||
#elif defined(__linux__)
|
||||
fd = -1;
|
||||
#endif
|
||||
|
||||
mRecvBufferLength = 0;
|
||||
|
||||
mExpectSpecialImmRead = false;
|
||||
mExpectVariableLengthImmWrite = false;
|
||||
}
|
||||
|
||||
CEXIETHERNET::~CEXIETHERNET()
|
||||
{
|
||||
/*/ crashy crashy
|
||||
if (isActivated())
|
||||
deactivate();
|
||||
//*/
|
||||
Deactivate();
|
||||
|
||||
delete tx_fifo;
|
||||
delete mBbaMem;
|
||||
delete mRecvBuffer;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::SetCS(int cs)
|
||||
{
|
||||
INFO_LOG(SP1, "chip select: %s%s", cs ? "true" : "false",
|
||||
mExpectVariableLengthImmWrite ? ", expecting variable write" : "");
|
||||
if (!cs)
|
||||
if (cs)
|
||||
{
|
||||
if (mExpectVariableLengthImmWrite)
|
||||
{
|
||||
INFO_LOG(SP1, "Variable write complete. Final size: %i bytes",
|
||||
mWriteBuffer.size());
|
||||
mExpectVariableLengthImmWrite = false;
|
||||
mReadyToSend = true;
|
||||
}
|
||||
mExpectSpecialImmRead = false;
|
||||
mWriteP = mReadP = INVALID_P;
|
||||
// Invalidate the previous transfer
|
||||
transfer.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,336 +148,515 @@ bool CEXIETHERNET::IsPresent()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::Update()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::IsInterruptSet()
|
||||
{
|
||||
return m_bInterruptSet;
|
||||
return !!(exi_status.interrupt & exi_status.interrupt_mask);
|
||||
}
|
||||
|
||||
void CEXIETHERNET::recordSendComplete()
|
||||
void CEXIETHERNET::ImmWrite(u32 data, u32 size)
|
||||
{
|
||||
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
|
||||
if (mBbaMem[BBA_IMR] & INT_T)
|
||||
data >>= (4 - size) * 8;
|
||||
|
||||
if (!transfer.valid)
|
||||
{
|
||||
mBbaMem[BBA_IR] |= INT_T;
|
||||
INFO_LOG(SP1, "\t\tBBA Send interrupt raised");
|
||||
m_bInterruptSet = true;
|
||||
}
|
||||
// TODO why did sonic put this here?
|
||||
//startRecv();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::checkRecvBuffer()
|
||||
{
|
||||
if (mRecvBufferLength)
|
||||
handleRecvdPacket();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::ImmWrite(u32 data, u32 size)
|
||||
{
|
||||
if (size == 1)
|
||||
data = (u8)Common::swap32(data);
|
||||
else if (size == 2)
|
||||
data >>= 16;
|
||||
|
||||
INFO_LOG(SP1, "imm write %0*x writep 0x%x", size * 2, data, mWriteP);
|
||||
|
||||
if (mExpectVariableLengthImmWrite)
|
||||
{
|
||||
INFO_LOG(SP1, "\tvariable length imm write");
|
||||
if (size == 4)
|
||||
data = Common::swap32(data);
|
||||
else if (size == 2)
|
||||
data = Common::swap16((u16)data);
|
||||
mWriteBuffer.write(size, &data);
|
||||
return;
|
||||
}
|
||||
else if (mWriteP != INVALID_P)
|
||||
{
|
||||
if (mWriteP + size > BBA_MEM_SIZE)
|
||||
{
|
||||
ERROR_LOG(SP1, "write error: writep + size = %04x + %i", mWriteP, size);
|
||||
return;
|
||||
}
|
||||
INFO_LOG(SP1, "\twrite to BBA address %0*x, %i byte%s: %0*x",
|
||||
mWriteP >= CB_OFFSET ? 4 : 2, mWriteP, size, (size==1?"":"s"), size*2, data);
|
||||
|
||||
switch (mWriteP)
|
||||
{
|
||||
case BBA_NCRA:
|
||||
{
|
||||
INFO_LOG(SP1, "\t\tNCRA");
|
||||
|
||||
// TODO is it really necessary to check last value?
|
||||
u8 NCRA_old = mBbaMem[BBA_NCRA];
|
||||
mBbaMem[BBA_NCRA] = data;
|
||||
#define RISE(flags) ((mBbaMem[BBA_NCRA] & flags) && !(NCRA_old & flags))
|
||||
|
||||
if (RISE(NCRA_RESET))
|
||||
{
|
||||
INFO_LOG(SP1, "\t\treset");
|
||||
activate();
|
||||
}
|
||||
if (RISE(NCRA_SR))
|
||||
{
|
||||
INFO_LOG(SP1, "\t\tstart receive");
|
||||
startRecv();
|
||||
}
|
||||
if (RISE(NCRA_ST1))
|
||||
{
|
||||
INFO_LOG(SP1, "\t\tstart transmit");
|
||||
if (!mReadyToSend)
|
||||
{
|
||||
INFO_LOG(SP1, "\t\ttramsit without a packet!");
|
||||
}
|
||||
sendPacket(mWriteBuffer.p(), mWriteBuffer.size());
|
||||
mReadyToSend = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
mBbaMem[0x03] = ~data;
|
||||
if (data & 0x80)
|
||||
m_bInterruptSet = false;
|
||||
break;
|
||||
case BBA_IR:
|
||||
_dbg_assert_(SP1, size == 1);
|
||||
INFO_LOG(SP1, "\t\tinterrupt reset %02x & ~(%02x) => %02x",
|
||||
mBbaMem[BBA_IR], MAKE(u8, data), mBbaMem[BBA_IR] & ~MAKE(u8, data));
|
||||
mBbaMem[BBA_IR] &= ~MAKE(u8, data);
|
||||
break;
|
||||
case BBA_RWP: // RWP - Receive Buffer Write Page Pointer
|
||||
INFO_LOG(SP1, "\t\tRWP");
|
||||
_dbg_assert_(SP1, size == 2 || size == 1);
|
||||
//_dbg_assert_(SP1, data == (u32)((u16)mCbw.p_write() + CB_OFFSET) >> 8);
|
||||
if (data != (u32)((u16)mCbw.p_write() + CB_OFFSET) >> 8)
|
||||
{
|
||||
ERROR_LOG(SP1, "BBA RWP ASSERT data %x p_write %lx",
|
||||
data, (unsigned long)mCbw.p_write());
|
||||
}
|
||||
break;
|
||||
case BBA_RRP: // RRP - Receive Buffer Read Page Pointer
|
||||
INFO_LOG(SP1, "\t\tRRP");
|
||||
_dbg_assert_(SP1, size == 2 || size == 1);
|
||||
mRBRPP = (u8)data << 8; // Hope this works with both write sizes.
|
||||
mRBEmpty = mRBRPP == ((u16)mCbw.p_write() + CB_OFFSET);
|
||||
checkRecvBuffer();
|
||||
break;
|
||||
case BBA_NWAYC:
|
||||
INFO_LOG(SP1, "\t\tNWAYC");
|
||||
mBbaMem[BBA_NWAYC] = data;
|
||||
if (mBbaMem[BBA_NWAYC] & (NWAYC_ANE | NWAYC_ANS_RA))
|
||||
{
|
||||
// say we've successfully negotiated for 10 Mbit full duplex
|
||||
// should placate libogc
|
||||
mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_ANCLPT | NWAYS_100TXF;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
INFO_LOG(SP1, "\t\tdefault: data: %0*x to %x", size * 2, data, mWriteP);
|
||||
memcpy(mBbaMem + mWriteP, &data, size);
|
||||
mWriteP = mWriteP + (u16)size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (size == 2 && data == 0)
|
||||
{
|
||||
INFO_LOG(SP1, "\trequest cid");
|
||||
mSpecialImmData = EXI_DEVTYPE_ETHER;
|
||||
mExpectSpecialImmRead = true;
|
||||
return;
|
||||
}
|
||||
else if ((size == 4 && (data & 0xC0000000) == 0xC0000000) ||
|
||||
(size == 2 && (data & 0x4000) == 0x4000))
|
||||
{
|
||||
INFO_LOG(SP1, "\twrite to register");
|
||||
if (size == 4)
|
||||
mWriteP = (u8)getbitsw(data, 16, 23);
|
||||
else //size == 2
|
||||
mWriteP = (u8)getbitsw(data & ~0x4000, 16, 23); // Dunno about this...
|
||||
|
||||
if (mWriteP == BBA_WRTXFIFOD)
|
||||
{
|
||||
mWriteBuffer.clear();
|
||||
mExpectVariableLengthImmWrite = true;
|
||||
INFO_LOG(SP1, "\t\tprepared for variable length write to address 0x48");
|
||||
}
|
||||
transfer.valid = true;
|
||||
transfer.region = IsMXCommand(data) ? transfer.MX : transfer.EXI;
|
||||
if (transfer.region == transfer.EXI)
|
||||
transfer.address = ((data & ~0xc000) >> 8) & 0xff;
|
||||
else
|
||||
transfer.address = (data >> 8) & 0xffff;
|
||||
transfer.direction = IsWriteCommand(data) ? transfer.WRITE : transfer.READ;
|
||||
|
||||
DEBUG_LOG(SP1, "%s %s %s %x",
|
||||
IsMXCommand(data) ? "mx " : "exi",
|
||||
IsWriteCommand(data) ? "write" : "read ",
|
||||
GetRegisterName(),
|
||||
transfer.address);
|
||||
|
||||
if (transfer.address == BBA_IOB && transfer.region == transfer.MX)
|
||||
{
|
||||
INFO_LOG(SP1, "\t\twritep set to %0*x", size * 2, mWriteP);
|
||||
ERROR_LOG(SP1, "Usage of BBA_IOB indicates that the rx packet descriptor has been corrupted, killing dolphin...");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// transfer has been setup
|
||||
return;
|
||||
}
|
||||
else if ((size == 4 && (data & 0xC0000000) == 0x80000000) ||
|
||||
(size == 2 && (data & 0x4000) == 0x0000))
|
||||
|
||||
// Reach here if we're actually writing data to the EXI or MX region.
|
||||
|
||||
DEBUG_LOG(SP1, "%s write %0*x",
|
||||
transfer.region == transfer.MX ? "mx " : "exi", size * 2, data);
|
||||
|
||||
if (transfer.region == transfer.EXI)
|
||||
{
|
||||
// Read from BBA Register
|
||||
if (size == 4)
|
||||
mReadP = (data >> 8) & 0xffff;
|
||||
else //size == 2
|
||||
mReadP = (u8)getbitsw(data, 16, 23);
|
||||
|
||||
INFO_LOG(SP1, "Read from BBA register 0x%x", mReadP);
|
||||
|
||||
switch (mReadP)
|
||||
switch (transfer.address)
|
||||
{
|
||||
case BBA_NCRA:
|
||||
// These Two lines were commented out in Whinecube
|
||||
//mBbaMem[mReadP] = 0x00;
|
||||
//if(!sendInProgress())
|
||||
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
|
||||
INFO_LOG(SP1, "\tNCRA %02x", mBbaMem[BBA_NCRA]);
|
||||
case INTERRUPT:
|
||||
exi_status.interrupt &= data ^ 0xff;
|
||||
break;
|
||||
case BBA_NCRB: // Revision ID
|
||||
INFO_LOG(SP1, "\tNCRB");
|
||||
break;
|
||||
case BBA_NAFR_PAR0:
|
||||
INFO_LOG(SP1, "\tMac Address");
|
||||
memcpy(mBbaMem + mReadP, mac_address, 6);
|
||||
break;
|
||||
case 0x03: // status TODO more fields
|
||||
mBbaMem[mReadP] = m_bInterruptSet ? 0x80 : 0;
|
||||
INFO_LOG(SP1, "\tStatus %x", mBbaMem[mReadP]);
|
||||
break;
|
||||
case BBA_LTPS:
|
||||
INFO_LOG(SP1, "\tLPTS");
|
||||
break;
|
||||
case BBA_IMR:
|
||||
INFO_LOG(SP1, "\tIMR");
|
||||
break;
|
||||
case BBA_IR:
|
||||
INFO_LOG(SP1, "\tIR");
|
||||
break;
|
||||
case BBA_RWP:
|
||||
case BBA_RWP+1:
|
||||
MAKE(u16, mBbaMem[BBA_RWP]) = Common::swap16((u16)mCbw.p_write() + CB_OFFSET);
|
||||
INFO_LOG(SP1, "\tRWP 0x%04x", MAKE(u16, mBbaMem[mReadP]));
|
||||
break;
|
||||
case BBA_RRP:
|
||||
case BBA_RRP+1:
|
||||
MAKE(u16, mBbaMem[BBA_RRP]) = Common::swap16(mRBRPP);
|
||||
INFO_LOG(SP1, "\tRRP 0x%04x", MAKE(u16, mBbaMem[mReadP]));
|
||||
break;
|
||||
case 0x3A: // bit 1 set if no data available
|
||||
INFO_LOG(SP1, "\tBit 1 set!");
|
||||
//mBbaMem[mReadP] = !mRBEmpty;
|
||||
break;
|
||||
case BBA_TWP:
|
||||
case BBA_TWP+1:
|
||||
INFO_LOG(SP1, "\tTWP");
|
||||
break;
|
||||
case BBA_NWAYC:
|
||||
INFO_LOG(SP1, "\tNWAYC");
|
||||
break;
|
||||
case BBA_NWAYS:
|
||||
mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_ANCLPT | NWAYS_100TXF;
|
||||
INFO_LOG(SP1, "\tNWAYS %02x", mBbaMem[BBA_NWAYS]);
|
||||
break;
|
||||
case BBA_SI_ACTRL:
|
||||
INFO_LOG(SP1, "\tSI_ACTRL");
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(SP1, "UNKNOWN BBA REG READ %02x", mReadP);
|
||||
case INTERRUPT_MASK:
|
||||
exi_status.interrupt_mask = data;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR_LOG(SP1, "\tNot expecting imm write of size %d", size);
|
||||
ERROR_LOG(SP1, "\t\t SKIPPING!");
|
||||
else
|
||||
{
|
||||
MXCommandHandler(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 CEXIETHERNET::ImmRead(u32 size)
|
||||
{
|
||||
INFO_LOG(SP1, "imm read %i readp %x", size, mReadP);
|
||||
u32 ret = 0;
|
||||
|
||||
if (mExpectSpecialImmRead)
|
||||
if (transfer.region == transfer.EXI)
|
||||
{
|
||||
INFO_LOG(SP1, "\tspecial imm read %08x", mSpecialImmData);
|
||||
mExpectSpecialImmRead = false;
|
||||
return mSpecialImmData;
|
||||
}
|
||||
|
||||
if (mReadP != INVALID_P)
|
||||
{
|
||||
if (mReadP + size > BBA_MEM_SIZE)
|
||||
switch (transfer.address)
|
||||
{
|
||||
ERROR_LOG(SP1, "\tRead error: readp + size = %04x + %i", mReadP, size);
|
||||
return 0;
|
||||
case EXI_ID:
|
||||
ret = EXI_DEVTYPE_ETHER;
|
||||
break;
|
||||
case REVISION_ID:
|
||||
ret = exi_status.revision_id;
|
||||
break;
|
||||
case DEVICE_ID:
|
||||
ret = exi_status.device_id;
|
||||
break;
|
||||
case ACSTART:
|
||||
ret = exi_status.acstart;
|
||||
break;
|
||||
case INTERRUPT:
|
||||
ret = exi_status.interrupt;
|
||||
break;
|
||||
}
|
||||
u32 uResult = 0;
|
||||
memcpy(&uResult, mBbaMem + mReadP, size);
|
||||
|
||||
uResult = Common::swap32(uResult);
|
||||
|
||||
INFO_LOG(SP1, "\tRead from BBA address %0*x, %i byte%s: %0*x",
|
||||
mReadP >= CB_OFFSET ? 4 : 2, mReadP,
|
||||
size, (size==1?"":"s"),size*2, getbitsw(uResult, 0, size * 8 - 1));
|
||||
mReadP = mReadP + size;
|
||||
return uResult;
|
||||
transfer.address += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(SP1, "Unhandled imm read size %d", size);
|
||||
return 0;
|
||||
for (int i = size - 1; i >= 0; i--)
|
||||
ret |= mBbaMem[transfer.address++] << (i * 8);
|
||||
}
|
||||
|
||||
DEBUG_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret);
|
||||
|
||||
ret <<= (4 - size) * 8;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
|
||||
{
|
||||
if (mExpectVariableLengthImmWrite)
|
||||
{
|
||||
INFO_LOG(SP1, "DMA Write: Address is 0x%x and size is 0x%x", addr, size);
|
||||
mWriteBuffer.write(size, Memory::GetPointer(addr));
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(SP1, "dma w: %08x %x", addr, size);
|
||||
|
||||
ERROR_LOG(SP1, "Unhandled BBA DMA write: %i, %08x", size, addr);
|
||||
if (transfer.region == transfer.MX &&
|
||||
transfer.direction == transfer.WRITE &&
|
||||
transfer.address == BBA_WRTXFIFOD)
|
||||
{
|
||||
DirectFIFOWrite(Memory::GetPointer(addr), size);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(SP1, "dma w in %s %s mode - not implemented",
|
||||
transfer.region == transfer.EXI ? "exi" : "mx",
|
||||
transfer.direction == transfer.READ ? "read" : "write");
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIETHERNET::DMARead(u32 addr, u32 size)
|
||||
{
|
||||
if (mReadP != INVALID_P)
|
||||
{
|
||||
if (mReadP + size > BBA_MEM_SIZE)
|
||||
{
|
||||
INFO_LOG(SP1, "Read error: mReadP + size = %04x + %i", mReadP, size);
|
||||
return;
|
||||
}
|
||||
memcpy(Memory::GetPointer(addr), mBbaMem + mReadP, size);
|
||||
INFO_LOG(SP1, "DMA Read from BBA address %0*x, %i bytes to %08x",
|
||||
mReadP >= CB_OFFSET ? 4 : 2, mReadP, size, addr);
|
||||
mReadP = mReadP + (u16)size;
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(SP1, "dma r: %08x %x", addr, size);
|
||||
|
||||
ERROR_LOG(SP1, "Unhandled BBA DMA read: %i, %08x", size, addr);
|
||||
memcpy(Memory::GetPointer(addr), &mBbaMem[transfer.address], size);
|
||||
|
||||
transfer.address += size;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(m_uPosition);
|
||||
p.Do(m_uCommand);
|
||||
p.Do(m_bInterruptSet);
|
||||
p.Do(mWriteP);
|
||||
p.Do(mReadP);
|
||||
p.Do(mExpectSpecialImmRead);
|
||||
p.Do(mSpecialImmData);
|
||||
p.Do(Activated);
|
||||
p.Do(mRBRPP);
|
||||
p.Do(mRBEmpty);
|
||||
p.Do(mBbaMem);
|
||||
p.Do(mExpectVariableLengthImmWrite);
|
||||
p.Do(mReadyToSend);
|
||||
p.Do(RegisterBlock);
|
||||
// TODO?
|
||||
//mWriteBuffer.DoState(p);
|
||||
//mCbw.DoState(p);
|
||||
// TODO ... the rest...
|
||||
ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!");
|
||||
}
|
||||
|
||||
//#pragma optimize("",on)
|
||||
bool CEXIETHERNET::IsMXCommand(u32 const data)
|
||||
{
|
||||
return !!(data & (1 << 31));
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::IsWriteCommand(u32 const data)
|
||||
{
|
||||
return IsMXCommand(data) ? !!(data & (1 << 30)) : !!(data & (1 << 14));
|
||||
}
|
||||
|
||||
char const * const CEXIETHERNET::GetRegisterName() const
|
||||
{
|
||||
#define STR_RETURN(x) case x: return #x;
|
||||
|
||||
if (transfer.region == transfer.EXI)
|
||||
{
|
||||
switch (transfer.address)
|
||||
{
|
||||
STR_RETURN(EXI_ID)
|
||||
STR_RETURN(REVISION_ID)
|
||||
STR_RETURN(INTERRUPT)
|
||||
STR_RETURN(INTERRUPT_MASK)
|
||||
STR_RETURN(DEVICE_ID)
|
||||
STR_RETURN(ACSTART)
|
||||
STR_RETURN(HASH_READ)
|
||||
STR_RETURN(HASH_WRITE)
|
||||
STR_RETURN(HASH_STATUS)
|
||||
STR_RETURN(RESET)
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (transfer.address)
|
||||
{
|
||||
STR_RETURN(BBA_NCRA)
|
||||
STR_RETURN(BBA_NCRB)
|
||||
STR_RETURN(BBA_LTPS)
|
||||
STR_RETURN(BBA_LRPS)
|
||||
STR_RETURN(BBA_IMR)
|
||||
STR_RETURN(BBA_IR)
|
||||
STR_RETURN(BBA_BP)
|
||||
STR_RETURN(BBA_TLBP)
|
||||
STR_RETURN(BBA_TWP)
|
||||
STR_RETURN(BBA_IOB)
|
||||
STR_RETURN(BBA_TRP)
|
||||
STR_RETURN(BBA_RWP)
|
||||
STR_RETURN(BBA_RRP)
|
||||
STR_RETURN(BBA_RHBP)
|
||||
STR_RETURN(BBA_RXINTT)
|
||||
STR_RETURN(BBA_NAFR_PAR0)
|
||||
STR_RETURN(BBA_NAFR_PAR1)
|
||||
STR_RETURN(BBA_NAFR_PAR2)
|
||||
STR_RETURN(BBA_NAFR_PAR3)
|
||||
STR_RETURN(BBA_NAFR_PAR4)
|
||||
STR_RETURN(BBA_NAFR_PAR5)
|
||||
STR_RETURN(BBA_NAFR_MAR0)
|
||||
STR_RETURN(BBA_NAFR_MAR1)
|
||||
STR_RETURN(BBA_NAFR_MAR2)
|
||||
STR_RETURN(BBA_NAFR_MAR3)
|
||||
STR_RETURN(BBA_NAFR_MAR4)
|
||||
STR_RETURN(BBA_NAFR_MAR5)
|
||||
STR_RETURN(BBA_NAFR_MAR6)
|
||||
STR_RETURN(BBA_NAFR_MAR7)
|
||||
STR_RETURN(BBA_NWAYC)
|
||||
STR_RETURN(BBA_NWAYS)
|
||||
STR_RETURN(BBA_GCA)
|
||||
STR_RETURN(BBA_MISC)
|
||||
STR_RETURN(BBA_TXFIFOCNT)
|
||||
STR_RETURN(BBA_WRTXFIFOD)
|
||||
STR_RETURN(BBA_MISC2)
|
||||
STR_RETURN(BBA_SI_ACTRL)
|
||||
STR_RETURN(BBA_SI_STATUS)
|
||||
STR_RETURN(BBA_SI_ACTRL2)
|
||||
default:
|
||||
if (transfer.address >= 0x100 &&
|
||||
transfer.address <= 0xfff)
|
||||
return "packet buffer";
|
||||
else
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#undef STR_RETURN
|
||||
}
|
||||
|
||||
void CEXIETHERNET::MXHardReset()
|
||||
{
|
||||
memset(mBbaMem, 0, BBA_MEM_SIZE);
|
||||
|
||||
mBbaMem[BBA_NCRB] = NCRB_PR;
|
||||
mBbaMem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE;
|
||||
mBbaMem[BBA_MISC] = MISC1_TPF | MISC1_TPH | MISC1_TXF | MISC1_TXH;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
|
||||
{
|
||||
switch (transfer.address)
|
||||
{
|
||||
case BBA_NCRA:
|
||||
if (data & NCRA_RESET)
|
||||
{
|
||||
DEBUG_LOG(SP1, "software reset");
|
||||
//MXSoftReset();
|
||||
Activate();
|
||||
}
|
||||
|
||||
if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
|
||||
{
|
||||
DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop");
|
||||
|
||||
if (data & NCRA_SR)
|
||||
RecvStart();
|
||||
else
|
||||
RecvStop();
|
||||
}
|
||||
|
||||
// Only start transfer if there isn't one currently running
|
||||
if (!(mBbaMem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1)))
|
||||
{
|
||||
// Technically transfer dma status is kept in TXDMA - not implemented
|
||||
|
||||
if (data & NCRA_ST0)
|
||||
{
|
||||
WARN_LOG(SP1, "start tx - local dma");
|
||||
SendFromPacketBuffer();
|
||||
}
|
||||
else if (data & NCRA_ST1)
|
||||
{
|
||||
DEBUG_LOG(SP1, "start tx - direct fifo");
|
||||
SendFromDirectFIFO();
|
||||
// Kind of a hack: send completes instantly, so we don't
|
||||
// actually write the "send in status" bit to the register
|
||||
data &= ~NCRA_ST1;
|
||||
}
|
||||
}
|
||||
goto write_to_register;
|
||||
|
||||
case BBA_WRTXFIFOD:
|
||||
if (size == 2)
|
||||
data = Common::swap16(data & 0xffff);
|
||||
else if (size == 4)
|
||||
data = Common::swap32(data);
|
||||
DirectFIFOWrite((u8 *)&data, size);
|
||||
// Do not increment address
|
||||
return;
|
||||
|
||||
case BBA_IR:
|
||||
data &= (data & 0xff) ^ 0xff;
|
||||
goto write_to_register;
|
||||
|
||||
write_to_register:
|
||||
default:
|
||||
for (int i = size - 1; i >= 0; i--)
|
||||
{
|
||||
mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size)
|
||||
{
|
||||
// In direct mode, the hardware handles creating the state required by the
|
||||
// GMAC instead of finagling with packet descriptors and such
|
||||
|
||||
u16 *tx_fifo_count = (u16 *)&mBbaMem[BBA_TXFIFOCNT];
|
||||
|
||||
memcpy(tx_fifo + *tx_fifo_count, data, size);
|
||||
|
||||
*tx_fifo_count += size;
|
||||
// TODO not sure this mask is correct.
|
||||
// However, BBA_TXFIFOCNT should never get even close to this amount,
|
||||
// so it shouldn't matter
|
||||
*tx_fifo_count &= (1 << 12) - 1;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::SendFromDirectFIFO()
|
||||
{
|
||||
SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
|
||||
}
|
||||
|
||||
void CEXIETHERNET::SendFromPacketBuffer()
|
||||
{
|
||||
ERROR_LOG(SP1, "tx packet buffer not implemented");
|
||||
}
|
||||
|
||||
void CEXIETHERNET::SendComplete()
|
||||
{
|
||||
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
|
||||
*(u16 *)&mBbaMem[BBA_TXFIFOCNT] = 0;
|
||||
|
||||
if (mBbaMem[BBA_IMR] & INT_T)
|
||||
{
|
||||
mBbaMem[BBA_IR] |= INT_T;
|
||||
|
||||
exi_status.interrupt |= exi_status.TRANSFER;
|
||||
}
|
||||
|
||||
mBbaMem[BBA_LTPS] = 0;
|
||||
}
|
||||
|
||||
inline u8 CEXIETHERNET::HashIndex(u8 *dest_eth_addr)
|
||||
{
|
||||
// Calculate crc
|
||||
u32 crc = 0xffffffff;
|
||||
|
||||
for (size_t byte_num = 0; byte_num < 6; ++byte_num)
|
||||
{
|
||||
u8 cur_byte = dest_eth_addr[byte_num];
|
||||
for (size_t bit = 0; bit < 8; ++bit)
|
||||
{
|
||||
u8 carry = ((crc >> 31) & 1) ^ (cur_byte & 1);
|
||||
crc <<= 1;
|
||||
cur_byte >>= 1;
|
||||
if (carry)
|
||||
crc = (crc ^ 0x4c11db6) | carry;
|
||||
}
|
||||
}
|
||||
|
||||
// return bits used as index
|
||||
return crc >> 26;
|
||||
}
|
||||
|
||||
inline bool CEXIETHERNET::RecvMACFilter()
|
||||
{
|
||||
static u8 const broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
// Accept all destination addrs?
|
||||
if (mBbaMem[BBA_NCRB] & NCRB_PR)
|
||||
return true;
|
||||
|
||||
// Unicast?
|
||||
if ((mRecvBuffer[0] & 0x01) == 0)
|
||||
{
|
||||
return memcmp(mRecvBuffer, &mBbaMem[BBA_NAFR_PAR0], 6) == 0;
|
||||
}
|
||||
else if (memcmp(mRecvBuffer, broadcast, 6) == 0)
|
||||
{
|
||||
// Accept broadcast?
|
||||
return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
|
||||
}
|
||||
else if (mBbaMem[BBA_NCRB] & NCRB_PM)
|
||||
{
|
||||
// Accept all multicast
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Lookup the dest eth address in the hashmap
|
||||
u16 index = HashIndex(mRecvBuffer);
|
||||
return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
|
||||
}
|
||||
}
|
||||
|
||||
inline void CEXIETHERNET::inc_rwp()
|
||||
{
|
||||
u16 *rwp = (u16 *)&mBbaMem[BBA_RWP];
|
||||
|
||||
if (*rwp + 1 == page_ptr(BBA_RHBP))
|
||||
*rwp = page_ptr(BBA_BP);
|
||||
else
|
||||
(*rwp)++;
|
||||
}
|
||||
|
||||
// This function is on the critical path for recving data.
|
||||
// Be very careful about calling into the logger and other slow things
|
||||
bool CEXIETHERNET::RecvHandlePacket()
|
||||
{
|
||||
u8 *write_ptr;
|
||||
u8 *end_ptr;
|
||||
u8 *read_ptr;
|
||||
Descriptor *descriptor;
|
||||
u32 status = 0;
|
||||
u16 rwp_initial = page_ptr(BBA_RWP);
|
||||
|
||||
if (!RecvMACFilter())
|
||||
goto wait_for_next;
|
||||
|
||||
#ifdef BBA_TRACK_PAGE_PTRS
|
||||
WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength,
|
||||
ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str());
|
||||
|
||||
WARN_LOG(SP1, "%x %x %x %x",
|
||||
page_ptr(BBA_BP),
|
||||
page_ptr(BBA_RRP),
|
||||
page_ptr(BBA_RWP),
|
||||
page_ptr(BBA_RHBP));
|
||||
#endif
|
||||
|
||||
write_ptr = ptr_from_page_ptr(BBA_RWP);
|
||||
end_ptr = ptr_from_page_ptr(BBA_RHBP);
|
||||
read_ptr = ptr_from_page_ptr(BBA_RRP);
|
||||
|
||||
descriptor = (Descriptor *)write_ptr;
|
||||
write_ptr += 4;
|
||||
|
||||
for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
|
||||
{
|
||||
*write_ptr++ = mRecvBuffer[i];
|
||||
|
||||
if (off == 0xff)
|
||||
{
|
||||
off = 0;
|
||||
inc_rwp();
|
||||
}
|
||||
|
||||
if (write_ptr == end_ptr)
|
||||
write_ptr = ptr_from_page_ptr(BBA_BP);
|
||||
|
||||
if (write_ptr == read_ptr)
|
||||
{
|
||||
/*
|
||||
halt copy
|
||||
if (cur_packet_size >= PAGE_SIZE)
|
||||
desc.status |= FO | BF
|
||||
if (RBFIM)
|
||||
raise RBFI
|
||||
if (AUTORCVR)
|
||||
discard bad packet
|
||||
else
|
||||
inc MPC instad of recving packets
|
||||
*/
|
||||
status |= DESC_FO | DESC_BF;
|
||||
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Align up to next page
|
||||
if ((mRecvBufferLength + 4) % 256)
|
||||
inc_rwp();
|
||||
|
||||
#ifdef BBA_TRACK_PAGE_PTRS
|
||||
WARN_LOG(SP1, "%x %x %x %x",
|
||||
page_ptr(BBA_BP),
|
||||
page_ptr(BBA_RRP),
|
||||
page_ptr(BBA_RWP),
|
||||
page_ptr(BBA_RHBP));
|
||||
#endif
|
||||
|
||||
// Is the current frame multicast?
|
||||
if (mRecvBuffer[0] & 0x01)
|
||||
status |= DESC_MF;
|
||||
|
||||
if (status & DESC_BF)
|
||||
{
|
||||
if (mBbaMem[BBA_MISC2] & MISC2_AUTORCVR)
|
||||
{
|
||||
*(u16 *)&mBbaMem[BBA_RWP] = rwp_initial;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(SP1, "RBF while AUTORCVR == 0!");
|
||||
}
|
||||
}
|
||||
|
||||
descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
|
||||
|
||||
mBbaMem[BBA_LRPS] = status;
|
||||
|
||||
// Raise interrupt
|
||||
if (mBbaMem[BBA_IMR] & INT_R)
|
||||
{
|
||||
mBbaMem[BBA_IR] |= INT_R;
|
||||
|
||||
exi_status.interrupt |= exi_status.TRANSFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This occurs if software is still processing the last raised recv interrupt
|
||||
WARN_LOG(SP1, "NOT raising recv interrupt");
|
||||
}
|
||||
|
||||
wait_for_next:
|
||||
if (mBbaMem[BBA_NCRA] & NCRA_SR)
|
||||
RecvStart();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,107 +20,16 @@
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
inline u8 makemaskb(int start, int end) {
|
||||
return (u8)_rotl((2 << (end - start)) - 1, 7 - end);
|
||||
}
|
||||
inline u32 makemaskh(int start, int end) {
|
||||
return (u32)_rotl((2 << (end - start)) - 1, 15 - end);
|
||||
}
|
||||
inline u32 makemaskw(int start, int end) {
|
||||
return _rotl((2 << (end - start)) - 1, 31 - end);
|
||||
}
|
||||
inline u8 getbitsb(u8 byte, int start, int end) {
|
||||
return (byte & makemaskb(start, end)) >> u8(7 - end);
|
||||
}
|
||||
inline u32 getbitsh(u32 hword, int start, int end) {
|
||||
return (hword & makemaskh(start, end)) >> u32(15 - end);
|
||||
}
|
||||
inline u32 getbitsw(u32 dword, int start, int end) {
|
||||
return (dword & makemaskw(start, end)) >> (31 - end);
|
||||
}
|
||||
|
||||
class WriteBuffer
|
||||
{
|
||||
public:
|
||||
WriteBuffer(u32 s) : _size(0)
|
||||
{
|
||||
_buffer = (u8*)malloc(s*sizeof(u8));
|
||||
ucapacity = s;
|
||||
}
|
||||
~WriteBuffer() { free(_buffer); }
|
||||
u32 size() const { return _size; }
|
||||
u32 capacity() const { return ucapacity; }
|
||||
void write(u32 s, const void *src)
|
||||
{
|
||||
if (_size + s >= ucapacity)
|
||||
{
|
||||
INFO_LOG(SP1, "Write too large!");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
memcpy(_buffer + _size, src, s);
|
||||
_size = _size + s;
|
||||
}
|
||||
void clear() { _size = 0; }
|
||||
u8* p() { return _buffer; }
|
||||
|
||||
private:
|
||||
u8* _buffer;
|
||||
u32 ucapacity;
|
||||
u32 _size;
|
||||
};
|
||||
|
||||
// Doesn't contain error checks for wraparound writes
|
||||
class CyclicBufferWriter
|
||||
{
|
||||
public:
|
||||
CyclicBufferWriter(u8 *buffer, size_t cap) {
|
||||
_buffer = buffer; _capacity = cap; _write = 0;
|
||||
}
|
||||
|
||||
size_t p_write() const { return _write; }
|
||||
void reset() { _write = 0; }
|
||||
|
||||
void write(void *src, size_t size) {
|
||||
_dbg_assert_(SP1, size < _capacity);
|
||||
u8* bsrc = (u8*) src;
|
||||
if (_write + size >= _capacity)
|
||||
{
|
||||
// wraparound
|
||||
memcpy(_buffer + _write, src, _capacity - _write);
|
||||
memcpy(_buffer, bsrc + (_capacity - _write), size - (_capacity - _write));
|
||||
_write = size - (_capacity - _write);
|
||||
} else {
|
||||
memcpy(_buffer + _write, src, size);
|
||||
_write += size;
|
||||
}
|
||||
//DEBUG_LOG(SP1, "CBWrote %i bytes", size);
|
||||
}
|
||||
// Aligns the write pointer to steps of 0x100, like the real BBA
|
||||
void align() {
|
||||
_write = (_write + 0xff) & ~0xff;
|
||||
if(_write >= _capacity)
|
||||
_write -= _capacity;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _write;
|
||||
size_t _capacity;
|
||||
u8 *_buffer;
|
||||
};
|
||||
|
||||
#define INVALID_P 0xFFFF
|
||||
|
||||
// Network Control Register A, RW
|
||||
// Network Control Register A
|
||||
enum NCRA
|
||||
{
|
||||
NCRA_RESET = 0x01, // RESET
|
||||
NCRA_ST0 = 0x02, // Start transmit command/status
|
||||
NCRA_ST1 = 0x04, // "
|
||||
NCRA_SR = 0x08, // Start Receive
|
||||
NCRA_SR = 0x08 // Start Receive
|
||||
};
|
||||
|
||||
// Network Control Register B, RW
|
||||
// Network Control Register B
|
||||
enum NCRB
|
||||
{
|
||||
NCRB_PR = 0x01, // Promiscuous Mode
|
||||
@@ -129,11 +38,11 @@ enum NCRB
|
||||
NCRB_PB = 0x08, // Pass Bad Frame
|
||||
NCRB_AB = 0x10, // Accept Broadcast
|
||||
NCRB_HBD = 0x20, // reserved
|
||||
NCRB_RXINTC = 0xC0, // Receive Interrupt Counter (mask)
|
||||
NCRB_RXINTC = 0xC0 // Receive Interrupt Counter (mask)
|
||||
};
|
||||
|
||||
// Interrupt Mask Register, RW, 00h
|
||||
// Interrupt Register, RW, 00h
|
||||
// Interrupt Mask Register
|
||||
// Interrupt Register
|
||||
enum Interrupts
|
||||
{
|
||||
INT_FRAG = 0x01, // Fragment Counter
|
||||
@@ -143,17 +52,20 @@ enum Interrupts
|
||||
INT_T_ERR = 0x10, // Transmit Error
|
||||
INT_FIFO_ERR = 0x20, // FIFO Error
|
||||
INT_BUS_ERR = 0x40, // BUS Error
|
||||
INT_RBF = 0x80, // RX Buffer Full
|
||||
INT_RBF = 0x80 // RX Buffer Full
|
||||
};
|
||||
|
||||
// NWAY Configuration Register, RW, 84h
|
||||
// NWAY Configuration Register
|
||||
enum NWAYC
|
||||
{
|
||||
NWAYC_FD = 0x01, // Full Duplex Mode
|
||||
NWAYC_PS100 = 0x02, // Port Select 100/10
|
||||
NWAYC_ANE = 0x03, // Autonegotiation Enable
|
||||
NWAYC_ANS_RA = 0x04, // Restart Autonegotiation
|
||||
NWAYC_LTE = 0x08, // Link Test Enable
|
||||
NWAYC_PS100_10 = 0x02, // Port Select 100/10
|
||||
NWAYC_ANE = 0x04, // Autonegotiate enable
|
||||
|
||||
// Autonegotiation status bits...
|
||||
|
||||
NWAYC_NTTEST = 0x40, // Reserved
|
||||
NWAYC_LTE = 0x80 // Link Test Enable
|
||||
};
|
||||
|
||||
enum NWAYS
|
||||
@@ -168,6 +80,30 @@ enum NWAYS
|
||||
NWAYS_10TXH = 0x80
|
||||
};
|
||||
|
||||
enum MISC1
|
||||
{
|
||||
MISC1_BURSTDMA = 0x01,
|
||||
MISC1_DISLDMA = 0x02,
|
||||
MISC1_TPF = 0x04,
|
||||
MISC1_TPH = 0x08,
|
||||
MISC1_TXF = 0x10,
|
||||
MISC1_TXH = 0x20,
|
||||
MISC1_TXFIFORST = 0x40,
|
||||
MISC1_RXFIFORST = 0x80
|
||||
};
|
||||
|
||||
enum MISC2
|
||||
{
|
||||
MISC2_HBRLEN0 = 0x01,
|
||||
MISC2_HBRLEN1 = 0x02,
|
||||
MISC2_RUNTSIZE = 0x04,
|
||||
MISC2_DREQBCTRL = 0x08,
|
||||
MISC2_RINTSEL = 0x10,
|
||||
MISC2_ITPSEL = 0x20,
|
||||
MISC2_A11A8EN = 0x40,
|
||||
MISC2_AUTORCVR = 0x80
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BBA_NCRA = 0x00,
|
||||
@@ -182,6 +118,7 @@ enum
|
||||
BBA_BP = 0x0a,
|
||||
BBA_TLBP = 0x0c,
|
||||
BBA_TWP = 0x0e,
|
||||
BBA_IOB = 0x10,
|
||||
BBA_TRP = 0x12,
|
||||
BBA_RWP = 0x16,
|
||||
BBA_RRP = 0x18,
|
||||
@@ -195,6 +132,14 @@ enum
|
||||
BBA_NAFR_PAR3 = 0x23,
|
||||
BBA_NAFR_PAR4 = 0x24,
|
||||
BBA_NAFR_PAR5 = 0x25,
|
||||
BBA_NAFR_MAR0 = 0x26,
|
||||
BBA_NAFR_MAR1 = 0x27,
|
||||
BBA_NAFR_MAR2 = 0x28,
|
||||
BBA_NAFR_MAR3 = 0x29,
|
||||
BBA_NAFR_MAR4 = 0x2a,
|
||||
BBA_NAFR_MAR5 = 0x2b,
|
||||
BBA_NAFR_MAR6 = 0x2c,
|
||||
BBA_NAFR_MAR7 = 0x2d,
|
||||
|
||||
BBA_NWAYC = 0x30,
|
||||
BBA_NWAYS = 0x31,
|
||||
@@ -210,37 +155,51 @@ enum
|
||||
|
||||
BBA_SI_ACTRL = 0x5c,
|
||||
BBA_SI_STATUS = 0x5d,
|
||||
BBA_SI_ACTRL2 = 0x60,
|
||||
BBA_SI_ACTRL2 = 0x60
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BBA_RECV_SIZE = 0x800,
|
||||
BBA_MEM_SIZE = 0x1000,
|
||||
|
||||
CB_OFFSET = 0x100,
|
||||
CB_SIZE = (BBA_MEM_SIZE - CB_OFFSET),
|
||||
SIZEOF_ETH_HEADER = 0xe,
|
||||
SIZEOF_RECV_DESCRIPTOR = 4,
|
||||
|
||||
EXI_DEVTYPE_ETHER = 0x04020200,
|
||||
BBA_NUM_PAGES = 0x10,
|
||||
BBA_PAGE_SIZE = 0x100,
|
||||
BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE
|
||||
};
|
||||
|
||||
enum
|
||||
enum { EXI_DEVTYPE_ETHER = 0x04020200 };
|
||||
|
||||
enum SendStatus
|
||||
{
|
||||
EXPECT_NONE = 0,
|
||||
EXPECT_ID
|
||||
DESC_CC0 = 0x01,
|
||||
DESC_CC1 = 0x02,
|
||||
DESC_CC2 = 0x04,
|
||||
DESC_CC3 = 0x08,
|
||||
DESC_CRSLOST= 0x10,
|
||||
DESC_UF = 0x20,
|
||||
DESC_OWC = 0x40,
|
||||
DESC_OWN = 0x80
|
||||
};
|
||||
|
||||
enum RecvStatus
|
||||
{
|
||||
DESC_BF = 0x01,
|
||||
DESC_CRC = 0x02,
|
||||
DESC_FAE = 0x04,
|
||||
DESC_FO = 0x08,
|
||||
DESC_RW = 0x10,
|
||||
DESC_MF = 0x20,
|
||||
DESC_RF = 0x40,
|
||||
DESC_RERR = 0x80
|
||||
};
|
||||
|
||||
#define BBA_RECV_SIZE 0x800
|
||||
|
||||
class CEXIETHERNET : public IEXIDevice
|
||||
{
|
||||
public:
|
||||
CEXIETHERNET(const std::string& mac_addr);
|
||||
~CEXIETHERNET();
|
||||
void SetMAC(u8 *new_addr);
|
||||
void SetCS(int _iCS);
|
||||
CEXIETHERNET();
|
||||
virtual ~CEXIETHERNET();
|
||||
void SetCS(int cs);
|
||||
bool IsPresent();
|
||||
void Update();
|
||||
bool IsInterruptSet();
|
||||
void ImmWrite(u32 data, u32 size);
|
||||
u32 ImmRead(u32 size);
|
||||
@@ -249,61 +208,133 @@ public:
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
//private:
|
||||
// STATE_TO_SAVE
|
||||
u32 m_uPosition;
|
||||
u32 m_uCommand;
|
||||
struct
|
||||
{
|
||||
enum
|
||||
{
|
||||
READ,
|
||||
WRITE
|
||||
} direction;
|
||||
|
||||
bool m_bInterruptSet;
|
||||
u16 mWriteP, mReadP;
|
||||
enum
|
||||
{
|
||||
EXI,
|
||||
MX
|
||||
} region;
|
||||
|
||||
bool mExpectSpecialImmRead; //reset to false on deselect
|
||||
u32 mSpecialImmData;
|
||||
bool Activated;
|
||||
u16 address;
|
||||
bool valid;
|
||||
} transfer;
|
||||
|
||||
u16 mRBRPP; //RRP - Receive Buffer Read Page Pointer
|
||||
bool mRBEmpty;
|
||||
|
||||
u8 mBbaMem[BBA_MEM_SIZE];
|
||||
|
||||
WriteBuffer mWriteBuffer;
|
||||
CyclicBufferWriter mCbw;
|
||||
|
||||
bool mExpectVariableLengthImmWrite;
|
||||
bool mReadyToSend;
|
||||
u8 RegisterBlock[0x1000];
|
||||
enum
|
||||
{
|
||||
CMD_ID = 0x00,
|
||||
CMD_READ_REG = 0x01,
|
||||
EXI_ID,
|
||||
REVISION_ID,
|
||||
INTERRUPT_MASK,
|
||||
INTERRUPT,
|
||||
DEVICE_ID,
|
||||
ACSTART,
|
||||
HASH_READ = 8,
|
||||
HASH_WRITE,
|
||||
HASH_STATUS = 0xb,
|
||||
RESET = 0xf
|
||||
};
|
||||
|
||||
void recordSendComplete();
|
||||
bool sendPacket(u8 *etherpckt, int size);
|
||||
bool checkRecvBuffer();
|
||||
bool handleRecvdPacket();
|
||||
// exi regs
|
||||
struct EXIStatus
|
||||
{
|
||||
enum
|
||||
{
|
||||
TRANSFER = 0x80
|
||||
};
|
||||
|
||||
//TAP interface
|
||||
bool activate();
|
||||
bool CheckRecieved();
|
||||
bool deactivate();
|
||||
bool isActivated();
|
||||
bool resume();
|
||||
bool startRecv();
|
||||
bool cbwriteDescriptor(u32 size);
|
||||
u8 revision_id;
|
||||
u8 interrupt_mask;
|
||||
u8 interrupt;
|
||||
u16 device_id;
|
||||
u8 acstart;
|
||||
u32 hash_challenge;
|
||||
u32 hash_response;
|
||||
u8 hash_status;
|
||||
|
||||
EXIStatus()
|
||||
{
|
||||
device_id = 0xd107;
|
||||
revision_id = 0;//0xf0;
|
||||
acstart = 0x4e;
|
||||
|
||||
volatile bool mWaiting;
|
||||
u8 mac_address[6];
|
||||
u8 mRecvBuffer[BBA_RECV_SIZE];
|
||||
#ifdef _WIN32
|
||||
interrupt_mask = 0;
|
||||
interrupt = 0;
|
||||
hash_challenge = 0;
|
||||
hash_response = 0;
|
||||
hash_status = 0;
|
||||
}
|
||||
|
||||
} exi_status;
|
||||
|
||||
struct Descriptor
|
||||
{
|
||||
u32 word;
|
||||
|
||||
inline void set(u32 const next_page, u32 const packet_length, u32 const status)
|
||||
{
|
||||
word = 0;
|
||||
word |= (status & 0xff) << 24;
|
||||
word |= (packet_length & 0xfff) << 12;
|
||||
word |= next_page & 0xfff;
|
||||
}
|
||||
};
|
||||
|
||||
inline u16 page_ptr(int const index) const
|
||||
{
|
||||
return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index];
|
||||
}
|
||||
|
||||
inline u8 *ptr_from_page_ptr(int const index) const
|
||||
{
|
||||
return &mBbaMem[page_ptr(index) << 8];
|
||||
}
|
||||
|
||||
bool IsMXCommand(u32 const data);
|
||||
bool IsWriteCommand(u32 const data);
|
||||
char const * const GetRegisterName() const;
|
||||
void MXHardReset();
|
||||
void MXCommandHandler(u32 data, u32 size);
|
||||
void DirectFIFOWrite(u8 *data, u32 size);
|
||||
void SendFromDirectFIFO();
|
||||
void SendFromPacketBuffer();
|
||||
void SendComplete();
|
||||
u8 HashIndex(u8 *dest_eth_addr);
|
||||
bool RecvMACFilter();
|
||||
void inc_rwp();
|
||||
bool RecvHandlePacket();
|
||||
|
||||
u8 *tx_fifo;
|
||||
u8 *mBbaMem;
|
||||
|
||||
// TAP interface
|
||||
bool Activate();
|
||||
void Deactivate();
|
||||
bool IsActivated();
|
||||
bool SendFrame(u8 *frame, u32 size);
|
||||
bool RecvInit();
|
||||
bool RecvStart();
|
||||
void RecvStop();
|
||||
|
||||
u8 *mRecvBuffer;
|
||||
u32 mRecvBufferLength;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE mHAdapter, mHRecvEvent, mHReadWait;
|
||||
DWORD mMtu;
|
||||
OVERLAPPED mReadOverlapped;
|
||||
DWORD mRecvBufferLength;
|
||||
static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired);
|
||||
#else
|
||||
u32 mRecvBufferLength;
|
||||
#elif defined(__linux__)
|
||||
int fd;
|
||||
std::thread readThread;
|
||||
volatile bool readEnabled;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -40,9 +40,11 @@
|
||||
|
||||
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
// casting userdata seems less error-prone than indexing a static (creation order issues, etc.)
|
||||
CEXIMemoryCard *ptr = (CEXIMemoryCard*)userdata;
|
||||
ptr->Flush();
|
||||
// note that userdata is forbidden to be a pointer, due to the implemenation of EventDoState
|
||||
int card_index = (int)userdata;
|
||||
CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index);
|
||||
if (pThis)
|
||||
pThis->Flush();
|
||||
}
|
||||
|
||||
CEXIMemoryCard::CEXIMemoryCard(const int index)
|
||||
@@ -50,6 +52,9 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
|
||||
, m_bDirty(false)
|
||||
{
|
||||
m_strFilename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB;
|
||||
if (Movie::IsUsingMemcard() && Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsBlankMemcard())
|
||||
m_strFilename = "Movie.raw";
|
||||
|
||||
// we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential
|
||||
et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardA" : "memcardB", FlushCallback);
|
||||
|
||||
@@ -110,7 +115,7 @@ void innerFlush(FlushData* data)
|
||||
SplitPath(data->filename, &dir, 0, 0);
|
||||
if (!File::IsDirectory(dir))
|
||||
File::CreateFullPath(dir);
|
||||
pFile.Open(data->filename, "r+b");
|
||||
pFile.Open(data->filename, "wb");
|
||||
}
|
||||
|
||||
if (!pFile) // Note - pFile changed inside above if
|
||||
@@ -237,7 +242,7 @@ void CEXIMemoryCard::SetCS(int cs)
|
||||
// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
|
||||
// But first we unschedule already scheduled flushes - no point in flushing once per page for a large write.
|
||||
CoreTiming::RemoveEvent(et_this_card);
|
||||
CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)this);
|
||||
CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)card_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -423,9 +428,17 @@ void CEXIMemoryCard::TransferByte(u8 &byte)
|
||||
DEBUG_LOG(EXPANSIONINTERFACE, "EXI MEMCARD: < %02x", byte);
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::OnAfterLoad()
|
||||
void CEXIMemoryCard::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
|
||||
if (doLock)
|
||||
{
|
||||
// we don't exactly have anything to pause,
|
||||
// but let's make sure the flush thread isn't running.
|
||||
if (flushThread.joinable())
|
||||
{
|
||||
flushThread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::DoState(PointerWrap &p)
|
||||
@@ -452,5 +465,15 @@ void CEXIMemoryCard::DoState(PointerWrap &p)
|
||||
p.Do(card_id);
|
||||
p.Do(memory_card_size);
|
||||
p.DoArray(memory_card_content, memory_card_size);
|
||||
p.Do(card_index);
|
||||
}
|
||||
}
|
||||
|
||||
IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int customIndex)
|
||||
{
|
||||
if (device_type != m_deviceType)
|
||||
return NULL;
|
||||
if (customIndex != card_index)
|
||||
return NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ public:
|
||||
bool IsInterruptSet();
|
||||
bool IsPresent();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
|
||||
|
||||
private:
|
||||
// This is scheduled whenever a page write is issued. The this pointer is passed
|
||||
|
||||
@@ -38,12 +38,12 @@ void CEXIMic::StreamLog(const char *msg)
|
||||
void CEXIMic::StreamInit()
|
||||
{
|
||||
// Setup the wonderful c-interfaced lib...
|
||||
pa_stream = NULL;
|
||||
pa_stream = nullptr;
|
||||
|
||||
if ((pa_error = Pa_Initialize()) != paNoError)
|
||||
StreamLog("Pa_Initialize");
|
||||
|
||||
stream_buffer = NULL;
|
||||
stream_buffer = nullptr;
|
||||
samples_avail = stream_wpos = stream_rpos = 0;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ static int Pa_Callback(const void *inputBuffer, void *outputBuffer,
|
||||
s16 *buff_in = (s16 *)inputBuffer;
|
||||
s16 *buff_out = &mic->stream_buffer[mic->stream_wpos];
|
||||
|
||||
if (buff_in == NULL)
|
||||
if (buff_in == nullptr)
|
||||
{
|
||||
for (int i = 0; i < mic->buff_size_samples; i++)
|
||||
{
|
||||
@@ -118,11 +118,13 @@ void CEXIMic::StreamStart()
|
||||
|
||||
void CEXIMic::StreamStop()
|
||||
{
|
||||
if (pa_stream != NULL && Pa_IsStreamActive(pa_stream) >= paNoError)
|
||||
if (pa_stream != nullptr && Pa_IsStreamActive(pa_stream) >= paNoError)
|
||||
Pa_AbortStream(pa_stream);
|
||||
|
||||
samples_avail = stream_wpos = stream_rpos = 0;
|
||||
|
||||
delete [] stream_buffer;
|
||||
stream_buffer = NULL;
|
||||
stream_buffer = nullptr;
|
||||
}
|
||||
|
||||
void CEXIMic::StreamReadOne()
|
||||
|
||||
@@ -473,11 +473,20 @@ std::string GCMemcard::DEntry_IconFmt(u8 index) const
|
||||
return format;
|
||||
}
|
||||
|
||||
u16 GCMemcard::DEntry_AnimSpeed(u8 index) const
|
||||
std::string GCMemcard::DEntry_AnimSpeed(u8 index) const
|
||||
{
|
||||
if (!m_valid || index > DIRLEN)
|
||||
return 0xFF;
|
||||
return BE16(CurrentDir->Dir[index].AnimSpeed);
|
||||
return "";
|
||||
int x = CurrentDir->Dir[index].AnimSpeed[0];
|
||||
std::string speed;
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if (i == 8) x = CurrentDir->Dir[index].AnimSpeed[1];
|
||||
speed.push_back((x & 0x80) ? '1' : '0');
|
||||
x = x << 1;
|
||||
}
|
||||
speed.push_back(0);
|
||||
return speed;
|
||||
}
|
||||
|
||||
std::string GCMemcard::DEntry_Permissions(u8 index) const
|
||||
@@ -687,6 +696,8 @@ u32 GCMemcard::ImportFile(DEntry& direntry, std::vector<GCMBlock> &saveBlocks)
|
||||
|
||||
int fileBlocks = BE16(direntry.BlockCount);
|
||||
|
||||
FZEROGX_MakeSaveGameValid(direntry, saveBlocks);
|
||||
PSO_MakeSaveGameValid(direntry, saveBlocks);
|
||||
|
||||
BlockAlloc UpdatedBat = *CurrentBat;
|
||||
u16 nextBlock;
|
||||
@@ -1084,15 +1095,18 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
|
||||
|
||||
// To ensure only one type of icon is used
|
||||
// Sonic Heroes it the only game I have seen that tries to use a CI8 and RGB5A3 icon
|
||||
int fmtCheck = 0;
|
||||
//int fmtCheck = 0;
|
||||
|
||||
int formats = BE16(CurrentDir->Dir[index].IconFmt);
|
||||
int fdelays = BE16(CurrentDir->Dir[index].AnimSpeed);
|
||||
|
||||
int flags = CurrentDir->Dir[index].BIFlags;
|
||||
// Timesplitters 2 is the only game that I see this in
|
||||
// Timesplitters 2 and 3 is the only game that I see this in
|
||||
// May be a hack
|
||||
if (flags == 0xFB) flags = ~flags;
|
||||
//if (flags == 0xFB) flags = ~flags;
|
||||
// Batten Kaitos has 0x65 as flag too. Everything but the first 3 bytes seems irrelevant.
|
||||
// Something similar happens with Wario Ware Inc. AnimSpeed
|
||||
|
||||
int bnrFormat = (flags&3);
|
||||
|
||||
u32 DataOffset = BE32(CurrentDir->Dir[index].ImageOffset);
|
||||
@@ -1108,7 +1122,6 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
|
||||
switch (bnrFormat)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
animData += 96*32 + 2*256; // image+palette
|
||||
break;
|
||||
case 2:
|
||||
@@ -1120,40 +1133,48 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
|
||||
u8* data[8];
|
||||
int frames = 0;
|
||||
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
fmts[i] = (formats >> (2*i))&3;
|
||||
delays[i] = ((fdelays >> (2*i))&3) << 2;
|
||||
delays[i] = ((fdelays >> (2*i))&3);
|
||||
data[i] = animData;
|
||||
|
||||
if (!fmtCheck) fmtCheck = fmts[i];
|
||||
if (fmtCheck == fmts[i])
|
||||
if (!delays[i])
|
||||
{
|
||||
//First icon_speed = 0 indicates there aren't any more icons
|
||||
break;
|
||||
}
|
||||
//If speed is set there is an icon (it can be a "blank frame")
|
||||
frames++;
|
||||
if (fmts[i] != 0)
|
||||
{
|
||||
switch (fmts[i])
|
||||
{
|
||||
case CI8SHARED: // CI8 with shared palette
|
||||
animData += 32*32;
|
||||
frames++;
|
||||
break;
|
||||
case RGB5A3: // RGB5A3
|
||||
animData += 32*32*2;
|
||||
frames++;
|
||||
break;
|
||||
case CI8: // CI8 with own palette
|
||||
animData += 32*32 + 2*256;
|
||||
frames++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16* sharedPal = (u16*)(animData);
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
|
||||
if (fmtCheck == fmts[i])
|
||||
if (!delays[i])
|
||||
{
|
||||
//First icon_speed = 0 indicates there aren't any more icons
|
||||
break;
|
||||
}
|
||||
if (fmts[i] != 0)
|
||||
{
|
||||
switch (fmts[i])
|
||||
{
|
||||
@@ -1163,6 +1184,7 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
|
||||
break;
|
||||
case RGB5A3: // RGB5A3
|
||||
decode5A3image(buffer, (u16*)(data[i]), 32, 32);
|
||||
buffer += 32*32;
|
||||
break;
|
||||
case CI8: // CI8 with own palette
|
||||
u16 *paldata = (u16*)(data[i] + 32*32);
|
||||
@@ -1171,6 +1193,33 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Speed is set but there's no actual icon
|
||||
//This is used to reduce animation speed in Pikmin and Luigi's Mansion for example
|
||||
//These "blank frames" show the next icon
|
||||
for(j=i; j<8;++j)
|
||||
{
|
||||
if (fmts[j] != 0)
|
||||
{
|
||||
switch (fmts[j])
|
||||
{
|
||||
case CI8SHARED: // CI8 with shared palette
|
||||
decodeCI8image(buffer,data[j],sharedPal,32,32);
|
||||
break;
|
||||
case RGB5A3: // RGB5A3
|
||||
decode5A3image(buffer, (u16*)(data[j]), 32, 32);
|
||||
buffer += 32*32;
|
||||
break;
|
||||
case CI8: // CI8 with own palette
|
||||
u16 *paldata = (u16*)(data[j] + 32*32);
|
||||
decodeCI8image(buffer, data[j], paldata, 32, 32);
|
||||
buffer += 32*32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
@@ -1266,3 +1315,129 @@ void GCMemcard::FormatInternal(GCMC_Header &GCP)
|
||||
calc_checksumsBE((u16*)p_bat+2, 0xFFE, &p_bat->Checksum, &p_bat->Checksum_Inv);
|
||||
calc_checksumsBE((u16*)p_bat_backup+2, 0xFFE, &p_bat_backup->Checksum, &p_bat_backup->Checksum_Inv);
|
||||
}
|
||||
|
||||
void GCMemcard::CARD_GetSerialNo(u32 *serial1,u32 *serial2)
|
||||
{
|
||||
u32 serial[8];
|
||||
int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
memcpy(&serial[i], (u8 *) &hdr+(i*4), 4);
|
||||
}
|
||||
|
||||
*serial1 = serial[0]^serial[2]^serial[4]^serial[6];
|
||||
*serial2 = serial[1]^serial[3]^serial[5]^serial[7];
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************/
|
||||
/* FZEROGX_MakeSaveGameValid */
|
||||
/* (use just before writing a F-Zero GX system .gci file) */
|
||||
/* */
|
||||
/* chn: Destination memory card port */
|
||||
/* ret: Error code */
|
||||
/*************************************************************/
|
||||
|
||||
s32 GCMemcard::FZEROGX_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer)
|
||||
{
|
||||
u32 i,j;
|
||||
u32 serial1,serial2;
|
||||
u16 chksum = 0xFFFF;
|
||||
int block = 0;
|
||||
|
||||
// check for F-Zero GX system file
|
||||
if (strcmp((char*)direntry.Filename,"f_zero.dat")!=0) return 0;
|
||||
|
||||
// get encrypted destination memory card serial numbers
|
||||
CARD_GetSerialNo(&serial1,&serial2);
|
||||
|
||||
// set new serial numbers
|
||||
*(u16*)&FileBuffer[1].block[0x0066] = BE16(BE32(serial1) >> 16);
|
||||
*(u16*)&FileBuffer[3].block[0x1580] = BE16(BE32(serial2) >> 16);
|
||||
*(u16*)&FileBuffer[1].block[0x0060] = BE16(BE32(serial1) & 0xFFFF);
|
||||
*(u16*)&FileBuffer[1].block[0x0200] = BE16(BE32(serial2) & 0xFFFF);
|
||||
|
||||
// calc 16-bit checksum
|
||||
for (i=0x02;i<0x8000;i++)
|
||||
{
|
||||
chksum ^= (FileBuffer[block].block[i-(block*0x2000)]&0xFF);
|
||||
for (j=8; j > 0; j--)
|
||||
{
|
||||
if (chksum&1) chksum = (chksum>>1)^0x8408;
|
||||
else chksum >>= 1;
|
||||
}
|
||||
if (!(i%0x2000)) block ++;
|
||||
}
|
||||
|
||||
// set new checksum
|
||||
*(u16*)&FileBuffer[0].block[0x00] = BE16(~chksum);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* PSO_MakeSaveGameValid */
|
||||
/* (use just before writing a PSO system .gci file) */
|
||||
/* */
|
||||
/* chn: Destination memory card port */
|
||||
/* ret: Error code */
|
||||
/***********************************************************/
|
||||
|
||||
s32 GCMemcard::PSO_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer)
|
||||
{
|
||||
u32 i,j;
|
||||
u32 chksum;
|
||||
u32 crc32LUT[256];
|
||||
u32 serial1,serial2;
|
||||
u32 pso3offset = 0x00;
|
||||
|
||||
// check for PSO1&2 system file
|
||||
if (strcmp((char*)direntry.Filename,"PSO_SYSTEM")!=0)
|
||||
{
|
||||
// check for PSO3 system file
|
||||
if (strcmp((char*)direntry.Filename,"PSO3_SYSTEM")==0)
|
||||
{
|
||||
// PSO3 data block size adjustment
|
||||
pso3offset = 0x10;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// get encrypted destination memory card serial numbers
|
||||
CARD_GetSerialNo(&serial1,&serial2);
|
||||
|
||||
// set new serial numbers
|
||||
*(u32*)&FileBuffer[1].block[0x0158] = serial1;
|
||||
*(u32*)&FileBuffer[1].block[0x015C] = serial2;
|
||||
|
||||
// generate crc32 LUT
|
||||
for (i=0; i < 256; i++)
|
||||
{
|
||||
chksum = i;
|
||||
for (j=8; j > 0; j--)
|
||||
{
|
||||
if (chksum&1) chksum = (chksum>>1)^0xEDB88320;
|
||||
else chksum >>= 1;
|
||||
}
|
||||
|
||||
crc32LUT[i] = chksum;
|
||||
}
|
||||
|
||||
// PSO initial crc32 value
|
||||
chksum = 0xDEBB20E3;
|
||||
|
||||
// calc 32-bit checksum
|
||||
for (i=0x004C; i < 0x0164+pso3offset; i++)
|
||||
{
|
||||
chksum = ((chksum>>8)&0xFFFFFF)^crc32LUT[(chksum^FileBuffer[1].block[i])&0xFF];
|
||||
}
|
||||
|
||||
// set new checksum
|
||||
*(u32*)&FileBuffer[1].block[0x0048] = BE32(chksum^0xFFFFFFFF);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -116,8 +116,8 @@ private:
|
||||
// bits 0 and 1: image format
|
||||
// 00 no banner
|
||||
// 01 CI8 banner
|
||||
// 01 RGB5A3 banner
|
||||
// 11 ? maybe ==01? haven't seen it
|
||||
// 10 RGB5A3 banner
|
||||
// 11 ? maybe ==00? Time Splitters 2 and 3 have it and don't have banner
|
||||
//
|
||||
u8 Filename[DENTRY_STRLEN]; //0x08 0x20 filename
|
||||
u8 ModTime[4]; //0x28 0x04 Time of file's last modification in seconds since 12am, January 1st, 2000
|
||||
@@ -213,7 +213,7 @@ public:
|
||||
u32 DEntry_ModTime(u8 index) const;
|
||||
u32 DEntry_ImageOffset(u8 index) const;
|
||||
std::string DEntry_IconFmt(u8 index) const;
|
||||
u16 DEntry_AnimSpeed(u8 index) const;
|
||||
std::string DEntry_AnimSpeed(u8 index) const;
|
||||
std::string DEntry_Permissions(u8 index) const;
|
||||
u8 DEntry_CopyCounter(u8 index) const;
|
||||
// get first block for file
|
||||
@@ -256,6 +256,10 @@ public:
|
||||
|
||||
// reads the animation frames
|
||||
u32 ReadAnimRGBA8(u8 index, u32* buffer, u8 *delays) const;
|
||||
|
||||
void CARD_GetSerialNo(u32 *serial1,u32 *serial2);
|
||||
s32 FZEROGX_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer);
|
||||
s32 PSO_MakeSaveGameValid(DEntry& direntry, std::vector<GCMBlock> &FileBuffer);
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -116,9 +116,4 @@ namespace HW
|
||||
}
|
||||
p.DoMarker("WIIHW");
|
||||
}
|
||||
|
||||
void OnAfterLoad()
|
||||
{
|
||||
ExpansionInterface::OnAfterLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace HW
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -635,7 +635,8 @@ u8 *GetPointer(const u32 _Address)
|
||||
{
|
||||
case 0x0:
|
||||
case 0x8:
|
||||
return m_pPhysicalRAM + (_Address & RAM_MASK);
|
||||
if ((_Address & 0xfffffff) < REALRAM_SIZE)
|
||||
return m_pPhysicalRAM + (_Address & RAM_MASK);
|
||||
case 0xc:
|
||||
switch (_Address >> 24)
|
||||
{
|
||||
@@ -647,14 +648,16 @@ u8 *GetPointer(const u32 _Address)
|
||||
break;
|
||||
|
||||
default:
|
||||
return m_pPhysicalRAM + (_Address & RAM_MASK);
|
||||
if ((_Address & 0xfffffff) < REALRAM_SIZE)
|
||||
return m_pPhysicalRAM + (_Address & RAM_MASK);
|
||||
}
|
||||
|
||||
case 0x1:
|
||||
case 0x9:
|
||||
case 0xd:
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
|
||||
return m_pPhysicalEXRAM + (_Address & EXRAM_MASK);
|
||||
if ((_Address & 0xfffffff) < EXRAM_SIZE)
|
||||
return m_pPhysicalEXRAM + (_Address & EXRAM_MASK);
|
||||
else
|
||||
break;
|
||||
|
||||
@@ -675,7 +678,7 @@ u8 *GetPointer(const u32 _Address)
|
||||
}
|
||||
|
||||
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache)
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache, bool allow_fake_vmem)
|
||||
{
|
||||
switch ((addr >> 24) & 0xFC)
|
||||
{
|
||||
@@ -698,6 +701,11 @@ bool IsRAMAddress(const u32 addr, bool allow_locked_cache)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case 0x7C:
|
||||
if (allow_fake_vmem && bFakeVMEM && addr >= 0x7E000000)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ extern u8 *base;
|
||||
extern u8 *m_pRAM;
|
||||
extern u8 *m_pEXRAM;
|
||||
extern u8 *m_pL1Cache;
|
||||
extern u8 *m_pVirtualFakeVMEM;
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -102,7 +103,7 @@ void WriteUnchecked_U32(const u32 _Data, const u32 _Address);
|
||||
void InitHWMemFuncs();
|
||||
void InitHWMemFuncsWii();
|
||||
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache = false);
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache = false, bool allow_fake_vmem = false);
|
||||
writeFn32 GetHWWriteFun32(const u32 _Address);
|
||||
|
||||
inline u8* GetCachePtr() {return m_pL1Cache;}
|
||||
|
||||
@@ -229,7 +229,33 @@ static u8 g_SIBuffer[128];
|
||||
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
// p.DoArray(g_Channel);
|
||||
for(int i = 0; i < NUMBER_OF_CHANNELS; i++)
|
||||
{
|
||||
p.Do(g_Channel[i].m_InHi.Hex);
|
||||
p.Do(g_Channel[i].m_InLo.Hex);
|
||||
p.Do(g_Channel[i].m_Out.Hex);
|
||||
|
||||
ISIDevice* pDevice = g_Channel[i].m_pDevice;
|
||||
SIDevices type = pDevice->GetDeviceType();
|
||||
p.Do(type);
|
||||
ISIDevice* pSaveDevice = (type == pDevice->GetDeviceType()) ? pDevice : SIDevice_Create(type, i);
|
||||
pSaveDevice->DoState(p);
|
||||
if(pSaveDevice != pDevice)
|
||||
{
|
||||
// if we had to create a temporary device, discard it if we're not loading.
|
||||
// also, if no movie is active, we'll assume the user wants to keep their current devices
|
||||
// instead of the ones they had when the savestate was created.
|
||||
if(p.GetMode() != PointerWrap::MODE_READ ||
|
||||
(!Movie::IsRecordingInput() && !Movie::IsPlayingInput()))
|
||||
{
|
||||
delete pSaveDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddDevice(pSaveDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Do(g_Poll);
|
||||
p.Do(g_ComCSR);
|
||||
p.Do(g_StatusReg);
|
||||
@@ -536,15 +562,23 @@ void RemoveDevice(int _iDeviceNumber)
|
||||
g_Channel[_iDeviceNumber].m_pDevice = NULL;
|
||||
}
|
||||
|
||||
void AddDevice(const SIDevices _device, int _iDeviceNumber)
|
||||
void AddDevice(ISIDevice* pDevice)
|
||||
{
|
||||
int _iDeviceNumber = pDevice->GetDeviceNumber();
|
||||
|
||||
//_dbg_assert_(SERIALINTERFACE, _iDeviceNumber < NUMBER_OF_CHANNELS);
|
||||
|
||||
// delete the old device
|
||||
RemoveDevice(_iDeviceNumber);
|
||||
|
||||
// create the new one
|
||||
g_Channel[_iDeviceNumber].m_pDevice = SIDevice_Create(_device, _iDeviceNumber);
|
||||
g_Channel[_iDeviceNumber].m_pDevice = pDevice;
|
||||
}
|
||||
|
||||
void AddDevice(const SIDevices _device, int _iDeviceNumber)
|
||||
{
|
||||
ISIDevice* pDevice = SIDevice_Create(_device, _iDeviceNumber);
|
||||
AddDevice(pDevice);
|
||||
}
|
||||
|
||||
void SetNoResponse(u32 channel)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Common.h"
|
||||
#include "SI_Device.h"
|
||||
class PointerWrap;
|
||||
class ISIDevice;
|
||||
|
||||
namespace SerialInterface
|
||||
{
|
||||
@@ -32,6 +33,7 @@ void UpdateDevices();
|
||||
|
||||
void RemoveDevice(int _iDeviceNumber);
|
||||
void AddDevice(const SIDevices _device, int _iDeviceNumber);
|
||||
void AddDevice(ISIDevice* pDevice);
|
||||
|
||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate);
|
||||
void ChangeDevice(SIDevices device, int channel);
|
||||
|
||||
@@ -52,7 +52,7 @@ int ISIDevice::RunBuffer(u8* _pBuffer, int _iLength)
|
||||
class CSIDevice_Null : public ISIDevice
|
||||
{
|
||||
public:
|
||||
CSIDevice_Null(int _iDeviceNumber) : ISIDevice(_iDeviceNumber) {}
|
||||
CSIDevice_Null(SIDevices device, int _iDeviceNumber) : ISIDevice(device, _iDeviceNumber) {}
|
||||
virtual ~CSIDevice_Null() {}
|
||||
|
||||
int RunBuffer(u8* _pBuffer, int _iLength) {
|
||||
@@ -73,24 +73,24 @@ ISIDevice* SIDevice_Create(const SIDevices device, const int port_number)
|
||||
switch (device)
|
||||
{
|
||||
case SIDEVICE_GC_CONTROLLER:
|
||||
return new CSIDevice_GCController(port_number);
|
||||
return new CSIDevice_GCController(device, port_number);
|
||||
break;
|
||||
|
||||
case SIDEVICE_GC_TARUKONGA:
|
||||
return new CSIDevice_TaruKonga(port_number);
|
||||
return new CSIDevice_TaruKonga(device, port_number);
|
||||
break;
|
||||
|
||||
case SIDEVICE_GC_GBA:
|
||||
return new CSIDevice_GBA(port_number);
|
||||
return new CSIDevice_GBA(device, port_number);
|
||||
break;
|
||||
|
||||
case SIDEVICE_AM_BASEBOARD:
|
||||
return new CSIDevice_AMBaseboard(port_number);
|
||||
return new CSIDevice_AMBaseboard(device, port_number);
|
||||
break;
|
||||
|
||||
case SIDEVICE_NONE:
|
||||
default:
|
||||
return new CSIDevice_Null(port_number);
|
||||
return new CSIDevice_Null(device, port_number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
// Devices can reply with these
|
||||
#define SI_ERROR_NO_RESPONSE 0x0008 // Nothing is attached
|
||||
#define SI_ERROR_UNKNOWN 0x0040 // Unknown device is attached
|
||||
@@ -33,30 +35,6 @@
|
||||
#define SI_GC_NOMOTOR 0x20000000u // No rumble motor
|
||||
#define SI_GC_STANDARD 0x01000000u
|
||||
|
||||
class ISIDevice
|
||||
{
|
||||
protected:
|
||||
int m_iDeviceNumber;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
ISIDevice(int _iDeviceNumber) :
|
||||
m_iDeviceNumber(_iDeviceNumber)
|
||||
{}
|
||||
|
||||
// Destructor
|
||||
virtual ~ISIDevice() {}
|
||||
|
||||
// Run the SI Buffer
|
||||
virtual int RunBuffer(u8* _pBuffer, int _iLength);
|
||||
|
||||
// Return true on new data
|
||||
virtual bool GetData(u32& _Hi, u32& _Low) = 0;
|
||||
|
||||
// Send a command directly (no detour per buffer)
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll) = 0;
|
||||
};
|
||||
|
||||
// SI Device IDs for emulator use
|
||||
enum TSIDevices
|
||||
{
|
||||
@@ -88,6 +66,47 @@ enum SIDevices
|
||||
SIDEVICE_AM_BASEBOARD
|
||||
};
|
||||
|
||||
|
||||
class ISIDevice
|
||||
{
|
||||
protected:
|
||||
int m_iDeviceNumber;
|
||||
SIDevices m_deviceType;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
ISIDevice(SIDevices deviceType, int _iDeviceNumber)
|
||||
: m_iDeviceNumber(_iDeviceNumber)
|
||||
, m_deviceType(deviceType)
|
||||
{}
|
||||
|
||||
// Destructor
|
||||
virtual ~ISIDevice() {}
|
||||
|
||||
// Run the SI Buffer
|
||||
virtual int RunBuffer(u8* _pBuffer, int _iLength);
|
||||
|
||||
// Return true on new data
|
||||
virtual bool GetData(u32& _Hi, u32& _Low) = 0;
|
||||
|
||||
// Send a command directly (no detour per buffer)
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll) = 0;
|
||||
|
||||
// Savestate support
|
||||
virtual void DoState(PointerWrap& p) {}
|
||||
|
||||
|
||||
int GetDeviceNumber()
|
||||
{
|
||||
return m_iDeviceNumber;
|
||||
}
|
||||
|
||||
SIDevices GetDeviceType()
|
||||
{
|
||||
return m_deviceType;
|
||||
}
|
||||
};
|
||||
|
||||
extern ISIDevice* SIDevice_Create(const SIDevices device, const int port_number);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,8 +89,8 @@ public:
|
||||
|
||||
|
||||
// AM-Baseboard device on SI
|
||||
CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(int _iDeviceNumber)
|
||||
: ISIDevice(_iDeviceNumber)
|
||||
CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(SIDevices device, int _iDeviceNumber)
|
||||
: ISIDevice(device, _iDeviceNumber)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ private:
|
||||
|
||||
public:
|
||||
// constructor
|
||||
CSIDevice_AMBaseboard(int _iDeviceNumber);
|
||||
CSIDevice_AMBaseboard(SIDevices device, int _iDeviceNumber);
|
||||
|
||||
// run the SI Buffer
|
||||
virtual int RunBuffer(u8* _pBuffer, int _iLength);
|
||||
|
||||
@@ -135,8 +135,8 @@ void GBASockServer::Transfer(char* si_buffer)
|
||||
si_buffer[i ^ 3] = current_data[i];
|
||||
}
|
||||
|
||||
CSIDevice_GBA::CSIDevice_GBA(int _iDeviceNumber)
|
||||
: ISIDevice(_iDeviceNumber)
|
||||
CSIDevice_GBA::CSIDevice_GBA(SIDevices _device, int _iDeviceNumber)
|
||||
: ISIDevice(_device, _iDeviceNumber)
|
||||
, GBASockServer()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
class CSIDevice_GBA : public ISIDevice, private GBASockServer
|
||||
{
|
||||
public:
|
||||
CSIDevice_GBA(int _iDeviceNumber);
|
||||
CSIDevice_GBA(SIDevices device, int _iDeviceNumber);
|
||||
~CSIDevice_GBA() {}
|
||||
|
||||
// Run the SI Buffer
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
#include "../Core.h"
|
||||
|
||||
// --- standard gamecube controller ---
|
||||
CSIDevice_GCController::CSIDevice_GCController(int _iDeviceNumber)
|
||||
: ISIDevice(_iDeviceNumber)
|
||||
CSIDevice_GCController::CSIDevice_GCController(SIDevices device, int _iDeviceNumber)
|
||||
: ISIDevice(device, _iDeviceNumber)
|
||||
, m_TButtonComboStart(0)
|
||||
, m_TButtonCombo(0)
|
||||
, m_LastButtonCombo(COMBO_NONE)
|
||||
@@ -115,7 +115,7 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
||||
{
|
||||
SPADStatus PadStatus;
|
||||
memset(&PadStatus, 0, sizeof(PadStatus));
|
||||
|
||||
|
||||
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
|
||||
Movie::CallInputManip(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
|
||||
@@ -268,3 +268,13 @@ void CSIDevice_GCController::SendCommand(u32 _Cmd, u8 _Poll)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Savestate support
|
||||
void CSIDevice_GCController::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_Origin);
|
||||
p.Do(m_Mode);
|
||||
p.Do(m_TButtonComboStart);
|
||||
p.Do(m_TButtonCombo);
|
||||
p.Do(m_LastButtonCombo);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ private:
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
CSIDevice_GCController(int _iDeviceNumber);
|
||||
CSIDevice_GCController(SIDevices device, int _iDeviceNumber);
|
||||
|
||||
// Run the SI Buffer
|
||||
virtual int RunBuffer(u8* _pBuffer, int _iLength);
|
||||
@@ -109,6 +109,9 @@ public:
|
||||
|
||||
// Send a command directly
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll);
|
||||
|
||||
// Savestate support
|
||||
virtual void DoState(PointerWrap& p);
|
||||
};
|
||||
|
||||
|
||||
@@ -116,7 +119,7 @@ public:
|
||||
class CSIDevice_TaruKonga : public CSIDevice_GCController
|
||||
{
|
||||
public:
|
||||
CSIDevice_TaruKonga(int _iDeviceNumber) : CSIDevice_GCController(_iDeviceNumber) { }
|
||||
CSIDevice_TaruKonga(SIDevices device, int _iDeviceNumber) : CSIDevice_GCController(device, _iDeviceNumber) { }
|
||||
|
||||
virtual bool GetData(u32& _Hi, u32& _Low)
|
||||
{
|
||||
|
||||
@@ -725,9 +725,6 @@ void UpdateInterrupts()
|
||||
{
|
||||
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_VI, false);
|
||||
}
|
||||
|
||||
if (m_InterruptRegister[1].IR_INT && m_InterruptRegister[1].IR_MASK)
|
||||
State::ProcessRequestedStates(1);
|
||||
}
|
||||
|
||||
u32 GetXFBAddressTop()
|
||||
|
||||
@@ -84,7 +84,7 @@ static ipc_msg_queue reply_queue; // arm -> ppc
|
||||
void Init()
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_HLE, g_DeviceMap.empty(), "DeviceMap isnt empty on init");
|
||||
|
||||
CWII_IPC_HLE_Device_es::m_ContentFile = "";
|
||||
u32 i;
|
||||
for (i=0; i<IPC_MAX_FDS; i++)
|
||||
{
|
||||
@@ -576,3 +576,12 @@ void UpdateDevices()
|
||||
|
||||
|
||||
} // end of namespace WII_IPC_HLE_Interface
|
||||
|
||||
// TODO: create WII_IPC_HLE_Device.cpp ?
|
||||
void IWII_IPC_HLE_Device::DoStateShared(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_Name);
|
||||
p.Do(m_DeviceID);
|
||||
p.Do(m_Hardware);
|
||||
p.Do(m_Active);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ public:
|
||||
|
||||
virtual ~IWII_IPC_HLE_Device() { }
|
||||
|
||||
virtual void DoState(PointerWrap&) { }
|
||||
virtual void DoState(PointerWrap& p) { DoStateShared(p); }
|
||||
|
||||
void DoStateShared(PointerWrap& p);
|
||||
|
||||
const std::string& GetDeviceName() const { return m_Name; }
|
||||
u32 GetDeviceID() const { return m_DeviceID; }
|
||||
|
||||
@@ -363,7 +363,30 @@ bool CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress)
|
||||
|
||||
void CWII_IPC_HLE_Device_FileIO::DoState(PointerWrap &p)
|
||||
{
|
||||
DoStateShared(p);
|
||||
|
||||
bool have_file_handle = (m_pFileHandle != 0);
|
||||
s32 seek = (have_file_handle) ? (s32)m_pFileHandle.Tell() : 0;
|
||||
|
||||
p.Do(have_file_handle);
|
||||
p.Do(m_Mode);
|
||||
p.Do(m_SeekPos);
|
||||
p.Do(m_Filename);
|
||||
p.Do(seek);
|
||||
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
int mode = m_Mode;
|
||||
bool active = m_Active;
|
||||
if (have_file_handle)
|
||||
{
|
||||
Open(0, m_Mode);
|
||||
_dbg_assert_msg_(WII_IPC_HLE, m_pFileHandle, "bad filehandle");
|
||||
}
|
||||
else
|
||||
Close(0, true);
|
||||
m_Mode = mode;
|
||||
m_Active = active;
|
||||
}
|
||||
|
||||
if (have_file_handle)
|
||||
m_pFileHandle.Seek(seek, SEEK_SET);
|
||||
}
|
||||
|
||||
@@ -266,8 +266,7 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
||||
|
||||
m_ContentAccessMap[CFD].m_Position = 0;
|
||||
m_ContentAccessMap[CFD].m_pContent = AccessContentDevice(m_TitleID).GetContentByIndex(Index);
|
||||
_dbg_assert_(WII_IPC_ES, m_ContentAccessMap[CFD].m_pContent != NULL);
|
||||
|
||||
|
||||
if (m_ContentAccessMap[CFD].m_pContent == NULL)
|
||||
CFD = 0xffffffff; //TODO: what is the correct error value here?
|
||||
|
||||
@@ -784,6 +783,7 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string tContentFile(m_ContentFile.c_str());
|
||||
WII_IPC_HLE_Interface::Reset(true);
|
||||
WII_IPC_HLE_Interface::Init();
|
||||
|
||||
@@ -800,7 +800,8 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
||||
s_Usb->m_WiiMotes[i].Activate(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WII_IPC_HLE_Interface::SetDefaultContentFile(tContentFile);
|
||||
}
|
||||
// Pass the "#002 check"
|
||||
// Apploader should write the IOS version and revision to 0x3140, and compare it
|
||||
|
||||
@@ -38,6 +38,9 @@ public:
|
||||
|
||||
virtual bool IOCtlV(u32 _CommandAddress);
|
||||
static u32 ES_DIVerify(u8 *_pTMD, u32 _sz);
|
||||
|
||||
// This should only be cleared on power reset
|
||||
static std::string m_ContentFile;
|
||||
private:
|
||||
|
||||
enum
|
||||
@@ -134,8 +137,6 @@ private:
|
||||
std::vector<u64> m_TitleIDs;
|
||||
u64 m_TitleID;
|
||||
u32 AccessIdentID;
|
||||
// This should only be cleared on power reset
|
||||
static std::string m_ContentFile;
|
||||
|
||||
u64 GetCurrentTitleID() const;
|
||||
|
||||
|
||||
@@ -501,6 +501,8 @@ s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _B
|
||||
|
||||
void CWII_IPC_HLE_Device_fs::DoState(PointerWrap& p)
|
||||
{
|
||||
DoStateShared(p);
|
||||
|
||||
// handle /tmp
|
||||
|
||||
std::string Path = File::GetUserPath(D_WIIUSER_IDX) + "tmp";
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "WII_IPC_HLE.h"
|
||||
#include "WII_IPC_HLE_Device_usb.h"
|
||||
#include "../ConfigManager.h"
|
||||
#include "../Movie.h"
|
||||
#include "CoreTiming.h"
|
||||
|
||||
// The device class
|
||||
@@ -104,7 +105,7 @@ CWII_IPC_HLE_Device_usb_oh1_57e_305::~CWII_IPC_HLE_Device_usb_oh1_57e_305()
|
||||
void CWII_IPC_HLE_Device_usb_oh1_57e_305::DoState(PointerWrap &p)
|
||||
{
|
||||
/*
|
||||
//things that do not get saved:
|
||||
//things that do not get saved: (why not?)
|
||||
|
||||
std::vector<CWII_IPC_HLE_WiiMote> m_WiiMotes;
|
||||
|
||||
@@ -120,26 +121,73 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::DoState(PointerWrap &p)
|
||||
p.Do(m_ScanEnable);
|
||||
m_acl_pool.DoState(p);
|
||||
|
||||
if (p.GetMode() == PointerWrap::MODE_READ) {
|
||||
m_EventQueue.clear();
|
||||
}
|
||||
if (p.GetMode() == PointerWrap::MODE_READ &&
|
||||
SConfig::GetInstance().m_WiimoteReconnectOnLoad)
|
||||
bool storeFullData = (Movie::IsRecordingInput() || Movie::IsPlayingInput());
|
||||
p.Do(storeFullData);
|
||||
p.DoMarker("storeFullData in CWII_IPC_HLE_Device_usb_oh1_57e_305");
|
||||
|
||||
if (!storeFullData)
|
||||
{
|
||||
// Reset the connection of all connected wiimotes
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!m_WiiMotes[i].IsInactive())
|
||||
{
|
||||
m_WiiMotes[i].Activate(false);
|
||||
m_WiiMotes[i].Activate(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_WiiMotes[i].Activate(false);
|
||||
}
|
||||
}
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
m_EventQueue.clear();
|
||||
|
||||
if (SConfig::GetInstance().m_WiimoteReconnectOnLoad)
|
||||
{
|
||||
// Reset the connection of all connected wiimotes
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!m_WiiMotes[i].IsInactive())
|
||||
{
|
||||
m_WiiMotes[i].Activate(false);
|
||||
m_WiiMotes[i].Activate(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_WiiMotes[i].Activate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// I'm not sure why these things aren't normally saved, but I think they can affect the emulation state,
|
||||
// so if sync matters (e.g. if a movie is active), we really should save them.
|
||||
// also, it's definitely not safe to do the above auto-reconnect hack either.
|
||||
// (unless we can do it without changing anything that affects emulation state, which is not currently the case)
|
||||
|
||||
p.Do(m_EventQueue);
|
||||
p.DoMarker("m_EventQueue");
|
||||
|
||||
// m_WiiMotes is kind of annoying to save. maybe this could be done in a more general way.
|
||||
u32 vec_size = (u32)m_WiiMotes.size();
|
||||
p.Do(vec_size);
|
||||
for (u32 i = 0; i < vec_size; ++i)
|
||||
{
|
||||
if (i < m_WiiMotes.size())
|
||||
{
|
||||
CWII_IPC_HLE_WiiMote& wiimote = m_WiiMotes[i];
|
||||
wiimote.DoState(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
bdaddr_t tmpBD = BDADDR_ANY;
|
||||
CWII_IPC_HLE_WiiMote wiimote = CWII_IPC_HLE_WiiMote(this, i, tmpBD, false);
|
||||
wiimote.DoState(p);
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
m_WiiMotes.push_back(wiimote);
|
||||
_dbg_assert_(WII_IPC_WIIMOTE, m_WiiMotes.size() == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
while ((u32)m_WiiMotes.size() > vec_size)
|
||||
m_WiiMotes.pop_back();
|
||||
p.DoMarker("m_WiiMotes");
|
||||
}
|
||||
|
||||
DoStateShared(p);
|
||||
}
|
||||
|
||||
bool CWII_IPC_HLE_Device_usb_oh1_57e_305::RemoteDisconnect(u16 _connectionHandle)
|
||||
@@ -1881,4 +1929,4 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::LOG_LinkKey(const u8* _pLinkKey)
|
||||
, _pLinkKey[0], _pLinkKey[1], _pLinkKey[2], _pLinkKey[3], _pLinkKey[4], _pLinkKey[5], _pLinkKey[6], _pLinkKey[7]
|
||||
, _pLinkKey[8], _pLinkKey[9], _pLinkKey[10], _pLinkKey[11], _pLinkKey[12], _pLinkKey[13], _pLinkKey[14], _pLinkKey[15]);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,12 @@ struct SQueuedEvent
|
||||
}
|
||||
memset(m_buffer, 0, 1024);
|
||||
}
|
||||
|
||||
SQueuedEvent()
|
||||
: m_size(0)
|
||||
, m_connectionHandle(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user