Está en la página 1de 22

Capítulo 1

El procesador matemático

1.1 Introducción
El coprocesador matemático, 8087 tiene 8 registros de coma flotante de 80 bits, maneja enteros
con signo, BCD y punto flotante. El 8086/8088 deberá funcionar en modo máximo para trabajar
con el 8087. Las instrucciones del 8087 incluyen un juego completo de funciones: aritméticas,
exponenciales, logarítmicas y trigonométricas. Utiliza un formato interno de números en coma
flotante de 80 bits con el cual gestiona siete formatos exteriores. El 8087 posee 45.000 transistores
y consume 3 watt.

1.2 Los números reales y su tratamiento


Hay dos tipos de números que aparecen normalmente durante el cálculo: los enteros y los reales.
Aunque los enteros son un subconjunto de los reales, la computadora trabaja de formas distintas
con ambos. El microprocesador trabaja con números enteros utilizando la aritmética de precisión
múltiple.

El coprocesador trabaja con números reales, que casi nunca pueden representarse exactamente; sin
embargo, en la práctica la representación en coma flotante permite una muy buena aproximación.
Con este sistema, la representación de un número consta de tres partes: el signo, el exponente y
la mantisa.

Los enteros con signo pueden ser de 16 bits, que se definen con DW; de 32 bits, que se definen
con DD y de 64 bits, que se definen con DQ. En los tres casos los números positivos se almace-
nan en formato verdadero con un bit de signo igual a “0” en el extremo izquierdo. Los números
negativos se almacenan en complemento a dos, con el bit de signo igual a “1” en el extremo izquierdo.

Los números BCD requieren 80 bits. Se almacenan como enteros empaquetados de 18 dígitos, en
nueve bytes, con dos dígitos por byte. El décimo byte contiene el bit de signo. Los números positivos
y negativos son almacenados en formato real utilizando la directiva DT.

Los números en punto flotante o números reales, constan de tres partes: un bit de signo, un expo-
nente descentrado (bias) y una mantisa. Se pueden manejar tres tipos de números de punto flotante

1
1.2. LOS NÚMEROS REALES Y SU TRATAMIENTO 2

con el estándar IEEE-754: real corto de 32 bits que se define con DD, real largo de 64 bits que se
define con DQ y real temporal de 80 bits que se define con DT.

Formato Rango Precisión Byte 9 Byte 8 Byte 7 Byte 6 Byte 5 Byte 4 Byte 3 Byte 2 Byte 1 Byte 0
Word 104 16 bits I15 I0
Integer Short 109 32 bits I31 I0
Long 1018 64 bits I63 I0
Packed BCD 1018 S D17 D0
Short 10±38 24 bits S E1 E0 F8 F23
Real Long 10±308 54 bits S E 10 E0 F4 F12 F20 F28 F36 F44 F52
Temporary 10±932 64 bits S E8 E0 F0 F7 F15 F23 F31 F39 F47 F55 F63

Ii : Integer Packed BCD: (-1)S (D17 . . . D0 )


Real: (-1)E-Bias (F0 •F1 . . . )
Bias = 7F for short real
7FF for long real
3FFF for temporary real

Tabla 1.1: Tipos de datos del procesador matemático

En los números reales Short y Long el bit F0 es implícito (F0 •), esto se aclarará con un ejemplo.
En los números reales Temporary el bit F0 es explícito. Por precisión se entiende la exactitud de un
número, en la representación de coma flotante, la mantisa es la encargada de la precisión. El rango
está relacionado con el tamaño de los números que se pueden representar, en la notación de coma
flotante, es el exponente el que fija el rango

El número 0 se almacena como solo ceros, exceptuando el bit de signo, que puede ser “1” para
representar un cero negativo. El ±∞ se almacena con todos los bits del exponente en “1” y todos
los bits de la mantisa en “0” y el bit de signo en “1” negativo o “0” positivo. Finalmente un NAN
(no es un número) es un resultado no válido en punto flotante que tiene todos los bits del exponente
en “1” y todos los bits de la mantisa en “0”.

Ejemplo 1.1
Convertir a binario 137.25
1. Convertir a binario 137.25d = 10001001.01b
2. Normalizar
1 , 0 0 0 1 0 0 1 0 1 × 27
mantisa
F0 bit 1 implícito
3. Cálculo del exponente desplazado (bias)
07h = 0 0 0 0 0 1 1 1 Exponente
7Fh = 0 1 1 1 1 1 1 1 Polarización
86h = 1 0 0 0 0 1 1 0 Exponente polarizado
4. Número en formato IEEE-754
F0
S E7 E6 E5 E4 E 3 E 2 E1 E0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23
S Exponente Mantisa
0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4h 3h 0h 9h 4h 0h 0h 0h
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 3

Para ver esto en nuestro programa, simplemente definimos un número en el segmento de datos y
escribimos el encabezamiento de un programa que termina como .exe.

num dd 137 .25 ; numero de 32 bits

push ds
sub ax , ax
push ax
mov ax , datos
mov ds , ax

Después de hacer correr, paso a paso el encabezamiento (de push ds a mov ds,ax), podemos ver que
en el segmento de datos de la computadora está el número en formato hexadecimal e invertido.
ds:0000 00 40 09 43. Es decir, 137.25 = 43 09 40 00 ¿Cómo se obtiene esto? Lo mostramos a
continuación
137 = 10001001
.25 x 2 = 0 .5
.5 x 2 = 1

Entonces 137 .25 = 10001001 .01


137 .25 = 1 .000100101x2 ^7
Exponente = 07 = 0000 0111
+
Polarizacion = 7 F = 0111 1111
---------
Exponente polarizado = 1000 0110

Por tanto en número en el estandar IEEE-754 es:

137.25 = 0100 0011 0000 1001 0100 0000 0000 0000 = 43 09 40 00

1.3 Señales del procesador matemático, 8087

AD15 − AD0
8087
Reg. Control
Reg. Estado S2
A16 /S3 S1
A17 /S4 Reg. Marcas
S0
A18 /S5 QS 0
A19 /S6 D79 D0
QS 1
st(0)
BHE/S7 st(1) BU SY
RQ/GT 1 st(2) READY
st(3)
RQ/GT 0 st(4) RESET
IN T st(5) V cc
st(6)
CLK st(7) GN D

Figura 1.1: Señales del 8087

1.3.1 Registro de estado del 8087


D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
B C3 T O P C2 C1 C0 IR X PE UE OE ZE DE IE
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 4

B Ocupado El coprocesador está ocupado. Se evalúa analizando el registro de estado o


