UNPKG

@soapbox.pub/wasmboy

Version:

Soapbox fork of Wasmboy.

1,954 lines (1,895 loc) 60.2 kB
// Imports import { Cpu } from './index'; import { handleCbOpcode } from './cbOpcodes'; import { setZeroFlag, getZeroFlag, setSubtractFlag, getSubtractFlag, setHalfCarryFlag, getHalfCarryFlag, setCarryFlag, getCarryFlag, checkAndSetEightBitHalfCarryFlag, checkAndSetSixteenBitFlagsAddOverflow } from './flags'; import { addARegister, addAThroughCarryRegister, subARegister, subAThroughCarryRegister, andARegister, xorARegister, orARegister, cpARegister, relativeJump } from './instructions'; import { syncCycles } from '../cycles'; import { rotateByteLeft, rotateByteLeftThroughCarry, rotateByteRight, rotateByteRightThroughCarry, concatenateBytes, splitHighByte, splitLowByte, checkBitOnByte, resetBitOnByte, setBitOnByte } from '../helpers/index'; import { Memory, eightBitLoadFromGBMemoryWithTraps, eightBitStoreIntoGBMemoryWithTraps, sixteenBitStoreIntoGBMemoryWithTraps, eightBitLoadFromGBMemory, sixteenBitLoadFromGBMemory } from '../memory/index'; import { setInterrupts } from '../interrupts/index'; import { u8Portable, u16Portable, i8Portable } from '../portable/portable'; // Take in any opcode, and decode it, and return the number of cycles // Program counter can be gotten from getProgramCounter(); // Setting return value to i32 instead of u16, as we want to return a negative number on error // https://rednex.github.io/rgbds/gbz80.7.html // http://pastraiser.com/cpu/gameboy/gameboyopcodes.html export function executeOpcode(opcode: i32): i32 { // Always implement the program counter by one // Any other value can just subtract or add however much offset before reaching this line let programCounter = Cpu.programCounter; programCounter = u16Portable(programCounter + 1); // Check if we are in the halt bug if (Cpu.isHaltBug) { // Need to not increment program counter, // thus, running the next opcode twice // E.g // 0x76 - halt // FA 34 12 - ld a,(1234) // Becomes // FA FA 34 ld a,(34FA) // 12 ld (de),a programCounter = u16Portable(programCounter - 1); } Cpu.programCounter = programCounter; // Split our opcode into a high nibble to speed up performance // Running 255 if statements is slow, even in wasm haha! let opcodeHighNibble = opcode & 0xf0; opcodeHighNibble = opcodeHighNibble >> 4; // NOTE: @binji rule of thumb: it takes 4 cpu cycles to read one byte // Therefore isntructions that use more than just the opcode (databyte one and two) will take at least // 8 cyckles to use getDataByteOne(), and two cycles to use the concatented // Not using a switch statement to avoid cannot redeclare this variable errors // And it would be a ton of work :p switch (opcodeHighNibble) { case 0x00: return handleOpcode0x(opcode); case 0x01: return handleOpcode1x(opcode); case 0x02: return handleOpcode2x(opcode); case 0x03: return handleOpcode3x(opcode); case 0x04: return handleOpcode4x(opcode); case 0x05: return handleOpcode5x(opcode); case 0x06: return handleOpcode6x(opcode); case 0x07: return handleOpcode7x(opcode); case 0x08: return handleOpcode8x(opcode); case 0x09: return handleOpcode9x(opcode); case 0x0a: return handleOpcodeAx(opcode); case 0x0b: return handleOpcodeBx(opcode); case 0x0c: return handleOpcodeCx(opcode); case 0x0d: return handleOpcodeDx(opcode); case 0x0e: return handleOpcodeEx(opcode); default: return handleOpcodeFx(opcode); } } // Wrapper functions around loading and storing memory, and syncing those cycles export function eightBitLoadSyncCycles(gameboyOffset: i32): u8 { syncCycles(4); return <u8>eightBitLoadFromGBMemoryWithTraps(gameboyOffset); } export function eightBitStoreSyncCycles(gameboyOffset: i32, value: i32): void { syncCycles(4); eightBitStoreIntoGBMemoryWithTraps(gameboyOffset, value); } export function sixteenBitLoadSyncCycles(gameboyOffset: i32): u16 { syncCycles(8); // sixteen bit load has traps even though it has no label return <u16>sixteenBitLoadFromGBMemory(gameboyOffset); } export function sixteenBitStoreSyncCycles(gameboyOffset: i32, value: i32): void { syncCycles(8); sixteenBitStoreIntoGBMemoryWithTraps(gameboyOffset, value); } // Functions to access the next operands of a opcode, reffering to them as "dataBytes" function getDataByteOne(): u8 { syncCycles(4); return <u8>eightBitLoadFromGBMemory(Cpu.programCounter); } function getDataByteTwo(): u8 { syncCycles(4); return <u8>eightBitLoadFromGBMemory(u16Portable(Cpu.programCounter + 1)); } // Get our concatenated databyte one and getDataByteTwo() // Find and replace with : getConcatenatedDataByte() function getConcatenatedDataByte(): u16 { return <u16>concatenateBytes(getDataByteTwo(), getDataByteOne()); } function handleOpcode0x(opcode: i32): i32 { switch (opcode) { case 0x00: // NOP // 1 4 // No Operation return 4; case 0x01: { // LD BC,d16 // 3 12 // 8 cycles let concatenatedDataByte: i32 = getConcatenatedDataByte(); Cpu.registerB = <u8>splitHighByte(concatenatedDataByte); Cpu.registerC = <u8>splitLowByte(concatenatedDataByte); Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 4; } case 0x02: { // LD (BC),A // 1 8 // () means load into address pointed by BC // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerB, Cpu.registerC), Cpu.registerA); return 4; } case 0x03: { // INC BC // 1 8 let registerBC3: u16 = <u16>concatenateBytes(Cpu.registerB, Cpu.registerC); registerBC3++; Cpu.registerB = <u8>splitHighByte(registerBC3); Cpu.registerC = <u8>splitLowByte(registerBC3); return 8; } case 0x04: { // INC B // 1 4 // Z 0 H - let registerB = Cpu.registerB; checkAndSetEightBitHalfCarryFlag(registerB, 1); registerB = u8Portable(registerB + 1); Cpu.registerB = registerB; setZeroFlag(<i32>(registerB === 0)); setSubtractFlag(0); return 4; } case 0x05: { // DEC B // 1 4 // Z 1 H - let registerB = Cpu.registerB; checkAndSetEightBitHalfCarryFlag(registerB, -1); registerB = u8Portable(registerB - 1); Cpu.registerB = registerB; setZeroFlag(<i32>(registerB === 0)); setSubtractFlag(1); return 4; } case 0x06: { // LD B,d8 // 2 8 // 4 cycles Cpu.registerB = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x07: { // RLCA // 1 4 // 0 0 0 C // Check for the carry let registerA = Cpu.registerA; setCarryFlag(<i32>((registerA & 0x80) === 0x80)); Cpu.registerA = rotateByteLeft(registerA); // Set all other flags to zero setZeroFlag(0); setSubtractFlag(0); setHalfCarryFlag(0); return 4; } case 0x08: { // LD (a16),SP // 3 20 // Load the stack pointer into the 16 bit address represented by the two data bytes // 16 cycles, 8 from data byte, 8 from sixteenbit store sixteenBitStoreSyncCycles(getConcatenatedDataByte(), Cpu.stackPointer); Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 4; } case 0x09: { // ADD HL,BC // 1 8 // - 0 H C let registerHL: u16 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); let registerBC9: u16 = <u16>concatenateBytes(Cpu.registerB, Cpu.registerC); checkAndSetSixteenBitFlagsAddOverflow(<u16>registerHL, <u16>registerBC9, false); let result: u16 = u16Portable(<u16>(registerHL + registerBC9)); Cpu.registerH = <u8>splitHighByte(<u16>result); Cpu.registerL = <u8>splitLowByte(<u16>result); setSubtractFlag(0); return 8; } case 0x0a: { // LD A,(BC) // 1 8 // 4 cycles from load Cpu.registerA = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerB, Cpu.registerC)); return 4; } case 0x0b: { // DEC BC // 1 8 let registerBCB: u16 = <u16>concatenateBytes(Cpu.registerB, Cpu.registerC); registerBCB = u16Portable(registerBCB - 1); Cpu.registerB = <u8>splitHighByte(registerBCB); Cpu.registerC = <u8>splitLowByte(registerBCB); return 8; } case 0x0c: { // INC C // 1 4 // Z 0 H - let registerC = Cpu.registerC; checkAndSetEightBitHalfCarryFlag(registerC, 1); registerC = u8Portable(registerC + 1); Cpu.registerC = registerC; setZeroFlag(<i32>(registerC === 0)); setSubtractFlag(0); return 4; } case 0x0d: { // DEC C // 1 4 // Z 1 H - let registerC = Cpu.registerC; checkAndSetEightBitHalfCarryFlag(registerC, -1); registerC = u8Portable(registerC - 1); Cpu.registerC = registerC; setZeroFlag(<i32>(registerC === 0)); setSubtractFlag(1); return 4; } case 0x0e: { // LD C,d8 // 2 8 // 4 cycles Cpu.registerC = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x0f: { // RRCA // 1 4 // 0 0 0 C // Check for the last bit, to see if it will be carried let registerA = Cpu.registerA; setCarryFlag(<i32>((registerA & 0x01) > 0)); Cpu.registerA = rotateByteRight(registerA); // Set all other flags to zero setZeroFlag(0); setSubtractFlag(0); setHalfCarryFlag(0); return 4; } } return -1; } function handleOpcode1x(opcode: i32): i32 { switch (opcode) { case 0x10: { // STOP 0 // 2 4 // Enter CPU very low power mode. Also used to switch between double and normal speed CPU modes in GBC. // Meaning Don't Decode anymore opcodes , or updated the LCD until joypad interrupt (or when button is pressed if I am wrong) // See HALT // If we are in gameboy color mode, set the new speed if (Cpu.GBCEnabled) { // 4 cycles let speedSwitch: i32 = eightBitLoadSyncCycles(Cpu.memoryLocationSpeedSwitch); if (checkBitOnByte(0, speedSwitch)) { // Reset the prepare bit speedSwitch = resetBitOnByte(0, speedSwitch); // Switch to the new mode, and set the speed switch to the OTHER speed, to represent our new speed if (!checkBitOnByte(7, speedSwitch)) { Cpu.GBCDoubleSpeed = true; speedSwitch = setBitOnByte(7, speedSwitch); } else { Cpu.GBCDoubleSpeed = false; speedSwitch = resetBitOnByte(7, speedSwitch); } // Store the final speed switch // 4 cycles eightBitStoreSyncCycles(Cpu.memoryLocationSpeedSwitch, speedSwitch); // Cycle accurate gameboy docs says this takes 76 clocks // 76 - 8 cycles (from load/store) = 68 return 68; } } // NOTE: This breaks Blarggs CPU tests if CGB Stop is not implemented Cpu.isStopped = true; Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x11: { // LD DE,d16 // 3 12 // 8 cycles let concatenatedDataByte = getConcatenatedDataByte(); Cpu.registerD = <u8>splitHighByte(concatenatedDataByte); Cpu.registerE = <u8>splitLowByte(concatenatedDataByte); Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 4; } case 0x12: { // LD (DE),A // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerD, Cpu.registerE), Cpu.registerA); return 4; } case 0x13: { // INC DE // 1 8 let registerDE3 = <u16>concatenateBytes(Cpu.registerD, Cpu.registerE); registerDE3 = u16Portable(registerDE3 + 1); Cpu.registerD = <u8>splitHighByte(registerDE3); Cpu.registerE = <u8>splitLowByte(registerDE3); return 8; } case 0x14: { // INC D // 1 4 // Z 0 H - let registerD = Cpu.registerD; checkAndSetEightBitHalfCarryFlag(registerD, 1); registerD = u8Portable(registerD + 1); Cpu.registerD = registerD; setZeroFlag(<i32>(Cpu.registerD === 0)); setSubtractFlag(0); return 4; } case 0x15: { // DEC D // 1 4 // Z 1 H - let registerD = Cpu.registerD; checkAndSetEightBitHalfCarryFlag(registerD, -1); registerD = u8Portable(registerD - 1); Cpu.registerD = registerD; setZeroFlag(<i32>(Cpu.registerD === 0)); setSubtractFlag(1); return 4; } case 0x16: { // LD D,d8 // 2 8 // 4 cycles Cpu.registerD = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x17: { // RLA // 1 4 // 0 0 0 C // Check for the carry // setting has first bit since we need to use carry let hasHighbit = (Cpu.registerA & 0x80) === 0x80; Cpu.registerA = rotateByteLeftThroughCarry(Cpu.registerA); // OR the carry flag to the end setCarryFlag(<i32>hasHighbit); // Set all other flags to zero setZeroFlag(0); setSubtractFlag(0); setHalfCarryFlag(0); return 4; } case 0x18: { // JR r8 // 2 12 // NOTE: Discoved dataByte is signed // However the relative Jump Function handles this // 4 cycles relativeJump(getDataByteOne()); return 8; } // Relative Jump Function Handles program counter case 0x19: { // ADD HL,DE // 1 8 // - 0 H C let registerHL = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); let registerDE9 = <u16>concatenateBytes(Cpu.registerD, Cpu.registerE); checkAndSetSixteenBitFlagsAddOverflow(<u16>registerHL, <u16>registerDE9, false); let result = u16Portable(<u16>(registerHL + registerDE9)); Cpu.registerH = <u8>splitHighByte(<u16>result); Cpu.registerL = <u8>splitLowByte(<u16>result); setSubtractFlag(0); return 8; } case 0x1a: { // LD A,(DE) // 1 8 let registerDEA = <u16>concatenateBytes(Cpu.registerD, Cpu.registerE); // 4 cycles Cpu.registerA = <u8>eightBitLoadSyncCycles(registerDEA); return 4; } case 0x1b: { // DEC DE // 1 8 let registerDEB = <u16>concatenateBytes(Cpu.registerD, Cpu.registerE); registerDEB = u16Portable(registerDEB - 1); Cpu.registerD = <u8>splitHighByte(registerDEB); Cpu.registerE = <u8>splitLowByte(registerDEB); return 8; } case 0x1c: { // INC E // 1 4 // Z 0 H - let registerE = Cpu.registerE; checkAndSetEightBitHalfCarryFlag(registerE, 1); registerE = u8Portable(registerE + 1); Cpu.registerE = registerE; setZeroFlag(<i32>(registerE === 0)); setSubtractFlag(0); return 4; } case 0x1d: { // DEC E // 1 4 // Z 1 H - let registerE = Cpu.registerE; checkAndSetEightBitHalfCarryFlag(registerE, -1); registerE = u8Portable(registerE - 1); Cpu.registerE = registerE; setZeroFlag(<i32>(registerE === 0)); setSubtractFlag(1); return 4; } case 0x1e: { // LD E,d8 // 2 8 // 4 cycles Cpu.registerE = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x1f: { // RRA // 1 4 // 0 0 0 C // Check for the carry // setting has low bit since we need to use carry let hasLowBit = (Cpu.registerA & 0x01) === 0x01; Cpu.registerA = rotateByteRightThroughCarry(Cpu.registerA); setCarryFlag(<i32>hasLowBit); // Set all other flags to zero setZeroFlag(0); setSubtractFlag(0); setHalfCarryFlag(0); return 4; } } return -1; } function handleOpcode2x(opcode: i32): i32 { switch (opcode) { case 0x20: { // JR NZ,r8 // 2 12/8 // NOTE: NZ stands for not [flag], so in this case, not zero flag // Also, / means, if condition. so if met, 12 cycles, otherwise 8 cycles if (getZeroFlag() === 0) { // 4 cycles relativeJump(getDataByteOne()); // Relative Jump Funciton handles program counter } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 1); } return 8; } case 0x21: { // LD HL,d16 // 3 12 // 8 cycles let sixteenBitDataByte = getConcatenatedDataByte(); Cpu.registerH = <u8>splitHighByte(sixteenBitDataByte); Cpu.registerL = <u8>splitLowByte(sixteenBitDataByte); Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 4; } case 0x22: { // LD (HL+),A // 1 8 let registerHL2 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles eightBitStoreSyncCycles(registerHL2, Cpu.registerA); registerHL2 = u16Portable(registerHL2 + 1); Cpu.registerH = <u8>splitHighByte(registerHL2); Cpu.registerL = <u8>splitLowByte(registerHL2); return 4; } case 0x23: { // INC HL // 1 8 let registerHL3 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); registerHL3 = u16Portable(registerHL3 + 1); Cpu.registerH = <u8>splitHighByte(registerHL3); Cpu.registerL = <u8>splitLowByte(registerHL3); return 8; } case 0x24: { // INC H // 1 4 // Z 0 H - let registerH = Cpu.registerH; checkAndSetEightBitHalfCarryFlag(registerH, 1); registerH = u8Portable(registerH + 1); Cpu.registerH = registerH; setZeroFlag(<i32>(registerH === 0)); setSubtractFlag(0); return 4; } case 0x25: { // DEC H // 1 4 // Z 1 H - let registerH = Cpu.registerH; checkAndSetEightBitHalfCarryFlag(registerH, -1); registerH = u8Portable(registerH - 1); Cpu.registerH = registerH; setZeroFlag(<i32>(registerH === 0)); setSubtractFlag(1); return 4; } case 0x26: { // LD H,d8 // 2 8 // 4 cycles Cpu.registerH = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x27: { // DAA // 1 4 // Z - 0 C let adjustedRegister: u8 = 0; let adjustment: u8 = 0; if (getHalfCarryFlag() > 0) { adjustment = adjustment | 0x06; } if (getCarryFlag() > 0) { adjustment = adjustment | 0x60; } let registerA = Cpu.registerA; if (getSubtractFlag() > 0) { adjustedRegister = u8Portable(registerA - <u8>adjustment); } else { if ((registerA & 0x0f) > 0x09) { adjustment = adjustment | 0x06; } if (registerA > 0x99) { adjustment = adjustment | 0x60; } adjustedRegister = u8Portable(registerA + <u8>adjustment); } // Now set our flags to the correct values setZeroFlag(<i32>(adjustedRegister === 0)); setCarryFlag(<i32>((adjustment & 0x60) !== 0)); setHalfCarryFlag(0); Cpu.registerA = <u8>adjustedRegister; return 4; } case 0x28: { // JR Z,r8 // 2 12/8 if (getZeroFlag() > 0) { // 4 cycles relativeJump(getDataByteOne()); // Relative Jump funciton handles pogram counter } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 1); } return 8; } case 0x29: { // ADD HL,HL // 1 8 // - 0 H C let registerHL9 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); checkAndSetSixteenBitFlagsAddOverflow(registerHL9, registerHL9, false); registerHL9 = u16Portable(registerHL9 * 2); Cpu.registerH = <u8>splitHighByte(registerHL9); Cpu.registerL = <u8>splitLowByte(registerHL9); setSubtractFlag(0); return 8; } case 0x2a: { // LD A,(HL+) // 1 8 let registerHLA = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles Cpu.registerA = <u8>eightBitLoadSyncCycles(registerHLA); registerHLA = u16Portable(registerHLA + 1); Cpu.registerH = <u8>splitHighByte(registerHLA); Cpu.registerL = <u8>splitLowByte(registerHLA); return 4; } case 0x2b: { // DEC HL // 1 8 let registerHLB = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); registerHLB = u16Portable(registerHLB - 1); Cpu.registerH = <u8>splitHighByte(registerHLB); Cpu.registerL = <u8>splitLowByte(registerHLB); return 8; } case 0x2c: { // INC L // 1 4 // Z 0 H - let registerL = Cpu.registerL; checkAndSetEightBitHalfCarryFlag(registerL, 1); registerL = u8Portable(registerL + 1); Cpu.registerL = registerL; setZeroFlag(<i32>(registerL === 0)); setSubtractFlag(0); return 4; } case 0x2d: { // DEC L // 1 4 // Z 1 H - let registerL = Cpu.registerL; checkAndSetEightBitHalfCarryFlag(registerL, -1); registerL = u8Portable(registerL - 1); Cpu.registerL = registerL; setZeroFlag(<i32>(registerL === 0)); setSubtractFlag(1); return 4; } case 0x2e: { // LD L,d8 // 2 8 // 4 cycles Cpu.registerL = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x2f: { // CPL // 1 4 // - 1 1 - Cpu.registerA = ~Cpu.registerA; setSubtractFlag(1); setHalfCarryFlag(1); return 4; } } return -1; } function handleOpcode3x(opcode: i32): i32 { switch (opcode) { case 0x30: { // JR NC,r8 // 2 12 / 8 if (getCarryFlag() === 0) { // 4 cycles relativeJump(getDataByteOne()); // Relative Jump function handles program counter } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 1); } return 8; } case 0x31: { // LD SP,d16 // 3 12 // 8 cycles Cpu.stackPointer = getConcatenatedDataByte(); Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 4; } case 0x32: { // LD (HL-),A // 1 8 let registerHL2 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles eightBitStoreSyncCycles(registerHL2, Cpu.registerA); registerHL2 = u16Portable(registerHL2 - 1); Cpu.registerH = <u8>splitHighByte(registerHL2); Cpu.registerL = <u8>splitLowByte(registerHL2); return 4; } case 0x33: { // INC SP // 1 8 Cpu.stackPointer = u16Portable(Cpu.stackPointer + 1); return 8; } case 0x34: { // INC (HL) // 1 12 // Z 0 H - let registerHL4 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles let valueAtHL4 = <u8>eightBitLoadSyncCycles(registerHL4); // Creating a varible for this to fix assemblyscript overflow bug // Requires explicit casting // https://github.com/AssemblyScript/assemblyscript/issues/26 let incrementer: u8 = 1; checkAndSetEightBitHalfCarryFlag(<u8>valueAtHL4, <i16>incrementer); valueAtHL4 = u8Portable(<u8>valueAtHL4 + <u8>incrementer); setZeroFlag(<i32>(valueAtHL4 === 0)); setSubtractFlag(0); // 4 cycles eightBitStoreSyncCycles(registerHL4, <u8>valueAtHL4); return 4; } case 0x35: { // DEC (HL) // 1 12 // Z 1 H - let registerHL5 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles let valueAtHL5 = <u8>eightBitLoadSyncCycles(registerHL5); // NOTE: This opcode may not overflow correctly, // Please see previous opcode checkAndSetEightBitHalfCarryFlag(valueAtHL5, -1); valueAtHL5 = u8Portable(valueAtHL5 - 1); setZeroFlag(<i32>(valueAtHL5 === 0)); setSubtractFlag(1); // 4 cycles eightBitStoreSyncCycles(registerHL5, valueAtHL5); return 4; } case 0x36: { // LD (HL),d8 // 2 12 // 8 cycles, 4 from store, 4 from data byte eightBitStoreSyncCycles(<u16>concatenateBytes(Cpu.registerH, Cpu.registerL), getDataByteOne()); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x37: { // SCF // 1 4 // - 0 0 1 // Simply set the carry flag setSubtractFlag(0); setHalfCarryFlag(0); setCarryFlag(1); return 4; } case 0x38: { // JR C,r8 // 2 12/8 if (getCarryFlag() === 1) { // 4 cycles relativeJump(getDataByteOne()); // Relative Jump Funciton handles program counter } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 1); } return 8; } case 0x39: { // ADD HL,SP // 1 8 // - 0 H C let registerHL9 = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); checkAndSetSixteenBitFlagsAddOverflow(<u16>registerHL9, Cpu.stackPointer, false); let result = u16Portable(<u16>(registerHL9 + Cpu.stackPointer)); Cpu.registerH = <u8>splitHighByte(<u16>result); Cpu.registerL = <u8>splitLowByte(<u16>result); setSubtractFlag(0); return 8; } case 0x3a: { // LD A,(HL-) // 1 8 let registerHLA = <u16>concatenateBytes(Cpu.registerH, Cpu.registerL); // 4 cycles Cpu.registerA = <u8>eightBitLoadSyncCycles(registerHLA); registerHLA = u16Portable(registerHLA - 1); Cpu.registerH = <u8>splitHighByte(registerHLA); Cpu.registerL = <u8>splitLowByte(registerHLA); return 4; } case 0x3b: { // DEC SP // 1 8 Cpu.stackPointer = u16Portable(Cpu.stackPointer - 1); return 8; } case 0x3c: { // INC A // 1 4 // Z 0 H - let registerA = Cpu.registerA; checkAndSetEightBitHalfCarryFlag(registerA, 1); registerA = u8Portable(registerA + 1); Cpu.registerA = registerA; setZeroFlag(<i32>(registerA === 0)); setSubtractFlag(0); return 4; } case 0x3d: { // DEC A // 1 4 // Z 1 H - let registerA = Cpu.registerA; checkAndSetEightBitHalfCarryFlag(registerA, -1); registerA = u8Portable(registerA - 1); Cpu.registerA = registerA; setZeroFlag(<i32>(registerA === 0)); setSubtractFlag(1); return 4; } case 0x3e: { // LD A,d8 // 2 8 // 4 cycles Cpu.registerA = getDataByteOne(); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0x3f: { // CCF // 1 4 // - 0 0 C setSubtractFlag(0); setHalfCarryFlag(0); setCarryFlag(<i32>(getCarryFlag() <= 0)); return 4; } } return -1; } function handleOpcode4x(opcode: i32): i32 { switch (opcode) { case 0x40: // LD B,B // 1 4 // Load B into B, Do nothing return 4; case 0x41: // LD B,C // 1 4 Cpu.registerB = Cpu.registerC; return 4; case 0x42: // LD B,D // 1 4 Cpu.registerB = Cpu.registerD; return 4; case 0x43: // LD B,E // 1 4 Cpu.registerB = Cpu.registerE; return 4; case 0x44: // LD B,H // 1 4 Cpu.registerB = Cpu.registerH; return 4; case 0x45: // LD B,L // 1 4 Cpu.registerB = Cpu.registerL; return 4; case 0x46: // LD B,(HL) // 1 8 // 4 cycles Cpu.registerB = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x47: // LD B,A // 1 4 Cpu.registerB = Cpu.registerA; return 4; case 0x48: // LD C,B // 1 4 Cpu.registerC = Cpu.registerB; return 4; case 0x49: // LD C,C // 1 4 // Do nothing return 4; case 0x4a: // LD C,D // 1 4 Cpu.registerC = Cpu.registerD; return 4; case 0x4b: // LD C,E // 1 4 Cpu.registerC = Cpu.registerE; return 4; case 0x4c: // LD C,H // 1 4 Cpu.registerC = Cpu.registerH; return 4; case 0x4d: // LD C,L // 1 4 Cpu.registerC = Cpu.registerL; return 4; case 0x4e: // LD C,(HL) // 1 8 // 4 cycles Cpu.registerC = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x4f: // LD C,A // 1 4 Cpu.registerC = Cpu.registerA; return 4; } return -1; } function handleOpcode5x(opcode: i32): i32 { switch (opcode) { case 0x50: // LD D,B // 1 4 Cpu.registerD = Cpu.registerB; return 4; case 0x51: // LD D,C // 1 4 Cpu.registerD = Cpu.registerC; return 4; case 0x52: // LD D,D // 1 4 // Do Nothing return 4; case 0x53: // LD D,E // 1 4 Cpu.registerD = Cpu.registerE; return 4; case 0x54: // LD D,H // 1 4 Cpu.registerD = Cpu.registerH; return 4; case 0x55: // LD D,L // 1 4 Cpu.registerD = Cpu.registerL; return 4; case 0x56: // LD D,(HL) // 1 8 // 4 cycles Cpu.registerD = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x57: // LD D,A // 1 4 Cpu.registerD = Cpu.registerA; return 4; case 0x58: // LD E,B // 1 4 Cpu.registerE = Cpu.registerB; return 4; case 0x59: // LD E,C // 1 4 Cpu.registerE = Cpu.registerC; return 4; case 0x5a: // LD E,D // 1 4 Cpu.registerE = Cpu.registerD; return 4; case 0x5b: // LD E,E // 1 4 // Do Nothing return 4; case 0x5c: // LD E,H // 1 4 Cpu.registerE = Cpu.registerH; return 4; case 0x5d: // LD E,L // 1 4 Cpu.registerE = Cpu.registerL; return 4; case 0x5e: // LD E,(HL) // 1 8 // 4 cycles Cpu.registerE = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x5f: // LD E,A // 1 4 Cpu.registerE = Cpu.registerA; return 4; } return -1; } function handleOpcode6x(opcode: i32): i32 { switch (opcode) { case 0x60: // LD H,B // 1 4 Cpu.registerH = Cpu.registerB; return 4; case 0x61: // LD H,C // 1 4 Cpu.registerH = Cpu.registerC; return 4; case 0x62: // LD H,D // 1 4 Cpu.registerH = Cpu.registerD; return 4; case 0x63: // LD H,E // 1 4 Cpu.registerH = Cpu.registerE; return 4; case 0x64: // LD H,H // 1 4 Cpu.registerH = Cpu.registerH; return 4; case 0x65: // LD H,L // 1 4 Cpu.registerH = Cpu.registerL; return 4; case 0x66: // LD H,(HL) // 1 8 // 4 cycles Cpu.registerH = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x67: // LD H,A // 1 4 Cpu.registerH = Cpu.registerA; return 4; case 0x68: // LD L,B // 1 4 Cpu.registerL = Cpu.registerB; return 4; case 0x69: // LD L,C // 1 4 Cpu.registerL = Cpu.registerC; return 4; case 0x6a: // LD L,D // 1 4 Cpu.registerL = Cpu.registerD; return 4; case 0x6b: // LD L,E // 1 4 Cpu.registerL = Cpu.registerE; return 4; case 0x6c: // LD L,H // 1 4 Cpu.registerL = Cpu.registerH; return 4; case 0x6d: // LD L,L // 1 4 Cpu.registerL = Cpu.registerL; return 4; case 0x6e: // LD L,(HL) // 1 8 // 4 cycles Cpu.registerL = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x6f: // LD L,A // 1 4 Cpu.registerL = Cpu.registerA; return 4; } return -1; } function handleOpcode7x(opcode: i32): i32 { switch (opcode) { case 0x70: // LD (HL),B // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerB); return 4; case 0x71: // LD (HL),C // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerC); return 4; case 0x72: // LD (HL),D // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerD); return 4; case 0x73: // LD (HL),E // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerE); return 4; case 0x74: // LD (HL),H // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerH); return 4; case 0x75: // LD (HL),L // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerL); return 4; case 0x76: // HALT // 1 4 // Enter CPU very low power mode // Meaning Don't Decode anymore opcodes until an interrupt occurs // Still need to do timers and things // Can't Halt during an HDMA // https://gist.github.com/drhelius/3394856 if (!Memory.isHblankHdmaActive) { Cpu.enableHalt(); } return 4; case 0x77: // LD (HL),A // 1 8 // 4 cycles eightBitStoreSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerA); return 4; case 0x78: // LD A,B // 1 4 Cpu.registerA = Cpu.registerB; return 4; case 0x79: // LD A,C // 1 4 Cpu.registerA = Cpu.registerC; return 4; case 0x7a: // LD A,D // 1 4 Cpu.registerA = Cpu.registerD; return 4; case 0x7b: // LD A,E // 1 4 Cpu.registerA = Cpu.registerE; return 4; case 0x7c: // LD A,H // 1 4 Cpu.registerA = Cpu.registerH; return 4; case 0x7d: // LD A,L // 1 4 Cpu.registerA = Cpu.registerL; return 4; case 0x7e: // LD A,(HL) // 1 8 // NOTE: Thanks to @binji for catching that this should be 8 cycles, not 4 // 4 cycles Cpu.registerA = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); return 4; case 0x7f: // LD A,A // 1 4 // Do Nothing return 4; } return -1; } function handleOpcode8x(opcode: i32): i32 { switch (opcode) { case 0x80: // ADD A,B // 1 4 // Z 0 H C addARegister(Cpu.registerB); return 4; case 0x81: // ADD A,C // 1 4 // Z 0 H C addARegister(Cpu.registerC); return 4; case 0x82: // ADD A,D // 1 4 // Z 0 H C addARegister(Cpu.registerD); return 4; case 0x83: // ADD A,E // 1 4 // Z 0 H C addARegister(Cpu.registerE); return 4; case 0x84: // ADD A,H // 1 4 // Z 0 H C addARegister(Cpu.registerH); return 4; case 0x85: // ADD A,L // 1 4 // Z 0 H C addARegister(Cpu.registerL); return 4; case 0x86: // ADD A,(HL) // 1 8 // Z 0 H C // 4 cycles let valueAtHL6: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); addARegister(<u8>valueAtHL6); return 4; case 0x87: // ADD A,A // 1 4 // Z 0 H C addARegister(Cpu.registerA); return 4; case 0x88: // ADC A,B // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerB); return 4; case 0x89: // ADC A,C // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerC); return 4; case 0x8a: // ADC A,D // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerD); return 4; case 0x8b: // ADC A,E // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerE); return 4; case 0x8c: // ADC A,H // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerH); return 4; case 0x8d: // ADC A,L // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerL); return 4; case 0x8e: // ADC A,(HL) // 1 8 // Z 0 H C // 4 cycles let valueAtHLE: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); addAThroughCarryRegister(<u8>valueAtHLE); return 4; case 0x8f: // ADC A,A // 1 4 // Z 0 H C addAThroughCarryRegister(Cpu.registerA); return 4; } return -1; } function handleOpcode9x(opcode: i32): i32 { switch (opcode) { case 0x90: // SUB B // 1 4 // Z 1 H C subARegister(Cpu.registerB); return 4; case 0x91: // SUB C // 1 4 // Z 1 H C subARegister(Cpu.registerC); return 4; case 0x92: // SUB D // 1 4 // Z 1 H C subARegister(Cpu.registerD); return 4; case 0x93: // SUB E // 1 4 // Z 1 H C subARegister(Cpu.registerE); return 4; case 0x94: // SUB H // 1 4 // Z 1 H C subARegister(Cpu.registerH); return 4; case 0x95: // SUB L // 1 4 // Z 1 H C subARegister(Cpu.registerL); return 4; case 0x96: // SUB (HL) // 1 8 // Z 1 H C // 4 cycles let valueAtHL6: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); subARegister(<u8>valueAtHL6); return 4; case 0x97: // SUB A // 1 4 // Z 1 H C subARegister(Cpu.registerA); return 4; case 0x98: // SBC A,B // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerB); return 4; case 0x99: // SBC A,C // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerC); return 4; case 0x9a: // SBC A,D // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerD); return 4; case 0x9b: // SBC A,E // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerE); return 4; case 0x9c: // SBC A,H // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerH); return 4; case 0x9d: // SBC A,L // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerL); return 4; case 0x9e: // SBC A,(HL) // 1 8 // Z 1 H C // 4 cycles let valueAtHLE: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); subAThroughCarryRegister(<u8>valueAtHLE); return 4; case 0x9f: // SBC A,A // 1 4 // Z 1 H C subAThroughCarryRegister(Cpu.registerA); return 4; } return -1; } function handleOpcodeAx(opcode: i32): i32 { switch (opcode) { case 0xa0: // AND B // 1 4 // Z 0 1 0 andARegister(Cpu.registerB); return 4; case 0xa1: // AND C // 1 4 // Z 0 1 0 andARegister(Cpu.registerC); return 4; case 0xa2: // AND D // 1 4 // Z 0 1 0 andARegister(Cpu.registerD); return 4; case 0xa3: // AND E // 1 4 // Z 0 1 0 andARegister(Cpu.registerE); return 4; case 0xa4: // AND H // 1 4 // Z 0 1 0 andARegister(Cpu.registerH); return 4; case 0xa5: // AND L // 1 4 // Z 0 1 0 andARegister(Cpu.registerL); return 4; case 0xa6: // AND (HL) // 1 8 // Z 0 1 0 // 4 cycles let valueAtHL6: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); andARegister(<u8>valueAtHL6); return 4; case 0xa7: // AND A // 1 4 // Z 0 1 0 // NOTE: & Yourself, does nothing andARegister(Cpu.registerA); return 4; case 0xa8: // XOR B // 1 4 // Z 0 0 0 xorARegister(Cpu.registerB); return 4; case 0xa9: // XOR C // 1 4 // Z 0 0 0 xorARegister(Cpu.registerC); return 4; case 0xaa: // XOR D // 1 4 // Z 0 0 0 xorARegister(Cpu.registerD); return 4; case 0xab: // XOR E // 1 4 // Z 0 0 0 xorARegister(Cpu.registerE); return 4; case 0xac: // XOR H // 1 4 // Z 0 0 0 xorARegister(Cpu.registerH); return 4; case 0xad: // XOR L // 1 4 // Z 0 0 0 xorARegister(Cpu.registerL); return 4; case 0xae: // XOR (HL) // 1 8 // Z 0 0 0 // 4 cycles let valueAtHLE: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); xorARegister(<u8>valueAtHLE); return 4; case 0xaf: // XOR A // 1 4 // Z 0 0 0 xorARegister(Cpu.registerA); return 4; } return -1; } function handleOpcodeBx(opcode: i32): i32 { switch (opcode) { case 0xb0: // OR B // 1 4 // Z 0 0 0 orARegister(Cpu.registerB); return 4; case 0xb1: // OR C // 1 4 // Z 0 0 0 orARegister(Cpu.registerC); return 4; case 0xb2: // OR D // 1 4 // Z 0 0 0 orARegister(Cpu.registerD); return 4; case 0xb3: // OR E // 1 4 // Z 0 0 0 orARegister(Cpu.registerE); return 4; case 0xb4: // OR H // 1 4 // Z 0 0 0 orARegister(Cpu.registerH); return 4; case 0xb5: // OR L // 1 4 // Z 0 0 0 orARegister(Cpu.registerL); return 4; case 0xb6: // OR (HL) // 1 8 // Z 0 0 0 // 4 cycles let valueAtHL6: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); orARegister(<u8>valueAtHL6); return 4; case 0xb7: // OR A // 1 4 // Z 0 0 0 orARegister(Cpu.registerA); return 4; case 0xb8: // CP B // 1 4 // Z 1 H C cpARegister(Cpu.registerB); return 4; case 0xb9: // CP C // 1 4 // Z 1 H C cpARegister(Cpu.registerC); return 4; case 0xba: // CP D // 1 4 // Z 1 H C cpARegister(Cpu.registerD); return 4; case 0xbb: // CP E // 1 4 // Z 1 H C cpARegister(Cpu.registerE); return 4; case 0xbc: // CP H // 1 4 // Z 1 H C cpARegister(Cpu.registerH); return 4; case 0xbd: // CP L // 1 4 // Z 1 H C cpARegister(Cpu.registerL); return 4; case 0xbe: // CP (HL) // 1 8 // Z 1 H C // 4 cycles let valueAtHLE: u8 = <u8>eightBitLoadSyncCycles(concatenateBytes(Cpu.registerH, Cpu.registerL)); cpARegister(<u8>valueAtHLE); return 4; case 0xbf: // CP A // 1 4 // Z 1 H C cpARegister(Cpu.registerA); return 4; } return -1; } function handleOpcodeCx(opcode: i32): i32 { switch (opcode) { case 0xc0: { // RET NZ // 1 20/8 if (getZeroFlag() === 0) { // 8 cycles let stackPointer = Cpu.stackPointer; Cpu.programCounter = <u16>sixteenBitLoadSyncCycles(stackPointer); Cpu.stackPointer = u16Portable(stackPointer + 2); return 12; } else { return 8; } } case 0xc1: { // POP BC // 1 12 // 8 cycles let registerBC1: i32 = sixteenBitLoadSyncCycles(Cpu.stackPointer); Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2); Cpu.registerB = <u8>splitHighByte(registerBC1); Cpu.registerC = <u8>splitLowByte(registerBC1); return 4; } case 0xc2: { // JP NZ,a16 // 3 16/12 if (getZeroFlag() === 0) { // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 12; } } case 0xc3: { // JP a16 // 3 16 // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } case 0xc4: { // CALL NZ,a16 // 3 24/12 if (getZeroFlag() === 0) { let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, u16Portable(Cpu.programCounter + 2)); // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 12; } } case 0xc5: { // PUSH BC // 1 16 let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, concatenateBytes(Cpu.registerB, Cpu.registerC)); return 8; } case 0xc6: { // ADD A,d8 // 2 8 // Z 0 H C // 4 cycles addARegister(getDataByteOne()); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0xc7: { // RST 00H // 1 16 let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, Cpu.programCounter); Cpu.programCounter = 0x00; return 8; } case 0xc8: { // RET Z // 1 20/8 if (getZeroFlag() === 1) { // 8 cycles let stackPointer = Cpu.stackPointer; Cpu.programCounter = <u16>sixteenBitLoadSyncCycles(stackPointer); Cpu.stackPointer = u16Portable(stackPointer + 2); return 12; } else { return 8; } } case 0xc9: { // RET // 1 16 // 8 cycles let stackPointer = Cpu.stackPointer; Cpu.programCounter = <u16>sixteenBitLoadSyncCycles(stackPointer); Cpu.stackPointer = u16Portable(stackPointer + 2); return 8; } case 0xca: { // JP Z,a16 // 3 16/12 if (getZeroFlag() === 1) { // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 12; } } case 0xcb: { // PREFIX CB // 1 4 // 4 cycles let cbCycles: i32 = handleCbOpcode(getDataByteOne()); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return cbCycles; } case 0xcc: { // CALL Z,a16 // 3 24/12 if (getZeroFlag() === 1) { let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, Cpu.programCounter + 2); // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 12; } } case 0xcd: { // CALL a16 // 3 24 let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, u16Portable(Cpu.programCounter + 2)); // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } case 0xce: { // ADC A,d8 // 2 8 // Z 0 H C // 4 cycles addAThroughCarryRegister(getDataByteOne()); Cpu.programCounter = u16Portable(Cpu.programCounter + 1); return 4; } case 0xcf: { // RST 08H // 1 16 let stackPointer = u16Portable(Cpu.stackPointer - 2); Cpu.stackPointer = stackPointer; // 8 cycles sixteenBitStoreSyncCycles(stackPointer, Cpu.programCounter); Cpu.programCounter = 0x08; return 8; } } return -1; } function handleOpcodeDx(opcode: i32): i32 { switch (opcode) { case 0xd0: { // RET NC // 1 20/8 if (getCarryFlag() === 0) { // 8 cycles let stackPointer = Cpu.stackPointer; Cpu.programCounter = <u16>sixteenBitLoadSyncCycles(stackPointer); Cpu.stackPointer = u16Portable(stackPointer + 2); return 12; } else { return 8; } } case 0xd1: { // POP DE // 1 12 // 8 cycles let stackPointer = Cpu.stackPointer; let registerDE1: i32 = sixteenBitLoadSyncCycles(stackPointer); Cpu.stackPointer = u16Portable(stackPointer + 2); Cpu.registerD = <u8>splitHighByte(registerDE1); Cpu.registerE = <u8>splitLowByte(registerDE1); return 4; } case 0xd2: { // JP NC,a16 // 3 16/12 if (getCarryFlag() === 0) { // 8 cycles Cpu.programCounter = getConcatenatedDataByte(); return 8; } else { Cpu.programCounter = u16Portable(Cpu.programCounter + 2); return 12; } } /* No Opcode for: 0xD3 */ case 0xd4: { // CALL NC,a16 // 3 24/12 if (getCarryFlag() === 0) {