Compare commits

...

25 Commits

Author SHA1 Message Date
A2 Geek
5981f65db1
Merge pull request #30 from AppleCommander/dependabot/gradle/net.sf.applecommander-AppleCommander-1.9.0
Bump net.sf.applecommander:AppleCommander from 1.8.0 to 1.9.0
2023-11-08 23:57:41 -06:00
dependabot[bot]
459394d1a9
Bump net.sf.applecommander:AppleCommander from 1.8.0 to 1.9.0
Bumps [net.sf.applecommander:AppleCommander](https://github.com/AppleCommander/AppleCommander) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/AppleCommander/AppleCommander/releases)
- [Commits](https://github.com/AppleCommander/AppleCommander/compare/1.8.0...1.9.0)

---
updated-dependencies:
- dependency-name: net.sf.applecommander:AppleCommander
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 00:13:48 +00:00
Rob Greene
3d936a435c Fixing Maven publish artifactId. 2023-10-31 18:27:37 -05:00
Rob Greene
797224c65a Bumping minor version since there is an enhancement. 2023-10-30 12:59:39 -05:00
Rob Greene
87336e7d97 Fixing typo. 2023-10-30 12:59:09 -05:00
Rob Greene
c55b15b505 Merge remote-tracking branch 'origin/master' 2023-10-30 12:53:57 -05:00
Rob Greene
5e72a68c42 Adding a "wrapper" capability to help with DOS rewriting application. #24 2023-10-30 12:53:33 -05:00
Rob Greene
778c094b7b Adding a "wrapper" capability to help with DOS rewriting application. #124 2023-10-29 22:43:32 -05:00
Rob Greene
089356a3a2 Adding note regarding DOS vs ProDOS for '$embed'. #24 2023-10-29 14:38:15 -05:00
Rob Greene
02269e8abb Using the same version of Gradle across all AppleCommander projects. #21 2023-10-29 14:35:21 -05:00
Rob Greene
ca87be4733 Updating version of Picocli. 2023-10-29 13:55:46 -05:00
A2 Geek
848468ed21
Merge pull request #26 from AppleCommander/dependabot/gradle/net.sf.applecommander-AppleCommander-1.8.0
Bump net.sf.applecommander:AppleCommander from 1.4.0 to 1.8.0
2023-10-29 13:48:36 -05:00
A2 Geek
f61fdbac27
Create gradle.yml 2023-10-29 13:45:29 -05:00
Rob Greene
d84d4ca1e8 Correcting call when moving to call the move code instead of the embedded code. Related to #24. 2023-10-29 13:41:44 -05:00
Rob Greene
81e1fa0ce3 Bumping to next snapshot version. 2023-10-29 13:41:08 -05:00
dependabot[bot]
8e8bf72618
Bump net.sf.applecommander:AppleCommander from 1.4.0 to 1.8.0
Bumps [net.sf.applecommander:AppleCommander](https://github.com/AppleCommander/AppleCommander) from 1.4.0 to 1.8.0.
- [Release notes](https://github.com/AppleCommander/AppleCommander/releases)
- [Commits](https://github.com/AppleCommander/AppleCommander/commits/1.8.0)

---
updated-dependencies:
- dependency-name: net.sf.applecommander:AppleCommander
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-28 17:54:35 +00:00
A2 Geek
baabfead2c
Merge pull request #25 from AppleCommander/dependabot/gradle/net.sf.applecommander-applesingle-api-1.2.2
Bump net.sf.applecommander:applesingle-api from 1.2.1 to 1.2.2
2023-10-28 12:53:45 -05:00
A2 Geek
d59a033550
Merge pull request #29 from AppleCommander/dependabot/gradle/junit-junit-4.13.2
Bump junit:junit from 4.12 to 4.13.2
2023-10-28 12:53:19 -05:00
dependabot[bot]
013790a610
Bump junit:junit from 4.12 to 4.13.2
Bumps [junit:junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.2.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.2)

---
updated-dependencies:
- dependency-name: junit:junit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-28 17:51:54 +00:00
dependabot[bot]
9f657dc531
Bump net.sf.applecommander:applesingle-api from 1.2.1 to 1.2.2
Bumps [net.sf.applecommander:applesingle-api](https://github.com/AppleCommander/applesingle) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/AppleCommander/applesingle/releases)
- [Commits](https://github.com/AppleCommander/applesingle/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: net.sf.applecommander:applesingle-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-28 17:51:37 +00:00
A2 Geek
06eddc96c6
Create dependabot.yml 2023-10-28 12:51:19 -05:00
A2 Geek
814f199782
Merge pull request #22 from ryandesign/patch-1
Fix typo in README-TOKENIZER.md
2021-10-29 23:06:31 -05:00
Ryan Schmidt
9fbbcc5b81
Fix typo in README-TOKENIZER.md 2021-10-01 18:08:36 -05:00
Rob Greene
bcfa634f7b Marking most of this class as private to prevent API poisoning.
Preparation for #43.
2020-11-25 12:38:31 -06:00
Rob Greene
71f28fccc0 First round of updates. Leaving publication and the mainClassName
reference for later. #21
2020-11-24 19:36:34 -06:00
18 changed files with 446 additions and 250 deletions

11
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

44
.github/workflows/gradle.yml vendored Normal file
View File

@ -0,0 +1,44 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Build bt and st
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: build
- name: Collect artifacts
run: |
mkdir artifacts
cp $(find tools/ -name "bastools-*.jar" -not -name "bastools-*-plain.jar") artifacts/
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: artifacts/
if-no-files-found: error

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
}
}

View File

@ -8,14 +8,6 @@ Generally, the usage pattern is:
## Code snippets
```java
Configuration config = Configuration.builder()
.sourceFile(this.sourceFile)
.build();
```
The `Configuration` class also allows the BASIC start address to be set (defaults to `0x801`), set the maximum line length (this is in bytes, and defaults to `255`, but feel free to experiment). Some of the classes report output via the debug stream, which defaults to a simple null stream (no output) - replace with `System.out` or another `PrintStream`.
```java
Queue<Token> tokens = TokenReader.tokenize(config.sourceFile);
```
@ -29,12 +21,29 @@ Program program = parser.parse();
The `Program` is now the parsed version of the BASIC program. Various `Visitor`s may be used to report, gather information, or manipulate the tree in various ways.
```java
Configuration config = Configuration.builder()
.sourceFile(this.sourceFile)
.build();
```
The `Configuration` class also allows the BASIC start address to be set (defaults to `0x801`), set the maximum line length (this is in bytes, and defaults to `255`, but feel free to experiment). Some of the classes report output via the debug stream, which defaults to a simple null stream (no output) - replace with `System.out` or another `PrintStream`.
```java
ByteVisitor byteVisitor = Visitors.byteVisitor(config);
byte[] programData = byteVisitor.dump(program);
```
Finally, the ByteVisitor will transform the program into the tokenized form.
## Directives
The framework allows embedding of directives.
### `$embed`
> NOTE: It appears that DOS 3.3 _rewrites_ the resulting application and messes up the linked list of lines. ProDOS does not.
`$embed` will allow a binary to be embedded within the resulting application and can move it to a destination in memory. Please note that once the application is loaded on the Apple II, the program cannot be altered as the computer will crash.
Options:
@ -57,7 +66,7 @@ From the `circles-timing.bas` sample, this is the beginning of the program:
```
0801:9A 09 00 00 8C 32 30 36 32 3A AB 31 00 A9 2B 85
\___/ \___/ \____________/ \___/ \_______...
Ptr, Line 0, CALL 3062, :, GOTO 1, Assembly code...
Ptr, Line 0, CALL 2062, :, GOTO 1, Assembly code...
```
The move code is based on what Beagle Bros put into their [Peeks, Pokes, and Pointers](https://beagle.applearchives.com/Posters/Poster%202.pdf) poster. (See _Memory Move_ under the *Useful Calls*; the `CALL -468` entry.)

View File

@ -1,81 +1,95 @@
repositories {
jcenter()
plugins {
id 'java-library'
id 'maven-publish'
id 'signing'
}
apply plugin: 'java-library'
apply plugin: 'maven'
apply plugin: 'signing'
ext.isSnapshotVersion = version.endsWith("SNAPSHOT")
ext.isReleaseVersion = !ext.isSnapshotVersion
sourceCompatibility = 11
targetCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.2'
}
jar {
manifest {
attributes(
'Implementation-Title': 'B/BAS Tools API',
'Implementation-Version': "${version} (${new Date().format('yyyy-MM-dd HH:mm')})"
'Implementation-Version': "${project.version} (${new Date().format('yyyy-MM-dd HH:mm')})"
)
}
}
javadoc {
title = "bastools ${version}"
source = sourceSets.main.allJava
options.addStringOption('Xdoclint:none', '-quiet')
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
archiveClassifier = 'javadoc'
from javadoc
}
task sourcesJar(type: Jar) {
classifier = 'sources'
archiveClassifier = 'sources'
from sourceSets.main.allSource
}
artifacts {
archives javadocJar, sourcesJar
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
pom {
groupId = "net.sf.applecommander"
artifactId = "bastools-api"
name = "B/BAS Tools (bastools)"
description = 'An Applesoft BASIC tools library.'
url = 'https://applecommander.github.io/'
licenses {
license {
name = 'The GNU General Public License (GPL) Version 3, 29 June 2007'
url = 'https://www.gnu.org/licenses/gpl-3.0.html'
}
}
developers {
developer {
id = 'robgreene'
name = 'Rob Greene'
email = 'robgreene@gmail.com'
}
}
scm {
connection = 'scm:git:https://github.com/AppleCommander/bastools.git'
developerConnection = 'scm:git:git@github.com:AppleCommander/bastools.git'
url = 'https://github.com/AppleCommander/bastools'
}
}
repositories {
maven {
def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
url = isSnapshotVersion ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = findProperty('ossrhUsername')
password = findProperty('ossrhPassword')
}
}
}
}
}
}
signing {
// Only sign if we're uploading...
required { gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
sign publishing.publications.mavenJava
}
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: findProperty('ossrhUsername'), password: findProperty('ossrhPassword'))
}
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: findProperty('ossrhUsername'), password: findProperty('ossrhPassword'))
}
pom.project {
name archivesBaseName
packaging 'jar'
description 'An Applesoft BASIC tools library.'
url 'https://applecommander.github.io/'
scm {
url 'https://github.com/AppleCommander/bastools'
}
licenses {
license {
name 'The GNU General Public License (GPL) Version 3, 29 June 2007'
url 'https://www.gnu.org/licenses/gpl-3.0.html'
}
}
developers {
developer {
id 'robgreene'
email 'robgreene@gmail.com'
}
}
}
}
}
}

