P. 1
El.Universo.Digital.del.IBM.PC.AT.y.PS2.(4ª.Edicion)

El.Universo.Digital.del.IBM.PC.AT.y.PS2.(4ª.Edicion)

|Views: 4.360|Likes:
Publicado porecrapdr
Un texto básico pero imprescindible para el que en verdad busca aprender.
Un texto básico pero imprescindible para el que en verdad busca aprender.

More info:

Published by: ecrapdr on Aug 08, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

06/26/2013

pdf

text

original

Sections

El programa de ejemplo es un completo reloj-alarma residente. No posee intuitivas ventanas de
configuración ni cientos de opciones, pero es sencillo y muy económico en cuanto a consumo de memoria
se refiere. Admite la siguiente sintaxis:

RCLOCK [/A=hh:mm:ss | OFF] [ON|OFF] [/T=n] [/X=nn] [/Y=nn] [/C=nn] [/ML] [/U] [/?|H]

La opción /A permite indicar una hora concreta para activar la alarma sonora o bien desactivar una
alarma (/A=OFF) previamente programada -por defecto, no hay alarma definida-. Los parámetros ON y OFF,
por sí solos, se emplean para controlar la aparición en pantalla o no del reloj -por defecto aparece nada más
ser instalado-. El parámetro /T puede tomar un valor 1 para activar la señal horaria -por defecto-, 2 para
avisar a las medias, 4 para pitar a los cuartos y 5 para avisar cada cinco minutos; si vale 0 no se harán
señales de ninguna clase. Los parámetros opcionales X e Y permiten colocarlo en la posición deseada dentro
de la pantalla: si /X=72 (valor por defecto), el reloj no aparecerá realmente en esa coordenada sino lo más
a la derecha posible en cada tipo de pantalla activa. Con /C se puede modificar el valor del byte de atributos
empleado para colorear el reloj. /ML fuerza la instalación en memoria convencional. Por último, con /U se
puede desinstalar de la memoria, en los casos en que sea posible.

Es posible ejecutarlo cuando ya está instalado con objeto de cambiar sus parámetros o programar la
alarma. Si las coordenadas elegidas están fuera de la pantalla -ej., al cambiar a un modo de menos columnas
o filas- el resultado puede ser decepcionante (esto no sucede si /X=72). Si se produce un cambio de modo
de pantalla o una limpieza de la misma, el reloj seguirá apareciendo correctamente casi al instante -se refresca
su impresión 4 veces por segundo-.

Una vez cargado, se puede controlar la presencia o no en pantalla pulsado Ctrl-Alt-R o AltGr-R (sin
necesidad de volver a ejecutar el programa con los parámetros ON u OFF). Cuando se expulsa el reloj de
la pantalla, se restaura el contenido anterior a la aparición del reloj. Por ello, si se han producido cambios
en el monitor desde que apareció el reloj, el fragmento de pantalla restaurado puede quedar feo, aunque
también quedaría feo de todas maneras si se rellenara de espacios en blanco. De hecho, esto último es lo que
sucede cuando se trabaja con pantallas gráficas.

Cuando comienza a sonar la alarma, estando o no el reloj en pantalla, se puede pulsar Ctrl-Alt-R o
AltGr-R para cancelarla; de lo contrario avisará durante 15 segundos. Este es el único caso en que AltGr-R
o Ctrl-Alt-R no servirá para activar o desactivar el reloj (una posterior pulsación, sí). Después de haber
sonado, la alarma quedará desactivada y no volverá a actuar, ni siquiera al cabo de 24 horas.

El programa utiliza el convenio CiriSOFT para detectar su presencia en memoria, por lo que es
desinstalable incluso aunque no sea el último programa residente cargado, siempre que tras él se hayan
instalado sólo programas del convenio (o al menos otros que no utilicen las mismas interrupciones). Posee
su propia rutina de desinstalación (opción /U), con lo que no es necesario utilizar la utilidad general de
desinstalación. También está equipado con las rutinas que asignan memoria superior XMS o, en su defecto,

177

PROGRAMAS RESIDENTES

memoria superior solicitada al DOS 5.0: por ello, aunque el fichero ejecutable ocupa casi 6 Kb, sólo hacen
falta 1,5 Kb libres de memoria superior para instalarlo en este área, lo que se realiza automáticamente en
todos los entornos operativos que existen en la actualidad. Evidentemente, también se instala en memoria
convencional y sus requerimientos mínimos son un PC/XT y (recomendable) DOS 3.0 o superior.

Se utiliza la función de impresión en pantalla de la BIOS, con lo cual el reloj se imprime también
en las pantallas gráficas (incluida SuperVGA). Por ello, es preciso desviar la INT 10h con objeto de detectar
su invocación y no llamarla cuando ya se está dentro de ella (el reloj funciona ligado a la interrupción
periódica y es impredecible el estado de la máquina cuando ésta se produce). Si se anula la rutina que
controla INT 10h, en los modos gráficos SuperVGA de elevada resolución aparecen fuertes anomalías al
deslizarse la pantalla (por ejemplo, cuando se hace DIR) e incluso cuando se imprime; sin embargo, la BIOS
es dura como una roca (no se cuelga el ordenador, en cualquier caso). En los modos de pantalla normales
no habría tanta conflictividad, aunque conviene ser precavidos. La impresión del reloj se produce sólo 4 veces
por segundo para no ralentizar el ordenador; aunque se realizara 18,2 veces por segundo tampoco se notaría
un retraso perceptible. La interrupción periódica es empleada no sólo para imprimir el reloj sino también para
hacer sonar la música, enviando las notas adecuadamente al temporizador a medida que se van produciendo
las interrupciones. No se utiliza INT 1Ch porque la considero menos segura y fiable que INT 8; sin embargo
se toma la precaución de llamar justo al principio al anterior controlador de la interrupción. De la manera que
está diseñado el programa, es sencillo modificar las melodías que suenan, o crear una utilidad de música
residente por interrupciones para amenizar el uso del PC. Los valores para programar el temporizador, según
la nota que se trate, se obtienen de una tabla donde están ya calculados, ya que sería difícil utilizar la coma
flotante al efecto. Al leer el teclado, se tiene la precaución de comprobar si al pulsar Ctrl-Alt-R o AltGr-R
la BIOS o el KEYB han colocado un código Alt-R en el buffer. Esto suele suceder a menos que el KEYB
no sea demasiado compatible (Ctrl-Alt equivale, en teoría, a Alt a secas). Si así es, ese carácter se saca del
buffer para que no lo detecte el programa principal (si se sacara sin cerciorarse de que realmente está, en caso
de no estar el ordenador se quedaría esperando una pulsación de tecla). El método utilizado para detectar la
pulsación de AltGr en los teclados expandidos no funciona con el KEYB de DR-DOS 5.0/6.0 (excepto en
modo KEYB US), aunque esto es un fallo exclusivo de dicho controlador.

Sin duda, la parte más engorrosa del programa es la interpretación de los parámetros en la línea de
comandos, tarea incómoda en ensamblador. Aún así, el programa es bastante flexible y se puede indicar, por
ejemplo, un parámetro /A=000020:3:48 para programar la alarma a las 20:03:48. Sin embargo, el uso del
ensamblador para este tipo de programas es más que recomendable: además de aumentar la fiabilidad del
código, el consumo de memoria es más que asequible, incluso en máquinas modestas.

;*********************************************************************
;*

*

;* RCLOCK v2.3 (c) Septiembre 1992 CiriSOFT

*

;*

(c) Grupo Universitario de Informática - Valladolid *

;*

*

;* »»» Utilidad de reloj-alarma residente «««

*

;*

*
;*********************************************************************

; ------------ Macros de propósito general

XPUSH

MACRO RM
IRP reg,
PUSH reg

ENDM

ENDM

XPOP

MACRO RM
IRP reg,
POP reg

ENDM

ENDM

; ------------ Programa

rclock

SEGMENT
ASSUME CS:rclock, DS:rclock

ORG 100h

ini_residente EQU $

; ****************************************
; *

*
; * D A T O S R E S I D E N T E S *
; *

*
; ****************************************

inicio:

JMP main

; ------------ Identificación estandarizada del programa

program_id

LABEL BYTE

segmento_real DW 0 ; segmento real donde será cargado
offset_real DW 0 ; offset real

"

"

"

longitud_total DW 0 ; zona de memoria ocupada (párrafos)
info_extra

DB 80h ; bits 0, 1 y 2-> 000: normal, con PSP
;

001: bloque UMB XMS

;

010: *.SYS

;

011: *.SYS formato EXE
; bit 7 a 1: «extension_id» definida
multiplex_id DB 0 ; número Multiplex de este TSR
vectores_id DW tabla_vectores
extension_id DW tabla_extra
DB "*##*"
autor_nom_ver DB "CiriSOFT:RCLOCK:2.3",0

DB 4 ; número de vectores de interrupción usados

tabla_vectores EQU $
DB 8

; INT 8

ant_int08

LABEL DWORD

; dirección original

ant_int08_off DW 0
ant_int08_seg DW 0
DB 9

; INT 9

ant_int09

LABEL DWORD

; dirección original

ant_int09_off DW 0
ant_int09_seg DW 0
DB 10h

; INT 10h

ant_int10

LABEL DWORD

; dirección original

ant_int10_off DW 0
ant_int10_seg DW 0
DB 2Fh

; INT 2Fh

ant_int2F

LABEL DWORD

; dirección original

ant_int2F_off DW 0
ant_int2F_seg DW 0

tabla_extra LABEL BYTE

DW ctrl_exterior ; permitido control exterior
DW 0

; campo reservado

ctrl_exterior LABEL BYTE
reubicabilidad DB 1

; programa 100% reubicable

activacion

DW visibilidad

; ------------ Tabla de períodos de las notas
;

178

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

; Datos para el período de las 89 notas, tomando como base un reloj de
; 1,19318 MHz (el del 8253). Las notas están ordenadas ascendentemente
; como las de un piano, aunque las de código 0 al 6 son «silenciosas».
; Los datos (para notas mayores de 6) se han calculado con la fórmula:
;
;

1193180/(36.8*(2^(1/12))^(nota-6))

;
;
;

41 43

46 48 50

53 55

58 60 62

;
; . .

. .

;
; . . 40 42 44 45 47 49 51 52 54 56 57 59 61 63 . .
;
;

tabla_periodos LABEL WORD

DW 37,37,37,37,37,37,37,30603
DW 28885,27264,25734,24290,22926,21640,20425,19279
DW 18197,17175,16211,15301,14442,13632,12867,12145
DW 11463,10820,10212,9639,9098,8587,8105,7650
DW 7221,6816,6433,6072,5731,5410,5106,4819
DW 4549,4293,4052,3825,3610,3408,3216,3036
DW 2865,2705,2553,2409,2274,2146,2026,1912
DW 1805,1704,1608,1518,1432,1352,1276,1204
DW 1137,1073,1013,956,902,852,804,759
DW 716,676,638,602,568,536,506,478
DW 451,426,402,379,358,338,319,301
DW 284

; ------------ Sonido

; formato de la música:
; número de nota (0-88), duración (en 1/18,2 seg.)
;
; Las primeras 7 notas son inaudibles y sirven para
; hacer pausas; si al byte de duración se le suma 128,
; se produce una pausa de 1/18,2 segundos antes de que
; suene otra nota. El final se indica con un 255.

; fragmento del preludio 924 de Bach:

musica_alarma DB 47,2,52,2,56,3,1,1,47,2,52,2,56,3,1,1
DB 47,2,52,2,54,3,1,1,51,2,54,2,59,3,1,1
DB 49,2,54,2,59,3,1,1,49,2,54,2,57,3,1,1
DB 49,2,52,2,56,3,1,1,52,2,56,2,61,3,1,1
DB 51,2,56,2,61,3,1,1,51,2,56,2,59,3,1,1
DB 51,2,54,2,57,3,1,1
DB 255

; típica música de las iglesias:

musica_horas DB 61,10,57,10,59,10,52,20,1,7,52,10,59,10,61,10,57
DB 20,255

; tres pitidos descendentes

musica_medias DB 47,7,54,7,56,7,52,7,255

; tres pitidos ascendentes:

musica_cuartos DB 52,7,56,7,59,10,255

; un par de dobles pitidos:

musica_5min DB 57,3+128,57,3+128,1,8,57,3+128,57,3+128,255

; ------------ Parámetros básicos del reloj

alarm_enable DB 0

; por defecto, alarma OFF

hora_alarma LABEL BYTE
alarm_h

DW "0 "
DB ":"

alarm_m

DW "00"
DB ":"

alarm_s

DW "00"

visibilidad DB 1

; por defecto, reloj aparece

tipo_aviso

DB 1

; 1 -> señal horaria; 2 -> a las medias
; 4 -> a los cuartos; 5 -> cada 5 min.,
; 0 -> sin señal

c_x

DB 72

; coordenada X para el reloj

c_y

DB 0

; coordenada Y

color

DB 14+4*16 ; tinta amarilla y fondo rojo

refresco

DB 4

; cada 4/18,2 sg. se reimprime el reloj

; ------------ Variables de control general

in10

DW 0

; flag contador de entradas en INT 10h

cont_refresco DB 1

; contador de INT’s 8 a «saltar»

pagina

DB 0

; página de vídeo activa

modo_video

DB 255

; modo de vídeo activo (valor imposible
; para provocar inicialización)

operacion

DB 0

; 8/9 para preservar/restaurar la zona
; de pantalla ocupada por el reloj

visible

DB 1

; 1 si el reloj está en pantalla

c_xx

DB 0

; coordenada X real del reloj

musica_sonando DB 0

; a 1 si música sonando

puntero_notas DW 0

; apunta a la siguiente nota musical
; que va a sonar

contador_nota DB 0

; INT’s 8 que le quedan por sonar a la
; nota que está en curso

turno_blanco DB 0

; a 1 si se procesa la nota separadora
; de notas

parando

DB 0

; contador para detener el sonido

; ------------ Cadenas para imprimir

hora_actual LABEL BYTE
horasH

DB 0

horasL

DB 0
DB ":"

minutosH

DB 0

minutosL

DB 0
DB ":"

segundosH

DB 0

segundosL

DB 0
DB 0

restaurar

DB 8 DUP (’ ’) ; para almacenar el contenido previo
DB 8 DUP (7) ; de la pantalla (sólo modo texto)

; ***************************************
; *

*
; * C O D I G O R E S I D E N T E *
; *

*
; ***************************************

; ------------ Rutina de gestión de 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

; ------------ Rutina de control INT 10h. No se imprimirá en pantalla
;

cuando se ejecute una INT 10h para no reentrar al BIOS.

ges_int10

PROC FAR
INC CS:in10

; indicar entrada en INT 10h

PUSHF
CALL CS:ant_int10
DEC CS:in10

; fin de la INT 10h

IRET

ges_int10

ENDP

; ------------ Rutina de gestión de INT 9

ges_int09

PROC FAR
PUSH AX
IN AL,60h

; espiar código de rastreo

PUSHF
CALL CS:ant_int09

; llamar al KEYB

CMP AL,13h

; ¿tecla «R»?

JNE fin_int09

; no

PUSH DS
MOV AX,40h
MOV DS,AX
MOV AL,DS:[17h]
XOR AL,12

; invertir bits de Ctrl y Alt

TEST AL,12
JZ ctrl_alt

; pulsado Ctrl-Alt

TEST BYTE PTR DS:[96h],8
JZ fin_int09ds

; no pulsado AltGr

ctrl_alt:

STI
PUSH CS
POP DS
MOV AH,1
CMP musica_sonando,AH
JNE no_sonando

; no hay sonido

DEC AH
MOV parando,19

; en 1 segundo, no más notas

MOV musica_sonando,AH ; parar música
MOV alarm_enable,AH ; desactivar alarma
CALL chiton

; silenciar altavoz

JMP ret_int09
no_sonando: XOR visibilidad,AH ; invertir visibilidad reloj
MOV cont_refresco,AH ; acelerar presencia/ausencia

ret_int09:

XPUSH
MOV AH,1
INT 16h

; consultar estado del buffer

JZ no_hay_alt_r

; no se colocó Alt-R en buffer

MOV AH,0

; este KEYB es más compatible:

INT 16h

; sacar código Alt-R del buffer

no_hay_alt_r: XPOP
fin_int09ds: POP DS
fin_int09:

POP AX
IRET

ges_int09

ENDP

; ------------ Rutina de gestión de INT 8

ges_int08

PROC FAR
PUSHF
CALL CS:ant_int08

; llamar al controlador previo

STI
XPUSH
MOV AX,CS
MOV DS,AX
MOV ES,AX
CALL avisos_sonoros ; darlos si es necesario
DEC cont_refresco

; contador de INTs 8 a «saltar»

JNZ fin_int08

; no han pasado las suficientes

MOV AL,refresco
MOV cont_refresco,AL ; recargar cuenta
CMP CS:in10,0
JNE fin_int08

; estamos dentro de INT 10h

CALL obtiene_hora

; crear cadena con la hora

CMP visibilidad,1

; ¿reloj visible?

JNE restaurar?

; no

CMP visible,1

; sí, ¿acaba de aparecer?

JE scr_getted

; no

MOV visible,1

; en efecto: es preciso

MOV operacion,8

; entonces tomar el contenido

CALL bios_scr_proc

; previo de la pantalla
scr_getted: CALL gestiona_fondo ; detectar cambio en pantalla
CALL print_reloj

; imprimir reloj

JMP fin_int08
restaurar?: CMP visible,1

; reloj oculto ¿recientemente?

JNE fin_int08

; no, ya había desaparecido

MOV visible,0

; sí:

MOV operacion,9
CALL bios_scr_proc

; reponer contenido de pantalla

fin_int08:

XPOP
IRET

ges_int08

ENDP

; ------------ Controlar la generación de señales sonoras

avisos_sonoros PROC

CMP parando,0

; ¿"callar" durante 1 segundo?

JE avisos_on

; no

DEC parando

; sí

JMP fin_avisos

avisos_on:

CMP musica_sonando,1
JNE no_mas_notas

; no hay sonido en curso

DEC contador_nota
JNZ misma_nota

; sigue sonando todavía la nota
CMP turno_blanco,0 ; ¿pausa entre notas?
JE otra_nota

; no
MOV turno_blanco,0 ; sí, sólo una vez
MOV contador_nota,1 ; y durante una interrupción
MOV AX,0

; período inaudible

CALL programar_8253

179

PROGRAMAS RESIDENTES

misma_nota: JMP fin_avisos
otra_nota:

MOV BX,puntero_notas ; puntero a la siguiente nota
INC BX
INC BX
MOV puntero_notas,BX ; actualizarlo
MOV BX,[BX]

; siguiente nota

MOV AL,BH
AND AL,128

; aislar bit más significativo

ROL AL,1

; ahora el menos significativo
MOV turno_blanco,AL ; bit de separación entre notas
AND BH,127

; el resto de BH es la duración

CMP BL,255

; ¿se acabaron las notas?

JNE sonar

; no, luego tocar esta nota

MOV musica_sonando,0 ; sí
MOV alarm_enable,0 ; desactivar alarma
CALL chiton

; acallar altavoz

JMP no_mas_notas

sonar:

INC BH
MOV contador_nota,BH ; INT’s 8 que dura esa nota
XOR BH,BH

; BX = posición en la tabla

SHL BX,1

; la tabla es de palabras
MOV AX,[BX+tabla_periodos] ; período del sonido
CALL programar_8253
JMP fin_avisos
no_mas_notas: CMP alarm_enable,0
JE no_alarma

; alarma desactivada

LEA SI,hora_actual
LEA DI,hora_alarma
MOV CX,8
CLD
REP CMPSB

; ¿hora actual = hora alarma?

JNE no_alarma

; no es la hora de la alarma

LEA AX,musica_alarma-2 ; sí lo es
JMP fin_avisando

no_alarma:

MOV CL,tipo_aviso
MOV SI,WORD PTR minutosH
MOV DI,WORD PTR segundosH
CMP SI,"00"

; ¿hora en punto?

JNE media?
CMP DI,"00"
JNE media?
LEA AX,musica_horas-2 ; hora en punto
CMP CL,1

; ¿avisar a las horas?

JAE fin_avisando

; en efecto

media?:

CMP SI,"03"

; ¿30 minutos exactos?

JNE cuarto?
CMP DI,"00"
JNE cuarto?
LEA AX,musica_medias-2 ; 30 minutos exactos
CMP CL,2

; ¿avisar a las medias?

JAE fin_avisando

; en efecto

cuarto?:

CMP SI,"51"

; ¿15 ó 45 minutos exactos?

JE cuar_quiza?
CMP SI,"54"
JNE cinco_min?
cuar_quiza?: CMP DI,"00"
JNE cinco_min?
LEA AX,musica_cuartos-2 ; 15 ó 45 minutos exactos
CMP CL,4

; ¿avisar a los cuartos?

JAE fin_avisando

; en efecto

cinco_min?: CMP minutosL,’5’

; ¿minutos múltiplos de 5?

JE cinc_quiza?
CMP minutosL,’0’
JNE fin_avisos
cinc_quiza?: CMP DI,"00"
JNE fin_avisos
LEA AX,musica_5min-2 ; minutos múltiplo exacto de 5
CMP CL,5

; ¿avisar cada 5 minutos?

JB fin_avisos

; pues no
fin_avisando: MOV puntero_notas,AX ; inicio de la melodía
MOV contador_nota,1 ; compensar futuro decremento
MOV musica_sonando,1 ; activar música

fin_avisos: RET
avisos_sonoros ENDP

; ------------ Detener sonido por el altavoz

chiton

PROC
IN AL,61h
AND AL,0FCh
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL

; altavoz silenciado

RET

chiton

ENDP

; ------------ Preparar la producción de sonido

programar_8253 PROC

PUSH AX
MOV AL,182

; preparar canal 2

OUT 43h,AL
POP AX
JMP SHORT $+2
JMP SHORT $+2
OUT 42h,AL
MOV AL,AH
JMP SHORT $+2
JMP SHORT $+2
OUT 42h,AL

; canal #2 del 8253 programado

JMP SHORT $+2
JMP SHORT $+2
IN AL,61h
OR AL,3
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL

; activar sonido

RET
programar_8253 ENDP

; ------------ Controlar posible cambio de modo de pantalla o página
;

de visualización activa, que afectan al fragmento de

;

pantalla preservado antes de imprimir el reloj.

gestiona_fondo PROC

MOV AH,15
INT 10h

; modo de vídeo AL y página BH

CMP AL,modo_video

; ¿ha cambiado modo de vídeo?

JNE clr_fondo?

; en efecto

CMP BH,pagina

; ¿ha cambiado la página?

JNE clr_fondo?

; así es

RET

; no ha cambiado nada

clr_fondo?: MOV modo_video,AL

; actualizar nuevos parámetros

MOV pagina,BH
MOV BL,c_x

; coordenada X teórica

CMP BL,72

; ¿es la 72?

JNE dejar_c_x

; no: se deja como tal

MOV BL,AH

; sí: ajustar posición lo más

SUB BL,8

;

a la derecha posible

dejar_c_x:

MOV c_xx,BL

; coordenada X real

CMP AL,3

; ¿modo de texto de color?

JBE get_fondo

; sí: preservar área pantalla

CMP AL,7

; ¿modo de texto monocromo?

JE get_fondo

; sí: preservar área pantalla

MOV CX,8

; modo gráfico: no preservar,

LEA BX,restaurar

; cubrir con espacios en blanco

fondo_clr_ar: MOV BYTE PTR DS:[BX],’ ’
MOV BYTE PTR DS:[BX+8],7 ; y atributos blancos
INC BX
LOOP fondo_clr_ar

; acabar buffer

RET

get_fondo:

MOV operacion,8

; preservar zona de la pantalla

CALL bios_scr_proc
RET
gestiona_fondo ENDP

; ------------ Imprimir reloj en pantalla

print_reloj PROC

MOV AH,3
MOV BH,pagina
INT 10h

; coordenadas del cursor en DX

PUSH DX

; guardarlas para restaurarlas

MOV AH,2
MOV DL,c_xx
MOV DH,c_y

; coordenadas del reloj

MOV BH,pagina
INT 10h

; ubicar cursor
LEA BX,hora_actual ; cadena a imprimir
CALL bios_print

; imprimir reloj

POP DX

; recuperar posición del cursor

MOV BH,pagina

; y página activa

MOV AH,2
INT 10h

; restaurar posición del cursor

RET
print_reloj ENDP

; ------------ Crear cadena de caracteres con la hora actual

obtiene_hora PROC

PUSH DS
XOR AX,AX
MOV DS,AX
MOV SI,DS:[46Ch]
MOV DI,DS:[46Eh]

; contador de hora del BIOS

POP DS
MOV AX,1080
CALL mult32x16

; DXDISI = DISI * 1080

MOV AX,19663
CALL divi48x15

; DXDISI = DXDISI / 19663

PUSH DI
PUSH SI

; DISI = tics/18,2065 = seg.

MOV AX,3600
CALL divi48x15
MOV AX,SI

; AX = SI = horas

MOV CL,10
DIV CL

; pasar a BCD no empaquetado

OR AX,"00"

; pasar BCD a ASCII

CMP AL,’0’
JNE no_cero_izda
MOV AL,’ ’

; evitar cero a la izda en hora

no_cero_izda: MOV horasH,AL
MOV horasL,AH
MOV AX,3600
MUL SI

; DXAX = horas*3600

POP SI
POP DI
SUB SI,AX
SBB DI,DX

; DISI = segundos+minutos*60

MOV AX,SI
MOV CL,60
DIV CL

; AL = minutos

PUSH AX
MOV AH,0
MOV CL,10
DIV CL

; pasar binario a BCD

OR AX,"00"

; pasar BCD a ASCII

MOV minutosH,AL
MOV minutosL,AH
POP AX
MOV CL,60
MUL CL
SUB SI,AX

; SI = segundos restantes

MOV AX,SI
MOV CL,10
DIV CL

; pasar binario a BCD

OR AX,"00"

; pasar BCD a ASCII

MOV segundosH,AL
MOV segundosL,AH
RET
obtiene_hora ENDP

; ------------ Imprimir en color usando BIOS; sería más rápido acceder
;

a la memoria de vídeo, pero así también funciona en los

;

modos gráficos y en cualquier tarjeta (incluído SVGA).

;

La cadena ASCIIZ se entrega en DS:BX.

bios_print

PROC
MOV AL,[BX]

; primer carácter a imprimir

INC BX
AND AL,AL
JZ fin_print

; byte 0 -> fin de cadena

PUSH BX
MOV AH,9

; función de impresión

MOV BH,pagina
MOV BL,color
MOV CX,1

; número de caracteres

INT 10h
CALL cursor_derecha ; avanzar cursor
POP BX
JMP bios_print

; siguiente carácter

fin_print:

RET

bios_print

ENDP

; ------------ Avanzar cursor a la derecha

cursor_derecha PROC

MOV BH,pagina
MOV AH,3
INT 10h

; DX = coordenadas actuales

INC DL

; incrementar X (sin controlar

MOV AH,2

; posible desbordamiento)

MOV BH,pagina
INT 10h

; actualizar posición cursor

RET
cursor_derecha ENDP

180

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

; ------------ Procesar fragmento de pantalla empleado por el reloj:
;

si «operacion» es 8 se copiará de la pantalla a un

;

buffer y si es 9 se hará la operación inversa.

bios_scr_proc PROC

MOV AH,3
MOV BH,pagina
INT 10h

; obtener posición del cursor

PUSH DX

; y preservarla para el final

MOV AH,2
MOV DL,c_xx
MOV DH,c_y

; coordenadas del reloj

MOV BH,pagina
INT 10h

; mover cursor

LEA SI,restaurar

; dirección del buffer

MOV CX,8

; 8 caracteres

proximo_car: PUSH CX

MOV AH,operacion

; 8 ->preservar, 9 ->restaurar

MOV BH,pagina
MOV BL,[SI+8]

; preparar BL por si AH=9

MOV AL,[SI]

; preparar AL por si AH=9

MOV CX,1

; preparar CX por si AH=9

INT 10h

; leer/escribir carácter

CMP operacion,8

; ¿se trataba de leer?

JNE opcont

; no

MOV [SI],AL

; sí, guardar carácter leído

MOV [SI+8],AH

; y su atributo

opcont:

CALL cursor_derecha ; siguiente posición
INC SI

; próximo carácter

POP CX
LOOP proximo_car

; acabar caracteres

POP DX

; recuperar coordenadas

MOV BH,pagina
MOV AH,2
INT 10h

; y reponer posición del cursor

RET
bios_scr_proc ENDP

; ------------ Rutina para multiplicar números de 32 por números de 16
;

bits generando resultado de 48 bits: DISI * AX = DXDISI

mult32x16

PROC
PUSH AX
XCHG SI,AX ; multiplicador en SI
MUL SI

; AX (parte baja) * SI --> DXAX

PUSH DX

; preservar resultado parcial

PUSH AX
MOV AX,DI
MUL SI

; AX (parte alta) * SI --> DXAX

POP SI

; parte baja del resultado

POP DI

; parte media del resultado
ADD DI,AX ; acumular resultado intermedio
ADC DX,0

; arrastrar posible acarreo

POP AX
RET

mult32x16

ENDP

; ------------ Rutina para dividir números de 48 por números de 15
;

bits sin desbordamientos y con cociente de 48 bits.

;

DXDISI/AX --> cociente en DXDISI y resto en AX.

;

No se modifican otros registros. No se comprueba si

;

el divisor es cero o excede los 15 bits.

divi48x15

PROC
PUSH BX
PUSH CX
XOR BX,BX
MOV CX,49

; rotar 49 veces

divi48_15_cmp: CMP AX,BX

JA divi48_nosub
SUB BX,AX
STC
divi48_nosub: RCL SI,1
RCL DI,1
RCL DX,1
PUSHF
CMP CX,1
JE divi48_resto

; ¡no rotar el resto al final!

POPF
RCL BX,1
PUSHF
divi48_resto: POPF

LOOP divi48_15_cmp
MOV AX,BX
POP CX
POP BX
RET

divi48x15

ENDP

fin_residente EQU $ ; fin del área residente

bytes_resid EQU fin_residente-ini_residente

parrafos_resid EQU (bytes_resid+15)/16

; *****************************
; *

*
; * I N S T A L A C I O N *
; *

*
; *****************************

main

PROC
LEA DX,rclock_txt

; nombre del programa

CALL print
CALL obtener_param

; analizar posibles parámetros

JNC params_ok

; son correctos

CALL print_err

; no: informar del error/ayuda

JMP fin_noresid

params_ok:

CALL inic_XMS

; considerar presencia de XMS

CALL residente?

; ¿programa ya residente?

JC no_residente

; todavía no

CMP param_u,1

; sí: ¿solicitan desinstalarlo?

JE desinst

; así es

CALL adaptar_param

; parámetros en copia residente

JMP fin_noresid

desinst:

MOV ES,tsr_seg
CALL rclock_off
MOV AH,ES:multiplex_id
CALL mx_unload

; desinstalarlo:

LEA DX,des_ok_txt
JNC mens_ok

; ha sido posible
LEA DX,des_no_ok_txt ; es imposible

mens_ok:

CALL print
JMP fin_noresid

no_residente: CMP AX,0

; ¿reside una versión distinta?

JE instalable

; no: se admite instalación

CALL error_version

; error de versión incompatible

JMP fin_noresid
instalable: CMP param_u,1

; no residente: ¿desinstalar?

JNE instalar

; no lo piden
LEA DX,imp_desins_txt ; lo piden, ¡serán despistados!
CALL print
JMP fin_noresid

instalar:

CALL mx_get_handle

; obtener entrada Multiplex

JNC handle_ok
LEA DX,nocabe_txt

; no quedan entradas

CALL print
JMP fin_noresid

handle_ok:

MOV multiplex_id,AH ; entrada multiplex para RCLOCK
LEA DX,instalado_txt ; mensaje de instalación
CALL print
CALL preservar_ints ; tomar nota de vectores
CMP param_ml,0

; ¿se indicó parámetro /ML?

JNE instalar_ml

; en efecto
MOV AX,parrafos_resid ; párrafos de memoria precisos
CALL UMB_alloc

; pedir memoria superior XMS

JNC instalar_umb

; hay la suficiente

MOV AX,parrafos_resid
CALL UPPER_alloc

; pedir memoria superior DOS 5

JC instalar_ml

; no hay la suficiente

STC

; indicar que usa memoria DOS

instalar_umb: MOV ES,AX

; segmento del bloque UMB

MOV DI,0

; ES:0 zona a donde reubicar

CALL inicializa_id

; inicializar identificación

CALL reubicar_prog

; reubicar el programa a ES:DI

CALL activar_ints

; interceptar vectores

JMP fin_noresid

; programa instalado «arriba»

instalar_ml: STC

; indicar que usa memoria DOS

MOV DI,60h

; instalación mem. convencional

CALL inicializa_id

; inicializar identificación

CALL reubicar_prog

; reubicar programa a ES:DI

CALL activar_ints

; interceptar vectores

CALL free_environ

; liberar espacio de entorno
MOV DX,parrafos_resid ; tamaño zona residente, desde
ADD DX,6

; PSP:60h bytes (6 párrafos)

MOV AX,3100h
INT 21h

; terminar residente

fin_noresid: MOV AX,4C00h
INT 21h

; terminar no residente

main

ENDP

;*********************************************************
;*

*
;* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION *
;*

*
;*********************************************************

; ------------ Admitir posibles parámetros en la línea de comandos

obtener_param PROC

MOV BX,81h

; apuntar a zona de parámetros

otro_pmt_mas: CALL saltar_esp

; saltar delimitadores

JNC otro_pmt

; quedan más parámetros

JMP fin_proc_pmt

; no más parámetros

otro_pmt:

CMP AL,’/’
JE pmt_barrado

; parámetro precedido por ’/’

CMP AL,’?’
MOV DH,128

; código de «error» para ayuda

JNE pmt_nobarrado
JMP mal_proc_pmt

; «error» de solicitud de ayuda
pmt_nobarrado: OR WORD PTR [BX]," " ; pasar a minúsculas
CMP WORD PTR [BX],"no" ; ¿parámetro ON?
JNE pmt_off?
MOV visibilidad,1
MOV visible,1
MOV param_onoff,1
ADD BX,2
JMP otro_pmt_mas

pmt_off?:

CMP WORD PTR [BX],"fo" ; ¿parámetro OFx?
MOV DH,0

; código de error

JNE mal_proc_pmt
OR BYTE PTR [BX+2],’ ’ ; pasar a minúsculas
CMP BYTE PTR [BX+2],’f’ ; ¿parámetro OFF?
JNE mal_proc_pmt
MOV visibilidad,0
MOV visible,0
MOV param_onoff,1
ADD BX,3
JMP otro_pmt_mas

pmt_barrado: INC BX

MOV AL,[BX]

; letra del parámetro

CMP AL,13

; ¿fin de mandatos?

MOV DH,0
JE mal_proc_pmt

; falta parámetro

CMP AL,’?’
MOV DH,128

; código de «error» para ayuda

JE mal_proc_pmt
OR AL,’ ’

; poner en minúsculas

CMP AL,’h’
JE mal_proc_pmt
CMP AL,’a’
JNE pmt_no_A
JMP pmt_A

; parámetro /A=hh:mm:ss|ON|OFF

pmt_no_A:

CMP AL,’u’
JE pmt_U
MOV SI,[BX]

; ¿parámetro de dos caracteres?

OR SI," "

; mayusculizar

CMP SI,"lm"

; ¿parámetro /ML?

JNE no_ml
MOV param_ml,1

; en efecto

ADD BX,2
JMP otro_pmt_mas

no_ml:

PUSH AX
CALL get_num

; obtener valor del parámetro

POP CX

; CL tipo de parámetro

MOV DH,7

; código de error

JC mal_proc_pmt

; parámetro incorrecto

CMP CL,’t’
JE pmt_T
CMP CL,’x’
JE pmt_X
CMP CL,’y’
JE pmt_Y
CMP CL,’c’
MOV DH,2

; código de error

JE pmt_C

mal_proc_pmt: STC

; error en parámetro(s)

RET
fin_proc_pmt: CLC

; parámetros procesados

RET

pmt_U:

MOV param_u,1
INC BX
JMP otro_pmt_mas

181

PROGRAMAS RESIDENTES

pmt_T:

MOV param_t,1
MOV tipo_aviso,AL
CMP AX,5
MOV DH,3
JA mal_proc_pmt
CMP AL,3
JE mal_proc_pmt
JMP otro_pmt_mas

pmt_X:

MOV param_x,1
MOV c_x,AL
CMP AX,124

; admitir hasta 132 columnas

MOV DH,4
JA mal_proc_pmt
JMP otro_pmt_mas

pmt_Y:

MOV param_y,1
MOV c_y,AL

; y hasta 60 líneas

CMP AX,59
MOV DH,5
JA mal_proc_pmt
JMP otro_pmt_mas

pmt_C:

MOV param_c,1
MOV color,AL
CMP AX,255
MOV DH,6
JA mal_proc_pmt
JMP otro_pmt_mas

pmt_A:

PUSH BX
CALL get_num
JNC bien_pmt_A
POP BX
ADD BX,2
OR WORD PTR [BX]," " ; pasar a minúsculas
CMP WORD PTR [BX],"no" ; ¿parámetro ON?
JNE pmt_A_off?
MOV alarm_enable,1
MOV param_a_onoff,1
ADD BX,2
JMP otro_pmt_mas
pmt_A_off?: CMP WORD PTR [BX],"fo" ; ¿parámetro OFx?
MOV DH,0

; código de error

JNE mal_proc_pm
OR BYTE PTR [BX+2],’ ’ ; pasar a minúsculas
CMP BYTE PTR [BX+2],’f’ ; ¿parámetro OFF?
JNE mal_proc_pm
MOV alarm_enable,0
MOV param_a_onoff,1
ADD BX,3
JMP otro_pmt_mas
bien_pmt_A: MOV param_a,1
ADD SP,2

; «sacar» BX de la pila

CMP AX,23
JA mal_pmtA
MOV CL,10
DIV CL

; pasar binario a BCD

ADD AX,"00"

; pasar BCD a ASCII

CMP AL,’0’
JNE no_cero_izda2
MOV AL,’ ’

; evitar cero a la izda. hora

no_cero_izda2: MOV BYTE PTR alarm_h,AL
MOV BYTE PTR alarm_h+1,AH
DEC BX
CALL get_num
JC mal_pmtA
CMP AX,59
JA mal_pmtA
MOV CL,10
DIV CL

; pasar binario a BCD

ADD AX,’00’

; pasar BCD a ASCII

MOV BYTE PTR alarm_m,AL
MOV BYTE PTR alarm_m+1,AH
DEC BX
CALL get_num
JC mal_pmtA
CMP AX,59
JA mal_pmtA
MOV CL,10
DIV CL

; pasar binario a BCD

ADD AX,’00’

; pasar BCD a ASCII

MOV BYTE PTR alarm_s,AL
MOV BYTE PTR alarm_s+1,AH
MOV alarm_enable,1
JMP otro_pmt_mas

mal_pmtA:

MOV DH,1
mal_proc_pm: JMP mal_proc_pmt
obtener_param ENDP

; ------------ Saltar espacios, tabuladores, ... buscando un parámetro

saltar_esp: MOV AL,[BX]
INC BX
CMP AL,9

; carácter tabulador

JE saltar_esp
CMP AL,32

; espacio en blanco

JE saltar_esp
CMP AL,0Dh

; fin de zona de parámetros

JE fin_param
DEC BX

; puntero al primer carácter

CLC

; hay parámetro

RET

fin_param:

STC

; no hay parámetro

RET

; ------------ Obtener número chequeando delimitadores /= y /:

get_num:

INC BX
MOV AL,[BX]
INC BX
CMP AL,’=’
JE delimit_ok
CMP AL,’:’
JE delimit_ok

err_sintax: STC

; sintaxis incorrecta

RET
delimit_ok: MOV AL,[BX]
CALL obtener_num
JC err_sintax
INC BX
RET

; ------------ Extraer nº de 16 bits y depositarlo en AX; al final, el
;

puntero (BX) apuntará al final del número y CF=1 si el

;

número era incorrecto.

obtener_num PROC

CMP AL,0Dh

; fin zona parámetros y número

JE fin_num
CMP AL,32

; fin número

JE fin_num

CMP AL,9

; fin número

JE fin_num
CMP AL,’/’

; fin número (otro parámetro)

JE fin_num
CMP AL,’:’

; fin número (otro dato)

JE fin_num
INC BX
MOV AL,[BX]
JMP obtener_num

fin_num:

MOV SI,BX
DEC SI
XOR DX,DX
MOV AX,1

; AX = 10 elevado a la 0 = 1

otro_car:

DEC BX

; próximo carácter a procesar

MOV CL,[BX]
CMP CL,’=’
JE ok_num

; delimitador: fin de número

CMP CL,’:’
JE ok_num

; delimitador: fin de número

CMP CL,’.’
JNE no_millar

; saltar los puntos de millar

CMP AX,1000
JE otro_car
JMP mal_num

; separador millar descolocado

no_millar:

CMP CL,’0’
JB mal_num
CMP CL,’9’
JA mal_num
SUB CL,’0’

; pasar ASCII a binario

MOV CH,0

; CX = 0 .. 9

PUSH AX

; AX = 10 elevado a la N

AND AX,AX
JNZ multiplica
AND CL,CL
JNZ mal_num_pop

; a la izda sólo permitir ceros

multiplica: PUSH DX

; tras completar 5º dígito

MUL CX
POP DX
JC mal_num_pop
ADD DX,AX

; DX = DX + digito (CX) * 10 ^ N (AX)

JC mal_num_pop
POP AX
CMP AX,10000
JNE potencia

; AX*10 no se desbordará

MOV AX,0

; como próximo dígito<>0 a

JMP otro_car

; la izda ... pobre usuario

potencia:

MOV DI,10
PUSH DX

; no manchar DX al multiplicar

MUL DI

; AX = AX elevado a la (N+1)

POP DX
JMP otro_car

mal_num_pop: POP AX

; reequilibrar pila

mal_num:

MOV BX,SI

; número mayor de 65535

STC

; condición de error

RET

ok_num:

MOV BX,SI

; número correcto

MOV AX,DX

; resultado

CLC

; condición de Ok.

RET
obtener_num ENDP

; ------------ Imprimir errores en los parámetros

print_err

PROC
CMP DH,128

; error: DH código de error

JNE no_ayuda
LEA DX,ayuda_txt
JMP pr_ret

no_ayuda:

MOV AH,DH
MOV AL,CL

; CL=parámetro en errores 1..6

LEA DX,ini_err_txt
CALL print
LEA BX,tabla_err

; tabla de mensajes de error

PUSH AX
MOV AL,AH
SHL AL,1

; AL = AL * 2

XOR AH,AH

; AX = AL

ADD BX,AX
MOV DX,[BX]

; dirección del texto

CALL print
POP AX

; recuperar código y parámetro

CMP AH,1
JBE no_pr_pmt

; error 0 ó 1

MOV DL,AL
MOV AH,2
INT 21h

; imprimir letra del parámetro

no_pr_pmt:

LEA DX,fin_err_txt

pr_ret:

CALL print
RET

print_err

ENDP

; ------------ Ya está instalada otra versión distinta del programa

error_version PROC

PUSH ES
LEA DX,mal_ver_txt1
CALL print
LES DI,tsr_dir
MOV AL,’:’
MOV CL,255
CLD
REPNE SCASB
REPNE SCASB
MOV DL,ES:[DI]

; número de versión

MOV AH,2
INT 21h
MOV DL,’.’
MOV AH,2
INT 21h
MOV DL,ES:[DI+2]

; revisión

MOV AH,2
INT 21h
LEA DX,mal_ver_txt2
CALL print
POP ES
RET
error_version ENDP

; ------------ Considerar presencia de controlador XMS

inic_XMS

PROC
MOV AX,4300h
INT 2Fh

; chequear presencia XMS

CMP AL,80h
JNE XMS_ausente

; no instalado

PUSH ES
MOV AX,4310h
INT 2Fh

; sí: obtener su dirección

182

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

MOV XMS_off,BX

; y preservarla

MOV XMS_seg,ES
MOV xms_ins,1
POP ES
RET
XMS_ausente: MOV xms_ins,0
RET

inic_XMS

ENDP

; ------------ Comprobar si el programa ya reside en memoria. A la
;

salida, CF=0 si programa ya reside, con «tsr_seg» y

;

«tsr_off» inicializadas apuntando a la cadena de

;

identificación de la copia residente. Si CF=1, el

;

programa no reside aún (AX=0) o reside pero en otra

;

versión distinta (AX=1).

residente?

PROC
PUSH CX
PUSH SI
PUSH DI
PUSH ES
PUSH AX
LEA DI,autor_nom_ver ; identificación del programa
MOV SI,DI
MOV AL,0
MOV CL,255
CLD
REPNE SCASB
SUB DI,SI
MOV CX,DI

; tamaño autor+programa+versión

MOV AX,1492h
MOV ES,AX
MOV DI,1992h

; ES:DI protocolo de búsqueda

CALL mx_find_tsr

; buscar si está en memoria

MOV tsr_off,DI

; anotar la dirección programa

MOV tsr_seg,ES

; por si estaba instalado

POP AX
JNC resid_ok

; CF=0 -> programa ya residente

POP ES
PUSH ES
LEA DI,autor_nom_ver
MOV SI,DI
MOV AL,’:’
MOV CL,255
REPNE SCASB
REPNE SCASB
SUB DI,SI
MOV CX,DI

; tamaño autor+programa

MOV AX,1492h
MOV ES,AX
MOV DI,1992h

; ES:DI protocolo de búsqueda

CALL mx_find_tsr

; buscar si está en memoria

MOV tsr_off,DI

; anotar dirección del programa

MOV tsr_seg,ES

; por si instalada otra versión

MOV AX,0
JC resid_ok

; CF=1, AX=0 -> no residente

MOV AX,1
STC

; CF=1, AX=1 -> sí: otra vers.

resid_ok:

POP ES
POP DI
POP SI
POP CX
RET

residente?

ENDP

; ------------ Adaptar parámetros de un RCLOCK ya instalado.
;

Sólo se adaptan los indicados, testeando la variable

;

que indica si se han especificado.

adaptar_param PROC

LEA DX,ya_install_txt
CALL print
MOV ES,tsr_seg
CMP param_onoff,1
JNE param_a?
MOV AL,visibilidad ; parámetros ON u OFF:
MOV ES:visibilidad,AL ; adaptar visibilidad del reloj

param_a?:

CMP param_a,1
JNE param_aonoff?
LEA SI,alarm_enable ; parámetro /A=hh:mm:ss
MOV DI,SI

; programar nueva alarma

MOV CX,9
CLD
REP MOVSB
param_aonoff?: CMP param_a_onoff,1
JNE param_t?
MOV AL,alarm_enable ; parámetro /A=ON o /A=OFF:
MOV ES:alarm_enable,AL ; actualizar estado alarma

param_t?:

CMP param_t,1
JNE param_x?
MOV AL,tipo_aviso

; parámetro /T:
MOV ES:tipo_aviso,AL ; actualizar byte

param_x?:

CMP param_x,1
JNE param_y?
MOV AL,ES:visibilidad ; parámetro /X:
MOV ES:visibilidad,0 ; eliminar reloj de pantalla
CALL espera_reloj

; esperar a que se vaya

MOV AH,c_x
MOV ES:c_x,AH

; actualizar coordenada X

MOV ES:c_xx,AH
MOV ES:visibilidad,AL ; restaurar visibilidad

param_y?:

CMP param_y,1
JNE param_c?
MOV AL,ES:visibilidad ; parámetro /Y:
MOV ES:visibilidad,0 ; eliminar reloj de pantalla
CALL espera_reloj

; esperar a que se vaya

MOV AH,c_y
MOV ES:c_y,AH

; actualizar coordenada Y
MOV ES:visibilidad,AL ; restaurar visibilidad

param_c?:

CMP param_c,1
JNE param_adapted
MOV AL,color

; parámetro /C:

MOV ES:color,AL

; actualizar byte de atributos

param_adapted: RET
adaptar_param ENDP

; ------------ Eliminar el RCLOCK de la pantalla

rclock_off

PROC
MOV ES:visibilidad,0
CALL espera_reloj

; eliminarlo de la pantalla

MOV ES:musica_sonando,0
IN AL,61h

; parar posible sonido

AND AL,0FCh
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL
RET

rclock_off

ENDP

; ------------ Esperar una INT 8 que refresque la impresión del reloj
;

en pantalla si ésta -la impresión- está habilitada.

espera_reloj PROC

PUSH DS
PUSH AX
PUSH CX
MOV CL,refresco

