0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのZ80エミュレーターで遊ぶ。

Last updated at Posted at 2025-09-29

参考

結果

o_coq969.jpg

プログラム

  • オンラインコンパイラpaiza


process.stdin.resume();
process.stdin.setEncoding('utf8');
// Your code here!

import { Z80 } from "./Z80.js";

const mem = new Array(64 * 1024);

mem[0] = 0x3C; // INC A
mem[1] = 0x76; // HALT

const z80 = new Z80({
  mem_read: (address) => {
    console.log("mem_read", address.toString(16));
    return mem[address];
  },
  mem_write: (address, value) => {
    console.log("mem_write", address.toString(16), value);
    mem[address] = value;
  },
  io_read: (port) => {
    console.log("io read", port);
    return 0;
  },
  io_write: (port, value) => {
    console.log("io write", port, value);
  },
});
console.log(z80);
console.log(z80.getState());

z80.reset();
console.log(z80.run_instruction());
console.log(z80.run_instruction());
console.log(z80.run_instruction());
console.log(z80.run_instruction());
console.log(z80.getState());



package.json


{
    "type": "module"
}

Z80.js



///////////////////////////////////////////////////////////////////////////////
/// @file Z80.js
///
/// @brief Emulator for the Zilog Z80 microprocessor
///
/// @author Molly Howell
///
/// @remarks
///  This module is a simple, straightforward instruction interpreter.
///   There is no fancy dynamic recompilation or cycle-accurate emulation.
///   The author believes that this should be sufficient for any emulator that
///   would be feasible to write in JavaScript anyway.
///  The code and the comments in this file assume that the reader is familiar
///   with the Z80 architecture. If you're not, here are some references I use:
///  http://clrhome.org/table/ - Z80 instruction set tables
///  http://www.zilog.com/docs/z80/um0080.pdf - The official manual
///  http://www.myquest.nl/z80undocumented/z80-documented-v0.91.pdf
///   - The Undocumented Z80, Documented
///
/// @copyright (c) Molly Howell
///  This code is released under the MIT license,
///  a copy of which is available in the associated README.md file,
///  or at http://opensource.org/licenses/MIT
///////////////////////////////////////////////////////////////////////////////

"use strict";

///////////////////////////////////////////////////////////////////////////////
/// We'll begin with the object constructor and the public API functions.
///////////////////////////////////////////////////////////////////////////////
export function Z80(coreParameter)
{
   // Obviously we'll be needing the core object's functions again.
   let core = coreParameter;

   // The argument to this constructor should be an object containing 4 functions:
   // mem_read(address) should return the byte at the given memory address,
   // mem_write(address, value) should write the given value to the given memory address,
   // io_read(port) should read a return a byte read from the given I/O port,
   // io_write(port, value) should write the given byte to the given I/O port.
   // If any of those functions is missing, this module cannot run.
   if (!core || (typeof core.mem_read !== "function") || (typeof core.mem_write !== "function") ||
                (typeof core.io_read !== "function")  || (typeof core.io_write !== "function"))
      throw("Z80: Core object is missing required functions.");
   
   //if (this === window)
   //   throw("Z80: This function is a constructor; call it using operator new.");

   // All right, let's initialize the registers.
   // First, the standard 8080 registers.
   let a = 0x00;
   let b = 0x00;
   let c = 0x00;
   let d = 0x00;
   let e = 0x00;
   let h = 0x00;
   let l = 0x00;
   // Now the special Z80 copies of the 8080 registers
   //  (the ones used for the SWAP instruction and such).
   let a_prime = 0x00;
   let b_prime = 0x00;
   let c_prime = 0x00;
   let d_prime = 0x00;
   let e_prime = 0x00;
   let h_prime = 0x00;
   let l_prime = 0x00;
   // And now the Z80 index registers.
   let ix = 0x0000;
   let iy = 0x0000;
   // Then the "utility" registers: the interrupt vector,
   //  the memory refresh, the stack pointer, and the program counter.
   let i = 0x00;
   let r = 0x00;
   let sp = 0xdff0;
   let pc = 0x0000;
   // We don't keep an F register for the flags,
   //  because most of the time we're only accessing a single flag,
   //  so we optimize for that case and use utility functions
   //  for the rarer occasions when we need to access the whole register.
   let flags = {S:0, Z:0, Y:0, H:0, X:0, P:0, N:0, C:0};
   let flags_prime = {S:0, Z:0, Y:0, H:0, X:0, P:0, N:0, C:0};
   // And finally we have the interrupt mode and flip-flop registers.
   let imode = 0;
   let iff1 = 0;
   let iff2 = 0;
   
   // These are all specific to this implementation, not Z80 features.
   // Keep track of whether we've had a HALT instruction called.
   let halted = false;
   // EI and DI wait one instruction before they take effect;
   //  these flags tell us when we're in that wait state.
   let do_delayed_di = false;
   let do_delayed_ei = false;
   // This tracks the number of cycles spent in a single instruction run,
   //  including processing any prefixes and handling interrupts.
   let cycle_counter = 0;
   
   function getState() {
      return {
         b: b,
         a: a,
         c: c,
         d: d,
         e: e,
         h: h,
         l: l,
         a_prime: a_prime,
         b_prime: b_prime,
         c_prime: c_prime,
         d_prime: d_prime,
         e_prime: e_prime,
         h_prime: h_prime,
         l_prime: l_prime,
         ix  : ix,
         iy  : iy,
         i   : i,
         r   : r,
         sp  : sp,
         pc  : pc,      
         flags: { 
            S: flags.S,
            Z: flags.Z,
            Y: flags.Y,
            H: flags.H,
            X: flags.X,
            P: flags.P,
            N: flags.N,
            C: flags.C
         },         
         flags_prime: { 
            S: flags_prime.S,
            Z: flags_prime.Z,
            Y: flags_prime.Y,
            H: flags_prime.H,
            X: flags_prime.X,
            P: flags_prime.P,
            N: flags_prime.N,
            C: flags_prime.C
         },         
         imode         : imode,
         iff1          : iff1,
         iff2          : iff2,
         halted        : halted,
         do_delayed_di : do_delayed_di,
         do_delayed_ei : do_delayed_ei,
         cycle_counter : cycle_counter
      };   
   }

   function setState(state) {
      b = state.b;
      a = state.a;
      c = state.c;
      d = state.d;
      e = state.e;
      h = state.h;
      l = state.l;
      a_prime = state.a_prime;
      b_prime = state.b_prime;
      c_prime = state.c_prime;
      d_prime = state.d_prime;
      e_prime = state.e_prime;
      h_prime = state.h_prime;
      l_prime = state.l_prime;
      ix  = state.ix;
      iy  = state.iy;
      i   = state.i;
      r   = state.r;
      sp  = state.sp;
      pc  = state.pc;      
      flags.S = state.flags.S;
      flags.Z = state.flags.Z;
      flags.Y = state.flags.Y;
      flags.H = state.flags.H;
      flags.X = state.flags.X;
      flags.P = state.flags.P;
      flags.N = state.flags.N;
      flags.C = state.flags.C;         
      flags_prime.S = state.flags_prime.S;
      flags_prime.Z = state.flags_prime.Z;
      flags_prime.Y = state.flags_prime.Y;
      flags_prime.H = state.flags_prime.H;
      flags_prime.X = state.flags_prime.X;
      flags_prime.P = state.flags_prime.P;
      flags_prime.N = state.flags_prime.N;
      flags_prime.C = state.flags_prime.C;               
      imode = state.imode;
      iff1 = state.iff1;
      iff2 = state.iff2;
      halted = state.halted;
      do_delayed_di = state.do_delayed_di;
      do_delayed_ei = state.do_delayed_ei;
      cycle_counter = state.cycle_counter;
   }

///////////////////////////////////////////////////////////////////////////////
/// @public reset
///
/// @brief Re-initialize the processor as if a reset or power on had occured
///////////////////////////////////////////////////////////////////////////////
let reset = function()
{
   // These registers are the ones that have predictable states
   //  immediately following a power-on or a reset.
   // The others are left alone, because their states are unpredictable.
   sp = 0xdff0;
   pc = 0x0000;
   a = 0x00;
   r = 0x00;
   set_flags_register(0);
   // Start up with interrupts disabled.
   imode = 0;
   iff1 = 0;
   iff2 = 0;
   // Don't start halted or in a delayed DI or EI.
   halted = false;
   do_delayed_di = false;
   do_delayed_ei = false;
   // Obviously we've not used any cycles yet.
   cycle_counter = 0;
};

///////////////////////////////////////////////////////////////////////////////
/// @public run_instruction
///
/// @brief Runs a single instruction
///
/// @return The number of T cycles the instruction took to run,
///          plus any time that went into handling interrupts that fired
///          while this instruction was executing
///////////////////////////////////////////////////////////////////////////////
let run_instruction = function()
{
   if (!halted)
   {
      // If the previous instruction was a DI or an EI,
      //  we'll need to disable or enable interrupts
      //  after whatever instruction we're about to run is finished.
      var doing_delayed_di = false, doing_delayed_ei = false;
      if (do_delayed_di)
      {
         do_delayed_di = false;
         doing_delayed_di = true;
      }
      else if (do_delayed_ei)
      {
         do_delayed_ei = false;
         doing_delayed_ei = true;
      }

      // R is incremented at the start of every instruction cycle,
      //  before the instruction actually runs.
      // The high bit of R is not affected by this increment,
      //  it can only be changed using the LD R, A instruction.
      r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);
      
      // Read the byte at the PC and run the instruction it encodes.
      var opcode = core.mem_read(pc);
      decode_instruction(opcode);
      pc = (pc + 1) & 0xffff;
      
      // Actually do the delayed interrupt disable/enable if we have one.
      if (doing_delayed_di)
      {
         iff1 = 0;
         iff2 = 0;
      }
      else if (doing_delayed_ei)
      {
         iff1 = 1;
         iff2 = 1;
      }
      
      // And finally clear out the cycle counter for the next instruction
      //  before returning it to the emulator core.
      var retval = cycle_counter;
      cycle_counter = 0;
      return retval;
   }
   else
   {
      // While we're halted, claim that we spent a cycle doing nothing,
      //  so that the rest of the emulator can still proceed.
      return 1;
   }
};

///////////////////////////////////////////////////////////////////////////////
/// @public interrupt
///
/// @brief Simulates pulsing the processor's INT (or NMI) pin
///
/// @param non_maskable - true if this is a non-maskable interrupt
/// @param data - the value to be placed on the data bus, if needed
///////////////////////////////////////////////////////////////////////////////
let interrupt = function(non_maskable, data)
{
   if (non_maskable)
   {
      // The high bit of R is not affected by this increment,
      //  it can only be changed using the LD R, A instruction.
      r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);
      // Non-maskable interrupts are always handled the same way;
      //  clear IFF1 and then do a CALL 0x0066.
      // Also, all interrupts reset the HALT state.
      halted = false;
      iff2 = iff1;
      iff1 = 0;
      push_word(pc);
      pc = 0x66;
      cycle_counter += 11;
   }
   else if (iff1)
   {
      // The high bit of R is not affected by this increment,
      //  it can only be changed using the LD R, A instruction.
      r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);
   
      halted = false;
      iff1 = 0;
      iff2 = 0;
   
      if (imode === 0)
      {
         // In the 8080-compatible interrupt mode,
         //  decode the content of the data bus as an instruction and run it.
         // it's probably a RST instruction, which pushes (PC+1) onto the stack
         // so we should decrement PC before we decode the instruction
         pc = (pc - 1) & 0xffff;
         decode_instruction(data);
         pc = (pc + 1) & 0xffff; // increment PC upon return
         cycle_counter += 2;
      }
      else if (imode === 1)
      {
         // Mode 1 is always just RST 0x38.
         push_word(pc);
         pc = 0x38;
         cycle_counter += 13;
      }
      else if (imode === 2)
      {
         // Mode 2 uses the value on the data bus as in index
         //  into the vector table pointer to by the I register.
         push_word(pc);
         // The Z80 manual says that this address must be 2-byte aligned,
         //  but it doesn't appear that this is actually the case on the hardware,
         //  so we don't attempt to enforce that here.
         var vector_address = ((i << 8) | data);
         pc = core.mem_read(vector_address) | 
                   (core.mem_read((vector_address + 1) & 0xffff) << 8);
         
         cycle_counter += 19;
      }
   }
};

///////////////////////////////////////////////////////////////////////////////
/// The public API functions end here.
///
/// What begins here are just general utility functions, used variously.
///////////////////////////////////////////////////////////////////////////////
let decode_instruction = function(opcode)
{
   // The register-to-register loads and ALU instructions
   //  are all so uniform that we can decode them directly
   //  instead of going into the instruction array for them.
   // This function gets the operand for all of these instructions.
   var get_operand = function(opcode)
   {
      return ((opcode & 0x07) === 0) ? b :
             ((opcode & 0x07) === 1) ? c :
             ((opcode & 0x07) === 2) ? d :
             ((opcode & 0x07) === 3) ? e :
             ((opcode & 0x07) === 4) ? h :
             ((opcode & 0x07) === 5) ? l :
             ((opcode & 0x07) === 6) ? core.mem_read(l | (h << 8)) : a;
   };

   // Handle HALT right up front, because it fouls up our LD decoding
   //  by falling where LD (HL), (HL) ought to be.
   if (opcode === 0x76)
   {
      halted = true;
   }
   else if ((opcode >= 0x40) && (opcode < 0x80))
   {
      // This entire range is all 8-bit register loads.
      // Get the operand and assign it to the correct destination.
      var operand = get_operand(opcode);
         
      if (((opcode & 0x38) >>> 3) === 0)
         b = operand;
      else if (((opcode & 0x38) >>> 3) === 1)
         c = operand;
      else if (((opcode & 0x38) >>> 3) === 2)
         d = operand;
      else if (((opcode & 0x38) >>> 3) === 3)
         e = operand;
      else if (((opcode & 0x38) >>> 3) === 4)
         h = operand;
      else if (((opcode & 0x38) >>> 3) === 5)
         l = operand;
      else if (((opcode & 0x38) >>> 3) === 6)
         core.mem_write(l | (h << 8), operand);
      else if (((opcode & 0x38) >>> 3) === 7)
         a = operand;
   }
   else if ((opcode >= 0x80) && (opcode < 0xc0))
   {
      // These are the 8-bit register ALU instructions.
      // We'll get the operand and then use this "jump table"
      //  to call the correct utility function for the instruction.
      var operand = get_operand(opcode),
          op_array = [do_add, do_adc, do_sub, do_sbc,
                      do_and, do_xor, do_or, do_cp];
      
      op_array[(opcode & 0x38) >>> 3]( operand);
   }
   else
   {
      // This is one of the less formulaic instructions;
      //  we'll get the specific function for it from our array.
      var func = instructions[opcode];
      func();
   }
   
   // Update the cycle counter with however many cycles
   //  the base instruction took.
   // If this was a prefixed instruction, then
   //  the prefix handler has added its extra cycles already.
   cycle_counter += cycle_counts[opcode];
};

let get_signed_offset_byte = function(value)
{
   // This function requires some explanation.
   // We just use JavaScript Number variables for our registers,
   //  not like a typed array or anything.
   // That means that, when we have a byte value that's supposed
   //  to represent a signed offset, the value we actually see
   //  isn't signed at all, it's just a small integer.
   // So, this function converts that byte into something JavaScript
   //  will recognize as signed, so we can easily do arithmetic with it.
   // First, we clamp the value to a single byte, just in case.
   value &= 0xff;
   // We don't have to do anything if the value is positive.
   if (value & 0x80)
   {
      // But if the value is negative, we need to manually un-two's-compliment it.
      // I'm going to assume you can figure out what I meant by that,
      //  because I don't know how else to explain it.
      // We could also just do value |= 0xffffff00, but I prefer
      //  not caring how many bits are in the integer representation
      //  of a JavaScript number in the currently running browser.
      value = -((0xff & ~value) + 1);
   }
   return value;
};

let get_flags_register = function()
{
   // We need the whole F register for some reason.
   //  probably a PUSH AF instruction,
   //  so make the F register out of our separate flags.
   return (flags.S << 7) |
          (flags.Z << 6) |
          (flags.Y << 5) |
          (flags.H << 4) |
          (flags.X << 3) |
          (flags.P << 2) |
          (flags.N << 1) |
          (flags.C);
};

let get_flags_prime = function()
{
   // This is the same as the above for the F' register.
   return (flags_prime.S << 7) |
          (flags_prime.Z << 6) |
          (flags_prime.Y << 5) |
          (flags_prime.H << 4) |
          (flags_prime.X << 3) |
          (flags_prime.P << 2) |
          (flags_prime.N << 1) |
          (flags_prime.C);
};

let set_flags_register = function(operand)
{
   // We need to set the F register, probably for a POP AF,
   //  so break out the given value into our separate flags.
   flags.S = (operand & 0x80) >>> 7;
   flags.Z = (operand & 0x40) >>> 6;
   flags.Y = (operand & 0x20) >>> 5;
   flags.H = (operand & 0x10) >>> 4;
   flags.X = (operand & 0x08) >>> 3;
   flags.P = (operand & 0x04) >>> 2;
   flags.N = (operand & 0x02) >>> 1;
   flags.C = (operand & 0x01);
};

let set_flags_prime = function(operand)
{
   // Again, this is the same as the above for F'.
   flags_prime.S = (operand & 0x80) >>> 7;
   flags_prime.Z = (operand & 0x40) >>> 6;
   flags_prime.Y = (operand & 0x20) >>> 5;
   flags_prime.H = (operand & 0x10) >>> 4;
   flags_prime.X = (operand & 0x08) >>> 3;
   flags_prime.P = (operand & 0x04) >>> 2;
   flags_prime.N = (operand & 0x02) >>> 1;
   flags_prime.C = (operand & 0x01);
};

let update_xy_flags = function(result)
{
   // Most of the time, the undocumented flags
   //  (sometimes called X and Y, or 3 and 5),
   //  take their values from the corresponding bits
   //  of the result of the instruction,
   //  or from some other related value.
   // This is a utility function to set those flags based on those bits.
   flags.Y = (result & 0x20) >>> 5;
   flags.X = (result & 0x08) >>> 3;
};

let get_parity = function(value)
{
   // We could try to actually calculate the parity every time,
   //  but why calculate what you can pre-calculate?
   var parity_bits = [
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
   ];
   return parity_bits[value];
};

let push_word = function(operand)
{
   // Pretty obvious what this function does; given a 16-bit value,
   //  decrement the stack pointer, write the high byte to the new
   //  stack pointer location, then repeat for the low byte.
   sp = (sp - 1) & 0xffff;
   core.mem_write(sp, (operand & 0xff00) >>> 8);
   sp = (sp - 1) & 0xffff;
   core.mem_write(sp, operand & 0x00ff);
};

let pop_word = function()
{
   // Again, not complicated; read a byte off the top of the stack,
   //  increment the stack pointer, rinse and repeat.
   var retval = core.mem_read(sp) & 0xff;
   sp = (sp + 1) & 0xffff;
   retval |= core.mem_read(sp) << 8;
   sp = (sp + 1) & 0xffff;
   return retval;
};

///////////////////////////////////////////////////////////////////////////////
/// Now, the way most instructions work in this emulator is that they set up
///  their operands according to their addressing mode, and then they call a
///  utility function that handles all variations of that instruction.
/// Those utility functions begin here.
///////////////////////////////////////////////////////////////////////////////
let do_conditional_absolute_jump = function(condition)
{
   // This function implements the JP [condition],nn instructions.
   if (condition)
   {
      // We're taking this jump, so write the new PC,
      //  and then decrement the thing we just wrote,
      //  because the instruction decoder increments the PC
      //  unconditionally at the end of every instruction
      //  and we need to counteract that so we end up at the jump target.
      pc =  core.mem_read((pc + 1) & 0xffff) |
                (core.mem_read((pc + 2) & 0xffff) << 8);
      pc = (pc - 1) & 0xffff;
   }
   else
   {
      // We're not taking this jump, just move the PC past the operand.
      pc = (pc + 2) & 0xffff;
   }
};

let do_conditional_relative_jump = function(condition)
{
   // This function implements the JR [condition],n instructions.
   if (condition)
   {
      // We need a few more cycles to actually take the jump.
      cycle_counter += 5;
      // Calculate the offset specified by our operand.
      var offset = get_signed_offset_byte(core.mem_read((pc + 1) & 0xffff));
      // Add the offset to the PC, also skipping past this instruction.
      pc = (pc + offset + 1) & 0xffff;
   }
   else
   {
      // No jump happening, just skip the operand.
      pc = (pc + 1) & 0xffff;
   }
};

let do_conditional_call = function(condition)
{
   // This function is the CALL [condition],nn instructions.
   // If you've seen the previous functions, you know this drill.
   if (condition)
   {
      cycle_counter += 7;
      push_word((pc + 3) & 0xffff);
      pc =  core.mem_read((pc + 1) & 0xffff) |
                (core.mem_read((pc + 2) & 0xffff) << 8);
      pc = (pc - 1) & 0xffff;
   }
   else
   {
      pc = (pc + 2) & 0xffff;
   }
};

let do_conditional_return = function(condition)
{
   if (condition)
   {
      cycle_counter += 6;
      pc = (pop_word() - 1) & 0xffff;
   }
};

let do_reset = function(address)
{
   // The RST [address] instructions go through here.
   push_word((pc + 1) & 0xffff);
   pc = (address - 1) & 0xffff;
};

let do_add = function(operand)
{
   // This is the ADD A, [operand] instructions.
   // We'll do the literal addition, which includes any overflow,
   //  so that we can more easily figure out whether we had
   //  an overflow or a carry and set the flags accordingly.
   var result = a + operand;
   
   // The great majority of the work for the arithmetic instructions
   //  turns out to be setting the flags rather than the actual operation.
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = (((operand & 0x0f) + (a & 0x0f)) & 0x10) ? 1 : 0;
   // An overflow has happened if the sign bits of the accumulator and the operand
   //  don't match the sign bit of the result value.
   flags.P = ((a & 0x80) === (operand & 0x80)) && ((a & 0x80) !== (result & 0x80)) ? 1 : 0;
   flags.N = 0;
   flags.C = (result & 0x100) ? 1 : 0;
   
   a = result & 0xff;
   update_xy_flags(a);
};

