promotional bannermobile promotional banner

Create: Computationally Expensive

Adds compute power into Create: Power Grid

Computationally Expensive Alpha Title

Welcome to Computationally Expensive!

This is an addon to Power Grid that adds low-level compute power based on microcontrollers and the Assembly programming language.

Systems that this addon adds encourage the player to understand their controllers and computers at the deepest layer while keeping it accessible AND challenging. By the end of your playthrough you will be the proud owner of a datacenter you've built yourself, and hopefully it doesn't STACK_OVERFLOW, but that's up to you!

Difference from other compute mods

This mod doesn't handle the dirty laundry for you like some of the other mods that add compute power to Minecraft! You are responsible all the way from handling CPU registers to writing to memory to controlling peripherals!

Currently the mod is still in Alpha

So many features are bound to change and maybe even break some existing saves. When updating to a new version - make sure to read the changelog, likely there will be a mention of a Controller suddenly combusting after updating!

I would greatly appreciate your feedback in Power Grid's Discord server in the Computationally Expensive channels!

Operand notation

Notation Accepted values
Rd Writable register: R0R7
value Register, decimal literal, hexadecimal literal such as 0x2A, binary literal such as 0b101010, constant, label, variable address, or symbol + offset / symbol - offset
address RAM or NVM byte address supplied directly, by symbol/expression, or through a register
target Zero-based instruction index, normally written as a label
pin P1P8
mode INPUT or OUTPUT
edge RISING, FALLING, or BOTH
source P1P8 or TIMER
start, end End-exclusive byte range containing raw eight-byte instructions

Square brackets around an address are accepted but optional. For example, LDW R0, counter, LDW R0, [counter], and LDW R0, R1 are valid address forms.

Instructions

Operator Opcode Arguments Description
MOV 0 / 0x00 Rd, value Copies a 16-bit value into a register. Does not change flags.
LDB 1 / 0x01 Rd, address Loads one unsigned byte from RAM and zero-extends it into Rd.
LDW 2 / 0x02 Rd, address Loads a little-endian 16-bit word from RAM.
STB 3 / 0x03 address, value Stores the low eight bits of value in RAM.
STW 4 / 0x04 address, value Stores a little-endian 16-bit word in RAM.
LEA 5 / 0x05 Rd, address Copies an address into Rd. Variables evaluate to their byte addresses.
PUSH 6 / 0x06 value Pushes a 16-bit value onto the downward-growing RAM stack.
POP 7 / 0x07 Rd Pops a 16-bit value from the stack into Rd.
ADD 8 / 0x08 Rd, value Calculates Rd = Rd + value. Updates Z, N, C, and V.
ADC 9 / 0x09 Rd, value Calculates Rd = Rd + value + C. Updates arithmetic flags.
SUB 10 / 0x0A Rd, value Calculates Rd = Rd - value. C is set when a borrow occurs.
SBC 11 / 0x0B Rd, value Calculates Rd = Rd - value - C, using C as the previous borrow.
MUL 12 / 0x0C Rd, value Multiplies the operands and stores the low 16 bits in Rd.
DIV 13 / 0x0D Rd, value Signed 16-bit division. Division by zero faults the VM.
MOD 14 / 0x0E Rd, value Signed 16-bit remainder. Division by zero faults the VM.
INC 15 / 0x0F Rd Increments Rd by one.
DEC 16 / 0x10 Rd Decrements Rd by one.
NEG 17 / 0x11 Rd Replaces Rd with its two's-complement negation.
AND 18 / 0x12 Rd, value Calculates a bitwise AND. Updates Z and N; clears C and V.
OR 19 / 0x13 Rd, value Calculates a bitwise OR. Updates Z and N; clears C and V.
XOR 20 / 0x14 Rd, value Calculates a bitwise XOR. Updates Z and N; clears C and V.
NOT 21 / 0x15 Rd Inverts all 16 bits of Rd.
SHL 22 / 0x16 Rd, count Logical left shift. Only the low four bits of count are used.
SHR 23 / 0x17 Rd, count Logical right shift with zero fill.
SAR 24 / 0x18 Rd, count Arithmetic right shift preserving the sign bit.
ROL 25 / 0x19 Rd, count Rotates the 16-bit value left.
ROR 26 / 0x1A Rd, count Rotates the 16-bit value right.
CMP 27 / 0x1B left, right Updates flags as if left - right were calculated, without storing the result.
TEST 28 / 0x1C left, right Updates flags from left AND right, without storing the result.
JMP 29 / 0x1D target Unconditionally jumps to an instruction index.
JE 30 / 0x1E target Jumps when equal or zero: Z = 1.
JNE 31 / 0x1F target Jumps when not equal: Z = 0.
JL 32 / 0x20 target Signed less-than jump: N ≠ V.
JLE 33 / 0x21 target Signed less-than-or-equal jump: Z = 1 or N ≠ V.
JG 34 / 0x22 target Signed greater-than jump: Z = 0 and N = V.
JGE 35 / 0x23 target Signed greater-than-or-equal jump: N = V.
JB 36 / 0x24 target Unsigned below jump: C = 1 after comparison.
JBE 37 / 0x25 target Unsigned below-or-equal jump: C = 1 or Z = 1.
JA 38 / 0x26 target Unsigned above jump: C = 0 and Z = 0.
JAE 39 / 0x27 target Unsigned above-or-equal jump: C = 0.
JC 40 / 0x28 target Jumps when carry/borrow is set. Equivalent to JB.
JNC 41 / 0x29 target Jumps when carry/borrow is clear. Equivalent to JAE.
JO 42 / 0x2A target Jumps when signed overflow is set.
JNO 43 / 0x2B target Jumps when signed overflow is clear.
CALL 44 / 0x2C target Pushes the return instruction index and jumps to target.
RET 45 / 0x2D None Pops an instruction index and returns to it.
HALT 46 / 0x2E None Stops top-level firmware while retaining current outputs. Inside EXEC or NVEXEC, returns to the caller.
SLEEP 47 / 0x2F cycles Suspends instruction execution for the specified number of VM cycles. Interrupts can wake it.
MODE 48 / 0x30 pin, mode Configures a GPIO pin as INPUT or OUTPUT.
IN 49 / 0x31 Rd, pin Reads a pin voltage in millivolts into Rd, clamped to 012000.
OUT 50 / 0x32 pin, value Drives an output pin at value millivolts, clamped to 012000.
PIX 51 / 0x33 x, y, value Sets or clears one display pixel. Valid coordinates are x = 0..9 and y = 0..7; zero clears and nonzero lights it.
CLS 52 / 0x34 None Clears the entire 10×8 display.
PINT 53 / 0x35 pin, threshold, edge Enables a pin interrupt at a 012000 mV threshold for RISING, FALLING, or BOTH crossings.
TIMER 54 / 0x36 cycles Configures a repeating timer interrupt measured in VM cycles. A value of zero disables it.
EI 55 / 0x37 None Enables interrupt dispatch. Pending interrupts remain latched until handled.
DI 56 / 0x38 None Disables interrupt dispatch without clearing pending interrupts.
IRET 57 / 0x39 None Restores saved flags and the return PC after an interrupt. Using it outside an interrupt faults.
NOP 58 / 0x3A None Performs no operation but still consumes one VM cycle.
SPEED 59 / 0x3B value Selects the number of VM cycles scheduled for the next server tick, clamped from 1 to the configured maximum.
NVLDB 60 / 0x3C Rd, address Loads one unsigned byte from raw nonvolatile memory.
NVLDW 61 / 0x3D Rd, address Loads a little-endian 16-bit word from raw nonvolatile memory.
NVSTB 62 / 0x3E address, value Stores the low eight bits of value in raw nonvolatile memory.
NVSTW 63 / 0x3F address, value Stores a little-endian 16-bit word in raw nonvolatile memory.
EXEC 64 / 0x40 start, end Executes an end-exclusive byte range of eight-byte native instructions stored in RAM.
NVEXEC 65 / 0x41 start, end Executes an end-exclusive byte range of eight-byte native instructions stored in NVM.
IVEC 66 / 0x42 source, target Sets an interrupt vector for the current EXEC or NVEXEC context. Use before EI.

Flags

Flag Meaning
Z Zero result
N Negative result; bit 15 is set
C Carry after addition or borrow after subtraction/comparison
V Signed arithmetic overflow
I Interrupts enabled

CMP should normally precede a conditional jump. Signed jumps use N and V; unsigned jumps use C and Z.

Assembler directives

Directive Arguments Description
label: Identifier followed by : Declares a label at the current zero-based instruction index.
.ENTRY label or index Selects the first instruction executed after boot. Defaults to instruction zero.
.EQU name value Declares a compile-time constant.
.DATA address, byte, ... Writes raw initial bytes into RAM. Each byte must be in 0..255.
.VAR BYTE name [= value] Allocates one byte in static RAM.
.VAR WORD name [= value] Allocates one aligned, little-endian 16-bit word.
.ARRAY BYTE name, count Allocates a fixed-length byte array. Optional initializers may follow the count.
.ARRAY WORD name, count Allocates an aligned word array. Offsets are still measured in bytes.
.ARRAY BYTE/WORD name = values Allocates an array whose length is inferred from its initializer list.
.VECTOR source, label Assigns a firmware interrupt handler for P1P8 or TIMER.

Identifiers may contain letters, digits, and underscores, but must not begin with a digit. Symbols, labels, mnemonics, and register names are case-insensitive.

BYTE initializers accept -128..255. WORD initializers accept -32768..65535. Uninitialized data is zero-filled. Static data grows upward from low RAM addresses; the stack grows downward from address 1024, so programs must avoid overlap.

Interrupts

There are nine interrupt sources, handled in priority order:

  1. P1
  2. P2
  3. P3
  4. P4
  5. P5
  6. P6
  7. P7
  8. P8
  9. TIMER

Firmware handlers are declared with .VECTOR. Dynamically executed RAM/NVM programs begin with interrupts disabled and an empty vector table; they must configure handlers using IVEC and then execute EI.

An enabled interrupt without a corresponding handler causes a MISSING_INTERRUPT_HANDLER fault.

Raw executable format

EXEC and NVEXEC interpret memory as consecutive eight-byte instructions:

Byte Contents
0 Stable opcode byte from the instruction table
1 Packed operand modes
2–3 Operand A, little-endian
4–5 Operand B, little-endian
6–7 Operand C, little-endian

The execution range must:

  • Be nonempty
  • Stay within RAM or NVM
  • Use an end-exclusive address
  • Have a size divisible by eight

Jumps and interrupt vectors inside the range use instruction indices relative to that range. Executed ranges may nest up to eight levels. Registers, flags, RAM, NVM, GPIO state, timers, pending interrupts, and the normal RAM stack are shared between execution contexts.

Faults

Fault Cause
INVALID_FIRMWARE The flashed firmware image is malformed, unsupported, or fails validation.
INVALID_OPCODE A raw RAM/NVM instruction contains an unknown opcode.
INVALID_OPERAND An instruction receives an unsupported operand type or invalid value.
MEMORY_BOUNDS A RAM/NVM address or executable range is outside available memory.
STACK_OVERFLOW The stack exceeds RAM or executable nesting exceeds eight levels.
STACK_UNDERFLOW A pop, return, or interrupt return has no matching saved value/context.
DIVISION_BY_ZERO DIV or MOD uses zero as its divisor.
MISSING_INTERRUPT_HANDLER An enabled pending interrupt has no assigned vector.
PROGRAM_COUNTER_BOUNDS Execution jumps to or falls through to an invalid instruction index.
INTERNAL_ERROR An unexpected internal VM failure occurs.

Programs that intentionally stop must execute HALT or remain in a valid loop. Falling beyond the final instruction causes PROGRAM_COUNTER_BOUNDS.

The Create: Computationally Expensive Team

profile avatar
  • 4
    Projects
  • 4.1K
    Downloads

More from justkleby