<commit_before>// Copyright (C) 2008 <NAME>.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// js8080 original by <NAME> (http://www.bluishcoder.co.nz/js8080/)
//        modified by <NAME>, 2010
//        modified by <NAME>, 2013   (http://www.compucolor.org)

function pad(str, n) {
  var r = [];
  for(var i=0; i < (n - str.length); ++i)
    r.push("0");
  r.push(str);
  return r.join("");
};

function Cpu(raw_ram, rd_func, wr_func, input_func, output_func, intvec_func)
{
  // cache/localize resource accessor functions
  this.ram    = raw_ram;      // should only be used by the disassembler
  this.rd     = rd_func;
  this.wr     = wr_func;
  this.input  = input_func;
  this.output = output_func;
  this.intvec = intvec_func;
  this.reset();

  // pick a style of disassembly you prefer
  this.dasmStyle = [ 'zilog', 'intel', 'both' ][2];
}

Cpu.CARRY     = 0x01;
Cpu.PARITY    = 0x04;
Cpu.HALFCARRY = 0x10;
Cpu.INTERRUPT = 0x20;
Cpu.ZERO      = 0x40;
Cpu.SIGN      = 0x80;


Cpu.prototype.reset = function () {
  this.b = 0;
  this.c = 0;
  this.d = 0;
  this.e = 0;
  this.f = 0;
  this.h = 0;
  this.l = 0;
  this.a = 0;
  this.pc = 0;
  this.sp = 0x0000;
  this.halted = false;
  this.intpending = false;
};

Cpu.prototype.af = function() {
  return this.a << 8 | this.f;
};

Cpu.prototype.AF = function(n) {
  this.a = n >> 8 & 0xFF;
  this.f = n & 0xFF;
};

Cpu.prototype.bc = function () {
  return this.b << 8 | this.c;
};

Cpu.prototype.BC = function(n) {
  this.b = n >> 8 & 0xFF;
  this.c = n & 0xFF;
};

Cpu.prototype.de = function () {
  return this.d << 8 | this.e;
};

Cpu.prototype.DE = function(n) {
  this.d = n >> 8 & 0xFF;
  this.e = n & 0xFF;
};

Cpu.prototype.hl = function () {
  return this.h << 8 | this.l;
};

Cpu.prototype.HL = function(n) {
  this.h = n >> 8 & 0xFF;
  this.l = n & 0xFF;
};

Cpu.prototype.set = function(flag) {
  this.f |= flag;
};

Cpu.prototype.clear = function(flag) {
  this.f &= ~flag & 0xFF;
};

Cpu.prototype.toString = function() {
  return "{" +
    " af: " + pad(this.af().toString(16),4) +
    " bc: " + pad(this.bc().toString(16),4) +
    " de: " + pad(this.de().toString(16),4) +
    " hl: " + pad(this.hl().toString(16),4) +
    " pc: " + pad(this.pc.toString(16),4) +
    " sp: " + pad(this.sp.toString(16),4) +
    " flags: " +
    (this.f & Cpu.ZERO ? "z" : ".") +
    (this.f & Cpu.SIGN ? "s" : ".") +
    (this.f & Cpu.PARITY ? "p" : ".") +
    (this.f & Cpu.CARRY ? "c" : ".") +
    " " + this.disassemble1(this.pc)[1] +
    " }";
};

Cpu.prototype.cpuStatus = function() {
  var s = "";
  s += " AF:"+pad(this.af().toString(16),4);
  s += " " +
       (this.f & Cpu.SIGN ? "s" : ".") +
       (this.f & Cpu.ZERO ? "z" : ".") +
       (this.f & Cpu.INTERRUPT ? "I" : ".") +
       (this.f & Cpu.HALFCARRY ? "h" : ".") +
       (this.f & Cpu.PARITY ? "p" : ".") +
       (this.f & Cpu.CARRY ? "c" : ".");
  s += " BC:"+pad(this.bc().toString(16),4);
  s += " DE:"+pad(this.de().toString(16),4);
  s += " HL:"+pad(this.hl().toString(16),4);
  s += " (HL):"+pad(this.rd(this.hl()).toString(16),2);
  s += " SP:"+pad(this.sp.toString(16),4);
  s += " PC:"; //+pad(this.pc.toString(16),4);
  s += this.disassemble1(this.pc)[1];
  return s;
};

