Você está na página 1de 107

O que Assembler?

? -------------------Assembler uma linguagem de baixo nvel, que voc pode usar em seus programas para acelerar tarefas lentas. Basicamente ela consite de sentenas que representam instrues em linguagem de mquina, e, como ela est prxima ao cdigo de mquina, ela rpida. H muito tempo atrs, quando o 8086 apareceu, programar no era uma tarefa fcil. Quando os primeiros computadores foram desenvolvidos, a programao tinha que ser feita em cdigo de mquina, que no era uma tarefa fcil, e assim o Assembler nasceu. Por que us-lo? ----------------Assembler veloz. Ele tambm permite a voc falar com a mquina a nvel de hardware, e lhe d muito maior controle e flexibilidade sobre o PC.

LIO 1 - Registradores -------------------------Quando voc est trabalhando com Assembler, voc tem que usar registradores. Voc pode imagin-los como sendo variveis j definidas para voc. Os mais comuns esto listados abaixo: AX - o acumulador. Compreende AH e AL, os bytes alto e baixo de AX. Comumente usado em operaes matemticas e de E/S. BX - a base. Compreende BH e BL. Comumente usado como uma base ou registrador apontador. CX - o contador. Compreende CH e CL. Usado frequentemente em loops. DX - o deslocamento, similar ao registrador de base. Compreende DH e DL. Acho que voc est pegando o esprito da coisa agora. Estes registradores so definidos como registradores de uso geral pois podemos realmente armazenar qualquer coisa que quisermos neles. So tambm registradores de 16 bits, o que significa que podemos armazenar um inteiro positivo de 0 a 65535, ou um inteiro com sinal de -32768 to 32768.

Incidentalmente, o assunto do alto e do baixo byte destes resgistradores causou muita confuso no passado, logo, tentarei dar alguma explicao aqui. AX tem um intervalo de 0 at FFFFh. Isto significa que voc tem um intervalo de 0 at FFh para AH e AL. (Se sabe pouco sobre hexadecimal, no se preocupe. O prximo tutorial vai falar sobre ele.) Agora, se ns tivermos que armazenar 0A4Ch em AX, AH conter 0Ah, e AL conter 4Ch. Sacou? Este um conceito muito importante, e eu falarei sobre ele mais profundamente no prximo tutorial. Os registradores de segmento: - ta da! Estes so outros registradores que ns no vamos ver nos primeiros tutorias, mas vamos v-los em maior profundidade mais tarde. Eles so imensamente teis, mas podem ser tambm perigosos. CS - o segmento de cdigo. O bloco de memria onde o cdigo armazenado. NO brinque com esse, a menos que saiba o que est fazendo. DS - o segmento de dados. A rea na memria onde os dados so armazenados. Durante operaes de bloco, quando grandes blocos de dados so movidos, este o segmento a que a CPU comumente se refere. ES - o segmento extra. Apenas outro segmento de dados, mas este comumente usado quando se quer acessar o vdeo. SS - o segmento de pilha, em que a CPU armazena endereos de retorno de subrotinas. Tome cuidado com ele. Alguns outros que voc vai comumente usar: SI - o ndice de fonte. Frequentemente usado para movimentaes de blocos de instrues. Este um ponteiro que, com um segmento, geralmente DS, usado pela CPU para leitura. DI - o ndice de destino. Novamente, voc o usar muito. Um outro ponteiro que, com um segmento, geralmente ES, usado para escrita pela CPU. BP - o apontador da base, usado em conjunto com o segmento de pilha. Ns no vamos us-lo muito. SP - o apontador da pilha, comumente usado com o segmento de pilha. NO brinque com isso de jeito nenhum.

Por enquanto voc deveria saber o que so registradores. H outros registradores tambm, e coisas conhecidas como flags, mas ns no iremos a eles agora. COISAS PARA FAZER: 1) Aprender os vrios registradores de cor. 2) Arrumar uma calculadora que suporte hexadecimal - ou pelo menos uma tabela ASCII. Isso cobre 0 - 255, ou, de 0h a FFh.

LIO 2 - O conjunto de instrues do 8086: ---------------------------------------------Okay, ento voc j aprendeu sobre registradores, mas, como us-los, e como se codifica em Assembler? Bem, primeiro voc precisa de algumas instrues. As seguintes instrues podem ser usadas em todas as CPU's do 8086 para cima. MOV <dest>, <valor> - MOVE. Esta instruo permite MOVER um valor para uma posio na memria. Ex.: MOV AX, 13h Isso deveria mover 13h (19 em decimal) para o registrador AX. Logo, se AX valia antes 0, ele agora seria 13h. ISSO APENAS MOVE UM VALOR PARA UM REGISTRADOR, NO FAZ NADA MAIS. Ex.: (Em Pascal) AX := $13; INT <nmero> - INTERRUPO. Esta instruo gera uma interupo. Voc pode pensar nisso como sendo quase uma procedure. Ex.: INT 10h Geraria a interrupo 10h (16 em decimal). Agora, o que isso faria depende do contedo do registrador AH, entre outras coisas. Por exemplo, se AX = 13h e a interrupo 10h foi gerada, o vdeo seria

colocado no modo 320x200x256. Mais precisamente: AH seria igual a 00 - seleciona a subfuno do modo, e AL seria igual a 13h - modo grfico 320x200x256. Contudo, se AH = 2h, e a interrupo 16h foi gerada, isso instruiria a CPU para checar se alguma tecla pressionada est no buffer do teclado. Se AH = 2h, e BH = 0h e a interrupo 10h foi gerada, ento a CPU moveria o cursor para a posio X em DL e posio Y DH. NO SE PREOCUPE COM ISSO POR ENQUANTO! NS FALAREMOS NISSO MAIS TARDE, COM MAIS DETALHES. ADD <dest> <valor> - ADICIONA. Esta instruo soma um nmero ao valor armazenado em dest. Ex: MOV AX, 0h ; AX agora igual a 0h ADD AX, 5h ; AX agora igual a 5h ADD AX, 10h ; AX agora igual a 15h SUB <dest> <valor> - SUBTRAI. Acho que d pra voc adivinhar o que isso faz. Ex: MOV AX, 13h ; AX agora igual a 13h (19 dec) SUB AX, 5h ; AX agora igual a 0Eh (14 dec) DEC <registrador> - DECREMENTA algo. Ex: MOV AX, 13h ; AX agora igual a 13h DEC AX ; AX agora igual a 12h INC <registrador> - INCREMENTA algo. Ex: MOV AX, 13h ; Adivinha... INC AX ; AX = AX + 1 JMP <posio> - PULA para uma posio.

EG: JMP 020Ah ; Pula para a instruo em 020Ah JMP @MyLabel ; Pula para @MyLabel. NO SE PREOCUPE SE ISTO UM POUCO CONFUSO - VAI FICAR PIOR! H OUTRAS 28 INSTRUES JUMP PARA APRENDER, TALVEZ MAIS. FALAREMOS NELAS MAIS TARDE. CALL <procedimento> - CHAMA uma subfuno. EG: Procedure MyProc; Begin { MyProc } { ... } End; { MyProc } Begin { Main } Asm CALL MyProc ; Adivinha o que isso faz! End; End. Ou: CALL F6E0h ; Chama subfuno em F6E0h LOOP <rtulo/label> - Faz LOOPS (repetio) durante um certo tempo. EG: MOV CX, 10h ; Isto o porque de CX ser ; chamado de registro CONTADOR. ; 10h = 16 @MyLabel: ; alguma coisa ; mais coisa LOOP @MyLabel ; At que CX = 0 ; Note: CX decrementado ; a cada vez. No decremente-o ; voc mesmo (DEC CX). ; ISSO DEVERIA SE REPETIR 16 vezes - i.e., 10 em hexadecimal. LODSB LODSW - Carrega um byte - Carrega uma word

STOSB STOSW

- Armazena um byte - Armazena uma word

Estas instrues so usadas para pr ou conseguir algo numa posio na memria. O registrador DS:SI, (lembra que ns falamos sobre isso antes, sobre SI ser o ndice de fonte?), aponta para a localizao de onde queremos obter os dados, e DS:DI aponta para onde colocaremos informaes. claro, no somos obrigados a usar DS - poderia ser ES por exemplo. Meu procedimento PutPixel colocar um byte em ES:DI. De qualquer modo, imagine que temos a seguinte configurao na memria: Posio na memria 06 07 08 09 10 11 12 Valor 50 32 38 03 23 01 12

Quando ns usamos LODSB ou STOSB, ele retorna ou pega um nmero de AL. Assim, se DS:SI apontava para 07 e executssemos uma instruo LODSB, AL seria agora igual a 32. Agora, se ns apontssemos DS:DI para 11, colocando, diria, 50 no registrador AL, e executasse STOSB, ento teramos o seguinte resultado: Posio na Memria 06 07 08 09 10 11 12 Valor 50 32 38 03 23 50 12

OBS.: Quando usamos LODSB/STOSB, usamos AL. Isto porque estaremos mexendo com um nmero de 8 bits (um byte), apenas. Podemos armazenar um nmero de 8 bits em AL, AH, ou AX, mas no podemos armazenar um nmero de 16 bits em AH ou AL porque eles so REGISTRADORES DE 8 BITS. Como resultado, quando usarmos LODSW ou STOSW, ns devemos usar AX e no AL, j que estaremos pegando/colocando um nmero de 16 bits. MOVSB - Move um byte MOVSW - Move uma word Como exemplo vamos pegar um byte de DS:SI e mand-lo para ES:DI. Em DS:SI:

Posio de Memria 06 07 08 09 10 11 12 Valor Em ES:DI: Posio de Memria 06 07 08 09 10 11 12 Valor 10 11 20 02 67 00 12 50 32 38 03 23 50 12

Se apontarmos DS:SI para a posio 07, apontarmos ES:SI para a posio 11 e executarmos MOVSB, o resultado em ES:DI pareceria com: Em ES:DI: Posio de Memria 06 07 08 09 10 11 12 Valor 10 11 20 02 67 32 12

ESPERO QUE VOC PEGUE A IDIA GERAL. CONTUDO, CLARO, NO TO SIMPLES. POSIES DE MEMRIA NO SO ARRUMADOAS EM FORMA DE ARRAY, EMBORA EU DESEJASSE MUITO QUE FOSSEM. QUANDO FOR MOVER/PEGAR/COLOCAR, VOCE ESTAR MEXENDO COM UMA POSIO TAL COMO: 100:102H. AINDA ASSIM, VOC DEVERIA PEGAR A IDIA. REP - REPETE o nmero de vezes especificado no registrador CX. Um REP na frente de um MOVSB/LODSB/STOSB causaria a repetio da instruo. Logo: Se CX = 5, e se ES:DI apontava para 1000:1000h, ento REP STOSB armazenaria o que estava no registrador AL na posio 1000:1000h 5 vezes. COISAS A FAZER: 1) Memorizar todas as instrues acima - no to difcil assim e no h tantas l. 2) Tenha certeza que voc entendeu a teoria por trs delas.

LIO 3 - Segmentos e Offsets --------------------------------Antes de explorarmos o grande e mau mundo dos segmentos e offsets, h umas terminologias que voc precisar conhecer. O BIT - a menor parte de dados que podemos usar. Um bit - um oitavo de um byte pode ser ou um 1 ou um 0. Usando esses dois dgitos podemos fazer nmeros em BINRIO ou BASE 2. EX. 0000 = 0 0001 = 1 0010 = 2 0011 = 3 0100 = 4 0101 = 5 0110 = 6 0111 = 7 1000 = 8 1001 = 9 1010 = 10 1011 = 11 1100 = 12 10000 = 16 1101 = 13 ... 1110 = 14 1111 = 15

O NIBBLE, ou quatro bits. Um nibble pode ter um valor mximo de 1111 que 15 em decimal. aqui que o hexadecimal entra. Hex baseado naqueles 16 nmeros, (0-15), e quando escrevemos em hex, usamos os 'dgitos' abaixo: 0123456789ABCDEF Hexadecimal na verdade muito fcil de se usar, e, apenas como curiosidade, eu acho que os Babilnios - uma civilizao antiga qualquer - usava um sistema de numerao em BASE 16. Tem algum historiador a fora que queira confirmar isso? IMPORTANTE >>> Um nibble pode aguentar um valor at Fh <<< IMPORTANTE O BYTE - o que mais usaremos. O byte tem 8 bits de tamanho - isso 2 nibbles, e o nico valor que voc vai conseguir colocar num registrador de 8 bits. EX.: AH, AL, BH, BL, ... Um byte tem um valor mximo de 255 em decimal, 11111111 em binrio, ou FFh em hexadecimal. A WORD - outra unidade comumente usada. Uma word um nmero de 16 bits, e capaz de armazenar um nmero at 65535. Isso 1111111111111111 em binrio, e FFFFh em hex. Obs.: Por causa de uma word ser quatro nibbles, tambm representada por quatro dgitos hexadecimais. Obs.: Isto um nmero de 16 bits, e corresponde aos registradores de 16 bits. Ou seja, AX, BX, CX, DX, DI, SI, BP, SP, DS, ES, SS

e IP. A DWORD, ou double word consiste de 2 words ou 4 bytes ou 8 nibbles ou 32 bits. Voc no vai usar muito as double words nestes tutoriais, mas vamos mencion-las mais tarde quando falarmos de PROGRAMAO EM 32 BITS. Uma DWORD pode armazenar de 0 a 4,294,967,295, que FFFFFFFFh, ou 11111111111111111111111111111111. Espero que haja 32 um's l atrs. A DWORD tambm o tamanho dos registradores extendiddos de 32 BITS, ou seja, EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP e EIP. O KILOBYTE, 1024 bytes, NO 1000 bytes. O kilobyte igual a 256 double-words, 512 words, 1024 bytes, 2048 nibbles ou 8192 BITS. Eu no vou escrever todos os um's. O MEGABYTE, ou 1024 kilobytes. Isso 1,048,576 bytes ou 8,388,608 bits. Agora que j cobrimos a terminologia, vamos dar uma olhada mais de perto como aqueles registradores so estruturados. Ns dissemos que AL e AH eram registradores de 8 bits, logo, eles no deveriam se parecer com algo assim? AH 00000000 AL 00000000

Neste caso, ambos AH e AL = 0, OU 00h e 00h. Como resultado, para calcular AX usamos: AX = 00h + 00h. Quando digo + eu quero dizer, 'ponha junto' no AX = AH MAIS AL. Assim, se AH era igual a 00000011 e AL era igual a 0000100, para calcular AX ns devemos fazer o seguinte. 1) Pegue os valores hexadecimais de AH e AL. 00000011 = 03h 00010000 = 10h 2) Combine-os. AX = AH + AL AX = 03h + 10h AX = 0310h E a voc consegue o resultado. No to macetoso assim.

Okay, agora vamos ver os registradores de 16 bits: AX AH 00000000 AL 00000000

De onde podemos ver que AX = 00000000 e 00000000, ou 0000000000000000. Agora por ltimo, vejamos como um registrador de 32 bits se parece: AX EAX 00000000

00000000 00000000 00000000 AH AL

No muito difcil, espero. E se entendeu isso, voc est pronto para SEGMENTOS e OFFSETS. Uma Arquitetura Segmentada ----------------------------H muito, muito tempo atrs, quando a IBM construiu o primeiro PC, no era costume programas terem mais de 1 megabyte - eca, os primeiros XT's tinham apenas 64K de RAM! De qualquer modo, vendo que os projetistas do XT no consideravam aplicaes enormes, decidiram dividir a memria em SEGMENTOS, pequenas reas de memria RAM que voc pode colocar APENAS uma tela virtual para grficos em modo 320x200x256. claro, voc pode acessar mais de um megabyte de RAM, mas voc tem que dividi-la em segmentos para us-la, e esse o problema. obvio, com programao em 32 bits d pra acessas at 4GB de RAM sem usar segmentos, mas isso uma outra histria. Segmentos e offsets so apenas um mtodo de especificar uma posio na memria.

EG: 3CE5:502A ^^^^ ^^^^ SEG OFS Okay, aqui est a especificao: Um OFFSET = SEGMENT X 16 Um SEGMENT = OFFSET / 16 Algums registradores de segmento so: CS, DS, ES, SS e FS, GF - Obs.: Os ltimos 2 so registradores que s existem em 386 ou superiores. Alguns registradores de offset so: BX, DI, SI, BP, SP, IP - Obs.: Quando em modo protegido, voc pode usar qualquer registrador de uso geral como um registrador de offset - EXCETO IP.

Alguns segmentos e offsets comuns so: CS:IP - Endereo do cdigo executando no momento. SS:SP - Endereo da posio atual da pilha. OBS.: NO SE INTROMETA COM ELES ! Assim quando nos referirmos a segmentos e offsets, faremos dessa forma: SEGMENTO:OFFSET Um bom exemplo seria: A000:0000 - que na verdade corresponde ao topo esquerdo da tela VGA em modo colorido 320x200x256. ** FATO ENGRAADO ** A RAM da VGA comea em A000h :) Ufa! Isso foi muito para o segundo tutorial. Contudo, ainda no terminamos. Esse negcio de AX, AH, AL um conceito que voc pode no ter sacado ainda, ento l vamos ns:

MOV AX, 0 MOV AL, 0 MOV AH, 0

; AX = 0 ; AL = 0 ; AH = 0

MOV AL, FFh ; AL = FFh ; AX = 00FFh ; AH = 00h INC AX ; AX = AX + 1 ; AX = 0100h ; AH = 01h ; AL = 00h MOV AH, ABh ; AX = AB00h ; AH = ABh ; AL = 00h

COISAS A FAZER: 1) Aprender aquele negcio de BIT/NIBBLE/BYTE... de cor. 2) Voltar nos exemplos de segmento e offset. 3) Tenha certeza que voc entendeu a relao entre AX, AH e AL. 4) Que tal um problemas de adio hexadecimal?

A Pilha ----------A pilha uma caracterstica muito til de que podemos tirar vantagem. Pense nela como uma pilha de papis numa bandeja de ENTRADA. Se voc pe algo no topo, ela ser a primeira a ser tirada. medida que voc adiciona algo pilha, o apontador de pilha DECREMENTADO, e quando tira, INCREMENTADO. Para explicar isso melhor, veja o diagrama abaixo: A PILHA <<< Quando colocamos um byte na pilha, ele vai aqui - ltimo a entrar, primeiro a sair.

SP E na prtica:

<<< O ponteiro de pilha se move para baixo.

MOV AX, 03h ; AX = 03h PUSH AX ; PUSH AX na pilha (coloca no topo) MOV AX, 04Eh ; AX = 04Eh ; Faa alguma coisa... uma soma? POP AX Ou: MOV AX, 03h ; AX = 03h PUSH AX ; Adiciona AX pilha MOV AX, 04Eh ; AX = 04Eh ; Faa alguma coisa... uma soma? POP BX ; BX = 03h ; AX = 03h

Voc acabou de aprender duas instrues: PUSH <registrador> - PUSH (coloca algo na pilha), e POP <registrador> - POP (retira ele de volta).

tudo o que voc precisa de aprender sobre pilha - por enquanto.

Por ltimo, algumas procedures que demonstram algo disso tudo. Note que os comentrios foram DELIBERADAMENTE REMOVIDOS. seu dever tentar coment-los. Note tambm, que algumas novas instrues so introduzidas.

Procedure ClearScreen(A : Byte; Ch : Char); Assembler; Asm { ClearScreen } mov ax, 0B800h mov es, ax xor di, di mov cx, 2000 mov ah, A mov al, &Ch rep stosw End; { ClearScreen } Procedure CursorXY(X, Y : Word); Assembler; Asm { CursorXY } mov ax, Y mov dh, al dec dh mov ax, X mov dl, al dec dl mov ah, 2 xor bh, bh int 10h End; { CursorXY } Procedure PutPixel(X, Y : Integer; C : Byte; Adr : Word); Assembler; Asm { PutPixel } mov ax, [Adr] mov es, ax mov bx, [X] mov dx, [Y] xchg dh, dl mov al, [C] mov di, dx shr di, 2 add di, dx add di, bx stosb End; { PutPixel }

Procedure Delay(ms : Word); Assembler; Asm { Delay } mov ax, 1000 mul ms mov cx, dx mov dx, ax mov ah, 86h int 15h End; { Delay } COISAS A FAZER: 1) V ao exemplo de pilha. Faa seu prprio cdigo exemplo. 2) Comente as procedures acima do melhor modo que puder. Tente adivinhar o que as novas intrues fazem. No to difcil.

Durante este tutorial, voc achar os livros "Peter Norton's Guide to Assembler", "Peter Norton's Guide to the VGA Card", ou qualquer um dos livros "Peter Norton's Guide to..." muito teis. Voc no pode programar em Assembler sem saber pra que so todas as interrupes e o que so todas as subfunes. Eu lhe recomendo conseguir uma cpia desses livros assim que possvel. Um Programa Assembly ----------------------Eu geralmente no escrevo cdigo 100% em Assembly. muito mais conveniente usar uma linguagem de alto nvel como C ou Pascal, e usar Assembly para acelerar os bits lentos. Contudo, voc pode querer se torturar e escrever uma aplicao completamente em Assembly, ento aqui vai a configurao bsica: DOSSEG MODEL STACK DATA CODE - diz CPU como organizar o segmento - declara o modelo que vamos usar - quanta pilha vamos alocar? - o qu vai no segmento de dados - o qu vai no segmento de cdigo

START

- o incio do seu cdigo

END START - o fim do seu cdigo

DOSSEG .MODEL SMALL .STACK 200h .DATA .CODE START: MOV AX, 4C00h ; AH = 4Ch, AL = 00h INT 21h END START Vamos ver em detalhes. Abaixo, cada uma das frases acima est explicada. DOSSEG - isto ordena os segmentos na ordem:

Segmentos de Cdigo; Segmentos de Dados; Segmentos de Pilha. No se preocupe muito com isso por enquanto, apenas inclua at que voc saiba o que est fazendo. MODEL - isso permite CPU determinar como seu programa est estruturado. Voc pode ter os seguintes MODELos: 1) TINY - tanto cdigo quanto dados se encaixam no mesmo segmento de 64K. 2) SMALL - cdigo e dados esto em segmentos diferentes, embora cada um tenha menos de. 3) MEDIUM - cdigo pode ser maior que 64K, mas os dados tm que ter menos que 64K. 4) COMPACT - cdido menos de 64K, mas dados podem ter mais que 64K. 5) LARGE - cdigo e dados podem ter mais que 64K, embora arrays no possam ser maiores que 64K.

6) HUGE

- cdigo, dados e arrays podem ter mais de 64K.

STACK - isso instrui ao PC para arrumar uma pilha to grande quanto for especificado. DATA - permite a voc criar um segmento de dados. Por exemplo: MySegment SEGMENT PARA PUBLIC 'DATA' ; Declare alguns bytes, words, etc. MySegment ENDS Isso similar a CONSTANTES in Pascal. CODE - permite a voc criar um segmento de cdigo. Ex.: MyCodeSegment SEGMENT PARA PUBLIC 'CODE' ; Declare algo MyCodeSegment ENDS START - Apenas um label para dizer ao compilador onde a parte principal do seu programa comea. MOV AX, 4C00h ; AH = 4Ch, AL = 00h Isso move 4Ch para ah, que coincidentemente nos traz de volta ao DOS. Quando a interrupo 21h chamada e AH = 4Ch, de volta ao DOS l vamos ns. INT 21h END START - Voc no tem imaginao? Okay, Espeo que voc tenha entendido tudo isso. Neste exemplo ns vamos usar a interrupo 21h, (a interrupo do DOS), para imprimir uma string. Para ser preciso, vamos usar a subfuno 9h, e ela se parece com isso: INTERRUPO 21h SUBFUNO 9h

Requer: AH = 9h DS:DX = ponteiro FAR para a string a ser impressa. A string deve ser terminada com um sinal $. Assim, aqui est o exemplo: DOSSEG .MODEL SMALL .STACK 200h .DATA OurString DB "Isto uma string de caracteres" .CODE START: MOV AX, SEG OurString ; Move o segmento onde OurString est MOV DS, AX ; para AX, e agora para DS MOV DX, OFFSET OurString ; Offset de OurString -> DX MOV AH, 9h ; Subfuno de imprimir strings INT 21h ; Gera a interrupo 21h MOV AX, 4C00h ; Subfuno de sada para o DOS INT 21h ; Gera a interrupo 21h END START Se voc assemblar isso com TASM - TASM SEJALADOQUEVOCECHAMOUELE.ASM ento linkar com TLINK - TLINK SEJALADOQUEVOCECHAMOUELE.OBJ voc vai conseguir um arquivo EXE de cerca de 652 bytes. Voc pode usar estes programas no DEBUG com algumas modificaes, mas eu vou deixar isso contigo. Para trabalhar com Assembler puro voc _precisa_ de TASM e TLINK, embora eu ache que MASM faria o mesmo trabalho muito bem. Agora vamos ao cdigo com um pouco mais detalhado: MOV AX, SEG OurString ; Move o segment onde OurString est MOV DS, AX ; para AX, e agora para DS MOV DX, OFFSET OurString ; Move o offset onde OurString est localizado MOV AH, 9h ; Subfuno de escrita de strings INT 21h ; Gera a interrupo 21h

Voc vai notar que tivemos que usar AX para pr o endereo do segmento de OurString em DS. Voc vai descobrir que no d pra referenciar um registrador de segmento diretamente em Assembler. Na procedure PutPixel do ltimo tutorial, eu movi o endereo da VGA para AX, e ento para ES. A instruo SEG tambm introduzida. SEG retorna o segmento onde a string OurString est localizada, e OFFSET retorna, adivinha o qu?, o offset do incio do segmento para onde a string termina. Note tambm que ns usamos DB. DB no nada de especial, e significa Declare Byte, que o que tudo o que ela faz. DW, Declare Word e DD, Declare Double Word tambm existem. Voc poderia ter tambm colocado OurString segmento de cdigo, a vantagem que estaria CS apontando para o mesmo segmento que OurSting, de modo que voc no tem que se preocupar em procurar o segmento em que OurString est. O programa acima no segmento de cdigo seria mais ou menos assim: DOSSEG .MODEL SMALL .STACK 200h .CODE OurString DB "Abaixo o segmento de dados!$"

START: MOV AX, CS MOV DS, AX MOV DX, OFFSET OurString MOV AH, 9 INT 21h MOV AX, 4C00h INT 21h END START Simples, no? Ento, o que so flags? --------------------------

Esta parte para meu companheiro Clive que tem me perguntado sobre flags, ento l vamos ns Clive, com FLAGS. Eu no me lembro se j introduzimos a instruo CMP ou no, CMP - (COMPARE), mas CMP compara dois nmeros e reflete a comparao nos FLAGS. Para us-la voc faria algo desse tipo: CMP AX, BX ento seguir com uma instruo como essas abaixo: COMPARAES SEM SINAL: ----------------------- JA JAE JB JBE JNA JNAE JNB JNBE JZ JE JNZ JNE - pula (jump) se AX foi MAIOR que BX; - pula se AX foi MAIOR ou IGUAL a BX; - pula se AX foi MENOR que BX; - pula se AX foi MENOR ou IGUAL a BX; - pula se AX foi NO MAIOR que BX; - pula se AX foi NO MAIOR ou IGUAL a BX; - pula se AX foi NO MENOR que BX; - pula se AX foi NO MENOR ou IGUAL a BX; - pula se o flag de ZERO est setado - o mesmo que JE; - pula se AX for IGUAL a BX; - pula se o flag de ZERO NO est setado - o mesmo que JNE; - pula se AX NO for IGUAL a BX;

COMPARAES COM SINAL: ----------------------- JG - pula (jump) se AX foi MAIOR que BX; JGE - pula se AX foi MAIOR ou IGUAL a BX; JL - pula se AX foi MENOR que BX; JLE - pula se AX foi MENOR ou IGUAL a BX JNG - pula se AX foi NO MAIOR que BX JNGE - pula se AX foi NO MAIOR ou IGUAL a BX; JNL - pula se AX foi NO MENOR que BX; JNLE - pula se AX foi NO MENOR ou IGUAL a BX; JZ - pula se o flag de ZERO est setado - o mesmo que JE; JE - pula se AX for IGUAL a BX; JNZ - pula se o flag de ZERO NO est setado - o mesmo que JNE; JNE - pula se AX NO for IGUAL a BX;

NO TO COMUNS: ---------------- JC JNC JO JNO JP JNP JPE JPO JS JNS - pula se o flag de CARRY est setado; - pula se o flag de CARRY NO est setado; - pula se o flag de OVERFLOW est setado; - pula se o flag de OVERFLOW NO est setado; - pula se o flag de PARIDADE est setado; - pula se o flag de PARIDADE NO est setado; - pula se a PARIDADE for PAR - o mesmo que JP; - pula se a PARIDADE for MPAR - o mesmo que JNP; - pula se o flag de SINAL NO est setado; - pula se o flag de SINAL est setado.

De qualquer modo, aqui est com o que eles se parecem: Flag SF ZF -- AF -- PF -- CF Bit 07 06 05 04 03 02 01 00 Legenda: ---------SF - Flag de Sinal; ZF - Flag de Zero; AF - Flag Auxiliar; PF - Flag de Paridade. CF - Flag de Carry (vai um). Nota: H MUITO MAIS FLAGS PARA APRENDER. Eles sero vistos num Tutorial mais frente.

COISAS PARA FAZER: 1) Volte ao frame da configurao bsica de Assembler e memorize-o. 2) Tenter escreer um programa simples que mostre alguns comentrios criativos. 3) Aprenda as instrues JUMP menos criptogrficos de cor.

PS: - Procedure: apenas um sumrio do que cada comando faz. Ex.: MOV AX, 0003h ; AX agora igual a 03h; ADD AX, 0004h ; AX agora igual a 07h; Ento, aqui vai o conjunto completo das procedures com comentrios: { Esta procedure limpa a tela em modo texto } Procedure ClearScreen(A : Byte; Ch : Char); Assembler; Asm { ClearScreen } mov ax, 0B800h { Move o endereo de vdeo para AX } mov es, ax { Aponta ES para o segmento de vdeo } xor di, di { Zera DI } mov cx, 2000 { Move 2000 (80x25) para CX } mov ah, A { Move o atributo para AH } mov al, &Ch { Move o caracter a usar para AL } rep stosw { Faz isso } End; { ClearScreen } Explicao: Ns zeramos DI, logo igual a 0 - o canto esquerdo da tela. Isto de onde vamos comear a encher a tela. Movemos 2000 para CX porque vamos colocar 2000 caracteres na tela. { Esta procedure move o cursor para a posio X, Y } Procedure CursorXY(X, Y : Word); Assembler; Asm mov mov dec mov mov dec { CursorXY } ax, Y { Move o valor Y para AX } dh, al { Y vai para DH } dh { rotina baseada em ajustar para zero } ax, X { Move o valor de X para AX } dl, al { X vai para DL } dl { rotina baseada em ajustar para zero }

mov ah, 2 { Chama a funo correspondente } xor bh, bh { Zera BH } int 10h { faz isso (pe o cursor na posio) } End; { CursorXY } Explicao: A ' rotina baseada em ajustar para zero' realizada porque a BIOS refere-se posio (1, 1) como (0, 0), e igualmente (80, 25) como (79, 24).

Procedure PutPixel(X, Y : Integer; C : Byte; Adr : Word); Assembler; Asm { PutPixel } mov ax, [Adr] { Move o endereo do VGA em AX mov es, ax { Joga AX em ES } mov bx, [X] { Move o valor de X para BX } mov dx, [Y] { Move o valor de Y para DX } xchg dh, dl { Daqui pra frente calcula o } mov al, [C] { offset do pixel a ser plotado } mov di, dx { e pe este valor em DI. Vamos } shr di, 2 { ver isso mais tarde - prximo tutorial} add di, dx { quando falarmos sobre shifts } add di, bx { versus muls } stosb { Guarda o byte em ES:DI } End; { PutPixel } }

NOTA: Eu estaria muito interessado em achar uma procedure PutPixel mais rpida que essa. Eu j vi uma inline que faz isso em metade do tempo, mas mesmo assim, essa muito quente. { Esta procedure uma funo de delay independente de CPU } Procedure Delay(ms : Word); Assembler; Asm { Delay } mov ax, 1000 mul ms mov cx, dx mov dx, ax mov ah, 86h int 15h { Move o nmero de ms em um segundo para AX } { Faz AX = nmero de ms a esperar } { Prepara para o delay - pe nmero de ms } { onde necessrio } { Cria o delay }

End;

{ Delay }

Vamos falar um pouco sobre o VGA. Isso basicamente para onde eu tenho conduzido no meu modo desconjuntado, de qualquer modo, j que programao grfica no s recompensante, divertido tambm! Bem, eu acho que . Primeiramente porm, devemos terminar aquela coisa de CMP/JMP, e falar de shifts. Quando se est programando em Assembler, a gente acha que comparaes, shifts e testar bits so operaes muito comuns.

Um Exemplo de Comparao -------------------------Eu no vou perder tempo explicando minuciosamente o seguinte exemplo - ele muito fcil de entender e voc deve pegar a idia basica seja l como for. DOSSEG .MODEL SMALL .STACK 200h .DATA FirstString DB 13, 10, "Este um grande tutorial ou o qu? :) - $" SecondString DB 13, 10, "NO? NO? O que voc quer dizer, NO?$" ThirdString DB 13, 10, "Excelente, vamos ouvir voc dizer isso de novo.$" FourthString DB 13, 10, "Apenas um Y ou N j basta.$" ExitString DB 13, 10, "Bem, deixa pra l!$" .CODE START: MOV AX, @DATA MOV DS, AX ; Novo modo de dizer: ; DS -> SEG segmento de dados

KeepOnGoing: MOV AH, 9 MOV DX, OFFSET FirstString ; DX -> OFFSET FirstString INT 21h ; Escreve a primeira mensagem MOV AH, 0 ; Pega uma tecla - armazena-a em AX

INT 16h

; AL - cdigo ASCII, AH - "scan code" ; Ela no ecoa na tela, contudo, ; ns mesmos temos que fazer isso. ; Aqui ns mostramos na tela o caracter ; note que ns salvamos AX. Obviamente, ; usando-se AH para imprimir uma string ; destri-se AX ; Checa se foi teclado 'Y' ; Se foi, continua

PUSH AX MOV DL, AL MOV AH, 2 INT 21h POP AX CMP AL, "Y" JNE HatesTute

MOV AH, 9 ; Mostra a mensagem "Excelente..." MOV DX, OFFSET ThirdString INT 21h JMP KeepOnGoing ; Volta ao incio e comea de novo HatesTute: CMP AL, "N" JE DontLikeYou ; Certifica que foi teclado 'N' ; Infelizmente, sim.

MOV DX, OFFSET FourthString ; Pede ao usurio para tentar de novo MOV AH, 9 INT 21h JMP KeepOnGoing ; Deixa ele tentar DontLikeYou: MOV DX, OFFSET SecondString MOV AH, 9 INT 21h MOV DX, OFFSET ExitString MOV AH, 9 INT 21h MOV AX, 4C00h INT 21h END START ; Mostra a string "NO? NO? O que..."

; Mostra a string "Bem, deixa pra l!"

; Volta para o DOS

Voc deveria entender este exemplo, brincar um pouco com ele e escrever algo melhor. Aqueles com um livro do Peter Norton ou algo semelhante, experimentem as subfunes do teclado, e veja quais outras combinaes de GetKey existem, ou melhor ainda, brinque com a interrupo 10h e entre em algum modo de vdeo sobrenatural - um que seu PC suporte! - e use algumas

cores.

Shifts -------Um simples conceito, e um que eu j devia ter discutido antes, mas como eu disse - eu tenho minha prpria maneira desconjuntada de fazer as coisas. Primeiro voc vai precisar de entender um pouco de aritmtica hexadecimal e binria. Eu geralmente uso uma calculadora cientfica, mas bom ser capaz de saber como multiplicar, somar e converter entre as vrias bases. CONVERTENDO DE BINRIO PARA DECIMAL: De Volta ao Tutorial Um, ns vimos como nmeros binrios se parecem, ento imagine que eu tenha um nmero binrio de oito dgitos, como: 11001101 O que isso em decimal??? H vrias formas de converter tal nmero, e eu uso a seguinte, que acredito se provavelmente a mais fcil: Nmero Binrio 1 1 0 0 1 1 0 1 7 6 5 4 3 2 1 0

Equivalente Decimal 2 2 2 2 2 2 2 2 Equivalente Decimal 128 64 32 16 8 4 2 1 Valor Decimal 128 + 64 + 0 + 0 + 8 + 4 + 0 + 1 = 205

Pegou a idia? Note que para a ltima linha, seria mais preciso escrever: 1 x 128 + 1 x 64 + 0 x 32 + 0 x 16 + 1 x 8 + 1 x 4 + 0 x 2 + 1 x 1 = 28 + 64 + 0 + 0 + 8 + 4 + 0 + 1 = 205 Desculpe se isto um pouco confuso, mas difcil explicar sem demonstrar. Aqui vai outro exemplo:

Nmero Binrio

0 1 1 1 1 1 0 0 7 6 5 4 3 2 1 0

Equivalente Decimal 2 2 2 2 2 2 2 2 Equivalente Decimal 128 64 32 16 8 4 2 1 Valor Decimal Obs.: Voc pode usar esta tcnica com palavras de 16 ou 32 bits tambm, apenas faa do jeito certo. Ex: Depois de 128, voc escreveria 256, depois 512, 1024 e assim por diante. Voc pode dizer se o equivalente decimal serpar ou mpar pelo primeiro bit. Ex.: No exemplo acima, o primeiro bit = 0, ento o nmero PAR. No primeiro exemplo, o primeiro bit 1, ento o nmero MPAR. 0 + 64 + 32 + 16 + 8 + 4 + 0 + 0 = 124

CONVERTENDO DE DECIMAL PARA BINRIO: Isso provavelmente mais fcil que da base-2 para base-10. Para calcular o que 321 seria em binrio, voc faria o seguinte: 321 = 256 X 1 321 - 256 = 65 = 128 X 0 65 = 64 X 1 65 - 64 = 1 = 32 X 0 1 = 16 X 0 1 = 8 X 0 1 = 4 X 0 1 = 2 X 0 1 = 1 X 1 E voc obteria o nmero binrio - 101000001. Fcil, n? Vamos tentar outro para ter certeza que sabemos fazer: 198 = 128 X 1 198 - 128 = 70 = 64 X 1 70 - 64 = 6 = 32 X 0 6 = 16 X 0 6 = 8 X0 6 = 4 X1

6 - 4= 2 2 - 2= 0

= =

2 X1 1 X0

E isto nos d - 11000110. Note como voc pode checar o primeiro dgito para ver se voc conseguiu sua converso certa. Quando eu escrevi o primeiro exemplo, eu notei que eu fiz um erro quando eu chequei o primeiro bit. No primeiro exemplo, eu consegui 0 - no muito bom para um nmero mpar. Eu entendi o erro e corrigi o exemplo. CONVERTENDO DE HEXADECIMAL PARA DECIMAL: Antes de comear, voc deveria saber que o sistema numrico hexadecimal usa os 'dgitos': 0 1 2 3 4 5 6 7 8 9 A B C D E F = 0 (decimal) = 0 (binrio) = 1 (decimal) = 1 (binrio) = 2 (decimal) = 10 (binrio) = 3 (decimal) = 11 (binrio) = 4 (decimal) = 100 (binrio) = 5 (decimal) = 101 (binrio) = 6 (decimal) = 110 (binrio) = 7 (decimal) = 111 (binrio) = 8 (decimal) = 1000 (binrio) = 9 (decimal) = 1001 (binrio) = 10 (decimal) = 1010 (binrio) = 11 (decimal) = 1011 (binrio) = 12 (decimal) = 1100 (binrio) = 13 (decimal) = 1101 (binrio) = 14 (decimal) = 1110 (binrio) = 15 (decimal) = 1111 (binrio)

Voc vai comumente ouvir hexadecimal referenciado como hex, ou base-16 e ela comumente denotada por um 'h' - ex.: 4C00h, ou um '$', ex.: - $B800. Trabalhar com hexadecimal no to difcil como pode parecer, e converter pra l ou pra c bem fcil. Como exemplo, vamos converter B800h para decimal: FATO ENGRAADO: B800h o endereo inicial do vdeo em modo texto para CGA e placas superiores. :) B = 4096 x B = 4096 x 11 = 45056 8 = 256 x 8 = 256 x 8 = 2048 0 = 16 x 0 = 16 x 0 = 0

1x0=

1x 0=

Logo B800h = 45056 + 2048 + 0 + 0 = 47104 Obs.: Para nmeros em hexadecimal maiores que FFFFh (65535 em decimal), voc somente segue o mesmo procedimento como para binrio, logo, para o quinto dgito hexadecimal, voc multiplicaria por 65535. Tecle 16 X X na sua calculadora, e fique apertando =. Voc ver os nmeros que precisaria usar. O mesmo aplica-se para binrio. Ex.: 2 X X e = lhe daria 1, 2, 4, 8, 16... etc. Vamos dar uma olhada em: CONVERTENDO DE DECIMAL PARA HEXADECIMAL: Mais uma vez, o mesmo tipo de procedimento como usamos para binrio. Logo, para converter 32753 para hexadecimal, voc faria assim: 32753 / 4096 = 7 (decimal) = 7h

32753 - (4096 x 7) = 4081 4081 / 256 = 15 (decimal) = Fh

4081 - (256 x 15) = 241 241 / 16 241 - (16 x 15) 1/1 =1 = 1 (decimal) = 1h = 15 (decimal) = Fh

Assim, eventualmente temos 7FF1h como resposta. Este no particularmente um bom processo e requer alguma explicao. 1) Quando voc divide 32753 por 4096 voc consegue 7.9963379... No estamos interessados no lixo .9963379, s pegamos o 7, j que 7 o maior nmero inteiro que podemos usar.

2) O resto da operao acima 4081. Devemos agora realizar a mesma operao nisso, mas com 256. Dividindo 4081 por 256 nos d 15.941406... Novamente, pegamos s o 15. 3) Agora temos um resto de 241. Dividindo isto por 16 nos d 15.0625. Pegamos o 15, e calculamos o resto. 4) Nosso ltimo resto acontece que um. Dividindo isso por um chegamos a, voc advinhou - um. VOC NO DEVERIA CONSEGUIR UMA RESPOSTA COM MUITAS CASAS DECIMAIS AQUI. SE VOC TEM - VOC CALCULOU ERRADO. um processo muito imundo, mas funciona. OK, agora que j lidamos com os clculos horripilantes, Voc j est pronto para os shifts. - SHIFTS: H geralmente 2 formas da instruo shift - SHL (shift left/esquerda) e SHR (shift right/direita). Basicamente, tudo o que essas instrues fazem deslocar uma expresso para a esquerda ou direita um certo nmero de bits. Sua principal vantagem a habilidade de lhe deixar substituir multiplicaes lentas com shifts mais rpidos. Voc vai achar que isso acelerar pra caramba os algoritmos de pixel/linhas/crculo. Os PC's esto ficando cada vez mais rpidos a cada dia. De volta aos dias do XT - a multiplicao era realmente lenta - talvez levando at 4 segundos para certas operaes. Hoje em dia isso no acontece assim, mas uma boa idia otimizar seu cdigo. Quando ns plotamos um pixel na tela, temos que encontar o offset do pixel a plotar. Basicamente, o que fazemos multiplicar a posio Y por 320, somar a posio X, e somar isso ao endereo A000h. Assim basicamente, temos: A000:Yx320+X Agora, seja l quo rpido seu maravilhoso 486 ou Pentium , isso poderia se feito um pouco mais rpido. Vamos reescrever aquela equao acima, assim, vamos usar alguns nmeros diferentes: 8 6 Offset = Y x 2 + Y x 2 + X Ou: Offset = Y x 256 + y x 64 + X

Reconhece esses nmeros? Eles parecem terrivelmente com aqueles que ns vimos naquela tabela de converso binrio-decimal. Contudo, ns ainda estamos usando multiplicao. Como podemos incorporar shifts? Que tal: Offset = Y SHL 8 + Y SHL 6 + X Agora, isso _muito_ mais rpido, j que tudo o que o computador tem que fazer um shift esquerda com o nmero - muito melhor. Note que o shift esquerda AUMENTA o nmero, e o shift direita DIMINUI o nmero. Aqui est um exemplo que pode te ajudar se voc ainda est em dvida no que est acontecendo. Digamos que estamos trabalhando em base-10 - decimal. Agora Peguemos o nmero 36 como exemplo. "Shiftando" este nmero esquerda de 1, temos: 36 + 36 Agora SHL 2: 36 + 36 + 36 + 36 = 144 = 72

E SHL 3: 36 + 36 + 36 + 36 + 36 + 36 + 36 + 36 = 288 Notou os neros que se formaram? Havia 2 36's com SHL 1, 4 36's com SHL 2 e 8 36's com SHL 3. Seguindo este padro, seria justo assumir que 36 SHL 4 equivaler a 36 x 16. Note porm, o que est realmente acontecendo. Se voc fosse trabalhar com o valor binrio de 36, que mais ou menos isso: 100100, e ento shiftasse 36 esquerda de 2, voc teria 144, ou 10010000. Tudo o que a CPU faz na verdade colocar alguns 1's e 0's extras na posio de memria. Como outro exemplo, pegue o nmero binrio 1000101. Se fizermos um shift esquerda de 3, terminaramos com: 1000101 <---------- SHL 3 1000101000

Agora vamos deslocar o nmero 45 DIREITA de 2 unidades. Em binrio isso 101101. De onde: 101101 SHR 2 ----> 1011 Notou o que ocorreu? muito mais fcil para a CPU apenas mover alguns bits (aproximadamente 2 unidades de clock), do que multiplicar um nmero. (Pode demorar at 133 unidades de clock). Ns vamos usar bastante shifts quando estivermos programando a VGA, assim, tenha certeza de que voc entendeu os conceitos por trs disso. PROGRAMANDO A VGA EM ASSEMBLER Quando ns falamos sobre programar a VGA, ns estamos geralmente falando do modo 13h, ou um de seus parentes. Em VGA padro este o _nico_ modo de usar 256 cores, e provavelmente um dos modos mais fceis tambm. Se voc j tentou experincias com a SVGA, voc vai entender o pesadelo que para o programador dar suporte a todas as diferentes placas SVGA que existem - exceto se voc usar VESA que o que discutiremos outra hora. A grande vantagem do modo padro 13h que voc sabe que todas as placas VGA que existem vo suport-lo. As pessoas hoje frequentemente ignoram o modo 13h, achando a resoluo muito granulada para os padres de hoje, mas no se esquea que Duke Nukem, DOOM, DOOM II, Halloween Harry e a maioria dos jogos da Apogee usam este modo para realizar alguns grandes efeitos. A grande coisa sobre o modo 13h - isto 320x200x256 caso voc desconhea, que acessar a VGA RAM incrivelmente fcil. Como 320 x 200 igual a 64,000, possvel encaixar a tela inteira em um segmento de 64K. As ms notcias so que o modo padro 13h realmente s te d uma pgina para usar, seriamente embaraante para scroll e page-flipping. ns vamos cobrir mais tarde estes assuntos, como entrar em seus prprios modos - e modo X que evitar esses problemas. Ento, como entrar no modo padro 13h? A resposta simples. Usamos a interrupo 10h - interrupo de vdeo, e chamamos a subfuno 00h - seleciona o modo. Em Pascal, voc poderia declarar uma procedure como esta:

Procedure Init300x200; Assembler; Asm { Init300x200 } mov ah, 00h { Acerta o modo de vdeo } mov al, 13h { Usa o modo 13h } int 10h { Faz isso } End; { Init300x200 } voc tambm pode ver: mov ax, 13h int 10h Isso perfeitamente correto, e provavelmente economiza um tempo de clock por no colocar 00h em AH e ento 13h em AL, mas mais correto usar o primeiro exemplo. OK, ento estamos no modo 13h, mas o que podemos realmente fazer nele, alm de olhar para uma tela em branco? Poderamos voltar ao modo texto usando: mov ah, 00h mov al, 03h int 10h Mas isso um pouco idiota. Porque no pintar um pixel? H inmeros modos de colocar um pixel na tela. O modo mais fcil em Assembler usar interrupes. Voc faria mais ou menos assim em Pascal: Procedure PutPixel(X, Y : Integer; Color : Byte); Assembler; Asm { PutPixel } mov ah, 0Ch { subfuno de desenhar pixel } mov al, [Color] { Move a cor a plotar para AL } mov cx, [X] { Move o valor X para CX } mov dx, [Y] { Move o valor Y para DX } mov bx, 1h { BX = 1, pgina 1 } int 10h { Plota } End; { PutPixel } Contudo, mesmo isso sendo em Assembler, no particularmente rpido. Por qu?, voc pergunta. Porque isso usa interrupo. Interrupes so timas para entar

e sair de modos de vdeo, ligar e desligar o cursor, etc... mas no para grficos. Voc pode imaginar interrupes como uma secretria eletrnica. "A CPU est ocupada neste momento, mas se voc deixar sua subfuno aps o sinal - ns entraremos em contato." No bom. Vamos usar a tcnica que discutimos anteriormente durante shifts. O que queremos fazer botar o valor da cor que desejamor plotar na VGA diretamente. Para fazer isso, precisamos mover o endereo da VGA para ES, e calcular o offset do pixel que queremos plotar. Um exemplo disso mostrado abaixo: Procedure PutPixel(X, Y : Integer; Color : Byte); Assembler; Asm { PutPixel } mov ax, 0A000h { Move o segmento da VGA para AX, } mov es, ax { e agora para ES } mov bx, [X] { Move o valor X para BX } mov dx, [Y] { Move o valor Y para DX } mov di, bx { Move X para DI } mov bx, dx { Move Y para BX } shl dx, 8 { Nesta parte usamos shifts para multiplicar } shl bx, 6 { Y por 320 } add dx, bx { Agora somamos X ao valor acima calculado, } add di, dx { dando DI = Y x 320 + X } mov al, [Color] { Pe a cor a plotar em AL } stosb { Pe o byte, AL, em ES:DI } End; { PutPixel } Esta procedure rpida o suficiente para comear, embora eu tenha dado uma muito mais rpida uns tutoriais atrs que usa uma tcnica genial para pegar DI.

Brinque com as rotinas de PutPixel e veja o que voc pode fazer com elas. Para aqueles com um livro do Peter Norton, veja que outros procedimentos voc pode fazer usando interrupes. COISAS PARA FAZER: 1) Cobrimos muita coisa nesse tutorial, e alguns conceitos importantes esto nele. Certifique-se de estar comfortvel com comparaes, porque vamos comear a testar bits em breve.

2) Tenha certeza que entendeu aquela coisa de binrio -> decimal, decimal -> binrio, decimal -> hex e hex -> decimal. Faa voc mesmo alguns exemplos de soma e teste suas respostas com a calculadora do Windows. 3) voc _deve_ entender shifts. Se voc ainda tem problemas, faa algumas expresses num papel e teste suas respostas num programa como: Begin { Main } WriteLn(45 SHL 6); ReadLn; End. { Main } e/ou a calculadora do Windows. 4) D uma olhada na parte de VGA, e certifique-se de ter pego a teoria, porque na prxima semana vamos entrar a fundo nisso.

Note que eu inclu um demo starfield neste tutorial dessa semana. Voc pode rodar STARS.EXE, ou olhar STARS.PAS que o cdigo fonte. s um simples demo, mas pode ser usado para fazer alguns efeitos interessantes. Agora, esta semana vamos inicialmente listar um sumrio de todas as instrues que voc j deveria ter aprendido at agora, e umas novas. Ento vamos ver como a VGA arrumada, e cobrir uma rotina simples de linha.

SUMRIO DO CONJUNTO DE INSTRUES

ADC <DEST>, <FONTE> - Nome: Soma com Vai-Um Tipo: 8086+ Descrio: Esta instruo soma <FONTE> e <DEST> e soma o valor armazenado no flag de vai-um, que ser um ou zero,

a <DEST> tambm. Basicamente, DEST = DEST + FONTE + CF EX.: ADD AX, BX ADD <DEST>, <FONTE> - Nome: Add Tipo: 8086+ Descrio: Esta instruo soma <FONTE> e <DEST>, armazenando o resultado em <DEST>. EX.: ADD AX, BX AND <DEST>, <FONTE> - Nome: E Lgico Tipo: 8086+ Descrio: Esta instruo realiza uma comparao bit a bit de <DEST> e <FONTE>, armazenando o resultado em <DEST>. EX.: AND 0, 0 = 0 AND 0, 1 = 0 AND 1, 0 = 0 AND 1, 1 = 1 BT <DEST>, <BIT NUMBER> Tipo: 80386+ - Nome: Testa Bit

Descrio: Esta instruo testa <BIT NUMBER> de <DEST> que pode ser um registrador de 16 ou 32 bits ou posio da memria. Se <DEST> um nmero de 16 bits ento o <BIT NUMBER> pode ser de 0 a 15, seno, se <DEST> um nmero de 32 bits, ento o <BIT NUMBER> pode ter um valor de 0 a 31. O valor em <BIT NUMBER> de <DEST> ento copiado para o flag de vai-um.

EX.: BT AX, 3 JC EraIgualAUm CALL <DEST> - Nome: Chama Procedimento Tipo: 8086+ Descrio: Esta instruo simplesmente chama uma subrotina. Em termos mais tcnicos, ela pe o endereo da prxima instruo, IP, na pilha, e seta IP, o registro de instrues, para o valor especificado por <DEST>. EX.: CALL MyProc CBW - Nome: Converte Byte para Word Tipo: 8086+ Descrio: Esta instruo extende o byte em AL para AX. EX.: MOV AL, 01h CBW ADD BX, AX ; Faz algo com AX CLC - Nome: Limpa Flag de Vai-Um Tipo: 8086+ Descrio: Esta instruo zera o flag de vai-um no registrador de flags. EX.: CLC CLD - Nome: Limpa Flag de Direo Tipo: 8086+ Descrio: Esta instruo limpa o flag de direo no registrador de flags para 0. Quando o flag de direo 0, qualquer instruo de strings incrementa os registradores de ndice SI e DI.

EX.: CLD CLI - Nome: limpa Flag de Interrupo Tipo: 8086+ Descrio: Esta instruo limpa o flag de interrupo no registrador de flags para 0, assim desabilitando interrupes de hardware. EX.: CLI CMC - Nome: Complementa o Flag de Vai-Um Tipo: 8086+ Descrio: Esta instruo checa o valor atual no flag de vai-um. Se for 0 - transforma em 1 e se for 1 - passa a ser 0. EX.: BT AX, 1 JC EraUm JMP Fim EraUm: CMC Fim: CMP <VALOR1>, <VALOR2> Tipo: 8086+ - Nome: Comparao Inteira ; Testa o bit 1 de AX

; Retorna CF para 0

Descrio: Esta instruo compara <VALOR1> e <VALOR2> e reflete a comparao nos flags. EX.: CMP AX, BX Veja tambm as intrues Jcc. CWD - Nome: Converte Word para Doubleword Tipo: 8086+

Descrio: Esta instruo extende a word em AX para o par DX:AX. EX.: CWD DEC <VALOR> - Nome: Decrementa Tipo: 8086+ Descrio: Esta instruo subtrai um do valor em <VALOR> e armazena o resultado em <VALOR>. EX.: DEC AX DIV <VALOR> - Nome: Diviso sem Sinal Tipo: 8086+ Descrio: Esta instruo divide <VALOR> por, ou AX para byte, DX:AX para word ou EDX:EAX para doubleword. Para byte, o quociente retornado em AL e o resto em AH, para word o quociente retornado em AX e o resto em DX e para DWORD, o quociente volta em EAX e o resto em EDX. EX.: MOV AX, 12 MOV BH, 5 DIV BH MOV Quociente, AL MOV Resto, AH IN <ACUMULADOR>, <PORTA> Tipo: 8086+ - Nome: Entrada de porta de E/S

Descrio: Esta instruo l um valor de uma das 65536 portas de hardware especificada no <ACUMULADOR>. AX e AL so comumente usados para portas

de entrada, e DX comumente usado para identificar a porta. EX.: IN AX, 72h MOV DX, 3C7h IN AL, DX INC <VALOR> - Nome: Incrementa Tipo: 8086+ Descrio: Esta instruo soma um ao nmero em <VALOR>, e armazena o resultado em <VALOR>. EX.: MOV AX, 13h ; AX = 13h INC AX ; AX = 14h INT <INTERRUPO> - Nome: Gera uma Interrupo Tipo: 8086+ Descrio: Esta instruo salva os valores correntes dos flags e IP na pilha, e ento chama a <INTERRUPO> baseada no valor de AH. EX.: MOV AH, 00h ; Seta o modo de ; vdeo MOV AL, 13h ; Modo 13h INT 10h ; Gera interrupo Jcc - Nome: Pula (Jump) se Condio Tipo: 8086+

No vou repetir eu mesmo todos os 32, d uma olhada no Tutorial Trs a lista completa deles. Tenha em mente que seria uma boa idia chamar CMP, OR, DEC ou algo semelhante antes de usar uma dessas instrues. :) EX.: DEC AX JZ AX_Chegou_A_Zero JMP <DEST> - Nome: Jump

Tipo: 8086+ Descrio: Esta instruo simplesmente carrega um novo valor, <DEST>, em IP, assim transferindo o controle para outra parte do cdigo. EX.: JMP MyLabel LAHF - Nome: Carega AH com Flags Tipo: 8086+ Descrio: Esta instruo copia os bytes mais baixos do registrador de flags para AH. O contedo de AH vai parecer com algo semeelhante depois que a instruo for executada:

Flag SF ZF Bit 07 06 05

AF

PF

CF

04 03 02 01 00

Voc pode agora testar os bits individualmente, ou realizar uma instruo similar seguinte para pegar um flag apenas: EG: LAHF SHR AH, 6 AND AH, 1 ; AH contm o flag ZF. LEA <DEST>, <FONTE> - Nome: Carrega Endereo Efetivo Tipo: 8086+ Descrio: Esta instruo carrega o endreo de memria que <FONTE> significa, em <DEST>. EX.: eu uso LEA SI, Str numa das minhas procedures que pes uma string na tela bem rpido. LOOP <LABEL> - Nome: Decrementa CX e Repete

Tipo: 8086+ Descrio: Esta instruo uma forma do loop For...Do que existe na maioria das linguagens de alto nvel. Basicamente ele volta ao label, ou segmento de memria at que CX = 0. EX.: MOV CX, 12 FazAlgumaCoisa: ;... ;... ;... Isto ser repetido 12 vezes LOOP FazAlgumaCoisa Lseg <DEST>, <FONTE> - Nome: Carega Registrador de Segmento Tipo: 8086+ Descrio: Esta instruo existe de vrias formas. Todas aceitam mesma sintaxe, em que <FONTE> especifica um ponteiro de 48 bits, consistindo de um offset de 32 bits e um seletor de 16 bit. O offset de 32 bis caregado em <DEST>, e o seletor carregado no registrador de segmento especificado por seg. As seguintes formas existem: LDS LES LFS LGS LSS

* 32 bits * 32 bits

EX.: LES SI, Um_Ponteiro MOV <DEST>, <FONTE> Tipo: 8086+ - Nome: Move Dados

Descrio: Esta instruo copia

<FONTE> em <DEST>. EX.: MOV AX, 3Eh MOV SI, 12h MUL <FONTE> - Nome: Multiplicao sem Sinal Tipo: 8086+ Descrio: Esta instruo multiplica <FONTE> pelo acumulador, que depende do tamanho de <FONTE>. Se <FONTE> um byte ento: * AL o multiplicando; * AX o produto. Se <FONTE> uma word ento: * AX o multiplicando; * DX:AX o produto. Se <FONTE> uma doubleword ento: * EAX o multiplicando; * EDX:EAX o produto. OBS.: Os flags so inalterados excetos para OF e CF, que so zerados se o byte alto, word ou dword do produto for 0. EX.: MOV AL, 3 MUL 10 MOV Resultado, AX NEG <VALOR> - Nome: Nega Tipo: 8086+ Descrio: Esta instruo subtrai <VALOR> de 0, resultando na negao do complemento a dois de <VALOR>. EX.: MOV AX, 03h

NEG AX NOT <VALOR>

; AX = -3

- Nome: Complemento Lgico Tipo: 8086+ Descrio: Esta instruo inverte o estado de cada bit no operando. EX.: NOT CX

OR <DEST>, <FONTE> - Nome: OU Lgico Tipo: 8086+ Descrio: Esta instruo realiza uma operao de OU booleano entre cada bit de <DEST> e <FONTE>, guardando o resultado em <DEST>. EX.: OR 0, 0 = 0 OR 0, 1 = 1 OR 1, 0 = 1 OR 1, 1 = 1 OUT <PORTA>, <ACUMULADOR> Tipo: 8086+ - Nome: Sada para a Porta

Descrio: Esta instruo manda para a sada o valor do acumulador para <PORTA>. Usando o registrador DX para pasar a porta OUT, voc pode acessar 65,536 portas. EX.: MOV DX, 378h OUT DX, AX POP <REGISTRADOR> - Nome: Carega Registrador da pilha Tipo: 8086+ Descrio: Esta instruo pega o valor atual do topo da pilha e coloca no <REGISTRADOR>. EX.: POP AX

POPA

- Nome: Pega Todos os Registradores Gerais Tipo: 80186+ Descrio: Esta instruo pega todos os registradores de uso geral de 16 bits da pilha, exceto SP. o mesmo que: POP AX POP BX POP CX ... EX.: POPA

POPF

- Nome: Pega o valor do topo para Flags Tipo: 8086+ Descrio: Esta instruo pega o byte baixo dos flags da pilha. EX.: POPF

PUSH <REGISTRADOR> Tipo: 8086+

- Nome: Pe na Pilha o Registrador

Descrio: Esta instruo pe <REGISTRADOR> na pilha. EX.: PUSH AX PUSHA - Nome: Pe todos os registradores na pilha Tipo: 80186+ Descrio: Esta instruo pe todos os registradores de uso geral de 16 bits na pilha. o mesmo que:

PUSH AX PUSH BX PUSH CX ... EX.: PUSHA PUSHF - Nome: Pe flags na pilha Tipo: 8086+ Descrio: Esta instruo pe o byte baixo dos flags na pilha. EX.: PUSHF REP - Nome: Sufixo de Repetio Tipo: 8086+ Descrio: Esta instruo repetir a intruo seguinte o nmero de vezes especificado em CX. EX.: MOV CX, 6 REP STOSB ; Armazena 6 bytes RET - Nome: Retorno de Subrotina (Near/prximo) Tipo: 8086+ Descrio: Esta instruo retorna IP ao valor que ele tinha antes da ltima instruo CALL. RET, ou RETF para um jump distante (far), deve ser chamado quando se usa assembler puro. EX.: RET ROL <DEST>, <VALOR> - Nome: Roda esquerda Type: 8086+ Descrio: Esta instruo roda <DEST> <VALOR> vezes. Uma rodada realizada shiftando <DEST> uma vez, ento

transfere-se o bit que saiu para a posio de mais baixa ordem de <DEST>. EX.: ROL AX, 3 ROR <DEST>, <VALOR> Tipo: 8086+ - Nome: Roda Direita

Descrio: Esta instruo roda <DEST> <VALOR> vezes. Uma rodada realizada shiftando <DEST> uma vez, ento transfere-se o bit que saiu para a posio de mais alta ordem de <DEST>. EX.: ROR BX, 5 SAHF - Nome: Armazena AH nos Flags Tipo: 8086+ Descrio: Esta instruo carrega o contedo do registrador AH nos bits 7, 6, 4, 2 e 0 do registrador de flags. EX.: SAHF SBB <DEST>, <FONTE> - Nome: Subtrai com "pede-emprestado" Tipo: 8086+ Descrio: Esta instruo subtrai <FONTE> de <DEST>, e decrementa <DEST> de uma unidade de o flag de vai-um estiver setado, armazenando o resultado em <DEST>. Basicamemte, <DEST> = <DEST> - <FONTE> - CF EX.: SBB AX, BX SHL <DEST>, <VALOR> - Nome: Shift esquerda Tipo: 8086+ Descrio: Esta instruo desloca <DEST>

esquerda de <VALUE> unidades. Eu no vou entrar em detalhes sobre a teoria disso de novo. Se voc no tem certeza do que esta instruo faz, por favor, leia o Tutorial Quatro. EX.: SHL AX, 5 SHR <DEST>, <VALOR> - Name: Shift direita Type: 8086+ Descrio: Esta instruo desloca <DEST> direita de <VALUE> unidades. Por favor veja o Tutorial Quatro para a teoria dos shifts. EX.: SHR DX, 1 STC - Nome: Seta o flag de vai-um (Carry Flag) Tipo: 8086+ Descrio: Esta instruo seta o valor do carry flag para um. EX.: STC STD - Nome: Seta o flag de direo Tipo: 8086+ Descrio: Esta instruo seta o valor do flag de direo para um. Isto instrui a todas operaes a decrementar os registradores de ndice. EX.: STD REP STOSB ; DI est sendo ; decrementado STI - Nome: Seta Flag de Interrupo Tipo: 8086+

Descrio: Esta instruo seta o valor do flag de interrupo para um, assim permitindo que interrupes de hardware ocorram. EX.: CLI ; Pra interrupes ... ; Realiza uma funo crucial STI ; Habilita interrupes STOS - Nome: Armazena String Tipo: 8086+ Descrio: Esta instruo existe nas seguintes formas: STOSB STOSW STOSD - Armazena um byte - AL - Armazena uma word - AX - Armazena uma doubleword - EAX

As instrues escrevem o contedo atual do acumulador para a posio de memria apontada por ES:DI. Ento ela incrementa ou decrementa DI de acordo com o operando usado, e o valor do flag de direo. Ex.: MOV AX, 0A000h MOV ES, AX MOV AL, 03h MOV DI, 0 STOSB ; Armazena 03 em ES:DI, ; que o topo da tela ; em modo 13h SUB <DEST>, <FONTE> - Nome: Subtrao Tipo: 8086+ Descrio: Esta instruo subtrai <FONTE> de <DEST>, armazenando o resultado em <DEST>. EX.: SUB ECX, 12 TEST <DEST>, <FONTE> - Nome: Testa Bits

Tipo: 8086+ Descrio: Esta instruo realiza uma operao AND bit-a-bit em <FONTE> e <DEST>. O resultado refflete nos flags, e eles so so setados como se fizssemos um AND. EX.: TEST AL, 0Fh ; Checa se algum ; bits est setado ; no mais baixo ; nibble de AL XCHG <VALOR1>, <VALOR2> Tipo: 8086+ - Nome: Troca

Descrio: Esta instruo troca os valores de <VALOR1> e <VALOR2>. EX.: XCHG AX, BX XOR <DEST>, <FONTE> - Nome: OU Exclusivo Lgico Tipo: 8086+ Descrio: Esta instruo realiza um OU exclusivo bit-a-bit em <FONTE> e <DEST>. A operao definida como segue: XOR XOR XOR XOR 0, 0 0, 1 1, 0 1, 1 =0 =1 =1 =0

EX.: XOR AX, BX

No se preocupe com isso por enquanto. Vamos continuar normalmente, e introduzir as novas instrues acima uma por uma, explicando-as quando o fizermos. Se voc j as entende, isso um bnus. Voc vai notar que havia muitas instrues acima, do 8086. H, na verdade, poucos casos em que necessrio usar uma instruo do 386 ou 486, muito menos do Pentium.

De qualquer modo, antes de avanar com a VGA, eu vou s listar a velocidade em que cada instruo acima executada, assim voc pode usar isso para ver como as rotinas em Assembler so rpidas.

Instruo

Clocks no 386 2 2 2 3 7+m 3 2 2 5 2 2 2 2 9-14 9-22 9-38 12/13 2 depende 7+m 3 7+m 2 2 11 7 2 9-14 9-22 9-38 2 2 2 1 1 1 3 3 3 2 2 3 2 1 3 1 -

Clocks no 486

ADC ADD AND BT CALL CBW CLC CLD CLI CMC CMP CWD DEC DIV - Byte - Word - DWord IN INC INT Jcc - Em loop - No loop JMP LAHF LEA LOOP Lseg MOV MUL - Byte - Word - DWord NEG NOT OR

13-18 13-26 13-42 14 1 depende 3 1 3 3 1 6 6 1 13-18 13-26 13-42 1 1 1

OUT POP POPA POPF PUSH PUSHA PUSHF REP RET ROL ROR SAHF SBB SHL SHR STC STD STI STOS SUB TEST XCHG XOR

10/11 4 24 5 2 18 4 depende 10+m 3 3 3 2 3 3 2 2 3 4 2 2 3 2

16 1 9 9 1 11 4 depende 5 3 3 2 1 3 3 2 2 5 5 1 1 3 1

Obs.: m = Nmero de componentes na prxima instruo executada.

Voc provavlmente j notou que sua placa de vdeo tem mais que 256K de RAM. (se no tem, ento estes tutoriais no so provavelmente para voc.) Mesmo que voc tenha s 256K de RAM, como meu velho 386, voc ainda capaz de entrar no modo 13h - 320x200x256. Porm, isto levanta algumas quetes. Multiplique 320 por 200 e voc vai notar que voc s precisa de 64,000 bytes de memria para armazenar uma tela simples. (A VGA na verdade nos d 64K, que 65,536 bytes para quem no sabe.) O que aconteceu com os restantes 192K? Bem, a VGA na verdade arrumada em planos de bits, como isso: 64,000 Cada plano sendo de 64,000 bytes. Aqui est como isso funciona: Um pixel at 0, 0 mapeado no plano 0 no offset 0; Um pixel at 1, 0 mapeado no plano 1 no offset 0;

Um pixel at 2, 0 mapeado no plano 2 no offset 0; Um pixel at 3, 0 mapeado no plano 3 no offset 0; Um pixel at 4, 0 mapeado no plano 0 no offset 1 ... e assim por diante... Por causa de os pixels serem encadeados atravs dos 4 planos, impossvel usar mltiplas pginas no modo 13h sem ter que to usar uma tela virtual, ou algo do tipo. O mapeamento automtico dos pixels feito todo pela placa de vdeo, de modo que voc pode trabalhar de olhos fechados sem saber dos 4 planos de bits se voc quiser. Vamos ver como voc pode contornar essa situao, entrando num modo especial, conhecido como Modo X, mais tarde, mas por enquanto, vamos s ver o que podemos fazer no velho modo 13h.

DESENHANDO LINHAS

Passamos um pouco da tamanho do tamanho que eu tinha planejado para este tutorial, e eu pretendo falar do Algoritmo de Retas de Bresenham, mas isso vai ter que esperar a semana que vem. No entanto, vou cobrir como desenhar um linha reta horzontal simples em Assembler. Um Rotina em Assembler para Retas Horizontais: -----------------------------------------------Primeiramente vamos precisar de apontar ES para a VGA. Isso deve resolver: MOV AX, 0A000h MOV ES, AX Agora, precisaremos de ler os valores de X1, X2 e Y nos registradores, ento algo assim deveria funcionar: MOV AX, X1 ; AX igual ao valor X1 agora MOV BX, Y ; BX igual ao valor Y agora MOV CX, X2 ; CX igual ao valor X2 agora Ser preciso calcular o tamanho da linha, ento vamos usar CX para

guardar isso, sendo que: i) CX j tem o valor de X2, e ii) vamos usar uma instruo REP, que usar CX como contador. SUB CX, AX ; CX = X2 - X1 Agora vamos precisar de calcular o DI para o primeiro pixel que plotaremos, ento vamos fazer o que fizemos na rotina de PutPixel: MOV MOV SHL SHL ADD ADD DI, AX ; DI = X1 DX, BX ; DX = Y BX, 8 ; Shift Y esquerda 8 DX, 6 ; Shift Y esquerda 6 DX, BX ; DX = Y SHL 8 + Y SHL 6 DI, DX ; DI = Y x 320 + X

Temos o offset do primeiro pixel agora, ento tudo o que temos que fazer colocar a cor que queremos desenhar em AL, e usar STOSB para plotar o resto da linha. MOV AL, Cor ; Move a cor a plotar em AL REP STOSB ; Plota CX pixels Note que usamos STOSB porque ele vai incrementar DI para ns, assim economizando um monte de MOV's e INC's. Agora, dependendo de que linguagem voc vai usar para implementar, voc vai chegar a algo assim: void Draw_Horizontal_Line(int x1, int x2, int y, unsigned char color) { _asm { mov ax, 0A000h mov es, ax ; Aponta ES para a VGA mov ax, x1 mov bx, y mov cx, x2 sub cx, ax mov di, ax mov dx, bx shl bx, 8 shl dx, 6 add dx, bx add di, dx ; AX = X1 ; BX = Y ; CX = X2 ; CX = Diferena de X2 e X1 ; DI = X1 ; DX = Y ; Y SHL 8 ; Y SHL 6 ; DX = Y SHL 8 + Y SHL 6 ; DI = Offset do primeiro pixel

mov al, color ; Pe a cor a plotar em AL rep stosb ; Desenha a linha } }

A rotina acima no cegamente rpida, mas no de toda m. S de mudar o clculo da parte de DI como na PutPixel que eu dei no Tutorial Dois, j dobraria a velocidade desta rotina. Minha prpria rotina de linha horizontal provavelmente cerca de 4 a 5 vezes mais rpida que esta, assim, no futuro, eu vou lhe mostrar como otimizar essa rotina por completo. Semana que vem vamos ver como pegar e acertar a palette, e como podemos desenhar crculos. Sinto muito se no fiz isso nesse tutorial, mas ele cresceu um pouco demais... COISAS PARA FAZER: -------------------1) Escreva um rotina para linhas verticais baseada na rotina acima. Dica: Voc precisa incrementar DI de 320 em algum lugar. 2) Volte lista das instrues de Assembler, e aprenda quantas puder. 3) D uma olhda no Starfield, e tente corrigir os bugs dele. Veja o que voc pode fazer com ele.

Semana passada ns chegamos seguinte rotina de linhas horizontais mov ax, 0A000h mov es, ax ; Aponta ES para a VGA mov ax, x1 mov bx, y mov cx, x2 sub cx, ax mov di, ax mov dx, bx shl bx, 8 ; AX = X1 ; BX = Y ; CX = X2 ; CX = Diferena de X2 e X1 ; DI = X1 ; DX = Y ; Y SHL 8

shl dx, 6 add dx, bx add di, dx

; Y SHL 6 ; DX = Y SHL 8 + Y SHL 6 ; DI = Offset do primeiro pixel

mov al, color ; Pe a cor a plotar em AL rep stosb ; Desenha a linha Agora, embora essa rotina seja muito mais rpida que as rotinas do BGI, (ou seja l o que for que seu compilador tenha), ela poderia ser melhorada pra caramba. Se entrarmos na rotina, com a lista de clocks que eu dei no ltimo tutorial, voc vai ver que ela gasta bem pouco. Eu vou deixar a otimizao com voc por enquanto, (vamos ver isso em outro tutorial), mas se substituir STOSB por MOV ES:[DI], AL ou STOSW vai melhorar muito as coisas. No se esquea que se voc decidir usar um loop, para jogar words na VGA, voc ter que decrementar CX de uma unidade. Agora, vamos ver uma linha vertical. Teremos que calcular o offset do primeiro pixel como ns fizemos na rotina de linhas horizontais, ento, algo desse tipo funcionaria: mov ax, 0A000h ; Pe o segmento VGA em AX mov es, ax ; Aponta ES para a VGA mov ax, Y1 shl ax, 6 mov di, ax shl ax, 2 add di, ax add di, X ; Move o primeiro valor de Y para AX ; Y x 2^^6 (dois sexta potncia) ; Move o novo valor de Y para DI ; Agora temos Y = Y x 320 ; Adiciona aquele valor a DI ; Soma o valor de X a DI

Agora umas coisas bsicas... mov cx, Y2 mov al, Color sub cx, Y1 ; Guarda Y2 em CX ; Guarda a cor a plotar em AL ; CX = tamanho da linha

E agora o loop final... Plota: mov es:[di], al

; Pe um pixel no offset corrente

add di, 320 dec cx jnz Plota

; Move para a prxima linha ; Decrementa CX de um ; Se CX <> 0, ento continua plotando

No uma rotina fantstica, mas muito boa. Note como foi possvel realizar uma comparao depois de DEC CX. Isto um conceito extremamente til, logo, no se esquea de que isso possvel. Brinque um pouco com o cdigo, e tente faz-lo mais rpido. Tente outros mtodos de calcular o offset, ou mtodos diferentes de controle de fluxo. Agora, isso foi a coisa fcil. Vamos ver agora uma rotina capaz de desenhar linhas diagonais. A seguinte rotina foi tirada de SWAG, autor desconhecido, e uma rotina ideal para demonstrar um algoritmo de linhas. Ele est precisando muito de uma otimizao, assim, essa pode ser uma tarefa para voc - se voc quiser. Alguns dos pontos a considerar so: 1) Seja l quem o escreveu nunca ouviu falar de XCHG - isso economizaria alguns clocks; 2) Ele comete um dos grandes pecados do cdigo no-otimizado - ele move um valor para AX, e ento realiza uma operao envolvendo AX na prxima instruo, assim fazendo um ciclo a mais. (Vamos falar sobre isso semana que vem). 3) Ele trabalha com BYTES e no WORDS, assim, a velocidade de escrita para a VGA poderia se dobrada se usasse words. 4) E o maior pecado de todos, ele usa um MUL para achar o offset. Tente usar shifts ou um XCHG para acelerar as coisas. De qualquer modo, eu pus os comentrios nele, e acho que ele auto-explicativo, assim, eu no vou entrar em detalhes como ele funciona. Voc deve ser capaz de pegar isso sozinho. Adentre a rotina, e veja como a derivada (gradiente, variao, inclinao...) da linha foi calculada. Procedure Line(X1, Y1, X2, Y2 : Word; Color : Byte); Assembler; Var DeX DeY : Integer; : Integer;

IncF

: Integer; } (X2 - X1) } }

Asm { Line } mov ax, [X2] { Move X2 para AX sub ax, [X1] { Pega o tamanho horizontal da linha jnc @Dont1 { X2 - X1 negativo? neg ax { Sim, ento faz com que seja positivo

@Dont1: mov [DeX], ax { Agora, move o tamanho horizontal da linha para DeX } mov ax, [Y2] { Move Y2 para AX } sub ax, [Y1] { Subtrai Y1 de Y2, dando o tamanho vertical } jnc @Dont2 { Foi negativo? } neg ax { Sim, ento faa-o positivo } @Dont2: mov [DeY], ax { Move o tamanho vertica para DeY } cmp ax, [DeX] { Compara o tamanho vertivcal com o horizontal jbe @OtherLine { Se o vertical foi <= horizontal ento pula } mov ax, [Y1] { Move Y1 para AX } cmp ax, [Y2] { Compara Y1 a Y2 } jbe @DontSwap1 { Se Y1 <= Y2 ento pula, seno... mov bx, [Y2] { Pe Y2 em BX } mov [Y1], bx { Pe Y2 em Y1 } mov [Y2], ax { Move Y1 para Y2 } { Para que depois de tudo isso..... } { Y1 = Y2 e Y2 = Y1 } mov mov mov mov ax, [X1] bx, [X2] [X1], bx [X2], ax { Pe X1 em AX { Pe X2 em BX { Pe X2 em X1 { Pe X1 em X2 } } } } } } } }

@DontSwap1: mov [IncF], 1 { Pe 1 em IncF, i.e., plota outro pixel mov ax, [X1] { Pe X1 em AX cmp ax, [X2] { Compara X1 com X2 jbe @SkipNegate1 { Se X1 <= X2 ento pula, seno... neg [IncF] { Nega IncF } @SkipNegate1: mov ax, [Y1] { Move Y1 para AX mov bx, 320 { Move 320 para BX mul bx { Multiplica 320 por Y1 mov di, ax { Pe o resultado em DI

} } } }

add mov mov mov mov mov mov

di, [X1] { Soma X1 a DI, e tcham - offset em DI bx, [DeY] { Pe DeY em BX } cx, bx { Pe DeY em CX } ax, 0A000h { Pe o segmento a ser plotado, em AX es, ax { ES aponta para a VGA } dl, [Color] { Pe a cor a usar em DL } si, [DeX] { Aponta SI para DeX }

} }

@DrawLoop1: mov es:[di], dl { Pe a cor a plotar, DL, em ES:DI } add di, 320 { Soma 320 a DI, i.e., prxima linha abaixo } sub bx, si { Subtrai DeX de BX, DeY } jnc @GoOn1 { Ficou negativo? } add bx, [DeY] { Sim, ento soma DeY a BX } add di, [IncF] { Soma a quantidade a incrementar a DI } @GoOn1: loop @DrawLoop1 { Nenhum resultado negativo, ento plota outro pixel } jmp @ExitLine { Acabou, ento vamos embora! } @OtherLine: mov ax, [X1] { Move X1 para AX cmp ax, [X2] { Compara X1 a X2 jbe @DontSwap2 { X1 <= X2 ? mov bx, [X2] { No, ento move X2 para BX mov [X1], bx { Move X2 para X1 mov [X2], ax { Move X1 para X2 mov ax, [Y1] { Move Y1 para AX mov bx, [Y2] { Move Y2 para BX mov [Y1], bx { Move Y2 para Y1 mov [Y2], ax { Move Y1 para Y2 } } } } } } } } } }

@DontSwap2: mov [IncF], 320 { Move 320 para IncF, i.e., o prximo pixel est na } { prxima linha } mov ax, [Y1] { Move Y1 para AX } cmp ax, [Y2] { Compara Y1 a Y2 } jbe @SkipNegate2 { Y1 <= Y2 ? } neg [IncF] { No, ento nega IncF } @SkipNegate2: mov ax, [Y1] { Move Y1 para AX mov bx, 320 { Move 320 para BX mul bx { Multiplica AX por 320 mov di, ax { Move o resultado para DI add di, [X1] { Soma X1 a DI, dando o offset } } } } }

mov mov mov mov mov mov

bx, [DeX] { Move DeX para BX cx, bx { Move BX para CX ax, 0A000h { Move o endereo da VGA para AX es, ax { Aponta ES para a VGA dl, [Color] { Move a cor a plotar para DL si, [DeY] { Move DeY para SI

} } } } } } } } }

@DrawLoop2: mov es:[di], dl { Pe o byte em DL para ES:DI inc di { Incrementa DI de um, o prximo pixel sub bx, si { Subtrai SI de BX } jnc @GoOn2 { Ficou negativo? } add bx, [DeX] { Sim, ento soma DeX a BX add di, [IncF] { Soma IncF a DI } @GoOn2: loop @DrawLoop2 @ExitLine: { Pronto! End; } { Continua plotando

Acho que no fiz nenhum erro com os comentrios, mas eu estou bem cansado, e no tenho bebido cafena h dias - se voc encontrar um erro - por favor me diga.

OS INS E OUTS DE IN E OUT

IN e OUT so uma parte muito importante de cdigo em Assembler. Elas permitem voc a mandar/receber diretamente dados de qualquer uma das 65,536 portas de hardware, ou registradores. A sintaxe bsica como segue: IN <ACUMULADOR>, <PORTA> Tipo: 8086+ - Nome: Entrada de porta de E/S

Descrio: Esta instruo l um valor de uma das 65536 portas de hardware para o acumulador especificado.

AX e AL so comumente usados para portas de entrada, e DX mais usado para identificar a porta. EX.: IN AX, 72h MOV DX, 3C7h IN AL, DX OUT <PORTA>, <ACUMULADOR> Tipo: 8086+ - Nome: Sada para a Porta

Descrio: Esta instruo pe na sada o valor no acumulador para <PORTA>. Usando o registrador DX para passar a porta para OUT, voc pode acessar at 65,536 portas. EX.: MOV DX, 378h OUT DX, AX OK, isso no ajudou muito, j que no disse muito sobre como usar - muito menos para que usar. Bem, se voc pretende trabalhar muito com a VGA, voc ter que ser capaz de programar seus registradores internos. Semelhantes aos registradores com que voc tem trabalhado at agora, voc pode pensar em mud-los como interrupes, exceto que: 1) Voc passa os valores para a porta, e isso a; e 2) muito perto de ser instantneo. Como exemplo, vamos ver como setar e pegar a palette controlando diretamente o hardware da VGA. Agora, a VGA tem uma poro de registradores, mas as prximas trs bom que voc conhea bem: 03C7h 03C8h 03C9h - PEL Registrador de Endereos (Leitura) Seta a palette em mode de leitura - PEL Registrador de Endereos (Escrita) Seta a palette em modo de escrita - PEL Registrador de Dados (Leitura/Escrita) L, ou escreve 3 valores RGB, a cada terceira escrita, o ndice, ou cor que voc est setando, incrementado de um.

O que tudo isso significa Se ns fssemos setar um valor RGB de uma cor RGB, ns mandaramos o valor da cor que queramos mudar para 03C8h, ento ler os 3 valores de 03C9h. Em Assembler, faramos isso: mov dx, 03C8h ; Pe o registrador DAC de leitura em DX mov al, [Color] ; Pe o valor da cor em AL out dx, al ; Manda AL para a porta DX inc dx ; Agora usa a porta 03C9h mov al, [R] ; Pe o novo valor VERMELHO em AL out dx, al ; Manda AL para a porta DX mov al, [G] ; Pe o novo valor VERDE em AL out dx, al ; Manda AL para a porta DX mov al, [B] ; Pe o novo valor AZUL em AL out dx, al ; Manda AL para a porta DX E aquilo deveria fazer a coisas direitinho. Para ler a palette, faramos isso: mov dx, 03C7h ; Pe o registrador DAC de escrita em DX mov al, [Color] ; Pe o valor da cor em AL out dx, al ; Manda AL para a porta DX add dx, 2 ; Agora usa a porta 03C9h in al, dx les di, [R] stosb in al, dx les di, [G] stosb in al, dx les di, [B] stosb ; Pe o valor conseguido da porta DX em AL ; Aponta DI para a varivel R - Isso vem do Pascal ; Guarda AL em R ; Pe o valor conseguido da porta DX em AL ; Aponta DI para a varivel G ; Guarda AL em G ; Pe o valor conseguido da porta DX em AL ; Aponta DI para a varivel B ; Guarda AL em B

Note como essa rotina foi codificada diferentemente. Isso era originalmente uma rotina em Pascal, e como o Pascal no gosta que voc mexa com variveis de Pascal em Assembler, voc tem que improvisar. Se voc est trabalhando com Assembler puro, ento voc pode codificar isso muito mais eficientemente, como o primeiro exemplo. Eu deixei o cdigo como estava para que aqueles trabalhando com uma linguagem de alto nvel possam chegar a um problema particularmente irritante. Agora voc j viu como IN e OUT podem ser teis. Controlar diretamente o

hardware mais rpido e mais eficiente. Nas prximas semanas, eu posso incluir uma lista das portas mais comuns, mas se voc tivesse uma cpia da Ralf Brown's Interrupt List (disponvel no X2FTP), voc j teria uma cpia. OBS.: Voc pode achar um link para a Ralf Brown's Interrupt List na minha pgina.

Um pouco mais sobre o registrador de flags: Agora, embora tenhamos usado o registrador de flags em quase todo nosso cdigo at esse ponto, eu no entrei profundamente nesse assunto. Voc pode trabalhar felizmente sem conhecer muito sobre os flags, e comparar coisas sem saber o que est realmente acontecendo, mas se voc quiser avanar no Assembler, voc precisa saber algo mais. De volta ao Tutorial Trs, eu dei uma viso simplista do registrador de FLAGS. Na realidade, os FLAGS, ou EFLAGS na verdade um registrador de 32-bit, embora apenas s os bits de 0 a 18 sejam usados. Na realidade no precisamos conhecer os flags acima do 11 por enquanto, mas bom saber que eles existem. O registrador EFLAGS na verdade se parece com isso: 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 AC VM RF -- NT IO/PL OF DF IF TF SF ZF -- AF -- PF -- CF Agora, os flags so os seguintes: AC - Alignment Check (80486) / Checagem de Alinhamento VM - Virtual 8086 Mode / Modo Virtual 8086 RF - Resume Flag / Flag de Continuao NT - Nested Task Flag / Flag de Tarefa Aninhada IOPL - I/O Privilege Level / Nvel de Privilgio de E/S Tem um valor de 0,1,2 ou 3 logo ocupa 2 bits OF - Overflow Flag / Flag de Overflow Este bit setado para UM se uma instruo aritmtica gerar um resultado que muito grande ou muito pequeno para caber no registrador destino. DF - Direction Flag / Flag de Direo

Qaundo setado para ZERO, as instrues de string, como MOVS, LODS, e STOS incrementaro o endereo de memria que elas esto trabalhando de uma unidade. Isto significa que, digamos, DI ser incrementado quando voc usar STOSB para colocar um pixel em ES:DI. Setando o bit para UM decrementar o endereo de memria aps cada chamada. IF - Interrupt Enable Flag / Flag de Habilitao de Interrupes Quando este bit est setado, o processador responder a interrupes externas do hardware. Quando o bit for resetado, interrupes de hardware so ignoradas. TF - Trap Flag / Flag de Trap ("armadilha") Quando este bit estiver setado, uma interrupo ocorrer imediatamente depois que a prxima instruo executar. Isto geralmente usado em depuraes. SF - Sign Flag / Flag de Sinal Este bit mudado aps operaes aritmticas. O bit recebe o bit de mais alta ordem do resultado, e se setado para UM, indica que o resultado da operao foi negativo. ZF - Zero Flag / Flag de Zero Este bit setado quando instrues aritmticas geram um resultado zero. AF - Auxiliary Carry Flag / Flag de Vai-Um Auxiliar Este bit indica que um vai-um no nibble de baixa ordem de AL ocorreu na instruo aritmtica. PF - Parity Flag / Flag de Paridade Este bit setado para um quando uma instruo aritmtica resulta num nmero par de bits 1. CF - Carry Flag / Flag de Vai-Um Este bit setado quando o resultado de uma operao aritmtica muito grande ou muito pequena para o registrador destino ou endereo de memria. Agora, de todos esses acima, voc no precisa mesmo se preocupar muito com a maioria deles. Por enquanto, s conhecer CF, PF, ZF, IF, DF e OF ser suficiente. Eu no dei comentrios para os primeiros j que eles so puramente tcnicos, e so usados mais no modo protegido e situaes complexas. Voc no deveria ter que conhec-los.

Voc pode, se quiser, mover uma copia do flags para AH com LAHF - (Carrega AH com Flags) - e modificar ou ler bits individualmente, ou mudar o status dos bits mais facilmente com CLx e STx. Contudo se voc planeja mudar os flags, lembre-se de que eles podem ser extremamente teis em muitas situaes. (Eles podem tambm ser muito enjoados quando tarde da noite, linhas comeam a desenhar para trs, e voc gasta um hora procurando o porqu - e ento se lembra que voc se esqueceu de limpar o flag de direo!)

Acho que cobrimos muito pouca coisa importante neste tutorial. D uma olhada nos flags, e volte naquela rotina compridona de fazer linhas, j que ela um timo exemplo de controle de fluxo. Assegure-se de que suas capacidades de controlar fluxos de intrues esto perfeitas.

Quando eu comecei a brincar com Assembler eu logo vi que o Turbo Pascal, (a linguagem com que eu trabalhava at ento), tinha poucas limitaes - uma delas que ela era, e ainda , uma linguagem de 16 bits. Isso significava que se eu quisesse brincar com escritas super-rpidas em 32 bits, eu no poderia. Nem mesmo com seu prprio Assembler (bem, no facilmente). O que eu precisava fazer era escrever cdigo separadamente 100% em Assembler e linkar ao Turbo. Isso no uma tarefa particularmente difcil, e uma das que eu vou tentar ensinar a voc hoje. A outra vantagem de escrever rotinas em Assembler puro que voc tambm pode linkar o cdigo objeto resultante a outra linguagem de alto nvel, como o C. ESCREVENDO CDIGO EXTERNO PARA SUA LINGUAGEM DE ALTO NVEL

Antes de comearmos, voc precisa de uma idia do que so chamadas far e near. Se voc j sabe, ento pule essa pequena seo. Como discutimos antes, o PC tem uma arquitetura segmentada. Como voc sabe, voc s pode acessar um segmento de 64K de cada vez. Agora se voc est trabalhando com cdigo de menos de 64K de tamanho, ou em uma linguagem que cuida de todas as preocupaes para voc, voc no precisa de se preocupar tanto. Contudo, trabalhando em Assembler, precisamos sim. Imagine que tenhamos o seguinte programa caregado na memria:

64K

ROTINA DOIS ROTINA UM

64K Entradada PROGRAMA PRINCIPAL Sada Quando um JMP for executado para transferir o controle para a Rotina Um, esse ser uma chamada near(perto). Ns no deixamos o segmento em que o corpo principal do programa est localizado, e assim quando o JMP ou CALL executado, e CS:IP mudado por JMP, s o IP precisa ser mudado, no CS. O offset muda, mas o segmento no. Agora, pular para a Rotina Dois seria diferente. Isto deixa o segmento corrente, e assim ambas as partes do par CS:IP precisaro de ser alteradas. Isto uma chamada far (longe). O problema ocorre quando a CPU encontra um RET ou RETF no fim da chamada. Digamos que voc, por acidente, colocou RET no fim da Rotina Dois, ao invs de RETF. Como a CPU viu RET, ela s tiraria IP da pilha, e assim, sua mquina travaria, provavelmente, j que CS:IP estaria apontando para um lixo. Este ponto especialmente importante quando for linkar a uma linguagem de alto nvel. Seja l quando for que voc escrever um cdigo em Assembly e linkar, digamos, ao Pascal, lembre-se de usar a diretiva de compilao {$F+}, mesmo se no foi uma chamada FAR. Deste modo, depois de o Turbo chamar a rotina, ele tira CS e IP da pilha, e tudo vai bem. Falhas em fazer isso so problemas seus!

OK, de volta ao modelo em Assembly puro do Tutorial Trs. Eu no me lembro direito, mas eu acho que era alguma coisa assim: DOSSEG .MODEL SMALL .STACK 200h

.DATA .CODE START: END START Agora, acho que hora de vocs pularem um grau no uso daquele esqueleto. Vejamos outros modos de arrumar uma rotina-esqueleto.

DATA DATA

SEGMENT WORD PUBLIC ENDS

CODE SEGMENT WORD PUBLIC ASSUME CS:CODE, DS:DATA CODE END Este , obviamente, um esqueleto diferente. Note como eu omiti o ponto antes de DATA e CODE. Dependendo de que Assembler/Linker voc usar, voc pode precisar do ponto ou no. TASM, o Assembler que eu uso, aceita os dois formatos, ento, pegue um com que voc e seu assembler estejam felizes. Note tambm o uso de DATA SEGMENT WORD PUBLIC. Primeiramente, WORD diz ao Assembler para alinhar o segmento em limites de word. FATO ENGRAADO: Voc no precisa se preocupar com isso por enquanto, pois o Turbo Pascal faz isso de qualquer modo, assim, colocar BYTE ao invs de word no faria diferena nenhuma. :) PUBLIC permite ao compilador que voc usar, acessar quaisquer variveis no segmento de dados. Se voc no quer que seu compilador tenha acesso a qualquer varivel que voc declarar, ento apenas omita isso. Se voc no precisar de acessar o segmento de dados, ento esquea o segmento de dados todo. Agora, o segmento de cdigo. Geralmente, voc vai precisar incluir isso em todo o cdigo que voc escrever. :) A sentena ASSUME tambm ser um padro em tudo que voc vai trabalhar. Voc tambm pode esperar ver CSEG e DSEG ENDS

ao invs de CODE e DATA. Note de novo que ele declarado como PUBLIC. nele que todas as nossas rotinas vo.

Ento, como eu declaro procedures externas? Ok, por exemplo, vamos usar umas poucas rotinas simples similares quelas na biblioteca do modo 13H do PASCAL (disponvel na minha homepage). Se voc se lembrar, a procedure se parece um pouco com isso: Procedure PutPixel(X, Y : Integer; Color : Byte); Procedure InitMCGA; Procedure Init80x25; Ajustando isso no nosso esqueleto, temos: CODE SEGMENT WORD PUBLIC ASSUME CS:CODE DS:DATA PUBLIC PutPixel PUBLIC InitMCGA PUBLIC Init80x25 CODE END Agora, tudo o que temos a fazer codific-los. Mas, espere um minuto - a rotina PutPixel tem PARMETROS! Como us-los em cdigo externo?? Isto um macete. O que fazemos colocar os valores na pilha, simplesmente dizendo -- PutPixel(10,25,15); -- j faz isso para ns. tirar eles de l que o mais difcil. O que eu geralmente fao, e sugiro a vocs fazer, se certificar de DECLARAR TODAS AS PROCEDURES EXTERNAS COMO FAR. Isso faz as coisas trabalharem com a pilha mais fcil. ENDS

FATO ENGRAADO: Lembre que a primeira cois a entrar na pilha a LTIMA A SAIR. :) Quando voc chamar a Putpixel, a pilha estar mudada. Como isso uma chamada FAR, os primeiros 4 bytes so CS:IP. Os bytes da em diante so os nossos parmetros. Para encurtar a histria, digamos que a pilha se parea com isso: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... Depois de chamar -- PutPixel(10, 20, 15); -- um tempo depois, ela pode se parecer com isso: 4C EF 43 12 0F 00 14 00 0A 00 9E F4 3A 23 1E 21 ... ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^ CS:IP Color Y X Algum lixo Agora, para complicar as coisas, a CPU armazena words na pilha com a PARTE MENOS SIGNIFICATIVA PRIMEIRO. Isso no nos atrapalha muito, mas se voc ficar andando por a com um debugger sem saber disso, voc vai ficar mesmo confuso. Note tambm que quando o Turbo Pascal pe um tipo de dado byte na pilha, ele te chupa DOIS BYTES, NO UM. Voc no adora o modo como o PC organizado? ;) Agora, tudo que eu disse at agora s se aos parmetros passados por valor PARMETROS QUE VOC NO PODE MUDAR. Quando voc estiver por a brincando com PARMETROS PASSADOS POR REFERNCIA, como -- MyProc(Var A, B, C : Word); -cada parmetro agora usa QUATRO BYTES de pilha, dois para o segmento e dois para o offset de onde a varivel est na memria. Assim, se voc pegou uma varivel que est, digamos, na posio de memria 4AD8:000Eh, no interessa o valor dela, 4AD8:000Eh seria armazenado na pilha. J que isso acontece, voc ia ver 0E 00 D8 4A na pilha, lembrando que o nibble menos significativo armazenado primeiro. FATO ENGRAADO: Parmetros de Valor pe na verdade o valor na pilha, e Parmetros de Referncia armazenam o endereo. :)

Ok, agora que eu tenho voc realmente confuso, e com razo, isso piora! Para referenciar estes parmetros no seu cdigo, voc tem que usar o ponteiro de pilha, SP. O problema que voc no pode brincar com SP diretamente, voc tem que botar BP na pilha, e mover SP para ele. Isso agora coloca mais dois bytes na pilha. Digamos que BP era igual a 0A45h. Antes de colocar BP, a pilha era assim: 4C EF 43 12 0F 00 14 00 0A 00 ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^ CS:IP Color Y X Depois, ela ficou assim: 45 0A 4C EF 43 12 0F 00 14 00 0A 00 ^^^^ ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^ BP CS:IP Color Y X Agora que ns passamos por isso tudo, podemos realmente acessar essas as porcarias! O que voc faria depois de chamar -- PutPixel(10, 20, 15); -para acessar o valor de Color isto: PUSH BP MOV BP, SP MOV AX, [BP+6] ; Agora temos Color Podemos acessar X e Y assim: MOV BX, [BP+8] ; Agora temos Y MOV CX, [BP+10] ; Agora temos X E agora restauramos BP: POP BP Agora, retornamos de uma chamada FAR, e removemos os seis bytes de dados que pusemos na pilha:

RETF 6 E s isso!

Agora vamos por a PutPixel, InitMCGS e Init80x25 em cdigo Assembler. Voc obtm algo assim: CODE SEGMENT WORD PUBLIC ASSUME CS:CODE DS:DATA PUBLIC PutPixel PUBLIC InitMCGA PUBLIC Init80x25 .386 ; Declara as procedures pblicas

; Vamos usar alguns registradores de 386

Procedure PutPixel(X, Y : Integer; Color : Byte)

PutPixel PROC FAR PUSH BP MOV BP, SP

; Declara uma procedure FAR ; Arruma a pilha

MOV BX, [BP+10] ; BX = X MOV DX, [BP+08] ; DX = Y XCHG DH, DL ; Como Y sempre ter um valor menor quee 200, MOV AL, [BP+06] ; isto 320x200, no se esquea, dizer XCHG DH,DL MOV DI, DX ; um modo genial de dizer SHL DX, 8 SHR DI, 2 ADD DI, DX ADD DI, BX ; Agora temos o offset, ento... MOV FS:[DI], AL ; ...plote em FS:DI POP BP RETF 6 PutPixel ENDP

Procedure InitMCGA InitMCGA PROC FAR MOV AX, 0A000H ; Aponta AX para a VGA MOV FS, AX ; Porque no FS? MOV AH, 00H MOV AL, 13H INT 10H RETF InitMCGA ENDP

Procedure Init80x25 Init80x25 PROC FAR MOV AH, 00H MOV AL, 03H INT 10H RETF Init80x25 ENDP CODE ENDS END

E s. Desculpe-me se eu fiz a coisa toda um pouco confusa, mas essa a graa dos computadores! :) Ah! A propsito, voc pode usar o cdigo acima em Pascal, assemblando-o com TASM ou MASM. Depois, inclua no seu cdigo isso: {$L SEJALDOQUEVOCCHAMOU.OBJ} {$F+} Procedure PutPixel(X, Y : Integer; Color : Byte); External; Procedure InitMCGA; External;

Procedure Init80x25; {$F-} Begin InitMCGA; PutPixel(100, 100, 100); ReadLn; Init80x25; End.

External;

FUNES E OTIMIZAES POSTERIORES

Voc pode fazer suas rotinas Assembler retornarem valores que voc pode usar em sua linguagem de alto-nvel, se voc quiser. A tabela abaixo contm toda a informao que voc precisa saber. Tipo a Retornar Registrador(es)a Usar Byte AL Word AX LongInt DX:AX Pointer DX:AX Real DX:BX:AX Agora que voc j viu como escrever cdigo externo, voc provavelmente quer saber como melhor-lo para obter a performance total que o cdigo externo pode oferecer. Alguns pontos para voc trabalhar se seguem: No se pode trabalhar com SP diretamente, mas voc pode usar ESP. Isso vai acabar com a lentido de empilhar/desempilhar BP. Lembre-se de mudar [xx+6] para [xx+4] para o ltimo (primeiro) parmetro, j que BP no est mais na pilha. Gaste um tempo e veja o que voc pode fazer com isso. possvel atravs de melhorias, fazer um cdigo mais rpido que a rotina no MODE13H.ZIP verso 1 (disponvel na minha homepage).

Nota: Eu planejo mais pra frente desenvolver a biblioteca MODE13H, adicionando fontes e outras coisas legais. Ela ser eventualmente codificada s em Assembler, e poder ser chamada do C ou Pascal. Cdigo puro em Assembler tambm tem um grande aumento de velocidade. Hoje eu testei a rotina PutPixel da biblioteca MODE13H e uma pura (praticamente idntica), e vi uma diferena espantosa. Num 486SX-25 com 4Mb de RAm e uma placa VGA de 16 bits, levou 5 centssimos de segundo para a rotina pura desenhar 65536 pixels no meio da tela, contra 31 centsimos de segundo da outra. Grande diferena, no?

OTIMIZAO Por mais rpido que o Assembler seja, voc sempre pode acelerar as coisas. Eu vou falar como acelerar seu cdigo no 80486, e no 80386. Eu no vou me preocupar muito com o Pentium por enquanto, j que os truques de uso do Pentium so realmente truques, e demoraria um pouco para explicar. Tambm, voc deveria evitar cdigo especfico para Pentium (embora isso esteja mudando lentamente).

A AGI (Address Generation Interlock): Que merda essa?, voc pergunta. Uma AGI ocorre quando uma registrador que est correntemente sendo usado como base ou ndice foi o destino da ltima instruo. AGI's so ruins, e chupam clocks. EX.: MOV ECX, 3 MOV FS, ECX Isso pode ser evitado executando-se uma outra instruo entre os dois MOV's, pois AGI's podem ocorrer s entre instrues adjacentes (no 486). No Pentium, uma AGI pode acontecer at entre 3 instrues!

Use Instrues/Registradores de 32 bits: Usar registradores de 32 bits tende a ser mais rpido que usar seus equivalentes de 16 (particularmente EAX, j que muitas instrues ficam um byte menor quando ele usado. Usar DS ao invs de ES tambm mais rpido pelo mesmo motivo). Outras coisas para se tentar: Evite LOOP's. tente usar um DEC, ou INC seguido de um JZ ou instruo similar. Isso pode fazer uma diferena enorme. Quando for zerar registradores, use XOR ao invs de MOV xx, 0. acredite ou no, mesmo mais rpido. Use o TEST quando for checar se um registrador igual a zero. Em se fazer um AND dos operandos juntos no se gasta tempo com um registrador destino. TEST EAX,EAX um bom mode de checar de EAX=0. USE SHIFTS! No use multiplicao para calcular mesmo as mais simples somas. A CPU pode mover uns poucos zeros para a esquerda ou para a direita muito mais rpido que ela pode fazer uma multiplicao/diviso. Faa uso da LEA. Uma instruo tudo o que leva para realizar uma multiplicao inteira e armazenar o resultado num registrador. Esta uma alternativa til para SHL/SHR (eu sei, eu sei... eu disse que a multiplicao era ruim. Mas uma LEA s vezes pode ser til, j que pode economizar vrias instrues.). EX.: LEA ECX, [EDX+EDX*4] ; ECX = EDX x 5 Evite fazer MOV's para registradores de segmento muito frequentemente. Se voc vai trabalhar com um valor que no muda, tal como A000h, ento carregue-o em FS, por exemplo, e use FS da em diante. Acredite ou no, instrues de string (LODSx, MOVSx, STOSx))so muito mais rpidas num 386 que num 486. Se estiver trabalhando num 486 ou mais, ento use outra instruo, mais simples. Quando for mover pedaos de 32 bits, REP STOSD mais rpido que usar um loop para fazer a mesma coisa.

Bem, agora voc j viu como escrever cdigo externo, declarar procedures em Assembler e otimizar suas rotinas.

ESTRUTURAS DE DADOS EM ASSEMBLER Bem, at agora voc deveria saber que voc pode usar o DB, (Declare Byte) e DW, (Declare Word) para criar variveis. Porm, at agora ns os temos usado como voc usaria a declarao de Const em Pascal. Quer dizer, temos usado isto para dar a um byte ou a uma word um valor. Ex.: MyByte DB 10 -- que o mesmo que -- Const MyByte : Byte = 10; Contudo, poderamos dizer: MyByte DB ? ...e ento dizer depois: MOV MyByte, 10 De fato DB realmente muito poderoso. H vrios tutoriais atrs, quando voc estava aprendendo a escrever strings na tela, voc viu algo desse tipo: MyString DB 10, 13 "This is a string$" Agora, o mais curioso de vocs provavelmente teria dito a si prrpio: "Pera!... aquele cara do tutorial disse que DB declara um BYTE. Como que o DB pode declarar uma string, ento "? Bem, DB tem a habilidade de reservar espao para valores de vrios bytes - de 1 a tantos bytes quanto voc precisa. Voc tambm pode ter desejado saber o que os nmeros 10 e 13 antes do texto representavam. Bem, d uma olhada na sua tabela ASCII e veja o que so o 10 e

o 13. Voc notar que 10 o Line Feed e o 13 o Carriage Return. Basicamente, o mesmo que dizer: MyString := #10 + #13 + 'This is a string'; em Pascal.

Ok, ento voc viu como criar variveis corretamente. Mas, e constantes? Bem, em Assembler, constantes so conhecidas como Equates. Equates fazem a cofificao em Assembler muito mais fcil, e pode simplificar muito as coisas. Por exemplo, se eu tivesse usado o seguinte em tutoriais anteriores: LF EQU 10 CR EQU 13 DB LF, CR "Isso uma string$" ...as pessoas teriam entendido direito aquela coisa de 10 e 13. Mas, para fazer as coisas um pouco mais complicadas, h ainda um outro modo que voc pode usar para dar valores a identificadores. Voc pode fazer como voc faria em BASIC: Population = 4Ch Magnitude = 0 Basicamente, voc pode ter em mente os seguintes pontos: Uma vez que voc tenha usado EQU para dar um valor a um identificador, voc no pode mudar isto. EQU pode ser usado para definir quase qualquer tipo - inclusive strings. Contudo, voc no pode fazer isto quando se usa um ' = '. Um ' = ' s pode definir valores numricos. Voc pode usar EQU quase em qualquer lugar de seu programa. Valores definidos com ' = ' podem ser mudados.

E agora, vamos a um dos pontos mais macetosos de codificao em Assembler estruturas. Estruturas no so variveis, so um TIPO - basicamente um esquema

de uma varivel. Como um exemplo, se voc tivesse o seguinte em Pascal: Type Date = Record; Day : Byte; Month : Byte; Year : Word; End; { Record } Voc poderia representar isto em Assembler como segue: Date Day Month Year Date STRUC DB ? DB ? DW ? ENDS

Porm, um das vantagens de Assembler que voc pode inicializar todos ou alguns dos campos da estrutura antes mesmo de voc se referir estrutura em seu segmento de cdigo. Aquela estrutura acima poderia ser escrita facilmente como: Date Day Month Year Date STRUC DB ? DB 6 DW 1996 ENDS

Alguns pontos importantes para se lembrar so os seguintes: Voc pode declarar uma estrutura em qualquer lugar em seu cdigo, embora que, para um bom design, voc deva coloc-los no segmento de dados, a menos que eles s sejam usados por uma subrotina. Definir uma estrutura no reserva qualquer byte de memria para a mesma. Isso s acontece quando voc declara uma varivel dessa estrutura - a a memria alocada.

REFERENCIANDO ESTRUTURAS DE DADOS EM ASSEMBLER

Bem, voc viu como definir estruturas, mas como voc se refere de verdade a elas em seu cdigo? Tudo o que voc tem a fazer, colocar em algum lugar algumas linhas como as seguintes em seu programa - de preferncia no segmento de dados. Date Day Month Year Date STRUC DB 19 DB 6 DW 1996 ENDS

Date_I_Passed_Physics Date <> ; Espero! Neste momento, Date_I_Passed_Physics tem todos os seus trs campos preenchidos. Dia setado para 19, Ms para 6 e Ano para 1996. Agora, o que so esses parnteses,"<>", fazendo depois de data? - voc pergunta. Os parnteses nos apresentam um outro modo de alterar os contedos dos campos da varivel. Se eu tivesse escrito isto: Date_I_Passed_Physics Date <10,10,1900> ...ento os campos teriam sido mudados para os valores nos parnteses. Alternativamente, teria sido possvel fazer isto: Date_I_Passed_Physics Date <,10,> ; E s agora o campo de Ms foi mudado. Note que neste exemplo, a segunda vrgula no era necessria, pois ns no mudamos outros campos posteriores. sua escolha, (e do compilador!), se deixar a segunda vrgula ou no. Agora tudo isso t indo muito bem, mas como voc se estes valores em seu cdigo? Simplesmente basta dizer: MOV AX, [Date_I_Passed_Physics.Month] MOV [Date_I_Passed_Physics.Day], 5 CMP [Date_I_Passed_Physics.Year], 1996 ; ou algo como

; ou at mesmo

Simples, n?

CRIANDO ARRAYS EM ASSEMBLER Certo, arrays so bem fceis de se implementar. Por exemplo, digamos que voc tivesse a seguintes estrutura de array em Pascal: Var MyArray: Array[0 ..19] of Word; Para criar um array semelhante em Assembler, voc tem que usar o operador DUP. DUP, ou DUPlique Varivel, tem a seguinte sintaxe: <rtulo> <diretiva> <contador> DUP (expresso)

Onde (expresso) um valor opcional para inicializar o array. Basicamente, aquele array no Pascal se pareceria com isso: MyArray DW 20 DUP (?) Ou, se voc quisesse inicializar cada valor para zero, ento voc poderia dizer isto: MyArray DW 20 DUP (0) E, como outro exemplo de como o Assembler flexvel, voc poderia dizer algo desse tipo: MyArray DB 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,,, ...para criar um array de 10 bytes, com todos os dez elementos inicializados em 1, 2, 3...

INDEXANDO ARRAYS EM ASSEMBLER

Bem, agora que voc j viu como criar arrays, eu suponho que voc queira saber como referenciar elementos individualmente. Bem, digamos que voc tivesse o seguinte array: OutroArray DB 50 DUP (?) Se voc quisesse mover o elemento 24 para, digamos, BL, ento voc poderia fazer isto: MOV BL, [OutroArray + 23]; Ou, seria possvel dizer: MOV AX, 23, MOV BL, [OutroArray + AX] NOTA: No esquea que todos os arrays comeam no elemento ZERO. Linguagens de alto-nvel como C e Pascal fazem voc esquecer isto devido ao modo que eles deixam voc referenciar arrays. Agora, isso foi fcil, mas, e se OutroArray fosse de 50 WORDS, no BYTES? OutroArray DW 50 DUP (?) ; como esse. Bem, para acessar o elemento 24, voc teria que multiplicar o valor de ndice por dois, e ento somar isso a OutroArray para conseguir o elemento desejado. MOV AX, 23 ; Acesso elemento 24 SHL AX, 1 ; Multiplique AX por dois MOV BX, [OutroArray + AX] ; Adquira elemento 24 em BX No to difcil assim, n? Porm, este mtodo fica um pouco macetoso quando voc no tem clculos fceis para fazer quando o ndice no uma potncia de dois. Digamos que voc tivesse um array que tem um tamanho de elemento de 5 bytes. Se ns quisssemos conferir o stimo elemento, ns teramos que fazer algo assim: MOV AX, 6 ; Pega o stimo elemento MOV BX, 5 ; Cada elemento tem cinco bytes MUL BX ; AX = 6 x 5 MOV DX, [YetAnotherArray + AX] ; Coloca o elemento 7 em DX

Porm, como eu disse antes, MUL no um modo muito eficiente de codificao, assim, substituir o MUL por um SHL 2 e um ADD seria a ordem do dia.

Antes de continuarmos com mais alguma coisa, eu suponho que seja hora de falar sobre nmeros de ponto flutuante. Agora, nmeros de ponto flutuantes podem ser desajeitados para se manipular em Assembler, assim v se no sai escrevendo aquele programa de planilha eletrnica que voc sempre quis, em cdigo de mquina! Porm, quando estiver trabalhando com mapeamento de textura, crculos e outras funes mais complicadas, inevitvel que voc precise de algo para declarar nmeros de ponto flutuante. Digamos que quisssemos armazenar Pi. Para declarar Pi, ns precisamos usar a DT diretiva. Voc poderia declarar Pi assim: Pi DT 3.14 DT na verdade reserva dez bytes de memria, assim seria possvel declarar Pi com um nmero maior de casas decimais. Eu no vou entrar nas particularidades de nmeros de ponto flutuante neste tutorial. Quando ns precisarmos deles mais tarde, eu falo sobre isso.

Certo, no ltimo tutorial disse eu que eu daria algum tipo de resumo do que ns cobrimos durante os ltimos quatro meses. (Ei - isso como se fosse um tutorial a cada duas semanas, ento talvez eles no tenham sado to irregularmente, afinal de contas!) De qualquer maneira, eu vou falar sobre a parte de pegar e setar bits individuais num registrador, porque este um tpico importante que eu deveria ter coberto h muito tempo atrs.

OPERADORES LGICOS

Certo, de volta ao Tutorial Cinco, eu dei as trs tabelas verdade para E, OU

e XOR. (A propsito, em uma edio de Tutorial Cinco, eu errei a tabela para XOR, amavelmente apontado por Keith Weatherby, assim se voc no tem a verso mais atual, (V 1.3), ento pegue agora. Por favor, embora eu tente o meu melhor para excluir qualquer erro dos Tutoriais, alguns ficam com erros, assim se voc achar algum, por favor me avise. Mas tenha certeza de que voc tem as edies mais recentes dos tutoriais antes de fazer isto!) Certo, chega de meus erros. Essas tabelas se pareciam com estas: AND 0 AND 0 = 0 0 AND 1 = 0 1 AND 0 = 0 1 AND 1 = 1 OR 0 OR 0 = 0 0 OR 1 = 1 1 OR 0 = 1 1 OR 1 = 1 XOR 0 XOR 0 = 0 0 XOR 1 = 1 1 XOR 0 = 1 1 XOR 1 = 0

Isto est tudo muito bem, mas pra qu vamos usar isso? Bem, em primeiro lugar, vamos dar uma olhada no que o AND pode fazer. Ns podemos usar o AND para mascarar bits em um registrador ou varivel, e assim setar e resetar bits individuais. Como um exemplo, usaremos o AND para testar um valor de um nico bit. Olhe os exemplos seguintes, e veja como voc pode usar AND para seus prprios fins. Um uso bom para AND seria conferir se um caracter lido do teclado uma maiscula ou no. (Voc pode fazer isto, porque a diferena entre uma maiscula e sua minscula de um bit. Ex: 'A' = 65 = 01000001 'a' = 97 = 01100001 'S' = 83 = 01010011 's' = 115 = 01110011) Assim, da mesma forma que voc pode azer um AND de nmeros binrios, voc poderia usar uma aproximao semelhante para escrever uma rotina que confere se um caracter maisculo ou minsculo. Ex: 0101 0011 0111 0011

AND 0010 0000 = 0000 0000 ^^^ Essa maiscula ^^^

AND 0010 0000 = 0010 0000 ^^^ Essa minscula ^^^

Agora, e o OR? O OR geralmente usado depois de um AND, mas no tem que ser. Voc pode usar OR para mudar bits individuais em um registrador ou varivel sem mudar quaisquer um dos outros bits. Voc poderia usar OR para escrever uma rotina para mudar um caracter para maisculo se j no for, ou talvez para minscula se fosse maiscula. Ex: 0101 0011 OR 0010 0000 = 0111 0011 ^^^ S maisculo agora foi mudado para s minsculo ^^^ A combinao de AND/OR um dos truques mais frequentemente usados no mundo do Assember, assim tenha certeza de que voc entendeu bem o conceito. Voc me ver freqentemente usando-os, tirando proveito da velocidade das instrues. Finalmente, e o XOR? Bem, o OU exclusivo pode ser s vezes muito til. XOR pode ser de til para alternar bits individuaisentre 0 e 1 sem ter que saber qual o contedo que cada bit tinha anteriormente. Lembre-se, como com OU, uma mscara de zero permite ao bit original continuar com seu valor. Ex: 1010 0010 XOR 1110 1011 = 0100 1001 Faa alguma tentativa para aprender estes operadores binrios, e o que eles fazem. Eles so uma ferramenta inestimvel quando se est trabalhando com nmeros binrios. OBS.: Para simplicidade, o Turbo Assembler lhe permite usar nmeros binrios em seu cdigo. Por exemplo, seria possvel dizer, AND AX, 0001000b em vez de AND AX, 8h para testar o bit 3 de AX. Isto pode facilitar as coisas para voc quando codificar.

O PROGRAM DEMONSTRATIVO Certo, chega da parte chata - vamos ao programa demonstrativo que eu inclu! Eu pensei que j era sem tempo escrever outra demonstrao - 100% Assembler desta vez, e vamos a uma rotina de fogo. Rotinas de fogo podem parecer bem efetivas, e so surpreendentemente fceis de se fazer, assim, pensei, por que no...

Agora, os princpios de uma rotina de fogo so bastante simples. Voc basicamente faz o seguinte: Crie um buffer com o qual voc vai trabalhar Este buffer pode ser quase de qualquer tamanho, entretanto quanto menor voc o fizer, o mais rpido seu programa ser, e quanto maior voc o fizer, o mais bem definido o fogo ser. Voc precisa acertar um equilbrio entre claridade e velocidade. Minha rotina est um pouco lenta, e isto devido em parte claridade do fogo. Eu escolhi 320 x 104 como tamanho do meu buffer, assim eu fiz um compromisso. A resoluo horizontal boa - 1 pixel por elemento de array, mas a resoluo vertical um pouco baixa - 2 pixels por elemento de array. Contudo, eu j vi rotinas onde um buffer de 80 x 50 usado, significando que h 4 pixels por elemento para o eixo horizontal e vertical. rpido, mas com baixssima definio. Faa uma palette agradvel Seria idia boa para ter cor 0 como preto, (0, 0, 0) e a cor 255 como branco - (63, 63, 63). Tudo entre isso deveria ser uma mistura de amarelo-avermelhado flamejante. Eu suponho voc poderia ter chamas verdes se voc quisesse, mas ns vamos usar as chamas que ns conhecemos agora. :) Agora o loop principal comea. No loop voc deve: Criar uma linha de fundo ramdmica, ou duas linhas de fundo

Basicamente, voc tem um loop como: For X := 1 To Xmax Do Begin Temp := Random(256); Buffer[X, Ymax - 1] := Temp; Buffer[X, Ymax] := Temp; End; Codifique isso na linguagem de sua escolha, e voc est no negcio. Suavize o array: Agora este o nico pedao com macete. O que voc tem que fazer, como segue: * Comece da segunda linha pra baixo do buffer. * Mover para baixo, e para cada pixel: * Some os valores dos quatro pixels que cercam o pixel. * Divida o total por quatro conseguir uma mdia. * Tire um da mdia. * Ponha a mdia - 1 no array DIRETAMENTE ACIMA onde o pixel velho estava. (Voc pode alterar isto, e digamos, pr acima e direita, e ento parecer que a chama est sendo soprada pelo vento.) * Faa isso at voc chegar ltima linha. Copie o array para a tela Se seu array de 320 x 200, ento voc pode copiar elemento-para-pixel. Se no , ento coisas so mais difceis. O que eu tive que fazer era copiar uma linha do array para a tela, abaixar uma linha da tela, copiar a mesma linha do array para a tela, e ento entrar numa linha diferente no array e na tela. Deste modo, eu espalhei o fogo um pouco. Voc vai, , querer saber exatamente por que meu array de 320 x 104 e no de 320 x 100. Bem, a razo para isto bastante simples. Se eu tivesse usado 320 x 100 como minhas dimenses de array, e ento copiasse isso para a tela, as ltimas quatro linhas teriam parecido bem estranhas. Elas no teriam sido suavizados corretamente, e o resultado final no estaria de todo flamejante. Assim, eu apenas copiei at a linha 100 para a tela, e deixei o resto pra l.

Como uma experincia, tente mudar a terceira linha abaixo no procedimento de DrawScreen para MOV BX, BufferY e mudar as dimenses para 320x100 e veja o que acontece. MOV SI, OFFSET Buffer ; Aponta SI para o incio do buffer XOR DI, DI ; Comea a desenhar em 0, 0 MOV BX, BufferY - 4 ; Perde as 4 ltimas linhas do ; buffer. Estas linhas no vo se parecer ; com fogo de jeito nehum. Volta para o incio.

Bem, no importa o quo bem eu expliquei isso tudo, muito difcil de ver o que est acontecendo sem olhar o cdigo. Ento agora ns vamos dar uma olhada no programa, seguindo o que est acontecendo. Bem, em primeiro lugar, voc tem o header. .MODEL SMALL ; Segmento de dados < 64K, segmento de cdigo < 64K .STACK 200H ; Arruma 512 bytes de espao para a pilha .386 Aqui, eu disse que o programa ter um segmento de cdigo e de dados total de menos que 128K. Eu vou dar para o programa uma pilha de 512 bytes, e permitir instrues do 386. .DATA CR LF EQU 13 EQU 10

O segmento de dados comea, e eu dou para CR e para LF os valores de "carriage return" e "line feed" (retorno de carro e alimentao de linha, i.e, volta pro incio e desce uma linha). BufferX EQU 320 BufferY EQU 104 ; Largura do buffer de tela ; Altura do buffer de tela

AllDone DB CR, LF, "That was:" DB CR, LF DB CR, LF, " FFFFFFFFF IIIIIII RRRRRRRRR ..." DB CR, LF, " FFF III RRR RRR ..." DB CR, LF, " FFF III RRR RRR ..." DB CR, LF, " FFF III RRRRRRRR ..." DB CR, LF, " FFFFFFF III RRRRRRRR ..." DB CR, LF, " FFF III RRR RRR ..." DB CR, LF, " FFF III RRR RRR ..." DB CR, LF, " FFF III RRR RRR ..." DB CR, LF, " FFFFF IIIIIII RRRR RRRR ..." DB CR, LF DB CR, LF DB CR, LF, " The demo program from Assembler Tutorial 8. ..." DB CR, LF, " author, Adam Hyde, at: ", CR, LF DB CR, LF, " blackcat@faroc.com.au" DB CR, LF, " http://www.faroc.com.au/~blackcat", CR, LF, "$" Buffer DB BufferX * BufferY DUP (?) ; O buffer de tela Seed DW 3749h ; O valor de seed, e metado do meu nmero de ; telefone - no em hexa. :)

INCLUDE PALETTE.DAT ; A palette, gerada com ; Autodesk Animator, e um programa simples em ; Pascal.

Agora, no fim, eu declaro o array e declaro um VALOR DE SEED (semente) para o procedimento Random que segue. A seed s um nmero que necessrio para comear o procedimento Random, e pode ser qualquer coisa que voc quiser. Eu tambm economizei algum espao e pus os dados para a palette em um arquivo externo que includo no cdigo assembly. D uma olhada no arquivo. Usar INCLUDE pode economizar muito espao e confuso. Eu pulei alguns procedimentos que so bastante auto-explicativos, e fui direto para a procedure DrawScreen. DrawScreen PROC MOV SI, OFFSET Buffer ; Aponta SI para o incio do buffer XOR DI, DI ; Comea a desenhar em 0, 0

MOV BX, BufferY - 4 ; Perde as ltimas 4 linhas do buffer ; Essas linhas no se parecem ; com fogo, de jeito nenhum Row: MOV CX, BufferX SHR 1 ; 160 WORDS REP MOVSW ; Move-as SUB SI, 320 ; Volta pro incio da linha do array MOV CX, BufferX SHR 1 ; 160 WORDS REP MOVSW ; Move-as DEC BX ; Decrementa o nmero de linhas VGA restantes JNZ Row ; Terminamos? RET DrawScreen ENDP Isto tambm fcil seguir, e tira proveito de MOVSW, usando-a para mover dados entre DS:SI e ES:DI. AveragePixels PROC MOV CX, BufferX * BufferY - BufferX * 2 ; Altera todo o buffer, ; exceto a primeira linha e a ltima MOV SI, OFFSET Buffer + 320 ; Comea da segunda linha Alter: XOR MOV ADD ADC ADD ADC ADD ADC SHR AX, AX ; Zera AX AL, DS:[SI] ; Pega o valor do pixel atual AL, DS:[SI+1] ; Pega o valor do pixel direita AH, 0 AL, DS:[SI-1] ; Pega o valor do pixel esquerda AH, 0 AL, DS:[SI+BufferX] ; Pega o valor do pixel abaixo AH, 0 AX, 2 ; Divide o total por quatro ; O resultado zero? ; No, ento decrementa de um

JZ NextPixel DEC AX

NOTA: O valor de decay (queda) UM. Se voc mudar a linha acima para, por exemplo "SUB AX, 2" voc vai ver que o fogo no chega to alto. Experimente... seja criativo! :)

NextPixel: MOV DS:[SI-BufferX], AL ; Pe o novo valor no array INC SI ; Prximo pixel DEC CX ; Um a menos para fazer JNZ Alter ; J fizemos todos? RET AveragePixels ENDP Agora ns vimos a procedure que faz toda a suavizao. Basicamente, ns s temos um loop que soma os valores de cor dos pixels ao redor de um pixel, carregando os valores dos pixels antes. Quando ela tem o total em AX, dividido por quatro para conseguir uma mdia. A mdia ento plotada diretamente sobre o pixel atual. Para mais informao relativo instruo de ADC, observe isto em Tutorial 5, e olhe os programas abaixo: Var W : Word; Begin Asm MOV AL, 255 ADD AL, 1 MOV AH, 0 ADC AH, 0 MOV W, AX End; Write(W); End; ^^^ Este programa returna 256 ^^^ Este programa returna 0 Var W : Word; Begin Asm MOV AL, 255 ADD AL, 1 MOV W, AX End; Write(W); End;

Lembre-se de que ADC usado para ter certeza que quando um registrador ou varivel no grande bastante para armazenar um resultado, o resultado no ser perdido. OK, depois de pular algumas procedures um pouco mais irrelevantes, chegamos ao corpo principal do programa, que algo desse tipo: Start:

MOV AX, @DATA MOV DS, AX

; DS agora aponta para o segmento de dados.

Ns apontamos DS primeiramente para o segmento de dados, de modo que possamos ter acesso a todas nossas variveis. CALL InitializeMCGA CALL SetUpPalette MainLoop: CALL AveragePixels MOV SI, OFFSET Buffer + BufferX * BufferY - BufferX SHL 1 ; SI agora aponta para o incio da segunda ltima linha (?????? - by Krull) MOV CX, BufferX SHL 1 ; Prepara para pegar BufferX x 2 nmeros randmicos BottomLine: CALL Random MOV DS:[SI], DL INC SI DEC CX JNZ BottomLine ; Pega um nmero randmico ; Usa apenas o byte baixo de DX, i.e., ; o nmero vai ser de 0 --> 255 ; Um pixel a menos para fazer ; J acabamos?

Aqui, uma nova linha do fundo calculada. O procedimento Random - muitas graas ao autor desconhecido da USENET - retorna um valor muito alto em DX:AX. Porm, ns s requeremos um nmero de 0 a 255, assim, usando s DL, ns temos tal nmero. CALL DrawScreen MOV AH, 01H INT 16H JZ MainLoop MOV AH, 00H INT 16H CALL TextMode MOV AH, 4CH MOV AL, 00H ; Copia o buffer para a VGA ; Checa se foi pressionada alguma tecla ; H alguma tecla esperando no buffer? ; No, segue em frente ; Sim, ento pega a tecla

INT 21H END Start

; Volta ao DOS

E eu acho que essa ltima parte tambm bem fcil de entender. Eu tentei comentar o fonte o tanto quanto eu pude, talvez um pouco mais fortemente em algumas partes, mas eu espero que agora todo mundo tenha uma idia de como uma rotina de fogo funciona.

De qualquer maneira, vamos l com:

E/S DE ARQUIVOS Cedo ou tarde, voc vai querer mexer com arquivos. Tudo que voc tem que ter em mente aqui que tudo BASEADO EM HANDLES. Aqueles de vocs que usaram ou experimentaram com XMS percebero o que eu quero dizer com handles exatamente, mas se voc no, ento aqui vai um resumo rpido: * Voc abre/cria um arquivo. * Voc recebe um inteiro de 16 bits sem sinal para referenci-lo. Qual a dificuldade nisso? Nota: Antigamente, antes do DOS 2, voc tinha que usar Blocos de Controle de Arquivo (FCB) para referenciar seus arquivos. (Voc provavelmente j viu FCBS=xxxx em arquivos de CONFIG.SYS, e isso para ajudar programas que foram projetados para o XT.) Ns podemos esquecer agora tudo sobre FCBs, j eles esto quase obsoletos.

Abrindo UM Arquivo: AH = 3DH AL = tipo de operao:

(Interrupo 21H)

0 = operao s de leitura;

