Is the ISR(....) a macro that does all this? or does the compiler do it? What level do I need to be concerned about this?
for AVRs using avr-gcc, ISR(name) is a macro that tells the compiler to do the things necessary for the following function to work as an interrupt service routine. It's compiler and target dependent, and you have to use a correct "name" for it to be effective.
Some of the AVR interrupts already have "handlers" in the Arduino core, so you won't be able to change them without editing some core source code as well. Other interrupts are NOT used by the core, and you can make your own handlers.
In addition, the Arduino libraries have "attachInterrupt()" which will allow you to set up a normal C function as the target for certain types of AVR interrupts at runtime (normally, interrupt wrappers are at a fixed location in flash, not changeable at run-time. attachInterrupt() has a wrapper function at the fixed location for that interrupt that does the important context saving, and then calls the user-specified function (incurring quite a bit of overhead in the process, but that's life...)
For the ARM chips, the ARM hardware saves exactly the same registers that the C ABI requires be saved, so the compiler doesn't need to do anything special. You just need to have a normal C function with the same name that the startup code puts in the vector table.
STM32: attachInterrupt(<PIN>, <FUNCTION>, <MODE>); --- attachInterrupt(PB8, interruptFunction, CHANGE);
"attachInterrupt()" is normally a significantly different thing than defining an interrupt handler. For Arduino (incuding the above STM32 example, I think) it attaches a C function to the pin change interrupt for a particular pin, which usually involves manipulating the GPIO peripheral, and storing a pointer to the user function in a separate table. (usually there is one pin-change interrupt per port, so the actual ISR has to do some decoding to figure out which individual-pin handler to call.)