Difference between revisions of "IC10"
From Unofficial Stationeers Wiki
(→Device Variables) |
(→IcX preprocessor) |
||
Line 896: | Line 896: | ||
|} | |} | ||
− | |||
− | |||
− | |||
− |
Revision as of 11:57, 26 July 2023
Contents
MIPS scripting language for IC10 housings / chips
MIPS is Stationeers' in-game scripting language. It runs on IC10 chips crafted at the Electronics Printer.
Comments
Comments can be placed using a # symbol. All comments are ignored by the game when it reads commands. Below is an example of valid code with two comments.
alias MyAlias r0 # Text after the hash tag will be ignored to the end of the line.
# You can also write comments on their own lines, like this.
Registers
The IC contains 16 registers, numbered r0-r15. A register is a single unit of memory that can hold different values. Think of these as variables in other programming languages. In fact, you can rename them with the alias command (see below).
Registers can be manipulated in various ways.
- Their values can be directly set using the move command.
Example:move r0 2
sets r0 to hold the number 2. - Values can be read from connected devices and put into the register using the l (load) command. For example, if you want to load the state of a door.
Example:l r0 Door Open
reads the 'Open' field of an object named 'Door', that would be connected to the IC housing of the chip. - To write a value back to a device, use the s (set) command. Note that MIPS is written like most machine languages, where the destination of an action comes before the source.
Example:s Door Open 0
closes a door, by setting the 'Open' field of the 'Door' to 0. It can be helpful to think of the order of the operation on the formaction destination source
Here are some examples demonstrating all three operations:
move r0 10
Sets register r0 to the value 10
move r0 r1
Copies the value of register r1 to register r0
l r0 d0 Temperature
Reads the Temperature parameter from device d0 and places the value in register r0.
Note: not all devices have a Temperature parameter, check the in-game stationpedia.
To set a device specific value (like "On"), you can write into this value.
s d0 On r0
Writes the value from register r0 out to On parameter of device d0. In this example the device will be turned On, if valve of register r0 equals 1, otherwise (register r0 equals 0) it will turned off. See section Device Variables.
It's recommended to use labels (like: someVariable) instead of a direct reference to the register. See alias in section Instructions.
Special registers
There are two more registers. One called ra (return address) and one called sp (stack pointer). The ra is used by certain jump and branching instructions (those ending with -al) to remember which line in the script it should return to. The sp tracks the next index within the stack (a memory that can store up to 512 values) to be pushed (written) to or popped (read) from. Neither ra or sp is protected, their values can be changed by instructions like any other register.
Device Ports
ICs can interact with up to 6 other devices via d0 - d5, as well as the device it's attached to via db. To change or set a device, use a screwdriver and adjust the device in the IC housing. You can read or set any of the device's properties, so it is possible to do things like read the pressure or oxygen content of a room on the same Device port.
Additionally, is possible to set other IC housings as devices, allowing you to create programs that run across multiple ICs together. For example, an Gas Mixing IC could check the Setting field of a Atmosphere Sensor IC and act based on the value of the sensor chip.
The l (load) or s (set) instructions you have to read or set these values to your device. Examples:
l r0 d0 Temperature
#Reads the Temperature from an atmosphere sensor at device port d0 into register r0.
s d1 Setting r0
# Writes the value of the register r0 to the device on port d1 into the variable Setting.
Labels
Labels are used to make it easier to jump between lines in the script. The label will have a numerical value that is the same as its line number. Even though it's possible to use a labels value for calculations, doing so is a bad idea since any changes to the code can change the line numbers of the labels.
main:
# define a jump mark with label main
j main
# jumps back to main
Constants
Instead of using a register to store a fixed value, a constant can be made. Using this name will refer to the assigned value. With the help of Constants you can save register places.
define pi 3.14159
# defines a Constant with name pi and set it's value to 3.14159
You can use these constants like any other variables (see: alias in section Instructions). Example:
move r0 pi
# set the value of register r0 to the value of constant named pi.
Indirect referencing
This is a way of accessing a register by using another register as a pointer. Adding an additional r infront of the register turns on this behaviour. The value stored in the register being used as the pointer must be between 0 to 15, this will then point to a register from r0 to r15, higher or lower values will cause an error.
move r0 5
stores the value 5 in r0
move rr0 10
is now the same as move r5 10
since r0 has the value 5, rr0 points at the register r5
Additional r's can be added to do indirect referencing multiple times in a row.
move r1 2
move r2 3
move rrr1 4
is now the same as move r3 4
since r1 points at r2 which points at r3
This also works with devices
move r0 2
stores the value 2 in r0
s dr0 On 1
is now the same as s d2 On 1
, r0 has the value 2 so dr0 points at d2
Debugging advices
The value stored in a register or variable can easily be displayed by writing it to the Setting parameter of the IC housing. This has no side effects. To see the value, just stand close to the IC housing and look directly at the housing.
s db Setting r0
. # sets/writes the value of register r0 into the parameter Setting of the IC Housing(db)
To check if a certain block of code is executed, use the above trick but with a random number that you choose, like the line number.
This example will display the number 137 on the IC housing.
s db Setting 137
# sets/writes the number 137 into the parameter Setting of the IC Housing(db)
Always use unique names for labels. When a label is named after a MIPS keyword like "Temperature:" or "Setting:" the original meaning of the keyword is overwritten, so when an instruction tries to use it an error will occur.
A configuration cartridge installed in a tablet can be used to see all available values and configuration parameter for all devices you focus on.
Learning MIPS
MIPS can be difficult to get started with. So here is a list of instructions that are useful for beginners. These can be used to write many different scripts.
General:
-
alias
make the script easier to read by assigning a name to a register or device, example:alias rTemperature r15
-
label:
where "label" can be replaced with almost any word, jump and branch instructions can use these in place of line numbers, example:start:
-
yield
pause for 1-tick and then resume, if not used the script will automatically pause for 1-tick after 128 lines
Jumps:
j someLabelName
jump to line with someLabelNamejal someLabelName
stores the next line number into the register ra (return address) and then jump to someLabelNamej ra
jump to register ra (return address)
Branching (jump-if):
beq (branch if equal) bne (branch if not-equal) bgt (branch if greater than) blt (branch if less than) The suffix -al can be added to each of these (example: beqal) to save the next line number into the "return address" register
Device interactions:
l (load) lb (load batch, requires one of the following: 0(Average) / 1(Sum) / 2(Minimum) / 3(Maximum)) ls (load slot) s (store) sb (store batch)
Logic and Math:
seqz (common NOT-gate: turns 0 into 1, and all other values into 0) move add (addition) sub (subtraction) mul (multiplication) div (division)
Common device variables:
On (1 is on, 0 is off) Open (1 is open, 0 is closed) Setting (meaning varies between devices, example: a LED display(console) will show this value) Activate (1 usually means running, example: a Daylight sensor is 1 when the sun shines on it) Temperature (in Kelvin, Celsius - 273.15) Pressure (in kPa)
Notes:
-All instructions and variables can be seen in-game in the MIPS editor window by clicking the "f", "x" and "s(x)" buttons on the top right.
-The stationpedia is the best source to see which variables are available to each device.
-Most scripts are loops, they end with a jump instruction that leads back up to the start. Otherwise they will just run once and then stop.
Two practice scripts:
Automatic Night Light: Load "Activate" from a Daylight sensor, flip the value with a NOT-gate, store the value to the "On" variable of one or more lights.
Automatic Wall Cooler: Read "Temperature" from a Gas Sensor. Branch if the value is greater than X, turn on the cooler. Branch if the value is less than Y, turn off the cooler. (Wall coolers need a minumum of 12.5 kPa pressure in the connected pipe)
Instructions
- alias
- alias str r? d? # labels register or device reference with name. When alias is applied to a device, it will affect what shows on the screws in the IC base. (housing)
alias vTemperature r0
alias dAutoHydro1 d0
- move
- d s # stores the value of s in d
move r0 42 # Store 42 in register 0
- l (load)
- l r# d# parameter
Reads from a device (d#) and stores the value in a register (r#)
l r0 d0 Setting
Read from the device on d0 into register 0
l r1 d5 Pressure
Read the pressure from a sensor
This also works with aliases. For example:
alias Sensor d0
l r0 Sensor Temperature
- ls (load slot)
- ls r# d# slotNum parameter
Reads from a slot (slotNum) of a device (d#) and stores the value in a register (r#)
ls r0 d0 2 Occupied
Read from the second slot of device on d0, stores 1 in r0 if it's occupied, 0 otherwise.
And here is the code to read the charge of an AIMeE:
alias robot d0
alias charge r0
ls charge robot 0 Charge
- s (set)
- s d# parameter r#
Writes a setting to a device.
s d0 Setting r0
- add
- d s t # calculates s + t and stores the result in d
add r0 r1 1 # add 1 to r1 and store the result as r0
add r0 r0 1 # increment r0 by one
- sub
- d s t # calculates s - t and stores the result in d
- mul
- d s t # calculates s * t and stores the result in d
- div
- d s t # calculates s / t and stores the result in d
- mod
- d s t
- calculates s mod t and stores the result in d. Note this
- doesn't behave like the % operator - the result will be
- positive even if the either of the operands are negative
- slt
- d s t # stores 1 in d if s < t, 0 otherwise
- sqrt
- d s # calculates sqrt(s) and stores the result in d
- round
- d s # finds the rounded value of s and stores the result in d
- trunc
- d s # finds the truncated value of s and stores the result in d
- ceil
- d s # calculates the ceiling of s and stores the result in d
- floor
- d s # calculates the floor of s and stores the result in d
- max
- d s t # calculates the maximum of s and t and stores the result in d
- min
- d s t # calculates the minimum of s and t and stores the result in d
- abs
- d s # calculates the absolute value of s and stores the result in d
- log
- d s # calculates the natural logarithm of s and stores the result
- in d
- exp
- d s # calculates the exponential of s and stores the result in d
- rand
- d # selects a random number uniformly at random between 0 and 1
- inclusive and stores the result in d
- boolean arithmetic uses the C convention that 0 is false and any non-zero
- value is true.
- and
- d s t # stores 1 in d if both s and t have non-zero values,
- 0 otherwise
- or
- d s t # stores 1 in d if either s or t have non-zero values,
- 0 otherwise
- xor
- d s t # stores 1 in d if exactly one of s and t are non-zero,
- 0 otherwise
- nor
- d s t # stores 1 in d if both s and t equal zero, 0 otherwise
- Lines are numbered starting at zero
- j
- a # jumps to line a.
- bltz
- s a # jumps to line a if s < 0
- blez
- s a # jumps to line a if s <= 0
- bgez
- s a # jumps to line a if s >= 0
- bgtz
- s a # jumps to line a if s > 0
- beq
- s t a # jumps to line a if s == t
- bne
- s t a # jumps to line a if s != t
- bdseal
- d? a(r?|num) # Jump execution to line a and store current line number if device d? is set.
bdseal d0 32 #Store line number and jump to line 32 if d0 is assigned.
bdseal dThisVictim HarvestCrop #Store line in ra and jump to sub HarvestCrop if device dThisVictim is assigned.
- yield
- # ceases code execution for this power tick
- lb
- r? typeHash var batchMode # Loads var from all output network devices with provided typeHash using provided batchMode: Average(0), Sum (1), Minimum (2), Maximum (3). Can be used word or number. Result store into r?
- sb
- typeHash var r? # Store register r? to var on all output network devices with provided typeHash
- #
- # The following text will be ignored during compiling; use this to create comments.
Conditional functions cheatsheet
suffix | description | branch to line | branch and store return address | relative jump to line | set register |
---|---|---|---|---|---|
prefix: | b- | b-al | br- | s- | |
unconditional | j | jal | jr | ||
-eq | if a == b | beq | beqal | breq | seq |
-eqz | if a == 0 | beqz | beqzal | breqz | seqz |
-ge | if a >= b | bge | bgeal | brge | sge |
-gez | if a >= 0 | bgez | bgezal | brgez | sgez |
-gt | if a > b | bgt | bgtal | brgt | sgt |
-gtz | if a > 0 | bgtz | bgtzal | brgtz | sgtz |
-le | if a <= b | ble | bleal | brle | sle |
-lez | if a <= 0 | blez | blezal | brlez | slez |
-lt | if a < b | blt | bltal | brlt | slt |
-ltz | if a < 0 | bltz | bltzal | brltz | sltz |
-ne | if a != b | bne | bneal | brne | sne |
-nez | if a != 0 | bnez | bnezal | brnez | snez |
-dns | if device d is not set | bdns | bdnsal | brdns | sdns |
-dse | if device d is set | bdse | bdseal | brdse | sdse |
-ap | if a approximately equals b | bap | bapal | brap | sap |
-apz | if a approximately equals 0 | bapz | bapzal | brapz | sapz |
-na | if a not approximately equals b | bna | bnaal | brna | sna |
-naz | if a not approximately equals 0 | bnaz | bnazal | brnaz | snaz |
All b-
commands require target line as last argument, all s-
commands require register to store result as first argument. All br-
commands require number to jump relatively as last argument. e.g. breq a b 3
means if a=b then jump to 3 lines after.
All approximate functions require additional argument denoting how close two numbers need to be considered equal. E.g.: sap r0 100 101 0.01
will consider 100 and 101 almost equal (not more than 1%=0.01 different) and will set r0 to 1. The exact formula is if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8)
for -ap
and is similar for other approximate functions.
FLT_EPSILON = 2^(−23) ≈ 1.19e−07; float (32 bit) DBL_EPSILON = 2^(−52) ≈ 2.20e−16; double (64 bit)
if abs(100 - 101) <= max(0.01 * max(abs(100), abs(101)), float.epsilon * 8)
if abs(-1) <= max(0.01 * 101), float.epsilon * 8)
if 1 <= max(0.01 * 101, float.epsilon * 8)
if 1 <= max(1.01, FLT_EPSILON * 8) if 1 <= max(1.01, DBL_EPSILON * 8)
if 1 <= max(1.01, 1.19e−07 * 8) if 1 <= max(1.01, 2.20e−16 * 8)
if 1 <= max(1.01, 0.000000952) if 1 <= max(1.01, 0.00000000000000176)
if 1 <= 1.01 TRUE 1 if 1 <= 1.01 TRUE 1
Device Variables
- Activate
- 1 if device is activated (usually means running), otherwise 0
l r0 d0 Activate # sets r0 to 1 if on or 0 if off
- AirRelease
- Charge
- The current charge the device has.
- CLearMemory
- When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when triggered.
- Color
- ▇▇▇ 0 (or lower) = Blue
- ▇▇▇ 1 = Grey
- ▇▇▇ 2 = Green
- ▇▇▇ 3 = Orange
- ▇▇▇ 4 = Red
- ▇▇▇ 5 = Yellow
- ▇▇▇ 6 = White
- ▇▇▇ 7 = Black
- ▇▇▇ 8 = Brown
- ▇▇▇ 9 = Khaki
- ▇▇▇ 10 = Pink
- ▇▇▇ 11 (or higher) = Purple
- CompletionRatio
- ElevatorLevel
- ElevatorSpeed
- Error
- 1 if device is in error state, otherwise 0
- ExportCount
- How many items exporfted since last ClearMemory.
- Filtration
- The current state of the filtration system. For example filtration = 1 for a Hardsuit when filtration is On.
- Harvest
- Performs the harvesting action for any plant based machinery.
-
s d0 Harvest 1 # Performs 1 harvest action on device d0
- Horizontal
- HorizontalRatio
- Idle
- ImportCount
- Lock
- Maximum
- Mode
- On
- Open
- Output
- Plant
- Performs the planting operation for any plant based machinery.
-
s d0 Plant 1 # Plants one crop in device d0
- PositionX
- PositionY
- PositionZ
- Power
- PowerActual
- PowerPotential
- PowerRequired
- Pressure
- PressureExternal
- PressureInteral
- PressureSetting
- Quantity
- Total quantity in the device.
- Ratio
- Context specific value depending on device, 0 to 1 based ratio.
- RatioCarbonDioxide
- RatioNitrogen
- The ratio of nitrogen in device atmosphere.
- RatioOxygen
- The ratio of oxygen in device atmosphere.
- RatioPollutant
- The ratio of pollutant in device atmosphere.
- RatioVolatiles
- The ratio of volatiles in device atmosphere.
- RatioWater
- The ratio of water in device atmosphere.
- Reagents
- RecipeHash
- RequestHash
- RequiredPower
- Setting
- SolarAngle
- Solar angle of the device.
-
l r0 d0 SolarAngle # Sets r0 to the solar angle of d0.
- Temperature
- TemperatureSettings
- TotalMoles
- VelocityMagnitude
- VelocityRelativeX
- VelocityRelativeY
- VelocityRelativeZ
- Vertical
- Vertical setting of the device.
- VerticalRatio
- Ratio of vertical setting for device.
- Volume
- Returns the device atmosphere volume
Slot Variables
In general (always?) slots are assigned as follows.
- Slot 0: Import
- Slot 1: Export
- Slot 2: Inside Machine
- Occupied
ls r0 d0 2 Occupied #Stores 1 in r0 if d0 has more seeds
ls vOccupied dThisVictim 2 Occupied #stores 1 in vOccupied if dThisVictim has more seeds
- OccupantHash
- Quantity
- Damage
- Efficiency
- Health
- Growth
ls r0 d0 0 Growth # Store the numerical growth stage of d0 in r0
- Pressure
- Temperature
- Charge
- ChargeRatio
- Class
- PressureWaste
- PressureAir
- MaxQuantity
- Mature
ls r0 d0 0 Mature # Store 1 in r0 if d0 has a mature crop
ls vMature dThisVictim 0 Mature # Store 1 in vMature if dThisVictim has a mature crop
Examples
Previous examples were obsolete due to game changes, or confusing, they have been moved into the Discussions section
Harvie automation
This script uses the batch instruction sb ...
to control all Harvie devices on the network. But only one Harvie and one Tray will be the master and have their values read, the rest of the Harvies will repeat exactly what this unit does. Some problems with this design is that different types of crops mature at different speeds, and if seeds were manually planted and the master unit recieved the first seed, the harvesting action will be performed too early on all the other plants since they are growing a few seconds slower.
alias dHarvie d0 alias dTray d1 alias rHarvieHash r8 alias rTrayHash r9 l rHarvieHash dHarvie PrefabHash l rTrayHash dTray PrefabHash main: yield #read plant data from the Tray ls r0 dTray 0 Mature #harvestable plants return 1, young plants return 0 #nothing planted returns -1 beq r0 -1 plantCrop beq r0 1 harvestCrop ls r0 dTray 0 Seeding #seeds available returns 1, all seeds picked returns 0 #plants too young or old for seeds returns -1 beq r0 1 harvestCrop j main plantCrop: #stop the planting if no seeds available #otherwise it will plant nothing repeatedly ls r0 dHarvie 0 Occupied beq r0 0 main sb rHarvieHash Plant 1 j main harvestCrop: sb rHarvieHash Harvest 1 j main ### End Script ###
Solar Panel 2-axis tracking
This script was copied from the Solar_Logic_Circuits_Guide (code provided by bti, comments and readability changes by Fudd79)
# This code assumes the following: # Daylight Sensor data-port points north # Solar Panel data-port points east alias sensor d0 alias v_angle r0 alias h_angle r1 alias sun_up r2 define solar_panel_hash -539224550 define heavy_solar_panel_hash -1545574413 start: # Check to see if sun is up l sun_up sensor Activate # Go to reset if it's not beqz sun_up reset # Calculate vertical angle l v_angle sensor Vertical div v_angle v_angle 1.5 sub v_angle 50 v_angle # Write vertical angle to all solar panels sb solar_panel_hash Vertical v_angle sb heavy_solar_panel_hash Vertical v_angle # Obtain horizontal angle l h_angle sensor Horizontal # Write vertical angle to all solar panels sb solar_panel_hash Horizontal h_angle sb heavy_solar_panel_hash Horizontal h_angle # Go to start again yield j start reset: # Park solar panels vertically facing sunrise sb solar_panel_hash Vertical 0 sb heavy_solar_panel_hash Vertical 0 # Park solar panels horizontally facing sunrise sb solar_panel_hash Horizontal -90 sb heavy_solar_panel_hash Horizontal -90 # Wait 10 seconds sleep 10 # Go to start again j start ### End Script ###
Example experiment: how many lines of code are executed each tick?
To determine this, a script without yield
will be used. It should have as few lines as possible (so no labels are used, but a reset value at the top will be needed) and count the number of lines, the IC Housing will be used to display the result.
move r0 1 #the first line has number 0 add r0 r0 3 s db Setting r0 j 1
Result (the numbers appears every 0.5 seconds):
127
256 (+129)
385 (+129)
511 (+126)
640 (+129)
769 (+129)
895 (+126)
1024 (+129)
1153 (+129)
There is a repeating +129, +129, +126 sequence, a hint that the real value is 128. Which also happens to be the number of lines in a script, which makes sense. A variation of this experiment will show that empty rows are also counted towards this number.
Links
- [1] Stationeering.com offers a programmable circuits simulator so you can develop your code without repeatedly dying in game!
- [2] EASy68K is a 68000 Structured Assembly Language IDE.
- [3] syntax highlighting for IC10 MIPS for Visual Studio Code (updated Feb 10th 2022)
- [4] syntax highlighting for IC10 MIPS for KDE kwrite/kate text editor
- [5] syntax highlighting for IC10 MIPS for Notepad++
- [6] syntax highlighting for IC10 MIPS for Notepad++ (updated: 05/05/2021)
- [7] syntax highlighting for IC10 MIPS for Notepad++ (updated: 11/08/2022)