You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1348 lines
32 KiB
1348 lines
32 KiB
/* SharpLr35902.Ops.cpp |
|
* |
|
* Sharp LR35902 opcode execution. |
|
*/ |
|
|
|
#include <sstream> |
|
|
|
#include "../../PlipEmulationException.h" |
|
|
|
#include "SharpLr35902.h" |
|
#include "SharpLr35902.Macros.h" |
|
|
|
namespace Plip::Cpu { |
|
/* |
|
* Standard opcodes: starts at m_mcycle == 2. |
|
*/ |
|
// ADD A, n |
|
void SharpLr35902::OpAccumAddImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
uint16_t res = m_reg.a + m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_ADD_HALFCARRY(m_reg.a, m_instr[1]); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// ADC A, n |
|
void SharpLr35902::OpAccumAddCarryImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
uint8_t cf = (FLAG_TEST(CARRY) ? 1 : 0); |
|
uint16_t res = m_reg.a + m_instr[1] + cf; |
|
CHECK_CARRY(res); |
|
|
|
// CHECK_ADD_HALFCARRY won't work here, since the carry flag needs |
|
// to be accounted for. |
|
if(((m_reg.a & 0xF) + (m_instr[1] & 0xF) + cf) > 0xF) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// AND n |
|
void SharpLr35902::OpAccumAndImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
m_reg.a &= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(HALFCARRY); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// DAA |
|
void SharpLr35902::OpAccumBcd() { |
|
// DAAaaaaaaaaaaaaaaaahhhhhhh!!!! |
|
// Source: https://www.reddit.com/r/EmuDev/comments/4ycoix/a_guide_to_the_gameboys_halfcarry_flag/d6p3rtl/ |
|
uint8_t adjust = 0x00; |
|
|
|
if(FLAG_TEST(CARRY) || (!FLAG_TEST(SUBTRACT) && m_reg.a > 0x99)) { |
|
adjust |= 0x60; |
|
FLAG_SET(CARRY); |
|
} |
|
|
|
if(FLAG_TEST(HALFCARRY) || (!FLAG_TEST(SUBTRACT) && (m_reg.a & 0x0F) > 0x09)) { |
|
adjust |= 0x06; |
|
} |
|
|
|
m_reg.a += FLAG_TEST(SUBTRACT) ? -adjust : adjust; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// CP n |
|
void SharpLr35902::OpAccumCarryImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
uint16_t res = m_reg.a - m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, m_instr[1]); |
|
CHECK_ZERO(res & 0xFF); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// CPL |
|
void SharpLr35902::OpAccumFlip() { |
|
m_reg.a = ~(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
FLAG_SET(HALFCARRY); |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// OR n |
|
void SharpLr35902::OpAccumOrImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
m_reg.a |= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// RLCA |
|
void SharpLr35902::OpAccumRotateLeft() { |
|
uint8_t msb = m_reg.a >> 7; |
|
CHECK_BIT_CARRY(msb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
m_reg.a <<= 1; |
|
m_reg.a |= msb; |
|
FLAG_CLEAR(ZERO); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// RLA |
|
void SharpLr35902::OpAccumRotateLeftThruCarry() { |
|
uint8_t cf = FLAG_TEST(CARRY) ? 1 : 0; |
|
CHECK_BIT_CARRY(m_reg.a & 0b10000000); |
|
m_reg.a <<=1; |
|
m_reg.a |= cf; |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(ZERO); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// RRCA |
|
void SharpLr35902::OpAccumRotateRight() { |
|
uint8_t lsb = m_reg.a & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
m_reg.a >>= 1; |
|
m_reg.a |= lsb << 7; |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(ZERO); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// RRA |
|
void SharpLr35902::OpAccumRotateRightThruCarry() { |
|
uint8_t cf = FLAG_TEST(CARRY) ? 0b10000000 : 0; |
|
CHECK_BIT_CARRY(m_reg.a & 0b00000001); |
|
m_reg.a >>= 1; |
|
m_reg.a |= cf; |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(ZERO); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// SUB A, n |
|
void SharpLr35902::OpAccumSubImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
uint16_t res = m_reg.a - m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, m_instr[1]); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SBC A, n |
|
void SharpLr35902::OpAccumSubBorrowImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
uint8_t cf = (FLAG_TEST(CARRY) ? 1 : 0); |
|
uint16_t res = m_reg.a - m_instr[1] - cf; |
|
|
|
CHECK_CARRY(res); |
|
|
|
// CHECK_SUB_HALFCARRY won't work here, since the borrow flag needs |
|
// to be accounted for. |
|
if(((m_reg.a & 0xF) - (m_instr[1] & 0xF) - cf) < 0x00) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// XOR n |
|
void SharpLr35902::OpAccumXorImm() { |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
m_reg.a ^= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// ADD A, r |
|
// ADD A, (HL) |
|
void SharpLr35902::OpAdd() { |
|
auto src = OP_REG_Y(0); |
|
uint16_t res; |
|
|
|
if(src == IDX_HL) { |
|
// ADD A, (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_reg.a + m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_ADD_HALFCARRY(m_reg.a, m_instr[1]); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// ADD A, r |
|
auto val = *(GetRegister8(src)); |
|
res = m_reg.a + val; |
|
CHECK_CARRY(res); |
|
CHECK_ADD_HALFCARRY(m_reg.a, val); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// ADD HL, rr |
|
void SharpLr35902::OpAdd16() { |
|
CYCLE(3) { |
|
auto hl = GetRegister16Value(IDX_16_HL); |
|
auto val = GetRegister16Value(OP_REG16(0)); |
|
|
|
uint32_t res = hl + val; |
|
CHECK_CARRY16(res); |
|
CHECK_ADD_HALFCARRY16(hl, val); |
|
FLAG_CLEAR(SUBTRACT); |
|
|
|
m_reg.h = res >> 8; |
|
m_reg.l = res & 0xFF; |
|
} |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// ADD SP, e |
|
void SharpLr35902::OpAddSpOffset() { |
|
FETCH_CYCLE(2); |
|
CYCLE(5) { |
|
auto offset = (int8_t)m_instr[1]; |
|
uint16_t res = m_reg.sp + offset; |
|
|
|
// Both carry flags are calculated from the low bit. |
|
CHECK_CARRY((m_reg.sp & 0xFF) + m_instr[1]); |
|
CHECK_ADD_HALFCARRY(m_reg.sp, m_instr[1]); |
|
|
|
m_reg.sp = res; |
|
FLAG_CLEAR(ZERO); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// ADC A, r |
|
// ADC A, (HL) |
|
void SharpLr35902::OpAddCarry() { |
|
auto src = OP_REG_Y(0); |
|
uint16_t res; |
|
uint8_t cf = (FLAG_TEST(CARRY) ? 1 : 0); |
|
|
|
if(src == IDX_HL) { |
|
// ADC A, (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_reg.a + m_instr[1] + (FLAG_TEST(CARRY) ? 1 : 0); |
|
CHECK_CARRY(res); |
|
|
|
if(((m_reg.a & 0xF) + (m_instr[1] & 0xF) + cf) > 0xF) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// ADC A, r |
|
auto val = *(GetRegister8(src)); |
|
res = m_reg.a + val + (FLAG_TEST(CARRY) ? 1 : 0); |
|
CHECK_CARRY(res); |
|
|
|
if(((m_reg.a & 0xF) + (m_instr[1] & 0xF) + cf) > 0xF) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// AND r |
|
// AND (HL) |
|
void SharpLr35902::OpAnd() { |
|
auto src = OP_REG_Y(0); |
|
|
|
if(src == IDX_HL) { |
|
// AND (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
m_reg.a &= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_SET(HALFCARRY); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// AND r |
|
m_reg.a &= *(GetRegister8(src)); |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_SET(HALFCARRY); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// CALL cc, nn |
|
void SharpLr35902::OpCallCond() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
|
|
if(!TestConditional(OP_COND)) { |
|
NUM_MCYCLES(4); |
|
return; |
|
} |
|
|
|
STACK_PUSH_PC(5); |
|
CYCLE(7) { m_reg.pc = (m_instr[2] << 8) | m_instr[1]; } |
|
NUM_MCYCLES(7); |
|
} |
|
|
|
// CALL nn |
|
void SharpLr35902::OpCallUnc() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
STACK_PUSH_PC(5); |
|
CYCLE(7) { m_reg.pc = (m_instr[2] << 8) | m_instr[1]; } |
|
NUM_MCYCLES(7); |
|
} |
|
|
|
// CP r |
|
// CP (HL) |
|
void SharpLr35902::OpCarry() { |
|
auto src = OP_REG_Y(0); |
|
uint16_t res; |
|
|
|
if(src == IDX_HL) { |
|
// CP (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_reg.a - m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, m_instr[1]); |
|
CHECK_ZERO(res & 0xFF); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// CP r |
|
auto val = *(GetRegister8(src)); |
|
res = m_reg.a - val; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, val); |
|
CHECK_ZERO(res & 0xFF); |
|
FLAG_SET(SUBTRACT); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// DEC r |
|
// DEC (HL) |
|
void SharpLr35902::OpDecReg() { |
|
auto dest = OP_REG_X(0); |
|
uint16_t res; |
|
|
|
if(dest == IDX_HL) { |
|
// DEC (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_instr[1] - 1; |
|
|
|
FLAG_SET(SUBTRACT); |
|
CHECK_SUB_HALFCARRY(m_instr[1], 1); |
|
res &= 0xFF; |
|
CHECK_ZERO(res); |
|
|
|
MEM_WRITE(REG_HL, res); |
|
} |
|
NUM_MCYCLES(4); |
|
return; |
|
} |
|
|
|
// DEC r |
|
auto reg = GetRegister8(dest); |
|
res = *reg - 1; |
|
|
|
FLAG_SET(SUBTRACT); |
|
CHECK_SUB_HALFCARRY(*reg, 1); |
|
res &= 0xFF; |
|
CHECK_ZERO(res); |
|
*reg = res; |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// DEC rr |
|
void SharpLr35902::OpDecPair() { |
|
uint8_t *high = nullptr; |
|
uint8_t *low = nullptr; |
|
|
|
std::tie(high, low) = GetRegisterPair(OP_REG16(0)); |
|
DecPair(high, low); |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// DI |
|
void SharpLr35902::OpDisableInterrupts() { |
|
m_ime = ScheduledState::Disabled; |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// EI |
|
void SharpLr35902::OpEnableInterrupts() { |
|
m_ime = ScheduledState::Scheduled; |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// CCF |
|
void SharpLr35902::OpFlipCarry() { |
|
FLAG_FLIP(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// RST [00h, 08h, 10h, 18h, 20h, 28h, 30h, 38h] |
|
void SharpLr35902::OpFuncFixedUnc() { |
|
STACK_PUSH_PC(3); |
|
CYCLE(5) { m_reg.pc = OP_IDX(0) * 0x08; } |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// HALT |
|
void SharpLr35902::OpHalt() { |
|
m_halt = true; |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// INC r |
|
// INC (HL) |
|
void SharpLr35902::OpIncReg() { |
|
auto dest = OP_REG_X(0); |
|
uint16_t res; |
|
|
|
if(dest == IDX_HL) { |
|
// INC (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_instr[1] + 1; |
|
|
|
FLAG_CLEAR(SUBTRACT); |
|
CHECK_ADD_HALFCARRY(m_instr[1], 1); |
|
res &= 0xFF; |
|
CHECK_ZERO(res); |
|
|
|
MEM_WRITE(REG_HL, res); |
|
} |
|
NUM_MCYCLES(4); |
|
return; |
|
} |
|
|
|
// INC r |
|
auto reg = GetRegister8(dest); |
|
res = *reg + 1; |
|
|
|
FLAG_CLEAR(SUBTRACT); |
|
CHECK_ADD_HALFCARRY(*reg, 1); |
|
res &= 0xFF; |
|
CHECK_ZERO(res); |
|
*reg = res; |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// INC rr |
|
void SharpLr35902::OpIncPair() { |
|
uint8_t *high = nullptr; |
|
uint8_t *low = nullptr; |
|
|
|
std::tie(high, low) = GetRegisterPair(OP_REG16(0)); |
|
IncPair(high, low); |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// JP cc, nn |
|
void SharpLr35902::OpJumpAbsCond() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
|
|
if(!TestConditional(OP_COND)) { |
|
NUM_MCYCLES(4); |
|
return; |
|
} |
|
|
|
CYCLE(5) { SET_PC_IMM; } |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// JP nn |
|
void SharpLr35902::OpJumpAbsUnc() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
CYCLE(5) { SET_PC_IMM; } |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// JP (HL) |
|
void SharpLr35902::OpJumpRegUnc() { |
|
m_reg.pc = REG_HL; |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// JR cc, e |
|
void SharpLr35902::OpJumpRelCond() { |
|
FETCH_CYCLE(2); |
|
|
|
CYCLE(3) { |
|
if(!TestConditional(OP_COND)) { |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
} |
|
|
|
CYCLE(4) { m_reg.pc += (int8_t)m_instr[1]; } |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// JR e |
|
void SharpLr35902::OpJumpRelUnc() { |
|
FETCH_CYCLE(2); |
|
CYCLE(4) { m_reg.pc += (int8_t)m_instr[1]; } |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// LD A, (nn) |
|
void SharpLr35902::OpLdAccumMem() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
CYCLE(4) { |
|
m_reg.a = MEM_READ(COMBINE16LE(m_instr[2], m_instr[1])); |
|
} |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// LDH A, (n) |
|
void SharpLr35902::OpLdAccumMemHighImm() { |
|
FETCH_CYCLE(2); |
|
FETCH_ADDR_CYCLE(3, 0xFF00 | m_instr[1]); |
|
CYCLE(4) { m_reg.a = m_instr[2]; } |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// LDH A, (C) |
|
void SharpLr35902::OpLdAccumMemHighC() { |
|
FETCH_ADDR_CYCLE(2, 0xFF00 | m_reg.c); |
|
CYCLE(3) { m_reg.a = m_instr[1]; } |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// LD HL, SP+e |
|
void SharpLr35902::OpLdHlSpOffset() { |
|
FETCH_CYCLE(2); |
|
CYCLE(4) { |
|
auto offset = (int8_t)m_instr[1]; |
|
uint16_t res = m_reg.sp + offset; |
|
|
|
CHECK_CARRY((m_reg.sp & 0xFF) + m_instr[1]); |
|
CHECK_ADD_HALFCARRY(m_reg.sp, m_instr[1]); |
|
|
|
m_reg.h = (res >> 8) & 0xFF; |
|
m_reg.l = res & 0xFF; |
|
FLAG_CLEAR(ZERO); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// LD (nn), A |
|
void SharpLr35902::OpLdMemAccum() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
CYCLE(4) { |
|
MEM_WRITE(COMBINE16LE(m_instr[2], m_instr[1]), m_reg.a); |
|
} |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// LDH (n), A |
|
void SharpLr35902::OpLdMemHighImmAccum() { |
|
FETCH_CYCLE(2); |
|
CYCLE(3) { MEM_WRITE(0xFF00 | m_instr[1], m_reg.a); } |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// LDH (C), A |
|
void SharpLr35902::OpLdMemHighCAccum() { |
|
CYCLE(2) { MEM_WRITE(0xFF00 | m_reg.c, m_reg.a); } |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// LD (HL), r |
|
// LD (rr), A |
|
void SharpLr35902::OpLdMemReg() { |
|
auto dest = OP_REG_X(0); |
|
auto src = OP_REG_Y(0); |
|
|
|
if(dest == REG_HL) { |
|
// LD (HL), r |
|
CYCLE(2) { |
|
auto reg = *GetRegister8(src); |
|
MEM_WRITE(REG_HL, reg); |
|
} |
|
} else { |
|
// LD (rr), A |
|
CYCLE(2) { |
|
auto reg = GetRegister16Pointer(OP_REG16(0)); |
|
MEM_WRITE(reg, m_reg.a); |
|
} |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// LD (nn), SP |
|
void SharpLr35902::OpLdMemSp() { |
|
FETCH_CYCLE(2); |
|
FETCH_CYCLE(3); |
|
CYCLE(4) { MEM_WRITE(COMBINE16LE(m_instr[2], m_instr[1]), |
|
m_reg.sp & 0xFF); } |
|
CYCLE(5) { MEM_WRITE(COMBINE16LE(m_instr[2], m_instr[1]) + 1, |
|
m_reg.sp >> 8); } |
|
NUM_MCYCLES(6); |
|
} |
|
|
|
// LD rr, nn |
|
void SharpLr35902::OpLdReg16Imm16() { |
|
FETCH_IMM_CYCLE(2); |
|
FETCH_IMM_CYCLE(3); |
|
CYCLE(4) { |
|
auto [high, low] = GetRegisterPair(OP_REG16(0)); |
|
*low = m_instr[1]; |
|
*high = m_instr[2]; |
|
} |
|
|
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// LD r, n |
|
// LD (HL), n |
|
void SharpLr35902::OpLdRegImm() { |
|
auto dest = OP_REG_X(0); |
|
|
|
if(dest == IDX_HL) { |
|
// LD (HL), n |
|
FETCH_IMM_CYCLE(2); |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
MEM_WRITE(REG_HL, m_instr[1]); |
|
} |
|
NUM_MCYCLES(4); |
|
return; |
|
} |
|
|
|
// LD r, n |
|
FETCH_IMM_CYCLE(2); |
|
CYCLE(3) { |
|
*(GetRegister8(dest)) = m_instr[1]; |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// LD A, (rr) |
|
void SharpLr35902::OpLdRegMem() { |
|
FETCH_ADDR_CYCLE(2, GetRegister16Pointer(OP_REG16(0))); |
|
CYCLE(3) { |
|
m_reg.a = m_instr[1]; |
|
} |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// LD r, r' |
|
// LD r, (HL) |
|
// LD (HL), r |
|
void SharpLr35902::OpLdRegReg() { |
|
auto dest = OP_REG_X(0); |
|
auto src = OP_REG_Y(0); |
|
|
|
if(dest == IDX_HL) { |
|
// LD (HL), r |
|
CYCLE(2) { |
|
MEM_WRITE(REG_HL, *(GetRegister8(src))); |
|
} |
|
NUM_MCYCLES(3); |
|
} else if(src == IDX_HL) { |
|
// LD r, (HL) |
|
FETCH_ADDR_CYCLE(2, IDX_HL); |
|
CYCLE(3) { |
|
*(GetRegister8(dest)) = m_memory->GetByte(REG_HL); |
|
} |
|
NUM_MCYCLES(3); |
|
} else { |
|
// LD r, r' |
|
*(GetRegister8(dest)) = *(GetRegister8(src)); |
|
NUM_MCYCLES(2); |
|
} |
|
} |
|
|
|
// LD SP, HL |
|
void SharpLr35902::OpLdSpHl() { |
|
CYCLE(2) { m_reg.sp &= 0xFF00; m_reg.sp |= m_reg.l; } |
|
CYCLE(3) { m_reg.sp &= 0x00FF; m_reg.sp |= (m_reg.h << 8); } |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// OR r |
|
// OR (HL) |
|
void SharpLr35902::OpOr() { |
|
auto src = OP_REG_Y(0); |
|
|
|
if(src == IDX_HL) { |
|
// OR (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
m_reg.a |= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// OR r |
|
m_reg.a |= *(GetRegister8(src)); |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// POP rr |
|
void SharpLr35902::OpPopReg16() { |
|
auto reg = OP_REG16(0); |
|
uint8_t *high, *low; |
|
|
|
if(reg == IDX_16_SP) { |
|
// AF shares an index with SP for PUSH/POP. |
|
high = &(m_reg.a); |
|
low = &(m_reg.f); |
|
*low &= 0xF0; // Ensure that only the flag bits are considered. |
|
} else { |
|
std::tie(high, low) = GetRegisterPair(reg); |
|
} |
|
|
|
CYCLE(2) { *low = STACK_POP; } |
|
CYCLE(3) { *high = STACK_POP; } |
|
NUM_MCYCLES(4); |
|
} |
|
|
|
// PUSH rr |
|
void SharpLr35902::OpPushReg16() { |
|
auto reg = OP_REG16(0); |
|
uint16_t val; |
|
|
|
if(reg == IDX_16_SP) { |
|
// AF shares an index with SP for PUSH/POP. |
|
// Additionally, only the top four bits of F can be written to. |
|
val = (m_reg.a << 8) | (m_reg.f & 0xF0); |
|
} else { |
|
val = GetRegister16Value(OP_REG16(0)); |
|
} |
|
|
|
CYCLE(3) { STACK_PUSH(val >> 8); } |
|
CYCLE(4) { STACK_PUSH(val & 0xFF); } |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// RET |
|
void SharpLr35902::OpRetUnc() { |
|
SET_PC_STACK(2); |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// RET cc |
|
void SharpLr35902::OpRetCond() { |
|
if(!TestConditional(OP_COND)) { |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
SET_PC_STACK(3); |
|
NUM_MCYCLES(6); |
|
} |
|
|
|
// RETI |
|
void SharpLr35902::OpRetImeUnc() { |
|
SET_PC_STACK(2); |
|
CYCLE(4) { m_ime = ScheduledState::Enabled; } |
|
NUM_MCYCLES(5); |
|
} |
|
|
|
// SCF |
|
void SharpLr35902::OpSetCarry() { |
|
FLAG_SET(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// STOP |
|
void SharpLr35902::OpStop() const { |
|
// :( |
|
std::stringstream ex; |
|
ex << "STOP opcode executed\n\n" << DumpRegisters(); |
|
throw PlipEmulationException(ex.str().c_str()); |
|
} |
|
|
|
// SUB r |
|
// SUB (HL) |
|
void SharpLr35902::OpSub() { |
|
auto src = OP_REG_Y(0); |
|
uint16_t res; |
|
|
|
if(src == IDX_HL) { |
|
// SUB A, (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_reg.a - m_instr[1]; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, m_instr[1]); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// SUB A, r |
|
auto val = *(GetRegister8(src)); |
|
res = m_reg.a - val; |
|
CHECK_CARRY(res); |
|
CHECK_SUB_HALFCARRY(m_reg.a, val); |
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// SBC r |
|
// SBC (HL) |
|
void SharpLr35902::OpSubBorrow() { |
|
auto src = OP_REG_Y(0); |
|
uint16_t res; |
|
uint8_t cf = (FLAG_TEST(CARRY) ? 1 : 0); |
|
|
|
if(src == IDX_HL) { |
|
// SBC A, (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
res = m_reg.a - m_instr[1] - (FLAG_TEST(CARRY) ? 1 : 0); |
|
CHECK_CARRY(res); |
|
|
|
// CHECK_SUB_HALFCARRY won't work here, since the borrow flag needs |
|
// to be accounted for. |
|
if(((m_reg.a & 0xF) - (m_instr[1] & 0xF) - cf) < 0x00) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// SBC A, r |
|
auto val = *(GetRegister8(src)); |
|
res = m_reg.a - val - (FLAG_TEST(CARRY) ? 1 : 0); |
|
CHECK_CARRY(res); |
|
|
|
if(((m_reg.a & 0xF) - (m_instr[1] & 0xF) - cf) < 0x00) |
|
FLAG_SET(HALFCARRY); |
|
else |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
m_reg.a = res & 0xFF; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_SET(SUBTRACT); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
// XOR r |
|
// XOR (HL) |
|
void SharpLr35902::OpXor() { |
|
auto src = OP_REG_Y(0); |
|
|
|
if(src == IDX_HL) { |
|
// XOR (HL) |
|
FETCH_ADDR_CYCLE(2, REG_HL); |
|
CYCLE(3) { |
|
m_reg.a ^= m_instr[1]; |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
} |
|
NUM_MCYCLES(3); |
|
return; |
|
} |
|
|
|
// XOR r |
|
m_reg.a ^= *(GetRegister8(src)); |
|
CHECK_ZERO(m_reg.a); |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
|
|
NUM_MCYCLES(2); |
|
} |
|
|
|
/* |
|
* CB-prefixed opcodes: starts at m_mcycle == 3 |
|
*/ |
|
// RES n, r |
|
// RES n, (HL) |
|
void SharpLr35902::OpBitClear() { |
|
auto idx = OP_REG_X(1); |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// RES n, (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
MEM_WRITE(REG_HL, m_instr[2] &= ~(1 << idx)); |
|
} |
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// RES n, r |
|
*(GetRegister8(reg)) &= ~(1 << idx); |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SET n, r |
|
// SET n, (HL) |
|
void SharpLr35902::OpBitSet() { |
|
auto idx = OP_REG_X(1); |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// SET n, (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
MEM_WRITE(REG_HL, m_instr[2] |= (1 << idx)); |
|
} |
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// SET n, r |
|
*(GetRegister8(reg)) |= (1 << idx); |
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// BIT n, r |
|
// BIT n, (HL) |
|
void SharpLr35902::OpBitTest() { |
|
auto idx = OP_REG_X(1); |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// BIT n, (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_SET(HALFCARRY); |
|
CHECK_ZERO(m_instr[2] & (1 << idx)); |
|
} |
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// BIT n, r |
|
FLAG_CLEAR(SUBTRACT); |
|
FLAG_SET(HALFCARRY); |
|
CHECK_ZERO(*(GetRegister8(reg)) & (1 << idx)); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SWAP n |
|
// SWAP (HL) |
|
void SharpLr35902::OpNibbleSwap() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// SWAP (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
uint8_t res = (m_instr[2] << 4) | (m_instr[2] >> 4); |
|
MEM_WRITE(REG_HL, res); |
|
CHECK_ZERO(res); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// SWAP r |
|
auto r = GetRegister8(reg); |
|
*r = (*r << 4) | (*r >> 4); |
|
CHECK_ZERO(*r); |
|
FLAG_CLEAR(CARRY); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// RLC r |
|
// RLC (HL) |
|
void SharpLr35902::OpRotateLeft() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// RLC (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t msb = res >> 7; |
|
CHECK_BIT_CARRY(msb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res <<= 1; |
|
res |= msb; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// RLC r |
|
auto r = GetRegister8(reg); |
|
uint8_t msb = *r >> 7; |
|
CHECK_BIT_CARRY(msb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r <<= 1; |
|
*r |= msb; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// RL r |
|
// RL (HL) |
|
void SharpLr35902::OpRotateLeftThruCarry() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// RL (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t cf = FLAG_TEST(CARRY) ? 1 : 0; |
|
CHECK_BIT_CARRY(res & 0b10000000); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res <<= 1; |
|
res |= cf; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// RL r |
|
auto r = GetRegister8(reg); |
|
uint8_t cf = FLAG_TEST(CARRY) ? 1 : 0; |
|
CHECK_BIT_CARRY(*r & 0b10000000); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r <<= 1; |
|
*r |= cf; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// RRC r |
|
// RRC (HL) |
|
void SharpLr35902::OpRotateRight() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// RRC (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t lsb = res & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res >>= 1; |
|
res |= (lsb << 7); |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// RRC r |
|
auto r = GetRegister8(reg); |
|
uint8_t lsb = *r & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r >>= 1; |
|
*r |= (lsb << 7); |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// RR r |
|
// RR (HL) |
|
void SharpLr35902::OpRotateRightThruCarry() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// RR (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t cf = FLAG_TEST(CARRY) ? 0b10000000 : 0; |
|
CHECK_BIT_CARRY(res & 0b00000001); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res >>= 1; |
|
res |= cf; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// RR r |
|
auto r = GetRegister8(reg); |
|
uint8_t cf = FLAG_TEST(CARRY) ? 0b10000000 : 0; |
|
CHECK_BIT_CARRY(*r & 0b00000001); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r >>= 1; |
|
*r |= cf; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SLA r |
|
// SLA (HL) |
|
void SharpLr35902::OpShiftLeftArithmetic() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// SLA (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t msb = res & 0b10000000; |
|
CHECK_BIT_CARRY(msb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res <<= 1; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// SLA r |
|
auto r = GetRegister8(reg); |
|
uint8_t msb = *r & 0b10000000; |
|
CHECK_BIT_CARRY(msb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r <<= 1; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SRA r |
|
// SRA (HL) |
|
void SharpLr35902::OpShiftRightArithmetic() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// SRA (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t msb = res & 0b10000000; |
|
uint8_t lsb = res & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res >>= 1; |
|
res |= msb; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// SRA r |
|
auto r = GetRegister8(reg); |
|
uint8_t msb = *r & 0b10000000; |
|
uint8_t lsb = *r & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r >>= 1; |
|
*r |= msb; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
|
|
// SRL r |
|
// SRL (HL) |
|
void SharpLr35902::OpShiftRightLogical() { |
|
auto reg = OP_REG_Y(1); |
|
|
|
if(reg == IDX_HL) { |
|
// SRL (HL) |
|
FETCH_ADDR_CYCLE(3, REG_HL); |
|
CYCLE(4) { |
|
auto res = m_instr[2]; |
|
uint8_t lsb = res & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
res >>= 1; |
|
CHECK_ZERO(res); |
|
MEM_WRITE(REG_HL, res); |
|
} |
|
|
|
NUM_MCYCLES(5); |
|
return; |
|
} |
|
|
|
// SRL r |
|
auto r = GetRegister8(reg); |
|
uint8_t lsb = *r & 0b00000001; |
|
CHECK_BIT_CARRY(lsb); |
|
FLAG_CLEAR(HALFCARRY); |
|
FLAG_CLEAR(SUBTRACT); |
|
*r >>= 1; |
|
CHECK_ZERO(*r); |
|
|
|
NUM_MCYCLES(3); |
|
} |
|
}
|
|
|