Presentación.

¿Qué hay de nuevo, viejo?

Buenas

Estamos alucinando en colores  :)  8)  después de distribuir el CPV no hacen más que llegarnos felicitaciones y enhorabuenas, la verdad es que a nosotros nos gustaba mucho la lección, como no podía ser de otra manera, pero sí que pensábamos que podía ser demasiado teórica, pues ná, a la peña le va el rollo y tragáis con todo, lo que nos podemos imaginar es que nos diréis cuando veáis esta lección, porque creernos, a pesar de tirarnos un mes entero 'fabricándola', no hacemos más que leerla y leerla y darnos palmaditas en la espalda. :)

Por los mensajes que nos llegan, mucha gente tiene el CPV, pero la mayor parte del tiempo está metido en un cajón, la verdad es que lo entendemos y quizás sea culpa nuestra por lanzar el programa en tan mala fecha (Enero-Febrero 95), pero por otro lado ha sido una buena elección. A los 15 días de distribuir la primera lección (45 de empezar con el proyecto) comenzó la  fiebre de los videojuegos, todas las revistas están abriendo nuevas secciones sobre juegos, salen revistas enteramente dedicadas a la programación de estas aplicaciones, e incluso se han publicado Enciclopedias enteras que cubren este área (por el escalofriante precio de más de 10000 pesetas), con esto no queremos decir que hayamos provocado nosotros la estampida, todo lo contrario, JASM y BMP somos meros continuadores de una tendencia formativa que ha comenzado en distintos lugares del mundo y que en el momento de crear el CPV no había llegado a España de manera notable. Como se ve vamos a tener una fuerte competencia, pero al precio de cero pesetas con el que han salido las dos primeras lecciones, de momento, no vamos a tener competidores, a menos que haya un loco que regale su producto y encima dé dinero.

Respecto al tironcillo de orejas que os hemos dado antes con lo de encajonar un poco el CPV, no hay problema, estamos seguros de que cuando estéis desagobiados os meteréis de lleno con el curso. Además en nuestras filas también se han producido cambios importantes, BMP está hasta el cuello de trabajo al andar metido en proyectos de desarrollo de aplicaciones tele-informáticas, además de continuar con sus estudios superiores, yo por mi parte acabo de ser becado por una multinacional y durante dos meses me van a 'poner a prueba' metiéndome toda la caña habida y por haber, así que la cantidad de tiempo que dedicábamos al curso se van a ver reducidas drásticamente y por consiguiente el lanzamiento de nuevas lecciones se verá retrasado.....

Pero no abandonamos, NO ME SIENTO LAS PIERNAS, pero no podrán con nosotros, el CPV sigue en pie y que lo paremos un poco no significa que se vaya a eternizar, además con estas dos lecciones ya tenéis carnaza para una buena temporada.

Una manera de paliar los efectos de nuestra nueva situación, sería mediante vuestras colaboraciones, si os sentís capaces, ahora vais a poder hacer constar vuestros nombres junto a JASM-BMP en nuestras lecciones. En la primera lección os dijimos que podíamos abrir nuestro CPV a colaboraciones externas, pues ya es una realidad y necesitamos vuestra colaboración más que nunca, nos gustaría recibir artículos, programas, gráficos, melodías y todo lo que consideréis relacionado con la programación de sistemas, nosotros  entendemos el trabajo que cuesta realizar un único artículo, así que hemos  pensado una forma de gratificar a nuestros colaboradores. En el apartado de  Colaboraciones y Condiciones, os explicamos más detalladamente nuestra idea, remito allí a todo el mundo. :)

Ya acabo la introducción tranquilos. He incluido una sección dedicada a los bugs del CPV-01 y a responder las consultas que nos han llegado,  respecto a abrir nuevas secciones, no puedo, no puedo, decidirme ya que ... Hoy 20 días después del lanzamiento del CPV, no me ha llegado ni un solo cuestionario de la primera lección. Sí, sí, esas 10 preguntitas de nada que os hicimos, como veo le tenéis mucha rabia a todo lo que sean cuestionarios y tests. Espero que no sea por lo incomunicados que estamos con la gente que no tiene modem, ni cuenta en Internet, para ellos proporcionaremos nuestras direcciones de correo tortuga (lento pero seguro) para que podáis escribir al que más rabia os de.

Bueno estaréis impacientes por empezar con la lección pues veamos ... Pre-parados..... Listos..... (PUUUUUM)   Ya........
 
 



2.2.- Colaboraciones y condiciones.

Participa

Como ya os he dicho en la presentación de la lección, vamos a estar un poco agobiadillos a partir de ahora y recibir vuestra ayuda sería un hecho que nos ayudaría y agradeceríamos mucho.

Algunos pensaréis que no podéis aportar nada, (eso me dijo un profesor de 'C' y aficionado el Autodesk 3D), pero todo el mundo puede. En muchos de los mensajes recibidos vemos que hay mucha gente 'puesta' en el mundo de la programación, otras personas me dicen que son unos virgueros con los gráficos pero que no salen de ahí, pues ya esta, si lo tenéis todo. Mucho de vosotros no estaréis en la misma situación que el profesor de 'C' pero quizás podéis colaborar con programas, con gráficos, melodías.... :) Pues  ¿ a qué esperáis para participar?  Quizás os falte una motivación, pues aquí está:

Entre todas las personas que nos mande cualquier tipo de colaboración, (gráficos, sonidos, programas, artículos, correcciones) se realizará un sorteo en cada lección y al ganador de éste, se le mandará gratuitamente por correo (en 3 1/2) la lección en la que haya resultado ganador. Entre el resto de participaciones, seleccionaremos los mejores trabajos para que aparezcan en las lecciones del CPV y sus autores serán debidamente mencionados y figuraran con honores en los créditos de la lección.

Para la gente que vaya un poquito más lejos y nos mande programas y/o artículos, seremos más generosos. A toda persona que se le publique un artículo en el CPV, se le mandará por correo la lección donde se incluya su trabajo y si envía y son publicados más de tres artículos recibirá todo el CPV gratuitamente.

En un principio esto último era para nosotros impensable, pero ya hemos recibido un mensaje de cierta persona que podría hacer varios artículos sobre el tema de las 3 dimensiones (área que habréis notado no incluimos en el CPV por nuestra inmadurez en ella) y parece que su propuesta podría llegar a buen puerto. Ni que decir tiene que los autores mantendrán todos sus derechos sobre los programas/artículos y que serán mencionados y figuraran con enormes méritos en los créditos de la lección y del CPV. :)

Espero que con estas ofertas os animéis a participar y nos echéis una mano que necesitamos.
 

LEEME


** IMPORTANTE **   *** IMPORTANTE ***    *** IMPORTANTE ****   ** IMPORTANTE **

Si eres responsable de alguna BBS y te gustaría recibir el CPV, ponte en contacto con nosotros ....

** IMPORTANTE **   *** IMPORTANTE ***    *** IMPORTANTE ****   ** IMPORTANTE **

Por el momento Sakery Fox BBS 'es nuestra casa' y podéis contactar con nosotros allí, seguimos sin saber si se abrirá un área especial para el curso, luego dejad un mensaje en el área de programación o en 'mi' dirección. Cualquier duda, consulta, petición, sugerencia, ofrecimiento, aviso, ideas, vítores, insultos, alabanzas, teléfonos de tías buenas, besos, abrazos, saludos, cortes de manga, y sobre todo CUESTIONARIOS DE LA PRIMERA LECCION,   podéis dejarlos en dicha zona. :)

Sakery Fox BBS 91- 413 98 55

También se puede obtener el CPV en el servidor de FTP del nodo de INTERNET, asterix.fi.upm.es, las lecciones se situarán en el directorio /pub/facultad/alumnos/juegos, para ponerte en contacto con nosotros en esta red, consulta los créditos.

Para los que no tengan ni modem, ni cuenta en Internet o prefieran la comunicación de toda la vida, aquí tenéis nuestras direcciones de correo. Si os habéis decidido a colaborar podéis mandarnos también vuestras creaciones a estas direcciones:

         Jesús Angel Sánchez Mena       Benjamín Moreno Palacios
         c\ Berlín 1                    c\ Estocolmo 1
         28850 Torrejón de Ardoz        28850 Torrejón de Ardoz
         Madrid                         Madrid

** NECESARIO **   *** NECESARIO ***    *** NECESARIO ****   ** NECESARIO **

Bueno llega el momento de cubrirse las espaldas, sé que es un poco pesado pero, queremos que sepáis que:


** NECESARIO **   *** NECESARIO ***    *** NECESARIO ****   ** NECESARIO **
 

2.3.- Problemillas y consultillas.

Bugs

Llega el momento de entonar el 'mea culpa', os presento a continuación los errores detectados y/o comunicados, hasta el momento, sobre la primera lección (N. del A.: como la primera lección se ha distribuido en varias fases puede ser que algunos de los siguientes problemas ya los tengáis solucionados en vuestra primera lección):
 


        - Manda un mensaje de espera mientras se cargan las lecciones.
        - Permite la visualización de artículo de más de 199 líneas.
        - Mejora en la impresión de artículos.
        - Evita parpadeo de las cabeceras.
        - Permite la visualización e impresión de los fuentes.
        - Presenta los banner correctamente como ya os explique antes.
        - Permite desactivar/activar el color para monitores monocromos.
        - Evita bloqueos del teclado.
        - Mantiene en el menú el cursor sobre la última lección consultada.

Podéis consultar la ayuda para ver cómo acceder a las nuevas funciones. Como he comentado, respeto el formato de los artículos en la nueva versión así que podéis machacar tranquilamente el VCPV de la primera lección y poner el nuevo. Un problema de esta versión del VCPV es la visualización de colores que a lo mejor se produce con parpadeos, pero ya estoy agotado y he decidido dejarlo ahí. En cualquier caso si es muy tedioso el parpadeo desactivar el color y sin problemas. :)
 
 

Otras sugerencias curiosas que he recibido son:
 

        A - A mí nadie me ríe las gracias y tengo que hacerlo yo mismo.
        B - Es un modo de expresar mis sentimientos y de identificarme.
        De todos modos intentaré no abusar de ellos, pero antes permitirme un gustazo:  :-)

Para cerrar este artículo voy a publicar algunos mensajes que me han llegado y que considero proponen cuestiones que pueden interesar a más  personas:
 


 De    :Sergio Cruz Moral
 Para  :Jesús Angel Sánchez Mena
 Tema  :Un problemilla.
------------------------------------------------------------------------------
Hola Jesús, ¿Cómo andamos? ;-)

Mira, tengo un problemilla referente a los gráficos .PIC.

Yo los hago con  el  ANIMATOR  PRO.   Como  bien dijiste en el artículo correspondiente del curso los ficheros .PIC contienen un gráfico en formato semibruto.  Normalmente (el .PIC que  mandabas  dentro  del  curso)   suelen tener  una longitud de 64800 o 64748 bytes.  Pues el ANIMATOR PRO los  graba en el directorio \PAAT como  ficheros  .PIC  pero  de una longitud de  64846 bytes.  He intentado leer 78 bytes de cabecera del fichero, luego  los  768 que en teoría corresponderían a la paleta de colores y por último los 64000 que forman el dibujo en si.

Pues nada tío, me saca la paleta que le da la gana y cualquier parecido con la realidad es coincidencia (que todavía no se ha dado :-)).  ¿Sabes tú como va eso?. Me empiezo a desesperar.

Gracias  de  antemano y felicitaciones por el curso ;-).  A ver si te mando la respuesta a la encuesta que planteasteis.

Saludos
Sergio Cruz Moral
 

******************************************************************************

Hombre uno que me va a mandar la encuesta, te voy a poner un monumento en la plaza de mi pueblo. ;) Sobre la pregunta que planteas te voy a responder, pero no sólo quiero resolver esta duda, voy a proporcionaros un método para que podáis ver vosotros mismos el formato de diferentes archivos crudos. El método es simple, crearos una imagen toda del mismo color, por ejemplo el 01, después modificar la paleta y situar en el primer y último color unos valores para los componentes de RGB que podáis reconocer, por ejemplo, situad en los tres componentes el valor 02. Una vez hecho esto salvad la imagen en crudo, y con un editor binario, (PCT, NORTON...) mirad el resultado, fácilmente distinguiréis dónde está la cabecera, dónde la paleta y dónde la imagen.

Aplicando esta técnica para los ficheros .Pic de Animator Pro, que me mencionas descubrí su formato que es el siguiente: Esos ficheros están compuestos de una cabecera que ocupa 72 bytes, seguidamente a esta cabecera se sitúan los 768 bytes de la paleta, pero ojo, con una pequeña modificación, todos los valores de esta paleta se encuentran justificados a la izquierda por lo que hay que desplazarlos dos posiciones a la derecha o dividirlos por cuatro, antes de inicializarla. Seguidamente se guardan 6 bytes que son de relleno y después de estos, se sitúan los 64000 bytes de la imagen en crudo.

Os remito a los artículo La paleta y el formato gráfico PCX dónde tratamos asuntos relacionados con la paleta, su creación, utilización....

Veamos, siguiente pregunta.........
 



 De    :David Carrero
 Para  :Jesus Angel Sanchez
 Tema  :Primera leccion Curso de Programacion de Videojuegos
------------------------------------------------------------------------------
Hola Jesus ...

    Puedo pasar el curso por el area de programación de Alcazar BBs, donde
estoy habitualmente, estamos de momento 2 en el area de programación y dentro
de poco 3, ahh es un area local.

    Espero que no te importe que lo haga, aunque esperare tu respuesta.

******************************************************************************

David, veo que el apartado de condiciones no ha sido de los preferidos por ti, en ese apartado menciono que todas las lecciones distribuidas hasta el momento, pueden y nos gustarían que fuesen distribuidas por el mayor número de sitios posibles. Uno de los factores que determinarán si el CPV llegará a completarse algún día es el hecho de que se distribuyan las primeras lecciones a diestro y siniestro. Así que ya sabéis, 'dígaselo a su vecina'....

Hechos como este mensaje me emocionan, este país es el típico de dispara y después pregunta y David no ha actuado así, es un tío legal de  verdad. Os voy a proporcionar el teléfono de ALCAZAR BBs por que si todos son como David seguro que es una BBS de primera, a ver si se apunta más gente al  área de programación, espero que el CPV ayude a ello.

               !NUEVA BBs¡ Alcazar BBs --> (926) 55 09 03

Que pase el siguiente.............



 De    :Francisco Priego
 Para  :All
 Tema  :Curso Programacion Videojuegos
------------------------------------------------------------------------------
Hola All 8)

¿habéis pillado ya el CPV-01.ARJ?

Efectivamente está cojonudo, tal como esperábamos todos pero....

Unas peguillas:

El visualizador que viene con el cpv-01.arj no funciona, sale RUNTIME ERROR 0003 AT 0000:0A000  (tanto en msdos como en os/2).

El juego demostración "dardos" tampoco funciona:  te muestra las instrucciones, pero luego se queda la pantalla en negro, y no sale de ahí. El pcx de la diana se ve correctamente (con un visualizador de pcx, claro).

       ¿Qué ha pasado Jesus?
       ¿Os pasa a todos?

Bueno, y lo importante es que el texto escrito está muy bien, y sobre todo la rutinilla para mover texto haciendo un scroll por la pantalla está majísima 8).

******************************************************************************

La mayoría de las cuestiones que planteas ya las he respondido en el apartado de bugs, un hecho interesante que me comunicas es que ejecutas el CPV desde otro sistema operativo que no es MS-DOS, respecto a esa cuestión no se que decirte, en la primera lección no hurgamos demasiado en el interior de nuestra máquina pero a partir de este numero, empezamos a tocar componentes que puede que sean manipulados por otro sistemas operativos de diferente forma que lo hace el MS-DOS, recomiendo a todo el mundo, ejecutar el CPV desde el DOS de toda la vida, para evitar colisiones entre distintos procesos...

        Que pase el último.....


De      :Javier Manrique Tarrasa
Para    :Benjamin Moreno Palacios
Tema    :CPV-01
-----------------------------------------------------------------------------

Hola Benjamin. Te envio este MAIL para decirte que estoy encantado con vuestro CPV, y que espero que sigais en la misma linea que hasta ahora, es alucinante. Hace poco que he empezado a meterme en este mundillo de los videojuegos, y gracias a esta serie de articulos me vais a hacer la vida mas facil. Ya conocia otros cursillos de este tipo pero ninguno en espanol, y tan facil de entender. Me recuerda mucho al GDM, que es SHAREWARE (no habeis pensado distribuirlo de esta manera?, nos seria mas comodo a todos). Aparte de notificaros unos problemillas de incompatibilidad que tuve con el raton, me gustaria comentaros otras dudas generales que tengo sobre el CPV:

- En vuestro primer numero, prometisteis ofrecer algunos programas en C ademas de los de PASCAL, sin embargo solo han aparecido programas en PASCAL. Cuando comenzareis a dar algo en C ?

- En el temario que tambien distruisteis con este numero, comentais  que a partir de la leccion cuarta, vendra un capitulo titulado 'modo 4x13h'. Es este el tan famoso MODO X?. De que va eso ?.

Nada mas POR AHORA, ya os seguire comentando mis dudas. Pero no quiero despedirme sin felicitaros una vez mas por vuestro trabajo, que espero se vea recompensado como merece, porque si a mi me lleva tiempo leerlo y ejecutar todo, me imagino lo que os estara costando a vosotros hacer el CPV.  Hasta pronto.

*****************************************************************************

La verdad es que sí, estamos sudando sangre, pero recibir mensajes cómo el tuyo, nos da fuerza para seguir. 8D Intentaré responder a todas tus dudas:

- Es verdad que en un principio dijimos que iba a haber programas en C, (buena memoria Javier). Pues bien, a partir de la leccion tercera y cuarta, empezaremos a meter cosillas en C. Pero ojo, no os asustéis, las funciones que incluiremos ya os las habremos presentado en PASCAL y funcionarán de la misma manera sólo cambiará el lenguaje con el que serán codificadas, con esto al finalizar el CPV contaréis con una espléndida librería de funciones en dos lenguajes C y PASCAL.

- En cuanto a lo del modo 4x13h, nos referimos a un modo especial de la VGA (un modo 'tweaked') que se consigue a base de programar directamente ciertos registros de la VGA para obtener cuatro páginas de vídeo en vez de una. Las ventajas de tener estas cuatro páginas, es que podemos realizar una animación de sprites, efectuar unos 'scrolling' alucinantes... y mucho más.... El modo X, descubierto por Michael Abrash, tiene esta misma filosofía, sólo que para una resolución de 320x240. Existen multitud de modos tweaked, con otras resoluciones y otro número de páginas (360x200,360x240,376x564 ...), si tenemos tiempo (con esto de los exámenes, el curro de JASM y eso...) investigaremos la programación de estas resoluciones. Pero esto no está programado (por ahora), si alguien está puesto en estos temas, ya sabe a colaboraaaarrr. 8;)

En cuanto a la forma de distribución que nos comentas, la verdad es que nos lo estamos planteando seriamente, ya que, echando cuentas de lo que cuesta traerse un fichero de 300 Kb vía MODEM, os saldría más barato (a parte de aportar un donativo a nuestra causa) encargar las lecciones para que os lleguen cómodamente a vuestra casa por correo, obteniendo además, ventajas extras. Pero esto es todavía hacer conjeturas, para el numero tres de verdad, de verdad, de la buena, os comunicaremos la forma definitiva de distribución.8)

Me gustaría despedir este apartado dando las gracias a todo el mundo  que colabora de cierta manera con el CPV poniéndose en contacto con los  autores, es un hecho que nos gratifica ya que muchas veces nos sentimos muy solos.... :) 8)
 
 


2.4.- La rata del ordenata.

El ratón.

Para darnos cuenta de lo importante que es un periférico, tenemos que ver si se regala al comprar un ordenador, si es así sabemos que es imprescindible. ;)

Todos coincidiremos al pensar que cualquier programa que soporta el ratón es más fácil de utilizar y puede ofrecer más posibilidades de forma sencilla, que otro que no lo soporte. Pero a mi modo de ver en la programación de videojuegos el ratón no es muy importante, baso esta afirmación en dos hechos: primero en los videojuegos, los jugadores suelen buscar acción y prefieren que se les agobie un poco con los controles y que tengan que usar las dos manos para manejar al endiablado muñequito ;) y segundo existe otro periférico más indicado para el entretenimiento, el joystick, del que hablaremos en una próxima lección. :)

De todos modos vamos a estudiar el ratón, por varias razones: es un dispositivo que todo el mundo posee, a diferencia del joystick que sólo lo tienen los ludópatas profundos como yo, ;)) además, existe un tipo de videojuego en los que está muy indicado, las videoaventuras del tipo de Monkey Island, Indiana Jones...) y como última razón, siempre existe la posibilidad de incluir en nuestro programa un bonito menu controlado por la rata.

Para manejar el ratón existen una serie de funciones que son suministradas por el driver del fabricante, este driver se suele instalar durante el arranque del ordenador bien mediante un programa 'mouse.com', o con un archivo de dispositivo 'mouse.sys', en cualquier caso lo que hace este proceso es configurar la tabla de vectores de interrupción para que la int 33h apunte a un driver que se situará en memoria ofreciendo sus servicios.

Como os dije en la primera lección, mediante las interrupciones software se pueden acceder tanto a servicios de la BIOS, del DOS o como en este caso de un controlador de un fabricante de dispositivos.
 
Antes de ver las herramientas que podemos utilizar, tenemos que conocer las posibilidades que existen para controlar el ratón. Aquí como en la mayoría de periféricos existen dos variantes: Control mediante 'polling' (sondeo) o control por interrupción:

El primero es el más sencillo y consiste en preguntar periódicamente al dispositivo por su estado, la pega de este método es que tenemos que perder tiempo de CPU preguntando repetidas veces al ratón, cuando a lo peor no ha cambiado el estado del mismo.

El segundo método se apoya en el uso de interrupciones. Haciendo uso de unas funciones especiales del driver, podemos crearnos nuestro propio controlador y hacer que sea llamado al producirse un cambio de estado en el ratón, la ventaja de esta alternativa es que no tenemos que preguntar nada al periférico ya que éste se encarga de informarnos automáticamente de su estado, la desventaja es que es más complicado de implementar.
 

Veamos ahora los servicios ofrecidos por el driver, en total existen 53 funciones pero sólo voy a exponer las más utilizadas:

int 33h     Función 00h
 
         Entrada: ax   = 0000h  Función: Resetear el driver del ratón
 
         Salida:  ax   = FFFFh -> Driver del ratón instalado
                         bx = Número de botones del ratón
                  ax   = 0000h -> Driver no instalado

 Esta función la utilizaremos para comprobar si el driver está instalado y
 para hacer un réset del mismo.
------------------------------------------------------------------------------
 int 33h     Función 01h
 
         Entrada: ax   = 0001h  Función: Mostrar el cursor del ratón

         Salida:  Ninguna
 
 En modo texto el cursor por defecto es un rectángulo, en modo gráfico una
 flecha. Se pueden crear cursores propios pero no vamos a ver cómo, ya que es
 una función que no se suele utilizar.
------------------------------------------------------------------------------
 int 33h     Función 02h

         Entrada: ax   = 0002h  Función: Ocultar el driver del ratón

         Salida:  Ninguna
 
 Aunque el cursor esté oculto se sigue actualizando su posición.
------------------------------------------------------------------------------
 int 33h     Función 03h

         Entrada: ax   = 0003h  Función: Obtener posición del ratón y estado
                                         de los botones
         Salida:  cx   = Posición horizontal
                  dx   = Posición vertical
                  bx   = Estado de los botones

                    Bit 0    1=Pulsado botón izquierdo
                    Bit 1    1=Pulsado botón derecho
                    Bit 2    1=Pulsado botón central

 Las coordenadas son respecto a la pantalla virtual del ratón. En modo 13h
 éstas van del rango:
 
           Horizontal 0..639   (*Tendremos que dividirlas por 2*)
           Vertical   0..199
 
 Esta será la función que utilizaremos en el método de polling.
------------------------------------------------------------------------------
 int 33h     Función 04h

         Entrada: ax   = 0004h  Función: Posiciona el cursor del ratón
                  cx   = Posición horizontal
                  dx   = Posición vertical

         Salida:  Ninguna
------------------------------------------------------------------------------
 int 33h     Función 07h

         Entrada: ax   = 0007h  Función: Limita zona de movimiento horizontal
                                         del ratón
                  cx   = Mínima posición horizontal
                  dx   = Máxima posición horizontal

         Salida:  Ninguna

 Esta función y la siguiente nos servirán para determinar la zona en la que
 permitiremos el uso del ratón. El ratón es centrado automaticamente en dicha
 zona.
------------------------------------------------------------------------------
 int 33h     Función 08h

         Entrada: ax   = 0008h  Función: Limita zona de movimiento vertical
                                         del ratón
                  cx   = Mínima posición vertical
                  dx   = Máxima posición vertical

         Salida:  Ninguna
------------------------------------------------------------------------------
 int 33h     Función 0Ch

         Entrada: ax   = 000Ch  Función: Instala controlador de eventos
                  cx   = Máscara de eventos que provocarán que el controlador
                         sea llamado.

                   Bit 0 Movimiento del ratón
                   Bit 1 Pulsado el botón izquierdo
                   Bit 2 Soltado el botón izquierdo
                   Bit 3 Pulsado el botón derecho
                   Bit 4 Soltado el botón derecho
                   Bit 5 Pulsado el botón central
                   Bit 6 Soltado el botón central

                  es:dx = Dirección del controlador a instalar

         Salida:  Ninguna

 Está será la función que utilizaremos en el método de interrupción. Aunque
 hemos hablado continuamente de interrupciones, el controlador en verdad será
 invocado por el driver del ratón mediante una llamada Far call, esto quiere
 decir que el controlador deberá terminar con la instrucción ret.
 
 Los registros deberán ser salvados y posteriormente recuperados en el
 controlador si van a ser modificados. (Las rutinas embebidas en ensamblador
 en Pascal 'creo' que guardan automaticamente todos los registros)

 El driver del ratón le pasará al controlador de eventos la siguiente
 información en los registros del procesador:

      ax  = Máscara de eventos. Indicará los eventos que se han producido,
            puede haber ocurrido algún otro además del que nosotros
            interceptábamos.
      bx  = Estado de los botones
 
                Bit 0  Pulsado botón izquierdo                        *
                Bit 1  Pulsado botón derecho
                Bit 2  Pulsado botón central
 
      cx  = Coordenada horizontal del ratón.
      dx  = Coordenada vertical del ratón.
      si  = Longitud del último movimiento horizontal del ratón
      di  = Longitud del último movimiento vertical del ratón
            Tanto si como di vendrán en la unidades de medida del ratón o
            Mickeys :)) que se corresponden a 1/200 o 1/400 pulgadas. Os cuento
            esto para que fardéis como lo hago yo, ;) pero no vamos a utilizar
            estos valores.
      ds  = Segmento de datos del driver del Ratón. ¡Ojo! ésto a mi me consto
            mucho darme cuenta, Oo·:( si queréis utilizar en vuestro
            controlador variables Pascal tendréis que poner en ds el valor
            apropiado. Ver demo para más detalles.

------------------------------------------------------------------------------

Bueno, ya os he contado la teoría, pasemos a la práctica. En el siguiente programa os muestro un ejemplo de utilización de las funciones BIOS para el control del ratón, entiendo que alguien considere el programa un tanto machista, pero hasta ahora no he recibido ningún mensaje de chicas :) 8 -< y a ver si con ésto empiezan a participar.... ;)

Pulsa F2 para ejecutar el programa Ratón  - Posibilidad no disponible en la versión HTML-.

Espero que os haya gustado el programa, en el juego del mes también utilizo algunas de las funciones facilitadas, y allí podéis ver otra aplicación muy interesante de todas ellas. Nos vemos. :)
 


2.5.- La paleta.

Veamos ahora cómo se forman y se utilizan los colores en nuestro ordenador, siempre centrándonos en la VGA y en particular en el modo 13h.

En la lección anterior, ya vimos que un punto en pantalla se representa con un byte que indica su color. Como con 8 bits se pueden codificar 256 valores, este número será el máximo de colores representables simultáneamente en pantalla.

