Implement snes graphics debugger for the new bsnes core (#3367)

* Partial port of graphics debugger to new BSNES

* minimal "working" copy-paste

* small fix for the previous commit

* Implement more stuff

* no idea whose responsibility "EnterExit" is but this should work

* add support for backdropcolor

i have 0% trust in this code

* implement mode7, apply backcolor on load

* 🙈

un-"implement" the nonfunctional scanlinehookmanager as well as the non-functional palette setting logic
- this may actually break config lol

* don't break libsnes config

* Provide IBSNESForGfxDebugger in the subbsnes core

* Remove redundant semicolon

* Clean up diff of `comboPalette_SelectedIndexChanged`

* Fix crash

Co-authored-by: YoshiRulz <OSSYoshiRulz@gmail.com>
This commit is contained in:
Moritz Bender 2022-09-03 19:13:56 +02:00 committed by GitHub
parent 28d62e69d7
commit 6f0953aaa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1401 additions and 509 deletions

Binary file not shown.

View File

@ -1708,7 +1708,6 @@ namespace BizHawk.Client.EmuHawk
private void SnesSubMenu_DropDownOpened(object sender, EventArgs e)
{
SNESControllerConfigurationMenuItem.Enabled = MovieSession.Movie.NotActive();
SnesGfxDebuggerMenuItem.Enabled = true;
}
private DialogResult OpenOldBSNESGamepadSettingsDialog(ISettingsAdapter settable)

View File

@ -2042,21 +2042,17 @@ namespace BizHawk.Client.EmuHawk
case VSystemID.Raw.SNES when Emulator is LibsnesCore { IsSGB: true }: // doesn't use "SGB" sysID
SNESSubMenu.Text = "&SGB";
SNESSubMenu.Visible = true;
SnesGfxDebuggerMenuItem.Visible = true;
break;
case VSystemID.Raw.SNES when Emulator is LibsnesCore { IsSGB: false }:
SNESSubMenu.Text = "&SNES";
SNESSubMenu.Visible = true;
SnesGfxDebuggerMenuItem.Visible = true;
break;
case VSystemID.Raw.SNES when Emulator is BsnesCore bsnesCore:
SNESSubMenu.Text = bsnesCore.IsSGB ? "&SGB" : "&SNES";
SnesGfxDebuggerMenuItem.Visible = false;
SNESSubMenu.Visible = true;
break;
case VSystemID.Raw.SNES when Emulator is SubBsnesCore subBsnesCore:
SNESSubMenu.Text = subBsnesCore.IsSGB ? "&SGB" : "&SNES";
SnesGfxDebuggerMenuItem.Visible = false;
SNESSubMenu.Visible = true;
break;
default:

View File

@ -41,7 +41,7 @@ namespace BizHawk.Client.EmuHawk
private readonly List<DisplayTypeItem> displayTypeItems = new List<DisplayTypeItem>();
[RequiredService]
private LibsnesCore Emulator { get; set; }
private IBSNESForGfxDebugger Emulator { get; set; }
[ConfigPersist]
public bool UseUserBackdropColor
@ -63,7 +63,7 @@ namespace BizHawk.Client.EmuHawk
displayTypeItems.Add(new DisplayTypeItem("Sprites", eDisplayType.Sprites));
displayTypeItems.Add(new DisplayTypeItem("OBJ", eDisplayType.OBJ));
displayTypeItems.Add(new DisplayTypeItem("BG1 Screen", eDisplayType.BG1));
displayTypeItems.Add(new DisplayTypeItem("BG2 Screen", eDisplayType.BG2));
displayTypeItems.Add(new DisplayTypeItem("BG3 Screen", eDisplayType.BG3));
@ -85,6 +85,7 @@ namespace BizHawk.Client.EmuHawk
var paletteTypeItems = new List<PaletteTypeItem>
{
// must be in same order as enum
new PaletteTypeItem("BizHawk", SnesColors.ColorType.BizHawk),
new PaletteTypeItem("bsnes", SnesColors.ColorType.BSNES),
new PaletteTypeItem("Snes9X", SnesColors.ColorType.Snes9x)
@ -104,12 +105,14 @@ namespace BizHawk.Client.EmuHawk
UserBackdropColor = -1;
}
private LibsnesCore currentSnesCore;
private IBSNESForGfxDebugger currentSnesCore;
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
currentSnesCore?.ScanlineHookManager.Unregister(this);
currentSnesCore?.ScanlineHookManager?.Unregister(this);
currentSnesCore = null;
gd = null;
}
private string FormatBpp(int bpp)
@ -168,11 +171,12 @@ namespace BizHawk.Client.EmuHawk
{
if (currentSnesCore != Emulator)
{
currentSnesCore?.ScanlineHookManager.Unregister(this);
currentSnesCore?.ScanlineHookManager?.Unregister(this);
}
if (currentSnesCore != Emulator && Emulator != null)
{
gd = null;
suppression = true;
comboPalette.SelectedValue = Emulator.CurrPalette;
RefreshBGENCheckStatesFromConfig();
@ -184,9 +188,9 @@ namespace BizHawk.Client.EmuHawk
if (currentSnesCore != null)
{
if (Visible && checkScanlineControl.Checked)
currentSnesCore.ScanlineHookManager.Register(this, ScanlineHook);
currentSnesCore.ScanlineHookManager?.Register(this, ScanlineHook);
else
currentSnesCore.ScanlineHookManager.Unregister(this);
currentSnesCore.ScanlineHookManager?.Unregister(this);
}
}
@ -200,7 +204,7 @@ namespace BizHawk.Client.EmuHawk
}
}
private SNESGraphicsDecoder gd;
private ISNESGraphicsDecoder gd;
private SNESGraphicsDecoder.ScreenInfo si;
private SNESGraphicsDecoder.TileEntry[] map;
private readonly byte[,] spriteMap = new byte[256, 224];
@ -208,14 +212,10 @@ namespace BizHawk.Client.EmuHawk
private void RegenerateData()
{
gd?.Dispose();
gd = null;
if (currentSnesCore == null) return;
gd = NewDecoder();
gd ??= NewDecoder();
using (gd.EnterExit())
{
if (checkBackdropColor.Checked)
gd.SetBackColor(DecodeWinformsColorToSNES(pnBackdropColor.BackColor));
gd.CacheTiles();
si = gd.ScanScreenInfo();
}
@ -247,7 +247,7 @@ namespace BizHawk.Client.EmuHawk
txtScreenCGADSUB_AddSub_Descr.Text = si.CGADSUB_AddSub == 1 ? "SUB" : "ADD";
txtScreenCGADSUB_Half.Checked = si.CGADSUB_Half;
txtModeBits.Text = si.Mode.MODE.ToString();
txtModeBits.Text = si.Mode.ToString();
txtScreenBG1Bpp.Text = FormatBpp(si.BG.BG1.Bpp);
txtScreenBG2Bpp.Text = FormatBpp(si.BG.BG2.Bpp);
txtScreenBG3Bpp.Text = FormatBpp(si.BG.BG3.Bpp);
@ -303,7 +303,7 @@ namespace BizHawk.Client.EmuHawk
checkMathBG3.Checked = si.BG.BG3.MathEnabled;
checkMathBG4.Checked = si.BG.BG4.MathEnabled;
if (si.Mode.MODE == 1 && si.Mode1_BG3_Priority)
if (si.Mode == 1 && si.Mode1_BG3_Priority)
{
lblBG3.ForeColor = Color.Red;
if (toolTip1.GetToolTip(lblBG3) != "Mode 1 BG3 priority toggle bit of $2105 is SET")
@ -370,7 +370,7 @@ namespace BizHawk.Client.EmuHawk
for (int y = 0; y < 224; y++) for (int x = 0; x < 256; x++) spriteMap[x, y] = 0xFF;
for(int i=127;i>=0;i--)
{
var oam = new SNESGraphicsDecoder.OAMInfo(gd, si, i);
var oam = gd.CreateOAMInfo(si, i);
gd.RenderSpriteToScreen(pixelptr, stride / 4, oam.X, oam.Y, si, i, oam, 256, 224, spriteMap);
}
}
@ -379,24 +379,24 @@ namespace BizHawk.Client.EmuHawk
allocate(128, 256);
int startTile;
startTile = si.OBJTable0Addr / 32;
gd.RenderTilesToScreen(pixelptr, 16, 16, stride / 4, 4, currPaletteSelection.start, startTile, 256, true);
gd.RenderTilesToScreen(pixelptr, stride / 4, 4, currPaletteSelection.start, startTile, 256);
startTile = si.OBJTable1Addr / 32;
gd.RenderTilesToScreen(pixelptr + (stride/4*8*16), 16, 16, stride / 4, 4, currPaletteSelection.start, startTile, 256, true);
gd.RenderTilesToScreen(pixelptr + (stride/4*8*16), stride / 4, 4, currPaletteSelection.start, startTile, 256);
}
if (selection == eDisplayType.Tiles2bpp)
{
allocate(512, 512);
gd.RenderTilesToScreen(pixelptr, 64, 64, stride / 4, 2, currPaletteSelection.start);
gd.RenderTilesToScreen(pixelptr, stride / 4, 2, currPaletteSelection.start);
}
if (selection == eDisplayType.Tiles4bpp)
{
allocate(512, 512);
gd.RenderTilesToScreen(pixelptr, 64, 32, stride / 4, 4, currPaletteSelection.start);
gd.RenderTilesToScreen(pixelptr, stride / 4, 4, currPaletteSelection.start);
}
if (selection == eDisplayType.Tiles8bpp)
{
allocate(256, 256);
gd.RenderTilesToScreen(pixelptr, 32, 32, stride / 4, 8, currPaletteSelection.start);
gd.RenderTilesToScreen(pixelptr, stride / 4, 8, currPaletteSelection.start);
}
if (selection == eDisplayType.TilesMode7)
{
@ -431,7 +431,7 @@ namespace BizHawk.Client.EmuHawk
bool DirectColor = si.CGWSEL_DirectColor && bg.Bpp == 8; //any exceptions?
int numPixels = 0;
//TODO - could use BGMode property on BG... too much chaos to deal with it now
if (si.Mode.MODE == 7)
if (si.Mode == 7)
{
bool mode7 = bgnum == 1;
bool mode7extbg = (bgnum == 2 && si.SETINI_Mode7ExtBG);
@ -499,7 +499,7 @@ namespace BizHawk.Client.EmuHawk
default: throw new InvalidOperationException();
}
}
private class DisplayTypeItem
{
public eDisplayType Type { get; }
@ -533,6 +533,9 @@ namespace BizHawk.Client.EmuHawk
private void SNESGraphicsDebugger_Load(object sender, EventArgs e)
{
currentSnesCore = Emulator;
gd = NewDecoder();
if (UserBackdropColor != -1)
{
pnBackdropColor.BackColor = Color.FromArgb(UserBackdropColor);
@ -540,6 +543,7 @@ namespace BizHawk.Client.EmuHawk
if (checkBackdropColor.Checked)
{
SyncBackdropColor();
gd.SetBackColor(DecodeWinformsColorToSNES(pnBackdropColor.BackColor));
}
UpdateToolsLoadstate();
@ -639,16 +643,14 @@ namespace BizHawk.Client.EmuHawk
ret.start = 0;
return ret;
}
ret.size = 1 << bpp;
ret.start = colorSelection & (~(ret.size - 1));
return ret;
}
private SNESGraphicsDecoder NewDecoder()
{
return currentSnesCore != null ? new SNESGraphicsDecoder(currentSnesCore.Api, currentSnesCore.CurrPalette) : null;
}
private ISNESGraphicsDecoder NewDecoder()
=> currentSnesCore?.CreateGraphicsDecoder();
private void RenderPalette()
{
@ -713,13 +715,13 @@ namespace BizHawk.Client.EmuHawk
private void UpdateOBJDetails()
{
if (currObjDataState == null) return;
var oam = new SNESGraphicsDecoder.OAMInfo(gd, si, currObjDataState.Number);
var oam = gd.CreateOAMInfo(si, currObjDataState.Number);
txtObjNumber.Text = $"#${currObjDataState.Number:X2}";
txtObjCoord.Text = $"({oam.X}, {oam.Y})";
cbObjHFlip.Checked = oam.HFlip;
cbObjVFlip.Checked = oam.VFlip;
cbObjLarge.Checked = oam.Size == 1;
txtObjSize.Text = SNESGraphicsDecoder.ObjSizes[si.OBSEL_Size, oam.Size].ToString();
cbObjLarge.Checked = oam.Size;
txtObjSize.Text = SNESGraphicsDecoder.ObjSizes[si.OBSEL_Size, oam.Size ? 1 : 0].ToString();
txtObjPriority.Text = oam.Priority.ToString();
txtObjPalette.Text = oam.Palette.ToString();
txtObjPaletteMemo.Text = $"${oam.Palette * 16 + 128:X2}";
@ -874,7 +876,7 @@ namespace BizHawk.Client.EmuHawk
if (tp == tpPalette) groupFreeze.Text = "Freeze - Color";
if (tp == tpTile) groupFreeze.Text = "Freeze - Tile";
if (tp == tpOBJ) groupFreeze.Text = "Freeze - OBJ";
groupFreeze.ResumeLayout();
Win32Imports.SendMessage(groupFreeze.Handle, 11, (IntPtr)1, IntPtr.Zero); //WM_SETREDRAW true
@ -981,9 +983,9 @@ namespace BizHawk.Client.EmuHawk
{
//render an obj tile
int tile = currTileDataState.Address / 32;
gd.RenderTilesToScreen((int*)bmpdata.Scan0, 1, 1, bmpdata.Stride / 4, bpp, currPaletteSelection.start, tile, 1);
gd.RenderTilesToScreen((int*)bmpdata.Scan0, bmpdata.Stride / 4, bpp, currPaletteSelection.start, tile, 1);
}
else gd.RenderTilesToScreen((int*)bmpdata.Scan0, 1, 1, bmpdata.Stride / 4, bpp, currPaletteSelection.start, currTileDataState.Tile, 1);
else gd.RenderTilesToScreen((int*)bmpdata.Scan0, bmpdata.Stride / 4, bpp, currPaletteSelection.start, currTileDataState.Tile, 1);
bmp.UnlockBits(bmpdata);
viewerTile.SetBitmap(bmp);
@ -1056,7 +1058,7 @@ namespace BizHawk.Client.EmuHawk
{
int ox = px / si.ObjSizeBounds.Width;
int oy = py / si.ObjSizeBounds.Height;
if (!0.RangeTo(7).Contains(ox) || !0.RangeTo(15).Contains(oy)) return;
int objNum = oy * 8 + ox;
@ -1195,6 +1197,10 @@ namespace BizHawk.Client.EmuHawk
private void checkBackdropColor_CheckedChanged(object sender, EventArgs e)
{
SyncBackdropColor();
if (checkBackdropColor.Checked)
gd?.SetBackColor(DecodeWinformsColorToSNES(pnBackdropColor.BackColor));
else
gd?.SetBackColor();
RegenerateData();
}
@ -1265,13 +1271,21 @@ namespace BizHawk.Client.EmuHawk
if (suppression) return;
var pal = (SnesColors.ColorType)comboPalette.SelectedValue;
Console.WriteLine("set {0}", pal);
var s = Emulator.GetSettings();
s.Palette = pal.ToString();
currentSnesCore?.PutSettings(s);
try
{
currentSnesCore?.SetPalette(pal);
}
catch (NotImplementedException)
{
comboPalette.Enabled = false;
}
RegenerateData();
RenderView();
RenderPalette();
RenderTileView();
using (gd.EnterExit())
{
RenderView();
RenderPalette();
RenderTileView();
}
}
private void RefreshBGENCheckStatesFromConfig()

View File

@ -33,6 +33,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
[BizImport(CallingConvention.Cdecl)]
public abstract void* snes_get_memory_region(int id, out int size, out int wordSize);
[BizImport(CallingConvention.Cdecl)]
public abstract int snes_peek_logical_register(BsnesApi.SNES_REGISTER register);
[BizImport(CallingConvention.Cdecl)]
public abstract byte snes_bus_read(uint address);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_bus_write(uint address, byte value);

View File

@ -2,6 +2,91 @@
{
public partial class BsnesApi
{
public enum SNES_REGISTER {
//$2105
BG_MODE,
BG3_PRIORITY,
BG1_TILESIZE,
BG2_TILESIZE,
BG3_TILESIZE,
BG4_TILESIZE,
//$2107
BG1_SCADDR,
BG1_SCSIZE,
//$2108
BG2_SCADDR,
BG2_SCSIZE,
//$2109,
BG3_SCADDR,
BG3_SCSIZE,
//$210A
BG4_SCADDR,
BG4_SCSIZE,
//$210B
BG1_TDADDR,
BG2_TDADDR,
//$210C
BG3_TDADDR,
BG4_TDADDR,
//$2133 SETINI
SETINI_MODE7_EXTBG,
SETINI_HIRES,
SETINI_OVERSCAN,
SETINI_OBJ_INTERLACE,
SETINI_SCREEN_INTERLACE,
//$2130 CGWSEL
CGWSEL_COLORMASK,
CGWSEL_COLORSUBMASK,
CGWSEL_ADDSUBMODE,
CGWSEL_DIRECTCOLOR,
//$2101 OBSEL
OBSEL_NAMEBASE,
OBSEL_NAMESEL,
OBSEL_SIZE,
//$2131 CGADSUB
CGADDSUB_MODE,
CGADDSUB_HALF,
CGADDSUB_BG4,
CGADDSUB_BG3,
CGADDSUB_BG2,
CGADDSUB_BG1,
CGADDSUB_OBJ,
CGADDSUB_BACKDROP,
//$212C TM
TM_BG1,
TM_BG2,
TM_BG3,
TM_BG4,
TM_OBJ,
//$212D TM
TS_BG1,
TS_BG2,
TS_BG3,
TS_BG4,
TS_OBJ,
//Mode7 regs
M7SEL_REPEAT,
M7SEL_HFLIP,
M7SEL_VFLIP,
M7A,
M7B,
M7C,
M7D,
M7X,
M7Y,
//BG scroll regs
BG1HOFS,
BG1VOFS,
BG2HOFS,
BG2VOFS,
BG3HOFS,
BG3VOFS,
BG4HOFS,
BG4VOFS,
M7HOFS,
M7VOFS
}
public enum SNES_MEMORY
{
CARTRIDGE_RAM,
@ -18,7 +103,7 @@
WRAM,
APURAM,
VRAM,
// OAM, // needs some work in the core probably? or we return an objects pointer
OBJECTS,
CGRAM
}

View File

@ -9,6 +9,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
return _settings.Clone();
}
SNES.IBSNESForGfxDebugger.SettingsObj SNES.IBSNESForGfxDebugger.GetSettings()
=> GetSettings();
public SnesSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
@ -21,6 +24,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
return PutSettingsDirtyBits.None;
}
void SNES.IBSNESForGfxDebugger.PutSettings(SNES.IBSNESForGfxDebugger.SettingsObj s)
=> PutSettings((SnesSettings) s);
public PutSettingsDirtyBits PutSyncSettings(SnesSyncSettings o)
{
bool ret = o.LeftPort != _syncSettings.LeftPort
@ -41,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
private SnesSettings _settings;
private SnesSyncSettings _syncSettings;
public class SnesSettings
public class SnesSettings : SNES.IBSNESForGfxDebugger.SettingsObj
{
public bool ShowBG1_0 { get; set; } = true;
public bool ShowBG2_0 { get; set; } = true;

View File

@ -4,6 +4,7 @@ using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.Base_Implementations;
using BizHawk.Emulation.Cores.Components.W65816;
using BizHawk.Emulation.Cores.Nintendo.SNES;
// http://wiki.superfamicom.org/snes/show/Backgrounds
@ -11,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
[PortedCore(CoreNames.Bsnes115, "bsnes team", "v115+", "https://github.com/bsnes-emu/bsnes")]
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public unsafe partial class BsnesCore : IEmulator, IDebuggable, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings>
public unsafe partial class BsnesCore : IEmulator, IDebuggable, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings>, IBSNESForGfxDebugger
{
[CoreConstructor(VSystemID.Raw.SGB)]
[CoreConstructor(VSystemID.Raw.SNES)]
@ -232,6 +233,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
}
}
public SnesColors.ColorType CurrPalette => SnesColors.ColorType.BSNES;
public void SetPalette(SnesColors.ColorType colorType)
{
if (colorType != SnesColors.ColorType.BSNES)
throw new NotImplementedException("This core does not currently support different palettes.");
}
public ISNESGraphicsDecoder CreateGraphicsDecoder() => new SNESGraphicsDecoder(Api);
public ScanlineHookManager ScanlineHookManager => null;
private void snes_video_refresh(ushort* data, int width, int height, int pitch)
{
int widthMultiplier = 1;

View File

@ -0,0 +1,642 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using static BizHawk.Emulation.Cores.Nintendo.BSNES.BsnesApi.SNES_REGISTER;
using static BizHawk.Emulation.Cores.Nintendo.SNES.SNESGraphicsDecoder;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public sealed unsafe class SNESGraphicsDecoder : ISNESGraphicsDecoder
{
[StructLayout(LayoutKind.Sequential)]
public struct Object // size: 10 bytes; equivalent to the c++ version
{
public readonly ushort x;
public readonly byte y;
public readonly byte character;
public readonly bool nameSelect;
public readonly bool vflip;
public readonly bool hflip;
public readonly byte priority;
public readonly byte palette;
public readonly bool size;
}
private readonly BsnesApi _api;
private readonly byte* vram; // waterbox pointer, ALWAYS access with EnterExit()
private readonly Object* objects; // waterbox pointer, ALWAYS access with EnterExit()
private readonly ushort* cgram; // waterbox pointer, ALWAYS access with EnterExit()
private readonly byte[][] cachedTiles = new byte[5][];
private readonly int[] bppArrayIndex = { 0, 0, 0, 0, 1, 0, 0, 0, 2 };
private bool useBackColor;
private int backColor;
private readonly int[] palette = new int[32768];
private readonly short[] directColorTable = new short[256];
private void generate_palette()
{
const int a = 0xFF;
for (int color = 0; color < 32768; color++) {
int r = (color >> 10) & 31;
int g = (color >> 5) & 31;
int b = (color >> 0) & 31;
r = r << 3 | r >> 2; r = r << 8 | r << 0;
g = g << 3 | g >> 2; g = g << 8 | g << 0;
b = b << 3 | b >> 2; b = b << 8 | b << 0;
palette[color] = a << 24 | b >> 8 << 16 | g >> 8 << 8 | r >> 8 << 0;
}
}
private void generate_directColorTable()
{
for (int i = 0; i < 256; i++)
{
directColorTable[i] = (short)
( (i << 2 & 0x001c) //R
+ (i << 4 & 0x0380) //G
+ (i << 7 & 0x6000) ); //B
}
}
public SNESGraphicsDecoder(BsnesApi api)
{
_api = api;
vram = (byte*)api.core.snes_get_memory_region((int)BsnesApi.SNES_MEMORY.VRAM, out _, out _);
objects = (Object*)api.core.snes_get_memory_region((int)BsnesApi.SNES_MEMORY.OBJECTS, out _, out _);
cgram = (ushort*)api.core.snes_get_memory_region((int)BsnesApi.SNES_MEMORY.CGRAM, out _, out _);
generate_palette();
generate_directColorTable();
}
public void CacheTiles()
{
CacheTiles2Bpp();
CacheTiles_Merge(4);
CacheTiles_Merge(8);
CacheTilesMode7();
CacheTilesMode7ExtBg();
}
private void CacheTiles2Bpp()
{
const int tileCount = 65536 / 8 / 2;
cachedTiles[0] ??= new byte[8 * 8 * tileCount];
for (int i = 0; i < tileCount; i++)
{
int offset = 64 * i;
int addr = 16 * i;
const int stride = 8;
for (int y = 0; y < 8; y++)
{
byte val = vram[addr + 1];
for (int x = 0; x < 8; x++)
cachedTiles[0][offset + y * stride + x] = (byte)(val >> (7 - x) & 1);
val = vram[addr + 0];
for (int x = 0; x < 8; x++)
cachedTiles[0][offset + y * stride + x] = (byte)((cachedTiles[0][offset + y * stride + x] << 1) | (val >> (7 - x) & 1));
addr += 2;
}
}
}
private void CacheTiles_Merge(int toBpp)
{
int shift = toBpp / 2;
int tileCount = 8192 / toBpp;
int destinationIndex = bppArrayIndex[toBpp];
cachedTiles[destinationIndex] ??= new byte[8 * 8 * tileCount];
for (int i = 0; i < tileCount; i++)
{
int srcAddr = 128 * i;
int dstAddr = 64 * i;
for (int p = 0; p < 64; p++)
{
int tileA = cachedTiles[destinationIndex - 1][srcAddr + p];
int tileB = cachedTiles[destinationIndex - 1][srcAddr + p + 64];
cachedTiles[destinationIndex][dstAddr + p] = (byte)(tileA | (tileB << shift));
}
}
}
private void CacheTilesMode7()
{
const int tileCount = 256;
const int pixelCount = 8 * 8 * tileCount;
cachedTiles[3] ??= new byte[pixelCount];
for (int i = 0; i < pixelCount; i++)
cachedTiles[3][i] = vram[2*i + 1];
}
private void CacheTilesMode7ExtBg()
{
const int tileCount = 256;
const int pixelCount = 8 * 8 * tileCount;
cachedTiles[4] ??= new byte[pixelCount];
for (int i = 0; i < pixelCount; i++)
cachedTiles[4][i] = (byte)(cachedTiles[3][i] & 0x7F);
}
public int Colorize(int rgb555)
{
return palette[rgb555];
}
public void Colorize(int* buf, int offset, int numpixels)
{
for (int i = 0; i < numpixels; i++)
{
buf[offset + i] = palette[buf[offset + i]];
}
}
public ISNESGraphicsDecoder.OAMInfo CreateOAMInfo(ScreenInfo si, int num) => new OAMInfo(objects, si, num);
public void DecodeBG(
int* screen,
int stride, TileEntry[] map,
int tiledataBaseAddr, ScreenSize size,
int bpp,
int tilesize,
int paletteStart)
{
//emergency backstop. this can only happen if we're displaying an unavailable BG or other similar such value
if (bpp == 0) return;
int ncolors = 1 << bpp;
var dims = SizeInTilesForBGSize(size);
int count8x8 = tilesize / 8;
int tileSizeBytes = 8 * bpp;
int baseTileNum = tiledataBaseAddr / tileSizeBytes;
byte[] tileCache = cachedTiles[bppArrayIndex[bpp]];
int tileCacheMask = tileCache.Length - 1;
for (int mty = 0; mty < dims.Height; mty++)
for (int mtx = 0; mtx < dims.Width; mtx++)
for (int tx = 0; tx < count8x8; tx++)
for (int ty = 0; ty < count8x8; ty++)
{
int mapIndex = mty * dims.Width + mtx;
var te = map[mapIndex];
//apply metatile flipping
int tnx = tx, tny = ty;
if (tilesize == 16)
{
if ((te.flags & TileEntryFlags.Horz) != 0) tnx = 1 - tnx;
if ((te.flags & TileEntryFlags.Vert) != 0) tny = 1 - tny;
}
int tileNum = te.tilenum + tnx + tny * 16 + baseTileNum;
int srcOfs = tileNum * 64;
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
{
int px = x;
int py = y;
if ((te.flags & TileEntryFlags.Horz) != 0) px = 7 - x;
if ((te.flags & TileEntryFlags.Vert) != 0) py = 7 - y;
int dstX = (mtx * count8x8 + tx) * 8 + px;
int dstY = (mty * count8x8 + ty) * 8 + py;
int dstOfs = dstY * stride + dstX;
int color = tileCache[srcOfs & tileCacheMask];
srcOfs++;
// if (color != 0)
// {
color += te.palette * ncolors;
color += paletteStart;
// }
screen[dstOfs] = color;
}
}
}
public void DecodeMode7BG(int* screen, int stride, bool extBg)
{
byte[] cachedTileBuffer = cachedTiles[extBg ? 4 : 3];
for (int ty = 0, tidx = 0; ty < 128; ty++)
for (int tx = 0; tx < 128; tx++, tidx++)
{
int tileEntry = vram[tidx * 2];
int src = tileEntry * 64;
for (int py = 0; py < 8; py++)
for (int px = 0; px < 8; px++)
{
int dst = (ty * 8 + py) * stride + (tx * 8 + px);
int srcData = cachedTileBuffer[src++];
screen[dst] = srcData;
}
}
}
public void DirectColorify(int* screen, int numPixels)
{
for (int i = 0; i < numPixels; i++)
{
screen[i] = directColorTable[screen[i]];
}
}
public void Enter()
=> _api.Enter();
public void Exit()
=> _api.Exit();
public TileEntry[] FetchMode7Tilemap()
{
var buf = new TileEntry[128*128];
for (int tidx = 0; tidx < 128 * 128; tidx++)
{
buf[tidx].address = tidx * 2;
buf[tidx].tilenum = vram[tidx * 2];
}
return buf;
}
public TileEntry[] FetchTilemap(int addr, ScreenSize size)
{
Dimensions blockDimensions = SizeInBlocksForBGSize(size);
int realWidth = blockDimensions.Width * 32;
int realHeight = blockDimensions.Height * 32;
TileEntry[] buf = new TileEntry[realWidth*realHeight];
for (int by = 0; by < blockDimensions.Height; by++)
for (int bx = 0; bx < blockDimensions.Width; bx++)
for (int y = 0; y < 32; y++)
for (int x = 0; x < 32; x++)
{
int idx = (by * 32 + y) * realWidth + bx * 32 + x;
ushort entry = *(ushort*)(vram + addr);
buf[idx].tilenum = (ushort)(entry & 0x3FF);
buf[idx].palette = (byte)((entry >> 10) & 7);
buf[idx].flags = (TileEntryFlags)((entry >> 13) & 7);
buf[idx].address = addr;
addr += 2;
}
return buf;
}
public int[] GetPalette()
{
int[] ret = new int[256];
for (int i = 0; i < 256; i++)
ret[i] = cgram[i] & 0x7FFF;
return ret;
}
public void Paletteize(int* buf, int offset, int startcolor, int numpixels)
{
for (int i = 0; i < numpixels; i++)
{
int entry = buf[offset + i];
int color = entry == 0 && useBackColor ? backColor : cgram[startcolor + entry] & 0x7FFF;
buf[offset + i] = color;
}
}
public void RenderMode7TilesToScreen(
int* screen,
int stride,
bool ext,
bool directColor,
int tilesWide,
int startTile,
int numTiles)
{
byte[] cachedTileBuffer = cachedTiles[ext ? 4 : 3];
for (int i = 0; i < numTiles; i++)
{
int targetYPos = i / tilesWide * stride * 8;
int targetXPos = i % tilesWide * 8;
int destinationOffset = targetYPos + targetXPos;
int sourceOffset = (startTile + i) * 64;
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
{
screen[destinationOffset + y * stride + x] = cachedTileBuffer[sourceOffset++];
}
}
int numPixels = numTiles * 8 * 8;
if (directColor) DirectColorify(screen, numPixels);
else Paletteize(screen, 0, 0, numPixels);
Colorize(screen, 0, numPixels);
}
public void RenderSpriteToScreen(
int* screen,
int stride,
int destx,
int desty, ScreenInfo si,
int spritenum,
ISNESGraphicsDecoder.OAMInfo oam,
int xlimit,
int ylimit,
byte[,] spriteMap)
{
oam ??= new OAMInfo(objects, si, spritenum);
Size dim = ObjSizes[si.OBSEL_Size, oam.Size ? 1 : 0];
byte[] cachedTileBuffer = cachedTiles[bppArrayIndex[4]];
int baseaddr = oam.Table ? si.OBJTable1Addr : si.OBJTable0Addr;
//TODO - flips of 'undocumented' rectangular oam settings are wrong. probably easy to do right, but we need a test
int bcol = oam.Tile & 0xF;
int brow = (oam.Tile >> 4) & 0xF;
for(int oy=0;oy<dim.Height;oy++)
for (int ox = 0; ox < dim.Width; ox++)
{
int dy, dx;
if (oam.HFlip)
dx = dim.Width - 1 - ox;
else dx = ox;
if (oam.VFlip)
dy = dim.Height - 1 - oy;
else dy = oy;
dx += destx;
dy += desty;
if(dx>=xlimit || dy>=ylimit || dx<0 || dy<0)
continue;
int col = (bcol + (ox >> 3)) & 0xF;
int row = (brow + (oy >> 3)) & 0xF;
int sx = ox & 0x7;
int sy = oy & 0x7;
int addr = baseaddr*2 + (row * 16 + col) * 64;
addr += sy * 8 + sx;
int dofs = stride*dy+dx;
int color = cachedTileBuffer[addr];
if (spriteMap != null && color == 0)
{
//skip transparent pixels
}
else
{
screen[dofs] = color;
Paletteize(screen, dofs, oam.Palette * 16 + 128, 1);
Colorize(screen, dofs, 1);
if (spriteMap != null) spriteMap[dx, dy] = (byte)spritenum;
}
}
}
public void RenderTilesToScreen(
int* screen,
int stride,
int bpp,
int startcolor,
int startTile,
int numTiles)
{
if (numTiles == -1)
numTiles = 8192 / bpp;
byte[] cachedTileBuffer = cachedTiles[bppArrayIndex[bpp]];
int tilesPerRow = stride / 8;
for (int i = 0; i < numTiles; i++)
{
int targetYPos = i / tilesPerRow * stride * 8;
int targetXPos = i % tilesPerRow * 8;
int destinationOffset = targetYPos + targetXPos;
int sourceOffset = (startTile + i) * 64;
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
{
screen[destinationOffset + y * stride + x] = cachedTileBuffer[sourceOffset++];
}
}
int numPixels = numTiles * 8 * 8;
Paletteize(screen, 0, startcolor, numPixels);
Colorize(screen, 0, numPixels);
}
public ScreenInfo ScanScreenInfo()
{
int OBSEL_NameSel = _api.core.snes_peek_logical_register(OBSEL_NAMESEL);
int OBSEL_NameBase = _api.core.snes_peek_logical_register(OBSEL_NAMEBASE);
ScreenInfo screenInfo = new()
{
Mode = _api.core.snes_peek_logical_register(BG_MODE),
Mode1_BG3_Priority = _api.core.snes_peek_logical_register(BG3_PRIORITY) == 1,
SETINI_Mode7ExtBG = _api.core.snes_peek_logical_register(SETINI_MODE7_EXTBG) == 1,
SETINI_HiRes = _api.core.snes_peek_logical_register(SETINI_HIRES) == 1,
SETINI_Overscan = _api.core.snes_peek_logical_register(SETINI_OVERSCAN) == 1,
SETINI_ObjInterlace = _api.core.snes_peek_logical_register(SETINI_OBJ_INTERLACE) == 1,
SETINI_ScreenInterlace = _api.core.snes_peek_logical_register(SETINI_SCREEN_INTERLACE) == 1,
CGWSEL_ColorMask = _api.core.snes_peek_logical_register(CGWSEL_COLORMASK),
CGWSEL_ColorSubMask = _api.core.snes_peek_logical_register(CGWSEL_COLORSUBMASK),
CGWSEL_AddSubMode = _api.core.snes_peek_logical_register(CGWSEL_ADDSUBMODE),
CGWSEL_DirectColor = _api.core.snes_peek_logical_register(CGWSEL_DIRECTCOLOR) == 1,
CGADSUB_AddSub = _api.core.snes_peek_logical_register(CGADDSUB_MODE),
CGADSUB_Half = _api.core.snes_peek_logical_register(CGADDSUB_HALF) == 1,
OBSEL_Size = _api.core.snes_peek_logical_register(OBSEL_SIZE),
OBSEL_NameSel = OBSEL_NameSel,
OBSEL_NameBase = OBSEL_NameBase,
OBJTable0Addr = OBSEL_NameBase << 14,
OBJTable1Addr = ((OBSEL_NameBase << 14) + ((OBSEL_NameSel + 1) << 13)) & 0xFFFF,
OBJ_MainEnabled = _api.core.snes_peek_logical_register(TM_OBJ) == 1,
OBJ_SubEnabled = _api.core.snes_peek_logical_register(TS_OBJ) == 1,
OBJ_MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_OBJ) == 1,
BK_MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_BACKDROP) == 1,
M7HOFS = _api.core.snes_peek_logical_register(M7HOFS),
M7VOFS = _api.core.snes_peek_logical_register(M7VOFS),
M7A = _api.core.snes_peek_logical_register(M7A),
M7B = _api.core.snes_peek_logical_register(M7B),
M7C = _api.core.snes_peek_logical_register(M7C),
M7D = _api.core.snes_peek_logical_register(M7D),
M7X = _api.core.snes_peek_logical_register(M7X),
M7Y = _api.core.snes_peek_logical_register(M7Y),
M7SEL_REPEAT = _api.core.snes_peek_logical_register(M7SEL_REPEAT),
M7SEL_HFLIP = _api.core.snes_peek_logical_register(M7SEL_HFLIP) == 1,
M7SEL_VFLIP = _api.core.snes_peek_logical_register(M7SEL_VFLIP) == 1
};
screenInfo.ObjSizeBounds = ObjSizes[screenInfo.OBSEL_Size,1];
int square = Math.Max(screenInfo.ObjSizeBounds.Width, screenInfo.ObjSizeBounds.Height);
screenInfo.ObjSizeBoundsSquare = new Size(square, square);
screenInfo.BG.BG1.Bpp = ModeBpps[screenInfo.Mode, 0];
screenInfo.BG.BG2.Bpp = ModeBpps[screenInfo.Mode, 1];
screenInfo.BG.BG3.Bpp = ModeBpps[screenInfo.Mode, 2];
screenInfo.BG.BG4.Bpp = ModeBpps[screenInfo.Mode, 3];
//initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later)
for (int i = 1; i <= 4; i++)
screenInfo.BG[i].BGMode = screenInfo.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
screenInfo.BG.BG1.TILESIZE = _api.core.snes_peek_logical_register(BG1_TILESIZE);
screenInfo.BG.BG2.TILESIZE = _api.core.snes_peek_logical_register(BG2_TILESIZE);
screenInfo.BG.BG3.TILESIZE = _api.core.snes_peek_logical_register(BG3_TILESIZE);
screenInfo.BG.BG4.TILESIZE = _api.core.snes_peek_logical_register(BG4_TILESIZE);
screenInfo.BG.BG1.SCSIZE = _api.core.snes_peek_logical_register(BG1_SCSIZE);
screenInfo.BG.BG2.SCSIZE = _api.core.snes_peek_logical_register(BG2_SCSIZE);
screenInfo.BG.BG3.SCSIZE = _api.core.snes_peek_logical_register(BG3_SCSIZE);
screenInfo.BG.BG4.SCSIZE = _api.core.snes_peek_logical_register(BG4_SCSIZE);
screenInfo.BG.BG1.SCADDR = _api.core.snes_peek_logical_register(BG1_SCADDR);
screenInfo.BG.BG2.SCADDR = _api.core.snes_peek_logical_register(BG2_SCADDR);
screenInfo.BG.BG3.SCADDR = _api.core.snes_peek_logical_register(BG3_SCADDR);
screenInfo.BG.BG4.SCADDR = _api.core.snes_peek_logical_register(BG4_SCADDR);
screenInfo.BG.BG1.TDADDR = _api.core.snes_peek_logical_register(BG1_TDADDR);
screenInfo.BG.BG2.TDADDR = _api.core.snes_peek_logical_register(BG2_TDADDR);
screenInfo.BG.BG3.TDADDR = _api.core.snes_peek_logical_register(BG3_TDADDR);
screenInfo.BG.BG4.TDADDR = _api.core.snes_peek_logical_register(BG4_TDADDR);
screenInfo.BG.BG1.MainEnabled = _api.core.snes_peek_logical_register(TM_BG1) == 1;
screenInfo.BG.BG2.MainEnabled = _api.core.snes_peek_logical_register(TM_BG2) == 1;
screenInfo.BG.BG3.MainEnabled = _api.core.snes_peek_logical_register(TM_BG3) == 1;
screenInfo.BG.BG4.MainEnabled = _api.core.snes_peek_logical_register(TM_BG4) == 1;
screenInfo.BG.BG1.SubEnabled = _api.core.snes_peek_logical_register(TS_BG1) == 1;
screenInfo.BG.BG2.SubEnabled = _api.core.snes_peek_logical_register(TS_BG2) == 1;
screenInfo.BG.BG3.SubEnabled = _api.core.snes_peek_logical_register(TS_BG3) == 1;
screenInfo.BG.BG4.SubEnabled = _api.core.snes_peek_logical_register(TS_BG4) == 1;
screenInfo.BG.BG1.MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_BG1) == 1;
screenInfo.BG.BG2.MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_BG2) == 1;
screenInfo.BG.BG3.MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_BG3) == 1;
screenInfo.BG.BG4.MathEnabled = _api.core.snes_peek_logical_register(CGADDSUB_BG4) == 1;
screenInfo.BG.BG1.HOFS = _api.core.snes_peek_logical_register(BG1HOFS);
screenInfo.BG.BG1.VOFS = _api.core.snes_peek_logical_register(BG1VOFS);
screenInfo.BG.BG2.HOFS = _api.core.snes_peek_logical_register(BG2HOFS);
screenInfo.BG.BG2.VOFS = _api.core.snes_peek_logical_register(BG2VOFS);
screenInfo.BG.BG3.HOFS = _api.core.snes_peek_logical_register(BG3HOFS);
screenInfo.BG.BG3.VOFS = _api.core.snes_peek_logical_register(BG3VOFS);
screenInfo.BG.BG4.HOFS = _api.core.snes_peek_logical_register(BG4HOFS);
screenInfo.BG.BG4.VOFS = _api.core.snes_peek_logical_register(BG4VOFS);
for (int i = 1; i <= 4; i++)
{
screenInfo.BG[i].Mode = screenInfo.Mode;
screenInfo.BG[i].TiledataAddr = screenInfo.BG[i].TDADDR << 13;
screenInfo.BG[i].ScreenAddr = screenInfo.BG[i].SCADDR << 9;
}
//fixup irregular things for mode 7
if (screenInfo.Mode == 7)
{
screenInfo.BG.BG1.TiledataAddr = 0;
screenInfo.BG.BG1.ScreenAddr = 0;
screenInfo.BG.BG1.BGMode = screenInfo.CGWSEL_DirectColor ? BGMode.Mode7DC : BGMode.Mode7;
if (screenInfo.SETINI_Mode7ExtBG)
{
screenInfo.BG.BG2.BGMode = BGMode.Mode7Ext;
screenInfo.BG.BG2.Bpp = 7;
screenInfo.BG.BG2.TiledataAddr = 0;
screenInfo.BG.BG2.ScreenAddr = 0;
}
}
//determine which colors each BG could use
switch (screenInfo.Mode)
{
case 0:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 32);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(32, 32);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(64, 32);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(96, 32);
break;
case 1:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(0, 32);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 2:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 3:
case 7:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 4:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 5:
case 6:
screenInfo.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
screenInfo.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
screenInfo.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
screenInfo.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
}
return screenInfo;
}
public void SetBackColor(int snescol)
{
if (snescol == -1)
{
useBackColor = false;
}
else
{
useBackColor = true;
backColor = snescol;
}
}
}
internal class OAMInfo : ISNESGraphicsDecoder.OAMInfo
{
public ushort X { get; }
public byte Y { get; }
public int Tile { get; }
public bool Table { get; }
public int Palette { get; }
public byte Priority { get; }
public bool VFlip { get; }
public bool HFlip { get; }
public bool Size { get; }
public int Address { get; }
public unsafe OAMInfo(SNESGraphicsDecoder.Object* objects, ScreenInfo si, int index)
{
X = objects[index].x;
Y = objects[index].y;
Table = objects[index].nameSelect;
Palette = objects[index].palette;
Priority = objects[index].priority;
HFlip = objects[index].hflip;
VFlip = objects[index].vflip;
Size = objects[index].size;
byte character = objects[index].character;
Tile = character + (Table ? 256 : 0);
Address = character * 32 + (Table ? si.OBJTable1Addr : si.OBJTable0Addr);
Address &= 0xFFFF;
}
}
}

