Update to v101r25 release.

byuu says:

Changelog:

  - SMS: emulated VDP mode 4 graphical output (background, sprites)
  - added $(windres) to icarus as well

I'm sure the VDP emulation is still really, really buggy, but
essentially I handle:

  - mode 4 rendering
  - background scrolling
  - background hscroll lock
  - background vscroll lock
  - background nametable relocation
  - sprite nametable relocation
  - sprite tiledata relocation
  - sprite 192-line y=0xd0 edge case (end sprite rendering)
  - sprite 8-pixel x-coordinate displacement
  - sprite extended size (height only in mode 4)
  - sprite overflow
  - sprite collision
  - left column masking
  - display disable
  - backdrop color
  - 192, 224, 240 height

I do not support:

  - mode 2 rendering
  - sprite zoom
  - disallowing 240 height in NTSC mode
  - PAL mode
  - probably lots more
This commit is contained in:
Tim Allen 2016-12-30 18:24:35 +11:00
parent e30780bb72
commit 5bdf55f08f
10 changed files with 213 additions and 10 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 = "101.25"; static const string Version = "101.26";
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

@ -66,7 +66,15 @@ auto Interface::videoColors() -> uint32 {
} }
auto Interface::videoColor(uint32 color) -> uint64 { auto Interface::videoColor(uint32 color) -> uint64 {
return 0; uint2 B = color >> 4;
uint2 G = color >> 2;
uint2 R = color >> 0;
uint64 r = image::normalize(R, 2, 16);
uint64 g = image::normalize(G, 2, 16);
uint64 b = image::normalize(B, 2, 16);
return r << 32 | g << 16 | b << 0;
} }
auto Interface::audioFrequency() -> double { auto Interface::audioFrequency() -> double {

View File

@ -0,0 +1,54 @@
auto VDP::Background::scanline() -> void {
state.x = 0;
state.y = vdp.io.vcounter;
}
auto VDP::Background::run() -> void {
if(state.y >= vdp.vlines()) {
output.color = 0;
output.palette = 0;
output.priority = 0;
return;
}
uint8 hoffset = state.x;
if(!vdp.io.horizontalScrollLock || hoffset >= 16) hoffset += vdp.io.hscroll;
uint8 voffset = state.y;
if(!vdp.io.verticalScrollLock || hoffset <= 191) voffset += vdp.io.vscroll;
uint14 nameTableAddress;
if(vdp.vlines() == 192) {
nameTableAddress = vdp.io.nameTableAddress << 11;
} else {
nameTableAddress = (vdp.io.nameTableAddress & ~1) << 11 | 0x700;
}
nameTableAddress += ((voffset >> 3) << 6) + ((hoffset >> 3) << 1);
uint16 tiledata;
tiledata = vdp.vram[nameTableAddress + 0] << 0;
tiledata |= vdp.vram[nameTableAddress + 1] << 8;
uint14 patternAddress = tiledata.bits(0,8) << 5;
if(tiledata.bit(9)) hoffset ^= 7;
if(tiledata.bit(10)) voffset ^= 7;
output.palette = tiledata.bit(11);
output.priority = tiledata.bit(12);
auto index = hoffset & 7;
patternAddress += (voffset & 7) << 2;
output.color.bit(0) = vdp.vram[patternAddress + 0].bit(index);
output.color.bit(1) = vdp.vram[patternAddress + 1].bit(index);
output.color.bit(2) = vdp.vram[patternAddress + 2].bit(index);
output.color.bit(3) = vdp.vram[patternAddress + 3].bit(index);
state.x++;
}
auto VDP::Background::power() -> void {
}
auto VDP::Background::reset() -> void {
memory::fill(&state, sizeof(State));
memory::fill(&output, sizeof(Output));
}

View File

@ -50,7 +50,7 @@ auto VDP::data(uint8 data) -> void {
if(io.code <= 2) { if(io.code <= 2) {
vram[io.address++] = data; vram[io.address++] = data;
} else { } else {
cram[io.address++ & 0x3f] = data; cram[io.address++ & 0x1f] = data;
} }
} }

70
higan/ms/vdp/sprite.cpp Normal file
View File

@ -0,0 +1,70 @@
auto VDP::Sprite::scanline() -> void {
state.x = 0;
state.y = vdp.io.vcounter;
objects.reset();
bool large = vdp.io.extendedHeight;
uint14 attributeAddress = vdp.io.spriteAttributeTableAddress << 8;
for(uint index : range(64)) {
uint8 y = vdp.vram[attributeAddress + index];
uint8 x = vdp.vram[attributeAddress + 0x80 + (index << 1)];
uint8 pattern = vdp.vram[attributeAddress + 0x81 + (index << 1)];
if(vdp.vlines() == 192 && y == 0xd0) break;
if(vdp.io.spriteShift) x -= 8;
y += 1;
if(state.y < y) continue;
if(state.y > y + (large ? 15 : 7)) continue;
if(large) pattern.bit(0) = 0;
objects.append({x, y, pattern});
if(objects.size() == 8) {
vdp.io.spriteOverflow = 1;
break;
}
}
}
auto VDP::Sprite::run() -> void {
output.color = 0;
if(state.y >= vdp.vlines()) return;
bool large = vdp.io.extendedHeight;
for(auto& o : objects) {
if(state.x < o.x) continue;
if(state.x > o.x + 7) continue;
uint x = o.x - state.x;
uint y = o.y - state.y;
uint14 address = vdp.io.spritePatternTableAddress << 13;
address += o.pattern << 5;
address += (y & (large ? 15 : 7)) << 2;
uint4 color;
color.bit(0) = vdp.vram[address + 0].bit(x & 7);
color.bit(1) = vdp.vram[address + 1].bit(x & 7);
color.bit(2) = vdp.vram[address + 2].bit(x & 7);
color.bit(3) = vdp.vram[address + 3].bit(x & 7);
if(color == 0) continue;
if(output.color) {
vdp.io.spriteCollision = true;
break;
}
output.color = color;
}
state.x++;
}
auto VDP::Sprite::power() -> void {
}
auto VDP::Sprite::reset() -> void {
memory::fill(&state, sizeof(State));
memory::fill(&output, sizeof(Output));
}

View File

@ -4,6 +4,8 @@ namespace MasterSystem {
VDP vdp; VDP vdp;
#include "io.cpp" #include "io.cpp"
#include "background.cpp"
#include "sprite.cpp"
auto VDP::Enter() -> void { auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main(); while(true) scheduler.synchronize(), vdp.main();
@ -15,16 +17,34 @@ auto VDP::main() -> void {
io.lcounter = io.lineCounter; io.lcounter = io.lineCounter;
io.intLine = 1; io.intLine = 1;
} }
} else {
io.lcounter = io.lineCounter;
} }
if(io.vcounter == vlines() + 1) { if(io.vcounter == vlines() + 1) {
io.lcounter = io.lineCounter;
io.intFrame = 1; io.intFrame = 1;
} }
for(uint x : range(684)) { background.scanline();
step(1); sprite.scanline();
//684 clocks/scanline
for(uint x : range(256)) {
background.run();
sprite.run();
step(2);
uint5 color = cram[16 | io.backdropColor];
if(background.output.color && (background.output.priority || !sprite.output.color)) {
color = cram[background.output.palette << 4 | background.output.color];
} else if(sprite.output.color) {
color = cram[16 | sprite.output.color];
}
if(x <= 7 && io.leftClip) color = cram[7];
if(!io.displayEnable) color = 0;
buffer[io.vcounter * 256 + x] = color;
} }
step(172);
if(io.vcounter == 240) scheduler.exit(Scheduler::Event::Frame); if(io.vcounter == 240) scheduler.exit(Scheduler::Event::Frame);
} }
@ -59,12 +79,17 @@ auto VDP::vblank() -> bool {
} }
auto VDP::power() -> void { auto VDP::power() -> void {
background.power();
sprite.power();
} }
auto VDP::reset() -> void { auto VDP::reset() -> void {
create(VDP::Enter, system.colorburst() * 15.0 / 5.0); create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
memory::fill(&io, sizeof(IO)); memory::fill(&io, sizeof(IO));
background.reset();
sprite.reset();
} }
} }

View File

@ -22,10 +22,56 @@ struct VDP : Thread {
auto control(uint8) -> void; auto control(uint8) -> void;
auto registerWrite(uint4 addr, uint8 data) -> void; auto registerWrite(uint4 addr, uint8 data) -> void;
//background.cpp
struct Background {
auto scanline() -> void;
auto run() -> void;
auto power() -> void;
auto reset() -> void;
struct State {
uint x;
uint y;
} state;
struct Output {
uint4 color;
uint1 palette;
uint1 priority;
} output;
} background;
//sprite.cpp
struct Sprite {
auto scanline() -> void;
auto run() -> void;
auto power() -> void;
auto reset() -> void;
struct Object {
uint8 x;
uint8 y;
uint8 pattern;
};
struct State {
uint x;
uint y;
} state;
struct Output {
uint4 color;
} output;
array<Object, 8> objects;
} sprite;
private: private:
uint32 buffer[256 * 240]; uint32 buffer[256 * 262];
uint8 vram[0x4000]; uint8 vram[0x4000];
uint8 cram[0x40]; uint8 cram[0x20];
struct IO { struct IO {
uint vcounter; //vertical counter uint vcounter; //vertical counter

View File

@ -20,7 +20,6 @@ ifeq ($(platform),windows)
ruby += video.direct3d video.wgl video.directdraw video.gdi ruby += video.direct3d video.wgl video.directdraw video.gdi
ruby += audio.wasapi audio.xaudio2 audio.directsound ruby += audio.wasapi audio.xaudio2 audio.directsound
ruby += input.windows ruby += input.windows
windres := windres
else ifeq ($(platform),macosx) else ifeq ($(platform),macosx)
ruby += video.cgl ruby += video.cgl
ruby += audio.openal ruby += audio.openal

View File

@ -29,7 +29,7 @@ obj/icarus.o: icarus.cpp $(call rwildcard,core/) $(call rwildcard,heuristics/) $
$(compiler) $(cppflags) $(flags) -o obj/icarus.o -c icarus.cpp $(compiler) $(cppflags) $(flags) -o obj/icarus.o -c icarus.cpp
obj/resource.o: obj/resource.o:
windres ../hiro/windows/hiro.rc obj/resource.o $(windres) ../hiro/windows/hiro.rc obj/resource.o
clean: clean:
ifeq ($(platform),macosx) ifeq ($(platform),macosx)

View File

@ -70,6 +70,7 @@ endif
# windows settings # windows settings
ifeq ($(platform),windows) ifeq ($(platform),windows)
link += -lws2_32 -lole32 link += -lws2_32 -lole32
windres := windres
endif endif
# macosx settings # macosx settings