Sinclair QL Programación Avanzada
Anterior Siguiente

4. Experimentando con QDOS

4.1 Introducción

La mayoría de los capítulos de este libro se dedican a describir como opera QDOS. Este capítulo es diferente porque está dedicado a experimentar desde el BASIC y el ensamblador. Si no ha leído el capítulo 2 y el capítulo 3, este es un buen momento para hacerlo, ya que este capítulo asume ciertos conocimientos del sistema y conceptos como 'TRAPS'.

Tanto el programa Experimentador del QL como los ejemplos en esta sección, han sido pensados para dar a los usuarios un sentido intuitivo del sistema QL. El experimentador le permite acceder a muchas de las facilidades del QDOS. Inicialmente, el programa Experimentador le permitirá generar TRAPS desde el BASIC. Es esencial entender los conceptos que hay detrás de cada TRAP en el QL si se quieren explotar otras interesantes facilidades del QDOS. A medida que nos introduzcamos en las profundidades del ordenador, quedará más patente que muchas de las facilidades avanzadas solo se pueden usar adecuadamente desde Ensamblador.

En esta fase se introducen programas prácticos escritos en Ensamblador, para ilustrar los aspectos más importantes del QDOS, vistos desde un programa en código máquina. Como introducción, tenemos un programa que nos pone un reloj en la pantalla. Este simple programa es muy útil para analizar, ya que nos muestra la iniciación de un job, preparando una ventana, y convirtiendo la hora para poderla sacar a la pantalla. El programa auto-duplicable que ilustra muchos de los aspectos más complicados de los Jobs, incluida la asignación de memoria y el control de Jobs.

Hay otros ejemplos más especializados, repartidos a lo largo del libro. El capítulo 10 contiene ejemplos completos para producir una FUNCIÓN BASIC y un procedimiento BASIC en Ensamblador 68008.

4.2 BASIC a Código Máquina

Antes de comenzar con el programa Experimentador, debemos clarificar algunos puntos acerca del uso del código máquina.

Las siguientes palabras clave de Superbasic tiene que ver con los programas en código máquina, pero no están suficientemente explicados en la 'Sinclair QL Guía del Usuario':

CALL

Este comando permite acceder al código máquina directamente desde el BASIC. Se pueden pasar a la rutina siete registros de datos y seis de dirección, usando el formato:

CALL dirección,D1,D2,D3,D4,D5,D6,D7,A0,A1,A2,A3,A4,A5

Esto hará que se ejecute un JSR a la 'dirección'. Se le pueden pasar como parámetros los registros de datos D1 a D7 y los de dirección A0 a A5. No es necesario que se pasen todos:

CALL 262000,23,4

pondrá solo D1=23 y D2=4. Tenga en cuenta que D0 no se pasa mediante el comando CALL, ya que se suele utilizar como registro de error. Sin embargo, esto no significa que no sea posible poner D0 en una rutina particular de TRAP desde el BASIC. El programa experimentador resuelve este problema pasando D0 en D4, transfiriendolo después de D4 a D0 en código máquina. En las primeras versiones de la ROM, el comando CALL puede fallar si se usa desde un programa BASIC que ocupe más de 32K de memoria.

EXEC

EXEC carga y ejecuta un programa. Al programa se le asigna una parte de tiempo del procesador, pero el BASIC permanece operacional. Esto es debido a una de las principales características del QDOS - el hecho de soportar multitarea. Esencialmente, es una rutina dentro del sistema operativo, llamada planificador. Se invoca normalmente 50 veces cada segundo por medio de interrupciones. Cuando toma control, mira todos los jobs que hay en la máquina y decide cual de ellos debe ejecutarse (de acuerdo con la prioridad). El Job seleccionado se ejecutará durante el siguiente cincuentavo de segundo.

Por ejemplo:

EXEC MDV1_prog1

cargará el programa 'prog1' y comenzará a ejecutarlo, compartiendo su tiempo con el BASIC y los otros trabajos en el QL.

Tenga en cuenta que el programa que se inicializa usando EXEC debe haber sido salvado usando el comando SEXEC.

EXEC_W

Es casi idéntico al comando EXEC. La única diferencia es que completa el proceso antes de volver al BASIC. Si el proceso no está diseñado para retornar, el BASIC no volverá a tomar control.

LBYTES

LBYTES carga un fichero en memoria, en la dirección de memoria especificada. Por ejemplo:

LBYTES mdv1_programa,comienzo

cargará el fichero 'programa' desde el microdrive 1, poniendo el primer octeto en la dirección de 'comienzo'.

PEEK

PEEK es una función que retorna el octeto contenido en una posición de memoria especificada. Por ejemplo:

PRINT PEEK(100)

nos devolvería el contenido del octeto 100 (en la ROM).

PEEK_W

PEEK_W es una función similar a PEEK, pero devuelve la palabra contenida en una posición especificada (2 octetos consecutivos). Tenga en cuenta que la dirección debe ser par.

PEEK_L

PEEK_L es similar a PEEK, pero devuelve la palabra larga contenida en la posición de memoria especificada (4 octetos consecutivos). La dirección debe ser par. Esto se debe a que todas las palabras y palabras largas están alineadas en frontera de palabra. Los octetos pueden ocupar algunas veces media palabra. En esos casos, el resto de la palabra se suele rellenar a ceros y olvidarlo.

POKE

El comando POKE permite cambiar el contenido de un octeto de la memoria. Por ejemplo:

POKE 140000,52
Pondrá 52 en la posición 140000 de memoria. Si hacemos POKE dentro de la ROM, no cambiaremos el contenido de esta posición de memoria, ya que la ROM no se puede alterar.

POKE_W

POKE_W es similar a POKE, pero cambia una palabra entera (2 octetos). La dirección especificada debe ser par.

POKE_L

POKE_L es similar a POKE, pero cambia una palabra larga en memoria (4 octetos). La dirección especificada debe ser par.

RESPR

RESPR es una función que asigna memoria en el área de procedimientos residentes. Se usa, después de que se ha inicializado la máquina, para asignar memoria para código máquina que debe estar residente. No les es posible asignar memoria una vez que se ha introducido algo en el área de programas transitorios. Por ejemplo:

PRINT RESPR(100)

asignará 100 octetos de memoria en el área de procedimientos residentes. El valor retornado es la dirección de comienzo del bloque de memoria asignado.

SBYTES

SBYTES permite salvar áreas de memoria en un dispositivo del QL. Por ejemplo:

SBYTES mdv1_panta,131072,32768

almacenará el contenido de la pantalla en el fichero 'panta' en el microdrive 1.

SEXEC

Este comando permite salvar un área de memoria de forma que se pueda ejecutar posteriormente con los comandos EXEC o EXEC_W. Tiene este formato:

SEXEC mdv1_prog,principio,longitud,espacio de datos

donde 'prog' es el nombre del fichero almacenado en el microdrive 1, 'principio' es la dirección desde donde se salva el programa (y donde será cargado después), 'longitud' es la longitud total del área y 'espacio de datos' es la cantidad de memoria requerida por el programa salvado.

VER$

Esta variable contiene el número de versión del intérprete de BASIC contenido en la ROM, de modo que:

PRINT VER$

nos dará la versión de la ROM contenida en el QL.

4.3 El programa EXPERIMENTADOR

4.3.1 Introducción

Esta sección contiene el programa QL Experimentador. Debido a que el QL no contiene un Ensamblador residente, es necesario acceder al QDOS desde el BASIC (a menos que tenga usted la suerte de poseer un Ensamblador). Este programa le permite manejarse por dentro del sistema. No puede enseñarle todo, pero le proporciona un buen punto de partida. La parte más importante de esta sección es la de ayudarle a experimentar por usted mismo. La información contenida en las secciones de referencia de los capítulos 5, 6 y 7, le serán útiles para este propósito. ¡Diviertase!

4.3.2 Listado del programa Experimentador

100 REMark Programa QL Experimentador
110 REMark
120 REMark por A C Dickens 1984
130 REMark
140 REMark Asignar memoria primero
150 ad=RESPR(100)
160 screen
170 REPeat main
180 valin
190 trpgen
200 peekout
210 END REPeat main
220 STOP
230 REMark ******* VALIN *******
240 DEFine PROCedure valin
250 REMark VALIN toma los valores de entrada
260 valout
270 AT 2,7: INPUT a$
280 IF a$<>"" THEN trap=a$
290 AT 2,13: INPUT a$
300 IF a$<>"" THEN D0=hexdec(a$)
310 AT 8,4: INPUT a$
320 IF a$<>"" THEN D1=hexdec(a$)
330 AT 9,4: INPUT a$
340 IF a$<>"" THEN D2=hexdec(a$)
350 AT 10,4: INPUT a$
360 IF a$<>"" THEN D3=hexdec(a$)
370 AT 12,4: INPUT a$
380 IF a$<>"" THEN A0=hexdec(a$)
390 AT 13,4: INPUT a$
400 IF a$<>"" THEN A1=hexdec(a$)
410 AT 14,4: INPUT a$
420 IF a$<>"" THEN A2=hexdec(a$)
430 AT 15,4: INPUT a$
440 IF a$<>"" THEN A3=hexdec(a$)
450 END DEFine
460 REMark ******* VALOUT ********
470 DEFine PROCedure valout
480 REMark Imprime los parámetros
490 REMark de la CALL
500 AT 2,7: PRINT trap
510 AT 2,13: Dechex(D0)
520 AT 8,4: Dechex(D1)
530 AT 9,4: Dechex(D2)
540 AT 10,4: Dechex(D3)
550 AT 12,4: Dechex(A0)
560 AT 13,4: Dechex(A1)
570 AT 14,4: Dechex(A2)
580 AT 15,4: Dechex(A3)
590 END DEFine
600 REMark ************** PEEKOUT *************
610 DEFine PROCedure peekout
620 REMark PEEK desde memoria de
630 REMark los valores devueltos
640 RESTORE
650 DATA 24,8,24,9,24,10
660 DATA 24,12,24,13,24,14,24,15
670 REMark Imprime en decimal el error retornado
680 AT 17,18: PRINT PEEK_L(ad+16);" "
690 FOR p=1 TO 7
700 READ xp,yp
710 AT yp,xp: Dechex(PEEK_L(ad+16+4*p))
720 NEXT p
730 END DEFine
740 REMark ********** TRPGEN ************
750 DEFine PROCedure trpgen
760 REMark Genera el TRAP requerido
770 init
780 CALL ad,D1,D2,D3,D0,0,0,0,A0,A1,A2,A3
790 END DEFine
800 REMark ************ INIT *************
810 DEFine PROCedure init
820 REMark POKE el siguiente código
830 REMark en la primera parte de
840 REMark la memoria de la pantalla
850 REMark MOVE D4,D0
860 REMark TRAP #n
870 REMark MOVEML D0D1D2D3A0A1A2A3,abs.L
880 REMark MOVEQ #0,D0
890 REMark RTS
900 REMark Los datos se introducen aquí
910 POKE_W ad,8196
920 POKE ad+2,78
930 POKE ad+3,(64+trap)
940 POKE_W ad+4,18681
950 POKE_W ad+6,3855
960 POKE_W ad+8,ad+16
970 POKE_W ad+12,28672
980 POKE_W ad+14,20085
990 END DEFine
1000 REMark ************* HEXDEC **************
1010 DEFine FuNction hexdec(hex$)
1020 REMark Convierte hex a decimal
1030 dec=0
1040 FOR n=1 TO LEN(hex$)
1050 LET v=CODE(hex$(n))
1060 IF v>64 THEN v=v-55
1070 IF v>47 THEN v=v-48
1080 dec=dec+2^((8-n)*4)*v)
1090 NEXT n
1100 RETurn dec
1110 END DEFine
1120 REMark ************* DECHEX ********
1130 DEFine PROCedure Dechex(dec)
1140 REMark Convierte decimal a hex
1150 LOCal x,y
1160 x=dec
1170 c$="00000000"
1180 FOR y=1 TO 8
1190 c=INT(x/16^(8-y)))
1200 x=x-16^(8-y)*c
1210 IF c<10 THEN
1220 c$(y)=CHR$(48+c)
1230 ELSE
1240 c$(y)=CHR$(55+c)
1250 END IF
1260 NEXT Y
1270 hex$=c$
1280 PRINT hex$
1290 END DEFine
1300 REMark ************ SCREEN **************
1310 DEFine PROCedure screen
1320 CLS
1330 D0=0
1340 D1=0
1350 D2=0
1360 D3=0
1370 A0=0
1380 A1=0
1390 A2=0
1400 A3=0
1410 trap=1
1420 PAPER 4
1430 CLS
1440 PAPER 7: INK 4
1450 AT 0,0: PRINT " QDOS EXPERIMENTADOR v1.0 por ACD 1984"
1460 PRINT
1470 INK 2
1480 AT 4,1: PRINT "Introduzca los valores en HEX con mayúsculas"
1490 PAPER 1: INK 7
1500 AT 6,1: PRINT "CALL, parámetros"
1510 AT 6,21: PRINT "Valores Retornados"
1520 AT 2,1: PRINT "Trap #"; trap
1530 AT 2,9: PRINT " D0"
1540 FOR pos=0 TO 20 STEP 20
1550 AT 8,pos: PRINT " D1"
1560 AT 9,pos: PRINT " D2"
1570 AT 10,pos: PRINT " D3"
1580 AT 12,pos: PRINT " A0"
1590 AT 13,pos: PRINT " A1"
1600 AT 14,pos: PRINT " A2"
1610 AT 15,pos: PRINT " A3"
1620 NEXT pos
1630 AT 17,1: PRINT "Error retornado: "
1640 AT 18,1: PRINT " (valor en D0)"
1650 END DEFine

4.3.3 Como trabaja el Experimentador

El programa funciona en un bucle contínuo. Usted introduce los valores que deben ser pasados a un TRAP, el Experimentador llama al TRAP, usted introduce más valores y así sucesivamente.

La operación del programa se realiza en dos títulos generales; inicialización y experimento.

La inicialización consiste en la asignación de memoria para la subrutina en código máquina. Esta subrutina es un pequeño programa en código 68008 para generar el TRAP y devolver al BASIC los valores de los registros. Para reservar 100 octetos de memoria se ha usado RESPR(100). A continuación se llama al procedimiento screen. Se inicializan todas las variables a sus valores por defecto y se imprimen los rótulos fijos de la pantalla.

El experimento se realiza mediante el bucle principal, por medio de una sentencia REPEAT. Este bucle se ejecuta contínuamente repitiendo los ciclos de tomar datos del TRAP, generar el TRAP e imprimir los resultados en la pantalla.

Se usan los siguientes procedimientos:

valin se usa para tomar todos los parámetros desde el teclado. Pulsando la tecla ENTER se toma el valor por defecto. Si no, se deben introducir los ocho dígitos de longitud de la palabra larga del parámetro. Tenga en cuenta que deben ser introducidos en hexadecimal (usando solamente letras mayúsculas).

valout se usa para imprimir los valores actuales de todos los registros de dirección y de datos en sus lugares correspondientes en la pantalla.

peekout se usa para recoger los valores de los registros de dirección y datos, retornados por la subrutina especial en código máquina (ver más adelante), e imprimirlos en la pantalla.

trpgen es el que lanza el TRAP mediante la llamada a una pequeña subrutina en código máquina. Esta subrutina es explica en init.

hexdec es una función que convierte un número hexadecimal dentro de una cadena ASCII, en un número decimal.

Dechex es un procedimiento que convierte el parámetro decimal en una cadena hexadecimal ASCII. Esta cadena (de 8 caracteres de longitud) se imprime en la pantalla en la posición del cursor.

screen inicializa las variables y prepara el formato de la pantalla para que esta pueda aceptar los valores de los parámetros.

init inicializa la subrutina en código máquina en la memoria y la llama. El espacio para init se reserva al principio del programa usando RESPR. La rutina es:
        MOVE D4,D0
        TRAP #N
        MOVEM.L D0-D3/A0-A3.DATA
        MOVEQ #0,D0
        RTS
*
DATA    aquí van los valores retornados

El MOVE D4,D0 transfiere el contenido del registro D4 dentro del registro D0. Esto es necesario porque el comando CALL del BASIC mo puede pasar D0.

El TRAP#N genera el TRAP número N. Todos los valores de los registros, que se introdujeron por la pantalla, se pasan en este TRAP.

La instrucción MOVE.L es un movimiento múltiple. Almacena todos los registros retornados, D0, D1, D2, D3, A0, A1, A2, A3 en la memoria, empezando en DATA. Esto permite al programa BASIC hacer PEEK de todas las variables de retorno para que se puedan imprimir en la pantalla.

El MOVEQ #0,D0 se usa para poner el registro D0 a ceros. Si no se hiciera esto, cualquier error devuelto por el TRAP se imprimiría en la pantalla al retorno al BASIC, y cesaría la ejecución.

El RTS devuelve control al BASIC.

4.3.4 Experimentos

Ahora que ya conocemos el funcionamiento del programa Experimentador, empieza lo divertido. Antes de nada, el programa debe teclearse en el QL y salvarse en microdrive. Después ya se puede ejecutar.

Asumiendo que se ha introducido correctamente el programa, al ejecutarlo debe mostrarnos una pantalla como la que tenemos a continuación:

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #1   D0 00000000
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000000         D1
D2 00000000         D2
D3 00000000         D3
A0 00000000         A0
A1 00000000         A1
A2 00000000         A2
A3 00000000         A3
Error retornado:
 (valor en D0)

El Experimentador está dispuesto para usarse. Se puede teclear el número del TRAP y el contenido de los registros que se van a pasar al TRAP. Tenga en cuenta que todos los valores se deben teclear en hexadecimal y con mayúsculas. Es conveniente usar CAPS LOCK para seleccionarlas. Cuando se halla tecleado una entrada completa, pulse ENTER. Si se pulsa ENTER sin cambiar los valores, asume los anteriores.

Se deben usar los capítulos 5, 6 y 7 junto con esta sección para ver exactamente lo que se está haciendo.

Obtener información del estado del sistema

Como primer ejemplo, intente usar el TRAP #1 con D0=0. Este es un TRAP del Gestos llamado MT.INF, que devuelve información del sistema. No requiere que se pase ningún parámetro, así que pulsaremos simplemente la tecla ENTER hasta que llegue a A3. Pulsando ENTER de nuevo hará que se genere el TRAP. Los valores devueltos que aparecerán en la pantalla, serán similares a los que se ven más adelante.

Si nos referimos a la sección 5.4, veremos que el indentificador del trabajo actual es devuelto en D1, el número ASCII de la versión en D2 y el apuntador a las variables del sistema en A0. El BASIC es el job 0 y este programa está ejecutándose desde el BASIC, por lo tanto D1 está a ceros. El número de versión está en D2. Convirtiendo los valores de HEX a ASCII veremos que la versión del QDOS de este QL es 1.01 (una versión un poco antigua). Puede ser diferente dependiendo del QL donde se ejecute. El valor en A0 es $28000, un octeto por encima de la parte superior de la pantalla en el mapa de memoria del QL. Las variable del sistema comienzan en este punto.

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #1   D0 00000000
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000000         D1 00000000
D2 00000000         D2 312E3032
D3 00000000         D3 00000000
A0 00000000         A0 00028000
A1 00000000         A1 00000000
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Finalmente, el error retornado es cero. Esto significa que el TRAP no ha generado ningún error. Cuando se generan errores, aparece un número negativo en este punto (ver Apéndice G para los códigos de error).

Suspender el BASIC

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #1   D0 00000008
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000000         D1 00000000
D2 00000000         D2 00000000
D3 00000100         D3 00000100
A0 00000000         A0 00003DA0
A1 00000000         A1 00000000
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

El TRAP del gestor MT.SUSJB, se usa para suspender un trabajo por un periodo de determinado. En esta rutina ponemos D0 con un $8. El periodo de tiempo se pone en unidades de cuadros (cincuentaavos de segundo), por lo que $100 (256 en decimal) son 5 segundos. Este valor se pasa en el registro D3. Intente generar este TRAP. Tenga en cuenta que el cursor no reaparecerá hasta pasados los cinco segundos. Esto es debido a que el Job 0 (BASIC) ha sido suspendido durante 5 segundos. Se puede suspender por periodos más largos (hasta 10 minutos) o indefinidamente si D3 es $FFFF.

Sacar un carácter a la pantalla

El canal de comandos (las cuatro lineas inferiores de la pantalla) están definidas como canal 0 en BASIC. Hay un TRAP, llamado IO.SBYTE, que nos permite mandar caracteres a un canal de salida. IO.SBYTE es uno de los TRAPS de utilización de E/S (3). El octeto inferior de D1 contiene el código Hex del carácter a imprimir. Si ponemos $41 en D1, se imprimirá el carácter 'A' en la pantalla (suponiendo que se haya puesto un 0 en A0 para el canal de la parte inferior de la pantalla).

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #3   D0 00000005
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000041         D1 00000041
D2 00000000         D2 00000000
D3 0000FFFF         D3 0000FFFF
A0 00000000         A0 00000002
A1 00000000         A1 00000000
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Mover una ventana en la pantalla

Se puede mover lateralmente todo el contenido de una ventana un número de puntos determinado. Para ello se usa el TRAP SD.PAN . Como en el último TRAP de E/S, D3 contiene el tiempo y A0 la identificación del canal. D1 contiene el número de puntos requeridos para el movimiento, un valor positivo la mueve a la derecha el número de puntos. El ejemplo que vemos a continuación mueve a la derecha 64 puntos ($40). Los valores negativos harán que se mueva hacia la izquierda.

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #3   D0 0000001B
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000040         D1 000000D8
D2 00000000         D2 00000000
D3 0000FFFF         D3 0000FFFF
A0 00000000         A0 00000000
A1 00000000         A1 00028E36
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Poner el color de la tinta

El TRAP SD.SETIN pone el color de la tinta que se usa para imprimir caracteres.

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #3   D0 00000029
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000005         D1 000000D5
D2 00000000         D2 00000000
D3 0000FFFF         D3 0000FFFF
A0 00000000         A0 00000000
A1 00000000         A1 00028E3E
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Si le llamamos con los parámetros que vemos en nuestro ejemplo, todos los caracteres que se impriman en adelante tendrán el color cyan. Para verlo, intente llamar IO.SBYTE de nuevo. Los caracteres se imprimirán en cyan.

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #3   D0 00000029
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000005         D1 00000005
D2 00000000         D2 00000000
D3 0000FFFF         D3 0000FFFF
A0 00000000         A0 00000000
A1 00000000         A1 00028E3E
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Operaciones más complicadas

Los cinco ejemplos anteriores son bastante sencillos de realizar. Si los ha seguido junto a los capítulos 5 y 7, probablemente habrá visto que hay bastantes más TRAPS. Gran parte de ellos se pueden llamar desde el programa Experimentador, pasando en los registros todos los parámetros necesarios.

