mirror of https://github.com/bsnes-emu/bsnes.git
Update to v101r11 release.
byuu says: Changelog: - 68K: fixed NEG/NEGX operand order - 68K: fixed bug in disassembler that was breaking trace logging - VDP: improved sprite rendering (still 100% broken) - VDP: added horizontal/vertical scrolling (90% broken) Forgot: - 68K: fix extension word sign bit on indexed modes for disassembler as well - 68K: emulate STOP properly (use r.stop flag; clear on IRQs firing) I'm really wearing out fast here. The Genesis documentation is somehow even worse than Game Boy documentation, but this is a far more complex system. It's a massive time sink to sit here banging away at every possible combination of how things could work, only to see no positive improvements. Nothing I do seems to get sprites to do a goddamn thing. squee says the sprite Y field is 10-bits, X field is 9-bits. genvdp says they're both 10-bits. BlastEm treats them like they're both 10-bits, then masks off the upper bit so it's effectively 9-bits anyway. Nothing ever bothers to tell you whether the horizontal scroll values are supposed to add or subtract from the current X position. Probably the most basic detail you could imagine for explaining horizontal scrolling and yet ... nope. Nothing. I can't even begin to understand how the VDP FIFO functionality works, or what the fuck is meant by "slots". I'm completely at a loss as how how in the holy hell the 68K works with 8-bit accesses. I don't know whether I need byte/word handlers for every device, or if I can just hook it right into the 68K core itself. This one's probably the most major design detail. I need to know this before I go and implement the PSG/YM2612/IO ports-\>gamepads/Z80/etc. Trying to debug the 68K is murder because basically every game likes to start with a 20,000,000-instruction reset phase of checksumming entire games, and clearing out the memory as agonizingly slowly as humanly possible. And like the ARM, there's too many registers so I'd need three widescreen monitors to comfortably view the entire debugger output lines onscreen. I can't get any test ROMs to debug functionality outside of full games because every **goddamned** test ROM coder thinks it's acceptable to tell people to go fetch some toolchain from a link that died in the late '90s and only works on MS-DOS 6.22 to build their fucking shit, because god forbid you include a 32KiB assembled ROM image in your fucking archives. ... I may have to take a break for a while. We'll see.
This commit is contained in:
parent
0b70a01b47
commit
f7ddbfc462
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "101.10";
|
||||
static const string Version = "101.11";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -95,8 +95,8 @@ auto CPU::readWord(uint24 addr) -> uint16 {
|
|||
if(addr < 0xa00000) return 0x0000;
|
||||
if(addr < 0xc00000) return rand(), 0;
|
||||
if(addr < 0xe00000) return vdp.readWord(addr);
|
||||
uint16 data = ram[addr + 0 & 65535] << 8;
|
||||
return data | ram[addr + 1 & 65535] << 0;
|
||||
uint16 data = ram[addr + 0 & 0xffff] << 8;
|
||||
return data | ram[addr + 1 & 0xffff] << 0;
|
||||
}
|
||||
|
||||
auto CPU::writeByte(uint24 addr, uint8 data) -> void {
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
auto VDP::Background::scanline(uint y) -> void {
|
||||
uint15 address = io.horizontalScrollAddress;
|
||||
|
||||
static const uint mask[] = {0u, 7u, ~7u, ~0u};
|
||||
address += (y & mask[io.horizontalScrollMode]) << 1;
|
||||
address += (this == &vdp.planeB);
|
||||
|
||||
state.horizontalScroll = vdp.vram[address].bits(0,9);
|
||||
}
|
||||
|
||||
auto VDP::Background::run(uint x, uint y) -> void {
|
||||
output.priority = 0;
|
||||
output.color = 0;
|
||||
|
||||
static const uint tiles[] = {32, 64, 0, 128};
|
||||
y -= vdp.vsram[(x >> 4) & (io.verticalScrollMode ? ~0u : 0u)];
|
||||
y &= (tiles[io.nametableHeight] << 3) - 1;
|
||||
x -= state.horizontalScroll;
|
||||
x &= (tiles[io.nametableWidth] << 3) - 1;
|
||||
|
||||
uint tileX = x >> 3;
|
||||
uint tileY = y >> 3;
|
||||
uint15 nametableAddress = io.nametableAddress;
|
||||
nametableAddress += (tileY << io.nametableWidth) + tileX;
|
||||
nametableAddress += tileY * tiles[io.nametableWidth] + tileX;
|
||||
|
||||
uint16 tileAttributes = vdp.vram[nametableAddress];
|
||||
uint15 tileAddress = tileAttributes.bits(0,10) << 4;
|
||||
|
@ -29,4 +42,5 @@ auto VDP::Background::power() -> void {
|
|||
|
||||
auto VDP::Background::reset() -> void {
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&state, sizeof(State));
|
||||
}
|
||||
|
|
|
@ -217,8 +217,12 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
|
||||
//mode register 3
|
||||
case 0x0b: {
|
||||
io.horizontalScrollMode = data.bits(0,1);
|
||||
io.verticalScrollMode = data.bit(2);
|
||||
planeA.io.horizontalScrollMode = data.bits(0,1);
|
||||
window.io.horizontalScrollMode = data.bits(0,1);
|
||||
planeB.io.horizontalScrollMode = data.bits(0,1);
|
||||
planeA.io.verticalScrollMode = data.bit(2);
|
||||
window.io.verticalScrollMode = data.bit(2);
|
||||
planeB.io.verticalScrollMode = data.bit(2);
|
||||
io.externalInterruptEnable = data.bit(3);
|
||||
return;
|
||||
}
|
||||
|
@ -236,7 +240,9 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
|
||||
//horizontal scroll data location
|
||||
case 0x0d: {
|
||||
io.horizontalScrollTable = data.bits(1,6);
|
||||
planeA.io.horizontalScrollAddress = data.bits(0,6) << 9;
|
||||
window.io.horizontalScrollAddress = data.bits(0,6) << 9;
|
||||
planeB.io.horizontalScrollAddress = data.bits(0,6) << 9;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -255,20 +261,12 @@ auto VDP::writeControlPort(uint16 data) -> void {
|
|||
|
||||
//plane size
|
||||
case 0x10: {
|
||||
//0 = 32 tiles
|
||||
//1 = 64 tiles
|
||||
//2 = invalid (repeats first row for every scanline)
|
||||
//3 = 128 tiles
|
||||
static const uint shift[] = {5, 6, 0, 7};
|
||||
|
||||
planeA.io.nametableWidth = shift[data.bits(0,1)];
|
||||
window.io.nametableWidth = shift[data.bits(0,1)];
|
||||
planeB.io.nametableWidth = shift[data.bits(0,1)];
|
||||
|
||||
planeA.io.nametableHeight = shift[data.bits(4,5)];
|
||||
window.io.nametableHeight = shift[data.bits(4,5)];
|
||||
planeB.io.nametableHeight = shift[data.bits(4,5)];
|
||||
|
||||
planeA.io.nametableWidth = data.bits(0,1);
|
||||
window.io.nametableWidth = data.bits(0,1);
|
||||
planeB.io.nametableWidth = data.bits(0,1);
|
||||
planeA.io.nametableHeight = data.bits(4,5);
|
||||
window.io.nametableHeight = data.bits(4,5);
|
||||
planeB.io.nametableHeight = data.bits(4,5);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ auto VDP::scanline() -> void {
|
|||
state.x = 0;
|
||||
if(++state.y >= 262) state.y = 0;
|
||||
|
||||
if(state.y < 240) {
|
||||
if(state.y < screenHeight()) {
|
||||
planeA.scanline(state.y);
|
||||
window.scanline(state.y);
|
||||
planeB.scanline(state.y);
|
||||
|
@ -16,6 +16,7 @@ auto VDP::scanline() -> void {
|
|||
|
||||
auto VDP::run() -> void {
|
||||
if(!io.displayEnable) return outputPixel(0);
|
||||
if(state.y >= screenHeight()) return outputPixel(0);
|
||||
|
||||
bool windowed = false; //todo: broken
|
||||
windowed &= state.x >= io.windowHorizontalLo && state.x <= io.windowHorizontalHi;
|
||||
|
|
|
@ -5,7 +5,7 @@ auto VDP::Sprite::write(uint9 address, uint16 data) -> void {
|
|||
switch(address.bits(0,1)) {
|
||||
|
||||
case 0: {
|
||||
object.y = data.bits(0,9) - 128;
|
||||
object.y = data.bits(0,8);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ auto VDP::Sprite::write(uint9 address, uint16 data) -> void {
|
|||
}
|
||||
|
||||
case 3: {
|
||||
object.x = data.bits(0,9) - 128;
|
||||
object.x = data.bits(0,8);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -34,34 +34,31 @@ auto VDP::Sprite::write(uint9 address, uint16 data) -> void {
|
|||
}
|
||||
|
||||
auto VDP::Sprite::scanline(uint y) -> void {
|
||||
object.reset();
|
||||
objects.reset();
|
||||
|
||||
uint7 link = 0;
|
||||
while(link) {
|
||||
auto& o = oam[link];
|
||||
do {
|
||||
auto& object = oam[link];
|
||||
link = object.link;
|
||||
|
||||
if((uint9)(o.y + o.height - 1) < y) continue;
|
||||
if((uint9)(y + o.height - 1) < o.y) continue;
|
||||
if(o.x == 0) break;
|
||||
if(128 + y < object.y) continue;
|
||||
if(128 + y >= object.y + object.height - 1) continue;
|
||||
if(object.x == 0) break;
|
||||
|
||||
object.append(o);
|
||||
if(object.size() >= 20) break;
|
||||
|
||||
link = o.link;
|
||||
if(!link || link >= 80) break;
|
||||
}
|
||||
objects.append(object);
|
||||
} while(link && link < 80 && objects.size() < 20);
|
||||
}
|
||||
|
||||
auto VDP::Sprite::run(uint x, uint y) -> void {
|
||||
output.priority = 0;
|
||||
output.color = 0;
|
||||
|
||||
for(auto& o : object) {
|
||||
if((uint9)(o.x + o.width - 1) < x) continue;
|
||||
if((uint9)(y + o.width - 1) < o.x) continue;
|
||||
for(auto& o : objects) {
|
||||
if(128 + x < o.x) continue;
|
||||
if(128 + x >= o.x + o.width - 1) continue;
|
||||
|
||||
auto objectX = (uint9)(x - o.x);
|
||||
auto objectY = (uint9)(y - o.y);
|
||||
uint objectX = 128 + x - o.x;
|
||||
uint objectY = 128 + y - o.y;
|
||||
if(o.horizontalFlip) objectX = (o.width - 1) - objectX;
|
||||
if(o.verticalFlip) objectY = (o.height - 1) - objectY;
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ auto VDP::reset() -> void {
|
|||
create(VDP::Enter, system.colorburst() * 15.0 / 2.0);
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&state, sizeof(State));
|
||||
|
||||
planeA.reset();
|
||||
window.reset();
|
||||
|
|
|
@ -43,10 +43,17 @@ struct VDP : Thread {
|
|||
|
||||
struct IO {
|
||||
uint15 nametableAddress;
|
||||
uint3 nametableWidth; //1 << value
|
||||
uint3 nametableHeight; //1 << value
|
||||
uint2 nametableWidth;
|
||||
uint2 nametableHeight;
|
||||
uint15 horizontalScrollAddress;
|
||||
uint2 horizontalScrollMode;
|
||||
uint1 verticalScrollMode;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
uint10 horizontalScroll;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
uint6 color;
|
||||
boolean priority;
|
||||
|
@ -71,8 +78,8 @@ struct VDP : Thread {
|
|||
} io;
|
||||
|
||||
struct Object {
|
||||
uint10 x;
|
||||
uint10 y;
|
||||
uint9 x;
|
||||
uint9 y;
|
||||
uint width;
|
||||
uint height;
|
||||
bool horizontalFlip;
|
||||
|
@ -89,11 +96,14 @@ struct VDP : Thread {
|
|||
} output;
|
||||
|
||||
array<Object, 80> oam;
|
||||
array<Object, 20> object;
|
||||
array<Object, 20> objects;
|
||||
};
|
||||
Sprite sprite;
|
||||
|
||||
private:
|
||||
auto screenWidth() const -> uint { return io.tileWidth ? 320 : 256; }
|
||||
auto screenHeight() const -> uint { return io.overscan ? 240 : 224; }
|
||||
|
||||
uint16 vram[32768];
|
||||
uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware
|
||||
uint9 cram[64];
|
||||
|
@ -130,8 +140,6 @@ private:
|
|||
uint8 horizontalInterruptCounter;
|
||||
|
||||
//$0b mode register 3
|
||||
uint2 horizontalScrollMode;
|
||||
uint1 verticalScrollMode;
|
||||
uint1 externalInterruptEnable;
|
||||
|
||||
//$0c mode register 4
|
||||
|
@ -142,9 +150,6 @@ private:
|
|||
uint1 horizontalSync;
|
||||
uint1 verticalSync;
|
||||
|
||||
//$0d horizontal scroll data location
|
||||
uint7 horizontalScrollTable;
|
||||
|
||||
//$0e nametable pattern base address
|
||||
uint1 nametableBasePatternA;
|
||||
uint1 nametableBasePatternB;
|
||||
|
|
|
@ -22,7 +22,7 @@ auto M68K::_readDisplacement(uint32 base) -> uint32 {
|
|||
}
|
||||
|
||||
auto M68K::_readIndex(uint32 base) -> uint32 {
|
||||
auto extension = readPC<Word>();
|
||||
auto extension = _readPC<Word>();
|
||||
auto index = extension & 0x8000
|
||||
? read(AddressRegister{extension >> 12})
|
||||
: read(DataRegister{extension >> 12});
|
||||
|
|
|
@ -779,14 +779,12 @@ auto M68K::instructionNBCD(EffectiveAddress with) -> void {
|
|||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionNEG(EffectiveAddress with) -> void {
|
||||
auto source = read<Size, Hold>(with);
|
||||
auto result = SUB<Size>(0, source);
|
||||
auto result = SUB<Size>(read<Size, Hold>(with), 0);
|
||||
write<Size>(with, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionNEGX(EffectiveAddress with) -> void {
|
||||
auto source = read<Size, Hold>(with);
|
||||
auto result = SUB<Size, Extend>(0, source);
|
||||
auto result = SUB<Size, Extend>(read<Size, Hold>(with), 0);
|
||||
write<Size>(with, result);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue