Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breakpoints at the beginning of exception handlers are ignored #1

Closed
dirkwhoffmann opened this issue Jan 16, 2020 · 5 comments
Closed
Labels
bug Something isn't working

Comments

@dirkwhoffmann
Copy link
Owner

Posted by Elmer:

I actually run into one issue concerning breakpoints at the start of exception handlers:
It is possible (of course) to add a breakpoint at the specific address, but the system is unable to detect it
The reason seems to be that the execute() function is atomic:
First it checks for changed ipl levels, if necessary initiates the exception (during which an address is loaded from the auto-vector table).
Subsequently, in one go, it executes the first instruction of the exception code, after which the address of the next instruction will be checked for a breakpoint.

@dirkwhoffmann dirkwhoffmann added the bug Something isn't working label Jan 16, 2020
@dirkwhoffmann
Copy link
Owner Author

I need to think about it in more detail, but the most obvious solution might be to simply skip the exec call in Moira::execute() if a trace or interrupt exception has occurred.

void
Moira::execute()
{
    // Process execution flags (if any)
    if (flags) {

        // Process pending trace exception (if any)
        if (flags & CPU_TRACE_EXCEPTION) {
            execTraceException();
        }

        // Check if the T flag is set inside the status register
        if (flags & CPU_TRACE_FLAG) {
            flags |= CPU_TRACE_EXCEPTION;

>>> ADD LINE:        goto exit;
        }

        // Process pending interrupt (if any)
        if (flags & CPU_CHECK_IRQ) {
            checkForIrq();

>>> ADD LINE:       IF "IRQ HAS TRIGGERED" goto exit;

        }

        // If the CPU is stopped, poll the IPL lines and return
        if (flags & CPU_IS_STOPPED) {
            pollIrq();
            sync(MIMIC_MUSASHI ? 1 : 2);
            return;
        }

        // If logging is enabled, record the executed instruction
        if (flags & CPU_LOG_INSTRUCTION) {
            debugger.logInstruction();
        }
    }

    // Execute the instruction
    reg.pc += 2;
    (this->*exec[queue.ird])(queue.ird);

>>> ADD LABEL:  exit:

    // Check if a breakpoint has been reached
    if (debugger.breakpoints.needsCheck)
        if (debugger.breakpointMatches(reg.pc)) breakpointReached(reg.pc);
}

@dirkwhoffmann
Copy link
Owner Author

@elmerucr: I think I have a decent fix for the breakpoint issue. My plan is to change the execution function as follows:

void
Moira::execute()
{
    //
    // The quick execution path: Call the instruction handler and return
    //

    if (!flags) {

        reg.pc += 2;
        (this->*exec[queue.ird])(queue.ird);
        return;
    }

    //
    // The slow execution path: Process flags one by one
    //

    // Process pending trace exception (if any)
    if (flags & CPU_TRACE_EXCEPTION) {
        execTraceException();
        goto done;
    }

    // Check if the T flag is set inside the status register
    if (flags & CPU_TRACE_FLAG) {
        flags |= CPU_TRACE_EXCEPTION;
    }

    // Process pending interrupt (if any)
    if (flags & CPU_CHECK_IRQ) {
        if (checkForIrq()) goto done;
    }

    // If the CPU is stopped, poll the IPL lines and return
    if (flags & CPU_IS_STOPPED) {
        pollIrq();
        sync(MIMIC_MUSASHI ? 1 : 2);
        return;
    }

    // If logging is enabled, record the executed instruction
    if (flags & CPU_LOG_INSTRUCTION) {
        debugger.logInstruction();
    }

    // Execute the instruction
    reg.pc += 2;
    (this->*exec[queue.ird])(queue.ird);

done:

    // Check if a breakpoint has been reached
    if (flags & CPU_CHECK_BP)
        if (debugger.breakpointMatches(reg.pc)) breakpointReached(reg.pc);
} 

In addition to returning directly from this function after executing an exception condition, I have moved breakpoint checking into the "slow execution path". Now, the "quick execution path" only consists of a single call to the instruction handler which should slightly speed up emulation (I guess, the quick execution path is called 99% of the time under normal operating conditions).

I haven't checked in the code yet, but I did some experiments with it in vAmiga. With the new code, I was successful to interrupt the CPU right at the beginning of the VBLANK IRQ handler.

Bildschirmfoto 2020-01-18 um 13 09 44

@elmerucr
Copy link

Looks like a good solution! Are you going to commit the changes to the repository?

@dirkwhoffmann
Copy link
Owner Author

New code has been checked in and uploaded.

@elmerucr
Copy link

Thanks, code works great so far!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants