Merge pull request #1853 from TiKevin83/master

Fix Console Sync for GB games on GBC/GBA
This commit is contained in:
alyosha-tas 2020-02-18 08:18:37 -05:00 committed by GitHub
commit fd793322f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 2998 additions and 2388 deletions

52
libgambatte/array.h Normal file
View File

@ -0,0 +1,52 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
template<typename T>
class SimpleArray : Uncopyable {
public:
explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {}
~SimpleArray() { delete[] defined_ptr(a_); }
void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; }
T* get() const { return a_; }
operator T* () const { return a_; }
private:
T* a_;
};
class Uncopyable {
protected:
Uncopyable() {}
private:
Uncopyable(Uncopyable const&);
Uncopyable& operator=(Uncopyable const&);
};
template<class T>
inline T* defined_ptr(T* t) {
typedef char type_is_defined[sizeof * t ? 1 : -1];
(void)sizeof(type_is_defined);
return t;
}
#endif

View File

@ -22,32 +22,32 @@
<ProjectGuid>{5D630682-7BDA-474D-B387-0EB420DDC199}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libgambatte</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@ -154,9 +154,11 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="src\array.h" />
<ClInclude Include="include\gambatte.h" />
<ClInclude Include="include\gbint.h" />
<ClInclude Include="include\loadres.h" />
<ClInclude Include="src\interrupter.h" />
<ClInclude Include="src\cinterface.h" />
<ClInclude Include="src\counterdef.h" />
<ClInclude Include="src\cpu.h" />
@ -180,18 +182,21 @@
<ClInclude Include="src\sound\envelope_unit.h" />
<ClInclude Include="src\sound\length_counter.h" />
<ClInclude Include="src\sound\master_disabler.h" />
<ClInclude Include="src\sound\psgdef.h" />
<ClInclude Include="src\sound\sound_unit.h" />
<ClInclude Include="src\sound\static_output_tester.h" />
<ClInclude Include="src\tima.h" />
<ClInclude Include="src\video.h" />
<ClInclude Include="src\video\lcddef.h" />
<ClInclude Include="src\video\lyc_irq.h" />
<ClInclude Include="src\video\ly_counter.h" />
<ClInclude Include="src\video\m0_irq.h" />
<ClInclude Include="src\video\mstat_irq.h" />
<ClInclude Include="src\video\next_m0_time.h" />
<ClInclude Include="src\video\ppu.h" />
<ClInclude Include="src\video\sprite_mapper.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Interrupter.cpp" />
<ClCompile Include="src\cinterface.cpp" />
<ClCompile Include="src\cpu.cpp" />
<ClCompile Include="src\gambatte.cpp" />

View File

@ -111,9 +111,6 @@
<ClInclude Include="src\video\ly_counter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\m0_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\next_m0_time.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -123,6 +120,21 @@
<ClInclude Include="src\video\sprite_mapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\interrupter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\array.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\psgdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\mstat_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\lcddef.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cinterface.cpp">
@ -203,5 +215,8 @@
<ClCompile Include="src\video\sprite_mapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Interrupter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,103 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "interrupter.h"
#include "memory.h"
namespace gambatte {
Interrupter::Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched)
: sp_(sp)
, pc_(pc)
, opcode_(opcode)
, prefetched_(prefetched)
{
}
void Interrupter::prefetch(unsigned long cc, Memory& mem) {
if (!prefetched_) {
opcode_ = mem.read(pc_, cc);
pc_ = (pc_ + 1) & 0xFFFF;
prefetched_ = true;
}
}
unsigned long Interrupter::interrupt(unsigned long cc, Memory& memory) {
// undo prefetch (presumably unconditional on hw).
if (prefetched_) {
pc_ = (pc_ - 1) & 0xFFFF;
prefetched_ = false;
}
cc += 12;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ >> 8, cc);
cc += 4;
unsigned const pendingIrqs = memory.pendingIrqs(cc);
unsigned const n = pendingIrqs & -pendingIrqs;
unsigned address;
if (n <= 4) {
static unsigned char const lut[] = { 0x00, 0x40, 0x48, 0x48, 0x50 };
address = lut[n];
}
else
address = 0x50 + n;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ & 0xFF, cc);
memory.ackIrq(n, cc);
pc_ = address;
cc += 4;
if (address == 0x40 && !gsCodes_.empty())
applyVblankCheats(cc, memory);
return cc;
}
static int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
void Interrupter::setGameShark(std::string const& codes) {
std::string code;
gsCodes_.clear();
for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) {
code = codes.substr(pos, codes.find(';', pos) - pos);
if (code.length() >= 8) {
GsCode gs;
gs.type = asHex(code[0]) << 4 | asHex(code[1]);
gs.value = (asHex(code[2]) << 4 | asHex(code[3])) & 0xFF;
gs.address = (asHex(code[4]) << 4
| asHex(code[5])
| asHex(code[6]) << 12
| asHex(code[7]) << 8) & 0xFFFF;
gsCodes_.push_back(gs);
}
}
}
void Interrupter::applyVblankCheats(unsigned long const cc, Memory& memory) {
for (std::size_t i = 0, size = gsCodes_.size(); i < size; ++i) {
if (gsCodes_[i].type == 0x01)
memory.write(gsCodes_[i].address, gsCodes_[i].value, cc);
}
}
}

57
libgambatte/src/array.h Normal file
View File

@ -0,0 +1,57 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
template<typename T>
class SimpleArray : Uncopyable {
public:
explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {}
~SimpleArray() { delete[] defined_ptr(a_); }
void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; }
T* get() const { return a_; }
operator T* () const { return a_; }
private:
T* a_;
};
class Uncopyable {
protected:
Uncopyable() {}
private:
Uncopyable(Uncopyable const&);
Uncopyable& operator=(Uncopyable const&);
};
template<class T>
inline T* defined_ptr(T* t) {
typedef char type_is_defined[sizeof * t ? 1 : -1];
(void)sizeof(type_is_defined);
return t;
}
template<class T>
inline void defined_delete(T* t) { delete defined_ptr(t); }
struct defined_deleter { template<class T> static void del(T* p) { defined_delete(p); } };
#endif

View File

@ -23,7 +23,7 @@
namespace gambatte {
CPU::CPU()
: mem_(sp, pc)
: mem_(Interrupter(sp, pc, opcode_, prefetched_))
, cycleCounter_(0)
, pc(0x100)
, sp(0xFFFE)
@ -38,7 +38,8 @@ CPU::CPU()
, e(0xD8)
, h(0x01)
, l(0x4D)
, skip_(false)
, opcode_(0)
, prefetched_(false)
, numInterruptAddresses()
, tracecallback(0)
{
@ -103,7 +104,13 @@ void CPU::loadState(SaveState const &state) {
cf = cfFromF(state.cpu.f);
h = state.cpu.h & 0xFF;
l = state.cpu.l & 0xFF;
skip_ = state.cpu.skip;
opcode_ = state.cpu.opcode;
prefetched_ = state.cpu.prefetched;
if (state.cpu.skip) {
opcode_ = mem_.read(pc, cycleCounter_);
prefetched_ = true;
}
}
// The main reasons for the use of macros is to more conveniently be able to tweak
@ -111,12 +118,11 @@ void CPU::loadState(SaveState const &state) {
// time they were written GCC had a tendency to not be able to keep hot variables
// in regs if you took an address/reference in an inline function.
#define bc() ( b << 8 | c )
#define de() ( d << 8 | e )
#define hl() ( h << 8 | l )
#define bc() ( b * 0x100u | c )
#define de() ( d * 0x100u | e )
#define hl() ( h * 0x100u | l )
#define READ(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); cycleCounter += 4; } while (0)
#define PEEK(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); } while (0)
#define PC_READ(dest) do { (dest) = mem_.read_excb(pc, cycleCounter, false); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0)
#define PC_READ_FIRST(dest) do { (dest) = mem_.read_excb(pc, cycleCounter, true); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0)
#define FF_READ(dest, addr) do { (dest) = mem_.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0)
@ -174,7 +180,7 @@ void CPU::loadState(SaveState const &state) {
// Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF:
#define rr_r(r) do { \
unsigned const oldcf = cf & 0x100; \
cf = (r) << 8; \
cf = (r) * 0x100u; \
(r) = zf = ((r) | oldcf) >> 1; \
hf2 = 0; \
} while (0)
@ -190,7 +196,7 @@ void CPU::loadState(SaveState const &state) {
// sra r (8 cycles):
// Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF:
#define sra_r(r) do { \
cf = (r) << 8; \
cf = (r) * 0x100u; \
zf = (r) >> 1; \
(r) = zf | ((r) & 0x80); \
hf2 = 0; \
@ -200,7 +206,7 @@ void CPU::loadState(SaveState const &state) {
// Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF:
#define srl_r(r) do { \
zf = (r); \
cf = (r) << 8; \
cf = (r) * 0x100u; \
zf >>= 1; \
(r) = zf; \
hf2 = 0; \
@ -261,7 +267,7 @@ void CPU::loadState(SaveState const &state) {
unsigned const hl = hl(); \
unsigned val; \
READ(val, hl); \
val &= ~(1 << (n)); \
val &= ~(1u << (n)); \
WRITE(hl, val); \
} while (0)
@ -461,9 +467,8 @@ void CPU::loadState(SaveState const &state) {
// rst n (16 Cycles):
// Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h):
#define rst_n(n) do { \
cycleCounter += 4; \
PUSH(pc >> 8, pc & 0xFF); \
pc = n; \
push_rr(pc >> 8, pc & 0xFF); \
pc = (n); \
} while (0)
// ret (16 cycles):
@ -474,6 +479,17 @@ void CPU::loadState(SaveState const &state) {
PC_MOD(high << 8 | low); \
} while (0)
namespace {
unsigned long freeze(Memory & mem, unsigned long cc) {
mem.freeze(cc);
if (cc < mem.nextEventTime()) {
unsigned long cycles = mem.nextEventTime() - cc;
cc += cycles + (-cycles & 3);
}
return cc;
}
}
void CPU::process(unsigned long const cycles) {
mem_.setEndtime(cycleCounter_, cycles);
mem_.updateInput();
@ -524,19 +540,23 @@ void CPU::process(unsigned long const cycles) {
result[8] = toF(hf2, cf, zf);
result[9] = h;
result[10] = l;
result[11] = skip_;
PC_READ_FIRST(opcode);
result[11] = prefetched_;
//PC_READ_FIRST(opcode);
result[12] = opcode;
result[13] = mem_.debugGetLY();
tracecallback((void *)result);
}
else {
PC_READ_FIRST(opcode);
//PC_READ_FIRST(opcode);
}
if (skip_) {
pc = (pc - 1) & 0xFFFF;
skip_ = false;
if (!prefetched_) {
PC_READ(opcode);
}
else {
opcode = opcode_;
cycleCounter += 4;
prefetched_ = false;
}
switch (opcode) {
@ -607,7 +627,7 @@ void CPU::process(unsigned long const cycles) {
// rrca (4 cycles):
// Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF:
case 0x0F:
cf = a << 8 | a;
cf = a * 0x100u | a;
a = cf >> 1 & 0xFF;
hf2 = 0;
zf = 1;
@ -616,14 +636,13 @@ void CPU::process(unsigned long const cycles) {
// stop (4 cycles):
// Halt CPU and LCD display until button pressed:
case 0x10:
{
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
PC_READ(opcode_);
cycleCounter = mem_.stop(cycleCounter - 4, prefetched_);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
break;
case 0x11:
@ -687,7 +706,7 @@ void CPU::process(unsigned long const cycles) {
case 0x1F:
{
unsigned oldcf = cf & 0x100;
cf = a << 8;
cf = a * 0x100u;
a = (a | oldcf) >> 1;
}
@ -1023,14 +1042,12 @@ void CPU::process(unsigned long const cycles) {
// halt (4 cycles):
case 0x76:
if (mem_.ff_read(0x0F, cycleCounter) & mem_.ff_read(0xFF, cycleCounter) & 0x1F) {
if (mem_.ime())
pc = (pc - 1) & 0xFFFF;
else
skip_ = true;
opcode_ = mem_.read(pc, cycleCounter);
if (mem_.pendingIrqs(cycleCounter)) {
prefetched_ = true;
} else {
mem_.halt(cycleCounter);
prefetched_ = mem_.halt(cycleCounter);
cycleCounter += 4 + 4 * !mem_.isCgb();
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
@ -1705,13 +1722,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xD3: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
// call nc,nn (24;12 cycles):
@ -1779,13 +1790,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xDB: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
// call z,nn (24;12 cycles):
@ -1801,13 +1806,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xDD: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xDE:
@ -1846,13 +1845,7 @@ void CPU::process(unsigned long const cycles) {
case 0xE3:
case 0xE4: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xE5:
@ -1898,16 +1891,10 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xEB:
case 0xEC:
case 0xEB: // not specified. should freeze.
case 0xEC: // not specified. should freeze.
case 0xED: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xEE:
@ -1957,13 +1944,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xF4: // not specified. should freeze.
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xF5:
@ -2027,15 +2008,9 @@ void CPU::process(unsigned long const cycles) {
mem_.ei(cycleCounter);
break;
case 0xFC:
case 0xFC: // not specified. should freeze.
case 0xFD: // not specified. should freeze
mem_.di();
cycleCounter = mem_.stop(cycleCounter);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xFE:
{
@ -2102,7 +2077,8 @@ SYNCFUNC(CPU)
NSS(e);
NSS(h);
NSS(l);
NSS(skip_);
NSS(opcode_);
NSS(prefetched_);
}
}

View File

@ -73,8 +73,8 @@ public:
mem_.setLinkCallback(callback);
}
LoadRes load(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) {
return mem_.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat);
LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags) {
return mem_.loadROM(romfiledata, romfilelength, flags);
}
bool loaded() const { return mem_.loaded(); }
@ -94,7 +94,6 @@ public:
void setRtcDivisorOffset(long const rtcDivisorOffset) { mem_.setRtcDivisorOffset(rtcDivisorOffset); }
void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); }
bool gbIsCgb() { return mem_.gbIsCgb(); }
unsigned char externalRead(unsigned short addr) {return mem_.peek(addr); }
@ -115,7 +114,8 @@ private:
unsigned short sp;
unsigned hf1, hf2, zf, cf;
unsigned char a, b, c, d, e, /*f,*/ h, l;
bool skip_;
unsigned char opcode_;
bool prefetched_;
int *interruptAddresses;
int numInterruptAddresses;

View File

@ -88,7 +88,7 @@ void GB::reset() {
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB);
setInitState(state, !(p_->loadflags & FORCE_DMG));
p_->cpu.loadState(state);
if (length > 0)
{
@ -139,13 +139,13 @@ void GB::setRtcDivisorOffset(long const rtcDivisorOffset) {
}
LoadRes GB::load(char const *romfiledata, unsigned romfilelength, unsigned const flags) {
LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT);
LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags);
if (loadres == LOADRES_OK) {
SaveState state;
p_->cpu.setStatePtrs(state);
p_->loadflags = flags;
setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB);
setInitState(state, !(flags & FORCE_DMG));
p_->cpu.loadState(state);
}

View File

@ -1147,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char ioamhram[]) {
} // anon namespace
void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
void gambatte::setInitState(SaveState &state, const bool cgb) {
static unsigned char const cgbObjpDump[0x40] = {
0x00, 0x00, 0xF2, 0xAB,
0x61, 0xC2, 0xD9, 0xBA,
@ -1178,10 +1178,10 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.cpu.f = 0;
state.cpu.h = 0;
state.cpu.l = 0;
state.cpu.opcode = 0x00;
state.cpu.prefetched = false;
state.cpu.skip = false;
state.mem.biosMode = true;
state.mem.cgbSwitching = false;
state.mem.agbMode = gbaCgbMode;
std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.size());
@ -1199,26 +1199,27 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.mem.ioamhram.ptr[0x140] = 0;
state.mem.ioamhram.ptr[0x144] = 0x00;
// DIV, TIMA, and the PSG frame sequencer are clocked by bits of the
// cycle counter less divLastUpdate (equivalent to a counter that is
// reset on DIV write).
state.mem.divLastUpdate = 0;
state.mem.timaBasetime = 0;
state.mem.timaLastUpdate = 0;
state.mem.tmatime = disabled_time;
state.mem.nextSerialtime = disabled_time;
state.mem.lastOamDmaUpdate = disabled_time;
state.mem.unhaltTime = disabled_time;
state.mem.minIntTime = 0;
state.mem.halttime = 0;
state.mem.rombank = 1;
state.mem.dmaSource = 0;
state.mem.dmaDestination = 0;
state.mem.rambank = 0;
state.mem.oamDmaPos = 0xFE;
state.mem.haltHdmaState = 0;
state.mem.IME = false;
state.mem.halted = false;
state.mem.enableRam = false;
state.mem.rambankMode = false;
state.mem.hdmaTransfer = false;
state.mem.gbIsCgb = cgb;
state.mem.stopped = false;
@ -1265,15 +1266,16 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.ppu.nextM0Irq = 0;
state.ppu.oldWy = state.mem.ioamhram.get()[0x14A];
state.ppu.pendingLcdstatIrq = false;
state.ppu.isCgb = cgb;
state.ppu.notCgbDmg = true;
// spu.cycleCounter >> 12 & 7 represents the frame sequencer position.
state.spu.cycleCounter = state.cpu.cycleCounter >> 1;
state.spu.lastUpdate = 0;
state.spu.ch1.sweep.counter = SoundUnit::counter_disabled;
state.spu.ch1.sweep.shadow = 0;
state.spu.ch1.sweep.nr0 = 0;
state.spu.ch1.sweep.negging = false;
state.spu.ch1.sweep.neg = false;
state.spu.ch1.duty.nextPosUpdate = SoundUnit::counter_disabled;
state.spu.ch1.duty.pos = 0;
state.spu.ch1.duty.high = false;

View File

@ -23,7 +23,7 @@
namespace gambatte {
void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
void setInitState(struct SaveState &state, bool cgb);
}
#endif

View File

@ -0,0 +1,54 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INTERRUPTER_H
#define INTERRUPTER_H
#include <string>
#include <vector>
namespace gambatte {
struct GsCode {
unsigned short address;
unsigned char value;
unsigned char type;
};
class Memory;
class Interrupter {
public:
Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched);
void prefetch(unsigned long cc, Memory& mem);
unsigned long interrupt(unsigned long cycleCounter, Memory& memory);
void setGameShark(std::string const& codes);
private:
unsigned short& sp_;
unsigned short& pc_;
unsigned char& opcode_;
bool& prefetched_;
std::vector<GsCode> gsCodes_;
void applyVblankCheats(unsigned long cc, Memory& mem);
};
}
#endif

View File

@ -83,9 +83,14 @@ void InterruptRequester::flagIrq(unsigned bit) {
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::ackIrq(unsigned bit) {
ifreg_ ^= bit;
di();
void InterruptRequester::flagIrq(unsigned bit, unsigned long cc) {
unsigned const prevPending = pendingIrqs();
ifreg_ |= bit;
if (!prevPending && pendingIrqs() && intFlags_.imeOrHalted()) {
minIntTime_ = std::max(minIntTime_, cc);
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
}
void InterruptRequester::setIereg(unsigned iereg) {
@ -108,6 +113,13 @@ void InterruptRequester::setIfreg(unsigned ifreg) {
}
}
void InterruptRequester::setMinIntTime(unsigned long cc) {
minIntTime_ = cc;
if (eventTimes_.value(intevent_interrupts) < minIntTime_)
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
SYNCFUNC(InterruptRequester)
{
SSS(eventTimes_);

View File

@ -52,9 +52,11 @@ public:
void halt();
void unhalt();
void flagIrq(unsigned bit);
void ackIrq(unsigned bit);
void flagIrq(unsigned bit, unsigned long cc);
void ackIrq(unsigned bit) { ifreg_ &= ~bit; }
void setIereg(unsigned iereg);
void setIfreg(unsigned ifreg);
void setMinIntTime(unsigned long cc);
IntEventId minEventId() const { return static_cast<IntEventId>(eventTimes_.min()); }
unsigned long minEventTime() const { return eventTimes_.minValue(); }
@ -71,9 +73,9 @@ private:
bool halted() const { return flags_ & flag_halted; }
bool imeOrHalted() const { return flags_; }
void setIme() { flags_ |= flag_ime; }
void unsetIme() { flags_ &= ~flag_ime; }
void unsetIme() { flags_ &= ~(1u * flag_ime); }
void setHalted() { flags_ |= flag_halted; }
void unsetHalted() { flags_ &= ~flag_halted; }
void unsetHalted() { flags_ &= ~(1u * flag_halted); }
void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; }
private:

View File

@ -23,18 +23,18 @@
#include <cstring>
#include <fstream>
namespace gambatte {
using namespace gambatte;
namespace {
static unsigned toMulti64Rombank(unsigned rombank) {
unsigned toMulti64Rombank(unsigned rombank) {
return (rombank >> 1 & 0x30) | (rombank & 0xF);
}
class DefaultMbc : public Mbc {
public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr< 0x4000) == (bank == 0);
return (addr < rombank_size()) == (bank == 0);
}
virtual void SyncState(NewState *ns, bool isReader)
@ -50,8 +50,12 @@ public:
{
}
virtual unsigned char curRomBank() const {
return 1;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
if (p < 0x2000) {
if (p < rambank_size()) {
enableRam_ = (data & 0xF) == 0xA;
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
}
@ -73,12 +77,12 @@ public:
}
};
static inline unsigned rambanks(MemPtrs const &memptrs) {
return std::size_t(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000;
inline unsigned rambanks(MemPtrs const &memptrs) {
return (memptrs.rambankdataend() - memptrs.rambankdata()) / rambank_size();
}
static inline unsigned rombanks(MemPtrs const &memptrs) {
return std::size_t(memptrs.romdataend() - memptrs.romdata() ) / 0x4000;
inline unsigned rombanks(MemPtrs const &memptrs) {
return (memptrs.romdataend() - memptrs.romdata()) / rombank_size();
}
class Mbc1 : public DefaultMbc {
@ -92,6 +96,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p >> 13 & 3) {
case 0:
@ -113,8 +121,7 @@ public:
break;
case 3:
// Pretty sure this should take effect immediately, but I have a policy not to change old behavior
// unless I have something (eg. a verified test or a game) that justifies it.
// Should this take effect immediately rather?
rambankMode_ = data & 1;
break;
}
@ -143,7 +150,7 @@ private:
rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); }
void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); }
public:
@ -166,6 +173,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p >> 13 & 3) {
case 0:
@ -238,6 +249,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p & 0x6100) {
case 0x0000:
@ -282,6 +297,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const cc) {
switch (p >> 13 & 3) {
case 0:
@ -356,6 +375,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p >> 13 & 3) {
case 0:
@ -424,6 +447,10 @@ public:
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p >> 13 & 3) {
case 0:
@ -459,14 +486,12 @@ private:
unsigned char rambank_;
bool enableRam_;
static unsigned adjustedRombank(unsigned bank) { return bank; }
void setRambank() const {
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0,
rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); }
void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); }
public:
virtual void SyncState(NewState *ns, bool isReader)
@ -477,12 +502,76 @@ public:
}
};
static bool hasRtc(unsigned headerByte0x147) {
std::string stripExtension(std::string const& str) {
std::string::size_type const lastDot = str.find_last_of('.');
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
std::string stripDir(std::string const& str) {
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
void enforce8bit(unsigned char* data, std::size_t size) {
if (static_cast<unsigned char>(0x100))
while (size--)
*data++ &= 0xFF;
}
unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
}
bool hasBattery(unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFE: // huc3
case 0xFF:
return true;
}
return false;
}
bool hasRtc(unsigned headerByte0x147) {
switch (headerByte0x147) {
case 0x0F:
case 0x10: return true;
default: return false;
case 0x10:
case 0xFE: // huc3
return true;
}
return false;
}
int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
}
@ -503,23 +592,6 @@ void Cartridge::loadState(SaveState const &state) {
mbc_->loadState(state.mem);
}
static void enforce8bit(unsigned char *data, std::size_t size) {
if (static_cast<unsigned char>(0x100))
while (size--)
*data++ &= 0xFF;
}
static unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
static bool isMbc2(unsigned char h147) { return h147 == 5 || h147 == 6; }
static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) {
@ -532,10 +604,6 @@ static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) {
return 4;
}
static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
}
LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) {
enum Cartridgetype { type_plain,
type_mbc1,
@ -610,17 +678,17 @@ LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool
cgb = !forceDmg;
}
std::size_t const filesize = romfilelength;
rombanks = std::max(pow2ceil(filesize / 0x4000), 2u);
rombanks = std::max(pow2ceil(filesize / rombank_size()), 2u);
mbc_.reset();
memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2);
rtc_.set(false, 0);
std::memcpy(memptrs_.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul);
std::memset(memptrs_.romdata() + filesize / 0x4000 * 0x4000ul,
std::memcpy(memptrs_.romdata(), romfiledata, (filesize / rombank_size() * rombank_size()));
std::memset(memptrs_.romdata() + filesize / rombank_size() * rombank_size(),
0xFF,
(rombanks - filesize / 0x4000) * 0x4000ul);
enforce8bit(memptrs_.romdata(), rombanks * 0x4000ul);
(rombanks - filesize / rombank_size()) * rombank_size());
enforce8bit(memptrs_.romdata(), rombanks * rombank_size());
switch (type) {
case type_plain: mbc_.reset(new Mbc0(memptrs_)); break;
@ -642,21 +710,6 @@ LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool
return LOADRES_OK;
}
static bool hasBattery(unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFF: return true;
default: return false;
}
}
void Cartridge::loadSavedata(char const *data, unsigned long const cc) {
if (hasBattery(memptrs_.romdata()[0x147])) {
int length = memptrs_.rambankdataend() - memptrs_.rambankdata();
@ -747,5 +800,3 @@ SYNCFUNC(Cartridge)
SSS(rtc_);
TSS(mbc_);
}
}

View File

@ -34,6 +34,7 @@ namespace gambatte {
class Mbc {
public:
virtual ~Mbc() {}
virtual unsigned char curRomBank() const = 0;
virtual void romWrite(unsigned P, unsigned data, unsigned long cycleCounter) = 0;
virtual void loadState(SaveState::Mem const &ss) = 0;
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
@ -62,10 +63,11 @@ public:
unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); }
unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); }
OamDmaSrc oamDmaSrc() const { return memptrs_.oamDmaSrc(); }
bool isInOamDmaConflictArea(unsigned p) const { return memptrs_.isInOamDmaConflictArea(p); }
void setVrambank(unsigned bank) { memptrs_.setVrambank(bank); }
void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); }
void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); }
unsigned curRomBank() const { return memptrs_.curRomBank(); }
unsigned char curRomBank() const { return mbc_->curRomBank(); }
void mbcWrite(unsigned addr, unsigned data, unsigned long const cc) { mbc_->romWrite(addr, data, cc); }
bool isCgb() const { return gambatte::isCgb(memptrs_); }
void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); }

