use a2kit_macro::DiskStructError;
use log::{trace,debug,error};
use crate::img;
use crate::bios::Block;
use crate::bios::blocks;
use crate::{STDRESULT,DYNERR};
use super::names::*;
pub fn file_extensions() -> Vec<String> {
vec!["img".to_string(),"ima".to_string(),"dsk".to_string()]
}
pub struct Img {
kind: img::DiskKind,
sec_size: usize,
cylinders: usize,
heads: usize,
sectors: usize,
data: Vec<u8>
}
impl Img {
pub fn create(kind: img::DiskKind) -> Self {
let layout = match kind {
img::DiskKind::D35(layout) => layout,
img::DiskKind::D525(layout) => layout,
_ => panic!("unsupported track layout")
};
if layout.zones() > 1 {
panic!("layout has multiple zones");
}
let sec_size = layout.sector_size[0];
let cylinders = layout.cylinders[0];
let heads = layout.sides();
let sectors = layout.sectors[0];
let img_size = layout.byte_capacity();
Self {
kind,
sec_size,
cylinders,
heads,
sectors,
data: vec![0;img_size]
}
}
}
impl img::DiskImage for Img {
fn track_count(&self) -> usize {
return self.cylinders * self.heads;
}
fn end_track(&self) -> usize {
return self.cylinders * self.heads;
}
fn num_heads(&self) -> usize {
return self.heads;
}
fn nominal_capacity(&self) -> Option<usize> {
Some(self.data.len())
}
fn actual_capacity(&mut self) -> Result<usize,DYNERR> {
Ok(self.data.len())
}
fn read_block(&mut self,addr: Block) -> Result<Vec<u8>,DYNERR> {
trace!("read {}",addr);
match addr {
Block::FAT((_sec1,_secs)) => {
let secs_per_track = self.sectors;
let mut ans: Vec<u8> = Vec::new();
let deblocked_ts_list = addr.get_lsecs(secs_per_track as usize);
let chs_list = blocks::fat::std_blocking(deblocked_ts_list,self.heads)?;
for (trk,lsec) in chs_list {
match self.read_sector(trk,lsec) {
Ok(mut slice) => {
ans.append(&mut slice);
},
Err(e) => return Err(e)
}
}
Ok(ans)
},
_ => Err(Box::new(img::Error::ImageTypeMismatch))
}
}
fn write_block(&mut self, addr: Block, dat: &[u8]) -> STDRESULT {
trace!("write {}",addr);
match addr {
Block::FAT((_sec1,_secs)) => {
let secs_per_track = self.sectors;
let sec_size = self.sec_size;
let deblocked_ts_list = addr.get_lsecs(secs_per_track as usize);
let chs_list = blocks::fat::std_blocking(deblocked_ts_list,self.heads)?;
let mut src_offset = 0;
let padded = super::quantize_block(dat, chs_list.len()*sec_size);
for (trk,lsec) in chs_list {
match self.write_sector(trk,lsec,&padded[src_offset..src_offset+sec_size].to_vec()) {
Ok(_) => src_offset += sec_size,
Err(e) => return Err(e)
}
}
Ok(())
},
_ => Err(Box::new(img::Error::ImageTypeMismatch))
}
}
fn read_sector(&mut self,trk: super::Track,sec: super::Sector) -> Result<Vec<u8>,DYNERR> {
let track = self.get_track(trk.clone())?;
let [cyl,head,sec] = self.get_rzq(trk,sec)?;
trace!("reading {}/{}/{}",cyl,head,sec);
if track>=self.end_track() || sec<1 || sec>self.sectors as usize {
error!("track/sector range should be 0-{}/1-{}",self.end_track()-1,self.sectors);
return Err(Box::new(img::Error::SectorAccess));
}
let offset = (track*self.sectors as usize + sec - 1)*self.sec_size;
Ok(self.data[offset..offset+self.sec_size].to_vec())
}
fn write_sector(&mut self,trk: super::Track,sec: super::Sector,dat: &[u8]) -> STDRESULT {
let track = self.get_track(trk.clone())?;
let [cyl,head,sec] = self.get_rzq(trk,sec)?;
trace!("writing {}/{}/{}",cyl,head,sec);
if track>=self.end_track() || sec<1 || sec>self.sectors as usize {
error!("track/sector range should be 0-{}/1-{}",self.end_track()-1,self.sectors);
return Err(Box::new(img::Error::SectorAccess));
}
let offset = (track*self.sectors as usize + sec - 1)*self.sec_size;
let padded = super::quantize_block(dat, self.sec_size);
self.data[offset..offset+self.sec_size].copy_from_slice(&padded);
Ok(())
}
fn from_bytes(data: &[u8]) -> Result<Self,DiskStructError> {
let kind = match data.len() {
l if l==CPM_1.byte_capacity() => img::DiskKind::D8(CPM_1),
l if l==DSDD_77.byte_capacity() => img::DiskKind::D8(DSDD_77),
l if l==IBM_SSDD_8.byte_capacity() => img::DiskKind::D525(IBM_SSDD_8),
l if l==IBM_SSDD_9.byte_capacity() => img::DiskKind::D525(IBM_SSDD_9),
l if l==IBM_DSDD_8.byte_capacity() => img::DiskKind::D525(IBM_DSDD_8),
l if l==IBM_DSDD_9.byte_capacity() => img::DiskKind::D525(IBM_DSDD_9),
l if l==IBM_SSQD.byte_capacity() => img::DiskKind::D525(IBM_SSQD),
l if l==IBM_DSQD.byte_capacity() => img::DiskKind::D525(IBM_DSQD),
l if l==IBM_DSHD.byte_capacity() => img::DiskKind::D525(IBM_DSHD),
l if l==IBM_720.byte_capacity() => img::DiskKind::D35(IBM_720),
l if l==IBM_1440.byte_capacity() => img::DiskKind::D35(IBM_1440),
l if l==IBM_1680.byte_capacity() => img::DiskKind::D35(IBM_1680),
l if l==IBM_1720.byte_capacity() => img::DiskKind::D35(IBM_1720),
l if l==IBM_2880.byte_capacity() => img::DiskKind::D35(IBM_2880),
_ => return Err(DiskStructError::IllegalValue)
};
let layout = match kind {
img::DiskKind::D35(l) => l,
img::DiskKind::D525(l) => l,
img::DiskKind::D8(l) => l,
_ => return Err(DiskStructError::UnexpectedValue)
};
debug!("IMG size matches {}",kind);
let sec_size = layout.sector_size[0];
let cylinders = layout.cylinders[0];
let heads = layout.sides();
let sectors = layout.sectors[0];
Ok(Self {
kind,
sec_size,
cylinders,
heads,
sectors,
data: data.to_vec()
})
}
fn what_am_i(&self) -> img::DiskImageType {
img::DiskImageType::IMG
}
fn file_extensions(&self) -> Vec<String> {
file_extensions()
}
fn kind(&self) -> img::DiskKind {
self.kind
}
fn change_kind(&mut self,kind: img::DiskKind) {
self.kind = kind;
}
fn to_bytes(&mut self) -> Vec<u8> {
return self.data.clone();
}
fn get_track_buf(&mut self,_trk: super::Track) -> Result<Vec<u8>,DYNERR> {
error!("IMG images have no track bits");
return Err(Box::new(img::Error::ImageTypeMismatch));
}
fn set_track_buf(&mut self,_trk: super::Track,_dat: &[u8]) -> STDRESULT {
error!("IMG images have no track bits");
return Err(Box::new(img::Error::ImageTypeMismatch));
}
fn get_track_solution(&mut self,trk: super::Track) -> Result<img::TrackSolution,DYNERR> {
let [cylinder,head] = self.get_rz(trk)?;
let mut addr_map: Vec<[u8;6]> = Vec::new();
for i in 0..self.sectors {
let addr = [cylinder.try_into()?,head.try_into()?,(i+1).try_into()?,super::highest_bit(self.sec_size >> 8)];
addr_map.push(super::append_ibm_crc(addr, None));
}
let (flux_code,speed_kbps) = match self.kind {
img::DiskKind::D35(l) => (l.flux_code[0],l.speed_kbps[0]),
img::DiskKind::D525(l) => (l.flux_code[0],l.speed_kbps[0]),
img::DiskKind::D8(l) => (l.flux_code[0],l.speed_kbps[0]),
_ => (img::FluxCode::None,0)
};
return Ok(img::TrackSolution::Solved(img::SolvedTrack {
speed_kbps,
density: None,
flux_code,
addr_code: img::FieldCode::None,
data_code: img::FieldCode::None,
addr_type: "CHSFKK".to_string(),
addr_mask: [0,0,255,0,0,0],
addr_map,
size_map: vec![self.sec_size;self.sectors]
}));
}
fn get_track_nibbles(&mut self,_trk: super::Track) -> Result<Vec<u8>,DYNERR> {
error!("IMG images have no track bits");
return Err(Box::new(img::Error::ImageTypeMismatch));
}
fn display_track(&self,_bytes: &[u8]) -> String {
String::from("IMG images have no track bits to display")
}
}