UNPKG

broken-neees

Version:

A really broken NEEES emulator that introduces glitches and random bugs on purpose!

464 lines (457 loc) 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _byte = _interopRequireDefault(require("../lib/byte")); var _interrupts = _interopRequireDefault(require("../lib/cpu/interrupts")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const ADC = function (cpu, val) { // Adds the contents of <val> to [A] together with the Carry Flag // ([A] = [A] + <val> + C), updating the Z and N flags. const oldValue = cpu.a.getValue(); const result = oldValue + val + cpu.flags.c; const newValue = _byte.default.toU8(result); cpu.a.setValue(newValue); cpu.flags.updateZeroAndNegative(newValue); // C and V flags are set in case of unsigned and signed overflow respectively. // Unsigned overflow occurs when the result is >= `256` (use `byte.overflows(...)`) // Signed overflow occurs when `Positive + Positive = Negative` or `Negative + Negative = Positive` cpu.flags.c = _byte.default.overflows(result); cpu.flags.v = _byte.default.isPositive(oldValue) && _byte.default.isPositive(val) && _byte.default.isNegative(newValue) || _byte.default.isNegative(oldValue) && _byte.default.isNegative(val) && _byte.default.isPositive(newValue); }; const SE_ = flagName => { return cpu => { cpu.flags[flagName] = true; }; }; const CL_ = flagName => { return cpu => { cpu.flags[flagName] = false; }; }; const LD_ = registerName => { return (cpu, value) => { cpu[registerName].setValue(value); cpu.flags.updateZeroAndNegative(value); }; }; const ST_ = registerName => { return (cpu, address) => { const value = cpu[registerName].getValue(); cpu.memory.write(address, value); }; }; const T__ = function (sourceRegister, targetRegister) { let updateFlags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; return cpu => { const value = cpu[sourceRegister].getValue(); cpu[targetRegister].setValue(value); if (updateFlags) cpu.flags.updateZeroAndNegative(value); }; }; const CP_ = registerName => { return (cpu, value) => { const source = cpu[registerName].getValue(); cpu.flags.z = source === value; cpu.flags.updateNegative(_byte.default.toU8(source - value)); cpu.flags.c = source >= value; }; }; const LOGICAL_INSTRUCTION = operator => { return (cpu, value) => { const result = operator(cpu.a.getValue(), value); cpu.a.setValue(result); cpu.flags.updateZeroAndNegative(result); }; }; const B__ = (flag, value) => { return (cpu, address) => { if (cpu.flags[flag] === value) { cpu.pc.setValue(address); cpu.extraCycles++; } else { cpu.extraCycles = 0; } }; }; const instructions = { // ---------- // ARITHMETIC // ---------- // Add with Carry ADC: { argument: "value", run: ADC }, ASL: { argument: "address", run(cpu, address) { const value = cpu.memory.read(address); const result = value << 1; const newValue = _byte.default.toU8(result); cpu.memory.write(address, newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 7); } }, ASLa: { argument: "no", run(cpu) { const value = cpu.a.getValue(); const result = value << 1; const newValue = _byte.default.toU8(result); cpu.a.setValue(newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 7); } }, DEC: { argument: "address", run(cpu, address) { const value = cpu.memory.read(address); const newValue = _byte.default.toU8(value - 1); cpu.flags.updateZeroAndNegative(newValue); cpu.memory.write(address, newValue); } }, DEX: { argument: "no", run(cpu) { const register = cpu.x; register.decrement(); cpu.flags.updateZeroAndNegative(register.getValue()); } }, DEY: { argument: "no", run(cpu) { const register = cpu.y; register.decrement(); cpu.flags.updateZeroAndNegative(register.getValue()); } }, // Increment Memory INC: { argument: "address", run(cpu, addr) { // Adds one to the value held at <addr>, updating the Z and N flags. const value = cpu.memory.read(addr); const newValue = _byte.default.toU8(value + 1); cpu.memory.write(addr, newValue); cpu.flags.updateZeroAndNegative(newValue); } }, // Increment X Register INX: { argument: "no", run(cpu) { // Increments [X], updating the Z and N flags. cpu.x.increment(); cpu.flags.updateZeroAndNegative(cpu.x.getValue()); } }, INY: { argument: "no", run(cpu) { cpu.y.increment(); cpu.flags.updateZeroAndNegative(cpu.y.getValue()); } }, LSR: { argument: "address", run(cpu, address) { const value = cpu.memory.read(address); const result = value >> 1; const newValue = _byte.default.toU8(result); cpu.memory.write(address, newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 0); } }, LSRa: { argument: "no", run(cpu) { const value = cpu.a.getValue(); const result = value >> 1; const newValue = _byte.default.toU8(result); cpu.a.setValue(newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 0); } }, ROL: { argument: "address", run(cpu, address) { const value = cpu.memory.read(address); const result = value << 1 | +cpu.flags.c; const newValue = _byte.default.toU8(result); cpu.memory.write(address, newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 7); } }, ROLa: { argument: "no", run(cpu) { const value = cpu.a.getValue(); const result = value << 1 | +cpu.flags.c; const newValue = _byte.default.toU8(result); cpu.a.setValue(newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 7); } }, ROR: { argument: "address", run(cpu, address) { const value = cpu.memory.read(address); const result = value >> 1 | +cpu.flags.c << 7; const newValue = _byte.default.toU8(result); cpu.memory.write(address, newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 0); } }, RORa: { argument: "no", run(cpu) { const value = cpu.a.getValue(); const result = value >> 1 | +cpu.flags.c << 7; const newValue = _byte.default.toU8(result); cpu.a.setValue(newValue); cpu.flags.updateZeroAndNegative(newValue); cpu.flags.c = _byte.default.getFlag(value, 0); } }, SBC: { argument: "value", run(cpu, value) { return ADC(cpu, 255 - value); } }, // ---- // DATA // ---- CLC: { argument: "no", run: CL_("c") }, CLD: { argument: "no", run: CL_("d") }, CLI: { argument: "no", run: CL_("i") }, CLV: { argument: "no", run: CL_("v") }, LDA: { argument: "value", run: LD_("a") }, LDX: { argument: "value", run: LD_("x") }, LDY: { argument: "value", run: LD_("y") }, PHA: { argument: "no", run(cpu) { cpu.stack.push(cpu.a.getValue()); } }, PHP: { argument: "no", run(cpu) { cpu.stack.push(cpu.flags.getValue() | 0b00010000); } }, PLA: { argument: "no", run(cpu) { const value = cpu.stack.pop(); cpu.a.setValue(value); cpu.flags.updateZeroAndNegative(value); } }, PLP: { argument: "no", run(cpu) { cpu.flags.setValue(cpu.stack.pop()); } }, SEC: { argument: "no", run: SE_("c") }, SED: { argument: "no", run: SE_("d") }, SEI: { argument: "no", run: SE_("i") }, STA: { argument: "address", run: ST_("a") }, STX: { argument: "address", run: ST_("x") }, STY: { argument: "address", run: ST_("y") }, TAX: { argument: "no", run: T__("a", "x") }, TAY: { argument: "no", run: T__("a", "y") }, TSX: { argument: "no", run: T__("sp", "x") }, TXA: { argument: "no", run: T__("x", "a") }, TXS: { argument: "no", run: T__("x", "sp", false) }, TYA: { argument: "no", run: T__("y", "a") }, // ------ // CHECKS // ------ BIT: { argument: "value", run(cpu, value) { if (!cpu.unbroken) { // [!!!] if (Math.random() < 0.15) return; } const mask = cpu.a.getValue(); const result = value & mask; cpu.flags.updateZero(result); cpu.flags.updateNegative(value); cpu.flags.v = _byte.default.getFlag(value, 6); } }, CMP: { argument: "value", run: CP_("a") }, CPX: { argument: "value", run: CP_("x") }, CPY: { argument: "value", run: CP_("y") }, AND: { argument: "value", run: LOGICAL_INSTRUCTION((one, another) => one & another) }, EOR: { argument: "value", run: LOGICAL_INSTRUCTION((one, another) => one ^ another) }, ORA: { argument: "value", run: LOGICAL_INSTRUCTION((one, another) => one | another) }, // --------- // BRANCHING // --------- BCC: { argument: "address", run: B__("c", false) }, BCS: { argument: "address", run: B__("c", true) }, BEQ: { argument: "address", run: B__("z", true) }, BMI: { argument: "address", run: B__("n", true) }, BNE: { argument: "address", run: B__("z", false) }, BPL: { argument: "address", run: B__("n", false) }, BVC: { argument: "address", run: B__("v", false) }, BVS: { argument: "address", run: B__("v", true) }, JMP: { argument: "address", run(cpu, address) { cpu.pc.setValue(address); } }, JSR: { argument: "address", run(cpu, address) { cpu.stack.push16(cpu.pc.getValue() - 1); cpu.pc.setValue(address); } }, RTI: { argument: "no", run(cpu) { cpu.flags.setValue(cpu.stack.pop()); cpu.pc.setValue(cpu.stack.pop16()); } }, RTS: { argument: "no", run(cpu) { cpu.pc.setValue(cpu.stack.pop16() + 1); } }, // ------ // SYSTEM // ------ BRK: { argument: "no", run(cpu) { cpu.interrupt(_interrupts.default.IRQ, true); } }, NOP: { argument: "no", run() {} } }; for (let key in instructions) { instructions[key].id = key; } var _default = exports.default = instructions;