Cpu.prototype.halted = function() {
  return this.halted;
};

// Step through one instruction
Cpu.prototype.step = function() {
  var i = this.rd(this.pc++);
  this.pc &= 0xFFFF;
  var r = this.execute(i);
  return r;
};

Cpu.prototype.writePort = function (port, v) {
  this.output(port, v);
  return this;
};

Cpu.prototype.readPort = function (port) {
  return this.input(port);
};

Cpu.prototype.getByte = function (addr) {
  return this.rd(addr);
};

Cpu.prototype.getWord = function (addr) {
  var l = this.rd(addr);
  var h = this.rd(addr+1);
  return h << 8 | l;
};

Cpu.prototype.nextByte = function() {
  var b = this.rd(this.pc++);
  this.pc &= 0xFFFF;
  return b;
};

Cpu.prototype.nextWord = function() {
  var pc = this.pc;
  var l = this.rd(pc++);
  var h = this.rd(pc++);
  this.pc = pc & 0xFFFF;
  return h << 8 | l;
};

Cpu.prototype.writeByte = function(addr, value) {
  var v = value & 0xFF;
  this.wr(addr, v);
  return this;
};

Cpu.prototype.writeWord = function(addr, value) {
  var l = value;
  var h = value >> 8;
  this.writeByte(addr, l);
  this.writeByte(addr+1, h);
  return this;
};

// use this for address arithmetic
Cpu.prototype.add = function(a, b) {
  return (a + b) & 0xffff;
};

// set flags after arithmetic and logical ops
Cpu.prototype.calcFlags = function(v, lhs, rhs) {
  var x = v & 0xFF;

  // calc parity (see <NAME> "Hackers Delight", page 74)
  var y = x ^ (x >> 1);
  y ^= y >> 2;
  y ^= y >> 4;

  if (y & 1)
    this.f &= ~Cpu.PARITY & 0xFF; // PO
  else
    this.f |= Cpu.PARITY; // PE

  if (v & 0x80)
    this.f |= Cpu.SIGN;
  else
    this.f &= ~Cpu.SIGN & 0xFF;

  if (x)
    this.f &= ~Cpu.ZERO & 0xFF;
  else
    this.f |= Cpu.ZERO;

  if (((rhs ^ v) ^ lhs) & 0x10)
    this.f |= Cpu.HALFCARRY;
  else
    this.f &= ~Cpu.HALFCARRY & 0xFF;

  if (v >= 0x100 || v < 0)
    this.f |= Cpu.CARRY;
  else
    this.f &= ~Cpu.CARRY & 0xFF;

  return x;
};

Cpu.prototype.incrementByte = function(o) {
  var c = this.f & Cpu.CARRY; // carry isnt affected
  var r = this.calcFlags(o+1, o, 1);
  this.f = (this.f & ~Cpu.CARRY & 0xFF) | c;
  return r;
};

Cpu.prototype.decrementByte = function(o) {
  var c = this.f & Cpu.CARRY; // carry isnt affected
  var r = this.calcFlags(o-1, o, 1);
  this.f = (this.f & ~Cpu.CARRY & 0xFF) | c;
  return r;
};

Cpu.prototype.addByte = function(lhs, rhs) {
  return this.calcFlags(lhs + rhs, lhs, rhs);
};

Cpu.prototype.addByteWithCarry = function(lhs, rhs) {
  return this.addByte(lhs, rhs + ((this.f & Cpu.CARRY) ? 1 : 0));
};

