jueves, 16 de mayo de 2013

Acceso a discos en lenguaje ensamblador

Programación de E/S de disco
 IBM ha programó  una interface para el manejo de discos que respetan las 
especificaciones ATA: la interrupción INT 13h.

Para especificar las direcciones se emplean los siguientes registros:

 CH: 8  bits  bajos  de los 10 bits empleados para especificar el 
     número de cilindro o pista.

 CL: 2 bits altos de  los 10 bits del número de cilindro o pìsta: 
     se almacenan en los bits 6 y 7. Los bits 0-5 son  los 6 bits 
     del número de sector.

 DH: Número de cabezal o lado. Sólo se usan los bits 0-3.

Para  indicar  error,  la  int13 pone en cero la bandera CF y regresa el 
código del error en el registro AH.

A continuación algunos servicios de la INT 13h:

 · RESTABLECER EL SISTEMA DE DISCO
      AH = 00h (servicio o número de función).
      DL = No.  de  unidad  (80h-FFh para discos duros, 128-255 decimal; 
    0-7Fh para u. de floppy).

   Este servicio reinicializa el controlador del disco: cuando se 
   acceda de nuevo a la unidad, se coloca el cilindro en cero. Se 
   emplea después de un error grave.

      Una operación válida pone en cero la bandera CF; un error activa CF 
      y regresa el código de error en AH. 


 · LEER ESTADO DEL DISCO
      AH = 01h (servicio o número de función).
      DL = No.  de  unidad  (80h-FFh para discos duros, 128-255 decimal; 
    0-7Fh para u. de floppy).

      La operación se usa para examinar el estado de las operaciones  de 
      disco más recientes.  Regresa en AL el código  de  estado,  que la 
      última operación puso seguramente en AH.


 · LEER SECTOR(ES)
      AH = 02h (servicio o número de función).
      AL = número de sectores a leer (Base 1: 1..255).
      CH = bits  0-7,  No. de cilindro o pista (LSB del No. de cilindro, 
    10-bits, 0-1023).
      CL = bits 6-7, No. de cilindro o pista.
      CL = bits 0-5, No. de sector inicial (Base 1: 1-63)
      DH = No. de cabezal o lado (Base 0: 0-15, con traducción puede ser 
    0-255).
      DL = No.  de  unidad (80h-FFh para discos duros,  128-255 decimal; 
    0-7Fh para u. de floppy).
      ES:BX = dirección del buffer E/S.

   Desde  una  unidad  de  disquete  hay que asegurarse de que un 
   pedido  de  lectura  no  cruce  el  límite  de  una página DMA 
   (dirección alineada 64K).  Es altamente indeseable que el BIOS 
   se  encargue  del  problema  por  tí.  La  manera más fácil de 
   cuidarse de esto es simplemente asegurarse de que la dirección 
   del buffer que usas esté  siempre  alineada  sobre  un  límite 
   igual al tamaño pedido. Si lees cuatro sectores de  512 bytes, 
   hay que alinear el buffer sobre el límite de 2KB.

      Ejemplo:
   _sector db 512 dup (?)  ; buffer
 ...
  mov ah, 2   ; petición de lectura
  mov al, 1   ; un sector
  lea bx, 5   ; puntero al buffer
  mov ch, 4   ; pista 4
  mov cl, 3   ; sector 3
  mov dh, 0   ; cabeza o lado 0
  mov dl, 0   ; unidad 0 (C)
  int 13h   ; llamada al BIOS


 · ESCRIBIR SECTOR(ES)
          AH = 03h (número de la función de escritura).

          El resto de los parámetros son  exactamente los mismos que los 
   usados para leer sectores.  Recuerda  que  se  debe  llenar el 
   buffer  apuntado  por  ES:BX  con  los  datos  que  se quieren 
   escribir.


 · OBTENER LOS PARÁMETROS DEL DISCO
          AH = 08h  (número  de la función para obener los parámetros de 
        la unidad).
          DL = número de unidad (igual que para leer/esribir sectores).

          Esta  función  regresará con  CF=0 si  la unidad es válida. Al 
   parecer,  no todos los BIOS activan o desactivan correctamente 
   el indicador CF. Cuando se intenta detectar el disco instalado, 
   se debe chequear el número de unidades regresadas en DL, cuando 
   se ha chequeado el primer disco con DL=80h.

          Esta función regresa el máximo de parámetros CHS en los mismos 
   registros en que ellos se pasaron a INT13, con las funciones o 
   servicios 02h y 03h:

   BL regresa el tipo de disco

          CH[0-7] y CL[6-7] regresa el valor máximo del cilindro (menor 
   o igual a 1023).

          CL[0-5] el máximo número de sectores, ya que los números de 
   sectores son con base uno, este valor es también una cuenta de 
   los sectores.

          DH regresa el máximo número de cabezales (0-255, una vez más, 
   el número de cabezales-1).

   DL regresa el No. de unidades conectadas al controlador del 
   disco.

   ES:DI regresa, para disquetes, la dirección de una tabla de 
   11 bytes con parámetros de la unidad de floppy:

  dbp struct
   dpbCONTROL_TIMERS     DW      ?
   dpbMOTOR_OFF_DELAY    DB      ? 
   dpbBYTES_PER_SECTOR   DB      ?
   dpbSECTORS_PER_TRACK  DB      ?
   dpbGAP_LENGTH         DB      ?
   dpbDATA_LENGTH        DB      ?
   dpbFORMAT_GAP_LENGTH  DB      ?
   dpbFORMAT_FILLER_BYTE DB      ?
   dpbHEAD_SETTLE_TIME   DB      ?
   dpbMOTOR_START_TIME   DB      ?
  dbb ends

  (dbp: disk parameter block - bloque de parámetros del
  disco)

          Notas:
          Es común excluir el último cilindro de la información regresada 
   por estas funciones. En los compatibles IBM PCs, este cilindro 
   se reserva para diagnósticos del fabricante. Si se escribe un 
   sistema operativo o una utilidad de partición, se podría dejar 
   como una opción al usuario incluir o no este cilindro en una 
   partición. Recordar siempre que la utilidad debería hacer una 
   lectura/escritura/verificación del cilindro antes de intentar 
   usarlo. 


 · CHEQUEO DE INSTALACIÓN DE EXTENSIONES INT13
          AH = 41h
          DL = número de unidad (igual que para leer/esribir sectores).
          BX = 055AAh

          Regresa:

          BX = 0AA55h (en orden invertido) y CF=0 si las extensones son 
          soportadas por la unidad.

          Esta llamada también regresa en CX información sobre las 
          subfunciones soportadas. 


 · LECTURA EXTENDIDA DE SECTOR(ES) 
          AH = 42h
          DL = número de unidad (igual que para leer/esribir sectores).
          DS:SI = puntero a la estructura del paquete pedido al disco 
   (disk request packet structure.)

          Formato del paquete requerido:

          BYTE                    Tamaño del paquete (10h)
          BYTE                    Reservado (00h)
          WORD                    Conteo de sectores
          WORD                    Desplazamiento (offset) del buffer
          WORD                    Segmento del buffer
          QWORD                   64-bits con la dirección del bloque 
      lógico 


 · ESCRITURA EXTENDIDA DE SECTOR(ES)
          AH = 43h
          DL = número de unidad (igual que para leer/esribir sectores).
          DS:SI = puntero a la estructura del paquete pedido al disco 
         (disk request packet structure.) Ee lo mismo que el 
  servicio anterior. 


 · OBTENCIÓN DE PARÁMETROS EXTENDIDOS DE LA UNIDAD (EXTENDED GET DRIVE 
   PARAMETERS)
          AH = 48h
          DL = número de unidad (igual para lectura/escritura de sectores).
          DS:SI = buffer para la estructura de parámetros de la unidad.

          (Estructura):

          WORD                    Tamaño del buffer, 1Ah, 1Eh or 42h, 
      depende de la versión de las 
      extensiones.
          WORD                    Indicadores (flags) de información
          DWORD                   # de cilindros físicos
          DWORD                   # de cabezales físicos
          DWORD                   # de sectores físicos
          QWORD                   64-bits con el número total de 
      sectores de la unidad.

         Notas:
          La  tabla  de  arriba  describe  los valores regresados por una 
   llamada en la versión 1.0.

          Mayor información sobre esta materia, puede encontrarase  en la 
   lista de interrupciones de Ralf Browns: 

      http://www-2.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html


