Arquitetura IPC-8
Este documento descreve, com precisão suficiente para reimplementar, a máquina didática executada pela bancada do InsidePC. Tudo o que está aqui corresponde ao comportamento real do simulador — as tabelas de instruções e de portas são geradas a partir do próprio motor.
1. O modelo da máquina
A IPC-8 é uma máquina de acumulador: quase toda operação aritmética ou lógica usa o registrador A como um dos operandos e como destino do resultado. Essa escolha reduz o número de bits necessários para codificar uma instrução e torna o fluxo de dados fácil de acompanhar visualmente — que é o objetivo do projeto.
Ela segue o modelo de Von Neumann: instruções e dados residem no mesmo espaço de endereçamento e trafegam pelo mesmo barramento. Isso significa que um programa pode, em princípio, sobrescrever a si mesmo — e também que a CPU precisa de fases distintas para buscar instrução e buscar dado.
Como código e dados dividem o mesmo barramento, a CPU nunca busca uma instrução e um operando ao mesmo tempo. Esse é o chamado gargalo de Von Neumann, e é exatamente o problema que a cache L1 do LAB 04 ameniza.
Blocos funcionais
Decodifica o opcode presente no IR e gera a sequência de sinais que comanda todos os outros blocos. É ela que define quantos ciclos cada instrução consome.
Executa soma, subtração, AND, OR, XOR, NOT, deslocamentos e comparação, produzindo o resultado e as quatro flags de condição.
PC, IR, MAR, MDR, A, B, SP e o registrador de estado. Todos visíveis na bancada, aba Execução.
Traduz pedidos de leitura/escrita em ciclos de barramento, consultando antes a cache L1 de mapeamento direto.
2. Bits, bytes e hexadecimal
Um bit é a menor unidade de informação: dois estados possíveis, representados eletricamente por dois níveis de tensão. Oito bits formam um byte, capaz de assumir 256 valores distintos (0 a 255, ou $00 a $FF).
O hexadecimal existe por conveniência humana: cada dígito hexadecimal corresponde exatamente a quatro bits, então um byte é sempre escrito com dois dígitos e um endereço de 16 bits com quatro. Neste manual e na bancada, o prefixo $ indica hexadecimal, % indica binário e # indica um valor imediato.
Complemento de dois
A IPC-8 não distingue números com e sem sinal no armazenamento: o mesmo byte $FF vale 255 se você o interpretar como sem sinal e −1 se interpretar em complemento de dois. Quem decide a interpretação é o programa, olhando para as flags N (bit 7 do resultado) e V (estouro com sinal).
| Byte | Sem sinal | Com sinal | Flag N |
|---|---|---|---|
$00 | 0 | 0 | 0 |
$7F | 127 | +127 | 0 |
$80 | 128 | −128 | 1 |
$FF | 255 | −1 | 1 |
3. Registradores
Registradores são as memórias mais rápidas e mais escassas da máquina: ficam dentro do próprio die, ao lado da ALU, e são acessados sem nenhum ciclo de barramento.
| Registrador | Largura | Função |
|---|---|---|
PC | 16 bits | Contador de programa. Aponta para o próximo byte a ser buscado. Alterado por incremento, desvio, chamada, retorno e interrupção. |
IR | 8 bits | Registrador de instrução. Guarda o opcode que a UC está decodificando neste instante. |
MAR | 16 bits | Registrador de endereço de memória. O que está no MAR é o que aparece no barramento de endereços. |
MDR | 8 bits | Registrador de dados de memória. Toda informação que entra ou sai da memória passa por aqui. |
A | 8 bits | Acumulador. Operando principal e destino padrão das operações da ALU. |
B | 8 bits | Registrador auxiliar. Segundo operando em ADD B, SUB B e destino de TAB. |
SP | 16 bits | Ponteiro de pilha. Inicia em $01FF e decresce a cada byte empilhado. |
FLAGS | 4 bits | Z (zero), C (vai-um), N (negativo) e V (estouro com sinal). |
IE | 1 bit | Habilitação de interrupção, controlada por EI e DI. |
Sem eles, o endereço teria de ficar estável no barramento durante todo o acesso enquanto o dado trafega — o que é fisicamente inviável no mesmo conjunto de pinos. O MAR retém o endereço; o MDR retém o dado. Na bancada, o sinal ALE marca exatamente o instante em que o endereço é travado.
4. Formato das instruções e modos de endereçamento
Toda instrução começa por um byte de opcode. O que vem depois — nada, um byte ou dois bytes — depende do modo de endereçamento codificado naquele opcode.
NOT, INC, TAB, ADD B, HLT.LDA #$41.OUT $10.LDA $0040.Em JMP $F0A3, os bytes gravados são 30 A3 F0. A CPU busca primeiro $A3 (parte baixa) e depois $F0 (parte alta), montando o endereço com alto × 256 + baixo. Você pode conferir isso na aba Código da bancada.
5. Referência completa do conjunto de instruções
A tabela abaixo é gerada diretamente do motor de execução: se uma instrução aparece aqui, ela existe e funciona na bancada. A coluna RTL descreve a operação em Register Transfer Language, a mesma notação exibida no modo engenharia.
Legenda dos modos: imp implícito · reg registrador · imm imediato · port porta de E/S · abs absoluto.
6. A linguagem do montador
O montador integrado faz duas passagens: na primeira, calcula o endereço de cada linha e registra os rótulos; na segunda, resolve as referências e emite os bytes. É por isso que você pode saltar para um rótulo definido mais adiante no arquivo.
Sintaxe
| Elemento | Forma | Exemplo |
|---|---|---|
| Rótulo | NOME: no início da linha | LACO: DEC |
| Comentário | de ; até o fim da linha | HLT ; encerra |
| Hexadecimal | $ | LDA $40 |
| Binário | % | AND #%00000001 |
| Decimal | sem prefixo | LDA #10 |
| Caractere | aspas simples | LDA #'K' |
| Imediato | # antes do valor | ADD #1 |
Diretivas
| Diretiva | Efeito |
|---|---|
.ORG $F000 | Define o endereço de montagem das linhas seguintes. |
.BYTE $01, 10 | Emite bytes literais na posição atual. |
.WORD $7C00 | Emite uma palavra de 16 bits em little-endian — usado para preencher vetores. |
.ASCII "OK" | Emite os códigos ASCII dos caracteres da string. |
Se o seu programa habilita interrupções, precisa preencher o vetor de IRQ:
.ORG $FFFE seguido de .WORD MINHA_ISR. Sem isso, a CPU desviará para o endereço $0000.
7. O ciclo de instrução
Um processador não “roda um programa”: ele repete indefinidamente um ciclo curto. Todo o resto — sistemas operacionais, navegadores, jogos — é consequência de repetir esse ciclo bilhões de vezes por segundo.
- Busca (fetch) — o conteúdo do PC vai para o MAR, o sinal ALE trava o endereço, RD é ativado, a memória devolve um byte pelo MDR e o PC é incrementado.
- Decodificação (decode) — o byte passa do MDR para o IR. A UC identifica a operação, o modo de endereçamento e quantos bytes adicionais precisa buscar.
- Busca de operando — se o modo exigir, um ou dois bytes extras são buscados da memória, sempre incrementando o PC.
- Execução — a ALU calcula, a interface de memória lê ou escreve, ou o PC recebe um novo valor no caso de um desvio.
- Escrita (writeback) — o resultado é gravado no destino e as flags são atualizadas.
- Verificação de interrupção — se IE estiver ativo e houver IRQ pendente, o contexto é empilhado antes da próxima busca.
Na bancada, o painel Ciclo destaca a fase atual e o analisador lógico mostra CLK, ALE, RD, WR e IRQ. Use Shift+→ para avançar uma instrução inteira e → para avançar microoperação por microoperação.
8. Tabela de microoperações
Cada instrução se desdobra em microoperações elementares, executadas em ciclos consecutivos. Abaixo, três exemplos representativos.
LDA $0040 — carga absoluta
| T | RTL | Sinais | Comentário |
|---|---|---|---|
| T1 | MAR ← PC | ALE | endereço da instrução no barramento |
| T2 | MDR ← M[MAR] | RD | opcode chega da memória |
| T3 | IR ← MDR · PC ← PC+1 | — | decodificação |
| T4 | MAR ← PC · MDR ← M[MAR] | ALE, RD | byte baixo do endereço |
| T5 | MAR ← PC · MDR ← M[MAR] | ALE, RD | byte alto do endereço |
| T6 | MAR ← alto:baixo | ALE | endereço efetivo montado |
| T7 | MDR ← M[MAR] | RD | leitura do operando (consulta a cache) |
| T8 | A ← MDR · flags ← A | — | escrita no acumulador |
ADD B — soma entre registradores
| T | RTL | Sinais | Comentário |
|---|---|---|---|
| T1–T3 | busca e decodificação | ALE, RD | idêntico a qualquer instrução |
| T4 | ALU ← A, B | — | operandos nas entradas da ALU |
| T5 | A ← A + B | — | resultado no acumulador |
| T6 | flags ← Z,C,N,V | — | condição do resultado |
Atendimento de IRQ
| T | RTL | Sinais | Comentário |
|---|---|---|---|
| T1 | IE = 1 ∧ IRQ = 1 | INTA | pedido reconhecido ao fim da instrução |
| T2 | M[SP] ← PCalto · SP ← SP−1 | WR | empilha retorno |
| T3 | M[SP] ← PCbaixo · SP ← SP−1 | WR | empilha retorno |
| T4 | M[SP] ← FLAGS · SP ← SP−1 | WR | empilha estado |
| T5 | IE ← 0 | — | evita reentrância |
| T6 | PC ← M[$FFFE:$FFFF] | RD | desvio para a rotina de tratamento |
9. A ALU e as flags
A ALU é combinacional: dadas as entradas e a operação, a saída aparece após o atraso de propagação, sem memória interna. O que persiste são as flags, gravadas no registrador de estado ao final da operação.
| Flag | Nome | Vale 1 quando… | Usada por |
|---|---|---|---|
Z | Zero | o resultado é $00 | JZ, JNZ |
C | Carry | houve vai-um além do bit 7 (ou empréstimo na subtração) | JC, JNC |
N | Negativo | o bit 7 do resultado é 1 | JN |
V | Overflow | o resultado com sinal saiu da faixa −128…+127 | testes com sinal |
10. Mapa de memória
Os 64 KB endereçáveis não são todos RAM: partes do espaço são reservadas por convenção do firmware, e a faixa superior é ROM — escritas ali são simplesmente ignoradas pelo hardware.
Uma escrita em $F000–$FFFF não gera erro: ela é descartada, exatamente como aconteceria com uma memória somente de leitura real. Se o seu programa parece “não gravar”, verifique se o endereço caiu na ROM.
11. Barramentos e temporização
Barramento é um conjunto de condutores compartilhados. Compartilhar é econômico — menos trilhas na placa — mas exige disciplina: apenas um dispositivo pode dirigir o barramento por vez, e os demais ficam em alta impedância (mostrada na bancada como um padrão hachurado).
| Barramento | Largura | Sentido | Papel |
|---|---|---|---|
| Endereços | 16 linhas | unidirecional (CPU → memória/E-S) | seleciona qual célula ou porta responde |
| Dados | 8 linhas | bidirecional | transporta o byte lido ou escrito |
| Controle | sinais discretos | misto | define quando e o quê acontece |
Sinais de controle
| Sinal | Significado |
|---|---|
CLK | Relógio. Define o compasso; toda transferência ocorre em relação a uma de suas bordas. |
ALE | Address Latch Enable. Marca o instante em que o endereço no MAR é considerado válido. |
RD | Leitura. O dispositivo endereçado deve colocar seu dado no barramento. |
WR | Escrita. O dispositivo endereçado deve capturar o dado presente no barramento. |
IO/M | Seleciona o espaço de E/S em vez do espaço de memória. |
INTA | Interrupt Acknowledge. A CPU reconheceu um pedido de interrupção. |
HALT | A CPU encontrou HLT e parou de buscar instruções. |
Custo em ciclos
| Acesso | Ciclos | Por quê |
|---|---|---|
| Acerto na cache L1 | 1 | o dado já está dentro do die |
| Falta na cache (RAM) | 4 | ciclo de barramento completo + preenchimento do bloco |
| Leitura de ROM | 2 | memória serial mais lenta, não passa pela cache |
| Escrita em RAM | 2 | política write-through: sempre chega à memória |
12. Cache L1
A cache existe porque programas reais têm localidade: eles tendem a acessar de novo o que acabaram de acessar (localidade temporal) e a acessar endereços vizinhos (localidade espacial). A cache aposta nisso.
A da IPC-8 é de mapeamento direto: cada endereço de memória só pode ocupar uma linha específica. Isso torna a busca trivial — basta comparar a tag — ao custo de conflitos quando dois endereços muito usados disputam a mesma linha.
| Parâmetro | Valor |
|---|---|
| Capacidade total | 32 bytes |
| Linhas | 8 |
| Bloco por linha | 4 bytes |
| Organização | mapeamento direto |
| Política de escrita | write-through, sem alocação na escrita |
| Abrangência | somente RAM (a ROM é acessada diretamente) |
Como o endereço é dividido
Com blocos de 4 bytes, os 2 bits menos significativos escolhem o byte dentro do bloco (deslocamento). Os 3 bits seguintes escolhem uma das 8 linhas (índice). O que sobra é a tag, guardada junto com a linha para provar de qual endereço aquele bloco veio.
No LAB 04, observe que ler $0040 traz também $0041, $0042 e $0043 para a cache. O acesso seguinte a qualquer um deles custa 1 ciclo em vez de 4 — sem que o programa tenha pedido nada disso.
13. Pilha e sub-rotinas
A pilha ocupa a página $0100–$01FF e cresce para baixo: SP começa em $01FF e é decrementado a cada byte empilhado. Essa convenção — herdada de máquinas reais como o 6502 — deixa a pilha e os dados do programa crescendo em direções opostas, aproveitando melhor a memória livre entre eles.
O que CALL e RET fazem
| Instrução | Operação |
|---|---|
CALL end | M[SP] ← PCalto; SP−1; M[SP] ← PCbaixo; SP−1; PC ← end |
RET | SP+1; PCbaixo ← M[SP]; SP+1; PCalto ← M[SP] |
PUSH | M[SP] ← A; SP ← SP−1 |
POP | SP ← SP+1; A ← M[SP] |
Se uma sub-rotina empilha com PUSH e esquece o POP correspondente, o RET vai desempilhar o dado em vez do endereço de retorno e o PC saltará para um lugar arbitrário. Acompanhe o SP na aba Execução e a região da pilha na aba Memória para diagnosticar isso.
14. Interrupções
Sem interrupções, a CPU só descobre que algo aconteceu se ficar perguntando — é o polling do LAB 01, que queima ciclos sem produzir nada. A interrupção inverte a iniciativa: o dispositivo avisa a CPU.
- O dispositivo ativa a linha
IRQ. - A CPU termina a instrução em andamento — nunca interrompe pela metade.
- Se
IE = 1, responde comINTAe empilha PC e FLAGS. - Zera
IE, para não ser interrompida dentro do tratamento. - Carrega o PC com o endereço lido do vetor
$FFFE–$FFFF. - Executa a ISR e encerra com
IRET, que restaura FLAGS, PC eIE.
| Vetor | Endereço | Quando é usado |
|---|---|---|
| NMI | $FFFA | reservado (não mascarável) |
| RESET | $FFFC | ao ligar ou reiniciar a máquina |
| IRQ | $FFFE | pedido mascarável de dispositivo |
Uma ISR bem escrita preserva tudo o que altera. Como a IPC-8 empilha automaticamente apenas PC e FLAGS, se a sua rotina usar A, ela deve começar com PUSH e terminar com POP — caso contrário o programa interrompido continuará com um acumulador que não é dele.
15. Entrada e saída
A IPC-8 usa E/S mapeada em portas: um espaço de endereçamento separado de 256 posições, acessado exclusivamente por IN e OUT, com o sinal IO/M distinguindo os dois mundos. A alternativa — E/S mapeada em memória, usada por ARM e RISC-V — dispensa instruções especiais, mas consome parte do espaço de endereçamento.
Protocolo do teclado
O controlador mantém um bit de status e um registrador de dado. O programa lê a porta $00, isola o bit 0 e só consome a porta $01 quando há tecla disponível — a leitura do dado limpa o bit de status. Esse é o padrão clássico de handshake por status.
Protocolo do NVMe
Escrever $01 na porta $20 solicita a leitura do setor 0. Cada leitura da porta $21 devolve o byte seguinte do buffer; a porta $22 informa se ainda há dados. É assim que o firmware do LAB 03 traz o bootloader para a memória.
16. Inicialização e códigos POST
Quando a máquina é ligada, a memória RAM contém lixo e a CPU não sabe o que fazer. O único ponto fixo é o vetor de reset: a CPU lê os dois bytes em $FFFC e desvia para lá. Como essa região é ROM, sempre há código válido esperando.
- Vetor de reset —
PC ← M[$FFFC:$FFFD], apontando para o firmware. - POST — o firmware publica códigos de progresso na porta
$80, que aparecem no display de diagnóstico da placa. - Teste de memória — grava e relê um padrão conhecido para confirmar que a RAM responde.
- Enumeração de dispositivos — identifica o que está conectado.
- Leitura do setor 0 — copia o setor de boot do NVMe para
$7C00, byte a byte, pelas portas do controlador. - Entrega de controle —
JMP $7C00. A partir daqui, quem manda é o código que veio do disco.
O endereço é uma homenagem: é exatamente onde o BIOS dos PCs compatíveis carrega o setor de boot desde 1981. A escolha original deixava espaço abaixo para a pilha e as estruturas do BIOS, e permaneceu por compatibilidade décadas afora.
17. Von Neumann × Harvard
Um único espaço de endereçamento e um único barramento para instruções e dados. Simples, flexível e barato — permite carregar programas como se fossem dados, o que é a base de qualquer sistema operacional. Em troca, sofre com o gargalo de acesso único.
Memórias e barramentos separados para código e dados, permitindo buscar instrução e operando simultaneamente. Comum em microcontroladores e DSPs, onde o código é fixo na fábrica.
Processadores modernos são, na prática, híbridos: usam um espaço de endereçamento único (Von Neumann) do ponto de vista do programador, mas caches L1 separadas para instruções e dados (Harvard) do ponto de vista do hardware. A IPC-8 é deliberadamente Von Neumann pura, com cache unificada, porque isso torna o gargalo — e a razão de existir da cache — visíveis.
18. Limitações conscientes do modelo
Um simulador didático honesto precisa declarar o que não modela. Nada nesta lista é um defeito: são simplificações escolhidas para manter o conjunto compreensível.
| Não modelado | Como é na realidade |
|---|---|
| Pipeline | CPUs reais sobrepõem as fases de várias instruções, iniciando uma antes de a anterior terminar — com todos os riscos de dados e de controle que isso acarreta. |
| Execução fora de ordem e superescalar | Instruções são reordenadas e despachadas em paralelo para múltiplas unidades funcionais. |
| Previsão de desvio | Preditores estimam o resultado de um desvio antes de ele ser resolvido; um erro custa dezenas de ciclos. |
| Hierarquia completa de cache | L1 dividida em instruções e dados, L2 por núcleo, L3 compartilhada, além de associatividade por conjuntos e políticas de substituição. |
| MMU e memória virtual | Tradução de endereços, tabelas de páginas, TLB e proteção entre processos. |
| Múltiplos núcleos e coerência | Vários núcleos compartilhando memória exigem protocolos de coerência de cache. |
| DMA | Controladores que transferem dados sem passar pela CPU — no LAB 03, o firmware copia o setor byte a byte justamente para tornar o processo visível. |
| Barramentos seriais reais | PCI Express, DDR e NVMe usam sinalização diferencial, filas de comandos e protocolos por pacote, não os ciclos paralelos simples aqui representados. |
| Temporização física | Latências de DRAM (CAS, RAS), atraso de propagação e integridade de sinal. |
19. Glossário
- Acumulador
- Registrador que serve simultaneamente de operando e destino na maioria das operações aritméticas.
- ALU
- Arithmetic Logic Unit: circuito combinacional que executa as operações aritméticas e lógicas.
- Alta impedância
- Estado em que um dispositivo desconecta eletricamente suas saídas do barramento, permitindo que outro o utilize.
- Barramento
- Conjunto de condutores compartilhados por vários dispositivos para troca de sinais.
- CPI
- Cycles Per Instruction: média de ciclos de clock consumidos por instrução. Quanto menor, mais eficiente a execução.
- Endianness
- Ordem em que os bytes de um valor multibyte são armazenados. A IPC-8 é little-endian: byte menos significativo primeiro.
- Firmware
- Programa gravado em memória não volátil, executado antes de qualquer sistema operacional.
- Flag
- Bit de estado que descreve uma característica do último resultado da ALU.
- ISR
- Interrupt Service Routine: rotina executada em resposta a uma interrupção.
- Localidade
- Tendência de um programa a acessar repetidamente os mesmos endereços (temporal) ou endereços próximos (espacial).
- Opcode
- Byte que identifica qual operação a CPU deve executar.
- Polling
- Estratégia em que o programa consulta repetidamente o status de um dispositivo em vez de ser avisado por ele.
- POST
- Power-On Self-Test: sequência de verificações executada pelo firmware ao ligar a máquina.
- RTL
- Register Transfer Language: notação que descreve operações como transferências entre registradores.
- Tag
- Parte alta do endereço guardada junto a uma linha de cache para identificar de onde veio o bloco.
- Vetor de interrupção
- Posição fixa de memória que contém o endereço da rotina a executar em determinado evento.
- Write-through
- Política em que toda escrita atualiza a cache e a memória principal simultaneamente.
20. Referências e leituras
Os conceitos representados aqui seguem a literatura clássica de organização e arquitetura de computadores. Para aprofundamento:
- Patterson & Hennessy — Organização e Projeto de Computadores: a Interface Hardware/Software. Referência para ciclo de instrução, hierarquia de memória e avaliação de desempenho.
- Andrew S. Tanenbaum — Organização Estruturada de Computadores. Excelente para a noção de níveis de abstração e microarquitetura.
- William Stallings — Arquitetura e Organização de Computadores. Cobertura detalhada de barramentos, E/S e interrupções.
- Charles Petzold — Code: The Hidden Language of Computer Hardware and Software. Constrói um computador do relé ao processador, em linguagem acessível.
- Nisan & Schocken — The Elements of Computing Systems (projeto Nand2Tetris). Percurso completo de portas lógicas a sistema operacional.
A IPC-8 não reproduz nenhum processador comercial específico. Algumas convenções — pilha descendente na página 1, endereço de boot $7C00, vetores no topo da memória — foram tomadas emprestadas de máquinas históricas por seu valor pedagógico e de reconhecimento.