{"id":15060138,"url":"https://github.com/jbrosdevelopment/virtualcomputer","last_synced_at":"2026-01-05T12:31:46.960Z","repository":{"id":256860075,"uuid":"856637016","full_name":"JBrosDevelopment/VirtualComputer","owner":"JBrosDevelopment","description":"This project is a virtual computer that takes a vector of bytes and runs it as instructions. Also included is a complete assembler and compiler.","archived":false,"fork":false,"pushed_at":"2024-09-23T00:14:28.000Z","size":38190,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-02T21:01:39.394Z","etag":null,"topics":["assembler","binary","bytes","c","compiler","computer","instruction-set-architecture","rust","vc","virtual-computer"],"latest_commit_sha":null,"homepage":"https://jbrosdev.hashnode.dev/making-a-virtual-machine-binary-to-assembly-to-c-all-in-rust","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JBrosDevelopment.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-12T23:40:33.000Z","updated_at":"2024-12-14T15:34:48.000Z","dependencies_parsed_at":"2024-10-12T20:40:41.130Z","dependency_job_id":"522e9d2f-230e-49e8-9e28-92742cabf12a","html_url":"https://github.com/JBrosDevelopment/VirtualComputer","commit_stats":null,"previous_names":["jbrosdevelopment/virtualcomputer"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBrosDevelopment%2FVirtualComputer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBrosDevelopment%2FVirtualComputer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBrosDevelopment%2FVirtualComputer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBrosDevelopment%2FVirtualComputer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JBrosDevelopment","download_url":"https://codeload.github.com/JBrosDevelopment/VirtualComputer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239185848,"owners_count":19596775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembler","binary","bytes","c","compiler","computer","instruction-set-architecture","rust","vc","virtual-computer"],"created_at":"2024-09-24T22:53:38.106Z","updated_at":"2025-10-31T11:30:33.710Z","avatar_url":"https://github.com/JBrosDevelopment.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Virtual Computer, Assembler, and Compiler\n\n![Cover](https://raw.githubusercontent.com/JBrosDevelopment/VirtualComputer/refs/heads/master/blog/blog%20cover.png)\n\nThis project is a virtual 8 bit computer that takes a vector of bytes and runs it as instructions It includes the virtual machine, assembler, and compiler for custom assembly and high level language.\n\nI created a blog post about this project and you can find that on **[Hashnode](https://jbrosdev.hashnode.dev/making-a-virtual-machine-binary-to-assembly-to-c-all-in-rust)**. It goes deeper into explaining the different parts of this project.\n\n## Add to Your Project\n\n[vc_8bit](https://crates.io/crates/vc_8bit) can be added to your Rust project with the terminal command:\n\n```\ncargo add vc_8bit\n```\n\nThe code shown below assumes you are using the following modules:\n\n```rs\nuse vc_8bit::{assembly, c_lang, vc_8bit::Computer};\n```\n\n\n## Virtual Computer\n\nThe virtual computer works by instantiating the Computer and inserting the program into the ram as bytes. \n\n```rs\nlet bytes = vec![Byte::from_string(\"11010100\"), Byte::from_string(\"1001100\")];\nlet mut computer: Computer = Computer::new();\ncomputer.ram.insert_bytes(bytes);\ncomputer.run();\n```\n\nThe **VC** (Virtual Computer) is basically a big function that will take an array of bytes and run the instructions associated with the bytes. I have emulated components like a **Binary Decoder**, **RAM**, **ALU**, and **CPU**.\n\nWhat the VC does is take the binary and figure out the instructions that go with it. It uses Binary Decoders in a *match* statement to decide what instruction it is. Every Byte is an instruction. Some examples of instructions are moving from memory to registers, adding the values from one register to another using the ALU. This is why assembly is basically binary. Maybe the binary sequence `01001110` is the `MOV` instruction. All an assembler does is convert the instructions like `CPY`, `LDR`, and `ADD` to there corrosponding bytes. It gets a little more complex than this. For this VC, the first 6 bits are for the instruction, and the last 2 bits are for the register. If this sounds interesting to you, I highly recommend watching [Core Dumpped](https://www.youtube.com/@CoreDumpped) and his videos. \n\nWhen working with the VC, remember the RAM has 256 byte limit because the VC is only an 8 bit computer compared to modern 64 bit computers.  \n\n## Assembler\n\nThe assembler works by first assembling the code to binary. It will then turn the binary into an array of bytes. \n```rs\nlet value = \"MOV R0 50\";\n\n// assemble code\nlet contents = assembly::compile_assembly_to_binary(value.to_string());\nlet bytes = assembly::string_to_bytes(contents.as_str());\n\n// run on VC\nlet mut computer: Computer = Computer::new();\ncomputer.ram.insert_bytes(bytes);\ncomputer.run();\n```\n\nThe assembler will go line by line the code into binary. I created a custom assembly language to work with the VC. \n\n- `HALT`: Stops the program\n- `STR R0 #0000000`: Stores the value in the register to the address in memory\n- `LDR R0 #0000000`: Loads the value in the memory address to the register\n- `MOV R0 #0000000`: Moves a byte value into a register\n- `CPY R0 R1`: Copys the value of 1 register to another\n- `SHR R0 #0000000`: Shifts a register value by the left many times the number in the byte is\n- `SHL R0 #0000000`: Shifts a register value by the right however many times the number in the byte is\n- `OUT R0`: Outputs the value in the register to the console as a byte value\n- `MSG R0`: Outputs the value in the register to the console as an ASCII character\n- `INC R0`: Increments the value in the register\n- `DEC R0`: Decrements the value in the register\n- `JMP #0000000`: Moves the RAM index \n- `JMP_NEG #0000000`: Moves the RAM index if the ALU Negative flag is on\n- `JMP_ZRO #0000000`: Moves the RAM index if the ALU Zero flag is on\n- `JMP_ABV #0000000`: Moves the RAM index if neither the ALU negative flag or Zero flag is on\n- `CMP_NEG R0 #0000000`: Moves `11111111` to the register if the ALU negative flag is on. If not, it moves `00000000` to the register\n- `CMP_ZRO R0 #0000000`: Moves `11111111` to the register if the ALU zero flag is on. If not, it moves `00000000` to the register\n- `CMP_ABV R0 #0000000`: Moves `11111111` to the register if neither the ALU negative flag or Zero flag is on. If not, it moves `00000000` to the register\n- `ADD R0 R1`: Adds the byte value from registers 0 and 1 and moves the result to the first register \n- `SUB R0 R1`: Subtracts the byte value from registers 0 and 1 and moves the result to the first register \n- `MUL R0 R1`: Multiplies the byte value from registers 0 and 1 and moves the result to the first register \n- `DIV R0 R1`: Divides the byte value from registers 0 and 1 and moves the result to the first register \n- `AND R0 R1`: Does an and operation on the bytes from registers 0 and 1 and moves the result to the first register\n- `OR R0 R1`: Does an or operation on the bytes from registers 0 and 1 and moves the result to the first register\n- `XOR R0 R1`: Does an exclusive or operation on the bytes from registers 0 and 1 and moves the result to the first register\n- `NOT R0`: Does a not operation on the byte in register 1 and moves result to it\n- `RPRT R0 #0000000`: Reads the value in the port at the address and writes the value to register 0. The address needs to be 0 through 7.\n- `WPRT R0 #0000000`: Writes the value in register 0 to the port at the address. The address needs to be 0 through 7.\n\nThe assembler will identify integers, bytes, and hexadecimals:\n\n```\nMOV R0 5 ; moves 5 into R0\nMOV R1 0x3A ; moves the hexadecimal 3A (integer 58) to R1\nMOV R2 #00110100 ; moves the byte value 00110100 (integer 52) to R2\n```\n\nThere is also the `%ASSIGN` feature which can be used as *constants* inside the assembly\n\n```\n%ASSIGN VARIABLE_ADDRESS #11110100\n\nMOV R0 37\nSTR R0 VARIABLE_ADDRESS\n```\n\n## Compiler\n\nThe compiler works by compiling the code into assembly.\n\n```rs\nlet value = \"print('a');\";\n\n// compile code\nlet asm = c_lang::compile(value.to_string());\n\n// assemble code\nlet contents = assembly::compile_assembly_to_binary(\u0026asm);\nlet bytes = assembly::string_to_bytes(contents.as_str());\n\n// run on VC\nlet mut computer: Computer = Computer::new();\ncomputer.ram.insert_bytes(bytes);\ncomputer.run();\n```\n\n### Language\n\nThe language looks like C with a few key distinctions. \n\nThere are 3 types:\n\n- `uint8` an unsigned 8 bit integer. Any whole number between 0 and 255.\n- `bool` a true of false value. Is either `00000000` or `11111111` in binary\n- `char` a character value. Uses the ASCII codes to convert to binary\n\nThere are 5 functions:\n- `to_char(uint8)` turns a number into a character. This works for numbers 0 - 9. Any number above that will get the ASCII character of that number plus 48. This is because 48 is the ASCII character for 0. Here is an example of th function: `char c = to_char(7)`\n- `out(any)` will output the byte value to the console of any type. For example, the value 5 will output 00000101.\n- `print(char)` will output the character to the console.\n- `write_port(uint8, any)` will write the byte value to the port number 0 - 7. The address needs to be a constant. For example `write_port(2, 'a')` is fine but `write_port(99 - 3, 'a')` and `write_port(variable, 'a')` will not work.\n- `read_port(uint8)` will read the byte value from the port address 0 - 7. The address needs to be a constant. For example `read_port(2)` is fine but `read_port(99 - 3)` and `read_port(variable)` will not work.\n\nThere is only the `if` and `while` statements. The code inside of the statement needs to be seperated by commas and not semicolons. The last line inside the statement can not have a comma. The ending bracket of the statement needs to end with a semicolon. Here is what it looks like:\n\n```c\nif (true \u0026\u0026 0 == 0) {\n    char c = 'a',\n    print(c)\n};\n```\n\nAll operators work except for the `+=` type of operators. Just use `A = A + B` instead. For the `\u003c\u003c` and `\u003e\u003e` operators, the right operand needs to be constant. For example, `'a' \u003e\u003e 3` works but `'a' \u003e\u003e 3 + 1` or `'a' \u003e\u003e variable` do not work.\n\n- `++`, `--` increment and decrement. Works with `uint8`.\n- `+`, `-`, `*`, `/` arithmatic operators. Works with `uint8`.\n- `\u0026\u0026`, `||`, `==`, `!=` logic operators. Works with `bool`.\n- `\u003c\u003c`, `\u003e\u003e` bit shift operators. Shifting amount needs to be constant. Works with any type.\n- `\u0026`, `|`, `^`, `!` and, or, excluseive or, and not operators. Works with any type.\n- `=` set variable.\n\nThere is no way to define functions in this language. Also remember there are only 256 bytes in memory to work with in the VC. This means your program must be less than 255 bytes, and any variables that you use will take up one of those bytes.\n\n### Example\n\nThis simple program took me 27 bytes to write it in assembly. The compiler was able to use 38 bytes. It's not the smartest compiler it works.\n```c\nuint8 a = 0;\n\nwhile (a \u003c 5) {\n    a = a + 1,\n    char c = to_char(a),\n    print(c)\n}\n```\n\nThe assembly output for this code is as follows:\n\n```\nMOV R0 #00000000\nSTR R0 #11111110 ; store created variable ; BYTE ADDRESS 4\nLDR R0 #11111110 ; load variable ; value left\nMOV R1 #00000101 ; value right\nSUB R0 R1\nCMP_NEG R0 ; compare\nCPY R3 R0 ; copy value right ; get value for statement\nMOV R2 0 ; set R2 to 0\nSUB R2 R3 ; check if statement is true\nJMP_ZRO 37 ; jump if false\nLDR R0 #11111110 ; load variable ; value left\nMOV R1 #00000001 ; value right\nADD R0 R1 ; math\nSTR R0 #11111110 ; store variable\nLDR R3 #11111110 ; load variable\nMOV R2 48 ; 48 is ascii '0'\nADD R3 R2 ; get ascii\nCPY R0 R3 ; move value to correct register\nSTR R0 #11111101 ; store created variable\nLDR R2 #11111101 ; load variable\nMSG R2 ; print value\nJMP 4 ; jump back to start ; BYTE ADDRESS 38\nHALT\n```\n\nThe binary code that was assembled from this:\n\n```\n110010000000000011000000111111101100010011111110110010010000010100010001111100001100111100000000110010100000000000011011111010100010010111000100111111101100100100000001000000011100000011111110110001111111111011001010001100000000111011001100110000001100000011111101110001101111110111011110111010000000010011111111\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbrosdevelopment%2Fvirtualcomputer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjbrosdevelopment%2Fvirtualcomputer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbrosdevelopment%2Fvirtualcomputer/lists"}