/pub/apm-msdos-assembly.md


MS-DOS, APM with C and Assembly

· updated 2026-08-28 · #development #retro #assembly #c

Watch the video of this article here.

For retrocomputing fun, I use VirtualBox with MS-DOS 6.22 and DOSBox. Both work very well, but the screen looks a bit better in VirtualBox, and DOSBox is faster and easier for developing and switching quickly between systems. So I use both.

In DOSBox, you can close the emulator by typing the exit command. That closes a command interpreter session and, if it is the last one, closes DOSBox itself. It is convenient because you do not need the mouse and it behaves more like a terminal.

In VirtualBox, you have to close the emulation by command. If the operating system were more modern, you could use the host+u key combination to power off via ACPI (Advanced Configuration and Power Interface). But with DOS, VirtualBox shows an error message saying the guest does not support software shutdown.

I found out that the system supports APM (Advanced Power Management), the predecessor of ACPI. You can shut down by software by calling int 15h and passing the parameters to power off the machine.

I put all of this together and wrote small utilities to shut down and reboot the machine. With a bit of ingenuity, I even got it to shut down by typing exit in DOS.

Reboot using the BIOS

This reboot utility is simple. It does not use APM; instead, it calls int 19h, which makes the BIOS restart the system. The idea is not mine; I read this tip in a magazine in the early ’90s. It is so simple that whenever I sit down at a DOS machine, I create the executable using debug.

reboot.com using debug

This is the debug script that generates reboot.com. Type it exactly like this, including the blank line.

debug
a100
int 19

rcx
2
n reboot.com
w

That produces a .com executable with only 2 bytes!

Now, a more elaborate version using the NASM assembler, which generates exactly the same executable.

;reboot using BIOS.
;nasm -f bin -o reboot.com reboot.asm

org 100h
section .text

start:  int 19h

.end

Shutdown using APM

First, I wanted to check whether APM is supported. That matters because if I write a bat script, I want my executable to return a value in the errorlevel variable. That way I can take different actions. For example, DOSBox does not support APM, only VirtualBox does. So if I try to use this utility in DOSBox, I need to know it failed.

;check whether APM is ok
mov ax, 5300h
xor bx, bx
int 15h
jc APM_error
.
.
.
APM_error: mov  dx, msgAPMError
           mov  ah, 9
           int  21h

exitError: mov  ax, 4CFFh
           int  21h

msgAPMError db "Erro de APM ou não disponível",0

The code above checks for APM support. If everything is fine, the program continues. Otherwise, it jumps to the APM_error label, which prints an error message on screen and exits the program, setting errorlevel to 255.

If APM is ok, the program prepares to connect and set version 1.2 (the last revision, from 1996) and finally powers off the machine with the following snippet.

;power off the system
mov     ax, 5307h
mov     bx, 0001h
mov     cx, 0003h
int     15h

If you use FreeDOS, I believe it supports version 1.11, so you may need some adjustments.

Combining reset and shutdown using C

Besides the pure assembly version, I wrote a C application combining both features: reset and shutdown. They can be selected through command line parameters. I did it just to experiment with Borland Turbo C and mix it with assembly.

/*
compile com:
tcc -mt -tDc main.c
*/
#include <stdio.h>

// Verifica instalação do APM
char chkAPM() {
  asm {
    mov ax, 5300h
    xor bx, bx
    int 15h
    jc APM_error
  }
  return 0;
APM_error:
  return -1;
}

void usage() {
  printf("shutdown para DOS\n"
         "Cesar Gimenes, @crgimenes\n"
         "https://github.com/crgimenes/shutdown\n"
         "Uso:\n"
         "    -h halt requer APM\n"
         "    -r reboot (chama BIOS int 19h)\n");
}

int main(int argc, char *argv[]) {
  if (argc == 1) {
    usage();
    return 1;
  }

  if (strcmp(argv[1], "-r") == 0) {
    asm int 19h
  }

  if (chkAPM()) {
    printf("Erro de APM ou não disponível");
    return 1;
  }

  if (strcmp(argv[1], "-h") == 0) {
    asm {
      /* conecta ao APM */
      mov     ax, 5301h
      xor     bx, bx
      int     15h

      /* define versão do APM */
      mov     ax, 530Eh
      mov     cx, 0102h
      xor     bx, bx
      int     15h

      /* desligar */
      mov     ax, 5307h
      mov     bx, 0001h
      mov     cx, 0003h
      int     15h
      hlt
    }
  }

  return 0;
}

Finally, to make VirtualBox behave like DOSBox and close when we type the exit command, we can start a new session of the command.com interpreter at the end of autoexec.bat and then call the shutdown command. The downside of this approach is that it uses more RAM and keeps another instance running.

...
command
sd -h

I hope this is useful. The source code and compiled versions are on GitHub. You will notice the .com applications are extremely small. In fact, reboot.com is only 2 bytes, not kilobytes, just bytes. The largest one, sd.com, is only 6.4K. That happens because they are direct instructions for the processor, with no headers. The operating system just loads the file into RAM at a specific address and points the execution pointer there. But that is a topic for another day.

Cesar Gimenes


crg.eti.br · © 2026 Cesar Gimenes · CC BY 4.0 · github · pt