Fixed subdirectory bugs, added support for A2GameServer data files suffix (#TTAAAA where TT is the hex code and AAAA is the aux data/load address). This should be very servicable now.

This commit is contained in:
Brendan Robert 2015-12-31 21:48:48 -06:00
parent 5f9352abb3
commit 732f4768a6
8 changed files with 269 additions and 180 deletions

View File

@ -80,7 +80,7 @@ public abstract class ProdosDriver {
MOS65C02 cpu = (MOS65C02) computer.getCpu();
cpu.A = returnCode;
// Clear carry flag if no error, otherwise set carry flag
cpu.C = (returnCode == 0x00) ? 00 : 01;
cpu.C = (returnCode == 0x00) ? 00 : 01;
}
private MLI_RETURN prodosMLI() {

View File

@ -18,14 +18,17 @@
*/
package jace.hardware.massStorage;
import static jace.hardware.massStorage.IDisk.BLOCK_SIZE;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -35,10 +38,14 @@ import java.util.logging.Logger;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class DirectoryNode extends DiskNode implements FileFilter {
// public static int FILE_ENTRY_SIZE = 38;
public static int FILE_ENTRY_SIZE = 0x027;
public static final byte STANDARD_PERMISSIONS = (byte) 0x0c3;
public static final int PRODOS_VERSION = 0x023;
public static final int FILE_ENTRY_SIZE = 0x027;
public static final int ENTRIES_PER_BLOCK = (ProdosVirtualDisk.BLOCK_SIZE - 4) / FILE_ENTRY_SIZE;
private boolean isRoot;
private List<DiskNode> directoryEntries;
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException {
super(ownerFilesystem, baseBlock);
@ -52,9 +59,11 @@ public class DirectoryNode extends DiskNode implements FileFilter {
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException {
isRoot = root;
directoryEntries = new ArrayList<>();
setPhysicalFile(physicalFile);
setType(EntryType.SUBDIRECTORY);
setName(physicalFile.getName());
allocate();
}
@Override
@ -63,15 +72,15 @@ public class DirectoryNode extends DiskNode implements FileFilter {
@Override
public void doAllocate() throws IOException {
File[] files = physicalFile.listFiles(this);
int numEntries = files.length;
// First block has 12 entries, subsequent blocks have 13 entries
int numBlocks = 1 + numEntries / 13;
for (int i=1; i < numBlocks; i++) {
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock());
for (int i = 1; i < getBlockCount(); i++) {
if (isRoot) {
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock(3));
} else {
new SubNode(i, this);
}
}
for (File f : files) {
for (File f : physicalFile.listFiles()) {
addFile(f);
}
Collections.sort(children, (DiskNode o1, DiskNode o2) -> o1.getName().compareTo(o2.getName()));
@ -88,58 +97,66 @@ public class DirectoryNode extends DiskNode implements FileFilter {
*/
public boolean checkFile() throws IOException {
boolean success = true;
if (!super.checkFile()) {
return false;
}
HashSet<String> realFiles = new HashSet<>();
File[] realFileList = physicalFile.listFiles(this);
for (File f : realFileList) {
realFiles.add(f.getName());
}
for (Iterator<DiskNode> i = getChildren().iterator(); i.hasNext();) {
DiskNode node = i.next();
if (realFiles.contains(node.getPhysicalFile().getName())) {
realFiles.remove(node.getPhysicalFile().getName());
} else {
i.remove();
success = false;
}
if (node.isAllocated()) {
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
success = false;
if (!allocated) {
allocate();
} else {
try {
if (!super.checkFile()) {
return false;
}
HashSet<String> realFiles = new HashSet<>();
File[] realFileList = physicalFile.listFiles(this);
for (File f : realFileList) {
realFiles.add(f.getName());
}
for (Iterator<DiskNode> i = directoryEntries.iterator(); i.hasNext();) {
DiskNode node = i.next();
if (realFiles.contains(node.getPhysicalFile().getName())) {
realFiles.remove(node.getPhysicalFile().getName());
} else {
i.remove();
success = false;
}
if (node.isAllocated()) {
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
success = false;
}
}
}
if (!realFiles.isEmpty()) {
success = false;
// New files showed up -- deal with them!
realFiles.stream().forEach((fileName) -> {
addFile(new File(physicalFile, fileName));
});
}
} catch (IOException ex) {
return false;
}
}
if (!realFiles.isEmpty()) {
success = false;
// New files showed up -- deal with them!
realFiles.stream().forEach((fileName) -> {
addFile(new File(physicalFile, fileName));
});
}
return success;
}
@Override
public void readBlock(int block, byte[] buffer) throws IOException {
Arrays.fill(buffer, (byte) 0);
checkFile();
int start = 0;
int end = 0;
int offset = 4;
generatePointers(buffer, block);
// System.out.println("Directory "+getName()+" sequence "+block+"; physical block "+getNodeSequence(block).getBaseBlock());
if (block == 0) {
generateHeader(buffer);
for (int i = 0; i < 12 && i < children.size(); i++) {
generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i);
}
offset += FILE_ENTRY_SIZE;
end = ENTRIES_PER_BLOCK - 1;
} else {
generatePointers(buffer, getNodeSequence(block-1).getBaseBlock(), getNodeSequence(block+1) != null ? getNodeSequence(block+1).getBaseBlock() : 0);
int start = (block * 13) - 1;
int end = start + 13;
int offset = 4;
for (int i = start; i < end && i < children.size(); i++) {
// TODO: Add any parts that are not file entries.
generateFileEntry(buffer, offset, i);
offset += FILE_ENTRY_SIZE;
}
start = (block * ENTRIES_PER_BLOCK) - 1;
end = start + ENTRIES_PER_BLOCK;
}
for (int i = start; i < end && i < directoryEntries.size(); i++, offset += FILE_ENTRY_SIZE) {
// TODO: Add any parts that are not file entries.
// System.out.println("Entry "+i+": "+children.get(i).getName()+"; offset "+offset);
generateFileEntry(buffer, offset, i);
}
}
@ -155,13 +172,16 @@ public class DirectoryNode extends DiskNode implements FileFilter {
return !file.isHidden();
}
private void generatePointers(byte[] buffer, int prevBlock, int nextBlock) {
// Previous block = 0
generateWord(buffer, 0, prevBlock);
// Next block
generateWord(buffer, 0x02, nextBlock);
private void generatePointers(byte[] buffer, int sequence) {
DiskNode prev = getNodeSequence(sequence - 1);
DiskNode next = getNodeSequence(sequence + 1);
// System.out.println("Sequence "+sequence+" prev="+(prev != null ? prev.getBaseBlock() : 0)+"; next="+(next != null ? next.getBaseBlock() : 0));
// Previous block (or 0)
generateWord(buffer, 0, prev != null ? prev.getBaseBlock() : 0);
// Next block (or 0)
generateWord(buffer, 0x02, next != null ? next.getBaseBlock() : 0);
}
/**
* Generate the directory header found in the base block of a directory
*
@ -169,40 +189,43 @@ public class DirectoryNode extends DiskNode implements FileFilter {
*/
@SuppressWarnings("static-access")
private void generateHeader(byte[] buffer) {
generatePointers(buffer, 0, getNodeSequence(1) == null ? 0 : getNodeSequence(1).getBaseBlock());
// Directory header + name length
// Volumme header = 0x0f0; Subdirectory header = 0x0e0
buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length());
generateName(buffer, 5, this);
for (int i = 0x014; i <= 0x01b; i++) {
buffer[i] = 0;
}
if (!isRoot) {
buffer[0x014] = 0x075;
buffer[0x015] = PRODOS_VERSION;
buffer[0x017] = STANDARD_PERMISSIONS;
buffer[0x018] = FILE_ENTRY_SIZE;
buffer[0x019] = ENTRIES_PER_BLOCK;
}
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
// Prodos 1.9
buffer[0x020] = 0x019;
// Prodos 1.0 = 0
buffer[0x020] = PRODOS_VERSION;
// Minimum version = 0 (no min)
buffer[0x021] = 0x000;
// Directory may be read/written to, may not be destroyed or renamed
buffer[0x022] = 0x03;
buffer[0x022] = STANDARD_PERMISSIONS;
// Entry size
buffer[0x023] = (byte) FILE_ENTRY_SIZE;
// Entries per block
buffer[0x024] = (byte) 0x0d;
buffer[0x024] = (byte) ENTRIES_PER_BLOCK;
// Directory items count
generateWord(buffer, 0x025, children.size());
generateWord(buffer, 0x025, directoryEntries.size()+1);
if (isRoot) {
// Volume bitmap pointer
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock());
// Total number of blocks
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
} else {
// According to the Beneath Apple Prodos supplement
int indexInParent = getParent().getChildren().indexOf(this) + 2;
int parentBlock = getParent().getNodeSequence(indexInParent / ENTRIES_PER_BLOCK).getBaseBlock();
// Parent pointer
generateWord(buffer, 0x027, getParent().getBaseBlock());
buffer[0x029] = (byte) (getParent().getChildren().indexOf(this) + 1);
buffer[0x02a] = 0x027;
generateWord(buffer, 0x027, parentBlock);
buffer[0x029] = (byte) (indexInParent % ENTRIES_PER_BLOCK);
buffer[0x02a] = (byte) FILE_ENTRY_SIZE;
}
}
@ -214,9 +237,9 @@ public class DirectoryNode extends DiskNode implements FileFilter {
* @param fileNumber number of file (indexed in Children array) to write
*/
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
DiskNode child = children.get(fileNumber);
DiskNode child = directoryEntries.get(fileNumber);
// Entry Type and length
buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length());
buffer[offset] = (byte) ((child.getType().code << 4) | child.getName().length());
// Name
generateName(buffer, offset + 1, child);
// File type
@ -224,20 +247,20 @@ public class DirectoryNode extends DiskNode implements FileFilter {
// Key pointer
generateWord(buffer, offset + 0x011, child.getBaseBlock());
// Blocks used -- will report only one unless file is actually allocated
generateWord(buffer, offset + 0x013, child.additionalNodes.size());
// EOF
// TODO: Verify this is the right thing to do -- is EOF total length or a modulo?
int length = ((int) child.physicalFile.length()) & 0x0ffffff;
generateWord(buffer, offset + 0x013, child.additionalNodes.size() + 1);
// EOF (file size or directory structure size
int length = child.getLength();
length &= 0x0ffffff;
generateWord(buffer, offset + 0x015, length & 0x0ffff);
buffer[offset + 0x017] = (byte) ((length >> 16) & 0x0ff);
// Creation date
generateTimestamp(buffer, offset + 0x018, child.physicalFile.lastModified());
// Version = 1.9
buffer[offset + 0x01c] = 0x19;
// Version = 1.0
buffer[offset + 0x01c] = PRODOS_VERSION;
// Minimum version = 0
buffer[offset + 0x01d] = 0;
// Access = Read-only
buffer[offset + 0x01e] = (byte) 0x001;
buffer[offset + 0x01e] = STANDARD_PERMISSIONS;
// AUX type
if (child instanceof FileNode) {
generateWord(buffer, offset + 0x01f, ((FileNode) child).loadAddress);
@ -267,20 +290,40 @@ public class DirectoryNode extends DiskNode implements FileFilter {
}
private void generateName(byte[] buffer, int offset, DiskNode node) {
for (int i = 0; i < node.getName().length(); i++) {
for (int i = 0; i < node.getName().length() && i < 15; i++) {
buffer[offset + i] = (byte) node.getName().charAt(i);
}
}
private Optional<DiskNode> findChildByFilename(String name) {
return directoryEntries.stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
}
private void addFile(File file) {
try {
if (file.isDirectory()) {
addChild(new DirectoryNode(getOwnerFilesystem(), file, false));
} else {
addChild(new FileNode(getOwnerFilesystem(), file));
if (!hasChildNamed(file.getName())) {
try {
if (file.isDirectory()) {
addFileEntry(new DirectoryNode(getOwnerFilesystem(), file, false));
} else {
addFileEntry(new FileNode(getOwnerFilesystem(), file));
}
} catch (IOException ex) {
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void addFileEntry(DiskNode entry) {
directoryEntries.add(entry);
entry.setParent(this);
}
@Override
public int getLength() {
return getBlockCount() * BLOCK_SIZE;
}
private int getBlockCount() {
return isRoot ? 4 : 1 + (physicalFile.listFiles().length / ENTRIES_PER_BLOCK);
}
}

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Prodos file/directory node abstraction. This provides a lot of the glue for
@ -60,12 +61,12 @@ public abstract class DiskNode {
public DiskNode(ProdosVirtualDisk fs) throws IOException {
init(fs);
setBaseBlock(fs.getNextFreeBlock());
fs.allocateEntry(this);
}
public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException {
init(fs);
setBaseBlock(blockNumber);
fs.allocateEntryNear(this, blockNumber);
}
private void init(ProdosVirtualDisk fs) throws IOException {
@ -89,7 +90,6 @@ public abstract class DiskNode {
public void allocate() throws IOException {
if (!allocated) {
doAllocate();
getOwnerFilesystem().physicalMap.put(baseBlock, this);
allocationTime = System.currentTimeMillis();
allocated = true;
}
@ -118,7 +118,7 @@ public abstract class DiskNode {
public DiskNode getNodeSequence(int num) {
if (num == 0) {
return this;
} else if (num <= additionalNodes.size()) {
} else if (num > 0 && num <= additionalNodes.size()) {
return additionalNodes.get(num-1);
} else {
return null;
@ -156,9 +156,8 @@ public abstract class DiskNode {
/**
* @param baseBlock the baseBlock to set
*/
private void setBaseBlock(int baseBlock) {
public void setBaseBlock(int baseBlock) {
this.baseBlock = baseBlock;
ownerFilesystem.physicalMap.put(baseBlock, this);
}
/**
@ -189,6 +188,7 @@ public abstract class DiskNode {
public void setPhysicalFile(File physicalFile) {
this.physicalFile = physicalFile;
setName(physicalFile.getName());
lastCheckTime = physicalFile.lastModified();
}
/**
@ -227,6 +227,14 @@ public abstract class DiskNode {
public void removeChild(DiskNode child) {
children.remove(child);
}
public boolean hasChildNamed(String name) {
return findChildByFilename(name).isPresent();
}
private Optional<DiskNode> findChildByFilename(String name) {
return getChildren().stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
}
/**
* @return the type
@ -253,10 +261,7 @@ public abstract class DiskNode {
* @param name the name to set
*/
public void setName(String name) {
if (name.length() > 15) {
name = name.substring(0, 15);
}
this.name = name.toUpperCase();
this.name = (name.length() > 15 ? name.substring(0, 15) : name).toUpperCase();
}
public abstract void doDeallocate();
@ -266,6 +271,8 @@ public abstract class DiskNode {
public abstract void doRefresh();
public abstract void readBlock(int sequence, byte[] buffer) throws IOException;
public abstract int getLength();
public void readBlock(byte[] buffer) throws IOException {
checkFile();

View File

@ -21,7 +21,6 @@ package jace.hardware.massStorage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* Representation of a prodos file with a known file type and having a known
@ -31,6 +30,11 @@ import java.util.Arrays;
*/
public class FileNode extends DiskNode {
@Override
public int getLength() {
return (int) getPhysicalFile().length();
}
public enum FileType {
UNKNOWN(0x00, 0x0000),
@ -67,6 +71,15 @@ public class FileNode extends DiskNode {
this.code = code;
this.defaultLoadAddress = addr;
}
public static FileType findByCode(int code) {
for (FileType t : FileType.values()) {
if (t.code == code) {
return t;
}
}
return UNKNOWN;
}
}
public int fileType = 0x00;
public int loadAddress = 0x00;
@ -90,28 +103,35 @@ public class FileNode extends DiskNode {
@Override
public void setName(String name) {
String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\.");
FileType t = FileType.UNKNOWN;
int offset = 0;
String prodosName = name;
if (parts.length > 1) {
String extension = parts[parts.length - 1].toUpperCase();
String[] extParts = extension.split("\\#");
if (extParts.length == 2) {
offset = Integer.parseInt(extParts[1], 16);
extension = extParts[0];
}
try {
t = FileType.valueOf(extension);
} catch (IllegalArgumentException ex) {
System.out.println("Not sure what extension " + extension + " is!");
}
prodosName = "";
for (int i = 0; i < parts.length - 1; i++) {
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
}
if (extParts[extParts.length - 1].equals("SYSTEM")) {
prodosName += ".SYSTEM";
if (name.matches("^.*?#[0-9A-Fa-f]{6}$")) {
int type = Integer.parseInt(name.substring(name.length() - 6, name.length() - 4), 16);
offset = Integer.parseInt(name.substring(name.length() - 4), 16);
t = FileType.findByCode(type);
prodosName = name.substring(0, name.length()-7).replaceAll("[^A-Za-z0-9#]", ".").toUpperCase();
} else {
String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\.");
if (parts.length > 1) {
String extension = parts[parts.length - 1].toUpperCase();
String[] extParts = extension.split("\\#");
if (extParts.length == 2) {
offset = Integer.parseInt(extParts[1], 16);
extension = extParts[0];
}
try {
t = FileType.valueOf(extension);
} catch (IllegalArgumentException ex) {
System.out.println("Not sure what extension " + extension + " is!");
}
prodosName = "";
for (int i = 0; i < parts.length - 1; i++) {
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
}
if (extParts[extParts.length - 1].equals("SYSTEM")) {
prodosName += ".SYSTEM";
}
}
}
if (offset == 0) {
@ -128,6 +148,7 @@ public class FileNode extends DiskNode {
super(ownerFilesystem);
setPhysicalFile(file);
setName(file.getName());
allocate();
}
@Override
@ -167,7 +188,7 @@ public class FileNode extends DiskNode {
readFile(buffer, (block - 1));
} else {
// Generate seedling index block
generateIndex(buffer, 1, dataBlocks+1);
generateIndex(buffer, 1, dataBlocks + 1);
}
break;
case TREE:
@ -192,7 +213,6 @@ public class FileNode extends DiskNode {
}
private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
Arrays.fill(buffer, (byte) 0);
for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) {
int base = getNodeSequence(i).getBaseBlock();
buffer[count] = (byte) (base & 0x0ff);

View File

@ -22,25 +22,28 @@ import java.io.IOException;
/**
* Maintain freespace and node allocation
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class FreespaceBitmap extends DiskNode {
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
super(fs, start);
for (int i=1; i < size; i++) {
SubNode subNode = new SubNode(i, this, start+i);
}
allocate();
}
@Override
public void doDeallocate() {
//
}
@Override
public void doAllocate() {
///
public void doAllocate() throws IOException {
for (int i = 1; i < size; i++) {
SubNode subNode = new SubNode(i, this, getBaseBlock());
}
}
@Override
@ -51,22 +54,18 @@ public class FreespaceBitmap extends DiskNode {
@Override
public void readBlock(int sequence, byte[] buffer) throws IOException {
int startBlock = sequence * ProdosVirtualDisk.BLOCK_SIZE * 8;
int endBlock = (sequence+1)* ProdosVirtualDisk.BLOCK_SIZE * 8;
int bitCounter=0;
int pos=0;
int value=0;
for (int i=startBlock; i < endBlock; i++) {
if (!getOwnerFilesystem().isAllocated(i)) {
value++;
}
bitCounter++;
if (bitCounter < 8) {
value *= 2;
} else {
bitCounter = 0;
buffer[pos++]=(byte) value;
value = 0;
int endBlock = (sequence + 1) * ProdosVirtualDisk.BLOCK_SIZE * 8;
for (int i = startBlock; i < endBlock; i++) {
if (!getOwnerFilesystem().isBlockAllocated(i)) {
int pos = (i - startBlock) / 8;
int bit = 1 << (i % 8);
buffer[pos] |= bit;
}
}
}
}
@Override
public int getLength() {
return (1 + getChildren().size()) * IDisk.BLOCK_SIZE;
}
}

View File

@ -28,7 +28,7 @@ import java.io.IOException;
*/
public interface IDisk {
public static int BLOCK_SIZE = 512;
public static int MAX_BLOCK = 65535;
public static int MAX_BLOCK = 0x07fff;
public void mliFormat() throws IOException;
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException;

View File

@ -18,6 +18,7 @@
*/
package jace.hardware.massStorage;
import jace.Emulator;
import jace.EmulatorUILogic;
import jace.apple2e.MOS65C02;
import jace.core.Computer;
@ -27,8 +28,6 @@ import jace.hardware.ProdosDriver.MLI_COMMAND_TYPE;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -42,16 +41,17 @@ import java.util.logging.Logger;
*/
public class ProdosVirtualDisk implements IDisk {
public static int VOLUME_START = 2;
public static int FREESPACE_BITMAP_START = 6;
public static final int VOLUME_START = 2;
public static final int FREESPACE_BITMAP_START = 6;
byte[] ioBuffer;
File physicalRoot;
Map<Integer, DiskNode> physicalMap;
private final DiskNode[] physicalMap;
DirectoryNode rootDirectory;
FreespaceBitmap freespaceBitmap;
public ProdosVirtualDisk(File rootPath) throws IOException {
ioBuffer = new byte[BLOCK_SIZE];
physicalMap = new DiskNode[MAX_BLOCK];
initDiskStructure();
setPhysicalPath(rootPath);
}
@ -59,29 +59,28 @@ public class ProdosVirtualDisk implements IDisk {
@Override
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException {
// System.out.println("Read block " + block + " to " + Integer.toHexString(bufferAddress));
DiskNode node = physicalMap.get(block);
Arrays.fill(ioBuffer, (byte) (block & 0x0ff));
DiskNode node = physicalMap[block];
Arrays.fill(ioBuffer, (byte) 0);
if (node == null) {
System.out.println("Reading unknown block " + Integer.toHexString(block));
for (int i = 0; i < BLOCK_SIZE; i++) {
memory.write(bufferAddress + i, (byte) 0, false, false);
}
System.out.println("Unknown block " + Integer.toHexString(block));
} else {
// if (node.getPhysicalFile() == null) {
// System.out.println("reading block "+block+ " from directory structure to "+Integer.toHexString(bufferAddress));
// } else {
// System.out.println("reading block "+block+ " from "+node.getPhysicalFile().getName()+" to "+Integer.toHexString(bufferAddress));
// }
node.readBlock(ioBuffer);
for (int i = 0; i < BLOCK_SIZE; i++) {
memory.write(bufferAddress + i, ioBuffer[i], false, false);
}
}
// for (int i=0; i < 512; i++) {
// if (i % 32 == 0 && i > 0) System.out.println();
// System.out.print(((ioBuffer[i]&0x0ff)<16 ? "0" : "") + Integer.toHexString(ioBuffer[i] & 0x0ff) + " ");
for (int i = 0; i < ioBuffer.length; i++) {
memory.write(bufferAddress + i, ioBuffer[i], false, false);
}
// System.out.println("Block " + Integer.toHexString(block));
// for (int i = 0; i < 32; i++) {
// String hex = "";
// String text = "";
// for (int j = 0; j < 16; j++) {
// int val = 0x0ff & memory.readRaw(bufferAddress + i * 16 + j);
// char show = (char) (((val & 0x7f) < ' ') ? '.' : val & 0x7f);
// hex += (val < 16 ? "0" : "") + Integer.toString(val, 16) + " ";
// text += show;
// }
// System.out.println(hex + " " + text);
// }
// System.out.println();
}
@Override
@ -121,33 +120,51 @@ public class ProdosVirtualDisk implements IDisk {
return mostLikelyMatch;
}
public int getNextFreeBlock() throws IOException {
public int getNextFreeBlock(int start) throws IOException {
// Don't allocate Zero block for anything!
// for (int i = 0; i < MAX_BLOCK; i++) {
for (int i = 2; i < MAX_BLOCK; i++) {
if (!physicalMap.containsKey(i)) {
for (int i = start; i < MAX_BLOCK; i++) {
if (physicalMap[i] == null) {
return i;
}
}
throw new IOException("Virtual Disk Full!");
}
public int allocateEntry(DiskNode node) throws IOException {
return allocateEntryNear(node, FREESPACE_BITMAP_START);
}
public int allocateEntryNear(DiskNode node, int start) throws IOException {
if (isNodeAllocated(node)) {
return node.getBaseBlock();
}
int block = getNextFreeBlock(start);
node.setBaseBlock(block);
physicalMap[block] = node;
return block;
}
public boolean isNodeAllocated(DiskNode node) {
return node.getBaseBlock() >= 0 && physicalMap[node.getBaseBlock()] == node;
}
// Mark space occupied by nodes as free (remove allocation mapping)
public void deallocateEntry(DiskNode node) {
// Only de-map nodes if the allocation table is actually pointing to the nodes!
if (physicalMap.get(node.getBaseBlock()) != null && physicalMap.get(node.getBaseBlock()).equals(node)) {
physicalMap.remove(node.getBaseBlock());
if (physicalMap[node.getBaseBlock()] != null && physicalMap[node.getBaseBlock()].equals(node)) {
physicalMap[node.getBaseBlock()] = null;
}
node.additionalNodes.stream().filter((sub)
-> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.getBaseBlock()).equals(sub))).
-> (physicalMap[sub.getBaseBlock()] != null && physicalMap[sub.getBaseBlock()].equals(sub))).
forEach((sub) -> {
physicalMap.remove(sub.getBaseBlock());
physicalMap[sub.getBaseBlock()] = null;
});
}
// Is the specified block in use?
public boolean isAllocated(int i) {
return (physicalMap.containsKey(i));
public boolean isBlockAllocated(int i) {
return (i >= physicalMap.length || physicalMap[i] != null);
}
@Override
@ -174,10 +191,9 @@ public class ProdosVirtualDisk implements IDisk {
}
private void initDiskStructure() throws IOException {
physicalMap = new HashMap<>();
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
}
private void setPhysicalPath(File f) throws IOException {
if (physicalRoot != null && physicalRoot.equals(f)) {
return;
@ -193,7 +209,6 @@ public class ProdosVirtualDisk implements IDisk {
// Root directory ALWAYS starts on block 2!
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true);
rootDirectory.setName("VIRTUAL");
rootDirectory.allocate();
}
@Override
@ -208,6 +223,6 @@ public class ProdosVirtualDisk implements IDisk {
@Override
public int getSize() {
return 0x0ffff;
return MAX_BLOCK;
}
}

View File

@ -69,4 +69,9 @@ public class SubNode extends DiskNode {
public void readBlock(int sequence, byte[] buffer) throws IOException {
parent.readBlock(sequenceNumber, buffer);
}
@Override
public int getLength() {
return IDisk.BLOCK_SIZE;
}
}