Assembly NumaBoa - Capítulo 3

LABORATÓRIO

Um bom programador de Assembly precisa conhecer um pouco mais do que o conjunto de instruções da linguagem - entender a arquitetura do computador também é necessário. A não ser que se conheça a operação interna das instruções e como estas operações internas interagem, não é possível escrever programas rápidos e eficientes. Da mesma forma, se não se souber como a máquina codifica instruções, não será possível avaliar se uma determinada sequência de instruções é mais curta ou mais longa que uma outra.

Neste laboratório usaremos um debugger rudimentar para criar, testar e executar pequenos programas em linguagem de máquina. Também exploraremos os métodos de otimização de contagem de ciclos e contagem de bytes. Estes métodos são importantes quando se trabalha com programas complexos em linguagem Assembly.

Debuggers e o SIMx86

Um debugger é um programa que permite mostrar e modificar diferentes posições de memória, executar instruções, mostrar registradores da máquina e realizar outras operações comuns necessárias para se testar e corrigir programas em Assembly. O uso correto de um debugger reduz dramaticamente o tempo de desenvolvimento. Neste laboratório usaremos um debugger muito simples para os processadores x86. É uma introdução para o debugger CodeView que será descrito no capítulo 4.

Antes de descrever o debugger é preciso esclarecer o que é o simulador x86. Como os processadores x86 são hipotéticos, não existe qualquer tipo de hardware no qual os programas x86 possam ser executados. O simulador x86 (SIMx86) é um software que simula um processador x86. Este programa busca os opcodes x86 na memória (a do 80x86) e depois executa uma sequência de instruções que emulam o comportamento destas instruções.

O programa SIMx86 usa variáveis inteiras para guardar os valores dos registradores e os endereços de memória. Não é possível encontrar diferenças entre o programa SIMx86 e o processador 886 (se existisse um). Aliás, a técnica de simulação não se limita apenas a processadores hipotéticos. SoftPC e outros programas usam exatamente esta técnica para emular processadores 80x86 no Macintosh, NeXT e outros sistemas.

O programa SIMx86 é da autoria de Randall Hyde. Originalmente escrito em Object Pascal (Delphi), foi traduzido e recompilado por mim. Você pode fazer o download do executável, dos códigos fonte e de exemplos de programas aqui na Aldeia.

Mostrando e dando entrada de valores na memória

O programa SIMx86 simula o espaço de endereços de 64 K do processador 886 usando um array de 64K no espaço de memória do 80x86. Tudo o que normalmente apareceria na localização zero da memória do processador 886 é colocado no índice zero deste array. Como em qualquer outro debugger decente, o programa SIMx86 mostra e permite a entrada de valores na memória. Quando o programa é iniciado, todo o espaço da memória é preenchido com zeros. Para verificar o conteúdo da memória, clique na aba "memória" e veja inicialmente as posições de 0000h a 0038h. Para verificar outras posições basta dar entrada de um novo endereço inicial (na notação hexadecimal) no campo situado no canto superior esquerdo que o dump é automaticamente realizado, mostrando os novos valores encontrados.

Este dump é muito útil para mostrar os valores de variáveis, arrays ou até de código de máquina (a representação binária de instruções). Entretanto é preciso ter em mente um aspecto muito importante - o dump mostra os valores na sequência endereço mais baixo/endereço mais alto. Até aí, tudo normal. Acontece que, por este motivo, os valores word, que são armazenados em dois bytes consecutivos com o byte menos significativo no endereço mais baixo e o byte mais significativo no endereço mais alto, aparecem com os "bytes trocados". Se o word no endereço 1000h contém 1234h, o que será visto no dump será:

	1000: 34 12 xx xx xx xx xx xx

Você precisa se lembrar de reposicionar mentalmente os bytes para obter os valores corretos. Um erro muito comum feito pelos iniciantes é esquecer de trocar as posições dos bytes mostrados pelo debugger.

Para alterar um valor na memória basta ativar a posição desejada e digitar o novo valor (tudo, sempre, em hexadecimal). Não se esqueça da "inversão" de bytes também na entrada de novos valores!

Q03.01

	1. Escolher a aba "Memória".
	2. Digitar 18A0 no campo do endereço inicial.

Q03.02

	1. Escolher a aba "Memória".
	2. Ativar a posição 8000.
	3. Digitar 37.
	4. Ativar a posição 8001.
	5. Digitar 98.

Revisando o conjunto de instruções e os opcodes do x86