View File

@ -20,7 +20,56 @@
#include <algorithm>
#include <cstring>
namespace gambatte {
using namespace gambatte;
namespace {
template <OamDmaSrc src, bool cgb> struct OamDmaConflictMap;
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_rom, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_sram, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_vram, cgb> { enum { r = 0x0300 }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_wram, cgb> { enum { r = cgb ? 0xF000 : 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_invalid, cgb> { enum { r = cgb ? 0xFCFF : 0x0000 }; };
template <bool cgb>
bool isInOamDmaConflictArea(OamDmaSrc src, unsigned p)
{
static unsigned short const m[] = {
OamDmaConflictMap<oam_dma_src_rom, cgb>::r,
OamDmaConflictMap<oam_dma_src_sram, cgb>::r,
OamDmaConflictMap<oam_dma_src_vram, cgb>::r,
OamDmaConflictMap<oam_dma_src_wram, cgb>::r,
OamDmaConflictMap<oam_dma_src_invalid, cgb>::r,
0 };
return p < mm_oam_begin && (m[src] >> (p >> 12) & 1);
}
template <OamDmaSrc src, bool cgb>
void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10])
{
if (OamDmaConflictMap<src, cgb>::r & 0x00FF)
std::fill_n(rmem, 8, static_cast<unsigned char*>(0));
if (OamDmaConflictMap<src, cgb>::r & 0x0C00)
rmem[0xB] = rmem[0xA] = wmem[0xB] = wmem[0xA] = 0;
if (OamDmaConflictMap<src, cgb>::r & 0x7000)
rmem[0xE] = rmem[0xD] = rmem[0xC] = wmem[0xE] = wmem[0xD] = wmem[0xC] = 0;
}
template <bool cgb>
void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10],
OamDmaSrc src)
{
switch (src) {
case oam_dma_src_rom: disconnectOamDmaAreas<oam_dma_src_rom, cgb>(rmem, wmem); break;
case oam_dma_src_sram: disconnectOamDmaAreas<oam_dma_src_sram, cgb>(rmem, wmem); break;
case oam_dma_src_vram: disconnectOamDmaAreas<oam_dma_src_vram, cgb>(rmem, wmem); break;
case oam_dma_src_wram: disconnectOamDmaAreas<oam_dma_src_wram, cgb>(rmem, wmem); break;
case oam_dma_src_invalid: disconnectOamDmaAreas<oam_dma_src_invalid, cgb>(rmem, wmem); break;
case oam_dma_src_off: break;
}
}
} // unnamed namespace.
MemPtrs::MemPtrs()
: rmem_()
@ -30,7 +79,6 @@ MemPtrs::MemPtrs()
, vrambankptr_(0)
, rsrambankptr_(0)
, wsrambankptr_(0)
, memchunk_(0)
, rambankdata_(0)
, wramdataend_(0)
, oamDmaSrc_(oam_dma_src_off)
@ -39,32 +87,27 @@ MemPtrs::MemPtrs()
{
}
MemPtrs::~MemPtrs() {
delete []memchunk_;
}
void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) {
delete []memchunk_;
memchunk_len =
0x4000
+ rombanks * 0x4000ul
+ 0x4000
+ rambanks * 0x2000ul
+ wrambanks * 0x1000ul
+ 0x4000;
memchunk_ = new unsigned char[memchunk_len];
int const num_disabled_ram_areas = 2;
memchunk_.reset(
pre_rom_pad_size()
+ rombanks * rombank_size()
+ max_num_vrambanks * vrambank_size()
+ rambanks * rambank_size()
+ wrambanks * wrambank_size()
+ num_disabled_ram_areas * rambank_size());
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000;
wramdata_[0] = rambankdata_ + rambanks * 0x2000ul;
wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul;
rambankdata_ = romdata_[0] + rombanks * rombank_size() + max_num_vrambanks * vrambank_size();
wramdata_[0] = rambankdata_ + rambanks * rambank_size();
wramdataend_ = wramdata_[0] + wrambanks * wrambank_size();
std::memset(rdisabledRamw(), 0xFF, 0x2000);
std::fill_n(rdisabledRamw(), rambank_size(), 0xFF);
oamDmaSrc_ = oam_dma_src_off;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
setRombank(1);
setRambank(0, 0);
setVrambank(0);
@ -76,38 +119,40 @@ void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned c
}
void MemPtrs::setRombank0(unsigned bank) {
romdata_[0] = romdata() + bank * 0x4000ul;
romdata_[0] = romdata() + bank * rombank_size();
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
disconnectOamDmaAreas();
}
void MemPtrs::setRombank(unsigned bank) {
curRomBank_ = bank;
romdata_[1] = romdata() + bank * 0x4000ul - 0x4000;
romdata_[1] = romdata() + bank * rombank_size() - mm_rom1_begin;
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
disconnectOamDmaAreas();
}
void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) {
unsigned char *srambankptr = 0;
unsigned char* srambankptr = 0;
if (!(flags & rtc_en)) {
srambankptr = rambankdata() != rambankdataend()
? rambankdata_ + rambank * 0x2000ul - 0xA000
: wdisabledRam() - 0xA000;
? rambankdata_ + rambank * rambank_size() - mm_sram_begin
: wdisabledRam() - mm_sram_begin;
}
rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - 0xA000
? srambankptr
: rdisabledRamw() - 0xA000;
wsrambankptr_ = flags & write_en ? srambankptr : wdisabledRam() - 0xA000;
rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - mm_sram_begin
? srambankptr
: rdisabledRamw() - mm_sram_begin;
wsrambankptr_ = flags & write_en
? srambankptr
: wdisabledRam() - mm_sram_begin;
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
disconnectOamDmaAreas();
}
void MemPtrs::setWrambank(unsigned bank) {
wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * 0x1000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * wrambank_size();
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
disconnectOamDmaAreas();
}
@ -116,51 +161,25 @@ void MemPtrs::setOamDmaSrc(OamDmaSrc oamDmaSrc) {
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
oamDmaSrc_ = oamDmaSrc;
disconnectOamDmaAreas();
}
void MemPtrs::disconnectOamDmaAreas() {
if (isCgb(*this)) {
switch (oamDmaSrc_) {
case oam_dma_src_rom: // fall through
case oam_dma_src_sram:
case oam_dma_src_invalid:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
break;
case oam_dma_src_vram:
break;
case oam_dma_src_wram:
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case oam_dma_src_off:
break;
}
} else {
switch (oamDmaSrc_) {
case oam_dma_src_rom: // fall through
case oam_dma_src_sram:
case oam_dma_src_wram:
case oam_dma_src_invalid:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case oam_dma_src_vram:
break;
case oam_dma_src_off:
break;
}
}
return isCgb(*this)
? ::disconnectOamDmaAreas<true>(rmem_, wmem_, oamDmaSrc_)
: ::disconnectOamDmaAreas<false>(rmem_, wmem_, oamDmaSrc_);
}
bool MemPtrs::isInOamDmaConflictArea(unsigned p) const
{
return isCgb(*this)
? ::isInOamDmaConflictArea<true>(oamDmaSrc_, p)
: ::isInOamDmaConflictArea<false>(oamDmaSrc_, p);
}
// all pointers here are relative to memchunk_
@ -220,4 +239,3 @@ SYNCFUNC(MemPtrs)
NSS(curRomBank_);
}
}

View File

@ -20,46 +20,65 @@
#define MEMPTRS_H
#include "newstate.h"
#include "array.h"
namespace gambatte {
enum OamDmaSrc { oam_dma_src_rom,
oam_dma_src_sram,
oam_dma_src_vram,
oam_dma_src_wram,
oam_dma_src_invalid,
oam_dma_src_off, };
enum OamDmaSrc {
oam_dma_src_rom,
oam_dma_src_sram,
oam_dma_src_vram,
oam_dma_src_wram,
oam_dma_src_invalid,
oam_dma_src_off };
enum {
mm_rom_begin = 0x0000,
mm_rom1_begin = 0x4000,
mm_vram_begin = 0x8000,
mm_sram_begin = 0xA000,
mm_wram_begin = 0xC000,
mm_wram1_begin = 0xD000,
mm_wram_mirror_begin = 0xE000,
mm_oam_begin = 0xFE00,
mm_io_begin = 0xFF00,
mm_hram_begin = 0xFF80 };
enum { max_num_vrambanks = 2 };
inline std::size_t rambank_size() { return 0x2000; }
inline std::size_t rombank_size() { return 0x4000; }
inline std::size_t vrambank_size() { return 0x2000; }
inline std::size_t wrambank_size() { return 0x1000; }
class MemPtrs {
public:
enum RamFlag { read_en = 1, write_en = 2, rtc_en = 4 };
MemPtrs();
~MemPtrs();
void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks);
unsigned char const * rmem(unsigned area) const { return rmem_[area]; }
unsigned char * wmem(unsigned area) const { return wmem_[area]; }
unsigned char * vramdata() const { return rambankdata_ - 0x4000; }
unsigned char * vramdataend() const { return rambankdata_; }
unsigned char * romdata() const { return memchunk_ + 0x4000; }
unsigned char * romdata(unsigned area) const { return romdata_[area]; }
unsigned char * romdataend() const { return rambankdata_ - 0x4000; }
unsigned char * wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char * wramdataend() const { return wramdataend_; }
unsigned char * rambankdata() const { return rambankdata_; }
unsigned char * rambankdataend() const { return wramdata_[0]; }
unsigned char const * rdisabledRam() const { return rdisabledRamw(); }
unsigned char const * rsrambankptr() const { return rsrambankptr_; }
unsigned char * wsrambankptr() const { return wsrambankptr_; }
unsigned char * vrambankptr() const { return vrambankptr_; }
unsigned char const* rmem(unsigned area) const { return rmem_[area]; }
unsigned char* wmem(unsigned area) const { return wmem_[area]; }
unsigned char* romdata() const { return memchunk_ + pre_rom_pad_size(); }
unsigned char* romdata(unsigned area) const { return romdata_[area]; }
unsigned char* romdataend() const { return rambankdata_ - max_num_vrambanks * vrambank_size(); }
unsigned char* vramdata() const { return romdataend(); }
unsigned char* vramdataend() const { return rambankdata_; }
unsigned char* rambankdata() const { return rambankdata_; }
unsigned char* rambankdataend() const { return wramdata_[0]; }
unsigned char* wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char* wramdataend() const { return wramdataend_; }
unsigned char const* rdisabledRam() const { return rdisabledRamw(); }
unsigned char const* rsrambankptr() const { return rsrambankptr_; }
unsigned char* wsrambankptr() const { return wsrambankptr_; }
unsigned char* vrambankptr() const { return vrambankptr_; }
OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; }
unsigned curRomBank() const { return curRomBank_; }
bool isInOamDmaConflictArea(unsigned p) const;
void setRombank0(unsigned bank);
void setRombank(unsigned bank);
void setRambank(unsigned ramFlags, unsigned rambank);
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * vrambank_size() - mm_vram_begin; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
@ -71,7 +90,7 @@ private:
unsigned char *vrambankptr_;
unsigned char *rsrambankptr_;
unsigned char *wsrambankptr_;
unsigned char *memchunk_;
SimpleArray<unsigned char> memchunk_;
unsigned char *rambankdata_;
unsigned char *wramdataend_;
OamDmaSrc oamDmaSrc_;
@ -82,18 +101,19 @@ private:
int memchunk_saveoffs;
int memchunk_savelen;
MemPtrs(MemPtrs const &);
MemPtrs & operator=(MemPtrs const &);
static std::size_t pre_rom_pad_size() { return mm_rom1_begin; }
void disconnectOamDmaAreas();
unsigned char * rdisabledRamw() const { return wramdataend_ ; }
unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; }
unsigned char * rdisabledRamw() const { return wramdataend_; }
unsigned char * wdisabledRam() const { return wramdataend_ + rambank_size(); }
public:
template<bool isReader>void SyncState(NewState *ns);
};
inline bool isCgb(MemPtrs const &memptrs) {
return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000;
inline bool isCgb(MemPtrs const& memptrs) {
int const num_cgb_wrambanks = 8;
std::size_t const wramsize = memptrs.wramdataend() - memptrs.wramdata(0);
return wramsize == num_cgb_wrambanks * wrambank_size();
}
}

