/* * 6502 Based Virtual Computer * Copyright (C) 2011 Scott C. Duensing * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // 6502 CPU Core - based on http://www.6502asm.com function cpu6502() { // --- Private variables. var self = this; // Memory Pointer. var MEMORY = null; // Default Program Counter. var DEFAULT_PC = 0x600; // Registers. var REG_A = 0; var REG_X = 0; var REG_Y = 0; var REG_P = 0; var REG_PC = DEFAULT_PC; var REG_SP = 0x100; // Internal CPU Status. var CODE_RUNNING = false; // CPU Event Callback. var CALLBACK = null; // Instruction Function "Pointers". var INSTRUCTION_POINTERS = null; // --- Public variables, enums. this.E_HALT = 0; this.E_UNKNOWN_OPCODE = 1; this.E_STACK_OVERFLOW = 2; this.E_STACK_UNDERFLOW = 3; // --- Private methods, general. var doCompare = function(reg, val) { if (reg >= val) REG_P |= 1; else REG_P &= 0xfe; val = (reg - val); if (val) REG_P &= 0xfd; else REG_P |= 0x02; if (val & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; } // Might as well Jump... JUMP! var jumpBranch = function(offset) { if (offset > 0x7f) REG_PC = (REG_PC - (0x100 - offset)); else REG_PC = (REG_PC + offset); }; // Fetch the next byte pointed to by the program counter. var popByte = function() { return (MEMORY.readByte(REG_PC++) & 0xff); }; // Fetch the next word pointed to by the program counter. var popWord = function() { return popByte() + (popByte() << 8); }; // Run a block of instructions. This is used by 'run' to avoid blocking the browser event queue. var runBlock = function() { var instructions = 100; while ((instructions-- > 0) && (CODE_RUNNING)) self.execute(); if (CODE_RUNNING) setTimeout(runBlock, 0); }; // Pop a value off the stack. var stackPop = function() { if (REG_SP < 0x100) { var value = MEMORY.readByte(REG_SP + 0x100); REG_SP++; return value; } else { CODE_RUNNING = false; CALLBACK(self.E_STACK_UNDERFLOW); return 0; } }; // Push a value onto the stack. var stackPush = function(value) { if (REG_SP >= 0) { REG_SP--; MEMORY.writeByte((REG_SP & 0xff) + 0x100, value & 0xff); } else { CODE_RUNNING = false; CALLBACK(self.E_STACK_OVERFLOW); } }; var testSBC = function(value) { var vflag = 0; var tmp = 0; var w = 0; if ((REG_A ^ value) & 0x80) vflag = 1; if (REG_P & 8) { tmp = 0xf + (REG_A & 0xf) - (value & 0xf) + (REG_P & 1); if (tmp < 0x10) { w = 0; tmp -= 6; } else { w = 0x10; tmp -= 0x10; } w += 0xf0 + (REG_A & 0xf0) - (value & 0xf0); if (w < 0x100) { REG_P &= 0xfe; if ((REG_P & 0xbf) && w < 0x80) REG_P &= 0xbf; w -= 0x60; } else { REG_P |= 1; if ((REG_P & 0xbf) && w >= 0x180) REG_P &= 0xbf; } w += tmp; } else { w = 0xff + REG_A - value + (REG_P & 1); if (w<0x100) { REG_P &= 0xfe; if ((REG_P & 0xbf) && w < 0x80) REG_P &= 0xbf; } else { REG_P |= 1; if ((REG_P & 0xbf) && w >= 0x180) REG_P &= 0xbf; } } REG_A = w & 0xff; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; } var testADC = function(value) { var tmp = 0; if ((REG_A ^ value) & 0x80) REG_P &= 0xbf; else REG_P |= 0x40; if (REG_P & 8) { tmp = (REG_A & 0xf) + (value & 0xf) + (REG_P & 1); if (tmp >= 10) tmp = 0x10 | ((tmp + 6) & 0xf); tmp += (REG_A & 0xf0) + (value & 0xf0); if (tmp >= 160) { REG_P |= 1; if ((REG_P & 0xbf) && tmp >= 0x180) REG_P &= 0xbf; tmp += 0x60; } else { REG_P &= 0xfe; if ((REG_P & 0xbf) && tmp < 0x80) REG_P &= 0xbf; } } else { tmp = REG_A + value + (REG_P & 1); if (tmp >= 0x100) { REG_P |= 1; if ((REG_P & 0xbf) && tmp >= 0x180) REG_P &= 0xbf; } else { REG_P &= 0xfe; if ((REG_P & 0xbf) && tmp < 0x80) REG_P &= 0xbf; } } REG_A = tmp & 0xff; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; } // --- Private methods, CPU core. var i00 = function() { CODE_RUNNING = false; }; var i01 = function() { var addr = popByte() + REG_X; var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); REG_A |= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i05 = function() { var zp = popByte(); REG_A |= MEMORY.readByte(zp); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i06 = function() { var zp = popByte(); var value = MEMORY.readByte(zp); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; MEMORY.writeByte(zp, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i08 = function() { stackPush(REG_P); }; var i09 = function() { REG_A |= popByte(); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i0a = function() { REG_P = (REG_P & 0xfe) | ((REG_A >> 7) & 1); REG_A = REG_A << 1; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i0d = function() { REG_A |= MEMORY.readByte(popWord()); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i0e = function() { var addr = popWord(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 2; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i10 = function() { var offset = popByte(); if ((REG_P & 0x80) == 0) jumpBranch(offset); }; var i11 = function() { var zp = popByte(); var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; REG_A |= MEMORY.readByte(value); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i15 = function() { var addr = (popByte() + REG_X) & 0xff; REG_A |= MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i16 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i18 = function() { REG_P &= 0xfe; }; var i19 = function() { addr = popWord() + REG_Y; REG_A |= MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i1d = function() { var addr = popWord() + REG_X; REG_A |= MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i1e = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i20 = function() { var addr = popWord(); var currAddr = REG_PC - 1; stackPush(((currAddr >> 8) & 0xff)); stackPush((currAddr & 0xff)); REG_PC = addr; }; var i21 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); REG_A &= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i24 = function() { var zp = popByte(); var value = MEMORY.readByte(zp); if (value & REG_A) REG_P &= 0xfd; else REG_P |= 0x02; REG_P = (REG_P & 0x3f) | (value & 0xc0); }; var i25 = function() { var zp = popByte(); REG_A &= MEMORY.readByte(zp); if (REG_A) REG_P &= 0xfd; else REG_P |= 2; if (REG_A & 0x80) REG_P &= 0x80; else REG_P &= 0x7f; }; var i26 = function() { var sf = (REG_P & 1); var addr = popByte(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; value |= sf; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i28 = function() { REG_P = stackPop() | 0x20; }; var i29 = function() { REG_A &= popByte(); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i2a = function() { var sf = (REG_P & 1); REG_P = (REG_P & 0xfe) | ((REG_A >> 7) & 1); REG_A = REG_A << 1; REG_A |= sf; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i2c = function() { var value = MEMORY.readByte(popWord()); if (value & REG_A) REG_P &= 0xfd; else REG_P |= 0x02; REG_P = (REG_P & 0x3f) | (value & 0xc0); }; var i2d = function() { var value = MEMORY.readByte(popWord()); REG_A &= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i2e = function() { var sf = REG_P & 1; var addr = popWord(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; value |= sf; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i30 = function() { var offset = popByte(); if (REG_P & 0x80) jumpBranch(offset); }; var i31 = function() { var zp = popByte(); var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; REG_A &= MEMORY.readByte(value); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i35 = function() { var zp = popByte(); var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_X; REG_A &= MEMORY.readByte(value); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i36 = function() { var sf = REG_P & 1; var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; value |= sf; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i38 = function() { REG_P |= 1; }; var i39 = function() { var addr = popWord() + REG_Y; var value = MEMORY.readByte(addr); REG_A &= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i3d = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_A &= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i3e = function() { var sf = REG_P & 1; var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); value = value << 1; value |= sf; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i40 = function() { }; var i41 = function() { var zp = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); REG_A ^= MEMORY.readByte(value); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i45 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_A ^= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i46 = function() { var addr = popByte() & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; MEMORY.writeByte(addr, value); if (value != 0) REG_P &= 0xfd; else REG_P |= 2; if ((value & 0x80) == 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i48 = function() { stackPush(REG_A); }; var i49 = function() { REG_A ^= popByte(); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i4a = function() { REG_P = (REG_P & 0xfe) | (REG_A & 1); REG_A = REG_A >> 1; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i4c = function() { REG_PC = popWord(); }; var i4d = function() { var addr = popWord(); var value = MEMORY.readByte(addr); REG_A ^= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i4e = function() { var addr = popWord(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i50 = function() { var offset = popByte(); if ((REG_P & 0x40) == 0) jumpBranch(offset); }; var i51 = function() { var zp = popByte(); var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; REG_A ^= MEMORY.readByte(value); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i55 = function() { var addr = (popByte() + REG_X) & 0xff; REG_A ^= MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i56 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i58 = function() { }; var i59 = function() { var addr = popWord() + REG_Y; var value = MEMORY.readByte(addr); REG_A ^= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i5d = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_A ^= value; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i5e = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i60 = function() { REG_PC = (stackPop() + 1) | (stackPop() << 8); }; var i61 = function() { var zp = (popByte() + REG_X) & 0xff; var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); value = MEMORY.readByte(addr); testADC(value); }; var i65 = function() { var addr = popByte(); var value = MEMORY.readByte(addr); testADC(value); }; var i66 = function() { var sf = REG_P & 1; var addr = popByte(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; if (sf) value |= 0x80; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i68 = function() { REG_A = stackPop(); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i69 = function() { var value = popByte(); testADC(value); }; var i6a = function() { var sf = REG_P & 1; REG_P = (REG_P & 0xfe) | (REG_A & 1); REG_A = REG_A >> 1; if (sf) REG_A |= 0x80; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i6c = function() { }; var i6d = function() { var addr = popWord(); var value = MEMORY.readByte(addr); testADC(value); }; var i6e = function() { var sf = REG_P & 1; var addr = popWord(); var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; if (sf) value |= 0x80; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i70 = function() { var offset = popByte(); if (REG_P & 0x40) jumpBranch(offset); }; var i71 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); var value = MEMORY.readByte(addr + REG_Y); testADC(value); }; var i75 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); testADC(value); }; var i76 = function() { var sf = (REG_P & 1); var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; if (sf) value |= 0x80; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i78 = function() { }; var i79 = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_Y); testADC(value); }; var i7d = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_X); testADC(value); }; var i7e = function() { //var sf = REG_P & 1; var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); value = value >> 1; if (value) value |= 0x80; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i81 = function() { var zp = (popByte() + REG_X) & 0xff; var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); MEMORY.writeByte(addr, REG_A); }; var i84 = function() { MEMORY.writeByte(popByte(), REG_Y); }; var i85 = function() { MEMORY.writeByte(popByte(), REG_A); }; var i86 = function() { MEMORY.writeByte(popByte(), REG_X); }; var i88 = function() { REG_Y = (REG_Y - 1) & 0xff; if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i8a = function() { REG_A = REG_X & 0xff; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i8c = function() { MEMORY.writeByte(popWord(), REG_Y); }; var i8d = function() { MEMORY.writeByte(popWord(), REG_A); }; var i8e = function() { MEMORY.writeByte(popWord(), REG_X); }; var i90 = function() { var offset = popByte(); if ((REG_P & 1) == 0) jumpBranch(offset); }; var i91 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; MEMORY.writeByte(addr, REG_A); }; var i94 = function() { MEMORY.writeByte(popByte() + REG_X, REG_Y); }; var i95 = function() { MEMORY.writeByte(popByte() + REG_X, REG_A); }; var i96 = function() { MEMORY.writeByte(popByte() + REG_Y, REG_X); }; var i98 = function() { REG_A = REG_Y & 0xff; if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var i99 = function() { MEMORY.writeByte(popWord() + REG_Y, REG_A); }; var i9a = function() { REG_SP = REG_X & 0xff; }; var i9d = function() { var addr = popWord(); MEMORY.writeByte(addr + REG_X, REG_A); }; var ia0 = function() { REG_Y = popByte(); if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia1 = function() { var zp = (popByte() + REG_X) & 0xff; var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); REG_A = MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia2 = function() { REG_X = popByte(); if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia4 = function() { REG_Y = MEMORY.readByte(popByte()); if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia5 = function() { REG_A = MEMORY.readByte(popByte()); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia6 = function() { REG_X = MEMORY.readByte(popByte()); if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia8 = function() { REG_Y = REG_A & 0xff; if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ia9 = function() { REG_A = popByte(); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var iaa = function() { REG_X = REG_A & 0xff; if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var iac = function() { REG_Y = MEMORY.readByte(popWord()); if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var iad = function() { REG_A = MEMORY.readByte(popWord()); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var iae = function() { REG_X = MEMORY.readByte(popWord()); if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ib0 = function() { var offset = popByte(); if (REG_P & 1) jumpBranch(offset); }; var ib1 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; REG_A = MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ib4 = function() { REG_Y = MEMORY.readByte(popByte() + REG_X); if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ib5 = function() { REG_A = MEMORY.readByte((popByte() + REG_X) & 0xff); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ib6 = function() { REG_X = MEMORY.readByte(popByte() + REG_Y); if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ib8 = function() { REG_P &= 0xbf; }; var ib9 = function() { var addr = popWord() + REG_Y; REG_A = MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var iba = function() { REG_X = REG_SP & 0xff; }; var ibc = function() { var addr = popWord() + REG_X; REG_Y = MEMORY.readByte(addr); if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ibd = function() { var addr = popWord() + REG_X; REG_A = MEMORY.readByte(addr); if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ibe = function() { var addr = popWord() + REG_Y; REG_X = MEMORY.readByte(addr); if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ic0 = function() { var value = popByte(); if ((REG_Y + value) > 0xff) REG_P |= 1; else REG_P &= 0xfe; //var ov = value; value = (REG_Y-value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ic1 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; var ic4 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_Y, value); }; var ic5 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_A, value); }; var ic6 = function() { var zp = popByte(); var value = MEMORY.readByte(zp); --value; MEMORY.writeByte(zp, value & 0xff); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ic8 = function() { REG_Y = (REG_Y + 1) & 0xff; if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ic9 = function() { var value = popByte(); doCompare(REG_A, value); }; var ica = function() { REG_X = (REG_X - 1) & 0xff; if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var icc = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_Y, value); }; var icd = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_A, value); }; /* var ice = function() { var addr = popWord(); var value = MEMORY.readByte(addr); --value; value = value & 0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; */ var id0 = function() { var offset = popByte(); if ((REG_P & 2) == 0) jumpBranch(offset); }; var id1 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; var id5 = function() { var value = MEMORY.readByte(popByte() + REG_X); doCompare(REG_A, value); }; var id6 = function() { var addr = popByte() + REG_X; var value = MEMORY.readByte(addr); --value; value = value & 0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var id8 = function() { REG_P &= 0xf7; }; var id9 = function() { var addr = popWord() + REG_Y; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; var idd = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; var ide = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); --value; value = value&0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ie0 = function() { var value = popByte(); doCompare(REG_X, value); }; var ie1 = function() { var zp = (popByte()+REG_X)&0xff; var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); var value = MEMORY.readByte(addr); testSBC(value); }; var ie4 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_X, value); }; var ie5 = function() { var addr = popByte(); var value = MEMORY.readByte(addr); testSBC(value); }; var ie6 = function() { var zp = popByte(); var value = MEMORY.readByte(zp); ++value; value = value & 0xff; MEMORY.writeByte(zp, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ie8 = function() { REG_X = (REG_X + 1) & 0xff; if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ie9 = function() { var value = popByte(); testSBC(value); }; var iea = function() { }; var iec = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_X, value); }; var ied = function() { var addr = popWord(); var value = MEMORY.readByte(addr); testSBC(value); }; var iee = function() { var addr = popWord(); var value = MEMORY.readByte(addr); ++value; value = value & 0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var if0 = function() { var offset = popByte(); if (REG_P & 2) jumpBranch(offset); }; var if1 = function() { var zp = popByte(); var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); var value = MEMORY.readByte(addr + REG_Y); testSBC(value); }; var if5 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); REG_P = (REG_P & 0xfe) | (value & 1); testSBC(value); }; var if6 = function() { var addr = popByte() + REG_X; var value = MEMORY.readByte(addr); ++value; value = value & 0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var if8 = function() { REG_P |= 8; }; var if9 = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_Y); testSBC(value); }; var ifd = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_X); testSBC(value); }; var ife = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); ++value; value = value & 0xff; MEMORY.writeByte(addr, value); if (value) REG_P &= 0xfd; else REG_P |= 0x02; if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; }; var ierr = function() { CODE_RUNNING = false; CALLBACK(self.E_UNKNOWN_OPCODE); }; // --- Public methods. // Attach the CPU to the VM. this.attach = function(newMemory, newPC, newCallback) { MEMORY = newMemory; DEFAULT_PC = newPC; CALLBACK = newCallback; INSTRUCTION_POINTERS = [ i00, //00 i01, //01 ierr, //02 ierr, //03 ierr, //04 i05, //05 i06, //06 ierr, //07 i08, //08 i09, //09 i0a, //0a ierr, //0b ierr, //0c i0d, //0d i0e, //0e ierr, //0f i10, //10 i11, //11 ierr, //12 ierr, //13 ierr, //14 i15, //15 i16, //16 ierr, //17 i18, //18 i19, //19 ierr, //1a ierr, //1b ierr, //1c i1d, //1d i1e, //1e ierr, //1f i20, //20 i21, //21 ierr, //22 ierr, //23 i24, //24 i25, //25 i26, //26 ierr, //27 i28, //28 i29, //29 i2a, //2a ierr, //2b i2c, //2c i2d, //2d i2e, //2e ierr, //2f i30, //30 i31, //31 ierr, //32 ierr, //33 ierr, //34 i35, //35 i36, //36 ierr, //37 i38, //38 i39, //39 ierr, //3a ierr, //3b ierr, //3c i3d, //3d i3e, //3e ierr, //3f i40, //40 i41, //41 ierr, //42 ierr, //43 ierr, //44 i45, //45 i46, //46 ierr, //47 i48, //48 i49, //49 i4a, //4a ierr, //4b i4c, //4c i4d, //4d i4e, //4e ierr, //4f i50, //50 i51, //51 ierr, //52 ierr, //53 ierr, //54 i55, //55 i56, //56 ierr, //57 i58, //58 i59, //59 ierr, //5a ierr, //5b ierr, //5c i5d, //5d i5e, //5e ierr, //5f i60, //60 i61, //61 ierr, //62 ierr, //63 ierr, //64 i65, //65 i66, //66 ierr, //67 i68, //68 i69, //69 i6a, //6a ierr, //6b i6c, //6c i6d, //6d i6e, //6e ierr, //6f i70, //70 i71, //71 ierr, //72 ierr, //73 ierr, //74 i75, //75 i76, //76 ierr, //77 i78, //78 i79, //79 ierr, //7a ierr, //7b ierr, //7c i7d, //7d i7e, //7e ierr, //7f ierr, //80 i81, //81 ierr, //82 ierr, //83 i84, //84 i85, //85 i86, //86 ierr, //87 i88, //88 ierr, //89 i8a, //8a ierr, //8b i8c, //8c i8d, //8d i8e, //8e ierr, //8f i90, //90 i91, //91 ierr, //92 ierr, //93 i94, //94 i95, //95 i96, //96 ierr, //97 i98, //98 i99, //99 i9a, //9a ierr, //9b ierr, //9c i9d, //9d ierr, //9e ierr, //9f ia0, //a0 ia1, //a1 ia2, //a2 ierr, //a3 ia4, //a4 ia5, //a5 ia6, //a6 ierr, //a7 ia8, //a8 ia9, //a9 iaa, //aa ierr, //ab iac, //ac iad, //ad iae, //ae ierr, //af ib0, //b0 ib1, //b1 ierr, //b2 ierr, //b3 ib4, //b4 ib5, //b5 ib6, //b6 ierr, //b7 ib8, //b8 ib9, //b9 iba, //ba ierr, //bb ibc, //bc ibd, //bd ibe, //be ierr, //bf ic0, //c0 ic1, //c1 ierr, //c2 ierr, //c3 ic4, //c4 ic5, //c5 ic6, //c6 ierr, //c7 ic8, //c8 ic9, //c9 ica, //ca ierr, //cb icc, //cc icd, //cd ierr, //ce ierr, //cf id0, //d0 id1, //d1 ierr, //d2 ierr, //d3 ierr, //d4 id5, //d5 id6, //d6 ierr, //d7 id8, //d8 id9, //d9 ierr, //da ierr, //db ierr, //dc idd, //dd ide, //de ierr, //df ie0, //e0 ie1, //e1 ierr, //e2 ierr, //e3 ie4, //e4 ie5, //e5 ie6, //e6 ierr, //e7 ie8, //e8 ie9, //e9 iea, //ea ierr, //eb iec, //ec ied, //ed iee, //ee ierr, //ef if0, //f0 if1, //f1 ierr, //f2 ierr, //f3 ierr, //f4 if5, //f5 if6, //f6 ierr, //f7 if8, //f8 if9, //f9 ierr, //fa ierr, //fb ierr, //fc ifd, //fd ife, //fe ierr //ff ]; this.reset(); }; // Execute a single instruction. this.execute = function() { if (!CODE_RUNNING) return; var opcode = popByte(); //console.log("PC = 0x" + (REG_PC - 1).toString(16) + " OP = 0x" + opcode.toString(16)); INSTRUCTION_POINTERS[opcode](); if ((REG_PC == 0) || (!CODE_RUNNING)) { CODE_RUNNING = false; CALLBACK(self.E_HALT); } }; // Get PC. this.getPC = function() { return REG_PC; }; // Reset CPU. this.reset = function() { REG_A = 0; REG_X = 0; REG_Y = 0; REG_P = 0; REG_PC = DEFAULT_PC; REG_SP = 0x100; }; // Run until we die. this.run = function() { console.log("Starting CPU."); CODE_RUNNING = true; setTimeout(runBlock, 0); }; }