My question is: What exactly makes a code 16, 32 or 64 bit, what
triggers the incompatibility problems 16-bit applications apparently
have?
Well: the obvious answer would be: depends on that program using 8,16,32 or 64 bit registers. Obvious but not true: a program running in MS DOS can use 8,16, or 32 bit registers if the processor supports them. Alternatively, a Windows program can use (and indeed uses) 8 and 16 bit registers, besides using 32 bit registers and (if the processor and OS supports it) 64 bit registers.
The question, I think, should be: what exactly makes a program to be run under DOS or Windows 32 bits or Windows 64 bits.
First, the structure of the executable file. A 32-bit or 64-bit Windows executable file is not the same as the 16-bit executable file. Windows uses the so called "Portable Executable" (short for PE). MS DOS uses both .COM files (inherited from CP/M) and .EXE files (also known as MZ executables).
Second: one the program is stored in memory and begin executing, there some environment that the program expects to be there, for example, an army of function calls accessed by special instructions, like INT, which are in charge of performing different task, from outputting a character to the console, to open and read files, and many more.
Third: MS DOS applications expects some structures in memory to be at a specific memory address; for example the screen memory. Also it expects some I/O devices to exist at a specific I/O address. Many so called "bad behavioured" applications don't use BIOS or DOS to print on screen, but write directly to screen memory, for faster output. I remember a setting in Turbo Pascal, Turbo Basic, or maybe Turbo C compiler, to let the programmer choose console output functions to go through BIOS, or to go directly to hardware.
Fourth: the program expects the processor to behave in a particular way. 16-bit DOS programs are designed to use the so called Real Mode of x86 processors, and this mode imposes its particular vision of memory addresses, to name the most distinct feature of this mode. With the arriving of 32-bit OS's that puts the processor in Protected Mode, real mode programs cannot work because the way memory addresses work in protected mode is very different from the way it worked for real mode.
An OS willing to run MZ executables need some sort of loader for these files (to comply with the first point) and an environment that has to mimic the one the application should work into (to comply with the rest of points). In the Linux world these components are part of what they call personalities (a way for Linux to run code from other OS's such as FreeBSD, providing that code is intended for the same processor on both platforms).
In the Windows world this is known as subsystems. Windows XP and Windows 7, for example, have the Win32 subsystem and the DOS subsystem. Ancient versions of Windows (Windows NT 3.5 or NT 4.0 perhaps) had an OS/2 personality too, capable of running OS/2 16-bit text mode applications.
So, your Windows OS need the appropiate subsystem to run code designed for another OS like MS DOS. But the processor must help in any possible way. This is because the x86 platform introduced the 8086 virtual mode in the 80386: to be able to create a task in which the processor behaves much like a real mode 8086.
So with assistance from the CPU itself, the appropiate loader, an INT handler capable of translating DOS request into Windows request, an intelligent page handler that is able to catch accesses to specific sections of memory and translate them into requests for drawing characters on screen, and a I/O fault handler capable of impersonate the devices that the application used to work with, a program written for DOS can run under Windows, or Linux (DOSemu)
So, what happens with 64 bits? For a code to be able to run with access to 64-bit registers, another kind of protected mode is employed, called long mode. I don't know much about this long mode, but from what I have read, this mode doesn't allow the processor to create a task using the 8086 virtual mode. There is another mode called compatibility mode or something like that, that allows the processor to create 64 and 32-bit tasks, but no 8086 virtual mode tasks.
Without CPU assistance, a DOS subsystem is much difficult to achieve: you could mimic the memory map, trap any access to special memory regions or I/O, trap INT instructions, but besides, you should trap any attempt to update the segment registers in order to try to mimic the real mode addressing as well, and I'm not actually sure segments are in use in long mode. It's doable, as applications like DOSBox prove it, but Microsoft doesn't need DOS anymore (it hasn't need it for a long time, but customers didn't share their point until, perhaps, the introduction of Windows 2000/XP). Windows 7 32-bits can use the same DOS subsystem as previous Windows versions used (I even read some time ago about a hack to bring the OS/2 subsystem back into Windows 2000 or Windows XP), but for Windows 7 64-bits, a complete new DOS subsystem had to be designed (or bought).
More info here:
http://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for#Main