let do_adc = function(operand)
{
   var result = a + operand + flags.C;
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = (((operand & 0x0f) + (a & 0x0f) + flags.C) & 0x10) ? 1 : 0;
   flags.P = ((a & 0x80) === (operand & 0x80)) && ((a & 0x80) !== (result & 0x80)) ? 1 : 0;
   flags.N = 0;
   flags.C = (result & 0x100) ? 1 : 0;
   
   a = result & 0xff;
   update_xy_flags(a);
};

let do_sub = function(operand)
{
   var result = a - operand;
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = (((a & 0x0f) - (operand & 0x0f)) & 0x10) ? 1 : 0;
   flags.P = ((a & 0x80) !== (operand & 0x80)) && ((a & 0x80) !== (result & 0x80)) ? 1 : 0;
   flags.N = 1;
   flags.C = (result & 0x100) ? 1 : 0;
   
   a = result & 0xff;
   update_xy_flags(a);
};

let do_sbc = function(operand)
{
   var result = a - operand - flags.C;
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = (((a & 0x0f) - (operand & 0x0f) - flags.C) & 0x10) ? 1 : 0;
   flags.P = ((a & 0x80) !== (operand & 0x80)) && ((a & 0x80) !== (result & 0x80)) ? 1 : 0;
   flags.N = 1;
   flags.C = (result & 0x100) ? 1 : 0;
   
   a = result & 0xff;
   update_xy_flags(a);
};

let do_cp = function(operand)
{
   // A compare instruction is just a subtraction that doesn't save the value,
   //  so we implement it as... a subtraction that doesn't save the value.
   var temp = a;
   do_sub(operand);
   a = temp;
   // Since this instruction has no "result" value, the undocumented flags
   //  are set based on the operand instead.
   update_xy_flags(operand);
};

let do_and = function(operand)
{
   // The logic instructions are all pretty straightforward.
   a &= operand & 0xff;
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = !a ? 1 : 0;
   flags.H = 1;
   flags.P = get_parity(a);
   flags.N = 0;
   flags.C = 0;
   update_xy_flags(a);
};

let do_or = function(operand)
{
   a = (operand | a) & 0xff;
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = !a ? 1 : 0;
   flags.H = 0;
   flags.P = get_parity(a);
   flags.N = 0;
   flags.C = 0;
   update_xy_flags(a);
};

let do_xor = function(operand)
{
   a = (operand ^ a) & 0xff;
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = !a ? 1 : 0;
   flags.H = 0;
   flags.P = get_parity(a);
   flags.N = 0;
   flags.C = 0;
   update_xy_flags(a);
};

let do_inc = function(operand)
{
   var result = operand + 1;
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = ((operand & 0x0f) === 0x0f) ? 1 : 0;
   // It's a good deal easier to detect overflow for an increment/decrement.
   flags.P = (operand === 0x7f) ? 1 : 0;
   flags.N = 0;
   
   result &= 0xff;
   update_xy_flags(result);
   
   return result;
};

let do_dec = function(operand)
{
   var result = operand - 1;
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = !(result & 0xff) ? 1 : 0;
   flags.H = ((operand & 0x0f) === 0x00) ? 1 : 0;
   flags.P = (operand === 0x80) ? 1 : 0;
   flags.N = 1;
   
   result &= 0xff;
   update_xy_flags(result);
   
   return result;
};

let do_hl_add = function(operand)
{
   // The HL arithmetic instructions are the same as the A ones,
   //  just with twice as many bits happening.
   var hl = l | (h << 8), result = hl + operand;
   
   flags.N = 0;
   flags.C = (result & 0x10000) ? 1 : 0;
   flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) ? 1 : 0;
   
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;

   update_xy_flags(h);
};

let do_hl_adc = function(operand)
{
   operand += flags.C;
   var hl = l | (h << 8), result = hl + operand;
   
   flags.S = (result & 0x8000) ? 1 : 0;
   flags.Z = !(result & 0xffff) ? 1 : 0;
   flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) ? 1 : 0;
   flags.P = ((hl & 0x8000) === (operand & 0x8000)) && ((result & 0x8000) !== (hl & 0x8000)) ? 1 : 0;
   flags.N = 0;
   flags.C = (result & 0x10000) ? 1 : 0;
   
   l = result & 0xff;
   h = (result >>> 8) & 0xff;
   
   update_xy_flags(h);
};

let do_hl_sbc = function(operand)
{
   operand += flags.C;
   var hl = l | (h << 8), result = hl - operand;
   
   flags.S = (result & 0x8000) ? 1 : 0;
   flags.Z = !(result & 0xffff) ? 1 : 0;
   flags.H = (((hl & 0x0fff) - (operand & 0x0fff)) & 0x1000) ? 1 : 0;
   flags.P = ((hl & 0x8000) !== (operand & 0x8000)) && ((result & 0x8000) !== (hl & 0x8000)) ? 1 : 0;
   flags.N = 1;
   flags.C = (result & 0x10000) ? 1 : 0;
   
   l = result & 0xff;
   h = (result >>> 8) & 0xff;
   
   update_xy_flags(h);
};

let do_in = function(port)
{
   var result = core.io_read(port);
   
   flags.S = (result & 0x80) ? 1 : 0;
   flags.Z = result ? 0 : 1;
   flags.H = 0;
   flags.P = get_parity(result) ? 1 : 0;
   flags.N = 0;
   update_xy_flags(result);
   
   return result;
};

let do_neg = function()
{
   // This instruction is defined to not alter the register if it === 0x80.
   if (a !== 0x80)
   {
      // This is a signed operation, so convert A to a signed value.
      a = get_signed_offset_byte(a);
      
      a = (-a) & 0xff;
   }
   
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = !a ? 1 : 0;
   flags.H = (((-a) & 0x0f) > 0) ? 1 : 0;
   flags.P = (a === 0x80) ? 1 : 0;
   flags.N = 1;
   flags.C = a ? 1 : 0;
   update_xy_flags(a);
};

let do_ldi = function()
{
   // Copy the value that we're supposed to copy.
   var read_value = core.mem_read(l | (h << 8));
   core.mem_write(e | (d << 8), read_value);
   
   // Increment DE and HL, and decrement BC.
   var result = (e | (d << 8)) + 1;
   e = result & 0xff;
   d = (result & 0xff00) >>> 8;
   result = (l | (h << 8)) + 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   result = (c | (b << 8)) - 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
   
   flags.H = 0;
   flags.P = (c || b) ? 1 : 0;
   flags.N = 0;
   flags.Y = ((a + read_value) & 0x02) >>> 1;
   flags.X = ((a + read_value) & 0x08) >>> 3;
};

let do_cpi = function()
{
   var temp_carry = flags.C;
   var read_value = core.mem_read(l | (h << 8))
   do_cp(read_value);
   flags.C = temp_carry;
   flags.Y = ((a - read_value - flags.H) & 0x02) >>> 1;
   flags.X = ((a - read_value - flags.H) & 0x08) >>> 3;
   
   var result = (l | (h << 8)) + 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   result = (c | (b << 8)) - 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
   
   flags.P = result ? 1 : 0;
};

let do_ini = function()
{
   b = do_dec(b);
   
   core.mem_write(l | (h << 8), core.io_read(c));
   
   var result = (l | (h << 8)) + 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;

   flags.N = 1;
};

let do_outi = function()
{
   core.io_write(c, core.mem_read(l | (h << 8)));
   
   var result = (l | (h << 8)) + 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   
   b = do_dec(b);
   flags.N = 1;
};

let do_ldd = function()
{
   flags.N = 0;
   flags.H = 0;
   
   var read_value = core.mem_read(l | (h << 8));
   core.mem_write(e | (d << 8), read_value);
   
   var result = (e | (d << 8)) - 1;
   e = result & 0xff;
   d = (result & 0xff00) >>> 8;
   result = (l | (h << 8)) - 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   result = (c | (b << 8)) - 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
   
   flags.P = (c || b) ? 1 : 0;
   flags.Y = ((a + read_value) & 0x02) >>> 1;
   flags.X = ((a + read_value) & 0x08) >>> 3;
};

let do_cpd = function()
{
   var temp_carry = flags.C
   var read_value = core.mem_read(l | (h << 8))
   do_cp(read_value);
   flags.C = temp_carry;
   flags.Y = ((a - read_value - flags.H) & 0x02) >>> 1;
   flags.X = ((a - read_value - flags.H) & 0x08) >>> 3;
   
   var result = (l | (h << 8)) - 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   result = (c | (b << 8)) - 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
   
   flags.P = result ? 1 : 0;
};

let do_ind = function()
{
   b = do_dec(b);
   
   core.mem_write(l | (h << 8), core.io_read(c));
   
   var result = (l | (h << 8)) - 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   
   flags.N = 1;
};

let do_outd = function()
{
   core.io_write(c, core.mem_read(l | (h << 8)));
   
   var result = (l | (h << 8)) - 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
   
   b = do_dec(b);
   flags.N = 1;
};

let do_rlc = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = (operand & 0x80) >>> 7;
   operand = ((operand << 1) | flags.C) & 0xff;
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);
   
   return operand;
};

let do_rrc = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = operand & 1;
   operand = ((operand >>> 1) & 0x7f) | (flags.C << 7);
   
   flags.Z = !(operand & 0xff) ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);
   
   return operand & 0xff;
};

let do_rl = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   var temp = flags.C;
   flags.C = (operand & 0x80) >>> 7;
   operand = ((operand << 1) | temp) & 0xff;
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);

   return operand;
};

let do_rr = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   var temp = flags.C;
   flags.C = operand & 1;
   operand = ((operand >>> 1) & 0x7f) | (temp << 7);
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);

   return operand;
};

let do_sla = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = (operand & 0x80) >>> 7;
   operand = (operand << 1) & 0xff;
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);
   
   return operand;
};

let do_sra = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = operand & 1;
   operand = ((operand >>> 1) & 0x7f) | (operand & 0x80);
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);
   
   return operand;
};

let do_sll = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = (operand & 0x80) >>> 7;
   operand = ((operand << 1) & 0xff) | 1;
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = (operand & 0x80) ? 1 : 0;
   update_xy_flags(operand);
   
   return operand;
};

let do_srl = function(operand)
{
   flags.N = 0;
   flags.H = 0;
   
   flags.C = operand & 1;
   operand = (operand >>> 1) & 0x7f;
   
   flags.Z = !operand ? 1 : 0;
   flags.P = get_parity(operand);
   flags.S = 0;
   update_xy_flags(operand);
   
   return operand;
};

let do_ix_add = function(operand)
{
   flags.N = 0;
   
   var result = ix + operand;
   
   flags.C = (result & 0x10000) ? 1 : 0;
   flags.H = (((ix & 0xfff) + (operand & 0xfff)) & 0x1000) ? 1 : 0;
   update_xy_flags((result & 0xff00) >>> 8);
   
   ix = result;
};


///////////////////////////////////////////////////////////////////////////////
/// This table contains the implementations for the instructions that weren't
///  implemented directly in the decoder function (everything but the 8-bit
///  register loads and the accumulator ALU instructions, in other words).
/// Similar tables for the ED and DD/FD prefixes follow this one.
///////////////////////////////////////////////////////////////////////////////
let instructions = [];

// 0x00 : NOP
instructions[0x00] = function() { };
// 0x01 : LD BC, nn
instructions[0x01] = function()
{
   pc = (pc + 1) & 0xffff;
   c = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   b = core.mem_read(pc);
};
// 0x02 : LD (BC), A
instructions[0x02] = function()
{
   core.mem_write(c | (b << 8), a);
};
// 0x03 : INC BC
instructions[0x03] = function()
{
   var result = (c | (b << 8));
   result += 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
};
// 0x04 : INC B
instructions[0x04] = function()
{
   b = do_inc(b);
};
// 0x05 : DEC B
instructions[0x05] = function()
{
   b = do_dec(b);
};
// 0x06 : LD B, n
instructions[0x06] = function()
{
   pc = (pc + 1) & 0xffff;
   b = core.mem_read(pc);
};
// 0x07 : RLCA
instructions[0x07] = function()
{
   // This instruction is implemented as a special case of the
   //  more general Z80-specific RLC instruction.
   // Specifially, RLCA is a version of RLC A that affects fewer flags.
   // The same applies to RRCA, RLA, and RRA.
   var temp_s = flags.S, temp_z = flags.Z, temp_p = flags.P;
   a = do_rlc(a);
   flags.S = temp_s;
   flags.Z = temp_z;
   flags.P = temp_p;
};
// 0x08 : EX AF, AF'
instructions[0x08] = function()
{
   var temp = a;
   a = a_prime;
   a_prime = temp;
   
   temp = get_flags_register();
   set_flags_register(get_flags_prime());
   set_flags_prime(temp);
};
// 0x09 : ADD HL, BC
instructions[0x09] = function()
{
   do_hl_add(c | (b << 8));
};
// 0x0a : LD A, (BC)
instructions[0x0a] = function()
{
   a = core.mem_read(c | (b << 8));
};
// 0x0b : DEC BC
instructions[0x0b] = function()
{
   var result = (c | (b << 8));
   result -= 1;
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
};
// 0x0c : INC C
instructions[0x0c] = function()
{
   c = do_inc(c);
};
// 0x0d : DEC C
instructions[0x0d] = function()
{
   c = do_dec(c);
};
// 0x0e : LD C, n
instructions[0x0e] = function()
{
   pc = (pc + 1) & 0xffff;
   c = core.mem_read(pc);
};
// 0x0f : RRCA
instructions[0x0f] = function()
{
   var temp_s = flags.S, temp_z = flags.Z, temp_p = flags.P;
   a = do_rrc(a);
   flags.S = temp_s;
   flags.Z = temp_z;
   flags.P = temp_p;
};
// 0x10 : DJNZ nn
instructions[0x10] = function()
{
   b = (b - 1) & 0xff;
   do_conditional_relative_jump(b !== 0);
};
// 0x11 : LD DE, nn
instructions[0x11] = function()
{
   pc = (pc + 1) & 0xffff;
   e = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   d = core.mem_read(pc);
};
// 0x12 : LD (DE), A
instructions[0x12] = function()
{
   core.mem_write(e | (d << 8), a);
};
// 0x13 : INC DE
instructions[0x13] = function()
{
   var result = (e | (d << 8));
   result += 1;
   e = result & 0xff;
   d = (result & 0xff00) >>> 8;
};
// 0x14 : INC D
instructions[0x14] = function()
{
   d = do_inc(d);
};
// 0x15 : DEC D
instructions[0x15] = function()
{
   d = do_dec(d);
};
// 0x16 : LD D, n
instructions[0x16] = function()
{
   pc = (pc + 1) & 0xffff;
   d = core.mem_read(pc);
};
// 0x17 : RLA
instructions[0x17] = function()
{
   var temp_s = flags.S, temp_z = flags.Z, temp_p = flags.P;
   a = do_rl(a);
   flags.S = temp_s;
   flags.Z = temp_z;
   flags.P = temp_p;
};
// 0x18 : JR n
instructions[0x18] = function()
{
   var offset = get_signed_offset_byte(core.mem_read((pc + 1) & 0xffff));
   pc = (pc + offset + 1) & 0xffff;
};
// 0x19 : ADD HL, DE
instructions[0x19] = function()
{
   do_hl_add(e | (d << 8));
};
// 0x1a : LD A, (DE)
instructions[0x1a] = function()
{
   a = core.mem_read(e | (d << 8));
};
// 0x1b : DEC DE
instructions[0x1b] = function()
{
   var result = (e | (d << 8));
   result -= 1;
   e = result & 0xff;
   d = (result & 0xff00) >>> 8;
};
// 0x1c : INC E
instructions[0x1c] = function()
{
   e = do_inc(e);
};
// 0x1d : DEC E
instructions[0x1d] = function()
{
   e = do_dec(e);
};
// 0x1e : LD E, n
instructions[0x1e] = function()
{
   pc = (pc + 1) & 0xffff;
   e = core.mem_read(pc);
};
// 0x1f : RRA
instructions[0x1f] = function()
{
   var temp_s = flags.S, temp_z = flags.Z, temp_p = flags.P;
   a = do_rr(a);
   flags.S = temp_s;
   flags.Z = temp_z;
   flags.P = temp_p;
};
// 0x20 : JR NZ, n
instructions[0x20] = function()
{
   do_conditional_relative_jump(!flags.Z);
};
// 0x21 : LD HL, nn
instructions[0x21] = function()
{
   pc = (pc + 1) & 0xffff;
   l = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   h = core.mem_read(pc);
};
// 0x22 : LD (nn), HL
instructions[0x22] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, l);
   core.mem_write((address + 1) & 0xffff, h);
};
// 0x23 : INC HL
instructions[0x23] = function()
{
   var result = (l | (h << 8));
   result += 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
};
// 0x24 : INC H
instructions[0x24] = function()
{
   h = do_inc(h);
};
// 0x25 : DEC H
instructions[0x25] = function()
{
   h = do_dec(h);
};
// 0x26 : LD H, n
instructions[0x26] = function()
{
   pc = (pc + 1) & 0xffff;
   h = core.mem_read(pc);
};
// 0x27 : DAA
instructions[0x27] = function()
{
   var temp = a;
   if (!flags.N)
   {
      if (flags.H || ((a & 0x0f) > 9))
         temp += 0x06;
      if (flags.C || (a > 0x99))
         temp += 0x60;
   }
   else
   {
      if (flags.H || ((a & 0x0f) > 9))
         temp -= 0x06;
      if (flags.C || (a > 0x99))
         temp -= 0x60;
   }
   
   flags.S = (temp & 0x80) ? 1 : 0;
   flags.Z = !(temp & 0xff) ? 1 : 0;
   flags.H = ((a & 0x10) ^ (temp & 0x10)) ? 1 : 0;
   flags.P = get_parity(temp & 0xff);
   // DAA never clears the carry flag if it was already set,
   //  but it is able to set the carry flag if it was clear.
   // Don't ask me, I don't know.
   // Note also that we check for a BCD carry, instead of the usual.
   flags.C = (flags.C || (a > 0x99)) ? 1 : 0;
   
   a = temp & 0xff;
   
   update_xy_flags(a);
};
// 0x28 : JR Z, n
instructions[0x28] = function()
{
   do_conditional_relative_jump(!!flags.Z);
};
// 0x29 : ADD HL, HL
instructions[0x29] = function()
{
   do_hl_add(l | (h << 8));
};
// 0x2a : LD HL, (nn)
instructions[0x2a] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   l = core.mem_read(address);
   h = core.mem_read((address + 1) & 0xffff);
};
// 0x2b : DEC HL
instructions[0x2b] = function()
{
   var result = (l | (h << 8));
   result -= 1;
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
};
// 0x2c : INC L
instructions[0x2c] = function()
{
   l = do_inc(l);
};
// 0x2d : DEC L
instructions[0x2d] = function()
{
   l = do_dec(l);
};
// 0x2e : LD L, n
instructions[0x2e] = function()
{
   pc = (pc + 1) & 0xffff;
   l = core.mem_read(pc);
};
// 0x2f : CPL
instructions[0x2f] = function()
{
   a = (~a) & 0xff;
   flags.N = 1;
   flags.H = 1;
   update_xy_flags(a);
};
// 0x30 : JR NC, n
instructions[0x30] = function()
{
   do_conditional_relative_jump(!flags.C);
};
// 0x31 : LD SP, nn
instructions[0x31] = function()
{
   sp =  core.mem_read((pc + 1) & 0xffff) | 
            (core.mem_read((pc + 2) & 0xffff) << 8);
   pc = (pc + 2) & 0xffff;
};
// 0x32 : LD (nn), A
instructions[0x32] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, a);
};
// 0x33 : INC SP
instructions[0x33] = function()
{
   sp = (sp + 1) & 0xffff;
};
// 0x34 : INC (HL)
instructions[0x34] = function()
{
   var address = l | (h << 8);
   core.mem_write(address, do_inc(core.mem_read(address)));
};
// 0x35 : DEC (HL)
instructions[0x35] = function()
{
   var address = l | (h << 8);
   core.mem_write(address, do_dec(core.mem_read(address)));
};
// 0x36 : LD (HL), n
instructions[0x36] = function()
{
   pc = (pc + 1) & 0xffff;
   core.mem_write(l | (h << 8), core.mem_read(pc));
};
// 0x37 : SCF
instructions[0x37] = function()
{
   flags.N = 0;
   flags.H = 0;
   flags.C = 1;
   update_xy_flags(a);
};
// 0x38 : JR C, n
instructions[0x38] = function()
{
   do_conditional_relative_jump(!!flags.C);
};
// 0x39 : ADD HL, SP
instructions[0x39] = function()
{
   do_hl_add(sp);
};
// 0x3a : LD A, (nn)
instructions[0x3a] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   a = core.mem_read(address);
};
// 0x3b : DEC SP
instructions[0x3b] = function()
{
   sp = (sp - 1) & 0xffff;
};
// 0x3c : INC A
instructions[0x3c] = function()
{
   a = do_inc(a);
};
// 0x3d : DEC A
instructions[0x3d] = function()
{
   a = do_dec(a);
};
// 0x3e : LD A, n
instructions[0x3e] = function()
{
   a = core.mem_read((pc + 1) & 0xffff);
   pc = (pc + 1) & 0xffff;
};
// 0x3f : CCF
instructions[0x3f] = function()
{
   flags.N = 0;
   flags.H = flags.C;
   flags.C = flags.C ? 0 : 1;
   update_xy_flags(a);
};
// 0xc0 : RET NZ
instructions[0xc0] = function()
{
   do_conditional_return(!flags.Z);
};
// 0xc1 : POP BC
instructions[0xc1] = function()
{
   var result = pop_word();
   c = result & 0xff;
   b = (result & 0xff00) >>> 8;
};
// 0xc2 : JP NZ, nn
instructions[0xc2] = function()
{
   do_conditional_absolute_jump(!flags.Z);
};
// 0xc3 : JP nn
instructions[0xc3] = function()
{
   pc =  core.mem_read((pc + 1) & 0xffff) |
            (core.mem_read((pc + 2) & 0xffff) << 8);
   pc = (pc - 1) & 0xffff;
};
// 0xc4 : CALL NZ, nn
instructions[0xc4] = function()
{
   do_conditional_call(!flags.Z);
};
// 0xc5 : PUSH BC
instructions[0xc5] = function()
{
   push_word(c | (b << 8));
};
// 0xc6 : ADD A, n
instructions[0xc6] = function()
{
   pc = (pc + 1) & 0xffff;
   do_add(core.mem_read(pc));
};
// 0xc7 : RST 00h
instructions[0xc7] = function()
{
   do_reset(0x00);
};
// 0xc8 : RET Z
instructions[0xc8] = function()
{
   do_conditional_return(!!flags.Z);
};
// 0xc9 : RET
instructions[0xc9] = function()
{
   pc = (pop_word() - 1) & 0xffff;
};
// 0xca : JP Z, nn
instructions[0xca] = function()
{
   do_conditional_absolute_jump(!!flags.Z);
};
// 0xcb : CB Prefix
instructions[0xcb] = function()
{
   // R is incremented at the start of the second instruction cycle,
   //  before the instruction actually runs.
   // The high bit of R is not affected by this increment,
   //  it can only be changed using the LD R, A instruction.
   r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);

   // We don't have a table for this prefix,
   //  the instructions are all so uniform that we can directly decode them.
   pc = (pc + 1) & 0xffff;
   var opcode = core.mem_read(pc),
       bit_number = (opcode & 0x38) >>> 3,
       reg_code = opcode & 0x07;
   
   if (opcode < 0x40)
   {
      // Shift/rotate instructions
      var op_array = [do_rlc, do_rrc, do_rl, do_rr,
                      do_sla, do_sra, do_sll, do_srl];
      
      if (reg_code === 0)
         b = op_array[bit_number]( b);
      else if (reg_code === 1)
         c = op_array[bit_number]( c);
      else if (reg_code === 2)
         d = op_array[bit_number]( d);
      else if (reg_code === 3)
         e = op_array[bit_number]( e);
      else if (reg_code === 4)
         h = op_array[bit_number]( h);
      else if (reg_code === 5)
         l = op_array[bit_number]( l);
      else if (reg_code === 6)
         core.mem_write(l | (h << 8),
                            op_array[bit_number]( core.mem_read(l | (h << 8))));
      else if (reg_code === 7)
         a = op_array[bit_number]( a);
   }
   else if (opcode < 0x80)
   {
      // BIT instructions
      if (reg_code === 0)
         flags.Z = !(b & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 1)
         flags.Z = !(c & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 2)
         flags.Z = !(d & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 3)
         flags.Z = !(e & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 4)
         flags.Z = !(h & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 5)
         flags.Z = !(l & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 6)
         flags.Z = !((core.mem_read(l | (h << 8))) & (1 << bit_number)) ? 1 : 0;
      else if (reg_code === 7)
         flags.Z = !(a & (1 << bit_number)) ? 1 : 0;
         
      flags.N = 0;
      flags.H = 1;
      flags.P = flags.Z;
      flags.S = ((bit_number === 7) && !flags.Z) ? 1 : 0;
      // For the BIT n, (HL) instruction, the X and Y flags are obtained
      //  from what is apparently an internal temporary register used for
      //  some of the 16-bit arithmetic instructions.
      // I haven't implemented that register here,
      //  so for now we'll set X and Y the same way for every BIT opcode,
      //  which means that they will usually be wrong for BIT n, (HL).
      flags.Y = ((bit_number === 5) && !flags.Z) ? 1 : 0;
      flags.X = ((bit_number === 3) && !flags.Z) ? 1 : 0;
   }
   else if (opcode < 0xc0)
   {
      // RES instructions
      if (reg_code === 0)
         b &= (0xff & ~(1 << bit_number));
      else if (reg_code === 1)
         c &= (0xff & ~(1 << bit_number));
      else if (reg_code === 2)
         d &= (0xff & ~(1 << bit_number));
      else if (reg_code === 3)
         e &= (0xff & ~(1 << bit_number));
      else if (reg_code === 4)
         h &= (0xff & ~(1 << bit_number));
      else if (reg_code === 5)
         l &= (0xff & ~(1 << bit_number));
      else if (reg_code === 6)
         core.mem_write(l | (h << 8),
                            core.mem_read(l | (h << 8)) & ~(1 << bit_number));
      else if (reg_code === 7)
         a &= (0xff & ~(1 << bit_number));
   }
   else
   {
      // SET instructions
      if (reg_code === 0)
         b |= (1 << bit_number);
      else if (reg_code === 1)
         c |= (1 << bit_number);
      else if (reg_code === 2)
         d |= (1 << bit_number);
      else if (reg_code === 3)
         e |= (1 << bit_number);
      else if (reg_code === 4)
         h |= (1 << bit_number);
      else if (reg_code === 5)
         l |= (1 << bit_number);
      else if (reg_code === 6)
         core.mem_write(l | (h << 8),
                            core.mem_read(l | (h << 8)) | (1 << bit_number));
      else if (reg_code === 7)
         a |= (1 << bit_number);
   }
   
   cycle_counter += cycle_counts_cb[opcode];
};
// 0xcc : CALL Z, nn
instructions[0xcc] = function()
{
   do_conditional_call(!!flags.Z);
};
// 0xcd : CALL nn
instructions[0xcd] = function()
{
   push_word((pc + 3) & 0xffff);
   pc =  core.mem_read((pc + 1) & 0xffff) |
            (core.mem_read((pc + 2) & 0xffff) << 8);
   pc = (pc - 1) & 0xffff;
};
// 0xce : ADC A, n
instructions[0xce] = function()
{
   pc = (pc + 1) & 0xffff;
   do_adc(core.mem_read(pc));
};
// 0xcf : RST 08h
instructions[0xcf] = function()
{
   do_reset(0x08);
};
// 0xd0 : RET NC
instructions[0xd0] = function()
{
   do_conditional_return(!flags.C);
};
// 0xd1 : POP DE
instructions[0xd1] = function()
{
   var result = pop_word();
   e = result & 0xff;
   d = (result & 0xff00) >>> 8;
};
// 0xd2 : JP NC, nn
instructions[0xd2] = function()
{
   do_conditional_absolute_jump(!flags.C);
};
// 0xd3 : OUT (n), A
instructions[0xd3] = function()
{
   pc = (pc + 1) & 0xffff;
   core.io_write(core.mem_read(pc), a);
};
// 0xd4 : CALL NC, nn
instructions[0xd4] = function()
{
   do_conditional_call(!flags.C);
};
// 0xd5 : PUSH DE
instructions[0xd5] = function()
{
   push_word(e | (d << 8));
};
// 0xd6 : SUB n
instructions[0xd6] = function()
{
   pc = (pc + 1) & 0xffff;
   do_sub(core.mem_read(pc));
};
// 0xd7 : RST 10h
instructions[0xd7] = function()
{
   do_reset(0x10);
};
// 0xd8 : RET C
instructions[0xd8] = function()
{
   do_conditional_return(!!flags.C);
};
// 0xd9 : EXX
instructions[0xd9] = function()
{
   var temp = b;
   b = b_prime;
   b_prime = temp;
   temp = c;
   c = c_prime;
   c_prime = temp;
   temp = d;
   d = d_prime;
   d_prime = temp;
   temp = e;
   e = e_prime;
   e_prime = temp;
   temp = h;
   h = h_prime;
   h_prime = temp;
   temp = l;
   l = l_prime;
   l_prime = temp;
};
// 0xda : JP C, nn
instructions[0xda] = function()
{
   do_conditional_absolute_jump(!!flags.C);
};
// 0xdb : IN A, (n)
instructions[0xdb] = function()
{
   pc = (pc + 1) & 0xffff;
   a = core.io_read(core.mem_read(pc));
};
// 0xdc : CALL C, nn
instructions[0xdc] = function()
{
   do_conditional_call(!!flags.C);
};
// 0xdd : DD Prefix (IX instructions)
instructions[0xdd] = function()
{
   // R is incremented at the start of the second instruction cycle,
   //  before the instruction actually runs.
   // The high bit of R is not affected by this increment,
   //  it can only be changed using the LD R, A instruction.
   r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);

   pc = (pc + 1) & 0xffff;
   var opcode = core.mem_read(pc),
       func = dd_instructions[opcode];
       
   if (func)
   {
      //func = func.bind(this);
      func();
      cycle_counter += cycle_counts_dd[opcode];
   }
   else
   {
      // Apparently if a DD opcode doesn't exist,
      //  it gets treated as an unprefixed opcode.
      // What we'll do to handle that is just back up the 
      //  program counter, so that this byte gets decoded
      //  as a normal instruction.
      pc = (pc - 1) & 0xffff;
      // And we'll add in the cycle count for a NOP.
      cycle_counter += cycle_counts[0];
   }
};
// 0xde : SBC n
instructions[0xde] = function()
{
   pc = (pc + 1) & 0xffff;
   do_sbc(core.mem_read(pc));
};
// 0xdf : RST 18h
instructions[0xdf] = function()
{
   do_reset(0x18);
};
// 0xe0 : RET PO
instructions[0xe0] = function()
{
   do_conditional_return(!flags.P);
};
// 0xe1 : POP HL
instructions[0xe1] = function()
{
   var result = pop_word();
   l = result & 0xff;
   h = (result & 0xff00) >>> 8;
};
// 0xe2 : JP PO, (nn)
instructions[0xe2] = function()
{
   do_conditional_absolute_jump(!flags.P);
};
// 0xe3 : EX (SP), HL
instructions[0xe3] = function()
{
   var temp = core.mem_read(sp);
   core.mem_write(sp, l);
   l = temp;
   temp = core.mem_read((sp + 1) & 0xffff);
   core.mem_write((sp + 1) & 0xffff, h);
   h = temp;
};
// 0xe4 : CALL PO, nn
instructions[0xe4] = function()
{
   do_conditional_call(!flags.P);
};
// 0xe5 : PUSH HL
instructions[0xe5] = function()
{
   push_word(l | (h << 8));
};
// 0xe6 : AND n
instructions[0xe6] = function()
{
   pc = (pc + 1) & 0xffff;
   do_and(core.mem_read(pc));
};
// 0xe7 : RST 20h
instructions[0xe7] = function()
{
   do_reset(0x20);
};
// 0xe8 : RET PE
instructions[0xe8] = function()
{
   do_conditional_return(!!flags.P);
};
// 0xe9 : JP (HL)
instructions[0xe9] = function()
{
   pc = l | (h << 8);
   pc = (pc - 1) & 0xffff;
};
// 0xea : JP PE, nn
instructions[0xea] = function()
{
   do_conditional_absolute_jump(!!flags.P);
};
// 0xeb : EX DE, HL
instructions[0xeb] = function()
{
   var temp = d;
   d = h;
   h = temp;
   temp = e;
   e = l;
   l = temp;
};
// 0xec : CALL PE, nn
instructions[0xec] = function()
{
   do_conditional_call(!!flags.P);
};
// 0xed : ED Prefix
instructions[0xed] = function()
{
   // R is incremented at the start of the second instruction cycle,
   //  before the instruction actually runs.
   // The high bit of R is not affected by this increment,
   //  it can only be changed using the LD R, A instruction.
   r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);

   pc = (pc + 1) & 0xffff;
   var opcode = core.mem_read(pc),
       func = ed_instructions[opcode];
       
   if (func)
   {
      //func = func.bind(this);
      func();
      cycle_counter += cycle_counts_ed[opcode];
   }
   else
   {
      // If the opcode didn't exist, the whole thing is a two-byte NOP.
      cycle_counter += cycle_counts[0];
   }
};
// 0xee : XOR n
instructions[0xee] = function()
{
   pc = (pc + 1) & 0xffff;
   do_xor(core.mem_read(pc));
};
// 0xef : RST 28h
instructions[0xef] = function()
{
   do_reset(0x28);
};
// 0xf0 : RET P
instructions[0xf0] = function()
{
   do_conditional_return(!flags.S);
};
// 0xf1 : POP AF
instructions[0xf1] = function()
{
   var result = pop_word();
   set_flags_register(result & 0xff);
   a = (result & 0xff00) >>> 8;
};
// 0xf2 : JP P, nn
instructions[0xf2] = function()
{
   do_conditional_absolute_jump(!flags.S);
};
// 0xf3 : DI
instructions[0xf3] = function()
{
   // DI doesn't actually take effect until after the next instruction.
   do_delayed_di = true;
};
// 0xf4 : CALL P, nn
instructions[0xf4] = function()
{
   do_conditional_call(!flags.S);
};
// 0xf5 : PUSH AF
instructions[0xf5] = function()
{
   push_word(get_flags_register() | (a << 8));
};
// 0xf6 : OR n
instructions[0xf6] = function()
{
   pc = (pc + 1) & 0xffff;
   do_or(core.mem_read(pc));
};
// 0xf7 : RST 30h
instructions[0xf7] = function()
{
   do_reset(0x30);
};
// 0xf8 : RET M
instructions[0xf8] = function()
{
   do_conditional_return(!!flags.S);
};
// 0xf9 : LD SP, HL
instructions[0xf9] = function()
{
   sp = l | (h << 8);
};
// 0xfa : JP M, nn
instructions[0xfa] = function()
{
   do_conditional_absolute_jump(!!flags.S);
};
// 0xfb : EI
instructions[0xfb] = function()
{
   // EI doesn't actually take effect until after the next instruction.
   do_delayed_ei = true;
};
// 0xfc : CALL M, nn
instructions[0xfc] = function()
{
   do_conditional_call(!!flags.S);
};
// 0xfd : FD Prefix (IY instructions)
instructions[0xfd] = function()
{
   // R is incremented at the start of the second instruction cycle,
   //  before the instruction actually runs.
   // The high bit of R is not affected by this increment,
   //  it can only be changed using the LD R, A instruction.
   r = (r & 0x80) | (((r & 0x7f) + 1) & 0x7f);
   
   pc = (pc + 1) & 0xffff;
   var opcode = core.mem_read(pc),
       func = dd_instructions[opcode];
       
   if (func)
   {
      // Rather than copy and paste all the IX instructions into IY instructions,
      //  what we'll do is sneakily copy IY into IX, run the IX instruction,
      //  and then copy the result into IY and restore the old IX.
      var temp = ix;
      ix = iy;
      //func = func.bind(this);
      func();
      iy = ix;
      ix = temp;
      
      cycle_counter += cycle_counts_dd[opcode];
   }
   else
   {
      // Apparently if an FD opcode doesn't exist,
      //  it gets treated as an unprefixed opcode.
      // What we'll do to handle that is just back up the 
      //  program counter, so that this byte gets decoded
      //  as a normal instruction.
      pc = (pc - 1) & 0xffff;
      // And we'll add in the cycle count for a NOP.
      cycle_counter += cycle_counts[0];
   }
};
// 0xfe : CP n
instructions[0xfe] = function()
{
   pc = (pc + 1) & 0xffff;
   do_cp(core.mem_read(pc));
};
// 0xff : RST 38h
instructions[0xff] = function()
{
   do_reset(0x38);
};


///////////////////////////////////////////////////////////////////////////////
/// This table of ED opcodes is pretty sparse;
///  there are not very many valid ED-prefixed opcodes in the Z80,
///  and many of the ones that are valid are not documented.
///////////////////////////////////////////////////////////////////////////////
let ed_instructions = [];
// 0x40 : IN B, (C)
ed_instructions[0x40] = function()
{
   b = do_in(c);
};
// 0x41 : OUT (C), B
ed_instructions[0x41] = function()
{
   core.io_write(c, b);
};
// 0x42 : SBC HL, BC
ed_instructions[0x42] = function()
{
   do_hl_sbc(c | (b << 8));
};
// 0x43 : LD (nn), BC
ed_instructions[0x43] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, c);
   core.mem_write((address + 1) & 0xffff, b);
};
// 0x44 : NEG
ed_instructions[0x44] = function()
{
   do_neg();
};
// 0x45 : RETN
ed_instructions[0x45] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x46 : IM 0
ed_instructions[0x46] = function()
{
   imode = 0;
};
// 0x47 : LD I, A
ed_instructions[0x47] = function()
{
   i = a
};
// 0x48 : IN C, (C)
ed_instructions[0x48] = function()
{
   c = do_in(c);
};
// 0x49 : OUT (C), C
ed_instructions[0x49] = function()
{
   core.io_write(c, c);
};
// 0x4a : ADC HL, BC
ed_instructions[0x4a] = function()
{
   do_hl_adc(c | (b << 8));
};
// 0x4b : LD BC, (nn)
ed_instructions[0x4b] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   c = core.mem_read(address);
   b = core.mem_read((address + 1) & 0xffff);
};
// 0x4c : NEG (Undocumented)
ed_instructions[0x4c] = function()
{
   do_neg();
};
// 0x4d : RETI
ed_instructions[0x4d] = function()
{
   pc = (pop_word() - 1) & 0xffff;
};
// 0x4e : IM 0 (Undocumented)
ed_instructions[0x4e] = function()
{
   imode = 0;
};
// 0x4f : LD R, A
ed_instructions[0x4f] = function()
{
   r = a;
};
// 0x50 : IN D, (C)
ed_instructions[0x50] = function()
{
   d = do_in(c);
};
// 0x51 : OUT (C), D
ed_instructions[0x51] = function()
{
   core.io_write(c, d);
};
// 0x52 : SBC HL, DE
ed_instructions[0x52] = function()
{
   do_hl_sbc(e | (d << 8));
};
// 0x53 : LD (nn), DE
ed_instructions[0x53] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, e);
   core.mem_write((address + 1) & 0xffff, d);
};
// 0x54 : NEG (Undocumented)
ed_instructions[0x54] = function()
{
   do_neg();
};
// 0x55 : RETN
ed_instructions[0x55] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x56 : IM 1
ed_instructions[0x56] = function()
{
   imode = 1;
};
// 0x57 : LD A, I
ed_instructions[0x57] = function()
{
   a = i;
   flags.S = a & 0x80 ? 1 : 0;
   flags.Z = a ? 0 : 1;
   flags.H = 0;
   flags.P = iff2;
   flags.N = 0;
   update_xy_flags(a);
};
// 0x58 : IN E, (C)
ed_instructions[0x58] = function()
{
   e = do_in(c);
};
// 0x59 : OUT (C), E
ed_instructions[0x59] = function()
{
   core.io_write(c, e);
};
// 0x5a : ADC HL, DE
ed_instructions[0x5a] = function()
{
   do_hl_adc(e | (d << 8));
};
// 0x5b : LD DE, (nn)
ed_instructions[0x5b] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   e = core.mem_read(address);
   d = core.mem_read((address + 1) & 0xffff);
};
// 0x5c : NEG (Undocumented)
ed_instructions[0x5c] = function()
{
   do_neg();
};
// 0x5d : RETN
ed_instructions[0x5d] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x5e : IM 2
ed_instructions[0x5e] = function()
{
   imode = 2;
};
// 0x5f : LD A, R
ed_instructions[0x5f] = function()
{
   a = r;
   flags.S = a & 0x80 ? 1 : 0;
   flags.Z = a ? 0 : 1;
   flags.H = 0;
   flags.P = iff2;
   flags.N = 0;
   update_xy_flags(a);
};
// 0x60 : IN H, (C)
ed_instructions[0x60] = function()
{
   h = do_in(c);
};
// 0x61 : OUT (C), H
ed_instructions[0x61] = function()
{
   core.io_write(c, h);
};
// 0x62 : SBC HL, HL
ed_instructions[0x62] = function()
{
   do_hl_sbc(l | (h << 8));
};
// 0x63 : LD (nn), HL (Undocumented)
ed_instructions[0x63] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, l);
   core.mem_write((address + 1) & 0xffff, h);
};
// 0x64 : NEG (Undocumented)
ed_instructions[0x64] = function()
{
   do_neg();
};
// 0x65 : RETN
ed_instructions[0x65] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x66 : IM 0
ed_instructions[0x66] = function()
{
   imode = 0;
};
// 0x67 : RRD
ed_instructions[0x67] = function()
{
   var hl_value = core.mem_read(l | (h << 8));
   var temp1 = hl_value & 0x0f, temp2 = a & 0x0f;
   hl_value = ((hl_value & 0xf0) >>> 4) | (temp2 << 4);
   a = (a & 0xf0) | temp1;
   core.mem_write(l | (h << 8), hl_value);
   
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = a ? 0 : 1;
   flags.H = 0;
   flags.P = get_parity(a) ? 1 : 0;
   flags.N = 0;
   update_xy_flags(a);
};
// 0x68 : IN L, (C)
ed_instructions[0x68] = function()
{
   l = do_in(c);
};
// 0x69 : OUT (C), L
ed_instructions[0x69] = function()
{
   core.io_write(c, l);
};
// 0x6a : ADC HL, HL
ed_instructions[0x6a] = function()
{
   do_hl_adc(l | (h << 8));
};
// 0x6b : LD HL, (nn) (Undocumented)
ed_instructions[0x6b] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   l = core.mem_read(address);
   h = core.mem_read((address + 1) & 0xffff);
};
// 0x6c : NEG (Undocumented)
ed_instructions[0x6c] = function()
{
   do_neg();
};
// 0x6d : RETN
ed_instructions[0x6d] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x6e : IM 0 (Undocumented)
ed_instructions[0x6e] = function()
{
   imode = 0;
};
// 0x6f : RLD
ed_instructions[0x6f] = function()
{
   var hl_value = core.mem_read(l | (h << 8));
   var temp1 = hl_value & 0xf0, temp2 = a & 0x0f;
   hl_value = ((hl_value & 0x0f) << 4) | temp2;
   a = (a & 0xf0) | (temp1 >>> 4);
   core.mem_write(l | (h << 8), hl_value);
   
   flags.S = (a & 0x80) ? 1 : 0;
   flags.Z = a ? 0 : 1;
   flags.H = 0;
   flags.P = get_parity(a) ? 1 : 0;
   flags.N = 0;
   update_xy_flags(a);
};
// 0x70 : IN (C) (Undocumented)
ed_instructions[0x70] = function()
{
   do_in(c);
};
// 0x71 : OUT (C), 0 (Undocumented)
ed_instructions[0x71] = function()
{
   core.io_write(c, 0);
};
// 0x72 : SBC HL, SP
ed_instructions[0x72] = function()
{
   do_hl_sbc(sp);
};
// 0x73 : LD (nn), SP
ed_instructions[0x73] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   core.mem_write(address, sp & 0xff);
   core.mem_write((address + 1) & 0xffff, (sp >>> 8) & 0xff);
};
// 0x74 : NEG (Undocumented)
ed_instructions[0x74] = function()
{
   do_neg();
};
// 0x75 : RETN
ed_instructions[0x75] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x76 : IM 1
ed_instructions[0x76] = function()
{
   imode = 1;
};
// 0x78 : IN A, (C)
ed_instructions[0x78] = function()
{
   a = do_in(c);
};
// 0x79 : OUT (C), A
ed_instructions[0x79] = function()
{
   core.io_write(c, a);
};
// 0x7a : ADC HL, SP
ed_instructions[0x7a] = function()
{
   do_hl_adc(sp);
};
// 0x7b : LD SP, (nn)
ed_instructions[0x7b] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= core.mem_read(pc) << 8;
   
   sp = core.mem_read(address);
   sp |= core.mem_read((address + 1) & 0xffff) << 8;
};
// 0x7c : NEG (Undocumented)
ed_instructions[0x7c] = function()
{
   do_neg();
};
// 0x7d : RETN
ed_instructions[0x7d] = function()
{
   pc = (pop_word() - 1) & 0xffff;
   iff1 = iff2;
};
// 0x7e : IM 2
ed_instructions[0x7e] = function()
{
   imode = 2;
};
// 0xa0 : LDI
ed_instructions[0xa0] = function()
{
   do_ldi();
};
// 0xa1 : CPI
ed_instructions[0xa1] = function()
{
   do_cpi();
};
// 0xa2 : INI
ed_instructions[0xa2] = function()
{
   do_ini();
};
// 0xa3 : OUTI
ed_instructions[0xa3] = function()
{
   do_outi();
};
// 0xa8 : LDD
ed_instructions[0xa8] = function()
{
   do_ldd();
};
// 0xa9 : CPD
ed_instructions[0xa9] = function()
{
   do_cpd();
};
// 0xaa : IND
ed_instructions[0xaa] = function()
{
   do_ind();
};
// 0xab : OUTD
ed_instructions[0xab] = function()
{
   do_outd();
};
// 0xb0 : LDIR
ed_instructions[0xb0] = function()
{
   do_ldi();
   if (b || c)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xb1 : CPIR
ed_instructions[0xb1] = function()
{
   do_cpi();
   if (!flags.Z && (b || c))
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xb2 : INIR
ed_instructions[0xb2] = function()
{
   do_ini();
   if (b)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xb3 : OTIR
ed_instructions[0xb3] = function()
{
   do_outi();
   if (b)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xb8 : LDDR
ed_instructions[0xb8] = function()
{
   do_ldd();
   if (b || c)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xb9 : CPDR
ed_instructions[0xb9] = function()
{
   do_cpd();
   if (!flags.Z && (b || c))
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xba : INDR
ed_instructions[0xba] = function()
{
   do_ind();
   if (b)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};
// 0xbb : OTDR
ed_instructions[0xbb] = function()
{
   do_outd();
   if (b)
   {
      cycle_counter += 5;
      pc = (pc - 2) & 0xffff;
   }
};


///////////////////////////////////////////////////////////////////////////////
/// Like ED, this table is quite sparse,
///  and many of the opcodes here are also undocumented.
/// The undocumented instructions here are those that deal with only one byte
///  of the two-byte IX register; the bytes are designed IXH and IXL here.
///////////////////////////////////////////////////////////////////////////////
let dd_instructions = [];
// 0x09 : ADD IX, BC
dd_instructions[0x09] = function()
{
   do_ix_add(c | (b << 8));
};
// 0x19 : ADD IX, DE
dd_instructions[0x19] = function()
{
   do_ix_add(e | (d << 8));
};
// 0x21 : LD IX, nn
dd_instructions[0x21] = function()
{
   pc = (pc + 1) & 0xffff;
   ix = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   ix |= (core.mem_read(pc) << 8);
};
// 0x22 : LD (nn), IX
dd_instructions[0x22] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= (core.mem_read(pc) << 8);
   
   core.mem_write(address, ix & 0xff);
   core.mem_write((address + 1) & 0xffff, (ix >>> 8) & 0xff);
};
// 0x23 : INC IX
dd_instructions[0x23] = function()
{
   ix = (ix + 1) & 0xffff;
};
// 0x24 : INC IXH (Undocumented)
dd_instructions[0x24] = function()
{
   ix = (do_inc(ix >>> 8) << 8) | (ix & 0xff);
};
// 0x25 : DEC IXH (Undocumented)
dd_instructions[0x25] = function()
{
   ix = (do_dec(ix >>> 8) << 8) | (ix & 0xff);
};
// 0x26 : LD IXH, n (Undocumented)
dd_instructions[0x26] = function()
{
   pc = (pc + 1) & 0xffff;
   ix = (core.mem_read(pc) << 8) | (ix & 0xff);
};
// 0x29 : ADD IX, IX
dd_instructions[0x29] = function()
{
   do_ix_add(ix);
};
// 0x2a : LD IX, (nn)
dd_instructions[0x2a] = function()
{
   pc = (pc + 1) & 0xffff;
   var address = core.mem_read(pc);
   pc = (pc + 1) & 0xffff;
   address |= (core.mem_read(pc) << 8);
   
   ix = core.mem_read(address);
   ix |= (core.mem_read((address + 1) & 0xffff) << 8);
};
// 0x2b : DEC IX
dd_instructions[0x2b] = function()
{
   ix = (ix - 1) & 0xffff;
};
// 0x2c : INC IXL (Undocumented)
dd_instructions[0x2c] = function()
{
   ix = do_inc(ix & 0xff) | (ix & 0xff00);
};
// 0x2d : DEC IXL (Undocumented)
dd_instructions[0x2d] = function()
{
   ix = do_dec(ix & 0xff) | (ix & 0xff00);
};
// 0x2e : LD IXL, n (Undocumented)
dd_instructions[0x2e] = function()
{
   pc = (pc + 1) & 0xffff;
   ix = (core.mem_read(pc) & 0xff) | (ix & 0xff00);
};
// 0x34 : INC (IX+n)
dd_instructions[0x34] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc)),
       value = core.mem_read((offset + ix) & 0xffff);
   core.mem_write((offset + ix) & 0xffff, do_inc(value));
};
// 0x35 : DEC (IX+n)
dd_instructions[0x35] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc)),
       value = core.mem_read((offset + ix) & 0xffff);
   core.mem_write((offset + ix) & 0xffff, do_dec(value));
};
// 0x36 : LD (IX+n), n
dd_instructions[0x36] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   pc = (pc + 1) & 0xffff;
   core.mem_write((ix + offset) & 0xffff, core.mem_read(pc));   
};
// 0x39 : ADD IX, SP
dd_instructions[0x39] = function()
{
   do_ix_add(sp);
};
// 0x44 : LD B, IXH (Undocumented)
dd_instructions[0x44] = function()
{
   b = (ix >>> 8) & 0xff;
};
// 0x45 : LD B, IXL (Undocumented)
dd_instructions[0x45] = function()
{
   b = ix & 0xff;
};
// 0x46 : LD B, (IX+n)
dd_instructions[0x46] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   b = core.mem_read((ix + offset) & 0xffff);
};
// 0x4c : LD C, IXH (Undocumented)
dd_instructions[0x4c] = function()
{
   c = (ix >>> 8) & 0xff;
};
// 0x4d : LD C, IXL (Undocumented)
dd_instructions[0x4d] = function()
{
   c = ix & 0xff;
};
// 0x4e : LD C, (IX+n)
dd_instructions[0x4e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   c = core.mem_read((ix + offset) & 0xffff);
};
// 0x54 : LD D, IXH (Undocumented)
dd_instructions[0x54] = function()
{
   d = (ix >>> 8) & 0xff;
};
// 0x55 : LD D, IXL (Undocumented)
dd_instructions[0x55] = function()
{
   d = ix & 0xff;
};
// 0x56 : LD D, (IX+n)
dd_instructions[0x56] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   d = core.mem_read((ix + offset) & 0xffff);
};
// 0x5c : LD E, IXH (Undocumented)
dd_instructions[0x5c] = function()
{
   e = (ix >>> 8) & 0xff;
};
// 0x5d : LD E, IXL (Undocumented)
dd_instructions[0x5d] = function()
{
   e = ix & 0xff;
};
// 0x5e : LD E, (IX+n)
dd_instructions[0x5e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   e = core.mem_read((ix + offset) & 0xffff);
};
// 0x60 : LD IXH, B (Undocumented)
dd_instructions[0x60] = function()
{
   ix = (ix & 0xff) | (b << 8);
};
// 0x61 : LD IXH, C (Undocumented)
dd_instructions[0x61] = function()
{
   ix = (ix & 0xff) | (c << 8);
};
// 0x62 : LD IXH, D (Undocumented)
dd_instructions[0x62] = function()
{
   ix = (ix & 0xff) | (d << 8);
};
// 0x63 : LD IXH, E (Undocumented)
dd_instructions[0x63] = function()
{
   ix = (ix & 0xff) | (e << 8);
};
// 0x64 : LD IXH, IXH (Undocumented)
dd_instructions[0x64] = function()
{
   // No-op.
};
// 0x65 : LD IXH, IXL (Undocumented)
dd_instructions[0x65] = function()
{
   ix = (ix & 0xff) | ((ix & 0xff) << 8);
};
// 0x66 : LD H, (IX+n)
dd_instructions[0x66] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   h = core.mem_read((ix + offset) & 0xffff);
};
// 0x67 : LD IXH, A (Undocumented)
dd_instructions[0x67] = function()
{
   ix = (ix & 0xff) | (a << 8);
};
// 0x68 : LD IXL, B (Undocumented)
dd_instructions[0x68] = function()
{
   ix = (ix & 0xff00) | b;
};
// 0x69 : LD IXL, C (Undocumented)
dd_instructions[0x69] = function()
{
   ix = (ix & 0xff00) | c;
};
// 0x6a : LD IXL, D (Undocumented)
dd_instructions[0x6a] = function()
{
   ix = (ix & 0xff00) | d;
};
// 0x6b : LD IXL, E (Undocumented)
dd_instructions[0x6b] = function()
{
   ix = (ix & 0xff00) | e;
};
// 0x6c : LD IXL, IXH (Undocumented)
dd_instructions[0x6c] = function()
{
   ix = (ix & 0xff00) | (ix >>> 8);
};
// 0x6d : LD IXL, IXL (Undocumented)
dd_instructions[0x6d] = function()
{
   // No-op.
};
// 0x6e : LD L, (IX+n)
dd_instructions[0x6e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   l = core.mem_read((ix + offset) & 0xffff);
};
// 0x6f : LD IXL, A (Undocumented)
dd_instructions[0x6f] = function()
{
   ix = (ix & 0xff00) | a;
};
// 0x70 : LD (IX+n), B
dd_instructions[0x70] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, b);
};
// 0x71 : LD (IX+n), C
dd_instructions[0x71] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, c);
};
// 0x72 : LD (IX+n), D
dd_instructions[0x72] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, d);
};
// 0x73 : LD (IX+n), E
dd_instructions[0x73] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, e);
};
// 0x74 : LD (IX+n), H
dd_instructions[0x74] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, h);
};
// 0x75 : LD (IX+n), L
dd_instructions[0x75] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, l);
};
// 0x77 : LD (IX+n), A
dd_instructions[0x77] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   core.mem_write((ix + offset) & 0xffff, a);
};
// 0x7c : LD A, IXH (Undocumented)
dd_instructions[0x7c] = function()
{
   a = (ix >>> 8) & 0xff;
};
// 0x7d : LD A, IXL (Undocumented)
dd_instructions[0x7d] = function()
{
   a = ix & 0xff;
};
// 0x7e : LD A, (IX+n)
dd_instructions[0x7e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   a = core.mem_read((ix + offset) & 0xffff);
};
// 0x84 : ADD A, IXH (Undocumented)
dd_instructions[0x84] = function()
{
   do_add((ix >>> 8) & 0xff);
};
// 0x85 : ADD A, IXL (Undocumented)
dd_instructions[0x85] = function()
{
   do_add(ix & 0xff);
};
// 0x86 : ADD A, (IX+n)
dd_instructions[0x86] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_add(core.mem_read((ix + offset) & 0xffff));
};
// 0x8c : ADC A, IXH (Undocumented)
dd_instructions[0x8c] = function()
{
   do_adc((ix >>> 8) & 0xff);
};
// 0x8d : ADC A, IXL (Undocumented)
dd_instructions[0x8d] = function()
{
   do_adc(ix & 0xff);
};
// 0x8e : ADC A, (IX+n)
dd_instructions[0x8e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_adc(core.mem_read((ix + offset) & 0xffff));
};
// 0x94 : SUB IXH (Undocumented)
dd_instructions[0x94] = function()
{
   do_sub((ix >>> 8) & 0xff);
};
// 0x95 : SUB IXL (Undocumented)
dd_instructions[0x95] = function()
{
   do_sub(ix & 0xff);
};
// 0x96 : SUB A, (IX+n)
dd_instructions[0x96] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_sub(core.mem_read((ix + offset) & 0xffff));
};
// 0x9c : SBC IXH (Undocumented)
dd_instructions[0x9c] = function()
{
   do_sbc((ix >>> 8) & 0xff);
};
// 0x9d : SBC IXL (Undocumented)
dd_instructions[0x9d] = function()
{
   do_sbc(ix & 0xff);
};
// 0x9e : SBC A, (IX+n)
dd_instructions[0x9e] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_sbc(core.mem_read((ix + offset) & 0xffff));
};
// 0xa4 : AND IXH (Undocumented)
dd_instructions[0xa4] = function()
{
   do_and((ix >>> 8) & 0xff);
};
// 0xa5 : AND IXL (Undocumented)
dd_instructions[0xa5] = function()
{
   do_and(ix & 0xff);
};
// 0xa6 : AND A, (IX+n)
dd_instructions[0xa6] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_and(core.mem_read((ix + offset) & 0xffff));
};
// 0xac : XOR IXH (Undocumented)
dd_instructions[0xac] = function()
{
   do_xor((ix >>> 8) & 0xff);
};
// 0xad : XOR IXL (Undocumented)
dd_instructions[0xad] = function()
{
   do_xor(ix & 0xff);
};
// 0xae : XOR A, (IX+n)
dd_instructions[0xae] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_xor(core.mem_read((ix + offset) & 0xffff));
};
// 0xb4 : OR IXH (Undocumented)
dd_instructions[0xb4] = function()
{
   do_or((ix >>> 8) & 0xff);
};
// 0xb5 : OR IXL (Undocumented)
dd_instructions[0xb5] = function()
{
   do_or(ix & 0xff);
};
// 0xb6 : OR A, (IX+n)
dd_instructions[0xb6] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_or(core.mem_read((ix + offset) & 0xffff));
};
// 0xbc : CP IXH (Undocumented)
dd_instructions[0xbc] = function()
{
   do_cp((ix >>> 8) & 0xff);
};
// 0xbd : CP IXL (Undocumented)
dd_instructions[0xbd] = function()
{
   do_cp(ix & 0xff);
};
// 0xbe : CP A, (IX+n)
dd_instructions[0xbe] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   do_cp(core.mem_read((ix + offset) & 0xffff));
};
// 0xcb : CB Prefix (IX bit instructions)
dd_instructions[0xcb] = function()
{
   pc = (pc + 1) & 0xffff;
   var offset = get_signed_offset_byte(core.mem_read(pc));
   pc = (pc + 1) & 0xffff;
   var opcode = core.mem_read(pc), value;
   
   // As with the "normal" CB prefix, we implement the DDCB prefix
   //  by decoding the opcode directly, rather than using a table.
   if (opcode < 0x40)
   {
      // Shift and rotate instructions.
      var ddcb_functions = [do_rlc, do_rrc, do_rl, do_rr,
                            do_sla, do_sra, do_sll, do_srl];
      
      // Most of the opcodes in this range are not valid,
      //  so we map this opcode onto one of the ones that is.
      var func = ddcb_functions[(opcode & 0x38) >>> 3],
      value = func( core.mem_read((ix + offset) & 0xffff));
      
      core.mem_write((ix + offset) & 0xffff, value);
   }
   else
   {
      var bit_number = (opcode & 0x38) >>> 3;
      
      if (opcode < 0x80)
      {
         // BIT
         flags.N = 0;
         flags.H = 1;
         flags.Z = !(core.mem_read((ix + offset) & 0xffff) & (1 << bit_number)) ? 1 : 0;
         flags.P = flags.Z;
         flags.S = ((bit_number === 7) && !flags.Z) ? 1 : 0;
      }
      else if (opcode < 0xc0)
      {
         // RES
         value = core.mem_read((ix + offset) & 0xffff) & ~(1 << bit_number) & 0xff;
         core.mem_write((ix + offset) & 0xffff, value);
      }
      else
      {
         // SET
         value = core.mem_read((ix + offset) & 0xffff) | (1 << bit_number);
         core.mem_write((ix + offset) & 0xffff, value);
      }
   }
   
   // This implements the undocumented shift, RES, and SET opcodes,
   //  which write their result to memory and also to an 8080 register.
   if (value !== undefined)
   {
      if ((opcode & 0x07) === 0)
         b = value;
      else if ((opcode & 0x07) === 1)
         c = value;
      else if ((opcode & 0x07) === 2)
         d = value;
      else if ((opcode & 0x07) === 3)
         e = value;
      else if ((opcode & 0x07) === 4)
         h = value;
      else if ((opcode & 0x07) === 5)
         l = value;
      // 6 is the documented opcode, which doesn't set a register.
      else if ((opcode & 0x07) === 7)
         a = value;
   }
   
   cycle_counter += cycle_counts_cb[opcode] + 8;
};
// 0xe1 : POP IX
dd_instructions[0xe1] = function()
{
   ix = pop_word();
};
// 0xe3 : EX (SP), IX
dd_instructions[0xe3] = function()
{
   var temp = ix;
   ix = core.mem_read(sp);
   ix |= core.mem_read((sp + 1) & 0xffff) << 8;
   core.mem_write(sp, temp & 0xff);
   core.mem_write((sp + 1) & 0xffff, (temp >>> 8) & 0xff);
};
// 0xe5 : PUSH IX
dd_instructions[0xe5] = function()
{
   push_word(ix);
};
// 0xe9 : JP (IX)
dd_instructions[0xe9] = function()
{
   pc = (ix - 1) & 0xffff;
};
// 0xf9 : LD SP, IX
dd_instructions[0xf9] = function()
{
   sp = ix;
};


///////////////////////////////////////////////////////////////////////////////
/// These tables contain the number of T cycles used for each instruction.
/// In a few special cases, such as conditional control flow instructions,
///  additional cycles might be added to these values.
/// The total number of cycles is the return value of run_instruction().
///////////////////////////////////////////////////////////////////////////////
let cycle_counts = [
    4, 10,  7,  6,  4,  4,  7,  4,  4, 11,  7,  6,  4,  4,  7,  4,
    8, 10,  7,  6,  4,  4,  7,  4, 12, 11,  7,  6,  4,  4,  7,  4,
    7, 10, 16,  6,  4,  4,  7,  4,  7, 11, 16,  6,  4,  4,  7,  4,
    7, 10, 13,  6, 11, 11, 10,  4,  7, 11, 13,  6,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    7,  7,  7,  7,  7,  7,  4,  7,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    4,  4,  4,  4,  4,  4,  7,  4,  4,  4,  4,  4,  4,  4,  7,  4,
    5, 10, 10, 10, 10, 11,  7, 11,  5, 10, 10,  0, 10, 17,  7, 11,
    5, 10, 10, 11, 10, 11,  7, 11,  5,  4, 10, 11, 10,  0,  7, 11,
    5, 10, 10, 19, 10, 11,  7, 11,  5,  4, 10,  4, 10,  0,  7, 11,
    5, 10, 10,  4, 10, 11,  7, 11,  5,  6, 10,  4, 10,  0,  7, 11
];

