Well, I think I found the VERY problem of this VERY kernel version.
A 16-bit iret (IRET instead of IRETD, returns 16-bit regs, not 32-bit) is causing a stack corruption. It looks like only half of the registers are being restored when returning from INTERRUPT. Try changing the opcode prefix for 16-bit registers operation, 0x66, by a simple NOP instruction 0x90 to solve it. It must be done because bytes cannot be easily removed from a readily linked executable without getting further instructions with wrong addresses.
Look at the byte at the physical, actual offset 0x0000169C inside the file HACK.EXE and KERNEL.EXE:
------------------------------------
| 169C: 66 CF IRET | 16-bit registers
------------------------------------
Modified by:
------------------------------------
| 169C: 90 CF IRETD | 32-bit registers
------------------------------------
I have attached a new disk image with a new entry called "Hacked MSVC Kernel". Note that I didn't recompile it with Visual C, the only thing I did was to change the byte 0x66 by a NOP 0x90.
It is sure that if you keep using MSVC and if there is really not a way of avoiding and making sure that it isn't putting prefixes for 16-bit instructions in 32-bit code, then you will have to be editing manually your machine code in the worst case, and is very likely that there will be many more hard to find instructions with problems like this or other not foreseen at the moment.
Maybe it would be better to use the original tools (mostly GCC/NASM, etc.) indicated by each tutorial for not overcomplicating things and not facing nearly impossible bugs.
I hope that the attached disk image works fine for you as well, but you must bear in mind that whenever you recompile this code or code like this you will keep facing this problem. My last bet is that MSVC is producing 16-bit code (isn't it MSVC6, or maybe its built-in assembler produces it for low level inline assembly like IRET?). Maybe there is some way to produce fully 32-bit code as in nasm by something like:
Which in fact would only produce the opcode byte for 0xCF, automatically interpreted as a 32-bit interrupt return if in protected mode.
------------------------------------------
HINT: Try replacing all the occurrences of the mnemonic IRET with IRETD (note the D at the end denoting that it will pop DWORDs out of the stack on interrupt return); maybe that is the easy fix.