Cpu.prototype.subtractByte = function(lhs, rhs) {
  return this.calcFlags(lhs - rhs, lhs, rhs);
};

Cpu.prototype.subtractByteWithCarry = function(lhs, rhs) {
  return this.subtractByte(lhs, rhs + ((this.f & Cpu.CARRY) ? 1 : 0));
};

Cpu.prototype.andByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs & rhs, lhs, rhs);
  this.f |= Cpu.HALFCARRY;
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.xorByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs ^ rhs, lhs, rhs);
  this.f |= Cpu.HALFCARRY;
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.orByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs | rhs, lhs, rhs);
  this.f |= Cpu.HALFCARRY;
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.addWord = function(lhs, rhs) {
  var r = lhs + rhs;
  if (r > 0xFFFF)
    this.f |= Cpu.CARRY;
  else
    this.f &= ~Cpu.CARRY & 0xFF;
  return r & 0xFFFF;
};

Cpu.prototype.pop = function() {
  var pc = this.getWord(this.sp);
  this.sp = (this.sp + 2) & 0xFFFF;
  return pc;
};

Cpu.prototype.push = function(v) {
  this.sp = (this.sp - 2) & 0xFFFF;
  this.writeWord(this.sp, v);
};

// on interrupt, the interrupting device has to supply an interrupt
// vector (in real life, it could supply any one byte instruction,
// but typically it is an "RST n" operation.
Cpu.prototype.irq = function(req) {
  this.intpending = req;
}

