* Add tests for Applesoft compiler in preparation for refactoring
While refactoring the compiler, I found several small bugs:
* Lower-case letters in strings and REM statements were converted
to upper-case.
* Lines are stored in the order received, not sorted by line number.
* Does not prefer `ATN` to `AT`.
* Does not prefer `TO` to `AT`.
* `DATA` statements don't preserve spaces.
* `DATA` statements don't preserve lowercase.
These will be fixed in the upcoming refactoring.
* Refactor the Applesoft Compiler
Before, the compiler had a few bugs that were not trivial to solve
because the implementation was in one heavily-nested function.
In this refactoring of the compiler, things like tokenization have
been split into separate methods which makes them a bit easier to
understand.
This refactoring also passes all of the tests.
* Set `PRGEND` when compiling to memory
Before, `PRGEND` was not adjusted which made round-tripping from
the Applesoft compiler to the decompiler not work. This change
now updates `PRGEND` with the end-of-program + 2 bytes which seems
to be the most frequent value that I have observed.
* Fix two compiler bugs
In debugging the decompiler, I noticed two bugs in the compiler:
* The first character after a line number was skipped.
* `?` was not accepted as a shortcut for `PRINT`.
This change fixes these two problems and adds tests.
* Ignore spaces more aggressively
It turns out that Applesoft happily accepts 'T H E N' for `THEN`
but the parser did not. This change fixes that and adds tests for
some odd cases.
Interestingly, this means that there are some valid statements
that Applesoft can never parse correctly because it is greedy
and ignores (most) spaces. For example, `NOT RACE` will always
parse as `NOTRACE` even though `NOT RACE` is a valid expression.
* Move tokens into a separate file
Because the token lists are just maps in opposite directions, put
them in the same file. In the future, maybe we can build one
automatically.
* Fix `apple2.ts`
I had neglected to actually update `apple2.ts` to use the new
compiler and decompiler. They now do.
Also, the decompiler can be created from `Memory`. It assumes,
though, that the zero page pointers to the start and end of the
program are correct.
* Address comments
* No more `as const` for tokens.
* Extracted zero page constants to their own file.
Co-authored-by: Will Scullin <scullin@scullin.com>
* Add a basic write test for WOZ images
The new test just tries to change some random nibbles at the beginning
of the image and then verifies that the change has been recorded.
This exposed a bug where `q7` was never set to `true` when write mode
was toggled on.
Also, the assumptions and limitations of `moveHead` are more clearly
documented.
* Address comments
* Improved `moveHead` documentation a bit more.
* Removed redundant variable in `readNibble`.
* Refactored `findSector` and commented out the chatty log line.
All tests pass. No lint warnings.
* Add a test for the dirty callback on writes
This new test just checks that a clean disk becomes dirty after a
write _and_ that the dirty callback is fired.
* Add tests for WOZ disks
The new tests verify the basic read behavior of the state sequencer on
well-behaved disks, including sync bytes and so on. Write tests are
still to come.
There's also a change to the Woz format to return the info chunk data
as well.
* Add tests for the DiskII card
This change adds basic read tests for nibble-based disks for the
DiskII card and fixes a few minor errors.
These tests are in preparation for refactoring.
* Add write tests
These are some basic tests of writing to nibble disks. In the process,
one minor bug was found, fixed and documented.
* Fix the write tests
I misinterpreted something from Sather and thought that the high bit
had to be set on the data for writing to happen at all. This is not
true. Instead, there is a flux transition every time the high bit is
set as the data is left-shifted out of the data register. The
erroneous test has been removed.
At the same time, I finally understand what `skip` does and documented
that.
* Add tests for saving and restoring Disk II state
These are not exhaustive tests, but they ensure that some basic state
is saved and restored.
* Add `spawn` as a way of calling promise-returning blocks
This change adds `spawn` which takes a no-argument, promise-returning
function, calls it, and returns `void`. This makes it easy to call
async blocks from `useEffect` and other places that don't take async
functions, but also makes such calls explicit.
* Adds interruptability to `spawn`
Now, the task function passed to `spawn` can take an `Interrupted`
argument, which is merely a method that returns `true` if the task
should stop doing work. Likewise, `spawn` returns an `Interrupt`
function that causes the `Interrupted` function to return `true`.
* Change to using `AbortController` and `AbortSignal`
Before, `spawn` used functions to interrupt and determine interruption
state. Now, based on feedback from @whscullin, it uses
`AbortController` and `AbortSignal`.
Tests now show how the controller can be used to abort long-running
tasks and API calls in the `spawn`. The also show how signals can be
chained using `addEventListener`.
* Fix `Apple2.tsx`
Forgot to change it to use `AbortController` and `AbortSignal`.
Co-authored-by: Will Scullin <scullin@scullin.com>
This change adds `spawn` which takes a no-argument, promise-returning
function, calls it, and returns `void`. This makes it easy to call
async blocks from `useEffect` and other places that don't take async
functions, but also makes such calls explicit.
Before, the offset for `FLAGS` in `2mg.ts` was `0x0A`, which is
incorrect according to the spec at:
https://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
Now, all of the fields in the 2mg header are described, including
their lengths and any constraints. These constraints are enforced by
`read2MGHeader` and tested by new tests.
This removes the `FileSystemFileHandleLike` interface in preference to
just implementing the correct interface. The advantage of the
`FileSystemFileHandle` interface is that it can be passed to the
worker directly to load the file.
This adds both the recommended TypeScript checks, plus the recommended
TypeScript checks that require type checking. This latter addition
means that eslint essentially has to compile all of the TypeScript in
the project, causing it to be slower. This isn't much of a problem in
VS Code because there's a lot of caching being done, but it's clearly
slower when run on the commandline.
All of the errors are either fixed or suppressed. Some errors are
suppressed because fixing them would be too laborious for the little
value gained.
The eslint config is also slightly refactored to separate the strictly
TypeScript checks from the JavaScript checks.
* Create a FileChooser component using showOpenFilePicker
Before, `FileModal` always used a file input control for selecting
local files. This allowed the emulator to read from the file, but
precluded writing back to the file.
With this change, the `FileModal` delegates to the new `FileChooser`
component. The `FileChooser` will use `showOpenFilePicker` if it is
available and a regular file input if it's not.
Using `showOpenFilePicker` has the advantage of allowing the emulator
to write back to the file (if the user grants permission). While the
emulator does not yet take advantage of this write capability, that
will come.
* Addressed comments
* useState() instead of direct DOM manipulation
* backed out eslint changes in favor of suppressing the warning