Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Poor Man's cycle-ticked emulation framework? #91

Open
gtoal opened this issue Jan 11, 2024 · 0 comments
Open

Poor Man's cycle-ticked emulation framework? #91

gtoal opened this issue Jan 11, 2024 · 0 comments

Comments

@gtoal
Copy link

gtoal commented Jan 11, 2024

This is a paraphrased version of https://vectorgaming.proboards.com/thread/2537/cycle-ticked-emulation which I posted on that group a few years ago. I was checking in here just now on the off-chance that a 6809 might have been added by someone, but no such luck! Anyway, I thought I might share my old notes for comment.

In our "PiTrex" project, which uses a Pi Zero to replace the 6809 in the Vectrex vector-drawing console, my colleague Malban improved a version of vecx for our project (and also fed back the improvements to LibRetro ( https://github.com/malbanGit/libretro-vecx )). Among other improvements, he corrected its cycle-accuracy so that many of the issues that make vector-drawing timing a problem under an emulator work better.

The ARM in the PiTrex (it's a Pi Zero piggybacked on top of the PiTrex board) emulates the CPU of a 6809 Vectrex but uses all of the Vectrex's real devices and peripherals.

This means it can drive the Vectrex hardware almost identically to how the Vectrex's 6809 drives it, when running Vectrex code - as long as the emulated timing matches the native 6809 timing. This is how we make the PiTrex behave like a multicart... (the pitrex cart hardware is too simple to allow the real 6809 to load rom data from it directly - the board's address bus was only just wide enough to access the peripheral space, we couldn't also access main memory, so we halt the 6809 and let the ARM do the work, since it has direct access to the SD on the Pi Zero...)

Malban has done really well given the constraints of a traditional emulator structure and the emulation looks absolutely perfect, but when we get to emulating various peripherals in the future, it may be that a traditional instruction-stepped emulator is going to have problems when real hardware devices have to perform actions at specific times within the instruction cycle.

There is a method of writing emulators that works in these circumstances, but at the moment it is only used by FPGA implementations of CPUs or (as far as I know) in only one soft emulator, the 6502 at floooh.github.io/2019/12/13/cycle-stepped-6502.html (or in the Z80 later added to that project)

So... here's a crude sketch of a generic cycle-ticked 6809 emulator...

m6809_t *cpu = init_6809();
hires_clock_t now = hires_timer();

// E is the external clock that drives the system...
for(;;) {
    cpu->pins->E = 1;
    wait_until_realtime(now+HALF_CLOCK_CYCLE);

    // tick the CPU for one half clock cycle
    m6809_tick(&cpu);        // do any state changes that are triggered on falling E

    cpu->pins->E = 0;
    wait_until_realtime(now+CLOCK_CYCLE);

    // tick the CPU for one half clock cycle
    m6809_tick(&cpu);        // do any state changes that are triggered on rising E

    now += CLOCK_CYCLE;
}

Unfortunately, our existing emulator (based on vecx) may be cycle-counting, but it is instruction-ticked rather than cycle-ticked, but we can fake cycle-ticking by not doing anything in the emulation procedure for the other clock cycles, eg something like this...

int emulate_m6809_instruction(m6809_t *cpu) {
  // standard emulator, returns cycles used
}

void m6809_tick(m6809_t *cpu) {

  switch (cpu->half_clock_ticks_remaining) {

  case 0:  cpu->half_clock_ticks = emulate_m6809_instruction(cpu) * 2;
           break;
  case 1:  cpu->half_clock_ticks_remaining -= 1;
           break;
  case 2:  cpu->half_clock_ticks_remaining -= 1;
           break;
  case 3:  cpu->half_clock_ticks_remaining -= 1;
           break;
  // ... up to max number of clock edges used by any instruction...

  default: cpu->half_clock_ticks_remaining -= 1;
           break;
  }
}

Clearly this isn't really a cycle-stepped emulation, because everything happens at the very first clock tick when executing each instruction, and the CPU sits idle and no pins change on the subsequent clock ticks when inputs and outputs should be changing, but at least now the framework is in place.

But... at this point you can identify the few instructions where within-instruction cycle accuracy is required and migrate those instructions from being instruction-ticked to being cycle-ticked, with the actions happening on the correct tick within the instruction; leaving instructions that are not critical to be executed on the first tick - and thus we can migrate any existing emulator slowly without breaking it... With enough effort such an emulator might eventually become genuinely cycle-ticked completely.

In the end we decided our vecx modifications were sufficient and we did not need to go to these lengths to add accurate cycle-ticking to our project, but I thought I'ld mention this potential framework here to see if you think it might be a plausible way of migrating other instruction-ticked emulators to the cycle-ticked world...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant