8bitworkshop/src/common/cpu/ZilogZ80.ts

3424 lines
86 KiB
TypeScript

// Generated by CoffeeScript 1.9.3
import { CPU, Bus, InstructionBased, IOBusConnected, SavesState, Interruptable } from "../devices";
///////////////////////////////////////////////////////////////////////////////
/// @file Z80.js
///
/// @brief Emulator for the Zilog Z80 microprocessor
///
/// @author Matthew 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) 2013 Matthew 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
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// We'll begin with the object constructor and the public API functions.
///////////////////////////////////////////////////////////////////////////////
function FastZ80(coreParameter)
{
// Obviously we'll be needing the core object's functions again.
const 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.");
// 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():Z80State {
return {
PC:pc,
SP:sp,
IX:ix,
IY:iy,
AF:(a<<8)+get_flags_register(),
BC:(b<<8)+c,
DE:(d<<8)+e,
HL:(h<<8)+l,
AF_:(a_prime<<8)+get_flags_prime(),
BC_:(b_prime<<8)+c_prime,
DE_:(d_prime<<8)+e_prime,
HL_:(h_prime<<8)+l_prime,
IR:(i<<8)+r,
im : 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:Z80State) {
pc = state.PC;
sp = state.SP;
ix = state.IX;
iy = state.IY;
a = (state.AF >> 8) & 0xff;
set_flags_register(state.AF);
b = (state.BC >> 8) & 0xff;
c = state.BC & 0xff;
d = (state.DE >> 8) & 0xff;
e = state.DE & 0xff;
h = (state.HL >> 8) & 0xff;
l = state.HL & 0xff;
a_prime = (state.AF_ >> 8) & 0xff;
set_flags_prime(state.AF_);
b_prime = (state.BC_ >> 8) & 0xff;
c_prime = state.BC_ & 0xff;
d_prime = (state.DE_ >> 8) & 0xff;
e_prime = state.DE_ & 0xff;
h_prime = (state.HL_ >> 8) & 0xff;
l_prime = state.HL_ & 0xff;
i = (state.IR >> 8) & 0xff;
r = state.IR & 0xff;
imode = state.im;
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;
//console.log("DI",pc);
}
else if (doing_delayed_ei)
{
iff1 = 1;
iff2 = 1;
//console.log("EI",pc);
}
// 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:boolean, data:number) : boolean
{
//console.log(non_maskable, data, iff1, iff2, do_delayed_ei, do_delayed_di);
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;
return true;
}
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.
pc = (pc - 1) & 0xffff; //SEH: so do_reset() pushes right value
decode_instruction(data);
pc = (pc + 1) & 0xffff; //SEH: so do_reset() pushes right value
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;
}
//console.log(imode,data,pc);
return true;
}
};
///////////////////////////////////////////////////////////////////////////////
/// 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((b << 8) | c));
var result = (l | (h << 8)) + 1;
l = result & 0xff;
h = (result & 0xff00) >>> 8;
flags.N = 1;
};
let do_outi = function()
{
core.io_write((b << 8) | 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((b << 8) | c));
var result = (l | (h << 8)) - 1;
l = result & 0xff;
h = (result & 0xff00) >>> 8;
flags.N = 1;
};
let do_outd = function()
{
core.io_write((b << 8) | 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((a << 8) | 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((a << 8) | 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((b << 8) | c);
};
// 0x41 : OUT (C), B
ed_instructions[0x41] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x49 : OUT (C), C
ed_instructions[0x49] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x51 : OUT (C), D
ed_instructions[0x51] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x59 : OUT (C), E
ed_instructions[0x59] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x61 : OUT (C), H
ed_instructions[0x61] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x69 : OUT (C), L
ed_instructions[0x69] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x71 : OUT (C), 0 (Undocumented)
ed_instructions[0x71] = function()
{
core.io_write((b << 8) | 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((b << 8) | c);
};
// 0x79 : OUT (C), A
ed_instructions[0x79] = function()
{
core.io_write((b << 8) | 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.
this.saveState = getState;
this.loadState = setState;
this.reset = reset;
this.advanceInsn = run_instruction;
this.interrupt = interrupt;
this.getPC = ():number => { return pc; }
this.getSP = ():number => { return sp; }
this.getHalted = ():boolean => { return halted; }
}
export interface Z80State {
AF,BC,DE,HL,AF_,BC_,DE_,HL_,IX,IY,SP,PC,IR : number;
iff1,iff2,im : number;
halted : boolean;
do_delayed_di,do_delayed_ei : boolean;
cycle_counter : number;
}
export class Z80 implements CPU, InstructionBased, IOBusConnected, SavesState<Z80State>, Interruptable<number> {
cpu;
interruptType;
memBus : Bus;
ioBus : Bus;
retryInterrupts : boolean = false;
retryData : number = -1;
private buildCPU() {
if (this.memBus && this.ioBus) {
this.cpu = new FastZ80({
mem_read: this.memBus.read.bind(this.memBus),
mem_write: this.memBus.write.bind(this.memBus),
io_read: this.ioBus.read.bind(this.ioBus),
io_write: this.ioBus.write.bind(this.ioBus),
});
}
}
connectMemoryBus(bus:Bus) {
this.memBus = bus;
this.buildCPU();
}
connectIOBus(bus:Bus) {
this.ioBus = bus;
this.buildCPU();
}
advanceInsn() {
if (this.retryInterrupts && this.retryData >= 0 && this.cpu.interrupt(false, this.retryData)) {
this.retryData = -1;
}
return this.cpu.advanceInsn();
}
reset() {
this.cpu.reset();
}
interrupt(data:number) {
if (!this.cpu.interrupt(false, data) && this.retryInterrupts) {
this.retryData = data;
}
}
NMI() {
this.cpu.interrupt(true, 0);
}
getSP() {
return this.cpu.getSP();
}
getPC() {
return this.cpu.getPC();
}
isHalted() {
return this.cpu.getHalted();
}
saveState() {
return this.cpu.saveState();
}
loadState(s) {
this.cpu.loadState(s);
}
isStable() { return true; }
// TODO: metadata
// TODO: disassembler
}