Para poder criar pequenos programas para o x86 é preciso conhecer seu conjunto de instruções. Este assunto foi amplamente discutido no tópico O conjunto de instruções do x86. Apenas para refrescar a memória, veja abaixo as formas das instruções possíveis:

	mov     reg, reg/mem/const
	mov     mem, reg

	add     reg, reg/mem/const
	sub     reg, reg/mem/const
	cmp     reg, reg/mem/const
	and     reg, reg/mem/const
	or      reg, reg/mem/const
	not     reg/mem

	ja      dest            -- Desvia se maior
	jae     dest            -- Desvia se maior ou igual
	jb      dest            -- Desvia se menor
	jbe     dest            -- Desvia se menor ou igual
	je      dest            -- Desvia se igual
	jne     dest            -- Desvia se não igual
	jmp     dest            -- Desvio incondicional
	iret                    -- Retorna de uma interrupção

	get		-- Espera entrada do usuário em ax
	put		-- Mostra o valor de ax
	halt		-- Termina o programa
	brk		-- Suspende a execução

Os opcodes são os códigos que o processador usa para identificar as instruções. Para reavivar a memória, os opcodes das instruções básicas podem ser vistos abaixo:

Codificação de opcodes

Fig.1 - Codificação de opcodes no x86

Observe que, para montar o opcode de uma instrução, basta compor o primeiro byte de acordo com o padrão adotado. Assim, por exemplo, o opcode da instrução and ax, bx é montado da seguinte forma:

and = 010 (3 bits)
ax = 00 (2 bits)
bx = 001 (3 bits)
and ax, bx = 0100 0001 (8 bits)
and ax, bx = 41h

Coloque o valor hexadecimal 41 na posição de memória 0000 e clique na aba "Emulador". Verifique que a posição 0000 mostra exatamente a instrução referente ao opcode 41, ou seja, and ax, bx.

Q03.03

	 mov ax, ... = 1100 0111
	             =  C    7
	        7BA2 =  A2  7B
	mov ax, 7BA2 = C7 A2 7B

Q03.04

	  mov bx, [....] = 1100 1110
	                 = C E
	            8000 = 00 80
	  mov bx, [8000] = CE 00 80

Q03.05

	mov bx, [8000] = CE 00 80 = 3 bytes

Q03.06

	Move o conteúdo do endereço de memória 8000
	para o registrador BX.

Q03.07

	mov ax, [1000]
	mov [8000], ax

Q03.08

	1011 0100 = B4

Q03.09

	add cx, [bx] = B4 = 1 byte

Q03.10

	Adiciona o valor do conteúdo do endereço de memória indicado
	pelo registrador BX ao valor do registrador CX e armazena o
	resultado no registrador CX.

Q03.11

	sub dx, [xxxx+bx] = 3 bytes

Q03.12

	        1001 1101 = 9D
	             2002 = 02 20
	sub dx, [2002+bx] = 9D 02 20

Q03.13

	Busca o valor das posições de memória 2002+BX e
	2003+BX e o subtrai do registrador DX, deixando o resuldo no
	registrador DX, ou seja, DX = DX - (2002+BX 2003+BX).

Os opcodes das instruções de desvios de execução, também chamados de saltos, podem ser vistos na Fig.2.

Saltos

Fig.2 - Opcodes de saltos

Se os bits de número 3 a 7 (numerados de 0 a 7, da direita para a esquerda) forem 10000 (que são vistos na Fig.2 como 00001), o 886 sabe que o opcode se refere a uma instrução de salto. Estas instruções sempre exigem, além do byte do opcode, mais dois bytes (ou 16 bits) que contenham o endereço alvo do salto. Portanto, estas instruções sempre têm o comprimento de 3 bytes.

Não custa lembrar que estas instruções, com exceção da instução jmp, só têm sentido se usadas após uma instrução de comparação (cmp), a qual prepara as flags que serão utilizadas pelo processador para decidir se o salto condicional deve ou não ser efetuado. Portanto, quando se pretende um salto condicional, além dos 3 bytes do próprio salto precisamos contar com 1 byte adicional da operação de comparação (total de 4 bytes em duas instruções).

Q03.14

	cmp dx, bx = 0111 1001 = 79h

Q03.15

	ja 8098 = 0000 1100 = 0C 98 80

Q03.16

	O valor do registrador DX será comparado com o valor do
	registrador BX (cmp dx, bx). A seguir, se o valor de DX
	for maior que o do registrador BX, a execução é desviada
	para o endereço de memória 8098. Caso contrário, a execução
	continua com a próxima instrução.

Criando programas no SIMx86

Agora que sabemos como os opcodes são montados, como inserí-los em posições de memória e como visualizá-los no simulador, chegou a hora de criar alguns programas. Logicamente, o SIMx86 também permite executá-los (passo a passo ou de forma corrida) e testá-los. Além disso podemos alterar valores nos registradores e analisar comparações. Mas antes, uma palavrinha sobre o editor do SIMx86.

O editor do simulador SIMx86

O simulador SIMx86 possui um editor para facilitar a nossa vida. Não será preciso ficar calculando cada um dos opcodes das instruções que quisermos utilizar - o editor faz este trabalho para nós - basta indicar a sequência de instruções que nos interessa. E mais! Caso haja algum erro de sintaxe ou de "ortografia", ele gentilmente nos informa. Estes programas podem ser salvos (como podem ser abertos) para serem usados em outras ocasiões. NÃO SE ESQUEÇA: estes programas só rodam no simulador pois baseiam-se no funcionamento de um processador hipotético!

Antes de começar com um programa "de verdade" que será rodado no processador 886 "de mentira", é melhor fazer uma faxina: clique na aba "Memória" e no botão "Limpar Memória" para zerar todas as posições. A seguir, clique na aba "Editor" e ponha no "Endereço Inicial" o valor 0000 (se é que já não está assim). A seguir, digite o seguinte programa exemplo que pede ao usuário que entre com cinco números e no final apresenta a soma dos mesmos. NÃO digite as observações, estas são citadas apenas para explicar o funcionamento do programa:

	mov	dx, 5 	; Repetir o loop 5 vezes
	mov	cx, 0	; Contador do loop
	mov	bx, 0	; Acumular o resultado aqui
a:	get		; Ler o valor do usuário
	add	bx, ax	; Soma o valor em bx
	add	cx, 1	; Aumenta o contador em 1
	cmp	cx, dx	; Compara o contador com 5
	jb	a	; Se CX for menor, desvia para o marcador a:
	mov	ax, bx	; Põe a soma em AX
	put		; Apresenta o resultado de AX
	halt		; Tudo em riba, terminamos

Observe que usamos um marcador para o salto condicional (o marcador e o salto estão destacados em negrito). Se a condição for preenchida, ou seja, se o valor do contador CX for menor do que o valor de DX=5, então a execução deve ser desviada para o endereço do marcador "a:". Os marcadores são sempre letras únicas seguidas por dois pontos. A vantagem do uso de marcadores é que não precisamos calcular o endereço do salto - o editor faz isto para nós.

Depois de digitar o código do programa, clique no botão "Transferir". Se o código estiver correto, não aparecem mensagens de erro. Caso alguma seja mostrada, corrija o erro e clique novamente no botão. Só por curiosidade, clique na aba "Memória", certifique-se de que o endereço inicial é 0000 e dê uma olhada nos opcodes. Observe que, com apenas 21 bytes, até que fizemos um programinha legal. Agora só falta testá-lo.

Clique na aba "Emulador". Você deve encontrar o seguinte:

Emulador SIMx86

Observe o programa já posicionado na memória com os opcodes e as respectivas instruções. Se o ponteiro de instruções não estiver em 0000 ou se algum registrador estiver com um valor diferente de 0000, clique apenas no botão "Reset" para "limpar" o emulador. A seguir, clique no botão "Rodar". O programa deve pedir 5 valores hexadecimais numa janela própria. Digite o valor desejado e depois no botão "Ok". Cada vez que você repetir esta operação, o painel "Entrada" indicará os valores escolhidos.

Depois do quinto número, o programa indica a soma dos valores no painel "Saída" e avisa que encontrou uma instrução halt na linha 0014. Os registradores mostram os valores finais. Apesar de ter funcionado bem, é muito mais interessante rodar este programa passo a passo. Clique no botão "Reset" e observe: tudo é zerado e o Ponteiro de Instrução aponta novamente para o início do programa.

Agora clique no botão "Passo". A primeira instrução é executada e o registrador DX mostra o valor 0005. Continue clicando no botão "Passo" e, logo após a execução da instrução de comparação (cmp cx,dx) observe que a checkbox identificada com "Menor" está checada - ela indica o resultado da comparação que acabou de ser feita. Continue no passo a passo e acompanhe a atualização dos valores dos registradores de acordo com o andamento do programa.

Parabéns! Você acaba de programar em Assembly para o processador 886! E não pense que programar para outros processadores seja uma coisa muito diferente!

Q03.17

	A posição que o programa ocupa na memória não
	influencia o seu funcionamento contanto que os endereços
	dos saltos estejam ajustados.

Os tempos de execução de um programa x86

De acordo com a tabela de tempos mostrada no tópico O processador 886 do capítulo 3, os tempos medidos em ciclos de clock são os seguintes:

	Instrução		mov	add, sub, cmp,	not	jmp	jxx
			         and, or
	-------------|----------|------------------|--------|--------|--------|
	reg,reg		5	7
	reg,xxxx		6-7	8-9
	reg,[bx]		7-8	9-10
	reg,[xxxx]	8-10	10-12
	reg,[xxxx+bx]	10-12	12-14
	[bx],reg		7-8
	[xxxx],reg	8-10
	[xxxx+bx],reg	10-12
	reg					6
	[bx]			9-11
	[xxxx]					10-13
	[xxxx+bx]					12-15
	[xxxx]						6-7	6-8

Vamos analisar quantos ciclos nosso programa consome para ser executado. Para isto, criaremos uma tabela com as instruções e seus respectivos consumos de ciclos de clock:

	mov	dx, 5 	= 6-7 ciclos
	mov	cx, 0	= 6-7 ciclos
	mov	bx, 0	= 6-7 ciclos
a:	get		= 1 ciclo
	add	bx, ax	= 7 ciclos
	add	cx, 1	= 8-9 ciclos
	cmp	cx, dx	= 7 ciclos
	jb	a	= 6-8 ciclos
	mov	ax, bx	= 5 ciclos
	put		= 1 ciclo
	halt		= 0 ciclos

É preciso lembrar que o loop passa pelo código 5 vezes portanto, o total de ciclos dentro do loop precisa ser multiplicado por 5: no mínimo (1 + 7 + 8 + 7 + 6) * 5 = 145 e no máximo (1 + 7 + 9 + 7 + 8) * 5 = 160. Os ciclos consumidos fora do loop são no mínimo 6 + 6 + 6 + 5 + 1 = 24 e no máximo 7 + 7 + 7 + 5 + 1 = 27. Portanto, para rodar nosso programa precisamos de no mínimo 145 + 24 = 169 ciclos e no máximo de 160 + 27 = 187 ciclos. Afinal de contas, quantos ciclos são realmente necessários para executar o programa?

Basta lembrar da história do número de bytes de uma instrução e dos endereços de memória pares e ímpares. Tomemos como exemplo a primeira instrução, mov cx, 0. O código operacional tem 1 byte e a constante é de dois bytes (porque nosso 886 é um processador de 16 bits). Se a constante cair num endereço par, o processador é capaz de buscá-la num único ciclo de clock; se a constante cair num endereço ímpar, o processador vai precisar de dois ciclos de clock para obtê-la. Como o endereço inicial do nosso programa é 0000, basta verificar as posições das constantes das primeiras três instruções:

	0000:	opcode mov
	0001:	05 00	<= endereço ímpar = 7 ciclos
	0003:	opcode mov
	0004:	00 00	<= endereço par = 6 ciclos
	0006:	opcode mov
	0007:	00 00	<= endereço ímpar = 7 ciclos

As outras duas instruções cujos ciclos podem variar são add cx,1 e mov ax,bx. Analisando suas posições verificamos que:

	000B:	opcode add
	000C:	01 00	<= endereço par = 8 ciclos
	...
	000F:	opcode ja
	0010:	09 00	<= endereço par sem cálculo = 6 ciclos

Agora temos o número exato de ciclos do nosso programa: 7 + 6 + 7 + ((1 + 7 + 8 + 7 + 6) * 5) + 5 + 1 + 0 = 20 + 145 + 6 = 171.

Q03.18

	0001:	opcode mov
	0002:	05 00		<= endereço par = 6 ciclos
	0004:	opcode mov
	0005:	00 00		<= endereço ímpar = 7 ciclos
	0007:	opcode mov
	0008:	00 00		<= endereço par = 6 ciclos
	000A:	get		1 * 5 = 5 ciclos
	000B:	add bx,ax	7 * 5 = 35 ciclos
	000C:	opcode add
	000D:	01 00		<= endereço ímpar = 9 * 5 = 45 ciclos
	000F:	cmp dx,cx	7 * 5 = 35 ciclos
	0010:	opcode jb
	0011:	00 0A		<= endereço ímpar sem cálculo =
				7 * 5 = 35 ciclos
	0013:	mov ax,bx	5 ciclos
	0014:	put		1 ciclo
	0015:	halt		0 ciclos

   TOTAL = 6 + 7 + 6 + 5 + 35 + 45 + 35 + 35 + 5 + 1 = 180 ciclos

	O deslocamento de endereço tornou o programa mais lento.
	Ao invés de 171 ciclos, irá precisar de 180.