View File

@ -43,7 +43,7 @@ void Time::loadState(SaveState const &state) {
lastTime_.tv_sec = state.time.lastTimeSec;
lastTime_.tv_usec = state.time.lastTimeUsec;
lastCycles_ = state.time.lastCycles;
ds_ = state.ppu.isCgb & state.mem.ioamhram.get()[0x14D] >> 7;
ds_ = state.mem.ioamhram.get()[0x14D] >> 7;
}
std::uint32_t Time::get(unsigned long const cc) {

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
#include "mem/cartridge.h"
#include "interrupter.h"
#include "sound.h"
#include "tima.h"
#include "video.h"
@ -34,7 +35,7 @@ class FilterInfo;
class Memory {
public:
explicit Memory(unsigned short &sp, unsigned short &pc);
explicit Memory(Interrupter const &interrupter);
~Memory();
bool loaded() const { return cart_.loaded(); }
@ -54,12 +55,12 @@ public:
memcpy(bios_, buffer, size);
biosSize_ = size;
}
bool gbIsCgb() { return gbIsCgb_; }
bool getMemoryArea(int which, unsigned char **data, int *length);
unsigned long stop(unsigned long cycleCounter);
unsigned long stop(unsigned long cycleCounter, bool& skip);
bool isCgb() const { return lcd_.isCgb(); }
bool isCgbDmg() const { return lcd_.isCgbDmg(); }
bool ime() const { return intreq_.ime(); }
bool halted() const { return intreq_.halted(); }
unsigned long nextEventTime() const { return intreq_.minEventTime(); }
@ -73,10 +74,14 @@ public:
return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed();
}
void halt(unsigned long cycleCounter) { halttime_ = cycleCounter; intreq_.halt(); }
void freeze(unsigned long cc);
bool halt(unsigned long cc);
void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } }
void di() { intreq_.di(); }
unsigned pendingIrqs(unsigned long cc);
void ackIrq(unsigned bit, unsigned long cc);
unsigned readBios(unsigned p) {
if(agbMode_ && p >= 0xF3 && p < 0x100) {
return (agbOverride[p-0xF3] + bios_[p]) & 0xFF;
@ -221,7 +226,7 @@ public:
unsigned long event(unsigned long cycleCounter);
unsigned long resetCounters(unsigned long cycleCounter);
LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat);
LoadRes loadROM(char const *romfiledata, unsigned romfilelength, unsigned flags);
void setInputGetter(unsigned (*getInput)()) {
getInput_ = getInput;
@ -262,6 +267,10 @@ public:
lcd_.setDmgPaletteColor(palNum, colorNum, rgb32);
}
void blackScreen() {
lcd_.blackScreen();
}
void setCgbPalette(unsigned *lut);
void setTimeMode(bool useCycles, unsigned long const cc) {
cart_.setTimeMode(useCycles, cc);
@ -282,20 +291,18 @@ private:
Tima tima_;
LCD lcd_;
PSG psg_;
Interrupter interrupter_;
unsigned short dmaSource_;
unsigned short dmaDestination_;
unsigned char oamDmaPos_;
unsigned char oamDmaStartPos_;
unsigned char serialCnt_;
bool blanklcd_;
bool biosMode_;
bool cgbSwitching_;
bool agbMode_;
bool gbIsCgb_;
unsigned short &sp_;
unsigned short &pc_;
unsigned long basetime_;
unsigned long halttime_;
bool stopped_;
enum HdmaState { hdma_low, hdma_high, hdma_requested } haltHdmaState_;
MemoryCallback readCallback_;
MemoryCallback writeCallback_;
@ -311,6 +318,7 @@ private:
void startOamDma(unsigned long cycleCounter);
void endOamDma(unsigned long cycleCounter);
unsigned char const * oamDmaSrcPtr() const;
unsigned long dma(unsigned long cc);
unsigned nontrivial_ff_read(unsigned p, unsigned long cycleCounter);
unsigned nontrivial_read(unsigned p, unsigned long cycleCounter);
void nontrivial_ff_write(unsigned p, unsigned data, unsigned long cycleCounter);

View File

@ -36,7 +36,7 @@ struct SaveState {
void set(T *p, std::size_t size) { ptr = p; size_ = size; }
friend class SaverList;
friend void setInitState(SaveState &, bool, bool);
friend void setInitState(SaveState &, bool);
private:
T *ptr;
std::size_t size_;
@ -54,6 +54,8 @@ struct SaveState {
unsigned char f;
unsigned char h;
unsigned char l;
unsigned char opcode;
unsigned char /*bool*/ prefetched;
unsigned char /*bool*/ skip;
} cpu;
@ -63,28 +65,24 @@ struct SaveState {
Ptr<unsigned char> wram;
Ptr<unsigned char> ioamhram;
unsigned long divLastUpdate;
unsigned long timaBasetime;
unsigned long timaLastUpdate;
unsigned long tmatime;
unsigned long nextSerialtime;
unsigned long lastOamDmaUpdate;
unsigned long minIntTime;
unsigned long unhaltTime;
unsigned long halttime;
unsigned short rombank;
unsigned short dmaSource;
unsigned short dmaDestination;
unsigned char rambank;
unsigned char oamDmaPos;
unsigned char haltHdmaState;
unsigned char /*bool*/ IME;
unsigned char /*bool*/ halted;
unsigned char /*bool*/ enableRam;
unsigned char /*bool*/ rambankMode;
unsigned char /*bool*/ hdmaTransfer;
unsigned char /*bool*/ biosMode;
unsigned char /*bool*/ cgbSwitching;
unsigned char /*bool*/ agbMode;
unsigned char /*bool*/ gbIsCgb;
unsigned char /*bool*/ stopped;
} mem;
@ -121,7 +119,7 @@ struct SaveState {
unsigned char wscx;
unsigned char /*bool*/ weMaster;
unsigned char /*bool*/ pendingLcdstatIrq;
unsigned char /*bool*/ isCgb;
unsigned char /*bool*/ notCgbDmg;
} ppu;
struct SPU {
@ -147,7 +145,7 @@ struct SaveState {
unsigned long counter;
unsigned short shadow;
unsigned char nr0;
unsigned char /*bool*/ negging;
unsigned char /*bool*/ neg;
} sweep;
Duty duty;
Env env;
@ -188,6 +186,7 @@ struct SaveState {
} ch4;
unsigned long cycleCounter;
unsigned char lastUpdate;
} spu;
struct Time {

View File

@ -43,12 +43,13 @@ Clock) clock timer on transition to step.
*/
namespace gambatte {
using namespace gambatte;
PSG::PSG()
: buffer_(0)
, bufferPos_(0)
, lastUpdate_(0)
, cycleCounter_(0)
, soVol_(0)
, rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later
, enabled_(false)
@ -60,11 +61,41 @@ void PSG::init(bool cgb) {
ch3_.init(cgb);
}
void PSG::reset() {
void PSG::reset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ = (cc & 0xFFF) + 2 * (~(cc + 1 + !ds) & 0x800);
lastUpdate_ = ((lastUpdate_ + 3) & -4) - !ds;
ch1_.reset();
ch2_.reset();
ch3_.reset();
ch4_.reset();
ch4_.reset(cycleCounter_);
}
void PSG::divReset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
cycleCounter_ = (cc & -0x1000) + 2 * (cc & 0x800) - divOffset;
ch1_.resetCc(cc - divOffset, cycleCounter_);
ch2_.resetCc(cc - divOffset, cycleCounter_);
ch3_.resetCc(cc - divOffset, cycleCounter_);
ch4_.resetCc(cc - divOffset, cycleCounter_);
}
void PSG::speedChange(unsigned long const cpuCc, bool const ds) {
generateSamples(cpuCc, ds);
lastUpdate_ -= ds;
// correct for cycles since DIV reset (if any).
if (!ds) {
unsigned long const cc = cycleCounter_;
unsigned const divCycles = cc & 0xFFF;
cycleCounter_ = cc - divCycles / 2 - lastUpdate_ % 2;
ch1_.resetCc(cc, cycleCounter_);
ch2_.resetCc(cc, cycleCounter_);
ch3_.resetCc(cc, cycleCounter_);
ch4_.resetCc(cc, cycleCounter_);
}
}
void PSG::setStatePtrs(SaveState &state) {
@ -77,23 +108,26 @@ void PSG::loadState(SaveState const &state) {
ch3_.loadState(state);
ch4_.loadState(state);
lastUpdate_ = state.cpu.cycleCounter;
cycleCounter_ = state.spu.cycleCounter;
lastUpdate_ = state.cpu.cycleCounter - (1 - state.spu.lastUpdate) % 4u;
setSoVolume(state.mem.ioamhram.get()[0x124]);
mapSo(state.mem.ioamhram.get()[0x125]);
enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1;
}
void PSG::accumulateChannels(unsigned long const cycles) {
uint_least32_t *const buf = buffer_ + bufferPos_;
std::memset(buf, 0, cycles * sizeof *buf);
ch1_.update(buf, soVol_, cycles);
ch2_.update(buf, soVol_, cycles);
ch3_.update(buf, soVol_, cycles);
ch4_.update(buf, soVol_, cycles);
inline void PSG::accumulateChannels(unsigned long const cycles) {
unsigned long const cc = cycleCounter_;
uint_least32_t* const buf = buffer_ + bufferPos_;
std::memset(buf, 0, cycles * sizeof * buf);
ch1_.update(buf, soVol_, cc, cc + cycles);
ch2_.update(buf, soVol_, cc, cc + cycles);
ch3_.update(buf, soVol_, cc, cc + cycles);
ch4_.update(buf, soVol_, cc, cc + cycles);
cycleCounter_ = (cc + cycles) % SoundUnit::counter_max;
}
void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) {
unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed);
void PSG::generateSamples(unsigned long const cpuCc, bool const doubleSpeed) {
unsigned long const cycles = (cpuCc - lastUpdate_) >> (1 + doubleSpeed);
lastUpdate_ += cycles << (1 + doubleSpeed);
if (cycles)
@ -168,10 +202,10 @@ void PSG::setSoVolume(unsigned nr50) {
void PSG::mapSo(unsigned nr51) {
unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul();
ch1_.setSo((so & 0x00010001) * 0xFFFF);
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF);
ch1_.setSo((so & 0x00010001) * 0xFFFF, cycleCounter_);
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF, cycleCounter_);
ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF);
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF);
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF, cycleCounter_);
}
unsigned PSG::getStatus() const {
@ -189,9 +223,8 @@ SYNCFUNC(PSG)
SSS(ch3_);
SSS(ch4_);
NSS(lastUpdate_);
NSS(cycleCounter_);
NSS(soVol_);
NSS(rsum_);
NSS(enabled_);
}
}

View File

@ -31,12 +31,14 @@ class PSG {
public:
PSG();
void init(bool cgb);
void reset();
void reset(bool ds);
void divReset(bool ds);
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state);
void generateSamples(unsigned long cycleCounter, bool doubleSpeed);
void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed);
void speedChange(unsigned long cc, bool doubleSpeed);
std::size_t fillBuffer();
void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; }
@ -44,28 +46,28 @@ public:
void setEnabled(bool value) { enabled_ = value; }
void setNr10(unsigned data) { ch1_.setNr0(data); }
void setNr11(unsigned data) { ch1_.setNr1(data); }
void setNr12(unsigned data) { ch1_.setNr2(data); }
void setNr13(unsigned data) { ch1_.setNr3(data); }
void setNr14(unsigned data) { ch1_.setNr4(data); }
void setNr11(unsigned data) { ch1_.setNr1(data, cycleCounter_); }
void setNr12(unsigned data) { ch1_.setNr2(data, cycleCounter_); }
void setNr13(unsigned data) { ch1_.setNr3(data, cycleCounter_); }
void setNr14(unsigned data, bool ds) { ch1_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr21(unsigned data) { ch2_.setNr1(data); }
void setNr22(unsigned data) { ch2_.setNr2(data); }
void setNr23(unsigned data) { ch2_.setNr3(data); }
void setNr24(unsigned data) { ch2_.setNr4(data); }
void setNr21(unsigned data) { ch2_.setNr1(data, cycleCounter_); }
void setNr22(unsigned data) { ch2_.setNr2(data, cycleCounter_); }
void setNr23(unsigned data) { ch2_.setNr3(data, cycleCounter_); }
void setNr24(unsigned data, bool ds) { ch2_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr30(unsigned data) { ch3_.setNr0(data); }
void setNr31(unsigned data) { ch3_.setNr1(data); }
void setNr31(unsigned data) { ch3_.setNr1(data, cycleCounter_); }
void setNr32(unsigned data) { ch3_.setNr2(data); }
void setNr33(unsigned data) { ch3_.setNr3(data); }
void setNr34(unsigned data) { ch3_.setNr4(data); }
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index); }
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data); }
void setNr34(unsigned data) { ch3_.setNr4(data, cycleCounter_); }
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index, cycleCounter_); }
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data, cycleCounter_); }
void setNr41(unsigned data) { ch4_.setNr1(data); }
void setNr42(unsigned data) { ch4_.setNr2(data); }
void setNr43(unsigned data) { ch4_.setNr3(data); }
void setNr44(unsigned data) { ch4_.setNr4(data); }
void setNr41(unsigned data) { ch4_.setNr1(data, cycleCounter_); }
void setNr42(unsigned data) { ch4_.setNr2(data, cycleCounter_); }
void setNr43(unsigned data) { ch4_.setNr3(data, cycleCounter_); }
void setNr44(unsigned data) { ch4_.setNr4(data, cycleCounter_); }
void setSoVolume(unsigned nr50);
void mapSo(unsigned nr51);
@ -79,6 +81,7 @@ private:
uint_least32_t *buffer_;
std::size_t bufferPos_;
unsigned long lastUpdate_;
unsigned long cycleCounter_;
unsigned long soVol_;
uint_least32_t rsum_;
bool enabled_;

View File

@ -17,30 +17,28 @@
//
#include "channel1.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit)
: disableMaster_(disabler)
, dutyUnit_(dutyUnit)
, shadow_(0)
, nr0_(0)
, negging_(false)
, neg_(false)
, cgb_(false)
{
}
unsigned Channel1::SweepUnit::calcFreq() {
unsigned freq = shadow_ >> (nr0_ & 0x07);
if (nr0_ & 0x08) {
freq = shadow_ - freq;
negging_ = true;
} else
freq = shadow_ + freq;
unsigned const freq = nr0_ & psg_nr10_neg
? shadow_ - (shadow_ >> (nr0_ & psg_nr10_rsh))
: shadow_ + (shadow_ >> (nr0_ & psg_nr10_rsh));
if (nr0_ & psg_nr10_neg)
neg_ = true;
if (freq & 2048)
disableMaster_();
@ -48,12 +46,12 @@ unsigned Channel1::SweepUnit::calcFreq() {
}
void Channel1::SweepUnit::event() {
unsigned long const period = nr0_ >> 4 & 0x07;
unsigned long const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
if (period) {
unsigned const freq = calcFreq();
if (!(freq & 2048) && (nr0_ & 0x07)) {
if (!(freq & 2048) && nr0_ & psg_nr10_rsh) {
shadow_ = freq;
dutyUnit_.setFreq(freq, counter_);
calcFreq();
@ -65,25 +63,25 @@ void Channel1::SweepUnit::event() {
}
void Channel1::SweepUnit::nr0Change(unsigned newNr0) {
if (negging_ && !(newNr0 & 0x08))
if (neg_ && !(newNr0 & 0x08))
disableMaster_();
nr0_ = newNr0;
}
void Channel1::SweepUnit::nr4Init(unsigned long const cc) {
negging_ = false;
neg_ = false;
shadow_ = dutyUnit_.freq();
unsigned const period = nr0_ >> 4 & 0x07;
unsigned const shift = nr0_ & 0x07;
unsigned const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
unsigned const rsh = nr0_ & psg_nr10_rsh;
if (period | shift)
if (period | rsh)
counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2;
else
counter_ = counter_disabled;
if (shift)
if (rsh)
calcFreq();
}
@ -95,7 +93,7 @@ void Channel1::SweepUnit::loadState(SaveState const &state) {
counter_ = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow_ = state.spu.ch1.sweep.shadow;
nr0_ = state.spu.ch1.sweep.nr0;
negging_ = state.spu.ch1.sweep.negging;
neg_ = state.spu.ch1.sweep.neg;
}
template<bool isReader>
@ -104,7 +102,7 @@ void Channel1::SweepUnit::SyncState(NewState *ns)
NSS(counter_);
NSS(shadow_);
NSS(nr0_);
NSS(negging_);
NSS(neg_);
NSS(cgb_);
}
@ -115,7 +113,6 @@ Channel1::Channel1()
, envelopeUnit_(staticOutputTest_)
, sweepUnit_(disableMaster_, dutyUnit_)
, nextEventUnit_(0)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@ -137,52 +134,48 @@ void Channel1::setNr0(unsigned data) {
setEvent();
}
void Channel1::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
dutyUnit_.nr1Change(data, cycleCounter_);
void Channel1::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel1::setNr2(unsigned data) {
void Channel1::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel1::setNr3(unsigned data) {
dutyUnit_.nr3Change(data, cycleCounter_);
void Channel1::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel1::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel1::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
dutyUnit_.nr4Change(data, cc, ref, master_);
nr4_ = data;
dutyUnit_.nr4Change(data, cycleCounter_, master_);
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
sweepUnit_.nr4Init(cycleCounter_);
staticOutputTest_(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
sweepUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
setEvent();
}
void Channel1::setSo(unsigned long soMask) {
void Channel1::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel1::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
dutyUnit_.reset();
envelopeUnit_.reset();
sweepUnit_.reset();
@ -196,58 +189,51 @@ void Channel1::init(bool cgb) {
void Channel1::loadState(SaveState const &state) {
sweepUnit_.loadState(state);
dutyUnit_.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111],
state.spu.ch1.nr4, state.spu.cycleCounter);
state.spu.ch1.nr4, state.spu.cycleCounter);
envelopeUnit_.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch1.nr4;
master_ = state.spu.ch1.master;
}
void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel1::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cycleCounter_;
cycleCounter_ = dutyUnit_.counter();
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cycleCounter_ < nextMajorEvent) {
if (cc < nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cycleCounter_);
lengthCounter_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
sweepUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
sweepUnit_.resetCounters(cc);
}
}
@ -265,12 +251,9 @@ SYNCFUNC(Channel1)
EVS(nextEventUnit_, &lengthCounter_, 4);
EES(nextEventUnit_, NULL);
NSS(cycleCounter_);
NSS(soMask_);
NSS(prevOut_);
NSS(nr4_);
NSS(master_);
}
}

View File

@ -35,14 +35,15 @@ class Channel1 {
public:
Channel1();
void setNr0(unsigned data);
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void init(bool cgb);
void loadState(SaveState const &state);
@ -54,6 +55,7 @@ private:
void nr0Change(unsigned newNr0);
void nr4Init(unsigned long cycleCounter);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void init(bool cgb) { cgb_ = cgb; }
void loadState(SaveState const &state);
@ -62,7 +64,7 @@ private:
DutyUnit &dutyUnit_;
unsigned short shadow_;
unsigned char nr0_;
bool negging_;
bool neg_;
bool cgb_;
unsigned calcFreq();
@ -80,7 +82,6 @@ private:
EnvelopeUnit envelopeUnit_;
SweepUnit sweepUnit_;
SoundUnit *nextEventUnit_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;

View File

@ -17,17 +17,17 @@
//
#include "channel2.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
Channel2::Channel2()
: staticOutputTest_(*this, dutyUnit_)
, disableMaster_(master_, dutyUnit_)
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@ -42,51 +42,47 @@ void Channel2::setEvent() {
nextEventUnit = &lengthCounter_;
}
void Channel2::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
dutyUnit_.nr1Change(data, cycleCounter_);
void Channel2::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel2::setNr2(unsigned data) {
void Channel2::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel2::setNr3(unsigned data) {
dutyUnit_.nr3Change(data, cycleCounter_);
void Channel2::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel2::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel2::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
dutyUnit_.nr4Change(data, cycleCounter_, master_);
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
staticOutputTest_(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
dutyUnit_.nr4Change(data, cc, ref, master_);
setEvent();
}
void Channel2::setSo(unsigned long soMask) {
void Channel2::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel2::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
dutyUnit_.reset();
envelopeUnit_.reset();
setEvent();
@ -94,57 +90,50 @@ void Channel2::reset() {
void Channel2::loadState(SaveState const &state) {
dutyUnit_.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116],
state.spu.ch2.nr4, state.spu.cycleCounter);
state.spu.ch2.nr4, state.spu.cycleCounter);
envelopeUnit_.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch2.nr4;
master_ = state.spu.ch2.master;
}
void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel2::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles);
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cycleCounter_;
cycleCounter_ = dutyUnit_.counter();
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cycleCounter_ < nextMajorEvent) {
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit->counter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cycleCounter_);
lengthCounter_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
@ -160,7 +149,6 @@ SYNCFUNC(Channel2)
EVS(nextEventUnit, &lengthCounter_, 3);
EES(nextEventUnit, NULL);
NSS(cycleCounter_);
NSS(soMask_);
NSS(prevOut_);
@ -168,4 +156,3 @@ SYNCFUNC(Channel2)
NSS(master_);
}
}

View File

@ -33,14 +33,15 @@ struct SaveState;
class Channel2 {
public:
Channel2();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void loadState(SaveState const &state);
private:
@ -52,7 +53,6 @@ private:
DutyUnit dutyUnit_;
EnvelopeUnit envelopeUnit_;
SoundUnit *nextEventUnit;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;

View File

@ -17,20 +17,22 @@
//
#include "channel3.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
#include <cstring>
static inline unsigned toPeriod(unsigned nr3, unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
using namespace gambatte;
namespace gambatte {
namespace {
unsigned toPeriod(unsigned nr3, unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
}
Channel3::Channel3()
: disableMaster_(master_, waveCounter_)
, lengthCounter_(disableMaster_, 0xFF)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, waveCounter_(SoundUnit::counter_disabled)
@ -47,25 +49,22 @@ Channel3::Channel3()
}
void Channel3::setNr0(unsigned data) {
nr0_ = data & 0x80;
if (!(data & 0x80))
nr0_ = data & psg_nr4_init;
if (!nr0_)
disableMaster_();
}
void Channel3::setNr2(unsigned data) {
rshift_ = (data >> 5 & 3U) - 1;
if (rshift_ > 3)
rshift_ = 4;
rshift_ = std::min((data >> 5 & 3) - 1, 4u);
}
void Channel3::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
nr4_ = data & 0x7F;
void Channel3::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data & ~(1u * psg_nr4_init);
if (data & nr0_/* & 0x80*/) {
if (!cgb_ && waveCounter_ == cycleCounter_ + 1) {
unsigned const pos = ((wavePos_ + 1) & 0x1F) >> 1;
if (data & nr0_) {
if (!cgb_ && waveCounter_ == cc + 1) {
int const pos = (wavePos_ + 1) / 2 % sizeof waveRam_;
if (pos < 4)
waveRam_[0] = waveRam_[pos];
@ -75,7 +74,7 @@ void Channel3::setNr4(unsigned const data) {
master_ = true;
wavePos_ = 0;
lastReadTime_ = waveCounter_ = cycleCounter_ + toPeriod(nr3_, data) + 3;
lastReadTime_ = waveCounter_ = cc + toPeriod(nr3_, data) + 3;
}
}
@ -84,13 +83,15 @@ void Channel3::setSo(unsigned long soMask) {
}
void Channel3::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
sampleBuf_ = 0;
}
void Channel3::resetCc(unsigned long cc, unsigned long newCc) {
lastReadTime_ -= cc - newCc;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= cc - newCc;
}
void Channel3::init(bool cgb) {
cgb_ = cgb;
}
@ -102,16 +103,15 @@ void Channel3::setStatePtrs(SaveState &state) {
void Channel3::loadState(SaveState const &state) {
lengthCounter_.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
waveCounter_ = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
lastReadTime_ = state.spu.ch3.lastReadTime;
nr3_ = state.spu.ch3.nr3;
nr4_ = state.spu.ch3.nr4;
wavePos_ = state.spu.ch3.wavePos & 0x1F;
wavePos_ = state.spu.ch3.wavePos % (2 * sizeof waveRam_);
sampleBuf_ = state.spu.ch3.sampleBuf;
master_ = state.spu.ch3.master;
nr0_ = state.mem.ioamhram.get()[0x11A] & 0x80;
nr0_ = state.mem.ioamhram.get()[0x11A] & psg_nr4_init;
setNr2(state.mem.ioamhram.get()[0x11C]);
}
@ -122,77 +122,80 @@ void Channel3::updateWaveCounter(unsigned long const cc) {
lastReadTime_ = waveCounter_ + periods * period;
waveCounter_ = lastReadTime_ + period;
wavePos_ += periods + 1;
wavePos_ &= 0x1F;
sampleBuf_ = waveRam_[wavePos_ >> 1];
wavePos_ = (wavePos_ + periods + 1) % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
}
}
void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0;
void Channel3::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = nr0_ ? soBaseVol & soMask_ : 0;
if (outBase && rshift_ != 4) {
unsigned long const endCycles = cycleCounter_ + cycles;
for (;;) {
while (std::min(waveCounter_, lengthCounter_.counter()) <= end) {
unsigned pos = wavePos_;
unsigned const period = toPeriod(nr3_, nr4_), rsh = rshift_;
unsigned long const nextMajorEvent =
std::min(lengthCounter_.counter(), endCycles);
std::min(lengthCounter_.counter(), end);
unsigned long cnt = waveCounter_, prevOut = prevOut_;
unsigned long out = master_
? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul
: 0 - 15ul;
? ((pos % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rsh) * 2l - 15
: -15;
out *= outBase;
while (waveCounter_ <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += waveCounter_ - cycleCounter_;
cycleCounter_ = waveCounter_;
lastReadTime_ = waveCounter_;
waveCounter_ += toPeriod(nr3_, nr4_);
++wavePos_;
wavePos_ &= 0x1F;
sampleBuf_ = waveRam_[wavePos_ >> 1];
out = ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul;
while (cnt <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += cnt - cc;
cc = cnt;
cnt += period;
++pos;
unsigned const s = waveRam_[pos / 2 % sizeof waveRam_];
out = ((pos % 2 ? s & 0xF : s >> 4) >> rsh) * 2l - 15;
out *= outBase;
}
if (cycleCounter_ < nextMajorEvent) {
if (cnt != waveCounter_) {
wavePos_ = pos % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
prevOut_ = prevOut;
waveCounter_ = cnt;
lastReadTime_ = cc;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (lengthCounter_.counter() == nextMajorEvent) {
if (lengthCounter_.counter() == nextMajorEvent)
lengthCounter_.event();
} else
break;
}
} else {
unsigned long const out = outBase * (0 - 15ul);
if (cc < end) {
unsigned long out = master_
? ((wavePos_ % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rshift_) * 2l - 15
: -15;
out *= outBase;
*buf += out - prevOut_;
prevOut_ = out;
cc = end;
}
}
else {
unsigned long const out = outBase * -15;
*buf += out - prevOut_;
prevOut_ = out;
cycleCounter_ += cycles;
while (lengthCounter_.counter() <= cycleCounter_) {
cc = end;
while (lengthCounter_.counter() <= cc) {
updateWaveCounter(lengthCounter_.counter());
lengthCounter_.event();
}
updateWaveCounter(cycleCounter_);
updateWaveCounter(cc);
}
if (cycleCounter_ >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cycleCounter_);
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lastReadTime_ -= SoundUnit::counter_max;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= SoundUnit::counter_max;
lastReadTime_ -= SoundUnit::counter_max;
cycleCounter_ -= SoundUnit::counter_max;
}
}
@ -202,7 +205,6 @@ SYNCFUNC(Channel3)
SSS(lengthCounter_);
NSS(cycleCounter_);
NSS(soMask_);
NSS(prevOut_);
NSS(waveCounter_);
@ -218,5 +220,3 @@ SYNCFUNC(Channel3)
NSS(master_);
NSS(cgb_);
}
}

View File

@ -32,35 +32,37 @@ class Channel3 {
public:
Channel3();
bool isActive() const { return master_; }
bool isCgb() const { return cgb_; }
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void init(bool cgb);
void setStatePtrs(SaveState &state);
void loadState(const SaveState &state);
void loadState(SaveState const &state);
void setNr0(unsigned data);
void setNr1(unsigned data) { lengthCounter_.nr1Change(data, nr4_, cycleCounter_); }
void setNr1(unsigned data, unsigned long cc) { lengthCounter_.nr1Change(data, nr4_, cc); }
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3_ = data; }
void setNr4(unsigned data);
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
unsigned waveRamRead(unsigned index) const {
unsigned waveRamRead(unsigned index, unsigned long cc) const {
if (master_) {
if (!cgb_ && cycleCounter_ != lastReadTime_)
if (!cgb_ && cc != lastReadTime_)
return 0xFF;
index = wavePos_ >> 1;
index = wavePos_ / 2;
}
return waveRam_[index];
}
void waveRamWrite(unsigned index, unsigned data) {
void waveRamWrite(unsigned index, unsigned data, unsigned long cc) {
if (master_) {
if (!cgb_ && cycleCounter_ != lastReadTime_)
if (!cgb_ && cc != lastReadTime_)
return;
index = wavePos_ >> 1;
index = wavePos_ / 2;
}
waveRam_[index] = data;
@ -83,7 +85,6 @@ private:
unsigned char waveRam_[0x10];
Ch3MasterDisabler disableMaster_;
LengthCounter lengthCounter_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned long waveCounter_;

View File

@ -17,23 +17,26 @@
//
#include "channel4.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
static unsigned long toPeriod(unsigned const nr3) {
unsigned s = (nr3 >> 4) + 3;
unsigned r = nr3 & 7;
using namespace gambatte;
if (!r) {
r = 1;
--s;
namespace {
static unsigned long toPeriod(unsigned const nr3) {
unsigned s = nr3 / (1u * psg_nr43_s & -psg_nr43_s) + 3;
unsigned r = nr3 & psg_nr43_r;
if (!r) {
r = 1;
--s;
}
return r << s;
}
return r << s;
}
namespace gambatte {
Channel4::Lfsr::Lfsr()
: backupCounter_(counter_disabled)
, reg_(0x7FFF)
@ -48,17 +51,18 @@ void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) {
unsigned long periods = (cc - backupCounter_) / period + 1;
backupCounter_ += periods * period;
if (master_ && nr3_ < 0xE0) {
if (nr3_ & 8) {
if (master_ && nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
if (nr3_ & psg_nr43_7biten) {
while (periods > 6) {
unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E;
reg_ = (reg_ >> 6 & ~0x7E) | xored | xored << 8;
reg_ = (reg_ >> 6 & ~0x7Eu) | xored | xored << 8;
periods -= 6;
}
unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F;
reg_ = (reg_ >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
} else {
reg_ = (reg_ >> periods & ~(0x80u - (0x80 >> periods))) | xored | xored << 8;
}
else {
while (periods > 15) {
reg_ = reg_ ^ reg_ >> 1;
periods -= 15;
@ -76,13 +80,13 @@ void Channel4::Lfsr::reviveCounter(unsigned long cc) {
}
inline void Channel4::Lfsr::event() {
if (nr3_ < 0xE0) {
if (nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
unsigned const shifted = reg_ >> 1;
unsigned const xored = (reg_ ^ shifted) & 1;
reg_ = shifted | xored << 14;
if (nr3_ & 8)
reg_ = (reg_ & ~0x40) | xored << 6;
if (nr3_ & psg_nr43_7biten)
reg_ = (reg_ & ~0x40u) | xored << 6;
}
counter_ += toPeriod(nr3_);
@ -92,6 +96,7 @@ inline void Channel4::Lfsr::event() {
void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) {
updateBackupCounter(cc);
nr3_ = newNr3;
counter_ = cc;
}
void Channel4::Lfsr::nr4Init(unsigned long cc) {
@ -108,6 +113,13 @@ void Channel4::Lfsr::reset(unsigned long cc) {
backupCounter_ = cc + toPeriod(nr3_);
}
void Channel4::Lfsr::resetCc(unsigned long cc, unsigned long newCc) {
updateBackupCounter(cc);
backupCounter_ -= cc - newCc;
if (counter_ != counter_disabled)
counter_ -= cc - newCc;
}
void Channel4::Lfsr::resetCounters(unsigned long oldCc) {
updateBackupCounter(oldCc);
backupCounter_ -= counter_max;
@ -137,7 +149,6 @@ Channel4::Channel4()
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, nextEventUnit_(0)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@ -152,49 +163,44 @@ void Channel4::setEvent() {
nextEventUnit_ = &lengthCounter_;
}
void Channel4::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
void Channel4::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
setEvent();
}
void Channel4::setNr2(unsigned data) {
void Channel4::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel4::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel4::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
if (master_)
lfsr_.nr4Init(cycleCounter_);
lfsr_.nr4Init(cc);
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
}
setEvent();
}
void Channel4::setSo(unsigned long soMask) {
void Channel4::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel4::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
lfsr_.reset(cycleCounter_);
void Channel4::reset(unsigned long cc) {
lfsr_.reset(cc);
envelopeUnit_.reset();
setEvent();
}
@ -202,53 +208,49 @@ void Channel4::reset() {
void Channel4::loadState(SaveState const &state) {
lfsr_.loadState(state);
envelopeUnit_.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch4.nr4;
master_ = state.spu.ch4.master;
}
void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel4::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul);
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
while (cc < end) {
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2l - 15);
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = lfsr_.isHighState() ? outHigh : outLow;
while (lfsr_.counter() <= nextMajorEvent) {
if (lfsr_.counter() <= nextMajorEvent) {
Lfsr lfsr = lfsr_;
while (lfsr.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += lfsr.counter() - cc;
cc = lfsr.counter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
lfsr_ = lfsr;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += lfsr_.counter() - cycleCounter_;
cycleCounter_ = lfsr_.counter();
lfsr_.event();
out = lfsr_.isHighState() ? outHigh : outLow;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (cycleCounter_ < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cycleCounter_);
lfsr_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lfsr_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
@ -264,12 +266,9 @@ SYNCFUNC(Channel4)
EVS(nextEventUnit_, &lengthCounter_, 3);
EES(nextEventUnit_, NULL);
NSS(cycleCounter_);
NSS(soMask_);
NSS(prevOut_);
NSS(nr4_);
NSS(master_);
}
}

View File

@ -33,14 +33,15 @@ struct SaveState;
class Channel4 {
public:
Channel4();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); }
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc) { lfsr_.nr3Change(data, cc); }
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void reset();
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc) { lfsr_.resetCc(cc, newCc); }
void loadState(SaveState const &state);
private:
@ -53,6 +54,7 @@ private:
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Init(unsigned long cc);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc);
void loadState(SaveState const &state);
void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; }
void killCounter() { counter_ = counter_disabled; }
@ -87,7 +89,6 @@ private:
EnvelopeUnit envelopeUnit_;
Lfsr lfsr_;
SoundUnit *nextEventUnit_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;

View File

@ -17,17 +17,24 @@
//
#include "duty_unit.h"
#include "psgdef.h"
#include <algorithm>
static inline bool toOutState(unsigned duty, unsigned pos) {
return 0x7EE18180 >> (duty * 8 + pos) & 1;
namespace {
int const duty_pattern_len = 8;
bool toOutState(unsigned duty, unsigned pos) {
return 0x7EE18180 >> (duty * duty_pattern_len + pos) & 1;
}
unsigned toPeriod(unsigned freq) {
return (2048 - freq) * 2;
}
}
static inline unsigned toPeriod(unsigned freq) {
return (2048 - freq) * 2;
}
namespace gambatte {
using namespace gambatte;
DutyUnit::DutyUnit()
: nextPosUpdate_(counter_disabled)
@ -44,27 +51,26 @@ void DutyUnit::updatePos(unsigned long const cc) {
if (cc >= nextPosUpdate_) {
unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1;
nextPosUpdate_ += period_ * inc;
pos_ += inc;
pos_ &= 7;
pos_ = (pos_ + inc) % duty_pattern_len;
high_ = toOutState(duty_, pos_);
}
}
void DutyUnit::setCounter() {
static unsigned char const nextStateDistance[4 * 8] = {
7, 6, 5, 4, 3, 2, 1, 1,
1, 6, 5, 4, 3, 2, 1, 2,
1, 4, 3, 2, 1, 4, 3, 2,
1, 6, 5, 4, 3, 2, 1, 2
static unsigned char const nextStateDistance[][duty_pattern_len] = {
{ 7, 6, 5, 4, 3, 2, 1, 1 },
{ 1, 6, 5, 4, 3, 2, 1, 2 },
{ 1, 4, 3, 2, 1, 4, 3, 2 },
{ 1, 6, 5, 4, 3, 2, 1, 2 }
};
if (enableEvents_ && nextPosUpdate_ != counter_disabled) {
unsigned const npos = (pos_ + 1) & 7;
unsigned const npos = (pos_ + 1) % duty_pattern_len;
counter_ = nextPosUpdate_;
inc_ = nextStateDistance[duty_ * 8 + npos];
inc_ = nextStateDistance[duty_][npos];
if (toOutState(duty_, npos) == high_) {
counter_ += period_ * inc_;
inc_ = nextStateDistance[duty_ * 8 + ((npos + inc_) & 7)];
inc_ = nextStateDistance[duty_][(npos + inc_) % duty_pattern_len];
}
} else
counter_ = counter_disabled;
@ -77,16 +83,16 @@ void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) {
}
void DutyUnit::event() {
static unsigned char const inc[] = {
1, 7,
2, 6,
4, 4,
6, 2,
static unsigned char const inc[][2] = {
{ 1, 7 },
{ 2, 6 },
{ 4, 4 },
{ 6, 2 }
};
high_ ^= true;
counter_ += inc_ * period_;
inc_ = inc[duty_ * 2 + high_];
inc_ = inc[duty_][high_];
}
void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) {
@ -99,12 +105,11 @@ void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) {
setFreq((freq() & 0x700) | newNr3, cc);
}
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc,
bool const master) {
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, unsigned long const ref, bool const master) {
setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc);
if (newNr4 & 0x80) {
nextPosUpdate_ = (cc & ~1ul) + period_ + 4 - (master << 1);
if (newNr4 & psg_nr4_init) {
nextPosUpdate_ = cc - (cc - ref) % 2 + period_ + 4 - (master << 1);
setCounter();
}
}
@ -116,6 +121,15 @@ void DutyUnit::reset() {
setCounter();
}
void DutyUnit::resetCc(unsigned long cc, unsigned long newCc) {
if (nextPosUpdate_ == counter_disabled)
return;
updatePos(cc);
nextPosUpdate_ -= cc - newCc;
setCounter();
}
void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
unsigned const nr1, unsigned const nr4, unsigned long const cc) {
nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc);
@ -127,13 +141,8 @@ void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
setCounter();
}
void DutyUnit::resetCounters(unsigned long const oldCc) {
if (nextPosUpdate_ == counter_disabled)
return;
updatePos(oldCc);
nextPosUpdate_ -= counter_max;
setCounter();
void DutyUnit::resetCounters(unsigned long cc) {
resetCc(cc, cc - counter_max);
}
void DutyUnit::killCounter() {
@ -158,5 +167,3 @@ SYNCFUNC(DutyUnit)
NSS(high_);
NSS(enableEvents_);
}
}

View File

@ -34,8 +34,9 @@ public:
bool isHighState() const { return high_; }
void nr1Change(unsigned newNr1, unsigned long cc);
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Change(unsigned newNr4, unsigned long cc, bool master);
void nr4Change(unsigned newNr4, unsigned long cc, unsigned long ref, bool master);
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
void killCounter();
void reviveCounter(unsigned long cc);

View File

@ -17,9 +17,10 @@
//
#include "envelope_unit.h"
#include "psgdef.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_;
@ -41,51 +42,53 @@ void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, un
}
void EnvelopeUnit::event() {
unsigned long const period = nr2_ & 7;
unsigned long const period = nr2_ & psg_nr2_step;
if (period) {
unsigned newVol = volume_;
if (nr2_ & 8)
if (nr2_ & psg_nr2_inc)
++newVol;
else
--newVol;
if (newVol < 0x10U) {
if (newVol < 0x10) {
volume_ = newVol;
if (volume_ < 2)
volOnOffEvent_(counter_);
counter_ += period << 15;
} else
}
else
counter_ = counter_disabled;
} else
}
else
counter_ += 8ul << 15;
}
bool EnvelopeUnit::nr2Change(unsigned const newNr2) {
if (!(nr2_ & 7) && counter_ != counter_disabled)
if (!(nr2_ & psg_nr2_step) && counter_ != counter_disabled)
++volume_;
else if (!(nr2_ & 8))
else if (!(nr2_ & psg_nr2_inc))
volume_ += 2;
if ((nr2_ ^ newNr2) & 8)
if ((nr2_ ^ newNr2) & psg_nr2_inc)
volume_ = 0x10 - volume_;
volume_ &= 0xF;
nr2_ = newNr2;
return !(newNr2 & 0xF8);
return !(newNr2 & (psg_nr2_initvol | psg_nr2_inc));
}
bool EnvelopeUnit::nr4Init(unsigned long const cc) {
unsigned long period = nr2_ & 7 ? nr2_ & 7 : 8;
unsigned long period = nr2_ & psg_nr2_step ? nr2_ & psg_nr2_step : 8;
if (((cc + 2) & 0x7000) == 0x0000)
++period;
counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
volume_ = nr2_ >> 4;
return !(nr2_ & 0xF8);
volume_ = nr2_ / (1u * psg_nr2_initvol & -psg_nr2_initvol);
return !(nr2_ & (psg_nr2_initvol | psg_nr2_inc));
}
SYNCFUNC(EnvelopeUnit)
@ -94,5 +97,3 @@ SYNCFUNC(EnvelopeUnit)
NSS(nr2_);
NSS(volume_);
}
}

View File

@ -18,9 +18,10 @@
#include "length_counter.h"
#include "master_disabler.h"
#include "psgdef.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask)
: disableMaster_(disabler)
@ -38,35 +39,30 @@ void LengthCounter::event() {
void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) {
lengthCounter_ = (~newNr1 & lengthMask_) + 1;
counter_ = nr4 & 0x40
? ((cc >> 13) + lengthCounter_) << 13
: static_cast<unsigned long>(counter_disabled);
counter_ = nr4 & psg_nr4_lcen
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) {
if (counter_ != counter_disabled)
lengthCounter_ = (counter_ >> 13) - (cc >> 13);
{
unsigned dec = 0;
if (newNr4 & 0x40) {
dec = ~cc >> 12 & 1;
if (!(oldNr4 & 0x40) && lengthCounter_) {
if (!(lengthCounter_ -= dec))
disableMaster_();
}
unsigned dec = 0;
if (newNr4 & psg_nr4_lcen) {
dec = ~cc >> 12 & 1;
if (!(oldNr4 & psg_nr4_lcen) && lengthCounter_) {
if (!(lengthCounter_ -= dec))
disableMaster_();
}
if ((newNr4 & 0x80) && !lengthCounter_)
lengthCounter_ = lengthMask_ + 1 - dec;
}
if ((newNr4 & 0x40) && lengthCounter_)
counter_ = ((cc >> 13) + lengthCounter_) << 13;
else
counter_ = counter_disabled;
if (newNr4 & psg_nr4_init && !lengthCounter_)
lengthCounter_ = lengthMask_ + 1 - dec;
counter_ = newNr4 & psg_nr4_lcen && lengthCounter_
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) {
@ -79,5 +75,3 @@ SYNCFUNC(LengthCounter)
NSS(counter_);
NSS(lengthCounter_);
}
}

View File

@ -0,0 +1,31 @@
#ifndef PSGDEF_H
#define PSGDEF_H
namespace gambatte {
enum {
psg_nr10_rsh = 0x07,
psg_nr10_neg = 0x08,
psg_nr10_time = 0x70
};
enum {
psg_nr2_step = 0x07,
psg_nr2_inc = 0x08,
psg_nr2_initvol = 0xF0
};
enum {
psg_nr43_r = 0x07,
psg_nr43_7biten = 0x08,
psg_nr43_s = 0xF0
};
enum {
psg_nr4_lcen = 0x40,
psg_nr4_init = 0x80
};
}
#endif

View File

@ -19,12 +19,17 @@
#include "tima.h"
#include "savestate.h"
static unsigned char const timaClock[4] = { 10, 4, 6, 8 };
using namespace gambatte;
namespace {
unsigned char const timaClock[4] = { 10, 4, 6, 8 };
}
namespace gambatte {
Tima::Tima()
: lastUpdate_(0)
: divLastUpdate_(0)
, lastUpdate_(0)
, tmatime_(disabled_time)
, tima_(0)
, tma_(0)
@ -33,7 +38,7 @@ Tima::Tima()
}
void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) {
basetime_ = state.mem.timaBasetime;
divLastUpdate_ = state.mem.divLastUpdate - 0x100l * state.mem.ioamhram.get()[0x104];
lastUpdate_ = state.mem.timaLastUpdate;
tmatime_ = state.mem.tmatime;
tima_ = state.mem.ioamhram.get()[0x105];
@ -43,8 +48,8 @@ void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) {
unsigned long nextIrqEventTime = disabled_time;
if (tac_ & 4) {
nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter
? tmatime_
: lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3;
? tmatime_
: lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3;
}
timaIrq.setNextIrqEventTime(nextIrqEventTime);
@ -82,7 +87,6 @@ void Tima::updateTima(unsigned long const cc) {
if (tmp == 0x100) {
tmp = 0;
tmatime_ = lastUpdate_ + 3;
if (cc >= tmatime_) {
if (cc >= tmatime_ + 4)
tmatime_ = disabled_time;
@ -102,7 +106,7 @@ void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptReq
if (tmatime_ - cc < 4)
tmatime_ = disabled_time;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3);
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - data) << timaClock[tac_ & 3]) + 3);
}
tima_ = data;
@ -122,32 +126,26 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ
unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
if (tac_ & 0x04) {
updateIrq(cc, timaIrq);
updateTima(cc);
lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
tmatime_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
unsigned const inc = ~(data >> 2 & (cc - divLastUpdate_) >> (timaClock[data & 3] - 1)) & 1;
lastUpdate_ -= (inc << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (inc << (timaClock[tac_ & 3] - 1)) + 3;
if (cc >= nextIrqEventTime)
timaIrq.flagIrq();
updateTima(cc);
tmatime_ = disabled_time;
nextIrqEventTime = disabled_time;
}
if (data & 4) {
unsigned long diff = cc - basetime_;
if (agbFlag) {
unsigned long diff = cc - divLastUpdate_;
if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0)
tima_++;
}
lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]);
nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3;
lastUpdate_ = cc - ((cc - divLastUpdate_) & ((1u << timaClock[data & 3]) - 1));
nextIrqEventTime = lastUpdate_ + ((256l - tima_) << timaClock[data & 3]) + 3;
}
timaIrq.setNextIrqEventTime(nextIrqEventTime);
@ -156,12 +154,26 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ
tac_ = data;
}
void Tima::resTac(unsigned long const cc, TimaInterruptRequester timaIrq) {
basetime_ = cc;
void Tima::divReset(unsigned long cc, TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
setTac(tac_ & ~0x04, cc, timaIrq, false);
setTac(tac_ | 0x04, cc, timaIrq, false);
unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
if (cc >= nextIrqEventTime)
timaIrq.flagIrq();
updateTima(cc);
lastUpdate_ = cc;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3);
}
divLastUpdate_ = cc;
}
void Tima::speedChange(TimaInterruptRequester timaIrq) {
if ((tac_ & 0x07) >= 0x05) {
lastUpdate_ -= 4;
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - 4);
}
}
@ -173,15 +185,15 @@ unsigned Tima::tima(unsigned long cc) {
}
void Tima::doIrqEvent(TimaInterruptRequester timaIrq) {
timaIrq.flagIrq();
timaIrq.flagIrq(timaIrq.nextIrqEventTime());
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime()
+ ((256u - tma_) << timaClock[tac_ & 3]));
+ ((256l - tma_) << timaClock[tac_ & 3]));
}
SYNCFUNC(Tima)
{
NSS(lastUpdate_);
NSS(basetime_);
NSS(divLastUpdate_);
NSS(tmatime_);
NSS(tima_);
NSS(tma_);

View File

@ -27,6 +27,7 @@ class TimaInterruptRequester {
public:
explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {}
void flagIrq() const { intreq_.flagIrq(4); }
void flagIrq(unsigned long cc) const { intreq_.flagIrq(4, cc); }
unsigned long nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); }
void setNextIrqEventTime(unsigned long time) const { intreq_.setEventTime<intevent_tima>(time); }
@ -42,12 +43,14 @@ public:
void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool agbFlag);
void resTac(unsigned long cc, TimaInterruptRequester timaIrq);
void divReset(unsigned long cc, TimaInterruptRequester);
void speedChange(TimaInterruptRequester);
unsigned long divLastUpdate() const { return divLastUpdate_; }
unsigned tima(unsigned long cc);
void doIrqEvent(TimaInterruptRequester timaIrq);
private:
unsigned long basetime_;
unsigned long divLastUpdate_;
unsigned long lastUpdate_;
unsigned long tmatime_;
unsigned char tima_;

View File

@ -21,18 +21,58 @@
#include <algorithm>
#include <cstring>
namespace gambatte {
using namespace gambatte;
unsigned long LCD::gbcToRgb32(const unsigned bgr15) {
unsigned long const r = bgr15 & 0x1F;
unsigned long const g = bgr15 >> 5 & 0x1F;
unsigned long const r = bgr15 & 0x1F;
unsigned long const g = bgr15 >> 5 & 0x1F;
unsigned long const b = bgr15 >> 10 & 0x1F;
return cgbColorsRgb32_[bgr15 & 0x7FFF];
}
namespace {
// TODO: simplify cycle offsets.
long const mode1_irq_frame_cycle = 1l * lcd_vres * lcd_cycles_per_line - 2;
int const mode2_irq_line_cycle = lcd_cycles_per_line - 4;
int const mode2_irq_line_cycle_ly0 = lcd_cycles_per_line - 2;
unsigned long mode1IrqSchedule(LyCounter const& lyCounter, unsigned long cc) {
return lyCounter.nextFrameCycle(mode1_irq_frame_cycle, cc);
}
unsigned long mode2IrqSchedule(unsigned const statReg,
LyCounter const& lyCounter, unsigned long const cc) {
if (!(statReg & lcdstat_m2irqen))
return disabled_time;
unsigned long const lastM2Fc = (lcd_vres - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle;
unsigned long const ly0M2Fc = (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle_ly0;
return lyCounter.frameCycles(cc) - lastM2Fc < ly0M2Fc - lastM2Fc || (statReg & lcdstat_m0irqen)
? lyCounter.nextFrameCycle(ly0M2Fc, cc)
: lyCounter.nextLineCycle(mode2_irq_line_cycle, cc);
}
unsigned long m0TimeOfCurrentLine(
unsigned long nextLyTime,
unsigned long lastM0Time,
unsigned long nextM0Time) {
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
}
bool isHdmaPeriod(LyCounter const& lyCounter,
unsigned long m0TimeOfCurrentLy, unsigned long cc) {
return lyCounter.ly() < lcd_vres
&& cc + 3 + 3 * lyCounter.isDoubleSpeed() < lyCounter.time()
&& cc >= m0TimeOfCurrentLy;
}
} // unnamed namespace.
void LCD::setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], unsigned data) {
palette[0] = dmgColors[data & 3];
palette[0] = dmgColors[data & 3];
palette[1] = dmgColors[data >> 2 & 3];
palette[2] = dmgColors[data >> 4 & 3];
palette[3] = dmgColors[data >> 6 & 3];
@ -47,10 +87,10 @@ void LCD::setCgbPalette(unsigned *lut) {
LCD::LCD(unsigned char const *oamram, unsigned char const *vram,
VideoInterruptRequester memEventRequester)
: ppu_(nextM0Time_, oamram, vram)
, bgpData_()
, objpData_()
, eventTimes_(memEventRequester)
, statReg_(0)
, m2IrqStatReg_(0)
, m1IrqStatReg_(0)
, scanlinecallback(0)
, scanlinecallbacksl(0)
{
@ -60,7 +100,7 @@ LCD::LCD(unsigned char const *oamram, unsigned char const *vram,
std::memset(objpData_, 0, sizeof objpData_);
reset(oamram, vram, false);
setVideoBuffer(0, 160);
setVideoBuffer(0, lcd_hres);
}
void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) {
@ -69,44 +109,6 @@ void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb
refreshPalettes();
}
void LCD::setCgb(bool cgb) {
ppu_.setCgb(cgb);
}
static unsigned long mode2IrqSchedule(unsigned const statReg,
LyCounter const &lyCounter, unsigned long const cc) {
if (!(statReg & lcdstat_m2irqen))
return disabled_time;
int next = lyCounter.time() - cc;
if (lyCounter.ly() >= 143
|| (lyCounter.ly() == 142 && next <= 4)
|| (statReg & lcdstat_m0irqen)) {
next += (153u - lyCounter.ly()) * lyCounter.lineTime();
} else {
next -= 4;
if (next <= 0)
next += lyCounter.lineTime();
}
return cc + next;
}
static unsigned long m0IrqTimeFromXpos166Time(unsigned long xpos166Time, bool cgb, bool ds) {
return xpos166Time + cgb - ds;
}
static unsigned long hdmaTimeFromM0Time(unsigned long m0Time, bool ds) {
return m0Time + 1 - ds;
}
static unsigned long nextHdmaTime(unsigned long lastM0Time,
unsigned long nextM0Time, unsigned long cc, bool ds) {
return cc < hdmaTimeFromM0Time(lastM0Time, ds)
? hdmaTimeFromM0Time(lastM0Time, ds)
: hdmaTimeFromM0Time(nextM0Time, ds);
}
void LCD::setStatePtrs(SaveState &state) {
state.ppu.bgpData.set( bgpData_, sizeof bgpData_);
state.ppu.objpData.set(objpData_, sizeof objpData_);
@ -115,40 +117,35 @@ void LCD::setStatePtrs(SaveState &state) {
void LCD::loadState(SaveState const &state, unsigned char const *const oamram) {
statReg_ = state.mem.ioamhram.get()[0x141];
m2IrqStatReg_ = statReg_;
m1IrqStatReg_ = statReg_;
ppu_.loadState(state, oamram);
lycIrq_.loadState(state);
m0Irq_.loadState(state);
mstatIrq_.loadState(state);
if (ppu_.lcdc() & lcdc_en) {
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now());
eventTimes_.setm<memevent_oneshot_statirq>(
state.ppu.pendingLcdstatIrq
eventTimes_.setm<memevent_oneshot_statirq>(state.ppu.pendingLcdstatIrq
? ppu_.now() + 1
: static_cast<unsigned long>(disabled_time));
: 1 * disabled_time);
eventTimes_.setm<memevent_oneshot_updatewy2>(
state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu_.now() + 1
: static_cast<unsigned long>(disabled_time));
state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu_.now() + 2 - isDoubleSpeed()
: 1 * disabled_time);
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(
ppu_.lyCounter().nextFrameCycle(144 * 456, ppu_.now()) - 2);
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? ppu_.now() + state.ppu.nextM0Irq
: static_cast<unsigned long>(disabled_time));
: 1 * disabled_time);
eventTimes_.setm<memevent_hdma>(state.mem.hdmaTransfer
? nextHdmaTime(ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(),
ppu_.now(), isDoubleSpeed())
: static_cast<unsigned long>(disabled_time));
? nextM0Time_.predictedNextM0Time()
: 1 * disabled_time);
} else for (int i = 0; i < num_memevents; ++i)
eventTimes_.set(MemEvent(i), disabled_time);
@ -156,10 +153,10 @@ void LCD::loadState(SaveState const &state, unsigned char const *const oamram) {
}
void LCD::refreshPalettes() {
if (ppu_.cgb()) {
for (unsigned i = 0; i < 8 * 8; i += 2) {
ppu_.bgPalette()[i >> 1] = gbcToRgb32( bgpData_[i] | bgpData_[i + 1] << 8);
ppu_.spPalette()[i >> 1] = gbcToRgb32(objpData_[i] | objpData_[i + 1] << 8);
if (isCgb() && !isCgbDmg()) {
for (int i = 0; i < max_num_palettes * num_palette_entries; ++i) {
ppu_.bgPalette()[i] = gbcToRgb32(bgpData_[2 * i] | bgpData_[2 * i + 1] * 0x100l);
ppu_.spPalette()[i] = gbcToRgb32(objpData_[2 * i] | objpData_[2 * i + 1] * 0x100l);
}
} else {
setDmgPalette(ppu_.bgPalette() , dmgColorsRgb32_ , bgpData_[0]);
@ -180,7 +177,7 @@ void LCD::copyCgbPalettesToDmg() {
namespace {
template<typename T>
static void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) {
void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) {
unsigned lines = 144;
while (lines--) {
@ -200,6 +197,12 @@ void LCD::updateScreen(bool const blanklcd, unsigned long const cycleCounter) {
}
}
void LCD::blackScreen() {
if (ppu_.frameBuf().fb()) {
clear(ppu_.frameBuf().fb(), gbcToRgb32(0x0000), ppu_.frameBuf().pitch());
}
}
void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) {
update(oldCc);
ppu_.resetCc(oldCc, newCc);
@ -221,72 +224,45 @@ void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) {
void LCD::speedChange(unsigned long const cc) {
update(cc);
ppu_.speedChange(cc);
ppu_.speedChange();
if (ppu_.lcdc() & lcdc_en) {
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), cc);
lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now());
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2);
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
if (eventTimes_(memevent_m0irq) != disabled_time
&& eventTimes_(memevent_m0irq) - cc > 1) {
eventTimes_.setm<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
if (eventTimes_(memevent_m0irq) != disabled_time) {
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled() && eventTimes_(memevent_hdma) - cc > 1) {
eventTimes_.setm<memevent_hdma>(nextHdmaTime(ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed()));
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
}
}
static unsigned long m0TimeOfCurrentLine(
unsigned long nextLyTime,
unsigned long lastM0Time,
unsigned long nextM0Time) {
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
}
unsigned long LCD::m0TimeOfCurrentLine(unsigned long const cc) {
if (cc >= nextM0Time_.predictedNextM0Time()) {
update(cc);
nextM0Time_.predictNextM0Time(ppu_);
}
return gambatte::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time());
return ::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time());
}
static bool isHdmaPeriod(LyCounter const &lyCounter,
unsigned long m0TimeOfCurrentLy, unsigned long cc) {
int timeToNextLy = lyCounter.time() - cc;
return lyCounter.ly() < 144 && timeToNextLy > 4
&& cc >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
}
void LCD::enableHdma(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
void LCD::enableHdma(unsigned long const cycleCounter) {
if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
update(cycleCounter);
nextM0Time_.predictNextM0Time(ppu_);
} else if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
unsigned long const m0TimeCurLy =
gambatte::m0TimeOfCurrentLine(ppu_.lyCounter().time(),
ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time());
if (isHdmaPeriod(ppu_.lyCounter(), m0TimeCurLy, cycleCounter))
if (::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc + 4))
eventTimes_.flagHdmaReq();
eventTimes_.setm<memevent_hdma>(nextHdmaTime(
ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(),
cycleCounter, isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
void LCD::disableHdma(unsigned long const cycleCounter) {
@ -296,14 +272,33 @@ void LCD::disableHdma(unsigned long const cycleCounter) {
eventTimes_.setm<memevent_hdma>(disabled_time);
}
bool LCD::vramAccessible(unsigned long const cc) {
bool LCD::isHdmaPeriod(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return ::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc);
}
bool LCD::vramReadable(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= 144
|| ppu_.lyCounter().lineCycles(cc) < 80U
|| cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc);
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed())
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 76u + 3 * ppu_.cgb()
|| cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::vramWritable(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed())
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 79
|| cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::cgbpAccessible(unsigned long const cc) {
@ -311,9 +306,10 @@ bool LCD::cgbpAccessible(unsigned long const cc) {
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= 144
|| ppu_.lyCounter().lineCycles(cc) < 80U + isDoubleSpeed()
|| cc >= m0TimeOfCurrentLine(cc) + 3 - isDoubleSpeed();
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc)
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 80
|| cc >= m0TimeOfCurrentLine(cc) + 2;
}
void LCD::doCgbColorChange(unsigned char *pdata,
@ -338,71 +334,67 @@ void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned long cc) {
}
bool LCD::oamReadable(unsigned long const cc) {
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc))
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4))
return true;
if (cc >= eventTimes_.nextEventTime())
update(cc);
if (ppu_.lyCounter().lineCycles(cc) + 4 - isDoubleSpeed() * 3u >= 456)
return ppu_.lyCounter().ly() >= 144-1 && ppu_.lyCounter().ly() != 153;
if (ppu_.lyCounter().lineCycles(cc) + 4 - isDoubleSpeed() >= lcd_cycles_per_line)
return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1;
return ppu_.lyCounter().ly() >= 144
|| cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc);
return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::oamWritable(unsigned long const cc) {
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc))
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4 + isDoubleSpeed()))
return true;
if (cc >= eventTimes_.nextEventTime())
update(cc);
if (ppu_.lyCounter().lineCycles(cc) + 3 + ppu_.cgb() - isDoubleSpeed() * 2u >= 456)
return ppu_.lyCounter().ly() >= 144-1 && ppu_.lyCounter().ly() != 153;
if (ppu_.lyCounter().lineCycles(cc) + 3 + ppu_.cgb() >= lcd_cycles_per_line)
return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1;
return ppu_.lyCounter().ly() >= 144
|| cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc);
return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc)
|| (ppu_.lyCounter().lineCycles(cc) == 76 && !ppu_.cgb());
}
void LCD::mode3CyclesChange() {
bool const ds = isDoubleSpeed();
nextM0Time_.invalidatePredictedNextM0Time();
if (eventTimes_(memevent_m0irq) != disabled_time
&& eventTimes_(memevent_m0irq)
> m0IrqTimeFromXpos166Time(ppu_.now(), ppu_.cgb(), ds)) {
unsigned long t = m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166),
ppu_.cgb(), ds);
&& eventTimes_(memevent_m0irq) > ppu_.now()) {
unsigned long t = ppu_.predictedNextXposTime(lcd_hres + 6);
eventTimes_.setm<memevent_m0irq>(t);
}
if (eventTimes_(memevent_hdma) != disabled_time
&& eventTimes_(memevent_hdma) > hdmaTimeFromM0Time(ppu_.lastM0Time(), ds)) {
&& eventTimes_(memevent_hdma) > ppu_.lastM0Time()) {
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(
hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), ds));
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
}
void LCD::wxChange(unsigned newValue, unsigned long cycleCounter) {
update(cycleCounter + isDoubleSpeed() + 1);
update(cycleCounter + 1 + ppu_.cgb());
ppu_.setWx(newValue);
mode3CyclesChange();
}
void LCD::wyChange(unsigned const newValue, unsigned long const cc) {
update(cc + 1);
ppu_.setWy(newValue);
update(cc + 1 + ppu_.cgb());
ppu_.setWy(newValue);
// mode3CyclesChange();
// (should be safe to wait until after wy2 delay, because no mode3 events are
// close to when wy1 is read.)
// wy2 is a delayed version of wy. really just slowness of ly == wy comparison.
// wy2 is a delayed version of wy for convenience (is this really simpler?).
if (ppu_.cgb() && (ppu_.lcdc() & lcdc_en)) {
eventTimes_.setm<memevent_oneshot_updatewy2>(cc + 5);
} else {
eventTimes_.setm<memevent_oneshot_updatewy2>(cc + 6 - isDoubleSpeed());
}
else {
update(cc + 2);
ppu_.updateWy2();
mode3CyclesChange();
@ -410,13 +402,13 @@ void LCD::wyChange(unsigned const newValue, unsigned long const cc) {
}
void LCD::scxChange(unsigned newScx, unsigned long cycleCounter) {
update(cycleCounter + ppu_.cgb() + isDoubleSpeed());
update(cycleCounter + 2 * ppu_.cgb());
ppu_.setScx(newScx);
mode3CyclesChange();
}
void LCD::scyChange(unsigned newValue, unsigned long cycleCounter) {
update(cycleCounter + ppu_.cgb() + isDoubleSpeed());
update(cycleCounter + 2 * ppu_.cgb());
ppu_.setScy(newValue);
}
@ -438,18 +430,14 @@ void LCD::oamChange(unsigned char const *oamram, unsigned long cc) {
void LCD::lcdcChange(unsigned const data, unsigned long const cc) {
unsigned const oldLcdc = ppu_.lcdc();
update(cc);
if ((oldLcdc ^ data) & lcdc_en) {
update(cc);
ppu_.setLcdc(data, cc);
if (data & lcdc_en) {
lycIrq_.lcdReset();
m0Irq_.lcdReset(statReg_, lycIrq_.lycReg());
if (lycIrq_.lycReg() == 0 && (statReg_ & lcdstat_lycirqen))
eventTimes_.flagIrq(2);
mstatIrq_.lcdReset(lycIrq_.lycReg());
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), cc);
@ -457,48 +445,49 @@ void LCD::lcdcChange(unsigned const data, unsigned long const cc) {
eventTimes_.setm<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(
ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2);
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc));
if (statReg_ & lcdstat_m0irqen) {
eventTimes_.setm<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(nextHdmaTime(ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
} else for (int i = 0; i < num_memevents; ++i)
}
else for (int i = 0; i < num_memevents; ++i)
eventTimes_.set(MemEvent(i), disabled_time);
} else if (data & lcdc_en) {
}
else if (data & lcdc_en) {
if (ppu_.cgb()) {
ppu_.setLcdc( (oldLcdc & ~(lcdc_tdsel | lcdc_obj2x))
| (data & (lcdc_tdsel | lcdc_obj2x)), cc);
update(cc + 1);
ppu_.setLcdc((oldLcdc & ~(1u * lcdc_tdsel)) | (data & lcdc_tdsel), cc + 1);
update(cc + 2);
ppu_.setLcdc(data, cc + 2);
if ((oldLcdc ^ data) & lcdc_obj2x) {
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc);
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2);
eventTimes_.setm<memevent_spritemap>(t);
}
update(cc + isDoubleSpeed() + 1);
ppu_.setLcdc(data, cc + isDoubleSpeed() + 1);
if ((oldLcdc ^ data) & lcdc_we)
mode3CyclesChange();
} else {
ppu_.setLcdc(data, cc);
}
else {
update(cc);
ppu_.setLcdc((oldLcdc & lcdc_obj2x) | (data & ~(1u * lcdc_obj2x)), cc);
if ((oldLcdc ^ data) & lcdc_obj2x) {
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc);
update(cc + 2);
ppu_.setLcdc(data, cc + 2);
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2);
eventTimes_.setm<memevent_spritemap>(t);
}
if ((oldLcdc ^ data) & (lcdc_we | lcdc_objen))
mode3CyclesChange();
}
} else
}
else {
update(cc);
ppu_.setLcdc(data, cc);
}
}
namespace {
@ -508,98 +497,116 @@ struct LyCnt {
LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {}
};
static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
unsigned ly = lyCounter.ly();
int timeToNextLy = lyCounter.time() - cc;
if (ly == 153) {
if (timeToNextLy - (448 << lyCounter.isDoubleSpeed()) > 0) {
timeToNextLy -= (448 << lyCounter.isDoubleSpeed());
} else {
ly = 0;
timeToNextLy += lyCounter.lineTime();
}
if (ly == lcd_lines_per_frame - 1) {
int const lineTime = lyCounter.lineTime();
if ((timeToNextLy -= (lineTime - 6 - 6 * lyCounter.isDoubleSpeed())) <= 0)
ly = 0, timeToNextLy += lineTime;
}
else if ((timeToNextLy -= (2 + 2 * lyCounter.isDoubleSpeed())) <= 0)
++ly, timeToNextLy += lyCounter.lineTime();
return LyCnt(ly, timeToNextLy);
}
} // anon ns
bool statChangeTriggersM2IrqCgb(unsigned const old,
unsigned const data, int const ly, int const timeToNextLy, bool const ds) {
if ((old & lcdstat_m2irqen)
|| (data & (lcdstat_m2irqen | lcdstat_m0irqen)) != lcdstat_m2irqen) {
return false;
}
if (ly < lcd_vres - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 2;
if (ly == lcd_vres - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 4 + 2 * ds;
if (ly == lcd_lines_per_frame - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle_ly0) * (1 + ds) && timeToNextLy > 2;
return false;
}
unsigned incLy(unsigned ly) { return ly == lcd_lines_per_frame - 1 ? 0 : ly + 1; }
} // unnamed namespace.
inline bool LCD::statChangeTriggersStatIrqDmg(unsigned const old, unsigned long const cc) {
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (ppu_.lyCounter().ly() < 144) {
if (cc + 1 < m0TimeOfCurrentLine(cc))
if (ppu_.lyCounter().ly() < lcd_vres) {
int const m0_cycles_upper_bound = lcd_cycles_per_line - 80 - 160;
unsigned long m0IrqTime = eventTimes_(memevent_m0irq);
if (m0IrqTime == disabled_time && ppu_.lyCounter().time() - cc < m0_cycles_upper_bound) {
update(cc);
m0IrqTime = ppu_.predictedNextXposTime(lcd_hres + 6);
}
if (m0IrqTime == disabled_time || m0IrqTime < ppu_.lyCounter().time())
return lycCmp.ly == lycIrq_.lycReg() && !(old & lcdstat_lycirqen);
return !(old & lcdstat_m0irqen)
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
}
return !(old & lcdstat_m1irqen)
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
}
static bool statChangeTriggersM2IrqCgb(unsigned const old,
unsigned const data, unsigned const ly, int const timeToNextLy) {
if ((old & lcdstat_m2irqen)
|| (data & (lcdstat_m2irqen | lcdstat_m0irqen)) != lcdstat_m2irqen
|| ly >= 144) {
return false;
}
return timeToNextLy == 456 * 2
|| (timeToNextLy <= 4 && ly < 143);
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
}
inline bool LCD::statChangeTriggersM0LycOrM1StatIrqCgb(
unsigned const old, unsigned const data, unsigned long const cc) {
unsigned const ly = ppu_.lyCounter().ly();
unsigned const old, unsigned const data, bool const lycperiod,
unsigned long const cc) {
int const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
bool const lycperiod = lycCmp.ly == lycIrq_.lycReg()
&& lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4;
if (lycperiod && (old & lcdstat_lycirqen))
return false;
bool const ds = isDoubleSpeed();
int const m1_irq_lc_inv = lcd_cycles_per_line - mode1_irq_frame_cycle % lcd_cycles_per_line;
if (ly < 144) {
if (cc + isDoubleSpeed() * 2 < m0TimeOfCurrentLine(cc) || timeToNextLy <= 4)
if (ly < lcd_vres - 1 || (ly == lcd_vres - 1 && timeToNextLy > m1_irq_lc_inv* (1 + ds))) {
if (eventTimes_(memevent_m0irq) < ppu_.lyCounter().time()
|| timeToNextLy <= (ly < lcd_vres - 1 ? 4 + 4 * ds : 4 + 2 * ds)) {
return lycperiod && (data & lcdstat_lycirqen);
}
if (old & lcdstat_m0irqen)
return false;
return (data & lcdstat_m0irqen)
|| (lycperiod && (data & lcdstat_lycirqen));
|| (lycperiod && (data & lcdstat_lycirqen));
}
if (old & lcdstat_m1irqen)
if (old & lcdstat_m1irqen && (ly < lcd_lines_per_frame - 1 || timeToNextLy > 3 + 3 * ds))
return false;
return ((data & lcdstat_m1irqen) && (ly < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4))
|| (lycperiod && (data & lcdstat_lycirqen));
return ((data & lcdstat_m1irqen)
&& (ly < lcd_lines_per_frame - 1 || timeToNextLy > 4 + 2 * ds))
|| (lycperiod && (data & lcdstat_lycirqen));
}
inline bool LCD::statChangeTriggersStatIrqCgb(
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(data & ~old & ( lcdstat_lycirqen
| lcdstat_m2irqen
| lcdstat_m1irqen
| lcdstat_m0irqen))) {
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(data & ~old & (lcdstat_lycirqen
| lcdstat_m2irqen
| lcdstat_m1irqen
| lcdstat_m0irqen))) {
return false;
}
unsigned const ly = ppu_.lyCounter().ly();
int const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
return statChangeTriggersM0LycOrM1StatIrqCgb(old, data, cc)
|| statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy);
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
bool const lycperiod = lycCmp.ly == lycIrq_.lycReg()
&& lycCmp.timeToNextLy > 2;
if (lycperiod && (old & lcdstat_lycirqen))
return false;
return statChangeTriggersM0LycOrM1StatIrqCgb(old, data, lycperiod, cc)
|| statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy, isDoubleSpeed());
}
inline bool LCD::statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc) {
return ppu_.cgb()
? statChangeTriggersStatIrqCgb(old, data, cc)
: statChangeTriggersStatIrqDmg(old, cc);
? statChangeTriggersStatIrqCgb(old, data, cc)
: statChangeTriggersStatIrqDmg(old, cc);
}
void LCD::lcdstatChange(unsigned const data, unsigned long const cc) {
@ -611,56 +618,45 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cc) {
lycIrq_.statRegChange(data, ppu_.lyCounter(), cc);
if (ppu_.lcdc() & lcdc_en) {
if (statChangeTriggersStatIrq(old, data, cc))
eventTimes_.flagIrq(2);
if ((data & lcdstat_m0irqen) && eventTimes_(memevent_m0irq) == disabled_time) {
update(cc);
eventTimes_.setm<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(data, ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
if (statChangeTriggersStatIrq(old, data, cc))
eventTimes_.flagIrq(2);
}
m2IrqStatReg_ = eventTimes_(memevent_m2irq) - cc > (ppu_.cgb() - isDoubleSpeed()) * 4U
? data
: (m2IrqStatReg_ & lcdstat_m1irqen) | (statReg_ & ~lcdstat_m1irqen);
m1IrqStatReg_ = eventTimes_(memevent_m1irq) - cc > (ppu_.cgb() - isDoubleSpeed()) * 4U
? data
: (m1IrqStatReg_ & lcdstat_m0irqen) | (statReg_ & ~lcdstat_m0irqen);
m0Irq_.statRegChange(data, eventTimes_(memevent_m0irq), cc, ppu_.cgb());
mstatIrq_.statRegChange(data, eventTimes_(memevent_m0irq), eventTimes_(memevent_m1irq),
eventTimes_(memevent_m2irq), cc, ppu_.cgb());
}
static unsigned incLy(unsigned ly) { return ly == 153 ? 0 : ly + 1; }
inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long const cc) {
inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc) {
int const timeToNextLy = ppu_.lyCounter().time() - cc;
if (ppu_.lyCounter().ly() < 144) {
if (ppu_.lyCounter().ly() < lcd_vres) {
return (statReg_ & lcdstat_m0irqen)
&& cc >= m0TimeOfCurrentLine(cc)
&& timeToNextLy > 4 << ppu_.cgb();
&& eventTimes_(memevent_m0irq) > ppu_.lyCounter().time()
&& data == ppu_.lyCounter().ly();
}
return (statReg_ & lcdstat_m1irqen)
&& !(ppu_.lyCounter().ly() == 153
&& timeToNextLy <= 4
&& ppu_.cgb() && !isDoubleSpeed());
&& !(ppu_.lyCounter().ly() == lcd_lines_per_frame - 1
&& timeToNextLy <= 2 + 2 * isDoubleSpeed() + 2 * ppu_.cgb());
}
bool LCD::lycRegChangeTriggersStatIrq(
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(statReg_ & lcdstat_lycirqen) || data >= 154
|| lycRegChangeStatTriggerBlockedByM0OrM1Irq(cc)) {
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(statReg_ & lcdstat_lycirqen) || data >= lcd_lines_per_frame
|| lycRegChangeStatTriggerBlockedByM0OrM1Irq(data, cc)) {
return false;
}
LyCnt lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (lycCmp.timeToNextLy <= 4 << ppu_.cgb()) {
bool const ds = isDoubleSpeed();
if (old == lycCmp.ly && !(lycCmp.timeToNextLy <= 4 && ppu_.cgb() && !ds))
if (lycCmp.timeToNextLy <= 4 + 4 * isDoubleSpeed() + 2 * ppu_.cgb()) {
if (old == lycCmp.ly && lycCmp.timeToNextLy > 2 * ppu_.cgb())
return false; // simultaneous ly/lyc inc. lyc flag never goes low -> no trigger.
lycCmp.ly = incLy(lycCmp.ly);
@ -677,8 +673,9 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
m0Irq_.lycRegChange(data, eventTimes_(memevent_m0irq), cc, isDoubleSpeed(), ppu_.cgb());
lycIrq_.lycRegChange(data, ppu_.lyCounter(), cc);
mstatIrq_.lycRegChange(data, eventTimes_(memevent_m0irq),
eventTimes_(memevent_m2irq), cc, isDoubleSpeed(), ppu_.cgb());
if (ppu_.lcdc() & lcdc_en) {
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
@ -686,7 +683,8 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cc) {
if (lycRegChangeTriggersStatIrq(old, data, cc)) {
if (ppu_.cgb() && !isDoubleSpeed()) {
eventTimes_.setm<memevent_oneshot_statirq>(cc + 5);
} else
}
else
eventTimes_.flagIrq(2);
}
}
@ -701,61 +699,51 @@ unsigned LCD::getStat(unsigned const lycReg, unsigned long const cc) {
unsigned const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
if (ly > 143) {
if (ly < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)
int const lineCycles = lcd_cycles_per_line - (timeToNextLy >> isDoubleSpeed());
long const frameCycles = 1l * ly * lcd_cycles_per_line + lineCycles;
if (frameCycles >= lcd_vres * lcd_cycles_per_line - 3 && frameCycles < lcd_cycles_per_frame - 3) {
if (frameCycles >= lcd_vres * lcd_cycles_per_line - 2
&& frameCycles < lcd_cycles_per_frame - 4 + isDoubleSpeed()) {
stat = 1;
} else {
int const lineCycles = 456 - (timeToNextLy >> isDoubleSpeed());
if (lineCycles < 80) {
if (!ppu_.inactivePeriodAfterDisplayEnable(cc))
stat = 2;
} else if (cc + isDoubleSpeed() - ppu_.cgb() + 2 < m0TimeOfCurrentLine(cc))
}
}
else if (lineCycles < 77 || lineCycles >= lcd_cycles_per_line - 3) {
if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1))
stat = 2;
}
else if (cc + 2 < m0TimeOfCurrentLine(cc)) {
if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1))
stat = 3;
}
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4)
if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 2)
stat |= lcdstat_lycflag;
}
return stat;
}
static bool isMode2IrqEventBlockedByM1Irq(unsigned ly, unsigned statreg) {
return ly == 0 && (statreg & lcdstat_m1irqen);
}
static bool isMode2IrqEventBlockedByLycIrq(unsigned ly, unsigned statreg, unsigned lycreg) {
return (statreg & lcdstat_lycirqen)
&& (ly == 0 ? ly : ly - 1) == lycreg;
}
static bool isMode2IrqEventBlocked(unsigned ly, unsigned statreg, unsigned lycreg) {
return isMode2IrqEventBlockedByM1Irq(ly, statreg)
|| isMode2IrqEventBlockedByLycIrq(ly, statreg, lycreg);
}
inline void LCD::doMode2IrqEvent() {
unsigned const ly = eventTimes_(event_ly) - eventTimes_(memevent_m2irq) < 8
? incLy(ppu_.lyCounter().ly())
: ppu_.lyCounter().ly();
if (!isMode2IrqEventBlocked(ly, m2IrqStatReg_, lycIrq_.lycReg()))
eventTimes_.flagIrq(2);
m2IrqStatReg_ = statReg_;
unsigned const ly = eventTimes_(event_ly) - eventTimes_(memevent_m2irq) < 16
? incLy(ppu_.lyCounter().ly())
: ppu_.lyCounter().ly();
if (mstatIrq_.doM2Event(ly, statReg_, lycIrq_.lycReg()))
eventTimes_.flagIrq(2, eventTimes_(memevent_m2irq));
bool const ds = isDoubleSpeed();
unsigned long next = lcd_cycles_per_frame;
if (!(statReg_ & lcdstat_m0irqen)) {
unsigned long nextTime = eventTimes_(memevent_m2irq) + ppu_.lyCounter().lineTime();
next = lcd_cycles_per_line;
if (ly == 0) {
nextTime -= 4;
} else if (ly == 143)
nextTime += ppu_.lyCounter().lineTime() * 10 + 4;
eventTimes_.setm<memevent_m2irq>(nextTime);
} else {
eventTimes_.setm<memevent_m2irq>(eventTimes_(memevent_m2irq)
+ (70224 << isDoubleSpeed()));
next -= mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle;
}
else if (ly == lcd_vres) {
next += lcd_cycles_per_line * (lcd_lines_per_frame - lcd_vres - 1)
+ mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle;
}
}
eventTimes_.setm<memevent_m2irq>(eventTimes_(memevent_m2irq) + (next << ds));
}
inline void LCD::event() {
@ -763,22 +751,18 @@ inline void LCD::event() {
case event_mem:
switch (eventTimes_.nextMemEvent()) {
case memevent_m1irq:
eventTimes_.flagIrq((m1IrqStatReg_ & (lcdstat_m1irqen | lcdstat_m0irqen))
== lcdstat_m1irqen
? 3
: 1);
m1IrqStatReg_ = statReg_;
eventTimes_.flagIrq(mstatIrq_.doM1Event(statReg_) ? 3 : 1,
eventTimes_(memevent_m1irq));
eventTimes_.setm<memevent_m1irq>(eventTimes_(memevent_m1irq)
+ (70224 << isDoubleSpeed()));
+ (lcd_cycles_per_frame << isDoubleSpeed()));
break;
case memevent_lycirq: {
unsigned char ifreg = 0;
lycIrq_.doEvent(&ifreg, ppu_.lyCounter());
eventTimes_.flagIrq(ifreg);
case memevent_lycirq:
if (lycIrq_.doEvent(ppu_.lyCounter()))
eventTimes_.flagIrq(2, eventTimes_(memevent_lycirq));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
break;
}
case memevent_spritemap:
eventTimes_.setm<memevent_spritemap>(
@ -789,8 +773,7 @@ inline void LCD::event() {
case memevent_hdma:
eventTimes_.flagHdmaReq();
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(hdmaTimeFromM0Time(
nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
break;
case memevent_m2irq:
@ -798,17 +781,12 @@ inline void LCD::event() {
break;
case memevent_m0irq:
{
unsigned char ifreg = 0;
m0Irq_.doEvent(&ifreg, ppu_.lyCounter().ly(), statReg_,
lycIrq_.lycReg());
eventTimes_.flagIrq(ifreg);
}
if (mstatIrq_.doM0Event(ppu_.lyCounter().ly(), statReg_, lycIrq_.lycReg()))
eventTimes_.flagIrq(2, eventTimes_(memevent_m0irq));
eventTimes_.setm<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166),
ppu_.cgb(), isDoubleSpeed())
: static_cast<unsigned long>(disabled_time));
? ppu_.predictedNextXposTime(lcd_hres + 6)
: 1 * disabled_time);
break;
case memevent_oneshot_statirq:
@ -828,8 +806,6 @@ inline void LCD::event() {
case event_ly:
ppu_.doLyCountEvent();
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
if (scanlinecallback && ppu_.lyCounter().ly() == (unsigned)scanlinecallbacksl)
scanlinecallback();
break;
}
}
@ -864,15 +840,12 @@ SYNCFUNC(LCD)
{
SSS(ppu_);
NSS(dmgColorsRgb32_);
NSS(cgbColorsRgb32_);
NSS(bgpData_);
NSS(objpData_);
SSS(eventTimes_);
SSS(m0Irq_);
SSS(mstatIrq_);
SSS(lycIrq_);
SSS(nextM0Time_);
NSS(statReg_);
NSS(m2IrqStatReg_);
NSS(m1IrqStatReg_);
}
}

View File

@ -22,7 +22,7 @@
#include "interruptrequester.h"
#include "minkeeper.h"
#include "video/lyc_irq.h"
#include "video/m0_irq.h"
#include "video/mstat_irq.h"
#include "video/next_m0_time.h"
#include "video/ppu.h"
#include "newstate.h"
@ -36,8 +36,9 @@ public:
{
}
void flagHdmaReq() const { gambatte::flagHdmaReq(intreq_); }
void flagHdmaReq() const { if (!intreq_.halted()) gambatte::flagHdmaReq(intreq_); }
void flagIrq(unsigned bit) const { intreq_.flagIrq(bit); }
void flagIrq(unsigned bit, unsigned long cc) const { intreq_.flagIrq(bit, cc); }
void setNextEventTime(unsigned long time) const { intreq_.setEventTime<intevent_video>(time); }
private:
@ -49,13 +50,13 @@ public:
LCD(unsigned char const *oamram, unsigned char const *vram,
VideoInterruptRequester memEventRequester);
void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb);
void setCgbDmg(bool enabled) { ppu_.setCgbDmg(enabled); }
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state, unsigned char const *oamram);
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
void setCgbPalette(unsigned *lut);
void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch);
void setLayers(unsigned mask) { ppu_.setLayers(mask); }
void setCgb(bool cgb);
void copyCgbPalettesToDmg();
int debugGetLY() const { return ppu_.lyCounter().ly(); }
@ -89,17 +90,19 @@ public:
}
unsigned cgbBgColorRead(unsigned index, unsigned long cycleCounter) {
return ppu_.cgb() & cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF;
return ppu_.cgb() && cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF;
}
unsigned cgbSpColorRead(unsigned index, unsigned long cycleCounter) {
return ppu_.cgb() & cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF;
return ppu_.cgb() && cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF;
}
void updateScreen(bool blanklcd, unsigned long cc);
void blackScreen();
void resetCc(unsigned long oldCC, unsigned long newCc);
void speedChange(unsigned long cycleCounter);
bool vramAccessible(unsigned long cycleCounter);
bool vramReadable(unsigned long cycleCounter);
bool vramWritable(unsigned long cycleCounter);
bool oamReadable(unsigned long cycleCounter);
bool oamWritable(unsigned long cycleCounter);
void wxChange(unsigned newValue, unsigned long cycleCounter);
@ -119,15 +122,16 @@ public:
update(cc);
lyReg = ppu_.lyCounter().ly();
if (lyReg == 153) {
if (isDoubleSpeed()) {
if (ppu_.lyCounter().time() - cc <= 456 * 2 - 8)
lyReg = 0;
} else
if (lyReg == lcd_lines_per_frame - 1) {
if (ppu_.lyCounter().time() - cc <= 2 * lcd_cycles_per_line - 2)
lyReg = 0;
} else if (ppu_.lyCounter().time() - cc <= 4)
++lyReg;
}
else if (ppu_.lyCounter().time() - cc <= 10
&& ppu_.lyCounter().time() - cc <= 6u + 4 * isDoubleSpeed()) {
lyReg = ppu_.lyCounter().time() - cc == 6u + 4 * isDoubleSpeed()
? lyReg & (lyReg + 1)
: lyReg + 1;
}
}
return lyReg;
@ -139,9 +143,11 @@ public:
void lycRegChange(unsigned data, unsigned long cycleCounter);
void enableHdma(unsigned long cycleCounter);
void disableHdma(unsigned long cycleCounter);
bool isHdmaPeriod(unsigned long cycleCounter);
bool hdmaIsEnabled() const { return eventTimes_(memevent_hdma) != disabled_time; }
void update(unsigned long cycleCounter);
bool isCgb() const { return ppu_.cgb(); }
bool isCgbDmg() const { return ppu_.cgbDmg(); }
bool isDoubleSpeed() const { return ppu_.lyCounter().isDoubleSpeed(); }
unsigned long *bgPalette() { return ppu_.bgPalette(); }
@ -189,6 +195,7 @@ private:
void set(MemEvent e, unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
void flagIrq(unsigned bit) { memEventRequester_.flagIrq(bit); }
void flagIrq(unsigned bit, unsigned long cc) { memEventRequester_.flagIrq(bit, cc); }
void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
private:
@ -214,15 +221,13 @@ public:
PPU ppu_;
unsigned long dmgColorsRgb32_[3 * 4];
unsigned long cgbColorsRgb32_[32768];
unsigned char bgpData_[8 * 8];
unsigned char objpData_[8 * 8];
unsigned char bgpData_[2 * max_num_palettes * num_palette_entries];
unsigned char objpData_[2 * max_num_palettes * num_palette_entries];
EventTimes eventTimes_;
M0Irq m0Irq_;
MStatIrqEvent mstatIrq_;
LycIrq lycIrq_;
NextM0Time nextM0Time_;
unsigned char statReg_;
unsigned char m2IrqStatReg_;
unsigned char m1IrqStatReg_;
static void setDmgPalette(unsigned long palette[],
unsigned long const dmgColors[],
@ -237,9 +242,9 @@ public:
void event();
unsigned long m0TimeOfCurrentLine(unsigned long cc);
bool cgbpAccessible(unsigned long cycleCounter);
bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long cc);
bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc);
bool lycRegChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc);
bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, unsigned long cc);
bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, bool lycperiod, unsigned long cc);
bool statChangeTriggersStatIrqCgb(unsigned old, unsigned data, unsigned long cc);
bool statChangeTriggersStatIrqDmg(unsigned old, unsigned long cc);
bool statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc);

