{"id":16162515,"url":"https://github.com/sunsided/dcpu-16","last_synced_at":"2026-04-26T23:31:23.358Z","repository":{"id":38329950,"uuid":"384279719","full_name":"sunsided/dcpu-16","owner":"sunsided","description":"A DCPU-16 emulator and assembler written in Rust.","archived":false,"fork":false,"pushed_at":"2022-06-17T01:44:55.000Z","size":177,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T03:48:14.816Z","etag":null,"topics":["assembler","assembly","cpu-emulator","dcpu-16-asm","emulator","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sunsided.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-09T00:27:54.000Z","updated_at":"2023-02-01T05:46:35.000Z","dependencies_parsed_at":"2022-08-25T02:00:25.559Z","dependency_job_id":null,"html_url":"https://github.com/sunsided/dcpu-16","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sunsided/dcpu-16","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunsided%2Fdcpu-16","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunsided%2Fdcpu-16/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunsided%2Fdcpu-16/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunsided%2Fdcpu-16/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sunsided","download_url":"https://codeload.github.com/sunsided/dcpu-16/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunsided%2Fdcpu-16/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32317162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"ssl_error","status_checked_at":"2026-04-26T23:26:25.802Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","assembly","cpu-emulator","dcpu-16-asm","emulator","rust"],"created_at":"2024-10-10T02:30:34.632Z","updated_at":"2026-04-26T23:31:23.343Z","avatar_url":"https://github.com/sunsided.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DCPU-16 Emulator and Assembler\n\nAn emulator for the DCPU-16 16-bit processor described for the \n[0x10\u003csup\u003ec\u003c/sup\u003e] video game.\n\nThe [DCPU-16 Specification](docs/specification.txt) is no longer available on the\noriginal website (as is the entire website) but still can be obtained from the [Wayback Machine].\n\nAn implementation of a DCPU-16 assembler is also provided in this repo and is built by default through \nthe crate's `assembler` feature.\n\n---\n\nCycle counts are currently not emulated.\n\n## Example usage\n\nSee [examples/sample.rs] for a commented example application. Here's a sneak peek:\n\n```rust\nuse dcpu16::{Register, DCPU16};\n\nfn main() {\n    let program = [\n        0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d, 0x7dc1, 0x001a, 0xa861,\n        0x7c01, 0x2000, 0x2161, 0x2000, 0x8463, 0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018,\n        0x7dc1, 0x001a, 0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000,\n    ];\n\n    let mut cpu = DCPU16::new(\u0026program);\n\n    // Use cpu.step() to step through each instruction.\n    // cpu.run() executes until a crash loop is detected.\n    cpu.run();\n\n    assert_eq!(cpu.program_counter, 0x001A);\n    assert_eq!(cpu.register(Register::A), 0x2000);\n    assert_eq!(cpu.register(Register::X), 0x40);\n    \n    let ram = cpu.ram();\n    assert_eq!(ram[0x1000], 0x20);\n}\n```\n\n### Running the emulation example\n\nThe example program can be started with\n\n```console\nRUST_LOG=dcpu16=trace cargo run --example sample\n```\n\nIt executes the program given in the [DCPU-16 Specification](docs/specification.txt):\n\n```asm\n; Try some basic stuff\n              SET A, 0x30              ; 7c01 0030\n              SET [0x1000], 0x20       ; 7de1 1000 0020\n              SUB A, [0x1000]          ; 7803 1000\n              IFN A, 0x10              ; c00d\n                 SET PC, crash         ; 7dc1 001a\n\n; Do a loopy thing\n              SET I, 10                ; a861\n              SET A, 0x2000            ; 7c01 2000\n:loop         SET [0x2000+I], [A]      ; 2161 2000\n              SUB I, 1                 ; 8463\n              IFN I, 0                 ; 806d\n                 SET PC, loop          ; 7dc1 000d\n\n; Call a subroutine\n              SET X, 0x4               ; 9031\n              JSR testsub              ; 7c10 0018\n              SET PC, crash            ; 7dc1 001a\n\n:testsub      SHL X, 4                 ; 9037\n              SET PC, POP              ; 61c1\n\n; Hang forever. X should now be 0x40 if everything went right.\n:crash        SET PC, crash            ; 7dc1 001a\n```\n\nThe above program can be assembled into to the following bytecode:\n\n```hexdump\n0000: 7c01 0030 7de1 1000 0020 7803 1000 c00d\n0008: 7dc1 001a a861 7c01 2000 2161 2000 8463\n0010: 806d 7dc1 000d 9031 7c10 0018 7dc1 001a\n0018: 9037 61c1 7dc1 001a 0000 0000 0000 0000\n```\n\nWhen executing the bytecode instructions, the program output looks like this:\n\n```\n INFO dcpu16: Loaded 32 words of program data\nDEBUG dcpu16: Registers: A=0000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0000 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0000: 7c01 0030 ; SET A, 0x30 (\"A \u003c- 0x30\")\nDEBUG dcpu16: Registers: A=0030 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0002 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0002: 7de1 1000 0020 ; SET [0x1000], 0x20 (\"RAM[0x1000] \u003c- 0x20\")\nDEBUG dcpu16: Registers: A=0030 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0005 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0005: 7803 1000 ; SUB A, [0x1000] (\"A \u003c- A - RAM[0x1000]\")\nDEBUG dcpu16: Registers: A=0010 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0007 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0007: c00d ; IFN A, 0x10 (\"execute next instruction if A != 0x10\")\nDEBUG dcpu16: Registers: A=0010 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0008 SP=FFFF O=0000\nDEBUG dcpu16: SKIP 0008: 7dc1 001a ; SET PC, 0x1A (\"PC \u003c- 0x1A\")\nDEBUG dcpu16: Registers: A=0010 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=000A SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000A: a861 ; SET I, 0x0A (\"I \u003c- 0x0A\")\nDEBUG dcpu16: Registers: A=0010 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=000A J=0000 PC⁎=000B SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000B: 7c01 2000 ; SET A, 0x2000 (\"A \u003c- 0x2000\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=000A J=0000 PC⁎=000D SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000D: 2161 2000 ; SET [0x2000+I], [A] (\"RAM[0x2000 + I] \u003c- RAM[A]\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=000A J=0000 PC⁎=000F SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000F: 8463 ; SUB I, 0x01 (\"I \u003c- I - 0x01\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0009 J=0000 PC⁎=0010 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0010: 806d ; IFN I, 0x00 (\"execute next instruction if I != 0x00\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0009 J=0000 PC⁎=0011 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0011: 7dc1 000d ; SET PC, 0x0D (\"PC \u003c- 0x0D\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0009 J=0000 PC⁎=000D SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000D: 2161 2000 ; SET [0x2000+I], [A] (\"RAM[0x2000 + I] \u003c- RAM[A]\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0009 J=0000 PC⁎=000F SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000F: 8463 ; SUB I, 0x01 (\"I \u003c- I - 0x01\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0008 J=0000 PC⁎=0010 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0010: 806d ; IFN I, 0x00 (\"execute next instruction if I != 0x00\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0008 J=0000 PC⁎=0011 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0011: 7dc1 000d ; SET PC, 0x0D (\"PC \u003c- 0x0D\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0008 J=0000 PC⁎=000D SP=FFFF O=0000\n... loop repeats ...\nDEBUG dcpu16: EXEC 000D: 2161 2000 ; SET [0x2000+I], [A] (\"RAM[0x2000 + I] \u003c- RAM[A]\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0001 J=0000 PC⁎=000F SP=FFFF O=0000\nDEBUG dcpu16: EXEC 000F: 8463 ; SUB I, 0x01 (\"I \u003c- I - 0x01\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0010 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0010: 806d ; IFN I, 0x00 (\"execute next instruction if I != 0x00\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0011 SP=FFFF O=0000\nDEBUG dcpu16: SKIP 0011: 7dc1 000d ; SET PC, 0x0D (\"PC \u003c- 0x0D\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0000 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0013 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0013: 9031 ; SET X, 0x04 (\"X \u003c- 0x04\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0004 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0014 SP=FFFF O=0000\nTRACE dcpu16::instruction: Decoding non-basic instruction 7C10, opcode 01, value 1F\nDEBUG dcpu16: EXEC 0014: 7c10 0018 ; JSR 0x18 (\"jump to subroutine at 0x18\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0004 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0018 SP=FFFE O=0000\nDEBUG dcpu16: EXEC 0018: 9037 ; SHL X, 0x04 (\"X \u003c- X \u003c\u003c 0x04\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0040 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0019 SP=FFFE O=0000\nDEBUG dcpu16: EXEC 0019: 61c1 ; SET PC, POP (\"PC \u003c- pop value from stack\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0040 Y=0000 Z=0000 I=0000 J=0000 PC⁎=0016 SP=FFFF O=0000\nDEBUG dcpu16: EXEC 0016: 7dc1 001a ; SET PC, 0x1A (\"PC \u003c- 0x1A\")\nDEBUG dcpu16: Registers: A=2000 B=0000 C=0000 X=0040 Y=0000 Z=0000 I=0000 J=0000 PC⁎=001A SP=FFFF O=0000\nDEBUG dcpu16: EXEC 001A: 7dc1 001a ; SET PC, 0x1A (\"PC \u003c- 0x1A\")\n WARN dcpu16: Crash loop detected at PC=001A - terminating\n```\n\nAfter the execution, the `X` register contains the word `0040` as expected (by the specification).\n\n### Running the assembly / compilation example\n\nSee [examples/assemble.rs] for a commented example application. It can be started with\n\n```console\nRUST_LOG=dcpu16=trace cargo run --example assemble\n```\n\nHere's some example code:\n\n```rust\nuse dcpu16::{assemble, DCPU16};\n\nfn main() {\n    tracing_subscriber::fmt::init();\n\n    let source = r\"\n        ; Try some basic stuff\n                      SET A, 0x30              ; 7c01 0030\n                      SET [0x1000], 0x20       ; 7de1 1000 0020\n                      SUB A, [0x1000]          ; 7803 1000\n                      IFN A, 0x10              ; c00d\n                         SET PC, crash         ; d9c1*\n\n        ; Do a loopy thing\n                      SET I, 10                ; a861\n                      SET A, 0x2000            ; 7c01 2000\n        :loop         SET [0x2000+I], [A]      ; 2161 2000\n                      SUB I, 1                 ; 8463\n                      IFN I, 0                 ; 806d\n                         SET PC, loop          ; b1c1*\n\n        ; Call a subroutine\n                      SET X, 0x4               ; 9031\n                      JSR testsub              ; d010*\n                      SET PC, crash            ; d9c1*\n\n        :testsub      SHL X, 4                 ; 9037\n                      SET PC, POP              ; 61c1\n\n        ; Hang forever. X should now be 0x40 if everything went right.\n        :crash        SET PC, crash            ; d9c1*\n    \";\n\n    let program = assemble(\u0026source);\n\n    let mut cpu = DCPU16::new(program.as_slice());\n    println!(\"{}\", cpu.hexdump_program(8));\n\n    cpu.run();\n\n    // The last instruction perform a crash loop by jumping to itself (SET PC, 0x0016).\n    // The length of that operation is one word, hence the following assertion.\n    assert_eq!(cpu.program_counter, (program.len() - 1) as u16);\n}\n```\n\nHere's the hexdump of the generated program. Note that this code is shorter\nthan the provided original bytecode due to inlining of small constants;\nfor example, the `:crash SET PC, crash` instruction is rendered as `7dc1 001a`\nin the original code but represented using `d9c1` in this output:\n\n```hexdump\n0000: 7C01 0030 7DE1 1000 0020 7803 1000 C00D\n0008: D9C1 A861 7C01 2000 2161 2000 8463 806D\n0010: B1C1 9031 D010 D9C1 9037 61C1 D9C1\n```\n\nThis is the tracing output of the assembler:\n\n```\nTRACE dcpu16::assembler: instruction Basic(SET, Register(A), Static(Literal(48))), len = 2\nTRACE dcpu16::assembler: instruction Basic(SET, Address(4096), Static(Literal(32))), len = 3\nTRACE dcpu16::assembler: instruction Basic(SUB, Register(A), Static(Address(4096))), len = 2\nTRACE dcpu16::assembler: instruction Basic(IFN, Register(A), Static(Literal(16))), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, SpecialRegister(ProgramCounter), LabelReference(\"crash\")), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, Register(I), Static(Literal(10))), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, Register(A), Static(Literal(8192))), len = 2\nTRACE dcpu16::assembler: instruction Basic(SET, AddressOffset { address: 8192, register: I }, Static(AddressFromRegister(A))), len = 2\nTRACE dcpu16::assembler: instruction Basic(SUB, Register(I), Static(Literal(1))), len = 1\nTRACE dcpu16::assembler: instruction Basic(IFN, Register(I), Static(Literal(0))), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, SpecialRegister(ProgramCounter), LabelReference(\"loop\")), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, Register(X), Static(Literal(4))), len = 1\nTRACE dcpu16::assembler: instruction NonBasic(JSR, LabelReference(\"testsub\")), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, SpecialRegister(ProgramCounter), LabelReference(\"crash\")), len = 1\nTRACE dcpu16::assembler: instruction Basic(SHL, Register(X), Static(Literal(4))), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, SpecialRegister(ProgramCounter), Static(StackOperation(Pop))), len = 1\nTRACE dcpu16::assembler: instruction Basic(SET, SpecialRegister(ProgramCounter), LabelReference(\"crash\")), len = 1\n```\n\n[0x10\u003csup\u003ec\u003c/sup\u003e]: https://en.wikipedia.org/wiki/0x10c\n[DCPU-16 Specification]: docs/specification.txt\n[Wayback Machine]: http://web.archive.org/web/20120504005858/http://0x10c.com/doc/dcpu-16.txt\n[examples/sample.rs]: examples/sample.rs\n[examples/assemble.rs]: examples/assemble.rs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunsided%2Fdcpu-16","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunsided%2Fdcpu-16","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunsided%2Fdcpu-16/lists"}