feat: reimplement cycles newtype
This commit is contained in:
parent
2fc7ac3833
commit
9b4c95ce4c
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::instruction::Cycles;
|
||||||
|
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::high_ram::HighRAM;
|
use super::high_ram::HighRAM;
|
||||||
use super::interrupt::Interrupt;
|
use super::interrupt::Interrupt;
|
||||||
|
@ -50,6 +52,12 @@ impl Bus {
|
||||||
pub fn load_cartridge(&mut self, path: &str) {
|
pub fn load_cartridge(&mut self, path: &str) {
|
||||||
self.cartridge = Some(Cartridge::new(path).unwrap());
|
self.cartridge = Some(Cartridge::new(path).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, cycles: Cycles) {
|
||||||
|
let _ = self.timer.step(cycles);
|
||||||
|
let _ = self.sound.step(cycles);
|
||||||
|
let _ = self.ppu.step(cycles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
|
|
15
src/cpu.rs
15
src/cpu.rs
|
@ -68,6 +68,21 @@ impl Cpu {
|
||||||
pub fn execute(&mut self, instruction: Instruction) -> Cycles {
|
pub fn execute(&mut self, instruction: Instruction) -> Cycles {
|
||||||
Instruction::execute(self, instruction)
|
Instruction::execute(self, instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self) -> Cycles {
|
||||||
|
let opcode = self.fetch();
|
||||||
|
let instr = self.decode(opcode);
|
||||||
|
let cycles = self.execute(instr);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Addr: {:#06X} | Opcode: {:#04X} | Instr: {:X?}",
|
||||||
|
self.reg.pc, opcode, instr
|
||||||
|
);
|
||||||
|
|
||||||
|
self.bus.step(cycles);
|
||||||
|
|
||||||
|
cycles
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub struct HighRAM {
|
||||||
impl Default for HighRAM {
|
impl Default for HighRAM {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
buf: vec![0u8; 127].into_boxed_slice(),
|
buf: vec![0u8; 128].into_boxed_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,18 +118,18 @@ pub enum JumpCondition {
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
struct Table;
|
struct Table;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Cycles(u8);
|
pub struct Cycles(u32);
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
pub fn execute(cpu: &mut Cpu, instruction: Self) -> Cycles {
|
pub fn execute(cpu: &mut Cpu, instruction: Self) -> Cycles {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::NOP => Cycles(4),
|
Instruction::NOP => Cycles::new(4),
|
||||||
Instruction::LD(lhs, rhs) => match (lhs, rhs) {
|
Instruction::LD(lhs, rhs) => match (lhs, rhs) {
|
||||||
(LDTarget::ByteAtAddress(nn), LDTarget::RegisterPair(RegisterPair::SP)) => {
|
(LDTarget::ByteAtAddress(nn), LDTarget::RegisterPair(RegisterPair::SP)) => {
|
||||||
// LD (nn), SP | Put Stack Pointer at address nn
|
// LD (nn), SP | Put Stack Pointer at address nn
|
||||||
cpu.write_word(nn, cpu.register_pair(RegisterPair::SP));
|
cpu.write_word(nn, cpu.register_pair(RegisterPair::SP));
|
||||||
Cycles(20)
|
Cycles::new(20)
|
||||||
}
|
}
|
||||||
(LDTarget::RegisterPair(pair), LDTarget::ImmediateWord(nn)) => {
|
(LDTarget::RegisterPair(pair), LDTarget::ImmediateWord(nn)) => {
|
||||||
// LD rp[p], nn | Put value nn into register pair
|
// LD rp[p], nn | Put value nn into register pair
|
||||||
|
@ -140,7 +140,7 @@ impl Instruction {
|
||||||
| RegisterPair::SP => cpu.set_register_pair(pair, nn),
|
| RegisterPair::SP => cpu.set_register_pair(pair, nn),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
(LDTarget::IndirectRegister(pair), LDTarget::Register(InstrRegister::A)) => {
|
(LDTarget::IndirectRegister(pair), LDTarget::Register(InstrRegister::A)) => {
|
||||||
let a = cpu.register(Register::A);
|
let a = cpu.register(Register::A);
|
||||||
|
@ -167,7 +167,7 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectRegister(pair)) => {
|
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectRegister(pair)) => {
|
||||||
match pair {
|
match pair {
|
||||||
|
@ -196,7 +196,7 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(reg), LDTarget::ImmediateByte(n)) => {
|
(LDTarget::Register(reg), LDTarget::ImmediateByte(n)) => {
|
||||||
// LD r[y], n | Store n in Register
|
// LD r[y], n | Store n in Register
|
||||||
|
@ -204,7 +204,7 @@ impl Instruction {
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
cpu.write_byte(addr, n);
|
cpu.write_byte(addr, n);
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
InstrRegister::A
|
InstrRegister::A
|
||||||
| InstrRegister::B
|
| InstrRegister::B
|
||||||
|
@ -214,7 +214,7 @@ impl Instruction {
|
||||||
| InstrRegister::H
|
| InstrRegister::H
|
||||||
| InstrRegister::L => {
|
| InstrRegister::L => {
|
||||||
cpu.set_register(Register::try_from(reg).unwrap(), n);
|
cpu.set_register(Register::try_from(reg).unwrap(), n);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,13 +222,13 @@ impl Instruction {
|
||||||
// LD (0xFF00 + C), A | Store value of register A at address 0xFF00 + C
|
// LD (0xFF00 + C), A | Store value of register A at address 0xFF00 + C
|
||||||
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
||||||
cpu.write_byte(addr, cpu.register(Register::A));
|
cpu.write_byte(addr, cpu.register(Register::A));
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectC) => {
|
(LDTarget::Register(InstrRegister::A), LDTarget::IndirectC) => {
|
||||||
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
let addr = 0xFF00 + cpu.register(Register::C) as u16;
|
||||||
let byte = cpu.read_byte(addr);
|
let byte = cpu.read_byte(addr);
|
||||||
cpu.set_register(Register::A, byte);
|
cpu.set_register(Register::A, byte);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(lhs), LDTarget::Register(rhs)) => {
|
(LDTarget::Register(lhs), LDTarget::Register(rhs)) => {
|
||||||
// LD r[y], r[z] | Store value of RHS Register in LHS Register
|
// LD r[y], r[z] | Store value of RHS Register in LHS Register
|
||||||
|
@ -258,25 +258,25 @@ impl Instruction {
|
||||||
| InstrRegister::L
|
| InstrRegister::L
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
cpu.set_register(Register::try_from(lhs).unwrap(), rhs_value);
|
cpu.set_register(Register::try_from(lhs).unwrap(), rhs_value);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
cpu.write_byte(addr, rhs_value);
|
cpu.write_byte(addr, rhs_value);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(LDTarget::ByteAtAddressWithOffset(n), LDTarget::Register(InstrRegister::A)) => {
|
(LDTarget::ByteAtAddressWithOffset(n), LDTarget::Register(InstrRegister::A)) => {
|
||||||
// LD (0xFF00 + n), A | Store register A at address (0xFF00 + n)
|
// LD (0xFF00 + n), A | Store register A at address (0xFF00 + n)
|
||||||
cpu.write_byte(0xFF00 + (n as u16), cpu.register(Register::A));
|
cpu.write_byte(0xFF00 + (n as u16), cpu.register(Register::A));
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddressWithOffset(n)) => {
|
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddressWithOffset(n)) => {
|
||||||
// LD A, (0xFF00 + n) | Store value at address (0xFF00 + n) in register A
|
// LD A, (0xFF00 + n) | Store value at address (0xFF00 + n) in register A
|
||||||
let byte = cpu.read_byte(0xFF00 + (n as u16));
|
let byte = cpu.read_byte(0xFF00 + (n as u16));
|
||||||
cpu.set_register(Register::A, byte);
|
cpu.set_register(Register::A, byte);
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
LDTarget::RegisterPair(RegisterPair::SP),
|
LDTarget::RegisterPair(RegisterPair::SP),
|
||||||
|
@ -284,20 +284,20 @@ impl Instruction {
|
||||||
) => {
|
) => {
|
||||||
// LD SP, HL | Load Register HL into Register SP
|
// LD SP, HL | Load Register HL into Register SP
|
||||||
cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL));
|
cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL));
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(LDTarget::ByteAtAddress(nn), LDTarget::Register(InstrRegister::A)) => {
|
(LDTarget::ByteAtAddress(nn), LDTarget::Register(InstrRegister::A)) => {
|
||||||
cpu.write_byte(nn, cpu.register(Register::A));
|
cpu.write_byte(nn, cpu.register(Register::A));
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddress(nn)) => {
|
(LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddress(nn)) => {
|
||||||
let byte = cpu.read_byte(nn);
|
let byte = cpu.read_byte(nn);
|
||||||
cpu.set_register(Register::A, byte);
|
cpu.set_register(Register::A, byte);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
Instruction::STOP => Cycles(4),
|
Instruction::STOP => Cycles::new(4),
|
||||||
Instruction::JR(cond, offset) => {
|
Instruction::JR(cond, offset) => {
|
||||||
// JR cc[y - 4], d | If condition is true, then add d to current address and jump
|
// 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
|
// JR d | Add d to current address and jump
|
||||||
|
@ -308,36 +308,36 @@ impl Instruction {
|
||||||
match cond {
|
match cond {
|
||||||
JumpCondition::Always => {
|
JumpCondition::Always => {
|
||||||
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::NotZero => {
|
JumpCondition::NotZero => {
|
||||||
if !flags.z {
|
if !flags.z {
|
||||||
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
||||||
return Cycles(12);
|
return Cycles::new(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::Zero => {
|
JumpCondition::Zero => {
|
||||||
if flags.z {
|
if flags.z {
|
||||||
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
||||||
return Cycles(12);
|
return Cycles::new(12);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::NotCarry => {
|
JumpCondition::NotCarry => {
|
||||||
if !flags.c {
|
if !flags.c {
|
||||||
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
||||||
return Cycles(12);
|
return Cycles::new(12);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::Carry => {
|
JumpCondition::Carry => {
|
||||||
if flags.c {
|
if flags.c {
|
||||||
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
cpu.set_register_pair(RegisterPair::PC, new_addr);
|
||||||
return Cycles(12);
|
return Cycles::new(12);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ impl Instruction {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
(MATHTarget::Register(InstrRegister::A), MATHTarget::Register(reg)) => {
|
(MATHTarget::Register(InstrRegister::A), MATHTarget::Register(reg)) => {
|
||||||
// ADD A, r[z] | Add (A + r[z]) to register A
|
// ADD A, r[z] | Add (A + r[z]) to register A
|
||||||
|
@ -378,12 +378,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
sum = Self::add_u8s(a_value, value, &mut flags);
|
sum = Self::add_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
sum = Self::add_u8s(a_value, value, &mut flags);
|
sum = Self::add_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ impl Instruction {
|
||||||
let d = d as i8;
|
let d = d as i8;
|
||||||
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
||||||
cpu.set_register_pair(RegisterPair::SP, sum);
|
cpu.set_register_pair(RegisterPair::SP, sum);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
(MATHTarget::Register(InstrRegister::A), MATHTarget::ImmediateByte(n)) => {
|
(MATHTarget::Register(InstrRegister::A), MATHTarget::ImmediateByte(n)) => {
|
||||||
// ADD A, n | Add n to register A
|
// ADD A, n | Add n to register A
|
||||||
|
@ -406,7 +406,7 @@ impl Instruction {
|
||||||
|
|
||||||
cpu.set_register(Register::A, sum);
|
cpu.set_register(Register::A, sum);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -429,13 +429,13 @@ impl Instruction {
|
||||||
|
|
||||||
let value = cpu.register(reg);
|
let value = cpu.register(reg);
|
||||||
cpu.set_register(reg, Self::inc_register(value, &mut flags));
|
cpu.set_register(reg, Self::inc_register(value, &mut flags));
|
||||||
cycles = Cycles(4)
|
cycles = Cycles::new(4)
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::inc_register(cpu.read_byte(addr), &mut flags);
|
let byte = Self::inc_register(cpu.read_byte(addr), &mut flags);
|
||||||
cpu.write_byte(addr, byte);
|
cpu.write_byte(addr, byte);
|
||||||
cycles = Cycles(12)
|
cycles = Cycles::new(12)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
|
@ -453,7 +453,7 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,7 +466,7 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
Instruction::DEC(Registers::Byte(reg)) => {
|
Instruction::DEC(Registers::Byte(reg)) => {
|
||||||
// DEC r[y] | Decrement Register
|
// DEC r[y] | Decrement Register
|
||||||
|
@ -485,13 +485,13 @@ impl Instruction {
|
||||||
|
|
||||||
let value = cpu.register(reg);
|
let value = cpu.register(reg);
|
||||||
cpu.set_register(reg, Self::dec_register(value, &mut flags));
|
cpu.set_register(reg, Self::dec_register(value, &mut flags));
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = cpu.read_byte(addr);
|
let byte = cpu.read_byte(addr);
|
||||||
cpu.write_byte(addr, Self::dec_register(byte, &mut flags));
|
cpu.write_byte(addr, Self::dec_register(byte, &mut flags));
|
||||||
cycles = Cycles(12);
|
cycles = Cycles::new(12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
|
@ -508,7 +508,7 @@ impl Instruction {
|
||||||
flags.update(false, false, false, msb == 0x01);
|
flags.update(false, false, false, msb == 0x01);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, rot_a);
|
cpu.set_register(Register::A, rot_a);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::RRCA => {
|
Instruction::RRCA => {
|
||||||
// Rotate Register A right
|
// Rotate Register A right
|
||||||
|
@ -521,7 +521,7 @@ impl Instruction {
|
||||||
flags.update(false, false, false, lsb == 0x01);
|
flags.update(false, false, false, lsb == 0x01);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, rot_a);
|
cpu.set_register(Register::A, rot_a);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::RLA => {
|
Instruction::RLA => {
|
||||||
// Rotate register A left through carry
|
// Rotate register A left through carry
|
||||||
|
@ -533,7 +533,7 @@ impl Instruction {
|
||||||
flags.update(false, false, false, carry);
|
flags.update(false, false, false, carry);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, rot_a);
|
cpu.set_register(Register::A, rot_a);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::RRA => {
|
Instruction::RRA => {
|
||||||
// Rotate register A right through carry
|
// Rotate register A right through carry
|
||||||
|
@ -545,7 +545,7 @@ impl Instruction {
|
||||||
flags.update(false, false, false, carry);
|
flags.update(false, false, false, carry);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, rot_a);
|
cpu.set_register(Register::A, rot_a);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::DAA => unimplemented!(),
|
Instruction::DAA => unimplemented!(),
|
||||||
Instruction::CPL => {
|
Instruction::CPL => {
|
||||||
|
@ -558,7 +558,7 @@ impl Instruction {
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, !a); // Bitwise not is ! instead of ~
|
cpu.set_register(Register::A, !a); // Bitwise not is ! instead of ~
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::SCF => {
|
Instruction::SCF => {
|
||||||
// Set Carry Flag
|
// Set Carry Flag
|
||||||
|
@ -569,7 +569,7 @@ impl Instruction {
|
||||||
flags.c = true;
|
flags.c = true;
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::CCF => {
|
Instruction::CCF => {
|
||||||
// Compliment Carry Flag (inverse)
|
// Compliment Carry Flag (inverse)
|
||||||
|
@ -580,7 +580,7 @@ impl Instruction {
|
||||||
flags.c = !flags.c;
|
flags.c = !flags.c;
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::HALT => unimplemented!(),
|
Instruction::HALT => unimplemented!(),
|
||||||
Instruction::ADC(target) => match target {
|
Instruction::ADC(target) => match target {
|
||||||
|
@ -603,12 +603,12 @@ impl Instruction {
|
||||||
let value =
|
let value =
|
||||||
cpu.register(Register::try_from(reg).unwrap()) + (flags.c as u8);
|
cpu.register(Register::try_from(reg).unwrap()) + (flags.c as u8);
|
||||||
sum = Self::add_u8s(a_value, value, &mut flags);
|
sum = Self::add_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
sum = Self::add_u8s(a_value, value, &mut flags);
|
sum = Self::add_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
|
@ -623,7 +623,7 @@ impl Instruction {
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, sum);
|
cpu.set_register(Register::A, sum);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -645,12 +645,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
diff = Self::sub_u8s(a_value, value, &mut flags);
|
diff = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
diff = Self::sub_u8s(a_value, value, &mut flags);
|
diff = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +665,7 @@ impl Instruction {
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, diff);
|
cpu.set_register(Register::A, diff);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -689,12 +689,12 @@ impl Instruction {
|
||||||
let value =
|
let value =
|
||||||
cpu.register(Register::try_from(reg).unwrap()) + (flags.c as u8);
|
cpu.register(Register::try_from(reg).unwrap()) + (flags.c as u8);
|
||||||
diff = Self::sub_u8s(a_value, value, &mut flags);
|
diff = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
diff = Self::sub_u8s(a_value, value, &mut flags);
|
diff = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +711,7 @@ impl Instruction {
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, diff);
|
cpu.set_register(Register::A, diff);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -733,12 +733,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
result = a_value & value;
|
result = a_value & value;
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
result = a_value & value;
|
result = a_value & value;
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +755,7 @@ impl Instruction {
|
||||||
flags.update(result == 0, false, true, false);
|
flags.update(result == 0, false, true, false);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, result);
|
cpu.set_register(Register::A, result);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -777,12 +777,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
result = a_value ^ value;
|
result = a_value ^ value;
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
result = a_value ^ value;
|
result = a_value ^ value;
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -799,7 +799,7 @@ impl Instruction {
|
||||||
flags.update(result == 0, false, false, false);
|
flags.update(result == 0, false, false, false);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, result);
|
cpu.set_register(Register::A, result);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -821,12 +821,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
result = a_value | value;
|
result = a_value | value;
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
result = a_value | value;
|
result = a_value | value;
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,7 +843,7 @@ impl Instruction {
|
||||||
flags.update(result == 0, false, false, false);
|
flags.update(result == 0, false, false, false);
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
cpu.set_register(Register::A, result);
|
cpu.set_register(Register::A, result);
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -864,12 +864,12 @@ impl Instruction {
|
||||||
| InstrRegister::A => {
|
| InstrRegister::A => {
|
||||||
let value = cpu.register(Register::try_from(reg).unwrap());
|
let value = cpu.register(Register::try_from(reg).unwrap());
|
||||||
let _ = Self::sub_u8s(a_value, value, &mut flags);
|
let _ = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(4);
|
cycles = Cycles::new(4);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL));
|
||||||
let _ = Self::sub_u8s(a_value, value, &mut flags);
|
let _ = Self::sub_u8s(a_value, value, &mut flags);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,7 +882,7 @@ impl Instruction {
|
||||||
let _ = Self::sub_u8s(cpu.register(Register::A), n, &mut flags);
|
let _ = Self::sub_u8s(cpu.register(Register::A), n, &mut flags);
|
||||||
|
|
||||||
cpu.set_register(Register::Flag, flags.into());
|
cpu.set_register(Register::Flag, flags.into());
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
@ -896,38 +896,38 @@ impl Instruction {
|
||||||
if !flags.z {
|
if !flags.z {
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
return Cycles(20);
|
return Cycles::new(20);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::Zero => {
|
JumpCondition::Zero => {
|
||||||
if flags.z {
|
if flags.z {
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
return Cycles(20);
|
return Cycles::new(20);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::NotCarry => {
|
JumpCondition::NotCarry => {
|
||||||
if !flags.c {
|
if !flags.c {
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
return Cycles(20);
|
return Cycles::new(20);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::Carry => {
|
JumpCondition::Carry => {
|
||||||
if flags.c {
|
if flags.c {
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
return Cycles(20);
|
return Cycles::new(20);
|
||||||
}
|
}
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
JumpCondition::Always => {
|
JumpCondition::Always => {
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -937,7 +937,7 @@ impl Instruction {
|
||||||
let mut flags: Flags = cpu.register(Register::Flag).into();
|
let mut flags: Flags = cpu.register(Register::Flag).into();
|
||||||
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags);
|
||||||
cpu.set_register_pair(RegisterPair::HL, sum);
|
cpu.set_register_pair(RegisterPair::HL, sum);
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
Instruction::POP(pair) => {
|
Instruction::POP(pair) => {
|
||||||
// POP rp2[p] | Pop from stack into register pair rp[2]
|
// POP rp2[p] | Pop from stack into register pair rp[2]
|
||||||
|
@ -949,20 +949,20 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
Instruction::RETI => {
|
Instruction::RETI => {
|
||||||
// Same as RET, after which interrupts are enabled.
|
// Same as RET, after which interrupts are enabled.
|
||||||
let addr = Self::pop(cpu);
|
let addr = Self::pop(cpu);
|
||||||
cpu.set_register_pair(RegisterPair::PC, addr);
|
cpu.set_register_pair(RegisterPair::PC, addr);
|
||||||
cpu.set_ime(true);
|
cpu.set_ime(true);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
Instruction::JP(cond, target) => match target {
|
Instruction::JP(cond, target) => match target {
|
||||||
JPTarget::RegisterPair(RegisterPair::HL) => {
|
JPTarget::RegisterPair(RegisterPair::HL) => {
|
||||||
// JP HL | Load register pair HL into program counter
|
// JP HL | Load register pair HL into program counter
|
||||||
cpu.set_register_pair(RegisterPair::PC, cpu.register_pair(RegisterPair::HL));
|
cpu.set_register_pair(RegisterPair::PC, cpu.register_pair(RegisterPair::HL));
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
JPTarget::ImmediateWord(nn) => {
|
JPTarget::ImmediateWord(nn) => {
|
||||||
// JP cc[y], nn | Store Immediate Word in the Program Counter if cond is met
|
// JP cc[y], nn | Store Immediate Word in the Program Counter if cond is met
|
||||||
|
@ -973,34 +973,34 @@ impl Instruction {
|
||||||
JumpCondition::NotZero => {
|
JumpCondition::NotZero => {
|
||||||
if !flags.z {
|
if !flags.z {
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(16);
|
return Cycles::new(16);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Zero => {
|
JumpCondition::Zero => {
|
||||||
if flags.z {
|
if flags.z {
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(16);
|
return Cycles::new(16);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::NotCarry => {
|
JumpCondition::NotCarry => {
|
||||||
if !flags.c {
|
if !flags.c {
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(16);
|
return Cycles::new(16);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Carry => {
|
JumpCondition::Carry => {
|
||||||
if flags.c {
|
if flags.c {
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(16);
|
return Cycles::new(16);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Always => {
|
JumpCondition::Always => {
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1009,13 +1009,13 @@ impl Instruction {
|
||||||
Instruction::DI => {
|
Instruction::DI => {
|
||||||
// Disable IME
|
// Disable IME
|
||||||
cpu.set_ime(false);
|
cpu.set_ime(false);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::EI => {
|
Instruction::EI => {
|
||||||
// Enable IME (After the next instruction)
|
// Enable IME (After the next instruction)
|
||||||
// FIXME: IME is set after the next instruction, this currently is not represented in this emulator.
|
// FIXME: IME is set after the next instruction, this currently is not represented in this emulator.
|
||||||
cpu.set_ime(true);
|
cpu.set_ime(true);
|
||||||
Cycles(4)
|
Cycles::new(4)
|
||||||
}
|
}
|
||||||
Instruction::CALL(cond, nn) => {
|
Instruction::CALL(cond, nn) => {
|
||||||
// CALL cc[y], nn | Store nn on the stack, then store nn in the program coutner if cond is met
|
// CALL cc[y], nn | Store nn on the stack, then store nn in the program coutner if cond is met
|
||||||
|
@ -1028,38 +1028,38 @@ impl Instruction {
|
||||||
if !flags.z {
|
if !flags.z {
|
||||||
Self::push(cpu, pc);
|
Self::push(cpu, pc);
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(24);
|
return Cycles::new(24);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Zero => {
|
JumpCondition::Zero => {
|
||||||
if flags.z {
|
if flags.z {
|
||||||
Self::push(cpu, pc);
|
Self::push(cpu, pc);
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(24);
|
return Cycles::new(24);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::NotCarry => {
|
JumpCondition::NotCarry => {
|
||||||
if !flags.c {
|
if !flags.c {
|
||||||
Self::push(cpu, pc);
|
Self::push(cpu, pc);
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(24);
|
return Cycles::new(24);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Carry => {
|
JumpCondition::Carry => {
|
||||||
if flags.c {
|
if flags.c {
|
||||||
Self::push(cpu, pc);
|
Self::push(cpu, pc);
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
return Cycles(24);
|
return Cycles::new(24);
|
||||||
}
|
}
|
||||||
Cycles(12)
|
Cycles::new(12)
|
||||||
}
|
}
|
||||||
JumpCondition::Always => {
|
JumpCondition::Always => {
|
||||||
Self::push(cpu, pc);
|
Self::push(cpu, pc);
|
||||||
cpu.set_register_pair(RegisterPair::PC, nn);
|
cpu.set_register_pair(RegisterPair::PC, nn);
|
||||||
Cycles(24)
|
Cycles::new(24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1072,14 +1072,14 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
Instruction::RST(n) => {
|
Instruction::RST(n) => {
|
||||||
// RST n | Push current address onto the stack, jump to 0x0000 + n
|
// RST n | Push current address onto the stack, jump to 0x0000 + n
|
||||||
let addr = cpu.register_pair(RegisterPair::PC);
|
let addr = cpu.register_pair(RegisterPair::PC);
|
||||||
Self::push(cpu, addr);
|
Self::push(cpu, addr);
|
||||||
cpu.set_register_pair(RegisterPair::PC, n as u16);
|
cpu.set_register_pair(RegisterPair::PC, n as u16);
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
Instruction::RLC(reg) => {
|
Instruction::RLC(reg) => {
|
||||||
// RLC r[z] | Rotate register r[z] left
|
// RLC r[z] | Rotate register r[z] left
|
||||||
|
@ -1103,7 +1103,7 @@ impl Instruction {
|
||||||
rot_reg = value.rotate_left(1);
|
rot_reg = value.rotate_left(1);
|
||||||
|
|
||||||
cpu.set_register(register, rot_reg);
|
cpu.set_register(register, rot_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1113,7 +1113,7 @@ impl Instruction {
|
||||||
rot_reg = value.rotate_left(1);
|
rot_reg = value.rotate_left(1);
|
||||||
|
|
||||||
cpu.write_byte(addr, rot_reg);
|
cpu.write_byte(addr, rot_reg);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,7 +1143,7 @@ impl Instruction {
|
||||||
rot_reg = value.rotate_right(1);
|
rot_reg = value.rotate_right(1);
|
||||||
|
|
||||||
cpu.set_register(register, rot_reg);
|
cpu.set_register(register, rot_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1153,7 +1153,7 @@ impl Instruction {
|
||||||
rot_reg = value.rotate_right(1);
|
rot_reg = value.rotate_right(1);
|
||||||
|
|
||||||
cpu.write_byte(addr, rot_reg);
|
cpu.write_byte(addr, rot_reg);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1184,7 +1184,7 @@ impl Instruction {
|
||||||
carry = new_carry;
|
carry = new_carry;
|
||||||
|
|
||||||
cpu.set_register(register, rot_reg);
|
cpu.set_register(register, rot_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1195,7 +1195,7 @@ impl Instruction {
|
||||||
carry = new_carry;
|
carry = new_carry;
|
||||||
|
|
||||||
cpu.write_byte(addr, rot_reg);
|
cpu.write_byte(addr, rot_reg);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,7 +1227,7 @@ impl Instruction {
|
||||||
carry = new_carry;
|
carry = new_carry;
|
||||||
|
|
||||||
cpu.set_register(register, rot_reg);
|
cpu.set_register(register, rot_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1238,7 +1238,7 @@ impl Instruction {
|
||||||
carry = new_carry;
|
carry = new_carry;
|
||||||
|
|
||||||
cpu.write_byte(addr, rot_reg);
|
cpu.write_byte(addr, rot_reg);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1269,7 +1269,7 @@ impl Instruction {
|
||||||
shift_reg = value << 1;
|
shift_reg = value << 1;
|
||||||
|
|
||||||
cpu.set_register(register, shift_reg);
|
cpu.set_register(register, shift_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1279,7 +1279,7 @@ impl Instruction {
|
||||||
shift_reg = value << 1;
|
shift_reg = value << 1;
|
||||||
|
|
||||||
cpu.write_byte(addr, value);
|
cpu.write_byte(addr, value);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,7 +1311,7 @@ impl Instruction {
|
||||||
shift_reg = (value >> 1) | (msb << 7); // msb is duplicated in this op
|
shift_reg = (value >> 1) | (msb << 7); // msb is duplicated in this op
|
||||||
|
|
||||||
cpu.set_register(register, shift_reg);
|
cpu.set_register(register, shift_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1322,7 +1322,7 @@ impl Instruction {
|
||||||
shift_reg = (value >> 1) | (msb << 7); // msb is duplicated in this op
|
shift_reg = (value >> 1) | (msb << 7); // msb is duplicated in this op
|
||||||
|
|
||||||
cpu.write_byte(addr, value);
|
cpu.write_byte(addr, value);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,7 +1351,7 @@ impl Instruction {
|
||||||
swap_reg = Self::swap_bits(value);
|
swap_reg = Self::swap_bits(value);
|
||||||
|
|
||||||
cpu.set_register(register, swap_reg);
|
cpu.set_register(register, swap_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
|
@ -1361,7 +1361,7 @@ impl Instruction {
|
||||||
swap_reg = Self::swap_bits(value);
|
swap_reg = Self::swap_bits(value);
|
||||||
|
|
||||||
cpu.write_byte(addr, swap_reg);
|
cpu.write_byte(addr, swap_reg);
|
||||||
cycles = Cycles(16)
|
cycles = Cycles::new(16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1392,7 +1392,7 @@ impl Instruction {
|
||||||
shift_reg = value >> 1;
|
shift_reg = value >> 1;
|
||||||
|
|
||||||
cpu.set_register(register, shift_reg);
|
cpu.set_register(register, shift_reg);
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
|
@ -1402,7 +1402,7 @@ impl Instruction {
|
||||||
shift_reg = value >> 1;
|
shift_reg = value >> 1;
|
||||||
|
|
||||||
cpu.write_byte(addr, shift_reg);
|
cpu.write_byte(addr, shift_reg);
|
||||||
cycles = Cycles(16);
|
cycles = Cycles::new(16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1428,14 +1428,14 @@ impl Instruction {
|
||||||
let value = cpu.register(register);
|
let value = cpu.register(register);
|
||||||
|
|
||||||
is_bit_set = ((value >> y) & 0x01) == 0x01;
|
is_bit_set = ((value >> y) & 0x01) == 0x01;
|
||||||
cycles = Cycles(8);
|
cycles = Cycles::new(8);
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let value = cpu.read_byte(addr);
|
let value = cpu.read_byte(addr);
|
||||||
|
|
||||||
is_bit_set = ((value >> y) & 0x01) == 0x01;
|
is_bit_set = ((value >> y) & 0x01) == 0x01;
|
||||||
cycles = Cycles(12);
|
cycles = Cycles::new(12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1462,14 +1462,14 @@ impl Instruction {
|
||||||
let value = cpu.register(register);
|
let value = cpu.register(register);
|
||||||
|
|
||||||
cpu.set_register(register, value & !(1u8 << y));
|
cpu.set_register(register, value & !(1u8 << y));
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let value = cpu.read_byte(addr);
|
let value = cpu.read_byte(addr);
|
||||||
|
|
||||||
cpu.write_byte(addr, value & !(1u8 << y));
|
cpu.write_byte(addr, value & !(1u8 << y));
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1490,14 +1490,14 @@ impl Instruction {
|
||||||
let value = cpu.register(register);
|
let value = cpu.register(register);
|
||||||
|
|
||||||
cpu.set_register(register, value | (1u8 << y));
|
cpu.set_register(register, value | (1u8 << y));
|
||||||
Cycles(8)
|
Cycles::new(8)
|
||||||
}
|
}
|
||||||
InstrRegister::IndirectHL => {
|
InstrRegister::IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let value = cpu.read_byte(addr);
|
let value = cpu.read_byte(addr);
|
||||||
|
|
||||||
cpu.write_byte(addr, value | (1u8 << y));
|
cpu.write_byte(addr, value | (1u8 << y));
|
||||||
Cycles(16)
|
Cycles::new(16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2085,3 +2085,80 @@ impl std::fmt::Debug for Registers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Cycles {
|
||||||
|
pub fn new(num: u32) -> Self {
|
||||||
|
Self(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Cycles {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 + rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add<u32> for Cycles {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: u32) -> Self::Output {
|
||||||
|
Self(self.0 + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for Cycles {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
*self = Self(self.0 + rhs.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign<u32> for Cycles {
|
||||||
|
fn add_assign(&mut self, rhs: u32) {
|
||||||
|
*self = Self(self.0 + rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Rem for Cycles {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn rem(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 % rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Rem<u32> for Cycles {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn rem(self, rhs: u32) -> Self::Output {
|
||||||
|
Self(self.0 % rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Cycles {
|
||||||
|
fn from(num: u32) -> Self {
|
||||||
|
Self(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Cycles;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cycle_add_works() {
|
||||||
|
let lhs: Cycles = Cycles::new(0);
|
||||||
|
let rhs: Cycles = Cycles::new(0);
|
||||||
|
|
||||||
|
assert_eq!(Cycles::new(9), rhs + lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cycle_add_assign_works() {
|
||||||
|
let mut cycles: Cycles = Cycles::new(0);
|
||||||
|
cycles += 5;
|
||||||
|
|
||||||
|
assert_eq!(Cycles::new(10), cycles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -1,7 +1,6 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gb::cpu::Cpu as LR35902;
|
use gb::cpu::Cpu as LR35902;
|
||||||
use pixels::{Pixels, SurfaceTexture};
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
use std::io::Write;
|
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::LogicalSize,
|
dpi::LogicalSize,
|
||||||
event::{Event, VirtualKeyCode},
|
event::{Event, VirtualKeyCode},
|
||||||
|
@ -20,9 +19,6 @@ fn main() -> Result<()> {
|
||||||
let mut input = WinitInputHelper::new();
|
let mut input = WinitInputHelper::new();
|
||||||
let window = create_window(&event_loop)?;
|
let window = create_window(&event_loop)?;
|
||||||
let mut pixels = create_pixels(&window)?;
|
let mut pixels = create_pixels(&window)?;
|
||||||
|
|
||||||
let out = Box::leak(Box::new(std::io::stdout()));
|
|
||||||
let mut out_handle = out.lock();
|
|
||||||
let mut game_boy = LR35902::new();
|
let mut game_boy = LR35902::new();
|
||||||
game_boy.load_cartridge("bin/cpu_instrs.gb");
|
game_boy.load_cartridge("bin/cpu_instrs.gb");
|
||||||
|
|
||||||
|
@ -54,19 +50,8 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulation
|
// Emulation
|
||||||
let addr = game_boy.register_pair(gb::cpu::RegisterPair::PC);
|
let _cycles = game_boy.step();
|
||||||
|
// window.request_redraw();
|
||||||
let opcode = game_boy.fetch();
|
|
||||||
let instruction = game_boy.decode(opcode);
|
|
||||||
let _cycles = game_boy.execute(instruction);
|
|
||||||
window.request_redraw();
|
|
||||||
|
|
||||||
write!(
|
|
||||||
out_handle,
|
|
||||||
"Addr: {:#06X} | Opcode: {:#04X} | Instr: {:X?}\n",
|
|
||||||
addr, opcode, instruction
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
31
src/ppu.rs
31
src/ppu.rs
|
@ -1,14 +1,32 @@
|
||||||
|
use crate::instruction::Cycles;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
pub lcd_control: LCDControl,
|
pub lcd_control: LCDControl,
|
||||||
pub monochrome: Monochrome,
|
pub monochrome: Monochrome,
|
||||||
pub pos: ScreenPosition,
|
pub pos: ScreenPosition,
|
||||||
pub vram: Box<[u8]>,
|
pub vram: Box<[u8]>,
|
||||||
|
pub oam: Box<[u8]>,
|
||||||
pub stat: LCDStatus,
|
pub stat: LCDStatus,
|
||||||
|
cycles: Cycles,
|
||||||
|
mode: PPUMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
pub fn step(&mut self) {}
|
pub fn step(&mut self, cycles: Cycles) {
|
||||||
|
self.cycles += cycles;
|
||||||
|
|
||||||
|
match self.mode {
|
||||||
|
PPUMode::OAMScan => {
|
||||||
|
if self.cycles >= 80.into() {
|
||||||
|
self.cycles = cycles % 80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PPUMode::Draw => {}
|
||||||
|
PPUMode::HBlank => {}
|
||||||
|
PPUMode::VBlank => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw(&self, frame: &mut [u8]) {
|
pub fn draw(&self, frame: &mut [u8]) {
|
||||||
for (_i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
for (_i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
||||||
|
@ -26,10 +44,21 @@ impl Default for PPU {
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
stat: Default::default(),
|
stat: Default::default(),
|
||||||
vram: vec![0; 8192].into_boxed_slice(),
|
vram: vec![0; 8192].into_boxed_slice(),
|
||||||
|
oam: vec![0; 160].into_boxed_slice(),
|
||||||
|
cycles: 0.into(),
|
||||||
|
mode: PPUMode::OAMScan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum PPUMode {
|
||||||
|
OAMScan,
|
||||||
|
Draw,
|
||||||
|
HBlank,
|
||||||
|
VBlank,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct LCDStatus {
|
pub struct LCDStatus {
|
||||||
lyc_eq_ly: bool,
|
lyc_eq_ly: bool,
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
|
use crate::instruction::Cycles;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct Sound {
|
pub struct Sound {
|
||||||
pub control: SoundControl,
|
pub control: SoundControl,
|
||||||
pub ch1: Channel1,
|
pub ch1: Channel1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sound {
|
||||||
|
pub fn step(&mut self, _cycles: Cycles) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct SoundControl {
|
pub struct SoundControl {
|
||||||
pub channel: ChannelControl,
|
pub channel: ChannelControl,
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
|
use crate::instruction::Cycles;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
pub control: TimerControl,
|
pub control: TimerControl,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn step(&mut self, _cycles: Cycles) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Timer {
|
impl Default for Timer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
Loading…
Reference in New Issue