initial upload
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
mu808: an even more ultralight numeric VM designed to run everywhere
|
||||
====================================================================
|
||||
mu808 (always lowercase, pronounced as _micro-bob_) is a minimalist virtual
|
||||
machine specification that is based on the 1V0 and 808UL ideas but has an
|
||||
even leaner instruction set and is even easier to implement in any modern
|
||||
or even old programming language.
|
||||
|
||||
Overview
|
||||
--------
|
||||
mu808 is a Harvard-architecture virtual machine with separate memory space for
|
||||
programs and data. The only available data type is a floating point number,
|
||||
although some implementations may use fixed point numbers instead.
|
||||
|
||||
Unlike 808UL, mu808 strictly defines support of up to 16384 data cells and
|
||||
up to 16383 program steps, with the program step 0 being unavailable and the
|
||||
data cell 0 always returning 0.
|
||||
|
||||
On the machine start, both memory areas are initialized with zeroes and the
|
||||
interactive mode is entered which accepts instructions from the platform's
|
||||
standard input.
|
||||
|
||||
### Overall features
|
||||
|
||||
* 16 runtime instructions (from 0 to 15, including NOP as instruction 0);
|
||||
* ability to enter integer commands (positive and negative) into program memory;
|
||||
* ability to enter floating (or fixed) point numbers (positive and negative) at
|
||||
program runtime;
|
||||
* immutability of the data address 0 (must always return 0 when accessed).
|
||||
|
||||
The following things are implementation-dependent:
|
||||
|
||||
* instruction and data internal representation;
|
||||
* tracing capabilities;
|
||||
* runlimit (the amount of program steps before forcefully halting the program).
|
||||
|
||||
mu808 instruction set
|
||||
---------------------
|
||||
Unlike 808UL, mu808 only has a core instruction set. Also, regardless of the
|
||||
instruction number, all instructions strictly contain five numbers separated
|
||||
by a whitespace:
|
||||
|
||||
* `[lno]` - instruction number,
|
||||
* `[cmd]` - command,
|
||||
* `[arg1]` - argument 1,
|
||||
* `[arg2]` - argument 2,
|
||||
* `[arg3]` - argument 3.
|
||||
|
||||
Any mu808 implementation must not distinguish between a whitespace and a newline
|
||||
character: both of them, if supported, must be used to delimit individual parts
|
||||
of an instruction and not instructions as a whole. Instructions as a whole,
|
||||
however, are separated naturally by reading five delimited numbers at a time.
|
||||
|
||||
This rule applies to the plaintext format of the machine code. See the section
|
||||
about mu808 file formats to find out about the binary machine code format.
|
||||
|
||||
### Instruction number semantics
|
||||
|
||||
* Any positive integer: the instruction is written into the program memory under
|
||||
this address (any previous instruction at this address is overwritten).
|
||||
* 0: the instruction is NOT written into the program memory and is immediately
|
||||
executed instead. If the instruction is a jump instruction that jumps into a
|
||||
valid positive address, program execution in the program memory begins until
|
||||
the program memory is exhausted.
|
||||
* -1: displays a range of instructions from address `[cmd]` to `[arg1]` (incl).
|
||||
* -2: if `[cmd]` is 0, clears a range of instructions from address `[arg1]` to
|
||||
`[arg2]` (inclusively), otherwise clears a range of data from address `[cmd]`
|
||||
to `[arg2]` (inclusively).
|
||||
* -3: if supported, turns off execution tracing output if the `[cmd]` value is
|
||||
equal to 0, otherwise turns it on.
|
||||
* -4: if supported, changes the program runlimit to the value of `[cmd]`.
|
||||
* -5: if supported, the VM exits to the OS environment, otherwise resets to
|
||||
its initial state (both memory areas filled with zeroes).
|
||||
|
||||
For the data entry and display, a runtime command is required instead
|
||||
(see below for details).
|
||||
|
||||
### Runtime command set
|
||||
|
||||
The following commands are required for all mu808 ports and implementations.
|
||||
|
||||
The following notation is in place for this list:
|
||||
|
||||
* `A1` - address passed as the first command argument;
|
||||
* `A2` - address passed as the second command argument;
|
||||
* `A3` - address passed as the third command argument;
|
||||
* `V1` - numeric value stored at the address `A1`;
|
||||
* `V2` - numeric value stored at the address `A2`;
|
||||
* `V3` - numeric value stored at the address `A3`.
|
||||
|
||||
The mu808 runtime command list follows along with their mnemonics.
|
||||
|
||||
* 0 (NOP): No operation. The instruction is ignored regardless of parameters.
|
||||
* 1 (JMP): Jump. This is the most complicated operation in the list, see the
|
||||
next section ("Jump operation semantics") for the full explanation how it
|
||||
works.
|
||||
* 2 (IAT): Indirect addressing toggle. This instruction 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.
|
||||
* 3 (OUT): Output. Print the range of values stored from the address `A1` to
|
||||
the address `A2` (inclusively) to the port number in `A3` (0 is the platform's
|
||||
standard output).
|
||||
* 4 (INP): Input. Prompt the user to enter the range of values to be stored from
|
||||
the address `A1` to the address `A2` (inclusively) from the port number in
|
||||
`A3` (0 is the platform's standard input).
|
||||
* 5 (SET): Set value at address. Interpret `A1` and `A2` as two direct integer
|
||||
values and then set the value at `A3` to
|
||||
`(A1 mod 10000) + (A2 mod 10000) / 10000`.
|
||||
* 6 (CPY): Direct/indirect value copy. If `A1` is 0, then set the value at `A3`
|
||||
to `A2`. If `A1` is 1, set the value at `A3` to `V2`. Otherwise, set the value
|
||||
at `V3` to the contents of the address `V2` (converted to integer).
|
||||
* 7 (FMA): Fused multiply-add. Set the value at `A3` to `V1 + (V2 * V3)`.
|
||||
* 8 (SUB): Subtract. Set the value at `A3` to `V1 - V2`.
|
||||
* 9 (DIV): Divide. Set the value at `A3` to 0 if `V2` is 0, otherwise set it to
|
||||
`V1 / V2`.
|
||||
* 10 (MDF): Modulo/floor. Interpret `V1` and `V2` as integers and set the value
|
||||
at `A3` to `V1 mod V2` if `V2` is not 0, otherwise set the result to the
|
||||
integer part of `V1`.
|
||||
* 11 (ABS): Absolute value. Set the value at `A3` to `|V2|`.
|
||||
* 12 (SQR): Square root. Set the value at `A3` to the square root of `|V2|`.
|
||||
* 13 (NEL): Natural exponent/logarithm. If the value of `A1` is 0, set the value
|
||||
at `A3` to `e ** V2`, otherwise set it to `ln |V2|`.
|
||||
* 14 (TRI): Trigonometric function. If the value of `A1` is 0, set the value at
|
||||
`A3` to `sin V2`, if the value of `A1` is 1, set the value at `A3` to
|
||||
`cos V2`, otherwise set it to `arctg V2`. The parameters are given in radians.
|
||||
* 15 (RND): Random number. Set the value at `A3` to a random integer number
|
||||
between `V1` and `V2` (inclusively).
|
||||
|
||||
### Jump operation semantics
|
||||
|
||||
The jump operation (instruction command 1) in mu808 is almost identical to the
|
||||
corresponding operation in 808UL, which, in turn, was directly modeled after
|
||||
the corresponding operation in the 1V0 TZ IV variant. It allows for all kinds
|
||||
of conditional and unconditional, direct and indirect jumps based on three
|
||||
input parameters.
|
||||
|
||||
Using the same notation as above, the jump operation semantics is as follows:
|
||||
|
||||
* If `V2` == 0 and `A1` == 0, or
|
||||
* if `V2` > 0 and `A1` == 1, or
|
||||
* if `V2` < 0 and `A1` == 2, or
|
||||
* if `V2` >= 0 and `A1` == 3, or
|
||||
* if `V2` <= 0 and `A1` == 4, or
|
||||
* if `V2` != 0 and `A1` == 5, or
|
||||
* if `A1` == 6,
|
||||
|
||||
then jump to the address `A3`.
|
||||
|
||||
* If `V2` == 0 and `A1` == 7, or
|
||||
* if `V2` > 0 and `A1` == 8, or
|
||||
* if `V2` < 0 and `A1` == 9, or
|
||||
* if `V2` >= 0 and `A1` == 10, or
|
||||
* if `V2` <= 0 and `A1` == 11, or
|
||||
* if `V2` != 0 and `A1` == 12, or
|
||||
* if `A1` == 13,
|
||||
|
||||
then jump to the address `V3` (converting it to an integer first).
|
||||
|
||||
**Note** that the order of operands has changed compared to the 1V0/808UL
|
||||
specifications: here, it's `[selector] [value] [jumpaddr]` as opposed to
|
||||
`[value] [jumpaddr] [selector]`. This has been done for more convenient
|
||||
jump programming by selecting the condition as the first operand.
|
||||
|
||||
### Port input and output
|
||||
|
||||
For the commands 3 and 4, mu808 supports optional non-zero port numbers, where
|
||||
port 1 is the character output port and port 2 is the character input port
|
||||
respectively. Additional custom ports may be implementation-defined.
|
||||
|
||||
File and data formats related to mu808
|
||||
--------------------------------------
|
||||
This specification also defines several file formats that can be used for mu808
|
||||
programming process.
|
||||
|
||||
### Plain text machine code format (MU8)
|
||||
|
||||
This is the main format to store and enter mu808 programs in. It essentially
|
||||
duplicates what's being entered into the mu808 REPL, with the exception of the
|
||||
negative instruction numbers. Unlike 808UL, it only allows specifying digits and
|
||||
whitespace in the file. Implementations are only required to support the space
|
||||
character (32, 0x20) as the delimiter, but should also support newline, tabs and
|
||||
other kinds of whitespace in the same way.
|
||||
|
||||
The recommended file suffix for mu808 plain text machine code is .mu8.
|
||||
|
||||
### Binary machine code format (MU8B)
|
||||
|
||||
Unlike 808UL, mu808 also defines a MU8B binary format to store every instruction
|
||||
in 64 bits of data in the following order (for easier processing):
|
||||
|
||||
* 4 higher bits are unused,
|
||||
* 4 bits are used for the command opcode (0 to 15),
|
||||
* 14 bits are used for the instruction number (can't be 0),
|
||||
* 14 bits are used for the command argument 1,
|
||||
* 14 bits are used for the command argument 2,
|
||||
* 14 bits are used for the command argument 3.
|
||||
|
||||
Hence, every instruction can only contain the number from 0 to 16383 in any of
|
||||
its command arguments, and from 1 to 16383 in its instruction number field.
|
||||
|
||||
The recommended file suffix for mu808 binaries is .mu8b or .bin. Implementors
|
||||
are encouraged (but not required) to provide tools for conversion between the
|
||||
binary and text representations of mu808 machine code.
|
||||
|
||||
When exporting the entire program memory to the MU8B format, NOPs may or may
|
||||
not be omitted. If a mu808 implementation supports MU8B file import, it must
|
||||
behave the same way as if loading MU8 (plain text) machine code with the same
|
||||
instructions or entering them manually.
|
||||
|
||||
### Assembly source code file format (MU8A)
|
||||
|
||||
In addition to direct machine code in the plain text or MU8B formats, the mu808
|
||||
VM also allows using an assembly-like language to write programs using labels
|
||||
and the above mnemonics (case-insensitive). The recommended file suffix is
|
||||
.mu8a.
|
||||
|
||||
An assembly line looks like this (the optional parts are enclosed in square
|
||||
brackets): `[:lbl] MNEMONIC arg1 arg2 arg3 [;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.
|
||||
|
||||
Here, the exact assembly algorithm is specified step-by-step for each line in
|
||||
the MU8A 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. Record the current line number N (not counting completely empty lines),
|
||||
starting with 1.
|
||||
3. Split the line into space-delimited fields (1-based numbering as well).
|
||||
4. 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).
|
||||
5. 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.
|
||||
6. Prepend N and the space to the current line and write the result into the
|
||||
target file.
|
||||
7. After the steps 1 to 6 are complete for every line, replace every label
|
||||
occurrence in the mapping with the corresponding number in the target file.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The [examples](examples/) subdirectory contains several MU8A source code file
|
||||
examples for mu808 which are ports from the same 808UL example programs, namely:
|
||||
|
||||
* [Compound interest calculator](examples/compound.mu8a),
|
||||
* [Linear regression calculator](examples/linreg.mu8a),
|
||||
* [Hellorld!](examples/hellorld.mu8a) (a tribute to @UsagiElectric),
|
||||
* [Bulls and Cows game](examples/moo.mu8a),
|
||||
* [Lunar Lander game](examples/lunar.mu8a).
|
||||
|
||||
You can assemble them using any of the reference assemblers provided within the
|
||||
repository, or even by hand (by numbering lines, resolving the labels and
|
||||
replacing mnemonics with corresponding opcodes). The "Hellorld!" example only
|
||||
works on the platforms supporting I/O port 1 (character output).
|
||||
|
||||
Reference implementations
|
||||
-------------------------
|
||||
|
||||
### mu808 VM reference implementations
|
||||
|
||||
* [Python 3 implementation](mu808.py): the first one where all prototyping and
|
||||
initial spec development has been done. Also compatible with MicroPython an
|
||||
tested on it. Supports the entire documented mu808 instruction set, including
|
||||
the two extended I/O ports (requires termios library to work with the
|
||||
character input port correctly on desktop OSes but falls back to a simple
|
||||
stream file read in case the library is not found). Accepts a file name to
|
||||
preload a MU8 (plain text) machine code program from the OS command line.
|
||||
|
||||
### Assembler reference implementations
|
||||
|
||||
* [Python 3](mu808asm.py): supports assembling MU8A source files into both
|
||||
plain text (MU8) and binary (MU8B) machine code formats. Also supports
|
||||
disassembling MU8 and MU8B files into their MU8A sources and converting
|
||||
machine code between MU8 and MU8B formats.
|
||||
|
||||
These lists are going to be expanded as soon as new reference implementations
|
||||
appear.
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
### Why another 1V0 derivative that's even code-incompatible with 808UL?
|
||||
|
||||
808UL has served its purpose of fulfilling the author's vision on what an
|
||||
1V0-like (virtual) system should look like. However, its ISA still somewhat
|
||||
lacked rigidity and refinedness in terms of an optimal functionality
|
||||
distribution: there were a lot of instructions that didn't use all three
|
||||
command arguments, there were some instructions that could be parameterized
|
||||
better to avoid functionality duplication, and dedicated port I/O subcommands
|
||||
seemed like a hack on top of already available I/O routines. The mu808 ISA does
|
||||
a pretty good job at polishing those bits while retaining all the main features
|
||||
of the 808UL specification, also adding much stricter constraints on how the
|
||||
operating environment is to be organized.
|
||||
|
||||
### What can be considered a minimum compatible mu808 VM implementation?
|
||||
|
||||
A minimum implementation must implement all runtime instruction commands (from 0
|
||||
to 15 inclusive) and the interactive loop that accepts the following instruction
|
||||
numbers: any positive number, 0, -1, -2 and -5.
|
||||
|
||||
A minimum implementation is required to support floating point numbers as a data
|
||||
type, or, in case it's technically impossible, fixed point numbers with at least
|
||||
two digits after the decimal point (four or more recommended).
|
||||
|
||||
### 808UL supported sparse instructions, is this feature gone in mu808?
|
||||
|
||||
No, not at all. It's the MU8A assembly language that doesn't (yet) support this
|
||||
feature and autonumbers instructions based on their position in the source code.
|
||||
When writing machine code directly, you can specify any instruction numbers you
|
||||
like in the program.
|
||||
|
||||
### Why combine multiplication and addition into a single instruction command?
|
||||
|
||||
Because it is faster on modern architectures if both of them are required, and
|
||||
doesn't add any complexity if only one of them is. Additionally, the FMA
|
||||
operation is included in IEEE 754-2008, and all reference mu808 implementations
|
||||
are using it in case the target platform allows them to.
|
||||
|
||||
### How to do simple addition and simple multiplication, given only the FMA?
|
||||
|
||||
It might seem inconvenient at first, but the FMA operation is quite capable.
|
||||
First off, you already have a constant 0 always stored in 0, but it's advisable
|
||||
to store a constant 1 into some unused location, like 99. You can do this e.g.
|
||||
with the `set 1 0 99` operation. Then, let's go with three cases (assuming we
|
||||
have stored 1 into the data memory location 99):
|
||||
|
||||
1. Incrementing a memory location is as easy as `fma 99 99 [loc]`.
|
||||
2. Adding two numbers at loc1 and loc2 into loc2: `fma [loc1] 99 [loc2]`.
|
||||
3. Multiplying two numbers at loc1 and loc2 into loc2: `fma 0 [loc1] [loc2]`.
|
||||
|
||||
The only inconvenience with the FMA operation is that you need to sacrifice one
|
||||
of the operand locations to store the result. If you want to fully emulate the
|
||||
808UL's behavior, then you have to copy one of the operands into the target
|
||||
memory location first and then use it as the last parameter to the FMA command.
|
||||
|
||||
Credits
|
||||
-------
|
||||
Created by Luxferre in 2025, released into public domain with no warranties.
|
||||
@@ -0,0 +1,13 @@
|
||||
; A simple compound interest calculator in MU8A for mu808 VM
|
||||
; Prompts for the percentage and then for the period, outputs the resulting multiplier
|
||||
; Created by Luxferre in 2025, released into public domain
|
||||
|
||||
set 100 0 1 ; store the constant 100 at loc 1
|
||||
inp 2 3 0 ; prompt for the percentage into loc 2 and the period into loc 3
|
||||
div 2 1 4 ; divide the percentage value at loc 2 by the constant at loc 1 into loc 4
|
||||
set 1 0 5 ; set the loc 5 to 1
|
||||
fma 5 4 5 ; add the constant at loc 5 to the value at loc 4 into loc 5
|
||||
nel 1 5 5 ; replace loc 5 with its ln
|
||||
fma 0 3 5 ; replace loc 5 with loc 3 * ln loc 5
|
||||
nel 0 5 5 ; replace loc 4 with its nexp
|
||||
out 5 5 0 ; output the resulting value
|
||||
@@ -0,0 +1,19 @@
|
||||
set 72 0 1 ; fill in the data bytes from 1 to 10
|
||||
set 101 0 2
|
||||
set 108 0 3
|
||||
set 108 0 4
|
||||
set 111 0 5
|
||||
set 114 0 6
|
||||
set 108 0 7
|
||||
set 100 0 8
|
||||
set 33 0 9
|
||||
set 10 0 10 ; end the string with an LF character for newline
|
||||
set 1 0 50 ; set the constant 1 to memory loc 50
|
||||
set 1 0 10 ; set the first address to 1 at loc 10
|
||||
set 10 0 11 ; set the counter variable to 10 at loc 11
|
||||
:lp set 12 0 12 ; prepare the loc 12 with its own address
|
||||
cpy 2 10 12 ; load the contents of the current address at loc 10 into loc 12
|
||||
out 12 12 1 ; output the character at the current address to port 1
|
||||
sub 11 50 11 ; decrement the counter at loc 11
|
||||
fma 50 50 10 ; increment the current address at loc 10
|
||||
jmp 1 11 :lp ; jump to the loop start if the counter is over zero
|
||||
@@ -0,0 +1,55 @@
|
||||
; Linear regression calculator in MU8A for mu808 VM
|
||||
; Enter the pairs number by number, end with 0,0 pair
|
||||
; The program will then output A and B parameters of A + Bx
|
||||
; linear function and then the correlation coefficient r
|
||||
; Created by Luxferre in 2025, released into public domain
|
||||
|
||||
set 0 0 10 ; x-sum to loc 10
|
||||
set 0 0 11 ; y-sum to loc 11
|
||||
set 0 0 12 ; x-square-sum to loc 12
|
||||
set 0 0 13 ; y-square-sum to loc 13
|
||||
set 0 0 14 ; xy-sum to loc 14
|
||||
set 0 0 15 ; n to loc 15
|
||||
set 1 0 50 ; constant 1 to loc 50
|
||||
:lp inp 1 2 0 ; loop start; input xi and yi into loc 1 and loc 2
|
||||
cpy 1 1 3 ; copy x into loc 3
|
||||
fma 0 3 3 ; save x-squared into loc 3
|
||||
cpy 1 2 4 ; copy y into loc 4
|
||||
fma 0 4 4 ; save y-squared into loc y
|
||||
cpy 1 2 5 ; copy y into loc 5
|
||||
fma 0 1 5 ; save xy into loc 5
|
||||
fma 1 50 10 ; update x-sum (Sx) => loc 10
|
||||
fma 2 50 11 ; update y-sum (Sy) => loc 11
|
||||
fma 3 50 12 ; update x-square-sum (Sxx) => loc 12
|
||||
fma 4 50 13 ; update y-square-sum (Syy) => loc 13
|
||||
fma 5 50 14 ; update xy-sum (Sxy) => loc 14
|
||||
fma 50 50 15 ; increment n at loc 15
|
||||
cpy 1 3 6 ; copy x-squared into loc 6
|
||||
fma 4 50 6 ; add x-squared and y-squared into loc 6
|
||||
jmp 1 6 :lp ; loop back to the input if the square sum is over zero
|
||||
sub 15 50 15 ; decrement last n at loc 15 to omit the (0,0) input
|
||||
cpy 1 15 1 ; copy n to loc 1
|
||||
fma 0 14 1 ; n * Sxy => loc 1
|
||||
cpy 1 11 5 ; copy Sy to loc 5
|
||||
fma 0 10 5 ; Sx * Sy => loc 5
|
||||
sub 1 5 6 ; n * Sxy - Sx * Sy => loc 6 (to be stored for r calculation)
|
||||
cpy 1 15 3 ; copy n to loc 3
|
||||
fma 0 12 3 ; n * Sxx => loc 3
|
||||
cpy 1 10 4 ; copy Sx to loc 4
|
||||
fma 0 10 4 ; Sx squared => loc 4
|
||||
sub 3 4 3 ; n * Sxx - Sx^2 => loc 3 (to be stored for r calculation)
|
||||
div 6 3 21 ; coefficient B => loc 21
|
||||
cpy 1 10 1 ; copy Sx to loc 1
|
||||
fma 0 21 1 ; B * Sx => loc 1
|
||||
sub 11 1 1 ; Sy - B * Sx => loc 1
|
||||
div 1 15 20 ; coefficient A => loc 20
|
||||
cpy 1 11 2 ; copy Sy to loc 2
|
||||
fma 0 11 2 ; (Sy) ^ 2 => loc 2
|
||||
cpy 1 15 5 ; n to loc 5
|
||||
fma 0 13 5 ; n * Syy => loc 5
|
||||
sub 5 2 2 ; n * Syy - Sy^2 => loc 2
|
||||
cpy 1 3 1 ; loc 3 => loc 1
|
||||
fma 0 2 1 ; loc 3 * loc 2 => loc 1
|
||||
sqr 0 1 1 ; sqrt(loc 1) => loc 1
|
||||
div 6 1 22 ; correlation coefficient r => loc 22
|
||||
out 20 22 0 ; output all three resulting numbers
|
||||
@@ -0,0 +1,77 @@
|
||||
; Lunar Lander game in MU8A for mu808 VM
|
||||
; On each turn, the following parameters are displayed in this order:
|
||||
; altitude (meters), speed (m/s) and remaining fuel (kg)
|
||||
; Your goal is to apply (or not apply) thrust
|
||||
; (values that make any sense are 0 to 2000) every 10 seconds of flight
|
||||
; and get the lunar module to land safely without running out of fuel.
|
||||
; At the end, the game shows one of the following statuses:
|
||||
; 4444 is a disaster landing with no survivors,
|
||||
; 5555 is a crash landing with the crew surviving the impact,
|
||||
; 6666 is a hard landing with some damage to the pod,
|
||||
; 7777 is a good landing,
|
||||
; 8888 is a perfect landing.
|
||||
; Created by Luxferre in 2025, released into public domain
|
||||
|
||||
set 1 0 50 ; set constant 1 to loc 50
|
||||
set 10 0 52 ; altitude threshold AND time period into loc 52
|
||||
set 16 2500 53 ; freefall speed delta into loc 53
|
||||
set 7480 0 54 ; capsule weight (in kg) into loc 54
|
||||
set 3 4483 55 ; fuel burn rate (3.4483) into loc 55
|
||||
set 2900 0 56 ; exhaust velocity (m/s) into loc 56
|
||||
set 1930 0 40 ; starting altitude (m) into loc 40
|
||||
set 100 0 57 ; store 100 into loc 57
|
||||
fma 0 57 40 ; multiply this altitude value by 100
|
||||
set 1609 0 41 ; starting speed (m/s) into loc 41
|
||||
set 7260 0 42 ; starting fuel (in kg) into loc 42
|
||||
set 4444 0 30 ; disaster code into loc 30
|
||||
set 5555 0 31 ; crash landing code into loc 31
|
||||
set 6666 0 32 ; damage landing code into loc 32
|
||||
set 7777 0 33 ; good landing code into loc 33
|
||||
set 8888 0 34 ; perfect landing code into loc 34
|
||||
set 26 6667 0 36 ; criterion for crash landing into loc 36
|
||||
set 9 7300 0 37 ; criterion for damage landing into loc 37
|
||||
set 4 4445 0 38 ; criterion for good landing into loc 38
|
||||
set 0 4500 0 39 ; criterion for perfect landing into loc 39
|
||||
:lp out 40 42 0 ; loop start; print altitude, speed and fuel
|
||||
set 0 0 2 ; set fuel loss in loc 2 to 0
|
||||
jmp 4 42 :cnt ; skip prompting for thrust if already out of fuel
|
||||
inp 1 1 0 ; prompt for thrust into loc 1
|
||||
cpy 1 1 2 ; copy thrust into loc 2
|
||||
fma 0 55 2 ; multiply thrust by fuel burn rate to get fuel loss in loc 2
|
||||
cpy 1 42 3 ; copy fuel weight into loc 3
|
||||
:cnt fma 54 50 3 ; add capsule weight and fuel weight to get m0 in loc 3
|
||||
sub 3 2 4 ; subtract m0 and fuel loss value to get m1 in loc 4
|
||||
div 3 4 1 ; divide m0 by m1 and rewrite the result into loc 1
|
||||
nel 1 1 1 ; calculate natural logarithm of the previous result
|
||||
fma 0 56 1 ; multiply the result by the exhaust velocity to get thrust speed delta
|
||||
sub 41 1 41 ; subtract the thrust speed delta from the current speed
|
||||
fma 53 50 41 ; add the freefall speed delta to the current speed
|
||||
cpy 1 41 3 ; copy the speed value into loc 3
|
||||
fma 0 52 3 ; multiply speed by time period into loc 3
|
||||
sub 40 3 40 ; decrease the altitude by the result of this operation
|
||||
sub 42 2 42 ; decrease the amount of fuel by fuel loss value still at loc 2
|
||||
jmp 1 42 :flc ; skip the next instruction if the amount of fuel is positive
|
||||
set 0 0 42 ; just set the amount of fuel to zero if it's negative
|
||||
:flc jmp 1 40 :al ; do the same for altitude
|
||||
set 0 0 40 ; set it to zero if negative
|
||||
:al sub 40 52 1 ; subtract the threshold from the altitude
|
||||
jmp 1 1 :lp ; go to the loop start if the altitude is above the threshold
|
||||
out 40 42 0 ; print the final altitude/speed/fuel
|
||||
sub 41 39 1 ; subtract the perfect speed
|
||||
jmp 1 1 :good ; skip if > 0
|
||||
out 34 34 0 ; output the perfect score
|
||||
jmp 6 0 :end ; go to end
|
||||
:good sub 41 38 1 ; subtract the good speed
|
||||
jmp 1 1 :dmg ; skip if > 0
|
||||
out 33 33 0 ; output the good score
|
||||
jmp 6 0 :end ; go to end
|
||||
:dmg sub 41 37 1 ; subtract the damage speed
|
||||
jmp 1 1 :crsh ; skip if > 0
|
||||
out 32 32 0 ; output the damage score
|
||||
jmp 6 0 :end ; go to end
|
||||
:crsh sub 41 36 1 ; subtract the crash speed
|
||||
jmp 1 1 :disa ; skip if > 0
|
||||
out 31 31 0 ; output the crash score
|
||||
jmp 6 0 :end ; go to end
|
||||
:disa out 30 30 0 ; output the disaster score
|
||||
:end nop 0 0 0 ; program end label
|
||||
@@ -0,0 +1,68 @@
|
||||
; Bulls and Cows game in MU8A for mu808 VM
|
||||
; Enter your guesses digit by digit, you have 7 attempts
|
||||
; After each guess, the game replies with bulls.cows
|
||||
; (if you have 4.0, you win)
|
||||
; On victory or after running out of attempts, the game
|
||||
; displays the target digits and halts
|
||||
; Created by Luxferre in 2025, released into public domain
|
||||
|
||||
set 0 0 60 ; store digits 0 to 9 to locations 60 to 69
|
||||
set 1 0 61
|
||||
set 2 0 62
|
||||
set 3 0 63
|
||||
set 4 0 64
|
||||
set 5 0 65
|
||||
set 6 0 66
|
||||
set 7 0 67
|
||||
set 8 0 68
|
||||
set 9 0 69
|
||||
set 1 0 50 ; store 1 into loc 50
|
||||
set 4 0 11 ; store the counter to loc 11
|
||||
set 60 0 70 ; store the source base address 60 to loc 70
|
||||
set 80 0 71 ; store the target base address 80 to loc 71
|
||||
set 10 0 3 ; store the constant 10 to loc 3
|
||||
:dsl set 9 0 1 ; store the upper boundary to loc 1
|
||||
rnd 0 1 1 ; store a random digit into loc 1
|
||||
fma 70 50 1 ; add the source base address to loc 1
|
||||
set 2 0 2 ; init loc 2 with its own address
|
||||
cpy 2 1 2 ; read the value at that loc 1 address back into loc 2
|
||||
sub 2 3 4 ; subtract 10 from the read value into loc 4
|
||||
jmp 0 4 :dsl ; jump back to digit selection if the value at loc 4 is zero
|
||||
iat 0 3 1 ; enable indirect addressing to...
|
||||
cpy 0 0 0 ; ...set the value at the address still at loc 1 to 10
|
||||
cpy 1 11 1 ; copy the counter into loc 1
|
||||
fma 71 50 1 ; add the counter and the target base address (result is 81..84 at loc 1)
|
||||
set 2 0 5 ; set the constant 2 into loc 5
|
||||
cpy 2 5 1
|
||||
sub 11 50 11 ; decrement the counter at loc 11
|
||||
jmp 1 11 :dsl ; jump back to digit selection if the counter is over zero
|
||||
set 0 1000 51 ; store 0.1 to loc 51
|
||||
set 7 0 11 ; the number to guess is at loc 81..84; store the attempt counter to loc 11
|
||||
:prm inp 91 94 0 ; prompt the player to enter the number digit by digit into loc 91 to 94
|
||||
set 90 0 70 ; store the entered digits base address to loc 70
|
||||
set 0 0 20 ; init bull/cow counter at loc 20
|
||||
set 4 0 21 ; init outer loop counter at loc 21
|
||||
:olp set 4 0 22 ; start of the outer loop; init inner loop counter at loc 22
|
||||
:ilp cpy 1 21 15 ; start of the inner loop; copy the outer counter
|
||||
fma 71 50 15 ; shape the address of the target digit in loc 15
|
||||
cpy 1 22 16 ; copy the inner counter
|
||||
fma 70 50 16 ; shape the address of the entered digit in loc 16
|
||||
iat 15 16 50 ; prepare to save the digits difference into loc 1 (1 is stored at loc 50)
|
||||
sub 0 0 0 ; do it
|
||||
jmp 5 1 :ei ; jump to the next comparator if the digits don't match
|
||||
sub 21 22 2 ; save the _indices_ difference into loc 2
|
||||
jmp 5 2 :cc ; jump to the cow counter if the indices don't match
|
||||
fma 50 50 20 ; increase the bull counter if they do
|
||||
jmp 6 0 :ei ; skip the next instruction
|
||||
:cc fma 51 50 20 ; increase the cow counter if they don't
|
||||
:ei sub 22 50 22 ; decrease the inner loop counter
|
||||
jmp 1 22 :ilp ; jump to the start of the inner loop if the inner counter is over zero
|
||||
sub 21 50 21 ; decrease the outer loop counter
|
||||
jmp 1 21 :olp ; jump to the start of the outer loop if the outer counter is over zero
|
||||
out 20 20 0 ; output the match result
|
||||
set 4 0 7 ; store the constant 4.0 at loc 7
|
||||
sub 20 7 1 ; store the difference between the result and 4 to loc 1
|
||||
jmp 0 1 :end ; jump to the last instruction if they match
|
||||
sub 11 50 11 ; decrement the attempt counter at loc 11
|
||||
jmp 1 11 :prm ; jump to guess prompt if the counter is over zero
|
||||
:end out 81 84 0 ; output the target number before halting
|
||||
@@ -0,0 +1,225 @@
|
||||
#!/usr/bin/env python3
|
||||
# mu808 VM reference implementation in Python 3 / MicroPython
|
||||
# See the documentation in README.md
|
||||
# Created by Luxferre in 2025, released into public domain
|
||||
|
||||
import sys, math, random, re
|
||||
|
||||
advterm = False
|
||||
try:
|
||||
import tty, termios # for the character input port
|
||||
advterm = True
|
||||
except:
|
||||
pass
|
||||
|
||||
MEMLIMIT:int = 16384 # 16K floats for data, 64K integers for program
|
||||
PMEM = [[0,0,0,0] for i in range(0, MEMLIMIT)] # program memory
|
||||
DMEM = [0.0 for i in range(0, MEMLIMIT)] # data memory
|
||||
traceflag:bool = False
|
||||
runlimit:int = MEMLIMIT # default runlimit is the program space size
|
||||
runcount:int = 0
|
||||
|
||||
# mu808 mnemonics for assembly and disasssembly
|
||||
mnemos = ['nop', 'jmp', 'iat', 'out', 'inp', 'set', 'cpy', 'fma',
|
||||
'sub', 'div', 'mdf', 'abs', 'sqr', 'nel', 'tri', 'rnd']
|
||||
|
||||
# port output
|
||||
def portout(port:int, data:float):
|
||||
if port == 0: # standard value output port
|
||||
print(data)
|
||||
elif port == 1: # character output port
|
||||
sys.stdout.write(chr(int(data)&255))
|
||||
sys.stdout.flush()
|
||||
|
||||
# port input
|
||||
def portin(port:int):
|
||||
val = 0
|
||||
if port == 0: # standard input port
|
||||
try:
|
||||
val = float(input())
|
||||
except ValueError:
|
||||
val = 0
|
||||
if port == 2: # character input port
|
||||
if advterm: # normal OS with termios
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
try:
|
||||
tty.setraw(fd)
|
||||
ch = sys.stdin.read(1)
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
else: # crippled OS without termios
|
||||
ch = sys.stdin.read(1)
|
||||
val = ord(ch)
|
||||
return float(val)
|
||||
|
||||
# instruction line execution function
|
||||
# the main mu808 logic is defined here
|
||||
def ilexec(lno:int, cmd:int=0, x:int=0, y:int=0, z:int=0):
|
||||
global runcount, PMEM, DMEM
|
||||
halt = False
|
||||
while not halt:
|
||||
if traceflag:
|
||||
print(f'PC: {lno} INSTR: {cmd} {x} {y} {z}')
|
||||
DMEM[0] = 0.0 # force the value at 0 to always be 0
|
||||
data_override = False
|
||||
# perform a boundary check and prefetch the memory values
|
||||
bcheck = True
|
||||
try:
|
||||
v1 = DMEM[x]
|
||||
v2 = DMEM[y]
|
||||
v3 = DMEM[z]
|
||||
except IndexError:
|
||||
bcheck = False
|
||||
if bcheck:
|
||||
# command switch starts here (commands <=0 and >15 are nops)
|
||||
if cmd == 1: # JMP: conditional/unconditional, direct/indirect jump
|
||||
if (v2 == 0 and x == 0) or (v2 > 0 and x == 1) or (v2 < 0 and x == 2) \
|
||||
or (v2 >= 0 and x == 3) or (v2 <= 0 and x == 4) or (v2 != 0 and x == 5) \
|
||||
or x == 6:
|
||||
halt = False
|
||||
lno = z - 1
|
||||
elif (v2 == 0 and x == 7) or (v2 > 0 and x == 8) or (v2 < 0 and x == 9) \
|
||||
or (v2 >= 0 and x == 10) or (v2 <= 0 and x == 11) or (v2 != 0 and x == 12) \
|
||||
or x == 13:
|
||||
halt = False
|
||||
lno = int(v3) - 1
|
||||
elif cmd == 2: # IAT
|
||||
data_override = True
|
||||
elif cmd == 3: # OUT
|
||||
for i in range(x, y + 1):
|
||||
portout(z, DMEM[i])
|
||||
elif cmd == 4: # INP
|
||||
for i in range(x, y + 1):
|
||||
DMEM[i] = portin(z)
|
||||
elif cmd == 5: # SET
|
||||
DMEM[z] = (x % 10000) + (y % 10000) / 10000.
|
||||
elif cmd == 6: # CPY
|
||||
if x == 0:
|
||||
DMEM[z] = y
|
||||
elif x == 1:
|
||||
DMEM[z] = v2
|
||||
elif int(v3) < MEMLIMIT and int(v2) < MEMLIMIT:
|
||||
DMEM[int(v3)] = DMEM[int(v2)]
|
||||
elif cmd == 7: # FMA (v1 + v2 * v3)
|
||||
try:
|
||||
DMEM[z] = math.fma(v3, v2, v1)
|
||||
except:
|
||||
DMEM[z] = v1 + v2 * v3
|
||||
elif cmd == 8: # SUB
|
||||
DMEM[z] = v1 - v2
|
||||
elif cmd == 9: # DIV
|
||||
if v2 == 0:
|
||||
DMEM[z] = 0
|
||||
else:
|
||||
DMEM[z] = v1 / v2
|
||||
elif cmd == 10: # MDF
|
||||
if v2 == 0:
|
||||
DMEM[z] = math.floor(v1)
|
||||
else:
|
||||
DMEM[z] = float(int(v1) % int(v2))
|
||||
elif cmd == 11: # ABS
|
||||
DMEM[z] = math.fabs(v2)
|
||||
elif cmd == 12: # SQR
|
||||
DMEM[z] = math.sqrt(math.fabs(v2))
|
||||
elif cmd == 13: # NEL
|
||||
if x == 0:
|
||||
DMEM[z] = math.exp(v2)
|
||||
else:
|
||||
if v2 == 0:
|
||||
DMEM[z] = 0
|
||||
else:
|
||||
DMEM[z] = math.log(math.fabs(v2))
|
||||
elif cmd == 14: # TRI
|
||||
if x == 0:
|
||||
DMEM[z] = math.sin(v2)
|
||||
elif x == 1:
|
||||
DMEM[z] = math.cos(v2)
|
||||
else:
|
||||
DMEM[z] = math.atan(v2)
|
||||
elif cmd == 15: # RND
|
||||
DMEM[z] = float(random.randint(int(v1), int(v2)))
|
||||
# command switch ends here
|
||||
# increment the program counter
|
||||
lno += 1
|
||||
if lno >= MEMLIMIT or lno < 1 or (runlimit > 0 and runcount > runlimit):
|
||||
if traceflag:
|
||||
print('Memory limit or runlimit hit, halting...')
|
||||
halt = True
|
||||
else: # fetch the next instruction
|
||||
runcount += 1
|
||||
if data_override and bcheck:
|
||||
cmd = PMEM[lno][0]
|
||||
x = int(v1) % MEMLIMIT
|
||||
y = int(v2) % MEMLIMIT
|
||||
z = int(v3) % MEMLIMIT
|
||||
else:
|
||||
cmd, x, y, z = tuple(PMEM[lno])
|
||||
|
||||
# instruction line entry function
|
||||
def ilenter(lno:int, cmd:int=0, x:int=0, y:int=0, z:int=0):
|
||||
global traceflag, runlimit, runcount
|
||||
if lno > 0: # record the instruction line in memory
|
||||
PMEM[lno] = [cmd, x, y, z]
|
||||
elif lno == 0: # immediately execute the instruction line
|
||||
runcount = 0
|
||||
ilexec(lno, cmd, x, y, z)
|
||||
elif lno == -1: # display a range of instructions from cmd to x
|
||||
for i in range(cmd, x + 1):
|
||||
print(f'@{i}:\t{mnemos[PMEM[i][0]]}|{PMEM[i][0]}', PMEM[i][1],
|
||||
PMEM[i][2], PMEM[i][3])
|
||||
elif lno == -2: # clear a range of data or instructions from x to y
|
||||
for i in range(x, y + 1):
|
||||
try:
|
||||
if cmd == 0:
|
||||
PMEM[i] = [0,0,0,0]
|
||||
else:
|
||||
DMEM[i] = 0.0
|
||||
except IndexError:
|
||||
pass
|
||||
elif lno == -3: # turn on/off tracing
|
||||
if cmd == 0:
|
||||
traceflag = False
|
||||
print('Tracing off')
|
||||
else:
|
||||
traceflag = True
|
||||
print('Tracing on')
|
||||
elif lno == -4: # set the runlimit to the value of cmd
|
||||
runlimit = cmd
|
||||
print('Runlimit set to', runlimit)
|
||||
elif lno == -5: # exit to the environment
|
||||
print('Bye!')
|
||||
sys.exit(0)
|
||||
|
||||
# main REPL environment
|
||||
if __name__ == '__main__':
|
||||
print(f'mu808 v1 by Luxferre\nPROGMEM: {MEMLIMIT} steps\nDATAMEM: {MEMLIMIT} floats')
|
||||
vreg = re.compile(r'[^-\d]+')
|
||||
if len(sys.argv) > 1: # preload the file contents
|
||||
fc = ''
|
||||
try:
|
||||
fd = open(sys.argv[1], 'r')
|
||||
fc = fd.read()
|
||||
close(fd)
|
||||
except:
|
||||
pass
|
||||
instr = [] # current instruction cache
|
||||
for instrpart in vreg.split(fc):
|
||||
if len(instrpart) > 0:
|
||||
instr.append(int(instrpart))
|
||||
if len(instr) == 5: # full instruction registered
|
||||
ilenter(int(instr[0]), abs(int(instr[1])),
|
||||
abs(int(instr[2])), abs(int(instr[3])), abs(int(instr[4])))
|
||||
instr = []
|
||||
instr = [] # current instruction cache
|
||||
while True: # main interactive loop
|
||||
rinput = input('> ')
|
||||
if len(rinput) > 0:
|
||||
for v in vreg.split(rinput): # loop over the current input
|
||||
if len(v) > 0:
|
||||
instr.append(v)
|
||||
if len(instr) == 5: # full instruction registered
|
||||
ilenter(int(instr[0]), abs(int(instr[1])),
|
||||
abs(int(instr[2])), abs(int(instr[3])), abs(int(instr[4])))
|
||||
instr = []
|
||||
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
# a simple assembler/disassembler for the mu808 VM
|
||||
# supports both text and binary program formats
|
||||
# usage: mu808asm [at|ab|dt|db|t2b|b2t] file outfile
|
||||
# modes:
|
||||
# at: assemble to plaintext (MU8) machine code
|
||||
# ab: assemble to binary (MU8B) machine code
|
||||
# dt: disassemble plaintext (MU8) machine code
|
||||
# db: disassemble binary (MU8B) machine code
|
||||
# t2b: convert from plaintext MU8 to binary MU8B
|
||||
# b2t: convert from binary MU8B to plaintext MU8
|
||||
|
||||
import sys, re, struct
|
||||
|
||||
# mu808 mnemonics for assembly and disasssembly
|
||||
mnemos = ['nop', 'jmp', 'iat', 'out', 'inp', 'set', 'cpy', 'fma',
|
||||
'sub', 'div', 'mdf', 'abs', 'sqr', 'nel', 'tri', 'rnd']
|
||||
|
||||
# converts the MU8A source code to the plaintext machine code representation
|
||||
def assemble(source):
|
||||
labels = {}
|
||||
out = ''
|
||||
lno = 0
|
||||
vreg = re.compile(r'\s+')
|
||||
for line in source.split('\n'):
|
||||
fields = []
|
||||
line = line.split(';')[0].strip()
|
||||
if len(line) > 0: # actual line to be counted on
|
||||
lno += 1 # populate the line number
|
||||
fields = vreg.split(line)[:5] # split it into fields
|
||||
if fields[0].startswith(':'): # this is a label
|
||||
labels[fields[0]] = lno # remember the label
|
||||
fields = fields[1:] # remove the label from fields
|
||||
try:
|
||||
cmd = mnemos.index(fields[0].lower())
|
||||
except ValueError:
|
||||
try:
|
||||
cmd = int(cmd)
|
||||
except:
|
||||
print('Assembly error - unknown mnemonic at line', lno)
|
||||
sys.exit(1)
|
||||
try:
|
||||
out += ' '.join([str(lno), str(cmd), fields[1], fields[2], fields[3]]) + '\n'
|
||||
except IndexError:
|
||||
print('Assembly error - not enough operands at line', lno)
|
||||
sys.exit(1)
|
||||
|
||||
for lbl, lineno in labels.items():
|
||||
out = out.replace(lbl, str(lineno))
|
||||
return out
|
||||
|
||||
# converts the plaintext machine code representation to MU8A source code
|
||||
def disassemble(machcode):
|
||||
vreg = re.compile(r'\s+')
|
||||
iindex = 0
|
||||
instr = []
|
||||
instrs = []
|
||||
for instrpart in vreg.split(machcode):
|
||||
if iindex == 5:
|
||||
instrs.append(instr)
|
||||
instr = []
|
||||
iindex = 0
|
||||
if len(instrpart) > 0:
|
||||
instr.append(int(instrpart))
|
||||
iindex += 1
|
||||
# now, instrs contains 5-number groups
|
||||
assembly = [None for i in range(0, 16384)]
|
||||
labels = {}
|
||||
for instr in instrs: # iterate over each instruction
|
||||
lno, cmd, x, y, z = instr
|
||||
try:
|
||||
mnemo = mnemos[cmd]
|
||||
except IndexError:
|
||||
print('Disassembly error - unknown opcode at line', lno)
|
||||
sys.exit(1)
|
||||
if cmd == 1: # save the label for jump instructions
|
||||
labels[z] = ':lbl_' + str(z)
|
||||
assembly[lno] = [mnemo, str(x), str(y), labels[z]]
|
||||
else:
|
||||
assembly[lno] = [mnemo, str(x), str(y), str(z)]
|
||||
out = ''
|
||||
lno = 0
|
||||
for asline in assembly:
|
||||
if lno in labels:
|
||||
out += labels[lno] + ' '
|
||||
if asline is not None:
|
||||
out += ' '.join(asline) + '\n'
|
||||
lno += 1
|
||||
return out
|
||||
|
||||
# converts the plaintext machine code representation to binary representation
|
||||
def tobinary(txtrep):
|
||||
instrs = [] # instruction list to store here
|
||||
vreg = re.compile(r'\s+')
|
||||
iindex = 0
|
||||
instr = []
|
||||
for instrpart in vreg.split(txtrep):
|
||||
if iindex == 5:
|
||||
instrs.append(instr)
|
||||
instr = []
|
||||
iindex = 0
|
||||
if len(instrpart) > 0:
|
||||
instr.append(int(instrpart))
|
||||
iindex += 1
|
||||
# now, instrs contains 5-number groups
|
||||
out = b''
|
||||
# the format is: cmd (8 bits), lno (14 bits), arg1 to arg3 (14 bits each)
|
||||
for instr in instrs: # iterate over each instruction
|
||||
b1 = instr[1] & 15
|
||||
b2 = (instr[0] >> 6) & 255
|
||||
b3 = ((instr[0] << 2) | (instr[2] >> 12)) & 255
|
||||
b4 = (instr[2] >> 4) & 255
|
||||
b5 = ((instr[2] << 4) | (instr[3] >> 10)) & 255
|
||||
b6 = (instr[3] >> 2) & 255
|
||||
b7 = ((instr[3] << 6) | (instr[4] >> 8)) & 255
|
||||
b8 = instr[4] & 255
|
||||
out += struct.pack('BBBBBBBB', b1, b2, b3, b4, b5, b6, b7, b8)
|
||||
return out
|
||||
|
||||
# converts the binary machine code representation to plaintext representation
|
||||
def totext(binrep):
|
||||
out = ''
|
||||
while len(binrep) > 0:
|
||||
chunk = binrep[0:8]
|
||||
binrep = binrep[8:]
|
||||
b1, b2, b3, b4, b5, b6, b7, b8 = struct.unpack('BBBBBBBB', chunk)
|
||||
cmd = b1 & 15
|
||||
lno = ((b2 << 6) | (b3 >> 2)) & 16383
|
||||
x = ((b3 << 12) | (b4 << 4) | (b5 >> 4)) & 16383
|
||||
y = ((b5 << 10) | (b6 << 2) | (b7 >> 6)) & 16383
|
||||
z = ((b7 << 8) | b8) & 16383
|
||||
out += ' '.join([str(lno), str(cmd), str(x), str(y), str(z)]) + '\n'
|
||||
return out
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: mu808asm [at|ab|dt|db|t2b|b2t] file outfile")
|
||||
return
|
||||
mode = sys.argv[1]
|
||||
srcfile = sys.argv[2]
|
||||
targetfile = sys.argv[3]
|
||||
srctext = ''
|
||||
inpmode = 'r'
|
||||
if mode == 'b2t' or mode == 'db':
|
||||
inpmode = 'rb'
|
||||
try:
|
||||
fd = open(srcfile, inpmode)
|
||||
srctext = fd.read()
|
||||
close(fd)
|
||||
except:
|
||||
pass
|
||||
if len(srctext) > 0:
|
||||
outmode = 'w'
|
||||
output = ''
|
||||
if mode == 'b2t': # binary-to-text
|
||||
output = totext(srctext)
|
||||
elif mode == 't2b': # text-to-binary
|
||||
output = tobinary(srctext)
|
||||
outmode = 'wb'
|
||||
elif mode == 'dt': # disassemble text
|
||||
output = disassemble(srctext)
|
||||
elif mode == 'db': # disassemble binary
|
||||
srctext = totext(srctext)
|
||||
output = disassemble(srctext)
|
||||
else: # assemble
|
||||
output = assemble(srctext) # default mode
|
||||
if mode == 'ab':
|
||||
output = tobinary(output)
|
||||
outmode = 'wb'
|
||||
# write the output file
|
||||
try:
|
||||
fd = open(targetfile, outmode)
|
||||
fd.write(output)
|
||||
close(fd)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print('Nothing to process!')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user