2021-10-04 20:02:58 +00:00
|
|
|
|
|
|
|
use std::fs;
|
|
|
|
|
2023-04-24 01:49:40 +00:00
|
|
|
use moa_core::{Error, ClockTime, Address, Addressable, Transmutable, debug};
|
2021-10-04 20:02:58 +00:00
|
|
|
|
|
|
|
|
2021-10-05 23:22:21 +00:00
|
|
|
const ATA_REG_DATA_WORD: Address = 0x20;
|
2021-10-04 20:02:58 +00:00
|
|
|
const ATA_REG_DATA_BYTE: Address = 0x21;
|
|
|
|
const ATA_REG_FEATURE: Address = 0x23;
|
|
|
|
const ATA_REG_ERROR: Address = 0x23;
|
|
|
|
const ATA_REG_SECTOR_COUNT: Address = 0x25;
|
|
|
|
const ATA_REG_SECTOR_NUM: Address = 0x27;
|
|
|
|
const ATA_REG_CYL_LOW: Address = 0x29;
|
|
|
|
const ATA_REG_CYL_HIGH: Address = 0x2B;
|
|
|
|
const ATA_REG_DRIVE_HEAD: Address = 0x2D;
|
|
|
|
const ATA_REG_STATUS: Address = 0x2F;
|
|
|
|
const ATA_REG_COMMAND: Address = 0x2F;
|
|
|
|
|
|
|
|
const ATA_CMD_READ_SECTORS: u8 = 0x20;
|
|
|
|
const ATA_CMD_WRITE_SECTORS: u8 = 0x30;
|
|
|
|
const ATA_CMD_IDENTIFY: u8 = 0xEC;
|
|
|
|
const ATA_CMD_SET_FEATURE: u8 = 0xEF;
|
|
|
|
|
2021-10-11 22:16:04 +00:00
|
|
|
#[allow(dead_code)]
|
2021-10-05 23:22:21 +00:00
|
|
|
const ATA_ST_BUSY: u8 = 0x80;
|
2021-10-11 22:16:04 +00:00
|
|
|
#[allow(dead_code)]
|
2021-10-05 23:22:21 +00:00
|
|
|
const ATA_ST_DATA_READY: u8 = 0x08;
|
2021-10-11 22:16:04 +00:00
|
|
|
#[allow(dead_code)]
|
2021-10-05 23:22:21 +00:00
|
|
|
const ATA_ST_ERROR: u8 = 0x01;
|
|
|
|
|
|
|
|
const ATA_SECTOR_SIZE: u32 = 512;
|
2021-10-04 20:02:58 +00:00
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
const DEV_NAME: &str = "ata";
|
2021-10-04 20:02:58 +00:00
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
#[derive(Default)]
|
2021-10-04 20:02:58 +00:00
|
|
|
pub struct AtaDevice {
|
2021-12-13 20:00:24 +00:00
|
|
|
selected_sector: u32,
|
|
|
|
selected_count: u32,
|
|
|
|
last_error: u8,
|
|
|
|
contents: Vec<u8>,
|
2021-10-04 20:02:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AtaDevice {
|
|
|
|
pub fn load(&mut self, filename: &str) -> Result<(), Error> {
|
|
|
|
match fs::read(filename) {
|
|
|
|
Ok(contents) => {
|
|
|
|
self.contents = contents;
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
Err(_) => Err(Error::new(&format!("Error reading contents of {}", filename))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Addressable for AtaDevice {
|
2023-06-08 02:56:00 +00:00
|
|
|
fn size(&self) -> usize {
|
2021-10-04 20:02:58 +00:00
|
|
|
0x30
|
|
|
|
}
|
|
|
|
|
2023-04-24 01:49:40 +00:00
|
|
|
fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
2021-10-04 20:02:58 +00:00
|
|
|
match addr {
|
2021-10-05 23:22:21 +00:00
|
|
|
ATA_REG_DATA_WORD => {
|
|
|
|
self.selected_count -= 2;
|
2021-10-16 17:58:27 +00:00
|
|
|
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
2021-10-05 23:22:21 +00:00
|
|
|
data[0] = self.contents[offset];
|
|
|
|
data[1] = self.contents[offset + 1];
|
2021-10-08 17:52:15 +00:00
|
|
|
if self.selected_count == 0 {
|
|
|
|
self.selected_sector = 0;
|
|
|
|
self.selected_count = 0;
|
|
|
|
}
|
2021-10-05 23:22:21 +00:00
|
|
|
},
|
|
|
|
ATA_REG_DATA_BYTE => {
|
|
|
|
self.selected_count -= 1;
|
|
|
|
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
|
|
|
data[0] = self.contents[offset];
|
2021-10-08 17:52:15 +00:00
|
|
|
if self.selected_count == 0 {
|
|
|
|
self.selected_sector = 0;
|
|
|
|
self.selected_count = 0;
|
|
|
|
}
|
2021-10-05 23:22:21 +00:00
|
|
|
},
|
|
|
|
ATA_REG_STATUS => {
|
|
|
|
data[0] = ATA_ST_DATA_READY;
|
2021-10-04 20:02:58 +00:00
|
|
|
},
|
2021-10-11 22:16:04 +00:00
|
|
|
ATA_REG_ERROR => {
|
|
|
|
data[0] = self.last_error;
|
|
|
|
},
|
2021-10-15 04:16:31 +00:00
|
|
|
_ => { debug!("{}: reading from {:0x}", DEV_NAME, addr); },
|
2021-10-04 20:02:58 +00:00
|
|
|
}
|
|
|
|
|
2021-10-27 00:33:23 +00:00
|
|
|
Ok(())
|
2021-10-04 20:02:58 +00:00
|
|
|
}
|
|
|
|
|
2023-04-24 01:49:40 +00:00
|
|
|
fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> {
|
2021-10-15 04:16:31 +00:00
|
|
|
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
2021-10-04 20:02:58 +00:00
|
|
|
match addr {
|
2021-10-05 23:22:21 +00:00
|
|
|
ATA_REG_DRIVE_HEAD => { self.selected_sector |= ((data[0] & 0x1F) as u32) << 24; },
|
|
|
|
ATA_REG_CYL_HIGH => { self.selected_sector |= (data[0] as u32) << 16; },
|
|
|
|
ATA_REG_CYL_LOW => { self.selected_sector |= (data[0] as u32) << 8; },
|
|
|
|
ATA_REG_SECTOR_NUM => { self.selected_sector |= data[0] as u32; },
|
|
|
|
ATA_REG_SECTOR_COUNT => { self.selected_count = (data[0] as u32) * ATA_SECTOR_SIZE; },
|
|
|
|
ATA_REG_COMMAND => {
|
|
|
|
match data[0] {
|
2021-10-15 04:16:31 +00:00
|
|
|
ATA_CMD_READ_SECTORS => { debug!("{}: reading sector {:x}", DEV_NAME, self.selected_sector); },
|
|
|
|
ATA_CMD_WRITE_SECTORS => { debug!("{}: writing sector {:x}", DEV_NAME, self.selected_sector); },
|
2021-10-05 23:22:21 +00:00
|
|
|
ATA_CMD_IDENTIFY => { },
|
|
|
|
ATA_CMD_SET_FEATURE => { },
|
2021-10-15 04:16:31 +00:00
|
|
|
_ => { debug!("{}: unrecognized command {:x}", DEV_NAME, data[0]); },
|
2021-10-05 23:22:21 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ATA_REG_FEATURE => {
|
|
|
|
// TODO implement features
|
|
|
|
},
|
2021-10-06 23:14:56 +00:00
|
|
|
ATA_REG_DATA_BYTE => {
|
2021-10-05 23:22:21 +00:00
|
|
|
// TODO implement writing
|
|
|
|
},
|
2021-10-15 04:16:31 +00:00
|
|
|
_ => { debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); },
|
2021-10-04 20:02:58 +00:00
|
|
|
}
|
2021-10-06 02:58:22 +00:00
|
|
|
Ok(())
|
2021-10-04 20:02:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-17 17:39:43 +00:00
|
|
|
impl Transmutable for AtaDevice {
|
|
|
|
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
|
|
|
Some(self)
|
2021-10-06 23:14:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|