c64bridge 0.9.1 → 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/dist/c64Client.js CHANGED
@@ -8,8 +8,8 @@ See <https://www.gnu.org/licenses/> for details.
8
8
  import { Buffer } from "node:buffer";
9
9
  import { createSocket } from "node:dgram";
10
10
  import axios from "axios";
11
- import { basicToPrg } from "./tools/basicTokenizer.js";
12
- import { assemblyToPrg } from "./tools/assember.js";
11
+ import { basicToPrg } from "./tools/translation/basicTokenizer.js";
12
+ import { assemblyToPrg } from "./tools/translation/assembler.js";
13
13
  import { screenCodesToAscii } from "./petscii.js";
14
14
  import { resolveAddressSymbol } from "./knowledge.js";
15
15
  import { createAllFacades, createFacade } from "./device.js";
@@ -28,6 +28,17 @@ const DEFAULT_TEXT_FOREGROUND = 1;
28
28
  const DEFAULT_BORDER_COLOR = 6;
29
29
  const DEFAULT_BACKGROUND_COLOR = 0;
30
30
  const SPACE_SCREEN_CODE = 0x20;
31
+ function validateMemoryRange(address, length) {
32
+ if (!Number.isInteger(address) || address < 0 || address > 0xffff) {
33
+ throw new Error("Address must be within 0x0000 - 0xFFFF");
34
+ }
35
+ if (!Number.isInteger(length) || length <= 0) {
36
+ throw new Error("Length must be greater than zero");
37
+ }
38
+ if (address + length - 1 > 0xffff) {
39
+ throw new Error("Memory range must stay within 0x0000 - 0xFFFF");
40
+ }
41
+ }
31
42
  export class C64Client {
32
43
  constructor(baseUrl, options = {}) {
33
44
  this.allFacades = new Map();
@@ -481,9 +492,7 @@ export class C64Client {
481
492
  const resolved = resolveAddressSymbol(addressInput);
482
493
  const address = resolved ?? this.parseNumeric(addressInput);
483
494
  const length = this.parseNumeric(lengthInput);
484
- if (length <= 0) {
485
- throw new Error("Length must be greater than zero");
486
- }
495
+ validateMemoryRange(address, length);
487
496
  const rawBytes = await this.readMemoryRaw(address, length);
488
497
  const bytes = rawBytes.slice(0, length);
489
498
  return {
@@ -507,9 +516,7 @@ export class C64Client {
507
516
  const resolved = resolveAddressSymbol(addressInput);
508
517
  const address = resolved ?? this.parseNumeric(addressInput);
509
518
  const dataBuffer = this.hexStringToBuffer(bytesInput);
510
- if (dataBuffer.length === 0) {
511
- throw new Error("No bytes provided");
512
- }
519
+ validateMemoryRange(address, dataBuffer.length);
513
520
  // Prefer PUT with hex data for up to 128 bytes; fall back to POST binary for larger writes
514
521
  const addrStr = this.formatAddress(address);
515
522
  try {
@@ -1467,6 +1474,7 @@ export class C64Client {
1467
1474
  * Public to allow advanced polling and monitoring use cases.
1468
1475
  */
1469
1476
  async readMemoryRaw(address, length) {
1477
+ validateMemoryRange(address, length);
1470
1478
  try {
1471
1479
  const facade = await this.facadePromise;
1472
1480
  return await facade.readMemory(address, length);
@@ -1498,8 +1506,12 @@ export class C64Client {
1498
1506
  * keyboard-buffer injection) can avoid backend-specific paths.
1499
1507
  */
1500
1508
  async writeMemoryRaw(address, bytes) {
1509
+ if (!Number.isInteger(address) || address < 0 || address > 0xffff) {
1510
+ throw new Error("Address must be within 0x0000 - 0xFFFF");
1511
+ }
1501
1512
  const facade = await this.facadePromise;
1502
1513
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
1514
+ validateMemoryRange(address, buf.length);
1503
1515
  await facade.writeMemory(address, buf);
1504
1516
  }
1505
1517
  /**
@@ -1695,17 +1707,27 @@ export class C64Client {
1695
1707
  const lower = trimmed.toLowerCase();
1696
1708
  let radix = 10;
1697
1709
  let literal = lower;
1710
+ let pattern;
1698
1711
  if (lower.startsWith("$")) {
1699
1712
  radix = 16;
1700
1713
  literal = lower.slice(1);
1714
+ pattern = /^[0-9a-f]+$/;
1701
1715
  }
1702
1716
  else if (lower.startsWith("0x")) {
1703
1717
  radix = 16;
1704
1718
  literal = lower.slice(2);
1719
+ pattern = /^[0-9a-f]+$/;
1705
1720
  }
1706
1721
  else if (lower.startsWith("%")) {
1707
1722
  radix = 2;
1708
1723
  literal = lower.slice(1);
1724
+ pattern = /^[01]+$/;
1725
+ }
1726
+ else {
1727
+ pattern = /^[0-9]+$/;
1728
+ }
1729
+ if (!pattern.test(literal)) {
1730
+ throw new Error(`Unable to parse numeric value "${value}"`);
1709
1731
  }
1710
1732
  const parsed = Number.parseInt(literal, radix);
1711
1733
  if (!Number.isFinite(parsed) || Number.isNaN(parsed) || parsed < 0) {
@@ -1739,6 +1761,9 @@ export class C64Client {
1739
1761
  if (cleaned.length % 2 !== 0) {
1740
1762
  throw new Error("Hex string must contain an even number of characters");
1741
1763
  }
1764
+ if (!/^[0-9a-f]+$/.test(cleaned)) {
1765
+ throw new Error("Hex string contains non-hexadecimal characters");
1766
+ }
1742
1767
  return Buffer.from(cleaned, "hex");
1743
1768
  }
1744
1769
  hzToSidFrequency(hz, system = "PAL") {
@@ -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 { assemblyToPrg } from "./tools/assember.js";
9
+ import { assemblyToPrg } from "./tools/translation/assembler.js";
10
10
  import { midiToHz, noteNameToMidi } from "./sidwave.js";
11
11
  /**
12
12
  * Small SIDWAVE to PRG compiler: generates a 50/60Hz player that writes SID registers from a precomputed frame table.
@@ -1,4 +1,4 @@
1
- import { createOperationDispatcher, defineToolModule, } from "./types.js";
1
+ import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "./types.js";
2
2
  import { arraySchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "./schema.js";
3
3
  import { textResult } from "./responses.js";
4
4
  import { ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
@@ -285,7 +285,7 @@ export const inputModule = defineToolModule({
285
285
  domain: "input",
286
286
  summary: "Cross-platform keyboard input plus VICE-only joystick simulation.",
287
287
  supportedPlatforms: ["c64u", "vice"],
288
- resources: ["c64://specs/assembly", "c64://specs/memory-map"],
288
+ resources: ["c64://guide/bootstrap", "c64://memory/map", "c64://io/cia/spec"],
289
289
  prompts: [],
290
290
  defaultTags: ["input"],
291
291
  workflowHints: [
@@ -298,16 +298,14 @@ export const inputModule = defineToolModule({
298
298
  name: "c64_input",
299
299
  description: "Keyboard buffer injection (cross-platform) and joystick simulation (VICE only).",
300
300
  summary: "Types text, taps keys, and simulates joystick movements.",
301
- inputSchema: {
302
- type: "object",
301
+ inputSchema: discriminatedUnionSchema({
303
302
  description: "Input operations: write_text, key, joystick.",
304
- oneOf: [
303
+ variants: [
305
304
  writeTextArgsSchema.jsonSchema,
306
305
  keyArgsSchema.jsonSchema,
307
306
  joystickArgsSchema.jsonSchema,
308
307
  ],
309
- discriminator: { propertyName: "op" },
310
- },
308
+ }),
311
309
  operationPlatforms: { joystick: ["vice"] },
312
310
  tags: ["input", "keyboard", "joystick"],
313
311
  examples: [
@@ -5,8 +5,8 @@ import { defineToolModule, OPERATION_DISCRIMINATOR, } from "./types.js";
5
5
  import { booleanSchema, numberSchema, objectSchema, optionalSchema, stringSchema } from "./schema.js";
6
6
  import { jsonResult, textResult } from "./responses.js";
7
7
  import { ToolError, ToolExecutionError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
8
- import { disassemble, formatDisassembly } from "./disassembler.js";
9
- import { getViceSymbols } from "./symbolRegistry.js";
8
+ import { disassemble, formatDisassembly } from "./translation/disassembler.js";
9
+ import { getViceSymbols } from "./translation/symbolRegistry.js";
10
10
  import { resolveAddressSymbol } from "../knowledge.js";
11
11
  function toRecord(details) {
12
12
  if (details && typeof details === "object") {
@@ -39,6 +39,12 @@ function parseHexInternal(value, path) {
39
39
  if (cleaned.length === 0) {
40
40
  return { bytes: new Uint8Array(), canonical: "$" };
41
41
  }
42
+ if (!/^[0-9A-F]+$/.test(cleaned)) {
43
+ throw new ToolValidationError("Hex string contains non-hexadecimal characters", {
44
+ path,
45
+ details: { value },
46
+ });
47
+ }
42
48
  const bytes = Uint8Array.from(Buffer.from(cleaned, "hex"));
43
49
  return { bytes, canonical: `$${cleaned}` };
44
50
  }
@@ -399,6 +405,7 @@ async function executeDisassemble(rawArgs, ctx) {
399
405
  const length = parsed.length ?? 64;
400
406
  ctx.logger.info("Disassembling VICE memory", { address: parsed.address, length });
401
407
  const address = parseAddressArg(parsed.address, "$.address");
408
+ validateRangeWithinAddressSpace(address, length, "$.address");
402
409
  const bytes = await ctx.client.readMemoryRaw(address, length);
403
410
  const symbols = ctx.platform?.id === "vice" ? getViceSymbols() : undefined;
404
411
  const lines = disassemble(bytes, address, parsed.instructionCount, symbols);
@@ -597,6 +604,8 @@ async function executeCompareMemory(rawArgs, ctx) {
597
604
  const a2 = parseAddressArg(parsed.address2, "$.address2");
598
605
  const len = parsed.length;
599
606
  const maxDiffs = parsed.maxDiffs ?? 10;
607
+ validateRangeWithinAddressSpace(a1, len, "$.address1");
608
+ validateRangeWithinAddressSpace(a2, len, "$.address2");
600
609
  ctx.logger.info("Comparing memory regions", { a1: fmtAddr(a1), a2: fmtAddr(a2), len });
601
610
  const [r1, r2] = await Promise.all([
602
611
  ctx.client.readMemoryRaw(a1, len),
@@ -1,12 +1,12 @@
1
1
  import fs from "node:fs";
2
- import { assemblyToPrgDetailed, AssemblyError } from "./assember.js";
3
- import { basicToPrg } from "./basicTokenizer.js";
2
+ import { assemblyToPrgDetailed, AssemblyError } from "./translation/assembler.js";
3
+ import { basicToPrg } from "./translation/basicTokenizer.js";
4
4
  import { defineToolModule, OPERATION_DISCRIMINATOR, } from "./types.js";
5
5
  import { booleanSchema, objectSchema, optionalSchema, stringSchema } from "./schema.js";
6
6
  import { textResult } from "./responses.js";
7
7
  import { ToolExecutionError, ToolError, ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
8
8
  import { pollForProgramOutcome } from "./pollValidator.js";
9
- import { clearViceSymbols, setViceSymbols, parseViceSymbolFile } from "./symbolRegistry.js";
9
+ import { clearViceSymbols, setViceSymbols, parseViceSymbolFile } from "./translation/symbolRegistry.js";
10
10
  function extractFailureDetails(details) {
11
11
  if (details === undefined || details === null) {
12
12
  return undefined;
@@ -1,5 +1,5 @@
1
1
  export function textResult(text, metadata) {
2
- return {
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"
@@ -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";
@@ -519,7 +521,19 @@ function parseOperand(stream) {
519
521
  }
520
522
  if (first.kind === "punct" && first.value === "(") {
521
523
  stream.consumePunct("(");
522
- 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
+ }
523
537
  stream.consumePunct(")", "Expected ')' to close indirect operand");
524
538
  if (stream.isAtEnd()) {
525
539
  return { kind: "indirect", expr };
@@ -545,9 +559,9 @@ function parseOperand(stream) {
545
559
  const next = stream.peek();
546
560
  if (next && next.kind === "punct" && next.value === ",") {
547
561
  stream.consumePunct(",");
548
- const register = stream.consumeSymbol("Expected X or Y after comma").toUpperCase();
549
- if (register !== "X" && register !== "Y") {
550
- throw new Error("Only X or Y are allowed after comma");
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");
551
565
  }
552
566
  return { kind: "expression", expr, register };
553
567
  }
@@ -830,6 +844,7 @@ class Assembler {
830
844
  case AddressingMode.ZeroPage:
831
845
  case AddressingMode.ZeroPageX:
832
846
  case AddressingMode.ZeroPageY:
847
+ case AddressingMode.ZeroPageS:
833
848
  case AddressingMode.IndirectX:
834
849
  case AddressingMode.IndirectY:
835
850
  this.writeByte(info.value ?? 0, location);
@@ -1040,6 +1055,11 @@ function chooseAddressingMode(mnemonic, operand, value, pass, location) {
1040
1055
  return AddressingMode.AbsoluteY;
1041
1056
  }
1042
1057
  }
1058
+ else if (register === "S") {
1059
+ if (available.has(AddressingMode.ZeroPageS)) {
1060
+ return AddressingMode.ZeroPageS;
1061
+ }
1062
+ }
1043
1063
  else {
1044
1064
  if (available.has(AddressingMode.ZeroPage) && value !== undefined && fitsByte(value)) {
1045
1065
  return AddressingMode.ZeroPage;
@@ -1066,6 +1086,7 @@ function instructionSize(mode) {
1066
1086
  case AddressingMode.ZeroPage:
1067
1087
  case AddressingMode.ZeroPageX:
1068
1088
  case AddressingMode.ZeroPageY:
1089
+ case AddressingMode.ZeroPageS:
1069
1090
  case AddressingMode.IndirectX:
1070
1091
  case AddressingMode.IndirectY:
1071
1092
  case AddressingMode.Relative:
@@ -1095,6 +1116,7 @@ function normalizeOperandValue(mode, value, locationCounterOrLocation, locationM
1095
1116
  case AddressingMode.ZeroPage:
1096
1117
  case AddressingMode.ZeroPageX:
1097
1118
  case AddressingMode.ZeroPageY:
1119
+ case AddressingMode.ZeroPageS:
1098
1120
  case AddressingMode.IndirectX:
1099
1121
  case AddressingMode.IndirectY:
1100
1122
  if (value === undefined)
@@ -1141,168 +1163,40 @@ function fitsByte(value) {
1141
1163
  function formatHex(value) {
1142
1164
  return `$${value.toString(16).toUpperCase().padStart(4, "0")}`;
1143
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
+ };
1144
1182
  function buildInstructionTable() {
1145
1183
  const table = new Map();
1146
- for (const entry of INSTRUCTION_DATA) {
1147
- const mnemonic = entry.mnemonic;
1148
- if (!table.has(mnemonic)) {
1149
- table.set(mnemonic, new Map());
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());
1150
1197
  }
1151
- table.get(mnemonic).set(entry.mode, entry.opcode);
1198
+ table.get(entry.mnemonic).set(mode, entry.opcode);
1152
1199
  }
1153
1200
  return table;
1154
1201
  }
1155
- const INSTRUCTION_DATA = [
1156
- { mnemonic: "ADC", mode: AddressingMode.Immediate, opcode: 0x69 },
1157
- { mnemonic: "ADC", mode: AddressingMode.ZeroPage, opcode: 0x65 },
1158
- { mnemonic: "ADC", mode: AddressingMode.ZeroPageX, opcode: 0x75 },
1159
- { mnemonic: "ADC", mode: AddressingMode.Absolute, opcode: 0x6d },
1160
- { mnemonic: "ADC", mode: AddressingMode.AbsoluteX, opcode: 0x7d },
1161
- { mnemonic: "ADC", mode: AddressingMode.AbsoluteY, opcode: 0x79 },
1162
- { mnemonic: "ADC", mode: AddressingMode.IndirectX, opcode: 0x61 },
1163
- { mnemonic: "ADC", mode: AddressingMode.IndirectY, opcode: 0x71 },
1164
- { mnemonic: "AND", mode: AddressingMode.Immediate, opcode: 0x29 },
1165
- { mnemonic: "AND", mode: AddressingMode.ZeroPage, opcode: 0x25 },
1166
- { mnemonic: "AND", mode: AddressingMode.ZeroPageX, opcode: 0x35 },
1167
- { mnemonic: "AND", mode: AddressingMode.Absolute, opcode: 0x2d },
1168
- { mnemonic: "AND", mode: AddressingMode.AbsoluteX, opcode: 0x3d },
1169
- { mnemonic: "AND", mode: AddressingMode.AbsoluteY, opcode: 0x39 },
1170
- { mnemonic: "AND", mode: AddressingMode.IndirectX, opcode: 0x21 },
1171
- { mnemonic: "AND", mode: AddressingMode.IndirectY, opcode: 0x31 },
1172
- { mnemonic: "ASL", mode: AddressingMode.Accumulator, opcode: 0x0a },
1173
- { mnemonic: "ASL", mode: AddressingMode.ZeroPage, opcode: 0x06 },
1174
- { mnemonic: "ASL", mode: AddressingMode.ZeroPageX, opcode: 0x16 },
1175
- { mnemonic: "ASL", mode: AddressingMode.Absolute, opcode: 0x0e },
1176
- { mnemonic: "ASL", mode: AddressingMode.AbsoluteX, opcode: 0x1e },
1177
- { mnemonic: "BIT", mode: AddressingMode.ZeroPage, opcode: 0x24 },
1178
- { mnemonic: "BIT", mode: AddressingMode.Absolute, opcode: 0x2c },
1179
- { mnemonic: "BPL", mode: AddressingMode.Relative, opcode: 0x10 },
1180
- { mnemonic: "BMI", mode: AddressingMode.Relative, opcode: 0x30 },
1181
- { mnemonic: "BVC", mode: AddressingMode.Relative, opcode: 0x50 },
1182
- { mnemonic: "BVS", mode: AddressingMode.Relative, opcode: 0x70 },
1183
- { mnemonic: "BCC", mode: AddressingMode.Relative, opcode: 0x90 },
1184
- { mnemonic: "BCS", mode: AddressingMode.Relative, opcode: 0xb0 },
1185
- { mnemonic: "BNE", mode: AddressingMode.Relative, opcode: 0xd0 },
1186
- { mnemonic: "BEQ", mode: AddressingMode.Relative, opcode: 0xf0 },
1187
- { mnemonic: "BRK", mode: AddressingMode.Implied, opcode: 0x00 },
1188
- { mnemonic: "CLC", mode: AddressingMode.Implied, opcode: 0x18 },
1189
- { mnemonic: "CLD", mode: AddressingMode.Implied, opcode: 0xd8 },
1190
- { mnemonic: "CLI", mode: AddressingMode.Implied, opcode: 0x58 },
1191
- { mnemonic: "CLV", mode: AddressingMode.Implied, opcode: 0xb8 },
1192
- { mnemonic: "CMP", mode: AddressingMode.Immediate, opcode: 0xc9 },
1193
- { mnemonic: "CMP", mode: AddressingMode.ZeroPage, opcode: 0xc5 },
1194
- { mnemonic: "CMP", mode: AddressingMode.ZeroPageX, opcode: 0xd5 },
1195
- { mnemonic: "CMP", mode: AddressingMode.Absolute, opcode: 0xcd },
1196
- { mnemonic: "CMP", mode: AddressingMode.AbsoluteX, opcode: 0xdd },
1197
- { mnemonic: "CMP", mode: AddressingMode.AbsoluteY, opcode: 0xd9 },
1198
- { mnemonic: "CMP", mode: AddressingMode.IndirectX, opcode: 0xc1 },
1199
- { mnemonic: "CMP", mode: AddressingMode.IndirectY, opcode: 0xd1 },
1200
- { mnemonic: "CPX", mode: AddressingMode.Immediate, opcode: 0xe0 },
1201
- { mnemonic: "CPX", mode: AddressingMode.ZeroPage, opcode: 0xe4 },
1202
- { mnemonic: "CPX", mode: AddressingMode.Absolute, opcode: 0xec },
1203
- { mnemonic: "CPY", mode: AddressingMode.Immediate, opcode: 0xc0 },
1204
- { mnemonic: "CPY", mode: AddressingMode.ZeroPage, opcode: 0xc4 },
1205
- { mnemonic: "CPY", mode: AddressingMode.Absolute, opcode: 0xcc },
1206
- { mnemonic: "DEC", mode: AddressingMode.ZeroPage, opcode: 0xc6 },
1207
- { mnemonic: "DEC", mode: AddressingMode.ZeroPageX, opcode: 0xd6 },
1208
- { mnemonic: "DEC", mode: AddressingMode.Absolute, opcode: 0xce },
1209
- { mnemonic: "DEC", mode: AddressingMode.AbsoluteX, opcode: 0xde },
1210
- { mnemonic: "DEX", mode: AddressingMode.Implied, opcode: 0xca },
1211
- { mnemonic: "DEY", mode: AddressingMode.Implied, opcode: 0x88 },
1212
- { mnemonic: "EOR", mode: AddressingMode.Immediate, opcode: 0x49 },
1213
- { mnemonic: "EOR", mode: AddressingMode.ZeroPage, opcode: 0x45 },
1214
- { mnemonic: "EOR", mode: AddressingMode.ZeroPageX, opcode: 0x55 },
1215
- { mnemonic: "EOR", mode: AddressingMode.Absolute, opcode: 0x4d },
1216
- { mnemonic: "EOR", mode: AddressingMode.AbsoluteX, opcode: 0x5d },
1217
- { mnemonic: "EOR", mode: AddressingMode.AbsoluteY, opcode: 0x59 },
1218
- { mnemonic: "EOR", mode: AddressingMode.IndirectX, opcode: 0x41 },
1219
- { mnemonic: "EOR", mode: AddressingMode.IndirectY, opcode: 0x51 },
1220
- { mnemonic: "INC", mode: AddressingMode.ZeroPage, opcode: 0xe6 },
1221
- { mnemonic: "INC", mode: AddressingMode.ZeroPageX, opcode: 0xf6 },
1222
- { mnemonic: "INC", mode: AddressingMode.Absolute, opcode: 0xee },
1223
- { mnemonic: "INC", mode: AddressingMode.AbsoluteX, opcode: 0xfe },
1224
- { mnemonic: "INX", mode: AddressingMode.Implied, opcode: 0xe8 },
1225
- { mnemonic: "INY", mode: AddressingMode.Implied, opcode: 0xc8 },
1226
- { mnemonic: "JMP", mode: AddressingMode.Absolute, opcode: 0x4c },
1227
- { mnemonic: "JMP", mode: AddressingMode.Indirect, opcode: 0x6c },
1228
- { mnemonic: "JSR", mode: AddressingMode.Absolute, opcode: 0x20 },
1229
- { mnemonic: "LDA", mode: AddressingMode.Immediate, opcode: 0xa9 },
1230
- { mnemonic: "LDA", mode: AddressingMode.ZeroPage, opcode: 0xa5 },
1231
- { mnemonic: "LDA", mode: AddressingMode.ZeroPageX, opcode: 0xb5 },
1232
- { mnemonic: "LDA", mode: AddressingMode.Absolute, opcode: 0xad },
1233
- { mnemonic: "LDA", mode: AddressingMode.AbsoluteX, opcode: 0xbd },
1234
- { mnemonic: "LDA", mode: AddressingMode.AbsoluteY, opcode: 0xb9 },
1235
- { mnemonic: "LDA", mode: AddressingMode.IndirectX, opcode: 0xa1 },
1236
- { mnemonic: "LDA", mode: AddressingMode.IndirectY, opcode: 0xb1 },
1237
- { mnemonic: "LDX", mode: AddressingMode.Immediate, opcode: 0xa2 },
1238
- { mnemonic: "LDX", mode: AddressingMode.ZeroPage, opcode: 0xa6 },
1239
- { mnemonic: "LDX", mode: AddressingMode.ZeroPageY, opcode: 0xb6 },
1240
- { mnemonic: "LDX", mode: AddressingMode.Absolute, opcode: 0xae },
1241
- { mnemonic: "LDX", mode: AddressingMode.AbsoluteY, opcode: 0xbe },
1242
- { mnemonic: "LDY", mode: AddressingMode.Immediate, opcode: 0xa0 },
1243
- { mnemonic: "LDY", mode: AddressingMode.ZeroPage, opcode: 0xa4 },
1244
- { mnemonic: "LDY", mode: AddressingMode.ZeroPageX, opcode: 0xb4 },
1245
- { mnemonic: "LDY", mode: AddressingMode.Absolute, opcode: 0xac },
1246
- { mnemonic: "LDY", mode: AddressingMode.AbsoluteX, opcode: 0xbc },
1247
- { mnemonic: "LSR", mode: AddressingMode.Accumulator, opcode: 0x4a },
1248
- { mnemonic: "LSR", mode: AddressingMode.ZeroPage, opcode: 0x46 },
1249
- { mnemonic: "LSR", mode: AddressingMode.ZeroPageX, opcode: 0x56 },
1250
- { mnemonic: "LSR", mode: AddressingMode.Absolute, opcode: 0x4e },
1251
- { mnemonic: "LSR", mode: AddressingMode.AbsoluteX, opcode: 0x5e },
1252
- { mnemonic: "NOP", mode: AddressingMode.Implied, opcode: 0xea },
1253
- { mnemonic: "ORA", mode: AddressingMode.Immediate, opcode: 0x09 },
1254
- { mnemonic: "ORA", mode: AddressingMode.ZeroPage, opcode: 0x05 },
1255
- { mnemonic: "ORA", mode: AddressingMode.ZeroPageX, opcode: 0x15 },
1256
- { mnemonic: "ORA", mode: AddressingMode.Absolute, opcode: 0x0d },
1257
- { mnemonic: "ORA", mode: AddressingMode.AbsoluteX, opcode: 0x1d },
1258
- { mnemonic: "ORA", mode: AddressingMode.AbsoluteY, opcode: 0x19 },
1259
- { mnemonic: "ORA", mode: AddressingMode.IndirectX, opcode: 0x01 },
1260
- { mnemonic: "ORA", mode: AddressingMode.IndirectY, opcode: 0x11 },
1261
- { mnemonic: "PHA", mode: AddressingMode.Implied, opcode: 0x48 },
1262
- { mnemonic: "PHP", mode: AddressingMode.Implied, opcode: 0x08 },
1263
- { mnemonic: "PLA", mode: AddressingMode.Implied, opcode: 0x68 },
1264
- { mnemonic: "PLP", mode: AddressingMode.Implied, opcode: 0x28 },
1265
- { mnemonic: "ROL", mode: AddressingMode.Accumulator, opcode: 0x2a },
1266
- { mnemonic: "ROL", mode: AddressingMode.ZeroPage, opcode: 0x26 },
1267
- { mnemonic: "ROL", mode: AddressingMode.ZeroPageX, opcode: 0x36 },
1268
- { mnemonic: "ROL", mode: AddressingMode.Absolute, opcode: 0x2e },
1269
- { mnemonic: "ROL", mode: AddressingMode.AbsoluteX, opcode: 0x3e },
1270
- { mnemonic: "ROR", mode: AddressingMode.Accumulator, opcode: 0x6a },
1271
- { mnemonic: "ROR", mode: AddressingMode.ZeroPage, opcode: 0x66 },
1272
- { mnemonic: "ROR", mode: AddressingMode.ZeroPageX, opcode: 0x76 },
1273
- { mnemonic: "ROR", mode: AddressingMode.Absolute, opcode: 0x6e },
1274
- { mnemonic: "ROR", mode: AddressingMode.AbsoluteX, opcode: 0x7e },
1275
- { mnemonic: "RTI", mode: AddressingMode.Implied, opcode: 0x40 },
1276
- { mnemonic: "RTS", mode: AddressingMode.Implied, opcode: 0x60 },
1277
- { mnemonic: "SBC", mode: AddressingMode.Immediate, opcode: 0xe9 },
1278
- { mnemonic: "SBC", mode: AddressingMode.ZeroPage, opcode: 0xe5 },
1279
- { mnemonic: "SBC", mode: AddressingMode.ZeroPageX, opcode: 0xf5 },
1280
- { mnemonic: "SBC", mode: AddressingMode.Absolute, opcode: 0xed },
1281
- { mnemonic: "SBC", mode: AddressingMode.AbsoluteX, opcode: 0xfd },
1282
- { mnemonic: "SBC", mode: AddressingMode.AbsoluteY, opcode: 0xf9 },
1283
- { mnemonic: "SBC", mode: AddressingMode.IndirectX, opcode: 0xe1 },
1284
- { mnemonic: "SBC", mode: AddressingMode.IndirectY, opcode: 0xf1 },
1285
- { mnemonic: "SEC", mode: AddressingMode.Implied, opcode: 0x38 },
1286
- { mnemonic: "SED", mode: AddressingMode.Implied, opcode: 0xf8 },
1287
- { mnemonic: "SEI", mode: AddressingMode.Implied, opcode: 0x78 },
1288
- { mnemonic: "STA", mode: AddressingMode.ZeroPage, opcode: 0x85 },
1289
- { mnemonic: "STA", mode: AddressingMode.ZeroPageX, opcode: 0x95 },
1290
- { mnemonic: "STA", mode: AddressingMode.Absolute, opcode: 0x8d },
1291
- { mnemonic: "STA", mode: AddressingMode.AbsoluteX, opcode: 0x9d },
1292
- { mnemonic: "STA", mode: AddressingMode.AbsoluteY, opcode: 0x99 },
1293
- { mnemonic: "STA", mode: AddressingMode.IndirectX, opcode: 0x81 },
1294
- { mnemonic: "STA", mode: AddressingMode.IndirectY, opcode: 0x91 },
1295
- { mnemonic: "STX", mode: AddressingMode.ZeroPage, opcode: 0x86 },
1296
- { mnemonic: "STX", mode: AddressingMode.ZeroPageY, opcode: 0x96 },
1297
- { mnemonic: "STX", mode: AddressingMode.Absolute, opcode: 0x8e },
1298
- { mnemonic: "STY", mode: AddressingMode.ZeroPage, opcode: 0x84 },
1299
- { mnemonic: "STY", mode: AddressingMode.ZeroPageX, opcode: 0x94 },
1300
- { mnemonic: "STY", mode: AddressingMode.Absolute, opcode: 0x8c },
1301
- { mnemonic: "TAX", mode: AddressingMode.Implied, opcode: 0xaa },
1302
- { mnemonic: "TAY", mode: AddressingMode.Implied, opcode: 0xa8 },
1303
- { mnemonic: "TSX", mode: AddressingMode.Implied, opcode: 0xba },
1304
- { mnemonic: "TXA", mode: AddressingMode.Implied, opcode: 0x8a },
1305
- { mnemonic: "TXS", mode: AddressingMode.Implied, opcode: 0x9a },
1306
- { mnemonic: "TYA", mode: AddressingMode.Implied, opcode: 0x98 },
1307
- ];
1308
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 "../petscii.js";
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();