View File

@ -18,7 +18,12 @@ import io.github.applecommander.bastools.api.model.Token;
/**
* The TokenReader, given a text file, generates a series of Tokens (in the compiler sense,
* not AppleSoft) for the AppleSoft program.
*
* <p/>
* Note that this relies on the Java StreamTokenizer. The goal is to provide a more modern
* parsing of tokens (in which it succeeds), however, AppleSoft itself is dated and mixes with
* what a modern syntax would consist of. Specifically, the '#' is out of place as part of a
* token and not a syntax element. Hence there is some funny business embedded in the code.
*
* @author rob
*/
public class TokenReader {
@ -57,16 +62,16 @@ public class TokenReader {
return tokens;
}
public TokenReader(Reader reader) {
private TokenReader(Reader reader) {
this.reader = reader;
this.tokenizer = ApplesoftKeyword.tokenizer(reader);
}
public boolean hasMore() {
private boolean hasMore() {
return hasMore;
}
public Optional<Token> next(int depth) throws IOException {
private Optional<Token> next(int depth) throws IOException {
// A cheesy attempt to prevent too much looping...
if (depth > 0) {
if (this.needSyntheticEol) {

View File

@ -37,6 +37,7 @@ public class EmbeddedBinaryDirective extends Directive {
byte[] bin = Files.readAllBytes(file.toPath());
CodeBuilder builder = new CodeBuilder();
CodeMark moveStart = new CodeMark();
CodeMark embeddedStart = new CodeMark();
CodeMark embeddedEnd = new CodeMark();
@ -48,7 +49,7 @@ public class EmbeddedBinaryDirective extends Directive {
targetAddress.ifPresent(address -> {
builder.basic()
.CALL(embeddedStart)
.CALL(moveStart)
.endStatement();
Optional<Line> nextLine = line.nextLine();
@ -64,8 +65,10 @@ public class EmbeddedBinaryDirective extends Directive {
builder.basic()
.endLine();
targetAddress.ifPresent(address -> {
builder.asm()
builder.set(moveStart)
.asm()
.setAddress(embeddedStart, 0x3c)
.setAddress(embeddedEnd, 0x3e)
.setAddress(address, 0x42)

View File

@ -63,6 +63,7 @@ public class ByteVisitor implements Visitor {
ByteArrayOutputStream os = stack.peek();
os.write(0x00);
os.write(0x00);
this.address += 2;
return program;
}

View File

@ -1,7 +1,7 @@
# Universal bastools version number. Used for:
# - Naming JAR file.
# - The build will insert this into a file that is read at run time as well.
version=0.3.1
version=0.4.0
# Maven Central Repository G and A of GAV coordinate. :-)
group=net.sf.applecommander

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

286
gradlew vendored
View File

@ -1,78 +1,129 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@ -89,84 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View File

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@ -1,47 +1,47 @@
## Usage
```shell
$ bt
Missing required parameter: <sourceFile>
$ bt --help
Usage: bt [-chOVx] [--addresses] [--applesingle] [--debug] [--list] [--pretty]
[--stdout] [--tokens] [--variables]
[--max-line-length=<maxLineLength>] [-a=<address>] [-o=<outputFile>]
[--stdout] [--tokens] [--variables] [--wrapper] [-a=<address>]
[--max-line-length=<maxLineLength>] [-o=<outputFile>]
[-f=<optimizations>[,<optimizations>...]]... <sourceFile>
Transforms an AppleSoft program from text back to its tokenized state.
<sourceFile> AppleSoft BASIC program to process.
Options:
--addresses Dump line number addresses out.
--applesingle Write output in AppleSingle format
--debug Print debug output.
--list List structure as bastools understands it.
--max-line-length=<maxLineLength>
Maximum line length for generated lines.
Default: 255
--pretty Pretty print structure as bastools understands it.
--stdout Send binary output to stdout.
--tokens Dump token list to stdout for debugging.
--variables Generate a variable report
-a, --address=<address> Base address for program
Default: 2049
-c, --copy Generate a copy/paste form of output for testing in an
emulator.
-f= <optimizations>[,<optimizations>...]
--addresses Dump line number addresses out.
--applesingle Write output in AppleSingle format
-c, --copy Generate a copy/paste form of output for testing in
an emulator.
--debug Print debug output.
-f=<optimizations>[,<optimizations>...]
Enable specific optimizations.
* remove-empty-statements - Strip out all '::'-like
statements.
* remove-rem-statements - Remove all REM statements.
* shorten-variable-names - Ensure all variables are 1 or
2 characters long.
* extract-constant-values - Assign all constant values
first.
* shorten-variable-names - Ensure all variables are
1 or 2 characters long.
* extract-constant-values - Assign all constant
values first.
* merge-lines - Merge lines.
* renumber - Renumber program.
-h, --help Show this help message and exit.
--list List structure as bastools understands it.
--max-line-length=<maxLineLength>
Maximum line length for generated lines.
Default: 255
-o, --output=<outputFile> Write binary output to file.
-O, --optimize Apply all optimizations.
--pretty Pretty print structure as bastools understands it.
--stdout Send binary output to stdout.
--tokens Dump token list to stdout for debugging.
-V, --version Print version information and exit.
--variables Generate a variable report
--wrapper Wrap the Applesoft program (DOS 3.3).
-x, --hex Generate a binary hex dump for debugging.
```
@ -153,3 +153,13 @@ demo.dsk /DEMO/
ProDOS format; 139,264 bytes free; 4,096 bytes used.
```
## Wrapping the application
DOS 3.3 (but not ProDOS) seems to rewrite the application linked list when an Applesoft program is loaded; this rewrites the pointers and impacts any embedded (via `$embed`) machine code. With the wrapper, the application is "wrapped" with a startup Applesoft program that prevents the rewrite. The wrapper is just a simple program:
```basic
10 POKE 103,24:POKE 104,8:RUN
```
This is a valid program that resets the Applesoft pointer to just after the current program and runs that other program.

View File

@ -1,26 +1,29 @@
plugins {
id 'org.springframework.boot' version '2.0.2.RELEASE'
id 'org.springframework.boot' version "2.7.17"
id 'java'
id 'application'
}
sourceCompatibility = 11
targetCompatibility = 11
repositories {
jcenter()
mavenCentral()
}
apply plugin: 'application'
mainClassName = "io.github.applecommander.bastools.tools.bt.Main"
bootJar {
manifest {
attributes(
'Implementation-Title': 'BT CLI',
'Implementation-Version': "${version} (${new Date().format('yyyy-MM-dd HH:mm')})"
'Implementation-Version': "${project.version} (${new Date().format('yyyy-MM-dd HH:mm')})"
)
}
}
dependencies {
compile 'info.picocli:picocli:3.0.2'
compile 'net.sf.applecommander:applesingle-api:1.2.1'
compile project(':bastools-api')
implementation 'info.picocli:picocli:4.7.5'
implementation 'net.sf.applecommander:applesingle-api:1.2.2'
implementation project(':bastools-api')
}

View File

@ -1,10 +1,6 @@
package io.github.applecommander.bastools.tools.bt;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
@ -74,6 +70,9 @@ public class Main implements Callable<Void> {
@Option(names = "--max-line-length", description = "Maximum line length for generated lines.", showDefaultValue = Visibility.ALWAYS)
private int maxLineLength = 255;
@Option(names = "--wrapper", description = "Wrap the Applesoft program (DOS 3.3).")
private boolean wrapProgram;
@Option(names = "-f", converter = OptimizationTypeConverter.class, split = ",", description = {
"Enable specific optimizations.",
@ -103,7 +102,8 @@ public class Main implements Callable<Void> {
public static void main(String[] args) throws FileNotFoundException, IOException {
try {
CommandLine.call(new Main(), args);
int exitCode = new CommandLine(new Main()).execute(args);
System.exit(exitCode);
} catch (Throwable t) {
if (Main.debugFlag) {
t.printStackTrace(System.err);
@ -173,17 +173,34 @@ public class Main implements Callable<Void> {
}
ByteVisitor byteVisitor = Visitors.byteVisitor(config);
byte[] data = byteVisitor.dump(program);
byte[] wrapperData = new byte[0];
if (wrapProgram) {
Queue<Token> wrapperTokens = TokenReader.tokenize(new ByteArrayInputStream(
"10 POKE 103,24:POKE 104,8:RUN".getBytes()));
Parser wrapperParser = new Parser(wrapperTokens);
Program wrapperProgram = wrapperParser.parse();
wrapperData = byteVisitor.dump(wrapperProgram);
}
byte[] programData = byteVisitor.dump(program);
if (showLineAddresses) {
byteVisitor.getLineAddresses().forEach((l,a) -> System.out.printf("%5d ... $%04x\n", l, a));
}
// Merge both programs together. Note that wrapperData may be a 0 byte array.
ByteArrayOutputStream output = new ByteArrayOutputStream();
output.write(wrapperData);
output.write(programData);
output.flush();
byte[] data = output.toByteArray();
if (hexFormat) {
HexDumper.standard().dump(address, data);
}
if (copyFormat) {
HexDumper.apple2().dump(address, data);
}
saveResults(data);
}

View File

@ -1,27 +1,30 @@
plugins {
id 'org.springframework.boot' version '2.0.2.RELEASE'
id 'org.springframework.boot' version "2.7.17"
id 'java'
id 'application'
}
sourceCompatibility = 11
targetCompatibility = 11
repositories {
jcenter()
mavenCentral()
}
apply plugin: 'application'
mainClassName = "io.github.applecommander.bastools.tools.st.Main"
bootJar {
manifest {
attributes(
'Implementation-Title': 'Shape Tools CLI',
'Implementation-Version': "${version} (${new Date().format('yyyy-MM-dd HH:mm')})"
'Implementation-Version': "${project.version} (${new Date().format('yyyy-MM-dd HH:mm')})"
)
}
}
dependencies {
compile 'info.picocli:picocli:3.0.2'
compile 'net.sf.applecommander:applesingle-api:1.2.1'
compile 'net.sf.applecommander:AppleCommander:1.4.0'
compile project(':bastools-api')
implementation 'info.picocli:picocli:4.7.5'
implementation 'net.sf.applecommander:applesingle-api:1.2.2'
implementation 'net.sf.applecommander:AppleCommander:1.9.0'
implementation project(':bastools-api')
}

View File

@ -26,7 +26,8 @@ public class Main implements Runnable {
public static void main(String[] args) {
try {
CommandLine.run(new Main(), args);
int exitCode = new CommandLine(new Main()).execute(args);
System.exit(exitCode);
} catch (Throwable t) {
if (Main.debugFlag) {
t.printStackTrace(System.err);