~half the changes from Sinamas/GSR 2020 updates

This commit is contained in:
TiKevin83 2020-02-16 12:05:56 -05:00 committed by Travis
parent 3b804b789a
commit 85ee2af0b2
36 changed files with 1527 additions and 997 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

@ -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,6 +182,7 @@
<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" />
@ -192,6 +195,7 @@
<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

@ -123,6 +123,15 @@
<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>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cinterface.cpp">
@ -203,5 +212,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_(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,7 +540,7 @@ void CPU::process(unsigned long const cycles) {
result[8] = toF(hf2, cf, zf);
result[9] = h;
result[10] = l;
result[11] = skip_;
result[11] = prefetched_;
PC_READ_FIRST(opcode);
result[12] = opcode;
result[13] = mem_.debugGetLY();
@ -534,9 +550,13 @@ void CPU::process(unsigned long const cycles) {
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:
{

View File

@ -115,7 +115,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

@ -1178,6 +1178,8 @@ 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;
@ -1199,20 +1201,22 @@ 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;
@ -1269,11 +1273,12 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
// 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

@ -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:
@ -459,14 +478,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 +494,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 +584,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 +596,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 +670,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 +702,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 +792,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

@ -20,11 +20,27 @@
#include "savestate.h"
#include "sound.h"
#include "video.h"
#include <algorithm>
#include <cstring>
namespace gambatte {
using namespace gambatte;
Memory::Memory(unsigned short &sp, unsigned short &pc)
namespace {
int const oam_size = 4 * lcd_num_oam_entries;
void decCycles(unsigned long& counter, unsigned long dec) {
if (counter != disabled_time)
counter -= dec;
}
int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) {
return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
}
} // unnamed namespace.
Memory::Memory(Interrupter const& interrupter)
: readCallback_(0)
, writeCallback_(0)
, execCallback_(0)
@ -35,17 +51,18 @@ Memory::Memory(unsigned short &sp, unsigned short &pc)
, divLastUpdate_(0)
, lastOamDmaUpdate_(disabled_time)
, lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_))
, interrupter_(interrupter)
, dmaSource_(0)
, dmaDestination_(0)
, oamDmaPos_(0xFE)
, oamDmaPos_(-2u & 0xFF)
, oamDmaStartPos_(0)
, serialCnt_(0)
, blanklcd_(false)
, LINKCABLE_(false)
, linkClockTrigger_(false)
, sp_(sp)
, pc_(pc)
, haltHdmaState_(hdma_low)
{
intreq_.setEventTime<intevent_blit>(144 * 456ul);
intreq_.setEventTime<intevent_blit>(1l * lcd_vres * lcd_cycles_per_line);
intreq_.setEventTime<intevent_end>(0);
}
@ -61,11 +78,6 @@ void Memory::setStatePtrs(SaveState &state) {
psg_.setStatePtrs(state);
}
static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) {
return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
}
void Memory::loadState(SaveState const &state) {
biosMode_ = state.mem.biosMode;
cgbSwitching_ = state.mem.cgbSwitching;
@ -73,51 +85,55 @@ void Memory::loadState(SaveState const &state) {
gbIsCgb_ = state.mem.gbIsCgb;
stopped_ = state.mem.stopped;
psg_.loadState(state);
lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_);
lcd_.loadState(state, state.mem.oamDmaPos < oam_size ? cart_.rdisabledRam() : ioamhram_);
tima_.loadState(state, TimaInterruptRequester(intreq_));
cart_.loadState(state);
intreq_.loadState(state);
divLastUpdate_ = state.mem.divLastUpdate;
intreq_.setEventTime<intevent_serial>(state.mem.nextSerialtime > state.cpu.cycleCounter
? state.mem.nextSerialtime
: state.cpu.cycleCounter);
intreq_.setEventTime<intevent_unhalt>(state.mem.unhaltTime);
halttime_ = state.mem.halttime;
lastOamDmaUpdate_ = state.mem.lastOamDmaUpdate;
dmaSource_ = state.mem.dmaSource;
dmaDestination_ = state.mem.dmaDestination;
oamDmaPos_ = state.mem.oamDmaPos;
oamDmaStartPos_ = 0;
haltHdmaState_ = static_cast<HdmaState>(std::min(1u * state.mem.haltHdmaState, 1u * hdma_requested));
serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time
? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
ioamhram_[0x102] & isCgb() * 2)
: 8;
? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
ioamhram_[0x102] & isCgb() * 2)
: 8;
cart_.setVrambank(ioamhram_[0x14F] & isCgb());
cart_.setOamDmaSrc(oam_dma_src_off);
cart_.setWrambank(isCgb() && (ioamhram_[0x170] & 0x07) ? ioamhram_[0x170] & 0x07 : 1);
if (lastOamDmaUpdate_ != disabled_time) {
if (lastOamDmaUpdate_ > state.cpu.cycleCounter) {
oamDmaStartPos_ = (oamDmaPos_ + (lastOamDmaUpdate_ - state.cpu.cycleCounter) / 4) & 0xFF;
lastOamDmaUpdate_ = state.cpu.cycleCounter;
}
oamDmaInitSetup();
unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100;
unsigned oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_;
intreq_.setEventTime<intevent_oam>(
lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4);
lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4);
}
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: state.cpu.cycleCounter);
? lcd_.nextMode1IrqTime()
: state.cpu.cycleCounter);
blanklcd_ = false;
if (!isCgb())
std::memset(cart_.vramdata() + 0x2000, 0, 0x2000);
std::fill_n(cart_.vramdata() + vrambank_size(), vrambank_size(), 0);
}
void Memory::setEndtime(unsigned long cc, unsigned long inc) {
if (intreq_.eventTime(intevent_blit) <= cc) {
intreq_.setEventTime<intevent_blit>(intreq_.eventTime(intevent_blit)
+ (70224 << isDoubleSpeed()));
+ (lcd_cycles_per_frame << isDoubleSpeed()));
}
intreq_.setEventTime<intevent_end>(cc + (inc << isDoubleSpeed()));
@ -129,11 +145,11 @@ void Memory::updateSerial(unsigned long const cc) {
if (intreq_.eventTime(intevent_serial) <= cc) {
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF;
ioamhram_[0x102] &= 0x7F;
intreq_.flagIrq(8, intreq_.eventTime(intevent_serial));
intreq_.setEventTime<intevent_serial>(disabled_time);
intreq_.flagIrq(8);
} else {
int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc,
ioamhram_[0x102] & isCgb() * 2);
ioamhram_[0x102] & isCgb() * 2);
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF;
serialCnt_ = targetCnt;
}
@ -167,11 +183,14 @@ unsigned long Memory::event(unsigned long cc) {
switch (intreq_.minEventId()) {
case intevent_unhalt:
if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low)
|| haltHdmaState_ == hdma_requested) {
flagHdmaReq(intreq_);
}
intreq_.unhalt();
intreq_.setEventTime<intevent_unhalt>(disabled_time);
nontrivial_ff_write(0xFF04, 0, cc);
pc_ = (pc_ + 1) & 0xFFFF;
cc += 4;
stopped_ = false;
break;
case intevent_end:
intreq_.setEventTime<intevent_end>(disabled_time - 1);
@ -197,7 +216,7 @@ unsigned long Memory::event(unsigned long cc) {
while (cc >= intreq_.minEventTime())
cc = event(cc);
} else
blitTime += 70224 << isDoubleSpeed();
blitTime += lcd_cycles_per_frame << isDoubleSpeed();
blanklcd_ = lcden ^ 1;
intreq_.setEventTime<intevent_blit>(blitTime);
@ -207,77 +226,23 @@ unsigned long Memory::event(unsigned long cc) {
updateSerial(cc);
break;
case intevent_oam:
intreq_.setEventTime<intevent_oam>(lastOamDmaUpdate_ == disabled_time
? static_cast<unsigned long>(disabled_time)
: intreq_.eventTime(intevent_oam) + 0xA0 * 4);
if (lastOamDmaUpdate_ != disabled_time) {
unsigned const oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_;
intreq_.setEventTime<intevent_oam>(
lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4);
}
else
intreq_.setEventTime<intevent_oam>(disabled_time);
break;
case intevent_dma:
{
bool const doubleSpeed = isDoubleSpeed();
unsigned dmaSrc = dmaSource_;
unsigned dmaDest = dmaDestination_;
unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10;
unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength;
ackDmaReq(intreq_);
if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
length = 0x10000 - dmaDest;
ioamhram_[0x155] |= 0x80;
}
dmaLength -= length;
if (!(ioamhram_[0x140] & lcdc_en))
dmaLength = 0;
{
unsigned long lOamDmaUpdate = lastOamDmaUpdate_;
lastOamDmaUpdate_ = disabled_time;
while (length--) {
unsigned const src = dmaSrc++ & 0xFFFF;
unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF
? 0xFF
: read(src, cc);
cc += 2 << doubleSpeed;
if (cc - 3 > lOamDmaUpdate) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lOamDmaUpdate += 4;
if (oamDmaPos_ < 0xA0) {
if (oamDmaPos_ == 0)
startOamDma(lOamDmaUpdate - 1);
ioamhram_[src & 0xFF] = data;
} else if (oamDmaPos_ == 0xA0) {
endOamDma(lOamDmaUpdate - 1);
lOamDmaUpdate = disabled_time;
}
}
nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc);
}
lastOamDmaUpdate_ = lOamDmaUpdate;
}
cc += 4;
dmaSource_ = dmaSrc;
dmaDestination_ = dmaDest;
ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80);
if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
lcd_.disableHdma(cc);
}
interrupter_.prefetch(cc, *this);
cc = dma(cc);
if (haltHdmaState_ == hdma_requested) {
haltHdmaState_ = hdma_low;
intreq_.setMinIntTime(cc);
cc -= 4;
}
break;
case intevent_tima:
tima_.doIrqEvent(TimaInterruptRequester(intreq_));
@ -292,45 +257,21 @@ unsigned long Memory::event(unsigned long cc) {
}
if (halted()) {
if (gbIsCgb_ || (!gbIsCgb_ && cc <= halttime_ + 4))
cc += 4;
cc += 4 * (isCgb() || cc - intreq_.eventTime(intevent_interrupts) < 2);
if (cc > lastOamDmaUpdate_)
updateOamDma(cc);
if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low)
|| haltHdmaState_ == hdma_requested) {
flagHdmaReq(intreq_);
}
intreq_.unhalt();
intreq_.setEventTime<intevent_unhalt>(disabled_time);
}
if (ime()) {
cc += 12;
sp_ = (sp_ - 1) & 0xFFFF;
write(sp_, pc_ >> 8, cc);
cc += 4;
updateIrqs(cc);
unsigned const pendingIrqs = intreq_.pendingIrqs();
sp_ = (sp_ - 1) & 0xFFFF;
write(sp_, pc_ & 0xFF, cc);
cc += 2;
unsigned const n = pendingIrqs & -pendingIrqs;
unsigned address;
if (n == 0) {
address = 0;
} else if (n <= 4) {
static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 };
address = lut[n-1];
} else
address = 0x50 + n;
updateIrqs(cc);
intreq_.ackIrq(n);
cc += 2;
pc_ = address;
di();
cc = interrupter_.interrupt(cc, *this);
}
break;
@ -339,38 +280,175 @@ unsigned long Memory::event(unsigned long cc) {
return cc;
}
unsigned long Memory::stop(unsigned long cc) {
unsigned long Memory::dma(unsigned long cc) {
bool const doubleSpeed = isDoubleSpeed();
unsigned dmaSrc = dmaSource_;
unsigned dmaDest = dmaDestination_;
unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 1) * 0x10;
unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength;
if (1ul * dmaDest + length >= 0x10000) {
length = 0x10000 - dmaDest;
ioamhram_[0x155] |= 0x80;
}
dmaLength -= length;
if (!(ioamhram_[0x140] & lcdc_en))
dmaLength = 0;
unsigned long lOamDmaUpdate = lastOamDmaUpdate_;
lastOamDmaUpdate_ = disabled_time;
while (length--) {
unsigned const src = dmaSrc++ & 0xFFFF;
unsigned const data = (src & -vrambank_size()) == mm_vram_begin || src >= mm_oam_begin
? 0xFF
: read(src, cc);
cc += 2 + 2 * doubleSpeed;
if (cc - 3 > lOamDmaUpdate && !halted()) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lOamDmaUpdate += 4;
if (oamDmaPos_ == oamDmaStartPos_)
startOamDma(lOamDmaUpdate);
if (oamDmaPos_ < oam_size) {
ioamhram_[src & 0xFF] = data;
}
else if (oamDmaPos_ == oam_size) {
endOamDma(lOamDmaUpdate);
if (oamDmaStartPos_ == 0)
lOamDmaUpdate = disabled_time;
}
}
nontrivial_write(mm_vram_begin | dmaDest++ % vrambank_size(), data, cc);
}
lastOamDmaUpdate_ = lOamDmaUpdate;
ackDmaReq(intreq_);
cc += 4;
if (ioamhram_[0x14D] & isCgb()) {
psg_.generateSamples(cc + 4, isDoubleSpeed());
lcd_.speedChange((cc + 7) & ~7);
cart_.speedChange(cc);
ioamhram_[0x14D] ^= 0x81;
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: cc + (70224 << isDoubleSpeed()));
dmaSource_ = dmaSrc;
dmaDestination_ = dmaDest;
ioamhram_[0x155] = halted()
? ioamhram_[0x155] | 0x80
: ((dmaLength / 0x10 - 1) & 0xFF) | (ioamhram_[0x155] & 0x80);
if (intreq_.eventTime(intevent_end) > cc) {
intreq_.setEventTime<intevent_end>(cc
+ ( isDoubleSpeed()
? (intreq_.eventTime(intevent_end) - cc) << 1
: (intreq_.eventTime(intevent_end) - cc) >> 1));
}
intreq_.halt();
intreq_.setEventTime<intevent_unhalt>(cc + 0x20000);
}
else {
stopped_ = true;
intreq_.halt();
if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
lcd_.disableHdma(cc);
}
return cc;
}
static void decCycles(unsigned long &counter, unsigned long dec) {
if (counter != disabled_time)
counter -= dec;
void Memory::freeze(unsigned long cc) {
// permanently halt CPU.
// simply halt and clear IE to avoid unhalt from occuring,
// which avoids additional state to represent a "frozen" state.
nontrivial_ff_write(0xFF, 0, cc);
ackDmaReq(intreq_);
intreq_.halt();
}
bool Memory::halt(unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc)
? hdma_high : hdma_low;
bool const hdmaReq = hdmaReqFlagged(intreq_);
if (hdmaReq)
haltHdmaState_ = hdma_requested;
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
ackDmaReq(intreq_);
intreq_.halt();
return hdmaReq;
}
unsigned Memory::pendingIrqs(unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
updateIrqs(cc);
return intreq_.pendingIrqs();
}
void Memory::ackIrq(unsigned bit, unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
// TODO: adjust/extend IRQ assertion time rather than use the odd cc offsets?
// NOTE: a minimum offset of 2 is required for the LCD due to implementation assumptions w.r.t. cc headroom.
updateSerial(cc + 3 + isCgb());
updateTimaIrq(cc + 2 + isCgb());
lcd_.update(cc + 2);
intreq_.ackIrq(bit);
}
unsigned long Memory::stop(unsigned long cc, bool &skip) {
// FIXME: this is incomplete.
intreq_.setEventTime<intevent_unhalt>(cc + 0x20000 + 4);
// speed change.
if (ioamhram_[0x14D] & isCgb()) {
tima_.speedChange(TimaInterruptRequester(intreq_));
// DIV reset.
nontrivial_ff_write(0x04, 0, cc);
haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc)
? hdma_high : hdma_low;
skip = hdmaReqFlagged(intreq_);
if (skip && isDoubleSpeed())
haltHdmaState_ = hdma_requested;
unsigned long const cc_ = cc + 8 * !isDoubleSpeed();
if (cc_ >= cc + 4) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
if (!skip || isDoubleSpeed())
ackDmaReq(intreq_);
intreq_.halt();
}
psg_.speedChange(cc_, isDoubleSpeed());
lcd_.speedChange(cc_);
cart_.speedChange(cc_);
ioamhram_[0x14D] ^= 0x81;
// TODO: perhaps make this a bit nicer?
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: cc + (lcd_cycles_per_frame << isDoubleSpeed()));
if (intreq_.eventTime(intevent_end) > cc_) {
intreq_.setEventTime<intevent_end>(cc_
+ (isDoubleSpeed()
? (intreq_.eventTime(intevent_end) - cc_) * 2
: (intreq_.eventTime(intevent_end) - cc_) / 2));
}
if (cc_ < cc + 4) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
if (!skip || !isDoubleSpeed())
ackDmaReq(intreq_);
intreq_.halt();
}
// ensure that no updates with a previous cc occur.
cc += 8;
}
else {
// FIXME: test and implement stop correctly.
skip = halt(cc);
cc += 4;
stopped_ = true;
intreq_.setEventTime<intevent_unhalt>(disabled_time);
}
return cc;
}
void Memory::decEventCycles(IntEventId eventId, unsigned long dec) {
@ -384,15 +462,9 @@ unsigned long Memory::resetCounters(unsigned long cc) {
updateIrqs(cc);
{
unsigned long divinc = (cc - divLastUpdate_) >> 8;
ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF;
divLastUpdate_ += divinc << 8;
}
unsigned long const dec = cc < 0x10000
? 0
: (cc & ~0x7FFFul) - 0x8000;
unsigned long const dec = cc < 0x20000
? 0
: (cc & -0x10000l) - 0x10000;
decCycles(divLastUpdate_, dec);
decCycles(lastOamDmaUpdate_, dec);
decEventCycles(intevent_serial, dec);
@ -434,62 +506,68 @@ void Memory::updateOamDma(unsigned long const cc) {
unsigned char const *const oamDmaSrc = oamDmaSrcPtr();
unsigned cycles = (cc - lastOamDmaUpdate_) >> 2;
while (cycles--) {
if (halted()) {
lastOamDmaUpdate_ += 4 * cycles;
}
else while (cycles--) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lastOamDmaUpdate_ += 4;
if (oamDmaPos_ == oamDmaStartPos_)
startOamDma(lastOamDmaUpdate_);
if (oamDmaPos_ < 0xA0) {
if (oamDmaPos_ == 0)
startOamDma(lastOamDmaUpdate_ - 1);
ioamhram_[oamDmaPos_] = oamDmaSrc ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead();
} else if (oamDmaPos_ == 0xA0) {
endOamDma(lastOamDmaUpdate_ - 1);
lastOamDmaUpdate_ = disabled_time;
break;
if (oamDmaPos_ < oam_size) {
ioamhram_[oamDmaPos_] = ((oamDmaSrc) ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead());
}
else if (oamDmaPos_ == oam_size) {
endOamDma(lastOamDmaUpdate_);
if (oamDmaStartPos_ == 0) {
lastOamDmaUpdate_ = disabled_time;
break;
}
}
}
}
void Memory::oamDmaInitSetup() {
if (ioamhram_[0x146] < 0xA0) {
cart_.setOamDmaSrc(ioamhram_[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram);
} else if (ioamhram_[0x146] < 0xFE - isCgb() * 0x1E) {
cart_.setOamDmaSrc(ioamhram_[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram);
} else
if (ioamhram_[0x146] < mm_sram_begin / 0x100) {
cart_.setOamDmaSrc(ioamhram_[0x146] < mm_vram_begin / 0x100 ? oam_dma_src_rom : oam_dma_src_vram);
}
else if (ioamhram_[0x146] < 0x100 - isCgb() * 0x20) {
cart_.setOamDmaSrc(ioamhram_[0x146] < mm_wram_begin / 0x100 ? oam_dma_src_sram : oam_dma_src_wram);
}
else
cart_.setOamDmaSrc(oam_dma_src_invalid);
}
static unsigned char const * oamDmaSrcZero() {
static unsigned char zeroMem[0xA0];
return zeroMem;
}
unsigned char const * Memory::oamDmaSrcPtr() const {
unsigned char const* Memory::oamDmaSrcPtr() const {
switch (cart_.oamDmaSrc()) {
case oam_dma_src_rom:
return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8);
return cart_.romdata(ioamhram_[0x146] >> 6) + ioamhram_[0x146] * 0x100l;
case oam_dma_src_sram:
return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0;
return cart_.rsrambankptr() ? cart_.rsrambankptr() + ioamhram_[0x146] * 0x100l : 0;
case oam_dma_src_vram:
return cart_.vrambankptr() + (ioamhram_[0x146] << 8);
return cart_.vrambankptr() + ioamhram_[0x146] * 0x100l;
case oam_dma_src_wram:
return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF);
return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] * 0x100l & 0xFFF);
case oam_dma_src_invalid:
case oam_dma_src_off:
break;
}
return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam();
return cart_.rdisabledRam();
}
void Memory::startOamDma(unsigned long cc) {
oamDmaPos_ = 0;
oamDmaStartPos_ = 0;
lcd_.oamChange(cart_.rdisabledRam(), cc);
}
void Memory::endOamDma(unsigned long cc) {
oamDmaPos_ = 0xFE;
cart_.setOamDmaSrc(oam_dma_src_off);
if (oamDmaStartPos_ == 0) {
oamDmaPos_ = -2u & 0xFF;
cart_.setOamDmaSrc(oam_dma_src_off);
}
lcd_.oamChange(ioamhram_, cc);
}
@ -506,13 +584,7 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) {
updateSerial(cc);
break;
case 0x04:
{
unsigned long divcycles = (cc - divLastUpdate_) >> 8;
ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF;
divLastUpdate_ += divcycles << 8;
}
break;
return (cc - tima_.divLastUpdate()) >> 8 & 0xFF;
case 0x05:
ioamhram_[0x105] = tima_.tima(cc);
break;
@ -561,47 +633,28 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) {
return ioamhram_[p + 0x100];
}
static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) {
struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; };
static Area const cgbAreas[] = {
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0xA000, 0x0000, 0x8000, 0 },
{ 0xFE00, 0x0000, 0xC000, 0 },
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0x0000, 0x0000, 0x0000, 0 }
};
static Area const dmgAreas[] = {
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xA000, 0x0000, 0x8000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0x0000, 0x0000, 0x0000, 0 }
};
Area const *a = cgb ? cgbAreas : dmgAreas;
return p < a[oamDmaSrc].areaUpper
&& p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
}
unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) {
if (p < 0xFF80) {
if (p < mm_hram_begin) {
if (lastOamDmaUpdate_ != disabled_time) {
updateOamDma(cc);
if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0)
return ioamhram_[oamDmaPos_];
if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) {
int const r = isCgb() && cart_.oamDmaSrc() != oam_dma_src_wram && p >= mm_wram_begin
? cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF]
: ioamhram_[oamDmaPos_];
if (isCgb() && cart_.oamDmaSrc() == oam_dma_src_vram)
ioamhram_[oamDmaPos_] = 0;
return r;
}
}
if (p < 0xC000) {
if (p < 0x8000)
if (p < mm_wram_begin) {
if (p < mm_vram_begin)
return cart_.romdata(p >> 14)[p];
if (p < 0xA000) {
if (!lcd_.vramAccessible(cc))
if (p < mm_sram_begin) {
if (!lcd_.vramReadable(cc))
return 0xFF;
return cart_.vrambankptr()[p];
@ -613,18 +666,18 @@ unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) {
return cart_.rtcRead();
}
if (p < 0xFE00)
if (p < mm_oam_begin)
return cart_.wramdata(p >> 12 & 1)[p & 0xFFF];
long const ffp = long(p) - 0xFF00;
long const ffp = static_cast<long>(p) - mm_io_begin;
if (ffp >= 0)
return nontrivial_ff_read(ffp, cc);
if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0)
if (!lcd_.oamReadable(cc) || oamDmaPos_ < oam_size)
return 0xFF;
}
return ioamhram_[p - 0xFE00];
return ioamhram_[p - mm_oam_begin];
}
unsigned Memory::nontrivial_peek(unsigned const p) {
@ -674,17 +727,25 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
if ((data & 0x81) == 0x81) {
intreq_.setEventTime<intevent_serial>(data & isCgb() * 2
? (cc & ~0x07ul) + 0x010 * 8
: (cc & ~0xFFul) + 0x200 * 8);
? cc - (cc - tima_.divLastUpdate()) % 8 + 0x10 * serialCnt_
: cc - (cc - tima_.divLastUpdate()) % 0x100 + 0x200 * serialCnt_);
} else
intreq_.setEventTime<intevent_serial>(disabled_time);
data |= 0x7E - isCgb() * 2;
break;
case 0x04:
ioamhram_[0x104] = 0;
divLastUpdate_ = cc;
tima_.resTac(cc, TimaInterruptRequester(intreq_));
if (intreq_.eventTime(intevent_serial) != disabled_time
&& intreq_.eventTime(intevent_serial) > cc) {
unsigned long const t = intreq_.eventTime(intevent_serial);
unsigned long const n = ioamhram_[0x102] & isCgb() * 2
? t + (cc - t) % 8 - 2 * ((cc - t) & 4)
: t + (cc - t) % 0x100 - 2 * ((cc - t) & 0x80);
intreq_.setEventTime<intevent_serial>(std::max(cc, n));
}
psg_.generateSamples(cc, isDoubleSpeed());
psg_.divReset(isDoubleSpeed());
tima_.divReset(cc, TimaInterruptRequester(intreq_));
return;
case 0x05:
tima_.setTima(data, cc, TimaInterruptRequester(intreq_));
@ -697,7 +758,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
tima_.setTac(data, cc, TimaInterruptRequester(intreq_), agbMode_);
break;
case 0x0F:
updateIrqs(cc);
updateIrqs(cc + 1 + isDoubleSpeed());
intreq_.setIfreg(0xE0 | data);
return;
case 0x10:
@ -739,7 +800,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
psg_.generateSamples(cc, isDoubleSpeed());
psg_.setNr14(data);
psg_.setNr14(data, isDoubleSpeed());
data |= 0xBF;
break;
case 0x16:
@ -773,7 +834,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
psg_.generateSamples(cc, isDoubleSpeed());
psg_.setNr24(data);
psg_.setNr24(data, isDoubleSpeed());
data |= 0xBF;
break;
case 0x1A:
@ -866,8 +927,9 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
ff_write(i, 0, cc);
psg_.setEnabled(false);
} else {
psg_.reset();
}
else {
psg_.reset(isDoubleSpeed());
psg_.setEnabled(true);
}
}
@ -896,8 +958,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
case 0x40:
if (ioamhram_[0x140] != data) {
if ((ioamhram_[0x140] ^ data) & lcdc_en) {
unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc)
& lcdstat_lycflag;
unsigned const stat = data & lcdc_en ? ioamhram_[0x141] : lcd_.getStat(ioamhram_[0x145], cc);
bool const hdmaEnabled = lcd_.hdmaIsEnabled();
lcd_.lcdcChange(data, cc);
@ -905,19 +966,23 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
ioamhram_[0x141] &= 0xF8;
if (data & lcdc_en) {
if (ioamhram_[0x141] & lcdstat_lycirqen && ioamhram_[0x145] == 0 && !(stat & lcdstat_lycflag))
intreq_.flagIrq(2);
intreq_.setEventTime<intevent_blit>(blanklcd_
? lcd_.nextMode1IrqTime()
: lcd_.nextMode1IrqTime()
+ (70224 << isDoubleSpeed()));
} else {
ioamhram_[0x141] |= lyc;
: lcd_.nextMode1IrqTime() + (lcd_cycles_per_frame << isDoubleSpeed()));
}
else {
ioamhram_[0x141] |= stat & lcdstat_lycflag;
intreq_.setEventTime<intevent_blit>(
cc + (456 * 4 << isDoubleSpeed()));
cc + (lcd_cycles_per_line * 4 << isDoubleSpeed()));
if (hdmaEnabled)
flagHdmaReq(intreq_);
}
} else
}
else
lcd_.lcdcChange(data, cc);
ioamhram_[0x140] = data;
@ -926,6 +991,10 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
case 0x41:
lcd_.lcdstatChange(data, cc);
if (!(ioamhram_[0x140] & lcdc_en) && (ioamhram_[0x141] & lcdstat_lycflag)
&& (~ioamhram_[0x141] & lcdstat_lycirqen & (isCgb() ? data : -1))) {
intreq_.flagIrq(2);
}
data = (ioamhram_[0x141] & 0x87) | (data & 0x78);
break;
case 0x42:
@ -938,11 +1007,9 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
lcd_.lycRegChange(data, cc);
break;
case 0x46:
if (lastOamDmaUpdate_ != disabled_time)
endOamDma(cc);
lastOamDmaUpdate_ = cc;
intreq_.setEventTime<intevent_oam>(cc + 8);
oamDmaStartPos_ = (oamDmaPos_ + 2) & 0xFF;
intreq_.setEventTime<intevent_oam>(std::min(intreq_.eventTime(intevent_oam), cc + 8));
ioamhram_[0x146] = data;
oamDmaInitSetup();
return;
@ -1033,8 +1100,8 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
if (isCgb()) {
unsigned index = ioamhram_[0x168] & 0x3F;
lcd_.cgbBgColorChange(index, data, cc);
ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F)
| ((index + (ioamhram_[0x168] >> 7)) & 0x3F);
ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3Fu)
| ((index + (ioamhram_[0x168] >> 7)) & 0x3F);
}
return;
@ -1047,8 +1114,8 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
if (isCgb()) {
unsigned index = ioamhram_[0x16A] & 0x3F;
lcd_.cgbSpColorChange(index, data, cc);
ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F)
| ((index + (ioamhram_[0x16A] >> 7)) & 0x3F);
ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3Fu)
| ((index + (ioamhram_[0x16A] >> 7)) & 0x3F);
}
return;
@ -1092,38 +1159,56 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo
if (lastOamDmaUpdate_ != disabled_time) {
updateOamDma(cc);
if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) {
ioamhram_[oamDmaPos_] = data;
if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) {
if (isCgb()) {
if (p < mm_wram_begin)
ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() != oam_dma_src_vram ? data : 0;
else if (cart_.oamDmaSrc() != oam_dma_src_wram)
cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF] = data;
}
else {
ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() == oam_dma_src_wram
? ioamhram_[oamDmaPos_] & data
: data;
}
return;
}
}
if (p < 0xFE00) {
if (p < 0xA000) {
if (p < 0x8000) {
if (p < mm_oam_begin) {
if (p < mm_sram_begin) {
if (p < mm_vram_begin) {
cart_.mbcWrite(p, data, cc);
} else if (lcd_.vramAccessible(cc)) {
}
else if (lcd_.vramWritable(cc)) {
lcd_.vramChange(cc);
cart_.vrambankptr()[p] = data;
}
} else if (p < 0xC000) {
}
else if (p < mm_wram_begin) {
if (cart_.wsrambankptr())
cart_.wsrambankptr()[p] = data;
else
cart_.rtcWrite(data, cc);
} else
}
else
cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data;
} else if (p - 0xFF80u >= 0x7Fu) {
long const ffp = long(p) - 0xFF00;
}
else if (p - mm_hram_begin >= 0x7Fu) {
long const ffp = static_cast<long>(p) - mm_io_begin;
if (ffp < 0) {
if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) {
if (lcd_.oamWritable(cc) && oamDmaPos_ >= oam_size
&& (p < mm_oam_begin + oam_size || isCgb())) {
lcd_.oamChange(cc);
ioamhram_[p - 0xFE00] = data;
ioamhram_[p - mm_oam_begin] = data;
}
} else
}
else
nontrivial_ff_write(ffp, data, cc);
} else
ioamhram_[p - 0xFE00] = data;
}
else
ioamhram_[p - mm_oam_begin] = data;
}
LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) {
@ -1223,5 +1308,3 @@ SYNCFUNC(Memory)
NSS(LINKCABLE_);
NSS(linkClockTrigger_);
}
}

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(); }
@ -58,7 +59,7 @@ public:
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 ime() const { return intreq_.ime(); }
bool halted() const { return intreq_.halted(); }
@ -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;
@ -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,20 @@ 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 +320,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

@ -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,19 +65,18 @@ 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;
@ -147,7 +148,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 +189,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 {
@ -193,5 +227,3 @@ SYNCFUNC(PSG)
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_);
@ -108,6 +112,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 +148,6 @@ Channel4::Channel4()
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, nextEventUnit_(0)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@ -152,49 +162,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 +207,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 +265,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

@ -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
};
}