The usual advantage of SW interrupts is that they permit a "barrier" between the user program and the "operating system" providing services. The OS can implement a bunch of functions, and the user program doesn't have to have any knowledge of exactly where those functions reside; the SW interrupts are "absolute", and with a bit of care in defining the APIs, the user program doesn't need to know anything about the internal structure of the OS.
On "advanced" processors, the SWI may change other context as well - "privilege level", memory mapping, stack pointer, different register sets...
But an AVR Arudino sketch doesn't have an operating system. It has one piece of monolithic code that knows everything about itself, and can (and does) use absolute addresses determined at link time to invoke all the services from "the Core."
The Arduino environment DOES have "pointers to functions" that can be used as you described. You can build your own "SWI" code based on a table of pointers to functions, and it will be essentially identical to the services that a true SWI capability would provide. (In fact, the HW interrupts on AVR are very primitive - the only thing they do differently than a CALL instruction is disable further interrupts. Also, the AVR interrupt vectors are in flash, and are NOT writable at run-time.)
The Arduino attachInterrupt() function is an example of using pointers-to-functions to provide the sort of capabilities that you describe:
static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS] = {
nothing,
:
};
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
intFunc[interruptNum] = userFunc;
: