mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
ae5968cfeb
commit
c40e9754bc
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "102";
|
||||
static const string Version = "102.01";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -9,6 +9,7 @@ Controller::Controller(uint port) : port(port) {
|
|||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
|
|
|
@ -9,6 +9,7 @@ Controller::Controller(uint port) : port(port) {
|
|||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
|
|
|
@ -9,6 +9,7 @@ Controller::Controller() {
|
|||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
|
|
|
@ -5,7 +5,7 @@ struct Controller : Thread {
|
|||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
virtual auto readData() -> uint4 { return 0; }
|
||||
virtual auto readData() -> uint4 { return 0x0f; }
|
||||
virtual auto writeData(uint2) -> void {}
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ auto CPU::power() -> void {
|
|||
|
||||
memory::fill(&irq, sizeof(IRQ));
|
||||
memory::fill(&timer, sizeof(Timer));
|
||||
memory::fill(&io, sizeof(IO));
|
||||
}
|
||||
|
||||
auto CPU::lastCycle() -> void {
|
||||
|
|
|
@ -55,6 +55,10 @@ struct CPU : Processor::HuC6280, Thread {
|
|||
friend class CPU;
|
||||
} timer;
|
||||
|
||||
struct IO {
|
||||
uint8 mdr;
|
||||
} io;
|
||||
|
||||
private:
|
||||
uint8 ram[0x2000];
|
||||
};
|
||||
|
|
|
@ -22,12 +22,12 @@ auto CPU::read(uint21 addr) -> uint8 {
|
|||
|
||||
//$0800-0bff PSG
|
||||
if((addr & 0x1c00) == 0x0800) {
|
||||
return 0x00;
|
||||
return io.mdr;
|
||||
}
|
||||
|
||||
//$0c00-0fff Timer
|
||||
if((addr & 0x1c00) == 0x0c00) {
|
||||
return timer.value;
|
||||
return (io.mdr & 0x80) | timer.value;
|
||||
}
|
||||
|
||||
//$1000-13ff I/O
|
||||
|
@ -43,11 +43,20 @@ auto CPU::read(uint21 addr) -> uint8 {
|
|||
|
||||
//$1400-17ff IRQ
|
||||
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) {
|
||||
return (
|
||||
irq.disableExternal << 0
|
||||
| irq.disableVDC << 1
|
||||
| irq.disableTimer << 2
|
||||
| (io.mdr & 0xf8)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -56,6 +65,7 @@ auto CPU::read(uint21 addr) -> uint8 {
|
|||
irq.pendingExternal << 0
|
||||
| irq.pendingVDC << 1
|
||||
| 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 {
|
||||
|
@ -99,11 +109,13 @@ auto CPU::write(uint21 addr, uint8 data) -> void {
|
|||
|
||||
//$0800-0bff PSG
|
||||
if((addr & 0x1c00) == 0x0800) {
|
||||
io.mdr = data;
|
||||
return;
|
||||
}
|
||||
|
||||
//$0c00-0fff Timer
|
||||
if((addr & 0x1c00) == 0x0c00) {
|
||||
io.mdr = data;
|
||||
if(!addr.bit(0)) {
|
||||
timer.latch = data.bits(0,6);
|
||||
} else {
|
||||
|
@ -115,12 +127,14 @@ auto CPU::write(uint21 addr, uint8 data) -> void {
|
|||
|
||||
//$1000-13ff I/O
|
||||
if((addr & 0x1c00) == 0x1000) {
|
||||
io.mdr = data;
|
||||
PCEngine::peripherals.controllerPort->writeData(data.bits(0,1));
|
||||
return;
|
||||
}
|
||||
|
||||
//$1400-17ff IRQ
|
||||
if((addr & 0x1c00) == 0x1400) {
|
||||
io.mdr = data;
|
||||
if(addr.bits(0,1) == 2) {
|
||||
irq.disableExternal = data.bit(0);
|
||||
irq.disableVDC = data.bit(1);
|
||||
|
|
|
@ -45,13 +45,13 @@ auto Interface::title() -> string {
|
|||
}
|
||||
|
||||
auto Interface::videoSize() -> VideoSize {
|
||||
return {256, 240};
|
||||
return {1140, 242};
|
||||
}
|
||||
|
||||
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
|
||||
auto a = arc ? 8.0 / 7.0 : 1.0;
|
||||
uint w = 256;
|
||||
uint h = 240;
|
||||
uint w = 285;
|
||||
uint h = 242;
|
||||
uint m = min(width / (w * a), height / h);
|
||||
return {uint(w * a * m), uint(h * m)};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
auto VDC::Background::scanline(uint y) -> void {
|
||||
hoffset = hscroll;
|
||||
if(y == 0) {
|
||||
voffset = vscroll;
|
||||
vcounter = vscroll;
|
||||
} else {
|
||||
voffset++;
|
||||
vcounter++;
|
||||
}
|
||||
hoffset = hscroll;
|
||||
voffset = vcounter;
|
||||
}
|
||||
|
||||
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 += (hoffset >> 3) & (width - 1);
|
||||
|
||||
uint16 tiledata = vdc.vramRead(batAddress);
|
||||
uint16 tiledata = vdc.vram.read(batAddress);
|
||||
uint16 patternAddress = tiledata.bits(0,11) << 4;
|
||||
patternAddress += (voffset & 7);
|
||||
uint4 palette = tiledata.bits(12,15);
|
||||
|
||||
uint16 d0 = vdc.vramRead(patternAddress + 0);
|
||||
uint16 d1 = vdc.vramRead(patternAddress + 8);
|
||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc.vram.read(patternAddress + 8);
|
||||
|
||||
uint3 index = 7 - (hoffset & 7);
|
||||
uint4 output;
|
||||
|
@ -30,6 +31,6 @@ auto VDC::Background::run(uint x, uint y) -> void {
|
|||
output.bit(2) = d1.bit(0 + 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++;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
auto VDC::DMA::step(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
if(vramActive) {
|
||||
uint16 data = vdc.vramRead(source);
|
||||
vdc.vramWrite(target, data);
|
||||
uint16 data = vdc.vram.read(source);
|
||||
vdc.vram.write(target, data);
|
||||
sourceIncrementMode == 0 ? source++ : source--;
|
||||
targetIncrementMode == 0 ? target++ : target--;
|
||||
if(!--length) {
|
||||
|
@ -12,8 +12,8 @@ auto VDC::DMA::step(uint clocks) -> void {
|
|||
}
|
||||
|
||||
if(satbActive) {
|
||||
uint16 data = vdc.vramRead(satbSource + satbOffset);
|
||||
vdc.satb[satbOffset] = data;
|
||||
uint16 data = vdc.vram.read(satbSource + satbOffset);
|
||||
vdc.satb.write(satbOffset, data);
|
||||
if(++satbOffset == 256) {
|
||||
satbActive = false;
|
||||
satbOffset = 0;
|
||||
|
|
|
@ -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 {
|
||||
bool a0 = addr.bit(0);
|
||||
if(!addr.bit(10)) {
|
||||
//VDC
|
||||
if(addr.bit(1) == 0) {
|
||||
//SR
|
||||
if(a0) return 0x00;
|
||||
|
@ -29,26 +18,25 @@ auto VDC::read(uint11 addr) -> uint8 {
|
|||
if(addr.bit(1) == 1) {
|
||||
if(io.address == 0x02) {
|
||||
//VRR
|
||||
uint8 data = io.vramDataRead.byte(a0);
|
||||
uint8 data = vram.dataRead.byte(a0);
|
||||
if(a0) {
|
||||
io.vramAddressRead += io.vramAddressIncrement;
|
||||
io.vramDataRead = vramRead(io.vramAddressRead);
|
||||
vram.addressRead += vram.addressIncrement;
|
||||
vram.dataRead = vram.read(vram.addressRead);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//VCE
|
||||
if(addr.bits(0,2) == 0x04) {
|
||||
//CTR
|
||||
uint8 data = cram[io.colorAddress].bits(0,7);
|
||||
uint8 data = cram.read(cram.address).bits(0,7);
|
||||
return data;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x05) {
|
||||
//CTR
|
||||
uint8 data = cram[io.colorAddress].bit(0);
|
||||
io.colorAddress++;
|
||||
uint8 data = 0xfe | cram.read(cram.address).bit(0);
|
||||
cram.address++;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +47,6 @@ auto VDC::read(uint11 addr) -> uint8 {
|
|||
auto VDC::write(uint11 addr, uint8 data) -> void {
|
||||
bool a0 = addr.bit(0);
|
||||
if(!addr.bit(10)) {
|
||||
//VDC
|
||||
if(addr.bit(1) == 0) {
|
||||
//AR
|
||||
if(a0) return;
|
||||
|
@ -70,23 +57,23 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
if(addr.bit(1) == 1) {
|
||||
if(io.address == 0x00) {
|
||||
//MAWR
|
||||
io.vramAddressWrite.byte(a0) = data;
|
||||
vram.addressWrite.byte(a0) = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(io.address == 0x01) {
|
||||
//MARR
|
||||
io.vramAddressRead.byte(a0) = data;
|
||||
io.vramDataRead = vramRead(io.vramAddressRead);
|
||||
vram.addressRead.byte(a0) = data;
|
||||
vram.dataRead = vram.read(vram.addressRead);
|
||||
return;
|
||||
}
|
||||
|
||||
if(io.address == 0x02) {
|
||||
//VWR
|
||||
io.vramDataWrite.byte(a0) = data;
|
||||
vram.dataWrite.byte(a0) = data;
|
||||
if(a0) {
|
||||
vramWrite(io.vramAddressWrite, io.vramDataWrite);
|
||||
io.vramAddressWrite += io.vramAddressIncrement;
|
||||
vram.write(vram.addressWrite, vram.dataWrite);
|
||||
vram.addressWrite += vram.addressIncrement;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -104,10 +91,10 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
} else {
|
||||
io.displayOutput = data.bits(0,1);
|
||||
io.dramRefresh = data.bit(2);
|
||||
if(data.bits(3,4) == 0) io.vramAddressIncrement = 0x01;
|
||||
if(data.bits(3,4) == 1) io.vramAddressIncrement = 0x20;
|
||||
if(data.bits(3,4) == 2) io.vramAddressIncrement = 0x40;
|
||||
if(data.bits(3,4) == 3) io.vramAddressIncrement = 0x80;
|
||||
if(data.bits(3,4) == 0) vram.addressIncrement = 0x01;
|
||||
if(data.bits(3,4) == 1) vram.addressIncrement = 0x20;
|
||||
if(data.bits(3,4) == 2) vram.addressIncrement = 0x40;
|
||||
if(data.bits(3,4) == 3) vram.addressIncrement = 0x80;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -127,7 +114,7 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
if(io.address == 0x08) {
|
||||
//BYR
|
||||
background.vscroll.byte(a0) = data;
|
||||
background.voffset = background.vscroll;
|
||||
background.vcounter = background.vscroll;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,9 +136,9 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
if(io.address == 0x0a) {
|
||||
//HSR
|
||||
if(!a0) {
|
||||
io.horizontalSyncWidth = data.bits(0,4);
|
||||
vce.horizontalSyncWidth = data.bits(0,4);
|
||||
} else {
|
||||
io.horizontalDisplayStart = data.bits(0,6);
|
||||
vce.horizontalDisplayStart = data.bits(0,6);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -159,9 +146,9 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
if(io.address == 0x0b) {
|
||||
//HDR
|
||||
if(!a0) {
|
||||
io.horizontalDisplayWidth = data.bits(0,6);
|
||||
vce.horizontalDisplayLength = data.bits(0,6);
|
||||
} else {
|
||||
io.horizontalDisplayEnd = data.bits(0,6);
|
||||
vce.horizontalDisplayEnd = data.bits(0,6);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -169,23 +156,27 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
if(io.address == 0x0c) {
|
||||
//VPR
|
||||
if(!a0) {
|
||||
io.verticalSyncWidth = data.bits(0,4);
|
||||
vce.verticalSyncWidth = data.bits(0,4);
|
||||
} else {
|
||||
io.verticalDisplayStart = data.bits(0,7);
|
||||
vce.verticalDisplayStart = data.bits(0,7);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(io.address == 0x0d) {
|
||||
//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;
|
||||
}
|
||||
|
||||
if(io.address == 0x0e) {
|
||||
//VCR
|
||||
if(a0) return;
|
||||
io.verticalDisplayEnd = data.bits(0,7);
|
||||
vce.verticalDisplayEnd = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -227,10 +218,12 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
//VCE
|
||||
if(addr.bits(0,2) == 0x00) {
|
||||
//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.grayscale = data.bit(7);
|
||||
return;
|
||||
|
@ -238,26 +231,26 @@ auto VDC::write(uint11 addr, uint8 data) -> void {
|
|||
|
||||
if(addr.bits(0,2) == 0x02) {
|
||||
//CTA
|
||||
io.colorAddress.bits(0,7) = data.bits(0,7);
|
||||
cram.address.bits(0,7) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x03) {
|
||||
//CTA
|
||||
io.colorAddress.bit(8) = data.bit(0);
|
||||
cram.address.bit(8) = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x04) {
|
||||
//CTW
|
||||
cram[io.colorAddress].bits(0,7) = data.bits(0,7);
|
||||
cram.write(cram.address, 0, data.bits(0,7));
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x05) {
|
||||
//CTW
|
||||
cram[io.colorAddress].bit(8) = data.bit(0);
|
||||
io.colorAddress++;
|
||||
cram.write(cram.address, 1, data.bit(0));
|
||||
cram.address++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -2,19 +2,19 @@ auto VDC::Sprite::scanline(uint y) -> void {
|
|||
y += 64;
|
||||
objects.reset();
|
||||
|
||||
static const uint width[] = {15, 31};
|
||||
static const uint height[] = {15, 31, 63, 63};
|
||||
static const uint widths[] = {15, 31};
|
||||
static const uint heights[] = {15, 31, 63, 63};
|
||||
|
||||
uint count = 0;
|
||||
for(uint index : range(64)) {
|
||||
uint16 d0 = vdc.satb[index << 2 | 0];
|
||||
uint16 d1 = vdc.satb[index << 2 | 1];
|
||||
uint16 d2 = vdc.satb[index << 2 | 2];
|
||||
uint16 d3 = vdc.satb[index << 2 | 3];
|
||||
uint16 d0 = vdc.satb.read(index << 2 | 0);
|
||||
uint16 d1 = vdc.satb.read(index << 2 | 1);
|
||||
uint16 d2 = vdc.satb.read(index << 2 | 2);
|
||||
uint16 d3 = vdc.satb.read(index << 2 | 3);
|
||||
|
||||
Object object;
|
||||
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 + object.height) continue;
|
||||
|
||||
|
@ -23,15 +23,29 @@ auto VDC::Sprite::scanline(uint y) -> void {
|
|||
object.pattern = d2.bits(1,10);
|
||||
object.palette = d3.bits(0,3);
|
||||
object.priority = d3.bit(7);
|
||||
object.width = width[d3.bit(8)];
|
||||
object.width = widths[d3.bit(8)];
|
||||
object.hflip = d3.bit(11);
|
||||
object.vflip = d3.bit(15);
|
||||
objects.append(object);
|
||||
object.first = index == 0;
|
||||
|
||||
count += 1 + d3.bit(8); //32-width sprites consume two slots
|
||||
if(count >= 16) {
|
||||
vdc.irq.raise(VDC::IRQ::Line::Overflow);
|
||||
break;
|
||||
if(object.width == 31) object.pattern.bit(0) = 0;
|
||||
if(object.height == 31) object.pattern.bit(1) = 0;
|
||||
if(object.height == 63) object.pattern.bits(1,2) = 0;
|
||||
|
||||
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;
|
||||
if(!enable) return;
|
||||
|
||||
bool zero = 0;
|
||||
uint index = 0;
|
||||
bool first = false;
|
||||
for(auto& object : objects) {
|
||||
uint id = index++;
|
||||
if(x < object.x) 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.vflip) voffset ^= object.height;
|
||||
|
||||
uint10 pattern = 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;
|
||||
uint16 patternAddress = object.pattern;
|
||||
patternAddress += (voffset >> 4) << 1;
|
||||
patternAddress += (hoffset >> 4);
|
||||
patternAddress <<= 6;
|
||||
patternAddress += (voffset & 15);
|
||||
|
||||
uint16 d0 = vdc.vramRead(patternAddress + 0);
|
||||
uint16 d1 = vdc.vramRead(patternAddress + 16);
|
||||
uint16 d2 = vdc.vramRead(patternAddress + 32);
|
||||
uint16 d3 = vdc.vramRead(patternAddress + 48);
|
||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc.vram.read(patternAddress + 16);
|
||||
uint16 d2 = vdc.vram.read(patternAddress + 32);
|
||||
uint16 d3 = vdc.vram.read(patternAddress + 48);
|
||||
|
||||
uint4 index = 15 - (hoffset & 15);
|
||||
uint4 output;
|
||||
|
@ -80,12 +87,12 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
|
|||
if(output == 0) continue;
|
||||
|
||||
if(color) {
|
||||
if(zero) vdc.irq.raise(VDC::IRQ::Line::Collision);
|
||||
break;
|
||||
if(first) return vdc.irq.raise(VDC::IRQ::Line::Collision);
|
||||
return;
|
||||
}
|
||||
|
||||
color = vdc.cram[1 << 8 | object.palette << 4 | output];
|
||||
color = vdc.cram.read(1 << 8 | object.palette << 4 | output);
|
||||
priority = object.priority;
|
||||
if(id == 0) zero = 1;
|
||||
if(object.first) first = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace PCEngine {
|
||||
|
||||
VDC vdc;
|
||||
#include "memory.cpp"
|
||||
#include "io.cpp"
|
||||
#include "irq.cpp"
|
||||
#include "dma.cpp"
|
||||
|
@ -14,66 +15,108 @@ auto VDC::Enter() -> void {
|
|||
}
|
||||
|
||||
auto VDC::main() -> void {
|
||||
//1365 cycles/scanline
|
||||
state.x = 0;
|
||||
uint y = state.y;
|
||||
auto output = buffer + y * 512;
|
||||
background.scanline(y);
|
||||
sprite.scanline(y);
|
||||
for(uint x : range(256)) {
|
||||
if(y < 240) {
|
||||
background.run(x, y);
|
||||
sprite.run(x, y);
|
||||
auto output = buffer + 1140 * vce.vclock;
|
||||
|
||||
//todo: if block breaks TV Sports Basketball
|
||||
//if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength) {
|
||||
background.scanline(vce.voffset);
|
||||
sprite.scanline(vce.voffset);
|
||||
//}
|
||||
|
||||
while(vce.hclock < 1140) {
|
||||
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) {
|
||||
*output++ = sprite.color();
|
||||
color = sprite.color();
|
||||
} else if(background.color) {
|
||||
*output++ = background.color();
|
||||
color = background.color();
|
||||
} else if(sprite.color) {
|
||||
*output++ = sprite.color();
|
||||
color = sprite.color();
|
||||
} else {
|
||||
*output++ = cram[0];
|
||||
color = cram.read(0);
|
||||
}
|
||||
}
|
||||
step(4);
|
||||
}
|
||||
step(341);
|
||||
|
||||
if(++state.y == 262) {
|
||||
state.y = 0;
|
||||
if(vce.clock >= 2) *output++ = color;
|
||||
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);
|
||||
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 {
|
||||
state.x += clocks;
|
||||
vce.hclock += clocks;
|
||||
Thread::step(clocks);
|
||||
dma.step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
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 {
|
||||
create(VDC::Enter, system.colorburst() * 6.0);
|
||||
|
||||
for(auto& pixel : buffer) pixel = 0;
|
||||
for(auto& word : vram) word = 0x0000;
|
||||
for(auto& word : satb) word = 0x0000;
|
||||
for(auto& word : cram) word = 0x0000;
|
||||
memory::fill(&state, sizeof(State));
|
||||
memory::fill(&vram, sizeof(VRAM));
|
||||
memory::fill(&satb, sizeof(SATB));
|
||||
memory::fill(&cram, sizeof(CRAM));
|
||||
memory::fill(&irq, sizeof(IRQ));
|
||||
memory::fill(&dma, sizeof(DMA));
|
||||
memory::fill(&vce, sizeof(VCE));
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&background, sizeof(Background));
|
||||
memory::fill(&sprite, sizeof(Sprite));
|
||||
|
||||
vce.clock = 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,28 +5,54 @@ struct VDC : Thread {
|
|||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto vramRead(uint16 addr) -> uint16;
|
||||
auto vramWrite(uint16 addr, uint16 data) -> void;
|
||||
|
||||
auto read(uint11 addr) -> uint8;
|
||||
auto write(uint11 addr, uint8 data) -> void;
|
||||
|
||||
private:
|
||||
uint32 buffer[512 * 484];
|
||||
uint32 buffer[1140 * 512];
|
||||
|
||||
uint16 vram[0x8000];
|
||||
uint16 satb[0x100];
|
||||
uint9 cram[0x200];
|
||||
struct VRAM {
|
||||
//memory.cpp
|
||||
auto read(uint16 addr) -> uint16;
|
||||
auto write(uint16 addr, uint16 data) -> void;
|
||||
|
||||
struct State {
|
||||
uint x;
|
||||
uint y;
|
||||
} state;
|
||||
uint16 addressRead;
|
||||
uint16 addressWrite;
|
||||
uint16 addressIncrement;
|
||||
|
||||
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 {
|
||||
enum class Line : uint {
|
||||
|
@ -79,6 +105,32 @@ private:
|
|||
uint16 satbOffset;
|
||||
} 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 {
|
||||
//background.cpp
|
||||
auto scanline(uint y) -> void;
|
||||
|
@ -87,6 +139,7 @@ private:
|
|||
bool enable;
|
||||
uint10 hscroll;
|
||||
uint9 vscroll;
|
||||
uint9 vcounter;
|
||||
uint8 width;
|
||||
uint8 height;
|
||||
|
||||
|
@ -114,6 +167,7 @@ private:
|
|||
bool hflip;
|
||||
uint height;
|
||||
bool vflip;
|
||||
bool first;
|
||||
};
|
||||
array<Object, 64> objects;
|
||||
|
||||
|
@ -124,60 +178,22 @@ private:
|
|||
struct IO {
|
||||
uint5 address;
|
||||
|
||||
//VDC
|
||||
|
||||
//$00 MAWR (W)
|
||||
uint16 vramAddressWrite;
|
||||
|
||||
//$01 MARR (W)
|
||||
uint16 vramAddressRead;
|
||||
|
||||
//$02 VWR (W)
|
||||
//$02 VRR (R)
|
||||
uint16 vramDataWrite;
|
||||
uint16 vramDataRead;
|
||||
|
||||
//$05 CR (W)
|
||||
//$0005 CR (W)
|
||||
uint2 externalSync;
|
||||
uint2 displayOutput;
|
||||
bool dramRefresh;
|
||||
uint vramAddressIncrement;
|
||||
|
||||
//$06 RCR
|
||||
//$0006 RCR
|
||||
uint10 lineCoincidence;
|
||||
|
||||
//$09 MWR
|
||||
//$0009 MWR
|
||||
uint2 vramAccess;
|
||||
uint2 spriteAccess;
|
||||
bool cgMode;
|
||||
|
||||
//$0a HSR
|
||||
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;
|
||||
//$0400 CR
|
||||
bool colorBlur;
|
||||
bool grayscale;
|
||||
|
||||
//$02 CTA
|
||||
uint9 colorAddress;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
|
|
@ -237,8 +237,8 @@ auto Presentation::resizeViewport() -> void {
|
|||
uint windowWidth = 0, windowHeight = 0;
|
||||
bool aspectCorrection = true;
|
||||
if(!fullScreen()) {
|
||||
windowWidth = 320 * scale;
|
||||
windowHeight = 240 * scale;
|
||||
windowWidth = 326 * scale;
|
||||
windowHeight = 242 * scale;
|
||||
aspectCorrection = settings["Video/AspectCorrection"].boolean();
|
||||
} else {
|
||||
windowWidth = geometry().width();
|
||||
|
|
|
@ -319,7 +319,7 @@ template<> struct stringify<Pair> {
|
|||
do {
|
||||
Pair quotient, remainder;
|
||||
div(source, 10, quotient, remainder);
|
||||
*p++ = '0' + remainder;
|
||||
*p++ = remainder + '0';
|
||||
source = quotient;
|
||||
} while(source);
|
||||
_size = p - _output;
|
||||
|
|
Loading…
Reference in New Issue