Writing one program for different microcontrollers
The trick is to define an interface that gives you access to the I/O pins (or other peripherals and functionality that you need, and maybe a delay functions). Declare this interface in a .h file. Now write your application in one or more .c files that use this .h file. Write two .c files, one for each platfom, that implement the interface for that particular platform. When you build for one platform, compile and link with the correct implementation .c file.

A possible .h file:

bool pin_read( unsigned char pin );
void pin_write( unsigned char pin, bool value );
void configure_pin_as_input(  unsigned char pin );
void configure_pin_as_output(  unsigned char pin );
void delay_us( unsigned int t );


This approach does not require any preprocessor trickery, but its does impose a function call overhead on each access to a platform-specific function. If this is not acceptable, you could implement both interfaces as macro's in the .h file (using #ifdefs to select between them). 

Another trick is to stick with the .h/.c approach, but to compile all application code with the implementation.c included, and rely on the  optimizer to eliminate the function call overhead. This requires two different 'main' files, one for the 16-bit target

// main.c for MSP430
#include "interface.h"
#include "msp430-interface.c"
#include "application.c"


and one for the other target

// main.c for Cortex-M0
#include "interface.h"
#include "CortexM0-interface.c"
#include "application.c"


Personally I prefer C++ to tackle such problems, and I essentialy use the last approach, which is quite normal for C++ template-based programming (templates must be in the .h file). (See this article and this video for my approach)