As tabelas Near Call / Jump nem sempre funcionam em um gerenciador de inicialização
Estou desenvolvendo um gerenciador de inicialização simples e deparei com um problema em alguns ambientes em que instruções como estas não funcionam:
mov si, call_tbl ; SI=Call table pointer
call [call_tbl] ; Call print_char using near indirect absolute call
; via memory operand
call [ds:call_tbl] ; Call print_char using near indirect absolute call
; via memory operand w/segment override
call near [si] ; Call print_char using near indirect absolute call
; via register
Cada um deles envolve envolvimento indiretoLIGAR compensações absolutas de memória. Descobri que tenho problemas se usar itens semelhantesJMP mesas. As chamadas e saltos que são relativos não parecem ser afetados. Código como este funciona:
call print_char
Eu segui os conselhos apresentados no Stackoverflow por pôsteres discutindo os prós e contras de escrever um gerenciador de inicialização. Em particular, eu vi issoStackoverflow responda comDicas gerais sobre o carregador de inicialização. A primeira dica foi:
Quando o BIOS salta para o seu código, você não pode confiarCS,DS,ES,SS,SP registradores com valores válidos ou esperados. Eles devem ser configurados adequadamente quando o seu carregador de inicialização é iniciado. Você só pode garantir que o seu carregador de inicialização seja carregado e executado a partir do endereço físico 0x07c00 e que o número da unidade de inicialização seja carregado no diretórioDL registo.Seguindo todos os conselhos, não confiei emCS, Montei uma pilha e configureiDS apropriado para oORG (Deslocamento de origem) eu usei. Eu criei um exemplo mínimo verificável completo que demonstra o problema. Eu construí isso usandoNASM, mas não parece ser um problema específico paraNASM.
Exemplo MínimoO código a ser testado é o seguinte:
[ORG 0x7c00]
[Bits 16]
section .text
main:
xor ax, ax
mov ds, ax ; DS=0x0000 since OFFSET=0x7c00
cli ; Turn off interrupts for potentially buggy 8088
mov ss, ax
mov sp, 0x7c00 ; SS:SP = Stack just below 0x7c00
sti ; Turn interrupts back on
mov si, call_tbl ; SI=Call table pointer
mov al, [char_arr] ; First char to print 'B' (beginning)
call print_char ; Call print_char directly (relative jump)
mov al, [char_arr+1] ; Character to print 'M' (middle)
call [call_tbl] ; Call print_char using near indirect absolute call
; via memory operand
call [ds:call_tbl] ; Call print_char using near indirect absolute call
; via memory operand w/segment override
call near [si] ; Call print_char using near indirect absolute call
; via register
mov al, [char_arr+2] ; Third char to print 'E' (end)
call print_char ; Call print_char directly (relative jump)
end:
cli
.endloop:
hlt ; Halt processor
jmp .endloop
print_char:
mov ah, 0x0e ; Write CHAR/Attrib as TTY
mov bx, 0x00 ; Page 0
int 0x10
retn
; Near call address table with one entry
call_tbl: dw print_char
; Simple array of characters
char_arr: db 'BME'
; Bootsector padding
times 510-($-$) db 0
dw 0xAA55
Eu construo umISO imagem e uma imagem de disquete de 1,44 MB para fins de teste. Estou usando um ambiente Debian Jessie, mas a maioria das distribuições Linux seria semelhante:
nasm -f bin boot.asm -o boot.bin
dd if=/dev/zero of=floppy.img bs=1024 count=1440
dd if=boot.bin of=floppy.img conv=notrunc
mkdir iso
cp floppy.img iso/
genisoimage -quiet -V 'MYBOOT' -input-charset iso8859-1 -o myos.iso -b floppy.img -hide floppy.img iso
Acabo com uma imagem de disquete chamadafloppy.img
e umISO imagem chamadamyos.iso
.
Na maioria das condições, esse código funciona, mas em vários ambientes não funciona. Quando funciona, simplesmente imprime isso no visor:
BMMME
Eu imprimoB
usando um típicoLIGAR com deslocamento relativo, parece funcionar bem. Em alguns ambientes, quando executo o código, apenas recebo:
B
E então parece simplesmente parar de fazer qualquer coisa. Parece imprimir oB
corretamente, mas então algo inesperado acontece.
Ambientes que parecem funcionar:
QEMU inicializado com disquete e ISOVirtualBox inicializado com disquete e ISOVMWare 9 inicializado com disquete e ISODosBox inicializado com disqueteEmbalado oficialmenteBochs(2.6) no Debian Jessie usando imagem de disqueteBochs 2.6.6 (construído a partir do controle de origem) no Debian Jessie usando imagem de disquete eISO imagemSistema AST Premmia SMP P90 de meados dos anos 90 usando disquete eISOAmbientes que não funcionam conforme o esperado:
Embalado oficialmenteBochs(2.6) no Debian Jessie usandoISO imagemSistema baseado em 486DX com BIOS AMI do início dos anos 90 usando imagem de disquete. Os CDs não inicializam neste sistema, portanto não foi possível testá-lo.O que eu acho interessante é queBochs (versão 2.6) não funciona como esperado no Debian Jessie usando umISO. Quando eu inicializo a partir do disquete com a mesma versão, ele funciona conforme o esperado.
Em todos os casos, oISO e a imagem do disquete parecia carregar e começar a rodar desdeTUDO casos, pelo menos, foi capaz de imprimirB
no visor.
B
e nada mais?Por que alguns ambientes funcionam e outros falham?Isso é um bug no meu código ou no hardware / BIOS?Como posso corrigi-lo para que eu ainda possa usar tabelas Jump e Call indiretas próximas para compensações absolutas de memória? Estou ciente de que posso evitar essas instruções completamente e isso parece resolver meu problema, mas gostaria de entender como e se posso usá-las adequadamente em um gerenciador de inicialização.