{"id":21229577,"url":"https://github.com/kosarev/z80","last_synced_at":"2025-04-05T18:10:07.137Z","repository":{"id":34169921,"uuid":"105360641","full_name":"kosarev/z80","owner":"kosarev","description":"Fast and flexible Z80/i8080 emulator in Python and C++","archived":false,"fork":false,"pushed_at":"2025-01-21T21:35:37.000Z","size":1227,"stargazers_count":65,"open_issues_count":24,"forks_count":11,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-29T17:11:09.556Z","etag":null,"topics":["8080","cpu-emulators","emulator","i8080","i8080a","mit-license","python","z80","z80-emulator"],"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/kosarev.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}},"created_at":"2017-09-30T09:53:51.000Z","updated_at":"2025-01-21T21:39:14.000Z","dependencies_parsed_at":"2024-11-20T23:29:01.264Z","dependency_job_id":"5ef3bd1a-9aeb-4bd3-923c-386e8490eefe","html_url":"https://github.com/kosarev/z80","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosarev%2Fz80","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosarev%2Fz80/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosarev%2Fz80/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosarev%2Fz80/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kosarev","download_url":"https://codeload.github.com/kosarev/z80/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378149,"owners_count":20929297,"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":["8080","cpu-emulators","emulator","i8080","i8080a","mit-license","python","z80","z80-emulator"],"created_at":"2024-11-20T23:28:26.522Z","updated_at":"2025-04-05T18:10:07.099Z","avatar_url":"https://github.com/kosarev.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# z80\nFast and flexible Z80/i8080 emulator.\n\n![Python package CI](https://github.com/kosarev/z80/actions/workflows/python-package.yml/badge.svg)\n![C/C++ CI](https://github.com/kosarev/z80/actions/workflows/c-cpp.yml/badge.svg)\n\n\n## Quick facts\n\n* Implements accurate machine cycle-level emulation.\n\n* Supports undocumented instructions, flags and registers.\n\n* Passes the well-known `cputest`, `8080pre`, `8080exer`,\n  `8080exm`, `prelim` and `zexall` tests.\n\n* Follows a modular event-driven design for flexible interfacing.\n\n* Employs compile-time polymorphism for zero performance\n  overhead.\n\n* Cache-friendly implementation without large code switches and\n  data tables.\n\n* Offers default modules for the breakpoint support and generic\n  memory.\n\n* Supports multiple independently customized emulator instances.\n\n* Written in strict C++11.\n\n* Does not rely on implementation-defined or unspecified\n  behavior.\n\n* Single-header implementation.\n\n* Provides a generic Python 3 API and instruments to create\n  custom bindings.\n\n* MIT license.\n\n\n## Contents\n\n* [Hello world](#hello-world)\n* [Adding memory](#adding-memory)\n* [Input and output](#input-and-output)\n* [Accessing processor's state](#accessing-processors-state)\n* [Modules](#modules)\n* [The root module](#the-root-module)\n* [State modules](#state-modules)\n* [Feedback](#feedback)\n\n\n## Hello world\n\n```c++\n#include \"z80.h\"\n\nclass my_emulator : public z80::z80_cpu\u003cmy_emulator\u003e {\npublic:\n    typedef z80::z80_cpu\u003cmy_emulator\u003e base;\n\n    my_emulator() {}\n\n    void on_set_pc(z80::fast_u16 pc) {\n        std::printf(\"pc = 0x%04x\\n\", static_cast\u003cunsigned\u003e(pc));\n        base::on_set_pc(pc);\n    }\n};\n\nint main() {\n    my_emulator e;\n    e.on_step();\n    e.on_step();\n    e.on_step();\n}\n```\n[hello.cpp](https://github.com/kosarev/z80/blob/master/examples/hello.cpp)\n\nBuilding:\n```shell\n$ git clone git@github.com:kosarev/z80.git\n$ cmake z80\n$ make\n$ make test\n$ make hello  # Or 'make examples' to build all examples at once.\n```\n\nRunning:\n```\n$ ./examples/hello\npc = 0x0000\npc = 0x0001\npc = 0x0002\n```\n\nIn this example we derive our custom emulator class,\n`my_emulator`, from a\n[mix-in](https://en.wikipedia.org/wiki/Mixin) that implements the\nlogic and default interfaces necessary to emulate the Zilog Z80\nprocessor.\nAs you may guess, replacing `z80_cpu` with `i8080_cpu` would give\nus a similar Intel 8080 emulator.\n\nThe `on_set_pc()` method overrides its default counterpart to\nprint the current value of the `PC` register before changing it.\nFor this compile-time polymorphism to be able to do its job, we\npass the type of the custom emulator to the processor mix-in as a\nparameter.\n\nThe `main()` function creates an instance of the emulator and\nasks it to execute a few instructions, thus triggering the custom\nversion of `on_set_pc()`.\nThe following section reveals what are those instructions and\nwhere the emulator gets them from.\n\n\n## Adding memory\n\nEvery time the CPU emulator needs to access memory, it calls\n`on_read()` and `on_write()` methods.\nTheir default implementations do not really access any memory;\n`on_read()` simply returns `0x00`, meaning the emulator in the\nexample above actually executes a series of `nop`s, and\n`on_write()` does literally nothing.\n\nSince both the reading and writing functions are considered by\nthe `z80::z80_cpu` class to be handlers, which we know because\nthey have the `on` preposition in their names, we can use the\nsame technique as with `on_set_pc()` above to override the\ndefault handlers to actually read and write something.\n\n```c++\nclass my_emulator : public z80::z80_cpu\u003cmy_emulator\u003e {\npublic:\n    ...\n\n    fast_u8 on_read(fast_u16 addr) {\n        assert(addr \u003c z80::address_space_size);\n        fast_u8 n = memory[addr];\n        std::printf(\"read 0x%02x at 0x%04x\\n\", static_cast\u003cunsigned\u003e(n),\n                    static_cast\u003cunsigned\u003e(addr));\n        return n;\n    }\n\n    void on_write(fast_u16 addr, fast_u8 n) {\n        assert(addr \u003c z80::address_space_size);\n        std::printf(\"write 0x%02x at 0x%04x\\n\", static_cast\u003cunsigned\u003e(n),\n                    static_cast\u003cunsigned\u003e(addr));\n        memory[addr] = static_cast\u003cleast_u8\u003e(n);\n    }\n\nprivate:\n    least_u8 memory[z80::address_space_size] = {\n        0x21, 0x34, 0x12,  // ld hl, 0x1234\n        0x3e, 0x07,        // ld a, 7\n        0x77,              // ld (hl), a\n    };\n};\n```\n[adding_memory.cpp](https://github.com/kosarev/z80/blob/master/examples/adding_memory.cpp)\n\nOutput:\n```\nread 0x21 at 0x0000\npc = 0x0001\nread 0x34 at 0x0001\nread 0x12 at 0x0002\npc = 0x0003\nread 0x3e at 0x0003\npc = 0x0004\nread 0x07 at 0x0004\npc = 0x0005\nread 0x77 at 0x0005\npc = 0x0006\nwrite 0x07 at 0x1234\n```\n\n\n## Input and output\n\nAside of memory, another major way the processors use to\ncommunicate with the outside world is via input and output ports.\nIf you read the previous sections, it's now easy to guess that\nthere is a couple of handlers that do that.\nThese are `on_input()` and `on_output()`.\n\nNote that the handlers have different types of parameters that\nstore the port address, because i8080 only supports 256 ports\nwhile Z80 extends that number to 64K.\n\n```c++\n    // i8080_cpu\n    fast_u8 on_input(fast_u8 port)\n    void on_output(fast_u8 port, fast_u8 n)\n\n    // z80_cpu\n    fast_u8 on_input(fast_u16 port)\n    void on_output(fast_u16 port, fast_u8 n)\n```\n\nThe example:\n```c++\nclass my_emulator : public z80::z80_cpu\u003cmy_emulator\u003e {\npublic:\n    ...\n\n    fast_u8 on_input(fast_u16 port) {\n        fast_u8 n = 0xfe;\n        std::printf(\"input 0x%02x from 0x%04x\\n\", static_cast\u003cunsigned\u003e(n),\n                    static_cast\u003cunsigned\u003e(port));\n        return n;\n    }\n\n    void on_output(fast_u16 port, fast_u8 n) {\n        std::printf(\"output 0x%02x to 0x%04x\\n\", static_cast\u003cunsigned\u003e(n),\n                    static_cast\u003cunsigned\u003e(port));\n    }\n\nprivate:\n    least_u8 memory[z80::address_space_size] = {\n        0xdb,        // in a, (0xfe)\n        0xee, 0x07,  // xor 7\n        0xd3,        // out (0xfe), a\n    };\n};\n```\n[input_and_output.cpp](https://github.com/kosarev/z80/blob/master/examples/input_and_output.cpp)\n\n\n## Accessing processor's state\n\nSometimes it's necessary to examine and/or alter the current\nstate of the CPU emulator and do that in a way that is\ntransparent to the custom code in overridden handlers.\nFor this purpose the default state interface implemented in the\n`i8080_state\u003c\u003e` and `z80_state\u003c\u003e` classes provdes a number of\ngetters and setters for registers, register pairs, interrupt\nflip-flops and other fields constituting the internal state of\nthe emulator.\nBy convention, calling such functions does not fire up any\nhandlers. The example below demonstrates a typical usage.\n\nNote that there are no such accessors for memory as it is\nexternal to the processor emulators and they themselves have to\nuse handlers, namely, the `on_read()` and `on_write()` ones, to\ndeal with memory.\n\n```c++\nclass my_emulator : public z80::z80_cpu\u003cmy_emulator\u003e {\npublic:\n    ...\n\n    void on_step() {\n        std::printf(\"hl = %04x\\n\", static_cast\u003cunsigned\u003e(get_hl()));\n        base::on_step();\n\n        // Start over on every new instruction.\n        set_pc(0x0000);\n    }\n```\n[accessing_state.cpp](https://github.com/kosarev/z80/blob/master/examples/accessing_state.cpp)\n\n\n## Modules\n\nBy overriding handlers we can extend and otherwise alter the\ndefault behavior of CPU emulators.\nThat's good, but what do we do if it's not enough?\nFor example, what if the default representation of the\nprocessor's internal state doesn't fit the needs of your\napplication?\nSay, you might be forced to follow a particular order of\nregisters or you just want to control the way they are packed in\na structure because there's some external binary API to be\ncompatible with.\nOr, what if you don't need to emulate the whole processor's\nlogic, and just want to check if a given sequence of bytes forms\na specific instruction?\n\nThat's where modules come into play.\nTo understand what they are and how to use them, let's take a\nlook at the definitions of the emulator classes and see what's\nunder the hood.\n\n```c++\ntemplate\u003ctypename D\u003e\nclass i8080_cpu : public i8080_executor\u003ci8080_decoder\u003ci8080_state\u003croot\u003cD\u003e\u003e\u003e\u003e\n{};\n\ntemplate\u003ctypename D\u003e\nclass z80_cpu : public z80_executor\u003cz80_decoder\u003cz80_state\u003croot\u003cD\u003e\u003e\u003e\u003e\n{};\n```\n\nEach of these classes is no more than a stack of a few other\nmix-ins.\nThe `root\u003c\u003e` template provides helpers that make it possible to\ncall handlers of the most derived class in the heirarchy, `D`,\nwhich is why it takes that class as its type parameter.\nIt also contains dummy implementations of the standard handlers,\nsuch as `on_output()`, so you don't have to define them when you\ndon't need them.\n\n`i8080_state\u003c\u003e` and `z80_state\u003c\u003e` have been mentioned in the\nprevious section as classes that define transparent accessors to\nthe processor state, e.g., `set_hl()`.\nThey also define corresponding handlers, like `on_set_hl()`, that\nother modules use to inspect and modify the state.\n\n`i8080_decoder\u003c\u003e` and `z80_decoder\u003c\u003e` modules analyze op-codes\nand fire up handlers for specific instructions, e.g, `on_halt()`.\n\nFinally, the job of `i8080_executor\u003c\u003e` and `z80_executor\u003c\u003e` is to\nimplement handlers like `on_halt()` to actually execute\ncorresponding instructions.\n\nThe convention is that modules shall communicate with each other\nonly via handlers.\nIndeed, if they would call the transparent accessors or refer to\ndata fields directly, then those accessors wouldn't be\ntransparent anymore and handlers would never be called.\nThis also means that modules are free to define transparent\naccessors in a way that seems best for their purpose or even not\ndefine them at all.\n\nAll and any of the standard modules can be used and customized\nindependently of each other.\nMoreover, all and any of the modules can be replaced with custom\nimplementations.\nNew modules can be developed and used separately or together with\nthe standard ones.\nIn all cases the only requirement is to implement handlers other\nmodules rely on.\n\n\n## The root module\n\n```c++\ntemplate\u003ctypename D\u003e\nclass root {\npublic:\n    typedef D derived;\n\n    ...\n\n    fast_u8 on_read(fast_u16 addr) {\n        unused(addr);\n        return 0x00;\n    }\n\n    void on_write(fast_u16 addr, fast_u8 n) {\n        unused(addr, n);\n    }\n\n    ...\n\nprotected:\n    const derived \u0026self() const{ return static_cast\u003cconst derived\u0026\u003e(*this); }\n    derived \u0026self() { return static_cast\u003cderived\u0026\u003e(*this); }\n};\n```\n\nThe main function of the root module is to define the `self()`\nmethod that other modules can use to call handlers. For example,\na decoder could do `self().on_ret()` whenever it runs into a\n`ret` instruction.\n\nAside of that, the module contains dummy implementations of the\nstandard handlers that do nothing or, if they have to return\nsomething, return some default values.\n\n\n## State modules\n\n```c++\ntemplate\u003ctypename B\u003e\nclass i8080_state : public internals::cpu_state_base\u003cB\u003e {\npublic:\n    ...\n\n    bool get_iff() const { ... }\n    void set_iff(bool f) { ... }\n\n    ...\n};\n\ntemplate\u003ctypename B\u003e\nclass z80_state : public internals::cpu_state_base\u003cz80_decoder_state\u003cB\u003e\u003e {\npublic:\n    ...\n\n    void exx_regs() { ... }\n    void on_exx_regs() { exx_regs(); }\n\n    ...\n};\n```\n\nThe purpose of state modules is to provide handlers to access the\ninternal state of the emulated CPU.\nThey also usually store the fields of the state, thus defining\nits layout in memory.\n\nRegardless of the way the fields are represented and stored, the\ndefault getting and setting handlers for register pairs use\naccess handlers for the corresponding 8-bit registers to obtain\nor set the 16-bit values.\nFurthermore, the low half of the register pair is always\nretrieved and set before the high half.\nThis means that by default handlers for 8-bit registers are\ngetting called even if originally a value of a register pair they\nare part of has been queried.\nCustom implementations of processor states, however, are not\nrequired to do so.\n\n```c++\n    fast_u16 on_get_bc() {\n        // Always get the low byte first.\n        fast_u8 l = self().on_get_c();\n        fast_u8 h = self().on_get_b();\n        return make16(h, l);\n\n    void on_set_bc(fast_u16 n) {\n        // Always set the low byte first.\n        self().on_set_c(get_low8(n));\n        self().on_set_b(get_high8(n));\n    }\n```\n\nAside of the usual getters and setters for the registers and\nflip-flops, both the i8080 and Z80 states have to provide an\n`on_ex_de_hl_regs()` handler that exchanges `hl` and `de`\nregisters the same way the `xchg` and `ex de, hl` do.\nAnd the Z80 state additionally has to have an `on_exx_regs()`\nthat swaps register pairs just as the `exx` instruction does.\nThe default swapping handlers do their work by accessing\nregisters directly, without relying on the getting and setting\nhandlers, similarly to how silicon implementations of the\nprocessors toggle internal flip-flops demux'ing access to\nregister cells without actually transferring their values.\n\nBecause the CPUs have a lot of similarities, processor-specific\nvariants of modules usually share some common code in helper base\nclasses that in turn are defined in the `internal` class.\nThat class defines entities that are internal to the\nimplementation of the library.\nThe client code is therefore supposed to be written as if the\nmodule classes are derived directly from their type parameters,\n`B`.\n\nNote that `z80_state` has an additional mix-in in its inheritance\nchain, `z80_decoder_state\u003c\u003e`, whereas `i8080_state` is derived\ndirectly from the generic base.\nThis is because Z80 decoders are generally not stateless objects;\nthey have to track which of the `IX`, `IY` or `HL` registers has\nto be used as the index register for the current instruction.\nThe decoder state class stores and provides access to that\ninformation.\n\n```c++\ntemplate\u003ctypename B\u003e\nclass z80_decoder_state : public B {\npublic:\n    ...\n\n    iregp get_iregp_kind() const { ... }\n    void set_iregp_kind(iregp r) { ... }\n\n    iregp on_get_iregp_kind() const { return get_iregp_kind(); }\n    void on_set_iregp_kind(iregp r) { set_iregp_kind(r); }\n\n    ...\n};\n```\n\nIn its simplest form, a custom state module can be a structure defining the\nnecessary state fields together with corresponding access handlers.\n\n```c++\ntemplate\u003ctypename B\u003e\nstruct my_state : public B {\n    fast_u16 pc;\n\n    ...\n\n    fast_u16 on_get_pc() const { return pc; }\n    void on_set_pc(fast_u16 n) { pc = n; }\n\n    ...\n\n    // These always have to be explicitly defined.\n    void on_ex_de_hl_regs() {}\n    void on_ex_af_alt_af_regs() {}\n    void on_exx_regs() {}\n};\n\n```\n[custom_state.cpp](https://github.com/kosarev/z80/blob/master/examples/custom_state.cpp)\n\n\n## Feedback\n\nAny notes on overall design, improving performance and testing\napproaches are highly appreciated.\nPlease file an issue or use the email given at\n\u003chttps://github.com/kosarev\u003e.\nThanks!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosarev%2Fz80","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkosarev%2Fz80","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosarev%2Fz80/lists"}