Replies: 1 comment
-
|
Your implementation will depend on whether the devices activated by the timer access memory or interact with the CPU in some way, for example by making it wait via /WAIT or /BUSACK or by keeping /CLK high for a period of time. The Z80 emulator does not implement /BUSACK, as that would require M-cycle-level granularity with micro-operations so that the execution of a given instruction could be interrupted after each M-cycle. Normally, the best way to emulate this is to assume that the device asserts /BUSACK during the last M-cycle of an instruction. The implementation of /BUSACK would consist of adding the number of clock cycles that this line has been held low to the counter of clock cycles already executed in the current frame ( /WAIT, on the other hand, is specific to the opcode fetch M-cycle and, unlike /BUSACK, usually only blocks short periods of time, so it is advisable to add those wait cycles to the counter in the opcode fetch callback functions ( Also note that in many cases it is not necessary to emulate timers that send signals every n cycles if you know that this causes other devices to advance their execution and that advance always occurs in a constant manner. Sometimes, it is best to advance the execution of such devices when they somehow interact with memory. For example, say we have a graphics chip that, during a certain interval of the machine frame, reads VRAM and produces a video signal. The emulation of that chip could run only when the CPU writes to VRAM during that interval, and if it does not do so before the interval ends, run entirely at the end of that interval. This is called opportunistic emulation. Anyway, and trying to answer your question literally, another solution would be to execute the CPU for 1 clock cycle or #define CYCLES_PER_FRAME 69888
typedef struct {
size_t cycles;
size_t last_timer_event_cycle;
uint8_t memory[65536];
Z80 cpu;
} Machine;
void machine_power(Machine *self, zbool state)
{
if (state)
{
self->cycles = self->last_timer_event_cycle = 0;
memset(self->memory, 0, 65536);
}
z80_power(&self->cpu, state);
}
void machine_run_frame(Machine *self)
{
while (self->cycles < CYCLES_PER_FRAME)
{
size_t t;
/* Execute the minimum possible number of clock cycles. */
self->cycles += z80_run(&self->cpu, Z80_MINIMUM_CYCLES_PER_STEP);
/* Produce the timer events corresponding to the clock cycle interval
from the last timer event of the previous iteration to the last timer
event of current one. */
for (t = (self->cycles - self->last_timer_cycle) / 4; t--)
{
/* Produce timer event ... */
}
/* Update the cycle of the last timer event.
This cycle must be a multiple of 4. */
self->last_timer_cycle = self->cycles & 3;
}
self->cycles -= CYCLES_PER_FRAME;
}If you prefer to use a more fine-grained approach and generate timer events from within the different callbacks, you will need to know the clock cycle, relative to the start of the instruction, at which the M-cycle corresponding to the callback being called begins, as well as its duration. To do this, you will need the Z80InsnClock library. You can find an example of its use in the |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
What is the best way to emulate the timers?
I am working on emulator of 80s system. It has 8 MHz crystal oscillator and 74ALS93 counter conected to it. The Z80 clock pin is driven by Qa output, so CPU runs at 4 MHz. The system has i8253 programmable timer too, and one timer's clock input is driven by Qc output. So, timer runs at 1 MHz.
The question is, How to emulate events, that occur every 4 CPU's ticks?
Beta Was this translation helpful? Give feedback.
All reactions