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:
parent
28d62e69d7
commit
6f0953aaa3
Binary file not shown.
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue