{"id":17950804,"url":"https://github.com/jonathanpeppers/dotnes","last_synced_at":"2026-04-02T20:31:38.989Z","repository":{"id":194867467,"uuid":"430943403","full_name":"jonathanpeppers/dotnes","owner":"jonathanpeppers","description":".NET for the NES game console","archived":false,"fork":false,"pushed_at":"2026-03-28T18:15:38.000Z","size":8899,"stargazers_count":673,"open_issues_count":23,"forks_count":24,"subscribers_count":15,"default_branch":"main","last_synced_at":"2026-03-28T18:33:44.170Z","etag":null,"topics":["csharp","dotnet","emulation","hacktoberfest","nes"],"latest_commit_sha":null,"homepage":"","language":"C#","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/jonathanpeppers.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-11-23T03:15:22.000Z","updated_at":"2026-03-28T18:06:37.000Z","dependencies_parsed_at":"2024-05-20T23:48:24.051Z","dependency_job_id":"67fd8816-6a14-4a05-98b5-e1e8d7a4974b","html_url":"https://github.com/jonathanpeppers/dotnes","commit_stats":null,"previous_names":["jonathanpeppers/dotnes"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/jonathanpeppers/dotnes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanpeppers%2Fdotnes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanpeppers%2Fdotnes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanpeppers%2Fdotnes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanpeppers%2Fdotnes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonathanpeppers","download_url":"https://codeload.github.com/jonathanpeppers/dotnes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanpeppers%2Fdotnes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31258787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-31T18:32:52.363Z","status":"ssl_error","status_checked_at":"2026-03-31T18:32:51.507Z","response_time":111,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["csharp","dotnet","emulation","hacktoberfest","nes"],"created_at":"2024-10-29T09:40:35.396Z","updated_at":"2026-04-02T20:31:38.981Z","avatar_url":"https://github.com/jonathanpeppers.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# .NES (\"dot\" NES)\n\n\u003cimg height=\"128\" src=\"assets/Transparent/dotnes-ms.png\" alt=\"dot NES logo\" /\u003e\n\n.NET for the NES game console!\n\n![Gif of NES Emulator launching from VS Code](assets/vscode.gif)\n\n## Samples\n\n| | |\n|:---:|:---:|\n| **shoot2** ⬇️ | **pong** ⬇️ |\n| ![shoot2](samples/shoot2/shoot2.gif) | ![pong](samples/pong/pong.gif) |\n| **climber** ⬇️ | **procgen** ⬇️ |\n| ![climber](samples/climber/climber.gif) | ![procgen](samples/procgen/procgen.gif) |\n| **snake** ⬇️ | **siegegame** ⬇️ |\n| ![snake](samples/snake/snake.gif) | ![siegegame](samples/siegegame/siegegame.gif) |\n| **metasprites** ⬇️ | **animation** ⬇️ |\n| ![metasprites](samples/metasprites/metasprites.gif) | ![animation](samples/animation/animation.gif) |\n| **scoreboard** ⬇️ | **bigsprites** ⬇️ |\n| ![scoreboard](samples/scoreboard/scoreboard.gif) | ![bigsprites](samples/bigsprites/bigsprites.gif) |\n| **sprites** ⬇️ | **horizscroll** ⬇️ |\n| ![sprites](samples/sprites/sprites.gif) | ![horizscroll](samples/horizscroll/horizscroll.gif) |\n| **vertscroll** ⬇️ | **scroll** ⬇️ |\n| ![vertscroll](samples/vertscroll/vertscroll.gif) | ![scroll](samples/scroll/scroll.gif) |\n| **fade** ⬇️ | **slideshow** ⬇️ |\n| ![fade](samples/fade/fade.gif) | ![slideshow](samples/slideshow/slideshow.gif) |\n| **monobitmap** ⬇️ | **music** ⬇️ |\n| ![monobitmap](samples/monobitmap/monobitmap.gif) | ![music](samples/music/music.gif) |\n| **fami** ⬇️ | **conio** ⬇️ |\n| ![fami](samples/fami/fami.gif) | ![conio](samples/conio/conio.gif) |\n| **metacursor** ⬇️ | **metatrigger** ⬇️ |\n| ![metacursor](samples/metacursor/metacursor.gif) | ![metatrigger](samples/metatrigger/metatrigger.gif) |\n| **horizmask** ⬇️ | **statusbar** ⬇️ |\n| ![horizmask](samples/horizmask/horizmask.gif) | ![statusbar](samples/statusbar/statusbar.gif) |\n| **peekpoke** ⬇️ | **irq** ⬇️ |\n| ![peekpoke](samples/peekpoke/peekpoke.gif) | ![irq](samples/irq/irq.gif) |\n| **rletitle** ⬇️ | **attributetable** ⬇️ |\n| ![rletitle](samples/rletitle/rletitle.png) | ![attributetable](samples/attributetable/attributetable.png) |\n| **tileset1** ⬇️ | **hello** ⬇️ |\n| ![tileset1](samples/tileset1/tileset1.png) | ![hello](samples/hello/hello.png) |\n| **tint** ⬇️ | **flicker** ⬇️ |\n| ![tint](samples/tint/tint.png) | ![flicker](samples/flicker/flicker.gif) |\n\n\u003e See the [samples/](samples/) folder for all 46 sample projects. Generate images with `dotnet run scripts/record-all-samples.cs`.\n\n## Contributing\n\nPRs of any kind are welcome! If you have a question, feel free to:\n\n* [File an Issue](https://github.com/jonathanpeppers/dotnes/issues)\n* [Open a Discussion](https://github.com/jonathanpeppers/dotnes/discussions)\n* [Join the Discord](https://discord.gg/xcYmpC5EPF)\n\nThanks!\n\n## Getting Started\n\nSimply install the template:\n\n```sh\ndotnet new install dotnes.templates\n```\n\nCreate a project:\n\n```sh\ndotnet new nes\n```\n\nOr use the project template in Visual Studio:\n\n![Screenshot of the NES project template in Visual Studio](assets/vs-template.png)\n\nBuild and run it as you would a console app:\n\n```sh\ndotnet run\n```\n\nOf course, you can also just open the project in Visual Studio and hit F5.\n\n\u003e Note that Ctrl+F5 currently works better in C# Dev Kit in VS Code.\n\nCheck out the video for a full demo:\n\n[![Check out the video](https://img.youtube.com/vi/m4TU5PJ8WtY/maxresdefault.jpg)](https://youtu.be/m4TU5PJ8WtY)\n\n## MSBuild Properties\n\n.NES exposes several MSBuild properties for configuring your ROM (mapper, mirroring,\nPRG/CHR bank counts, diagnostic logging, and more). See\n[docs/msbuild-properties.md](docs/msbuild-properties.md) for the full reference.\n\n## Anatomy of an NES application\n\n\"Hello World\" looks something like:\n\n```csharp\n// set palette colors\npal_col(0, 0x02);   // set screen to dark blue\npal_col(1, 0x14);   // fuchsia\npal_col(2, 0x20);   // grey\npal_col(3, 0x30);   // white\n\n// write text to name table\nvram_adr(NTADR_A(2, 2));            // set address\nvram_write(\"Hello, world!\");         // write bytes to video RAM\n\n// enable PPU rendering (turn on screen)\nppu_on_all();\n\n// infinite loop\nwhile (true) ;\n```\n\nThis looks very much like [\"Hello World\" in\nC](https://8bitworkshop.com/v3.10.0/?platform=nes\u0026file=hello.c), taking\nadvantage of the latest C# features in 2023.\n\nBy default the APIs like `pal_col`, etc. are provided by an implicit\n`global using static NESLib;` and all code is written within a single\n`Program.cs`.\n\nAdditionally, a `chr_generic.s` file is included as your game's \"artwork\" (lol?):\n\n```assembly\n.segment \"CHARS\"\n.byte $00,$00,$00,$00,$00,$00,$00,$00\n...\n.byte $B4,$8C,$FC,$3C,$98,$C0,$00,$00\n;;\n```\n\nThis table of data is used to render sprites, text, etc.\n\n## Music\n\n.NES also supports NES music playback via the APU. The `samples/music` project\nplays \"The Easy Winners\" by Scott Joplin using pulse and triangle channels:\n\n```csharp\nushort[] note_table = [ 4304, 4062, 3834, ... ];\nset_music_pulse_table(note_table);\n\nushort[] tri_table = [ 2138, 2018, 1905, ... ];\nset_music_triangle_table(tri_table);\n\nbyte[] music1 = [ 0x2a, 0x1e, 0x95, ... ];\n\napu_init();\nstart_music(music1);\n\nwhile (true)\n{\n    ppu_wait_nmi();\n    music_tick();\n}\n```\n\nFor more details, see [docs/music-sample.md](docs/music-sample.md).\n\n## Metasprites\n\nThe `samples/metasprites` project demonstrates runtime arrays, for loops, random\nnumbers, and metasprites — combining multiple hardware sprites into larger\nsprites:\n\n```csharp\nbyte[] metasprite = [\n    0, 0, 0xD8, 0,   // top-left\n    0, 8, 0xD9, 0,   // bottom-left\n    8, 0, 0xDA, 0,   // top-right\n    8, 8, 0xDB, 0,   // bottom-right\n    128               // terminator\n];\n\nbyte[] actor_x = new byte[16];\nbyte[] actor_y = new byte[16];\n\nbyte i = 0;\nwhile (i \u003c 16)\n{\n    actor_x[i] = rand8();\n    actor_y[i] = rand8();\n    i = (byte)(i + 1);\n}\n\nwhile (true)\n{\n    byte oam_id = 0;\n    i = 0;\n    while (i \u003c 16)\n    {\n        oam_id = oam_meta_spr(actor_x[i], actor_y[i], oam_id, metasprite);\n        i = (byte)(i + 1);\n    }\n    if (oam_id != 0) oam_hide_rest(oam_id);\n    ppu_wait_frame();\n}\n```\n\n## Scope\n\nThe types of things I wanted to get working initially:\n\n* An object model for writing NES binaries\n* Building a project should produce a `*.nes` binary, that is byte-for-byte\n  identical to a program written in C.\n* \"Hello World\" runs\n* Byte arrays, and a more advanced sample like `attributetable` run\n* Local variables work in some form\n* Project template, MSBuild support, IDE support\n* Music playback via the NES APU (see `samples/music`)\n* Metasprites, for loops, runtime arrays, and `rand8()` (see `samples/metasprites`)\n\nDown the road, I might think about support for:\n\n* Methods\n* Structs\n* Multiple files\n* Some subset of useful BCL methods\n\n## How it works\n\nFor lack of a better word, .NES is a \"transpiler\" that takes\n[MSIL](https://en.wikipedia.org/wiki/MSIL) and transforms it directly into a\nworking [6502 microprocessor](http://www.6502.org/) binary that can run in your\nfavorite NES emulator. If you think about .NET's Just-In-Time (JIT) compiler or\nthe various an Ahead-Of-Time (AOT) compilers, .NES is doing something similiar:\ntaking MSIL and turning it into runnable machine code.\n\nTo understand further, let's look at the MSIL of a `pal_col` method call:\n\n```msil\n// pal_col((byte)0, (byte)2);\nIL_0000: ldc.i4.0\nIL_0001: ldc.i4.2\nIL_0002: call void [neslib]NES.NESLib::pal_col(uint8, uint8)\n```\n\nIn 6502 assembly, this would look something like:\n\n```assembly\nA900          LDA #$00\n20A285        JSR pusha\nA902          LDA #$02\n203E82        JSR _pal_col\n```\n\nYou can see how one might envision using [System.Reflection.Metadata][srm] to\niterate over the contents of a .NET assembly and generate [6502\ninstructions][6502-instructions] -- that's how this whole idea was born!\n\nNote that the method `NESLib.pal_col()` has no actual C# implementation. In\nfact! there is *only* a reference assembly even shipped in .NES:\n\n```powershell\n\u003e 7z l dotnes.0.2.0-alpha.nupkg\n   Date      Time    Attr         Size   Compressed  Name\n------------------- ----- ------------ ------------  ------------------------\n2023-09-14 14:37:38 .....         8192         3169  ref\\net10.0\\neslib.dll\n```\n\nIf you decompile `neslib.dll`, no code is inside:\n\n```csharp\n// Warning! This assembly is marked as a 'reference assembly', which means that it only contains metadata and no executable code.\n// neslib, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null\n// NES.NESLib\npublic static void pal_col(byte index, byte color) =\u003e throw null;\n```\n\nWhen generating `*.nes` binaries, .NES simply does a lookup for `pal_col` to\n\"jump\" to the appropriate subroutine to call it.\n\n.NES also emits the assembly instructions for the actual `pal_col` subroutine. \nThe implementation uses an object model that represents 6502 instructions:\n\n```csharp\n/*\n* 823E\t8517          \tSTA TEMP                      ; _pal_col\n* 8240\t209285        \tJSR popa                      \n* 8243\t291F          \tAND #$1F                      \n* 8245\tAA            \tTAX                           \n* 8246\tA517          \tLDA TEMP                      \n* 8248\t9DC001        \tSTA $01C0,x                   \n* 824B\tE607          \tINC PAL_UPDATE                \n* 824D\t60            \tRTS\n*/\n// Uses Block and Instruction objects from the 6502 object model:\nvar block = new Block(\"_pal_col\");\nblock.Emit(STA_zpg(TEMP))\n     .Emit(JSR(popa))\n     .Emit(AND(0x1F))\n     .Emit(TAX())\n     .Emit(LDA_zpg(TEMP))\n     .Emit(STA_abs_X(PAL_BUF))\n     .Emit(INC_zpg(PAL_UPDATE))\n     .Emit(RTS());\n```\n\n[srm]: https://learn.microsoft.com/dotnet/api/system.reflection.metadata\n[6502-instructions]: https://www.masswerk.at/6502/6502_instruction_set.html\n\n## Limitations\n\nThis is a hobby project, so there are around 42 C# sample programs that are known to work. But to\nget an idea of what is not available:\n\n* No runtime\n* No BCL\n* No objects or GC\n* No debugger\n* Strings are ASCII\n\nWhat we *do* have is a way to express an NES program in a single `Program.cs`.\n\n## Links\n\nTo learn more about NES development, I found the following useful:\n\n* [8bitworkshop](https://8bitworkshop.com)\n* [NES 6502 Programming Tutorial](https://www.vbforums.com/showthread.php?858389-NES-6502-Programming-Tutorial-Part-1-Getting-Started)\n* [INES File Format](https://wiki.nesdev.org/w/index.php/INES)\n* [6502 Instruction Set][6502-instructions]\n* [HxD Hex Editor](https://mh-nexus.de/en/hxd/)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanpeppers%2Fdotnes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonathanpeppers%2Fdotnes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanpeppers%2Fdotnes/lists"}