c64bridge 0.9.0 → 0.9.2
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 +161 -135
- package/data/assembly/assembly-spec.md +1 -1
- package/data/context/bootstrap.md +3 -3
- package/data/context/fast-paths.md +3 -1
- package/data/graphics/petscii-style-guide.md +1 -1
- package/data/graphics/sprite-charset-best-practices.md +14 -13
- package/data/sound/sid-file-structure.md +5 -5
- package/data/sound/sid-programming-best-practices.md +5 -5
- package/data/vice/vice-binary-monitor-spec.md +26 -10
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +107 -8
- package/dist/device.js +43 -3
- package/dist/rag/indexer.js +18 -8
- package/dist/sidwaveCompiler.js +1 -1
- package/dist/tools/debug.js +158 -5
- package/dist/tools/input.js +336 -0
- package/dist/tools/memory.js +326 -1
- package/dist/tools/programRunners.js +45 -6
- package/dist/tools/registry/batch.js +167 -0
- package/dist/tools/registry/graphics.js +101 -1
- package/dist/tools/registry/index.js +4 -0
- package/dist/tools/registry/input.js +2 -0
- package/dist/tools/registry/memory.js +61 -1
- package/dist/tools/responses.js +14 -1
- package/dist/tools/schema.js +4 -5
- package/dist/{assemblyConverter.js → tools/translation/assembler.js} +66 -163
- package/dist/{basicConverter.js → tools/translation/basicTokenizer.js} +1 -1
- package/dist/tools/translation/disassembler.js +150 -0
- package/dist/tools/translation/opcodeInventory.js +126 -0
- package/dist/tools/translation/symbolRegistry.js +49 -0
- package/dist/tools/types.js +19 -4
- package/dist/vice/process.js +43 -2
- package/dist/vice/viceClient.js +5 -1
- package/doc/reviews/audit.md +143 -0
- package/mcp/schemas/c64_batch.schema.json +53 -0
- package/mcp/schemas/c64_config.schema.json +461 -2
- package/mcp/schemas/c64_debug.schema.json +641 -3
- package/mcp/schemas/c64_disk.schema.json +340 -2
- package/mcp/schemas/c64_drive.schema.json +157 -2
- package/mcp/schemas/c64_extract.schema.json +359 -2
- package/mcp/schemas/c64_graphics.schema.json +415 -3
- package/mcp/schemas/c64_input.schema.json +254 -0
- package/mcp/schemas/c64_memory.schema.json +562 -3
- package/mcp/schemas/c64_printer.schema.json +253 -2
- package/mcp/schemas/c64_program.schema.json +499 -2
- package/mcp/schemas/c64_rag.schema.json +82 -2
- package/mcp/schemas/c64_select_backend.schema.json +35 -2
- package/mcp/schemas/c64_sound.schema.json +793 -2
- package/mcp/schemas/c64_stream.schema.json +73 -2
- package/mcp/schemas/c64_system.schema.json +301 -2
- package/mcp/schemas/c64_vice.schema.json +71 -2
- package/mcp/server.json +1 -1
- package/mcp/tools.json +7509 -1929
- package/mcp.json +1 -1
- package/package.json +2 -2
- package/scripts/c64-cli.mjs +2 -2
- package/scripts/run-coverage.mjs +61 -3
- package/scripts/run-tests.mjs +1 -0
- package/scripts/run-tests.ts +3 -0
- package/scripts/update-readme.ts +79 -2
- package/server.json +2 -2
|
@@ -3,8 +3,8 @@ import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema,
|
|
|
3
3
|
import { graphicsModule, graphicsOperationHandlers as groupedGraphicsHandlers } from "../graphics.js";
|
|
4
4
|
import { booleanSchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "../schema.js";
|
|
5
5
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, } from "./utils.js";
|
|
6
|
-
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
7
6
|
import { jsonResult } from "../responses.js";
|
|
7
|
+
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
8
8
|
const graphicsDescriptorIndex = buildDescriptorIndex(graphicsModule);
|
|
9
9
|
const captureFrameArgsSchema = objectSchema({
|
|
10
10
|
description: "Capture one or more complete video frames from the active backend.",
|
|
@@ -30,6 +30,72 @@ const captureFrameArgsSchema = objectSchema({
|
|
|
30
30
|
required: ["op"],
|
|
31
31
|
additionalProperties: false,
|
|
32
32
|
});
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// get_display_state helpers — VIC-II register decoding
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
function parseVicState(d011, d016, d018, dd00, d020, d021) {
|
|
37
|
+
const bank = (~dd00) & 0x03; // CIA2 Port A bits 0-1 select VIC bank (inverted)
|
|
38
|
+
const bankBase = bank * 0x4000;
|
|
39
|
+
const bitmapMode = (d011 & 0x20) !== 0;
|
|
40
|
+
const extendedColor = (d011 & 0x40) !== 0;
|
|
41
|
+
const multicolor = (d016 & 0x10) !== 0;
|
|
42
|
+
const screenVisible = (d011 & 0x10) !== 0;
|
|
43
|
+
const raster8 = (d011 & 0x80) !== 0;
|
|
44
|
+
const vertScroll = d011 & 0x07;
|
|
45
|
+
const horizScroll = d016 & 0x07;
|
|
46
|
+
const columns40 = (d016 & 0x08) !== 0;
|
|
47
|
+
const rows25 = (d011 & 0x08) !== 0;
|
|
48
|
+
// D018 bits 4-7 = screen RAM offset (× $0400); bits 1-3 = char/bitmap pointer (× $0800)
|
|
49
|
+
const screenRamOffset = ((d018 >> 4) & 0x0f) * 0x0400;
|
|
50
|
+
const screenRamAddress = bankBase + screenRamOffset;
|
|
51
|
+
const charOrBitmapPointer = ((d018 >> 1) & 0x07) * 0x0800;
|
|
52
|
+
const charOrBitmapAddress = bankBase + charOrBitmapPointer;
|
|
53
|
+
let mode;
|
|
54
|
+
if (bitmapMode && multicolor)
|
|
55
|
+
mode = "multicolor_bitmap";
|
|
56
|
+
else if (bitmapMode)
|
|
57
|
+
mode = "hires_bitmap";
|
|
58
|
+
else if (extendedColor)
|
|
59
|
+
mode = "extended_color_text";
|
|
60
|
+
else if (multicolor)
|
|
61
|
+
mode = "multicolor_text";
|
|
62
|
+
else
|
|
63
|
+
mode = "standard_text";
|
|
64
|
+
return {
|
|
65
|
+
mode,
|
|
66
|
+
bank,
|
|
67
|
+
bankBase: `$${bankBase.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
68
|
+
screenRamAddress: `$${screenRamAddress.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
69
|
+
charOrBitmapAddress: `$${charOrBitmapAddress.toString(16).toUpperCase().padStart(4, "0")}`,
|
|
70
|
+
screenVisible,
|
|
71
|
+
bitmapMode,
|
|
72
|
+
extendedColor,
|
|
73
|
+
multicolor,
|
|
74
|
+
rows25,
|
|
75
|
+
columns40,
|
|
76
|
+
vertScroll,
|
|
77
|
+
horizScroll,
|
|
78
|
+
raster8,
|
|
79
|
+
borderColor: d020 & 0x0f,
|
|
80
|
+
backgroundColor: d021 & 0x0f,
|
|
81
|
+
registers: {
|
|
82
|
+
d011: `$${d011.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
83
|
+
d016: `$${d016.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
84
|
+
d018: `$${d018.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
85
|
+
d020: `$${d020.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
86
|
+
d021: `$${d021.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
87
|
+
dd00: `$${dd00.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const getDisplayStateArgsSchema = objectSchema({
|
|
92
|
+
description: "Read VIC-II and CIA2 registers to determine the current graphics mode and memory layout. Works on C64U and VICE; both backends are read through shared memory primitives so the response shape is identical.",
|
|
93
|
+
properties: {
|
|
94
|
+
op: literalSchema("get_display_state"),
|
|
95
|
+
},
|
|
96
|
+
required: ["op"],
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
});
|
|
33
99
|
const graphicsOperations = [
|
|
34
100
|
{
|
|
35
101
|
op: "capture_frame",
|
|
@@ -105,6 +171,35 @@ const graphicsOperations = [
|
|
|
105
171
|
schema: extendSchemaWithOp("render_bitmap", ensureDescriptor(graphicsDescriptorIndex, "render_bitmap").inputSchema, { description: "Import an image file, convert it to VIC-II bitmap memory, write it into RAM, and display it." }),
|
|
106
172
|
handler: groupedGraphicsHandlers.render_bitmap,
|
|
107
173
|
},
|
|
174
|
+
{
|
|
175
|
+
op: "get_display_state",
|
|
176
|
+
schema: getDisplayStateArgsSchema.jsonSchema,
|
|
177
|
+
handler: async (_rawArgs, ctx) => {
|
|
178
|
+
try {
|
|
179
|
+
// Use cross-platform memory reads so the same handler works on both
|
|
180
|
+
// C64U and VICE. The VIC-II and CIA2 registers live in machine memory
|
|
181
|
+
// either way; the underlying facade hides the transport difference.
|
|
182
|
+
const vicRegs = await ctx.client.readMemoryRaw(0xD011, 8);
|
|
183
|
+
const colorRegs = await ctx.client.readMemoryRaw(0xD020, 2);
|
|
184
|
+
const cia2 = await ctx.client.readMemoryRaw(0xDD00, 1);
|
|
185
|
+
const d011 = vicRegs[0] ?? 0;
|
|
186
|
+
const d016 = vicRegs[5] ?? 0; // $D016 = $D011 + 5
|
|
187
|
+
const d018 = vicRegs[7] ?? 0; // $D018 = $D011 + 7
|
|
188
|
+
const d020 = colorRegs[0] ?? 0;
|
|
189
|
+
const d021 = colorRegs[1] ?? 0;
|
|
190
|
+
const dd00 = cia2[0] ?? 0x03;
|
|
191
|
+
const backend = await ctx.client.getActiveBackendType();
|
|
192
|
+
const state = { ...parseVicState(d011, d016, d018, dd00, d020, d021), backend };
|
|
193
|
+
ctx.logger.info("Read VIC-II display state", { mode: state.mode, bank: state.bank, backend });
|
|
194
|
+
return jsonResult(state, { success: true, mode: state.mode, backend });
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error instanceof ToolError)
|
|
198
|
+
return toolErrorResult(error);
|
|
199
|
+
return unknownErrorResult(error);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
},
|
|
108
203
|
];
|
|
109
204
|
const graphicsOperationHandlers = createOperationHandlers(graphicsOperations);
|
|
110
205
|
export const graphicsModuleGroup = defineToolModule({
|
|
@@ -154,6 +249,11 @@ export const graphicsModuleGroup = defineToolModule({
|
|
|
154
249
|
description: "Convert a PNG into VIC bitmap memory and display it",
|
|
155
250
|
arguments: { op: "render_bitmap", imagePath: "./artifacts/sample.png", format: "hires" },
|
|
156
251
|
},
|
|
252
|
+
{
|
|
253
|
+
name: "Read VIC-II state",
|
|
254
|
+
description: "Decode graphics mode, memory layout, and colours from VIC-II registers (cross-platform)",
|
|
255
|
+
arguments: { op: "get_display_state" },
|
|
256
|
+
},
|
|
157
257
|
],
|
|
158
258
|
execute: createOperationDispatcher("c64_graphics", graphicsOperationHandlers),
|
|
159
259
|
},
|
|
@@ -14,6 +14,8 @@ import { extractModule } from "./extract.js";
|
|
|
14
14
|
import { streamModule } from "./stream.js";
|
|
15
15
|
import { debugModuleGroup as debugModule } from "../debug.js";
|
|
16
16
|
import { viceModuleGroup as viceModule } from "../vice.js";
|
|
17
|
+
import { inputModuleGroup as inputModule } from "./input.js";
|
|
18
|
+
import { batchModuleGroup as batchModule } from "./batch.js";
|
|
17
19
|
const modules = [
|
|
18
20
|
programModule,
|
|
19
21
|
memoryModule,
|
|
@@ -30,6 +32,8 @@ const modules = [
|
|
|
30
32
|
streamModule,
|
|
31
33
|
debugModule,
|
|
32
34
|
viceModule,
|
|
35
|
+
inputModule,
|
|
36
|
+
batchModule,
|
|
33
37
|
];
|
|
34
38
|
const toolMap = new Map();
|
|
35
39
|
for (const module of modules) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "../types.js";
|
|
2
|
-
import { memoryModule, memoryOperationHandlers as groupedMemoryHandlers } from "../memory.js";
|
|
2
|
+
import { memoryModule, memoryOperationHandlers as groupedMemoryHandlers, disassembleArgsSchema, copyMemoryArgsSchema, fillMemoryArgsSchema, searchMemoryArgsSchema, compareMemoryArgsSchema, saveMemoryArgsSchema, } from "../memory.js";
|
|
3
3
|
import { metaModule } from "../meta/index.js";
|
|
4
4
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, invokeModuleTool, } from "./utils.js";
|
|
5
5
|
const memoryDescriptorIndex = buildDescriptorIndex(memoryModule);
|
|
@@ -25,6 +25,36 @@ const memoryOperations = [
|
|
|
25
25
|
schema: extendSchemaWithOp("wait_for_text", ensureDescriptor(metaDescriptorIndex, "wait_for_screen_text").inputSchema, { description: "Poll the screen until a substring or regex appears, or timeout elapses." }),
|
|
26
26
|
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "wait_for_screen_text", rawArgs, ctx),
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
op: "disassemble",
|
|
30
|
+
schema: extendSchemaWithOp("disassemble", disassembleArgsSchema.jsonSchema, { description: "Disassemble a memory region into annotated 6502/6510 instructions, including undocumented opcodes with canonical names. Symbol annotations from `.vs` files are applied when available. Works on both C64U and VICE." }),
|
|
31
|
+
handler: groupedMemoryHandlers.disassemble,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
op: "copy_memory",
|
|
35
|
+
schema: extendSchemaWithOp("copy_memory", copyMemoryArgsSchema.jsonSchema, { description: "Copy a RAM region to another address." }),
|
|
36
|
+
handler: groupedMemoryHandlers.copy_memory,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
op: "fill_memory",
|
|
40
|
+
schema: extendSchemaWithOp("fill_memory", fillMemoryArgsSchema.jsonSchema, { description: "Fill a memory range with a repeating byte pattern." }),
|
|
41
|
+
handler: groupedMemoryHandlers.fill_memory,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
op: "search_memory",
|
|
45
|
+
schema: extendSchemaWithOp("search_memory", searchMemoryArgsSchema.jsonSchema, { description: "Search for a byte pattern within a memory range and return matching addresses." }),
|
|
46
|
+
handler: groupedMemoryHandlers.search_memory,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
op: "compare_memory",
|
|
50
|
+
schema: extendSchemaWithOp("compare_memory", compareMemoryArgsSchema.jsonSchema, { description: "Compare two memory regions byte-by-byte and report differences." }),
|
|
51
|
+
handler: groupedMemoryHandlers.compare_memory,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
op: "save_memory",
|
|
55
|
+
schema: extendSchemaWithOp("save_memory", saveMemoryArgsSchema.jsonSchema, { description: "Dump a memory range to a local file, with an optional PRG load-address header." }),
|
|
56
|
+
handler: groupedMemoryHandlers.save_memory,
|
|
57
|
+
},
|
|
28
58
|
];
|
|
29
59
|
const memoryOperationHandlers = createOperationHandlers(memoryOperations);
|
|
30
60
|
export const memoryModuleGroup = defineToolModule({
|
|
@@ -59,6 +89,36 @@ export const memoryModuleGroup = defineToolModule({
|
|
|
59
89
|
description: "Poll until the READY. prompt appears",
|
|
60
90
|
arguments: { op: "wait_for_text", pattern: "READY." },
|
|
61
91
|
},
|
|
92
|
+
{
|
|
93
|
+
name: "Disassemble entry point",
|
|
94
|
+
description: "Disassemble 20 instructions starting at $0810",
|
|
95
|
+
arguments: { op: "disassemble", address: "$0810", instructionCount: 20 },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "Copy screen RAM",
|
|
99
|
+
description: "Copy 1000 bytes of screen RAM to a scratch area",
|
|
100
|
+
arguments: { op: "copy_memory", source: "$0400", dest: "$C000", length: 1000 },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Clear screen RAM",
|
|
104
|
+
description: "Fill screen RAM with space character ($20)",
|
|
105
|
+
arguments: { op: "fill_memory", address: "$0400", length: 1000, pattern: "20" },
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "Search for RTS",
|
|
109
|
+
description: "Find RTS ($60) instructions in BASIC ROM",
|
|
110
|
+
arguments: { op: "search_memory", startAddress: "$A000", endAddress: "$BFFF", pattern: "60" },
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "Compare two buffers",
|
|
114
|
+
description: "Check whether two 256-byte regions differ",
|
|
115
|
+
arguments: { op: "compare_memory", address1: "$C000", address2: "$C100", length: 256 },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "Save PRG dump",
|
|
119
|
+
description: "Save $0801-$CFFF as a PRG file",
|
|
120
|
+
arguments: { op: "save_memory", startAddress: "$0801", endAddress: "$CFFF", filePath: "/tmp/dump.prg" },
|
|
121
|
+
},
|
|
62
122
|
],
|
|
63
123
|
execute: createOperationDispatcher("c64_memory", memoryOperationHandlers),
|
|
64
124
|
},
|
package/dist/tools/responses.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function textResult(text, metadata) {
|
|
2
|
-
|
|
2
|
+
const result = {
|
|
3
3
|
content: [
|
|
4
4
|
{
|
|
5
5
|
type: "text",
|
|
@@ -8,6 +8,19 @@ export function textResult(text, metadata) {
|
|
|
8
8
|
],
|
|
9
9
|
metadata,
|
|
10
10
|
};
|
|
11
|
+
if (metadata !== undefined) {
|
|
12
|
+
return {
|
|
13
|
+
...result,
|
|
14
|
+
structuredContent: {
|
|
15
|
+
type: "json",
|
|
16
|
+
data: {
|
|
17
|
+
message: text,
|
|
18
|
+
...metadata,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
11
24
|
}
|
|
12
25
|
export function jsonResult(data, metadata) {
|
|
13
26
|
const text = typeof data === "string"
|
package/dist/tools/schema.js
CHANGED
|
@@ -195,15 +195,14 @@ function isPlainObject(value) {
|
|
|
195
195
|
}
|
|
196
196
|
export function objectSchema(options) {
|
|
197
197
|
const required = options.required ?? [];
|
|
198
|
+
const additionalProperties = options.additionalProperties ?? false;
|
|
198
199
|
const propertyEntries = Object.entries(options.properties).map(([key, schema]) => [key, schema.jsonSchema]);
|
|
199
200
|
const jsonSchema = {
|
|
200
201
|
type: "object",
|
|
201
202
|
...(options.description ? { description: options.description } : {}),
|
|
202
203
|
properties: Object.fromEntries(propertyEntries),
|
|
203
204
|
...(required.length > 0 ? { required: required.map((key) => key) } : {}),
|
|
204
|
-
|
|
205
|
-
? { additionalProperties: options.additionalProperties }
|
|
206
|
-
: { additionalProperties: false }),
|
|
205
|
+
additionalProperties,
|
|
207
206
|
};
|
|
208
207
|
return createSchema(jsonSchema, (value, path) => {
|
|
209
208
|
ensureType(isPlainObject(value), "Expected an object", path, {
|
|
@@ -234,7 +233,7 @@ export function objectSchema(options) {
|
|
|
234
233
|
}
|
|
235
234
|
}
|
|
236
235
|
}
|
|
237
|
-
if (
|
|
236
|
+
if (!additionalProperties) {
|
|
238
237
|
for (const key of Object.keys(input)) {
|
|
239
238
|
if (!hasOwn.call(options.properties, key)) {
|
|
240
239
|
throw new ToolValidationError("Unexpected property", {
|
|
@@ -243,7 +242,7 @@ export function objectSchema(options) {
|
|
|
243
242
|
}
|
|
244
243
|
}
|
|
245
244
|
}
|
|
246
|
-
else
|
|
245
|
+
else {
|
|
247
246
|
for (const key of Object.keys(input)) {
|
|
248
247
|
if (!hasOwn.call(options.properties, key)) {
|
|
249
248
|
result[key] = input[key];
|
|
@@ -6,6 +6,7 @@ Licensed under the GNU General Public License v2.0 or later.
|
|
|
6
6
|
See <https://www.gnu.org/licenses/> for details.
|
|
7
7
|
*/
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
|
+
import { OPCODE_INVENTORY } from "./opcodeInventory.js";
|
|
9
10
|
export class AssemblyError extends Error {
|
|
10
11
|
constructor(message, location) {
|
|
11
12
|
super(`${location.file}:${location.line}: ${message}`);
|
|
@@ -20,6 +21,7 @@ var AddressingMode;
|
|
|
20
21
|
AddressingMode["ZeroPage"] = "zeroPage";
|
|
21
22
|
AddressingMode["ZeroPageX"] = "zeroPageX";
|
|
22
23
|
AddressingMode["ZeroPageY"] = "zeroPageY";
|
|
24
|
+
AddressingMode["ZeroPageS"] = "zeroPageS";
|
|
23
25
|
AddressingMode["Absolute"] = "absolute";
|
|
24
26
|
AddressingMode["AbsoluteX"] = "absoluteX";
|
|
25
27
|
AddressingMode["AbsoluteY"] = "absoluteY";
|
|
@@ -77,6 +79,9 @@ const DIRECTIVE_ALIASES = {
|
|
|
77
79
|
};
|
|
78
80
|
const RELATIVE_ONLY = new Set(["BCC", "BCS", "BEQ", "BMI", "BNE", "BPL", "BVC", "BVS"]);
|
|
79
81
|
export function assemblyToPrg(source, options) {
|
|
82
|
+
return assemblyToPrgDetailed(source, options).prg;
|
|
83
|
+
}
|
|
84
|
+
export function assemblyToPrgDetailed(source, options) {
|
|
80
85
|
const normalized = normalizeSource(source);
|
|
81
86
|
const loadAddress = options?.loadAddress ?? DEFAULT_LOAD_ADDRESS;
|
|
82
87
|
const rootFile = options?.fileName ?? "(input)";
|
|
@@ -86,7 +91,7 @@ export function assemblyToPrg(source, options) {
|
|
|
86
91
|
const assembler = new Assembler(statements, loadAddress);
|
|
87
92
|
assembler.runPass(0);
|
|
88
93
|
assembler.runPass(1);
|
|
89
|
-
return assembler.buildPrg();
|
|
94
|
+
return { prg: assembler.buildPrg(), symbols: assembler.getSymbols() };
|
|
90
95
|
}
|
|
91
96
|
function normalizeSource(input) {
|
|
92
97
|
return input.replace(/\r\n?/g, "\n");
|
|
@@ -516,7 +521,19 @@ function parseOperand(stream) {
|
|
|
516
521
|
}
|
|
517
522
|
if (first.kind === "punct" && first.value === "(") {
|
|
518
523
|
stream.consumePunct("(");
|
|
519
|
-
const expr = parseExpression(stream, new Set([")"]));
|
|
524
|
+
const expr = parseExpression(stream, new Set([",", ")"]));
|
|
525
|
+
if (stream.peek()?.kind === "punct" && stream.peek()?.value === ",") {
|
|
526
|
+
stream.consumePunct(",");
|
|
527
|
+
const register = stream.consumeSymbol("Expected X before ')'").toUpperCase();
|
|
528
|
+
if (register !== "X") {
|
|
529
|
+
throw new Error("Only X is allowed inside pre-indexed indirect operands");
|
|
530
|
+
}
|
|
531
|
+
stream.consumePunct(")", "Expected ')' to close indirect operand");
|
|
532
|
+
if (!stream.isAtEnd()) {
|
|
533
|
+
throw new Error("Unexpected tokens after indirect operand");
|
|
534
|
+
}
|
|
535
|
+
return { kind: "indirect", expr, register: "X" };
|
|
536
|
+
}
|
|
520
537
|
stream.consumePunct(")", "Expected ')' to close indirect operand");
|
|
521
538
|
if (stream.isAtEnd()) {
|
|
522
539
|
return { kind: "indirect", expr };
|
|
@@ -542,9 +559,9 @@ function parseOperand(stream) {
|
|
|
542
559
|
const next = stream.peek();
|
|
543
560
|
if (next && next.kind === "punct" && next.value === ",") {
|
|
544
561
|
stream.consumePunct(",");
|
|
545
|
-
const register = stream.consumeSymbol("Expected X or
|
|
546
|
-
if (register !== "X" && register !== "Y") {
|
|
547
|
-
throw new Error("Only X or
|
|
562
|
+
const register = stream.consumeSymbol("Expected X, Y, or S after comma").toUpperCase();
|
|
563
|
+
if (register !== "X" && register !== "Y" && register !== "S") {
|
|
564
|
+
throw new Error("Only X, Y, or S are allowed after comma");
|
|
548
565
|
}
|
|
549
566
|
return { kind: "expression", expr, register };
|
|
550
567
|
}
|
|
@@ -608,6 +625,9 @@ class SymbolTable {
|
|
|
608
625
|
}
|
|
609
626
|
return this.globals.get(label);
|
|
610
627
|
}
|
|
628
|
+
allGlobals() {
|
|
629
|
+
return this.globals;
|
|
630
|
+
}
|
|
611
631
|
ensureLocalScope(scope) {
|
|
612
632
|
if (!this.locals.has(scope)) {
|
|
613
633
|
this.locals.set(scope, new Map());
|
|
@@ -658,6 +678,9 @@ class Assembler {
|
|
|
658
678
|
header.writeUInt16LE(start, 0);
|
|
659
679
|
return Buffer.concat([header, Buffer.from(body)]);
|
|
660
680
|
}
|
|
681
|
+
getSymbols() {
|
|
682
|
+
return this.symbolTable.allGlobals();
|
|
683
|
+
}
|
|
661
684
|
processLine(line, pass) {
|
|
662
685
|
for (const label of line.labels) {
|
|
663
686
|
if (pass === 0) {
|
|
@@ -821,6 +844,7 @@ class Assembler {
|
|
|
821
844
|
case AddressingMode.ZeroPage:
|
|
822
845
|
case AddressingMode.ZeroPageX:
|
|
823
846
|
case AddressingMode.ZeroPageY:
|
|
847
|
+
case AddressingMode.ZeroPageS:
|
|
824
848
|
case AddressingMode.IndirectX:
|
|
825
849
|
case AddressingMode.IndirectY:
|
|
826
850
|
this.writeByte(info.value ?? 0, location);
|
|
@@ -1031,6 +1055,11 @@ function chooseAddressingMode(mnemonic, operand, value, pass, location) {
|
|
|
1031
1055
|
return AddressingMode.AbsoluteY;
|
|
1032
1056
|
}
|
|
1033
1057
|
}
|
|
1058
|
+
else if (register === "S") {
|
|
1059
|
+
if (available.has(AddressingMode.ZeroPageS)) {
|
|
1060
|
+
return AddressingMode.ZeroPageS;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1034
1063
|
else {
|
|
1035
1064
|
if (available.has(AddressingMode.ZeroPage) && value !== undefined && fitsByte(value)) {
|
|
1036
1065
|
return AddressingMode.ZeroPage;
|
|
@@ -1057,6 +1086,7 @@ function instructionSize(mode) {
|
|
|
1057
1086
|
case AddressingMode.ZeroPage:
|
|
1058
1087
|
case AddressingMode.ZeroPageX:
|
|
1059
1088
|
case AddressingMode.ZeroPageY:
|
|
1089
|
+
case AddressingMode.ZeroPageS:
|
|
1060
1090
|
case AddressingMode.IndirectX:
|
|
1061
1091
|
case AddressingMode.IndirectY:
|
|
1062
1092
|
case AddressingMode.Relative:
|
|
@@ -1086,6 +1116,7 @@ function normalizeOperandValue(mode, value, locationCounterOrLocation, locationM
|
|
|
1086
1116
|
case AddressingMode.ZeroPage:
|
|
1087
1117
|
case AddressingMode.ZeroPageX:
|
|
1088
1118
|
case AddressingMode.ZeroPageY:
|
|
1119
|
+
case AddressingMode.ZeroPageS:
|
|
1089
1120
|
case AddressingMode.IndirectX:
|
|
1090
1121
|
case AddressingMode.IndirectY:
|
|
1091
1122
|
if (value === undefined)
|
|
@@ -1132,168 +1163,40 @@ function fitsByte(value) {
|
|
|
1132
1163
|
function formatHex(value) {
|
|
1133
1164
|
return `$${value.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
1134
1165
|
}
|
|
1166
|
+
const OPCODE_MODE_TO_ADDRESSING_MODE = {
|
|
1167
|
+
implied: AddressingMode.Implied,
|
|
1168
|
+
accumulator: AddressingMode.Accumulator,
|
|
1169
|
+
immediate: AddressingMode.Immediate,
|
|
1170
|
+
zeroPage: AddressingMode.ZeroPage,
|
|
1171
|
+
zeroPageX: AddressingMode.ZeroPageX,
|
|
1172
|
+
zeroPageY: AddressingMode.ZeroPageY,
|
|
1173
|
+
zeroPageS: AddressingMode.ZeroPageS,
|
|
1174
|
+
absolute: AddressingMode.Absolute,
|
|
1175
|
+
absoluteX: AddressingMode.AbsoluteX,
|
|
1176
|
+
absoluteY: AddressingMode.AbsoluteY,
|
|
1177
|
+
indirect: AddressingMode.Indirect,
|
|
1178
|
+
indirectX: AddressingMode.IndirectX,
|
|
1179
|
+
indirectY: AddressingMode.IndirectY,
|
|
1180
|
+
relative: AddressingMode.Relative,
|
|
1181
|
+
};
|
|
1135
1182
|
function buildInstructionTable() {
|
|
1136
1183
|
const table = new Map();
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1184
|
+
const entryByMnemonicAndMode = new Map();
|
|
1185
|
+
for (const entry of OPCODE_INVENTORY) {
|
|
1186
|
+
const mode = OPCODE_MODE_TO_ADDRESSING_MODE[entry.mode];
|
|
1187
|
+
const key = `${entry.mnemonic}\u0000${mode}`;
|
|
1188
|
+
const existing = entryByMnemonicAndMode.get(key);
|
|
1189
|
+
// The inventory is sorted by opcode, but assembly should prefer official
|
|
1190
|
+
// encodings over undocumented aliases for duplicate mnemonic/mode pairs.
|
|
1191
|
+
if (existing && (!existing.illegal || entry.illegal)) {
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
entryByMnemonicAndMode.set(key, { opcode: entry.opcode, illegal: entry.illegal });
|
|
1195
|
+
if (!table.has(entry.mnemonic)) {
|
|
1196
|
+
table.set(entry.mnemonic, new Map());
|
|
1141
1197
|
}
|
|
1142
|
-
table.get(mnemonic).set(
|
|
1198
|
+
table.get(entry.mnemonic).set(mode, entry.opcode);
|
|
1143
1199
|
}
|
|
1144
1200
|
return table;
|
|
1145
1201
|
}
|
|
1146
|
-
const INSTRUCTION_DATA = [
|
|
1147
|
-
{ mnemonic: "ADC", mode: AddressingMode.Immediate, opcode: 0x69 },
|
|
1148
|
-
{ mnemonic: "ADC", mode: AddressingMode.ZeroPage, opcode: 0x65 },
|
|
1149
|
-
{ mnemonic: "ADC", mode: AddressingMode.ZeroPageX, opcode: 0x75 },
|
|
1150
|
-
{ mnemonic: "ADC", mode: AddressingMode.Absolute, opcode: 0x6d },
|
|
1151
|
-
{ mnemonic: "ADC", mode: AddressingMode.AbsoluteX, opcode: 0x7d },
|
|
1152
|
-
{ mnemonic: "ADC", mode: AddressingMode.AbsoluteY, opcode: 0x79 },
|
|
1153
|
-
{ mnemonic: "ADC", mode: AddressingMode.IndirectX, opcode: 0x61 },
|
|
1154
|
-
{ mnemonic: "ADC", mode: AddressingMode.IndirectY, opcode: 0x71 },
|
|
1155
|
-
{ mnemonic: "AND", mode: AddressingMode.Immediate, opcode: 0x29 },
|
|
1156
|
-
{ mnemonic: "AND", mode: AddressingMode.ZeroPage, opcode: 0x25 },
|
|
1157
|
-
{ mnemonic: "AND", mode: AddressingMode.ZeroPageX, opcode: 0x35 },
|
|
1158
|
-
{ mnemonic: "AND", mode: AddressingMode.Absolute, opcode: 0x2d },
|
|
1159
|
-
{ mnemonic: "AND", mode: AddressingMode.AbsoluteX, opcode: 0x3d },
|
|
1160
|
-
{ mnemonic: "AND", mode: AddressingMode.AbsoluteY, opcode: 0x39 },
|
|
1161
|
-
{ mnemonic: "AND", mode: AddressingMode.IndirectX, opcode: 0x21 },
|
|
1162
|
-
{ mnemonic: "AND", mode: AddressingMode.IndirectY, opcode: 0x31 },
|
|
1163
|
-
{ mnemonic: "ASL", mode: AddressingMode.Accumulator, opcode: 0x0a },
|
|
1164
|
-
{ mnemonic: "ASL", mode: AddressingMode.ZeroPage, opcode: 0x06 },
|
|
1165
|
-
{ mnemonic: "ASL", mode: AddressingMode.ZeroPageX, opcode: 0x16 },
|
|
1166
|
-
{ mnemonic: "ASL", mode: AddressingMode.Absolute, opcode: 0x0e },
|
|
1167
|
-
{ mnemonic: "ASL", mode: AddressingMode.AbsoluteX, opcode: 0x1e },
|
|
1168
|
-
{ mnemonic: "BIT", mode: AddressingMode.ZeroPage, opcode: 0x24 },
|
|
1169
|
-
{ mnemonic: "BIT", mode: AddressingMode.Absolute, opcode: 0x2c },
|
|
1170
|
-
{ mnemonic: "BPL", mode: AddressingMode.Relative, opcode: 0x10 },
|
|
1171
|
-
{ mnemonic: "BMI", mode: AddressingMode.Relative, opcode: 0x30 },
|
|
1172
|
-
{ mnemonic: "BVC", mode: AddressingMode.Relative, opcode: 0x50 },
|
|
1173
|
-
{ mnemonic: "BVS", mode: AddressingMode.Relative, opcode: 0x70 },
|
|
1174
|
-
{ mnemonic: "BCC", mode: AddressingMode.Relative, opcode: 0x90 },
|
|
1175
|
-
{ mnemonic: "BCS", mode: AddressingMode.Relative, opcode: 0xb0 },
|
|
1176
|
-
{ mnemonic: "BNE", mode: AddressingMode.Relative, opcode: 0xd0 },
|
|
1177
|
-
{ mnemonic: "BEQ", mode: AddressingMode.Relative, opcode: 0xf0 },
|
|
1178
|
-
{ mnemonic: "BRK", mode: AddressingMode.Implied, opcode: 0x00 },
|
|
1179
|
-
{ mnemonic: "CLC", mode: AddressingMode.Implied, opcode: 0x18 },
|
|
1180
|
-
{ mnemonic: "CLD", mode: AddressingMode.Implied, opcode: 0xd8 },
|
|
1181
|
-
{ mnemonic: "CLI", mode: AddressingMode.Implied, opcode: 0x58 },
|
|
1182
|
-
{ mnemonic: "CLV", mode: AddressingMode.Implied, opcode: 0xb8 },
|
|
1183
|
-
{ mnemonic: "CMP", mode: AddressingMode.Immediate, opcode: 0xc9 },
|
|
1184
|
-
{ mnemonic: "CMP", mode: AddressingMode.ZeroPage, opcode: 0xc5 },
|
|
1185
|
-
{ mnemonic: "CMP", mode: AddressingMode.ZeroPageX, opcode: 0xd5 },
|
|
1186
|
-
{ mnemonic: "CMP", mode: AddressingMode.Absolute, opcode: 0xcd },
|
|
1187
|
-
{ mnemonic: "CMP", mode: AddressingMode.AbsoluteX, opcode: 0xdd },
|
|
1188
|
-
{ mnemonic: "CMP", mode: AddressingMode.AbsoluteY, opcode: 0xd9 },
|
|
1189
|
-
{ mnemonic: "CMP", mode: AddressingMode.IndirectX, opcode: 0xc1 },
|
|
1190
|
-
{ mnemonic: "CMP", mode: AddressingMode.IndirectY, opcode: 0xd1 },
|
|
1191
|
-
{ mnemonic: "CPX", mode: AddressingMode.Immediate, opcode: 0xe0 },
|
|
1192
|
-
{ mnemonic: "CPX", mode: AddressingMode.ZeroPage, opcode: 0xe4 },
|
|
1193
|
-
{ mnemonic: "CPX", mode: AddressingMode.Absolute, opcode: 0xec },
|
|
1194
|
-
{ mnemonic: "CPY", mode: AddressingMode.Immediate, opcode: 0xc0 },
|
|
1195
|
-
{ mnemonic: "CPY", mode: AddressingMode.ZeroPage, opcode: 0xc4 },
|
|
1196
|
-
{ mnemonic: "CPY", mode: AddressingMode.Absolute, opcode: 0xcc },
|
|
1197
|
-
{ mnemonic: "DEC", mode: AddressingMode.ZeroPage, opcode: 0xc6 },
|
|
1198
|
-
{ mnemonic: "DEC", mode: AddressingMode.ZeroPageX, opcode: 0xd6 },
|
|
1199
|
-
{ mnemonic: "DEC", mode: AddressingMode.Absolute, opcode: 0xce },
|
|
1200
|
-
{ mnemonic: "DEC", mode: AddressingMode.AbsoluteX, opcode: 0xde },
|
|
1201
|
-
{ mnemonic: "DEX", mode: AddressingMode.Implied, opcode: 0xca },
|
|
1202
|
-
{ mnemonic: "DEY", mode: AddressingMode.Implied, opcode: 0x88 },
|
|
1203
|
-
{ mnemonic: "EOR", mode: AddressingMode.Immediate, opcode: 0x49 },
|
|
1204
|
-
{ mnemonic: "EOR", mode: AddressingMode.ZeroPage, opcode: 0x45 },
|
|
1205
|
-
{ mnemonic: "EOR", mode: AddressingMode.ZeroPageX, opcode: 0x55 },
|
|
1206
|
-
{ mnemonic: "EOR", mode: AddressingMode.Absolute, opcode: 0x4d },
|
|
1207
|
-
{ mnemonic: "EOR", mode: AddressingMode.AbsoluteX, opcode: 0x5d },
|
|
1208
|
-
{ mnemonic: "EOR", mode: AddressingMode.AbsoluteY, opcode: 0x59 },
|
|
1209
|
-
{ mnemonic: "EOR", mode: AddressingMode.IndirectX, opcode: 0x41 },
|
|
1210
|
-
{ mnemonic: "EOR", mode: AddressingMode.IndirectY, opcode: 0x51 },
|
|
1211
|
-
{ mnemonic: "INC", mode: AddressingMode.ZeroPage, opcode: 0xe6 },
|
|
1212
|
-
{ mnemonic: "INC", mode: AddressingMode.ZeroPageX, opcode: 0xf6 },
|
|
1213
|
-
{ mnemonic: "INC", mode: AddressingMode.Absolute, opcode: 0xee },
|
|
1214
|
-
{ mnemonic: "INC", mode: AddressingMode.AbsoluteX, opcode: 0xfe },
|
|
1215
|
-
{ mnemonic: "INX", mode: AddressingMode.Implied, opcode: 0xe8 },
|
|
1216
|
-
{ mnemonic: "INY", mode: AddressingMode.Implied, opcode: 0xc8 },
|
|
1217
|
-
{ mnemonic: "JMP", mode: AddressingMode.Absolute, opcode: 0x4c },
|
|
1218
|
-
{ mnemonic: "JMP", mode: AddressingMode.Indirect, opcode: 0x6c },
|
|
1219
|
-
{ mnemonic: "JSR", mode: AddressingMode.Absolute, opcode: 0x20 },
|
|
1220
|
-
{ mnemonic: "LDA", mode: AddressingMode.Immediate, opcode: 0xa9 },
|
|
1221
|
-
{ mnemonic: "LDA", mode: AddressingMode.ZeroPage, opcode: 0xa5 },
|
|
1222
|
-
{ mnemonic: "LDA", mode: AddressingMode.ZeroPageX, opcode: 0xb5 },
|
|
1223
|
-
{ mnemonic: "LDA", mode: AddressingMode.Absolute, opcode: 0xad },
|
|
1224
|
-
{ mnemonic: "LDA", mode: AddressingMode.AbsoluteX, opcode: 0xbd },
|
|
1225
|
-
{ mnemonic: "LDA", mode: AddressingMode.AbsoluteY, opcode: 0xb9 },
|
|
1226
|
-
{ mnemonic: "LDA", mode: AddressingMode.IndirectX, opcode: 0xa1 },
|
|
1227
|
-
{ mnemonic: "LDA", mode: AddressingMode.IndirectY, opcode: 0xb1 },
|
|
1228
|
-
{ mnemonic: "LDX", mode: AddressingMode.Immediate, opcode: 0xa2 },
|
|
1229
|
-
{ mnemonic: "LDX", mode: AddressingMode.ZeroPage, opcode: 0xa6 },
|
|
1230
|
-
{ mnemonic: "LDX", mode: AddressingMode.ZeroPageY, opcode: 0xb6 },
|
|
1231
|
-
{ mnemonic: "LDX", mode: AddressingMode.Absolute, opcode: 0xae },
|
|
1232
|
-
{ mnemonic: "LDX", mode: AddressingMode.AbsoluteY, opcode: 0xbe },
|
|
1233
|
-
{ mnemonic: "LDY", mode: AddressingMode.Immediate, opcode: 0xa0 },
|
|
1234
|
-
{ mnemonic: "LDY", mode: AddressingMode.ZeroPage, opcode: 0xa4 },
|
|
1235
|
-
{ mnemonic: "LDY", mode: AddressingMode.ZeroPageX, opcode: 0xb4 },
|
|
1236
|
-
{ mnemonic: "LDY", mode: AddressingMode.Absolute, opcode: 0xac },
|
|
1237
|
-
{ mnemonic: "LDY", mode: AddressingMode.AbsoluteX, opcode: 0xbc },
|
|
1238
|
-
{ mnemonic: "LSR", mode: AddressingMode.Accumulator, opcode: 0x4a },
|
|
1239
|
-
{ mnemonic: "LSR", mode: AddressingMode.ZeroPage, opcode: 0x46 },
|
|
1240
|
-
{ mnemonic: "LSR", mode: AddressingMode.ZeroPageX, opcode: 0x56 },
|
|
1241
|
-
{ mnemonic: "LSR", mode: AddressingMode.Absolute, opcode: 0x4e },
|
|
1242
|
-
{ mnemonic: "LSR", mode: AddressingMode.AbsoluteX, opcode: 0x5e },
|
|
1243
|
-
{ mnemonic: "NOP", mode: AddressingMode.Implied, opcode: 0xea },
|
|
1244
|
-
{ mnemonic: "ORA", mode: AddressingMode.Immediate, opcode: 0x09 },
|
|
1245
|
-
{ mnemonic: "ORA", mode: AddressingMode.ZeroPage, opcode: 0x05 },
|
|
1246
|
-
{ mnemonic: "ORA", mode: AddressingMode.ZeroPageX, opcode: 0x15 },
|
|
1247
|
-
{ mnemonic: "ORA", mode: AddressingMode.Absolute, opcode: 0x0d },
|
|
1248
|
-
{ mnemonic: "ORA", mode: AddressingMode.AbsoluteX, opcode: 0x1d },
|
|
1249
|
-
{ mnemonic: "ORA", mode: AddressingMode.AbsoluteY, opcode: 0x19 },
|
|
1250
|
-
{ mnemonic: "ORA", mode: AddressingMode.IndirectX, opcode: 0x01 },
|
|
1251
|
-
{ mnemonic: "ORA", mode: AddressingMode.IndirectY, opcode: 0x11 },
|
|
1252
|
-
{ mnemonic: "PHA", mode: AddressingMode.Implied, opcode: 0x48 },
|
|
1253
|
-
{ mnemonic: "PHP", mode: AddressingMode.Implied, opcode: 0x08 },
|
|
1254
|
-
{ mnemonic: "PLA", mode: AddressingMode.Implied, opcode: 0x68 },
|
|
1255
|
-
{ mnemonic: "PLP", mode: AddressingMode.Implied, opcode: 0x28 },
|
|
1256
|
-
{ mnemonic: "ROL", mode: AddressingMode.Accumulator, opcode: 0x2a },
|
|
1257
|
-
{ mnemonic: "ROL", mode: AddressingMode.ZeroPage, opcode: 0x26 },
|
|
1258
|
-
{ mnemonic: "ROL", mode: AddressingMode.ZeroPageX, opcode: 0x36 },
|
|
1259
|
-
{ mnemonic: "ROL", mode: AddressingMode.Absolute, opcode: 0x2e },
|
|
1260
|
-
{ mnemonic: "ROL", mode: AddressingMode.AbsoluteX, opcode: 0x3e },
|
|
1261
|
-
{ mnemonic: "ROR", mode: AddressingMode.Accumulator, opcode: 0x6a },
|
|
1262
|
-
{ mnemonic: "ROR", mode: AddressingMode.ZeroPage, opcode: 0x66 },
|
|
1263
|
-
{ mnemonic: "ROR", mode: AddressingMode.ZeroPageX, opcode: 0x76 },
|
|
1264
|
-
{ mnemonic: "ROR", mode: AddressingMode.Absolute, opcode: 0x6e },
|
|
1265
|
-
{ mnemonic: "ROR", mode: AddressingMode.AbsoluteX, opcode: 0x7e },
|
|
1266
|
-
{ mnemonic: "RTI", mode: AddressingMode.Implied, opcode: 0x40 },
|
|
1267
|
-
{ mnemonic: "RTS", mode: AddressingMode.Implied, opcode: 0x60 },
|
|
1268
|
-
{ mnemonic: "SBC", mode: AddressingMode.Immediate, opcode: 0xe9 },
|
|
1269
|
-
{ mnemonic: "SBC", mode: AddressingMode.ZeroPage, opcode: 0xe5 },
|
|
1270
|
-
{ mnemonic: "SBC", mode: AddressingMode.ZeroPageX, opcode: 0xf5 },
|
|
1271
|
-
{ mnemonic: "SBC", mode: AddressingMode.Absolute, opcode: 0xed },
|
|
1272
|
-
{ mnemonic: "SBC", mode: AddressingMode.AbsoluteX, opcode: 0xfd },
|
|
1273
|
-
{ mnemonic: "SBC", mode: AddressingMode.AbsoluteY, opcode: 0xf9 },
|
|
1274
|
-
{ mnemonic: "SBC", mode: AddressingMode.IndirectX, opcode: 0xe1 },
|
|
1275
|
-
{ mnemonic: "SBC", mode: AddressingMode.IndirectY, opcode: 0xf1 },
|
|
1276
|
-
{ mnemonic: "SEC", mode: AddressingMode.Implied, opcode: 0x38 },
|
|
1277
|
-
{ mnemonic: "SED", mode: AddressingMode.Implied, opcode: 0xf8 },
|
|
1278
|
-
{ mnemonic: "SEI", mode: AddressingMode.Implied, opcode: 0x78 },
|
|
1279
|
-
{ mnemonic: "STA", mode: AddressingMode.ZeroPage, opcode: 0x85 },
|
|
1280
|
-
{ mnemonic: "STA", mode: AddressingMode.ZeroPageX, opcode: 0x95 },
|
|
1281
|
-
{ mnemonic: "STA", mode: AddressingMode.Absolute, opcode: 0x8d },
|
|
1282
|
-
{ mnemonic: "STA", mode: AddressingMode.AbsoluteX, opcode: 0x9d },
|
|
1283
|
-
{ mnemonic: "STA", mode: AddressingMode.AbsoluteY, opcode: 0x99 },
|
|
1284
|
-
{ mnemonic: "STA", mode: AddressingMode.IndirectX, opcode: 0x81 },
|
|
1285
|
-
{ mnemonic: "STA", mode: AddressingMode.IndirectY, opcode: 0x91 },
|
|
1286
|
-
{ mnemonic: "STX", mode: AddressingMode.ZeroPage, opcode: 0x86 },
|
|
1287
|
-
{ mnemonic: "STX", mode: AddressingMode.ZeroPageY, opcode: 0x96 },
|
|
1288
|
-
{ mnemonic: "STX", mode: AddressingMode.Absolute, opcode: 0x8e },
|
|
1289
|
-
{ mnemonic: "STY", mode: AddressingMode.ZeroPage, opcode: 0x84 },
|
|
1290
|
-
{ mnemonic: "STY", mode: AddressingMode.ZeroPageX, opcode: 0x94 },
|
|
1291
|
-
{ mnemonic: "STY", mode: AddressingMode.Absolute, opcode: 0x8c },
|
|
1292
|
-
{ mnemonic: "TAX", mode: AddressingMode.Implied, opcode: 0xaa },
|
|
1293
|
-
{ mnemonic: "TAY", mode: AddressingMode.Implied, opcode: 0xa8 },
|
|
1294
|
-
{ mnemonic: "TSX", mode: AddressingMode.Implied, opcode: 0xba },
|
|
1295
|
-
{ mnemonic: "TXA", mode: AddressingMode.Implied, opcode: 0x8a },
|
|
1296
|
-
{ mnemonic: "TXS", mode: AddressingMode.Implied, opcode: 0x9a },
|
|
1297
|
-
{ mnemonic: "TYA", mode: AddressingMode.Implied, opcode: 0x98 },
|
|
1298
|
-
];
|
|
1299
1202
|
const INSTRUCTION_TABLE = buildInstructionTable();
|
|
@@ -6,7 +6,7 @@ Licensed under the GNU General Public License v2.0 or later.
|
|
|
6
6
|
See <https://www.gnu.org/licenses/> for details.
|
|
7
7
|
*/
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
|
-
import { asciiToPetscii, encodeStringWithNames } from "
|
|
9
|
+
import { asciiToPetscii, encodeStringWithNames } from "../../petscii.js";
|
|
10
10
|
const DEFAULT_START_ADDRESS = 0x0801;
|
|
11
11
|
const REM_TOKEN = 0x8f;
|
|
12
12
|
const TOKEN_TABLE = buildTokenTable();
|