View File

@ -3,18 +3,38 @@
namespace gambatte {
enum { lcdc_bgen = 0x01,
lcdc_objen = 0x02,
lcdc_obj2x = 0x04,
lcdc_tdsel = 0x10,
lcdc_we = 0x20,
lcdc_en = 0x80 };
enum {
lcdc_bgen = 0x01,
lcdc_objen = 0x02,
lcdc_obj2x = 0x04,
lcdc_bgtmsel = 0x08,
lcdc_tdsel = 0x10,
lcdc_we = 0x20,
lcdc_wtmsel = 0x40,
lcdc_en = 0x80
};
enum { lcdstat_lycflag = 0x04,
lcdstat_m0irqen = 0x08,
lcdstat_m1irqen = 0x10,
lcdstat_m2irqen = 0x20,
lcdstat_lycirqen = 0x40 };
enum {
lcdstat_lycflag = 0x04,
lcdstat_m0irqen = 0x08,
lcdstat_m1irqen = 0x10,
lcdstat_m2irqen = 0x20,
lcdstat_lycirqen = 0x40
};
enum {
lcd_hres = 160,
lcd_vres = 144,
lcd_lines_per_frame = 154,
lcd_max_num_sprites_per_line = 10,
lcd_num_oam_entries = 40,
lcd_cycles_per_line = 456,
lcd_force_signed_enum1 = -1
};
enum {
lcd_cycles_per_frame = 1l * lcd_lines_per_frame * lcd_cycles_per_line,
lcd_force_signed_enum2 = -1
};
}

