nesdev.com
https://forums.nesdev.com/

Does interrupt check happen after fetch?
https://forums.nesdev.com/viewtopic.php?f=20&t=16836
Page 1 of 1

Author:  zeroone [ Fri Dec 15, 2017 7:04 pm ]
Post subject:  Does interrupt check happen after fetch?

Here's the fetch function from Gekkio's source:

Code:
  fn fetch_cycle<H: Bus>(&mut self, bus: &mut H) {
    let result = bus.fetch_cycle(self.regs.pc);
    let interrupt = match self.ime {
      Ime::Enabled => result.interrupt,
      Ime::Enabling => {
        self.ime = Ime::Enabled;
        false
      },
      _ => false,
    };
    if interrupt {
      self.dispatch_interrupt(bus)
    } else {
      self.regs.pc = self.regs.pc.wrapping_add(1);
      ops::decode((self, bus), result.opcode)
    }
  }


It appears to perform the interrupt check after fetch rather than in between instructions. Is that actually the case? And, does this mean that a return from interrupt jumps to the byte after the opcode?

Author:  zeroone [ Sat Dec 16, 2017 12:02 pm ]
Post subject:  Re: Does interrupt check happen after fetch?

I studied the code a bit further. Here's the Bus fetch_cycle function:

Code:
  fn fetch_cycle(&mut self, addr: u16) -> FetchResult {
    let interrupt = self.has_interrupt();
    self.emu_time += EmuDuration::machine_cycles(1);
    self.emulate();

    let opcode = self.read_internal(addr);
    FetchResult {
      opcode,
      interrupt,
    }
  }


self.emulate() steps the OAM module, the timer, the GPU and the APU. That step potentially transfers 1 byte via OAM, it increments the timer register by 4, etc. In other words, the opcode read takes 1 M-cycle, as expected.

However, before it calls self.emulate(), it checks the interrupt flags. Meaning, if the interrupt flags change during the read M-cycle, that change is not reflected in the returned pair.

Again, here's the code that invokes it:

Code:
  fn fetch_cycle<H: Bus>(&mut self, bus: &mut H) {
    let result = bus.fetch_cycle(self.regs.pc);
    let interrupt = match self.ime {
      Ime::Enabled => result.interrupt,
      Ime::Enabling => {
        self.ime = Ime::Enabled;
        false
      },
      _ => false,
    };
    if interrupt {
      self.dispatch_interrupt(bus)
    } else {
      self.regs.pc = self.regs.pc.wrapping_add(1);
      ops::decode((self, bus), result.opcode)
    }
  }


Note that if an interrupt did not occur, then PC is incremented and the opcode is decoded, completing the fetch.

But, if an interrupt occurs, then PC is not incremented. Rather, this happens:

Code:
  fn dispatch_interrupt<H: Bus>(&mut self, bus: &mut H) {
    self.halt = false;
    self.ime = Ime::Disabled;
    self.internal_cycle(bus);
    self.internal_cycle(bus);
    let pc = self.regs.pc;
    self.push_u8(bus, (pc >> 8) as u8);
    let interrupt = bus.ack_interrupt();
    self.push_u8(bus, pc as u8);
    self.regs.pc = interrupt.map(|i| i.get_addr()).unwrap_or(0x0000);
  }


Both self.internal_cycle() and self.push_u8() consume an M-cycle. So, the opcode read + the 2 internal cycles + the 2 push cycles = 5 M-cycles, the expected duration of interrupt handling. It's just very odd that the opcode read cycle happens as part of that chain rather than just having 3 internal cycles prior to the 2 pushes.

It's also interesting that the IF bit is cleared in between the pushes. I wonder if that was motivated by some timing test.

Long story short, all the timings make sense. It does check for interrupts in between instructions. And, of course, it handles interrupts only in between instructions. What is unusual is that the opcode read happens even when the interrupt is handled. Since it's reading from PC as opposed to a memory mapped register address, it seems unlikely that opcode read would make much of a difference as compared to an idle M-cycle. That said, why the code was written this was is unclear.

Author:  zeroone [ Sat Dec 16, 2017 2:59 pm ]
Post subject:  Re: Does interrupt check happen after fetch?

I just realized that Gekkio's emulator doesn't pass most of the test roms that I was actually interested in. Gekkio created the tests, but doesn't provide the solutions :( The strange code above might not explain much at all.

Page 1 of 1 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/