This might be obvious, but one of the key activities is to minimize platform-dependent code so that most of the game logic is portable and shared across all platforms. So no Win32-isms or Cocoa-isms or <unistd.h> littered all over your game.
One way (out of many) to do this is have a cross-platform interface for each non-portable thing, with separate platform-specific implementation files that you swap in depending on which platform build you're doing. For example, if your game has sound, you'll probably have a platform-independent sound.h the rest of the game calls into, and then sound_windows.c, sound_linux.c, sound_mac.c, etc. files that contain the platform-specific implementations of those functions. Repeat for graphics, input, and other things that cannot be done (or you don't want to do) in a portable way.
Another thing is the game loop. Back in the DOS days, when you were the only thing running on the system, you implement main() did something like:
while (game_running) { do_everything(); }
Modern platforms expect to be in the driver seat, and will instead call into your code, so this turns into something like:
function windows_call_me_please() {
do_everything();
}
Obviously this is a simplification, and there is a lot more to it, but hopefully it answers some basic questions.
Also choosing a base platform agnostic framework like SDL which has a lot of the OS interactions (keyboard & mouse) already abstracted away for you gets you pretty far on the cross-platform road.
One way (out of many) to do this is have a cross-platform interface for each non-portable thing, with separate platform-specific implementation files that you swap in depending on which platform build you're doing. For example, if your game has sound, you'll probably have a platform-independent sound.h the rest of the game calls into, and then sound_windows.c, sound_linux.c, sound_mac.c, etc. files that contain the platform-specific implementations of those functions. Repeat for graphics, input, and other things that cannot be done (or you don't want to do) in a portable way.
Another thing is the game loop. Back in the DOS days, when you were the only thing running on the system, you implement main() did something like:
Modern platforms expect to be in the driver seat, and will instead call into your code, so this turns into something like: Obviously this is a simplification, and there is a lot more to it, but hopefully it answers some basic questions.