Quando se é uma pessoa organizada (ou quando o projeto cresce/tende a crescer muito), o projeto deve ser desenvolvido de forma modularizada. Isso tanto por causa de conferência do código, quanto de reaproveitamento do mesmo.
Modularizar quase sempre em dividir o código em arquivos fontes diferentes. Ou seja, implica em montagem/compilação por partes. Por exemplo, dividir o jogo em 3 arquivos 1) “cabeçalho da ROM” (cabecalho.s) e 2) instruções do processador (codigo.s) 3) imagem da tela de abertura (imagem.s).
Para “compilar” (o correto em assembly é “montar“) isso:
$ m68k-megadrive-as -m68000 cabecalho.s -o cabecalho.elf
$ m68k-megadrive-as -m68000 codigo.s -o codigo.elf
$ m68k-megadrive-as -m68000 imagem.s -o imagem.elf
Como fazer tudo isso virar um único arquivo? Esse processo é chamado de ligação (linkagem ou link) e o programa GNU que faz isso é o ld (GNU linker).
Teoricamente, poderia se fazer desta forma:
$ m68k-megadrive-ld cabecalho.elf codigo.elf imagem.elf -o jogo.elf
$ m68k-megadrive-objcopy -O binary jogo.elf -o jogo.bin
No entanto, a saída pode não sair da forma esperada. Se ordem dos parâmetros for diferente (imagem.elf cabecalho.elf codigo.elf), o arquivo elf de união pode sair em uma ordem interna diferente. O problema é o objcopy, que, quando passa para o formato binário (dump de memória), ele segue a ordem das seções internas do elf, pelo que entendo. Isso faz com que o cabeçalho não fique necessariamente no começo da ROM, como é obrigatório ser. Quem determina essa ordem, é a seqüência dos arquivos objetos passadas ao ld. No entanto, como não sei se essa regra da ordem é ainda realmente válida e nem por quanto tempo vai durar, segue aqui uma forma segura de corrigir isso.
É necessário obrigar, então, com que o elf da união dos códigos esteja bem ordenado (sem se preocupar com a ordem dos parâmetros): que o cabeçalho fique na posição 0×00000000, que o código comece, por exemplo (o caso padrão), na posição 0×00000200, etc. Para ter esse controle, é preciso escrever um script pro linker que ordene as coisas da forma correta. O script é um arquivo texto comum contendo algo assim:
SECTIONS {
_cabecalho 0x00000000 :
{
*(cabecalho)
}
_principal 0x00000200 :
{
codigo.elf(.text)
}
_resto :
{
*(*)
}
}
Da forma como o script está escrito, o elf gerado (jogo.elf – nome definido na linha de comando) terá 3 sessões: _cabecalho (localizada na posição 0×00), _principal (residindo na posição 0×0200) e _resto. Nessa nova sessão chamada _cabeçalho , estará o código (no caso, serão apenas dados) assembly que estiver na seção cabecalho de qualquer .elf passado na linha de comando do ld. As instruções iniciais do jogo devem estar escritas no arquivo codigo.elf. A seção .text (que implica em ser código, em geral) dele ficará na posição 0×200. Por fim, vêm todas as outras seções e arquivos .elf não contemplados.
Isso não funcionou aqui. O objcopy descartou a seção _cabeçalho pelo fato de eu não ter usado nenhuma referência de lá em lugar algum, tornando-o desnecessário.
O script então ficou assim:
SECTIONS {
rom 0x00000000 :
{
*(cabecalho)
codigo.elf(.text)
}
resto :
{
*(*)
}
}
Para realizar a ligação, então fica:
$ m68k-megadrive-ld -dT ldscript cabecalho.elf codigo.elf imagem.elf -o jogo.elf
$ m68k-megadrive-objcopy -O binary jogo.elf -o jogo.bin
Repare que para chamar o script, usei a opção -dT .
E, assim, consegui fazer a geração do binário para o megadrive a partir de vários arquivos-objetos.
Obs.: Para quem tentar usar a diretiva assembly .org um aviso: no GNU assembler, ele dita a posição relativa na seção do .elf em questão (chama-se location counter ou lc), e não a posição absoluta que a diretiva ORG que a maioria dos assembler faz acontecer.
Filed under: gcc, megadrive | Leave a Comment
Tags: 68000, assembler, assembly, binutils, gas, genesis, ld, link, linker script, megadrive, sega
No Responses Yet to “Criando o binário para Megadrive a partir de mais de um arquivo-objeto”