MS-DOS, APM com C e Assembly
Veja o vídeo desse arquivo aqui.
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 sesseão do interpretador de comandos e se for a ultima sessão ela vai fechar o proprio 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 la no início 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ácel .com
que tem apenas 2 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, 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 la 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 chamanmos 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 ponteiso de execução lá, mas isso é historia para outro dia.