------------------------------------
 · Ejemplo de código INT13: MBRREST
------------------------------------
Con  esta  información  ya podemos escribir nuestra rutina para  salvar el 
sector de arranque de un disco. El algoritmo es simple:

 · Creamos un archivo
 · Leemos el sector de arranque del disco en un buffer de 512 bytes
   (512 bytes es el tamaño de un sector)
 · Escribimos el contenido del buffer en el archivo
 · Cerramos el archivo

Para crear, leer y escribir un archivo en disco, emplearemos la  interface
suministradas  por  los  servicios  3Ch-40h de la interrupción 21h de DOS.
Quiere decir que este programa supone DOS.

Luego de la rutina para hacer un respaldo del sector de arranque del disco
seguiremos  con  otra  que  escriba  en el inicio del disco un archivo que
especifiquemos:

 · Abrimos un archivo para su lectura
 · Leemos de él un bloque de 512 bytes y lo ponemos en un buffer.
 · Copiamos el contenido del buffer en el primer sector del disco.
 · Revisamos si ya hemos copiado todo el contenido del archivo. Si
   ya lo hicimos, cerramos el archiv o y salimos; sino copiamos el
   siguiente bloque del archivo hasta que hallamos copiado todo el
   contenido del archivo e el disco 

Agregaremos también el código para obtener argumentos desde  la  línea  de
órdenes y crear un respaldo del sector de arranque original.  El  programa 
sólo hará el respaldo si no encuentra el fichero binario que se  escribirá 
en el primer sector del disquete.

; -----------------------------------------------------------------------
  TITLE MBRREST.ASM: Utilidad para escribir el MBR en una unidad de floppy
; ---------------------------------------------------------------------------------
 ; Para ensamblar con TASM:
 ; tasm mbrrest
 ; tlink /t mbrrest
 ; Esto producirá un archivo mbrrest.com
; ---------------------------------------------------------------------------------

 .model tiny

sectors_per_track equ 18
tracks_per_side  equ 80

 .code
 org 100h    
