Tentarei dar uma idéia de como os circuitos digitais são projetados para resolver problemas de processamento digital usando os problemas que você apresenta: como as CPUs implementam adições e multiplicações.
Primeiro, vamos tirar a questão direta do caminho: como uma linguagem de programação avalia eficientemente multiplicações e acréscimos. A resposta é simples, eles os compilam para multiplicar e adicionar instruções. Por exemplo, o seguinte código:
a = 1 + 1;
b = a * 20;
é simplesmente compilado para algo como:
ADD 1 1 a
MUL a 20 b
(observe que o assembly acima é para uma CPU imaginária que não existe, por uma questão de simplicidade).
Nesse ponto, você percebe que a resposta acima simplesmente muda o problema e o resolve pela mágica do hardware. A pergunta seguinte é obviamente como essa mágica de hardware funciona?
Vamos analisar primeiro o problema mais simples: adição.
Primeiro, fazemos um problema familiar, adicionando regularmente números de base 10:
17
+28
O primeiro passo seria adicionar 7 e 8. Mas isso resulta em 15, que é mais do que um único dígito. Então, carregamos o 1:
(1)
17
+28
= 5
Agora adicionamos 1, 1 e 2 juntos:
17
+28
=45
Então, a partir disso, obtemos as seguintes regras:
quando o resultado da adição é superior a um dígito, mantemos o dígito menos significativo e avançamos com o dígito mais significativo
se temos um dígito transportado para a nossa coluna, o adicionamos juntamente com os números que estamos adicionando
Agora é hora de interpretar as regras acima na base 2 - álgebra booleana.
Assim, na álgebra booleana, adicionando 0 e 1 juntos = 1. Adicionando 0 e 0 = 0. E adicionando 1 e 1 = 10, que é mais de um dígito, para que continuemos com o 1.
A partir disso, podemos construir uma tabela de verdade:
a b | sum carry
-------------------
0 0 | 0 0
0 1 | 1 0
1 0 | 1 0
1 1 | 0 1
A partir disso, podemos construir dois circuitos / equações booleanas - uma para a saída da soma e outra para a saída do carry. A maneira mais ingênua é simplesmente listar todas as entradas. Qualquer tabela verdade, não importa quão grande e complexa possa ser reapresentada desta forma:
(AND inputs in first row) OR (AND of inputs in second row) OR ...
Esta é basicamente a soma da forma dos produtos. Nós olhamos apenas as saídas que resultam em 1 e ignoramos os 0s:
sum = (NOT a AND b) OR (a AND NOT b)
Vamos substituir AND e NOT por símbolos da linguagem de programação para facilitar a leitura:
sum = (!a & b) | (a & !b)
Basicamente, convertemos a tabela da seguinte maneira:
a b | sum equation
-------------------
0 0 | 0
0 1 | 1 (!a & b)
1 0 | 1 (a & !b)
1 1 | 0
Isso pode ser implementado diretamente como um circuito:
_____
a ------------| |
\ | AND |-. ____
\ ,-NOT--|_____| \ | |
\/ `--| OR |----- sum
/\ _____ ,--|____|
/ `-NOT--| | /
/ | AND |-`
b ------------|_____|
Os leitores observadores nesse momento perceberiam que a lógica acima pode realmente ser implementada como uma única porta - uma porta XOR que possui convenientemente o comportamento exigido por nossa tabela de verdade:
_____
a ------------| |
| XOR |---- sum
b ------------|_____|
Mas se o seu hardware não fornecer um portão XOR, as etapas acima são como definir e implementar em termos de portas AND, OR e NOT.
Como você converteria portas lógicas em hardware real depende do hardware que você possui. Eles podem ser implementados usando vários mecanismos físicos, desde que o mecanismo forneça algum tipo de comportamento de comutação. Os portões lógicos foram implementados com tudo, desde jatos de água ou sopros de ar (fluídicos) a transisitores (eletrônicos) a bolas de gude caindo. É um grande tópico por si só, então vou descrevê-lo e dizer que é possível implementar portas lógicas como dispositivos físicos.
Agora fazemos o mesmo para o sinal de transporte. Como existe apenas uma condição em que o sinal de transporte é verdadeiro, a equação é simplesmente:
carry = a & b
Portanto, carregar é simples:
_____
a ------------| |
| AND |---- carry
b ------------|_____|
Combinando-os, obtemos o que é conhecido como meio adicionador:
_____
a ------;-----| |
| | XOR |---- sum
b --;---|-----|_____|
| | _____
| '-----| |
| | AND |---- carry
'---------|_____|
As equações para o circuito acima, a propósito, são assim:
sum = a ^ b
carry = a & b
O meio adicionador está faltando alguma coisa. Implementamos a primeira regra - se o resultado for mais de um dígito do que transportar, mas não implementamos a segunda regra - se houver um transportador, adicione-o juntamente com os números.
Portanto, para implementar um somador completo, um circuito de adição que pode adicionar números com mais de um dígito, precisamos definir uma tabela verdade:
a b c | sum carry
---------------------
0 0 0 | 0 0
0 0 1 | 1 0
0 1 0 | 1 0
0 1 1 | 0 1
1 0 0 | 1 0
1 0 1 | 0 1
1 1 0 | 0 1
1 1 1 | 1 1
A equação da soma é agora:
sum = (!a & !b & c) | (!a & b & !c) | (a & !b & !c) | (a & b & c)
Podemos passar pelo mesmo processo de fatorar e simplificar a equação e interpretá-la como um circuito etc., como fizemos acima, mas acho que essa resposta está ficando muito longa.
Agora você deve ter uma idéia de como a lógica digital é projetada. Existem outros truques que não mencionei, como mapas de Karnaugh (usados para simplificar tabelas verdadeiras) e compiladores lógicos, como café expresso (para que você não precise fatorar equações booleanas à mão), mas o básico é basicamente o que eu tenho. descrito acima:
Decomponha o problema até poder trabalhar no nível de bit único (dígito).
Defina as saídas que você deseja usando uma tabela verdade.
Converta a tabela em uma equação booleana e simplifique a equação.
Interprete a equação como portas lógicas.
Converta seu circuito lógico em circuitos reais de hardware implementando portas lógicas.
É assim que os problemas fundamentais (ou melhor, de baixo nível) são realmente resolvidos - muitas e muitas tabelas verdadeiras. O verdadeiro trabalho criativo está no detalhamento de uma tarefa complexa, como decodificação de MP3 no nível de bits, para que você possa trabalhar com tabelas verdadeiras.
Desculpe, não tenho tempo para explicar como implementar a multiplicação. Você pode tentar entender um pouco sobre isso, descobrindo regras de quanto tempo a multiplicação funciona e depois interpretando-a em binário, e tente dividi-la em tabelas verdadeiras. Ou você pode ler a Wikipedia: http://en.wikipedia.org/wiki/Binary_multiplier