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.




Porque desactiváis la opción de copiar las páginas web, programáis códigos inútiles, acaso no sabéis que mientras exista buscadores que presenten el código fuente de las paginas siempre será posible copiar cualquier pagina web de hecho tengo una copia de esta página solo como reto personal para verificar esta posibilidad, aunque realmente mientras se siga en la línea de código interpretado como es el HTML y no código compilado es inútil evitar que cualquier programador no necesariamente hábil copie sin problemas cualquier pagina, es mas alentáis a esa posibilidad solo por el hecho de prohibir es un reto que dejáis a la imaginación humana
ResponderEliminarPermiteme, te aplaudo de pie, congrats ;)
EliminarTan fácil como es CTRL + U, y ya lo copias
ResponderEliminar:V
ResponderEliminarqe sonso, ya lo copie todo
ResponderEliminar