Zim80, the Z80 Simulator

It’s been quiet for a while now and that is because I am working on building a simulator for the Z80 – or an emulator; it’s very confusing what difference is exactly. “Why would you build on from scratch when there must be some out there already?”, you may ask. Yes, they are out there but most of them are old and some of them don’t even work and they just don’t do what I would want out of one…

When I began writing the first code for the BIOS (kernel) for my Zalt Z80 Computer, I quickly realized that I needed some way of verifying the correctness of that code even before trying to upload it to the Zalt computer. So I was looking for something (a simulator or emulator) to do that with. Problem is with having a homebrewn computer design is that the normal Spectrum, MSX or TI-calc sims won’t work.

I wanted to be able to write unit tests that could validate each piece of code, each function and control the state of the entire system, not just the Z80, before it is called and assert the state of the entire system after it had executed.

So after a couple of days searching and trying out archaic software I was fed up and started to  think about writing my own Z80 simulator. My simulator would need to be able to also simulate the electrical signals of the Z80 and other parts of the system, like RAM etc. Simulating the Z80 itself would probably be just a really big (and complex) state machine, with all sorts of special cases.

Because I know Visual Studio and .NET / C# best, I decided to use these tools to create the simulator. Alright, I set to work. The basis of a digital signal is a datatype (enum) that represents four states:

  • Low
  • High
  • PosEdge
  • NegEdge

The simulator is not meant to measure performance or anything so no steps are taken to even consider absolute timing. Relative timing, however, needs to be very accurate based on these four states.

I created general digital signal classes with providers and consumers and even buses (a collection of a fixed number of digital signals). Then I started on the Z80 class and its implementation. I try to make a unit test for each aspect I program so coverage is pretty good.

I have approached the Z80 as a state machine. So there are a couple of states the CPU can be in:

  • Fetch
    This is the initial state that gets an instruction out of memory. When the opcode is known (decode) it switches to the Execute state.
  • Execute
    This state instantiates an instruction class associated with the opcode and gives it a chance to run until it signals complete.
  • Refresh
    This state class is used as a base class for other state classes that have a refresh cycle in their T3 and T4 cycles (of M1).
  • Interrupt
    This state indicates that the normal program flow has been interrupted and a new execution path will be followed. Not ready yet though.

So the Execute state executes instructions, which themselves are states as well and even can contain sub states (instruction parts I’ve called them) usually to read or write from/to memory. Think about an instruction like LD (IX+d), n. It has quite a few bytes: $DD to indicate IX, the opcode byte, a byte for the displacement (d) and a byte for the parameter value n. Then when its executing, that value n has to be written (instruction part) to the correct memory address – computed by adding the signed value of d to register IX.

At the time of writing I have just over 500 instructions implemented. That doesn’t mean I have implemented 500 classes, thankfully. It turns out that the Z80 instruction (binary) opcodes have a method to them. Using this some instruction classes implement almost a complete range of mnemonic in all its variations. Think of all the LD A, B and LD A, C etc. variations there are. The entire range is implemented by one instruction class. But  not always. Sometimes it just makes more sense to implement only on Z80 instruction in one class (like NOP). I still have to do a final completeness check if I have all Z80 documented and undocumented instructions in my opcode definition list (the root definition of all instructions) but currently I still have around 250 unimplemented opcodes. Most of the easy ones are done so there is still a lot of work to do.

Oh, by the way: Zim80 is pronounced as ‘symmetry’. I have made the project open source so you can all read along if you want.

Hope you liked it.

 

Published in: on October 23, 2016 at 3:50 pm  Leave a Comment  
Tags: