#virtual-machine #compiler #toolchain #transient #tvm #transient-assembly #tasm

app transient-asm

Toolchain for TransientAssembly. Includes a compiler and virtual machine.

2 releases

0.1.1 Aug 9, 2024
0.1.0 Aug 8, 2024

#78 in Programming languages

Download history 215/week @ 2024-08-07 13/week @ 2024-08-14

228 downloads per month

MIT license

40KB
697 lines

Transient

🔥 ~ Getting started ~

[!IMPORTANT]
This is a purely education project and consequently offers no guarantees. My projects are free to use for anyone so long as the relevant licenses are followed and credit is given, where appropriate. Enjoy!

This is a collection of utilities for compiling and running TransientAssembly (TASM) programs. The Transient project includes a compiler, which compilies TransientAssembly (TASM) down to TransientBytecode (TBC), which is then executed by the TransientVM (TVM). This document includes an installation guide, a guide to the compiler, and a quick tutorial of how to write TransientAssembly (TASM).

⬇️ Installation

[!NOTE]
This guide assumes that you have a Rust toolchain installed. If you don't have this, install Rust using rustup or a suitable equivalent. A rust compiler is only needed when first installing the compiler and virtual machine. Their runtime do not rely on the rust toolchain.

The compiler (transientcompile) and virtual machine (transientvm) can both be installed as a bundle using Cargo. You first need to clone the GitHub repo:

$ git clone https://github.com/redtechtiger/transient

Then, navigate into the newly created directory and run Cargo to install the binaries:

$ cd transient
$ cargo install --path .

🗑️ Removal

If you wish to remove these utilities from your system you can do so using Cargo. Run the following command:

$ cargo uninstall --package transient

📚 Sample usage

To compile and run a TransientAssembly (TASM) file, simply invoke the compiler with the input and output file. This produces a .tbc file (Transient bytecode) which you can then run using the virtual machine. In order to test that the toolchain is working, you can try to compile of the example projects. For instance, here's how to compile the fibonacci sequence:

$ transientcompile examples/fibonacci.tasm fibonacci.tbc
$ transientvm fibonacci.tbc

💻 ~ TransientAssembly ~

This is a basic tutorial to get you started with writing TransientAssembly. As this entire project is in its early days, expect major changes to both syntax, features, and even the fundamental workings on the language. This also means that the language is very basic as of now, and may only make sense for those who are familiar with assembly or very low level code.

Structure

Here's a typical instruction to have as a reference while you read the text below.

add64 $variable1 $another_var $result

Every line in TransientAssembly will execute one operation, with the exception of comments "//", tags "#", and empty lines. We'll get back to tags in a second, but for now, let's look at the general structure of the language. At the beginning of a line, you have the operation - pretty self explanatory once you read some of the examples. It's accompanied by a suffix (64, 32, 16, 8) that dictates how many bits the variables in the expression are. Again, more on this later. You then have the arguments, which can be either a variable (prefixed by a $), or an intermediate (prefixed by a !{size}_, e.g. !64_). Have a look at the following examples:

// This is a comment.
// Blablabla

// This is a tag!
#tag_1

// And this is an operation. We are creating a 64 bit variable called `my_variable` with the value 5.
set64 $my_variable 5

// Create another 64 bit variable with value 10
set64 $another_var 10

// We need to initialize this beforehand!
set64 $result 0

// Add `my_variable` and `another_var`, and store the sum in `result`.
add64 $my_variable $another_var $result

// The variable `result` will now be 15

Operations

Here is a list of available operations.

mov - Copies the first variable into the second
add - Adds two variables and stores the result in a third
sub - Subtracts the second variable from the first and stores the result in a third
mov - Same except multiplied
divt - Same except divided (truncated)
divr - Same except divided (rounded)
rem - Same except moduli
cgt - Compares if the first variable is greater than the second variable. If true, the third variable will be set to 1. If false, it will be set to 0
cls - Same except less than
equ - Same except equals
jmp - Stops execution, jumps to a tag, and resumes
jie - If variable two is 1, jumps to a tag. If 0 or other value, keep executing as normal
jne - Same except only jumps if variable is 0
puti - Prints the integer stored at the first variable to the console
putc - Prints the integer at first variable formatted to an ascii character
imz - Get the size of the program in bytes and stores it in the first variable
hlt - Stop program execution and exit the virtual machine

Sizes

The size of an operation just specifies how big the variables are that are used. For example, if you are adding two 8-bit integers, you have to use add8. The output of add8 will also always be an 8-bit integer. Likewise, if you're adding two 64-bit integers and saving into a 64-bit integer, you need to use add64. Every operation must include a size hint, however not all operations actually use it. For instance, the halt instruction (hlt) is always going to be 64 bits (8 bytes) but the compiler still requires you to specify a size for technical reasons.

Most importantly, the size needs to be the same for all the variables that concern it. (For all operations, you may keep this at 64 if you're unsure of what to choose). For instance, you cannot create a 64 bit variable and attempt to use it in 32, 16, or 8 bit operations unless you really know what you're doing, or this will cause corruption of memory and/or runtime code, which will lead to nasty bugs. Currently the compiler does not validate this, so it is up to you to verify this.

Tags

Tags are simply points in your code that you can jump to. Under the hood, the compiler expands these to the memory address of the closest instruction after the tag. Here's an example of an infinite loop:

#foo
jmp #foo

Here's another loop that counts to 10.

set64 $value 0
set64 $comparison_result 0

#loop
puti64 $value
add64 $value !64_1 $value
cgt64 $value !64_10 $comparison_result
jne64 #loop $comparison_result

hlt64

No runtime deps