My simulator can almost more charitably be called a 6502 interpreter, as that's what it is. I pay no attention to cycles. I could count them, accurately, but I make no attempt to tie instructions to clock time at all. I run them as fast as I can. All I care about is making sure the status register is updated properly, and all of the instructions due what they're supposed to at a logical level.
Cycle accuracy is less important in isolation. My two pieces of "hardware" are memory addresses used to read the keyboard, spit a character out to the screen, and read/write blocks of ram to disk. For example, when you put the magic value in the "disk read" address, the CPU stops cold, the system goes out, loads a block of memory from the disk, stuffs it in to the CPUs memory space and then returns. How many cycles does that take? Dunno. Don't care. For my purposes it's unimportant. If it were more realistic, I would load the buffer in a separate thread, and then poll some address waiting for the "disk hardware" to signify success. Or have an interrupt handler, or something else.
But it doesn't, as I'm not replicating anything in the real world. I/O is a logical concept to me. Read character, write character, read block, write block. It's not like I was going to do anything but read the disk block at the same time anyway.
Cycle accuracy is far more important when you're actually trying to mimic a real system. In real systems, you have cooperating bits of hardware that are pretty much all racing each other.
A classic example is a memory mapped video display. The video circuit has hard limits on what it needs to do in order to keep the display refreshed properly. Every 60th of a second, it needs to pull data out of memory, and feed it out to the video display in lock step with the electron beam that's bouncing back and forth across the CRT. So, the video hardware is racing the CPU. You can see that if the CPU is not fast enough, that the data you want on the display won't be in memory in time for the video to display it. The election beam is a harsh task master.
But in the real systems, they figured all that stuff out, but even more so, the developers figured it out too. They also know that "hey the electron beam is going this fast, and will take X microseconds to cross the screen, and I can run Z instructions in X microseconds, and if I do this first, it'll be before the electron beam hits the screen, and that way I can get a lot more colors on the screen". So, those simulators trying to replicate actual hardware must strive to match in simulation what the actual hardware did in order for programs that pushed the machines to the limits of tolerances actually do what they did on the original hardware.
Whereas when my simulator sees AND, it just grabs data out of memory, ANDs it to the accumulator, and sets the flags.
Code:
public void AND(int value) {
acc = acc & value;
setFlagsNZ(acc);
}
That's it. No cycles, no clocks, no nothing.
My extent of granularity in the system is the actual instructions. In a real hardware simulator for like a C64 or Atari, that program is likely simulating clock phases. When the clock goes down, it's goes around tickling the virtual pins on the of the virtual hardware. They may well model things like the Read/Write pins, bus enable pins, the address pins, etc. To the point that they have to simulate the UNDOCUMENTED behavior on the pins and busses within the system, as the designers may well be using that in their design. So, rather than just being a list of 6502 instructions, like in my program, it's network of virtual ICs connected by virtual wires and driven by a virtual clock.
The reason it all works is that the machine upon which the simulation is running is able to perform all of the necessary updates to the virtual hardware, fast enough (i.e. more than ~2 millions times per second, considering the clocks) all "at once" so as to give the illusion of simultaneity. When the clock drops low, all this stuff happens on all of these chips. When the clock bump high, all these other things happen.
Very important for video games, and sound chips, and other things that affect the real world.
But for me? To see if a monitor program parses commands properly, and prints out memory in Hex? Meh -- who cares. Mine is Good Enough for that.