Como arrancar basicamente com o Assembly (Intel)
8 de Junho de 2005
Este documento não substitui, de forma alguma, a documentação principal
recomendada sobre a programação da linguagem assembly (Intel).
Trata-se dum guia rápido para dar a conhecer as bases essenciais necessárias à
programação em assembly.
Este documento deve ser lido depois dos documentos principais, caso contrário
poderia ser limitativo. Deve ser lido no género de “assentar ideias” para depois
voltarmos novamente à documentação principal.
Os fundamentos essenciais para arrancarmos e criarmos programas
simples e interessantes em Linguagem Assembly são:
Os Registos
Os Segmentos do Pentium
O formato duma instrução (comando)
A locação de dados
Alguns comandos (instruções) muito essenciais
Os Modos de Endereçamento mais usados
A importância da Z flag do registo Status Register (SR)
Como ler a Z flag
A importância do INT 21H (Interrupt 21h)
A estrutura básica dum programa em emu8086
Como criar e chamar subrotinas
A importância do Stack (Pilha)
A Tabela ASCII básica (não extendida)
Alguns exemplos de programas elementares
1. Os Registos
Os registos são memórias rápidas que se encontram dentro do processador, bastante
úteis no auxílio dos cálculos e armazenamento temporário de dados (efectuados pelo
próprio processador). Normalmente, os registos são constituídos por 16 ou 32 bits. No
caso do Pentium, os registos de 32 bits têm, como parte do seu nome, o prefixo E (de
Extended) - por exemplo, o registo EAX, que significa Extended AX. Há registos que
podem ser acedidos ao nível do seu todo ou em sub partes quer ao nível de byte ou word
(dois bytes). No caso do Pentium, temos, por exemplo, o registo EAX, o qual pode ser
acedido na sua totalidade (EAX) ou através das suas partes sub constituintes, duma
forma independente e consoante as necessidades em relação ao número de bits
necessários: AX (dois bytes), AH (o byte alto de AX) e AL (o byte baixo de AX).
Ainda, no caso do Pentium, os registos EBX, ECX e EDX funcionam da mesma forma
do que o registo EAX (são todos de uso geral, mas apresentam diferentes
especificidades entre si). Outros registos, então mais específicos, somente podem ser
acedidos através do seu todo ou ao nível da palavra baixa – é o caso do registo ESI
(acede-se através de ESI ou SI). Os registos EDI, EBP, ESP funcionam da mesma
forma do que o registo ESI, embora com diferentes especificidades.
Finalmente, podemos categorizar os registos em reservados e não reservados.
Os registos não reservados são categorizados em registos de uso geral, de offset e de
stack.
Por uma questão de simplicidade, vamos apenas considerar a parte base destes registos e
não a sua parte extendida ( por exemplo, o registo EAX vamos considerar apenas AX e
não EAX)
Registos Não Reservados:
AX
BX
DX
CX
SI
DI
BP e SP
Registo de uso geral. Decompõe-se em AH e AL, podendo ser assim
acedido independentemente ao nível do byte (AL ou AH) ou ao nível dos
dois bytes conjuntos AX.
O registo AL é muito usado, durante a manipulação de strings, para o
carregamento dum carácter.
Registo de uso geral. Decompõe-se em BH e BL, podendo ser assim
acedido independentemente ao nível do byte (BL ou BH) ou ao nível dos
dois bytes conjuntos BX.
O registo BX é também muito usado como offset em relação aos
segmentos de Dados.
Registo de uso geral. Decompõe-se em DH e DL, podendo ser assim
acedido independentemente ao nível do byte (DL ou DH) ou ao nível dos
dois bytes conjuntos DX.
O registo DX é também muito usado como offset em relação aos
segmentos de Dados.
Registo de uso geral. Decompõe-se em CH e CL, podendo ser assim
acedido independentemente ao nível do byte (CL ou CH) ou ao nível dos
dois bytes conjuntos CX.
O registo CX tem o seu principal uso como contador.
Usado principalmente em offsets (source index). Não se decompõe.
(portanto, somente pode ser acedido através de SI ou ESI)
Usado principalmente em offsets (destination index). Não se decompõe.
(portanto, somente pode ser acedido através de DI ou EDI)
Registos usados principalmente na manipulação do Stack. BP aponta para
a base do Stack e SP aponta para o topo do Stack.
Os registos reservados são somente acedidos pelo próprio processador. Neste caso
(reservados), são principalmente os registos CS, DS, ES, SS, IR, IP, MBR, MAR
Registos Reservados:
CS
Code Segment
DS
Data Segment
ES
Extra Segment
SS
Stack Segment
IR
Instruction Register
IP
Instruction Pointer
MBR
Memory Buffer Reg.
MAR
Memory Address Reg.
Armazena o endereço do início da área de memória destinada
ao carregamento das instruções (código). Por exemplo:
100,000.
Armazena o endereço do início da área de memória destinada
ao carregamento dos dados. Por exemplo: 200,000.
Armazena o endereço do início da área de memória destinada
ao carregamento Extra de dados. Por exemplo: 300,000.
Armazena o endereço do início da área de memória destinada
ao Stack (Pilha). Por exemplo: 400,000.
Armazena a instrução, proveniente da memória, para
descodificação.
Contém o offset (deslocamento ou intervalo em bytes em
relação à base do segmento de código) da instrução a ser
acedida dentro da área de código. Por exemplo, vamos supor
que o segmento de código se inicia na memória no endereço
100,000 e a a instrução a ser acedida se encontra na posição
(offset) 800 (em relação à base ou início do segmento), então o
registo CS conterá uma referência a 100,000 e o registo IP
conterá o valor 800 (em notação, temos CS:IP, ou seja, mais
exactamente, 100,000:800 (e diz-se: 800 é o offset em relação
à base 100,000).
Contém os dados a ser transferidos para o Data Bus ou os
dados provenientes do Data Bus (vindos da memória ou I/O)
Contém os endereços a ser transferidos para o Address Bus.
2. Os Segmentos do Pentium
Os segmentos do Pentium são áreas de memória distintas e dedicadas ao
armazenamento específico da informação (código, dados e pilha). Nomeadamente, são
os seguintes:
Code Segment
Data Segment
Extra Data Segment
Stack Segment
Armazena as instruções dos programas (o código)
Por exemplo: mov ax,bx
Armazena os dados utilizados pelos programas
Por exemplo: msg db ‘Lisboa$’
Armazenamento extra de dados ( bom quando um programa
contém muitos dados)
Armazena temporariamente dados numa PILHA
Por exemplo: push ax
Os segmentos ocupam normalmente 64Kb cada, mas podem ser programados para
outros tamanhos. O endereço do início de cada segmento é armazenado em registos
apropriados dentro do Pentium. Estes registos têm o mesmo nome dos segmentos de
memória e o endereço que armazenam é apenas uma parte, pois requerem um
determinado cálculo para obterem o endereço completo e real. Vejamos os seus nomes:
CS (registo Code Segment)
DS (registo Data Segment)
ES (registo Extra Segment)
SS (registo Stack Segment)
Armazena o endereço-início do Segmento de Código
que se encontra na RAM (normalmente 64Kb)
Armazena o endereço-início do Segmento de Dados
Armazena o endereço-início do Segmento Extra de
Dados
Armazena o endereço-início da Pilha
O conteúdo dentro dum segmento é acedido através da referência à base do segmento e
dum offset (deslocamento) que indica uma posição (a partir de 0) dentro do segmento.
Os offsets popularmente usados para aceder dados são bx, si, e di. O registo ip funciona
somente como offset do registo cs. O comando lea (Load Effective Address) carrega
num registo o offset ou endereço-offset do início do dado em memória a ser acedido.
Assim, dada a ilustração abaixo, e o seguinte código:
...
msg db ‘LISBOA$’
...
lea
si, msg
mov al, [si]
carrega no registo si o offset 200 (o endereço-offset do início da string msg ) e no
registo al o carácter L (o conteúdo), através do modo de endereçamento indirecto. O
registo DS, por sua vez, contém o valor 60,000, neste exemplo, o início do segmento de
dados (daí a notação DS:SI, ou seja, 60,000:200)
Conteúdo
…
L
200
I
201
S
202
B
203
O
204
A
205
$
206
Offset (si)
Inicio do Segmento de Dados – 60,000
3. O formato duma instrução (comando)
As instruções ou comandos, têm o seguinte formato, na sua acepção completa:
<label:> <instrução/directiva> <operando, operando> <;comentário>
Por exemplo:
inicio: mov ax, bx ; carrega (atribui a) em ax o conteúdo de bx
A label (etiqueta) é opcional. Serve para marcar (associar) uma linha de código,
permitindo assim servir de referência para futuros saltos (para essa mesma linha de
código). A label deve ser um símbolo único e terminar com : de forma a não se
confundir com uma variável, que se identifica também através dum símbolo, mas
sem os dois pontos (:).
A instrução é um comando, como, por exemplo, mov (que significa atribuir ou
carregar), ou uma directiva, como, por exemplo, endp (que significa end de um
procedimento).
O operando é normalmente um registo, label ou memória. Uma instrução não pode
conter mais do que dois operandos. Quando uma instrução usa dois operandos,
então devem estar separados por uma vírgula (,) e o operando da esquerda é o
destino (escrita da execução da operação) e o da direita é a fonte (leitura do seu
conteúdo). Uma instrução pode conter apenas um operando. Uma instrução –
directiva não contém quaisquer operandos.Vejamos alguns exemplos.
mov ax,bx
jmp inicio
mov num, al
.data
Carrega em ax uma cópia do valor que se encontra no
registo bx.
Salta (jump) para a linha de código que está associada à
label inicio.
Carrega na posição de memória, simbolizada pela variável
num, o valor ou conteúdo do registo al.
É uma directiva usada pelo TASM (Turbo Assembler) para
indicar o início da área de dados.
Os comentários são apenas informação do tipo esclarecimento (finalidade duma
instrução, objectivo do programa) que nos pode ser útil mais tarde (para
esclarecimentos). Os comentários iniciam-se através dum (;) e não são executados.
4. A locação de dados
A locação de dados faz-se na área própria dos dados (nunca misturá-los com as
instruções). No TASM, a área de dados do programa começa logo a seguir à directiva
.data e antes da directiva .code. No emu8086, começa antes da label start: já que este
emulador tem uma instrução, logo no início do programa, antes da definição dos dados,
que permite saltar a área de dados, não misturando-a, desta forma, com as instruções. A
instrução é jmp start.
O tipo dos dados é consoante a natureza dos dados atribuídos (numérico se números,
string se caracteres entre plicas, etc).
A alocação de dados é feita através de símbolos (usados na nomeação das variáveis) e
directivas. Algumas directivas principais são: DB (Define Byte), DW (Define Word),
DUP(?) (reserva de vários espaços de memória), EQU (definição de constantes).
Vejamos alguns exemplos:
num db ?
val dw ?
msg db ‘Viva$’
info db 40 dup(?)
max equ 20
Aloca (reserva) 1 byte à variável num, inicializando-a, neste
momento, a indefinido (?).
Aloca 2 bytes (uma word) à variável val, inicializando-a, neste
momento, a indefinido (?).
Aloca a string msg com os caracteres V, i, v, a, $, ocupando
cada um (carácter) um byte. O carácter $ indica o terminus da
string e é obrigatória a sua inclusão em determinadas situações,
conforme veremos brevemente. Portanto, é boa prática incluir
este carácter como delimitador-terminus duma string. As
strings devem ser delineadas através de plicas (‘).
Aloca 40 bytes à variável info (muito provavelmente uma
string), inicializando-os, neste momento, a indefinido.
O símbolo max passa a ser uma constante com o valor 20.
5. Alguns comandos (instruções) muito essenciais
Nome
Finalidade
Exemplo
Descrição
mov
Carregamento
atribuição
mov ax,bx
Carrega no registo ax uma cópia do valor
que está armazenado no registo bx.
Carrega no registo bl uma cópia do valor
que está armazenado no registo cl.
Adiciona o valor de dx ao valor de ax,
ficando o novo resultado armazenado em
ax (ax = ax + dx).
Subtrai o valor de bx ao valor de ax,
ficando o novo resultado armazenado em
ax (ax = ax – bx).
Incrementa uma unidade ao registo cx
(cx=cx + 1)
Decrementa uma unidade ao registo cx
(cx=cx - 1)
Salta para a linha de código que contém a
label inicio.
Compara o valor de cx a 0 (zero).
Compara o valor do registo al com o valor
ASCII do carácter ‘a’.
mov bl,cl
add
Adição
ax, dx
sub
Subtração
sub ax,bx
inc
Incrementa 1
inc cx
dec
Decrementa 1
dec cx
jmp
Salta
jmp inicio
cmp
Comparações
cmp cx, 0
cmp al, ‘a’
loop
controla
ciclos
loop inicio
Este comando controla um processo
repetitivo da seguinte maneira:
O registo CX deve ser carregado com o
número de iterações. Sempre que o
comando loop é executado, decrementa CX
e testa-o: se for > 0, repete o ciclo; caso
contrário, cessa o ciclo.
Exemplo:
mov cx, 5
repetir:
...
loop repetir
lea
Carrega
offset
lea dx,msg
Carrega o endereço-offset (load effective
address) da string msg no registo dx. Ou
seja, carrega o endereço-offset do primeiro
carácter armazenado na string msg. Pra ser
lido o conteúdo que se encontra nessa
posição o registo dx tem que estar entre [].
6. Os Modos de Endereçamento mais usados
Os modos de endereçamento determinam o modo como uma determinada arquitectura
pode conjugar legalmente os seus comandos. Normalmente, os modos de
endereçamento são o imediato, registo, directo, indirecto, relativo e indexado. Todavia,
os mais usados são o imediato, registo, directo e indirecto. Estes quatro modos
permitem-nos praticamente combinar quase todos os comandos.
Vejamo-los através de exemplos:
Nome
Definição
Exemplo
Imediato
A instrução contém o
próprio valor a ser
aplicado na operação
Registo
Os operandos da
add ax, bx
instrução são registos.
Um dos operandos é uma
mov num,ax
variável, cuja memória,
que lhe está associada,
pode ser acedida
directamente
O acesso ao conteúdo
mov al,[bx]
duma variável faz-se
através dum registo
offest, que deve estar
Directo
Indirecto
mov al, 5
Descrição
Carrega em al o valor 5 (ou
seja, este valor encontra-se
na própria implementação da
instrução, não sendo
necessário ir buscá-lo à
memória ou a outro registo).
Soma o valor de bx a ax.
Uma cópia do valor de ax é
carregada directamente na
posição de memória onde se
encontra alocada a variável
num.
Carrega em al uma cópia do
valor que se encontra na
posição de memória indicada
pelo registo offset. O registo
contido entre [].
offset deve ser previamente
carregado com o offset onde
se encontra o valor a ser
acedido. Isto faz-se através
do comando LEA ou
OFFSET. Por exemplo,
dado o seguinte excerto de
código e a ilustração da
memória que se segue a esta
tabela:
...
str db ‘Viva$’
...
lea bx,str
mov al,[bx]
...
O registo bx é carregado com
o offset 200 (o deslocamento
200 em relação à base do
segmento de dados, que é,
neste exemplo, 60,0000) e o
registo al é carregado com
uma cópia do conteúdo que
se encontra nessa posição, ou
seja, o carácter ‘L’.
Conteúdo
…
L
200
I
201
S
202
B
203
O
204
A
205
$
206
Offset (si)
Inicio do Segmento de Dados – 60,000
7. A importância da Z flag do registo Status Register (SR)
O registo SR é constituído à base de bits que reagem (activam/desactivam) a
resultados de determinadas operações causadas pela execução das instruções. São
assim indicadores de estados, daí também serem conhecidos por flags, já que
funcionam com “bandeirinhas” que assinalam algo. Assim, há bits para assinalar a
ocorrência de interrupts (I), resultados negativos (S), erros de paridade (P), uma
operação com Carry (C), etc.
Estes bits têm nomes apropriados, bem como posições (dentro do SR) e podem ser
lidos através de comandos especiais.
Uma das flags mais úteis é a Z flag, que se activa quando uma operação produz um
0 (ZERO). A leitura desta flag permite-nos controlar ciclos, através da leitura do seu
contador de iterações, bem como saber do resultado de comparações. Vejamos a
seguinte ilustração:
Comando Z flag
mov cx,2
Z= 0
dec cx
Z=0
dec cx
Z=1
mov ax, 5
cmp ax, 7
Z=0
Z=0
cmp ax, 5
Z=1
Descrição
O valor 2 é carregado no registo CX. A Z flag fica inactiva,
já que esta operação não produziu um 0 (zero).
O registo cx é decrementado, passando assim a conter o valor
1. A Z flag continua assim inactiva.
O registo cx é novamente decrementado, passando agora a
conter o valor 0 (zero). A Z flag reage imediatamente,
passando a activa, assinalando, desta forma, a produção dum
0, como resultado da execução da instrução dec cx.
A Z flag desactiva-se.
A Z flag continua desactivada já que a comparação do
registo ax com o valor 7 não resultou em verdadeira.
A Z flag activa-se imediatamente já que a comparação é
verdadeira.
8. Como ler a Z flag
A Z flag é lida (testada) através dos jumps condicionais JZ e JNZ.
Exemplo 1:
...
dec cx
jz fim
...
Se o registo cx for igual a 0, então dá-se o salto para a label fim. Caso contrário,
nenhum salto é realizado e a instrução seguinte é executada.
9. A importância do INT 21H (Interrupt 21h)
Um programa em linguagem assembly pode utilizar uma variedade de interrupts para a
realizaçao de diferentes tarefas. Basta ver alguns dos samples do emu8086, como, por
exemplo, o color.asm, que utiliza o INT 10H para “jogar” com as diferentes cores.
O Int 21H é um interrupt bastante útil, que, para agora, é-nos suficiente, já que permite
uma boa comunicação entre utilizador-teclado-memória-ecrã-utilizador, permitindo
assim o desenvolvimento de alguns programas interessantes.
O Int 21h, ao causar uma interrupção, deve estar sempre associado a uma função,
devidamente numerada, que indica assim a razão porque está a acontecer a interrupção.
Por conseguinte, a chave da sua utilidade está em sabermos o número da função que
desejamos que o Int 21H realize durante o processo de interrupção.
O número da função pretendida deve ser carregado no registo AH, antes da ocorrência
da interrupção através Int 21H – isto é uma regra do uso do Int 21H.
Algumas funções úteis e regras da sua aplicação:
Função
Finalidade
01H
Leitura, a partir do
teclado, dum carácter
(letra ou número) em
formato ASCII
02H
09H
04CH
Regras
O carácter lido é-nos
disponibilizado no registo
AL (fica assim ao nosso
dispor para fazermos dele o
que pretendermos)
Display dum carácter O carácter deve ser
ASCII
carregado no registo DL.
Por exemplo, fazer o display
do carácter a, que está
demonstrado ao lado.
Display duma string O offset da string é o registo
DX. A string deve terminar
através do carácter $.
Fim do programa
Como usar
mov ah, 1
int 21H
mov ah,2
mov dl, ‘a’
int 21H
lea dx, msg
mov ah, 9
int 21H
O processo que está a correr
o programa em assembly
mov ah, 04CH
cessa a sua actividade. A
int 21H
memória usada é libertada e
o processo é extinto.
10. A estrutura básica dum programa em emu8086
A estrutura mais elementar dum programa em ambiente emu8086 é a seguinte:
____________________________________________________________
; Comentário opcional dedicado ao objectivo do programa
; Cabeçalho padrão de directivas iniciais e salto da área dos dados
; a primeira instrução cria um programa do tipo COM
; a segunda instrução, carrega a parte do código do programa a partir
; do offset 100H em relação ao Segmento de Código (boa prática).
; a última instrução salta a área dedicada aos dados para o início
; das instruções
#make_COM#
ORG 100H
JMP inicio
; Data:
; área destinada à locação dos dados
; início das instruções
inicio:
; escrever nesta área as instruções
; Fim do programa
MOV AH, 4CH
INT 21h
_____________________________________________________________
11. Como criar e chamar subrotinas
Chama-se uma subrotina através da cláusula CALL, seguindo-se o nome da subrotina.
Por sua vez, a subrotina deve incluir no cabeçalho da sua declaração e implementação,
para além do seu próprio nome, a cláusula proc (de procedimento) e uma indicação da
distância em bytes entre o ponto da sua chamada e a sua própria implementação, a qual
representa-se através das cláusulas near (até 127 bytes) ou far (acima de 127 bytes).
Finalmente, a subrotina deve terminar com a cláusula ret (de return) e uma indicação
do fim do procedimento, através da cláusula ENDP.
Os dados (variáveis, constantes e registos) usados pela subrotina são todos globais.
Desta forma, se se pretender reutilizar alguns registos, então devemos salvaguardá-los
no STACK, à entrada da subrotina, e reavê-los à saída da subrotina.
Vejamos um exemplo, através dum excerto de programa:
...
call CalcSoma
; chama a subrotina CalcSoma
...
; fim do programa principal
CalcSoma proc near
; implementação da subrotina
; escreve-se aqui o código da subrotina
ret
; retorno da subrotina
CalcSoma ENDP
; fim do procedimento CalcSoma
12. A importância do Stack
O stack é uma área de memória do tipo LIFO, ou seja, o último elemento a chegar é
o primeiro a sair. É uma excelente estrutura, pois permite salvaguardarmos dados,
principalmente registos, permitindo assim a sua reutilização. A salvaguarda dum
registo faz-se através do comando PUSH, seguindo-se do nome do registo. Para
reavermos o valor original dum registo, aplica-se o comando POP, seguindo-se do
nome do registo. Mas atenção à natureza da estrutura LIFO, se tivermos mais do que
um registo no stack, os POPs devem ser aplicados na ordem inversa dos PUSHs.
Vejamos um exemplo, através dum excerto de programa:
...
push ax
push bx
push cx
; salvaguarda de registos
; pode-se então agora reutilizar livremente estes registos porque
; os seus valores originais estão salvaguardados no stack.
pop cx
pop bx
pop ax
; reavendo os seus valores originais
13. A Tabela ASCII básica (não extendida)
A Tabela ASCII (American Standard Code for Information Interchange) é usada pela
maior parte da industria de computadores para a troca de informações. Cada carácter é
representado por um código de 8 bits (um byte). Abaixo mostramos a tabela ASCII de 7
bits. Existe uma tabela extendida para 8 bits que inclui os caracteres acentuados.
A tabela ASCII pode-nos ser útil durante o processo de depuração dum programa ( ou
para melhor acompanhamento passo-a-passo dum programa).
Caracter Decimal Hexadecimal
Binário
Comentário
NUL
00
00
0000 0000 Caracter Nulo
SOH
01
01
0000 0001
Começo de cabeçalho de
transmissão
STX
02
02
0000 0010 Começo de texto
ETX
03
03
0000 0011 Fim de texto
EOT
04
04
0000 0100 Fim de transmissão
ENQ
05
05
0000 0101 Interroga
ACK
06
06
0000 0110 Confirmação
BEL
07
07
0000 0111 Sinal sonoro
BS
08
08
0000 0100 Volta um caracter
HT
09
09
0000 1001 Tabulação Horizontal
LF
10
0A
0000 1010 Próxima linha
VT
11
0B
0000 1011 Tabulação Vertical
FF
12
0C
0000 1100 Próxima Página
CR
13
0D
0000 1101 Início da Linha
SO
14
0E
0000 1110 Shift-out
SI
15
0F
0000 1111 Shift-in
DLE
16
10
0001 0000 Data link escape
D1
17
11
0001 0001 Controle de dispositivo
D2
18
12
0001 0010 Controle de dispositivo
D3
19
13
0001 0011 Controle de dispositivo
D4
20
14
0001 0100 Controle de dispositivo
NAK
21
15
0001 0101 Negativa de Confirmação
SYN
22
16
0001 0110 Synchronous idle
ETB
23
17
0001 0111 Fim de transmissão de bloco
CAN
24
18
0001 1000 Cancela
EM
25
19
0001 1001 Fim de meio de transmissão
SUB
26
1A
0001 1010 Substitui
ESC
27
1B
0001 1011 Escape
FS
28
1C
0001 1100 Separador de Arquivo
GS
29
1D
0001 1101 Separador de Grupo
RS
30
1E
0001 1110 Separador de registro
US
31
1F
0001 1111 Separador de Unidade
Espaço
32
20
0010 0000
!
33
21
0010 0001
"
34
22
0010 0010
#
35
23
0010 0011
$
36
24
0010 0100
%
37
25
0010 0101
&
38
26
0010 0110
'
39
27
0010 0111
(
40
28
0010 1000
)
41
29
0010 1001
*
42
2A
0010 1010
+
43
2B
0010 1011
,
44
2C
0010 1100
-
45
2D
0010 1101
.
46
2E
0010 1110
/
47
2F
0010
FFFF
0
48
30
0011 0000
1
49
31
0011 0001
2
50
32
0011 0010
3
51
33
0011 0011
4
52
34
0011 0100
5
53
35
0011 0101
6
54
36
0011 0110
7
55
37
0011 0111
8
56
38
0011 1000
9
57
39
0011 1001
:
58
3A
0011 1010
;
59
3B
0011 1011
<
60
3C
0011 1100
=
61
3D
0011 1101
>
62
3E
0011 1110
?
63
3F
0011 1111
@
64
40
0100 0000
A
65
41
0100 0001
B
66
42
0100 0010
C
67
43
0100 0011
D
68
44
0100 0100
E
69
45
0100 0101
F
70
46
0100 0110
G
71
47
0100 0111
H
72
48
0100 1000
I
73
49
0100 1001
J
74
4A
0100 1010
K
75
4B
0100 1011
L
76
4C
0100 1100
M
77
4D
0100 1101
N
78
4E
0100 1110
O
79
4F
0100 1111
P
80
50
0101 0000
Q
81
51
0101 0001
R
82
52
0101 0010
S
83
53
0101 0011
T
84
54
0101 0100
U
85
55
0101 0101
V
86
56
0101 0110
W
87
57
0101 0111
X
88
58
0101 1000
Y
89
59
0101 1001
Z
90
5A
0101 1010
[
91
5B
0101 1011
\
92
5C
0101 1100
]
93
5D
0101 1101
^
94
5E
0101 1110
_
95
5F
0101 1111
`
96
60
0110 0000
a
97
61
0110 0001
b
98
62
0110 0010
c
99
63
0110 0011
d
100
64
0110 0100
e
101
65
0110 0101
f
102
66
0110 0110
g
103
67
0110 0111
h
104
68
0110 1000
i
105
69
0110 1001
j
106
6A
0110 1010
k
107
6B
0110 1011
l
108
6C
0110 1100
m
109
6D
0110 1101
n
110
6E
0110 1110
o
111
6F
0110 1111
p
112
70
0111 0000
q
113
71
0111 0001
r
114
72
0111 0010
s
115
73
0111 0011
t
116
74
0111 0100
u
117
75
0111 0101
v
118
76
0111 0110
w
119
77
0111 0111
x
120
78
0111 1000
y
121
79
0111 1001
z
122
7A
0111 1010
{
123
7B
0111 1011
|
124
7C
0111 1100
}
125
7D
0111 1101
~
126
7E
0111 1110
DELETE
127
7F
0111 1111
14. Alguns exemplos de programas elementares
a) Prog1.asm -- mostra uma string, enviando para o ecrã um
carácter de cada vez, usando assim a função 2 do Int 21H. Vai
causar uma interrupção por cada caracter visualizado.
b) Prog2.asm -- mostra uma string toda duma vez, usando assim
a função 9 do Int 21H. Vai causar apenas uma interrupção para
a visualização de toda a string.
c) Prog3.asm -- aplica as funções 01 e 02, as quais permitem a
leitura e visualização de caracteres digitados.
d) Prog4.asm -- copia uma string-fonte, criada à base de
caracteres digitados, para uma outra string-destino.
e) Prog5.asm -- inverte uma string, através da construção duma
string auxiliar.
f) Prog6.asm -- chama uma subrotina para analisar uma string, a
fim de contar o número de ocorrências do carácter ‘a’ e,
seguidamente, mostrar um ‘*’ por cada ‘a’.
g) Prog7.asm – Soma de dois números inteiros, usando
subrotinas inerentes ao emu8086.