This repo contains the code for a Zilog Z80 processor emulator library.
The Z80Emulator solution consists of the following projects:
- Z80EmulatorConsoleTestApp: A simple console application that demonstrates the functionality of the library.
- Z80EmulatorLib: The code for the library itself.
- Z80Emulator.Tests: An extensive set of tests.
For the very same reason I also wrote a 6502 processor emulator... to relive a little of my youth, but mainly "just for fun" :-)
This library does not currently support any of the 'undocumented' Z80 instructions or flags.
It does very little in the way of interrupt handling (and by that I mean almost nothing!).
The included Z80EmulatorConsoleTestApp project demonstrates how to use the emulator. This application has a couple of simple Z80 code examples that it runs through the emulator.
From a developer's point of view, the emulator is used as follows:
- Create an instance of the
Machineclass, supplying an instance of a class that implements theIPortinterface (that performs input/output used for the Z80 IN/OUT instructions) - NOTE: The emulator has its own 'dummy' implementation of this interface (that is used if noIPortimplementation is supplied). - Load binary executable data into the machine by calling the
LoadExecutableDatamethod, supplying a byte array containing the binary data and the address at which the data should be loaded in memory. - Load any other binary data into the machine [if required] by calling the
LoadDatamethod, supplying a byte array containing the binary data and the address at which the data should be loaded in memory. The final parameter passed toLoadDatashould befalseto avoid clearing all memory before loading the data (otherwise any previously loaded executable data will be lost). - Set the initial state of the machine (e.g. register values, flags, etc.) [if required] by calling the
SetCPUStatemethod. - Call the
Executemethod to execute the loaded Z80 code. - Once execution has completed, the
GetCPUStatemethod can be called to retrieve the final state of the machine (register values, flags, etc.). - The
Dumpmethod can be called to get a string detailing the final state of the machine (which can be useful for debugging purposes).
The disassembler takes binary Z80 code and converts it to Z80 Assembly Language instructions. The included Z80EmulatorConsoleTestApp project includes a simple demonstration of this.
The disassembler functionality is used as follows:
- Create an instance of the
Machineclass and load executable data into it (as above). - Create an instance of the
Disassemblerclass, supplying theMachineclass instance, the address in memory at which to start disassembly, and the length of the code (in bytes). - Add non-executable data sections as required - these allow you to mark specific blocks of memory as containing data that is not executable (this tells the disassembler not to attempt to disassemble this data into [what would be invalid] Z80 instructions; instead, it will output them as byte values using a DB assembler directive).
- Call the
Disassemblemethod to perform the disassembly. This method returns a collection of tuple values, each of which consists of the address of the instruction and a string containing the disassembled instruction; for example, (0x2000, "LD BC,0400h"). - Iterate through the collection of tuples, outputting each address and disassembled instruction string (see the Z80EmulatorConsoleTestApp project for an example of this).
The assembler takes Z80 assembly language source code and generates Z80 binary code from it. The included Z80EmulatorConsoleTestApp project includes demonstrations of this.
The assembler functionality is used as follows:
- Create an instance of the
Assemblerclass. - Call the
Assemblemethod, passing a list of strings containing the lines of Z80 assembly language source code (one string per line of source code). This method returns a tuple, the first value of which indicates if the assembler successfully processed the supplied source code, if so, the second tuple value is a collection of bytes consisting of the generated binary data. - Once successfully assembled, the binary data can then be written out to a file or fed straight into the emulator to be executed (see the Z80EmulatorConsoleTestApp project for an example of this).
- If the assembler failed to process the source code then the
AsmErrorsproperty of the instance of theAssemblerclass can be accessed to obtain information about any errors that occurred during assembly. Again, the Z80EmulatorConsoleTestApp project includes demonstrations of this.
The Z80EmulatorConsoleTestApp project folder contains a sub-folder called "DataFiles" that contains two files that should be placed into the folder into which the Z80EmulatorConsoleTestApp application is built. The test application uses the code.asm file from this folder to test the assembler and then compares the generated binary data with that in the 48k.rom file (also in the DataFiles folder) to confirm that the assembler has correctly generated the binary code data.
The code.asm file is an assembly file listing for the 16K ROM of a Sinclair ZX Spectrum (and was acquired from https://www.tablix.org/~avian/spectrum/rom/zx82.htm)
The 48k.rom file is a binary file of the ZX Spectrum 16K ROM image and is included purely to allow the binary data generated by running code.asm through the assembler to be compared with pre-built ROM image data.
The following are features that are being considered for the future:
- Implement some form of interactive debugger (with features such as single stepping, breakpoint handling, etc.).
- Add support for 'undocumented' instructions.
| Version | Details |
|---|---|
| 1.0.0 | Initial implementation of Z80 emulator. |
| 1.1.0 | Implemented Z80 Assembler functionality and fixed a few issues in the Disassembler.DisassembleInstruction method and with some SUB instructions in the instruction tables. |