You have the .code16 directive in there before the actual switch to real mode. As a result of that, the assembler emits an operand size override that is meant for real mode code, even though the following three instructions are still executed in protected mode. My guess is that this results in the CPU actually executing:
Code: Select all
movl %cr0,%ax
andl $0xfffffffe,%ax
movl %ax,%cr0
Or even worse because it interprets the now-redundant additional 16 bits of the operand 0xFFFF as a separate instruction, which is obviously not going to work.
I'm not sure if that is the problem, but try putting the .code16 directive after the 'movl %eax,%cr0' instruction.
EDIT: I tried assembling it in 16 bit mode and then disassembling in 32 bits mode, and this is what I got (NASM syntax):
Code: Select all
00000000 0F20C0 mov eax,cr0
00000003 6625FEFF and ax,0xfffe
00000007 FF db 0xFF
00000008 FF0F dec dword [edi]
0000000A 22C0 and al,al
0000000C EA11000000B800 jmp dword 0xb8:0x11
00000013 B88ED8C606 mov eax,0x6c6d88e
00000018 0000 add [eax],al
0000001A 41 inc ecx
0000001B C60601 mov byte [esi],0x1
0000001E 001E add [esi],bl
00000020 EBFE jmp short 0x20
Which is obviously completely messed up. The byte 0x66 at the start of the second instruction says 'this instruction's registers are the non-default type'. The assembler adds this because the instruction uses eax, which is not the default size (ax is the default size because of the .code16). However when the CPU actually runs it, it's running in 32 bit mode, so the non-default size is now 16 bits, using ax. As a result, it only actually grabs the first two bytes of the operand, 0xFFFE, and assumes that the following 0xFFFF are part of the next instruction. Then it tries to execute 0xFF which according to NDISASM isn't even a valid instruction, so QEMU is probably dumping registers as a result of an unhandled 'invalid opcode' exception. And even if 0xFF had been a valid instruction, the program would never work, as the instructions are misaligned.