Skip to content

Latest commit

 

History

History
232 lines (193 loc) · 15.3 KB

File metadata and controls

232 lines (193 loc) · 15.3 KB

Processor emulator

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.

Linked blocks

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 cell1 to cell9,
  • memory banks bank1 to bank9,
  • large logic displays display1 to display4,
  • logic displays display5 to display9.
  • message blocks message1 to message9.

At the beginning of the executions all the memory cells and memory banks are empty.

Supported variables

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

Supported instructions

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 format instruction is fully supported. Only text produced since the last printflush instructions is available for formatting.
    • When the contents of the text buffer encountered at the printflush instruction is the same as the content encountered at the previous printflush instruction, the text isn't generated again, just the number of the text block repetitions is output.
    • draw print instruction is handled like the printflush instructions: the text consumed by the draw print instruction isn't removed from the text buffer and is included in the program output.
  • The read and write instructions in targets 8.0 and higher emulate access to other processor variables faithfully (non-existent variables aren't accesible).
  • The sensor instruction is supported for a limited number of properties:
    • Version 6+:
      • @dead: returns true if 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., lead for @lead)
      • @id: returns the ID of the object if it exists (e.g., 1 for @lead)
      • @x, @y: returns the block position if the object is a block created from a schematic, otherwise 0 or null.
    • Version 8.0+:
      • @size, @bufferSize: returns the length when invoked on a string.
  • The wait instruction's effect on processor scheduling and time variables is emulated, but the instruction doesn't pause the emulation.

Irregular situation handling

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

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.

Inspecting program state

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 »