You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

6510模拟器Klaus Dormann中断测试异常:NMI频繁触发求助

6510模拟器NMI误触发及IRQ电平敏感处理修复方案

问题核心

用Klaus Dormann的6502测试集验证自研6510模拟器时,除中断模块外功能正常,但NMI持续误触发。结合提示的两个关键问题:

  • IRQ是电平敏感,而非边沿敏感
  • 当前代码错误地在低电平转高电平时触发IRQ

从提供的代码来看,核心逻辑错误集中在中断触发判断、NMI规则实现两部分。

代码修复方案

1. 重构边沿/电平检测函数

原changed函数仅能检测低→高边沿,无法满足NMI和IRQ的不同触发需求,需扩展为更灵活的检测函数:

// 检测指定bit的电平是否发生变化
static inline bool bit_changed(int old_val, int new_val, int bit) {
    return ((old_val & bit) != (new_val & bit));
}

// 检测指定bit是否从高电平转为低电平(负边沿,适配NMI触发规则)
static inline bool high_to_low(int old_val, int new_val, int bit) {
    return ((old_val & bit) != 0) && ((new_val & bit) == 0);
}

2. 重写中断处理逻辑

严格区分NMI和IRQ的触发规则:

  • NMI:负边沿触发(高→低),一旦触发必须响应(不受IRQ屏蔽位影响)
  • IRQ:低电平有效且电平敏感,仅当IRQ屏蔽位未置位时,在指令执行结束后响应
void interrupt_handler(MOS_6510* const c) {
    // 处理NMI:高→低负边沿触发,仅触发一次
    if (high_to_low(c->old_status, c->irq_status, 0x2)) {
        NMI(c);
    }

    // 处理IRQ:低电平有效+电平敏感,未屏蔽时响应
    // 需避免持续低电平下重复触发,所以仅在电平从高转低时响应一次
    if (bit_changed(c->old_status, c->irq_status, 0x1) && !(c->irq_status & 0x1)) {
        if (!((c->p >> IRQ_DISABLE_FLAG) & 1)) {
            IRQ(c);
        }
    }

    // 更新旧状态,用于下一轮检测
    c->old_status = c->irq_status;
}

3. 修正NMI函数的错误标志位设置

原NMI函数错误设置了BREAK_COMMAND_FLAG,该标志仅在BRK指令触发中断时需要置位,NMI无需处理:

void NMI(MOS_6510* const c) {
    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;
}

4. 调整执行循环的状态读取逻辑

将irq_status的读取移至循环内部,确保每次迭代都获取最新的中断引脚状态:

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

关键规则说明

  • NMI触发逻辑:6502/6510的NMI为负边沿触发,仅在引脚从高电平转为低电平时触发一次,即使电平持续低,也不会重复触发,直到恢复高电平后再次变低。
  • IRQ电平敏感逻辑:IRQ引脚保持低电平时,只要IRQ屏蔽位未置位,CPU会在每条指令执行结束后响应一次;若电平持续低,需等待电平恢复高后再次变低,才会触发下一次响应,避免无限循环触发。

内容的提问来源于stack exchange,提问作者cdunku

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:52:51