Please examine the following assembly code from a 1980s IBM PC computer game. This game uses CGA graphics, PC speaker sound and keyboard input. The following code has been observed to be executed while the game is at a screen reading "LEVEL 1", before actual gameplay has begun. FUN_0000_5d86 is the handler for INT 1C, which is automatically called on each clock tick by the INT 08 handler. INT 8 is generated 18.2 times per second by channel 0 of the 8254 system timer.

FUN_0000_2e48()
PUSH       BP
SUB        SP,0x6
MOV        BP,SP
MOV        AX,0x28
PUSH       AX
PUSH       AX
PUSH       word ptr [0x2968]
CALL       FUN_0000_5ad6
MOV        SP,BP
MOV        AL,[0x17c]
XOR        AH,AH
SHL        AX,0x1
MOV        SI,AX
MOV        AX,0x64
PUSH       AX
MOV        AX,0x50
PUSH       AX
PUSH       word ptr [SI + 0x296a]
CALL       FUN_0000_5ad6
MOV        SP,BP
MOV        AL,[0x17a]
XOR        AH,AH
CMP        AX,0x3
MOV        word ptr [BP + 0x0],AX
MOV        [0x17b],AL
JGE        LAB_0000_2e8c
INC        AX
MOV        [0x17a],AL
JMP        LAB_0000_2e91
                             LAB_0000_2e8c
MOV        byte ptr [0x17a],0x0
                             LAB_0000_2e91
MOV        AL,[0x17b]
XOR        AH,AH
MOV        SI,AX
MOV        AL,byte ptr [SI + 0x176]
XOR        AH,AH
MOV        BL,byte ptr [0x17a]
XOR        BH,BH
MOV        CL,byte ptr [BX + 0x176]
XOR        CH,CH
PUSH       CX
PUSH       AX
PUSH       word ptr [0x2968]
CALL       FUN_0000_50b9
MOV        SP,BP
MOV        AL,[0x17c]
XOR        AH,AH
SHL        AX,0x1
MOV        SI,AX
MOV        AL,[0x17b]
XOR        AH,AH
MOV        DI,AX
MOV        AL,byte ptr [DI + 0x176]
XOR        AH,AH
MOV        BL,byte ptr [0x17a]
XOR        BH,BH
MOV        CL,byte ptr [BX + 0x176]
XOR        CH,CH
PUSH       CX
PUSH       AX
PUSH       word ptr [SI + 0x296a]
CALL       FUN_0000_50b9
MOV        SP,BP
ADD        SP,0x6
POP        BP
RET

FUN_0000_5ad6
PUSH       BP
MOV        BP,SP
MOV        SI,word ptr [BP + Stack[0x2]]
MOV        DX,word ptr [BP + Stack[0x4]]
MOV        BX,word ptr [BP + Stack[0x6]]
SUB        DX,0xa
LODSW      SI
INC        AX
TEST       AX,0x1
JZ         LAB_0000_5aed
INC        AX
                             LAB_0000_5aed 
SHR        AX,0x1
MOV        [0x5ff2],AX
LODSW      SI
INC        AX
MOV        [0x5ff4],AX
SHL        BX,0x1
MOV        AX,DX
SHR        AX,0x1
MOV        BP,AX
TEST       DX,0x1
JZ         LAB_0000_5b06
DEC        DX
                             LAB_0000_5b06
MOV        CX,word ptr [0x5ff2]
MOV        DX,word ptr [0x5ff4]
PUSH       ES
MOV        AX,DAT_0000_b800
MOV        ES,AX
                             LAB_0000_5b14
MOV        DI,word ptr [BX + 0x5c46]
ADD        DI,BP
PUSH       BP
PUSH       CX
MOVSB.REP  ES:DI,SI
POP        CX
POP        BP
INC        BX
INC        BX
DEC        DX
JNZ        LAB_0000_5b14
POP        ES
POP        BP
RET

FUN_0000_50b9 
PUSH       BP
SUB        SP,0xe
MOV        BP,SP
MOV        SI,word ptr [BP + Stack[0x2]]
MOV        AL,byte ptr [SI]
XOR        AH,AH
INC        SI
INC        SI
MOV        BL,byte ptr [SI]
XOR        BH,BH
MOV        CX,SI
MOV        word ptr [BP + Stack[0x2]],CX
INC        CX
INC        CX
MOV        word ptr [BP + local_6],BX
INC        BX
MOV        word ptr [BP + local_8],AX
INC        AX
INC        AX
MOV        word ptr [BP + local_8],AX
MOV        word ptr [BP + 0x0],BX
MOV        word ptr [BP + local_6],BX
MOV        BX,0x2
CWD
IDIV       BX
MOV        word ptr [BP + local_e],AX
MOV        AX,word ptr [BP + 0x0]
MOV        BX,word ptr [BP + local_e]
IMUL       BX
MOV        word ptr [BP + local_4],0x0
MOV        word ptr [BP + local_a],AX
MOV        word ptr [BP + Stack[0x2]],CX
                             LAB_0000_5101
MOV        AX,word ptr [BP + local_4]
CMP        AX,word ptr [BP + local_a]
JGE        LAB_0000_5167
MOV        byte ptr [BP + local_b],0x0
MOV        SI,word ptr [BP + Stack[0x2]]
MOV        AL,byte ptr [SI]
XOR        AH,AH
MOV        byte ptr [BP + local_c],AL
AND        AX,0xf0
MOV        BX,word ptr [BP + Stack[0x4]]
AND        BX,0xf0
CMP        AX,BX
JNZ        LAB_0000_512e
MOV        AX,word ptr [BP + Stack[0x6]]
AND        AX,0xf0
MOV        byte ptr [BP + local_b],AL
                             LAB_0000_512e
MOV        AL,byte ptr [BP + local_c]
XOR        AH,AH
AND        AX,0xf
MOV        BX,word ptr [BP + Stack[0x4]]
AND        BX,0xf
CMP        AX,BX
JNZ        LAB_0000_5151
MOV        AX,word ptr [BP + Stack[0x6]]
AND        AX,0xf
MOV        BL,byte ptr [BP + local_b]
XOR        BH,BH
OR         BX,AX
MOV        byte ptr [BP + local_b],BL
                             LAB_0000_5151
MOV        AL,byte ptr [BP + local_b]
XOR        AH,AH
TEST       AX,AX
JZ         LAB_0000_515f
MOV        SI,word ptr [BP + Stack[0x2]]
MOV        byte ptr [SI],AL
                             LAB_0000_515f
INC        word ptr [BP + Stack[0x2]]
INC        word ptr [BP + local_4]
JMP        LAB_0000_5101
                             LAB_0000_5167
ADD        SP,0xe
POP        BP
RET


FUN_0000_5d86
PUSH       AX
PUSH       BX
PUSH       CX
PUSH       DX
PUSH       SI
PUSH       DI
PUSH       DS
PUSH       ES
PUSH       BP
MOV        AX,0x4ce
MOV        DS,AX
DEC        byte ptr [0x6305]=>DAT_0000_afe5
JNZ        LAB_0000_5da4
MOV        byte ptr [0x6306]=>DAT_0000_afe6,0x1
MOV        byte ptr [0x6305]=>DAT_0000_afe5,0x12
                             LAB_0000_5da4
CMP        byte ptr [0x1fd0]=>DAT_0000_6cb0,0x1
JNZ        LAB_0000_5db5
CMP        byte ptr [0x6303]=>DAT_0000_afe3,0x1
JNZ        LAB_0000_5db5
CALL       FUN_0000_5dcd
                             LAB_0000_5db5
CMP        byte ptr [0x175]=>LAB_0000_4e55,0x1
JNZ        LAB_0000_5dc3
MOV        BP,SP
CALL       FUN_0000_2e48
MOV        SP,BP
                             LAB_0000_5dc3
POP        BP
POP        ES
POP        DS
POP        DI
POP        SI
POP        DX
POP        CX
POP        BX
POP        AX
IRET

FUN_0000_5dcd
MOV        DI,word ptr [0x62f8]
MOV        BX,word ptr [0x62fa]
MOV        AX,word ptr [BX + DI]
INC        BX
INC        BX
CMP        AX,0xffff
JNZ        LAB_0000_5e02
CMP        byte ptr [0x6300],0x1
JNZ        LAB_0000_5dec
MOV        BX,0x0
MOV        AX,word ptr [BX + DI]
JMP        LAB_0000_5e02
                             LAB_0000_5dec
MOV        AX,0x0
MOV        byte ptr [0x6303],0x0
XOR        BX,BX
CMP        byte ptr [0x6304],0x1
JNZ        LAB_0000_5e02
MOV        byte ptr [0x6304],0x0
                             LAB_0000_5e02
MOV        word ptr [0x62fa],BX
CMP        AX,0xfffe
JNZ        LAB_0000_5e0c
RET
                             LAB_0000_5e0c
TEST       AX,AX
JNZ        LAB_0000_5e18
IN         AL,0x61
AND        AL,0xfc
OUT        0x61,AL
JMP        LAB_0000_5e2a
                             LAB_0000_5e18
PUSH       AX
MOV        AL,0xb6
OUT        0x43,AL
POP        AX
OUT        0x42,AL
MOV        AL,AH
OUT        0x42,AL
IN         AL,0x61
OR         AL,0x3
OUT        0x61,AL
                             LAB_0000_5e2a
RET

Now, I would like your advice on problem. There is an emulator that runs old PC programs such as these. The emulator has an adjustable value for the number of emulated instructions per millisecond. However, at a value that would be appropriate to emulate a 1980s PC that would have been used to run this program, the program becomes stuck at a screen showing the text "LEVEL 1" cycling through 4 different colors while running the INT 1C handler over and over, never moving on into gameplay. If the number of emulated instructions per millisecond is raised high enough, the program gets unstuck and proceeds to gameplay, but it should not be necessary to raise the number of emulated instructions that high, as it becomes higher than it should be to emulate a 1980s PC, and gameplay runs too quickly.

The cause seems to be that, when the number of emulated instructions per millisecond is too low, by the time INT 1C finishes INT 8 is generated again, and the program re-enters INT 1C processing, never allowing the code to return to other processing that would allow the program to continue to gameplay. In a different emulator in which the program seems to work correctly, after 1 or 2 cycles through the four colors, always ending on white, the last color, gameplay begins.

Here is the IRQ0 handler:

FFEA5: sti     
(omitted)
FFEE3: int     1Ch   (cycles the "LEVEL 1" text through a color)
FFEE5: mov     al,20h
FFEE7: out     20h,al
FFEE9: pop     dx
FFEEA: pop     ax
FFEEB: pop     ds 
FFEEC: iret

In the emulator where the game works correctly, after returning from the 1C handler, for the first 3 colors, execution resumes at FFEE5. After "mov al,20h" and "out 20h, al", the IRQ0 handler begins again, from FFEA5.

However, after the 4th color, execution resumes at FFEE5 but the IRQ0 handler is not run after the "out 20h, al" instruction. Instead it finishes out the last three "pop" instructions for the stacked IRQ0 handlers, then returns to game code, where it is able to run thousands of instructions before IRQ0 is raised again. If the game code proceeds far enough before IRQ0 is raised again, the LEVEL 1 screen will exit to the gameplay screen. If IRQ0 happens first, the 4-color cycle will begin again.

With the emulator where the game doesn't work correctly, with an appropriate number of emulated instructions per millisecond, even on the end of the 4th color, it starts the IRQ0 handler after "out 20h, al" again, the same as for the first 3 colors. It thus never gets to run the code necessary to move on to the gameplay screen. With a high number of emulated instructions per millisecond it gets past that screen, presumably because instruction execution outpaces the system timer and is able to return to the code to proceed even after showing just a single color. However, such a high number of emulated instructions per millisecond should not be required and make gameplay too fast, as mentioned above.

For the emulator in which the program does not work correctly, what might be the problem with its emulation?