Skip to content

Instantly share code, notes, and snippets.

@BasedUser
Last active January 15, 2023 21:28
Show Gist options
  • Save BasedUser/321b38eb63eee08a376f3b3a3c3e1528 to your computer and use it in GitHub Desktop.
Save BasedUser/321b38eb63eee08a376f3b3a3c3e1528 to your computer and use it in GitHub Desktop.
The Mindustry Instruction Set Architecture (revision 2)
The Mindustry Logic ISA (mISA, "miss-uh") Manual
Revision 2 (2021/II/12)
This document describes an ISA capable of being translated into mlog interpretable code. This will also be a good target architecture for any compilers, and a decent ISA for message block (blocks containing up to 220 characters) outputs.
As I (router) am unable to describe the hardware's exact requirements to run such code due to lack of experience with hardware engineering, I will define the minimum criteria for interpreting mISA here.
----------------
Every variable is 10-bit, or in the case of string literals, 16-bit long for just the header followed by up to 34 (maxTokenLength-2) UTF-8 characters. Every U+000A "\n" (newline) must be interpreted as 2 characters "\" and "n", regardless of line ending and even if "\n" represents one UTF-8 character (which it does). User-addressable registers have a prefix of 0, while enums and constants have a reserved prefix of 1. This means that the variable 1000110111 can be equally interpreted as the user register 567, or the enum @pulverizer - the latter behavior is advised due to the nature of enums.
There must be 512 unique, user-addressable registers (aka logic variables) in any processor executing this ISA. The reason for this is the maximum amount of instructions you can store in-game is 1000. To use all 512 registers, say, as individually addressable RAM, you would *really* be storing 511, and with that using up 511*2 + 4 = 1026 instructions, which is just over the limit of 1000 and thus perfectly legal no matter what code is stuffed into an instruction. This isn't to say that you *can't* use more than 512 registers at once: just that this is the minimal amount for ALL code to be valid, and code using more registers can be optimized to ≤512.
There are also 512 potential enums and constants defined. Any undefined enums must preserve their value via getting their ID from Vars.constants.vars.get("@enumname") (at build 123, also that class is private - make it public manually and read it). but a select few will be reassigned. The third element in this list is what I mapped these enums to (with their prefix removed).
000000000 - the end - false
000000001 - false - true
000000010 - true - null
000000011 - null - @this
111111100 - undefined - (constant IEEE754 number header)
111111101 - undefined - (string literal header)
111111110 - undefined - (constant 64-bit integer header)
111111111 - undefined - (constant block link header)
However, redefining these can only do so much, due to how fluid Mindustry's code is. The following enums were appended to the end of the list generated by Vars.constants.vars, in the following order to successfully map mlog to mISA. All senseables, other enums must be appended to the list below after @unit.
@mapw, @maph, @links, @ipt, @thisx, @thisy, @counter, @time, @unit.
I also defined 3 header variables to convey constant values. These would be regular mlog decimals (IEEE754 double-precision), strings (followed by 6 bits encoding length), and integers such as 0xFEDCBA9876543210.
This is what a string literal's structure is. Example: "Hi!\n"
string length | "H" | "i" | "!" | "\n"
11111111 | 10 000101 | 01001000 | 01101001 | 00100001 | 00001010
This is what a block link's structure is. Example: "pulverizer4"
blink | benum | uint12+1
11111111 | 11 100011 | 0111 0000 | 00000011
Enum mappings to names are written in the end of this file (with their prefix removed).
If there is a 1 after a senseable enum, it is writable.
----------------
mISA has 16 instructions with configurable parameters.
While describing this architecture, I made the executive decision to reduce three instructions: "end", "uradar", "noop".
Any "end" is equivalent "set @counter 0", "uradar" can be mapped back and forth to "radar" with @unit instead of the turret (while this usage won't work in Mindustry, it fits more info into radar), and "noop" is equivalent to "op" with an invalid argument.
All opcodes that don't end in a byte-divisible value must be padded with data.
For the sake of forwards compatibility with additional instructions AND comments, any metadata starting with 1 will imply a 4bit counter immediately leading it describing how many variables are expected after it. Usually, these variables will be full string literals, the first one starting with "#". If anything else is read, parsing it is considered undefined behavior and should be skipped if the compiler is not completely sure what these strings mean. This may be amended in future mISA revisions.
INSTRUCTIONS
0000 - op
0001 - set
0010 - read
0011 - write
0100 - draw
0101 - print
0110 - drawflush
0111 - printflush
1000 - getlink
1001 - control
1010 - radar
1011 - sensor
1100 - jump
1101 - ubind
1110 - ucontrol
1111 - ulocate
--------------------------------
Syntax
All instruction/opcode descriptions will be written as follows:
XXXX - instrName
These next 2 lines will be examples of full-length instructions, assuming all arguments besides instruction-local enums are user register 0. Different variables are separated by spaces, unless there is a " | ", in which case it's a byte separation.
inst en1 | en2 | arg1
XXXX 0000 | 000000 00 | 00000000
A short description of the instrName instruction, sources of local enums.
Local enum values (third/fourth argument in mapping description):
EVALUE - ENAME - THIRD - FOURTH
----------------
0000 - op
inst opENUM | arg0 | arg1 | arg2
0000 0000 | 00 000000 |0000 0000 | 000000 00| 00000000
Executes an operation over arg1 and arg2, and stores it in arg0 (by default). Arguments can be omitted depending on local enum.
All op enums were taken from LogicOp.all and inserted at position 1, the rest are inserted/appended.
However, the `wait` enum refers to a new instruction (as of 125), "wait x". Its argument is the amount of seconds to pause for, in *world time*.
Local enum values (third argument - amount of STORED variables following it):
000000 - noop0 - 0
000001 - add - 3
000010 - subtract - 3
000011 - mul - 3
000100 - div - 3
000101 - idiv - 3
000110 - mod - 3
000111 - pow - 3
001000 - equal - 3
001001 - notEqual - 3
001010 - land - 3
001011 - lessThan - 3
001100 - lessThanEq - 3
001101 - greaterThanEq - 3
001110 - shl - 3
001111 - shr - 3
010000 - or - 3
010001 - and - 3
010010 - xor - 3
010011 - not - 2
010100 - max - 3
010101 - min - 3
010110 - atan2 - 3
010111 - dst - 3
011000 - noise - 3
011001 - abs - 2
011010 - log - 2
011011 - log10 - 2
011100 - sin - 2
011101 - cos - 2
011110 - tan - 2
011111 - floor - 2
100001 - ceil - 2
100010 - sqrt - 2
100011 - rand - 2
100100 - isNull - 1
100101 - wait - 1
111101 - noop3 - 3
111110 - noop2 - 2
111111 - noop1 - 1
others - noop0 - 0
----------------
0001 - set
inst arg0 | arg1
0001 0000 | 000000 00 | 00000000
Sets arg0's value to arg1.
No local enums.
----------------
0010 - read
inst arg0 | arg1 | arg2 |
0010 0000 | 000000 00 | 00000000 | 00000000 | 00 000000
Sets arg0's value to whatever value was at address arg2 in the memory cell arg1.
No local enums.
----------------
0011 - write
inst arg0 | arg1 | arg2 |
0010 0000 | 000000 00 | 00000000 | 00000000 | 00 000000
Writes arg0's value to address arg2 in the memory cell arg1.
No local enums.
----------------
0100 - draw
inst op | B| arg0 | arg1 | arg2 | arg3 | arg4 | arg5 |
0100 0000 | 0 0000000 | 000 00000 | 00000 000 | 0000000 0 | 00000000 | 0 0000000 | 000 00000 | 00000 000
Appends a drawing instruction to the draw buffer of this processor. Arguments can be omitted depending on enum. HOWEVER, if the "op" is an invalid draw operation (such as noop), the B flag is NOT stored, and the draw instruction terminates. Arguments may be omitted depending on local enum.
If the B flag is set, all enum variables (1XXXXXXXXX) will be treated as integers 0-511.
If the draw buffer is full, this instruction will be skipped.
Enums gathered from in-game.
Local enum values (third argument - amount of STORED variables following it):
0000 - noop - 0
0100 - clear - 3
0001 - color - 4
0010 - stroke - 1
0011 - line - 4
0101 - rect - 4
0110 - lineRect - 4
0111 - poly - 5
1000 - linePoly - 5
1001 - triangle - 6
1010 - image - 5
----------------
0101 - print
inst arg |
0101 0000 | 000000 00
Appends a value to the text buffer of this processor. Numeric values are converted to human-readable strings.
No local enums.
----------------
0110 - drawflush
inst arg |
0110 0000 | 000000 00
Renders an RGBA image from the draw buffer and submits an overlay to the block specified in the argument. The buffer is then cleared.
No local enums.
----------------
0111 - printflush
inst arg |
0111 0000 | 000000 00
Prints the text buffer to the block specified in the argument. The buffer is then cleared.
No local enums.
----------------
1000 - getlink
inst arg0 | arg1
1000 0000 | 000000 00 | 00000000
Gets the arg1th link of the processor, and stores its building in arg0.
No local enums.
----------------
1001 - control
inst m | arg1 | arg2 | arg3 | arg4 |
1001 00 00 | 00000000 | 00000000 | 00 000000 | 0000 0000 | 000000 00
Controls the block arg1 with parameters arg2..4. Arguments may be omitted depending on mode.
Local enum values (third argument - amount of STORED variables following it):
00 - enabled - 2
01 - shoot - 4
10 - shootp - 3
11 - configure - 2
----------------
1010 - radar
inst f1 | f2 | f3| s | arg0 | arg1 | arg2 |
1010 000 0 | 00 000 000 | 00000000 | 00 000000 | 0000 0000 | 000000 00
The most complex instruction.
Finds an unit according to filters f1, f2, f3, ordered either first or last with arg1, sorted by s, searching from arg0's perspective; and stores the unit in arg2.
Local enum values:
f1..3
000 - any
001 - enemy
010 - ally
011 - player
100 - attacker
101 - flying
110 - boss
111 - ground
s
000 - distance
001 - health
010 - shield
011 - armor
100 - maxHealth
----------------
1011 - sensor
inst arg0 | arg1 | arg2 |
1011 0000 | 000000 00 | 00000000 | 00000000 | 00 000000
Returns the value of arg1 and arg2, and stores it in arg0.
No local enums.
----------------
1100 - jump
inst ptr | op | arg0 | arg1 |
1100 0000 | 000000 00 | 0 0000000 | 000 00000 | 00000 000
Jumps to ptr if the expression "arg0 op arg1" evaluates to true.
ptr has the type uint10 and is a constant inside 0~1023.
Local enum values:
000 - equal
001 - notEqual
010 - lessThan
011 - lessThanEq
100 - greaterThan
101 - greaterThanEq
110 - always
111 - never
----------------
1101 - ubind
inst arg0 |
1101 0000 | 000000 00
Finds an unit of the type arg0, and stores it in @unit. If no unit of that type is found, stores null in @unit.
No local enums.
----------------
1110 - ucontrol
inst actn | arg0 | arg1 | arg2 | arg3 | arg4 |
1110 0000 | 00000000 | 00 000000 | 0000 0000 | 000000 00 | 00000000 | 00000000 | 00 000000
Makes @unit perform a certain action with parameters arg0..4. Arguments may be omitted depending on action.
Local enum values (third argument - amount of STORED variables following it):
0000 - stop - 0
0001 - move - 2
0010 - approach - 3
0011 - boost - 1
0100 - pathfind - 0
0101 - target - 3
0110 - targetp - 2
0111 - itemDrop - 2
1000 - itemTake - 3
1001 - payDrop - 0
1010 - payTake - 1
1011 - mine - 2
1100 - flag - 1
1101 - build - 5
1110 - getBlock - 4
1111 - within - 4
----------------
1111 - ulocate
ulocate ore core true @copper outx outy found building
inst what | arg0 | arg1 | arg2 | arg3 | arg4 |
1111 0000 | 00000000 | 00 000000 | 0000 0000 | 000000 00 | 00000000 | 00000000 | 00 000000
Makes @unit find something.
building - Checks whether there's a building that is arg0 an enemy, stores coordinates in arg1 and arg2; stores it in arg4. If there is no such building, sets arg4 to null (?) and sets arg3 to false. Otherwise, sets arg3 to true.
ore - Checks whether an ore of the type arg0 exists, writes its data to arg1..3.
damaged/spawn - Checks whether a damaged block/drop zone exists, writes its data to arg1..3.
Local enum values (third argument - amount of STORED variables following it):
0000 - building core
0001 - building storage
0010 - building generator
0011 - building turret
0100 - building factory
0101 - building repair
0110 - building rally
0111 - building battery
1000 - building resupply
1001 - building reactor
1010 - building unitModifier
1011 - building extinguisher
1100 - ore core
1101 - spawn core
1110 - damaged core
--------------------------------
ENUMS
000000000 - false
000000001 - true
000000010 - null
000000011 - @this
000000100 - @copper
000000101 - @lead
000000110 - @metaglass
000000111 - @graphite
000001000 - @sand
000001001 - @coal
000001010 - @titanium
000001011 - @thorium
000001100 - @scrap
000001101 - @silicon
000001110 - @plastanium
000001111 - @phase-fabric
000010000 - @surge-alloy
000010001 - @spore-pod
000010010 - @blast-compound
000010011 - @pyratite
000010100 - @water
000010101 - @slag
000010110 - @oil
000010111 - @cryofluid
000011000 - @build1
000011001 - @build2
000011010 - @build3
000011011 - @build4
000011100 - @build5
000011101 - @build6
000011110 - @build7
000011111 - @build8
000100000 - @build9
000100001 - @build10
000100010 - @build11
000100011 - @build12
000100100 - @build13
000100101 - @build14
000100110 - @build15
000100111 - @build16
000101000 - @graphite-press
000101001 - @multi-press
000101010 - @silicon-smelter
000101011 - @silicon-crucible
000101100 - @kiln
000101101 - @plastanium-compressor
000101110 - @phase-weaver
000101111 - @alloy-smelter
000110000 - @cryofluid-mixer
000110001 - @pyratite-mixer
000110010 - @blast-mixer
000110011 - @melter
000110100 - @separator
000110101 - @disassembler
000110110 - @spore-press
000110111 - @pulverizer
000111000 - @coal-centrifuge
000111001 - @incinerator
000111010 - @copper-wall
000111011 - @copper-wall-large
000111100 - @titanium-wall
000111101 - @titanium-wall-large
000111110 - @plastanium-wall
000111111 - @plastanium-wall-large
001000000 - @thorium-wall
001000001 - @thorium-wall-large
001000010 - @phase-wall
001000011 - @phase-wall-large
001000100 - @surge-wall
001000101 - @surge-wall-large
001000110 - @door
001000111 - @door-large
001001000 - @scrap-wall
001001001 - @scrap-wall-large
001001010 - @scrap-wall-huge
001001011 - @scrap-wall-gigantic
001001100 - @thruster
001001101 - @mender
001001110 - @mend-projector
001001111 - @overdrive-projector
001010000 - @overdrive-dome
001010001 - @force-projector
001010010 - @shock-mine
001010011 - @conveyor
001010100 - @titanium-conveyor
001010101 - @plastanium-conveyor
001010110 - @armored-conveyor
001010111 - @junction
001011000 - @bridge-conveyor
001011001 - @phase-conveyor
001011010 - @sorter
001011011 - @inverted-sorter
001011100 - @router
001011101 - @distributor
001011110 - @overflow-gate
001011111 - @underflow-gate
001100000 - @mass-driver
001100001 - @payload-conveyor
001100010 - @payload-router
001100011 - @mechanical-pump
001100100 - @rotary-pump
001100101 - @thermal-pump
001100110 - @conduit
001100111 - @pulse-conduit
001101000 - @plated-conduit
001101001 - @liquid-router
001101010 - @liquid-tank
001101011 - @liquid-junction
001101100 - @bridge-conduit
001101101 - @phase-conduit
001101110 - @power-node
001101111 - @power-node-large
001110000 - @surge-tower
001110001 - @diode
001110010 - @battery
001110011 - @battery-large
001110100 - @combustion-generator
001110101 - @thermal-generator
001110110 - @steam-generator
001110111 - @differential-generator
001111000 - @rtg-generator
001111001 - @solar-panel
001111010 - @solar-panel-large
001111011 - @thorium-reactor
001111100 - @impact-reactor
001111101 - @mechanical-drill
001111110 - @pneumatic-drill
001111111 - @laser-drill
010000000 - @blast-drill
010000001 - @water-extractor
010000010 - @cultivator
010000011 - @oil-extractor
010000100 - @core-shard
010000101 - @core-foundation
010000110 - @core-nucleus
010000111 - @vault
010001000 - @container
010001001 - @unloader
010001010 - @duo
010001011 - @scatter
010001100 - @scorch
010001101 - @hail
010001110 - @wave
010001111 - @lancer
010010000 - @arc
010010001 - @parallax
010010010 - @swarmer
010010011 - @salvo
010010100 - @segment
010010101 - @tsunami
010010110 - @fuse
010010111 - @ripple
010011000 - @cyclone
010011001 - @foreshadow
010011010 - @spectre
010011011 - @meltdown
010011100 - @command-center
010011101 - @ground-factory
010011110 - @air-factory
010011111 - @naval-factory
010100000 - @additive-reconstructor
010100001 - @multiplicative-reconstructor
010100010 - @exponential-reconstructor
010100011 - @tetrative-reconstructor
010100100 - @repair-point
010100101 - @resupply-point
010100110 - @power-source
010100111 - @power-void
010101000 - @item-source
010101001 - @item-void
010101010 - @liquid-source
010101011 - @liquid-void
010101100 - @illuminator
010101101 - @legacy-mech-pad
010101110 - @legacy-unit-factory
010101111 - @legacy-unit-factory-air
010110000 - @legacy-unit-factory-ground
010110001 - @launch-pad
010110010 - @launch-pad-large
010110011 - @interplanetary-accelerator
010110100 - @message
010110101 - @switch
010110110 - @micro-processor
010110111 - @logic-processor
010111000 - @hyper-processor
010111001 - @memory-cell
010111010 - @memory-bank
010111011 - @logic-display
010111100 - @large-logic-display
010111101 - @block-forge
010111110 - @block-loader
010111111 - @block-unloader
011000000 - @solid
011000001 - @air
011000010 - @dagger
011000011 - @mace
011000100 - @fortress
011000101 - @scepter
011000110 - @reign
011000111 - @nova
011001000 - @pulsar
011001001 - @quasar
011001010 - @vela
011001011 - @corvus
011001100 - @crawler
011001101 - @atrax
011001110 - @spiroct
011001111 - @arkyid
011010000 - @toxopid
011010001 - @flare
011010010 - @horizon
011010011 - @zenith
011010100 - @antumbra
011010101 - @eclipse
011010110 - @mono
011010111 - @poly
011011000 - @mega
011011001 - @quad
011011010 - @oct
011011011 - @risso
011011100 - @minke
011011101 - @bryde
011011110 - @sei
011011111 - @omura
011100000 - @alpha
011100001 - @beta
011100010 - @gamma
011100011 - @block
011100100 - @totalItems
011100101 - @firstItem
011100110 - @totalLiquids
011100111 - @totalPower
011101000 - @itemCapacity
011101001 - @liquidCapacity
011101010 - @powerCapacity
011101011 - @powerNetStored
011101100 - @powerNetCapacity
011101101 - @powerNetIn
011101110 - @powerNetOut
011101111 - @ammo
011110000 - @ammoCapacity
011110001 - @health
011110010 - @maxHealth
011110011 - @heat
011110100 - @efficiency
011110101 - @rotation
011110110 - @x
011110111 - @y
011111000 - @shootX
011111001 - @shootY
011111010 - @shooting
011111011 - @mineX
011111100 - @mineY
011111101 - @mining
011111110 - @team
011111111 - @type
100000000 - @flag
100000001 - @controlled
100000010 - @commanded
100000011 - @name
100000100 - @config
100000101 - @payloadCount
100000110 - @payloadType
100000111 - @enabled
100001000 - @shoot
100001001 - @shootp
100001010 - @configure
100001011 - @mapw
100001100 - @maph
100001101 - @links
100001110 - @ipt
100001111 - @thisx
100010000 - @thisy
100010001 - @counter - 1
100010010 - @time
100010011 - @unit - 1
100010100 - @range
100010101 - @timescale
111111100 - (constant IEEE754 number header)
111111101 - (string literal header)
111111110 - (constant 64-bit integer header)
111111111 - (constant block link header)
Documented by BasedUser AKA router
This file is in the public domain. You may modify it without permission.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment