# CyberVoid: The Deck - part 0.2

*In our previous article, we introduced the Deck, a new gameplay mechanic in the CyberVoid universe that merges gaming and coding. In this article we’re going to take a closer look at the interpreter that makes it all possible: the RWInterpreter. This is still an ongoing coding and review. We are still missing some key operations.*

**The RWInterpreter is a smart contract that operates a stack-based bytecode language.**

# **RW Overview**

**RWInterpreter Contract**

The RWInterpreter contract is written in Solidity making it compatible with any EVM. Is heavily inspired by EVM itself.

**Error Handling**

The contract has several custom errors defined for robust error handling. These include `ProgramOutOfBounds`, `StackUnderflow`, `DivisionByZero`, `NotEnoughElements`, and `InvalidOpcode`, which helps pinpoint issues in the program flow.

**OPCodes**

The contract defines several operation codes (OPCodes) that represent different operations in the bytecode language. There are **stack operations**, **arithmetic operations,** **bit operations**, and **branching operations**. Each operation is assigned a unique byte value, making it "easy" to read and write programs.

**The** `run` **Function**

The `run` function is the heart of the RWInterpreter. It takes a byte array representing a **program as input**, and it **returns the top value of the stack when the program finishes executing**. Inside the function, the program is iterated over, and each opcode is identified and executed

**Stack Operations**

Stack operations manipulate the stack's state. The `OP_PUSH` operation pushes a value onto the stack, `OP_POP` removes the top value from the stack, `OP_DUP` duplicates the top value of the stack, and `OP_SWP` swaps the two top values on the stack.

**Arithmetic Operations**

Arithmetic operations perform mathematical operations on the two top values of the stack. The operations include **addition**, **subtraction**, **multiplication**, **division**, and **modulo**.

**Bit Operations**

Bit operations perform bitwise operations on the stack's top values. These operations include bitwise **AND**, **OR**, **XOR**, and **NOT**.

**Branching Operations**

Branching operations control the flow of the program based on certain conditions. The `OP_JMP` operation jumps to a specific address, `OP_JZ` jumps if the top of the stack is zero, and `OP_JNZ` jumps if the top of the stack is not zero.

**Execution**

The program iterates over each opcode and performs the appropriate action. If an invalid opcode is encountered, the contract reverts with an `InvalidOpcode` error. If the `OP_STOP` opcode is encountered, the execution breaks and the function returns the top value of the stack.

## Examples of execution

### Arithmetical

```solidity
// compute (3+4)*2
function testCompleteProgram1() public {
    bytes memory program = hex"0103010414010216";
    uint256 topElement = interpreter.run(program);
    assertEq(topElement, 14, "top element should be 14");
}
```

### Bit operation

```solidity
function testOpCodeOr_ThreeWords() public {
	// PUSH 03 -> PUSH 02 -> PUSH 01 -> OR -> OR
	uint256 topElement = interpreter.run(hex"0103010301012A2A");
	assertEq(topElement, 3, "top element should be 3");
}
```

### Branching Operation

```solidity
function testOpCodeJMP_Forward() public {
	// OP_CODE JMP 0x0D
	uint256 topElement = interpreter.run(hex"0102");
	assertEq(topElement, 2, "top element should be 2");

	topElement = interpreter.run(hex"01020D0103");
	assertEq(topElement, 2, "top element should be 2");

	topElement = interpreter.run(hex"01020D01030104");
	assertEq(topElement, 4, "top element should be 4");
}
```

*Let's break this example down*

This test is verifying the correct operation of the `OP_JMP` opcode, which stands for "jump" in the RWInterpreter's bytecode language.

The `OP_JMP` opcode is used to jump forward in the bytecode by a specified number of steps. This is done by adding the operand's value to the program counter (`pc`). The operand in this case comes from the next byte in the program (right after the `OP_JMP` opcode).

**Step by Step**

1. `topElement =` [`interpreter.run`](http://interpreter.run)`(hex"0102");`: This part is testing the `OP_PUSH` opcode. It pushes the value 2 onto the stack (`01` being the `OP_PUSH` opcode and `02` is the value to be pushed). The `assertEq` function checks if the top element of the stack is 2, which it should be as it's the value that was just pushed onto the stack.
    
2. `topElement =` [`interpreter.run`](http://interpreter.run)`(hex"01020D0103");`: This part is testing the `OP_JMP` opcode. It first pushes the value 2 onto the stack (with `0102`), then it jumps forward one instruction (`0D01`), skipping the next `OP_PUSH` instruction which would have pushed 3 onto the stack. The `assertEq` function checks if the top element of the stack is 2, which it should be because `OP_JMP` operation didn't affect the stack and the push of the number 3 was skipped.
    
3. `topElement =` [`interpreter.run`](http://interpreter.run)`(hex"01020D01030104");`: This part extends the previous test. After the `OP_JMP` opcode, adds another `OP_PUSH` opcode which pushes 4 onto the stack. Since `OP_JMP` operation jumped one step, it skipped the push of the number 3, and the top element should be 4 after execution, which is checked with the `assertEq` function.
    

## **RWInterpreter Specification Alpha v0.1**

### **Properties**

**Stack and Reverse Polish Notation (RPN):** The RWInterpreter uses a stack data structure to process bytecode programs. The stack operates using [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation).

The **stack has 256 elements** for v0.1.

**Words are 8 bits** long for v0.1.

**OPCodes**: The bytecode language used in the RWInterpreter defines a range of operation codes (OPCodes) that specify different operations that can be executed. These include:

* Stack operations (8bits):
    
    * `OP_PUSH (0x01)`: push value to stack
        
    * `OP_POP (0x02)`: pop value from stack
        
    * `OP_DUP (0x03)`: duplicate top of stack
        
    * `OP_SWP (0x04)`: swap top two values on stack
        
* Arithmetic operations (8bits):
    
    * `OP_ADD (0x14)`: add top two values on stack
        
    * `OP_SUB (0x15)`: subtract top two values on stack
        
    * `OP_MUL (0x16)`: multiply top two values on stack
        
    * `OP_DIV (0x17)`: divide top two values on stack
        
    * `OP_MOD (0x18)`: modulo top two values on stack
        
* Bitwise operations (8bits):
    
    * `OP_AND (0x29)`: bitwise and top two values on stack
        
    * `OP_OR (0x2A)`: bitwise or top two values on stack
        
    * `OP_XOR (0x2B)`: bitwise xor top two values on stack
        
    * `OP_NOT (0x2C)`: bitwise not top value on stack
        
* Branching operations (8bits):
    
    * `OP_JMP (0x0D)`: jump to address
        
    * `OP_JZ (0x0E)`: jump to address if top of stack is zero
        
    * `OP_JNZ (0x0F)`: jump to address if top of stack is not zero
        
* Stop operation: `OP_STOP (0xFF)`: stop execution
    

**Each opcode has a unique byte value and programs are written and interpreted as byte arrays.**

The opcode values are spaced out in hexadecimal values, allowing for easy expansion of the interpreter. The gaps between opcode ranges (`0x01-0x04, 0x14-0x18, 0x29-0x2C, 0x0D-0x0F, and 0xFF`) leave room for future additions in each category, while keeping related opcodes grouped.

**Error Handling**: The RWInterpreter defines several custom error types for managing exceptions that occur during program execution:

* `ProgramOutOfBounds`
    
* `StackUnderflow`
    
* `DivisionByZero`
    
* `NotEnoughElements`
    
* `InvalidOpcode`
    

These errors help identify and debug issues while making the interpreter safer and more reliable.

### **Functionalities**

`run`: The core of the RWInterpreter is the `run` function. This function accepts a byte array, representing a program, as an argument and returns the top value of the stack after the program has finished execution.

The `run` function carries out several operations:

* It sets up a **memory stack of 256 elements, Stack Pointer** **and Program Counter**
    
* It iterates over each byte in the input program, identifying the opcode and carrying out the corresponding operation.
    
* It handles different types of operations, including stack operations, arithmetic operations, bitwise operations, and branching operations.
    
* If an invalid opcode is encountered, it reverts with an `InvalidOpcode` error. If `OP_STOP` opcode is encountered, it halts execution and returns the top value of the stack.
    

# **Wrapping Up**

Throughout this blog post, we've explored the workings of the RWInterpreter, a key component of CyberVoid's Deck gameplay mechanic. From stack operations to arithmetic and branching operations, we've uncovered the inner work of its stack-based bytecode language. We've also delved into its error handling and the `run` function, both integral to the contract's functionality.

Still, **this is just the beginning of our journey**. We are continuously improving and enhancing the **RWInterpreter** and have plans to implement new opcodes like `OP_EQ` for equality checks and `OP_STORE` and `OP_LOAD` for memory operations.

Introducing memory operations will allow programs to store and retrieve values from memory during their execution. This significant step forward will enable more complex and dynamic programs, making your Deck even more powerful and versatile.

In parallel with these developments, we're working on a tool named **RWLang**, a high-level language interpreter written in JavaScript. Our goal with **RWLang** is to make it easier for players to write and understand programs for their Decks.

The RWInterpreter is a reflection of our commitment to creating an immersive, strategic and intellectually stimulating gameplay environment. Your Deck isn't just a tool; **it's an evolving manifestation of your strategy**.

Until next time, keep exploring the void.
