Skip to content

Commit

Permalink
Adding a "wrapper" capability to help with DOS rewriting application.…
Browse files Browse the repository at this point in the history
… #124
  • Loading branch information
a2geek committed Oct 30, 2023
1 parent 089356a commit 778c094
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 36 deletions.
23 changes: 15 additions & 8 deletions api/README-TOKENIZER.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
Expand All @@ -29,6 +21,21 @@ 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public Program visit(Program program) {
ByteArrayOutputStream os = stack.peek();
os.write(0x00);
os.write(0x00);
this.address += 2;
return program;
}

Expand Down
52 changes: 31 additions & 21 deletions tools/bt/README.md
Original file line number Diff line number Diff line change
@@ -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:
-a, --address=<address> Base address for program
Default: 2049
--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.
--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>...]
-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.
```
Expand Down Expand Up @@ -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 to Applesoft pointer to just after the current program and runs that other program.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -174,17 +173,34 @@ public void process(Configuration config) throws FileNotFoundException, IOExcept
}

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);
}

Expand Down

0 comments on commit 778c094

Please sign in to comment.