It depends at what level of abstraction you want to simulate the 8085. Pedantically, the real chip is a 40-pin IC, so that's 40 signal lines that permutate through a defined set of transitions based on their historic states.
More usefully, you probably want either to simulate at the digital bus level, which usually means just knowing what the chip is attempting to do during each bus cycle (read from here, write to there, etc), or simply to interpret an 8085 instruction stream so as to react to each as the execution unit in a real chip might.
If the latter then the total body of code won't be so problematic — an array of bytes for the program, grab the next one the program counter says to fetch, use a switch
statement to decide what to do to process it, perform the action. I've a Z80 (which is closely related to the 8085) emulator embedded within a CP/M emulator (so, just getting the instruction stream correct is enough) and that's a few hundred lines, heavily oriented around being as non-repetitive as possible, speed be damned.
Even if the former, it shouldn't be so bad. In the past I've written a Z80 emulator that had as input and output only a 64-bit integer representing a digital version of current bus state. It had a broad structure similar to the previous but necessarily involved queuing up future transitions and making additional mid-instruction decisions. If you care only about correctness and simplicity for now, you might just keep an NSArray
of GCD blocks, representing what the CPU should do upon each clock transition or during each cycle.
Probably the smartest middle ground is to have the output stream from the CPU be in terms of machine cycles. They decompose exactly to bus states if you want to but also allow a much higher-level interpretation.
EDIT:
In its Objective-C form, thereby making it quite a bit less compact but hopefully easiest for the poster to follow, this is the Z80 emulator in instruction-accurate form.
It postdates and adapts a much simpler C-format version that was merely a disassembler (with hindsight: writing a disassembler first is probably a good step). That project does however contain the full half-cycle accurate Z80 simulation. However I ended up deciding that I don't like that design — making a C function call, especially by function pointer, every half a cycle is overly expensive and welding that low of a level of communications directly to the emulated execution unit is probably overkill; anything that exactly describes the same process but does so in a more implicit form is acceptable.
It's a 6502 emulator instead but this is my (C++) attempt to digest those lessons learnt; it's machine-cycle-by-machine-cycle (though machine cycles are exactly one clock cycle long on the 6502, as it happens — unless you assert the ready line, it loads or stores every single cycle whether it needs to or not) and schedules its work based on an internal list of micro-ops. It all ends up in the header because I decided the best way to integrate a simulated execution unit with an arbitrary bus was to provide it as a template. If I wanted exactly quarter-cycle bus reports like the Z80 code previously linked then I'd just use the template to create somebody that serialises each machine cycle into the appropriate bus states.
(... though if you're just talking emulation then even interruptable-at-any-cycle cycle accuracy doesn't necessarily imply the serialisation into smaller steps that I'm now a fan of — this 6502 emulation I wrote about 15 years ago instead just jumps onto a separate thread and blocks via semaphore at any permitted interruption point, which are placed upon the conclusion of every cycle)