It turns out that in MS C 5.1, in addition to all the /O... options there is one seemingly unrelated one that influences the optimizations for this compiler: /Zi. In the output of CL /help it is described as:
/Zi symbolic debugging information
I have been ignoring it for a long time because the executable does not contain any symbols. However, enabling it and building the source with CL /Gs /Zi /c src.c caused the compiler to generate the exact code I was looking for from a simple for loop:
for (timerCounter = 0; timerCounter < 0x78;) { if (check_keybuf() == 0) { getkey(); break; } } The fine manual offers some explanation to what this option does exactly:
/Zi: Creates object file for use with Microsoft Code View debugger
The /Zi option produces an object file containing full symbolic-debugging information for use with the CodeView debugger. This object file includes full symbol-table information and line numbers. If the /Zi option is given with no explicit /O options, all optimizations involving code motion and rearrangement are suppressed, although simple optimizations are still performed. If any explicit /O options are given, all requested optimizations are performed.
This explains why the code seems partially optimized; it only gets rid of optimizations that could interfere with debugging. Also, there are no symbols in the executable because the symbols were put in the object files instead for the debugger to use.
Anyway, with this option enabled, I'm getting exactly matching opcodes here and elsewhere.