Dispositivos CPU
1. Visão geral
Derivados de dispositivos de CPU são, sem surpresa, usados para
implementar a emulação de CPUs, MCUs e SOCs. Um dispositivo de CPU é
inicialmente uma combinação de device_execute_interface,
device_memory_interface, device_state_interface e
device_disasm_interface. Consulte a documentação apropriada, se
estiver disponível.
Dois outros recursos são específicos dos dispositivos da CPU, o DRC e o suporte a interrupções.
2. DRC
A FAZER.
3. Interruptibilidade
3.1 Definição
Uma CPU com a capacidade de interromper é definida como um núcleo capaz
de interromper a execução de uma instrução a qualquer momento, sair do
execute_run e continuar de onde parou no próximo execute_run.
Isso inclui a capacidade de abortar um acesso à memória emitido, sair do
execute_run e, em seguida, reemitir exatamente o mesmo acesso na
próxima vez que o execute_run for invocado.
3.2 Requisitos para a implementação
Os acessos à memória devem ser feitos com read_interruptible ou
write_interruptible num memory_access_specific ou num
memory_access_cache. O acesso deve ser feito conforme a largura e o
alinhamento do barramento.
Após cada acesso, o núcleo deve testar se icount <= 0. Esse teste
deve ser feito após diminuir o icount pelo tempo gasto pelo próprio
acesso para limitar a quantidade de testes. Se o icount chegar a
0 ou menos, isso significa que a emulação da instrução deve ser
suspensa.
Para saber se o acesso precisa ser reemitido, o
access_to_be_redone() deve ser invocado. Se o retorno for
verdadeiro, o tempo gasto pelo acesso deverá ser creditado de volta,
pois ele ainda não aconteceu, e o acesso deverá ser reemitido. A chamada
access_to_be_redone() limpa o sinalizador de reemissão. Caso precise
verificar o sinalizador sem limpá-lo, use
access_to_be_redone_noclear().
O núcleo deve fazer uma contabilidade suficiente para, eventualmente, reiniciar a execução da instrução logo antes do acesso ou logo após o teste, dependendo da necessidade de reemissão.
Por fim, para indicar o suporte ao restante da infraestrutura, ele deve
substituir cpu_is_interruptible() para retornar true.
3.3 Exemplo de uma implementação com geradores
Para garantir um desempenho decente, as implementações atuais (h8, 6502 e 68000) usam um gerador python para gerar duas versões de cada interpretador de instruções, uma para a emulação normal e outra para reiniciar a instrução.
A versão reiniciada tem a seguinte aparência (para uma CPU com 4 ciclos por acesso):
void device::execute_inst_restarted()
{
switch(m_inst_substate) {
case 0:
[...]
m_address = [...];
m_mask = [...];
[[fallthrough]];
case 42:
m_result = specific.read_interruptible(m_address, m_mask);
m_icount -= 4;
if(m_icount <= 0) {
if(access_to_be_redone()) {
m_icount += 4;
m_inst_substate = 42;
} else
m_inst_substate = 43;
return;
}
[[fallthrough]];
case 43:
[...] = m_result;
[...]
}
m_inst_substate = 0;
return;
}
A versão não reiniciada é a mesma, com a chave e a limpeza final
removidas de m_inst_substate.
void device::execute_inst_non_restarted()
{
[...]
m_address = [...];
m_mask = [...];
m_result = specific.read_interruptible(m_address, m_mask);
m_icount -= 4;
if(m_icount <= 0) {
if(access_to_be_redone()) {
m_icount += 4;
m_inst_substate = 42;
} else
m_inst_substate = 43;
return;
}
[...] = m_result;
[...]
return;
}
O loop principal será semelhante a este:
void device::execute_run()
{
if(m_inst_substate)
call appropriate restarted instrution handler
while(m_icount > 0) {
debugger_instruction_hook(m_pc);
call appropriate non-restarted instruction handler
}
}
Portanto, a ideia é que m_inst_substate indique onde você está numa
instrução, mas apenas quando ocorre uma interrupção. Caso contrário,
ele permanece em 0 e, essencialmente, nunca é examinado. Ao ter duas
versões da interpretação permite remover a sobrecarga da troca e a
limpeza dos estados secundários no final da instrução.
Não é obrigatório usar um método baseado em gerador, mas ainda não foi encontrado nenhum outro que não tenha implicações inaceitáveis para o desempenho.
3.4 Interagindo com o DRC
Nesse ponto, a interruptibilidade e o DRC são totalmente incompatíveis. Não temos nenhum método para interromper o código gerado antes ou após um acesso. Isso é teoricamente possível, mas definitivamente não é trivial.