-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathAssemblyGenerator.cs
More file actions
117 lines (90 loc) · 4.12 KB
/
AssemblyGenerator.cs
File metadata and controls
117 lines (90 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System.IO;
using Diz.Core.export;
using Diz.Core.Interfaces;
using Diz.Cpu._65816;
using Diz.LogWriter.util;
// ReSharper disable ParameterOnlyUsedForPreconditionCheck.Global
namespace Diz.LogWriter;
public interface ILogCreatorForGenerator
{
public LogWriterSettings Settings { get; }
ILogCreatorDataSource<IData> Data { get; }
int GetLineByteLength(int offset);
// report some events to the log creator so it can stash some meta-information about the assembly output
void OnLabelVisited(int snesAddress);
// cache some data that happens when an instruction is visited during creation of a line
// WARNING: introduces side-effects and means the asm must be generated before the defines in the step order,
// or we won't get the data. kinda sucks if we need to re-order things
void OnInstructionVisited(int offset, CpuInstructionDataFormatted cpuInstructionDataFormatted);
}
public abstract class TokenBase
{
}
public class TokenString : TokenBase
{
public string Value { get; init; } = "";
}
public class TokenComment : TokenString {}
public class TokenLabelAssign : TokenString
{
// in this, value contains the entire assignment for ONE label
}
public abstract class AssemblyPartialLineGenerator
{
public ILogCreatorForGenerator LogCreator { get; set; }
protected ILogCreatorDataSource<IData> Data => LogCreator.Data;
protected ISnesApi<IData> SnesApi => Data.Data.GetSnesApi();
public string Token { get; init; } = "";
public int DefaultLength { get; init; }
public bool RequiresToken { get; init; } = true;
public bool UsesOffset { get; init; } = true;
// helper
public static TokenBase[] GenerateFromStr(string str)
{
return [
new TokenString {
Value = str
}
];
}
public TokenBase[] GenerateTokens(int? offset, int? lengthOverride, LineGenerator.TokenExtraContext context = null)
{
var finalLength = lengthOverride ?? DefaultLength;
Validate(offset, finalLength);
if (offset == null && UsesOffset)
throw new InvalidDataException("Invalid Emit() call: Can't call without an offset.");
var generatedTokens = !UsesOffset
? Generate(finalLength, context)
: Generate(offset ?? -1, finalLength, context);
return generatedTokens;
}
protected virtual TokenBase[] Generate(int length, LineGenerator.TokenExtraContext context = null)
{
throw new InvalidDataException("Invalid Generate() call: Can't call without an offset.");
}
protected virtual TokenBase[] Generate(int offset, int length, LineGenerator.TokenExtraContext context = null)
{
// NOTE: if you get here (without an override in a derived class)
// it means the client code should have instead been calling the other Generate(length) overload
// directly. for now, we'll gracefully handle it, but client code should try and be better about it
// eventually.
return Generate(length);
// throw new InvalidDataException("Invalid Generate() call: Can't call with offset.");
}
// call Validate() before doing anything in each Emit()
// if length is non-zero, use that as our length, if not we use the default length
protected virtual void Validate(int? offset, int finalLength)
{
if (finalLength == 0)
throw new InvalidDataException("Assembly output component needed a length but received none.");
if (RequiresToken && string.IsNullOrEmpty(Token))
throw new InvalidDataException("Assembly output component needed a token but received none.");
// we should throw exceptions both ways, for now though we'll let it slide if we were passed in
// an offset and we don't need it.
var hasOffset = offset != null;
if (UsesOffset != hasOffset)
throw new InvalidDataException(UsesOffset
? "Assembly output component needed an offset but received none."
: "Assembly output component doesn't use an offset but we were provided one anyway.");
}
}