{"id":17324936,"url":"https://github.com/fwsgonzo/gamebro","last_synced_at":"2026-03-08T01:33:45.926Z","repository":{"id":44999469,"uuid":"156282831","full_name":"fwsGonzo/gamebro","owner":"fwsGonzo","description":"Gameboy Color emulator library for C++17","archived":false,"fork":false,"pushed_at":"2024-03-19T19:28:55.000Z","size":402,"stargazers_count":24,"open_issues_count":0,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-14T16:57:16.060Z","etag":null,"topics":["cpp","cpp17","gameboy-emulator","gbc","gbc-emulator","library"],"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/fwsGonzo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","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":"2018-11-05T21:04:23.000Z","updated_at":"2025-04-12T12:06:40.000Z","dependencies_parsed_at":"2025-04-14T16:55:36.349Z","dependency_job_id":"622a9b0d-ce67-4383-b15b-a2be20cfde79","html_url":"https://github.com/fwsGonzo/gamebro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fwsGonzo/gamebro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwsGonzo%2Fgamebro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwsGonzo%2Fgamebro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwsGonzo%2Fgamebro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwsGonzo%2Fgamebro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fwsGonzo","download_url":"https://codeload.github.com/fwsGonzo/gamebro/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fwsGonzo%2Fgamebro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30240898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-08T00:58:18.660Z","status":"ssl_error","status_checked_at":"2026-03-08T00:55:48.608Z","response_time":53,"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":["cpp","cpp17","gameboy-emulator","gbc","gbc-emulator","library"],"created_at":"2024-10-15T14:12:00.998Z","updated_at":"2026-03-08T01:33:45.902Z","avatar_url":"https://github.com/fwsGonzo.png","language":"C++","readme":"## GBC emulator library and trainer written in modern C++\n\n### Load a ROM\n```C++\n    std::vector\u003cuint8_t\u003e romdata = load_file(...);\n    gbc::Machine machine(romdata);\n```\n\n### Pixel output\nIntended for embedded where you have direct access to framebuffers. Your computer needs a way to get a high-precision timestamp and sleep for micros at a time. Delegates will be called async from the virtual machine, and you must call the system calls from there. You can tell the virtual machine about key presses through the API.\n\n\nThe virtual machine does not copy the ROM data unnecessarily, and uses memory conservatively. The instruction decoder is written to reduce instruction count. When building with clang you should enable LTO.\n\n```C++\n    // trap on palette writes, resulting in a 15-bit RGB color\n    machine-\u003egpu.on_palchange(\n        [] (const uint8_t idx, const uint16_t color)\n        {\n            // do some magic with the 5-5-5 bit channels\n        });\n    // trap on V-blank interrupts\n    machine-\u003eset_handler(gbc::Machine::VBLANK,\n        [] (gbc::Machine\u0026 machine, gbc::interrupt_t\u0026)\n        {\n            // Retrieve a vector of indexed colors\n            const auto\u0026 pixels = machine.gpu.pixels();\n\n            for (int y = 0; y \u003c gbc::GPU::SCREEN_H; y++)\n            for (int x = 0; x \u003c gbc::GPU::SCREEN_W; x++)\n            {\n                const uint16_t idx = pixels.at(gbc::GPU::SCREEN_W * y + x);\n                // in mode13h just write index directly to backbuffer\n                set_pixel(x+80, y+32, idx);\n            }\n            // blit and flip front framebuffer here\n            /* gbz80_limited_blit(...); */\n        });\n\n```\nYou can assume that the index gbc::GPU::WHITE_IDX is always a white color.\n\n### GB color palettes\n\nThe emulator has some predefined color palettes for GB.\n```C++\n    enum dmg_variant_t {\n        LIGHTER_GREEN = 0,\n        DARKER_GREEN,\n        GRAYSCALE\n    };\n    machine.gpu.set_dmg_variant(...);\n    // now we can expand color indices on V-blanks\n    uint32_t color = machine.gpu.expand_dmg_color(idx);\n```\nIf you don't trap on palette writes in CGB mode, you can still expand CGB color indices like this:\n```C++\n    // Expand CGB color index to 32-bit RGB (with no alpha):\n    uint32_t color = machine.gpu.expand_cgb_color(idx);\n```\n\n### 16-bit pixel buffer\n\nThe pixel buffer contains indices for colors in the current frame. You must assume that the palette changes between frames, and in some games even changes during frame rendering. An index is 8-bits and the machine needs 64 (0-63), where index 32 is white. The pixel buffer element size is 16-bits so that it can fit 15-bit colors if anyone wants to re-add the support. It is somewhat costly to do it this way without using macros. The emulator used to support a wide variety of color modes, but it's too costly to maintain and the vast majority of GBC games don't change palettes mid-frame, even though they can. You can replace some code in the GPU, specifically the return values of colorize_tile() and colorize_sprite(), to just write the color directly to the pixel buffer. The method to computing a CGB color is simply:\n```C++\n  uint16_t rgb15 = this-\u003egetpal(index*2) | (this-\u003egetpal(index*2+1) \u003c\u003c 8);\n```\nYou should apply a curve to the 15-bit color to make it more appealing, or dull if you want to emulate the real GBC LCD screen. You can use the last bit (bit 15) for something extra.\n\n### Debugging\nRun the command-line variant in your favorite OS, and press Ctrl+C to break into a debugger. Only caveat is that the break is always at the next instruction.\n\n```C++\n#include \u003clibgbc/machine.hpp\u003e\n#include \u003csignal.h\u003e\n\nstatic gbc::Machine* machine = nullptr;\nstatic void int_handler(int) {\n\tmachine-\u003ebreak_now();\n}\n\nint main(int argc, char** args)\n{\n\tmachine = new gbc::Machine(romvector); // load this yourself\n\tmachine-\u003ebreak_now();                  // start in breakpoint\n\tsignal(SIGINT, int_handler);           // break on Ctrl+C\n\n\twhile (machine-\u003eis_running())\n\t{\n\t\tmachine-\u003esimulate();\n\t}\n    return 0;\n}\n\n```\n\n### Replaying\nBy trapping on joypad reads, the implementor can give the virtual machine inputs exactly only when necessary, reducing state by several magnitudes. 7kB of uncompressed input data (when recording only on dpad reads) is typically 60+ seconds of gameplay. With knowledge about how many times a specific game reads the I/O register per frame, the amount can probably be halved again.\n\nRecording example:\n```C++\n// disable rendering to pixel buffer\nmachine-\u003egpu.scanline_rendering(false);\n// trap on joypad reads\nmachine-\u003eio.on_joypad_read(\n    [] (gbc::Machine\u0026 machine, const int mode)\n    {\n        // recording on dpad reads, which saves an extra magnitude of data\n        if (mode == 1) {\n            const uint64_t frame = machine.gpu.frame_count();\n            // create record of sporadic jumps\n            const uint8_t jp = (frame % 2) ? gbc::BUTTON_A : 0;\n            keyboard_buffer.push_back(jp);\n        }\n    });\n```\nPlayback example:\n```C++\n// trap on joypad reads\nmachine-\u003eio.on_joypad_read(\n    [] (gbc::Machine\u0026 machine, const int mode)\n    {\n        // recording on dpad reads, which saves an extra magnitude of data\n        if (mode == 1) {\n            if (counter \u003c keyboard_buffer-\u003esize()) {\n                // input 1 byte from buffer and increment\n                machine.set_inputs(keyboard_buffer-\u003eat(counter++));\n            }\n        }\n    });\n```\n\nReplay example: https://cloud.nwcs.no/index.php/s/2iGRYDj7FJLpK7j\n\n### Training\nWe can use reinforcement learning with full machine-inspection to train a neural network to play games well. Use cheat searching in other GUI-based emulators to get memory addresses that can be used as rewards.\n\n### Post-mortem tidbits after writing a GBC emulator\n\n[Click here to read POSTERITY.md](POSTERITY.md)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffwsgonzo%2Fgamebro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffwsgonzo%2Fgamebro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffwsgonzo%2Fgamebro/lists"}