Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 81 additions & 6 deletions src/zorro_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
//! [`DeviceHost`] is the narrow host-services view a device is handed on every
//! call. Today it exposes guest memory for DMA; capability hooks (CD audio,
//! networking) are added alongside the devices that need them. It also owns the
//! single 24-bit DMA address decode (chip / slow / Zorro-board RAM) that the
//! A2091 and CDTV bus masters previously each open-coded.
//! single DMA address decode (chip / slow / motherboard / accelerator /
//! Zorro-board RAM) that the A2091 and CDTV bus masters previously each
//! open-coded. Zorro II masters only drive the low 24 bits; a Zorro III master
//! like the A4091 reaches the 32-bit motherboard and accelerator banks too.

use crate::chipset::paula::CdAudioRing;
use crate::memory::{Memory, SLOW_RAM_BASE};
use crate::memory::{Memory, ACCEL_RAM_BASE, SLOW_RAM_BASE};

/// 24-bit DMA bus-master word read: the chip / slow / Zorro-board RAM decode a
/// Zorro DMA controller sees. Returns `None` when the (word-aligned) address is
/// DMA bus-master word read: the chip / slow / motherboard / accelerator /
/// Zorro-board RAM decode a Zorro DMA controller sees. Returns `None` when the
/// (word-aligned) address is
/// not backed by RAM, leaving the unmapped sentinel and warn policy to the
/// caller. Word reads require both bytes in range, matching the hardware word
/// access; the last odd byte of a region therefore does not satisfy a word.
Expand All @@ -33,6 +36,16 @@ pub(crate) fn dma_read_word(mem: &Memory, addr: u32) -> Option<u16> {
let o = a - slow;
return Some((u16::from(mem.slow_ram[o]) << 8) | u16::from(mem.slow_ram[o + 1]));
}
let mb = mem.mb_ram_base() as usize;
if a >= mb && a - mb + 1 < mem.mb_ram.len() {
let o = a - mb;
return Some((u16::from(mem.mb_ram[o]) << 8) | u16::from(mem.mb_ram[o + 1]));
}
let accel = ACCEL_RAM_BASE as usize;
if a >= accel && a - accel + 1 < mem.accel_ram.len() {
let o = a - accel;
return Some((u16::from(mem.accel_ram[o]) << 8) | u16::from(mem.accel_ram[o + 1]));
}
if let Some((board, off)) = mem.zorro.region_at(addr, 2) {
let ram = mem.zorro.board_ram(board);
return Some((u16::from(ram[off]) << 8) | u16::from(ram[off + 1]));
Expand All @@ -56,6 +69,20 @@ pub(crate) fn dma_write_word(mem: &mut Memory, addr: u32, w: u16) -> bool {
mem.slow_ram[o + 1] = w as u8;
return true;
}
let mb = mem.mb_ram_base() as usize;
if a >= mb && a - mb + 1 < mem.mb_ram.len() {
let o = a - mb;
mem.mb_ram[o] = (w >> 8) as u8;
mem.mb_ram[o + 1] = w as u8;
return true;
}
let accel = ACCEL_RAM_BASE as usize;
if a >= accel && a - accel + 1 < mem.accel_ram.len() {
let o = a - accel;
mem.accel_ram[o] = (w >> 8) as u8;
mem.accel_ram[o + 1] = w as u8;
return true;
}
if let Some((board, off)) = mem.zorro.region_at(addr, 2) {
let ram = mem.zorro.board_ram_mut(board);
ram[off] = (w >> 8) as u8;
Expand All @@ -77,6 +104,14 @@ pub(crate) fn dma_read_byte(mem: &Memory, addr: u32) -> Option<u8> {
if a >= slow && a < slow + mem.slow_ram.len() {
return Some(mem.slow_ram[a - slow]);
}
let mb = mem.mb_ram_base() as usize;
if a >= mb && a - mb < mem.mb_ram.len() {
return Some(mem.mb_ram[a - mb]);
}
let accel = ACCEL_RAM_BASE as usize;
if a >= accel && a - accel < mem.accel_ram.len() {
return Some(mem.accel_ram[a - accel]);
}
if let Some((board, off)) = mem.zorro.region_at(addr, 1) {
return Some(mem.zorro.board_ram(board)[off]);
}
Expand All @@ -96,6 +131,16 @@ pub(crate) fn dma_write_byte(mem: &mut Memory, addr: u32, b: u8) -> bool {
mem.slow_ram[a - slow] = b;
return true;
}
let mb = mem.mb_ram_base() as usize;
if a >= mb && a - mb < mem.mb_ram.len() {
mem.mb_ram[a - mb] = b;
return true;
}
let accel = ACCEL_RAM_BASE as usize;
if a >= accel && a - accel < mem.accel_ram.len() {
mem.accel_ram[a - accel] = b;
return true;
}
if let Some((board, off)) = mem.zorro.region_at(addr, 1) {
mem.zorro.board_ram_mut(board)[off] = b;
return true;
Expand Down Expand Up @@ -506,6 +551,29 @@ mod tests {
assert_eq!(mem.slow_ram[0x20], 0x12);
}

#[test]
fn dma_round_trips_motherboard_and_accelerator_ram() {
// A Zorro III bus master (the A4091) reaches the 32-bit motherboard and
// accelerator fast-RAM banks. The A4000 profile fits its stock RAM
// there, so a driver's DMA buffers land in these banks; a decode that
// stopped at slow RAM read them back as 0xFF and dropped writes.
let mut mem = mem_with(0x100, 0x100);
mem.mb_ram = vec![0u8; 0x100];
mem.accel_ram = vec![0u8; 0x100];

let mb = mem.mb_ram_base() as u32;
assert!(dma_write_word(&mut mem, mb + 0x40, 0xCAFE));
assert_eq!(dma_read_word(&mem, mb + 0x40), Some(0xCAFE));
assert!(dma_write_byte(&mut mem, mb + 0x42, 0x5A));
assert_eq!(dma_read_byte(&mem, mb + 0x42), Some(0x5A));

let accel = ACCEL_RAM_BASE as u32;
assert!(dma_write_word(&mut mem, accel + 0x08, 0xF00D));
assert_eq!(dma_read_word(&mem, accel + 0x08), Some(0xF00D));
assert!(dma_write_byte(&mut mem, accel + 0x0A, 0xA5));
assert_eq!(dma_read_byte(&mem, accel + 0x0A), Some(0xA5));
}

#[test]
fn word_access_needs_both_bytes_in_range() {
// chip_ram is 0x100 bytes: the last word starts at 0xFE (0xFE,0xFF in
Expand All @@ -530,9 +598,16 @@ mod tests {

#[test]
fn unmapped_addresses_report_none() {
let mem = mem_with(0x100, 0x100);
let mut mem = mem_with(0x100, 0x100);
// Between chip top and slow base: nothing backs it.
assert_eq!(dma_read_word(&mem, 0x0040_0000), None);
assert_eq!(dma_read_byte(&mem, 0x0040_0000), None);

// The top of the 32-bit space is above the fitted banks; the offset
// bound must reject it without overflowing `a + 1`.
mem.mb_ram = vec![0u8; 0x100];
mem.accel_ram = vec![0u8; 0x100];
assert_eq!(dma_read_word(&mem, 0xFFFF_FFFF), None);
assert_eq!(dma_read_byte(&mem, 0xFFFF_FFFF), None);
}
}
Loading