6510 Interrupt Handler for the 6502 Klaus Dormman Interrupt test

121 views Asked by At

So I have been testing my 6510 emulator using the 6502 Klaus Dormann tests. Everything seems to be right except the interrupt testing. I have looked around and did some research and found out that I need to create an interrupt handler. But for whatever reason NMI is being triggered every time. One fellow discord user mentioned this (while I was looking for a solution):

Message 1: "keep in mind that IRQ is level sensitive, not edge sensitive."

Message 2: "if I am following your code correctly it looks like you are triggering the IRQ when there is a low to high transition which would not be correct."

Here is the code for better reference:

static inline bool 
changed(int ov, int nv, int bit) {
  return ((ov & bit) == 0 && (nv & bit) != 0);
}

Interrupt handler:

void 
interrupt_handler(MOS_6510* const c)
{
  if(changed(c->old_status, c->irq_status, 0x2))
  {
    NMI(c);
    c->old_status |= 0x2;
  }
  else if(changed(c->old_status, c->irq_status, 0x1))
  {
    if(IRQ(c)) c->old_status |= 0x1;
  }
  else if(changed(c->irq_status, c->old_status, 0x2))
  {
    c->old_status &= ~0x2;
  }
  else if(changed(c->irq_status, c->old_status, 0x1))
  {
    c->old_status &= ~0x1;
  }
}

Execution loop:

  c->irq_status = rb(c, 0xBFFC);
  while(true) 
  {
    interrupt_handler(c);
    mnemonics(c);
    ...
}

Here are my IRQ and NMI implementations:

bool
IRQ(MOS_6510* const c)
{
  if((c->p >> IRQ_DISABLE_FLAG) & 1) return false;

  push_word(c, c->pc);
  push_byte(c, c->p);

  change_bit(c->p, IRQ_DISABLE_FLAG, 1);

  c->pc = rw(c, 0xFFFE);

  c->cyc += 7;

  return true;
}

void
NMI(MOS_6510* const c)
{
  change_bit(c->p, BREAK_COMMAND_FLAG, 1);
  push_word(c, c->pc);
  push_byte(c, c->p);

  change_bit(c->p, IRQ_DISABLE_FLAG, 1);

  c->pc = rw(c, 0xFFFA);

  c->cyc += 7;
}

I think I have posted all the necessary information, if I need to edit the post please let me know!

Thank you in advance!

1

There are 1 answers

1
JeremyP On

if I am following your code correctly it looks like you are triggering the IRQ when there is a low to high transition which would not be correct.

Because an interrupt is triggered when the level of IRQ is high. You should be triggering an interrupt on (c->irq_status & 0x1) != 0 regardless of what its old value is. With the 6502, if the "not IRQ" pin remains low1, when you reenable interrupts, the interrupt will immediately be triggered again. The interrupt routine has to clear the source, which means that it has to tell the chip that raised the interrupt that the interrupt has been serviced (this can often happen automatically as per Weather Vane's second comment on the question).

On the 6502, NMI is edge triggered, so the logic you have here is correct. However, you start with your NMI flag set to true (bit 2 of 0xBFFC is set) and I am assuming you have c->old_status initialised to 0, probably, but you don't show that code. That means NMI will always trigger the first time through.


1To assert an interrupt on the 6502, you pull the relevant pin to ground. I'm assuming you have inverted that internally to make it less confusing.