GEN: Preliminary sprite rendering. hook up MemoryDomains.

This commit is contained in:
beirich 2011-10-16 06:23:15 +00:00
parent a95957dcf6
commit f8564bf8a7
7 changed files with 229 additions and 79 deletions

View File

@ -8,6 +8,7 @@ namespace BizHawk.Emulation.Consoles.Sega
public Func<int, short> DmaReadFrom68000; // TODO make ushort
public int DmaLength { get { return Registers[19] | (Registers[20] << 8); } }
public int DmaMode { get { return (Registers[23] >> 6) & 3; } }
public int DmaSource
{
@ -21,28 +22,26 @@ namespace BizHawk.Emulation.Consoles.Sega
}
}
bool DmaFillModePending;
void ExecuteDmaFill(ushort data)
{
Console.WriteLine("DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
Log.Note("VDP","DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
// TODO: Is the address really VdpDataAddr and not DMA source? I guess that makes sense.
// TODO: It should spread this out, not do it all at once.
// TODO: DMA can go to places besides just VRAM (eg CRAM, VSRAM)
// TODO: Does DMA fill really use the actual increment register value?
// TODO: DMA can go to places besides just VRAM (eg CRAM, VSRAM) ??? can it?
// TODO: what is this genvdp.txt comment about accurate vdp fill emulation writing some other byte first?
int length = DmaLength;
if (length == 0)
length = 0x10000; // Really necessary?
length = 0x10000;
byte fillByte = (byte)(data >> 8);
do
{
VRAM[VdpDataAddr & 0xFFFF] = fillByte;
UpdatePatternBuffer(VdpDataAddr & 0xFFFF);
VRAM[VdpDataAddr] = fillByte;
UpdatePatternBuffer(VdpDataAddr);
VdpDataAddr += Registers[15];
} while (--length > 0);
@ -51,7 +50,7 @@ namespace BizHawk.Emulation.Consoles.Sega
void Execute68000VramCopy()
{
Console.WriteLine("DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
Log.Note("VDP", "DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
int length = DmaLength;
if (length == 0)

View File

@ -8,15 +8,14 @@ namespace BizHawk.Emulation.Consoles.Sega
{
if (ScanLine == 0)
{
for (int i = 0; i < FrameBuffer.Length; i++)
FrameBuffer[i] = 0;
Array.Clear(FrameBuffer, 0, FrameBuffer.Length);
//RenderPatterns();
RenderPalette();
RenderScrollA();
RenderScrollB();
RenderSprites();
}
RenderSprites();
}
void RenderPalette()
@ -76,33 +75,68 @@ namespace BizHawk.Emulation.Consoles.Sega
}
}
static readonly int[] SpriteSizeTable = { 8, 16, 24, 32 };
Sprite sprite;
void RenderSprites()
{
Sprite sprite = FetchSprite(0);
/*if (sprite.X > 0)
Console.WriteLine("doot");*/
int scanLineBase = ScanLine * FrameWidth;
int processedSprites = 0;
FetchSprite(0);
while (true)
{
if (sprite.Y > ScanLine || sprite.Y+sprite.HeightPixels <= ScanLine)
goto nextSprite;
if (sprite.X + sprite.WidthPixels <= 0)
goto nextSprite;
if (sprite.HeightCells == 2)
sprite.HeightCells = 2;
int yline = ScanLine - sprite.Y;
int paletteBase = sprite.Palette * 16;
int pattern = sprite.PatternIndex + ((yline / 8));
int x = sprite.X;
for (int xi = 0; xi < sprite.WidthPixels; xi++)
{
if (sprite.X + xi < 0 || sprite.X + xi > FrameWidth)
continue;
int pixel = PatternBuffer[((pattern+((xi/8)*sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (xi & 7)];
if (pixel != 0)
FrameBuffer[scanLineBase + sprite.X + xi] = Palette[paletteBase + pixel];
}
nextSprite:
if (sprite.Link == 0)
break;
if (++processedSprites > 80)
break;
FetchSprite(sprite.Link);
}
}
Sprite FetchSprite(int spriteNo)
void FetchSprite(int spriteNo)
{
int satbase = SpriteAttributeTableAddr + (spriteNo*8);
Sprite sprite = new Sprite();
sprite.Y = (VRAM[satbase + 1] | (VRAM[satbase + 0] << 8) & 0x3FF) - 128;
sprite.X = (VRAM[satbase + 7] | (VRAM[satbase + 6] << 8) & 0x3FF) - 128;
sprite.Width = ((VRAM[satbase + 2] >> 2) & 3) + 1;
sprite.Height = (VRAM[satbase + 2] & 3) + 1;
sprite.Link = VRAM[satbase + 3] & 0x7F;
sprite.PatternIndex = VRAM[satbase + 5] | (VRAM[satbase + 6] << 8) & 0x7FF;
sprite.Y = (VRAM[satbase + 0] | (VRAM[satbase + 1] << 8) & 0x3FF) - 128;
sprite.X = (VRAM[satbase + 6] | (VRAM[satbase + 7] << 8) & 0x3FF) - 128;
sprite.WidthPixels = SpriteSizeTable[(VRAM[satbase + 3] >> 2) & 3];
sprite.HeightPixels = SpriteSizeTable[VRAM[satbase + 3] & 3];
sprite.WidthCells = ((VRAM[satbase + 3] >> 2) & 3) + 1;
sprite.HeightCells = (VRAM[satbase + 3] & 3) + 1;
sprite.Link = VRAM[satbase + 2] & 0x7F;
sprite.PatternIndex = (VRAM[satbase + 4] | (VRAM[satbase + 5] << 8)) & 0x7FF;
sprite.Palette = (VRAM[satbase + 5] >> 5) & 3;
return sprite;
}
struct Sprite
{
public int X, Y;
public int Width, Height;
public int WidthPixels, HeightPixels;
public int WidthCells, HeightCells;
public int Link;
public int Palette;
public int PatternIndex;

View File

@ -24,23 +24,23 @@ namespace BizHawk.Emulation.Consoles.Sega
public bool CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } }
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
private ushort NameTableAddrA;
private ushort NameTableAddrB;
private ushort NameTableAddrWindow;
private ushort SpriteAttributeTableAddr;
private ushort HScrollTableAddr;
private byte NameTableWidth;
private byte NameTableHeight;
ushort NameTableAddrA;
ushort NameTableAddrB;
ushort NameTableAddrWindow;
ushort SpriteAttributeTableAddr;
ushort HScrollTableAddr;
byte NameTableWidth;
byte NameTableHeight;
private bool ControlWordPending;
private ushort VdpDataAddr;
private byte VdpDataCode;
bool ControlWordPending;
ushort VdpDataAddr;
byte VdpDataCode;
private static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
public void WriteVdpControl(ushort data)
{
//Console.WriteLine("[PC = {0:X6}] VDP: Control Write {1:X4}", /*Genesis._MainCPU.PC*/0, data);
Log.Note("VDP", "VDP: Control Write {0:X4}", data);
if (ControlWordPending == false)
{
@ -77,28 +77,28 @@ namespace BizHawk.Emulation.Consoles.Sega
// what type of DMA?
switch (Registers[23] >> 6)
{
case 2:
Console.WriteLine("VRAM FILL");
case 2:
Log.Note("VDP", "VRAM FILL");
DmaFillModePending = true;
break;
case 3:
Console.WriteLine("VRAM COPY **** UNIMPLEMENTED ***");
case 3:
Log.Note("VDP", "VRAM COPY **** UNIMPLEMENTED ***");
break;
default:
Console.WriteLine("68k->VRAM COPY **** UNIMPLEMENTED ***");
Log.Note("VDP", "68k->VRAM COPY");
Execute68000VramCopy();
break;
}
Console.WriteLine("DMA LEN = "+DmaLength);
Log.Note("VDP", "DMA LEN = " + DmaLength);
}
}
}
public ushort ReadVdpControl()
{
//Console.WriteLine("VDP: Control Read");
ushort value = 0x3400; // fixed bits per genvdp.txt TODO test on everdrive, I guess.
value |= 0x0200; // Fifo empty
Log.Note("VDP", "VDP: Control Read {0:X4}", value);
return value;
}
@ -144,40 +144,67 @@ namespace BizHawk.Emulation.Consoles.Sega
public ushort ReadVdpData()
{
//Console.WriteLine("VDP: Data Read");
Console.WriteLine("VDP: Data Read");
return 0;
}
public void WriteVdpRegister(int register, byte data)
{
//Console.WriteLine("Register {0}: {1:X2}", register, data);
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
switch (register)
{
case 0x00:
case 0x00: // Mode Set Register 1
Registers[register] = data;
Console.WriteLine("HINT enabled: "+ HInterruptsEnabled);
//Log.Note("VDP", "HINT enabled: " + HInterruptsEnabled);
break;
case 0x01:
case 0x01: // Mode Set Register 2
Registers[register] = data;
Console.WriteLine("DmaEnabled: "+DmaEnabled);
Console.WriteLine("VINT enabled: " + VInterruptEnabled);
//Log.Note("VDP", "DisplayEnabled: " + DisplayEnabled);
//Log.Note("VDP", "DmaEnabled: " + DmaEnabled);
//Log.Note("VDP", "VINT enabled: " + VInterruptEnabled);
break;
case 0x02: // Name Table Address for Layer A
NameTableAddrA = (ushort) ((data & 0x38) << 10);
Console.WriteLine("SET NTa A = {0:X4}",NameTableAddrA);
//Log.Note("VDP", "SET NTa A = {0:X4}", NameTableAddrA);
break;
case 0x03: // Name Table Address for Window
NameTableAddrWindow = (ushort) ((data & 0x3E) << 10);
Console.WriteLine("SET NTa W = {0:X4}", NameTableAddrWindow);
//Log.Note("VDP", "SET NTa W = {0:X4}", NameTableAddrWindow);
break;
case 0x04: // Name Table Address for Layer B
NameTableAddrB = (ushort) (data << 13);
Console.WriteLine("SET NTa B = {0:X4}", NameTableAddrB);
//Log.Note("VDP", "SET NTa B = {0:X4}", NameTableAddrB);
break;
case 0x05: // Sprite Attribute Table Address
SpriteAttributeTableAddr = (ushort) (data << 9);
Console.WriteLine("SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
//Log.Note("VDP", "SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
break;
case 0x0A: // H Interrupt Register
//Log.Note("VDP", "HInt occurs every {0} lines.", data);
break;
case 0x0B: // VScroll/HScroll modes
/*if ((data & 4) != 0)
Log.Note("VDP", "VSCroll Every 2 Cells Enabled");
else
Log.Note("VDP", "Full Screen VScroll");*/
int hscrollmode = data & 3;
switch (hscrollmode)
{
//case 0: Log.Note("VDP", "Full Screen HScroll"); break;
//case 1: Log.Note("VDP", "Prohibited HSCROLL mode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); break;
//case 2: Log.Note("VDP", "HScroll every 1 cell"); break;
//case 3: Log.Note("VDP", "HScroll every 2 cell"); break;
}
break;
case 0x0C: // Mode Set #4
// TODO interlaced modes
if ((data & 0x81) == 0)
@ -187,7 +214,7 @@ namespace BizHawk.Emulation.Consoles.Sega
{
FrameBuffer = new int[256*224];
FrameWidth = 256;
Console.WriteLine("SWITCH TO 32 CELL WIDE MODE");
//Log.Note("VDP", "SWITCH TO 32 CELL WIDE MODE");
}
} else {
// Display is 40 cells wide
@ -195,18 +222,21 @@ namespace BizHawk.Emulation.Consoles.Sega
{
FrameBuffer = new int[320*224];
FrameWidth = 320;
Console.WriteLine("SWITCH TO 40 CELL WIDE MODE");
//Log.Note("VDP", "SWITCH TO 40 CELL WIDE MODE");
}
}
break;
case 0x0D: // H Scroll Table Address
HScrollTableAddr = (ushort) (data << 10);
Console.WriteLine("SET HScrollTab attr = {0:X4}", HScrollTableAddr);
//Log.Note("VDP", "SET HScrollTab attr = {0:X4}", HScrollTableAddr);
break;
case 0x0F:
Console.WriteLine("Set Data Increment to "+data);
case 0x0F: // Auto Address Register Increment
//Log.Note("VDP", "Set Data Increment to " + data);
break;
case 0x10:
case 0x10: // Nametable Dimensions
switch (data & 0x03)
{
case 0: NameTableWidth = 32; break;
@ -221,13 +251,49 @@ namespace BizHawk.Emulation.Consoles.Sega
case 2: NameTableHeight = 32; break; // invalid setting
case 3: NameTableHeight = 128; break;
}
Console.WriteLine("Name Table Dimensions set to {0}x{1}", NameTableWidth, NameTableHeight);
//Log.Note("VDP", "Name Table Dimensions set to {0}x{1}", NameTableWidth, NameTableHeight);
break;
case 0x11: // Window H Position
int whp = data & 31;
bool fromright = (data & 0x80) != 0;
//Log.Note("VDP", "Window H is {0} units from {1}", whp, fromright ? "right" : "left");
break;
case 0x12: // Window V
whp = data & 31;
fromright = (data & 0x80) != 0;
//Log.Note("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
break;
case 0x13: // DMA Length Low
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x14: // DMA Length High
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x15: // DMA Source Low
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x16: // DMA Source Mid
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x17: // DMA Source High
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
}
Registers[register] = data;
}
private void ProcessPalette(int slot)
void ProcessPalette(int slot)
{
byte r = PalXlatTable[(CRAM[slot] & 0x000F) >> 0];
byte g = PalXlatTable[(CRAM[slot] & 0x00F0) >> 4];
@ -235,7 +301,7 @@ namespace BizHawk.Emulation.Consoles.Sega
Palette[slot] = Colors.ARGB(r, g, b);
}
private void UpdatePatternBuffer(int addr)
void UpdatePatternBuffer(int addr)
{
PatternBuffer[(addr*2) + 1] = (byte) (VRAM[addr^1] & 0x0F);
PatternBuffer[(addr*2) + 0] = (byte) (VRAM[addr^1] >> 4);

View File

@ -82,6 +82,7 @@ namespace BizHawk.Emulation.Consoles.Sega
for (int i = 0; i < rom.Length; i++)
RomData[i] = rom[i];
SetupMemoryDomains();
MainCPU.Reset();
}
@ -91,7 +92,7 @@ namespace BizHawk.Emulation.Consoles.Sega
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++)
{
Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
if (VDP.ScanLine < 224)
VDP.RenderLine();
@ -179,8 +180,30 @@ namespace BizHawk.Emulation.Consoles.Sega
return new byte[0];
}
public IList<MemoryDomain> MemoryDomains { get { throw new NotImplementedException(); } }
public MemoryDomain MainMemory { get { throw new NotImplementedException(); } }
IList<MemoryDomain> memoryDomains;
void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>(3);
var MainMemoryDomain = new MemoryDomain("68000 RAM", Ram.Length, Endian.Big,
addr => Ram[addr & 0xFFFF],
(addr, value) => Ram[addr & 0xFFFF] = value);
var Z80Domain = new MemoryDomain("Z80 RAM", Z80Ram.Length, Endian.Little,
addr => Z80Ram[addr & 0x1FFF],
(addr, value) => { Z80Ram[addr & 0x1FFF] = value; });
var VRamDomain = new MemoryDomain("Video RAM", VDP.VRAM.Length, Endian.Big,
addr => VDP.VRAM[addr & 0xFFFF],
(addr, value) => VDP.VRAM[addr & 0xFFFF] = value);
domains.Add(MainMemoryDomain);
domains.Add(Z80Domain);
domains.Add(VRamDomain);
memoryDomains = domains.AsReadOnly();
}
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
public void Dispose() { }
}

View File

@ -54,7 +54,7 @@
case 0x04: return IOPorts[0].Control;
case 0x05: return IOPorts[1].Control;
case 0x06: return 0xFF;// IOPorts[2].Control; TODO hack? returning FF fixes revenge of shinobi and strider. investigate later.
case 0x06: return 0xFF;// return IOPorts[2].Control; //TODO hack? returning FF fixes revenge of shinobi and strider. investigate later.
case 0x07: return IOPorts[0].TxData;
case 0x08: return IOPorts[0].RxData;

View File

@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (address == 0xA11100) // Z80 BUS status
{
Console.WriteLine("QUERY z80 bus status. 68000 can access? " + (M68000HasZ80Bus && Z80Reset == false));
//Console.WriteLine("QUERY z80 bus status. 68000 can access? " + (M68000HasZ80Bus && Z80Reset == false));
return (sbyte) (M68000HasZ80Bus && Z80Reset == false ? 0 : 1);
}
@ -69,13 +69,17 @@ namespace BizHawk.Emulation.Consoles.Sega
int maskedAddr;
if (address < 0x400000) // Cartridge ROM
return (RomData[address] << 24) | (RomData[address + 1] << 16) | (RomData[address + 2] << 8) | RomData[address + 3];
if (address >= 0xE00000) // Work RAM
{
maskedAddr = address & 0xFFFF;
return (Ram[maskedAddr] << 24) | (Ram[maskedAddr + 1] << 16) | (Ram[maskedAddr + 2] << 8) | Ram[maskedAddr + 3];
}
// try to handle certain things separate if they need to be separate? otherwise handle as 2x readwords?
{
return ((ushort)ReadWord(address) | (ushort)(ReadWord(address + 2) << 16));
}
if (address == 0xA10008) return 0; // FIXME HACK for tg-sync.
Console.WriteLine("UNHANDLED READL {0:X6}", address);
@ -105,7 +109,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (address == 0xA11100)
{
M68000HasZ80Bus = (value & 1) != 0;
Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
//Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
return;
}
if (address == 0xA11200) // Z80 RESET
@ -113,7 +117,7 @@ namespace BizHawk.Emulation.Consoles.Sega
Z80Reset = (value & 1) == 0;
if (Z80Reset)
SoundCPU.Reset();
Console.WriteLine("z80 reset: " + Z80Reset);
//Console.WriteLine("z80 reset: " + Z80Reset);
return;
}
if (address >= 0xC00000)
@ -170,7 +174,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (address == 0xA11100) // Z80 BUSREQ
{
M68000HasZ80Bus = (value & 0x100) != 0;
Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
//Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
return;
}
if (address == 0xA11200) // Z80 RESET
@ -178,7 +182,7 @@ namespace BizHawk.Emulation.Consoles.Sega
Z80Reset = (value & 0x100) == 0;
if (Z80Reset)
SoundCPU.Reset();
Console.WriteLine("z80 reset: " + Z80Reset);
//Console.WriteLine("z80 reset: " + Z80Reset);
return;
}
Console.WriteLine("UNHANDLED WRITEW {0:X6}:{1:X4}", address, value);

View File

@ -16,13 +16,15 @@ namespace BizHawk.Emulation.Consoles.Sega
if (address >= 0x4000 && address < 0x6000)
{
//Console.WriteLine(" === Z80 READS FM STATUS ===");
return YM2612.ReadStatus();
return YM2612.ReadStatus(); // TODO: more than 1 read port probably?
}
if (address >= 0x8000)
{
// 68000 Bank region
return (byte) ReadByte(BankRegion | (address & 0x7FFF));
}
if (address <= 0x6100) // read from bank address register - returns FF
return 0xFF;
Console.WriteLine("UNHANDLED Z80 READ {0:X4}",address);
return 0xCD;
}
@ -37,18 +39,40 @@ namespace BizHawk.Emulation.Consoles.Sega
}
if (address >= 0x4000 && address < 0x6000)
{
//Console.WriteLine(" === Z80 WRITES Z80 {0:X4}:{1:X2} ===",address, value);
//Console.WriteLine(" === Z80 WRITES YM2612 {0:X4}:{1:X2} ===",address, value);
YM2612.Write(address & 3, value);
return;
}
if (address == 0x6000)
if (address < 0x6100)
{
BankRegion >>= 1;
BankRegion |= (value & 1) << 23;
BankRegion &= 0x00FF8000;
Console.WriteLine("Bank pointing at {0:X8}",BankRegion);
//Console.WriteLine("Bank pointing at {0:X8}",BankRegion);
return;
}
if (address >= 0x7F00 && address < 0x7F20)
{
switch (address & 0x1F)
{
case 0x00:
case 0x02:
VDP.WriteVdpData((ushort) ((value<<8) | value));
return;
case 0x04:
case 0x06:
VDP.WriteVdpControl((ushort)((value << 8) | value));
return;
case 0x11:
case 0x13:
case 0x15:
case 0x17:
PSG.WritePsgData(value, SoundCPU.TotalExecutedCycles);
return;
}
}
if (address >= 0x8000)
{
WriteByte(BankRegion | (address & 0x7FFF), (sbyte) value);