Assembler no Linux
Assembly no Linux é possível?
Para todos aqueles que não puderam desfrutar de boas literaturas do tempo, hoje estão de volta. Sempre tive uma grande queda por linguagens antigas, como COBOL, BASIC,PASCAL, FORTRAN e claro o ASSEMBLY, mas dentre todas essas eu sempre achei que Assembly era coisa de doido, e ainda acho, sendo assim não tenho como garantir minha própria sanidade mental já que vi algumas coisas para doidos, digamos que pude provar do Elixir da loucura.
O Unix, Linux, FreeBSD, e não apenas este mas outros também da família BSD, nos oferece igual e se não muito maior potencial para fuçadores natos como eu. Bem sabemos o quanto estas plataformas se tornaram famosas tanto em grandes servidores como em sistemas embutidos (embedded systems).
Digamos que neste artigo vamos fazer um paralelo do que encontrava-se nas antigas literaturas conforme citado acima, e como "portar" os mesmos raciocínio para o Linux. Claro levando em consideração as características do sistema, não teremos como criar um exemplo do tipo relógio no canto da tela, não que não dê, mas é trabalhoso e teríamos que estender muito este artigo, vou tentar buscar a cabo com as explicações sobre como se comportam o que chamamos de interfaces disponíveis.
Modo Real e Modo protegido
Antes de mais nada lembremos das limitações que eram
encontradas nos processadores antigos, 640 Kb se quer
pareciam ser um problema já que para a época era o que
se tinha disponível. A grande maioria eram baseados em
processadores como o Z80 (Microprocessadores de 8 Bits
e com endereçamento máximo e direto de 64Kb).
Já nos 80286, existia um modo de trabalho do processador
que era chamado de modo protegido. Ou seja, quando o
processador era ligado, se comportava como um 8086, mas
após a entrada neste modo, novas características eram
habilitadas, como maior capacidade de endereçamento de
memória, permissões de acesso dessa memória etc.
A grosso modo podemos dizer que processadores em
modo real, eram por exemplo os 386 que se portavam como
um 8086, rodando diretamente programas, como o DOS em
16 Bits, com endereçamento de até 640 Kb, não importando o quanto
tivesse instalado na máquina. E o modo protegido eram aqueles
que apresentavam além de as vantagens citadas, a melhoria
de forma de entrada e saída deste modo para o modo real,
outra das vantagens do modo protegido eram as facilidades
de multitarefas e que com certeza revolucionaram os programas
da época.
No caso do Linux, temos a memória que está protegida pelo Kernel, e que está disponível para os programas dos usuários (Kernel e User memories). Esta tentativa poderia causar um erro de segmentação (Segmentation Fault), uma exceção que é disparada pelo processador e sinalizada pelo Kernel, indicando que foi tentado uma operação não permitida.
Bem, agora sabemos que o Linux utiliza o máximo que pode de um processador e que é um sistema multitarefa, multiusuários e de 32 bits, que roda em "Modo protegido". Fazendo um paralelo sobre a BIOS, está claro que o software que roda nas BIOS de hoje em dia estão bem mais elaborados que antigamente até por que hoje em dia os Hardwares existente exigem isso, mas ainda temos a mesma limitação de interface como antigamente, inclusive se usa a mesma interface para os programas.
Mas estas interfaces não foram pensadas para um sistema multitarefa como o Linux, e portanto se fosse permitido usar as funções da BIOS, programando no Linux, teríamos um problema.
As rotinas não prevêem que mais de um processo pode tentar acessar o mesmo recurso e, portanto não implementam travas para isso, vamos ilustrar um pequeno exemplo em C, antes de mais nada vou dar uma explicadinha. A grosso modo o que faremos é, usar uma função que quando executada, checa se o flag busy é 0 (zero), se for significa que o recurso está livre, sendo assim é inicializado, flag setado para 1 e se procede o resto da função.
2 static char busy=0;
3 if (!busy){
4 init_stuff();
5 busy=1;
6 }
7 else {
8 fprintf(stderr,"Recurso ocupado\n");
9 return ERROR;
10 }
11 }
Na linha 2 criamos uma variável estática e inicializamos ela com o valor 0 para o flag, dessa forma sinalizamos o uso de um recurso, caso não esteja em uso linha 3, setamos na linha 5 o valor 1 do flag cimo 1 a fim de sinalizar o não uso de um recurso, caso o valor seja diferente de 1 então imprimimos na tela a mensagem de recurso ocupado que neste caso é um retorno de erro.
Enquanto este flag for 1, nenhum outro processo pode acessá-las, claro, existem as chamadas race-conditions, mas aqui estamos apenas demonstrando uma forma de checar o uso de um recurso em um ambiente multitarefa e que poderia com muita facilidade se transformar em algo caótico no meu ponto de vista como programador.
Até lembrei dos velhos tempos do MSX com o cartucho Mega Assembler. Para aumentar as vidas dos joguinhos era sensacional.