{"id":21629740,"url":"https://github.com/harryr/nanac","last_synced_at":"2025-08-28T06:24:50.805Z","repository":{"id":139181242,"uuid":"87029979","full_name":"HarryR/nanac","owner":"HarryR","description":"Tiny modular assembler \u0026 vm for lightweight control microcode","archived":false,"fork":false,"pushed_at":"2020-04-24T16:05:27.000Z","size":35,"stargazers_count":36,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-29T03:57:48.212Z","etag":null,"topics":["assembler","bytecode","c","embeddable","microcode","modular","tiny","virtual-machine"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":false,"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/HarryR.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2017-04-03T01:56:10.000Z","updated_at":"2024-09-30T17:56:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d9c1303-2507-4210-8271-cfe0d420db7c","html_url":"https://github.com/HarryR/nanac","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HarryR/nanac","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryR%2Fnanac","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryR%2Fnanac/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryR%2Fnanac/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryR%2Fnanac/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HarryR","download_url":"https://codeload.github.com/HarryR/nanac/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryR%2Fnanac/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272453309,"owners_count":24937467,"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","status":"online","status_checked_at":"2025-08-28T02:00:10.768Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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","bytecode","c","embeddable","microcode","modular","tiny","virtual-machine"],"created_at":"2024-11-25T02:08:37.831Z","updated_at":"2025-08-28T06:24:50.788Z","avatar_url":"https://github.com/HarryR.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nanac\n\nNanac is a tiny C virtual machine, with a small Python two-pass assembler. The portable microcode VM is under 200 lines of C and is fully modular, which allows modules to be added at fruntime or compile-time without changing the assembler or core VM code.\n\n### Features\n\n * 256 registers, no stack, 16bit address space\n * Register windows for subroutines\n * Easy and quick to modify and experiment\n * Typeless registers, `void*`, for extensibility\n * No heap allocation in VM core\n * Microcontroller friendly\n * Clean portable C89 / ANSI-C code\n * Auto-updating (dis)assembler\n * Bytecode to C translator\n * Highly modular (can even add opcodes at runtime)\n\n\n## Example.asm\n\n```asm\n$ZERO 0   # Friendly names for registers\n$ONE 1\n\n:start  jmp to :main\n:exit   jmp die\n:main\n    reg mov $ZERO $ONE\n    reg swp $ONE $ZERO\n    cnd neq $ZERO $ONE\n    jmp to :exit\n\n:end\n    cnd eq $ZERO $ONE\n    jmp to :exit\n```\n\nWhen executing it will generate a trace:\n\n```asm\n@0    jmp to 2 0\n@2    reg mov 0 1\n@3    reg swp 1 0\n@4    cnd neq 0 1\n@6    cnd eq 0 1\n@7    jmp to 1 0\n@1    jmp die 0 0\n```\n\nThe assembler will generate a listing:\n\n```asm\n00000200 @0    jmp to :main   # test/example.asm:5\n00010000 @1    jmp die    # test/example.asm:6\n02000001 @2    reg mov $ZERO $ONE  # test/example.asm:8\n02020100 @3    reg swp $ONE $ZERO  # test/example.asm:9\n01010001 @4    cnd neq $ZERO $ONE  # test/example.asm:10\n00000100 @5    jmp to :exit   # test/example.asm:11\n01000001 @6    cnd eq $ZERO $ONE  # test/example.asm:14\n00000100 @7    jmp to :exit   # test/example.asm:15\n```\n\n\n## Using and Extending\n\nThe builtins and vm are contained in `libnanac.a` and `nanac.h` which can be\nincluded in projects as a git submodule. Build with:\n\n```\ngit submodule add https://github.com/HarryR/nanac\ncc -o myproject.exe -Inanac myproject.c -Lnanac -lnanac\n```\n\n### Implementing a native command\n\nAll commands have the same interface, they are passed the CPU context pointer and\ntwo 8 bit values from the data half of the opcode. Each opcode is 4 bytes, two\nindicating the module and command, then two arbitrary arguments.\n\nAccess registers through `nanac_reg_get` and `nanac_reg_set`, all registers are\na union type called `nanac_reg_t`.\n\n```c\nint reg_mov( struct nanac_s *cpu, unsigned char arga, unsigned char argb )\n{\n    nanac_reg_set(cpu, arga, nanac_reg_get(cpu, argb));\n    return NANAC_OK;\n} \n```\n\n### Registering a native module\n\n```c\nint jmp_to( struct nanac_s *cpu, unsigned char arga, unsigned char argb )\n{\n    // ...\n    return NANAC_OK\n}\n\nstatic const nanac_cmd_t _cmds_jmp[4] = {\n    {\"to\", \u0026jmp_to},\n    {\"die\", \u0026jmp_die},\n    {\"sub\", \u0026jmp_sub},\n    {\"ret\", \u0026jmp_ret},\n};\nnanac_mods_add(mods, \"jmp\", 4, _cmds_jmp);\n```\n\n### Example host program\n\n```c\nint main( int argc, char **argv )\n{\n    struct nanac_mods_s mods;\n    struct nanac_s ctx;\n    int ret;\n\n    nanac_mods_init(\u0026mods);         /* setup standard built-in modules + commands */\n    nanac_mods_builtins(\u0026mods);\n    \n    nanac_init(\u0026ctx, \u0026mods);        /* initialise context/CPU with the modules */\n\n    if( load_file(\u0026ctx, argv[1]) )  /* load bytecode file into ctx.ops */\n    {\n        ret = nanac_run(\u0026ctx);\n        if( ctx.ops )\n            free(ctx.ops);  /* free loaded file */\n    }\n\n    return ret;\n}\n```\n\n\n## Opcodes and Instructions\n\n The machine uses 32bit operations encoded as 4 independent bytes:\n\n * module ID `uint8`\n * cmd ID `uint8`\n * arg A `uint8`\n * arg B `uint`\n\nNew operations are registered as modules containing commands in your programs source code,\nthe module and cmd IDs, when new modules are added the assembler \u0026 disassembler automatically update their instructions.\n\nThere are `2^8` registers, each is a native sized `void*` pointer.\n\nThere can be up to `2^16` operations (256 kilobytes of code), the instruction pointer `eip` is 16bit.\n\nBasic Modules:\n\n * `jmp` - Jumps and subroutines\n * `cnd` - Conditionally execute the next opcode\n * `reg` - Register manipulation\n\nOther modules must be implemented by the user.\n\n### `jmp` module\n\n * `to :label` - Immediate jump to an absolute address\n * `die` - Terminate program\n * `sub :label` - Enter sub-routine, allowing for `ret` to return\n * `ret $A $B` - Load opcode from register A, reduce register window by B, run temporary opcode\n\n\n### `cnd` module\n\n * `eq $A $B` - If $A and $B are equal, execute next instruction\n * `neq $A $B` - If $A and $B aren't the same, execute next instruction\n * `nil $A $B` - f either $A or $B are uninitialised (`NULL`), execute next instruction\n * `nz $A $B` - If either $A or $B are *not* uninitialised (`NULL`), execute next instruction\n\n### `reg` module\n\n * `swp $A $B` - Swap register B with register A\n * `mov $A $B` - Copy register B to register A\n * `crl $A $B` - Reset registers A and B to an uninitialised state (e.g. `NULL`)\n * `win $A $B` - Increment register window by $A, decrement by $B","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharryr%2Fnanac","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharryr%2Fnanac","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharryr%2Fnanac/lists"}