From 85ee2af0b2fcfde91ebf4df81aa23a06b2fb39a6 Mon Sep 17 00:00:00 2001 From: TiKevin83 Date: Sun, 16 Feb 2020 12:05:56 -0500 Subject: [PATCH] ~half the changes from Sinamas/GSR 2020 updates --- libgambatte/array.h | 52 ++ libgambatte/libgambatte.vcxproj | 4 + libgambatte/libgambatte.vcxproj.filters | 12 + libgambatte/src/Interrupter.cpp | 103 ++++ libgambatte/src/array.h | 57 ++ libgambatte/src/cpu.cpp | 147 +++--- libgambatte/src/cpu.h | 3 +- libgambatte/src/initstate.cpp | 11 +- libgambatte/src/interrupter.h | 54 ++ libgambatte/src/interruptrequester.cpp | 18 +- libgambatte/src/interruptrequester.h | 8 +- libgambatte/src/mem/cartridge.cpp | 163 +++--- libgambatte/src/mem/cartridge.h | 4 +- libgambatte/src/mem/memptrs.cpp | 162 +++--- libgambatte/src/mem/memptrs.h | 82 +-- libgambatte/src/memory.cpp | 639 +++++++++++++---------- libgambatte/src/memory.h | 22 +- libgambatte/src/savestate.h | 8 +- libgambatte/src/sound.cpp | 68 ++- libgambatte/src/sound.h | 37 +- libgambatte/src/sound/channel1.cpp | 125 ++--- libgambatte/src/sound/channel1.h | 17 +- libgambatte/src/sound/channel2.cpp | 85 ++- libgambatte/src/sound/channel2.h | 14 +- libgambatte/src/sound/channel3.cpp | 148 +++--- libgambatte/src/sound/channel3.h | 23 +- libgambatte/src/sound/channel4.cpp | 142 +++-- libgambatte/src/sound/channel4.h | 17 +- libgambatte/src/sound/duty_unit.cpp | 79 +-- libgambatte/src/sound/duty_unit.h | 3 +- libgambatte/src/sound/envelope_unit.cpp | 31 +- libgambatte/src/sound/length_counter.cpp | 40 +- libgambatte/src/sound/psgdef.h | 31 ++ libgambatte/src/tima.cpp | 66 ++- libgambatte/src/tima.h | 7 +- libgambatte/src/video/lcddef.h | 42 +- 36 files changed, 1527 insertions(+), 997 deletions(-) create mode 100644 libgambatte/array.h create mode 100644 libgambatte/src/Interrupter.cpp create mode 100644 libgambatte/src/array.h create mode 100644 libgambatte/src/interrupter.h create mode 100644 libgambatte/src/sound/psgdef.h diff --git a/libgambatte/array.h b/libgambatte/array.h new file mode 100644 index 0000000000..9b692d8412 --- /dev/null +++ b/libgambatte/array.h @@ -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 + +template +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 +inline T* defined_ptr(T* t) { + typedef char type_is_defined[sizeof * t ? 1 : -1]; + (void)sizeof(type_is_defined); + return t; +} + +#endif \ No newline at end of file diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 2a1e315b6d..549134284f 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -154,9 +154,11 @@ + + @@ -180,6 +182,7 @@ + @@ -192,6 +195,7 @@ + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index b34a8d79f1..93ddd6e40b 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -123,6 +123,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -203,5 +212,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/libgambatte/src/Interrupter.cpp b/libgambatte/src/Interrupter.cpp new file mode 100644 index 0000000000..3ee5af603e --- /dev/null +++ b/libgambatte/src/Interrupter.cpp @@ -0,0 +1,103 @@ +// +// Copyright (C) 2007 by sinamas +// +// 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); + } + } + +} \ No newline at end of file diff --git a/libgambatte/src/array.h b/libgambatte/src/array.h new file mode 100644 index 0000000000..239af8f2ca --- /dev/null +++ b/libgambatte/src/array.h @@ -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 + +template +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 +inline T* defined_ptr(T* t) { + typedef char type_is_defined[sizeof * t ? 1 : -1]; + (void)sizeof(type_is_defined); + return t; +} + +template +inline void defined_delete(T* t) { delete defined_ptr(t); } + +struct defined_deleter { template static void del(T* p) { defined_delete(p); } }; + +#endif \ No newline at end of file diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index d086c9842f..1833b0290b 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -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: { diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 96c1a25b4f..2b75eef7a4 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -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; diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 72e26c6f85..d71543e0ad 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -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; diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h new file mode 100644 index 0000000000..5daa808835 --- /dev/null +++ b/libgambatte/src/interrupter.h @@ -0,0 +1,54 @@ +// +// Copyright (C) 2007 by sinamas +// +// 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 +#include + +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 gsCodes_; + + void applyVblankCheats(unsigned long cc, Memory& mem); + }; + +} + +#endif diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index af37ed584e..eaa0fce20f 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -83,9 +83,14 @@ void InterruptRequester::flagIrq(unsigned bit) { eventTimes_.setValue(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(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(minIntTime_); +} + SYNCFUNC(InterruptRequester) { SSS(eventTimes_); diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index cb54265078..1b8aa3230f 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -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(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: diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 0d6539af68..1cdd7cc968 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -23,18 +23,18 @@ #include #include -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(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(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_); } - -} diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index e07baa4aa8..2260c31349 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -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); } diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 65b5125bbf..82fd17a9bb 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -20,7 +20,56 @@ #include #include -namespace gambatte { +using namespace gambatte; + +namespace { + + template struct OamDmaConflictMap; + template struct OamDmaConflictMap { enum { r = 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = 0x0300 }; }; + template struct OamDmaConflictMap { enum { r = cgb ? 0xF000 : 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = cgb ? 0xFCFF : 0x0000 }; }; + + template + bool isInOamDmaConflictArea(OamDmaSrc src, unsigned p) + { + static unsigned short const m[] = { + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + 0 }; + return p < mm_oam_begin && (m[src] >> (p >> 12) & 1); + } + + template + void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10]) + { + if (OamDmaConflictMap::r & 0x00FF) + std::fill_n(rmem, 8, static_cast(0)); + if (OamDmaConflictMap::r & 0x0C00) + rmem[0xB] = rmem[0xA] = wmem[0xB] = wmem[0xA] = 0; + if (OamDmaConflictMap::r & 0x7000) + rmem[0xE] = rmem[0xD] = rmem[0xC] = wmem[0xE] = wmem[0xD] = wmem[0xC] = 0; + } + + template + void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10], + OamDmaSrc src) + { + switch (src) { + case oam_dma_src_rom: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_sram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_vram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_wram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_invalid: disconnectOamDmaAreas(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(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(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(rmem_, wmem_, oamDmaSrc_) + : ::disconnectOamDmaAreas(rmem_, wmem_, oamDmaSrc_); +} + +bool MemPtrs::isInOamDmaConflictArea(unsigned p) const +{ + return isCgb(*this) + ? ::isInOamDmaConflictArea(oamDmaSrc_, p) + : ::isInOamDmaConflictArea(oamDmaSrc_, p); } // all pointers here are relative to memchunk_ @@ -220,4 +239,3 @@ SYNCFUNC(MemPtrs) NSS(curRomBank_); } -} diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index d41a265394..6191624481 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -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 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: templatevoid 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(); } } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 6ddce9da05..b63a243fe8 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -20,11 +20,27 @@ #include "savestate.h" #include "sound.h" #include "video.h" +#include #include -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(144 * 456ul); + intreq_.setEventTime(1l * lcd_vres * lcd_cycles_per_line); intreq_.setEventTime(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(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter); intreq_.setEventTime(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(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( - lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4); + lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4); } intreq_.setEventTime(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(intreq_.eventTime(intevent_blit) - + (70224 << isDoubleSpeed())); + + (lcd_cycles_per_frame << isDoubleSpeed())); } intreq_.setEventTime(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(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(disabled_time); - nontrivial_ff_write(0xFF04, 0, cc); - pc_ = (pc_ + 1) & 0xFFFF; - cc += 4; + stopped_ = false; break; case intevent_end: intreq_.setEventTime(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(blitTime); @@ -207,77 +226,23 @@ unsigned long Memory::event(unsigned long cc) { updateSerial(cc); break; case intevent_oam: - intreq_.setEventTime(lastOamDmaUpdate_ == disabled_time - ? static_cast(disabled_time) - : intreq_.eventTime(intevent_oam) + 0xA0 * 4); + if (lastOamDmaUpdate_ != disabled_time) { + unsigned const oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_; + intreq_.setEventTime( + lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4); + } + else + intreq_.setEventTime(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(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(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(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(cc - + ( isDoubleSpeed() - ? (intreq_.eventTime(intevent_end) - cc) << 1 - : (intreq_.eventTime(intevent_end) - cc) >> 1)); - } - intreq_.halt(); - intreq_.setEventTime(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(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(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : cc + (lcd_cycles_per_frame << isDoubleSpeed())); + if (intreq_.eventTime(intevent_end) > cc_) { + intreq_.setEventTime(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(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(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(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(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(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(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( - 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(cc + 8); + oamDmaStartPos_ = (oamDmaPos_ + 2) & 0xFF; + intreq_.setEventTime(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(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_); } - -} diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 7d7dcc1d73..babf029853 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -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); diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index c130ab5ecb..5e5bf02349 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -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 wram; Ptr 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 { diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index 69ebe39f18..db98678b2e 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -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_); } - -} diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index b664d8c616..232d52a518 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -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_; diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index 9e94ab2a20..58d90edbf1 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -17,30 +17,28 @@ // #include "channel1.h" +#include "psgdef.h" #include "../savestate.h" #include -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 @@ -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_); } - -} diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h index 36c62e646b..99e03431f8 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -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_; diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index 6f32e4dd46..efbfa18670 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -17,17 +17,17 @@ // #include "channel2.h" +#include "psgdef.h" #include "../savestate.h" #include -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_); } -} diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h index 8934cc7991..978b9bc8ed 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -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_; diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index 223378b1ab..0f6cdb3eee 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -17,20 +17,22 @@ // #include "channel3.h" +#include "psgdef.h" #include "../savestate.h" #include #include -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_); } - -} diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h index fe2e43fd1b..a64ce4f5c3 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -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_; diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index 9528ceb943..2b3f99533c 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -17,23 +17,26 @@ // #include "channel4.h" +#include "psgdef.h" #include "../savestate.h" #include -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_); } - -} diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h index 0ad159181f..629ba11b92 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -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_; diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index 5f9f484742..2c91fa10d3 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -17,17 +17,24 @@ // #include "duty_unit.h" +#include "psgdef.h" #include -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_); } - -} diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h index 0f96140c9e..902beaad74 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -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); diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index 7e9a81bb81..02369c0a41 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -17,9 +17,10 @@ // #include "envelope_unit.h" +#include "psgdef.h" #include -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_); } - -} diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index cdfd4c18bb..50ac6310e9 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -18,9 +18,10 @@ #include "length_counter.h" #include "master_disabler.h" +#include "psgdef.h" #include -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(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_); } - -} diff --git a/libgambatte/src/sound/psgdef.h b/libgambatte/src/sound/psgdef.h new file mode 100644 index 0000000000..e0aa66eb32 --- /dev/null +++ b/libgambatte/src/sound/psgdef.h @@ -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 \ No newline at end of file diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 5b935c8a9c..854d247367 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -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_); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index d1db0854b9..8079dcccab 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -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(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_; diff --git a/libgambatte/src/video/lcddef.h b/libgambatte/src/video/lcddef.h index 5fbf3756cb..0ef743537e 100644 --- a/libgambatte/src/video/lcddef.h +++ b/libgambatte/src/video/lcddef.h @@ -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 + }; }