Nota: Chegaram algumas respostas. Considere também votar em novas respostas.
- Lisp comum de happy5214
- C de luser droog
- Java a partir de NeatMonster
- Javascript de crempp
- C de Mike C
- C ++ de Darius Goad
- Postscript do luser droog
- C ++ de JoeFish
- Javascript de inteiramente subjetivo
- C de RichTX
- C ++ de Dave C
- Haskell de JB
- Python de ja
O 8086 é o primeiro microprocessador x86 da Intel. Sua tarefa é escrever um emulador para ele. Como isso é relativamente avançado, quero limitar um pouco:
- Apenas os seguintes opcodes precisam ser implementados:
- mov, empurrão, pop, xchg
- adicionar, adc, sub, sbb, cmp e, ou, xor
- inc, dec
- chamada, ret, jmp
- jb, jz, jbe, js, jnb, jnz, jnbe, jns
- stc, clc
- hlt, nop
- Como resultado, você só precisa calcular os sinalizadores de transporte, zero e sinal
- Não implemente segmentos. Suponha
cs = ds = ss = 0
. - Nenhum prefixo
- Nenhum tipo de interrupção ou porta IO
- Nenhuma função de string
- Nenhum código de operação de dois bytes (0F ..)
- Sem aritmética de ponto flutuante
- (obviamente) nada de 32 bits, sse, mmx, ... o que ainda não foi inventado em 1979
- Você não precisa contar ciclos ou fazer qualquer tempo
Comece com ip = 0
e sp = 100h
.
Entrada: Seu emulador deve pegar um programa binário em qualquer tipo de formato que você desejar como entrada (ler do arquivo, matriz predefinida, ...) e carregá-lo na memória no endereço 0.
Saída: a RAM de vídeo inicia no endereço 8000h, cada byte é um caractere (ASCII-). Emule uma tela 80x25 para o console. Trate zero bytes como espaços.
Exemplo:
08000 2E 2E 2E 2E 2E 2E 2E 2E 2E 00 00 00 00 00 00 00 ................
08010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08050 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 00 00 Hello,.world!...
Nota: Isso é muito semelhante ao modo de vídeo real, que geralmente está em 0xB8000 e possui outro byte por caractere para cores.
Critérios de vitória:
- Todas as instruções mencionadas precisam ser implementadas
Eu fiz um programa de teste não comentado ( link , fonte nasm ) que deve ser executado corretamente. Produz
......... Hello, world! 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ################################################################################ ## ## ## 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 ## ## ## ## 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 ## ## ## ## 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ################################################################################
Não tenho muita certeza se isso deve ser um codegolf; é uma tarefa difícil, então qualquer envio ganhará muitos votos positivos de qualquer maneira. Por favor comente.
Aqui estão alguns links para ajudá-lo nessa tarefa:
- formato de instruções , mais
- tabela opcode
- descrições opcode
- Decodificação de bytes R / M mod de 16 bits
- registradores , sinalizadores de registro
- 1979 Manual
Esta é a minha primeira entrada nesta plataforma. Se houver algum erro, indique-o; se eu perdi um detalhe, basta perguntar.
fonte
Respostas:
Sinta-se à vontade para jogar e jogar golfe: https://github.com/julienaubert/py8086
Eu incluí um depurador interativo também.
Existem três arquivos: emu8086.py (obrigatório) console.py (opcional para saída de exibição), disasm.py (opcional, para obter uma lista do asm no codegolf).
Para executar com a exibição (a nota usa maldições):
Para executar com o depurador interativo:
Para executar com o "depurador" não interativo:
O programa " codegolf " deve estar no mesmo diretório.
emu8086.py
console.py
disasm.py
No github
fonte
Haskell,
256234196 linhasEu tenho esse trabalho em andamento há algum tempo, pretendia polir um pouco mais antes de publicar, mas agora a diversão começou oficialmente, não há muito sentido em mantê-la oculta. Ao extrair, notei que ele tem exatamente 256 linhas, então suponho que esteja em um ponto "notável" de sua existência.
Conteúdo: apenas o conjunto de instruções 8086 é suficiente para executar o exemplo binário na perfeição. O código de modificação automática é suportado. (pré-busca: zero bytes)
Ironicamente, as primeiras iterações suficientes do código foram mais longas e suportaram menos o período do opcode. A refatoração acabou sendo benéfica tanto para o comprimento do código quanto para a cobertura do código de operação.
O que há: obviamente, segmentos, prefixos e opcodes multibyte, interrupções, portas de E / S, operações de string e FP. Inicialmente, segui o
PUSH SP
comportamento original , mas tive que abandoná-lo após algumas iterações.Os resultados de sinalização de transporte provavelmente estão muito confusos em alguns casos de
ADC
/SBB
.Enfim, aqui está o código:
A saída para o binário de amostra fornecido corresponde perfeitamente à especificação. Experimente usando uma chamada como:
A maioria das operações não implementadas resultará simplesmente em uma falha na correspondência de padrões.
Ainda pretendo fatorar um pouco mais e implementar a saída ao vivo real com maldições.
Atualização 1: reduziu para 234 linhas. Organizei melhor o código por funcionalidade, realinhei o que poderia ser, tentei manter 80 colunas. E refatorou a ALU várias vezes.
Atualização 2: faz cinco anos, imaginei que uma atualização para compilar perfeitamente o GHC mais recente poderia estar em ordem. Pelo caminho:
<$>
e<*>
no Prelúdio.Como os comentários do código dizem, 5 linhas (a importação do Data.Char, os mapeamentos de registro de 8 bits e o despejo de tela) estão fora das especificações, então você pode descontá-las se sentir vontade :-)
fonte
.|.
? / 10charLinhas C - 7143 (própria CPU 3162 linhas)
EDIT: A compilação do Windows agora possui menus suspensos para alterar os discos virtuais.
Eu escrevi um emulador de PC 80186 / V20 completo (com CGA / MCGA / VGA, blaster de som, adlib, mouse etc.), não é uma coisa trivial imitar um 8086 de qualquer maneira. Demorou muitos meses para ser totalmente preciso. Aqui está o módulo da CPU apenas fora do meu emulador.
http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c
Serei o primeiro a admitir que uso muitas variáveis globais neste emulador. Comecei a escrever isso quando ainda era novo em C, e isso mostra. Preciso limpar um pouco desses dias. A maioria dos outros arquivos de origem não parece tão feia.
Você pode ver todo o código (e algumas capturas de tela, uma abaixo) por aqui: http://sourceforge.net/p/fake86
Eu ficaria muito feliz em ajudar qualquer pessoa que queira escrever por conta própria, porque é muito divertido e você aprende muito sobre a CPU! Isenção de responsabilidade: eu não adicionei a emulação 8080 do V20, pois quase nunca foi usada em um programa para PC. Parece muito trabalho sem lucro.
fonte
Postscript (
130200367517531222246 linhas)Ainda um trabalho em andamento, masEu queria mostrar algum código em um esforço para incentivar outras pessoas a mostrar algum código .O conjunto de registradores é representado como uma sequência, de modo que os vários registradores de tamanho de bytes e palavras podem se sobrepor naturalmente, referindo-se a substrings. As seqüências de caracteres são usadas como ponteiros, para que um registro e um local de memória (substring da sequência de memória) possam ser tratados uniformemente nas funções do operador.
Depois, há algumas palavras para obter e armazenar dados (bytes ou palavras) de um "ponteiro", da memória, de mem [(IP)] (aumento de IP). Depois, existem algumas funções para buscar o byte MOD-REG-R / M e definir as variáveis REG e R / M e MOD e decodificá-las usando tabelas. Em seguida, o operador funciona, digitado no byte opcode. Portanto, o loop de execução é simples
fetchb load exec
.Eu só tenho um punhado de opcodes implementados, mas gObter a decodificação do operando parecia um marco que eu queria compartilhar.editar: palavras adicionadas para estender números negativos. Mais opcodes. Correção de bug nas atribuições de registro. Comentários. Ainda trabalhando em bandeiras e preenchendo os operadores. A saída apresenta algumas opções: saída de texto para stdout na terminação, saída contínua usando códigos vt100, saída para a janela de imagem usando a fonte CP437.
editar: gravação concluída, iniciada a depuração. Obtém os quatro primeiros pontos de saída! Então o transporte dá errado. Sonolento.
edit: Eu acho que tenho a bandeira de transporte classificada. Parte da história aconteceu em comp.lang.postscript . Adicionei alguns aparelhos de depuração e a saída vai para a janela de gráficos (usando minha fonte Code-Page Type-3 Type-3 escrita anteriormente ), para que a saída de texto possa estar cheia de traços e despejos. Ele escreve "Olá Mundo!" e depois há aquele sinal de suspeita. Então muito mais nada. :( Vamos chegar lá. Obrigado por todo o incentivo!
editar: executa o teste até a conclusão. Os poucos bugs finais foram: XCHG fazendo 2 {ler loja} repita as cópias, é claro, em vez de trocas, E não definindo sinalizadores, (FE) INC tentando obter uma palavra de um ponteiro de bytes.
editar: Total de reescritas do zero usando a tabela concisa do manual ( virou uma nova página! ). Estou começando a pensar que levar em consideração a loja dos opcodes era uma péssima idéia, mas isso ajudou a manter o optab bonito. Nenhuma captura de tela desta vez. Eu adicionei um contador de instruções e um mod-trigger para despejar a memória de vídeo, para que ela se interceda facilmente com as informações de depuração.
edit: Executa o programa de teste novamente! Os poucos bugs finais para a reescrita mais curta estavam deixando de estender o byte imediato nos opcodes 83 (grupo "Imediato") e EB (JMP curto). O aumento de 24 linhas abrange rotinas adicionais de depuração necessárias para rastrear os erros finais.
E a saída (com o final da saída de depuração abreviada).
fonte
Javascript
Estou escrevendo um emulador 486 em javascript inspirado no jslinux. Se eu soubesse quanto trabalho seria, provavelmente nunca teria começado, mas agora quero finalizá-lo.
Então me deparei com seu desafio e fiquei muito feliz por ter um programa 8086 para testar.
Você pode "vê-lo" ao vivo aqui: http://codinguncut.com/jsmachine/
Eu tive um problema ao imprimir o buffer gráfico. Onde deveria haver espaços, a memória contém elementos "00". É correto interpretar "0x00" como espaço ou eu tenho um bug no meu emulador?
Felicidades,
Johannes
fonte
C ++
Gostaria de enviar nossa entrada para este desafio de código. Foi escrito em c ++ e executa o programa de teste perfeitamente. Implementamos 90% dos códigos operacionais de um byte e da segmentação básica (alguns desativados porque não funcionam com o programa de teste).
Gravação do programa: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator
Você pode encontrar o código em um arquivo zip no final da postagem do blog.
Screenshot executando o programa de teste:
Demorou um pouco de tempo ... se você tiver quaisquer perguntas ou comentários, sinta-se à vontade para me enviar uma mensagem. Certamente foi um ótimo exercício de programação para parceiros.
fonte
ret imm
instrução está errada (veja aqui ) e você está perdendo o0xff
grupo. Eu gosto das suas mensagens de erro: throw "O valor imediato não pode armazenar um valor, retarde.";cs
registroC
Grande desafio e meu primeiro. Criei uma conta apenas porque o desafio me intrigou muito. O lado ruim é que eu não conseguia parar de pensar no desafio quando tinha trabalho real, pagador e de programação para fazer.
Sinto-me compelido a executar uma emulação 8086 completa, mas esse é outro desafio ;-)
O código está escrito em ANSI-C, então apenas compile / vincule os arquivos .c, passe o binário do codegolf e vá.
fonte compactada
fonte
C ++ 1064 linhas
Projeto fantástico. Eu fiz um emulador da Intellivision há muitos anos, por isso foi ótimo flexionar meus músculos novamente.
Após cerca de uma semana de trabalho, eu não poderia estar mais animado quando isso aconteceu:
Um pouco de depuração mais tarde e ... SHAZAM!
Além disso, reconstruí o programa de teste original sem as extensões 80386, pois queria criar meu emulador fiel ao 8086 e não falsificar nenhuma instrução extra. Link direto para o código aqui: Arquivo Zip .
Ok, estou me divertindo muito com isso. Interrompi o gerenciamento de memória e tela, e agora a tela é atualizada quando o buffer de tela é gravado. Eu fiz um vídeo :)
http://www.youtube.com/watch?v=qnAssaTpmnA
Atualizações: a primeira passagem da segmentação está em vigor. Poucas instruções são realmente implementadas, mas eu a testei movendo o CS / DS e SS, e tudo ainda funciona bem.
Também foi adicionado um tratamento rudimentar de interrupção. Muito rudimentar. Mas eu implementei int 21h para imprimir uma string. Adicionadas algumas linhas à fonte de teste e carregadas também.
Se alguém tiver algum código de montagem bastante simples que teste os segmentos, eu adoraria brincar com ele.
Estou tentando descobrir até onde quero levar isso. Emulação de CPU completa? Modo VGA? Agora estou escrevendo o DOSBox.
12/6: Confira, modo VGA!
fonte
C ++ - 4455 linhas
E não, não fiz apenas os requisitos da pergunta. Eu fiz o INTEIRO 8086, incluindo 16 opcodes CONHECIDOS nunca antes. reenigne ajudou a descobrir esses códigos.
https://github.com/Alegend45/IBM5150
fonte
#include "cpu.h"
é difícil de ver.Javascript - 4.404 linhas
Eu me deparei com este post ao pesquisar informações para o meu próprio emulador. Este post sobre Codegolf foi absolutamente inestimável para mim. O programa de exemplo e o conjunto associado tornaram possível depurar facilmente e ver o que estava acontecendo.
Obrigado!!!
E aqui está a primeira versão do meu emulador Javascript 8086.
Recursos:
Demo
Eu tenho uma demo online, sinta-se à vontade para jogar com ela e me avise se você encontrar bugs :)
http://js86emu.chadrempp.com/
Para executar o programa codegolf
1) clique no botão de configurações
2) clique em carregar (você pode jogar com as opções de depuração aqui, como percorrer o programa). O programa codegolf é o único disponível no momento, estou trabalhando para ficar mais online.
Fonte
Fonte completa aqui. https://github.com/crempp/js86emu
Tentei colar aqui as tripas da emulação 8086 (como sugerido pela maçaneta da porta), mas excedeu o limite de caracteres ("O corpo é limitado a 30000 caracteres; você inseriu 158.272").
Aqui está um link rápido para o código que eu ia colar aqui - https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/8086.js
*Edit - updated for new demo and repo location
fonte
Java
Eu queria fazer esse desafio por tanto tempo e finalmente tirei um tempo para fazê-lo. Foi uma experiência maravilhosa até agora e estou orgulhoso de perceber que finalmente a completei.
Fonte
O código fonte está disponível no GitHub em NeatMonster / Intel8086 . Tentei documentar praticamente tudo, com a ajuda do holly 8086 Family Manual do Usuário .
Pretendo implementar todos os opcodes e recursos ausentes, portanto, você pode conferir o release 1.0 para uma versão com apenas os necessários para este desafio.
Muito obrigado a @copy!
fonte
Common Lisp - 580 loc (442 sem linhas em branco e comentários)
Eu usei esse desafio como uma desculpa para aprender o Common Lisp. Aqui está o resultado:
Aqui está a saída no Emacs:
Eu quero destacar três características principais. Esse código faz uso intenso de macros ao implementar instruções, como
mov
,xchg
e as operações artithmetic. Cada instrução inclui umadisasm-instr
chamada de macro. Isso implementa a desmontagem ao lado do código real usando uma variável global if sobre uma definida em tempo de execução. Estou particularmente orgulhoso da abordagem agnóstica do destino usada para escrever valores em registros e endereços indiretos. As macros que implementam as instruções não se importam com o destino, pois os formulários emendados para qualquer possibilidade funcionarão com asetf
macro Common Lisp genérica .O código pode ser encontrado no meu repositório GitHub . Procure o ramo "codegolf", pois eu já comecei a implementar outros recursos do 8086 no master. Eu já implementei os sinalizadores de excesso e paridade, junto com o registro FLAGS.
Existem três operações neste, não no 8086, o
0x82
0x83
versões e dos operadores lógicos. Isso foi pego muito tarde e seria muito complicado remover essas operações.Gostaria de agradecer ao @ja por sua versão em Python, que me inspirou no início deste empreendimento.
fonte
C -
319348 linhasEsta é uma tradução mais ou menos direta do meu programa Postscript para C. É claro que o uso da pilha é substituído por variáveis explícitas. Os campos de uma instrução são divididos nas variáveis
o
- instrução opcode byte,d
- direction field,w
- width field. Se for uma instrução "mod-reg-r / m", o byte mr-rm é lidostruct rm r
. A decodificação dos campos reg e r / m prossegue em duas etapas: calculando o ponteiro para os dados e carregando os dados, reutilizando a mesma variável. Então, para algo comoADD AX,BX
, primeiro x é um ponteiro para ax e y é um ponteiro para bx, depois x é o conteúdo (ax) e y é o conteúdo (bx). Há muita conversão necessária para reutilizar a variável para diferentes tipos como este.O byte opcode é decodificado com uma tabela de ponteiros de função. Cada corpo de função é composto usando macros para peças reutilizáveis. A
DW
macro está presente em todas as funções do opcode e decodifica as variáveisd
ew
doo
byte do opcode. ARMP
macro executa o primeiro estágio de decodificação do byte "mr-rm" eLDXY
executa o segundo estágio. Os códigos de operação que armazenam um resultado usam ap
variável para manter o ponteiro no local do resultado e az
variável para manter o valor do resultado. Os sinalizadores são calculados após oz
valor ser calculado. As operaçõesINC
eDEC
salvam o sinalizador de transporte antes de usar aMATHFLAGS
função genérica (como parte doADD
ouSUB
submacro) e restaure depois, para preservar o Carry.Edit: bugs corrigidos!
Editar: expandido e comentado. Quando
trace==0
ele agora gera um comando ANSI mover para 0,0 ao descarregar o vídeo. Por isso, simula melhor uma exibição real. ABIGENDIAN
coisa (que nem funcionou) foi removida. Em alguns lugares, ele depende da ordem de bytes little-endian, mas pretendo corrigir isso na próxima revisão. Basicamente, todo acesso de ponteiro precisa passar pelas funçõesget_
eput_
que explicitamente (des) compõem os bytes na ordem LE.O uso de macros para os estágios das várias operações resulta em uma correspondência semântica muito próxima da maneira como o código postscript opera de maneira puramente seqüencial. Por exemplo, os quatro primeiros opcodes, 0x00-0x03, são todas instruções ADD com direção variável (REG -> REG / MOD, REG <- REG / MOD) e tamanhos de bytes / palavras, portanto, são representados exatamente da mesma forma na tabela de funções .
A tabela de funções é instanciada com esta macro:
que se aplica
OPF()
a cada representação opcode.OPF()
é definido como:Portanto, os quatro primeiros opcodes se expandem (uma vez) para:
Essas funções se distinguem pelo resultado da
DW
macro que determina a direção e os bits de byte / palavra diretamente do byte do código de operação. Expandir o corpo de uma dessas funções (uma vez) produz:Onde o loop principal já definiu a
o
variável:Expandir mais uma vez fornece toda a "carne" do código de operação:
E a função totalmente pré-processada passou por
indent
:Não é o melhor estilo C para uso diário, mas o uso de macros dessa maneira parece bastante perfeito para tornar a implementação aqui muito curta e direta.
Saída do programa de teste, com cauda da saída do rastreio:
Compartilhei algumas versões anteriores no comp.lang.c, mas elas não estavam muito interessadas.
fonte
indent
ed 5810 linhas.