@gaialabs/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +27 -0
- package/dist/browser.d.mts +0 -0
- package/dist/browser.d.ts +0 -0
- package/dist/browser.js +0 -0
- package/dist/browser.js.map +0 -0
- package/dist/browser.mjs +0 -0
- package/dist/browser.mjs.map +0 -0
- package/dist/index.d.mts +367 -499
- package/dist/index.d.ts +367 -499
- package/dist/index.js +2094 -1635
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1996 -1534
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +0 -0
- package/dist/node.d.ts +0 -0
- package/dist/node.js +0 -0
- package/dist/node.js.map +0 -0
- package/dist/node.mjs +0 -0
- package/dist/node.mjs.map +0 -0
- package/dist/worker.d.mts +0 -0
- package/dist/worker.d.ts +0 -0
- package/dist/worker.js +0 -0
- package/dist/worker.js.map +0 -0
- package/dist/worker.mjs +0 -0
- package/dist/worker.mjs.map +0 -0
- package/package.json +16 -16
package/dist/index.js
CHANGED
|
@@ -1,311 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var fs = require('fs');
|
|
5
|
-
var path = require('path');
|
|
3
|
+
var shared = require('@gaialabs/shared');
|
|
6
4
|
|
|
7
|
-
// src/
|
|
8
|
-
|
|
9
|
-
// src/sprites/SpriteFrame.ts
|
|
10
|
-
var SpriteFrame = class {
|
|
11
|
-
constructor(duration = 0, groupIndex = 0, groupOffset = 0) {
|
|
12
|
-
this.duration = duration;
|
|
13
|
-
this.groupIndex = groupIndex;
|
|
14
|
-
this.groupOffset = groupOffset;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// src/sprites/SpritePart.ts
|
|
19
|
-
var SpritePart = class {
|
|
20
|
-
constructor() {
|
|
21
|
-
this.isLarge = false;
|
|
22
|
-
this.xOffset = 0;
|
|
23
|
-
this.xOffsetMirror = 0;
|
|
24
|
-
this.yOffset = 0;
|
|
25
|
-
this.yOffsetMirror = 0;
|
|
26
|
-
this.vMirror = false;
|
|
27
|
-
this.hMirror = false;
|
|
28
|
-
this.someOffset = 0;
|
|
29
|
-
this.paletteIndex = 0;
|
|
30
|
-
this.tileIndex = 0;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// src/sprites/SpriteGroup.ts
|
|
35
|
-
var SpriteGroup = class {
|
|
36
|
-
constructor() {
|
|
37
|
-
this.xOffset = 0;
|
|
38
|
-
this.xOffsetMirror = 0;
|
|
39
|
-
this.yOffset = 0;
|
|
40
|
-
this.yOffsetMirror = 0;
|
|
41
|
-
this.xRecoilHitboxOffset = 0;
|
|
42
|
-
this.yRecoilHitboxOffset = 0;
|
|
43
|
-
this.xRecoilHitboxTilesize = 0;
|
|
44
|
-
this.yRecoilHitboxTilesize = 0;
|
|
45
|
-
this.xHostileHitboxOffset = 0;
|
|
46
|
-
this.xHostileHitboxSize = 0;
|
|
47
|
-
this.yHostileHitboxOffset = 0;
|
|
48
|
-
this.yHostileHitboxSize = 0;
|
|
49
|
-
this.parts = [];
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// src/sprites/SpriteMap.ts
|
|
54
|
-
var SpriteMap = class _SpriteMap {
|
|
55
|
-
constructor() {
|
|
56
|
-
this.frameSets = [];
|
|
57
|
-
this.groups = [];
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Create a SpriteMap from binary data
|
|
61
|
-
* @param data Binary data buffer
|
|
62
|
-
* @returns SpriteMap instance
|
|
63
|
-
*/
|
|
64
|
-
static fromBytes(data) {
|
|
65
|
-
let position = 0;
|
|
66
|
-
const getByte = () => {
|
|
67
|
-
if (position >= data.length) return 0;
|
|
68
|
-
return data[position++];
|
|
69
|
-
};
|
|
70
|
-
const getUshort = () => {
|
|
71
|
-
if (position + 1 >= data.length) return 0;
|
|
72
|
-
const low = data[position++];
|
|
73
|
-
const high = data[position++];
|
|
74
|
-
return low | high << 8;
|
|
75
|
-
};
|
|
76
|
-
const spriteMap = new _SpriteMap();
|
|
77
|
-
const setOffsets = /* @__PURE__ */ new Set();
|
|
78
|
-
const groupOffsets = /* @__PURE__ */ new Set();
|
|
79
|
-
position = 0;
|
|
80
|
-
while (!setOffsets.has(position)) {
|
|
81
|
-
const offset = getUshort() - 16384;
|
|
82
|
-
setOffsets.add(offset);
|
|
83
|
-
}
|
|
84
|
-
for (const offStart of setOffsets) {
|
|
85
|
-
position = offStart;
|
|
86
|
-
const frameList = [];
|
|
87
|
-
while (true) {
|
|
88
|
-
const duration = getUshort();
|
|
89
|
-
if (duration === 65535) {
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
const groupOffset = getUshort() - 16384;
|
|
93
|
-
frameList.push(new SpriteFrame(duration, 0, groupOffset));
|
|
94
|
-
groupOffsets.add(groupOffset);
|
|
95
|
-
}
|
|
96
|
-
spriteMap.frameSets.push(frameList);
|
|
97
|
-
}
|
|
98
|
-
let grpIx = 0;
|
|
99
|
-
const sortedGroupOffsets = Array.from(groupOffsets).sort((a, b) => a - b);
|
|
100
|
-
for (const offStart of sortedGroupOffsets) {
|
|
101
|
-
position = offStart;
|
|
102
|
-
for (const set of spriteMap.frameSets) {
|
|
103
|
-
for (const frame of set) {
|
|
104
|
-
if (frame.groupOffset === offStart) {
|
|
105
|
-
frame.groupIndex = grpIx;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const grp = new SpriteGroup();
|
|
110
|
-
grp.xOffset = getByte();
|
|
111
|
-
grp.xOffsetMirror = getByte();
|
|
112
|
-
grp.yOffset = getByte();
|
|
113
|
-
grp.yOffsetMirror = getByte();
|
|
114
|
-
grp.xRecoilHitboxOffset = getByte();
|
|
115
|
-
grp.yRecoilHitboxOffset = getByte();
|
|
116
|
-
grp.xRecoilHitboxTilesize = getByte();
|
|
117
|
-
grp.yRecoilHitboxTilesize = getByte();
|
|
118
|
-
grp.xHostileHitboxOffset = getByte();
|
|
119
|
-
grp.xHostileHitboxSize = getByte();
|
|
120
|
-
grp.yHostileHitboxOffset = getByte();
|
|
121
|
-
grp.yHostileHitboxSize = getByte();
|
|
122
|
-
let numParts = getByte();
|
|
123
|
-
while (numParts-- > 0) {
|
|
124
|
-
const part = new SpritePart();
|
|
125
|
-
part.isLarge = getByte() !== 0;
|
|
126
|
-
part.xOffset = getByte();
|
|
127
|
-
part.xOffsetMirror = getByte();
|
|
128
|
-
part.yOffset = getByte();
|
|
129
|
-
part.yOffsetMirror = getByte();
|
|
130
|
-
const props = getUshort();
|
|
131
|
-
part.vMirror = (props & 32768) !== 0;
|
|
132
|
-
part.hMirror = (props & 16384) !== 0;
|
|
133
|
-
part.someOffset = props >> 12 & 3;
|
|
134
|
-
part.paletteIndex = props >> 9 & 7;
|
|
135
|
-
part.tileIndex = props & 511;
|
|
136
|
-
grp.parts.push(part);
|
|
137
|
-
}
|
|
138
|
-
spriteMap.groups.push(grp);
|
|
139
|
-
grpIx++;
|
|
140
|
-
}
|
|
141
|
-
return spriteMap;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Convert this SpriteMap to binary data
|
|
145
|
-
* @returns Binary data buffer
|
|
146
|
-
*/
|
|
147
|
-
toBytes() {
|
|
148
|
-
let size = this.frameSets.length * 2;
|
|
149
|
-
for (const set of this.frameSets) {
|
|
150
|
-
size += set.length * 4 + 2;
|
|
151
|
-
}
|
|
152
|
-
for (const grp of this.groups) {
|
|
153
|
-
size += grp.parts.length * 7 + 13;
|
|
154
|
-
}
|
|
155
|
-
const buffer = new Uint8Array(size);
|
|
156
|
-
let position = 0;
|
|
157
|
-
const writeShort = (val) => {
|
|
158
|
-
buffer[position++] = val & 255;
|
|
159
|
-
buffer[position++] = val >> 8 & 255;
|
|
160
|
-
};
|
|
161
|
-
const writeLoc = (val) => writeShort(val + 16384);
|
|
162
|
-
let pos = this.frameSets.length << 1;
|
|
163
|
-
const groupPos = new Array(this.groups.length);
|
|
164
|
-
for (const set of this.frameSets) {
|
|
165
|
-
writeLoc(pos);
|
|
166
|
-
pos += (set.length << 2) + 2;
|
|
167
|
-
}
|
|
168
|
-
for (let i = 0; i < this.groups.length; i++) {
|
|
169
|
-
groupPos[i] = pos;
|
|
170
|
-
pos += this.groups[i].parts.length * 7 + 13;
|
|
171
|
-
}
|
|
172
|
-
for (const set of this.frameSets) {
|
|
173
|
-
for (const frm of set) {
|
|
174
|
-
writeShort(frm.duration);
|
|
175
|
-
writeLoc(groupPos[frm.groupIndex]);
|
|
176
|
-
}
|
|
177
|
-
writeShort(65535);
|
|
178
|
-
}
|
|
179
|
-
for (const grp of this.groups) {
|
|
180
|
-
buffer[position++] = grp.xOffset;
|
|
181
|
-
buffer[position++] = grp.xOffsetMirror;
|
|
182
|
-
buffer[position++] = grp.yOffset;
|
|
183
|
-
buffer[position++] = grp.yOffsetMirror;
|
|
184
|
-
buffer[position++] = grp.xRecoilHitboxOffset;
|
|
185
|
-
buffer[position++] = grp.yRecoilHitboxOffset;
|
|
186
|
-
buffer[position++] = grp.xRecoilHitboxTilesize;
|
|
187
|
-
buffer[position++] = grp.yRecoilHitboxTilesize;
|
|
188
|
-
buffer[position++] = grp.xHostileHitboxOffset;
|
|
189
|
-
buffer[position++] = grp.xHostileHitboxSize;
|
|
190
|
-
buffer[position++] = grp.yHostileHitboxOffset;
|
|
191
|
-
buffer[position++] = grp.yHostileHitboxSize;
|
|
192
|
-
buffer[position++] = grp.parts.length;
|
|
193
|
-
for (const prt of grp.parts) {
|
|
194
|
-
buffer[position++] = prt.isLarge ? 1 : 0;
|
|
195
|
-
buffer[position++] = prt.xOffset;
|
|
196
|
-
buffer[position++] = prt.xOffsetMirror;
|
|
197
|
-
buffer[position++] = prt.yOffset;
|
|
198
|
-
buffer[position++] = prt.yOffsetMirror;
|
|
199
|
-
const accum = (prt.vMirror ? 32768 : 0) | (prt.hMirror ? 16384 : 0) | (prt.someOffset & 3) << 12 | (prt.paletteIndex & 7) << 9 | prt.tileIndex;
|
|
200
|
-
writeShort(accum);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return buffer.slice(0, position);
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// src/rom/state.ts
|
|
208
|
-
var RomState = class _RomState {
|
|
209
|
-
constructor() {
|
|
210
|
-
// Hardware memory buffers
|
|
211
|
-
this.cgram = new Uint8Array(512);
|
|
212
|
-
// Color palette RAM
|
|
213
|
-
this.vram = new Uint8Array(65536);
|
|
214
|
-
// Video RAM
|
|
215
|
-
// Tileset data
|
|
216
|
-
this.mainTileset = new Uint8Array(2048);
|
|
217
|
-
this.effectTileset = new Uint8Array(2048);
|
|
218
|
-
// Tilemap data
|
|
219
|
-
this.mainTilemap = new Uint8Array(8192);
|
|
220
|
-
this.mainTilemapW = 0;
|
|
221
|
-
this.mainTilemapH = 0;
|
|
222
|
-
this.effectTilemap = new Uint8Array(8192);
|
|
223
|
-
this.effectTilemapW = 0;
|
|
224
|
-
this.effectTilemapH = 0;
|
|
225
|
-
}
|
|
226
|
-
static {
|
|
227
|
-
// Sprite data
|
|
228
|
-
this.spriteMap = null;
|
|
229
|
-
}
|
|
230
|
-
static {
|
|
231
|
-
this.spriteMapPath = null;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Strip address space prefixes from names
|
|
235
|
-
*/
|
|
236
|
-
static stripName(name) {
|
|
237
|
-
const addressSpace = ["@", "&", "^", "#", "$", "%", "*"];
|
|
238
|
-
while (name.length > 0 && addressSpace.includes(name[0])) {
|
|
239
|
-
name = name.substring(1);
|
|
240
|
-
}
|
|
241
|
-
return name;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Create ROM state from scene metadata
|
|
245
|
-
* Simplified version of the C# implementation
|
|
246
|
-
*/
|
|
247
|
-
static async fromScene(baseDir, root, metaFile, id) {
|
|
248
|
-
const state = new _RomState();
|
|
249
|
-
return state;
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Process scene command (simplified version)
|
|
253
|
-
* TODO: Implement full command processing
|
|
254
|
-
*/
|
|
255
|
-
static async processCommand(state, command, params, getResource) {
|
|
256
|
-
switch (command) {
|
|
257
|
-
case 2:
|
|
258
|
-
break;
|
|
259
|
-
case 3:
|
|
260
|
-
break;
|
|
261
|
-
case 4:
|
|
262
|
-
break;
|
|
263
|
-
case 5:
|
|
264
|
-
break;
|
|
265
|
-
case 6:
|
|
266
|
-
break;
|
|
267
|
-
case 16:
|
|
268
|
-
if (params.length > 0) {
|
|
269
|
-
const spritePath = getResource(params[0], gaiaShared.BinType.Spritemap);
|
|
270
|
-
try {
|
|
271
|
-
const data = await gaiaShared.readFileAsBinary(spritePath);
|
|
272
|
-
_RomState.spriteMap = SpriteMap.fromBytes(data);
|
|
273
|
-
_RomState.spriteMapPath = spritePath;
|
|
274
|
-
} catch (error) {
|
|
275
|
-
console.warn(`Failed to load sprite map: ${spritePath}`, error);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
var RomStateUtils = class {
|
|
283
|
-
/**
|
|
284
|
-
* Create empty ROM state
|
|
285
|
-
*/
|
|
286
|
-
static createEmpty() {
|
|
287
|
-
return new RomState();
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Clear all data in ROM state
|
|
291
|
-
*/
|
|
292
|
-
static clear(state) {
|
|
293
|
-
state.cgram.fill(0);
|
|
294
|
-
state.vram.fill(0);
|
|
295
|
-
state.mainTileset.fill(0);
|
|
296
|
-
state.effectTileset.fill(0);
|
|
297
|
-
state.mainTilemap.fill(0);
|
|
298
|
-
state.effectTilemap.fill(0);
|
|
299
|
-
state.mainTilesetPath = void 0;
|
|
300
|
-
state.effectTilesetPath = void 0;
|
|
301
|
-
state.mainTilemapPath = void 0;
|
|
302
|
-
state.effectTilemapPath = void 0;
|
|
303
|
-
state.mainTilemapW = 0;
|
|
304
|
-
state.mainTilemapH = 0;
|
|
305
|
-
state.effectTilemapW = 0;
|
|
306
|
-
state.effectTilemapH = 0;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
5
|
+
// src/rom/project.ts
|
|
309
6
|
var QuintetLZ = class _QuintetLZ {
|
|
310
7
|
static {
|
|
311
8
|
this.DICTIONARY_SIZE = 256;
|
|
@@ -327,7 +24,7 @@ var QuintetLZ = class _QuintetLZ {
|
|
|
327
24
|
* @returns Expanded data
|
|
328
25
|
*/
|
|
329
26
|
expand(srcData, srcPosition = 0, srcLen = _QuintetLZ.DEFAULT_PAGE_SIZE) {
|
|
330
|
-
const bitStream = new
|
|
27
|
+
const bitStream = new shared.BitStream(srcData, srcPosition);
|
|
331
28
|
const srcStop = srcPosition + srcLen;
|
|
332
29
|
const dictionary = new Uint8Array(_QuintetLZ.DICTIONARY_SIZE);
|
|
333
30
|
dictionary.fill(_QuintetLZ.DICTIONARY_INIT);
|
|
@@ -377,7 +74,7 @@ var QuintetLZ = class _QuintetLZ {
|
|
|
377
74
|
const outputBuffer = new Uint8Array(srcLen * 2);
|
|
378
75
|
outputBuffer[dstIx++] = srcLen & 255;
|
|
379
76
|
outputBuffer[dstIx++] = srcLen >> 8 & 255;
|
|
380
|
-
const bitStream = new
|
|
77
|
+
const bitStream = new shared.BitStream(outputBuffer, 2);
|
|
381
78
|
const getCommand = () => {
|
|
382
79
|
const maxLen = Math.min(srcLen - srcIx, 17);
|
|
383
80
|
if (maxLen < 2) {
|
|
@@ -429,13 +126,111 @@ var QuintetLZ = class _QuintetLZ {
|
|
|
429
126
|
return outputBuffer.slice(0, finalSize);
|
|
430
127
|
}
|
|
431
128
|
};
|
|
129
|
+
function registerCompressionProviders() {
|
|
130
|
+
shared.CompressionRegistry.register("QuintetLZ", () => new QuintetLZ());
|
|
131
|
+
}
|
|
132
|
+
registerCompressionProviders();
|
|
432
133
|
|
|
433
|
-
// src/rom/
|
|
434
|
-
var
|
|
435
|
-
constructor() {
|
|
436
|
-
this.
|
|
437
|
-
this.
|
|
438
|
-
this.
|
|
134
|
+
// src/rom/project.ts
|
|
135
|
+
var ProjectRoot = class _ProjectRoot {
|
|
136
|
+
constructor(config) {
|
|
137
|
+
this.config = config;
|
|
138
|
+
this.name = config.name;
|
|
139
|
+
this.romPath = config.romPath;
|
|
140
|
+
this.baseDir = config.baseDir;
|
|
141
|
+
this.system = config.system;
|
|
142
|
+
this.database = config.database;
|
|
143
|
+
this.flipsPath = config.flipsPath;
|
|
144
|
+
this.resources = config.resources;
|
|
145
|
+
this.databasePath = config.databasePath;
|
|
146
|
+
this.systemPath = config.systemPath;
|
|
147
|
+
this.compression = config.compression;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Load project from file or directory
|
|
151
|
+
*/
|
|
152
|
+
static async load(path) {
|
|
153
|
+
path = path || "./project.json";
|
|
154
|
+
try {
|
|
155
|
+
const config = await shared.readJsonFile(path);
|
|
156
|
+
if (!config.baseDir) {
|
|
157
|
+
config.baseDir = await shared.getDirectory(path);
|
|
158
|
+
}
|
|
159
|
+
return new _ProjectRoot(config);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.warn(`Failed to load project file: ${error}. Using directory-based configuration.`);
|
|
162
|
+
}
|
|
163
|
+
const defaultConfig = {
|
|
164
|
+
name: "GaiaLabs",
|
|
165
|
+
baseDir: path,
|
|
166
|
+
flipsPath: process?.env?.flips_path,
|
|
167
|
+
romPath: process?.env?.rom_path || "",
|
|
168
|
+
database: "us",
|
|
169
|
+
databasePath: `${path}/db/us`,
|
|
170
|
+
systemPath: `${path}/db/snes`,
|
|
171
|
+
resources: {}
|
|
172
|
+
};
|
|
173
|
+
return new _ProjectRoot(defaultConfig);
|
|
174
|
+
}
|
|
175
|
+
// /**
|
|
176
|
+
// * Build the ROM (simplified)
|
|
177
|
+
// */
|
|
178
|
+
// public async build(files: ChunkFile[], entryPoints: DbEntryPoint[]): Promise<void> {
|
|
179
|
+
// // Build ROM using rebuild writer
|
|
180
|
+
// const writer = new RomWriter(entryPoints, this.config.cartName, this.config.makerCode);
|
|
181
|
+
// await writer.repack(files);
|
|
182
|
+
// }
|
|
183
|
+
/**
|
|
184
|
+
* Dump database and extract ROM data with comprehensive assembly processing
|
|
185
|
+
*/
|
|
186
|
+
// public async dumpDatabase(): Promise<ChunkFile[]> {
|
|
187
|
+
// // Load database and ROM data
|
|
188
|
+
// const root = await DbRootUtils.fromFolder(this.databasePath!, this.systemPath!);
|
|
189
|
+
// const rom = await readFileAsBinary(this.romPath);
|
|
190
|
+
// //root.paths = this.resources;
|
|
191
|
+
// const chunkReader = new BlockReader(rom, root);
|
|
192
|
+
// const chunkFiles = chunkReader.analyzeAndResolve();
|
|
193
|
+
// const blockWriter = new BlockWriter(chunkReader);
|
|
194
|
+
// const writtenFiles = blockWriter.generateAllAsm(chunkFiles);
|
|
195
|
+
// return chunkFiles;
|
|
196
|
+
// }
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/rom/state.ts
|
|
200
|
+
var RomState = class _RomState {
|
|
201
|
+
constructor() {
|
|
202
|
+
// Basic memory buffers
|
|
203
|
+
this.cgram = new Uint8Array(512);
|
|
204
|
+
// Color palette RAM
|
|
205
|
+
this.vram = new Uint8Array(65536);
|
|
206
|
+
}
|
|
207
|
+
// Video RAM
|
|
208
|
+
/**
|
|
209
|
+
* Create ROM state from scene metadata.
|
|
210
|
+
* Parameters are currently unused but kept for future implementation.
|
|
211
|
+
*/
|
|
212
|
+
static async fromScene(_baseDir, _root, _metaFile, _id) {
|
|
213
|
+
return new _RomState();
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var RomStateUtils = class {
|
|
217
|
+
/** Create an empty ROM state */
|
|
218
|
+
static createEmpty() {
|
|
219
|
+
return new RomState();
|
|
220
|
+
}
|
|
221
|
+
/** Clear all data in the given ROM state */
|
|
222
|
+
static clear(state) {
|
|
223
|
+
state.cgram.fill(0);
|
|
224
|
+
state.vram.fill(0);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/rom/extraction/processor.ts
|
|
229
|
+
var ProcessorStateManager = class {
|
|
230
|
+
constructor() {
|
|
231
|
+
this.accumulatorFlags = /* @__PURE__ */ new Map();
|
|
232
|
+
this.indexFlags = /* @__PURE__ */ new Map();
|
|
233
|
+
this.bankNotes = /* @__PURE__ */ new Map();
|
|
439
234
|
this.stackPositions = /* @__PURE__ */ new Map();
|
|
440
235
|
}
|
|
441
236
|
/**
|
|
@@ -445,15 +240,15 @@ var ProcessorStateManager = class {
|
|
|
445
240
|
hydrateRegisters(position, reg) {
|
|
446
241
|
const acc = this.accumulatorFlags.get(position);
|
|
447
242
|
if (acc !== void 0) {
|
|
448
|
-
reg.accumulatorFlag = acc;
|
|
243
|
+
reg.accumulatorFlag = acc ?? void 0;
|
|
449
244
|
}
|
|
450
245
|
const ind = this.indexFlags.get(position);
|
|
451
246
|
if (ind !== void 0) {
|
|
452
|
-
reg.indexFlag = ind;
|
|
247
|
+
reg.indexFlag = ind ?? void 0;
|
|
453
248
|
}
|
|
454
249
|
const bnk = this.bankNotes.get(position);
|
|
455
250
|
if (bnk !== void 0) {
|
|
456
|
-
reg.dataBank = bnk;
|
|
251
|
+
reg.dataBank = bnk ?? void 0;
|
|
457
252
|
}
|
|
458
253
|
const stack = this.stackPositions.get(position);
|
|
459
254
|
if (stack !== void 0) {
|
|
@@ -603,7 +398,7 @@ var ReferenceManager = class {
|
|
|
603
398
|
}
|
|
604
399
|
createTypeName(type, location) {
|
|
605
400
|
let name = type.toLowerCase();
|
|
606
|
-
while (name.length > 0 &&
|
|
401
|
+
while (name.length > 0 && shared.BlockReaderConstants.POINTER_CHARACTERS.includes(name[0])) {
|
|
607
402
|
name = name.substring(1) + "_list";
|
|
608
403
|
}
|
|
609
404
|
return `${name}_${location.toString(16).toUpperCase().padStart(6, "0")}`;
|
|
@@ -630,7 +425,7 @@ var ReferenceManager = class {
|
|
|
630
425
|
return void 0;
|
|
631
426
|
}
|
|
632
427
|
resolveName(location, type, isBranch) {
|
|
633
|
-
const prefix =
|
|
428
|
+
const prefix = shared.Address.codeFromType(type);
|
|
634
429
|
let name;
|
|
635
430
|
let label = null;
|
|
636
431
|
let resolvedLocation = location;
|
|
@@ -649,7 +444,7 @@ var ReferenceManager = class {
|
|
|
649
444
|
return `${prefix || ""}${name}${label || ""}`;
|
|
650
445
|
}
|
|
651
446
|
findClosestReference(location) {
|
|
652
|
-
let closestDistance =
|
|
447
|
+
let closestDistance = shared.BlockReaderConstants.REF_SEARCH_MAX_RANGE;
|
|
653
448
|
let closestName = null;
|
|
654
449
|
let closestLocation = null;
|
|
655
450
|
for (const [entryKey, entryValue] of this.nameTable) {
|
|
@@ -675,12 +470,12 @@ var ReferenceManager = class {
|
|
|
675
470
|
const absOffset = Math.abs(offset);
|
|
676
471
|
let label = null;
|
|
677
472
|
const structType = this.structTable.get(rewrite);
|
|
678
|
-
if (structType ===
|
|
473
|
+
if (structType === shared.BlockReaderConstants.WIDE_STRING_TYPE) {
|
|
679
474
|
this.markerTable.set(rewrite, absOffset);
|
|
680
475
|
this.markerTable.set(location, absOffset);
|
|
681
|
-
label = cmd === "-" ?
|
|
476
|
+
label = cmd === "-" ? shared.BlockReaderConstants.NEGATIVE_MARKER_FORMAT : shared.BlockReaderConstants.MARKER_FORMAT;
|
|
682
477
|
} else {
|
|
683
|
-
const formatString = cmd === "-" ?
|
|
478
|
+
const formatString = cmd === "-" ? shared.BlockReaderConstants.NEGATIVE_OFFSET_FORMAT : shared.BlockReaderConstants.OFFSET_FORMAT;
|
|
684
479
|
label = formatString.replace("{0:X}", absOffset.toString(16).toUpperCase());
|
|
685
480
|
}
|
|
686
481
|
return { location: rewrite, label };
|
|
@@ -691,10 +486,10 @@ var ReferenceManager = class {
|
|
|
691
486
|
}
|
|
692
487
|
let result = closestName;
|
|
693
488
|
const structType = this.structTable.get(closestLocation);
|
|
694
|
-
if (structType ===
|
|
489
|
+
if (structType === shared.BlockReaderConstants.WIDE_STRING_TYPE) {
|
|
695
490
|
this.markerTable.set(closestLocation, closestDistance);
|
|
696
491
|
this.markerTable.set(location, closestDistance);
|
|
697
|
-
result +=
|
|
492
|
+
result += shared.BlockReaderConstants.MARKER_FORMAT;
|
|
698
493
|
} else {
|
|
699
494
|
result += `+${closestDistance.toString(16).toUpperCase()}`;
|
|
700
495
|
}
|
|
@@ -811,9 +606,9 @@ var CopCommandProcessor = class {
|
|
|
811
606
|
*/
|
|
812
607
|
parseCopCommand(copDef, operands) {
|
|
813
608
|
for (const partStr of copDef.parts) {
|
|
814
|
-
const addrType =
|
|
815
|
-
const isPtr = addrType !==
|
|
816
|
-
const referenceType = isPtr ? partStr.substring(1) : this._blockReader.
|
|
609
|
+
const addrType = shared.Address.typeFromCode(partStr[0]);
|
|
610
|
+
const isPtr = addrType !== shared.AddressType.Unknown;
|
|
611
|
+
const referenceType = isPtr ? partStr.substring(1) : this._blockReader._currentAsmBlock.structName ?? "Binary";
|
|
817
612
|
const memberTypeName = isPtr ? addrType.toString() : partStr;
|
|
818
613
|
const memberType = this.tryParseMemberType(memberTypeName);
|
|
819
614
|
if (memberType === null) {
|
|
@@ -830,7 +625,7 @@ var CopCommandProcessor = class {
|
|
|
830
625
|
}
|
|
831
626
|
tryParseMemberType(memberTypeName) {
|
|
832
627
|
const upperName = memberTypeName.toUpperCase();
|
|
833
|
-
for (const [key, value] of Object.entries(
|
|
628
|
+
for (const [key, value] of Object.entries(shared.MemberType)) {
|
|
834
629
|
if (key.toUpperCase() === upperName) {
|
|
835
630
|
return value;
|
|
836
631
|
}
|
|
@@ -839,12 +634,12 @@ var CopCommandProcessor = class {
|
|
|
839
634
|
}
|
|
840
635
|
getMemberTypeSize(memberType) {
|
|
841
636
|
switch (memberType) {
|
|
842
|
-
case
|
|
637
|
+
case shared.MemberType.Byte:
|
|
843
638
|
return 1;
|
|
844
|
-
case
|
|
845
|
-
case
|
|
639
|
+
case shared.MemberType.Word:
|
|
640
|
+
case shared.MemberType.Offset:
|
|
846
641
|
return 2;
|
|
847
|
-
case
|
|
642
|
+
case shared.MemberType.Address:
|
|
848
643
|
return 3;
|
|
849
644
|
default:
|
|
850
645
|
throw new Error("Unsupported COP member type");
|
|
@@ -852,13 +647,13 @@ var CopCommandProcessor = class {
|
|
|
852
647
|
}
|
|
853
648
|
readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType) {
|
|
854
649
|
switch (memberType) {
|
|
855
|
-
case
|
|
856
|
-
return
|
|
857
|
-
case
|
|
858
|
-
return
|
|
859
|
-
case
|
|
650
|
+
case shared.MemberType.Byte:
|
|
651
|
+
return new shared.Byte(this._romDataReader.readByte());
|
|
652
|
+
case shared.MemberType.Word:
|
|
653
|
+
return new shared.Word(this._romDataReader.readUShort());
|
|
654
|
+
case shared.MemberType.Offset:
|
|
860
655
|
return this.createCopLocation(this._romDataReader.readUShort(), null, partStr, isPtr, referenceType, addrType);
|
|
861
|
-
case
|
|
656
|
+
case shared.MemberType.Address:
|
|
862
657
|
return this.createCopLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), partStr, isPtr, referenceType, addrType);
|
|
863
658
|
default:
|
|
864
659
|
throw new Error("Unsupported COP member type");
|
|
@@ -866,24 +661,24 @@ var CopCommandProcessor = class {
|
|
|
866
661
|
}
|
|
867
662
|
createCopLocation(offset, bank, partStr, isPtr, otherStr, type) {
|
|
868
663
|
if (bank === null && offset === 0) {
|
|
869
|
-
return
|
|
664
|
+
return new shared.Word(offset);
|
|
870
665
|
}
|
|
871
|
-
const addr = new
|
|
872
|
-
if (addr.space ===
|
|
666
|
+
const addr = new shared.Address(bank ?? this._romDataReader.position >> 16, offset);
|
|
667
|
+
if (addr.space === shared.AddressSpace.ROM) {
|
|
873
668
|
const location = addr.toInt();
|
|
874
669
|
if (partStr !== "Address" && isPtr && !this._blockReader._root.rewrites[location]) {
|
|
875
670
|
this._blockReader.noteType(location, otherStr, true);
|
|
876
671
|
}
|
|
877
|
-
if (type ===
|
|
878
|
-
type = this.tryParseAddressType(partStr) ??
|
|
672
|
+
if (type === shared.AddressType.Unknown) {
|
|
673
|
+
type = this.tryParseAddressType(partStr) ?? shared.AddressType.Unknown;
|
|
879
674
|
}
|
|
880
|
-
return new
|
|
675
|
+
return new shared.LocationWrapper(location, type);
|
|
881
676
|
}
|
|
882
677
|
return addr;
|
|
883
678
|
}
|
|
884
679
|
tryParseAddressType(addressTypeName) {
|
|
885
680
|
const upperName = addressTypeName.toUpperCase();
|
|
886
|
-
for (const [key, value] of Object.entries(
|
|
681
|
+
for (const [key, value] of Object.entries(shared.AddressType)) {
|
|
887
682
|
if (key.toUpperCase() === upperName) {
|
|
888
683
|
return value;
|
|
889
684
|
}
|
|
@@ -912,51 +707,54 @@ var AddressingModeHandler = class {
|
|
|
912
707
|
processAddressingMode(code, context, reg) {
|
|
913
708
|
const operands = [];
|
|
914
709
|
switch (code.mode) {
|
|
915
|
-
case
|
|
916
|
-
case
|
|
710
|
+
case "Stack":
|
|
711
|
+
case "Implied":
|
|
712
|
+
case "Accumulator":
|
|
917
713
|
this.handleStackOrImpliedMode(code.mnem, reg);
|
|
918
714
|
break;
|
|
919
|
-
case
|
|
715
|
+
case "Immediate":
|
|
920
716
|
this.handleImmediateMode(code.mnem, context.size, operands, reg);
|
|
921
717
|
break;
|
|
922
|
-
case
|
|
923
|
-
case
|
|
924
|
-
case
|
|
925
|
-
case
|
|
926
|
-
case
|
|
927
|
-
case
|
|
718
|
+
case "AbsoluteIndirect":
|
|
719
|
+
case "AbsoluteIndirectLong":
|
|
720
|
+
case "AbsoluteIndexedIndirect":
|
|
721
|
+
case "Absolute":
|
|
722
|
+
case "AbsoluteIndexedX":
|
|
723
|
+
case "AbsoluteIndexedY":
|
|
928
724
|
this.handleAbsoluteMode(code.mnem, void 0, context.nextAddress, reg.dataBank, operands, reg);
|
|
929
725
|
break;
|
|
930
|
-
case
|
|
931
|
-
case
|
|
726
|
+
case "AbsoluteLong":
|
|
727
|
+
case "AbsoluteLongIndexedX":
|
|
932
728
|
this.handleAbsoluteLongMode(code.mnem, operands, reg);
|
|
933
729
|
break;
|
|
934
|
-
case
|
|
730
|
+
case "BlockMove":
|
|
935
731
|
this.handleBlockMoveMode(operands, context);
|
|
936
732
|
break;
|
|
937
|
-
case
|
|
938
|
-
case
|
|
939
|
-
case
|
|
940
|
-
case
|
|
941
|
-
case
|
|
942
|
-
case
|
|
943
|
-
case
|
|
944
|
-
case
|
|
733
|
+
case "DirectPage":
|
|
734
|
+
case "DirectPageIndexedIndirectX":
|
|
735
|
+
case "DirectPageIndexedX":
|
|
736
|
+
case "DirectPageIndexedY":
|
|
737
|
+
case "DirectPageIndirect":
|
|
738
|
+
case "DirectPageIndirectIndexedY":
|
|
739
|
+
case "DirectPageIndirectLong":
|
|
740
|
+
case "DirectPageIndirectLongIndexedY":
|
|
945
741
|
this.handleDirectPageMode(operands);
|
|
946
742
|
break;
|
|
947
|
-
case
|
|
743
|
+
case "PCRelative":
|
|
948
744
|
this.handlePCRelativeMode(operands, context.nextAddress, reg, false);
|
|
949
745
|
break;
|
|
950
|
-
case
|
|
746
|
+
case "PCRelativeLong":
|
|
951
747
|
this.handlePCRelativeMode(operands, context.nextAddress, reg, true);
|
|
952
748
|
break;
|
|
953
|
-
case
|
|
954
|
-
case
|
|
749
|
+
case "StackRelative":
|
|
750
|
+
case "StackRelativeIndirectIndexedY":
|
|
955
751
|
this.handleStackRelativeMode(operands);
|
|
956
752
|
break;
|
|
957
|
-
case
|
|
753
|
+
case "StackInterrupt":
|
|
958
754
|
this.handleStackInterruptMode(code.mnem, operands, context);
|
|
959
755
|
break;
|
|
756
|
+
default:
|
|
757
|
+
throw new Error(`Unknown addressing mode: ${code}`);
|
|
960
758
|
}
|
|
961
759
|
return operands;
|
|
962
760
|
}
|
|
@@ -966,7 +764,7 @@ var AddressingModeHandler = class {
|
|
|
966
764
|
this.updateRegisterForImmediateInstruction(mnemonic, operand, reg);
|
|
967
765
|
}
|
|
968
766
|
readImmediateOperand(size) {
|
|
969
|
-
return size === 3 ?
|
|
767
|
+
return size === 3 ? new shared.Word(this._dataReader.readUShort()) : new shared.Byte(this._dataReader.readByte());
|
|
970
768
|
}
|
|
971
769
|
updateRegisterForImmediateInstruction(mnemonic, operand, reg) {
|
|
972
770
|
const value = typeof operand === "number" ? operand : operand.value;
|
|
@@ -996,19 +794,19 @@ var AddressingModeHandler = class {
|
|
|
996
794
|
updateStatusFlags(mnemonic, flagValue) {
|
|
997
795
|
const flag = flagValue;
|
|
998
796
|
const isSep = mnemonic === "SEP";
|
|
999
|
-
if (flag &
|
|
797
|
+
if (flag & shared.StatusFlags.AccumulatorMode) {
|
|
1000
798
|
this._blockReader.AccumulatorFlags.set(this._dataReader.position, isSep);
|
|
1001
799
|
}
|
|
1002
|
-
if (flag &
|
|
800
|
+
if (flag & shared.StatusFlags.IndexMode) {
|
|
1003
801
|
this._blockReader.IndexFlags.set(this._dataReader.position, isSep);
|
|
1004
802
|
}
|
|
1005
803
|
}
|
|
1006
804
|
handleAbsoluteLongMode(mnemonic, operands, reg) {
|
|
1007
805
|
const refLoc = this._dataReader.readUShort();
|
|
1008
806
|
const bank = this._dataReader.readByte();
|
|
1009
|
-
const address = new
|
|
1010
|
-
if (address.space ===
|
|
1011
|
-
const wrapper = new
|
|
807
|
+
const address = new shared.Address(bank, refLoc);
|
|
808
|
+
if (address.space === shared.AddressSpace.ROM) {
|
|
809
|
+
const wrapper = new shared.LocationWrapper(address.toInt(), shared.AddressType.Address);
|
|
1012
810
|
if (this.isJumpInstruction(mnemonic)) {
|
|
1013
811
|
this._blockReader.noteType(wrapper.location, "Code", false, reg);
|
|
1014
812
|
}
|
|
@@ -1018,24 +816,24 @@ var AddressingModeHandler = class {
|
|
|
1018
816
|
}
|
|
1019
817
|
}
|
|
1020
818
|
handleBlockMoveMode(operands, context) {
|
|
1021
|
-
operands.push(
|
|
819
|
+
operands.push(new shared.Byte(this._dataReader.readByte()));
|
|
1022
820
|
context.xForm2 = this._transformProcessor.getTransform();
|
|
1023
|
-
operands.push(
|
|
821
|
+
operands.push(new shared.Byte(this._dataReader.readByte()));
|
|
1024
822
|
}
|
|
1025
823
|
handleDirectPageMode(operands) {
|
|
1026
|
-
operands.push(
|
|
824
|
+
operands.push(new shared.Byte(this._dataReader.readByte()));
|
|
1027
825
|
}
|
|
1028
826
|
handlePCRelativeMode(operands, nextAddress, reg, isLong) {
|
|
1029
827
|
const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
|
|
1030
828
|
this._blockReader.noteType(relative, "Code", void 0, reg);
|
|
1031
|
-
operands.push(new
|
|
829
|
+
operands.push(new shared.LocationWrapper(relative, shared.AddressType.Relative));
|
|
1032
830
|
}
|
|
1033
831
|
handleStackRelativeMode(operands) {
|
|
1034
|
-
operands.push(
|
|
832
|
+
operands.push(new shared.Byte(this._dataReader.readByte()));
|
|
1035
833
|
}
|
|
1036
834
|
handleStackInterruptMode(mnemonic, operands, context) {
|
|
1037
835
|
const cmd = this._dataReader.readByte();
|
|
1038
|
-
operands.push(cmd);
|
|
836
|
+
operands.push(new shared.Byte(cmd));
|
|
1039
837
|
if (mnemonic === "COP") {
|
|
1040
838
|
const copDef = this._blockReader._root.copDef[cmd];
|
|
1041
839
|
if (!copDef) {
|
|
@@ -1057,9 +855,9 @@ var AddressingModeHandler = class {
|
|
|
1057
855
|
}
|
|
1058
856
|
const isJump = isPush || this.isJumpInstruction(mnemonic);
|
|
1059
857
|
const bank = xBank1 ?? (isJump ? this._dataReader.position >> 16 : dataBank ?? 129);
|
|
1060
|
-
const addr = new
|
|
858
|
+
const addr = new shared.Address(bank, refLoc);
|
|
1061
859
|
if (addr.isROM) {
|
|
1062
|
-
const wrapper = new
|
|
860
|
+
const wrapper = new shared.LocationWrapper(addr.toInt(), shared.AddressType.Offset);
|
|
1063
861
|
if (isJump) {
|
|
1064
862
|
const name = this._blockReader.noteType(wrapper.location, "Code", isPush, registers);
|
|
1065
863
|
if (isPush) {
|
|
@@ -1145,12 +943,12 @@ var TransformProcessor = class _TransformProcessor {
|
|
|
1145
943
|
}
|
|
1146
944
|
cleanTransformName(transform) {
|
|
1147
945
|
let name = transform;
|
|
1148
|
-
while (name.length > 0 &&
|
|
946
|
+
while (name.length > 0 && shared.RomProcessingConstants.ADDRESS_SPACE.includes(name[0])) {
|
|
1149
947
|
name = name.substring(1);
|
|
1150
948
|
}
|
|
1151
949
|
let mathIndex = -1;
|
|
1152
950
|
for (let i = 0; i < name.length; i++) {
|
|
1153
|
-
if (
|
|
951
|
+
if (shared.RomProcessingConstants.OPERATORS.includes(name[i])) {
|
|
1154
952
|
mathIndex = i;
|
|
1155
953
|
break;
|
|
1156
954
|
}
|
|
@@ -1169,169 +967,6 @@ var TransformProcessor = class _TransformProcessor {
|
|
|
1169
967
|
return match ? parseInt(match[1], 16) : null;
|
|
1170
968
|
}
|
|
1171
969
|
};
|
|
1172
|
-
var SfxReader = class {
|
|
1173
|
-
constructor(romData, dbRoot) {
|
|
1174
|
-
this._romData = romData;
|
|
1175
|
-
this._dbRoot = dbRoot;
|
|
1176
|
-
this._location = dbRoot.config.sfxLocation;
|
|
1177
|
-
this._count = dbRoot.config.sfxCount;
|
|
1178
|
-
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Reads a byte from the rom data
|
|
1181
|
-
*/
|
|
1182
|
-
readByte() {
|
|
1183
|
-
if ((this._location & gaiaShared.Address.UPPER_BANK) !== 0) {
|
|
1184
|
-
this._location += gaiaShared.Address.UPPER_BANK;
|
|
1185
|
-
}
|
|
1186
|
-
return this._romData[this._location++];
|
|
1187
|
-
}
|
|
1188
|
-
/**
|
|
1189
|
-
* Reads a short from the rom data
|
|
1190
|
-
*/
|
|
1191
|
-
readShort() {
|
|
1192
|
-
return this.readByte() | this.readByte() << 8;
|
|
1193
|
-
}
|
|
1194
|
-
/**
|
|
1195
|
-
* Extracts the sfx from the rom data to the given output path
|
|
1196
|
-
*/
|
|
1197
|
-
async extract(outPath) {
|
|
1198
|
-
const res = this.getPath(gaiaShared.BinType.Sound);
|
|
1199
|
-
if (res?.folder) {
|
|
1200
|
-
outPath = `${outPath}/${res.folder}`;
|
|
1201
|
-
}
|
|
1202
|
-
const isNode = typeof window === "undefined";
|
|
1203
|
-
const fs2 = isNode ? await import('fs') : null;
|
|
1204
|
-
if (isNode) {
|
|
1205
|
-
fs2.mkdirSync(outPath, { recursive: true });
|
|
1206
|
-
}
|
|
1207
|
-
for (let i = 0; i < this._count; i++) {
|
|
1208
|
-
const size = this.readShort();
|
|
1209
|
-
const filePath = `${outPath}/sfx${i.toString(16).padStart(2, "0").toUpperCase()}.${res?.extension || "bin"}`;
|
|
1210
|
-
if (isNode) {
|
|
1211
|
-
if (fs2.existsSync(filePath)) {
|
|
1212
|
-
this._location += size;
|
|
1213
|
-
} else {
|
|
1214
|
-
const sfxData = new Uint8Array(size);
|
|
1215
|
-
for (let x = 0; x < size; x++) {
|
|
1216
|
-
sfxData[x] = this.readByte();
|
|
1217
|
-
}
|
|
1218
|
-
fs2.writeFileSync(filePath, sfxData);
|
|
1219
|
-
}
|
|
1220
|
-
} else {
|
|
1221
|
-
this._location += size;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Gets the path information for a binary type
|
|
1227
|
-
* TODO: This should be implemented in DbRoot
|
|
1228
|
-
*/
|
|
1229
|
-
getPath(binType) {
|
|
1230
|
-
switch (binType) {
|
|
1231
|
-
case gaiaShared.BinType.Sound:
|
|
1232
|
-
return { folder: "sound", extension: "sfc" };
|
|
1233
|
-
default:
|
|
1234
|
-
return null;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
|
-
var FileReader = class _FileReader {
|
|
1239
|
-
static {
|
|
1240
|
-
this.PALETTE_MIN_SIZE = 512;
|
|
1241
|
-
}
|
|
1242
|
-
constructor(romData, dbRoot, provider) {
|
|
1243
|
-
this._romData = romData;
|
|
1244
|
-
this._dbRoot = dbRoot;
|
|
1245
|
-
this._compression = provider;
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* Extracts all files from the ROM to the given output path
|
|
1249
|
-
*/
|
|
1250
|
-
async extract(outPath) {
|
|
1251
|
-
const isNode = typeof window === "undefined";
|
|
1252
|
-
const fs2 = isNode ? await import('fs') : null;
|
|
1253
|
-
for (const file of this._dbRoot.files) {
|
|
1254
|
-
let start = file.start;
|
|
1255
|
-
const res = this.getPath(file.type);
|
|
1256
|
-
let filePath = outPath;
|
|
1257
|
-
if (res.folder) {
|
|
1258
|
-
filePath = `${outPath}/${res.folder}`;
|
|
1259
|
-
if (isNode) {
|
|
1260
|
-
fs2.mkdirSync(filePath, { recursive: true });
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
filePath = `${filePath}/${file.name}.${res.extension}`;
|
|
1264
|
-
if (isNode && fs2.existsSync(filePath)) {
|
|
1265
|
-
continue;
|
|
1266
|
-
}
|
|
1267
|
-
let header = null;
|
|
1268
|
-
if (file.type === gaiaShared.BinType.Tilemap) {
|
|
1269
|
-
header = new Uint8Array([this._romData[start++], this._romData[start++]]);
|
|
1270
|
-
} else if (file.type === gaiaShared.BinType.Meta17) {
|
|
1271
|
-
header = new Uint8Array([
|
|
1272
|
-
this._romData[start++],
|
|
1273
|
-
this._romData[start++],
|
|
1274
|
-
this._romData[start++],
|
|
1275
|
-
this._romData[start++]
|
|
1276
|
-
]);
|
|
1277
|
-
}
|
|
1278
|
-
let length = file.end - start;
|
|
1279
|
-
let fileData;
|
|
1280
|
-
if (file.compressed === true) {
|
|
1281
|
-
const data = this._compression.expand(this._romData, start, length);
|
|
1282
|
-
fileData = this.processFileData(data, 0, data.length, header, file.type);
|
|
1283
|
-
} else {
|
|
1284
|
-
if (file.compressed !== null) {
|
|
1285
|
-
start += 2;
|
|
1286
|
-
length -= 2;
|
|
1287
|
-
}
|
|
1288
|
-
fileData = this.processFileData(this._romData, start, length, header, file.type);
|
|
1289
|
-
}
|
|
1290
|
-
if (isNode) {
|
|
1291
|
-
fs2.writeFileSync(filePath, fileData);
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
processFileData(data, position, length, header, type) {
|
|
1296
|
-
let totalLength = length;
|
|
1297
|
-
let headerLength = 0;
|
|
1298
|
-
if (header) {
|
|
1299
|
-
headerLength = header.length;
|
|
1300
|
-
totalLength += headerLength;
|
|
1301
|
-
}
|
|
1302
|
-
if (type === gaiaShared.BinType.Palette) {
|
|
1303
|
-
const remain = _FileReader.PALETTE_MIN_SIZE - length;
|
|
1304
|
-
if (remain > 0) {
|
|
1305
|
-
totalLength += remain;
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
const result = new Uint8Array(totalLength);
|
|
1309
|
-
let offset = 0;
|
|
1310
|
-
if (header) {
|
|
1311
|
-
result.set(header, offset);
|
|
1312
|
-
offset += headerLength;
|
|
1313
|
-
}
|
|
1314
|
-
result.set(data.slice(position, position + length), offset);
|
|
1315
|
-
offset += length;
|
|
1316
|
-
if (type === gaiaShared.BinType.Palette) {
|
|
1317
|
-
const remain = _FileReader.PALETTE_MIN_SIZE - length;
|
|
1318
|
-
if (remain > 0) {
|
|
1319
|
-
result.fill(0, offset, offset + remain);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
return result;
|
|
1323
|
-
}
|
|
1324
|
-
/**
|
|
1325
|
-
* Gets the path information for a binary type from DbRoot configuration
|
|
1326
|
-
*/
|
|
1327
|
-
getPath(binType) {
|
|
1328
|
-
const pathConfig = this._dbRoot.paths[binType] || this._dbRoot.paths[gaiaShared.BinType.Unknown];
|
|
1329
|
-
return {
|
|
1330
|
-
folder: pathConfig.folder,
|
|
1331
|
-
extension: pathConfig.extension
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
};
|
|
1335
970
|
|
|
1336
971
|
// src/utils/index.ts
|
|
1337
972
|
function indexOfAny(str, chars, startIndex = 0) {
|
|
@@ -1364,20 +999,20 @@ var StringReader = class _StringReader {
|
|
|
1364
999
|
builder.push(",");
|
|
1365
1000
|
}
|
|
1366
1001
|
switch (t) {
|
|
1367
|
-
case
|
|
1002
|
+
case shared.MemberType.Byte:
|
|
1368
1003
|
builder.push(this._romDataReader.readByte().toString(16).toUpperCase());
|
|
1369
1004
|
break;
|
|
1370
|
-
case
|
|
1005
|
+
case shared.MemberType.Word:
|
|
1371
1006
|
builder.push(this._romDataReader.readUShort().toString(16).toUpperCase());
|
|
1372
1007
|
break;
|
|
1373
|
-
case
|
|
1008
|
+
case shared.MemberType.Offset:
|
|
1374
1009
|
const loc = this._romDataReader.readUShort() | this._romDataReader.position & 4128768;
|
|
1375
1010
|
builder.push(`^${loc.toString(16).toUpperCase().padStart(6, "0")}`);
|
|
1376
1011
|
break;
|
|
1377
|
-
case
|
|
1012
|
+
case shared.MemberType.Address:
|
|
1378
1013
|
builder.push(`~${this._romDataReader.readAddress().toString(16).toUpperCase().padStart(6, "0")}`);
|
|
1379
1014
|
break;
|
|
1380
|
-
case
|
|
1015
|
+
case shared.MemberType.Binary:
|
|
1381
1016
|
let sfirst = true;
|
|
1382
1017
|
do {
|
|
1383
1018
|
const r = this._romDataReader.readByte();
|
|
@@ -1464,11 +1099,11 @@ var StringReader = class _StringReader {
|
|
|
1464
1099
|
const hexStr = str.substring(ix + 1, ix + 7);
|
|
1465
1100
|
const sloc = parseInt(hexStr, 16);
|
|
1466
1101
|
if (!isNaN(sloc)) {
|
|
1467
|
-
const addrs = new
|
|
1468
|
-
if (addrs.space ===
|
|
1102
|
+
const addrs = new shared.Address(sloc >> 16, sloc & 65535);
|
|
1103
|
+
if (addrs.space === shared.AddressSpace.ROM) {
|
|
1469
1104
|
this._blockReader.resolveInclude(sloc, false);
|
|
1470
|
-
const name = this._blockReader.resolveName(sloc,
|
|
1471
|
-
const opix = indexOfAny(name,
|
|
1105
|
+
const name = this._blockReader.resolveName(sloc, shared.AddressType.Unknown, false);
|
|
1106
|
+
const opix = indexOfAny(name, shared.RomProcessingConstants.OPERATORS);
|
|
1472
1107
|
if (opix > 0) {
|
|
1473
1108
|
const offsetStr = name.substring(opix + 1);
|
|
1474
1109
|
let offset;
|
|
@@ -1482,12 +1117,11 @@ var StringReader = class _StringReader {
|
|
|
1482
1117
|
}
|
|
1483
1118
|
name.substring(0, opix);
|
|
1484
1119
|
const target = sloc - offset;
|
|
1485
|
-
const [isOutside,
|
|
1486
|
-
if (
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
entry.Object.marker = offset;
|
|
1120
|
+
const [isOutside, block, part] = shared.ChunkFileUtils.isOutsideWithPart(this._blockReader._enrichedChunks, this._blockReader._currentChunk, sloc);
|
|
1121
|
+
if (part != null) {
|
|
1122
|
+
const entry = part.objList?.find((x) => typeof x === "object" && x !== null && "location" in x && "object" in x && x.location === target);
|
|
1123
|
+
if (entry && entry.object) {
|
|
1124
|
+
entry.object.marker = offset;
|
|
1491
1125
|
}
|
|
1492
1126
|
}
|
|
1493
1127
|
}
|
|
@@ -1508,10 +1142,10 @@ var TypeParser = class {
|
|
|
1508
1142
|
}
|
|
1509
1143
|
parseType(typeName, reg, depth, bank) {
|
|
1510
1144
|
if (typeName[0] === "&") {
|
|
1511
|
-
return this.parseLocation(this._romDataReader.readUShort(), bank, typeName.substring(1),
|
|
1145
|
+
return this.parseLocation(this._romDataReader.readUShort(), bank, typeName.substring(1), shared.AddressType.Offset);
|
|
1512
1146
|
}
|
|
1513
1147
|
if (typeName[0] === "@") {
|
|
1514
|
-
return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1),
|
|
1148
|
+
return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1), shared.AddressType.Address);
|
|
1515
1149
|
}
|
|
1516
1150
|
const stringType = this._stringTypes[typeName];
|
|
1517
1151
|
if (stringType) {
|
|
@@ -1520,17 +1154,17 @@ var TypeParser = class {
|
|
|
1520
1154
|
const mType = this.tryParseMemberType(typeName);
|
|
1521
1155
|
if (mType !== null) {
|
|
1522
1156
|
switch (mType) {
|
|
1523
|
-
case
|
|
1524
|
-
return
|
|
1525
|
-
case
|
|
1526
|
-
return
|
|
1527
|
-
case
|
|
1528
|
-
return this.parseLocation(this._romDataReader.readUShort(), bank, null,
|
|
1529
|
-
case
|
|
1530
|
-
return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null,
|
|
1531
|
-
case
|
|
1157
|
+
case shared.MemberType.Byte:
|
|
1158
|
+
return new shared.Byte(this._romDataReader.readByte());
|
|
1159
|
+
case shared.MemberType.Word:
|
|
1160
|
+
return new shared.Word(this.parseWordSafe());
|
|
1161
|
+
case shared.MemberType.Offset:
|
|
1162
|
+
return this.parseLocation(this._romDataReader.readUShort(), bank, null, shared.AddressType.Offset);
|
|
1163
|
+
case shared.MemberType.Address:
|
|
1164
|
+
return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, shared.AddressType.Address);
|
|
1165
|
+
case shared.MemberType.Binary:
|
|
1532
1166
|
return this.parseBinary();
|
|
1533
|
-
case
|
|
1167
|
+
case shared.MemberType.Code:
|
|
1534
1168
|
return this.parseCode(reg);
|
|
1535
1169
|
default:
|
|
1536
1170
|
throw new Error("Invalid member type");
|
|
@@ -1565,7 +1199,7 @@ var TypeParser = class {
|
|
|
1565
1199
|
const parts = new Array(memberCount);
|
|
1566
1200
|
const def = { name: targetType.name, parts };
|
|
1567
1201
|
for (let i = 0; i < memberCount; i++) {
|
|
1568
|
-
parts[i] = this.parseType(types[i], null, depth + 1);
|
|
1202
|
+
parts[i] = this.parseType(types[i], null, depth + 1, bank);
|
|
1569
1203
|
}
|
|
1570
1204
|
if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) {
|
|
1571
1205
|
this._romDataReader.position++;
|
|
@@ -1590,7 +1224,7 @@ var TypeParser = class {
|
|
|
1590
1224
|
}
|
|
1591
1225
|
tryParseMemberType(memberTypeName) {
|
|
1592
1226
|
const upperName = memberTypeName.toUpperCase();
|
|
1593
|
-
for (const [key, value] of Object.entries(
|
|
1227
|
+
for (const [key, value] of Object.entries(shared.MemberType)) {
|
|
1594
1228
|
if (key.toUpperCase() === upperName) {
|
|
1595
1229
|
return value;
|
|
1596
1230
|
}
|
|
@@ -1613,22 +1247,22 @@ var TypeParser = class {
|
|
|
1613
1247
|
return outBuffer;
|
|
1614
1248
|
}
|
|
1615
1249
|
parseLocation(offset, bank, typeName, addrType) {
|
|
1616
|
-
if (bank === void 0 && offset === 0) {
|
|
1617
|
-
return
|
|
1250
|
+
if ((bank === void 0 || bank === null) && offset === 0) {
|
|
1251
|
+
return new shared.Word(offset);
|
|
1618
1252
|
}
|
|
1619
1253
|
const resolvedBank = bank ?? this._romDataReader.position >> 16;
|
|
1620
|
-
const adrs = new
|
|
1621
|
-
if (adrs.space !==
|
|
1254
|
+
const adrs = new shared.Address(resolvedBank, offset);
|
|
1255
|
+
if (adrs.space !== shared.AddressSpace.ROM) {
|
|
1622
1256
|
return adrs;
|
|
1623
1257
|
}
|
|
1624
1258
|
const loc = adrs.toInt();
|
|
1625
|
-
if (this._blockReader.
|
|
1626
|
-
const resolvedTypeName = typeName ?? this._blockReader.
|
|
1259
|
+
if (this._blockReader._currentChunk && shared.ChunkFileUtils.isInside(this._blockReader._currentChunk, loc) && !this._blockReader._root.rewrites[loc]) {
|
|
1260
|
+
const resolvedTypeName = typeName ?? this._blockReader._currentAsmBlock.structName ?? "Binary";
|
|
1627
1261
|
this._referenceManager.tryAddStruct(loc, resolvedTypeName);
|
|
1628
1262
|
const referenceName = `${resolvedTypeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
|
|
1629
1263
|
this._referenceManager.tryAddName(loc, referenceName);
|
|
1630
1264
|
}
|
|
1631
|
-
return new
|
|
1265
|
+
return new shared.LocationWrapper(loc, addrType);
|
|
1632
1266
|
}
|
|
1633
1267
|
parseCode(reg) {
|
|
1634
1268
|
const opList = [];
|
|
@@ -1640,7 +1274,7 @@ var TypeParser = class {
|
|
|
1640
1274
|
break;
|
|
1641
1275
|
}
|
|
1642
1276
|
if (reg) {
|
|
1643
|
-
this._blockReader.
|
|
1277
|
+
this._blockReader.hydrateRegisters(reg);
|
|
1644
1278
|
}
|
|
1645
1279
|
const op = this._blockReader._asmReader.parseAsm(reg);
|
|
1646
1280
|
opList.push(op);
|
|
@@ -1648,6 +1282,93 @@ var TypeParser = class {
|
|
|
1648
1282
|
return opList;
|
|
1649
1283
|
}
|
|
1650
1284
|
};
|
|
1285
|
+
var AsmReader = class _AsmReader {
|
|
1286
|
+
static {
|
|
1287
|
+
// Constants
|
|
1288
|
+
this.PROCESSOR_FLAG_MASK = 223;
|
|
1289
|
+
}
|
|
1290
|
+
static {
|
|
1291
|
+
this.ACCUMULATOR_OP_MASK = 15;
|
|
1292
|
+
}
|
|
1293
|
+
static {
|
|
1294
|
+
this.ACCUMULATOR_OP_VALUE = 9;
|
|
1295
|
+
}
|
|
1296
|
+
static {
|
|
1297
|
+
this.VARIABLE_SIZE_INDICATOR = -2;
|
|
1298
|
+
}
|
|
1299
|
+
static {
|
|
1300
|
+
this.TWO_BYTES_SIZE = 2;
|
|
1301
|
+
}
|
|
1302
|
+
static {
|
|
1303
|
+
this.THREE_BYTES_SIZE = 3;
|
|
1304
|
+
}
|
|
1305
|
+
constructor(blockReader) {
|
|
1306
|
+
this._blockReader = blockReader;
|
|
1307
|
+
this._transformProcessor = new TransformProcessor(blockReader);
|
|
1308
|
+
this._addressingModeHandler = new AddressingModeHandler(blockReader, this._transformProcessor);
|
|
1309
|
+
this._romDataReader = blockReader._romDataReader;
|
|
1310
|
+
}
|
|
1311
|
+
parseAsm(reg) {
|
|
1312
|
+
const opStart = this._romDataReader.position;
|
|
1313
|
+
const opCode = this._romDataReader.readByte();
|
|
1314
|
+
const code = this._blockReader._root.opCodes[opCode];
|
|
1315
|
+
if (!code) {
|
|
1316
|
+
throw new Error("Unknown OpCode");
|
|
1317
|
+
}
|
|
1318
|
+
const operationContext = this.initializeOperation(code, reg, opStart);
|
|
1319
|
+
const operands = this._addressingModeHandler.processAddressingMode(code, operationContext, reg);
|
|
1320
|
+
this._transformProcessor.applyTransforms(operationContext.xForm1, operationContext.xForm2, operands);
|
|
1321
|
+
const op = new shared.Op(
|
|
1322
|
+
code,
|
|
1323
|
+
opStart,
|
|
1324
|
+
operands,
|
|
1325
|
+
this._romDataReader.position - opStart
|
|
1326
|
+
);
|
|
1327
|
+
if (operationContext.copDef) {
|
|
1328
|
+
op.copDef = operationContext.copDef;
|
|
1329
|
+
}
|
|
1330
|
+
return op;
|
|
1331
|
+
}
|
|
1332
|
+
clearDestinationRegister(code, reg) {
|
|
1333
|
+
switch (code.mnem) {
|
|
1334
|
+
case "LDA":
|
|
1335
|
+
reg.accumulator = void 0;
|
|
1336
|
+
break;
|
|
1337
|
+
case "LDX":
|
|
1338
|
+
reg.xIndex = void 0;
|
|
1339
|
+
break;
|
|
1340
|
+
case "LDY":
|
|
1341
|
+
reg.yIndex = void 0;
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
initializeOperation(code, reg, loc) {
|
|
1346
|
+
const size = this.calculateInstructionSize(code, reg);
|
|
1347
|
+
const next = loc + size;
|
|
1348
|
+
this.clearDestinationRegister(code, reg);
|
|
1349
|
+
const context = new OperationContext();
|
|
1350
|
+
context.size = size;
|
|
1351
|
+
context.nextAddress = next;
|
|
1352
|
+
context.xForm1 = this._transformProcessor.getTransform();
|
|
1353
|
+
context.xForm2 = null;
|
|
1354
|
+
context.copDef = null;
|
|
1355
|
+
return context;
|
|
1356
|
+
}
|
|
1357
|
+
calculateInstructionSize(code, reg) {
|
|
1358
|
+
const addrMode = this._blockReader._root.addrLookup[code.mode];
|
|
1359
|
+
let size = addrMode.size;
|
|
1360
|
+
if (size === _AsmReader.VARIABLE_SIZE_INDICATOR || code.mode === "Immediate") {
|
|
1361
|
+
if ((code.code & _AsmReader.PROCESSOR_FLAG_MASK) === 194) {
|
|
1362
|
+
size = _AsmReader.TWO_BYTES_SIZE;
|
|
1363
|
+
} else if ((code.code & _AsmReader.ACCUMULATOR_OP_MASK) === _AsmReader.ACCUMULATOR_OP_VALUE) {
|
|
1364
|
+
size = reg.accumulatorFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
1365
|
+
} else {
|
|
1366
|
+
size = reg.indexFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return size;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1651
1372
|
|
|
1652
1373
|
// src/assembly/Stack.ts
|
|
1653
1374
|
var Stack = class {
|
|
@@ -1700,10 +1421,10 @@ var Registers = class {
|
|
|
1700
1421
|
get statusFlags() {
|
|
1701
1422
|
let flags = 0;
|
|
1702
1423
|
if (this.accumulatorFlag ?? false) {
|
|
1703
|
-
flags |=
|
|
1424
|
+
flags |= shared.StatusFlags.AccumulatorMode;
|
|
1704
1425
|
}
|
|
1705
1426
|
if (this.indexFlag ?? false) {
|
|
1706
|
-
flags |=
|
|
1427
|
+
flags |= shared.StatusFlags.IndexMode;
|
|
1707
1428
|
}
|
|
1708
1429
|
return flags;
|
|
1709
1430
|
}
|
|
@@ -1711,8 +1432,8 @@ var Registers = class {
|
|
|
1711
1432
|
* Set the status flags
|
|
1712
1433
|
*/
|
|
1713
1434
|
set statusFlags(value) {
|
|
1714
|
-
this.accumulatorFlag = (value &
|
|
1715
|
-
this.indexFlag = (value &
|
|
1435
|
+
this.accumulatorFlag = (value & shared.StatusFlags.AccumulatorMode) !== 0;
|
|
1436
|
+
this.indexFlag = (value & shared.StatusFlags.IndexMode) !== 0;
|
|
1716
1437
|
}
|
|
1717
1438
|
/**
|
|
1718
1439
|
* Reset all registers to initial state
|
|
@@ -1728,506 +1449,45 @@ var Registers = class {
|
|
|
1728
1449
|
this.stack.reset();
|
|
1729
1450
|
}
|
|
1730
1451
|
};
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
this.
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
}
|
|
1752
|
-
};
|
|
1753
|
-
var OpCode = class {
|
|
1754
|
-
constructor(code, mnem, mode, size) {
|
|
1755
|
-
this.code = code;
|
|
1756
|
-
this.mnem = mnem;
|
|
1757
|
-
this.mode = mode;
|
|
1758
|
-
this.size = size;
|
|
1759
|
-
}
|
|
1760
|
-
};
|
|
1761
|
-
var ALL_OPCODES = {
|
|
1762
|
-
// ADC - Add with Carry
|
|
1763
|
-
105: new OpCode(105, "ADC", gaiaShared.AddressingMode.Immediate, -2),
|
|
1764
|
-
109: new OpCode(109, "ADC", gaiaShared.AddressingMode.Absolute, 3),
|
|
1765
|
-
111: new OpCode(111, "ADC", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1766
|
-
101: new OpCode(101, "ADC", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1767
|
-
114: new OpCode(114, "ADC", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1768
|
-
103: new OpCode(103, "ADC", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1769
|
-
125: new OpCode(125, "ADC", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1770
|
-
127: new OpCode(127, "ADC", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1771
|
-
121: new OpCode(121, "ADC", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1772
|
-
117: new OpCode(117, "ADC", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1773
|
-
97: new OpCode(97, "ADC", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1774
|
-
113: new OpCode(113, "ADC", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1775
|
-
119: new OpCode(119, "ADC", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1776
|
-
99: new OpCode(99, "ADC", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1777
|
-
115: new OpCode(115, "ADC", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1778
|
-
// AND - Logical AND
|
|
1779
|
-
41: new OpCode(41, "AND", gaiaShared.AddressingMode.Immediate, -2),
|
|
1780
|
-
45: new OpCode(45, "AND", gaiaShared.AddressingMode.Absolute, 3),
|
|
1781
|
-
47: new OpCode(47, "AND", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1782
|
-
37: new OpCode(37, "AND", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1783
|
-
50: new OpCode(50, "AND", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1784
|
-
39: new OpCode(39, "AND", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1785
|
-
61: new OpCode(61, "AND", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1786
|
-
63: new OpCode(63, "AND", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1787
|
-
57: new OpCode(57, "AND", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1788
|
-
53: new OpCode(53, "AND", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1789
|
-
33: new OpCode(33, "AND", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1790
|
-
49: new OpCode(49, "AND", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1791
|
-
55: new OpCode(55, "AND", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1792
|
-
35: new OpCode(35, "AND", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1793
|
-
51: new OpCode(51, "AND", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1794
|
-
// ASL - Arithmetic Shift Left
|
|
1795
|
-
10: new OpCode(10, "ASL", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1796
|
-
14: new OpCode(14, "ASL", gaiaShared.AddressingMode.Absolute, 3),
|
|
1797
|
-
6: new OpCode(6, "ASL", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1798
|
-
30: new OpCode(30, "ASL", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1799
|
-
22: new OpCode(22, "ASL", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1800
|
-
// Branch instructions
|
|
1801
|
-
16: new OpCode(16, "BPL", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1802
|
-
48: new OpCode(48, "BMI", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1803
|
-
128: new OpCode(128, "BRA", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1804
|
-
130: new OpCode(130, "BRL", gaiaShared.AddressingMode.PCRelativeLong, 3),
|
|
1805
|
-
144: new OpCode(144, "BCC", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1806
|
-
176: new OpCode(176, "BCS", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1807
|
-
208: new OpCode(208, "BNE", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1808
|
-
240: new OpCode(240, "BEQ", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1809
|
-
80: new OpCode(80, "BVC", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1810
|
-
112: new OpCode(112, "BVS", gaiaShared.AddressingMode.PCRelative, 2),
|
|
1811
|
-
// BIT - Bit Test
|
|
1812
|
-
137: new OpCode(137, "BIT", gaiaShared.AddressingMode.Immediate, -2),
|
|
1813
|
-
44: new OpCode(44, "BIT", gaiaShared.AddressingMode.Absolute, 3),
|
|
1814
|
-
36: new OpCode(36, "BIT", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1815
|
-
60: new OpCode(60, "BIT", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1816
|
-
52: new OpCode(52, "BIT", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1817
|
-
// BRK - Break
|
|
1818
|
-
0: new OpCode(0, "BRK", gaiaShared.AddressingMode.StackInterrupt, 2),
|
|
1819
|
-
// Clear flag instructions
|
|
1820
|
-
24: new OpCode(24, "CLC", gaiaShared.AddressingMode.Implied, 1),
|
|
1821
|
-
216: new OpCode(216, "CLD", gaiaShared.AddressingMode.Implied, 1),
|
|
1822
|
-
88: new OpCode(88, "CLI", gaiaShared.AddressingMode.Implied, 1),
|
|
1823
|
-
184: new OpCode(184, "CLV", gaiaShared.AddressingMode.Implied, 1),
|
|
1824
|
-
// CMP - Compare Accumulator
|
|
1825
|
-
201: new OpCode(201, "CMP", gaiaShared.AddressingMode.Immediate, -2),
|
|
1826
|
-
205: new OpCode(205, "CMP", gaiaShared.AddressingMode.Absolute, 3),
|
|
1827
|
-
207: new OpCode(207, "CMP", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1828
|
-
197: new OpCode(197, "CMP", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1829
|
-
210: new OpCode(210, "CMP", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1830
|
-
199: new OpCode(199, "CMP", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1831
|
-
221: new OpCode(221, "CMP", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1832
|
-
223: new OpCode(223, "CMP", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1833
|
-
217: new OpCode(217, "CMP", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1834
|
-
213: new OpCode(213, "CMP", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1835
|
-
193: new OpCode(193, "CMP", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1836
|
-
209: new OpCode(209, "CMP", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1837
|
-
215: new OpCode(215, "CMP", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1838
|
-
195: new OpCode(195, "CMP", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1839
|
-
211: new OpCode(211, "CMP", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1840
|
-
// COP - Coprocessor Instruction
|
|
1841
|
-
2: new OpCode(2, "COP", gaiaShared.AddressingMode.StackInterrupt, 2),
|
|
1842
|
-
// CPX - Compare X Register
|
|
1843
|
-
224: new OpCode(224, "CPX", gaiaShared.AddressingMode.Immediate, -2),
|
|
1844
|
-
236: new OpCode(236, "CPX", gaiaShared.AddressingMode.Absolute, 3),
|
|
1845
|
-
228: new OpCode(228, "CPX", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1846
|
-
// CPY - Compare Y Register
|
|
1847
|
-
192: new OpCode(192, "CPY", gaiaShared.AddressingMode.Immediate, -2),
|
|
1848
|
-
204: new OpCode(204, "CPY", gaiaShared.AddressingMode.Absolute, 3),
|
|
1849
|
-
196: new OpCode(196, "CPY", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1850
|
-
// DEC - Decrement
|
|
1851
|
-
58: new OpCode(58, "DEC", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1852
|
-
206: new OpCode(206, "DEC", gaiaShared.AddressingMode.Absolute, 3),
|
|
1853
|
-
198: new OpCode(198, "DEC", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1854
|
-
222: new OpCode(222, "DEC", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1855
|
-
214: new OpCode(214, "DEC", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1856
|
-
// DEX/DEY - Decrement Index Registers
|
|
1857
|
-
202: new OpCode(202, "DEX", gaiaShared.AddressingMode.Implied, 1),
|
|
1858
|
-
136: new OpCode(136, "DEY", gaiaShared.AddressingMode.Implied, 1),
|
|
1859
|
-
// EOR - Exclusive OR
|
|
1860
|
-
73: new OpCode(73, "EOR", gaiaShared.AddressingMode.Immediate, -2),
|
|
1861
|
-
77: new OpCode(77, "EOR", gaiaShared.AddressingMode.Absolute, 3),
|
|
1862
|
-
79: new OpCode(79, "EOR", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1863
|
-
69: new OpCode(69, "EOR", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1864
|
-
82: new OpCode(82, "EOR", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1865
|
-
71: new OpCode(71, "EOR", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1866
|
-
93: new OpCode(93, "EOR", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1867
|
-
95: new OpCode(95, "EOR", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1868
|
-
89: new OpCode(89, "EOR", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1869
|
-
85: new OpCode(85, "EOR", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1870
|
-
65: new OpCode(65, "EOR", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1871
|
-
81: new OpCode(81, "EOR", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1872
|
-
87: new OpCode(87, "EOR", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1873
|
-
67: new OpCode(67, "EOR", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1874
|
-
83: new OpCode(83, "EOR", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1875
|
-
// INC - Increment
|
|
1876
|
-
26: new OpCode(26, "INC", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1877
|
-
238: new OpCode(238, "INC", gaiaShared.AddressingMode.Absolute, 3),
|
|
1878
|
-
230: new OpCode(230, "INC", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1879
|
-
254: new OpCode(254, "INC", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1880
|
-
246: new OpCode(246, "INC", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1881
|
-
// INX/INY - Increment Index Registers
|
|
1882
|
-
232: new OpCode(232, "INX", gaiaShared.AddressingMode.Implied, 1),
|
|
1883
|
-
200: new OpCode(200, "INY", gaiaShared.AddressingMode.Implied, 1),
|
|
1884
|
-
// JMP/JML - Jump
|
|
1885
|
-
76: new OpCode(76, "JMP", gaiaShared.AddressingMode.Absolute, 3),
|
|
1886
|
-
108: new OpCode(108, "JMP", gaiaShared.AddressingMode.AbsoluteIndirect, 3),
|
|
1887
|
-
124: new OpCode(124, "JMP", gaiaShared.AddressingMode.AbsoluteIndexedIndirect, 3),
|
|
1888
|
-
92: new OpCode(92, "JML", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1889
|
-
220: new OpCode(220, "JML", gaiaShared.AddressingMode.AbsoluteIndirectLong, 3),
|
|
1890
|
-
// JSR/JSL - Jump to Subroutine
|
|
1891
|
-
32: new OpCode(32, "JSR", gaiaShared.AddressingMode.Absolute, 3),
|
|
1892
|
-
252: new OpCode(252, "JSR", gaiaShared.AddressingMode.AbsoluteIndexedIndirect, 3),
|
|
1893
|
-
34: new OpCode(34, "JSL", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1894
|
-
// LDA - Load Accumulator
|
|
1895
|
-
169: new OpCode(169, "LDA", gaiaShared.AddressingMode.Immediate, -2),
|
|
1896
|
-
173: new OpCode(173, "LDA", gaiaShared.AddressingMode.Absolute, 3),
|
|
1897
|
-
175: new OpCode(175, "LDA", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1898
|
-
165: new OpCode(165, "LDA", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1899
|
-
178: new OpCode(178, "LDA", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1900
|
-
167: new OpCode(167, "LDA", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1901
|
-
189: new OpCode(189, "LDA", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1902
|
-
191: new OpCode(191, "LDA", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1903
|
-
185: new OpCode(185, "LDA", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1904
|
-
181: new OpCode(181, "LDA", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1905
|
-
161: new OpCode(161, "LDA", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1906
|
-
177: new OpCode(177, "LDA", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1907
|
-
183: new OpCode(183, "LDA", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1908
|
-
163: new OpCode(163, "LDA", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1909
|
-
179: new OpCode(179, "LDA", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1910
|
-
// LDX - Load X Register
|
|
1911
|
-
162: new OpCode(162, "LDX", gaiaShared.AddressingMode.Immediate, -2),
|
|
1912
|
-
174: new OpCode(174, "LDX", gaiaShared.AddressingMode.Absolute, 3),
|
|
1913
|
-
166: new OpCode(166, "LDX", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1914
|
-
190: new OpCode(190, "LDX", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1915
|
-
182: new OpCode(182, "LDX", gaiaShared.AddressingMode.DirectPageIndexedY, 2),
|
|
1916
|
-
// LDY - Load Y Register
|
|
1917
|
-
160: new OpCode(160, "LDY", gaiaShared.AddressingMode.Immediate, -2),
|
|
1918
|
-
172: new OpCode(172, "LDY", gaiaShared.AddressingMode.Absolute, 3),
|
|
1919
|
-
164: new OpCode(164, "LDY", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1920
|
-
188: new OpCode(188, "LDY", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1921
|
-
180: new OpCode(180, "LDY", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1922
|
-
// LSR - Logical Shift Right
|
|
1923
|
-
74: new OpCode(74, "LSR", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1924
|
-
78: new OpCode(78, "LSR", gaiaShared.AddressingMode.Absolute, 3),
|
|
1925
|
-
70: new OpCode(70, "LSR", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1926
|
-
94: new OpCode(94, "LSR", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1927
|
-
86: new OpCode(86, "LSR", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1928
|
-
// MVN/MVP - Block Move
|
|
1929
|
-
84: new OpCode(84, "MVN", gaiaShared.AddressingMode.BlockMove, 3),
|
|
1930
|
-
68: new OpCode(68, "MVP", gaiaShared.AddressingMode.BlockMove, 3),
|
|
1931
|
-
// NOP - No Operation
|
|
1932
|
-
234: new OpCode(234, "NOP", gaiaShared.AddressingMode.Implied, 1),
|
|
1933
|
-
// ORA - Logical OR
|
|
1934
|
-
9: new OpCode(9, "ORA", gaiaShared.AddressingMode.Immediate, -2),
|
|
1935
|
-
13: new OpCode(13, "ORA", gaiaShared.AddressingMode.Absolute, 3),
|
|
1936
|
-
15: new OpCode(15, "ORA", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1937
|
-
5: new OpCode(5, "ORA", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1938
|
-
18: new OpCode(18, "ORA", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1939
|
-
7: new OpCode(7, "ORA", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1940
|
-
29: new OpCode(29, "ORA", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1941
|
-
31: new OpCode(31, "ORA", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1942
|
-
25: new OpCode(25, "ORA", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1943
|
-
21: new OpCode(21, "ORA", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1944
|
-
1: new OpCode(1, "ORA", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1945
|
-
17: new OpCode(17, "ORA", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1946
|
-
23: new OpCode(23, "ORA", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1947
|
-
3: new OpCode(3, "ORA", gaiaShared.AddressingMode.StackRelative, 2),
|
|
1948
|
-
19: new OpCode(19, "ORA", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1949
|
-
// Push instructions
|
|
1950
|
-
244: new OpCode(244, "PEA", gaiaShared.AddressingMode.Absolute, 3),
|
|
1951
|
-
212: new OpCode(212, "PEI", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1952
|
-
98: new OpCode(98, "PER", gaiaShared.AddressingMode.PCRelativeLong, 3),
|
|
1953
|
-
72: new OpCode(72, "PHA", gaiaShared.AddressingMode.Stack, 1),
|
|
1954
|
-
139: new OpCode(139, "PHB", gaiaShared.AddressingMode.Stack, 1),
|
|
1955
|
-
11: new OpCode(11, "PHD", gaiaShared.AddressingMode.Stack, 1),
|
|
1956
|
-
75: new OpCode(75, "PHK", gaiaShared.AddressingMode.Stack, 1),
|
|
1957
|
-
8: new OpCode(8, "PHP", gaiaShared.AddressingMode.Stack, 1),
|
|
1958
|
-
218: new OpCode(218, "PHX", gaiaShared.AddressingMode.Stack, 1),
|
|
1959
|
-
90: new OpCode(90, "PHY", gaiaShared.AddressingMode.Stack, 1),
|
|
1960
|
-
// Pull instructions
|
|
1961
|
-
104: new OpCode(104, "PLA", gaiaShared.AddressingMode.Stack, 1),
|
|
1962
|
-
171: new OpCode(171, "PLB", gaiaShared.AddressingMode.Stack, 1),
|
|
1963
|
-
43: new OpCode(43, "PLD", gaiaShared.AddressingMode.Stack, 1),
|
|
1964
|
-
40: new OpCode(40, "PLP", gaiaShared.AddressingMode.Stack, 1),
|
|
1965
|
-
250: new OpCode(250, "PLX", gaiaShared.AddressingMode.Stack, 1),
|
|
1966
|
-
122: new OpCode(122, "PLY", gaiaShared.AddressingMode.Stack, 1),
|
|
1967
|
-
// REP - Reset Status Bits
|
|
1968
|
-
194: new OpCode(194, "REP", gaiaShared.AddressingMode.Immediate, 2),
|
|
1969
|
-
// ROL - Rotate Left
|
|
1970
|
-
42: new OpCode(42, "ROL", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1971
|
-
46: new OpCode(46, "ROL", gaiaShared.AddressingMode.Absolute, 3),
|
|
1972
|
-
38: new OpCode(38, "ROL", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1973
|
-
62: new OpCode(62, "ROL", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1974
|
-
54: new OpCode(54, "ROL", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1975
|
-
// ROR - Rotate Right
|
|
1976
|
-
106: new OpCode(106, "ROR", gaiaShared.AddressingMode.Accumulator, 1),
|
|
1977
|
-
110: new OpCode(110, "ROR", gaiaShared.AddressingMode.Absolute, 3),
|
|
1978
|
-
102: new OpCode(102, "ROR", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1979
|
-
126: new OpCode(126, "ROR", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1980
|
-
118: new OpCode(118, "ROR", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1981
|
-
// Return instructions
|
|
1982
|
-
64: new OpCode(64, "RTI", gaiaShared.AddressingMode.Stack, 1),
|
|
1983
|
-
107: new OpCode(107, "RTL", gaiaShared.AddressingMode.Stack, 1),
|
|
1984
|
-
96: new OpCode(96, "RTS", gaiaShared.AddressingMode.Stack, 1),
|
|
1985
|
-
// SBC - Subtract with Carry
|
|
1986
|
-
233: new OpCode(233, "SBC", gaiaShared.AddressingMode.Immediate, -2),
|
|
1987
|
-
237: new OpCode(237, "SBC", gaiaShared.AddressingMode.Absolute, 3),
|
|
1988
|
-
239: new OpCode(239, "SBC", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
1989
|
-
229: new OpCode(229, "SBC", gaiaShared.AddressingMode.DirectPage, 2),
|
|
1990
|
-
242: new OpCode(242, "SBC", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
1991
|
-
231: new OpCode(231, "SBC", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
1992
|
-
253: new OpCode(253, "SBC", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
1993
|
-
255: new OpCode(255, "SBC", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1994
|
-
249: new OpCode(249, "SBC", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
1995
|
-
245: new OpCode(245, "SBC", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
1996
|
-
225: new OpCode(225, "SBC", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1997
|
-
241: new OpCode(241, "SBC", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1998
|
-
247: new OpCode(247, "SBC", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1999
|
-
227: new OpCode(227, "SBC", gaiaShared.AddressingMode.StackRelative, 2),
|
|
2000
|
-
243: new OpCode(243, "SBC", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
2001
|
-
// Set flag instructions
|
|
2002
|
-
56: new OpCode(56, "SEC", gaiaShared.AddressingMode.Implied, 1),
|
|
2003
|
-
248: new OpCode(248, "SED", gaiaShared.AddressingMode.Implied, 1),
|
|
2004
|
-
120: new OpCode(120, "SEI", gaiaShared.AddressingMode.Implied, 1),
|
|
2005
|
-
226: new OpCode(226, "SEP", gaiaShared.AddressingMode.Immediate, 2),
|
|
2006
|
-
// STA - Store Accumulator
|
|
2007
|
-
141: new OpCode(141, "STA", gaiaShared.AddressingMode.Absolute, 3),
|
|
2008
|
-
143: new OpCode(143, "STA", gaiaShared.AddressingMode.AbsoluteLong, 4),
|
|
2009
|
-
133: new OpCode(133, "STA", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2010
|
-
146: new OpCode(146, "STA", gaiaShared.AddressingMode.DirectPageIndirect, 2),
|
|
2011
|
-
135: new OpCode(135, "STA", gaiaShared.AddressingMode.DirectPageIndirectLong, 2),
|
|
2012
|
-
157: new OpCode(157, "STA", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
2013
|
-
159: new OpCode(159, "STA", gaiaShared.AddressingMode.AbsoluteLongIndexedX, 4),
|
|
2014
|
-
153: new OpCode(153, "STA", gaiaShared.AddressingMode.AbsoluteIndexedY, 3),
|
|
2015
|
-
149: new OpCode(149, "STA", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
2016
|
-
129: new OpCode(129, "STA", gaiaShared.AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
2017
|
-
145: new OpCode(145, "STA", gaiaShared.AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
2018
|
-
151: new OpCode(151, "STA", gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
2019
|
-
131: new OpCode(131, "STA", gaiaShared.AddressingMode.StackRelative, 2),
|
|
2020
|
-
147: new OpCode(147, "STA", gaiaShared.AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
2021
|
-
// STP - Stop
|
|
2022
|
-
219: new OpCode(219, "STP", gaiaShared.AddressingMode.Implied, 1),
|
|
2023
|
-
// STX - Store X Register
|
|
2024
|
-
142: new OpCode(142, "STX", gaiaShared.AddressingMode.Absolute, 3),
|
|
2025
|
-
134: new OpCode(134, "STX", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2026
|
-
150: new OpCode(150, "STX", gaiaShared.AddressingMode.DirectPageIndexedY, 2),
|
|
2027
|
-
// STY - Store Y Register
|
|
2028
|
-
140: new OpCode(140, "STY", gaiaShared.AddressingMode.Absolute, 3),
|
|
2029
|
-
132: new OpCode(132, "STY", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2030
|
-
148: new OpCode(148, "STY", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
2031
|
-
// STZ - Store Zero
|
|
2032
|
-
156: new OpCode(156, "STZ", gaiaShared.AddressingMode.Absolute, 3),
|
|
2033
|
-
100: new OpCode(100, "STZ", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2034
|
-
158: new OpCode(158, "STZ", gaiaShared.AddressingMode.AbsoluteIndexedX, 3),
|
|
2035
|
-
116: new OpCode(116, "STZ", gaiaShared.AddressingMode.DirectPageIndexedX, 2),
|
|
2036
|
-
// Transfer instructions
|
|
2037
|
-
170: new OpCode(170, "TAX", gaiaShared.AddressingMode.Implied, 1),
|
|
2038
|
-
168: new OpCode(168, "TAY", gaiaShared.AddressingMode.Implied, 1),
|
|
2039
|
-
91: new OpCode(91, "TCD", gaiaShared.AddressingMode.Implied, 1),
|
|
2040
|
-
27: new OpCode(27, "TCS", gaiaShared.AddressingMode.Implied, 1),
|
|
2041
|
-
123: new OpCode(123, "TDC", gaiaShared.AddressingMode.Implied, 1),
|
|
2042
|
-
28: new OpCode(28, "TRB", gaiaShared.AddressingMode.Absolute, 3),
|
|
2043
|
-
20: new OpCode(20, "TRB", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2044
|
-
12: new OpCode(12, "TSB", gaiaShared.AddressingMode.Absolute, 3),
|
|
2045
|
-
4: new OpCode(4, "TSB", gaiaShared.AddressingMode.DirectPage, 2),
|
|
2046
|
-
59: new OpCode(59, "TSC", gaiaShared.AddressingMode.Implied, 1),
|
|
2047
|
-
186: new OpCode(186, "TSX", gaiaShared.AddressingMode.Implied, 1),
|
|
2048
|
-
138: new OpCode(138, "TXA", gaiaShared.AddressingMode.Implied, 1),
|
|
2049
|
-
154: new OpCode(154, "TXS", gaiaShared.AddressingMode.Implied, 1),
|
|
2050
|
-
155: new OpCode(155, "TXY", gaiaShared.AddressingMode.Implied, 1),
|
|
2051
|
-
152: new OpCode(152, "TYA", gaiaShared.AddressingMode.Implied, 1),
|
|
2052
|
-
187: new OpCode(187, "TYX", gaiaShared.AddressingMode.Implied, 1),
|
|
2053
|
-
// Miscellaneous
|
|
2054
|
-
203: new OpCode(203, "WAI", gaiaShared.AddressingMode.Implied, 1),
|
|
2055
|
-
66: new OpCode(66, "WDM", gaiaShared.AddressingMode.Implied, 1),
|
|
2056
|
-
235: new OpCode(235, "XBA", gaiaShared.AddressingMode.Implied, 1),
|
|
2057
|
-
251: new OpCode(251, "XCE", gaiaShared.AddressingMode.Implied, 1)
|
|
2058
|
-
};
|
|
2059
|
-
var GROUPED_OPCODES = {};
|
|
2060
|
-
for (const opcode of Object.values(ALL_OPCODES)) {
|
|
2061
|
-
if (!GROUPED_OPCODES[opcode.mnem]) {
|
|
2062
|
-
GROUPED_OPCODES[opcode.mnem] = [];
|
|
2063
|
-
}
|
|
2064
|
-
GROUPED_OPCODES[opcode.mnem].push(opcode);
|
|
2065
|
-
}
|
|
2066
|
-
var ADDRESSING_REGEX = {
|
|
2067
|
-
[gaiaShared.AddressingMode.DirectPageIndexedIndirectX]: /^\(\$([A-Fa-f0-9]{2}),\s?[Xx]\)$/,
|
|
2068
|
-
[gaiaShared.AddressingMode.StackRelative]: /^\$([A-Fa-f0-9]{2}),\s?[Ss]$/,
|
|
2069
|
-
[gaiaShared.AddressingMode.StackInterrupt]: /^#\$([A-Fa-f0-9]{2})$/,
|
|
2070
|
-
[gaiaShared.AddressingMode.DirectPage]: /^\$([A-Fa-f0-9]{2})$/,
|
|
2071
|
-
[gaiaShared.AddressingMode.DirectPageIndirectLong]: /^\[\$([A-Fa-f0-9]{2})\]$/,
|
|
2072
|
-
[gaiaShared.AddressingMode.Immediate]: /^#(\$[A-Fa-f0-9]{2,4}|\$?[&^*][A-Za-z0-9-+_]+)$/,
|
|
2073
|
-
[gaiaShared.AddressingMode.Absolute]: /^\$([A-Fa-f0-9]{4}|&[A-Za-z0-9-+_]+)$/,
|
|
2074
|
-
[gaiaShared.AddressingMode.AbsoluteLong]: /^\$([A-Fa-f0-9]{6}|\@[A-Za-z0-9-+_]+)$/,
|
|
2075
|
-
[gaiaShared.AddressingMode.DirectPageIndirectIndexedY]: /^\(\$([A-Fa-f0-9]{2})\),\s?[Yy]$/,
|
|
2076
|
-
[gaiaShared.AddressingMode.DirectPageIndirect]: /^\(\$([A-Fa-f0-9]{2})\)$/,
|
|
2077
|
-
[gaiaShared.AddressingMode.StackRelativeIndirectIndexedY]: /^\(\$([A-Fa-f0-9]{2}),\s?[Ss]\),\s?[Yy]$/,
|
|
2078
|
-
[gaiaShared.AddressingMode.DirectPageIndexedX]: /^\$([A-Fa-f0-9]{2}),\s?[Xx]$/,
|
|
2079
|
-
[gaiaShared.AddressingMode.DirectPageIndirectLongIndexedY]: /^\[\$([A-Fa-f0-9]{2})\],\s?[Yy]$/,
|
|
2080
|
-
[gaiaShared.AddressingMode.AbsoluteIndexedY]: /^(\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s?[Yy]$/,
|
|
2081
|
-
[gaiaShared.AddressingMode.AbsoluteIndexedX]: /^(\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s?[Xx]$/,
|
|
2082
|
-
[gaiaShared.AddressingMode.AbsoluteLongIndexedX]: /^(\$[A-Fa-f0-9]{6}|\$?@[A-Za-z0-9-+_]+),\s?[Xx]$/,
|
|
2083
|
-
[gaiaShared.AddressingMode.AbsoluteIndexedIndirect]: /^\((\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s*[Xx]\)$/,
|
|
2084
|
-
[gaiaShared.AddressingMode.BlockMove]: /^#\$([A-Fa-f0-9]{2}|\^[A-Za-z0-9-+_]+),\s?#\$([A-Fa-f0-9]{2}|\^[A-Za-z0-9-+_]+)$/,
|
|
2085
|
-
// Default entries for other addressing modes
|
|
2086
|
-
[gaiaShared.AddressingMode.Implied]: /^$/,
|
|
2087
|
-
[gaiaShared.AddressingMode.Accumulator]: /^[Aa]$/,
|
|
2088
|
-
[gaiaShared.AddressingMode.PCRelative]: /^/,
|
|
2089
|
-
[gaiaShared.AddressingMode.PCRelativeLong]: /^/,
|
|
2090
|
-
[gaiaShared.AddressingMode.AbsoluteIndirect]: /^/,
|
|
2091
|
-
[gaiaShared.AddressingMode.AbsoluteIndirectLong]: /^/,
|
|
2092
|
-
[gaiaShared.AddressingMode.DirectPageIndexedY]: /^/,
|
|
2093
|
-
[gaiaShared.AddressingMode.Stack]: /^/
|
|
2094
|
-
};
|
|
2095
|
-
var HEX_REGEX = /[^A-Fa-f0-9]/g;
|
|
2096
|
-
var OpCodeUtils = class {
|
|
2097
|
-
/**
|
|
2098
|
-
* Get all available opcodes
|
|
2099
|
-
*/
|
|
2100
|
-
static getAllOpcodes() {
|
|
2101
|
-
return Object.values(ALL_OPCODES);
|
|
2102
|
-
}
|
|
2103
|
-
/**
|
|
2104
|
-
* Get opcodes by mnemonic
|
|
2105
|
-
*/
|
|
2106
|
-
static getByMnemonic(mnemonic) {
|
|
2107
|
-
return GROUPED_OPCODES[mnemonic] || [];
|
|
2108
|
-
}
|
|
2109
|
-
/**
|
|
2110
|
-
* Find opcode by hex value
|
|
2111
|
-
*/
|
|
2112
|
-
static findByCode(code) {
|
|
2113
|
-
return ALL_OPCODES[code];
|
|
1452
|
+
registerCompressionProviders();
|
|
1453
|
+
var BlockReader = class {
|
|
1454
|
+
constructor(romData, root) {
|
|
1455
|
+
// Current Processing State (Database)
|
|
1456
|
+
//public _currentBlock!: DbBlock;
|
|
1457
|
+
//public _currentPart: DbPart | null = null;
|
|
1458
|
+
this._partEnd = 0;
|
|
1459
|
+
// ChunkFile Processing State
|
|
1460
|
+
this._currentChunk = null;
|
|
1461
|
+
this._currentAsmBlock = null;
|
|
1462
|
+
this._enrichedChunks = [];
|
|
1463
|
+
this._romDataReader = new RomDataReader(romData);
|
|
1464
|
+
this._stateManager = new ProcessorStateManager();
|
|
1465
|
+
this._referenceManager = new ReferenceManager(root);
|
|
1466
|
+
this._root = root;
|
|
1467
|
+
this._stringReader = new StringReader(this);
|
|
1468
|
+
this._asmReader = new AsmReader(this);
|
|
1469
|
+
this._typeParser = new TypeParser(this);
|
|
1470
|
+
this.initializeOverrides();
|
|
1471
|
+
this.initializeFileReferences();
|
|
2114
1472
|
}
|
|
2115
|
-
};
|
|
2116
|
-
|
|
2117
|
-
// src/rom/extraction/asm.ts
|
|
2118
|
-
var AsmReader = class _AsmReader {
|
|
2119
1473
|
static {
|
|
2120
1474
|
// Constants
|
|
2121
|
-
this.
|
|
1475
|
+
this.REF_SEARCH_MAX_RANGE = 416;
|
|
2122
1476
|
}
|
|
2123
1477
|
static {
|
|
2124
|
-
this.
|
|
1478
|
+
this.BANK_MASK_CHECK = 64;
|
|
2125
1479
|
}
|
|
2126
1480
|
static {
|
|
2127
|
-
this.
|
|
1481
|
+
this.BYTE_DELIMITER_THRESHOLD = 256;
|
|
2128
1482
|
}
|
|
2129
1483
|
static {
|
|
2130
|
-
this.
|
|
1484
|
+
this.BANK_HIGH_MEMORY_1 = 126;
|
|
2131
1485
|
}
|
|
2132
1486
|
static {
|
|
2133
|
-
this.
|
|
1487
|
+
this.BANK_HIGH_MEMORY_2 = 127;
|
|
2134
1488
|
}
|
|
2135
|
-
|
|
2136
|
-
this.
|
|
2137
|
-
this._transformProcessor = new TransformProcessor(blockReader);
|
|
2138
|
-
this._addressingModeHandler = new AddressingModeHandler(blockReader, this._transformProcessor);
|
|
2139
|
-
this._romDataReader = blockReader._romDataReader;
|
|
2140
|
-
}
|
|
2141
|
-
parseAsm(reg) {
|
|
2142
|
-
const opStart = this._romDataReader.position;
|
|
2143
|
-
const opCode = this._romDataReader.readByte();
|
|
2144
|
-
const code = this._blockReader._root.opCodes[opCode];
|
|
2145
|
-
if (!code) {
|
|
2146
|
-
throw new Error("Unknown OpCode");
|
|
2147
|
-
}
|
|
2148
|
-
const operationContext = this.initializeOperation(code, reg, opStart);
|
|
2149
|
-
const operands = this._addressingModeHandler.processAddressingMode(code, operationContext, reg);
|
|
2150
|
-
this._transformProcessor.applyTransforms(operationContext.xForm1, operationContext.xForm2, operands);
|
|
2151
|
-
const op = new Op(
|
|
2152
|
-
code,
|
|
2153
|
-
opStart,
|
|
2154
|
-
operands,
|
|
2155
|
-
this._romDataReader.position - opStart
|
|
2156
|
-
);
|
|
2157
|
-
if (operationContext.copDef) {
|
|
2158
|
-
op.copDef = operationContext.copDef;
|
|
2159
|
-
}
|
|
2160
|
-
return op;
|
|
2161
|
-
}
|
|
2162
|
-
clearDestinationRegister(code, reg) {
|
|
2163
|
-
switch (code.mnem) {
|
|
2164
|
-
case "LDA":
|
|
2165
|
-
reg.accumulator = void 0;
|
|
2166
|
-
break;
|
|
2167
|
-
case "LDX":
|
|
2168
|
-
reg.xIndex = void 0;
|
|
2169
|
-
break;
|
|
2170
|
-
case "LDY":
|
|
2171
|
-
reg.yIndex = void 0;
|
|
2172
|
-
break;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
initializeOperation(code, reg, loc) {
|
|
2176
|
-
const size = this.calculateInstructionSize(code, reg);
|
|
2177
|
-
const next = loc + size;
|
|
2178
|
-
this.clearDestinationRegister(code, reg);
|
|
2179
|
-
const context = new OperationContext();
|
|
2180
|
-
context.size = size;
|
|
2181
|
-
context.nextAddress = next;
|
|
2182
|
-
context.xForm1 = this._transformProcessor.getTransform();
|
|
2183
|
-
context.xForm2 = null;
|
|
2184
|
-
context.copDef = null;
|
|
2185
|
-
return context;
|
|
2186
|
-
}
|
|
2187
|
-
calculateInstructionSize(code, reg) {
|
|
2188
|
-
let size = code.size;
|
|
2189
|
-
if (size === _AsmReader.VARIABLE_SIZE_INDICATOR) {
|
|
2190
|
-
if ((code.code & _AsmReader.ACCUMULATOR_OP_MASK) === _AsmReader.ACCUMULATOR_OP_VALUE) {
|
|
2191
|
-
size = reg.accumulatorFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
2192
|
-
} else {
|
|
2193
|
-
size = reg.indexFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
return size;
|
|
2197
|
-
}
|
|
2198
|
-
};
|
|
2199
|
-
var BlockReader = class {
|
|
2200
|
-
constructor(romData, root) {
|
|
2201
|
-
this._currentPart = null;
|
|
2202
|
-
this._partEnd = 0;
|
|
2203
|
-
this._romDataReader = new RomDataReader(romData);
|
|
2204
|
-
this._stateManager = new ProcessorStateManager();
|
|
2205
|
-
this._referenceManager = new ReferenceManager(root);
|
|
2206
|
-
this._root = root;
|
|
2207
|
-
this._stringReader = new StringReader(this);
|
|
2208
|
-
this._asmReader = new AsmReader(this);
|
|
2209
|
-
this._typeParser = new TypeParser(this);
|
|
2210
|
-
this.initializeOverrides();
|
|
2211
|
-
this.initializeFileReferences();
|
|
2212
|
-
}
|
|
2213
|
-
static {
|
|
2214
|
-
// Constants
|
|
2215
|
-
this.REF_SEARCH_MAX_RANGE = 416;
|
|
2216
|
-
}
|
|
2217
|
-
static {
|
|
2218
|
-
this.BANK_MASK_CHECK = 64;
|
|
2219
|
-
}
|
|
2220
|
-
static {
|
|
2221
|
-
this.BYTE_DELIMITER_THRESHOLD = 256;
|
|
2222
|
-
}
|
|
2223
|
-
static {
|
|
2224
|
-
this.BANK_HIGH_MEMORY_1 = 126;
|
|
2225
|
-
}
|
|
2226
|
-
static {
|
|
2227
|
-
this.BANK_HIGH_MEMORY_2 = 127;
|
|
2228
|
-
}
|
|
2229
|
-
static {
|
|
2230
|
-
this.POINTER_CHARACTERS = ["&", "@"];
|
|
1489
|
+
static {
|
|
1490
|
+
this.POINTER_CHARACTERS = ["&", "@"];
|
|
2231
1491
|
}
|
|
2232
1492
|
static {
|
|
2233
1493
|
// Location regex pattern: _([A-Fa-f0-9]{6})
|
|
@@ -2262,13 +1522,13 @@ var BlockReader = class {
|
|
|
2262
1522
|
initializeOverrides() {
|
|
2263
1523
|
for (const over of Object.values(this._root.overrides)) {
|
|
2264
1524
|
switch (over.register) {
|
|
2265
|
-
case
|
|
1525
|
+
case shared.RegisterType.M:
|
|
2266
1526
|
this._stateManager.setAccumulatorFlag(over.location, over.value === 1);
|
|
2267
1527
|
break;
|
|
2268
|
-
case
|
|
1528
|
+
case shared.RegisterType.X:
|
|
2269
1529
|
this._stateManager.setIndexFlag(over.location, over.value === 1);
|
|
2270
1530
|
break;
|
|
2271
|
-
case
|
|
1531
|
+
case shared.RegisterType.B:
|
|
2272
1532
|
this._stateManager.setBankNote(over.location, over.value);
|
|
2273
1533
|
break;
|
|
2274
1534
|
}
|
|
@@ -2286,18 +1546,15 @@ var BlockReader = class {
|
|
|
2286
1546
|
* Resolves mnemonic for a given address
|
|
2287
1547
|
*/
|
|
2288
1548
|
resolveMnemonic(addr) {
|
|
2289
|
-
if ((addr.bank &
|
|
1549
|
+
if ((addr.bank & shared.Address.DATA_BANK_FLAG) !== 0) {
|
|
2290
1550
|
return;
|
|
2291
1551
|
}
|
|
2292
1552
|
let offset = addr.offset;
|
|
2293
|
-
if (offset === 1750) {
|
|
2294
|
-
console.log(this._root.mnemonics[offset]);
|
|
2295
|
-
}
|
|
2296
1553
|
const label = this._root.mnemonics[offset];
|
|
2297
1554
|
if (!label) {
|
|
2298
1555
|
return;
|
|
2299
1556
|
}
|
|
2300
|
-
const ix = indexOfAny(label,
|
|
1557
|
+
const ix = indexOfAny(label, shared.RomProcessingConstants.OPERATORS);
|
|
2301
1558
|
if (ix >= 0) {
|
|
2302
1559
|
let opnd = parseInt(label.substring(ix + 1), 16);
|
|
2303
1560
|
const op = label[ix];
|
|
@@ -2305,9 +1562,7 @@ var BlockReader = class {
|
|
|
2305
1562
|
opnd = -opnd;
|
|
2306
1563
|
offset -= opnd;
|
|
2307
1564
|
}
|
|
2308
|
-
|
|
2309
|
-
this._currentBlock.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
|
|
2310
|
-
}
|
|
1565
|
+
this._currentChunk.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
|
|
2311
1566
|
}
|
|
2312
1567
|
/**
|
|
2313
1568
|
* Resolves name for a location (delegated to ReferenceManager)
|
|
@@ -2319,21 +1574,9 @@ var BlockReader = class {
|
|
|
2319
1574
|
* Resolves include for a location
|
|
2320
1575
|
*/
|
|
2321
1576
|
resolveInclude(loc, isBranch) {
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
for (const part of block.parts) {
|
|
2326
|
-
if (loc >= part.start && loc < part.end) {
|
|
2327
|
-
foundPart = part;
|
|
2328
|
-
break;
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
if (foundPart) break;
|
|
2332
|
-
}
|
|
2333
|
-
if (foundPart) {
|
|
2334
|
-
this._currentPart.includes = this._currentPart.includes || /* @__PURE__ */ new Set();
|
|
2335
|
-
this._currentPart.includes.add(foundPart);
|
|
2336
|
-
}
|
|
1577
|
+
const [outside, foundBlock, foundPart] = shared.ChunkFileUtils.isOutsideWithPart(this._enrichedChunks, this._currentChunk, loc);
|
|
1578
|
+
if (outside && foundBlock && foundPart) {
|
|
1579
|
+
this._currentAsmBlock.includes.add({ block: foundBlock, part: foundPart });
|
|
2337
1580
|
} else if (isBranch && !this._referenceManager.tryGetName(loc).found) {
|
|
2338
1581
|
const name = `loc_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
|
|
2339
1582
|
this._referenceManager.tryAddName(loc, name);
|
|
@@ -2352,7 +1595,7 @@ var BlockReader = class {
|
|
|
2352
1595
|
} else {
|
|
2353
1596
|
name = nameResult.referenceName;
|
|
2354
1597
|
}
|
|
2355
|
-
if (!silent && type ===
|
|
1598
|
+
if (!silent && type === shared.BlockReaderConstants.CODE_TYPE && reg) {
|
|
2356
1599
|
this.updateRegisterState(loc, reg);
|
|
2357
1600
|
}
|
|
2358
1601
|
return name;
|
|
@@ -2375,7 +1618,7 @@ var BlockReader = class {
|
|
|
2375
1618
|
if (delimiter === void 0) {
|
|
2376
1619
|
return false;
|
|
2377
1620
|
}
|
|
2378
|
-
if (delimiter >=
|
|
1621
|
+
if (delimiter >= shared.BlockReaderConstants.BYTE_DELIMITER_THRESHOLD) {
|
|
2379
1622
|
if (this._romDataReader.peekShort() === delimiter) {
|
|
2380
1623
|
this._romDataReader.position += 2;
|
|
2381
1624
|
return true;
|
|
@@ -2392,34 +1635,37 @@ var BlockReader = class {
|
|
|
2392
1635
|
partCanContinue() {
|
|
2393
1636
|
return this._romDataReader.position < this._partEnd && !this._referenceManager.containsStruct(this._romDataReader.position);
|
|
2394
1637
|
}
|
|
2395
|
-
/**
|
|
2396
|
-
* Main analysis entry point
|
|
2397
|
-
*/
|
|
2398
1638
|
analyzeAndResolve() {
|
|
2399
|
-
this.
|
|
1639
|
+
this.createChunkFilesFromDatabase();
|
|
1640
|
+
this.initializeBlocksAndParts();
|
|
1641
|
+
this.analyzeChunkFiles();
|
|
2400
1642
|
this.resolveReferences();
|
|
1643
|
+
return this._enrichedChunks;
|
|
2401
1644
|
}
|
|
2402
1645
|
/**
|
|
2403
1646
|
* Analyzes all blocks in the ROM
|
|
2404
1647
|
*/
|
|
2405
|
-
analyzeBlocks() {
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
}
|
|
1648
|
+
// private analyzeBlocks(): void {
|
|
1649
|
+
// this.initializeBlocksAndParts();
|
|
1650
|
+
// for (const chunk of this._enrichedChunks) {
|
|
1651
|
+
// this._currentChunk = chunk;
|
|
1652
|
+
// for (const part of chunk.parts || []) {
|
|
1653
|
+
// this.processPart(part);
|
|
1654
|
+
// }
|
|
1655
|
+
// }
|
|
1656
|
+
// }
|
|
2414
1657
|
/**
|
|
2415
1658
|
* Initializes blocks and parts with base references
|
|
2416
1659
|
*/
|
|
2417
1660
|
initializeBlocksAndParts() {
|
|
2418
|
-
for (const block of this.
|
|
2419
|
-
for (const part of block.parts) {
|
|
2420
|
-
part.
|
|
2421
|
-
|
|
2422
|
-
|
|
1661
|
+
for (const block of this._enrichedChunks) {
|
|
1662
|
+
for (const part of block.parts || []) {
|
|
1663
|
+
if (part.structName) {
|
|
1664
|
+
this._referenceManager.tryAddStruct(part.location, part.structName);
|
|
1665
|
+
}
|
|
1666
|
+
if (part.label) {
|
|
1667
|
+
this._referenceManager.tryAddName(part.location, part.label);
|
|
1668
|
+
}
|
|
2423
1669
|
}
|
|
2424
1670
|
}
|
|
2425
1671
|
}
|
|
@@ -2427,10 +1673,10 @@ var BlockReader = class {
|
|
|
2427
1673
|
* Processes a single part
|
|
2428
1674
|
*/
|
|
2429
1675
|
processPart(part) {
|
|
2430
|
-
this.
|
|
2431
|
-
this._romDataReader.position = part.
|
|
2432
|
-
this._partEnd = part.
|
|
2433
|
-
let current = part.
|
|
1676
|
+
this._currentAsmBlock = part;
|
|
1677
|
+
this._romDataReader.position = part.location;
|
|
1678
|
+
this._partEnd = part.location + part.size;
|
|
1679
|
+
let current = part.structName || shared.BlockReaderConstants.BINARY_TYPE;
|
|
2434
1680
|
const chunks = [];
|
|
2435
1681
|
const reg = new Registers();
|
|
2436
1682
|
const bank = part.bank;
|
|
@@ -2443,11 +1689,11 @@ var BlockReader = class {
|
|
|
2443
1689
|
this.processContinuousEntry(current, reg, bank, last);
|
|
2444
1690
|
continue;
|
|
2445
1691
|
}
|
|
2446
|
-
last =
|
|
1692
|
+
last = shared.createTableEntry(this._romDataReader.position);
|
|
2447
1693
|
chunks.push(last);
|
|
2448
1694
|
this.processNewEntry(current, reg, bank, last);
|
|
2449
1695
|
}
|
|
2450
|
-
part.
|
|
1696
|
+
part.objList = chunks;
|
|
2451
1697
|
}
|
|
2452
1698
|
/**
|
|
2453
1699
|
* Processes a continuous entry (same type as previous)
|
|
@@ -2464,20 +1710,116 @@ var BlockReader = class {
|
|
|
2464
1710
|
*/
|
|
2465
1711
|
processNewEntry(current, reg, bank, last) {
|
|
2466
1712
|
let res = this._typeParser.parseType(current, reg, 0, bank);
|
|
2467
|
-
if (
|
|
1713
|
+
if (shared.BlockReaderConstants.POINTER_CHARACTERS.includes(current[0]) && !Array.isArray(res)) {
|
|
2468
1714
|
res = [res];
|
|
2469
1715
|
}
|
|
2470
1716
|
last.object = res;
|
|
2471
1717
|
}
|
|
2472
1718
|
/**
|
|
2473
|
-
*
|
|
1719
|
+
* Creates ChunkFile objects from database structure
|
|
1720
|
+
*/
|
|
1721
|
+
createChunkFilesFromDatabase() {
|
|
1722
|
+
this._enrichedChunks = [];
|
|
1723
|
+
this.createChunkFilesFromSfx();
|
|
1724
|
+
this.createChunkFilesFromDbFiles();
|
|
1725
|
+
this.createChunkFilesFromDbBlocks();
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Creates binary ChunkFiles from DbFiles (enriched with rawData)
|
|
1729
|
+
*/
|
|
1730
|
+
createChunkFilesFromSfx() {
|
|
1731
|
+
let pos = this._root.config.sfxLocation;
|
|
1732
|
+
const count = this._root.config.sfxCount;
|
|
1733
|
+
const romData = this._romDataReader.romData;
|
|
1734
|
+
const getSize = () => romData[pos++] | romData[pos++] << 8;
|
|
1735
|
+
for (let i = 0; i < count; i++) {
|
|
1736
|
+
const size = getSize();
|
|
1737
|
+
const startPos = pos;
|
|
1738
|
+
let remaining = size;
|
|
1739
|
+
let end = pos + remaining;
|
|
1740
|
+
let data;
|
|
1741
|
+
if (end & shared.RomProcessingConstants.PAGE_SIZE) {
|
|
1742
|
+
const endLen = end & 32767;
|
|
1743
|
+
remaining -= endLen;
|
|
1744
|
+
data = romData.slice(pos, pos + remaining);
|
|
1745
|
+
pos += remaining + shared.RomProcessingConstants.PAGE_SIZE;
|
|
1746
|
+
end = pos + endLen;
|
|
1747
|
+
const data2 = romData.slice(pos, end);
|
|
1748
|
+
data = new Uint8Array([...data, ...data2]);
|
|
1749
|
+
} else {
|
|
1750
|
+
data = romData.slice(pos, end);
|
|
1751
|
+
}
|
|
1752
|
+
pos = end;
|
|
1753
|
+
const chunk = new shared.ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, shared.BinType.Sound);
|
|
1754
|
+
chunk.rawData = data;
|
|
1755
|
+
this._enrichedChunks.push(chunk);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Creates binary ChunkFiles from DbFiles (enriched with rawData)
|
|
2474
1760
|
*/
|
|
2475
|
-
|
|
1761
|
+
createChunkFilesFromDbFiles() {
|
|
1762
|
+
for (const dbFile of this._root.files) {
|
|
1763
|
+
const chunkFile = shared.createChunkFileFromDbFile(this._romDataReader.romData, this._root.compression, dbFile);
|
|
1764
|
+
this._enrichedChunks.push(chunkFile);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Creates assembly ChunkFiles from DbBlocks (enriched with parts)
|
|
1769
|
+
*/
|
|
1770
|
+
createChunkFilesFromDbBlocks() {
|
|
2476
1771
|
for (const block of this._root.blocks) {
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
1772
|
+
const chunkFile = shared.createChunkFileFromDbBlock(block);
|
|
1773
|
+
this._enrichedChunks.push(chunkFile);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Analyzes only assembly ChunkFiles (those with parts from DbBlocks)
|
|
1778
|
+
*/
|
|
1779
|
+
analyzeChunkFiles() {
|
|
1780
|
+
const assemblyChunks = this._enrichedChunks.filter((chunk) => chunk.type === shared.BinType.Assembly);
|
|
1781
|
+
for (const chunkFile of assemblyChunks) {
|
|
1782
|
+
this._currentChunk = chunkFile;
|
|
1783
|
+
for (const asmBlock of chunkFile.parts || []) {
|
|
1784
|
+
this._currentAsmBlock = asmBlock;
|
|
1785
|
+
this.processPart(asmBlock);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
// /**
|
|
1790
|
+
// * Processes a single AsmBlock (similar to processPart)
|
|
1791
|
+
// */
|
|
1792
|
+
// private processAsmBlock(asmBlock: AsmBlock): void {
|
|
1793
|
+
// this._romDataReader.position = asmBlock.location;
|
|
1794
|
+
// this._partEnd = asmBlock.location + asmBlock.size;
|
|
1795
|
+
// let current = asmBlock.structName || BlockReaderConstants.BINARY_TYPE;
|
|
1796
|
+
// const chunks: TableEntry[] = [];
|
|
1797
|
+
// const reg = new Registers();
|
|
1798
|
+
// const bank = this._currentAsmBlock?.bank;
|
|
1799
|
+
// let last: TableEntry | null = null;
|
|
1800
|
+
// while (this._romDataReader.position < this._partEnd) {
|
|
1801
|
+
// const structResult = this._referenceManager.tryGetStruct(this._romDataReader.position);
|
|
1802
|
+
// if (structResult.found) {
|
|
1803
|
+
// current = structResult.chunkType!;
|
|
1804
|
+
// } else if (last !== null) {
|
|
1805
|
+
// this.processContinuousEntry(current, reg, bank, last);
|
|
1806
|
+
// continue;
|
|
1807
|
+
// }
|
|
1808
|
+
// last = createTableEntry(this._romDataReader.position);
|
|
1809
|
+
// chunks.push(last);
|
|
1810
|
+
// this.processNewEntry(current, reg, bank, last);
|
|
1811
|
+
// }
|
|
1812
|
+
// asmBlock.objList = chunks;
|
|
1813
|
+
// }
|
|
1814
|
+
/**
|
|
1815
|
+
* Resolves references in assembly ChunkFiles only
|
|
1816
|
+
*/
|
|
1817
|
+
resolveReferences() {
|
|
1818
|
+
for (const chunkFile of this._enrichedChunks) {
|
|
1819
|
+
this._currentChunk = chunkFile;
|
|
1820
|
+
for (const asmBlock of chunkFile.parts || []) {
|
|
1821
|
+
this._currentAsmBlock = asmBlock;
|
|
1822
|
+
this.resolveObject(asmBlock.objList, false);
|
|
2481
1823
|
}
|
|
2482
1824
|
}
|
|
2483
1825
|
}
|
|
@@ -2494,7 +1836,7 @@ var BlockReader = class {
|
|
|
2494
1836
|
}
|
|
2495
1837
|
return;
|
|
2496
1838
|
}
|
|
2497
|
-
if (obj instanceof
|
|
1839
|
+
if (obj instanceof shared.Address) {
|
|
2498
1840
|
this.resolveMnemonic(obj);
|
|
2499
1841
|
return;
|
|
2500
1842
|
}
|
|
@@ -2502,7 +1844,7 @@ var BlockReader = class {
|
|
|
2502
1844
|
this.resolveInclude(obj, isBranch);
|
|
2503
1845
|
return;
|
|
2504
1846
|
}
|
|
2505
|
-
if (obj instanceof
|
|
1847
|
+
if (obj instanceof shared.LocationWrapper) {
|
|
2506
1848
|
this.resolveInclude(obj.location, isBranch);
|
|
2507
1849
|
return;
|
|
2508
1850
|
}
|
|
@@ -2538,7 +1880,7 @@ var BlockReader = class {
|
|
|
2538
1880
|
* Checks if an operation is a branch operation
|
|
2539
1881
|
*/
|
|
2540
1882
|
isBranchOperation(op) {
|
|
2541
|
-
return op.
|
|
1883
|
+
return op.mode === "PCRelative" || op.mode === "PCRelativeLong" || op.mnem[0] === "J";
|
|
2542
1884
|
}
|
|
2543
1885
|
/**
|
|
2544
1886
|
* Hydrates registers with stored state
|
|
@@ -2546,12 +1888,6 @@ var BlockReader = class {
|
|
|
2546
1888
|
hydrateRegisters(reg) {
|
|
2547
1889
|
this._stateManager.hydrateRegisters(this._romDataReader.position, reg);
|
|
2548
1890
|
}
|
|
2549
|
-
/**
|
|
2550
|
-
* PascalCase wrapper for hydrateRegisters (for C# compatibility)
|
|
2551
|
-
*/
|
|
2552
|
-
HydrateRegisters(reg) {
|
|
2553
|
-
this.hydrateRegisters(reg);
|
|
2554
|
-
}
|
|
2555
1891
|
};
|
|
2556
1892
|
var PostProcessor = class {
|
|
2557
1893
|
constructor(reader) {
|
|
@@ -2593,7 +1929,10 @@ var PostProcessor = class {
|
|
|
2593
1929
|
Lookup(block, keyIx, valueIx) {
|
|
2594
1930
|
const kix = parseInt(keyIx.trim());
|
|
2595
1931
|
const vix = parseInt(valueIx.trim());
|
|
2596
|
-
|
|
1932
|
+
if (!block.parts || block.parts.length === 0) {
|
|
1933
|
+
throw new Error("Invalid block structure for Lookup post process");
|
|
1934
|
+
}
|
|
1935
|
+
const table = block.parts[0]?.objList;
|
|
2597
1936
|
const tableEntry = table && table[0];
|
|
2598
1937
|
const entries = tableEntry?.object;
|
|
2599
1938
|
if (!tableEntry || !entries) {
|
|
@@ -2631,16 +1970,17 @@ var PostProcessor = class {
|
|
|
2631
1970
|
newParts.push({ location: loc, object: value });
|
|
2632
1971
|
this._referenceManager.nameTable.set(loc, name);
|
|
2633
1972
|
while (newList.length <= key) {
|
|
2634
|
-
newList.push(
|
|
1973
|
+
newList.push(new shared.Word(0));
|
|
2635
1974
|
}
|
|
2636
1975
|
newList[key] = `&${name}`;
|
|
2637
1976
|
eIx++;
|
|
2638
1977
|
}
|
|
2639
|
-
block.parts[0].
|
|
1978
|
+
block.parts[0].objList = newParts;
|
|
2640
1979
|
}
|
|
2641
1980
|
};
|
|
2642
1981
|
|
|
2643
1982
|
// src/rom/extraction/writer.ts
|
|
1983
|
+
var NEWLINE = process.platform === "win32" ? "\r\n" : "\n";
|
|
2644
1984
|
var ObjectType = /* @__PURE__ */ ((ObjectType2) => {
|
|
2645
1985
|
ObjectType2["TableEntryArray"] = "TableEntryArray";
|
|
2646
1986
|
ObjectType2["StructDef"] = "StructDef";
|
|
@@ -2664,28 +2004,46 @@ var BlockWriter = class {
|
|
|
2664
2004
|
this._referenceManager = reader._referenceManager;
|
|
2665
2005
|
this._postProcessor = new PostProcessor(reader);
|
|
2666
2006
|
}
|
|
2667
|
-
async writeBlocks(outPath) {
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2007
|
+
// async writeBlocks(outPath: string): Promise<void> {
|
|
2008
|
+
// const res = DbRootUtils.getPath(this._root, BinType.Assembly);
|
|
2009
|
+
// const folderPath = join(outPath, res.folder);
|
|
2010
|
+
// for (const block of this._root.blocks) {
|
|
2011
|
+
// const groupedFolderPath = block.group
|
|
2012
|
+
// ? join(folderPath, block.group)
|
|
2013
|
+
// : folderPath;
|
|
2014
|
+
// await fs.mkdir(groupedFolderPath, { recursive: true });
|
|
2015
|
+
// const outFile = join(groupedFolderPath, `${block.name}.${res.extension}`);
|
|
2016
|
+
// // Check if file exists, skip if it does
|
|
2017
|
+
// try {
|
|
2018
|
+
// await fs.access(outFile);
|
|
2019
|
+
// continue;
|
|
2020
|
+
// } catch {
|
|
2021
|
+
// // File doesn't exist, continue with creation
|
|
2022
|
+
// }
|
|
2023
|
+
// const content = this.generateAsm(block);
|
|
2024
|
+
// await fs.writeFile(outFile, content);
|
|
2025
|
+
// }
|
|
2026
|
+
// }
|
|
2027
|
+
generateAllAsm(chunkFiles) {
|
|
2028
|
+
const lines = [];
|
|
2029
|
+
for (const block of chunkFiles) {
|
|
2030
|
+
lines.push({
|
|
2031
|
+
name: block.name,
|
|
2032
|
+
group: block.group,
|
|
2033
|
+
text: this.generateAsm(block)
|
|
2034
|
+
});
|
|
2681
2035
|
}
|
|
2036
|
+
return lines;
|
|
2682
2037
|
}
|
|
2683
2038
|
generateAsm(block) {
|
|
2039
|
+
if (!block.parts) {
|
|
2040
|
+
throw new Error("Invalid block structure for generateAsm");
|
|
2041
|
+
}
|
|
2684
2042
|
const lines = [];
|
|
2685
|
-
if (
|
|
2686
|
-
lines.push(`?BANK ${
|
|
2043
|
+
if (block.bank !== void 0) {
|
|
2044
|
+
lines.push(`?BANK ${block.bank.toString(16).toUpperCase().padStart(2, "0")}`);
|
|
2687
2045
|
}
|
|
2688
|
-
const includes =
|
|
2046
|
+
const includes = shared.ChunkFileUtils.getIncludes(block);
|
|
2689
2047
|
if (includes && includes.length > 0) {
|
|
2690
2048
|
lines.push("");
|
|
2691
2049
|
for (const inc of includes) {
|
|
@@ -2701,15 +2059,15 @@ var BlockWriter = class {
|
|
|
2701
2059
|
}
|
|
2702
2060
|
}
|
|
2703
2061
|
this._postProcessor.process(block);
|
|
2704
|
-
for (const part of block.parts) {
|
|
2062
|
+
for (const part of block.parts || []) {
|
|
2705
2063
|
this._currentPart = part;
|
|
2706
2064
|
this._isInline = true;
|
|
2707
2065
|
lines.push("");
|
|
2708
2066
|
lines.push("---------------------------------------------");
|
|
2709
|
-
const objectLines = this.writeObject(part.
|
|
2067
|
+
const objectLines = this.writeObject(part.objList, -1);
|
|
2710
2068
|
lines.push(...objectLines);
|
|
2711
2069
|
}
|
|
2712
|
-
let content = lines.join(
|
|
2070
|
+
let content = lines.join(NEWLINE);
|
|
2713
2071
|
if (block.transforms) {
|
|
2714
2072
|
for (const x of block.transforms) {
|
|
2715
2073
|
if (x.key && x.value) {
|
|
@@ -2718,6 +2076,9 @@ var BlockWriter = class {
|
|
|
2718
2076
|
}
|
|
2719
2077
|
}
|
|
2720
2078
|
}
|
|
2079
|
+
if (process.platform !== "win32") {
|
|
2080
|
+
content = content.replace(/\r/g, "");
|
|
2081
|
+
}
|
|
2721
2082
|
return content;
|
|
2722
2083
|
}
|
|
2723
2084
|
getMnemonicsForBlock(block) {
|
|
@@ -2727,13 +2088,15 @@ var BlockWriter = class {
|
|
|
2727
2088
|
return Object.entries(block.mnemonics).map(([k, v]) => [v, parseInt(k, 10)]).sort((a, b) => a[1] - b[1]);
|
|
2728
2089
|
}
|
|
2729
2090
|
resolveOperand(op, obj, isBranch = false) {
|
|
2730
|
-
|
|
2091
|
+
if (obj instanceof shared.TypedNumber) {
|
|
2092
|
+
const len = obj.size * 2;
|
|
2093
|
+
return obj.value.toString(16).toUpperCase().padStart(len, "0");
|
|
2094
|
+
}
|
|
2731
2095
|
if (typeof obj === "number") {
|
|
2732
|
-
if (op.
|
|
2733
|
-
if (op.code.mode === gaiaShared.AddressingMode.Immediate) {
|
|
2096
|
+
if (op.mode === "Immediate") {
|
|
2734
2097
|
return obj;
|
|
2735
2098
|
}
|
|
2736
|
-
return this._blockReader.resolveName(obj,
|
|
2099
|
+
return this._blockReader.resolveName(obj, shared.AddressType.Address, isBranch);
|
|
2737
2100
|
}
|
|
2738
2101
|
if (this.getObjectType(obj) === "LocationWrapper" /* LocationWrapper */) {
|
|
2739
2102
|
const lw = obj;
|
|
@@ -2744,7 +2107,7 @@ var BlockWriter = class {
|
|
|
2744
2107
|
if (op.size === 4) {
|
|
2745
2108
|
return addr;
|
|
2746
2109
|
}
|
|
2747
|
-
if (addr.isCodeBank && addr.offset <
|
|
2110
|
+
if (addr.isCodeBank && addr.offset < shared.Address.UPPER_BANK) {
|
|
2748
2111
|
const label = this._root.mnemonics[addr.offset];
|
|
2749
2112
|
if (label) {
|
|
2750
2113
|
return label;
|
|
@@ -2752,15 +2115,7 @@ var BlockWriter = class {
|
|
|
2752
2115
|
}
|
|
2753
2116
|
return addr.offset;
|
|
2754
2117
|
}
|
|
2755
|
-
if (obj &&
|
|
2756
|
-
const typed = obj;
|
|
2757
|
-
if (typed._tag === "Byte" || typed._tag === "Word" || typed._tag === "TypedNumber") {
|
|
2758
|
-
return obj;
|
|
2759
|
-
}
|
|
2760
|
-
if (typeof typed.value === "number" && op.code.mode !== gaiaShared.AddressingMode.Immediate) {
|
|
2761
|
-
const resolved = this._blockReader.resolveName(typed.value, gaiaShared.AddressType.Address, isBranch);
|
|
2762
|
-
return { ...typed, value: resolved };
|
|
2763
|
-
}
|
|
2118
|
+
if (obj && obj instanceof shared.TypedNumber) {
|
|
2764
2119
|
return obj;
|
|
2765
2120
|
}
|
|
2766
2121
|
return obj;
|
|
@@ -2770,29 +2125,29 @@ var BlockWriter = class {
|
|
|
2770
2125
|
return "String" /* String */;
|
|
2771
2126
|
}
|
|
2772
2127
|
if (obj._tag) {
|
|
2773
|
-
if (obj._tag === "Byte" || obj._tag === "Word") {
|
|
2774
|
-
return "TypedNumber" /* TypedNumber */;
|
|
2775
|
-
}
|
|
2776
2128
|
return obj._tag;
|
|
2777
2129
|
}
|
|
2130
|
+
if (obj instanceof shared.TypedNumber) {
|
|
2131
|
+
return "TypedNumber" /* TypedNumber */;
|
|
2132
|
+
}
|
|
2778
2133
|
if (Array.isArray(obj)) {
|
|
2779
2134
|
if (obj.length > 0) {
|
|
2780
2135
|
if (obj[0] && typeof obj[0] === "object" && "location" in obj[0] && "object" in obj[0]) {
|
|
2781
2136
|
return "TableEntryArray" /* TableEntryArray */;
|
|
2782
2137
|
}
|
|
2783
|
-
if (obj[0] instanceof Op) {
|
|
2138
|
+
if (obj[0] instanceof shared.Op) {
|
|
2784
2139
|
return "OpArray" /* OpArray */;
|
|
2785
2140
|
}
|
|
2786
2141
|
}
|
|
2787
2142
|
return "Array" /* Array */;
|
|
2788
2143
|
}
|
|
2789
|
-
if (obj instanceof Op) {
|
|
2144
|
+
if (obj instanceof shared.Op) {
|
|
2790
2145
|
return "OpArray" /* OpArray */;
|
|
2791
2146
|
}
|
|
2792
|
-
if (obj instanceof
|
|
2147
|
+
if (obj instanceof shared.LocationWrapper) {
|
|
2793
2148
|
return "LocationWrapper" /* LocationWrapper */;
|
|
2794
2149
|
}
|
|
2795
|
-
if (obj instanceof
|
|
2150
|
+
if (obj instanceof shared.Address) {
|
|
2796
2151
|
return "Address" /* Address */;
|
|
2797
2152
|
}
|
|
2798
2153
|
if (obj instanceof Uint8Array) {
|
|
@@ -2889,7 +2244,7 @@ var BlockWriter = class {
|
|
|
2889
2244
|
this._isInline = true;
|
|
2890
2245
|
for (const part of structObj.parts) {
|
|
2891
2246
|
const partLines = this.writeObject(part, depth);
|
|
2892
|
-
parts.push(partLines.join(
|
|
2247
|
+
parts.push(partLines.join(NEWLINE));
|
|
2893
2248
|
}
|
|
2894
2249
|
const line = `${structObj.name} < ${parts.join(", ")} >`;
|
|
2895
2250
|
this._isInline = isInline;
|
|
@@ -2911,7 +2266,7 @@ var BlockWriter = class {
|
|
|
2911
2266
|
lines.push(` ${labelResult.referenceName}:`);
|
|
2912
2267
|
}
|
|
2913
2268
|
}
|
|
2914
|
-
let opLine = ` ${op.
|
|
2269
|
+
let opLine = ` ${op.mnem} `;
|
|
2915
2270
|
if (op.copDef) {
|
|
2916
2271
|
opLine += `[${op.copDef.mnem}]`;
|
|
2917
2272
|
if (op.operands && op.operands.length > 1) {
|
|
@@ -2923,26 +2278,18 @@ var BlockWriter = class {
|
|
|
2923
2278
|
opLine += ` ( ${operandStrings.join(", ")} )`;
|
|
2924
2279
|
}
|
|
2925
2280
|
} else if (op.operands && op.operands.length > 0) {
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2281
|
+
const isBr = op.mnem[0] === "J" || op.mode === "PCRelative" || op.mode === "PCRelativeLong";
|
|
2282
|
+
const resolvedOperand = this.resolveOperand(op, op.operands[0], isBr);
|
|
2283
|
+
const format = this._root.addrLookup[op.mode]?.formatString;
|
|
2284
|
+
if (format) {
|
|
2285
|
+
let actualFormat = format;
|
|
2286
|
+
if (op.mode === "Immediate" && op.size === 3) {
|
|
2287
|
+
actualFormat = format.replace("X2", "X4");
|
|
2932
2288
|
}
|
|
2289
|
+
const resolvedSecondOperand = op.operands.length > 1 ? this.resolveOperand(op, op.operands[1], isBr) : void 0;
|
|
2290
|
+
opLine += this.formatOperand(actualFormat, [resolvedOperand, resolvedSecondOperand]);
|
|
2933
2291
|
} else {
|
|
2934
|
-
|
|
2935
|
-
const resolvedOperand = this.resolveOperand(op, op.operands[0], isBr);
|
|
2936
|
-
const format = this._root.config.asmFormats?.[op.code.mode];
|
|
2937
|
-
if (format) {
|
|
2938
|
-
let actualFormat = format;
|
|
2939
|
-
if (op.code.mode === gaiaShared.AddressingMode.Immediate && op.size === 3) {
|
|
2940
|
-
actualFormat = format.replace("X2", "X4");
|
|
2941
|
-
}
|
|
2942
|
-
opLine += this.formatOperand(actualFormat, [resolvedOperand, ...op.operands.slice(1)]);
|
|
2943
|
-
} else {
|
|
2944
|
-
opLine += this.formatDefaultOperand(resolvedOperand, op.size);
|
|
2945
|
-
}
|
|
2292
|
+
opLine += this.formatDefaultOperand(resolvedOperand, op.size);
|
|
2946
2293
|
}
|
|
2947
2294
|
}
|
|
2948
2295
|
lines.push(opLine);
|
|
@@ -2955,7 +2302,7 @@ var BlockWriter = class {
|
|
|
2955
2302
|
if (typeof operand === "string") {
|
|
2956
2303
|
return operand;
|
|
2957
2304
|
}
|
|
2958
|
-
if (operand &&
|
|
2305
|
+
if (operand && operand instanceof shared.TypedNumber) {
|
|
2959
2306
|
return this.formatTypedNumber(operand);
|
|
2960
2307
|
}
|
|
2961
2308
|
if (typeof operand === "number") {
|
|
@@ -2973,9 +2320,9 @@ var BlockWriter = class {
|
|
|
2973
2320
|
while (ix >= 0) {
|
|
2974
2321
|
const hexStr = str.substring(ix + 1, ix + 7);
|
|
2975
2322
|
const rawAddr = parseInt(hexStr, 16);
|
|
2976
|
-
const adrs = new
|
|
2977
|
-
if (adrs.space ===
|
|
2978
|
-
const addressType = char === "^" ?
|
|
2323
|
+
const adrs = new shared.Address(rawAddr >> 16 & 255, rawAddr & 65535);
|
|
2324
|
+
if (adrs.space === shared.AddressSpace.ROM) {
|
|
2325
|
+
const addressType = char === "^" ? shared.AddressType.Offset : shared.AddressType.Address;
|
|
2979
2326
|
const location = adrs.toInt();
|
|
2980
2327
|
const name = this._blockReader.resolveName(location, addressType, false);
|
|
2981
2328
|
str = str.replace(str.substring(ix, ix + 7), name);
|
|
@@ -3002,17 +2349,17 @@ var BlockWriter = class {
|
|
|
3002
2349
|
if (cmd && cmd.types) {
|
|
3003
2350
|
for (const t of cmd.types) {
|
|
3004
2351
|
switch (t) {
|
|
3005
|
-
case
|
|
2352
|
+
case shared.MemberType.Byte:
|
|
3006
2353
|
mix += 1;
|
|
3007
2354
|
break;
|
|
3008
|
-
case
|
|
3009
|
-
case
|
|
2355
|
+
case shared.MemberType.Word:
|
|
2356
|
+
case shared.MemberType.Offset:
|
|
3010
2357
|
mix += 2;
|
|
3011
2358
|
break;
|
|
3012
|
-
case
|
|
2359
|
+
case shared.MemberType.Address:
|
|
3013
2360
|
mix += 3;
|
|
3014
2361
|
break;
|
|
3015
|
-
case
|
|
2362
|
+
case shared.MemberType.Binary:
|
|
3016
2363
|
mix += parts.length - 1;
|
|
3017
2364
|
break;
|
|
3018
2365
|
default:
|
|
@@ -3033,7 +2380,7 @@ var BlockWriter = class {
|
|
|
3033
2380
|
}
|
|
3034
2381
|
writeArray(arr, depth) {
|
|
3035
2382
|
const lines = [];
|
|
3036
|
-
const indent = " ".repeat(depth);
|
|
2383
|
+
const indent = " ".repeat(Math.max(0, depth));
|
|
3037
2384
|
const isInline = this._isInline;
|
|
3038
2385
|
lines.push("[");
|
|
3039
2386
|
this._isInline = false;
|
|
@@ -3058,16 +2405,10 @@ var BlockWriter = class {
|
|
|
3058
2405
|
return [`#$${num.toString(16).toUpperCase().padStart(6, "0")}`];
|
|
3059
2406
|
}
|
|
3060
2407
|
formatTypedNumber(num) {
|
|
3061
|
-
let size = 1;
|
|
3062
|
-
if ("size" in num) {
|
|
3063
|
-
size = num.size;
|
|
3064
|
-
} else if (num._tag === "Word") {
|
|
3065
|
-
size = 2;
|
|
3066
|
-
}
|
|
3067
2408
|
if (typeof num.value === "number") {
|
|
3068
|
-
const width = size * 2;
|
|
2409
|
+
const width = num.size * 2;
|
|
3069
2410
|
const hex = num.value.toString(16).toUpperCase().padStart(width, "0");
|
|
3070
|
-
return size === 1 ? `#${hex}` : `#$${hex}`;
|
|
2411
|
+
return num.size === 1 ? `#${hex}` : `#$${hex}`;
|
|
3071
2412
|
}
|
|
3072
2413
|
return `#${num.value}`;
|
|
3073
2414
|
}
|
|
@@ -3095,509 +2436,1627 @@ var BlockWriter = class {
|
|
|
3095
2436
|
});
|
|
3096
2437
|
}
|
|
3097
2438
|
};
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
this.
|
|
3103
|
-
this.
|
|
3104
|
-
this.
|
|
3105
|
-
this.
|
|
3106
|
-
this.
|
|
3107
|
-
this.
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
2439
|
+
var RomLayout = class _RomLayout {
|
|
2440
|
+
constructor(files) {
|
|
2441
|
+
this.bestResult = new Array(512).fill(0);
|
|
2442
|
+
this.bestSample = new Array(512).fill(0);
|
|
2443
|
+
this.currentBank = 0;
|
|
2444
|
+
this.currentUpper = false;
|
|
2445
|
+
this.bestDepth = 0;
|
|
2446
|
+
this.bestOffset = 0;
|
|
2447
|
+
this.bestRemain = 0;
|
|
2448
|
+
this.unmatchedFiles = Array.from(files).filter((x) => (x.size || 0) > 0).sort((a, b) => {
|
|
2449
|
+
const aAsm = a.parts ? 0 : 1;
|
|
2450
|
+
const bAsm = b.parts ? 0 : 1;
|
|
2451
|
+
if (aAsm !== bAsm) return aAsm - bAsm;
|
|
2452
|
+
if (b.size !== a.size) return b.size - a.size;
|
|
2453
|
+
if (a.location !== b.location) return a.location - b.location;
|
|
2454
|
+
return a.name.localeCompare(b.name);
|
|
2455
|
+
});
|
|
3113
2456
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
2457
|
+
static {
|
|
2458
|
+
this.MIN_ACCEPTED_REMAINING = 32;
|
|
2459
|
+
}
|
|
2460
|
+
organize() {
|
|
2461
|
+
for (let page = 0; page < 128; page++) {
|
|
2462
|
+
if (this.unmatchedFiles.length === 0) break;
|
|
2463
|
+
let remain = shared.RomProcessingConstants.PAGE_SIZE;
|
|
2464
|
+
if (page === 1) remain -= shared.RomProcessingConstants.SNES_HEADER_SIZE;
|
|
2465
|
+
this.currentUpper = (page & 1) !== 0;
|
|
2466
|
+
this.currentBank = page >> 1;
|
|
2467
|
+
this.bestDepth = 0;
|
|
2468
|
+
this.bestRemain = remain;
|
|
2469
|
+
this.bestOffset = 0;
|
|
2470
|
+
const start = page << 15;
|
|
2471
|
+
this.testDepth(0, 0, remain, this.currentUpper);
|
|
2472
|
+
if (this.currentUpper) {
|
|
2473
|
+
this.bestOffset = this.bestDepth;
|
|
2474
|
+
this.testDepth(0, this.bestDepth, this.bestRemain, false);
|
|
2475
|
+
}
|
|
2476
|
+
let position = start;
|
|
2477
|
+
for (let i = 0; i < this.bestDepth; ) {
|
|
2478
|
+
const file = this.unmatchedFiles[this.bestResult[i++]];
|
|
2479
|
+
file.location = position;
|
|
2480
|
+
console.log(` ${position.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
|
|
2481
|
+
position += file.size || 0;
|
|
2482
|
+
}
|
|
2483
|
+
console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with ${this.bestDepth} files ${this.bestRemain} remaining`);
|
|
2484
|
+
this.commitPage();
|
|
2485
|
+
}
|
|
2486
|
+
if (this.unmatchedFiles.length > 0) {
|
|
2487
|
+
const names = this.unmatchedFiles.map((x) => x.name).join("\r\n");
|
|
2488
|
+
throw new Error(`Unable to match ${this.unmatchedFiles.length} files\r
|
|
2489
|
+
${names}`);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
testDepth(startIndex, depth, remain, asmMode) {
|
|
2493
|
+
for (let fileIndex = startIndex; fileIndex < this.unmatchedFiles.length; fileIndex++) {
|
|
2494
|
+
const file = this.unmatchedFiles[fileIndex];
|
|
2495
|
+
const fileSize = file.size || 0;
|
|
2496
|
+
if (fileSize > remain) continue;
|
|
2497
|
+
if (file.parts) {
|
|
2498
|
+
if (!asmMode) {
|
|
2499
|
+
if (!this.currentUpper || (file.bank ?? -1) >= 0) continue;
|
|
2500
|
+
} else if (file.bank !== this.currentBank) {
|
|
2501
|
+
continue;
|
|
2502
|
+
}
|
|
2503
|
+
} else if (asmMode) {
|
|
2504
|
+
continue;
|
|
2505
|
+
} else if (file.upper && !this.currentUpper) {
|
|
2506
|
+
continue;
|
|
2507
|
+
}
|
|
2508
|
+
let inList = false;
|
|
2509
|
+
for (let y = this.bestOffset; --y >= 0; ) {
|
|
2510
|
+
if (this.bestResult[y] === fileIndex) {
|
|
2511
|
+
inList = true;
|
|
2512
|
+
break;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
if (inList) continue;
|
|
2516
|
+
this.bestSample[depth] = fileIndex;
|
|
2517
|
+
const newRemain = remain - fileSize;
|
|
2518
|
+
if (newRemain < this.bestRemain) {
|
|
2519
|
+
this.bestRemain = newRemain;
|
|
2520
|
+
this.bestDepth = depth + 1;
|
|
2521
|
+
for (let i = this.bestOffset; i < this.bestDepth; i++) {
|
|
2522
|
+
this.bestResult[i] = this.bestSample[i];
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
if (newRemain < _RomLayout.MIN_ACCEPTED_REMAINING) return true;
|
|
2526
|
+
if (this.testDepth(fileIndex + 1, depth + 1, newRemain, asmMode)) return true;
|
|
3123
2527
|
}
|
|
2528
|
+
return true;
|
|
3124
2529
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
2530
|
+
commitPage() {
|
|
2531
|
+
if (this.bestOffset > 0) {
|
|
2532
|
+
for (let i = this.bestDepth; --i >= 0; ) {
|
|
2533
|
+
let lastY = 0;
|
|
2534
|
+
let lastX = 0;
|
|
2535
|
+
let y = 0;
|
|
2536
|
+
for (let x = this.bestDepth; --x >= 0; ) {
|
|
2537
|
+
y = this.bestResult[x];
|
|
2538
|
+
if (y > lastY) {
|
|
2539
|
+
lastY = y;
|
|
2540
|
+
lastX = x;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
this.bestResult[lastX] = 0;
|
|
2544
|
+
this.unmatchedFiles.splice(lastY, 1);
|
|
2545
|
+
}
|
|
2546
|
+
} else {
|
|
2547
|
+
for (let i = this.bestDepth; --i >= 0; ) {
|
|
2548
|
+
this.unmatchedFiles.splice(this.bestResult[i], 1);
|
|
3134
2549
|
}
|
|
3135
|
-
return new _ProjectRoot(config);
|
|
3136
|
-
} catch (error) {
|
|
3137
|
-
console.warn(`Failed to load project file: ${error}. Using directory-based configuration.`);
|
|
3138
2550
|
}
|
|
3139
|
-
const defaultConfig = {
|
|
3140
|
-
name: "GaiaLabs",
|
|
3141
|
-
baseDir: path,
|
|
3142
|
-
flipsPath: process?.env?.flips_path,
|
|
3143
|
-
romPath: process?.env?.rom_path || "",
|
|
3144
|
-
database: "us",
|
|
3145
|
-
databasePath: `${path}/db/us`,
|
|
3146
|
-
systemPath: `${path}/db/snes`,
|
|
3147
|
-
resources: {}
|
|
3148
|
-
};
|
|
3149
|
-
return new _ProjectRoot(defaultConfig);
|
|
3150
|
-
}
|
|
3151
|
-
/**
|
|
3152
|
-
* Build the ROM (simplified)
|
|
3153
|
-
*/
|
|
3154
|
-
async build() {
|
|
3155
|
-
throw new Error("ROM building not yet implemented");
|
|
3156
|
-
}
|
|
3157
|
-
/**
|
|
3158
|
-
* Dump database and extract ROM data
|
|
3159
|
-
*/
|
|
3160
|
-
async dumpDatabase() {
|
|
3161
|
-
const root = await gaiaShared.DbRootUtils.fromFolder(this.databasePath, this.systemPath);
|
|
3162
|
-
const data = await gaiaShared.readFileAsBinary(this.romPath);
|
|
3163
|
-
root.paths = this.resources;
|
|
3164
|
-
const fileReader = new FileReader(data, root, this.getCompression());
|
|
3165
|
-
await fileReader.extract(this.baseDir);
|
|
3166
|
-
const sfxReader = new SfxReader(data, root);
|
|
3167
|
-
await sfxReader.extract(this.baseDir);
|
|
3168
|
-
const blockReader = new BlockReader(data, root);
|
|
3169
|
-
blockReader.analyzeAndResolve();
|
|
3170
|
-
const blockWriter = new BlockWriter(blockReader);
|
|
3171
|
-
await blockWriter.writeBlocks(this.baseDir);
|
|
3172
|
-
return root;
|
|
3173
2551
|
}
|
|
3174
2552
|
};
|
|
3175
|
-
|
|
3176
|
-
// src/api/RomProcessor.ts
|
|
3177
2553
|
var RomProcessor = class _RomProcessor {
|
|
3178
|
-
constructor(
|
|
3179
|
-
this.
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
const
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
2554
|
+
constructor(writer) {
|
|
2555
|
+
this.writer = writer;
|
|
2556
|
+
}
|
|
2557
|
+
async repack(allFiles) {
|
|
2558
|
+
const patches = allFiles.filter((x) => x.type === shared.BinType.Patch);
|
|
2559
|
+
const asmFiles = allFiles.filter((x) => !!x.parts);
|
|
2560
|
+
_RomProcessor.applyPatches(asmFiles, patches);
|
|
2561
|
+
for (const asm of asmFiles) {
|
|
2562
|
+
shared.ChunkFileUtils.calculateSize(asm);
|
|
2563
|
+
}
|
|
2564
|
+
const layout = new RomLayout(allFiles);
|
|
2565
|
+
layout.organize();
|
|
2566
|
+
for (const file of asmFiles) {
|
|
2567
|
+
shared.ChunkFileUtils.rebase(file);
|
|
2568
|
+
}
|
|
2569
|
+
for (const f of asmFiles) {
|
|
2570
|
+
const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
|
|
2571
|
+
f.includeLookup = /* @__PURE__ */ new Map();
|
|
2572
|
+
for (const b of includeBlocks) {
|
|
2573
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
2574
|
+
}
|
|
2575
|
+
for (const b of (f.parts || []).filter((x) => !!x.label)) {
|
|
2576
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
const blockLookup = /* @__PURE__ */ new Map();
|
|
2580
|
+
for (const f of allFiles) {
|
|
2581
|
+
blockLookup.set(f.name.toUpperCase(), f.location);
|
|
2582
|
+
}
|
|
2583
|
+
for (const file of allFiles) {
|
|
2584
|
+
await this.writer.writeFile(file, blockLookup);
|
|
2585
|
+
}
|
|
2586
|
+
const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
|
|
2587
|
+
for (const ep of this.writer.entryPoints) {
|
|
2588
|
+
const match = entryBlocks.find((b) => b.label === ep.name);
|
|
2589
|
+
if (match) this.writer.writeTransform(ep.location, match.location & 65535);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
static applyPatches(asmFiles, patches) {
|
|
2593
|
+
for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
|
|
2594
|
+
let file = null;
|
|
2595
|
+
let dstIx = -1;
|
|
2596
|
+
const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
|
|
2597
|
+
for (let ix = 0; patch.parts && ix < patch.parts.length; ) {
|
|
2598
|
+
const block = patch.parts[ix];
|
|
2599
|
+
let match = null;
|
|
2600
|
+
if (block.label) {
|
|
2601
|
+
for (const i of inc) {
|
|
2602
|
+
if (!i.parts) continue;
|
|
2603
|
+
for (let y = 0; y < i.parts.length; y++) {
|
|
2604
|
+
const check = i.parts[y];
|
|
2605
|
+
if (check.label === block.label) {
|
|
2606
|
+
file = i;
|
|
2607
|
+
dstIx = y;
|
|
2608
|
+
match = check;
|
|
2609
|
+
break;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
if (match) {
|
|
2615
|
+
file.parts[dstIx++] = block;
|
|
2616
|
+
} else if (dstIx >= 0) {
|
|
2617
|
+
file.parts.splice(dstIx++, 0, block);
|
|
2618
|
+
} else {
|
|
2619
|
+
ix++;
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
file.includes = file.includes || /* @__PURE__ */ new Set();
|
|
2623
|
+
file.includes.add(patch.name.toUpperCase());
|
|
2624
|
+
patch.parts.splice(ix, 1);
|
|
2625
|
+
}
|
|
3203
2626
|
}
|
|
3204
2627
|
}
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
2628
|
+
};
|
|
2629
|
+
var RomWriter = class {
|
|
2630
|
+
constructor(entryPoints, cartName, makerCode) {
|
|
2631
|
+
this.cartName = cartName;
|
|
2632
|
+
this.makerCode = makerCode;
|
|
2633
|
+
this.entryPoints = entryPoints;
|
|
2634
|
+
this.outBuffer = new Uint8Array(4194304);
|
|
2635
|
+
}
|
|
2636
|
+
async repack(files) {
|
|
2637
|
+
const processor = new RomProcessor(this);
|
|
2638
|
+
await processor.repack(files);
|
|
2639
|
+
this.writeHeader();
|
|
2640
|
+
this.writeEntryPoints(files.filter((x) => !!x.parts));
|
|
2641
|
+
this.writeChecksum();
|
|
2642
|
+
return this.outBuffer;
|
|
2643
|
+
}
|
|
2644
|
+
writeHeader() {
|
|
2645
|
+
const buf = this.outBuffer;
|
|
2646
|
+
let pos = 65456;
|
|
2647
|
+
this.writeAscii(this.makerCode.padEnd(6, " "), pos);
|
|
2648
|
+
pos += 6;
|
|
2649
|
+
for (let i = 0; i < 10; i++) buf[pos++] = 0;
|
|
2650
|
+
this.writeAscii(this.cartName.toUpperCase().padEnd(21, " "), pos);
|
|
2651
|
+
pos += 21;
|
|
2652
|
+
buf[pos++] = 49;
|
|
2653
|
+
buf[pos++] = 2;
|
|
2654
|
+
buf[pos++] = 12;
|
|
2655
|
+
buf[pos++] = 3;
|
|
2656
|
+
buf[pos++] = 1;
|
|
2657
|
+
buf[pos++] = 51;
|
|
2658
|
+
buf[pos++] = 0;
|
|
2659
|
+
}
|
|
2660
|
+
writeChecksum() {
|
|
2661
|
+
const buf = this.outBuffer;
|
|
2662
|
+
let sum = 0;
|
|
2663
|
+
for (let i = 0; i < buf.length; i++) sum += buf[i] & 255;
|
|
2664
|
+
buf[65502] = sum & 255;
|
|
2665
|
+
buf[65503] = sum >> 8 & 255;
|
|
2666
|
+
const comp = ~sum & 65535;
|
|
2667
|
+
buf[65500] = comp & 255;
|
|
2668
|
+
buf[65501] = comp >> 8 & 255;
|
|
2669
|
+
}
|
|
2670
|
+
// private async generatePatch(): Promise<void> {
|
|
2671
|
+
// const flips = this._projectRoot.flipsPath;
|
|
2672
|
+
// if (!flips) return;
|
|
2673
|
+
// if (typeof process === 'undefined' || !process.versions?.node) return;
|
|
2674
|
+
// const { spawn } = await import('child_process');
|
|
2675
|
+
// const { resolve } = await import('path');
|
|
2676
|
+
// this.bpsPath = `${this._projectRoot.baseDir}/${this._projectRoot.name}.bps`;
|
|
2677
|
+
// await new Promise<void>((resolvePromise) => {
|
|
2678
|
+
// const p = spawn(flips, ['--create', '--bps', `${this._projectRoot.romPath}`, `${this.romPath}`, `${this.bpsPath}`], {
|
|
2679
|
+
// stdio: ['ignore', 'pipe', 'pipe']
|
|
2680
|
+
// });
|
|
2681
|
+
// p.on('close', () => resolvePromise());
|
|
2682
|
+
// });
|
|
2683
|
+
// }
|
|
2684
|
+
writeEntryPoints(asmFiles) {
|
|
2685
|
+
this.outBuffer;
|
|
2686
|
+
const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
|
|
2687
|
+
for (const ep of this.entryPoints) {
|
|
2688
|
+
const match = entryBlocks.find((b) => b.label === ep.name);
|
|
2689
|
+
if (match) this.writeTransform(ep.location, match.location & 65535);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
writeTransform(location, value, size) {
|
|
2693
|
+
const buf = this.outBuffer;
|
|
2694
|
+
if (size === void 0) {
|
|
2695
|
+
if (value <= 255) {
|
|
2696
|
+
size = 1;
|
|
2697
|
+
} else if (value <= 65535) {
|
|
2698
|
+
size = 2;
|
|
2699
|
+
} else if (value <= 16777215) {
|
|
2700
|
+
size = 3;
|
|
2701
|
+
} else {
|
|
2702
|
+
size = 4;
|
|
2703
|
+
}
|
|
3211
2704
|
}
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
outputPath,
|
|
3275
|
-
romSize: 0,
|
|
3276
|
-
errors: []
|
|
3277
|
-
};
|
|
3278
|
-
console.warn("ROM building not yet implemented - awaiting Assembler conversion");
|
|
3279
|
-
return result;
|
|
2705
|
+
switch (size) {
|
|
2706
|
+
case 1:
|
|
2707
|
+
buf[location] = value & 255;
|
|
2708
|
+
break;
|
|
2709
|
+
case 2:
|
|
2710
|
+
buf[location] = value & 255;
|
|
2711
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2712
|
+
break;
|
|
2713
|
+
case 3:
|
|
2714
|
+
buf[location] = value & 255;
|
|
2715
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2716
|
+
buf[location + 2] = value >> 16 & 255;
|
|
2717
|
+
break;
|
|
2718
|
+
case 4:
|
|
2719
|
+
buf[location] = value & 255;
|
|
2720
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2721
|
+
buf[location + 2] = value >> 16 & 255;
|
|
2722
|
+
buf[location + 3] = value >> 24 & 255;
|
|
2723
|
+
break;
|
|
2724
|
+
default:
|
|
2725
|
+
throw new Error(`Invalid size ${size} for writeTransform`);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
async writeFile(file, _chunkLookup) {
|
|
2729
|
+
const start = file.location;
|
|
2730
|
+
let pos = start;
|
|
2731
|
+
const buf = this.outBuffer;
|
|
2732
|
+
if (file.rawData) {
|
|
2733
|
+
const data = file.rawData;
|
|
2734
|
+
let remain = file.size;
|
|
2735
|
+
let srcPos = 0;
|
|
2736
|
+
if (file.type === shared.BinType.Tilemap) {
|
|
2737
|
+
remain -= 2;
|
|
2738
|
+
buf[pos++] = data[srcPos++];
|
|
2739
|
+
buf[pos++] = data[srcPos++];
|
|
2740
|
+
} else if (file.type === shared.BinType.Meta17) {
|
|
2741
|
+
remain -= 4;
|
|
2742
|
+
for (let i = 0; i < 4; i++) buf[pos++] = data[srcPos++];
|
|
2743
|
+
} else if (file.type === shared.BinType.Sound) {
|
|
2744
|
+
remain -= 2;
|
|
2745
|
+
buf[pos++] = remain & 255;
|
|
2746
|
+
buf[pos++] = remain >> 8 & 255;
|
|
2747
|
+
}
|
|
2748
|
+
if (file.compressed !== void 0) {
|
|
2749
|
+
remain -= 2;
|
|
2750
|
+
const inverse = 0 - remain & 65535;
|
|
2751
|
+
buf[pos++] = inverse & 255;
|
|
2752
|
+
buf[pos++] = inverse >> 8 & 255;
|
|
2753
|
+
}
|
|
2754
|
+
while (remain > 0) {
|
|
2755
|
+
buf[pos++] = data[srcPos++];
|
|
2756
|
+
remain--;
|
|
2757
|
+
}
|
|
2758
|
+
} else {
|
|
2759
|
+
if (file.parts && file.parts.length > 0) {
|
|
2760
|
+
if (file.parts[0].location !== file.location && (file.location || 0) !== 0) {
|
|
2761
|
+
throw new Error("Assembly was not based properly");
|
|
2762
|
+
}
|
|
2763
|
+
this.parseAssembly(file.parts, _chunkLookup, file.includeLookup);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return pos - start;
|
|
3280
2767
|
}
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
return this.projectRoot.getCompression();
|
|
2768
|
+
// No address mapping required: layout assigns absolute file offsets
|
|
2769
|
+
writeAscii(text, pos) {
|
|
2770
|
+
const buf = this.outBuffer;
|
|
2771
|
+
for (let i = 0; i < text.length; i++) buf[pos + i] = text.charCodeAt(i) & 255;
|
|
3286
2772
|
}
|
|
3287
2773
|
/**
|
|
3288
|
-
*
|
|
2774
|
+
* Parse assembly blocks and write binary data to output buffer
|
|
2775
|
+
* Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs ParseAssembly method
|
|
3289
2776
|
*/
|
|
3290
|
-
|
|
3291
|
-
|
|
2777
|
+
parseAssembly(blocks, chunkLookup, includeLookup) {
|
|
2778
|
+
if (!blocks) {
|
|
2779
|
+
throw new Error("Assembly has not been parsed");
|
|
2780
|
+
}
|
|
2781
|
+
const buf = this.outBuffer;
|
|
2782
|
+
for (const block of blocks) {
|
|
2783
|
+
let position = block.location;
|
|
2784
|
+
const objList = block.objList;
|
|
2785
|
+
let opos = 0;
|
|
2786
|
+
const processObject = (obj, parentOp) => {
|
|
2787
|
+
let currentObj = obj;
|
|
2788
|
+
while (true) {
|
|
2789
|
+
if (Array.isArray(currentObj)) {
|
|
2790
|
+
for (const obj2 of currentObj) {
|
|
2791
|
+
processObject(obj2, parentOp);
|
|
2792
|
+
}
|
|
2793
|
+
break;
|
|
2794
|
+
} else if (this.isTableEntry(currentObj)) {
|
|
2795
|
+
currentObj = currentObj.object;
|
|
2796
|
+
continue;
|
|
2797
|
+
} else if (currentObj instanceof shared.Op) {
|
|
2798
|
+
const op = currentObj;
|
|
2799
|
+
buf[position++] = op.code & 255;
|
|
2800
|
+
opos += op.size;
|
|
2801
|
+
for (const operand of op.operands) {
|
|
2802
|
+
processObject(operand, op);
|
|
2803
|
+
}
|
|
2804
|
+
break;
|
|
2805
|
+
} else if (currentObj instanceof Uint8Array) {
|
|
2806
|
+
const arr = currentObj;
|
|
2807
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2808
|
+
buf[position + i] = arr[i];
|
|
2809
|
+
}
|
|
2810
|
+
position += arr.length;
|
|
2811
|
+
opos += arr.length;
|
|
2812
|
+
break;
|
|
2813
|
+
} else if (typeof currentObj === "string") {
|
|
2814
|
+
const str = currentObj;
|
|
2815
|
+
let label = str;
|
|
2816
|
+
let ix = 0;
|
|
2817
|
+
while (ix < label.length && shared.RomProcessingConstants.ADDRESS_SPACE.includes(label[ix])) {
|
|
2818
|
+
ix++;
|
|
2819
|
+
}
|
|
2820
|
+
if (ix > 0) {
|
|
2821
|
+
label = label.substring(ix);
|
|
2822
|
+
}
|
|
2823
|
+
let loc;
|
|
2824
|
+
const isRelative = parentOp && (parentOp.mode === "PCRelative" || parentOp.mode === "PCRelativeLong");
|
|
2825
|
+
const operatorIdx = this.indexOfAny(label, shared.RomProcessingConstants.OPERATORS);
|
|
2826
|
+
let offset = null;
|
|
2827
|
+
let useMarker = false;
|
|
2828
|
+
if (operatorIdx > 0) {
|
|
2829
|
+
if (label[operatorIdx + 1] === "M") {
|
|
2830
|
+
useMarker = true;
|
|
2831
|
+
} else {
|
|
2832
|
+
offset = parseInt(label.substring(operatorIdx + 1), 16);
|
|
2833
|
+
if (label[operatorIdx] === "-") {
|
|
2834
|
+
offset = -offset;
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
label = label.substring(0, operatorIdx);
|
|
2838
|
+
}
|
|
2839
|
+
const labelUpper = label.toUpperCase();
|
|
2840
|
+
let target;
|
|
2841
|
+
if (includeLookup.has(labelUpper)) {
|
|
2842
|
+
target = includeLookup.get(labelUpper);
|
|
2843
|
+
loc = target.location;
|
|
2844
|
+
} else if (chunkLookup.has(labelUpper)) {
|
|
2845
|
+
loc = chunkLookup.get(labelUpper);
|
|
2846
|
+
} else {
|
|
2847
|
+
if (label.startsWith("#")) {
|
|
2848
|
+
label = label.substring(1);
|
|
2849
|
+
}
|
|
2850
|
+
if (label.startsWith("$")) {
|
|
2851
|
+
label = label.substring(1);
|
|
2852
|
+
}
|
|
2853
|
+
if (isRelative && label.length > 4) {
|
|
2854
|
+
const off = parseInt(label, 16) - (block.location + opos);
|
|
2855
|
+
currentObj = parentOp?.size === 2 ? off & 255 : off & 65535;
|
|
2856
|
+
continue;
|
|
2857
|
+
} else {
|
|
2858
|
+
switch (label.length) {
|
|
2859
|
+
case 1:
|
|
2860
|
+
case 2:
|
|
2861
|
+
currentObj = new shared.Byte(parseInt(label, 16));
|
|
2862
|
+
continue;
|
|
2863
|
+
case 3:
|
|
2864
|
+
case 4:
|
|
2865
|
+
currentObj = new shared.Word(parseInt(label, 16));
|
|
2866
|
+
continue;
|
|
2867
|
+
case 5:
|
|
2868
|
+
case 6:
|
|
2869
|
+
currentObj = new shared.Long(parseInt(label, 16));
|
|
2870
|
+
continue;
|
|
2871
|
+
default:
|
|
2872
|
+
throw new Error(`Invalid operand '${label}'`);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
let type = shared.Address.typeFromCode(str[0]);
|
|
2877
|
+
if (type === shared.AddressType.Unknown) {
|
|
2878
|
+
type = parentOp?.size === 4 ? shared.AddressType.Address : parentOp?.size === 2 ? shared.AddressType.Unknown : shared.AddressType.Offset;
|
|
2879
|
+
}
|
|
2880
|
+
if (isRelative) {
|
|
2881
|
+
loc -= block.location + opos;
|
|
2882
|
+
if (type === shared.AddressType.Unknown && !(loc < 128 || loc >= 4194176)) {
|
|
2883
|
+
throw new Error("Relative out of range");
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
if (offset !== null) {
|
|
2887
|
+
loc += offset;
|
|
2888
|
+
} else if (useMarker && target) {
|
|
2889
|
+
let markerOffset = 0;
|
|
2890
|
+
for (const part of target.objList) {
|
|
2891
|
+
if (this.isStringMarker(part)) {
|
|
2892
|
+
loc += markerOffset;
|
|
2893
|
+
break;
|
|
2894
|
+
} else {
|
|
2895
|
+
markerOffset += shared.RomProcessingConstants.getSize(part);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
switch (type) {
|
|
2900
|
+
case shared.AddressType.Offset:
|
|
2901
|
+
currentObj = new shared.Word(loc);
|
|
2902
|
+
continue;
|
|
2903
|
+
case shared.AddressType.Bank:
|
|
2904
|
+
currentObj = new shared.Byte(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
|
|
2905
|
+
continue;
|
|
2906
|
+
case shared.AddressType.WBank:
|
|
2907
|
+
currentObj = new shared.Word(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
|
|
2908
|
+
continue;
|
|
2909
|
+
case shared.AddressType.Address:
|
|
2910
|
+
currentObj = new shared.Long(loc | ((loc & 65535) >= 32768 ? 8388608 : 12582912));
|
|
2911
|
+
continue;
|
|
2912
|
+
default:
|
|
2913
|
+
currentObj = new shared.Byte(loc);
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
} else if (currentObj instanceof shared.TypedNumber) {
|
|
2917
|
+
let value = currentObj.value;
|
|
2918
|
+
for (let i = 0; i < currentObj.size; i++) {
|
|
2919
|
+
buf[position++] = value & 255;
|
|
2920
|
+
value >>= 8;
|
|
2921
|
+
}
|
|
2922
|
+
break;
|
|
2923
|
+
} else if (typeof currentObj === "number") {
|
|
2924
|
+
const num = currentObj;
|
|
2925
|
+
const size = parentOp?.size ?? 0;
|
|
2926
|
+
if (num <= 255 && size <= 2) {
|
|
2927
|
+
buf[position] = num & 255;
|
|
2928
|
+
position++;
|
|
2929
|
+
} else if (num <= 65535 && size <= 3) {
|
|
2930
|
+
buf[position] = num & 255;
|
|
2931
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2932
|
+
position += 2;
|
|
2933
|
+
} else if (num <= 16777215 && size <= 4) {
|
|
2934
|
+
buf[position] = num & 255;
|
|
2935
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2936
|
+
buf[position + 2] = num >> 16 & 255;
|
|
2937
|
+
position += 3;
|
|
2938
|
+
} else {
|
|
2939
|
+
buf[position] = num & 255;
|
|
2940
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2941
|
+
buf[position + 2] = num >> 16 & 255;
|
|
2942
|
+
buf[position + 3] = num >> 24 & 255;
|
|
2943
|
+
position += 4;
|
|
2944
|
+
}
|
|
2945
|
+
break;
|
|
2946
|
+
} else if (this.isStringMarker(currentObj)) {
|
|
2947
|
+
break;
|
|
2948
|
+
} else {
|
|
2949
|
+
throw new Error(`Unable to process '${currentObj}'`);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
};
|
|
2953
|
+
for (const obj of objList) {
|
|
2954
|
+
processObject(obj);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
3292
2957
|
}
|
|
3293
2958
|
/**
|
|
3294
|
-
*
|
|
2959
|
+
* Helper method to find index of any character from an array in a string
|
|
3295
2960
|
*/
|
|
3296
|
-
|
|
3297
|
-
|
|
2961
|
+
indexOfAny(str, chars) {
|
|
2962
|
+
for (let i = 0; i < str.length; i++) {
|
|
2963
|
+
if (chars.includes(str[i])) {
|
|
2964
|
+
return i;
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
return -1;
|
|
3298
2968
|
}
|
|
3299
2969
|
/**
|
|
3300
|
-
*
|
|
2970
|
+
* Helper method to check if an object is a StringMarker
|
|
3301
2971
|
*/
|
|
3302
|
-
|
|
3303
|
-
return
|
|
3304
|
-
}
|
|
3305
|
-
// Private helper methods
|
|
3306
|
-
analyzeHeader() {
|
|
3307
|
-
if (!this.romData) {
|
|
3308
|
-
throw new Error("ROM data not loaded");
|
|
3309
|
-
}
|
|
3310
|
-
const headerOffset = 32704;
|
|
3311
|
-
const title = new TextDecoder().decode(this.romData.slice(headerOffset, headerOffset + 21)).replace(/\0/g, "");
|
|
3312
|
-
const mapMode = this.romData[headerOffset + 21];
|
|
3313
|
-
const cartridgeType = this.romData[headerOffset + 22];
|
|
3314
|
-
const romSize = this.romData[headerOffset + 23];
|
|
3315
|
-
const ramSize = this.romData[headerOffset + 24];
|
|
3316
|
-
return {
|
|
3317
|
-
title,
|
|
3318
|
-
mapMode,
|
|
3319
|
-
cartridgeType,
|
|
3320
|
-
romSize,
|
|
3321
|
-
ramSize,
|
|
3322
|
-
isValid: title.length > 0 && mapMode !== 255
|
|
3323
|
-
};
|
|
2972
|
+
isStringMarker(obj) {
|
|
2973
|
+
return typeof obj === "object" && obj !== null && "offset" in obj;
|
|
3324
2974
|
}
|
|
3325
|
-
|
|
3326
|
-
return
|
|
2975
|
+
isTableEntry(obj) {
|
|
2976
|
+
return typeof obj === "object" && obj !== null && "location" in obj && "object" in obj;
|
|
3327
2977
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
2978
|
+
};
|
|
2979
|
+
var StringProcessor = class {
|
|
2980
|
+
constructor(context) {
|
|
2981
|
+
this.memBuffer = [];
|
|
2982
|
+
this.context = context;
|
|
2983
|
+
this.root = context.root;
|
|
2984
|
+
}
|
|
2985
|
+
dispose() {
|
|
2986
|
+
this.memBuffer.length = 0;
|
|
2987
|
+
}
|
|
2988
|
+
consumeString() {
|
|
2989
|
+
let str = null;
|
|
2990
|
+
const typeChar = this.context.lineBuffer[0];
|
|
2991
|
+
const endIx = this.context.lineBuffer.indexOf(typeChar, 1);
|
|
2992
|
+
if (endIx >= 0) {
|
|
2993
|
+
str = this.context.lineBuffer.substring(1, endIx);
|
|
2994
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
2995
|
+
} else {
|
|
2996
|
+
str = this.context.lineBuffer.substring(1);
|
|
2997
|
+
this.context.lineBuffer = "";
|
|
2998
|
+
}
|
|
2999
|
+
this.memBuffer.length = 0;
|
|
3000
|
+
const stringType = this.root.stringCharLookup[typeChar];
|
|
3001
|
+
this.processString(str, stringType);
|
|
3002
|
+
}
|
|
3003
|
+
flushBuffer(stringType, wrap = false) {
|
|
3004
|
+
const size = this.memBuffer.length;
|
|
3005
|
+
if (size > 0) {
|
|
3006
|
+
const buffer = new Uint8Array(this.memBuffer);
|
|
3007
|
+
this.context.currentBlock.objList.push(buffer);
|
|
3008
|
+
this.context.currentBlock.size += size;
|
|
3009
|
+
this.memBuffer.length = 0;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
processString(str, stringType) {
|
|
3013
|
+
const dict = stringType.commands;
|
|
3014
|
+
stringType.characterMap;
|
|
3015
|
+
this.getShiftUp(stringType.shiftType);
|
|
3016
|
+
let lastCmd = null;
|
|
3017
|
+
for (let x = 0; x < str.length; x++) {
|
|
3018
|
+
const c = str[x];
|
|
3019
|
+
if (c === "[") {
|
|
3020
|
+
const endIx = str.indexOf("]", x + 1);
|
|
3021
|
+
const splitChars = [":", ",", " "];
|
|
3022
|
+
const parts = str.substring(x + 1, endIx).split(new RegExp(`[${splitChars.join("")}]`)).filter((p) => p.length > 0);
|
|
3023
|
+
x = endIx;
|
|
3024
|
+
if (parts.length === 0) {
|
|
3025
|
+
this.flushBuffer(stringType, true);
|
|
3026
|
+
this.context.currentBlock.objList.push({
|
|
3027
|
+
offset: this.context.currentBlock.size
|
|
3028
|
+
});
|
|
3029
|
+
continue;
|
|
3030
|
+
}
|
|
3031
|
+
const cmd = Object.values(dict).find((x2) => x2.value === parts[0]);
|
|
3032
|
+
if (cmd) {
|
|
3033
|
+
lastCmd = cmd;
|
|
3034
|
+
this.memBuffer.push(cmd.key);
|
|
3035
|
+
this.processStringCommand(cmd, stringType, parts);
|
|
3036
|
+
continue;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
lastCmd = null;
|
|
3040
|
+
if (this.applyLayers(c, stringType)) {
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
if (lastCmd === null || !lastCmd.halt) {
|
|
3045
|
+
this.memBuffer.push(stringType.terminator);
|
|
3046
|
+
}
|
|
3047
|
+
this.flushBuffer(stringType, true);
|
|
3048
|
+
}
|
|
3049
|
+
applyLayers(c, stringType) {
|
|
3050
|
+
if (this.applyMap(c, stringType.characterMap, this.getShiftUp(stringType.shiftType))) {
|
|
3051
|
+
return true;
|
|
3052
|
+
}
|
|
3053
|
+
if (stringType.layers) {
|
|
3054
|
+
for (const layer of stringType.layers) {
|
|
3055
|
+
if (this.applyMap(c, layer.map, (x) => x + layer.base)) {
|
|
3056
|
+
return true;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
return false;
|
|
3061
|
+
}
|
|
3062
|
+
applyMap(c, map, shift) {
|
|
3063
|
+
for (let i = 0, len = map.length; i < len; i++) {
|
|
3064
|
+
const v = map[i];
|
|
3065
|
+
if (v != null && c === v[0]) {
|
|
3066
|
+
this.memBuffer.push(shift(i));
|
|
3067
|
+
return true;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
return false;
|
|
3071
|
+
}
|
|
3072
|
+
processStringCommand(cmd, stringType, parts) {
|
|
3073
|
+
const hasPointer = cmd.types.includes(shared.MemberType.Address) || cmd.types.includes(shared.MemberType.Offset);
|
|
3074
|
+
if (hasPointer) {
|
|
3075
|
+
this.flushBuffer(stringType, true);
|
|
3076
|
+
}
|
|
3077
|
+
for (let y = 0, pix = 1; y < cmd.types.length; y++, pix++) {
|
|
3078
|
+
switch (cmd.types[y]) {
|
|
3079
|
+
case shared.MemberType.Byte:
|
|
3080
|
+
this.memBuffer.push(parseInt(parts[pix], 16));
|
|
3081
|
+
break;
|
|
3082
|
+
case shared.MemberType.Word:
|
|
3083
|
+
const us = parseInt(parts[pix], 16);
|
|
3084
|
+
this.memBuffer.push(us & 255);
|
|
3085
|
+
this.memBuffer.push(us >> 8 & 255);
|
|
3086
|
+
break;
|
|
3087
|
+
case shared.MemberType.Binary:
|
|
3088
|
+
while (pix < parts.length) {
|
|
3089
|
+
const ch = parseInt(parts[pix], 16);
|
|
3090
|
+
this.memBuffer.push(ch);
|
|
3091
|
+
pix++;
|
|
3092
|
+
}
|
|
3093
|
+
if (cmd.delimiter !== void 0) {
|
|
3094
|
+
this.memBuffer.push(cmd.delimiter);
|
|
3095
|
+
}
|
|
3096
|
+
break;
|
|
3097
|
+
case shared.MemberType.Offset:
|
|
3098
|
+
case shared.MemberType.Address:
|
|
3099
|
+
this.flushBuffer(stringType, false);
|
|
3100
|
+
this.context.currentBlock.objList.push(parts[pix]);
|
|
3101
|
+
this.context.currentBlock.size += cmd.types[y] === shared.MemberType.Offset ? 2 : 3;
|
|
3102
|
+
break;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
if (hasPointer) {
|
|
3106
|
+
this.flushBuffer(stringType, false);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
getShiftUp(shiftType) {
|
|
3110
|
+
switch (shiftType) {
|
|
3111
|
+
case "h2":
|
|
3112
|
+
return (x) => (x & 112) << 1 | x & 15;
|
|
3113
|
+
case "wh2":
|
|
3114
|
+
return (x) => (x & 56) << 1 | x & 7;
|
|
3115
|
+
default:
|
|
3116
|
+
return (x) => x;
|
|
3331
3117
|
}
|
|
3332
|
-
const totalOpcodes = Object.keys(this.dbRoot.opCodes).length;
|
|
3333
|
-
const uniqueOpcodes = new Set(Object.values(this.dbRoot.opCodes)).size;
|
|
3334
|
-
const coverage = totalOpcodes > 0 ? uniqueOpcodes / OpCodeUtils.getAllOpcodes().length * 100 : 0;
|
|
3335
|
-
return {
|
|
3336
|
-
totalOpcodes,
|
|
3337
|
-
uniqueOpcodes,
|
|
3338
|
-
coverage
|
|
3339
|
-
};
|
|
3340
3118
|
}
|
|
3341
3119
|
};
|
|
3342
3120
|
|
|
3343
|
-
// src/
|
|
3344
|
-
var
|
|
3345
|
-
constructor(
|
|
3346
|
-
this.
|
|
3347
|
-
this.
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3121
|
+
// src/rom/rebuild/sorted-map.ts
|
|
3122
|
+
var SortedMap = class {
|
|
3123
|
+
constructor() {
|
|
3124
|
+
this.map = /* @__PURE__ */ new Map();
|
|
3125
|
+
this._keys = [];
|
|
3126
|
+
}
|
|
3127
|
+
get size() {
|
|
3128
|
+
return this.map.size;
|
|
3129
|
+
}
|
|
3130
|
+
set(key, value) {
|
|
3131
|
+
if (!this.map.has(key)) {
|
|
3132
|
+
this._keys.push(key);
|
|
3133
|
+
this.sortKeys();
|
|
3134
|
+
}
|
|
3135
|
+
this.map.set(key, value);
|
|
3136
|
+
return this;
|
|
3137
|
+
}
|
|
3138
|
+
get(key) {
|
|
3139
|
+
return this.map.get(key);
|
|
3140
|
+
}
|
|
3141
|
+
has(key) {
|
|
3142
|
+
return this.map.has(key);
|
|
3143
|
+
}
|
|
3144
|
+
delete(key) {
|
|
3145
|
+
const result = this.map.delete(key);
|
|
3146
|
+
if (result) {
|
|
3147
|
+
const index = this._keys.indexOf(key);
|
|
3148
|
+
if (index >= 0) {
|
|
3149
|
+
this._keys.splice(index, 1);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
return result;
|
|
3153
|
+
}
|
|
3154
|
+
clear() {
|
|
3155
|
+
this.map.clear();
|
|
3156
|
+
this._keys.length = 0;
|
|
3157
|
+
}
|
|
3158
|
+
keys() {
|
|
3159
|
+
return [...this._keys];
|
|
3160
|
+
}
|
|
3161
|
+
values() {
|
|
3162
|
+
return this._keys.map((key) => this.map.get(key));
|
|
3163
|
+
}
|
|
3164
|
+
entries() {
|
|
3165
|
+
return this._keys.map((key) => [key, this.map.get(key)]);
|
|
3166
|
+
}
|
|
3167
|
+
*[Symbol.iterator]() {
|
|
3168
|
+
for (const key of this._keys) {
|
|
3169
|
+
yield [key, this.map.get(key)];
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
sortKeys() {
|
|
3173
|
+
this._keys.sort((a, b) => {
|
|
3174
|
+
if (a.length !== b.length) {
|
|
3175
|
+
return b.length - a.length;
|
|
3176
|
+
}
|
|
3177
|
+
return a.localeCompare(b);
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
3181
|
+
var AssemblerState = class _AssemblerState {
|
|
3182
|
+
constructor(context, structType = null, saveDelimiter = false) {
|
|
3183
|
+
this.context = context;
|
|
3184
|
+
this.root = context.root;
|
|
3185
|
+
this.dbStruct = structType === null ? null : Object.values(this.root.structs).find(
|
|
3186
|
+
(x) => x.name.toLowerCase() === structType.toLowerCase()
|
|
3187
|
+
) || null;
|
|
3188
|
+
this.parentStruct = this.dbStruct?.parent == null ? null : Object.values(this.root.structs).find(
|
|
3189
|
+
(x) => x.name.toLowerCase() === this.dbStruct.parent.toLowerCase()
|
|
3190
|
+
) || null;
|
|
3191
|
+
this.discriminator = this.parentStruct?.discriminator ?? null;
|
|
3192
|
+
this.delimiter = this.dbStruct?.delimiter || null;
|
|
3193
|
+
this.memberOffset = 0;
|
|
3194
|
+
this.dataOffset = 0;
|
|
3195
|
+
this.memberTypes = this.dbStruct?.types || null;
|
|
3196
|
+
this.currentType = this.memberTypes?.[this.memberOffset] || null;
|
|
3197
|
+
if (saveDelimiter) {
|
|
3198
|
+
this.context.lastDelimiter = this.dbStruct?.delimiter ?? this.parentStruct?.delimiter ?? null;
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
checkDisc() {
|
|
3202
|
+
if (this.discriminator === this.dataOffset) {
|
|
3203
|
+
this.context.currentBlock.objList.push(this.dbStruct.discriminator);
|
|
3204
|
+
this.context.currentBlock.size += 1;
|
|
3205
|
+
this.dataOffset += 1;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
advancePart() {
|
|
3209
|
+
if (this.currentType != null && this.discriminator != null) {
|
|
3210
|
+
this.dataOffset += shared.RomProcessingConstants.getSize(this.currentType);
|
|
3211
|
+
}
|
|
3212
|
+
if (this.memberTypes != null && this.memberOffset + 1 < this.memberTypes.length) {
|
|
3213
|
+
this.currentType = this.memberTypes[++this.memberOffset];
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
processOrigin() {
|
|
3217
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(3).replace(/^[\s,\t]+/, "");
|
|
3218
|
+
if (this.context.lineBuffer.startsWith("$")) {
|
|
3219
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1);
|
|
3220
|
+
}
|
|
3221
|
+
let hex;
|
|
3222
|
+
const endIx = this.context.lineBuffer.search(/[\s,\t]/);
|
|
3223
|
+
if (endIx >= 0) {
|
|
3224
|
+
hex = this.context.lineBuffer.substring(0, endIx);
|
|
3225
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
3226
|
+
} else {
|
|
3227
|
+
hex = this.context.lineBuffer;
|
|
3228
|
+
this.context.lineBuffer = "";
|
|
3229
|
+
}
|
|
3230
|
+
const location = parseInt(hex, 16);
|
|
3231
|
+
this.context.blocks.push(this.context.currentBlock = new shared.AsmBlock(location));
|
|
3232
|
+
this.context.blockIndex++;
|
|
3233
|
+
}
|
|
3234
|
+
static doMath(operand) {
|
|
3235
|
+
const ix = operand.search(/[-+]/);
|
|
3236
|
+
if (ix >= 0) {
|
|
3237
|
+
const op = operand[ix];
|
|
3238
|
+
const vix = operand.lastIndexOf("$", ix) + 1;
|
|
3239
|
+
const valueStr = operand.substring(vix, ix);
|
|
3240
|
+
if (valueStr.match(/^[a-fA-F0-9]+$/)) {
|
|
3241
|
+
const value = parseInt(valueStr, 16);
|
|
3242
|
+
let endIx = operand.substring(ix + 1).search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3243
|
+
if (endIx < 0) {
|
|
3244
|
+
endIx = operand.length;
|
|
3245
|
+
}
|
|
3246
|
+
const number = parseInt(operand.substring(ix + 1, endIx), 16);
|
|
3247
|
+
let result;
|
|
3248
|
+
if (op === "-") {
|
|
3249
|
+
result = value - number;
|
|
3250
|
+
} else {
|
|
3251
|
+
result = value + number;
|
|
3252
|
+
}
|
|
3253
|
+
const len = ix - vix <= 2 ? 2 : ix - vix <= 4 ? 4 : 6;
|
|
3254
|
+
operand = operand.substring(0, vix) + result.toString(16).toUpperCase().padStart(len, "0") + operand.substring(endIx);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
return operand;
|
|
3258
|
+
}
|
|
3259
|
+
tryCreateLabel(mnemonic, operand) {
|
|
3260
|
+
if (!operand || operand.length === 0) {
|
|
3261
|
+
return false;
|
|
3262
|
+
}
|
|
3263
|
+
const labelChar = operand[0];
|
|
3264
|
+
if (!shared.RomProcessingConstants.LABEL_SPACE.includes(labelChar)) {
|
|
3265
|
+
return false;
|
|
3266
|
+
}
|
|
3267
|
+
const newBlock = new shared.AsmBlock(
|
|
3268
|
+
this.context.currentBlock.location + this.context.currentBlock.size,
|
|
3269
|
+
0,
|
|
3270
|
+
this.root.stringDelimiters.includes(operand[0]),
|
|
3271
|
+
mnemonic
|
|
3272
|
+
);
|
|
3273
|
+
this.context.blocks.push(newBlock);
|
|
3274
|
+
this.context.currentBlock = newBlock;
|
|
3275
|
+
this.context.blockIndex++;
|
|
3276
|
+
if (labelChar === ":") {
|
|
3277
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1);
|
|
3278
|
+
} else if (labelChar === "[" || labelChar === "{") {
|
|
3279
|
+
this.context.lineBuffer = operand.substring(1).replace(/^[\s,\t]+/, "");
|
|
3280
|
+
const state = new _AssemblerState(this.context, this.currentType);
|
|
3281
|
+
state.processText(labelChar);
|
|
3282
|
+
this.advancePart();
|
|
3283
|
+
}
|
|
3284
|
+
return true;
|
|
3285
|
+
}
|
|
3286
|
+
processText(openTag) {
|
|
3287
|
+
this.checkDisc();
|
|
3288
|
+
while (!this.context.eof) {
|
|
3289
|
+
if (!this.context.getLine()) {
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3292
|
+
let mnemonic = null;
|
|
3293
|
+
let operand = null;
|
|
3294
|
+
let operand2 = null;
|
|
3295
|
+
while (this.context.lineBuffer.length > 0) {
|
|
3296
|
+
const lineSymbol = this.context.lineBuffer[0];
|
|
3297
|
+
if (this.root.stringDelimiters.includes(lineSymbol)) {
|
|
3298
|
+
this.context.stringProcessor.consumeString();
|
|
3299
|
+
this.advancePart();
|
|
3300
|
+
continue;
|
|
3301
|
+
}
|
|
3302
|
+
if (shared.RomProcessingConstants.ADDRESS_SPACE.includes(lineSymbol)) {
|
|
3303
|
+
this.context.processRawData();
|
|
3304
|
+
if (openTag === "[") {
|
|
3305
|
+
this.context.lastDelimiter = null;
|
|
3306
|
+
}
|
|
3307
|
+
this.advancePart();
|
|
3308
|
+
continue;
|
|
3309
|
+
}
|
|
3310
|
+
if (lineSymbol === ">") {
|
|
3311
|
+
if (openTag === "<") {
|
|
3312
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3313
|
+
this.checkDisc();
|
|
3314
|
+
}
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
if (lineSymbol === "]") {
|
|
3318
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3319
|
+
this.delimiter = this.delimiter ?? this.context.lastDelimiter;
|
|
3320
|
+
if (this.delimiter != null) {
|
|
3321
|
+
if (this.delimiter >= 256) {
|
|
3322
|
+
this.context.currentBlock.objList.push(this.delimiter);
|
|
3323
|
+
this.context.currentBlock.size += 2;
|
|
3324
|
+
} else {
|
|
3325
|
+
this.context.currentBlock.objList.push(this.delimiter);
|
|
3326
|
+
this.context.currentBlock.size += 1;
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
return;
|
|
3330
|
+
}
|
|
3331
|
+
if (lineSymbol === "}") {
|
|
3332
|
+
if (openTag === "{") {
|
|
3333
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3334
|
+
}
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
if (lineSymbol === "[") {
|
|
3338
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3339
|
+
const state = new _AssemblerState(this.context, this.currentType);
|
|
3340
|
+
state.processText("[");
|
|
3341
|
+
this.advancePart();
|
|
3342
|
+
continue;
|
|
3343
|
+
}
|
|
3344
|
+
if (this.context.lineBuffer.startsWith("ORG")) {
|
|
3345
|
+
this.processOrigin();
|
|
3346
|
+
continue;
|
|
3347
|
+
}
|
|
3348
|
+
const symbolIndex = this.context.lineBuffer.search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3349
|
+
if (symbolIndex > 0) {
|
|
3350
|
+
mnemonic = this.context.lineBuffer.substring(0, symbolIndex);
|
|
3351
|
+
operand = this.context.lineBuffer.substring(symbolIndex).replace(/^[, \t]+/, "");
|
|
3352
|
+
if (operand && operand.startsWith("<")) {
|
|
3353
|
+
this.context.lineBuffer = operand.substring(1).replace(/^[, \t]+/, "");
|
|
3354
|
+
const state = new _AssemblerState(this.context, mnemonic, openTag === "[" && this.currentType == null);
|
|
3355
|
+
state.processText("<");
|
|
3356
|
+
mnemonic = null;
|
|
3357
|
+
continue;
|
|
3358
|
+
}
|
|
3359
|
+
this.context.lineBuffer = operand;
|
|
3360
|
+
} else {
|
|
3361
|
+
mnemonic = this.context.lineBuffer;
|
|
3362
|
+
this.context.lineBuffer = "";
|
|
3363
|
+
}
|
|
3364
|
+
break;
|
|
3365
|
+
}
|
|
3366
|
+
if (mnemonic && mnemonic.length > 0) {
|
|
3367
|
+
const codes = this.root.opLookup[mnemonic.toUpperCase()];
|
|
3368
|
+
if (!codes || codes.length === 0) {
|
|
3369
|
+
if (this.tryCreateLabel(mnemonic, operand || void 0)) {
|
|
3370
|
+
continue;
|
|
3371
|
+
}
|
|
3372
|
+
throw new Error(`Unknown instruction line ${this.context.lineCount}: '${mnemonic}'`);
|
|
3373
|
+
}
|
|
3374
|
+
this.context.lineBuffer = "";
|
|
3375
|
+
if (!operand) {
|
|
3376
|
+
const opCode2 = codes.find((x) => this.root.addrLookup[x.mode].size === 1);
|
|
3377
|
+
this.context.currentBlock.objList.push(new shared.Op(opCode2, 0, [], 1));
|
|
3378
|
+
this.context.currentBlock.size++;
|
|
3379
|
+
continue;
|
|
3380
|
+
}
|
|
3381
|
+
operand = _AssemblerState.doMath(operand);
|
|
3382
|
+
let opCode = codes[0];
|
|
3383
|
+
if (opCode?.mnem === "COP") {
|
|
3384
|
+
const parts = operand.split(/[\s\t,()[\]$#]/).filter((p) => p.length > 0);
|
|
3385
|
+
const cmd = parts[0];
|
|
3386
|
+
const cop = this.root.copLookup[cmd];
|
|
3387
|
+
if (!cop) {
|
|
3388
|
+
throw new Error(`Unknown COP command ${cmd}`);
|
|
3389
|
+
}
|
|
3390
|
+
this.context.currentBlock.objList.push(new shared.Op(opCode, 0, [new shared.Byte(cop.code), ...parts.slice(1)], cop.size + 2));
|
|
3391
|
+
this.context.currentBlock.size += cop.size + 2;
|
|
3392
|
+
continue;
|
|
3393
|
+
}
|
|
3394
|
+
opCode = null;
|
|
3395
|
+
for (const code of codes) {
|
|
3396
|
+
if (code.mode === "PCRelative" || code.mode === "PCRelativeLong") {
|
|
3397
|
+
opCode = code;
|
|
3398
|
+
break;
|
|
3399
|
+
}
|
|
3400
|
+
const addrMode2 = this.root.addrLookup[code.mode];
|
|
3401
|
+
const regex = addrMode2.parseRegex;
|
|
3402
|
+
if (regex) {
|
|
3403
|
+
const match = new RegExp(regex).exec(operand);
|
|
3404
|
+
if (match) {
|
|
3405
|
+
opCode = code;
|
|
3406
|
+
operand = match[1];
|
|
3407
|
+
if (match.length > 2) {
|
|
3408
|
+
operand2 = match[2];
|
|
3409
|
+
}
|
|
3410
|
+
break;
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
if (operand.startsWith("#")) {
|
|
3415
|
+
operand = operand.substring(1);
|
|
3416
|
+
}
|
|
3417
|
+
if (operand.startsWith("$")) {
|
|
3418
|
+
operand = operand.substring(1);
|
|
3419
|
+
}
|
|
3420
|
+
if (opCode == null) {
|
|
3421
|
+
const addrIx = operand.search(shared.RomProcessingConstants.ADDRESS_SPACE_REGEX);
|
|
3422
|
+
if (addrIx && addrIx >= 0) {
|
|
3423
|
+
const eix = operand.search(/[\s\t,\])]/);
|
|
3424
|
+
if (eix && eix >= 0) {
|
|
3425
|
+
operand = operand.substring(addrIx, eix);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
opCode = codes.find((x) => x.mode === "Immediate") ?? codes.find((x) => x.mode === "AbsoluteLong") ?? codes.find((x) => x.mode === "Absolute") ?? null;
|
|
3429
|
+
if (opCode == null) {
|
|
3430
|
+
throw new Error(`Unable to determine mode/code line ${this.context.lineCount}: '${this.context.lineBuffer}'`);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
const opnd1 = this.context.parseOperand(operand);
|
|
3434
|
+
const addrMode = this.root.addrLookup[opCode.mode];
|
|
3435
|
+
let size = addrMode.size;
|
|
3436
|
+
if (size === AsmReader.VARIABLE_SIZE_INDICATOR || opCode.mode === "Immediate") {
|
|
3437
|
+
size = operand?.startsWith("^") || operand?.length === 2 ? 2 : 3;
|
|
3438
|
+
}
|
|
3439
|
+
const operands = operand2 != null ? [opnd1, this.context.parseOperand(operand2)] : [opnd1];
|
|
3440
|
+
this.context.currentBlock.objList.push(new shared.Op(opCode, 0, operands, size));
|
|
3441
|
+
this.context.currentBlock.size += size;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
hexStringToBytes(hex) {
|
|
3446
|
+
const result = new Uint8Array(hex.length / 2);
|
|
3447
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
3448
|
+
result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
3449
|
+
}
|
|
3450
|
+
return result;
|
|
3451
|
+
}
|
|
3452
|
+
};
|
|
3453
|
+
|
|
3454
|
+
// src/rom/rebuild/assembler.ts
|
|
3455
|
+
var Assembler = class {
|
|
3456
|
+
constructor(dbRoot, textData) {
|
|
3457
|
+
this.currentLineIndex = 0;
|
|
3458
|
+
this.lineBuffer = "";
|
|
3459
|
+
this.includes = /* @__PURE__ */ new Set();
|
|
3460
|
+
this.blocks = [];
|
|
3461
|
+
this.tags = new SortedMap();
|
|
3462
|
+
this.currentBlock = null;
|
|
3463
|
+
this.lineCount = 0;
|
|
3464
|
+
this.blockIndex = 0;
|
|
3465
|
+
this.lastDelimiter = null;
|
|
3466
|
+
this.reqBank = null;
|
|
3467
|
+
this.eof = false;
|
|
3468
|
+
this.root = dbRoot;
|
|
3469
|
+
this.lines = textData.split(/\r?\n/);
|
|
3470
|
+
this.stringProcessor = new StringProcessor(this);
|
|
3471
|
+
}
|
|
3472
|
+
dispose() {
|
|
3473
|
+
this.stringProcessor.dispose();
|
|
3474
|
+
}
|
|
3475
|
+
parseAssembly() {
|
|
3476
|
+
this.blocks.push(this.currentBlock = new shared.AsmBlock());
|
|
3477
|
+
const state = new AssemblerState(this);
|
|
3478
|
+
state.processText();
|
|
3479
|
+
return {
|
|
3480
|
+
blocks: this.blocks,
|
|
3481
|
+
includes: this.includes,
|
|
3482
|
+
reqBank: this.reqBank
|
|
3353
3483
|
};
|
|
3354
|
-
this.events = {};
|
|
3355
|
-
this.events = events || {};
|
|
3356
3484
|
}
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3485
|
+
getLine() {
|
|
3486
|
+
if (this.eof) {
|
|
3487
|
+
return false;
|
|
3488
|
+
}
|
|
3489
|
+
if (this.lineBuffer.length > 0) {
|
|
3490
|
+
return true;
|
|
3491
|
+
}
|
|
3492
|
+
let rawLine = null;
|
|
3493
|
+
while (true) {
|
|
3494
|
+
if (this.currentLineIndex >= this.lines.length) {
|
|
3495
|
+
if (this.lineBuffer.length === 0) {
|
|
3496
|
+
this.eof = true;
|
|
3497
|
+
return false;
|
|
3498
|
+
} else {
|
|
3499
|
+
rawLine = "";
|
|
3500
|
+
}
|
|
3501
|
+
} else {
|
|
3502
|
+
rawLine = this.lines[this.currentLineIndex++];
|
|
3503
|
+
this.lineBuffer += rawLine;
|
|
3504
|
+
}
|
|
3505
|
+
this.lineCount++;
|
|
3506
|
+
this.trimComments("--");
|
|
3507
|
+
this.trimComments(";");
|
|
3508
|
+
this.trimComments("//");
|
|
3509
|
+
this.lineBuffer = this.lineBuffer.replace(shared.RomProcessingConstants.COMMA_SPACE_TRIM_REGEX, "");
|
|
3510
|
+
if (this.lineBuffer.length === 0) {
|
|
3511
|
+
continue;
|
|
3512
|
+
}
|
|
3513
|
+
if (this.lineBuffer.endsWith("\\")) {
|
|
3514
|
+
this.lineBuffer = this.lineBuffer.slice(0, -1);
|
|
3515
|
+
continue;
|
|
3516
|
+
}
|
|
3517
|
+
if (this.lineBuffer[0] === "?") {
|
|
3518
|
+
this.processDirectives();
|
|
3519
|
+
this.lineBuffer = "";
|
|
3520
|
+
continue;
|
|
3521
|
+
}
|
|
3522
|
+
if (this.lineBuffer[0] === "!") {
|
|
3523
|
+
this.processTags();
|
|
3524
|
+
this.lineBuffer = "";
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3527
|
+
this.resolveTags();
|
|
3528
|
+
return true;
|
|
3529
|
+
}
|
|
3364
3530
|
}
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3531
|
+
trimComments(sequence) {
|
|
3532
|
+
const index = this.lineBuffer.indexOf(sequence);
|
|
3533
|
+
if (index >= 0) {
|
|
3534
|
+
const strDelimRegex = new RegExp(`[${this.root.stringDelimiters.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("")}]`);
|
|
3535
|
+
const strIndex = this.lineBuffer.search(strDelimRegex);
|
|
3536
|
+
if (strIndex < 0 || strIndex > index || this.lineBuffer.lastIndexOf(this.lineBuffer[strIndex] || "") < index) {
|
|
3537
|
+
this.lineBuffer = this.lineBuffer.substring(0, index);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3372
3540
|
}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
});
|
|
3390
|
-
this.events.onLoadComplete?.(this.processor);
|
|
3391
|
-
} catch (error) {
|
|
3392
|
-
const err = error;
|
|
3393
|
-
this.updateStatus({ lastError: err, currentTask: "Load failed", progress: 0 });
|
|
3394
|
-
this.events.onLoadError?.(err);
|
|
3395
|
-
throw err;
|
|
3541
|
+
processDirectives() {
|
|
3542
|
+
let endIx = this.lineBuffer.search(shared.RomProcessingConstants.COMMA_SPACE_REGEX);
|
|
3543
|
+
if (endIx < 0) {
|
|
3544
|
+
endIx = this.lineBuffer.length;
|
|
3545
|
+
}
|
|
3546
|
+
const value = this.lineBuffer.substring(endIx).replace(/^[\s,\t]+/, "");
|
|
3547
|
+
switch (this.lineBuffer.substring(1, endIx).toUpperCase()) {
|
|
3548
|
+
// This is taken care of by the loader
|
|
3549
|
+
case "BANK":
|
|
3550
|
+
this.reqBank = parseInt(value, 16);
|
|
3551
|
+
break;
|
|
3552
|
+
case "INCLUDE":
|
|
3553
|
+
if (value.length > 0) {
|
|
3554
|
+
this.includes.add(value.toUpperCase().replace(/'/g, ""));
|
|
3555
|
+
}
|
|
3556
|
+
break;
|
|
3396
3557
|
}
|
|
3397
3558
|
}
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3559
|
+
processTags() {
|
|
3560
|
+
this.lineBuffer = this.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3561
|
+
while (this.lineBuffer.length > 0) {
|
|
3562
|
+
let name = this.lineBuffer;
|
|
3563
|
+
let value = null;
|
|
3564
|
+
let endIx = this.lineBuffer.search(/[\s,\t]/);
|
|
3565
|
+
if (endIx >= 0) {
|
|
3566
|
+
name = this.lineBuffer.substring(0, endIx);
|
|
3567
|
+
value = this.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
3568
|
+
const nextIx = value.search(/[\s,\t]/);
|
|
3569
|
+
if (nextIx >= 0) {
|
|
3570
|
+
this.lineBuffer = value.substring(nextIx + 1).replace(/^[\s,\t]+/, "");
|
|
3571
|
+
value = value.substring(0, nextIx);
|
|
3572
|
+
} else {
|
|
3573
|
+
this.lineBuffer = "";
|
|
3574
|
+
}
|
|
3575
|
+
} else {
|
|
3576
|
+
this.lineBuffer = "";
|
|
3577
|
+
}
|
|
3578
|
+
this.tags.set(name, value);
|
|
3579
|
+
}
|
|
3404
3580
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3581
|
+
resolveTags() {
|
|
3582
|
+
for (const [key, value] of this.tags) {
|
|
3583
|
+
let ix;
|
|
3584
|
+
while ((ix = this.lineBuffer.toLowerCase().indexOf(key.toLowerCase())) >= 0) {
|
|
3585
|
+
this.lineBuffer = this.lineBuffer.substring(0, ix) + (value || "") + this.lineBuffer.substring(ix + key.length);
|
|
3586
|
+
}
|
|
3411
3587
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
const err = error;
|
|
3425
|
-
this.updateStatus({ lastError: err, currentTask: "Analysis failed" });
|
|
3426
|
-
throw err;
|
|
3588
|
+
}
|
|
3589
|
+
parseOperand(opnd) {
|
|
3590
|
+
if (!opnd) return null;
|
|
3591
|
+
if (opnd.match(/^[a-fA-F0-9]{2,6}$/)) {
|
|
3592
|
+
switch (opnd.length) {
|
|
3593
|
+
case 2:
|
|
3594
|
+
return new shared.Byte(parseInt(opnd, 16));
|
|
3595
|
+
case 4:
|
|
3596
|
+
return new shared.Word(parseInt(opnd, 16));
|
|
3597
|
+
case 6:
|
|
3598
|
+
return new shared.Long(parseInt(opnd, 16));
|
|
3599
|
+
}
|
|
3427
3600
|
}
|
|
3601
|
+
return opnd;
|
|
3428
3602
|
}
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
if (!this.processor) {
|
|
3434
|
-
throw new Error("Project not loaded. Call loadProject() first.");
|
|
3603
|
+
processRawData() {
|
|
3604
|
+
let reverse = false;
|
|
3605
|
+
if (this.lineBuffer[0] === "#") {
|
|
3606
|
+
this.lineBuffer = this.lineBuffer.substring(1);
|
|
3435
3607
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
this.
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
this.
|
|
3447
|
-
|
|
3448
|
-
}
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3608
|
+
if (this.lineBuffer[0] === "$") {
|
|
3609
|
+
reverse = true;
|
|
3610
|
+
this.lineBuffer = this.lineBuffer.substring(1);
|
|
3611
|
+
}
|
|
3612
|
+
let hex;
|
|
3613
|
+
const symbolIx = this.lineBuffer.search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3614
|
+
if (symbolIx >= 0) {
|
|
3615
|
+
hex = this.lineBuffer.substring(0, symbolIx);
|
|
3616
|
+
this.lineBuffer = this.lineBuffer.substring(symbolIx).replace(/^[, \t]+/, "");
|
|
3617
|
+
} else {
|
|
3618
|
+
hex = this.lineBuffer;
|
|
3619
|
+
this.lineBuffer = "";
|
|
3620
|
+
}
|
|
3621
|
+
if (hex.length > 0) {
|
|
3622
|
+
if (shared.RomProcessingConstants.ADDRESS_SPACE.includes(hex[0])) {
|
|
3623
|
+
this.currentBlock.objList.push(hex);
|
|
3624
|
+
this.currentBlock.size += shared.RomProcessingConstants.getSize(hex);
|
|
3625
|
+
} else {
|
|
3626
|
+
const data = this.hexStringToBytes(hex);
|
|
3627
|
+
if (data.length === 1) {
|
|
3628
|
+
this.currentBlock.objList.push(new shared.Byte(data[0]));
|
|
3629
|
+
this.currentBlock.size++;
|
|
3630
|
+
} else {
|
|
3631
|
+
if (reverse) {
|
|
3632
|
+
data.reverse();
|
|
3633
|
+
}
|
|
3634
|
+
if (data.length === 2) {
|
|
3635
|
+
this.currentBlock.objList.push(new shared.Word(data[0] | data[1] << 8));
|
|
3636
|
+
this.currentBlock.size += 2;
|
|
3637
|
+
} else {
|
|
3638
|
+
this.currentBlock.objList.push(data);
|
|
3639
|
+
this.currentBlock.size += data.length;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3452
3643
|
}
|
|
3453
3644
|
}
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
if (!this.processor) {
|
|
3459
|
-
throw new Error("Project not loaded. Call loadProject() first.");
|
|
3645
|
+
hexStringToBytes(hex) {
|
|
3646
|
+
const result = new Uint8Array(hex.length / 2);
|
|
3647
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
3648
|
+
result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
3460
3649
|
}
|
|
3461
|
-
|
|
3462
|
-
|
|
3650
|
+
return result;
|
|
3651
|
+
}
|
|
3652
|
+
};
|
|
3653
|
+
var RomGenerator = class {
|
|
3654
|
+
constructor(modules, sourceData) {
|
|
3655
|
+
this.moduleLookup = /* @__PURE__ */ new Map();
|
|
3656
|
+
this.moduleList = ["jp-viper", "title-enhanced"];
|
|
3657
|
+
this.patchFiles = [];
|
|
3658
|
+
this.chunkFiles = [];
|
|
3659
|
+
this.asmFiles = [];
|
|
3660
|
+
this.dbRoot = {};
|
|
3661
|
+
this.sourceData = sourceData;
|
|
3662
|
+
this.moduleList = modules;
|
|
3663
|
+
const crc = shared.crc32_buffer(this.sourceData);
|
|
3664
|
+
if (crc !== 473450688) {
|
|
3665
|
+
throw new Error("CRC mismatch, please provide the correct ROM");
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
async generateProject(projectName) {
|
|
3669
|
+
await this.initializeDatabase(projectName);
|
|
3670
|
+
const reader = this.initializeChunks();
|
|
3671
|
+
this.writeScriptTexts(reader);
|
|
3672
|
+
this.applyBaseRom();
|
|
3673
|
+
this.applyProjectInit();
|
|
3674
|
+
this.applySelectedModules();
|
|
3675
|
+
this.assembleCodeFromText();
|
|
3676
|
+
this.applyCodePatches();
|
|
3677
|
+
this.calculateSizes();
|
|
3678
|
+
this.performLayout();
|
|
3679
|
+
this.rebaseAssemblies();
|
|
3680
|
+
this.generateAsmIncludeLookups();
|
|
3681
|
+
const blockLookup = this.generateBlockLookup();
|
|
3682
|
+
const outRom = await this.writeRom(blockLookup);
|
|
3683
|
+
return outRom;
|
|
3684
|
+
}
|
|
3685
|
+
//Grab structures from supabase
|
|
3686
|
+
async initializeDatabase(projectName) {
|
|
3687
|
+
this.dbRoot = await shared.DbRootUtils.fromSupabaseProject({ projectName });
|
|
3688
|
+
}
|
|
3689
|
+
//Read graph from rom
|
|
3690
|
+
initializeChunks() {
|
|
3691
|
+
const reader = new BlockReader(this.sourceData, this.dbRoot);
|
|
3692
|
+
this.chunkFiles = reader.analyzeAndResolve();
|
|
3693
|
+
this.asmFiles = this.chunkFiles.filter((b) => b.type === shared.BinType.Assembly);
|
|
3694
|
+
return reader;
|
|
3695
|
+
}
|
|
3696
|
+
//Convert blocks to text
|
|
3697
|
+
writeScriptTexts(reader) {
|
|
3698
|
+
const writer = new BlockWriter(reader);
|
|
3699
|
+
for (const block of this.asmFiles) {
|
|
3700
|
+
block.textData = writer.generateAsm(block);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
//Apply base rom files
|
|
3704
|
+
applyBaseRom() {
|
|
3705
|
+
for (const chunkFile of this.dbRoot.baseRomFiles) {
|
|
3706
|
+
this.applyPatchFile(chunkFile);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
applyProjectInit() {
|
|
3710
|
+
for (const chunkFile of this.dbRoot.projectFiles) {
|
|
3711
|
+
console.log(`Processing patch: ${chunkFile.name}`);
|
|
3712
|
+
if (chunkFile.group) {
|
|
3713
|
+
let modArray;
|
|
3714
|
+
if (!this.moduleLookup.has(chunkFile.group)) {
|
|
3715
|
+
this.moduleLookup.set(chunkFile.group, modArray = []);
|
|
3716
|
+
} else {
|
|
3717
|
+
modArray = this.moduleLookup.get(chunkFile.group);
|
|
3718
|
+
}
|
|
3719
|
+
modArray.push(chunkFile);
|
|
3720
|
+
} else {
|
|
3721
|
+
this.applyPatchFile(chunkFile);
|
|
3722
|
+
}
|
|
3463
3723
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
this.
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
});
|
|
3472
|
-
this.events.onBuildComplete?.(results);
|
|
3473
|
-
return results;
|
|
3474
|
-
} catch (error) {
|
|
3475
|
-
const err = error;
|
|
3476
|
-
this.updateStatus({ lastError: err, currentTask: "Build failed" });
|
|
3477
|
-
throw err;
|
|
3724
|
+
}
|
|
3725
|
+
//Apply selected project modules
|
|
3726
|
+
applySelectedModules() {
|
|
3727
|
+
for (const module of this.moduleList) {
|
|
3728
|
+
for (const file of this.moduleLookup.get(module)) {
|
|
3729
|
+
this.applyPatchFile(file);
|
|
3730
|
+
}
|
|
3478
3731
|
}
|
|
3479
3732
|
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3733
|
+
//Assemble code from script text
|
|
3734
|
+
assembleCodeFromText() {
|
|
3735
|
+
for (const block of this.asmFiles) {
|
|
3736
|
+
const assembler = new Assembler(this.dbRoot, block.textData);
|
|
3737
|
+
const { blocks, includes, reqBank } = assembler.parseAssembly();
|
|
3738
|
+
block.parts = blocks;
|
|
3739
|
+
block.includes = includes;
|
|
3740
|
+
block.bank = reqBank ?? void 0;
|
|
3486
3741
|
}
|
|
3487
|
-
return await this.processor.processScene(sceneId, metaFile);
|
|
3488
3742
|
}
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
getStatus() {
|
|
3493
|
-
return { ...this.status };
|
|
3743
|
+
//Apply patches
|
|
3744
|
+
applyCodePatches() {
|
|
3745
|
+
RomProcessor.applyPatches(this.asmFiles, this.patchFiles);
|
|
3494
3746
|
}
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3747
|
+
//Calculate sizes
|
|
3748
|
+
calculateSizes() {
|
|
3749
|
+
for (const asm of this.chunkFiles) {
|
|
3750
|
+
shared.ChunkFileUtils.calculateSize(asm);
|
|
3751
|
+
}
|
|
3500
3752
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
return this.processor?.getProject().config || null;
|
|
3753
|
+
// Assign locations
|
|
3754
|
+
performLayout() {
|
|
3755
|
+
const layout = new RomLayout(this.chunkFiles);
|
|
3756
|
+
layout.organize();
|
|
3506
3757
|
}
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3758
|
+
// Rebase assemblies
|
|
3759
|
+
rebaseAssemblies() {
|
|
3760
|
+
for (const file of this.asmFiles) {
|
|
3761
|
+
shared.ChunkFileUtils.rebase(file);
|
|
3762
|
+
}
|
|
3512
3763
|
}
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3764
|
+
// Build include lookup map per asm file
|
|
3765
|
+
generateAsmIncludeLookups() {
|
|
3766
|
+
for (const f of this.asmFiles) {
|
|
3767
|
+
const includeBlocks = this.asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
|
|
3768
|
+
f.includeLookup = /* @__PURE__ */ new Map();
|
|
3769
|
+
for (const b of includeBlocks) {
|
|
3770
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
3771
|
+
}
|
|
3772
|
+
for (const b of (f.parts || []).filter((x) => !!x.label)) {
|
|
3773
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
applyPatchFile(chunkFile) {
|
|
3778
|
+
console.log(`Processing patch: ${chunkFile.name}`);
|
|
3779
|
+
const existing = this.chunkFiles.find((x) => x.name === chunkFile.name);
|
|
3780
|
+
if (chunkFile.type === shared.BinType.Patch || chunkFile.type === shared.BinType.Assembly) {
|
|
3781
|
+
if (existing) {
|
|
3782
|
+
existing.textData = chunkFile.textData;
|
|
3783
|
+
} else {
|
|
3784
|
+
if (chunkFile.type === shared.BinType.Patch) {
|
|
3785
|
+
this.patchFiles.push(chunkFile);
|
|
3786
|
+
}
|
|
3787
|
+
this.asmFiles.push(chunkFile);
|
|
3788
|
+
this.chunkFiles.push(chunkFile);
|
|
3789
|
+
}
|
|
3790
|
+
} else {
|
|
3791
|
+
if (existing) {
|
|
3792
|
+
existing.rawData = chunkFile.rawData;
|
|
3793
|
+
existing.size = chunkFile.size;
|
|
3794
|
+
} else {
|
|
3795
|
+
this.chunkFiles.push(chunkFile);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
// Create block lookup for resolving labels to locations
|
|
3800
|
+
generateBlockLookup() {
|
|
3801
|
+
const blockLookup = /* @__PURE__ */ new Map();
|
|
3802
|
+
for (const f of this.chunkFiles) {
|
|
3803
|
+
blockLookup.set(f.name.toUpperCase(), f.location);
|
|
3804
|
+
}
|
|
3805
|
+
return blockLookup;
|
|
3806
|
+
}
|
|
3807
|
+
async writeRom(blockLookup) {
|
|
3808
|
+
const romWriter = new RomWriter(this.dbRoot.entryPoints, "GAIALABS", "01JG ");
|
|
3809
|
+
for (const file of this.chunkFiles) {
|
|
3810
|
+
await romWriter.writeFile(file, blockLookup);
|
|
3811
|
+
}
|
|
3812
|
+
romWriter.writeHeader();
|
|
3813
|
+
romWriter.writeEntryPoints(this.asmFiles);
|
|
3814
|
+
romWriter.writeChecksum();
|
|
3815
|
+
return romWriter.outBuffer;
|
|
3816
|
+
}
|
|
3817
|
+
};
|
|
3818
|
+
|
|
3819
|
+
// src/sprites/SpriteFrame.ts
|
|
3820
|
+
var SpriteFrame = class {
|
|
3821
|
+
constructor(duration = 0, groupIndex = 0, groupOffset = 0) {
|
|
3822
|
+
this.duration = duration;
|
|
3823
|
+
this.groupIndex = groupIndex;
|
|
3824
|
+
this.groupOffset = groupOffset;
|
|
3825
|
+
}
|
|
3826
|
+
};
|
|
3827
|
+
|
|
3828
|
+
// src/sprites/SpritePart.ts
|
|
3829
|
+
var SpritePart = class {
|
|
3830
|
+
constructor() {
|
|
3831
|
+
this.isLarge = false;
|
|
3832
|
+
this.xOffset = 0;
|
|
3833
|
+
this.xOffsetMirror = 0;
|
|
3834
|
+
this.yOffset = 0;
|
|
3835
|
+
this.yOffsetMirror = 0;
|
|
3836
|
+
this.vMirror = false;
|
|
3837
|
+
this.hMirror = false;
|
|
3838
|
+
this.someOffset = 0;
|
|
3839
|
+
this.paletteIndex = 0;
|
|
3840
|
+
this.tileIndex = 0;
|
|
3841
|
+
}
|
|
3842
|
+
};
|
|
3843
|
+
|
|
3844
|
+
// src/sprites/SpriteGroup.ts
|
|
3845
|
+
var SpriteGroup = class {
|
|
3846
|
+
constructor() {
|
|
3847
|
+
this.xOffset = 0;
|
|
3848
|
+
this.xOffsetMirror = 0;
|
|
3849
|
+
this.yOffset = 0;
|
|
3850
|
+
this.yOffsetMirror = 0;
|
|
3851
|
+
this.xRecoilHitboxOffset = 0;
|
|
3852
|
+
this.yRecoilHitboxOffset = 0;
|
|
3853
|
+
this.xRecoilHitboxTilesize = 0;
|
|
3854
|
+
this.yRecoilHitboxTilesize = 0;
|
|
3855
|
+
this.xHostileHitboxOffset = 0;
|
|
3856
|
+
this.xHostileHitboxSize = 0;
|
|
3857
|
+
this.yHostileHitboxOffset = 0;
|
|
3858
|
+
this.yHostileHitboxSize = 0;
|
|
3859
|
+
this.parts = [];
|
|
3860
|
+
}
|
|
3861
|
+
};
|
|
3862
|
+
|
|
3863
|
+
// src/sprites/SpriteMap.ts
|
|
3864
|
+
var SpriteMap = class _SpriteMap {
|
|
3865
|
+
constructor() {
|
|
3866
|
+
this.frameSets = [];
|
|
3867
|
+
this.groups = [];
|
|
3518
3868
|
}
|
|
3519
3869
|
/**
|
|
3520
|
-
*
|
|
3870
|
+
* Create a SpriteMap from binary data
|
|
3871
|
+
* @param data Binary data buffer
|
|
3872
|
+
* @returns SpriteMap instance
|
|
3521
3873
|
*/
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
isExtracted: false,
|
|
3528
|
-
canBuild: false,
|
|
3529
|
-
progress: 0
|
|
3874
|
+
static fromBytes(data) {
|
|
3875
|
+
let position = 0;
|
|
3876
|
+
const getByte = () => {
|
|
3877
|
+
if (position >= data.length) return 0;
|
|
3878
|
+
return data[position++];
|
|
3530
3879
|
};
|
|
3880
|
+
const getUshort = () => {
|
|
3881
|
+
if (position + 1 >= data.length) return 0;
|
|
3882
|
+
const low = data[position++];
|
|
3883
|
+
const high = data[position++];
|
|
3884
|
+
return low | high << 8;
|
|
3885
|
+
};
|
|
3886
|
+
const spriteMap = new _SpriteMap();
|
|
3887
|
+
const setOffsets = /* @__PURE__ */ new Set();
|
|
3888
|
+
const groupOffsets = /* @__PURE__ */ new Set();
|
|
3889
|
+
position = 0;
|
|
3890
|
+
while (!setOffsets.has(position)) {
|
|
3891
|
+
const offset = getUshort() - 16384;
|
|
3892
|
+
setOffsets.add(offset);
|
|
3893
|
+
}
|
|
3894
|
+
for (const offStart of setOffsets) {
|
|
3895
|
+
position = offStart;
|
|
3896
|
+
const frameList = [];
|
|
3897
|
+
while (true) {
|
|
3898
|
+
const duration = getUshort();
|
|
3899
|
+
if (duration === 65535) {
|
|
3900
|
+
break;
|
|
3901
|
+
}
|
|
3902
|
+
const groupOffset = getUshort() - 16384;
|
|
3903
|
+
frameList.push(new SpriteFrame(duration, 0, groupOffset));
|
|
3904
|
+
groupOffsets.add(groupOffset);
|
|
3905
|
+
}
|
|
3906
|
+
spriteMap.frameSets.push(frameList);
|
|
3907
|
+
}
|
|
3908
|
+
let grpIx = 0;
|
|
3909
|
+
const sortedGroupOffsets = Array.from(groupOffsets).sort((a, b) => a - b);
|
|
3910
|
+
for (const offStart of sortedGroupOffsets) {
|
|
3911
|
+
position = offStart;
|
|
3912
|
+
for (const set of spriteMap.frameSets) {
|
|
3913
|
+
for (const frame of set) {
|
|
3914
|
+
if (frame.groupOffset === offStart) {
|
|
3915
|
+
frame.groupIndex = grpIx;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
const grp = new SpriteGroup();
|
|
3920
|
+
grp.xOffset = getByte();
|
|
3921
|
+
grp.xOffsetMirror = getByte();
|
|
3922
|
+
grp.yOffset = getByte();
|
|
3923
|
+
grp.yOffsetMirror = getByte();
|
|
3924
|
+
grp.xRecoilHitboxOffset = getByte();
|
|
3925
|
+
grp.yRecoilHitboxOffset = getByte();
|
|
3926
|
+
grp.xRecoilHitboxTilesize = getByte();
|
|
3927
|
+
grp.yRecoilHitboxTilesize = getByte();
|
|
3928
|
+
grp.xHostileHitboxOffset = getByte();
|
|
3929
|
+
grp.xHostileHitboxSize = getByte();
|
|
3930
|
+
grp.yHostileHitboxOffset = getByte();
|
|
3931
|
+
grp.yHostileHitboxSize = getByte();
|
|
3932
|
+
let numParts = getByte();
|
|
3933
|
+
while (numParts-- > 0) {
|
|
3934
|
+
const part = new SpritePart();
|
|
3935
|
+
part.isLarge = getByte() !== 0;
|
|
3936
|
+
part.xOffset = getByte();
|
|
3937
|
+
part.xOffsetMirror = getByte();
|
|
3938
|
+
part.yOffset = getByte();
|
|
3939
|
+
part.yOffsetMirror = getByte();
|
|
3940
|
+
const props = getUshort();
|
|
3941
|
+
part.vMirror = (props & 32768) !== 0;
|
|
3942
|
+
part.hMirror = (props & 16384) !== 0;
|
|
3943
|
+
part.someOffset = props >> 12 & 3;
|
|
3944
|
+
part.paletteIndex = props >> 9 & 7;
|
|
3945
|
+
part.tileIndex = props & 511;
|
|
3946
|
+
grp.parts.push(part);
|
|
3947
|
+
}
|
|
3948
|
+
spriteMap.groups.push(grp);
|
|
3949
|
+
grpIx++;
|
|
3950
|
+
}
|
|
3951
|
+
return spriteMap;
|
|
3531
3952
|
}
|
|
3532
3953
|
/**
|
|
3533
|
-
*
|
|
3954
|
+
* Convert this SpriteMap to binary data
|
|
3955
|
+
* @returns Binary data buffer
|
|
3534
3956
|
*/
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
const
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3957
|
+
toBytes() {
|
|
3958
|
+
let size = this.frameSets.length * 2;
|
|
3959
|
+
for (const set of this.frameSets) {
|
|
3960
|
+
size += set.length * 4 + 2;
|
|
3961
|
+
}
|
|
3962
|
+
for (const grp of this.groups) {
|
|
3963
|
+
size += grp.parts.length * 7 + 13;
|
|
3964
|
+
}
|
|
3965
|
+
const buffer = new Uint8Array(size);
|
|
3966
|
+
let position = 0;
|
|
3967
|
+
const writeShort = (val) => {
|
|
3968
|
+
buffer[position++] = val & 255;
|
|
3969
|
+
buffer[position++] = val >> 8 & 255;
|
|
3544
3970
|
};
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3971
|
+
const writeLoc = (val) => writeShort(val + 16384);
|
|
3972
|
+
let pos = this.frameSets.length << 1;
|
|
3973
|
+
const groupPos = new Array(this.groups.length);
|
|
3974
|
+
for (const set of this.frameSets) {
|
|
3975
|
+
writeLoc(pos);
|
|
3976
|
+
pos += (set.length << 2) + 2;
|
|
3977
|
+
}
|
|
3978
|
+
for (let i = 0; i < this.groups.length; i++) {
|
|
3979
|
+
groupPos[i] = pos;
|
|
3980
|
+
pos += this.groups[i].parts.length * 7 + 13;
|
|
3981
|
+
}
|
|
3982
|
+
for (const set of this.frameSets) {
|
|
3983
|
+
for (const frm of set) {
|
|
3984
|
+
writeShort(frm.duration);
|
|
3985
|
+
writeLoc(groupPos[frm.groupIndex]);
|
|
3986
|
+
}
|
|
3987
|
+
writeShort(65535);
|
|
3988
|
+
}
|
|
3989
|
+
for (const grp of this.groups) {
|
|
3990
|
+
buffer[position++] = grp.xOffset;
|
|
3991
|
+
buffer[position++] = grp.xOffsetMirror;
|
|
3992
|
+
buffer[position++] = grp.yOffset;
|
|
3993
|
+
buffer[position++] = grp.yOffsetMirror;
|
|
3994
|
+
buffer[position++] = grp.xRecoilHitboxOffset;
|
|
3995
|
+
buffer[position++] = grp.yRecoilHitboxOffset;
|
|
3996
|
+
buffer[position++] = grp.xRecoilHitboxTilesize;
|
|
3997
|
+
buffer[position++] = grp.yRecoilHitboxTilesize;
|
|
3998
|
+
buffer[position++] = grp.xHostileHitboxOffset;
|
|
3999
|
+
buffer[position++] = grp.xHostileHitboxSize;
|
|
4000
|
+
buffer[position++] = grp.yHostileHitboxOffset;
|
|
4001
|
+
buffer[position++] = grp.yHostileHitboxSize;
|
|
4002
|
+
buffer[position++] = grp.parts.length;
|
|
4003
|
+
for (const prt of grp.parts) {
|
|
4004
|
+
buffer[position++] = prt.isLarge ? 1 : 0;
|
|
4005
|
+
buffer[position++] = prt.xOffset;
|
|
4006
|
+
buffer[position++] = prt.xOffsetMirror;
|
|
4007
|
+
buffer[position++] = prt.yOffset;
|
|
4008
|
+
buffer[position++] = prt.yOffsetMirror;
|
|
4009
|
+
const accum = (prt.vMirror ? 32768 : 0) | (prt.hMirror ? 16384 : 0) | (prt.someOffset & 3) << 12 | (prt.paletteIndex & 7) << 9 | prt.tileIndex;
|
|
4010
|
+
writeShort(accum);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
return buffer.slice(0, position);
|
|
3549
4014
|
}
|
|
3550
4015
|
};
|
|
3551
4016
|
|
|
3552
|
-
// src/rom/rebuild/index.ts
|
|
3553
|
-
var ROM_REBUILD_MODULE = "gaia-core/rom/rebuild";
|
|
3554
|
-
|
|
3555
4017
|
// src/index.ts
|
|
3556
4018
|
var GAIA_CORE_VERSION = "0.1.0";
|
|
3557
4019
|
var isPlatformBrowser = typeof window !== "undefined";
|
|
3558
4020
|
var isPlatformNode = typeof process !== "undefined" && process.versions?.node;
|
|
3559
4021
|
var isPlatformWebWorker = typeof importScripts !== "undefined";
|
|
3560
4022
|
|
|
3561
|
-
exports.ADDRESSING_REGEX = ADDRESSING_REGEX;
|
|
3562
|
-
exports.ALL_OPCODES = ALL_OPCODES;
|
|
3563
4023
|
exports.AddressingModeHandler = AddressingModeHandler;
|
|
3564
4024
|
exports.AsmReader = AsmReader;
|
|
4025
|
+
exports.Assembler = Assembler;
|
|
4026
|
+
exports.AssemblerState = AssemblerState;
|
|
3565
4027
|
exports.BlockReader = BlockReader;
|
|
3566
4028
|
exports.BlockWriter = BlockWriter;
|
|
3567
4029
|
exports.CopCommandProcessor = CopCommandProcessor;
|
|
3568
|
-
exports.FileReader = FileReader;
|
|
3569
4030
|
exports.GAIA_CORE_VERSION = GAIA_CORE_VERSION;
|
|
3570
|
-
exports.GROUPED_OPCODES = GROUPED_OPCODES;
|
|
3571
|
-
exports.HEX_REGEX = HEX_REGEX;
|
|
3572
4031
|
exports.ObjectType = ObjectType;
|
|
3573
|
-
exports.Op = Op;
|
|
3574
|
-
exports.OpCode = OpCode;
|
|
3575
|
-
exports.OpCodeUtils = OpCodeUtils;
|
|
3576
4032
|
exports.OperationContext = OperationContext;
|
|
3577
4033
|
exports.PostProcessor = PostProcessor;
|
|
3578
4034
|
exports.ProcessorStateManager = ProcessorStateManager;
|
|
3579
|
-
exports.ProjectManager = ProjectManager;
|
|
3580
4035
|
exports.ProjectRoot = ProjectRoot;
|
|
3581
4036
|
exports.QuintetLZ = QuintetLZ;
|
|
3582
|
-
exports.ROM_REBUILD_MODULE = ROM_REBUILD_MODULE;
|
|
3583
4037
|
exports.ReferenceManager = ReferenceManager;
|
|
3584
4038
|
exports.Registers = Registers;
|
|
3585
4039
|
exports.RomDataReader = RomDataReader;
|
|
4040
|
+
exports.RomGenerator = RomGenerator;
|
|
4041
|
+
exports.RomLayout = RomLayout;
|
|
3586
4042
|
exports.RomProcessor = RomProcessor;
|
|
3587
4043
|
exports.RomState = RomState;
|
|
3588
4044
|
exports.RomStateUtils = RomStateUtils;
|
|
3589
|
-
exports.
|
|
4045
|
+
exports.RomWriter = RomWriter;
|
|
4046
|
+
exports.SortedMap = SortedMap;
|
|
3590
4047
|
exports.SpriteFrame = SpriteFrame;
|
|
3591
4048
|
exports.SpriteGroup = SpriteGroup;
|
|
3592
4049
|
exports.SpriteMap = SpriteMap;
|
|
3593
4050
|
exports.SpritePart = SpritePart;
|
|
3594
4051
|
exports.Stack = Stack;
|
|
3595
4052
|
exports.StackOperations = StackOperations;
|
|
4053
|
+
exports.StringProcessor = StringProcessor;
|
|
3596
4054
|
exports.StringReader = StringReader;
|
|
3597
4055
|
exports.TransformProcessor = TransformProcessor;
|
|
3598
4056
|
exports.TypeParser = TypeParser;
|
|
3599
4057
|
exports.isPlatformBrowser = isPlatformBrowser;
|
|
3600
4058
|
exports.isPlatformNode = isPlatformNode;
|
|
3601
4059
|
exports.isPlatformWebWorker = isPlatformWebWorker;
|
|
4060
|
+
exports.registerCompressionProviders = registerCompressionProviders;
|
|
3602
4061
|
//# sourceMappingURL=index.js.map
|
|
3603
4062
|
//# sourceMappingURL=index.js.map
|