; nº tics suficientes para que

MOV CH,0

; aparezca en pantalla

ADD CX,2

; redondear hacia arriba

MOV AX,40h
MOV DS,AX
STI
espera_tics: MOV AX,DS:[6Ch]
espera_tic: CMP AX,DS:[6Ch]
JE espera_tic
LOOP espera_tics
POP CX
POP AX
POP DS
RET
espera_reloj ENDP

; ------------ Preservar vectores de interrupción previos

preservar_INTs PROC

PUSH ES
PUSH DI
LEA DI,tabla_vectores
MOV CL,[DI-1]
MOV CH,0

; CX vectores interceptados

otro_vector: PUSH CX
PUSH DI
MOV AH,35h
MOV AL,[DI]
INT 21h

; obtener vector de INT xx

POP DI
POP CX
MOV [DI+1],BX

; anotar donde apunta

MOV [DI+3],ES
ADD DI,5
LOOP otro_vector

; repetir con los restantes

POP DI
POP ES
RET
preservar_INTs ENDP

; ------------ Liberar espacio de entorno

free_environ PROC

PUSH ES
MOV ES,DS:[2Ch]

; dirección del entorno

MOV AH,49h
INT 21h

; liberar espacio de entorno

POP ES
RET
free_environ ENDP

; ------------ Reservar bloque de memoria superior del nº párrafos AX,
;

devolviendo en AX el segmento donde está. CF=1 si no

;

está instalado el gestor XMS (AX=0) o hay un error (AL

;

devuelve el código de error del controlador XMS).

UMB_alloc

PROC
PUSH BX
PUSH CX
PUSH DX
CMP xms_ins,1
JNE no_umb_disp

; no hay controlador XMS

MOV DX,AX

; número de párrafos

MOV AH,10h

; solicitar memoria superior

CALL gestor_XMS
CMP AX,1

; ¿ha ido todo bien?

MOV AX,BX

; segmento UMB/código de error

JNE XMS_fallo

; fallo

POP DX

; ok

POP CX
POP BX
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo:

POP DX
POP CX
POP BX
STC
RET

UMB_alloc

ENDP

; ------------ Reservar memoria superior, con DOS 5.0, del tamaño
;

solicitado (AX párrafos). Si no hay bastante CF=1,

;

en caso contrario devuelve el segmento en AX.

UPPER_alloc PROC

PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin

; necesario DOS 5.0 mínimo

UPPER_existe: PUSH AX

; preservar párrafos...

MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL

; preservar estado UMB

MOV AX,5803h
MOV BX,1
INT 21h

; conectar cadena UMB’s

MOV AX,5801h
MOV BX,41h
INT 21h

; High Memory best fit

POP BX

; ...párrafos requeridos

MOV AH,48h
INT 21h

; asignar memoria

PUSHF
PUSH AX

; guardado el resultado

MOV AX,5801h
MOV BX,alloc_strat
INT 21h

; restaurar estrategia

MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h

; restaurar estado cadena UMB

183

PROGRAMAS RESIDENTES

POP AX
POPF
JC UPPER_fin

; hubo fallo

PUSH DS
DEC AX
MOV DS,AX
INC AX
MOV WORD PTR DS:[1],AX

; manipular PID
MOV WORD PTR DS:[16],20CDh ; simular PSP
PUSH ES
MOV CX,DS
MOV ES,CX
MOV CX,CS
DEC CX
MOV DS,CX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB

; copiar nombre de programa

POP ES
POP DS
CLC

UPPER_fin:

RET
UPPER_alloc ENDP

; ------------ Inicializar área «program_id» del programa residente.
;

A la entrada, ES:DI = seg:off a donde será reubicado

;

y CF=1 si se utiliza memoria superior XMS.

inicializa_id PROC
PUSHF
MOV segmento_real,ES ; anotar segmento del bloque
MOV offset_real,DI ; ídem con el offset
MOV longitud_total,parrafos_resid
MOV CL,4
MOV AX,DI
SHR AX,CL
ADD longitud_total,AX ; consumirá desde offset=0
MOV AL,1
POPF

; CF=0: usar memoria UMB XMS

JNC info_ok
DEC AL

; usar memoria convencional

info_ok:

OR info_extra,AL
RET
inicializa_id ENDP

; ------------ Reubicar programa residente a su dirección definitiva.

reubicar_prog PROC

PUSH DI
LEA SI,ini_residente
MOV CX,bytes_resid
CLD
ADD SI,2

; no copiar primera palabra

ADD DI,2

; respetar primera palabra

SUB CX,2
REP MOVSB
POP DI
RET
reubicar_prog ENDP

; ------------ Desviar vectores de interrupción a las nuevas rutinas.
;

Se tendrá en cuenta que está ensambladas para correr en

;

un offset inicial (100h) y que el offset real en que

;

han sido instaladas está en DI. Por ello, CS ha de

;

desplazarse (100h-DI)/16 unidades atrás (DI se supone

;

múltiplo de 16). El segmento inicial es ES.

activar_INTs PROC

PUSH CX
PUSH DS

; preservar DS para el retorno

MOV AX,100h
SUB AX,DI

; AX = 100h-DI

MOV CL,4
SHR AX,CL

; AX = (100h-DI)/16

MOV CX,ES
SUB CX,AX
MOV DS,CX
LEA SI,offsets_ints
MOV CX,CS:[SI]

; CX vectores a desviar

ADD SI,2
desvia_otro: MOV AL,CS:[SI]

; número del vector en curso

MOV DX,CS:[SI+1]

; obtener offset

MOV AH,25h
INT 21h

; desviar INT xx a DS:DX

ADD SI,3
LOOP desvia_otro
POP DS
POP CX
RET
activar_INTs ENDP

; ------------ Buscar entrada no usada en la interrupción Multiplex.
;

A la salida, CF=1 si no hay hueco (ya hay 64 programas

;

residentes instalados con esta técnica). Si CF=0, se

;

devuelve en AH un valor de entrada libre en la INT 2Fh.

mx_get_handle PROC

MOV AH,0C0h

mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl

mx_no_hueco: STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP

; ------------ Buscar un TSR por la interrupción Multiplex. A la
;

entrada, DS:SI cadena de identificación del programa

;

(CX bytes) y ES:DI protocolo de búsqueda (normalmente

;

1492h:1992h). A la salida, si el TSR ya está instalado,

;

CF=0 y ES:DI apunta a la cadena de identificación del

;

mismo. Si no, CF=1 y ningún registro alterado.

mx_find_tsr PROC

MOV AH,0C0h

mx_rep_find: PUSH AX
PUSH CX
PUSH SI
PUSH DS
PUSH ES

PUSH DI
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl

; no hay TSR ahí

CLD
PUSH DI
REP CMPSB

; comparar identificación

POP DI
JE mx_tsr_found

; programa buscado hallado

mx_skip_hndl: POP DI
POP ES
POP DS
POP SI
POP CX
POP AX
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4

; «sacar» ES y DI de la pila

POP DS
POP SI
POP CX
POP AX
CLC
RET
mx_find_tsr ENDP

; ------------ Eliminar TSR del convenio si es posible. A la entrada,
;

en AH se indica la entrada Multiplex; a la salida, CF=1

;

si fue imposible y CF=0 si se pudo. Se corrompen todos

;

los registros salvo los de segmento. En caso de fallo

;

al desinstalar, AL devuelve el vector «culpable».

mx_unload

PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX

; BP=entrada Multiplex del TSR

MOV CX,2
mx_ul_pasada: PUSH CX

; siguiente pasada

LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0

; CX = nº vectores

mx_ul_masvect: POP AX
PUSH AX

; pasada en curso

DEC AL
PUSH CX

mx_ul_2f:

MOV AL,ES:[SI]

; vector en curso

JNZ mx_ul_pasok
CMP CX,1

; ¿último vector?

JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL

; ¿INT 2Fh?

JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f

mx_ul_noult: CMP AL,2Fh

; ¿restaurar INT 2Fh?

JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f

mx_ul_pasok: PUSH ES
PUSH AX
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h

; vector en ES:BX

POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX

; INT xx en DX (aprox.)

MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16]

; ...TSR del convenio en ES:DI

PUSH ES:[DI-12]
MOV DI,ES:[DI-8]

; offset a la tabla de vectores

