use crate::fs::cpm::types::{DIR_ENTRY_SIZE,LOGICAL_EXTENT_SIZE,RECORD_SIZE};
use log::debug;
use std::fmt;
#[derive(PartialEq,Eq,Clone)]
pub struct DiskParameterBlock {
pub spt: u16,
pub bsh: u8,
pub blm: u8,
pub exm: u8,
pub dsm: u16,
pub drm: u16,
pub al0: u8,
pub al1: u8,
pub cks: u16,
pub off: u16,
pub psh: u8,
pub phm: u8,
pub reserved_track_capacity: usize
}
impl DiskParameterBlock {
pub fn to_json(&self,indent: Option<u16>) -> String {
let mut ans = json::JsonValue::new_object();
let mut dpb = json::JsonValue::new_object();
dpb["spt"] = json::JsonValue::String(hex::encode_upper(&u16::to_le_bytes(self.spt)));
dpb["bsh"] = json::JsonValue::String(hex::encode_upper(&vec![self.bsh]));
dpb["blm"] = json::JsonValue::String(hex::encode_upper(&vec![self.blm]));
dpb["exm"] = json::JsonValue::String(hex::encode_upper(&vec![self.exm]));
dpb["dsm"] = json::JsonValue::String(hex::encode_upper(&u16::to_le_bytes(self.dsm)));
dpb["drm"] = json::JsonValue::String(hex::encode_upper(&u16::to_le_bytes(self.drm)));
dpb["al0"] = json::JsonValue::String(hex::encode_upper(&vec![self.al0]));
dpb["al1"] = json::JsonValue::String(hex::encode_upper(&vec![self.al1]));
dpb["cks"] = json::JsonValue::String(hex::encode_upper(&u16::to_le_bytes(self.cks)));
dpb["off"] = json::JsonValue::String(hex::encode_upper(&u16::to_le_bytes(self.off)));
dpb["psh"] = json::JsonValue::String(hex::encode_upper(&vec![self.psh]));
dpb["phm"] = json::JsonValue::String(hex::encode_upper(&vec![self.phm]));
ans["dpb"] = dpb;
if let Some(spaces) = indent {
return json::stringify_pretty(ans,spaces);
} else {
return json::stringify(ans);
}
}
}
pub const A2_525: DiskParameterBlock = DiskParameterBlock {
spt: 32,
bsh: 3,
blm: 7,
exm: 0,
dsm: 127, drm: 47, al0: 0b11000000,
al1: 0b00000000,
cks: 12, off: 3,
psh: 0,
phm: 0,
reserved_track_capacity: 3*32*128
};
pub const CPM1: DiskParameterBlock = DiskParameterBlock {
spt: 26,
bsh: 3,
blm: 7,
exm: 0,
dsm: 242,
drm: 63,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 2,
psh: 0,
phm: 0,
reserved_track_capacity: 2*26*128
};
pub const SSSD_525: DiskParameterBlock = DiskParameterBlock {
spt: 20,
bsh: 4,
blm: 15,
exm: 1,
dsm: 45,
drm: 63,
al0: 0b10000000,
al1: 0b00000000,
cks: 16,
off: 3,
psh: 0,
phm: 0,
reserved_track_capacity: 3*20*128
};
pub const SSDD_525_OFF3: DiskParameterBlock = DiskParameterBlock {
spt: 40,
bsh: 3,
blm: 7,
exm: 0,
dsm: 184,
drm: 63,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 3,
psh: 0,
phm: 0,
reserved_track_capacity: 3*40*128
};
pub const SSDD_525_OFF1: DiskParameterBlock = DiskParameterBlock {
spt: 40,
bsh: 3,
blm: 7,
exm: 0,
dsm: 194,
drm: 63,
al0: 0b11110000,
al1: 0b00000000,
cks: 16,
off: 1,
psh: 0,
phm: 0,
reserved_track_capacity: 40*128
};
pub const DSDD_525_OFF1: DiskParameterBlock = DiskParameterBlock {
spt: 40,
bsh: 4,
blm: 15,
exm: 1,
dsm: 196,
drm: 63,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 1,
psh: 0,
phm: 0,
reserved_track_capacity: 40*128
};
pub const SSDD_3: DiskParameterBlock = DiskParameterBlock {
spt: 36,
bsh: 3,
blm: 7,
exm: 0,
dsm: 174,
drm: 63,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 1,
psh: 0,
phm: 0,
reserved_track_capacity: 36*128
};
pub const TRS80_M2: DiskParameterBlock = DiskParameterBlock {
spt: 64,
bsh: 4,
blm: 15,
exm: 0,
dsm: 299,
drm: 127,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 2,
psh: 0,
phm: 0,
reserved_track_capacity: 26*128 + 16*512
};
pub const NABU: DiskParameterBlock = DiskParameterBlock {
spt: 52,
bsh: 4,
blm: 15,
exm: 0,
dsm: 493,
drm: 127,
al0: 0b11000000,
al1: 0b00000000,
cks: 16,
off: 2,
psh: 0,
phm: 0,
reserved_track_capacity: 2*26*128
};
impl DiskParameterBlock {
pub fn create(kind: &crate::img::DiskKind) -> Self {
match *kind {
crate::img::names::A2_DOS33_KIND => A2_525,
crate::img::names::IBM_CPM1_KIND => CPM1,
crate::img::names::OSBORNE1_SD_KIND => SSSD_525,
crate::img::names::OSBORNE1_DD_KIND => SSDD_525_OFF3,
crate::img::names::KAYPROII_KIND => SSDD_525_OFF1,
crate::img::names::KAYPRO4_KIND => DSDD_525_OFF1,
crate::img::names::AMSTRAD_SS_KIND => SSDD_3,
crate::img::names::TRS80_M2_CPM_KIND => TRS80_M2,
crate::img::names::NABU_CPM_KIND => NABU,
_ => panic!("Disk kind not supported")
}
}
pub fn verify(&self) -> bool {
if self.bsh<3 || self.bsh>7 {
debug!("BSH is invalid");
return false;
}
if self.blm as usize!=num_traits::pow(2,self.bsh as usize)-1 {
debug!("BLM must be 2^BSH-1");
return false;
}
if self.dsm>0x7fff {
debug!("block count exceeds maximum");
return false;
}
if self.bsh==3 && self.dsm>0xff {
debug!("block count exceeds maximum for 1K blocks");
return false;
}
let bls = (128 as usize) << self.bsh as usize;
let max_exm = match self.dsm {
dsm if dsm<256 => 16*bls/LOGICAL_EXTENT_SIZE - 1,
_ => 8*bls/LOGICAL_EXTENT_SIZE - 1
};
if self.exm as usize > max_exm {
debug!("too many logical extents");
return false;
}
match self.exm {
0b0 | 0b1 | 0b11 | 0b111 | 0b1111 => {},
_ => {
debug!("invalid extent mask {}",self.exm);
return false;
}
}
if self.drm as usize + 1 > 16*bls/32 {
debug!("directory exceeds 16 blocks");
return false;
}
let mut mask16 = self.al0 as u16 * 256 + self.al1 as u16;
let mut contiguous_dir_blocks = 0;
for _i in 0..16 {
if mask16 & 0x8000 > 0 {
contiguous_dir_blocks += 1;
mask16 <<= 1;
} else {
break;
}
}
if (contiguous_dir_blocks as usize) < (self.drm as usize + 1)*32/bls {
debug!("block map fails to cover directory : {} contiguous blocks were provided",contiguous_dir_blocks);
return false;
}
if self.dir_blocks() > self.user_blocks() {
debug!("directory end block out of range");
return false;
}
return true;
}
pub fn block_size(&self) -> usize {
(128 as usize) << self.bsh as usize
}
pub fn ptr_size(&self) -> usize {
match self.dsm {
dsm if dsm<256 => 1,
_ => 2
}
}
pub fn extent_capacity(&self) -> usize {
(self.exm as usize + 1) * LOGICAL_EXTENT_SIZE
}
pub fn user_blocks(&self) -> usize {
self.dsm as usize + 1
}
pub fn dir_entries(&self) -> usize {
self.drm as usize + 1
}
pub fn dir_blocks(&self) -> usize {
let full_blocks = self.dir_entries()*DIR_ENTRY_SIZE/self.block_size();
match (self.dir_entries() * DIR_ENTRY_SIZE) % self.block_size() {
0 => full_blocks,
_ => 1 + full_blocks
}
}
pub fn reserved_blocks(&self) -> usize {
let mut ans = 0;
let mut mask16 = self.al0 as u16 * 256 + self.al1 as u16;
for _i in 0..16 {
if mask16 & 0x8000 > 0 {
ans += 1;
mask16 <<= 1;
}
}
ans
}
pub fn is_reserved(&self,block: usize) -> bool {
if block > 15 {
return false;
}
((self.al0 as u16 * 256 + self.al1 as u16) << block) & 0x8000 > 0
}
pub fn disk_capacity(&self) -> usize {
let track_capacity = self.spt as usize * RECORD_SIZE;
let user = self.user_blocks() * self.block_size();
let remainder = user % track_capacity;
if remainder>0 {
return self.reserved_track_capacity + user + track_capacity - remainder;
} else {
return self.reserved_track_capacity + user;
}
}
}
impl fmt::Display for DiskParameterBlock {
fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
A2_525 => write!(f,"Apple 5.25 inch"),
CPM1 => write!(f,"IBM 8 inch SSSD"),
SSSD_525 => write!(f,"IBM 5.25 inch SSSD"),
SSDD_525_OFF1 => write!(f,"IBM 5.25 inch SSDD"),
SSDD_525_OFF3 => write!(f,"IBM 5.25 inch SSDD"),
DSDD_525_OFF1 => write!(f,"IBM 5.25 inch DSSD"),
SSDD_3 => write!(f,"IBM 3 inch SSDD"),
TRS80_M2 => write!(f,"IBM 8 inch SSDD"),
NABU => write!(f,"IBM 8 inch DSDD"),
_ => write!(f,"unknown disk")
}
}
}