1use std::cell::RefCell;
2use std::io;
3use std::path::Path;
4use std::rc::Rc;
5
6use disk::bam::{BAMFormat, BAMSection};
7use disk::block::{BlockDevice, BlockDeviceRef, ImageBlockDevice};
8use disk::error::DiskError;
9use disk::header::{Header, HeaderFormat};
10use disk::{self, BAMRef, Disk, DiskFormat, Geometry, Id, Image, Location, Track, BAM};
11
12const TRACK_COUNT: usize = 70;
13const HEADER_DOUBLE_SIDED_FLAG_OFFSET: usize = 0x03;
14const HEADER_DOUBLE_SIDED_VALUE: u8 = 0x80;
15
16static HEADER_FORMAT: HeaderFormat = HeaderFormat {
18 location: Location(18, 0),
19 first_directory_offset: 0x00,
20 disk_dos_version_offset: 0x02,
21 disk_name_offset: 0x90,
22 disk_id_offset: 0xA2,
23 dos_type_offset: 0xA5,
24 padding_offsets: &[0xA0, 0xA1, 0xA4, 0xA7, 0xA8, 0xA9, 0xAA],
25 expected_dos_version: 0x41,
26 expected_dos_type: Id([b'2', b'A']),
27 double_sided_flag_expectation: Some((
28 HEADER_DOUBLE_SIDED_FLAG_OFFSET,
29 HEADER_DOUBLE_SIDED_VALUE,
30 )),
31};
32
33static BAM_FORMAT: BAMFormat = BAMFormat {
35 sections: &[
36 BAMSection {
37 bitmap_location: Location(18, 0),
38 bitmap_offset: 0x05,
39 bitmap_size: 3,
40 bitmap_stride: 4,
41 free_location: Location(18, 0),
42 free_offset: 0x04,
43 free_stride: 4,
44 tracks: 35,
45 },
46 BAMSection {
47 bitmap_location: Location(53, 0),
48 bitmap_offset: 0x00,
49 bitmap_size: 3,
50 bitmap_stride: 3,
51 free_location: Location(18, 0),
52 free_offset: 0xDD,
53 free_stride: 1,
54 tracks: 35,
55 },
56 ],
57};
58
59static DISK_FORMAT: DiskFormat = DiskFormat {
61 directory_track: 18,
62 first_directory_sector: 1,
63 reserved_track: 53,
64 first_track: 1,
65 last_track: 70,
66 last_nonstandard_track: 70,
67 interleave: 6,
68 directory_interleave: 3,
69 geos: false,
70 tracks: &TRACKS,
71 header: &HEADER_FORMAT,
72 bam: &BAM_FORMAT,
73};
74
75pub static GEOMETRY: Geometry = Geometry {
76 track_layouts: &TRACKS,
77 tracks: TRACK_COUNT as u8,
78 with_error_table: false,
79};
80
81pub static GEOMETRY_ERRORS: Geometry = Geometry {
82 track_layouts: &TRACKS,
83 tracks: TRACK_COUNT as u8,
84 with_error_table: true,
85};
86
87static ALLOWED_GEOMETRIES: [&Geometry; 2] = [&GEOMETRY, &GEOMETRY_ERRORS];
88
89#[cfg_attr(rustfmt, rustfmt_skip)]
90static TRACKS: [Track; TRACK_COUNT+1] = [
91 Track { sectors: 0, sector_offset: 0, byte_offset: 0, }, Track { sectors: 21, sector_offset: 0, byte_offset: 0x00000, }, Track { sectors: 21, sector_offset: 21, byte_offset: 0x01500, }, Track { sectors: 21, sector_offset: 42, byte_offset: 0x02A00, }, Track { sectors: 21, sector_offset: 63, byte_offset: 0x03F00, }, Track { sectors: 21, sector_offset: 84, byte_offset: 0x05400, }, Track { sectors: 21, sector_offset: 105, byte_offset: 0x06900, }, Track { sectors: 21, sector_offset: 126, byte_offset: 0x07E00, }, Track { sectors: 21, sector_offset: 147, byte_offset: 0x09300, }, Track { sectors: 21, sector_offset: 168, byte_offset: 0x0A800, }, Track { sectors: 21, sector_offset: 189, byte_offset: 0x0BD00, }, Track { sectors: 21, sector_offset: 210, byte_offset: 0x0D200, }, Track { sectors: 21, sector_offset: 231, byte_offset: 0x0E700, }, Track { sectors: 21, sector_offset: 252, byte_offset: 0x0FC00, }, Track { sectors: 21, sector_offset: 273, byte_offset: 0x11100, }, Track { sectors: 21, sector_offset: 294, byte_offset: 0x12600, }, Track { sectors: 21, sector_offset: 315, byte_offset: 0x13B00, }, Track { sectors: 21, sector_offset: 336, byte_offset: 0x15000, }, Track { sectors: 19, sector_offset: 357, byte_offset: 0x16500, }, Track { sectors: 19, sector_offset: 376, byte_offset: 0x17800, }, Track { sectors: 19, sector_offset: 395, byte_offset: 0x18B00, }, Track { sectors: 19, sector_offset: 414, byte_offset: 0x19E00, }, Track { sectors: 19, sector_offset: 433, byte_offset: 0x1B100, }, Track { sectors: 19, sector_offset: 452, byte_offset: 0x1C400, }, Track { sectors: 19, sector_offset: 471, byte_offset: 0x1D700, }, Track { sectors: 18, sector_offset: 490, byte_offset: 0x1EA00, }, Track { sectors: 18, sector_offset: 508, byte_offset: 0x1FC00, }, Track { sectors: 18, sector_offset: 526, byte_offset: 0x20E00, }, Track { sectors: 18, sector_offset: 544, byte_offset: 0x22000, }, Track { sectors: 18, sector_offset: 562, byte_offset: 0x23200, }, Track { sectors: 18, sector_offset: 580, byte_offset: 0x24400, }, Track { sectors: 17, sector_offset: 598, byte_offset: 0x25600, }, Track { sectors: 17, sector_offset: 615, byte_offset: 0x26700, }, Track { sectors: 17, sector_offset: 632, byte_offset: 0x27800, }, Track { sectors: 17, sector_offset: 649, byte_offset: 0x28900, }, Track { sectors: 17, sector_offset: 666, byte_offset: 0x29A00, }, Track { sectors: 21, sector_offset: 683, byte_offset: 0x2AB00, }, Track { sectors: 21, sector_offset: 704, byte_offset: 0x2C000, }, Track { sectors: 21, sector_offset: 725, byte_offset: 0x2D500, }, Track { sectors: 21, sector_offset: 746, byte_offset: 0x2EA00, }, Track { sectors: 21, sector_offset: 767, byte_offset: 0x2FF00, }, Track { sectors: 21, sector_offset: 788, byte_offset: 0x31400, }, Track { sectors: 21, sector_offset: 809, byte_offset: 0x32900, }, Track { sectors: 21, sector_offset: 830, byte_offset: 0x33E00, }, Track { sectors: 21, sector_offset: 851, byte_offset: 0x35300, }, Track { sectors: 21, sector_offset: 872, byte_offset: 0x36800, }, Track { sectors: 21, sector_offset: 893, byte_offset: 0x37D00, }, Track { sectors: 21, sector_offset: 914, byte_offset: 0x39200, }, Track { sectors: 21, sector_offset: 935, byte_offset: 0x3A700, }, Track { sectors: 21, sector_offset: 956, byte_offset: 0x3BC00, }, Track { sectors: 21, sector_offset: 977, byte_offset: 0x3D100, }, Track { sectors: 21, sector_offset: 998, byte_offset: 0x3E600, }, Track { sectors: 21, sector_offset: 1019, byte_offset: 0x3FB00, }, Track { sectors: 19, sector_offset: 1040, byte_offset: 0x41000, }, Track { sectors: 19, sector_offset: 1059, byte_offset: 0x42300, }, Track { sectors: 19, sector_offset: 1078, byte_offset: 0x43600, }, Track { sectors: 19, sector_offset: 1097, byte_offset: 0x44900, }, Track { sectors: 19, sector_offset: 1116, byte_offset: 0x45C00, }, Track { sectors: 19, sector_offset: 1135, byte_offset: 0x46F00, }, Track { sectors: 19, sector_offset: 1154, byte_offset: 0x48200, }, Track { sectors: 18, sector_offset: 1173, byte_offset: 0x49500, }, Track { sectors: 18, sector_offset: 1191, byte_offset: 0x4A700, }, Track { sectors: 18, sector_offset: 1209, byte_offset: 0x4B900, }, Track { sectors: 18, sector_offset: 1227, byte_offset: 0x4CB00, }, Track { sectors: 18, sector_offset: 1245, byte_offset: 0x4DD00, }, Track { sectors: 18, sector_offset: 1263, byte_offset: 0x4EF00, }, Track { sectors: 17, sector_offset: 1281, byte_offset: 0x50100, }, Track { sectors: 17, sector_offset: 1298, byte_offset: 0x51200, }, Track { sectors: 17, sector_offset: 1315, byte_offset: 0x52300, }, Track { sectors: 17, sector_offset: 1332, byte_offset: 0x53400, }, Track { sectors: 17, sector_offset: 1349, byte_offset: 0x54500, }, ];
163
164pub struct D71 {
167 blocks: Rc<RefCell<ImageBlockDevice>>,
168 header: Option<Header>,
169 bam: Option<BAMRef>,
170 format: Option<DiskFormat>,
171}
172
173impl D71 {
174 fn new(image: Image) -> io::Result<D71> {
175 let geometry = match Geometry::find_by_size(image.len(), &ALLOWED_GEOMETRIES[..]) {
177 Some(geometry) => geometry,
178 None => return Err(DiskError::InvalidLayout.into()),
179 };
180
181 let blocks = ImageBlockDevice::new(image, geometry);
182
183 let mut d71 = D71 {
184 blocks: Rc::new(RefCell::new(blocks)),
185 format: None,
186 header: None,
187 bam: None,
188 };
189 d71.initialize();
190 Ok(d71)
191 }
192
193 pub fn open<P: AsRef<Path>>(path: P, writable: bool) -> io::Result<D71> {
196 let image = if writable {
197 Image::open_read_write(path)?
198 } else {
199 Image::open_read_only(path)?
200 };
201 Self::new(image)
202 }
203
204 pub fn create<P: AsRef<Path>>(
208 path: P,
209 geometry: &Geometry,
210 create_new: bool,
211 ) -> io::Result<D71> {
212 Self::new(Image::create(path, geometry.size(), create_new)?)
213 }
214
215 pub fn open_memory(geometry: &Geometry) -> io::Result<D71> {
217 Self::new(Image::open_memory(geometry.size())?)
218 }
219
220 #[inline]
222 pub fn geometry(with_error_table: bool) -> &'static Geometry {
223 if with_error_table {
224 &GEOMETRY_ERRORS
225 } else {
226 &GEOMETRY
227 }
228 }
229}
230
231impl Disk for D71 {
232 #[inline]
233 fn native_disk_format(&self) -> &'static DiskFormat {
234 &DISK_FORMAT
235 }
236
237 fn disk_format(&self) -> io::Result<&DiskFormat> {
238 match self.format {
239 Some(ref format) => Ok(format),
240 None => Err(DiskError::Unformatted.into()),
241 }
242 }
243
244 fn disk_format_mut(&mut self) -> io::Result<&mut DiskFormat> {
245 match self.format {
246 Some(ref mut format) => Ok(format),
247 None => Err(DiskError::Unformatted.into()),
248 }
249 }
250
251 fn set_disk_format(&mut self, disk_format: Option<DiskFormat>) {
252 self.format = disk_format;
253 }
254
255 fn blocks(&self) -> BlockDeviceRef {
256 self.blocks.clone()
257 }
258
259 fn blocks_ref(&self) -> ::std::cell::Ref<'_, BlockDevice> {
260 self.blocks.borrow()
261 }
262
263 fn blocks_ref_mut(&self) -> ::std::cell::RefMut<'_, BlockDevice> {
264 self.blocks.borrow_mut()
265 }
266
267 fn header<'a>(&'a self) -> io::Result<&'a Header> {
268 match &self.header {
269 &Some(ref header) => Ok(&header),
270 &None => Err(DiskError::Unformatted.into()),
271 }
272 }
273
274 fn header_mut<'a>(&'a mut self) -> io::Result<&'a mut Header> {
275 self.blocks.borrow().check_writability()?;
276 match &mut self.header {
277 &mut Some(ref mut header) => Ok(header),
278 &mut None => Err(DiskError::Unformatted.into()),
279 }
280 }
281
282 fn set_header(&mut self, header: Option<Header>) {
283 self.header = header;
284 }
285
286 fn flush_header(&mut self) -> io::Result<()> {
287 let header = match self.header {
288 Some(ref mut header) => header,
289 None => return Err(DiskError::Unformatted.into()),
290 };
291 header.write(self.blocks.clone(), &HEADER_FORMAT)
292 }
293
294 fn bam(&self) -> io::Result<BAMRef> {
295 match &self.bam {
296 &Some(ref bam) => Ok(bam.clone()),
297 &None => Err(DiskError::Unformatted.into()),
298 }
299 }
300
301 fn set_bam(&mut self, bam: Option<BAM>) {
302 disk::set_bam(&mut self.bam, bam);
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 #[allow(unused_imports)]
309 use super::*;
310 use disk::BLOCK_SIZE;
311
312 #[test]
313 fn test_track_consistency() {
314 let mut sector_offset = 0;
315 let mut byte_offset = 0;
316 for track in super::TRACKS.iter() {
317 assert_eq!(track.sector_offset, sector_offset);
318 assert_eq!(track.byte_offset, byte_offset);
319 sector_offset += track.sectors as u16;
320 byte_offset += track.sectors as u32 * BLOCK_SIZE as u32;
321 }
322 }
323}