MOV CL,ES:[DI-1]
MOV CH,0

; número de vectores en CX

mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect

; este TSR usa vector analizado

ADD DI,5
LOOP mx_ul_buscav
ADD SP,4

; no lo usa

JMP mx_ul_otro

mx_ul_usavect: POP CX

; tamaño del TSR

POP BX

; segmento del TSR

CMP DX,BX
JB mx_ul_otro

; la INT xx no le apunta

ADD BX,CX
CMP DX,BX
JA mx_ul_otro

; la INT xx le apunta

PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP

; ¿es el propio TSR?

POP AX
JNE mx_ul_chain

; no

POP ES

; sí: ¡posible reponer vector!

POP CX
POP BX
PUSH BX
PUSH CX
PUSH ES
DEC BX
JNZ mx_ul_norest

; no es la segunda pasada

POP ES

; segunda pasada...

PUSH ES
PUSH DS
MOV BX,CS:mx_ul_tsroff ; restaurar INT’s
MOV DS,CS:mx_ul_tsrseg
CLI

184

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: POP ES
POP CX
ADD SI,5

; siguiente vector

DEC CX
JZ mx_unloadable

; no más, ¡desinstal-ar/ado!

JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la dirección
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX

; INT xx en DX (aprox.)

MOV AH,0BFh

mx_ul_otro: INC AH

; a por otro TSR

JZ mx_ul_exitnok

; ¡se acabaron!

JMP mx_ul_masmx

mx_ul_exitnok: ADD SP,6

; equilibrar pila

POP ES
STC
RET

; imposible desinstalar

mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok

; desinstalado

JMP mx_ul_pasada

; 1ª pasada exitosa: por la 2ª
mx_ul_exitok: TEST ES:info_extra,111b ; ¿tipo de instalación?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml

; cargado en RAM convencional

CMP xms_ins,1
JNE mx_ul_freeml

; no hay controlador XMS (¿?)

MOV DX,ES
MOV AH,11h
CALL gestor_XMS

; liberar memoria superior

POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h

; liberar bloque de memoria ES:

POP ES
CLC
RET
mx_ul_tsrcv?: PUSH AX

; ¿es TSR del convenio?...

PUSH ES
PUSH DI
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4

; CF=0

POP AX
RET
mx_ul_ncvexit: POP DI

; ...no es TSR del convenio

POP ES
POP AX
STC

; CF=1

RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload

ENDP

; ------------ imprimir cadena en DS:DX delimitada por un ’$’

print

PROC
PUSH AX
MOV AH,9
INT 21h
POP AX
RET

print

ENDP

; ***********************************************
; *

*
; * D A T O S N O R E S I D E N T E S *
; *

*
; ***********************************************

xms_ins

DB 0

; a 1 si presente controlador XMS

gestor_XMS

LABEL DWORD

; dirección del controlador XMS

XMS_off

DW 0

XMS_seg

DW 0

alloc_strat DW 0

; estrategia asignación (DOS 5)

umb_state

DB 0

; estado de bloques UMB (DOS 5)

tsr_dir

LABEL DWORD

; dirección de la copia residente

tsr_off

DW 0

tsr_seg

DW 0

offsets_ints DW 4

; número de vectores interceptados

DB 8

; tabla de offsets de los vectores
DW ges_int08 ; de interrupción interceptados

DB 9
DW ges_int09
DB 10h
DW ges_int10
DB 2Fh
DW ges_int2F

param_ml

DB 0 ; a 1 si se indicó /ML

param_u

DB 0 ; a 1 si se indicó /U
param_onoff DB 0 ; a 1 si se indicó ON u OFF
param_a

DB 0 ; a 1 si se indicó /A
param_a_onoff DB 0 ; a 1 si se indicó /A=ON o /A=OFF
param_t

DB 0 ; a 1 si se indicó /T

param_x

DB 0 ; a 1 si se indicó /X

param_y

DB 0 ; a 1 si se indicó /Y

param_c

DB 0 ; a 1 si se indicó /C

rclock_txt

DB 13,10," RCLOCK v2.3$"

instalado_txt DB " instalado.",13,10,"$"

ya_install_txt DB " ya instalado.",13,10
DB " - Parámetros indicados actualizados."
DB 13,10,"$"

tabla_err

DW err0_txt, err1_txt, err2_txt, err3_txt
DW err4_txt,err5_txt, err6_txt, err7_txt
ini_err_txt DB 13,10," - Error: $"
err0_txt

DB "sintaxis incorrecta$"

err1_txt

DB "hora de alarma incorrecta$"

err2_txt

DB "parámetro no admitido: /$"

err3_txt

DB "parámetro distinto de 0, 1, 2, 4 ó 5: /$"

err4_txt

DB "parámetro fuera del rango 0..124: /$"

err5_txt

DB "parámetro fuera del rango 0..59: /$"

err6_txt

DB "parámetro fuera del rango 0..255: /$"

err7_txt

DB "necesario numéro en el parámetro /$"

fin_err_txt DB 13,10
DB "

Ejecute RCLOCK /? para obtener ayuda."

DB 13,10,7,"$"

mal_ver_txt1 DB " - Error: ya está instalada la versión $"
mal_ver_txt2 DB " de este programa.",13,10,7,"$"

des_ok_txt

DB " desinstalado.",13,10,"$"

des_no_ok_txt DB 13,10," - Desinstalación imposible (se ha "
DB "instalado después un programa"
DB 13,10,"

que no respeta el convenio y tiene "
DB "alguna interrupción común).",13,10,7,"$"

imp_desins_txt DB 13,10," - Programa aún no instalado: "
DB "imposible desinstalarlo.",13,10,"$"

nocabe_txt

DB ": Instalación imposible.",13,10
DB "

Ya hay 64 programas residentes con la "

DB "misma técnica.",13,10,"$"

ayuda_txt

LABEL BYTE
DB 13,9,9,"RCLOCK v2.3 - Utilidad de reloj-alarma residente.",13,10
DB " (c) 1992 CiriSOFT, (c) Grupo Universitario de Informática - "
DB "Valladolid.",13,10,10
DB " RCLOCK [/A=hh:mm:ss|OFF|ON] [ON|OFF] [/T=] [/X=] [/Y=] [/C=] "
DB "[/U] [/ML] [/?|H]",13,10,10
DB " /A Indica una hora de alarma y activa la misma; con /A=ON o "
DB "/A=OFF se puede",13,10
DB "

controlar a posteriori la habilitación de la alarma. Tras "

DB "sonar, quedará",13,10
DB "

desactivada (hasta un posterior /A=ON o bien /A=hh:mm:ss). "

DB "Se puede can-",13,10
DB "

celar siempre el sonido pulsando Ctrl-Alt-R o AltGr-R "

DB "durante el mismo.",13,10
DB " ON y OFF Controlan la aparición del reloj en pantalla. "
DB "Equivalente a pulsar",13,10
DB "

AltGr-R ó Ctrl-Alt-R con el reloj ya instalado y sin "

DB "sonido en curso.",13,10
DB " /T Indica el nivel de avisos sonoros del reloj: 0 ninguno; 1 "
DB "señal horaria;",13,10
DB "

2, a las medias; 4 a los cuartos y 5 cada cinco minutos. "

DB "Cada uno de los",13,10
DB "

niveles incluye a su vez a los anteriores. Por defecto, "

DB "/T=1.",13,10
DB " /X e /Y Indican las coordenadas de pantalla donde se "
DB "imprimirá el reloj; su",13,10
DB "

valor varía según el modo de pantalla. Las coordenadas son "

DB "siempre refe-",13,10
DB "

ridas al modo texto, aunque la pantalla esté en modo "

DB "gráfico. Para /X=72",13,10
DB "

(valor por defecto) el reloj no se imprimirá realmente en "

DB "la columna 72,",13,10
DB "

sino lo más a la derecha posible según el modo de vídeo "

DB "activo.",13,10
DB " /C Indica los atributos de color en que aparece el reloj."
DB 13,10
DB " /U Permite desinstalar el programa de la memoria si ello es "
DB "posible.",13,10
DB " /ML Fuerza la instalación en memoria convencional -por defecto "
DB "se cargará en",13,10
DB "

memoria superior XMS o en su ausencia en la administrada "

DB "por el DOS 5.0-",13,10,"$"

rclock

ENDS
END inicio

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->