Alguns exercícios de programação

Como vimos, o SIMx86 é uma "poderosa" ferramenta de programação. O conjunto de instruções do 886 é pequeno mas, sabendo usá-lo, podemos programar coisas muito interessantes e o emulador permite que os programas sejam executados. Use a imaginação e mãos à obra! A seguir, algumas idéias:

Q03.19

	mov	bx, 1000
a:	get
	mov	[bx], ax
	add	bx, 2
	cmp	ax, 0
	jne	a

	mov	cx, bx
	mov	bx,1000
	mov	ax, 0
b:	add	ax, [bx]
	add	bx, 2
	cmp	bx, cx
	jb	b

	put
	halt

Além das instruções GET e PUT, os processadores x86 permitem E/S mapeada para a memória. Sistemas com E/S mapeada para a memória usam posições de memória como interface para dispositivos externos. o SIMx86 permite até 8 dispositivos externos: quatro LEDs e quatro interruptores. As localizações de memória de 0FFF0h a 0FFF6h correspondem aos interruptores e as posições de memória de 0FFF8h a 0FFFEh correspondem aos quatro LEDs.

Lembre-se de que o x86 é de 16 bits. Portanto, 0FFF0h/0FFF1h corresponde ao primeiro interruptor, 0FFF2h/0FFF3 ao segundo e assim por diante. Se, por exemplo, a posição de memória 0FFF2h contiver 0 (zero), é porque o interruptor está desligado; se contiver 1, é porque está ligado. O mesmo ocorre com os LEDs. Se a posição de memória correspondente a um LED contiver 0 (zero), o LED está desligado, se contiver 1, o LED está ligado.

Q03.20

	mov ax, 1	; Valor para aceso
	mov bx, FFF8	; Endereço do primeiro LED
	mov [bx], ax	; Acende
	add bx, 2	; Vai para o endereço seguinte (FFFA)
	mov [bx], ax	; Acende
	halt

Q03.21

		mov ax, 0	; Valor para desligado
		mov bx, FFEE	; Endereço inicial - 2
		mov cx, FFFE	; Último endereço
	a:	add bx, 2	; Ajusta o endereço para o
				  próximo dispositivo
		mov [bx], ax	; Desliga
		cmp bx, cx	; É o último endereço?
		jb a		; Não, então repete o loop
		halt		; Sim, termina o programa

Agora, para fechar com chave de ouro, tente criar um programa que responde acendendo ou apagando os três primeiros LEDs de acordo com a posição dos três primeiros interruptores. Reserve o interruptor mais da direita para terminar o programa.

Q03.22

	mov ax, 0	; Vamos desligar tudo (veja acima)
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Todos os dispositivos zerados
	mov dx, ax	; Guarda zero em DX que é o
			  último interruptor desligado
b:	mov bx, FFF0	; Endereço do primeiro interruptor
	mov cx, FFF6	; Endereço do último interruptor
c:	mov ax, [bx]	; Pega status do interruptor
	mov [8+bx], ax	; Passa para o LED correspondente
	add bx, 2	; Avança endereço
	cmp bx, cx	; Passou do último interruptor?
	jbe c		; Não, vai para o próximo e repete o loop
	cmp ax, dx	; Sim, então verifica se o último
			  interruptor foi ligado
	je b		; Se continua zerado, rastreia novamente
	halt		; Se foi ligado, termina o programa

Q03.23

	mov ax, 0	; Desligar todos os dispositivos
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Tudo desligado
	get		; Pede o primeiro valor
	mov bx, ax	; Transfere para BX
	get		; Pede o segundo valor
	and ax, bx	; AND dos dois valores
	put		; Põe resultado na saída
	mov bx, FFF8	; Endereço do primeiro LED
	mov [bx], ax	; Aciona o LED com o resultado
	halt

Q03.24

	mov ax, 0	; Desligar todos os dispositivos
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Tudo desligado
	get		; Pede o primeiro valor
	mov bx, ax	; Transfere para BX
	get		; Pede o segundo valor
	or ax, bx	; OR dos dois valores
	put		; Põe resultado na saída
	mov bx, FFFA	; Endereço do segundo LED
	mov [bx], ax	; Aciona o LED com o resultado
	halt

Q03.25

	mov ax, 0	; Desligar todos os dispositivos
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Tudo desligado
	get		; Pede o primeiro valor
	mov bx, ax	; Transfere para BX
	get		; Pede o segundo valor
	and ax, bx	; AND dos dois valores
	not ax		; NOT do resultado
	put		; Põe resultado na saída
	mov bx, FFFC	; Endereço do terceiro LED
	mov [bx], ax	; Aciona o LED com o resultado
	halt

O conjunto de instruções do x86 é bastante restrito. Não possui, por exemplo, instruções de multiplicação e de divisão. Apesar disso, esta falha aparente não prejudica a nossa programação.

Q03.26

	mov ax, 0	; Desligar todos os dispositivos
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Tudo desligado
	get		; Pede o primeiro valor
	mov cx, ax	; Guarda em CX
	get		; Pede o segundo valor
	mov dx, ax	; Guarda em DX
	mov ax, 0	; Zera o acumulador AX
	mov bx, 0	; Zera BX para comparar com contador
	cmp cx, ax	; Compara o primeiro valor com zero
	je c		; Se for zero, põe resultado zero
b:	add ax, dx	; Acumula o segundo valor
	sub cx, 1	; Decrementa o contador
	cmp cx, bx	; Compara o contador com zero
	ja b		; Se for maior, acumula novamente
c:	put		; Mostra o resultado de AX
	and ax, 1	; Isola o último bit de AX
			; (0 = par e 1 = ímpar)
	cmp ax, bx	; Compara com zero
	je d		; Se zero (par), salta para d:
	mov bx, FFFA	; Põe endereço do segundo LED em BX
	mov [bx], ax	; Liga o segundo LED (AX = 1)
	halt		; Termina o programa
d:	mov bx, FFF8	; Põe endereço do primeiro LED em BX
	mov ax, 1	; Põe 1 em AX
	mov [bx], ax	; Liga o primeiro LED
	halt		; Termina o programa

Q03.26

	mov ax, 0	; Desligar todos os dispositivos
	mov bx, FFEE
	mov cx, FFFE
a:	add bx, 2
	mov [bx], ax
	cmp bx, cx
	jb a		; Tudo desligado
	get		; Pede o valor
	mov cx, 0	; Zera o contador
	cmp ax, cx	; Compara o valor com zero
	je c		; Se for zero, apresenta o resultado
	mov dx, 3	; Põe o divisor 3 em DX
b:	cmp ax, dx	; Compara o valor com 3
	jb c		; Se for menor, apresenta o resultado
	sub ax, dx	; Se for maior, faz a subtração
	add cx, 1	; e incrementa o contador
	jmp b		; Repete a operação
c:	mov dx, ax	; Passa o resto para DX
	mov ax, cx	; Põe contador em AX
	put		; Apresenta o resultado da divisão
	mov ax, dx	; Põe o resto em AX
	put		; Apresenta o resto
	mov ax, 1	; Põe 1 em AX para ligar o LED
	mov bx, 0	; Zera BX para comparar com o resto
	cmp dx, bx	; Compara o resto com zero
	ja d		; Se o resto for maior do que zero
			; acende o último LED
	mov bx, FFF8	; Se resto = 0, põe o endereço do
			; primeiro LED em BX
	mov [bx], ax	; Liga o primeiro LED
	halt		; Termina o programa
d:	mov bx, FFFE	; Põe o endereço do último LED em BX
	mov [bx], ax	; Liga o último LED
	halt		; Termina o programa

Comentários

Deu para se divertir? Espero que sim. Já deu para perceber que a linguagem Assembly não é um bicho de sete cabeças. Pelo menos quando se trata do nosso processador hipotético, o 886 de 16 bits e quando podemos contar com o SIMx86. Brinque até não poder mais. Quanto mais você programar e testar nesta fase, mais preparado vai estar para os próximos assuntos.



| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > Assembly NumaBoa > Entrada e Saída > Laboratório cap.3 > Memória
Autoria: Randall Hyde - Art of Assembly Language Programming. Tradução: vovó Vicki

webdesign sobMedida by vickiSoft - /informatica/assembly/cap3_lab.php (23.01.04) versão 1.0 de 12.02.04
Licença Creative Commons 1998-2006 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.