It is currently Fri Apr 27, 2018 1:51 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Fri Dec 15, 2017 7:04 pm 
Offline
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 765
Location: New York, NY
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?


Top
 Profile  
 
PostPosted: Sat Dec 16, 2017 12:02 pm 
Offline
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 765
Location: New York, NY
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.


Top
 Profile  
 
PostPosted: Sat Dec 16, 2017 2:59 pm 
Offline
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 765
Location: New York, NY
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group