Como chegar entrada de teclado com x86 bare metal montagem?
-
03-07-2019 - |
Pergunta
Eu estou no processo de tentar cortar juntos os primeiros bits de um kernel. Atualmente tenho todo o kernel compilado para baixo como o código C, e eu consegui fazê-lo exibir texto na janela do console e tudo isso bem bondade. Agora, eu quero começar a aceitar a entrada do teclado para que eu possa realmente fazer algum uso da coisa e ir em frente na gestão de processos.
Eu estou usando DJGPP para compilar e carregamento com o GRUB. Eu também estou usando um pequeno pedaço de montagem que basicamente vai diretamente em meu código C compilado e estou feliz de lá.
Toda a pesquisa que fiz parece apontar para um ISR em US $ 0x16 para ler no próximo caractere do buffer de teclado. Do que eu posso dizer, isso é suposto para armazenar o valor ASCII em ah, eo código de acesso em al, ou algo nesse sentido. Eu estou tentando código isso usando a seguinte rotina na linha de montagem:
char getc(void)
{
int output = 0;
//CRAZY VOODOO CODE
asm("xor %%ah, %%ah\n\t"
"int $0x16"
: "=a" (output)
: "a" (output)
:
);
return (char)output;
}
Quando este código é chamado, o núcleo deixa de funcionar imediatamente. (Estou executando-o em VirtualBox, eu não senti a necessidade de tentar algo tão básicas sobre hardware real.)
Agora eu tenho realmente um par de perguntas. Ninguém foi capaz de me dizer se (desde o meu código foi lançado do GRUB) Estou correndo em modo real ou modo protegido no momento. Eu não fiz o salto de uma maneira ou de outra, eu estava pensando em correr em modo real até que eu tenho um manipulador processo de configuração.
Assim, supondo que eu estou correndo em modo real, o que estou fazendo de errado, e como faço para corrigir isso? Eu só preciso de uma rotina getc básica, de preferência sem bloqueio, mas eu vou ser amaldiçoado se google está ajudando em um presente em tudo. Uma vez que eu posso fazer isso, eu posso fazer o resto de lá.
Eu acho que o que eu estou pedindo aqui é, eu estou em qualquer lugar perto o caminho certo? Como é que uma pessoa geralmente ir sobre a obtenção de entrada de teclado a este nível?
EDIT: Oohh ... então eu estou correndo em modo protegido. Isso certamente explica o acidente tentando funções de acesso de modo real depois.
Então eu acho que eu estou procurando como acessar o teclado IO de modo protegido. Eu poderia ser capaz de encontrar isso no meu próprio, mas se alguém acontece a conhecer sensação livre. Obrigado mais uma vez.
Solução
Se você está compilando com gcc, a menos que você estiver usando o ".code16gcc" louco enganar os usos do kernel do Linux (o que eu duvido muito), você não pode estar em modo real. Se você estiver usando a especificação multiboot GRUB, o GRUB em si é a mudança para o modo protegido para você. Assim, como outros apontaram, você vai ter que falar diretamente com o controlador de teclado 8042-compatível / mouse. A menos que seja um teclado / rato USB e 8042 emulação está desativado, onde você precisaria de uma pilha USB (mas você pode usar o protocolo de "boot" para o teclado / mouse, que é mais simples).
Ninguém disse que escrever um kernel do sistema operacional era simples.
Outras dicas
O código que você tem aí está tentando acessar um serviço de BIOS de modo real. Se você estiver executando em modo protegido, que é provável, considerando que você está escrevendo um kernel, em seguida, a interrupção não vai funcionar. Você vai precisar fazer uma das seguintes opções:
- Thunk a CPU em modo real, certificando-se a tabela vetor de interrupção está correto e usar o código de modo real que você tem ou
- Escreva seu próprio manipulador protegido modo teclado (ou seja, utilizar as instruções / out).
A primeira solução vai envolver uma sobrecarga de desempenho de tempo de execução whist o segundo vai exigir algumas informações sobre o teclado IO.
Eu tenho um pedaço de GeekOS que parece fazer
In_Byte(KB_CMD);
e
In_Byte(KB_DATA);
para buscar uma scancode. Eu colocá-lo: keyboard.c e keyboard.h . KB_CMD
e KB_DATA
sendo 0x64 e 0x60 respectivamente. Eu poderia talvez também de salientar que isso é feito em um manipulador de interrupção para intr:. 1
Você está fazendo a coisa certa, mas eu me lembro que djgpp Saída em modo só gera protegida, o que você não pode chamar interrupções de. você pode cair para o modo real, como já foi sugerido, ou você prefere para abordar o hardware diretamente?
Para efeitos de explicação, vamos supor que você estava escrevendo tudo em linguagem assembly mesmo, carregador de boot e kernel (* tosse * Eu fiz isso).
No modo real, você pode fazer uso das rotinas de interrupção que vêm do BIOS. Você também pode substituir os vetores de interrupção com o seu próprio. No entanto todo o código é um código de 16 bits, que é não binário compatível com código de 32 bits.
Quando você saltar através de algumas aros de queima para chegar ao modo protegido (incluindo a reprogramação do controlador de interrupção, para contornar o fato de que a IBM usou interrupções Intel reservados no PC), você tem a oportunidade de criar 16 e segmentos de código de 32 bits. Isso pode ser usado para executar código de 16 bits. Então você pode usar isso para acessar a interrupção getchar!
... não completamente. Para esta interrupção ao trabalho, você realmente precisa de dados em um buffer de teclado que foi colocado ali por um ISR diferente - o que é acionado pelo teclado quando uma tecla é pressionada. Há várias questões que praticamente impedem usando BIOS ISRs como ISRs de hardware real em modo protegido. Assim, as rotinas de teclado BIOS são inúteis.
chamadas de vídeo BIOS, por outro lado, são muito bem, porque não há nenhum componente acionado por hardware. Você tem que preparar um segmento de código de 16 bits, mas se que está sob controle, então você pode alternar entre os modos de vídeo e esse tipo de coisa usando interrupções de BIOS.
Voltar para o teclado: o que você precisa (novamente assumindo que você está escrevendo todo o código) é escrever um driver de teclado. A menos que você é um masoquista (eu sou um), então não vá lá.
Uma sugestão: tentar escrever um kernel multitarefa no modo Real. (Isso é modo de 16 bits.) Você pode usar todas as interrupções do BIOS! Você não ganha proteção de memória, mas você ainda pode obter multitarefa preemptiva ligando a interrupção timer.
Apenas uma idéia: olhando para GRUB para fonte DOS (asm.s), o console_checkkey
função está usando BIOS INT 16H Function 01
, e não funcionar 00, como você está tentando fazer. Talvez você gostaria de verificar se uma chave está esperando para ser de entrada.
O código console_checkkey
está definindo o CPU para modo real, a fim de usar a BIOS, como @ Skizz sugeriu .
Você também pode tentar usar funções do GRUB directamente (se ainda mapeada em modo real).
Uma nota sobre a leitura fonte de montagem: nesta versão
movb $0x1, %ah
meios mover byte constante (0x1) para registar %ah
O console_checkkey
do GRUB asm.s:
/*
* int console_checkkey (void)
* if there is a character pending, return it; otherwise return -1
* BIOS call "INT 16H Function 01H" to check whether a character is pending
* Call with %ah = 0x1
* Return:
* If key waiting to be input:
* %ah = keyboard scan code
* %al = ASCII character
* Zero flag = clear
* else
* Zero flag = set
*/
ENTRY(console_checkkey)
push %ebp
xorl %edx, %edx
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti /* checkkey needs interrupt on */
movb $0x1, %ah
int $0x16
DATA32 jz notpending
movw %ax, %dx
//call translate_keycode
call remap_ascii_char
DATA32 jmp pending
notpending:
movl $0xFFFFFFFF, %edx
pending:
DATA32 call EXT_C(real_to_prot)
.code32
mov %edx, %eax
pop %ebp
ret
Exemplo para interrogar o controlador de teclado:
Start:
cli
mov al,2 ; dissable IRQ 1
out 21h,al
sti
;--------------------------------------
; Main-Routine
AGAIN:
in al,64h ; get the status
test al,1 ; check output buffer
jz short NOKEY
test al,20h ; check if it is a PS2Mouse-byte
jnz short NOKEY
in al,60h ; get the key
; insert your code here (maybe for converting into ASCII...)
NOKEY:
jmp AGAIN
;--------------------------------------
; At the end
cli
xor al,al ; enable IRQ 1
out 21h,al
sti