Update to v102r01 release.

byuu says:

Changelog:

  - MS, MD, PCE: remove controllers from scheduler in destructor
    [hex_usr]
  - PCE: no controller should return all bits set (still causing errant
    key presses when swapping gamepads)
  - PCE: emulate MDR for hardware I/O $0800-$17ff
  - PCE: change video resolution to 1140x242
  - PCE: added tertiary background Vscroll register (secondary cache)
  - PCE: create classes out of VDC VRAM, SATB, CRAM for cleaner access
    and I/O registers
  - PCE: high bits of CRAM read should be set
  - PCE: partially emulated VCE display registers: color frequency, HDS,
    HDW, VDS, VDW
  - PCE: 32-width sprites now split to two 16-width sprites to handle
    overflow properly
  - PCE: hopefully emulated sprite zero hit correctly (it's not well
    documented, and not often used)
  - PCE: trigger line coincidence interrupts during the previous
    scanline's Hblank period
  - tomoko: raise viewport from 320x240 to 326x242 to accommodate PC
    Engine's max resolution
  - nall: workaround for Clang compilation bug that can't figure out
    that a char is an integral data type
This commit is contained in:
Tim Allen 2017-01-22 11:33:36 +11:00
parent ae5968cfeb
commit c40e9754bc
18 changed files with 289 additions and 178 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 = "102"; static const string Version = "102.01";
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

