@specy/x86 2.0.4 → 2.1.0
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/README.md +117 -117
- package/dist/assets/assemblers/fasm.1.73.32.elf +0 -0
- package/dist/assets/assemblers/gnu-as.2.43.50.elf +0 -0
- package/dist/assets/assemblers/gnu-ld.2.43.50.elf +0 -0
- package/dist/assets/assemblers/nasm.3.00.elf +0 -0
- package/dist/index.d.mts +53 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +234 -48
- package/dist/index.mjs.map +1 -1
- package/dist/wasm/blinkenlib.d.ts +2 -2
- package/dist/wasm/blinkenlib.js +0 -0
- package/package.json +7 -1
package/dist/index.mjs
CHANGED
|
@@ -49,12 +49,13 @@ const DEFAULT_ASSEMBLER_ID = "NASM_trunk";
|
|
|
49
49
|
function nasmDiagnostics(str) {
|
|
50
50
|
const diagnostics = [];
|
|
51
51
|
const lines = str.split(/\r?\n/);
|
|
52
|
-
const regex =
|
|
52
|
+
const regex = /^(.*):(\d+): (error|warning): (.*)$/;
|
|
53
53
|
for (const line of lines) {
|
|
54
54
|
const match = line.match(regex);
|
|
55
55
|
if (match) diagnostics.push({
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
file: match[1] || void 0,
|
|
57
|
+
line: Number.parseInt(match[2] ?? "0", 10),
|
|
58
|
+
error: (match[4] ?? "").trim()
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
return diagnostics;
|
|
@@ -62,32 +63,39 @@ function nasmDiagnostics(str) {
|
|
|
62
63
|
function gnuDiagnostics(str) {
|
|
63
64
|
const diagnostics = [];
|
|
64
65
|
const lines = str.split("\n");
|
|
65
|
-
const regex =
|
|
66
|
+
const regex = /^(.*):(\d+): (Error: .+)$/;
|
|
66
67
|
for (const line of lines) {
|
|
67
68
|
const match = line.match(regex);
|
|
68
69
|
if (match) diagnostics.push({
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
file: match[1] || void 0,
|
|
71
|
+
line: Number.parseInt(match[2] ?? "0", 10),
|
|
72
|
+
error: match[3] ?? ""
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
75
|
return diagnostics;
|
|
74
76
|
}
|
|
75
77
|
function fasmDiagnostics(str) {
|
|
76
78
|
const diagnostics = [];
|
|
77
|
-
const lineRegex =
|
|
79
|
+
const lineRegex = /^(.*) \[(\d+)\]:/;
|
|
78
80
|
const errorRegex = /error: .+/;
|
|
79
81
|
const lines = str.split("\n");
|
|
80
82
|
let lineNumber = null;
|
|
83
|
+
let file;
|
|
81
84
|
for (const line of lines) {
|
|
82
85
|
const lineMatch = line.match(lineRegex);
|
|
83
86
|
const errorMatch = line.match(errorRegex);
|
|
84
|
-
if (lineMatch)
|
|
87
|
+
if (lineMatch) {
|
|
88
|
+
file = lineMatch[1] || void 0;
|
|
89
|
+
lineNumber = Number.parseInt(lineMatch[2] ?? "0", 10);
|
|
90
|
+
}
|
|
85
91
|
if (errorMatch && lineNumber !== null) {
|
|
86
92
|
diagnostics.push({
|
|
93
|
+
file,
|
|
87
94
|
line: lineNumber,
|
|
88
95
|
error: errorMatch[0]
|
|
89
96
|
});
|
|
90
97
|
lineNumber = null;
|
|
98
|
+
file = void 0;
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
return diagnostics;
|
|
@@ -170,6 +178,9 @@ var SourceMap = class {
|
|
|
170
178
|
return this.entries.length;
|
|
171
179
|
}
|
|
172
180
|
getLineIndex(address) {
|
|
181
|
+
return this.getLocation(address)?.lineIndex ?? null;
|
|
182
|
+
}
|
|
183
|
+
getLocation(address) {
|
|
173
184
|
let left = 0;
|
|
174
185
|
let right = this.entries.length - 1;
|
|
175
186
|
let match = null;
|
|
@@ -182,13 +193,19 @@ var SourceMap = class {
|
|
|
182
193
|
left = middle + 1;
|
|
183
194
|
} else right = middle - 1;
|
|
184
195
|
}
|
|
185
|
-
return match
|
|
196
|
+
return match ? { ...match } : null;
|
|
186
197
|
}
|
|
187
198
|
getAddressesForLine(lineIndex) {
|
|
199
|
+
return this.getAddressesForLocation(lineIndex);
|
|
200
|
+
}
|
|
201
|
+
getAddressesForLocation(lineIndex, file) {
|
|
202
|
+
return this.getAddressesMatching(lineIndex, (candidate) => file === void 0 || candidate === file);
|
|
203
|
+
}
|
|
204
|
+
getAddressesMatching(lineIndex, matchesFile) {
|
|
188
205
|
const addresses = [];
|
|
189
206
|
const seen = /* @__PURE__ */ new Set();
|
|
190
207
|
for (const entry of this.entries) {
|
|
191
|
-
if (entry.lineIndex !== lineIndex) continue;
|
|
208
|
+
if (entry.lineIndex !== lineIndex || !matchesFile(entry.file)) continue;
|
|
192
209
|
const key = entry.address.toString();
|
|
193
210
|
if (seen.has(key)) continue;
|
|
194
211
|
seen.add(key);
|
|
@@ -196,6 +213,16 @@ var SourceMap = class {
|
|
|
196
213
|
}
|
|
197
214
|
return addresses;
|
|
198
215
|
}
|
|
216
|
+
getAddresses() {
|
|
217
|
+
const addresses = [];
|
|
218
|
+
let previous;
|
|
219
|
+
for (const entry of this.entries) {
|
|
220
|
+
if (entry.address === previous) continue;
|
|
221
|
+
addresses.push(entry.address);
|
|
222
|
+
previous = entry.address;
|
|
223
|
+
}
|
|
224
|
+
return addresses;
|
|
225
|
+
}
|
|
199
226
|
};
|
|
200
227
|
function parseSourceMap(bytes) {
|
|
201
228
|
const section = findElfSection(bytes, ".debug_line");
|
|
@@ -302,7 +329,7 @@ function parseDebugLine(bytes) {
|
|
|
302
329
|
entries.push({
|
|
303
330
|
address,
|
|
304
331
|
lineIndex: Math.max(0, line - 1),
|
|
305
|
-
file: sourceFile
|
|
332
|
+
file: dwarfSourcePath(sourceFile)
|
|
306
333
|
});
|
|
307
334
|
};
|
|
308
335
|
const advanceAddress = (operationAdvance) => {
|
|
@@ -392,6 +419,11 @@ function parseDebugLine(bytes) {
|
|
|
392
419
|
}
|
|
393
420
|
return entries;
|
|
394
421
|
}
|
|
422
|
+
function dwarfSourcePath(sourceFile) {
|
|
423
|
+
if (!sourceFile?.name) return void 0;
|
|
424
|
+
if (!sourceFile.directory || sourceFile.name.startsWith("/")) return sourceFile.name;
|
|
425
|
+
return `${sourceFile.directory.replace(/\/$/, "")}/${sourceFile.name}`;
|
|
426
|
+
}
|
|
395
427
|
function readU64Number(view, offset) {
|
|
396
428
|
const value = view.getBigUint64(offset, true);
|
|
397
429
|
if (value > BigInt(Number.MAX_SAFE_INTEGER)) return Number.MAX_SAFE_INTEGER;
|
|
@@ -438,6 +470,87 @@ function compareBigInt(left, right) {
|
|
|
438
470
|
return 0;
|
|
439
471
|
}
|
|
440
472
|
//#endregion
|
|
473
|
+
//#region src/project.ts
|
|
474
|
+
const X86_PROJECT_ROOT = "/__x86_project";
|
|
475
|
+
const stagedFiles = /* @__PURE__ */ new WeakMap();
|
|
476
|
+
function validateX86Project(project) {
|
|
477
|
+
if (!isProjectPath(project.entry)) throw new Error(`Invalid x86 Project entry path: ${project.entry}`);
|
|
478
|
+
if (!Object.hasOwn(project.files, project.entry)) throw new Error(`x86 Project entry file not found: ${project.entry}`);
|
|
479
|
+
if (typeof project.files[project.entry] !== "string") throw new Error(`x86 Project entry must be a text file: ${project.entry}`);
|
|
480
|
+
for (const [path, contents] of Object.entries(project.files)) {
|
|
481
|
+
if (!isProjectPath(path)) throw new Error(`Invalid x86 Project file path: ${path}`);
|
|
482
|
+
if (typeof contents !== "string" && !(contents instanceof Uint8Array)) throw new Error(`Invalid x86 Project file contents: ${path}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Writes a Project into the emulator filesystem and selects the Entry directory as NASM's working
|
|
487
|
+
* directory. NASM consequently owns `%include` and `incbin` resolution, including paths produced by
|
|
488
|
+
* preprocessor macros.
|
|
489
|
+
*/
|
|
490
|
+
function stageX86Project(fs, project) {
|
|
491
|
+
validateX86Project(project);
|
|
492
|
+
const previous = stagedFiles.get(fs) ?? /* @__PURE__ */ new Set();
|
|
493
|
+
const current = /* @__PURE__ */ new Set();
|
|
494
|
+
fs.mkdirTree(X86_PROJECT_ROOT);
|
|
495
|
+
for (const [path, contents] of Object.entries(project.files)) {
|
|
496
|
+
const absolutePath = `${X86_PROJECT_ROOT}/${path}`;
|
|
497
|
+
const separator = absolutePath.lastIndexOf("/");
|
|
498
|
+
if (separator > 0) fs.mkdirTree(absolutePath.slice(0, separator));
|
|
499
|
+
fs.writeFile(absolutePath, contents);
|
|
500
|
+
current.add(absolutePath);
|
|
501
|
+
}
|
|
502
|
+
for (const path of previous) {
|
|
503
|
+
if (current.has(path)) continue;
|
|
504
|
+
try {
|
|
505
|
+
fs.unlink(path);
|
|
506
|
+
} catch {}
|
|
507
|
+
}
|
|
508
|
+
fs.writeFile("/assembly.s", project.files[project.entry]);
|
|
509
|
+
fs.chdir(projectDirectory(project.entry));
|
|
510
|
+
stagedFiles.set(fs, current);
|
|
511
|
+
}
|
|
512
|
+
/** Maps a path emitted by NASM or DWARF back to the exact Project path. */
|
|
513
|
+
function x86ProjectSourcePath(sourcePath, project) {
|
|
514
|
+
if (!sourcePath || sourcePath === "/assembly.s" || sourcePath === "assembly.s") return project.entry;
|
|
515
|
+
const withoutLeadingSlash = (sourcePath.startsWith(`/__x86_project/`) ? sourcePath.slice(15) : sourcePath).replace(/^\/+/, "");
|
|
516
|
+
const relativeToEntry = resolvePath(parentPath(project.entry), withoutLeadingSlash);
|
|
517
|
+
if (relativeToEntry && relativeToEntry in project.files) return relativeToEntry;
|
|
518
|
+
const relativeToRoot = resolvePath("", withoutLeadingSlash);
|
|
519
|
+
if (relativeToRoot && relativeToRoot in project.files) return relativeToRoot;
|
|
520
|
+
const suffix = `/${withoutLeadingSlash}`;
|
|
521
|
+
const suffixMatches = Object.keys(project.files).filter((path) => path === withoutLeadingSlash || path.endsWith(suffix));
|
|
522
|
+
return suffixMatches.length === 1 ? suffixMatches[0] : project.entry;
|
|
523
|
+
}
|
|
524
|
+
function projectDirectory(entry) {
|
|
525
|
+
const parent = parentPath(entry);
|
|
526
|
+
return parent ? `${X86_PROJECT_ROOT}/${parent}` : X86_PROJECT_ROOT;
|
|
527
|
+
}
|
|
528
|
+
function parentPath(path) {
|
|
529
|
+
const separator = path.lastIndexOf("/");
|
|
530
|
+
return separator < 0 ? "" : path.slice(0, separator);
|
|
531
|
+
}
|
|
532
|
+
function isProjectPath(path) {
|
|
533
|
+
return typeof path === "string" && path.length > 0 && !path.includes("\\") && !path.startsWith("/") && path.split("/").every((part) => part !== "" && part !== "." && part !== "..") && !Array.from(path).some((character) => {
|
|
534
|
+
const code = character.charCodeAt(0);
|
|
535
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
function resolvePath(base, written) {
|
|
539
|
+
const parts = base ? base.split("/") : [];
|
|
540
|
+
for (const part of written.split("/")) {
|
|
541
|
+
if (!part || part === ".") continue;
|
|
542
|
+
if (part === "..") {
|
|
543
|
+
if (!parts.length) return null;
|
|
544
|
+
parts.pop();
|
|
545
|
+
} else parts.push(part);
|
|
546
|
+
}
|
|
547
|
+
return parts.join("/") || null;
|
|
548
|
+
}
|
|
549
|
+
function x86ProjectText(project, path) {
|
|
550
|
+
const contents = project.files[path];
|
|
551
|
+
return typeof contents === "string" ? contents : "";
|
|
552
|
+
}
|
|
553
|
+
//#endregion
|
|
441
554
|
//#region src/types.ts
|
|
442
555
|
const X86_REGISTER_NAMES = [
|
|
443
556
|
"rax",
|
|
@@ -575,6 +688,7 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
575
688
|
stateWaiters = [];
|
|
576
689
|
stdinBytes = [];
|
|
577
690
|
sourceMap = null;
|
|
691
|
+
sourceProject = null;
|
|
578
692
|
defaultArgc = "/program";
|
|
579
693
|
defaultArgv = "";
|
|
580
694
|
constructor(module, mode, callbacks, scheduler) {
|
|
@@ -626,17 +740,32 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
626
740
|
this.setState("READY");
|
|
627
741
|
}
|
|
628
742
|
async compileAssembly(code) {
|
|
743
|
+
return this.compileProject({
|
|
744
|
+
entry: "assembly.s",
|
|
745
|
+
files: { "assembly.s": code }
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
async compileProject(project) {
|
|
629
749
|
this.assertReadyForCompile();
|
|
750
|
+
const sourceProject = {
|
|
751
|
+
entry: project.entry,
|
|
752
|
+
files: Object.fromEntries(Object.entries(project.files).map(([path, contents]) => [path, contents instanceof Uint8Array ? contents.slice() : contents]))
|
|
753
|
+
};
|
|
754
|
+
stageX86Project(this.module.FS, sourceProject);
|
|
755
|
+
this.sourceProject = sourceProject;
|
|
630
756
|
this.stopReason = null;
|
|
631
757
|
this.assemblerLogs = "";
|
|
632
758
|
this.assemblerErrors = [];
|
|
633
759
|
this.sourceMap = null;
|
|
634
|
-
this.module.FS.writeFile("/assembly.s", code);
|
|
635
760
|
this.setState("ASSEMBLING");
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
761
|
+
try {
|
|
762
|
+
await defer();
|
|
763
|
+
this.setEmulationArgs("/assembler", this.mode.binaries.assembler.commands, "");
|
|
764
|
+
this.module._blinkenlib_run_fast();
|
|
765
|
+
await this.waitForState((state) => state !== "ASSEMBLING" && state !== "LINKING");
|
|
766
|
+
} finally {
|
|
767
|
+
this.module.FS.chdir("/");
|
|
768
|
+
}
|
|
640
769
|
if (this.state === "PROGRAM_LOADED") {
|
|
641
770
|
this.sourceMap = this.tryReadSourceMap();
|
|
642
771
|
return {
|
|
@@ -646,7 +775,10 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
646
775
|
}
|
|
647
776
|
return {
|
|
648
777
|
ok: false,
|
|
649
|
-
errors: this.assemblerErrors
|
|
778
|
+
errors: this.assemblerErrors.map((error) => ({
|
|
779
|
+
...error,
|
|
780
|
+
file: x86ProjectSourcePath(error.file, sourceProject)
|
|
781
|
+
})),
|
|
650
782
|
report: this.assemblerLogs
|
|
651
783
|
};
|
|
652
784
|
}
|
|
@@ -655,6 +787,7 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
655
787
|
this.writeExecutableSync("/program", data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
656
788
|
this.stopReason = null;
|
|
657
789
|
this.sourceMap = null;
|
|
790
|
+
this.sourceProject = null;
|
|
658
791
|
this.setState("PROGRAM_LOADED");
|
|
659
792
|
}
|
|
660
793
|
starti() {
|
|
@@ -745,30 +878,48 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
745
878
|
return this.module.blinkenlibResolveSymbol(address);
|
|
746
879
|
}
|
|
747
880
|
getSourceLineForAddress(address) {
|
|
748
|
-
return this.
|
|
881
|
+
return this.getSourceLocationForAddress(address)?.line ?? null;
|
|
882
|
+
}
|
|
883
|
+
getSourceLocationForAddress(address) {
|
|
884
|
+
const location = this.sourceMap?.getLocation(address);
|
|
885
|
+
if (!location) return null;
|
|
886
|
+
return {
|
|
887
|
+
path: this.sourceProject ? x86ProjectSourcePath(location.file, this.sourceProject) : location.file ?? "assembly.s",
|
|
888
|
+
line: location.lineIndex
|
|
889
|
+
};
|
|
749
890
|
}
|
|
750
891
|
getAddressesForSourceLine(lineIndex) {
|
|
751
892
|
return this.sourceMap?.getAddressesForLine(lineIndex) ?? [];
|
|
752
893
|
}
|
|
753
|
-
|
|
894
|
+
getAddressesForSourceLocation(location) {
|
|
895
|
+
if (!this.sourceMap || !this.sourceProject) return [];
|
|
896
|
+
return this.sourceMap.getAddressesMatching(location.line, (file) => x86ProjectSourcePath(file, this.sourceProject) === location.path);
|
|
897
|
+
}
|
|
898
|
+
getSourceMappedAddresses() {
|
|
899
|
+
return this.sourceMap?.getAddresses() ?? [];
|
|
900
|
+
}
|
|
901
|
+
pauseForBreakpoint(address, location) {
|
|
754
902
|
this.stopReason = {
|
|
755
903
|
loadFail: false,
|
|
756
904
|
exitCode: 0,
|
|
757
905
|
kind: "breakpoint",
|
|
758
906
|
details: `execution paused at breakpoint 0x${address.toString(16)}`,
|
|
759
907
|
address,
|
|
760
|
-
lineNumber
|
|
908
|
+
lineNumber: location?.line,
|
|
909
|
+
file: location?.path
|
|
761
910
|
};
|
|
762
911
|
this.setState("PROGRAM_PAUSED");
|
|
763
912
|
}
|
|
764
913
|
pauseForLimit(address, executedInstructions) {
|
|
914
|
+
const location = this.getSourceLocationForAddress(address);
|
|
765
915
|
this.stopReason = {
|
|
766
916
|
loadFail: false,
|
|
767
917
|
exitCode: 0,
|
|
768
918
|
kind: "limit",
|
|
769
919
|
details: `execution paused after ${executedInstructions.toString()} instructions`,
|
|
770
920
|
address,
|
|
771
|
-
lineNumber:
|
|
921
|
+
lineNumber: location?.line,
|
|
922
|
+
file: location?.path,
|
|
772
923
|
executedInstructions
|
|
773
924
|
};
|
|
774
925
|
this.setState("PROGRAM_PAUSED");
|
|
@@ -842,14 +993,15 @@ var BlinkRuntime = class BlinkRuntime {
|
|
|
842
993
|
}
|
|
843
994
|
if (code === SIGTRAP_CODES.BLINK_BREAKPOINT || code === SIGTRAP_CODES.BLINK_RUN_LIMIT) {
|
|
844
995
|
const stop = this.module.blinkenlibGetRunStop();
|
|
845
|
-
const
|
|
996
|
+
const location = this.getSourceLocationForAddress(stop.address);
|
|
846
997
|
this.stopReason = {
|
|
847
998
|
loadFail: false,
|
|
848
999
|
exitCode: 0,
|
|
849
1000
|
kind: stop.kind === "limit" ? "limit" : "breakpoint",
|
|
850
1001
|
details: stop.kind === "limit" ? `execution paused after ${stop.executedInstructions.toString()} instructions` : `execution paused at breakpoint 0x${stop.address.toString(16)}`,
|
|
851
1002
|
address: stop.address,
|
|
852
|
-
lineNumber,
|
|
1003
|
+
lineNumber: location?.line,
|
|
1004
|
+
file: location?.path,
|
|
853
1005
|
executedInstructions: stop.executedInstructions
|
|
854
1006
|
};
|
|
855
1007
|
this.setState("PROGRAM_PAUSED");
|
|
@@ -1101,14 +1253,15 @@ function stripPrivateHistory(entry) {
|
|
|
1101
1253
|
pc: entry.pc,
|
|
1102
1254
|
old_ccr: entry.old_ccr,
|
|
1103
1255
|
new_ccr: entry.new_ccr,
|
|
1104
|
-
line: entry.line
|
|
1256
|
+
line: entry.line,
|
|
1257
|
+
file: entry.file
|
|
1105
1258
|
};
|
|
1106
1259
|
}
|
|
1107
1260
|
function toHistoryPc(address) {
|
|
1108
1261
|
return Number(address);
|
|
1109
1262
|
}
|
|
1110
1263
|
function makeFrameColor(index, address) {
|
|
1111
|
-
return `hsl(${
|
|
1264
|
+
return `hsl(${index * 137 % 360}, 40%, 60%)`;
|
|
1112
1265
|
}
|
|
1113
1266
|
function maskForSize(size) {
|
|
1114
1267
|
return (1n << BigInt(size * 8)) - 1n;
|
|
@@ -1201,9 +1354,15 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1201
1354
|
await this.runtime.setMode(mode);
|
|
1202
1355
|
}
|
|
1203
1356
|
async compile(code) {
|
|
1357
|
+
return this.compileProject({
|
|
1358
|
+
entry: "assembly.s",
|
|
1359
|
+
files: { "assembly.s": code }
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
async compileProject(project) {
|
|
1204
1363
|
this.clearExecutionTrace();
|
|
1205
|
-
this.lastSourceCode =
|
|
1206
|
-
this.lastCompileResult = await this.runtime.
|
|
1364
|
+
this.lastSourceCode = x86ProjectText(project, project.entry);
|
|
1365
|
+
this.lastCompileResult = await this.runtime.compileProject(project);
|
|
1207
1366
|
return this.lastCompileResult;
|
|
1208
1367
|
}
|
|
1209
1368
|
loadElf(data) {
|
|
@@ -1244,19 +1403,28 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1244
1403
|
return String(error);
|
|
1245
1404
|
}
|
|
1246
1405
|
async checkCode(code) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1406
|
+
return this.checkProject({
|
|
1407
|
+
entry: "assembly.s",
|
|
1408
|
+
files: { "assembly.s": code }
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
async checkProject(project) {
|
|
1412
|
+
const result = await this.compileProject(project);
|
|
1413
|
+
return (result.ok === false ? result.errors : []).map((error) => {
|
|
1414
|
+
const path = error.file ?? project.entry;
|
|
1415
|
+
const lineIndex = Math.max(0, error.line - 1);
|
|
1416
|
+
return {
|
|
1417
|
+
file: path,
|
|
1418
|
+
lineIndex,
|
|
1419
|
+
column: 0,
|
|
1420
|
+
line: {
|
|
1421
|
+
line: x86ProjectText(project, path).split(/\r?\n/)[lineIndex] ?? "",
|
|
1422
|
+
line_index: lineIndex
|
|
1423
|
+
},
|
|
1424
|
+
message: error.error,
|
|
1425
|
+
formatted: error.error
|
|
1426
|
+
};
|
|
1427
|
+
});
|
|
1260
1428
|
}
|
|
1261
1429
|
undo() {
|
|
1262
1430
|
const entry = this.history.pop();
|
|
@@ -1329,12 +1497,24 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1329
1497
|
getInstructionAt(address) {
|
|
1330
1498
|
const instruction = this.runtime.getInstructionAt(address);
|
|
1331
1499
|
if (!instruction) return null;
|
|
1500
|
+
const location = this.runtime.getSourceLocationForAddress(instruction.address);
|
|
1332
1501
|
return {
|
|
1333
1502
|
address: instruction.address,
|
|
1334
|
-
lineNumber:
|
|
1503
|
+
lineNumber: location?.line ?? -1,
|
|
1504
|
+
file: location?.path,
|
|
1505
|
+
size: instruction.size,
|
|
1506
|
+
bytes: this.runtime.readMemoryBytes(instruction.address, BigInt(instruction.size)),
|
|
1335
1507
|
code: instruction.code
|
|
1336
1508
|
};
|
|
1337
1509
|
}
|
|
1510
|
+
/** Every instruction address represented in the linked program's DWARF source map. */
|
|
1511
|
+
getCompiledInstructions() {
|
|
1512
|
+
if (this.runtime.state === "PROGRAM_LOADED") this.runtime.pauseAtEntry();
|
|
1513
|
+
return this.runtime.getSourceMappedAddresses().flatMap((address) => {
|
|
1514
|
+
const instruction = this.getInstructionAt(address);
|
|
1515
|
+
return instruction ? [instruction] : [];
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1338
1518
|
getRegisterValues() {
|
|
1339
1519
|
return X86_REGISTER_NAMES.map((register) => this.getRegisterValue(register));
|
|
1340
1520
|
}
|
|
@@ -1370,9 +1550,11 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1370
1550
|
}
|
|
1371
1551
|
resolveBreakpointAddresses(breakpoints) {
|
|
1372
1552
|
const addresses = /* @__PURE__ */ new Map();
|
|
1373
|
-
for (const
|
|
1553
|
+
for (const breakpoint of breakpoints) {
|
|
1554
|
+
const lineIndex = typeof breakpoint === "number" ? breakpoint : breakpoint.line;
|
|
1374
1555
|
if (!Number.isSafeInteger(lineIndex) || lineIndex < 0) throw new Error(`Invalid breakpoint line index: ${lineIndex}`);
|
|
1375
|
-
|
|
1556
|
+
const resolved = typeof breakpoint === "number" ? this.runtime.getAddressesForSourceLine(lineIndex) : this.runtime.getAddressesForSourceLocation(breakpoint);
|
|
1557
|
+
for (const address of resolved) addresses.set(address.toString(), address);
|
|
1376
1558
|
}
|
|
1377
1559
|
return [...addresses.values()];
|
|
1378
1560
|
}
|
|
@@ -1384,7 +1566,7 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1384
1566
|
while (this.runtime.state === "PROGRAM_RUNNING") {
|
|
1385
1567
|
const pc = this.getPc();
|
|
1386
1568
|
if (breakpointSet.has(pc.toString())) {
|
|
1387
|
-
this.runtime.pauseForBreakpoint(pc, this.runtime.
|
|
1569
|
+
this.runtime.pauseForBreakpoint(pc, this.runtime.getSourceLocationForAddress(pc) ?? void 0);
|
|
1388
1570
|
return this.getStatus();
|
|
1389
1571
|
}
|
|
1390
1572
|
if (hasLimit && executedInstructions >= limit) {
|
|
@@ -1405,7 +1587,7 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1405
1587
|
while (this.runtime.state === "PROGRAM_RUNNING") {
|
|
1406
1588
|
const pc = this.getPc();
|
|
1407
1589
|
if (breakpointSet.has(pc.toString())) {
|
|
1408
|
-
this.runtime.pauseForBreakpoint(pc, this.runtime.
|
|
1590
|
+
this.runtime.pauseForBreakpoint(pc, this.runtime.getSourceLocationForAddress(pc) ?? void 0);
|
|
1409
1591
|
return this.getStatus();
|
|
1410
1592
|
}
|
|
1411
1593
|
if (hasLimit && executedInstructions >= limit) {
|
|
@@ -1463,12 +1645,14 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1463
1645
|
}
|
|
1464
1646
|
const memoryWrites = nativeStep.valid ? this.recordMemoryMutations(nativeStep.memoryWrites, mutations) : [];
|
|
1465
1647
|
if (nativeStep.valid) this.recordControlFlowMutation(nativeStep, instruction, mutations);
|
|
1648
|
+
const location = this.runtime.getSourceLocationForAddress(pcBefore);
|
|
1466
1649
|
const entry = {
|
|
1467
1650
|
mutations,
|
|
1468
1651
|
pc: toHistoryPc(pcBefore),
|
|
1469
1652
|
old_ccr: { bits: flagsBefore },
|
|
1470
1653
|
new_ccr: { bits: flagsAfter },
|
|
1471
|
-
line:
|
|
1654
|
+
line: location?.line ?? -1,
|
|
1655
|
+
file: location?.path,
|
|
1472
1656
|
registersBefore,
|
|
1473
1657
|
flagsBefore,
|
|
1474
1658
|
callStackBefore,
|
|
@@ -1506,12 +1690,14 @@ var X86Emulator = class X86Emulator extends BaseEmulator {
|
|
|
1506
1690
|
const returnAddress = instruction ? instruction.address + BigInt(instruction.size) : step.pcBefore;
|
|
1507
1691
|
const symbol = this.runtime.resolveSymbol(step.pcAfter);
|
|
1508
1692
|
const frameAddress = symbol?.address ?? step.pcAfter;
|
|
1693
|
+
const location = this.runtime.getSourceLocationForAddress(frameAddress);
|
|
1509
1694
|
this.callStack.push({
|
|
1510
1695
|
name: symbol?.name ?? "",
|
|
1511
1696
|
address: frameAddress,
|
|
1512
1697
|
destination: returnAddress,
|
|
1513
1698
|
sp: step.spAfter,
|
|
1514
|
-
line:
|
|
1699
|
+
line: location?.line ?? -1,
|
|
1700
|
+
file: location?.path,
|
|
1515
1701
|
color: makeFrameColor(this.callStack.length, frameAddress)
|
|
1516
1702
|
});
|
|
1517
1703
|
mutations.push({
|
|
@@ -1541,6 +1727,6 @@ async function createX86Emulator(options = {}) {
|
|
|
1541
1727
|
return X86Emulator.create(options);
|
|
1542
1728
|
}
|
|
1543
1729
|
//#endregion
|
|
1544
|
-
export { BaseEmulator, BlinkRuntime, BlinkState, DEFAULT_ASSEMBLER_ID, EmulatorStatus, RegisterSize, X86Emulator, X86_REGISTER_NAMES, assemblers, createX86Emulator, defaultResourceUrl, fasmDiagnostics, gnuDiagnostics, isNodeRuntime, nasmDiagnostics, observeCallbackResult, readResourceBytes, toResourceUrl };
|
|
1730
|
+
export { BaseEmulator, BlinkRuntime, BlinkState, DEFAULT_ASSEMBLER_ID, EmulatorStatus, RegisterSize, X86Emulator, X86_PROJECT_ROOT, X86_REGISTER_NAMES, assemblers, createX86Emulator, defaultResourceUrl, fasmDiagnostics, gnuDiagnostics, isNodeRuntime, nasmDiagnostics, observeCallbackResult, readResourceBytes, stageX86Project, toResourceUrl, validateX86Project, x86ProjectSourcePath, x86ProjectText };
|
|
1545
1731
|
|
|
1546
1732
|
//# sourceMappingURL=index.mjs.map
|