Notas de Aula - MAC 211 - Laboratório de Programação
Aula anterior (aula 1)
Aula 2 - 25/2/2010 - Preparando o caminho para a Linguagem de Montagem
Do código-fonte de alto nível ao código executável
O ciclo de busca-execução
- O ENIAC (1945) era programado através de cabos e chaves
liga/desliga
- A arquitetura proposta por John Von Neumann previa que o
programa também era armazenado na memória.
- Portanto, o computador funcionaria através de um ciclo de
busca e execução (fetch-execute
cycle):
- O processador obtém (lê) uma instrução da memória.
- O processador executa essa instrução.
- O processador volta para o passo 1 avançando para a
posição seguinte da memória (a não ser no caso de uma instrução de jmp
ou similar)
História do surgimento dos computadores pessoais
- Muitas vezes é dito que o primeiro computador pessoal da
história foi o Altair lançado em 1975, mas a coisa não é tão simples
assim.
- A página http://www.blinkenlights.com/pc.shtml
contém um ponto de vista interessante sobre esse assunto que mostra que
a coisa evoluiu muito mais lentamente.
Evolução da Arquitetura da família 80x86
- Arquitetura do 8080 usado no Altair:

- Arquitetura do 8086 usado nos primeiros IBM PC:

- Arquitetura do 80386, usados em PCs da década de 1990:
Por que programar em linguagem de montagem? (Fonte: Linux Assembly HOWTO)
- you can access machine-dependent registers and I/O
you
can control the exact code behavior in critical sections that might
otherwise involve deadlock between multiple software threads or
hardware devices
you can break the conventions of your usual
compiler, which might allow some optimizations (like temporarily
breaking rules about memory allocation, threading, calling conventions,
etc)
you can build interfaces between code fragments using
incompatible conventions (e.g. produced by different compilers, or
separated by a low-level interface)
you can get access to
unusual programming modes of your processor (e.g. 16 bit mode to
interface startup, firmware, or legacy code on Intel PCs)
you
can produce reasonably fast code for tight loops to cope with a bad
non-optimizing compiler (but then, there are free optimizing compilers
available!)
you can produce hand-optimized code perfectly tuned for your particular hardware setup, though not to someone else's
you
can write some code for your new language's optimizing compiler (that
is something what very few ones will ever do, and even they not often)
i.e. you can be in complete control of your code
Por que não programar em linguagem de montagem? (Fonte: Linux Assembly HOWTO)
- Assembly is a very low-level language (the lowest above hand-coding the binary instruction patterns). This means
it is long and tedious to write initially
it is quite bug-prone
your bugs can be very difficult to chase
your code can be fairly difficult to understand and modify, i.e. to maintain
the result is non-portable to other architectures, existing or upcoming
your
code will be optimized only for a certain implementation of a same
architecture: for instance, among Intel-compatible platforms each CPU
design and its variations (relative latency, through-output, and
capacity, of processing units, caches, RAM, bus, disks, presence of
FPU, MMX, 3DNOW, SIMD extensions, etc) implies potentially completely
different optimization techniques. CPU designs already include: Intel
386, 486, Pentium, PPro, PII, PIII, PIV; Cyrix 5x86, 6x86, M2; AMD K5,
K6 (K6-2, K6-III), K7 (Athlon, Duron). New designs keep popping up, so
don't expect either this listing and your code to be up-to-date.
you
spend more time on a few details and can't focus on small and large
algorithmic design, that are known to bring the largest part of the
speed up (e.g. you might spend some time building very fast list/array
manipulation primitives in assembly; only a hash table would have sped
up your program much more; or, in another context, a binary tree; or
some high-level structure distributed over a cluster of CPUs)
a
small change in algorithmic design might completely invalidate all your
existing assembly code. So that either you're ready (and able) to
rewrite it all, or you're tied to a particular algorithmic design
On
code that ain't too far from what's in standard benchmarks, commercial
optimizing compilers outperform hand-coded assembly (well, that's less
true on the x86 architecture than on RISC architectures, and perhaps
less true for widely available/free compilers; anyway, for typical C
code, GCC is fairly good);
And in any case, as moderator John Levine says on comp.compilers,
"compilers make it a lot easier to use complex data structures,
and compilers don't get bored halfway through
and generate reliably pretty good code."
They
will also correctly propagate code transformations throughout the whole
(huge) program when optimizing code between procedures and module
boundaries.
Sistemas de numeração
-
Decimal
-
123 = 1*10**2 + 2 * 10**1 + 3*10**0 = 100 + 20 + 3
-
123.456 = 1*10**2 + 2*10**1 + 3*10**0 + 4*10**-1 + 5*10**-2 + 6*10**-3
-
Binário
-
11001010 = 1*2**7 + 1*2**6 + 0*2**5 + 0*2**4 + 1*2**3 + 0*2**2 + 1*2**1
+ 0*2**0 = 128 + 64 + 8 + 2 = 202 (na base 10)
-
Exercício: converter 3 números de 8 bits para decimal
-
Exercício: converter um número de 16 bits para decimal (calcular os 2 bytes
em separado, depois multiplicar o mais significativo por 256 e somar o
menos significativo)
-
Exercício: converter 3 números decimais para binário (professor faz o primeiro)
-
converter 2725 -> 2725/2 = 1362 -> 681 -> 340 -> 170 -> 85 -> 42 -> 21
-> 10 -> 5 -> 2 -> 1
-
portanto 2725 (decimal) = 101010100101 (binário) = 1+4+32+128+512 +2048
-
outra forma de calcular para quem sabe as pontências de 2 de cor: qual
a potência de dois mais alta que cabe dentro do número a transformar? (p.ex., 131)
-
alunos fazem outros dois números usando uma técnica diferente em cada conversão
-
bits mais significativos
-
os 8 bits de um byte são numerados de 0 a 7 da direita para a esquerda
-
Hexadecimal
-
dígitos de 0 a F
-
exemplos: 1234 FEA DEAD BEEF 0AFB FEED DEAF
-
2B (hexa) = 2*16 + 11 (decimal) = 43 (decimal)
-
potencias: 1, 16, 256, 4096, 65536
-
137 (decimal): 137/16 = 8 sobram 9, entao 137 (decimal) = 89
-
conferir: 8*16 + 9 = 137, confere.
-
exercícios: converter dois números em cada direção
-
hexadecimais são usados pois são bem compactos, são fáceis de converter
para binário e com dois dígitos se representa exatamente um byte (nem mais
nem menos).
-
Calculadora calctool no Linux trabalha com bases 2, 8, 10 e 16.
Referências
Próxima aula (aula 3)
Página de MAC211
Página do Fabio
Página do DCC