mirror of https://github.com/bsnes-emu/bsnes.git
Update to v104r02 release.
byuu says: Changelog: - md/vdp: backgrounds always update priority bit output [Cydrak] - md/vdp: vcounter.d0 becomes vcounter.d8 in interlace mode 3 - md/vdp: return field number in interlace modes from status register - md/vdp: rework scanline/frame counting in main loop so first frame won't clock to field 1 instead of field 0 - md/vdp: add support for shadow/highlight mode; optimize to minimal code [Cydrak] - md/vdp: update outputPixel() to support interlace modes - sfc/cpu: auto joypad polling start should clear the shift registers; fixes Nuke (PD) - thanks to BMF54123 for this bug report - tomoko: if an invalid video/audio/input driver is found in the configuration file, it's reset to "None" - prevents showing the wrong driver under advanced settings; no longer requires possibly two reboots to fix Note: the Mega Drive interlace mode 1 should be working fully, but I don't know any games that use it. Interlace mode 3 (Sonic 2's two-player mode) does not work at all yet, but this is a good start.
This commit is contained in:
parent
9be4e59a05
commit
11357169a5
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "104.01";
|
||||
static const string Version = "104.02";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -69,17 +69,24 @@ auto Interface::videoResolution() -> VideoResolution {
|
|||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return 1 << 9;
|
||||
return 3 * (1 << 9);
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 color) -> uint64 {
|
||||
uint R = color.bits(0,2);
|
||||
uint G = color.bits(3,5);
|
||||
uint B = color.bits(6,8);
|
||||
uint R = color.bits(0, 2);
|
||||
uint G = color.bits(3, 5);
|
||||
uint B = color.bits(6, 8);
|
||||
uint M = color.bits(9,10);
|
||||
|
||||
uint64 r = image::normalize(R, 3, 16);
|
||||
uint64 g = image::normalize(G, 3, 16);
|
||||
uint64 b = image::normalize(B, 3, 16);
|
||||
uint lookup[3][8] = {
|
||||
{ 0, 29, 52, 70, 87, 101, 116, 130}, //shadow
|
||||
{ 0, 52, 87, 116, 144, 172, 206, 255}, //normal
|
||||
{130, 144, 158, 172, 187, 206, 228, 255}, //highlight
|
||||
};
|
||||
|
||||
uint64 r = image::normalize(lookup[M][R], 8, 16);
|
||||
uint64 g = image::normalize(lookup[M][G], 8, 16);
|
||||
uint64 b = image::normalize(lookup[M][B], 8, 16);
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
}
|
||||
|
|
|
@ -47,9 +47,6 @@ auto VDP::Background::scanline(uint y) -> void {
|
|||
auto VDP::Background::run(uint x, uint y) -> void {
|
||||
updateVerticalScroll(x, y);
|
||||
|
||||
output.priority = 0;
|
||||
output.color = 0;
|
||||
|
||||
x -= state.horizontalScroll;
|
||||
y += state.verticalScroll;
|
||||
|
||||
|
@ -70,13 +67,11 @@ auto VDP::Background::run(uint x, uint y) -> void {
|
|||
|
||||
uint16 tileData = vdp.vram.read(tileAddress);
|
||||
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
||||
if(color) {
|
||||
output.color = tileAttributes.bits(13,14) << 4 | color;
|
||||
output.color = color ? tileAttributes.bits(13,14) << 4 | color : 0;
|
||||
output.priority = tileAttributes.bit(15);
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::Background::power() -> void {
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&state, sizeof(State));
|
||||
io = {};
|
||||
state = {};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ auto VDP::read(uint24 addr) -> uint16 {
|
|||
|
||||
//counter
|
||||
case 0xc00008: case 0xc0000a: case 0xc0000c: case 0xc0000e: {
|
||||
return state.vcounter << 8 | (state.hdot >> 1) << 0;
|
||||
auto vcounter = state.vcounter;
|
||||
if(io.interlaceMode == 3) vcounter.bit(0) = vcounter.bit(8);
|
||||
return vcounter << 8 | (state.hdot >> 1) << 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -114,11 +116,12 @@ auto VDP::readControlPort() -> uint16 {
|
|||
io.commandPending = false;
|
||||
|
||||
uint16 result = 0b0011'0100'0000'0000;
|
||||
result |= 1 << 9; //FIFO empty
|
||||
result |= (state.vcounter >= screenHeight()) << 3; //vertical blank
|
||||
result |= (state.hcounter >= 1280) << 2; //horizontal blank
|
||||
result |= io.command.bit(5) << 1; //DMA active
|
||||
result |= Region::PAL() << 0;
|
||||
result |= io.command.bit(5) << 1; //DMA active
|
||||
result |= (state.hcounter >= 1280) << 2; //horizontal blank
|
||||
result |= (state.vcounter >= screenHeight()) << 3; //vertical blank
|
||||
result |= io.interlaceMode.bit(0) ? state.field << 4 : 0;
|
||||
result |= 1 << 9; //FIFO empty
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,4 @@
|
|||
auto VDP::frame() -> void {
|
||||
latch.overscan = io.overscan;
|
||||
}
|
||||
|
||||
auto VDP::scanline() -> void {
|
||||
state.hdot = 0;
|
||||
state.hcounter = 0;
|
||||
if(++state.vcounter >= frameHeight()) state.vcounter = 0;
|
||||
if(state.vcounter == 0) frame();
|
||||
|
||||
latch.displayWidth = io.displayWidth;
|
||||
|
||||
if(state.vcounter < screenHeight()) {
|
||||
planeA.scanline(state.vcounter);
|
||||
window.scanline(state.vcounter);
|
||||
|
@ -31,22 +20,40 @@ auto VDP::run() -> void {
|
|||
planeB.run(state.hdot, state.vcounter);
|
||||
sprite.run(state.hdot, state.vcounter);
|
||||
|
||||
auto output = io.backgroundColor;
|
||||
if(auto color = planeB.output.color) output = color;
|
||||
if(auto color = planeA.output.color) output = color;
|
||||
if(auto color = sprite.output.color) output = color;
|
||||
if(planeB.output.priority) if(auto color = planeB.output.color) output = color;
|
||||
if(planeA.output.priority) if(auto color = planeA.output.color) output = color;
|
||||
if(sprite.output.priority) if(auto color = sprite.output.color) output = color;
|
||||
Pixel g = {io.backgroundColor, 0};
|
||||
Pixel a = planeA.output;
|
||||
Pixel b = planeB.output;
|
||||
Pixel s = sprite.output;
|
||||
|
||||
outputPixel(cram.read(output));
|
||||
state.hdot++;
|
||||
auto& bg = a.above() || a.color && !b.above() ? a : b.color ? b : g;
|
||||
auto& fg = s.above() || s.color && !b.above() && !a.above() ? s : bg;
|
||||
uint mode = a.priority || b.priority;
|
||||
|
||||
if(&fg == &s) switch(s.color) {
|
||||
case 0x0e:
|
||||
case 0x1e:
|
||||
case 0x2e: mode = 1; break;
|
||||
case 0x3e: mode += 1; fg = bg; break;
|
||||
case 0x3f: mode = 0; fg = bg; break;
|
||||
default: mode |= s.priority; break;
|
||||
}
|
||||
|
||||
auto color = cram.read(fg.color);
|
||||
if(!io.shadowHighlightEnable) mode = 1;
|
||||
outputPixel(mode << 9 | color);
|
||||
}
|
||||
|
||||
auto VDP::outputPixel(uint9 color) -> void {
|
||||
auto VDP::outputPixel(uint32 color) -> void {
|
||||
uint32* field[2] = {&state.output[0], &state.output[1280]};
|
||||
if(!io.interlaceMode.bit(0)) {
|
||||
for(auto n : range(pixelWidth())) {
|
||||
state.output[ 0 + n] = color;
|
||||
state.output[1280 + n] = color;
|
||||
field[0][n] = color;
|
||||
field[1][n] = color;
|
||||
}
|
||||
} else {
|
||||
for(auto n : range(pixelWidth())) {
|
||||
field[state.field][n] = color;
|
||||
}
|
||||
}
|
||||
state.output += pixelWidth();
|
||||
}
|
||||
|
|
|
@ -75,14 +75,14 @@ auto VDP::Sprite::run(uint x, uint y) -> void {
|
|||
|
||||
uint16 tileData = vdp.vram.read(tileAddress);
|
||||
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
||||
if(color) {
|
||||
if(!color) continue;
|
||||
|
||||
output.color = o.palette << 4 | color;
|
||||
output.priority = o.priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::Sprite::power() -> void {
|
||||
memory::fill(&io, sizeof(IO));
|
||||
io = {};
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ auto VDP::main() -> void {
|
|||
if(state.vcounter < screenHeight()) {
|
||||
while(state.hcounter < 1280) {
|
||||
run();
|
||||
state.hdot++;
|
||||
step(pixelWidth());
|
||||
}
|
||||
|
||||
|
@ -51,6 +52,15 @@ auto VDP::main() -> void {
|
|||
} else {
|
||||
step(1710);
|
||||
}
|
||||
|
||||
state.hdot = 0;
|
||||
state.hcounter = 0;
|
||||
if(++state.vcounter >= frameHeight()) {
|
||||
state.vcounter = 0;
|
||||
state.field ^= 1;
|
||||
latch.overscan = io.overscan;
|
||||
}
|
||||
latch.displayWidth = io.displayWidth;
|
||||
}
|
||||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
|
@ -74,9 +84,9 @@ auto VDP::power() -> void {
|
|||
|
||||
output = buffer + 16 * 1280; //overscan offset
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&latch, sizeof(Latch));
|
||||
memory::fill(&state, sizeof(State));
|
||||
io = {};
|
||||
latch = {};
|
||||
state = {};
|
||||
|
||||
planeA.power();
|
||||
window.power();
|
||||
|
|
|
@ -41,10 +41,17 @@ struct VDP : Thread {
|
|||
} dma;
|
||||
|
||||
//render.cpp
|
||||
auto frame() -> void;
|
||||
auto scanline() -> void;
|
||||
auto run() -> void;
|
||||
auto outputPixel(uint9 color) -> void;
|
||||
auto outputPixel(uint32 color) -> void;
|
||||
|
||||
struct Pixel {
|
||||
inline auto above() const -> bool { return priority == 1 && color; }
|
||||
inline auto below() const -> bool { return priority == 0 && color; }
|
||||
|
||||
uint6 color;
|
||||
uint1 priority;
|
||||
};
|
||||
|
||||
struct Background {
|
||||
enum class ID : uint { PlaneA, Window, PlaneB } id;
|
||||
|
@ -89,10 +96,7 @@ struct VDP : Thread {
|
|||
uint10 verticalScroll;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
uint6 color;
|
||||
uint1 priority;
|
||||
} output;
|
||||
Pixel output;
|
||||
};
|
||||
Background planeA{Background::ID::PlaneA};
|
||||
Background window{Background::ID::Window};
|
||||
|
@ -127,10 +131,7 @@ struct VDP : Thread {
|
|||
uint7 link;
|
||||
};
|
||||
|
||||
struct Output {
|
||||
uint6 color;
|
||||
uint1 priority;
|
||||
} output;
|
||||
Pixel output;
|
||||
|
||||
array<Object, 80> oam;
|
||||
array<Object, 20> objects;
|
||||
|
@ -243,9 +244,10 @@ private:
|
|||
|
||||
struct State {
|
||||
uint32* output = nullptr;
|
||||
uint hdot;
|
||||
uint hcounter;
|
||||
uint vcounter;
|
||||
uint16 hdot;
|
||||
uint16 hcounter;
|
||||
uint16 vcounter;
|
||||
uint1 field;
|
||||
} state;
|
||||
|
||||
uint32 buffer[1280 * 512];
|
||||
|
|
|
@ -149,6 +149,12 @@ auto CPU::joypadEdge() -> void {
|
|||
controllerPort2.device->latch(1);
|
||||
controllerPort1.device->latch(0);
|
||||
controllerPort2.device->latch(0);
|
||||
|
||||
//shift registers are cleared at start of auto joypad polling
|
||||
io.joy1 = 0;
|
||||
io.joy2 = 0;
|
||||
io.joy3 = 0;
|
||||
io.joy4 = 0;
|
||||
}
|
||||
|
||||
uint2 port0 = controllerPort1.device->data();
|
||||
|
|
|
@ -43,12 +43,18 @@ Program::Program(string_vector args) {
|
|||
settings["Crashed"].setValue(true);
|
||||
settings.save();
|
||||
|
||||
if(!Video::availableDrivers().find(settings["Video/Driver"].text())) {
|
||||
settings["Video/Driver"].setValue("None");
|
||||
}
|
||||
video = Video::create(settings["Video/Driver"].text());
|
||||
video->setContext(presentation->viewport.handle());
|
||||
video->setBlocking(settings["Video/Synchronize"].boolean());
|
||||
if(!video->ready()) MessageDialog().setText("Failed to initialize video driver").warning();
|
||||
presentation->clearViewport();
|
||||
|
||||
if(!Audio::availableDrivers().find(settings["Audio/Driver"].text())) {
|
||||
settings["Audio/Driver"].setValue("None");
|
||||
}
|
||||
audio = Audio::create(settings["Audio/Driver"].text());
|
||||
audio->setExclusive(settings["Audio/Exclusive"].boolean());
|
||||
audio->setContext(presentation->viewport.handle());
|
||||
|
@ -57,6 +63,9 @@ Program::Program(string_vector args) {
|
|||
audio->setChannels(2);
|
||||
if(!audio->ready()) MessageDialog().setText("Failed to initialize audio driver").warning();
|
||||
|
||||
if(!Input::availableDrivers().find(settings["Input/Driver"].text())) {
|
||||
settings["Input/Driver"].setValue("None");
|
||||
}
|
||||
input = Input::create(settings["Input/Driver"].text());
|
||||
input->setContext(presentation->viewport.handle());
|
||||
input->onChange({&InputManager::onChange, &inputManager()});
|
||||
|
|
Loading…
Reference in New Issue