O device_disasm_interface e os desmontadores

Das capacidades

Os desmontadores são classes que fornecem desmontagem e o opcode meta informações para os núcleos da CPU e unidasm. O device_disasm_interface conecta um núcleo da CPU com o seu desmontador.

Os desmontadores

Definição

Um desmontador é uma classe que deriva de util::disasm_interface. Em seguida, ele tem dois métodos necessários de implementação, opcode_alignment e disassemble assim como 6 opcionais, interface_flags, page_address_bits, pc_linear_to_real, pc_real_to_linear e uma com quatro variantes possíveis, decrypt8/16/32/64.

opcode_alignment

u32 \ **opcode_alignment**\ () const

Retorna o alinhamento de opcode requisitado pela CPU nas unidades PC. Em outras palavras o alinhamento necessário para os registros PC da CPU. Tende a ser 1 (quase todos), 2 (68000...), 4 (mips, ppc...), com um excepcional 8 (processador paralelo tms 32082) e 16 (tms32010, instruções são 16-bits aligned e o PC targets bits). Deve ser a potência de dois para evitar que as coias se quebrem.

Note que processadores como o tms32031 que têm instruções em 32-bits onde os valores PC targets em 32-bits têm um alinhamento de 1.

disassemble

offs_t \ **disassemble**\ (std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)

Este é o método onde o trabalho de fato é acontece. Esse comando desmonta uma instrução no endereço PC e escreve o resultado para stream. Os valores a serem decodificados são recuperados da memória intermediária opcode. Um objeto data_buffer oferecem quatro métodos de acesso:

u8  util::disasm_interface::data_buffer::\ **r8**\  (offs_t pc) const
u16 util::disasm_interface::data_buffer::\ **r16**\ (offs_t pc) const
u32 util::disasm_interface::data_buffer::\ **r32**\ (offs_t pc) const
u64 util::disasm_interface::data_buffer::\ **r64**\ (offs_t pc) const

Eles leem os dados num determinado endereço e pegam o endianness e os PCs não lineares por acessos maiores que a largura do barramento. A variante do depurador também armazena em cache os dados lidos num bloco, então por essa razão um não deve ler os dados muito longe da base pc (ficar entre de 16K ou então, ter cuidado ao tentar seguir acessos indiretos, por exemplo).

Uma quantidade de CPUs tem um sinal externo que divide as buscas em parte um opcode e parte um parâmetro. Este é, por exemplo o sinal M1 do z80 ou o sinal SYNC do 6502. Alguns sistemas apresentam diferentes valores para a CPU dependendo se esse sinal for ativo, em geral usado para fins de proteção. Nestes CPUs a parte do opcode deve ser lida a partir da memória intermediária do opcode e o parâmetro part vindo da memória intermediária params. Eles serão ou não a mesma memória intermediária, tudo vai depender do próprio sistema.

O método retorna o tamanho da instrução em unidades de PC, com um valor máximo de 65535. Além disso, caso seja possível o desmontador deve dar algumas informações meta sobre o opcode por "OR-ing" no resultado:

  • STEP_OVER para chamadas de sub-rotina ou auto-decrementos de

    loops. Caso haja alguns slots com atraso, faça também OR com step_over_extra(n) onde n é o número da instrução.

  • STEP_OUT para o retorno das instruções da sub-rotina

Além disso, para indicar que esses sinalizadores são compatíveis, OU o resultado com SUPPORTED. Uma quantidade chata de desmontadores mentem sobre essa compatibilidade (eles fazem um OR com SUPPORTED mesmo sem gerar o STEP_OVER ou STEP_OUT, por exemplo). Não faça isso, pois quebra a funcionalidade do step over/step out do depurador.

interface_flags

u32 **interface_flags**\ () const

Esse método opcional mostra detalhes do desmontador. O valor zero predefinido é o correto na maioria das vezes. As bandeiras possíveis e que precisam ser "OR-ed" juntas, são:

  • NONLINEAR_PC: passar para o próximo opcode ou o próximo byte do opcode se não adicionar um ao pc. Usado para antigos PCs com base em LFSR.

  • PAGED: o PC é envolvido com um limite de página

  • PAGED2LEVEL: não apenas o PC envolve em algum tipo de limite de página, mas há dois níveis de paginação

  • INTERNAL_DECRYPTION: há alguma descriptografia escondida entre a leitura de AS_PROGRAM e o desmontador atual

  • SPLIT_DECRYPTION: há alguma descriptografia escondida entre a leitura do AS_PROGRAM e o desmontador atual, assim como essa descriptografia é diferente para os opcodes e os parâmetros

Note que, na prática, os sistemas de PC não lineares também são paginados, o PAGED2LEVEL implica no PAGED e o SPLIT_DECRYPTION implica em DECRYPTION.

pc_linear_to_real e pc_real_to_linear

offs_t **pc_linear_to_real**\ (offs_t pc) const
offs_t **pc_real_to_linear**\ (offs_t pc) const

Esses métodos devem estar presentes apenas quando NONLINEAR_PC estiver definido nos sinalizadores da interface. Eles devem converter o PC de e para um valor com destino a um domínio linear onde os parâmetros de instrução e a próxima instrução sejam alcançadas ao incrementar o valor. O pc_real_to_linear converte para aquele domínio, já o pc_linear_to_real é convertido de volta daquele domínio.

page_address_bits

u32 **page_address_bits**\ () const

Presente quando PAGED ou PAGED2LEVEL for definido, retorna a quantidade de endereços de bits na pagina inferior.

page2_address_bits

u32 **page2_address_bits**\ () const

Presente quando PAGED2LEVEL for definido, retorna a quantidade de endereços de bits na página superior.

decryptnn

u8  **decrypt8**\  (u8  value, offs_t pc, bool opcode) const
u16 **decrypt16**\ (u16 value, offs_t pc, bool opcode) const
u32 **decrypt32**\ (u32 value, offs_t pc, bool opcode) const
u64 **decrypt64**\ (u64 value, offs_t pc, bool opcode) const

Um destes deve ser definido quando INTERNAL_DECRYPTION ou SPLIT_DECRYPTION for configurado. O escolhido será aquele que leva o que opcode_alignment representa em bytes.

Esse método descriptografa um determinado valor do endereço PC (a partir do AS_PROGRAM) e retorna o que será passado para o desmontador. No caso da descriptografia dividida, o opcode indica se estamos no opcode (true) ou na parte da instrução do parâmetro (false).

Interface do desmontador, device_disasm_interface

Definição

Um núcleo de CPU deriva de device_disasm_interface através do cpu_device . Um método deve ser implementado, create_disassembler .

create_disassembler

util::disasm_interface \*\ **create_disassembler**\ ()

Esse método deve retornar um ponteiro para um novo objeto desmontado que foi recém-alocado. O solicitante apropria-se do objeto e lida com o seu tempo de vida.

Esse método será chamado no máximo uma vez durante a vida útil do objeto da CPU.

A comunicação e a configuração do desmontador

Alguns desmontadores precisam ser configurados. A configuração pode ser imutável (estático) duração da execução (como o modelo da CPU por exemplo) ou dinâmico (o estado de um sinalizador ou uma preferência de usuário). A configuração estática que pode ser feita seja por parâmetro(s) para o construtor do desmontador ou através da derivação da classe do desmontador principal. Caso a informação seja curta e sua semântica seja óbvia (como o nome do modelo), fique à vontade para usar um parâmetro. Caso contrário, deriva a classe.

A configuração dinâmica deve ser feita definindo primeiro uma estrutura de grupo público chamado "config" no desmontador, com o destruidor virtual e métodos virtuais puros para extrair as informações necessárias. Um ponteiro para essa estrutura deve ser passada para o construtor do desmontador. O núcleo da CPU deve então adicionar uma derivação dessa estrutura de configuração e implementar os métodos. O Unidasm terá que separar pequena classe da configuração de classes para que possa passar a informação.

Coisas que ainda faltam

Atualmente, não há como a GUI do depurador adicionar uma configuração para cada núcleo. Ela se faz necessária para o s2650 e os núcleos do saturn. É necessário também passar pela própria classe do núcleo da CPU uma vez que é retirado da estrutura de configuração.

Falta compatibilidade do unidasm para uma configuração individual dos núcleos da CPU. Isso se faz útil para muitas coisas, veja o código-fonte do unidasm para a um lista atual (comentários "Configuration missing").