The operating system variously known as ‘Multitasking MS-DOS 4.0’ or ‘European MS-DOS’, to avoid confusion with the better-known single-tasking MS-DOS 4.0, has the mildly irritating flaw that it doesn’t invoke the HLT instruction that stops the CPU when the system is idle. This has the effect that whenever the OS is run in a virtual machine, CPU usage of the host spikes to 100%, whether the guest is actually doing anything or not, inevitably leading to my laptop CPU fan revving up with an all-too-familiar noise.

There doesn’t seem to be a simple, obvious way to remedy this. Unlike the mainstream edition of the OS, there is not an idle interrupt to which one can hook a TSR which can invoke HLT on its own. Having studied (what I assume to be) the kernel idle loop in a debugger, there doesn’t even seem to be any room to squeeze in a HLT instruction by patching the kernel. Here it is disassembled:

0506:3DE0 0E                PUSH    CS
0506:3DE1 1F                POP     DS
0506:3DE2 FB                STI
0506:3DE3 F6066105FF        TEST    BYTE PTR [0561],FF
0506:3DE8 74F8              JZ      3DE2
0506:3DEA E832FD            CALL    3B1F
0506:3DED EBF1              JMP     3DE0

Is there a way to inject a halt instruction into the idle loop somewhere?

  • 2
    Is it feasible (permissible?) to post the code of the idle loop? May give someone inspiration... – TripeHound May 26 at 13:42
  • 2
    Just to confirm, Vernon Brooks explicitly lists “no INT 28h or INT 2Ah function 84h idle callout”. – Stephen Kitt May 26 at 15:28
  • 2
    How do you know that it can properly recover from a HLT? – Will Hartung May 26 at 20:19
  • 2
    The not-retro answer: some virtual machines allow you to limit the maximum CPU time expended. E.g. in VirtualBox look for 'Execution Cap'. Or, less satisfactorily, your OS may allow you to put a limit on the virtual machine software, see e.g. CPULimit. – Tommy May 26 at 20:41
  • 2
    @WillHartung It's a closed loop around a memory location. The only way to flip that location is by an interrupt routine doing so (Using DMA would be way too obscure in a PC-XT environment). And each and every interrupt will after it's termination continue execution after a HLT. Exactly what the routine requires. But the test can not be simply replaced by a HLT, as it shoulc only leave the loop if an interrupt happened that changed the flag as well. – Raffzahn May 26 at 21:04

In place and without changing other code: No

Simply because there is no shorter encoding of that sequence - that is unless we rely on

  • additional information how (and when) the tested byte changes


  • how and when values set here gets reused or not


  • certain register setup prior to this routine.

The basic issue here is that a HLT instruction will end/continue operation after any arbitrary interrupt was handled, but it's not said that every interrupt will as well set the flag byte at CS:0561h. All that do not set that byte should of loop back and halt again.

There is always the usual method of jumping out by replacing the test instruction by a call to some new routine, intended to whatever is necessary - like

    TEST xxxx

The 5 bytes of the TEST instruction is long enough to even hold a far call, so it can be located anywhere. This method has the advantage that all other code is untouched.

Possible Other Ways depending on code/workings outside the core loop:

Of course if it can be guaranteed that certain data is present, some instructions can be used for patching like

  • if DS is already equalling CS when entered, then the PUSH/POP pair can be dropped and their space repurposed to add a HLT instruction
0506:3DE0 90                NOP
0506:3DE1 FB                STI
0506:3DE2 F4                HLT
0506:3DE2 F6066105FF        TEST    BYTE PTR [0561],FF
0506:3DE8 74F7              JZ      3DE1
0506:3DEA E832FD            CALL    3B1F
0506:3DED EBF1              JMP     3DE0

Likewise if it's guaranteed that DS is not used later on with the assumption of still having the same value as CS, they can be replaced by a segment override prefix freeing up a much needed byte for the HLT instruction.

  • if DS gets reloaded afterwards (like in the function starting at CS:3B1Fh) without being uses first, the following patch might work:
0506:3DE0 FB                STI
0506:3DE1 F4                HLT
0506:3DE2 2EF6066105FF      TEST    CS:BYTE PTR [0561],FF
0506:3DE8 74F6              JZ      3DE0
0506:3DEA E832FD            CALL    3B1F
0506:3DED EBF1              JMP     3DE0
  • 3
    "Or"... Or what??? Now I'm going to have to go into my HLT routine waiting for the next character read from your keyboard... – Jon Custer May 27 at 13:30
  • @JonCuster Well, you got me ... you got me thinking again, so here's a second possible patch :)) – Raffzahn May 27 at 19:10
  • 1
    Very nice. And now I can get back to work again! – Jon Custer May 27 at 19:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.