### Z80 core. To avoid mass repetition of code across the numerous instruction variants the code for this component is built up programmatically and evaluated in the global scope. CoffeeScript is used here for its support of multi-line strings, and expression interpolation in strings. ### ### Registers are stored in a typed array as a way of automatically casting calculations to 8/16 bit, and to allow accessing them interchangeably as register pairs or individual registers by having two arrays backed by the same buffer. For the latter to work, we need to find out the endianness of the host processor, as typed arrays are native-endian (http://lists.w3.org/Archives/Public/public-script-coord/2010AprJun/0048.html, http://cat-in-136.blogspot.com/2011/03/javascript-typed-array-use-native.html) ### window.buildZ80 = (opts) -> opts ?= {} endianTestBuffer = new ArrayBuffer(2) endianTestUint16 = new Uint16Array(endianTestBuffer) endianTestUint8 = new Uint8Array(endianTestBuffer) endianTestUint16[0] = 0x0100 isBigEndian = (endianTestUint8[0] == 0x01) # Offsets into register set when read as register pairs rpAF = 0 rpBC = 1 rpDE = 2 rpHL = 3 rpAF_ = 4 rpBC_ = 5 rpDE_ = 6 rpHL_ = 7 rpIX = 8 rpIY = 9 rpIR = 10 rpSP = 11 rpPC = 12 registerPairIndexes = { 'IX': 8, 'IY': 9 } if isBigEndian rA = 0; rF = 1 rB = 2; rC = 3 rD = 4; rE = 5 rH = 6; rL = 7 rA_ = 8; rF_ = 9 rB_ = 10; rC_ = 11 rD_ = 12; rE_ = 13 rH_ = 14; rL_ = 15 rIXH = 16; rIXL = 17 rIYH = 18; rIYL = 19 rI = 20; rR = 21 registerIndexes = { A: 0, F: 1 B: 2, C: 3 D: 4, E: 5 H: 6, L: 7 IXH: 16, IXL: 17 IYH: 18, IYL: 19 } else # little-endian rF = 0; rA = 1 rC = 2; rB = 3 rE = 4; rD = 5 rL = 6; rH = 7 rF_ = 8; rA_ = 9 rC_ = 10; rB_ = 11 rE_ = 12; rD_ = 13 rL_ = 14; rH_ = 15 rIXL = 16; rIXH = 17 rIYL = 18; rIYH = 19 rR = 20; rI = 21 registerIndexes = { F: 0, A: 1 C: 2, B: 3 E: 4, D: 5 L: 6, H: 7 IXL: 16, IXH: 17 IYL: 18, IYH: 19 } FLAG_C = 0x01 FLAG_N = 0x02 FLAG_P = 0x04 FLAG_V = 0x04 FLAG_3 = 0x08 FLAG_H = 0x10 FLAG_5 = 0x20 FLAG_Z = 0x40 FLAG_S = 0x80 # JS block setting up internal Z80 state and lookup tables setUpStateJS = """ var memory = opts.memory; var ioBus = opts.ioBus; var display = opts.display; var registerBuffer = new ArrayBuffer(26); /* Expose registerBuffer as both register pairs and individual registers */ var regPairs = new Uint16Array(registerBuffer); var regs = new Uint8Array(registerBuffer); var tstates = 0; /* number of tstates since start of this frame */ var iff1 = 0; var iff2 = 0; var im = 0; var halted = false; /* tables for setting Z80 flags */ /* Whether a half carry occurred or not can be determined by looking at the 3rd bit of the two arguments and the result; these are hashed into this table in the form r12, where r is the 3rd bit of the result, 1 is the 3rd bit of the 1st argument and 2 is the third bit of the 2nd argument; the tables differ for add and subtract operations */ var halfcarryAddTable = new Uint8Array([0, #{FLAG_H}, #{FLAG_H}, #{FLAG_H}, 0, 0, 0, #{FLAG_H}]); var halfcarrySubTable = new Uint8Array([0, 0, #{FLAG_H}, 0, #{FLAG_H}, 0, #{FLAG_H}, #{FLAG_H}]); /* Similarly, overflow can be determined by looking at the 7th bits; again the hash into this table is r12 */ var overflowAddTable = new Uint8Array([0, 0, 0, #{FLAG_V}, #{FLAG_V}, 0, 0, 0]); var overflowSubTable = new Uint8Array([0, #{FLAG_V}, 0, 0, 0, 0, #{FLAG_V}, 0]); var sz53Table = new Uint8Array(0x100); /* The S, Z, 5 and 3 bits of the index */ var parityTable = new Uint8Array(0x100); /* The parity of the lookup value */ var sz53pTable = new Uint8Array(0x100); /* OR the above two tables together */ for (var i = 0; i < 0x100; i++) { sz53Table[i] = i & ( #{FLAG_3 | FLAG_5 | FLAG_S} ); var j = i; var parity = 0; for (var k = 0; k < 8; k++) { parity ^= j & 1; j >>=1; } parityTable[i] = (parity ? 0 : #{FLAG_P}); sz53pTable[i] = sz53Table[i] | parityTable[i]; sz53Table[0] |= #{FLAG_Z}; sz53pTable[0] |= #{FLAG_Z}; } var interruptible = true; var interruptPending = false; var interruptDataBus = 0; var opcodePrefix = ''; """ ### Boilerplate generator: a helper to deal with classes of opcodes which perform the same task on different types of operands: e.g. XOR B, XOR (HL), XOR nn, XOR (IX+nn). This function accepts the parameter in question, and returns a set of canned strings for use in the opcode runner body: 'getter': a block of code that performs any necessary memory access etc in order to make 'v' a valid expression; 'v': an expression with no side effects, evaluating to the operand's value. (Must also be a valid lvalue for assignment) 'trunc': an expression such as '& 0xff' to truncate v back to its proper range, if appropriate 'setter': a block of code that writes an updated value back to its proper location, if any Passing hasIXOffsetAlready = true indicates that we have already read the offset value of (IX+nn)/(IY+nn) into a variable 'offset' (necessary because DDCB/FFCB instructions put this before the final opcode byte). ### getParamBoilerplate = (param, hasIXOffsetAlready = false) -> if param.match(/^[AFBCDEHL]|I[XY][HL]$/) regNum = registerIndexes[param] { 'getter': '' 'v': "regs[#{regNum}]" 'trunc': '' 'setter': '' } else if param == '(HL)' { 'getter': "var val = READMEM(regPairs[#{rpHL}]);" 'v': 'val' 'trunc': '& 0xff' 'setter': """ CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); WRITEMEM(regPairs[#{rpHL}], val); """ } else if param == 'nn' { 'getter': "var val = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++;" 'v': 'val' 'trunc': '& 0xff' 'setter': '' } else if (match = param.match(/^\((I[XY])\+nn\)$/)) rp = registerPairIndexes[match[1]] if hasIXOffsetAlready getter = '' else getter = """ var offset = READMEM(regPairs[#{rpPC}]); if (offset & 0x80) offset -= 0x100; CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; """ getter += """ var addr = (regPairs[#{rp}] + offset) & 0xffff; var val = READMEM(addr); """ { 'getter': getter 'v': 'val' 'trunc': '& 0xff' 'setter': """ CONTEND_READ_NO_MREQ(addr, 1); WRITEMEM(addr, val); """ } else if param == 'add' # special case for incorporating ADD/SUB into DAA calculation using a custom variable 'add' { 'getter': '' 'v': 'add' 'trunc': '' 'setter': '' } else throw "Unknown param format: #{param}" ### Opcode generator functions: each returns a string of Javascript that performs the opcode when executed within this module's scope. Note that instructions with DDCBnn opcodes also require an 'offset' variable to be defined as nn (as a signed byte). ### ADC_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} var adctemp = regs[#{rA}] + #{operand.v} + (regs[#{rF}] & #{FLAG_C}); var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (adctemp & 0x88) >> 1 ); regs[#{rA}] = adctemp; regs[#{rF}] = ( adctemp & 0x100 ? #{FLAG_C} : 0 ) | halfcarryAddTable[lookup & 0x07] | overflowAddTable[lookup >> 4] | sz53Table[regs[#{rA}]]; """ ADC_HL_RR = (rp2) -> """ var add16temp = regPairs[#{rpHL}] + regPairs[#{rp2}] + (regs[#{rF}] & #{FLAG_C}); var lookup = ( ( (regPairs[#{rpHL}] & 0x8800) >> 11 ) | ( (regPairs[#{rp2}] & 0x8800) >> 10 ) | ( (add16temp & 0x8800) >> 9 ) ); regPairs[#{rpHL}] = add16temp; regs[#{rF}] = ( (add16temp & 0x10000 ? #{FLAG_C} : 0) | overflowAddTable[lookup >> 4] | (regs[#{rH}] & #{FLAG_3 | FLAG_5 | FLAG_S}) | halfcarryAddTable[lookup & 0x07] | (regPairs[#{rpHL}] ? 0 : #{FLAG_Z}) ); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ ADD_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} var addtemp = regs[#{rA}] + #{operand.v}; var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (addtemp & 0x88) >> 1 ); regs[#{rA}] = addtemp; regs[#{rF}] = ( addtemp & 0x100 ? #{FLAG_C} : 0 ) | halfcarryAddTable[lookup & 0x07] | overflowAddTable[lookup >> 4] | sz53Table[regs[#{rA}]]; """ ADD_RR_RR = (rp1, rp2) -> """ var add16temp = regPairs[#{rp1}] + regPairs[#{rp2}]; var lookup = ( (regPairs[#{rp1}] & 0x0800) >> 11 ) | ( (regPairs[#{rp2}] & 0x0800) >> 10 ) | ( (add16temp & 0x0800) >> 9 ); regPairs[#{rp1}] = add16temp; regs[#{rF}] = ( regs[#{rF}] & ( #{FLAG_V | FLAG_Z | FLAG_S} ) ) | ( add16temp & 0x10000 ? #{FLAG_C} : 0 ) | ( ( add16temp >> 8 ) & ( #{FLAG_3 | FLAG_5} ) ) | halfcarryAddTable[lookup]; CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ AND_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} regs[#{rA}] &= #{operand.v}; regs[#{rF}] = #{FLAG_H} | sz53pTable[regs[#{rA}]]; """ BIT_N_iRRpNNi = (bit, rp) -> # requires 'offset' if bit == 7 updateSignFlag = "if (value & 0x80) regs[#{rF}] |= #{FLAG_S};" else updateSignFlag = "" """ var addr = (regPairs[#{rp}] + offset) & 0xffff; var value = READMEM(addr); regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( ( addr >> 8 ) & #{FLAG_3 | FLAG_5} ); if ( !(value & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z}; #{updateSignFlag} CONTEND_READ_NO_MREQ(addr, 1); """ BIT_N_iHLi = (bit) -> if bit == 7 updateSignFlag = "if (value & 0x80) regs[#{rF}] |= #{FLAG_S};" else updateSignFlag = "" """ var addr = regPairs[#{rpHL}]; var value = READMEM(addr); CONTEND_READ_NO_MREQ(addr, 1); regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( value & #{FLAG_3 | FLAG_5} ); if( !(value & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z}; #{updateSignFlag} """ BIT_N_R = (bit, r) -> if bit == 7 updateSignFlag = "if (regs[#{r}] & 0x80) regs[#{rF}] |= #{FLAG_S};" else updateSignFlag = "" """ regs[#{rF}] = ( regs[#{rF}] & #{FLAG_C} ) | #{FLAG_H} | ( regs[#{r}] & #{FLAG_3 | FLAG_5} ); if( !(regs[#{r}] & #{0x01 << bit}) ) regs[#{rF}] |= #{FLAG_P | FLAG_Z}; #{updateSignFlag} """ CALL_C_NN = (flag, sense) -> condition = "regs[#{rF}] & #{flag}" condition = "!(#{condition})" if not sense """ if (#{condition}) { var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff); regPairs[#{rpPC}] = (h<<8) | l; } else { CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; } """ CALL_NN = () -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff); regPairs[#{rpPC}] = (h<<8) | l; """ CCF = () -> """ regs[#{rF}] = ( regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S} ) | ( (regs[#{rF}] & #{FLAG_C}) ? #{FLAG_H} : #{FLAG_C} ) | ( regs[#{rA}] & #{FLAG_3 | FLAG_5} ); """ CP_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} var cptemp = regs[#{rA}] - #{operand.v}; var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (cptemp & 0x88) >> 1 ); regs[#{rF}] = ( cptemp & 0x100 ? #{FLAG_C} : ( cptemp ? 0 : #{FLAG_Z} ) ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | ( #{operand.v} & #{FLAG_3 | FLAG_5} ) | ( cptemp & #{FLAG_S} ); """ CPI_CPD = (modifier) -> """ var value = READMEM(regPairs[#{rpHL}]); var bytetemp = (regs[#{rA}] - value) & 0xff; var lookup = ((regs[#{rA}] & 0x08) >> 3) | ((value & 0x08) >> 2) | ((bytetemp & 0x08) >> 1); var originalHL = regPairs[#{rpHL}]; CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); regPairs[#{rpHL}]#{modifier}; regPairs[#{rpBC}]--; regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | (regPairs[#{rpBC}] ? #{FLAG_V | FLAG_N} : #{FLAG_N}) | halfcarrySubTable[lookup] | (bytetemp ? 0 : #{FLAG_Z}) | (bytetemp & #{FLAG_S}); if (regs[#{rF}] & #{FLAG_H}) bytetemp--; regs[#{rF}] |= (bytetemp & #{FLAG_3}) | ( (bytetemp & 0x02) ? #{FLAG_5} : 0 ); """ CPIR_CPDR = (modifier) -> """ #{CPI_CPD(modifier)} if ((regs[#{rF}] & #{FLAG_V | FLAG_Z}) == #{FLAG_V}) { regPairs[#{rpPC}] -= 2; CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); CONTEND_READ_NO_MREQ(originalHL, 1); } """ CPD = () -> CPI_CPD('--') CPI = () -> CPI_CPD('++') CPDR = () -> CPIR_CPDR('--') CPIR = () -> CPIR_CPDR('++') DAA = () -> subClause = SUB_A('add') addClause = ADD_A('add') """ var add = 0; var carry = regs[#{rF}] & #{FLAG_C}; if( ( regs[#{rF}] & #{FLAG_H} ) || ( ( regs[#{rA}] & 0x0f ) > 9 ) ) add = 6; if( carry || ( regs[#{rA}] > 0x99 ) ) add |= 0x60; if( regs[#{rA}] > 0x99 ) carry = #{FLAG_C}; if( regs[#{rF}] & #{FLAG_N} ) { #{subClause} } else { #{addClause} } regs[#{rF}] = ( regs[#{rF}] & #{~( FLAG_C | FLAG_P )} ) | carry | parityTable[regs[#{rA}]]; """ CPL = () -> """ regs[#{rA}] ^= 0xff; regs[#{rF}] = (regs[#{rF}] & #{FLAG_C | FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | #{FLAG_N | FLAG_H}; """ DEC = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} regs[#{rF}] = (regs[#{rF}] & #{FLAG_C} ) | ( #{operand.v} & 0x0f ? 0 : #{FLAG_H} ) | #{FLAG_N}; #{operand.v} = (#{operand.v} - 1) #{operand.trunc}; #{operand.setter} regs[#{rF}] |= (#{operand.v} == 0x7f ? #{FLAG_V} : 0) | sz53Table[#{operand.v}]; """ DEC_RR = (rp) -> """ regPairs[#{rp}]--; CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ DI = () -> """ iff1 = iff2 = 0; """ DJNZ_N = () -> """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); regs[#{rB}]--; if (regs[#{rB}]) { /* take branch */ var offset = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset); } else { /* do not take branch */ CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; } """ EI = () -> """ iff1 = iff2 = 1; interruptible = false; """ EX_iSPi_RR = (rp) -> """ var l = READMEM(regPairs[#{rpSP}]); var spPlus1 = (regPairs[#{rpSP}] + 1) & 0xffff; var h = READMEM(spPlus1); CONTEND_READ_NO_MREQ(spPlus1, 1); WRITEMEM(spPlus1, regPairs[#{rp}] >> 8); WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] & 0xff); regPairs[#{rp}] = (h<<8) | l; CONTEND_WRITE_NO_MREQ(regPairs[#{rpSP}], 1); CONTEND_WRITE_NO_MREQ(regPairs[#{rpSP}], 1); """ EX_RR_RR = (rp1, rp2) -> """ var temp = regPairs[#{rp1}]; regPairs[#{rp1}] = regPairs[#{rp2}]; regPairs[#{rp2}] = temp; """ EXX = () -> """ var wordtemp; wordtemp = regPairs[#{rpBC}]; regPairs[#{rpBC}] = regPairs[#{rpBC_}]; regPairs[#{rpBC_}] = wordtemp; wordtemp = regPairs[#{rpDE}]; regPairs[#{rpDE}] = regPairs[#{rpDE_}]; regPairs[#{rpDE_}] = wordtemp; wordtemp = regPairs[#{rpHL}]; regPairs[#{rpHL}] = regPairs[#{rpHL_}]; regPairs[#{rpHL_}] = wordtemp; """ HALT = () -> """ halted = true; regPairs[#{rpPC}]--; """ IM = (val) -> """ im = #{val}; """ IN_A_N = () -> """ var val = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var port = (regs[#{rA}] << 8) | val; CONTEND_PORT_EARLY(port); regs[#{rA}] = ioBus.read(port); CONTEND_PORT_LATE(port); """ IN_F_iCi = () -> # as IN_R_iCi, but result is written to a local variable rather than a register """ var port = regPairs[#{rpBC}]; CONTEND_PORT_EARLY(port); var result = ioBus.read(port); CONTEND_PORT_LATE(port); regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[result]; """ IN_R_iCi = (r) -> """ var port = regPairs[#{rpBC}]; CONTEND_PORT_EARLY(port); regs[#{r}] = ioBus.read(port); CONTEND_PORT_LATE(port); regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{r}]]; """ INC = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | (#{operand.v} & 0x0f ? 0 : #{FLAG_H}) | #{FLAG_N}; #{operand.v} = (#{operand.v} + 1) #{operand.trunc}; #{operand.setter} regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | ( #{operand.v} == 0x80 ? #{FLAG_V} : 0 ) | ( #{operand.v} & 0x0f ? 0 : #{FLAG_H} ) | sz53Table[#{operand.v}]; """ INC_RR = (rp) -> """ regPairs[#{rp}]++; CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ INI_IND = (modifier) -> """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_PORT_EARLY(regPairs[#{rpBC}]); var initemp = ioBus.read(regPairs[#{rpBC}]); CONTEND_PORT_LATE(regPairs[#{rpBC}]); WRITEMEM(regPairs[#{rpHL}], initemp); regs[#{rB}]--; var originalHL = regPairs[#{rpHL}]; regPairs[#{rpHL}]#{modifier}#{modifier}; var initemp2 = (initemp + regs[#{rC}] #{modifier} 1) & 0xff; regs[#{rF}] = (initemp & 0x80 ? #{FLAG_N} : 0) | ((initemp2 < initemp) ? #{FLAG_H | FLAG_C} : 0 ) | ( parityTable[ (initemp2 & 0x07) ^ regs[#{rB}] ] ? #{FLAG_P} : 0 ) | sz53Table[regs[#{rB}]]; """ INIR_INDR = (modifier) -> """ #{INI_IND(modifier)} if (regs[#{rB}]) { CONTEND_WRITE_NO_MREQ(originalHL, 1); CONTEND_WRITE_NO_MREQ(originalHL, 1); CONTEND_WRITE_NO_MREQ(originalHL, 1); CONTEND_WRITE_NO_MREQ(originalHL, 1); CONTEND_WRITE_NO_MREQ(originalHL, 1); regPairs[#{rpPC}] -= 2; } """ INI = () -> INI_IND('+') IND = () -> INI_IND('-') INIR = () -> INIR_INDR('+') INDR = () -> INIR_INDR('-') JP_C_NN = (flag, sense) -> condition = "regs[#{rF}] & #{flag}" condition = "!(#{condition})" if not sense """ if (#{condition}) { var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regPairs[#{rpPC}] = (h<<8) | l; } else { CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; } """ JP_RR = (rp) -> """ regPairs[#{rpPC}] = regPairs[#{rp}]; """ JP_NN = () -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regPairs[#{rpPC}] = (h<<8) | l; """ JR_C_N = (flag, sense) -> condition = "regs[#{rF}] & #{flag}" condition = "!(#{condition})" if not sense """ if (#{condition}) { var offset = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset); } else { CONTEND_READ(regPairs[#{rpPC}], 3); regPairs[#{rpPC}]++; /* skip past offset byte */ } """ JR_N = () -> """ var offset = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; regPairs[#{rpPC}] += (offset & 0x80 ? offset - 0x100 : offset); """ LD_A_iNNi = () -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var addr = (h<<8) | l; regs[#{rA}] = READMEM(addr); """ LD_iNNi_A = () -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var addr = (h<<8) | l; WRITEMEM(addr, regs[#{rA}]); """ LD_iNNi_RR = (rp) -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var addr = (h<<8) | l; WRITEMEM(addr, regPairs[#{rp}] & 0xff); addr = (addr + 1) & 0xffff; WRITEMEM(addr, regPairs[#{rp}] >> 8); """ LD_iRRi_N = (rp) -> """ var n = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; WRITEMEM(regPairs[#{rp}], n); """ LD_iRRi_R = (rp, r) -> """ WRITEMEM(regPairs[#{rp}], regs[#{r}]); """ LD_iRRpNNi_N = (rp) -> """ var offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; if (offset & 0x80) offset -= 0x100; var addr = (regPairs[#{rp}] + offset) & 0xffff; var val = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; WRITEMEM(addr, val); """ LD_iRRpNNi_R = (rp, r) -> """ var offset = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; if (offset & 0x80) offset -= 0x100; var addr = (regPairs[#{rp}] + offset) & 0xffff; WRITEMEM(addr, regs[#{r}]); """ LD_R_iRRi = (r, rp) -> """ regs[#{r}] = READMEM(regPairs[#{rp}]); """ LD_R_iRRpNNi = (r, rp) -> """ var offset = READMEM(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; if (offset & 0x80) offset -= 0x100; var addr = (regPairs[#{rp}] + offset) & 0xffff; regs[#{r}] = READMEM(addr); """ LD_R_N = (r) -> """ regs[#{r}] = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; """ LD_R_R = (r1, r2) -> if r1 == rI || r2 == rI || r1 == rR || r2 == rR output = """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); regs[#{r1}] = regs[#{r2}]; """ if (r1 == rA) output += """ regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53Table[regs[#{rA}]] | ( iff2 ? #{FLAG_V} : 0 ); """ output else """ regs[#{r1}] = regs[#{r2}]; """ LD_RR_iNNi = (rp, shifted) -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var addr = (h<<8) | l; l = READMEM(addr); addr = (addr + 1) & 0xffff; h = READMEM(addr); regPairs[#{rp}] = (h<<8) | l; """ LD_RR_NN = (rp) -> """ var l = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; var h = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regPairs[#{rp}] = (h<<8) | l; """ LD_RR_RR = (rp1, rp2) -> # only used for LD SP,HL/IX/IY """ regPairs[#{rp1}] = regPairs[#{rp2}]; CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ LDBITOP = (regName, opcode, bit, rp) -> # load (rp+nn) into register regName, perform opcode, write back to (rp+nn) regNum = registerIndexes[regName] """ var addr = (regPairs[#{rp}] + offset) & 0xffff; regs[#{regNum}] = READMEM(addr); #{opcode(bit, regName)} CONTEND_READ_NO_MREQ(addr, 1); WRITEMEM(addr, regs[#{regNum}]); """ LDI_LDD = (modifier) -> """ var bytetemp = READMEM(regPairs[#{rpHL}]); regPairs[#{rpBC}]--; WRITEMEM(regPairs[#{rpDE}],bytetemp); var originalDE = regPairs[#{rpDE}]; regPairs[#{rpDE}]#{modifier}; regPairs[#{rpHL}]#{modifier}; bytetemp = (bytetemp + regs[#{rA}]) & 0xff; regs[#{rF}] = (regs[#{rF}] & #{FLAG_C | FLAG_Z | FLAG_S}) | (regPairs[#{rpBC}] ? #{FLAG_V} : 0) | (bytetemp & #{FLAG_3}) | ((bytetemp & 0x02) ? #{FLAG_5} : 0); CONTEND_READ_NO_MREQ(originalDE, 1); CONTEND_READ_NO_MREQ(originalDE, 1); """ LDIR_LDDR = (modifier) -> """ #{LDI_LDD(modifier)} if (regPairs[#{rpBC}]) { regPairs[#{rpPC}]-=2; CONTEND_READ_NO_MREQ(originalDE, 1); CONTEND_READ_NO_MREQ(originalDE, 1); CONTEND_READ_NO_MREQ(originalDE, 1); CONTEND_READ_NO_MREQ(originalDE, 1); CONTEND_READ_NO_MREQ(originalDE, 1); } """ LDI = () -> LDI_LDD('++') LDD = () -> LDI_LDD('--') LDIR = () -> LDIR_LDDR('++') LDDR = () -> LDIR_LDDR('--') LDSHIFTOP = (regName, opcode, rp) -> # load (rp+nn) into register regName, perform opcode, write back to (rp+nn) regNum = registerIndexes[regName] """ var addr = (regPairs[#{rp}] + offset) & 0xffff; regs[#{regNum}] = READMEM(addr); #{opcode(regName)} CONTEND_READ_NO_MREQ(addr, 1); WRITEMEM(addr, regs[#{regNum}]); """ NEG = () -> """ var val = regs[#{rA}]; var subtemp = -val; var lookup = ( (val & 0x88) >> 2 ) | ( (subtemp & 0x88) >> 1 ); regs[#{rA}] = subtemp; regs[#{rF}] = ( subtemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]]; """ NOP = () -> """ """ OR_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} regs[#{rA}] |= #{operand.v}; regs[#{rF}] = sz53pTable[regs[#{rA}]]; """ OUT_iCi_0 = (r) -> """ CONTEND_PORT_EARLY(regPairs[#{rpBC}]); ioBus.write(regPairs[#{rpBC}], 0, tstates); CONTEND_PORT_LATE(regPairs[#{rpBC}]); """ OUT_iCi_R = (r) -> """ CONTEND_PORT_EARLY(regPairs[#{rpBC}]); ioBus.write(regPairs[#{rpBC}], regs[#{r}], tstates); CONTEND_PORT_LATE(regPairs[#{rpBC}]); """ OUT_iNi_A = () -> """ var port = (regs[#{rA}] << 8) | READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; CONTEND_PORT_EARLY(port); ioBus.write(port, regs[#{rA}], tstates); CONTEND_PORT_LATE(port); """ OUTI_OUTD = (modifier) -> """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); var outitemp = READMEM(regPairs[#{rpHL}]); regs[#{rB}]--; /* This does happen first, despite what the specs say */ CONTEND_PORT_EARLY(regPairs[#{rpBC}]); ioBus.write(regPairs[#{rpBC}], outitemp, tstates); CONTEND_PORT_LATE(regPairs[#{rpBC}]); regPairs[#{rpHL}]#{modifier}; outitemp2 = (outitemp + regs[#{rL}]) & 0xff; regs[#{rF}] = (outitemp & 0x80 ? #{FLAG_N} : 0) | ( (outitemp2 < outitemp) ? #{FLAG_H | FLAG_C} : 0) | (parityTable[ (outitemp2 & 0x07) ^ regs[#{rB}] ] ? #{FLAG_P} : 0 ) | sz53Table[ regs[#{rB}] ]; """ OTIR_OTDR = (modifier) -> """ #{OUTI_OUTD(modifier)} if (regs[#{rB}]) { regPairs[#{rpPC}]-=2; CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpBC}], 1); } """ OUTD = () -> OUTI_OUTD('--'); OUTI = () -> OUTI_OUTD('++'); OTDR = () -> OTIR_OTDR('--'); OTIR = () -> OTIR_OTDR('++'); POP_RR = (rp) -> """ var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; regPairs[#{rp}] = (h<<8) | l; """ PUSH_RR = (rp) -> """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] >> 8); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rp}] & 0xff); """ RES = (bit, param) -> operand = getParamBoilerplate(param, true) hexMask = 0xff ^ (1 << bit) """ #{operand.getter} #{operand.v} &= #{hexMask}; #{operand.setter} """ RET = () -> """ var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; regPairs[#{rpPC}] = (h<<8) | l; """ RET_C = (flag, sense) -> condition = "regs[#{rF}] & #{flag}" condition = "!(#{condition})" if not sense """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); if (#{condition}) { var l = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; var h = READMEM(regPairs[#{rpSP}]); regPairs[#{rpSP}]++; regPairs[#{rpPC}] = (h<<8) | l; } """ RETN = () -> """ iff1 = iff2; #{RET()} """ RL = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} var rltemp = #{operand.v}; #{operand.v} = ( (#{operand.v} << 1) | (regs[#{rF}] & #{FLAG_C}) ) #{operand.trunc}; regs[#{rF}] = ( rltemp >> 7 ) | sz53pTable[#{operand.v}]; #{operand.setter} """ RLA = () -> """ var bytetemp = regs[#{rA}]; regs[#{rA}] = (regs[#{rA}] << 1) | (regs[#{rF}] & #{FLAG_C}); regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | (bytetemp >> 7); """ RLC = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} #{operand.v} = ( (#{operand.v} << 1) | (#{operand.v} >> 7) ) #{operand.trunc}; regs[#{rF}] = (#{operand.v} & #{FLAG_C}) | sz53pTable[#{operand.v}]; #{operand.setter} """ RLD = () -> """ var bytetemp = READMEM(regPairs[#{rpHL}]); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); var val = (bytetemp << 4) | (regs[#{rA}] & 0x0f); WRITEMEM(regPairs[#{rpHL}], val); regs[#{rA}] = (regs[#{rA}] & 0xf0) | (bytetemp >> 4); regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{rA}]]; """ RLCA = () -> """ regs[#{rA}] = (regs[#{rA}] << 1) | (regs[#{rA}] >> 7); regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_C | FLAG_3 | FLAG_5}); """ RR = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} var rrtemp = #{operand.v}; #{operand.v} = ( (#{operand.v} >> 1) | ( regs[#{rF}] << 7 ) ) #{operand.trunc}; regs[#{rF}] = (rrtemp & #{FLAG_C}) | sz53pTable[#{operand.v}]; #{operand.setter} """ RRA = () -> """ var bytetemp = regs[#{rA}]; regs[#{rA}] = (bytetemp >> 1) | (regs[#{rF}] << 7); regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | (bytetemp & #{FLAG_C}); """ RRC = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} regs[#{rF}] = #{operand.v} & #{FLAG_C}; #{operand.v} = ( (#{operand.v} >> 1) | (#{operand.v} << 7) ) #{operand.trunc}; regs[#{rF}] |= sz53pTable[#{operand.v}]; #{operand.setter} """ RRCA = () -> """ regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_C}); regs[#{rA}] = (regs[#{rA}] >> 1) | (regs[#{rA}] << 7); regs[#{rF}] |= (regs[#{rA}] & #{FLAG_3 | FLAG_5}); """ RRD = () -> """ var bytetemp = READMEM(regPairs[#{rpHL}]); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpHL}], 1); var val = (regs[#{rA}] << 4) | (bytetemp >> 4); WRITEMEM(regPairs[#{rpHL}], val); regs[#{rA}] = (regs[#{rA}] & 0xf0) | (bytetemp & 0x0f); regs[#{rF}] = (regs[#{rF}] & #{FLAG_C}) | sz53pTable[regs[#{rA}]]; """ RST = (addr) -> """ CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff); regPairs[#{rpPC}] = #{addr}; """ SBC_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} var sbctemp = regs[#{rA}] - #{operand.v} - (regs[#{rF}] & #{FLAG_C}); var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (sbctemp & 0x88) >> 1 ); regs[#{rA}] = sbctemp; regs[#{rF}] = ( sbctemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]]; """ SBC_HL_RR = (rp) -> """ var sub16temp = regPairs[#{rpHL}] - regPairs[#{rp}] - (regs[#{rF}] & #{FLAG_C}); var lookup = ( (regPairs[#{rpHL}] & 0x8800) >> 11 ) | ( (regPairs[#{rp}] & 0x8800) >> 10 ) | ( (sub16temp & 0x8800) >> 9 ); regPairs[#{rpHL}] = sub16temp; regs[#{rF}] = ( sub16temp & 0x10000 ? #{FLAG_C} : 0 ) | #{FLAG_N} | overflowSubTable[lookup >> 4] | (regs[#{rH}] & #{FLAG_3 | FLAG_5 | FLAG_S}) | halfcarrySubTable[lookup&0x07] | (regPairs[#{rpHL}] ? 0 : #{FLAG_Z}); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpIR}], 1); """ SCF = () -> """ regs[#{rF}] = (regs[#{rF}] & #{FLAG_P | FLAG_Z | FLAG_S}) | (regs[#{rA}] & #{FLAG_3 | FLAG_5}) | #{FLAG_C}; """ SET = (bit, param) -> hexMask = 1 << bit operand = getParamBoilerplate(param, true) """ #{operand.getter} #{operand.v} |= #{hexMask}; #{operand.setter} """ SHIFT = (prefix) -> # Fake instruction for shifted opcodes - passes control to a secondary opcode table """ opcodePrefix = '#{prefix}'; interruptible = false; """ SLA = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} regs[#{rF}] = #{operand.v} >> 7; #{operand.v} = (#{operand.v} << 1) #{operand.trunc}; regs[#{rF}] |= sz53pTable[#{operand.v}]; #{operand.setter} """ SLL = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} regs[#{rF}] = #{operand.v} >> 7; #{operand.v} = (((#{operand.v}) << 1) #{operand.trunc}) | 0x01; regs[#{rF}] |= sz53pTable[#{operand.v}]; #{operand.setter} """ SRA = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} regs[#{rF}] = #{operand.v} & #{FLAG_C}; #{operand.v} = ( (#{operand.v} & 0x80) | (#{operand.v} >> 1) ) #{operand.trunc}; regs[#{rF}] |= sz53pTable[#{operand.v}]; #{operand.setter} """ SRL = (param) -> operand = getParamBoilerplate(param, true) """ #{operand.getter} regs[#{rF}] = #{operand.v} & #{FLAG_C}; #{operand.v} >>= 1; regs[#{rF}] |= sz53pTable[#{operand.v}]; #{operand.setter} """ SUB_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} var subtemp = regs[#{rA}] - #{operand.v}; var lookup = ( (regs[#{rA}] & 0x88) >> 3 ) | ( (#{operand.v} & 0x88) >> 2 ) | ( (subtemp & 0x88) >> 1 ); regs[#{rA}] = subtemp; regs[#{rF}] = ( subtemp & 0x100 ? #{FLAG_C} : 0 ) | #{FLAG_N} | halfcarrySubTable[lookup & 0x07] | overflowSubTable[lookup >> 4] | sz53Table[regs[#{rA}]]; """ XOR_A = (param) -> operand = getParamBoilerplate(param) """ #{operand.getter} regs[#{rA}] ^= #{operand.v}; regs[#{rF}] = sz53pTable[regs[#{rA}]]; """ ### Given a table mapping opcodes to Javascript snippets (and optionally a fallback table for opcodes that aren't defined in the first one), build an enormous switch statement for them ### opcodeSwitch = (runStringTable, fallbackTable = {}, traps = []) -> clauses = [] for i in [0...0x100] runString = runStringTable[i] if not runString? runString = fallbackTable[i] if runString? relevantTraps = ([address, action] for [address, opcode, action] in traps when opcode == i) trapCode = ( "if (regPairs[#{rpPC}] == #{(address + 1) & 0xffff} && !(#{action})) break;" for [address, action] in relevantTraps ) clauses.push """ case #{i}: { var fn#{i} = function() { #{trapCode.join("\n")} #{runString} }; fn#{i}(); } break; """ """ switch (opcode) { #{clauses.join('')} default: var addr = regPairs[#{rpPC}] - 1; throw("Unimplemented opcode " + opcode + " in page #{runStringTable[0x100]} - PC = " + addr); } """ # Tables mapping opcodes to Javascript snippets OPCODE_RUN_STRINGS_CB = { 0x00: RLC "B" # RLC B 0x01: RLC "C" # RLC C 0x02: RLC "D" # RLC D 0x03: RLC "E" # RLC E 0x04: RLC "H" # RLC H 0x05: RLC "L" # RLC L 0x06: RLC "(HL)" # RLC (HL) 0x07: RLC "A" # RLC A 0x08: RRC "B" # RRC B 0x09: RRC "C" # RRC C 0x0a: RRC "D" # RRC D 0x0b: RRC "E" # RRC E 0x0c: RRC "H" # RRC H 0x0d: RRC "L" # RRC L 0x0e: RRC "(HL)" # RRC (HL) 0x0f: RRC "A" # RRC A 0x10: RL 'B' # RL B 0x11: RL 'C' # RL C 0x12: RL 'D' # RL D 0x13: RL 'E' # RL E 0x14: RL 'H' # RL H 0x15: RL 'L' # RL L 0x16: RL '(HL)' # RL (HL) 0x17: RL 'A' # RL A 0x18: RR 'B' # RR B 0x19: RR 'C' # RR C 0x1a: RR 'D' # RR D 0x1b: RR 'E' # RR E 0x1c: RR 'H' # RR H 0x1d: RR 'L' # RR L 0x1e: RR '(HL)' # RR (HL) 0x1f: RR 'A' # RR A 0x20: SLA 'B' # SLA B 0x21: SLA 'C' # SLA C 0x22: SLA 'D' # SLA D 0x23: SLA 'E' # SLA E 0x24: SLA 'H' # SLA H 0x25: SLA 'L' # SLA L 0x26: SLA '(HL)' # SLA (HL) 0x27: SLA 'A' # SLA A 0x28: SRA 'B' # SRA B 0x29: SRA 'C' # SRA C 0x2a: SRA 'D' # SRA D 0x2b: SRA 'E' # SRA E 0x2c: SRA 'H' # SRA H 0x2d: SRA 'L' # SRA L 0x2e: SRA '(HL)' # SRA (HL) 0x2f: SRA 'A' # SRA A 0x30: SLL 'B' # SLL B 0x31: SLL 'C' # SLL C 0x32: SLL 'D' # SLL D 0x33: SLL 'E' # SLL E 0x34: SLL 'H' # SLL H 0x35: SLL 'L' # SLL L 0x36: SLL '(HL)' # SLL (HL) 0x37: SLL 'A' # SLL A 0x38: SRL 'B' # SRL B 0x39: SRL 'C' # SRL C 0x3a: SRL 'D' # SRL D 0x3b: SRL 'E' # SRL E 0x3c: SRL 'H' # SRL H 0x3d: SRL 'L' # SRL L 0x3e: SRL '(HL)' # SRL (HL) 0x3f: SRL 'A' # SRL A 0x40: BIT_N_R(0, rB) # BIT 0,B 0x41: BIT_N_R(0, rC) # BIT 0,C 0x42: BIT_N_R(0, rD) # BIT 0,D 0x43: BIT_N_R(0, rE) # BIT 0,E 0x44: BIT_N_R(0, rH) # BIT 0,H 0x45: BIT_N_R(0, rL) # BIT 0,L 0x46: BIT_N_iHLi(0) # BIT 0,(HL) 0x47: BIT_N_R(0, rA) # BIT 0,A 0x48: BIT_N_R(1, rB) # BIT 1,B 0x49: BIT_N_R(1, rC) # BIT 1,C 0x4A: BIT_N_R(1, rD) # BIT 1,D 0x4B: BIT_N_R(1, rE) # BIT 1,E 0x4C: BIT_N_R(1, rH) # BIT 1,H 0x4D: BIT_N_R(1, rL) # BIT 1,L 0x4E: BIT_N_iHLi(1) # BIT 1,(HL) 0x4F: BIT_N_R(1, rA) # BIT 1,A 0x50: BIT_N_R(2, rB) # BIT 2,B 0x51: BIT_N_R(2, rC) # BIT 2,C 0x52: BIT_N_R(2, rD) # BIT 2,D 0x53: BIT_N_R(2, rE) # BIT 2,E 0x54: BIT_N_R(2, rH) # BIT 2,H 0x55: BIT_N_R(2, rL) # BIT 2,L 0x56: BIT_N_iHLi(2) # BIT 2,(HL) 0x57: BIT_N_R(2, rA) # BIT 2,A 0x58: BIT_N_R(3, rB) # BIT 3,B 0x59: BIT_N_R(3, rC) # BIT 3,C 0x5A: BIT_N_R(3, rD) # BIT 3,D 0x5B: BIT_N_R(3, rE) # BIT 3,E 0x5C: BIT_N_R(3, rH) # BIT 3,H 0x5D: BIT_N_R(3, rL) # BIT 3,L 0x5E: BIT_N_iHLi(3) # BIT 3,(HL) 0x5F: BIT_N_R(3, rA) # BIT 3,A 0x60: BIT_N_R(4, rB) # BIT 4,B 0x61: BIT_N_R(4, rC) # BIT 4,C 0x62: BIT_N_R(4, rD) # BIT 4,D 0x63: BIT_N_R(4, rE) # BIT 4,E 0x64: BIT_N_R(4, rH) # BIT 4,H 0x65: BIT_N_R(4, rL) # BIT 4,L 0x66: BIT_N_iHLi(4) # BIT 4,(HL) 0x67: BIT_N_R(4, rA) # BIT 4,A 0x68: BIT_N_R(5, rB) # BIT 5,B 0x69: BIT_N_R(5, rC) # BIT 5,C 0x6A: BIT_N_R(5, rD) # BIT 5,D 0x6B: BIT_N_R(5, rE) # BIT 5,E 0x6C: BIT_N_R(5, rH) # BIT 5,H 0x6D: BIT_N_R(5, rL) # BIT 5,L 0x6E: BIT_N_iHLi(5) # BIT 5,(HL) 0x6F: BIT_N_R(5, rA) # BIT 5,A 0x70: BIT_N_R(6, rB) # BIT 6,B 0x71: BIT_N_R(6, rC) # BIT 6,C 0x72: BIT_N_R(6, rD) # BIT 6,D 0x73: BIT_N_R(6, rE) # BIT 6,E 0x74: BIT_N_R(6, rH) # BIT 6,H 0x75: BIT_N_R(6, rL) # BIT 6,L 0x76: BIT_N_iHLi(6) # BIT 6,(HL) 0x77: BIT_N_R(6, rA) # BIT 6,A 0x78: BIT_N_R(7, rB) # BIT 7,B 0x79: BIT_N_R(7, rC) # BIT 7,C 0x7A: BIT_N_R(7, rD) # BIT 7,D 0x7B: BIT_N_R(7, rE) # BIT 7,E 0x7C: BIT_N_R(7, rH) # BIT 7,H 0x7D: BIT_N_R(7, rL) # BIT 7,L 0x7E: BIT_N_iHLi(7) # BIT 7,(HL) 0x7F: BIT_N_R(7, rA) # BIT 7,A 0x80: RES 0, 'B' # RES 0,B 0x81: RES 0, 'C' # RES 0,C 0x82: RES 0, 'D' # RES 0,D 0x83: RES 0, 'E' # RES 0,E 0x84: RES 0, 'H' # RES 0,H 0x85: RES 0, 'L' # RES 0,L 0x86: RES 0, '(HL)' # RES 0,(HL) 0x87: RES 0, 'A' # RES 0,A 0x88: RES 1, 'B' # RES 1,B 0x89: RES 1, 'C' # RES 1,C 0x8A: RES 1, 'D' # RES 1,D 0x8B: RES 1, 'E' # RES 1,E 0x8C: RES 1, 'H' # RES 1,H 0x8D: RES 1, 'L' # RES 1,L 0x8E: RES 1, '(HL)' # RES 1,(HL) 0x8F: RES 1, 'A' # RES 1,A 0x90: RES 2, 'B' # RES 2,B 0x91: RES 2, 'C' # RES 2,C 0x92: RES 2, 'D' # RES 2,D 0x93: RES 2, 'E' # RES 2,E 0x94: RES 2, 'H' # RES 2,H 0x95: RES 2, 'L' # RES 2,L 0x96: RES 2, '(HL)' # RES 2,(HL) 0x97: RES 2, 'A' # RES 2,A 0x98: RES 3, 'B' # RES 3,B 0x99: RES 3, 'C' # RES 3,C 0x9A: RES 3, 'D' # RES 3,D 0x9B: RES 3, 'E' # RES 3,E 0x9C: RES 3, 'H' # RES 3,H 0x9D: RES 3, 'L' # RES 3,L 0x9E: RES 3, '(HL)' # RES 3,(HL) 0x9F: RES 3, 'A' # RES 3,A 0xA0: RES 4, 'B' # RES 4,B 0xA1: RES 4, 'C' # RES 4,C 0xA2: RES 4, 'D' # RES 4,D 0xA3: RES 4, 'E' # RES 4,E 0xA4: RES 4, 'H' # RES 4,H 0xA5: RES 4, 'L' # RES 4,L 0xA6: RES 4, '(HL)' # RES 4,(HL) 0xA7: RES 4, 'A' # RES 4,A 0xA8: RES 5, 'B' # RES 5,B 0xA9: RES 5, 'C' # RES 5,C 0xAA: RES 5, 'D' # RES 5,D 0xAB: RES 5, 'E' # RES 5,E 0xAC: RES 5, 'H' # RES 5,H 0xAD: RES 5, 'L' # RES 5,L 0xAE: RES 5, '(HL)' # RES 5,(HL) 0xAF: RES 5, 'A' # RES 5,A 0xB0: RES 6, 'B' # RES 6,B 0xB1: RES 6, 'C' # RES 6,C 0xB2: RES 6, 'D' # RES 6,D 0xB3: RES 6, 'E' # RES 6,E 0xB4: RES 6, 'H' # RES 6,H 0xB5: RES 6, 'L' # RES 6,L 0xB6: RES 6, '(HL)' # RES 6,(HL) 0xB7: RES 6, 'A' # RES 6,A 0xB8: RES 7, 'B' # RES 7,B 0xB9: RES 7, 'C' # RES 7,C 0xBA: RES 7, 'D' # RES 7,D 0xBB: RES 7, 'E' # RES 7,E 0xBC: RES 7, 'H' # RES 7,H 0xBD: RES 7, 'L' # RES 7,L 0xBE: RES 7, '(HL)' # RES 7,(HL) 0xBF: RES 7, 'A' # RES 7,A 0xC0: SET 0, 'B' # SET 0,B 0xC1: SET 0, 'C' # SET 0,C 0xC2: SET 0, 'D' # SET 0,D 0xC3: SET 0, 'E' # SET 0,E 0xC4: SET 0, 'H' # SET 0,H 0xC5: SET 0, 'L' # SET 0,L 0xC6: SET 0, '(HL)' # SET 0,(HL) 0xC7: SET 0, 'A' # SET 0,A 0xC8: SET 1, 'B' # SET 1,B 0xC9: SET 1, 'C' # SET 1,C 0xCA: SET 1, 'D' # SET 1,D 0xCB: SET 1, 'E' # SET 1,E 0xCC: SET 1, 'H' # SET 1,H 0xCD: SET 1, 'L' # SET 1,L 0xCE: SET 1, '(HL)' # SET 1,(HL) 0xCF: SET 1, 'A' # SET 1,A 0xD0: SET 2, 'B' # SET 2,B 0xD1: SET 2, 'C' # SET 2,C 0xD2: SET 2, 'D' # SET 2,D 0xD3: SET 2, 'E' # SET 2,E 0xD4: SET 2, 'H' # SET 2,H 0xD5: SET 2, 'L' # SET 2,L 0xD6: SET 2, '(HL)' # SET 2,(HL) 0xD7: SET 2, 'A' # SET 2,A 0xD8: SET 3, 'B' # SET 3,B 0xD9: SET 3, 'C' # SET 3,C 0xDA: SET 3, 'D' # SET 3,D 0xDB: SET 3, 'E' # SET 3,E 0xDC: SET 3, 'H' # SET 3,H 0xDD: SET 3, 'L' # SET 3,L 0xDE: SET 3, '(HL)' # SET 3,(HL) 0xDF: SET 3, 'A' # SET 3,A 0xE0: SET 4, 'B' # SET 4,B 0xE1: SET 4, 'C' # SET 4,C 0xE2: SET 4, 'D' # SET 4,D 0xE3: SET 4, 'E' # SET 4,E 0xE4: SET 4, 'H' # SET 4,H 0xE5: SET 4, 'L' # SET 4,L 0xE6: SET 4, '(HL)' # SET 4,(HL) 0xE7: SET 4, 'A' # SET 4,A 0xE8: SET 5, 'B' # SET 5,B 0xE9: SET 5, 'C' # SET 5,C 0xEA: SET 5, 'D' # SET 5,D 0xEB: SET 5, 'E' # SET 5,E 0xEC: SET 5, 'H' # SET 5,H 0xED: SET 5, 'L' # SET 5,L 0xEE: SET 5, '(HL)' # SET 5,(HL) 0xEF: SET 5, 'A' # SET 5,A 0xF0: SET 6, 'B' # SET 6,B 0xF1: SET 6, 'C' # SET 6,C 0xF2: SET 6, 'D' # SET 6,D 0xF3: SET 6, 'E' # SET 6,E 0xF4: SET 6, 'H' # SET 6,H 0xF5: SET 6, 'L' # SET 6,L 0xF6: SET 6, '(HL)' # SET 6,(HL) 0xF7: SET 6, 'A' # SET 6,A 0xF8: SET 7, 'B' # SET 7,B 0xF9: SET 7, 'C' # SET 7,C 0xFA: SET 7, 'D' # SET 7,D 0xFB: SET 7, 'E' # SET 7,E 0xFC: SET 7, 'H' # SET 7,H 0xFD: SET 7, 'L' # SET 7,L 0xFE: SET 7, '(HL)' # SET 7,(HL) 0xFF: SET 7, 'A' # SET 7,A 0x100: 'cb' } # Generate the opcode runner lookup table for either the DDCB or FDCB set generateddfdcbOpcodeSet = (prefix) -> if prefix == 'DDCB' rp = rpIX rh = rIXH rl = rIXL rpn = 'IX' rhn = 'IXH' rln = 'IXL' else # prefix == 'FDCB' rp = rpIY rh = rIYH rl = rIYL rpn = 'IY' rhn = 'IYH' rln = 'IYL' return { 0x00: LDSHIFTOP('B', RLC, rp) # LD B,RLC (REGISTER+dd) 0x01: LDSHIFTOP('C', RLC, rp) # LD C,RLC (REGISTER+dd) 0x02: LDSHIFTOP('D', RLC, rp) # LD D,RLC (REGISTER+dd) 0x03: LDSHIFTOP('E', RLC, rp) # LD E,RLC (REGISTER+dd) 0x04: LDSHIFTOP('H', RLC, rp) # LD H,RLC (REGISTER+dd) 0x05: LDSHIFTOP('L', RLC, rp) # LD L,RLC (REGISTER+dd) 0x06: RLC "(#{rpn}+nn)" # RLC (IX+nn) 0x07: LDSHIFTOP('A', RLC, rp) # LD A,RLC (REGISTER+dd) 0x08: LDSHIFTOP('B', RRC, rp) # LD B,RRC (REGISTER+dd) 0x09: LDSHIFTOP('C', RRC, rp) # LD C,RRC (REGISTER+dd) 0x0A: LDSHIFTOP('D', RRC, rp) # LD D,RRC (REGISTER+dd) 0x0B: LDSHIFTOP('E', RRC, rp) # LD E,RRC (REGISTER+dd) 0x0C: LDSHIFTOP('H', RRC, rp) # LD H,RRC (REGISTER+dd) 0x0D: LDSHIFTOP('L', RRC, rp) # LD L,RRC (REGISTER+dd) 0x0E: RRC "(#{rpn}+nn)" # RRC (IX+nn) 0x0F: LDSHIFTOP('A', RRC, rp) # LD A,RRC (REGISTER+dd) 0x10: LDSHIFTOP('B', RL, rp) # LD B,RL (REGISTER+dd) 0x11: LDSHIFTOP('C', RL, rp) # LD C,RL (REGISTER+dd) 0x12: LDSHIFTOP('D', RL, rp) # LD D,RL (REGISTER+dd) 0x13: LDSHIFTOP('E', RL, rp) # LD E,RL (REGISTER+dd) 0x14: LDSHIFTOP('H', RL, rp) # LD H,RL (REGISTER+dd) 0x15: LDSHIFTOP('L', RL, rp) # LD L,RL (REGISTER+dd) 0x16: RL "(#{rpn}+nn)" # RL (IX+nn) 0x17: LDSHIFTOP('A', RL, rp) # LD A,RL (REGISTER+dd) 0x18: LDSHIFTOP('B', RR, rp) # LD B,RR (REGISTER+dd) 0x19: LDSHIFTOP('C', RR, rp) # LD C,RR (REGISTER+dd) 0x1A: LDSHIFTOP('D', RR, rp) # LD D,RR (REGISTER+dd) 0x1B: LDSHIFTOP('E', RR, rp) # LD E,RR (REGISTER+dd) 0x1C: LDSHIFTOP('H', RR, rp) # LD H,RR (REGISTER+dd) 0x1D: LDSHIFTOP('L', RR, rp) # LD L,RR (REGISTER+dd) 0x1E: RR "(#{rpn}+nn)" # RR (IX+nn) 0x1F: LDSHIFTOP('A', RR, rp) # LD A,RR (REGISTER+dd) 0x20: LDSHIFTOP('B', SLA, rp) # LD B,SLA (REGISTER+dd) 0x21: LDSHIFTOP('C', SLA, rp) # LD C,SLA (REGISTER+dd) 0x22: LDSHIFTOP('D', SLA, rp) # LD D,SLA (REGISTER+dd) 0x23: LDSHIFTOP('E', SLA, rp) # LD E,SLA (REGISTER+dd) 0x24: LDSHIFTOP('H', SLA, rp) # LD H,SLA (REGISTER+dd) 0x25: LDSHIFTOP('L', SLA, rp) # LD L,SLA (REGISTER+dd) 0x26: SLA "(#{rpn}+nn)" # SLA (IX+nn) 0x27: LDSHIFTOP('A', SLA, rp) # LD A,SLA (REGISTER+dd) 0x28: LDSHIFTOP('B', SRA, rp) # LD B,SRA (REGISTER+dd) 0x29: LDSHIFTOP('C', SRA, rp) # LD C,SRA (REGISTER+dd) 0x2A: LDSHIFTOP('D', SRA, rp) # LD D,SRA (REGISTER+dd) 0x2B: LDSHIFTOP('E', SRA, rp) # LD E,SRA (REGISTER+dd) 0x2C: LDSHIFTOP('H', SRA, rp) # LD H,SRA (REGISTER+dd) 0x2D: LDSHIFTOP('L', SRA, rp) # LD L,SRA (REGISTER+dd) 0x2E: SRA "(#{rpn}+nn)" # SRA (IX+nn) 0x2F: LDSHIFTOP('A', SRA, rp) # LD A,SRA (REGISTER+dd) 0x30: LDSHIFTOP('B', SLL, rp) # LD B,SLL (REGISTER+dd) 0x31: LDSHIFTOP('C', SLL, rp) # LD C,SLL (REGISTER+dd) 0x32: LDSHIFTOP('D', SLL, rp) # LD D,SLL (REGISTER+dd) 0x33: LDSHIFTOP('E', SLL, rp) # LD E,SLL (REGISTER+dd) 0x34: LDSHIFTOP('H', SLL, rp) # LD H,SLL (REGISTER+dd) 0x35: LDSHIFTOP('L', SLL, rp) # LD L,SLL (REGISTER+dd) 0x36: SLL "(#{rpn}+nn)" # SLL (IX+nn) 0x37: LDSHIFTOP('A', SLL, rp) # LD A,SLL (REGISTER+dd) 0x38: LDSHIFTOP('B', SRL, rp) # LD B,SRL (REGISTER+dd) 0x39: LDSHIFTOP('C', SRL, rp) # LD C,SRL (REGISTER+dd) 0x3A: LDSHIFTOP('D', SRL, rp) # LD D,SRL (REGISTER+dd) 0x3B: LDSHIFTOP('E', SRL, rp) # LD E,SRL (REGISTER+dd) 0x3C: LDSHIFTOP('H', SRL, rp) # LD H,SRL (REGISTER+dd) 0x3D: LDSHIFTOP('L', SRL, rp) # LD L,SRL (REGISTER+dd) 0x3E: SRL "(#{rpn}+nn)" # SRL (IX+nn) 0x3F: LDSHIFTOP('A', SRL, rp) # LD A,SRL (REGISTER+dd) 0x40: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x41: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x42: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x43: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x44: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x45: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x46: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x47: BIT_N_iRRpNNi(0, rp) # BIT 0,(IX+nn) 0x48: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x49: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4A: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4B: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4C: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4D: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4E: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x4F: BIT_N_iRRpNNi(1, rp) # BIT 1,(IX+nn) 0x50: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x51: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x52: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x53: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x54: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x55: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x56: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x57: BIT_N_iRRpNNi(2, rp) # BIT 2,(IX+nn) 0x58: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x59: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5A: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5B: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5C: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5D: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5E: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x5F: BIT_N_iRRpNNi(3, rp) # BIT 3,(IX+nn) 0x60: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x61: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x62: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x63: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x64: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x65: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x66: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x67: BIT_N_iRRpNNi(4, rp) # BIT 4,(IX+nn) 0x68: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x69: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6A: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6B: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6C: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6D: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6E: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x6F: BIT_N_iRRpNNi(5, rp) # BIT 5,(IX+nn) 0x70: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x71: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x72: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x73: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x74: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x75: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x76: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x77: BIT_N_iRRpNNi(6, rp) # BIT 6,(IX+nn) 0x78: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x79: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7A: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7B: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7C: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7D: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7E: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x7F: BIT_N_iRRpNNi(7, rp) # BIT 7,(IX+nn) 0x80: LDBITOP('B', RES, 0, rp) # LD B,RES 0,(IX+dd) 0x81: LDBITOP('C', RES, 0, rp) # LD C,RES 0,(IX+dd) 0x82: LDBITOP('D', RES, 0, rp) # LD D,RES 0,(IX+dd) 0x83: LDBITOP('E', RES, 0, rp) # LD E,RES 0,(IX+dd) 0x84: LDBITOP('H', RES, 0, rp) # LD H,RES 0,(IX+dd) 0x85: LDBITOP('L', RES, 0, rp) # LD L,RES 0,(IX+dd) 0x86: RES 0, "(#{rpn}+nn)" # RES 0,(IX+nn) 0x87: LDBITOP('A', RES, 0, rp) # LD A,RES 0,(IX+dd) 0x88: LDBITOP('B', RES, 1, rp) # LD B,RES 1,(IX+dd) 0x89: LDBITOP('C', RES, 1, rp) # LD C,RES 1,(IX+dd) 0x8A: LDBITOP('D', RES, 1, rp) # LD D,RES 1,(IX+dd) 0x8B: LDBITOP('E', RES, 1, rp) # LD E,RES 1,(IX+dd) 0x8C: LDBITOP('H', RES, 1, rp) # LD H,RES 1,(IX+dd) 0x8D: LDBITOP('L', RES, 1, rp) # LD L,RES 1,(IX+dd) 0x8E: RES 1, "(#{rpn}+nn)" # RES 1,(IX+nn) 0x8F: LDBITOP('A', RES, 1, rp) # LD A,RES 1,(IX+dd) 0x90: LDBITOP('B', RES, 2, rp) # LD B,RES 2,(IX+dd) 0x91: LDBITOP('C', RES, 2, rp) # LD C,RES 2,(IX+dd) 0x92: LDBITOP('D', RES, 2, rp) # LD D,RES 2,(IX+dd) 0x93: LDBITOP('E', RES, 2, rp) # LD E,RES 2,(IX+dd) 0x94: LDBITOP('H', RES, 2, rp) # LD H,RES 2,(IX+dd) 0x95: LDBITOP('L', RES, 2, rp) # LD L,RES 2,(IX+dd) 0x96: RES 2, "(#{rpn}+nn)" # RES 2,(IX+nn) 0x97: LDBITOP('A', RES, 2, rp) # LD A,RES 2,(IX+dd) 0x98: LDBITOP('B', RES, 3, rp) # LD B,RES 3,(IX+dd) 0x99: LDBITOP('C', RES, 3, rp) # LD C,RES 3,(IX+dd) 0x9A: LDBITOP('D', RES, 3, rp) # LD D,RES 3,(IX+dd) 0x9B: LDBITOP('E', RES, 3, rp) # LD E,RES 3,(IX+dd) 0x9C: LDBITOP('H', RES, 3, rp) # LD H,RES 3,(IX+dd) 0x9D: LDBITOP('L', RES, 3, rp) # LD L,RES 3,(IX+dd) 0x9E: RES 3, "(#{rpn}+nn)" # RES 3,(IX+nn) 0x9F: LDBITOP('A', RES, 3, rp) # LD A,RES 3,(IX+dd) 0xA0: LDBITOP('B', RES, 4, rp) # LD B,RES 4,(IX+dd) 0xA1: LDBITOP('C', RES, 4, rp) # LD C,RES 4,(IX+dd) 0xA2: LDBITOP('D', RES, 4, rp) # LD D,RES 4,(IX+dd) 0xA3: LDBITOP('E', RES, 4, rp) # LD E,RES 4,(IX+dd) 0xA4: LDBITOP('H', RES, 4, rp) # LD H,RES 4,(IX+dd) 0xA5: LDBITOP('L', RES, 4, rp) # LD L,RES 4,(IX+dd) 0xA6: RES 4, "(#{rpn}+nn)" # RES 4,(IX+nn) 0xA7: LDBITOP('A', RES, 4, rp) # LD A,RES 4,(IX+dd) 0xA8: LDBITOP('B', RES, 5, rp) # LD B,RES 5,(IX+dd) 0xA9: LDBITOP('C', RES, 5, rp) # LD C,RES 5,(IX+dd) 0xAA: LDBITOP('D', RES, 5, rp) # LD D,RES 5,(IX+dd) 0xAB: LDBITOP('E', RES, 5, rp) # LD E,RES 5,(IX+dd) 0xAC: LDBITOP('H', RES, 5, rp) # LD H,RES 5,(IX+dd) 0xAD: LDBITOP('L', RES, 5, rp) # LD L,RES 5,(IX+dd) 0xAE: RES 5, "(#{rpn}+nn)" # RES 5,(IX+nn) 0xAF: LDBITOP('A', RES, 5, rp) # LD A,RES 5,(IX+dd) 0xB0: LDBITOP('B', RES, 6, rp) # LD B,RES 6,(IX+dd) 0xB1: LDBITOP('C', RES, 6, rp) # LD C,RES 6,(IX+dd) 0xB2: LDBITOP('D', RES, 6, rp) # LD D,RES 6,(IX+dd) 0xB3: LDBITOP('E', RES, 6, rp) # LD E,RES 6,(IX+dd) 0xB4: LDBITOP('H', RES, 6, rp) # LD H,RES 6,(IX+dd) 0xB5: LDBITOP('L', RES, 6, rp) # LD L,RES 6,(IX+dd) 0xB6: RES 6, "(#{rpn}+nn)" # RES 6,(IX+nn) 0xB7: LDBITOP('A', RES, 6, rp) # LD A,RES 6,(IX+dd) 0xB8: LDBITOP('B', RES, 7, rp) # LD B,RES 7,(IX+dd) 0xB9: LDBITOP('C', RES, 7, rp) # LD C,RES 7,(IX+dd) 0xBA: LDBITOP('D', RES, 7, rp) # LD D,RES 7,(IX+dd) 0xBB: LDBITOP('E', RES, 7, rp) # LD E,RES 7,(IX+dd) 0xBC: LDBITOP('H', RES, 7, rp) # LD H,RES 7,(IX+dd) 0xBD: LDBITOP('L', RES, 7, rp) # LD L,RES 7,(IX+dd) 0xBE: RES 7, "(#{rpn}+nn)" # RES 7,(IX+nn) 0xBF: LDBITOP('A', RES, 7, rp) # LD A,RES 7,(IX+dd) 0xC0: LDBITOP('B', SET, 0, rp) # LD B,SET 0,(IX+dd) 0xC1: LDBITOP('C', SET, 0, rp) # LD C,SET 0,(IX+dd) 0xC2: LDBITOP('D', SET, 0, rp) # LD D,SET 0,(IX+dd) 0xC3: LDBITOP('E', SET, 0, rp) # LD E,SET 0,(IX+dd) 0xC4: LDBITOP('H', SET, 0, rp) # LD H,SET 0,(IX+dd) 0xC5: LDBITOP('L', SET, 0, rp) # LD L,SET 0,(IX+dd) 0xC6: SET 0, "(#{rpn}+nn)" # SET 0,(IX+nn) 0xC7: LDBITOP('A', SET, 0, rp) # LD A,SET 0,(IX+dd) 0xC8: LDBITOP('B', SET, 1, rp) # LD B,SET 1,(IX+dd) 0xC9: LDBITOP('C', SET, 1, rp) # LD C,SET 1,(IX+dd) 0xCA: LDBITOP('D', SET, 1, rp) # LD D,SET 1,(IX+dd) 0xCB: LDBITOP('E', SET, 1, rp) # LD E,SET 1,(IX+dd) 0xCC: LDBITOP('H', SET, 1, rp) # LD H,SET 1,(IX+dd) 0xCD: LDBITOP('L', SET, 1, rp) # LD L,SET 1,(IX+dd) 0xCE: SET 1, "(#{rpn}+nn)" # SET 1,(IX+nn) 0xCF: LDBITOP('A', SET, 1, rp) # LD A,SET 1,(IX+dd) 0xD0: LDBITOP('B', SET, 2, rp) # LD B,SET 2,(IX+dd) 0xD1: LDBITOP('C', SET, 2, rp) # LD C,SET 2,(IX+dd) 0xD2: LDBITOP('D', SET, 2, rp) # LD D,SET 2,(IX+dd) 0xD3: LDBITOP('E', SET, 2, rp) # LD E,SET 2,(IX+dd) 0xD4: LDBITOP('H', SET, 2, rp) # LD H,SET 2,(IX+dd) 0xD5: LDBITOP('L', SET, 2, rp) # LD L,SET 2,(IX+dd) 0xD6: SET 2, "(#{rpn}+nn)" # SET 2,(IX+nn) 0xD7: LDBITOP('A', SET, 2, rp) # LD A,SET 2,(IX+dd) 0xD8: LDBITOP('B', SET, 3, rp) # LD B,SET 3,(IX+dd) 0xD9: LDBITOP('C', SET, 3, rp) # LD C,SET 3,(IX+dd) 0xDA: LDBITOP('D', SET, 3, rp) # LD D,SET 3,(IX+dd) 0xDB: LDBITOP('E', SET, 3, rp) # LD E,SET 3,(IX+dd) 0xDC: LDBITOP('H', SET, 3, rp) # LD H,SET 3,(IX+dd) 0xDD: LDBITOP('L', SET, 3, rp) # LD L,SET 3,(IX+dd) 0xDE: SET 3, "(#{rpn}+nn)" # SET 3,(IX+nn) 0xDF: LDBITOP('A', SET, 3, rp) # LD A,SET 3,(IX+dd) 0xE0: LDBITOP('B', SET, 4, rp) # LD B,SET 4,(IX+dd) 0xE1: LDBITOP('C', SET, 4, rp) # LD C,SET 4,(IX+dd) 0xE2: LDBITOP('D', SET, 4, rp) # LD D,SET 4,(IX+dd) 0xE3: LDBITOP('E', SET, 4, rp) # LD E,SET 4,(IX+dd) 0xE4: LDBITOP('H', SET, 4, rp) # LD H,SET 4,(IX+dd) 0xE5: LDBITOP('L', SET, 4, rp) # LD L,SET 4,(IX+dd) 0xE6: SET 4, "(#{rpn}+nn)" # SET 4,(IX+nn) 0xE7: LDBITOP('A', SET, 4, rp) # LD A,SET 4,(IX+dd) 0xE8: LDBITOP('B', SET, 5, rp) # LD B,SET 5,(IX+dd) 0xE9: LDBITOP('C', SET, 5, rp) # LD C,SET 5,(IX+dd) 0xEA: LDBITOP('D', SET, 5, rp) # LD D,SET 5,(IX+dd) 0xEB: LDBITOP('E', SET, 5, rp) # LD E,SET 5,(IX+dd) 0xEC: LDBITOP('H', SET, 5, rp) # LD H,SET 5,(IX+dd) 0xED: LDBITOP('L', SET, 5, rp) # LD L,SET 5,(IX+dd) 0xEE: SET 5, "(#{rpn}+nn)" # SET 5,(IX+nn) 0xEF: LDBITOP('A', SET, 5, rp) # LD A,SET 5,(IX+dd) 0xF0: LDBITOP('B', SET, 6, rp) # LD B,SET 6,(IX+dd) 0xF1: LDBITOP('C', SET, 6, rp) # LD C,SET 6,(IX+dd) 0xF2: LDBITOP('D', SET, 6, rp) # LD D,SET 6,(IX+dd) 0xF3: LDBITOP('E', SET, 6, rp) # LD E,SET 6,(IX+dd) 0xF4: LDBITOP('H', SET, 6, rp) # LD H,SET 6,(IX+dd) 0xF5: LDBITOP('L', SET, 6, rp) # LD L,SET 6,(IX+dd) 0xF6: SET 6, "(#{rpn}+nn)" # SET 6,(IX+nn) 0xF7: LDBITOP('A', SET, 6, rp) # LD A,SET 6,(IX+dd) 0xF8: LDBITOP('B', SET, 7, rp) # LD B,SET 7,(IX+dd) 0xF9: LDBITOP('C', SET, 7, rp) # LD C,SET 7,(IX+dd) 0xFA: LDBITOP('D', SET, 7, rp) # LD D,SET 7,(IX+dd) 0xFB: LDBITOP('E', SET, 7, rp) # LD E,SET 7,(IX+dd) 0xFC: LDBITOP('H', SET, 7, rp) # LD H,SET 7,(IX+dd) 0xFD: LDBITOP('L', SET, 7, rp) # LD L,SET 7,(IX+dd) 0xFE: SET 7, "(#{rpn}+nn)" # SET 7,(IX+nn) 0xFF: LDBITOP('A', SET, 7, rp) # LD A,SET 7,(IX+dd) 0x100: 'ddcb' } OPCODE_RUN_STRINGS_DDCB = generateddfdcbOpcodeSet('DDCB') OPCODE_RUN_STRINGS_FDCB = generateddfdcbOpcodeSet('FDCB') # Generate the opcode runner lookup table for either the DD or FD set, acting on the # specified register pair (IX or IY) generateddfdOpcodeSet = (prefix) -> if prefix == 'DD' rp = rpIX rh = rIXH rl = rIXL rpn = 'IX' rhn = 'IXH' rln = 'IXL' else # prefix == 'FD' rp = rpIY rh = rIYH rl = rIYL rpn = 'IY' rhn = 'IYH' rln = 'IYL' return { 0x09: ADD_RR_RR(rp, rpBC) # ADD IX,BC 0x19: ADD_RR_RR(rp, rpDE) # ADD IX,DE 0x21: LD_RR_NN(rp) # LD IX,nnnn 0x22: LD_iNNi_RR(rp) # LD (nnnn),IX 0x23: INC_RR(rp) # INC IX 0x24: INC rhn # INC IXh 0x25: DEC rhn # DEC IXh 0x26: LD_R_N(rh) # LD IXh, nn 0x29: ADD_RR_RR(rp, rp) # ADD IX,IX 0x2A: LD_RR_iNNi(rp) # LD IX,(nnnn) 0x2B: DEC_RR(rp) # DEC IX 0x2C: INC rln # INC IXl 0x2D: DEC rln # DEC IXl 0x2E: LD_R_N(rl) # LD IXl, nn 0x34: INC "(#{rpn}+nn)" # INC (IX+nn) 0x35: DEC "(#{rpn}+nn)" # DEC (IX+nn) 0x36: LD_iRRpNNi_N(rp) # LD (IX+nn),nn 0x39: ADD_RR_RR(rp, rpSP) # ADD IX,SP 0x44: LD_R_R(rB, rh) # LD B,IXh 0x45: LD_R_R(rB, rl) # LD B,IXl 0x46: LD_R_iRRpNNi(rB, rp) # LD B,(IX+nn) 0x4C: LD_R_R(rC, rh) # LD C,IXh 0x4D: LD_R_R(rC, rl) # LD C,IXl 0x4E: LD_R_iRRpNNi(rC, rp) # LD C,(IX+nn) 0x54: LD_R_R(rD, rh) # LD D,IXh 0x55: LD_R_R(rD, rl) # LD D,IXl 0x56: LD_R_iRRpNNi(rD, rp) # LD D,(IX+nn) 0x5C: LD_R_R(rE, rh) # LD E,IXh 0x5D: LD_R_R(rE, rl) # LD E,IXl 0x5E: LD_R_iRRpNNi(rE, rp) # LD E,(IX+nn) 0x60: LD_R_R(rh, rB) # LD IXh,B 0x61: LD_R_R(rh, rC) # LD IXh,C 0x62: LD_R_R(rh, rD) # LD IXh,D 0x63: LD_R_R(rh, rE) # LD IXh,E 0x64: LD_R_R(rh, rh) # LD IXh,IXh 0x65: LD_R_R(rh, rl) # LD IXh,IXl 0x66: LD_R_iRRpNNi(rH, rp) # LD H,(IX+nn) 0x67: LD_R_R(rh, rA) # LD IXh,A 0x68: LD_R_R(rl, rB) # LD IXl,B 0x69: LD_R_R(rl, rC) # LD IXl,C 0x6A: LD_R_R(rl, rD) # LD IXl,D 0x6B: LD_R_R(rl, rE) # LD IXl,E 0x6C: LD_R_R(rl, rh) # LD IXl,IXh 0x6D: LD_R_R(rl, rl) # LD IXl,IXl 0x6E: LD_R_iRRpNNi(rL, rp) # LD L,(IX+nn) 0x6F: LD_R_R(rl, rA) # LD IXl,A 0x70: LD_iRRpNNi_R(rp, rB) # LD (IX+nn),B 0x71: LD_iRRpNNi_R(rp, rC) # LD (IX+nn),C 0x72: LD_iRRpNNi_R(rp, rD) # LD (IX+nn),D 0x73: LD_iRRpNNi_R(rp, rE) # LD (IX+nn),E 0x74: LD_iRRpNNi_R(rp, rH) # LD (IX+nn),H 0x75: LD_iRRpNNi_R(rp, rL) # LD (IX+nn),L 0x77: LD_iRRpNNi_R(rp, rA) # LD (IX+nn),A 0x7C: LD_R_R(rA, rh) # LD A,IXh 0x7D: LD_R_R(rA, rl) # LD A,IXl 0x7E: LD_R_iRRpNNi(rA, rp) # LD A,(IX+nn) 0x84: ADD_A rhn # ADD A,IXh 0x85: ADD_A rln # ADD A,IXl 0x86: ADD_A "(#{rpn}+nn)" # ADD A,(IX+nn) 0x8C: ADC_A rhn # ADC A,IXh 0x8D: ADC_A rln # ADC A,IXl 0x8E: ADC_A "(#{rpn}+nn)" # ADC A,(IX+nn) 0x94: SUB_A rhn # SUB IXh 0x95: SUB_A rln # SUB IXl 0x96: SUB_A "(#{rpn}+nn)" # SUB A,(IX+dd) 0x9C: SBC_A rhn # SBC IXh 0x9D: SBC_A rln # SBC IXl 0x9E: SBC_A "(#{rpn}+nn)" # SBC A,(IX+dd) 0xA4: AND_A rhn # AND IXh 0xA5: AND_A rln # AND IXl 0xA6: AND_A "(#{rpn}+nn)" # AND (IX+dd) 0xAC: XOR_A rhn # XOR IXh 0xAD: XOR_A rln # XOR IXl 0xAE: XOR_A "(#{rpn}+nn)" # XOR A,(IX+dd) 0xB4: OR_A rhn # OR IXh 0xB5: OR_A rln # OR IXl 0xB6: OR_A "(#{rpn}+nn)" # OR A,(IX+dd) 0xBC: CP_A rhn # CP IXh 0xBD: CP_A rln # CP IXl 0xBE: CP_A "(#{rpn}+nn)" # CP (IX+dd) 0xCB: SHIFT(prefix + 'CB') # shift code 0xDD: SHIFT('DD') # shift code 0xE1: POP_RR(rp) # POP IX 0xE3: EX_iSPi_RR(rp) # EX (SP),IX 0xE5: PUSH_RR(rp) # PUSH IX 0xE9: JP_RR(rp) # JP (IX) 0xF9: LD_RR_RR(rpSP, rp) # LD SP,IX 0xFD: SHIFT('FD') # shift code 0x100: 'dd' } OPCODE_RUN_STRINGS_DD = generateddfdOpcodeSet('DD') OPCODE_RUN_STRINGS_ED = { 0x40: IN_R_iCi(rB) # IN B,(C) 0x41: OUT_iCi_R(rB) # OUT (C),B 0x42: SBC_HL_RR(rpBC) # SBC HL,BC 0x43: LD_iNNi_RR(rpBC) # LD (nnnn),BC 0x44: NEG() # NEG 0x45: RETN() # RETN 0x46: IM(0) # IM 0 0x47: LD_R_R(rI, rA) # LD I,A 0x48: IN_R_iCi(rC) # IN C,(C) 0x49: OUT_iCi_R(rC) # OUT (C),C 0x4A: ADC_HL_RR(rpBC) # ADC HL,BC 0x4B: LD_RR_iNNi(rpBC) # LD BC,(nnnn) 0x4C: NEG() # NEG 0x4D: RETN() # RETN 0x4E: IM(0) # IM 0 0x4F: LD_R_R(rR, rA) # LD R,A 0x50: IN_R_iCi(rD) # IN D,(C) 0x51: OUT_iCi_R(rD) # OUT (C),D 0x52: SBC_HL_RR(rpDE) # SBC HL,DE 0x53: LD_iNNi_RR(rpDE) # LD (nnnn),DE 0x54: NEG() # NEG 0x55: RETN() # RETN 0x56: IM(1) # IM 1 0x57: LD_R_R(rA, rI) # LD A,I 0x58: IN_R_iCi(rE) # IN E,(C) 0x59: OUT_iCi_R(rE) # OUT (C),E 0x5A: ADC_HL_RR(rpDE) # ADC HL,DE 0x5B: LD_RR_iNNi(rpDE) # LD DE,(nnnn) 0x5C: NEG() # NEG 0x5D: RETN() # RETN 0x5E: IM(2) # IM 2 0x5F: LD_R_R(rA, rR) # LD A,R 0x60: IN_R_iCi(rH) # IN H,(C) 0x61: OUT_iCi_R(rH) # OUT (C),H 0x62: SBC_HL_RR(rpHL) # SBC HL,HL 0x63: LD_iNNi_RR(rpHL) # LD (nnnn),HL 0x64: NEG() # NEG 0x65: RETN() # RETN 0x66: IM(0) # IM 0 0x67: RRD() # RRD 0x68: IN_R_iCi(rL) # IN L,(C) 0x69: OUT_iCi_R(rL) # OUT (C),L 0x6A: ADC_HL_RR(rpHL) # ADC HL,HL 0x6B: LD_RR_iNNi(rpHL, true) # LD HL,(nnnn) 0x6C: NEG() # NEG 0x6D: RETN() # RETN 0x6E: IM(0) # IM 0 0x6F: RLD() # RLD 0x70: IN_F_iCi() # IN F,(C) 0x71: OUT_iCi_0() # OUT (C),0 0x72: SBC_HL_RR(rpSP) # SBC HL,SP 0x73: LD_iNNi_RR(rpSP) # LD (nnnn),SP 0x74: NEG() # NEG 0x75: RETN() # RETN 0x76: IM(1) # IM 1 0x78: IN_R_iCi(rA) # IN A,(C) 0x79: OUT_iCi_R(rA) # OUT (C),A 0x7A: ADC_HL_RR(rpSP) # ADC HL,SP 0x7B: LD_RR_iNNi(rpSP) # LD SP,(nnnn) 0x7C: NEG() # NEG 0x7D: RETN() # RETN 0x7E: IM(2) # IM 2 0xA0: LDI() # LDI 0xA1: CPI() # CPI 0xA2: INI() # INI 0xA3: OUTI() # OUTI 0xA8: LDD() # LDD 0xA9: CPD() # CPD 0xAA: IND() # IND 0xAB: OUTD() # OUTD 0xB0: LDIR() # LDIR 0xb1: CPIR() # CPIR 0xB2: INIR() # INIR 0xB3: OTIR() # OTIR 0xB8: LDDR() # LDDR 0xb9: CPDR() # CPDR 0xBA: INDR() # INDR 0xBB: OTDR() # OTDR 0x100: 'ed' } OPCODE_RUN_STRINGS_FD = generateddfdOpcodeSet('FD') OPCODE_RUN_STRINGS = { 0x00: NOP() # NOP 0x01: LD_RR_NN(rpBC) # LD BC,nnnn 0x02: LD_iRRi_R(rpBC, rA) # LD (BC),A 0x03: INC_RR(rpBC) # INC BC 0x04: INC "B" # INC B 0x05: DEC "B" # DEC B 0x06: LD_R_N(rB) # LD B,nn 0x07: RLCA() # RLCA 0x08: EX_RR_RR(rpAF, rpAF_) # EX AF,AF' 0x09: ADD_RR_RR(rpHL, rpBC) # ADD HL,BC 0x0A: LD_R_iRRi(rA, rpBC) # LD A,(BC) 0x0B: DEC_RR(rpBC) # DEC BC 0x0C: INC "C" # INC C 0x0D: DEC "C" # DEC C 0x0E: LD_R_N(rC) # LD C,nn 0x0F: RRCA() # RRCA 0x10: DJNZ_N() # DJNZ nn 0x11: LD_RR_NN(rpDE) # LD DE,nnnn 0x12: LD_iRRi_R(rpDE, rA) # LD (DE),A 0x13: INC_RR(rpDE) # INC DE 0x14: INC "D" # INC D 0x15: DEC "D" # DEC D 0x16: LD_R_N(rD) # LD D,nn 0x17: RLA() # RLA 0x18: JR_N() # JR nn 0x19: ADD_RR_RR(rpHL, rpDE) # ADD HL,DE 0x1A: LD_R_iRRi(rA, rpDE) # LD A,(DE) 0x1B: DEC_RR(rpDE) # DEC DE 0x1C: INC "E" # INC E 0x1D: DEC "E" # DEC E 0x1E: LD_R_N(rE) # LD E,nn 0x1F: RRA() # RRA 0x20: JR_C_N(FLAG_Z, false) # JR NZ,nn 0x21: LD_RR_NN(rpHL) # LD HL,nnnn 0x22: LD_iNNi_RR(rpHL) # LD (nnnn),HL 0x23: INC_RR(rpHL) # INC HL 0x24: INC "H" # INC H 0x25: DEC "H" # DEC H 0x26: LD_R_N(rH) # LD H,nn 0x27: DAA() # DAA 0x28: JR_C_N(FLAG_Z, true) # JR Z,nn 0x29: ADD_RR_RR(rpHL, rpHL) # ADD HL,HL 0x2A: LD_RR_iNNi(rpHL) # LD HL,(nnnn) 0x2B: DEC_RR(rpHL) # DEC HL 0x2C: INC "L" # INC L 0x2D: DEC "L" # DEC L 0x2E: LD_R_N(rL) # LD L,nn 0x2F: CPL() # CPL 0x30: JR_C_N(FLAG_C, false) # JR NC,nn 0x31: LD_RR_NN(rpSP) # LD SP,nnnn 0x32: LD_iNNi_A() # LD (nnnn),a 0x33: INC_RR(rpSP) # INC SP 0x34: INC "(HL)" # INC (HL) 0x35: DEC "(HL)" # DEC (HL) 0x36: LD_iRRi_N(rpHL) # LD (HL),nn 0x37: SCF() # SCF 0x38: JR_C_N(FLAG_C, true) # JR C,nn 0x39: ADD_RR_RR(rpHL, rpSP) # ADD HL,SP 0x3A: LD_A_iNNi() # LD A,(nnnn) 0x3B: DEC_RR(rpSP) # DEC SP 0x3C: INC "A" # INC A 0x3D: DEC "A" # DEC A 0x3E: LD_R_N(rA) # LD A,nn 0x3F: CCF() # CCF 0x40: LD_R_R(rB, rB) # LD B,B 0x41: LD_R_R(rB, rC) # LD B,C 0x42: LD_R_R(rB, rD) # LD B,D 0x43: LD_R_R(rB, rE) # LD B,E 0x44: LD_R_R(rB, rH) # LD B,H 0x45: LD_R_R(rB, rL) # LD B,L 0x46: LD_R_iRRi(rB, rpHL) # LD B,(HL) 0x47: LD_R_R(rB, rA) # LD B,A 0x48: LD_R_R(rC, rB) # LD C,B 0x49: LD_R_R(rC, rC) # LD C,C 0x4a: LD_R_R(rC, rD) # LD C,D 0x4b: LD_R_R(rC, rE) # LD C,E 0x4c: LD_R_R(rC, rH) # LD C,H 0x4d: LD_R_R(rC, rL) # LD C,L 0x4e: LD_R_iRRi(rC, rpHL) # LD C,(HL) 0x4f: LD_R_R(rC, rA) # LD C,A 0x50: LD_R_R(rD, rB) # LD D,B 0x51: LD_R_R(rD, rC) # LD D,C 0x52: LD_R_R(rD, rD) # LD D,D 0x53: LD_R_R(rD, rE) # LD D,E 0x54: LD_R_R(rD, rH) # LD D,H 0x55: LD_R_R(rD, rL) # LD D,L 0x56: LD_R_iRRi(rD, rpHL) # LD D,(HL) 0x57: LD_R_R(rD, rA) # LD D,A 0x58: LD_R_R(rE, rB) # LD E,B 0x59: LD_R_R(rE, rC) # LD E,C 0x5a: LD_R_R(rE, rD) # LD E,D 0x5b: LD_R_R(rE, rE) # LD E,E 0x5c: LD_R_R(rE, rH) # LD E,H 0x5d: LD_R_R(rE, rL) # LD E,L 0x5e: LD_R_iRRi(rE, rpHL) # LD E,(HL) 0x5f: LD_R_R(rE, rA) # LD E,A 0x60: LD_R_R(rH, rB) # LD H,B 0x61: LD_R_R(rH, rC) # LD H,C 0x62: LD_R_R(rH, rD) # LD H,D 0x63: LD_R_R(rH, rE) # LD H,E 0x64: LD_R_R(rH, rH) # LD H,H 0x65: LD_R_R(rH, rL) # LD H,L 0x66: LD_R_iRRi(rH, rpHL) # LD H,(HL) 0x67: LD_R_R(rH, rA) # LD H,A 0x68: LD_R_R(rL, rB) # LD L,B 0x69: LD_R_R(rL, rC) # LD L,C 0x6a: LD_R_R(rL, rD) # LD L,D 0x6b: LD_R_R(rL, rE) # LD L,E 0x6c: LD_R_R(rL, rH) # LD L,H 0x6d: LD_R_R(rL, rL) # LD L,L 0x6e: LD_R_iRRi(rL, rpHL) # LD L,(HL) 0x6f: LD_R_R(rL, rA) # LD L,A 0x70: LD_iRRi_R(rpHL, rB) # LD (HL),B 0x71: LD_iRRi_R(rpHL, rC) # LD (HL),C 0x72: LD_iRRi_R(rpHL, rD) # LD (HL),D 0x73: LD_iRRi_R(rpHL, rE) # LD (HL),E 0x74: LD_iRRi_R(rpHL, rH) # LD (HL),H 0x75: LD_iRRi_R(rpHL, rL) # LD (HL),L 0x76: HALT() # HALT 0x77: LD_iRRi_R(rpHL, rA) # LD (HL),A 0x78: LD_R_R(rA, rB) # LD A,B 0x79: LD_R_R(rA, rC) # LD A,C 0x7a: LD_R_R(rA, rD) # LD A,D 0x7b: LD_R_R(rA, rE) # LD A,E 0x7c: LD_R_R(rA, rH) # LD A,H 0x7d: LD_R_R(rA, rL) # LD A,L 0x7e: LD_R_iRRi(rA, rpHL) # LD A,(HL) 0x7f: LD_R_R(rA, rA) # LD A,A 0x80: ADD_A "B" # ADD A,B 0x81: ADD_A "C" # ADD A,C 0x82: ADD_A "D" # ADD A,D 0x83: ADD_A "E" # ADD A,E 0x84: ADD_A "H" # ADD A,H 0x85: ADD_A "L" # ADD A,L 0x86: ADD_A "(HL)" # ADD A,(HL) 0x87: ADD_A "A" # ADD A,A 0x88: ADC_A "B" # ADC A,B 0x89: ADC_A "C" # ADC A,C 0x8a: ADC_A "D" # ADC A,D 0x8b: ADC_A "E" # ADC A,E 0x8c: ADC_A "H" # ADC A,H 0x8d: ADC_A "L" # ADC A,L 0x8e: ADC_A "(HL)" # ADC A,(HL) 0x8f: ADC_A "A" # ADC A,A 0x90: SUB_A "B" # SUB A,B 0x91: SUB_A "C" # SUB A,C 0x92: SUB_A "D" # SUB A,D 0x93: SUB_A "E" # SUB A,E 0x94: SUB_A "H" # SUB A,H 0x95: SUB_A "L" # SUB A,L 0x96: SUB_A "(HL)" # SUB A,(HL) 0x97: SUB_A "A" # SUB A,A 0x98: SBC_A "B" # SBC A,B 0x99: SBC_A "C" # SBC A,C 0x9a: SBC_A "D" # SBC A,D 0x9b: SBC_A "E" # SBC A,E 0x9c: SBC_A "H" # SBC A,H 0x9d: SBC_A "L" # SBC A,L 0x9e: SBC_A "(HL)" # SBC A,(HL) 0x9f: SBC_A "A" # SBC A,A 0xa0: AND_A "B" # AND A,B 0xa1: AND_A "C" # AND A,C 0xa2: AND_A "D" # AND A,D 0xa3: AND_A "E" # AND A,E 0xa4: AND_A "H" # AND A,H 0xa5: AND_A "L" # AND A,L 0xa6: AND_A "(HL)" # AND A,(HL) 0xa7: AND_A "A" # AND A,A 0xA8: XOR_A "B" # XOR B 0xA9: XOR_A "C" # XOR C 0xAA: XOR_A "D" # XOR D 0xAB: XOR_A "E" # XOR E 0xAC: XOR_A "H" # XOR H 0xAD: XOR_A "L" # XOR L 0xAE: XOR_A "(HL)" # XOR (HL) 0xAF: XOR_A "A" # XOR A 0xb0: OR_A "B" # OR B 0xb1: OR_A "C" # OR C 0xb2: OR_A "D" # OR D 0xb3: OR_A "E" # OR E 0xb4: OR_A "H" # OR H 0xb5: OR_A "L" # OR L 0xb6: OR_A "(HL)" # OR (HL) 0xb7: OR_A "A" # OR A 0xb8: CP_A "B" # CP B 0xb9: CP_A "C" # CP C 0xba: CP_A "D" # CP D 0xbb: CP_A "E" # CP E 0xbc: CP_A "H" # CP H 0xbd: CP_A "L" # CP L 0xbe: CP_A "(HL)" # CP (HL) 0xbf: CP_A "A" # CP A 0xC0: RET_C(FLAG_Z, false) # RET NZ 0xC1: POP_RR(rpBC) # POP BC 0xC2: JP_C_NN(FLAG_Z, false) # JP NZ,nnnn 0xC3: JP_NN() # JP nnnn 0xC4: CALL_C_NN(FLAG_Z, false) # CALL NZ,nnnn 0xC5: PUSH_RR(rpBC) # PUSH BC 0xC6: ADD_A "nn" # ADD A,nn 0xC7: RST(0x0000) # RST 0x00 0xC8: RET_C(FLAG_Z, true) # RET Z 0xC9: RET() # RET 0xCA: JP_C_NN(FLAG_Z, true) # JP Z,nnnn 0xCB: SHIFT('CB') # shift code 0xCC: CALL_C_NN(FLAG_Z, true) # CALL Z,nnnn 0xCD: CALL_NN() # CALL nnnn 0xCE: ADC_A "nn" # ADC A,nn 0xCF: RST(0x0008) # RST 0x08 0xD0: RET_C(FLAG_C, false) # RET NC 0xD1: POP_RR(rpDE) # POP DE 0xD2: JP_C_NN(FLAG_C, false) # JP NC,nnnn 0xD3: OUT_iNi_A() # OUT (nn),A 0xD4: CALL_C_NN(FLAG_C, false) # CALL NC,nnnn 0xD5: PUSH_RR(rpDE) # PUSH DE 0xD6: SUB_A "nn" # SUB nn 0xD7: RST(0x0010) # RST 0x10 0xD8: RET_C(FLAG_C, true) # RET C 0xD9: EXX() # EXX 0xDA: JP_C_NN(FLAG_C, true) # JP C,nnnn 0xDB: IN_A_N() # IN A,(nn) 0xDC: CALL_C_NN(FLAG_C, true) # CALL C,nnnn 0xDD: SHIFT('DD') # shift code 0xDE: SBC_A "nn" # SBC A,nn 0xDF: RST(0x0018) # RST 0x18 0xE0: RET_C(FLAG_P, false) # RET PO 0xE1: POP_RR(rpHL) # POP HL 0xE2: JP_C_NN(FLAG_P, false) # JP PO,nnnn 0xE3: EX_iSPi_RR(rpHL) # EX (SP),HL 0xE4: CALL_C_NN(FLAG_P, false) # CALL PO,nnnn 0xE5: PUSH_RR(rpHL) # PUSH HL 0xE6: AND_A "nn" # AND nn 0xE7: RST(0x0020) # RST 0x20 0xE8: RET_C(FLAG_P, true) # RET PE 0xE9: JP_RR(rpHL) # JP (HL) 0xEA: JP_C_NN(FLAG_P, true) # JP PE,nnnn 0xEB: EX_RR_RR(rpDE, rpHL) # EX DE,HL 0xEC: CALL_C_NN(FLAG_P, true) # CALL PE,nnnn 0xED: SHIFT('ED') # shift code 0xEE: XOR_A "nn" # XOR nn 0xEF: RST(0x0028) # RST 0x28 0xF0: RET_C(FLAG_S, false) # RET P 0xF1: POP_RR(rpAF) # POP AF 0xF2: JP_C_NN(FLAG_S, false) # JP NZ,nnnn 0xF3: DI() # DI 0xF4: CALL_C_NN(FLAG_S, false) # CALL P,nnnn 0xF5: PUSH_RR(rpAF) # PUSH AF 0xF6: OR_A "nn" # OR nn 0xF7: RST(0x0030) # RST 0x30 0xF8: RET_C(FLAG_S, true) # RET M 0xF9: LD_RR_RR(rpSP, rpHL) # LD SP,HL 0xFA: JP_C_NN(FLAG_S, true) # JP M,nnnn 0xFB: EI() # EI 0xFC: CALL_C_NN(FLAG_S, true) # CALL M,nnnn 0xFD: SHIFT('FD') # shift code 0xFE: CP_A "nn" # CP nn 0xFF: RST(0x0038) # RST 0x38 0x100: 0 } ### Assemble and evaluate the final JS code for the Z80 component. The indirection on 'eval' causes most browsers to evaluate it in the global scope, giving a significant speed boost ### defineZ80JS = """ window.Z80 = function(opts) { var self = {}; #{setUpStateJS} self.requestInterrupt = function(dataBus) { interruptPending = true; interruptDataBus = dataBus & 0xffff; /* TODO: use event scheduling to keep the interrupt line active for a fixed ~48T window, to support retriggered interrupts and interrupt blocking via chains of EI or DD/FD prefixes */ } self.nonMaskableInterrupt = function() { iff1 = 1; self.requestInterrupt(0x66); } var z80Interrupt = function() { if (iff1) { if (halted) { /* move PC on from the HALT opcode */ regPairs[#{rpPC}]++; halted = false; } iff1 = iff2 = 0; /* push current PC in readiness for call to interrupt handler */ regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] >> 8); regPairs[#{rpSP}]--; WRITEMEM(regPairs[#{rpSP}], regPairs[#{rpPC}] & 0xff); /* TODO: R register */ switch (im) { case 0: regPairs[#{rpPC}] = interruptDataBus; // assume always RST tstates += 6; break; case 1: regPairs[#{rpPC}] = 0x0038; tstates += 7; break; case 2: inttemp = (regs[#{rI}] << 8) | (interruptDataBus & 0xff); l = READMEM(inttemp); inttemp = (inttemp+1) & 0xffff; h = READMEM(inttemp); /*console.log(hex(interruptDataBus), hex(inttemp), hex(l), hex(h));*/ regPairs[#{rpPC}] = (h<<8) | l; tstates += 7; break; } } }; self.runFrame = function(frameLength) { var lastOpcodePrefix, offset, opcode; while (tstates < frameLength || opcodePrefix) { if (interruptible && interruptPending) { z80Interrupt(); interruptPending = false; } interruptible = true; /* unless overridden by opcode */ lastOpcodePrefix = opcodePrefix; opcodePrefix = ''; switch (lastOpcodePrefix) { case '': CONTEND_READ(regPairs[#{rpPC}], 4); opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80); #{opcodeSwitch(OPCODE_RUN_STRINGS, null, opts.traps)} break; case 'CB': CONTEND_READ(regPairs[#{rpPC}], 4); opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80); #{opcodeSwitch(OPCODE_RUN_STRINGS_CB)} break; case 'DD': CONTEND_READ(regPairs[#{rpPC}], 4); opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80); #{opcodeSwitch(OPCODE_RUN_STRINGS_DD, OPCODE_RUN_STRINGS)} break; case 'DDCB': offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; if (offset & 0x80) offset -= 0x100; CONTEND_READ(regPairs[#{rpPC}], 3); opcode = memory.read(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; #{opcodeSwitch(OPCODE_RUN_STRINGS_DDCB)} break; case 'ED': CONTEND_READ(regPairs[#{rpPC}], 4); opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80); #{opcodeSwitch(OPCODE_RUN_STRINGS_ED)} break; case 'FD': CONTEND_READ(regPairs[#{rpPC}], 4); opcode = memory.read(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; regs[#{rR}] = ((regs[#{rR}] + 1) & 0x7f) | (regs[#{rR}] & 0x80); #{opcodeSwitch(OPCODE_RUN_STRINGS_FD, OPCODE_RUN_STRINGS)} break; case 'FDCB': offset = READMEM(regPairs[#{rpPC}]); regPairs[#{rpPC}]++; if (offset & 0x80) offset -= 0x100; CONTEND_READ(regPairs[#{rpPC}], 3); opcode = memory.read(regPairs[#{rpPC}]); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); CONTEND_READ_NO_MREQ(regPairs[#{rpPC}], 1); regPairs[#{rpPC}]++; #{opcodeSwitch(OPCODE_RUN_STRINGS_FDCB)} break; default: throw("Unknown opcode prefix: " + lastOpcodePrefix); } } while (display.nextEventTime != null && display.nextEventTime <= tstates) display.doEvent(); }; self.reset = function() { regPairs[#{rpPC}] = regPairs[#{rpIR}] = 0; iff1 = 0; iff2 = 0; im = 0; halted = false; }; self.loadState = function(snapRegs) { regPairs[#{rpAF}] = snapRegs['AF']; regPairs[#{rpBC}] = snapRegs['BC']; regPairs[#{rpDE}] = snapRegs['DE']; regPairs[#{rpHL}] = snapRegs['HL']; regPairs[#{rpAF_}] = snapRegs['AF_']; regPairs[#{rpBC_}] = snapRegs['BC_']; regPairs[#{rpDE_}] = snapRegs['DE_']; regPairs[#{rpHL_}] = snapRegs['HL_']; regPairs[#{rpIX}] = snapRegs['IX']; regPairs[#{rpIY}] = snapRegs['IY']; regPairs[#{rpSP}] = snapRegs['SP']; regPairs[#{rpPC}] = snapRegs['PC']; regPairs[#{rpIR}] = snapRegs['IR']; iff1 = snapRegs['iff1'] & 1; iff2 = snapRegs['iff2'] & 1; im = snapRegs['im'] & 3; halted = !!snapRegs['halted']; tstates = snapRegs['T'] * 1; interruptPending = !!snapRegs['intp']; interruptDataBus = snapRegs['intd'] & 0xffff; }; self.saveState = function() { return { AF: regPairs[#{rpAF}], BC: regPairs[#{rpBC}], DE: regPairs[#{rpDE}], HL: regPairs[#{rpHL}], AF_: regPairs[#{rpAF_}], BC_: regPairs[#{rpBC_}], DE_: regPairs[#{rpDE_}], HL_: regPairs[#{rpHL_}], IX: regPairs[#{rpIX}], IY: regPairs[#{rpIY}], SP: regPairs[#{rpSP}], PC: regPairs[#{rpPC}], IR: regPairs[#{rpIR}], iff1: iff1, iff2: iff2, im: im, halted: halted, T: tstates, intp: interruptPending, intd: interruptDataBus, }; }; /* Register / flag accessors (used for tape trapping and test harness) */ self.getAF = function() { return regPairs[#{rpAF}]; } self.getBC = function() { return regPairs[#{rpBC}]; } self.getDE = function() { return regPairs[#{rpDE}]; } self.getHL = function() { return regPairs[#{rpHL}]; } self.getAF_ = function() { return regPairs[#{rpAF_}]; } self.getBC_ = function() { return regPairs[#{rpBC_}]; } self.getDE_ = function() { return regPairs[#{rpDE_}]; } self.getHL_ = function() { return regPairs[#{rpHL_}]; } self.getIX = function() { return regPairs[#{rpIX}]; } self.getIY = function() { return regPairs[#{rpIY}]; } self.getI = function() { return regs[#{rI}]; } self.getR = function() { return regs[#{rR}]; } self.getSP = function() { return regPairs[#{rpSP}]; } self.getPC = function() { return regPairs[#{rpPC}]; } self.getIFF1 = function() { return iff1; } self.getIFF2 = function() { return iff2; } self.getIM = function() { return im; } self.getHalted = function() { return halted; } self.setAF = function(val) { regPairs[#{rpAF}] = val; } self.setBC = function(val) { regPairs[#{rpBC}] = val; } self.setDE = function(val) { regPairs[#{rpDE}] = val; } self.setHL = function(val) { regPairs[#{rpHL}] = val; } self.setAF_ = function(val) { regPairs[#{rpAF_}] = val; } self.setBC_ = function(val) { regPairs[#{rpBC_}] = val; } self.setDE_ = function(val) { regPairs[#{rpDE_}] = val; } self.setHL_ = function(val) { regPairs[#{rpHL_}] = val; } self.setIX = function(val) { regPairs[#{rpIX}] = val; } self.setIY = function(val) { regPairs[#{rpIY}] = val; } self.setI = function(val) { regs[#{rI}] = val; } self.setR = function(val) { regs[#{rR}] = val; } self.setSP = function(val) { regPairs[#{rpSP}] = val; } self.setPC = function(val) { regPairs[#{rpPC}] = val; } self.setIFF1 = function(val) { iff1 = val & 1; } self.setIFF2 = function(val) { iff2 = val & 1; } self.setIM = function(val) { im = val & 1; } self.setHalted = function(val) { halted = !!val; } self.getTstates = function() { return tstates; } self.setTstates = function(val) { tstates = val * 1; } self.getCarry_ = function() { return regs[#{rF_}] & #{FLAG_C}; }; self.setCarry = function(val) { if (val) { regs[#{rF}] |= #{FLAG_C}; } else { regs[#{rF}] &= #{~FLAG_C}; } }; self.getA_ = function() { return regs[#{rA_}]; }; return self; }; """ # Apply macro expansions defineZ80JS = defineZ80JS.replace(/READMEM\((.*?)\)/g, '(CONTEND_READ($1, 3), memory.read($1))'); defineZ80JS = defineZ80JS.replace(/WRITEMEM\((.*?),(.*?)\)/g, """ CONTEND_WRITE($1, 3); while (display.nextEventTime != null && display.nextEventTime < tstates) display.doEvent(); memory.write($1,$2); """); if opts.applyContention defineZ80JS = defineZ80JS.replace(/CONTEND_READ\((.*?),(.*?)\)/g, '(tstates += memory.contend($1, tstates) + ($2))'); defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE\((.*?),(.*?)\)/g, '(tstates += memory.contend($1, tstates) + ($2))'); defineZ80JS = defineZ80JS.replace(/CONTEND_READ_NO_MREQ\((.*?),(.*?)\)/g, '(tstates += memory.contend($1, tstates) + ($2))'); defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE_NO_MREQ\((.*?),(.*?)\)/g, '(tstates += memory.contend($1, tstates) + ($2))'); defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_EARLY\((.*?)\)/g, """ var isContendedMemory = memory.isContended($1); var isULAPort = ioBus.isULAPort($1); if (isContendedMemory) tstates += ioBus.contend($1, tstates); tstates += 1; while (display.nextEventTime != null && display.nextEventTime < tstates) display.doEvent(); """); defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_LATE\((.*?)\)/g, """ if (isContendedMemory || isULAPort) { ioBus.contend($1); tstates += 1; if (!isULAPort) { ioBus.contend($1); tstates += 1; ioBus.contend($1); tstates += 1; } else { tstates += 2; } } else { tstates += 3; } """); else defineZ80JS = defineZ80JS.replace(/CONTEND_READ\((.*?),(.*?)\)/g, 'tstates += ($2)'); defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE\((.*?),(.*?)\)/g, 'tstates += ($2)'); defineZ80JS = defineZ80JS.replace(/CONTEND_READ_NO_MREQ\((.*?),(.*?)\)/g, 'tstates += ($2)'); defineZ80JS = defineZ80JS.replace(/CONTEND_WRITE_NO_MREQ\((.*?),(.*?)\)/g, 'tstates += ($2)'); defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_EARLY\((.*?)\)/g, 'tstates += 1'); defineZ80JS = defineZ80JS.replace(/CONTEND_PORT_LATE\((.*?)\)/g, 'tstates += 3'); # console.log(defineZ80JS); indirectEval = eval indirectEval(defineZ80JS);