On the Mega 2560, normal external interrupts are available on pins 2, 3, 18, 19, 20, and 21. These can be attached using attachInterrupt and support modes like RISING, FALLING, or CHANGE.
If that's not enough, you can use pin change interrupts, which are available through three groups: PCINT0 for digital pins 10 to 13 and 50 to 53, PCINT1 for analog inputs A8 to A15, and PCINT2 for analog inputs A0 to A7.
Each PCINT group has its own mask register: PCMSK0 for PCINT0 (PORTB), PCMSK1 for PCINT1 (PORTJ), and PCMSK2 for PCINT2 (PORTK). By setting individual bits in these registers, you enable pin change interrupts only for the specific pins you want. The ISR still triggers for the whole port, but only masked pins can cause it.
These interrupts are PORT based, meaning that when any enabled pin in a PCINT group changes state, the corresponding ISR is triggered, but it does not specify which pin caused the change, so software must detect it.
This can be easy if there is only one pin in the PCINT group that can trigger the ISR. If you have activated more than one in the group, then it's up to you to track the change in the ISR.
The typical way to do this is to declare a static byte to hold the previous port state. Read the current port input register for the PCINT group. Compare the current state with the stored previous state using XOR to find which pins changed. Update the static byte with the current state for the next interrupt. Then check which specific pins changed by testing bits in the XOR result. This lets you detect exactly which pins triggered the interrupt.
May be something like that (typed here not tested) for PORTB (PCINT0 group)
ISR(PCINT0_vect) {
const byte pinMask = 1 << PCINT3; // PCINT3 is BIT #3 so pin 50 in PORTB
static byte prevState = 0;
byte currState = PINB;
byte changedPins = currState ^ prevState;
prevState = currState;
if (changedPins & pinMask) {
if (currState & pinMask) {
// Pin 50 went HIGH
} else {
// Pin 50 went LOW
}
}
}