mirror of https://github.com/bsnes-emu/bsnes.git
Update to v102r09 release.
byuu says: Changelog: - MD: restructured DMA to a subclass of VDP - MD: implemented VRAM copy mode (fixes Langrisser II ... mostly) - MS: implemened PSG support [Cydrak] - GG: implemented PSG stereo sound support - MS: use the new struct Model {} design that other cores use The MS/GG PSG should be feature complete, but I don't have good tests for Game Gear stereo mode, nor for the noise channel. There's also a really weird behavior with when to reload the channel counters on volume register writes. I can confirm what Cydrak observed in that following the docs and reloading always creates serious audio distortion problems. So, more research is needed there. To get the correct sound out of the PSG, I have to run it at 3.58MHz / 16, which seems really weird to me. The docs make it sound like it's supposed to run at the full 3.58MHz. If we can really run it at 223.7KHz, then that's help reduce the overhead of PSG emulation, which will definitely come in handy for Mega Drive, and possibly later Mega CD, emulation. I have not implemented the PSG into the Mega Drive just yet. Nor have I implemented save states or cheat code support into the MS/GG cores yet. The latter is next on my list.
This commit is contained in:
parent
d76c0c7e82
commit
8071da4c6a
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "102.08";
|
||||
static const string Version = "102.09";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -1,36 +1,45 @@
|
|||
auto VDP::dmaRun() -> void {
|
||||
if(!io.dmaEnable) return;
|
||||
if(!io.command.bit(5)) return;
|
||||
auto VDP::DMA::run() -> void {
|
||||
if(!io.enable || io.wait) return;
|
||||
if(!vdp.io.command.bit(5)) return;
|
||||
|
||||
if(io.dmaMode <= 1) return dmaLoad();
|
||||
if(io.dmaMode == 2) return dmaFill();
|
||||
if(io.dmaMode == 3) return dmaCopy();
|
||||
if(io.mode <= 1) return load();
|
||||
if(io.mode == 2) return fill();
|
||||
if(io.mode == 3) return copy();
|
||||
}
|
||||
|
||||
auto VDP::dmaLoad() -> void {
|
||||
auto VDP::DMA::load() -> void {
|
||||
cpu.wait |= Wait::VDP_DMA;
|
||||
|
||||
auto data = busCPU.readWord(io.dmaMode.bit(0) << 23 | io.dmaSource << 1);
|
||||
writeDataPort(data);
|
||||
auto data = busCPU.readWord(io.mode.bit(0) << 23 | io.source << 1);
|
||||
vdp.writeDataPort(data);
|
||||
|
||||
io.dmaSource.bits(0,15)++;
|
||||
if(--io.dmaLength == 0) {
|
||||
io.command.bit(5) = 0;
|
||||
io.source.bits(0,15)++;
|
||||
if(--io.length == 0) {
|
||||
vdp.io.command.bit(5) = 0;
|
||||
cpu.wait &=~ Wait::VDP_DMA;
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::dmaFill() -> void {
|
||||
if(io.dmaFillWait) return;
|
||||
auto VDP::DMA::fill() -> void {
|
||||
auto data = io.fill;
|
||||
vdp.writeDataPort(data << 8 | data << 0);
|
||||
|
||||
auto data = io.dmaFillByte;
|
||||
writeDataPort(data << 8 | data << 0);
|
||||
|
||||
io.dmaSource.bits(0,15)++;
|
||||
if(--io.dmaLength == 0) {
|
||||
io.command.bit(5) = 0;
|
||||
io.source.bits(0,15)++;
|
||||
if(--io.length == 0) {
|
||||
vdp.io.command.bit(5) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::dmaCopy() -> void {
|
||||
auto VDP::DMA::copy() -> void {
|
||||
auto data = vdp.vram[io.source.bits(0,14)];
|
||||
vdp.writeDataPort(data);
|
||||
|
||||
io.source.bits(0,15)++;
|
||||
if(--io.length == 0) {
|
||||
vdp.io.command.bit(5) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::DMA::power() -> void {
|
||||
memory::fill(&io, sizeof(IO));
|
||||
}
|
||||
|
|
|
@ -74,8 +74,9 @@ auto VDP::writeDataPort(uint16 data) -> void {
|
|||
io.commandPending = false;
|
||||
|
||||
//DMA VRAM fill
|
||||
if(io.dmaFillWait.lower()) {
|
||||
io.dmaFillByte = data >> 8;
|
||||
if(dma.io.wait.lower()) {
|
||||
dma.io.fill = data >> 8;
|
||||
return;
|
||||
}
|
||||
|
||||
//VRAM write
|
||||
|
@ -132,7 +133,7 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
|
||||
io.command.bits(2,5) = data.bits(4,7);
|
||||
io.address.bits(14,15) = data.bits(0,1);
|
||||
io.dmaFillWait = io.dmaMode == 2 && io.command.bits(4,5) == 2;
|
||||
if(dma.io.mode == 3) dma.io.wait = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -162,12 +163,12 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
case 0x01: {
|
||||
io.videoMode = data.bit(2);
|
||||
io.overscan = data.bit(3);
|
||||
io.dmaEnable = data.bit(4);
|
||||
dma.io.enable = data.bit(4);
|
||||
io.verticalBlankInterruptEnable = data.bit(5);
|
||||
io.displayEnable = data.bit(6);
|
||||
io.externalVRAM = data.bit(7);
|
||||
|
||||
if(!io.dmaEnable) io.command.bit(5) = 0;
|
||||
if(!dma.io.enable) io.command.bit(5) = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -280,32 +281,33 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
|
||||
//DMA length
|
||||
case 0x13: {
|
||||
io.dmaLength.bits(0,7) = data.bits(0,7);
|
||||
dma.io.length.bits(0,7) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA length
|
||||
case 0x14: {
|
||||
io.dmaLength.bits(8,15) = data.bits(0,7);
|
||||
dma.io.length.bits(8,15) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA source
|
||||
case 0x15: {
|
||||
io.dmaSource.bits(0,7) = data.bits(0,7);
|
||||
dma.io.source.bits(0,7) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA source
|
||||
case 0x16: {
|
||||
io.dmaSource.bits(8,15) = data.bits(0,7);
|
||||
dma.io.source.bits(8,15) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA source
|
||||
case 0x17: {
|
||||
io.dmaSource.bits(16,21) = data.bits(0,5);
|
||||
io.dmaMode = data.bits(6,7);
|
||||
dma.io.source.bits(16,21) = data.bits(0,5);
|
||||
dma.io.mode = data.bits(6,7);
|
||||
dma.io.wait = dma.io.mode.bit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ auto VDP::main() -> void {
|
|||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
dmaRun();
|
||||
dma.run();
|
||||
Thread::step(1);
|
||||
synchronize(cpu);
|
||||
synchronize(apu);
|
||||
|
@ -63,6 +63,7 @@ auto VDP::power() -> void {
|
|||
window.power();
|
||||
planeB.power();
|
||||
sprite.power();
|
||||
dma.power();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,10 +19,23 @@ struct VDP : Thread {
|
|||
auto writeControlPort(uint16 data) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dmaRun() -> void;
|
||||
auto dmaLoad() -> void;
|
||||
auto dmaFill() -> void;
|
||||
auto dmaCopy() -> void;
|
||||
struct DMA {
|
||||
auto run() -> void;
|
||||
auto load() -> void;
|
||||
auto fill() -> void;
|
||||
auto copy() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
struct IO {
|
||||
uint2 mode;
|
||||
uint22 source;
|
||||
uint16 length;
|
||||
uint8 fill;
|
||||
boolean enable;
|
||||
boolean wait;
|
||||
} io;
|
||||
} dma;
|
||||
|
||||
//render.cpp
|
||||
auto scanline() -> void;
|
||||
|
@ -119,15 +132,10 @@ private:
|
|||
auto screenHeight() const -> uint { return io.overscan ? 240 : 224; }
|
||||
|
||||
uint16 vram[32768];
|
||||
uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware
|
||||
uint9 cram[64];
|
||||
uint10 vsram[40];
|
||||
|
||||
struct IO {
|
||||
//internal state
|
||||
boolean dmaFillWait;
|
||||
uint8 dmaFillByte;
|
||||
|
||||
//command
|
||||
uint6 command;
|
||||
uint16 address;
|
||||
|
@ -142,7 +150,6 @@ private:
|
|||
//$01 mode register 2
|
||||
uint1 videoMode; //0 = Master System; 1 = Mega Drive
|
||||
uint1 overscan; //0 = 224 lines; 1 = 240 lines
|
||||
uint1 dmaEnable;
|
||||
uint1 verticalBlankInterruptEnable;
|
||||
uint1 displayEnable;
|
||||
uint1 externalVRAM;
|
||||
|
@ -170,13 +177,6 @@ private:
|
|||
|
||||
//$0f data port auto-increment value
|
||||
uint8 dataIncrement;
|
||||
|
||||
//$13-$14 DMA length
|
||||
uint16 dmaLength;
|
||||
|
||||
//$15-$17 DMA source
|
||||
uint22 dmaSource;
|
||||
uint2 dmaMode;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
|
|
|
@ -19,7 +19,7 @@ auto Bus::in(uint8 addr) -> uint8 {
|
|||
switch(addr >> 6) {
|
||||
|
||||
case 0: {
|
||||
if(system.model() == Model::GameGear) {
|
||||
if(Model::GameGear()) {
|
||||
bool start = !platform->inputPoll(ID::Port::Hardware, ID::Device::GameGearControls, 6);
|
||||
return start << 7 | 0x7f;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ auto Bus::in(uint8 addr) -> uint8 {
|
|||
}
|
||||
|
||||
case 3: {
|
||||
if(system.model() == Model::MasterSystem) {
|
||||
if(Model::MasterSystem()) {
|
||||
bool reset = !platform->inputPoll(ID::Port::Hardware, ID::Device::MasterSystemControls, 0);
|
||||
auto port1 = peripherals.controllerPort1->readData();
|
||||
auto port2 = peripherals.controllerPort2->readData();
|
||||
|
@ -46,7 +46,8 @@ auto Bus::in(uint8 addr) -> uint8 {
|
|||
return port2.bits(2,5) << 0 | reset << 4 | 1 << 5 | port1.bit(6) << 6 | port2.bit(6) << 7;
|
||||
}
|
||||
}
|
||||
if(system.model() == Model::GameGear) {
|
||||
|
||||
if(Model::GameGear()) {
|
||||
bool up = !platform->inputPoll(ID::Port::Hardware, ID::Device::GameGearControls, 0);
|
||||
bool down = !platform->inputPoll(ID::Port::Hardware, ID::Device::GameGearControls, 1);
|
||||
bool left = !platform->inputPoll(ID::Port::Hardware, ID::Device::GameGearControls, 2);
|
||||
|
@ -61,6 +62,7 @@ auto Bus::in(uint8 addr) -> uint8 {
|
|||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
@ -70,8 +72,16 @@ auto Bus::in(uint8 addr) -> uint8 {
|
|||
}
|
||||
|
||||
auto Bus::out(uint8 addr, uint8 data) -> void {
|
||||
if(addr == 0x06) {
|
||||
if(Model::GameGear()) return psg.balance(data);
|
||||
}
|
||||
|
||||
switch(addr >> 6) {
|
||||
|
||||
case 1: {
|
||||
return psg.write(data);
|
||||
}
|
||||
|
||||
case 2: {
|
||||
return !addr.bit(0) ? vdp.data(data) : vdp.control(data);
|
||||
}
|
||||
|
|
|
@ -8,17 +8,16 @@ Cartridge cartridge;
|
|||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
|
||||
switch(system.model()) {
|
||||
case Model::MasterSystem:
|
||||
if(Model::MasterSystem()) {
|
||||
if(auto pathID = platform->load(ID::MasterSystem, "Master System", "ms")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
case Model::GameGear:
|
||||
}
|
||||
|
||||
if(Model::GameGear()) {
|
||||
if(auto pathID = platform->load(ID::GameGear, "Game Gear", "gg")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
||||
|
|
|
@ -31,7 +31,7 @@ auto CPU::step(uint clocks) -> void {
|
|||
|
||||
//called once per frame
|
||||
auto CPU::pollPause() -> void {
|
||||
if(system.model() == Model::MasterSystem) {
|
||||
if(Model::MasterSystem()) {
|
||||
static bool pause = 0;
|
||||
bool state = platform->inputPoll(ID::Port::Hardware, ID::Device::MasterSystemControls, 1);
|
||||
if(!pause && state) setNMI(1);
|
||||
|
|
|
@ -53,6 +53,6 @@ auto GameGearInterface::videoColor(uint32 color) -> uint64 {
|
|||
}
|
||||
|
||||
auto GameGearInterface::load(uint id) -> bool {
|
||||
if(id == ID::GameGear) return system.load(this, Model::GameGear);
|
||||
if(id == ID::GameGear) return system.load(this, System::Model::GameGear);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,6 @@ auto MasterSystemInterface::videoColor(uint32 color) -> uint64 {
|
|||
}
|
||||
|
||||
auto MasterSystemInterface::load(uint id) -> bool {
|
||||
if(id == ID::MasterSystem) return system.load(this, Model::MasterSystem);
|
||||
if(id == ID::MasterSystem) return system.load(this, System::Model::MasterSystem);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -16,11 +16,6 @@ namespace MasterSystem {
|
|||
extern Scheduler scheduler;
|
||||
struct Interface;
|
||||
|
||||
enum class Model : uint {
|
||||
MasterSystem,
|
||||
GameGear,
|
||||
};
|
||||
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
|
@ -32,6 +27,11 @@ namespace MasterSystem {
|
|||
}
|
||||
};
|
||||
|
||||
struct Model {
|
||||
inline static auto MasterSystem() -> bool;
|
||||
inline static auto GameGear() -> bool;
|
||||
};
|
||||
|
||||
#include <ms/controller/controller.hpp>
|
||||
|
||||
#include <ms/cpu/cpu.hpp>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
//note: tone is supposed to reload counters on volume writes
|
||||
//however, if this is always done, the output is very grainy
|
||||
//as such, this behavior is suppressed when pitch >= 2 (which is a hack)
|
||||
|
||||
auto PSG::write(uint8 data) -> void {
|
||||
bool l = data.bit(7);
|
||||
if(l) select = data.bits(4,6);
|
||||
|
||||
switch(select) {
|
||||
|
||||
case 0: {
|
||||
if(l) tone0.pitch.bits(0,3) = data.bits(0,3);
|
||||
else tone0.pitch.bits(4,9) = data.bits(0,5);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
tone0.volume = data.bits(0,3);
|
||||
if(tone0.pitch < 2) {
|
||||
tone0.output = 1;
|
||||
tone0.counter = tone0.pitch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
if(l) tone1.pitch.bits(0,3) = data.bits(0,3);
|
||||
else tone1.pitch.bits(4,9) = data.bits(0,5);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
tone1.volume = data.bits(0,3);
|
||||
if(tone1.pitch < 2) {
|
||||
tone1.output = 1;
|
||||
tone1.counter = tone1.pitch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: {
|
||||
if(l) tone2.pitch.bits(0,3) = data.bits(0,3);
|
||||
else tone2.pitch.bits(4,9) = data.bits(0,5);
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: {
|
||||
tone2.volume = data.bits(0,3);
|
||||
if(tone2.pitch < 2) {
|
||||
tone2.output = 1;
|
||||
tone2.counter = tone2.pitch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 6: {
|
||||
noise.rate = data.bits(0,1);
|
||||
noise.enable = data.bit(2);
|
||||
noise.lfsr = 0x8000;
|
||||
break;
|
||||
}
|
||||
|
||||
case 7: {
|
||||
noise.volume = data.bits(0,3);
|
||||
noise.output = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Game Gear only
|
||||
auto PSG::balance(uint8 data) -> void {
|
||||
tone0.right = data.bit(0);
|
||||
tone1.right = data.bit(1);
|
||||
tone2.right = data.bit(2);
|
||||
noise.right = data.bit(3);
|
||||
tone0.left = data.bit(4);
|
||||
tone1.left = data.bit(5);
|
||||
tone2.left = data.bit(6);
|
||||
noise.left = data.bit(7);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
auto PSG::Noise::run() -> void {
|
||||
auto latch = clock;
|
||||
|
||||
counter++;
|
||||
if(rate < 3) {
|
||||
clock ^= counter & ((16 << rate) - 1) == 0;
|
||||
} else {
|
||||
clock ^= psg.tone2.clock;
|
||||
}
|
||||
|
||||
if(!latch && clock) {
|
||||
auto eor = enable ? ~lfsr >> 3 : 0;
|
||||
lfsr = (lfsr ^ eor) << 15 | lfsr >> 1;
|
||||
}
|
||||
|
||||
output = lfsr.bit(0);
|
||||
}
|
||||
|
||||
auto PSG::Noise::power() -> void {
|
||||
volume = ~0;
|
||||
counter = 0;
|
||||
enable = 0;
|
||||
rate = 0;
|
||||
lfsr = 0x8000;
|
||||
clock = 0;
|
||||
output = 0;
|
||||
|
||||
left = 1;
|
||||
right = 1;
|
||||
}
|
|
@ -3,14 +3,42 @@
|
|||
namespace MasterSystem {
|
||||
|
||||
PSG psg;
|
||||
#include "io.cpp"
|
||||
#include "tone.cpp"
|
||||
#include "noise.cpp"
|
||||
|
||||
auto PSG::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), psg.main();
|
||||
}
|
||||
|
||||
auto PSG::main() -> void {
|
||||
tone0.run();
|
||||
tone1.run();
|
||||
tone2.run();
|
||||
noise.run();
|
||||
|
||||
int left = 0;
|
||||
if(tone0.output && tone0.left) left += levels[tone0.volume];
|
||||
if(tone1.output && tone1.left) left += levels[tone1.volume];
|
||||
if(tone2.output && tone2.left) left += levels[tone2.volume];
|
||||
if(noise.output && noise.left) left += levels[noise.volume];
|
||||
|
||||
lowpassLeft += (left - lowpassLeft) * 20.0 / 256.0;
|
||||
left = left * 2.0 / 6.0 + lowpassLeft * 3.0 / 4.0;
|
||||
left = sclamp<16>(left);
|
||||
|
||||
int right = 0;
|
||||
if(tone0.output && tone0.right) right += levels[tone0.volume];
|
||||
if(tone1.output && tone1.right) right += levels[tone1.volume];
|
||||
if(tone2.output && tone2.right) right += levels[tone2.volume];
|
||||
if(noise.output && noise.right) right += levels[noise.volume];
|
||||
|
||||
lowpassRight += (right - lowpassRight) * 20.0 / 256.0;
|
||||
right = right * 2.0 / 6.0 + lowpassRight * 3.0 / 4.0;
|
||||
right = sclamp<16>(right);
|
||||
|
||||
step(1);
|
||||
stream->sample(0.0, 0.0);
|
||||
stream->sample(left / 32768.0, right / 32768.0);
|
||||
}
|
||||
|
||||
auto PSG::step(uint clocks) -> void {
|
||||
|
@ -19,8 +47,23 @@ auto PSG::step(uint clocks) -> void {
|
|||
}
|
||||
|
||||
auto PSG::power() -> void {
|
||||
create(PSG::Enter, system.colorburst());
|
||||
stream = Emulator::audio.createStream(2, system.colorburst());
|
||||
//Master System is monaural; Game Gear is stereo
|
||||
//use stereo mode for both; output same sample to both channels for Master System
|
||||
create(PSG::Enter, system.colorburst() / 16.0);
|
||||
stream = Emulator::audio.createStream(2, frequency());
|
||||
|
||||
select = 0;
|
||||
lowpassLeft = 0;
|
||||
lowpassRight = 0;
|
||||
for(auto n : range(15)) {
|
||||
levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5;
|
||||
}
|
||||
levels[15] = 0;
|
||||
|
||||
tone0.power();
|
||||
tone1.power();
|
||||
tone2.power();
|
||||
noise.power();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,48 @@ struct PSG : Thread {
|
|||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto write(uint8 data) -> void;
|
||||
auto balance(uint8 data) -> void;
|
||||
|
||||
private:
|
||||
struct Tone {
|
||||
//tone.cpp
|
||||
auto run() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
uint4 volume;
|
||||
uint10 counter;
|
||||
uint10 pitch;
|
||||
uint1 clock;
|
||||
uint1 output;
|
||||
|
||||
uint1 left;
|
||||
uint1 right;
|
||||
} tone0, tone1, tone2;
|
||||
|
||||
struct Noise {
|
||||
//noise.cpp
|
||||
auto run() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
uint4 volume;
|
||||
uint6 counter;
|
||||
uint1 enable;
|
||||
uint2 rate;
|
||||
uint16 lfsr;
|
||||
uint1 clock;
|
||||
uint1 output;
|
||||
|
||||
uint1 left;
|
||||
uint1 right;
|
||||
} noise;
|
||||
|
||||
uint3 select;
|
||||
int lowpassLeft;
|
||||
int lowpassRight;
|
||||
uint16 levels[16];
|
||||
};
|
||||
|
||||
extern PSG psg;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
auto PSG::Tone::run() -> void {
|
||||
clock = 0;
|
||||
if(--counter) return;
|
||||
|
||||
clock = 1;
|
||||
counter = pitch;
|
||||
output ^= 1;
|
||||
}
|
||||
|
||||
auto PSG::Tone::power() -> void {
|
||||
volume = ~0;
|
||||
counter = 0;
|
||||
pitch = 0;
|
||||
clock = 0;
|
||||
output = 0;
|
||||
|
||||
left = 1;
|
||||
right = 1;
|
||||
}
|
|
@ -15,7 +15,7 @@ auto Peripherals::reset() -> void {
|
|||
auto Peripherals::connect(uint port, uint device) -> void {
|
||||
cpu.peripherals.reset();
|
||||
|
||||
if(system.model() == Model::MasterSystem) {
|
||||
if(Model::MasterSystem()) {
|
||||
if(port == ID::Port::Controller1) {
|
||||
settings.controllerPort1 = device;
|
||||
if(!system.loaded()) return;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct System {
|
||||
enum class Model : uint { MasterSystem, GameGear };
|
||||
|
||||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto model() const -> Model { return information.model; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
@ -33,3 +35,6 @@ struct Peripherals {
|
|||
|
||||
extern System system;
|
||||
extern Peripherals peripherals;
|
||||
|
||||
auto Model::MasterSystem() -> bool { return system.model() == System::Model::MasterSystem; }
|
||||
auto Model::GameGear() -> bool { return system.model() == System::Model::GameGear; }
|
||||
|
|
|
@ -51,8 +51,8 @@ auto VDP::data(uint8 data) -> void {
|
|||
vram[io.address++] = data;
|
||||
} else {
|
||||
uint mask = 0;
|
||||
if(system.model() == Model::MasterSystem) mask = 0x1f;
|
||||
if(system.model() == Model::GameGear) mask = 0x3f;
|
||||
if(Model::MasterSystem()) mask = 0x1f;
|
||||
if(Model::GameGear()) mask = 0x3f;
|
||||
cram[io.address++ & mask] = data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ auto VDP::step(uint clocks) -> void {
|
|||
}
|
||||
|
||||
auto VDP::refresh() -> void {
|
||||
if(system.model() == Model::MasterSystem) {
|
||||
if(Model::MasterSystem()) {
|
||||
//center the video output vertically in the viewport
|
||||
uint32* screen = buffer;
|
||||
if(vlines() == 224) screen += 16 * 256;
|
||||
|
@ -81,7 +81,7 @@ auto VDP::refresh() -> void {
|
|||
Emulator::video.refresh(screen, 256 * sizeof(uint32), 256, 240);
|
||||
}
|
||||
|
||||
if(system.model() == Model::GameGear) {
|
||||
if(Model::GameGear()) {
|
||||
Emulator::video.refresh(buffer + 48 * 256 + 48, 256 * sizeof(uint32), 160, 144);
|
||||
}
|
||||
}
|
||||
|
@ -107,11 +107,11 @@ auto VDP::power() -> void {
|
|||
}
|
||||
|
||||
auto VDP::palette(uint5 index) -> uint12 {
|
||||
if(system.model() == Model::MasterSystem) {
|
||||
if(Model::MasterSystem()) {
|
||||
return cram[index];
|
||||
}
|
||||
|
||||
if(system.model() == Model::GameGear) {
|
||||
if(Model::GameGear()) {
|
||||
return cram[index * 2 + 0] << 0 | cram[index * 2 + 1] << 8;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue