Adding a "wrapper" capability to help with DOS rewriting application. #24

This commit is contained in:
Rob Greene 2023-10-29 22:43:32 -05:00
parent 089356a3a2
commit 5e72a68c42
4 changed files with 71 additions and 37 deletions

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,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.

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,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 to Applesoft pointer to just after the current program and runs that other program.

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.",
@ -174,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);
}