Newer
Older
//! System state (BEES format) functions and structures.
io::{Cursor, Read, Seek, SeekFrom, Write},
mem::size_of,
use crate::{
gb::{GameBoy, GameBoySpeed},
info::Info,
/// Writes the data from the internal structure into the
/// provided buffer.
fn write(&mut self, buffer: &mut Vec<u8>);
/// Reads the data from the provided buffer and populates
/// the internal structure with it.
fn read(&mut self, data: &mut Cursor<Vec<u8>>);
/// Obtains a new instance of the state from the provided
/// `GameBoy` instance and returns it.
fn from_gb(gb: &mut GameBoy) -> Result<Self, String>
where
Self: Sized;
/// Applies the state to the provided `GameBoy` instance.
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String>;
name: BeesName,
info: BeesInfo,
core: BeesCore,
impl BeesState {
pub fn description(&self, column_length: usize) -> String {
let emulator_l = format!("{:width$}", "Emulator", width = column_length);
let title_l: String = format!("{:width$}", "Title", width = column_length);
let version_l: String = format!("{:width$}", "Version", width = column_length);
let model_l: String = format!("{:width$}", "Model", width = column_length);
let ram_l: String = format!("{:width$}", "RAM", width = column_length);
let vram_l: String = format!("{:width$}", "VRAM", width = column_length);
let pc_l: String = format!("{:width$}", "PC", width = column_length);
let sp_l: String = format!("{:width$}", "SP", width = column_length);
"{} {}\n{} {}\n{} {}.{}\n{} {}\n{} {}\n{} {}\n{} 0x{:04X}\n{} 0x{:04X}\n",
emulator_l,
self.name.name,
title_l,
self.info.title(),
version_l,
self.core.major,
self.core.minor,
model_l,
self.core.model,
ram_l,
self.core.ram.size,
vram_l,
self.core.vram.size,
pc_l,
self.core.pc,
sp_l,
self.core.sp
pub fn verify(&self) -> Result<(), String> {
/// Dumps the core data into the provided buffer and returns.
/// This will effectively populate the majority of the save
/// file with the core emulator contents.
fn dump_core(&mut self, buffer: &mut Vec<u8>) -> u32 {
let mut offset = 0x0000_u32;
let mut buffers = vec![
&mut self.core.ram,
&mut self.core.vram,
&mut self.core.mbc_ram,
&mut self.core.oam,
&mut self.core.hram,
&mut self.core.background_palettes,
&mut self.core.object_palettes,
];
for item in buffers.iter_mut() {
item.offset = offset;
buffer.write_all(&item.buffer).unwrap();
offset += item.size;
fn write(&mut self, buffer: &mut Vec<u8>) {
self.footer.start_offset = self.dump_core(buffer);
self.name.write(buffer);
self.info.write(buffer);
self.core.write(buffer);
self.mbc.write(buffer);
self.end.write(buffer);
self.footer.write(buffer);
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
// moves the cursor to the end of the file
// to read the footer, and then places the
// the cursor in the start of the BEES data
// according to the footer information
data.seek(SeekFrom::End(-8)).unwrap();
data.seek(SeekFrom::Start(self.footer.start_offset as u64))
.unwrap();
// reads the block header information and then moves the
// cursor back to the original position to be able to
// re-read the block data
let block = BeesBlockHeader::from_data(data);
let offset = -((size_of::<u32>() * 2) as i64);
data.seek(SeekFrom::Current(offset)).unwrap();
"NAME" => self.name = BeesName::from_data(data),
"INFO" => self.info = BeesInfo::from_data(data),
"CORE" => self.core = BeesCore::from_data(data),
"MBC " => self.mbc = BeesMbc::from_data(data),
"END " => self.end = BeesBlock::from_data(data),
if block.is_end() {
break;
}
}
}
}
impl State for BeesState {
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
Ok(Self {
name: BeesName::from_gb(gb)?,
info: BeesInfo::from_gb(gb)?,
core: BeesCore::from_gb(gb)?,
mbc: BeesMbc::from_gb(gb)?,
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
self.verify()?;
self.name.to_gb(gb)?;
self.info.to_gb(gb)?;
self.core.to_gb(gb)?;
}
impl Display for BeesState {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description(9))
magic: String,
size: u32,
impl BeesBlockHeader {
pub fn new(magic: String, size: u32) -> Self {
Self { magic, size }
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
instance
}
pub fn is_end(&self) -> bool {
self.magic == "END "
}
}
impl Serialize for BeesBlockHeader {
fn write(&mut self, buffer: &mut Vec<u8>) {
buffer.write_all(&self.size.to_le_bytes()).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
self.magic = String::from_utf8(Vec::from(buffer)).unwrap();
impl Default for BeesBlockHeader {
fn default() -> Self {
Self::new(String::from(" "), 0)
}
}
pub struct BeesBlock {
header: BeesBlockHeader,
buffer: Vec<u8>,
}
impl BeesBlock {
pub fn new(header: BeesBlockHeader, buffer: Vec<u8>) -> Self {
Self { header, buffer }
}
pub fn from_magic(magic: String) -> Self {
Self::new(BeesBlockHeader::new(magic, 0), vec![])
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
instance
}
pub fn magic(&self) -> &String {
&self.header.magic
}
pub fn is_end(&self) -> bool {
}
}
impl Serialize for BeesBlock {
fn write(&mut self, buffer: &mut Vec<u8>) {
self.header.write(buffer);
buffer.write_all(&self.buffer).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
self.header.read(data);
self.buffer.reserve_exact(self.header.size as usize);
data.read_exact(&mut self.buffer).unwrap();
}
}
impl Default for BeesBlock {
fn default() -> Self {
Self::new(BeesBlockHeader::default(), vec![])
}
}
size: u32,
offset: u32,
buffer: Vec<u8>,
}
impl BeesBuffer {
pub fn new(size: u32, offset: u32, buffer: Vec<u8>) -> Self {
Self {
size,
offset,
buffer,
}
}
/// Fills the buffer with new data and updating the size
/// value accordingly.
fn fill_buffer(&mut self, data: &[u8]) {
self.size = data.len() as u32;
self.buffer = data.to_vec();
}
/// Loads the internal buffer structure with the provided
/// data according to the size and offset defined.
fn load_buffer(&self, data: &mut Cursor<Vec<u8>>) -> Vec<u8> {
let mut buffer = vec![0x00; self.size as usize];
let position = data.position();
data.seek(SeekFrom::Start(self.offset as u64)).unwrap();
data.read_exact(&mut buffer).unwrap();
data.set_position(position);
buffer
}
fn write(&mut self, buffer: &mut Vec<u8>) {
buffer.write_all(&self.size.to_le_bytes()).unwrap();
buffer.write_all(&self.offset.to_le_bytes()).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
self.buffer = self.load_buffer(data);
}
}
impl Default for BeesBuffer {
fn default() -> Self {
Self::new(0, 0, vec![])
start_offset: u32,
magic: u32,
impl BeesFooter {
pub fn new(start_offset: u32, magic: u32) -> Self {
Self {
start_offset,
magic,
}
}
pub fn verify(&self) -> Result<(), String> {
if self.magic != 0x53534542 {
return Err(String::from("Invalid magic"));
}
Ok(())
}
}
impl Serialize for BeesFooter {
fn write(&mut self, buffer: &mut Vec<u8>) {
buffer.write_all(&self.start_offset.to_le_bytes()).unwrap();
buffer.write_all(&self.magic.to_le_bytes()).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
}
}
impl Default for BeesFooter {
fn default() -> Self {
Self::new(0x00, 0x53534542)
}
}
header: BeesBlockHeader,
name: String,
impl BeesName {
pub fn new(name: String) -> Self {
Self {
header: BeesBlockHeader::new(String::from("NAME"), name.len() as u32),
name,
}
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
fn write(&mut self, buffer: &mut Vec<u8>) {
self.header.write(buffer);
buffer.write_all(self.name.as_bytes()).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
self.header.read(data);
let mut buffer = vec![0x00; self.header.size as usize];
fn from_gb(_gb: &mut GameBoy) -> Result<Self, String> {
Ok(Self::new(format!("{} v{}", Info::name(), Info::version())))
fn to_gb(&self, _gb: &mut GameBoy) -> Result<(), String> {
Ok(())
}
impl Default for BeesName {
fn default() -> Self {
Self::new(String::from(""))
}
}
header: BeesBlockHeader,
title: [u8; 16],
checksum: [u8; 2],
impl BeesInfo {
pub fn new(title: &[u8], checksum: &[u8]) -> Self {
Self {
header: BeesBlockHeader::new(
String::from("INFO"),
title.len() as u32 + checksum.len() as u32,
),
title: title.try_into().unwrap(),
checksum: checksum.try_into().unwrap(),
}
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
for (offset, byte) in self.title.iter().enumerate() {
final_index = offset;
break;
}
// in we're at the final byte of the title and the value
// is one that is reserved for CGB compatibility testing
// then we must ignore it for title processing purposes
if offset > 14
&& (*byte == CgbMode::CgbCompatible as u8 || *byte == CgbMode::CgbOnly as u8)
{
final_index = offset;
String::from_utf8(Vec::from(&self.title[..final_index]))
.unwrap()
.trim_matches(char::from(0))
.trim(),
)
fn write(&mut self, buffer: &mut Vec<u8>) {
self.header.write(buffer);
buffer.write_all(&self.title).unwrap();
buffer.write_all(&self.checksum).unwrap();
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
self.header.read(data);
data.read_exact(&mut self.title).unwrap();
data.read_exact(&mut self.checksum).unwrap();
}
}
impl State for BeesInfo {
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
Ok(Self::new(
&gb.cartridge_i().rom_data()[0x134..=0x143],
&gb.cartridge_i().rom_data()[0x14e..=0x14f],
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
if self.title() != gb.rom_i().title() {
return Err(format!(
"Invalid ROM loaded, expected '{}' (len {}) got '{}' (len {})",
self.title(),
self.title().len(),
gb.rom_i().title(),
gb.rom_i().title().len(),
));
}
Ok(())
}
impl Default for BeesInfo {
fn default() -> Self {
Self::new(&[0_u8; 16], &[0_u8; 2])
}
}
header: BeesBlockHeader,
major: u16,
minor: u16,
pc: u16,
af: u16,
bc: u16,
de: u16,
hl: u16,
sp: u16,
// 0 = running; 1 = halted; 2 = stopped
io_registers: [u8; 128],
ram: BeesBuffer,
vram: BeesBuffer,
mbc_ram: BeesBuffer,
oam: BeesBuffer,
hram: BeesBuffer,
background_palettes: BeesBuffer,
object_palettes: BeesBuffer,
pub fn new(
model: String,
pc: u16,
af: u16,
bc: u16,
de: u16,
hl: u16,
sp: u16,
ime: bool,
ie: u8,
execution_mode: u8,
io_registers: [u8; 128],
) -> Self {
Self {
header: BeesBlockHeader::new(
String::from("CORE"),
((size_of::<u16>() * 2)
+ size_of::<u32>()
+ (size_of::<u16>() * 6)
+ (size_of::<u8>() * 4)
+ (size_of::<u8>() * 128)
+ ((size_of::<u32>() + size_of::<u32>()) * 7)) as u32,
),
major: 1,
minor: 1,
model,
pc,
af,
bc,
de,
hl,
sp,
ram: BeesBuffer::default(),
vram: BeesBuffer::default(),
mbc_ram: BeesBuffer::default(),
oam: BeesBuffer::default(),
hram: BeesBuffer::default(),
background_palettes: BeesBuffer::default(),
object_palettes: BeesBuffer::default(),
}
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
pub fn verify(&self) -> Result<(), String> {
if self.header.magic != "CORE" {
return Err(String::from("Invalid magic"));
if self.oam.size != 0xa0 {
return Err(String::from("Invalid OAM size"));
}
if self.hram.size != 0x7f {
return Err(String::from("Invalid HRAM size"));
}
if (self.is_cgb() && self.background_palettes.size != 0x40)
|| (self.is_dmg() && self.background_palettes.size != 0x00)
{
return Err(String::from("Invalid background palettes size"));
}
if (self.is_cgb() && self.object_palettes.size != 0x40)
|| (self.is_dmg() && self.object_palettes.size != 0x00)
{
return Err(String::from("Invalid object palettes size"));
}
/// Obtains the BEES (Game Boy) model string using the
/// provided `GameBoy` instance.
fn bees_model(gb: &GameBoy) -> String {
let mut buffer = [0x00_u8; 4];
if gb.is_dmg() {
buffer[0] = b'C';
} else if gb.is_sgb() {
buffer[0] = b'S';
buffer[1] = b'C';
} else if gb.is_sgb() {
buffer[1] = b'N';
buffer[2] = b'B';
} else if gb.is_cgb() {
buffer[2] = b'A';
} else {
buffer[2] = b' ';
}
String::from_utf8(Vec::from(buffer)).unwrap()
}
fn is_dmg(&self) -> bool {
if let Some(first_char) = self.model.chars().next() {
return first_char == 'G';
}
false
}
fn is_cgb(&self) -> bool {
if let Some(first_char) = self.model.chars().next() {
return first_char == 'C';
}
false
}
fn write(&mut self, buffer: &mut Vec<u8>) {
self.header.write(buffer);
buffer.write_all(&self.major.to_le_bytes()).unwrap();
buffer.write_all(&self.minor.to_le_bytes()).unwrap();
buffer.write_all(self.model.as_bytes()).unwrap();
buffer.write_all(&self.pc.to_le_bytes()).unwrap();
buffer.write_all(&self.af.to_le_bytes()).unwrap();
buffer.write_all(&self.bc.to_le_bytes()).unwrap();
buffer.write_all(&self.de.to_le_bytes()).unwrap();
buffer.write_all(&self.hl.to_le_bytes()).unwrap();
buffer.write_all(&self.sp.to_le_bytes()).unwrap();
buffer.write_all(&(self.ime as u8).to_le_bytes()).unwrap();
buffer.write_all(&self.ie.to_le_bytes()).unwrap();
buffer
.write_all(&self.execution_mode.to_le_bytes())
.unwrap();
buffer.write_all(&self._padding.to_le_bytes()).unwrap();
buffer.write_all(&self.io_registers).unwrap();
self.ram.write(buffer);
self.vram.write(buffer);
self.mbc_ram.write(buffer);
self.oam.write(buffer);
self.hram.write(buffer);
self.background_palettes.write(buffer);
self.object_palettes.write(buffer);
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
self.header.read(data);
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 4];
data.read_exact(&mut buffer).unwrap();
self.model = String::from_utf8(Vec::from(buffer)).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
data.read_exact(&mut self.io_registers).unwrap();
self.ram.read(data);
self.vram.read(data);
self.mbc_ram.read(data);
self.oam.read(data);
self.hram.read(data);
self.background_palettes.read(data);
self.object_palettes.read(data);
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
let mut core = Self::new(
gb.cpu_i().pc(),
gb.cpu_i().af(),
gb.cpu_i().bc(),
gb.cpu_i().de(),
gb.cpu_i().hl(),
gb.cpu_i().sp(),
gb.cpu_i().ime(),
gb.mmu_i().ie,
// @TODO: these registers cannot be totally retrieved
// because of that some audio noise exists
// The loading of the registers should be done in a much
// more manual way like SameBoy does here
// https://github.com/LIJI32/SameBoy/blob/7e6f1f866e89430adaa6be839aecc4a2ccabd69c/Core/save_state.c#L673
gb.mmu().read_many_unsafe(0xff00, 128).try_into().unwrap(),
core.vram.fill_buffer(gb.ppu().vram_device());
core.mbc_ram.fill_buffer(gb.rom_i().ram_data());
core.oam.fill_buffer(&gb.mmu().read_many(0xfe00, 0x00a0));
core.hram.fill_buffer(&gb.mmu().read_many(0xff80, 0x007f));
if gb.is_cgb() {
core.background_palettes
.fill_buffer(&gb.ppu_i().palettes_color()[0]);
core.object_palettes
.fill_buffer(&gb.ppu_i().palettes_color()[1]);
}
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
gb.cpu().set_pc(self.pc);
gb.cpu().set_af(self.af);
gb.cpu().set_bc(self.bc);
gb.cpu().set_de(self.de);
gb.cpu().set_hl(self.hl);
gb.cpu().set_sp(self.sp);
gb.cpu().set_ime(self.ime);
gb.mmu().ie = self.ie;
match self.execution_mode {
0 => gb.cpu().set_halted(false),
1 => gb.cpu().set_halted(true),
2 => gb.cpu().stop(),
_ => unimplemented!(),
}
// @TODO: we need to be careful about this writing and
// should make this a bit more robust, to handle this
// special case/situations
// The registers should be handled in a more manual manner
// to avoid unwanted side effects
// https://github.com/LIJI32/SameBoy/blob/7e6f1f866e89430adaa6be839aecc4a2ccabd69c/Core/save_state.c#L1003
gb.mmu().write_many_unsafe(0xff00, &self.io_registers);
gb.mmu().set_ram(self.ram.buffer.to_vec());
gb.ppu().set_vram(&self.vram.buffer);
gb.rom().set_ram_data(&self.mbc_ram.buffer);
gb.mmu().write_many(0xfe00, &self.oam.buffer);
gb.mmu().write_many(0xff80, &self.hram.buffer);
// updates the internal palettes for the CGB with the values
// stored in the BEES state
gb.ppu().set_palettes_color([
self.background_palettes.buffer.to_vec().try_into().unwrap(),
self.object_palettes.buffer.to_vec().try_into().unwrap(),
]);
// updates the speed of the CGB according to the KEY1 register
let is_double = self.io_registers[0x4d_usize] & 0x80 == 0x80;
gb.mmu().set_speed(if is_double {
GameBoySpeed::Double
} else {
GameBoySpeed::Normal
});
// need to disable DMA transfer to avoid unwanted
// DMA transfers when loading the state
gb.dma().set_active(false);
}
}
impl Default for BeesCore {
fn default() -> Self {
Self::new(
String::from("GD "),
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
false,
0x00,
0,
[0x00; 128],
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
pub struct BeesMbrRegister {
address: u16,
value: u8,
}
impl BeesMbrRegister {
pub fn new(address: u16, value: u8) -> Self {
Self { address, value }
}
}
pub struct BeesMbc {
header: BeesBlockHeader,
registers: Vec<BeesMbrRegister>,
}
impl BeesMbc {
pub fn new(registers: Vec<BeesMbrRegister>) -> Self {
Self {
header: BeesBlockHeader::new(
String::from("MBC "),
((size_of::<u8>() + size_of::<u16>()) * registers.len()) as u32,
),
registers,
}
}
pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
let mut instance = Self::default();
instance
}
}
impl Serialize for BeesMbc {
fn write(&mut self, buffer: &mut Vec<u8>) {
self.header.write(buffer);
for register in self.registers.iter() {
buffer.write_all(®ister.address.to_le_bytes()).unwrap();
buffer.write_all(®ister.value.to_le_bytes()).unwrap();
}
}
fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
self.header.read(data);
for _ in 0..(self.header.size / 3) {
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let address = u16::from_le_bytes(buffer);
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let value = u8::from_le_bytes(buffer);
self.registers.push(BeesMbrRegister::new(address, value));
}
}
}
impl State for BeesMbc {
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
let mut registers = vec![];
match gb.cartridge().rom_type().mbc_type() {
MbcType::NoMbc => (),
MbcType::Mbc1 => {
registers.push(BeesMbrRegister::new(
0x0000,
if gb.rom().ram_enabled() {
0x0a_u8
} else {
0x00_u8
},
));
registers.push(BeesMbrRegister::new(
0x2000,
gb.rom().rom_bank() as u8 & 0x1f,
));
registers.push(BeesMbrRegister::new(0x4000, gb.rom().ram_bank()));
registers.push(BeesMbrRegister::new(0x6000, 0x00_u8));
}
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
MbcType::Mbc3 => {
registers.push(BeesMbrRegister::new(
0x0000,
if gb.rom().ram_enabled() {
0x0a_u8
} else {
0x00_u8
},
));
registers.push(BeesMbrRegister::new(0x2000, gb.rom().rom_bank() as u8));
registers.push(BeesMbrRegister::new(0x4000, gb.rom().ram_bank()));
}
MbcType::Mbc5 => {
registers.push(BeesMbrRegister::new(
0x0000,
if gb.rom().ram_enabled() {
0x0a_u8
} else {
0x00_u8
},
));
registers.push(BeesMbrRegister::new(0x2000, gb.rom().rom_bank() as u8));
registers.push(BeesMbrRegister::new(
0x3000,
(gb.rom().rom_bank() >> 8) as u8 & 0x01,
));
registers.push(BeesMbrRegister::new(0x4000, gb.rom().ram_bank()));
}
_ => unimplemented!(),
}
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
for register in self.registers.iter() {
gb.mmu().write(register.address, register.value);
}