View File

@ -33,7 +33,7 @@ LyCounter::LyCounter()
void LyCounter::doEvent() {
++ly_;
if (ly_ == 154)
if (ly_ == lcd_lines_per_frame)
ly_ = 0;
time_ = time_ + lineTime_;
@ -48,21 +48,22 @@ unsigned long LyCounter::nextLineCycle(unsigned const lineCycle, unsigned long c
}
unsigned long LyCounter::nextFrameCycle(unsigned long const frameCycle, unsigned long const cc) const {
unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds_);
if (tmp - cc > 70224U << ds_)
tmp -= 70224U << ds_;
unsigned long tmp = time_ + (((lcd_lines_per_frame - 1l - ly()) * lcd_cycles_per_line + frameCycle) << ds_);
if (tmp - cc > 1ul * lcd_cycles_per_frame << ds_)
tmp -= 1ul * lcd_cycles_per_frame << ds_;
return tmp;
}
void LyCounter::reset(unsigned long videoCycles, unsigned long lastUpdate) {
ly_ = videoCycles / 456;
time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
ly_ = videoCycles / lcd_cycles_per_line;
time_ = lastUpdate + ((lcd_cycles_per_line
- (videoCycles - 1l * ly_ * lcd_cycles_per_line)) << isDoubleSpeed());
}
void LyCounter::setDoubleSpeed(bool ds) {
ds_ = ds;
lineTime_ = 456U << ds;
lineTime_ = lcd_cycles_per_line << ds;
}
SYNCFUNC(LyCounter)