Sin embargo hay un grupo de TRAPS que requieren más información. Esta suele almacenarse en algún lugar de la memoria al que apunta un (o varios) registro de dirección. Un ejemplo típico de este tipo de TRAPS es SD.RECOL (TRAP #3 con D0=$26). Este TRAP cambia el color de una ventana. A cada color de la ventana se le puede asignar otro distinto. La lista que define el nuevo color de cada uno de los existentes es apuntado por el registro A1. Detrás del código máquina de nuestra rutina residente en el area de procedimientos, se pueden almacenar estos datos.

Ya que la variable de BASIC 'ad' contiene la dirección de la subrutina, y el espacio que hay 60 octetos más arriba no se usan, podemos teclear:

POKE (ad+60),7
POKE (ad+61),6
POKE (ad+62),5
POKE (ad+63),4
POKE (ad+64),3
POKE (ad+65),2
POKE (ad+66),1
POKE (ad+67),0
Dechex(ad+60)

introducirá una tabla de valores de conversión, en la memoria. El procedimiento dechex devolverá el valor hexadecimal del principio de la tabla. Asumiendo que esta fuera $FFCA, el ejemplo que vemos abajo cambiará los colores del dispositivo conectado al canal 0 (parte inferior de la pantalla). Tenga en cuenta que no necesita ejecutar de nuevo el Experimentador, simplemente teclee GO TO 160 para evitar que se asigne nueva memoria con el comando RESPR(100).

QDOS EXPERIMENTADOR v1.0 por ACD 1984
Trap #3   D0 00000026
Introduzca los valores en HEX con mayúsculas
CALL, parámetros    Valores retornados
D1 00000005         D1 000000D8
D2 00000000         D2 00000000
D3 00000000         D3 00000000
A0 00000000         A0 00000000
A1 0003FFCA         A1 0003FFCA
A2 00000000         A2 00000000
A3 00000000         A3 00000000
Error retornado: 0
 (valor en D0)

Las ideas que se presentan en adelante en este capítulo, no son fáciles de realizar con el BASIC. Para conseguir todas las ventajas del QDOS necesitaremos un Ensamblador. Ahora vamos a dejar el BASIC y nos introduciremos en el lenguaje Ensamblador.

4.4 Programando en Ensamblador

Los ejemplos de esta seción le ayudarán a clarificar algunos de los problemas y errores de concepto que le puedan surgir. Cada ejemplo es un programa completo, y se podrá ejecutar si se introduce con un Ensamblador adecuado.

4.4.1 Un reloj en la pantalla

Este programa produce un reloj en la pantalla que se va actualizando continuamente. Para comprender este programa, le sugiero que lea las explicaciones junto con los comentarios del programa. Se hacen continuas referencias a los capítulos de Utilidades TRAPS.

*
* reloj - un reloj en lo alto de la ventana 0 (modo monitor)
*
ME EQU -1
MT.SUSJB EQU $08
MT.PRIOR EQU $0B
MT.RCLK EQU $13
IO.SSTRG EQU $07
SD.TAB EQU $11
*
UT.SCR EQU $C8
CN.DATE EQU $EC
*
BRA.S RELOJ bifurca a código reloj
DS.B 4 y se salta
DC.W $4AFB ind. de Job en octetos 6 y 7
DC.W 5 caracteres en nombre de job
DC.W 'Clock' seguido de caracteres ASCII
*
CLOCK
SUBA.L A6,A6 limpia A6 para CN_DATE y
MOVEQ #MT.PRIOR,D0 pone prioridad
MOVEQ #ME,D1 ..... de este Job
MOVEQ #1,D2 ..... a 1 (la menor)
TRAP #1
*
MOVE.W UT.SCR,A2 abrir ventana / sacar reloj
LEA SCR(PC),A1 dirección de definición
JSR (A2)
MOVE.L A0,A4 salvar identificación canal
*
CLOCK_LOOP
MOVEQ #MT.RCLCK,D0 leer hora en D1
TRAP #1
*
LEA BUF_TOP(PC),A1 usar buffer de arriba abajo
MOVE.W CN.DATE,A2 para convertir fecha
JSR (A2)
*
MOVEQ #IO.SSTRG,D0 mandar la cadena resultante MOVE.W (A1),D2 de 20 caracteres MOVEQ #-1,D3 .... sin perder tiempo MOVE.L A4,A0 a nuestra ventana TRAP #3 * MOVEQ #SD.TAB,D0 ahora, mandar cursor al MOVEQ #0,D1 ...... principio de la linea TRAP #3 * MOVEQ #MT.SUSJB,D0 susp. Para reducir carga ES MOVEQ #ME,D1 MOVEQ #5,D3 por una décima de segundo SUBA.L A1,A1 limpiar indicadores TRAP #1 BRA.S CLOCK_LOOP * SCR DC.B 0 no borde DC.B 0 no color verde DC:B 4 fondo verde DC.B 0 caracteres negros * * definición de la ventana para modo monitor * DC.W 120 puntos ancho (20 car./6 de ancho) DC.W 10 10 puntos de alto DC.W 512-120 120 puntos del margen derecho DC.W 206 y en lo alto de la ventana #0 * * definición alternativa del modo TV * * DC.W 240 puntos ancho (20 car./12 ancho) * DC.W 10 10 puntos alto * DC.W 480-240 240 puntos del margen derecho * DC.W 216 y en lo alto de la ventana #0 BUFFER DS.B 22 BUF_TOP END
Antes de nada, el programa pone la prioridad del Job (trabajo) a la más baja posible (1) usando MT.PRIOR. Tenga en cuenta que la identificación del Job (Job ID) se pasa a la MT.PRIOR en D1 como '-1'. Esto permite que MT.PRIOR cambie la prioridad actual del Job.

La utilidad UT.SCR (accedida mediante vector), se usa después para abrir una ventana en la pantalla. La ventana se puede definir para usarla en un monitor o en un televisor, dependiendo del bloque de definición que se ponga en SCR.

La parte principal de la rutina está situada dentro de CLOCK_LOOP. Se lee el valor binario del reloj, se convierte en ASCII y se manda a la pantalla dentro del bucle. MT.RCLK toma la hora de RTC dentro de D1. Se deja un espacio en memoria de 22 octetos para convertirlo en una cadena ASCII. CN.DATE se usa para convertir la fecha de binario a ASCII. IO:SSTRG saca después la cadena a la pantalla.

Observe que se usa MT.SUSJB al final del bucle. Este TRAP del gestor suspende el Job durante una décima de segundo (ya que no tiene caso actualizar continuamente el reloj, que cambia cada segundo). Todos los trabajos que puedan ser suspendidos se deben suspender para que otros trabajos que necesitan más atención puedan disponer del tiempo del procesador.

4.4.2 El programa autoduplicable

Este programa ilustra la enorme potencia de multitarea en el QL. El propósito de este programa es dibujar un grupo de burbujas en una línea desde el centro de la pantalla hasta el borde. Sin embargo, tiene una característica especial. Después de que se hayan dibujado 25 burbujas, el programa se duplica por sí mismo. La única diferencia entre la versión original y la nueva es que producen burbujas de diferentes colores que se mueven en línea recta. Cada duplicado se divide hasta que hay gran cantidad de burbujas separadas, moviendose hacia los bordes de la pantalla.

*
* Programa para preparar el programa autoduplicable
*
* Los duplicados dibujan una línea de puntos en una direc-
* ción y de un color determinados por un número aleatorio en 
* el OS. Este número es actualizado cada vez que se usa.
*
ME       EQU     -1
MT.CJOB  EQU     $01
MT.RJOB  EQU     $04
MT.FRJOB EQU     $05
MT.SUSJB EQU     $08
MT.ACTIV EQU     $0A
MT.PRIOR EQU     $0B
SD.FILL  EQU     $2E
UT.SCR   EQU     $C8
SV_RAND  EQU     $2802E
JB_A7    EQU     $5C
*
         BRA.S   MCLONE         comienzo copia maestra
         DS.B    4
         DC.W    $4AFB
         DC.W    6,'Mclone'
MCLONE
         LEA     SCR(PC),A1     prepara ventana toda la
         MOVE.W  UT.SCR,A2      pantalla
         JSR     (A2)
         MOVE.W  #254,D4        punto comienzo en el centro
         MOVEQ   #127,D5        .... X = 254, Y = 127
         MOVE.L  A0,A4          pone A4 para crear
         BSR.W   CREATE_JOB
         MOVE.L  D1,D6          salvar job ID nuevo
*
KILL
         MOVEQ   #MT.SUSJB,D0   suspendeme
         MOVEQ   #ME,D1
         MOVEQ   #50,D3         por un segundo
         SUBA.L  A1,A1          borrar indicadores
         TRAP    #1
*
         MOVEQ   #MT.RJOB,D0    ahora intento eliminar todos
         MOVE.L  D6,D1          mis jobs
         TRAP    #1
         TST.L   D0             están todos inactivos?
         BNE.S   KILL
*
         MOVEQ   #MT.FRJOB,D0   ahora me elimino a mi mismo
         MOVEQ   #ME,D1
         MOVEQ   #0,D3          no error
         TRAP    #1             (no puede volver)
*
SCR      DC.B    0,0,208,7      no borde, blanco en rojo
         DC.W    512,256,0,0    oscuro toda la pantalla
*
programa auto duplicable
*
SCLONE
         ADDQ    #2,A7          sacar parámetros de la pila
         MOVE.L  (A7)+, A4      primero id del canal
         ADDQ    #2,A7
         MOVEM.W (A7),D4/D5     después coordenadas X e Y
         MOVE.L  #$00040003,-(A7) pone bloque 4x3 puntos
         MOVE.L  A7,A5          parámetros bloque en pila
*
         MOVE.W  SV_RAND,D6     toma num. Aleatorio sistema
         ROR.W   #2,D6          hace cambiar byte +signific.
         MOVE.W  D6,D7          .... incrementa Y
         ROR.W   #4,D6          rota e incrementa X
         MOVE.W  D6,SV_RAND     actualiza número aleatorio
*
         MOVEQ   #7,D1          pone color de bloque
         AND.B   D7,D1          a 0 y a 7
*		
         EXT.L   D6             incremento en palabras largas
         EXT.L   D7
         ASL.L   #2,D6          hasta 4 en la palabra más
         ASL.L   #2,D7          .... significativa
         MOVEM.L D6/D7,-(A7)    pone incrementos en la pila
*
         MOVE.L  D1,D6          y salva el color
SET_LOOP
         MOVEQ   #24,D7         dibuja 25 burb. entre copias
LOOP
         SWAP    D4             mueve X, Y a palabra +  significativa
         SWAP    D5
         ADD.L   (A7),D4        añade los incrementos
         ADD.L   4(A7),D5
         SWAP    D4             y muevelos atrás de nuevo
         SWAP    D5
         MOVEM.W D4/D5,4(A5)    pone X, Y actuales
*
         BSR.S   WVLOB          dibuja burbuja
         TST.L   D0             lo ha hecho bien?
         BNE.S   DONE           .... no, parar
*
         MOVEQ   #MT.SUSJB,D0   suspendeme
         MOVEQ   #ME,D1
         MOVEQ   #10,D3         durante 10 cuadrados
         SUBA.L  A1,A1          limpia indicadores
         TRAP    #1
*
         DBRA    D7,LOOP        repitelo de nuevo
*
         BSR.S   CREATE_JOB     crea una copia
         BRA.S   SET_LOOP       y la ejecuta
*
DONE
         MOVEQ   #MT.PRIOR,D0   desactivame
         MOVEQ   #ME,D1
         MOVEQ   #0,D2
         TRAP    #1
*
WBLOB
         MOVEQ   #SD.FILL,D0    llena las burbujas
         MOVE.L  D6,D1          con un color predefinido
         MOVEQ   #-1,D3         espera
         MOVE.L  A4,A0          ID canal
         MOVE.L  A5,A1          dirección del bloque lleno
         TRAP    #3
         RTS
*
* Produce un duplicado
*
CREATE_JOB
         MOVEQ   #MT.CJOB,D0    crea un nuevo job
         MOVEQ   #ME,D1         de mi propiedad
         MOVEQ   #16,D2         espacio para nombre del job
         MOVEQ   #20,D3         espacio para la pila
         SUB.L   A1,A1          dirección de inicio 0
         TRAP    #1
         TST.L   D0             tuvo éxito?
         BNE.S   CR_EXIT        .... no darselo de momento
*
         MOVE.L  A0,A6          dirección base para el job
         MOVE.W  #$4EF9,(A6)+   JMP.L
         LEA     SCLONE(PC),A1
         MOVE.L  A1,(A6)+       a sclone
         MOVE.W  #$4AFB,(A6)+   poner id de job estándar
         MOVE.L  #$00065343,(A6)+  y nombre de job
         MOVE.L  #$6C6F6E65,(A6)+
*
         LEA     36(A0),A6
         MOVEM.W D4/D5,-(A6)    pone posición actual en pila
         MOVE.W  #4,-(A6)       (4 octetos de longitud)
         MOVE.L  A4,-(A6)       e ID canal
         MOVE.W  #1,-(A6)       (uno)
*
         MOVEQ   #MT.PRIOR,D0   usa la CALL poner prioridad
         MOVEQ   #0,D2          para encontrar el apuntador
         TRAP    #1             ....  de la pila salvada
         MOVE.L  A6,JB_A7(A0)   y restaurarlo
*
         MOVEQ   #MT.ACTIV,D0   activa el job
         MOVEQ   #1,D2          en prioridad 1
         MOVEQ   #0,D3
         TRAP    #1
CR_EXIT
         RTS
         END

El programa se puede dividir en dos partes principales. La primera se llama MCLONE (la copia maestra) y prepara la pantalla completa como una ventana, después comienza una copia por el centro. Después de esperar un segundo, la maestra comprueba si se pueden eliminar todas las copias. Continua haciendo esto cada segundo desde este punto hacia fuera hasta que todas las copias han llegado al borde de la pantalla. En este punto, se destruye a sí mismo.

En la segunda parte del programa, los duplicados están ocupados en producir más copias. El color de las burbujas que se van a dibujar, viene dado por un número aleatorio seleccionado y el número aleatorio se mantiene en el espacio de variables. Las burbujas se dibujan 25 al mismo tiempo dentro de LOOP. Cuando se ha terminado, se crea un nuevo duplicado. El nacimiento del job duplicado tiene lugar en CREATE_JOB. Para ello se usa el TRAP del gestor MT.CJOB.

Los jobs se desactivan cuando llegan al borde de la pantalla. El maestro termina el job. A lo largo de todo el proceso, solo hay una copia (reentrante) del código. Cada copia subsidiaria son solo datos.


Anterior Tabla de contenidos Siguiente
QDOS - Visión general   Los TRAPS del Gestor