mirror of https://github.com/bsnes-emu/bsnes.git
342 lines
6.8 KiB
C++
342 lines
6.8 KiB
C++
//todo: does data mirroring occur for all VDP addresses; or just data/control ports?
|
|
|
|
auto VDP::readByte(uint24 addr) -> uint8 {
|
|
auto data = readWord(addr & ~1);
|
|
return data << 8 | data << 0;
|
|
}
|
|
|
|
auto VDP::writeByte(uint24 addr, uint8 data) -> void {
|
|
return writeWord(addr & ~1, data << 8 | data << 0);
|
|
}
|
|
|
|
//
|
|
|
|
auto VDP::readWord(uint24 addr) -> uint16 {
|
|
switch(addr & 0xc0001f) {
|
|
|
|
//data port
|
|
case 0xc00000: case 0xc00002: {
|
|
return readDataPort();
|
|
}
|
|
|
|
//control port
|
|
case 0xc00004: case 0xc00006: {
|
|
return readControlPort();
|
|
}
|
|
|
|
}
|
|
|
|
return 0x0000;
|
|
}
|
|
|
|
auto VDP::writeWord(uint24 addr, uint16 data) -> void {
|
|
//print("[VDP] ", hex(addr, 6L), "=", hex(data, 4L), "\n");
|
|
|
|
switch(addr & 0xc0001f) {
|
|
|
|
//data port
|
|
case 0xc00000: case 0xc00002: {
|
|
return writeDataPort(data);
|
|
}
|
|
|
|
//control port
|
|
case 0xc00004: case 0xc00006: {
|
|
return writeControlPort(data);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
auto VDP::readDataPort() -> uint16 {
|
|
io.commandPending = false;
|
|
|
|
//VRAM read
|
|
if(io.command.bits(0,3) == 0) {
|
|
return 0x0000;
|
|
}
|
|
|
|
//VSRAM read
|
|
if(io.command.bits(0,3) == 4) {
|
|
return 0x0000;
|
|
}
|
|
|
|
//CRAM read
|
|
if(io.command.bits(0,3) == 8) {
|
|
return 0x0000;
|
|
}
|
|
}
|
|
|
|
auto VDP::writeDataPort(uint16 data) -> void {
|
|
io.commandPending = false;
|
|
|
|
//DMA VRAM fill
|
|
if(io.command.bits(4,5) == 2) {
|
|
io.dmaActive = true;
|
|
io.dmaFillWord = data;
|
|
return;
|
|
}
|
|
|
|
//VRAM write
|
|
if(io.command.bits(0,3) == 1) {
|
|
auto address = io.address.bits(1,15);
|
|
if(io.address.bit(0)) data = data >> 8 | data << 8;
|
|
vram[address] = data;
|
|
io.address += io.dataIncrement;
|
|
return;
|
|
}
|
|
|
|
//VSRAM write
|
|
if(io.command.bits(0,3) == 5) {
|
|
auto address = io.address.bits(1,6);
|
|
if(address >= 40) return;
|
|
//data format: ---- --yy yyyy yyyy
|
|
vsram[address] = data.bits(0,9);
|
|
io.address += io.dataIncrement;
|
|
return;
|
|
}
|
|
|
|
//CRAM write
|
|
if(io.command.bits(0,3) == 3) {
|
|
auto address = io.address.bits(1,6);
|
|
//data format: ---- bbb- ggg- rrr-
|
|
cram[address] = data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6;
|
|
io.address += io.dataIncrement;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
auto VDP::readControlPort() -> uint16 {
|
|
io.commandPending = false;
|
|
|
|
uint16 result = 0b0011'0100'0000'0000;
|
|
result |= io.dmaActive << 1;
|
|
return result;
|
|
}
|
|
|
|
auto VDP::writeControlPort(uint16 data) -> void {
|
|
//print("[VDPC] ", hex(data, 4L), "\n");
|
|
|
|
//command write (lo)
|
|
if(io.commandPending) {
|
|
io.commandPending = false;
|
|
|
|
io.command.bits(2,5) = data.bits(4,7);
|
|
io.address.bits(14,15) = data.bits(0,1);
|
|
|
|
return;
|
|
}
|
|
|
|
//command write (hi)
|
|
if(data.bits(14,15) != 2) {
|
|
io.commandPending = true;
|
|
|
|
io.command.bits(0,1) = data.bits(14,15);
|
|
io.address.bits(0,13) = data.bits(0,13);
|
|
return;
|
|
}
|
|
|
|
//register write (d13 is ignored)
|
|
if(data.bits(14,15) == 2)
|
|
switch(data.bits(8,12)) {
|
|
|
|
//mode register 1
|
|
case 0x00: {
|
|
io.displayOverlayEnable = data.bit(0);
|
|
io.counterLatch = data.bit(1);
|
|
io.horizontalInterruptEnable = data.bit(4);
|
|
io.leftColumnBlank = data.bit(5);
|
|
return;
|
|
}
|
|
|
|
//mode register 2
|
|
case 0x01: {
|
|
io.videoMode = data.bit(2);
|
|
io.overscan = data.bit(3);
|
|
io.dmaEnable = data.bit(4);
|
|
io.verticalBlankInterruptEnable = data.bit(5);
|
|
io.displayEnable = data.bit(6);
|
|
io.externalVRAM = data.bit(7);
|
|
|
|
if(!io.dmaEnable) io.command.bit(5) = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
//plane A name table location
|
|
case 0x02: {
|
|
planeA.io.nametableAddress = data.bits(3,6) << 12;
|
|
return;
|
|
}
|
|
|
|
//window name table location
|
|
case 0x03: {
|
|
window.io.nametableAddress = data.bits(1,6) << 10;
|
|
return;
|
|
}
|
|
|
|
//plane B name table location
|
|
case 0x04: {
|
|
planeB.io.nametableAddress = data.bits(0,3) << 12;
|
|
return;
|
|
}
|
|
|
|
//sprite attribute table location
|
|
case 0x05: {
|
|
io.attrtableSprite = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//sprite pattern base address
|
|
case 0x06: {
|
|
io.nametableBaseSprite = data.bit(5);
|
|
return;
|
|
}
|
|
|
|
//background color
|
|
case 0x07: {
|
|
io.backgroundColor = data.bits(0,5);
|
|
return;
|
|
}
|
|
|
|
//horizontal interrupt counter
|
|
case 0x0a: {
|
|
io.horizontalInterruptCounter = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//mode register 3
|
|
case 0x0b: {
|
|
io.horizontalScrollMode = data.bits(0,1);
|
|
io.verticalScrollMode = data.bit(2);
|
|
io.externalInterruptEnable = data.bit(3);
|
|
return;
|
|
}
|
|
|
|
//mode register 4
|
|
case 0x0c: {
|
|
io.tileWidth = data.bit(0) | data.bit(7) << 1;
|
|
io.interlaceMode = data.bits(1,2);
|
|
io.shadowHighlightEnable = data.bit(3);
|
|
io.externalColorEnable = data.bit(4);
|
|
io.horizontalSync = data.bit(5);
|
|
io.verticalSync = data.bit(6);
|
|
return;
|
|
}
|
|
|
|
//horizontal scroll data location
|
|
case 0x0d: {
|
|
io.horizontalScrollTable = data.bits(1,6);
|
|
return;
|
|
}
|
|
|
|
//nametable pattern base address
|
|
case 0x0e: {
|
|
io.nametableBasePatternA = data.bit(0);
|
|
io.nametableBasePatternB = data.bit(1);
|
|
return;
|
|
}
|
|
|
|
//data port auto-increment value
|
|
case 0x0f: {
|
|
io.dataIncrement = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//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)];
|
|
|
|
return;
|
|
}
|
|
|
|
//window plane horizontal position
|
|
case 0x11: {
|
|
if(!data) {
|
|
//disable
|
|
io.windowHorizontalLo = ~0;
|
|
io.windowHorizontalHi = ~0;
|
|
} else if(data.bit(7) == 0) {
|
|
//left
|
|
io.windowHorizontalLo = 0;
|
|
io.windowHorizontalHi = (data.bits(0,4) << 4) - 1;
|
|
} else {
|
|
//right
|
|
io.windowHorizontalLo = (data.bits(0,4) << 4) - 1;
|
|
io.windowHorizontalHi = ~0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//window plane vertical position
|
|
case 0x12: {
|
|
if(!data) {
|
|
//disable
|
|
io.windowVerticalLo = ~0;
|
|
io.windowVerticalHi = ~0;
|
|
} else if(data.bit(7) == 0) {
|
|
//up
|
|
io.windowVerticalLo = 0;
|
|
io.windowVerticalHi = (data.bits(0,4) << 3) - 1;
|
|
} else {
|
|
//down
|
|
io.windowVerticalLo = (data.bits(0,4) << 3) - 1;
|
|
io.windowVerticalHi = ~0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//DMA length
|
|
case 0x13: {
|
|
io.dmaLength.bits(0,7) = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//DMA length
|
|
case 0x14: {
|
|
io.dmaLength.bits(8,15) = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//DMA source
|
|
case 0x15: {
|
|
io.dmaSource.bits(0,7) = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//DMA source
|
|
case 0x16: {
|
|
io.dmaSource.bits(8,15) = data.bits(0,7);
|
|
return;
|
|
}
|
|
|
|
//DMA source
|
|
case 0x17: {
|
|
io.dmaSource.bits(16,21) = data.bits(0,5);
|
|
io.dmaMode = data.bits(6,7);
|
|
return;
|
|
}
|
|
|
|
//unused
|
|
default: {
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|