entry:
 lea     si, msg1         ; desplegar el texto de copyright :-)
 call _szDisplay
; ------------------------------------------------------------
; Obtener el nombre del archivo pasado en la línea de órdenes
; ------------------------------------------------------------
 mov si, 80h
 lodsb      ; obtener número de caracteres
 test al, al     ; ¿el número es cero?
 je no_file     ; si es cero saltar

 ; saltarse los espacios
is_space:
 lodsb      ; obtener el caracter
 cmp al, 20h     ; ¿es un espacio?
 je  is_space    ; saltar si es un espacio
 cmp al, 13h     ; se alcanzó el final de la línea
 je no_file     ; saltar si se llegó al final

 dec si     ; ajustar el puntero
 push si     ; salvar la dirección de la cadena
 ; buscar el final de la cadena
next: lodsb
 cmp al, 13     ; ¿se alcanzó el final de línea?
 jne next     ; revisar siguiente si no se alcanzó?

 ; poner cero al final de la cadena
 dec si     ; apuntar al final
 mov byte ptr [si], 0   ; poner cero al final
 pop si     ; restablecer la direción de la cadena
 jmp _with_file    ; proceder a hacer el respaldo

no_file:
 dec si
 mov byte ptr [si], 0   ; SI apunta a cero
_with_file:
; ---------------------------------
; Hacer un respaldo del MBR actual 
; ---------------------------------
 mov     ax, 0              ; reestablecer sistema de disco
 mov     dl, al     ; 0 para floppy, cambiar a 80h para
 int     13h     ; discos duros

 ; Verificar si se trabaja sobre una unidad de floppy: se lee en
 ; el sector 18 de la primera pista; si da error, no es una unidad
 ; de floppy de 1.44MB

 mov ah, 2     ; leer
 mov al, 1              ; un sector
 mov dh, 0              ; cabezal o lado 0
 mov dl, 0              ; disquete: unidad A
 mov ch, 0     ; pista 0
 mov cl, 18             ; sector 18
 lea     bx, buffer
 int 13h

 or ah, ah     ; Si ah=0, se pudo leer el sector 18 
 je is_1_44     ; y el disquete es de 1.44MB

 lea si, msg3    ; Si no es un disquete de 1.44MB
 call _szDisplay    ; desplegar el mensaje 3
 jmp close_file    ; cerrar el archivo y salir

is_1_44:

 ; Lectura CHS del MBR            

 mov     bp, 4              ; leer el sector de arranque 4 veces ...
read:       
 lea di, buffer    ; buffer para la transferencia de datos
 mov cx, 512     ; tamaño del buffer = bytes por sector
 call _ZeroMemory    ; Limpiar el buffer

 mov     ah, 2     ; leer
 mov al, 1              ; un sector
 mov     dh, 0              ; cabezal o lado 0
 mov dl, 0              ; disquete: unidad A (poner 80h para disco duro)
 mov ch, 0     ; pista 0
 mov     cl, 1              ; sector 1
 lea     bx, buffer
 int     13h 
 dec     bp  
 jnz     read

 ; Creación del fichero de respaldo

 mov     ax, 3C00h          ; crear el fichero de respaldo
 mov     cx, 0     ; atributo normal
 lea     dx, bu_file
 int     21h 
 mov     bx, ax             ; mover el handle del fichero a BX
            
 ; Escritura del buffer en el fichero de respaldo

 mov     ah, 40h            ; escribir el mbr en boot.bak
 mov     cx, 512     ; un sector = 512 bytes
 lea     dx, buffer
 int     21h 
 jc      error_writing
            
 ; Cierre del fichero de respaldo

 mov     ah, 3Eh           ; cerrar el archivo creado para respaldo
 int     21h 
 jmp     restore
            
error_writing:
 lea si, error_w
no_name:
 call _szDisplay
 int 20h

; -------------------------------------------------------------------------------

; ----------------------------
; Escribir el archivo en disco
; ----------------------------
restore:
 ; ¿Se pasó nombre de archivo como parámetro?

 lodsb
 test al, al
 jne @00

 ; Si no se pasó nombre, avisar y salir

 lea si, warning
 jmp no_name 

 ; Abrir fichero para lectura

@00: push si
 lea     si, msg2         ; desplegar el mensaje de aviso
 call _szDisplay
 pop si

 dec si
 mov     ax, 3D00h          ; abrir fichero indicado para su lectura
 mov     dx, si
 int     21h
 jc      error              ; error abriendo el fichero?
 mov     bx, ax             ; mover el handle del fichero a BX

 ; Obtener el tamaño del archivo

 mov ax, 4202h
 mov cx, 0
 mov dx, cx
 int 21h
 jc      error              ; error abriendo el fichero?

 ; Verificar si cabe en una unidad de floppy

 cmp dx, 016h
 jl good_for_floppy1

bad_for_floppy:
 lea si, msg3
 call _szDisplay
 jmp close_file    ; cerrar el fichero

error:
 lea     si, error_r        ; desplegar mensaje de error
 call    _szDisplay
 int     20h                ; regresar a DOS ...

good_for_floppy1:
 cmp dx, 015h
 jl good_for_floppy2
 cmp ax, 0F900h
 jg bad_for_floppy

 ; Obtener el No. de sectores del archivo

good_for_floppy2:
 mov cx, 200h
 div cx
 mov uSector, ax
 or dx, dx
 je rounded

 inc word ptr [uSector]

rounded:
 mov ax, 4200h
 mov cx, 0
 mov dx, cx
 int 21h
 jc      error              ; error abriendo el fichero?

 ; Leer el fichero

 mov track, al
 mov head, al
 inc ax
 mov sector, al

get_next_sector:
 lea di, buffer
 mov cx, 512
 call _ZeroMemory

 mov     ah, 3Fh            ; leer el fichero en el buffer
 mov     cx, 512
 lea     dx, buffer
 int     21h
 pushf
 or ax, ax
 je write_sector
 popf
 jnc write_sector    ; ¿hubo error?

 lea si, error_r    ; si hubo error,
 call    _szDisplay    ; avisar y
 jmp close_file    ; cerrar el fichero

write_sector:
 popf
 push ax

 ; Escritura del fichero en el disco

 mov     ax, 0             ; restablecer el sistema de disco
 mov     dl, 0     ; para disquete: mov dl, 0
 int     13h

 push bx
 mov     bp, 4              ; escribir el MBR cuatro veces ...
write:
 mov     ax, 0301h          ; AH: 3, escribir, AL: 1 sector 
 mov     dh, head
 mov dl, 0
 mov ch, track
 mov cl, sector
 lea     bx, buffer     ; dirección del buffer a escribir
 int     13h
 dec     bp
 jnz     write
 pop bx

 ; Si se llega al sector 18 (sectors_per_track), se escribe 
 ; en el siguiente track y se comienzade nuevo el conteo de
 ; sectores

 inc sector
 cmp sector, sectors_per_track
 jne count_sector

 mov sector, 1
 inc track

 ; Si se llega a la pista 80 (tracks_per_side), se lee en el
 ; otro lado del disquete

 cmp track, tracks_per_side
 jne count_sector
 inc side

 ; Si hay que leer el lado 3, se termina porque el disquete
 ; sólo tiene 2 lados

 cmp side, 3
 je close_file

count_sector:
 dec uSector     ; Disminuir conteo de sectores a copiar
 jne get_next_sector    ; Si no se llegó a cero, copiar el siguiente

 ; Cerrar el fichero

close_file:
 mov     ah, 3Eh            ; cerrar el fichero
 int     21h

 int     20h                ; regresar a DOS ...

 _szDisplay:
  push si
  _print_string:
   lodsb
   test al, al
   jz _exit_szDisplay
   mov bx, 000Fh                                                   
   mov ah, 0Eh                                                     
   int 10h
   jmp _print_string 
  _exit_szDisplay:
  pop si
  ret

 _ZeroMemory:
  push di
  mov al, 0
  rep stosb
  pop di
  ret
  file       db 'BOOT.BIN',0
  bu_file    db 'BOOT.BAK',0  
  msg1       db 'MBRREST Version 2.0 (c) 2002', 13, 10
             db '----------------------------', 13, 10
             db 'Haciendo respaldo del sector de arranque...', 13, 10, 0
  msg2      db 'Escribiendo archivo en el inicio del disco...', 13, 10, 0
  msg3      db 'No se trabaja con un disquete de 1.44 MB',13, 10, 0
  warning    db 'No se introdujo el nombre del archivo a escribir. ' 
      db 'No se escribir', 0A0h, 20h, 'en el disco', 13, 10, 0
  error_r    db 'Error leyendo el binario.', 13, 10, 0
  error_w    db 'ERROR: No se pudo escribir el sector de arranque a ' 
      db 'boot.bak', 13, 10, 0
  head      db 0
  track      db 0
  sector     db 0
  side      db 0
  uSector    dw 0
  buffer     db 512 dup (0)
END entry


 *NOTA*
 Nota que el archivo de respaldo simpre lleva el mismo nombre:
 "BOOT.BAK"  y  que  es  copiado  en  el  mismo directorio del 
 archivo copiado al disquete.  Esto quiere decir  que,  si  se
 va a restaurar  este  archivo,  *debe cambiársele el nombre*,
 sino  cuando  haga  el  respaldo  nuevo,   sobreescribirá  el 
 "BOOT.BAK" anterior y  el archivo que se  copiará  no será el
 anteriormente salvado. 


Para usar MBRREST, hay que usar la siguiente orden:

 MBRREST image.bin

donde " image.bin" es el  nombre de la imagen a copiar en los primeros
sectores  del disquete.  Si no se le pasa nombre, MBRREST sólo hará un
respaldo del primer sector del disquete.

Esta herramienta no te funcionará correctamente desde Windows 2000, ya
que protege contra escritura en discos.



 - Análisis de MBRREST - 
La primera observación que hay que hacer es que debemos obtener un  fichero
.COM, no .EXE. Un fichero .COM usa siempre modelo de memoria TINY: un único
segmento  para datos y código.  Tampoco es necesario definir una pila:  los
ejecutables  con  formato  COM generan automáticamente la pila.  Como estos 
archivos usan un segmento para código y datos,  su  tamaño está restringido
a 64KB. 

