Core: Fixed for memory mappings, implemented multiplication register, added logging to help debugging missing functionalities

This commit is contained in:
Sour 2019-02-17 14:42:35 -05:00
parent 0757ccefa6
commit 93e8fd9d5e
22 changed files with 646 additions and 102 deletions

128
Core/BaseCartridge.cpp Normal file
View file

@ -0,0 +1,128 @@
#include "stdafx.h"
#include "BaseCartridge.h"
#include "RamHandler.h"
#include "RomHandler.h"
#include "MemoryManager.h"
#include "IMemoryHandler.h"
#include "../Utilities/VirtualFile.h"
BaseCartridge::~BaseCartridge()
{
delete[] _prgRom;
delete[] _saveRam;
}
shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(VirtualFile &romFile, VirtualFile &patchFile)
{
if(romFile.IsValid()) {
vector<uint8_t> romData;
romFile.ReadFile(romData);
shared_ptr<BaseCartridge> cart(new BaseCartridge());
cart->_prgRomSize = (uint32_t)romData.size();
cart->_prgRom = new uint8_t[cart->_prgRomSize];
memcpy(cart->_prgRom, romData.data(), cart->_prgRomSize);
cart->Init();
return cart;
} else {
return nullptr;
}
}
void BaseCartridge::Init()
{
uint32_t headerOffset = 0;
if(((0x400 << _prgRom[0x7FD7]) == _prgRomSize && (_prgRom[0x7FD5] & 0x20)) || _prgRomSize < 0xFFFF) {
//LoROM
headerOffset = 0x7FB0;
} else if((0x400 << _prgRom[0xFFD7]) == _prgRomSize && (_prgRom[0xFFD5] & 0x20)) {
//HiROM
headerOffset = 0xFFB0;
} else if(_prgRom[0x7FD5] & 0x20) {
//LoROM
headerOffset = 0x7FB0;
} else if(_prgRom[0xFFD5] & 0x20) {
//HiROM
headerOffset = 0xFFB0;
} else {
throw new std::runtime_error("invalid rom (?)");
}
_cartInfo = *(SnesCartInformation*)(&_prgRom[headerOffset]);
_saveRamSize = _cartInfo.SramSize > 0 ? 1024 * (1 << _cartInfo.SramSize) : 0;
_saveRam = new uint8_t[_saveRamSize];
}
CartFlags::CartFlags BaseCartridge::GetCartFlags()
{
uint32_t flags = 0;
if(_cartInfo.MapMode & 0x04) {
flags |= CartFlags::ExHiRom;
} else if(_cartInfo.MapMode & 0x02) {
flags |= CartFlags::ExLoRom;
} else if(_cartInfo.MapMode & 0x01) {
flags |= CartFlags::HiRom;
} else {
flags |= CartFlags::LoRom;
}
if(_cartInfo.MapMode & 0x10) {
flags |= CartFlags::FastRom;
}
return (CartFlags::CartFlags)flags;
}
void BaseCartridge::MapBanks(MemoryManager &mm, vector<unique_ptr<IMemoryHandler>> &handlers, uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage, uint16_t pageIncrement, bool mirror)
{
if(handlers.empty()) {
return;
}
uint32_t pageNumber = 0;
for(uint32_t i = startBank; i <= endBank; i++) {
uint32_t baseAddress = i << 16;
pageNumber += pageIncrement;
for(uint32_t j = startPage; j <= endPage; j++) {
mm.RegisterHandler(baseAddress + (j * 0x1000), baseAddress + (j * 0x1000) | 0xFFF, handlers[pageNumber].get());
//MessageManager::Log("Map [$" + HexUtilities::ToHex(i) + ":" + HexUtilities::ToHex(j)[1] + "xxx] to page number " + HexUtilities::ToHex(pageNumber));
pageNumber++;
if(pageNumber >= handlers.size()) {
if(mirror) {
pageNumber = 0;
} else {
return;
}
}
}
}
}
void BaseCartridge::RegisterHandlers(MemoryManager &mm)
{
for(uint32_t i = 0; i < _prgRomSize; i += 0x1000) {
_prgRomHandlers.push_back(unique_ptr<RomHandler>(new RomHandler(_prgRom + i)));
}
for(uint32_t i = 0; i < _saveRamSize; i += 0x1000) {
_saveRamHandlers.push_back(unique_ptr<RamHandler>(new RamHandler(_saveRam + i)));
}
if(GetCartFlags() & CartFlags::LoRom) {
MapBanks(mm, _prgRomHandlers, 0x00, 0x6F, 0x08, 0x0F, 0, true);
MapBanks(mm, _saveRamHandlers, 0x70, 0x7D, 0x00, 0x0F, 0, true);
MapBanks(mm, _prgRomHandlers, 0x80, 0xEF, 0x08, 0x0F, 0, true);
MapBanks(mm, _saveRamHandlers, 0xF0, 0xFF, 0x00, 0x0F, 0, true);
} else {
MapBanks(mm, _prgRomHandlers, 0x40, 0x7D, 0x00, 0x0F, 0, true);
MapBanks(mm, _prgRomHandlers, 0xC0, 0xFF, 0x00, 0x0F, 0, true);
MapBanks(mm, _prgRomHandlers, 0x00, 0x3F, 0x08, 0x0F, 8, true);
MapBanks(mm, _prgRomHandlers, 0x80, 0xBF, 0x08, 0x0F, 8, true);
MapBanks(mm, _saveRamHandlers, 0x20, 0x3F, 0x06, 0x07, 0, true);
}
}

