initial upload
This commit is contained in:
@@ -0,0 +1,538 @@
|
||||
n808 VM specification
|
||||
=====================
|
||||
n808 (always lowercase, pronounced _nano-bob_) is a Harvard-type numeric-only
|
||||
virtual machine based on an elaborate effort of further simplification of the
|
||||
[mu808 VM](https://codeberg.org/luxferre/mu808) specification. The n808 VM comes
|
||||
with its own assembly language, N8A, and strict plaintext and binary machine
|
||||
code format definitions.
|
||||
|
||||
Features
|
||||
--------
|
||||
* Data memory: 128 cells (125 of which are mutable, see below)
|
||||
* Program memory: 128 steps
|
||||
* Data cell type: floating point (or fixed point where floats are unsupported)
|
||||
|
||||
The n808 data memory contains several special addresses that cannot be used
|
||||
for writing custom data into them:
|
||||
|
||||
* 0: read-only, always returns 0 when accessed;
|
||||
* 125: read-write but gets overwritten by `jmp 14` instructions to store the
|
||||
return instruction address;
|
||||
* 126: read-only, always returns -1 when accessed;
|
||||
* 127: read-only, always returns 1 when accessed.
|
||||
|
||||
Instruction format
|
||||
------------------
|
||||
Every n808 instruction `[opcode] [p1] [p2] [p3]` is 24 bits (3 bytes) long:
|
||||
|
||||
* 3 bits for opcode;
|
||||
* 7 bits for parameter 1;
|
||||
* 7 bits for parameter 2;
|
||||
* 7 bits for parameter 3.
|
||||
|
||||
Implementations may accept both text and binary representations, but every
|
||||
instruction can only be entered as a single non-negative integer number.
|
||||
The value of this number is: `opcode * 2097152 + p1 * 16384 + p2 * 128 + p3`.
|
||||
In case an implementation is only expecting the binary format, the byte order
|
||||
of the number must be big-endian, from the most to the least significant byte.
|
||||
In case of plain text representation, instructions can be separated with any
|
||||
non-digit characters, but only the support for whitespaces and newlines as the
|
||||
delimiters is absolutely required.
|
||||
|
||||
If an implementation supports the host filesystem, it is recommended to store
|
||||
the plain text machine code with the `.n8` file suffix, and the binary machine
|
||||
code with the `.n8b` file suffix. Since a program can at most contain 128 n808
|
||||
instructions that are 24 bits each, the maximum `.n8b` file size is 384 bytes.
|
||||
|
||||
Note: contrary to 1V0, 808UL and mu808, instruction numbers are no longer part
|
||||
of the instructions. Every instruction is numbered sequentially, starting from
|
||||
the step 0 (unlike 808UL and mu808, step number 0 is not reserved for immediate
|
||||
execution).
|
||||
|
||||
Instruction set
|
||||
---------------
|
||||
This description assumes that every instruction accepts address parameters
|
||||
`p1`, `p2` and `p3`, and `v1`, `v2` and `v3` refer to the actual contents
|
||||
of data memory cells at those addresses.
|
||||
|
||||
Some instruction descriptions also contain shortcut mnemonics. These mnemonics
|
||||
are just for convenience, as they, just like the main mnemonics, are converted
|
||||
into real numeric instructions by the N8A assembler. Shortcuts always take less
|
||||
parameters than real instructions. Some shortcuts, like `nnn` or `ret`, do not
|
||||
take any parameters at all.
|
||||
|
||||
Note that mnemonics and shortcuts are a feature of the N8A assembly language and
|
||||
not n808 per se. The VM itself only operates on numbers in both program and data
|
||||
memory areas.
|
||||
|
||||
### 0 NOP: no operation
|
||||
|
||||
Ignore all parameters and do nothing.
|
||||
|
||||
Shortcut: `nnn` = `nop 0 0 0`
|
||||
|
||||
### 1 JMP: jump
|
||||
|
||||
The logic depends on the value of `p1`:
|
||||
|
||||
* 0: jump to the address `p3` if `v2` equals to zero;
|
||||
* 1: jump to the address `p3` if `v2` is above zero;
|
||||
* 2: jump to the address `p3` if `v2` is below zero;
|
||||
* 3: jump to the address `p3` if `v2` is above or equals to zero;
|
||||
* 4: jump to the address `p3` if `v2` is below or equals to zero;
|
||||
* 5: jump to the address `p3` if `v2` does not equal to zero;
|
||||
* 6: jump to the address `p3` unconditionally;
|
||||
* 7: jump to the address `v3` if `v2` equals to zero;
|
||||
* 8: jump to the address `v3` if `v2` is above zero;
|
||||
* 9: jump to the address `v3` if `v2` is below zero;
|
||||
* 10: jump to the address `v3` if `v2` is above or equals to zero;
|
||||
* 11: jump to the address `v3` if `v2` is below or equals to zero;
|
||||
* 12: jump to the address `v3` if `v2` does not equal to zero;
|
||||
* 13: jump to the address `v3` unconditionally;
|
||||
* 14: save the next instruction pointer to the cell 125 and jump to
|
||||
the address `p3` unconditionally.
|
||||
|
||||
In case of jumping to the address `v3`, it is converted to an integer first.
|
||||
|
||||
Shortcuts:
|
||||
|
||||
* `jeq` = `jmp 0` (direct jump if equals to zero)
|
||||
* `jgt` = `jmp 1` (direct jump if greater than zero)
|
||||
* `jlt` = `jmp 2` (direct jump if less than zero)
|
||||
* `jge` = `jmp 3` (direct jump if greater than or equals to zero)
|
||||
* `jle` = `jmp 4` (direct jump if less than or equals to zero)
|
||||
* `jne` = `jmp 5` (direct jump if not equals to zero)
|
||||
* `juc` = `jmp 6 0` (direct unconditional jump)
|
||||
* `ieq` = `jmp 7` (indirect jump if equals to zero)
|
||||
* `igt` = `jmp 8` (indirect jump if greater than zero)
|
||||
* `ilt` = `jmp 9` (indirect jump if less than zero)
|
||||
* `ige` = `jmp 10` (indirect jump if greater than or equals to zero)
|
||||
* `ile` = `jmp 11` (indirect jump if less than or equals to zero)
|
||||
* `ine` = `jmp 12` (indirect jump if not equals to zero)
|
||||
* `iuc` = `jmp 13 0` (indirect unconditional jump)
|
||||
* `jpr` = `jmp 14 0` (jump to a procedure)
|
||||
* `ret` = `jmp 13 0 125` (return from a procedure)
|
||||
|
||||
### 2 IAT: indirect addressing toggle
|
||||
|
||||
Overrides the next instruction by providing `v1`, `v2` and `v3` as the parameters
|
||||
for the next instruction's command. The actual command parameters provided with
|
||||
the next instruction will be ignored.
|
||||
|
||||
Shortcuts: none
|
||||
|
||||
### 3 INO: port input/output
|
||||
|
||||
This instruction combines input and output depending on the port number in `p1`.
|
||||
Generally, even ports are related to output and odd ports are related to input:
|
||||
|
||||
* 0: standard (numeric) output;
|
||||
* 1: standard (numeric) input;
|
||||
* 2: character output (if supported);
|
||||
* 3: character input (if supported).
|
||||
|
||||
The `p2` and `p3` parameters define the range of addresses to output the data from
|
||||
or input the data into.
|
||||
|
||||
Shortcuts:
|
||||
|
||||
* `out` = `ino 0` (numeric output)
|
||||
* `inp` = `ino 1` (numeric input)
|
||||
* `ouc` = `ino 2` (character output)
|
||||
* `ipc` = `ino 3` (character input)
|
||||
|
||||
### 4 CPY: copying/assignment
|
||||
|
||||
The logic depends on the value of `p1`:
|
||||
|
||||
* 0: set the memory cell `p3` to `p2`;
|
||||
* 1: set the memory cell `p3` to `v2`;
|
||||
* 2: set the memory cell `v3` (converted to integer) to `p2`;
|
||||
* 3: set the memory cell `v3` (converted to integer) to `v2`;
|
||||
* 4: set the memory cell `v3` (converted to integer) to the value at address
|
||||
`v2` (converted to integer).
|
||||
|
||||
Shortcuts:
|
||||
|
||||
* `dca` = `cpy 0` (direct constant assignment)
|
||||
* `dva` = `cpy 1` (direct value assignment)
|
||||
* `ica` = `cpy 2` (indirect constant assignment)
|
||||
* `iva` = `cpy 3` (indirect value assignment)
|
||||
* `ivc` = `cpy 4` (indirect value copy)
|
||||
|
||||
### 5 SET: large value assignment
|
||||
|
||||
Set the memory cell `p3` to the value of `p1 * 100 + p2 + v3 / 100`.
|
||||
|
||||
Shortcuts: none
|
||||
|
||||
### 6 MAT: mathematical operations
|
||||
|
||||
The logic depends on the value of `p1`:
|
||||
|
||||
* 0: set the memory cell `p3` to `v2 + v3`;
|
||||
* 1: set the memory cell `p3` to `v2 - v3`;
|
||||
* 2: set the memory cell `p3` to `v2 * v3`;
|
||||
* 3: set the memory cell `p3` to `v2 / v3` if `v3` is not zero,
|
||||
otherwise set it to zero;
|
||||
* 4: set the memory cell `p3` to `v2 mod v3` if `v3` is not zero,
|
||||
otherwise set it to the integer part of `v2`.
|
||||
* 5: set the memory cell `p3` to `|v2|` (absolute value of `v2`);
|
||||
* 6: set the memory cell `p3` to the square root of `|v2|`;
|
||||
* 7: set the memory cell `p3` to the natural exponent of `v2` (`e ** v2`);
|
||||
* 8: set the memory cell `p3` to `ln |v2|`;
|
||||
* 9: set the memory cell `p3` to `sin v2` (`v2` given in radians);
|
||||
* 10: set the memory cell `p3` to `cos v2` (`v2` given in radians);
|
||||
* 11: set the memory cell `p3` to `arctg v2`.
|
||||
|
||||
Shortcuts:
|
||||
|
||||
* `add` = `mat 0` (addition)
|
||||
* `sub` = `mat 1` (subtraction)
|
||||
* `mul` = `mat 2` (multiplication)
|
||||
* `div` = `mat 3` (division)
|
||||
* `mdf` = `mat 4` (modulo/floor)
|
||||
* `inc` = `mat 0 127` (increment)
|
||||
* `dec` = `mat 0 126` (decrement)
|
||||
* `neg` = `mat 1 0` (negation)
|
||||
* `inv` = `mat 3 127` (inverse/reciprocal)
|
||||
* `abs` = `mat 5` (absolute value)
|
||||
* `sqr` = `mat 6` (square root)
|
||||
* `exp` = `mat 7` (natural exponent)
|
||||
* `log` = `mat 8` (natural logarithm)
|
||||
* `sin` = `mat 9` (sine)
|
||||
* `cos` = `mat 10` (cosine)
|
||||
* `atn` = `mat 11` (arctangent)
|
||||
|
||||
### 7 RND: random number generator
|
||||
|
||||
Set the memory cell `p3` to a random integer number between `v1` and `v2`
|
||||
(inclusively).
|
||||
|
||||
Shortcuts: none
|
||||
|
||||
Interactive mode
|
||||
----------------
|
||||
Unlike 1V0/808UL/mu808, n808 only accepts the following command parameters
|
||||
in the interactive mode:
|
||||
|
||||
* `0 [step no] 0`: run the currently loaded program starting at a particular step;
|
||||
* `1 [step no] [instr]`: enter an instruction into the program
|
||||
memory (the previous instruction at that step will be overwritten);
|
||||
* `2 [p1] [p2]`: clear a range of instructions from address `p1` to `p2` (incl.);
|
||||
* `3 [p1] [p2]`: clear a range of data from address `p1` to `p2` (incl.);
|
||||
* `4 0 0`: exit to the OS or reset the VM if the exit is not supported.
|
||||
|
||||
Assembly source code file format (N8A)
|
||||
--------------------------------------
|
||||
In addition to direct machine code in the plain text or N8B formats, the n808
|
||||
VM also allows using an assembly-like language to write programs using labels
|
||||
and the above mnemonics (case-insensitive). The recommended file suffix is
|
||||
.n8a.
|
||||
|
||||
An assembly line looks like this (the optional parts are enclosed in square
|
||||
brackets): `[:lbl] MNEMONIC p1 p2 p3 [;comment]`. Labels are optional but must
|
||||
start with a colon (`:`) and be on the same line before the instruction they
|
||||
label. The mnemonics are specified above in the core opcode list and the
|
||||
shortcut list for each opcode. In the second case, shortcuts accept less
|
||||
instruction parameters than the opcode they refer to.
|
||||
|
||||
Besides normal assembly lines, N8A also supports alias definition lines that
|
||||
start with `#` and have the following format: `#number alias`. In the rest of
|
||||
your code, you can recall any alias with the `@alias` form. For instance, if you
|
||||
have defined `#21 counter` (use the cell 21 as counter), you can then write
|
||||
`inc @counter` as opposed to `inc 21`. The alias feature allows you to replace
|
||||
any constant numbers with easily remembered words within your N8A assembly.
|
||||
Every alias must be defined on a separate line of code.
|
||||
|
||||
Here, the exact assembly algorithm is specified step-by-step for each line in
|
||||
the N8A assembly file to convert it into a plain text based machine code
|
||||
representation:
|
||||
|
||||
1. Remove all comments (starting with `;` until the end of the line).
|
||||
2. Replace all jump/function shortcuts according to the above mnemonics.
|
||||
3. Record the current line number N (not counting completely empty lines),
|
||||
starting with 0.
|
||||
4. Split the line into space-delimited fields (1-based numbering as well).
|
||||
5. Check if the first field starts with `:`. If so, mark the mapping between
|
||||
the field text and the number N, then remove the field from the set
|
||||
(so that the field 2 becomes field 1 and so on).
|
||||
6. Check if the first field starts with `#`. If so, mark the mapping between
|
||||
the text of the field 2 and the rest of the field 1 into the label mapping,
|
||||
prepending `@` to the text of the field 2.
|
||||
7. If the field 1 is not already a number, replace the field 1 mnemonic text
|
||||
with the numeric opcode if it can be found. If it's not a number and the
|
||||
mnemonic cannot be found, report an error and halt the process.
|
||||
8. Write the result as a new line into the intermediate text file.
|
||||
9. After the steps 1 to 8 are complete for every line, replace every label
|
||||
occurrence in the mapping with the corresponding number in the intermediate
|
||||
text file.
|
||||
10. For every line in the intermediate text file, convert the four numbers on
|
||||
that line (`opcode`, `p1`, `p2`, `p3`) into a single number according to this
|
||||
formula: `instruction = opcode * 2097152 + p1 * 16384 + p2 * 128 + p3`.
|
||||
Write the result as a (space-delimited) field into the target code file.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The [examples](examples/) subdirectory contains several N8A source code file
|
||||
examples for n808 (some of which are ports of the same mu808 example programs),
|
||||
namely:
|
||||
|
||||
* [Compound interest calculator](examples/compound.n8a),
|
||||
* [Linear regression calculator](examples/linreg.n8a),
|
||||
* [Hellorld!](examples/hellorld.n8a) (a tribute to @UsagiElectric)
|
||||
(requires I/O port 2 support),
|
||||
* [FizzBuzz classic program](examples/fizzbuzz.n8a)
|
||||
(requires I/O port 2 support),
|
||||
* [A simple 10-character echo test](examples/echo.n8a) (requires both I/O
|
||||
port 2 and port 3 support),
|
||||
* [Bulls and Cows game](examples/moo.n8a),
|
||||
* [Lunar Lander game](examples/lunar.n8a),
|
||||
* [NumberJack](examples/numjack.n8a) port of a Blackjack game, utilizing some
|
||||
advanced techniques (see the comments in the beginning on how to play it).
|
||||
|
||||
You can assemble them using any of the reference assemblers provided within the
|
||||
repository, or even by hand (by numbering lines, resolving the labels/shortcuts
|
||||
and replacing mnemonics with corresponding opcodes).
|
||||
|
||||
If you just want to test an implementation, assembled N8 machine code files
|
||||
(in the plaintext format) are stored in the `examples/assembled` subdirectory.
|
||||
After loading into the REPL, you can run each of them with the `0 0 0` sequence.
|
||||
|
||||
Reference implementations
|
||||
-------------------------
|
||||
|
||||
### n808 VM implementations
|
||||
|
||||
* [ANSI C implementation](n808.c) (C89 standard): the primary version where all
|
||||
development is being done. Supports the entire specification but only preloads
|
||||
the `.n8` (text-based format) machine code files.
|
||||
Compile the source with: `cc -std=c89 -O2 -s -lm -o n808 n808.c`
|
||||
* [Python 3/MicroPython implementation](n808.py): supports the entire n808 spec
|
||||
and runs in any Python 3 environment. Only preloads the `.n8`-type code files.
|
||||
* [POSIX AWK implementation](n808.awk): supports the entire specification except
|
||||
the I/O port 3 (character input). Otherwise, it is a line-to-line port of the
|
||||
C89 and Python 3 versions. Only preloads the `.n8`-type code files, can be run
|
||||
as follows: `LC_ALL=C awk -f n808.awk [- input_program.n8]`
|
||||
|
||||
### N8A assembler implementations
|
||||
|
||||
* [n8asm.py](n8asm.py): the reference assembler/disassembler for the N8A
|
||||
language. Supports all real and shortcut mnemonics mentioned in this README.
|
||||
Besides assembling and disassembling `.n8` and `.n8b` files, also supports
|
||||
in-place conversion between these two formats and exporting N8 text-based
|
||||
machine code into the N74 format for usage in the TI-74 and other similar
|
||||
BASIC-based n808 VM implementations.
|
||||
|
||||
Other implementations (VMs, assemblers, helper tools)
|
||||
-----------------------------------------------------
|
||||
|
||||
### Texas Instruments TI-74 portable computer
|
||||
|
||||
The [n808.b74](n808.b74) file contains a BASIC port of n808 for the Texas
|
||||
Instruments TI-74 portable computer (tested on the TI-74S variant). Due to the
|
||||
resource constraints, the following limitations apply:
|
||||
|
||||
* no interactive mode (the RUN command directly executes the predefined program
|
||||
in the VM),
|
||||
* the program itself is entered into the DATA statements in the so-called N74
|
||||
format (see below),
|
||||
* no boundary checks for the addresses inside the program.
|
||||
|
||||
Also, since the VM runs on top of a BASIC interpreter, program execution is
|
||||
extremely slow most of the time. Keep in mind, this is more of a proof of
|
||||
concept than a viable solution, and using the "native" TI BASIC is preferred for
|
||||
any serious computing on that machine.
|
||||
|
||||
In order to store your programs for execution, you must store the N8 instruction
|
||||
values in the DATA statements, starting from the BASIC line number 1000. The
|
||||
last data entry of the program must be -1. You can fit as many data entries on
|
||||
a line as the machine allows (usually up to 7, given the instruction number
|
||||
length in the decimal form). To ease the program entry process, you can start it
|
||||
with the `NUM 1000,1` command, and use `FN N` key combo to enter the `DATA `
|
||||
keyword.
|
||||
|
||||
For instance, the compound interest calculator example looks like this in N74:
|
||||
```
|
||||
1000 DATA 8401409,6308099,12632321,12599169,12714113,12616065,12697729
|
||||
1001 DATA 6291585,-1
|
||||
```
|
||||
|
||||
To make the conversion easier, the official n808 assembler, n8asm.py, supports
|
||||
the `t74` mode that accepts a plain N8 machine code file and outputs the same
|
||||
program in the N74 format.
|
||||
|
||||
### Casio fx-3400P programmable scientific calculator
|
||||
|
||||
Unfortunately, the fx-3400P's program memory is too small to be able to fit in
|
||||
any full-featured n808 VM or assembler, but here's a couple of helper keystroke
|
||||
programs for converting the four numeric instruction parts into a machine code
|
||||
instruction and vice versa. The encoding and decoding process is done according
|
||||
to the formula `ins = opcode * 2097152 + p1 * 16384 + p2 * 128 + p3`.
|
||||
These programs will help you with hand-assembling n808 code in case you don't
|
||||
have a PC or any modern Web-enabled device to do it on.
|
||||
|
||||
The complete encoding/decoding suite consisting of both routines to be saved in
|
||||
the P1 and P2 areas is presented here along with the sequences to enter them
|
||||
(the `ENT` key is the same as the `RUN` key in the program entry mode):
|
||||
```
|
||||
128 Kin 6
|
||||
MODE EXP SHIFT PCL MODE 1 DEC P1
|
||||
x Kout 6 + ENT = x Kout 6 + ENT = x Kout 6 + ENT =
|
||||
SHIFT P2
|
||||
Kin 1 / Kout 6 = Kin 2 * Kout 6 - Kout 1 = +/- SHIFT HLT Kout 2 SHIFT x>0
|
||||
MODE .
|
||||
```
|
||||
Note that you need to keep the value 128 in the register 6 at all times for both
|
||||
routines to work correctly.
|
||||
|
||||
To encode an instruction, first enter the opcode and press the `P1` key, then
|
||||
enter parameter 1 and press `RUN`, then enter parameter 2 and press `RUN`, then
|
||||
enter parameter 3 and press `RUN` The program will output the resulting machine
|
||||
instruction as a single decimal number. In case you're directly hand-assembling
|
||||
a binary machine code file (N8B) file, you can view the hexadecimal
|
||||
representation of the number by pressing the `MODE 1 HEX` sequence (press
|
||||
`MODE 0` to return to the normal mode).
|
||||
|
||||
Example: suppose the instruction is `out 16 18`, which translates to
|
||||
`ino 0 16 18`, meaning `3 0 16 18`. As expected, after entering each parameter
|
||||
and pressing the `RUN` key, the program will output the final result 6293522
|
||||
to be entered into the VM as the machine code.
|
||||
|
||||
To decode an instruction, enter the instruction value and press `SHIFT P2`. The
|
||||
program will output the instruction parameters in the reverse order: parameter
|
||||
3, parameter 2, parameter 1 and then the opcode. Continue pressing the `RUN` key
|
||||
until you get all four parameters, press it once more to finish the program.
|
||||
|
||||
E.g. if we enter the instruction value 6293522 and press `SHIFT P2`, the program
|
||||
will first output 18, then 16, then 0, then 3.
|
||||
|
||||
Note that the whole suite takes exactly 29 steps (the entire program memory in
|
||||
the Casio fx-3400P calculator), so it doesn't clear the mode after finishing.
|
||||
Once you don't want to stay in the integer calculation mode, press `MODE 0` to
|
||||
return to the normal mode.
|
||||
|
||||
### Citizen SRP-145 and other programmable calculators using Sharp LI3301A chip
|
||||
|
||||
One of the first cheap programmable calculator architectures of the past was the
|
||||
Sharp LI3301A chip that never made it into Sharp's own calculators. I happen to
|
||||
have a Citizen SRP-145T-II based on the same hardware.
|
||||
|
||||
Similarly to Casio fx-3400P, this calculator only has enough (40-step) program
|
||||
memory for instruction encoding/decoding helper routines. However, they must be
|
||||
entered separately as the SRP-145T only has a single program storage area. It
|
||||
also has much less register memory, no flow control or integer calculation mode.
|
||||
|
||||
Here's what an n808 instruction encoding routine looks like in SRP-145:
|
||||
```
|
||||
SHIFT PGM
|
||||
x 128 + SHIFT [x] = x 128 + SHIFT [x] = x 128 + SHIFT [x] =
|
||||
SHIFT PGM
|
||||
```
|
||||
To encode an instruction, first enter the opcode and press the `RUN` key, then
|
||||
enter parameter 1 and press `RUN`, then enter parameter 2 and press `RUN`, then
|
||||
enter parameter 3 and press `RUN` The program will output the resulting machine
|
||||
instruction as a single decimal number.
|
||||
|
||||
Since there's no integer conversion in LI3301A, the instruction decoding routine
|
||||
relies on a DMS precision exhaustion hack (the DMS is the `SHIFT /` key to
|
||||
convert decimal degrees into degrees/minutes/seconds, where the minutes and the
|
||||
seconds are shown after the decimal point):
|
||||
```
|
||||
SHIFT PGM
|
||||
MR SHIFT Ka 128 SHIFT 1/x Ka
|
||||
SHIFT / (23 times)
|
||||
X->M x 128 - 1 Ka = +/-
|
||||
SHIFT PGM
|
||||
```
|
||||
To run the routine, enter the instruction value and then press `X->M RUN`.
|
||||
The program will output the parameters in the reverse order (first parameter 3,
|
||||
then parameter 2, then parameter 1, then the opcode). Continue pressing `RUN`
|
||||
until you get all four parameters. Clear the memory register(s) when finished.
|
||||
|
||||
Note: due to the hackiness of the method, you may get non-integer outputs. Just
|
||||
round the results to the nearest integer if you get a fractional part.
|
||||
|
||||
### Sharp EL-506P scientific calculator and its clones
|
||||
|
||||
These calculators are non-programmable but are the cheapest scientific calcs in
|
||||
the world, and can help you with n808 instruction encoding and decoding.
|
||||
|
||||
Since this architecture has algebraic input, encoding is straightforward and
|
||||
based on the initial formula:
|
||||
```
|
||||
[opcode] x 2097152 + [p1] x 16384 + [p2] x 128 + [p3] =
|
||||
```
|
||||
Decoding can be done based on the fact that conversion to hexadecimal and back
|
||||
only leaves the integer part. One of the most optimal sequences is mostly using
|
||||
hexadecimal flow:
|
||||
```
|
||||
[instruction] X->M 2ndf HEX / 80 * 80 - RM = +/- 2ndf DEC # display parameter 3
|
||||
RM 2ndf HEX / 80 = X->M / 80 * 80 - RM = +/- 2ndf DEC # display parameter 2
|
||||
RM 2ndf HEX / 80 = X->M / 80 * 80 - RM = +/- 2ndf DEC # display parameter 1
|
||||
RM 2ndf HEX / 80 = 2ndf DEC # display the opcode
|
||||
```
|
||||
|
||||
### Generic 8-digit four-function calculators
|
||||
|
||||
Four-function calculators mostly have their architecture stemming from early
|
||||
Sharp LCD models such as EL-211 and EL-330. They are famous for their extremely
|
||||
low prices, limited precision and non-algebraic input. Because of this, an
|
||||
optimal keystroke sequence for n808 instruction encoding using them would be:
|
||||
```
|
||||
[opcode] x 128 + [p1] x 128 + [p2] x 128 + [p3] =
|
||||
```
|
||||
On the other hand, instruction decoding on such calculators is generally not
|
||||
possible in a fully automated fashion, so the following algorithm is suggested
|
||||
instead:
|
||||
```
|
||||
[instruction] / 2097152 - # note the integer part as the [opcode] value
|
||||
[opcode] x 128 - # note the integer part as the [p1] value
|
||||
[p1] x 128 - # note the integer part as the [p2] value
|
||||
[p2] x 128 = # round to the nearest integer as the [p3] value
|
||||
```
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
### Why another ultralight VM? Isn't mu808 enough?
|
||||
|
||||
While mu808 already is compact enough, it still has some room for optimization
|
||||
when it comes to the instruction format and port-based I/O. n808 aims to be an
|
||||
architecture that can run on the devices where even mu808 would struggle.
|
||||
|
||||
Besides, n808 can also serve as a demo platform for the enthusiasts to try and
|
||||
fit as much useful code as possible into the space as tight as 128 program steps
|
||||
and 124 data cells (not counting cells 0, 125, 126 and 127). For instance, the
|
||||
"Examples" section of this document contains several games that could be rather
|
||||
difficult to fit into such space.
|
||||
|
||||
### Does n808 deprecate mu808 in the same way that mu808 deprecated 808UL?
|
||||
|
||||
**No**. These two VMs are being actively maintained in parallel. Moreover, there
|
||||
are some plans to upgrade mu808's assembly language (MU8A) based on the
|
||||
innovations introduced in the N8A language.
|
||||
|
||||
The author is currently more focused on the n808 implementations because it is a
|
||||
more interesting challenge both to port the n808 VM itself and to write useful
|
||||
software for it given the space constraints.
|
||||
|
||||
### When to choose mu808 and when to choose n808?
|
||||
|
||||
Choose mu808 when:
|
||||
|
||||
* resource constraints are not a significant factor;
|
||||
* you need to retain the ability to compose programs in a human-readable
|
||||
machine language (seeing every instruction component as opposed to a
|
||||
single number) in addition to assembly;
|
||||
* you rely on the interactive mode more than on preloaded programs.
|
||||
|
||||
Choose n808 when:
|
||||
|
||||
* your target environment is really tight on memory and CPU performance;
|
||||
* you need or just want to have the machine code as compact as possible;
|
||||
* you primarily run programs by preloading them and not entering via console.
|
||||
|
||||
Credits
|
||||
-------
|
||||
Created by Luxferre in 2025, released into public domain with no warranties.
|
||||
Reference in New Issue
Block a user