Merge pull request #199 from AppleCommander/feature/various-fixups

Feature/various fixups
This commit is contained in:
A2 Geek authored and GitHub committed 2025-10-07 15:25:42 -05:00
commit fc6f6c6ec3
40 files changed
+750 -440

No files matched your search

+5
View File
@@ -47,6 +47,11 @@ jobs:
- name: Build ac/acx
run: ./gradlew nativeCompile
- name: Run CLTH tests
if: ${{ matrix.os != 'windows' }}
shell: bash
run: ./scripts/run-clth-tests.sh
- name: Upload binary
uses: actions/upload-artifact@v4
with:
+3
View File
@@ -7,6 +7,9 @@ build/
.vscode
.idea
# Any binaries downloaded
.bin/
# In case we run it locally, AppleCommander files we don't want to commit
AppleCommander.preferences
*.bas
@@ -64,7 +64,7 @@ public enum ExportMethod {
private final Function<FileEntry,FileFilter> fileFilterFn;
private final List<String> codes;
private ExportMethod(Function<FileEntry,FileFilter> fileFilterFn, String... codes) {
ExportMethod(Function<FileEntry, FileFilter> fileFilterFn, String... codes) {
this.fileFilterFn = fileFilterFn;
this.codes = Arrays.asList(codes);
}
@@ -44,7 +44,7 @@ public enum SystemType {
PASCAL(OrderType.PRODOS, SystemType::enforce140KbDisk,
SystemType::copyPascalSystemFiles);
static Logger LOG = Logger.getLogger(SystemType.class.getName());
static final Logger LOG = Logger.getLogger(SystemType.class.getName());
private final OrderType defaultOrderType;
private final Function<Integer,Integer> enforceDiskSizeFn;
@@ -122,9 +122,9 @@ public class ListCommand extends ReadOnlyDiskImageCommandOptions {
CSV(CsvListingStrategy::new),
JSON(JsonListingStrategy::new);
private Function<Integer,ListingStrategy> constructorFn;
private final Function<Integer,ListingStrategy> constructorFn;
private OutputStrategy(Function<Integer,ListingStrategy> constructorFn) {
OutputStrategy(Function<Integer, ListingStrategy> constructorFn) {
this.constructorFn = constructorFn;
}
@@ -132,8 +132,8 @@ public class ListCommand extends ReadOnlyDiskImageCommandOptions {
return constructorFn.apply(display);
}
};
}
@Option(names = "--text", description = "Formatted text (default).")
public void selectTextOutput(boolean flag) {
this.outputStrategy = OutputStrategy.TEXT;
+31
View File
@@ -0,0 +1,31 @@
plugins {
id 'java'
}
java {
sourceCompatibility = 21
targetCompatibility = 21
}
repositories {
mavenCentral()
}
dependencies {
implementation project(':app:cli-ac')
implementation project(':app:cli-acx')
testImplementation platform("org.junit:junit-bom:$junitVersion")
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "io.github.a2geek:clth:$clthVersion"
}
test {
useJUnitPlatform()
def junit5SystemExit = configurations.testRuntimeClasspath.files
.find { it.name.contains('junit5-system-exit') }
jvmArgumentProviders.add({["-javaagent:$junit5SystemExit"]} as CommandLineArgumentProvider)
}
@@ -0,0 +1,55 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2025 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.webcodepro.applecommander.ui;
import io.github.a2geek.clth.Config;
import io.github.a2geek.clth.JUnitHelper;
import io.github.a2geek.clth.TestHarness;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.stream.Stream;
import io.github.a2geek.clth.TestSuite;
public class AcCommandLineTest {
@ParameterizedTest(name = "{1}: {2}")
@MethodSource("testCases")
public void test(TestSuite testSuite, String name, String parameters) {
TestHarness.run(testSuite, JUnitHelper::execute, TestHarness.FilePreservation.DELETE);
}
public static Stream<Arguments> testCases() {
try (InputStream inputStream = AcCommandLineTest.class.getResourceAsStream("/ac-config.yml")) {
assert inputStream != null;
String document = new String(inputStream.readAllBytes());
Config config = Config.load(document);
return TestSuite.build(config)
.map(t -> Arguments.of(t, t.testName(), String.join(" ", t.variables().values())));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
@@ -0,0 +1,55 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2025 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package io.github.applecommander.acx;
import io.github.a2geek.clth.Config;
import io.github.a2geek.clth.JUnitHelper;
import io.github.a2geek.clth.TestHarness;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.stream.Stream;
import io.github.a2geek.clth.TestSuite;
public class AcxCommandLineTest {
@ParameterizedTest(name = "{1}: {2}")
@MethodSource("testCases")
public void test(TestSuite testSuite, String name, String parameters) {
TestHarness.run(testSuite, JUnitHelper::execute, TestHarness.FilePreservation.DELETE);
}
public static Stream<Arguments> testCases() {
try (InputStream inputStream = AcxCommandLineTest.class.getResourceAsStream("/acx-config.yml")) {
assert inputStream != null;
String document = new String(inputStream.readAllBytes());
Config config = Config.load(document);
return TestSuite.build(config)
.map(t -> Arguments.of(t, t.testName(), String.join(" ", t.variables().values())));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
@@ -0,0 +1,50 @@
commands:
ac:
main-class: com.webcodepro.applecommander.ui.ac
system-exit: no
executable: app/cli-ac/build/native/nativeCompile/ac-*
files:
txtfile:
type: text
# See: https://www.lipsum.com/
content: |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
basfile:
type: text
content: |
10 TEXT : HOME
20 PRINT "HELLO, WORLD"
30 END
binfile:
type: binary
# Address $300
content: |
20 2f fb 20 58 fc a0 0b b9 14 03 20 ed fd 88 d0
f7 20 8e fd 60 c4 cc d2 cf d7 a0 cf cc cc c5 c8
testdisk.dsk:
type: temporary
prefix: testdisk
suffix: .dsk
testdisk.po:
type: temporary
prefix: testdisk
suffix: .po
tests:
- name: ticket192-ac
variables:
# All arrays must be same length -- only iterated over, not matrixed
flag: ["-dos140", "-pro140", "-pas140"]
volname: ["", "TEST", "TEST"] # blank will be skipped
disk: ["$testdisk.dsk", "$testdisk.po", "$testdisk.po"]
steps:
- command: ac $flag $disk $volname
- command: ac -pt $disk TESTFILE
stdin: $txtfile
- command: ac -d $disk TESTFILE
- command: ac -pt $disk TESTFILE
stdin: $txtfile
@@ -0,0 +1,47 @@
commands:
acx:
main-class: io.github.applecommander.acx.Main
system-exit: yes
executable: app/cli-acx/build/native/nativeCompile/acx-*
files:
txtfile:
type: text
# See: https://www.lipsum.com/
content: |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
basfile:
type: text
content: |
10 TEXT : HOME
20 PRINT "HELLO, WORLD"
30 END
binfile:
type: binary
# Address $300
content: |
20 2f fb 20 58 fc a0 0b b9 14 03 20 ed fd 88 d0
f7 20 8e fd 60 c4 cc d2 cf d7 a0 cf cc cc c5 c8
testdisk.dsk:
type: temporary
prefix: testdisk
suffix: .dsk
testdisk.po:
type: temporary
prefix: testdisk
suffix: .po
tests:
- name: ticket192-acx
variables:
# All arrays must be same length -- only iterated over, not matrixed
flag: ["--dos", "--prodos", "--pascal"]
disk: ["$testdisk.dsk", "$testdisk.po", "$testdisk.po"]
steps:
- command: acx mkdisk -d $disk $flag
- command: acx import -d $disk $txtfile --name TESTFILE
- command: acx rm -d $disk TESTFILE
- command: acx import -d $disk $txtfile --name TESTFILE
+1
View File
@@ -17,4 +17,5 @@ ext {
springBoot = "2.7.7"
acdasmVersion = "6.0"
graalvmBuildVersion = "0.10.6"
clthVersion = "1.1"
}
@@ -36,7 +36,7 @@ public interface DirectoryEntry {
/**
* Return a name for this directory.
*/
public String getDirname();
String getDirname();
/**
* Retrieve the list of files in this directory.
@@ -45,17 +45,17 @@ public interface DirectoryEntry {
* return value should always be a list - a directory
* with 0 entries returns an empty list.
*/
public List<FileEntry> getFiles() throws DiskException;
List<FileEntry> getFiles() throws DiskException;
/**
* Create a new FileEntry.
*/
public FileEntry createFile() throws DiskException;
FileEntry createFile() throws DiskException;
/**
* Create a new DirectoryEntry.
*/
public DirectoryEntry createDirectory(String name) throws DiskException;
DirectoryEntry createDirectory(String name) throws DiskException;
/**
* Identify if additional directories can be created. This
@@ -63,19 +63,19 @@ public interface DirectoryEntry {
* operating system or simply that the disk image is "locked"
* to writing.
*/
public boolean canCreateDirectories();
boolean canCreateDirectories();
/**
* Indicates if this disk image can create a file.
* If not, the reason may be as simple as it has not beem implemented
* to something specific about the disk.
*/
public boolean canCreateFile();
boolean canCreateFile();
/**
* Get the FormattedDisk associated with this DirectoryEntry.
* This is useful to interfaces that need to retrieve the associated
* disk.
*/
public FormattedDisk getFormattedDisk();
FormattedDisk getFormattedDisk();
}
@@ -34,8 +34,8 @@ public class DiskCorruptException extends DiskException {
RECURSIVE_DIRECTORY_STRUCTURE {
public String toString() {
return "DiskCorruptException.RecursiveDirectoryStructure"; //$NON-NLS-1$
};
}
}
}
}
public final Kind kind;
@@ -32,7 +32,7 @@ public enum DiskGeometry {
public final int size;
public final String text;
private DiskGeometry(int size, String text) {
DiskGeometry(int size, String text) {
this.size = size;
this.text = text;
}
@@ -31,92 +31,92 @@ public interface FileEntry {
/**
* Return the name of this file.
*/
public String getFilename();
String getFilename();
/**
* Set the name of this file.
*/
public void setFilename(String filename);
void setFilename(String filename);
/**
* Return the filetype of this file.
* This will be OS specific.
*/
public String getFiletype();
String getFiletype();
/**
* Set the filetype.
*/
public void setFiletype(String filetype);
void setFiletype(String filetype);
/**
* Identify if this file is locked.
*/
public boolean isLocked();
boolean isLocked();
/**
* Set the lock indicator.
*/
public void setLocked(boolean lock);
void setLocked(boolean lock);
/**
* Compute the size of this file (in bytes).
*/
public int getSize();
int getSize();
/**
* Identify if this is a directory file.
*/
public boolean isDirectory();
boolean isDirectory();
/**
* Identify if this file has been deleted.
*/
public boolean isDeleted();
boolean isDeleted();
/**
* Delete the file.
*/
public void delete();
void delete();
/**
* Get the standard file column header information.
* This default implementation is intended only for standard mode.
* displayMode is specified in FormattedDisk.
*/
public List<String> getFileColumnData(int displayMode);
List<String> getFileColumnData(int displayMode);
/**
* Get file data. This handles any operating-system specific issues.
* Specifically, DOS 3.3 places address and length into binary files
* and length into Applesoft files.
*/
public byte[] getFileData();
byte[] getFileData();
/**
* Set file data. This, essentially, is saving data to disk using this
* file entry.
*/
public void setFileData(byte[] data) throws DiskFullException;
void setFileData(byte[] data) throws DiskFullException;
/**
* Get the suggested FileFilter. This appears to be operating system
* specific, so each operating system needs to implement some manner
* of guessing the appropriate filter.
*/
public FileFilter getSuggestedFilter();
FileFilter getSuggestedFilter();
/**
* Get the FormattedDisk associated with this FileEntry.
* This is useful to interfaces that need to retrieve the associated
* disk.
*/
public FormattedDisk getFormattedDisk();
FormattedDisk getFormattedDisk();
/**
* Return the maximum filename length.
*/
public int getMaximumFilenameLength();
int getMaximumFilenameLength();
/**
* Indicates if this filetype requires an address component.
@@ -124,15 +124,15 @@ public interface FileEntry {
* this will defer to the method on FormattedDisk, as it will be
* more generic.
*/
public abstract boolean needsAddress();
boolean needsAddress();
/**
* Set the address that this file loads at.
*/
public abstract void setAddress(int address);
void setAddress(int address);
/**
* Get the address that this file loads at.
*/
public abstract int getAddress();
int getAddress();
}
@@ -33,9 +33,9 @@ public interface FileFilter {
/**
* Process the given FileEntry and return a byte array with filtered data.
*/
public byte[] filter(FileEntry fileEntry);
byte[] filter(FileEntry fileEntry);
/**
* Give suggested file name.
*/
public String getSuggestedFileName(FileEntry fileEntry);
String getSuggestedFileName(FileEntry fileEntry);
}
@@ -80,10 +80,10 @@ public abstract class FormattedDisk implements DirectoryEntry, Container{
* set the value and isFree/isUsed are available for that location.
*/
public interface DiskUsage {
public boolean hasNext();
public void next();
public boolean isFree();
public boolean isUsed();
boolean hasNext();
void next();
boolean isFree();
boolean isUsed();
}
/**
@@ -22,7 +22,7 @@ package com.webcodepro.applecommander.storage.filters;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FileFilter;
import org.applecommander.os.pascal.CodeFile;
import org.applecommander.os.pascal.PascalSupport;
import org.applecommander.os.pascal.CodeFileLister;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -37,11 +37,11 @@ public class PascalCodeFileFilter implements FileFilter {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
PascalSupport.disassemble(pw, codeFile);
CodeFileLister lister = new CodeFileLister();
lister.list(pw, codeFile);
return sw.toString().getBytes();
}
/**
* Give suggested file name.
*/
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk;
@@ -229,7 +229,11 @@ public class DosFormatDisk extends FormattedDisk {
while (offset < 0xff) { // iterate through all entries
int value = AppleUtil.getUnsignedByte(catalogSector[offset]);
if (value == 0 || value == 0xff) {
return new DosFileEntry(this, track, sector, offset);
DosFileEntry fileEntry = new DosFileEntry(this, track, sector, offset);
byte[] entry = fileEntry.readFileEntry();
Arrays.fill(entry, (byte)0);
fileEntry.writeFileEntry(entry);
return fileEntry;
}
offset+= DosFileEntry.FILE_DESCRIPTIVE_ENTRY_LENGTH;
}
@@ -1458,13 +1458,14 @@ public class ProdosFormatDisk extends FormattedDisk {
@Override
public String toNativeFiletype(String prodosFiletype) {
return prodosFiletype;
};
/**
}
/**
* Provides conversation to a given ProDOS file type since as it is common across
* many archiving tools.
*/
@Override
public String toProdosFiletype(String nativeFiletype) {
return nativeFiletype;
};
}
}
@@ -23,7 +23,6 @@ import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import com.webcodepro.applecommander.storage.DiskFullException;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FileFilter;
import com.webcodepro.applecommander.storage.FormattedDisk;
@@ -82,12 +82,13 @@ public class DirectoryLister {
this.display = display;
}
public void first(FormattedDisk d) {};
public void beforeDisk(FormattedDisk d) {}
public void first(FormattedDisk d) {}
public void beforeDisk(FormattedDisk d) {}
public void afterDisk(FormattedDisk d) {}
public void forEach(FileTuple f) {}
public void last() {};
}
public void last() {}
}
public static class TextListingStrategy extends ListingStrategy {
protected TextListingStrategy(int display) {
@@ -26,111 +26,111 @@ package com.webcodepro.applecommander.util;
* @author Rob
*/
public interface ApplesoftTokens {
public static final byte END = (byte) 0x80;
public static final byte FOR = (byte) 0x81;
public static final byte NEXT = (byte) 0x82;
public static final byte DATA = (byte) 0x83;
public static final byte INPUT = (byte) 0x84;
public static final byte DEL = (byte) 0x85;
public static final byte DIM = (byte) 0x86;
public static final byte READ = (byte) 0x87;
public static final byte GR = (byte) 0x88;
public static final byte TEXT = (byte) 0x89;
public static final byte PRnbr = (byte) 0x8a;
public static final byte INnbr = (byte) 0x8b;
public static final byte CALL = (byte) 0x8c;
public static final byte PLOT = (byte) 0x8d;
public static final byte HLIN = (byte) 0x8e;
public static final byte VLIN = (byte) 0x8f;
public static final byte HGR2 = (byte) 0x90;
public static final byte HGR = (byte) 0x91;
public static final byte HCOLOR = (byte) 0x92;
public static final byte HPLOT = (byte) 0x93;
public static final byte DRAW = (byte) 0x94;
public static final byte XDRAW = (byte) 0x95;
public static final byte HTAB = (byte) 0x96;
public static final byte HOME = (byte) 0x97;
public static final byte ROT = (byte) 0x98;
public static final byte SCALE = (byte) 0x99;
public static final byte SHLOAD = (byte) 0x9a;
public static final byte TRACE = (byte) 0x9b;
public static final byte NOTRACE = (byte) 0x9c;
public static final byte NORMAL = (byte) 0x9d;
public static final byte INVERSE = (byte) 0x9e;
public static final byte FLASH = (byte) 0x9f;
public static final byte COLOR = (byte) 0xa0;
public static final byte POP = (byte) 0xa1;
public static final byte VTAB = (byte) 0xa2;
public static final byte HIMEM = (byte) 0xa3;
public static final byte LOMEM = (byte) 0xa4;
public static final byte ONERR = (byte) 0xa5;
public static final byte RESUME = (byte) 0xa6;
public static final byte RECALL = (byte) 0xa7;
public static final byte STORE = (byte) 0xa8;
public static final byte SPEED = (byte) 0xa9;
public static final byte LET = (byte) 0xaa;
public static final byte GOTO = (byte) 0xab;
public static final byte RUN = (byte) 0xac;
public static final byte IF = (byte) 0xad;
public static final byte RESTORE = (byte) 0xae;
public static final byte AMPERSAND = (byte) 0xaf;
public static final byte GOSUB = (byte) 0xb0;
public static final byte RETURN = (byte) 0xb1;
public static final byte REM = (byte) 0xb2;
public static final byte STOP = (byte) 0xb3;
public static final byte ON = (byte) 0xb4;
public static final byte WAIT = (byte) 0xb5;
public static final byte LOAD = (byte) 0xb6;
public static final byte SAVE = (byte) 0xb7;
public static final byte DEF = (byte) 0xb8;
public static final byte POKE = (byte) 0xb9;
public static final byte PRINT = (byte) 0xba;
public static final byte CONT = (byte) 0xbb;
public static final byte LIST = (byte) 0xbc;
public static final byte CLEAR = (byte) 0xbd;
public static final byte GET = (byte) 0xbe;
public static final byte NEW = (byte) 0xbf;
public static final byte TAB = (byte) 0xc0;
public static final byte TO = (byte) 0xc1;
public static final byte FN = (byte) 0xc2;
public static final byte SPC = (byte) 0xc3;
public static final byte THEN = (byte) 0xc4;
public static final byte AT = (byte) 0xc5;
public static final byte NOT = (byte) 0xc6;
public static final byte STEP = (byte) 0xc7;
public static final byte PLUS = (byte) 0xc8;
public static final byte MINUS = (byte) 0xc9;
public static final byte MULTIPLY = (byte) 0xca;
public static final byte DIVIDE = (byte) 0xcb;
public static final byte POWER = (byte) 0xcc;
public static final byte AND = (byte) 0xcd;
public static final byte OR = (byte) 0xce;
public static final byte GREATERTHAN = (byte) 0xcf;
public static final byte EQUALS = (byte) 0xd0;
public static final byte LESSTHAN = (byte) 0xd1;
public static final byte SGN = (byte) 0xd2;
public static final byte INT = (byte) 0xd3;
public static final byte ABS = (byte) 0xd4;
public static final byte USR = (byte) 0xd5;
public static final byte FRE = (byte) 0xd6;
public static final byte SCRN = (byte) 0xd7;
public static final byte PDL = (byte) 0xd8;
public static final byte POS = (byte) 0xd9;
public static final byte SQR = (byte) 0xda;
public static final byte RND = (byte) 0xdb;
public static final byte LOG = (byte) 0xdc;
public static final byte EXP = (byte) 0xdd;
public static final byte COS = (byte) 0xde;
public static final byte SIN = (byte) 0xdf;
public static final byte TAN = (byte) 0xe0;
public static final byte ATN = (byte) 0xe1;
public static final byte PEEK = (byte) 0xe2;
public static final byte LEN = (byte) 0xe3;
public static final byte STR$ = (byte) 0xe4;
public static final byte VAL = (byte) 0xe5;
public static final byte ASC = (byte) 0xe6;
public static final byte CHR$ = (byte) 0xe7;
public static final byte LEFT$ = (byte) 0xe8;
public static final byte RIGHT$ = (byte) 0xe9;
public static final byte MID$ = (byte) 0xea;
byte END = (byte) 0x80;
byte FOR = (byte) 0x81;
byte NEXT = (byte) 0x82;
byte DATA = (byte) 0x83;
byte INPUT = (byte) 0x84;
byte DEL = (byte) 0x85;
byte DIM = (byte) 0x86;
byte READ = (byte) 0x87;
byte GR = (byte) 0x88;
byte TEXT = (byte) 0x89;
byte PRnbr = (byte) 0x8a;
byte INnbr = (byte) 0x8b;
byte CALL = (byte) 0x8c;
byte PLOT = (byte) 0x8d;
byte HLIN = (byte) 0x8e;
byte VLIN = (byte) 0x8f;
byte HGR2 = (byte) 0x90;
byte HGR = (byte) 0x91;
byte HCOLOR = (byte) 0x92;
byte HPLOT = (byte) 0x93;
byte DRAW = (byte) 0x94;
byte XDRAW = (byte) 0x95;
byte HTAB = (byte) 0x96;
byte HOME = (byte) 0x97;
byte ROT = (byte) 0x98;
byte SCALE = (byte) 0x99;
byte SHLOAD = (byte) 0x9a;
byte TRACE = (byte) 0x9b;
byte NOTRACE = (byte) 0x9c;
byte NORMAL = (byte) 0x9d;
byte INVERSE = (byte) 0x9e;
byte FLASH = (byte) 0x9f;
byte COLOR = (byte) 0xa0;
byte POP = (byte) 0xa1;
byte VTAB = (byte) 0xa2;
byte HIMEM = (byte) 0xa3;
byte LOMEM = (byte) 0xa4;
byte ONERR = (byte) 0xa5;
byte RESUME = (byte) 0xa6;
byte RECALL = (byte) 0xa7;
byte STORE = (byte) 0xa8;
byte SPEED = (byte) 0xa9;
byte LET = (byte) 0xaa;
byte GOTO = (byte) 0xab;
byte RUN = (byte) 0xac;
byte IF = (byte) 0xad;
byte RESTORE = (byte) 0xae;
byte AMPERSAND = (byte) 0xaf;
byte GOSUB = (byte) 0xb0;
byte RETURN = (byte) 0xb1;
byte REM = (byte) 0xb2;
byte STOP = (byte) 0xb3;
byte ON = (byte) 0xb4;
byte WAIT = (byte) 0xb5;
byte LOAD = (byte) 0xb6;
byte SAVE = (byte) 0xb7;
byte DEF = (byte) 0xb8;
byte POKE = (byte) 0xb9;
byte PRINT = (byte) 0xba;
byte CONT = (byte) 0xbb;
byte LIST = (byte) 0xbc;
byte CLEAR = (byte) 0xbd;
byte GET = (byte) 0xbe;
byte NEW = (byte) 0xbf;
byte TAB = (byte) 0xc0;
byte TO = (byte) 0xc1;
byte FN = (byte) 0xc2;
byte SPC = (byte) 0xc3;
byte THEN = (byte) 0xc4;
byte AT = (byte) 0xc5;
byte NOT = (byte) 0xc6;
byte STEP = (byte) 0xc7;
byte PLUS = (byte) 0xc8;
byte MINUS = (byte) 0xc9;
byte MULTIPLY = (byte) 0xca;
byte DIVIDE = (byte) 0xcb;
byte POWER = (byte) 0xcc;
byte AND = (byte) 0xcd;
byte OR = (byte) 0xce;
byte GREATERTHAN = (byte) 0xcf;
byte EQUALS = (byte) 0xd0;
byte LESSTHAN = (byte) 0xd1;
byte SGN = (byte) 0xd2;
byte INT = (byte) 0xd3;
byte ABS = (byte) 0xd4;
byte USR = (byte) 0xd5;
byte FRE = (byte) 0xd6;
byte SCRN = (byte) 0xd7;
byte PDL = (byte) 0xd8;
byte POS = (byte) 0xd9;
byte SQR = (byte) 0xda;
byte RND = (byte) 0xdb;
byte LOG = (byte) 0xdc;
byte EXP = (byte) 0xdd;
byte COS = (byte) 0xde;
byte SIN = (byte) 0xdf;
byte TAN = (byte) 0xe0;
byte ATN = (byte) 0xe1;
byte PEEK = (byte) 0xe2;
byte LEN = (byte) 0xe3;
byte STR$ = (byte) 0xe4;
byte VAL = (byte) 0xe5;
byte ASC = (byte) 0xe6;
byte CHR$ = (byte) 0xe7;
byte LEFT$ = (byte) 0xe8;
byte RIGHT$ = (byte) 0xe9;
byte MID$ = (byte) 0xea;
}
@@ -26,111 +26,111 @@ package com.webcodepro.applecommander.util;
* @author Rob
*/
public interface BusinessBASICTokens {
public static final byte END = (byte) 0x80;
public static final byte FOR = (byte) 0x81;
public static final byte NEXT = (byte) 0x82;
public static final byte DATA = (byte) 0x83;
public static final byte INPUT = (byte) 0x84;
public static final byte DEL = (byte) 0x85;
public static final byte DIM = (byte) 0x86;
public static final byte READ = (byte) 0x87;
public static final byte GR = (byte) 0x88;
public static final byte TEXT = (byte) 0x89;
public static final byte PRnbr = (byte) 0x8a;
public static final byte INnbr = (byte) 0x8b;
public static final byte CALL = (byte) 0x8c;
public static final byte PLOT = (byte) 0x8d;
public static final byte HLIN = (byte) 0x8e;
public static final byte VLIN = (byte) 0x8f;
public static final byte HGR2 = (byte) 0x90;
public static final byte HGR = (byte) 0x91;
public static final byte HCOLOR = (byte) 0x92;
public static final byte HPLOT = (byte) 0x93;
public static final byte DRAW = (byte) 0x94;
public static final byte XDRAW = (byte) 0x95;
public static final byte HTAB = (byte) 0x96;
public static final byte HOME = (byte) 0x97;
public static final byte ROT = (byte) 0x98;
public static final byte SCALE = (byte) 0x99;
public static final byte SHLOAD = (byte) 0x9a;
public static final byte TRACE = (byte) 0x9b;
public static final byte NOTRACE = (byte) 0x9c;
public static final byte NORMAL = (byte) 0x9d;
public static final byte INVERSE = (byte) 0x9e;
public static final byte FLASH = (byte) 0x9f;
public static final byte COLOR = (byte) 0xa0;
public static final byte POP = (byte) 0xa1;
public static final byte VTAB = (byte) 0xa2;
public static final byte HIMEM = (byte) 0xa3;
public static final byte LOMEM = (byte) 0xa4;
public static final byte ONERR = (byte) 0xa5;
public static final byte RESUME = (byte) 0xa6;
public static final byte RECALL = (byte) 0xa7;
public static final byte STORE = (byte) 0xa8;
public static final byte SPEED = (byte) 0xa9;
public static final byte LET = (byte) 0xaa;
public static final byte GOTO = (byte) 0xab;
public static final byte RUN = (byte) 0xac;
public static final byte IF = (byte) 0xad;
public static final byte RESTORE = (byte) 0xae;
public static final byte AMPERSAND = (byte) 0xaf;
public static final byte GOSUB = (byte) 0xb0;
public static final byte RETURN = (byte) 0xb1;
public static final byte REM = (byte) 0xb2;
public static final byte STOP = (byte) 0xb3;
public static final byte ON = (byte) 0xb4;
public static final byte WAIT = (byte) 0xb5;
public static final byte LOAD = (byte) 0xb6;
public static final byte SAVE = (byte) 0xb7;
public static final byte DEF = (byte) 0xb8;
public static final byte POKE = (byte) 0xb9;
public static final byte PRINT = (byte) 0xba;
public static final byte CONT = (byte) 0xbb;
public static final byte LIST = (byte) 0xbc;
public static final byte CLEAR = (byte) 0xbd;
public static final byte GET = (byte) 0xbe;
public static final byte NEW = (byte) 0xbf;
public static final byte TAB = (byte) 0xc0;
public static final byte TO = (byte) 0xc1;
public static final byte FN = (byte) 0xc2;
public static final byte SPC = (byte) 0xc3;
public static final byte THEN = (byte) 0xc4;
public static final byte AT = (byte) 0xc5;
public static final byte NOT = (byte) 0xc6;
public static final byte STEP = (byte) 0xc7;
public static final byte PLUS = (byte) 0xc8;
public static final byte MINUS = (byte) 0xc9;
public static final byte MULTIPLY = (byte) 0xca;
public static final byte DIVIDE = (byte) 0xcb;
public static final byte POWER = (byte) 0xcc;
public static final byte AND = (byte) 0xcd;
public static final byte OR = (byte) 0xce;
public static final byte GREATERTHAN = (byte) 0xcf;
public static final byte EQUALS = (byte) 0xd0;
public static final byte LESSTHAN = (byte) 0xd1;
public static final byte SGN = (byte) 0xd2;
public static final byte INT = (byte) 0xd3;
public static final byte ABS = (byte) 0xd4;
public static final byte USR = (byte) 0xd5;
public static final byte FRE = (byte) 0xd6;
public static final byte SCRN = (byte) 0xd7;
public static final byte PDL = (byte) 0xd8;
public static final byte POS = (byte) 0xd9;
public static final byte SQR = (byte) 0xda;
public static final byte RND = (byte) 0xdb;
public static final byte LOG = (byte) 0xdc;
public static final byte EXP = (byte) 0xdd;
public static final byte COS = (byte) 0xde;
public static final byte SIN = (byte) 0xdf;
public static final byte TAN = (byte) 0xe0;
public static final byte ATN = (byte) 0xe1;
public static final byte PEEK = (byte) 0xe2;
public static final byte LEN = (byte) 0xe3;
public static final byte STR$ = (byte) 0xe4;
public static final byte VAL = (byte) 0xe5;
public static final byte ASC = (byte) 0xe6;
public static final byte CHR$ = (byte) 0xe7;
public static final byte LEFT$ = (byte) 0xe8;
public static final byte RIGHT$ = (byte) 0xe9;
public static final byte MID$ = (byte) 0xea;
byte END = (byte) 0x80;
byte FOR = (byte) 0x81;
byte NEXT = (byte) 0x82;
byte DATA = (byte) 0x83;
byte INPUT = (byte) 0x84;
byte DEL = (byte) 0x85;
byte DIM = (byte) 0x86;
byte READ = (byte) 0x87;
byte GR = (byte) 0x88;
byte TEXT = (byte) 0x89;
byte PRnbr = (byte) 0x8a;
byte INnbr = (byte) 0x8b;
byte CALL = (byte) 0x8c;
byte PLOT = (byte) 0x8d;
byte HLIN = (byte) 0x8e;
byte VLIN = (byte) 0x8f;
byte HGR2 = (byte) 0x90;
byte HGR = (byte) 0x91;
byte HCOLOR = (byte) 0x92;
byte HPLOT = (byte) 0x93;
byte DRAW = (byte) 0x94;
byte XDRAW = (byte) 0x95;
byte HTAB = (byte) 0x96;
byte HOME = (byte) 0x97;
byte ROT = (byte) 0x98;
byte SCALE = (byte) 0x99;
byte SHLOAD = (byte) 0x9a;
byte TRACE = (byte) 0x9b;
byte NOTRACE = (byte) 0x9c;
byte NORMAL = (byte) 0x9d;
byte INVERSE = (byte) 0x9e;
byte FLASH = (byte) 0x9f;
byte COLOR = (byte) 0xa0;
byte POP = (byte) 0xa1;
byte VTAB = (byte) 0xa2;
byte HIMEM = (byte) 0xa3;
byte LOMEM = (byte) 0xa4;
byte ONERR = (byte) 0xa5;
byte RESUME = (byte) 0xa6;
byte RECALL = (byte) 0xa7;
byte STORE = (byte) 0xa8;
byte SPEED = (byte) 0xa9;
byte LET = (byte) 0xaa;
byte GOTO = (byte) 0xab;
byte RUN = (byte) 0xac;
byte IF = (byte) 0xad;
byte RESTORE = (byte) 0xae;
byte AMPERSAND = (byte) 0xaf;
byte GOSUB = (byte) 0xb0;
byte RETURN = (byte) 0xb1;
byte REM = (byte) 0xb2;
byte STOP = (byte) 0xb3;
byte ON = (byte) 0xb4;
byte WAIT = (byte) 0xb5;
byte LOAD = (byte) 0xb6;
byte SAVE = (byte) 0xb7;
byte DEF = (byte) 0xb8;
byte POKE = (byte) 0xb9;
byte PRINT = (byte) 0xba;
byte CONT = (byte) 0xbb;
byte LIST = (byte) 0xbc;
byte CLEAR = (byte) 0xbd;
byte GET = (byte) 0xbe;
byte NEW = (byte) 0xbf;
byte TAB = (byte) 0xc0;
byte TO = (byte) 0xc1;
byte FN = (byte) 0xc2;
byte SPC = (byte) 0xc3;
byte THEN = (byte) 0xc4;
byte AT = (byte) 0xc5;
byte NOT = (byte) 0xc6;
byte STEP = (byte) 0xc7;
byte PLUS = (byte) 0xc8;
byte MINUS = (byte) 0xc9;
byte MULTIPLY = (byte) 0xca;
byte DIVIDE = (byte) 0xcb;
byte POWER = (byte) 0xcc;
byte AND = (byte) 0xcd;
byte OR = (byte) 0xce;
byte GREATERTHAN = (byte) 0xcf;
byte EQUALS = (byte) 0xd0;
byte LESSTHAN = (byte) 0xd1;
byte SGN = (byte) 0xd2;
byte INT = (byte) 0xd3;
byte ABS = (byte) 0xd4;
byte USR = (byte) 0xd5;
byte FRE = (byte) 0xd6;
byte SCRN = (byte) 0xd7;
byte PDL = (byte) 0xd8;
byte POS = (byte) 0xd9;
byte SQR = (byte) 0xda;
byte RND = (byte) 0xdb;
byte LOG = (byte) 0xdc;
byte EXP = (byte) 0xdd;
byte COS = (byte) 0xde;
byte SIN = (byte) 0xdf;
byte TAN = (byte) 0xe0;
byte ATN = (byte) 0xe1;
byte PEEK = (byte) 0xe2;
byte LEN = (byte) 0xe3;
byte STR$ = (byte) 0xe4;
byte VAL = (byte) 0xe5;
byte ASC = (byte) 0xe6;
byte CHR$ = (byte) 0xe7;
byte LEFT$ = (byte) 0xe8;
byte RIGHT$ = (byte) 0xe9;
byte MID$ = (byte) 0xea;
}
@@ -28,7 +28,7 @@ public enum TypeOfFile {
public final Predicate<FileTuple> predicate;
private TypeOfFile(Predicate<FileTuple> predicate) {
TypeOfFile(Predicate<FileTuple> predicate) {
this.predicate = predicate;
}
}
@@ -32,28 +32,28 @@ import com.webcodepro.applecommander.storage.os.rdos.RdosFileEntry;
public interface FileEntryReader {
// FileEntry common
public default Optional<String> getFilename() { return Optional.empty(); }
public default Optional<String> getProdosFiletype() { return Optional.empty(); }
public default Optional<Boolean> isLocked() { return Optional.empty(); }
public default Optional<byte[]> getFileData() { return Optional.empty(); }
public default Optional<byte[]> getResourceData() { return Optional.empty(); }
default Optional<String> getFilename() { return Optional.empty(); }
default Optional<String> getProdosFiletype() { return Optional.empty(); }
default Optional<Boolean> isLocked() { return Optional.empty(); }
default Optional<byte[]> getFileData() { return Optional.empty(); }
default Optional<byte[]> getResourceData() { return Optional.empty(); }
/**
* The address embedded in binary objects.
* This varies by DOS's so is split apart.
*/
public default Optional<Integer> getBinaryAddress() { return Optional.empty(); }
default Optional<Integer> getBinaryAddress() { return Optional.empty(); }
/**
* The length embedded in binary, Applesoft, Integer BASIC objects.
* This varies by DOS's so is split apart.
*/
public default Optional<Integer> getBinaryLength() { return Optional.empty(); }
default Optional<Integer> getBinaryLength() { return Optional.empty(); }
// ProdosFileEntry specific
public default Optional<Integer> getAuxiliaryType() { return Optional.empty(); }
public default Optional<Date> getCreationDate() { return Optional.empty(); }
default Optional<Integer> getAuxiliaryType() { return Optional.empty(); }
default Optional<Date> getCreationDate() { return Optional.empty(); }
// ProdosFileEntry / PascalFileEntry specific
public default Optional<Date> getLastModificationDate() { return Optional.empty(); }
default Optional<Date> getLastModificationDate() { return Optional.empty(); }
public default boolean equals(FileEntryReader reader) {
default boolean equals(FileEntryReader reader) {
return getFilename().equals(reader.getFilename())
&& getProdosFiletype().equals(reader.getProdosFiletype())
&& isLocked().equals(reader.isLocked())
@@ -66,7 +66,7 @@ public interface FileEntryReader {
&& getLastModificationDate().equals(reader.getLastModificationDate());
}
public static FileEntryReader get(FileEntry fileEntry) {
static FileEntryReader get(FileEntry fileEntry) {
if (fileEntry instanceof DosFileEntry) {
return new DosFileEntryReaderWriter((DosFileEntry)fileEntry);
}
@@ -28,29 +28,29 @@ import com.webcodepro.applecommander.storage.os.prodos.ProdosFileEntry;
public interface FileEntryWriter {
// FileEntry common
public default void setFilename(String filename) { }
public default void setProdosFiletype(String filetype) { }
public default void setLocked(boolean flag) { }
public default void setFileData(byte[] data) { }
default void setFilename(String filename) { }
default void setProdosFiletype(String filetype) { }
default void setLocked(boolean flag) { }
default void setFileData(byte[] data) { }
// Special case for GS/OS files (uglifies API; sets 0x05)
public default void setFileData(byte[] data, byte[] resource) { }
default void setFileData(byte[] data, byte[] resource) { }
/**
* The address embedded in binary objects.
* This varies by DOS's so is split apart.
*/
public default void setBinaryAddress(int address) { }
default void setBinaryAddress(int address) { }
/**
* The length embedded in binary, Applesoft, Integer BASIC objects.
* This varies by DOS's so is split apart.
*/
public default void setBinaryLength(int length) { }
default void setBinaryLength(int length) { }
// ProdosFileEntry specific
public default void setAuxiliaryType(int auxType) { }
public default void setCreationDate(Date date) { }
default void setAuxiliaryType(int auxType) { }
default void setCreationDate(Date date) { }
// ProdosFileEntry / PascalFileEntry specific
public default void setLastModificationDate(Date date) { }
default void setLastModificationDate(Date date) { }
public static FileEntryWriter get(FileEntry fileEntry) {
static FileEntryWriter get(FileEntry fileEntry) {
if (fileEntry instanceof DosFileEntry) {
return new DosFileEntryReaderWriter((DosFileEntry)fileEntry);
}
@@ -0,0 +1,176 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2025 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.applecommander.os.pascal;
import org.applecommander.disassembler.api.Disassembler;
import org.applecommander.disassembler.api.Instruction;
import org.applecommander.disassembler.api.InstructionSet;
import org.applecommander.disassembler.api.mos6502.InstructionSet6502;
import org.applecommander.disassembler.api.pcode.InstructionSetPCode;
import org.applecommander.util.DataBuffer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
/**
* Provide standardized list mechanism for Apple Pascal CODEFILE.
* This allows minimal configuration in the disassemble function for pcode and 6502.
*/
public class CodeFileLister {
private final DisassemblyFunc disassemblyFunc;
public CodeFileLister() {
this.disassemblyFunc = this::disassemble;
}
public CodeFileLister(DisassemblyFunc disassemblyFunc) {
this.disassemblyFunc = disassemblyFunc;
}
public void list(PrintWriter pw, CodeFile codeFile) {
// Reset components that are implied in the CodeFile itself:
if (codeFile.comment() != null && !codeFile.comment().isEmpty()) {
pw.printf("Comment: %s\n", codeFile.comment());
}
for (Segment segment : codeFile.segments()) {
if (segment != null) list(pw, segment);
}
}
public void list(PrintWriter pw, Segment segment) {
pw.printf(">> Seg #%02d: FROM=$%04x, TO=$%04x, N='%s', %-10s, T=$%04x, M=%-10s, Ver=%d\n",
segment.segNum(), segment.data().position(), segment.data().limit(), segment.name(),
segment.kind(), segment.textAddr(), segment.machineType(), segment.version());
if (segment.textInterface() != null && !segment.textInterface().isEmpty()) {
pw.println("> Interface text:");
pw.println(segment.textInterface().indent(5));
}
for (var proc : segment.dictionary()) {
if (proc == null) {
pw.println("> Invalid procedure header.");
continue;
}
switch (proc) {
case PCodeProcedure pcode -> list(pw, pcode);
case AssemblyProcedure asm -> list(pw, asm);
default -> throw new RuntimeException("Unexpected procedure type: " + proc.getClass().getName());
}
}
}
public void list(PrintWriter pw, PCodeProcedure pcode) {
pw.printf("> Proc#%d, Lex Lvl %d, Enter $%04x, Exit $%04x, Param %d, Data %d, JTAB=$%04x\n",
pcode.procNum(), pcode.lexLevel(), pcode.enterIC(), pcode.exitIC(),
pcode.paramsSize(), pcode.dataSize(), pcode.jumpTable());
// We want to indent the resulting assembly, so a temporary new PrintWriter so indentation can be applied
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter, true);
disassemblyFunc.disassemble(printWriter, InstructionSetPCode.forApplePascal(), pcode.enterIC(), pcode.codeBytes());
pw.println(stringWriter.toString().indent(5));
}
public void list(PrintWriter pw, AssemblyProcedure asm) {
pw.printf("> ASM Proc#%d, Relocation Segment #%d, Enter $%04x\n",
asm.procNum(), asm.relocSegNum(), asm.enterIC());
BiConsumer<int[], String> formatter = (table, name) -> {
if (table.length > 0) {
pw.printf("\t%s-relative relocation table: ", name);
for (int addr : table) pw.printf("$%04X ", addr);
pw.println();
}
};
formatter.accept(asm.baseRelativeReloc(), "base");
formatter.accept(asm.segRelativeReloc(), "segment");
formatter.accept(asm.procRelativeReloc(), "procedure");
formatter.accept(asm.interpRelativeReloc(), "interpreter");
var db = DataBuffer.wrap(asm.codeBytes());
for (int addr : asm.procRelativeReloc()) {
int offset = addr - asm.enterIC();
db.putUnsignedShort(offset, db.getUnsignedShort(offset) + asm.enterIC());
}
// We want to indent the resulting assembly, so a temporary new PrintWriter so indentation can be applied
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter, true);
disassemblyFunc.disassemble(printWriter, InstructionSet6502.for6502(), asm.enterIC(), db.asBytes());
pw.println(stringWriter.toString().indent(5));
}
public interface DisassemblyFunc {
void disassemble(PrintWriter pw, InstructionSet instructionSet, int startAddress, byte[] procedureCode);
}
public void disassemble(PrintWriter pw, InstructionSet instructionSet, int startAddress, byte[] procedureCode) {
Map<Integer, String> labels = new HashMap<>();
List<Instruction> assembly = Disassembler.with(procedureCode)
.startingAddress(startAddress)
.use(instructionSet)
.decode(labels);
final int bytesPerLine = instructionSet.defaults().bytesPerInstruction();
assembly.forEach(instruction -> {
pw.printf("%04X- ", instruction.address());
byte[] instructionCode = instruction.code();
for (int i = 0; i < bytesPerLine; i++) {
if (i >= instructionCode.length) {
pw.print(" ");
} else {
pw.printf("%02X ", instructionCode[i]);
}
}
pw.printf(" %-10.10s ", labels.getOrDefault(instruction.address(), ""));
pw.printf("%-5s ", instruction.mnemonic());
pw.printf("%-30s ", instruction.operands().stream().map(operand -> {
if (operand.address().isPresent() && labels.containsKey(operand.address().get())) {
return operand.format(labels.get(operand.address().get()));
} else {
return operand.format();
}
})
.collect(Collectors.joining(",")));
if (instructionSet.defaults().includeDescription()) {
instruction.description().ifPresent(description -> {
pw.printf("; %s", description);
});
}
pw.println();
if (instructionCode.length > bytesPerLine) {
for (int i = bytesPerLine; i < instructionCode.length; i++) {
if (i % bytesPerLine == 0) {
if (i > bytesPerLine) pw.println();
pw.printf("%04X- ", instruction.address() + i);
}
pw.printf("%02X ", instructionCode[i]);
}
pw.println();
}
});
}
}
@@ -19,21 +19,11 @@
*/
package org.applecommander.os.pascal;
import org.applecommander.disassembler.api.Disassembler;
import org.applecommander.disassembler.api.Instruction;
import org.applecommander.disassembler.api.InstructionSet;
import org.applecommander.disassembler.api.mos6502.InstructionSet6502;
import org.applecommander.disassembler.api.pcode.InstructionSetPCode;
import org.applecommander.util.DataBuffer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
/**
* Provides common Pascal support code.
*/
public class PascalSupport {
private PascalSupport() {
// prevent construction
@@ -62,126 +52,4 @@ public class PascalSupport {
}
return sb.toString();
}
public static void disassemble(PrintWriter pw, CodeFile codeFile) {
// Reset components that are implied in the CodeFile itself:
if (codeFile.comment() != null && !codeFile.comment().isEmpty()) {
pw.printf("Comment: %s\n", codeFile.comment());
}
for (Segment segment : codeFile.segments()) {
if (segment != null) disassemble(pw, segment);
}
}
public static void disassemble(PrintWriter pw, Segment segment) {
pw.printf(">> Seg #%02d: FROM=$%04x, TO=$%04x, N='%s', %-10s, T=$%04x, M=%-10s, Ver=%d\n",
segment.segNum(), segment.data().position(), segment.data().limit(), segment.name(),
segment.kind(), segment.textAddr(), segment.machineType(), segment.version());
if (segment.textInterface() != null && !segment.textInterface().isEmpty()) {
pw.println("> Interface text:");
pw.println(segment.textInterface().indent(5));
}
for (var proc : segment.dictionary()) {
if (proc == null) {
pw.println("> Invalid procedure header.");
continue;
}
switch (proc) {
case PCodeProcedure pcode -> disassemble(pw, pcode);
case AssemblyProcedure asm -> disassemble(pw, asm);
default -> throw new RuntimeException("Unexpected procedure type: " + proc.getClass().getName());
}
}
}
public static void disassemble(PrintWriter pw, PCodeProcedure pcode) {
pw.printf("> Proc#%d, Lex Lvl %d, Enter $%04x, Exit $%04x, Param %d, Data %d, JTAB=$%04x\n",
pcode.procNum(), pcode.lexLevel(), pcode.enterIC(), pcode.exitIC(),
pcode.paramsSize(), pcode.dataSize(), pcode.jumpTable());
// We want to indent the resulting assembly, so a temporary new PrintWriter so indentation can be applied
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter, true);
disassemble(printWriter, InstructionSetPCode.forApplePascal(), pcode.enterIC(), pcode.codeBytes());
pw.println(stringWriter.toString().indent(5));
}
public static void disassemble(PrintWriter pw, AssemblyProcedure asm) {
pw.printf("> ASM Proc#%d, Relocation Segment #%d, Enter $%04x\n",
asm.procNum(), asm.relocSegNum(), asm.enterIC());
BiConsumer<int[], String> formatter = (table, name) -> {
if (table.length > 0) {
pw.printf("\t%s-relative relocation table: ", name);
for (int addr : table) pw.printf("$%04X ", addr);
pw.println();
}
};
formatter.accept(asm.baseRelativeReloc(), "base");
formatter.accept(asm.segRelativeReloc(), "segment");
formatter.accept(asm.procRelativeReloc(), "procedure");
formatter.accept(asm.interpRelativeReloc(), "interpreter");
var db = DataBuffer.wrap(asm.codeBytes());
for (int addr : asm.procRelativeReloc()) {
int offset = addr - asm.enterIC();
db.putUnsignedShort(offset, db.getUnsignedShort(offset) + asm.enterIC());
}
// We want to indent the resulting assembly, so a temporary new PrintWriter so indentation can be applied
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter, true);
disassemble(printWriter, InstructionSet6502.for6502(), asm.enterIC(), db.asBytes());
pw.println(stringWriter.toString().indent(5));
}
public static void disassemble(PrintWriter pw, InstructionSet instructionSet, int startAddress, byte[] procedureCode) {
Map<Integer,String> labels = new HashMap<>();
List<Instruction> assembly = Disassembler.with(procedureCode)
.startingAddress(startAddress)
.use(instructionSet)
.decode(labels);
final int bytesPerLine = instructionSet.defaults().bytesPerInstruction();
assembly.forEach(instruction -> {
pw.printf("%04X- ", instruction.address());
byte[] instructionCode = instruction.code();
for (int i=0; i<bytesPerLine; i++) {
if (i >= instructionCode.length) {
pw.print(" ");
} else {
pw.printf("%02X ", instructionCode[i]);
}
}
pw.printf(" %-10.10s ", labels.getOrDefault(instruction.address(), ""));
pw.printf("%-5s ", instruction.mnemonic());
pw.printf("%-30s ", instruction.operands().stream().map(operand -> {
if (operand.address().isPresent() && labels.containsKey(operand.address().get())) {
return operand.format(labels.get(operand.address().get()));
}
else {
return operand.format();
}
})
.collect(Collectors.joining(",")));
if (instructionSet.defaults().includeDescription()) {
instruction.description().ifPresent(description -> {
pw.printf("; %s", description);
});
}
pw.println();
if (instructionCode.length > bytesPerLine) {
for (int i=bytesPerLine; i<instructionCode.length; i++) {
if (i % bytesPerLine == 0) {
if (i > bytesPerLine) pw.println();
pw.printf("%04X- ", instruction.address()+i);
}
pw.printf("%02X ", instructionCode[i]);
}
pw.println();
}
});
}
}
@@ -5,19 +5,19 @@
"pattern": "META-INF/MANIFEST.MF"
},
{
"pattern": "META-INF/services/*"
"pattern": "META-INF/services/.*"
},
{
"pattern": "com/webcodepro/applecommander/storage/os/prodos/*"
"pattern": "com/webcodepro/applecommander/storage/os/prodos/.*"
},
{
"pattern": "com/webcodepro/applecommander/storage/*"
"pattern": "com/webcodepro/applecommander/storage/.*"
},
{
"pattern": "com/webcodepro/applecommander/ui/*"
"pattern": "com/webcodepro/applecommander/ui/.*"
},
{
"pattern": "com/webcodepro/applecommander/util/*"
"pattern": "com/webcodepro/applecommander/util/.*"
}
]
},
@@ -27,7 +27,6 @@ import org.applecommander.source.Source;
import org.junit.jupiter.api.Test;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFullException;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -27,7 +27,6 @@ import org.applecommander.source.DataBufferSource;
import org.applecommander.source.Source;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFullException;
import org.junit.jupiter.api.Test;
public class PascalFormatDiskTest {
@@ -26,7 +26,6 @@ import org.applecommander.source.Source;
import org.junit.jupiter.api.Test;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFullException;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -19,7 +19,6 @@
*/
package com.webcodepro.applecommander.util.filestreamer;
import java.io.IOException;
import java.util.List;
import com.webcodepro.applecommander.storage.*;
@@ -21,7 +21,6 @@ package com.webcodepro.applecommander.ui.swt;
import com.webcodepro.applecommander.storage.filters.imagehandlers.AppleImage;
import java.io.IOException;
import java.io.OutputStream;
import org.eclipse.swt.SWT;
@@ -25,7 +25,7 @@ package com.webcodepro.applecommander.ui.swt.util.contentadapter;
* @author Rob Greene
*/
public interface ContentTypeAdapter {
public void print();
public void selectAll();
public void copy();
void print();
void selectAll();
void copy();
}
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Note: Credentials are in the netrc file
curl --netrc -v -X POST https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/org.applecommander
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
DIR=".bin"
if [ ! -d ${DIR} ]
then
mkdir ${DIR}
fi
CLTH="${DIR}/clth.jar"
if [ ! -f ${CLTH} ]
then
curl -o ${CLTH} -L https://github.com/a2geek/command-line-test-harness/releases/download/1.1/clth-1.1.jar
fi
java -jar ${CLTH} app/cli-tests/src/test/resources/{ac,acx}-config.yml
+1
View File
@@ -4,6 +4,7 @@ include 'lib:ac-api'
include 'lib:ac-swt-common'
include 'app:cli-ac'
include 'app:cli-acx'
include 'app:cli-tests'
include 'app:ant-ac'
include 'app:gui-swt-macosx-aarch64'
include 'app:gui-swt-macosx-x86_64'