// execute one instruction, and returns the number of cycles it took
Cpu.prototype.execute = function(i) {
  var cycles, op;
  if (this.intpending && (this.f & Cpu.INTERRUPT)) {
    // take an interrupt
    this.halted = false;
    this.f &= ~Cpu.INTERRUPT;  // disable interrupt
    // the routine which called this one has already incremented the PC.
    // undo it so it will be fetched again after the ISR.
    this.pc = (this.pc - 1) & 0xFFFF;
    // this.intpending isn't automatically cleared because it is the job of
    // intvec() to evalute if more interrupts are pending and call cpu.irq()
    // with the new status
    op = this.intvec();         // fetch RST n
    cycles = this.execute(op);  // do it
  } else if (this.halted) {
    // just burn time until either reset or an interrupt
    cycles = 4;
  } else switch(i) {
  case 0x00:
    {
      // NOP
      cycles = 4;
    }
    break;
  case 0x01:
    {
      // LD BC,nn
      this.BC(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x02:
    {
      // LD (BC),A
      this.writeByte(this.bc(), this.a);
      cycles = 7;
    }
    break;
  case 0x03:
    {
      // INC BC
      this.BC((this.bc() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x04:
    {
      // INC  B
      this.b = this.incrementByte(this.b);
      cycles = 5;
    }
    break;
  case 0x05:
    {
      // DEC  B
      this.b = this.decrementByte(this.b);
      cycles = 5;
    }
    break;
  case 0x06:
    {
      // LD   B,n
      this.b = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x07:
    {
      // RLCA
      var l = (this.a & 0x80) >> 7;
      if (l)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;

      this.a = ((this.a << 1) & 0xFE) | l;
      cycles = 4;
    }
    break;
  case 0x09:
    {
      // ADD  HL,BC
      this.HL(this.addWord(this.hl(), this.bc()));
      cycles = 11;
    }
    break;
  case 0x0A:
    {
      // LD   A,(BC)
      this.a = this.rd(this.bc());
      cycles = 7;
    }
    break;
  case 0x0B:
    {
      // DEC  BC
      this.BC((this.bc() - 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x0C:
    {
      // INC  C
      this.c = this.incrementByte(this.c);
      cycles = 5;
    }
    break;
  case 0x0D:
    {
      // DEC  C
      this.c = this.decrementByte(this.c);
      cycles = 5;
    }
    break;
  case 0x0E:
    {
      // LD   C,n
      this.c = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x0F:
    {
      // RRCA
      var h = (this.a & 1) << 7;
      if (h)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;

      this.a = ((this.a >> 1) & 0x7F) | h;
      cycles = 4;
    }
    break;
  case 0x11:
    {
      // LD   DE,nn
      this.DE(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x12:
    {
      // LD   (DE),A
      this.writeByte(this.de(), this.a);
      cycles = 7;
    }
    break;
  case 0x13:
    {
      // INC  DE
      this.DE((this.de() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x14:
    {
      // INC  D
      this.d = this.incrementByte(this.d);
      cycles = 5;
    }
    break;
  case 0x15:
    {
      // DEC  D
      this.d = this.decrementByte(this.d);
      cycles = 5;
    }
    break;
  case 0x16:
    {
      // LD   D,n
      this.d = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x17:
    {
      // RLA
      var c = (this.f & Cpu.CARRY) ? 1 : 0;
      if(this.a & 0x80)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;
      this.a = ((this.a << 1) & 0xFE) | c;
      cycles = 4;
    }
    break;
  case 0x19:
    {
      // ADD  HL,DE
      this.HL(this.addWord(this.hl(), this.de()));
      cycles = 11;
    }
    break;
  case 0x1A:
    {
      // LD   A,(DE)
      this.a = this.rd(this.de());
      cycles = 7;
    }
    break;
  case 0x1B:
    {
      // DEC  DE
      this.DE((this.de() - 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x1C:
    {
      // INC  E
      this.e = this.incrementByte(this.e);
      cycles = 5;
    }
    break;
  case 0x1D:
    {
      // DEC  E
      this.e = this.decrementByte(this.e);
      cycles = 5;
    }
    break;
  case 0x1E:
    {
      // LD   E,n
      this.e = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x1F:
    {
      // RRA
      var c = (this.f & Cpu.CARRY) ? 0x80 : 0;
      if(this.a & 1)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;
      this.a = ((this.a >> 1) & 0x7F) | c;
      cycles = 4;
    }
    break;
  case 0x21:
    {
      // LD   HL,nn
      this.HL(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x22:
    {
      // LD   (nn),HL
      this.writeWord(this.nextWord(), this.hl());
      cycles = 16;
    }
    break;
  case 0x23:
    {
      // INC  HL
      this.HL((this.hl() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x24:
    {
      // INC  H
      this.h = this.incrementByte(this.h);
      cycles = 5;
    }
    break;
  case 0x25:
    {
      // DEC  H
      this.h = this.decrementByte(this.h);
      cycles = 5;
    }
    break;
  case 0x26:
    {
      // LD   H,n
      this.h = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x27:
    {
      // DAA
      var p1 = ((this.f & Cpu.HALFCARRY) || (this.a & 0x0f) > 9) ? 6 : 0;
      this.a = this.calcFlags(this.a+p1, this.a, p1);
      var p3 = ((this.f & Cpu.CARRY) || (this.a & 0xf0) > 0x90) ? 0x60 : 0;
      this.a = this.calcFlags(this.a+p3, this.a, p3);
      cycles = 4;
    }
    break;
  case 0x29:
    {
      // ADD  HL,HL<commit_msg>Fix bugs in AND, OR, XOR, and DAA operation<commit_after>// Copyright (C) 2008 <NAME>.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// js8080 original by <NAME> (http://www.bluishcoder.co.nz/js8080/)
//        modified by <NAME>, 2010 (http://www.tramm.li/i8080/)
//        modified by <NAME>, 2013   (http://www.compucolor.org)

function pad(str, n) {
  var r = [];
  for(var i=0; i < (n - str.length); ++i)
    r.push("0");
  r.push(str);
  return r.join("");
};

function Cpu(raw_ram, rd_func, wr_func, input_func, output_func, intvec_func)
{
  // cache/localize resource accessor functions
  this.ram    = raw_ram;      // should only be used by the disassembler
  this.rd     = rd_func;
  this.wr     = wr_func;
  this.input  = input_func;
  this.output = output_func;
  this.intvec = intvec_func;
  this.reset();

  // pick a style of disassembly you prefer
  this.dasmStyle = [ 'zilog', 'intel', 'both' ][2];
}

Cpu.CARRY     = 0x01;
Cpu.PARITY    = 0x04;
Cpu.HALFCARRY = 0x10;
Cpu.INTERRUPT = 0x20;
Cpu.ZERO      = 0x40;
Cpu.SIGN      = 0x80;


Cpu.prototype.reset = function () {
  this.b = 0;
  this.c = 0;
  this.d = 0;
  this.e = 0;
  this.f = 0;
  this.h = 0;
  this.l = 0;
  this.a = 0;
  this.pc = 0;
  this.sp = 0x0000;
  this.halted = false;
  this.intpending = false;
};

Cpu.prototype.af = function() {
  return this.a << 8 | this.f;
};

Cpu.prototype.AF = function(n) {
  this.a = n >> 8 & 0xFF;
  this.f = n & 0xFF;
};

Cpu.prototype.bc = function () {
  return this.b << 8 | this.c;
};

Cpu.prototype.BC = function(n) {
  this.b = n >> 8 & 0xFF;
  this.c = n & 0xFF;
};

Cpu.prototype.de = function () {
  return this.d << 8 | this.e;
};

Cpu.prototype.DE = function(n) {
  this.d = n >> 8 & 0xFF;
  this.e = n & 0xFF;
};

Cpu.prototype.hl = function () {
  return this.h << 8 | this.l;
};

Cpu.prototype.HL = function(n) {
  this.h = n >> 8 & 0xFF;
  this.l = n & 0xFF;
};

Cpu.prototype.set = function(flag) {
  this.f |= flag;
};

Cpu.prototype.clear = function(flag) {
  this.f &= ~flag & 0xFF;
};

Cpu.prototype.toString = function() {
  return "{" +
    " af: " + pad(this.af().toString(16),4) +
    " bc: " + pad(this.bc().toString(16),4) +
    " de: " + pad(this.de().toString(16),4) +
    " hl: " + pad(this.hl().toString(16),4) +
    " pc: " + pad(this.pc.toString(16),4) +
    " sp: " + pad(this.sp.toString(16),4) +
    " flags: " +
    (this.f & Cpu.ZERO ? "z" : ".") +
    (this.f & Cpu.SIGN ? "s" : ".") +
    (this.f & Cpu.PARITY ? "p" : ".") +
    (this.f & Cpu.CARRY ? "c" : ".") +
    " " + this.disassemble1(this.pc)[1] +
    " }";
};

Cpu.prototype.cpuStatus = function() {
  var s = "";
  s += " AF:"+pad(this.af().toString(16),4);
  s += " " +
       (this.f & Cpu.SIGN ? "s" : ".") +
       (this.f & Cpu.ZERO ? "z" : ".") +
       (this.f & Cpu.INTERRUPT ? "I" : ".") +
       (this.f & Cpu.HALFCARRY ? "h" : ".") +
       (this.f & Cpu.PARITY ? "p" : ".") +
       (this.f & Cpu.CARRY ? "c" : ".");
  s += " BC:"+pad(this.bc().toString(16),4);
  s += " DE:"+pad(this.de().toString(16),4);
  s += " HL:"+pad(this.hl().toString(16),4);
  s += " (HL):"+pad(this.rd(this.hl()).toString(16),2);
  s += " SP:"+pad(this.sp.toString(16),4);
  s += " PC:"; //+pad(this.pc.toString(16),4);
  s += this.disassemble1(this.pc)[1];
  return s;
};

Cpu.prototype.halted = function() {
  return this.halted;
};

// Step through one instruction
Cpu.prototype.step = function() {
  var i = this.rd(this.pc++);
  this.pc &= 0xFFFF;
  var r = this.execute(i);
  return r;
};

Cpu.prototype.writePort = function (port, v) {
  this.output(port, v);
  return this;
};

Cpu.prototype.readPort = function (port) {
  return this.input(port);
};

Cpu.prototype.getByte = function (addr) {
  return this.rd(addr);
};

Cpu.prototype.getWord = function (addr) {
  var l = this.rd(addr);
  var h = this.rd(addr+1);
  return h << 8 | l;
};

Cpu.prototype.nextByte = function() {
  var b = this.rd(this.pc++);
  this.pc &= 0xFFFF;
  return b;
};

Cpu.prototype.nextWord = function() {
  var pc = this.pc;
  var l = this.rd(pc++);
  var h = this.rd(pc++);
  this.pc = pc & 0xFFFF;
  return h << 8 | l;
};

Cpu.prototype.writeByte = function(addr, value) {
  var v = value & 0xFF;
  this.wr(addr, v);
  return this;
};

Cpu.prototype.writeWord = function(addr, value) {
  var l = value;
  var h = value >> 8;
  this.writeByte(addr, l);
  this.writeByte(addr+1, h);
  return this;
};

// use this for address arithmetic
Cpu.prototype.add = function(a, b) {
  return (a + b) & 0xffff;
};

// set flags after arithmetic and logical ops
Cpu.prototype.calcFlags = function(v, lhs, rhs) {
  var x = v & 0xFF;

  // calc parity (see <NAME> "Hackers Delight", page 74)
  var y = x ^ (x >> 1);
  y ^= y >> 2;
  y ^= y >> 4;

  if (y & 1)
    this.f &= ~Cpu.PARITY & 0xFF; // PO
  else
    this.f |= Cpu.PARITY; // PE

  if (v & 0x80)
    this.f |= Cpu.SIGN;
  else
    this.f &= ~Cpu.SIGN & 0xFF;

  if (x)
    this.f &= ~Cpu.ZERO & 0xFF;
  else
    this.f |= Cpu.ZERO;

  if (((rhs ^ v) ^ lhs) & 0x10)
    this.f |= Cpu.HALFCARRY;
  else
    this.f &= ~Cpu.HALFCARRY & 0xFF;

  if (v >= 0x100 || v < 0)
    this.f |= Cpu.CARRY;
  else
    this.f &= ~Cpu.CARRY & 0xFF;

  return x;
};

Cpu.prototype.incrementByte = function(o) {
  var c = this.f & Cpu.CARRY; // carry isnt affected
  var r = this.calcFlags(o+1, o, 1);
  this.f = (this.f & ~Cpu.CARRY & 0xFF) | c;
  return r;
};

Cpu.prototype.decrementByte = function(o) {
  var c = this.f & Cpu.CARRY; // carry isnt affected
  var r = this.calcFlags(o-1, o, 1);
  this.f = (this.f & ~Cpu.CARRY & 0xFF) | c;
  return r;
};

Cpu.prototype.addByte = function(lhs, rhs) {
  return this.calcFlags(lhs + rhs, lhs, rhs);
};

Cpu.prototype.addByteWithCarry = function(lhs, rhs) {
  return this.addByte(lhs, rhs + ((this.f & Cpu.CARRY) ? 1 : 0));
};

Cpu.prototype.subtractByte = function(lhs, rhs) {
  return this.calcFlags(lhs - rhs, lhs, rhs);
};

Cpu.prototype.subtractByteWithCarry = function(lhs, rhs) {
  return this.subtractByte(lhs, rhs + ((this.f & Cpu.CARRY) ? 1 : 0));
};

Cpu.prototype.andByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs & rhs, lhs, rhs);
  this.f &= ~(Cpu.HALFCARRY) & 0xFF;
  if ((lhs | rhs) & 0x08) {
    this.f |= Cpu.HALFCARRY;
  }
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.xorByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs ^ rhs, lhs, rhs);
  this.f &= ~Cpu.HALFCARRY & 0xFF;
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.orByte = function(lhs, rhs) {
  var x = this.calcFlags(lhs | rhs, lhs, rhs);
  this.f &= ~Cpu.HALFCARRY & 0xFF;
  this.f &= ~Cpu.CARRY & 0xFF;
  return x;
};

Cpu.prototype.addWord = function(lhs, rhs) {
  var r = lhs + rhs;
  if (r > 0xFFFF)
    this.f |= Cpu.CARRY;
  else
    this.f &= ~Cpu.CARRY & 0xFF;
  return r & 0xFFFF;
};

Cpu.prototype.pop = function() {
  var pc = this.getWord(this.sp);
  this.sp = (this.sp + 2) & 0xFFFF;
  return pc;
};

Cpu.prototype.push = function(v) {
  this.sp = (this.sp - 2) & 0xFFFF;
  this.writeWord(this.sp, v);
};

// on interrupt, the interrupting device has to supply an interrupt
// vector (in real life, it could supply any one byte instruction,
// but typically it is an "RST n" operation.
Cpu.prototype.irq = function(req) {
  this.intpending = req;
}

// execute one instruction, and returns the number of cycles it took
Cpu.prototype.execute = function(i) {
  var cycles, op;
  if (this.intpending && (this.f & Cpu.INTERRUPT)) {
    // take an interrupt
    this.halted = false;
    this.f &= ~Cpu.INTERRUPT;  // disable interrupt
    // the routine which called this one has already incremented the PC.
    // undo it so it will be fetched again after the ISR.
    this.pc = (this.pc - 1) & 0xFFFF;
    // this.intpending isn't automatically cleared because it is the job of
    // intvec() to evalute if more interrupts are pending and call cpu.irq()
    // with the new status
    op = this.intvec();         // fetch RST n
    cycles = this.execute(op);  // do it
  } else if (this.halted) {
    // just burn time until either reset or an interrupt
    cycles = 4;
  } else switch(i) {
  case 0x00:
    {
      // NOP
      cycles = 4;
    }
    break;
  case 0x01:
    {
      // LD BC,nn
      this.BC(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x02:
    {
      // LD (BC),A
      this.writeByte(this.bc(), this.a);
      cycles = 7;
    }
    break;
  case 0x03:
    {
      // INC BC
      this.BC((this.bc() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x04:
    {
      // INC  B
      this.b = this.incrementByte(this.b);
      cycles = 5;
    }
    break;
  case 0x05:
    {
      // DEC  B
      this.b = this.decrementByte(this.b);
      cycles = 5;
    }
    break;
  case 0x06:
    {
      // LD   B,n
      this.b = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x07:
    {
      // RLCA
      var l = (this.a & 0x80) >> 7;
      if (l)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;

      this.a = ((this.a << 1) & 0xFE) | l;
      cycles = 4;
    }
    break;
  case 0x09:
    {
      // ADD  HL,BC
      this.HL(this.addWord(this.hl(), this.bc()));
      cycles = 11;
    }
    break;
  case 0x0A:
    {
      // LD   A,(BC)
      this.a = this.rd(this.bc());
      cycles = 7;
    }
    break;
  case 0x0B:
    {
      // DEC  BC
      this.BC((this.bc() - 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x0C:
    {
      // INC  C
      this.c = this.incrementByte(this.c);
      cycles = 5;
    }
    break;
  case 0x0D:
    {
      // DEC  C
      this.c = this.decrementByte(this.c);
      cycles = 5;
    }
    break;
  case 0x0E:
    {
      // LD   C,n
      this.c = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x0F:
    {
      // RRCA
      var h = (this.a & 1) << 7;
      if (h)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;

      this.a = ((this.a >> 1) & 0x7F) | h;
      cycles = 4;
    }
    break;
  case 0x11:
    {
      // LD   DE,nn
      this.DE(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x12:
    {
      // LD   (DE),A
      this.writeByte(this.de(), this.a);
      cycles = 7;
    }
    break;
  case 0x13:
    {
      // INC  DE
      this.DE((this.de() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x14:
    {
      // INC  D
      this.d = this.incrementByte(this.d);
      cycles = 5;
    }
    break;
  case 0x15:
    {
      // DEC  D
      this.d = this.decrementByte(this.d);
      cycles = 5;
    }
    break;
  case 0x16:
    {
      // LD   D,n
      this.d = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x17:
    {
      // RLA
      var c = (this.f & Cpu.CARRY) ? 1 : 0;
      if(this.a & 0x80)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;
      this.a = ((this.a << 1) & 0xFE) | c;
      cycles = 4;
    }
    break;
  case 0x19:
    {
      // ADD  HL,DE
      this.HL(this.addWord(this.hl(), this.de()));
      cycles = 11;
    }
    break;
  case 0x1A:
    {
      // LD   A,(DE)
      this.a = this.rd(this.de());
      cycles = 7;
    }
    break;
  case 0x1B:
    {
      // DEC  DE
      this.DE((this.de() - 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x1C:
    {
      // INC  E
      this.e = this.incrementByte(this.e);
      cycles = 5;
    }
    break;
  case 0x1D:
    {
      // DEC  E
      this.e = this.decrementByte(this.e);
      cycles = 5;
    }
    break;
  case 0x1E:
    {
      // LD   E,n
      this.e = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x1F:
    {
      // RRA
      var c = (this.f & Cpu.CARRY) ? 0x80 : 0;
      if(this.a & 1)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;
      this.a = ((this.a >> 1) & 0x7F) | c;
      cycles = 4;
    }
    break;
  case 0x21:
    {
      // LD   HL,nn
      this.HL(this.nextWord());
      cycles = 10;
    }
    break;
  case 0x22:
    {
      // LD   (nn),HL
      this.writeWord(this.nextWord(), this.hl());
      cycles = 16;
    }
    break;
  case 0x23:
    {
      // INC  HL
      this.HL((this.hl() + 1) & 0xFFFF);
      cycles = 6;
    }
    break;
  case 0x24:
    {
      // INC  H
      this.h = this.incrementByte(this.h);
      cycles = 5;
    }
    break;
  case 0x25:
    {
      // DEC  H
      this.h = this.decrementByte(this.h);
      cycles = 5;
    }
    break;
  case 0x26:
    {
      // LD   H,n
      this.h = this.nextByte();
      cycles = 7;
    }
    break;
  case 0x27:
    {
      // DAA -- algorithm taken from <NAME>'s "ksim.c" (brouhaha.com)
      var adjust = 0x00;
      var nib0 = (this.a & 0xf);
      var nib1 = (this.a >> 4);

      if ((nib0 > 9) || (this.f & Cpu.HALFCARRY))
	adjust = 0x06;
      if ((nib1 > 9) ||
          (this.f & Cpu.CARRY) ||
          ((nib1 === 9) && ((this.f & Cpu.CARRY) || (nib0 > 9))))
	adjust |= 0x60;

      var new_ac = (nib0 >= 0xa);
      var new_cy = (((nib1 >= 9) && (nib0 >= 10)) || (nib1 >= 10));

      this.a = this.calcFlags(this.a + adjust, this.a, adjust);

      if (new_ac)
	this.f |= Cpu.HALFCARRY;
      else
	this.f &= ~Cpu.HALFCARRY & 0xFF;

      if (new_cy)
	this.f |= Cpu.CARRY;
      else
	this.f &= ~Cpu.CARRY & 0xFF;

      cycles = 4;
    }
    break;
  case 0x29:
    {
      // ADD  HL,HL
{'fraction_non_alphanumeric': 0.17008818342151674, 'fraction_numerical': 0.027195767195767197, 'mean_word_length': 2.0494783263418306, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 5, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3256546', 'n_tokens_mistral': 12171, 'n_tokens_neox': 11346, 'n_words': 2761}