394 lines
10 KiB
C#
394 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Globalization;
|
|
using BizHawk.Emulation.Consoles.Nintendo;
|
|
using System.Diagnostics;
|
|
|
|
namespace BizHawk.MultiClient
|
|
{
|
|
public partial class NESPPU : Form
|
|
{
|
|
//TODO:
|
|
//Pattern viewer -
|
|
// Row interleaving
|
|
// option for 2x view (and 4x?)
|
|
// Mouse over - Usage (BG vs Sprite usage)
|
|
//Sprite viewer
|
|
//Nametable viewer
|
|
|
|
int defaultWidth; //For saving the default size of the dialog, so the user can restore if desired
|
|
int defaultHeight;
|
|
NES Nes;
|
|
|
|
NES.PPU.DebugCallback Callback = new NES.PPU.DebugCallback();
|
|
|
|
public NESPPU()
|
|
{
|
|
InitializeComponent();
|
|
Closing += (o, e) => SaveConfigSettings();
|
|
Callback.Callback = () => Generate();
|
|
}
|
|
|
|
private void SaveConfigSettings()
|
|
{
|
|
Global.Config.NESPPUWndx = this.Location.X;
|
|
Global.Config.NESPPUWndy = this.Location.Y;
|
|
}
|
|
|
|
public void Restart()
|
|
{
|
|
if (!(Global.Emulator is NES)) this.Close();
|
|
if (!this.IsHandleCreated || this.IsDisposed) return;
|
|
Nes = Global.Emulator as NES;
|
|
}
|
|
|
|
private void LoadConfigSettings()
|
|
{
|
|
defaultWidth = Size.Width; //Save these first so that the user can restore to its original size
|
|
defaultHeight = Size.Height;
|
|
|
|
if (Global.Config.NESPPUSaveWindowPosition && Global.Config.NESPPUWndx >= 0 && Global.Config.NESPPUWndy >= 0)
|
|
Location = new Point(Global.Config.NESPPUWndx, Global.Config.NESPPUWndy);
|
|
}
|
|
|
|
private byte GetBit(int address, int bit)
|
|
{
|
|
byte value = Nes.ppu.ppubus_peek(address);
|
|
return (byte)(((value >> (7 - bit)) & 1));
|
|
}
|
|
|
|
unsafe void Generate()
|
|
{
|
|
if (!this.IsHandleCreated || this.IsDisposed) return;
|
|
|
|
//Pattern Viewer
|
|
for (int x = 0; x < 16; x++)
|
|
{
|
|
PaletteView.bgPalettes[x].SetValue(Nes.LookupColor(Nes.ppu.PALRAM[PaletteView.bgPalettes[x].address]));
|
|
PaletteView.spritePalettes[x].SetValue(Nes.LookupColor(Nes.ppu.PALRAM[PaletteView.spritePalettes[x].address]));
|
|
}
|
|
PaletteView.Refresh();
|
|
|
|
//Pattern Viewer
|
|
int b0 = 0;
|
|
int b1 = 0;
|
|
byte value;
|
|
int cvalue;
|
|
int pal;
|
|
|
|
System.Drawing.Imaging.BitmapData bmpdata = PatternView.pattern.LockBits(new Rectangle(new Point(0, 0), PatternView.pattern.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
|
int* framebuf = (int*)bmpdata.Scan0.ToPointer();
|
|
for (int z = 0; z < 2; z++)
|
|
{
|
|
if (z == 0)
|
|
pal = PatternView.Pal0;
|
|
else
|
|
pal = PatternView.Pal1;
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
for (int j = 0; j < 16; j++)
|
|
{
|
|
for (int x = 0; x < 8; x++)
|
|
{
|
|
for (int y = 0; y < 8; y++)
|
|
{
|
|
b0 = GetBit((z * 0x1000) + (i * 256) + (j * 16) + y + 0 * 8, x);
|
|
b1 = GetBit((z * 0x1000) + (i * 256) + (j * 16) + y + 1 * 8, x);
|
|
|
|
value = (byte)(b0 + (b1 << 1));
|
|
|
|
cvalue = Nes.LookupColor(Nes.ppu.PALRAM[value + (pal * 4)]);
|
|
|
|
Color color = Color.FromArgb(cvalue);
|
|
|
|
int adr = (x + (j * 8)) + (y + (i * 8)) * (bmpdata.Stride / 4);
|
|
framebuf[adr + (z * 128)] = color.ToArgb();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PatternView.pattern.UnlockBits(bmpdata);
|
|
PatternView.Refresh();
|
|
/*
|
|
int SpriteNum, TileNum, Attr, MemAddr;
|
|
|
|
//Sprite Viewer
|
|
for (int y = 0; y < 4; y++)
|
|
{
|
|
for (int x = 0; x < 16; x++)
|
|
{
|
|
SpriteNum = (y << 4) | x;
|
|
TileNum = 0; //TODO
|
|
Attr = 0; //TODO
|
|
if (((int)Nes.ppu.reg_2000.Value & (int)0x20) > 0) //TODO why is C# being retarded about using & with a byte?
|
|
{
|
|
MemAddr = ((TileNum & 0xFE) << 4) | ((TileNum & 0x01) << 12);
|
|
//DrawTile(SprArray + y * 24 * D_SPR_W + x * 16, MemAddr, 4 | (Attr & 3), D_SPR_W);
|
|
//DrawTile(SprArray + y * 24 * D_SPR_W + x * 16 + 8 * D_SPR_W, MemAddr + 16, 4 | (Attr & 3), D_SPR_W);
|
|
|
|
}
|
|
else
|
|
{
|
|
MemAddr = (TileNum << 4) | ((Nes.ppu.reg_2000.Value & (byte)0x08) << 9);
|
|
//DrawTile(SprArray + y * 24 * D_SPR_W + x * 16, MemAddr, 4 | (Attr & 3), D_SPR_W);
|
|
}
|
|
}
|
|
}
|
|
* */
|
|
}
|
|
|
|
public unsafe void UpdateValues()
|
|
{
|
|
if (!(Global.Emulator is NES)) return;
|
|
NES.PPU ppu = (Global.Emulator as NES).ppu;
|
|
ppu.PPUViewCallback = Callback;
|
|
}
|
|
|
|
private void NESPPU_Load(object sender, EventArgs e)
|
|
{
|
|
LoadConfigSettings();
|
|
Nes = Global.Emulator as NES;
|
|
ClearDetails();
|
|
}
|
|
|
|
private void ClearDetails()
|
|
{
|
|
SectionLabel.Text = "";
|
|
AddressLabel.Text = "";
|
|
ValueLabel.Text = "";
|
|
Value2Label.Text = "";
|
|
}
|
|
|
|
private void PaletteView_MouseLeave(object sender, EventArgs e)
|
|
{
|
|
ClearDetails();
|
|
}
|
|
|
|
private void PaletteView_MouseEnter(object sender, EventArgs e)
|
|
{
|
|
SectionLabel.Text = "Section: Palette";
|
|
}
|
|
|
|
private void PaletteView_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
int baseAddr = 0x3F00;
|
|
if (e.Y > 16)
|
|
baseAddr += 16;
|
|
int column = (e.X - PaletteView.Location.X) / 16;
|
|
int addr = column + baseAddr;
|
|
AddressLabel.Text = "Address: 0x" + String.Format("{0:X4}", addr, NumberStyles.HexNumber);
|
|
int val;
|
|
if (baseAddr == 0x3F00)
|
|
val = PaletteView.bgPalettes[column].GetValue();
|
|
else
|
|
val = PaletteView.spritePalettes[column].GetValue();
|
|
ValueLabel.Text = "Color: 0x" + String.Format("{0:X2}", val, NumberStyles.HexNumber);
|
|
|
|
if (baseAddr == 0x3F00)
|
|
Value2Label.Text = "ID: BG" + (column / 4).ToString();
|
|
else
|
|
Value2Label.Text = "ID: SPR" + (column / 4).ToString();
|
|
}
|
|
|
|
private void autoloadToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Global.Config.AutoLoadNESPPU ^= true;
|
|
}
|
|
|
|
private void saveWindowPositionToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Global.Config.NESPPUSaveWindowPosition ^= true;
|
|
}
|
|
|
|
private void toolStripDropDownButton1_DropDownOpened(object sender, EventArgs e)
|
|
{
|
|
autoloadToolStripMenuItem.Checked = Global.Config.AutoLoadNESPPU;
|
|
saveWindowPositionToolStripMenuItem.Checked = Global.Config.NESPPUSaveWindowPosition;
|
|
}
|
|
|
|
private void PatternView_Click(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.X < PatternView.Width / 2)
|
|
{
|
|
PatternView.Pal0++;
|
|
if (PatternView.Pal0 > 7) PatternView.Pal0 = 0;
|
|
}
|
|
else
|
|
{
|
|
PatternView.Pal1++;
|
|
if (PatternView.Pal1 > 7) PatternView.Pal1 = 0;
|
|
}
|
|
UpdateTableLabels();
|
|
}
|
|
|
|
private void UpdateTableLabels()
|
|
{
|
|
Table0PaletteLabel.Text = "Palette: " + PatternView.Pal0;
|
|
Table1PaletteLabel.Text = "Palette: " + PatternView.Pal1;
|
|
PatternView.Refresh();
|
|
}
|
|
|
|
private void PatternView_MouseEnter(object sender, EventArgs e)
|
|
{
|
|
SectionLabel.Text = "Section: Pattern";
|
|
}
|
|
|
|
private void PatternView_MouseLeave(object sender, EventArgs e)
|
|
{
|
|
ClearDetails();
|
|
}
|
|
|
|
private void PatternView_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
int table = 0;
|
|
int address = 0;
|
|
int tile = 0;
|
|
if (e.X > PatternView.Width / 2)
|
|
table = 1;
|
|
|
|
if (table == 0)
|
|
{
|
|
tile = address = (e.X - 1) / 8;
|
|
|
|
}
|
|
else
|
|
{
|
|
address = 0x1000 + ((e.X - 128) / 8);
|
|
tile = (e.X - 128) / 8;
|
|
}
|
|
|
|
address += (e.Y / 8) * 256;
|
|
tile += (e.Y / 8) * 16;
|
|
|
|
AddressLabel.Text = "Address: " + String.Format("{0:X4}", address);
|
|
ValueLabel.Text = "Table " + table.ToString();
|
|
Value2Label.Text = "Tile " + String.Format("{0:X2}", tile);
|
|
}
|
|
|
|
private void toolStripDropDownButton2_DropDownOpened(object sender, EventArgs e)
|
|
{
|
|
Table0P0.Checked = false;
|
|
Table0P1.Checked = false;
|
|
Table0P2.Checked = false;
|
|
Table0P3.Checked = false;
|
|
Table0P4.Checked = false;
|
|
Table0P5.Checked = false;
|
|
Table0P6.Checked = false;
|
|
Table0P7.Checked = false;
|
|
Table1P0.Checked = false;
|
|
Table1P1.Checked = false;
|
|
Table1P2.Checked = false;
|
|
Table1P3.Checked = false;
|
|
Table1P4.Checked = false;
|
|
Table1P5.Checked = false;
|
|
Table1P6.Checked = false;
|
|
Table1P7.Checked = false;
|
|
|
|
Table0P0.Checked = false;
|
|
|
|
switch (PatternView.Pal0)
|
|
{
|
|
case 0:
|
|
Table0P0.Checked = true;
|
|
break;
|
|
case 1:
|
|
Table0P1.Checked = true;
|
|
break;
|
|
case 2:
|
|
Table0P2.Checked = true;
|
|
break;
|
|
case 3:
|
|
Table0P3.Checked = true;
|
|
break;
|
|
case 4:
|
|
Table0P4.Checked = true;
|
|
break;
|
|
case 5:
|
|
Table0P5.Checked = true;
|
|
break;
|
|
case 6:
|
|
Table0P6.Checked = true;
|
|
break;
|
|
case 7:
|
|
Table0P7.Checked = true;
|
|
break;
|
|
}
|
|
|
|
switch (PatternView.Pal1)
|
|
{
|
|
case 0:
|
|
Table1P0.Checked = true;
|
|
break;
|
|
case 1:
|
|
Table1P1.Checked = true;
|
|
break;
|
|
case 2:
|
|
Table1P2.Checked = true;
|
|
break;
|
|
case 3:
|
|
Table1P3.Checked = true;
|
|
break;
|
|
case 4:
|
|
Table1P4.Checked = true;
|
|
break;
|
|
case 5:
|
|
Table1P5.Checked = true;
|
|
break;
|
|
case 6:
|
|
Table1P6.Checked = true;
|
|
break;
|
|
case 7:
|
|
Table1P7.Checked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void Palette_Click(object sender, EventArgs e)
|
|
{
|
|
if (sender == Table0P0) PatternView.Pal0 = 0;
|
|
if (sender == Table0P1) PatternView.Pal0 = 1;
|
|
if (sender == Table0P2) PatternView.Pal0 = 2;
|
|
if (sender == Table0P3) PatternView.Pal0 = 3;
|
|
if (sender == Table0P4) PatternView.Pal0 = 4;
|
|
if (sender == Table0P5) PatternView.Pal0 = 5;
|
|
if (sender == Table0P6) PatternView.Pal0 = 6;
|
|
if (sender == Table0P7) PatternView.Pal0 = 7;
|
|
|
|
if (sender == Table1P0) PatternView.Pal1 = 0;
|
|
if (sender == Table1P1) PatternView.Pal1 = 1;
|
|
if (sender == Table1P2) PatternView.Pal1 = 2;
|
|
if (sender == Table1P3) PatternView.Pal1 = 3;
|
|
if (sender == Table1P4) PatternView.Pal1 = 4;
|
|
if (sender == Table1P5) PatternView.Pal1 = 5;
|
|
if (sender == Table1P6) PatternView.Pal1 = 6;
|
|
if (sender == Table1P7) PatternView.Pal1 = 7;
|
|
|
|
UpdateTableLabels();
|
|
}
|
|
|
|
private void txtScanline_TextChanged(object sender, EventArgs e)
|
|
{
|
|
int temp = 0;
|
|
if (int.TryParse(txtScanline.Text, out temp))
|
|
{
|
|
Callback.Scanline = temp;
|
|
}
|
|
}
|
|
|
|
private void NESPPU_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
if (Nes == null) return;
|
|
if (Nes.ppu.PPUViewCallback == Callback)
|
|
Nes.ppu.PPUViewCallback = null;
|
|
}
|
|
}
|
|
}
|