use super::NibbleError;
use log::{debug,trace,warn};
use crate::bios::skew;
const INVALID_NIB_BYTE: u8 = 0xff;
const CHUNK62: usize = 175;
pub const ZONED_SECS_PER_TRACK: [usize;5] = [12,11,10,9,8];
pub const ZONE_BOUNDS_1: [usize;6] = [0,192,368,528,672,800];
pub const ZONE_BOUNDS_2: [usize;6] = [0,384,736,1056,1344,1600];
const SECTOR_SIZE: usize = 524; const DATA_NIBS: usize = 699; const CHK_NIBS: usize = 4;
const ADDRESS_FULL_SEGMENT: usize = 3 + 5 + 2; const DATA_FULL_SEGMENT: usize = 3 + 1 + DATA_NIBS + CHK_NIBS + 2; const SYNC_TRACK_HEADER: usize = 36;
const SYNC_GAP: usize = 6;
const SYNC_CLOSE: usize = 36;
const SECTOR_BITS: usize = ADDRESS_FULL_SEGMENT*8 + SYNC_GAP*10 + DATA_FULL_SEGMENT*8 + SYNC_CLOSE*10;
pub const TRACK_BITS: [usize;5] = [
SYNC_TRACK_HEADER*10 + ZONED_SECS_PER_TRACK[0]*SECTOR_BITS,
SYNC_TRACK_HEADER*10 + ZONED_SECS_PER_TRACK[1]*SECTOR_BITS,
SYNC_TRACK_HEADER*10 + ZONED_SECS_PER_TRACK[2]*SECTOR_BITS,
SYNC_TRACK_HEADER*10 + ZONED_SECS_PER_TRACK[3]*SECTOR_BITS,
SYNC_TRACK_HEADER*10 + ZONED_SECS_PER_TRACK[4]*SECTOR_BITS
];
pub const DISK_BYTES_62: [u8;64] = [
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
];
#[derive(Clone,Copy)]
pub struct SectorAddressFormat {
prolog: [u8;3],
epilog: [u8;2],
chk_seed: u8,
verify_chk: bool,
verify_track: bool,
prolog_mask: [u8;3],
epilog_mask: [u8;2]
}
impl SectorAddressFormat {
pub fn create_std() -> Self {
Self {
prolog: [0xd5,0xaa,0x96],
epilog: [0xde,0xaa],
chk_seed: 0x00,
verify_chk: true,
verify_track: true,
prolog_mask: [0xff,0xff,0xff],
epilog_mask: [0xff,0xff]
}
}
}
#[derive(Clone,Copy)]
pub struct SectorDataFormat {
prolog: [u8;3],
epilog: [u8;2],
prolog_mask: [u8;3],
epilog_mask: [u8;2]
}
impl SectorDataFormat {
pub fn create_std() -> Self {
Self {
prolog: [0xd5,0xaa,0xad],
epilog: [0xde,0xaa],
prolog_mask: [0xff,0xff,0xff],
epilog_mask: [0xff,0xff]
}
}
}
pub struct TrackBits {
id: usize,
sides: usize,
adr_fmt: SectorAddressFormat,
dat_fmt: SectorDataFormat,
bit_count: usize,
bit_ptr: usize
}
impl TrackBits {
pub fn create(id: usize,bit_count: usize,sides: usize) -> Self {
Self {
id,
sides,
adr_fmt: SectorAddressFormat::create_std(),
dat_fmt: SectorDataFormat::create_std(),
bit_count,
bit_ptr: 0
}
}
pub fn set_format_protocol(&mut self,adr_fmt: SectorAddressFormat,dat_fmt: SectorDataFormat) {
self.adr_fmt = adr_fmt;
self.dat_fmt = dat_fmt;
}
pub fn shift_fwd(&mut self,bit_shift: usize) {
let mut ptr = self.bit_ptr;
ptr += bit_shift;
while ptr >= self.bit_count {
ptr -= self.bit_count;
}
self.bit_ptr = ptr;
}
pub fn shift_rev(&mut self,bit_shift: usize) {
let mut ptr = self.bit_ptr as i64;
ptr -= bit_shift as i64;
while ptr < 0 {
ptr += self.bit_count as i64;
}
self.bit_ptr = ptr as usize;
}
pub fn read_latch(&mut self,bits: &[u8],data: &mut [u8],num_bytes: usize) -> usize {
let mut bit_count: usize = 0;
for byte in 0..num_bytes {
for _try in 0..self.bit_count {
bit_count += 1;
if self.next(bits)==1 {
break;
}
}
let mut val: u8 = 1;
for _bit in 0..7 {
val = val*2 + self.next(bits);
}
data[byte] = val;
bit_count += 7;
}
return bit_count;
}
pub fn next(&mut self,bits: &[u8]) -> u8 {
let i = self.bit_ptr/8;
let b = 7 - (self.bit_ptr%8) as u8;
self.shift_fwd(1);
return (bits[i] >> b) & 1;
}
pub fn read(&mut self,bits: &[u8],data: &mut [u8],num_bits: usize) {
for i in 0..num_bits {
let src_idx = self.bit_ptr/8;
let src_rel_bit = 7 - (self.bit_ptr%8) as u8;
let dst_idx = i/8;
let dst_rel_bit = 7 - (i%8) as u8;
let term = ((bits[src_idx] >> src_rel_bit) & 1) << dst_rel_bit;
data[dst_idx] &= (1 << dst_rel_bit) ^ u8::MAX;
data[dst_idx] |= term;
self.shift_fwd(1);
}
}
pub fn write(&mut self,bits: &mut [u8],data: &[u8],num_bits: usize) {
for i in 0..num_bits {
let dst_idx = self.bit_ptr/8;
let dst_rel_bit = 7 - (self.bit_ptr%8) as u8;
let src_idx = i/8;
let src_rel_bit = 7 - (i%8) as u8;
let term = ((data[src_idx] >> src_rel_bit) & 1) << dst_rel_bit;
bits[dst_idx] &= (1 << dst_rel_bit) ^ u8::MAX;
bits[dst_idx] |= term;
self.shift_fwd(1);
}
}
fn decode_addr(&mut self,bits: &[u8]) -> Result<(u8,u8,u8,u8,u8),NibbleError> {
let mut buf: [u8;8] = [0;8];
self.read_latch(bits,&mut buf,5);
return Ok((
decode_62(buf[0],invert_62())?,
decode_62(buf[1],invert_62())?,
decode_62(buf[2],invert_62())?,
decode_62(buf[3],invert_62())?,
decode_62(buf[4],invert_62())?,
));
}
fn find_byte_pattern(&mut self,bits: &[u8],patt: &[u8],mask: &[u8],cap: Option<usize>) -> Option<usize> {
if patt.len()==0 {
return Some(0);
}
let mut bit_count: usize = 0;
let mut matches = 0;
let mut test_byte: [u8;1] = [0;1];
for tries in 0..bits.len() {
if let Some(max) = cap {
if tries>=max {
return None;
}
}
bit_count += self.read_latch(bits,&mut test_byte,1);
if test_byte[0] & mask[matches] == patt[matches] & mask[matches] {
matches += 1;
} else {
matches = 0;
}
if matches==patt.len() {
return Some(bit_count);
}
}
return None;
}
fn find_sector(&mut self,bits: &[u8],ts: [u8;2]) -> Result<u8,NibbleError> {
trace!("seeking sector {}",ts[1]);
let adr_pro = self.adr_fmt.prolog.clone();
let adr_pro_mask = self.adr_fmt.prolog_mask.clone();
let adr_epi = self.adr_fmt.epilog.clone();
let adr_epi_mask = self.adr_fmt.epilog_mask.clone();
for _try in 0..32 {
if let Some(_shift) = self.find_byte_pattern(bits,&adr_pro,&adr_pro_mask,None) {
let (cyl,sector,side,format,chksum) = self.decode_addr(bits)?;
trace!("found cyl {}, sec {}, side {}, format {}, chksum {}",cyl,sector,side,format,chksum);
let chk = self.adr_fmt.chk_seed ^ cyl ^ sector ^ side ^ format;
let track = match self.sides {
1 => cyl + 64 * (side & 0x01),
2 => 2*(cyl + 64 * (side & 0x01)) + (side >> 5),
_ => panic!("unexpected sides {}",self.sides)
};
if self.adr_fmt.verify_track && track!=ts[0] {
warn!("track mismatch (want {}, got {})",ts[0],track);
continue;
}
if self.adr_fmt.verify_chk && chk != chksum {
warn!("checksum mismatch ({},{})",chk,chksum);
continue;
}
if self.find_byte_pattern(bits,&adr_epi,&adr_epi_mask,Some(10))==None {
warn!("missed address epilog");
continue;
}
if ts[1] != sector {
trace!("skip sector {}",sector);
continue;
}
trace!("found sector {}",sector);
return Ok(sector);
} else {
debug!("no address prolog found on track");
return Err(NibbleError::BadTrack);
}
}
debug!("the sector address was never matched");
return Err(NibbleError::SectorNotFound);
}
fn encode_sector_62(&mut self,bits: &mut [u8],dat: &[u8]) {
assert!(dat.len()>=SECTOR_SIZE);
let mut bak_buf: [u8;DATA_NIBS+CHK_NIBS] = [0;DATA_NIBS+CHK_NIBS];
let mut part0: [u8;CHUNK62] = [0;CHUNK62];
let mut part1: [u8;CHUNK62] = [0;CHUNK62];
let mut part2: [u8;CHUNK62] = [0;CHUNK62];
let [mut chk0,mut chk1,mut chk2]: [usize;3] = [0;3];
let [mut val,mut twos]: [u8;2];
let mut i: usize = 0;
let mut s: usize = 0;
loop {
chk0 = (chk0 & 0xff) << 1;
if chk0 & 0x100 > 0 {
chk0 += 1;
}
val = dat[s];
chk2 += val as usize;
if chk0 & 0x100 > 0 {
chk2 += 1;
chk0 &= 0xff;
}
part0[i] = ((val as usize ^ chk0) & 0xff) as u8;
val = dat[s+1];
chk1 += val as usize;
if chk2 > 0xff {
chk1 += 1;
chk2 &= 0xff;
}
part1[i] = ((val as usize ^ chk2) & 0xff) as u8;
if s + 2 >= SECTOR_SIZE {
chk0 &= 0xff;
chk1 &= 0xff;
chk2 &= 0xff;
break;
}
val = dat[s+2];
chk0 += val as usize;
if chk1 > 0xff {
chk0 += 1;
chk1 &= 0xff;
}
part2[i] = ((val as usize ^ chk1) & 0xff) as u8;
i += 1;
s += 3;
}
assert!(i==CHUNK62-1);
for i in 0..CHUNK62 {
twos = ((part0[i] & 0xc0) >> 2) | ((part1[i] & 0xc0) >> 4) | ((part2[i] & 0xc0) >> 6);
bak_buf[i*4+0] = encode_62(twos);
bak_buf[i*4+1] = encode_62(part0[i] & 0x3f);
bak_buf[i*4+2] = encode_62(part1[i] & 0x3f);
if i*4 + 3 < DATA_NIBS+CHK_NIBS {
bak_buf[i*4+3] = encode_62(part2[i] & 0x3f);
}
}
twos = (((chk0 & 0xc0) >> 6) | ((chk1 & 0xc0) >> 4) | ((chk2 & 0xc0) >> 2)) as u8;
bak_buf[DATA_NIBS+0] = encode_62(twos);
bak_buf[DATA_NIBS+1] = encode_62(chk2 as u8 & 0x3f);
bak_buf[DATA_NIBS+2] = encode_62(chk1 as u8 & 0x3f);
bak_buf[DATA_NIBS+3] = encode_62(chk0 as u8 & 0x3f);
self.write(bits,&bak_buf,(DATA_NIBS+CHK_NIBS)*8);
}
fn encode_sector(&mut self,bits: &mut [u8],sec: u8,dat: &[u8]) {
trace!("encoding sector");
let dat_fmt = self.dat_fmt;
self.write_sync_gap(bits,SYNC_GAP,10);
self.write(bits,&dat_fmt.prolog,24);
self.write(bits,&[encode_62(sec)],8);
self.encode_sector_62(bits,dat);
self.write(bits,&dat_fmt.epilog,16);
}
fn decode_sector_62(&mut self,bits: &[u8]) -> Result<Vec<u8>,NibbleError> {
let mut ans: Vec<u8> = Vec::new();
let mut bak_buf: [u8;DATA_NIBS+CHK_NIBS] = [0;DATA_NIBS+CHK_NIBS];
self.read_latch(bits,&mut bak_buf,DATA_NIBS+CHK_NIBS);
let [mut val,mut nib0,mut nib1,mut nib2,mut twos]: [u8;5];
let mut part0: [u8;CHUNK62] = [0;CHUNK62];
let mut part1: [u8;CHUNK62] = [0;CHUNK62];
let mut part2: [u8;CHUNK62] = [0;CHUNK62];
let mut idx = 0;
let inv = invert_62();
for i in 0..CHUNK62 {
twos = decode_62(bak_buf[idx+0], inv)?;
nib0 = decode_62(bak_buf[idx+1], inv)?;
nib1 = decode_62(bak_buf[idx+2], inv)?;
idx += 3;
if i != CHUNK62-1 {
nib2 = decode_62(bak_buf[idx], inv)?;
idx += 1;
} else {
nib2 = 0;
}
part0[i] = nib0 | ((twos << 2) & 0xc0);
part1[i] = nib1 | ((twos << 4) & 0xc0);
part2[i] = nib2 | ((twos << 6) & 0xc0);
}
let [mut chk0,mut chk1,mut chk2]: [usize;3] = [0;3];
let mut i = 0;
loop {
chk0 = (chk0 & 0xff) << 1;
if chk0 & 0x100 > 0 {
chk0 += 1;
}
val = (part0[i] as usize ^ chk0) as u8;
chk2 += val as usize;
if chk0 & 0x100 > 0 {
chk2 += 1;
chk0 &= 0xff;
}
ans.push(val);
val = (part1[i] as usize ^ chk2) as u8;
chk1 += val as usize;
if chk2 > 0xff {
chk1 += 1;
chk2 &= 0xff;
}
ans.push(val);
if ans.len()>=524 {
chk0 &= 0xff;
chk1 &= 0xff;
chk2 &= 0xff;
break;
}
val = (part2[i] as usize ^ chk1) as u8;
chk0 += val as usize;
if chk1 > 0xff {
chk0 += 1;
chk1 &= 0xff;
}
ans.push(val);
i+= 1;
}
assert!(idx==DATA_NIBS);
twos = decode_62(bak_buf[idx+0], inv)?;
nib2 = decode_62(bak_buf[idx+1], inv)?;
nib1 = decode_62(bak_buf[idx+2], inv)?;
nib0 = decode_62(bak_buf[idx+3], inv)?;
let rdchk0 = (nib0 | ((twos << 6) & 0xc0)) as usize;
let rdchk1 = (nib1 | ((twos << 4) & 0xc0)) as usize;
let rdchk2 = (nib2 | ((twos << 2) & 0xc0)) as usize;
if chk0 != rdchk0 || chk1 != rdchk1 || chk2 != rdchk2 {
debug!("expect checksum {},{},{} got {},{},{}",chk0,chk1,chk2,rdchk0,rdchk1,rdchk2);
return Err(NibbleError::BadChecksum);
}
return Ok(ans);
}
fn decode_sector(&mut self,bits: &[u8]) -> Result<Vec<u8>,NibbleError> {
trace!("decoding sector");
let prolog = self.dat_fmt.prolog.clone();
let pmask = self.dat_fmt.prolog_mask.clone();
let epilog = self.dat_fmt.epilog.clone();
let emask = self.dat_fmt.epilog_mask.clone();
let mut sec: [u8;1] = [0;1];
if let Some(_shift) = self.find_byte_pattern(bits,&prolog,&pmask,Some(40)) {
trace!("data field found");
self.read_latch(bits,&mut sec,1);
let ans = self.decode_sector_62(bits)?;
if self.find_byte_pattern(bits,&epilog,&emask,Some(10))==None {
warn!("data epilog not found");
}
return Ok(ans);
} else {
return Ok([0;SECTOR_SIZE].to_vec());
}
}
fn write_sync_gap(&mut self,bits: &mut [u8],num: usize,num_bits: usize) {
for _i in 0..num {
self.write(bits,&[0xff,0x00],num_bits);
}
}
}
impl super::TrackBits for TrackBits {
fn id(&self) -> usize {
self.id
}
fn bit_count(&self) -> usize {
self.bit_count
}
fn reset(&mut self) {
self.bit_ptr = 0;
}
fn get_bit_ptr(&self) -> usize {
self.bit_ptr
}
fn set_bit_ptr(&mut self,displ: usize) {
self.bit_ptr = displ;
}
fn read_sector(&mut self,bits: &[u8],track: u8,sector: u8) -> Result<Vec<u8>,NibbleError> {
match self.find_sector(bits,[track,sector]) {
Ok(_vol) => self.decode_sector(bits),
Err(e) => Err(e)
}
}
fn write_sector(&mut self,bits: &mut [u8],dat: &[u8],track: u8,sector: u8) -> Result<(),NibbleError> {
match self.find_sector(bits,[track,sector]) {
Ok(_vol) => Ok(self.encode_sector(bits,sector,dat)),
Err(e) => Err(e)
}
}
fn to_nibbles(&mut self,bits: &[u8]) -> Vec<u8> {
let mut ans: Vec<u8> = Vec::new();
let mut byte: [u8;1] = [0;1];
if self.find_byte_pattern(bits,&self.adr_fmt.prolog.clone(), &self.adr_fmt.prolog_mask.clone(), None) == None {
self.reset();
} else {
self.shift_rev(self.adr_fmt.prolog.len()*8);
}
let mut bit_count = 0;
for _try in 0..bits.len()*2 {
bit_count += self.read_latch(bits,&mut byte,1);
ans.push(byte[0]);
if bit_count >= self.bit_count {
break;
}
}
return ans;
}
}
pub fn invert_62() -> [u8;256] {
let mut ans: [u8;256] = [INVALID_NIB_BYTE;256];
for i in 0..64 {
ans[DISK_BYTES_62[i] as usize] = i as u8;
}
return ans;
}
fn encode_62(nib6: u8) -> u8 {
return DISK_BYTES_62[(nib6 & 0x3f) as usize];
}
pub fn decode_62(byte: u8,inv: [u8;256]) -> Result<u8,NibbleError> {
match inv[byte as usize] {
INVALID_NIB_BYTE => Err(NibbleError::InvalidByte),
x => Ok(x)
}
}
pub fn create_track(track: u8,sides: u8,buf_len: usize,adr_fmt: SectorAddressFormat, dat_fmt: SectorDataFormat) ->
(Vec<u8>,Box<dyn super::TrackBits>) {
let interleave = skew::get_phys_interleave(&skew::D35_PHYSICAL[0]) as u8;
let (cyl,mut side,format) = match sides {
1 => (track, 0, 0x00 + interleave),
2 => (track/2, 0x20*(track%2) ,0x20 + interleave),
_ => panic!("unexpected number of sides")
};
side += match cyl>=64 { true => 1, false => 0 };
let zone = cyl as usize / 16;
trace!("create cyl {}, side {:06b}, zone {}",cyl,side,zone);
let sectors = ZONED_SECS_PER_TRACK[zone];
let bit_count = TRACK_BITS[zone];
let mut bits: Vec<u8> = vec![0;buf_len];
let mut ans = TrackBits::create(track as usize,bit_count,sides as usize);
ans.set_format_protocol(adr_fmt, dat_fmt);
ans.write_sync_gap(&mut bits,SYNC_TRACK_HEADER,10);
for sector_pos in 0..sectors {
let sector = skew::D35_PHYSICAL[zone][sector_pos];
let chk = cyl ^ sector as u8 ^ side ^ format;
ans.write(&mut bits,&adr_fmt.prolog,24);
ans.write(&mut bits,&[encode_62(cyl%64)],8);
ans.write(&mut bits,&[encode_62(sector as u8)],8);
ans.write(&mut bits,&[encode_62(side)],8);
ans.write(&mut bits,&[encode_62(format)],8);
ans.write(&mut bits,&[encode_62(chk)],8);
ans.write(&mut bits,&adr_fmt.epilog,16);
ans.encode_sector(&mut bits,sector as u8,&[0;SECTOR_SIZE].to_vec());
ans.write_sync_gap(&mut bits,SYNC_CLOSE,10);
}
let mut obj: Box<dyn super::TrackBits> = Box::new(ans);
obj.reset();
return (bits,obj);
}
pub fn create_std_track(track: u8,sides: u8,buf_len: usize) -> (Vec<u8>,Box< dyn super::TrackBits>) {
return create_track(track,sides,buf_len,SectorAddressFormat::create_std(),SectorDataFormat::create_std());
}