mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Add tests for WOZ disks (#136)
* Add a test for the dirty callback on writes This new test just checks that a clean disk becomes dirty after a write _and_ that the dirty callback is fired. * Add tests for WOZ disks The new tests verify the basic read behavior of the state sequencer on well-behaved disks, including sync bytes and so on. Write tests are still to come. There's also a change to the Woz format to return the info chunk data as well.
This commit is contained in:
parent
466a7eed78
commit
99ba052597
@ -1,4 +1,4 @@
|
|||||||
import { base64_encode} from '../base64';
|
import { base64_encode } from '../base64';
|
||||||
import type {
|
import type {
|
||||||
byte,
|
byte,
|
||||||
Card,
|
Card,
|
||||||
@ -28,7 +28,7 @@ import {
|
|||||||
createDiskFromJsonDisk
|
createDiskFromJsonDisk
|
||||||
} from '../formats/create_disk';
|
} from '../formats/create_disk';
|
||||||
|
|
||||||
import { debug, toHex } from '../util';
|
import { toHex } from '../util';
|
||||||
import { jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
|
import { jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
|
||||||
|
|
||||||
import { BOOTSTRAP_ROM_16, BOOTSTRAP_ROM_13 } from '../roms/cards/disk2';
|
import { BOOTSTRAP_ROM_16, BOOTSTRAP_ROM_13 } from '../roms/cards/disk2';
|
||||||
@ -395,12 +395,18 @@ export default class DiskII implements Card<State> {
|
|||||||
this.lastCycles = this.io.cycles();
|
this.lastCycles = this.io.cycles();
|
||||||
this.bootstrapRom = this.sectors === 16 ? BOOTSTRAP_ROM_16 : BOOTSTRAP_ROM_13;
|
this.bootstrapRom = this.sectors === 16 ? BOOTSTRAP_ROM_16 : BOOTSTRAP_ROM_13;
|
||||||
this.sequencerRom = this.sectors === 16 ? SEQUENCER_ROM_16 : SEQUENCER_ROM_13;
|
this.sequencerRom = this.sectors === 16 ? SEQUENCER_ROM_16 : SEQUENCER_ROM_13;
|
||||||
|
// From the example in UtA2e, p. 9-29, col. 1, para. 1., this is
|
||||||
|
// essentially the start of the sequencer loop and produces
|
||||||
|
// correctly synced nibbles immediately. Starting at state 0
|
||||||
|
// would introduce a spurrious 1 in the latch at the beginning,
|
||||||
|
// which requires reading several more sync bytes to sync up.
|
||||||
|
this.state = 2;
|
||||||
|
|
||||||
this.initWorker();
|
this.initWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
private debug(..._args: unknown[]) {
|
private debug(..._args: unknown[]) {
|
||||||
// debug.apply(this, arguments);
|
// debug(..._args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for WOZ disks
|
// Only used for WOZ disks
|
||||||
@ -422,7 +428,7 @@ export default class DiskII implements Card<State> {
|
|||||||
if (this.clock === 4) {
|
if (this.clock === 4) {
|
||||||
pulse = track[this.cur.head];
|
pulse = track[this.cur.head];
|
||||||
if (!pulse) {
|
if (!pulse) {
|
||||||
// More that 2 zeros can not be read reliably.
|
// More than 2 zeros can not be read reliably.
|
||||||
if (++this.zeros > 2) {
|
if (++this.zeros > 2) {
|
||||||
pulse = Math.random() >= 0.5 ? 1 : 0;
|
pulse = Math.random() >= 0.5 ? 1 : 0;
|
||||||
}
|
}
|
||||||
@ -440,9 +446,7 @@ export default class DiskII implements Card<State> {
|
|||||||
|
|
||||||
const command = this.sequencerRom[idx];
|
const command = this.sequencerRom[idx];
|
||||||
|
|
||||||
if (this.on && this.q7) {
|
this.debug(`clock: ${this.clock} state: ${toHex(this.state)} pulse: ${pulse} command: ${toHex(command)} q6: ${this.q6} latch: ${toHex(this.latch)}`);
|
||||||
debug('clock:', this.clock, 'command:', toHex(command), 'q6:', this.q6);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (command & 0xf) {
|
switch (command & 0xf) {
|
||||||
case 0x0: // CLR
|
case 0x0: // CLR
|
||||||
@ -461,11 +465,13 @@ export default class DiskII implements Card<State> {
|
|||||||
break;
|
break;
|
||||||
case 0xB: // LD
|
case 0xB: // LD
|
||||||
this.latch = this.bus;
|
this.latch = this.bus;
|
||||||
debug('Loading', toHex(this.latch), 'from bus');
|
this.debug('Loading', toHex(this.latch), 'from bus');
|
||||||
break;
|
break;
|
||||||
case 0xD: // SL1
|
case 0xD: // SL1
|
||||||
this.latch = ((this.latch << 1) | 0x01) & 0xff;
|
this.latch = ((this.latch << 1) | 0x01) & 0xff;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
this.debug(`unknown command: ${toHex(command & 0xf)}`);
|
||||||
}
|
}
|
||||||
this.state = (command >> 4 & 0xF) as nibble;
|
this.state = (command >> 4 & 0xF) as nibble;
|
||||||
|
|
||||||
@ -473,7 +479,7 @@ export default class DiskII implements Card<State> {
|
|||||||
if (this.on) {
|
if (this.on) {
|
||||||
if (this.q7) {
|
if (this.q7) {
|
||||||
track[this.cur.head] = this.state & 0x8 ? 0x01 : 0x00;
|
track[this.cur.head] = this.state & 0x8 ? 0x01 : 0x00;
|
||||||
debug('Wrote', this.state & 0x8 ? 0x01 : 0x00);
|
this.debug('Wrote', this.state & 0x8 ? 0x01 : 0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++this.cur.head >= track.length) {
|
if (++this.cur.head >= track.length) {
|
||||||
@ -544,6 +550,10 @@ export default class DiskII implements Card<State> {
|
|||||||
this.cur.phase = phase;
|
this.cur.phase = phase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The emulator clamps the track to the valid track range available
|
||||||
|
// in the image, but real Disk II drives can seek past track 34 by
|
||||||
|
// at least a half track, usually a full track. Some 3rd party
|
||||||
|
// drives can seek to track 39.
|
||||||
const maxTrack = isNibbleDrive(this.cur)
|
const maxTrack = isNibbleDrive(this.cur)
|
||||||
? this.cur.tracks.length * 4 - 1
|
? this.cur.tracks.length * 4 - 1
|
||||||
: this.cur.trackMap.length - 1;
|
: this.cur.trackMap.length - 1;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { byte, memory, MemberOf, word } from '../types';
|
import type { byte, memory, MemberOf, word } from '../types';
|
||||||
import type { GamepadConfiguration } from '../ui/types';
|
import type { GamepadConfiguration } from '../ui/types';
|
||||||
|
import { InfoChunk } from './woz';
|
||||||
|
|
||||||
export const DRIVE_NUMBERS = [1, 2] as const;
|
export const DRIVE_NUMBERS = [1, 2] as const;
|
||||||
export type DriveNumber = MemberOf<typeof DRIVE_NUMBERS>;
|
export type DriveNumber = MemberOf<typeof DRIVE_NUMBERS>;
|
||||||
@ -70,6 +71,7 @@ export interface WozDisk extends FloppyDisk {
|
|||||||
encoding: typeof ENCODING_BITSTREAM;
|
encoding: typeof ENCODING_BITSTREAM;
|
||||||
trackMap: number[];
|
trackMap: number[];
|
||||||
rawTracks: Uint8Array[];
|
rawTracks: Uint8Array[];
|
||||||
|
info: InfoChunk | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockDisk extends Disk {
|
export interface BlockDisk extends Disk {
|
||||||
|
@ -155,6 +155,9 @@ export class TrksChunk2 extends TrksChunk {
|
|||||||
const end = start + trk.blockCount * 512;
|
const end = start + trk.blockCount * 512;
|
||||||
const slice = bits.slice(start, end);
|
const slice = bits.slice(start, end);
|
||||||
const trackData = new Uint8Array(slice);
|
const trackData = new Uint8Array(slice);
|
||||||
|
if (trackNo === 0) {
|
||||||
|
// debug(`First bytes: ${toHex(trackData[0])} ${toHex(trackData[1])} ${toHex(trackData[2])} ${toHex(trackData[3])}`);
|
||||||
|
}
|
||||||
for (let jdx = 0; jdx < trk.bitCount; jdx++) {
|
for (let jdx = 0; jdx < trk.bitCount; jdx++) {
|
||||||
const byteIndex = jdx >> 3;
|
const byteIndex = jdx >> 3;
|
||||||
const bitIndex = 7 - (jdx & 0x07);
|
const bitIndex = 7 - (jdx & 0x07);
|
||||||
@ -284,9 +287,9 @@ export default function createDiskFromWoz(options: DiskOptions): WozDisk {
|
|||||||
debug('Invalid woz header');
|
debug('Invalid woz header');
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(chunks);
|
// debug(chunks);
|
||||||
|
|
||||||
const { meta, tmap, trks } = chunks;
|
const { meta, tmap, trks, info } = chunks;
|
||||||
|
|
||||||
const disk: WozDisk = {
|
const disk: WozDisk = {
|
||||||
encoding: ENCODING_BITSTREAM,
|
encoding: ENCODING_BITSTREAM,
|
||||||
@ -296,6 +299,7 @@ export default function createDiskFromWoz(options: DiskOptions): WozDisk {
|
|||||||
readOnly: true, //chunks.info.writeProtected === 1;
|
readOnly: true, //chunks.info.writeProtected === 1;
|
||||||
name: meta?.values['title'] || options.name,
|
name: meta?.values['title'] || options.name,
|
||||||
side: meta?.values['side_name'] || meta?.values['side'],
|
side: meta?.values['side_name'] || meta?.values['side'],
|
||||||
|
info
|
||||||
};
|
};
|
||||||
|
|
||||||
return disk;
|
return disk;
|
||||||
|
BIN
test/js/cards/data/DOS 3.3 System Master.woz
Normal file
BIN
test/js/cards/data/DOS 3.3 System Master.woz
Normal file
Binary file not shown.
@ -1,7 +1,10 @@
|
|||||||
/** @jest-environment jsdom */
|
/** @jest-environment jsdom */
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
import Apple2IO from 'js/apple2io';
|
import Apple2IO from 'js/apple2io';
|
||||||
import DiskII, { Callbacks } from 'js/cards/disk2';
|
import DiskII, { Callbacks } from 'js/cards/disk2';
|
||||||
import CPU6502 from 'js/cpu6502';
|
import CPU6502 from 'js/cpu6502';
|
||||||
|
import { byte } from 'js/types';
|
||||||
import { VideoModes } from 'js/videomodes';
|
import { VideoModes } from 'js/videomodes';
|
||||||
import { mocked } from 'ts-jest/utils';
|
import { mocked } from 'ts-jest/utils';
|
||||||
import { BYTES_BY_SECTOR_IMAGE, BYTES_BY_TRACK_IMAGE } from '../formats/testdata/16sector';
|
import { BYTES_BY_SECTOR_IMAGE, BYTES_BY_TRACK_IMAGE } from '../formats/testdata/16sector';
|
||||||
@ -248,6 +251,7 @@ describe('DiskII', () => {
|
|||||||
diskII.ioSwitch(0x83); // coil 1 on
|
diskII.ioSwitch(0x83); // coil 1 on
|
||||||
diskII.ioSwitch(0x80); // coil 0 off
|
diskII.ioSwitch(0x80); // coil 0 off
|
||||||
diskII.ioSwitch(0x85); // coil 2 on
|
diskII.ioSwitch(0x85); // coil 2 on
|
||||||
|
diskII.ioSwitch(0x82); // coil 1 off
|
||||||
diskII.ioSwitch(0x87); // coil 3 on
|
diskII.ioSwitch(0x87); // coil 3 on
|
||||||
diskII.ioSwitch(0x84); // coil 2 off
|
diskII.ioSwitch(0x84); // coil 2 off
|
||||||
diskII.ioSwitch(0x81); // coil 0 on
|
diskII.ioSwitch(0x81); // coil 0 on
|
||||||
@ -499,5 +503,196 @@ describe('DiskII', () => {
|
|||||||
expect(track0[0]).toBe(0x80);
|
expect(track0[0]).toBe(0x80);
|
||||||
expect(track0[1]).toBe(0x81);
|
expect(track0[1]).toBe(0x81);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets disk state to dirty and calls the dirty callback when written', () => {
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'BYTES_BY_TRACK', 'po', BYTES_BY_TRACK_IMAGE);
|
||||||
|
let state = diskII.getState();
|
||||||
|
state.drives[0].dirty = false;
|
||||||
|
diskII.setState(state);
|
||||||
|
jest.resetAllMocks();
|
||||||
|
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
diskII.ioSwitch(0x8F, 0x80); // write
|
||||||
|
diskII.ioSwitch(0x8C); // shift
|
||||||
|
|
||||||
|
expect(callbacks.dirty).toHaveBeenCalledTimes(1);
|
||||||
|
expect(callbacks.dirty).toHaveBeenCalledWith(1, true);
|
||||||
|
|
||||||
|
state = diskII.getState();
|
||||||
|
expect(state.drives[0].dirty).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reading WOZ-based disks', () => {
|
||||||
|
const DOS33_SYSTEM_MASTER_IMAGE =
|
||||||
|
fs.readFileSync('test/js/cards/data/DOS 3.3 System Master.woz').buffer;
|
||||||
|
|
||||||
|
it('accepts WOZ-based disks', () => {
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops the head at the end of the image', () => {
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
setTrack(diskII, 33);
|
||||||
|
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
diskII.ioSwitch(0x85); // coil 2 on
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
diskII.ioSwitch(0x87); // coil 3 on
|
||||||
|
diskII.ioSwitch(0x84); // coil 2 off
|
||||||
|
diskII.ioSwitch(0x81); // coil 0 on
|
||||||
|
diskII.ioSwitch(0x86); // coil 3 off
|
||||||
|
diskII.ioSwitch(0x83); // coil 1 on
|
||||||
|
diskII.ioSwitch(0x80); // coil 0 off
|
||||||
|
diskII.ioSwitch(0x85); // coil 2 on
|
||||||
|
diskII.ioSwitch(0x82); // coil 1 off
|
||||||
|
}
|
||||||
|
diskII.ioSwitch(0x84); // coil 2 off
|
||||||
|
|
||||||
|
const state = diskII.getState();
|
||||||
|
expect(state.drives[0].phase).toBe(2);
|
||||||
|
// For WOZ images, the number of tracks is the number in the image.
|
||||||
|
// The DOS3.3 System Master was imaged on a 40 track drive, so it
|
||||||
|
// has data for all 40 tracks, even though the last few are garbage.
|
||||||
|
expect(state.drives[0].track).toBe(40 * STEPS_PER_TRACK - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('spins the disk when motor is on', () => {
|
||||||
|
let cycles: number = 0;
|
||||||
|
mocked(mockApple2IO).cycles.mockImplementation(() => cycles);
|
||||||
|
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
let state = diskII.getState();
|
||||||
|
expect(state.drives[0].head).toBe(0);
|
||||||
|
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
cycles += 10;
|
||||||
|
diskII.tick();
|
||||||
|
|
||||||
|
state = diskII.getState();
|
||||||
|
expect(state.drives[0].head).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not spin the disk when motor is off', () => {
|
||||||
|
let cycles: number = 0;
|
||||||
|
mocked(mockApple2IO).cycles.mockImplementation(() => cycles);
|
||||||
|
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
let state = diskII.getState();
|
||||||
|
expect(state.drives[0].head).toBe(0);
|
||||||
|
|
||||||
|
cycles += 10;
|
||||||
|
diskII.tick();
|
||||||
|
|
||||||
|
state = diskII.getState();
|
||||||
|
expect(state.drives[0].head).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads an FF sync byte from the beginning of the image', () => {
|
||||||
|
let cycles: number = 0;
|
||||||
|
mocked(mockApple2IO).cycles.mockImplementation(() => cycles);
|
||||||
|
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
diskII.ioSwitch(0x8e); // read mode
|
||||||
|
|
||||||
|
// The initial bytes in the image are: FF 3F CF F3
|
||||||
|
// making the bit stream:
|
||||||
|
//
|
||||||
|
// 1111 1111 0011 1111 1100 1111 1111 0011
|
||||||
|
//
|
||||||
|
// That's three FF sync bytes in a row. Assuming
|
||||||
|
// the sequencer is in state 2, each sync byte takes
|
||||||
|
// 32 clock cycles to read, is held for 8 clock
|
||||||
|
// cycles while the extra zeros are shifted in, then
|
||||||
|
// is held 8 more clock cycles while the sequencer
|
||||||
|
// reads the next two bits.
|
||||||
|
cycles += 40; // shift 10 bits
|
||||||
|
const nibble = diskII.ioSwitch(0x8c); // read data
|
||||||
|
expect(nibble).toBe(0xFF);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads several FF sync bytes', () => {
|
||||||
|
let cycles: number = 0;
|
||||||
|
mocked(mockApple2IO).cycles.mockImplementation(() => cycles);
|
||||||
|
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
diskII.ioSwitch(0x8e); // read mode
|
||||||
|
|
||||||
|
// The initial bytes in the image are: FF 3F CF F3
|
||||||
|
// making the bit stream:
|
||||||
|
//
|
||||||
|
// 1111 1111 0011 1111 1100 1111 1111 0011
|
||||||
|
//
|
||||||
|
// That's three FF sync bytes in a row. Assuming
|
||||||
|
// the sequencer is in state 2, each sync byte takes
|
||||||
|
// 32 clock cycles to read, is held for 8 clock
|
||||||
|
// cycles while the extra zeros are shifted in, then
|
||||||
|
// is held 8 more clock cycles while the sequencer
|
||||||
|
// reads the next two bits. This means that 3 sync
|
||||||
|
// bytes will be available for 3 * 40 + 8 cycles.
|
||||||
|
for (let i = 0; i < 3 * 40 + 8; i++) {
|
||||||
|
cycles++;
|
||||||
|
const nibble = diskII.ioSwitch(0x8c); // read data
|
||||||
|
if (nibble & 0x80) {
|
||||||
|
// Nibbles are only valid when the high bit is set.
|
||||||
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
|
expect(nibble).toBe(0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads random garbage on uninitialized tracks', () => {
|
||||||
|
let cycles: number = 0;
|
||||||
|
mocked(mockApple2IO).cycles.mockImplementation(() => cycles);
|
||||||
|
|
||||||
|
const diskII = new DiskII(mockApple2IO, callbacks);
|
||||||
|
diskII.setBinary(1, 'DOS 3.3 System Master', 'woz', DOS33_SYSTEM_MASTER_IMAGE);
|
||||||
|
|
||||||
|
// Step to track 0.5
|
||||||
|
diskII.ioSwitch(0x89); // turn on the motor
|
||||||
|
diskII.ioSwitch(0x81); // coil 0 on
|
||||||
|
diskII.ioSwitch(0x83); // coil 1 on
|
||||||
|
diskII.ioSwitch(0x80); // coil 0 off
|
||||||
|
diskII.ioSwitch(0x82); // coil 1 off
|
||||||
|
diskII.ioSwitch(0x8e); // read mode
|
||||||
|
|
||||||
|
// Read 5 nibbles
|
||||||
|
const nibbles: byte[] = [];
|
||||||
|
let read = false;
|
||||||
|
while (nibbles.length < 5) {
|
||||||
|
cycles++;
|
||||||
|
const nibble = diskII.ioSwitch(0x8c); // read data
|
||||||
|
const qa = nibble & 0x80;
|
||||||
|
if (qa && !read) {
|
||||||
|
nibbles.push(nibble);
|
||||||
|
read = true;
|
||||||
|
}
|
||||||
|
if (!qa && read) {
|
||||||
|
read = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Test that the first doesn't equal any of the others.
|
||||||
|
// (Yes, this test could fail with some bad luck.)
|
||||||
|
let equal = false;
|
||||||
|
for (let i = 1; i < 5; i++) {
|
||||||
|
equal ||= nibbles[0] === nibbles[i];
|
||||||
|
}
|
||||||
|
expect(equal).not.toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,7 @@ describe('woz', () => {
|
|||||||
rawData: mockWoz1
|
rawData: mockWoz1
|
||||||
};
|
};
|
||||||
|
|
||||||
const disk = createDiskFromWoz(options) ;
|
const disk = createDiskFromWoz(options);
|
||||||
expect(disk).toEqual({
|
expect(disk).toEqual({
|
||||||
name: 'Mock Woz 1',
|
name: 'Mock Woz 1',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -31,8 +31,6 @@ describe('woz', () => {
|
|||||||
1, 0, 0, 1, 0, 1, 1, 0,
|
1, 0, 0, 1, 0, 1, 1, 0,
|
||||||
])],
|
])],
|
||||||
tracks: [new Uint8Array([0xD5, 0xAA, 0x96])],
|
tracks: [new Uint8Array([0xD5, 0xAA, 0x96])],
|
||||||
});
|
|
||||||
expect(console.log).toHaveBeenCalledWith(expect.objectContaining({
|
|
||||||
info: {
|
info: {
|
||||||
bitTiming: 0,
|
bitTiming: 0,
|
||||||
bootSector: 0,
|
bootSector: 0,
|
||||||
@ -47,7 +45,7 @@ describe('woz', () => {
|
|||||||
version: 1,
|
version: 1,
|
||||||
writeProtected: 0
|
writeProtected: 0
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can parse Woz version 2', () => {
|
it('can parse Woz version 2', () => {
|
||||||
@ -58,7 +56,7 @@ describe('woz', () => {
|
|||||||
rawData: mockWoz2
|
rawData: mockWoz2
|
||||||
};
|
};
|
||||||
|
|
||||||
const disk = createDiskFromWoz(options) ;
|
const disk = createDiskFromWoz(options);
|
||||||
expect(disk).toEqual({
|
expect(disk).toEqual({
|
||||||
name: 'Mock Woz 2',
|
name: 'Mock Woz 2',
|
||||||
side: 'B',
|
side: 'B',
|
||||||
@ -71,8 +69,6 @@ describe('woz', () => {
|
|||||||
1, 0, 0, 1, 0, 1, 1, 0,
|
1, 0, 0, 1, 0, 1, 1, 0,
|
||||||
])],
|
])],
|
||||||
tracks: [new Uint8Array([0xD5, 0xAA, 0x96])],
|
tracks: [new Uint8Array([0xD5, 0xAA, 0x96])],
|
||||||
});
|
|
||||||
expect(console.log).toHaveBeenCalledWith(expect.objectContaining({
|
|
||||||
info: {
|
info: {
|
||||||
bitTiming: 0,
|
bitTiming: 0,
|
||||||
bootSector: 0,
|
bootSector: 0,
|
||||||
@ -87,6 +83,6 @@ describe('woz', () => {
|
|||||||
version: 2,
|
version: 2,
|
||||||
writeProtected: 0
|
writeProtected: 0
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user