1 = operao s de escrita; 2 = operao de leitura/escrita. DS:DX = nome do arquivo Retorna: Se foi realizada com sucesso, o flag de carry zerado, e o handle de arquivo returnado em AX. Porm, se algo saiu errado, o flag de carry setado em um, e o cdigo de erro volta em MACHADO. Para uma lista de todo os cdigos de erro, veja a seguinte tabela mais abaixo. Agora, depois de tudo isso, um exemplo: .MODEL SMALL .STACK 200H .DATA FileName DB "EXAMPLE.TXT$" Error DB "Uh oh$" .CODE START: MOV AX, @DATA ; Aponta AX para o segmento de dados MOV DS, AX ; AX --> DX MOV DX, OFFSET FileName ; Pe o offset do arquivo a abrir em DX MOV AH, 3DH ; Abre MOV AL, 00H ; s para leitura INT 21H JC Problem ; Aconteceu algo de errado?

; Aqui voc deveria ter o handle AX, e fazer alguma coisa JMP Done Problem: MOV DX, OFFSET Error MOV AH, 09H INT 21H ; Uh oh ; Nada

Done: MOV AX, 4C00H ; Pula de volta pro DOS - fechando qualquer INT 21H ; arquivo aberto. Relaxado, ns ainda no sabemos ; como fechar arquivos. END START

OK... simples bastante, espero. Agora, suponha que queiramos criar um arquivo novo? apenas uma outra subfuno simples da interrupo 21H. assim que se faz: Criando UM Arquivo Novo: AH = 3CH CX = tipo de arquivo: 0 = arquivo normal; 1 = s de leitura; 2 = arquivo escondido; 4 = arquivo de sistema; DS:DX = nome do arquivo Retorna: Como antes, se realizar com sucesso, o flag de carry zerado, e o handle do arquivo retornado em AX. Note que voc deve tomar cuidado com arquivos existentes antes de criar um arquivo novo com mesmo nome. O DOS no conferir se um arquivo do mesmo nome j existe, e escrever por cima do velho. Antes de criar um arquivo novo - tente abrir o arquivo primeiro. Se voc obtiver o cdigo de erro 2 em AX, (arquivo no existe), ento prossiga e crie o arquivo novo. Se voc no conseguiu o erro 2, voc estar escrevendo por cima de um arquivo j existente! (Interrupo 21H)

Como voc deveria saber de experincias com linguagens de alto-nvel, voc tem que fechar seus arquivos antes de terminar seu programa. (Na verdade, a funo 4CH fecha todos os arquivos abertos de qualquer maneira, mas isso um modo

relaxado de fazer as coisas.) Para fechar um arquivo aberto, voc deveria fazer isto: Fechando UM Arquivo: AH = 3EH BX = handle de arquivo Retorna: De novo, qualquer erro refletido no flag de carry e AX. (Interrupo 21H)

Finalmente, cdigos de erro. Apenas checando o CF para ver se qualquer coisa saiu errado, nos deixar saber certamente se algo est faltando, mas ns realmente gostamos de mais detalhes. Examinar o AX depois de um erro ser descoberto o caminho a seguir, e AX poderia conter qualquer um dos cdigos seguintes:

Cdigo Explicao 00H erro Desconhecido 01H nmero de funo invlido 02H Arquivo no achado 03H Caminho no achado 04H muitos arquivos abertos 05H Acesso negado 06H handle invlido 07H Blocos de controle destrudos 08H Falta de memria 09H endereo de bloco de controle Ruim 0AH ambiente invlido 0BH formato invlido 0CH cdigo de acesso invlido 0DH dados invlidos 0EH erro desconhecido 0FH drive invlido 10H no pode remover diretrio atual 11H Dispositivo no o mesmo 12H mais nenhum arquivo disponvel

13H 14H 15H 16H 17H 18H 19H 1AH 1BH 1CH 1DH 1EH 1FH

Disco protegido contra escrita unidade Ruim Drive no pronto comando Desconhecido erro de CRC tamanho de estrutura ruim erro de procura mdia invlida Setor no achado Impressora desligada ** erro de escrita erro de leitura falha geral

Tudo cortesia da boa e velha referncia tcnica do DOS. Algum deles l encima so bem obscuros - na verdade s h alguns que voc precisa de se lembrar. Algum de meu *favoritos * : Setor no achado, Erro de procura e Erro de CRC no meio de uma pilha de disquetes relaxados arjeados. o tipo de porcaria que traz recordaes. :)

Certo, assim ns vimos como criar, abrir e fechar arquivos. Agora vamos fazer algo com eles. Para ler alguns bytes de um arquivo, voc tem que usar funo 3FH. Assumindo que voc j abriu o arquivo de onde voc quer ler, voc pode usar um pouco de cdigo como o abaixo: MOV AH, 3FH ; L byte(s) MOV BX, Handle ; arquivo a trabalhar MOV CX, BytesToRead ; quanto a ler MOV DX, OFFSET WhereToPutThem ; um array ou varivel INT 21H JC DidSomethingGoWrong ; Checa erros

Se voc est tendo problemas em sacar algo disso - no se preocupe muito. Apenas volte aos exemplos acima e veja como pode fazer sentido. Prximo tutorial ns continuaremos com sprites - (e como carrreg-los do disco) assim voc ver um bom exemplo. Bem... agora, escrevendo em um arquivo. Muito semelhante a ler, ns usamos a funo

40H. Um cdigo para escrever um byte se pareceria com isso: MOV AH, 40H ; Escreve byte(s) MOV BX, Handle ; arquivo para se escrever nele MOV CX, BytesToWrite ; quanto escrever MOV DX, OFFSET WhereToWriteFrom ; de onde os dados esto vindo INT 21H JC DidSomethingGoWrong ; algum erro? Bem, aquilo quase conclui E/S de arquivos para este tutorial. Embora no seja um componente principal da peogramao da linguuagem Assembly, E/S de arquivos todavia, um conceito importante para se pegar.

CHAMANDO O ASSEMBLER NO C/C++

Eu suponho que j passou da hora de falar sobre como linkar o Assembler no C. Pessoalmente, eu prefiro codificar VGA numa combinao de Assembler/Pascal. Porm, C tem seu lugar, e linkar com C um assunto importante que ns deveramos cobrir. Voc deve ter percebido que voc pode entrar cdigo Assembly em seu programa de C desse jeito: / * Seu cdigo em C vai aqui * / asm { /**/ / * Seu cdigo em Assembler vai aqui * / /**/ } / * Seu cdigo em C continua daqui * /

Agora, considerando que ns podemos inserir o Assembly diretamente no cdigo em C, por que nos preocuparamos em escrever cdigo externo? A resposta bastante simples. Usando rotinas externas, temos cdigo que mais rpido de se executar, mais rpido de compilar, que pode usar algumas das caractersticas especiais de Turbo Assembler - como modo ideal, e pode ser at mesmo portvel a outras linguagens. Escrever cdigo externo para C bem simples, e gratificantemente mais fcil que escrever cdigo externo para Pascal. (Veja o Tutorial Sete). Como voc pde observar no Tutorial Sete, ns tnhamos que declarar o segmento de cdigo e o de dados usando o a meio confusa diretiva SEGMENT. Isto devido ao modo como o Pascal gosta de organizar a memria, e s h um modo de contornar problema - ns podemos usar o modelo TPASCAL. Infelizmente, TPASCAL um modo antiquado de fazer as coisas, assim ns temos que pr um pouco de trabalho nisso. Eu no vou falar novamente em TPASCAL, assim ns podemos nos esquecer seguramente de detalhes chatos. Note que nada disto aplica a ns em C - ns podemos usar felizmente nossos simples e agradveis esqueletos de Assembler. H algumas restries colocadas, entretanto, a ns pela maioria dos compiladores: O compilador usa SI e DI para armazenar variveis registradoras. Se voc usou variveis registradoras em seu cdigo, lembre-se de dar um push e pop em SI e DI em seu cdigo externo. O compilador provavelmente no vai dar push e pop em CS, DS, SS e BP, ento tenha certeza de ter cuidado se for alterar algum desses registradores. Alm desses pequenos detalhes, h pouco que ns precisamos ter em mente. Vamos l!

OK... agora ns vamos escrever uma pequena rotina externa e linkar isto ao C. Vamos dar uma olhada num esqueleto bsico que apenas pe algum texto na tela. ==================== LIBRARY.ASM =============== .MODEL SMALL .DATA

Message DB "Well looky here - we got ourselves some text$" .CODE PUBLIC _sample

; --------------------------------------------------------------------------; ; void sample(); ; _sample PROC NEAR ; Declara uma procedure near ; Acerta o modo de video ; Modo 03H

MOV AH, 00H MOV AL, 03H INT 10H

MOV AH, 09H ; Imprime uma string MOV DX, OFFSET Message ; DS:DX <-- Mensagem INT 21H RET _sample END ENDP ; Fora daqui!

Bem.... no h nada muito engenhoso l. Agora, e o cdigo C que vai junto com isto? ======================== EXAMPLE.C =========================== extern void sample(); int main() { sample(); return 0;

E para compilar o lote, a linha abaixo far o trabalho. C:\> TCC EXAMPLE.C LIBRARY.ASM Claro que, se voc est usando ento que outro "sabor" de C, substitua TCC com qualquer outro interpretador de linha de comando que voc tiver. Tambm possvel fazer o C reconhecer variveis declaradas em Assembler, e o seguinte esqueleto explica como isso feito: ======================= LIBRARY.ASM ========================== .MODEL SMALL .DATA PUBLIC _YourVariable _YourVariable DW 9999 .CODE END ; Declara uma varivel externa ; Faz a varivel ser uma word valendo 9999

========================= EXAMPLE.C ========================== extern int YourVariable; int main() { printf("The Assembler external variable is: %d", YourVariable); return(0); } Novamente, compile isto com: TCC EXAMPLE.C LIBRARY.ASM

Mas que tal passar parmetros para suas rotinas? Ns poderamos fazer isso do modo difcil, como ns fizemos com Pascal, ou alternativamente, poderamos usar a diretiva ARG. ARG brilhante, porque simplifica grandemente as coisas -- mas tem algumas negligncias. Isto , em toda rotina voc precisa de umas trs instrues adicionais. Se voc quer velocidade e no se incomoda com um pouco de trabalho duro, trabalhe diretamente com a pilha como ns fizemos no Tutorial Sete. Aqui est como se usa ARG: ======================== LIBRARY.ASM ========================= .MODEL SMALL .DATA .CODE PUBLIC _putpixel ; Declara a procedure externa

; --------------------------------------------------------------------------; ; void putpixel(int x, int y, char color, int location); ; _putpixel PROC NEAR ARG X : Word, Y : Word, Color : Byte, Location : Word PUSH BP MOV BP, SP MOV MOV MOV MOV MOV MOV SHL SHL ADD ADD MOV ; Salva BP ; BP *deve ser* igual a SP para ARG funcionar ; Parmetros podem ser acessados facilmente agora

AX, [Location] ES, AX BX, [X] DX, [Y] DI, BX BX, DX DX, 8 BX, 6 DX, BX DI, DX AL, [Color]

MOV ES:[DI], AL POP BP RET _putpixel ENDP END ========================= EXAMPLE.C ========================== extern void putpixel(int x, int y, char color, int location); int main() { asm { mov ax, 0x13 int 0x10 } putpixel(100, 100, 12, 0xa000); sleep(2); asm { mov ax, 0x03 int 0x10 } return(0); } ; BP precisa ser restaurado!

No to macetoso, hein? Porm, se voc escolher escrever rotinas externas porque voc quer a velocidade que o Assembler pode lhe dar, ento acesse a pilha do modo difcil. Esses extras push's e pop's realmente podem crescer se sua rotina de putpixel for chamada 320x200 vezes!

UMA INTRODUO S MACROS

Macros so uma das caractersticas mais poderosas que voc tem sua disposio quando est trabalhando com o Assembler. Freqentemente voc se achar repetindo as mesmas poucas linhas de cdigo inmeras vezes quando estiver escrevendo programas maiores. Voc no quer fazer aquela dificuldade de criar um procedimento -- que reduziria a velocidade do cdigo, mas voc no quer continuar se repetindo. A resposta.... MACROS. Uma macro s um conjunto de instrues que recebe um nome pelo qual ela ser referenciada no cdigo. Voc pode definir um macro assim: MyMacroName MACRO

; ; Suas instrues vo aqui ; ENDM MyMacroName

E dali em diante, sempre que voc puser MyMacroName em seu cdigo, sero colocadas as instrues contidas dentro da macro no lugar do nome da macro. OBS.: provavelmente melhor declarar qualquer macro antes de declarar o segmento de dados. Para ficar mais claro, coloque todas suas macros em outro arquivo de texto e ento use INCLUDE<nomedoarquivo> para incluir as macros.

Macros tambm podem ter parmetros e podendo ser muito teis. Por exemplo, eu usei muito a funo DOS 09H para pr uma string na tela. Eu poderia fazer os programas que eu escrevo mais fceis de ler primeira vista criando a seguinte macro: PutText MACRO TextParam MOV AH, 09H ; TextParam o parmetro--NO MOV DX, OFFSET TextParam ; uma varivel. Substitua TextParam com INT 21H ; qualquer nome que voc escolher.

ENDM

PutText

Ento, assumindo no segmento de dados que eu tenha declarado uma string assim: AString DB "This is a string$" Eu poderia exibir aquela string escrevendo: PutText AString OBS.: Quando voc est trabalhando com macros, tenha cuidado em observar que registradores elas mudam. Se estiver em dvida, d um push e um pop em quaisquer registradores que voc sente que possam ser afetados.

Embora aquela macro simples realmente no fosse nada de especial, macros tm muitas outras utilidades. Eu no vou dizer mais nada sobre macros agora, mas eu as usarei de vez em quando em programas de demonstrao no futuro, e voc aprender outras tcnicas que voc pode pr em bom uso. De qualquer maneira, vamos ao que eu queria fazer:

O PROGRAMA DEMONSTRATIVO

No princpio eu ia lanar este tutorial sem um programa demo, mas vendo como eu fui um pouco preguioso esse tempo todo, (e tambm porque um amigo meu h pouco tempo fez uma demonstrao como essa), eu decidi incluir um demo de plasma. Plasmas podem ser um pouco engenhosas em umas partes -- me levou um bom tempo para fazer a coisa funcionar direito por causa de um problema que eu tive com minha tabela de lookup. Mas se voc seguir o algoritmo abaixo, voc no deve ter nenhum problema. *** Antes de comear, voc precisar de QUATRO variveis temporrias em *** seu cdigo. Em Assembler isto pode se pr um pouco trabalhoso porque voc

se achar freqentemente com falta de registradores. Voc poderia declarar alguns bytes no segmento de dados, mas mais rpido usar registradores. Estas quatro variveis temporrias armazenaro s nmeros entre 0 e 255, assim elas s precisam ser BYTES. No algoritmo, eu me refiro a estas variveis temporrias como Temp1, *** Temp2, Temp3 e Temp4. *** O algoritmo se parece com isso: Crie uma tabela de lookup Isto basicamente s uma senide longa. Voc pode experimentar usar uma onda de co-seno, ou alterar a amplitude da funo que voc est usando. Eu criei minha tabela de lookup usando a seguinte expresso: For W := 1 To 512 Do SinTable[W] := Round(Sin(W / 255 * Pi * 2) * 128); (SinTable um array de 512 BYTES) Inicialize a palette Eu pessoalmente gosto de fazer minhas palettes depois de ver o demonstrativo rodando com a palette padro. Desse modo, fazendo certas cores escuras e outras muito claras, o resultado exatamente do jeito que eu quero. Eu descobri que o melhor modo de fazer isto capturar a tela quando a demonstrao est rodando, com um programa como Screen Thief, ento carregar aquela tela em um programa de pintura que deixe alterar a palette. Depois de conseguir a palette do jeito que voc quer, salve-a para o disco como um arquivo COL (se possvel) e ento escreve um pequeno programa para ler no arquivo COL e escrever um arquivo tipo o PLASMA.DAT. Se lembre, Screen Thief shareware, assim se voc for us-lo, envie para o autor algum dinheiro, hein? Loop (1): Antes de comear a plotar a primeira linha, voc deve: * Zerar Temp4; * Decrementar Temp3 de dois;

Voc pode fazer experincias com Temp3 -- quanto maior for o nmero que voc subtrair, mais rpido o plasma vai mover. Voc agora vai para o Loop (2). Loop (2): Ao incio de cada linha voc deve: * Incrementar Temp4 de um; * Fazer Temp1 = Sintable[Linha corrente + Temp3]; * Fazer Temp2 = SinTable[Temp4]; Voc agora vai para o Loop (3). Loop (3): Para todo pixel na linha atual voc deve: * Calcular a cor daquele pixel a ser plotado; O valor de cor daquele pixel simplesmente definido por: SinTable[Temp1 + Temp2] + SinTable[Linha corrente + Temp2] Infelizmente, isto um pouco mais difcil de calcular no Assembler e acaba levando muitas linhas de cdigo!! * Incrementar Temp1 de um; * Incrementar Temp2 de um; Depois de fazer uma linha inteira, voc ento volta atrs ao Loop (2). Uma vez feitas todas as linhas (200), voc pode ento voltar ao Loop (1).

Claro que, voc tambm vai querer pr algo para checar o retrace, e seria uma boa idia tambm se algum apertou alguma tecla!! NOTA: Para quem no sabe, o VGA tem um registrador de estado que vale a pena prestar ateno em que ele serve. registrador 03DAH, e conferindo seus vrios bits, podemos ver o que est acontecendo com o VGA.

(Para aqueles que querem saber para qu so exatamente todos os bits, ache que deveriam obter uma cpia da Ralf Brown's Interrupt List. Isto est disponvel na minha homepage e ela contm uma lista completa de todas as interrupes, registradores e muito mais.) De qualquer modo, ns estamos s interessados no quarto bit do 03DAH que nos deixa saber se um retrace est acontecendo. Se pudermos acessar o VGA enquanto o canho de eltrons do monitor est voltando (retrace) ao topo da tela -- ns podemos obter um rpido acesso, livre de flicks (tremidas..., sacou? - by Renato) O que demais, j que o retrace acontece a cada 1/80 de segundo EM TODOS OS COMPUTADORES, agora ns temos um mtodo de fazer que nosso demo rode numa velocidade especfica em todas as mquinas. Para conferir se o retrace est acontecendo, ns examinamos simplesmente o bit quatro. Se o bit quatro est setado, um retrace est em execuo. Porm, ns no sabemos o quanto de um retrace j aconteceu, e no sabemos quanto tempo de acesso live de flicks ns temos. A soluo checar de novo por um retrace, de modo que podemos estar seguros de que estamos no COMEO de um. Eu usei retrace no cdigo para ter certeza de que o demo ia rodar mesma velocidade em todas as mquinas. (Mais ou menos). Tambm note que meu plasma mais uma variante de plasma. Voc pode, e encorajado a alterar o cdigo -- (tente incrementar os valores de temp em vez de decrementar e mudar a quanto o valor de temp decrementado ou mudar o modo como o valor das corer achado. Tambm tente mudar a palette, porque isto pode fazer o plasma parecer completamente diferente). possvel criar todos os tipos de efeitos s fazendo mudanas simples, assim, experimente... seja criativo!

Você também pode gostar