v6502/vm/computer/cpu6502.js

1533 lines
40 KiB
JavaScript

/*
* 6502 Based Virtual Computer
* Copyright (C) 2011 Scott C. Duensing <scott@jaegertech.com>
*
* 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);
};
}