el uso de la instrucción FWAIT. Los coprocesadores actuales se sincronizan
automáticamente con el µProcesador.
C0 - C 3 Código de condición Indican condiciones relacionadas con el coprocesador. Estos bits tienen sig-
nificados diferentes para distintas instrucciones (ve la tabla más abajo).
TOP Apunta a la parte superior de Indica el registro ST direccionado como parte superior de la pila, general-
la pila ST mente ST(0)
ES Resumen de errores Se activa si cualquiera de los bits (sin máscara): PE, UE, OE, ZE, DE o IE,
se activa.
PE Error de precisión Indica que el resultado o los operandos se excedieron de la precisión selec-
cionada.
UE Error de sub-desbordamiento Indica que el resultado es demasiado pequeño para representarlo con la
precisión seleccionada en la palabra de control.
OE Error de desbordamiento Indica que el resultado es demasiado grande para representarlo con la pre-
cisión seleccionada en la palabra de control. Si el error está enmascarado,
el coprocesador genera infinito para un error de desbordamiento.
ZE Error de división por cero Indica que el divisor es cero, el dividendo no es ni infinito ni cero.
DE Error Operando no normal- Indica que al menos uno de los operandos no está normalizado.
izado
IE Error operación no válida Indica error debido a operaciones del tipo 0/0, +∞, -∞, raíz de un número
negativo, etc, o un NAN como operando.
Instrucción C3 C2 C1 C0 Indicación
0 0 X 0 ST > Operando, salto con la instrucción JA
0 0 X 1 ST < Operando, salto con la instrucción JB
FTST,FCOM
1 0 X 1 ST = Operando, salto con la instrucción JE
1 1 X 1 ST no puede compararse
Q1 0 Q0 Q2 Los 3 bits de más a la derecha del cociente
FPREM
? 1 ? ? Incompleto
0 0 0 0 + Anormal
0 0 0 1 + NAM
0 0 1 0 - Anormal
0 0 1 1 - NAM
0 1 0 0 + Normal
0 1 0 1 +∞
0 1 1 0 - Normal
0 1 1 1 -∞
FXAM
1 0 0 0 +0
1 0 0 1 Vacío
1 0 1 0 -0
1 0 1 1 Vacío
1 1 0 0 + No normal
1 1 0 1 Vacío
1 1 1 0 - No normal
1 1 1 1 Vacío
Anormal = Los bits de la izquierda de la mantisa son cero
No normal = el exponente está en su valor más negativo
Normal = Formato de punto flotante estándar
NAN = Un exponente de solo unos y una mantisa distinta de cero, el operando para TST es cero.

Para evaluar el registro de estado, el procedimiento es el siguiente: i) se pasa su contenido al


registro AX utilizando la instrucción FSTSW AX, ii) se testea los bits necesarios utilizando la
instrucción TEST o la instrucción SAHF. En el primer caso, TEST es un AND a nivel de bits, el
resultado no se deposita en el operando destino. En el segundo caso SAHF transfiere bits específi-
cos del registro AH a las banderas, en el siguiente orden: D7 D6 D5 D4 D3 D2 D1 D0 = S Z _ A _ P _ C.
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 5

Ejemplo 1.2

Verificar si la división ST(0)/num es correcta.

La variable num es el resultado de un proceso anterior a la división.

fcom num ; Compara ST (0) con num


fstsw ax ; Copia el registro de estado en AX
sahl ; Copia banderas del 8087 al flag register del 8086
je ST.igual ; Salta si es igual
jb ST.menor ; Salta si es menor
ja ST.mayor ; Salta si es mayor

1.3.2 Registro de control

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0


IC RC RC PC PC PM UM OM ZM DM IM
IM Mascara de operación inválida
DM Mascara de operando no normalizado
ZM Mascara de división entre cero
OM Mascara de desbordamiento
UM Mascara de sub-desbordamiento
PM Mascara de error de precisión
Control de precisión
00 = Precisión simple (corto)
PC 01 = Reservado
10 = Doble precisión (largo)
11 = Precisión extendida (temporal)
Control de redondeo
00 = Redondeo al más cercano o par
RC 01 = Redondeo descendente hacia infinito negativo
10 = Redondeo ascendente hacia infinito positivo
11 = Cortar o truncar hacia cero
Control de infinito
IC 0 = Proyectivo
1 = Afín

1.3.3 Registro de etiquetas

El registro de etiquetas marca el contenido de cada registro ST. La función principal de las etiquetas
es optimizar el rendimiento del coprocesador. Sin embargo, las etiquetas pueden utilizarse para
interpretar el contenido de los registros del coprocesador.
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
TAG(7) TAG(6) TAG(5) TAG(4) TAG(3) TAG(2) TAG(1) TAG(0)
Los valores de las etiquetas son:
00 = Válido
01 = Cero
10 = Inválido o infinito
11 = Vacío
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 6

1.3.4 Instrucciones de transferencia de números


FLD mem: Introduce una copia de mem en ST(0). La fuente debe ser un número real en punto flotante de 4,
8 ó 10 bytes. Este operando se transforma automáticamente al formato real temporal.
FLD ST(num): Introduce una copia de ST(num) en ST(0).
FILD mem: Introduce una copia de mem en ST(0). La fuente debe ser un operando de memoria de 2, 4 u 8
bytes, que se interpreta como un número entero y se convierte al formato real temporal.
FBLD mem: Introduce una copia de mem en ST(0). La fuente debe ser un operando de 10 bytes, que se interpreta
como un valor BCD empaquetado y se convierte al formato real temporal.
FST mem: Copia ST(0) a mem sin afectar el puntero de pila. El destino puede ser un operando real de 4 u 8
bytes (no el de 10 bytes).
FST ST(num): Copia ST(0) al registro especificado.
FIST mem: Copia ST(0) a mem. El destino debe ser un operando de 2 ó 4 bytes (no de 8 bytes) y se convierte
automáticamente el número en formato temporal real a entero.
FSTP mem: Extrae una copia de ST(0) en mem. El destino puede ser un operando de memoria de 4, 8 ó 10
bytes, donde se carga el número en punto flotante.
FSTP ST(num): Extrae ST(0) hacia el registro especificado.
FISTP mem: Extrae una copia de ST(0) en mem. El destino debe ser un operando de memoria de 2, 4 u 8 bytes
y se convierte automáticamente el número en formato temporal real a entero.
FBSTP mem: Extrae una copia de ST(0) en mem. El destino debe ser un operando de memoria de 10 bytes. El
valor se redondea a un valor entero, si es necesario, y se convierte a BCD empaquetado.
FXCH: Intercambia ST(1) y ST(0).
FXCH ST(num): Intercambia ST(num) y ST(0).

1.3.5 Instrucciones de carga de constantes

Las constantes no pueden ser operandos de las instrucciones del coprocesador. Las constantes debe
estar ubicadas en memoria de datos de la computadora. Sin embargo, hay algunas instrucciones
para cargar ciertas constantes en ST(0), entre ellas: 0.0, 1.0, π y algunas constantes logarítmicas.

FLDZ: Carga el número +0.0 en ST(0).


FLD1: Carga el número uno en ST(0).
FLDPI: Carga el valor de π en ST(0).
FLDL2E: Carga el valor de log2 e en ST(0).
FLDL2T: Carga el valor de log2 10 en ST(0).
FLDLG2: Carga el valor de log10 2 en ST(0).
FLDLN2: Carga el valor de loge 2 en ST(0).

1.3.6 Instrucciones de transferencia de datos de control

El área de datos del coprocesador, o parte de él, puede ser guardado en memoria y luego se puede
volver a cargar. Una razón para hace esto es guardar una imagen del estado del coprocesador antes
de llamar una subrutina para luego restaurar. Otra razón es para modificar el comportamiento
del coprocesador, almacenando ciertos datos en memoria, operar con los mismos utilizando instruc-
ciones del µProcesador y finalmente cargarlo de nuevo en el coprocesador.

Se puede transferir el área de datos del coprocesador, los registros de control, o simplemente la
palabra de estado o de control.

Cada instrucción de carga tiene dos formas: i) La forma con espera verifica excepciones de errores
numéricos no enmascarados y espera a que sean atendidos. ii) La forma sin espera (cuyo mnemotéc-
nico comienza con “FN”) ignora excepciones sin enmascarar. Aquí [N] significa opcional.
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 7

FLDCW mem2byte: Carga la palabra de control desde la memoria de datos del µProccesador.
F[N]STCW mem2byte: Almacena la palabra de control en la memoria de datos del µProcesador.
F[N]STSW mem2byte: Almacena la palabra de estado en la memoria de datos del µProccesador.
FLENV mem14byte: Carga el entorno desde la memoria de datos del µProccesador.
F[N]STENV mem14byte: Almacena el entorno en la memoria de datos del µProccesador.
FRSTOR mem94byte: Restaura el estado completo del coprocesador.
F[N]SAVE mem94byte: Salva el estado completo del coprocesador.

1.3.7 Instrucciones aritméticas


Cuando se usan operandos de memoria con una instrucción aritmética, el mnemotécnico de la
instrucción distingue entre número real y número entero. No se pueden realizar operaciones arit-
méticas con números BCD empaquetados en la memoria, Para esto se usa FBLD que carga números
BCD desde la memoria.

FADD: Hace ST(1) más ST(0), ajusta el puntero de pila y pone el resultado en ST(0), por lo que
ambos operandos se destruyen.
FADD mem: Hace ST(0) ← ST(0) + [mem]. En mem deberá haber un número real en punto flotante.
FIADD mem: Hace ST(0) ← ST(0) + [mem]. En mem deberá haber un número entero en complemento
a dos.
FADD ST(num), ST(0): Realiza ST(num) ← ST(num) + ST(0).
FADD ST, ST(num): Realiza ST(0) ← ST(0) + ST(num).
FADDP ST(num), ST(0): Realiza ST(num) ← ST(num) + ST(0) y retira el valor de ST(0) de la pila, con lo que
ambos operandos se destruyen.
FSUB: Hace ST(1) menos ST(0), ajusta el puntero de pila y pone el resultado en ST(0), por lo
que ambos operandos se destruyen.
FSUB mem: Hace ST(0) ← ST(0) - [mem]. En mem deberá haber un número real en punto flotante.
FISUB mem: Hace ST(0) ← ST(0) - [mem]. En mem deberá haber un número entero en complemento
a dos.
FSUB ST(num), ST(0): Realiza ST(num) ← ST(num) - ST(0).
FSUB ST(0), ST(num): Realiza ST(0) ← ST(0) - ST(num).
FSUBP ST(num), ST(0): Realiza ST(num) ← ST(num) - ST(0) y retira el valor de ST(0) de la pila, con lo que
ambos operandos se destruyen.
FSUBR: Hace ST(0) menos ST(1), ajusta el puntero de pila y pone el resultado en ST(0), por lo
que ambos operandos se destruyen.
FSUBR mem: Hace ST(0) ← [mem] - ST(0). En mem deberá haber un número real en punto flotante.
FISUBR mem: Hace ST(0) ← [mem] - ST(0). En mem deberá haber un número entero en complemento
a dos.
FSUBR ST(num), ST(0): Realiza ST(num) ← ST(0) - ST(num).
FSUBR ST(0), ST(num): Realiza ST(0) ← ST(num) - ST(0).
FSUBRP ST(num), ST(0): Realiza ST(num) ← ST(0) - ST(num) y retira el valor de ST(0) de la pila, con lo que
ambos operandos se destruyen.
FDIV: Dividir el valor de ST(1) por ST(0), ajusta el puntero de pila y pone el resultado en ST(0),
por lo que ambos operandos se destruyen.
FDIV mem: Hace ST(0) ← ST(0)/[mem]. En mem deberá haber un número real en punto flotante.
FIDIV mem: Hace ST(0) ← ST(0)/[mem]. En mem deberá haber un número entero en complemento
a dos.
FDIV ST(num), ST(0): Realiza ST(num) ← ST(num)/ST.
FDIV ST(0), ST(num): Realiza ST ← ST(0)/ST(num).
FDIVP ST(num), ST(0): Realiza ST(num) ← ST(num)/ST(0) y retira el valor de ST(0) de la pila, con lo que ambos
operandos se destruyen.
FDIVR: Hace ST(0) dividido ST(1), ajusta el puntero de pila y pone el resultado en ST, por lo
que ambos operandos se destruyen.
FDIVR mem: Hace ST(0) ← [mem]/ST(0). En mem deberá haber un número real en punto flotante.
FIDIVR mem: Hace ST(0) ← [mem]/ST(0). En mem deberá haber un número entero en complemento
a dos.
FDIVR ST(num), ST(0): Realiza ST(num) ← ST(0)/ST(num).
FDIVR ST(0), ST(num): Realiza ST(0) ← ST(num)/ST(0).
FDIVRP ST(num), ST(0): Realiza ST(num) ← ST(0)/ST(num) y retira el valor de ST(0) de la pila, con lo que ambos
operandos se destruyen.
FABS: Pone el signo de ST(0) a positivo (valor absoluto).
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 8

FCHS: Cambia el signo de ST(0).


FRNDINT: Redondea ST(0) a un entero.
FSQRT: Reemplaza ST(0) con su raíz cuadrada.
FSCALE: Suma el valor de ST(1) al exponente del valor en ST(0). Esto efectivamente multiplica
ST(0) por dos a la potencia contenida en ST(1). ST(1) debe ser un número entero.
FPREM: Calcula el resto parcial hallando el módulo de la división de los dos registros de la pila
que están el tope. El valor de ST(0) se divide por el de ST(1). El resto reemplaza el
valor de ST(0). El valor de ST(1) no cambia. Como esta instrucción realiza sustracciones
repetidas, puede tomar mucho tiempo si los operandos son muy diferentes en magnitud.
Esta instrucción se utiliza a veces en funciones trigonométricas.
FXTRACT: Luego de esta operación, ST(0) contiene el valor de la mantisa original y ST(1) el del
exponente.

1.3.8 Control del flujo del programa


FCOM: Compara ST(0) y ST(1).
FCOM ST(num): Compara ST y ST(num).
FCOM mem: Compara ST(0) y mem. El operando de memoria deberá ser un número real de 4 u 8 bytes
(no de 10).
FICOM mem: Compara ST(0) y mem. El operando deberá ser un número entero de 2 ó 4 bytes (no de
8).
FTST: Compara ST(0) y cero.
FCOMP: Compara ST(0) y ST(1) y extrae ST(0) fuera de la pila.
FCOMP ST(num): Compara ST(0) y ST(num) y extrae ST(0) fuera de la pila.
FCOMP mem: Compara ST(0) y mem y extrae ST(0) fuera de la pila. El operando de memoria deberá
ser un número real de 4 u 8 bytes (no de 10).
FICOMP mem: Compara ST(0) y mem y extrae ST(0) fuera de la pila. El operando deberá ser un número
entero de 2 ó 4 bytes (no de 8).
FCOMPP: Compara ST(0) y ST(1) y extrae dos elementos de la pila, perdiéndose ambos operandos.
FXAM: Pone el valor de los indicadores según el tipo de número en ST(0). La instrucción se utiliza
para identificar y manejar valores especiales como infinito, cero, números no normalizados,
NAN (Not a Number), etc. Ciertas operaciones matemáticas son capaces de producir estos
números especiales. Una descripción de ellos va más allá del alcance de este apunte.

1.3.9 Instrucciones trascendentales

F2XM1 ST ← 2ST (0)−1.0 . El valor previo de ST debe estar entre 0 y 0,5.


FYL2X ST(1)log2 ST(0). El puntero de pila se actualiza y luego se deja el resultado en ST, por lo
que ambos operandos se destruyen.
FXL2P1 ST(1)log2 (ST (0) + 1.0). El puntero de pila se actualiza y luego se deja el resultado en ST,
por lo que ambos operandos se destruyen. El valor absoluto del valor previo de ST debe
estar entre 0 y la raíz cuadrada de 2 dividido 2.
FPTAN Tangente del valor en ST(0). El resultado es una razón Y/X, donde X reemplaza el valor
anterior de ST(0) e Y se introduce en la pila así que, después de la instrucción, ST(0)
contiene Y y ST(1) contiene X. El valor previo de ST(0) debe ser un número positivo menor
que π/4. El resultado de esta instrucción se puede utilizar para calcular otras funciones
trigonométricas, incluyendo seno y coseno.
FPATAN Arcotangente de la razón Y/X, donde X está en ST e Y está en ST(1). ST(0) es extraído
de la pila, dejando el resultado en ST(0), por lo que ambos operandos se destruyen. El
valor de Y debe ser menor que el de X y ambos deben ser positivos. El resultado de esta
instrucción se puede usar para calcular otras funciones trigonométricas inversas, incluyendo
arcoseno y arcocoseno.
FSIN/FCOS Encuentra el seno o el coseno de ST(0), que tiene que estar en radianes y debe ser menor
que 263 , el resultado se devuelve en ST(0).
FSINCOS Encuentra el seno y lo deposita en ST(0) y el coseno de ST(0) y lo deposita en ST(1).
ST(0) tiene que estar en radianes y debe ser menor que 263 .
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 9

1.3.10 Control del coprocesador


Aquí [N] significa opcional.
F[N]INIT: Inicializa el coprocesador y restaura todas las condiciones iniciales en las palabras de control
y de estado. Es una buena idea utilizar esta instrucción al principio y al final del programa.
Poniéndolo al principio asegura que los valores en los registros puestos por programas que
se ejecutaron antes no afecten al programa que se comienza a ejecutar. Poniéndolo al final
hará que no se afecten los programas que corran después.
F[N]CLEX: Pone a cero los indicadores de excepción y el indicador de ocupado de la palabra de estado.
También limpia el indicador de pedido de interrupción del 8087.
FINCSTP: Suma uno al puntero de pila en la palabra de estado. No se afecta ningún registro.
FDECSTP: Resta uno al puntero de pila en la palabra de estado. No se afecta ningún registro.
FREE ST(num): Marca el registro especificado como vacío.
FNOP: Copia ST(0) a sí mismo tomando tiempo de procesamiento sin tener ningún efecto en
registros o memoria.

Ahora que disponemos de las instrucciones del coprocesador, con el Turbo Debbuger, analizamos
un programa en ensamblador, que utiliza instrucciones del coprocesador. Primero analizaremos la
definición del segmento de código:

Veamos las figuras 1.2 y 1.3. El programa de la figura 1.2a define el segmento de código como
siempre lo hacemos; es decir, el segmento de código no permite utilizar registros de 16 bits, el
resultado se ve en el recuadro amarillo de la figura 1.3a. Por otro lado el programa de la figura 1.2b,
permite utilizar registros de 16 bits. Esto logramos con la directiva use16. El resultado se ve en la
recuadro amarillo de la figura 1.3b. También fíjese que es necesario utilizar las directivas .386, para
habilitar los registros de 16 bits del µProcesador y, .387, para utilizar el procesador matemático.

copro1.asm copro2.asm
.386 .386
.387 .387

; ------------------------------------- ; -------------------------------------
datos segment para ' data ' datos segment para ' data '
dato1 dd -3 .1415 ; precision sencilla angulo dw 45
dato3 dd 15 ; numeros enteros seno dw 0 ; seno ( angulo )
datos ends datos ends
; ------------------------------------- ; -------------------------------------

codigo segment para ’code’ codigo segment use16

program proc far program proc far


assume ss : pila , ds : datos , cs : codigo , es : datos assume ss : pila , ds : datos , cs : codigo , es : datos
push ds push ds
sub ax , ax sub ax , ax
push ax push ax
mov ax , datos mov ax , datos
mov ds , ax mov ds , ax

a) Sin habilitar registros de 16 bits b) Habilitando registros de 16 bits


Figura 1.2: Habilitación de los registros de 16 bits del µProcesador
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 10

a) Sin habilitar registros de 16 bits b) Habilitando registros de 16 bits


Figura 1.3: Uso de los registros de 16 bits del µProcesador

En segundo lugar, veremos como corre, paso a paso, un programa que utiliza el procesador matemático.
Para esto proponemos el programa de la figura 1.5. En el veremos operaciones con números enteros
y operaciones con números reales.

Figura 1.4: Procedimiento para ver los registros Procesador matemático

Para ver los registros de coprocesador, proceda como en la figura 1.4, haga correr el programa paso
a paso hasta la instrucción mov ds,ax, para ver los datos en el segmento de datos. En los registros
del coprocesador, verá como se cargan (con fld) los números -3,1415 y 3.0 y se suman con fadd para
luego depositar (con fstp) el resultado en la variable, suma. Luego vera como se carga (con fild) el
número 15, y se suma (con fiadd) directamente con el número 30, que esta en el segmento de datos
del µProcesador, luego la suma la depositamos (con fst) en la variable suma1. El estudiante debe
ver la diferencia entre las instrucciones fstp y fst. La primera extrae de ST(0) y deposita en suma,
la segunda copia de ST(0) a suma1. Con la ayuda del programa de la figura 1.5, el estudiante
deberá verificar la representación de números reales utilizando el estandar IEEE-754.
.386
.387
; Ejemplo de suma con el coprocesador matematico
; ----------------------------------------------------------
pila segment para stack ' stack '
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 11

dw 30 dup (0)
pila ends
; ----------------------------------------------------------
datos segment para ' data '

dato1 dd -3 .1415 ; precision sencilla


dato2 dd 3 .0
suma dd 0 .0
dato3 dd 15 ; numeros enteros
dato4 dd 30
suma1 dd 00
datos ends
; ----------------------------------------------------------
codigo segment use16
program proc far
assume ss : pila , ds : datos , cs : codigo , es : datos
push ds
sub ax , ax
push ax
mov ax , datos
mov ds , ax
; Operaciones con numeros reales
; ------------------------------
fld dato1 ; carga dato1 en ST
fld dato2 ; carga dato2 en ST (1)
fadd ; suma ST = ST + ST (1)
fstp suma ; remueve ST a suma
; Operaciones con numeros enteros
; -------------------------------
fild dato3
fiadd dato4
fst suma1
ret
program endp
; ----------------------------------------------------------
codigo ends
end program

Figura 1.5: Programa que utiliza el coprocesador matemático

El programa de la figura 1.6, calcula el seno de angulo = 45◦ . En primer lugar. Haga correr
el programa paso a paso. Cuando llegue a la instrucción fsin, verá que en realidad se ejecuta la
instrucción fcos, por esta razón utilizamos la identidad trigonométrica,

sin(x) = cos(x − 90)

En segundo lugar. El coprocesador matemático trabaja en radianes, por tanto hay que convertir
los grados a radianes,
π
 
radianes = angulo
180◦
Finalmente el valor de sin(x) multiplicamos por 100 y redondeamos (frndint). Esto para dibujar un
pixel, que represente el punto (x,sin(x)), en una pantalla gráfica. El programa debe revisarse con
el Turbo Debbuger.
1.3. SEÑALES DEL PROCESADOR MATEMÁTICO, 8087 12

.386
.387
; program para calcular el sen ( x ) = cos (x -90)
; -----------------------------------------------------
pila segment para stack ' stack '
dw 30 dup (0)
pila ends
; -----------------------------------------------------
datos segment para ' data '
angulo dw 45
seno dw 0 ; seno ( angulo )
tem dw 0 ; Temporal
datos ends
; -----------------------------------------------------
codigo segment use16
program proc far
assume ss : pila , ds : datos , cs : codigo , es : datos
push ds
sub ax , ax
push ax
mov ax , datos
mov ds , ax
finit
fild angulo ; -+ angulo = angulo -90
mov tem ,90 ; |
fild tem ; |
fsubp st (1) , st ; -+
fldpi ; -+ angulo a radianes
fmulp st (1) , st ; |
mov tem ,180 ; |
fild tem ; |
fdiv ; -+
fsin ; sen ( x ) = cos (x -90)
mov tem ,100 ; -+ escalamos el resultado
fild tem ; | para graficar
fmulp st (1) , st ; |
frndint ; -+
fistp seno ; guarda sen ( x ) escalado
ret
program endp
; -----------------------------------------------------
codigo ends
end program

Figura 1.6: Programa que calcula sen(x)


Capítulo 8

Sistemas de computación

8.1 Normalización de la pantalla


El programa del listado 8.1 dibuja la función f (x) = sin(x); sin embargo, tiene el problema de
ubicar los puntos (x,sin(x)) en la pantalla. Esto se debe a que dibujar en el papel no es lo mismo
que dibujar en la pantalla. La pantalla ¡NO ES CONTÍNUA! Es como una rejilla de pixeles cuyo
origen está en la esquina superior izquierda (x=0,y=0) y, dependiendo de la resolución tiene un
tamaño finito. Por ejemplo: horizontalmente va de 0 a 320 (de izquierda a derecha) y verticalmente
va de 0 a 200 (de arriba hacia abajo).
.386
.387
; program para calcular el sen ( x ) = cos (x -90)
; -----------------------------------------------------
pila segment para stack ' stack '
dw 30 dup (0)
pila ends
; -----------------------------------------------------
datos segment para ' data '
angulo dw 45
seno dw 0 ; seno ( angulo )
tem dw 0 ; Temporal
datos ends
; -----------------------------------------------------
codigo segment use16
program proc far
assume ss : pila , ds : datos , cs : codigo , es : datos
push ds
sub ax , ax
push ax
mov ax , datos
mov ds , ax
finit
fild angulo ; -+ angulo = angulo -90
mov tem ,90 ; |
fild tem ; |
fsubp st (1) , st ; -+
fldpi ; -+ angulo a radianes
fmulp st (1) , st ; |
mov tem ,180 ; |
fild tem ; |
fdiv ; -+
fsin ; sen ( x ) = cos (x -90)
mov tem ,100 ; -+ escalamos el resultado

13
8.1. NORMALIZACIÓN DE LA PANTALLA 14

fild tem ; | para graficar


fmulp st (1) , st ; |
frndint ; -+
fistp seno ; guarda sen ( x ) escalado
ret
program endp
; -----------------------------------------------------
codigo ends
end program

Listado 8.1: Programa que calcula sin(x)

Este problema lo resolvemos de la siguiente manera: i) Identificamos el tamaño de los ejes ‘x’
e ‘y’, ii) restamos máximo menos mínimo para cada eje, iii) dividimos entre (máximo-mínimo)
y iv) multiplicamos por ancho y alto respectivamente. Esto implementamos matemáticamente a
continuación:
Para las abscisas Para las ordenadas
0 ≤ x ≤ 2π −1 ≤ y ≤ 1

xmin ≤ x ≤ xmax ymin ≤ y ≤ ymax

xmin − xmin ≤ x − xmin ≤ xmax − xmin ymin − ymin ≤ y − ymin ≤ ymax − ymin

x−xmin y−ymin
0≤ xmax −xmin ≤1 0≤ ymax −ymin ≤1

x−xmin y−ymin
0≤ xmax −xmin × ancho ≤ ancho 0≤ ymax −ymin × alto ≤ alto

Entonces para dibujar en la pantalla bautizamos a las abscisas como

x − xmin
 
xp = × ancho (8.1)
xmax − xmin
y a las ordenadas como

y − ymin
 
yp = × alto (8.2)
ymax − ymin
Por tanto cada punto (x,y) que calculemos, ajustaremos con las ecuaciones 8.1 y 8.2 para obtener los
puntos [xp ,yp ] y dibujarlos en la pantalla. Para continuar, además de ajustar el punto (x,y), debe-
mos trasladarlo; por ejemplo, de la esquina superior izquierda al centro de la pantalla y, también
podemos modificarlo de acuerdo a las necesidades de la interfaz de usuario; por ejemplo, márgenes
izquierdo y derecho como superior e inferior. Entonces tendríamos:

ancho = ancho - margen izquierdo - margen derecho (8.3)


y

alto = alto - margen superior - margen inferior (8.4)


Por ejemplo para el modo 13h (320x200x256) ancho = 320, alto = 200. Para dibujar la señal
sinusoidal: xmin=0 , xmax = 2π y ymin = −1, ymax = 1. Estos aspectos los implementamos en el
8.1. NORMALIZACIÓN DE LA PANTALLA 15

programa del listado 8.2.

Las ecuaciones 8.1 y 8.2 se implementan en el procedimiento ajusXY: ancho/(xmax − xmin ) y


alto/(ymax − ymin ) y, se guardan en las variables reales ajusX y ajusY. En el procedimiento senox
se calcula, [(y −ymin )/(ymax −ymin )]∗alto y en el procedimiento graf, se calcula [(x−xmin )/(xmax −
xmin )] ∗ ancho.

.386
.387
; Grafica la funcion y = sen ( x )
; -----------------------------------------------------
pila segment para stack ' stack '
dw 30 dup (0)
pila ends
; -----------------------------------------------------
datos segment para ' data '
maxX equ 360
minX equ 0
maxY equ 2
minY equ -2
ancho equ 320
alto equ 200
N equ 5 ; Cada N pixels un punto
DESX equ ancho ; despl azamient o abscisa
DESY equ alto ; de splazamie nto ordenada
GREEN = 10
WHITE = 07
YELLOW = 14
seriex dw maxX / N dup (0) ; angulos en el eje x
seriey dw maxX / N dup (0) ; sen ( angulo )
temI dw 0
temR dd 0 .0
vian dw 0
ajusX dd 0 .0 ; ancho /( Xmax - Xmin )
ajusY dd 0 .0 ; alto /( Ymax - Ymin )
datos ends
; -----------------------------------------------------
codigo segment use16
program proc far
assume ss : pila , ds : datos , cs : codigo , es : datos
push ds
sub ax , ax
push ax
mov ax , datos
mov ds , ax

call ajusXY ; ancho y alto


call vectors
call omovi
mov al ,13 h ; 320 x200x256
call pmovi
call ejes
call graf
mov ah ,08 h ; lee de teclado
int 21 h
mov ax , vian ; repone video original
call pmovi
ret
program endp
; ----- Ajusta x y sin ( x ) para dibujar en pantalla - - - - - -
; ------------------------------------------
ajusXY proc
8.1. NORMALIZACIÓN DE LA PANTALLA 16

mov temI , maxX


fild temI ; - -+ ancho
mov temI , minX ; | - - - - - - - - - - -
fild temI ; | ( Xmax - Xmin )
fsubp st (1) , st ; |
mov temI , ancho ; |
fild temI ; |
fxch st (1) ; |
fdivp st (1) , st ; |
fstp ajusX ; - -+ se guarda en ajusX
mov temI , maxY
fild temI ; - -+ alto
mov temI , minY ; | - - - - - - - - - - -
fild temI ; | ( Ymax - Ymin )
fsubp st (1) , st ; |
mov temI , alto ; |
fild temI ; |
fxch st (1) ; |
fdivp st (1) , st ; |
fstp ajusY ; - -+ se guarda en ajusY
ret
ajusXY endp
; - - - - - - - - - - - Calcula los vectores x y sin ( x ) - - - - - - - - - - -
; -------------------------------
vectors proc
mov cx , maxX / N ; numero de puntos a generar
mov si ,0
mov di ,0
mov ax ,0
SEXY1 :
mov seriex [ si ] , ax ; crea los vectores
call senox ; x y sen ( x )
add ax , N
inc si
inc si
loop SEXY1
ret
vectors endp
; - - - - - - - - - - - - - Calcula el seno de un angulo - - - - - - - - - - - -
; ----------------------------
senox proc
finit
fild seriex [ si ] ; -+ angulo = angulo -90
mov temI ,90 ; |
fild temI ; |
fsubp st (1) , st ; -+
fldpi ; -+ angulo a radianes
fmulp st (1) , st ; |
mov temI ,180 ; |
fild temI ; |
fdiv ; -+
fsin ; sen ( x ) = cos (x -90)
fstp temR
mov temI , minY ; - -+ (Y - Ymin )
fild temI ; | - - - - - - - - - - - x alto
fld temR ; | ( Ymax - Ymin )
fxch st (1) ; |
fsubp st (1) , st ; |
fld ajusY ; |
fmulp st (1) , st ; |
frndint ; |
fistp seriey [ si ] ; - - -+ guarda sen ( x )
ret
senox endp
; - - - - - - - - - - - - - - Dibuja la funcion sin ( x ) - - - - - - - - - - - - - - -
8.1. NORMALIZACIÓN DE LA PANTALLA 17

; ------------------------
graf proc
mov cx , maxX / N ; numero de puntos a graficar
mov si ,0
G01 : push cx
mov temI , minX ; - -+
fild temI ; |
fild seriex [ si ] ; |
fxch st (1) ; | (x - xmin )
fsubp st (1) , st ; | - - - - - - - - - - - x ancho
fld ajusX ; | ( Xmax - Xmin )
fmulp st (1) , st ; |
frndint ; |
fistp temI ; - -+ guarda el valor de x
mov cx , temI
add cx , ancho ; -+ d esplazam iento en 'x '
mov dx , seriey [ si ] ; | y en 'y '
neg dx ; |
add dx , alto ; -+
mov al , YELLOW
call punto
inc si
inc si
pop cx
loop G01
ret
graf endp
; - - - - - - - - - - - - - - - Obtiene modo de video - - - - - - - - - - - - - - - - -
; ---------------------
omovi proc
mov ah ,0 fh
int 10 h
mov vian , ax
ret
omovi endp
; - - - - - - - - - - - - - - - - - - - Pone modo de video - - - - - - - - - - - - - - - -
; ------------------
pmovi proc
mov ah ,00
int 10 h
ret
pmovi endp
; - - - - - - - - - - - - - - - - - - pone un pixel - - - - - - - - - - - - - - - - - - - - - -
; -------------
punto proc
mov ah ,0 ch
mov bh ,00 ; pagina 0
int 10 h
ret
punto endp
; - - - - - - - - - - - - - - - - Dibuja las coordenadas - - - - - - - - - - - - - - -
; ----------------------
ejes proc
mov cx ,0 ; Abscisa
mov dx , alto /2
mov al , WHITE
EJE1 : call punto
inc cx
cmp cx , ancho
jne EJE1
mov dx ,0 ; Ordenada
mov cx , ancho /2
mov al , WHITE
EJE2 : call punto
inc dx
8.2. ALGORITMO DE BRESENHAM 18

cmp dx , alto
jne EJE2
ret
ejes endp
; -----------------------------------------------------
codigo ends
end program

Listado 8.2: Sistema que dibuja en la pantalla la señal sin(x)

Cuyo resultado se muestra en la figura 8.1. Observe que no coincide en punto (0,0) de las coorde-
nadas con el cruce por cero de la función sin(x).

Figura 8.1: Resultado del sistema del listado 8.2

8.2 Algoritmo de Bresenham

Comenzamos con el pixel P1 p(x1p ,y1p ), luego seleccionamos los píxeles subsiguientes a medida que
avanzamos hacia la derecha, una posición de píxel a la vez, en la dirección horizontal hacia el
pixel P2 p(x2p ,y2p ). Una vez que se elige un píxel en cualquier paso, el siguiente píxel es el de su
derecha (que constituye un límite inferior para la línea) o el que está a su derecha y hacia arriba
(que constituye un límite superior para la línea) debido al límite de la pendiente m. La línea se
aproxima mejor por aquellos píxeles que caen a la menor distancia de su camino real entre P1p y P2p .
8.2. ALGORITMO DE BRESENHAM 19

∆X < 0 ∆X > 0
III ∆Y > 0 ∆Y > 0 II
∆X < ∆Y ∆X < ∆Y
y IV I
P2

– • P2 ∆X < 0 ∆X > 0
P2p ∆Y > 0 ∆Y > 0
∆X > ∆Y ∆X > ∆Y

yi+1 – T

• P1 x
t ∆X < 0 ∆X > 0
∆Y < 0 ∆Y < 0
s ∆X > ∆Y ∆X > ∆Y
P1
yi – • •
P1p S

p p p p p p x ∆X < 0 ∆X > 0 VIII


V ∆Y < 0
∆X < ∆Y
∆Y < 0
∆X < ∆Y
xi xi+1 VI VII
a) scan-converting a line b) ∆X y ∆Y en los 8 octantes
Figura 8.2: Planteo del algoritmo de Bresenham

Usando la notación de la figura 8.2a), las coordenadas del último píxel elegido al ingresar al paso i
son (xi , yi ). Debemos eligir el siguiente, entre el píxel inferior S y el píxel superior T. Si se elige S,
tenemos x[i+1] = x[i] + 1 y y[i+1] = y[i] , si se elige T, tenemos x[i+1] = x[i] + 1 y y[i+1] = y[i] + 1. La
coordenada y real de la línea en x = x[i+1] es y = mx[i+1] + b = m(x[i] + 1) + b. La distancia de S a
la línea real en la dirección y es s = y − y[i] . La distancia de T a la línea real en la dirección y es
t = (y[i] + 1) − y.

Ahora consideremos la diferencia entre estos dos valores de distancia: s − t. Cuando s − t < 0,
tenemos s < t y el píxel más cercano es S. Por el contrario, cuando s − t ≥ 0, tenemos s ≥ t y el
pixel más cercano es T. Esta diferencia es:

s−t = (y − y[i] ) − [(y[i] + 1) − y]


= 2y − 2y[i] − 1
= 2m(x[i] + 1) + 2b − 2y[i] − 1

Como m = ∆y/∆x, entonces ∆y = m∆x, tenemos:

∆y
s−t=2 (x + 1) + 2b − 2y[i] − 1
∆x [i]
De donde

∆x(s − t) = 2∆y(x[i] + 1) + 2b∆x − 2y[i] ∆x − ∆x

Si definimos una variable de decisión, dn[i] = ∆x(s − t), que tiene el mismo signo que (s − t), ya
que ∆x es positivo en nuestro caso, tenemos:

dn[i] = 2∆y · x[i] − 2∆x · y[i] + C donde C = 2∆y + ∆x(2b − 1)


8.2. ALGORITMO DE BRESENHAM 20

De manera similar, podemos escribir la variable de decisión dn(i+1) para el siguiente paso como

dn[i+1] = 2∆y · x[i+1] − 2∆x · y[i+1] + C

Luego

dn[i+1] − dn[i] = 2∆y(x[i+1] − x[i] ) − 2∆x(y[i+1] − y[i] )

Ya que x[i+1] = x[i] + 1, tenemos

dn[i+1] = dn[i] + 2∆y − 2∆x(y([i+1]) − y[i] )

Si el píxel elegido es el píxel superior T (es decir dn[i] ≥ 0), entonces y[i+1] = y[i] + 1 luego tenemos

dn[i+1] = dn[i] + 2(∆y − ∆x)

por otro lado, si el píxel elegido es el píxel inferior S (es decir dn[i] < 0), entonces y[i+1] = y[i] luego
tenemos

dn[i+1] = dn[i] + 2∆y

Por lo tanto tenemos

dn[i+1] = dn[i] + 2(∆y − ∆x) si dn[i] ≥ 0


dn[i+1] = dn[i] + 2∆y si dn[i] < 0

finalmente, calculamos dn[i] , el valor del caso base para esta fórmula recursiva, a partir de la
definición original de la variable de decisión dn[i] :

dn[i] = ∆x[2m(x[i] + 1) + 2b − 2y[i] − 1]


dn[i] = ∆x[2(mx[i] + b − y[i] ) + 2m − 1]

Ya que mx[i] + b − y[i] = 0, tenemos

dn[i] = 2∆y − ∆x

Para implementar el programa vamos a hacer: dnT = 2(DeltaY - DeltaX), dnS = 2DeltaY
y dni = 2DeltaY - DeltaX. En resumen, el algoritmo de Bresenham para convertir por barrido
una línea de P1p (x1p , y1p ) hasta P2p (x2p , y2p ) con las condiciones que: x1p < x2p y 0 < m < 1, el
algoritmo de Bresenham para el primer octante, ver figura 8.2b), puede implementarse como en la
figura 8.3.
8.2. ALGORITMO DE BRESENHAM 21

liBres proc
Bresenham mov ax , x1 ; x = x1
mov x , ax
x ← x1 mov ax , y1 ; y = y1
y ← y1 mov y , ax
mov ax , x2 ; -+
sub ax , x1 ; | > DeltaX = x2 - x1
DeltaX ← x2 - x1
mov DeltaX , ax ; -+
DeltaY ← y2 - y1
mov ax , y2 ; -+
sub ax , y1 ; | > DeltaY = y2 - y1
mov DeltaY , ax ; -+
dnS ← 2DeltaY
dni ← 2DeltaY - DeltaX shl ax ,1 ; 2 DeltaY
mov dnS , ax ; dnS = 2 DeltaY
sub ax , DeltaX ; 2 DeltaY - DeltaX
dnT ← 2(DeltaY - DeltaX) mov dni , ax ; dni = 2 DeltaY - DeltaX
mov ax , DeltaY ; -+
sub ax , DeltaX ; | > 2( DeltaY - DeltaX )
call putPix shl ax ,1 ; -+
mov dnT , ax ; dnT
libre2 :
x←x+1
call putPix
inc x ; x = x +1
no mov ax , x
x < x2 ret
cmp ax , x2 ; x < x2
si jnb libre0
cmp dni ,0 ; dni >= 0
no
dni ≥ x2 dni ← dni + dnS jnge libre1
inc y ; y = y + 1
si
mov ax , dnT ;
y←y+1 add dni , ax ; dn ( i +1) = dni + dnT
jmp libre2
libre1 :
dni ← dni + dnT
mov ax , dnS ;
add dni , ax ; dn ( i +1) = dni + dnS
jmp libre2
libre0 :
ret
liBres endp

Figura 8.3: Algoritmo de Bresenham cuando x1 < x2

En la figura 8.3 primero inicializamos la variable dn[i] y establecemos el píxel P1p . Durante cada
iteración del ciclo, incrementamos x a la siguiente posición horizontal, luego usamos el valor actual
de dn[i] para seleccionar el píxel inferior o superior (incrementar y) y actualizamos dn[i] , y al final
establecemos el píxel elegido.
En cuanto a las líneas que tienen otros valores de m, podemos hacer uso del hecho de que pueden
reflejarse horizontal, vertical o diagonalmente en este rango de ángulos de 0◦ a 45◦ , ver figura 8.2b).
Por ejemplo, una línea de (x1p ,y1p ) a (x2p ,y2p ) con −1 < m < 0 tiene una contraparte reflejada
horizontalmente de (x1p ,−y1p ) a (x2p ,−y2p ) con 0 < m < 1. Simplemente podemos usar el algoritmo
para escanear y convertir esta contraparte, pero negar la coordenada y al final de cada iteración
para establecer el píxel correcto para la línea. Para una línea cuya pendiente está en el rango de
45◦ a 90◦ , podemos obtener su contraparte reflejada intercambiando las coordenadas x e y de sus
extremos. Podemos escanear y convertir esta contraparte, pero debemos intercambiar x e y en la
llamada a putPix. Entonces el programa que implementa el algoritmo de Bresenham par los ocho
octantes es el de la figura 8.4
8.2. ALGORITMO DE BRESENHAM 22

liBres proc
Bresenham mov ax , x2
sub ax , x1
mov DtaX , ax ; DtaX = x2 - x1
DeltaX ← x2 - x1
mov ax , y2
DeltaY ← y2 - y1
sub ax , y1
libre4: mov DtaY , ax ; DtaY = y2 - y1
no ∆Y ← -∆Y cmp DtaY ,0 ; DtaY >= 0 ?
∆Y ≥ 0
inY ← -1 jnge libre4
si
mov inY ,1
libre1 : cmp DtaX ,0 ; DtaX >= 0 ?
inY ← 1
jnge libre5
libre1: mov inX ,1
libre5:
no ∆X ← -∆X libre2 : mov ax , DtaX
∆X ≥ 0 cmp ax , DtaY
inX ← -1
jnge libre5 ; DtaX >= DtaY?
si
mov inYr ,0 ; InYr = 0
inX ← 1 mov bx , inX
libre2:
libre6: mov inXr , bx ; inXr = inX
libre3 : mov ax , DtaY
no inXr ← 0
∆X ≥ ∆Y shl ax ,1 ; 2 DtaY
inYr ← inY
mov dnS , ax ; dnS = 2 DtaY
si sub ax , DtaX ; 2 DtaY - DtaX
inYr ← 0 ∆X ← ∆Y mov dni , ax ; dni = 2 DtaY - DtaX
inXr ← inX ∆Y ← ∆X sub ax , DtaX ; 2 DtaY -2 DtaX
mov dnT , ax ; dnT = 2 DtaY -2 DtaX
libre3: mov ax , x1
dnS ← 2DeltaY mov x , ax ; x = x1
dni ← 2DeltaY - DeltaX mov ax , y1
dnT ← 2(DeltaY - DeltaX) mov y , ax ; y = y1
jmp libre7
libre4 : neg DtaY ; Dtay = - DtaY
x ← x1 mov inY , -1 ; inY = -1
y ← y1 jmp libre1
libre5 : neg DtaX ; DtaX = - DtaX
libre7: mov inX , -1 ; inX -1
call putPix jmp libre2
libre5 : mov inXr ,0 ; inXr = 0
si si
mov bx , inY
x = x2 y = y2 ret mov inYr , bx ; inYr = inY
mov bx , DtaY
no
libre8: libre9: mov DtaX , bx ; DTaX = DtaY
no x ← x + inXr
mov DtaY , ax ; DtaY = DtaX
dni ≥ 0
y ← y + inYr jmp libre3
libre7 : call putPix
si mov ax , x
x ← x + inX dni ← dni + dnS cmp ax , x2 ; x = x2 ?
y ← y + inY jne libre8
mov ax , y
dni ← dni + dnT cmp ax , y2 ; y = y2 ?
jne libre8
ret
libre8 : cmp dni ,0 ; dni >= 0 ?
jnge libre9
mov ax , inX
add x , ax
mov ax , inY ; x = x + inX
add y , ax ; y = y + inY
mov ax , dnT
add dni , ax ; dn ( i +1) = dni + dnT
jmp libre7
libre9 : mov ax , inXr
add x , ax ; x = x + inXr
mov ax , inYr
add y , ax ; y = y + inYr
mov ax , dnS
add dni , ax ; dn ( i +1) = dni + dnS
jmp libre7
liBres endp

Figura 8.4: Algoritmo de Bresenham para todos los octantes, figura 8.2b)

También podría gustarte