@moonbase-machines/mos6502 0.0.0-stage → 0.5.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/LICENSE +21 -0
- package/README.md +7 -2
- package/asm.js +185 -0
- package/cpu.js +360 -0
- package/disasm.js +64 -0
- package/dist/moonbase-mos6502.js +22 -0
- package/dist/moonbase-mos6502.js.map +7 -0
- package/index.js +6 -0
- package/package.json +13 -4
- package/test/asm.test.js +77 -0
- package/test/cpu.test.js +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Wharton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @moonbase-machines/mos6502
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The 6502: the documented instructions, run against a machine that gives it `read(address)` and
|
|
4
|
+
`write(address, value)`; an assembler for Moonbase's own 6502 code; and a disassembler. The CPU
|
|
5
|
+
both machine packages run on.
|
|
6
|
+
|
|
7
|
+
Part of Moonbase (moonbase.au): Mark Wharton's titles of 1985 to 1995, running again in the browser
|
|
8
|
+
on these machines. MIT; see LICENSE. Each file opens with what it is and what it covers.
|
package/asm.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// A 6502 assembler, for Moonbase's own machine code (Crayon on the 64): ordinary 6502 syntax, its
|
|
2
|
+
// opcodes the disassembler's table read backwards (disasm.js `OPS`), so that what it assembles
|
|
3
|
+
// reads back the same.
|
|
4
|
+
//
|
|
5
|
+
// const { prg, bytes, origin, symbols } = assemble(source, { file, include })
|
|
6
|
+
// source the text; `file` its name, for errors; `include(name)` the text of an .include
|
|
7
|
+
// prg the program as a C64 file: its load address, then its bytes
|
|
8
|
+
// symbols every label and constant, by name
|
|
9
|
+
//
|
|
10
|
+
// A line is a label, an instruction or a directive, then a comment after `;`:
|
|
11
|
+
// start: a label; `.loop:` a local one, under the last label (`.loop` reaches it)
|
|
12
|
+
// name = expr a constant; *= expr where the bytes go from here
|
|
13
|
+
// LDA #<table an instruction: #imm, zp, zp,X, zp,Y, abs, abs,X, abs,Y, (zp,X), (zp),Y,
|
|
14
|
+
// (abs), A or nothing; zero page chosen where the operand is known below 256
|
|
15
|
+
// by the first pass; a branch's reach checked
|
|
16
|
+
// .byte 1,'a',$ff bytes; .word expr,... little-endian words
|
|
17
|
+
// .text "HELLO" PETSCII (letters as the 64's capitals); .screen "HELLO" screen codes
|
|
18
|
+
// .mixed "Hello" screen codes for the 64's second set, its small letters and capitals:
|
|
19
|
+
// small letters 1 to 26, capitals $41 to $5A, the rest as .screen
|
|
20
|
+
// .fill n[,value] n bytes; .include "file" another file's lines, here
|
|
21
|
+
// Expressions: decimal, $hex, %binary, 'c' (its PETSCII), * (here), names; unary - ~ < (low byte)
|
|
22
|
+
// > (high byte); * / % + - << >> & ^ | as C has them; parentheses.
|
|
23
|
+
import { OPS, SIZE } from './disasm.js';
|
|
24
|
+
|
|
25
|
+
const CODES = {};
|
|
26
|
+
OPS.forEach((o, i) => { if (o.name !== '???') (CODES[o.name] ??= {})[o.mode] = i; });
|
|
27
|
+
|
|
28
|
+
export class AsmError extends Error {}
|
|
29
|
+
|
|
30
|
+
// a character as PETSCII: the letters as the 64's capitals, whichever case they are typed in
|
|
31
|
+
const petscii = c => { const n = c.charCodeAt(0); return n >= 0x61 && n <= 0x7a ? n - 0x20 : n === 0x5f ? 0xa4 : n; };
|
|
32
|
+
// and as a screen code: @ and the letters 0 to 26, the rest of $20 to $3F as they are
|
|
33
|
+
const screen = c => { const n = petscii(c); return n >= 0x40 && n < 0x60 ? n - 0x40 : n; };
|
|
34
|
+
// and in the 64's second set of characters, small letters and capitals: a small letter 1 to 26, a
|
|
35
|
+
// capital $41 to $5A, as that set has them
|
|
36
|
+
const mixed = c => { const n = c.charCodeAt(0); return n >= 0x61 && n <= 0x7a ? n - 0x60 : n >= 0x41 && n <= 0x5a ? n : screen(c); };
|
|
37
|
+
|
|
38
|
+
export function assemble(source, { file = 'source', include = null } = {}) {
|
|
39
|
+
const symbols = new Map(), sizes = [];
|
|
40
|
+
let mem, pc, low, high, pass, global, where, count;
|
|
41
|
+
|
|
42
|
+
// the lines, with any .include's in their place, each knowing its file and number
|
|
43
|
+
const lines = [];
|
|
44
|
+
const read = (text, name, depth = 0) => text.split(/\r?\n/).forEach((line, n) => {
|
|
45
|
+
const inc = line.match(/^\s*\.include\s+"([^"]+)"/i);
|
|
46
|
+
if (!inc) return lines.push({ line, file: name, n: n + 1 });
|
|
47
|
+
if (!include) throw new AsmError(`${name}:${n + 1}: .include, but no way to read "${inc[1]}"`);
|
|
48
|
+
if (depth > 8) throw new AsmError(`${name}:${n + 1}: .include too deep`);
|
|
49
|
+
read(include(inc[1]), inc[1], depth + 1);
|
|
50
|
+
});
|
|
51
|
+
read(source, file);
|
|
52
|
+
const fail = message => { throw new AsmError(`${where.file}:${where.n}: ${message}`); };
|
|
53
|
+
const local = name => (name.startsWith('.') ? (global ?? fail(`${name} before any label`)) + name : name);
|
|
54
|
+
|
|
55
|
+
// an expression's value, or undefined where the first pass has not yet seen a name in it
|
|
56
|
+
function evaluate(text) {
|
|
57
|
+
const tokens = text.match(/\$[0-9a-f]+|%[01]+|\d+|'(?:[^'\\]|\\.)'|[.@]?[a-z_][\w.]*|<<|>>|[-+*/%&|^~<>()]/gi);
|
|
58
|
+
if (!tokens || tokens.join('').replace(/\s/g, '') !== text.replace(/\s/g, '')) fail(`can't read "${text}"`);
|
|
59
|
+
let at = 0;
|
|
60
|
+
const peek = () => tokens[at], next = () => tokens[at++];
|
|
61
|
+
const LEVELS = [['|'], ['^'], ['&'], ['<<', '>>'], ['+', '-'], ['*', '/', '%']];
|
|
62
|
+
const binary = level => {
|
|
63
|
+
if (level === LEVELS.length) return unary();
|
|
64
|
+
let v = binary(level + 1);
|
|
65
|
+
while (LEVELS[level].includes(peek())) {
|
|
66
|
+
const op = next(), w = binary(level + 1);
|
|
67
|
+
v = v === undefined || w === undefined ? undefined
|
|
68
|
+
: op === '|' ? v | w : op === '^' ? v ^ w : op === '&' ? v & w : op === '<<' ? v << w : op === '>>' ? v >> w
|
|
69
|
+
: op === '+' ? v + w : op === '-' ? v - w : op === '*' ? v * w : op === '/' ? Math.trunc(v / w) : v % w;
|
|
70
|
+
}
|
|
71
|
+
return v;
|
|
72
|
+
};
|
|
73
|
+
const unary = () => {
|
|
74
|
+
const t = next();
|
|
75
|
+
if (t === undefined) fail(`"${text}" ends too soon`);
|
|
76
|
+
if (t === '-' || t === '~' || t === '<' || t === '>') {
|
|
77
|
+
const v = unary();
|
|
78
|
+
return v === undefined ? undefined : t === '-' ? -v : t === '~' ? ~v & 0xffff : t === '<' ? v & 0xff : (v >> 8) & 0xff;
|
|
79
|
+
}
|
|
80
|
+
if (t === '(') { const v = binary(0); if (next() !== ')') fail(`a ( without its ) in "${text}"`); return v; }
|
|
81
|
+
if (t === '*') return pc;
|
|
82
|
+
if (t[0] === '$') return parseInt(t.slice(1), 16);
|
|
83
|
+
if (t[0] === '%') return parseInt(t.slice(1), 2);
|
|
84
|
+
if (/^\d/.test(t)) return parseInt(t, 10);
|
|
85
|
+
if (t[0] === "'") return petscii(t[1] === '\\' ? t[2] : t[1]);
|
|
86
|
+
const name = local(t);
|
|
87
|
+
if (symbols.has(name)) return symbols.get(name);
|
|
88
|
+
if (pass === 2) fail(`${name}: not defined`);
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
const v = binary(0);
|
|
92
|
+
if (at < tokens.length) fail(`"${text}": more than one expression`);
|
|
93
|
+
return v;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const put = b => { if (pc > 0xffff) fail('past $FFFF'); mem[pc] = b & 0xff; low = Math.min(low, pc); high = Math.max(high, pc); pc++; };
|
|
97
|
+
const list = text => text.match(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)'|[^,])+/g)?.map(s => s.trim()) ?? [];
|
|
98
|
+
const define = (name, value) => {
|
|
99
|
+
if (pass === 1 && symbols.has(name) && symbols.get(name) !== value) fail(`${name} defined twice`);
|
|
100
|
+
if (pass === 2 && symbols.has(name) && symbols.get(name) !== value) fail(`${name} moved between the passes`);
|
|
101
|
+
symbols.set(name, value);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
function instruction(name, text) {
|
|
105
|
+
const modes = CODES[name] ?? fail(`${name}: no such instruction`);
|
|
106
|
+
const nth = count++;
|
|
107
|
+
let mode, value;
|
|
108
|
+
const operand = text.trim();
|
|
109
|
+
if (!operand || /^a$/i.test(operand)) {
|
|
110
|
+
mode = modes.imp !== undefined ? 'imp' : modes.acc !== undefined ? 'acc' : fail(`${name} needs an operand`);
|
|
111
|
+
} else if (operand[0] === '#') { mode = 'imm'; value = evaluate(operand.slice(1)); }
|
|
112
|
+
else {
|
|
113
|
+
let m;
|
|
114
|
+
if ((m = operand.match(/^\((.+),\s*x\s*\)$/i))) { mode = 'izx'; value = evaluate(m[1]); }
|
|
115
|
+
else if ((m = operand.match(/^\((.+)\)\s*,\s*y$/i))) { mode = 'izy'; value = evaluate(m[1]); }
|
|
116
|
+
else if (modes.ind !== undefined && (m = operand.match(/^\((.+)\)$/))) { mode = 'ind'; value = evaluate(m[1]); }
|
|
117
|
+
else {
|
|
118
|
+
const [, expr, reg] = operand.match(/^(.+?)(?:\s*,\s*([xy]))?$/i);
|
|
119
|
+
value = evaluate(expr);
|
|
120
|
+
const x = reg?.toLowerCase();
|
|
121
|
+
if (modes.rel !== undefined) mode = 'rel';
|
|
122
|
+
else {
|
|
123
|
+
const zp = x === 'x' ? 'zpx' : x === 'y' ? 'zpy' : 'zp', abs = x === 'x' ? 'abx' : x === 'y' ? 'aby' : 'abs';
|
|
124
|
+
// (zero page where the first pass knew the operand below 256, and the same size after)
|
|
125
|
+
if (pass === 1) sizes[nth] = value !== undefined && value < 0x100 && modes[zp] !== undefined ? zp : abs;
|
|
126
|
+
mode = sizes[nth];
|
|
127
|
+
if (modes[mode] === undefined) mode = modes[abs] !== undefined ? abs : modes[zp] !== undefined ? zp : fail(`${name} has no ${operand} form`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (modes[mode] === undefined) fail(`${name} has no ${mode} form`);
|
|
132
|
+
put(modes[mode]);
|
|
133
|
+
const v = value ?? 0;
|
|
134
|
+
if (mode === 'rel') {
|
|
135
|
+
const d = v - (pc + 1);
|
|
136
|
+
if (pass === 2 && (d < -128 || d > 127)) fail(`${name} ${operand}: ${d} bytes away, a branch reaches -128 to 127`);
|
|
137
|
+
put(d);
|
|
138
|
+
} else if (SIZE[mode] === 2) {
|
|
139
|
+
if (pass === 2 && (v < -128 || v > 0xff)) fail(`${operand}: ${v} is more than a byte`);
|
|
140
|
+
put(v);
|
|
141
|
+
} else if (SIZE[mode] === 3) { put(v); put(v >> 8); }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (pass = 1; pass <= 2; pass++) {
|
|
145
|
+
mem = new Uint8Array(0x10000); pc = 0x1000; low = 0x10000; high = -1; global = null; count = 0;
|
|
146
|
+
let set = false;
|
|
147
|
+
for (where of lines) {
|
|
148
|
+
let text = where.line.replace(/;.*$/, '').trim();
|
|
149
|
+
// (a ; inside quotes is kept: the comment is cut only outside them)
|
|
150
|
+
if (/["']/.test(where.line)) { let q = null, i = 0; for (; i < where.line.length; i++) { const c = where.line[i]; if (q) { if (c === q) q = null; } else if (c === '"' || c === "'") q = c; else if (c === ';') break; } text = where.line.slice(0, i).trim(); }
|
|
151
|
+
if (!text) continue;
|
|
152
|
+
let m = text.match(/^([.@]?[a-z_][\w]*):\s*(.*)$/i);
|
|
153
|
+
if (m) {
|
|
154
|
+
if (!m[1].startsWith('.')) global = m[1];
|
|
155
|
+
define(local(m[1]), pc);
|
|
156
|
+
text = m[2];
|
|
157
|
+
if (!text) continue;
|
|
158
|
+
}
|
|
159
|
+
if ((m = text.match(/^\*\s*=\s*(.+)$/))) { pc = evaluate(m[1]) ?? fail('*= needs a value the first pass knows'); set = true; continue; }
|
|
160
|
+
if ((m = text.match(/^([a-z_]\w*)\s*=\s*(.+)$/i))) { const v = evaluate(m[2]); if (v !== undefined) define(m[1], v); else if (pass === 2) fail(`${m[1]}: no value`); continue; }
|
|
161
|
+
if (!set) fail('no *= before the first byte');
|
|
162
|
+
if ((m = text.match(/^\.(\w+)\s*(.*)$/))) {
|
|
163
|
+
const [, directive, rest] = m;
|
|
164
|
+
switch (directive.toLowerCase()) {
|
|
165
|
+
case 'byte': case 'text': case 'screen': case 'mixed':
|
|
166
|
+
for (const item of list(rest)) {
|
|
167
|
+
if (item[0] === '"') { const as = { screen, mixed }[directive.toLowerCase()] ?? petscii; for (const c of item.slice(1, -1).replace(/\\(.)/g, '$1')) put(as(c)); }
|
|
168
|
+
else { const v = evaluate(item); if (pass === 2 && (v < -128 || v > 0xff)) fail(`${item}: more than a byte`); put(v ?? 0); }
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case 'word': for (const item of list(rest)) { const v = evaluate(item) ?? 0; put(v); put(v >> 8); } break;
|
|
172
|
+
case 'fill': { const [n, v = '0'] = list(rest); const k = evaluate(n) ?? fail('.fill needs a count the first pass knows'), b = evaluate(v) ?? 0; for (let i = 0; i < k; i++) put(b); break; }
|
|
173
|
+
default: fail(`.${directive}: no such directive`);
|
|
174
|
+
}
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if ((m = text.match(/^([a-z]{3})\b\s*(.*)$/i))) { instruction(m[1].toUpperCase(), m[2]); continue; }
|
|
178
|
+
fail(`can't read "${text}"`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (high < 0) throw new AsmError(`${file}: nothing assembled`);
|
|
182
|
+
const bytes = mem.slice(low, high + 1), prg = new Uint8Array(bytes.length + 2);
|
|
183
|
+
prg[0] = low & 0xff; prg[1] = low >> 8; prg.set(bytes, 2);
|
|
184
|
+
return { origin: low, bytes, prg, symbols };
|
|
185
|
+
}
|
package/cpu.js
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// A 6502, the documented instructions, running against a machine that gives it `read(address)`
|
|
2
|
+
// and `write(address, value)`. An NMOS chip, as the Commodore 64's 6510 is, also runs the
|
|
3
|
+
// undocumented instructions, NOPs and all, so `{ nmos: true }` adds them; the Apple's CPU is
|
|
4
|
+
// without them, since on the enhanced IIe's 65C02 the same opcodes are other instructions.
|
|
5
|
+
// `step()` runs one instruction and returns its cycles; `call(hook)` lets the machine take over
|
|
6
|
+
// an address: when the program counter reaches it, the hook runs in
|
|
7
|
+
// place of the code there and the CPU returns as if from a subroutine; a hook answering false
|
|
8
|
+
// lets the code run, and one answering 'wait' is run again on the next step.
|
|
9
|
+
export class CPU {
|
|
10
|
+
// writeBack: a read-modify-write instruction writes the value it read back before the new one,
|
|
11
|
+
// as the NMOS chip does on its spare cycle; on the C64 that first write is what acknowledges the
|
|
12
|
+
// VIC-II's interrupt in DEC $D019 or ASL $D019, a demo's usual way (@moonbase-machines/c64/machine.js)
|
|
13
|
+
constructor(machine, { nmos = false, writeBack = false } = {}) {
|
|
14
|
+
this.m = machine;
|
|
15
|
+
this.nmos = nmos; this.writeBack = writeBack;
|
|
16
|
+
this.a = 0; this.x = 0; this.y = 0; this.sp = 0xfd; this.pc = 0;
|
|
17
|
+
this.n = 0; this.v = 0; this.d = 0; this.i = 1; this.z = 0; this.c = 0;
|
|
18
|
+
this.hooks = new Map();
|
|
19
|
+
this.cycles = 0;
|
|
20
|
+
}
|
|
21
|
+
get p() { return (this.n << 7) | (this.v << 6) | 0x20 | (this.d << 3) | (this.i << 2) | (this.z << 1) | this.c; }
|
|
22
|
+
set p(v) { this.n = v >> 7 & 1; this.v = v >> 6 & 1; this.d = v >> 3 & 1; this.i = v >> 2 & 1; this.z = v >> 1 & 1; this.c = v & 1; }
|
|
23
|
+
read(a) { return this.m.read(a & 0xffff); }
|
|
24
|
+
write(a, v) { this.m.write(a & 0xffff, v & 0xff); }
|
|
25
|
+
read16(a) { return this.read(a) | this.read(a + 1) << 8; }
|
|
26
|
+
push(v) { this.write(0x100 | this.sp, v); this.sp = (this.sp - 1) & 0xff; }
|
|
27
|
+
pull() { this.sp = (this.sp + 1) & 0xff; return this.read(0x100 | this.sp); }
|
|
28
|
+
nz(v) { v &= 0xff; this.n = v >> 7; this.z = v === 0 ? 1 : 0; return v; }
|
|
29
|
+
branch(cond) { const off = this.read(this.pc++); if (cond) { const t = (this.pc + (off < 128 ? off : off - 256)) & 0xffff; this.cycles += ((t ^ this.pc) & 0xff00) ? 2 : 1; this.pc = t; } }
|
|
30
|
+
// addressing modes return an address
|
|
31
|
+
imm() { return this.pc++; }
|
|
32
|
+
zp() { return this.read(this.pc++); }
|
|
33
|
+
zpx() { return (this.read(this.pc++) + this.x) & 0xff; }
|
|
34
|
+
zpy() { return (this.read(this.pc++) + this.y) & 0xff; }
|
|
35
|
+
abs() { const a = this.read16(this.pc); this.pc += 2; return a; }
|
|
36
|
+
abx(penalty) { const a = this.abs(); if (penalty && ((a + this.x) ^ a) & 0xff00) this.cycles++; return (a + this.x) & 0xffff; }
|
|
37
|
+
aby(penalty) { const a = this.abs(); if (penalty && ((a + this.y) ^ a) & 0xff00) this.cycles++; return (a + this.y) & 0xffff; }
|
|
38
|
+
izx() { const z = (this.read(this.pc++) + this.x) & 0xff; return this.read(z) | this.read((z + 1) & 0xff) << 8; }
|
|
39
|
+
izy(penalty) { const z = this.read(this.pc++); const a = this.read(z) | this.read((z + 1) & 0xff) << 8; if (penalty && ((a + this.y) ^ a) & 0xff00) this.cycles++; return (a + this.y) & 0xffff; }
|
|
40
|
+
// operations
|
|
41
|
+
// Decimal mode: on the NMOS chip (the 6510), as the chip itself does it (perfect6502): a digit
|
|
42
|
+
// over 9 has 6 added and carries, and a result over $9F has $60 added, which is the same as
|
|
43
|
+
// adding the digits for a number in decimal and not otherwise; N and V from the sum before that
|
|
44
|
+
// last step, Z from the binary sum. SBC's flags are all the binary subtraction's.
|
|
45
|
+
adc(v) {
|
|
46
|
+
if (this.d && this.nmos) {
|
|
47
|
+
const a = this.a; let lo = (a & 15) + (v & 15) + this.c;
|
|
48
|
+
if (lo >= 10) lo = ((lo + 6) & 15) + 16;
|
|
49
|
+
let s = (a & 0xf0) + (v & 0xf0) + lo;
|
|
50
|
+
this.z = ((a + v + this.c) & 0xff) === 0 ? 1 : 0; this.n = s >> 7 & 1; this.v = ((~(a ^ v) & (a ^ s)) & 0x80) ? 1 : 0;
|
|
51
|
+
if (s >= 0xa0) s += 0x60;
|
|
52
|
+
this.c = s >= 0x100 ? 1 : 0; this.a = s & 0xff; return;
|
|
53
|
+
}
|
|
54
|
+
if (this.d) {
|
|
55
|
+
let lo = (this.a & 15) + (v & 15) + this.c, hi = (this.a >> 4) + (v >> 4);
|
|
56
|
+
if (lo > 9) { lo -= 10; hi++; }
|
|
57
|
+
const r = (this.a + v + this.c) & 0xff; this.z = r === 0 ? 1 : 0;
|
|
58
|
+
this.n = (hi & 8) ? 1 : 0; this.v = ((~(this.a ^ v) & (this.a ^ (hi << 4))) & 0x80) ? 1 : 0;
|
|
59
|
+
if (hi > 9) hi -= 10, this.c = 1; else this.c = 0;
|
|
60
|
+
this.a = ((hi << 4) | lo) & 0xff; return;
|
|
61
|
+
}
|
|
62
|
+
const r = this.a + v + this.c;
|
|
63
|
+
this.v = ((~(this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r > 0xff ? 1 : 0; this.a = this.nz(r);
|
|
64
|
+
}
|
|
65
|
+
sbc(v) {
|
|
66
|
+
if (this.d && this.nmos) {
|
|
67
|
+
const a = this.a, r = a - v - (1 - this.c); let lo = (a & 15) - (v & 15) - (1 - this.c);
|
|
68
|
+
if (lo < 0) lo = ((lo - 6) & 15) - 16;
|
|
69
|
+
let s = (a & 0xf0) - (v & 0xf0) + lo;
|
|
70
|
+
if (s < 0) s -= 0x60;
|
|
71
|
+
this.z = (r & 0xff) === 0 ? 1 : 0; this.n = (r & 0x80) ? 1 : 0; this.v = (((a ^ v) & (a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0;
|
|
72
|
+
this.a = s & 0xff; return;
|
|
73
|
+
}
|
|
74
|
+
if (this.d) {
|
|
75
|
+
let lo = (this.a & 15) - (v & 15) - (1 - this.c), hi = (this.a >> 4) - (v >> 4);
|
|
76
|
+
if (lo < 0) { lo += 10; hi--; }
|
|
77
|
+
const r = this.a - v - (1 - this.c); this.z = (r & 0xff) === 0 ? 1 : 0; this.n = (r & 0x80) ? 1 : 0;
|
|
78
|
+
this.v = (((this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0;
|
|
79
|
+
if (hi < 0) hi += 10;
|
|
80
|
+
this.a = ((hi << 4) | lo) & 0xff; return;
|
|
81
|
+
}
|
|
82
|
+
const r = this.a - v - (1 - this.c);
|
|
83
|
+
this.v = (((this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0; this.a = this.nz(r);
|
|
84
|
+
}
|
|
85
|
+
cmp(r, v) { const d = r - v; this.c = d >= 0 ? 1 : 0; this.nz(d); }
|
|
86
|
+
// a read-modify-write's read, and its value written back first where the chip does (writeBack)
|
|
87
|
+
modify(a) { const v = this.read(a); if (this.writeBack) this.write(a, v); return v; }
|
|
88
|
+
asl(a) { const v = a === null ? this.a : this.modify(a); this.c = v >> 7; const r = this.nz(v << 1); if (a === null) this.a = r; else this.write(a, r); }
|
|
89
|
+
lsr(a) { const v = a === null ? this.a : this.modify(a); this.c = v & 1; const r = this.nz(v >> 1); if (a === null) this.a = r; else this.write(a, r); }
|
|
90
|
+
rol(a) { const v = a === null ? this.a : this.modify(a); const r = this.nz((v << 1) | this.c); this.c = v >> 7; if (a === null) this.a = r; else this.write(a, r); }
|
|
91
|
+
ror(a) { const v = a === null ? this.a : this.modify(a); const r = this.nz((v >> 1) | (this.c << 7)); this.c = v & 1; if (a === null) this.a = r; else this.write(a, r); }
|
|
92
|
+
inc(a) { this.write(a, this.nz(this.modify(a) + 1)); }
|
|
93
|
+
dec(a) { this.write(a, this.nz(this.modify(a) - 1)); }
|
|
94
|
+
bit(v) { this.n = v >> 7; this.v = v >> 6 & 1; this.z = (this.a & v) === 0 ? 1 : 0; }
|
|
95
|
+
|
|
96
|
+
// an interrupt request, taken when interrupts are not masked: the return address and the
|
|
97
|
+
// flags go on the stack and the vector at $FFFE is followed; a non-maskable one goes by $FFFA
|
|
98
|
+
irq() { if (this.i) return false; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p & 0xef); this.i = 1; this.pc = this.read16(0xfffe); this.cycles += 7; return true; }
|
|
99
|
+
nmi() { this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p & 0xef); this.i = 1; this.pc = this.read16(0xfffa); this.cycles += 7; }
|
|
100
|
+
|
|
101
|
+
step() {
|
|
102
|
+
// (a hook that goes on elsewhere, setting the PC and returning false, has that place's hook
|
|
103
|
+
// run too: a routine of a ROM in JavaScript jumping on to another, @moonbase-machines/c64/rom.js)
|
|
104
|
+
for (let hook = this.hooks.get(this.pc), guard = 0; hook && guard < 8; hook = this.hooks.get(this.pc), guard++) {
|
|
105
|
+
const at = this.pc, r = hook(this);
|
|
106
|
+
if (r === 'wait') { this.cycles += 10; return 10; }
|
|
107
|
+
if (r !== false) { this.pc = (this.pull() | this.pull() << 8) + 1 & 0xffff; this.cycles += 6; return 6; }
|
|
108
|
+
if (this.pc === at) break;
|
|
109
|
+
}
|
|
110
|
+
const start = this.cycles;
|
|
111
|
+
this.trace?.(this.pc); // every instruction run, for a tool that wants them (code coverage)
|
|
112
|
+
const op = this.read(this.pc++);
|
|
113
|
+
this.cycles += CYCLES[op];
|
|
114
|
+
switch (op) {
|
|
115
|
+
case 0x00: this.pc++; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p | 0x10); this.i = 1; this.pc = this.read16(0xfffe); break;
|
|
116
|
+
case 0x01: this.a = this.nz(this.a | this.read(this.izx())); break;
|
|
117
|
+
case 0x05: this.a = this.nz(this.a | this.read(this.zp())); break;
|
|
118
|
+
case 0x06: this.asl(this.zp()); break;
|
|
119
|
+
case 0x08: this.push(this.p | 0x10); break;
|
|
120
|
+
case 0x09: this.a = this.nz(this.a | this.read(this.imm())); break;
|
|
121
|
+
case 0x0a: this.asl(null); break;
|
|
122
|
+
case 0x0d: this.a = this.nz(this.a | this.read(this.abs())); break;
|
|
123
|
+
case 0x0e: this.asl(this.abs()); break;
|
|
124
|
+
case 0x10: this.branch(!this.n); break;
|
|
125
|
+
case 0x11: this.a = this.nz(this.a | this.read(this.izy(1))); break;
|
|
126
|
+
case 0x15: this.a = this.nz(this.a | this.read(this.zpx())); break;
|
|
127
|
+
case 0x16: this.asl(this.zpx()); break;
|
|
128
|
+
case 0x18: this.c = 0; break;
|
|
129
|
+
case 0x19: this.a = this.nz(this.a | this.read(this.aby(1))); break;
|
|
130
|
+
case 0x1d: this.a = this.nz(this.a | this.read(this.abx(1))); break;
|
|
131
|
+
case 0x1e: this.asl(this.abx()); break;
|
|
132
|
+
case 0x20: { const a = this.abs(); this.pc--; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.pc = a; break; }
|
|
133
|
+
case 0x21: this.a = this.nz(this.a & this.read(this.izx())); break;
|
|
134
|
+
case 0x24: this.bit(this.read(this.zp())); break;
|
|
135
|
+
case 0x25: this.a = this.nz(this.a & this.read(this.zp())); break;
|
|
136
|
+
case 0x26: this.rol(this.zp()); break;
|
|
137
|
+
case 0x28: this.p = this.pull(); break;
|
|
138
|
+
case 0x29: this.a = this.nz(this.a & this.read(this.imm())); break;
|
|
139
|
+
case 0x2a: this.rol(null); break;
|
|
140
|
+
case 0x2c: this.bit(this.read(this.abs())); break;
|
|
141
|
+
case 0x2d: this.a = this.nz(this.a & this.read(this.abs())); break;
|
|
142
|
+
case 0x2e: this.rol(this.abs()); break;
|
|
143
|
+
case 0x30: this.branch(this.n); break;
|
|
144
|
+
case 0x31: this.a = this.nz(this.a & this.read(this.izy(1))); break;
|
|
145
|
+
case 0x35: this.a = this.nz(this.a & this.read(this.zpx())); break;
|
|
146
|
+
case 0x36: this.rol(this.zpx()); break;
|
|
147
|
+
case 0x38: this.c = 1; break;
|
|
148
|
+
case 0x39: this.a = this.nz(this.a & this.read(this.aby(1))); break;
|
|
149
|
+
case 0x3d: this.a = this.nz(this.a & this.read(this.abx(1))); break;
|
|
150
|
+
case 0x3e: this.rol(this.abx()); break;
|
|
151
|
+
case 0x40: this.p = this.pull(); this.pc = this.pull() | this.pull() << 8; break;
|
|
152
|
+
case 0x41: this.a = this.nz(this.a ^ this.read(this.izx())); break;
|
|
153
|
+
case 0x45: this.a = this.nz(this.a ^ this.read(this.zp())); break;
|
|
154
|
+
case 0x46: this.lsr(this.zp()); break;
|
|
155
|
+
case 0x48: this.push(this.a); break;
|
|
156
|
+
case 0x49: this.a = this.nz(this.a ^ this.read(this.imm())); break;
|
|
157
|
+
case 0x4a: this.lsr(null); break;
|
|
158
|
+
case 0x4c: this.pc = this.abs(); break;
|
|
159
|
+
case 0x4d: this.a = this.nz(this.a ^ this.read(this.abs())); break;
|
|
160
|
+
case 0x4e: this.lsr(this.abs()); break;
|
|
161
|
+
case 0x50: this.branch(!this.v); break;
|
|
162
|
+
case 0x51: this.a = this.nz(this.a ^ this.read(this.izy(1))); break;
|
|
163
|
+
case 0x55: this.a = this.nz(this.a ^ this.read(this.zpx())); break;
|
|
164
|
+
case 0x56: this.lsr(this.zpx()); break;
|
|
165
|
+
case 0x58: this.i = 0; break;
|
|
166
|
+
case 0x59: this.a = this.nz(this.a ^ this.read(this.aby(1))); break;
|
|
167
|
+
case 0x5d: this.a = this.nz(this.a ^ this.read(this.abx(1))); break;
|
|
168
|
+
case 0x5e: this.lsr(this.abx()); break;
|
|
169
|
+
case 0x60: this.pc = (this.pull() | this.pull() << 8) + 1 & 0xffff; break;
|
|
170
|
+
case 0x61: this.adc(this.read(this.izx())); break;
|
|
171
|
+
case 0x65: this.adc(this.read(this.zp())); break;
|
|
172
|
+
case 0x66: this.ror(this.zp()); break;
|
|
173
|
+
case 0x68: this.a = this.nz(this.pull()); break;
|
|
174
|
+
case 0x69: this.adc(this.read(this.imm())); break;
|
|
175
|
+
case 0x6a: this.ror(null); break;
|
|
176
|
+
case 0x6c: { const a = this.abs(); this.pc = this.read(a) | this.read((a & 0xff00) | ((a + 1) & 0xff)) << 8; break; }
|
|
177
|
+
case 0x6d: this.adc(this.read(this.abs())); break;
|
|
178
|
+
case 0x6e: this.ror(this.abs()); break;
|
|
179
|
+
case 0x70: this.branch(this.v); break;
|
|
180
|
+
case 0x71: this.adc(this.read(this.izy(1))); break;
|
|
181
|
+
case 0x75: this.adc(this.read(this.zpx())); break;
|
|
182
|
+
case 0x76: this.ror(this.zpx()); break;
|
|
183
|
+
case 0x78: this.i = 1; break;
|
|
184
|
+
case 0x79: this.adc(this.read(this.aby(1))); break;
|
|
185
|
+
case 0x7d: this.adc(this.read(this.abx(1))); break;
|
|
186
|
+
case 0x7e: this.ror(this.abx()); break;
|
|
187
|
+
case 0x81: this.write(this.izx(), this.a); break;
|
|
188
|
+
case 0x84: this.write(this.zp(), this.y); break;
|
|
189
|
+
case 0x85: this.write(this.zp(), this.a); break;
|
|
190
|
+
case 0x86: this.write(this.zp(), this.x); break;
|
|
191
|
+
case 0x88: this.y = this.nz(this.y - 1); break;
|
|
192
|
+
case 0x8a: this.a = this.nz(this.x); break;
|
|
193
|
+
case 0x8c: this.write(this.abs(), this.y); break;
|
|
194
|
+
case 0x8d: this.write(this.abs(), this.a); break;
|
|
195
|
+
case 0x8e: this.write(this.abs(), this.x); break;
|
|
196
|
+
case 0x90: this.branch(!this.c); break;
|
|
197
|
+
case 0x91: this.write(this.izy(), this.a); break;
|
|
198
|
+
case 0x94: this.write(this.zpx(), this.y); break;
|
|
199
|
+
case 0x95: this.write(this.zpx(), this.a); break;
|
|
200
|
+
case 0x96: this.write(this.zpy(), this.x); break;
|
|
201
|
+
case 0x98: this.a = this.nz(this.y); break;
|
|
202
|
+
case 0x99: this.write(this.aby(), this.a); break;
|
|
203
|
+
case 0x9a: this.sp = this.x; break;
|
|
204
|
+
case 0x9d: this.write(this.abx(), this.a); break;
|
|
205
|
+
case 0xa0: this.y = this.nz(this.read(this.imm())); break;
|
|
206
|
+
case 0xa1: this.a = this.nz(this.read(this.izx())); break;
|
|
207
|
+
case 0xa2: this.x = this.nz(this.read(this.imm())); break;
|
|
208
|
+
case 0xa4: this.y = this.nz(this.read(this.zp())); break;
|
|
209
|
+
case 0xa5: this.a = this.nz(this.read(this.zp())); break;
|
|
210
|
+
case 0xa6: this.x = this.nz(this.read(this.zp())); break;
|
|
211
|
+
case 0xa8: this.y = this.nz(this.a); break;
|
|
212
|
+
case 0xa9: this.a = this.nz(this.read(this.imm())); break;
|
|
213
|
+
case 0xaa: this.x = this.nz(this.a); break;
|
|
214
|
+
case 0xac: this.y = this.nz(this.read(this.abs())); break;
|
|
215
|
+
case 0xad: this.a = this.nz(this.read(this.abs())); break;
|
|
216
|
+
case 0xae: this.x = this.nz(this.read(this.abs())); break;
|
|
217
|
+
case 0xb0: this.branch(this.c); break;
|
|
218
|
+
case 0xb1: this.a = this.nz(this.read(this.izy(1))); break;
|
|
219
|
+
case 0xb4: this.y = this.nz(this.read(this.zpx())); break;
|
|
220
|
+
case 0xb5: this.a = this.nz(this.read(this.zpx())); break;
|
|
221
|
+
case 0xb6: this.x = this.nz(this.read(this.zpy())); break;
|
|
222
|
+
case 0xb8: this.v = 0; break;
|
|
223
|
+
case 0xb9: this.a = this.nz(this.read(this.aby(1))); break;
|
|
224
|
+
case 0xba: this.x = this.nz(this.sp); break;
|
|
225
|
+
case 0xbc: this.y = this.nz(this.read(this.abx(1))); break;
|
|
226
|
+
case 0xbd: this.a = this.nz(this.read(this.abx(1))); break;
|
|
227
|
+
case 0xbe: this.x = this.nz(this.read(this.aby(1))); break;
|
|
228
|
+
case 0xc0: this.cmp(this.y, this.read(this.imm())); break;
|
|
229
|
+
case 0xc1: this.cmp(this.a, this.read(this.izx())); break;
|
|
230
|
+
case 0xc4: this.cmp(this.y, this.read(this.zp())); break;
|
|
231
|
+
case 0xc5: this.cmp(this.a, this.read(this.zp())); break;
|
|
232
|
+
case 0xc6: this.dec(this.zp()); break;
|
|
233
|
+
case 0xc8: this.y = this.nz(this.y + 1); break;
|
|
234
|
+
case 0xc9: this.cmp(this.a, this.read(this.imm())); break;
|
|
235
|
+
case 0xca: this.x = this.nz(this.x - 1); break;
|
|
236
|
+
case 0xcc: this.cmp(this.y, this.read(this.abs())); break;
|
|
237
|
+
case 0xcd: this.cmp(this.a, this.read(this.abs())); break;
|
|
238
|
+
case 0xce: this.dec(this.abs()); break;
|
|
239
|
+
case 0xd0: this.branch(!this.z); break;
|
|
240
|
+
case 0xd1: this.cmp(this.a, this.read(this.izy(1))); break;
|
|
241
|
+
case 0xd5: this.cmp(this.a, this.read(this.zpx())); break;
|
|
242
|
+
case 0xd6: this.dec(this.zpx()); break;
|
|
243
|
+
case 0xd8: this.d = 0; break;
|
|
244
|
+
case 0xd9: this.cmp(this.a, this.read(this.aby(1))); break;
|
|
245
|
+
case 0xdd: this.cmp(this.a, this.read(this.abx(1))); break;
|
|
246
|
+
case 0xde: this.dec(this.abx()); break;
|
|
247
|
+
case 0xe0: this.cmp(this.x, this.read(this.imm())); break;
|
|
248
|
+
case 0xe1: this.sbc(this.read(this.izx())); break;
|
|
249
|
+
case 0xe4: this.cmp(this.x, this.read(this.zp())); break;
|
|
250
|
+
case 0xe5: this.sbc(this.read(this.zp())); break;
|
|
251
|
+
case 0xe6: this.inc(this.zp()); break;
|
|
252
|
+
case 0xe8: this.x = this.nz(this.x + 1); break;
|
|
253
|
+
case 0xe9: this.sbc(this.read(this.imm())); break;
|
|
254
|
+
case 0xea: break;
|
|
255
|
+
case 0xec: this.cmp(this.x, this.read(this.abs())); break;
|
|
256
|
+
case 0xed: this.sbc(this.read(this.abs())); break;
|
|
257
|
+
case 0xee: this.inc(this.abs()); break;
|
|
258
|
+
case 0xf0: this.branch(this.z); break;
|
|
259
|
+
case 0xf1: this.sbc(this.read(this.izy(1))); break;
|
|
260
|
+
case 0xf5: this.sbc(this.read(this.zpx())); break;
|
|
261
|
+
case 0xf6: this.inc(this.zpx()); break;
|
|
262
|
+
case 0xf8: this.d = 1; break;
|
|
263
|
+
case 0xf9: this.sbc(this.read(this.aby(1))); break;
|
|
264
|
+
case 0xfd: this.sbc(this.read(this.abx(1))); break;
|
|
265
|
+
case 0xfe: this.inc(this.abx()); break;
|
|
266
|
+
default: {
|
|
267
|
+
if (this.nmos && UNDOCUMENTED.has(op)) { this.undocumented(op); break; }
|
|
268
|
+
const nop = this.nmos && NOPS.get(op);
|
|
269
|
+
if (!nop) throw new Error('undocumented opcode $' + op.toString(16) + ' at $' + (this.pc - 1).toString(16));
|
|
270
|
+
this.cycles += nop[1];
|
|
271
|
+
if (nop[0]) this.read(this[nop[0]](1)); // the operand is fetched and read, as the chip does
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return this.cycles - start;
|
|
275
|
+
}
|
|
276
|
+
// The NMOS chip's undocumented instructions, as the chip itself runs them (perfect6502): two
|
|
277
|
+
// operations in one, on an operand found the usual ways (UNDOCUMENTED, below).
|
|
278
|
+
undocumented(op) {
|
|
279
|
+
const [name, mode, cycles, penalty] = UNDOCUMENTED.get(op);
|
|
280
|
+
this.cycles += cycles;
|
|
281
|
+
if (name === 'JAM') throw new Error('the processor halts (JAM, $' + op.toString(16) + ') at $' + (this.pc - 1).toString(16));
|
|
282
|
+
// SHA, SHX, SHY, TAS: the register stored ANDed with the high byte of the address before
|
|
283
|
+
// indexing, plus one; and where indexing crosses a page, that value is the high byte stored to
|
|
284
|
+
if (name === 'SHA' || name === 'SHX' || name === 'SHY' || name === 'TAS') {
|
|
285
|
+
let base, index;
|
|
286
|
+
if (mode === 'izy') { const z = this.read(this.pc++); base = this.read(z) | this.read((z + 1) & 0xff) << 8; index = this.y; }
|
|
287
|
+
else { base = this.read16(this.pc); this.pc += 2; index = mode === 'abx' ? this.x : this.y; }
|
|
288
|
+
if (name === 'TAS') this.sp = this.a & this.x;
|
|
289
|
+
const reg = name === 'SHA' ? this.a & this.x : name === 'SHX' ? this.x : name === 'SHY' ? this.y : this.sp;
|
|
290
|
+
const v = reg & ((base >> 8) + 1 & 0xff); let at = (base + index) & 0xffff;
|
|
291
|
+
if ((base ^ at) & 0xff00) at = v << 8 | (at & 0xff);
|
|
292
|
+
this.write(at, v); return;
|
|
293
|
+
}
|
|
294
|
+
const at = mode === 'imm' ? this.imm() : this[mode](penalty);
|
|
295
|
+
const v = ['SLO', 'RLA', 'SRE', 'RRA', 'DCP', 'ISC'].includes(name) ? this.modify(at) : this.read(at);
|
|
296
|
+
switch (name) {
|
|
297
|
+
case 'SLO': { this.c = v >> 7; const r = v << 1 & 0xff; this.write(at, r); this.a = this.nz(this.a | r); break; }
|
|
298
|
+
case 'RLA': { const r = (v << 1 | this.c) & 0xff; this.c = v >> 7; this.write(at, r); this.a = this.nz(this.a & r); break; }
|
|
299
|
+
case 'SRE': { this.c = v & 1; const r = v >> 1; this.write(at, r); this.a = this.nz(this.a ^ r); break; }
|
|
300
|
+
case 'RRA': { const r = v >> 1 | this.c << 7; this.c = v & 1; this.write(at, r); this.adc(r); break; }
|
|
301
|
+
case 'SAX': this.write(at, this.a & this.x); break;
|
|
302
|
+
case 'LAX': this.a = this.x = this.nz(v); break;
|
|
303
|
+
case 'DCP': { const r = v - 1 & 0xff; this.write(at, r); this.cmp(this.a, r); break; }
|
|
304
|
+
case 'ISC': { const r = v + 1 & 0xff; this.write(at, r); this.sbc(r); break; }
|
|
305
|
+
case 'ANC': this.a = this.nz(this.a & v); this.c = this.n; break;
|
|
306
|
+
case 'ALR': { const t = this.a & v; this.c = t & 1; this.a = this.nz(t >> 1); break; }
|
|
307
|
+
case 'ARR': {
|
|
308
|
+
const t = this.a & v; let r = t >> 1 | this.c << 7;
|
|
309
|
+
if (!this.d) { this.a = this.nz(r); this.c = r >> 6 & 1; this.v = (r >> 6 ^ r >> 5) & 1; break; }
|
|
310
|
+
// in decimal mode: N from the old carry, Z and V from the rotate, then each digit adjusted
|
|
311
|
+
this.n = this.c; this.z = r === 0 ? 1 : 0; this.v = ((t ^ r) & 0x40) ? 1 : 0;
|
|
312
|
+
if ((t & 0x0f) + (t & 0x01) > 5) r = (r & 0xf0) | ((r + 6) & 0x0f);
|
|
313
|
+
if ((t & 0xf0) + (t & 0x10) > 0x50) { r = (r + 0x60) & 0xff; this.c = 1; } else this.c = 0;
|
|
314
|
+
this.a = r; break;
|
|
315
|
+
}
|
|
316
|
+
case 'SBX': { const d = (this.a & this.x) - v; this.c = d >= 0 ? 1 : 0; this.x = this.nz(d); break; }
|
|
317
|
+
case 'SBC': this.sbc(v); break;
|
|
318
|
+
case 'ANE': this.a = this.nz((this.a | ANE_MAGIC) & this.x & v); break;
|
|
319
|
+
case 'LXA': this.a = this.x = this.nz((this.a | ANE_MAGIC) & v); break;
|
|
320
|
+
case 'LAS': this.a = this.x = this.sp = this.nz(v & this.sp); break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// The undocumented instructions an NMOS 6502 runs, by opcode: the operation, how the operand is
|
|
326
|
+
// found, the cycles, and whether an indexed read crossing a page takes one more. ANE and LXA mix in
|
|
327
|
+
// a constant that differs from chip to chip; ANE_MAGIC is perfect6502's.
|
|
328
|
+
const ANE_MAGIC = 0xee;
|
|
329
|
+
const UNDOCUMENTED = new Map([
|
|
330
|
+
...[['SLO', 0x03], ['RLA', 0x23], ['SRE', 0x43], ['RRA', 0x63], ['DCP', 0xc3], ['ISC', 0xe3]].flatMap(([n, b]) =>
|
|
331
|
+
[[b, [n, 'izx', 8]], [b + 4, [n, 'zp', 5]], [b + 0xc, [n, 'abs', 6]], [b + 0x10, [n, 'izy', 8]], [b + 0x14, [n, 'zpx', 6]], [b + 0x18, [n, 'aby', 7]], [b + 0x1c, [n, 'abx', 7]]]),
|
|
332
|
+
[0x83, ['SAX', 'izx', 6]], [0x87, ['SAX', 'zp', 3]], [0x8f, ['SAX', 'abs', 4]], [0x97, ['SAX', 'zpy', 4]],
|
|
333
|
+
[0xa3, ['LAX', 'izx', 6]], [0xa7, ['LAX', 'zp', 3]], [0xaf, ['LAX', 'abs', 4]], [0xb3, ['LAX', 'izy', 5, 1]], [0xb7, ['LAX', 'zpy', 4]], [0xbf, ['LAX', 'aby', 4, 1]],
|
|
334
|
+
[0x0b, ['ANC', 'imm', 2]], [0x2b, ['ANC', 'imm', 2]], [0x4b, ['ALR', 'imm', 2]], [0x6b, ['ARR', 'imm', 2]], [0xcb, ['SBX', 'imm', 2]], [0xeb, ['SBC', 'imm', 2]],
|
|
335
|
+
[0x8b, ['ANE', 'imm', 2]], [0xab, ['LXA', 'imm', 2]],
|
|
336
|
+
[0x93, ['SHA', 'izy', 6]], [0x9f, ['SHA', 'aby', 5]], [0x9e, ['SHX', 'aby', 5]], [0x9c, ['SHY', 'abx', 5]], [0x9b, ['TAS', 'aby', 5]], [0xbb, ['LAS', 'aby', 4, 1]],
|
|
337
|
+
...[0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xb2, 0xd2, 0xf2].map(op => [op, ['JAM', null, 0]]),
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
// base cycles for each opcode, 00 to FF
|
|
341
|
+
// the undocumented NOPs an NMOS 6502 runs: by opcode, how the operand is found and the cycles, with
|
|
342
|
+
// one more for an absolute indexed one that crosses a page
|
|
343
|
+
const NOPS = new Map([
|
|
344
|
+
...[0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa].map(op => [op, [null, 2]]),
|
|
345
|
+
...[0x80, 0x82, 0x89, 0xc2, 0xe2].map(op => [op, ['imm', 2]]),
|
|
346
|
+
...[0x04, 0x44, 0x64].map(op => [op, ['zp', 3]]),
|
|
347
|
+
...[0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4].map(op => [op, ['zpx', 4]]),
|
|
348
|
+
[0x0c, ['abs', 4]],
|
|
349
|
+
...[0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc].map(op => [op, ['abx', 4]]),
|
|
350
|
+
]);
|
|
351
|
+
|
|
352
|
+
export const CYCLES = [
|
|
353
|
+
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
|
354
|
+
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
|
355
|
+
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
|
356
|
+
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
|
357
|
+
0, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
|
|
358
|
+
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
|
|
359
|
+
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
|
360
|
+
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0];
|
package/disasm.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// A 6502 disassembler: the documented opcodes, each line an address, its bytes and the instruction.
|
|
2
|
+
// A byte that is no documented opcode is written as a .byte line, since the code also holds data.
|
|
3
|
+
|
|
4
|
+
// mnemonic and addressing mode for each opcode, 00 to FF, 16 a line; ??? is no documented opcode
|
|
5
|
+
const TABLE = `
|
|
6
|
+
BRK imp ORA izx ??? - ??? - ??? - ORA zp ASL zp ??? - PHP imp ORA imm ASL acc ??? - ??? - ORA abs ASL abs ??? -
|
|
7
|
+
BPL rel ORA izy ??? - ??? - ??? - ORA zpx ASL zpx ??? - CLC imp ORA aby ??? - ??? - ??? - ORA abx ASL abx ??? -
|
|
8
|
+
JSR abs AND izx ??? - ??? - BIT zp AND zp ROL zp ??? - PLP imp AND imm ROL acc ??? - BIT abs AND abs ROL abs ??? -
|
|
9
|
+
BMI rel AND izy ??? - ??? - ??? - AND zpx ROL zpx ??? - SEC imp AND aby ??? - ??? - ??? - AND abx ROL abx ??? -
|
|
10
|
+
RTI imp EOR izx ??? - ??? - ??? - EOR zp LSR zp ??? - PHA imp EOR imm LSR acc ??? - JMP abs EOR abs LSR abs ??? -
|
|
11
|
+
BVC rel EOR izy ??? - ??? - ??? - EOR zpx LSR zpx ??? - CLI imp EOR aby ??? - ??? - ??? - EOR abx LSR abx ??? -
|
|
12
|
+
RTS imp ADC izx ??? - ??? - ??? - ADC zp ROR zp ??? - PLA imp ADC imm ROR acc ??? - JMP ind ADC abs ROR abs ??? -
|
|
13
|
+
BVS rel ADC izy ??? - ??? - ??? - ADC zpx ROR zpx ??? - SEI imp ADC aby ??? - ??? - ??? - ADC abx ROR abx ??? -
|
|
14
|
+
??? - STA izx ??? - ??? - STY zp STA zp STX zp ??? - DEY imp ??? - TXA imp ??? - STY abs STA abs STX abs ??? -
|
|
15
|
+
BCC rel STA izy ??? - ??? - STY zpx STA zpx STX zpy ??? - TYA imp STA aby TXS imp ??? - ??? - STA abx ??? - ??? -
|
|
16
|
+
LDY imm LDA izx LDX imm ??? - LDY zp LDA zp LDX zp ??? - TAY imp LDA imm TAX imp ??? - LDY abs LDA abs LDX abs ??? -
|
|
17
|
+
BCS rel LDA izy ??? - ??? - LDY zpx LDA zpx LDX zpy ??? - CLV imp LDA aby TSX imp ??? - LDY abx LDA abx LDX aby ??? -
|
|
18
|
+
CPY imm CMP izx ??? - ??? - CPY zp CMP zp DEC zp ??? - INY imp CMP imm DEX imp ??? - CPY abs CMP abs DEC abs ??? -
|
|
19
|
+
BNE rel CMP izy ??? - ??? - ??? - CMP zpx DEC zpx ??? - CLD imp CMP aby ??? - ??? - ??? - CMP abx DEC abx ??? -
|
|
20
|
+
CPX imm SBC izx ??? - ??? - CPX zp SBC zp INC zp ??? - INX imp SBC imm NOP imp ??? - CPX abs SBC abs INC abs ??? -
|
|
21
|
+
BEQ rel SBC izy ??? - ??? - ??? - SBC zpx INC zpx ??? - SED imp SBC aby ??? - ??? - ??? - SBC abx INC abx ??? -
|
|
22
|
+
`.trim().split(/\s+/);
|
|
23
|
+
const OPS = [];
|
|
24
|
+
for (let i = 0; i < 256; i++) OPS.push({ name: TABLE[i * 2], mode: TABLE[i * 2 + 1] });
|
|
25
|
+
const SIZE = { imp: 1, acc: 1, imm: 2, zp: 2, zpx: 2, zpy: 2, izx: 2, izy: 2, rel: 2, abs: 3, abx: 3, aby: 3, ind: 3 };
|
|
26
|
+
|
|
27
|
+
const hex = (n, w) => n.toString(16).toUpperCase().padStart(w, '0');
|
|
28
|
+
|
|
29
|
+
function operand(mode, bytes, pc) {
|
|
30
|
+
const b = bytes[1], w = bytes[1] | bytes[2] << 8;
|
|
31
|
+
switch (mode) {
|
|
32
|
+
case 'imp': return '';
|
|
33
|
+
case 'acc': return 'A';
|
|
34
|
+
case 'imm': return '#$' + hex(b, 2);
|
|
35
|
+
case 'zp': return '$' + hex(b, 2);
|
|
36
|
+
case 'zpx': return '$' + hex(b, 2) + ',X';
|
|
37
|
+
case 'zpy': return '$' + hex(b, 2) + ',Y';
|
|
38
|
+
case 'izx': return '($' + hex(b, 2) + ',X)';
|
|
39
|
+
case 'izy': return '($' + hex(b, 2) + '),Y';
|
|
40
|
+
case 'rel': return '$' + hex((pc + 2 + (b < 128 ? b : b - 256)) & 0xffff, 4);
|
|
41
|
+
case 'abs': return '$' + hex(w, 4);
|
|
42
|
+
case 'abx': return '$' + hex(w, 4) + ',X';
|
|
43
|
+
case 'aby': return '$' + hex(w, 4) + ',Y';
|
|
44
|
+
case 'ind': return '($' + hex(w, 4) + ')';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function disassemble(body, origin) {
|
|
49
|
+
const lines = [];
|
|
50
|
+
for (let at = 0; at < body.length; ) {
|
|
51
|
+
const op = OPS[body[at]], pc = origin + at;
|
|
52
|
+
const size = op.name === '???' ? 1 : SIZE[op.mode];
|
|
53
|
+
if (at + size > body.length || op.name === '???') {
|
|
54
|
+
lines.push(hex(pc, 4) + ' ' + hex(body[at], 2).padEnd(9) + ' .byte $' + hex(body[at], 2));
|
|
55
|
+
at++; continue;
|
|
56
|
+
}
|
|
57
|
+
const bytes = body.subarray(at, at + size);
|
|
58
|
+
lines.push(hex(pc, 4) + ' ' + [...bytes].map(b => hex(b, 2)).join(' ').padEnd(9) + ' ' + (op.name + ' ' + operand(op.mode, bytes, pc)).trim());
|
|
59
|
+
at += size;
|
|
60
|
+
}
|
|
61
|
+
return lines.join('\n') + '\n';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { disassemble, OPS, SIZE, operand, hex };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*! @moonbase-machines/mos6502 0.5.0: MIT, (c) 2026 Mark Wharton (its LICENSE) */
|
|
2
|
+
var ts=Object.defineProperty;var Y=(a,s)=>{for(var i in s)ts(a,i,{get:s[i],enumerable:!0})};var W={};Y(W,{AsmError:()=>E,assemble:()=>es});var _={};Y(_,{OPS:()=>O,SIZE:()=>g,disassemble:()=>is,hex:()=>A,operand:()=>V});var F=`
|
|
3
|
+
BRK imp ORA izx ??? - ??? - ??? - ORA zp ASL zp ??? - PHP imp ORA imm ASL acc ??? - ??? - ORA abs ASL abs ??? -
|
|
4
|
+
BPL rel ORA izy ??? - ??? - ??? - ORA zpx ASL zpx ??? - CLC imp ORA aby ??? - ??? - ??? - ORA abx ASL abx ??? -
|
|
5
|
+
JSR abs AND izx ??? - ??? - BIT zp AND zp ROL zp ??? - PLP imp AND imm ROL acc ??? - BIT abs AND abs ROL abs ??? -
|
|
6
|
+
BMI rel AND izy ??? - ??? - ??? - AND zpx ROL zpx ??? - SEC imp AND aby ??? - ??? - ??? - AND abx ROL abx ??? -
|
|
7
|
+
RTI imp EOR izx ??? - ??? - ??? - EOR zp LSR zp ??? - PHA imp EOR imm LSR acc ??? - JMP abs EOR abs LSR abs ??? -
|
|
8
|
+
BVC rel EOR izy ??? - ??? - ??? - EOR zpx LSR zpx ??? - CLI imp EOR aby ??? - ??? - ??? - EOR abx LSR abx ??? -
|
|
9
|
+
RTS imp ADC izx ??? - ??? - ??? - ADC zp ROR zp ??? - PLA imp ADC imm ROR acc ??? - JMP ind ADC abs ROR abs ??? -
|
|
10
|
+
BVS rel ADC izy ??? - ??? - ??? - ADC zpx ROR zpx ??? - SEI imp ADC aby ??? - ??? - ??? - ADC abx ROR abx ??? -
|
|
11
|
+
??? - STA izx ??? - ??? - STY zp STA zp STX zp ??? - DEY imp ??? - TXA imp ??? - STY abs STA abs STX abs ??? -
|
|
12
|
+
BCC rel STA izy ??? - ??? - STY zpx STA zpx STX zpy ??? - TYA imp STA aby TXS imp ??? - ??? - STA abx ??? - ??? -
|
|
13
|
+
LDY imm LDA izx LDX imm ??? - LDY zp LDA zp LDX zp ??? - TAY imp LDA imm TAX imp ??? - LDY abs LDA abs LDX abs ??? -
|
|
14
|
+
BCS rel LDA izy ??? - ??? - LDY zpx LDA zpx LDX zpy ??? - CLV imp LDA aby TSX imp ??? - LDY abx LDA abx LDX aby ??? -
|
|
15
|
+
CPY imm CMP izx ??? - ??? - CPY zp CMP zp DEC zp ??? - INY imp CMP imm DEX imp ??? - CPY abs CMP abs DEC abs ??? -
|
|
16
|
+
BNE rel CMP izy ??? - ??? - ??? - CMP zpx DEC zpx ??? - CLD imp CMP aby ??? - ??? - ??? - CMP abx DEC abx ??? -
|
|
17
|
+
CPX imm SBC izx ??? - ??? - CPX zp SBC zp INC zp ??? - INX imp SBC imm NOP imp ??? - CPX abs SBC abs INC abs ??? -
|
|
18
|
+
BEQ rel SBC izy ??? - ??? - ??? - SBC zpx INC zpx ??? - SED imp SBC aby ??? - ??? - ??? - SBC abx INC abx ??? -
|
|
19
|
+
`.trim().split(/\s+/),O=[];for(let a=0;a<256;a++)O.push({name:F[a*2],mode:F[a*2+1]});var g={imp:1,acc:1,imm:2,zp:2,zpx:2,zpy:2,izx:2,izy:2,rel:2,abs:3,abx:3,aby:3,ind:3},A=(a,s)=>a.toString(16).toUpperCase().padStart(s,"0");function V(a,s,i){let t=s[1],h=s[1]|s[2]<<8;switch(a){case"imp":return"";case"acc":return"A";case"imm":return"#$"+A(t,2);case"zp":return"$"+A(t,2);case"zpx":return"$"+A(t,2)+",X";case"zpy":return"$"+A(t,2)+",Y";case"izx":return"($"+A(t,2)+",X)";case"izy":return"($"+A(t,2)+"),Y";case"rel":return"$"+A(i+2+(t<128?t:t-256)&65535,4);case"abs":return"$"+A(h,4);case"abx":return"$"+A(h,4)+",X";case"aby":return"$"+A(h,4)+",Y";case"ind":return"($"+A(h,4)+")"}}function is(a,s){let i=[];for(let t=0;t<a.length;){let h=O[a[t]],p=s+t,n=h.name==="???"?1:g[h.mode];if(t+n>a.length||h.name==="???"){i.push(A(p,4)+" "+A(a[t],2).padEnd(9)+" .byte $"+A(a[t],2)),t++;continue}let o=a.subarray(t,t+n);i.push(A(p,4)+" "+[...o].map(e=>A(e,2)).join(" ").padEnd(9)+" "+(h.name+" "+V(h.mode,o,p)).trim()),t+=n}return i.join(`
|
|
20
|
+
`)+`
|
|
21
|
+
`}var j={};O.forEach((a,s)=>{a.name!=="???"&&((j[a.name]??={})[a.mode]=s)});var E=class extends Error{},I=a=>{let s=a.charCodeAt(0);return s>=97&&s<=122?s-32:s===95?164:s},q=a=>{let s=I(a);return s>=64&&s<96?s-64:s},as=a=>{let s=a.charCodeAt(0);return s>=97&&s<=122?s-96:s>=65&&s<=90?s:q(a)};function es(a,{file:s="source",include:i=null}={}){let t=new Map,h=[],p,n,o,e,d,P,L,D,X=[],J=(r,z,c=0)=>r.split(/\r?\n/).forEach((u,x)=>{let b=u.match(/^\s*\.include\s+"([^"]+)"/i);if(!b)return X.push({line:u,file:z,n:x+1});if(!i)throw new E(`${z}:${x+1}: .include, but no way to read "${b[1]}"`);if(c>8)throw new E(`${z}:${x+1}: .include too deep`);J(i(b[1]),b[1],c+1)});J(a,s);let m=r=>{throw new E(`${L.file}:${L.n}: ${r}`)},v=r=>r.startsWith(".")?(P??m(`${r} before any label`))+r:r;function $(r){let z=r.match(/\$[0-9a-f]+|%[01]+|\d+|'(?:[^'\\]|\\.)'|[.@]?[a-z_][\w.]*|<<|>>|[-+*/%&|^~<>()]/gi);(!z||z.join("").replace(/\s/g,"")!==r.replace(/\s/g,""))&&m(`can't read "${r}"`);let c=0,u=()=>z[c],x=()=>z[c++],b=[["|"],["^"],["&"],["<<",">>"],["+","-"],["*","/","%"]],l=f=>{if(f===b.length)return C();let y=l(f+1);for(;b[f].includes(u());){let k=x(),S=l(f+1);y=y===void 0||S===void 0?void 0:k==="|"?y|S:k==="^"?y^S:k==="&"?y&S:k==="<<"?y<<S:k===">>"?y>>S:k==="+"?y+S:k==="-"?y-S:k==="*"?y*S:k==="/"?Math.trunc(y/S):y%S}return y},C=()=>{let f=x();if(f===void 0&&m(`"${r}" ends too soon`),f==="-"||f==="~"||f==="<"||f===">"){let k=C();return k===void 0?void 0:f==="-"?-k:f==="~"?~k&65535:f==="<"?k&255:k>>8&255}if(f==="("){let k=l(0);return x()!==")"&&m(`a ( without its ) in "${r}"`),k}if(f==="*")return n;if(f[0]==="$")return parseInt(f.slice(1),16);if(f[0]==="%")return parseInt(f.slice(1),2);if(/^\d/.test(f))return parseInt(f,10);if(f[0]==="'")return I(f[1]==="\\"?f[2]:f[1]);let y=v(f);if(t.has(y))return t.get(y);d===2&&m(`${y}: not defined`)},w=l(0);return c<z.length&&m(`"${r}": more than one expression`),w}let R=r=>{n>65535&&m("past $FFFF"),p[n]=r&255,o=Math.min(o,n),e=Math.max(e,n),n++},B=r=>r.match(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)'|[^,])+/g)?.map(z=>z.trim())??[],U=(r,z)=>{d===1&&t.has(r)&&t.get(r)!==z&&m(`${r} defined twice`),d===2&&t.has(r)&&t.get(r)!==z&&m(`${r} moved between the passes`),t.set(r,z)};function ss(r,z){let c=j[r]??m(`${r}: no such instruction`),u=D++,x,b,l=z.trim();if(!l||/^a$/i.test(l))x=c.imp!==void 0?"imp":c.acc!==void 0?"acc":m(`${r} needs an operand`);else if(l[0]==="#")x="imm",b=$(l.slice(1));else{let w;if(w=l.match(/^\((.+),\s*x\s*\)$/i))x="izx",b=$(w[1]);else if(w=l.match(/^\((.+)\)\s*,\s*y$/i))x="izy",b=$(w[1]);else if(c.ind!==void 0&&(w=l.match(/^\((.+)\)$/)))x="ind",b=$(w[1]);else{let[,f,y]=l.match(/^(.+?)(?:\s*,\s*([xy]))?$/i);b=$(f);let k=y?.toLowerCase();if(c.rel!==void 0)x="rel";else{let S=k==="x"?"zpx":k==="y"?"zpy":"zp",M=k==="x"?"abx":k==="y"?"aby":"abs";d===1&&(h[u]=b!==void 0&&b<256&&c[S]!==void 0?S:M),x=h[u],c[x]===void 0&&(x=c[M]!==void 0?M:c[S]!==void 0?S:m(`${r} has no ${l} form`))}}}c[x]===void 0&&m(`${r} has no ${x} form`),R(c[x]);let C=b??0;if(x==="rel"){let w=C-(n+1);d===2&&(w<-128||w>127)&&m(`${r} ${l}: ${w} bytes away, a branch reaches -128 to 127`),R(w)}else g[x]===2?(d===2&&(C<-128||C>255)&&m(`${l}: ${C} is more than a byte`),R(C)):g[x]===3&&(R(C),R(C>>8))}for(d=1;d<=2;d++){p=new Uint8Array(65536),n=4096,o=65536,e=-1,P=null,D=0;let r=!1;for(L of X){let z=L.line.replace(/;.*$/,"").trim();if(/["']/.test(L.line)){let u=null,x=0;for(;x<L.line.length;x++){let b=L.line[x];if(u)b===u&&(u=null);else if(b==='"'||b==="'")u=b;else if(b===";")break}z=L.line.slice(0,x).trim()}if(!z)continue;let c=z.match(/^([.@]?[a-z_][\w]*):\s*(.*)$/i);if(!(c&&(c[1].startsWith(".")||(P=c[1]),U(v(c[1]),n),z=c[2],!z))){if(c=z.match(/^\*\s*=\s*(.+)$/)){n=$(c[1])??m("*= needs a value the first pass knows"),r=!0;continue}if(c=z.match(/^([a-z_]\w*)\s*=\s*(.+)$/i)){let u=$(c[2]);u!==void 0?U(c[1],u):d===2&&m(`${c[1]}: no value`);continue}if(r||m("no *= before the first byte"),c=z.match(/^\.(\w+)\s*(.*)$/)){let[,u,x]=c;switch(u.toLowerCase()){case"byte":case"text":case"screen":case"mixed":for(let b of B(x))if(b[0]==='"'){let l={screen:q,mixed:as}[u.toLowerCase()]??I;for(let C of b.slice(1,-1).replace(/\\(.)/g,"$1"))R(l(C))}else{let l=$(b);d===2&&(l<-128||l>255)&&m(`${b}: more than a byte`),R(l??0)}break;case"word":for(let b of B(x)){let l=$(b)??0;R(l),R(l>>8)}break;case"fill":{let[b,l="0"]=B(x),C=$(b)??m(".fill needs a count the first pass knows"),w=$(l)??0;for(let f=0;f<C;f++)R(w);break}default:m(`.${u}: no such directive`)}continue}if(c=z.match(/^([a-z]{3})\b\s*(.*)$/i)){ss(c[1].toUpperCase(),c[2]);continue}m(`can't read "${z}"`)}}}if(e<0)throw new E(`${s}: nothing assembled`);let N=p.slice(o,e+1),T=new Uint8Array(N.length+2);return T[0]=o&255,T[1]=o>>8,T.set(N,2),{origin:o,bytes:N,prg:T,symbols:t}}var Q={};Y(Q,{CPU:()=>H,CYCLES:()=>K});var H=class{constructor(s,{nmos:i=!1,writeBack:t=!1}={}){this.m=s,this.nmos=i,this.writeBack=t,this.a=0,this.x=0,this.y=0,this.sp=253,this.pc=0,this.n=0,this.v=0,this.d=0,this.i=1,this.z=0,this.c=0,this.hooks=new Map,this.cycles=0}get p(){return this.n<<7|this.v<<6|32|this.d<<3|this.i<<2|this.z<<1|this.c}set p(s){this.n=s>>7&1,this.v=s>>6&1,this.d=s>>3&1,this.i=s>>2&1,this.z=s>>1&1,this.c=s&1}read(s){return this.m.read(s&65535)}write(s,i){this.m.write(s&65535,i&255)}read16(s){return this.read(s)|this.read(s+1)<<8}push(s){this.write(256|this.sp,s),this.sp=this.sp-1&255}pull(){return this.sp=this.sp+1&255,this.read(256|this.sp)}nz(s){return s&=255,this.n=s>>7,this.z=s===0?1:0,s}branch(s){let i=this.read(this.pc++);if(s){let t=this.pc+(i<128?i:i-256)&65535;this.cycles+=(t^this.pc)&65280?2:1,this.pc=t}}imm(){return this.pc++}zp(){return this.read(this.pc++)}zpx(){return this.read(this.pc++)+this.x&255}zpy(){return this.read(this.pc++)+this.y&255}abs(){let s=this.read16(this.pc);return this.pc+=2,s}abx(s){let i=this.abs();return s&&(i+this.x^i)&65280&&this.cycles++,i+this.x&65535}aby(s){let i=this.abs();return s&&(i+this.y^i)&65280&&this.cycles++,i+this.y&65535}izx(){let s=this.read(this.pc++)+this.x&255;return this.read(s)|this.read(s+1&255)<<8}izy(s){let i=this.read(this.pc++),t=this.read(i)|this.read(i+1&255)<<8;return s&&(t+this.y^t)&65280&&this.cycles++,t+this.y&65535}adc(s){if(this.d&&this.nmos){let t=this.a,h=(t&15)+(s&15)+this.c;h>=10&&(h=(h+6&15)+16);let p=(t&240)+(s&240)+h;this.z=(t+s+this.c&255)===0?1:0,this.n=p>>7&1,this.v=~(t^s)&(t^p)&128?1:0,p>=160&&(p+=96),this.c=p>=256?1:0,this.a=p&255;return}if(this.d){let t=(this.a&15)+(s&15)+this.c,h=(this.a>>4)+(s>>4);t>9&&(t-=10,h++);let p=this.a+s+this.c&255;this.z=p===0?1:0,this.n=h&8?1:0,this.v=~(this.a^s)&(this.a^h<<4)&128?1:0,h>9?(h-=10,this.c=1):this.c=0,this.a=(h<<4|t)&255;return}let i=this.a+s+this.c;this.v=~(this.a^s)&(this.a^i)&128?1:0,this.c=i>255?1:0,this.a=this.nz(i)}sbc(s){if(this.d&&this.nmos){let t=this.a,h=t-s-(1-this.c),p=(t&15)-(s&15)-(1-this.c);p<0&&(p=(p-6&15)-16);let n=(t&240)-(s&240)+p;n<0&&(n-=96),this.z=(h&255)===0?1:0,this.n=h&128?1:0,this.v=(t^s)&(t^h)&128?1:0,this.c=h>=0?1:0,this.a=n&255;return}if(this.d){let t=(this.a&15)-(s&15)-(1-this.c),h=(this.a>>4)-(s>>4);t<0&&(t+=10,h--);let p=this.a-s-(1-this.c);this.z=(p&255)===0?1:0,this.n=p&128?1:0,this.v=(this.a^s)&(this.a^p)&128?1:0,this.c=p>=0?1:0,h<0&&(h+=10),this.a=(h<<4|t)&255;return}let i=this.a-s-(1-this.c);this.v=(this.a^s)&(this.a^i)&128?1:0,this.c=i>=0?1:0,this.a=this.nz(i)}cmp(s,i){let t=s-i;this.c=t>=0?1:0,this.nz(t)}modify(s){let i=this.read(s);return this.writeBack&&this.write(s,i),i}asl(s){let i=s===null?this.a:this.modify(s);this.c=i>>7;let t=this.nz(i<<1);s===null?this.a=t:this.write(s,t)}lsr(s){let i=s===null?this.a:this.modify(s);this.c=i&1;let t=this.nz(i>>1);s===null?this.a=t:this.write(s,t)}rol(s){let i=s===null?this.a:this.modify(s),t=this.nz(i<<1|this.c);this.c=i>>7,s===null?this.a=t:this.write(s,t)}ror(s){let i=s===null?this.a:this.modify(s),t=this.nz(i>>1|this.c<<7);this.c=i&1,s===null?this.a=t:this.write(s,t)}inc(s){this.write(s,this.nz(this.modify(s)+1))}dec(s){this.write(s,this.nz(this.modify(s)-1))}bit(s){this.n=s>>7,this.v=s>>6&1,this.z=(this.a&s)===0?1:0}irq(){return this.i?!1:(this.push(this.pc>>8),this.push(this.pc&255),this.push(this.p&239),this.i=1,this.pc=this.read16(65534),this.cycles+=7,!0)}nmi(){this.push(this.pc>>8),this.push(this.pc&255),this.push(this.p&239),this.i=1,this.pc=this.read16(65530),this.cycles+=7}step(){for(let t=this.hooks.get(this.pc),h=0;t&&h<8;t=this.hooks.get(this.pc),h++){let p=this.pc,n=t(this);if(n==="wait")return this.cycles+=10,10;if(n!==!1)return this.pc=(this.pull()|this.pull()<<8)+1&65535,this.cycles+=6,6;if(this.pc===p)break}let s=this.cycles;this.trace?.(this.pc);let i=this.read(this.pc++);switch(this.cycles+=K[i],i){case 0:this.pc++,this.push(this.pc>>8),this.push(this.pc&255),this.push(this.p|16),this.i=1,this.pc=this.read16(65534);break;case 1:this.a=this.nz(this.a|this.read(this.izx()));break;case 5:this.a=this.nz(this.a|this.read(this.zp()));break;case 6:this.asl(this.zp());break;case 8:this.push(this.p|16);break;case 9:this.a=this.nz(this.a|this.read(this.imm()));break;case 10:this.asl(null);break;case 13:this.a=this.nz(this.a|this.read(this.abs()));break;case 14:this.asl(this.abs());break;case 16:this.branch(!this.n);break;case 17:this.a=this.nz(this.a|this.read(this.izy(1)));break;case 21:this.a=this.nz(this.a|this.read(this.zpx()));break;case 22:this.asl(this.zpx());break;case 24:this.c=0;break;case 25:this.a=this.nz(this.a|this.read(this.aby(1)));break;case 29:this.a=this.nz(this.a|this.read(this.abx(1)));break;case 30:this.asl(this.abx());break;case 32:{let t=this.abs();this.pc--,this.push(this.pc>>8),this.push(this.pc&255),this.pc=t;break}case 33:this.a=this.nz(this.a&this.read(this.izx()));break;case 36:this.bit(this.read(this.zp()));break;case 37:this.a=this.nz(this.a&this.read(this.zp()));break;case 38:this.rol(this.zp());break;case 40:this.p=this.pull();break;case 41:this.a=this.nz(this.a&this.read(this.imm()));break;case 42:this.rol(null);break;case 44:this.bit(this.read(this.abs()));break;case 45:this.a=this.nz(this.a&this.read(this.abs()));break;case 46:this.rol(this.abs());break;case 48:this.branch(this.n);break;case 49:this.a=this.nz(this.a&this.read(this.izy(1)));break;case 53:this.a=this.nz(this.a&this.read(this.zpx()));break;case 54:this.rol(this.zpx());break;case 56:this.c=1;break;case 57:this.a=this.nz(this.a&this.read(this.aby(1)));break;case 61:this.a=this.nz(this.a&this.read(this.abx(1)));break;case 62:this.rol(this.abx());break;case 64:this.p=this.pull(),this.pc=this.pull()|this.pull()<<8;break;case 65:this.a=this.nz(this.a^this.read(this.izx()));break;case 69:this.a=this.nz(this.a^this.read(this.zp()));break;case 70:this.lsr(this.zp());break;case 72:this.push(this.a);break;case 73:this.a=this.nz(this.a^this.read(this.imm()));break;case 74:this.lsr(null);break;case 76:this.pc=this.abs();break;case 77:this.a=this.nz(this.a^this.read(this.abs()));break;case 78:this.lsr(this.abs());break;case 80:this.branch(!this.v);break;case 81:this.a=this.nz(this.a^this.read(this.izy(1)));break;case 85:this.a=this.nz(this.a^this.read(this.zpx()));break;case 86:this.lsr(this.zpx());break;case 88:this.i=0;break;case 89:this.a=this.nz(this.a^this.read(this.aby(1)));break;case 93:this.a=this.nz(this.a^this.read(this.abx(1)));break;case 94:this.lsr(this.abx());break;case 96:this.pc=(this.pull()|this.pull()<<8)+1&65535;break;case 97:this.adc(this.read(this.izx()));break;case 101:this.adc(this.read(this.zp()));break;case 102:this.ror(this.zp());break;case 104:this.a=this.nz(this.pull());break;case 105:this.adc(this.read(this.imm()));break;case 106:this.ror(null);break;case 108:{let t=this.abs();this.pc=this.read(t)|this.read(t&65280|t+1&255)<<8;break}case 109:this.adc(this.read(this.abs()));break;case 110:this.ror(this.abs());break;case 112:this.branch(this.v);break;case 113:this.adc(this.read(this.izy(1)));break;case 117:this.adc(this.read(this.zpx()));break;case 118:this.ror(this.zpx());break;case 120:this.i=1;break;case 121:this.adc(this.read(this.aby(1)));break;case 125:this.adc(this.read(this.abx(1)));break;case 126:this.ror(this.abx());break;case 129:this.write(this.izx(),this.a);break;case 132:this.write(this.zp(),this.y);break;case 133:this.write(this.zp(),this.a);break;case 134:this.write(this.zp(),this.x);break;case 136:this.y=this.nz(this.y-1);break;case 138:this.a=this.nz(this.x);break;case 140:this.write(this.abs(),this.y);break;case 141:this.write(this.abs(),this.a);break;case 142:this.write(this.abs(),this.x);break;case 144:this.branch(!this.c);break;case 145:this.write(this.izy(),this.a);break;case 148:this.write(this.zpx(),this.y);break;case 149:this.write(this.zpx(),this.a);break;case 150:this.write(this.zpy(),this.x);break;case 152:this.a=this.nz(this.y);break;case 153:this.write(this.aby(),this.a);break;case 154:this.sp=this.x;break;case 157:this.write(this.abx(),this.a);break;case 160:this.y=this.nz(this.read(this.imm()));break;case 161:this.a=this.nz(this.read(this.izx()));break;case 162:this.x=this.nz(this.read(this.imm()));break;case 164:this.y=this.nz(this.read(this.zp()));break;case 165:this.a=this.nz(this.read(this.zp()));break;case 166:this.x=this.nz(this.read(this.zp()));break;case 168:this.y=this.nz(this.a);break;case 169:this.a=this.nz(this.read(this.imm()));break;case 170:this.x=this.nz(this.a);break;case 172:this.y=this.nz(this.read(this.abs()));break;case 173:this.a=this.nz(this.read(this.abs()));break;case 174:this.x=this.nz(this.read(this.abs()));break;case 176:this.branch(this.c);break;case 177:this.a=this.nz(this.read(this.izy(1)));break;case 180:this.y=this.nz(this.read(this.zpx()));break;case 181:this.a=this.nz(this.read(this.zpx()));break;case 182:this.x=this.nz(this.read(this.zpy()));break;case 184:this.v=0;break;case 185:this.a=this.nz(this.read(this.aby(1)));break;case 186:this.x=this.nz(this.sp);break;case 188:this.y=this.nz(this.read(this.abx(1)));break;case 189:this.a=this.nz(this.read(this.abx(1)));break;case 190:this.x=this.nz(this.read(this.aby(1)));break;case 192:this.cmp(this.y,this.read(this.imm()));break;case 193:this.cmp(this.a,this.read(this.izx()));break;case 196:this.cmp(this.y,this.read(this.zp()));break;case 197:this.cmp(this.a,this.read(this.zp()));break;case 198:this.dec(this.zp());break;case 200:this.y=this.nz(this.y+1);break;case 201:this.cmp(this.a,this.read(this.imm()));break;case 202:this.x=this.nz(this.x-1);break;case 204:this.cmp(this.y,this.read(this.abs()));break;case 205:this.cmp(this.a,this.read(this.abs()));break;case 206:this.dec(this.abs());break;case 208:this.branch(!this.z);break;case 209:this.cmp(this.a,this.read(this.izy(1)));break;case 213:this.cmp(this.a,this.read(this.zpx()));break;case 214:this.dec(this.zpx());break;case 216:this.d=0;break;case 217:this.cmp(this.a,this.read(this.aby(1)));break;case 221:this.cmp(this.a,this.read(this.abx(1)));break;case 222:this.dec(this.abx());break;case 224:this.cmp(this.x,this.read(this.imm()));break;case 225:this.sbc(this.read(this.izx()));break;case 228:this.cmp(this.x,this.read(this.zp()));break;case 229:this.sbc(this.read(this.zp()));break;case 230:this.inc(this.zp());break;case 232:this.x=this.nz(this.x+1);break;case 233:this.sbc(this.read(this.imm()));break;case 234:break;case 236:this.cmp(this.x,this.read(this.abs()));break;case 237:this.sbc(this.read(this.abs()));break;case 238:this.inc(this.abs());break;case 240:this.branch(this.z);break;case 241:this.sbc(this.read(this.izy(1)));break;case 245:this.sbc(this.read(this.zpx()));break;case 246:this.inc(this.zpx());break;case 248:this.d=1;break;case 249:this.sbc(this.read(this.aby(1)));break;case 253:this.sbc(this.read(this.abx(1)));break;case 254:this.inc(this.abx());break;default:{if(this.nmos&&G.has(i)){this.undocumented(i);break}let t=this.nmos&&hs.get(i);if(!t)throw new Error("undocumented opcode $"+i.toString(16)+" at $"+(this.pc-1).toString(16));this.cycles+=t[1],t[0]&&this.read(this[t[0]](1))}}return this.cycles-s}undocumented(s){let[i,t,h,p]=G.get(s);if(this.cycles+=h,i==="JAM")throw new Error("the processor halts (JAM, $"+s.toString(16)+") at $"+(this.pc-1).toString(16));if(i==="SHA"||i==="SHX"||i==="SHY"||i==="TAS"){let e,d;if(t==="izy"){let X=this.read(this.pc++);e=this.read(X)|this.read(X+1&255)<<8,d=this.y}else e=this.read16(this.pc),this.pc+=2,d=t==="abx"?this.x:this.y;i==="TAS"&&(this.sp=this.a&this.x);let L=(i==="SHA"?this.a&this.x:i==="SHX"?this.x:i==="SHY"?this.y:this.sp)&((e>>8)+1&255),D=e+d&65535;(e^D)&65280&&(D=L<<8|D&255),this.write(D,L);return}let n=t==="imm"?this.imm():this[t](p),o=["SLO","RLA","SRE","RRA","DCP","ISC"].includes(i)?this.modify(n):this.read(n);switch(i){case"SLO":{this.c=o>>7;let e=o<<1&255;this.write(n,e),this.a=this.nz(this.a|e);break}case"RLA":{let e=(o<<1|this.c)&255;this.c=o>>7,this.write(n,e),this.a=this.nz(this.a&e);break}case"SRE":{this.c=o&1;let e=o>>1;this.write(n,e),this.a=this.nz(this.a^e);break}case"RRA":{let e=o>>1|this.c<<7;this.c=o&1,this.write(n,e),this.adc(e);break}case"SAX":this.write(n,this.a&this.x);break;case"LAX":this.a=this.x=this.nz(o);break;case"DCP":{let e=o-1&255;this.write(n,e),this.cmp(this.a,e);break}case"ISC":{let e=o+1&255;this.write(n,e),this.sbc(e);break}case"ANC":this.a=this.nz(this.a&o),this.c=this.n;break;case"ALR":{let e=this.a&o;this.c=e&1,this.a=this.nz(e>>1);break}case"ARR":{let e=this.a&o,d=e>>1|this.c<<7;if(!this.d){this.a=this.nz(d),this.c=d>>6&1,this.v=(d>>6^d>>5)&1;break}this.n=this.c,this.z=d===0?1:0,this.v=(e^d)&64?1:0,(e&15)+(e&1)>5&&(d=d&240|d+6&15),(e&240)+(e&16)>80?(d=d+96&255,this.c=1):this.c=0,this.a=d;break}case"SBX":{let e=(this.a&this.x)-o;this.c=e>=0?1:0,this.x=this.nz(e);break}case"SBC":this.sbc(o);break;case"ANE":this.a=this.nz((this.a|Z)&this.x&o);break;case"LXA":this.a=this.x=this.nz((this.a|Z)&o);break;case"LAS":this.a=this.x=this.sp=this.nz(o&this.sp);break}}},Z=238,G=new Map([...[["SLO",3],["RLA",35],["SRE",67],["RRA",99],["DCP",195],["ISC",227]].flatMap(([a,s])=>[[s,[a,"izx",8]],[s+4,[a,"zp",5]],[s+12,[a,"abs",6]],[s+16,[a,"izy",8]],[s+20,[a,"zpx",6]],[s+24,[a,"aby",7]],[s+28,[a,"abx",7]]]),[131,["SAX","izx",6]],[135,["SAX","zp",3]],[143,["SAX","abs",4]],[151,["SAX","zpy",4]],[163,["LAX","izx",6]],[167,["LAX","zp",3]],[175,["LAX","abs",4]],[179,["LAX","izy",5,1]],[183,["LAX","zpy",4]],[191,["LAX","aby",4,1]],[11,["ANC","imm",2]],[43,["ANC","imm",2]],[75,["ALR","imm",2]],[107,["ARR","imm",2]],[203,["SBX","imm",2]],[235,["SBC","imm",2]],[139,["ANE","imm",2]],[171,["LXA","imm",2]],[147,["SHA","izy",6]],[159,["SHA","aby",5]],[158,["SHX","aby",5]],[156,["SHY","abx",5]],[155,["TAS","aby",5]],[187,["LAS","aby",4,1]],...[2,18,34,50,66,82,98,114,146,178,210,242].map(a=>[a,["JAM",null,0]])]),hs=new Map([...[26,58,90,122,218,250].map(a=>[a,[null,2]]),...[128,130,137,194,226].map(a=>[a,["imm",2]]),...[4,68,100].map(a=>[a,["zp",3]]),...[20,52,84,116,212,244].map(a=>[a,["zpx",4]]),[12,["abs",4]],...[28,60,92,124,220,252].map(a=>[a,["abx",4]])]),K=[7,6,0,0,0,3,5,0,3,2,2,0,0,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,6,6,0,0,3,3,5,0,4,2,2,0,4,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,6,6,0,0,0,3,5,0,3,2,2,0,3,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,6,6,0,0,0,3,5,0,4,2,2,0,5,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,0,6,0,0,3,3,3,0,2,0,2,0,4,4,4,0,2,6,0,0,4,4,4,0,2,5,2,0,0,5,0,0,2,6,2,0,3,3,3,0,2,2,2,0,4,4,4,0,2,5,0,0,4,4,4,0,2,4,2,0,4,4,4,0,2,6,0,0,3,3,5,0,2,2,2,0,4,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,2,6,0,0,3,3,5,0,2,2,2,0,4,4,6,0,2,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0];export{W as asm,Q as cpu,_ as disasm};
|
|
22
|
+
//# sourceMappingURL=moonbase-mos6502.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../asm.js", "../disasm.js", "../cpu.js"],
|
|
4
|
+
"sourcesContent": ["// A 6502 assembler, for Moonbase's own machine code (Crayon on the 64): ordinary 6502 syntax, its\n// opcodes the disassembler's table read backwards (disasm.js `OPS`), so that what it assembles\n// reads back the same.\n//\n// const { prg, bytes, origin, symbols } = assemble(source, { file, include })\n// source the text; `file` its name, for errors; `include(name)` the text of an .include\n// prg the program as a C64 file: its load address, then its bytes\n// symbols every label and constant, by name\n//\n// A line is a label, an instruction or a directive, then a comment after `;`:\n// start: a label; `.loop:` a local one, under the last label (`.loop` reaches it)\n// name = expr a constant; *= expr where the bytes go from here\n// LDA #<table an instruction: #imm, zp, zp,X, zp,Y, abs, abs,X, abs,Y, (zp,X), (zp),Y,\n// (abs), A or nothing; zero page chosen where the operand is known below 256\n// by the first pass; a branch's reach checked\n// .byte 1,'a',$ff bytes; .word expr,... little-endian words\n// .text \"HELLO\" PETSCII (letters as the 64's capitals); .screen \"HELLO\" screen codes\n// .mixed \"Hello\" screen codes for the 64's second set, its small letters and capitals:\n// small letters 1 to 26, capitals $41 to $5A, the rest as .screen\n// .fill n[,value] n bytes; .include \"file\" another file's lines, here\n// Expressions: decimal, $hex, %binary, 'c' (its PETSCII), * (here), names; unary - ~ < (low byte)\n// > (high byte); * / % + - << >> & ^ | as C has them; parentheses.\nimport { OPS, SIZE } from './disasm.js';\n\nconst CODES = {};\nOPS.forEach((o, i) => { if (o.name !== '???') (CODES[o.name] ??= {})[o.mode] = i; });\n\nexport class AsmError extends Error {}\n\n// a character as PETSCII: the letters as the 64's capitals, whichever case they are typed in\nconst petscii = c => { const n = c.charCodeAt(0); return n >= 0x61 && n <= 0x7a ? n - 0x20 : n === 0x5f ? 0xa4 : n; };\n// and as a screen code: @ and the letters 0 to 26, the rest of $20 to $3F as they are\nconst screen = c => { const n = petscii(c); return n >= 0x40 && n < 0x60 ? n - 0x40 : n; };\n// and in the 64's second set of characters, small letters and capitals: a small letter 1 to 26, a\n// capital $41 to $5A, as that set has them\nconst mixed = c => { const n = c.charCodeAt(0); return n >= 0x61 && n <= 0x7a ? n - 0x60 : n >= 0x41 && n <= 0x5a ? n : screen(c); };\n\nexport function assemble(source, { file = 'source', include = null } = {}) {\n const symbols = new Map(), sizes = [];\n let mem, pc, low, high, pass, global, where, count;\n\n // the lines, with any .include's in their place, each knowing its file and number\n const lines = [];\n const read = (text, name, depth = 0) => text.split(/\\r?\\n/).forEach((line, n) => {\n const inc = line.match(/^\\s*\\.include\\s+\"([^\"]+)\"/i);\n if (!inc) return lines.push({ line, file: name, n: n + 1 });\n if (!include) throw new AsmError(`${name}:${n + 1}: .include, but no way to read \"${inc[1]}\"`);\n if (depth > 8) throw new AsmError(`${name}:${n + 1}: .include too deep`);\n read(include(inc[1]), inc[1], depth + 1);\n });\n read(source, file);\n const fail = message => { throw new AsmError(`${where.file}:${where.n}: ${message}`); };\n const local = name => (name.startsWith('.') ? (global ?? fail(`${name} before any label`)) + name : name);\n\n // an expression's value, or undefined where the first pass has not yet seen a name in it\n function evaluate(text) {\n const tokens = text.match(/\\$[0-9a-f]+|%[01]+|\\d+|'(?:[^'\\\\]|\\\\.)'|[.@]?[a-z_][\\w.]*|<<|>>|[-+*/%&|^~<>()]/gi);\n if (!tokens || tokens.join('').replace(/\\s/g, '') !== text.replace(/\\s/g, '')) fail(`can't read \"${text}\"`);\n let at = 0;\n const peek = () => tokens[at], next = () => tokens[at++];\n const LEVELS = [['|'], ['^'], ['&'], ['<<', '>>'], ['+', '-'], ['*', '/', '%']];\n const binary = level => {\n if (level === LEVELS.length) return unary();\n let v = binary(level + 1);\n while (LEVELS[level].includes(peek())) {\n const op = next(), w = binary(level + 1);\n v = v === undefined || w === undefined ? undefined\n : op === '|' ? v | w : op === '^' ? v ^ w : op === '&' ? v & w : op === '<<' ? v << w : op === '>>' ? v >> w\n : op === '+' ? v + w : op === '-' ? v - w : op === '*' ? v * w : op === '/' ? Math.trunc(v / w) : v % w;\n }\n return v;\n };\n const unary = () => {\n const t = next();\n if (t === undefined) fail(`\"${text}\" ends too soon`);\n if (t === '-' || t === '~' || t === '<' || t === '>') {\n const v = unary();\n return v === undefined ? undefined : t === '-' ? -v : t === '~' ? ~v & 0xffff : t === '<' ? v & 0xff : (v >> 8) & 0xff;\n }\n if (t === '(') { const v = binary(0); if (next() !== ')') fail(`a ( without its ) in \"${text}\"`); return v; }\n if (t === '*') return pc;\n if (t[0] === '$') return parseInt(t.slice(1), 16);\n if (t[0] === '%') return parseInt(t.slice(1), 2);\n if (/^\\d/.test(t)) return parseInt(t, 10);\n if (t[0] === \"'\") return petscii(t[1] === '\\\\' ? t[2] : t[1]);\n const name = local(t);\n if (symbols.has(name)) return symbols.get(name);\n if (pass === 2) fail(`${name}: not defined`);\n return undefined;\n };\n const v = binary(0);\n if (at < tokens.length) fail(`\"${text}\": more than one expression`);\n return v;\n }\n\n const put = b => { if (pc > 0xffff) fail('past $FFFF'); mem[pc] = b & 0xff; low = Math.min(low, pc); high = Math.max(high, pc); pc++; };\n const list = text => text.match(/(\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)'|[^,])+/g)?.map(s => s.trim()) ?? [];\n const define = (name, value) => {\n if (pass === 1 && symbols.has(name) && symbols.get(name) !== value) fail(`${name} defined twice`);\n if (pass === 2 && symbols.has(name) && symbols.get(name) !== value) fail(`${name} moved between the passes`);\n symbols.set(name, value);\n };\n\n function instruction(name, text) {\n const modes = CODES[name] ?? fail(`${name}: no such instruction`);\n const nth = count++;\n let mode, value;\n const operand = text.trim();\n if (!operand || /^a$/i.test(operand)) {\n mode = modes.imp !== undefined ? 'imp' : modes.acc !== undefined ? 'acc' : fail(`${name} needs an operand`);\n } else if (operand[0] === '#') { mode = 'imm'; value = evaluate(operand.slice(1)); }\n else {\n let m;\n if ((m = operand.match(/^\\((.+),\\s*x\\s*\\)$/i))) { mode = 'izx'; value = evaluate(m[1]); }\n else if ((m = operand.match(/^\\((.+)\\)\\s*,\\s*y$/i))) { mode = 'izy'; value = evaluate(m[1]); }\n else if (modes.ind !== undefined && (m = operand.match(/^\\((.+)\\)$/))) { mode = 'ind'; value = evaluate(m[1]); }\n else {\n const [, expr, reg] = operand.match(/^(.+?)(?:\\s*,\\s*([xy]))?$/i);\n value = evaluate(expr);\n const x = reg?.toLowerCase();\n if (modes.rel !== undefined) mode = 'rel';\n else {\n const zp = x === 'x' ? 'zpx' : x === 'y' ? 'zpy' : 'zp', abs = x === 'x' ? 'abx' : x === 'y' ? 'aby' : 'abs';\n // (zero page where the first pass knew the operand below 256, and the same size after)\n if (pass === 1) sizes[nth] = value !== undefined && value < 0x100 && modes[zp] !== undefined ? zp : abs;\n mode = sizes[nth];\n if (modes[mode] === undefined) mode = modes[abs] !== undefined ? abs : modes[zp] !== undefined ? zp : fail(`${name} has no ${operand} form`);\n }\n }\n }\n if (modes[mode] === undefined) fail(`${name} has no ${mode} form`);\n put(modes[mode]);\n const v = value ?? 0;\n if (mode === 'rel') {\n const d = v - (pc + 1);\n if (pass === 2 && (d < -128 || d > 127)) fail(`${name} ${operand}: ${d} bytes away, a branch reaches -128 to 127`);\n put(d);\n } else if (SIZE[mode] === 2) {\n if (pass === 2 && (v < -128 || v > 0xff)) fail(`${operand}: ${v} is more than a byte`);\n put(v);\n } else if (SIZE[mode] === 3) { put(v); put(v >> 8); }\n }\n\n for (pass = 1; pass <= 2; pass++) {\n mem = new Uint8Array(0x10000); pc = 0x1000; low = 0x10000; high = -1; global = null; count = 0;\n let set = false;\n for (where of lines) {\n let text = where.line.replace(/;.*$/, '').trim();\n // (a ; inside quotes is kept: the comment is cut only outside them)\n if (/[\"']/.test(where.line)) { let q = null, i = 0; for (; i < where.line.length; i++) { const c = where.line[i]; if (q) { if (c === q) q = null; } else if (c === '\"' || c === \"'\") q = c; else if (c === ';') break; } text = where.line.slice(0, i).trim(); }\n if (!text) continue;\n let m = text.match(/^([.@]?[a-z_][\\w]*):\\s*(.*)$/i);\n if (m) {\n if (!m[1].startsWith('.')) global = m[1];\n define(local(m[1]), pc);\n text = m[2];\n if (!text) continue;\n }\n if ((m = text.match(/^\\*\\s*=\\s*(.+)$/))) { pc = evaluate(m[1]) ?? fail('*= needs a value the first pass knows'); set = true; continue; }\n if ((m = text.match(/^([a-z_]\\w*)\\s*=\\s*(.+)$/i))) { const v = evaluate(m[2]); if (v !== undefined) define(m[1], v); else if (pass === 2) fail(`${m[1]}: no value`); continue; }\n if (!set) fail('no *= before the first byte');\n if ((m = text.match(/^\\.(\\w+)\\s*(.*)$/))) {\n const [, directive, rest] = m;\n switch (directive.toLowerCase()) {\n case 'byte': case 'text': case 'screen': case 'mixed':\n for (const item of list(rest)) {\n if (item[0] === '\"') { const as = { screen, mixed }[directive.toLowerCase()] ?? petscii; for (const c of item.slice(1, -1).replace(/\\\\(.)/g, '$1')) put(as(c)); }\n else { const v = evaluate(item); if (pass === 2 && (v < -128 || v > 0xff)) fail(`${item}: more than a byte`); put(v ?? 0); }\n }\n break;\n case 'word': for (const item of list(rest)) { const v = evaluate(item) ?? 0; put(v); put(v >> 8); } break;\n case 'fill': { const [n, v = '0'] = list(rest); const k = evaluate(n) ?? fail('.fill needs a count the first pass knows'), b = evaluate(v) ?? 0; for (let i = 0; i < k; i++) put(b); break; }\n default: fail(`.${directive}: no such directive`);\n }\n continue;\n }\n if ((m = text.match(/^([a-z]{3})\\b\\s*(.*)$/i))) { instruction(m[1].toUpperCase(), m[2]); continue; }\n fail(`can't read \"${text}\"`);\n }\n }\n if (high < 0) throw new AsmError(`${file}: nothing assembled`);\n const bytes = mem.slice(low, high + 1), prg = new Uint8Array(bytes.length + 2);\n prg[0] = low & 0xff; prg[1] = low >> 8; prg.set(bytes, 2);\n return { origin: low, bytes, prg, symbols };\n}\n", "// A 6502 disassembler: the documented opcodes, each line an address, its bytes and the instruction.\n// A byte that is no documented opcode is written as a .byte line, since the code also holds data.\n\n// mnemonic and addressing mode for each opcode, 00 to FF, 16 a line; ??? is no documented opcode\nconst TABLE = `\nBRK imp ORA izx ??? - ??? - ??? - ORA zp ASL zp ??? - PHP imp ORA imm ASL acc ??? - ??? - ORA abs ASL abs ??? -\nBPL rel ORA izy ??? - ??? - ??? - ORA zpx ASL zpx ??? - CLC imp ORA aby ??? - ??? - ??? - ORA abx ASL abx ??? -\nJSR abs AND izx ??? - ??? - BIT zp AND zp ROL zp ??? - PLP imp AND imm ROL acc ??? - BIT abs AND abs ROL abs ??? -\nBMI rel AND izy ??? - ??? - ??? - AND zpx ROL zpx ??? - SEC imp AND aby ??? - ??? - ??? - AND abx ROL abx ??? -\nRTI imp EOR izx ??? - ??? - ??? - EOR zp LSR zp ??? - PHA imp EOR imm LSR acc ??? - JMP abs EOR abs LSR abs ??? -\nBVC rel EOR izy ??? - ??? - ??? - EOR zpx LSR zpx ??? - CLI imp EOR aby ??? - ??? - ??? - EOR abx LSR abx ??? -\nRTS imp ADC izx ??? - ??? - ??? - ADC zp ROR zp ??? - PLA imp ADC imm ROR acc ??? - JMP ind ADC abs ROR abs ??? -\nBVS rel ADC izy ??? - ??? - ??? - ADC zpx ROR zpx ??? - SEI imp ADC aby ??? - ??? - ??? - ADC abx ROR abx ??? -\n??? - STA izx ??? - ??? - STY zp STA zp STX zp ??? - DEY imp ??? - TXA imp ??? - STY abs STA abs STX abs ??? -\nBCC rel STA izy ??? - ??? - STY zpx STA zpx STX zpy ??? - TYA imp STA aby TXS imp ??? - ??? - STA abx ??? - ??? -\nLDY imm LDA izx LDX imm ??? - LDY zp LDA zp LDX zp ??? - TAY imp LDA imm TAX imp ??? - LDY abs LDA abs LDX abs ??? -\nBCS rel LDA izy ??? - ??? - LDY zpx LDA zpx LDX zpy ??? - CLV imp LDA aby TSX imp ??? - LDY abx LDA abx LDX aby ??? -\nCPY imm CMP izx ??? - ??? - CPY zp CMP zp DEC zp ??? - INY imp CMP imm DEX imp ??? - CPY abs CMP abs DEC abs ??? -\nBNE rel CMP izy ??? - ??? - ??? - CMP zpx DEC zpx ??? - CLD imp CMP aby ??? - ??? - ??? - CMP abx DEC abx ??? -\nCPX imm SBC izx ??? - ??? - CPX zp SBC zp INC zp ??? - INX imp SBC imm NOP imp ??? - CPX abs SBC abs INC abs ??? -\nBEQ rel SBC izy ??? - ??? - ??? - SBC zpx INC zpx ??? - SED imp SBC aby ??? - ??? - ??? - SBC abx INC abx ??? -\n`.trim().split(/\\s+/);\nconst OPS = [];\nfor (let i = 0; i < 256; i++) OPS.push({ name: TABLE[i * 2], mode: TABLE[i * 2 + 1] });\nconst SIZE = { imp: 1, acc: 1, imm: 2, zp: 2, zpx: 2, zpy: 2, izx: 2, izy: 2, rel: 2, abs: 3, abx: 3, aby: 3, ind: 3 };\n\nconst hex = (n, w) => n.toString(16).toUpperCase().padStart(w, '0');\n\nfunction operand(mode, bytes, pc) {\n const b = bytes[1], w = bytes[1] | bytes[2] << 8;\n switch (mode) {\n case 'imp': return '';\n case 'acc': return 'A';\n case 'imm': return '#$' + hex(b, 2);\n case 'zp': return '$' + hex(b, 2);\n case 'zpx': return '$' + hex(b, 2) + ',X';\n case 'zpy': return '$' + hex(b, 2) + ',Y';\n case 'izx': return '($' + hex(b, 2) + ',X)';\n case 'izy': return '($' + hex(b, 2) + '),Y';\n case 'rel': return '$' + hex((pc + 2 + (b < 128 ? b : b - 256)) & 0xffff, 4);\n case 'abs': return '$' + hex(w, 4);\n case 'abx': return '$' + hex(w, 4) + ',X';\n case 'aby': return '$' + hex(w, 4) + ',Y';\n case 'ind': return '($' + hex(w, 4) + ')';\n }\n}\n\nfunction disassemble(body, origin) {\n const lines = [];\n for (let at = 0; at < body.length; ) {\n const op = OPS[body[at]], pc = origin + at;\n const size = op.name === '???' ? 1 : SIZE[op.mode];\n if (at + size > body.length || op.name === '???') {\n lines.push(hex(pc, 4) + ' ' + hex(body[at], 2).padEnd(9) + ' .byte $' + hex(body[at], 2));\n at++; continue;\n }\n const bytes = body.subarray(at, at + size);\n lines.push(hex(pc, 4) + ' ' + [...bytes].map(b => hex(b, 2)).join(' ').padEnd(9) + ' ' + (op.name + ' ' + operand(op.mode, bytes, pc)).trim());\n at += size;\n }\n return lines.join('\\n') + '\\n';\n}\n\nexport { disassemble, OPS, SIZE, operand, hex };\n", "// A 6502, the documented instructions, running against a machine that gives it `read(address)`\n// and `write(address, value)`. An NMOS chip, as the Commodore 64's 6510 is, also runs the\n// undocumented instructions, NOPs and all, so `{ nmos: true }` adds them; the Apple's CPU is\n// without them, since on the enhanced IIe's 65C02 the same opcodes are other instructions.\n// `step()` runs one instruction and returns its cycles; `call(hook)` lets the machine take over\n// an address: when the program counter reaches it, the hook runs in\n// place of the code there and the CPU returns as if from a subroutine; a hook answering false\n// lets the code run, and one answering 'wait' is run again on the next step.\nexport class CPU {\n // writeBack: a read-modify-write instruction writes the value it read back before the new one,\n // as the NMOS chip does on its spare cycle; on the C64 that first write is what acknowledges the\n // VIC-II's interrupt in DEC $D019 or ASL $D019, a demo's usual way (@moonbase-machines/c64/machine.js)\n constructor(machine, { nmos = false, writeBack = false } = {}) {\n this.m = machine;\n this.nmos = nmos; this.writeBack = writeBack;\n this.a = 0; this.x = 0; this.y = 0; this.sp = 0xfd; this.pc = 0;\n this.n = 0; this.v = 0; this.d = 0; this.i = 1; this.z = 0; this.c = 0;\n this.hooks = new Map();\n this.cycles = 0;\n }\n get p() { return (this.n << 7) | (this.v << 6) | 0x20 | (this.d << 3) | (this.i << 2) | (this.z << 1) | this.c; }\n set p(v) { this.n = v >> 7 & 1; this.v = v >> 6 & 1; this.d = v >> 3 & 1; this.i = v >> 2 & 1; this.z = v >> 1 & 1; this.c = v & 1; }\n read(a) { return this.m.read(a & 0xffff); }\n write(a, v) { this.m.write(a & 0xffff, v & 0xff); }\n read16(a) { return this.read(a) | this.read(a + 1) << 8; }\n push(v) { this.write(0x100 | this.sp, v); this.sp = (this.sp - 1) & 0xff; }\n pull() { this.sp = (this.sp + 1) & 0xff; return this.read(0x100 | this.sp); }\n nz(v) { v &= 0xff; this.n = v >> 7; this.z = v === 0 ? 1 : 0; return v; }\n branch(cond) { const off = this.read(this.pc++); if (cond) { const t = (this.pc + (off < 128 ? off : off - 256)) & 0xffff; this.cycles += ((t ^ this.pc) & 0xff00) ? 2 : 1; this.pc = t; } }\n // addressing modes return an address\n imm() { return this.pc++; }\n zp() { return this.read(this.pc++); }\n zpx() { return (this.read(this.pc++) + this.x) & 0xff; }\n zpy() { return (this.read(this.pc++) + this.y) & 0xff; }\n abs() { const a = this.read16(this.pc); this.pc += 2; return a; }\n abx(penalty) { const a = this.abs(); if (penalty && ((a + this.x) ^ a) & 0xff00) this.cycles++; return (a + this.x) & 0xffff; }\n aby(penalty) { const a = this.abs(); if (penalty && ((a + this.y) ^ a) & 0xff00) this.cycles++; return (a + this.y) & 0xffff; }\n izx() { const z = (this.read(this.pc++) + this.x) & 0xff; return this.read(z) | this.read((z + 1) & 0xff) << 8; }\n izy(penalty) { const z = this.read(this.pc++); const a = this.read(z) | this.read((z + 1) & 0xff) << 8; if (penalty && ((a + this.y) ^ a) & 0xff00) this.cycles++; return (a + this.y) & 0xffff; }\n // operations\n // Decimal mode: on the NMOS chip (the 6510), as the chip itself does it (perfect6502): a digit\n // over 9 has 6 added and carries, and a result over $9F has $60 added, which is the same as\n // adding the digits for a number in decimal and not otherwise; N and V from the sum before that\n // last step, Z from the binary sum. SBC's flags are all the binary subtraction's.\n adc(v) {\n if (this.d && this.nmos) {\n const a = this.a; let lo = (a & 15) + (v & 15) + this.c;\n if (lo >= 10) lo = ((lo + 6) & 15) + 16;\n let s = (a & 0xf0) + (v & 0xf0) + lo;\n this.z = ((a + v + this.c) & 0xff) === 0 ? 1 : 0; this.n = s >> 7 & 1; this.v = ((~(a ^ v) & (a ^ s)) & 0x80) ? 1 : 0;\n if (s >= 0xa0) s += 0x60;\n this.c = s >= 0x100 ? 1 : 0; this.a = s & 0xff; return;\n }\n if (this.d) {\n let lo = (this.a & 15) + (v & 15) + this.c, hi = (this.a >> 4) + (v >> 4);\n if (lo > 9) { lo -= 10; hi++; }\n const r = (this.a + v + this.c) & 0xff; this.z = r === 0 ? 1 : 0;\n this.n = (hi & 8) ? 1 : 0; this.v = ((~(this.a ^ v) & (this.a ^ (hi << 4))) & 0x80) ? 1 : 0;\n if (hi > 9) hi -= 10, this.c = 1; else this.c = 0;\n this.a = ((hi << 4) | lo) & 0xff; return;\n }\n const r = this.a + v + this.c;\n this.v = ((~(this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r > 0xff ? 1 : 0; this.a = this.nz(r);\n }\n sbc(v) {\n if (this.d && this.nmos) {\n const a = this.a, r = a - v - (1 - this.c); let lo = (a & 15) - (v & 15) - (1 - this.c);\n if (lo < 0) lo = ((lo - 6) & 15) - 16;\n let s = (a & 0xf0) - (v & 0xf0) + lo;\n if (s < 0) s -= 0x60;\n this.z = (r & 0xff) === 0 ? 1 : 0; this.n = (r & 0x80) ? 1 : 0; this.v = (((a ^ v) & (a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0;\n this.a = s & 0xff; return;\n }\n if (this.d) {\n let lo = (this.a & 15) - (v & 15) - (1 - this.c), hi = (this.a >> 4) - (v >> 4);\n if (lo < 0) { lo += 10; hi--; }\n const r = this.a - v - (1 - this.c); this.z = (r & 0xff) === 0 ? 1 : 0; this.n = (r & 0x80) ? 1 : 0;\n this.v = (((this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0;\n if (hi < 0) hi += 10;\n this.a = ((hi << 4) | lo) & 0xff; return;\n }\n const r = this.a - v - (1 - this.c);\n this.v = (((this.a ^ v) & (this.a ^ r)) & 0x80) ? 1 : 0; this.c = r >= 0 ? 1 : 0; this.a = this.nz(r);\n }\n cmp(r, v) { const d = r - v; this.c = d >= 0 ? 1 : 0; this.nz(d); }\n // a read-modify-write's read, and its value written back first where the chip does (writeBack)\n modify(a) { const v = this.read(a); if (this.writeBack) this.write(a, v); return v; }\n asl(a) { const v = a === null ? this.a : this.modify(a); this.c = v >> 7; const r = this.nz(v << 1); if (a === null) this.a = r; else this.write(a, r); }\n lsr(a) { const v = a === null ? this.a : this.modify(a); this.c = v & 1; const r = this.nz(v >> 1); if (a === null) this.a = r; else this.write(a, r); }\n rol(a) { const v = a === null ? this.a : this.modify(a); const r = this.nz((v << 1) | this.c); this.c = v >> 7; if (a === null) this.a = r; else this.write(a, r); }\n ror(a) { const v = a === null ? this.a : this.modify(a); const r = this.nz((v >> 1) | (this.c << 7)); this.c = v & 1; if (a === null) this.a = r; else this.write(a, r); }\n inc(a) { this.write(a, this.nz(this.modify(a) + 1)); }\n dec(a) { this.write(a, this.nz(this.modify(a) - 1)); }\n bit(v) { this.n = v >> 7; this.v = v >> 6 & 1; this.z = (this.a & v) === 0 ? 1 : 0; }\n\n // an interrupt request, taken when interrupts are not masked: the return address and the\n // flags go on the stack and the vector at $FFFE is followed; a non-maskable one goes by $FFFA\n irq() { if (this.i) return false; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p & 0xef); this.i = 1; this.pc = this.read16(0xfffe); this.cycles += 7; return true; }\n nmi() { this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p & 0xef); this.i = 1; this.pc = this.read16(0xfffa); this.cycles += 7; }\n\n step() {\n // (a hook that goes on elsewhere, setting the PC and returning false, has that place's hook\n // run too: a routine of a ROM in JavaScript jumping on to another, @moonbase-machines/c64/rom.js)\n for (let hook = this.hooks.get(this.pc), guard = 0; hook && guard < 8; hook = this.hooks.get(this.pc), guard++) {\n const at = this.pc, r = hook(this);\n if (r === 'wait') { this.cycles += 10; return 10; }\n if (r !== false) { this.pc = (this.pull() | this.pull() << 8) + 1 & 0xffff; this.cycles += 6; return 6; }\n if (this.pc === at) break;\n }\n const start = this.cycles;\n this.trace?.(this.pc); // every instruction run, for a tool that wants them (code coverage)\n const op = this.read(this.pc++);\n this.cycles += CYCLES[op];\n switch (op) {\n case 0x00: this.pc++; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.push(this.p | 0x10); this.i = 1; this.pc = this.read16(0xfffe); break;\n case 0x01: this.a = this.nz(this.a | this.read(this.izx())); break;\n case 0x05: this.a = this.nz(this.a | this.read(this.zp())); break;\n case 0x06: this.asl(this.zp()); break;\n case 0x08: this.push(this.p | 0x10); break;\n case 0x09: this.a = this.nz(this.a | this.read(this.imm())); break;\n case 0x0a: this.asl(null); break;\n case 0x0d: this.a = this.nz(this.a | this.read(this.abs())); break;\n case 0x0e: this.asl(this.abs()); break;\n case 0x10: this.branch(!this.n); break;\n case 0x11: this.a = this.nz(this.a | this.read(this.izy(1))); break;\n case 0x15: this.a = this.nz(this.a | this.read(this.zpx())); break;\n case 0x16: this.asl(this.zpx()); break;\n case 0x18: this.c = 0; break;\n case 0x19: this.a = this.nz(this.a | this.read(this.aby(1))); break;\n case 0x1d: this.a = this.nz(this.a | this.read(this.abx(1))); break;\n case 0x1e: this.asl(this.abx()); break;\n case 0x20: { const a = this.abs(); this.pc--; this.push(this.pc >> 8); this.push(this.pc & 0xff); this.pc = a; break; }\n case 0x21: this.a = this.nz(this.a & this.read(this.izx())); break;\n case 0x24: this.bit(this.read(this.zp())); break;\n case 0x25: this.a = this.nz(this.a & this.read(this.zp())); break;\n case 0x26: this.rol(this.zp()); break;\n case 0x28: this.p = this.pull(); break;\n case 0x29: this.a = this.nz(this.a & this.read(this.imm())); break;\n case 0x2a: this.rol(null); break;\n case 0x2c: this.bit(this.read(this.abs())); break;\n case 0x2d: this.a = this.nz(this.a & this.read(this.abs())); break;\n case 0x2e: this.rol(this.abs()); break;\n case 0x30: this.branch(this.n); break;\n case 0x31: this.a = this.nz(this.a & this.read(this.izy(1))); break;\n case 0x35: this.a = this.nz(this.a & this.read(this.zpx())); break;\n case 0x36: this.rol(this.zpx()); break;\n case 0x38: this.c = 1; break;\n case 0x39: this.a = this.nz(this.a & this.read(this.aby(1))); break;\n case 0x3d: this.a = this.nz(this.a & this.read(this.abx(1))); break;\n case 0x3e: this.rol(this.abx()); break;\n case 0x40: this.p = this.pull(); this.pc = this.pull() | this.pull() << 8; break;\n case 0x41: this.a = this.nz(this.a ^ this.read(this.izx())); break;\n case 0x45: this.a = this.nz(this.a ^ this.read(this.zp())); break;\n case 0x46: this.lsr(this.zp()); break;\n case 0x48: this.push(this.a); break;\n case 0x49: this.a = this.nz(this.a ^ this.read(this.imm())); break;\n case 0x4a: this.lsr(null); break;\n case 0x4c: this.pc = this.abs(); break;\n case 0x4d: this.a = this.nz(this.a ^ this.read(this.abs())); break;\n case 0x4e: this.lsr(this.abs()); break;\n case 0x50: this.branch(!this.v); break;\n case 0x51: this.a = this.nz(this.a ^ this.read(this.izy(1))); break;\n case 0x55: this.a = this.nz(this.a ^ this.read(this.zpx())); break;\n case 0x56: this.lsr(this.zpx()); break;\n case 0x58: this.i = 0; break;\n case 0x59: this.a = this.nz(this.a ^ this.read(this.aby(1))); break;\n case 0x5d: this.a = this.nz(this.a ^ this.read(this.abx(1))); break;\n case 0x5e: this.lsr(this.abx()); break;\n case 0x60: this.pc = (this.pull() | this.pull() << 8) + 1 & 0xffff; break;\n case 0x61: this.adc(this.read(this.izx())); break;\n case 0x65: this.adc(this.read(this.zp())); break;\n case 0x66: this.ror(this.zp()); break;\n case 0x68: this.a = this.nz(this.pull()); break;\n case 0x69: this.adc(this.read(this.imm())); break;\n case 0x6a: this.ror(null); break;\n case 0x6c: { const a = this.abs(); this.pc = this.read(a) | this.read((a & 0xff00) | ((a + 1) & 0xff)) << 8; break; }\n case 0x6d: this.adc(this.read(this.abs())); break;\n case 0x6e: this.ror(this.abs()); break;\n case 0x70: this.branch(this.v); break;\n case 0x71: this.adc(this.read(this.izy(1))); break;\n case 0x75: this.adc(this.read(this.zpx())); break;\n case 0x76: this.ror(this.zpx()); break;\n case 0x78: this.i = 1; break;\n case 0x79: this.adc(this.read(this.aby(1))); break;\n case 0x7d: this.adc(this.read(this.abx(1))); break;\n case 0x7e: this.ror(this.abx()); break;\n case 0x81: this.write(this.izx(), this.a); break;\n case 0x84: this.write(this.zp(), this.y); break;\n case 0x85: this.write(this.zp(), this.a); break;\n case 0x86: this.write(this.zp(), this.x); break;\n case 0x88: this.y = this.nz(this.y - 1); break;\n case 0x8a: this.a = this.nz(this.x); break;\n case 0x8c: this.write(this.abs(), this.y); break;\n case 0x8d: this.write(this.abs(), this.a); break;\n case 0x8e: this.write(this.abs(), this.x); break;\n case 0x90: this.branch(!this.c); break;\n case 0x91: this.write(this.izy(), this.a); break;\n case 0x94: this.write(this.zpx(), this.y); break;\n case 0x95: this.write(this.zpx(), this.a); break;\n case 0x96: this.write(this.zpy(), this.x); break;\n case 0x98: this.a = this.nz(this.y); break;\n case 0x99: this.write(this.aby(), this.a); break;\n case 0x9a: this.sp = this.x; break;\n case 0x9d: this.write(this.abx(), this.a); break;\n case 0xa0: this.y = this.nz(this.read(this.imm())); break;\n case 0xa1: this.a = this.nz(this.read(this.izx())); break;\n case 0xa2: this.x = this.nz(this.read(this.imm())); break;\n case 0xa4: this.y = this.nz(this.read(this.zp())); break;\n case 0xa5: this.a = this.nz(this.read(this.zp())); break;\n case 0xa6: this.x = this.nz(this.read(this.zp())); break;\n case 0xa8: this.y = this.nz(this.a); break;\n case 0xa9: this.a = this.nz(this.read(this.imm())); break;\n case 0xaa: this.x = this.nz(this.a); break;\n case 0xac: this.y = this.nz(this.read(this.abs())); break;\n case 0xad: this.a = this.nz(this.read(this.abs())); break;\n case 0xae: this.x = this.nz(this.read(this.abs())); break;\n case 0xb0: this.branch(this.c); break;\n case 0xb1: this.a = this.nz(this.read(this.izy(1))); break;\n case 0xb4: this.y = this.nz(this.read(this.zpx())); break;\n case 0xb5: this.a = this.nz(this.read(this.zpx())); break;\n case 0xb6: this.x = this.nz(this.read(this.zpy())); break;\n case 0xb8: this.v = 0; break;\n case 0xb9: this.a = this.nz(this.read(this.aby(1))); break;\n case 0xba: this.x = this.nz(this.sp); break;\n case 0xbc: this.y = this.nz(this.read(this.abx(1))); break;\n case 0xbd: this.a = this.nz(this.read(this.abx(1))); break;\n case 0xbe: this.x = this.nz(this.read(this.aby(1))); break;\n case 0xc0: this.cmp(this.y, this.read(this.imm())); break;\n case 0xc1: this.cmp(this.a, this.read(this.izx())); break;\n case 0xc4: this.cmp(this.y, this.read(this.zp())); break;\n case 0xc5: this.cmp(this.a, this.read(this.zp())); break;\n case 0xc6: this.dec(this.zp()); break;\n case 0xc8: this.y = this.nz(this.y + 1); break;\n case 0xc9: this.cmp(this.a, this.read(this.imm())); break;\n case 0xca: this.x = this.nz(this.x - 1); break;\n case 0xcc: this.cmp(this.y, this.read(this.abs())); break;\n case 0xcd: this.cmp(this.a, this.read(this.abs())); break;\n case 0xce: this.dec(this.abs()); break;\n case 0xd0: this.branch(!this.z); break;\n case 0xd1: this.cmp(this.a, this.read(this.izy(1))); break;\n case 0xd5: this.cmp(this.a, this.read(this.zpx())); break;\n case 0xd6: this.dec(this.zpx()); break;\n case 0xd8: this.d = 0; break;\n case 0xd9: this.cmp(this.a, this.read(this.aby(1))); break;\n case 0xdd: this.cmp(this.a, this.read(this.abx(1))); break;\n case 0xde: this.dec(this.abx()); break;\n case 0xe0: this.cmp(this.x, this.read(this.imm())); break;\n case 0xe1: this.sbc(this.read(this.izx())); break;\n case 0xe4: this.cmp(this.x, this.read(this.zp())); break;\n case 0xe5: this.sbc(this.read(this.zp())); break;\n case 0xe6: this.inc(this.zp()); break;\n case 0xe8: this.x = this.nz(this.x + 1); break;\n case 0xe9: this.sbc(this.read(this.imm())); break;\n case 0xea: break;\n case 0xec: this.cmp(this.x, this.read(this.abs())); break;\n case 0xed: this.sbc(this.read(this.abs())); break;\n case 0xee: this.inc(this.abs()); break;\n case 0xf0: this.branch(this.z); break;\n case 0xf1: this.sbc(this.read(this.izy(1))); break;\n case 0xf5: this.sbc(this.read(this.zpx())); break;\n case 0xf6: this.inc(this.zpx()); break;\n case 0xf8: this.d = 1; break;\n case 0xf9: this.sbc(this.read(this.aby(1))); break;\n case 0xfd: this.sbc(this.read(this.abx(1))); break;\n case 0xfe: this.inc(this.abx()); break;\n default: {\n if (this.nmos && UNDOCUMENTED.has(op)) { this.undocumented(op); break; }\n const nop = this.nmos && NOPS.get(op);\n if (!nop) throw new Error('undocumented opcode $' + op.toString(16) + ' at $' + (this.pc - 1).toString(16));\n this.cycles += nop[1];\n if (nop[0]) this.read(this[nop[0]](1)); // the operand is fetched and read, as the chip does\n }\n }\n return this.cycles - start;\n }\n // The NMOS chip's undocumented instructions, as the chip itself runs them (perfect6502): two\n // operations in one, on an operand found the usual ways (UNDOCUMENTED, below).\n undocumented(op) {\n const [name, mode, cycles, penalty] = UNDOCUMENTED.get(op);\n this.cycles += cycles;\n if (name === 'JAM') throw new Error('the processor halts (JAM, $' + op.toString(16) + ') at $' + (this.pc - 1).toString(16));\n // SHA, SHX, SHY, TAS: the register stored ANDed with the high byte of the address before\n // indexing, plus one; and where indexing crosses a page, that value is the high byte stored to\n if (name === 'SHA' || name === 'SHX' || name === 'SHY' || name === 'TAS') {\n let base, index;\n if (mode === 'izy') { const z = this.read(this.pc++); base = this.read(z) | this.read((z + 1) & 0xff) << 8; index = this.y; }\n else { base = this.read16(this.pc); this.pc += 2; index = mode === 'abx' ? this.x : this.y; }\n if (name === 'TAS') this.sp = this.a & this.x;\n const reg = name === 'SHA' ? this.a & this.x : name === 'SHX' ? this.x : name === 'SHY' ? this.y : this.sp;\n const v = reg & ((base >> 8) + 1 & 0xff); let at = (base + index) & 0xffff;\n if ((base ^ at) & 0xff00) at = v << 8 | (at & 0xff);\n this.write(at, v); return;\n }\n const at = mode === 'imm' ? this.imm() : this[mode](penalty);\n const v = ['SLO', 'RLA', 'SRE', 'RRA', 'DCP', 'ISC'].includes(name) ? this.modify(at) : this.read(at);\n switch (name) {\n case 'SLO': { this.c = v >> 7; const r = v << 1 & 0xff; this.write(at, r); this.a = this.nz(this.a | r); break; }\n case 'RLA': { const r = (v << 1 | this.c) & 0xff; this.c = v >> 7; this.write(at, r); this.a = this.nz(this.a & r); break; }\n case 'SRE': { this.c = v & 1; const r = v >> 1; this.write(at, r); this.a = this.nz(this.a ^ r); break; }\n case 'RRA': { const r = v >> 1 | this.c << 7; this.c = v & 1; this.write(at, r); this.adc(r); break; }\n case 'SAX': this.write(at, this.a & this.x); break;\n case 'LAX': this.a = this.x = this.nz(v); break;\n case 'DCP': { const r = v - 1 & 0xff; this.write(at, r); this.cmp(this.a, r); break; }\n case 'ISC': { const r = v + 1 & 0xff; this.write(at, r); this.sbc(r); break; }\n case 'ANC': this.a = this.nz(this.a & v); this.c = this.n; break;\n case 'ALR': { const t = this.a & v; this.c = t & 1; this.a = this.nz(t >> 1); break; }\n case 'ARR': {\n const t = this.a & v; let r = t >> 1 | this.c << 7;\n if (!this.d) { this.a = this.nz(r); this.c = r >> 6 & 1; this.v = (r >> 6 ^ r >> 5) & 1; break; }\n // in decimal mode: N from the old carry, Z and V from the rotate, then each digit adjusted\n this.n = this.c; this.z = r === 0 ? 1 : 0; this.v = ((t ^ r) & 0x40) ? 1 : 0;\n if ((t & 0x0f) + (t & 0x01) > 5) r = (r & 0xf0) | ((r + 6) & 0x0f);\n if ((t & 0xf0) + (t & 0x10) > 0x50) { r = (r + 0x60) & 0xff; this.c = 1; } else this.c = 0;\n this.a = r; break;\n }\n case 'SBX': { const d = (this.a & this.x) - v; this.c = d >= 0 ? 1 : 0; this.x = this.nz(d); break; }\n case 'SBC': this.sbc(v); break;\n case 'ANE': this.a = this.nz((this.a | ANE_MAGIC) & this.x & v); break;\n case 'LXA': this.a = this.x = this.nz((this.a | ANE_MAGIC) & v); break;\n case 'LAS': this.a = this.x = this.sp = this.nz(v & this.sp); break;\n }\n }\n}\n\n// The undocumented instructions an NMOS 6502 runs, by opcode: the operation, how the operand is\n// found, the cycles, and whether an indexed read crossing a page takes one more. ANE and LXA mix in\n// a constant that differs from chip to chip; ANE_MAGIC is perfect6502's.\nconst ANE_MAGIC = 0xee;\nconst UNDOCUMENTED = new Map([\n ...[['SLO', 0x03], ['RLA', 0x23], ['SRE', 0x43], ['RRA', 0x63], ['DCP', 0xc3], ['ISC', 0xe3]].flatMap(([n, b]) =>\n [[b, [n, 'izx', 8]], [b + 4, [n, 'zp', 5]], [b + 0xc, [n, 'abs', 6]], [b + 0x10, [n, 'izy', 8]], [b + 0x14, [n, 'zpx', 6]], [b + 0x18, [n, 'aby', 7]], [b + 0x1c, [n, 'abx', 7]]]),\n [0x83, ['SAX', 'izx', 6]], [0x87, ['SAX', 'zp', 3]], [0x8f, ['SAX', 'abs', 4]], [0x97, ['SAX', 'zpy', 4]],\n [0xa3, ['LAX', 'izx', 6]], [0xa7, ['LAX', 'zp', 3]], [0xaf, ['LAX', 'abs', 4]], [0xb3, ['LAX', 'izy', 5, 1]], [0xb7, ['LAX', 'zpy', 4]], [0xbf, ['LAX', 'aby', 4, 1]],\n [0x0b, ['ANC', 'imm', 2]], [0x2b, ['ANC', 'imm', 2]], [0x4b, ['ALR', 'imm', 2]], [0x6b, ['ARR', 'imm', 2]], [0xcb, ['SBX', 'imm', 2]], [0xeb, ['SBC', 'imm', 2]],\n [0x8b, ['ANE', 'imm', 2]], [0xab, ['LXA', 'imm', 2]],\n [0x93, ['SHA', 'izy', 6]], [0x9f, ['SHA', 'aby', 5]], [0x9e, ['SHX', 'aby', 5]], [0x9c, ['SHY', 'abx', 5]], [0x9b, ['TAS', 'aby', 5]], [0xbb, ['LAS', 'aby', 4, 1]],\n ...[0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xb2, 0xd2, 0xf2].map(op => [op, ['JAM', null, 0]]),\n]);\n\n// base cycles for each opcode, 00 to FF\n// the undocumented NOPs an NMOS 6502 runs: by opcode, how the operand is found and the cycles, with\n// one more for an absolute indexed one that crosses a page\nconst NOPS = new Map([\n ...[0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa].map(op => [op, [null, 2]]),\n ...[0x80, 0x82, 0x89, 0xc2, 0xe2].map(op => [op, ['imm', 2]]),\n ...[0x04, 0x44, 0x64].map(op => [op, ['zp', 3]]),\n ...[0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4].map(op => [op, ['zpx', 4]]),\n [0x0c, ['abs', 4]],\n ...[0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc].map(op => [op, ['abx', 4]]),\n]);\n\nexport const CYCLES = [\n 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,\n 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,\n 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,\n 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,\n 0, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,\n 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,\n 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,\n 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0];\n"],
|
|
5
|
+
"mappings": "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,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC7F,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC7F,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC7F,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC7F,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,CAAC",
|
|
6
|
+
"names": ["asm_exports", "__export", "AsmError", "assemble", "disasm_exports", "__export", "OPS", "SIZE", "disassemble", "hex", "operand", "TABLE", "i", "n", "w", "mode", "bytes", "pc", "b", "body", "origin", "lines", "at", "op", "size", "CODES", "OPS", "o", "i", "AsmError", "petscii", "c", "n", "screen", "mixed", "assemble", "source", "file", "include", "symbols", "sizes", "mem", "pc", "low", "high", "pass", "global", "where", "count", "lines", "read", "text", "name", "depth", "line", "inc", "fail", "message", "local", "evaluate", "tokens", "at", "peek", "next", "LEVELS", "binary", "level", "unary", "v", "op", "w", "t", "put", "b", "list", "s", "define", "value", "instruction", "modes", "nth", "mode", "operand", "m", "expr", "reg", "x", "zp", "abs", "d", "SIZE", "set", "q", "directive", "rest", "item", "as", "k", "bytes", "prg", "cpu_exports", "__export", "CPU", "CYCLES", "machine", "nmos", "writeBack", "v", "a", "cond", "off", "penalty", "z", "lo", "s", "hi", "r", "d", "hook", "guard", "at", "start", "op", "UNDOCUMENTED", "nop", "NOPS", "name", "mode", "cycles", "base", "index", "t", "ANE_MAGIC", "n", "b"]
|
|
7
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @moonbase-machines/mos6502: The 6502, its assembler and its disassembler, every module by name, for one
|
|
2
|
+
// bundled file or to take the whole package at once: import { cpu } from '@moonbase-machines/mos6502'. Each
|
|
3
|
+
// module can be imported on its own too: '@moonbase-machines/mos6502/<module>.js'.
|
|
4
|
+
export * as asm from './asm.js';
|
|
5
|
+
export * as cpu from './cpu.js';
|
|
6
|
+
export * as disasm from './disasm.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moonbase-machines/mos6502",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "The 6502: its documented instructions, an assembler and a disassembler.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Mark Wharton",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js",
|
|
10
|
+
"./*": "./*"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node --test test/*.test.js"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/test/asm.test.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Moonbase's 6502 assembler (asm.js): every documented instruction in every mode assembled and
|
|
2
|
+
// read back by the disassembler (disasm.js) the same; labels before and after, local labels,
|
|
3
|
+
// zero page chosen where the operand is known, a branch too far refused, the directives and the
|
|
4
|
+
// expressions.
|
|
5
|
+
//
|
|
6
|
+
// node --test test/*.test.js
|
|
7
|
+
import { test } from 'node:test';
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { assemble, AsmError } from '@moonbase-machines/mos6502/asm.js';
|
|
10
|
+
import { OPS, disassemble, operand } from '@moonbase-machines/mos6502/disasm.js';
|
|
11
|
+
|
|
12
|
+
test('every instruction, in every mode, reads back the same', () => {
|
|
13
|
+
let source = '*= $1000\n';
|
|
14
|
+
const want = [];
|
|
15
|
+
OPS.forEach((o, i) => {
|
|
16
|
+
if (o.name === '???') return;
|
|
17
|
+
const op = o.mode === 'rel' ? '*' : o.mode === 'acc' ? 'A' : operand(o.mode, [i, 0x12, 0x34], 0x1000);
|
|
18
|
+
source += ` ${o.name} ${op}\n`; want.push([o.name, op]);
|
|
19
|
+
});
|
|
20
|
+
const { bytes } = assemble(source);
|
|
21
|
+
const lines = disassemble(bytes, 0x1000).trim().split('\n');
|
|
22
|
+
assert.equal(lines.length, want.length);
|
|
23
|
+
lines.forEach((line, k) => {
|
|
24
|
+
const [name, op] = want[k];
|
|
25
|
+
assert.equal(line.slice(17), `${name} ${op === '*' ? '$' + line.slice(0, 4) : op}`.trim(), line);
|
|
26
|
+
});
|
|
27
|
+
assert.equal(want.length, 151);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('labels, local labels, constants, zero page and the directives', () => {
|
|
31
|
+
const { prg, symbols } = assemble(`
|
|
32
|
+
zp = $fb
|
|
33
|
+
*= $0801
|
|
34
|
+
.word end, 10 ; a BASIC line
|
|
35
|
+
.byte $9e
|
|
36
|
+
.text "2061"
|
|
37
|
+
.byte 0
|
|
38
|
+
end: .word 0
|
|
39
|
+
start: lda #<message
|
|
40
|
+
ldx #>message
|
|
41
|
+
.loop: dex
|
|
42
|
+
bne .loop
|
|
43
|
+
sta zp
|
|
44
|
+
sta later
|
|
45
|
+
jmp start
|
|
46
|
+
message:
|
|
47
|
+
.screen "AZ@"
|
|
48
|
+
.text 'a', "b;c"
|
|
49
|
+
.fill 2, $ea
|
|
50
|
+
.mixed "Az@"
|
|
51
|
+
later = $02
|
|
52
|
+
`);
|
|
53
|
+
assert.deepEqual([...prg], [0x01, 0x08, 0x0b, 0x08, 0x0a, 0x00, 0x9e, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00,
|
|
54
|
+
0xa9, 0x1c, 0xa2, 0x08, 0xca, 0xd0, 0xfd, 0x85, 0xfb, 0x8d, 0x02, 0x00, 0x4c, 0x0d, 0x08,
|
|
55
|
+
0x01, 0x1a, 0x00, 0x41, 0x42, 0x3b, 0x43, 0xea, 0xea, 0x41, 0x1a, 0x00]);
|
|
56
|
+
assert.equal(symbols.get('start.loop'), 0x0811);
|
|
57
|
+
assert.equal(symbols.get('message'), 0x081c);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('expressions as C has them, the low and high bytes, and here', () => {
|
|
61
|
+
const { bytes } = assemble('*= $c000\n .byte 1+2*3, (1+2)*3, $10|%11, 7&~2, >$1234, <$1234, 1<<4, 100/7, 100%7, -1\n .word *, *+2');
|
|
62
|
+
assert.deepEqual([...bytes], [7, 9, 0x13, 5, 0x12, 0x34, 16, 14, 2, 0xff, 0x0a, 0xc0, 0x0e, 0xc0]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('a branch too far, a name not defined, and a name defined twice are refused, with the line', () => {
|
|
66
|
+
assert.throws(() => assemble('*= $1000\n bne far\n .fill 200\nfar: rts'), /source:2: BNE far: 200 bytes away/);
|
|
67
|
+
assert.throws(() => assemble('*= $1000\n jmp nowhere'), /source:2: nowhere: not defined/);
|
|
68
|
+
assert.throws(() => assemble('*= $1000\na: nop\na: nop'), /a defined twice/);
|
|
69
|
+
assert.throws(() => assemble('*= $1000\n lda #$100'), AsmError);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('.include brings another file in, its errors named by it', () => {
|
|
73
|
+
const files = { 'more.s': 'two: .byte 2\n' };
|
|
74
|
+
const { bytes } = assemble('*= $2000\none: .byte 1\n .include "more.s"\n .word two', { include: n => files[n] });
|
|
75
|
+
assert.deepEqual([...bytes], [1, 2, 0x01, 0x20]);
|
|
76
|
+
assert.throws(() => assemble('*= $2000\n .include "bad.s"', { include: () => '\n lda (' }), /bad.s:2/);
|
|
77
|
+
});
|
package/test/cpu.test.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// cpu.js against the 6502 itself: what perfect6502, the chip simulated transistor by transistor
|
|
2
|
+
// from Visual6502's netlist, gives for decimal mode on the NMOS chip, digits over 9 among them,
|
|
3
|
+
// where the textbook's way and the chip's part.
|
|
4
|
+
//
|
|
5
|
+
// node --test test/*.test.js
|
|
6
|
+
import { test } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { CPU } from '@moonbase-machines/mos6502/cpu.js';
|
|
9
|
+
|
|
10
|
+
// [opcode, A, operand, P before, A after, P after (NV-DIZC: bits 4 and 5 are not a register)]
|
|
11
|
+
const CHIP = [
|
|
12
|
+
[0x69, 0xdc, 0x27, 0x08, 0x69, 0x09], [0x69, 0x0f, 0x0f, 0x09, 0x15, 0x08], [0x69, 0xaf, 0xdf, 0x08, 0xe4, 0x89], [0x69, 0x99, 0x01, 0x09, 0x01, 0x89],
|
|
13
|
+
[0xe9, 0xf3, 0xcf, 0x08, 0x2d, 0x09], [0xe9, 0x0a, 0x0b, 0x09, 0x99, 0x88], [0xe9, 0x00, 0x01, 0x08, 0x98, 0x88], [0xe9, 0x50, 0x0f, 0x09, 0x4b, 0x09],
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
test('ADC and SBC in decimal mode, as the NMOS chip does them', () => {
|
|
17
|
+
for (const [op, a, b, p, wantA, wantP] of CHIP) {
|
|
18
|
+
const mem = new Uint8Array(0x10000); mem.set([op, b], 0x800);
|
|
19
|
+
const cpu = new CPU({ read: k => mem[k], write: (k, v) => { mem[k] = v; } }, { nmos: true });
|
|
20
|
+
Object.assign(cpu, { a, pc: 0x800 }); cpu.p = p; cpu.step();
|
|
21
|
+
assert.deepEqual([cpu.a, cpu.p & 0xcf], [wantA, wantP], `$${op.toString(16)} with A $${a.toString(16)}, $${b.toString(16)}, P $${p.toString(16)}`);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// The undocumented instructions perfect6502 cannot settle: what NMOS chips give, from the operand
|
|
26
|
+
// ANDed into A
|
|
27
|
+
test('ANC, ALR, ARR, SBX, LXA, LAS as the chips give them', () => {
|
|
28
|
+
const run = (bytes, regs) => {
|
|
29
|
+
const mem = new Uint8Array(0x10000); mem.set(bytes, 0x800); mem[0x3010] = regs.value ?? 0; delete regs.value;
|
|
30
|
+
const cpu = new CPU({ read: k => mem[k], write: (k, v) => { mem[k] = v; } }, { nmos: true });
|
|
31
|
+
Object.assign(cpu, { a: 0, x: 0, y: 0, sp: 0xff, pc: 0x800 }, regs); cpu.p = regs.p ?? 0; cpu.step();
|
|
32
|
+
return { a: cpu.a, x: cpu.x, sp: cpu.sp, p: cpu.p & 0xcf };
|
|
33
|
+
};
|
|
34
|
+
assert.deepEqual(run([0x0b, 0x8f], { a: 0xf0 }), { a: 0x80, x: 0, sp: 0xff, p: 0x81 }, 'ANC: N into C');
|
|
35
|
+
assert.deepEqual(run([0x4b, 0x0f], { a: 0xff }), { a: 0x07, x: 0, sp: 0xff, p: 0x01 }, 'ALR');
|
|
36
|
+
assert.deepEqual(run([0x6b, 0xff], { a: 0xc0, p: 0x01 }), { a: 0xe0, x: 0, sp: 0xff, p: 0x81 }, 'ARR: C from bit 6, V from 6 and 5');
|
|
37
|
+
assert.deepEqual(run([0xcb, 0x02], { a: 0x0f, x: 0xfc }), { a: 0x0f, x: 0x0a, sp: 0xff, p: 0x01 }, 'SBX: (A AND X) - operand');
|
|
38
|
+
assert.deepEqual(run([0xab, 0x0f], { a: 0x11 }).x, (0x11 | 0xee) & 0x0f, 'LXA');
|
|
39
|
+
assert.deepEqual(run([0xbb, 0x10, 0x30], { sp: 0x3c, value: 0xf5, y: 0 }), { a: 0x34, x: 0x34, sp: 0x34, p: 0x00 }, 'LAS');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// A read-modify-write writes twice on the NMOS chip, the value read and then the new one, which on
|
|
43
|
+
// the C64 is how DEC $D019 acknowledges the VIC-II's interrupt; only where the machine asks
|
|
44
|
+
// (writeBack), so that the Apple's soft switches are touched once.
|
|
45
|
+
test('read-modify-writes write the value back first, with writeBack', () => {
|
|
46
|
+
for (const [writeBack, want] of [[true, [[0xd019, 0xf1], [0xd019, 0xf0]]], [false, [[0xd019, 0xf0]]]]) {
|
|
47
|
+
const mem = new Uint8Array(0x10000), writes = [];
|
|
48
|
+
mem.set([0xce, 0x19, 0xd0], 0x800); mem[0xd019] = 0xf1; // DEC $D019
|
|
49
|
+
const cpu = new CPU({ read: k => mem[k], write: (k, v) => { writes.push([k, v]); mem[k] = v; } }, { nmos: true, writeBack });
|
|
50
|
+
cpu.pc = 0x800; cpu.step();
|
|
51
|
+
assert.deepEqual(writes, want, `writeBack ${writeBack}`);
|
|
52
|
+
}
|
|
53
|
+
});
|