# Jupiter ACE Forth interpreter

This repository is the home of three command-line programs:

- **JAForth.py**: An interpreter for the Jupiter ACE Forth, written in Python, that can also compile to a Jupiter-format or Spectrum-format `.TAP` tape file.
- **jforth**: A headless Jupiter ACE emulator which runs the Jupiter ACE Forth, reading the input from a file and writing the output to standard output.
- **JADecompile.py**: A Jupiter ACE Forth .TAP decompiler.

The first two do more or less the same. While the second was written with the sole purpose of validating the first, it turned out to cover almost the same functionality as the Python version.

Which one do you need? Well, the Python version is autonomous - you only need a Python interpreter in your machine (2.7 or 3); you don't even need the ACE ROM. Python interpreters come bundled by default in most systems nowadays, including Linux, Windows, OSX, BSD and whatnot; if yours is one of these rare cases where there isn't one, you can grab a pre-built binary or even compile it from source.

On the other hand, the headless emulator (**jforth**) requires a ROM (named `ace.rom` and present in the current directory) to run, and a C compiler and GNU make to build. The accuracy is of course guaranteed, but since it was used to test the accuracy of the Python interpreter, I'd say the Python version is on par in that respect.

**JAForth.py** can't run Z80 code because it's not an emulator, while **jforth** can. Not that there's much need for that in a tool that is aimed at compiling, anyway.

**JADecompile.py** is a simple Forth decompiler. It's not very accurate but it more or less does the job. Unlike the `LIST` command, it can list (to an extent) words defined with `VARIABLE`, `CONSTANT`, `CREATE` and `VOCABULARY`, and it can handle multiple vocabularies. It can detect words created with `DEFINER` but it doesn't know how to write them properly (e.g. if they invoke `WORD`), so it just lists their parameter field if there's one. For words created with `COMPILER`, if the number of parameter bytes is 0 and the compile-time body of the `COMPILER` definition is empty, it will just output the word; otherwise it will attempt to reproduce the same object code, but not the same source.

## Usage

To run the Forth interpreter in Python, you only need the `JAForth.py` file (it's all contained in that single file) and a Python interpreter. It can be invoked with several options; run it like this to see them all:

    python3 JAForth.py --help

(replace `python3` with the name of your Python executable, if it's different)

The C emulator accepts only a tape filename as argument, and all input and output go through standard input / standard output / standard error.

Neither is designed to be interactive. The input is a file containing a Forth program with a SAVE command at the end. The result is a .j.tap file (a Jupiter ACE format .tap file) containing the compiled program.

To run the C version:

    ./jforth [output_file.j.tap] < {input_file.4th}

As usual, to redirect the output to a file append ` > {output_file.txt}` to the command in either case. Lines should not exceed 702 bytes, the maximum size of the input buffer in a Jupiter.

Note that the C emulator includes a fix for a ROM bug that can cause the stack to be corrupted after deleting a word. This bug shouldn't affect normal use because in order to trigger it, you'd either need to have an error in a definition, or to use `FORGET`. In the first case, the emulator aborts, so you can't possibly enter the additional commands that trigger the bug; in the second case, `FORGET` is unlikely to be used at all (the Python version doesn't even implement it). Despite that, the emulator contains a ROM trap to fix this problem.

Details of the bug can be found at: https://codeberg.org/pgimeno/JupiterROMDisassembly/src/branch/129ba8e1791dcdfadcc84d9733b78aec9aa66e10/JupiterACE_ROM_Disassembly.asm#L446

## Supported words

For compiling, **JAForth.py** supports everything a normal Jupiter does; for executing, it supports everything except the following words:

    LOAD BVERIFY VERIFY BLOAD BSAVE
    VLIST LIST EDIT FORGET REDEFINE
    INKEY BEEP PLOT AT CLS

They will give an error ("Not implemented") if they're executed.

In addition, when executing from the compiler:

- `IN` always returns 255; `OUT` does nothing.
- `RETYPE` always gives an error and exits (except when called from LINE, where it also processes some extra words).
- `CALL` does not do anything useful, as the only addresses it knows are those of the ROM Forth words and will give an error on any other address.

The extra words supported by the Python version are:

- `\` is an immediate word that ignores everything until the end of the line, therefore it can be used as a single-line comment that doesn't produce any compiled result.
- `#INCBIN` encloses a binary file inside the current word, as if it was entered byte after byte with `C,`. The file name is everything between the command and the end of the line.
- `#IMPORTSYM` imports an assembler symbols file with lines in this format: `symbol EQU value` or `symbol: EQU value` or `symbol = value`. The symbols are imported as constants prefixed with the character `&` and with the given value, as if `value CONSTANT &symbol` was executed.
- `/"` encloses the string between the `/"` itself and the end of the line, as if `ASCII x C,` was executed for every character `x` of the string.
- `\"` does the same as `/"` but the characters are entered with bit 7 flipped.

**jforth** supports every command for compilation, and for running it supports everything except:

    LOAD BVERIFY VERIFY BLOAD

However, it's worth noting the following:

- `IN` always returns 255; `OUT` does nothing; `BEEP` does nothing. `INKEY` always returns 0.
- `RETYPE` always gives an error and exits.
- Everything that outputs to or manipulates the screen is useless, except for character output which is redirected to standard output and not written to the screen. The screen is entirely dedicated to the input buffer.

To run the decompiler:

    python3 JADecompile.py {input_file.tap}

The listing is output to standard output.

## License and Credits

The Python programs are entirely written by me, Pedro Gimeno, and offered under the Expat license (one of the many MIT licenses). The emulator consists of several files; **jforth.c** is entirely written by me, and the license is also the Expat license. The Z80 module is written by several authors (see the file **z80.c** for details), it has the same license (with different copyright holders, of course) and is copied from this exact revision:

https://github.com/carmiker/jgz80/tree/e987b9229c700c4db2c3f84dfce4fc0e63817701

The test suite and the makefile are written by me and donated to the public domain.

**JAForth.py** includes copies of three snippets from the ROM: the main loop, the `LINE` Forth primitive and the initial Forth environment that includes the system variables and the `FORTH` word. These are required for proper interoperability and are thus believed to be fair use.
