Oficina
8. O loader Numaboa
Sab 26 Mai 2007 16:20 |
- Detalhes
- Categoria: Sistemas Operacionais
- Atualização: Domingo, 12 Abril 2009 17:38
- Autor: vovó Vicki
- Acessos: 13439
MÓDULO 8 do SO Numaboa
Um loader com apenas um loop infinito foi um bom começo mas, para dizer a verdade, quando o testei pela primeira vez fiquei desconfiada. Será que o setor de boot estava realmente funcionando como pretendido? Numa situação como esta, um dos testes que podem ser efetuados é adicionar algum código que dê saída de um caracter ou de uma string para a tela: se aparecer na tela - funcionou, se não aparecer na tela - deu caca.
Entra aí uma novidade: é preciso saber como dar saída para a tela e a única opção que temos é usar uma das rotinas da BIOS fazendo um chamado de interrupção.
As interrupções da BIOS
As interrupções da BIOS são uma mão na roda. Oferecem uma série de rotinas básicas (daí o nome de Basic Input/Output Services) que podem ser acessadas via interrupções de hardware. Da extensa lista de interrupções disponíveis, a que nos interessa no momento é a INT 10.
A INT 10 é conhecida como Serviços da BIOS para Vídeo (Video BIOS Services). A INT 10 aciona uma porção de rotinas diferentes (mais de 25) dependendo do valor colocado na porção alta do registrador EAX (nos sistemas de 32 bits - AX nos sistemas de 16 bits). INT 10 com AH=00, por exemplo, configura o modo de vídeo; INT 10 com AH=06, rola a página para cima; INT 10 com AH=0C, escreve um pixel gráfico nas coordenadas, etc.
Para uma lista completa das interrupções da BIOS e seus respectivos serviços ou rotinas procure literatura técnica especializada (veja nas considerações finais deste texto). Uma descrição detalhada não faz parte deste tutorial.
A INT 10 com AH=0E
A rotina que nos interessa é a INT 10 com AH=0E, que escreve texto em modo teletipo (modo teletipo é quando o cursor avança uma posição depois de escrever um caracter). Os valores, todos em notação hexadecimal, que podem ser enviados para a BIOS através dos registradores são:
AH = 0E AL = ASCII do caracter que deve ser escrito na porção baixa do registrador EAX ou AX (Low = baixa) BH = número da página de vídeo (modos texto) na porção alta do registrador EBX ou BX (High = alta) BL = cor de frente e de fundo dos pixels (modos gráficos) na porção baixa do registrador EBX ou BX - não há valor de retorno - o cursor avança depois da escrita - caracteres BEL (07), BS (08), LF (0A), and CR (0D) são tratados como códigos de controle
Uma letra na tela
Vamos completar o código do boot loader de loop infinito, escrito no módulo anterior, para colocar uma letra na tela. A letra escolhida será a letra A, cujo ASCII em notação decimal é 65.
Entre o marcador do início do programa e o loop infinito estão algumas instruções MOV para MOVer valores para determinadas porções dos registradores AX e BX. Não se esqueça de que estamos trabalhando com 16 bits. Neste caso, os registradores aceitam apenas 16 bits e recebem o nome de AX, BX, CX, etc. Caso estivéssemos trabalhando com 32 bits, os registradores aceitariam 32 bits e receberiam o nome de EAX, EBX, ECX, etc. Na porção baixa de BX (bl) inserimos o valor 07, que determina texto branco em fundo preto. Você pode tentar outros valores para obter outras combinações de cores.
Conferindo o loader Numaboa
Escreva o código do exemplo e grave-o como loader2.asm (ou outro nome qualquer). Compile-o com o NASM usando nasm.exe -f bin loader2.asm -o loader2.bin ou com o NASM para Windows usando nasmw.exe -f bin loader2.asm -o loader2.bin. Abra o loader2.bin num editor hexadecimal e verifique:
0000 0000 B4 0E B0 41 B7 00 B3 07 CD 10 EB FE 00 00 00 00 0000 0010 00 00 00 00 ... ... 0000 01F0 ... 00 00 00 00 00 00 55 AA
Se o código compilado estiver ok, transfira-o para o setor de boot de um disquete (você pode formatar o disquete usado anteriormente para reutilizá-lo) e teste-o (como explicado no módulo anterior).
Identificando o sistema operacional Numaboa
Escrever apenas uma letra é um negócio marreta. No mínimo, precisamos limpar a tela e identificar nosso sistema operacional. Para isto, bastam alguns ajustes.
Limpando a tela
Como já sabemos que a INT 10 possui os serviços de vídeo, precisamos apenas identificar a rotina para limpar a tela: pode ser a 06 (Scroll window up) que rola a tela para cima ou a 07 (Scroll window down) que rola a tela para baixo. Fica a seu critério qual das duas usar pois o efeito será o mesmo:
AH = 06 - ROLAGEM PARA CIMA AL = número de linhas de rolagem. As linhas anteriores serão limpas. Se 0 ou maior que o tamanho da tela, a tela toda será limpa. BH = atributo usado para limpar a(s) linha(s). CH = linha do canto superior esquerdo da janela de rolagem. CL = coluna do canto superior esquerdo da janela de rolagem. DH = linha do canto inferior direito da janela de rolagem. DL = coluna do canto inferior direito da janela de rolagem. - não tem valor de retorno AH = 07 - ROLAGEM PARA BAIXO - usa os mesmos registradores com os mesmos valores.
Eu optei pelo serviço 07 da INT 10.
Para dar as coordenadas do canto superior esquerdo da tela também poderíamos usar mov cx, 0x0000 (observe o hexadecimal 0x0000 com quatro zeros! os quatro zeros são necessários para que os 16 bits sejam preenchidos). Se indicarmos um número de linha e coluna maior que a tela para o canto inferior direito, temos a certeza de que toda a tela será limpa.
Posicionando o cursor
Limpar a tela não significa que o cursor seja deslocado na posição 0,0 - o início da tela. É preciso usar mais um serviço da INT 10 que seta a posição do cursor: INT 10 serviço 02.
Declarando uma string
Uma sequência de caracteres, ou string, pode ser armazenada numa variável. Sabemos que variáveis nada mais são do que endereços de memória onde ficam armazenados seus valores. Se quisermos declarar a variável StringSO, que irá conter o valor da nossa string "Sistema Operacional NumaBoa", será preciso declará-la e atribuir-lhe um valor. Além dos caracteres, queremos que o cursor seja posicionado na próxima linha. Para isto, adicionamos os caracteres 13 (return=retorno de carro) e 10 (line feed = mudança de linha). Como se trata de uma string, o caracter terminador precisa ser zero, ou seja:
Indicando a posição da string na memória
Só para relembrar: sistemas de 16 bits são muito limitados para indicar endereços de memória. Com 16 bits, o maior número que podemos obter é 65535 (que é o mesmo que 1111 1111 1111 1111 em binário). O valor 65535 corresponde a 64 Kb, o que seria uma quantidade de memória insignificante e limitaria muito os aplicativos e sistemas operacionais. Foi daí que o pessoal resolveu "lotear" a memória em segmentos e, logicamente, em segmentos de 64 Kb. O endereçamento passou a ser composto por duas referências: o segmento e o deslocamento dentro do segmento. Assim, o endereço 0000:0005 se refere ao deslocamento 5 dentro do segmento 0.
A área de memória, onde são armazenados os dados (nossas variáveis), é chamado de segmento de dados. Um dos registradores da CPU fica encarregado de indicar a posição deste segmento: é o DS (Data Segment). O endereçamento é feito com DS:Deslocamento, como explicado acima. O registrador que guarda o deslocamento é o SI (Segment Index ou Índice de Segmento).
Como nosso programa é muito pequeno e deixamos o NASM alocar espaço de memória para o programa e para os dados, acaba ficando tudo "empacotado" no mesmo segmento, o segmento 0 (zero). Neste caso, fica fácil indicar a posição da nossa string: DS = 0 e SI = StringSO. Quando passamos o nome da variável para SI, na verdade passamos o deslocamento correspondente à StringSO. Completando nosso código teremos:
Novamente tome cuidado ao mover um valor de 16 bits para ax (mov ax,0x0000). Para não bagunçar nosso código, criamos a sub-rotina PoeString. Esta sub-rotina lerá uma a um os caracteres de StringSO, jogando-os na tela, até encontrar o caracter 0 (zero), marcador do final da string.
Escrevendo a string na tela
A sub-rotina ou procedimento PoeString vai precisar de um loop que, a cada passada, imprima o caracter encontrado na tela. Este loop termina quando o caracter 0 (zero) for encontrado. Nossa rotina de teletipo ficará dentro desta função:
O opcode LODSB carrega um byte existente em [DS:SI] em AL. Depois disto incrementa ou decrementa SI (dependendo da flag de direção: incrementa se a flag for 0, decrementa se for 1). Este código operacional é uma mão na roda para transferir sequências de bytes.
Bem, o código acima realiza o que projetamos. Salve-o como loader2a.asm e compile-o para loader2a.bin com o NASM.
Verificando o boot loader Numaboa
Só para matar a curiosidade, vamos dar uma olhada no código compilado. Use o editor hexadecimal da sua preferência e verifique os 512 bytes do arquivo loader2a.bin, que deve mostrar o seguinte:
0000 B4 07 B0 20 B7 07 B9 00 00 B6 20 B2 B0 CD 10 B4 ´.° ·.¹..¶ ²°Í.´ 0010 02 B7 00 BA 00 00 CD 10 B8 00 00 8E D8 BE 35 7C .·.º..Í.¸..ŽØ¾5| 0020 E8 02 00 EB FE B4 0E B7 00 B3 07 AC 08 C0 74 04 è..ëþ´.·³.¬.Àt. 0030 CD 10 EB F7 C3 53 69 73 74 65 6D 61 20 4F 70 65 Í.ë÷ÃSistema Ope 0040 72 63 61 69 6F 6E 61 6C 20 3E 75 6D 61 42 6F 61 racional NumaBoa 0050 0D 0A 00 00 00 00 00 00 ... ... 01F0 ... 00 00 00 00 00 00 55 AA
Testando o bootloader Numaboa
Prepare o disquete de teste da mesma forma como foi explicado no módulo anterior. Agora, é só cruzar os dedos e torcer para que tudo dê certo (pra mim deu ).
Dê o boot com o disquete no drive e deixe o Sistema Operacional Numaboa assumir o comando e ficar "travado" no loop infinito...
Considerações finais
O Sistema Operacional NumaBoa é composto apenas por um boot loader de 16 bits em modo real. Este trabalho todo para ficar transferindo o boot loader na unha usando o debug.exe e testando o loader com reinício de máquina tem sua razão de ser: se, futuramente, quisermos acessar a máquina em modo real, está meio caminho andado.Quero destacar o site do engenheiro Roger Morgan, que disponibiliza um verdadeiro "mapa da mina" de interrupções e outras informações preciosas sobre hardware. Se tiver interesse, dê uma olhada em Interrupt Services DOS, BIOS, EMS and Mouse. Além disto, não posso deixar de citar o livro Linguagem Assembly para IBM PC, de Peter Norton, pela Editora Campus em 1988 - livrinho antigo para 16 bits, mas que nunca sai de moda