Que sólo podamos ver a la vez 256 colores no significa que no dispongamos de más posibilidades, en verdad esos 256 forman parte de un total  de 262144 colores que podemos seleccionar. Para saber de dónde viene este número tenemos que conocer cómo se construye un color.

Un color en las tarjetas VGA se define a partir de tres colores primarios: rojo, verde y azul (de ahí vienen los monitores RGB). Cada uno de estos componentes se representa con 6 bits, luego cada color se definirá con 18 bits (6+6+6) y echando cuentas (2 elevado a 18=262144) nos sale el número de colores que podemos ver, pero recuerdo, a la vez sólo podemos visualizar 256 de éstos.

Como complemento a la explicación y para afianzar las ideas, podéis ejecutar el programa COLOR para que 'veáis' cómo se forma un color:

Pulsa F2 para ejecutar el programa COLOR.  - Posibilidad no disponible en la versión HTML-.

Una estructura que podemos utilizar para almacenar un color sería la siguiente:

       Color:record
              Rojo:byte;            (*Utilizaremos sólo 6 de los 8 bits*)
              Verde:byte;
              Azul:byte;
             end;

Pero a nosotros nos va a interesar guardar los 256 colores que podemos visualizar en cada momento. Estos 256 colores son conocidos como paleta gráfica y la vamos a representar con la siguiente variable:

      Paleta:Array[1..256] of Color;
 
En la tarjeta de vídeo la paleta gráfica se almacena en 256 registros, de donde podrá ser referenciada más rápidamente que si se almacenase en memoria. Nosotros utilizaremos nuestra variable paleta para cambiar los valores de esos registros que al fin y al cabo son los que vamos a poder ver en pantalla.

Existen distintas herramientas para modificar la paleta gráfica, podemos trabajar con las funciones BIOS o utilizar los puertos de la tarjeta gráfica. Como el overhead (ver lección 1) en este caso es pequeño, el escoger una u otra posibilidad depende de vosotros. ;)

Las funciones BIOS más importantes para trabajar con la paleta gráfica son :

int 10h     Función 10h      Subfunción 10h
 
         Entrada: ah   = 10h  Función: trabajar con la paleta
                  al   = 10h  Modificar el valor de un registro de color
                  bx   = Número de registro
                  dh   = Porcentaje de rojo  (0..63)
                  ch   = Porcentaje de verde (0..63)
                  cl   = Porcentaje de azul  (0..63)
 
         Salida:  Ninguna
------------------------------------------------------------------------------
int 10h     Función 10h      Subfunción 12h
 
         Entrada: ah   = 10h  Función: trabajar con la paleta
                  al   = 12h  Modificar el valor de un conjunto de registros
                  bx   = Primer color (registro) a modificar
                  cx   = Número de colores a modificar
               es:ds   = Puntero a la tabla con los nuevos colores

         Salida:  Ninguna
------------------------------------------------------------------------------
int 10h     Función 10h      Subfunción 15h
 
         Entrada: ah   = 10h  Función: trabajar con la paleta
                  al   = 15h  Leer el valor de un registro de color
                  bx   = Número de registro
 
         Salida:  dh   = Porcentaje de rojo  (0..63)
                  ch   = Porcentaje de verde (0..63)
                  cl   = Porcentaje de azul  (0..63)
 
int 10h     Función 10h      Subfunción 17h
 
         Entrada: ah   = 10h  Función: trabajar con la paleta
                  al   = 17h  Leer los valores de un conjunto de registros
                  bx   = Primer color (registro) a leer
                  cx   = Número de registros a leer
               es:dx   = Puntero a un búffer donde almacenar los colores
 
         Salida:  Puntero al búffer con los valores pedidos
 
Si utilizásemos los puertos de la VGA, tenendríamos que saber que el número de color que queremos cambiar debe situarse en el puerto 3C8h y los tres componentes de dicho color deberan mandarse consecutivamente a través del puerto 3C9h (Primero Rojo, depués Verde y finalmente Azul.

Veamos como ejemplo esta rutina que cambiaría un color de la paleta:

        port[$3C8]:=Color;  (*Puerto donde ponemos el color a modificar*)

        port[$3c9]:=Paleta[Color].Rojo;   (*Porcentaje de rojo*)
        port[$3c9]:=Paleta[Color].Verde;  (*Porcentaje de verde*)
        port[$3c9]:=Paleta[Color].Azul;   (*Porcentaje de azul*)

Si quisiéramos cambiar un conjunto de colores:

        port[$3c8]:=Color   (*Primer color a modificar se incrementa
                              automáticamente*)
 
        for i:=color to UltimoColor do  (*Grupo de colores*)
         begin
          port[$3c9]:=Paleta[i].Rojo;   (*Porcentaje de rojo*)
          port[$3c9]:=Paleta[i].Verde;  (*Porcentaje de verde*)
          port[$3c9]:=Paleta[i].Azul;   (*Porcentaje de azul*)
         end;
 

Para que veáis cómo utilizar estos servicios ejecuta y consulta cuando quieras el programa PALETA. :)

Pulsa F2 para ejecutar el programa PALETA. - Posibilidad no disponible en la versión HTML-.

Bueno ya sabemos cómo instalar una paleta, ahora tenemos que ver qué podemos hacer con ella aparte de conseguir que las imágenes se visualicen con sus correspondientes colores. Como os habréis dado cuenta al ejecutar el programa anterior podemos producir un cambio instantáneo en una imagen con sólo cambiar su paleta. Esto se debe a que no hay que volver a dibujar la imagen, ésta no se modifica, lo que cambia es el color que representa cada byte en la memoria de vídeo.

Pongamos un ejemplo, si tenemos en una imagen un cielo de color azul, (supongamos número de color 50) si queremos oscurecer el cielo para hacer un efecto de atardecer, no tendríamos que dibujar todo el cielo con el color azul oscuro, (supongamos color 51), únicamente tendríamos que intercambiar los valores de RVA de ambos colores, hecho que es casi instantáneo con cualquier método que utilicemos.

En esta idea se basa uno de los efectos más utilizados tanto en la programación de videojuegos, como en resto de áreas de la programación. Los fundidos (del inglés fade). Este efecto puede hacerse de dos maneras, o partimos de la imagen original y la vamos oscureciendo hasta que quede la pantalla negra (fundido descendiente o fade out), o al reves (fundido ascendente o fade in). El fundido descendiente se realiza de la siguiente manera:

Para hacer el efecto, nos valdremos de una paleta auxiliar que nos servirá para hacer las modificaciones, ya que si utilizásemos la paleta original perderíamos los valores de ésta y tendríamos que volver a recuperarla o generarla. El truco consiste en ir multiplicando los tres componentes de cada color de la paleta original por un factor y almacenar el resultado en la paleta auxiliar, cuando hayamos multiplicado los 256 colores 'instalamos' la paleta auxiliar y volvemos a multiplicar todos los componentes de los colores por un nuevo factor, si éste va disminuyendo de 1 hasta 0 conseguiremos que los colores de nuestra imagen, se conviertan desde su color original (factor=1) hasta el color negro (factor=0). La rapidez del fundido dependerá de la rapidez con que disminuyamos nuestro factor. Veamos una rutina en Pascal que realizaría un fundido:

procedure FundidoD(Paleta:TipoPaleta);
const Pasos=200;
var i,j:integer;
    Paletaux:TipoPaleta;   (*Variable para almacenar la paleta en cada paso*)
begin
  for i:=Pasos downto 0 do (*Número de pasos del fundido*)
    begin
      for j:=0 to 255 do   (*Todos los colores*)
        begin
          Paletaux[j].Rojo:=(Paleta[j].Rojo*i) div Pasos;
          Paletaux[j].Verde:=(Paleta[j].Verde*i) div Pasos;
          Paletaux[j].Azul:=(Paleta[j].Azul*i) div Pasos;
        end;
      CambiarColoresA(Paletaux);  (*Instalamos la nueva paleta en cada paso*)
    end;
end;

Os he mostrado cómo realizar un fundido descendente, para hacer un fundido ascendente tendréis que utilizar la misma rutina con la modificación de que el factor tiene que variar desde 0 hasta 1, ésto lo conseguimos con sólo cambiar el primer bucle for por el siguiente:

  for i:=0 to Pasos do

Podéis ejecutar ahora el programa FUNDIDO donde apreciaréis el efecto conseguido con este truco. :)

Pulsa F2 para ejecutar el programa FUNDIDO  - Posibilidad no disponible en la versión HTML-.

A lo peor durante la realización del efecto os aparece un tedioso parpadeo que estropea el fundido. Cómo evitar este contratiempo será uno de los temas para la siguiente lección. Además empezaremos una serie de artículos dónde jugaremos con la paleta para conseguir distintos efectos gráficos. Tened paciencia. ;)
  


2.6.- Formato gráfico PCX.

PCX es el formato original que utilizan los archivos gráficos de la saga de programas Paintbrush, aunque hoy en día es utilizado o soportado por la mayoría de programas.

Existen diferentes versiones dentro del mismo formato, la mayor clasificación se hace teniendo en cuenta la profundidad de color (o número de bits con los que se codifica un color), según esta clasificación podemos distinguir versiones de 4, 8 o 24 bits.

Nosotros nos vamos a centrar en el formato de 8 bits o de 256 colores y para el tamaño de 320x200, pero extrapolar mis explicaciones a otros tamaños es sencillo. :)

El formato PCX consta de una cabecera de 128 bytes, ésta contiene toda la información necesaria para identificar la versión y el tamaño de la imagen que contiene el archivo. Os muestro ahora detalladamente toda la información que contiene la cabecera:
:
 Posición    Longitud (bytes)      Significado              Valor en modo 13h

    0             1              Identificador PCX               0A
    1             1              Versión                         05
    2             1              Codificado RLE (1=SI)           01
    3             1              Bits por píxel                  08
    4             2              Esquina superior izqd. X      0000
    6             2              Esquina superior izqd. Y      0000
    8             2              Anchura - 1                   013F
   10             2              Altura  - 1                   00C7
   12             2              Resolución horizontal         0060
   14             2              Resolución vertical           0060
   16            48              Paleta de colores EGA       No lo utilizamos
   64             1              Sin uso                         00
   65             1              Planos de color                 01
   66             2              Bytes por línea               0140
   68             2              Tipo de paleta                0001
   70            58              Sin uso                         00

Me gustaría destacar dos cosas, primero los valores que constan de dos bytes son números enteros que se almacenan en memoria de la forma llamada de punta delgada, esto en cristiano quiere decir que primero viene el byte de menor peso y seguido el byte de mayor peso. Por ejemplo el número 140 se almacenaría como 40 01.

El segundo asunto que quería comentaros es la forma de almacenar la paleta que tiene el formato PCX. Como os habréis dado cuenta al ver la cabecera, ésta tiene un espacio reservado para contener la paleta, pero sólo para la tarjeta EGA, como en ese espacio no se podía guardar de ninguna manera la paleta VGA, se optó por almacenar ésta al final del archivo situando antes el valor 0C para identificarla y después los 768 bytes de la paleta. Los valores de la paleta se encuentran justificados a la izquierda en cada byte por lo que hay que desplazarlos a la derecha 2 posiciones o dividir por 4, antes de almacenar lo leído en nuestra variable paleta. El por qué de este rodeo preguntárselo al ingeniador.

La imagen en sí, se almacena justo detrás de la cabecera, es decir, a partir del byte 129. Esta imagen no viene en un formato crudo, como vimos en la primera lección, sino que viene comprimida mediante el algoritmo RLE (Run Length Encoding), cuya traducción Shakespiriana sería algo así como codificación por longitud de serie. Este método se basa en el hecho de que en la mayoría de los gráficos, se repiten consecutivamente muchos colores, por ejemplo imaginemos un fondo de cielo todo azul.

El funcionamiento del algoritmo lo expondré a través de un ejemplo. Imaginemos que en un gráfico se presenta la siguiente serie de colores:

        21 21 21 21 21 21 21

en el fichero PCX esta cadena se almacenaría como 07 21, es decir, la longitud de la serie seguido del elemento que se repite, pero para que no se produzca una confusión al reconocer un byte y distinguir entre la longitud y el valor de un color se optó por el siguiente criterio:

Si el byte indica la longitud de una cadena tendrá los dos bits de mayor peso 6 y 7, puestos a 1, el resto de ese byte (0..5) indicarán la longitud de la serie. Detrás de este byte se situará el color codificado con 8 bits. Para el caso de que no exista una serie y haya que codificar sólo un color éste se representará normalmente, con un sólo byte, siempre que el color sea menor de 192, sino es así, se representa como una serie de longitud 1. :)

        Un ejemplo más completo podría ser el siguiente:

   Cadena original 5x3              Cadena comprimida RLE

    21 21 21 21 FA                    C4 21 C1 FA
    28 1A 1A 1A 1A                    28 C4 1A
    3E 3E 3E 3E 2A                    C4 3E 2A

Relación de compresión 15:10 <> 3:2 <> 66 por ciento del tamaño original.

Sabiendo cómo se almacena la imagen ya somos capaces de recuperar y visualizar una imagen almacenada con formato PCX, veamos los pasos detallados:
 

Leer la cabecera del dibujo y comprobar que se trata de un fichero PCX de dimensiones 320x200 y con 256 colores.

2º Posicionarse sobre los últimos 768 del fichero para leer la paleta del dibujo.

Inicializar dicha paleta si queremos visualizar la imagen según se descomprime. Otra opción es empezar con la paleta a negro y una vez la imagen descomprimida, 'cargar' la paleta original. :)

4ª Situarse detrás de la cabecera, y empezar a descomprimir la imagen.
 

4.1 Leer un byte del archivo.

4.2 Si es mayor de 192, o lo que es lo mismo los dos bits de mayor peso puestos a 1, es el indicador de una cadena luego cogemos los seis bits de menor peso que será la longitud de la serie y leemos el siguiente byte que será el color. Si no es mayor de 192 la longitud de la serie será de uno y el color ya lo tenemos.

4.3 Llevamos a la pantalla tantas veces el color leído como indique longitud. Lo situamos en las coordenadas que correspondan.

4.4 Si estamos dentro de las coordenadas 320x200 vamos al paso 1, si no es así se acabó. ;)


Como es habitual, con el presente artículo os suministro un programa de demostración con sus fuentes, para que veáis implementado el algoritmo anterior.

Pulsa F2 para ejecutar el programa VISU_PCX.  - Posibilidad no disponible en la versión HTML-.

Como os daréis cuenta, la imagen que muestra no es nuestra pero con imágenes como esa luce más el programa, ;)) desde luego los de Microprose se lucieron de verdad al realizar esa magnífica presentación y suministrarla en formato FLI. X-))

Bueno con el programita acaba esta lección, próximamente trataremos el formato gráfico GIF, mucho mejor y por tanto, como suele ser normal más complicado. :)
 
 

2.7.- El modo 13h. 2/3.

Sprites

En el anterior capítulo sobre el modo 13h vimos como funcionaba la memoria de vídeo, sacamos puntos aleatorios por pantalla y estudiamos las imágenes en crudo... En esta entrega vamos a estudiar los sprites, la forma de almacenarlos, cómo sacarlos por pantalla y la manera de hacer animaciones con ellos. :)

Un sprite no es más que un bloque rectangular de puntos que representa una imagen determinada. Estas imágenes serán los objetos animados de los videojuegos, a diferencia del resto de gráficos estáticos que formarán el llamado fondo de la pantalla. También pueden existir sprites que no necesariamente se tengan que mover (un cargador para nuestra ametralladora, sangre extra... ya sabéis, brutalidades de ésas ;) pero que pueden ser situados libremente en la pantalla por lo que también los consideraremos animados.

Una definición más general y quizás más acertada sería considerar los sprites como todo objeto que tiene un significado en el juego y que no es simplemente un elemento decorativo. :)

Como he dicho, los sprites son rectangulares, aunque siempre los distingamos como objetos irregulares. Se almacenan bajo esa forma geométrica para que los gráficos puedan ser llevados más rápidamente a la pantalla conociendo únicamente las dos dimensiones del rectángulo, anchura y altura. Si se almacenasen con su forma original habría que tener muchísima más información para reconocer el contorno del dibujo, luego no es viable.

Para almacenar los sprites en memoria secundaria (disco duro) se pueden utilizar varias técnicas:
 


Nosotros utilizaremos la primera técnica ya que en el programa de dibujo que solemos emplear (Autodesk 2d Animator. ¡Toma publicidad gratuita! ;) , existen un tipo de elemento que se corresponde exactamente con la definición de sprite, los archivos CEL.

Estos archivos se utilizan para salvar partes de una pantalla, la forma de almacenarse es idéntica a la del archivo crudo aatemp2 que explicamos en la primera lección, esto es, fila tras fila. La diferencia está en el tamaño de los archivos y la forma de acceder a ellos.

El tamaño ahora es variable (no 64800 como antes) depende de la anchura y altura del sprite. Imaginemos que salvamos en un archivo Cel un pequeño cuadrado de pantalla que tiene un tamaño de 10x10. El archivo Cel tendrá un tamaño de 32 bytes de cabecera + 768 de la paleta + (10x10) de la imagen= 810 bytes.

La otra diferencia es la forma de obtener los gráficos ya que ahora se puede acceder a ellos directamente y no tenemos que irnos a directorios temporales y rollos de esos. :)

Los archivos Cel también pueden ser utilizados para almacenar pantallas enteras de 320x200, en este caso podemos utilizarlos en programas como Imagen, suministrado en la lección 1.

Otra característica importante de los archivos Cel es la posibilidad que tenemos de reconocer la anchura y altura de la imagen contenida en el archivo. Estos datos son almacenados en la cabecera, la anchura en la posición 3 y la altura en la posición 5, en verdad se utilizan dos bytes, pero como no utilizaremos sprites más grandes de 255 pixels no habrá problema. :)

Lo último que os quiero comentar sobre los archivos Cel es el hecho de que en cada fichero se almacene la paleta. Sabiendo ésto, si recuperamos distintos sprites que fueron realizados con la misma paleta, sólo deberemos inicializarla una vez, aunque la leamos en todos los casos.

Ya sabemos cómo se guardan nuestros muñequitos en el disco, ahora queda saber cómo los almacenaremos en nuestro programa. Tenemos también varias posibilidades:
 


Los que tenéis buenos conocimientos en estructuras abstractas de datos ya sabéis cual es la solución óptima. Para los que no, me explicaré lo mejor que pueda. Si optamos por cualquiera de las tres primeras opciones, tendremos que definirnos los arrays antes de saber el tamaño de los sprites, con lo cual fijaremos un tamaño máximo para los mismos y estaremos desaprovechando memoria.Un ejemplito:

Tenemos dos sprites uno de tamaño 20x20 y otro de 50x50, si nos definimos nuestro TipoSprite como:

        TipoSprite=Array[1..30,1..30] of byte;

En el primer caso estaremos desperdiciando 500 bytes de memoria y en el segundo no podremos almacenar el sprite en una variable de ese tipo.

La solución es utilizar la asignación dinámica de memoria. El método consiste en definirnos la variable donde vamos a almacenar el sprite como un puntero general, en pascal Pointer, y una vez sepamos el tamaño del dibujo asignarle la memoria estrictamente necesaria, mediante instrucciones como Getmem(Puntero,Tamano) de Pascal. En el ejemplo anterior nos definiríamos dos variables tipo Pointer:

 var    Sprite1,Sprite2:Pointer;

Y a cada una le asignaríamos, una vez calculado el tamaño, su correspondiente memoria

        GetMem(Sprite1,10*10);
        GetMem(Sprite2,30*30);

Para liberar la memoria asignada dinámicamente y dejar así que otros programas utilicen la misma existen instrucciones como la FreeMem de Pascal

        FreeMem(Sprite1,10*10);

El problema de este método es saber cuál es la anchura y altura de la imagen referenciada por el puntero. Para ello nosotros optamos por una fácil solución, almacenar la anchura y altura del sprite en las primeras posiciones de la memoria apuntada por el puntero. Ojo tened en cuenta que ahora tendremos que reservar al sprite un área de memoria de tamaño dos bytes mayor que antes.

Una vez esté la imagen en memoria principal el sacarla por pantalla es tarea fácil, leer fila a fila la imagen y visualizarlas. Para hacer este trabajo tenemos varias posibilidades, utilizar BIOS o ADMV (Acceso Directo a Memoria de Vídeo) para llevar puntos a la pantalla y utilizar lenguaje LAN o ASM. En las rutinas que entregaremos con esta lección utilizaremos ADMV y LAN, en la próxima lección os daremos las buenas ADMV y ASM.

Ejecutad ahora y consultad cuando podáis el programa demostración que carga y visualiza un sprite en pantalla. :)

Pulse F2 para ejecutar el programa SPRITE1. - Posibilidad no disponible en la versión HTML-.

Una vez que sabemos sacar sprites por pantalla, hacer una animación es sencillo. Una secuencia animada es una sucesión de imágenes estáticas que visualizadas rápida y consecutivamente producen un efecto de movimiento.

La animación puede constar de una única imagen que se desplaza por la pantalla,(ejemplo: una nave espacial), por varias imagenes que se suceden en la misma posición (una hoguera) o por varias imagenes que se suceden y se desplazan por la pantalla (una hormiguita con un peazo ametralladora que anda por la pantalla). X-))

La forma de realizar una animación también es simple. Almacenar en un array todas las imágenes que componen la secuencia e ir cambiando el índice del array para mostrar un sprite diferente en cada paso.

Type
     TipoSprite=Pointer
Var
     Animacion:Array [1..3] of TipoSprite;
     Indice:integer;

     Indice:=1;
     repeat
      ???
       PonSprite(x,y,Animacion[Indice];
       Indice:=(Indice mod 3)+1;         (*Hace variar el indice de 1..3*)
      ???
     until ???

Bueno se acabó este capítulo.... ¿Qué..? ¿Qué decís...?, ¡Ah! que queréis más información, bueeeeeno, ;)) tomad una demo. :)

Pulsa F2 para ejecutar el programa ANIMACIO. - Posibilidad no disponible en la versión HTML-

Podéis ver qué sencillo es animar una imagen, lo difícil de verdad es dibujarlas, como veréis una de las animaciones se compone de 10 imagenes...Gracias de verdad a Juán Antonio Ubeda Torres (grafista de Hormigator). :)

En la próxima y última entrega sobre el modo 13h, os mostraremos cómo hacer que las imágenes respeten el fondo al visualizarse, evitaremos los parpadeos tediosos de la pantalla y aceleraremos nuestros videojuegos trabajando en ensamblador puro y duro, así que ya sabéis, ;) nos vemos... :)

Sí, sí, aquí acaba este capítulo. ;)



2.8.- El PIT - 8253/8254.

PIT.


En el artículo sobre el speaker de la lección anterior, ya hablamos de este componente del PC, entonces sólo vimos lo que se necesitaba para generar una onda cuadrada a través del tímer 2, pero éste dispositivo puede dar mucho más juego como veremos en éste y otros artículos. :)

EL PIT es una de tantas pastillitas que forman nuestro PC, el circuito integrado que lo implementa se conoce como 8253 o 8254. La palabra PIT viene de Programable Interval Timer y la traducción literal sería 'temporizador programable de intervalos'. El 8253 cuenta con tres tímers independientes y para ver qué es un tímer me basaré en las tres palabras que forma el acrónimo PIT ya que definen perfectamente al aparatillo en cuestión:
 


Cada uno de los tímers del PIT tiene una misión específica:
 


Cada uno de estos tímers pueden ser programado de diferentes maneras, veamos los 6 posibles modos de funcionamiento:

Modo 0: (Interrupción al final de cuenta) Una vez cargado un valor en el contador, la salida permanece a nivel bajo hasta que llegue a cero, momento en que la salida permanecerá a nivel alto hasta nueva carga del contador.
 


Los modos 1 y 5 necesitan de una transición (cambio de nivel 0 a 1 ) en una de las patillas del chip (Pin Gate), para comenzar la cuenta.

Bueno una vez visto qué podemos hacer, os tengo que enseñar cómo hacerlo:

El PIT cuenta con 4 puertos, éstos son:
 


El puerto de control sirve para indicar el modo de funcionamiento de cada tímer, los valores posibles para este byte:

        ---------------------------------     A: Selección de contador:
        | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |          00 = Timer 0
        ---------------------------------          01 = Timer 1
         ======= ======= =========== ===           10 = Timer 2
                                                   11 = Comando de lectura
            |       |         |       |
            |       |         |       |       B: Forma de acceso al contador:
            |       |         |       |            00 = Comando de latch
            A       B         C       D               Es como el lap de los
                                                      cronómetros, el contador
  C: Modo de funcionamiento:                          sigue funcionando, pero
                                                      la salida está fija.
     000 = Modo 0                                  01 = Lectura/escritura del
     001 = Modo 1                                     byte menos significativo
     010 = Modo 2                                     del contador( 16 bits)
     011 = Modo 3                                  10 = L/E del byte más sign.
     100 = Modo 4                                  11 = L/E primero del B-S
     101 = Modo 5                                     después del B+S.

  D: Formato de la cuenta:

      0 = Binario
      1 = BCD

Como siempre la forma de entender el manejo del puerto del estado será con un ejemplo, en el artículo de la primera lección sobre el speaker, os dije que para programar el tímer 2 para que genere una onda cuadrada era $B6, os prometí una justificación pues aquí está:

         <---- B ---->   <---- 6 ---->
       ---------------------------------       Programación del tímer 2,
       | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |      para generar una onda cuadrada.
       ---------------------------------
        ======= ======= =========== ===
        Timer 2    |      Modo 3     Cuenta en binario lo más normal
                   |
                Como visteis, primero
                cargábamos el byte menos
                significativo y después
                el más significativo.

Bien, una vez programado el PIT es necesario indicar el valor para el contador, la forma de mandar los valores ya estará decidida y normalmente será mandando dos bytes (B-S, B+S). Para calcular el valor del contador, ya os conté el método, pero me voy a repetir cómo el pepino. :)

                        1193180     <-- $1234DD (hexadecimal)
          Contador   = ----------
                       Frecuencia
                        deseada
Ese tochazo de valor es la frecuencia del reloj interno que regula el funcionamiento del PC, suele ser un oscilador del tipo 8284, y como curiosidad os diré que el valor se obtiene a partir de la división (4.77272 / 4 MHz).

Bueno, ya hemos visto toda la teoría necesaria, os puede parecer un rollazo de campeonato, pero el PIT es una herramienta de gran potencia, como veréis en ésta y en la próxima lección, nosotros vamos a utilizarlo para trabajar con el speaker aunque su campo de aplicación es mucho más amplio. El presente artículo puede ser una buena guía de referencia para posteriores trabajos, así que no lo olvidéis. :)


2.9.- Sonido background.

666

En el número anterior vimos cómo generar un sonido programando el tímer 2, pues bien, eso se ha acabado para nosotros, el motivo es que estamos trabajando en Pascal y ya existen en éste y otros LAN, instrucciones bastante eficientes (dígase Sound y Nosound) que realizan lo que a nosotros nos costaba varias líneas de código, si vuestra situación es diferente al estar realizando vuestro cursillo particular en C o ASM puro y duro, pués na', seguir utilizando las rutinas facilitadas y sin problemas. :)

En ése artículo, os mostré cómo generar un sonido por el speaker sin intervención de la CPU, con ello ya podíamos hacer un sonido residente. El problema es que ésto sólo nos vale para sacar una nota, un pitido, en el caso de querer emitir una pequeña melodía (sucesión de notas) con éste método tendríamos que controlar la duración, apagar la nota actual y hacer sonar la siguiente. En el programa demostración de aquella lección utilizábamos esta técnica y como visteis las posibilidades estaban bastante limitadas. En el caso de un videojuego no podemos estar pendientes de que una melodía se ejecute nota a nota, ya que estaremos bastante ocupados 'con otras cosas'. :)

Tenemos que hacer que 'algo', ajeno a nuestro proceso, se encargue de  ir cambiando las notas de nuestros efectos sonoros, ahí es donde entra en juego el PIT (al que hemos dedicado un artículo completo). Cuando os expliqué el funcionamiento del Tímer 0, os dije, que era ideal para controlar procesos demonios... y tal y tal, veamos ahora de qué va eso.

