John B. Matthews, M.D.
Return home.
Download ac, a command-line Apple II disk image tool.
Visit the AppleCommander home page on Source Forge.
ac, a command-line Apple II disk image tool.
Introduction
"ac" is a command line interface to Rob Greene's AppleCommander, a java based tool for working with Apple ][ disk images. It works with ProDOS, DOS 3.3, Pascal and other disk images. The program is ideally suited to automate the transfer of compiler/assembler output onto an Apple II disk image
The program is provided under the GNU Public License, a copy of which is attached. You should read the license before using ac, noting that there is NO WARRANTY OF ANY KIND. Because here is NO LIABILITY FOR DAMAGES, never use ac on an image for which you do not have a backup.
Installing ac
After downloading the file ac.tgz, create a directory to hold the files and unpack ac.tgz with the tar command:
mkdir ac
cd ac
tar -zxvf ac.tgz
Using ac
The java classes for ac are in the file ac.jar.
The -p140 command creates a blank 140K ProDOS disk image; -p800 creates an 800K image. To create an image named misc.dsk having the volume name test, enter the command:
java -jar ac.jar -p140 misc.dsk test
The -l command lists the directory of a disk image. To examine a disk image named misc.dsk, enter the command:
java -jar ac.jar -l misc.dsk
The -e command lets you examine a file by printing a readable version to standard output. To examine a file named fred on a disk named misc.dsk, enter the command:
java -jar ac.jar -e fred misc.dsk
Because binary files are difficult to read, you may want to send the output to a program that can show the data in hexadecimal form:
java -jar ac.jar -e fred misc.dsk | hexdump
The -g command gets a file in its raw form. To copy a file named fred from misc.dsk to a file named ethel on your file system, enter the command:
java -jar ac.jar -g fred misc.dsk > ethel
The -p command puts data on a disk image. Suppose ethel is a binary file meant to start at address 2048 ($800). To put the binary file named ethel back into a file named fred with starting address 1028 on the image named misc.dsk:
cat ethel | java -jar ac.jar -p fred bin 2048 misc.dsk
The -d command deletes a file from an image:
java -jar ac.jar -p fred misc.dsk
To copy a file from one image to another, do something like this:
java -jar ac.jar -g fred one.dsk | java -jar ac.jar -p fred bin 2048 another.dsk
For a quick list of ac options, enter the -h command:
java -jar ac.jar -h
The result is shown:
AppleCommander command line options:
-l list directory of image.
-e export file from image to stdout.
-g get raw file from image to stdout.
-p put stdin
in destname on image, using file type and address.
-d delete file from image.
-p140 create a 140K ProDOS image.
-p800 create a 800K ProDOS image.
Known problems
To replace an existing file on an image, you must delete it first; ac will create as many duplicate entries as you specify, only one of which will be accessible to the operating system.
Getting a file from a ProDOS image will recursively search all directories, returning the first match. In contrast, files can only be put in the top level directory.
Changes to Apple Commander v1.2.3
The following changes were made to AppleCommander v1.2.3 to make it compatible with ac:
1. When compiling the package storage without swt, modify AppleWorksWordProcessorFileFilter to not import ui.AppleCommander or use AppleCommander.VERSION in the filter.
2. The format of Pascal dates is correct in the comments, but the shifts and mask are off. Also, Pascal months start at one. In AppleUtil
/**
* Extract a Pascal date from the buffer.
* Bits 0-3: month (1-12)
* Bits 4-8: day (1-31)
* Bits 9-15: year (0-99)
*/
public static Date getPascalDate(byte[] buffer, int offset) {
int pascalDate = getWordValue(buffer, offset);
int month = pascalDate & 0x000f - 1;
int day = (pascalDate & 0x01f0) >> 4;
int year = (pascalDate & 0xfe00) >> 9;
if (year < 50) year+= 2000;
if (year < 100) year+= 1900;
GregorianCalendar gc = new GregorianCalendar(year, month, day);
return gc.getTime();
}
/**
* Set a Pascal data to the buffer.
* Bits 0-3: month (1-12)
* Bits 4-8: day (1-31)
* Bits 9-15: year (0-99)
*/
public static void setPascalDate(byte[] buffer, int offset, Date date) {
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(date);
int month = gc.get(GregorianCalendar.MONTH) + 1;
int day = gc.get(GregorianCalendar.DAY_OF_MONTH);
int year = gc.get(GregorianCalendar.YEAR) % 100;
int pascalDate = (month & 0x000f)
| ((day << 4) & 0x01f0)
| ((year << 9) & 0xfe00);
setWordValue(buffer, offset, pascalDate);
}
3. In strage.PascalFileEntry.getFileColumnData use SimpleDateFormat("dd-MMM-yy").
4. In PascalFormatDisk.getDiskName add a ":" to the volume name.
5. Delete spurious ':' in @author tags.
6. Review javadoc warnings about broken @see tags.
7. Like Pascal, ProDOS months start at one, while Java expects zero for January. In AppleUtil.getProdosDate, subtract one from the month:
int month = ((ymd & 0x01e0) >> 5) - 1; // bits 5-8
In setProdosDate add one and fix the year:
month = gc.get(GregorianCalendar.MONTH) + 1;
...
if (year >= 2000) {
year -= 2000;
} else {
year -= 1900;
}
8. In ProdosFormatDisk.createFile(), add fileEntry.setKeyPointer(0); if this is a recyled directory entry, a subsequent call to setFileData will try to free blocks that previously belonged to the deleted file. These blocks may have subsequently been allocated to another file.
fileEntry.setKeyPointer(0); //may have been recycled
Also, call setSeedlingFile(), rather than setSaplingFile().
9. A file entry needs a header pointer: In ProdosFileEntry add
/**
* Set the block number of the block for the directory
* that describes this file.
*/
public void setHeaderPointer(int headerPointer) {
byte[] entry = readFileEntry();
AppleUtil.setWordValue(entry, 0x25, headerPointer);
writeFileEntry(entry);
}
10. Initailize header pointer in ProdosFormatDisk.createFile(), add
int headerBlock = blockNumber;
...
fileEntry.setHeaderPointer(headerBlock);
11. In ProdosFileEntry.delete(), the file count in the entry's directory header should be decremented. [ProDOS Tech. Ref. B.2.2 B.2.3] Also, both the storage type and the name length should be set to zero. [ProDOS Tech. Ref. B.2.4]
/**
* Delete the file.
*/
public void delete() {
getDisk().freeBlocks(this);
//decrement file count in header block
int headerBlock = getHeaderPointer();
byte[] data = getDisk().readBlock(headerBlock);
int fileCount = AppleUtil.getWordValue(data, 0x25);
if (fileCount != 0) fileCount--;
AppleUtil.setWordValue(data, 0x25, fileCount);
getDisk().writeBlock(headerBlock, data);
//clear storage type and name length
data = readFileEntry();
data[0] = 0;
writeFileEntry(data);
}
12. In TextFileFilter, use PrintWriter for cross-platform line endings:
/**
* Process the given FileEntry and return a byte array
* with filtered data; use PrintWriter to get platform
* agnostic line endings.
*/
public byte[] filter(FileEntry fileEntry) {
byte[] fileData = fileEntry.getFileData();
int offset = 0;
ByteArrayOutputStream byteArray = new
ByteArrayOutputStream(fileData.length);
PrintWriter printWriter = new PrintWriter(byteArray, true);
while (offset < fileData.length) {
char c = (char)(fileData[offset] & 0x7f);
if (c != 0) {
if (c == 0x0d) { //Apple line end
printWriter.println();
} else {
printWriter.print(c);
}
}
offset++;
}
return byteArray.toByteArray();
}
13. Applesoft files require a statign address, usually 2049 ($801); to Prodos FileTypes.properties add:
filetype.fc.address=true
Copyright 2003 John B. Matthews
Distribution permitted under the terms of the GPL: http://www.gnu.org/copyleft/gpl.html.
Last updated 06-Sep-2003
Return home.