mre / mos6502

MOS 6502 emulator written in Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

No handling of NMI or IRQ

omarandlorraine opened this issue · comments

I don't see a way to send an interrupt or reset or anything to the CPU.

I would guess this belongs in the Bus trait if #63 gets merged

We could add it there, yes.
I was thinking of a separate trait o stay platform-agnostic and separate concerns, but that would be overkill.

pub trait InterruptHandler {
    fn set_nmi(&mut self, value: bool);
    fn set_irq(&mut self, value: bool);
}

Let's keep it simple.

trait Bus {
    fn set_byte(&mut self, address: u16, value: u8);
    fn get_byte(&self, address: u16) -> u8;
    fn trigger_irq(&mut self);
}

I think it depends on how we expect client code to actually call the mos6502 library.

To emulate something like the Atari 2600, or any of the other well-known platforms, you'll need to run your own code with every machine cycle. So for example libz80, and Mike Chambers fake6502, and others have a callback to do this and our case is similar (the get_byte and set_byte methods are our analogue to the machine-cycle callback).

Therefore, a Bus object needs to know when an interrupt is pending, and needs to communicate this fact to the CPU. This point of view seems to suggest that the trait should look something like this:

trait Bus {
    fn set_byte(&mut self, address: u16, value: u8);
    fn get_byte(&self, address: u16) -> u8;
    fn irq_pending(&mut self) -> bool;
    fn nmi_pending(&self) -> bool;
    fn reset_pending(&mut self) -> bool;
}

I'm imagining that a Commodore 64 emulator will set a flag in the Bus object to raise the interrupt, so for IRQ and reset we have the method take a mutable reference to self so that the method can clear that flag. Then the CPU core can call any of the *_pending methods to determine whether to execute an instruction or to handle one of the interrupts.

How does that idea seem to you?

Yeah, that sounds quite reasonable.
I have two minor concerns:

  1. The trait is getting quite big. I tend to lean towards smaller traits to allow for composability.
  2. Are there any use-cases that don't use interrupts or resets? If so, it might make sense to separate the interrupt methods into a separate trait (that could be optionally implemented by the Bus).

I'd also be fine with the current proposal, though.