Skip to content

Commit b031279

Browse files
committed
new project: rewrite vector table logic
- fixes #124 where Native_IRQ was flat-out incorrect - un-hardcodes much of the GUI table mapping - allows us to do some better stuff in the future
1 parent dcb21f9 commit b031279

File tree

10 files changed

+100
-151
lines changed

10 files changed

+100
-151
lines changed

Diz.Controllers/Diz.Controllers.Test/Diz.Controllers.Test/src/ImportRomDialogontroller.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,10 @@ public void WithNoLabels()
8686
public void WithTwoLabels()
8787
{
8888
mockView!.SetupGet(x => x.EnabledVectorTableEntries)
89-
.Returns(new List<string>
90-
{
89+
.Returns([
9190
SnesVectorNames.Native_ABORT,
92-
SnesVectorNames.Emulation_RESET,
93-
});
91+
SnesVectorNames.Emulation_RESET
92+
]);
9493

9594
Run(() =>
9695
{

Diz.Controllers/Diz.Controllers/src/controllers/IImportRomDialogController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public interface IImportRomDialogController
1818
delegate void SettingsCreatedEvent();
1919

2020
bool Submit();
21-
int GetVectorTableValue(int whichTable, int whichEntry);
21+
public int ReadRomVectorTableEntryValueWord(int vectorEntryTableStartOffset);
2222
bool IsProbablyValidDetection();
2323
string GetDetectionMessage();
2424
}

Diz.Controllers/Diz.Controllers/src/controllers/ImportROMDialogController.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Diz.Core.Interfaces;
77
using Diz.Core.serialization;
88
using Diz.Core.util;
9+
using Diz.Cpu._65816;
910
using Diz.Cpu._65816.import;
1011
using JetBrains.Annotations;
1112

@@ -123,7 +124,7 @@ private void Refresh()
123124
View?.RefreshUi();
124125
SyncVectorTableEntriesFromGui();
125126
}
126-
127+
127128
private void SyncVectorTableEntriesFromGui()
128129
{
129130
Builder.OptionClearGenerateVectorTableLabels();
@@ -168,19 +169,14 @@ public bool Submit()
168169

169170
return true;
170171
}
171-
172-
public int GetVectorTableValue(int whichTable, int whichEntry)
172+
173+
public int ReadRomVectorTableEntryValueWord(int vectorEntryTableStartOffset)
173174
{
174-
Debug.Assert(whichTable is 0 or 1);
175-
var tableOffset = 0x10 * whichTable;
176-
177-
Debug.Assert(whichEntry is >= 0 and < 6);
178-
var vectorEntry = 2 * whichEntry;
179-
180-
var baseAddr = RomSettingsOffset + 15;
181-
var romOffset = baseAddr + tableOffset + vectorEntry;
182-
183-
var vectorValue = RomBytes[romOffset] + (RomBytes[romOffset + 1] << 8);
184-
return vectorValue;
175+
Debug.Assert(vectorEntryTableStartOffset % 2 == 0);
176+
var romOffset = RomSettingsOffset + CpuVectorTable.VectorTableSettingsOffset + vectorEntryTableStartOffset;
177+
return ReadRomWord(romOffset);
185178
}
179+
180+
private int ReadRomWord(int romOffset) =>
181+
RomBytes[romOffset] + (RomBytes[romOffset + 1] << 8);
186182
}

Diz.Core/serialization/ImportSettings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ public RomSpeed RomSpeed
3838

3939
/// <summary>
4040
/// List of Rom offsets (not SNES addresses) for labels for initial vector tables/etc
41-
/// TODO: actually... we probably want these to be SNES addresses after all.
4241
/// </summary>
4342
public Dictionary<int, Label> InitialLabels
4443
{

Diz.Core/util/RomUtil.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,7 @@ private static byte[] RemoveSmcHeader(byte[] allFileBytes)
379379
else
380380
rom = allFileBytes;
381381

382-
if (rom.Length < 0x8000)
383-
throw new InvalidDataException("This ROM is too small. It can't be opened.");
384-
385-
return rom;
382+
return rom.Length < 0x8000 ? throw new InvalidDataException("This ROM is too small. It can't be opened.") : rom;
386383
}
387384

388385
public static Dictionary<int, FlagType> GenerateHeaderFlags(int romSettingsOffset, IReadOnlyList<byte> romBytes)
@@ -392,18 +389,22 @@ public static Dictionary<int, FlagType> GenerateHeaderFlags(int romSettingsOffse
392389
if (romSettingsOffset == -1)
393390
return flags;
394391

392+
// cart title: either ASCII or shiftJIS (japanese) encoded x21 bytes
395393
for (var i = 0; i < LengthOfTitleName; i++)
396394
flags.Add(romSettingsOffset - LengthOfTitleName + i, FlagType.Text);
397395

396+
// bunch of 1-byte fields x7 bytes
398397
for (var i = 0; i < 7; i++)
399398
flags.Add(romSettingsOffset + i, FlagType.Data8Bit);
400399

400+
// checksum (2 bytes each) x2 words
401401
for (var i = 0; i < 4; i++)
402402
flags.Add(romSettingsOffset + 7 + i, FlagType.Data16Bit);
403403

404+
// vector table entries (we'll add labels to each of these later). 2x bytes each x 16 entries
404405
for (var i = 0; i < 0x20; i++)
405406
flags.Add(romSettingsOffset + 11 + i, FlagType.Pointer16Bit);
406-
407+
407408
if (romBytes[romSettingsOffset - 1] == 0)
408409
{
409410
flags.Remove(romSettingsOffset - 1);

Diz.Cpu.65816/src/CpuVectorTable.cs

Lines changed: 46 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -4,102 +4,54 @@ namespace Diz.Cpu._65816;
44

55
public static class CpuVectorTable
66
{
7-
public const int VectorTableBaseOffset = 15;
7+
// = FFE0 (start of vector table) minus FFD5 (start of settings after the cart title, it's the offset of the ROM map mode in the header).
8+
// vector table may be in different ROM locations for different rom map modes, but, it's always the same offset within the cart header.
9+
// in this file we only care about relative offset to the start of the tables
10+
public const int VectorTableSettingsOffset = 11;
11+
public record VectorRomEntry(int AbsoluteRomOffset, VectorTableEntry VectorTableEntry);
12+
public record VectorTableEntry(string Name, int VectorTableOffset);
813

9-
public record VectorTableEntry(string Name, int SettingsOffset);
10-
public record VectorTable(int SettingsOffset, VectorTableEntry[] Entries);
14+
private static readonly List<VectorTableEntry> VectorTableEntries =
15+
// these must be kept in order, with no gaps, starting from the start of the vector table.
16+
// we're going to include all of the vector table entries, including the stuff that's not used by the 65816 CPU,
17+
// and also the stuff not used by the SNES hardware
18+
new List<string> {
19+
// Native Mode Vectors (snes $FFE0-$FFEF)
20+
SnesVectorNames.Native_Reserved1__ignored,
21+
SnesVectorNames.Native_Reserved2__ignored,
22+
SnesVectorNames.Native_COP,
23+
SnesVectorNames.Native_BRK,
24+
SnesVectorNames.Native_ABORT,
25+
SnesVectorNames.Native_NMI,
26+
SnesVectorNames.Native_RESET__ignored,
27+
SnesVectorNames.Native_IRQ,
28+
29+
// Emulation Mode Vectors (snes $FFF0-$FFFF)
30+
SnesVectorNames.Emulation_Reserved1__ignored,
31+
SnesVectorNames.Emulation_Reserved2__ignored,
32+
SnesVectorNames.Emulation_COP,
33+
SnesVectorNames.Emulation_Reserved3__ignored,
34+
SnesVectorNames.Emulation_ABORT,
35+
SnesVectorNames.Emulation_NMI,
36+
SnesVectorNames.Emulation_RESET,
37+
SnesVectorNames.Emulation_IRQBRK
38+
}
39+
.Select((name, index) => new VectorTableEntry(
40+
Name: name,
41+
// relative offset from the start of the vector table, starting from 00 and going up by 2 bytes
42+
VectorTableOffset: index*2
43+
))
44+
.ToList();
1145

12-
/*
13-
* see:
14-
* https://github.com/IsoFrieze/DiztinGUIsh/issues/74
15-
*
16-
* TODO, pretty sure vector table below is generating incorrect output.
17-
* it SHOULD be made to match this (examples from some random ROM):
18-
19-
// values if lowrom
20-
Native_unused_vector1:
21-
dw $0000 ;00FFE0| |000000; unused by SNES
22-
23-
Native_unused_vector2:
24-
dw $0000 ;00FFE2| |000000; unused by SNES
25-
; | | ;
26-
Native_COP:
27-
dw fn_NOP_rti ;00FFE4| |008138; Fzero: NO-OP
28-
; | | ;
29-
Native_BRK:
30-
dw fn_NOP_rti ;00FFE6| |008138; Fzero: NO-OP
31-
; | | ;
32-
Native_ABORT_unused:
33-
dw fn_NOP_rti ;00FFE8| |008138; unused by SNES
34-
; | | ;
35-
Native_NMI:
36-
dw fn_native_NMI_routine ;00FFEA| |0080D9;
37-
; | | ;
38-
Native_unused_vector3:
39-
dw PTR16_00FFFF ;00FFEC| |00FFFF; unused by SNES
40-
; | | ;
41-
Native_IRQ:
42-
dw CODE_008601 ;00FFEE| |008601;
43-
; | | ;
44-
Emulation_unused_vector1:
45-
dw $0000 ;00FFF0| |000000; unused by SNES
46-
; | | ;
47-
Emulation_unused_vector2:
48-
dw $0000 ;00FFF2| |000000; unused by SNES
49-
; | | ;
50-
Emulation_COP:
51-
dw fn_NOP_rti ;00FFF4| |008138; Fzero: NO-OP
52-
; | | ;
53-
Emulation_unused_vector3:
54-
dw PTR16_00FFFF ;00FFF6| |00FFFF; unused by SNES
55-
; | | ;
56-
Emulation_ABORT:
57-
dw fn_NOP_rti ;00FFF8| |008138; unused by SNES
58-
; | | ;
59-
Emulation_NMI:
60-
dw fn_NOP_rti ;00FFFA| |008138; Fzero: NO-OP
61-
; | | ;
62-
Emulation_RESET:
63-
dw cpu_startup ;00FFFC| |008000; entrypoint of entire game
64-
; | | ;
65-
Emulation_IRQ:
66-
dw fn_NOP_rti ;00FFFE| |008138; Fzero: NO-OP
67-
68-
69-
*/
70-
71-
public static readonly VectorTable[] VectorTables =
72-
{
73-
// https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector
74-
// TODO: strong suspect some of this is wrong. see above.
75-
new( VectorTableBaseOffset, new []
76-
{
77-
new VectorTableEntry(SnesVectorNames.Native_COP, 0x00),
78-
new VectorTableEntry(SnesVectorNames.Native_BRK, 0x02),
79-
new VectorTableEntry(SnesVectorNames.Native_ABORT, 0x04),
80-
new VectorTableEntry(SnesVectorNames.Native_NMI, 0x06),
81-
new VectorTableEntry(SnesVectorNames.Native_RESET, 0x08),
82-
new VectorTableEntry(SnesVectorNames.Native_IRQ, 0x10)
83-
}),
84-
new(VectorTableBaseOffset + 0x12, new [] // prob this is wrong, 0x12?
85-
{
86-
new VectorTableEntry(SnesVectorNames.Emulation_COP, 0x00),
87-
new VectorTableEntry(SnesVectorNames.Emulation_Unknown, 0x02),
88-
new VectorTableEntry(SnesVectorNames.Emulation_ABORT, 0x04),
89-
new VectorTableEntry(SnesVectorNames.Emulation_NMI, 0x06),
90-
new VectorTableEntry(SnesVectorNames.Emulation_RESET, 0x08),
91-
new VectorTableEntry(SnesVectorNames.Emulation_IRQBRK, 0x10)
92-
}),
93-
};
94-
95-
public record VectorRomEntry(int AbsoluteRomOffset, VectorTableEntry Entry, VectorTable ParentTable);
96-
97-
public static IEnumerable<VectorRomEntry> ComputeVectorTableNamesAndOffsets(int settingsOffset)
46+
public static IEnumerable<VectorRomEntry> ComputeVectorTableNamesAndOffsets(int settingsAbsoluteRomOffset)
9847
{
99-
return VectorTables
100-
.Select(table => (romOffset: settingsOffset + table.SettingsOffset, entries: table.Entries, parentTable: table))
101-
.SelectMany(table => table.entries
102-
.Select(entry => new VectorRomEntry(entry.SettingsOffset + table.romOffset, entry, table.parentTable))
103-
);
48+
// compute the absolute ROM address of all vector table entries (including the invalid ones)
49+
// settingsAbsoluteRomOffset is the ROM address of the start of the "snes settings" i.e. the value after the cart title.
50+
// examples: ROM offset 0xFFD5 for hirom, 0x7FD5 for lorom
51+
return VectorTableEntries.Select(entry =>
52+
new VectorRomEntry(
53+
AbsoluteRomOffset: settingsAbsoluteRomOffset + VectorTableSettingsOffset + entry.VectorTableOffset,
54+
VectorTableEntry: entry
55+
));
10456
}
10557
}

Diz.Cpu.65816/src/import/ImportRomSettingsBuilder.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ public bool OptionGenerateSelectedVectorTableLabels
4343
private int? RomSettingOffset =>
4444
Input.AnalysisResults == null ? null : RomUtil.GetRomSettingOffset(Input.AnalysisResults.RomMapMode);
4545

46-
// ALL vector table entries for the currently selected Rom Map Mode
46+
// ALL vector table entries (native and emulation) for the currently selected Rom Map Mode
47+
// (including unused/deselected/etc)
4748
private IVectorTableCache VectorTableForCurrentMapMode { get; }
4849

4950
// a list of enabled vector table entries, varies with the UI.
50-
public List<string> EnabledVectorEntries { get; } = new();
51+
private List<string> EnabledVectorEntries { get; } = [];
5152

5253
public SnesRomImportSettingsBuilder(ISnesRomAnalyzer snesRomAnalyzer, IVectorTableCache vectorTableCache, IReadFromFileBytes fileReader)
5354
{
@@ -151,11 +152,10 @@ public ImportRomSettings GenerateSettings()
151152

152153
private Dictionary<int, Label> GenerateVectorLabels()
153154
{
154-
var allEntries = VectorTableForCurrentMapMode
155-
.Entries ?? new List<CpuVectorTable.VectorRomEntry>();
155+
var allEntries = VectorTableForCurrentMapMode.Entries ?? [];
156156

157157
return EnabledVectorEntries
158-
.Select(x => allEntries.Single(entry => entry.Entry.Name == x))
158+
.Select(x => allEntries.Single(entry => entry.VectorTableEntry.Name == x))
159159
.Select(CreateLabelForVectorEntry)
160160
.Where(x => x.HasValue)
161161
.Select(x => x!.Value)
@@ -169,7 +169,7 @@ private Dictionary<int, Label> GenerateVectorLabels()
169169

170170
// note: can also do a SNES address here if we wanted to. benefits to doing both.
171171
// when mirroring works in labels, this will be useful to have both
172-
var (romOffset, vectorTableEntry, _) = entry;
172+
var (romOffset, vectorTableEntry) = entry;
173173

174174
return new KeyValuePair<int, Label>(romOffset, new Label {
175175
Name = vectorTableEntry.Name,

Diz.Cpu.65816/src/import/ImportUtils.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,15 @@
55

66
namespace Diz.Cpu._65816.import;
77

8-
// TODO: hook this up
9-
10-
public class SnesProjectFactoryFromRomImportSettings : IProjectFactoryFromRomImportSettings
8+
public class SnesProjectFactoryFromRomImportSettings(
9+
IProjectFactory baseProjectFactory,
10+
IRomImportSettings importSettings)
11+
: IProjectFactoryFromRomImportSettings
1112
{
12-
private readonly IRomImportSettings importSettings;
13-
private readonly IProjectFactory baseProjectFactory;
14-
15-
public SnesProjectFactoryFromRomImportSettings(IProjectFactory baseProjectFactory, IRomImportSettings importSettings)
16-
{
17-
this.importSettings = importSettings;
18-
this.baseProjectFactory = baseProjectFactory;
19-
}
20-
2113
public Project Read()
2214
{
2315
var project = baseProjectFactory.Create()
24-
as Project; // TODO: after more refactoring, remove cast and use IProject directly
16+
as Project; // TODO: refactor more, remove this cast, and have us return IProject directly
2517

2618
Debug.Assert(project?.Data != null);
2719

Diz.Cpu.65816/src/import/SnesVectorNames.cs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,26 @@ namespace Diz.Cpu._65816.import;
55
[SuppressMessage("ReSharper", "InconsistentNaming")]
66
public static class SnesVectorNames
77
{
8-
public const string Native_COP = "Native_COP";
9-
public const string Native_BRK = "Native_BRK";
10-
public const string Native_ABORT = "Native_ABORT";
11-
public const string Native_NMI = "Native_NMI";
12-
public const string Native_RESET = "Native_RESET";
13-
public const string Native_IRQ = "Native_IRQ";
14-
public const string Emulation_COP = "Emulation_COP";
15-
public const string Emulation_Unknown = "Emulation_Unknown";
16-
public const string Emulation_ABORT = "Emulation_ABORT";
17-
public const string Emulation_NMI = "Emulation_NMI";
18-
public const string Emulation_RESET = "Emulation_RESET";
19-
public const string Emulation_IRQBRK = "Emulation_IRQBRK";
8+
// Note: All of these are valid 65816 vectors,
9+
// but not all are actually used by the SNES hardware.
10+
11+
// Native Mode Vectors ($FFE0-$FFEF)
12+
public const string Native_Reserved1__ignored = "Native_Reserved1__ignored"; // $FFE0 - reserved for future use, also unused by SNES
13+
public const string Native_Reserved2__ignored = "Native_Reserved2__ignored"; // $FFE2 - reserved for future use, also unused by SNES
14+
public const string Native_COP = "Native_COP"; // $FFE4 - ! USED by SNES !
15+
public const string Native_BRK = "Native_BRK"; // $FFE6 - ! USED by SNES !
16+
public const string Native_ABORT = "Native_ABORT__ignored"; // $FFE8 - unused by SNES
17+
public const string Native_NMI = "Native_NMI"; // $FFEA - ! USED by SNES ! - important
18+
public const string Native_RESET__ignored = "Native_RESET__ignored"; // $FFEC - unused by SNES - native reset vector
19+
public const string Native_IRQ = "Native_IRQ"; // $FFEE - ! USED by SNES ! - important
20+
21+
// Emulation Mode Vectors ($FFF0-$FFFF)
22+
public const string Emulation_Reserved1__ignored = "Emulation_Reserved1__ignored"; // $FFF0 - reserved for future use, also unused by SNES
23+
public const string Emulation_Reserved2__ignored = "Emulation_Reserved2__ignored"; // $FFF2 - reserved for future use, also unused by SNES
24+
public const string Emulation_COP = "Emulation_COP"; // $FFF4 - unused by SNES
25+
public const string Emulation_Reserved3__ignored = "Emulation_Reserved3__ignored"; // $FFF6 - reserved for future use, also unused by SNES
26+
public const string Emulation_ABORT = "Emulation_ABORT__ignored"; // $FFF8 - unused by SNES
27+
public const string Emulation_NMI = "Emulation_NMI"; // $FFFA - unused by SNES
28+
public const string Emulation_RESET = "Emulation_RESET"; // $FFFC - ! USED by SNES ! - main entry point. most ROMs only care about this one and ignore the rest of emulation vectors
29+
public const string Emulation_IRQBRK = "Emulation_IRQBRK"; // $FFFE - IRQ and BRK
2030
}

0 commit comments

Comments
 (0)