let cycle_counts_ed = [
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   12, 12, 15, 20,  8, 14,  8,  9, 12, 12, 15, 20,  8, 14,  8,  9,
   12, 12, 15, 20,  8, 14,  8,  9, 12, 12, 15, 20,  8, 14,  8,  9,
   12, 12, 15, 20,  8, 14,  8, 18, 12, 12, 15, 20,  8, 14,  8, 18,
   12, 12, 15, 20,  8, 14,  8,  0, 12, 12, 15, 20,  8, 14,  8,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   16, 16, 16, 16,  0,  0,  0,  0, 16, 16, 16, 16,  0,  0,  0,  0,
   16, 16, 16, 16,  0,  0,  0,  0, 16, 16, 16, 16,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
];

let cycle_counts_cb = [
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 12,  8,  8,  8,  8,  8,  8,  8, 12,  8,
    8,  8,  8,  8,  8,  8, 12,  8,  8,  8,  8,  8,  8,  8, 12,  8,
    8,  8,  8,  8,  8,  8, 12,  8,  8,  8,  8,  8,  8,  8, 12,  8,
    8,  8,  8,  8,  8,  8, 12,  8,  8,  8,  8,  8,  8,  8, 12,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8,
    8,  8,  8,  8,  8,  8, 15,  8,  8,  8,  8,  8,  8,  8, 15,  8
];

let cycle_counts_dd = [
    0,  0,  0,  0,  0,  0,  0,  0,  0, 15,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0, 15,  0,  0,  0,  0,  0,  0,
    0, 14, 20, 10,  8,  8, 11,  0,  0, 15, 20, 10,  8,  8, 11,  0,
    0,  0,  0,  0, 23, 23, 19,  0,  0, 15,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    8,  8,  8,  8,  8,  8, 19,  8,  8,  8,  8,  8,  8,  8, 19,  8,
   19, 19, 19, 19, 19, 19,  0, 19,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  8,  8, 19,  0,  0,  0,  0,  0,  8,  8, 19,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0, 14,  0, 23,  0, 15,  0,  0,  0,  8,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0
];

   // There's tons of stuff in this object,
   //  but only these three functions are the public API.
   return {      
      getState,
      setState,
      reset,
      run_instruction,
      interrupt
   };
}


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?