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:
Tim Allen 2017-08-22 11:09:07 +10:00
parent 9be4e59a05
commit 11357169a5
10 changed files with 107 additions and 68 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -69,17 +69,24 @@ auto Interface::videoResolution() -> VideoResolution {
} }
auto Interface::videoColors() -> uint32 { auto Interface::videoColors() -> uint32 {
return 1 << 9; return 3 * (1 << 9);
} }
auto Interface::videoColor(uint32 color) -> uint64 { auto Interface::videoColor(uint32 color) -> uint64 {
uint R = color.bits(0,2); uint R = color.bits(0, 2);
uint G = color.bits(3,5); uint G = color.bits(3, 5);
uint B = color.bits(6,8); uint B = color.bits(6, 8);
uint M = color.bits(9,10);
uint64 r = image::normalize(R, 3, 16); uint lookup[3][8] = {
uint64 g = image::normalize(G, 3, 16); { 0, 29, 52, 70, 87, 101, 116, 130}, //shadow
uint64 b = image::normalize(B, 3, 16); { 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; return r << 32 | g << 16 | b << 0;
} }

View File

@ -47,9 +47,6 @@ auto VDP::Background::scanline(uint y) -> void {
auto VDP::Background::run(uint x, uint y) -> void { auto VDP::Background::run(uint x, uint y) -> void {
updateVerticalScroll(x, y); updateVerticalScroll(x, y);
output.priority = 0;
output.color = 0;
x -= state.horizontalScroll; x -= state.horizontalScroll;
y += state.verticalScroll; y += state.verticalScroll;
@ -70,13 +67,11 @@ auto VDP::Background::run(uint x, uint y) -> void {
uint16 tileData = vdp.vram.read(tileAddress); uint16 tileData = vdp.vram.read(tileAddress);
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
if(color) { output.color = color ? tileAttributes.bits(13,14) << 4 | color : 0;
output.color = tileAttributes.bits(13,14) << 4 | color;
output.priority = tileAttributes.bit(15); output.priority = tileAttributes.bit(15);
}
} }
auto VDP::Background::power() -> void { auto VDP::Background::power() -> void {
memory::fill(&io, sizeof(IO)); io = {};
memory::fill(&state, sizeof(State)); state = {};
} }

View File

@ -13,7 +13,9 @@ auto VDP::read(uint24 addr) -> uint16 {
//counter //counter
case 0xc00008: case 0xc0000a: case 0xc0000c: case 0xc0000e: { 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; io.commandPending = false;
uint16 result = 0b0011'0100'0000'0000; 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 |= 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; return result;
} }

View File

@ -1,15 +1,4 @@
auto VDP::frame() -> void {
latch.overscan = io.overscan;
}
auto VDP::scanline() -> void { 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()) { if(state.vcounter < screenHeight()) {
planeA.scanline(state.vcounter); planeA.scanline(state.vcounter);
window.scanline(state.vcounter); window.scanline(state.vcounter);
@ -31,22 +20,40 @@ auto VDP::run() -> void {
planeB.run(state.hdot, state.vcounter); planeB.run(state.hdot, state.vcounter);
sprite.run(state.hdot, state.vcounter); sprite.run(state.hdot, state.vcounter);
auto output = io.backgroundColor; Pixel g = {io.backgroundColor, 0};
if(auto color = planeB.output.color) output = color; Pixel a = planeA.output;
if(auto color = planeA.output.color) output = color; Pixel b = planeB.output;
if(auto color = sprite.output.color) output = color; Pixel s = sprite.output;
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;
outputPixel(cram.read(output)); auto& bg = a.above() || a.color && !b.above() ? a : b.color ? b : g;
state.hdot++; 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())) { for(auto n : range(pixelWidth())) {
state.output[ 0 + n] = color; field[0][n] = color;
state.output[1280 + n] = color; field[1][n] = color;
}
} else {
for(auto n : range(pixelWidth())) {
field[state.field][n] = color;
}
} }
state.output += pixelWidth(); state.output += pixelWidth();
} }

View File

@ -75,14 +75,14 @@ auto VDP::Sprite::run(uint x, uint y) -> void {
uint16 tileData = vdp.vram.read(tileAddress); uint16 tileData = vdp.vram.read(tileAddress);
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
if(color) { if(!color) continue;
output.color = o.palette << 4 | color; output.color = o.palette << 4 | color;
output.priority = o.priority; output.priority = o.priority;
break; break;
} }
}
} }
auto VDP::Sprite::power() -> void { auto VDP::Sprite::power() -> void {
memory::fill(&io, sizeof(IO)); io = {};
} }

