feat: add more opcodes and stub serial output

This commit is contained in:
EliseZeroTwo 2021-12-07 18:57:34 +01:00
parent 1d4dc8bc22
commit 8c5c1c4b23
No known key found for this signature in database
GPG key ID: E6D56A6F7B7991DE
7 changed files with 493 additions and 13 deletions

View file

@ -4,6 +4,7 @@ mod joypad;
mod mapper;
mod memory;
mod ppu;
mod serial;
mod sound;
mod timer;
@ -14,7 +15,7 @@ use memory::Memory;
use ppu::Ppu;
use timer::Timer;
use self::{cpu::Registers, mapper::NoMBC, sound::Sound};
use self::{cpu::Registers, mapper::NoMBC, serial::Serial, sound::Sound};
pub struct DmaState {
pub base: u8,
@ -42,13 +43,14 @@ pub struct Gameboy {
timer: Timer,
pub registers: Registers,
pub joypad: Joypad,
pub serial: Serial,
pub dma: DmaState,
pub sound: Sound,
pub single_step: bool,
pub breakpoints: [bool; u16::MAX as usize],
pub mem_read_breakpoints: [bool; u16::MAX as usize],
pub mem_write_breakpoints: [bool; u16::MAX as usize],
pub breakpoints: [bool; u16::MAX as usize + 1],
pub mem_read_breakpoints: [bool; u16::MAX as usize + 1],
pub mem_write_breakpoints: [bool; u16::MAX as usize + 1],
trigger_bp: bool,
}
@ -60,14 +62,15 @@ impl Gameboy {
interrupts: Interrupts::new(),
timer: Timer::new(),
joypad: Joypad::new(),
serial: Serial::new(),
dma: DmaState::new(),
ppu: Ppu::new(),
registers: Registers::default(),
sound: Sound::default(),
single_step: false,
breakpoints: [false; u16::MAX as usize],
mem_read_breakpoints: [false; u16::MAX as usize],
mem_write_breakpoints: [false; u16::MAX as usize],
breakpoints: [false; u16::MAX as usize + 1],
mem_read_breakpoints: [false; u16::MAX as usize + 1],
mem_write_breakpoints: [false; u16::MAX as usize + 1],
trigger_bp: false,
}
}
@ -258,7 +261,8 @@ impl Gameboy {
fn cpu_read_io(&self, address: u16) -> u8 {
match address {
0xFF00 => self.joypad.cpu_read(),
0xFF01..=0xFF02 => unimplemented!("Serial"),
0xFF01 => self.serial.sb,
0xFF02 => self.serial.sc,
0xFF03 => 0, // Unused
0xFF04 => self.timer.div,
0xFF05 => self.timer.tima,
@ -317,7 +321,8 @@ impl Gameboy {
fn cpu_write_io(&mut self, address: u16, value: u8) {
match address {
0xFF00 => self.joypad.cpu_write(value),
0xFF01..=0xFF02 => unimplemented!("Serial"),
0xFF01 => self.serial.sb = value,
0xFF02 => self.serial.sc = value,
0xFF03 => {} // Unused
0xFF04 => self.timer.div = value,
0xFF05 => self.timer.tima = value,

View file

@ -1,6 +1,7 @@
mod alu;
mod flow;
mod load_store_move;
mod misc;
mod prefixed;
use super::Gameboy;
@ -177,13 +178,16 @@ pub fn tick_cpu(state: &mut Gameboy) {
};
let result: CycleResult = match opcode {
0x00 => misc::nop,
0x01 => load_store_move::ld_bc_imm_u16,
0x03 => alu::inc_bc,
0x04 => alu::inc_b,
0x05 => alu::dec_b,
0x06 => load_store_move::ld_b_imm_u8,
0x08 => load_store_move::ld_deref_imm_u16_sp,
0x09 => alu::add_hl_bc,
0x0a => load_store_move::ld_a_deref_bc,
0x0b => alu::dec_bc,
0x0c => alu::inc_c,
0x0d => alu::dec_c,
0x0e => load_store_move::ld_c_imm_u8,
@ -194,7 +198,9 @@ pub fn tick_cpu(state: &mut Gameboy) {
0x16 => load_store_move::ld_d_imm_u8,
0x17 => alu::rla,
0x18 => flow::jr_i8,
0x19 => alu::add_hl_de,
0x1a => load_store_move::ld_a_deref_de,
0x1b => alu::dec_de,
0x1c => alu::inc_e,
0x1d => alu::dec_e,
0x1e => load_store_move::ld_e_imm_u8,
@ -206,10 +212,13 @@ pub fn tick_cpu(state: &mut Gameboy) {
0x25 => alu::dec_h,
0x26 => load_store_move::ld_h_imm_u8,
0x28 => flow::jr_z_i8,
0x29 => alu::add_hl_hl,
0x2a => load_store_move::ld_a_hl_plus,
0x2b => alu::dec_hl,
0x2c => alu::inc_l,
0x2d => alu::dec_l,
0x2e => load_store_move::ld_l_imm_u8,
0x2f => alu::cpl,
0x30 => flow::jr_nc_i8,
0x31 => load_store_move::ld_sp_imm_u16,
0x32 => load_store_move::ld_hl_minus_a,
@ -217,11 +226,15 @@ pub fn tick_cpu(state: &mut Gameboy) {
0x34 => alu::inc_deref_hl,
0x35 => alu::dec_deref_hl,
0x36 => load_store_move::ld_deref_hl_imm_u8,
0x37 => alu::scf,
0x38 => flow::jr_c_i8,
0x39 => alu::add_hl_sp,
0x3a => load_store_move::ld_a_hl_minus,
0x3b => alu::dec_sp,
0x3c => alu::inc_a,
0x3d => alu::dec_a,
0x3e => load_store_move::ld_a_imm_u8,
0x3f => alu::ccf,
0x40 => load_store_move::ld_b_b,
0x41 => load_store_move::ld_b_c,
0x42 => load_store_move::ld_b_d,
@ -317,6 +330,14 @@ pub fn tick_cpu(state: &mut Gameboy) {
0x9D => alu::sbc_a_l,
0x9E => alu::sbc_a_deref_hl,
0x9F => alu::sbc_a_a,
0xA0 => alu::and_a_b,
0xA1 => alu::and_a_c,
0xA2 => alu::and_a_d,
0xA3 => alu::and_a_e,
0xA4 => alu::and_a_h,
0xA5 => alu::and_a_l,
0xA6 => alu::and_a_deref_hl,
0xA7 => alu::and_a_a,
0xA8 => alu::xor_a_b,
0xA9 => alu::xor_a_c,
0xAA => alu::xor_a_d,
@ -325,6 +346,14 @@ pub fn tick_cpu(state: &mut Gameboy) {
0xAD => alu::xor_a_l,
0xAE => alu::xor_a_deref_hl,
0xAF => alu::xor_a_a,
0xB0 => alu::or_a_b,
0xB1 => alu::or_a_c,
0xB2 => alu::or_a_d,
0xB3 => alu::or_a_e,
0xB4 => alu::or_a_h,
0xB5 => alu::or_a_l,
0xB6 => alu::or_a_deref_hl,
0xB7 => alu::or_a_a,
0xB8 => alu::cp_a_b,
0xB9 => alu::cp_a_c,
0xBA => alu::cp_a_d,
@ -340,6 +369,7 @@ pub fn tick_cpu(state: &mut Gameboy) {
0xC4 => flow::call_nz_u16,
0xC5 => load_store_move::push_bc,
0xC6 => alu::add_a_imm_u8,
0xC7 => flow::rst_0x0,
0xC8 => flow::ret_z,
0xC9 => flow::ret,
0xCA => flow::jp_z_u16,
@ -347,31 +377,42 @@ pub fn tick_cpu(state: &mut Gameboy) {
0xCC => flow::call_z_u16,
0xCD => flow::call_u16,
0xCE => alu::adc_a_imm_u8,
0xCF => flow::rst_0x08,
0xD0 => flow::ret_nc,
0xD1 => load_store_move::pop_de,
0xD2 => flow::jp_nc_u16,
0xD4 => flow::call_nc_u16,
0xD5 => load_store_move::push_de,
0xD6 => alu::sub_a_imm_u8,
0xD7 => flow::rst_0x10,
0xD8 => flow::ret_c,
0xD9 => flow::reti,
0xDA => flow::jp_c_u16,
0xDC => flow::call_c_u16,
0xDE => alu::sbc_a_imm_u8,
0xDF => flow::rst_0x18,
0xE0 => load_store_move::ldh_imm_u8_a,
0xE1 => load_store_move::pop_hl,
0xE2 => load_store_move::ldh_deref_c_a,
0xE5 => load_store_move::push_hl,
0xE6 => alu::and_a_imm_u8,
0xE7 => flow::rst_0x20,
0xE9 => flow::jp_hl,
0xEA => load_store_move::ld_deref_imm_u16_a,
0xEE => alu::xor_a_imm_u8,
0xEF => flow::rst_0x28,
0xF0 => load_store_move::ldh_a_imm_u8,
0xF1 => load_store_move::pop_af,
0xF2 => load_store_move::ldh_a_deref_c,
0xF3 => misc::di,
0xF5 => load_store_move::push_af,
0xF6 => alu::or_a_imm_u8,
0xF7 => flow::rst_0x30,
0xF9 => load_store_move::ld_sp_hl,
0xFA => load_store_move::ld_a_deref_imm_u16,
0xFB => misc::ei,
0xFE => alu::cp_a_imm_u8,
0xFF => flow::rst_0x38,
unknown => {
panic!("Unrecognized opcode: {:#X}\nRegisters: {:#x?}", unknown, state.registers)
}

View file

@ -197,7 +197,7 @@ pub fn sbc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
state.registers.set_subtract(true);
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
state.registers.opcode_bytecount = Some(1);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
@ -272,7 +272,7 @@ pub fn add_a_imm_u8(state: &mut Gameboy) -> CycleResult {
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
state.registers.opcode_bytecount = Some(1);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
@ -353,7 +353,7 @@ pub fn adc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
state.registers.opcode_bytecount = Some(1);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
@ -428,7 +428,7 @@ pub fn sub_a_imm_u8(state: &mut Gameboy) -> CycleResult {
state.registers.set_subtract(true);
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
state.registers.opcode_bytecount = Some(1);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
@ -608,6 +608,49 @@ pub fn inc_sp(state: &mut Gameboy) -> CycleResult {
}
}
macro_rules! define_dec_u16_reg {
($lreg:ident, $rreg:ident) => {
paste::paste! {
pub fn [<dec_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let (res, carry) = state.registers.$rreg.overflowing_sub(1);
state.registers.$rreg = res;
state.registers.set_hold(carry as u16);
CycleResult::NeedsMore
},
1 => {
if state.registers.take_hold() != 0 {
let (res, _) = state.registers.$lreg.overflowing_sub(1);
state.registers.$lreg = res;
}
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
}
};
}
define_dec_u16_reg!(b, c);
define_dec_u16_reg!(d, e);
define_dec_u16_reg!(h, l);
pub fn dec_sp(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => CycleResult::NeedsMore,
1 => {
let (res, _) = state.registers.sp.overflowing_sub(1);
state.registers.sp = res;
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
macro_rules! define_cp_reg_reg {
($lreg:ident, $rreg:ident) => {
paste::paste! {
@ -676,3 +719,252 @@ pub fn cp_a_deref_hl(state: &mut Gameboy) -> CycleResult {
_ => unreachable!(),
}
}
macro_rules! define_or_reg {
($reg:ident) => {
paste::paste! {
pub fn [<or_a_ $reg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let result = state.registers.a | state.registers.$reg;
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
},
_ => unreachable!(),
}
}
}
};
}
define_or_reg!(a);
define_or_reg!(b);
define_or_reg!(c);
define_or_reg!(d);
define_or_reg!(e);
define_or_reg!(h);
define_or_reg!(l);
pub fn or_a_deref_hl(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.get_hl());
CycleResult::NeedsMore
}
1 => {
let result = state.registers.a | state.registers.take_mem();
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn or_a_imm_u8(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.pc + 1);
CycleResult::NeedsMore
}
1 => {
let result = state.registers.a | state.registers.take_mem();
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
}
}
macro_rules! define_and_reg {
($reg:ident) => {
paste::paste! {
pub fn [<and_a_ $reg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let result = state.registers.a & state.registers.$reg;
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(true);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
},
_ => unreachable!(),
}
}
}
};
}
define_and_reg!(a);
define_and_reg!(b);
define_and_reg!(c);
define_and_reg!(d);
define_and_reg!(e);
define_and_reg!(h);
define_and_reg!(l);
pub fn and_a_deref_hl(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.get_hl());
CycleResult::NeedsMore
}
1 => {
let result = state.registers.a & state.registers.take_mem();
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(true);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn and_a_imm_u8(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.pc + 1);
CycleResult::NeedsMore
}
1 => {
let result = state.registers.a & state.registers.take_mem();
state.registers.a = result;
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(true);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn cpl(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.registers.a = !state.registers.a;
state.registers.set_subtract(true);
state.registers.set_half_carry(true);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn ccf(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(!state.registers.get_carry());
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn scf(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(true);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
macro_rules! define_add_hl_u16_reg {
($lreg:ident, $rreg:ident) => {
paste::paste! {
pub fn [<add_hl_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let CarryResult { result, carry, .. } = add(state.registers.l, state.registers.$rreg);
state.registers.l = result;
state.registers.set_hold(carry as u16);
CycleResult::NeedsMore
},
1 => {
if state.registers.take_hold() != 0 {
let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg);
state.registers.h = result;
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
} else {
state.registers.set_half_carry(false);
state.registers.set_carry(false);
}
state.registers.set_subtract(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
}
};
}
define_add_hl_u16_reg!(b, c);
define_add_hl_u16_reg!(d, e);
define_add_hl_u16_reg!(h, l);
pub fn add_hl_sp(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let CarryResult { result, carry, .. } =
add(state.registers.l, state.registers.sp as u8);
state.registers.l = result;
state.registers.set_hold(carry as u16);
CycleResult::NeedsMore
}
1 => {
if state.registers.take_hold() != 0 {
let CarryResult { result, carry, half_carry } =
add(state.registers.h, (state.registers.sp >> 8) as u8);
state.registers.h = result;
state.registers.set_half_carry(half_carry);
state.registers.set_carry(carry);
} else {
state.registers.set_half_carry(false);
state.registers.set_carry(false);
}
state.registers.set_subtract(false);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}

View file

@ -197,6 +197,7 @@ pub fn jp_nz_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if state.registers.get_zero() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -228,6 +229,7 @@ pub fn jp_nc_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if state.registers.get_carry() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -259,6 +261,7 @@ pub fn jp_z_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if !state.registers.get_zero() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -290,6 +293,7 @@ pub fn jp_c_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if !state.registers.get_carry() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -352,6 +356,7 @@ pub fn call_nz_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if state.registers.get_zero() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -391,6 +396,7 @@ pub fn call_nc_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if state.registers.get_carry() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -430,6 +436,7 @@ pub fn call_z_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if !state.registers.get_zero() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -469,6 +476,7 @@ pub fn call_c_u16(state: &mut Gameboy) -> CycleResult {
2 => {
if !state.registers.get_carry() {
state.registers.take_mem();
state.registers.take_hold();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
} else {
@ -655,3 +663,38 @@ pub fn ret_c(state: &mut Gameboy) -> CycleResult {
_ => unreachable!(),
}
}
macro_rules! define_rst {
($addr:literal) => {
paste::paste! {
pub fn [<rst_ $addr>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => CycleResult::NeedsMore,
1 => {
state.cpu_push_stack((state.registers.pc >> 8) as u8);
CycleResult::NeedsMore
},
2 => {
state.cpu_push_stack((state.registers.pc & 0xFF) as u8);
CycleResult::NeedsMore
},
3 => {
state.registers.pc = $addr;
state.registers.opcode_bytecount = Some(0);
CycleResult::Finished
},
_ => unreachable!(),
}
}
}
};
}
define_rst!(0x0);
define_rst!(0x08);
define_rst!(0x10);
define_rst!(0x18);
define_rst!(0x20);
define_rst!(0x28);
define_rst!(0x30);
define_rst!(0x38);

34
src/gameboy/cpu/misc.rs Normal file
View file

@ -0,0 +1,34 @@
use super::CycleResult;
use crate::gameboy::Gameboy;
pub fn nop(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn di(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.interrupts.ime = false;
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn ei(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.interrupts.ime = true;
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}

View file

@ -24,6 +24,13 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult {
0x14 => rl_h,
0x15 => rl_l,
0x17 => rl_a,
0x30 => swap_b,
0x31 => swap_c,
0x32 => swap_d,
0x33 => swap_e,
0x34 => swap_h,
0x35 => swap_l,
0x37 => swap_a,
0x40 => bit_0_b,
0x41 => bit_0_c,
0x42 => bit_0_d,
@ -204,3 +211,33 @@ define_rl_reg!(e);
define_rl_reg!(h);
define_rl_reg!(l);
define_rl_reg!(a);
macro_rules! define_swap_reg {
($reg:ident) => {
paste::paste! {
pub fn [<swap_ $reg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
1 => {
state.registers.$reg = (state.registers.$reg >> 4) | (state.registers.$reg << 4);
state.registers.set_zero(state.registers.$reg == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(false);
state.registers.set_carry(false);
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
},
_ => unreachable!(),
}
}
}
};
}
define_swap_reg!(b);
define_swap_reg!(c);
define_swap_reg!(d);
define_swap_reg!(e);
define_swap_reg!(h);
define_swap_reg!(l);
define_swap_reg!(a);

28
src/gameboy/serial.rs Normal file
View file

@ -0,0 +1,28 @@
pub struct Serial {
pub sb: u8,
pub sc: u8,
}
impl Serial {
pub fn new() -> Serial {
Self { sb: 0, sc: 0 }
}
pub fn set_transfer_in_process(&mut self, value: bool) {
self.sc &= !(1 << 7);
self.sc |= (value as u8) << 7;
}
pub fn get_transfer_in_process(&mut self) -> bool {
(self.sc >> 7) & 0b1 == 1
}
pub fn is_conductor(&self) -> bool {
self.sc & 0b1 == 1
}
pub fn set_side(&mut self, conductor: bool) {
self.sc &= !0b1;
self.sc |= conductor as u8;
}
}