2011-03-30 00:09:07 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Drawing;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
using System.Drawing.Imaging;
|
2011-03-30 00:09:07 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows.Forms;
|
2011-03-30 00:22:51 +00:00
|
|
|
|
using BizHawk.Emulation.Consoles.Nintendo;
|
2011-03-30 00:09:07 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.MultiClient
|
|
|
|
|
{
|
2011-06-10 08:10:16 +00:00
|
|
|
|
public partial class NESNameTableViewer : Form
|
|
|
|
|
{
|
|
|
|
|
int defaultWidth; //For saving the default size of the dialog, so the user can restore if desired
|
|
|
|
|
int defaultHeight;
|
|
|
|
|
NES Nes;
|
2011-03-30 00:09:07 +00:00
|
|
|
|
|
2011-06-10 07:43:48 +00:00
|
|
|
|
NES.PPU.DebugCallback Callback = new NES.PPU.DebugCallback();
|
|
|
|
|
|
|
|
|
|
|
2011-06-10 08:10:16 +00:00
|
|
|
|
public NESNameTableViewer()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
Closing += (o, e) => SaveConfigSettings();
|
2011-06-10 07:43:48 +00:00
|
|
|
|
Callback.Callback = () => Generate();
|
2011-06-10 08:10:16 +00:00
|
|
|
|
}
|
2011-03-30 00:09:07 +00:00
|
|
|
|
|
2011-06-10 08:10:16 +00:00
|
|
|
|
private void SaveConfigSettings()
|
|
|
|
|
{
|
|
|
|
|
Global.Config.NESNameTableWndx = this.Location.X;
|
|
|
|
|
Global.Config.NESNameTableWndy = this.Location.Y;
|
|
|
|
|
}
|
2011-03-30 00:09:07 +00:00
|
|
|
|
|
2011-06-10 07:43:48 +00:00
|
|
|
|
unsafe void Generate()
|
|
|
|
|
{
|
|
|
|
|
if (!this.IsHandleCreated || this.IsDisposed) return;
|
|
|
|
|
if (Nes == null) return;
|
2011-03-30 00:15:39 +00:00
|
|
|
|
|
2011-06-10 07:43:48 +00:00
|
|
|
|
NES.PPU ppu = Nes.ppu;
|
|
|
|
|
if (!this.IsHandleCreated || this.IsDisposed) return;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
BitmapData bmpdata = NameTableView.nametables.LockBits(new Rectangle(0, 0, 512, 480), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
2011-06-10 07:43:48 +00:00
|
|
|
|
|
2011-06-06 18:19:24 +00:00
|
|
|
|
int* dptr = (int*)bmpdata.Scan0.ToPointer();
|
|
|
|
|
int pitch = bmpdata.Stride / 4;
|
|
|
|
|
int pt_add = ppu.reg_2000.bg_pattern_hi ? 0x1000 : 0;
|
2011-03-30 00:15:39 +00:00
|
|
|
|
|
2011-06-06 18:19:24 +00:00
|
|
|
|
//TODO - buffer all the data from the ppu, because it will be read multiple times and that is slow
|
2011-03-30 02:39:19 +00:00
|
|
|
|
|
2011-06-10 07:43:48 +00:00
|
|
|
|
int ytable = 0, yline = 0;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
for (int y = 0; y < 480; y++)
|
|
|
|
|
{
|
|
|
|
|
if (y == 240)
|
|
|
|
|
{
|
|
|
|
|
ytable += 2;
|
|
|
|
|
yline = 240;
|
|
|
|
|
}
|
|
|
|
|
for (int x = 0; x < 512; x++, dptr++)
|
|
|
|
|
{
|
|
|
|
|
int table = (x >> 8) + ytable;
|
|
|
|
|
int ntaddr = (table << 10);
|
|
|
|
|
int px = x & 255;
|
|
|
|
|
int py = y - yline;
|
2011-06-10 07:43:48 +00:00
|
|
|
|
int tx = px >> 3;
|
|
|
|
|
int ty = py >> 3;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
int ntbyte_ptr = ntaddr + (ty * 32) + tx;
|
|
|
|
|
int atbyte_ptr = ntaddr + 0x3C0 + ((ty >> 2) << 3) + (tx >> 2);
|
|
|
|
|
int nt = ppu.ppubus_peek(ntbyte_ptr + 0x2000);
|
2011-06-10 07:43:48 +00:00
|
|
|
|
|
2011-06-06 18:19:24 +00:00
|
|
|
|
int at = ppu.ppubus_peek(atbyte_ptr + 0x2000);
|
2011-06-10 07:43:48 +00:00
|
|
|
|
if ((ty & 2) != 0) at >>= 4;
|
|
|
|
|
if ((tx & 2) != 0) at >>= 2;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
at &= 0x03;
|
|
|
|
|
at <<= 2;
|
|
|
|
|
|
|
|
|
|
int bgpx = x & 7;
|
|
|
|
|
int bgpy = y & 7;
|
|
|
|
|
int pt_addr = (nt << 4) + bgpy + pt_add;
|
|
|
|
|
int pt_0 = ppu.ppubus_peek(pt_addr);
|
|
|
|
|
int pt_1 = ppu.ppubus_peek(pt_addr + 8);
|
|
|
|
|
int pixel = ((pt_0 >> (7 - bgpx)) & 1) | (((pt_1 >> (7 - bgpx)) & 1) << 1);
|
2011-06-10 07:43:48 +00:00
|
|
|
|
|
2011-06-07 07:14:34 +00:00
|
|
|
|
//if the pixel is transparent, draw the backdrop color
|
|
|
|
|
//TODO - consider making this optional? nintendulator does it and fceux doesnt need to do it due to buggy palette logic which creates the same effect
|
2011-06-10 07:43:48 +00:00
|
|
|
|
if (pixel != 0)
|
2011-06-07 07:14:34 +00:00
|
|
|
|
pixel |= at;
|
2011-06-06 18:19:24 +00:00
|
|
|
|
|
|
|
|
|
pixel = ppu.PALRAM[pixel];
|
|
|
|
|
int cvalue = Nes.LookupColor(pixel);
|
|
|
|
|
*dptr = cvalue;
|
|
|
|
|
}
|
|
|
|
|
dptr += pitch - 512;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NameTableView.nametables.UnlockBits(bmpdata);
|
2011-03-30 02:39:19 +00:00
|
|
|
|
|
2011-06-10 07:43:48 +00:00
|
|
|
|
NameTableView.Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-10 08:10:16 +00:00
|
|
|
|
public void UpdateValues()
|
|
|
|
|
{
|
2011-08-27 14:56:17 +00:00
|
|
|
|
if (!this.IsHandleCreated || this.IsDisposed) return;
|
2011-06-10 08:10:16 +00:00
|
|
|
|
if (!(Global.Emulator is NES)) return;
|
2011-06-10 07:43:48 +00:00
|
|
|
|
NES.PPU ppu = (Global.Emulator as NES).ppu;
|
|
|
|
|
ppu.NTViewCallback = Callback;
|
2011-06-10 08:10:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Restart()
|
|
|
|
|
{
|
|
|
|
|
if (!(Global.Emulator is NES)) this.Close();
|
|
|
|
|
Nes = Global.Emulator as NES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NESNameTableViewer_Load(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
defaultWidth = this.Size.Width; //Save these first so that the user can restore to its original size
|
|
|
|
|
defaultHeight = this.Size.Height;
|
|
|
|
|
|
|
|
|
|
if (Global.Config.NESNameTableSaveWindowPosition && Global.Config.NESNameTableWndx >= 0 && Global.Config.NESNameTableWndy >= 0)
|
|
|
|
|
this.Location = new Point(Global.Config.NESNameTableWndx, Global.Config.NESNameTableWndy);
|
|
|
|
|
|
|
|
|
|
Nes = Global.Emulator as NES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
this.Close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void autoloadToolStripMenuItem_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Global.Config.AutoLoadNESNameTable ^= true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void saveWindowPositionToolStripMenuItem_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Global.Config.NESNameTableSaveWindowPosition ^= true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void optionsToolStripMenuItem_DropDownOpened(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
autoloadToolStripMenuItem.Checked = Global.Config.AutoLoadNESNameTable;
|
|
|
|
|
saveWindowPositionToolStripMenuItem.Checked = Global.Config.NESNameTableSaveWindowPosition;
|
|
|
|
|
}
|
2011-06-10 07:43:48 +00:00
|
|
|
|
|
|
|
|
|
private void txtScanline_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
2011-06-10 08:10:16 +00:00
|
|
|
|
int temp = 0;
|
2011-06-10 07:43:48 +00:00
|
|
|
|
if (int.TryParse(txtScanline.Text, out temp))
|
|
|
|
|
{
|
|
|
|
|
Callback.Scanline = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NESNameTableViewer_FormClosed(object sender, FormClosedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (Nes == null) return;
|
|
|
|
|
if (Nes.ppu.NTViewCallback == Callback)
|
|
|
|
|
Nes.ppu.NTViewCallback = null;
|
|
|
|
|
}
|
2011-06-10 08:10:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void rbNametable_CheckedChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (rbNametableNW.Checked) NameTableView.Which = NameTableViewer.WhichNametable.NT_2000;
|
|
|
|
|
if (rbNametableNE.Checked) NameTableView.Which = NameTableViewer.WhichNametable.NT_2400;
|
|
|
|
|
if (rbNametableSW.Checked) NameTableView.Which = NameTableViewer.WhichNametable.NT_2800;
|
|
|
|
|
if (rbNametableSE.Checked) NameTableView.Which = NameTableViewer.WhichNametable.NT_2C00;
|
|
|
|
|
if (rbNametableAll.Checked) NameTableView.Which = NameTableViewer.WhichNametable.NT_ALL;
|
|
|
|
|
}
|
2011-08-27 21:07:09 +00:00
|
|
|
|
|
|
|
|
|
private void NameTableView_MouseMove(object sender, MouseEventArgs e)
|
|
|
|
|
{
|
2011-08-27 23:11:47 +00:00
|
|
|
|
int TileX, TileY, NameTable;
|
|
|
|
|
if (NameTableView.Which == NameTableViewer.WhichNametable.NT_ALL)
|
|
|
|
|
{
|
|
|
|
|
TileX = e.X / 8;
|
|
|
|
|
TileY = e.Y / 8;
|
|
|
|
|
NameTable = (TileX / 32) + ((TileY / 30) * 2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (NameTableView.Which)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case NameTableViewer.WhichNametable.NT_2000:
|
|
|
|
|
NameTable = 0;
|
|
|
|
|
break;
|
|
|
|
|
case NameTableViewer.WhichNametable.NT_2400:
|
|
|
|
|
NameTable = 1;
|
|
|
|
|
break;
|
|
|
|
|
case NameTableViewer.WhichNametable.NT_2800:
|
|
|
|
|
NameTable = 2;
|
|
|
|
|
break;
|
|
|
|
|
case NameTableViewer.WhichNametable.NT_2C00:
|
|
|
|
|
NameTable = 3;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-08-27 21:07:09 +00:00
|
|
|
|
|
2011-08-27 23:11:47 +00:00
|
|
|
|
TileX = e.X / 16;
|
|
|
|
|
TileY = e.Y / 16;
|
|
|
|
|
}
|
2011-08-28 00:27:39 +00:00
|
|
|
|
|
2011-08-27 23:11:47 +00:00
|
|
|
|
XYLabel.Text = TileX.ToString() + " : " + TileY.ToString();
|
|
|
|
|
int PPUAddress = 0x2000 + (NameTable * 0x400) + ((TileY % 30) * 32) + (TileX % 32);
|
|
|
|
|
PPUAddressLabel.Text = String.Format("{0:X4}", PPUAddress);
|
2011-08-28 00:27:39 +00:00
|
|
|
|
int TileID = Nes.ppu.ppubus_read(PPUAddress, true);
|
2011-08-27 23:11:47 +00:00
|
|
|
|
TileIDLabel.Text = String.Format("{0:X2}", TileID);
|
2011-08-29 01:09:16 +00:00
|
|
|
|
TableLabel.Text = NameTable.ToString();
|
|
|
|
|
|
|
|
|
|
int ytable = 0, yline = 0;
|
|
|
|
|
if (e.Y >= 240)
|
|
|
|
|
{
|
|
|
|
|
ytable += 2;
|
|
|
|
|
yline = 240;
|
|
|
|
|
}
|
|
|
|
|
int pt_add = Nes.ppu.reg_2000.bg_pattern_hi ? 0x1000 : 0;
|
|
|
|
|
int table = (e.X >> 8) + ytable;
|
|
|
|
|
int ntaddr = (table << 10);
|
|
|
|
|
int px = e.X & 255;
|
|
|
|
|
int py = e.Y - yline;
|
|
|
|
|
int tx = px >> 3;
|
|
|
|
|
int ty = py >> 3;
|
|
|
|
|
int ntbyte_ptr = ntaddr + (ty * 32) + tx;
|
|
|
|
|
int atbyte_ptr = ntaddr + 0x3C0 + ((ty >> 2) << 3) + (tx >> 2);
|
|
|
|
|
int nt = Nes.ppu.ppubus_peek(ntbyte_ptr + 0x2000);
|
|
|
|
|
|
|
|
|
|
int at = Nes.ppu.ppubus_peek(atbyte_ptr + 0x2000);
|
|
|
|
|
if ((ty & 2) != 0) at >>= 4;
|
|
|
|
|
if ((tx & 2) != 0) at >>= 2;
|
|
|
|
|
at &= 0x03;
|
|
|
|
|
PaletteLabel.Text = at.ToString();
|
2011-08-27 23:11:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NameTableView_MouseLeave(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
XYLabel.Text = "";
|
|
|
|
|
PPUAddressLabel.Text = "";
|
|
|
|
|
TileIDLabel.Text = "";
|
2011-08-29 01:09:16 +00:00
|
|
|
|
TableLabel.Text = "";
|
|
|
|
|
PaletteLabel.Text = "";
|
2011-08-27 21:07:09 +00:00
|
|
|
|
}
|
2011-06-10 08:10:16 +00:00
|
|
|
|
}
|
2011-03-30 00:09:07 +00:00
|
|
|
|
}
|