Cuando DOS monta el programa, pone en DS y CS la dirección del PSP (Prefijo 
de Segmento el Programa). El PSP es una pequeña base de datos de 256 (100h) 
bytes que construye DOS en el límite de un párrafo (16 bytes) en la memoria 
interna disponible cuando carga  un  programa  en  memoria,  inmediatamente 
después se cargará el programa  mismo.  El  PSP  también  forma  parte  del 
segmento único de 64 KB.

Como el programa comienza a ejecutarse inmediatamente después del PSP, debe
indicársele al ensamblador que genere el código a partir del desplazamiento
100h (256).  Esto se le indica con la directiva ORG, inmediatamente después
de iniciar el segmento de código con .code o con la directiva SEGMENT:

 .code
 ORG 100h

Los registros CS y DS ya tienen la dirección apropiada,  así que no hay que
iniciarlos como se hacía con los archivos .EXE.





jueves, 9 de mayo de 2013

Procedimientos y Macros

PROCEDIMIENTO


Un procedimiento es una colección de instrucciones relacionadas que realiza una
tarea específica. También un procedimiento puede contener un conjunto de instrucciones que
deseamos que se ejecuten en varias partes del programa. Los procedimientos del lenguaje
ensamblador tienen su contra parte en los lenguajes de alto nivel, por ejemplo, en el lenguaje C estos
procedimientos se llaman funciones. Aunque en los lenguajes de alto nivel, el mecanismo empleado
por los procedimientos para implementar su llamada, ejecución y regreso es transparente para el
usuario, en ensamblador se requiere entender ese mecanismo. Un componente indispensable
empleado por ese mecanismo es la pila del programa.


Definición de un procedimiento
La sintaxis de la definición de un procedimiento es la siguiente:
 proc nomProc
 proposición
 [proposición]
 ...
 endp [nomProc]
Las directivas proc y endp marcan el inicio y el final del procedimiento. No generan código.
nomProc es el nombre del procedimiento y etiqueta la primera instrucción del procedimiento.
Al menos una de las proposiciones de un procedimiento es la instrucción ret, la cual se describe
más adelante.


Llamada a un procedimiento
La llamada a un procedimiento normalmente tiene la siguiente forma:
 call nomProc
La instrucción call se describe más adelante. nomProc es el nombre que se le dio al procedimiento
al definirlo.


  • Ejemplo sobre procedimientos:










Macros:


Una macro es un conjunto de instrucciones asociadas a un identificador: el nombre de la macro.
Este conjunto de instrucciones es invocado como una sola instrucción o macro instrucción.
Normalmente las instrucciones de una macro se repiten varias veces en un programa o aparecen
frecuentemente en los programas. Para emplear una macro en un programa debemos de hacer dos
cosas: Definir la macro e invocar la macro.



La definición de una macro establece el nombre al que se asocia la macro, el número y nombre de
sus parámetros formales y qué instrucciones contiene la macro instrucción. La sintaxis de la
definición de una macro es la siguiente:


macro nomMacro [parForm[, parForm]...]
proposición
 [proposición]
 ...
endm [nomMacro]
donde las directivas macro y endm marcan el inicio y el final de la definición de la macro. No
generan código.
nomMacro es el nombre de la macro y se emplea al invocar la macro.
parForm es cada uno de los parámetros formales de la macro. Los parámetros permiten que una
misma macro opere sobre datos distintos.
proposición son cada una de las instrucciones que forman la macroinstrucción.
Aunque la definición de una macro puede ir en cualquier parte de un programa, el lugar más
recomendable para su localización es al principio de un archivo, antes de los segmentos de datos y
de código.
Al encontrar una invocación de una macro el macroensamblador, Turbo Assembler por ejemplo,
substituye la línea con la invocación por las proposiciones que contiene la definición de la macro.
Este proceso de substitución se conoce expansión de la macro. La sintaxis de la invocación de la
macro es similar a cualquier instrucción:
 nomMacro [parReal[, parReal]...]
donde cada parReal es conocido como un parámetro real de la macro. Al expandirse la macro
cada una de las ocurrencias de un parámetro formal en la definición de la macro se substituye por su
correspondiente parámetro real.
Los parámetros reales pueden ser símbolos, mnemónicos, directivas, palabras reservadas,
expresiones y números.


  • Ejemplo:











Actividad #11



  • Definición de procedimiento


Un procedimiento es un conjunto de instrucciones a los que podemos dirigir el flujo de nuestro programa, y una vez terminada la ejecución de dichas instrucciones se devuelve el control a la siguiente linea a procesar del código que mando llamar al procedimiento.

Los procedimientos nos ayudan a crear programas legibles y fáciles de modificar.

Al momento de invocar a un procedimiento se guarda en la pila la dirección de la siguiente instrucción del programa para que, una vez transferido el flujo del programa y terminado el procedimiento, se pueda regresar a la linea siguiente del programa original (el que llam— al procedimiento).

Sintaxis de un procedimiento

Existen dos tipos de procedimientos, los intrasegmentos, que se encuentran en el mismo segmento de instrucciones y los intersegmentos que pueden ser almacenados en diferentes segmentos de memoria.

Cuando se utilizan los procedimientos intrasegmentos se almacena en la pila el valor de IP y cuando se utilizan los intersegmentos se almacena el valor CS:IP

Para desviar el flujo a un procedimiento (llamarlo) se utiliza la directiva:

CALL NombreDelProcedimiento

Las partes que componen a un procedimiento son:


Declaración del procedimiento
código del procedimiento
Directiva de regreso
Terminación del procedimiento
Por ejemplo, si queremos una rutina que nos sume dos bytes, almacenados en AH y AL cada uno y guardar la suma en el registro BX:

Suma Proc Near ;Declaración del procedimiento
Mov Bx, 0 ;Contenido del procedimiento
Mov Bl, Ah
Mov Ah, 00
Add Bx, Ax
Ret ;Directiva de regreso
Suma Endp ;Declaración de final del procedimiento

En la declaración la primera palabra, Suma, corresponde al nombre de nuestro procedimiento, Proc lo declara como tal y la palabra Near le indica al MASM que el procedimiento es intrasegmento. La directiva Ret carga la dirección IP almacenada en la pila para regresar al programa original, por último, la directiva Suma Endp indica el final del procedimiento.

Para declarar un procedimiento intersegmento sustituimos la palabra Near por la palabra FAR.

El llamado de este procedimiento se realiza de la siguiente forma:

Call Suma



  • Definición de una macro


Una macro es un grupo de instrucciones repetitivas en un programa que se codifican solo una vez y pueden utilizarse cuantas veces sea necesario.

Sintaxis de una macro

Las partes que componen a una macro son:

Declaración de la macro
código de la macro
Directiva de terminación de la macro
La declaración de la macro se lleva a cabo de la siguiente forma:
NombreMacro MACRO [parametro1, parametro2...]

Aunque se tiene la funcionalidad de los parametros es posible crear una macro que no los necesite.

La directiva de terminación de la macro es: ENDM

Un ejemplo de macro, para colocar el cursor en alguna posición determinada de la pantalla es:

Posicion MACRO Fila, Columna
PUSH AX
PUSH BX
PUSH DX
MOV AH, 02H
MOV DH, Fila
MOV DL, Columna
MOV BH, 0
INT 10H
POP DX
POP BX
POP AX
ENDM

Para utilizar una macro solo es necesario llamarla por su nombre, como si fuera una instrucción mas del ensamblador, ya no son necesarias las directivas como en el caso de los procedimientos. Ejemplo:

Posicion 8, 6


  • Semejanzas y Diferencias entre Macro y Procedimiento:



La principal diferencia entre una macro y un procedimiento es que en la macro se hace posible el paso de parámetros y en el procedimiento no (esto es aplicable solo para el MASM, hay otros lenguajes de Programación que si lo permiten). Al momento de ejecutarse la macro cada parámetro es sustituido por el nombre o valor especificado al momento de llamarla.

Podemos decir entonces que un procedimiento es una extensión de un determinado programa, mientras que la macro es un módulo con funciones específicas que puede ser utilizado por diferentes programas.

Otra diferencia entre una macro y un procedimiento es la forma de llamar a cada uno, para llamar a un procedimiento se requiere el uso de una directiva, en cambio la llamada a las macros se realiza como si se tratara de una instrucción del ensamblador.

Las macros ofrecen una mayor flexibilidad en la Programación comparadas con los procedimientos, pero no por ello se dejarán de utilizar estos últimos.



Actividad #12



Actividad #12

En un lenguaje de su conocimiento debe Hacer un programa para imprimir 100 veces un mensaje

1. Sin usar procedimientos

// CicloMsge.cpp : Defines the entry point for the console application.

//


#include "stdafx.h"

#include <iostream>

using namespace std;

#include <stdio.h>

#include <time.h>



void main()

{

int i;

cout<<"Mensaje 100 veces \n";

for(i=0;i<10000;i++)

{

if(i==0)

{

const time_t timer = time(NULL);

printf("%s\n", ctime(&timer));

}else if(i==9999)

{

cout<<"\n";

const time_t timer = time(NULL);

printf("%s\n", ctime(&timer));


}else

{

int f=i+1;

cout<<"\nImpresion de mensaje # --> "<<f;

}


}

system("pause");

}

Screenshots:

Inicio de ejecución:






Finaliza










2. Usando llamadas a procedimientos


Código:




// PryoMensajes.cpp: define el punto de entrada de la aplicación de consola.

#include "stdafx.h"

#include <iostream>

using namespace std;

#include <stdio.h>

#include <time.h>


int i,f=0;


void time()

{

const time_t timer = time(NULL);

cout<<f<<" -";

printf("%s\n", ctime(&timer));

}


void imprimir()

{

cout<<"\n"<<f<<"- Impresion de mensaje # --> ";

}


void main()

{

cout<<"Mensaje 100 veces \n";

for(i=0;i<100;i++)

{

if(i==0)

{

time();

}else if(i==99)

{

time();

}else

{

imprimir();

}

f=i+1;

}

system("pause");

}


Screenshots:











miércoles, 10 de abril de 2013

Saltos


Screenshoots





Codigo

title jejgjne
.model small
.stack 64
.data
.code
start:
MOV AX,1111h
MOV BX,1112h
CMP AX,BX
JB tirapalante
HLT
tirapalante: DEC BX
CMP AX,BX
JNE Acaba 
JE Continua
Continua: DEC BX
CMP AX,BX
JE Acaba ; No son iguales, por tanto no saltará
JB Acaba ; No es menor, tampoco salta
JG Acaba ; Es mayor, ahora SI saltará
Acaba: XOR AX,AX.
End

