Mindcode has a built-in processor emulator which allows running the compiled code or an entire schematic in a simulated environment. This emulator is available both to the web application and to the command-line tool. The emulated processor is much faster than Mindustry Logic processors; the speed of instruction executions is only limited by the speed of the computer.
At the end of the run, the output generated by the processor (or processors in case of schematic emulation) is written to the message log. Processors are identified by their IDs (see the processor-id compiler option), or, if an ID is not set, by their position.
The emulation is frame-based, and all instructions, as well as instruction scheduling and time-based variables (such as @time and @tick), are emulated according to the emulation target specified by compiler options. For a list of options affecting the emulation, see Emulator options.
When running a compiled schematic in the emulator, the linked blocks are set up as specified by the schematic. When running only the compiled code, independent of a schematic, the following blocks are created automatically:
- memory cells
cell1tocell9, - memory banks
bank1tobank9, - large logic displays
display1todisplay4, - logic displays
display5todisplay9. - message blocks
message1tomessage9.
At the beginning of the executions all the memory cells and memory banks are empty.
All constant mlog variables corresponding to the emulator target are supported. Additionally, the following built-in variables are provided:
| Variable | Target | Value or behavior |
|---|---|---|
@counter |
6.0+ | fully emulated |
@ipt |
6.0+ | current processor's ipt |
@links |
6.0+ | fully emulated |
@maph |
6.0+ | 300 |
@mapw |
6.0+ | 200 |
@minute |
7.0+ | emulated time flow |
@second |
7.0+ | emulated time flow |
@this |
6.0+ | current processor |
@thisx |
6.0+ | current processor's position in schematic |
@thisy |
6.0+ | current processor's position in schematic |
@tick |
6.0+ | emulated time flow |
@time |
6.0+ | emulated time flow |
@unit |
6.0+ | null |
@waveNumber |
7.0+ | 0 |
@waveTime |
7.0+ | 0 |
The listed instructions are emulated, either fully or with a specific implementation described in the table. Unlisted instructions are not supported and, unless specifically configured, they stop the execution of the program. Since the Mindustry World at large isn't simulated, it is not possible to meaningfully emulate these instructions.
| Instruction | Target | Implementation details |
|---|---|---|
draw |
6.0+ | ignored |
drawflush |
6.0+ | target is verified, otherwise ignored |
end |
6.0+ | |
format |
8.0+ | |
getlink |
6.0+ | |
jump |
6.0+ | |
lookup |
7.0+ | |
noop |
6.0+ | |
op |
6.0+ | |
packcolor |
7.0+ | |
print |
6.0+ | |
printchar |
8.0+ | |
printflush |
6.0+ | |
read |
6.0+ | memory banks/cells |
read |
8.0+ | logic blocks (processors), strings |
read |
8.1+ | message blocks |
select |
8.1+ | |
sensor |
6.0+ | see below |
sensor |
8.0+ | see below (added string support) |
set |
6.0+ | |
setrate |
7.0+ | |
stop |
7.0+ | |
unpackcolor |
8.1+ | |
wait |
7.0+ | see below |
write |
6.0+ | memory banks/cells |
write |
8.0+ | logic blocks (processors) |
Notes
- Printing to the text buffer is fully supported, and the text produced by the program is displayed when the execution ends.
- The overall size of the text buffer is limited to 10,000 characters.
- The
formatinstruction is fully supported. Only text produced since the lastprintflushinstructions is available for formatting. - When the contents of the text buffer encountered at the
printflushinstruction is the same as the content encountered at the previousprintflushinstruction, the text isn't generated again, just the number of the text block repetitions is output. draw printinstruction is handled like theprintflushinstructions: the text consumed by thedraw printinstruction isn't removed from the text buffer and is included in the program output.
- The
readandwriteinstructions in targets 8.0 and higher emulate access to other processor variables faithfully (non-existent variables aren't accesible). - The
sensorinstruction is supported for a limited number of properties:- Version 6+:
@dead: returnstrueif the object is null, false otherwise.@type: returns the type of the object (e.g.,@memory-cell)@name: returns the name of the object if it exists (e.g.,leadfor@lead)@id: returns the ID of the object if it exists (e.g.,1for@lead)@x,@y: returns the block position if the object is a block created from a schematic, otherwise0ornull.
- Version 8.0+:
@size,@bufferSize: returns the length when invoked on a string.
- Version 6+:
- The
waitinstruction's effect on processor scheduling and time variables is emulated, but the instruction doesn't pause the emulation.
Except the stop instruction, Mindustry processors never stop executing a program. Upon encountering an instruction which cannot be executed, the instruction simply isn't executed. For example, when the read instruction tries to read from memory which doesn't exist, it skips the execution without changing the variable which would receive the value read from the memory.
While mlog programs, and to a certain extent Mindcode programs, can sometimes put this mechanism to good use, in most cases such situations indicate a bug in the program. The processor emulator, therefore, by default, stops when encountering an irregularity. It is, however, possible to set up the emulated processor to ignore the error and continue executing the program, just like the Mindustry processor would. This configuration is done through execution flags.
Execution flags can be set through compiler directives or command-line options. Each flag can be set to true or false. The function of most flags is to stop the emulator when a corresponding condition is encountered. Some flags change the emulator behavior instead.
All flags are described in the following table:
| Flag | Default | Meaning |
|---|---|---|
| trace-execution | false | output instruction and variable states at each execution step |
| dump-variables-on-stop | true | output variable values when the 'stop' instruction is encountered |
| stop-on-stop-instruction | true | stop execution when the 'stop' instruction is encountered |
| stop-on-end-instruction | true | stop execution when the 'end' instruction is encountered |
| stop-on-program-end | true | stop execution when the end of instruction list is reached |
| err-parse-error | true | stop execution when an error or invalid instruction is encountered during parsing |
| err-invalid-counter | true | stop execution when an invalid value is written to '@counter' |
| err-unsupported-opcode | true | stop execution when an instruction unsupported by the emulator is encountered |
| err-nonexistent-var | false | stop execution when a nonexistent variable is being indirectly accessed |
| err-assignment-to-fixed-var | true | stop execution on attempts to write a value to an unmodifiable built-in variable |
| err-not-an-object | true | stop execution when a numeric value is used instead of an object |
| err-not-a-number | true | stop execution when an object is used instead of a numeric value (nulls are always permitted) |
| err-unknown-color | true | stop execution when an unknown color is used in a named color literal |
| err-invalid-character | true | stop execution when an invalid numeric value is used in the 'printchar' instruction |
| err-invalid-lookup | true | stop execution when an invalid index is used in the 'lookup' instruction |
| err-invalid-link | false | stop execution when an invalid index is used in the 'getlink' instruction |
| err-memory-access | false | stop execution when accessing invalid memory-cell or memory-bank index |
| err-memory-object | true | stop execution when attempting to store an object in external memory |
| err-unsupported-block-operation | true | stop execution when performing an unsupported operation on a block |
| err-text-buffer-overflow | false | stop execution when the text buffer size (400 characters) is exceeded |
| err-invalid-format | true | stop execution when no placeholder for the 'format' instruction exists in the buffer |
| err-graphics-buffer-overflow | true | stop execution when the graphics buffer size (256 operations) is exceeded |
| err-runtime-check-failed | true | stop execution when a compiler-generated runtime check fails. |
Some flags are false by default. It is expected that these events can happen even in an otherwise sound program. Setting them to true enforces even stricter standards in your programs.
The processor emulator provides two ways to inspect the internal state of the program.
Firstly, the content of all variables is written to the message log when the stop instruction is encountered. Therefore, it is possible to use the stopProcessor() function to output the values of all variables at some point in the program. To let the program continue after that, and possibly output the variable values using the stopProcessor() function repeatedly, the stop-on-stop-instruction flag must be set to false.
Example of the variable dump:
#set sort-variables;
#set target = 8m;
#set trace-execution = true;
require printing;
param number = 1.00000008976;
printExactFast(number);
printflush(message1);
stopProcessor();
Output:
stop instruction encountered, dumping variable values:
@counter: 77.0
number: 1.00000008976
:printExactFast:base: 60000.09930373835
:printExactFast:cmp: 8.976000009930374E-8
:printExactFast:exp: 0.0
:printExactFast:n: 1.00000008976
:printExactFast:t: 60000.0
*tmp1: 1.00000008976
*tmp7: 3.898227098923865E-8
*tmp9: 0.0
*tmp10: 1.0
*tmp13: 1.50000008976
*tmp12: 1.0
*tmp15: 8.976000009930374E-8
*tmp20: 1.0
*tmp21: 0.6000009930373835
Note that the sort-variables directive ensures the variables are sorted meaningfully. Without this directive, the variables are printed in the encountered order, which is identical to the order on the Vars screen in the game.
Secondly, when the trace-execution flag is set, each instruction and the prior values of its arguments are printed to the message log at each step. The values of arguments changed by the instruction are also printed after the instruction is executed. The size of the output produced by this mechanism can be quite sizable, but it can be used to inspect the program flow in detail.
Example of the execution trace:
Program execution trace:
Step 1, instruction #0: jump 3 always 0 0
Jumped to #3 (avoided 'draw triangle message1 number :printExactFast:base :printExactFast:cmp :printExactFast:exp :printExactFast:n')
Step 2, instruction #3: set number 1.00000008976
number --> null
number <-- 1.00000008976
Step 3, instruction #4: set :printExactFast:n number
:printExactFast:n --> null
number --> 1.00000008976
:printExactFast:n <-- 1.00000008976
Step 4, instruction #5: op abs *tmp1 number 0
*tmp1 --> null
number --> 1.00000008976
*tmp1 <-- 1.00000008976
Step 5, instruction #6: jump 9 greaterThan *tmp1 0
*tmp1 --> 1.00000008976
Jumped to #9 (avoided 'print number')
Step 6, instruction #9: jump 12 greaterThanEq number 0
number --> 1.00000008976
Jumped to #12 (avoided 'print "-"')
Step 7, instruction #12: op log10 *tmp7 :printExactFast:n 0
*tmp7 --> null
:printExactFast:n --> 1.00000008976
*tmp7 <-- 3.898227098923865E-8
Step 8, instruction #13: op floor :printExactFast:exp *tmp7 0
:printExactFast:exp --> null
*tmp7 --> 3.898227098923865E-8
:printExactFast:exp <-- 0.0
Step 9, instruction #14: op sub *tmp9 0 :printExactFast:exp
*tmp9 --> null
:printExactFast:exp --> 0.0
*tmp9 <-- 0.0
Step 10, instruction #15: op pow *tmp10 10 *tmp9
*tmp10 --> null
*tmp9 --> 0.0
*tmp10 <-- 1.0
Step 11, instruction #16: op mul :printExactFast:base :printExactFast:n *tmp10
:printExactFast:base --> null
:printExactFast:n --> 1.00000008976
*tmp10 --> 1.0
:printExactFast:base <-- 1.00000008976
In the web app, the number of messages output by either of these two mechanisms is limited to 1000; in the command-line application, the limit is 10,000.
« Previous: Mlog Watcher | Up: Contents | Next: Mlog Decompiler »