View File

@ -20,6 +20,7 @@
#define LY_COUNTER_H
#include "newstate.h"
#include "lcddef.h"
namespace gambatte {
@ -32,11 +33,11 @@ public:
bool isDoubleSpeed() const { return ds_; }
unsigned long frameCycles(unsigned long cc) const {
return ly_ * 456ul + lineCycles(cc);
return 1l * ly_ * lcd_cycles_per_line + lineCycles(cc);
}
unsigned lineCycles(unsigned long cc) const {
return 456u - ((time_ - cc) >> isDoubleSpeed());
return lcd_cycles_per_line - ((time_ - cc) >> isDoubleSpeed());
}
unsigned lineTime() const { return lineTime_; }

View File

@ -23,7 +23,26 @@
#include "savestate.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
namespace {
unsigned long schedule(unsigned statReg,
unsigned lycReg, LyCounter const& lyCounter, unsigned long cc) {
return (statReg & lcdstat_lycirqen) && lycReg < lcd_lines_per_frame
? lyCounter.nextFrameCycle(lycReg
? 1l * lycReg * lcd_cycles_per_line - 2
: (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + 6, cc)
: 1 * disabled_time;
}
bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) {
return ly <= lcd_vres && ly > 0
? statreg & lcdstat_m2irqen
: statreg & lcdstat_m1irqen;
}
}
LycIrq::LycIrq()
: time_(disabled_time)
@ -35,53 +54,40 @@ LycIrq::LycIrq()
{
}
static unsigned long schedule(unsigned statReg,
unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) {
return (statReg & lcdstat_lycirqen) && lycReg < 154
? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
: static_cast<unsigned long>(disabled_time);
}
void LycIrq::regChange(unsigned const statReg,
unsigned const lycReg, LyCounter const &lyCounter, unsigned long const cc) {
unsigned const lycReg, LyCounter const& lyCounter, unsigned long const cc) {
unsigned long const timeSrc = schedule(statReg, lycReg, lyCounter, cc);
statRegSrc_ = statReg;
lycRegSrc_ = lycReg;
time_ = std::min(time_, timeSrc);
if (cgb_) {
if (time_ - cc > 8 || (timeSrc != time_ && time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U))
if (time_ - cc > 6u + 4 * lyCounter.isDoubleSpeed() || (timeSrc != time_ && time_ - cc > 2))
lycReg_ = lycReg;
if (time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U)
if (time_ - cc > 2)
statReg_ = statReg;
} else {
}
else {
if (time_ - cc > 4 || timeSrc != time_)
lycReg_ = lycReg;
if (time_ - cc > 4 || lycReg_ != 0)
statReg_ = statReg;
statReg_ = (statReg_ & lcdstat_lycirqen) | (statReg & ~lcdstat_lycirqen);
statReg_ = statReg;
}
}
static bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) {
return ly - 1u < 144u - 1u
? statreg & lcdstat_m2irqen
: statreg & lcdstat_m1irqen;
}
void LycIrq::doEvent(unsigned char *const ifreg, LyCounter const &lyCounter) {
bool LycIrq::doEvent(LyCounter const& lyCounter) {
bool flagIrq = false;
if ((statReg_ | statRegSrc_) & lcdstat_lycirqen) {
unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly();
if (lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_))
*ifreg |= 2;
unsigned const cmpLy = lyCounter.ly() == lcd_lines_per_frame - 1
? 0
: lyCounter.ly() + 1;
flagIrq = lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_);
}
lycReg_ = lycRegSrc_;
statReg_ = statRegSrc_;
time_ = schedule(statReg_, lycReg_, lyCounter, time_);
return flagIrq;
}
void LycIrq::loadState(SaveState const &state) {
@ -110,5 +116,3 @@ SYNCFUNC(LycIrq)
NSS(statReg_);
NSS(cgb_);
}
}

View File

@ -29,7 +29,7 @@ class LyCounter;
class LycIrq {
public:
LycIrq();
void doEvent(unsigned char *ifreg, LyCounter const &lyCounter);
bool doEvent(LyCounter const& lyCounter);
unsigned lycReg() const { return lycRegSrc_; }
void loadState(SaveState const &state);
unsigned long time() const { return time_; }

View File

@ -1,68 +0,0 @@
#ifndef M0_IRQ_H
#define M0_IRQ_H
#include "lcddef.h"
#include "../savestate.h"
#include "../newstate.h"
namespace gambatte {
class M0Irq {
public:
M0Irq()
: statReg_(0)
, lycReg_(0)
{
}
void lcdReset(unsigned statReg, unsigned lycReg) {
statReg_ = statReg;
lycReg_ = lycReg;
}
void statRegChange(unsigned statReg,
unsigned long nextM0IrqTime, unsigned long cc, bool cgb) {
if (nextM0IrqTime - cc > cgb * 2U)
statReg_ = statReg;
}
void lycRegChange(unsigned lycReg,
unsigned long nextM0IrqTime, unsigned long cc,
bool ds, bool cgb) {
if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
lycReg_ = lycReg;
}
void doEvent(unsigned char *ifreg, unsigned ly, unsigned statReg, unsigned lycReg) {
if (((statReg_ | statReg) & lcdstat_m0irqen)
&& (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_)) {
*ifreg |= 2;
}
statReg_ = statReg;
lycReg_ = lycReg;
}
void loadState(SaveState const &state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
unsigned statReg() const { return statReg_; }
private:
unsigned char statReg_;
unsigned char lycReg_;
public:
template<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
}
#endif

View File

@ -0,0 +1,72 @@
#ifndef M0_IRQ_H
#define M0_IRQ_H
#include "lcddef.h"
#include "../savestate.h"
#include "../newstate.h"
namespace gambatte {
class MStatIrqEvent {
public:
MStatIrqEvent() : lycReg_(0), statReg_(0) {}
void lcdReset(unsigned lycReg) { lycReg_ = lycReg; }
void lycRegChange(unsigned lycReg, unsigned long nextM0IrqTime,
unsigned long nextM2IrqTime, unsigned long cc, bool ds, bool cgb) {
if (cc + 5 * cgb + 1 - ds < std::min(nextM0IrqTime, nextM2IrqTime))
lycReg_ = lycReg;
}
void statRegChange(unsigned statReg, unsigned long nextM0IrqTime, unsigned long nextM1IrqTime,
unsigned long nextM2IrqTime, unsigned long cc, bool cgb) {
if (cc + 2 * cgb < std::min(std::min(nextM0IrqTime, nextM1IrqTime), nextM2IrqTime))
statReg_ = statReg;
}
bool doM0Event(unsigned ly, unsigned statReg, unsigned lycReg) {
bool const flagIrq = ((statReg | statReg_) & lcdstat_m0irqen)
&& (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_);
lycReg_ = lycReg;
statReg_ = statReg;
return flagIrq;
}
bool doM1Event(unsigned statReg) {
bool const flagIrq = (statReg & lcdstat_m1irqen)
&& !(statReg_ & (lcdstat_m2irqen | lcdstat_m0irqen));
statReg_ = statReg;
return flagIrq;
}
bool doM2Event(unsigned ly, unsigned statReg, unsigned lycReg) {
bool const blockedByM1Irq = ly == 0 && (statReg_ & lcdstat_m1irqen);
bool const blockedByLycIrq = (statReg_ & lcdstat_lycirqen)
&& (ly == 0 ? ly : ly - 1) == lycReg_;
bool const flagIrq = !blockedByM1Irq && !blockedByLycIrq;
lycReg_ = lycReg;
statReg_ = statReg;
return flagIrq;
}
void loadState(SaveState const& state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
private:
unsigned char statReg_;
unsigned char lycReg_;
public:
template<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
}
#endif

View File

@ -2,7 +2,7 @@
#include "ppu.h"
void gambatte::NextM0Time::predictNextM0Time(PPU const &ppu) {
predictedNextM0Time_ = ppu.predictedNextXposTime(167);
predictedNextM0Time_ = ppu.predictedNextXposTime(lcd_hres + 7);
}
SYNCFUNC(gambatte::NextM0Time)

View File

@ -10,10 +10,10 @@ public:
NextM0Time() : predictedNextM0Time_(0) {}
void predictNextM0Time(class PPU const &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
unsigned long predictedNextM0Time() const { return predictedNextM0Time_; }
private:
unsigned predictedNextM0Time_;
unsigned long predictedNextM0Time_;
public:
template<bool isReader>void SyncState(NewState *ns);

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,14 @@
namespace gambatte {
enum { layer_mask_bg = 1, layer_mask_obj = 2, layer_mask_window = 4 };
enum {
layer_mask_bg = 1,
layer_mask_obj = 2,
layer_mask_window = 4 };
enum {
max_num_palettes = 8,
num_palette_entries = 4,
ppu_force_signed_enum = -1 };
class PPUFrameBuf {
public:
@ -57,10 +64,10 @@ struct PPUState {
};
struct PPUPriv {
unsigned long bgPalette[8 * 4];
unsigned long spPalette[8 * 4];
struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
unsigned short spwordList[11];
unsigned long bgPalette[max_num_palettes * num_palette_entries];
unsigned long spPalette[max_num_palettes * num_palette_entries];
struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[lcd_max_num_sprites_per_line + 1];
unsigned short spwordList[lcd_max_num_sprites_per_line + 1];
unsigned char nextSprite;
unsigned char currentSprite;
unsigned layersMask;
@ -96,6 +103,7 @@ struct PPUPriv {
unsigned char endx;
bool cgb;
bool cgbDmg;
bool weMaster;
PPUPriv(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram);
@ -110,6 +118,7 @@ public:
unsigned long * bgPalette() { return p_.bgPalette; }
bool cgb() const { return p_.cgb; }
bool cgbDmg() const { return p_.cgbDmg; }
void doLyCountEvent() { p_.lyCounter.doEvent(); }
unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
PPUFrameBuf const & frameBuf() const { return p_.framebuf; }
@ -127,6 +136,7 @@ public:
void oamChange(unsigned char const *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
unsigned long predictedNextXposTime(unsigned xpos) const;
void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb);
void setCgbDmg(bool enabled) { p_.cgbDmg = enabled; }
void resetCc(unsigned long oldCc, unsigned long newCc);
void setFrameBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { p_.framebuf.setBuf(buf, pitch); }
void setLcdc(unsigned lcdc, unsigned long cc);
@ -136,11 +146,10 @@ public:
void setWx(unsigned wx) { p_.wx = wx; }
void setWy(unsigned wy) { p_.wy = wy; }
void updateWy2() { p_.wy2 = p_.wy; }
void speedChange(unsigned long cycleCounter);
void speedChange();
unsigned long * spPalette() { return p_.spPalette; }
void update(unsigned long cc);
void setLayers(unsigned mask) { p_.layersMask = mask; }
void setCgb(bool cgb) { p_.cgb = cgb; }
private:
PPUPriv p_;

View File

@ -21,7 +21,8 @@
#include "next_m0_time.h"
#include "../insertion_sort.h"
#include <algorithm>
#include <cstring>
using namespace gambatte;
namespace {
@ -37,9 +38,15 @@ private:
unsigned char const *const spxlut_;
};
unsigned toPosCycles(unsigned long const cc, LyCounter const& lyCounter) {
unsigned lc = lyCounter.lineCycles(cc) + 1;
if (lc >= lcd_cycles_per_line)
lc -= lcd_cycles_per_line;
return lc;
}
namespace gambatte {
}
SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char const *oamram)
: lyCounter_(lyCounter)
@ -49,45 +56,35 @@ SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char con
reset(oamram, false);
}
void SpriteMapper::OamReader::reset(unsigned char const *const oamram, bool const cgb) {
void SpriteMapper::OamReader::reset(unsigned char const* const oamram, bool const cgb) {
oamram_ = oamram;
cgb_ = cgb;
setLargeSpritesSrc(false);
lu_ = 0;
lastChange_ = 0xFF;
std::fill(szbuf_, szbuf_ + 40, largeSpritesSrc_);
unsigned pos = 0;
unsigned distance = 80;
while (distance--) {
buf_[pos] = oamram[((pos * 2) & ~3) | (pos & 1)];
++pos;
std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, largeSpritesSrc_);
for (int i = 0; i < lcd_num_oam_entries; ++i) {
buf_[2 * i] = oamram[4 * i];
buf_[2 * i + 1] = oamram[4 * i + 1];
}
}
static unsigned toPosCycles(unsigned long const cc, LyCounter const &lyCounter) {
unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
if (lc >= 456)
lc -= 456;
return lc;
}
void SpriteMapper::OamReader::update(unsigned long const cc) {
if (cc > lu_) {
if (changed()) {
unsigned const lulc = toPosCycles(lu_, lyCounter_);
unsigned pos = std::min(lulc, 80u);
unsigned distance = 80;
unsigned pos = std::min(lulc, 2u * lcd_num_oam_entries);
unsigned distance = 2 * lcd_num_oam_entries;
if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < 456) {
if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < lcd_cycles_per_line) {
unsigned cclc = toPosCycles(cc, lyCounter_);
distance = std::min(cclc, 80u) - pos + (cclc < lulc ? 80 : 0);
distance = std::min(cclc, 2u * lcd_num_oam_entries)
- pos + (cclc < lulc ? 2 * lcd_num_oam_entries : 0);
}
{
unsigned targetDistance =
lastChange_ - pos + (lastChange_ <= pos ? 80 : 0);
lastChange_ - pos + (lastChange_ <= pos ? 2 * lcd_num_oam_entries : 0);
if (targetDistance <= distance) {
distance = targetDistance;
lastChange_ = 0xFF;
@ -96,16 +93,16 @@ void SpriteMapper::OamReader::update(unsigned long const cc) {
while (distance--) {
if (!(pos & 1)) {
if (pos == 80)
if (pos == 2 * lcd_num_oam_entries)
pos = 0;
if (cgb_)
szbuf_[pos >> 1] = largeSpritesSrc_;
lsbuf_[pos / 2] = largeSpritesSrc_;
buf_[pos ] = oamram_[pos * 2 ];
buf_[pos + 1] = oamram_[pos * 2 + 1];
} else
szbuf_[pos >> 1] = (szbuf_[pos >> 1] & cgb_) | largeSpritesSrc_;
buf_[pos] = oamram_[2 * pos];
buf_[pos + 1] = oamram_[2 * pos + 1];
}
else
lsbuf_[pos / 2] = (lsbuf_[pos / 2] & cgb_) | largeSpritesSrc_;
++pos;
}
@ -117,15 +114,15 @@ void SpriteMapper::OamReader::update(unsigned long const cc) {
void SpriteMapper::OamReader::change(unsigned long cc) {
update(cc);
lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 80u);
lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 2u * lcd_num_oam_entries);
}
void SpriteMapper::OamReader::setStatePtrs(SaveState &state) {
state.ppu.oamReaderBuf.set(buf_, sizeof buf_);
state.ppu.oamReaderSzbuf.set(szbuf_, sizeof szbuf_ / sizeof *szbuf_);
void SpriteMapper::OamReader::setStatePtrs(SaveState& state) {
state.ppu.oamReaderBuf.set(buf_, sizeof buf_ / sizeof * buf_);
state.ppu.oamReaderSzbuf.set(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_);
}
void SpriteMapper::OamReader::loadState(SaveState const &ss, unsigned char const *const oamram) {
void SpriteMapper::OamReader::loadState(SaveState const& ss, unsigned char const* const oamram) {
oamram_ = oamram;
largeSpritesSrc_ = ss.mem.ioamhram.get()[0x140] >> 2 & 1;
lu_ = ss.ppu.enableDisplayM0Time;
@ -135,7 +132,7 @@ void SpriteMapper::OamReader::loadState(SaveState const &ss, unsigned char const
SYNCFUNC(SpriteMapper::OamReader)
{
NSS(buf_);
NSS(szbuf_);
NSS(lsbuf_);
NSS(lu_);
NSS(lastChange_);
@ -144,10 +141,10 @@ SYNCFUNC(SpriteMapper::OamReader)
}
void SpriteMapper::OamReader::enableDisplay(unsigned long cc) {
std::memset(buf_, 0x00, sizeof buf_);
std::fill(szbuf_, szbuf_ + 40, false);
lu_ = cc + (80 << lyCounter_.isDoubleSpeed());
lastChange_ = 80;
std::fill_n(buf_, sizeof buf_ / sizeof * buf_, 0);
std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, false);
lu_ = cc + (2 * lcd_num_oam_entries << lyCounter_.isDoubleSpeed()) + 1;
lastChange_ = 2 * lcd_num_oam_entries;
}
SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
@ -165,28 +162,24 @@ void SpriteMapper::reset(unsigned char const *oamram, bool cgb) {
}
void SpriteMapper::clearMap() {
std::memset(num_, need_sorting_mask, sizeof num_);
std::fill_n(num_, sizeof num_ / sizeof * num_, 1 * need_sorting_flag);
}
void SpriteMapper::mapSprites() {
clearMap();
for (unsigned i = 0x00; i < 0x50; i += 2) {
int const spriteHeight = 8 << largeSprites(i >> 1);
unsigned const bottomPos = posbuf()[i] - (17u - spriteHeight);
for (int i = 0; i < lcd_num_oam_entries; ++i) {
int const spriteHeight = 8 + 8 * largeSprites(i);
unsigned const bottomPos = posbuf()[2 * i] - 17 + spriteHeight;
if (bottomPos < 143u + spriteHeight) {
unsigned const startly = std::max(int(bottomPos) + 1 - spriteHeight, 0);
unsigned char *map = spritemap_ + startly * 10;
unsigned char *n = num_ + startly;
unsigned char *const nend = num_ + std::min(bottomPos, 143u) + 1;
if (bottomPos < lcd_vres - 1u + spriteHeight) {
int ly = std::max(static_cast<int>(bottomPos) + 1 - spriteHeight, 0);
int const end = std::min(bottomPos, lcd_vres - 1u) + 1;
do {
if (*n < need_sorting_mask + 10)
map[(*n)++ - need_sorting_mask] = i;
map += 10;
} while (++n != nend);
if (num_[ly] < need_sorting_flag + lcd_max_num_sprites_per_line)
spritemap_[ly][num_[ly]++ - need_sorting_flag] = 2 * i;
} while (++ly != end);
}
}
@ -194,17 +187,17 @@ void SpriteMapper::mapSprites() {
}
void SpriteMapper::sortLine(unsigned const ly) const {
num_[ly] &= ~need_sorting_mask;
insertionSort(spritemap_ + ly * 10, spritemap_ + ly * 10 + num_[ly],
SpxLess(posbuf() + 1));
num_[ly] &= ~(1u * need_sorting_flag);
insertionSort(spritemap_[ly], spritemap_[ly] + num_[ly],
SpxLess(posbuf() + 1));
}
unsigned long SpriteMapper::doEvent(unsigned long const time) {
oamReader_.update(time);
mapSprites();
return oamReader_.changed()
? time + oamReader_.lineTime()
: static_cast<unsigned long>(disabled_time);
? time + oamReader_.lineTime()
: static_cast<unsigned long>(disabled_time);
}
SYNCFUNC(SpriteMapper)
@ -214,5 +207,3 @@ SYNCFUNC(SpriteMapper)
SSS(oamReader_);
}
}

View File

@ -34,14 +34,12 @@ public:
unsigned char const *oamram);
void reset(unsigned char const *oamram, bool cgb);
unsigned long doEvent(unsigned long time);
bool largeSprites(unsigned spNo) const { return oamReader_.largeSprites(spNo); }
unsigned numSprites(unsigned ly) const { return num_[ly] & ~need_sorting_mask; }
bool largeSprites(int spno) const { return oamReader_.largeSprites(spno); }
int numSprites(unsigned ly) const { return num_[ly] & ~(1u * need_sorting_flag); }
void oamChange(unsigned long cc) { oamReader_.change(cc); }
void oamChange(unsigned char const *oamram, unsigned long cc) { oamReader_.change(oamram, cc); }
unsigned char const * oamram() const { return oamReader_.oam(); }
unsigned char const * posbuf() const { return oamReader_.spritePosBuf(); }
void preSpeedChange(unsigned long cc) { oamReader_.update(cc); }
void postSpeedChange(unsigned long cc) { oamReader_.change(cc); }
void resetCycleCounter(unsigned long oldCc, unsigned long newCc) {
oamReader_.update(oldCc);
@ -50,11 +48,11 @@ public:
void setLargeSpritesSource(bool src) { oamReader_.setLargeSpritesSrc(src); }
unsigned char const * sprites(unsigned ly) const {
if (num_[ly] & need_sorting_mask)
unsigned char const* sprites(unsigned ly) const {
if (num_[ly] & need_sorting_flag)
sortLine(ly);
return spritemap_ + ly * 10;
return spritemap_[ly];
}
void setStatePtrs(SaveState &state) { oamReader_.setStatePtrs(state); }
@ -69,8 +67,8 @@ public:
return oamReader_.inactivePeriodAfterDisplayEnable(cc);
}
static unsigned long schedule(LyCounter const &lyCounter, unsigned long cc) {
return lyCounter.nextLineCycle(80, cc);
static unsigned long schedule(LyCounter const& lyCounter, unsigned long cc) {
return lyCounter.nextLineCycle(2 * lcd_num_oam_entries, cc);
}
private:
@ -81,7 +79,7 @@ private:
void change(unsigned long cc);
void change(unsigned char const *oamram, unsigned long cc) { change(cc); oamram_ = oamram; }
bool changed() const { return lastChange_ != 0xFF; }
bool largeSprites(unsigned spNo) const { return szbuf_[spNo]; }
bool largeSprites(int spNo) const { return lsbuf_[spNo]; }
unsigned char const * oam() const { return oamram_; }
void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { lu_ -= oldCc - newCc; }
void setLargeSpritesSrc(bool src) { largeSpritesSrc_ = src; }
@ -94,8 +92,8 @@ private:
unsigned lineTime() const { return lyCounter_.lineTime(); }
private:
unsigned char buf_[80];
bool szbuf_[40];
unsigned char buf_[2 * lcd_num_oam_entries];
bool lsbuf_[lcd_num_oam_entries];
LyCounter const &lyCounter_;
unsigned char const *oamram_;
unsigned long lu_;
@ -107,10 +105,10 @@ private:
template<bool isReader>void SyncState(NewState *ns);
};
enum { need_sorting_mask = 0x80 };
enum { need_sorting_flag = 0x80 };
mutable unsigned char spritemap_[144 * 10];
mutable unsigned char num_[144];
mutable unsigned char spritemap_[lcd_vres][lcd_max_num_sprites_per_line];
mutable unsigned char num_[lcd_vres];
NextM0Time &nextM0Time_;
OamReader oamReader_;

Binary file not shown.