lunes, 8 de abril de 2013

Programa 5-6


Codigo:

title codigo506(EXE)
.MODEL SMALL
.STACK 64
.CODE
BEGIN PROC FAR
MOV AL ,40H
SHL AL,1
MOV BL,22H
MUL BL
MOV AX ,4C00H
INT 21H
BEGIN ENDP
END BEGIN

Corriendo el programa en MASM:


Debug and Link del programa:







domingo, 7 de abril de 2013

Resumen Capitulo 7



ESCRITURAS DE PROGRAMAS .COM
Las ventajas de programas .COM están en que son mas pequeños que programas .EXE comparables y son más fáciles de adaptar para actuar como programas residentes de memoria.
DIFERENCIAS ENTRE  PROGRAMAS .COM Y .EXE
TAMAYO DEL PROGRAMA:
El .EXE puede ser de cualquier Tamaño, mientras que un programa .COM está restringido a un segmento y a un máximo de 62 k, incluyendo el PSP.
El PSP en un bloque de 256 bytes (100H) que el DOS inserte antes de los programas .COM y .EXE cuando se carga a la memoria.
Un programa .COM siempre es más pequeño que su contraparte .EXE; una razón es que el bloque de encabezado de 512 bytes a un programa  .EXE  no procede a un programa .COM.
SEGMENTOS
Es más fácil su uso en .COM que el .EXE
                SEGMENTO PILA.- en .EXE se define un segmento de pila, mientras que en .COM se genera automáticamente una pila. Cuando se escribe un programa en .COM se omite la definición de la pila. Si no se es suficiente los 64 k que se le asigna el ensamblador establece la pila fuera del programa, en la memoria superior.
                SEGMENTO DE DATOS.-en .EXE define un segmento de datos e inicializa el registro DS con la dirección de ese segmento. Como este definido dentro del segmento de código, tampoco tiene que definir el segmento de datos.
                SEGMENTO DE CÓDIGO.-  en .COM completo combina el PS, la pila, el segmento de datos y el segmento de código es un segmento de código de un máximo de 64k.
INICIALIZACIÓN
Cuando el DOS carga un programa.COM para ejecución, inicializa de forma automática todos los registros de segmentos con la dirección de PSP. Ya que los registros CS yDS contendrán las direcciones de segmento inicial correcta, su programa no tiene que cargarlos.
La dirección ORG le indica al ensamblador que empiece la generación del código objeto en un desplazamiento de 100H bytes pasando el inicio del PSP, en donde el programa .COM real inicia.



CONVERSIÓN A FORMATO .COM
Si su programa fuente ya está escrito en formato .EXE , puede utilizar un editor para convertir las instrucciones a formato .COM. Los formatos MASM Y TASM para .COM son idénticos. Cuando la conversión a .COM esta completa, puede borrar los archivos .OBJ y .EXE.
Conversión con Microsoft
Para ambos programas, con MASM de Microsoft se ensambla y produce un archivo .OBJ y después se enlaza para producir .EXE.
Si escribió el programa para ejecutarse como un programa .COM, el enlazador produce un mensaje:

Suponiendo que EXE2BIN está en la unidad por omisión, y que el archivo enlazado llamado CALC.EXE está en la unidad D, teclee:

EJEMPLO DE UN PROGRAMA .COM
*No existen definidos una pila o un segmento de datos.
*Un enunciado ASSUME le indica al ensamblador que inicie los desplazamientos desde el inicio del segmento de código.
*ORG 100H establece un desplazamiento para inicio de ejecución.
*Una instrucción JMP transfiere el control a la ejecución pasando los datos.
  
LA PILA DE .COM

Aquí están los pasos  para convertir el programa MASM y TASM:

Los programas .EXE y .COM son de 792 bytes y de 24 bytes, respectivamente
PILAS DE .COM
Para un programa .COM,el DOS define de manera automática una pila y establece la misma dirección de segmento de 64k para el programa es suficientemente grande, Dos establece la pila al final del segmento y carga el registro SP con FFFEH, a parte superior de la pila (tope de la pila).
Su programa es grande, o si la memoria está limitada, debe tener cuidado al enviar palabras a la pila. El comando DIR indicada el tamaño de un archivo y dará una idea del espacio disponible para una pila.

SUGERENCIAS PARA LA DEPURACIÓN
Si ejecuta un programa .COM con el DEBUG, utilice D CS: 100 para ver los datos e instrucciones. No siga el programa hasta su terminación; en lugar de eso, utilice el comando Q de DEBUG.
PUNTOS CLAVE
·         Un programa .COM está restringido a un segmento de 64k.
·         Un programa .COM es las pequeño que su programa.EXE contraparte.
·         Un programador escritorio para correr como .COM no define una pila o un segmento de datos ni inicializa el registro DS.

martes, 5 de marzo de 2013

Instrucciones Principales de lenguaje Ensablador

Instrucciones Principales de lenguaje Ensablador


Las instrucciones de transferencia de datos son las siguientes:

· MOV: transfiere

· XCHG: intercambia

· IN: entrada

· OUT: salida

· XLAT: traduce usando una tabla

· LEA: carga la dirección efectiva

· LDS: carga el segmento de datos

· LES: carga el segmento extra

