home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1995 November
/
PCWK1195.iso
/
inne
/
podstawy
/
dos
/
format
/
2m30src.exe
/
2MKERNEL.INC
< prev
next >
Wrap
Text File
|
1995-03-06
|
82KB
|
1,964 lines
;┌───────────────────────────────────────────────────────────────────┐
;│ │
;│ █████ █ █ █ █ █▀▀▀▀ █▀▀▄ █ █ █▀▀▀▀ █ │
;│ █ ██ ██ █ █ █ █ █ ██ █ █ █ │
;│ █████ █ █ █ ██▄ █████ ████ █ █ █ █████ █ │
;│ █ █ █ █ █ █ █ █ █ ██ █ █ │
;│ █████ █ █ █ █ █▄▄▄▄ █ █ █ █ █▄▄▄▄ █████ │
;│ │
;│ 2MKERNEL 3.0 - (C) 1993-1995 Ciriaco García de Celis. │
;│ │
;│ NUCLEO RESIDENTE DE 2M UTILIZADO POR SUS PRINCIPALES EJECUTABLES │
;│ │
;│ │
;│ Los siguientes símbolos se utilizan │
;│ para el ensamblaje condicional: │
;│ │
;│ XT -> Indica que el código ejecutable es para PC/XT y no posee │
;│ instrucciones de 286 ni utiliza recursos hardware de AT. │
;│ │
;│ SUPERBOOT -> Indica que el código ejecutable se ensambla para │
;│ ocupar 2560 bytes exactamente (para autoarranque). │
;│ │
;└───────────────────────────────────────────────────────────────────┘
; ------------ Códigos de modos y órdenes del DMA y del FDC.
F_READ EQU 46h ; modo DMA para lectura
F_WRITE EQU 4Ah ; modo DMA para escritura
F_VERIFY EQU 42h ; modo DMA para verificación
F_FORMAT EQU 01001101b ; orden de formateo del FDC
FD_STATUS EQU 3F4h ; registro de estado
FD_DOR EQU 3F2h ; registro de salida digital
FD_DCR EQU 3F7h ; registro de control disco
; ------------ Estructura de datos con información para cada unidad.
info_drv STRUC
maxs EQU 13 ; máximo 13 sectores físicos/pista
tipo_drv DB ? ; tipo de la disquetera (0 = no hay)
control2m_flag DB OFF ; a ON si 2M controla la unidad
cambio DB ON ; a ON indica cambio de soporte
version_fmt DB ? ; versión del formato de disco 2M
multi_io DB ? ; a 0 si posible acceso multi-sector
chk DB ? ; a 0 si checksum del sector 0 Ok
vunidad EQU THIS WORD
vunidad0 DB ? ; velocidad pista 0
vunidadx DB ? ; velocidad demás pistas
gap DB ? ; GAP entre sectores (leer/escribir)
sectpista DB ? ; sectores lógicos por pista
tabla_tsect DB maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
tam_fat DB ? ; sectores/FAT en la unidad
ENDS
; ------------ Variables del programa.
info_ptr DW info_A ; punteros a datos de las unidades
DW info_B
IFDEF SUPERBOOT
DB "30" ; Versión SuperBOOT 3.0
ENDIF
id_sistema DB "2M-STV" ; identificación de disco 2M
IFDEF XT
tbase DW ? ; base de tiempos para retardos
ENDIF
unidad DB ? ; unidad física de disco en curso
numsect DW ? ; sectores a transferir
sectini DW ? ; primer sector DOS a transferir
cilindro DB ? ; cilindro del disco a acceder
cabezal DB ? ; cabezal a emplear
sector DB ? ; número de sector físico
sector_ini DB ? ; número de sector físico inicial
sector_fin DB ? ; número de sector físico final
seccion DB ? ; parte del sector físico en curso
secciones DB ? ; sectores lógicos a transferir
tsector DB ? ; LOG2 (tamaño de sector) - 7
buffer DW buffer_io ; puntero al buffer intermedio
buf_unidad DB ? ; unidad del sector en el buffer
buf_cilcab DW ? ; cilindro/cabezal de sector buffer
buf_sector DB ? ; número de sector en el buffer
status DB ? ; resultado de los accesos a disco
fdc_result DB 7 DUP (?) ; bytes de resultados del FDC
orden DB ? ; operación F_READ/F_WRITE/F_VERIFY
tab_ordenes DB F_READ
DB F_WRITE
DB F_VERIFY ; órdenes 2, 3 y 4
; --- Interpretación BIOS de los bits de ST1
lista_errs DB 4 ; 'sector not found'
DB 0
DB 10h ; 'bad CRC'
DB 8 ; 'DMA overrun'
DB 0
DB 4 ; 'sector not found'
DB 3 ; 'write-protect error'
DB 2 ; 'address mark not found'
DB 20h ; en otro caso: 'bad NEC'
info_A info_drv <> ; datos de A:
info_B info_drv <> ; datos de B:
; ***************************************
; * *
; * C O D I G O R E S I D E N T E *
; * *
; ***************************************
; ------------ Rutina de gestión de INT 2Fh.
IFNDEF SUPERBOOT ; Código SuperBOOT no soporta INT 2Fh
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; sí llama: darle información
LEA DI,autor_nom_ver
ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"
IRET
ges_int2F ENDP
ENDIF
; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
; original o a una nueva rutina de control para la
; lectura (AH=2), escritura (AH=3) y verificación (AH=4)
; según el tipo de disco introducido. Ante una función de
; formateo (AH=5) no 2M, se pasa el control a la INT 13h
; original. Se detecta un posible cambio de disco y se
; retorna en ese caso con el correspondiente error. En el
; código SuperBOOT no hay soporte para formatear.
IFNDEF SUPERBOOT
ges_int13 PROC FAR
STI
CLD
PUSHF
CMP DL,2
JAE ges13bios ; no es disquetera A: ó B:
PUSH SI
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?
ges_2m: POP SI
JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
CALL detecta_cambio ; ¿cambio de disco?
JNC sin_cambio
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
sin_cambio: CMP AH,5
JNE dilucida ; no es orden de formateo
CALL leer_lin_camb
JNZ format_bios ; no hay disquete en la unidad
CMP AL,7Fh
JNE format_bios ; no es orden formateo de 2M
CMP SI,"2M"
JE format_2m ; es orden de formateo de 2M
format_bios: CLC
CALL set_flag_STV ; CF = 0 -> indicar no 2M
dilucida: PUSH SI
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].control2m_flag,OFF
POP SI
JE ges13bios ; la unidad la controla la BIOS
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POPF
JMP CS:ant_int13 ; saltar al gestor de INT 13h
; --- Función de formateo implementada por 2M. En los
; disquetes creados con /M todas las pistas salvo
; la 0 deberían ser formateadas invocando INT 13h
; de manera directa (con CALL) para evitar que se
; ejecute cierto código de WINDOWS en el modo
; protegido que provoca errores al formatear. Antes
; de formatear la primera pista física del disco se
; invoca la función de formateo de la INT 13h
; original con objeto de informar al DOS y a todos
; los TSR previos del cambio de soporte.
; El intento de formateo en la pista/cabezal 0 con
; CL=255 sirve para simular un cambio de disco.
format_2m: POPF
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv
MOV cilindro,CH
MOV cabezal,DH
OR CH,DH
JNZ format_trx ; no es cilindro 0 y cabezal 0
INC CL
JNZ no_f_chg
MOV [SI].cambio,ON ; simular cambio de disco
JMP fmt_exit
no_f_chg: XPUSHA
PUSHF ; formatear (AH=5) para
CALL ant_int13 ; avisar al DOS del nuevo disco
CLD ; mantener DF=0
STC
CALL reset_drv ; asegurar aceleración motor
XPOPA
CALL set_info ; características nuevo soporte
format_trx: CALL genera_info ; tabla de información formateo
CALL motor_ok ; asegurar que está en marcha
CALL seek_drv
CALL formatea_pista
PUSHF
CLC
CALL motor_off_cnt ; cuenta normal detención motor
POPF
CALL set_err
CALL set_bios_err ; no altera flags
fmt_exit: XPOPA ; **
MOV AH,status
POP DS ; *
RET 2
ges_int13 ENDP
ELSE ; El código SuperBOOT no formatea
ges_int13 PROC FAR
STI
CLD
PUSHF
PUSH SI
CMP DL,2
JAE ges13bios ; no es disquetera A: ó B:
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?
ges_2m: JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
JNE no_format
CALL set_flag_STV ; CF = 0 -> "disco no 2M"
JMP ges13bios
no_format: CALL detecta_cambio ; ¿cambio de disco?
JNC dilucida
POP SI
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
dilucida: CMP CS:[SI].control2m_flag,OFF
JE ges13bios ; la unidad la controla la BIOS
POP SI
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POP SI
POPF
JMP CS:ant_int13 ; saltar al gestor de INT 13h
ges_int13 ENDP
ENDIF
; ------------ A la entrada en DL se indica la unidad y a la salida se
; devuelve SI apuntando sus variables sin alterar flags.
set_SI_drv PROC
PUSHF
PUSH BX
MOV BL,DL
MOV BH,0
SHL BX,1
MOV SI,CS:[BX+OFFSET info_ptr]
POP BX
POPF
RET
set_SI_drv ENDP
; ------------ Si CF=1, indicar disquete 2M presente. A la
; entrada, DL indica la unidad de disco.
set_flag_STV PROC
XPUSHA
CALL set_SI_drv
MOV AL,ON ; indicar 2M
JC tipo_stv_ok
MOV AL,OFF ; indicar no 2M
tipo_stv_ok: MOV CS:[SI].control2m_flag,AL
XPOPA
RET
set_flag_STV ENDP
; ------------ Devolver ZF=1 si cilindro y cabezal 0.
pista0? PROC
PUSH AX
MOV AL,cabezal
OR AL,cilindro
POP AX
RET
pista0? ENDP
; ------------ Devolver ZF=1 si la línea de cambio de disco está
; inactiva. A la entrada, DL contiene la unidad. El
; motor es puesto en marcha y, si no lo estaba ya, la
; variable que indica lo que resta para detenerlo
; es llevada a su valor normal, por lo que el disco no
; tardará mucho en detenerse (incluso sin quizá haber
; acelerado aún). En la práctica, invocando esta rutina
; desde INT 13h nunca será necesario arrancar el motor
; ya que el DOS ejecuta antes la función equivalente,
; la 16h, que lo pone en marcha. Es simplemente una
; medida de seguridad contra las BIOS «de marca».
leer_lin_camb PROC
XPUSHA ; *
PUSH DS
DDS
MOV AL,1
MOV CL,DL
SHL AL,CL ; bit de motor en 0..3
TEST DS:[3Fh],AL
JNZ rodando ; el motor ya está girando
CLC
CALL motor_off_cnt ; cuenta normal detención motor
rodando: MOV AH,DL
XSHL AH,4
OR AH,AL ; AH = byte BIOS
XSHL AL,4
OR AL,00001100b ; modo DMA, no hacer reset
OR AL,DL ; AL para reg. salida digital
MOV DX,FD_DOR
CLI
MOV DS:[3Fh],AH ; actualizar variable BIOS
OUT DX,AL ; arrancado motor en la unidad
ADD DX,5
DELAY
IN AL,DX ; leer línea de cambio de disco
STI
TEST AL,80h ; ZF=0 -> cambio de disco
POP DS
XPOPA ; *
RET
leer_lin_camb ENDP
; ------------ Determinar si ha habido cambio de disco y, en ese caso,
; si el nuevo disquete es de tipo 2M o no. El cambio de
; disco se detecta leyendo la línea de cambio de disco o
; chequeando la variable que indica si ha habido cambio
; o no (esta variable está a ON tras instalar 2M para
; forzar la detección del tipo de disco introducido; se
; pone en ON también si no se logra bajar la línea de
; cambio de disco por si fuera un soporte raro y la BIOS
; sí lo lograra -forzando así una detección posterior-).
detecta_cambio PROC
XPUSHA ; *
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].cambio,ON ; ¿cambio de soporte?
MOV CS:[SI].cambio,OFF
JE hubo_cambio
CALL leer_lin_camb ; leer línea de cambio de disco
JNZ hubo_cambio
XPOPA
CLC ; no hay cambio de disco
RET
hubo_cambio: CLC
CALL set_flag_STV ; CF = 0 -> supuesto no 2M
XPUSH <DS, ES> ; **
MOV BX,90h
ADD BL,DL
DDS
AND BYTE PTR [BX],255-16 ; densidad no determinada
XPUSH <CS, CS>
XPOP <DS, ES>
MOV unidad,DL
STC ; asegurar motor en marcha
CALL reset_drv
MOV cilindro,1
MOV cabezal,0
CALL seek_drv ; bajar línea cambio de disco
DEC cilindro
CALL seek_drv
CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL leer_lin_camb ; ¿bajada línea cambio disco?
JZ disco_dentro ; se pudo: hay disco dentro
MOV [SI].cambio,ON ; futura detección tipo disco
CLC ; NO indicar cambio de disco...
JMP fin_detecta ; ...para pasar control a BIOS
disco_dentro: PUSH DS
DDS
MOV BYTE PTR DS:[41h],6 ; error 'media changed'
POP DS
IFNDEF SUPERBOOT
CMP AH,5 ; ¿función de formateo?
JE fin_detecta_c ; no perder el tiempo
ENDIF
MOV buf_unidad,-1 ; invalidar buffer
MOV [SI].gap,20 ; GAP provisional
MOV CX,3 ; 3 intentos
intenta_io0: PUSH CX
CMP CX,2 ; CF=1 la 3ª vez (a 0 si CX<>1)
CALL reset_drv
MOV [SI].vunidad0,0 ; empezar con 500 Kbit/seg.
intenta_io: MOV AL,0
MOV cilindro,AL
MOV cabezal,AL
MOV sector,1 ; sector de arranque
MOV seccion,AL
MOV secciones,1
MOV orden,F_READ
MOV DI,buffer
CALL direct_acceso
JNE otra_densidad ; es otra densidad de disco
POP CX
MOV BX,buffer
CALL set_info ; características nuevo soporte
CLC
JMP fin_detecta_c ; indicar cambio de disco
otra_densidad: MOV AL,[SI].vunidad0
INC AX ; próxima velocidad
CMP AL,3
JA otro_intento
MOV [SI].vunidad0,AL
JMP intenta_io ; probar otra velocidad
otro_intento: MOV [SI].vunidad0,0
POP CX
LOOP intenta_io0 ; reintento
fin_detecta_c: STC ; indicar cambio de disco
fin_detecta: XPOP <ES, DS> ; **
XPOPA ; *
RET
detecta_cambio ENDP
; ------------ Anotar la información del disquete si es de tipo 2M.
; A la entrada, DS:SI apunta a las variables de la unidad
; y ES:BX al sector de arranque del disco. Se actualiza
; también la variable BIOS de tipo de densidad (la BIOS
; no se da cuenta del cambio de disco y conviene ayudar).
set_info PROC
XPUSHA
CALL calc_chk
JC set_info_exit ; no es disco 2M
MOV [SI].chk,AL ; anotar checksum
MOV [SI].version_fmt,CL ; y versión del formato
MOV DL,unidad
STC
CALL set_flag_STV ; CF = 1 -> indicar disco 2M
MOV AL,ES:[BX+22] ; tamaño de FAT
MOV [SI].tam_fat,AL
MOV CL,ES:[BX+65] ; CL a 0 si acceso multi-sector
MOV [SI].multi_io,CL
MOV AX,ES:[BX+66]
MOV [SI].vunidad,AX ; velocidad pista 0 / demás
MOV AL,ES:[BX+24]
MOV [SI].sectpista,AL ; sectores/pista
MOV DI,ES:[BX+72]
MOV AL,ES:[BX+DI+1] ; GAP de formateo
MOV AH,AL
AND CL,CL ; CL a 0 si acceso multi-sector
JZ gap_rw_ok ; GAP R/W para /F
ADD AH,190
MOV AL,11
MUL AH ; AX = (190+GAP)*11
SUB AX,2048+62
gap_rw_ok: SHR AL,1 ; GAP R/W para /M
MOV [SI].gap,AL
MOV CX,maxs
MOV DI,ES:[BX+74]
ADD DI,BX
LEA BX,[SI].tabla_tsect
genera_ts: MOV AL,ES:[DI]
MOV [BX],AL
INC BX
INC DI
LOOP genera_ts ; información estructura pistas
set_info_exit: MOV AL,[SI].vunidad0
IFDEF XT
MOV CL,6
SHL AL,CL
ELSE
XSHL AL,6 ; velocidad en bits 7:6
ENDIF
OR AL,00010111b ; establecido otro medio físico
CMP [SI].tipo_drv,2
JA modo_ok ; es unidad de 3½
AND AL,11111000b
OR AL,00000101b ; 1.2 en 1.2
TEST AL,01000000b
JZ modo_ok
XOR AL,00100001b ; 360 en 1.2 y seek * 2
modo_ok: PUSH DS
MOV BX,90h
ADD BL,unidad
DDS
AND BYTE PTR DS:[BX],8 ; respetar bit de 2.88M
OR DS:[BX],AL ; actualizar variable BIOS
POP DS ; con el tipo de densidad
XPOPA
RET
set_info ENDP
; ------------ Calcular el checksum de la zona vital del sector de
; arranque. A la entrada, ES:BX -> sector de arranque.
; A la salida, CF=1 si el disco no es 2M; de otro modo
; checksum en AL y versión del formato de disco en CL.
calc_chk PROC
XPUSH <SI, DI>
LEA DI,[BX+3] ; DI=BX+3
LEA SI,id_sistema
MOV CX,6
REP CMPSB ; comparar identificación
STC
JNE chk_ret ; el disco no es 2M
XOR AX,AX
MOV CL,ES:[BX+64] ; versión del formateador
CMP CL,6
JB chk_ok ; no usaba este checksum
MOV DI,ES:[BX+68]
chk_sum: DEC DI
ADD AL,ES:[BX+DI]
CMP DI,63
JA chk_sum
chk_ok: CLC
chk_ret: XPOP <DI, SI>
RET
calc_chk ENDP
; ------------ Determinar el tipo de error producido en el acceso.
set_err PROC
XPUSHA
JNC err_ret ; no hay error
CMP status,0 ; ¿'status' ya asignado?
JNE err_retc ; no cambiarlo si es así
MOV AL,BYTE PTR fdc_result+1
AND AL,10110111b ; aislar condiciones de test
LEA BX,lista_errs
MOV CX,9
busca_err: MOV AH,[BX] ; código de error BIOS
SHL AL,1
JC err_ok ; es ese error
INC BX
LOOP busca_err ; buscar otro error
err_ok: OR status,AH
err_retc: STC ; condición de error
err_ret: XPOPA
RET
set_err ENDP
; ------------ Actualizar variables de error de la BIOS.
set_bios_err PROC
PUSHF ; *
XPUSHA ; **
PUSH ES ; ***
DES
MOV DI,41h ; bytes de resultados del 765:
LEA SI,status ; variable BIOS de status y 7
MOVSW ; bytes (4 palabras)
MOVSW
MOVSW
MOVSW
POP ES ; ***
XPOPA ; **
POPF ; *
RET
set_bios_err ENDP
; ------------ Realizar lecturas, escrituras y verificaciones: rutina
; que sustituye el código de la BIOS para poder soportar
; los formatos 2M. La operación puede quedar dividida en
; tres fases: el fragmento anterior a la FAT2, la zona
; correspondiente a la FAT2 (se ignora la escritura y se
; simula su lectura leyendo la FAT1) y un último bloque
; ubicado tras la FAT2. El sector de arranque es emulado
; empleando el primer sector físico de la FAT2 (aunque en
; los discos de versión de formato anterior a la 7 se usa
; el sector de arranque verdadero -permitiendo escribirlo
; sólo si es válido-). En cualquier caso, si el número de
; cabezal tiene el bit 7 activo, se sobreentiende que el
; programa que llama soporta disquetes 2M y no se emula
; la FAT2 ni el sector de arranque, para permitirle
; acceder al código SuperBOOT. Las coordenadas de la BIOS
; se traducen a las unidades del DOS por mayor comodidad.
control2m PROC
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv ; SI -> variables de la unidad
CMP [SI].chk,0
JE chk_valido ; checksum correcto en sector 0
MOV status,40h ; devolver 'Seek Error' al DOS
JMP exit_2m_ctrl
chk_valido: PUSH AX ; ***
MOV AH,0
MOV numsect,AX ; nº sectores
MOV AL,CH ; cilindro
SHL AL,1
MOV DL,DH
AND DH,01111111b
ADD AL,DH ; cabezal físico
MUL [SI].sectpista
ADD AL,CL ; sector
ADC AH,0
DEC AX ; AX = nº sector DOS
MOV sectini,AX ; 0FFFFh si sector 0 (error)
MOV DI,BX ; ES:DI -> dirección
POP BX ; ***
MOV BL,BH
MOV BH,0
MOV CL,[BX+OFFSET tab_ordenes-2]
MOV orden,CL
SHL DL,1
JC acceso_final ; cabezal >= 128: no emular
AND AX,AX ; ¿comienza en sector 0?
JNZ io_emula ; no
CMP [SI].version_fmt,7
JB boot_real ; no soportado BOOT virtual
MOV AL,[SI].tam_fat ; AH = 0
INC AX
MOV CX,1 ; sector BOOT emulado en
CALL ejecuta_io ; el primer sector FAT2
JNE fin_ctrl
boot_fin_op: DEC numsect
INC sectini
MOV AX,sectini
JMP io_emula
boot_real: CMP orden,F_WRITE
JNE io_emula
MOV BX,DI ; BOOT de 2M 1.3 y anteriores
CALL calc_chk
JC si_skip ; no es de tipo 2M
AND AL,AL
JZ io_emula ; lo es y con checksum correcto
si_skip: ADD DI,512
JMP boot_fin_op ; impedir estropicio de BOOT
io_emula: MOV CL,[SI].tam_fat
MOV CH,0 ; CX = primer sector FAT2 - 1
CMP AX,CX
JA en_fat2? ; ¿la operación afecta a FAT2?
CALL calc_iop ; calcular sectores antes FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
CMP numsect,0
JE fin_ctrl ; fin de la transferencia
en_fat2?: MOV AX,sectini
MOV CL,[SI].tam_fat
MOV CH,0
SHL CX,1 ; CX = último sector FAT2
CMP AX,CX
JA acceso_final ; la operación es tras la FAT2
CALL calc_iop ; sectores hasta fin de FAT2
CMP orden,F_WRITE
JNE emula_fat1
IFDEF XT
XCHG CH,CL
SHL CH,1
ELSE
XSHL CX,9 ; CX = CX * 512
ENDIF
ADD DI,CX ; ES:DI actualizado
JMP acceso_final
emula_fat1: MOV DL,[SI].tam_fat
MOV DH,0
SUB AX,DX ; leer de FAT1 y no de la FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
acceso_final: CMP numsect,0
JE fin_ctrl ; fin de la transferencia
MOV AX,sectini
MOV CX,numsect
CALL ejecuta_io
fin_ctrl: CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL set_bios_err ; actualizar variables BIOS
exit_2m_ctrl: XPOPA ; **
MOV AH,status
POP DS ; *
AND AH,AH
JZ st_ok ; resultado correcto (CF=0)
STC ; error
MOV AL,0 ; 0 sectores movidos
st_ok: RET
calc_iop: SUB CX,AX
INC CX ; CX sectores
CMP CX,numsect
JBE nsect_ok
MOV CX,numsect ; sólo quedan CX
nsect_ok: SUB numsect,CX
ADD sectini,CX
RET
control2m ENDP
; ------------ A la entrada, AX indica el sector inicial (coordenadas
; del DOS) y CX el número de sectores a procesar.
; * Definiciones: «Sector físico» es un sector del disco
; de 512, 1024 ó 2048 bytes (números de sector del 1 al N
; en la pista). Este sector físico está dividido en
; «secciones» de 512 bytes, constando por tanto de 1, 2 ó
; 4 secciones. «Sector virtual» es el número de sector
; del programa que llama a INT 13h, comprendido entre 1 y
; M. Esta estructura de N sectores por pista de distintos
; tamaños, se verifica en todo el disco con excepción del
; cabezal y cilindro 0 (con un formato más convencional
; de sectores de 512 bytes numerados de 1 a J, aunque no
; existen algunos de los intermedios que corresponden a
; la segunda copia de la FAT).
; * Primero se convierte el sector virtual (1..M) en su
; correspondiente físico (1..J en la pista 0 y 1..N en
; las demás), deduciendo qué porción de 512 bytes (o
; sección) es afectada. Un sector virtual (512 bytes)
; simulado suele ser parte de un sector físico de 2048
; bytes en muchos casos. Si dicho sector físico ya había
; sido leído al buffer en anteriores accesos, se extrae
; la sección necesaria. Si no, se carga del disco y se
; extrae dicho fragmento. El número de sectores virtuales
; que se solicitan (=secciones) permite realizar un bucle
; hasta completar la transferencia; el interleave 1:2 de
; los sectores físicos en /M permite acceder sector a
; sector sin pérdida de rendimiento. En el caso de la
; escritura, se estudia primero si hay varios sectores
; virtuales consecutivos que escribir, completando entre
; todos un sector físico: en ese caso, se prepara el
; mismo y se escribe sin más. En caso de que haya que
; modificar sólo una única sección de un sector físico,
; salvo si éste es de 512 bytes, no hay más remedio que
; cargarlo al buffer (realizar una prelectura),
; actualizar la sección correspondiente y volverlo a
; escribir.
; * En el formato /F se realiza una operación multisector
; si es posible y sin emplear el buffer intermedio (si
; bien podría ser preciso emplearlo con la primera y
; última sección); en los dos formatos de disco se hace
; la operación multisector en la primera pista. Las
; operaciones multisector puede que sea preciso
; dividirlas en tres fases: los sectores antes de una
; frontera de DMA, el que la cruza (que es transferido
; a través del buffer intermedio) y los que están detrás.
ejecuta_io PROC
MOV BX,AX ; AX = sector DOS inicial
CMP AH,0FFh
JE no_cabe ; (acceso a sector BIOS 0)
MOV secciones,CL ; CX sectores (CL realmente)
DIV [SI].sectpista
INC AH ; numerado desde 1...
MOV sector,AH ; ...el resto es el sector
SHR AL,1
MOV cilindro,AL ; cilindro
RCL AL,1
AND AL,1
MOV cabezal,AL ; cabezal
MOV AL,sector
ADD AL,secciones
JC no_cabe ; sector+secciones > 255
DEC AX ; DEC AX = DEC AL
CMP AL,[SI].sectpista
JBE si_cabe
no_cabe: MOV status,4 ; 'sector no encontrado'
JMP fin_io
si_cabe: MOV AL,AH ; sector en AL
CBW ; sección 0 (AH = 0)
CALL pista0?
JZ s_xx ; sector físico en pista/cara 0
LEA BX,[SI].tabla_tsect-1
DEC AX ; AH = 0
resta_secc: INC BX
INC AH
MOV CL,[BX]
SUB CL,2
MOV CH,1
SHL CH,CL
SUB AL,CH
JNC resta_secc ; en las demás pistas
ADD AL,CH
XCHG AH,AL
s_xx: MOV sector,AL ; sector lógico convertido a
MOV seccion,AH ; sector y sección físicas
direct_acceso: CALL motor_ok ; asegurar que está en marcha
MOV AH,0
MOV sector_fin,AH ; no acceder a más de 1 sector
CALL pista0? ; (al menos de momento)
JNZ decide_multi ; no es pista 0
MOV AL,secciones
MOV secciones,AH ; las que restan (AH = 0)
JMP multi_proc
decide_multi: CMP [SI].multi_io,AH ; AH = 0
JNE io_pasos ; acceso sector a sector
CMP seccion,AH
JE multi_acc
CALL acceso_secc ; no acceso a inicio sector
JC fin_io
multi_acc: CMP secciones,AH ; AH = 0
JE fin_io
CALL num_secciones
MOV CL,AL
MOV AL,secciones ; AH = 0
DIV CL
AND AL,AL
JZ io_pasos ; no quedan sectores enteros
MOV secciones,AH ; las que restan
multi_proc: CALL acceso_multi ; de AL sectores
JC fin_io
io_pasos: CMP secciones,0
JE fin_io ; no restan secciones finales
CALL acceso_secc
JNC io_pasos
fin_io: CMP status,0 ; ZF = 1 -> operación correcta
RET
acceso_secc: PUSH AX
CMP orden,F_WRITE ; acabar transferencia sector
JE escritura
CMP orden,F_VERIFY
JE verificacion
CALL leido? ; realizar lectura...
JNC ya_leido ; sector ya en el buffer
hay_que_leer: CALL acceso_sector ; efectuar E/S
JC acc_ret ; ha habido fallo
ya_leido: CALL trans_secc ; buffer -> memoria
JMP acc_ret
escritura: CMP seccion,0
JNE prelectura ; sólo parte del sector cambia
CALL num_secciones
CMP secciones,AL
JAE escribir ; Todo el sector físico cambia
prelectura: CALL leido? ; Leer el sector físico para
JNC escribir ; cambiar sólo una parte de él
MOV orden,F_READ ; de momento leer...
CALL acceso_sector ; efectuar E/S
MOV orden,F_WRITE ; ... restaurar orden original
JC acc_ret ; ha habido fallo
escribir: CALL trans_secc ; memoria -> buffer
CALL acceso_sector ; volcar buffer al disco
JMP acc_ret
verificacion: PUSH BX
MOV BL,seccion
CALL num_secciones
dec_sec_veri: DEC secciones
JZ verifica
INC BX
CMP BL,AL
JB dec_sec_veri
verifica: POP BX
CALL acceso_sector ; leer para forzar verificación
acc_ret: PUSHF
INC sector ; preparado para otro sector
MOV seccion,0 ; desde su primera sección
POPF
POP AX
RET
IFDEF SUPERBOOT ; SuperBOOT: válido el cruce del DMA
acceso_multi: PUSH AX ; AL = sectores a transferir
AND AL,AL
JZ acc_mult_fin
MOV AH,sector
MOV sector_ini,AH
ADD AL,AH
DEC AX
MOV sector_fin,AL
INC AL
CALL acceso_sector ; sectores no problemáticos
MOV sector,AL
acc_mult_fin: POP AX
RET
ELSE ; No es SuperBOOT: Evitar cruce frontera DMA
acceso_multi: PUSH AX ; AL = sectores a transferir
MOV BX,ES ; desde 'sector' teniendo
XSHL BX,4 ; cuidado con el DMA
ADD BX,DI
NEG BX ; BX = bytes hasta frontera DMA
CALL num_secciones
MOV CH,AL ; AL secciones de 512 bytes
MOV CL,0
SHL CX,1 ; CX = bytes por sector
XCHG AX,BX ; BL = secciones por sector
XOR DX,DX
DIV CX
MOV CL,AL ; CL = sectores que caben
POP AX ; AL = sectores a transferir
CMP AL,CL
JA acc_mult2 ; no hay problemas con el DMA
acc_mult1: MOV CL,AL
acc_mult2: AND CL,CL
JZ acc_mult3 ; primer sector problemático
MOV AH,sector
MOV sector_ini,AH
ADD AH,CL
DEC AH
MOV sector_fin,AH
INC AH
SUB AL,CL
CALL acceso_sector ; sectores no problemáticos
MOV sector,AH
JC acc_mult_fin
acc_mult3: AND AL,AL ; ahora el sector problemático
JZ acc_mult_fin
ADD secciones,BL ; compensar futuro decremento
CALL acceso_secc ; a través del buffer auxiliar
JC acc_mult_fin
DEC AL
JMP acc_mult1 ; sectores que restan
acc_mult_fin: RET
ENDIF
ejecuta_io ENDP
; ------------ Mover secciones desde el buffer hacia la memoria (con
; orden F_READ) después de la lectura o de la memoria al
; buffer (orden F_WRITE) antes de la escritura. En la
; verificación (orden F_VERIFY) no se mueve nada porque
; esta subrutina no es invocada.
trans_secc PROC
XPUSH <AX, BX, CX, SI> ; *
MOV BL,seccion ; desde esta sección
CALL num_secciones ; nº secciones del sector
otra_secci: PUSH BX
IFDEF XT
MOV BH,BL
SHL BH,1
MOV BL,0
ELSE
XSHL BX,9 ; sección * 512
ENDIF
ADD BX,buffer ; dirección
MOV SI,BX
MOV CX,256 ; tamaño sección (palabras)
CALL swap_reg ; ¿intercambiar origen-destino?
REP MOVSW ; copiar 512 bytes
CALL swap_reg ; ¿intercambiar origen-destino?
POP BX
DEC secciones ; una menos
JZ fin_secc
INC BX ; otra sección del sector
CMP BL,AL ; ¿sector agotado?
JB otra_secci ; aún no
fin_secc: XPOP <SI, CX, BX, AX> ; *
RET
swap_reg: CMP CS:orden,F_WRITE
JE interc
CLC
RET
interc: XCHG SI,DI ; en escritura, invertir el
XPUSH <ES, DS> ; sentido de la operación
XPOP <ES, DS>
RET
trans_secc ENDP
; ------------ Comprobar si el sector ya está en el buffer.
leido? PROC
PUSH AX
MOV AL,buf_unidad
CMP AL,unidad
JNE no_leido ; es en otra unidad
MOV AL,cilindro
MOV AH,cabezal
CMP AX,buf_cilcab
JNE no_leido ; es en otro cilindro/cabezal
MOV AL,buf_sector
CMP AL,sector
JNE no_leido ; es otro sector
POP AX
RET ; está en el buffer
no_leido: STC
POP AX
RET ; sector no leído
leido? ENDP
; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
; sector correcto antes de llamar a sector_io. En esta
; rutina se actualiza la variable «status» en función de
; los posibles errores de acceso. Si sector_fin es
; distinto de 0 se accede a los sectores indicados, si es
; 0 se accede sólo al sector «sector» a través del buffer
; intermedio y al final se anota el sector cargado ó
; escrito para evitar futuras lecturas innecesarias, a
; modo de mini-caché que dispara la velocidad de acceso a
; sectores lógicos consecutivos.
acceso_sector PROC
XPUSH <AX, BX>
CALL seek_drv ; posicionar el cabezal
JNC en_pista
CMP status,0 ; ¿error ya determinado?
JNE acc_fin_err
OR status,40h ; no: pues 'seek error'
acc_fin_err: STC
JMP acceso_fin
en_pista: CALL pista0?
MOV AL,2
JZ tam_acc_ok ; sectores 512 en cil./cab. 0
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV AL,[BX-1]
tam_acc_ok: MOV tsector,AL
CMP sector_fin,0 ; ¿usar buffer intermedio?
JE acceso_buffer
CALL sector_io
MOV sector_fin,0 ; no acceder a más de 1 sector
PUSHF ; **1
JMP acceso_rep ; en el futuro (por defecto)
acceso_buffer: XPUSH <ES, DI>
PUSH CS
POP ES
MOV DI,buffer ; acceso con buffer auxiliar
MOV AL,sector ; mismo sector inicial/final
MOV sector_ini,AL
MOV sector_fin,AL
CALL sector_io
MOV sector_fin,0
XPOP <DI, ES>
PUSHF ; **2
MOV AL,-1 ; invalidar contenido buffer
JC acceso_anota ; si hay error
CMP orden,F_VERIFY
JE acceso_rep ; nada leído físicamente
MOV AL,unidad
acceso_anota: MOV buf_unidad,AL
MOV AL,cilindro
MOV AH,cabezal
MOV buf_cilcab,AX
MOV AL,sector
MOV buf_sector,AL ; anotado el sector en buffer
acceso_rep: POPF ; ** mucho cuidado con la pila
CALL set_err ; ajustar variable «status»
acceso_fin: XPOP <BX, AX>
RET
acceso_sector ENDP
; ------------ Devolver el número de secciones del sector en curso.
num_secciones PROC
CALL pista0?
MOV AL,1
JZ num_secc_ok ; sectores 512 en cil./cab. 0
XPUSH <BX, CX>
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV CL,[BX-1]
SUB CL,2
MOV AL,1
SHL AL,CL
XPOP <CX, BX>
num_secc_ok: RET ; resultado en AL
num_secciones ENDP
; ------------ Asegurar que el motor está en marcha.
motor_ok PROC
XPUSHA ; *
PUSH DS ; **
MOV BX,40h
PUSH BX
POP DS
MOV CH,255-18 ; CH = 255 - 1 segundo
CLI
MOV CL,CS:unidad
MOV AL,1
SHL AL,CL
TEST [BX-1],AL ; ¿motor en marcha?
JZ arrancarlo ; arrancarlo
CMP [BX],CH ; Si encendido y acelerado...
JBE ok_motor ; ...seguir
arrancarlo: MOV AH,CL
MOV CL,4
SHL AH,CL ; unidad << 4
OR AL,AH
MOV [BX-1],AL ; nuevo estado motores
MOV BYTE PTR [BX],255 ; asegurar que no se pare
MOV DX,FD_DOR ; registro de salida digital
ADD CL,CS:unidad
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,CS:unidad ; seleccionar unidad
OR AL,00001100b ; modo DMA, no hacer reset
OUT DX,AL ; poner en marcha el motor
STI
MOV AX,90FDh
CLC
INT 15h ; permitir multitarea
JC ok_motor ; timeout
MOV AX,1000 ; 1 segundo aceleración
CALL retardo ; esperar aceleración disco
ok_motor: MOV [BX],CH ; cuenta máxima detención motor
STI ; sin forzar futura aceleración
POP DS ; **
XPOPA ; *
RET
motor_ok ENDP
; ------------ Establecer modalidad de operación del controlador
; y poner el motor en marcha. Si CF=1 se le da tiempo
; además a la unidad para que acelere.
reset_drv PROC
XPUSHA
CALL motor_off_cnt ; cuenta detención motor
IFNDEF SUPERBOOT
STC
CALL set_rate ; velocidad correcta
ENDIF
MOV CL,unidad
MOV AL,CL
XSHL AL,4 ; unidad seleccionada
MOV AH,1 ; bit de motor
SHL AH,CL ; colocar dicho bit
OR AL,AH
PUSH DS ; *
DDS
CLI
MOV DS:[3Fh],AL
AND BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
POP DS ; *
XSHL AL,4 ; bits motor en nibble alto
OR AL,CL ; seleccionar unidad
OR AL,00001000b ; interrupciones+DMA y reset
MOV DX,FD_DOR ; registro de salida digital
OUT DX,AL ; señal de reset
IFDEF XT
MOV CX,50
respiro: LOOP respiro
ELSE
CALL fdc_respiro ; tiempo reconocer reset en 486
ENDIF
OR AL,00000100b
OUT DX,AL ; fin de señal de reset
CALL espera_int ; rehabilitará interrupciones
AND status,7Fh ; perdonar controladora rara
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
CALL fdc_read
CALL fdc_read
CALL envia_specify ; comando 'specify' adecuado
XPOPA
RET
reset_drv ENDP
; ------------ Enviar comando specify a la controladora. El step-rate
; se selecciona según la densidad, para evitar un sonido
; extraño al posicionar o recalibrar el cabezal.
envia_specify PROC
PUSH AX
PUSH DS
DDS
MOV AH,DS:[8Bh]
POP DS
MOV AL,3 ; comando 'specify'
CALL fdc_write
MOV AL,0BFh ; step rate para 500 kbps
AND AH,11000000b
JZ spec1_ok
MOV AL,0AFh ; step rate para 1 Mbps
CMP AH,11000000b
JE spec1_ok
MOV AL,0DFh ; step rate para 250/300 Kbps
spec1_ok: CALL fdc_write
MOV AL,2
CALL fdc_write ; head load y modo DMA
POP AX
RET
envia_specify ENDP
; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
; entrar, se establece la mayor cuenta posible; en caso
; contrario, se pone el valor normal de la tabla base.
motor_off_cnt PROC
XPUSHA
PUSH DS
MOV AL,0FFh ; valor máximo
JC motor_off_ok
IFDEF XT
XOR BX,BX
MOV DS,BX
ELSE
PUSH 0
POP DS
ENDIF
LDS BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
MOV AL,[BX+2] ; byte 2 tabla base disco
motor_off_ok: DDS
MOV BYTE PTR DS:[40h],AL ; cuenta parada motor
POP DS
XPOPA
RET
motor_off_cnt ENDP
; ------------ Llevar el cabezal a la pista indicada, recalibrando si
; hubo un reset (se invocó la función 0 de la INT 13h o
; se ejecutó reset_drv) antes de esta operación. Primero
; se selecciona la velocidad de transferencia y se borra
; el resultado de cualquier operación anterior, para que
; todo quede listo para el próximo acceso a disco.
seek_drv PROC
XPUSHA
IFNDEF SUPERBOOT
CLC
ENDIF
CALL set_rate ; velocidad / borrar resultados
CALL envia_specify ; comando 'specify' adecuado
MOV AH,1
MOV CL,unidad
SHL AH,CL ; AH = 1 (A:) ó 2 (B:)
PUSH DS
DDS
TEST AH,DS:[3Eh]
POP DS
JNZ do_seek ; la unidad ya fue recalibrada
CALL recalibrar
JC fallo_seek ; fallo al recalibrar
do_seek: MOV BX,94h
ADD BL,unidad
MOV AL,cilindro
PUSH DS ; *
DDS
OR DS:[3Eh],AH ; unidad ya recalibrada
MOV AH,DS:[41h] ; código de error previo
CMP AL,[BX]
MOV [BX],AL
POP DS ; *
JNE hacer_seek ; seek necesario
CMP AH,40h ; ¿error de seek previo?
JNE seek_ok ; no, evitar seek innecesario
hacer_seek: MOV AL,0Fh
CALL fdc_write ; comando 'seek'
JC fallo_seek
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
CALL espera_int ; esperar interrupción
JC fallo_seek
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_seek
CALL fdc_read ; leer registro de estado 0
JC fallo_seek
MOV AH,AL
CALL fdc_read ; leer cilindro actual
TEST AH,11000000b ; comprobar ST0
JNZ fallo_seek
MOV AL,15 ; estabilización para escritura
CMP orden,F_WRITE
JE rseek_ok
MOV AL,1 ; estabilización para lectura
rseek_ok: CBW ; AH = 0
CALL retardo ; esperar asentamiento cabezal
seek_ok: XPOPA
CLC ; retornar con éxito
RET
fallo_seek: XPOPA
STC ; retornar indicando fallo
RET
seek_drv ENDP
; ------------ Establecer velocidad de transferencia correcta si aún
; no ha sido seleccionada y borrar el resultado de otra
; operación previa. Si CF=1 al entrar, la velocidad se
; establece incondicionalmente (por si la variable de
; la BIOS no está correctamente asignada), salvo en el
; código SuperBOOT (por razones de espacio).
set_rate PROC
XPUSHA
IFNDEF SUPERBOOT
PUSHF
ENDIF
CALL pista0?
MOV AX,[SI].vunidad ; velocidad pista 0 / demás
JZ vel_ok
MOV AL,AH
vel_ok: IFNDEF SUPERBOOT
POPF
ENDIF
PUSH DS ; *
DDS
IFNDEF SUPERBOOT
JC abs_rate
ENDIF
MOV AH,DS:[8Bh]
IFDEF XT
MOV CL,6
SHR AH,CL
ELSE
SHR AH,6 ; aislar bits de velocidad
ENDIF
CMP AL,AH
JE vel_set ; velocidad ya seleccionada
abs_rate: MOV DX,FD_DCR
OUT DX,AL ; seleccionarla
XSHL AL,6
AND BYTE PTR DS:[8Bh],00111111b
OR DS:[8Bh],AL
vel_set: POP DS ; *
LEA DI,status
MOV CX,8
borra_status: MOV [DI],CH ; borrar información de estado
INC DI
LOOP borra_status
XPOPA
RET
set_rate ENDP
; ------------ Recalibrar la unidad (si hay error se intenta otra vez
; para el caso de que deba moverse más de 77 pistas).
recalibrar PROC
XPUSHA
MOV BX,94h
ADD BL,unidad
PUSH DS ; *
DDS
MOV [BX],BH ; pista actual = 0
POP DS ; *
MOV CX,2 ; dos veces como mucho
recalibra: MOV AL,7
CALL fdc_write ; comando de 'recalibrado'
JC fallo_recal
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
JC fallo_recal
CALL espera_int ; esperar interrupción
JC fallo_recal
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_recal
CALL fdc_read ; leer registro de estado 0
JC fallo_recal
MOV AH,AL
CALL fdc_read ; leer cilindro actual
XOR AH,00100000b ; bajar bit de 'seek end'
TEST AH,11110000b ; comprobar resultado y ST0
JNZ fallo_recal ; sin 'seek end' o TRK0
MOV AX,1 ; pausa de 1 ms
CALL retardo
JMP recal_ret
fallo_recal: LOOP recalibra ; reintentar comando
STC ; condición de fallo
recal_ret: XPOPA
RET
recalibrar ENDP
; ------------ Cargar o escribir sector(es) del disco en ES:DI,
; actualizando la dirección en ES:DI pero sin alterar
; ningún otro registro. Si hay error se devuelve CF=1 y
; no se modifica ES:DI. A partir de fdc_result se dejan
; los 7 bytes que devuelve el FDC al final del acceso.
; En caso de verificación (F_VERIFY) se programa el DMA
; para que no realice transferencia física (convenio de
; las BIOS con fecha 15/11/85 y posterior).
sector_io PROC
XPUSH <AX, BX, CX, DX>
MOV CL,tsector
MOV CH,0
STC
RCL CH,CL
MOV CL,0 ; nº de bytes por sector
MOV AL,sector_fin
SUB AL,sector_ini
INC AX
CBW ; AX sectores (AH = 0)
MUL CX
MOV DX,AX ; bytes totales
MOV CX,AX
DEC CX ; bytes totales - 1
MOV AX,ES
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
IFDEF SUPERBOOT
JC sector_io_ko ; chequear cruce frontera DMA
ENDIF
MOV AL,orden ; modo DMA necesario
CALL prepara_DMA
CMP AL,F_WRITE
MOV AL,11000101b ; comando de escritura del FDC
JE orden_io_ok
MOV AL,11100110b ; comando leer (verif.) del FDC
orden_io_ok: CALL fdc_write ; comando leer/escribir del FDC
JC sector_io_ko
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
MOV AL,cabezal
CALL fdc_write ; enviar cabezal
MOV AL,sector_ini
CALL fdc_write ; enviar nº sector
MOV AL,tsector
CALL fdc_write ; longitud sector
MOV AL,sector_fin
CALL fdc_write ; último sector
MOV AL,[SI].gap
CALL fdc_write ; GAP de lectura/escritura
MOV AL,128
CALL fdc_write ; tamaño sector si longitud=0
CALL espera_int
PUSHF ; *
LEA BX,fdc_result
MOV CX,7
sect_io_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP sect_io_res
POPF ; *
JC sector_io_ko
TEST fdc_result,11000000b
JNZ sector_io_ko
ADD DI,DX ; actualizar dirección
CLC ; Ok
JMP sector_io_fin
sector_io_ko: STC ; indicar fallo
sector_io_fin: XPOP <DX, CX, BX, AX>
RET
sector_io ENDP
; ------------ Devolver en AH la página de DMA y en BX la base. A la
; entrada, AX:DI -> dirección de memoria y CX = bytes-1.
; Se supone que el buffer no cruza una frontera de DMA,
; aunque el código SuperBOOT devuelve error en ese caso.
calc_dir_DMA PROC
PUSH DX
MOV BX,16
MUL BX
ADD AX,DI
ADC DX,0 ; DX:AX = dirección 20 bits
MOV BX,AX ; base en BX
MOV AH,DL ; página
IFDEF SUPERBOOT
MOV DX,CX ; comprobar cruce en SuperBOOT
ADD DX,BX
JNC dir_DMA_ok
MOV status,9 ; error de frontera de DMA
ENDIF
dir_DMA_ok: POP DX
RET
calc_dir_DMA ENDP
; ------------ Crear tabla con información para formatear. En ES:BX
; está el futuro sector de arranque del disquete.
IFNDEF SUPERBOOT ; en SuperBOOT no se formatea
genera_info PROC
XPUSHA
MOV buf_unidad,-1 ; invalidar contenido buffer
MOV SI,buffer
MOV DI,BX
CALL pista0?
JNZ no_cilcab0 ; no es cilindro/cabezal 0
ADD DI,ES:[BX+70] ; DI -> datos pista 0
MOV CL,ES:[DI]
MOV CH,0 ; CX sectores en pista 0
INC DI
MOV AL,ES:[DI] ; GAP para pista 0
MOV AH,0F6h ; byte de relleno
INC DI
MOV BYTE PTR [SI],2 ; tamaño de sector
MOV BYTE PTR [SI+1],CL ; número de sectores
MOV [SI+2],AX ; GAP / byte de relleno
genera_0: ADD SI,4
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV AL,ES:[DI]
MOV AH,2 ; LOG2 (tamaño)-7
INC DI
MOV [SI+2],AX ; nº de sector / tamaño
LOOP genera_0
XPOPA
RET
no_cilcab0: ADD DI,ES:[BX+72]
CMP BYTE PTR ES:[BX+65],1
JE info_stv
MOV DL,ES:[DI+2] ; tamaño /F
MOV DH,ES:[DI] ; nº sectores
MOV [SI],DX
XCHG DH,DL ; tamaño en DH
MOV CL,DL
MOV CH,0 ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,0F6h ; byte relleno /F
MOV [SI+2],AX ; GAP / byte de relleno
PUSH DX
MOV AX,ES:[DI+3]
PUSH AX
ADD AL,AH
MUL cilindro
MOV DX,AX
POP AX
MUL cabezal
ADD AX,DX
XOR DX,DX
MOV BL,ES:[DI]
MOV BH,0
DIV BX ; DL = módulo
SUB DL,ES:[DI]
NEG DL
MOV AL,DL
POP DX ; restaurar tamaño en DH
MOV DL,AL ; primer sector de la pista - 1
MOV BL,ES:[DI] ; nº sectores en la pista
genera_pn: ADD SI,4
INC DX
CMP DL,BL
JBE ns_ok
MOV DL,1 ; empezar desde el 1
ns_ok: MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV [SI+2],DX ; nº sector / LOG2 (tamaño)-7
LOOP genera_pn
XPOPA
RET
info_stv: MOV CH,ES:[DI] ; nº sectores
MOV CL,0 ; CL:CH sectores
MOV [SI],CX ; tamaño (CL=0) y número
XCHG CH,CL ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,4Eh ; byte de relleno /M
MOV [SI+2],AX ; GAP / byte de relleno
MOV DL,128
genera_otro: ADD SI,4
INC DX
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
XPUSH <CX, DI> ; *
MOV CL,ES:[DI+2] ; CH está a 0
busca_num: ADD DI,3
CMP DL,ES:[DI]
MOV AX,ES:[DI+1] ; número de sector / tamaño
JE hallado ; es sector a cambiar número
LOOP busca_num
MOV AL,DL ; no cambiar número
MOV AH,0 ; e indicar tamaño 128
hallado: XPOP <DI, CX> ; *
MOV [SI+2],AX ; nº sector / LOG2 (tamaño)-7
LOOP genera_otro
XPOPA
RET
genera_info ENDP
; ------------ Formatear una pista.
formatea_pista PROC
XPUSHA
MOV BX,buffer
MOV DI,BX
MOV CL,[BX+1]
MOV CH,0 ; CX sectores
XSHL CX,2
DEC CX ; nº de bytes - 1
MOV AX,DS
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
MOV AL,4Ah ; modo DMA para escribir
ADD BX,4 ; saltar primeros 4 bytes
CALL prepara_DMA
MOV BX,buffer
MOV AL,F_FORMAT
CALL fdc_write
JC fallo_fmt
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
JC fallo_fmt
MOV CX,4
format_cmd: MOV AL,[BX]
CALL fdc_write
INC BX
LOOP format_cmd
CALL espera_int
fallo_fmt: PUSHF
LEA BX,fdc_result
MOV CX,7
format_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP format_res
POPF
JC fallo_format
TEST fdc_result,11000000b
JZ format_ret
fallo_format: STC ; fallo
format_ret: XPOPA
RET
formatea_pista ENDP
ENDIF
; ------------ Esperar interrupción de disquete durante casi 2
; segundos antes de considerar que ha sido un fracaso.
IFNDEF XT
espera_int PROC
STI
XPUSHA
PUSH DS
DDS
MOV AX,9001h
CLC
INT 15h ; permitir multitarea
MOV DX,0280h
MOV BX,3Eh
JC timeout_int
esp_int_1s: XOR CX,CX
esp_int: TEST [BX],DL ; ¿llegó la interrupción?
JNZ fin_espera
PMICRO
LOOP esp_int ; esperar durante casi 1 seg.
DEC DH
JNZ esp_int_1s
timeout_int: OR CS:status,DL ; timeout
STC
fin_espera: PUSHF
AND BYTE PTR [BX],7Fh ; para la próxima vez
POPF
POP DS
XPOPA
RET
espera_int ENDP
ELSE ; Si es XT...
espera_int PROC
STI
XPUSHA
XPUSH <DS, 40h>
POP DS
MOV AH,0FFh
esperar_int: CMP AL,DS:[6Ch]
JE mira_int
MOV AL,DS:[6Ch]
INC AH
CMP AH,37 ; ¿más de 2 segundos?
JB mira_int
timeout_int: OR CS:status,80h ; timeout
STC
JMP fin_espera
mira_int: TEST BYTE PTR DS:[3Eh],80h
JZ esperar_int
AND BYTE PTR DS:[3Eh],7Fh ; CF=0
fin_espera: POP DS
XPOPA
RET
espera_int ENDP
ENDIF
; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
; base, AH = registro de página y CX = nº bytes - 1.
prepara_DMA PROC
PUSH AX
CLI
OUT 0Bh,AL ; registro de modo del DMA
MOV AL,0
DELAY
OUT 0Ch,AL ; clear first/last flip-flop
MOV AL,BL
DELAY
OUT 4,AL
MOV AL,BH
DELAY
OUT 4,AL ; enviada dirección base
DELAY
MOV AL,AH
OUT 81h,AL ; registro de página del DMA
MOV AL,CL
DELAY
OUT 5,AL
MOV AL,CH
DELAY
OUT 5,AL ; enviada cuenta de bytes
STI
MOV AL,2
DELAY
OUT 0Ah,AL ; habilitar canal 2 de DMA
POP AX
RET
prepara_DMA ENDP
; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
IFNDEF XT
fdc_read PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,FD_STATUS ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_rd: DELAY
IN AL,DX
AND AL,11000000b
CMP AL,11000000b ; ¿dato listo?
JE fdc_rd_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_rd ; reintentarlo durante 15,09 µs
MOV AH,AL
LOOP espera_rd
XPOP <AX, DX, CX>
OR status,80h ; timeout
MOV AL,0
STC ; fallo
RET
fdc_rd_ok: POP AX
INC DX ; apuntar al registro de datos
DELAY
IN AL,DX ; leer byte del FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_read ENDP
ELSE
fdc_read PROC
XPUSH <CX, DX>
MOV DX,FD_STATUS ; registro de estado del FDC
XOR CX,CX ; evitar cuelgue total si falla
espera_rd: IN AL,DX ; leer registro de estado
TEST AL,80h ; ¿bit 7 inactivo?
LOOPZ espera_rd ; así es: el FDC está ocupado
JCXZ fdc_rd_nok
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte del FDC
CLC
XPOP <DX, CX>
RET
fdc_rd_nok: OR status,80h ; timeout
STC
XPOP <DX, CX>
RET
fdc_read ENDP
ENDIF
; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
IFNDEF XT
fdc_write PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,FD_STATUS ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_wr: DELAY
IN AL,DX
TEST AL,80h ; ¿listo para E/S?
JNZ fdc_wr_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_wr ; reintentarlo durante 15,09 µs
MOV AH,AL
LOOP espera_wr
XPOP <AX, DX, CX>
OR status,80h ; timeout
STC ; fallo
RET
fdc_wr_ok: INC DX ; apuntar al registro de datos
POP AX
DELAY
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_write ENDP
ELSE
fdc_write PROC
XPUSH <AX, CX, DX>
MOV DX,FD_STATUS ; registro de estado del FDC
XCHG AH,AL ; preservar AL en AH
XOR CX,CX ; evitar cuelgue total si falla
espera_wr: IN AL,DX ; leer registro de estado
TEST AL,80h ; ¿bit 7 inactivo?
LOOPZ espera_wr ; así es: el FDC está ocupado
JCXZ fdc_wr_nok
XCHG AH,AL ; recuperar el dato de AL
INC DX ; apuntar al registro de datos
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX, AX>
CLC
RET
fdc_wr_nok: OR status,80h ; timeout
XPOP <DX, CX, AX>
STC
RET
fdc_write ENDP
ENDIF
; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
IFNDEF XT
fdc_respiro PROC
XPUSH <AX, CX>
MOV CX,4
fdc_ret: PMICRO
LOOP fdc_ret
XPOP <CX, AX>
RET
fdc_respiro ENDP
ENDIF
; ------------ Esperar exactamente AX milisegundos.
IFNDEF XT
retardo PROC
PUSHF
XPUSHA
MOV DX,16970 ; 16970 = 1193180/18*256/1000
MUL DX
MOV CL,AH ; dividir DX:AX entre 256 y
MOV CH,DL ; dejar el resultado en DX:CX
MOV DL,DH
MOV DH,0 ; DX:CX 15,09 µs-avos
retardando: PMICRO
LOOP retardando
AND DX,DX
JZ retardado
DEC DX
JMP retardando
retardado: XPOPA
POPF
RET
retardo ENDP
ELSE
retardo PROC
PUSHF
XPUSH <AX, BX, CX, DX>
retarda_mas: CMP AX,54 ; como máximo 54 ms cada vez
JBE retarda_fin
PUSH AX
MOV AX,54
CALL rt_ax
POP AX
SUB AX,54
JMP retarda_mas
retarda_fin: CALL rt_ax
XPOP <DX, CX, BX, AX>
POPF
RET
rt_ax: MOV DX,1000 ; retardo de hasta 54 ms
MUL DX
MUL CS:tbase
MOV CX,54925
DIV CX ; AX = contador iteraciones
MOV CX,AX
EVEN ; forzar alineamiento
retarda: DEC CX
JMP SHORT $+2
JNZ retarda
RET
retardo ENDP
ENDIF
IFDEF SUPERBOOT
; ------------ Esta subrutina sustituye a la macro PMICRO en el
; código SuperBOOT por razones de espacio.
pmicro_iter: DELAY ; retardo de aprox. 15,09 µs
IN AL,61h ; (exactamente 18/1193180 sg.)
AND AL,10h ; La rutina se puede ejecutar
CMP AL,AH ; repetitivamente (se apoya en
JE pmicro_iter ; AX) para hacer retardos a
MOV AH,AL ; través de la temporización
RET ; del refresco de la memoria
; ------------ Código invocado durante el SuperBOOT desde 2MBOOTHD.ASM
; A la entrada: CS=ES, SS=0 y AX = tipo unidades.
initcode: PUSH DS
PUSH SS
POP DS
MOV ES:[info_A.tipo_drv],AL ; anotar tipo de A:
MOV ES:[info_B.tipo_drv],AH ; anotar tipo de B:
LEA DI,ant_int13
MOV SI,13h*4 ; vector de INT 13h
CLD
CLI
MOVSW
MOVSW ; anotada dirección INT 13h
MOV WORD PTR [SI-4],OFFSET ges_int13
MOV [SI-2],ES
STI ; desviada INT 13h
POP DS
RETF ; volver a 2MBOOTHD
ant_int13 LABEL DWORD ; vector de la INT 13h previa
ant_int13_off DW initcode
ant_int13_seg DW 0AA55h ; significa "SuperBOOT correcto"
ENDIF
; --- Todo esto ocupa 2560 bytes exactos.
; --- Ubicación del sector de hasta 2048 bytes.
EVEN
buffer_io EQU $
tbuffer EQU 2048