apple2js/test/js/formats/format_utils.spec.ts
Will Scullin 1e79d9d59d
Prettier (#203)
* Enabled prettier

* Update lint, fix issues

* Restore some array formatting
2023-11-24 06:45:55 -08:00

535 lines
19 KiB
TypeScript

import createDiskFromDOS13 from 'js/formats/d13';
import createDiskFromDOS from 'js/formats/do';
import {
defourXfour,
DO,
explodeSector13,
findSector,
fourXfour,
readSector,
} from 'js/formats/format_utils';
import {
BYTES_BY_SECTOR as BYTES_BY_SECTOR_13,
BYTES_IN_ORDER as BYTES_IN_ORDER_13,
} from './testdata/13sector';
import { BYTES_BY_SECTOR as BYTES_BY_SECTOR_16 } from './testdata/16sector';
describe('fourXfour', () => {
// d7 d6 d5 d4 d3 d2 d1 d0
// => 1 d7 1 d5 1 d3 1 d1
// 1 d6 1 d4 1 d2 1 d0
it('converts 0x00 correctly', () => {
// 0000 0000 => 1010 1010, 1010 1010
expect(fourXfour(0x00)).toEqual([0b1010_1010, 0b1010_1010]);
});
it('converts 0xff correctly', () => {
// 1111 1111 => 1111 1111, 1111 1111
expect(fourXfour(0xff)).toEqual([0b1111_1111, 0b1111_1111]);
});
it('converts 0x55 correctly', () => {
// 0101 0101 => 1010 1010, 1111 1111
expect(fourXfour(0x55)).toEqual([0b1010_1010, 0b1111_1111]);
});
it('converts 0xAA correctly', () => {
// 1010 1010 => 1111 1111, 1010 1010
expect(fourXfour(0xaa)).toEqual([0b1111_1111, 0b1010_1010]);
});
it('converts 0xA5 correctly', () => {
// 1010 0101 => 1111 1010, 1010 1111
expect(fourXfour(0xa5)).toEqual([0b1111_1010, 0b1010_1111]);
});
it('converts 0x5A correctly', () => {
// 0101 1010 => 1010 1111, 1111 1010
expect(fourXfour(0x5a)).toEqual([0b1010_1111, 0b1111_1010]);
});
it('converts 0xC3 (0b1100_0011) correctly', () => {
// 1100 0011 => 1110 1011, 1110 1011
expect(fourXfour(0b1100_0011)).toEqual([0b1110_1011, 0b1110_1011]);
});
it('converts 0x3C (0b0011_1100) correctly', () => {
// 0011 1100 => 1011 1110, 1011 1110
expect(fourXfour(0b0011_1100)).toEqual([0b1011_1110, 0b1011_1110]);
});
});
describe('defourXfour', () => {
it('converts to 0x00 correctly', () => {
// 1010 1010, 1010 1010 => 0000 0000
expect(defourXfour(0b1010_1010, 0b1010_1010)).toEqual(0x00);
});
it('converts to 0xff correctly', () => {
// 1111 1111, 1111 1111 => 1111 1111
expect(defourXfour(0b1111_1111, 0b1111_1111)).toEqual(0xff);
});
it('converts to 0x55 correctly', () => {
// 1010 1010, 1111 1111 => 0101 0101
expect(defourXfour(0b1010_1010, 0b1111_1111)).toEqual(0x55);
});
it('converts to 0xAA correctly', () => {
// 1111 1111, 1010 1010 => 1010 1010
expect(defourXfour(0b1111_1111, 0b1010_1010)).toEqual(0xaa);
});
it('converts to 0xA5 correctly', () => {
// 1111 1010, 1010 1111 => 1010 0101
expect(defourXfour(0b1111_1010, 0b1010_1111)).toEqual(0xa5);
});
it('converts to 0x5A correctly', () => {
// 1010 1111, 1111 1010 => 0101 1010
expect(defourXfour(0b1010_1111, 0b1111_1010)).toEqual(0x5a);
});
it('converts to 0xC3 (0b1100_0011) correctly', () => {
// 1110 1011, 1110 1011 => 1100 0011
expect(defourXfour(0b1110_1011, 0b1110_1011)).toEqual(0b1100_0011);
});
it('converts to 0x3C (0b0011_1100) correctly', () => {
// 1011 1110, 1011 1110 => 0011 1100
expect(defourXfour(0b1011_1110, 0b1011_1110)).toEqual(0b0011_1100);
});
});
describe('findSector', () => {
describe('for a 16 sector DOS disk', () => {
it('correctly finds track 0, sector 0', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 0);
expect(track).toBe(0);
expect(sector).toBe(0);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 0, sector 1', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 1);
expect(track).toBe(0);
expect(sector).toBe(1);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
1 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
342 /* data 6 & 2 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
41) /* GAP3 nibbles for track 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 0, sector 2', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 2);
expect(track).toBe(0);
expect(sector).toBe(2);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
2 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
342 /* data 6 & 2 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
41) /* GAP3 nibbles for track 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 0, sector 15', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 15);
expect(track).toBe(0);
expect(sector).toBe(15);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
15 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
342 /* data 6 & 2 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
41) /* GAP3 nibbles for track 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 1, sector 0', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 1, 0);
expect(track).toBe(1);
expect(sector).toBe(0);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 1, sector 1', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 1, 1);
expect(track).toBe(1);
expect(sector).toBe(1);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
1 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
342 /* data 6 & 2 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
39) /* GAP3 nibbles for track > 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
it('correctly finds track 1, sector 15', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 1, 15);
expect(track).toBe(1);
expect(sector).toBe(15);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
15 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
342 /* data 6 & 2 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
39) /* GAP3 nibbles for track > 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(16);
});
});
describe('for a 13 sector disk', () => {
it('correctly finds track 0, sector 0 of a 13 sector disk', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_13,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 0);
expect(track).toBe(0);
expect(sector).toBe(0);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(13);
});
it('correctly finds track 0, sector 1 of a 13 sector disk', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_13,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 0, 1);
expect(track).toBe(0);
expect(sector).toBe(1);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
4 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
410 /* data 5 & 3 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
41) /* GAP3 nibbles for track 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(13);
});
it('correctly finds track 1, sector 6 of a 13 sector disk', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_13,
readOnly: true,
});
const { nibble, track, sector, sectors } = findSector(disk, 1, 6);
expect(track).toBe(1);
expect(sector).toBe(6);
expect(nibble).toBe(
128 /* GAP1 nibbles */ +
11 *
(+14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */ +
410 /* data 5 & 3 */ +
1 /* checksum nibble */ +
3 /* epilogue nibbles */ +
39) /* GAP3 nibbles for track > 0 */ +
14 /* Address Field nibbles */ +
5 /* GAP2 nibbles */ +
3 /* prologue nibbles */
);
expect(sectors).toBe(13);
});
});
});
describe('readSector', () => {
describe('for a 16 sector disk', () => {
it('correctly reads track 0, sector 0', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const data = readSector(disk, 0, 0);
expect(data).toEqual(new Uint8Array(256));
});
it('correctly reads track 0, sector 1', () => {
const disk = createDiskFromDOS({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_16,
readOnly: true,
});
const data = readSector(disk, 0, 1);
expect(data).toEqual(new Uint8Array(256).fill(DO[1]));
});
});
describe('for a 13 sector disk', () => {
it('correctly reads track 0, sector 0', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_13,
readOnly: true,
});
const data = readSector(disk, 0, 0);
expect(data).toEqual(new Uint8Array(256));
});
it('correctly reads track 0, sector 1', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_BY_SECTOR_13,
readOnly: true,
});
const data = readSector(disk, 0, 1);
expect(data).toEqual(new Uint8Array(256).fill(1));
});
it('correctly reads track 0, sector 0 bytes in order', () => {
const disk = createDiskFromDOS13({
name: 'Disk by sector',
volume: 254,
data: BYTES_IN_ORDER_13,
readOnly: true,
});
const data = readSector(disk, 0, 0);
const expected = new Uint8Array(256);
for (let i = 0; i < 256; i++) {
expected[i] = i;
}
expect(data).toEqual(expected);
});
});
});
describe('explodeSector13', () => {
it('correctly encodes all 1s', () => {
const sector = explodeSector13(256, 0, 0, new Uint8Array(256).fill(1));
expect(sector[0]).toBe(0xff);
// Address prologue
expect(sector[0x80]).toBe(0xd5);
expect(sector[0x81]).toBe(0xaa);
expect(sector[0x82]).toBe(0xb5);
// Data prologue
expect(sector[0x93]).toBe(0xd5);
expect(sector[0x94]).toBe(0xaa);
expect(sector[0x95]).toBe(0xad);
// Data
expect(sector[0x96]).toBe(0xad); // 01 special low bit of 0xFF
expect(sector[0x97]).toBe(0xb7); // C:001 D0:1 E0:1 -> 07 -> 07 ^ 01 -> 06 -> B7
expect(sector[0x98]).toBe(0xab); // G:001 H0:1 I0:1 -> 07 -> 07 ^ 07 -> 00 -> AB
expect(sector[0x99]).toBe(0xab); // J:001 K0:1 L0:1 -> 07 -> 07 ^ 07 -> 00 -> AB
for (let i = 0x9a; i <= 0x96 + 0x33; i++) {
expect(sector[i]).toBe(0xab); // same as above
}
expect(sector[0x96 + 0x34]).toBe(0xaf); // B:001 D1:0 E1:0 -> 04 ^ 07 -> 03 -> AF
expect(sector[0x96 + 0x35]).toBe(0xab); // X:001 Y1:0 Z1:0 -> 04 ^ 04 -> 00 -> AB
for (let i = 0x96 + 0x36; i <= 0x96 + 0x33 + 0x33; i++) {
expect(sector[i]).toBe(0xab); // same as above
}
// expect(sector[0x98]).toBe(0xAB); // B:001 D1:0 E1:0 -> 04 -> 04 ^ 07 -> 03 -> AF
// expect(sector[0x97]).toBe(0xB7); // A:001 D2:0 E2:0 -> 04 -> 04 ^ 02 -> 06 -> B7
});
});
describe('test', () => {
it('5-bit nibble to data offset', () => {
// const off = (i: number) => 0x33 * (i % 5) + (0x32 - Math.floor(i / 5));
const off = (i: number) =>
Math.floor(i / 0x33) + 5 * (0x32 - (i % 0x33));
expect(off(0x32)).toBe(0);
expect(off(0x31)).toBe(5);
expect(off(0x30)).toBe(10);
expect(off(0x65)).toBe(1);
expect(off(0x64)).toBe(6);
expect(off(0x63)).toBe(11);
expect(off(0x98)).toBe(2);
expect(off(0x97)).toBe(7);
expect(off(0x96)).toBe(12);
expect(off(0xcb)).toBe(3);
expect(off(0xca)).toBe(8);
expect(off(0xc9)).toBe(13);
expect(off(0xfe)).toBe(4);
expect(off(0xfd)).toBe(9);
expect(off(0xfc)).toBe(14);
const seen = new Set<number>();
for (let i = 0; i < 0xff; i++) {
seen.add(off(i));
}
for (let i = 0; i < 0xff; i++) {
expect(seen).toContain(i);
}
});
it('3-bit nibble to data offset', () => {
// const off = 0x33 * (i % 3) + (0x32 - Math.floor(i / 3));
// const off = (i: number) => Math.floor(i / 0x33) + 3 * (0x32 - (i % 0x33));
const off = (i: number) =>
Math.floor(i / 0x33) + 5 * (0x32 - (i % 0x33));
const dOff = (i: number) => 3 + 5 * (0x32 - (i % 0x33));
const eOff = (i: number) => 4 + 5 * (0x32 - (i % 0x33));
const bit = (i: number) => 2 - Math.floor(i / 0x33);
expect(off(0x32)).toBe(0);
expect(dOff(0x32)).toBe(3);
expect(eOff(0x32)).toBe(4);
expect(bit(0x32)).toBe(2);
expect(off(0x65)).toBe(1);
expect(dOff(0x65)).toBe(3);
expect(eOff(0x65)).toBe(4);
expect(bit(0x65)).toBe(1);
expect(off(0x98)).toBe(2);
expect(dOff(0x98)).toBe(3);
expect(eOff(0x98)).toBe(4);
expect(bit(0x98)).toBe(0);
expect(off(0x31)).toBe(5);
expect(dOff(0x31)).toBe(8);
expect(eOff(0x31)).toBe(9);
expect(off(0x64)).toBe(6);
expect(dOff(0x64)).toBe(8);
expect(eOff(0x64)).toBe(9);
expect(off(0x97)).toBe(7);
expect(dOff(0x97)).toBe(8);
expect(eOff(0x97)).toBe(9);
expect(off(0x30)).toBe(10);
expect(dOff(0x30)).toBe(13);
expect(eOff(0x30)).toBe(14);
const seen = new Set<number>();
for (let i = 0; i < 0x99; i++) {
seen.add(off(i));
seen.add(dOff(i));
seen.add(eOff(i));
}
for (let i = 0; i < 0xff; i++) {
expect(seen).toContain(i);
}
});
});