8

I am studying an FLI routine in http://codebase64.org/doku.php?id=base:fli_displayer and I noticed that the CLI instruction is being used in this IRQ routine:

irq0:   pha
        ...
        cli ; <<< ???
        nop
        nop
        nop
        nop
        ...
        pla
        rti

As I know it probably does not have any effect on the IRQ routine. Or does it?

20

Without issuing a CLI, no further interrupts can occur (until EOI, at least, where flags are restored by the RTI instruction). As this is a raster interrupt, you need to acknowledge that as well for another one to happen, which the seemingly benign $D019 manipulation achieves earlier in the interrupt handler, cf. the linked FLI routine.

In this specific case, the idea is to let the raster interrupt occur again, while still being in the "outer" raster interrupt handler, such that the "inner" trigger will be interrupting a stream of NOPs.

The interrupt cannot happen in the middle of the CPU executing an instruction and gets deferred to the end of the current instruction. The clock cycles needed by various instructions the CPU might be executing at the point of interrupt varies, but knowing that what's interrupted is a NOP (2 clock cycles) means the timing of the raster interrupt at this point is much more predictable (only 1 cycle jitter possible).

Any jitter in the raster IRQ timing is unacceptable for most advanced VIC effects on the C64, so most will include a similar kind of raster stabilization.

  • 3
    Good lord that's a bit of nostalgia. How do you guys remember this stuff? It has been over 20 years since I used to know this inside out. Maybe 25. – Sentinel Jul 8 '18 at 21:20
8

As I know it probably do not any effect on IRQ routine. Or do it?

No, it does not. Well, it does, but that depends.

Before entering an interrupt routine the interrupt disable flag (I) is set, preventing the CPU from accepting other interrupts so it can setup the environment needed to do its job without being interrupted again. Basically an implied SEI, creating a semaphore (lock) around the (start of) the interrupt routine.

It is (as always) good practice to keep locked sections as short as possible. So as soon as a routine has established itself and gathered whatever data is necessary to be collected undisturbed, for example detecting the source, it should enable interrupts again. After this, other interrupt sources can get served in parallel.

Of course, all of this depends on the structure of the whole system, the program(s) in control, the number of concurrent interrupts and the like.

Without knowing more of either it's impossible to say if there are effects or not.

2

As I know it probably does not have any effect on the IRQ routine. Or does it?

It does indeed: It allows other IRQs to be serviced. The 6502 I flag "masks" IRQs, and it is set automatically as soon as an IRQ is serviced and cleared implicitly by RTI. So, normally, IRQs are only ever serviced sequentially. By clearing the I flag yourself in an ISR (interrupt service routine), you actively allow this routine to be interrupted by serving a different IRQ.

The code you show here is special, containing a bunch of NOPs after the CLI. This is typically done for (almost) cycle-exact timing of the code in an ISR, normally for an IRQ requested by the VIC graphics chip. To fully understand how this works, here's some in-depth information on 6502 IRQ handling (still not complete):

An IRQ is signaled to the 6502 by pulling down the IRQ line to low level. This low level is kept until the IRQ is serviced, which needs some assistence from the code in the ISR. With multiple IRQ sources, it's first necessary to check which one requested the ISR, then acknowledge the IRQ there, so this chip stops pulling down the IRQ line. When coding graphical effects on the C64, you often only enable a single IRQ source, so checking isn't necessary any more -- just acknowledge the IRQ at the VIC and it will stop pulling the line.

Now, the 6502 also has an internal flag for "IRQ requested". During execution of an instruction, typically right before the last cycle (but that's different for a few instructions), the CPU checks the state of the IRQ line, and if it is low, the internal flag gets set. Right after each instruction, this internal flag is checked, and, if set (while the I flag is unset), servicing an IRQ is started instead of executing the next instruction.

So, the exact time when the CPU starts servicing an IRQ can vary quite a few cycles (the difference between max and min cycles for executing an instruction, plus one extra as there are instructions checking the IRQ line before the second last cycle), which is unacceptable for many graphical effects. One strategy for achieving exact timing is to have the IRQ occur while the CPU does something we know: execute a bunch of NOPs. They take 2 cycles each, so the exact time of servicing the IRQ can only vary by one cycle. This last cycle can be accounted for using another trick not relevant for this question.

Code using this trick typically restores the stackpointer in the second ISR, so the stack looks like only one ISR was called by the CPU. Therefore, a simple RTI in the second ISR will return to the main code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.