mirror of https://github.com/bsnes-emu/bsnes.git
Update to v096r04 release.
byuu says: Changelog: - fixed S-DD1 RAM writes (Star Ocean audio fixed) - applied all of the DMG test ROM fixes discussed earlier; passes many more test ROMs now - at least until the GBVideoPlayer is working: for debugging purposes, CPU/PPU single-step now instead of sync just-in-time (~30% slower) - fixed OS X crash on NSTextView (hopefully, would be very odd if not) Unfortunately passing these test ROMs caused my favorite GB/GBC game to break all of its graphics =( Shin Megami Tensei - Devichil - Kuro no Sho (Japan) is all garbled now. I'm really quite bummed by this ... but I guess I'll go through and revert r04's fixes one at a time until I find what's causing it. On the plus side, Astro Rabby is playable now. Still acts weird when pressing B/A on the first screen, but the start button will start the game. EDIT: got it. Shin Megami Tensei - Devichil requires FF4F (VBK) to be readable. Before, it was always returning 0x00. With my return 0xFF patch, that broke. But it should be returning the VBK value, which also fixes it. Also need to handle FF68/FF6A reads. Was really hoping that'd help GBVideoPlayer too, but nope. It doesn't read any of those three registers.
This commit is contained in:
parent
653bb378ee
commit
72b6a8b32e
|
@ -1,5 +1,4 @@
|
|||
#ifndef EMULATOR_HPP
|
||||
#define EMULATOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/dsp.hpp>
|
||||
|
@ -7,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "096.03";
|
||||
static const string Version = "096.04";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
@ -55,5 +54,3 @@ template<typename R, typename... P> struct hook<auto (P...) -> R> {
|
|||
#endif
|
||||
|
||||
using varuint = varuint_t<uint>;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef EMULATOR_INTERFACE_HPP
|
||||
#define EMULATOR_INTERFACE_HPP
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
|
@ -113,5 +112,3 @@ struct Interface {
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef FC_HPP
|
||||
#define FC_HPP
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/r6502/r6502.hpp>
|
||||
|
@ -53,7 +52,6 @@ namespace Famicom {
|
|||
#include <fc/ppu/ppu.hpp>
|
||||
#include <fc/cheat/cheat.hpp>
|
||||
#include <fc/video/video.hpp>
|
||||
#include <fc/interface/interface.hpp>
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <fc/interface/interface.hpp>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#ifndef FC_HPP
|
||||
namespace Famicom {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
|
@ -58,6 +56,4 @@ private:
|
|||
|
||||
extern Interface* interface;
|
||||
|
||||
#ifndef FC_HPP
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -65,7 +65,6 @@ auto APU::power() -> void {
|
|||
create(Main, 2 * 1024 * 1024);
|
||||
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
||||
for(auto& n : mmio_data) n = 0x00;
|
||||
sequencer_base = 0;
|
||||
sequencer_step = 0;
|
||||
|
||||
|
@ -74,42 +73,30 @@ auto APU::power() -> void {
|
|||
wave.power();
|
||||
noise.power();
|
||||
master.power();
|
||||
|
||||
LinearFeedbackShiftRegisterGenerator r;
|
||||
for(auto& n : wave.pattern) n = r();
|
||||
}
|
||||
|
||||
auto APU::mmio_read(uint16 addr) -> uint8 {
|
||||
static const uint8 table[48] = {
|
||||
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
|
||||
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
|
||||
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
|
||||
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
|
||||
0x00, 0x00, 0x70, //master
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
};
|
||||
|
||||
if(addr == 0xff26) {
|
||||
uint8 data = master.enable << 7;
|
||||
if(square1.enable) data |= 0x01;
|
||||
if(square2.enable) data |= 0x02;
|
||||
if( wave.enable) data |= 0x04;
|
||||
if( noise.enable) data |= 0x08;
|
||||
return data | table[addr - 0xff10];
|
||||
}
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
|
||||
//if(!master.enable && addr != 0xff26) return 0xff;
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.read(addr);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.read(addr);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.read(addr);
|
||||
if(addr >= 0xff1f && addr <= 0xff23) return noise.read(addr);
|
||||
if(addr >= 0xff24 && addr <= 0xff26) return master.read(addr);
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) return wave.read(addr);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::mmio_write(uint16 addr, uint8 data) -> void {
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
|
||||
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
|
||||
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
|
||||
if(!master.enable && addr != 0xff26) return;
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.write(addr, data);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.write(addr, data);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write(addr, data);
|
||||
if(addr >= 0xff1f && addr <= 0xff23) return noise.write(addr, data);
|
||||
if(addr >= 0xff24 && addr <= 0xff26) return master.write(addr, data);
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write(addr, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ struct APU : Thread, MMIO {
|
|||
#include "noise/noise.hpp"
|
||||
#include "master/master.hpp"
|
||||
|
||||
uint8 mmio_data[48];
|
||||
uint12 sequencer_base;
|
||||
uint3 sequencer_step;
|
||||
|
||||
|
|
|
@ -39,15 +39,42 @@ auto APU::Master::run() -> void {
|
|||
right >>= 1;
|
||||
}
|
||||
|
||||
auto APU::Master::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff24 NR50
|
||||
auto APU::Master::read(uint16 addr) -> uint8 {
|
||||
if(addr == 0xff24) { //NR50
|
||||
return left_in_enable << 7 | left_volume << 4 | right_in_enable << 3 | right_volume;
|
||||
}
|
||||
|
||||
if(addr == 0xff25) { //NR51
|
||||
return channel4_left_enable << 7
|
||||
| channel3_left_enable << 6
|
||||
| channel2_left_enable << 5
|
||||
| channel1_left_enable << 4
|
||||
| channel4_right_enable << 3
|
||||
| channel3_right_enable << 2
|
||||
| channel2_right_enable << 1
|
||||
| channel1_right_enable << 0;
|
||||
}
|
||||
|
||||
if(addr == 0xff26) { //NR52
|
||||
return enable << 7 | 0x70
|
||||
| apu.noise.enable << 3
|
||||
| apu.wave.enable << 2
|
||||
| apu.square2.enable << 1
|
||||
| apu.square1.enable << 0;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::Master::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff24) { //NR50
|
||||
left_in_enable = data & 0x80;
|
||||
left_volume = (data >> 4) & 7;
|
||||
right_in_enable = data & 0x08;
|
||||
right_volume = (data >> 0) & 7;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff25 NR51
|
||||
if(addr == 0xff25) { //NR51
|
||||
channel4_left_enable = data & 0x80;
|
||||
channel3_left_enable = data & 0x40;
|
||||
channel2_left_enable = data & 0x20;
|
||||
|
@ -58,8 +85,15 @@ auto APU::Master::write(uint r, uint8 data) -> void {
|
|||
channel1_right_enable = data & 0x01;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff26 NR52
|
||||
if(addr == 0xff26) { //NR52
|
||||
enable = data & 0x80;
|
||||
if(!enable) {
|
||||
apu.square1.power();
|
||||
apu.square2.power();
|
||||
apu.wave.power();
|
||||
apu.noise.power();
|
||||
power();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct Master {
|
||||
auto run() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -2,9 +2,14 @@ auto APU::Noise::dac_enable() const -> bool {
|
|||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
auto APU::Noise::get_period() const -> uint {
|
||||
static const uint table[] = {4, 8, 16, 24, 32, 40, 48, 56};
|
||||
return table[divisor] << frequency;
|
||||
}
|
||||
|
||||
auto APU::Noise::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = divisor << frequency;
|
||||
period = get_period();
|
||||
if(frequency < 14) {
|
||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
|
||||
|
@ -18,7 +23,7 @@ auto APU::Noise::run() -> void {
|
|||
}
|
||||
|
||||
auto APU::Noise::clock_length() -> void {
|
||||
if(enable && counter) {
|
||||
if(counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
@ -31,27 +36,50 @@ auto APU::Noise::clock_envelope() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto APU::Noise::write(uint r, uint8 data) -> void {
|
||||
if(r == 1) { //$ff20 NR41
|
||||
auto APU::Noise::read(uint16 addr) -> uint8 {
|
||||
if(addr == 0xff1f) { //NR40
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff20) { //NR41
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff21) { //NR42
|
||||
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
|
||||
}
|
||||
|
||||
if(addr == 0xff22) { //NR43
|
||||
return frequency << 4 | narrow_lfsr << 3 | divisor;
|
||||
}
|
||||
|
||||
if(addr == 0xff23) { //NR44
|
||||
return 0x80 | counter << 6 | 0x3f;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::Noise::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff20) { //NR41
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff21 NR42
|
||||
if(addr == 0xff21) { //NR42
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff22 NR43
|
||||
if(addr == 0xff22) { //NR43
|
||||
frequency = data >> 4;
|
||||
narrow_lfsr = data & 0x08;
|
||||
divisor = (data & 0x07) << 3;
|
||||
if(divisor == 0) divisor = 4;
|
||||
period = divisor << frequency;
|
||||
divisor = data & 0x07;
|
||||
period = get_period();
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff34 NR44
|
||||
if(addr == 0xff23) { //NR44
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
struct Noise {
|
||||
auto dac_enable() const -> bool;
|
||||
auto get_period() const -> uint;
|
||||
|
||||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -16,7 +18,7 @@ struct Noise {
|
|||
uint3 envelope_frequency;
|
||||
uint4 frequency;
|
||||
bool narrow_lfsr;
|
||||
uint divisor;
|
||||
uint3 divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
auto APU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(mmio_data);
|
||||
s.integer(sequencer_base);
|
||||
s.integer(sequencer_step);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ auto APU::Square1::sweep(bool update) -> void {
|
|||
}
|
||||
|
||||
auto APU::Square1::clock_length() -> void {
|
||||
if(counter && enable) {
|
||||
if(counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
@ -58,31 +58,55 @@ auto APU::Square1::clock_envelope() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff10 NR10
|
||||
auto APU::Square1::read(uint16 addr) -> uint8 {
|
||||
if(addr == 0xff10) { //NR10
|
||||
return 0x80 | sweep_frequency << 4 | sweep_direction << 3 | sweep_shift;
|
||||
}
|
||||
|
||||
if(addr == 0xff11) { //NR11
|
||||
return duty << 6 | 0x3f;
|
||||
}
|
||||
|
||||
if(addr == 0xff12) { //NR12
|
||||
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
|
||||
}
|
||||
|
||||
if(addr == 0xff13) { //NR13
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff14) { //NR14
|
||||
return 0x80 | counter << 6 | 0x3f;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::Square1::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff10) { //NR10
|
||||
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
|
||||
sweep_frequency = (data >> 4) & 7;
|
||||
sweep_direction = data & 0x08;
|
||||
sweep_shift = data & 0x07;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff11 NR11
|
||||
if(addr == 0xff11) { //NR11
|
||||
duty = data >> 6;
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff12 NR12
|
||||
if(addr == 0xff12) { //NR12
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff13 NR13
|
||||
if(addr == 0xff13) { //NR13
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff14 NR14
|
||||
if(addr == 0xff14) { //NR14
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
|
|
@ -6,7 +6,8 @@ struct Square1 {
|
|||
auto clock_length() -> void;
|
||||
auto clock_sweep() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -21,7 +21,7 @@ auto APU::Square2::run() -> void {
|
|||
}
|
||||
|
||||
auto APU::Square2::clock_length() -> void {
|
||||
if(counter && enable) {
|
||||
if(counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
@ -34,24 +34,48 @@ auto APU::Square2::clock_envelope() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto APU::Square2::write(uint r, uint8 data) -> void {
|
||||
if(r == 1) { //$ff16 NR21
|
||||
auto APU::Square2::read(uint16 addr) -> uint8 {
|
||||
if(addr == 0xff15) { //NR20
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff16) { //NR21
|
||||
return duty << 6 | 0x3f;
|
||||
}
|
||||
|
||||
if(addr == 0xff17) { //NR22
|
||||
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
|
||||
}
|
||||
|
||||
if(addr == 0xff18) { //NR23
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff19) { //NR24
|
||||
return 0x80 | counter << 6 | 0x3f;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::Square2::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff16) { //NR21
|
||||
duty = data >> 6;
|
||||
length = (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff17 NR22
|
||||
if(addr == 0xff17) { //NR22
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff18 NR23
|
||||
if(addr == 0xff18) { //NR23
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff19 NR24
|
||||
if(addr == 0xff19) { //NR24
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
|
|
@ -4,7 +4,8 @@ struct Square2 {
|
|||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,45 +1,73 @@
|
|||
auto APU::Wave::get_pattern(uint5 offset) const -> uint4 {
|
||||
return pattern[offset >> 1] >> (offset & 1 ? 0 : 4);
|
||||
}
|
||||
|
||||
auto APU::Wave::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 1 * (2048 - frequency);
|
||||
pattern_sample = pattern[++pattern_offset];
|
||||
pattern_sample = get_pattern(++pattern_offset);
|
||||
}
|
||||
|
||||
uint4 sample = pattern_sample >> volume_shift;
|
||||
static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
|
||||
uint4 sample = pattern_sample >> shift[volume];
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
auto APU::Wave::clock_length() -> void {
|
||||
if(enable && counter) {
|
||||
if(counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Wave::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff1a NR30
|
||||
auto APU::Wave::read(uint16 addr) -> uint8 {
|
||||
if(addr == 0xff1a) { //NR30
|
||||
return dac_enable << 7 | 0x7f;
|
||||
}
|
||||
|
||||
if(addr == 0xff1b) { //NR31
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff1c) { //NR32
|
||||
return 0x80 | volume << 5 | 0x1f;
|
||||
}
|
||||
|
||||
if(addr == 0xff1d) { //NR33
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if(addr == 0xff1e) { //NR34
|
||||
return 0x80 | counter << 6 | 0x3f;
|
||||
}
|
||||
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) {
|
||||
return pattern[addr & 15];
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::Wave::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff1a) { //NR30
|
||||
dac_enable = data & 0x80;
|
||||
if(dac_enable == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff1b NR31
|
||||
if(addr == 0xff1b) { //NR31
|
||||
length = data;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff1c NR32
|
||||
switch((data >> 5) & 3) {
|
||||
case 0: volume_shift = 4; break; // 0%
|
||||
case 1: volume_shift = 0; break; //100%
|
||||
case 2: volume_shift = 1; break; // 50%
|
||||
case 3: volume_shift = 2; break; // 25%
|
||||
}
|
||||
if(addr == 0xff1c) { //NR32
|
||||
volume = data >> 5;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff1d NR33
|
||||
if(addr == 0xff1d) { //NR33
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff1e NR34
|
||||
if(addr == 0xff1e) { //NR34
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
@ -50,25 +78,20 @@ auto APU::Wave::write(uint r, uint8 data) -> void {
|
|||
pattern_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Wave::write_pattern(uint p, uint8 data) -> void {
|
||||
p <<= 1;
|
||||
pattern[p + 0] = (data >> 4) & 15;
|
||||
pattern[p + 1] = (data >> 0) & 15;
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) {
|
||||
pattern[addr & 15] = data;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Wave::power() -> void {
|
||||
enable = 0;
|
||||
|
||||
dac_enable = 0;
|
||||
volume_shift = 0;
|
||||
volume = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
LinearFeedbackShiftRegisterGenerator r;
|
||||
for(auto& n : pattern) n = r() & 15;
|
||||
|
||||
output = 0;
|
||||
length = 0;
|
||||
period = 0;
|
||||
|
@ -80,7 +103,7 @@ auto APU::Wave::serialize(serializer& s) -> void {
|
|||
s.integer(enable);
|
||||
|
||||
s.integer(dac_enable);
|
||||
s.integer(volume_shift);
|
||||
s.integer(volume);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
s.array(pattern);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
struct Wave {
|
||||
auto get_pattern(uint5 offset) const -> uint4;
|
||||
|
||||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto write_pattern(uint p, uint8 data) -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -10,10 +12,10 @@ struct Wave {
|
|||
bool enable;
|
||||
|
||||
bool dac_enable;
|
||||
uint volume_shift;
|
||||
uint2 volume;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[32];
|
||||
uint8 pattern[16];
|
||||
|
||||
int16 output;
|
||||
uint8 length;
|
||||
|
|
|
@ -81,13 +81,13 @@ auto CPU::interrupt_test() -> void {
|
|||
}
|
||||
|
||||
auto CPU::interrupt_exec(uint16 pc) -> void {
|
||||
op_io();
|
||||
op_io();
|
||||
op_io();
|
||||
r.ime = 0;
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = pc;
|
||||
op_io();
|
||||
op_io();
|
||||
op_io();
|
||||
}
|
||||
|
||||
auto CPU::stop() -> bool {
|
||||
|
@ -201,8 +201,8 @@ auto CPU::power() -> void {
|
|||
status.interrupt_enable_vblank = 0;
|
||||
|
||||
oamdma.active = false;
|
||||
oamdma.clock = 0;
|
||||
oamdma.bank = 0;
|
||||
oamdma.offset = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
|
|||
auto hblank() -> void;
|
||||
|
||||
struct Status {
|
||||
uint clock;
|
||||
uint22 clock;
|
||||
|
||||
//$ff00 JOYP
|
||||
bool p15;
|
||||
|
@ -53,7 +53,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
|
|||
bool serial_clock;
|
||||
|
||||
//$ff04 DIV
|
||||
uint8 div;
|
||||
uint16 div;
|
||||
|
||||
//$ff05 TIMA
|
||||
uint8 tima;
|
||||
|
@ -109,8 +109,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
|
|||
|
||||
struct OAMDMA {
|
||||
bool active;
|
||||
uint clock;
|
||||
uint8 bank;
|
||||
uint8 offset;
|
||||
} oamdma;
|
||||
|
||||
uint8 wram[32768]; //GB=8192, GBC=32768
|
||||
|
|
|
@ -6,14 +6,16 @@ auto CPU::op_io() -> void {
|
|||
auto CPU::op_read(uint16 addr) -> uint8 {
|
||||
cycle_edge();
|
||||
add_clocks(4);
|
||||
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return 0xff;
|
||||
//OAM is inaccessible during OAMDMA transfer
|
||||
if(oamdma.active && oamdma.clock >= 8 && addr >= 0xfe00 && addr <= 0xfe9f) return 0xff;
|
||||
return bus.read(addr);
|
||||
}
|
||||
|
||||
auto CPU::op_write(uint16 addr, uint8 data) -> void {
|
||||
cycle_edge();
|
||||
add_clocks(4);
|
||||
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return;
|
||||
//OAM is inaccessible during OAMDMA transfer
|
||||
if(oamdma.active && oamdma.clock >= 8 && addr >= 0xfe00 && addr <= 0xfe9f) return;
|
||||
bus.write(addr, data);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ auto CPU::mmio_read(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
if(addr == 0xff04) { //DIV
|
||||
return status.div;
|
||||
return status.div >> 8;
|
||||
}
|
||||
|
||||
if(addr == 0xff05) { //TIMA
|
||||
|
@ -188,8 +188,8 @@ auto CPU::mmio_write(uint16 addr, uint8 data) -> void {
|
|||
|
||||
if(addr == 0xff46) { //DMA
|
||||
oamdma.active = true;
|
||||
oamdma.clock = 0;
|
||||
oamdma.bank = data;
|
||||
oamdma.offset = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ auto CPU::mmio_write(uint16 addr, uint8 data) -> void {
|
|||
|
||||
if(status.dma_mode == 0) {
|
||||
do {
|
||||
for(unsigned n = 0; n < 16; n++) {
|
||||
for(auto n : range(16)) {
|
||||
dma_write(status.dma_target++, dma_read(status.dma_source++));
|
||||
}
|
||||
add_clocks(8 << status.speed_double);
|
||||
|
|
|
@ -55,6 +55,6 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(status.interrupt_enable_vblank);
|
||||
|
||||
s.integer(oamdma.active);
|
||||
s.integer(oamdma.clock);
|
||||
s.integer(oamdma.bank);
|
||||
s.integer(oamdma.offset);
|
||||
}
|
||||
|
|
|
@ -3,37 +3,44 @@
|
|||
// 154 scanlines/frame
|
||||
|
||||
auto CPU::add_clocks(uint clocks) -> void {
|
||||
if(oamdma.active) {
|
||||
for(uint n = 0; n < 4 * clocks; n++) {
|
||||
bus.write(0xfe00 + oamdma.offset, bus.read((oamdma.bank << 8) + oamdma.offset));
|
||||
if(++oamdma.offset == 160) {
|
||||
oamdma.active = false;
|
||||
break;
|
||||
if(system.sgb()) system.clocks_executed += clocks;
|
||||
|
||||
while(clocks--) {
|
||||
if(oamdma.active) {
|
||||
uint offset = oamdma.clock++;
|
||||
if((offset & 3) == 0) {
|
||||
offset >>= 2;
|
||||
if(offset == 0) {
|
||||
//warm-up
|
||||
} else if(offset == 161) {
|
||||
//cool-down; disable
|
||||
oamdma.active = false;
|
||||
} else {
|
||||
bus.write(0xfe00 + offset - 1, bus.read((oamdma.bank << 8) + offset - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(++status.clock == 0) {
|
||||
cartridge.mbc3.second();
|
||||
}
|
||||
|
||||
//4MHz / N(hz) - 1 = mask
|
||||
status.div++;
|
||||
if((status.div & 15) == 0) timer_262144hz();
|
||||
if((status.div & 63) == 0) timer_65536hz();
|
||||
if((status.div & 255) == 0) timer_16384hz();
|
||||
if((status.div & 511) == 0) timer_8192hz();
|
||||
if((status.div & 1023) == 0) timer_4096hz();
|
||||
|
||||
ppu.clock -= ppu.frequency;
|
||||
if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread);
|
||||
|
||||
apu.clock -= apu.frequency;
|
||||
if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread);
|
||||
}
|
||||
|
||||
system.clocks_executed += clocks;
|
||||
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
|
||||
|
||||
status.clock += clocks;
|
||||
if(status.clock >= 4 * 1024 * 1024) {
|
||||
status.clock -= 4 * 1024 * 1024;
|
||||
cartridge.mbc3.second();
|
||||
}
|
||||
|
||||
//4MHz / N(hz) - 1 = mask
|
||||
if((status.clock & 15) == 0) timer_262144hz();
|
||||
if((status.clock & 63) == 0) timer_65536hz();
|
||||
if((status.clock & 255) == 0) timer_16384hz();
|
||||
if((status.clock & 511) == 0) timer_8192hz();
|
||||
if((status.clock & 1023) == 0) timer_4096hz();
|
||||
|
||||
ppu.clock -= clocks * ppu.frequency;
|
||||
if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread);
|
||||
|
||||
apu.clock -= clocks * apu.frequency;
|
||||
if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread);
|
||||
}
|
||||
|
||||
auto CPU::timer_262144hz() -> void {
|
||||
|
@ -61,8 +68,6 @@ auto CPU::timer_16384hz() -> void {
|
|||
interrupt_raise(Interrupt::Timer);
|
||||
}
|
||||
}
|
||||
|
||||
status.div++;
|
||||
}
|
||||
|
||||
auto CPU::timer_8192hz() -> void {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef GB_HPP
|
||||
#define GB_HPP
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/lr35902/lr35902.hpp>
|
||||
|
@ -52,6 +51,6 @@ namespace GameBoy {
|
|||
#include <gb/apu/apu.hpp>
|
||||
#include <gb/cheat/cheat.hpp>
|
||||
#include <gb/video/video.hpp>
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <gb/interface/interface.hpp>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#ifndef GB_HPP
|
||||
namespace GameBoy {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
|
@ -72,6 +70,4 @@ private:
|
|||
|
||||
extern Interface* interface;
|
||||
|
||||
#ifndef GB_HPP
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -45,10 +45,12 @@ auto PPU::main() -> void {
|
|||
}
|
||||
|
||||
auto PPU::add_clocks(uint clocks) -> void {
|
||||
status.lx += clocks;
|
||||
clock += clocks * cpu.frequency;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
co_switch(scheduler.active_thread = cpu.thread);
|
||||
while(clocks--) {
|
||||
status.lx++;
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef GBA_HPP
|
||||
#define GBA_HPP
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/arm/arm.hpp>
|
||||
|
@ -56,7 +55,6 @@ namespace GameBoyAdvance {
|
|||
};
|
||||
|
||||
#include <gba/memory/memory.hpp>
|
||||
#include <gba/interface/interface.hpp>
|
||||
#include <gba/scheduler/scheduler.hpp>
|
||||
#include <gba/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
|
@ -67,4 +65,4 @@ namespace GameBoyAdvance {
|
|||
#include <gba/video/video.hpp>
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <gba/interface/interface.hpp>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#ifndef GBA_HPP
|
||||
namespace GameBoyAdvance {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
|
@ -55,6 +53,4 @@ private:
|
|||
|
||||
extern Interface* interface;
|
||||
|
||||
#ifndef GBA_HPP
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -103,9 +103,9 @@ auto LR35902::op_ld_sp_hl() {
|
|||
}
|
||||
|
||||
template<uint x> auto LR35902::op_push_rr() {
|
||||
op_io();
|
||||
op_write(--r[SP], r[x] >> 8);
|
||||
op_write(--r[SP], r[x] >> 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<uint x> auto LR35902::op_pop_rr() {
|
||||
|
@ -297,24 +297,24 @@ template<uint x> auto LR35902::op_dec_rr() {
|
|||
}
|
||||
|
||||
auto LR35902::op_add_sp_n() {
|
||||
op_io();
|
||||
op_io();
|
||||
int n = (int8)op_read(r[PC]++);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
|
||||
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
|
||||
r[SP] += n;
|
||||
op_io();
|
||||
op_io();
|
||||
}
|
||||
|
||||
auto LR35902::op_ld_hl_sp_n() {
|
||||
op_io();
|
||||
int n = (int8)op_read(r[PC]++);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
|
||||
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
|
||||
r[HL] = r[SP] + n;
|
||||
op_io();
|
||||
}
|
||||
|
||||
//rotate/shift commands
|
||||
|
@ -618,20 +618,20 @@ template<uint x, bool y> auto LR35902::op_jr_f_n() {
|
|||
auto LR35902::op_call_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
op_io();
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<uint x, bool y> auto LR35902::op_call_f_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
if(r.f[x] == y) {
|
||||
op_io();
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,8 +661,8 @@ auto LR35902::op_reti() {
|
|||
}
|
||||
|
||||
template<uint n> auto LR35902::op_rst_n() {
|
||||
op_io();
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = n;
|
||||
op_io();
|
||||
}
|
||||
|
|
|
@ -146,26 +146,14 @@ auto SDD1::mcurom_read(uint addr, uint8) -> uint8 {
|
|||
auto SDD1::mcurom_write(uint addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
//map address=00-3f,80-bf:6000-7fff mask=0xe000
|
||||
//map address=70-7d:0000-7fff mask=0x8000
|
||||
auto SDD1::mcuram_read(uint addr, uint8 data) -> uint8 {
|
||||
if((addr & 0x60e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
|
||||
return ram.read(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
|
||||
return ram.read(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
return ram.read(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
auto SDD1::mcuram_write(uint addr, uint8 data) -> void {
|
||||
if((addr & 0x60e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
|
||||
return ram.write(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
|
||||
return ram.write(addr & 0x1fff, data);
|
||||
}
|
||||
return ram.write(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#ifndef SFC_HPP
|
||||
namespace SuperFamicom {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
|
@ -126,6 +124,4 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
extern Interface* interface;
|
||||
|
||||
#ifndef SFC_HPP
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef SFC_HPP
|
||||
#define SFC_HPP
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/arm/arm.hpp>
|
||||
|
@ -71,10 +70,9 @@ namespace SuperFamicom {
|
|||
#include <sfc/slot/slot.hpp>
|
||||
#include <sfc/cartridge/cartridge.hpp>
|
||||
#include <sfc/cheat/cheat.hpp>
|
||||
#include <sfc/interface/interface.hpp>
|
||||
|
||||
#include <sfc/memory/memory-inline.hpp>
|
||||
#include <sfc/ppu/counter/counter-inline.hpp>
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <sfc/interface/interface.hpp>
|
||||
|
|
|
@ -78,15 +78,13 @@ auto pTextEdit::setCursor(Cursor cursor) -> void {
|
|||
|
||||
auto pTextEdit::setEditable(bool editable) -> void {
|
||||
@autoreleasepool {
|
||||
[[cocoaView content] setEditable:editable];
|
||||
[[cocoaView content] setEditable:(editable && self().enabled(true))];
|
||||
}
|
||||
}
|
||||
|
||||
auto pTextEdit::setEnabled(bool enabled) -> void {
|
||||
pWidget::setEnabled(enabled);
|
||||
@autoreleasepool {
|
||||
[[cocoaView content] setEnabled:enabled];
|
||||
}
|
||||
setEditable(self().editable); //Cocoa lacks NSTextView::setEnabled; simulate via setEnabled()
|
||||
}
|
||||
|
||||
auto pTextEdit::setFont(const Font& font) -> void {
|
||||
|
|
Loading…
Reference in New Issue