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:
<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:
| Componente | Especificação |
|---|---|
| taxa de quadros | 60 quadros por segundo |
| CPU | ByteByteJump com endereços de 3 bytes |
| Velocidade da CPU | 65536 instruções por quadro (3932160 instruções por segundo, ~3,93 MHz) |
| Ordenação de bytes | Big-endian |
| Memória | 16 MiB RAM |
| Gráficos | 256*256 pixels, 1 byte por pixel, 216 cores fixas |
| Som | mono de 8 bits, valores assinados. 256 amostras por quadro (15360 amostras por segundo), canal único |
| Teclado | 16 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:
- A própria CPU
- A Memória
- A exibição 256*256
- Mesa de Som
- 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ço | bytes | Descrição |
|---|---|---|
| 0 | 2 | Estado 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. |
| 2 | 3 | Endereç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. |
| 5 | 1 | Endereç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. |
| 6 | 2 | Endereç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 é:
- Aguarde o próximo tick do timer (60 ticks são gerados por segundo).
- Pesquise as chaves e armazene seus estados como um valor de 2 bytes no endereço 0.
- Busque o contador de programa de 3 bytes do endereço 2 e execute exatamente 65.536 instruções.
- 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.
- Volte para a etapa 1.
Ou, em outras palavras, para cada segundo:
- Crie um let tick = 60;
- Crie um loop
fordecrescente paratick; - Percorra todas as teclas pressionadas no momento e armazene seus estados em suas respectivas memórias;
- Crie outro loop for, desta vez para cada instrução;
- 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
- Logo após, envie o endereço que inicia o array de 256 bytes para o Audio Address tocar no aparelho de som
- Aguarde até o próximo quadro
- Vá para a etapa 1.
Com base nisso, vamos criar o loop principal no bytepusher.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:
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;
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.