El nombre de procesos demonios lo escuché por primera vez al estudiar los sistemas operativos multitarea, en concreto UNIX. En estos sistemas existen dos tipos de procesos: interactivos o sometidos (background). La mayor diferencia entre ambos, es que los interactivos necesitan de la acción del usuario y los sometidos no, además estos últimos suelen ejecutarse en los 'ratos libres' del ordenador. En el caso de los PC-DOS hablar de demonios es bastante erróneo, ya que como sabréis, a pesar de que los procesadores Intel a partir del 386 soportan la existencia de varias tareas, el D.O.S. no aprovecha esta capacidad al trabajar en modo real y sólo soporta la ejecución de un proceso a la vez (monotarea). Gracias al PIT y en concreto al tímer 0 vamos a poder tener un pseudo-demonio, que se ejecute periódicamente, realizando taréas que no necesiten de nuestra intervención, en nuestro caso, que se encargue de sustituir las notas de los efectos sonoros del speaker.

Bueno, llega el momento de ponerse manos a la obra, lo primero que  necesitaremos en nuestro programa es una estructura de datos donde se almacenarán las melodías, los tipos de datos siempre podréis variarlos para adaptarlos a vuestras necesidades, nosotros utilizamos una estructura estática como la siguiente:

        Const MaxNotas=30;         (*Máximo número de notas de la melodía*)

        Type

          TipoNota= record              (*Registro que representa cada nota*)
                     Frecuencia:word;   (* de la melodía, almacena su      *)
                     Duracion:integer;  (* frecuencia y su duración        *)
                    end;
      TipoMusica=Array[1..MaxNotas] of TipoNota; (*Tipo de nuestras melodías*)

Como véis, con ésta estructura limitamos nuestros sonidos a un número máximo de notas, lo que reducirá nuestras posibilidades a emitir pequeños efectos como los que vimos en la lección anterior. En verdad se pueden sacar auténticas sinfonías por el speaker, pero un abuso de este dispositivo puede resultar tedioso y el usuario en cuanto pueda, pasará del speaker y vuestro trabajo codificando canciones habrá sido en balde, y éso duele... :)

Un ejemplo de demonio que basándose en esta estructura emita sonidos podía ser el siguiente:

var
    Musica:TipoMusica;           (*Variable que almacena la melodía a tocar*)
    Ejecutando:boolean;          (*Se está emitiendo una melodía*)
    Actual:byte;                 (*Nota actual de la melodía*)

{$F+}                    (*Fuerza llamadas Far -necesario para controladores*)
Procedure Demonio;interrupt;           (*Procedimiento de interrupción*)
begin
 if Ejecutando then                    (*¿Se está ejecutando algún sonido?*)
  begin                                (* SI*)
   dec(Musica[Actual].Duracion);       (*Decrementamos la duración de la nota*)
   if (Musica[Actual].Duracion=0) then (*¿Tenemos que cambiar de nota?*)
    begin                                (* SI *)
     inc(Actual);                        (*Pasamos a la nota siguiente*)
     if (Actual=MaxNotas+1) or (Musica[Actual].Duracion=0) then (*Fin melodía*)
      begin                                 (* SI *)
       Ejecutando:=false;                   (*Ya no estamos emitiendo sonidos*)
       Actual:=1;                           (*La próxima será la primera nota*)
       nosound;                             (*Deja de emitir sonidos*)
      end
     else                                   (* NO fin de melodía*)
      sound(Musica[Actual].Frecuencia);     (*Cambiamos la nota de la melodía*)
    end
  end                                    (* Fin if de cambiar nota*)
 else                                  (* NO se está ejecutando sonido*)
 if (Musica[Actual].Duracion<>0) then  (*Cuando actual pase a valer 1*)
  begin                                (* pasaremos a ejecutar la melodía *)
   Ejecutando:=True;                   (*Estamos emitiendo música*)
   Sound(Musica[Actual].Frecuencia);   (*Sacamos la primera nota*)
  end;
 inline($9c);          (*Salva registro de bandera - necesario antes de int*)
 Reloj_Proc;                   (*Llamamos a la rutina original del IRQ 0*)
end;
{$F-}                          (*Fin de directiva*)

Cuando queramos cambiar de sonido o empezar uno nuevo tendremos que ejecutar un rutina como la siguiente:

  Procedure ActualizarMusica(NuevaMus:TipoMusica);
  begin
   inline($FA);           (*Instrucción ASM CLI no permite interrupciones*)
   Musica:=NuevaMus;      (*Cambia la melodía*)
   Actual:=1;             (*Primera nota*)
   Ejecutando:=false;     (*El demonio se encargará de empezar la melodía*)
   inline($FB);           (*Instrucción ASM STI permite interrupciones*)
  end;

Quiero mencionaros un detalle, os habréis fijado que al final del proceso demonio, llamo al procedimiento Reloj_Proc; en este procedimiento guardaremos la rutina original que atiende a la IRQ 0. Como ya os he dicho en la lección, esta rutina se encarga de mantener en hora el reloj del sistema, al ejecutar más instrucciones antes de la rutina original estaremos retrasando la llamada al procedimiento y paralelamente el reloj del sistema, pero os diré que el mío está dos meses retrasado y no pasa na'. :)

Siendo radicales, las dos últimas instrucciones pueden ser sustituidas por la orden 'Port[$20]:=$20' con ella, mandamos un Ack a la CPU que indica  que la llamada a la interrupción ha concluido normalmente, al no llamar a la rutina original, el reloj del sistema no se actualizaría, pero como os he dicho antes, el retraso producido no os tiene que preocupar.

Para hacer que nuestro demonio cantor 'se active' tenemos que sustituir la rutina que atiende a la IRQ 0, por la nuestra. La dirección de esta rutina se almacena en la posición 8 de la tabla de vectores de  interrupción (Para más detalles sobre este tema consulta el artículo La  Cacharrería de la lección 1), este proceso es idéntico al que utilizamos para sustituir el controlador del teclado, así que no voy a entrar en más detalles.

Como siempre os proporciono un programa demostración, donde implemento todo lo explicado en este artículo, me gustaría que en la siguiente demo probéis a apagar y encender el turbo, para que veáis que la IRQ 0 se genera con la frecuencia que fue programada y que es por tanto, independiente de la velocidad a la que funcione el microprocesador:

Pulse F2 para ejecutar el programa SPKBCK. - Posibilidad no disponible en la versión HTML-.

Con ésto ya podéis incluir el speaker perfectamente en vuestros juegos, 'Hormigator' llegó hasta aquí, pero el CPV va a ir un poco más lejos y en la  próxima lección os voy a mostrar cómo sacar sonidos digitalizados por el speaker, os quiero ver a todos en clase para entonces, ahora, al recreo todos, vaaaaamos. ;)
 


2.10.- Como se hizo...

Dardos


Quizás éste sea el artículo más esperado por muchos de vosotros,
- Ondiá, cómo hacer un juego, ya está me leo ésto y me hago un DOOM en cinco minutos
- No hijo no, si piensas así estas muuuuuy equivocado. :)

Como veréis esta sección es sólo un ejemplo de cómo aplicar lo explicado en el resto de los artículos para una aplicación en concreto y por consiguiente el objetivo es que una vez que os hayáis empollado el resto de artículos, paséis a estudiaros el juego del mes y así os resulte más fácil entenderlo. Además el programa que servirá de demostración no va a ser un nuevo lanzamiento mundial, todo lo contrario serán juegos sencillos y la mayoría clásicos, pero que os valdrán para entender los pasos básicos para la creación de un videojuego en su totalidad.

Estos pasos, son independientes del tipo de juego que vayáis a  realizar, y por esta razón en un principio pensamos en incluir un único artículo al final del curso explicando el método, realizar un juego final y ya está. Pero las cosas cambiaron, nos dimos cuenta de que muchos estáis impacientes por empezar a darle a la tecla y si dejásemos lo bueno para el final quizas os estuviésemos defraudando, y a la vez, faltaríamos a nuestra  palabra de realizar un CPV verdaderamente práctico.

Con todo ésto cambiamos nuestro parecer y decidimos realizar un juego por lección, explicarlo un poco por encima y sobre todo comentaros nuestra forma de proceder para realizar estas aplicaciones.

Para empezar con el tema os mostraré los pasos que solemos seguir para desarrollar un videojuego en general, para después centrarnos en cómo  se hizo el programa Dardos. Al desarrollar un videojuego conviene seguir las siguientes etapas:
 

La parte de programación, aunque os pueda parecer la más dura, no es así, en verdad la codificación tiene mucha tarea la primera vez, pero una vez escritas las rutinas, éstas son perfectamente reutilizables. Por ejemplo, sacar una imagen por pantalla requiere el mismo proceso dibujemos un elefante que una patata.
  1. Los programadores están hasta las criadillas del programita.
  2. A mi me ha pasado de estar probando un programa un mes, darle el visto, llegar un chavalote y en 30 segundos destrozarme el invento. Esto lo entiendo ya que esa gente 'no tiene escrúpulos'. ;)


Bueno ya os he comentado los pasos en general, me centraré ahora en un programa en concreto DARDOS:

La primera fase, no nos llevó mucho tiempo, ya que nuestra política para la elección de ideas, como ya os he dicho, es realizar versiones propias de juegos ya conocidos, ahorrándonos así trabajo, tiempo y muchas neuronas.

