Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

m32x vdp fixes #1820

Merged
merged 1 commit into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ares/md/m32x/bus-external.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
}

if(address >= 0x840000 && address <= 0x87ffff) {
if(vdp.framebufferAccess) return data;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k read while FEN==1"); return data; } // wait instead?
return vdp.bbram[address >> 1 & 0xffff];
}

Expand Down Expand Up @@ -41,6 +43,7 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {

if(address >= 0x840000 && address <= 0x85ffff) {
if(vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k write while FEN==1"); return; } // wait instead?
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
shm.debugger.tracer.instruction->invalidate(0x0400'0000 | address & 0x1fffe);
shs.debugger.tracer.instruction->invalidate(0x0400'0000 | address & 0x1fffe);
Expand All @@ -51,6 +54,7 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {

if(address >= 0x860000 && address <= 0x87ffff) {
if(vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k overwrite while FEN==1"); return; } // wait instead?
shm.debugger.tracer.instruction->invalidate(0x0402'0000 | address & 0x1fffe);
shs.debugger.tracer.instruction->invalidate(0x0402'0000 | address & 0x1fffe);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
Expand Down
49 changes: 24 additions & 25 deletions ares/md/m32x/bus-internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
return readInternalIO(upper, lower, address, data);
}

if(address >= 0x0200'0000 && address <= 0x023f'ffff) {
if(address >= 0x0200'0000 && address <= 0x03ff'ffff) {
while(dreq.vram) {
// SH2 ROM accesses stall while RV is set
if(shm.active()) { shm.internalStep(1); shm.syncM68k(true); }
if(shs.active()) { shs.internalStep(1); shs.syncM68k(true); }
if(shs.active()) { shs.internalStep(1); shs.syncM68k(true); }
}

// TODO: SH2 ROM accesses need to stall while the m68k is on the bus
if(shm.active()) shm.internalStep(6); if(shs.active()) shs.internalStep(6);
return cartridge.child->read(upper, lower, address, data);
}

if(address >= 0x0400'0000 && address <= 0x0405'ffff) {
if(address >= 0x0400'0000 && address <= 0x05ff'ffff) {
if (!vdp.framebufferAccess) return data;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 read while FEN==1"); return data; } // wait instead?
if(shm.active()) shm.internalStep(5); if(shs.active()) shs.internalStep(5);
return vdp.bbram[address >> 1 & 0xffff];
}
Expand All @@ -38,30 +40,27 @@ auto M32X::writeInternal(n1 upper, n1 lower, n32 address, n16 data) -> void {
return writeInternalIO(upper, lower, address, data);
}

if(address >= 0x0400'0000 && address <= 0x0401'ffff) {
if (!vdp.framebufferAccess) return;
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x0400'0000 && address <= 0x05ff'ffff) {
address &= 0x0403'ffff;

if(address >= 0x0402'0000 && address <= 0x0403'ffff) {
if (!vdp.framebufferAccess) return;
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x0400'0000 && address <= 0x0401'ffff) {
if (!vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 write while FEN==1"); return; } // wait instead?
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}

if(address >= 0x0404'0000 && address <= 0x0405'ffff) {
if (!vdp.framebufferAccess) return;
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
if(address >= 0x0402'0000 && address <= 0x0403'ffff) {
if (!vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 overwrite while FEN==1"); return; } // wait instead?
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
}

if(address >= 0x0600'0000 && address <= 0x0603'ffff) {
Expand Down
8 changes: 6 additions & 2 deletions ares/md/m32x/io-external.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,18 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
//frame buffer control
if(address == 0xa1518a) {
data.bit( 0) = vdp.framebufferActive;
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
data.bit( 1) = MegaDrive::vdp.refreshing()
|| vdp.framebufferEngaged(); // FEN: frame buffer engaged
data.bit( 2, 12) = 0;
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
data.bit(13) = !vdp.paletteEngaged(); // PEN: can access palette
data.bit(14) = vdp.hblank;
data.bit(15) = vdp.vblank;
}

//palette
if(address >= 0xa15200 && address <= 0xa153ff) {
if (vdp.framebufferAccess) return data;
if(vdp.paletteEngaged()) { debug(unusual, "[32X CRAM] 68k read while PEN==0"); return data; } // wait instead?
data = vdp.cram[address >> 1 & 0xff];
}

Expand Down Expand Up @@ -358,6 +361,7 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
//palette
if(address >= 0xa15200 && address <= 0xa153ff) {
if (vdp.framebufferAccess) return;
if(vdp.paletteEngaged()) { debug(unusual, "[32X CRAM] 68k write while PEN==0"); return; } // wait instead?
if(upper) vdp.cram[address >> 1 & 0xff].byte(1) = data.byte(1);
if(lower) vdp.cram[address >> 1 & 0xff].byte(0) = data.byte(0);
}
Expand Down
8 changes: 6 additions & 2 deletions ares/md/m32x/io-internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,17 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
if(shm.active()) shm.syncM68k();
if(shs.active()) shs.syncM68k();
data.bit( 0) = vdp.framebufferActive;
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
data.bit( 1) = MegaDrive::vdp.refreshing()
|| vdp.framebufferEngaged(); // FEN: frame buffer engaged
data.bit(13) = !vdp.paletteEngaged(); // PEN: can access palette
data.bit(14) = vdp.hblank;
data.bit(15) = vdp.vblank;
}

//palette
if(address >= 0x4200 && address <= 0x43ff) {
if (!vdp.framebufferAccess) return data;
if(vdp.paletteEngaged()) { debug(unusual, "[32X CRAM] SH2 read while PEN==0"); return data; } // wait instead?
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
data = vdp.cram[address >> 1 & 0xff];
}
Expand Down Expand Up @@ -340,6 +343,7 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
//palette
if(address >= 0x4200 && address <= 0x43ff) {
if (!vdp.framebufferAccess) return;
if(vdp.paletteEngaged()) { debug(unusual, "[32X CRAM] SH2 write while PEN==0"); return; } // wait instead?
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.cram[address >> 1 & 0xff].byte(1) = data.byte(1);
if(lower) vdp.cram[address >> 1 & 0xff].byte(0) = data.byte(0);
Expand Down
7 changes: 7 additions & 0 deletions ares/md/m32x/m32x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ auto M32X::vblank(bool line) -> void {
}

auto M32X::hblank(bool line) -> void {
if(vdp.hblank > line) {
// TODO: VDP regs should be latched 192 MClks (~82 cycles) before end of hblank (according to official docs)
vdp.latch.mode = vdp.mode;
vdp.latch.lines = vdp.lines;
vdp.latch.priority = vdp.priority;
vdp.latch.dotshift = vdp.dotshift;
}
vdp.hblank = line;
shm.irq.hint.active = 0;
shs.irq.hint.active = 0;
Expand Down
10 changes: 10 additions & 0 deletions ares/md/m32x/m32x.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ struct M32X {
auto plot(u32* output, u16 color) -> void;
auto fill() -> void;
auto selectFramebuffer(n1 active) -> void;
auto framebufferEngaged() -> bool;
auto paletteEngaged() -> bool;

//serialization.cpp
auto serialize(serializer&) -> void;
Expand All @@ -117,8 +119,16 @@ struct M32X {
n1 framebufferAccess;
n1 framebufferActive;
n1 framebufferSelect;
int framebufferWait;
n1 hblank;
n1 vblank;

struct Latch {
n2 mode;
n1 lines;
n1 priority;
n1 dotshift;
} latch;
};

struct PWM : Thread {
Expand Down
5 changes: 5 additions & 0 deletions ares/md/m32x/serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ auto M32X::VDP::serialize(serializer& s) -> void {
s(lines);
s(priority);
s(dotshift);
s(latch.mode);
s(latch.lines);
s(latch.priority);
s(latch.dotshift);
s(autofillLength);
s(autofillAddress);
s(autofillData);
s(framebufferAccess);
s(framebufferActive);
s(framebufferSelect);
s(framebufferWait);
s(hblank);
s(vblank);
selectFramebuffer(framebufferSelect);
Expand Down
2 changes: 2 additions & 0 deletions ares/md/m32x/sh7604.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ auto M32X::SH7604::step(u32 clocks) -> void {
cyclesUntilSh2Sync -= clocks;
cyclesUntilM68kSync -= clocks;

m32x.vdp.framebufferWait -= min(clocks, m32x.vdp.framebufferWait);

if(cyclesUntilSh2Sync <= 0) {
cyclesUntilSh2Sync = minCyclesBetweenSh2Syncs;
if (m32x.shm.active()) Thread::synchronize(m32x.shs);
Expand Down
29 changes: 22 additions & 7 deletions ares/md/m32x/vdp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,30 @@ auto M32X::VDP::power(bool reset) -> void {
lines = 0;
priority = 0;
dotshift = 0;
latch = {};
autofillLength = 0;
autofillAddress = 0;
autofillData = 0;
framebufferAccess = 0;
framebufferActive = 0;
framebufferSelect = 0;
framebufferWait = 0;
hblank = 0;
vblank = 1;
selectFramebuffer(framebufferSelect);
}

auto M32X::VDP::scanline(u32 pixels[1280], u32 y) -> void {
if(!Mega32X() || !pixels || y >= (lines ? 240 : 224)) return;
if(mode == 1) return scanlineMode1(pixels, y);
if(mode == 2) return scanlineMode2(pixels, y);
if(mode == 3) return scanlineMode3(pixels, y);
if(!Mega32X() || !pixels || y >= (latch.lines ? 240 : 224)) return;
if(latch.mode == 1) return scanlineMode1(pixels, y);
if(latch.mode == 2) return scanlineMode2(pixels, y);
if(latch.mode == 3) return scanlineMode3(pixels, y);
}

auto M32X::VDP::scanlineMode1(u32 pixels[1280], u32 y) -> void {
u16 address = fbram[y];
for(u32 x : range(320)) {
u8 color = fbram[address + (x + dotshift >> 1) & 0xffff].byte(!(x + dotshift & 1));
u8 color = fbram[address + (x + latch.dotshift >> 1) & 0xffff].byte(!(x + latch.dotshift & 1));
plot(&pixels[x * 4], cram[color]);
}
}
Expand Down Expand Up @@ -70,7 +72,7 @@ auto M32X::VDP::plot(u32* output, u16 color) -> void {
n1 throughbit = color >> 15;
b1 opaque = color & 0x7fff;

if(priority == 0) {
if(latch.priority == 0) {
//Mega Drive has priority
if(throughbit || backdrop) {
output[0] = color | 1 << 15;
Expand All @@ -90,6 +92,8 @@ auto M32X::VDP::plot(u32* output, u16 color) -> void {
}

auto M32X::VDP::fill() -> void {
if(framebufferWait > 0) { debug(unusual, "[32X FILL] triggered before last fill finished"); return; }
framebufferWait = 7+3*(autofillLength+1); // according to official docs
for(u32 repeat : range(1 + autofillLength)) {
bbram[autofillAddress] = autofillData;
autofillAddress.byte(0)++;
Expand All @@ -98,9 +102,20 @@ auto M32X::VDP::fill() -> void {

auto M32X::VDP::selectFramebuffer(n1 select) -> void {
framebufferSelect = select;
if(!vblank && mode) return;
if(!vblank && latch.mode) return;

framebufferActive = select;
fbram = {dram.data() + 0x10000 * (select == 0), 0x10000};
bbram = {dram.data() + 0x10000 * (select == 1), 0x10000};
}

// back buffer access
auto M32X::VDP::framebufferEngaged() -> bool {
// TODO: 40 cycle wait at start of hblank (according to official docs)
return (latch.mode != 0 && framebufferSelect != framebufferActive) || framebufferWait > 0;
}

auto M32X::VDP::paletteEngaged() -> bool {
// TODO: not available for 24 MClks (~10 cycles) at start of hblank (according to official docs)
return !vblank && !hblank && latch.mode.bit(0);
}
2 changes: 1 addition & 1 deletion ares/md/system/serialization.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v141.1";
static const string SerializerVersion = "v142";

auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
Expand Down
Loading