2021-06-07 01:47:11 +00:00
|
|
|
use crate::bus::BusIo;
|
|
|
|
use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair};
|
2021-01-18 08:47:41 +00:00
|
|
|
use std::{convert::TryFrom, fmt::Debug};
|
2020-12-23 07:17:13 +00:00
|
|
|
|
2021-06-02 06:50:16 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-03-16 06:05:13 +00:00
|
|
|
#[allow(clippy::upper_case_acronyms)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum Instruction {
|
2020-08-30 04:07:53 +00:00
|
|
|
NOP,
|
|
|
|
LD(LDTarget, LDTarget),
|
|
|
|
STOP,
|
|
|
|
JR(JumpCondition, i8),
|
|
|
|
ADD(MATHTarget, MATHTarget),
|
2020-09-02 22:26:46 +00:00
|
|
|
INC(Registers),
|
|
|
|
DEC(Registers),
|
2020-08-30 04:07:53 +00:00
|
|
|
RLCA,
|
|
|
|
RRCA,
|
|
|
|
RLA,
|
|
|
|
RRA,
|
|
|
|
DAA,
|
|
|
|
CPL,
|
|
|
|
SCF,
|
|
|
|
CCF,
|
|
|
|
HALT,
|
2020-09-08 01:28:24 +00:00
|
|
|
ADC(MATHTarget), // ADC A, MATHTarget
|
|
|
|
SUB(MATHTarget), // SUB A, MATHTarget
|
|
|
|
SBC(MATHTarget),
|
|
|
|
AND(MATHTarget), // AND A, MATHTarget
|
|
|
|
XOR(MATHTarget), // XOR A, MATHTarget
|
|
|
|
OR(MATHTarget), // OR A, MATHTarget
|
|
|
|
CP(MATHTarget), // CP A, MATHTarget
|
2020-08-30 04:07:53 +00:00
|
|
|
RET(JumpCondition),
|
|
|
|
LDHL(i8), // LD HL, SP + d
|
|
|
|
POP(RegisterPair),
|
|
|
|
RETI,
|
|
|
|
JP(JumpCondition, JPTarget),
|
|
|
|
DI,
|
|
|
|
EI,
|
|
|
|
CALL(JumpCondition, u16),
|
|
|
|
PUSH(RegisterPair),
|
|
|
|
RST(u8),
|
2020-09-08 02:18:53 +00:00
|
|
|
RLC(InstrRegister),
|
|
|
|
RRC(InstrRegister),
|
|
|
|
RL(InstrRegister),
|
|
|
|
RR(InstrRegister),
|
|
|
|
SLA(InstrRegister),
|
|
|
|
SRA(InstrRegister),
|
|
|
|
SWAP(InstrRegister),
|
|
|
|
SRL(InstrRegister),
|
|
|
|
BIT(u8, InstrRegister),
|
|
|
|
RES(u8, InstrRegister),
|
|
|
|
SET(u8, InstrRegister),
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-24 01:39:37 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum JPTarget {
|
2020-12-23 07:17:13 +00:00
|
|
|
RegisterPair(RegisterPair),
|
|
|
|
ImmediateWord(u16),
|
|
|
|
}
|
|
|
|
|
2021-01-18 08:47:41 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum Registers {
|
2020-12-23 07:17:13 +00:00
|
|
|
Byte(InstrRegister),
|
|
|
|
Word(RegisterPair),
|
|
|
|
}
|
|
|
|
|
2021-01-18 08:22:45 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum MATHTarget {
|
2020-12-23 07:17:13 +00:00
|
|
|
Register(InstrRegister),
|
|
|
|
RegisterPair(RegisterPair),
|
|
|
|
ImmediateByte(u8),
|
|
|
|
}
|
|
|
|
|
2020-12-24 01:39:37 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum LDTarget {
|
2020-12-23 07:23:38 +00:00
|
|
|
IndirectC,
|
2020-12-23 07:17:13 +00:00
|
|
|
Register(InstrRegister),
|
|
|
|
IndirectRegister(InstrRegisterPair),
|
|
|
|
ByteAtAddress(u16),
|
|
|
|
ImmediateWord(u16),
|
|
|
|
ImmediateByte(u8),
|
|
|
|
RegisterPair(RegisterPair),
|
|
|
|
ByteAtAddressWithOffset(u8),
|
|
|
|
}
|
|
|
|
|
2021-01-18 08:29:35 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum InstrRegisterPair {
|
2020-12-23 07:17:13 +00:00
|
|
|
AF,
|
|
|
|
BC,
|
|
|
|
DE,
|
|
|
|
HL,
|
|
|
|
SP,
|
|
|
|
PC,
|
|
|
|
IncrementHL,
|
|
|
|
DecrementHL,
|
|
|
|
}
|
|
|
|
|
2021-06-02 06:50:16 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum InstrRegister {
|
2020-12-23 07:17:13 +00:00
|
|
|
A,
|
|
|
|
B,
|
|
|
|
C,
|
|
|
|
D,
|
|
|
|
E,
|
|
|
|
H,
|
|
|
|
L,
|
|
|
|
IndirectHL, // (HL)
|
2020-12-23 07:23:38 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 06:50:16 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) enum JumpCondition {
|
2020-12-23 07:17:13 +00:00
|
|
|
NotZero,
|
|
|
|
Zero,
|
|
|
|
NotCarry,
|
|
|
|
Carry,
|
|
|
|
Always,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
struct Table;
|
|
|
|
|
2021-03-21 00:55:02 +00:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
2021-06-03 04:09:45 +00:00
|
|
|
#[repr(transparent)]
|
2021-03-27 17:10:18 +00:00
|
|
|
pub struct Cycle(u32);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
impl Instruction {
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
|
2020-09-01 05:16:05 +00:00
|
|
|
match instruction {
|
2021-03-27 17:10:18 +00:00
|
|
|
Instruction::NOP => Cycle::new(4),
|
2020-09-01 05:16:05 +00:00
|
|
|
Instruction::LD(lhs, rhs) => match (lhs, rhs) {
|
|
|
|
(LDTarget::ByteAtAddress(nn), LDTarget::RegisterPair(RegisterPair::SP)) => {
|
|
|
|
// LD (nn), SP | Put Stack Pointer at address nn
|
|
|
|
cpu.write_word(nn, cpu.register_pair(RegisterPair::SP));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(20)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-03 00:35:48 +00:00
|
|
|
(LDTarget::RegisterPair(pair), LDTarget::ImmediateWord(nn)) => {
|
2020-09-01 05:16:05 +00:00
|
|
|
// LD rp[p], nn | Put value nn into register pair
|
2021-03-23 07:11:40 +00:00
|
|
|
use RegisterPair::*;
|
|
|
|
|
2020-09-03 00:35:48 +00:00
|
|
|
match pair {
|
2021-03-23 07:11:40 +00:00
|
|
|
BC | DE | HL | SP => cpu.set_register_pair(pair, nn),
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"LD {:?}, nn\" instruction", pair),
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
(LDTarget::IndirectRegister(pair), LDTarget::Register(InstrRegister::A)) => {
|
2020-09-01 05:16:05 +00:00
|
|
|
let a = cpu.register(Register::A);
|
|
|
|
match pair {
|
2020-09-03 00:35:48 +00:00
|
|
|
InstrRegisterPair::BC | InstrRegisterPair::DE => {
|
2020-09-01 05:16:05 +00:00
|
|
|
// LD (BC), A | Put A into memory address BC
|
|
|
|
// LD (DE), A | Put A into memory address DE
|
2021-03-23 02:48:12 +00:00
|
|
|
let addr = cpu.register_pair(pair.to_register_pair());
|
2020-09-01 05:16:05 +00:00
|
|
|
cpu.write_byte(addr, a);
|
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
InstrRegisterPair::IncrementHL => {
|
2021-03-24 01:22:11 +00:00
|
|
|
// LD (HL+), A | Put A into byte at address HL, then increment HL
|
2020-09-01 05:16:05 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
cpu.write_byte(addr, a);
|
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::HL, addr + 1);
|
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
InstrRegisterPair::DecrementHL => {
|
2021-03-24 01:22:11 +00:00
|
|
|
// LD (HL-), A | Put A into byte at address HL, then decrement HL
|
2020-09-01 05:16:05 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
cpu.write_byte(addr, a);
|
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::HL, addr - 1);
|
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"LD ({:?}), A\" instruction", pair),
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectRegister(pair)) => {
|
2020-09-01 05:16:05 +00:00
|
|
|
match pair {
|
2020-09-03 00:35:48 +00:00
|
|
|
InstrRegisterPair::BC | InstrRegisterPair::DE => {
|
2020-09-01 05:16:05 +00:00
|
|
|
// LD A, (BC) | Put value at address BC into A
|
|
|
|
// LD A, (DE) | Put value at address DE into A
|
2021-03-23 02:48:12 +00:00
|
|
|
let addr = cpu.register_pair(pair.to_register_pair());
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(addr);
|
|
|
|
cpu.set_register(Register::A, byte);
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
InstrRegisterPair::IncrementHL => {
|
2020-09-01 05:16:05 +00:00
|
|
|
// LD A, (HL+) | Put value at address HL into A, then increment HL
|
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(addr);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(Register::A, byte);
|
2020-09-01 05:16:05 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::HL, addr + 1);
|
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
InstrRegisterPair::DecrementHL => {
|
2020-09-01 05:16:05 +00:00
|
|
|
// LD A, (HL-) | Put value at address HL into A, then increment HL
|
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(addr);
|
|
|
|
cpu.set_register(Register::A, byte);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::HL, addr - 1);
|
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"LD A, ({:?})\" instruction", pair),
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
(LDTarget::Register(reg), LDTarget::ImmediateByte(n)) => {
|
|
|
|
// LD r[y], n | Store n in Register
|
2021-03-23 23:21:59 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2020-09-01 05:16:05 +00:00
|
|
|
match reg {
|
2021-03-23 23:21:59 +00:00
|
|
|
A | B | C | D | E | H | L => {
|
|
|
|
cpu.set_register(reg.to_register(), n);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2021-03-23 23:21:59 +00:00
|
|
|
}
|
|
|
|
IndirectHL => {
|
2020-09-01 05:16:05 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
cpu.write_byte(addr, n);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-23 07:23:38 +00:00
|
|
|
(LDTarget::IndirectC, LDTarget::Register(InstrRegister::A)) => {
|
2020-09-08 01:19:10 +00:00
|
|
|
// LD (0xFF00 + C), A | Store value of register A at address 0xFF00 + C
|
|
|
|
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
|
|
|
cpu.write_byte(addr, cpu.register(Register::A));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2020-12-23 07:23:38 +00:00
|
|
|
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectC) => {
|
2020-09-08 01:19:10 +00:00
|
|
|
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(addr);
|
|
|
|
cpu.set_register(Register::A, byte);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2020-09-03 02:54:58 +00:00
|
|
|
(LDTarget::Register(lhs), LDTarget::Register(rhs)) => {
|
|
|
|
// LD r[y], r[z] | Store value of RHS Register in LHS Register
|
2021-03-23 07:11:40 +00:00
|
|
|
use InstrRegister::*;
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
match rhs {
|
|
|
|
B | C | D | E | H | L | A => {
|
|
|
|
let right = cpu.register(rhs.to_register());
|
|
|
|
|
|
|
|
match lhs {
|
|
|
|
B | C | D | E | H | L | A => {
|
|
|
|
cpu.set_register(lhs.to_register(), right);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
|
|
|
IndirectHL => {
|
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
cpu.write_byte(addr, right);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
2021-01-17 23:31:45 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
IndirectHL => {
|
2021-01-17 23:31:45 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
2021-03-23 07:11:40 +00:00
|
|
|
let right = cpu.read_byte(addr);
|
|
|
|
|
|
|
|
match lhs {
|
|
|
|
B | C | D | E | H | L | A => {
|
|
|
|
cpu.set_register(lhs.to_register(), right);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
|
|
|
IndirectHL => {
|
|
|
|
unreachable!(
|
|
|
|
"There is no \"LD ({:?}), ({:?})\" instruction",
|
|
|
|
lhs, rhs
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-01-17 23:31:45 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2020-09-04 05:41:19 +00:00
|
|
|
(LDTarget::ByteAtAddressWithOffset(n), LDTarget::Register(InstrRegister::A)) => {
|
|
|
|
// LD (0xFF00 + n), A | Store register A at address (0xFF00 + n)
|
|
|
|
cpu.write_byte(0xFF00 + (n as u16), cpu.register(Register::A));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddressWithOffset(n)) => {
|
|
|
|
// LD A, (0xFF00 + n) | Store value at address (0xFF00 + n) in register A
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(0xFF00 + (n as u16));
|
|
|
|
cpu.set_register(Register::A, byte);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
(
|
|
|
|
LDTarget::RegisterPair(RegisterPair::SP),
|
|
|
|
LDTarget::RegisterPair(RegisterPair::HL),
|
|
|
|
) => {
|
|
|
|
// LD SP, HL | Load Register HL into Register SP
|
|
|
|
cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
2020-09-08 01:19:10 +00:00
|
|
|
(LDTarget::ByteAtAddress(nn), LDTarget::Register(InstrRegister::A)) => {
|
|
|
|
cpu.write_byte(nn, cpu.register(Register::A));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddress(nn)) => {
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = cpu.read_byte(nn);
|
|
|
|
cpu.set_register(Register::A, byte);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"LD {:?}, {:?}\" instruction", lhs, rhs),
|
2020-09-01 05:16:05 +00:00
|
|
|
},
|
2021-03-27 17:10:18 +00:00
|
|
|
Instruction::STOP => Cycle::new(4),
|
2020-09-01 05:16:05 +00:00
|
|
|
Instruction::JR(cond, offset) => {
|
|
|
|
// JR cc[y - 4], d | If condition is true, then add d to current address and jump
|
|
|
|
// JR d | Add d to current address and jump
|
2021-03-16 04:35:20 +00:00
|
|
|
let flags: &Flags = cpu.flags();
|
2021-03-23 07:11:40 +00:00
|
|
|
|
|
|
|
let prev = cpu.register_pair(RegisterPair::PC);
|
2021-05-04 05:50:22 +00:00
|
|
|
let addr = prev.wrapping_add(offset as u16);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
match cond {
|
|
|
|
JumpCondition::Always => {
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
JumpCondition::NotZero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.z() {
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Zero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.z() {
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::NotCarry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.c() {
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Carry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.c() {
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::ADD(lhs, rhs) => match (lhs, rhs) {
|
|
|
|
(MATHTarget::RegisterPair(RegisterPair::HL), MATHTarget::RegisterPair(pair)) => {
|
|
|
|
// ADD HL, rp[p] | add register pair to HL.
|
2021-03-24 01:22:11 +00:00
|
|
|
use RegisterPair::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
match pair {
|
2021-03-24 01:22:11 +00:00
|
|
|
BC | DE | HL | SP => {
|
2020-09-03 00:35:48 +00:00
|
|
|
let hl_value = cpu.register_pair(RegisterPair::HL);
|
2020-12-23 09:25:16 +00:00
|
|
|
let value = cpu.register_pair(pair);
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add_u16(hl_value, value, &mut flags);
|
2020-09-03 00:35:48 +00:00
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::HL, sum);
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"ADD HL, {:?}\" instruction", pair),
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-03 02:54:58 +00:00
|
|
|
(MATHTarget::Register(InstrRegister::A), MATHTarget::Register(reg)) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// ADD A, r[z] | Add (A + r[z]) to register A
|
2021-03-22 02:16:23 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
2021-03-22 02:16:23 +00:00
|
|
|
|
|
|
|
let (cycles, sum) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-03-23 23:21:59 +00:00
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), sum)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-22 02:16:23 +00:00
|
|
|
IndirectHL => {
|
2021-03-23 23:21:59 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), sum)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-22 02:16:23 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
|
|
|
cpu.set_register(Register::A, sum);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-04 05:41:19 +00:00
|
|
|
(MATHTarget::RegisterPair(RegisterPair::SP), MATHTarget::ImmediateByte(d)) => {
|
|
|
|
// ADD SP, d | Add d (is signed) to register pair SP.
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-04 05:41:19 +00:00
|
|
|
let d = d as i8;
|
2021-04-07 07:27:57 +00:00
|
|
|
|
2020-09-04 05:41:19 +00:00
|
|
|
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
2021-04-07 07:27:57 +00:00
|
|
|
|
|
|
|
cpu.set_flags(flags);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::SP, sum);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
(MATHTarget::Register(InstrRegister::A), MATHTarget::ImmediateByte(n)) => {
|
|
|
|
// ADD A, n | Add n to register A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add(cpu.register(Register::A), n, &mut flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
|
|
|
|
cpu.set_register(Register::A, sum);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"ADD {:?}, {:?}\" instruction", lhs, rhs),
|
2020-09-01 05:16:05 +00:00
|
|
|
},
|
2020-12-23 07:11:03 +00:00
|
|
|
Instruction::INC(registers) => {
|
|
|
|
match registers {
|
|
|
|
Registers::Byte(reg) => {
|
|
|
|
// INC r[y] | Increment Register
|
2021-03-23 07:11:40 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-03-23 07:11:40 +00:00
|
|
|
|
|
|
|
let cycles = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let reg = reg.to_register();
|
2020-09-03 00:35:48 +00:00
|
|
|
|
2020-12-23 07:11:03 +00:00
|
|
|
let value = cpu.register(reg);
|
|
|
|
cpu.set_register(reg, Self::inc_register(value, &mut flags));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-12-23 07:11:03 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 07:11:03 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
2020-12-23 09:25:16 +00:00
|
|
|
let byte = Self::inc_register(cpu.read_byte(addr), &mut flags);
|
|
|
|
cpu.write_byte(addr, byte);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-12-23 07:11:03 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
};
|
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 07:11:03 +00:00
|
|
|
cycles
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-12-23 07:11:03 +00:00
|
|
|
Registers::Word(pair) => {
|
|
|
|
// INC rp[p] | Increment Register Pair
|
2021-03-23 07:11:40 +00:00
|
|
|
// Note: According to RGBDS, no flags are set here.
|
|
|
|
use RegisterPair::*;
|
|
|
|
|
2020-12-23 07:11:03 +00:00
|
|
|
match pair {
|
2021-03-23 07:11:40 +00:00
|
|
|
BC | DE | HL | SP => {
|
2020-12-23 07:11:03 +00:00
|
|
|
let value = cpu.register_pair(pair);
|
2021-04-07 07:27:57 +00:00
|
|
|
cpu.set_register_pair(pair, value.wrapping_add(1));
|
2020-12-23 07:11:03 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"INC {:?}\" instruction", pair),
|
2020-12-23 07:11:03 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
Instruction::DEC(registers) => {
|
|
|
|
match registers {
|
|
|
|
Registers::Byte(reg) => {
|
|
|
|
// DEC r[y] | Decrement Register
|
|
|
|
use InstrRegister::*;
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
|
|
|
|
|
|
|
let cycles = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
|
|
|
let reg = reg.to_register();
|
|
|
|
|
|
|
|
let value = cpu.register(reg);
|
|
|
|
cpu.set_register(reg, Self::dec_register(value, &mut flags));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
|
|
|
IndirectHL => {
|
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let byte = cpu.read_byte(addr);
|
|
|
|
cpu.write_byte(addr, Self::dec_register(byte, &mut flags));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
|
|
|
};
|
2020-09-03 00:35:48 +00:00
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_flags(flags);
|
|
|
|
cycles
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
Registers::Word(pair) => {
|
|
|
|
// DEC rp[p] | Decrement Register Pair
|
|
|
|
use RegisterPair::*;
|
|
|
|
|
|
|
|
match pair {
|
|
|
|
BC | DE | HL | SP => {
|
|
|
|
let value = cpu.register_pair(pair);
|
2021-04-07 07:27:57 +00:00
|
|
|
cpu.set_register_pair(pair, value.wrapping_sub(1));
|
2021-03-23 07:11:40 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!("There is no \"DEC {:?}\" instruction", pair),
|
|
|
|
};
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::RLCA => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Rotate Register A left
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
let a = cpu.register(Register::A);
|
2020-09-08 03:34:09 +00:00
|
|
|
let msb = a >> 7;
|
|
|
|
let rot_a = a.rotate_left(1);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(false, false, false, msb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-02 22:26:46 +00:00
|
|
|
cpu.set_register(Register::A, rot_a);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
Instruction::RRCA => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Rotate Register A right
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-01 05:16:05 +00:00
|
|
|
|
|
|
|
let a = cpu.register(Register::A);
|
2020-09-08 03:34:09 +00:00
|
|
|
let lsb = a & 0x01;
|
|
|
|
let rot_a = a.rotate_right(1);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(false, false, false, lsb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-02 22:26:46 +00:00
|
|
|
cpu.set_register(Register::A, rot_a);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
Instruction::RLA => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Rotate register A left through carry
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-02 22:26:46 +00:00
|
|
|
|
|
|
|
let a = cpu.register(Register::A);
|
2021-03-16 04:35:20 +00:00
|
|
|
let (rot_a, carry) = Self::rl_thru_carry(a, flags.c());
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2020-12-23 06:24:29 +00:00
|
|
|
flags.update(false, false, false, carry);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-02 22:26:46 +00:00
|
|
|
cpu.set_register(Register::A, rot_a);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-02 22:26:46 +00:00
|
|
|
}
|
|
|
|
Instruction::RRA => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Rotate register A right through carry
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-02 22:26:46 +00:00
|
|
|
|
|
|
|
let a = cpu.register(Register::A);
|
2021-03-16 04:35:20 +00:00
|
|
|
let (rot_a, carry) = Self::rr_thru_carry(a, flags.c());
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2020-12-23 06:24:29 +00:00
|
|
|
flags.update(false, false, false, carry);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-02 22:26:46 +00:00
|
|
|
cpu.set_register(Register::A, rot_a);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-02 22:26:46 +00:00
|
|
|
}
|
2021-04-04 06:03:44 +00:00
|
|
|
Instruction::DAA => {
|
|
|
|
// source: https://ehaskins.com/2018-01-30%20Z80%20DAA/
|
|
|
|
// TODO: Maybe i16 isn't the right choice here?
|
|
|
|
|
|
|
|
let mut correction: i16 = 0;
|
|
|
|
let mut value = cpu.register(Register::A) as i16;
|
|
|
|
let mut flags = *cpu.flags();
|
|
|
|
|
|
|
|
if flags.h() || (!flags.n() && (value & 0xF) > 9) {
|
|
|
|
correction |= 0x06;
|
|
|
|
}
|
|
|
|
|
|
|
|
if flags.c() || (!flags.n() && value > 0x99) {
|
|
|
|
correction |= 0x60;
|
|
|
|
flags.set_c(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
value += if flags.n() { -correction } else { correction };
|
|
|
|
let result = value as u8;
|
|
|
|
|
|
|
|
flags.set_z(result == 0);
|
|
|
|
flags.set_h(false);
|
|
|
|
|
|
|
|
cpu.set_flags(flags);
|
|
|
|
cpu.set_register(Register::A, value as u8);
|
|
|
|
|
|
|
|
Cycle::new(4)
|
|
|
|
}
|
2020-09-02 22:26:46 +00:00
|
|
|
Instruction::CPL => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Compliment A register (inverse)
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-02 22:26:46 +00:00
|
|
|
let a = cpu.register(Register::A);
|
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
flags.set_n(true);
|
|
|
|
flags.set_h(true);
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-02 22:26:46 +00:00
|
|
|
cpu.set_register(Register::A, !a); // Bitwise not is ! instead of ~
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-02 22:26:46 +00:00
|
|
|
}
|
|
|
|
Instruction::SCF => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Set Carry Flag
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
flags.set_n(false);
|
|
|
|
flags.set_h(false);
|
|
|
|
flags.set_c(true);
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-02 22:26:46 +00:00
|
|
|
}
|
|
|
|
Instruction::CCF => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// Compliment Carry Flag (inverse)
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
flags.set_n(false);
|
|
|
|
flags.set_h(false);
|
|
|
|
flags.set_c(!flags.c());
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-02 22:26:46 +00:00
|
|
|
}
|
2021-03-24 04:05:27 +00:00
|
|
|
Instruction::HALT => {
|
|
|
|
// Enter CPU low power consumption mode until interrupt occurs
|
|
|
|
use HaltState::*;
|
|
|
|
|
|
|
|
let req = cpu.read_byte(0xFF0F);
|
|
|
|
let enabled = cpu.read_byte(0xFFFF);
|
|
|
|
|
2021-04-05 05:52:12 +00:00
|
|
|
let halt_state = if let ImeState::Enabled = cpu.ime() {
|
2021-04-05 05:53:46 +00:00
|
|
|
ImeEnabled
|
2021-04-04 06:19:39 +00:00
|
|
|
} else if req & enabled != 0 {
|
|
|
|
SomePending
|
2021-03-24 04:05:27 +00:00
|
|
|
} else {
|
2021-04-04 06:19:39 +00:00
|
|
|
NonePending
|
2021-03-24 04:05:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
cpu.halt(halt_state);
|
|
|
|
|
|
|
|
// Though this can actually last forever
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2021-03-24 04:05:27 +00:00
|
|
|
}
|
2020-09-08 01:28:24 +00:00
|
|
|
Instruction::ADC(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// ADC A, r[z] | Add register r[z] plus the Carry flag to A
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-04-08 03:37:33 +00:00
|
|
|
let left = cpu.register(Register::A);
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, sum) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-04-08 03:37:33 +00:00
|
|
|
let right = cpu.register(reg.to_register());
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), sum)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2021-04-08 03:37:33 +00:00
|
|
|
let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), sum)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
|
|
|
|
2020-09-03 02:54:58 +00:00
|
|
|
cpu.set_register(Register::A, sum);
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// ADC A, n | Add immediate byte plus the carry flag to A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-04-08 03:37:33 +00:00
|
|
|
let value = cpu.register(Register::A);
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = Self::add_with_carry_bit(value, n, flags.c(), &mut flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, sum);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"ADC {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
|
|
|
Instruction::SUB(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// SUB r[z] | Subtract the value in register r[z] from register A, then store in A
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, diff) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-05-04 05:50:22 +00:00
|
|
|
let diff = Self::sub(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), diff)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-09-03 02:54:58 +00:00
|
|
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-05-04 05:50:22 +00:00
|
|
|
let diff = Self::sub(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), diff)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
|
|
|
cpu.set_register(Register::A, diff);
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// SUB n | Subtract the immediate byte from register A, then store in A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-05-04 05:50:22 +00:00
|
|
|
let diff = Self::sub(cpu.register(Register::A), n, &mut flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, diff);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"SUB {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
2020-09-08 01:28:24 +00:00
|
|
|
Instruction::SBC(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// SBC A, r[z] | Subtract the value from register r[z] from A, add the Carry flag and then store in A
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-04-08 03:37:33 +00:00
|
|
|
let left = cpu.register(Register::A);
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, diff) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-04-08 03:37:33 +00:00
|
|
|
let right = cpu.register(reg.to_register());
|
|
|
|
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), diff)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2021-04-08 03:37:33 +00:00
|
|
|
let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
|
|
|
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), diff)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
|
|
|
cpu.set_register(Register::A, diff);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// SBC A, n | Subtract the value from immediate byte from A, add the carry flag and then store in A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-04-08 03:37:33 +00:00
|
|
|
let value = cpu.register(Register::A);
|
|
|
|
|
|
|
|
let diff = Self::sub_with_carry(value, n, flags.c(), &mut flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, diff);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"SBC {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
|
|
|
Instruction::AND(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// AND r[z] | Bitwise AND register r[z] and register A, store in register A
|
2021-03-23 07:11:40 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
let (cycles, result) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), a_value & value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
IndirectHL => {
|
2020-09-03 02:54:58 +00:00
|
|
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), a_value & value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
};
|
2020-09-02 22:26:46 +00:00
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, true, false);
|
2020-09-03 02:54:58 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2021-01-20 04:44:48 +00:00
|
|
|
// AND n | Bitwise AND immediate byte and register A, store in register A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-08 01:50:33 +00:00
|
|
|
let result = cpu.register(Register::A) & n;
|
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, true, false);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
2021-03-23 07:11:40 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"AND {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
|
|
|
Instruction::XOR(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// XOR r[z] | Bitwise XOR register r[z] and register A, store in register A
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, result) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), a_value ^ value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-09-03 02:54:58 +00:00
|
|
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), a_value ^ value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, false, false);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// XOR n | Bitwise XOR immediate byte and register A, store in register A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-08 01:50:33 +00:00
|
|
|
let result = cpu.register(Register::A) ^ n;
|
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, false, false);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"XOR {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
|
|
|
Instruction::OR(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// OR r[z] | Bitwise OR register r[z] and register A, store in register A
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, result) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(4), a_value | value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-09-03 02:54:58 +00:00
|
|
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), a_value | value)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, false, false);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// OR n | Bitwise OR on immediate byte n and register A, store in register A
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-08 01:50:33 +00:00
|
|
|
let result = cpu.register(Register::A) | n;
|
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(result == 0, false, false, false);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
cpu.set_register(Register::A, result);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"OR {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
|
|
|
Instruction::CP(target) => match target {
|
|
|
|
MATHTarget::Register(reg) => {
|
2020-09-04 05:41:19 +00:00
|
|
|
// CP r[z] | Same behaviour as SUB, except the result is not stored.
|
2021-03-23 07:11:40 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-03 02:54:58 +00:00
|
|
|
let a_value = cpu.register(Register::A);
|
|
|
|
|
2021-03-22 02:16:23 +00:00
|
|
|
let cycles = match reg {
|
2021-03-23 07:11:40 +00:00
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let value = cpu.register(reg.to_register());
|
2021-05-04 05:50:22 +00:00
|
|
|
let _ = Self::sub(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-23 07:11:40 +00:00
|
|
|
IndirectHL => {
|
2020-09-03 02:54:58 +00:00
|
|
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
2021-05-04 05:50:22 +00:00
|
|
|
let _ = Self::sub(a_value, value, &mut flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
2021-03-22 02:16:23 +00:00
|
|
|
};
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-03 02:54:58 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-09-08 01:50:33 +00:00
|
|
|
MATHTarget::ImmediateByte(n) => {
|
2020-09-08 01:57:31 +00:00
|
|
|
// CP n | Same behaviour as SUB, except the result is not stored,
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-05-04 05:50:22 +00:00
|
|
|
let _ = Self::sub(cpu.register(Register::A), n, &mut flags);
|
2020-09-08 01:50:33 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-08 01:50:33 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"CP {:?}\" instruction", target),
|
2020-09-03 02:54:58 +00:00
|
|
|
},
|
2020-09-04 05:41:19 +00:00
|
|
|
Instruction::RET(cond) => {
|
|
|
|
// RET cc[y] | Essentially a POP PC, Return from Subroutine
|
|
|
|
// RET | Essentially a POP PC, Return from Subroutine
|
2021-03-16 04:35:20 +00:00
|
|
|
let flags: &Flags = cpu.flags();
|
2020-09-04 05:41:19 +00:00
|
|
|
|
|
|
|
match cond {
|
|
|
|
JumpCondition::NotZero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.z() {
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(20)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Zero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.z() {
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(20)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::NotCarry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.c() {
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(20)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Carry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.c() {
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(20)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Always => {
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::LDHL(d) => {
|
|
|
|
// LDHL SP + d | Add SP + d to register HL
|
|
|
|
// LD HL, SP + d | Add SP + d to register HL
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-04 05:41:19 +00:00
|
|
|
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
2021-03-24 01:22:11 +00:00
|
|
|
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::HL, sum);
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_flags(flags);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
Instruction::POP(pair) => {
|
2021-03-23 03:33:56 +00:00
|
|
|
// POP rp2[p] | Pop from stack into register pair rp2[]
|
2020-09-04 05:41:19 +00:00
|
|
|
// Flags are set when we call cpu.set_register_pair(RegisterPair::AF, value);
|
2021-03-24 01:22:11 +00:00
|
|
|
use RegisterPair::*;
|
|
|
|
|
2020-09-04 05:41:19 +00:00
|
|
|
match pair {
|
2021-03-24 01:22:11 +00:00
|
|
|
BC | DE | HL | AF => {
|
2020-09-08 02:49:10 +00:00
|
|
|
let value = Self::pop(cpu);
|
|
|
|
cpu.set_register_pair(pair, value);
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"POP {:?}\" instruction", pair),
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
Instruction::RETI => {
|
|
|
|
// Same as RET, after which interrupts are enabled.
|
2020-09-08 02:49:10 +00:00
|
|
|
let addr = Self::pop(cpu);
|
2020-09-04 05:41:19 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-04-05 05:52:12 +00:00
|
|
|
cpu.set_ime(ImeState::Enabled);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
Instruction::JP(cond, target) => match target {
|
|
|
|
JPTarget::RegisterPair(RegisterPair::HL) => {
|
2020-09-08 01:19:10 +00:00
|
|
|
// JP HL | Load register pair HL into program counter
|
2021-03-23 07:11:40 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
2020-09-08 01:19:10 +00:00
|
|
|
JPTarget::ImmediateWord(nn) => {
|
|
|
|
// JP cc[y], nn | Store Immediate Word in the Program Counter if cond is met
|
|
|
|
// JP nn | Store Immediate Word in the Program Counter
|
2021-03-16 04:35:20 +00:00
|
|
|
let flags: &Flags = cpu.flags();
|
2020-09-08 01:19:10 +00:00
|
|
|
|
|
|
|
match cond {
|
|
|
|
JumpCondition::NotZero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.z() {
|
2020-09-08 01:19:10 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Zero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.z() {
|
2020-09-08 01:19:10 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::NotCarry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.c() {
|
2020-09-08 01:19:10 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Carry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.c() {
|
2020-09-08 01:19:10 +00:00
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2021-03-23 07:11:40 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Always => {
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"JP {:?}\" instruction", target),
|
2020-09-04 05:41:19 +00:00
|
|
|
},
|
2020-09-08 01:19:10 +00:00
|
|
|
Instruction::DI => {
|
|
|
|
// Disable IME
|
2021-04-05 05:52:12 +00:00
|
|
|
cpu.set_ime(ImeState::Disabled);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
Instruction::EI => {
|
|
|
|
// Enable IME (After the next instruction)
|
|
|
|
// FIXME: IME is set after the next instruction, this currently is not represented in this emulator.
|
2021-04-05 06:10:03 +00:00
|
|
|
cpu.set_ime(ImeState::Pending);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(4)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
Instruction::CALL(cond, nn) => {
|
2021-03-19 02:06:01 +00:00
|
|
|
// CALL cc[y], nn | Store pc on the stack, then store nn in the program counter if cond is met
|
2020-09-08 01:19:10 +00:00
|
|
|
// CALL nn | Store nn on the stack, then store nn in the program counter
|
2021-03-16 04:35:20 +00:00
|
|
|
let flags: &Flags = cpu.flags();
|
2021-01-18 04:12:00 +00:00
|
|
|
let pc = cpu.register_pair(RegisterPair::PC);
|
2020-09-08 01:19:10 +00:00
|
|
|
|
|
|
|
match cond {
|
|
|
|
JumpCondition::NotZero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.z() {
|
2021-01-18 08:22:45 +00:00
|
|
|
Self::push(cpu, pc);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(24)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Zero => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.z() {
|
2021-01-18 08:22:45 +00:00
|
|
|
Self::push(cpu, pc);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(24)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::NotCarry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if !flags.c() {
|
2021-01-18 08:22:45 +00:00
|
|
|
Self::push(cpu, pc);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(24)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Carry => {
|
2021-03-16 04:35:20 +00:00
|
|
|
if flags.c() {
|
2021-01-18 08:22:45 +00:00
|
|
|
Self::push(cpu, pc);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(24)
|
2021-03-24 01:22:11 +00:00
|
|
|
} else {
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(12)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
JumpCondition::Always => {
|
2021-01-18 08:22:45 +00:00
|
|
|
Self::push(cpu, pc);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(24)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::PUSH(pair) => {
|
|
|
|
// PUSH rp2[p] | Push register pair onto the stack
|
2021-03-23 07:11:40 +00:00
|
|
|
use RegisterPair::*;
|
|
|
|
|
2020-09-08 01:19:10 +00:00
|
|
|
match pair {
|
2021-03-23 07:11:40 +00:00
|
|
|
BC | DE | HL | AF => {
|
2020-09-08 01:19:10 +00:00
|
|
|
let value = cpu.register_pair(pair);
|
2020-09-08 02:49:10 +00:00
|
|
|
Self::push(cpu, value);
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!("There is no \"PUSH {:?}\" instruction", pair),
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-09-08 02:49:10 +00:00
|
|
|
}
|
|
|
|
Instruction::RST(n) => {
|
|
|
|
// RST n | Push current address onto the stack, jump to 0x0000 + n
|
2021-04-08 04:05:03 +00:00
|
|
|
|
|
|
|
// The same behaviour will occur when handling an interrupt so this code
|
|
|
|
// is relegated to a method
|
|
|
|
Self::reset(cpu, n)
|
2020-09-08 01:19:10 +00:00
|
|
|
}
|
2020-09-08 03:34:09 +00:00
|
|
|
Instruction::RLC(reg) => {
|
2020-12-23 04:23:09 +00:00
|
|
|
// RLC r[z] | Rotate register r[z] left
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, msb, rotated) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-09-08 03:34:09 +00:00
|
|
|
|
|
|
|
let value = cpu.register(register);
|
2021-03-24 01:22:11 +00:00
|
|
|
let rotated = value.rotate_left(1);
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), value >> 7, rotated)
|
2020-09-08 03:34:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-09-08 03:34:09 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
|
|
|
|
let value = cpu.read_byte(addr);
|
2021-03-24 01:22:11 +00:00
|
|
|
let rotated = value.rotate_left(1);
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), value >> 7, rotated)
|
2020-09-08 03:34:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-08 01:57:31 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(rotated == 0, false, false, msb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-09-08 03:34:09 +00:00
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::RRC(reg) => {
|
2020-12-23 04:23:09 +00:00
|
|
|
// RRC r[z] | Rotate Register r[z] right
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, lsb, rotated) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-09-08 03:34:09 +00:00
|
|
|
|
|
|
|
let value = cpu.register(register);
|
2021-03-24 01:22:11 +00:00
|
|
|
let rotated = value.rotate_right(1);
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), value & 0x01, rotated)
|
2020-09-08 03:34:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-09-08 03:34:09 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
|
|
|
|
let value = cpu.read_byte(addr);
|
2021-03-24 01:22:11 +00:00
|
|
|
let rotated = value.rotate_right(1);
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), value & 0x01, rotated)
|
2020-09-08 03:34:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(rotated == 0, false, false, lsb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 04:23:09 +00:00
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::RL(reg) => {
|
|
|
|
// RL r[z] | Rotate register r[z] left through carry
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, rotated, carry) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 04:23:09 +00:00
|
|
|
let value = cpu.register(register);
|
2020-09-08 03:34:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (rotated, carry) = Self::rl_thru_carry(value, flags.c());
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), rotated, carry)
|
2020-12-23 04:23:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 04:23:09 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (rotated, carry) = Self::rl_thru_carry(value, flags.c());
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), rotated, carry)
|
2020-12-23 04:23:09 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(rotated == 0, false, false, carry);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 04:23:09 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::RR(reg) => {
|
2020-12-23 06:24:29 +00:00
|
|
|
// RR r[z] | Rotate register r[z] right through carry
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, rotated, carry) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 06:24:29 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (rotated, carry) = Self::rr_thru_carry(value, flags.c());
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), rotated, carry)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 06:24:29 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (rotated, carry) = Self::rr_thru_carry(value, flags.c());
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, rotated);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), rotated, carry)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(rotated == 0, false, false, carry);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::SLA(reg) => {
|
|
|
|
// SLA r[z] | Shift left arithmetic register r[z]
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, msb, shifted) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 06:24:29 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = value << 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), (value >> 7) & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 06:24:29 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = value << 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-04-23 01:38:13 +00:00
|
|
|
cpu.write_byte(addr, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), (value >> 7) & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(shifted == 0, false, false, msb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::SRA(reg) => {
|
|
|
|
// SRA r[z] | Shift right arithmetic register r[z]
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, lsb, shifted) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 06:24:29 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
|
|
|
let msb = (value >> 7) & 0x01;
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = msb << 7 | value >> 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), value & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 06:24:29 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
|
|
|
let msb = (value >> 7) & 0x01;
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = msb << 7 | value >> 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-04-23 01:38:13 +00:00
|
|
|
cpu.write_byte(addr, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), value & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(shifted == 0, false, false, lsb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::SWAP(reg) => {
|
|
|
|
// SWAP r[z] | Swap the 4 highest and lowest bits in a byte
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, swapped) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 06:24:29 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let swapped = Self::swap_bits(value);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, swapped);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), swapped)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 06:24:29 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let swapped = Self::swap_bits(value);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, swapped);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), swapped)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(swapped == 0, false, false, false);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::SRL(reg) => {
|
2021-01-20 04:44:48 +00:00
|
|
|
// SRL r[z] | Shift right logic register r[z]
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let (cycles, lsb, shift_reg) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 06:24:29 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = value >> 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.set_register(register, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), value & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 06:24:29 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let shifted = value >> 1;
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
cpu.write_byte(addr, shifted);
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(16), value & 0x01, shifted)
|
2020-12-23 06:24:29 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
flags.update(shift_reg == 0, false, false, lsb == 0x01);
|
2021-03-16 04:35:20 +00:00
|
|
|
cpu.set_flags(flags);
|
2020-12-23 04:23:09 +00:00
|
|
|
|
2020-09-08 03:34:09 +00:00
|
|
|
cycles
|
|
|
|
}
|
2020-12-23 07:07:30 +00:00
|
|
|
Instruction::BIT(y, reg) => {
|
|
|
|
// BIT y, r[z] | Test y is in register r[z]
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
let mut flags: Flags = *cpu.flags();
|
2021-03-24 01:22:11 +00:00
|
|
|
|
|
|
|
let (cycles, is_bit_set) = match reg {
|
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 07:07:30 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let is_bit_set = ((value >> y) & 0x01) == 0x01;
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(8), is_bit_set)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 07:07:30 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
2021-03-24 01:22:11 +00:00
|
|
|
let is_bit_set = ((value >> y) & 0x01) == 0x01;
|
2021-03-27 17:10:18 +00:00
|
|
|
(Cycle::new(12), is_bit_set)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
};
|
2020-12-23 07:07:30 +00:00
|
|
|
|
2021-03-16 04:35:20 +00:00
|
|
|
flags.update(!is_bit_set, false, true, flags.c());
|
|
|
|
cpu.set_flags(flags);
|
2020-12-23 07:07:30 +00:00
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
|
|
|
Instruction::RES(y, reg) => {
|
|
|
|
// RES y, r[z] | Reset bit y to zero
|
|
|
|
//
|
|
|
|
// 00000001 << 3 = 00001000
|
|
|
|
// ~00001000 = 11110111
|
|
|
|
// value & 11110111 means that only a specific bit will be reset
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2020-12-23 07:07:30 +00:00
|
|
|
match reg {
|
2021-03-24 01:22:11 +00:00
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 07:07:30 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
|
|
|
cpu.set_register(register, value & !(1u8 << y));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 07:07:30 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
|
|
|
cpu.write_byte(addr, value & !(1u8 << y));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::SET(y, reg) => {
|
|
|
|
// BIT y, r[z] | Set bit y to one
|
|
|
|
//
|
|
|
|
// 00000001 << 3 = 00001000
|
|
|
|
// value | 00001000 means that only a specific bit will be set
|
2021-03-24 01:22:11 +00:00
|
|
|
use InstrRegister::*;
|
|
|
|
|
2020-12-23 07:07:30 +00:00
|
|
|
match reg {
|
2021-03-24 01:22:11 +00:00
|
|
|
B | C | D | E | H | L | A => {
|
2021-03-23 02:48:12 +00:00
|
|
|
let register = reg.to_register();
|
2020-12-23 07:07:30 +00:00
|
|
|
let value = cpu.register(register);
|
|
|
|
|
|
|
|
cpu.set_register(register, value | (1u8 << y));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(8)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
2021-03-24 01:22:11 +00:00
|
|
|
IndirectHL => {
|
2020-12-23 07:07:30 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::HL);
|
|
|
|
let value = cpu.read_byte(addr);
|
|
|
|
|
|
|
|
cpu.write_byte(addr, value | (1u8 << y));
|
2021-03-27 17:10:18 +00:00
|
|
|
Cycle::new(16)
|
2020-12-23 07:07:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-16 07:31:07 +00:00
|
|
|
/// PUSHes a u16 onto the stack
|
2020-09-04 05:41:19 +00:00
|
|
|
///
|
2020-09-08 02:49:10 +00:00
|
|
|
/// Mutates the stack pointer and the stack
|
|
|
|
fn push(cpu: &mut Cpu, value: u16) {
|
|
|
|
let mut sp = cpu.register_pair(RegisterPair::SP);
|
|
|
|
|
|
|
|
sp -= 1;
|
2021-01-18 04:12:00 +00:00
|
|
|
cpu.write_byte(sp, (value >> 8) as u8);
|
2020-09-08 02:49:10 +00:00
|
|
|
sp -= 1;
|
2021-01-18 04:12:00 +00:00
|
|
|
cpu.write_byte(sp, value as u8);
|
2020-09-08 02:49:10 +00:00
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::SP, sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// POPs a u16 from the stack
|
|
|
|
///
|
|
|
|
/// Mutates the stack pointer and returns the u16 which was popped from the stack
|
|
|
|
fn pop(cpu: &mut Cpu) -> u16 {
|
|
|
|
let mut sp = cpu.register_pair(RegisterPair::SP);
|
|
|
|
|
2021-01-18 04:12:00 +00:00
|
|
|
let low = cpu.read_byte(sp);
|
2020-09-08 02:49:10 +00:00
|
|
|
sp += 1;
|
2021-01-18 04:12:00 +00:00
|
|
|
let high = cpu.read_byte(sp);
|
2020-09-08 02:49:10 +00:00
|
|
|
sp += 1;
|
|
|
|
|
|
|
|
cpu.set_register_pair(RegisterPair::SP, sp);
|
|
|
|
(high as u16) << 8 | low as u16
|
2020-09-04 05:41:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
fn dec_register(byte: u8, flags: &mut Flags) -> u8 {
|
2021-05-04 05:50:22 +00:00
|
|
|
Self::sub_no_carry(byte, 1, flags)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 07:11:40 +00:00
|
|
|
fn inc_register(byte: u8, flags: &mut Flags) -> u8 {
|
2021-05-04 05:50:22 +00:00
|
|
|
Self::add_no_carry(byte, 1, flags)
|
2020-09-03 02:54:58 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn sub_no_carry(left: u8, right: u8, flags: &mut Flags) -> u8 {
|
2020-09-03 02:54:58 +00:00
|
|
|
let diff = left.wrapping_sub(right);
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2021-03-22 02:16:23 +00:00
|
|
|
flags.set_z(diff == 0);
|
|
|
|
flags.set_n(true);
|
2021-04-07 07:27:57 +00:00
|
|
|
flags.set_h(Self::bit_4_borrow(left, right));
|
2021-03-22 02:16:23 +00:00
|
|
|
|
2020-09-03 02:54:58 +00:00
|
|
|
diff
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn sub(left: u8, right: u8, flags: &mut Flags) -> u8 {
|
2020-09-03 02:54:58 +00:00
|
|
|
let (diff, did_overflow) = left.overflowing_sub(right);
|
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(
|
|
|
|
diff == 0,
|
|
|
|
true,
|
2021-04-07 07:27:57 +00:00
|
|
|
Self::bit_4_borrow(left, right),
|
2020-12-23 04:23:09 +00:00
|
|
|
did_overflow,
|
|
|
|
);
|
2020-09-03 02:54:58 +00:00
|
|
|
diff
|
|
|
|
}
|
2020-09-01 05:16:05 +00:00
|
|
|
|
2021-04-08 03:37:33 +00:00
|
|
|
fn sub_with_carry(left: u8, right: u8, carry: bool, flags: &mut Flags) -> u8 {
|
|
|
|
let carry = carry as u8;
|
|
|
|
|
|
|
|
let (diff, did_overflow) = {
|
|
|
|
let (tmp_diff, did) = left.overflowing_sub(right);
|
|
|
|
let (diff, overflow) = tmp_diff.overflowing_sub(carry);
|
|
|
|
|
|
|
|
(diff, did || overflow)
|
|
|
|
};
|
|
|
|
|
|
|
|
flags.update(
|
|
|
|
diff == 0,
|
|
|
|
true,
|
|
|
|
(left & 0x0F).wrapping_sub(right & 0x0F).wrapping_sub(carry) > 0x0F,
|
|
|
|
did_overflow,
|
|
|
|
);
|
|
|
|
|
|
|
|
diff
|
|
|
|
}
|
|
|
|
|
2020-09-04 05:41:19 +00:00
|
|
|
fn add_u16_i8(left: u16, right: i8, flags: &mut Flags) -> u16 {
|
2021-04-07 07:27:57 +00:00
|
|
|
let (_, did_overflow) = (left as u8).overflowing_add(right as u8);
|
2021-05-04 05:50:22 +00:00
|
|
|
let sum = left.wrapping_add(right as u16);
|
2020-09-04 05:41:19 +00:00
|
|
|
|
2021-04-07 07:27:57 +00:00
|
|
|
let half_carry = Self::bit_3_overflow(left as u8, right as u8);
|
2021-03-24 01:22:11 +00:00
|
|
|
flags.update(false, false, half_carry, did_overflow);
|
2020-09-04 05:41:19 +00:00
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn add_no_carry(left: u8, right: u8, flags: &mut Flags) -> u8 {
|
2020-09-03 02:54:58 +00:00
|
|
|
let sum = left.wrapping_add(right);
|
|
|
|
|
2021-03-22 02:16:23 +00:00
|
|
|
flags.set_z(sum == 0);
|
|
|
|
flags.set_n(false);
|
2021-04-07 07:27:57 +00:00
|
|
|
flags.set_h(Self::bit_3_overflow(left, right));
|
2020-09-03 02:54:58 +00:00
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn add(left: u8, right: u8, flags: &mut Flags) -> u8 {
|
2020-09-03 02:54:58 +00:00
|
|
|
let (sum, did_overflow) = left.overflowing_add(right);
|
|
|
|
|
2020-12-23 04:23:09 +00:00
|
|
|
flags.update(
|
|
|
|
sum == 0,
|
|
|
|
false,
|
2021-04-07 07:27:57 +00:00
|
|
|
Self::bit_3_overflow(left, right),
|
2020-12-23 04:23:09 +00:00
|
|
|
did_overflow,
|
|
|
|
);
|
2021-04-08 03:37:33 +00:00
|
|
|
|
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn add_with_carry_bit(left: u8, right: u8, carry: bool, flags: &mut Flags) -> u8 {
|
2021-04-08 03:37:33 +00:00
|
|
|
let carry = carry as u8;
|
|
|
|
|
|
|
|
let (sum, did_overflow) = {
|
|
|
|
let (tmp_sum, did) = left.overflowing_add(right);
|
|
|
|
let (sum, overflow) = tmp_sum.overflowing_add(carry);
|
|
|
|
|
|
|
|
(sum, did || overflow)
|
|
|
|
};
|
|
|
|
|
|
|
|
flags.update(
|
|
|
|
sum == 0,
|
|
|
|
false,
|
|
|
|
(((left & 0x0F) + (right & 0x0F) + carry) & 0x10) == 0x10,
|
|
|
|
did_overflow,
|
|
|
|
);
|
|
|
|
|
2020-09-03 02:54:58 +00:00
|
|
|
sum
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 05:50:22 +00:00
|
|
|
fn add_u16(left: u16, right: u16, flags: &mut Flags) -> u16 {
|
2020-09-01 05:16:05 +00:00
|
|
|
let (sum, did_overflow) = left.overflowing_add(right);
|
2020-09-03 02:54:58 +00:00
|
|
|
|
2021-03-27 01:19:48 +00:00
|
|
|
flags.set_n(false);
|
2021-04-07 07:27:57 +00:00
|
|
|
flags.set_h(Self::bit_11_overflow(left, right));
|
2021-03-27 01:19:48 +00:00
|
|
|
flags.set_c(did_overflow);
|
|
|
|
|
2020-09-01 05:16:05 +00:00
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2021-04-07 07:27:57 +00:00
|
|
|
fn bit_11_overflow(left: u16, right: u16) -> bool {
|
|
|
|
(((left & 0x0FFF) + (right & 0x0FFF)) & 0x1000) == 0x1000
|
2021-03-22 02:16:23 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 07:27:57 +00:00
|
|
|
fn bit_3_overflow(left: u8, right: u8) -> bool {
|
2021-03-22 02:16:23 +00:00
|
|
|
(((left & 0xF) + (right & 0xF)) & 0x10) == 0x10
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 07:27:57 +00:00
|
|
|
fn bit_4_borrow(left: u8, right: u8) -> bool {
|
|
|
|
(left & 0x0F) < (right & 0x0F)
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
2020-12-23 06:24:29 +00:00
|
|
|
|
2020-12-23 07:07:30 +00:00
|
|
|
fn rl_thru_carry(byte: u8, carry: bool) -> (u8, bool) {
|
2020-12-23 06:24:29 +00:00
|
|
|
let carry_flag = (byte >> 7) & 0x01; // get the MSB of the u8 (which will rotate into the carry bit)
|
|
|
|
let new_byte = (byte << 1) | carry as u8; // shift the bit left, and then OR the carry bit in.
|
|
|
|
|
|
|
|
(new_byte, carry_flag == 0x01)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rr_thru_carry(byte: u8, carry: bool) -> (u8, bool) {
|
|
|
|
let carry_flag = byte & 0x01; // get the LSB of the u8 (which will rotate into the carry bit)
|
2021-03-24 01:22:11 +00:00
|
|
|
let new_byte = ((carry as u8) << 7) | (byte >> 1); // shift the bit right, and then OR the carry bit in.
|
2020-12-23 06:24:29 +00:00
|
|
|
|
|
|
|
(new_byte, carry_flag == 0x01)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn swap_bits(byte: u8) -> u8 {
|
|
|
|
let upper = byte >> 4;
|
|
|
|
let lower = byte & 0x0F;
|
|
|
|
|
|
|
|
(lower << 4) | upper
|
|
|
|
}
|
2021-04-08 04:05:03 +00:00
|
|
|
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) fn reset(cpu: &mut Cpu, vector: u8) -> Cycle {
|
2021-04-08 04:05:03 +00:00
|
|
|
let addr = cpu.register_pair(RegisterPair::PC);
|
|
|
|
Self::push(cpu, addr);
|
|
|
|
cpu.set_register_pair(RegisterPair::PC, vector as u16);
|
|
|
|
Cycle::new(16)
|
|
|
|
}
|
2020-09-01 05:16:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-29 23:38:27 +00:00
|
|
|
impl Instruction {
|
2021-06-07 00:14:28 +00:00
|
|
|
pub(crate) fn from_byte(cpu: &mut Cpu, byte: u8) -> Self {
|
2020-08-29 23:38:27 +00:00
|
|
|
if byte == 0xCB {
|
2020-09-08 02:18:53 +00:00
|
|
|
Self::from_prefixed_byte(cpu)
|
2020-08-29 23:38:27 +00:00
|
|
|
} else {
|
|
|
|
Self::from_unprefixed_byte(cpu, byte)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-23 09:25:16 +00:00
|
|
|
fn from_unprefixed_byte(cpu: &mut Cpu, opcode: u8) -> Self {
|
2020-08-29 23:38:27 +00:00
|
|
|
// https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html
|
2021-04-04 06:19:39 +00:00
|
|
|
#![allow(clippy::many_single_char_names)]
|
2021-03-16 06:05:13 +00:00
|
|
|
|
2021-01-19 08:05:04 +00:00
|
|
|
let x = (opcode >> 6) & 0x03;
|
|
|
|
let y = (opcode >> 3) & 0x07;
|
|
|
|
let z = opcode & 0x07;
|
2020-08-29 23:38:27 +00:00
|
|
|
let p = y >> 1;
|
2021-01-19 08:05:04 +00:00
|
|
|
let q = y & 0x01;
|
2020-08-29 23:38:27 +00:00
|
|
|
|
2020-12-23 09:25:16 +00:00
|
|
|
let pc = cpu.register_pair(RegisterPair::PC);
|
2020-08-29 23:38:27 +00:00
|
|
|
|
2020-08-30 04:07:53 +00:00
|
|
|
match (x, z, q, y, p) {
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 0, _, 0, _) => Self::NOP, // NOP
|
|
|
|
(0, 0, _, 1, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (nn), SP
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ByteAtAddress(cpu.read_imm_word(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
LDTarget::RegisterPair(RegisterPair::SP),
|
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 0, _, 2, _) => Self::STOP, // STOP
|
2021-01-18 04:12:00 +00:00
|
|
|
(0, 0, _, 3, _) => Self::JR(JumpCondition::Always, cpu.read_imm_byte(pc) as i8), // JR d
|
|
|
|
(0, 0, _, 4..=7, _) => Self::JR(Table::cc(y - 4), cpu.read_imm_byte(pc) as i8), // JR cc[y - 4], d
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 1, 0, _, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD rp[p], nn
|
|
|
|
LDTarget::RegisterPair(Table::rp(p)),
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ImmediateWord(cpu.read_imm_word(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 1, 1, _, _) => Self::ADD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// ADD HL, rp[p]
|
2021-01-19 08:05:04 +00:00
|
|
|
MATHTarget::RegisterPair(RegisterPair::HL),
|
2020-08-30 04:07:53 +00:00
|
|
|
MATHTarget::RegisterPair(Table::rp(p)),
|
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 0, _, 0) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (BC), A
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::BC),
|
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 0, _, 1) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (DE), A
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::DE),
|
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 1, _, 0) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (BC)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::BC),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 1, _, 1) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (DE)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::DE),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 0, _, 2) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (HL+), A
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::IncrementHL),
|
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 0, _, 3) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (HL-), A
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::DecrementHL),
|
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 1, _, 2) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (HL+)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::IncrementHL),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 2, 1, _, 3) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (HL-)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
|
|
|
LDTarget::IndirectRegister(InstrRegisterPair::DecrementHL),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 3, 0, _, _) => Self::INC(
|
2020-08-30 04:07:53 +00:00
|
|
|
// INC rp[p]
|
2020-09-02 22:26:46 +00:00
|
|
|
Registers::Word(Table::rp(p)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 3, 1, _, _) => Self::DEC(
|
2020-08-30 04:07:53 +00:00
|
|
|
// DEC rp[p]
|
2020-09-02 22:26:46 +00:00
|
|
|
Registers::Word(Table::rp(p)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 4, _, _, _) => Self::INC(
|
2020-08-30 04:07:53 +00:00
|
|
|
// INC r[y]
|
2020-09-02 22:26:46 +00:00
|
|
|
Registers::Byte(Table::r(y)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 5, _, _, _) => Self::DEC(
|
2020-08-30 04:07:53 +00:00
|
|
|
// DEC r[y]
|
2020-09-02 22:26:46 +00:00
|
|
|
Registers::Byte(Table::r(y)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 6, _, _, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD r[y], n
|
|
|
|
LDTarget::Register(Table::r(y)),
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ImmediateByte(cpu.read_imm_byte(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(0, 7, _, 0, _) => Self::RLCA,
|
|
|
|
(0, 7, _, 1, _) => Self::RRCA,
|
|
|
|
(0, 7, _, 2, _) => Self::RLA,
|
|
|
|
(0, 7, _, 3, _) => Self::RRA,
|
|
|
|
(0, 7, _, 4, _) => Self::DAA,
|
|
|
|
(0, 7, _, 5, _) => Self::CPL,
|
|
|
|
(0, 7, _, 6, _) => Self::SCF,
|
|
|
|
(0, 7, _, 7, _) => Self::CCF,
|
|
|
|
(1, 6, _, 6, _) => Self::HALT,
|
|
|
|
(1, _, _, _, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD r[y], r[z]
|
|
|
|
LDTarget::Register(Table::r(y)),
|
|
|
|
LDTarget::Register(Table::r(z)),
|
|
|
|
),
|
|
|
|
(2, _, _, _, _) => Table::x2_alu(y, z), // alu[y] r[z]
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 0, _, 0..=3, _) => Self::RET(Table::cc(y)), // RET cc[y]
|
|
|
|
(3, 0, _, 4, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (0xFF00 + n), A
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ByteAtAddressWithOffset(cpu.read_imm_byte(pc)),
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 0, _, 5, _) => Self::ADD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// ADD SP, d
|
|
|
|
MATHTarget::RegisterPair(RegisterPair::SP),
|
2021-01-18 04:12:00 +00:00
|
|
|
MATHTarget::ImmediateByte(cpu.read_imm_byte(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 0, _, 6, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (0xFF00 + n)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ByteAtAddressWithOffset(cpu.read_imm_byte(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2021-01-18 04:12:00 +00:00
|
|
|
(3, 0, _, 7, _) => Self::LDHL(cpu.read_imm_byte(pc) as i8), // LD HL, SP + d
|
|
|
|
(3, 1, 0, _, _) => Self::POP(Table::rp2(p)), // POP rp2[p]
|
|
|
|
(3, 1, 1, _, 0) => Self::RET(JumpCondition::Always), // RET
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 1, 1, _, 1) => Self::RETI,
|
|
|
|
(3, 1, 1, _, 2) => Self::JP(
|
2020-08-30 04:07:53 +00:00
|
|
|
// JP HL
|
|
|
|
JumpCondition::Always,
|
|
|
|
JPTarget::RegisterPair(RegisterPair::HL),
|
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 1, 1, _, 3) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD SP, HL
|
|
|
|
LDTarget::RegisterPair(RegisterPair::SP),
|
|
|
|
LDTarget::RegisterPair(RegisterPair::HL),
|
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 2, _, 0..=3, _) => Self::JP(
|
2020-08-30 04:07:53 +00:00
|
|
|
// JP cc[y], nn
|
|
|
|
Table::cc(y),
|
2021-01-18 04:12:00 +00:00
|
|
|
JPTarget::ImmediateWord(cpu.read_imm_word(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 2, _, 4, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (0xFF00 + C) ,A
|
2020-12-23 07:23:38 +00:00
|
|
|
LDTarget::IndirectC,
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 2, _, 5, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD (nn), A
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ByteAtAddress(cpu.read_imm_word(pc)),
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 2, _, 6, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (0xFF00 + C)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2020-12-23 07:23:38 +00:00
|
|
|
LDTarget::IndirectC,
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 2, _, 7, _) => Self::LD(
|
2020-08-30 04:07:53 +00:00
|
|
|
// LD A, (nn)
|
2020-09-02 22:26:46 +00:00
|
|
|
LDTarget::Register(InstrRegister::A),
|
2021-01-18 04:12:00 +00:00
|
|
|
LDTarget::ByteAtAddress(cpu.read_imm_word(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 3, _, 0, _) => Self::JP(
|
2020-08-30 04:07:53 +00:00
|
|
|
// JP nn
|
|
|
|
JumpCondition::Always,
|
2021-01-18 04:12:00 +00:00
|
|
|
JPTarget::ImmediateWord(cpu.read_imm_word(pc)),
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
2021-03-23 03:33:56 +00:00
|
|
|
(3, 3, _, 1, _) => unreachable!("0xCB is handled by Instruction::from_prefixed_byte"),
|
|
|
|
// (3, 3, _, 2, _) => unreachable!("\"removed\" in documentation"),
|
|
|
|
// (3, 3, _, 3, _) => unreachable!("\"removed\" in documentation"),
|
|
|
|
// (3, 3, _, 4, _) => unreachable!("\"removed\" in documentation"),
|
|
|
|
// (3, 3, _, 5, _) => unreachable!("\"removed\" in documentation"),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 3, _, 6, _) => Self::DI,
|
|
|
|
(3, 3, _, 7, _) => Self::EI,
|
2021-01-18 04:12:00 +00:00
|
|
|
(3, 4, _, 0..=3, _) => Self::CALL(Table::cc(y), cpu.read_imm_word(pc)), // CALL cc[y], nn
|
2021-03-23 03:33:56 +00:00
|
|
|
// (3, 4, _, 4..=7, _) => unreachable!("\"removed\" in documentation"),
|
2020-09-08 02:18:53 +00:00
|
|
|
(3, 5, 0, _, _) => Self::PUSH(Table::rp2(p)), // PUSH rp2[p]
|
2021-01-18 04:12:00 +00:00
|
|
|
(3, 5, 1, _, 0) => Self::CALL(JumpCondition::Always, cpu.read_imm_word(pc)), // CALL nn
|
2021-03-23 03:33:56 +00:00
|
|
|
// (3, 5, 1, _, 1..=3) => unreachable!("\"removed\" in documentation"),
|
2021-01-18 04:12:00 +00:00
|
|
|
(3, 6, _, _, _) => Table::x3_alu(y, cpu.read_imm_byte(pc)),
|
2020-09-08 02:51:20 +00:00
|
|
|
(3, 7, _, _, _) => Self::RST(y * 8), // RST n
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!(
|
|
|
|
"Unknown Opcode: {:#04X}\n x: {}, z: {}, q: {}, y: {}, p: {}",
|
2020-09-04 05:41:19 +00:00
|
|
|
opcode, x, z, q, y, p
|
2020-08-30 04:07:53 +00:00
|
|
|
),
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 09:25:16 +00:00
|
|
|
fn from_prefixed_byte(cpu: &mut Cpu) -> Self {
|
2021-04-04 06:19:39 +00:00
|
|
|
#![allow(clippy::many_single_char_names)]
|
|
|
|
|
2020-12-23 09:25:16 +00:00
|
|
|
let pc = cpu.register_pair(RegisterPair::PC);
|
2021-01-18 04:12:00 +00:00
|
|
|
let opcode = cpu.read_imm_byte(pc); // FIXME: Should the PC be incremented here?
|
2020-09-08 02:18:53 +00:00
|
|
|
|
|
|
|
// https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html
|
|
|
|
let x = (opcode >> 6) & 0b00000011;
|
|
|
|
let y = (opcode >> 3) & 0b00000111;
|
|
|
|
let z = opcode & 0b00000111;
|
|
|
|
let p = y >> 1;
|
|
|
|
let q = y & 0b00000001;
|
|
|
|
|
|
|
|
match x {
|
|
|
|
0 => Table::rot(y, z),
|
2020-09-08 02:22:26 +00:00
|
|
|
1 => Self::BIT(y, Table::r(z)), // BIT y, r[z]
|
|
|
|
2 => Self::RES(y, Table::r(z)), // RES y, r[z]
|
|
|
|
3 => Self::SET(y, Table::r(z)), // SET y, r[z]
|
2021-03-23 03:33:56 +00:00
|
|
|
_ => unreachable!(
|
|
|
|
"Unknown Prefixed Opcode: 0xCB {:#04X}\n x: {}, z: {}, q: {}, y: {}, p: {}",
|
2020-09-08 02:18:53 +00:00
|
|
|
opcode, x, z, q, y, p
|
|
|
|
),
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-03 00:35:48 +00:00
|
|
|
|
|
|
|
impl From<RegisterPair> for InstrRegisterPair {
|
|
|
|
fn from(pair: RegisterPair) -> Self {
|
|
|
|
match pair {
|
|
|
|
RegisterPair::AF => Self::AF,
|
|
|
|
RegisterPair::BC => Self::BC,
|
|
|
|
RegisterPair::DE => Self::DE,
|
|
|
|
RegisterPair::HL => Self::HL,
|
|
|
|
RegisterPair::SP => Self::SP,
|
|
|
|
RegisterPair::PC => Self::PC,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<InstrRegisterPair> for RegisterPair {
|
2020-09-04 19:43:19 +00:00
|
|
|
type Error = &'static str; // FIXME: Proper error type goes here.
|
2020-09-03 00:35:48 +00:00
|
|
|
|
|
|
|
fn try_from(pair: InstrRegisterPair) -> Result<Self, Self::Error> {
|
|
|
|
match pair {
|
|
|
|
InstrRegisterPair::AF => Ok(Self::AF),
|
|
|
|
InstrRegisterPair::BC => Ok(Self::BC),
|
|
|
|
InstrRegisterPair::DE => Ok(Self::DE),
|
|
|
|
InstrRegisterPair::HL => Ok(Self::HL),
|
|
|
|
InstrRegisterPair::SP => Ok(Self::SP),
|
|
|
|
InstrRegisterPair::PC => Ok(Self::PC),
|
|
|
|
InstrRegisterPair::IncrementHL => {
|
2020-09-04 19:43:19 +00:00
|
|
|
Err("Can not convert InstrRegisterPair::IncrementHL to RegisterPair")
|
2020-09-03 00:35:48 +00:00
|
|
|
}
|
|
|
|
InstrRegisterPair::DecrementHL => {
|
2020-09-04 19:43:19 +00:00
|
|
|
Err("Can not convert InstrRegisterPair::DecrementHL to RegisterPair")
|
2020-09-03 00:35:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<Register> for InstrRegister {
|
2020-09-04 19:43:19 +00:00
|
|
|
type Error = &'static str; // FIXME: Proper error type goes here
|
2020-09-03 00:35:48 +00:00
|
|
|
|
|
|
|
fn try_from(register: Register) -> Result<Self, Self::Error> {
|
|
|
|
match register {
|
|
|
|
Register::A => Ok(Self::A),
|
|
|
|
Register::B => Ok(Self::B),
|
|
|
|
Register::C => Ok(Self::C),
|
|
|
|
Register::D => Ok(Self::D),
|
|
|
|
Register::E => Ok(Self::E),
|
|
|
|
Register::H => Ok(Self::H),
|
|
|
|
Register::L => Ok(Self::L),
|
2020-09-04 19:43:19 +00:00
|
|
|
Register::Flag => Err("Can not convert Register::Flag to InstrRegister"),
|
2020-09-03 00:35:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<InstrRegister> for Register {
|
|
|
|
type Error = String; // FIXME: Proper error type goes here.
|
|
|
|
|
|
|
|
fn try_from(register: InstrRegister) -> Result<Self, Self::Error> {
|
|
|
|
match register {
|
|
|
|
InstrRegister::A => Ok(Self::A),
|
|
|
|
InstrRegister::B => Ok(Self::B),
|
|
|
|
InstrRegister::C => Ok(Self::C),
|
|
|
|
InstrRegister::D => Ok(Self::D),
|
|
|
|
InstrRegister::E => Ok(Self::E),
|
|
|
|
InstrRegister::H => Ok(Self::H),
|
|
|
|
InstrRegister::L => Ok(Self::L),
|
|
|
|
InstrRegister::IndirectHL => {
|
|
|
|
Err("Can not convert InstrRegister::IndirectHL to Register".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 23:38:27 +00:00
|
|
|
impl Table {
|
2021-06-07 01:47:11 +00:00
|
|
|
fn r(index: u8) -> InstrRegister {
|
2020-08-29 23:38:27 +00:00
|
|
|
match index {
|
2020-09-02 22:26:46 +00:00
|
|
|
0 => InstrRegister::B,
|
|
|
|
1 => InstrRegister::C,
|
|
|
|
2 => InstrRegister::D,
|
|
|
|
3 => InstrRegister::E,
|
|
|
|
4 => InstrRegister::H,
|
|
|
|
5 => InstrRegister::L,
|
|
|
|
6 => InstrRegister::IndirectHL,
|
|
|
|
7 => InstrRegister::A,
|
2020-08-30 04:07:53 +00:00
|
|
|
_ => unreachable!("Index {} is out of bounds in r[]", index),
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn rp2(index: u8) -> RegisterPair {
|
2020-08-29 23:38:27 +00:00
|
|
|
match index {
|
|
|
|
0 => RegisterPair::BC,
|
|
|
|
1 => RegisterPair::DE,
|
|
|
|
2 => RegisterPair::HL,
|
|
|
|
3 => RegisterPair::AF,
|
2020-08-30 04:07:53 +00:00
|
|
|
_ => unreachable!("Index {} out of bounds in rp2[]", index),
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn rp(index: u8) -> RegisterPair {
|
2020-08-29 23:38:27 +00:00
|
|
|
match index {
|
|
|
|
0 => RegisterPair::BC,
|
|
|
|
1 => RegisterPair::DE,
|
|
|
|
2 => RegisterPair::HL,
|
2020-08-30 04:07:53 +00:00
|
|
|
3 => RegisterPair::SP,
|
|
|
|
_ => unreachable!("Index {} out of bounds in rp[]", index),
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn cc(index: u8) -> JumpCondition {
|
2020-08-29 23:38:27 +00:00
|
|
|
match index {
|
|
|
|
0 => JumpCondition::NotZero,
|
|
|
|
1 => JumpCondition::Zero,
|
|
|
|
2 => JumpCondition::NotCarry,
|
|
|
|
3 => JumpCondition::Carry,
|
2020-08-30 04:07:53 +00:00
|
|
|
_ => unreachable!("Index {} out of bounds in cc[]", index),
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn x2_alu(index: u8, r_index: u8) -> Instruction {
|
2020-09-08 02:18:53 +00:00
|
|
|
match index {
|
2020-08-30 04:07:53 +00:00
|
|
|
0 => Instruction::ADD(
|
|
|
|
// ADD A, r[z]
|
2020-09-02 22:26:46 +00:00
|
|
|
MATHTarget::Register(InstrRegister::A),
|
2020-08-30 04:07:53 +00:00
|
|
|
MATHTarget::Register(Self::r(r_index)),
|
|
|
|
),
|
2020-09-08 01:28:24 +00:00
|
|
|
1 => Instruction::ADC(MATHTarget::Register(Self::r(r_index))), // ADC A, r[z]
|
2020-08-30 04:07:53 +00:00
|
|
|
2 => Instruction::SUB(MATHTarget::Register(Self::r(r_index))), // SUB r[z]
|
2020-09-08 01:28:24 +00:00
|
|
|
3 => Instruction::SBC(MATHTarget::Register(Self::r(r_index))), // SBC A, r[z]
|
2020-08-30 04:07:53 +00:00
|
|
|
4 => Instruction::AND(MATHTarget::Register(Self::r(r_index))), // AND r[z]
|
|
|
|
5 => Instruction::XOR(MATHTarget::Register(Self::r(r_index))), // XOR r[z]
|
|
|
|
6 => Instruction::OR(MATHTarget::Register(Self::r(r_index))), // OR r[z]
|
|
|
|
7 => Instruction::CP(MATHTarget::Register(Self::r(r_index))), // CP r[z]
|
|
|
|
_ => unreachable!("Index {} is out of bounds in alu[]"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn x3_alu(index: u8, n: u8) -> Instruction {
|
2020-09-08 02:18:53 +00:00
|
|
|
match index {
|
2020-09-04 05:41:19 +00:00
|
|
|
0 => Instruction::ADD(
|
|
|
|
// ADD A, n
|
|
|
|
MATHTarget::Register(InstrRegister::A),
|
|
|
|
MATHTarget::ImmediateByte(n),
|
|
|
|
),
|
2020-09-08 01:28:24 +00:00
|
|
|
1 => Instruction::ADC(MATHTarget::ImmediateByte(n)), // ADC A, n
|
2020-09-04 05:41:19 +00:00
|
|
|
2 => Instruction::SUB(MATHTarget::ImmediateByte(n)), // SUB n
|
2020-09-08 01:28:24 +00:00
|
|
|
3 => Instruction::SBC(MATHTarget::ImmediateByte(n)), // SBC A, n
|
2020-09-04 05:41:19 +00:00
|
|
|
4 => Instruction::AND(MATHTarget::ImmediateByte(n)), // AND n
|
|
|
|
5 => Instruction::XOR(MATHTarget::ImmediateByte(n)), // XOR n
|
|
|
|
6 => Instruction::OR(MATHTarget::ImmediateByte(n)), // OR n
|
|
|
|
7 => Instruction::CP(MATHTarget::ImmediateByte(n)), // CP n
|
|
|
|
_ => unreachable!("Index {} is out of bounds in alu[]"),
|
|
|
|
}
|
2020-08-29 23:38:27 +00:00
|
|
|
}
|
2020-09-08 02:18:53 +00:00
|
|
|
|
2021-06-07 01:47:11 +00:00
|
|
|
fn rot(index: u8, r_index: u8) -> Instruction {
|
2020-09-08 02:18:53 +00:00
|
|
|
match index {
|
2020-09-08 02:22:26 +00:00
|
|
|
0 => Instruction::RLC(Self::r(r_index)), // RLC r[z]
|
|
|
|
1 => Instruction::RRC(Self::r(r_index)), // RRC r[z]
|
|
|
|
2 => Instruction::RL(Self::r(r_index)), // RL r[z]
|
|
|
|
3 => Instruction::RR(Self::r(r_index)), // RR r[z]
|
|
|
|
4 => Instruction::SLA(Self::r(r_index)), // SLA r[z]
|
|
|
|
5 => Instruction::SRA(Self::r(r_index)), // SRA r[z]
|
|
|
|
6 => Instruction::SWAP(Self::r(r_index)), // SWAP r[z]
|
|
|
|
7 => Instruction::SRL(Self::r(r_index)), // SRL r[z]
|
2020-09-08 02:18:53 +00:00
|
|
|
_ => unreachable!("Index {} is out of bounds in rot[]"),
|
|
|
|
}
|
|
|
|
}
|
2020-08-30 04:07:53 +00:00
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
|
|
|
|
impl std::fmt::Debug for JPTarget {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
JPTarget::RegisterPair(pair) => write!(f, "{:?}", pair),
|
|
|
|
JPTarget::ImmediateWord(word) => write!(f, "{:#06X}", word),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for LDTarget {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match *self {
|
2021-06-02 06:50:16 +00:00
|
|
|
LDTarget::IndirectC => f.write_str("(0xFF00 + C)"),
|
2020-12-24 01:39:37 +00:00
|
|
|
LDTarget::Register(reg) => write!(f, "{:?}", reg),
|
2021-06-02 06:50:16 +00:00
|
|
|
LDTarget::IndirectRegister(pair) => write!(f, "({:?})", pair),
|
|
|
|
LDTarget::ByteAtAddress(addr) => write!(f, "({:#06X})", addr),
|
2020-12-24 01:39:37 +00:00
|
|
|
LDTarget::ImmediateWord(word) => write!(f, "{:#06X}", word),
|
2021-01-03 06:28:07 +00:00
|
|
|
LDTarget::ImmediateByte(byte) => write!(f, "{:#04X}", byte),
|
2020-12-24 01:39:37 +00:00
|
|
|
LDTarget::RegisterPair(pair) => write!(f, "{:?}", pair),
|
2021-01-03 06:28:07 +00:00
|
|
|
LDTarget::ByteAtAddressWithOffset(byte) => {
|
2021-06-02 06:50:16 +00:00
|
|
|
write!(f, "(0xFF00 + {:#04X})", byte)
|
2021-01-03 06:28:07 +00:00
|
|
|
}
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-18 08:22:45 +00:00
|
|
|
|
|
|
|
impl std::fmt::Debug for MATHTarget {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
MATHTarget::Register(reg) => write!(f, "{:?}", reg),
|
|
|
|
MATHTarget::RegisterPair(pair) => write!(f, "{:?}", pair),
|
|
|
|
MATHTarget::ImmediateByte(byte) => write!(f, "{:#04X}", byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-18 08:29:35 +00:00
|
|
|
|
|
|
|
impl std::fmt::Debug for InstrRegisterPair {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
InstrRegisterPair::AF => f.write_str("AF"),
|
|
|
|
InstrRegisterPair::BC => f.write_str("BC"),
|
|
|
|
InstrRegisterPair::DE => f.write_str("DE"),
|
|
|
|
InstrRegisterPair::HL => f.write_str("HL"),
|
|
|
|
InstrRegisterPair::SP => f.write_str("SP"),
|
|
|
|
InstrRegisterPair::PC => f.write_str("PC"),
|
|
|
|
InstrRegisterPair::IncrementHL => f.write_str("HL+"),
|
|
|
|
InstrRegisterPair::DecrementHL => f.write_str("HL-"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-18 08:47:41 +00:00
|
|
|
|
|
|
|
impl std::fmt::Debug for Registers {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
Registers::Byte(reg) => write!(f, "{:?}", reg),
|
|
|
|
Registers::Word(pair) => write!(f, "{:?}", pair),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 04:54:38 +00:00
|
|
|
|
2021-06-02 06:50:16 +00:00
|
|
|
impl std::fmt::Debug for Instruction {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
use Instruction::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
NOP => f.write_str("NOP"),
|
|
|
|
LD(left, right) => write!(f, "LD {:?}, {:?}", left, right),
|
|
|
|
STOP => f.write_str("STOP"),
|
|
|
|
JR(cond, dist) => write!(f, "JR {:?}, {:?}", cond, dist),
|
|
|
|
ADD(left, right) => write!(f, "ADD {:?}, {:?}", left, right),
|
|
|
|
INC(register) => write!(f, "INC {:?}", register),
|
|
|
|
DEC(register) => write!(f, "DEC {:?}", register),
|
|
|
|
RLCA => f.write_str("RLCA"),
|
|
|
|
RRCA => f.write_str("RRCA"),
|
|
|
|
RLA => f.write_str("RLA"),
|
|
|
|
RRA => f.write_str("RRA"),
|
|
|
|
DAA => f.write_str("DAA"),
|
|
|
|
CPL => f.write_str("CPL"),
|
|
|
|
SCF => f.write_str("SCF"),
|
|
|
|
CCF => f.write_str("CCF"),
|
|
|
|
HALT => f.write_str("HALT"),
|
|
|
|
ADC(target) => write!(f, "ADC {:?}", target),
|
|
|
|
SUB(target) => write!(f, "SUB {:?}", target),
|
|
|
|
SBC(target) => write!(f, "SBC {:?}", target),
|
|
|
|
AND(target) => write!(f, "AND {:?}", target),
|
|
|
|
XOR(target) => write!(f, "XOR {:?}", target),
|
|
|
|
OR(target) => write!(f, "OR {:?}", target),
|
|
|
|
CP(target) => write!(f, "CP {:?}", target),
|
|
|
|
RET(cond) => write!(f, "RET {:?}", cond),
|
|
|
|
LDHL(value) => write!(f, "LDHL {:?}", value),
|
|
|
|
POP(pair) => write!(f, "POP {:?}", pair),
|
|
|
|
RETI => f.write_str("RETI"),
|
|
|
|
JP(cond, target) => write!(f, "JP {:?}, {:?}", cond, target),
|
|
|
|
DI => f.write_str("DI"),
|
|
|
|
EI => f.write_str("EI"),
|
|
|
|
CALL(cond, addr) => write!(f, "CALL {:?}, {:?}", cond, addr),
|
|
|
|
PUSH(pair) => write!(f, "PUSH {:?}", pair),
|
|
|
|
RST(vector) => write!(f, "RST {:?}", vector),
|
|
|
|
RLC(register) => write!(f, "RLC {:?}", register),
|
|
|
|
RRC(register) => write!(f, "RRC {:?}", register),
|
|
|
|
RL(register) => write!(f, "RL {:?}", register),
|
|
|
|
RR(register) => write!(f, "RR {:?}", register),
|
|
|
|
SLA(register) => write!(f, "SLA {:?}", register),
|
|
|
|
SRA(register) => write!(f, "SRA {:?}", register),
|
|
|
|
SWAP(register) => write!(f, "SWAP {:?}", register),
|
|
|
|
SRL(register) => write!(f, "SRL {:?}", register),
|
|
|
|
BIT(bit, register) => write!(f, "BIT {:?}, {:?}", bit, register),
|
|
|
|
RES(bit, register) => write!(f, "RES {:?}, {:?}", bit, register),
|
|
|
|
SET(bit, register) => write!(f, "SET {:?}, {:?}", bit, register),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for JumpCondition {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
use JumpCondition::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
NotZero => f.write_str("NZ"),
|
|
|
|
Zero => f.write_str("Z"),
|
|
|
|
NotCarry => f.write_str("NC"),
|
|
|
|
Carry => f.write_str("C"),
|
|
|
|
Always => f.write_str(""),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for InstrRegister {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
use InstrRegister::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
A => f.write_str("A"),
|
|
|
|
B => f.write_str("B"),
|
|
|
|
C => f.write_str("C"),
|
|
|
|
D => f.write_str("D"),
|
|
|
|
E => f.write_str("E"),
|
|
|
|
H => f.write_str("H"),
|
|
|
|
L => f.write_str("L"),
|
|
|
|
IndirectHL => f.write_str("(HL)"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl Cycle {
|
2021-03-21 00:55:39 +00:00
|
|
|
pub const fn new(num: u32) -> Self {
|
2021-01-19 04:54:38 +00:00
|
|
|
Self(num)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Add for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
|
|
Self(self.0 + rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Add<u32> for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn add(self, rhs: u32) -> Self::Output {
|
|
|
|
Self(self.0 + rhs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::AddAssign for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
fn add_assign(&mut self, rhs: Self) {
|
|
|
|
*self = Self(self.0 + rhs.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::AddAssign<u32> for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
fn add_assign(&mut self, rhs: u32) {
|
|
|
|
*self = Self(self.0 + rhs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Rem for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn rem(self, rhs: Self) -> Self::Output {
|
|
|
|
Self(self.0 % rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Rem<u32> for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn rem(self, rhs: u32) -> Self::Output {
|
|
|
|
Self(self.0 % rhs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::RemAssign for Cycle {
|
2021-01-19 06:29:04 +00:00
|
|
|
fn rem_assign(&mut self, rhs: Self) {
|
|
|
|
*self = Self(self.0 % rhs.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::RemAssign<u32> for Cycle {
|
2021-01-19 06:29:04 +00:00
|
|
|
fn rem_assign(&mut self, rhs: u32) {
|
|
|
|
*self = Self(self.0 % rhs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Sub for Cycle {
|
|
|
|
type Output = Cycle;
|
2021-03-21 08:03:03 +00:00
|
|
|
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
|
|
Self(self.0 - rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::Sub<u32> for Cycle {
|
|
|
|
type Output = Cycle;
|
2021-03-21 08:03:03 +00:00
|
|
|
|
|
|
|
fn sub(self, rhs: u32) -> Self::Output {
|
|
|
|
Self(self.0 - rhs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::SubAssign for Cycle {
|
2021-03-21 08:03:03 +00:00
|
|
|
fn sub_assign(&mut self, rhs: Self) {
|
|
|
|
*self = Self(self.0 - rhs.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl std::ops::SubAssign<u32> for Cycle {
|
2021-03-21 08:03:03 +00:00
|
|
|
fn sub_assign(&mut self, rhs: u32) {
|
|
|
|
*self = Self(self.0 - rhs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 18:47:06 +00:00
|
|
|
impl PartialEq<u32> for Cycle {
|
|
|
|
fn eq(&self, other: &u32) -> bool {
|
|
|
|
self.0 == *other
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Div for Cycle {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn div(self, rhs: Self) -> Self::Output {
|
|
|
|
Self::new(self.0 / rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Div<u32> for Cycle {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn div(self, rhs: u32) -> Self::Output {
|
|
|
|
Self::new(self.0 / rhs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl From<u32> for Cycle {
|
2021-01-19 04:54:38 +00:00
|
|
|
fn from(num: u32) -> Self {
|
|
|
|
Self(num)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
impl From<Cycle> for u32 {
|
|
|
|
fn from(cycles: Cycle) -> Self {
|
2021-01-19 06:29:04 +00:00
|
|
|
cycles.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 02:48:12 +00:00
|
|
|
impl InstrRegisterPair {
|
2021-06-07 01:47:11 +00:00
|
|
|
fn to_register_pair(self) -> RegisterPair {
|
2021-07-08 23:50:58 +00:00
|
|
|
RegisterPair::try_from(self).expect("InstrRegisterPair is a valid RegisterPair")
|
2021-03-23 02:48:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InstrRegister {
|
2021-06-07 01:47:11 +00:00
|
|
|
fn to_register(self) -> Register {
|
2021-07-08 23:50:58 +00:00
|
|
|
Register::try_from(self).expect("InstrRegister is a valid Register")
|
2021-03-23 02:48:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-19 04:54:38 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-03-27 17:10:18 +00:00
|
|
|
use super::Cycle;
|
2021-01-19 04:54:38 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cycle_add_works() {
|
2021-03-27 17:10:18 +00:00
|
|
|
let lhs: Cycle = Cycle::new(5);
|
|
|
|
let rhs: Cycle = Cycle::new(4);
|
2021-01-19 04:54:38 +00:00
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
assert_eq!(Cycle::new(9), rhs + lhs);
|
2021-01-19 04:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cycle_add_assign_works() {
|
2021-03-27 17:10:18 +00:00
|
|
|
let mut cycles: Cycle = Cycle::new(5);
|
2021-01-19 04:54:38 +00:00
|
|
|
cycles += 5;
|
|
|
|
|
2021-03-27 17:10:18 +00:00
|
|
|
assert_eq!(Cycle::new(10), cycles);
|
2021-01-19 04:54:38 +00:00
|
|
|
}
|
|
|
|
}
|