DevSecOpsARTIGO

MS-DOS, APM com C e Assembly

No seguinte artigo, o autor fala sobre retro-computação e apresenta um passo a passo ensinando a usar MS-DOS, APM com C e Assembly.

Para brincar de retro-computação, eu uso o VirtualBox com o MS-DOS 6.22 e o DOSBox. Os dois funcionam muito bem, mas a tela fica ligeiramente melhor no VirtualBox e o DOSBox é mais rápido e fácil para desenvolver e ir rapidamente de um sistema para outro; então acabo usando os dois.

No DOSBox você pode fechar o simulador simplesmente digitando o comando “exit” que vai fechar uma sessão do interpretador de comandos, e se for a ultima sessão, ela vai fechar o próprio DOSBox, o que é bem legal, porque não precisa mover mouse nem nada, e é um comportamento mais parecido com um terminal.

Já no VirtualBox é preciso comandar isso mandando fechar a simulação. Se o sistema operacional fosse um pouco mais moderno, seria possível usar a combinação de teclas host+u que comanda o shutdown via ACPI (Advanced Configuration and Power Interface), mas no caso do DOS, o VirtualBox vai dar uma mensagem de erro avisando que o guest não suporta shutdown por software.

Mas eu sabia que o sistema suporta APM (Advanced Power Management), que é o antecessor do ACPI e é possível fazer shutdown por software chamando a int 15h e passando os parâmetros para desligar a maquina, então juntei tudo isso e criei alguns pequenos utilitários para desligar e reiniciar a maquina, e com um pouco de engenhosidade poder até mesmo desligar digitando “exit” no DOS.

Reboot usando BIOS

Esse utilitário para reboot é extremamente simples, ele não usa APM. No lugar disso, ele chama a int 19h que faz com que a BIOS reinicie e ela leva todo o sistema com ela. A ideia não é minha; li essa dica em uma revista lá no inicio dos anos 90, e é tão simples que toda vez que vou em uma maquina DOS, simplesmente crio o pequeno executável usando o debug.

reboot.com usando debug

Esse é o script do debug para gerar o reboot.com. Precisa digitar exatamente assim, incluindo a linha em branco.

debug
a100
int 19

rcx
2
n reboot.com
w

Isso vai gerar um executável .com que tem apenas dois bytes!

Agora uma versão mais bem feitinha usando o compilador NASM, mas que gera exatamente o mesmo executável.

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

org 100h
section .text

start: int 19h

.end

Shutdown usando APM

A primeira coisa que eu queria fazer é validar se o APM é suportado ou não. Isso é importante porque se eu fizer um script bat, eu quero que o meu executável retorne um valor na variável errorlevel, e assim eu posso tomar atitudes diferentes como, por exemplo, o DOSBox não suporta APM, só o VirtualBox, então se eu tentar usar esse utilitário no DOSBox, preciso de uma forma de saber que falhou.

;chke if 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 “APM error or not available”,0

O trecho de código acima vai verificar o suporte a APM. Se estiver tudo ok, o programa vai em frente, caso contrario ele salta para o label APM_error, que mostra uma mensagem de erro na tela e fecha o programa deixando em errorlevel o valor 255.

Se o APM estiver ok, o programa faz mais alguns preparativos para conectar e ajustar a versão para a 1.2 (ultima revisão lá de 1996) e finalmente desliga a maquina com o seguinte trecho de código:

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

Se você estiver usando FreeDOS, acredito que ele suporta a versão 1.11, então talvez precise de algum ajuste.

Combinando reset e shutdown usando C

Alem da versão em assembly puro mais direta e sem parâmetros, também fiz um aplicativo em C, combinando as duas funcionalidades, reset e shutdown, que podem ser selecionadas por parâmetros na linha de comando. Fiz apenas pela brincadeira de usar o Borland Turbo C e ainda integrar com asm.

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

// APM installation check
char chkAPM() {
  asm {
    mov ax, 5300h 
    xor bx, bx 
    int 15h 
    jc APM_error
  }
  return 0;
APM_error:
  return -1;
}

void usage() {
  printf("shutdown for DOS\r\n"
         "Cesar Gimenes, @crgimenes\r\n"
         "https://github.com/crgimenes/shutdown\r\n\r\n"
         "Usage:\r\n"
         "\t-h halt require APM\r\n"
         "\t-r reboot (call BIOS int 19h)\r\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("APM error or not available");
    return 1;
  }

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

      /* set APM version */
      mov     ax, 530Eh
      mov     cx, 0102h
      xor     bx, bx
      int     15h

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

  return 0;
}

E finalmente, para fazer VirtualBox ter o mesmo comportamento do DOSBox e fechar quando digitarmos o comando exit, podemos chamar uma nova sessão do interpretador de comandos command.com no fim do autoexec.bat e em seguida chamamos o comando de shutdown. A desvantagem dessa abordagem é consumir mais RAM e ter outra instancia rodando.

...
command
sd -h

Espero que seja útil o código fonte, e também as versões compiladas estão no GitHub. Você vai notar que os aplicativos .com são extremamente pequenos. Na verdade, reboot.com tem apenas 2 bytes – não kilo – só bytes, e o maior deles o sd.com tem apenas 6.4K, isso porque eles são instruções diretas para o processador, sem cabeçalhos, nem nada. O sistema operacional apenas carrega esse arquivo na RAM em um endereço especifico e coloca o ponteiro de execução lá, mas isso é historia para um outro dia.

Cesar GimenesAutor

Trabalha com tecnologia desde a década de 90. Já atuou na área de educação e participou de projetos de mobilidade de grande volume para laboratórios farmacêuticos. Criou games tanto para PC, como para iOS. Hoje está direcionando seus esforços em plataformas de Sistemas Embarcados, IoT, microservices e cloud computing. É um entusiasta de tecnologias como Golang e Docker.

Ver perfil