View File

@ -37,6 +37,7 @@ auto VDP::main() -> void {
if(state.vcounter < screenHeight()) { if(state.vcounter < screenHeight()) {
while(state.hcounter < 1280) { while(state.hcounter < 1280) {
run(); run();
state.hdot++;
step(pixelWidth()); step(pixelWidth());
} }
@ -51,6 +52,15 @@ auto VDP::main() -> void {
} else { } else {
step(1710); 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 { auto VDP::step(uint clocks) -> void {
@ -74,9 +84,9 @@ auto VDP::power() -> void {
output = buffer + 16 * 1280; //overscan offset output = buffer + 16 * 1280; //overscan offset
memory::fill(&io, sizeof(IO)); io = {};
memory::fill(&latch, sizeof(Latch)); latch = {};
memory::fill(&state, sizeof(State)); state = {};
planeA.power(); planeA.power();
window.power(); window.power();

View File

@ -41,10 +41,17 @@ struct VDP : Thread {
} dma; } dma;
//render.cpp //render.cpp
auto frame() -> void;
auto scanline() -> void; auto scanline() -> void;
auto run() -> 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 { struct Background {
enum class ID : uint { PlaneA, Window, PlaneB } id; enum class ID : uint { PlaneA, Window, PlaneB } id;
@ -89,10 +96,7 @@ struct VDP : Thread {
uint10 verticalScroll; uint10 verticalScroll;
} state; } state;
struct Output { Pixel output;
uint6 color;
uint1 priority;
} output;
}; };
Background planeA{Background::ID::PlaneA}; Background planeA{Background::ID::PlaneA};
Background window{Background::ID::Window}; Background window{Background::ID::Window};
@ -127,10 +131,7 @@ struct VDP : Thread {
uint7 link; uint7 link;
}; };
struct Output { Pixel output;
uint6 color;
uint1 priority;
} output;
array<Object, 80> oam; array<Object, 80> oam;
array<Object, 20> objects; array<Object, 20> objects;
@ -243,9 +244,10 @@ private:
struct State { struct State {
uint32* output = nullptr; uint32* output = nullptr;
uint hdot; uint16 hdot;
uint hcounter; uint16 hcounter;
uint vcounter; uint16 vcounter;
uint1 field;
} state; } state;
uint32 buffer[1280 * 512]; uint32 buffer[1280 * 512];

View File

@ -149,6 +149,12 @@ auto CPU::joypadEdge() -> void {
controllerPort2.device->latch(1); controllerPort2.device->latch(1);
controllerPort1.device->latch(0); controllerPort1.device->latch(0);
controllerPort2.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(); uint2 port0 = controllerPort1.device->data();

View File

@ -43,12 +43,18 @@ Program::Program(string_vector args) {
settings["Crashed"].setValue(true); settings["Crashed"].setValue(true);
settings.save(); settings.save();
if(!Video::availableDrivers().find(settings["Video/Driver"].text())) {
settings["Video/Driver"].setValue("None");
}
video = Video::create(settings["Video/Driver"].text()); video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle()); video->setContext(presentation->viewport.handle());
video->setBlocking(settings["Video/Synchronize"].boolean()); video->setBlocking(settings["Video/Synchronize"].boolean());
if(!video->ready()) MessageDialog().setText("Failed to initialize video driver").warning(); if(!video->ready()) MessageDialog().setText("Failed to initialize video driver").warning();
presentation->clearViewport(); presentation->clearViewport();
if(!Audio::availableDrivers().find(settings["Audio/Driver"].text())) {
settings["Audio/Driver"].setValue("None");
}
audio = Audio::create(settings["Audio/Driver"].text()); audio = Audio::create(settings["Audio/Driver"].text());
audio->setExclusive(settings["Audio/Exclusive"].boolean()); audio->setExclusive(settings["Audio/Exclusive"].boolean());
audio->setContext(presentation->viewport.handle()); audio->setContext(presentation->viewport.handle());
@ -57,6 +63,9 @@ Program::Program(string_vector args) {
audio->setChannels(2); audio->setChannels(2);
if(!audio->ready()) MessageDialog().setText("Failed to initialize audio driver").warning(); 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 = Input::create(settings["Input/Driver"].text());
input->setContext(presentation->viewport.handle()); input->setContext(presentation->viewport.handle());
input->onChange({&InputManager::onChange, &inputManager()}); input->onChange({&InputManager::onChange, &inputManager()});