IC10: Difference between revisions
More actions
AnthonyD973 (talk | contribs) mNo edit summary |
m tweaks to previously added sections, changed the section title, added ICCode templates |
||
| (32 intermediate revisions by 16 users not shown) | |||
| Line 21: | Line 21: | ||
You can also read/write to the device where the IC is planted in using device '''db'''. | You can also read/write to the device where the IC is planted in using device '''db'''. | ||
Generally, there are up to 6 devices which can be set using the screwdriver '''d0''' to '''d5'''. A special device register '''db''' is the device wherever the IC is mounted upon. Very convenient for atmospheric devices where no separate IC socket is required. | Generally, there are up to 6 devices which can be set using the [[screwdriver]] '''d0''' to '''d5'''. A special device register '''db''' is the device wherever the IC is mounted upon. Very convenient for atmospheric devices where no separate IC socket is required. | ||
Note, the IC is completely unaware where d? is actually connected to. So if you get a logicType error, check d? number, or check if the screw has been set | Note, the IC is completely unaware where d? is actually connected to. So if you get a logicType error, check d? number, or check if the screw has been set on the socket. An alias is only convenient to convey what is expected to be set on the d? screw, it does not actually set or program the screw. | ||
* Read from device (load) <code>l r? d? logicType</code>: Reads logicType, like Pressure from a [[Sensors|gas sensor]], from device d? to register r?. 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. <br> Example: <code>l r0 Door Open</code> reads the 'Open' field of an object named 'Door', that would be connected to the IC housing of the chip. | * Read from device (load) <code>l r? d? logicType</code>: Reads logicType, like [[Pressure]] from a [[Sensors|gas sensor]], from device d? to register r?. 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]]. <br> Example: <code>l r0 Door Open</code> reads the 'Open' field of an object named 'Door', that would be connected to the IC housing of the chip. | ||
* Write to a device (set) <code>s d? logicType r?</code>: Write a value from a register back to a device using the command <code>s d? logicType r?</code>. For example, if d0 is set to a door using the screwdriver, <code>s d0 Open 0</code> sets the 'Open' status of the d0 (a door) to 0, effectively closing the door. | * Write to a device (set) <code>s d? logicType r?</code>: Write a value from a register back to a device using the command <code>s d? logicType r?</code>. For example, if d0 is set to a door using the screwdriver, <code>s d0 Open 0</code> sets the 'Open' status of the d0 (a door) to 0, effectively closing the door. | ||
=== batch IO to - '''Device registers''' === | === batch IO to - '''Device registers''' === | ||
'''Batch writing''' needs to be done to a specific '''deviceHash''' instead of d?. Is unique per device type, which you can find in the [[Stationpedia]] entries. | '''Batch writing''' needs to be done to a specific '''deviceHash''' instead of d?. Is unique per device type, which you can find in the [[Stationpedia]] entries. | ||
{{ICCode| | |||
lb r? deviceHash logicType batchMode | |||
sb deviceHash logicType r? | |||
}} | |||
Additionally, using the following batch commands, a '''nameHash''' can be provided to only modify devices with a certain name. | Additionally, using the following batch commands, a '''nameHash''' can be provided to only modify devices with a certain name. | ||
{{ICCode| | |||
lbn r? deviceHash nameHash logicType batchMode | |||
sbn deviceHash nameHash logicType r? | |||
}} | |||
All hashes used in the game are CRC-32 checksums computed from the strings they represent. | |||
'''batchMode''' is a parameter equal to 0, 1, 2, or 3. These are also defined as the constants '''Average''', '''Sum''', '''Minimum''', and '''Maximum''' respectively. The word or number can be used. | '''batchMode''' is a parameter equal to 0, 1, 2, or 3. These are also defined as the constants '''Average''', '''Sum''', '''Minimum''', and '''Maximum''' respectively. The word or number can be used. | ||
| Line 40: | Line 46: | ||
Combining one of these functions with the <code>HASH()</code> function can be advantageous: | Combining one of these functions with the <code>HASH()</code> function can be advantageous: | ||
{{ICCode| | |||
lbn r0 HASH("StructureGasSensor") | |||
HASH("Sensor 1") Temperature Average | |||
}} | |||
This code will load the average temperature of all gas sensors on the network named "Sensor 1" onto register '''r0''' | This code will load the average temperature of all gas [[sensors]] on the network named "[[Sensor]] 1" onto register '''r0''' | ||
If the batch read (lb/lbn) is done on a network without any matching devices the results will be as specified in the table: | If the batch read (lb/lbn) is done on a network without any matching devices the results will be as specified in the table: | ||
| Line 54: | Line 63: | ||
| Sum (1) || 0 | | Sum (1) || 0 | ||
|- | |- | ||
| Minimum (2) || 0 | | Minimum (2) || 0* | ||
|- | |- | ||
| Maximum (3) || ninf | | Maximum (3) || ninf | ||
|} | |} | ||
<nowiki>*</nowiki> This apparently used to result in pinf, but in version 0.2.6091.26702 both lb and lbn are returning 0. | |||
pinf and ninf are the IC10 constants for "positive infinity" and "negative infinity" respectively, and work much like other numerical constants. | |||
=== Examples === | === Examples === | ||
| Line 77: | Line 90: | ||
=== Special registers === | === 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''' | 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''' nor '''sp''' is protected, their values can be changed by instructions like any other register. | ||
==Stack Memory== | ==Stack Memory== | ||
The Stack is a memory that can hold 512 different values. Each IC10 has its own Stack, and some devices (like the Logical [[Sorter]]) also have a Stack. | |||
;push r?: adds the value '''r?''' and increments the '''sp''' by 1. | ;push r?: adds the value '''r?''' and increments the '''sp''' by 1. | ||
;pop r?: loads the value in the stack memory at index <code>sp-1</code> into register '''r?''' and decrements the '''sp''' by 1. | ;pop r?: loads the value in the stack memory at index <code>sp-1</code> into register '''r?''' and decrements the '''sp''' by 1. | ||
;peek r?: loads the value in the stack memory at index <code>sp-1</code> into register '''r?'''. | ;peek r?: loads the value in the stack memory at index <code>sp-1</code> into register '''r?'''. | ||
;poke address(r?|num) value(r?|num): Stores the provided value at the provided address in the stack. | |||
;get r? d? address(r?|num): loads the value in the stack memory at index <code>address</code> on provided device into register '''r?'''. | ;get r? d? address(r?|num): loads the value in the stack memory at index <code>address</code> on provided device into register '''r?'''. | ||
;getd r? id(r?|num) address(r?|num): loads the value in the stack memory at index <code>address</code> on provided device id into register '''r?'''. | ;getd r? id(r?|num) address(r?|num): loads the value in the stack memory at index <code>address</code> on provided device id into register '''r?'''. | ||
| Line 88: | Line 103: | ||
;putd id(r?|num) address(r?|num) value(r?|num) : adds the value to the stack memory off the provided device id at index <code>address</code>. | ;putd id(r?|num) address(r?|num) value(r?|num) : adds the value to the stack memory off the provided device id at index <code>address</code>. | ||
As mentioned previously, '''sp''' can be both written to and read from any time. When reading ('''peek''' or '''pop'''), '''sp''' must be between 1 and 512, inclusive. While writing ('''push'''), '''sp''' must be between 0 and 511, inclusive. | As mentioned previously, '''sp''' can be both written to and read from any [[time]]. When reading ('''peek''' or '''pop'''), '''sp''' must be between 1 and 512, inclusive. While writing ('''push'''), '''sp''' must be between 0 and 511, inclusive. | ||
Stack memory is persistent on logic chips. This means that if you have a logic chip and push values to the stack, the code that pushes those values can be removed and the stack will retain those values. | Stack memory is persistent on [[logic]] chips. This means that if you have a logic chip and push values to the stack, the code that pushes those values can be removed and the stack will retain those values. | ||
Note that this does not carry over to any other logic chips which receive the program of the original; They will need to have their stack memories programmed individually. | Note that this does not carry over to any other logic chips which receive the program of the original; They will need to have their stack memories programmed individually. | ||
| Line 124: | Line 139: | ||
#continue on | #continue on | ||
}} | |||
==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. | |||
{{ICCode| | |||
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. | |||
}} | }} | ||
==Device Ports== | ==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. | 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, | Additionally, it's 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: | The '''l''' (load) or '''s''' (set) instructions you have to read or set these values to your device. Examples: | ||
| Line 149: | Line 171: | ||
j main # jumps back to 'main' | j main # jumps back to 'main' | ||
}} | }} | ||
==Constants== | ==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. | 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. | ||
| Line 202: | Line 225: | ||
# r0 has the value 2 so dr0 points at d2 | # r0 has the value 2 so dr0 points at d2 | ||
}} | }} | ||
== Dynamically changing LogicType when interacting with Device Registers == | |||
When the <code>l</code> or <code>s</code> instructions are used to read from or write to a Device Register, the LogicType is the variable that will be interacted with (example: in <code>l r0 myDevice Temperature</code> the LogicType is the Temperature). In most scripts the LogicType will be hardcoded. But it's also possible to change the LogicType dynamically. The LogicType is an enum, where each type is identified by a unique integer value. This makes it possible to cycle through them in various ways, either as a list placed on the Stack, or by increasing the LogicType value via addition in each loop. | |||
{{ICCode| | |||
#loop through a list of LogicTypes | |||
push LogicType.RatioOxygen | |||
push LogicType.RatioVolatiles | |||
push LogicType.Temperature | |||
loop: | |||
pop r1 | |||
l r0 myDevice r1 | |||
... | |||
bgtz sp loop #loop until the Stack is empty}} | |||
==Network Referencing / Channels== | ==Network Referencing / Channels== | ||
All cable networks have 8 Channels which can have data loaded from/stored to via a device and connection reference. Connections for each supported device are listed in the stationpedia. All 'connections' a device can make are a connection (pipe, chute, cable), but only cable networks have channels. | All cable networks have 8 Channels which can have data loaded from/stored to via a device and connection reference. Connections for each supported device are listed in the stationpedia. All 'connections' a device can make are a connection ([[pipe]], [[chute]], cable), but only cable networks have channels. | ||
The 8 channels (Channel0 to Channel7) are however volatile, in that data is destroyed if any part of the cable network is changed, removed, or added to, and also whenever the world is exited. All these channels default to NaN. Strictly speaking, they default to what we would call "quiet NaN", in that its not an error it simply means its not a number yet. Recommend you use these channels for reading and writing between networks, rather than as a data store. This effectively means an IC can read all the networks for all devices to connected to it, so not just their own local network, but any networks any device they can reference is connected to. | The 8 channels (Channel0 to Channel7) are however volatile, in that data is destroyed if any part of the cable network is changed, removed, or added to, and also whenever the [[world]] is exited. All these channels default to NaN. Strictly speaking, they default to what we would call "quiet NaN", in that its not an error it simply means its not a number yet. Recommend you use these channels for reading and writing between networks, rather than as a data store. This effectively means an IC can read all the networks for all devices to connected to it, so not just their own local network, but any networks any device they can reference is connected to. | ||
{{ICCode| | {{ICCode| | ||
# d0 is device zero, and the :0 refers | # d0 is device zero, and the :0 refers | ||
| Line 213: | Line 249: | ||
l r0 d0:0 Channel0}} | l r0 d0:0 Channel0}} | ||
For example: on an IC Housing, the 0 connection is the data port and 1 is power, so you could write out r0 to Channel0 of the power network of the Housing using <code>s db:1 Channel0 r0</code> | For example: on an [[IC Housing]], the 0 connection is the [[Data Port|data port]] and 1 is [[power]], so you could write out r0 to Channel0 of the power network of the Housing using <code>s db:1 Channel0 r0</code> | ||
{{ICCode| | {{ICCode| | ||
#read all 8 channels with a loop and | |||
# | #place the values in r0 to r7 | ||
move r15 LogicType.Channel0 #LogicType integer | |||
move r14 0 #pointer for indirect referencing | |||
loop: | |||
l rr14 db:0 r15 | |||
add r15 r15 1 #next channel | |||
add r14 r14 1 #next register | |||
ble r15 LogicType.Channel7 loop | |||
}} | }} | ||
==Logic gates, Bitwise and Logical== | |||
All logic gates in MIPS have a bitwise behavior. The available gates are NOT, AND, OR, XOR and NOR (XNOR and NAND are missing). | |||
In Bitwise operations, each bit is matched separately, which includes the [[sign]] bit. | |||
To understand what is going on with bitwise operations, a little bit of [[computer]] theory is needed. In Stationeers each register uses 64 bits for integer values (a number without decimals), where the 64th bit is the sign-bit (0 for positive and 1 for negative). Since the number 0 is counted as a positive value, this gives each register a range of (2^63 - 1) to -2^63. Negative numbers also behave according to Two's complement (https://en.wikipedia.org/wiki/Two%27s_complement). Which means that a number with a sign-bit of 1 will have all of its number bits flipped as well, so that the decimal value of -1 is represented by a binary value of sixtyfour 1's. | |||
MIPS have binary notation (https://en.wikipedia.org/wiki/Binary_number) that is activated by placing a % in front of the number. The _ characters are ignored and only used for readability. | |||
<code>not r0 0</code><br> | |||
<code># 0 = %00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000</code><br> | |||
<code># flip all bits</code><br> | |||
<code># %11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111 = -1</code><br> | |||
<code># r0 equals -1</code> | |||
<code>and r0 3 6</code><br> | |||
<code># 3 = %011</code><br> | |||
<code># 6 = %110</code><br> | |||
<code># only the bits in the "2" position are matching</code><br> | |||
<code># r0 equals %010 = 2</code> | |||
Logical operations can still be performed via alternative instructions. But these are not perfect substitutes, they treat negative values differently, and some can produce non-binary outputs. When using these, keep in mind that devices that wants a binary value will treat any non-binary values like this: >= 1 counts as "1" and <1 counts as "0". | |||
<code># r0 = result</code><br> | |||
<code># r1 = input A</code><br> | |||
<code># r2 = input B</code><br> | |||
<code>Logical NOT = seqz r0 r1</code><br> | |||
<code>Logical AND = min r0 r1 r2</code><br> | |||
<code>Logical OR = max r0 r1 r2</code><br> | |||
<code>Logical XOR = sne r0 r1 r2 (only for binary inputs)</code><br> | |||
<code>Logical NAND = not and</code><br> | |||
<code>Logical NOR = not or</code><br> | |||
<code>Logical XNOR = not xor</code> | |||
==Debugging advices== | ==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.<br> | 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 unless the housing is an [[Air Conditioner]]. To see the value, just stand close to the IC housing and look directly at the housing.<br> | ||
<code>s db Setting r0</code>. # sets/writes the value of register '''r0''' into the parameter '''Setting''' of the IC Housing('''db''') | <code>s db Setting r0</code>. # 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 | To check if a certain block of code is executed, use the above trick but with a number of your choice, like the line number.<br> This example will display the number 97 on the IC housing.<br> | ||
<code>s db Setting | <code>s db Setting 97</code> # sets/writes the number 97 into the parameter '''Setting''' of the IC Housing('''db''') | ||
With this trick you can trace your program as if you were using breakpoints: | |||
{{ICCode|... some code... | |||
jal debug | |||
...some more code... | |||
jal debug | |||
...rest of the code.... | |||
debug: | |||
s db Setting ra # print stored line number | |||
s db On 0 # stop execution. Turn the IC housing back manually on to proceed from there | |||
j ra # return to normal execution | |||
}} | |||
Always use unique names for labels. When a label is named after a IC10 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. | Always use unique names for labels. When a label is named after a IC10 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. | ||
| Line 233: | Line 322: | ||
A [[Cartridge#Configuration|configuration cartridge]] installed in a [[Handheld_Tablet|tablet]] can be used to see all available values and configuration parameter for all devices you focus on. | A [[Cartridge#Configuration|configuration cartridge]] installed in a [[Handheld_Tablet|tablet]] can be used to see all available values and configuration parameter for all devices you focus on. | ||
==Learning IC10== | == Learning IC10 == | ||
IC10 can | IC10 can feel overwhelming to beginners due to the large number of available instructions. Fortunately, only a small subset is actually needed to start writing useful scripts. All instructions and variables can be viewed in more detail in the IC editor inside the game, by clicking the "f", "x", and "s(x)" buttons in the top-right corner. The Stationpedia is also an essential resource for scripting, as it lists the logic values available to each device. | ||
20 useful instructions for IC10 beginners | |||
<code>alias</code> assigns a readable name to a register or device, completely optional but makes the script much easier to read<br> | |||
<code>label:</code> acts like a tag for jump instructions<br> | |||
<code>yield</code> pause at this line for 1-tick and then resume, if not used the script will automatically pause for 1-tick after executing 128 lines | |||
<code>j labelName/ra</code> jump to a label, or the value in register ra (return address) and continue executing from that line<br> | |||
<code>jal labelName</code> stores the next line number into register ra, then jump to labelName | |||
<code>beq</code> branch if equal, compares two values and jumps to a label if they are equal<br> | |||
<code>beq | <code>bne</code> branch if not equal, compares two values and jumps to a label if they are different<br> | ||
<code>bne | <code>bgt</code> branch if greater than, compares two values and jumps to a label if the first is greater than the second<br> | ||
<code>bgt | <code>blt</code> branch if less than, compares two values and jumps to a label if the first is less than the second<br> | ||
<code>blt | The suffix -al can be added to each of these instructions (example: beqal) to store the next line number in the "return address" register. | ||
The suffix -al can be added to each of these (example: beqal) to | |||
<br> | <code>l</code> loads a logic value from a device, example: the Temperature from a gas sensor<br> | ||
< | <code>lb</code> loads a batch of logic values from many identical devices and returns the average/sum/minimum/maximum value<br> | ||
<code>ls</code> loads a slot logic value, example: the Quantity of a filter slot shows a fitted filters remaining % value<br> | |||
<code>s</code> stores a logic value to a device, example: the On value will turn the device on and off<br> | |||
<code>sb</code> stores a batch of logic values to many identical devices, example: Horizontal will rotate all connected Solar Panels | |||
</ | |||
<br> | <code>move</code> copy a value to a register<br> | ||
<br> | <code>add</code> addition of two values<br> | ||
<br> | <code>sub</code> subtraction of two values<br> | ||
<br>- | <code>mul</code> multiplication of two values<br> | ||
<code>div</code> division of two values<br> | |||
<code>seqz</code> set if equal to zero, commonly used as a not-gate since it turns 0 into 1, and 1 into 0 | |||
It's important to know how scripts works in general in order to write them. All scripts begin by executing line 0, and then the line below etc, until a total of 128 lines has been executed, regardless if they have any code or not. Most scripts are written as loops by using a jump instruction, this allows the script to repeat the same tasks forever. By using a yield instruction, the script will always start from the same place every time, allowing it to load in fresh data values instead of accidentally using old data. | |||
A script based on a modular programming style, will break up its various tasks into different subroutines and use a main loop to call these subroutines. This is a good way to organize a script where the subroutines work independently from each other. It's commonly used when the script handles multiple independent tasks. A greenhouse script could simultaneously control the temperature, filter out oxygen produced by the plants, pump in CO2 as needed, turn grow lights on and off based on a timer, and maybe even use a LArRE for harvesting and planting, all at the same time. | |||
A script based on a state based programming style, will be written like a chain of sequences that follow after each other. This style allows a script to move into different states, where each state will perform different tasks, before transitioning to the next state in the chain. This style is useful for things like airlocks, the AIMEe mining bot, and mining rockets. | |||
When writing a script, knowledge about a process or a device, is often more valuable than knowing lots of IC10 instructions. Someone that knows that Grow Lights are only 80% efficient, will increase the light time accordingly to prevent illumination stress to build up over time. This is important knowledge when automating a greenhouse for non-perennial plants, but it's not something that can be learned by memorizing IC10 instructions. | |||
---- | ---- | ||
| Line 329: | Line 399: | ||
=== Batch instructions === | === Batch instructions === | ||
The batch instructions can address multiple devices only via their '''PrefabHash''' generated from the prefab name using the `HASH("Name")` macro or copied directly from the [[Stationpedia]]. A prefab hash is always an integer. All devices that can be read with logic contain the logic value '''PrefabHash''' and '''NameHash'''. | The batch instructions can address multiple devices only via their '''PrefabHash''' generated from the prefab name using the `HASH("Name")` macro or copied directly from the [[Stationpedia]]. A prefab [[hash]] is always an integer. All devices that can be read with logic contain the logic value '''PrefabHash''' and '''NameHash'''. | ||
See [[#Slot.2FLogic_.2F_Batched|Batched instructions]] for a comprehensive list of all batch instructions. | See [[#Slot.2FLogic_.2F_Batched|Batched instructions]] for a comprehensive list of all batch instructions. | ||
| Line 342: | Line 412: | ||
[[#clrd|clrd]], [[#getd|getd]], [[#putd|putd]],<br> | [[#clrd|clrd]], [[#getd|getd]], [[#putd|putd]],<br> | ||
[[#ld|ld]], [[#sd|sd]], (no slot access via reference ID) | [[#ld|ld]], [[#sd|sd]], (no slot access via reference ID) | ||
---- | |||
== Internal Stack Programming == | |||
The following devices support Stack instructions: [[Logic Sorter]], Autolathe, Electronics Printer, Hydraulic Pipe Bender, Tool Manufactory, Security Printer and Rocket Manufactory. The Medium Satellite Dish also supports Stack instructions, but it's only used to see the traders buy and sell data before they land; see the [[Satellite Tracking]] page for details. | |||
Stack instructions are encoded integer values that these devices can interpret as commands. Instead of controlling them directly through an IC10 script, they can be controlled by placing certain values on their Stacks. Each instruction is stored as a single number whose bits encode both an operational code (OP code) and any parameters required by that operation. In other words, each Stack instruction is a number whose bits are divided into fields, where each field stores a different piece of information. | |||
Although Stack instructions are stored as integers, they are best interpreted as binary values to show the individual bits. Binary numbers have their least significant bit on the right side. The lowest 8 bits always refers to the bits on the right side, and they are always reserved for the OP code that determines what type of instruction it is. | |||
====== Example: constructing a Stack instruction that filters out Iron Ore with a Logic Sorter ====== | |||
Always begin by looking at the Stationpedia to see the available OP codes and what their bit fields represent. The Logic Sorter has two OP codes that can be used to sort individual items: <code>SorterInstruction.FilterPrefabHashEquals</code> (OP code = 1) and <code>SorterInstruction.FilterPrefabHashNotEquals</code> (OP code = 2). These instructions use the same layout: the lowest 8 bits contain the OP code and the next 32 bits contain the Prefab Hash of the item being filtered. To combine the OP code and the Hash into a single value, the item Hash must first be moved into its expected field (between bits 8 to 39). This is done by bit shifting it 8 positions to the left, afterwards the OP code can be placed into its expected field (bits 0 to 7). | |||
<code>FilterPrefabHash instruction format = %[32-bit prefab hash][8-bit OP code]</code><br> | |||
<code>Prefab Hash for ItemIronOre = 1758427767</code><br> | |||
<code>ItemIronOre as a binary value = %01101000_11001111_01111111_10010111</code><br> | |||
<code>Bit shifted 8 positions to the left = %01101000_11001111_01111111_10010111_00000000</code><br> | |||
<code>OP code 1 = %00000001</code><br> | |||
<code>Combine OP code and bit shifted Hash = %01101000_11001111_01111111_10010111_00000001</code> | |||
The Stack instruction is complete and the value can be moved to the Logic Sorters Stack. The IC10 for doing that could look like: | |||
{{ICCode|alias LogicSorter d0 | |||
sll r0 HASH("ItemIronOre") 8 # bit-shift the hash value 8 times to the left | |||
or r0 r0 SorterInstruction.FilterPrefabHashEquals # combine bit shifted hash and OP code | |||
put LogicSorter 0 r0 # write the instruction to Stack at position 0}} | |||
The finalized Stack instruction can be constructed in various ways. Rocketwerkz demonstrated this with <code>sll</code> and <code>or</code>, which made that choice popular. The same result can also be obtained with <code>mul</code> (multiplying by 256 is equivalent to 8 left bit shifts) and <code>add</code>, or with <code>ins</code> to manipulate the bit fields directly (but limited to the first 52 bits of the total 64). | |||
For Logic Sorters the Mode value is also important. It can be either All (0), Any (1), or None (2), with All being the default value. All means every Stack instruction must evaluate as a match, Any means at least one must evaluate as a match, and None means that every instruction must fail to be a match. When sorting two or more different items, the Mode needs to be changed. To reverse the output sides for a Logic Sorter when using <code>SorterInstruction.FilterPrefabHashEquals</code> (OP code 1), change the Mode between Any (1) and None (2). | |||
When printers receive a <code>PrinterInstruction.ExecuteRecipe</code> they will immediately start printing, and the instruction's quantity value will decrease for each item produced. An instruction with a quantity of zero will not print anything. If the printing is halted due to missing reagents, the printer will proceed to the next instruction (unless it's preceded by a <code>PrinterInstruction.WaitUntilNextValid</code> ). When a printer is idle it will loop through its Stack, which causes any unfinished printing jobs will be resumed as soon as reagents become available. | |||
Some printer instructions should never be written to the Stack, because they are already present at fixed memory addresses. The <code>PrinterInstruction.StackPointer</code> is stored at stack address 63 and its value contains the current stack pointer (8 bits for the OP code and 16 bits for the pointer). The <code>PrinterInstruction.MissingRecipeReagent</code> entries occupy Stack addresses 54 to 62 (starting at 54). These addresses will only be populated when a <code>PrinterInstruction.WaitUntilNextValid</code> instruction precedes an <code>PrinterInstruction.ExecuteRecipe</code> instruction that is missing the required reagents. The hash values obtained from <code>PrinterInstruction.MissingRecipeReagent</code> can be converted into ingot hashes, first by using <code>sra</code> to isolate the reagent hash value while preserving its sign bit, and then with <code>rmap</code>. | |||
====== Example: automatically print 50 Cable Coils ====== | |||
{{ICCode|alias printer d0 | |||
move r0 PrinterInstruction.ExecuteRecipe | |||
ins r0 50 8 8 # insert quantity 50 starting at the 8th bit | |||
ins r0 HASH("ItemCableCoil") 16 32 # insert the item Hash starting at the 16th bit | |||
put printer 0 PrinterInstruction.WaitUntilNextValid # required to populate missing reagents | |||
put printer 1 r0 # ExecuteRecipe instruction placed after WaitUntilNextValid | |||
# if the printer runs out of copper, an external IC10 script can detect which ingot to send | |||
get r0 printer 54 # read the Stack address for the first missing reagent | |||
sra r1 r0 16 # remove the OPcode and quantity while preserving the sign bit | |||
rmap r2 printer r1 # convert the reagent hash into the corresponding ingot hash}} | |||
=Instructions= | =Instructions= | ||
| Line 445: | Line 560: | ||
: When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when triggered. | : When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when triggered. | ||
<div id="Color"></div> | <div id="Color"></div> | ||
[[Category:Data Network]] | |||
== Data Network == | |||
This is a list of the colors used by various items with the logic network. These are the same colors that can be applied to physical items with a [[Spray_Can|Spray Can]]. These codes can be used in IC10 scripts or logic circuits to set the color of various components, such as lights. | |||
{| class="wikitable sortable" | |||
|- | |||
! Value !! Color !! Hex | |||
|- | |||
| 0 || Blue || #212AA5<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#212AA5;"></div> | |||
|- | |||
| 1 || Gray || #7B7B7B<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#7B7B7B;"></div> | |||
|- | |||
| 2 || Green || #3F9B39<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#3F9B39;"></div> | |||
|- | |||
| 3 || Orange || #FF662B<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#FF662B;"></div> | |||
|- | |||
| 4 || Red || #E70200<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#E70200;"></div> | |||
|- | |||
| 5 || Yellow || #FFBC1B<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#FFBC1B;"></div> | |||
|- | |||
| 6 || White || #E7E7E7<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#E7E7E7;"></div> | |||
|- | |||
| 7 || Black || #080908<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#080908;"></div> | |||
|- | |||
| 8 || Brown || #633C2B<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#633C2B;"></div> | |||
|- | |||
| 9 || Khaki || #63633F<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#63633F;"></div> | |||
|- | |||
| 10 || Pink || #E41C99<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#E41C99;"></div> | |||
|- | |||
| 11 || Purple || #732CA7<div style="height: 20px; width: 20px; border: 1px solid black; margin-right: 5px; background-color:#732CA7;"></div> | |||
|} | |||
<div id="CompletionRatio"></div> | <div id="CompletionRatio"></div> | ||
;CompletionRatio | ;CompletionRatio | ||
| Line 471: | Line 604: | ||
: How many items exporfted since last ClearMemory. | : How many items exporfted since last ClearMemory. | ||
<div id="Filtration"></div> | <div id="Filtration"></div> | ||
;Filtration | ;[[Filtration]] | ||
: The current state of the filtration system. For example filtration = 1 for a Hardsuit when filtration is On. | : The current state of the filtration system. For example filtration = 1 for a [[Hardsuit]] when filtration is On. | ||
<div id="Harvest"></div> | <div id="Harvest"></div> | ||
;Harvest | ;Harvest | ||
| Line 533: | Line 666: | ||
<div id="RatioNitrogen"></div> | <div id="RatioNitrogen"></div> | ||
;RatioNitrogen | ;RatioNitrogen | ||
: The ratio of nitrogen in device atmosphere. | : The ratio of [[nitrogen]] in device atmosphere. | ||
<div id="RatioOxygen"></div> | <div id="RatioOxygen"></div> | ||
;RatioOxygen | ;RatioOxygen | ||
| Line 539: | Line 672: | ||
<div id="RatioPollutant"></div> | <div id="RatioPollutant"></div> | ||
;RatioPollutant | ;RatioPollutant | ||
: The ratio of pollutant in device atmosphere. | : The ratio of [[pollutant]] in device atmosphere. | ||
<div id="RatioVolatiles"></div> | <div id="RatioVolatiles"></div> | ||
;RatioVolatiles | ;RatioVolatiles | ||
: The ratio of volatiles in device atmosphere. | : The ratio of [[volatiles]] in device atmosphere. | ||
<div id="RatioWater"></div> | <div id="RatioWater"></div> | ||
;RatioWater | ;RatioWater | ||
: The ratio of water in device atmosphere. | : The ratio of [[water]] in device atmosphere. | ||
<div id="Reagents"></div> | <div id="Reagents"></div> | ||
;Reagents | ;[[Reagents]] | ||
<div id="RecipeHash"></div> | <div id="RecipeHash"></div> | ||
;RecipeHash | ;[[RecipeHash]] | ||
<div id="RequestHash"></div> | <div id="RequestHash"></div> | ||
;ReferenceId | ;ReferenceId | ||
| Line 607: | Line 740: | ||
<div id="Efficiency"></div> | <div id="Efficiency"></div> | ||
;Efficiency | ;Efficiency | ||
<div id="FilterType"></div> | |||
;FilterType | |||
Reports the type of [[filter]] installed in the slot (e.g. in a Filtration unit). | |||
:Return Values: 1: [[Oxygen]], 2: [[Nitrogen]], 4: [[Carbon Dioxide]], 8: [[Volatiles]], 16: [[Pollutants]], 32: [[Water]], 64: [[Nitrous Oxide]], 16384: [[Hydrogen]], 65536: [[Polluted Water]], 131072: [[Hydrazine]], 524288: [[Alcohol]], 1048576: [[Helium]], 2097152: [[Liquid Sodium Chloride]], 4194304: [[Silanol]], 16777216: [[Hydrochloric Acid]], 67108864: [[Ozone]] | |||
:<code>ls r0 db 0 FilterType</code> | |||
<div id="Health"></div> | <div id="Health"></div> | ||
;Health | ;Health | ||
| Line 643: | Line 781: | ||
===Harvie automation=== | ===Harvie automation=== | ||
This script uses the batch instruction <code>sb ...</code> 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. | This script uses the batch instruction <code>sb ...</code> 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. | ||
<div class="mw-collapsible mw-collapsed" data-expandtext="{{int:Expand, Automated Harvie Script}}" data-collapsetext="{{int:Collapse, Automated Harvie Script}}"> | <div class="mw-collapsible mw-collapsed" data-expandtext="{{int:Expand, Automated Harvie Script}}" data-collapsetext="{{int:Collapse, Automated Harvie Script}}"> | ||
| Line 741: | Line 879: | ||
sleep 10 | sleep 10 | ||
j start | j start | ||
}} | |||
</div> | |||
<br> | |||
----- | |||
===IC10 Schmitt Trigger - Sensor Range Based Device Toggle=== | |||
This script demonstrates the IC10 style Schmitt Trigger Pattern in basic examples using the <code>select</code> instruction with its ternary condition to toggle a device <code>On</code> state based on a range of sensor readings. Examples for both styles are provided below, i.e. 'cooling' or 'heating', for 'below min' or 'greater than max' style range-based toggle. A temperature based control is shown in the examples, but any sensor reading could be used to dynamically toggle a device <code>On</code> state such pressure, charge ratio, solar angle, etc. | |||
<div class="mw-collapsible mw-collapsed" data-expandtext="{{int:Expand, IC10 Schmitt Trigger Pattern}}" data-collapsetext="{{int:Collapse, IC10 Schmitt Trigger Pattern}}"> | |||
{{ICCode| | |||
# ---- IC10 Schmitt Trigger ---- | |||
# -- Standard - i.e. "cooling" | |||
#When the temperature sensor device reports a value | |||
# higher than TempMax and the valve is not On, the | |||
# valve will turn On. | |||
#When the valve is On, and the sensor device reports | |||
# higher than TempMin, the valve will remain on. | |||
#Once that sensor reports a temperature lower than | |||
# TempMin, the valve will once again shut off and | |||
# this cycle will continue indefinitely: | |||
define TempMax 296.15 #23C | |||
define TempMin 283.15 #10C | |||
alias Valve d0 | |||
alias Sensor d1 | |||
example1: | |||
yield | |||
l r0 Valve On | |||
select r0 r0 TempMin TempMax | |||
l r1 Sensor Temperature | |||
sgt r0 r1 r0 | |||
s Valve On r0 | |||
j example1 | |||
# ---- IC10 Schmitt Trigger ---- | |||
# -- Inverted - i.e. "heating" | |||
#When the temperature sensor device reports a value | |||
# lower than TempMin and the valve is not On, the | |||
# valve will turn On. | |||
#When the valve is On, and the sensor device reports | |||
# lower than TempMax, the valve will remain on. | |||
#Once that sensor reports a temperature higher than | |||
# TempMax, the valve will once again shut off and | |||
# this cycle will continue indefinitely: | |||
define TempMax 296.15 #23C | |||
define TempMin 283.15 #10C | |||
alias Valve d0 | |||
alias Sensor d1 | |||
example2: | |||
yield | |||
l r0 Valve On | |||
select r0 r0 TempMax TempMin | |||
l r1 Sensor Temperature | |||
slt r0 r1 r0 | |||
s Valve On r0 | |||
j example2 | |||
}} | }} | ||
</div> | </div> | ||
| Line 801: | Line 1,008: | ||
}} | }} | ||
This code would behave incorrectly if <code>push ra</code> and <code>pop ra</code> were not present: within <code>orientPanelsToStar</code>, | This code would behave incorrectly if <code>push ra</code> and <code>pop ra</code> were not present: within <code>orientPanelsToStar</code>, doing <code>jal orientPanelsTo</code> would replace the current value of the <code>ra</code> register, permanently erasing where <code>orientPanelsToStar</code> itself should jump back to once done. Pushing and popping <code>ra</code> effectively saves its value until we need it again. | ||
(A tempting but wrong approach to 'saving' <code>ra</code> would be to <code>move</code> it within a different register (e.g. <code>r15</code>) before calling <code>orientPanelsTo</code>, however that only permits two levels of functions, since if <code>orientPanelsTo</code> itself wants to call another function, it would not be able to use <code>r15</code> since | (A tempting but wrong approach to 'saving' <code>ra</code> would be to <code>move</code> it within a different register (e.g. <code>r15</code>) before calling <code>orientPanelsTo</code>, however that only permits two levels of functions, since if <code>orientPanelsTo</code> itself wants to call another function, it would not be able to use <code>r15</code> to save its <code>ra</code> register since <code>r15</code> already saves <code>orientPanelsToStar</code>'s return address. Just pushing/popping <code>ra</code> fixes all issues, allowing for a ginormous maximum function call depth of 512!) | ||
As a last note, if the script will push/pop values like <code>ra</code>, starting the script by clearing the stack (which where push/pop move the data to) by running <code>clr db</code> is advisable, unless the IC10 chip is not inserted inside an IC Housing (e.g. inserted in the slot of an Air Conditioner), since <code>clr db</code> will cause an error in this case. To do so, run <code>clr db</code> before the script's main loop. | |||
---- | ---- | ||
=Links= | =Links= | ||
---- | ---- | ||
* Stationeers online IC10 Emulators so you can develop your code without repeatedly dying in game | * Stationeers online IC10 Emulators so you can develop your code without repeatedly dying in game | ||
** [https://ic10.dev/] Stationeers Code Simulator | ** [https://ic10.dev/] Stationeers Code Simulator | ||
** [https://ic10emu.dev] Stationeers IC10 Editor & Emulator - A feature packed code editor for Stationeers IC10 code, paired with a robust debugger and emulator. Edit, test, and share code. | ** [https://ic10emu.dev] Stationeers IC10 Editor & Emulator - A feature packed code editor for Stationeers IC10 code, paired with a robust debugger and emulator. Edit, [[test]], and share code. | ||
** [https://stationeering.com/tools/ic] Stationeering provides a simulation of the IC10 chip inside Stationeers. IDE with error checking, full visibility of stack and registers. | ** [https://stationeering.com/tools/ic] Stationeering provides a simulation of the IC10 chip inside Stationeers. IDE with error checking, full visibility of stack and registers. | ||
* [http://www.easy68k.com/] EASy68K is a 68000 Structured Assembly Language IDE. | * [http://www.easy68k.com/] EASy68K is a 68000 Structured Assembly Language IDE. | ||
| Line 818: | Line 1,028: | ||
* [https://drive.google.com/file/d/1Xrv5U0ZI5jDcPv7yX7EAAxaGk5hKP0xO/view?usp=sharing] syntax highlighting for IC10 MIPS for Notepad++ (updated: 11/08/2022) | * [https://drive.google.com/file/d/1Xrv5U0ZI5jDcPv7yX7EAAxaGk5hKP0xO/view?usp=sharing] syntax highlighting for IC10 MIPS for Notepad++ (updated: 11/08/2022) | ||
* [https://pastebin.com/3kmGy0NN] syntax highlighting for IC10 MIPS for Notepad++ (updated: 23/03/2024) | * [https://pastebin.com/3kmGy0NN] syntax highlighting for IC10 MIPS for Notepad++ (updated: 23/03/2024) | ||
* [https://github.com/SnorreSelmer/stationeers_ic10/blob/main/README.md] Repo with a lot of code examples | |||
---- | ---- | ||
Latest revision as of 05:06, 10 June 2026
Scripting language for IC10 housings / chips
MIPS is Stationeers' inspiration for the in-game scripting language called IC10. It runs on IC10 chips crafted at the Electronics Printer.
Registers
Internal registers r?: The IC contains 16 CPU registers, numbered r0 to r15. From now on referred to as r?.
Device registers d? logicType: Device registers are written to and from the IC. A device register is numbered d0 to d5 (select via screw), or db (connected device). From now on referred to as d?.
Logic and algorithmic with Internal registers
All calculations are exclusively performed to and from r? registers, or generally more understood as variables in programming. You can use aliases to give convenient names with the alias string r?|d?command (see below).
Internal registers can be manipulated in various ways.
- Write constant values
move r? (r?|num): Example:move r0 2sets r0 to the number 2. - Calculate: Calculations are done to- and from these registers, like
add r? a(r?|num) b(r?|num). Example:add r1 r0 3adds 3 to r0, and writes to r1.
Note, for any kind of if statements or loop behaviours, knowing about labels, branching, and jumps is essential knowledge. See below.
IO to Device registers
Acronym d? stands for device, where ? is a number corresponding to the screw device selector on the socket. You can also read/write to the device where the IC is planted in using device db.
Generally, there are up to 6 devices which can be set using the screwdriver d0 to d5. A special device register db is the device wherever the IC is mounted upon. Very convenient for atmospheric devices where no separate IC socket is required.
Note, the IC is completely unaware where d? is actually connected to. So if you get a logicType error, check d? number, or check if the screw has been set on the socket. An alias is only convenient to convey what is expected to be set on the d? screw, it does not actually set or program the screw.
- Read from device (load)
l r? d? logicType: Reads logicType, like Pressure from a gas sensor, from device d? to register r?. 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 Openreads the 'Open' field of an object named 'Door', that would be connected to the IC housing of the chip. - Write to a device (set)
s d? logicType r?: Write a value from a register back to a device using the commands d? logicType r?. For example, if d0 is set to a door using the screwdriver,s d0 Open 0sets the 'Open' status of the d0 (a door) to 0, effectively closing the door.
batch IO to - Device registers
Batch writing needs to be done to a specific deviceHash instead of d?. Is unique per device type, which you can find in the Stationpedia entries.
lb r? deviceHash logicType batchMode sb deviceHash logicType r?
Additionally, using the following batch commands, a nameHash can be provided to only modify devices with a certain name.
lbn r? deviceHash nameHash logicType batchMode sbn deviceHash nameHash logicType r?
All hashes used in the game are CRC-32 checksums computed from the strings they represent.
batchMode is a parameter equal to 0, 1, 2, or 3. These are also defined as the constants Average, Sum, Minimum, and Maximum respectively. The word or number can be used.
Combining one of these functions with the HASH() function can be advantageous:
lbn r0 HASH("StructureGasSensor") HASH("Sensor 1") Temperature Average
This code will load the average temperature of all gas sensors on the network named "Sensor 1" onto register r0
If the batch read (lb/lbn) is done on a network without any matching devices the results will be as specified in the table:
| Batch Mode | Result |
|---|---|
| Average (0) | nan |
| Sum (1) | 0 |
| Minimum (2) | 0* |
| Maximum (3) | ninf |
* This apparently used to result in pinf, but in version 0.2.6091.26702 both lb and lbn are returning 0.
pinf and ninf are the IC10 constants for "positive infinity" and "negative infinity" respectively, and work much like other numerical constants.
Examples
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 nor sp is protected, their values can be changed by instructions like any other register.
Stack Memory
The Stack is a memory that can hold 512 different values. Each IC10 has its own Stack, and some devices (like the Logical Sorter) also have a Stack.
- push r?
- adds the value r? and increments the sp by 1.
- pop r?
- loads the value in the stack memory at index
sp-1into register r? and decrements the sp by 1. - peek r?
- loads the value in the stack memory at index
sp-1into register r?. - poke address(r?|num) value(r?|num)
- Stores the provided value at the provided address in the stack.
- get r? d? address(r?|num)
- loads the value in the stack memory at index
addresson provided device into register r?. - getd r? id(r?|num) address(r?|num)
- loads the value in the stack memory at index
addresson provided device id into register r?. - put d? address(r?|num) value(r?|num)
- adds the value to the stack memory off the provided device at index
address. - putd id(r?|num) address(r?|num) value(r?|num)
- adds the value to the stack memory off the provided device id at index
address.
As mentioned previously, sp can be both written to and read from any time. When reading (peek or pop), sp must be between 1 and 512, inclusive. While writing (push), sp must be between 0 and 511, inclusive.
Stack memory is persistent on logic chips. This means that if you have a logic chip and push values to the stack, the code that pushes those values can be removed and the stack will retain those values.
Note that this does not carry over to any other logic chips which receive the program of the original; They will need to have their stack memories programmed individually.
Stack Traversing
Traversing the stack can be done similarly to how an array would be traversed in some other languages:
#this will traverse indices {min value} through {max value}-1 move sp {min value} loop: add sp sp 1 peek r0 #do something here with your stack values (loaded into r0) blt sp {max value} loop #continue on
Alternatively, you can use the pop function's decrementing to make a more efficient loop:
move sp {max value} add sp sp 1 loop: pop r0 #do something here with your stack values (loaded into r0) bgt sp {min value} loop #continue on
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.
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, it's 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:
#Reads the 'Temperature' from an atmosphere sensor # at device port 'd0' into register 'r0'. l r0 d0 Temperature
# Writes the value of the register 'r0' to the # device on port 'd1' into the variable 'Setting'. s d1 Setting r0
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.
# defines a Constant with name 'pi' # and set its value to 3.14159 define pi 3.14159
You can use these constants like any other variables (see: alias in section Instructions). Example:
# set the value of register 'r0' to the value of constant named 'pi'. move r0 pi
Numeric values
Registers and constants are usually decimal values using double-precision floating point (confirmed?).
Unlike real CPU architectures, integers are not supported as a distinct type, but double FP can represent integers up to about 54 bits before rounding causes problems (the exact number depending what bit patterns you happen to have).
Numbers can be written in hexadecimal by preceding the value with a $ symbol. Values larger than 54 bits might get corrupted. Hex numbers are typically used for ReferenceId values.
Examples:
move r0 12345 move r1 123.456 move r2 $E1B2
Indirect referencing
This is a way of accessing a register by using another register as a pointer. Adding an additional r in front 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
Dynamically changing LogicType when interacting with Device Registers
When the l or s instructions are used to read from or write to a Device Register, the LogicType is the variable that will be interacted with (example: in l r0 myDevice Temperature the LogicType is the Temperature). In most scripts the LogicType will be hardcoded. But it's also possible to change the LogicType dynamically. The LogicType is an enum, where each type is identified by a unique integer value. This makes it possible to cycle through them in various ways, either as a list placed on the Stack, or by increasing the LogicType value via addition in each loop.
#loop through a list of LogicTypes push LogicType.RatioOxygen push LogicType.RatioVolatiles push LogicType.Temperature loop: pop r1 l r0 myDevice r1 ... bgtz sp loop #loop until the Stack is empty
Network Referencing / Channels
All cable networks have 8 Channels which can have data loaded from/stored to via a device and connection reference. Connections for each supported device are listed in the stationpedia. All 'connections' a device can make are a connection (pipe, chute, cable), but only cable networks have channels.
The 8 channels (Channel0 to Channel7) are however volatile, in that data is destroyed if any part of the cable network is changed, removed, or added to, and also whenever the world is exited. All these channels default to NaN. Strictly speaking, they default to what we would call "quiet NaN", in that its not an error it simply means its not a number yet. Recommend you use these channels for reading and writing between networks, rather than as a data store. This effectively means an IC can read all the networks for all devices to connected to it, so not just their own local network, but any networks any device they can reference is connected to.
# d0 is device zero, and the :0 refers # to that device's 0 connection l r0 d0:0 Channel0
For example: on an IC Housing, the 0 connection is the data port and 1 is power, so you could write out r0 to Channel0 of the power network of the Housing using s db:1 Channel0 r0
#read all 8 channels with a loop and #place the values in r0 to r7 move r15 LogicType.Channel0 #LogicType integer move r14 0 #pointer for indirect referencing loop: l rr14 db:0 r15 add r15 r15 1 #next channel add r14 r14 1 #next register ble r15 LogicType.Channel7 loop
Logic gates, Bitwise and Logical
All logic gates in MIPS have a bitwise behavior. The available gates are NOT, AND, OR, XOR and NOR (XNOR and NAND are missing).
In Bitwise operations, each bit is matched separately, which includes the sign bit.
To understand what is going on with bitwise operations, a little bit of computer theory is needed. In Stationeers each register uses 64 bits for integer values (a number without decimals), where the 64th bit is the sign-bit (0 for positive and 1 for negative). Since the number 0 is counted as a positive value, this gives each register a range of (2^63 - 1) to -2^63. Negative numbers also behave according to Two's complement (https://en.wikipedia.org/wiki/Two%27s_complement). Which means that a number with a sign-bit of 1 will have all of its number bits flipped as well, so that the decimal value of -1 is represented by a binary value of sixtyfour 1's.
MIPS have binary notation (https://en.wikipedia.org/wiki/Binary_number) that is activated by placing a % in front of the number. The _ characters are ignored and only used for readability.
not r0 0
# 0 = %00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
# flip all bits
# %11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111 = -1
# r0 equals -1
and r0 3 6
# 3 = %011
# 6 = %110
# only the bits in the "2" position are matching
# r0 equals %010 = 2
Logical operations can still be performed via alternative instructions. But these are not perfect substitutes, they treat negative values differently, and some can produce non-binary outputs. When using these, keep in mind that devices that wants a binary value will treat any non-binary values like this: >= 1 counts as "1" and <1 counts as "0".
# r0 = result
# r1 = input A
# r2 = input B
Logical NOT = seqz r0 r1
Logical AND = min r0 r1 r2
Logical OR = max r0 r1 r2
Logical XOR = sne r0 r1 r2 (only for binary inputs)
Logical NAND = not and
Logical NOR = not or
Logical XNOR = not xor
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 unless the housing is an Air Conditioner. 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 number of your choice, like the line number.
This example will display the number 97 on the IC housing.
s db Setting 97 # sets/writes the number 97 into the parameter Setting of the IC Housing(db)
With this trick you can trace your program as if you were using breakpoints:
... some code... jal debug ...some more code... jal debug ...rest of the code.... debug: s db Setting ra # print stored line number s db On 0 # stop execution. Turn the IC housing back manually on to proceed from there j ra # return to normal execution
Always use unique names for labels. When a label is named after a IC10 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 IC10
IC10 can feel overwhelming to beginners due to the large number of available instructions. Fortunately, only a small subset is actually needed to start writing useful scripts. All instructions and variables can be viewed in more detail in the IC editor inside the game, by clicking the "f", "x", and "s(x)" buttons in the top-right corner. The Stationpedia is also an essential resource for scripting, as it lists the logic values available to each device.
20 useful instructions for IC10 beginners
alias assigns a readable name to a register or device, completely optional but makes the script much easier to read
label: acts like a tag for jump instructions
yield pause at this line for 1-tick and then resume, if not used the script will automatically pause for 1-tick after executing 128 lines
j labelName/ra jump to a label, or the value in register ra (return address) and continue executing from that line
jal labelName stores the next line number into register ra, then jump to labelName
beq branch if equal, compares two values and jumps to a label if they are equal
bne branch if not equal, compares two values and jumps to a label if they are different
bgt branch if greater than, compares two values and jumps to a label if the first is greater than the second
blt branch if less than, compares two values and jumps to a label if the first is less than the second
The suffix -al can be added to each of these instructions (example: beqal) to store the next line number in the "return address" register.
l loads a logic value from a device, example: the Temperature from a gas sensor
lb loads a batch of logic values from many identical devices and returns the average/sum/minimum/maximum value
ls loads a slot logic value, example: the Quantity of a filter slot shows a fitted filters remaining % value
s stores a logic value to a device, example: the On value will turn the device on and off
sb stores a batch of logic values to many identical devices, example: Horizontal will rotate all connected Solar Panels
move copy a value to a register
add addition of two values
sub subtraction of two values
mul multiplication of two values
div division of two values
seqz set if equal to zero, commonly used as a not-gate since it turns 0 into 1, and 1 into 0
It's important to know how scripts works in general in order to write them. All scripts begin by executing line 0, and then the line below etc, until a total of 128 lines has been executed, regardless if they have any code or not. Most scripts are written as loops by using a jump instruction, this allows the script to repeat the same tasks forever. By using a yield instruction, the script will always start from the same place every time, allowing it to load in fresh data values instead of accidentally using old data.
A script based on a modular programming style, will break up its various tasks into different subroutines and use a main loop to call these subroutines. This is a good way to organize a script where the subroutines work independently from each other. It's commonly used when the script handles multiple independent tasks. A greenhouse script could simultaneously control the temperature, filter out oxygen produced by the plants, pump in CO2 as needed, turn grow lights on and off based on a timer, and maybe even use a LArRE for harvesting and planting, all at the same time.
A script based on a state based programming style, will be written like a chain of sequences that follow after each other. This style allows a script to move into different states, where each state will perform different tasks, before transitioning to the next state in the chain. This style is useful for things like airlocks, the AIMEe mining bot, and mining rockets.
When writing a script, knowledge about a process or a device, is often more valuable than knowing lots of IC10 instructions. Someone that knows that Grow Lights are only 80% efficient, will increase the light time accordingly to prevent illumination stress to build up over time. This is important knowledge when automating a greenhouse for non-perennial plants, but it's not something that can be learned by memorizing IC10 instructions.
Accessing devices via batch or ReferenceId
The IC housing has 6 pins you can use to configure the devices it uses. This provides flexibility to let the installer configure which devices will be controlled by the IC.
Alternatives for accessing devices include the batch load/store and the ReferenceId load/store instructions.
# get the average charge ratio across station batteries lb r0 HASH("StructureBattery") Ratio Average
# get the ReferenceId for the sorter named "Sorter Corn" lbn r1 HASH("StructureLogicSorter") HASH("Sorter Corn") ReferenceId Maximum ble r1 ninf ra #use the ReferenceId to set that sorter's mode. sd r1 Mode 1
Using the 6 configuration pins makes it easy to write reusable MIPS scripts where the installer uses the pins to select the devices that will be managed.
Using batch-name instructions frees you from the hassle of adjusting the pins, but requires you to name the devices via the Labeller. It can also allow you to control more than 6 devices.
Batch instructions
The batch instructions can address multiple devices only via their PrefabHash generated from the prefab name using the `HASH("Name")` macro or copied directly from the Stationpedia. A prefab hash is always an integer. All devices that can be read with logic contain the logic value PrefabHash and NameHash.
See Batched instructions for a comprehensive list of all batch instructions.
sb, sbn, sbs, (no sbns)
lb, lbs, lbn, lbns
Direct reference instructions
Direct reference instructions can address a specific device via its ReferenceId.
clrd, getd, putd,
ld, sd, (no slot access via reference ID)
Internal Stack Programming
The following devices support Stack instructions: Logic Sorter, Autolathe, Electronics Printer, Hydraulic Pipe Bender, Tool Manufactory, Security Printer and Rocket Manufactory. The Medium Satellite Dish also supports Stack instructions, but it's only used to see the traders buy and sell data before they land; see the Satellite Tracking page for details.
Stack instructions are encoded integer values that these devices can interpret as commands. Instead of controlling them directly through an IC10 script, they can be controlled by placing certain values on their Stacks. Each instruction is stored as a single number whose bits encode both an operational code (OP code) and any parameters required by that operation. In other words, each Stack instruction is a number whose bits are divided into fields, where each field stores a different piece of information.
Although Stack instructions are stored as integers, they are best interpreted as binary values to show the individual bits. Binary numbers have their least significant bit on the right side. The lowest 8 bits always refers to the bits on the right side, and they are always reserved for the OP code that determines what type of instruction it is.
Example: constructing a Stack instruction that filters out Iron Ore with a Logic Sorter
Always begin by looking at the Stationpedia to see the available OP codes and what their bit fields represent. The Logic Sorter has two OP codes that can be used to sort individual items: SorterInstruction.FilterPrefabHashEquals (OP code = 1) and SorterInstruction.FilterPrefabHashNotEquals (OP code = 2). These instructions use the same layout: the lowest 8 bits contain the OP code and the next 32 bits contain the Prefab Hash of the item being filtered. To combine the OP code and the Hash into a single value, the item Hash must first be moved into its expected field (between bits 8 to 39). This is done by bit shifting it 8 positions to the left, afterwards the OP code can be placed into its expected field (bits 0 to 7).
FilterPrefabHash instruction format = %[32-bit prefab hash][8-bit OP code]
Prefab Hash for ItemIronOre = 1758427767
ItemIronOre as a binary value = %01101000_11001111_01111111_10010111
Bit shifted 8 positions to the left = %01101000_11001111_01111111_10010111_00000000
OP code 1 = %00000001
Combine OP code and bit shifted Hash = %01101000_11001111_01111111_10010111_00000001
The Stack instruction is complete and the value can be moved to the Logic Sorters Stack. The IC10 for doing that could look like:
alias LogicSorter d0 sll r0 HASH("ItemIronOre") 8 # bit-shift the hash value 8 times to the left or r0 r0 SorterInstruction.FilterPrefabHashEquals # combine bit shifted hash and OP code put LogicSorter 0 r0 # write the instruction to Stack at position 0
The finalized Stack instruction can be constructed in various ways. Rocketwerkz demonstrated this with sll and or, which made that choice popular. The same result can also be obtained with mul (multiplying by 256 is equivalent to 8 left bit shifts) and add, or with ins to manipulate the bit fields directly (but limited to the first 52 bits of the total 64).
For Logic Sorters the Mode value is also important. It can be either All (0), Any (1), or None (2), with All being the default value. All means every Stack instruction must evaluate as a match, Any means at least one must evaluate as a match, and None means that every instruction must fail to be a match. When sorting two or more different items, the Mode needs to be changed. To reverse the output sides for a Logic Sorter when using SorterInstruction.FilterPrefabHashEquals (OP code 1), change the Mode between Any (1) and None (2).
When printers receive a PrinterInstruction.ExecuteRecipe they will immediately start printing, and the instruction's quantity value will decrease for each item produced. An instruction with a quantity of zero will not print anything. If the printing is halted due to missing reagents, the printer will proceed to the next instruction (unless it's preceded by a PrinterInstruction.WaitUntilNextValid ). When a printer is idle it will loop through its Stack, which causes any unfinished printing jobs will be resumed as soon as reagents become available.
Some printer instructions should never be written to the Stack, because they are already present at fixed memory addresses. The PrinterInstruction.StackPointer is stored at stack address 63 and its value contains the current stack pointer (8 bits for the OP code and 16 bits for the pointer). The PrinterInstruction.MissingRecipeReagent entries occupy Stack addresses 54 to 62 (starting at 54). These addresses will only be populated when a PrinterInstruction.WaitUntilNextValid instruction precedes an PrinterInstruction.ExecuteRecipe instruction that is missing the required reagents. The hash values obtained from PrinterInstruction.MissingRecipeReagent can be converted into ingot hashes, first by using sra to isolate the reagent hash value while preserving its sign bit, and then with rmap.
Example: automatically print 50 Cable Coils
alias printer d0 move r0 PrinterInstruction.ExecuteRecipe ins r0 50 8 8 # insert quantity 50 starting at the 8th bit ins r0 HASH("ItemCableCoil") 16 32 # insert the item Hash starting at the 16th bit put printer 0 PrinterInstruction.WaitUntilNextValid # required to populate missing reagents put printer 1 r0 # ExecuteRecipe instruction placed after WaitUntilNextValid # if the printer runs out of copper, an external IC10 script can detect which ingot to send get r0 printer 54 # read the Stack address for the first missing reagent sra r1 r0 16 # remove the OPcode and quantity while preserving the sign bit rmap r2 printer r1 # convert the reagent hash into the corresponding ingot hash
Instructions
Utility
- §
alias str r?|d?
Labels register or device reference with name, device references also affect what shows on the screws on the IC base.
alias dAutoHydro1 d0 alias vTemperature r0
- §
define str num
Creates a label that will be replaced throughout the program with the provided value.
define ultimateAnswer 42 move r0 ultimateAnswer # Store 42 in register 0
- §
move r? a(r?|num)
Register = provided num or register value.
move r0 42 # Store 42 in register 0
- §
yield
Pauses execution for 1 tick
- §
sleep a(r?|num)
Pauses execution on the IC for a seconds
- §
hcf
Halt and catch fire
Creates a small explosion, destroying the chip and starting fires on flammable atmospheres.
Mathematical
- §
abs r? a(r?|num)
Register = the absolute value of a
define negativeNumber -10 abs r0 negativeNumber # Compute the absolute value of -10 and store it in register 0
- §
sgn r a(r?|num)
Stores the sign of a in the register: -1 if a is negative, 1 if positive, 0 if a is 0 (or not a number).
- §
add r? a(r?|num) b(r?|num)
Register = a + b.
add r0 r0 1 # increment r0 by one
define num1 10 define num2 20 add r0 num1 num2 # Add 10 and 20 and store the result in register 0
- §
ceil r? a(r?|num)
Register = smallest integer greater than a
define floatNumber 10.3 ceil r0 floatNumber # Compute the ceiling of 10.3 and store it in register 0
- §
div r? a(r?|num) b(r?|num)
Register = a / b
- §
pow r? a(r?|num) b(r?|num)
Stores the result of raising a to the power of b in the register. Follows IEEE-754 standard for floating point arithmetic.
- §
exp r? a(r?|num)
exp(a) or e^a
- §
floor r? a(r?|num)
Register = largest integer less than a
- §
log r? a(r?|num)
base e log(a) or ln(a)
- §
max r? a(r?|num) b(r?|num)
Register = max of a or b
If any of the values is NaN, NaN is returned.
- §
min r? a(r?|num) b(r?|num)
Register = min of a or b
If any of the values is NaN, NaN is returned.
- §
clamp r? a(r?|num) min(r?|num) max(r?|num)
Stores a clamped to the inclusive inclusive range [min, max] in the register provided.
Functionally equivalent to min(max(a,min),max).
If any of the values are NaN, NaN is returned.
clamp 100 10 50 = 50
clamp 0 10 50 = 10
clamp 20 10 50 = 20
- §
mod r? a(r?|num) b(r?|num)
Register = a mod b (note: NOT a % b)
mod r0 10 20 # Expected: r0=10 mod r1 22 20 # Expected: r1 = 2 mod r2 22 -20 # Expected: r2 = 18 mod r2 22 -10 # Expected: r2 = 18 mod r2 -7 4 # Expected: r2 = 1 mod r2 -7 9 # Expected: r2 = 2
- §
mul r? a(r?|num) b(r?|num)
Register = a * b
- §
rand r?
Register = a random value x with 0 <= x < 1
- §
round r? a(r?|num)
Register = a rounded to nearest integer
- §
sqrt r? a(r?|num)
Register = square root of a
- §
sub r? a(r?|num) b(r?|num)
Register = a - b.
- §
trunc r? a(r?|num)
Register = a with fractional part removed
- §
lerp r? a(r?|num) b(r?|num) c(r?|num)
Linearly interpolates between a and b by the ratio c, and places the result in the register provided. The ratio c will be clamped between 0 and 1.
Mathematical / Trigonometric
- §
acos r? a(r?|num)
Returns the angle (radians) whos cos is the specified value
- §
asin r? a(r?|num)
Returns the angle (radians) whos sine is the specified value
- §
atan r? a(r?|num)
Returns the angle (radians) whos tan is the specified value
- §
atan2 r? a(r?|num) b(r?|num)
Returns the angle (radians) whose tangent is the quotient of two specified values: a (y) and b (x)
- §
cos r? a(r?|num)
Returns the cosine of the specified angle (radians)
- §
sin r? a(r?|num)
Returns the sine of the specified angle (radians)
- §
tan r? a(r?|num)
Returns the tan of the specified angle (radians)
Stack
- §
clr d?
Clears the stack memory for the provided device.
- §
clrd id(r?|num)
Seeks directly for the provided device id and clears the stack memory of that device
- §
get r? device(d?|r?|id) address(r?|num)
Using the provided device, attempts to read the stack value at the provided address, and places it in the register.
- §
getd r? id(r?|id) address(r?|num)
Seeks directly for the provided device id, attempts to read the stack value at the provided address, and places it in the register.
- §
peek r?
Register = the value at the top of the stack
- §
poke address(r?|num) value(r?|num)
Stores the provided value at the provided address in the stack.
- §
pop r?
Register = the value at the top of the stack and decrements sp
- §
push a(r?|num)
Pushes the value of a to the stack at sp and increments sp
- §
put device(d?|r?|id) address(r?|num) value(r?|num)
Using the provided device, attempts to write the provided value to the stack at the provided address.
- §
putd id(r?|id) address(r?|num) value(r?|num)
Seeks directly for the provided device id, attempts to write the provided value to the stack at the provided address.
Slot/Logic
- §
l r? device(d?|r?|id) logicType
Loads device LogicType to register by housing index value.
Read from the device on d0 into register 0
l r0 d0 Setting
Read the pressure from a sensor
l r1 d5 Pressure
This also works with aliases. For example:
alias Sensor d0 l r0 Sensor Temperature
- §
lr r? device(d?|r?|id) reagentMode int
Loads reagent of device's ReagentMode where a hash of the reagent type to check for. ReagentMode can be either Contents (0), Required (1), Recipe (2). Can use either the word, or the number.
- §
ls r? device(d?|r?|id) slotIndex logicSlotType
Loads slot LogicSlotType on device to register.
Read from the second slot of device on d0, stores 1 in r0 if it's occupied, 0 otherwise.
ls r0 d0 2 Occupied
And here is the code to read the charge of an AIMeE:
alias robot d0 alias charge r10 ls charge robot 0 Charge
- §
s device(d?|r?|id) logicType r?
Stores register value to LogicType on device by housing index value.
s d0 Setting r0
- §
ss device(d?|r?|id) slotIndex logicSlotType r?
Stores register value to device stored in a slot LogicSlotType on device.
- §
rmap r? d? reagentHash(r?|num)
Given a reagent hash (signed 32bit int), store the corresponding prefab hash that the device expects to fulfill the reagent requirement. For example, on an autolathe, the hash for Iron will store the hash for ItemIronIngot.
Slot/Logic / Batched
- §
lb r? deviceHash logicType batchMode
Loads LogicType from all output network devices with provided type hash using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
lb r0 HASH("StructureWallLight") On Sum
- §
lbn r? deviceHash nameHash logicType batchMode
Loads LogicType from all output network devices with provided type and name hashes using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
- §
lbns r? deviceHash nameHash slotIndex logicSlotType batchMode
Loads LogicSlotType from slotIndex from all output network devices with provided type and name hashes using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
- §
lbs r? deviceHash slotIndex logicSlotType batchMode
Loads LogicSlotType from slotIndex from all output network devices with provided type hash using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
- §
sb deviceHash logicType r?
Stores register value to LogicType on all output network devices with provided type hash.
sb HASH("StructureWallLight") On 1
- §
sbn deviceHash nameHash logicType r?
Stores register value to LogicType on all output network devices with provided type hash and name.
- §
sbs deviceHash slotIndex logicSlotType r?
Stores register value to LogicSlotType on all output network devices with provided type hash in the provided slot.
Bitwise
- §
and r? a(r?|num) b(r?|num)
Performs a bitwise logical AND operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If both bits are 1, the resulting bit is set to 1. Otherwise the resulting bit is set to 0.
- §
nor r? a(r?|num) b(r?|num)
Performs a bitwise logical NOR (NOT OR) operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If both bits are 0, the resulting bit is set to 1. Otherwise, if at least one bit is 1, the resulting bit is set to 0.
- §
not r? a(r?|num)
Performs a bitwise logical NOT operation flipping each bit of the input value, resulting in a binary complement. If a bit is 1, it becomes 0, and if a bit is 0, it becomes 1.
This is a bitwise operation, the NOT of 1 => -2, etc. You may want to use seqz instead
- §
or r? a(r?|num) b(r?|num)
Performs a bitwise logical OR operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If either bit is 1, the resulting bit is set to 1. If both bits are 0, the resulting bit is set to 0.
- §
sla r? a(r?|num) b(r?|num)
Performs a bitwise arithmetic left shift operation on the binary representation of a value. It shifts the bits to the left and fills the vacated rightmost bits with zeros (note that this is indistinguishable from 'sll').
- §
sll r? a(r?|num) b(r?|num)
Performs a bitwise logical left shift operation on the binary representation of a value. It shifts the bits to the left and fills the vacated rightmost bits with zeros.
- §
sra r? a(r?|num) b(r?|num)
Performs a bitwise arithmetic right shift operation on the binary representation of a value. It shifts the bits to the right and fills the vacated leftmost bits with a copy of the sign bit (the most significant bit).
- §
srl r? a(r?|num) b(r?|num)
Performs a bitwise logical right shift operation on the binary representation of a value. It shifts the bits to the right and fills the vacated leftmost bits with zeros
- §
rol r? a(r?|num) b(r?|num)
Performs a bitwise left rotation on the binary representation of a by b places, wrapping the bits shifted out of the most significant position back into the least significant position.
rol $DEADBEEFCAFE2000 16 -> $BEEFCAFE2000DEAD
- §
ror r? a(r?|num) b(r?|num)
Performs a bitwise right rotation on the binary representation of A by B places, wrapping the bits shifted out of the least significant position back into the most significant position.
ror $1234, 4 -> $4000000000000123
- §
xor r? a(r?|num) b(r?|num)
Performs a bitwise logical XOR (exclusive OR) operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If the bits are different (one bit is 0 and the other is 1), the resulting bit is set to 1. If the bits are the same (both 0 or both 1), the resulting bit is set to 0.
- §
ext r? source(r?|num) offset(r?|num) length(r?|num)
Extracts a bit field from source value, beginning at bit offset for length bits and places result in the provided register. Payload cannot exceed 53 bits in final length.
- §
ins r? field(r?|num) offset(r?|num) length(r?|num)
Inserts a bit field into the provided register, beginning at bit offset for length bits. Payload cannot exceed 53 bits in final length. NOTE: As of 2026-01-12, stable version has a bug with parameter order (uses offset-length-field instead of field-offset-length). Beta version has correct order.
move r0 $DE0000EF move r1 $ADBE ins r0 r1 8 16 #inserts field r1 at bit 8 for 16 bits, result: $DEADBEEF
Comparison
- §
select r? a(r?|num) b(r?|num) c(r?|num)
Register = b if a is non-zero, otherwise c
This operation can be used as a simple ternary condition
1)
move r0 0
select r1 r0 10 200
move r0 0 select r1 r0 10 200
after run, r1 = 200
2)
move r0 5
select r1 r0 10 200
move r0 1 select r1 r0 10 100
after run,
Comparison / Device Pin
- §
sdns r? device(d?|r?|id)
Register = 1 if device is not set, otherwise 0
- §
sdse r? device(d?|r?|id)
Register = 1 if device is set, otherwise 0.
Comparison / Value
- §
sap r? a(r?|num) b(r?|num) c(r?|num)
Register = 1 if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8), otherwise 0
Set register to 1 if a and b are close enough to each other with the scaling factor of c. Equivalent to Python math.isclose
- §
sapz r? a(r?|num) b(r?|num)
Register = 1 if abs(a) <= max(b * abs(a), float.epsilon * 8), otherwise 0
- §
seq r? a(r?|num) b(r?|num)
Register = 1 if a == b, otherwise 0
- §
seqz r? a(r?|num)
Register = 1 if a == 0, otherwise 0
- §
sge r? a(r?|num) b(r?|num)
Register = 1 if a >= b, otherwise 0
- §
sgez r? a(r?|num)
Register = 1 if a >= 0, otherwise 0
- §
sgt r? a(r?|num) b(r?|num)
Register = 1 if a > b, otherwise 0
- §
sgtz r? a(r?|num)
Register = 1 if a > 0, otherwise 0
- §
sle r? a(r?|num) b(r?|num)
Register = 1 if a <= b, otherwise 0
- §
slez r? a(r?|num)
Register = 1 if a <= 0, otherwise 0
- §
slt r? a(r?|num) b(r?|num)
Register = 1 if a < b, otherwise 0
- §
sltz r? a(r?|num)
Register = 1 if a < 0, otherwise 0
- §
sna r? a(r?|num) b(r?|num) c(r?|num)
Register = 1 if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8), otherwise 0
- §
snan r? a(r?|num)
Register = 1 if a is NaN, otherwise 0
- §
snanz r? a(r?|num)
Register = 0 if a is NaN, otherwise 1
- §
snaz r? a(r?|num) b(r?|num)
Register = 1 if abs(a) > max(b * abs(a), float.epsilon), otherwise 0
- §
sne r? a(r?|num) b(r?|num)
Register = 1 if a != b, otherwise 0
- §
snez r? a(r?|num)
Register = 1 if a != 0, otherwise 0
Branching
- §
j int
Jump execution to line a
j 0 # jump line 0
j label # jump to a label label: # your code here
- §
jal int
Jump execution to line a and store next line number in ra
jal provides a way to do function calls in IC10 mips
move r0 1000 move r1 0 start: jal average s db Setting r0 yield j start average: add r0 r0 r1 div r0 r0 2 j ra # jump back
- §
jr int
Relative jump to line a
Branching / Device Pin
- §
bdnvl device(d?|r?|id) logicType a(r?|num)
Will branch to line a if the provided device not valid for a load instruction for the provided logic type.
- §
bdnvs device(d?|r?|id) logicType a(r?|num)
Will branch to line a if the provided device not valid for a store instruction for the provided logic type.
- §
bdns d? a(r?|num)
Branch to line a if device d isn't set
- §
bdnsal d? a(r?|num)
Jump execution to line a and store next line number if device is not set
- §
bdse d? a(r?|num)
Branch to line a if device d is set
- §
bdseal d? a(r?|num)
Jump execution to line a and store next line number if device is set
#Store line number and jump to line 32 if d0 is assigned. bdseal d0 32
#Store line in ra and jump to label HarvestCrop if device d0 is assigned. bdseal d0 HarvestCrop
- §
brdns d? a(r?|num)
Relative branch to line a if device is not set
- §
brdse d? a(r?|num)
Relative branch to line a if device is set
Branching / Comparison
- §
bap a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8)
Branch if a and b are close enough to each other with the scaling factor of c. Equivalent to Python math.isclose
- §
brap a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Relative branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8)
- §
bapal a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Branch to line c if a != b and store next line number in ra
- §
bapz a(r?|num) b(r?|num) c(r?|num)
Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)
- §
brapz a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)
- §
bapzal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8) and store next line number in ra
- §
beq a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a == b
- §
breq a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if a == b
- §
beqal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a == b and store next line number in ra
- §
beqz a(r?|num) b(r?|num)
Branch to line b if a == 0
- §
breqz a(r?|num) b(r?|num)
Relative branch to line b if a == 0
- §
beqzal a(r?|num) b(r?|num)
Branch to line b if a == 0 and store next line number in ra
- §
bge a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a >= b
- §
brge a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if a >= b
- §
bgeal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a >= b and store next line number in ra
- §
bgez a(r?|num) b(r?|num)
Branch to line b if a >= 0
- §
brgez a(r?|num) b(r?|num)
Relative branch to line b if a >= 0
- §
bgezal a(r?|num) b(r?|num)
Branch to line b if a >= 0 and store next line number in ra
- §
bgt a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a > b
An example of a Schmitt trigger, turning on a device if the temperature is too low, and turning it off if it's too high and finally doing nothing if the temperature is within the desired range.
alias sensor d0 alias device d1 define mintemp 293.15 define maxtemp 298.15 start: yield l r0 sensor Temperature # If the temperature < mintemp, turn on the device blt r0 mintemp turnOn # If the temperature > maxtemp, turn off the device bgt r0 maxtemp turnOff j start turnOn: s device On 1 j start turnOff: s device On 0 j start
- §
brgt a(r?|num) b(r?|num) c(r?|num)
relative branch to line c if a > b
- §
bgtal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a > b and store next line number in ra
- §
bgtz a(r?|num) b(r?|num)
Branch to line b if a > 0
- §
brgtz a(r?|num) b(r?|num)
Relative branch to line b if a > 0
- §
bgtzal a(r?|num) b(r?|num)
Branch to line b if a > 0 and store next line number in ra
- §
ble a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a <= b
- §
brle a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if a <= b
- §
bleal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a <= b and store next line number in ra
- §
blez a(r?|num) b(r?|num)
Branch to line b if a <= 0
- §
brlez a(r?|num) b(r?|num)
Relative branch to line b if a <= 0
- §
blezal a(r?|num) b(r?|num)
Branch to line b if a <= 0 and store next line number in ra
- §
blt a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a < b
An example of a Schmitt trigger, turning on a device if the temperature is too low, and turning it off if it's too high and finally doing nothing if the temperature is within the desired range.
alias sensor d0 alias device d1 define mintemp 293.15 define maxtemp 298.15 start: yield l r0 sensor Temperature # If the temperature < mintemp, turn on the device blt r0 mintemp turnOn # If the temperature > maxtemp, turn off the device bgt r0 maxtemp turnOff j start turnOn: s device On 1 j start turnOff: s device On 0 j start
- §
brlt a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if a < b
- §
bltal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a < b and store next line number in ra
- §
bltz a(r?|num) b(r?|num)
Branch to line b if a < 0
- §
brltz a(r?|num) b(r?|num)
Relative branch to line b if a < 0
- §
bltzal a(r?|num) b(r?|num)
Branch to line b if a < 0 and store next line number in ra
- §
bna a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Branch to line d if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8)
- §
brna a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Relative branch to line d if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8)
- §
bnaal a(r?|num) b(r?|num) c(r?|num) d(r?|num)
Branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8) and store next line number in ra
- §
bnan a(r?|num) b(r?|num)
Branch to line b if a is not a number (NaN)
- §
brnan a(r?|num) b(r?|num)
Relative branch to line b if a is not a number (NaN)
- §
bnaz a(r?|num) b(r?|num) c(r?|num)
Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8)
- §
brnaz a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if abs(a) > max(b * abs(a), float.epsilon * 8)
- §
bnazal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8) and store next line number in ra
- §
bne a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a != b
- §
brne a(r?|num) b(r?|num) c(r?|num)
Relative branch to line c if a != b
- §
bneal a(r?|num) b(r?|num) c(r?|num)
Branch to line c if a != b and store next line number in ra
- §
bnez a(r?|num) b(r?|num)
branch to line b if a != 0
- §
brnez a(r?|num) b(r?|num)
Relative branch to line b if a != 0
- §
bnezal a(r?|num) b(r?|num)
Branch to line b if a != 0 and store next line number in ra
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 |
| -nan | if a == NaN | bnan | brnan | snan | |
| -nanz | if a != NaN | snanz | |||
| -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.
https://en.wikipedia.org/wiki/Machine_epsilon
Example:
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.
Data Network
This is a list of the colors used by various items with the logic network. These are the same colors that can be applied to physical items with a Spray Can. These codes can be used in IC10 scripts or logic circuits to set the color of various components, such as lights.
| Value | Color | Hex |
|---|---|---|
| 0 | Blue | #212AA5 |
| 1 | Gray | #7B7B7B |
| 2 | Green | #3F9B39 |
| 3 | Orange | #FF662B |
| 4 | Red | #E70200 |
| 5 | Yellow | #FFBC1B |
| 6 | White | #E7E7E7 |
| 7 | Black | #080908 |
| 8 | Brown | #633C2B |
| 9 | Khaki | #63633F |
| 10 | Pink | #E41C99 |
| 11 | Purple | #732CA7 |
- 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.
- ReferenceId
- Unique Identifier of a Device, this value is different for every device in a save.
- 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 (exceptions exist such as filtration units) 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 seedsls vOccupied dThisVictim 2 Occupied #stores 1 in vOccupied if dThisVictim has more seeds
- OccupantHash
- Quantity
- Damage
- Efficiency
- FilterType
Reports the type of filter installed in the slot (e.g. in a Filtration unit).
- Return Values: 1: Oxygen, 2: Nitrogen, 4: Carbon Dioxide, 8: Volatiles, 16: Pollutants, 32: Water, 64: Nitrous Oxide, 16384: Hydrogen, 65536: Polluted Water, 131072: Hydrazine, 524288: Alcohol, 1048576: Helium, 2097152: Liquid Sodium Chloride, 4194304: Silanol, 16777216: Hydrochloric Acid, 67108864: Ozone
ls r0 db 0 FilterType
- 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 cropls vMature dThisVictim 0 Mature # Store 1 in vMature if dThisVictim has a mature crop- ReferenceId
- Unique Identifier of a Device, this value is different for every device in a save.
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
#2 Axis Solar Tracking adapted from CowsAreEvil. #Place all panels in uniform manner. #Set one to 15 Vertical(Min value). 0 Horizontal. #Take note direction panel faces. #Place daylight sensor flat pointing in the direction #the panel now faces. (Cable port facing opposite) #Alias the sensor to d0 alias sensor d0 # define the Panel variants define Heavy -934345724 define HeavyDual -1545574413 define Solar -2045627372 define SolarDual -539224550 start: yield #Check for daylight. l r0 sensor Activate beqz r0 reset #Read the Horizontal data. l r0 sensor Horizontal #Set batch to the panels. sb Heavy Horizontal r0 sb HeavyDual Horizontal r0 sb Solar Horizontal r0 sb SolarDual Horizontal r0 #Read the Vertical data and subtract 90 l r0 sensor Vertical sub r0 90 r0 #Set batch to the panels. sb Heavy Vertical r0 sb HeavyDual Vertical r0 sb Solar Vertical r0 sb SolarDual Vertical r0 j start reset: yield sb Heavy Horizontal 270 #Edit this to face sunrise. sb HeavyDual Horizontal 270 #Edit this sb Solar Horizontal 270 #Edit this sb SolarDual Horizontal 270 #Edit this sb Heavy Vertical 0 sb HeavyDual Vertical 0 sb Solar Vertical 0 sb SolarDual Vertical 0 sleep 10 j start
IC10 Schmitt Trigger - Sensor Range Based Device Toggle
This script demonstrates the IC10 style Schmitt Trigger Pattern in basic examples using the select instruction with its ternary condition to toggle a device On state based on a range of sensor readings. Examples for both styles are provided below, i.e. 'cooling' or 'heating', for 'below min' or 'greater than max' style range-based toggle. A temperature based control is shown in the examples, but any sensor reading could be used to dynamically toggle a device On state such pressure, charge ratio, solar angle, etc.
# ---- IC10 Schmitt Trigger ---- # -- Standard - i.e. "cooling" #When the temperature sensor device reports a value # higher than TempMax and the valve is not On, the # valve will turn On. #When the valve is On, and the sensor device reports # higher than TempMin, the valve will remain on. #Once that sensor reports a temperature lower than # TempMin, the valve will once again shut off and # this cycle will continue indefinitely: define TempMax 296.15 #23C define TempMin 283.15 #10C alias Valve d0 alias Sensor d1 example1: yield l r0 Valve On select r0 r0 TempMin TempMax l r1 Sensor Temperature sgt r0 r1 r0 s Valve On r0 j example1 # ---- IC10 Schmitt Trigger ---- # -- Inverted - i.e. "heating" #When the temperature sensor device reports a value # lower than TempMin and the valve is not On, the # valve will turn On. #When the valve is On, and the sensor device reports # lower than TempMax, the valve will remain on. #Once that sensor reports a temperature higher than # TempMax, the valve will once again shut off and # this cycle will continue indefinitely: define TempMax 296.15 #23C define TempMin 283.15 #10C alias Valve d0 alias Sensor d1 example2: yield l r0 Valve On select r0 r0 TempMax TempMin l r1 Sensor Temperature slt r0 r1 r0 s Valve On r0 j example2
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.
Push & Pop return address when calling multiple levels of functions
More advanced scripts, or scripts that wish to be more generic, may want to allow calling more than one level of function. Allowing this requires pushing the current ra register before calling the function, then popping ra back afterward.
For example, imagine that the main loop of the code wants to call function orientPanelsToStar, which would calculate the panels' orientations, then place them in r0 and r1, and then in turn itself call orientPanelsTo, which would set the orientations of all panels based on the precomputed values of r0 and r1. Doing so requires orientPanelsToStar to push ra before calling orientPanelsTo, as show in the code below.
orientPanelsToStar: # Save return address set by the 'jal' instruction push ra # ...Calculate panels' orientation, for example leaving the results in r0 and r1... # Now call orientPanelsTo to actually set the panels' orientation # based on the computed values of r0 and r1. jal orientPanelsTo # ...Call other functions here if desired... # Restore the return address of orientPanelsToStar itself pop ra # Return to caller j ra ########## orientPanelsTo: # ...Actually set panels' orientation... # Return to caller j ra
This code would behave incorrectly if push ra and pop ra were not present: within orientPanelsToStar, doing jal orientPanelsTo would replace the current value of the ra register, permanently erasing where orientPanelsToStar itself should jump back to once done. Pushing and popping ra effectively saves its value until we need it again.
(A tempting but wrong approach to 'saving' ra would be to move it within a different register (e.g. r15) before calling orientPanelsTo, however that only permits two levels of functions, since if orientPanelsTo itself wants to call another function, it would not be able to use r15 to save its ra register since r15 already saves orientPanelsToStar's return address. Just pushing/popping ra fixes all issues, allowing for a ginormous maximum function call depth of 512!)
As a last note, if the script will push/pop values like ra, starting the script by clearing the stack (which where push/pop move the data to) by running clr db is advisable, unless the IC10 chip is not inserted inside an IC Housing (e.g. inserted in the slot of an Air Conditioner), since clr db will cause an error in this case. To do so, run clr db before the script's main loop.
Links
- Stationeers online IC10 Emulators so you can develop your code without repeatedly dying in game
- [1] Stationeers Code Simulator
- [2] Stationeers IC10 Editor & Emulator - A feature packed code editor for Stationeers IC10 code, paired with a robust debugger and emulator. Edit, test, and share code.
- [3] Stationeering provides a simulation of the IC10 chip inside Stationeers. IDE with error checking, full visibility of stack and registers.
- [4] EASy68K is a 68000 Structured Assembly Language IDE.
- [5] syntax highlighting for IC10 MIPS for Visual Studio Code (updated Feb 10th 2022)
- [6] syntax highlighting for IC10 MIPS for KDE kwrite/kate text editor
- [7] syntax highlighting for IC10 MIPS for Notepad++
- [8] syntax highlighting for IC10 MIPS for Notepad++ (updated: 11/08/2022)
- [9] syntax highlighting for IC10 MIPS for Notepad++ (updated: 23/03/2024)
- [10] Repo with a lot of code examples