use serde::Serialize;
const BANK_SELECT_MASK: u8 = 0x80;
const BANK_INDEX_MASK: u8 = 0x7F;
const ADDR_PORT_B_CLEAR_MASK: usize = !0xFF;
const ADDR_PORT_C_CLEAR_MASK: usize = !0x7F00;
const ADDR_PAGE_KEEP_MASK: usize = 0x7FFF;
#[derive(Serialize)]
pub struct RomDisk {
#[serde(skip)]
data: Vec<u8>,
cur_addr: usize,
old_a15: bool,
mask: usize,
}
impl RomDisk {
pub fn new() -> Self {
Self {
data: Vec::new(),
cur_addr: 0,
old_a15: false,
mask: 0,
}
}
pub fn load(&mut self, payload: &[u8]) {
self.data = payload.to_vec();
let size_kb = self.data.len() / 1024;
self.mask = match size_kb {
0..=512 => 0x0F,
513..=1024 => 0x1F,
1025..=2048 => 0x3F,
2049..=4096 => 0x7F,
_ => 0xFF,
};
self.cur_addr = 0;
self.old_a15 = false;
}
pub fn read_data(&self) -> u8 {
if self.cur_addr < self.data.len() {
self.data[self.cur_addr]
} else {
0xFF
}
}
pub fn update_addr(&mut self, port_b: u8, port_c: u8) {
let new_a15 = (port_c & BANK_SELECT_MASK) != 0;
let c_val = (port_c & BANK_INDEX_MASK) as usize;
let mut addr = self.cur_addr;
addr = (addr & ADDR_PORT_B_CLEAR_MASK) | (port_b as usize);
addr = (addr & ADDR_PORT_C_CLEAR_MASK) | (c_val << 8);
if new_a15 && !self.old_a15 {
addr = (addr & ADDR_PAGE_KEEP_MASK) | ((addr & self.mask) << 15);
}
self.old_a15 = new_a15;
self.cur_addr = addr;
}
}
impl Default for RomDisk {
fn default() -> Self {
Self::new()
}
}