En la segunda parte sí nos entretuvimos más, ya que uno de los hechos más importante para esta etapa fue decidir cómo podíamos detectar dónde el usuario 'clavaba' sus dardos. El nombre que reciben estas rutinas que se encargan de detectar los eventos del programa se conocen como algoritmos de colisión, existen multitud de ellos, tantos creo como programadores existen y ya dedicaremos algún artículo a ver algunas posibilidades. Por ahora como no queríamos utilizar alguna técnica complicada, empleamos un método bastante sencillo, éste es, cuando el usuario pulsa el botón de disparo, se presenta el sprite cruz en pantalla, leemos en las coordenadas actuales del ratón el color que hay en la pantalla y según este valor sabemos dónde impactó el dardo , para conseguir ésto al dibujar hay que seguir unas determinadas reglas (nota para el grafista) ya que las zonas de la diana con la misma puntuación deben llevar los mismos colores, (SI, todo amarillo pero que todos esos colores sean el color 57 por ejemplo, ya que podíamos tener un amarillo número de color 34, otro el 128, etc..). Una vez detectada la posición sólo nos quedaba aplicar las reglas del juego, pero en esta etapa no programamos nada, sólamente escogimos las estructuras de datos, lo demás sabíamos que es poner unas instrucciones u otras.

La tercera fase cómo ya os he comentado, no tiene por qué ser una tarea ardua y si no mirad, os voy a presentar todos los procedimientos que se emplean en este juego y os marcaré aquellos que son específicos para este programa el resto, formarán parte de nuestra ya nutrida librería:

            Speaker                Modo13h               Paleta
        ----------------      ------------------     --------------
            Demonio               PonPunto            CambiarPaletaA
        ActualizarSonido          CogePunto              FundidoA
        InstalarSonido             Modo13h               FundidoD
        DesinstalarSonido         ModoTexto           PonPaletaNegra

         Juego-Caracteres       Formatos Gráficos         Ratón
        -----------------      -------------------   ---------------
           DamePuntero             LeePCX             InicializaRaton
            VCadena                                      FinRaton
                                                      LimitaPantalla
        Propios de Dardos  (* Nuevos *)                MuestraRaton
        -------------------------------------          OcultaRaton
          Temblor            Disparo                   EstadoActual
          Cruz               PulsoSalida              PosicionaCursor
          Reglas             Final
          VisualizaPuntos    Creditos
          BorraCruz          FinalJuego
          Inicializa      Programa Principal

Como veréis un 80 por cien del código ya estaba hecho. De todos los procedimientos nuevos, nada hay que decir ya que es pura programación y no se utilizan en ellos técnicas especiales. Os presento el procedimiento Disparo que podemos considerar como núcleo del programa, para que veáis que no hay nada raro y para que notéis lo exhaustivamente comentado que viene el programa, como todos. :)

(*Procedimiento que comprueba si se pulsó el botón 1 Lanzar el dardo*)
procedure Disparo;
var St1:string;
begin
 if Boton1 then                     (*¿ Se pulsó botón izquierdo ?*)
     begin
      EscondeRaton;    (*Tenemos que ocultar el ratón antes de visualizar*)
                       (* nada, ya que el ratón ya salva automáticamente el *)
                       (* fondo antes de visualizarse y produciría un error*)
      Cruz;            (*Visualiza la cruz recoge el color*)
      Reglas;          (*Aplica las reglas del juego*)
      VisualizaPuntos; (*Visualiza la puntuación*)
      delay(500);      (*Retardo para el sonido*)
      nosound;         (*Para la música*)
      Borracruz;       (*Recupera el fondo debajo de la cruz*)
      MuestraRaton;    (*Muestra el ratón de nuevo*)
      dardos:=dardos-1;(*Un dardo menos en esta tirada*)
      if dardos=0 then                   (*¿Cambio de turno?*)
       begin
        jugador:=(jugador mod 2)+1;        (*SI*)
        dardos:=NumDardos;
       end;
      str(jugador,st1);                  (*Visualiza el turno y el número de*)
      Vcadena(98,187,st1,97,22);         (*de dardos*)
      str(dardos,st1);
      Vcadena(203,187,st1,97,22);
     end;
end;

En los otros apartados de esta fase como veréis no me compliqué la vida, la diana la dibuje a la virulé y el sonido Beep Beep. :)

La cuarta fase en la que podemos considerar que Dardos se encuentra actualmente y ahí se quedará, Dardos es simplemente un programa de prueba y demostración.

Ya os dije al comienzo del artículo que leyendo ésto no ibais a programar un juego de la noche a la mañana, pero me gustaría que cogierais una idea importantísima, el programar un videojuego es un 80 por cien pensar una buena idea y realizar unos buenos gráficos y sonidos, el codificar no tiene ningún misterio, o bien os lo curráis una vez para vivir de las rentas u os hacéis con unas buenas rutinas, las nuestras por ejemplo, sin dar ni palo. En próximas lecciones, seguiré machacando esta idea y de momento ir perdiendo el miedo. :)

Bueno a qué esperáis, por qué no os vais... ¿qué?, ¿qué decís?.. ah! que dónde están los fuentes, ¡leshe!, se me había olvidado, venga tomad y que aproveche. :)

Pulse F2 para ejecutar el programa DARDOS.  - Posibilidad no disponible en la versión HTML-
F5 para consultar los fuentes.
 


2.11.- Eso es to, eso es todo amigos.

Nos Vemos

Bueno, se acabó esta lección, antes de nada me gustaría recordaros algo que os dije en la primera lección.

-----------------------------------------------------------------------------

Para conocer los códigos scan y Ascii de cualquier tecla, ejecuta alguna de las demos TESTMFII y PLANTECL que incluimos con esta lección. Nosotros os mostramos los códigos make, para conocer los códigos release sólo tenéis que sumar 128 como es lógico ;)

Pulse F2 para ejecutar el programa PLANTECL. - Posibilidad no disponible en la versión HTML-

Me imagino que os habrá interesado este programa, en este capítulo no os proporcionamos los fuentes porque todavía no hemos visto la presentación de sprites en pantalla, pero en el próximo número os entregaremos los fuentes de PLANTECL. :)

------------------------------------------------------------------------------

Pues na' como lo prometido es deuda aquí tenéis los fuentes de este programa:

Pulse F2 para ejecutar el programa PLANTECL. - Posibilidad no disponible en la versión HTML-

Me gustaría destacar que Plantecl no lo consideramos parte del curso y por ello no lo he comentado exhaustivamente, sólo le he limpiado la cara un poco. Este programa se creó como una aplicación para obtener los códigos scan y Ascii de una manera más curiosa que con el programa TestmfII y no pretendemos con él, explicar ningún tipo de técnica especial.

Después de este detalle, os voy a pedir un pequeño favor, si no es mucha molestia, si no os importa, podríais, si queréis, por favor, sin problemas, sin compromiso y si os sobra tiempo rellenar el siguiente cuestionario y hacérnoslo llegar de alguna manera, aceptamos correo electrónico, correo terrestre, fichero de texto, telegrama, faxes, mensajero, paloma mensajera, tam tam, señales de humo, folios en forma de avioncito de papel, servilletas hechas pelotitas y lanzadas con fuerza, comunicación a voces como las vecinas del patio... etc, etc, etc....

        1. ¿Qué te parece la idea de crear un CPV?
        2. ¿Qué opinas sobre el entorno del CPV?
        3. ¿Conoces algún curso de estas características, en español o inglés?
        4. EL contenido del temario ¿Te parece acertado?
        5. ¿Qué tema te gusta más de él ?
        6. ¿Qué echas en falta?
        7. Según están redactados los artículos ¿Te resulta fácil entender las explicaciones?
        8. ¿Te gustaría que además de artículos de la lección se incluyesen otro tipo de secciones?
        9. En caso afirmativo. ¿Cuáles te gustarían?
       10. Observaciones generales sobre el CPV
       11. ¿Qué preguntarías tú en este cuestionario para que la gente te haga más caso?

        Espero que se estrenen estas preguntas.

Ya hemos dicho varías veces lo 'pillaos' que andamos de tiempo, me imagino que la próxima lección saldrá para Marzo-Abril del 95, pero en gran medida el lanzamiento depende de las colaboraciones que recibamos. En la próxima lección tenemos previsto entre otras cosas ofrecer los siguientes artículos

        -Efectos especiales.
        -Ultima entrega sobre el modo 13h.
        -Formato gráfico GIF.
        -Sonido digital por el speaker.

Un ejemplo de lo que seréis capaces de hacer en la próxima lección es el siguiente programa, tarda unos segundos en empezar, tened paciencia:

Pulsa F2 para ejecutar el programa EFECTO1.  - Posibilidad no disponible en la versión HTML-
 

Quiero dar las GRACIAS a todas las personas que están colaborando con nosotros en el lanzamiento del CPV:

Gracias a Juán Antonio Ubeda Torres por permitirnos utilizar sus gráficos para el CPV.

Gracias a Cristobal Saraiba Bello por hacer de cartero para mi en  Sakery BBS.

Gracias a Sara Martín Miedes por hacer las correcciones ortográficas a dos niños malos que dominan varios lenguajes pero todos de programación. :)

Gracias a David Blanco, Juan Carlos, Javier Aranda, Javier Ruiz, Miguel Angel Cortes y al resto de personas que me están ayudando a distribuir el CPV mano a mano.

Gracias a la gente de Sakery BBS por los mensajes que me están  mandando.

                Sakery Fox BBS 91- 413 98 55

Gracias en general a todos aquellos que han expresado su opinión sobre el CPV y nos la han hecho llegar.

Durante este período espero que estudiéis todo lo explicado hasta ahora, para comenzar con buen pie la nueva etapa del CPV, nos vemos en la  próxima lección y mientras tanto no dejéis de comunicad y colaborad con nosotros.

        Un saludo para todos de parte de:

JASM-BMP


[ Anterior | Índice | Siguiente ]

La última versión de este texto se podrá encontrar en Internet, en la dirección:
www.nachocabanes.com