View file

@ -1,50 +1,72 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "../Utilities/VirtualFile.h"
class BaseCartridge : public IMemoryHandler
class MemoryManager;
class VirtualFile;
struct SnesCartInformation
{
uint8_t MakerCode[2];
uint8_t GameCode[4];
uint8_t Reserved[7];
uint8_t ExpansionRamSize;
uint8_t SpecialVersion;
uint8_t CartridgeType;
char CartName[21];
uint8_t MapMode;
uint8_t RomType;
uint8_t RomSize;
uint8_t SramSize;
uint8_t DestinationCode;
uint8_t Reserved2;
uint8_t Version;
uint8_t ChecksumComplement[2];
uint8_t Checksum[2];
};
namespace CartFlags
{
enum CartFlags
{
LoRom = 1,
HiRom = 2,
FastRom = 4,
ExLoRom = 8,
ExHiRom = 16
};
}
class BaseCartridge
{
private:
vector<unique_ptr<IMemoryHandler>> _prgRomHandlers;
vector<unique_ptr<IMemoryHandler>> _saveRamHandlers;
SnesCartInformation _cartInfo;
uint8_t* _prgRom = nullptr;
uint8_t* _saveRam = nullptr;
uint32_t _prgRomSize = 0;
uint32_t _saveRamSize = 0;
bool _isHiRom = false;
void MapBanks(MemoryManager &mm, vector<unique_ptr<IMemoryHandler>> &handlers, uint8_t startBank, uint8_t endBank, uint16_t startPage = 0, uint16_t endPage = 0x0F, uint16_t pageIncrement = 0, bool mirror = false);
public:
~BaseCartridge()
{
delete[] _prgRom;
delete[] _saveRam;
}
~BaseCartridge();
static shared_ptr<BaseCartridge> CreateCartridge(VirtualFile romFile, VirtualFile patchFile)
{
if(romFile.IsValid()) {
vector<uint8_t> romData;
romFile.ReadFile(romData);
static shared_ptr<BaseCartridge> CreateCartridge(VirtualFile &romFile, VirtualFile &patchFile);
shared_ptr<BaseCartridge> cart(new BaseCartridge());
cart->_prgRomSize = (uint32_t)romData.size();
cart->_prgRom = new uint8_t[cart->_prgRomSize];
memcpy(cart->_prgRom, romData.data(), cart->_prgRomSize);
void Init();
return cart;
} else {
return nullptr;
}
}
CartFlags::CartFlags GetCartFlags();
uint8_t Read(uint32_t addr) override
{
uint8_t bank = (addr >> 16) & 0x7F;
return _prgRom[((bank * 0x8000) | (addr & 0x7FFF)) & (_prgRomSize - 1)];
}
void Write(uint32_t addr, uint8_t value) override
{
}
void RegisterHandlers(MemoryManager &mm);
uint8_t* DebugGetPrgRom() { return _prgRom; }
uint8_t* DebugGetSaveRam() { return _saveRam; }

View file

@ -10,6 +10,7 @@
#include "VideoDecoder.h"
#include "VideoRenderer.h"
#include "DebugHud.h"
#include "MessageManager.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
@ -82,6 +83,8 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
shared_ptr<BaseCartridge> cart = BaseCartridge::CreateCartridge(romFile, patchFile);
if(cart) {
MessageManager::ClearLog();
_ppu.reset(new Ppu(shared_from_this()));
_spc.reset(new Spc(shared_from_this()));
_cart = cart;

View file

@ -82,6 +82,8 @@
<ClInclude Include="NotificationManager.h" />
<ClInclude Include="Ppu.h" />
<ClInclude Include="PpuTypes.h" />
<ClInclude Include="RamHandler.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="SNES_SPC.h" />
<ClInclude Include="SoundMixer.h" />
@ -95,6 +97,7 @@
<ClInclude Include="VideoRenderer.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BaseCartridge.cpp" />
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="BaseSoundManager.cpp" />
<ClCompile Include="BaseVideoFilter.cpp" />

View file

@ -149,6 +149,12 @@
<ClInclude Include="SoundMixer.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="RomHandler.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="RamHandler.h">
<Filter>SNES</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -230,6 +236,9 @@
<ClCompile Include="SoundMixer.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="BaseCartridge.cpp">
<Filter>SNES</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -21,7 +21,7 @@ DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager)
_opSize = GetOperandSize() + 1;
for(int i = 1; i < _opSize; i++) {
_byteCode[i] = memoryManager->Peek(addr+i);
_byteCode[i] = memoryManager->Peek((addr+i) & 0xFFFFFF);
}
_emulationMode = state.EmulationMode;

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "DmaController.h"
#include "MemoryManager.h"
#include "MessageManager.h"
DmaController::DmaController(MemoryManager *memoryManager)
{
@ -37,6 +38,8 @@ void DmaController::RunDma(DmaChannelConfig &channel)
//"Note, however, that writing $0000 to this register actually results in a transfer of $10000 bytes, not 0."
uint32_t bytesLeft = channel.TransferSize ? channel.TransferSize : 0x10000;
MessageManager::Log("Run DMA: " + HexUtilities::ToHex(channel.DestAddress) + " -> " + HexUtilities::ToHex(channel.SrcAddress) + " Bytes: " + std::to_string(bytesLeft));
while(bytesLeft > 0) {
//Manual DMA transfers run to the end of the transfer when started
RunSingleTransfer(channel, bytesLeft);
@ -55,6 +58,13 @@ void DmaController::Write(uint16_t addr, uint8_t value)
}
break;
case 0x420C:
//HDMAEN - HDMA Enable
if(value > 0) {
MessageManager::DisplayMessage("Debug", "Unsupported HDMA operation");
}
break;
case 0x4300: case 0x4310: case 0x4320: case 0x4330: case 0x4340: case 0x4350: case 0x4360: case 0x4370:
{
//DMAPx - DMA Control for Channel x

View file

@ -3,9 +3,11 @@
#include "Console.h"
#include "Ppu.h"
#include "Spc.h"
#include "RamHandler.h"
#include "DmaController.h"
#include "BaseCartridge.h"
#include "IMemoryHandler.h"
#include "MessageManager.h"
#include "../Utilities/HexUtilities.h"
class CpuRegisterHandler : public IMemoryHandler
@ -14,13 +16,18 @@ private:
Ppu *_ppu;
Spc *_spc;
DmaController *_dmaController;
uint8_t *_workRam;
uint32_t _wramPosition;
public:
CpuRegisterHandler(Ppu *ppu, Spc *spc, DmaController *dmaController)
CpuRegisterHandler(Ppu *ppu, Spc *spc, DmaController *dmaController, uint8_t *workRam)
{
_ppu = ppu;
_spc = spc;
_dmaController = dmaController;
_workRam = workRam;
_wramPosition = 0;
}
uint8_t Read(uint32_t addr) override
@ -38,35 +45,25 @@ public:
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Write(addr & 0x03, value);
} if(addr >= 0x2180 && addr <= 0x2183) {
switch(addr & 0xFFFF) {
case 0x2180:
_workRam[_wramPosition] = value;
_wramPosition = (_wramPosition + 1) & (0x1FFFF);
break;
case 0x2181: _wramPosition = (_wramPosition & 0x1FF00) | value; break;
case 0x2182: _wramPosition = (_wramPosition & 0x100FF) | (value << 8); break;
case 0x2183: _wramPosition = (_wramPosition & 0xFFFF) | ((value & 0x01) << 16); break;
}
} else if(addr == 0x420B || addr == 0x420C || addr >= 0x4300) {
_dmaController->Write(addr, value);
} else {
_ppu->Write(addr, value);
_dmaController->Write(addr, value);
}
}
};
class WorkRamHandler : public IMemoryHandler
{
private:
uint8_t *_workRam;
public:
WorkRamHandler(uint8_t *workRam)
{
_workRam = workRam;
}
uint8_t Read(uint32_t addr) override
{
return _workRam[addr & 0xFFF];
}
void Write(uint32_t addr, uint8_t value) override
{
_workRam[addr & 0xFFF] = value;
}
};
class MemoryManager
{
public:
@ -75,15 +72,15 @@ public:
private:
shared_ptr<Console> _console;
uint8_t * _workRam;
IMemoryHandler* _handlers[0x100 * 0x10];
vector<unique_ptr<WorkRamHandler>> _workRamHandlers;
shared_ptr<BaseCartridge> _cart;
shared_ptr<CpuRegisterHandler> _cpuRegisterHandler;
shared_ptr<Ppu> _ppu;
shared_ptr<DmaController> _dmaController;
uint32_t _wramPosition;
IMemoryHandler* _handlers[0x100 * 0x10];
vector<unique_ptr<RamHandler>> _workRamHandlers;
uint8_t * _workRam;
uint64_t _masterClock;
uint64_t _lastMasterClock;
@ -97,15 +94,16 @@ public:
_cart = console->GetCartridge();
_ppu = console->GetPpu();
_workRam = new uint8_t[MemoryManager::WorkRamSize];
_dmaController.reset(new DmaController(console->GetMemoryManager().get()));
_cpuRegisterHandler.reset(new CpuRegisterHandler(_ppu.get(), console->GetSpc().get(), _dmaController.get()));
_cpuRegisterHandler.reset(new CpuRegisterHandler(_ppu.get(), console->GetSpc().get(), _dmaController.get(), _workRam));
memset(_handlers, 0, sizeof(_handlers));
_workRam = new uint8_t[MemoryManager::WorkRamSize];
//memset(_workRam, 0, 128 * 1024);
for(uint32_t i = 0; i < 128 * 1024; i += 0x1000) {
_workRamHandlers.push_back(unique_ptr<WorkRamHandler>(new WorkRamHandler(_workRam + i)));
_workRamHandlers.push_back(unique_ptr<RamHandler>(new RamHandler(_workRam + i)));
RegisterHandler(0x7E0000 | i, 0x7E0000 | (i + 0xFFF), _workRamHandlers[_workRamHandlers.size() - 1].get());
}
@ -117,13 +115,17 @@ public:
RegisterHandler(((i | 0x80) << 16) | 0x4000, ((i | 0x80) << 16) | 0x4FFF, _cpuRegisterHandler.get());
}
RegisterHandler(0x0000, 0x0FFF, _workRamHandlers[0].get());
RegisterHandler(0x1000, 0x1FFF, _workRamHandlers[1].get());
for(int bank = 0; bank < 0x20; bank++) {
RegisterHandler((bank << 16) | 0x8000, (bank << 16) | 0xFFFF, _cart.get());
RegisterHandler(((0x80 | bank) << 16) | 0x8000, ((0x80 | bank) << 16) | 0xFFFF, _cart.get());
for(int i = 0; i < 0x3F; i++) {
RegisterHandler((i << 16) | 0x0000, (i << 16) | 0x0FFF, _workRamHandlers[0].get());
RegisterHandler((i << 16) | 0x1000, (i << 16) | 0x1FFF, _workRamHandlers[1].get());
}
for(int i = 0x80; i < 0xBF; i++) {
RegisterHandler((i << 16) | 0x0000, (i << 16) | 0x0FFF, _workRamHandlers[0].get());
RegisterHandler((i << 16) | 0x1000, (i << 16) | 0x1FFF, _workRamHandlers[1].get());
}
_cart->RegisterHandlers(*this);
}
~MemoryManager()
@ -138,6 +140,10 @@ public:
}
for(uint32_t addr = startAddr; addr < endAddr; addr += 0x1000) {
if(_handlers[addr >> 12]) {
throw new std::runtime_error("handler already set");
}
_handlers[addr >> 12] = handler;
}
}
@ -191,7 +197,10 @@ public:
if(_handlers[addr >> 12]) {
value = _handlers[addr >> 12]->Read(addr);
} else {
//std::cout << "Read - missing handler: $" << HexUtilities::ToHex(addr) << std::endl;
//open bus
value = (addr>> 12);
MessageManager::DisplayMessage("Debug", "Read - missing handler: $" + HexUtilities::ToHex(addr));
}
_console->ProcessCpuRead(addr, value, type);
return value;
@ -203,8 +212,6 @@ public:
uint8_t value = 0;
if(_handlers[addr >> 12]) {
value = _handlers[addr >> 12]->Read(addr);
} else {
//std::cout << "Read - missing handler: $" << HexUtilities::ToHex(addr) << std::endl;
}
return value;
}
@ -213,30 +220,11 @@ public:
{
IncrementMasterClock(addr);
switch(addr & 0xFFFF) {
case 0x2180:
_workRam[_wramPosition] = value;
_wramPosition++;
break;
case 0x2181:
_wramPosition = (_wramPosition & 0x1FF00) | value;
break;
case 0x2182:
_wramPosition = (_wramPosition & 0x100FF) | (value << 8);
break;
case 0x2183:
_wramPosition = (_wramPosition & 0xFFFF) | ((value & 0x01) << 16);
break;
}
_console->ProcessCpuWrite(addr, value, type);
if(_handlers[addr >> 12]) {
return _handlers[addr >> 12]->Write(addr, value);
} else {
//std::cout << "Write - missing handler: $" << HexUtilities::ToHex(addr) << " = " << HexUtilities::ToHex(value) << std::endl;
MessageManager::DisplayMessage("Debug", "Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
}

View file

@ -172,6 +172,12 @@ void MessageManager::Log(string message)
#endif
}
void MessageManager::ClearLog()
{
auto lock = _logLock.AcquireSafe();
_log.clear();
}
string MessageManager::GetLog()
{
auto lock = _logLock.AcquireSafe();

View file

@ -27,5 +27,6 @@ public:
static void DisplayMessage(string title, string message, string param1 = "", string param2 = "");
static void Log(string message = "");
static void ClearLog();
static string GetLog();
};

View file

@ -192,6 +192,13 @@ uint8_t Ppu::Read(uint16_t addr)
(_scanline >= 225 ? 0x80 : 0) |
((_cycle >= 0x121 || _cycle <= 0x15) ? 0x40 : 0)
);
case 0x4216: return (uint8_t)_multResult;
case 0x4217: return (uint8_t)(_multResult >> 8);
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register read: " + HexUtilities::ToHex(addr));
break;
}
return 0;
@ -284,10 +291,20 @@ void Ppu::Write(uint32_t addr, uint8_t value)
//_autoJoypadRead = (value & 0x01) != 0;
break;
case 0x4202: _multOperand1 = value; break;
case 0x4203:
_multOperand2 = value;
_multResult = _multOperand1 * _multOperand2;
break;
case 0x4207: _horizontalTimer = (_horizontalTimer & 0x100) | value; break;
case 0x4208: _horizontalTimer = (_horizontalTimer & 0xFF) | ((value & 0x01) << 8); break;
case 0x4209: _verticalTimer = (_verticalTimer & 0x100) | value; break;
case 0x420A: _verticalTimer = (_verticalTimer & 0xFF) | ((value & 0x01) << 8); break;
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register write: " + HexUtilities::ToHex(addr));
break;
}
}

View file

@ -35,6 +35,10 @@ private:
uint8_t _vramIncrementValue;
uint8_t _vramAddressRemapping;
bool _vramAddrIncrementOnSecondReg;
uint8_t _multOperand1 = 0;
uint8_t _multOperand2 = 0;
uint16_t _multResult = 0;
uint16_t _cgramAddress;
uint8_t _cgram[Ppu::CgRamSize];

25
Core/RamHandler.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
class RamHandler : public IMemoryHandler
{
private:
uint8_t * _ram;
public:
RamHandler(uint8_t *ram)
{
_ram = ram;
}
uint8_t Read(uint32_t addr) override
{
return _ram[addr & 0xFFF];
}
void Write(uint32_t addr, uint8_t value) override
{
_ram[addr & 0xFFF] = value;
}
};

24
Core/RomHandler.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
class RomHandler : public IMemoryHandler
{
private:
uint8_t * _rom;
public:
RomHandler(uint8_t *rom)
{
_rom = rom;
}
uint8_t Read(uint32_t addr) override
{
return _rom[addr & 0xFFF];
}
void Write(uint32_t addr, uint8_t value) override
{
}
};

View file

@ -284,14 +284,6 @@ void TraceLogger::AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state)
_logCount++;
}
if(_logToFile) {
GetTraceRow(_outputBuffer, state.Cpu, state.Ppu, disassemblyInfo);
if(_outputBuffer.size() > 32768) {
_outputFile << _outputBuffer;
_outputBuffer.clear();
}
}
_currentPos = (_currentPos + 1) % ExecutionLogSize;
}
/*
@ -307,10 +299,20 @@ void TraceLogger::LogNonExec(OperationInfo& operationInfo)
void TraceLogger::LogEffectiveAddress(uint32_t effectiveAddress)
{
uint32_t pos;
if(_currentPos > 0) {
_disassemblyCache[_currentPos - 1].SetEffectiveAddress(effectiveAddress);
pos = _currentPos - 1;
} else {
_disassemblyCache[ExecutionLogSize - 1].SetEffectiveAddress(effectiveAddress);
pos = ExecutionLogSize - 1;
}
_disassemblyCache[pos].SetEffectiveAddress(effectiveAddress);
if(_logToFile) {
GetTraceRow(_outputBuffer, _cpuStateCache[pos], _ppuStateCache[pos], _disassemblyCache[pos]);
if(_outputBuffer.size() > 32768) {
_outputFile << _outputBuffer;
_outputBuffer.clear();
}
}
}

View file

@ -44,7 +44,7 @@ void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
size.Scale = scale;*/
if(true || ignoreScale) {
if(ignoreScale) {
size.Width = 256;
size.Height = 224;
} else {

54
UI/Forms/frmLogWindow.cs Normal file
View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Forms
{
public partial class frmLogWindow : BaseForm
{
private string _currentLog;
public frmLogWindow()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
UpdateLog(EmuApi.GetLog());
}
private void UpdateLog(string log)
{
_currentLog = log;
txtLog.Text = _currentLog;
txtLog.SelectionLength = 0;
txtLog.SelectionStart = txtLog.Text.Length;
txtLog.ScrollToCaret();
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void tmrRefresh_Tick(object sender, EventArgs e)
{
string newLog = EmuApi.GetLog();
if(_currentLog != newLog) {
UpdateLog(newLog);
}
}
}
}

108
UI/Forms/frmLogWindow.designer.cs generated Normal file
View file

@ -0,0 +1,108 @@
namespace Mesen.GUI.Forms
{
partial class frmLogWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.btnClose = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.tmrRefresh = new System.Windows.Forms.Timer(this.components);
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.Controls.Add(this.txtLog, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.btnClose, 1, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(480, 377);
this.tableLayoutPanel1.TabIndex = 0;
//
// btnClose
//
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnClose.Location = new System.Drawing.Point(407, 349);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(70, 25);
this.btnClose.TabIndex = 0;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// txtLog
//
this.txtLog.BackColor = System.Drawing.Color.White;
this.tableLayoutPanel1.SetColumnSpan(this.txtLog, 2);
this.txtLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtLog.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txtLog.Location = new System.Drawing.Point(3, 3);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtLog.Size = new System.Drawing.Size(474, 340);
this.txtLog.TabIndex = 1;
//
// tmrRefresh
//
this.tmrRefresh.Enabled = true;
this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick);
//
// frmLogWindow
//
this.AcceptButton = this.btnClose;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnClose;
this.ClientSize = new System.Drawing.Size(480, 377);
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "frmLogWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Log Window";
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.TextBox txtLog;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Timer tmrRefresh;
}
}

126
UI/Forms/frmLogWindow.resx Normal file
View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="tmrRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
</root>

View file

@ -32,6 +32,8 @@ namespace Mesen.GUI.Forms
_notifListener = new NotificationListener();
_notifListener.OnNotification += OnNotificationReceived;
new frmLogWindow().Show();
}
protected override void OnFormClosing(FormClosingEventArgs e)

View file

@ -39,5 +39,8 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void SetMousePosition(double x, double y);
[DllImport(DllPath)] public static extern void SetDisplayLanguage(Language lang);
[DllImport(DllPath, EntryPoint = "GetLog")] private static extern IntPtr GetLogWrapper();
public static string GetLog() { return Utf8Marshaler.PtrToStringUtf8(EmuApi.GetLogWrapper()).Replace("\n", Environment.NewLine); }
}
}

View file

@ -385,6 +385,12 @@
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\EntityBinder.cs" />
<Compile Include="Forms\frmLogWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\frmLogWindow.designer.cs">
<DependentUpon>frmLogWindow.cs</DependentUpon>
</Compile>
<Compile Include="Forms\frmMain.cs">
<SubType>Form</SubType>
</Compile>
@ -467,6 +473,9 @@
<EmbeddedResource Include="Forms\BaseInputForm.resx">
<DependentUpon>BaseInputForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\frmLogWindow.resx">
<DependentUpon>frmLogWindow.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\frmMain.resx">
<DependentUpon>frmMain.cs</DependentUpon>
</EmbeddedResource>