Update to v097r20 release.

byuu says:

Changelog:
- WS: fixed a major CPU bug where I was using the wrong bits for
  ModR/M's memory mode
- WS: added grayscale PPU emulation (exceptionally buggy)

GunPey now runs, as long as you add:

    eeprom name=save.ram size=0x800

to the manifest after importing with icarus.

Right now, you can't control the game due to missing keypad polling.
There's also a lot of glitchiness with the sprites. Seems like they're
not getting properly cleared sometimes or something.

Also, the PPU emulation is totally unrealistic bullshit. I decode and
evaluate every single tile and sprite on every single pixel of output.
No way in hell the hardware could ever come close to that. The speed's
around 500fps without the insane sprite evaluations, and around 90fps
with it. Obviously, I'll fix this in time.

Nothing else seems to run that I've tried. Not even far enough to
display any output whatsoever. Tried Langrisser Millenium, Rockman
& Forte and Riviera. I really need to update icarus to try and encode
eeprom/sram sizes, because that's going to break a lot of stuff if it's
missing.
This commit is contained in:
Tim Allen 2016-03-01 23:23:18 +11:00
parent 7dc62e3a69
commit 570eb9c5f5
9 changed files with 172 additions and 27 deletions

View File

@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "097.19";
static const string Version = "097.20";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -1,19 +1,14 @@
//ModRM functions
//d7-d6 => mod
//d5-d3 => reg
//d2-d0 => mem
auto V30MZ::modRM() -> void {
auto byte = fetch();
modrm.mod = byte >> 6;
modrm.reg = byte >> 3;
modrm.mem = byte >> 0;
auto data = fetch();
modrm.mem = data.bits(0,2);
modrm.reg = data.bits(3,5);
modrm.mod = data.bits(6,7);
if(modrm.mod == 0 && modrm.mem == 6) {
modrm.segment = segment(r.ds);
modrm.address = fetch(Word);
} else {
switch(modrm.reg) {
switch(modrm.mem) {
case 0: modrm.segment = segment(r.ds); modrm.address = r.bx + r.si; break;
case 1: modrm.segment = segment(r.ds); modrm.address = r.bx + r.di; break;
case 2: modrm.segment = segment(r.ss); modrm.address = r.bp + r.si; break;

View File

@ -3,9 +3,10 @@ auto CPU::poll() -> void {
state.halt = false;
if(!V30MZ::r.f.i) return;
//find and execute first pending interrupt in order of priority (7-0)
for(int n = 7; n >= 0; n--) {
if(r.interruptStatus & r.interruptEnable & (1 << n)) {
interrupt(r.interruptBase + n);
return interrupt(r.interruptBase + n);
}
}
}

View File

@ -12,12 +12,12 @@ auto IO::power() -> void {
}
auto IO::portRead(uint16 addr) -> uint8 {
print("[", hex(addr, 4L), "]: port unmapped\n");
//print("[", hex(addr, 4L), "]: port unmapped\n");
return 0x00;
}
auto IO::portWrite(uint16 addr, uint8 data) -> void {
print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n");
//print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n");
}
auto Bus::read(uint20 addr) -> uint8 {

View File

@ -3,7 +3,7 @@ auto PPU::portRead(uint16 addr) -> uint8 {
if(addr == 0x0000) {
return (
r.screenTwoWindowEnable << 5
| r.screenTwoWindowMode << 4
| r.screenTwoWindowInvert << 4
| r.spriteWindowEnable << 3
| r.spriteEnable << 2
| r.screenTwoEnable << 1
@ -122,7 +122,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
//DISP_CTRL
if(addr == 0x0000) {
r.screenTwoWindowEnable = data.bit(5);
r.screenTwoWindowMode = data.bit(4);
r.screenTwoWindowInvert = data.bit(4);
r.spriteWindowEnable = data.bit(3);
r.spriteEnable = data.bit(2);
r.screenTwoEnable = data.bit(1);
@ -292,8 +292,8 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
//PALMONO
if(addr >= 0x0020 && addr <= 0x003f) {
r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4);
r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0);
r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4);
r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0);
return;
}

View File

@ -4,6 +4,7 @@ namespace WonderSwan {
PPU ppu;
#include "io.cpp"
#include "render.cpp"
#include "video.cpp"
auto PPU::Enter() -> void {
@ -11,7 +12,21 @@ auto PPU::Enter() -> void {
}
auto PPU::main() -> void {
step(256);
if(status.vclk < 144) {
for(uint x = 0; x < 224; x++) {
pixel = {Pixel::Source::None, 0xfff};
renderScreenOne();
renderScreenTwo();
renderSprite();
output[status.vclk * 224 + status.hclk] = pixel.color;
step(1);
}
for(uint x = 224; x < 256; x++) {
step(1);
}
} else {
step(256);
}
scanline();
}

View File

@ -8,20 +8,33 @@ struct PPU : Thread, IO {
auto step(uint clocks) -> void;
auto power() -> void;
//io.cpp
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
uint16 output[224 * 144];
//render.cpp
auto renderScreenOne() -> void;
auto renderScreenTwo() -> void;
auto renderSprite() -> void;
//state
uint12 output[224 * 144];
struct Status {
uint vclk;
uint hclk;
} status;
struct Pixel {
enum class Source : uint { None, ScreenOne, ScreenTwo, Sprite };
Source source;
uint12 color;
} pixel;
struct Registers {
//$0000 DISP_CTRL
bool screenTwoWindowEnable;
bool screenTwoWindowMode;
bool screenTwoWindowInvert;
bool spriteWindowEnable;
bool spriteEnable;
bool screenTwoEnable;

121
higan/ws/ppu/render.cpp Normal file
View File

@ -0,0 +1,121 @@
auto PPU::renderScreenOne() -> void {
if(!r.screenOneEnable) return;
uint8 scrollX = status.hclk + r.scrollOneX;
uint8 scrollY = status.vclk + r.scrollOneY;
uint14 tilemapOffset = r.screenOneMapBase << 11;
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile;
tile.byte(0) = iram[tilemapOffset++];
tile.byte(1) = iram[tilemapOffset++];
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::ScreenOne, poolColor << 0 | poolColor << 4 | poolColor << 8};
}
auto PPU::renderScreenTwo() -> void {
if(!r.screenTwoEnable) return;
bool windowInside = (
status.hclk >= r.screenTwoWindowX0
&& status.hclk <= r.screenTwoWindowX1
&& status.vclk >= r.screenTwoWindowY0
&& status.vclk <= r.screenTwoWindowY1
);
windowInside ^= r.screenTwoWindowInvert;
if(r.screenTwoWindowEnable && !windowInside) return;
uint8 scrollX = status.hclk + r.scrollTwoX;
uint8 scrollY = status.vclk + r.scrollTwoY;
uint14 tilemapOffset = r.screenTwoMapBase << 11;
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile;
tile.byte(0) = iram[tilemapOffset++];
tile.byte(1) = iram[tilemapOffset++];
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
if(tile.bit(11) && tileColor == 0) return;
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::ScreenTwo, poolColor << 0 | poolColor << 4 | poolColor << 8};
}
auto PPU::renderSprite() -> void {
if(!r.spriteEnable) return;
bool windowInside = (
status.hclk >= r.spriteWindowX0
&& status.hclk <= r.spriteWindowX1
&& status.vclk >= r.spriteWindowY0
&& status.vclk <= r.spriteWindowY1
);
uint14 spriteBase = r.spriteBase << 9;
uint7 spriteIndex = r.spriteFirst;
uint8 spriteCount = max(128, (uint)r.spriteCount);
while(spriteCount--) {
uint32 sprite;
sprite.byte(0) = iram[spriteBase + (spriteIndex << 2) + 0];
sprite.byte(1) = iram[spriteBase + (spriteIndex << 2) + 1];
sprite.byte(2) = iram[spriteBase + (spriteIndex << 2) + 2];
sprite.byte(3) = iram[spriteBase + (spriteIndex << 2) + 3];
spriteIndex++;
if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue;
if(pixel.source == Pixel::Source::ScreenTwo && !sprite.bit(13)) continue;
uint8 spriteY = sprite.bits(16,23);
uint8 spriteX = sprite.bits(24,31);
if(status.hclk < spriteX) continue;
if(status.hclk > (uint8)(spriteX + 7)) continue;
if(status.vclk < spriteY) continue;
if(status.vclk > (uint8)(spriteY + 7)) continue;
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
if(sprite.bit(11) && tileColor == 0) continue;
uint3 paletteColor = r.palette[8 + sprite.bits(9,11)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8};
return;
}
}

View File

@ -9,12 +9,12 @@ Video::Video() {
auto Video::power() -> void {
memory::fill(output(), 224 * 224 * sizeof(uint32));
for(auto color : range(1 << 12)) {
for(uint12 color : range(1 << 12)) {
paletteLiteral[color] = color;
uint R = (uint4)(color >> 8);
uint G = (uint4)(color >> 4);
uint B = (uint4)(color >> 0);
uint B = color.bits(0, 3);
uint G = color.bits(4, 7);
uint R = color.bits(8,11);
R = image::normalize(R, 4, 16);
G = image::normalize(G, 4, 16);
@ -26,10 +26,10 @@ auto Video::power() -> void {
auto Video::refresh() -> void {
for(uint y = 0; y < 144; y++) {
auto source = ppu.output + y * 224;
auto target = output() + y * 224;
for(uint x = 0; x < 224; x++) {
auto color = paletteStandard[*source++];
*target++ = color;
//*(output() + y * 224 + x) = color;
*(output() + (223 - x) * 224 + 40 + y) = color;
}
}