Consola de jogos stm32

Alguns atiradores para stm32; como, porque, o que aconteceu.



Prefácio


Sendo um fã da “velha” escola de atiradores, por um lado, e desenvolvedor incorporado, por outro, sempre me perguntei como e por que os autores daquela época conseguiram implementar um novo gênero que exigia abordagens completamente novas em um hardware muito “modesto”. E decidi tentar lançar algo semelhante usando soluções baseadas no MK moderno - aqui existem recursos bare-metal e "modestos" e uma ferramenta de depuração bastante poderosa (stm32, IMHO). E assim, minha escolha caiu na placa de desenvolvimento de descoberta stm32f769i.

Notas


No momento, a montagem é possível apenas no ambiente Keil MDK (carregador de inicialização, jogos) ou usando arm-gcc + make (somente carregador de inicialização). Portas atualmente disponíveis para - Quake I (+ mods), Doom (+ mods), Duke Nukem (+ mods), Hexen, Heretic. Com todas as modificações, a lista pode ser significativamente expandida.

Vamos começar


Neste artigo, tentarei descrever brevemente as principais idéias e princípios de sua implementação no caminho para criar um console de jogos em particular para a descoberta do stm32f769i. Além disso, tentarei evitar detalhes técnicos detalhados, mas busco o objetivo de apresentar ao leitor outra opção para usar o MK moderno. Por "console de jogos" - quero dizer um dispositivo independente com a capacidade de executar aplicativos de "usuário" sem atualizar o software principal.

Arquitetura


Como a implementação final requer a capacidade de executar independentemente vários jogos sem atualizar o firmware MK, surgiu a necessidade de algumas versões do "gerenciador de inicialização", portanto:

  1. Carregador; trabalhar com memória - “instalação” de um aplicativo (jogo), iniciar.
  2. Motorista; Nível de sistema HAL de serviço e funções relacionadas, transfira a API para o aplicativo.
  3. Inscrição; O programa final não retorna o controle ao gerenciador de inicialização.

1. Carregador de Inicialização


É baseado no modelo IAP (In-Application-Programming) - drivers usando um exemplo da ST-microelectronics.

A peculiaridade dessa abordagem é que não há necessidade de alterar a configuração da inicialização MK.
Todo o "corpo" do gerenciador de inicialização está na memória principal e, por sua vez, permite o uso da descoberta stm32f769i "pronta para uso".

A principal funcionalidade desse nível é ler o arquivo .bin, gravar seu conteúdo na memória MK e transferir o controle. Nesta fase, o ponto principal é ler o endereço do ponto de entrada e o endereço do ponteiro da pilha, o segundo não é necessário, porque aplicativo não retorna controle -
a pilha é compartilhada e não há necessidade de reescrever o ponteiro. Uma chamada também pode ser feita através de um ponteiro para uma função. Assim, o resultado será um carregador "híbrido" - sua parte "driver" continua servindo o aplicativo, enquanto os recursos do próprio carregador são descarregados.

2. Driver


O driver é feito na forma de um "invólucro" acima do nível HAL, fornecendo acesso aos recursos necessários - sistema de arquivos, monitor / monitor, som, controlador de jogo / sensor de monitor. Para uso posterior, a API do driver é transmitida na forma de uma estrutura de ponteiro através da "memória compartilhada" - uma parte da memória reservada para o carregador de inicialização e para o lado de retorno. Essa manipulação requer custos de memória, e talvez a melhor solução seria usar SWI (interrupção suave, chamada svc), mas para isso, por sua vez, você deve poder mudar o contexto - porque nem todas as chamadas podem ser tratadas em uma interrupção. Além disso, a memória "compartilhada" é usada para transmitir argumentos do usuário (por exemplo, através do console), um pré-requisito é adicionar o atributo no-init para esta seção,isso evitará substituí-lo pela biblioteca de tempo de execução no momento da inicialização do aplicativo do usuário.

3. Aplicação


Como resultado, a única coisa que você precisa saber no momento da criação do aplicativo é a arquitetura do núcleo do processador, não há dependências do HAL, também não há tabela de vetores de interrupção, todas as interrupções são processadas pelo carregador. Como resultado, o aplicativo utiliza muito menos espaço na memória do programa - devido ao fato de que parte da funcionalidade é "protegida" junto com o carregador / driver de inicialização, o que permite instalá-lo na área de dados SRAM (RAM interna). Por sua vez, isso pode reduzir significativamente o número de ciclos de gravação da memória Flash e também acelerar o processo de depuração e execução em geral. Das desvantagens - no momento da depuração, é possível chamar o aplicativo apenas de fora, por exemplo, usando um comando do console (porta COM sobre ST-Link, VCOM), para isso é usada uma versão muito simplificada da linha de comando.

Recursos:

Carregador , hal , Driver

Recursos de desenvolvimento


Memória


A primeira coisa com a qual tive que lidar foi com o problema de alocar um recurso de memória externa (SDRAM). Isso se deve ao fato de alguns jogos exigirem mais memória para a seção .bss (duke nukem ~ 5.5mbytes). Colocar esse volume é possível apenas em sdram, mas porque a mesma memória é usada pelo gerenciador de inicialização para armazenar dados temporários - imagens, som, conteúdo do arquivo etc. necessários apenas antes do início do aplicativo - foi decidido dividir o gerenciamento dessa parte da memória - em cada lado existe seu próprio malloc / free. Após o início - o driver usa ponteiros para as funções malloc \ free, que, se necessário, são passadas como parâmetro para a função de chamada. Ou seja, depois de iniciar o jogo, o driver não pode executar diretamente a alocação do sdram.Um fato interessante sobre o D-Cache e o I-Cache - devido às peculiaridades de lidar com a memória externa - você deve desativar as duas linhas antes de iniciar, porque O sdram é reinicializado, tudo ficaria bem, mas existe um "mas" - você sempre deve invalidar o cache, caso contrário, por padrão, ele preserva o estado válido de todas as linhas, enquanto elas foram substituídas no intervalo em que o cache foi desativado.
Outro recurso - todos os dados do carregador de inicialização são colocados na seção DTCM, permitindo não usar o cache ao acessar a memória (a MPU permite o mesmo) e, como resultado - problemas de coerência são resolvidos ao trabalhar com o DMA;
Em conjunto - CPU -> D-Cache -> Memória <- DMA

Artes gráficas


Na maioria das vezes, essas são várias funções de escala de imagem (2x2, 3x3),
a função de inicialização e o carregamento da paleta. O ponto principal é a indicação correta dos atributos de memória do quadro (executados através da configuração da MPU) - para o carregador de inicialização, será "Write-back, no write alocate", para eliminar o efeito de flicker, como o modo de buffer único é usado, enquanto o aplicativo usa "Write through, no write alocate", que atinge o FPS mais alto (Doom ~ 28-40).

Todas as portas de jogos existentes operam com gráficos de 8 bits, mas também é possível alternar para o modo de cores verdadeiras de 16 bits (requer modificação na lateral do jogo).
É possível dimensionar uma imagem de 8 bits usando o DMA2D, mas essa abordagem não deu resultado - custa ~ 1000 interrupções necessárias para processar uma imagem com uma resolução final de 640x480 pixels, além de gerar muitos artefatos em jogos - imagens individuais (sprites, polígonos) não serão totalmente renderizados, porque nesse caso, todo o processo de renderização no jogo ocorrerá em paralelo com o esboço da tela.

Som


Esta parte é feita na forma de um software simples de 16 bits e 16 canais, baseado em exemplos da microeletrônica ST, o controlador I2S também é usado. No momento, não há como converter formatos de áudio entre si - essa parte é implementada no nível do jogo, dependendo dos requisitos. Duke Nukem, na minha opinião, possui o mais rico conjunto de utilitários para trabalhar com som, incluindo e reverb.

Entrar


O driver do joystick também é feito usando a classe usb-hid (na verdade, o gamepad será definido como um mouse de computador ..). O sensor de exibição - como um gamepad, usa o mesmo canal para transmitir eventos, e isso é extremamente inconveniente.

Jogos


Doom


A porta stm32doom foi tomada como base ,
adicionado suporte de som, corrigido com as alterações mais recentes da desgraça de chocolate , foram adicionadas algumas correções devido a Killough, do prBoom. O jogo permite que você use todas as modificações e mapas disponíveis para a desgraça de chocolate, incluindo Cenário e som adicionados das versões 3DO e PS1 do jogo. A otimização gráfica foi adicionada - a resolução da renderização da textura depende da distância, a solução é mais ou menos, em locais diferentes - um ganho de + 3-7 quadros por segundo. acessível. A versão mais recente também adiciona suporte a sprites "transparentes" - tudo é baseado na tabela gerada de combinações de elementos da paleta - algo semelhante é usado no Quake II e em jogos baseados no mecanismo Build. Jogo incl. as modificações podem ser totalmente concluídas.

Recursos:

stm32doom , 3DO doom , chocolate doom , Versão Atual

Duke Nukem


Baseado no porto do chocolate duque . Não havia tempo para entender completamente o código fonte do jogo, então tudo permaneceu "como está", apenas pequenos defeitos foram corrigidos. A porta também permite que você execute modificações oficiais e não completamente - edição Atomic, inverno Nuclear, etc ...
Nota - no momento, nenhum dos episódios do jogo pode ser completamente concluído devido a defeitos existentes.

Recursos:

duque de chocolate , versão atual

Quake i


Infelizmente, o link para o repositório original não foi preservado e não consigo encontrá-lo, a
porta foi executada com o nome sdl quake. Entre os recursos, vale a pena notar a presença de uma arquitetura cliente-servidor, uma pilha muito “glutonosa” (~ 700kb), devido à qual surgiram muitas situações interessantes pela primeira vez (o armcc não monitora realmente seu uso), problemas generalizados de alinhamento - talvez isso se preocupe apenas o armcc do compilador, mas em quase todos os lugares onde há um apelo a um elemento da estrutura com mais de um byte de tamanho - você precisa usar a função wrapper para ler / escrever o byte - exceção de falha grave. O jogo é muito bom, com uma média de fps ~ 15. Também podem ser concluídos vários episódios, mais ou menos confortáveis ​​apenas no primeiro nível de dificuldade :)

Recursos:

Versão atual

Hexen, Heretic


Na maioria das vezes, eles herdam o mecanismo Doom, portanto, do ponto de vista da transferência, eles são quase idênticos (IMHO). Em hexen adicionado a capacidade de iniciar o jogo no local selecionado, os jogos não podem ser concluídos completamente.

Recursos:

hexen stm32 , herético stm32

Resultado


Doom , Duke Nukem , Quake

Obrigado por sua atenção.

All Articles