View File

@ -1,4 +1,5 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.SNES;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
@ -28,18 +29,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
ser.Register(_bsnesCore.ServiceProvider.GetService<IMemoryDomains>());
ser.Register(_bsnesCore.ServiceProvider.GetService<IDisassemblable>());
ser.Register(_bsnesCore.ServiceProvider.GetService<ITraceable>());
ser.Register(_bsnesCore.ServiceProvider.GetService<IBSNESForGfxDebugger>());
if (IsSGB)
{
// board info is only set in SGB mode
ser.Register(_bsnesCore.ServiceProvider.GetService<IBoardInfo>());
{
// board info is only set in SGB mode
ser.Register(_bsnesCore.ServiceProvider.GetService<IBoardInfo>());
}
ServiceProvider = ser;
}
private readonly BsnesCore _bsnesCore;
public bool IsSGB => _bsnesCore.IsSGB;
public bool IsSGB => _bsnesCore.IsSGB;
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition => _bsnesCore.ControllerDefinition;
@ -64,20 +66,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
}
if (resetSignal)
{
_bsnesCore.Api.core.snes_reset();
}
if (powerSignal)
{
_bsnesCore.Api.core.snes_power();
{
_bsnesCore.Api.core.snes_reset();
}
if (powerSignal)
{
_bsnesCore.Api.core.snes_power();
}
}
else
{
// run the core for one (sub-)frame
bool subFrameRequested = controller.IsPressed("Subframe");
framePassed = _bsnesCore.Api.core.snes_run(subFrameRequested);
{
// run the core for one (sub-)frame
bool subFrameRequested = controller.IsPressed("Subframe");
framePassed = _bsnesCore.Api.core.snes_run(subFrameRequested);
}
if (!framePassed) _bsnesCore.IsLagFrame = false;

View File

@ -0,0 +1,48 @@
#nullable enable
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
public interface IBSNESForGfxDebugger : ISpecializedEmulatorService
{
public interface SettingsObj
{
bool ShowBG1_0 { get; set; }
bool ShowBG1_1 { get; set; }
bool ShowBG2_0 { get; set; }
bool ShowBG2_1 { get; set; }
bool ShowBG3_0 { get; set; }
bool ShowBG3_1 { get; set; }
bool ShowBG4_0 { get; set; }
bool ShowBG4_1 { get; set; }
bool ShowOBJ_0 { get; set; }
bool ShowOBJ_1 { get; set; }
bool ShowOBJ_2 { get; set; }
bool ShowOBJ_3 { get; set; }
}
SnesColors.ColorType CurrPalette { get; }
ScanlineHookManager? ScanlineHookManager { get; }
ISNESGraphicsDecoder CreateGraphicsDecoder();
SettingsObj GetSettings();
void PutSettings(SettingsObj s);
void SetPalette(SnesColors.ColorType palette);
}
}

