- A multiplicação de ponto flutuante da VU do PS2 tem um erro operacional de 1 bit, então, para certos valores,
1 * Xpode ser diferente deX - Segundo o manual de desenvolvedor da VU,
X * 1tem precisão garantida, mas1 * Xnão tem a mesma garantia; essa diferença vira um sinal de detecção de emulador - O exemplo usa 129.5f, um dos valores problemáticos encontrados por força bruta, para verificar a diferença de comportamento entre um PS2 real e emuladores
- A implementação tem uma estrutura simples: no modo macro da VU0, multiplica
129.5fpor1e compara apenas se o resultado difere da entrada original - PCSX2, Play!, DobieStation e hps2x64 atualmente não emulam esse comportamento, e a dificuldade de detecção é avaliada como 1/5
Erro de 1 bit na multiplicação da VU do PS2
- Este método é o segundo item da série sobre detecção de emuladores de PS2 e pode ser usado na VU1, no modo micro da VU0 e no modo macro da VU0
- O exemplo usa o modo macro da VU0 para simplificar a implementação
- Como a VU0 é usada como um coprocessador, é possível executar diretamente pela CPU EE
- Não é necessário lidar com um programa VU separado
- Nas instruções de multiplicação como
MULeMULido manual de desenvolvedor da VU, há uma observação de que existe um erro operacional de 1 bit1 * Xpode ser diferente do valor originalX- Ao usar
VF[fs]como multiplicando, a precisão do resultado no formatoX * 1é garantida
- Não se sabe exatamente por que o bit é perdido
Valor de detecção e forma de implementação
- Para detectar esse erro, é necessário um número que provoque o problema, e o método de busca mais simples é a força bruta
- O autor criou no passado uma lista dos primeiros 250 números que causam o problema em intervalos de 0,5, e publicou essa lista em um gist
- O código de exemplo usa 129.5f como o número-alvo da detecção
- Define
129.5femVF1comQMTC2 - Cria
1emVF2comVADDw - Calcula
VF1 = 1 * 129.5fcomVMUL - Traz o resultado para o lado da EE com
QMFC2e o compara com a entrada
- Define
- O valor de retorno é
in[0] != out[0]; se o valor original e o resultado da multiplicação forem diferentes, conclui-se que o erro de multiplicação da VU está presente
Impacto por emulador
- Atualmente, PCSX2, Play!, DobieStation e hps2x64 não emulam esse comportamento de multiplicação da VU do PS2
- Como basta multiplicar um único número por
1e verificar apenas o resultado, a dificuldade desse método de detecção fica no nível 1/5
1 comentários
Comentários no Hacker News
Um ARM real, por causa do pipeline, lê PC+8 enquanto está executando no PC e decodificando PC+4, então a instrução recém-armazenada não deveria ter efeito. Um emulador que não emule o pipeline de hardware acabaria executando essa instrução
Um texto que explica isso em mais detalhes, junto com várias técnicas antiemulação de 2004: https://mgba.io//2014/12/28/classic-nes/
Acho que, por alguns ciclos, o valor do registrador ficava indefinido. Escrever código assembly rigidamente otimizado para esse tipo de chip era bem horrível, como jogar um clone da Zachtronics de gosto especialmente duvidoso
Muito mais tarde, alguém encontrou outro caso de borda que não era detectado: uma instrução repetitiva de string que sobrescrevia a si mesma
https://silviocesare.wordpress.com/2009/02/02/anti-debugging...
O x86 tem esse mecanismo, mas não sei ao certo se ele acabou sendo removido nas variantes de 64 bits
Você não só precisa conhecer todos os comportamentos peculiares do hardware e do software originais, como também precisa reproduzi-los exatamente, por mais bizarros que sejam. Isso já é difícil por si só, mas ainda é preciso considerar o impacto no desempenho
Usar um recompilador JIT não pode ser perfeitamente preciso ciclo a ciclo em relação ao hardware original, mas, a menos que o código do jogo tenha sido feito de propósito para quebrar emuladores, em geral isso não é um problema
O Dolphin também precisou lidar com esse equilíbrio quando alguns jogos comerciais de Wii incluíram código antiemulador que explorava detalhes do comportamento de cache da CPU real do Wii. Em teoria, seria possível emular o cache da CPU real e rodar o jogo sem problemas, mas a sobrecarga de desempenho provavelmente deixaria tudo umas 10 vezes mais lento, tornando o jogo injogável; então a opção foi usar patches de contorno
https://dolphin-emu.org/blog/2017/02/01/dolphin-progress-rep...
Parece que, na prática, é preciso entender eletrônica e uma magia profunda de programação
No começo — e, na maior parte, até no fim — você não precisa entender nenhuma magia profunda. Em geral, basta olhar a especificação e implementar aquilo. É preciso ter habilidade para estruturar o código de modo que ele não vire uma bagunça, mas existem padrões comuns, e tudo fica muito mais fácil depois de escrever um ou dois emuladores
Quase nunca é preciso entender eletrônica. O que você emula é o comportamento. Quando se descobre um bug no comportamento do hardware original, normalmente basta colocar um tratamento especial no emulador. Conhecimento de eletrônica pode ajudar a entender por que aquele comportamento surgiu, mas isso é mais interesse histórico do que algo prático
Há dificuldades próprias. Quando surge um problema, normalmente você acaba depurando três coisas ao mesmo tempo: seu entendimento do hardware, a implementação do emulador e o jogo que está sendo emulado. Pode ser difícil restringir a causa exata. Ainda assim, recomendo simplesmente fazer uma versão aproximada. Não é bonito, mas todo emulador está cheio de tratamentos especiais para fazer jogos populares rodarem de algum jeito. Se alguns hacks sujos fazem o jogo rodar, então faça isso. Você não precisa implementar com precisão o comportamento do hardware original; precisa fazer o jogo rodar
Uma CPU de 8 bits é uma máquina de estados simples com alguns bytes de estado, isto é, registradores. Você lê o programa byte a byte e imita o que a CPU faria depois de ler aquele byte. São operações muito simples, como somar e subtrair números ou ler e armazenar bytes
http://www.6502.org/users/obelisk/6502/registers.html
http://www.6502.org/users/obelisk/6502/instructions.html
Um emulador da CPU 6502 lê os próximos bytes do programa, interpreta esses bytes como instruções e executa as instruções. Nesse processo, ele atualiza alguns registradores ou contadores da CPU, realiza operações aritméticas ou bit a bit e, quando necessário, lê ou armazena 1 byte de dados de uma posição para outra. Esse processo se repete em um loop infinito
Isto é uma simulação do ciclo de busca-decodificação-execução
https://en.wikipedia.org/wiki/Instruction_cycle
Antigamente, para tocar arquivos de música SID, portei um interpretador 6502 do UNIX para o Classic Macintosh. Como bastava rodar rápido o suficiente, a precisão de ciclos de clock não era importante
Funcionava chamando código C a partir do interpretador
Ainda tenho vontade de tentar, mas não tenho tempo
Fora isso, o comentário irmão do @xcv123 está absolutamente correto
Se um dia se tornar possível replicar o PS2 em FPGA, descobrir como esse comportamento acontecia será um projeto divertido para alguém
Não há garantia de que uma versão em FPGA do PS2 não implemente bugs iguais ou parecidos
Ponto flutuante em software seria lento, mas a solução comum provavelmente seguiria o emulador de PS2 do PS4: colocar em uma whitelist, para cada jogo, os trechos de código em que o caminho de ponto flutuante em software é permitido
Demorei demais para perceber que isso falava do PlayStation 2, não da porta Personal System/2 que conecta mouse e teclado
Siglas de três letras podem dificultar muito a identificação do contexto, porque, ao jogar só a sigla no Google, com bastante frequência a maior parte dos resultados é quase irrelevante