Emulator: Implement SUB/SBC opcodes
This commit is contained in:
+162
-39
@@ -208,6 +208,22 @@ void CPU::update()
|
|||||||
case 0x8d: adc8(); break;
|
case 0x8d: adc8(); break;
|
||||||
case 0x8e: adc8(); break;
|
case 0x8e: adc8(); break;
|
||||||
case 0x8f: adc8(); break;
|
case 0x8f: adc8(); break;
|
||||||
|
case 0x90: sub8(); break;
|
||||||
|
case 0x91: sub8(); break;
|
||||||
|
case 0x92: sub8(); break;
|
||||||
|
case 0x93: sub8(); break;
|
||||||
|
case 0x94: sub8(); break;
|
||||||
|
case 0x95: sub8(); break;
|
||||||
|
case 0x96: sub8(); break;
|
||||||
|
case 0x97: sub8(); break;
|
||||||
|
case 0x98: sbc8(); break;
|
||||||
|
case 0x99: sbc8(); break;
|
||||||
|
case 0x9a: sbc8(); break;
|
||||||
|
case 0x9b: sbc8(); break;
|
||||||
|
case 0x9c: sbc8(); break;
|
||||||
|
case 0x9d: sbc8(); break;
|
||||||
|
case 0x9e: sbc8(); break;
|
||||||
|
case 0x9f: sbc8(); break;
|
||||||
case 0xa0: and8(); break;
|
case 0xa0: and8(); break;
|
||||||
case 0xa1: and8(); break;
|
case 0xa1: and8(); break;
|
||||||
case 0xa2: and8(); break;
|
case 0xa2: and8(); break;
|
||||||
@@ -240,8 +256,10 @@ void CPU::update()
|
|||||||
case 0xd1: pop(); break;
|
case 0xd1: pop(); break;
|
||||||
case 0xd4: call(); break;
|
case 0xd4: call(); break;
|
||||||
case 0xd5: push(); break;
|
case 0xd5: push(); break;
|
||||||
|
case 0xd6: sub8(); break;
|
||||||
case 0xd7: rst(); break;
|
case 0xd7: rst(); break;
|
||||||
case 0xdc: call(); break;
|
case 0xdc: call(); break;
|
||||||
|
case 0xde: sbc8(); break;
|
||||||
case 0xdf: rst(); break;
|
case 0xdf: rst(); break;
|
||||||
case 0xe0: ldffi8(); break;
|
case 0xe0: ldffi8(); break;
|
||||||
case 0xe1: pop(); break;
|
case 0xe1: pop(); break;
|
||||||
@@ -420,6 +438,54 @@ void CPU::and8()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::cp()
|
||||||
|
{
|
||||||
|
auto compare = [this](uint32_t register_) -> void {
|
||||||
|
// CP A,r8, flags: Z 1 H C
|
||||||
|
m_wait_cycles += 4;
|
||||||
|
|
||||||
|
// Subtract the value in r8 from A and set flags accordingly,
|
||||||
|
// but don't store the result
|
||||||
|
|
||||||
|
// Set flags
|
||||||
|
// Zero flag
|
||||||
|
m_zf = ((m_a - register_) & 0xff) == 0;
|
||||||
|
m_nf = 1;
|
||||||
|
// Note: for negation between two positive numbers the carry result is flipped!
|
||||||
|
m_hf = !isCarry(m_a, register_, 0x10);
|
||||||
|
m_cf = !isCarry(m_a, register_, 0x100);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t opcode = pcRead();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0xb8: /* CP A,B */ compare(m_b); break;
|
||||||
|
case 0xb9: /* CP A,C */ compare(m_c); break;
|
||||||
|
case 0xba: /* CP A,D */ compare(m_d); break;
|
||||||
|
case 0xbb: /* CP A,E */ compare(m_e); break;
|
||||||
|
case 0xbc: /* CP A,H */ compare(m_h); break;
|
||||||
|
case 0xbd: /* CP A,L */ compare(m_l); break;
|
||||||
|
case 0xbe: /* CP A,(HL) */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the byte pointed to by HL from A and set flags accordingly,
|
||||||
|
// but don't store the result
|
||||||
|
compare(read(hl()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xbf: /* CP A,A */ compare(m_a); break;
|
||||||
|
case 0xfe: /* CP A,i8 */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the value i8 from A and set flags accordingly,
|
||||||
|
// but don't store the result
|
||||||
|
compare(pcRead());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::dec8()
|
void CPU::dec8()
|
||||||
{
|
{
|
||||||
auto decrement = [this](uint32_t& register_) -> void {
|
auto decrement = [this](uint32_t& register_) -> void {
|
||||||
@@ -500,6 +566,102 @@ void CPU::inc8()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::sbc8()
|
||||||
|
{
|
||||||
|
auto subtract_carry = [this](uint32_t register_) -> void {
|
||||||
|
// SBC A,r8, flags: Z 1 H C
|
||||||
|
m_wait_cycles += 4;
|
||||||
|
|
||||||
|
uint32_t old_carry = m_cf;
|
||||||
|
|
||||||
|
// Set flags
|
||||||
|
m_nf = 1;
|
||||||
|
// Note: for negation between two positive numbers the carry result is flipped!
|
||||||
|
m_hf = !isCarry(m_a, register_, 0x10);
|
||||||
|
m_cf = !isCarry(m_a, (register_ + (old_carry) ? 1 : 0), 0x100);
|
||||||
|
|
||||||
|
// Subtract the value in r8 and the carry flag from A
|
||||||
|
m_a = (m_a - register_ - (old_carry) ? 1 : 0) & 0xff;
|
||||||
|
|
||||||
|
// Zero flag
|
||||||
|
m_zf = m_a == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t opcode = pcRead();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x98: /* SBC A,B */ subtract_carry(m_b); break;
|
||||||
|
case 0x99: /* SBC A,C */ subtract_carry(m_c); break;
|
||||||
|
case 0x9a: /* SBC A,D */ subtract_carry(m_d); break;
|
||||||
|
case 0x9b: /* SBC A,E */ subtract_carry(m_e); break;
|
||||||
|
case 0x9c: /* SBC A,H */ subtract_carry(m_h); break;
|
||||||
|
case 0x9d: /* SBC A,L */ subtract_carry(m_l); break;
|
||||||
|
case 0x9e: /* SBC A,(HL) */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the byte pointed to by HL and the carry flag from A
|
||||||
|
subtract_carry(read(hl()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x9f: /* SBC A,A */ subtract_carry(m_a); break;
|
||||||
|
case 0xde: /* SBC A,i8 */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the value i8 and the carry flag from A
|
||||||
|
subtract_carry(pcRead());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::sub8()
|
||||||
|
{
|
||||||
|
auto subtract = [this](uint32_t register_) -> void {
|
||||||
|
// SUB A,r8, flags: Z 1 H C
|
||||||
|
m_wait_cycles += 4;
|
||||||
|
|
||||||
|
// Set flags
|
||||||
|
m_nf = 1;
|
||||||
|
// Note: for negation between two positive numbers the carry result is flipped!
|
||||||
|
m_hf = !isCarry(m_a, register_, 0x10);
|
||||||
|
m_cf = !isCarry(m_a, register_, 0x100);
|
||||||
|
|
||||||
|
// Subtract the value in r8 from A
|
||||||
|
m_a = (m_a - register_) & 0xff;
|
||||||
|
|
||||||
|
// Zero flag
|
||||||
|
m_zf = m_a == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t opcode = pcRead();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x90: /* SUB A,B */ subtract(m_b); break;
|
||||||
|
case 0x91: /* SUB A,C */ subtract(m_c); break;
|
||||||
|
case 0x92: /* SUB A,D */ subtract(m_d); break;
|
||||||
|
case 0x93: /* SUB A,E */ subtract(m_e); break;
|
||||||
|
case 0x94: /* SUB A,H */ subtract(m_h); break;
|
||||||
|
case 0x95: /* SUB A,L */ subtract(m_l); break;
|
||||||
|
case 0x96: /* SUB A,(HL) */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the byte pointed to by HL from A
|
||||||
|
subtract(read(hl()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x97: /* SUB A,A */ subtract(m_a); break;
|
||||||
|
case 0xd6: /* SUB A,i8 */ {
|
||||||
|
m_wait_cycles += 4; // + 4 = 8 total
|
||||||
|
|
||||||
|
// Subtract the value i8 from A
|
||||||
|
subtract(pcRead());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::xor8()
|
void CPU::xor8()
|
||||||
{
|
{
|
||||||
uint8_t opcode = pcRead();
|
uint8_t opcode = pcRead();
|
||||||
@@ -935,45 +1097,6 @@ void CPU::ra()
|
|||||||
m_hf = 0;
|
m_hf = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::cp()
|
|
||||||
{
|
|
||||||
uint8_t opcode = pcRead();
|
|
||||||
uint8_t value = 0;
|
|
||||||
switch (opcode) {
|
|
||||||
case 0xb8: /* CP B */ value = m_b; break;
|
|
||||||
case 0xb9: /* CP C */ value = m_c; break;
|
|
||||||
case 0xba: /* CP D */ value = m_d; break;
|
|
||||||
case 0xbb: /* CP E */ value = m_e; break;
|
|
||||||
case 0xbc: /* CP H */ value = m_h; break;
|
|
||||||
case 0xbd: /* CP L */ value = m_l; break;
|
|
||||||
case 0xbe: // CP,(HL)
|
|
||||||
m_wait_cycles += 4;
|
|
||||||
value = read(hl());
|
|
||||||
break;
|
|
||||||
case 0xbf: // CP A, flags: 1 1 0 0
|
|
||||||
m_wait_cycles += 4;
|
|
||||||
// A - A
|
|
||||||
m_zf = m_nf = 1;
|
|
||||||
m_hf = m_cf = 0;
|
|
||||||
return; // Early return as we already set the flags
|
|
||||||
case 0xfe: // CP i8
|
|
||||||
m_wait_cycles += 4;
|
|
||||||
value = pcRead();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
// CP r8, flags: Z 1 H C
|
|
||||||
m_wait_cycles += 4;
|
|
||||||
|
|
||||||
// Set flags
|
|
||||||
m_zf = ((m_a - value) & 0xff) == 0;
|
|
||||||
m_nf = 1;
|
|
||||||
m_hf = isCarry(m_a, value, 0x10);
|
|
||||||
m_cf = isCarry(m_a, value, 0x100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPU::ldffi8()
|
void CPU::ldffi8()
|
||||||
{
|
{
|
||||||
uint8_t opcode = pcRead();
|
uint8_t opcode = pcRead();
|
||||||
|
|||||||
@@ -31,8 +31,11 @@ public:
|
|||||||
void adc8();
|
void adc8();
|
||||||
void add8();
|
void add8();
|
||||||
void and8();
|
void and8();
|
||||||
|
void cp();
|
||||||
void dec8();
|
void dec8();
|
||||||
void inc8();
|
void inc8();
|
||||||
|
void sbc8();
|
||||||
|
void sub8();
|
||||||
void xor8();
|
void xor8();
|
||||||
|
|
||||||
// 16-bit
|
// 16-bit
|
||||||
@@ -65,7 +68,6 @@ public:
|
|||||||
// Load Instructions
|
// Load Instructions
|
||||||
|
|
||||||
// 8-bit
|
// 8-bit
|
||||||
void cp();
|
|
||||||
void lda8();
|
void lda8();
|
||||||
void ldffi8();
|
void ldffi8();
|
||||||
void ldi8();
|
void ldi8();
|
||||||
|
|||||||
Reference in New Issue
Block a user