Protecting 6502 decimal mode code from interrupts
Decimal mode does not need to be protected from interrupts.
Decimal mode is not cancelled/changed by interrupt routines(*1).
- Decimal mode is recorded in a status register flag (SED/CLD)
- When entering an interrupt, the status register is pushed on the stack
- When leaving an interrupt it is pulled back
See also the Interrupts and Decimal Mode remarks of the 6502 Decimal Mode Tutorial
If at all, then buggy interrupt routines may need to be protected from decimal mode :))
A common idiom in 6502 assembly code is to protect sequences of code that rely on decimal mode from being interrupted.
Serious? I have never seen that sequence in 40 years of 6502 programming - at least not for the purpose mentioned. It does not make much sense - except, see below.
Also, disabling interrupts does not disable NMI (or RESET), so even with that 'protection' an NMI may come along.
This because an interrupt handler typically clears decimal mode
Well, yes, interrupt code using ADC/SBC needs to set the mode it expects - usually binary. After all, an interrupt can happen any time, with decimal set or cleared, so the called interrupt routine has to set it the way needed. It does not have to set it back, as that's inherently done by RTI.
Fringe cases where it may make sense
One very, very rare case where it would make sense, is if an (expected) interrupt routine has no room for the two clock cycles needed to clear the decimal flag (CLD).
Except, such a routine would usually as well be delicate to delayed interrupt activation - which is what that code does.
The other, maybe a bit more common one, is to accommodate interrupt routines that do not clear the decimal flag, but do assume it to be cleared and which can not be changed or enhanced to handle the decimal flag.
This is usually the case when a program has been added to an environment were such a routine is already existing, stored in ROM with the interrupt pointer as well in ROM and pointing direct to this routine, with no way to hook.
In all other cases, it should be possible to hook the interrupt, clear decimal and continue with the existing routine.
Well, or changing the CPU to a 65C02, which does clear the decimal flag after saving the flag register, but before entering the interrupt code.
The 'Better' Approach
Well, saving the interrupt enable flag, before disabling is for sure not a bad idea, but it's slow and only to be taken if one has absolute no idea about the environment the routine is used in. Otherwise it is, like disabling interrupts when doing decimal, a great waste of time and resources.
It's the Environment, Stupid.
Saving the decimal state and even more so disabling interrupts should be avoided and never become the default case.
Using/setting decimal should be, like any other state and data changing behaviour, be part of a routines description, enabling users of such routines to work accordingly - like simply (re-)setting the wanted state after a subroutine again.
Disabling interrupts is even worse, as it changes the response to interrupts in a rather random manner, creating the worst kind of bugs to be searched. Just imagine some machine using raster interrupts to manage screen output. If some foreground program disables interrupts not synchronized with screen timing, some of the raster interrupts may sometimes fall a bit late, creating ugly glitches. Good luck in finding why and which routine and when does create one.
It's almost universally better to make sure the interrupts are compatible with either setting of the decimal flag.
*1 - Except for processing time taken and unless the interrupt code does willingly manipulate the saved flag register or returns without using an RTI.