import DOS from 'js/formats/do'; import { memory } from 'js/types'; import { BYTES_BY_SECTOR, BYTES_BY_TRACK } from './testdata/16sector'; import { expectSequence, findBytes, skipGap } from './util'; describe('DOS format', () => { it('is callable', () => { const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); expect(disk).not.toBeNull(); }); it('has correct number of tracks', () => { const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); expect(disk.tracks.length).toEqual(35); }); it('has correct number of bytes in track 0', () => { const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); expect(disk.tracks[0].length).toEqual(6632); }); it('has correct number of bytes in all tracks', () => { const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); // Track 0 is slightly longer for some reason. expect(disk.tracks[0].length).toEqual(6632); for (let i = 1; i < disk.tracks.length; i++) { expect(disk.tracks[i].length).toEqual(6602); } }); it('has correct GAP 1', () => { const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); // From Beneith Apple DOS, GAP 1 should have 12-85 0xFF bytes const track = disk.tracks[0]; let numFF = 0; while (track[numFF] === 0xFF && numFF < 0x100) { numFF++; } expect(numFF).toBeGreaterThanOrEqual(40); expect(numFF).toBeLessThanOrEqual(128); }); it('has correct Address Field for track 0, sector 0', () => { // _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); const track = disk.tracks[0]; let i = skipGap(track); // prologue i = expectSequence(track, i, [0xD5, 0xAA, 0x96]); // volume 10 = 0b00001010 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101010); // track 0 = 0b00000000 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101010); // sector 0 = 0b00000000 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101010); // checksum = 0b00000101 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101010); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Data Field for track 0, sector 0 (BYTES_BY_TRACK)', () => { // _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); const track: memory = disk.tracks[0]; // skip to the first address epilogue let i = findBytes(track, [0xDE, 0xAA, 0xEB]); expect(i).toBeGreaterThan(50); i = skipGap(track, i); // prologue i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]); // data (all zeros, which is 0x96 with 6 and 2 encoding) for (let j = 0; j < 342; j++) { expect(track[i++]).toBe(0x96); } // checksum (also zero) expect(track[i++]).toBe(0x96); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Address Field for track 0, sector 1', () => { // _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); const track = disk.tracks[0]; // first sector prologue let i = findBytes(track, [0xD5, 0xAA, 0x96]); // second sector prologue i = findBytes(track, [0xD5, 0xAA, 0x96], i); // volume 10 = 0b00001010 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101010); // track 0 = 0b00000000 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101010); // sector 1 = 0b00000001 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101011); // checksum = 0b00000101 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101011); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Data Field for track 0, sector 1 (BYTES_BY_SECTOR)', () => { // _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21 const disk = DOS({ name: 'test disk', data: BYTES_BY_SECTOR, volume: 10, readOnly: true, }); const track: memory = disk.tracks[0]; // First data field prologue let i = findBytes(track, [0xD5, 0xAA, 0xAD]); // Second data field prologue i = findBytes(track, [0xD5, 0xAA, 0xAD], i); // Sector 1 is DOS sector 7. // In 6 x 2 encoding, the lowest 2 bits of all the bytes come first. // 0x07 is 0b00000111, so the lowest two bits are 0b11, reversed and // repeated would be 0b111111 (3F -> 0xFF), but since each byte is // XOR'd with the previous, this means there are 85 0b00000000 (00 -> // 0x96) bytes. expect(track[i++]).toBe(0xFF); for (let j = 0; j < 85; j++) { expect(track[i++]).toBe(0x96); } // Next we get 256 instances of the top bits, 0b000001. Again, with // the XOR, this means one 0b000001 XOR 0b111111 = 0b111110 // (3E -> 0xFE) followed by 255 0b0000000 (00 -> 0x96). expect(track[i++]).toBe(0xFE); for (let j = 0; j < 255; j++) { expect(track[i++]).toBe(0x96); } // checksum expect(track[i++]).toBe(0x97); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Address Field for track 1, sector 0', () => { // _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); const track = disk.tracks[1]; let i = skipGap(track); // prologue i = expectSequence(track, i, [0xD5, 0xAA, 0x96]); // volume 10 = 0b00001010 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101010); // track 1 = 0b00000001 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101011); // sector 0 = 0b00000000 expect(track[i++]).toBe(0b10101010); expect(track[i++]).toBe(0b10101010); // checksum = 0b00000100 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101011); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Data Field for track 1, sector 0 (BYTES_BY_TRACK)', () => { // _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); const track: memory = disk.tracks[1]; let i = findBytes(track, [0xDE, 0xAA, 0xEB]); expect(i).toBeGreaterThan(50); i = skipGap(track, i); // prologue i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]); // In 6 x 2 encoding, the lowest 2 bits of all the bytes come first. // This would normally mean 86 instances of 0b101010 (2A -> 0xE6), // but each byte is XOR'd with the previous. Since all of the bits // are the same, this means there are 85 0b000000 (00 -> 0x96). expect(track[i++]).toBe(0xE6); for (let j = 0; j < 85; j++) { expect(track[i++]).toBe(0x96); } // Next we get 256 instances of the top bits, 0b000000. Again, with // the XOR, this means one 0x101010 (2A -> 0xE6) followed by 255 // 0b0000000 (00 -> 0x96). expect(track[i++]).toBe(0xE6); for (let j = 0; j < 255; j++) { expect(track[i++]).toBe(0x96); } // checksum (also zero) expect(track[i++]).toBe(0x96); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); }); it('has correct Address Fields for all tracks', () => { // _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12 const disk = DOS({ name: 'test disk', data: BYTES_BY_TRACK, volume: 10, readOnly: true, }); for (let t = 0; t < disk.tracks.length; t++) { // We essentially seek through the track for the Address Fields const track = disk.tracks[t]; let i = findBytes(track, [0xD5, 0xAA, 0x96]); for (let s = 0; s <= 15; s++) { // volume 10 = 0b00001010 expect(track[i++]).toBe(0b10101111); expect(track[i++]).toBe(0b10101010); // convert track to 4x4 encoding const track4x4XX = ((t & 0b10101010) >> 1) | 0b10101010; const track4x4YY = (t & 0b01010101) | 0b10101010; expect(track[i++]).toBe(track4x4XX); expect(track[i++]).toBe(track4x4YY); // convert sector to 4x4 encoding const sector4x4XX = ((s & 0b10101010) >> 1) | 0b10101010; const sector4x4YY = (s & 0b01010101) | 0b10101010; expect(track[i++]).toBe(sector4x4XX); expect(track[i++]).toBe(sector4x4YY); // checksum expect(track[i++]).toBe(0b10101111 ^ track4x4XX ^ sector4x4XX); expect(track[i++]).toBe(0b10101010 ^ track4x4YY ^ sector4x4YY); // epilogue i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]); // next sector i = findBytes(track, [0xD5, 0xAA, 0x96], i); } } }); });