Interrupts
can be confusing though shouldn’t be. Part of the problem is that
they happen outside of the program and are designed to be silent in
their nature. The reason interrupts exist is to allow for outside
devices to let the processor know that something has happened. When
it happens, the 6502 stops executing the current program, runs an
interrupt handler, then returns to the program that was running
without the executing program being aware that it was interrupted.
The
6502 uses a vector table located at the end of address space that
holds addresses for handlers for the 3 types of interrupts that it
supports. The Atari 2600 only needs to worry about resets and IRQ
caused by issuing a BRK statement but other systems (NES) do use NMIs.
Low
byte address
|
High
byte address
|
Interrupt
|
Explanation
|
$FFFA
|
$FFFB
|
NMI
|
Non-maskable
interrupt (SEI won’t stop it)
|
$FFFC
|
$FFFD
|
Reset
|
The
processor has been reset
|
$FFFE
|
$FFFF
|
IRQ
|
Maskable
interrupt or BRK has occurred
|
When
an interrupt request (IRQ) happens the processor does the following:
1.
Push
the next IP address (high byte then low byte) onto the stack to save
current executing position with the program.
2.
Pushes
the processor status register onto the stack (essentially performs a
PHP command)
3.
Sets
the IP address to the address stored in the vector table as outlined
in the table above.
This
takes a total of seven cycles plus the time of the interrupt code
plus the six cycles for the RTI instruction which means that an
interrupt that happens during a timing loop will potentially throw
off the program’s timing by quite a bit. It is the responsibility
of the interrupt handler to make sure that any registers it uses are
restored to the values that they originally contained before issuing
the RTI.
The
RTI instruction, in case it is not obvious already, returns execution
back to the program by doing the following:
1.
Pulls
the processor status from the stack (essentially a PLP instruction).
2.
Pulls
the IP address of the next instruction that would have occurred
before the interrupt (low byte then high byte) from the stack and
sets the IP address to that value
Writing
an interrupt handler is simply a matter of saving off any registers
you use before changing the registers and restoring the registers
before calling RTI. It is my understanding that the CLI command
doesn’t need to be issued unless you want the interrupt handler to
be interruptible, which may be the case if you are performing a
complex task.
RTI
– ReTurn
from Interrupt
Address
Mode
|
Decimal
OPCode
|
Hexadecimal
OpCode
|
Size
|
Cycles
|
Implied
|
77
|
$4D
|
1
|
6
|
Flags
affected:
CDINVZ
(from stack)
Usage:
Used
by interrupt handlers to return execution back to the program once
the interrupt has been handled.
Test
Code:
LDX
$FC
;
for test, set up stack
TXS
;
the interrupt data alreay in there (see org)
CLC
;
the carry flag is set in the stack
RTI
;
return using stack data
BRK
.ORG
$1FD
.BYTE
33 8 2
.ORG
520
BCC
done ; the carry flag is set in the stack!
LDX
#42
done:
BRK
;
expect X=42
Implementation:
state.flags
= pullByteFromStack()
val
low = pullByteFromStack()
val
high = pullByteFromStack()
state.ipNext
= (high * 256 + low)
No comments:
Post a Comment