UNIF: Added support for UNL-DRIPGAME boards
This commit is contained in:
parent
c2e063d5c9
commit
cef7fa9eaa
7 changed files with 320 additions and 4 deletions
|
@ -903,6 +903,8 @@
|
|||
<ClInclude Include="UnifLoader.h" />
|
||||
<ClInclude Include="Unl255in1.h" />
|
||||
<ClInclude Include="Unl43272.h" />
|
||||
<ClInclude Include="UnlDripGame.h" />
|
||||
<ClInclude Include="UnlDripGameAudio.h" />
|
||||
<ClInclude Include="UnlPuzzle.h" />
|
||||
<ClInclude Include="UnlVrc7.h" />
|
||||
<ClInclude Include="UnRom512.h" />
|
||||
|
|
|
@ -1432,6 +1432,12 @@
|
|||
<ClInclude Include="BmcGn45.h">
|
||||
<Filter>Nes\Mappers\Unif</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UnlDripGame.h">
|
||||
<Filter>Nes\Mappers\Unif</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UnlDripGameAudio.h">
|
||||
<Filter>Nes\Mappers\Unif</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
|
@ -244,6 +244,7 @@
|
|||
#include "Unl43272.h"
|
||||
#include "Unl8237A.h"
|
||||
#include "UnlD1038.h"
|
||||
#include "UnlDripGame.h"
|
||||
#include "UnlPci556.h"
|
||||
#include "UnlPuzzle.h"
|
||||
#include "UnlVrc7.h"
|
||||
|
@ -273,18 +274,18 @@ Supported mappers:
|
|||
-----------------------------------------------------------------
|
||||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15|
|
||||
| 16| 17| 18| 19|...| 21| 22| 23| 24| 25| 26| 27| 28| 29| 30| 31|
|
||||
| 32| 33| 34| 35| 36| 37| 38|---| 40| 41| 42| 43| 44| 45| 46| 47|
|
||||
| 32| 33| 34| 35| 36| 37| 38| 39| 40| 41| 42| 43| 44| 45| 46| 47|
|
||||
| 48| 49| 50| 51| 52| 53| 54|???| 56| 57| 58|===| 60| 61| 62| 63|
|
||||
| 64| 65| 66| 67| 68| 69| 70| 71| 72| 73| 74| 75| 76| 77| 78| 79|
|
||||
| 80|===| 82| 83|===| 85| 86| 87| 88| 89| 90| 91| 92| 93| 94| 95|
|
||||
| 96| 97|===| 99|...|101|===|103|104|105|106|107|108|===|===|111|
|
||||
|112|113|114|115| |117|118|119|120|121|===|123|===|125|126|===|
|
||||
|112|113|114|115|116|117|118|119|120|121|===|123|===|125|126|===|
|
||||
|===|===|===|===|132|133|134|===|136|137|138|139|140|141|142|143|
|
||||
|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|
|
||||
|---|===|162|163|164|165|166|167|168|===|170|171|172|173|174|175|
|
||||
|176|177|178|179|180|---|182|183|184|185|186|187|188|189|190|191|
|
||||
|192|193|194|195|196|197| |199|200|201|202|203|204|205|206|207|
|
||||
|???|209|210|211|212|213|214|215|216|217|218|219|220|221|222|???|
|
||||
|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|
|
||||
|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|???|
|
||||
|???|225|226|227|228|229|230|231|232|233|234|235|236|===|238|===|
|
||||
|240|241|242|243|244|245|246|===|===|249|250|===|252|253|254|255|
|
||||
-----------------------------------------------------------------
|
||||
|
@ -592,6 +593,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
|
|||
case UnifBoards::Unl43272: return new Unl43272();
|
||||
case UnifBoards::Unl8237A: return new Unl8237A();
|
||||
case UnifBoards::UnlD1038: return new UnlD1038();
|
||||
case UnifBoards::UnlDripGame: return new UnlDripGame();
|
||||
case UnifBoards::UnlPuzzle: return new UnlPuzzle();
|
||||
case UnifBoards::UnlVrc7: return new UnlVrc7();
|
||||
case UnifBoards::Yoko: return new Yoko();
|
||||
|
|
|
@ -70,5 +70,6 @@ namespace UnifBoards {
|
|||
BmcHpxx,
|
||||
DragonFighter,
|
||||
BmcGn45,
|
||||
UnlDripGame,
|
||||
};
|
||||
}
|
|
@ -160,4 +160,5 @@ std::unordered_map<string, int> UnifLoader::_boardMappings = std::unordered_map<
|
|||
{ "WAIXING-FS005", UnifBoards::UnknownBoard },
|
||||
{ "HPxx", UnifBoards::BmcHpxx },
|
||||
{ "GN-45", UnifBoards::BmcGn45 }, //Doesn't actually exist as a UNIF file (used to assign a mapper to GN-45 boards)
|
||||
{ "DRIPGAME", UnifBoards::UnlDripGame },
|
||||
};
|
169
Core/UnlDripGame.h
Normal file
169
Core/UnlDripGame.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "UnlDripGameAudio.h"
|
||||
|
||||
class UnlDripGame : public BaseMapper
|
||||
{
|
||||
private:
|
||||
UnlDripGameAudio _audioChannels[2];
|
||||
uint8_t _extendedAttributes[2][0x400];
|
||||
uint8_t _lowByteIrqCounter;
|
||||
uint16_t _irqCounter;
|
||||
uint16_t _lastNametableFetchAddr;
|
||||
bool _irqEnabled;
|
||||
bool _extAttributesEnabled;
|
||||
bool _wramWriteEnabled;
|
||||
bool _dipSwitch;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x800; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
uint16_t RegisterStartAddress() override { return 0x8000; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_lowByteIrqCounter = 0;
|
||||
_irqCounter = 0;
|
||||
_irqEnabled = false;
|
||||
_extAttributesEnabled = false;
|
||||
_wramWriteEnabled = false;
|
||||
_dipSwitch = false;
|
||||
_lastNametableFetchAddr = 0;
|
||||
|
||||
InitializeRam(_extendedAttributes[0], 0x400);
|
||||
InitializeRam(_extendedAttributes[1], 0x400);
|
||||
|
||||
SelectPRGPage(1, -1);
|
||||
|
||||
AddRegisterRange(0x4800, 0x5FFF, MemoryOperation::Read);
|
||||
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
|
||||
ArrayInfo<uint8_t> extAttributes1 { _extendedAttributes[0], 0x400 };
|
||||
ArrayInfo<uint8_t> extAttributes2 { _extendedAttributes[1], 0x400 };
|
||||
SnapshotInfo audioChannel1 { &_audioChannels[0] };
|
||||
SnapshotInfo audioChannel2 { &_audioChannels[1] };
|
||||
|
||||
Stream(extAttributes1, extAttributes2, audioChannel1, audioChannel2, _lowByteIrqCounter, _irqCounter, _irqEnabled,
|
||||
_extAttributesEnabled, _wramWriteEnabled, _dipSwitch);
|
||||
|
||||
if(!saving) {
|
||||
UpdateWorkRamState();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqEnabled) {
|
||||
if(_irqCounter > 0) {
|
||||
_irqCounter--;
|
||||
if(_irqCounter == 0) {
|
||||
//While the IRQ counter is enabled, the timer is decremented once per CPU
|
||||
//cycle.Once the timer reaches zero, the /IRQ line is set to logic 0 and the
|
||||
//timer stops decrementing
|
||||
_irqEnabled = false;
|
||||
CPU::SetIRQSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_audioChannels[0].Clock();
|
||||
_audioChannels[1].Clock();
|
||||
}
|
||||
|
||||
void UpdateWorkRamState()
|
||||
{
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::WorkRam, _wramWriteEnabled ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
||||
}
|
||||
|
||||
virtual uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType memoryOperationType) override
|
||||
{
|
||||
if(_extAttributesEnabled && memoryOperationType == MemoryOperationType::PpuRenderingRead) {
|
||||
if(addr >= 0x2000 && (addr & 0x3FF) < 0x3C0) {
|
||||
//Nametable fetches
|
||||
_lastNametableFetchAddr = addr & 0x03FF;
|
||||
} else if(addr >= 0x2000 && (addr & 0x3FF) >= 0x3C0) {
|
||||
//Attribute fetches
|
||||
uint8_t bank;
|
||||
switch(GetMirroringType()) {
|
||||
case MirroringType::ScreenAOnly: bank = 0; break;
|
||||
case MirroringType::ScreenBOnly: bank = 1; break;
|
||||
case MirroringType::Horizontal: bank = (addr & 0x800) ? 1 : 0; break;
|
||||
case MirroringType::Vertical: bank = (addr & 0x400) ? 1 : 0; break;
|
||||
}
|
||||
|
||||
//Return a byte containing the same palette 4 times - this allows the PPU to select the right palette no matter the shift value
|
||||
uint8_t value = _extendedAttributes[bank][_lastNametableFetchAddr & 0x3FF] & 0x03;
|
||||
return (value << 6) | (value << 4) | (value << 2) | value;
|
||||
}
|
||||
}
|
||||
return BaseMapper::MapperReadVRAM(addr, memoryOperationType);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
switch(addr & 0x5800) {
|
||||
case 0x4800: return (_dipSwitch ? 0x80 : 0) | 0x64;
|
||||
case 0x5000: return _audioChannels[0].ReadRegister();
|
||||
case 0x5800: return _audioChannels[1].ReadRegister();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr <= 0xBFFF) {
|
||||
switch(addr & 0x800F) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
_audioChannels[0].WriteRegister(addr, value);
|
||||
break;
|
||||
|
||||
case 0x8004: case 0x8005: case 0x8006: case 0x8007:
|
||||
_audioChannels[1].WriteRegister(addr, value);
|
||||
break;
|
||||
|
||||
case 0x8008:
|
||||
_lowByteIrqCounter = value;
|
||||
break;
|
||||
|
||||
case 0x8009:
|
||||
//Data written to the IRQ Counter Low register is buffered until writing to IRQ
|
||||
//Counter High, at which point the composite data is written directly to the IRQ timer.
|
||||
_irqCounter = ((value & 0x7F) << 8) | _lowByteIrqCounter;
|
||||
_irqEnabled = (value & 0x80) != 0;
|
||||
|
||||
//Writing to the IRQ Enable register will acknowledge the interrupt and return the /IRQ signal to logic 1.
|
||||
CPU::ClearIRQSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
case 0x800A:
|
||||
switch(value & 0x03) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 1: SetMirroringType(MirroringType::Horizontal); break;
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
_extAttributesEnabled = (value & 0x04) != 0;
|
||||
_wramWriteEnabled = (value & 0x08) != 0;
|
||||
UpdateWorkRamState();
|
||||
break;
|
||||
|
||||
case 0x800B: SelectPRGPage(0, value & 0x0F); break;
|
||||
case 0x800C: SelectCHRPage(0, value & 0x0F); break;
|
||||
case 0x800D: SelectCHRPage(1, value & 0x0F); break;
|
||||
case 0x800E: SelectCHRPage(2, value & 0x0F); break;
|
||||
case 0x800F: SelectCHRPage(3, value & 0x0F); break;
|
||||
}
|
||||
} else {
|
||||
//Attribute expansion memory at $C000-$C7FF is mirrored throughout $C000-$FFFF.
|
||||
_extendedAttributes[(addr & 0x400) ? 1 : 0][addr & 0x3FF] = value;
|
||||
}
|
||||
}
|
||||
};
|
135
Core/UnlDripGameAudio.h
Normal file
135
Core/UnlDripGameAudio.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "BaseExpansionAudio.h"
|
||||
|
||||
class UnlDripGameAudio : public BaseExpansionAudio
|
||||
{
|
||||
private:
|
||||
uint8_t _buffer[256];
|
||||
uint8_t _readPos;
|
||||
uint8_t _writePos;
|
||||
bool _bufferFull;
|
||||
bool _bufferEmpty;
|
||||
|
||||
uint16_t _freq;
|
||||
uint16_t _timer;
|
||||
uint8_t _volume;
|
||||
int16_t _prevOutput;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseExpansionAudio::StreamState(saving);
|
||||
ArrayInfo<uint8_t> buffer { _buffer, 256 };
|
||||
Stream(_readPos, _writePos, _bufferFull, _bufferEmpty, _freq, _timer, _volume, _prevOutput, buffer);
|
||||
}
|
||||
|
||||
void ClockAudio() override
|
||||
{
|
||||
if(_bufferEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
_timer--;
|
||||
if(_timer == 0) {
|
||||
//Each time the timer reaches zero, it is reloaded and a byte is removed from the
|
||||
//channel's FIFO and is output (with 0x80 being the 'center' voltage) at the
|
||||
//channel's specified volume.
|
||||
_timer = _freq;
|
||||
|
||||
if(_readPos == _writePos) {
|
||||
_bufferFull = false;
|
||||
}
|
||||
|
||||
_readPos++;
|
||||
SetOutput(((int)_buffer[_readPos] - 0x80) * _volume);
|
||||
|
||||
if(_readPos == _writePos) {
|
||||
_bufferEmpty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetOutput(int16_t output)
|
||||
{
|
||||
APU::AddExpansionAudioDelta(AudioChannel::VRC7, (output - _prevOutput) * 3);
|
||||
_prevOutput = output;
|
||||
}
|
||||
|
||||
void ResetBuffer()
|
||||
{
|
||||
memset(_buffer, 0, 256);
|
||||
_readPos = 0;
|
||||
_writePos = 0;
|
||||
_bufferFull = false;
|
||||
_bufferEmpty = true;
|
||||
}
|
||||
|
||||
public:
|
||||
UnlDripGameAudio()
|
||||
{
|
||||
_freq = 0;
|
||||
_timer = 0;
|
||||
_volume = 0;
|
||||
_prevOutput = 0;
|
||||
ResetBuffer();
|
||||
}
|
||||
|
||||
uint8_t ReadRegister()
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if(_bufferFull) {
|
||||
result |= 0x80;
|
||||
}
|
||||
if(_bufferEmpty) {
|
||||
result |= 0x40;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
//Writing any value will silence the corresponding sound channel
|
||||
//When a channel's Clear FIFO register is written to, its timer is reset to the
|
||||
//last written frequency and it is silenced, outputting a 'center' voltage.
|
||||
ResetBuffer();
|
||||
SetOutput(0);
|
||||
_timer = _freq;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
//Writing a value will insert it into the FIFO.
|
||||
if(_readPos == _writePos) {
|
||||
//When data is written to an empty channel's Data Port, the channel's timer is
|
||||
//reloaded from the Period registers and playback begins immediately.
|
||||
_bufferEmpty = false;
|
||||
SetOutput((value - 0x80) * _volume);
|
||||
_timer = _freq;
|
||||
}
|
||||
|
||||
_buffer[_writePos++] = value;
|
||||
if(_readPos == _writePos) {
|
||||
_bufferFull = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//Specifies channel playback rate, in cycles per sample (lower 8 bits)
|
||||
_freq = (_freq & 0x0F00) | value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
//Specifies channel playback rate, in cycles per sample (higher 8 bits) (bits 0-3)
|
||||
//Specifies channel playback volume (bits 4-7)
|
||||
_freq = (_freq & 0xFF) | ((value & 0x0F) << 8);
|
||||
_volume = (value & 0xF0) >> 4;
|
||||
|
||||
if(!_bufferEmpty) {
|
||||
//Updates to a channel's Period do not take effect until the current
|
||||
//sample has finished playing, but updates to a channel's Volume take effect immediately.
|
||||
SetOutput(((int)_buffer[_readPos] - 0x80) * _volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue