Author: @kusohanadayo
Version: 0.7
Hello there! You may be wondering why the hell I did this… and I actually don’t know! I did it mostly to entertain myself and as a way to improve my skills when it comes to coding in Udon Sharp. I always like to impose odd challenges on myself, and video games and VRChat weren’t going to be an exception!
This emulation is nowhere near as complicated as you might be thinking. In fact, it’s not directly emulating or translating bytes. Its architecture and entire operation are based on a similar contraption I made in the video game BAROTRAUMA using vanilla components not long ago. So instead of making it unnecessarily complicated, it can hold easily interpretable values in its memory cells instead of raw bytes.
All memory cells can store the following data types: String - Integer - Floating Point
When it needs to perform a comparison or an arithmetic operation, it first checks the data types of its registers and tries to convert them to the most convenient type. If that’s not possible, it simply skips the operation. I know this directly impacts performance, but this wasn’t made to be optimal—it was made to be fun!
When it comes to working, the emulation’s memory functions like a very long text file or a "Microsoft Excel" sheet. It normally starts interpreting operations at the first cell (which is address $0), but this can be set to any other point. The index that indicates which point is currently being interpreted is called the pointer, or program counter. This is a simple variable that holds the number of the cell currently being interpreted; after processing it, the pointer increments by one and moves on to the next memory cell.
User-implemented programs can also decide which parts of the code are interpreted when certain conditions are met, as you can see in the example below:
| Code | Explanation |
|---|---|
$0 = 5 |
Stores the number 5 at memory address $0 |
$1 = LDA $0 |
Loads the number 5 into the A register |
$2 = LDB $0 |
Loads the number 5 into the B register |
$3 = CMP AB |
Compare them ( A = B? / 5 = 5? ) |
$4 = JMP EQ $0 |
If they are equal, it makes the pointer go back to address $0, repeating this loop forever. |
Range: $0 to $1499899
The range of memory addresses between $0 and $1499899 is the programmable memory. You can place all the instructions that make up your program here.
To assign a value or instruction to any memory cell, you just need to write ${CellNumber} = {Value}.
Example:
$10 = POTATO
This stores the value "POTATO" in the 10th memory cell. If you want to store an instruction instead of a simple value, the method is the same.
Example:
$20 = $15 = POTATO
When the emulator reads and executes the 20th memory cell, it will set the value of memory cell 15 to "POTATO".
Range: $1499900 to $1499999
This region consists of memory points that directly affect the behavior of the computer itself. They work like commands but are executed immediately and/or automatically during program execution.
Setting these values to any number greater than zero will cause the generator to start playing the respective sound wave. The value written is interpreted as the target frequency (in Hz). Setting it back to 0 will stop the sound immediately.
| Address | Description |
|---|---|
| $1499900 | Square-wave sample generator control. |
| $1499905 | Sine-wave sample generator control. |
| $1499910 | Sawtooth-wave sample generator control. |
| $1499915 | Noise-wave sample generator control. |
| Address | Description |
|---|---|
| $1499950 | Changes the clock value. Any number written here will be used as the new clock speed. Same effect as $CLOCK command. |
| $1499960 | Changes the tick value. Any number written here will be used as the new tick speed. Same effect as $TICK command. |
| $1499970 | If set to 1, the emulator will immediately start executing the code. If it was loading data, that operation aborts so execution can begin right away. Used by diskettes to auto-run. |
| $1499980 | The number written to this address will be used as the program counter, causing code execution to start from that point. |
| Address | Description |
|---|---|
| $1499940 | User Input Buffer: Every time the user enters something starting with $ in the terminal, the input is written here in the format I:{UserInput}. Ex: If user types $POTATO, this address contains I:$POTATO. Loops can read this to determine user responses. Can be cleared to remove previous content. |
| $1499945 | RNG (Random Number Generator): Every time this address is read (e.g., LDA $1499945), it returns a random number between 0 and 1 with four decimal places. Writing to this address has no effect. |
Range: $1500000 to $1504096
This memory region is directly linked to the 64×64 screen texture. All values set here will directly affect the displayed pixels.
- Top Left:
$1500000 - Bottom Right:
$1504096
The pixels correspond linearly to the memory addresses. Adding +64 to any value will give you the pixel directly below the current one. When you reach the right edge and add one more, it wraps around to the next line on the left side.
If the value stored in these cells isn't recognized as a valid pixel value, it will be ignored and won't produce an update on the screen. As a last resort, you can also store programmable data in this region.
- Hint: To clean the screen you can use the commands
$FORMATor$RESET.
| Instruction | Description |
|---|---|
${TargetAddress} = {Value} |
Sets the desired value into the target memory address. |
$A = B |
Applies the value in register B to the address specified by A. |
| Instruction | Description |
|---|---|
LDA ${TargetAddress} |
Loads into register A the value stored at the target address. |
LDB ${TargetAddress} |
Loads into register B the value stored at the target address. |
STA ${TargetAddress} |
Stores the value contained in register A into the target address. |
STB ${TargetAddress} |
Stores the value contained in register B into the target address. |
STR ${TargetAddress} |
Stores the value contained in register R into the target address. |
STC ${TargetAddress} |
Stores the value contained in register C into the target address. |
| Instruction | Description |
|---|---|
ADD AB |
Adds the values of A and B and stores the result in register R. |
SUB AB |
Subtracts the values of A and B and stores the result in register R. |
MUL AB |
Multiplies the values of A and B and stores the result in register R. |
DIV AB |
Divides the values of A and B and stores the result in register R. |
| Instruction | Description |
|---|---|
JMP ${TargetAddress} |
Jumps to the specified target address. |
CMP AB |
Compares A and B and sets the internal flags [EQ, GT, LT]. |
JMP EQ ${TargetAddress|A} |
Jumps to the specified target address (or value in A) if EQ is true (Equals). |
JMP GT ${TargetAddress|A} |
Jumps to the specified target address (or value in A) if GT is true (Greater Than). |
JMP LT ${TargetAddress|A} |
Jumps to the specified target address (or value in A) if LT is true (Less Than). |
| Instruction | Description |
|---|---|
PRI ${TargetAddress} |
Prints the value of the specified address to the output terminal. |
CON AB |
Concatenates A and B (As strings) and stores the result in register C. |
NOP |
(No Operation) Performs no action and does not affect registers or memory. |
INC {A/B} |
Increments by one the value of A or B. |
DEC {A/B} |
Decrements by one the value of A or B. |
LDA $B |
Loads the A register with the value corresponding to the memory address stored in the B register. |
LDB $A |
Loads the B register with the value corresponding to the memory address stored in the A register. |
The following commands are entered using the VR keyboard to operate the emulation.
| Command | Description |
|---|---|
$RESET |
Resets the entire system, wiping all memory and internal function values back to defaults. Note: May cause a 1-second lag. |
$STOP |
Stops execution by halting the program counter increment. Also resets sound values to 0. |
$RUN |
Starts executing and interpreting code from the current program counter position (usually $0). |
$FORMAT |
Similar to reset, but only clears the memory and restores it to its default state. |
$LIST |
Lists all internal files stored in the emulator from Unity. |
$LOAD [NUMBER] |
Loads the file corresponding to that number in the list of internal files. |
$DEBUG |
Enables the debug overlay showing the real-time state of registers and internal data. |
$VERBOSE |
Displays the instructions currently being executed in real time. Note: Can cause significant lag at high speeds! |
$OSC |
Toggles the on-screen sound oscillator feature on or off. |
$GLOBAL |
Toggles global command broadcasting (makes the computer operate in global mode). Note: Disabled by default due to poor results; enable only for testing. |
$>{ADDRESS} |
Checks the value of a specific memory address (e.g., $50 displays the value at address 50). |
${ADDRESS} = {VALUE} |
Assigns a value to a specified memory address. |
$POINTER {NUMBER} |
Changes the current program counter position. |
$CLOCK {NUMBER} |
Changes internal clock value (frames per second the emulation executes instructions). Valid: 1 to 60. |
$TICK {NUMBER} |
Changes internal tick value (instruction batches executed per clock tick). Valid: 1 to 650. |
$HELP |
Displays the list of valid terminal commands. |
Valid values for each memory cell in the Screen Memory Region range from 0 to 255. In an effort to optimize these values, they represent a color range that may feel counter-intuitive.
- Value 0: Black.
- Value 1: White.
- Values 2 to 255: Create a gradient between Blue and Magenta.
For demonstration purposes, simple scripts were created to automatically convert any image or sequence of images to the format used by this emulation.
- Hint: The best approach is to force black-and-white for a monochrome effect, or map the luma value of the original image to the blue-magenta palette.