@ -9,6 +9,7 @@ Controller::Controller(uint port) : port(port) {
} }
Controller::~Controller() { Controller::~Controller() {
scheduler.remove(*this);
} }
auto Controller::Enter() -> void { auto Controller::Enter() -> void {

View File

@ -9,6 +9,7 @@ Controller::Controller(uint port) : port(port) {
} }
Controller::~Controller() { Controller::~Controller() {
scheduler.remove(*this);
} }
auto Controller::Enter() -> void { auto Controller::Enter() -> void {

View File

@ -9,6 +9,7 @@ Controller::Controller() {
} }
Controller::~Controller() { Controller::~Controller() {
scheduler.remove(*this);
} }
auto Controller::Enter() -> void { auto Controller::Enter() -> void {

View File

@ -5,7 +5,7 @@ struct Controller : Thread {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
virtual auto readData() -> uint4 { return 0; } virtual auto readData() -> uint4 { return 0x0f; }
virtual auto writeData(uint2) -> void {} virtual auto writeData(uint2) -> void {}
}; };

View File

@ -33,6 +33,7 @@ auto CPU::power() -> void {
memory::fill(&irq, sizeof(IRQ)); memory::fill(&irq, sizeof(IRQ));
memory::fill(&timer, sizeof(Timer)); memory::fill(&timer, sizeof(Timer));
memory::fill(&io, sizeof(IO));
} }
auto CPU::lastCycle() -> void { auto CPU::lastCycle() -> void {

View File

@ -55,6 +55,10 @@ struct CPU : Processor::HuC6280, Thread {
friend class CPU; friend class CPU;
} timer; } timer;
struct IO {
uint8 mdr;
} io;
private: private:
uint8 ram[0x2000]; uint8 ram[0x2000];
}; };

View File

@ -22,12 +22,12 @@ auto CPU::read(uint21 addr) -> uint8 {
//$0800-0bff PSG //$0800-0bff PSG
if((addr & 0x1c00) == 0x0800) { if((addr & 0x1c00) == 0x0800) {
return 0x00; return io.mdr;
} }
//$0c00-0fff Timer //$0c00-0fff Timer
if((addr & 0x1c00) == 0x0c00) { if((addr & 0x1c00) == 0x0c00) {
return timer.value; return (io.mdr & 0x80) | timer.value;
} }
//$1000-13ff I/O //$1000-13ff I/O
@ -43,11 +43,20 @@ auto CPU::read(uint21 addr) -> uint8 {
//$1400-17ff IRQ //$1400-17ff IRQ
if((addr & 0x1c00) == 0x1400) { if((addr & 0x1c00) == 0x1400) {
if(addr.bits(0,1) == 0) {
return io.mdr;
}
if(addr.bits(0,1) == 1) {
return io.mdr;
}
if(addr.bits(0,1) == 2) { if(addr.bits(0,1) == 2) {
return ( return (
irq.disableExternal << 0 irq.disableExternal << 0
| irq.disableVDC << 1 | irq.disableVDC << 1
| irq.disableTimer << 2 | irq.disableTimer << 2
| (io.mdr & 0xf8)
); );
} }
@ -56,6 +65,7 @@ auto CPU::read(uint21 addr) -> uint8 {
irq.pendingExternal << 0 irq.pendingExternal << 0
| irq.pendingVDC << 1 | irq.pendingVDC << 1
| irq.pendingTimer << 2 | irq.pendingTimer << 2
| (io.mdr & 0xf8)
); );
} }
} }
@ -71,7 +81,7 @@ auto CPU::read(uint21 addr) -> uint8 {
} }
} }
return 0x00; return 0xff;
} }
auto CPU::write(uint21 addr, uint8 data) -> void { auto CPU::write(uint21 addr, uint8 data) -> void {
@ -99,11 +109,13 @@ auto CPU::write(uint21 addr, uint8 data) -> void {
//$0800-0bff PSG //$0800-0bff PSG
if((addr & 0x1c00) == 0x0800) { if((addr & 0x1c00) == 0x0800) {
io.mdr = data;
return; return;
} }
//$0c00-0fff Timer //$0c00-0fff Timer
if((addr & 0x1c00) == 0x0c00) { if((addr & 0x1c00) == 0x0c00) {
io.mdr = data;
if(!addr.bit(0)) { if(!addr.bit(0)) {
timer.latch = data.bits(0,6); timer.latch = data.bits(0,6);
} else { } else {
@ -115,12 +127,14 @@ auto CPU::write(uint21 addr, uint8 data) -> void {
//$1000-13ff I/O //$1000-13ff I/O
if((addr & 0x1c00) == 0x1000) { if((addr & 0x1c00) == 0x1000) {
io.mdr = data;
PCEngine::peripherals.controllerPort->writeData(data.bits(0,1)); PCEngine::peripherals.controllerPort->writeData(data.bits(0,1));
return; return;
} }
//$1400-17ff IRQ //$1400-17ff IRQ
if((addr & 0x1c00) == 0x1400) { if((addr & 0x1c00) == 0x1400) {
io.mdr = data;
if(addr.bits(0,1) == 2) { if(addr.bits(0,1) == 2) {
irq.disableExternal = data.bit(0); irq.disableExternal = data.bit(0);
irq.disableVDC = data.bit(1); irq.disableVDC = data.bit(1);

View File

@ -45,13 +45,13 @@ auto Interface::title() -> string {
} }
auto Interface::videoSize() -> VideoSize { auto Interface::videoSize() -> VideoSize {
return {256, 240}; return {1140, 242};
} }
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0; auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 256; uint w = 285;
uint h = 240; uint h = 242;
uint m = min(width / (w * a), height / h); uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)}; return {uint(w * a * m), uint(h * m)};
} }

View File

@ -1,10 +1,11 @@
auto VDC::Background::scanline(uint y) -> void { auto VDC::Background::scanline(uint y) -> void {
hoffset = hscroll;
if(y == 0) { if(y == 0) {
voffset = vscroll; vcounter = vscroll;
} else { } else {
voffset++; vcounter++;
} }
hoffset = hscroll;
voffset = vcounter;
} }
auto VDC::Background::run(uint x, uint y) -> void { auto VDC::Background::run(uint x, uint y) -> void {
@ -15,13 +16,13 @@ auto VDC::Background::run(uint x, uint y) -> void {
batAddress *= width; batAddress *= width;
batAddress += (hoffset >> 3) & (width - 1); batAddress += (hoffset >> 3) & (width - 1);
uint16 tiledata = vdc.vramRead(batAddress); uint16 tiledata = vdc.vram.read(batAddress);
uint16 patternAddress = tiledata.bits(0,11) << 4; uint16 patternAddress = tiledata.bits(0,11) << 4;
patternAddress += (voffset & 7); patternAddress += (voffset & 7);
uint4 palette = tiledata.bits(12,15); uint4 palette = tiledata.bits(12,15);
uint16 d0 = vdc.vramRead(patternAddress + 0); uint16 d0 = vdc.vram.read(patternAddress + 0);
uint16 d1 = vdc.vramRead(patternAddress + 8); uint16 d1 = vdc.vram.read(patternAddress + 8);
uint3 index = 7 - (hoffset & 7); uint3 index = 7 - (hoffset & 7);
uint4 output; uint4 output;
@ -30,6 +31,6 @@ auto VDC::Background::run(uint x, uint y) -> void {
output.bit(2) = d1.bit(0 + index); output.bit(2) = d1.bit(0 + index);
output.bit(3) = d1.bit(8 + index); output.bit(3) = d1.bit(8 + index);
if(enable && output) color = vdc.cram[palette << 4 | output]; if(enable && output) color = vdc.cram.read(palette << 4 | output);
hoffset++; hoffset++;
} }

View File

@ -1,8 +1,8 @@
auto VDC::DMA::step(uint clocks) -> void { auto VDC::DMA::step(uint clocks) -> void {
while(clocks--) { while(clocks--) {
if(vramActive) { if(vramActive) {
uint16 data = vdc.vramRead(source); uint16 data = vdc.vram.read(source);
vdc.vramWrite(target, data); vdc.vram.write(target, data);
sourceIncrementMode == 0 ? source++ : source--; sourceIncrementMode == 0 ? source++ : source--;
targetIncrementMode == 0 ? target++ : target--; targetIncrementMode == 0 ? target++ : target--;
if(!--length) { if(!--length) {
@ -12,8 +12,8 @@ auto VDC::DMA::step(uint clocks) -> void {
} }
if(satbActive) { if(satbActive) {
uint16 data = vdc.vramRead(satbSource + satbOffset); uint16 data = vdc.vram.read(satbSource + satbOffset);
vdc.satb[satbOffset] = data; vdc.satb.write(satbOffset, data);
if(++satbOffset == 256) { if(++satbOffset == 256) {
satbActive = false; satbActive = false;
satbOffset = 0; satbOffset = 0;

View File

@ -1,17 +1,6 @@
auto VDC::vramRead(uint16 addr) -> uint16 {
if(addr.bit(15)) return 0x00;
return vram[addr];
}
auto VDC::vramWrite(uint16 addr, uint16 data) -> void {
if(addr.bit(15)) return;
vram[addr] = data;
}
auto VDC::read(uint11 addr) -> uint8 { auto VDC::read(uint11 addr) -> uint8 {
bool a0 = addr.bit(0); bool a0 = addr.bit(0);
if(!addr.bit(10)) { if(!addr.bit(10)) {
//VDC
if(addr.bit(1) == 0) { if(addr.bit(1) == 0) {
//SR //SR
if(a0) return 0x00; if(a0) return 0x00;
@ -29,26 +18,25 @@ auto VDC::read(uint11 addr) -> uint8 {
if(addr.bit(1) == 1) { if(addr.bit(1) == 1) {
if(io.address == 0x02) { if(io.address == 0x02) {
//VRR //VRR
uint8 data = io.vramDataRead.byte(a0); uint8 data = vram.dataRead.byte(a0);
if(a0) { if(a0) {
io.vramAddressRead += io.vramAddressIncrement; vram.addressRead += vram.addressIncrement;
io.vramDataRead = vramRead(io.vramAddressRead); vram.dataRead = vram.read(vram.addressRead);
} }
return data; return data;
} }
} }
} else { } else {
//VCE
if(addr.bits(0,2) == 0x04) { if(addr.bits(0,2) == 0x04) {
//CTR //CTR
uint8 data = cram[io.colorAddress].bits(0,7); uint8 data = cram.read(cram.address).bits(0,7);
return data; return data;
} }
if(addr.bits(0,2) == 0x05) { if(addr.bits(0,2) == 0x05) {
//CTR //CTR
uint8 data = cram[io.colorAddress].bit(0); uint8 data = 0xfe | cram.read(cram.address).bit(0);
io.colorAddress++; cram.address++;
return data; return data;
} }
} }
@ -59,7 +47,6 @@ auto VDC::read(uint11 addr) -> uint8 {
auto VDC::write(uint11 addr, uint8 data) -> void { auto VDC::write(uint11 addr, uint8 data) -> void {
bool a0 = addr.bit(0); bool a0 = addr.bit(0);
if(!addr.bit(10)) { if(!addr.bit(10)) {
//VDC
if(addr.bit(1) == 0) { if(addr.bit(1) == 0) {
//AR //AR
if(a0) return; if(a0) return;
@ -70,23 +57,23 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(addr.bit(1) == 1) { if(addr.bit(1) == 1) {
if(io.address == 0x00) { if(io.address == 0x00) {
//MAWR //MAWR
io.vramAddressWrite.byte(a0) = data; vram.addressWrite.byte(a0) = data;
return; return;
} }
if(io.address == 0x01) { if(io.address == 0x01) {
//MARR //MARR
io.vramAddressRead.byte(a0) = data; vram.addressRead.byte(a0) = data;
io.vramDataRead = vramRead(io.vramAddressRead); vram.dataRead = vram.read(vram.addressRead);
return; return;
} }
if(io.address == 0x02) { if(io.address == 0x02) {
//VWR //VWR
io.vramDataWrite.byte(a0) = data; vram.dataWrite.byte(a0) = data;
if(a0) { if(a0) {
vramWrite(io.vramAddressWrite, io.vramDataWrite); vram.write(vram.addressWrite, vram.dataWrite);
io.vramAddressWrite += io.vramAddressIncrement; vram.addressWrite += vram.addressIncrement;
} }
return; return;
} }
@ -104,10 +91,10 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
} else { } else {
io.displayOutput = data.bits(0,1); io.displayOutput = data.bits(0,1);
io.dramRefresh = data.bit(2); io.dramRefresh = data.bit(2);
if(data.bits(3,4) == 0) io.vramAddressIncrement = 0x01; if(data.bits(3,4) == 0) vram.addressIncrement = 0x01;
if(data.bits(3,4) == 1) io.vramAddressIncrement = 0x20; if(data.bits(3,4) == 1) vram.addressIncrement = 0x20;
if(data.bits(3,4) == 2) io.vramAddressIncrement = 0x40; if(data.bits(3,4) == 2) vram.addressIncrement = 0x40;
if(data.bits(3,4) == 3) io.vramAddressIncrement = 0x80; if(data.bits(3,4) == 3) vram.addressIncrement = 0x80;
} }
return; return;
} }
@ -127,7 +114,7 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(io.address == 0x08) { if(io.address == 0x08) {
//BYR //BYR
background.vscroll.byte(a0) = data; background.vscroll.byte(a0) = data;
background.voffset = background.vscroll; background.vcounter = background.vscroll;
return; return;
} }
@ -149,9 +136,9 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(io.address == 0x0a) { if(io.address == 0x0a) {
//HSR //HSR
if(!a0) { if(!a0) {
io.horizontalSyncWidth = data.bits(0,4); vce.horizontalSyncWidth = data.bits(0,4);
} else { } else {
io.horizontalDisplayStart = data.bits(0,6); vce.horizontalDisplayStart = data.bits(0,6);
} }
return; return;
} }
@ -159,9 +146,9 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(io.address == 0x0b) { if(io.address == 0x0b) {
//HDR //HDR
if(!a0) { if(!a0) {
io.horizontalDisplayWidth = data.bits(0,6); vce.horizontalDisplayLength = data.bits(0,6);
} else { } else {
io.horizontalDisplayEnd = data.bits(0,6); vce.horizontalDisplayEnd = data.bits(0,6);
} }
return; return;
} }
@ -169,23 +156,27 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(io.address == 0x0c) { if(io.address == 0x0c) {
//VPR //VPR
if(!a0) { if(!a0) {
io.verticalSyncWidth = data.bits(0,4); vce.verticalSyncWidth = data.bits(0,4);
} else { } else {
io.verticalDisplayStart = data.bits(0,7); vce.verticalDisplayStart = data.bits(0,7);
} }
return; return;
} }
if(io.address == 0x0d) { if(io.address == 0x0d) {
//VDR //VDR
io.verticalDisplayWidth.byte(a0) = data; if(!a0) {
vce.verticalDisplayLength.bits(0,7) = data.bits(0,7);
} else {
vce.verticalDisplayLength.bit(8) = data.bit(0);
}
return; return;
} }
if(io.address == 0x0e) { if(io.address == 0x0e) {
//VCR //VCR
if(a0) return; if(a0) return;
io.verticalDisplayEnd = data.bits(0,7); vce.verticalDisplayEnd = data.bits(0,7);
return; return;
} }
@ -227,10 +218,12 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
} }
} }
} else { } else {
//VCE
if(addr.bits(0,2) == 0x00) { if(addr.bits(0,2) == 0x00) {
//CR //CR
io.divisionRatio = data.bits(0,1); if(data.bits(0,1) == 0) vce.clock = 4;
if(data.bits(0,1) == 1) vce.clock = 3;
if(data.bits(0,1) == 2) vce.clock = 2;
if(data.bits(0,1) == 3) vce.clock = 2;
io.colorBlur = data.bit(2); io.colorBlur = data.bit(2);
io.grayscale = data.bit(7); io.grayscale = data.bit(7);
return; return;
@ -238,26 +231,26 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
if(addr.bits(0,2) == 0x02) { if(addr.bits(0,2) == 0x02) {
//CTA //CTA
io.colorAddress.bits(0,7) = data.bits(0,7); cram.address.bits(0,7) = data.bits(0,7);
return; return;
} }
if(addr.bits(0,2) == 0x03) { if(addr.bits(0,2) == 0x03) {
//CTA //CTA
io.colorAddress.bit(8) = data.bit(0); cram.address.bit(8) = data.bit(0);
return; return;
} }
if(addr.bits(0,2) == 0x04) { if(addr.bits(0,2) == 0x04) {
//CTW //CTW
cram[io.colorAddress].bits(0,7) = data.bits(0,7); cram.write(cram.address, 0, data.bits(0,7));
return; return;
} }
if(addr.bits(0,2) == 0x05) { if(addr.bits(0,2) == 0x05) {
//CTW //CTW
cram[io.colorAddress].bit(8) = data.bit(0); cram.write(cram.address, 1, data.bit(0));
io.colorAddress++; cram.address++;
return; return;
} }
} }

29
higan/pce/vdc/memory.cpp Normal file
View File

@ -0,0 +1,29 @@
auto VDC::VRAM::read(uint16 addr) -> uint16 {
if(addr.bit(15)) return 0x0000; //todo: random data?
return data[addr];
}
auto VDC::VRAM::write(uint16 addr, uint16 value) -> void {
if(addr.bit(15)) return;
data[addr] = value;
}
auto VDC::SATB::read(uint8 addr) -> uint16 {
return data[addr];
}
auto VDC::SATB::write(uint8 addr, uint16 value) -> void {
data[addr] = value;
}
auto VDC::CRAM::read(uint9 addr) -> uint9 {
return data[addr];
}
auto VDC::CRAM::write(uint9 addr, bool a0, uint8 value) -> void {
if(!a0) {
data[addr].bits(0,7) = value.bits(0,7);
} else {
data[addr].bit(8) = value.bit(0);
}
}

View File

@ -2,19 +2,19 @@ auto VDC::Sprite::scanline(uint y) -> void {
y += 64; y += 64;
objects.reset(); objects.reset();
static const uint width[] = {15, 31}; static const uint widths[] = {15, 31};
static const uint height[] = {15, 31, 63, 63}; static const uint heights[] = {15, 31, 63, 63};
uint count = 0; uint count = 0;
for(uint index : range(64)) { for(uint index : range(64)) {
uint16 d0 = vdc.satb[index << 2 | 0]; uint16 d0 = vdc.satb.read(index << 2 | 0);
uint16 d1 = vdc.satb[index << 2 | 1]; uint16 d1 = vdc.satb.read(index << 2 | 1);
uint16 d2 = vdc.satb[index << 2 | 2]; uint16 d2 = vdc.satb.read(index << 2 | 2);
uint16 d3 = vdc.satb[index << 2 | 3]; uint16 d3 = vdc.satb.read(index << 2 | 3);
Object object; Object object;
object.y = d0.bits(0,9); object.y = d0.bits(0,9);
object.height = height[d3.bits(12,13)]; object.height = heights[d3.bits(12,13)];
if(y < object.y) continue; if(y < object.y) continue;
if(y > object.y + object.height) continue; if(y > object.y + object.height) continue;
@ -23,15 +23,29 @@ auto VDC::Sprite::scanline(uint y) -> void {
object.pattern = d2.bits(1,10); object.pattern = d2.bits(1,10);
object.palette = d3.bits(0,3); object.palette = d3.bits(0,3);
object.priority = d3.bit(7); object.priority = d3.bit(7);
object.width = width[d3.bit(8)]; object.width = widths[d3.bit(8)];
object.hflip = d3.bit(11); object.hflip = d3.bit(11);
object.vflip = d3.bit(15); object.vflip = d3.bit(15);
objects.append(object); object.first = index == 0;
count += 1 + d3.bit(8); //32-width sprites consume two slots if(object.width == 31) object.pattern.bit(0) = 0;
if(count >= 16) { if(object.height == 31) object.pattern.bit(1) = 0;
vdc.irq.raise(VDC::IRQ::Line::Overflow); if(object.height == 63) object.pattern.bits(1,2) = 0;
break;
if(object.width == 15) {
objects.append(object);
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
} else {
//32-width sprites count as two 16-width sprite slots
object.pattern ^= object.hflip;
object.width = 15;
objects.append(object);
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
object.x += 16;
object.pattern ^= 1;
objects.append(object);
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
} }
} }
} }
@ -43,10 +57,8 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
color = nothing; color = nothing;
if(!enable) return; if(!enable) return;
bool zero = 0; bool first = false;
uint index = 0;
for(auto& object : objects) { for(auto& object : objects) {
uint id = index++;
if(x < object.x) continue; if(x < object.x) continue;
if(x > object.x + object.width) continue; if(x > object.x + object.width) continue;
@ -55,21 +67,16 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
if(object.hflip) hoffset ^= object.width; if(object.hflip) hoffset ^= object.width;
if(object.vflip) voffset ^= object.height; if(object.vflip) voffset ^= object.height;
uint10 pattern = object.pattern; uint16 patternAddress = object.pattern;
if(object.width == 31) pattern.bit(0) = 0;
if(object.height == 31) pattern.bit(1) = 0;
if(object.height == 63) pattern.bits(1,2) = 0;
uint16 patternAddress = pattern;
patternAddress += (voffset >> 4) << 1; patternAddress += (voffset >> 4) << 1;
patternAddress += (hoffset >> 4); patternAddress += (hoffset >> 4);
patternAddress <<= 6; patternAddress <<= 6;
patternAddress += (voffset & 15); patternAddress += (voffset & 15);
uint16 d0 = vdc.vramRead(patternAddress + 0); uint16 d0 = vdc.vram.read(patternAddress + 0);
uint16 d1 = vdc.vramRead(patternAddress + 16); uint16 d1 = vdc.vram.read(patternAddress + 16);
uint16 d2 = vdc.vramRead(patternAddress + 32); uint16 d2 = vdc.vram.read(patternAddress + 32);
uint16 d3 = vdc.vramRead(patternAddress + 48); uint16 d3 = vdc.vram.read(patternAddress + 48);
uint4 index = 15 - (hoffset & 15); uint4 index = 15 - (hoffset & 15);
uint4 output; uint4 output;
@ -80,12 +87,12 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
if(output == 0) continue; if(output == 0) continue;
if(color) { if(color) {
if(zero) vdc.irq.raise(VDC::IRQ::Line::Collision); if(first) return vdc.irq.raise(VDC::IRQ::Line::Collision);
break; return;
} }
color = vdc.cram[1 << 8 | object.palette << 4 | output]; color = vdc.cram.read(1 << 8 | object.palette << 4 | output);
priority = object.priority; priority = object.priority;
if(id == 0) zero = 1; if(object.first) first = true;
} }
} }

View File

@ -3,6 +3,7 @@
namespace PCEngine { namespace PCEngine {
VDC vdc; VDC vdc;
#include "memory.cpp"
#include "io.cpp" #include "io.cpp"
#include "irq.cpp" #include "irq.cpp"
#include "dma.cpp" #include "dma.cpp"
@ -14,66 +15,108 @@ auto VDC::Enter() -> void {
} }
auto VDC::main() -> void { auto VDC::main() -> void {
//1365 cycles/scanline auto output = buffer + 1140 * vce.vclock;
state.x = 0;
uint y = state.y; //todo: if block breaks TV Sports Basketball
auto output = buffer + y * 512; //if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength) {
background.scanline(y); background.scanline(vce.voffset);
sprite.scanline(y); sprite.scanline(vce.voffset);
for(uint x : range(256)) { //}
if(y < 240) {
background.run(x, y); while(vce.hclock < 1140) {
sprite.run(x, y); uint9 color = 0;
if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength
&& vce.hclock >= vce.hstart && vce.hoffset < vce.hlength
) {
background.run(vce.hoffset, vce.voffset);
sprite.run(vce.hoffset, vce.voffset);
vce.hoffset++;
if(sprite.color && sprite.priority) { if(sprite.color && sprite.priority) {
*output++ = sprite.color(); color = sprite.color();
} else if(background.color) { } else if(background.color) {
*output++ = background.color(); color = background.color();
} else if(sprite.color) { } else if(sprite.color) {
*output++ = sprite.color(); color = sprite.color();
} else { } else {
*output++ = cram[0]; color = cram.read(0);
} }
} }
step(4);
}
step(341);
if(++state.y == 262) { if(vce.clock >= 2) *output++ = color;
state.y = 0; if(vce.clock >= 2) *output++ = color;
if(vce.clock >= 3) *output++ = color;
if(vce.clock >= 4) *output++ = color;
step(vce.clock);
} }
if(state.y == (io.lineCoincidence - 64)) irq.raise(IRQ::Line::LineCoincidence);
if(state.y == 240) { if(vce.vclock == io.lineCoincidence - (66 - vce.vstart)) {
irq.raise(IRQ::Line::LineCoincidence);
}
step(1365 - vce.hclock);
scanline();
}
auto VDC::scanline() -> void {
vce.hclock = 0;
vce.hoffset = 0;
vce.hstart = vce.horizontalDisplayStart * vce.clock;
vce.hlength = (vce.horizontalDisplayLength + 1) << 3;
if(vce.clock == 2) vce.hstart += 0;
if(vce.clock == 3) vce.hstart += 0;
if(vce.clock == 4) vce.hstart += 0;
vce.vclock++;
if(vce.vclock >= vce.vstart) vce.voffset++;
if(vce.vclock == 262) {
frame();
}
if(vce.vclock == vce.vstart + vce.vlength) {
irq.raise(IRQ::Line::Vblank); irq.raise(IRQ::Line::Vblank);
dma.satbStart(); dma.satbStart();
scheduler.exit(Scheduler::Event::Frame);
} }
} }
auto VDC::frame() -> void {
scheduler.exit(Scheduler::Event::Frame);
vce.vclock = 0;
vce.voffset = 0;
vce.vstart = vce.verticalDisplayStart - 2;
vce.vlength = min(242, vce.verticalDisplayLength + 1);
}
auto VDC::step(uint clocks) -> void { auto VDC::step(uint clocks) -> void {
state.x += clocks; vce.hclock += clocks;
Thread::step(clocks); Thread::step(clocks);
dma.step(clocks); dma.step(clocks);
synchronize(cpu); synchronize(cpu);
} }
auto VDC::refresh() -> void { auto VDC::refresh() -> void {
Emulator::video.refresh(buffer, 512 * sizeof(uint32), 256, 240); Emulator::video.refresh(buffer + 1140 * vce.vstart, 1140 * sizeof(uint32), 1140, 242);
} }
auto VDC::power() -> void { auto VDC::power() -> void {
create(VDC::Enter, system.colorburst() * 6.0); create(VDC::Enter, system.colorburst() * 6.0);
for(auto& pixel : buffer) pixel = 0; for(auto& pixel : buffer) pixel = 0;
for(auto& word : vram) word = 0x0000; memory::fill(&vram, sizeof(VRAM));
for(auto& word : satb) word = 0x0000; memory::fill(&satb, sizeof(SATB));
for(auto& word : cram) word = 0x0000; memory::fill(&cram, sizeof(CRAM));
memory::fill(&state, sizeof(State));
memory::fill(&irq, sizeof(IRQ)); memory::fill(&irq, sizeof(IRQ));
memory::fill(&dma, sizeof(DMA)); memory::fill(&dma, sizeof(DMA));
memory::fill(&vce, sizeof(VCE));
memory::fill(&io, sizeof(IO)); memory::fill(&io, sizeof(IO));
memory::fill(&background, sizeof(Background)); memory::fill(&background, sizeof(Background));
memory::fill(&sprite, sizeof(Sprite)); memory::fill(&sprite, sizeof(Sprite));
vce.clock = 4;
} }
} }

View File

@ -5,28 +5,54 @@ struct VDC : Thread {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto step(uint clocks) -> void; auto step(uint clocks) -> void;
auto scanline() -> void;
auto frame() -> void;
auto refresh() -> void; auto refresh() -> void;
auto power() -> void; auto power() -> void;
//io.cpp //io.cpp
auto vramRead(uint16 addr) -> uint16;
auto vramWrite(uint16 addr, uint16 data) -> void;
auto read(uint11 addr) -> uint8; auto read(uint11 addr) -> uint8;
auto write(uint11 addr, uint8 data) -> void; auto write(uint11 addr, uint8 data) -> void;
private: private:
uint32 buffer[512 * 484]; uint32 buffer[1140 * 512];
uint16 vram[0x8000]; struct VRAM {
uint16 satb[0x100]; //memory.cpp
uint9 cram[0x200]; auto read(uint16 addr) -> uint16;
auto write(uint16 addr, uint16 data) -> void;
struct State { uint16 addressRead;
uint x; uint16 addressWrite;
uint y; uint16 addressIncrement;
} state;
uint16 dataRead;
uint16 dataWrite;
private:
uint16 data[0x8000];
} vram;
struct SATB {
//memory.cpp
auto read(uint8 addr) -> uint16;
auto write(uint8 addr, uint16 data) -> void;
private:
uint16 data[0x100];
} satb;
struct CRAM {
//memory.cpp
auto read(uint9 addr) -> uint9;
auto write(uint9 addr, bool a0, uint8 data) -> void;
uint9 address;
private:
uint9 data[0x200];
} cram;
struct IRQ { struct IRQ {
enum class Line : uint { enum class Line : uint {
@ -79,6 +105,32 @@ private:
uint16 satbOffset; uint16 satbOffset;
} dma; } dma;
struct VCE {
uint5 horizontalSyncWidth;
uint7 horizontalDisplayStart;
uint7 horizontalDisplayLength;
uint7 horizontalDisplayEnd;
uint5 verticalSyncWidth;
uint8 verticalDisplayStart;
uint9 verticalDisplayLength;
uint8 verticalDisplayEnd;
uint clock;
uint hclock;
uint vclock;
uint hoffset;
uint voffset;
uint hstart;
uint vstart;
uint hlength;
uint vlength;
} vce;
struct Background { struct Background {
//background.cpp //background.cpp
auto scanline(uint y) -> void; auto scanline(uint y) -> void;
@ -87,6 +139,7 @@ private:
bool enable; bool enable;
uint10 hscroll; uint10 hscroll;
uint9 vscroll; uint9 vscroll;
uint9 vcounter;
uint8 width; uint8 width;
uint8 height; uint8 height;
@ -114,6 +167,7 @@ private:
bool hflip; bool hflip;
uint height; uint height;
bool vflip; bool vflip;
bool first;
}; };
array<Object, 64> objects; array<Object, 64> objects;
@ -124,60 +178,22 @@ private:
struct IO { struct IO {
uint5 address; uint5 address;
//VDC //$0005 CR (W)
//$00 MAWR (W)
uint16 vramAddressWrite;
//$01 MARR (W)
uint16 vramAddressRead;
//$02 VWR (W)
//$02 VRR (R)
uint16 vramDataWrite;
uint16 vramDataRead;
//$05 CR (W)
uint2 externalSync; uint2 externalSync;
uint2 displayOutput; uint2 displayOutput;
bool dramRefresh; bool dramRefresh;
uint vramAddressIncrement;
//$06 RCR //$0006 RCR
uint10 lineCoincidence; uint10 lineCoincidence;
//$09 MWR //$0009 MWR
uint2 vramAccess; uint2 vramAccess;
uint2 spriteAccess; uint2 spriteAccess;
bool cgMode; bool cgMode;
//$0a HSR //$0400 CR
uint5 horizontalSyncWidth;
uint7 horizontalDisplayStart;
//$0b HDR
uint7 horizontalDisplayWidth;
uint7 horizontalDisplayEnd;
//$0c VPR
uint5 verticalSyncWidth;
uint8 verticalDisplayStart;
//$0d VDR
uint9 verticalDisplayWidth;
//$0e VCR
uint8 verticalDisplayEnd;
//VCE
//$00 CR
uint2 divisionRatio;
bool colorBlur; bool colorBlur;
bool grayscale; bool grayscale;
//$02 CTA
uint9 colorAddress;
} io; } io;
}; };

View File

@ -237,8 +237,8 @@ auto Presentation::resizeViewport() -> void {
uint windowWidth = 0, windowHeight = 0; uint windowWidth = 0, windowHeight = 0;
bool aspectCorrection = true; bool aspectCorrection = true;
if(!fullScreen()) { if(!fullScreen()) {
windowWidth = 320 * scale; windowWidth = 326 * scale;
windowHeight = 240 * scale; windowHeight = 242 * scale;
aspectCorrection = settings["Video/AspectCorrection"].boolean(); aspectCorrection = settings["Video/AspectCorrection"].boolean();
} else { } else {
windowWidth = geometry().width(); windowWidth = geometry().width();

View File

@ -319,7 +319,7 @@ template<> struct stringify<Pair> {
do { do {
Pair quotient, remainder; Pair quotient, remainder;
div(source, 10, quotient, remainder); div(source, 10, quotient, remainder);
*p++ = '0' + remainder; *p++ = remainder + '0';
source = quotient; source = quotient;
} while(source); } while(source);
_size = p - _output; _size = p - _output;