Getting started

This commit is contained in:
Reid 'arrdem' McKenzie 2022-12-10 12:51:28 -07:00
commit f46b47c70b
10 changed files with 344 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "uxn"
version = "0.1.0"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "uxn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

21
LICENSE.md Normal file
View file

@ -0,0 +1,21 @@
ANTI-CAPITALIST SOFTWARE LICENSE (v 1.4)
Copyright © 2022 Reid D. 'arrdem' McKenzie
This is anti-capitalist software, released for free use by individuals and organizations that do not operate by capitalist principles.
Permission is hereby granted, free of charge, to any person or organization (the "User") obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, distribute, and/or sell copies of the Software, subject to the following conditions:
1. The above copyright notice and this permission notice shall be included in all copies or modified versions of the Software.
2. The User is one of the following:
a. An individual person, laboring for themselves
b. A non-profit organization
c. An educational institution
d. An organization that seeks shared profit for all of its members, and allows non-members to set the cost of their labor
3. If the User is an organization with owners, then all owners are workers and all workers are owners with equal equity and/or equal vote.
4. If the User is an organization, then the User is not law enforcement or military, or working for or under either.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

50
README.md Normal file
View file

@ -0,0 +1,50 @@
# Varvara's rusty bicycle
This project is a (re)implementation of the Varvara personal computer in Rust, intended to target WASM and a web browser emulator.
- [Varvara](./doc/varvara.md) ([upstream docs](https://wiki.xxiivv.com/site/varvara.html)) is an 8bi personal computer reminiscent of video game controllers. Varvara is also a character in [Rekka](https://kokorobot.ca/site/home.html)'s art; this is her personal computer.
- [Tal](./doc/tal.md) ([upstream docs](https://wiki.xxiivv.com/site/uxntal.html)) is the reference assembler language for programming. The Tal is a friendly, hoofed quadruped implied to be helping Varvara.
- [UXN](./doc/uxn.md) ([upsteram docs](https://wiki.xxiivv.com/site/uxntal_reference.html)) is the instruction set underlying the Varvara computer.
## WTF UXN?
The entire uxn/tal/varvara stack will likely scan as unusual to professional developers.
Why design an 8/16bi microarchitecture in 202X?
The answer is that varvara is best understood as a political and art project.
Devine and Rekka are artists, living on a boat and embracing a minimalist/solarpunk/permaculture politic.
While Devine appears to have more professional software development experience than they let on, the entire project is amateur.
To those who've spent time with computer design, many design decisions in the system may seem odd.
Fundamentally the answer to many of the WTFs is that ... varvara isn't designed for that.
Why doesn't it have signed arithmetic?
Why doesn't it have memory protection?
Why doesn't it have floating point?
Why doesn't it have a real multiprocessing/interrupt model?
Why doesn't it have a networking model?
Why doesn't it have ...
Because it's an unscalable[¹](https://arxiv.org/abs/2010.08850) vision of personal computing.
It's designed to let a person with limited computing and network resources build their own reality marble of personal software.
"you build your pocket universe, and then in you go"
There's [a bunch of software](https://wiki.xxiivv.com/site/roms.html) for Varvara, all of which should emphasize this point.
Building [your own spreadsheet engine](https://wiki.xxiivv.com/site/nebu.html) is certainly a statement.
## Why this project?
Having done some compiler and computer architecture work, the Varvara computer was ... baffling.
The documentation is far more artistic than implementer or even user centric.
This project is the results of my effort to reverse engineer and document the Varvara system well enough to re-implement it.
Varvara isn't my vision of my personal computer.
I've got different requirements and expectations.
But I have a lot of respect for the relative success of the project, especially considering that I've spun my wheels on my own vision for years.
So consider this my meditation on an artifact.
## License
This software is released under the [Anti-capitalist software license v1.4](./LICENSE.md).

94
doc/tal.md Normal file
View file

@ -0,0 +1,94 @@
# TAL Assembler
See also [the UXN virtual machine](./uxn.md)
TAL is a Forth like two-pass assembler language translating directly to UXN memory images.
## Words
Words are up to 63 consecutive non-whitespace characters.
For instance `0x75786E00` (ascii UXN\0) would be one TAL "word" although its value is many bytes.
`foo`, `bar-baz` and `quix/qux` would all be examples of words.
Words starting with `_` are defined to be relative references.
Words starting with `,` are
## Comments
Comments in TAL are written `( ... )` and support nesting. Eg. `( () )` is a valid comment. `( ( )` is not.
TAL does not have a way to "close all start comments" like Java and some other languages do.
## Includes
TAL files can include other files by writing `~<filename>`.
For instance the `uxnasm.tal` file writes `~projects/library/string.tal` to include implementations of string functions.
As with other preprocessor and assembler languages, TAL does not support namespacing, renaming or selective importing.
- All included code is assembled at the point where it is included.
- TAL does not support multiple definition or idempotent includes, and will error on repeated or recursive inclusion.
## Macros
Macros are sequences of instructions which may be repeated.
Macros are defined by writing `%macro-name { ... }`.
The canonical UXNASM does not allow macros to exceed 64 words in size.
When macros are invoked by using the macro-name as a bare word, the contents of the macro will be inserted.
Sub-macro references are supported and will be expanded with no recursion guards or limit.
## Padding
`|<number>` "pad-absolute" pads the resulting UXN rom to a given absolute address.
For instance `|0x0000` would explicitly align the assembler's point to `0x0000`.
`$<number>` "pad-relative" pads the UXN rom by the specified number of words (bytes).
For instance `$2` would move the assembler's point forwards two words.
## Labels
`@<word>` defines a top-level label.
For instance `@foo` would make the word `foo` a valid symbol for use elsewhere.
Defining a top-level word establishes a scope within which sub-labels may be defined.
`&bar` following `@foo` would create the label `foo/bar`.
This can be used to create semantic tables.
## References
Labels may be referenced in one of seven ways:
- Literal byte zero-page - `.label`
- Raw byte relative - `_label`
- Literal byte relative - `,label`
- Raw byte absolute - `-label`
- Raw short absolute - `:label` or `=label`
- Literal short absolute - `;label`
Literal labels are inserted with a `LIT` or `LIT2` as appropriate.
Raw labels are inserted directly into bytecode.
Absolute labels are double quantities.
Relative labels are single signed byte quantities with a ±127 range.
The zero page (`#00XX`) is used for system devices, along other things.
It's common to see labels such as `.Mouse/state`, being a reference to the address `#0096`.
However as UXN has a special `LDZ` operation for loading from the zero page, this address can be specified as simply `#96` to save a byte.
As the last device is mapped to `#CX`, it is common to see `#DX`, `#EX` and `#FX` used for program-global variables for ease of access.
Literal byte relative references ala `,foo` are used for control flow.
Using only a single byte, these references have a range of ±127 instructions.
A typical opcode sequences would be `,loop JMP`, eg. emit a relative address value to the loop label and perform a computed relative jump.
For bytecode compactness, UXN programs tend to use computed rather than absolute jumps.
The difference between single and double word references is critical, because the `LDR` instruction is a computed relative load, whereas `LDA` is an absolute short address load.
## Brackets
`[` and `]` are treated as whitespace, and may be used for visual grouping.
While they have semantics in traditional Forth, they have no semantics in TAL.
## Literals
Hex constants are written `#[0-9a-f]{1,4}`.
For instance `#00` or `#ffff` would be valid hex constants, the first assembling to one word, the second to two.
One and two byte literal quantities may also be provided without the `#` prefix.
Words may be captured as ASCII formatted strings.
Such strings are written `"<word>`.
For instance `"foo` would cause the bytes `#66 #6f #6f #00` to be literally inserted into the memory image.
As `"` notation cannot capture whitespace, the `#20` (space), `#0a` (newline) and `#09` (tab) character constants are common.

101
doc/uxn.md Normal file
View file

@ -0,0 +1,101 @@
# UXN
Reformatted from [the reference document](https://wiki.xxiivv.com/site/uxntal_reference.html)
UXN is a deterministic, non-interrupting, 8-bit instruction set and word stack machine with a 16bi address space.
It interacts with the outside world via port mapped I/O devices, of which 16 are supported.
## Memory model
UXN presents a linear uniform main memory with a 16bi address space.
Programs may do I/O across the entire address space using the `LDR2` and `STR2` instructions.
The UXN address space is divided into chunks called pages, each with 8bi of addressing.
The 0 page (`0x00[00-FF]`) is special and, for convenience, can be accessed using the `LDZ` and `STRZ` instructions.
The UXN stack machine, in keeping with [the forth tradition](http://www.forth.org/svfig/Len/softstak.htm) has two stacks.
These are referred to as `DATA` (stack 0) and `RETURN` (stack 1) by convention.
Instructions may choose to transpose the stacks, allowing for computation on either stack.
See below for more on the `return` flag.
The `LDR` and `STR` instructions are stack relative and allow for computed loads and stores.
## Instruction set
UXN instructions are 8-bit values.
| k | r | 2 | opcode |
|------|--------|-------|-----------|
| keep | return | short | 0 0 0 0 0 |
The three most significant bits of the instructions are mode flags of which we will say more.
The least significant five bits select an instruction.
Effects are written using forth-style notation `inputs -- outputs`
| Opcode | Memonic | Long name | Data stack effect | Control stack effect | PC effect | Memory effect |
|--------|---------|--------------------------|---------------------------------------------|----------------------|---------------------|-------------------|
| 0x00 | BRK | Break | -- | | | |
| 0x01 | INC | Increment | a -- a+1 | | | |
| 0x02 | POP | Pop | a -- | | | |
| 0x03 | NIP | Nip | b a -- a | | | |
| 0x04 | SWP | Swap | b a -- a b | | | |
| 0x05 | ROT | Rotate | a b c -- b c a | | | |
| 0x06 | DUP | Duplicate | a -- a a | | | |
| 0x07 | OVR | Over | a b -- a b a | | | |
| 0x08 | EQU | Equal | b a -- (a == b) | | | |
| 0x09 | NEQ | Not equal | b a -- (a != b) | | | |
| 0x0A | GTH | Greater than | b a -- (b > a) | | | |
| 0x0B | LTH | Less than | b a -- (b < a) | | | |
| 0x0C | JMP | Jump | a -- | | PC+=a | |
| 0x0D | JCN | Conditional jump | b a -- | | PC+=(a if b else 1) | |
| 0x0E | JSR | Jump stash return (call) | a -- | -- pc | PC+=a | |
| 0x0F | STH | Stash | a -- | -- a | | |
| 0x10 | LDZ | Load zero page | a: u8 -- MEM[a] | | | |
| 0x11 | STZ | Store zero page | b a: u8 -- | | | MEM[a] = b |
| 0x12 | LDR | Load | a: i8 -- MEM[DATA + a] | | | |
| 0x13 | STR | Store | b a: i8 -- | | | MEM[DATA + a] = b |
| 0x14 | LDA | Load absolute | a: i16 -- MEM[a] | | | |
| 0x15 | STA | Store absolute | b a: i16 -- | | | MEM[a] = b |
| 0x16 | DEI | Device input | a: u8 -- DEV[a] | | | |
| 0x17 | DEO | Device output | b a: u8 -- | | | DEV[a] = b |
| 0x18 | ADD | Add | b a -- a + b | | | |
| 0x19 | SUB | Subtract | b a -- a - b | | | |
| 0x1A | MUL | Multiply | b a -- a * b | | | |
| 0x1B | DIV | Divide | b a -- a / b | | | |
| 0x1C | AND | And | b a -- a && b | | | |
| 0x1D | ORA | Or | b a -- a\|b | | | |
| 0x1E | EOR | Exclusive or (xor) | b a -- a^b | | | |
| 0x1F | SFT | Shift | b a -- b >> (a & 0x0f) << ((a & 0xf0) >> 4) | | | |
### Mode bits
Note that all these instructions are listed with `keep=0,return=0,short=0`.
The UXN documentation refers to `ADD` with `keep=1` as `ADDk`.
`ADD` with `short=1` is `ADD2`, and with `keep=1` would be `ADD2k`.
`POP` with `return=1` is `POPr`.
Conventionally these suffixes are formatted `%/2?k?r?/` as required.
#### Short mode
In When the `short` bit is set, the opcode will pop two 8bi quantities from the stack where it would only consume one in `byte` mode.
Each `b a` pair so popped is considered to be the unsigned 16bi quantity `a + b<<8`
For instance `ADDs` would be `a b c d - (a<<8 + b) + (c<<8+d)`.
**Exceptions:**
- The load/store operations move two 8bi values not one. `STR` becomes `c b a -- MEM[DATA + a] = ` Relative address computation is unaffected.
- The jump opcodes become absolute rather than computed relative jumps.
#### Keep mode
When the `keep` bit is set, no values are popped from the stack.
For instance `ADD2k` would be `a b c d -- a b c d e f`.
#### Return mode
"return mode" flips the stacks.
If `STH` pops from data and pushes to return, `STHr` pops from return and pushes to data.
Likewise if `ADD` pops from data and pushes to data, `ADDr` would pop from data and push to data.
`JMP2` would load `b a` from the data stack and branch absolutely, `JMP2r` would do the same from the control stack.
`DUP2r` would `a b -- a b a b` the return stack.
The bytecode sequence `LIT 0x01 STH JSRr BRK` would load `0x1` to the data stack, pop and stash it to the return stack, perform a relative jump by `+1` while pushing `PC` to the data stack and halt.

58
doc/varvara.md Normal file
View file

@ -0,0 +1,58 @@
# Varvara
[Varvara](https://wiki.xxiivv.com/site/varvara.html) is a personal computer, using the [uxn](./uxn.md) instruction set, programmed in [tal](./tal.md)
UXN presents 16 I/O ports via the `DEI` and `DEO` instructions.
Each port consists of 16 words of memory, and has its own I/O memory mapping behavior.
Note that while other memory read and write instructions can interface with port memory, only `DEI` and `DEO` will trigger port effects.
The Varvara computer presents the following canonical port mappings -
- `0x0`, System device
- `0x1`, Console device (text output)
- `0x2`, Screen device (bitmap output)
- `0x3`, Audio device (`audio0`)
- `0x4`, Audio device (`audio1`)
- `0x5`, Audio device (`audio2`)
- `0x6`, Audio device (`audio3`)
- `0x7`, Unused
- `0x8`, D-pad controller
- `0x9`, Mouse
- `0xA`, File (`file0`)
- `0xB`, File (`file1`)
- `0xC`, Datetime
- `0xD`, Unused
- `0xE`, Unused
- `0xF`, Unused
```
|00 @System &vector $2 &wst $1 &rst $1 &eaddr $2 &ecode $1 &pad $1 &r $2 &g $2 &b $2 &debug $1 &halt $1
|10 @Console &vector $2 &read $1 &pad $5 &write $1 &error $1
|20 @Screen &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1
|30 @Audio0 &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|40 @Audio1 &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|50 @Audio2 &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|60 @Audio3 &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|80 @Controller &vector $2 &button $1 &key $1 &func $1
|90 @Mouse &vector $2 &x $2 &y $2 &state $1 &pad $3 &scrollx $2 &scrolly $2
|a0 @File0 &vector $2 &success $2 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2
|b0 @File1 &vector $2 &success $2 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2
|c0 @DateTime &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1
```
Varvara executes a program starting at `0x0100` until the `BRK` instruction occurs.
`BRK` does not halt the machine (that would be `%halt { #01 LIT .System/halt DIO }`).
Instead it signals that the present 'thread' of execution has completed, and returns control so that an interrupt handler can occur.
The main or initialization program must register interrupt handlers before `BRK`.
Varvara provides 'interrupt' based I/O.
The 'vector' of a given device is a program point.
That point will be invoked with no stack arguments when an event on the device occurs.
Interrupt handlers execute until they terminate using the `BRK` instruction.
Varvara enqueues interrupts and will not fire another until the one under execution has completed.
The screen device's vector is a clock which ticks at 60hz.
The mouse device's vector likewise fires when a mouse event such as movement or a button press occurs.
The controller device similarly fires on input changes.
[themes](https://wiki.xxiivv.com/site/theme.html)

3
src/bin/main.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

1
src/uxn.rs Normal file
View file

@ -0,0 +1 @@