Archive for Junho, 2009
Assembly, o básico
Assembly é um linguagem da programação, estritamente relacionada com a arquitetura do computador, nesse artigo falarei sobre arquitetura CISC da intel em 16 e 32 bits, a maioria dos programas escritor para a arquitetura intel devem funcionar em AMDs.
A grande vantagem dessa linguagem de programação esta no controle que ela oferece sobre a máquina, a velocidade e o tamanho final do programa. Porem mesmo com todas essas vantagem ela é de forma alguma recomendada para ser usada em projetos grandes por causa do número grande de variavés ( muito mais além do espaço de memoria alocado) que tem que se manter controle, o que torna o desnvolvimento estremamente lerdo e penoso, porem eu acho extremamente interessante compreender o funcionamento de algo baixo nivel o que traz uma base sobre o que realmente acontece nos programas.
O primeiro fato a ser mencionado é a diferença entre as notação disponibilizadas, vou chama-las de notação da intel e notação da AT&T, mas qual seria essa diferença? bem as mnemônicas são basicamente identicas – podendo ter alguma variação dependendo do compilador – mas a grande diferença esta em como é determiado a origem e o destino dos dados, a arquitetura da intel segue esse padrão “mnemonico destino origem” já a notação da AT&T faz o oposto “mnemonico origem destino”, como base desse artigo vou usar o nasm – network assembler – que usa a notação da intel.
O segundo fato a ser mencionado são o registradores da cpu, eles são pequenos espaços de memória usados para fazer as operações matemáticas, para um processador 32 bits da intel nós temos os registradores mais importantes: eax(extended accumulator), ebx(extended base), ecx(exetended counter), edx(extended data), ebp(extende base pointer), esp(extented source pointer) esi(source index, edi(destination index)) para 16 bits temos, ah al(accumulator high/low), bh bl, ch cl, dh dl, além dos registradores de 8 bits e nos processadores modernos 64 bits.
Tem que se ter em mente que os registradores compartilham o espaço, ou seja, a parte ‘baixa’ do eax, é na verdade ah e al, de tal foram que se você modificar ah ou al eax também vai ser alterado:
[----------------[---ah---|---al---]]eax
Isso é verdade para os registradores ‘a’, ‘b’, ‘c’ e ‘d’, lembre-se que os registradores de 8 bits também estão dentro dos de 16 bit e os de 32 bit estão dentro dos de 64 bits.
Terceiro fato, a estrutura de um arquivo de código em assembly:
label: mnemonico [valor1(destino)] [valor2(orgiem)] ;comentário
nenhuma parte é obrigatoria, de forma que você pode ter apenas uma label na linha, ou um mnemonico com seus argumentos, ou um comentário, ou todos juntos. Importante notar que nem sempre vão ser dois argumentos para o mnemonico, podendo ter 0, 1, 2 ou mais em instruções mais especificas.
Quarto fato, mnemonicos são nomes dados a instruções do processador, e em muitos casos são fáceis de se compreender, como `MOV eax, ecx` que move o conteúdo de ecx para eax na notação da intel `ADD` `SUB` `MUL` são outros exemplos, outro fato sobre os mnemonicos é que eles não são case-senstive, então `MOV` e `mov` são equivalentes, a lista dos mnemonicos é extensa, para uma listagem completa
Add comment 12 Junho 2009
Considerações sobre fritar um ovo em uma sanduicheira
Em momento de fome e preguiça a gente recore a métodos não ortodoxos, eu resolvi fritar um ovo em uma sanduicheira para não sujar louça e aqui ficam algumas observações:
Ligue a sanduicheira antes de colocar o ovo
Não use oleo
Use o lado de “fora” – concavo não convexo – da colher para passar margarina
A gema precisa ou de mais sal ou de mais margarina
Add comment 12 Junho 2009
malloc, uma nova abordagem
Faz algum tempo desde meu primeiro post sobre a função malloc, ele não esta muito bem estruturado ficando rasoavelmente inconpreensivel então aqui uma nova abordagem no tópico:
A função malloc é uma função da bilbioteca padrão da linguagem C, a sua função é alocar dinamicamente espaços de memória para poder ser usado no programa, mas qual a utilidade disso?
Bem, vamos supor que nós tenhamos que escrever um programa que trabalha com matrizes, então nos criamos uma array bidimensional para armazenar os valores da matrizes para fazer os calculos, simples não é? mas pera ai, e se o tamanho da matriz variar? hora o usuario pode digitar uma matrz 2×2, hora 3×3, ate mesmo algo como 255×255, ai nós temos um problema, talvez se nós criarmos uma matriz grande de forma que o usuario não seja capaz de enche-la, digamos algo em torno de 1000×1000, dai nós usamos uma variavel para contar o número de linhas e outra para o número de colunas da matriz, perfeito! sera?
Bem, a abordagem acima funciona, mas sinceramente ela é horrivel, se for algum programa para auxiliar estudantes do ensino médio matrizes não passam de 4×4, são 8 elementos de um total de 1.000.000, um disperdicio tremendo de memória! Por outro lado se for uma aplicação cientifica nada garante que o total de dados não seja maior do 1.000.000, e o pior se nós temos que trabalhar com um número variavel de matrizes, como a gente faria? criar 1000 matrizes para ter certeza que não ficamos sem? uma abordagem horrivel, mas o que malloc tem a ver com tudo isso?
malloc permite que a gente crie durante a execução do programa uma matriz do tamanho que quisermos, mas como!? Simples, se pergunta para o usuario o tamanho da matriz que ele quer, então cria-se a matriz do tamanho que o usuario disse:
n = tamanho_matriz(); // pseudo-funcao que pergunta o tamanho da array para o usuario
int **ptr, i;
ptr = malloc(sizeof(int*)*n);
for( i=0; i<n; i++){
ptr[i] = malloc(sizeof(int) * n);
}
Mágico isso não é? mas como funciona? bem malloc leva como argumento o total de bytes para alocar, por isso do sizeof() que retorna o tamanho de um tipo de dado em bytes, e retorna um ponteiro para o inicio dessa seção de memória, o ponteiro é do tipo void*, e a razão disso é bem simples, malloc aloca espaço para qualquer tipo de dados, inclusive struct (desde que você use `sizeof(struct struct_name)`) então o tipo de retorna não poderia ser um ponteiro para algum tipo senão sem tipo( isso mesmo void quer dizer sem tipo) por isso você deve ter se deparado com coisas assim:
ptr = (int*)malloc(sizeof(int)*n);
onde o `(int*)` é um casting, a lógica esta certa afinal se o seu ptr for do tipo int* nada errado com esse casting de um void* para int* – inclusive no meu post anterior eu uso isso – mas de fato ele é redundante porque a conversão é implicita, fica a seu cretério usa-lo ou não.
Agora um ponto importante sobre o malloc, porque o compilador não tem como saber qual é a quantia de memória a ser alocada – ela só é definida durante execução pelo usuario – ele não pode libera-la, por isso não se esqueça de usar a função free(), você só precisa passar o ponteiro que o malloc lhe deu para a função free() que ela se encarrega do resto:
free(ptr);
só mais uma coisa, não se esqueça do `#include <stdlib.h>` para poder usar a função ;D
Add comment 12 Junho 2009