· LAHF: carga los indicadores en AH

· SAHF: guarda AH en los indicadores

· PUSH: FUENTE (sp) ¬ fuente

· POP: DESTINO destino ¬ (sp)

Control de Bucles (instrucciones simples)

Éstas posibilitan el grupo de control más elemental de nuestros programas. Un bucle es un bloque de código que se ejecuta varias veces. Hay 4 tipos de bucles básicos:

o Bucles sin fin

o Bucles por conteo

o Bucles hasta

o Bucles mientras

Las instrucciones de control de bucles son las siguientes:

· INC: incrementar

· DEC: decrementar

· LOOP: realizar un bucle

· LOOPZ,LOOPE: realizar un bucle si es cero

· LOOPNZ,LOOPNE: realizar un bucle si no es cero

· JCXZ: salta si CX es cero

 

Instrucciones de Prueba, Comparación y Saltos.

Este grupo es una continuación del anterior, incluye las siguientes instrucciones:

· TEST: verifica

· CMP: compara

· JMP: salta

· JE, JZ: salta si es igual a cero

· JNE, JNZ: salta si no igual a cero

· JS: salta si signo negativo

· JNS: salta si signo no negativo

· JP, JPE: salta si paridad par

· JNP, JOP: salta si paridad impar

· JO: salta si hay capacidad excedida

· JNO: salta si no hay capacidad excedida

· JB, JNAE: salta si por abajo (no encima o igual)

· JNB, JAE: salta si no está por abajo (encima o igual)

· JBE, JNA: salta si por abajo o igual (no encima)

· JNBE, JA: salta si no por abajo o igual (encima)

· JL, JNGE: salta si menor que (no mayor o igual)

· JNL, JGE: salta si no menor que (mayor o igual)

· JLE, JNG: salta si menor que o igual (no mayor)

· JNLE, JG: salta si no menor que o igual (mayor)

Instrucciones de Llamado y Retorno de Subrutinas.

Para que los programas resulten eficientes y legibles tanto en lenguaje ensamblador como en lenguaje de alto nivel, resultan indispensables las subrutinas:

· CALL: llamada a subrutina

· RET: retorno al programa o subrutina que llamó

Instrucciones Aritméticas.

Estas instrucciones son las que realiza directamente el 8086/8088

a. Grupo de adición:

· ADD: suma

· ADC: suma con acarreo

· AAA: ajuste ASCII para la suma

· DAA: ajuste decimal para la suma

b. Grupo de sustracción:

· SUB: resta

· SBB: resta con acarreo negativo

· AAS: ajuste ASCII para la resta

· DAS: ajuste decimal para la resta

c. Grupo de multiplicación:

· MUL: multiplicación

· IMUL: multiplicación entera

· AAM: ajuste ASCII para la multiplicación

d. Grupo de división:

· DIV: división

· IDIV: división entera

· AAD: ajuste ASCII para la división

e. Conversiones:

· CBW: pasar octeto a palabra

· CWD: pasar palabra a doble palabra

· NEG: negación

f. Tratamiento de cadenas:

Permiten el movimiento, comparación o búsqueda rápida en bloques de datos:

· MOVC: transferir carácter de una cadena

· MOVW: transferir palabra de una cadena

· CMPC: comparar carácter de una cadena

· CMPW: comparar palabra de una cadena

· SCAC: buscar carácter de una cadena

· SCAW: buscar palabra de una cadena

· LODC: cargar carácter de una cadena

· LODW: cargar palabra de una cadena

· STOC: guardar carácter de una cadena

· STOW: guardar palabra de una cadena

· REP: repetir

· CLD: poner a 0 el indicador de dirección

· STD: poner a 1 el indicador de dirección

Instrucciones Lógicas.

Son operaciones bit a bit que trabajan sobre octetos o palabras completas:

· NOT: negación

· AND: producto lógico

· OR: suma lógica

· XOR: suma lógica exclusiva

Instrucciones de Desplazamiento, Rotación y Adeudos.

Básicamente permiten multiplicar y dividir por potencias de 2

· SHL, SAL: desplazar a la izquierda (desplazamient o aritmético)

· SHR: desplazar a la derecha

· SAR: desplazamiento aritmético a la derecha

· ROL: rotación a la izquierda

· ROR: rotación a la derecha

· RCL: rotación con acarreo a la izquierda

· RCR: rotación con acarreo a la derecha

· CLC: borrar acarreo

· STC: poner acarreo a 1

Instrucciones de Pila.

Una de las funciones de la pila del sistema es la de salvaguardar (conservar) datos (la otra es la de salvaguardar las direcciones de retorno de las llamadas a subrutinas):

· PUSH: introducir

· POP: extraer

· PUSHF: introducir indicadores

· POPF: extraer indicadores

Instrucciones de Control del microprocesador.

Hay varias instrucciones para el control de la CPU, ya sea a ella sola, o en conjunción con otros procesadores:

· NOP: no operación

· HLT: parada

· WAIT: espera

· LOCK: bloquea

· ESC: escape

Instrucciones de Interrupción.

· STI: poner a 1 el indicador de interrupción

· CLI: borrar el indicador de interrupción

· INT: interrupción

· INTO: interrupción por capacidad excedida (desbordamiento)

· IRET: retorno de interrupción