Skip to content
On this page

Criando o andaime

Então, para não perder mais tempo do que perdi apenas tentando entender a ideia de não conseguir nem mesmo executar um "add" sem ter que escrevê-lo eu mesmo, não perdi muito tempo e criei as pastas, é index.html e bytepusher.js.

O HTML não poderia ser mais simples:

html
<corpo>
     <canvas
         largura="256"
         altura="256"
         style="transform: scale(4); transform-origin: superior esquerdo"
         id="tela"
     ></canvas>
     <script src="bytepusher.js"></script>
</body>

Basicamente, estou apenas criando uma tela que seria usada para a tela e dimensionando-a com algum css embutido para ver melhor os pixels. Então, estou importando um script que seria o próprio BytePusher(BP).

Por enquanto, o arquivo JS está vazio.

Entendendo as especificações

Na página Wiki, as especificações do BP são:

ComponenteEspecificação
taxa de quadros60 quadros por segundo
CPUByteByteJump com endereços de 3 bytes
Velocidade da CPU65536 instruções por quadro (3932160 instruções por segundo, ~3,93 MHz)
Ordenação de bytesBig-endian
Memória16 MiB RAM
Gráficos256*256 pixels, 1 byte por pixel, 216 cores fixas
Sommono de 8 bits, valores assinados. 256 amostras por quadro (15360 amostras por segundo), canal único
Teclado16 teclas, organizadas em 4 linhas por 4 colunas (que mapearei para setas, ASDFZXCVB, Enter, Esc, Espaço e LeftShift)

Assim, com base nisso, devemos ter 5 dispositivos principais para trabalhar:

  1. A própria CPU
  2. A Memória
  3. A exibição 256*256
  4. Mesa de Som
  5. Teclado

E nós temos que programá-lo por nós mesmos. Yay!

O mapa da memória

Para programar corretamente a CPU, temos que entender o que ela acessa em cada loop, então aqui está o mapa de memória:

EndereçobytesDescrição
02Estado do teclado. Cada bit corresponde a uma tecla no teclado, com o bit X representando a tecla X. Um valor de 1 significa que a tecla foi pressionada, enquanto 0 significa que não.
23Endereço de busca do contador do programa. Esse endereço é obtido no início de cada quadro e usado como ponto de partida para a execução do programa.
51Endereço de pixel. O valor ZZ representa o número da página e XX e YY representam as coordenadas horizontal e vertical do pixel, respectivamente. Para um determinado pixel nas coordenadas (XX, YY) na página ZZ, o endereço pode ser calculado como ZZYYXX.
62Endereço de amostra de áudio. O valor XXYY representa o endereço de uma amostra de áudio, com XX e YY fornecendo os bits superior e inferior do endereço, respectivamente. O terceiro byte ZZ representa os dados de amostra de áudio reais armazenados naquele endereço. (Não vou fazer agora)

E o loop externo é:

  1. Aguarde o próximo tick do timer (60 ticks são gerados por segundo).
  2. Pesquise as chaves e armazene seus estados como um valor de 2 bytes no endereço 0.
  3. Busque o contador de programa de 3 bytes do endereço 2 e execute exatamente 65.536 instruções.
  4. Envie o bloco de dados de pixel de 64 KiB designado pelo valor do byte no endereço 5 para o dispositivo de exibição. Envie o bloco de dados de amostra de 256 bytes designado pelo valor de 2 bytes no endereço 6 para o dispositivo de áudio.
  5. Volte para a etapa 1.

Ou, em outras palavras, para cada segundo:

  1. Crie um let tick = 60;
  2. Crie um loop for decrescente para tick;
  3. Percorra todas as teclas pressionadas no momento e armazene seus estados em suas respectivas memórias;
  4. Crie outro loop for, desta vez para cada instrução;
  5. Depois de sair do for loop, envie o endereço que inicia o array de 64kb bytes (256 * 256) para o Pixel Address desenhar no display
  6. Logo após, envie o endereço que inicia o array de 256 bytes para o Audio Address tocar no aparelho de som
  7. Aguarde até o próximo quadro
  8. Vá para a etapa 1.

Com base nisso, vamos criar o loop principal no bytepusher.js:

js
função principal() {
     setInterval(() => {
         deixe carrapatosEsquerda = 60;
         while (ticksEsquerda) {
             ticksLeft--;
         }
     }, 1000);
}

Não é muito, mas é um trabalho honesto.

Logo depois disso, criei as memórias:

js
const MEMORY_MAP = new Uint8Array(2 ** 6);
const MEMORY_ROM = new Uint8Array(2 ** 27);
const PAGE_SIZE = 2 ** 3;

E atualize a função de loop para ser um pouco mais legível;

js
função principal() {
     setInterval(() => {
         const KEYS_MAP = MEMORY_MAP.slice(0x0, PAGE_SIZE * 2);
         const INSTRUCTIONS_MAP = MEMORY_MAP.slice(
             KEYS_MAP.comprimento,
             KEYS_MAP.length + PAGE_SIZE * 3
         );
         const GFX_MAP = MEMORY_MAP.slice(
             INSTRUCTIONS_MAP.comprimento,
             INSTRUCTIONS_MAP.comprimento + PAGE_SIZE * 1
         );
         const SOUND_MAP = MEMORY_MAP.slice(
             GFX_MAP.comprimento,
             GFX_MAP.length + PAGE_SIZE * 2
         );

         deixe carrapatosEsquerda = 60;
         while (ticksEsquerda) {
             ticksLeft--;
         }
         desenharToCanvas();
     }, 1000);
}

::: informações Vale a pena notar que não tenho ideia do que estou fazendo, mas continuo codificando enquanto parece funcionar e fazer sentido (na minha mente real). :::

Então aproveitei para criar uma função initMemory() para valores padrão;

OK. Deixa para lá.

Enquanto eu tentava resolver, o código mudou muito conforme descobri maneiras muito melhores de conseguir algo.

Em vez de escrever um tutorial ou algo assim, vou esperar até obter uma exibição funcional e explicar o código aqui.

Released under the MIT License.