2015-06-01 09:35:51 +00:00
|
|
|
package com.bytezone.diskbrowser.disk;
|
|
|
|
|
|
|
|
import java.awt.AWTEventMulticaster;
|
|
|
|
import java.awt.event.ActionEvent;
|
|
|
|
import java.awt.event.ActionListener;
|
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.zip.CRC32;
|
|
|
|
import java.util.zip.Checksum;
|
|
|
|
|
2016-02-24 02:13:52 +00:00
|
|
|
import com.bytezone.common.Utility;
|
2015-06-01 09:35:51 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
2016-02-24 21:11:14 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.FileFormatException;
|
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
public class AppleDisk implements Disk
|
|
|
|
{
|
2016-08-08 04:53:29 +00:00
|
|
|
private static final int MAX_INTERLEAVE = 3;
|
|
|
|
private static final int SECTOR_SIZE = 256;
|
|
|
|
private static final int BLOCK_SIZE = 512;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
public final File file;
|
2015-12-17 23:00:00 +00:00
|
|
|
private final byte[] diskBuffer; // contains the disk contents in memory
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2015-12-17 23:00:00 +00:00
|
|
|
private final int tracks; // usually 35 for floppy disks
|
|
|
|
private int sectors; // 8 or 16
|
|
|
|
private int blocks; // 280 or 560
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2015-12-17 23:00:00 +00:00
|
|
|
private final int trackSize; // 4096
|
|
|
|
public int sectorSize; // 256 or 512
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
private int interleave = 0;
|
|
|
|
private static int[][] interleaveSector = //
|
2017-03-17 05:07:10 +00:00
|
|
|
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, // None
|
|
|
|
{ 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal
|
2016-07-31 02:14:48 +00:00
|
|
|
{ 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom
|
|
|
|
{ 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2016-07-31 02:14:48 +00:00
|
|
|
// Physical disk interleave:
|
2015-12-21 21:04:43 +00:00
|
|
|
// Info from http://www.applelogic.org/TheAppleIIEGettingStarted.html
|
|
|
|
// Block: 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
|
|
// Position: 0 8 1 9 2 A 3 B 4 C 5 D 6 E 7 F - Prodos (.PO disks)
|
|
|
|
// Position: 0 7 E 6 D 5 C 4 B 3 A 2 9 1 8 F - Dos (.DO disks)
|
|
|
|
|
2016-03-29 09:29:00 +00:00
|
|
|
// https://github.com/AppleWin/AppleWin/blob/master/source/DiskImageHelper.cpp
|
|
|
|
// DO logical order 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
|
|
|
// physical order 0 D B 9 7 5 3 1 E C A 8 6 4 2 F */
|
|
|
|
|
|
|
|
// PO logical order 0 E D C B A 9 8 7 6 5 4 3 2 1 F */
|
|
|
|
// physical order 0 2 4 6 8 A C E 1 3 5 7 9 B D F */
|
|
|
|
|
|
|
|
//BYTE CImageBase::ms_SectorNumber[NUM_SECTOR_ORDERS][0x10] =
|
|
|
|
//{
|
|
|
|
// {0x00,0x08,0x01,0x09,0x02,0x0A,0x03,0x0B, 0x04,0x0C,0x05,0x0D,0x06,0x0E,0x07,0x0F},
|
|
|
|
// {0x00,0x07,0x0E,0x06,0x0D,0x05,0x0C,0x04, 0x0B,0x03,0x0A,0x02,0x09,0x01,0x08,0x0F},
|
|
|
|
// {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
|
|
|
|
//};
|
|
|
|
|
2016-07-31 02:14:48 +00:00
|
|
|
// * TABLE OF PHYSICAL BSECTR NUMBERS
|
|
|
|
// * WHICH CORRESPOND TO THE LOGICAL
|
|
|
|
// * BSECTRS 0-F ON TRACK ZERO...
|
|
|
|
// BHERE2 EQU >*
|
|
|
|
// TABLE EQU $800+BHERE2
|
|
|
|
// DFB $00,13,11 ;00->00,01->13,02->11
|
|
|
|
// DFB 09,07,05 ;03->09,04->07;05->05
|
|
|
|
// DFB 03,01,14 ;06->03,07->01,08->14
|
|
|
|
// DFB 12,10,08 ;09->12,10->10,11->08
|
|
|
|
// DFB 06,04,02,15 ;12->06,13->04,14->02,15->15
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private boolean[] hasData;
|
2016-02-25 07:45:24 +00:00
|
|
|
private byte emptyByte = 0;
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private ActionListener actionListenerList;
|
|
|
|
private List<DiskAddress> blockList;
|
|
|
|
|
2016-02-24 02:13:52 +00:00
|
|
|
private final boolean debug = false;
|
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
public AppleDisk (File file, int tracks, int sectors) throws FileFormatException
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
assert (file.exists ()) : "No such path :" + file.getAbsolutePath ();
|
|
|
|
assert (!file.isDirectory ()) : "File is directory :" + file.getAbsolutePath ();
|
|
|
|
assert (file.length () <= Integer.MAX_VALUE) : "File too large";
|
|
|
|
assert (file.length () != 0) : "File empty";
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
String name = file.getName ();
|
2015-06-01 09:35:51 +00:00
|
|
|
int pos = name.lastIndexOf ('.');
|
2016-02-24 02:13:52 +00:00
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
String suffix = pos > 0 ? name.substring (pos + 1) : "";
|
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
byte[] buffer = getPrefix (file); // HDV could be a 2mg
|
2016-02-24 02:13:52 +00:00
|
|
|
String prefix = new String (buffer, 0, 4);
|
2016-02-25 01:27:22 +00:00
|
|
|
int skip = 0;
|
2016-02-24 02:13:52 +00:00
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
if (suffix.equalsIgnoreCase ("2mg") || "2IMG".equals (prefix))
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-02-24 02:13:52 +00:00
|
|
|
if (debug)
|
|
|
|
System.out.println (Utility.toHex (buffer));
|
|
|
|
|
|
|
|
// http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
|
|
|
|
if ("2IMG".equals (prefix))
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
String creator = new String (buffer, 4, 4);
|
|
|
|
System.out.printf ("Prefix : %s%n", prefix);
|
|
|
|
System.out.printf ("Creator : %s%n", creator);
|
|
|
|
int headerSize = Utility.getWord (buffer, 8);
|
|
|
|
System.out.printf ("Header : %d%n", headerSize);
|
|
|
|
int version = Utility.getWord (buffer, 10);
|
|
|
|
System.out.printf ("Version : %d%n", version);
|
|
|
|
System.out.printf ("Format : %02X%n", buffer[12]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int diskData = Utility.getLong (buffer, 28);
|
|
|
|
blocks = HexFormatter.intValue (buffer[20], buffer[21]); // 1600
|
2016-11-28 00:45:17 +00:00
|
|
|
|
2016-02-24 02:13:52 +00:00
|
|
|
if (debug)
|
2016-11-28 00:45:17 +00:00
|
|
|
{
|
|
|
|
System.out.printf ("Data size : %08X (%,d)%n", diskData, diskData);
|
2016-02-24 02:13:52 +00:00
|
|
|
System.out.printf ("Blocks : %,d%n", blocks);
|
2016-11-28 00:45:17 +00:00
|
|
|
}
|
2016-02-24 02:13:52 +00:00
|
|
|
|
2017-03-17 05:07:10 +00:00
|
|
|
if (diskData > 0)
|
|
|
|
this.blocks = diskData / 4096 * 8; // reduce blocks to a multiple of 8
|
|
|
|
|
|
|
|
// see /Asimov disks/images/gs/os/prodos16/ProDOS 16v1_3.2mg
|
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
if (debug)
|
|
|
|
System.out.printf ("Blocks : %,d%n", blocks);
|
2016-02-24 02:13:52 +00:00
|
|
|
|
|
|
|
this.sectorSize = 512;
|
|
|
|
this.trackSize = 8 * sectorSize;
|
|
|
|
skip = Utility.getWord (buffer, 8);
|
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
tracks = blocks / 8; // change parameter!
|
|
|
|
sectors = 8; // change parameter!
|
2016-02-24 02:13:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
System.out.println ("Not a 2mg file");
|
2016-11-28 00:45:17 +00:00
|
|
|
this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8
|
2016-02-24 02:13:52 +00:00
|
|
|
this.sectorSize = 512;
|
|
|
|
this.trackSize = sectors * sectorSize;
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
2016-08-08 04:53:29 +00:00
|
|
|
else if (suffix.equalsIgnoreCase ("HDV"))
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8
|
2015-06-01 09:35:51 +00:00
|
|
|
this.sectorSize = 512;
|
|
|
|
this.trackSize = sectors * sectorSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4
|
2016-08-08 04:53:29 +00:00
|
|
|
{
|
|
|
|
this.blocks = tracks * sectors;
|
|
|
|
this.sectorSize = 512;
|
|
|
|
this.trackSize = sectors * sectorSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.blocks = tracks * sectors;
|
2016-11-28 00:45:17 +00:00
|
|
|
this.sectorSize = (int) file.length () / blocks;
|
2016-08-08 04:53:29 +00:00
|
|
|
this.trackSize = sectors * sectorSize;
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sectorSize != 256 && sectorSize != 512)
|
|
|
|
throw new FileFormatException ("Invalid sector size : " + sectorSize);
|
|
|
|
|
2016-11-28 00:45:17 +00:00
|
|
|
this.file = file;
|
2015-06-01 09:35:51 +00:00
|
|
|
this.tracks = tracks;
|
|
|
|
this.sectors = sectors;
|
|
|
|
|
|
|
|
diskBuffer = new byte[tracks * sectors * sectorSize];
|
|
|
|
hasData = new boolean[blocks];
|
|
|
|
|
2016-02-24 02:13:52 +00:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
System.out.printf ("DiskBuffer size : %,d%n", diskBuffer.length);
|
|
|
|
System.out.printf ("Skip size : %,d%n", skip);
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
try
|
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
BufferedInputStream in = new BufferedInputStream (new FileInputStream (file));
|
2015-06-01 09:35:51 +00:00
|
|
|
if (skip > 0)
|
2018-04-25 20:41:03 +00:00
|
|
|
in.skip (skip);
|
2016-11-28 00:45:17 +00:00
|
|
|
in.read (diskBuffer);
|
|
|
|
in.close ();
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
e.printStackTrace ();
|
|
|
|
System.exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
checkSectorsForData ();
|
|
|
|
}
|
|
|
|
|
2016-11-28 06:26:26 +00:00
|
|
|
public AppleDisk (V2dDisk disk)
|
|
|
|
{
|
2016-11-29 21:27:44 +00:00
|
|
|
tracks = 35;
|
2016-11-28 06:26:26 +00:00
|
|
|
trackSize = 4096;
|
2016-11-29 21:27:44 +00:00
|
|
|
file = disk.file;
|
2016-12-03 08:20:01 +00:00
|
|
|
diskBuffer = disk.diskBuffer;
|
2016-11-29 21:27:44 +00:00
|
|
|
|
2016-11-28 06:26:26 +00:00
|
|
|
sectorSize = 256;
|
|
|
|
sectors = 16;
|
|
|
|
blocks = tracks * sectors;
|
|
|
|
hasData = new boolean[blocks];
|
|
|
|
|
|
|
|
checkSectorsForData ();
|
|
|
|
}
|
|
|
|
|
2016-11-29 21:27:44 +00:00
|
|
|
public AppleDisk (NibDisk disk) // not used yet
|
|
|
|
{
|
|
|
|
tracks = 35;
|
|
|
|
trackSize = 4096;
|
|
|
|
file = disk.file;
|
|
|
|
diskBuffer = disk.buffer;
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
private byte[] getPrefix (File path)
|
|
|
|
{
|
|
|
|
byte[] buffer = new byte[64];
|
|
|
|
try
|
|
|
|
{
|
|
|
|
BufferedInputStream file = new BufferedInputStream (new FileInputStream (path));
|
|
|
|
file.read (buffer);
|
|
|
|
file.close ();
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
e.printStackTrace ();
|
|
|
|
System.exit (1);
|
|
|
|
}
|
2016-02-24 02:13:52 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkSectorsForData ()
|
|
|
|
{
|
2016-08-08 04:53:29 +00:00
|
|
|
if (true)
|
|
|
|
{
|
|
|
|
checkSectorsFaster ();
|
|
|
|
return;
|
|
|
|
}
|
2016-02-24 02:13:52 +00:00
|
|
|
// force blockList to be rebuilt with the correct number/size of blocks
|
|
|
|
blockList = null;
|
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
for (DiskAddress da : this) // uses blockList.iterator
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
byte[] buffer = readSector (da);
|
|
|
|
hasData[da.getBlock ()] = false;
|
|
|
|
for (int i = 0; i < sectorSize; i++)
|
2016-02-25 07:45:24 +00:00
|
|
|
if (buffer[i] != emptyByte)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
hasData[da.getBlock ()] = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
private void checkSectorsFaster ()
|
|
|
|
{
|
|
|
|
// force blockList to be rebuilt with the correct number/size of blocks
|
|
|
|
blockList = null;
|
|
|
|
|
|
|
|
for (DiskAddress da : this) // uses blockList.iterator
|
|
|
|
{
|
|
|
|
if (sectorSize == SECTOR_SIZE)
|
|
|
|
{
|
|
|
|
int diskOffset = getBufferOffset (da);
|
|
|
|
hasData[da.getBlock ()] = check (diskOffset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int diskOffset1 = getBufferOffset (da, 0);
|
|
|
|
int diskOffset2 = getBufferOffset (da, 1);
|
|
|
|
hasData[da.getBlock ()] = check (diskOffset1) || check (diskOffset2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean check (int diskOffset)
|
|
|
|
{
|
|
|
|
for (int i = diskOffset, max = diskOffset + SECTOR_SIZE; i < max; i++)
|
|
|
|
if (diskBuffer[i] != emptyByte)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
/*
|
|
|
|
* Routines that implement the Disk interface
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getSectorsPerTrack ()
|
|
|
|
{
|
|
|
|
return trackSize / sectorSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getTrackSize ()
|
|
|
|
{
|
|
|
|
return trackSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getBlockSize ()
|
|
|
|
{
|
|
|
|
return sectorSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getTotalBlocks ()
|
|
|
|
{
|
|
|
|
return blocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getTotalTracks ()
|
|
|
|
{
|
|
|
|
return tracks;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isSectorEmpty (DiskAddress da)
|
|
|
|
{
|
|
|
|
return !hasData[da.getBlock ()];
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isSectorEmpty (int block)
|
|
|
|
{
|
|
|
|
return !hasData[block];
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isSectorEmpty (int track, int sector)
|
|
|
|
{
|
|
|
|
return !hasData[getDiskAddress (track, sector).getBlock ()];
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public File getFile ()
|
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
return file;
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] readSector (DiskAddress da)
|
|
|
|
{
|
|
|
|
byte[] buffer = new byte[sectorSize];
|
|
|
|
readBuffer (da, buffer, 0);
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] readSectors (List<DiskAddress> daList)
|
|
|
|
{
|
|
|
|
byte[] buffer = new byte[daList.size () * sectorSize];
|
|
|
|
int bufferOffset = 0;
|
|
|
|
for (DiskAddress da : daList)
|
|
|
|
{
|
2016-08-08 10:34:25 +00:00
|
|
|
if (da != null) // sparse text files may have gaps
|
2015-06-01 09:35:51 +00:00
|
|
|
readBuffer (da, buffer, bufferOffset);
|
|
|
|
bufferOffset += sectorSize;
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] readSector (int track, int sector)
|
|
|
|
{
|
|
|
|
return readSector (getDiskAddress (track, sector));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] readSector (int block)
|
|
|
|
{
|
|
|
|
return readSector (getDiskAddress (block));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-08-09 04:15:44 +00:00
|
|
|
public void writeSector (DiskAddress da, byte[] buffer)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-08-09 04:15:44 +00:00
|
|
|
writeBuffer (da, buffer);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setInterleave (int interleave)
|
|
|
|
{
|
|
|
|
assert (interleave >= 0 && interleave <= MAX_INTERLEAVE) : "Invalid interleave";
|
|
|
|
this.interleave = interleave;
|
|
|
|
checkSectorsForData ();
|
|
|
|
if (actionListenerList != null)
|
|
|
|
notifyListeners ("Interleave changed");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getInterleave ()
|
|
|
|
{
|
|
|
|
return interleave;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setBlockSize (int size)
|
|
|
|
{
|
2016-08-08 04:53:29 +00:00
|
|
|
assert (size == SECTOR_SIZE || size == BLOCK_SIZE) : "Invalid sector size : " + size;
|
2015-06-01 09:35:51 +00:00
|
|
|
if (sectorSize == size)
|
|
|
|
return;
|
2016-02-28 18:53:59 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
sectorSize = size;
|
|
|
|
sectors = trackSize / sectorSize;
|
|
|
|
blocks = tracks * sectors;
|
2016-02-28 18:53:59 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
hasData = new boolean[blocks];
|
|
|
|
checkSectorsForData ();
|
2016-02-28 18:53:59 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
if (actionListenerList != null)
|
|
|
|
notifyListeners ("Sector size changed");
|
|
|
|
}
|
|
|
|
|
2016-12-11 21:32:18 +00:00
|
|
|
@Override
|
|
|
|
public DiskAddress getDiskAddress (int track, int sector)
|
|
|
|
{
|
|
|
|
if (!isValidAddress (track, sector))
|
|
|
|
{
|
|
|
|
System.out.println ("Invalid block : " + track + "/" + sector);
|
|
|
|
return null;
|
|
|
|
// return new AppleDiskAddress (this, 0); this was looping 26/07/2016
|
|
|
|
}
|
|
|
|
return new AppleDiskAddress (this, track, sector);
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
@Override
|
|
|
|
public DiskAddress getDiskAddress (int block)
|
|
|
|
{
|
|
|
|
if (!isValidAddress (block))
|
|
|
|
{
|
2017-03-17 05:07:10 +00:00
|
|
|
System.out.printf ("Invalid block : %d of %d%n", block, this.blocks);
|
2016-07-29 12:28:11 +00:00
|
|
|
return null;
|
|
|
|
// return new AppleDiskAddress (this, 0); this was looping 26/07/2016
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
2016-07-17 22:35:18 +00:00
|
|
|
return new AppleDiskAddress (this, block);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<DiskAddress> getDiskAddressList (int... blocks)
|
|
|
|
{
|
|
|
|
List<DiskAddress> addressList = new ArrayList<DiskAddress> ();
|
|
|
|
|
|
|
|
for (int block : blocks)
|
|
|
|
{
|
|
|
|
assert (isValidAddress (block)) : "Invalid block : " + block;
|
2016-07-17 22:35:18 +00:00
|
|
|
addressList.add (new AppleDiskAddress (this, block));
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
return addressList;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isValidAddress (int block)
|
|
|
|
{
|
|
|
|
if (block < 0 || block >= this.blocks)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isValidAddress (int track, int sector)
|
|
|
|
{
|
|
|
|
if (track < 0 || track >= this.tracks)
|
|
|
|
return false;
|
|
|
|
if (sector < 0 || sector >= this.sectors)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isValidAddress (DiskAddress da)
|
|
|
|
{
|
|
|
|
return isValidAddress (da.getTrack (), da.getSector ());
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the only method that transfers data from the disk buffer to an output buffer.
|
|
|
|
* It handles sectors of 256 or 512 bytes, and both linear and interleaved sectors.
|
|
|
|
*/
|
|
|
|
private void readBuffer (DiskAddress da, byte[] buffer, int bufferOffset)
|
|
|
|
{
|
|
|
|
assert da.getDisk () == this : "Disk address not applicable to this disk";
|
2016-08-08 04:53:29 +00:00
|
|
|
assert sectorSize == SECTOR_SIZE
|
|
|
|
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
|
2015-06-01 09:35:51 +00:00
|
|
|
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
|
2016-07-31 02:14:48 +00:00
|
|
|
+ interleave;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
if (sectorSize == SECTOR_SIZE)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-08-08 04:53:29 +00:00
|
|
|
int diskOffset = getBufferOffset (da);
|
|
|
|
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
2016-08-08 04:53:29 +00:00
|
|
|
else
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2016-08-08 04:53:29 +00:00
|
|
|
int diskOffset = getBufferOffset (da, 0);
|
|
|
|
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE);
|
2016-02-25 01:27:22 +00:00
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
diskOffset = getBufferOffset (da, 1);
|
|
|
|
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE,
|
|
|
|
SECTOR_SIZE);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-09 04:15:44 +00:00
|
|
|
private void writeBuffer (DiskAddress da, byte[] buffer)
|
2016-08-08 10:34:25 +00:00
|
|
|
{
|
|
|
|
assert da.getDisk () == this : "Disk address not applicable to this disk";
|
|
|
|
assert sectorSize == SECTOR_SIZE
|
|
|
|
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
|
|
|
|
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
|
|
|
|
+ interleave;
|
|
|
|
|
|
|
|
if (sectorSize == SECTOR_SIZE)
|
|
|
|
{
|
|
|
|
int diskOffset = getBufferOffset (da);
|
2016-08-09 04:15:44 +00:00
|
|
|
System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE);
|
2016-08-08 10:34:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int diskOffset = getBufferOffset (da, 0);
|
2016-08-09 04:15:44 +00:00
|
|
|
System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE);
|
2016-08-08 10:34:25 +00:00
|
|
|
|
|
|
|
diskOffset = getBufferOffset (da, 1);
|
2016-08-09 04:15:44 +00:00
|
|
|
System.arraycopy (buffer, SECTOR_SIZE, diskBuffer, diskOffset, SECTOR_SIZE);
|
2016-08-08 10:34:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 04:53:29 +00:00
|
|
|
private int getBufferOffset (DiskAddress da)
|
|
|
|
{
|
|
|
|
assert sectorSize == SECTOR_SIZE;
|
|
|
|
return da.getTrack () * trackSize
|
|
|
|
+ interleaveSector[interleave][da.getSector ()] * SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getBufferOffset (DiskAddress da, int seq)
|
|
|
|
{
|
|
|
|
assert sectorSize == BLOCK_SIZE;
|
|
|
|
assert seq == 0 || seq == 1;
|
|
|
|
|
|
|
|
return da.getTrack () * trackSize
|
|
|
|
+ interleaveSector[interleave][da.getSector () * 2 + seq] * SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
@Override
|
|
|
|
public void addActionListener (ActionListener actionListener)
|
|
|
|
{
|
|
|
|
actionListenerList = AWTEventMulticaster.add (actionListenerList, actionListener);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void removeActionListener (ActionListener actionListener)
|
|
|
|
{
|
|
|
|
actionListenerList = AWTEventMulticaster.remove (actionListenerList, actionListener);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void notifyListeners (String text)
|
|
|
|
{
|
|
|
|
if (actionListenerList != null)
|
2015-12-17 23:00:00 +00:00
|
|
|
actionListenerList
|
2016-07-31 02:14:48 +00:00
|
|
|
.actionPerformed (new ActionEvent (this, ActionEvent.ACTION_PERFORMED, text));
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public AppleFileSource getDetails ()
|
|
|
|
{
|
2016-11-28 00:45:17 +00:00
|
|
|
return new DefaultAppleFileSource (toString (), file.getName (), null);
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString ()
|
|
|
|
{
|
|
|
|
StringBuilder text = new StringBuilder ();
|
|
|
|
|
2016-12-07 10:42:01 +00:00
|
|
|
text.append (String.format ("Path............ %s%n", file.getAbsolutePath ()));
|
|
|
|
text.append (String.format ("File name....... %s%n", file.getName ()));
|
2016-11-28 00:45:17 +00:00
|
|
|
text.append (String.format ("File size....... %,d%n", file.length ()));
|
2016-12-07 10:42:01 +00:00
|
|
|
text.append (String.format ("Tracks.......... %d%n", tracks));
|
|
|
|
text.append (String.format ("Sectors......... %d%n", sectors));
|
2015-06-01 09:35:51 +00:00
|
|
|
text.append (String.format ("Blocks.......... %,d%n", blocks));
|
2016-12-07 10:42:01 +00:00
|
|
|
text.append (String.format ("Track size...... %,d%n", trackSize));
|
|
|
|
text.append (String.format ("Sector size..... %d%n", sectorSize));
|
|
|
|
text.append (String.format ("Interleave...... %d", interleave));
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
return text.toString ();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Iterator<DiskAddress> iterator ()
|
|
|
|
{
|
|
|
|
if (blockList == null)
|
|
|
|
{
|
|
|
|
blockList = new ArrayList<DiskAddress> (blocks);
|
|
|
|
for (int block = 0; block < blocks; block++)
|
2016-07-17 22:35:18 +00:00
|
|
|
blockList.add (new AppleDiskAddress (this, block));
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
2016-02-24 02:13:52 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return blockList.iterator ();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getBootChecksum ()
|
|
|
|
{
|
|
|
|
byte[] buffer = readSector (0, 0);
|
|
|
|
Checksum checksum = new CRC32 ();
|
|
|
|
checksum.update (buffer, 0, buffer.length);
|
|
|
|
return checksum.getValue ();
|
|
|
|
}
|
2016-02-25 07:45:24 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setEmptyByte (byte value)
|
|
|
|
{
|
|
|
|
emptyByte = value;
|
|
|
|
checkSectorsForData ();
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|