View File

@ -9,6 +9,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
return _settings.Clone();
}
IBSNESForGfxDebugger.SettingsObj IBSNESForGfxDebugger.GetSettings()
=> GetSettings();
public SnesSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
@ -26,6 +29,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
return PutSettingsDirtyBits.None;
}
void IBSNESForGfxDebugger.PutSettings(IBSNESForGfxDebugger.SettingsObj s)
=> PutSettings((SnesSettings) s);
public PutSettingsDirtyBits PutSyncSettings(SnesSyncSettings o)
{
bool ret = o.LeftPort != _syncSettings.LeftPort
@ -40,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
private SnesSettings _settings;
private SnesSyncSettings _syncSettings;
public class SnesSettings
public class SnesSettings : IBSNESForGfxDebugger.SettingsObj
{
public bool ShowBG1_0 { get; set; } = true;
public bool ShowBG2_0 { get; set; } = true;

View File

@ -20,7 +20,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
[PortedCore(CoreNames.Bsnes, "byuu", "v87", "https://github.com/bsnes-emu/bsnes/tree/386ac87d21d14fafd15162d480a111209c9955ba")]
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public unsafe partial class LibsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ICodeDataLogger,
IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>
IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>, IBSNESForGfxDebugger
{
[CoreConstructor(VSystemID.Raw.SGB)]
[CoreConstructor(VSystemID.Raw.SNES)]
@ -235,7 +235,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public SnesColors.ColorType CurrPalette { get; private set; }
public MyScanlineHookManager ScanlineHookManager { get; }
public void SetPalette(SnesColors.ColorType palette)
{
var s = GetSettings();
s.Palette = Enum.GetName(typeof(SnesColors.ColorType), palette);
PutSettings(s);
}
public ISNESGraphicsDecoder CreateGraphicsDecoder()
=> new SNESGraphicsDecoder(Api, CurrPalette);
public ScanlineHookManager ScanlineHookManager { get; }
public class MyScanlineHookManager : ScanlineHookManager
{
@ -364,14 +374,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
}
private void SetPalette(SnesColors.ColorType pal)
{
CurrPalette = pal;
int[] tmp = SnesColors.GetLUT(pal);
fixed (int* p = &tmp[0])
Api.QUERY_set_color_lut((IntPtr)p);
}
private void ReadHook(uint addr)
{
if (MemoryCallbacks.HasReads)
@ -633,7 +635,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
private void RefreshPalette()
{
SetPalette((SnesColors.ColorType)Enum.Parse(typeof(SnesColors.ColorType), _settings.Palette, false));
CurrPalette = (SnesColors.ColorType)Enum.Parse(typeof(SnesColors.ColorType), _settings.Palette, false);
int[] tmp = SnesColors.GetLUT(CurrPalette);
fixed (int* p = &tmp[0])
Api.QUERY_set_color_lut((IntPtr)p);
}
}
}

View File

@ -10,10 +10,100 @@
using BizHawk.Common;
using System;
using System.Drawing;
namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
public unsafe class SNESGraphicsDecoder : IDisposable, IMonitor
public unsafe interface ISNESGraphicsDecoder : IMonitor
{
public interface OAMInfo
{
ushort X { get; }
byte Y { get; }
int Tile { get; }
bool Table { get; }
int Palette { get; }
byte Priority { get; }
bool VFlip { get; }
bool HFlip { get; }
bool Size { get; }
int Address { get; }
}
void CacheTiles();
int Colorize(int rgb555);
void Colorize(int* buf, int offset, int numpixels);
OAMInfo CreateOAMInfo(SNESGraphicsDecoder.ScreenInfo si, int num);
void DecodeBG(
int* screen,
int stride,
SNESGraphicsDecoder.TileEntry[] map,
int tiledataBaseAddr,
SNESGraphicsDecoder.ScreenSize size,
int bpp,
int tilesize,
int paletteStart);
void DecodeMode7BG(int* screen, int stride, bool extBg);
void DirectColorify(int* screen, int numPixels);
SNESGraphicsDecoder.TileEntry[] FetchMode7Tilemap();
SNESGraphicsDecoder.TileEntry[] FetchTilemap(int addr, SNESGraphicsDecoder.ScreenSize size);
int[] GetPalette();
void Paletteize(int* buf, int offset, int startcolor, int numpixels);
void RenderMode7TilesToScreen(
int* screen,
int stride,
bool ext,
bool directColor,
int tilesWide = 16,
int startTile = 0,
int numTiles = 256);
void RenderSpriteToScreen(
int* screen,
int stride,
int destx,
int desty,
SNESGraphicsDecoder.ScreenInfo si,
int spritenum,
OAMInfo oam = null,
int xlimit = 1024,
int ylimit = 1024,
byte[,] spriteMap = null);
void RenderTilesToScreen(
int* screen,
int stride,
int bpp,
int startcolor,
int startTile = 0,
int numTiles = -1);
SNESGraphicsDecoder.ScreenInfo ScanScreenInfo();
void SetBackColor(int snescol = -1);
}
public unsafe class SNESGraphicsDecoder : ISNESGraphicsDecoder
{
public class PaletteSelection
{
@ -48,16 +138,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
return ret;
}
public static Dimensions[,] ObjSizes =
public static Size[,] ObjSizes =
{
{ new Dimensions(8,8), new Dimensions(16,16) },
{ new Dimensions(8,8), new Dimensions(32,32) },
{ new Dimensions(8,8), new Dimensions(64,64) },
{ new Dimensions(16,16), new Dimensions(32,32) },
{ new Dimensions(16,16), new Dimensions(64,64) },
{ new Dimensions(32,32), new Dimensions(64,64) },
{ new Dimensions(16,32), new Dimensions(32,64) },
{ new Dimensions(16,32), new Dimensions(32,32) }
{ new(8,8), new(16,16) },
{ new(8,8), new(32,32) },
{ new(8,8), new(64,64) },
{ new(16,16), new(32,32) },
{ new(16,16), new(64,64) },
{ new(32,32), new(64,64) },
{ new(16,32), new(32,64) },
{ new(16,32), new(32,32) }
};
public static Dimensions SizeInBlocksForBGSize(ScreenSize size)
@ -199,27 +289,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public BGInfo this[int index] => bgs[index - 1];
}
public class ModeInfo
{
/// <summary>
/// the mode number, i.e. Mode 7
/// </summary>
public int MODE;
}
public class OAMInfo
public class OAMInfo : ISNESGraphicsDecoder.OAMInfo
{
public int Index { get; }
public int X { get; }
public int Y { get; }
public ushort X { get; }
public byte Y { get; }
public int Tile { get; }
public int Name { get; }
public int Table { get; }
public bool Table { get; }
public int Palette { get; }
public int Priority { get; }
public byte Priority { get; }
public bool VFlip { get; }
public bool HFlip { get; }
public int Size { get; }
public bool Size { get; }
/// <summary>
/// tiledata address
@ -229,15 +310,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public OAMInfo(SNESGraphicsDecoder dec, ScreenInfo si, int num)
{
Index = num;
int lowaddr = num*4;
X = dec.oam[lowaddr++];
Y = dec.oam[lowaddr++];
Name = dec.oam[lowaddr++];
Table = dec.oam[lowaddr] & 1;
byte name = dec.oam[lowaddr++];
Table = (dec.oam[lowaddr] & 1) == 1;
Palette = (dec.oam[lowaddr]>>1) & 7;
Priority = (dec.oam[lowaddr] >> 4) & 3;
HFlip = ((dec.oam[lowaddr] >> 6) & 1)==1;
Priority = (byte)((dec.oam[lowaddr] >> 4) & 3);
HFlip = ((dec.oam[lowaddr] >> 6) & 1) == 1;
VFlip = ((dec.oam[lowaddr] >> 7) & 1) == 1;
int highaddr = num / 4;
@ -246,13 +327,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
high >>= shift;
int x = high & 1;
high >>= 1;
Size = high & 1;
X |= (x << 8);
X = (X << 23) >> 23;
Size = (high & 1) != 0;
X = (ushort)(X | (x << 8));
Tile = Table*256 + Name;
Tile = name + (Table ? 256 : 0);
Address = 32 * Tile;
if (Tile < 256)
Address += si.OBJTable0Addr;
else
@ -264,240 +344,53 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public class ScreenInfo
{
public Dimensions ObjSizeBounds;
public Dimensions ObjSizeBoundsSquare;
public Size ObjSizeBounds;
public Size ObjSizeBoundsSquare;
public BGInfos BG = new BGInfos();
public ModeInfo Mode = new ModeInfo();
public int Mode { get; init; }
public bool Mode1_BG3_Priority { get; init; }
public bool Mode1_BG3_Priority { get; private set; }
public bool SETINI_Mode7ExtBG { get; init; }
public bool SETINI_HiRes { get; init; }
public bool SETINI_Overscan { get; init; }
public bool SETINI_ObjInterlace { get; init; }
public bool SETINI_ScreenInterlace { get; init; }
public bool SETINI_Mode7ExtBG { get; private set; }
public bool SETINI_HiRes { get; private set; }
public bool SETINI_Overscan { get; private set; }
public bool SETINI_ObjInterlace { get; private set; }
public bool SETINI_ScreenInterlace { get; private set; }
public int CGWSEL_ColorMask { get; init; }
public int CGWSEL_ColorSubMask { get; init; }
public int CGWSEL_AddSubMode { get; init; }
public bool CGWSEL_DirectColor { get; init; }
public int CGADSUB_AddSub { get; init; }
public bool CGADSUB_Half { get; init; }
public int CGWSEL_ColorMask { get; private set; }
public int CGWSEL_ColorSubMask { get; private set; }
public int CGWSEL_AddSubMode { get; private set; }
public bool CGWSEL_DirectColor { get; private set; }
public int CGADSUB_AddSub { get; private set; }
public bool CGADSUB_Half { get; private set; }
public int OBSEL_Size { get; init; }
public int OBSEL_NameSel { get; init; }
public int OBSEL_NameBase { get; init; }
public int OBSEL_Size { get; private set; }
public int OBSEL_NameSel { get; private set; }
public int OBSEL_NameBase { get; private set; }
public int OBJTable0Addr { get; init; }
public int OBJTable1Addr { get; init; }
public int OBJTable0Addr { get; private set; }
public int OBJTable1Addr { get; private set; }
public bool OBJ_MainEnabled { get; init; }
public bool OBJ_SubEnabled { get; init; }
public bool OBJ_MathEnabled { get; init; }
public bool BK_MathEnabled { get; init; }
public bool OBJ_MainEnabled { get; private set; }
public bool OBJ_SubEnabled { get; private set; }
public bool OBJ_MathEnabled { get; private set; }
public bool BK_MathEnabled { get; private set; }
public int M7HOFS { get; private set; }
public int M7VOFS { get; private set; }
public int M7A { get; private set; }
public int M7B { get; private set; }
public int M7C { get; private set; }
public int M7D { get; private set; }
public int M7X { get; private set; }
public int M7Y { get; private set; }
public int M7SEL_REPEAT { get; private set; }
public bool M7SEL_HFLIP { get; private set; }
public bool M7SEL_VFLIP { get; private set; }
public static ScreenInfo GetScreenInfo(LibsnesApi api)
{
var si = new ScreenInfo();
si.Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1;
si.OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE);
si.OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
si.OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1];
int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height);
si.ObjSizeBoundsSquare = new Dimensions(square, square);
si.OBJTable0Addr = si.OBSEL_NameBase << 14;
si.OBJTable1Addr = (si.OBJTable0Addr + ((si.OBSEL_NameSel + 1) << 13)) & 0xFFFF;
si.SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1;
si.SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1;
si.SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1;
si.SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1;
si.SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1;
si.CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK);
si.CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK);
si.CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE);
si.CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1;
si.CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE);
si.CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1;
si.OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1;
si.OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1;
si.OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1;
si.BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1;
si.Mode.MODE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE);
si.BG.BG1.Bpp = ModeBpps[si.Mode.MODE, 0];
si.BG.BG2.Bpp = ModeBpps[si.Mode.MODE, 1];
si.BG.BG3.Bpp = ModeBpps[si.Mode.MODE, 2];
si.BG.BG4.Bpp = ModeBpps[si.Mode.MODE, 3];
//initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later)
for(int i=1;i<=4;i++)
si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
si.M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS);
si.M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS);
si.M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A);
si.M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B);
si.M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C);
si.M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D);
si.M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X);
si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
si.M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT);
si.M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0;
si.M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0;
for (int i = 1; i <= 4; i++)
{
si.BG[i].Mode = si.Mode.MODE;
si.BG[i].TiledataAddr = si.BG[i].TDADDR << 13;
si.BG[i].ScreenAddr = si.BG[i].SCADDR << 9;
}
//fixup irregular things for mode 7
if (si.Mode.MODE == 7)
{
si.BG.BG1.TiledataAddr = 0;
si.BG.BG1.ScreenAddr = 0;
if (si.CGWSEL_DirectColor)
{
si.BG.BG1.BGMode = BGMode.Mode7DC;
}
else
si.BG.BG1.BGMode = BGMode.Mode7;
if (si.SETINI_Mode7ExtBG)
{
si.BG.BG2.BGMode = BGMode.Mode7Ext;
si.BG.BG2.Bpp = 7;
si.BG.BG2.TiledataAddr = 0;
si.BG.BG2.ScreenAddr = 0;
}
}
//determine which colors each BG could use
switch (si.Mode.MODE)
{
case 0:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG2.PaletteSelection = new PaletteSelection(32, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(64, 32);
si.BG.BG4.PaletteSelection = new PaletteSelection(96, 32);
break;
case 1:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 2:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 3:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 4:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 5:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 6:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 7:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
}
return si;
}
public int M7HOFS { get; init; }
public int M7VOFS { get; init; }
public int M7A { get; init; }
public int M7B { get; init; }
public int M7C { get; init; }
public int M7D { get; init; }
public int M7X { get; init; }
public int M7Y { get; init; }
public int M7SEL_REPEAT { get; init; }
public bool M7SEL_HFLIP { get; init; }
public bool M7SEL_VFLIP { get; init; }
}
private static readonly int[,] ModeBpps = {
public static readonly int[,] ModeBpps = {
{2,2,2,2},
{4,4,2,0},
{4,4,0,0},
@ -512,7 +405,182 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public ScreenInfo ScanScreenInfo()
{
return ScreenInfo.GetScreenInfo(api);
int OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
int OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
var si = new ScreenInfo
{
Mode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE),
Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1,
OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE),
OBSEL_NameSel = OBSEL_NameSel,
OBSEL_NameBase = OBSEL_NameBase,
OBJTable0Addr = OBSEL_NameBase << 14,
OBJTable1Addr = ((OBSEL_NameBase << 14) + ((OBSEL_NameSel + 1) << 13)) & 0xFFFF,
SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1,
SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1,
SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1,
SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1,
SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1,
CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK),
CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK),
CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE),
CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1,
CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE),
CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1,
OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1,
OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1,
OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1,
BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1,
M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS),
M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS),
M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A),
M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B),
M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C),
M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D),
M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X),
M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y),
M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT),
M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0,
M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0,
};
si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1];
int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height);
si.ObjSizeBoundsSquare = new Size(square, square);
si.BG.BG1.Bpp = ModeBpps[si.Mode, 0];
si.BG.BG2.Bpp = ModeBpps[si.Mode, 1];
si.BG.BG3.Bpp = ModeBpps[si.Mode, 2];
si.BG.BG4.Bpp = ModeBpps[si.Mode, 3];
//initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later)
for(int i=1;i<=4;i++)
si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
for (int i = 1; i <= 4; i++)
{
si.BG[i].Mode = si.Mode;
si.BG[i].TiledataAddr = si.BG[i].TDADDR << 13;
si.BG[i].ScreenAddr = si.BG[i].SCADDR << 9;
}
//fixup irregular things for mode 7
if (si.Mode == 7)
{
si.BG.BG1.TiledataAddr = 0;
si.BG.BG1.ScreenAddr = 0;
if (si.CGWSEL_DirectColor)
{
si.BG.BG1.BGMode = BGMode.Mode7DC;
}
else
si.BG.BG1.BGMode = BGMode.Mode7;
if (si.SETINI_Mode7ExtBG)
{
si.BG.BG2.BGMode = BGMode.Mode7Ext;
si.BG.BG2.Bpp = 7;
si.BG.BG2.TiledataAddr = 0;
si.BG.BG2.ScreenAddr = 0;
}
}
//determine which colors each BG could use
switch (si.Mode)
{
case 0:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG2.PaletteSelection = new PaletteSelection(32, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(64, 32);
si.BG.BG4.PaletteSelection = new PaletteSelection(96, 32);
break;
case 1:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 2:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 3:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 4:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 5:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 6:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
case 7:
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
break;
}
return si;
}
//the same basic color table that libsnes uses to convert from snes 555 to rgba32
@ -565,11 +633,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public int address;
}
[Flags]
public enum TileEntryFlags : byte
{
None = 0, Priority = 1, Horz = 2, Vert = 4,
}
public ISNESGraphicsDecoder.OAMInfo CreateOAMInfo(ScreenInfo si, int num)
=> new OAMInfo(this, si, num);
/// <summary>
/// decodes a mode7 BG. youll still need to paletteize and colorize it.
/// </summary>
@ -650,7 +722,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
int mapIndex = mty * dims.Width + mtx;
var te = map[mapIndex];
//apply metatile flipping
int tnx = tx, tny = ty;
if (tilesize == 16)
@ -766,13 +838,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
private readonly int[][] _tileCache = new int[18][];
private bool usingUserBackColor = false;
private bool usingUserBackColor;
private int userBackColor;
public void SetBackColor(int snescol)
{
usingUserBackColor = true;
userBackColor = snescol;
if (snescol == -1)
{
usingUserBackColor = false;
}
else
{
usingUserBackColor = true;
userBackColor = snescol;
}
}
/// <summary>
@ -909,11 +988,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
/// we might need 16x16 unscrambling and some other perks here eventually.
/// provide a start color to use as the basis for the palette
/// </summary>
public void RenderTilesToScreen(int* screen, int tilesWide, int tilesTall, int stride, int bpp, int startcolor, int startTile = 0, int numTiles = -1, bool descramble16 = false)
public void RenderTilesToScreen(int* screen, int stride, int bpp, int startcolor, int startTile = 0, int numTiles = -1)
{
if (numTiles == -1)
numTiles = 8192 / bpp;
int[] tilebuf = _tileCache[bpp];
int tilesWide = stride / 8;
for (int i = 0; i < numTiles; i++)
{
int tnum = startTile + i;
@ -935,20 +1015,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
public void RenderSpriteToScreen(int* screen, int stride, int destx, int desty, ScreenInfo si, int spritenum, OAMInfo oam = null, int xlimit = 1024, int ylimit = 1024, byte[,] spriteMap = null)
public void RenderSpriteToScreen(int* screen, int stride, int destx, int desty, ScreenInfo si, int spritenum, ISNESGraphicsDecoder.OAMInfo oam = null, int xlimit = 1024, int ylimit = 1024, byte[,] spriteMap = null)
{
var dims = new[] { SNESGraphicsDecoder.ObjSizes[si.OBSEL_Size, 0], SNESGraphicsDecoder.ObjSizes[si.OBSEL_Size, 1] };
if(oam == null)
oam = new OAMInfo(this, si, spritenum);
var dim = dims[oam.Size];
oam ??= new OAMInfo(this, si, spritenum);
var dim = ObjSizes[si.OBSEL_Size, oam.Size ? 1 : 0];
int[] tilebuf = _tileCache[4];
int baseaddr;
if (oam.Table == 0)
baseaddr = si.OBJTable0Addr;
else
baseaddr = si.OBJTable1Addr;
int baseaddr = oam.Table ? si.OBJTable1Addr : si.OBJTable0Addr;
//TODO - flips of 'undocumented' rectangular oam settings are wrong. probably easy to do right, but we need a test

View File

@ -11,8 +11,7 @@ using namespace SuperFamicom;
//zero 05-sep-2012
// currently unused; was only used in the graphics debugger as far as i can see
int snes_peek_logical_register(int reg)
EXPORT int snes_peek_logical_register(SNES_REGISTER reg)
{
if (SuperFamicom::system.fastPPU())
switch(reg)
@ -21,88 +20,88 @@ int snes_peek_logical_register(int reg)
// 3-may-2021 above timestamp left for reference because i like it
//$2105
case SNES_REG_BG_MODE: return ppufast.io.bgMode;
case SNES_REG_BG3_PRIORITY: return ppufast.io.bgPriority;
case SNES_REG_BG1_TILESIZE: return ppufast.io.bg1.tileSize;
case SNES_REG_BG2_TILESIZE: return ppufast.io.bg2.tileSize;
case SNES_REG_BG3_TILESIZE: return ppufast.io.bg3.tileSize;
case SNES_REG_BG4_TILESIZE: return ppufast.io.bg4.tileSize;
case BG_MODE: return ppufast.io.bgMode;
case BG3_PRIORITY: return ppufast.io.bgPriority;
case BG1_TILESIZE: return ppufast.io.bg1.tileSize;
case BG2_TILESIZE: return ppufast.io.bg2.tileSize;
case BG3_TILESIZE: return ppufast.io.bg3.tileSize;
case BG4_TILESIZE: return ppufast.io.bg4.tileSize;
//$2107
case SNES_REG_BG1_SCADDR: return ppufast.io.bg1.screenAddress >> 8;
case SNES_REG_BG1_SCSIZE: return ppufast.io.bg1.screenSize;
case BG1_SCADDR: return ppufast.io.bg1.screenAddress >> 8;
case BG1_SCSIZE: return ppufast.io.bg1.screenSize;
//$2108
case SNES_REG_BG2_SCADDR: return ppufast.io.bg2.screenAddress >> 8;
case SNES_REG_BG2_SCSIZE: return ppufast.io.bg2.screenSize;
case BG2_SCADDR: return ppufast.io.bg2.screenAddress >> 8;
case BG2_SCSIZE: return ppufast.io.bg2.screenSize;
//$2109
case SNES_REG_BG3_SCADDR: return ppufast.io.bg3.screenAddress >> 8;
case SNES_REG_BG3_SCSIZE: return ppufast.io.bg3.screenSize;
case BG3_SCADDR: return ppufast.io.bg3.screenAddress >> 8;
case BG3_SCSIZE: return ppufast.io.bg3.screenSize;
//$210A
case SNES_REG_BG4_SCADDR: return ppufast.io.bg4.screenAddress >> 8;
case SNES_REG_BG4_SCSIZE: return ppufast.io.bg4.screenSize;
case BG4_SCADDR: return ppufast.io.bg4.screenAddress >> 8;
case BG4_SCSIZE: return ppufast.io.bg4.screenSize;
//$210B
case SNES_REG_BG1_TDADDR: return ppufast.io.bg1.tiledataAddress >> 12;
case SNES_REG_BG2_TDADDR: return ppufast.io.bg2.tiledataAddress >> 12;
case BG1_TDADDR: return ppufast.io.bg1.tiledataAddress >> 12;
case BG2_TDADDR: return ppufast.io.bg2.tiledataAddress >> 12;
//$210C
case SNES_REG_BG3_TDADDR: return ppufast.io.bg3.tiledataAddress >> 12;
case SNES_REG_BG4_TDADDR: return ppufast.io.bg4.tiledataAddress >> 12;
case BG3_TDADDR: return ppufast.io.bg3.tiledataAddress >> 12;
case BG4_TDADDR: return ppufast.io.bg4.tiledataAddress >> 12;
//$2133 SETINI
case SNES_REG_SETINI_MODE7_EXTBG: return ppufast.io.extbg;
case SNES_REG_SETINI_HIRES: return ppufast.io.pseudoHires;
case SNES_REG_SETINI_OVERSCAN: return ppufast.io.overscan;
case SNES_REG_SETINI_OBJ_INTERLACE: return ppufast.io.obj.interlace;
case SNES_REG_SETINI_SCREEN_INTERLACE: return ppufast.io.interlace;
case SETINI_MODE7_EXTBG: return ppufast.io.extbg;
case SETINI_HIRES: return ppufast.io.pseudoHires;
case SETINI_OVERSCAN: return ppufast.io.overscan;
case SETINI_OBJ_INTERLACE: return ppufast.io.obj.interlace;
case SETINI_SCREEN_INTERLACE: return ppufast.io.interlace;
//$2130 CGWSEL
case SNES_REG_CGWSEL_COLORMASK: return ppufast.io.col.window.aboveMask;
case SNES_REG_CGWSEL_COLORSUBMASK: return ppufast.io.col.window.belowMask;
case SNES_REG_CGWSEL_ADDSUBMODE: return ppufast.io.col.blendMode;
case SNES_REG_CGWSEL_DIRECTCOLOR: return ppufast.io.col.directColor;
case CGWSEL_COLORMASK: return ppufast.io.col.window.aboveMask;
case CGWSEL_COLORSUBMASK: return ppufast.io.col.window.belowMask;
case CGWSEL_ADDSUBMODE: return ppufast.io.col.blendMode;
case CGWSEL_DIRECTCOLOR: return ppufast.io.col.directColor;
//$2101 OBSEL
case SNES_REG_OBSEL_NAMEBASE: return ppufast.io.obj.tiledataAddress >> 13; // TODO: figure out why these shifts are only in specific places
case SNES_REG_OBSEL_NAMESEL: return ppufast.io.obj.nameselect;
case SNES_REG_OBSEL_SIZE: return ppufast.io.obj.baseSize;
case OBSEL_NAMEBASE: return ppufast.io.obj.tiledataAddress >> 13;
case OBSEL_NAMESEL: return ppufast.io.obj.nameselect;
case OBSEL_SIZE: return ppufast.io.obj.baseSize;
//$2131 CGADDSUB
//enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
case SNES_REG_CGADDSUB_BG1: return ppufast.io.col.enable[PPUfast::Source::BG1];
case SNES_REG_CGADDSUB_BG2: return ppufast.io.col.enable[PPUfast::Source::BG2];
case SNES_REG_CGADDSUB_BG3: return ppufast.io.col.enable[PPUfast::Source::BG3];
case SNES_REG_CGADDSUB_BG4: return ppufast.io.col.enable[PPUfast::Source::BG4];
case SNES_REG_CGADDSUB_OBJ: return ppufast.io.col.enable[PPUfast::Source::OBJ2];
case SNES_REG_CGADDSUB_BACKDROP: return ppufast.io.col.enable[PPUfast::Source::COL];
case SNES_REG_CGADDSUB_HALF: return ppufast.io.col.halve;
case SNES_REG_CGADDSUB_MODE: return ppufast.io.col.mathMode;
case CGADDSUB_BG1: return ppufast.io.col.enable[PPUfast::Source::BG1];
case CGADDSUB_BG2: return ppufast.io.col.enable[PPUfast::Source::BG2];
case CGADDSUB_BG3: return ppufast.io.col.enable[PPUfast::Source::BG3];
case CGADDSUB_BG4: return ppufast.io.col.enable[PPUfast::Source::BG4];
case CGADDSUB_OBJ: return ppufast.io.col.enable[PPUfast::Source::OBJ2];
case CGADDSUB_BACKDROP: return ppufast.io.col.enable[PPUfast::Source::COL];
case CGADDSUB_HALF: return ppufast.io.col.halve;
case CGADDSUB_MODE: return ppufast.io.col.mathMode;
//$212C TM
case SNES_REG_TM_BG1: return ppufast.io.bg1.aboveEnable;
case SNES_REG_TM_BG2: return ppufast.io.bg2.aboveEnable;
case SNES_REG_TM_BG3: return ppufast.io.bg3.aboveEnable;
case SNES_REG_TM_BG4: return ppufast.io.bg4.aboveEnable;
case SNES_REG_TM_OBJ: return ppufast.io.obj.aboveEnable;
case TM_BG1: return ppufast.io.bg1.aboveEnable;
case TM_BG2: return ppufast.io.bg2.aboveEnable;
case TM_BG3: return ppufast.io.bg3.aboveEnable;
case TM_BG4: return ppufast.io.bg4.aboveEnable;
case TM_OBJ: return ppufast.io.obj.aboveEnable;
//$212D TS
case SNES_REG_TS_BG1: return ppufast.io.bg1.belowEnable;
case SNES_REG_TS_BG2: return ppufast.io.bg2.belowEnable;
case SNES_REG_TS_BG3: return ppufast.io.bg3.belowEnable;
case SNES_REG_TS_BG4: return ppufast.io.bg4.belowEnable;
case SNES_REG_TS_OBJ: return ppufast.io.obj.belowEnable;
case TS_BG1: return ppufast.io.bg1.belowEnable;
case TS_BG2: return ppufast.io.bg2.belowEnable;
case TS_BG3: return ppufast.io.bg3.belowEnable;
case TS_BG4: return ppufast.io.bg4.belowEnable;
case TS_OBJ: return ppufast.io.obj.belowEnable;
//Mode7 regs
case SNES_REG_M7SEL_HFLIP: return ppufast.io.mode7.hflip;
case SNES_REG_M7SEL_VFLIP: return ppufast.io.mode7.vflip;
case SNES_REG_M7SEL_REPEAT: return ppufast.io.mode7.repeat;
case SNES_REG_M7A: return ppufast.io.mode7.a;
case SNES_REG_M7B: return ppufast.io.mode7.b;
case SNES_REG_M7C: return ppufast.io.mode7.c;
case SNES_REG_M7D: return ppufast.io.mode7.d;
case SNES_REG_M7X: return ppufast.io.mode7.x;
case SNES_REG_M7Y: return ppufast.io.mode7.y;
case M7SEL_HFLIP: return ppufast.io.mode7.hflip;
case M7SEL_VFLIP: return ppufast.io.mode7.vflip;
case M7SEL_REPEAT: return ppufast.io.mode7.repeat;
case M7A: return ppufast.io.mode7.a;
case M7B: return ppufast.io.mode7.b;
case M7C: return ppufast.io.mode7.c;
case M7D: return ppufast.io.mode7.d;
case M7X: return ppufast.io.mode7.x;
case M7Y: return ppufast.io.mode7.y;
//BG scroll regs
case SNES_REG_BG1HOFS: return ppufast.io.bg1.hoffset;
case SNES_REG_BG1VOFS: return ppufast.io.bg1.voffset;
case SNES_REG_BG2HOFS: return ppufast.io.bg2.hoffset;
case SNES_REG_BG2VOFS: return ppufast.io.bg2.voffset;
case SNES_REG_BG3HOFS: return ppufast.io.bg3.hoffset;
case SNES_REG_BG3VOFS: return ppufast.io.bg3.voffset;
case SNES_REG_BG4HOFS: return ppufast.io.bg4.hoffset;
case SNES_REG_BG4VOFS: return ppufast.io.bg4.voffset;
case SNES_REG_M7HOFS: return ppufast.io.mode7.hoffset; // TODO figure out what that comment means .regs.m7_hofs & 0x1FFF; //rememebr to make these signed with <<19>>19
case SNES_REG_M7VOFS: return ppufast.io.mode7.voffset; //rememebr to make these signed with <<19>>19
case BG1HOFS: return ppufast.io.bg1.hoffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG1VOFS: return ppufast.io.bg1.voffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG2HOFS: return ppufast.io.bg2.hoffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG2VOFS: return ppufast.io.bg2.voffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG3HOFS: return ppufast.io.bg3.hoffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG3VOFS: return ppufast.io.bg3.voffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG4HOFS: return ppufast.io.bg4.hoffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case BG4VOFS: return ppufast.io.bg4.voffset & 0x3FF; // copied from old bsnes impl; not sure why this & exists
case M7HOFS: return ppufast.io.mode7.hoffset & 0x1FFF;
case M7VOFS: return ppufast.io.mode7.voffset & 0x1FFF;
}
else; // no fast ppu
// TODO: potentially provide register values even in this case? currently all those are private in ppu.hpp
@ -386,8 +385,11 @@ EXPORT void* snes_get_memory_region(int id, int* size, int* word_size)
*size = sizeof(ppufast.vram);
*word_size = sizeof(*ppufast.vram);
return ppufast.vram;
// case SNES_MEMORY::OAM: // probably weird since bsnes uses "object"s instead of bytes for oam rn
// return (uint8_t*) ppufast.objects;
case SNES_MEMORY::OBJECTS: // returns a pointer to an array of "objects", not raw OAM memory
if (!fast_ppu) break;
*size = sizeof(ppufast.objects);
*word_size = sizeof(*ppufast.objects);
return (void*) ppufast.objects;
case SNES_MEMORY::CGRAM:
if (!fast_ppu) break;
*size = sizeof(ppufast.cgram);

View File

@ -22,6 +22,7 @@ enum SNES_MEMORY {
WRAM,
APURAM,
VRAM,
OBJECTS,
CGRAM
};
@ -56,92 +57,89 @@ struct SnesRegisters
};
// below code unused; would be useful for the graphics debugger
//$2105
#define SNES_REG_BG_MODE 0
#define SNES_REG_BG3_PRIORITY 1
#define SNES_REG_BG1_TILESIZE 2
#define SNES_REG_BG2_TILESIZE 3
#define SNES_REG_BG3_TILESIZE 4
#define SNES_REG_BG4_TILESIZE 5
//$2107
#define SNES_REG_BG1_SCADDR 10
#define SNES_REG_BG1_SCSIZE 11
//$2108
#define SNES_REG_BG2_SCADDR 12
#define SNES_REG_BG2_SCSIZE 13
//$2109
#define SNES_REG_BG3_SCADDR 14
#define SNES_REG_BG3_SCSIZE 15
//$210A
#define SNES_REG_BG4_SCADDR 16
#define SNES_REG_BG4_SCSIZE 17
//$210B
#define SNES_REG_BG1_TDADDR 20
#define SNES_REG_BG2_TDADDR 21
//$210C
#define SNES_REG_BG3_TDADDR 22
#define SNES_REG_BG4_TDADDR 23
//$2133 SETINI
#define SNES_REG_SETINI_MODE7_EXTBG 30
#define SNES_REG_SETINI_HIRES 31
#define SNES_REG_SETINI_OVERSCAN 32
#define SNES_REG_SETINI_OBJ_INTERLACE 33
#define SNES_REG_SETINI_SCREEN_INTERLACE 34
//$2130 CGWSEL
#define SNES_REG_CGWSEL_COLORMASK 40
#define SNES_REG_CGWSEL_COLORSUBMASK 41
#define SNES_REG_CGWSEL_ADDSUBMODE 42
#define SNES_REG_CGWSEL_DIRECTCOLOR 43
//$2101 OBSEL
#define SNES_REG_OBSEL_NAMEBASE 50
#define SNES_REG_OBSEL_NAMESEL 51
#define SNES_REG_OBSEL_SIZE 52
//$2131 CGADSUB
#define SNES_REG_CGADDSUB_MODE 60
#define SNES_REG_CGADDSUB_HALF 61
#define SNES_REG_CGADDSUB_BG4 62
#define SNES_REG_CGADDSUB_BG3 63
#define SNES_REG_CGADDSUB_BG2 64
#define SNES_REG_CGADDSUB_BG1 65
#define SNES_REG_CGADDSUB_OBJ 66
#define SNES_REG_CGADDSUB_BACKDROP 67
//$212C TM
#define SNES_REG_TM_BG1 70
#define SNES_REG_TM_BG2 71
#define SNES_REG_TM_BG3 72
#define SNES_REG_TM_BG4 73
#define SNES_REG_TM_OBJ 74
//$212D TM
#define SNES_REG_TS_BG1 80
#define SNES_REG_TS_BG2 81
#define SNES_REG_TS_BG3 82
#define SNES_REG_TS_BG4 83
#define SNES_REG_TS_OBJ 84
//Mode7 regs
#define SNES_REG_M7SEL_REPEAT 90
#define SNES_REG_M7SEL_HFLIP 91
#define SNES_REG_M7SEL_VFLIP 92
#define SNES_REG_M7A 93
#define SNES_REG_M7B 94
#define SNES_REG_M7C 95
#define SNES_REG_M7D 96
#define SNES_REG_M7X 97
#define SNES_REG_M7Y 98
//BG scroll regs
#define SNES_REG_BG1HOFS 100
#define SNES_REG_BG1VOFS 101
#define SNES_REG_BG2HOFS 102
#define SNES_REG_BG2VOFS 103
#define SNES_REG_BG3HOFS 104
#define SNES_REG_BG3VOFS 105
#define SNES_REG_BG4HOFS 106
#define SNES_REG_BG4VOFS 107
#define SNES_REG_M7HOFS 108
#define SNES_REG_M7VOFS 109
int snes_peek_logical_register(int reg);
enum SNES_REGISTER {
//$2105
BG_MODE,
BG3_PRIORITY,
BG1_TILESIZE,
BG2_TILESIZE,
BG3_TILESIZE,
BG4_TILESIZE,
//$2107
BG1_SCADDR,
BG1_SCSIZE,
//$2108
BG2_SCADDR,
BG2_SCSIZE,
//$2109,
BG3_SCADDR,
BG3_SCSIZE,
//$210A
BG4_SCADDR,
BG4_SCSIZE,
//$210B
BG1_TDADDR,
BG2_TDADDR,
//$210C
BG3_TDADDR,
BG4_TDADDR,
//$2133 SETINI
SETINI_MODE7_EXTBG,
SETINI_HIRES,
SETINI_OVERSCAN,
SETINI_OBJ_INTERLACE,
SETINI_SCREEN_INTERLACE,
//$2130 CGWSEL
CGWSEL_COLORMASK,
CGWSEL_COLORSUBMASK,
CGWSEL_ADDSUBMODE,
CGWSEL_DIRECTCOLOR,
//$2101 OBSEL
OBSEL_NAMEBASE,
OBSEL_NAMESEL,
OBSEL_SIZE,
//$2131 CGADSUB
CGADDSUB_MODE,
CGADDSUB_HALF,
CGADDSUB_BG4,
CGADDSUB_BG3,
CGADDSUB_BG2,
CGADDSUB_BG1,
CGADDSUB_OBJ,
CGADDSUB_BACKDROP,
//$212C TM
TM_BG1,
TM_BG2,
TM_BG3,
TM_BG4,
TM_OBJ,
//$212D TM
TS_BG1,
TS_BG2,
TS_BG3,
TS_BG4,
TS_OBJ,
//Mode7 regs
M7SEL_REPEAT,
M7SEL_HFLIP,
M7SEL_VFLIP,
M7A,
M7B,
M7C,
M7D,
M7X,
M7Y,
//BG scroll regs
BG1HOFS,
BG1VOFS,
BG2HOFS,
BG2VOFS,
BG3HOFS,
BG3VOFS,
BG4HOFS,
BG4VOFS,
M7HOFS,
M7VOFS
};
#endif