Convertido a HTML por Nacho Cabanes
Ahora nos hemos propuesto ir un poco más lejos y nuestro objetivo con el presente curso es proporcionar a los interesados unas pequeñas nociones para realizar videojuegos propios. Debido a los temas que trataremos en las distintas lecciones, lo explicado puede ser aplicado a otros campos, como la programación de demos, el tratamiento de imágenes, programas de sonido y en general, cualquier tema que necesite de una programación a bajo nivel de nuestro ordenador PC.
Desde que nos planteamos la idea hasta hoy, habrá pasado poco más de un mes, en ese tiempo ha habido que planificar el curso, programar el editor, redactar los artículos y realizar los programas, ha sido un gran esfuerzo pero por fin hemos lanzado la primera lección y creo que ha valido la pena. :)
El CPV será impartido utilizando el lenguaje Pascal con rutinas embebidas en ensamblador, también proporcionaremos algunos programas en C. Los requisitos mínimos para poder seguir el curso con tranquilidad, son tener unos conocimientos básicos de programación en algún lenguaje de alto nivel y sobre todo muchas ganas de aprender. ;) Pensamos que sabiendo programar medianamente no tendréis problemas para, entender las explicaciones y saber 'leer' los programas, ya que el lenguaje Pascal, es muy parecido al pseudo-código y las rutinas de ensamblador serán bastantes sencillitas.
Me gustaría en este momento enseñaros como funciona el visualizador VCPV, por lo tanto pulsar F1 y leer ;)) -Posibilidad no existente en la versión HTML-.
El ordenador necesario para poder seguir el CPV es como mínimo un 386 con tarjeta VGA y 2 Mb de RAM. Nosotros trataremos la programación de distintos periféricos y tarjetas como el ratón, joystick, tarjeta de sonido. Si no contáis con estos dispositivos no pasa nada, salvo que no podréis ejecutar los programas de demostración. -Estas restricciones no se aplican a la versión HTML-.
Para terminar con esta introducción y pasar a la
acción
os presentaré las distintas lecciones de las que se
compondrá
el cursillo. Como éstas, las estamos haciendo sobre la marcha,
podéis
opinar y hacer sugerencias sobre las mismas ya que no son definitivas:
Programación de sistemas Hardware Teclado I Teclado II Modo gráfico 13h 1/3 Teoría del sonido Pc-speaker Juego de caracteres VGA |
Sonido background Modo gráfico 13h 2/3 Paleta gráfica Formato gráfico PCX Ratón Juego del mes
|
Modo gráfico 13h 3/3 Efectos especiales Formato gráfico GIF Formato de sonido VOC Sonido digital por speaker Algoritmos de colisión Juego del mes |
Efectos sonoros SB Formato de sonido WAV Soundblaster Modo gráfico 4x13h 1/3 Scrolling Joystick Juego del mes |
Modo gráfico 4x13h 2/3 Formato de sonido CMF Música de fondo SB Colas Compactación de archivos Juego del mes |
Modo gráfico 4x13h 3/3 Puerto serie Puerto paralelo Juegos multiusuario Memoria expandida y extendida Juego del mes |
Como os habéis fijado, esta lección es la más teórica de todas pero es que no podía ser de otra manera al pretender empezar de 0. :) El resto de las lecciones serán mucho más prácticas y prueba de ello es el videojuego que pretendemos incluir en cada una de ellas. Estos videojuegos serán sencillitos pero no por ello menos interesantes. :)
Bueno con esto acaba la presentación del CPV, esperó que disfrutéis con esta lección y con el curso en general. :) Si queréis enteraros de como seguir el CPV, consulta ahora el apartado de condiciones y por el momento distribuid esta lección en todos los sitios que podáis ya que, cuanta más gente siga el CPV, más posibilidad habrá de que nuestro barco llegue a buen puerto. Gracias por adelantado. :)
Notas sobre la versión HTML.
La versión "normal" del CPV fue creada por JASM-BMP, y estaba diseñada para funcionar bajo MsDos. En su día tuvo una difusión bastante razonable gracias a las BBS y al hecho de que algunas revistas, como PCActual, incluyeron el curso en su CdRom de portada. Pero hoy en día, gracias a Internet podría alcanzar una difusión mucho mayor, y la gran mayoría de los conceptos siguen siendo aplicables, aunque se tiende cada vez más a la programación bajo Windows y los detalles concretos sobre programación bajo MsDos están quedando anticuados.
En cualquier caso, mi intención es que no "se pierda" el trabajo de JASM y BMP. Tengo una página Web dedicada a la programación, y recibo muchas consultas sobre cómo programar videojuegos. No es un tema sencillo, y yo suelo remitirles al CPV, pero hoy en día no es tan fácil de encontrar, y a mucha gente, especialmente a los que están empezando, el simple hecho de tener que descomprimir ficheros, y de encontrarse con algo basado en MsDos le impone un cierto respeto. De modo que el convertirlo a HTML espero que lo haga accesible a mucha más gente, y que toda esta gente pueda seguir encontrando en el curso conocimientos que les sean de utilidad.
Nacho Cabanes, 2001.
Si eres responsable de alguna BBS y te gustaría que está fuese la base de operaciones para 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í, no sabemos 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, aff aff aff, podéis dejarlos en dicha zona. :)
También podréis obtener el CPV en el servidor de FTP
del
nodo de INTERNET, asterix.fi.upm.es aunque no sabemos
exactamente
en que directorio estará situado, posiblemente será
/pub/facultad/alumnos/juegos
o alguno relacionado con programación, para aseguraros consultad
antes las NEWS de INTERNET.
** NECESARIO ** *** NECESARIO *** *** NECESARIO **** ** NECESARIO **
Bueno llega el momento de cubrirse las espaldas, sé que es un poco pesado pero, queremos que sepáis que:
Existen dos grandes áreas para el diseño de programas
de ordenador:
La primera, se encarga de cómo la información va a
ser tratada dentro de un sistema, en contraposición a la
programación
de gestión que se encarga más bien, de cómo se
representa
y se trata la información dentro de un programa.
Pongamos un ejemplo. A un programador de gestión cuando guarda un valor en una variable, sólo le interesa saber dónde guarda ese valor, en una variable independiente, en una lista, una tabla, un árbol..., sin embargo, a un programador de sistemas también le puede interesar saber cómo se guarda ese valor, en qué dirección de memoria, con qué formato...
Otra característica que diferencia ambas áreas, es que la programación de aplicaciones, por así decirlo, es universal y los métodos que se usan pueden ser utilizados para cualquier tipo de máquina, al contrario que la programación de sistemas, que depende de la máquina sobre la que se va a trabajar, y antes de embarcarse en cualquier proyecto es necesario conocer el hardware del que se dispone.
De la programación de gestión no os hablaré más, ya que está orientada a otros fines que no son los que nos interesan. Unicamente comentaros que aunque conozco algunas excepciones (en Clipper con rutinas en C y asm), la verdad sea dicha, los lenguajes de gestión no están hechos para los gráficos. Como ejemplo os diré que en RPG y COBOL, (dos lenguajes que estoy aprendiendo), no es que no se pueda trabajar con gráficos, noooo, ¡¡Es que no se puede hacer ni una dichosa línea!!. X-O
Bueno volvamos a lo nuestro que me exalto. ;) Que la programación de sistemas esté tan ligada al hardware, no significa que para programar tengamos que 'ponernos el mono' y empezar a trabajar con los puertos de las tarjetas, nosotros también contamos con ayuda para no tener que 'ensuciarnos las manos', pues existen varias interfases software, como la BIOS (Basic Input Output System) y el Sistema Operativo (MS-DOS en nuestro caso), que nos ayudarán a realizar nuestra tarea.
La forma con la que tenemos que ver un ordenador es la siguiente:
---------------------------
| Sistema Operativo
|<----
------------
| -------------------
|
| Programa |
| |
BIOS
|<-------- | de |
| | ----------- |
|
| usuario |
| | | Hardware <------------
------------
| | ----------- | |
| ------------------- |
|------------------------ |
Es decir, el corazón de todo ordenador es el hardware del que se compone, pero para gestionar todas las posibilidades de la máquina y protegerla de nuestra manazas, surgieron las interfaces que nombramos antes. Estas son como caparazones que envuelven el hardware y que van ofreciendo los servicios de niveles internos, a los niveles más externos, proporcionando más seguridad, aislando el tipo de hardware y facilitando el acceso, pero como todo no van a ser ventajas, esto lleva un gasto de tiempo que puede ser necesario evitar.
Al trabajar con un ordenador, (me refiero a programarlo), no es obligatorio pasar por todos los niveles para utilizar el HW, podemos evitar el uso de algún nivel y acceder directamente a los servicios de los niveles más internos. Para decidir cuál de los niveles utilizar, primero tendremos que pensar que objetivo buscamos: velocidad, optimización del tamaño del programa, seguridad, independencia del hardware. ¿Lo mejor? trabajar siempre en ensamblador y a nivel hardware, pero 'casi' siempre no vale la pena. ¿Os imagináis qué se esconde detrás de ese casi...? ;) pues sí, la programación de buenos videojuegos y, en general, aplicaciones que necesitan un máximo aprovechamiento de la velocidad del procesador.
Existe un factor llamado 'overhead', cuyo valor es indicativo del tiempo gastado en pasar entre dos niveles adyacentes de los ya comentados, que nos dirá si vale la pena romperse la 'head' bajando a las capas más internas; si ese factor es pequeño, no te compliques la vida y sé feliz, ;) utiliza los niveles más externos y sin problemas, pero si ese valor es muy grande (que lo suele ser), vamos a tener que calentarnos la cabeza, Oo·:( y aprovechar al máximo las posibilidades de la máquina. Muchas veces no es necesario tener en cuenta ese valor y basta con fijarse en otros aspectos del programa que queremos realizar, un ejemplo, imaginaros que vamos a hacer una utilidad que trabajará con diskettes, para qué vamos a utilizar programación de muy bajo nivel, para ahorrarnos unos -->segundos<--- de overhead, si el acceso a disco de por sí ya es leeeento. Otra cosa sería estar trabajando con aplicaciones de video, donde ahorrar tiempo puede significar permitirte, o no, hacer una animación de más de 10 imágenes por segundo.
Bueno, con ésto ya acaba... (¿me ha parecido
oír
algunos suspiros y gritos de alegría?. Seguro que no, sois
buenos
alumnos y no me habéis interrumpido ni una vez JE JE) :)) , este
capítulo dedicado a la programación de sistemas. Todo
esto
sería paja si no os mostrase algún ejemplo de verdad,
donde
podáis apreciar ese overhead del que os he hablado. En el
artículo
del modo 13h, tengo previsto poner un ejemplo de la DIFERENCIA de
utilizar
unos servicios u otros, además, y ésto no estaba
previsto,
hablaré un poco de los conceptos y herramientas necesarias para
trabajar en cada nivel en el artículo La cacharrería.
Toda esta memoria se administra en base a segmentos de 64 Kb, esto es debido a que el tipo de direccionamiento que escogieron para poder referenciar todas las posiciones de memoria fue el direccionamiento segmentado. Pero para ésto sí existe una explicación más lógica.
La tecnología de aquella época tan lejana :) solo permitía construir registros de 16 bits, (hoy en día me parece que vamos por 64 bits). Como la cantidad de memoria que puede ser direccionada depende directamente de la longitud de un registro, llamado registro de direcciones, con 16 bits se puede acceder a 2 a la 16=64 Kb. Para gobernar 1 Mb se necesitaba un registro de 20 bits, imposible para aquella época, de este modo, se optó por utilizar dos registros de 16 bits, a uno de ellos (registro de segmento) lo desplazamos cuatro posiciones a la izquierda (o se multiplica por 16) y le sumamos el otro registro (registro de desplazamiento) tal y como está, consiguiendo así nuestra dirección de 20 posiciones.
Como veis este tipo de direccionamiento es un poco lioso, y mucho más si lo comparamos con el direccionamiento lineal, donde se trata a la memoria como una 'línea' de direcciones todas consecutivas. Esta técnica se utiliza en otras arquitecturas, como la basada en los procesadores de la familia del 68000 de Motorola, (yo he trabajado con estos procesadores y creedme no hay ni punto de comparación en ese aspecto y en muchos otros), esos procesadores son los que se utilizan para programar los videojuegos de verdad, (sí, sí, los de las máquinas recreativas) y no me extraña nada. :)
Si tan mala es esa organización de la memoria, por qué seguimos utilizándola, si hoy en día disponemos de microprocesadores que direccionan hasta 4 Gb y registros de 64 bits, pues echarle la culpa a esas 4000 líneas de código que en su día fue el MS-DOS 1.0, el por qué del auge que tuvo este sistema operativo es un tema que supongo todos conoceréis, por lo que no voy a seguir con la clase de historia..... :)
Os muestro, para terminar con el apartado de la memoria, la organización en bloques de 64 Kb de la memoria del PC:
Función
Dirección
Bloques
de 64 KB
-------
---------
----------------
Memoria
principal
0000:0000 - 9000:FFFF 10
RAM video
(EGA/VGA)
A000:0000 -
A000:FFFF
1
RAM de
video
B000:0000 -
B000:FFFF
1
BIOS-ROM
adic.
C000:0000 -
C000:FFFF
1
Cartuchos-ROM
D000:0000 -
D000:FFFF
1
Cartuchos-ROM
E000:0000 -
E000:FFFF
1
BIOS-ROM
F000:0000 -
F000:FFFF
1
Existen cuatro tipos de
registro
según su función:
Cada registro de segmento tiene un objetivo diferente:
El comunicarse con los puertos es semejante a trabajar con la
memoria.
Para utilizarlos se hará uso de las instrucciones máquina
in y out. Los puertos para nosotros serán el más bajo
nivel
en el que trabajaremos, ya que leer o escribir datos en un puerto es
'hablar'
directamente con la máquina sin necesidad de 'traductores' como
la BIOS, pero ¡Ojo! hay que tener cuidado porque pueden saltar
chispas.
:)
Existen dos tipos de interrupciones, software y hardware:
Por lo tanto, quede claro que cada entrada de 4 bytes en la tabla es una dirección, formada por 2 bytes del segmento y 2 del desplazamiento. Este indireccionamiento permite a los fabricantes de PC , usar sus propias rutinas ROM-BIOS, teniendo únicamente que poner la dirección de éstas, en la correspondiente posición de la tabla . Las nuevas rutinas, tendrán que tener los mismos parámetros y devolver los mismos valores que las funciones originales, así los usuarios y aplicaciones no distinguirán, unas rutinas de otras. Pero lo que a nosotros nos interesa es que también podemos sustituir esas direcciones, por la ídem de una rutina que hayamos creado nosotros, controlando así distintos sucesos como a nosotros nos interese. :)
Bueno con la somera explicación del funcionamiento de la tabla de interrupciones doy por terminado este artículo sobre el hardware del PC. Para que veáis como funcionan todas estas herramientas en un lenguaje determinado, (PASCAL) y con un objetivo determinado, (control del teclado) consultar el artículo sobre el teclado donde se utilizan todos estos recursos de la máquina y se dan más detalles 'sobre la marcha'. :)Empecemos viendo qué sucede desde que pulsamos una tecla hasta que tenemos disponible su valor ASCII. Al pulsar una tecla, un chip de nuestro teclado el 8048, detectará un cambio de nivel eléctrico y con él, calculará la posición de la tecla. Dependiendo de la colocación de ésta, el circuito depositará un código en un búffer interno de teclado de tamaño 10, (por muy rápidos que seáis no lo conseguiréis bloquear) de donde es transmitido (en serie a través de ese cable de dos hilos enrollado que veis) al ordenador.
Me gustaría hacer notar que ese código del que os hablo no es un código ASCII, se trata de un Scan-code que depende de la posición de la tecla, y que a pesar de lo que intentan algunos fabricantes de Taiwan, |) más o menos también está estandarizado.
En el ordenador el código es recibido por otro chip, que suele ser el 8042, el cual genera una interrupción hardware IRQ1, (de las que hablamos en la cacharrería). La dirección de la rutina que atiende a esta interrupción está en la posición 9 de la tabla de interrupciones (también os sonará). Esa rutina leerá el código de la tecla, que se sitúa en el puerto 60h. Hasta aquí se puede decir que estamos en el nivel máquina y será donde más trabajaremos, ya que si interceptamos el valor de ese código scan, nos ahorraremos todo lo que viene detrás.
Una vez leído el código-scan del puerto, la rutina de la BIOS se encarga de calcular el código ASCII y depositarlo en un búffer de teclado que se encuentra en el primer segmento de memoria de la RAM, donde también se guarda la tabla de interrupciones y otras variables importantes de la BIOS, luego moraleja, no juguéis con este segmento. ;)
Me gustaría también destacar dos asuntos: muchos pensaréis, cómo debe ser, ;) ¿cómo la BIOS sabe que utilizamos un teclado con mi querida ñ ?, pues bien, no lo sabe, el ordenador se 'entera' debido a que el sistema operativo a través de su programa KEYB sustituye la rutina de la BIOS (made in HUSA) por otra que nosotros le indiquemos, SP en nuestro caso. El otro asunto que quería mencionaros es que, el búffer BIOS del que os he hablado, a pesar de tener una longitud de 16 caracteres, sí podéis llegar a bloquearlo y si no hacer una prueba, lo primero de todo ¡Saliros de mi programa VCPV! y una vez en el DOS poner las dos manos bien extendidas sobre todas las teclas que podáis, :)) Como habréis visto sonará un pitidito que indica que el búffer está lleno, esto que puede parecer una tontería es muy importante, ya que imaginaros que estáis haciendo una presentación que no se puede interrumpir y el típico usuario impaciente empieza a 'aporrear' el teclado para pasar de vuestra presentación, cuando acabe ésta una avalancha de pulsaciones caerán sobre vuestro programa y si por alguna casualidad se activa alguna opción no deseada, puede que os haga poca gracia. =8O
Bueno volvamos a lo nuestro, dónde estabamos, :-? a sí, en el búffer de teclado... una vez el código ASCII esta disponible en el búffer, se podrá leer utilizando las funciones de la BIOS correspondientes, a través de la interrupción software 16h, con lo cual nos situaríamos en el segundo nivel de nuestra organización particular. :) También podemos acceder por medio de las DOS-API, otra serie de funciones que el sistema operativo pone en mano de los usuarios, con lo cual nos situaríamos entonces en el tercer nivel, muy lejos de lo bueno. Algunos de vosotros pensaréis (¡ Otra vez! no os vayáis a calentar la cabeza, ;)), es broma ), y ¿cómo accedo siempre con el readkey de PASCAL, el getch() de C, el get de ADA, el input de BASIC, el get de DBase, el read de MODULA-2, el +i(1) de RPG o el accept de COBOL?, (bueno yo 'sólo' practico en estos lenguajes, vosotros aplicaros los vuestros ;) pues bien todas esta instrucciones son de LAN, (sí de lenguajes de alto nivel, no de Local Area Network), pero estas ordenes se apoyan, algunas mejor que otras, en el uso de las funciones que ya hemos citado, por lo que podéis añadir otro caparazón más a nuestra Tortuga particular :)
Me imagino que os habréis dado cuenta de que cuantas más capas quitemos al ordenador más rápido irán las cosas.... ¡NO! No quitéis la carcasa al ordenador, me refiero a las capas lógicas de las que estamos hablando. ;) Bueno, para empezar a ponernos manos a la obra os explicaré cómo utilizar las funciones de la BIOS, por si alguno las queréis utilizar, para después pasar a lo bueno, los Códigos-SCAN.
La BIOS ofrece distintas funciones, accesibles a través de interrupciones software que permiten el manejo de todos los dispositivos de que consta el ordenador. Para acceder a las funciones del teclado utilizamos la interrupción 16h (hexadecimal), pero ¿cómo se llama a una interrupción?, pues bien, en emsamblador se utiliza la instrucción 'int' seguida del número de la interrupción que queremos 'originar', en nuestro caso int 16h. En los distintos lenguajes de alto nivel, también existen instrucciones que nos permiten causar interrupciones:
Intr(NumeroInterrupción:Byte ; Registros:Registers); <---PASCAL
int int86(int, union REGS *regsent, union REGS *regssal); <---C
Pero antes de llamar a cualquier interrupción, es necesario colocar algunos valores en determinados registros; estos valores identificarán la función a la que queremos acceder y serán los parámetros que la función necesita para ejecutarse. Una vez finalizada la ejecución de la rutina llamada, ésta también nos podrá devolver valores en los mismos o en otros registros.
Veamos ahora un ejemplo completo:
mov ah,1 ;ponemos el
parámetro
necesario en su lugar
int 16h ;causamos
la interrupción
Como veis el trabajar con interrupciones es bastante sencillo, lo complicado en este caso es saber cuál es la función que necesitamos y cuáles son sus parámetros, pero para eso están los libros. Existen cantidad de libros y archivos de texto donde se detalla exhaustivamente todas las funciones de la BIOS, de todos modos si os falta información sobre las interrupciones BIOS poneros en contacto con nosotros y os facilitaremos la información que necesitéis... :) En los tutoriales, 'sólo' explicaremos las interrupciones y funciones que utilicemos, que no serán pocas.... ;)
Para empezar os explicaré las seis funciones básicas del teclado, si tenéis un 8086 o un DOS por debajo de la versión 3.3 sólo os funcionarán tres, sin embargo, el hecho de tener solo tres funciones debe ser lo que menos os preocupe (por cierto en mis ratos libres, que son pocos, ensamblo ordenadores, podéis consultarme ;)
Para acceder a cada función expuesta es necesario poner el
número
de función en el registro AH (ya sabéis, parte alta del
registro
AX), hecho que se puede considerar común para el resto de
funciones
e interrupciones.
La mejor forma de utilizar estas funciones es, primero consultar con la función 1, si hay tecla en el búffer y después retirarla con la función 0.
BIT 0:Tecla derecha de mayúsculas BIT 4:Bloqueo de desplazamiento
BIT 1:Tecla izquierda de mayúsculas BIT 5:Bloqueo numérico
BIT 2:Tecla control BIT 6:Bloqueo de mayúsculas
BIT 3:Tecla alt BIT 7:InserciónEn todos los casos 1=activado, 0=desactivado.
Antes de explicaros el resto de las funciones, me detendré para destacar un asunto. Cómo os podéis dar cuenta, al consultar una tabla de códigos ASCII, no figuran ni las teclas de función, ni los cursores, ni distintas combinaciones de teclas. Esto se debe a que con un byte, (tamaño de un código ASCII) sólo se pueden codificar 256 valores diferentes, y como existen tantos caracteres raros que codificar, _++iÊ, se optó por crear los códigos ASCII extendidos, éstos se forman con 2 bytes, el primero con valor 0 y el segundo el valor ASCII en sí. Así que ya sabéis, si leéis un carácter de valor 0 volver a leer ya que se trata de un código extendido, para conocer los valores tanto ASCII como SCAN de vuestras teclas, en el segundo artículo sobre el teclado, os proporcionaré un programa para que los conozcáis.
Aún con estos códigos extendidos, no se logró cubrir con códigos ASCII todas las teclas del teclado, F11, F12, cursores grises... así que se añadieron nuevas funciones, la 10h y 11h, que funcionan igual que las 0 y 1 respectivamente, y la 12h que devuelve en AL el byte de estado ya descrito además de este nuevo byte en el registro AH
BIT 0:Tecla control BIT 4:Tecla Inter
BIT 1:Tecla alt BIT 5:Bloqueo numérico
BIT 2:Tecla Petición de sistema BIT 6:Bloqueo mayúsculas
BIT 3:Modo pausa BIT 7:Inserción
Pulse F2 para ejecutar el programa TESTMFII - Posibilidad no disponible en la versión HTML-.
Bueno para terminar con las funciones BIOS del teclado, os mostraré ahora una nueva función proporcionada por la interrupción 16h, ésta es la 03h, su objetivo es ajustar el factor de repetición del teclado.
El factor de repetición del teclado determina cuántas veces se provocará una pulsación mientras una tecla permanece apretada. La función 03h os puede ser de utilidad si finalmente os decidís por utilizar las funciones BIOS en vez de los códigos Scan para realizar vuestros videojuegos. Si éste es vuestro caso, convendría que cambiaseis el factor de repetición para leer más veces una tecla mientras está pulsada.
Los parámetros que hay que pasarle a la función son:
AH=03h
;número de función.
AL=05h
BH=Retardo
;Retardo que transcurre hasta que se empieza
a repetir la pulsación de la tecla.
BL=Factor de repetición
;Número de veces que se produce la pulsación
por seg.
En BH y BL no podéis poner cualquier valor, os tendréis que adaptar a una serie de valores determinados que son los siguientes:
Para el
retardo:
%C: BH
Retardo
(segundos)
---- -------------
0 0.25
1 0.5
2 0.75
3 1 %C0
Para el factor de rep:
BL Rep.
por
seg. BL Rep. por
seg.
BL Rep. por seg.
----
---------------
---- -------------- ----
---------------
0
30
11
10,9
22 4,3
1
26,7
12
10,0
23
4,0
2
24,0
13
9,2
24
3,7
3
21,8
14
8,6
25
3,3
4
20,0
15
8,0
26
3,0
5
18,5
16
7,5
27
2,7
6
17,1
17
6,7
28
2,5
7
16,0
18
6,0
29
2,3
8
15,0
19
5,5
30
2,1
9
13,3
20
5,0
31
2,0
10
12,0
21
4,6
Para terminar con este apartadillo os he proporcionado un programa como demostración para comprobar el funcionamiento de la función 03h. :)
Pulsa F2 para ejecutar el programa TESTFACT - Posibilidad no disponible en la versión HTML-.
Scan-code
Por así decirlo, ahora viene lo bueno, los scan-codes o códigos scan. Cuando os expliqué el funcionamiento del teclado, ya hablamos de estos códigos, pero de todos modos os refrescaré la memoria. ;) Cuando se producía una pulsación en el teclado, se generaba una interrupción hardware IRQ1 (9 en la tabla de vectores de interrupción), y el código scan de la tecla se situaba en el puerto 60h. Sabiendo esto podemos crearnos nuestra propia rutina que atienda a esa interrupción y así, leyendo del puerto 60h, detectaremos la tecla pulsada mucho antes que si utilizásemos las funciones BIOS y los códigos ASCII. Además de este ahorro de tiempo, el trabajar con códigos scan tiene otra ventaja importantííííísima....
Como os habréis dado cuenta, con los programas de demostración, al trabajar con la BIOS, no podemos detectar la pulsación de dos teclas a la vez. Con los códigos scan sí es posible; esto se debe a que el teclado genera un código cada vez que se pulsa una tecla (código make) y también cada vez que se libera la tecla (código release), con ello al pulsar una tecla recibiríamos el código make de la misma y hasta que no recibamos el código release, sabemos que esa tecla permanece pulsada, durante este tiempo, pueden haberse pulsado otras teclas y así recibiríamos los códigos make de éstas, pero si no hemos recibido los códigos release sabemos que todas estas teclas ¡¡¡están pulsadas a la vez!!!. :)
Esta característica también permite detectar cuánto tiempo permanece pulsada una tecla (detectamos el código make, ponemos en marcha un contador y lo paramos cuando recibamos el código release).
Ya dijimos que el código scan se representa con un byte, los 7 bits de menor peso, 0..6, indican que tecla se pulsó, con estos bits se podrían codificar 128 teclas diferentes, que sería el máximo número de teclas que puede tener el teclado, pero utilizando una técnica que explicaremos a continuación, se puede aumentar este número. El bit de mayor peso 7, indica si se trata de un código make (Bit 7=0) o release (Bit 7=1), luego si el código es mayor de 128 es un código release y si es menor será un código make. Pongamos un ejemplo:
Tecla Espaciadora:
Código scan 57 <- binario con 7 bits 0111001
Código make 57 <- binario con 8 bits 00111001
Código release 185 <- binario con 8 bits 10111001
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. :)
Volvamos con los códigos scan. Como todo no iba a ser tan sencillo, en los códigos scan también existen unos códigos extendidos, en ellos antes de enviar el código de la tecla, tanto make como release, se envía el byte E0h o 224. Los códigos scan extendidos se corresponden a las teclas grises del bloque de los cursores, y salvo excepciones lo mejor es ignorarlos ya que la función de estas teclas se puede conseguir con las teclas del teclado numérico.
Ahora os vamos a explicar cómo sustituir el controlador de teclado original y qué puede hacer el nuestro. Para sustituir la rutina por defecto, por la nuestra, únicamente tenemos que sustituir en la tabla de vectores de interrupciones, la dirección de la rutina original por la nueva, para realizar esto en el DOS-API (servicios proporcionados por el S.O. DOS a través de la interrupción 21h) existen dos funciones la 25h y la 35h que fijan y leen respectivamente un valor en la tabla de vectores. Os proporciono los parámetros para ambas funciones:
int 21h Función 25h
Entrada:
ah
= 25h
al = número de interrupción
ds:dx= puntero FAR (segmento:desplazamiento) de la nueva
rutina de interrupciones.
Salida:
Ninguno
int 21h Función 35h
Entrada:
ah
= 35h
al = número de interrupción.
Salida:
es:bx= puntero FAR de la rutina actual.
En diferentes LAN existen también una serie de instrucciones que realizan la misma función y como es lógico son más fáciles de utilizar:
En Pascal:
GetIntVec(número
de interrupción,dirección de la rutina actual);
SetIntVec(número
de interrupción,dirección de la nueva rutina);
En C:
dirección
de la rutina actual=GetVect(número de interrupción);
SetIntVec(número
de interrupción, dirección de la nueva rutina);
Lo primero que tenemos que hacer al sustituir el controlador es, guardar la dirección de la rutina actual para posteriormente poder recuperarla y dejar la tabla de vectores con sus valores iniciales, ya que de otra forma el sistema podría colgarse Hupsss! :O Lo siguiente es situar en esa posición de la tabla la dirección de nuestra rutina.
La rutina que nosotros empleamos se basa en la utilización de
un array de 128 posiciones booleanas. Cuando leemos un código
make
del puerto 60h, ponemos a 'true' la variable de la posición de
la
tabla correspondiente al código scan de la tecla y cuando leemos
el código release ponemos a 'false' la variable, restando antes
128 al código recibido. Tenéis que recordar que la
rutina,
al llamarse por cada IRQ1, (pulsación o liberación de una
tecla), actualiza automáticamente la tabla y así nosotros
en nuestro programa para ver el estado de una tecla únicamente
tendremos
que preguntar por su valor scan.
Por ejemplo:
%C:
if Letras[57]
then
(*¿Barra espaciadora pulsada?*)
*******;%C0
Para detectar la pulsación de varias teclas a la vez, solamente tendríamos que preguntar por los valores de esas teclas en nuestra tabla de variables booleanas:
if letras[59] and letras[60] and letras[61]
then
(* ¿A, B y C pulsadas ?*)
Pulse F2 para ejecutar el programa TESTSCAN - Posibilidad no disponible en la versión HTML-.
En el mercado existen varios tipos de tarjetas de vídeo: HGC, MDA, CGA, EGA, VGA y Super-VGA. Las cuatro primeras no las vamos a estudiar porque ya están obsoletas y la última porque es demasiado complicada para nuestros propósitos. Respecto a por qué hemos escogido el modo 13h de la VGA para realizar los programas y no otros modos con más resolución como el 0Eh (640x200), el 10h (640x350) o el 12h (640x480), pues sencillo, por el número de colores, estos tres modos que hemos nombrado, tienen un máximo de 16 colores simultáneos en pantalla y como ya hemos dicho, el modo 13h cuenta con 256 colores, y preferimos tener más colores en pantalla a consta de perder resolución y que los gráficos queden un poco 'bastos'. Además otra ventaja adicional de este modo es la organización de su memoria de vídeo, que es muy sencilla como veremos más adelante. :)
Algunos lenguajes LAN como C y Pascal suministran unos controladores e instrucciones especiales para trabajar en modo gráfico, pero nosotros en la medida de lo posible evitaremos el uso de estos servicios, ya que además de tener que cargar con los controladores gráficos (dígase BGI o como sea) suelen ser bastante lentos. Nosotros nos haremos las rutinas para todo aquello que necesitemos, estas rutinas, utilizarán instrucciones LAN para el manejo de la memoria, como MEM, ABSOLUTE y MOVE. Cuando el factor tiempo sea muy importante, utilizaremos rutinas en ensamblador para acelerar los procesos. :)
Empecemos con este modo de vídeo tan supersticioso. ;) Lo primero que hay que hacer al trabajar en modo gráfico es inicializar el modo de vídeo, para ello utilizaremos una función proporcionada por la interrupción BIOS de vídeo 10h, esta función es la 00h y sus parámetros son los siguientes:
Int 10h
ah= 00h
al= modo de vídeo a inicilizar
En nuestro caso la rutina en Asm sería:
mov ah,00
mov al,13h
int 10h
Cuando estudiamos la organización de la memoria de la familia 80x86, ya vimos que las tarjetas EGA/VGA disponían de un segmento de 64 kb para representar su imagen (A000), esta zona forma parte de la VRAM (Vídeo RAM) y es la zona de memoria de donde la tarjeta gráfica lee los datos para convertirlos en imágenes, es decir, cuando nosotros queremos visualizar algo en la pantalla no tratamos directamente con el monitor, si no que trabajamos con una zona de memoria y a partir de ahí, la VGA se encarga automáticamente de 'dibujar' las imágenes. :)
Hemos dicho que esa zona de memoria es de 64 Kb entonces, ¿Cómo se explica que algunas tarjetas VGA consten de una memoria de 256 kb ?, pues bien, ante la imposibilidad de trabajar con ese estrecho margen de 64 kb, los fabricantes de tarjetas se decidieron por utilizar los que se llaman Bit-planes de 64 kb cada uno (256 kb en total), que se 'esconden' detrás de esos 64 kb visibles y direccionables, aumentando la memoria disponible pero complicando mucho la electrónica del sistema de vídeo, en algunos modos de vídeo (los de 16 colores) se hace imprescindible trabajar directamente con los bit-planes, pero en este modo no es así... :)
Como ya dijimos al principio, una de las ventajas de trabajar en el modo 13h es la organización de la memoria de vídeo. En este modo cada punto en pantalla se representa por un byte que indica el color de ese punto, por lo tanto con 8 bits se pueden codificar 256 valores diferentes, de ahí viene el número de colores de este modo de vídeo. La resolución gráfica de este modo es de 320x200, cada línea de pantalla se compone de 320 puntos o bytes y en total habrá 200 líneas, todas estas líneas se suceden unas a otras de modo lineal a través de ese segmento de 64 kb situado en la dirección A000, ocupando un total de 64000 bytes. Sabiendo esto, si nosotros nos definimos una tabla de 64000 bytes y la situamos en esa zona de memoria con la claúsula Pascal absolute...:
Pantalla:Array[1..64000] of byte Absolute $A000:0000;
tendremos una variable definida justo 'encima' de la memoria de vídeo y cualquier cambio que hagamos sobre dicha variable se reflejará inmediatamente en pantalla. Para escribir un punto en pantalla de color C, en las coordenadas X,Y únicamente tendremos que calcular la posición en dicha tabla donde escribir el color. Debido a la disposición lineal que dijimos antes, esta será y*320+x, es decir, el número de línea por 320 más el número de columna, en Pascal nos quedaría:
Pantalla[Y*320+X]:=C (*Visualiza un punto de color C en coordenadas X,Y*)
En C no podemos poner cualquier valor, en la tarjeta VGA todos los colores (256) que se pueden representar en pantalla en un momento determinado, se guardan en una estructura conocida como tabla de colores o paleta, pero en este momento no vamos a explicar su funcionamiento, eso será en la lección 2.
El método anterior para representar un punto utiliza la técnica de acceso directo a memoria (no confundir con la DMA), para lograr sus propósitos podríamos haber tenido el mismo resultado si hubiésemos utilizado la instrucción MEM, que se utiliza para referenciar una posición de memoria.
MEM[$A000:Y*320+X]:=C (*Visualiza un punto de color C en coordenadas X,Y*)
Existen otros métodos para representar puntos en pantalla. La interrupción de vídeo BIOS 10h, además de la función 00h ya explicada, proporciona otras muchas funciones para trabajar en modo gráfico, una de ellas es la función 0Ch, que se encarga de poner un punto en pantalla. Para ver los parámetros de la función consulta los fuentes del programa Puntos.
Cuando estudiamos la división en niveles de nuestro ordenador, vimos que había un factor llamado overhead, que indicaba el tiempo que se tardaba en pasar de un nivel a otro, pues bien , en el programa de demostración apreciaréis ese factor, y os daréis cuenta de que el utilizar las funciones BIOS para dibujar es bastante más lento que el utilizar la técnica de acceso directo a memoria. :) Luego moraleja, no utilicéis las funciones BIOS al trabajar con gráficos, cuando tengáis prisa. Su uso lo restringiremos a tareas, como inicializar el modo de vídeo y tal vez, para trabajar con la paleta.
Podéis ejecutar ahora el programa PUNTOS que demuestra y compara las dos técnicas que he explicado para representar puntos en pantalla. Para los que no hayáis trabajado en modo gráfico, lo podéis considerar el 'Hola mundo' de la programación de videojuegos. ;)
Pulse F2 para ejecutar el programa PUNTOS - Posibilidad no disponible en la versión HTML-.
Como veis es muy sencillo poner un punto en pantalla. Algunos diréis, - pues vaya un punto y que hago yo con eso- , pues como supongo que sabréis ;) os diré que una línea se compone de puntos, un círculo también y lo más importante un sprite también. ;)
Nosotros no nos vamos a parar a explicar cómo sacar una línea por pantalla, porque aunque os parezca mentira, no se suelen utilizar en la programación de videojuegos, salvo en los llamados juegos vectoriales. En vez de eso para que veáis que un punto es importante, lo que vamos a hacer es sacar una imagen por pantalla. :)
Una imagen no es más que muchos puntos de colores que están dispuestos de tal manera que forman algo que nosotros reconocemos. El problema al representar una imagen, es que ésta, no suele venir en el formato con que se organiza la memoria (formato crudo), es decir, 200 líneas de 320 puntos cada una. Las imágenes vienen en unos determinados formatos gráficos, que suelen utilizar técnicas de compresión para ahorrar espacio de almacenamiento, (digase PCX algoritmo RLE, GIF algoritmo LZW...) así que antes de representar la imagen hay que convertirla al formato que utiliza la memoria.
El formato gráfico PCX lo veremos en el capítulo 2 y los ficheros GIF en el capítulo 3. Ahora os explicaré cómo conseguir una imagen en formato crudo (o RAW, o CAP, o PIC o como queráis llamarlo), para poder representar la imagen en pantalla moviéndola directamente a la memoria de vídeo. :)
Para obtener las imágenes crudas, nosotros utilizamos el programa de dibujo Animator 2d de Autodesk , (versión básica no la profesional). En este programa la última imagen que se ha visualizado se almacena en el directorio AAT con el nombre AATEMP2.PIC, este fichero es casi la imagen en crudo. Os explicaré de qué se compone este fichero...
El fichero AATEMP2.PIC tiene que tener un tamaño de 64800 bytes, si no es así, tenéis otra versión de Animator que no es la mía y no creo que lo siguiente se cumpla para vosotros. Los primeros 32 bytes del fichero son una cabecera que pone Animator a la imagen, los siguientes 768 son la paleta del dibujo, que como ya os he dicho no os voy a explicar ahora, ;) el resto 64000 son la imagen en crudo, si os fijáis, su tamaño coincide con el área visible del segmento de vídeo 320x200 = 64000.
Como supongo que sabréis y si no para eso estoy yo, ;) un segmento tiene una longitud de 65536 bytes, pero los últimos 1536 bytes del mismo no se representan en pantalla, luego 64000 son justo lo que se 'lleva' la VGA al monitor.
Si no tenéis la herramienta Animator, no importa, en otros muchos programas de dibujo también se puede trabajar con las imágenes en crudo, sólo tenéis que saber como descomponer la imagen, pero sabiendo que el tamaño es de 64000 bytes y que ésta se suele almacenar al final, lo tenéis más fácil. De todos modos os he proporcionado un Aatemp2.pic, para que veáis cómo visualizar una imagen. Ejecuta y consulta cuando puedas, los fuentes del programa IMAGEN para ver cómo visualizar una ídem. ;)
Pulsa F2 para ejecutar el programa IMAGEN - Posibilidad no disponible en la versión HTML-.
Aprovecho para dar las gracias a Juan Antonio Ubeda Torres, nuestro grafista en Hormigator, por realizar esta pantalla, también quiero dedicar el programa a Cristobal Saraiba Bello por hacer de cartero para mi en Sakery BBS. ¿Alguien más quiere saludar? ..... No pues seguimos ;)
Cuando consultéis los fuentes os daréis cuenta de que la imagen, no la llevo a pantalla punto a punto sino que utilizo la instrucción LAN Move que mueve bloques de bytes. Como ya os dije, en la medida que no necesitemos mucha velocidad, utilizaremos instrucciones LAN, pero cuando no sea así, utilizaremos rutinas en ensamblador para, en este caso llevarnos punto a punto la imagen a la memoria de vídeo, que aunque pueda parecer un método más lento es un 'pelín' mas rápido. :)
Bueno con esto ya acaba el primer capítulo dedicado al modo 13h, en las próximas lecciones ya empezaremos a trabajar con los sprites y la paleta, para poder empezar a hacer animaciones, scrolling y diversos efectos gráficos, cuanto antes posible. A que estáis impacientes por leer estos temas, ;) pues yo estoy impaciente por explicároslos. Un saludo. :)
El sonido en el ordenador es uno de los componentes fundamentales
en cualquier programa que se precie, no ya a nivel de video-juego, sino
incluso de aplicaciones que utilizan sonido para resaltar sucesos
relevantes
como errores, finalización de procesos, etc. En este primer
apartado
trataremos un poco la teoría del sonido (no os asustéis,
os aseguro que absolutamente todo será entendible y hasta os
resultará
divertido).
En esta primera parte, sólo tratamos de dar una somera idea
de
cómo funciona esto del 'sonido', no trataremos todavía la
programación de ningún dispositivo como el speaker o la
tarjeta
de sonido, esto lo haremos más adelante. Pero creo que unas
primeras
nociones de cómo se transmite el sonido, y de cómo se
genera,
son necesarias en un curso como este, en el que intentamos abarcar y
comprender
todo lo que sale de nuestro ordenador, así que manos a la obra.
Así pues hemos quedado en que
un
sonido es una simple
vibración del aire. Esta
vibración se representa
|
gráficamente mediante una
A
****
****
onda como la de la figura |
*
*
*
de la
derecha.
| *
*
*
|*
*
*
La gráfica dibujada con
|*
* *
asteriscos representa la
-+------------*----------T--------
perturbación mencionada con
|
* *
respecto al
tiempo
|
*
*
|
*
*
El tiempo marcado con la
|
* *
letra 'T' se le denomina
|
****
Período . Es un parámetro
|
de la onda (así nos
referiremos a partir de ahora al hablar de la perturbación).
Este parámetro lo
que nos indica es el tono del sonido que oímos: A un mayor
período el sonido
que oímos es mas grave, y por contra, con un menor
período,
el sonido será más
agudo.
En realidad no se suele hablar de período, sino de frecuencia que es la inversa del período (1/T) y se define como numero de ondas por segundo. Teniendo en cuenta lo que hemos dicho antes, un sonido será mas agudo cuanto mayor sea su frecuencia, y más grave cuanto menor.
Otro parámetro de la onda es el que hemos marcado en la
figura
como 'A'.Este parámetro se denomina amplitud y es la mayor
distancia
de la onda del eje horizontal. La importancia de este parámetro
estriba en que es el que determina el volumen del sonido. A mayor
amplitud,
mayor volumen, y viceversa.
1º) Las ondas son funciones continuas
del tiempo.
2º) Los valores que toman las ondas son
valores analógicos.
3º) Cómo reproducimos la onda
una vez que ya la tenemos almacenada.
'Las ondas son funciones continuas del tiempo': Esto quiere decir, en cristiano, que en cualquier intervalo de tiempo que tomemos, la onda puede tomar infinitos valores. Esto es un verdadero problema, pues la información ocupa espacio (ya sea en disco o en memoria) y nosotros no podemos guardar infinitos valores en nuestro PC. Es aquí donde intervienen las 'técnicas de muestreo'.
Muestrear una onda, es coger de ella el valor que toma cada cierto tiempo. Creo que lo mejor es que miréis la figura y así os enteraréis mejor:
|
|
|
71.3 70.0
|
50.0 *X******X****
60.2
30.4
| 22.1 ***X*****
| | **X***** 20.9
10.1 X*****
|
*X***
| |
|
| *X**** *****|
| **
|
| |
|
| | *
X
|
| *
|
| |
|
| |
*|
|
|*
|
| |
|
| |
|
|
-+------|------|------|------|------|------|------|-----|-------
|
Como vemos, hemos tomado de esta onda un total de 8 muestras, obteniendo 8 valores de la onda para el intervalo de tiempo. El objetivo de tomar estas muestras, es que a partir de ellas se pueda reconstruir la onda cuando la tengamos almacenada en nuestro disco duro o en memoria pero esto es otra historia que debe ser contada en otro lugar.
Es evidente que cuantas más muestras tomemos, cuando reproduzcamos el sonido a partir de éllas, más parecida será la onda a la inicial, pero también es evidente que mayor espacio ocuparán en nuestro disco.
Cuanto mayor numero de muestras cojamos por unidad de tiempo se dice que es mayor la 'frecuencia de muestreo'. Esta frecuencia está estandarizada en ciertos valores como son los siguientes:
. 8000 Hz
(8000
muestras por segundo): Que es suficiente cuando la onda que
digitalizamos
representa la voz humana.
. 11000 Hz,
22000 Hz, 44000 Hz
Estas frecuencias de muestreo no son aleatorias. Hay teoremas y desarrollos' que demuestran que estas frecuencias son las óptimas para sus objetivos, pero no nos vamos a extender más en este asunto.
En cuanto al espacio que ocuparían estas muestras en el disco, es un tema que trataré en el siguiente punto.
'Los valores que toman las ondas son valores analógicos': Como ya sabréis, nuestro querido ordenador, sólo es capaz de representar dígitos binarios (unos y ceros). Por contra, los valores que las ondas pueden tener son, no solo reales, sino infinitos. Por ejemplo un valor que una onda puede tomar es 25.237. Para solucionar esto, lo que hacemos es 'codificar' en binario estos valores.
Es evidente que con 8,16,32,... bits que podamos tener, según la tarjeta de sonido, solo podemos representar un numero finito de valores, con lo que habrá valores que no podamos representar, y habrá un cierto error. Pero también es cierto, que cuantos más bits tenga nuestra tarjeta, mayor numero de valores tendremos, y más exacta será la codificación de los valores de la onda.
Para aclarar todo esto que he escrito seguido, y que parece tan teórico vamos a poner un ejemplo. Imaginad que estamos digitalizando un sonido cualquiera como el ladrido de un perro. Ese sonido es una onda, que vamos a muestrear (tomar valores) a una frecuencia de muestreo de 22000 Hz (22000 muestras por segundo). Si estamos realizando el proceso con una Sound-Blaster (no es por hacer publicidad, es que es la marca que yo tengo) de 16 bits, esto quiere decir que cada muestra que tomamos es codificada con 16 bits. Si suponemos que el ladrido del perro dura 10 seg (ya se que es un ladrido un poco largo, pero es que es por redondear) y echando alguna cuenta:
1 seg=20000 muestras => 10 seg=200000 muestras
200000 muestras a 16 bits cada muestra son 3200000 bits, que, divididos entre 8 bits, que tiene un byte son... 400000 bytes!!!. Casi medio MegaByte para 10 segundos de ladrido de perro. Aquí es donde debemos tomar la decisión de si deseamos calidad o por el contrario poco espacio en disco. En este ejemplo si hubiéramos utilizado una tarjeta de 8 bits, es verdad que hubiéramos tenido una menor calidad en la digitalización, pero nuestro sonido hubiera tenido (200000*8)/8=200000 bytes, es decir, la mitad que en el caso anterior. Bueno, la decisión es vuestra.
'Cómo reproducimos la onda una vez que ya la tenemos
almacenada':
En realidad, esta es la parte mas fácil de todo el proceso, ya
que
se limita a poner el nivel de potencial que nos den las muestras en
cierto
dispositivo (dígase speaker, tarjeta de sonido, DAC conectado al
puerto paralelo, ...), y con esto tenemos el sonido reproducido en la
salida.
Posteriormente esta señal se amplifica para subir el volumen al
deseado, obteniendo el ladrido de perro, a nuestra madre gritando, o a
tu amigo al que has digitalizado para cambiarle la voz.
Superposición de sonidos:
La superposición de sonidos consiste en mezclar en una misma
onda dos o más sonidos y que se escuchen todos ellos a la vez.
Para
conseguir este efecto es necesario que ambos sonidos hayan sido
digitalizados
(muestreados) con una misma frecuencia de muestreo, y lo único
que
hay que hacer es sumar los valores de las muestras que correspondan al
mismo instante de tiempo. La onda resultante es pues la suma de las dos
anteriores, obteniéndose así la superposición de
los
dos sonidos.
Agregar eco:
Este efecto es muy sencillo de realizar, y consiste en sumarle a una
onda, ella misma desplazada en el tiempo unos milisegundos. Para ello,
a cada muestra se le suma una de un tiempo anterior. La mayor parte de
programas que realizan tratamiento de ondas tienen este efecto, e
incluso
algunos permiten definir a voluntad el tiempo de retardo, probar y
disfrutar
con ellos, conociendo ahora como funcionan.
Aumentar o disminuir el volumen:
Para realizar este efecto basta con multiplicar por un factor constante
todas las muestras del sonido digitalizado. El factor será una
constante
que, si es menor que uno, reducirá el volumen, y si es mayor que
uno, lo amplificará
Su importancia radica en el hecho de que es el único elemento de sonido que el ordenador incluye de serie, lo que quiere decir que todo aquel que tenga un PC, tendrá un PC-speaker.
En realidad este elemento del hardware es uno de los mas patateros como a continuación veréis. Esto es debido a que los ordenadores, en principio, no estaban destinados al tratamiento del sonido, y es por ello, que si queremos hacer algo que merezca la pena en cuanto a sonido, tendremos que utilizar alguna de las numerosas tarjetas que ahora existen el mercado. Pero bueno, vamos a lo nuestro, que si no nos desviamos del tema...
En primer lugar vamos a ver 'lo que es' el PC-speaker. El chisme en cuestión, no es más que un altavoz conectado mediante unos circuitos intermedios a la placa base de nuestro ordenador. La estructura va de la siguiente manera:
DIRECCION 61H ALTAVOZ
------------
/| /
| CIRCUITO | --/
| -
CPU ------> |
INTER. | ------> --\ | -
|
|
\| \
------------
Si recordáis en el artículo referente a la naturaleza del sonido, ya vimos que el sonido era en realidad una onda que se desplazaba por el medio en el que estuviéramos (en nuestro caso, el aire, salvo que haya alguien por ahí que esté buceando en la bañera), pues bien, nuestro amigo el Speaker es capaz de producir una serie de sonidos, pero el problema está en que al sólo tener dos posiciones (encendido y apagado) estos 'ruidos' serán siempre de la siguiente forma:
|
|
5v | ---- ---- ----
----
| --------
--------
| | | | |
| | | |
|
| | |
0v |--- ---- ---- ---- ----
|------- --------
-----------------------------
------------------------------
Sonido
Agudo
Sonido Grave
Este tipo de ondas se conocen como ondas cuadradas y se caracterizan por tener únicamente dos valores, éstos se corresponden con la situación del speaker, encendido (5v) o apagado (0v).
También mencioné en dicha lección que el sonido se produce debido a vibraciones, luego si nosotros nos limitásemos a encender una ver el speaker provocaríamos que la membrana del mismo se pusiese en posición extendida pero con eso sólo conseguiríamos un clic, el 'truco' esta en apagar y encender rápidamente esa membrana para producir la perturbación = sonido.
Del tiempo que mantengamos encendido y apagado el speaker dependerá la frecuencia del sonido generado. Como ya sabréis cuanto 'más juntas' estén las ondas mayor será su frecuencia. El jugar con la frecuencia será la única herramienta que dispongamos para generar sonidos ya que como os habreis dado cuenta no podemos controlar la amplitud (volumen) de la onda.
Pasemos a programar el Pc-speaker las operaciones que podemos hacer con este dispositivo son bien pocas. Para controlarlo únicamente disponemos de un puerto, el 61h, que pertenece a ese circuito intermedio mencionado antes, que en el caso de los AT es el 8042 también dedicado a controlar el teclado, esta multifunción se ve plasmada en el propio puerto ya que para manejar el speaker solo podremos utilizar los dos bits de menor peso, el resto se utilizan para otras tareas y si nos ponemos a juguetear con ellos con toda seguridad la cagaremos y Murphy volverá a tener razón. :)
El formato del puerto para nosotros será:
Bit 7 6 5 4 3 2 1
0
El bit 0 se utiliza para indicar quién
? ? ? ? ? ? X 0 controla el teclado. Si está a
0 lo controlará
el usuario, si está a 1 lo controlará el tímer
2 que veremos más adelante.
Si bit 0 =
0
Bit 1 = 0 Speaker apagado
Bit 1 = 1 Speaker encendido
Sabiendo esto os muestro, por fin, una rutina que genera un sonido:
:
var i:integer;
valor:byte;
.....
Valor:=port[$61];
(*Leemos primero el valor del puerto para*)
(* dejar los 6 bits que no utilicemos con *)
(* con los mismos valores*)
Valor:=Valor and
$FC;
(*Ponemos a 0 los dos bit que utilizamos*)
(*Esto hace que nosotros controlemos el*)
(*speaker y que permanezca apagado*)
for i:=1 to 100
do
(*Duración del sonido*)
begin
port[$61]:=Valor
or 2; (*Ponemos el Bit 1 a 1 luego encendemos el *)
delay(5);
(* speaker. Durante 5 milisegundos*)
port[$61]:=Valor;
(*Apagamos el speaker*)
delay(5);
(*Durante 5 mseg. también*)
end;
Veamos que hemos hecho con todo esto;
On | -----
-----
----- -----
| |
|
|
| |
| |
|
|
|
|
<-- 100 veces
|
-----
----- .......... -----
Off |-----------------------------------------------
<-5-><-5->
tiempo en mseg.
<---10--->
Como veis hemos generado 100 veces una onda de período 10 mseg, luego por ahora ya sabemos que sea lo que sea esto dura 1 segundo, además, según lo que os conté en el primer capítulo la frecuencia es la inversa del período por lo tanto f= 1/10 mseg = 100 Hz. ¡¡Yupi Yei!! hemos generado un sonido de 100 Hz durante un segundo, como veis es sencillo meter ruido. Al final del capítulo os proporcionaré una tabla con la equivalencia entre las frecuencias y las notas musicales para que practiquéis vuestro solfeo. ;))
Si después de este rollo os digo que esto no vale para nada me diréis de todo, hasta ya huelo mal... ;) Lo siento pero es la cruda realidad, si utilizásemos este método para meter bulla, tendríamos dos problemas, primero un delay no es un retardo constante, al producirse interrupciones en el ordenador, se alargaría su duración y se distorsionaría el sonido, además, las instrucciones tipo delay son las más odiadas por los programadores de video-juegos ya que mientras ellas se ejecutan, no se puede hacer nada más. Para evitar estos problemas se opta por otra técnica, cuya base es hacer que el ordenador se encargue de generar automáticamente la onda cuadrada con la frecuencia que nosotros le indiquemos.
Para ello utilizamos una cucaracha llamada 8253 o PIT, más conocida en la jerga como tímer. Un tímer consta básicamente de un contador, que una vez cargado con un valor inicial, se va decrementando hasta el valor 0 y en ese momento el tímer genera una señal, todo este proceso se realizará con independencia de la C.P.U. luego ya os imagináis su utilidad. El PIT contiene 3 timers independientes, cada uno con una misión especifica:
-Tímer 0. Genera
una
señal de reloj 18,2 veces por segundo, se utiliza para
actualizar
el reloj del sistema.
-Tímer 1. Se encarga
de refrescar la DRAM. No podemos utilizarlo.
-Tímer 2. Puede ser
conectado al speaker. Es nuestro objetivo.
Estos timers tienen varios modos de funcionamiento. Por ahora sólo os explicaré él que vamos a utilizar, en la próxima lección estudiaremos el PIT con más detalle. El modo en cuestión es el 3 - generador de onda cuadrada, en esta configuración el tímer, pondrá su salida a estado alto durante la mitad de la cuenta y cambiará a estado bajo durante la otra mitad, una vez el contador llegue a 0 se repetirá el proceso. El valor inicial de ese contador definirá por tanto la frecuencia de la onda cuadrada generada, la formula para calcular estos valores es la siguiente:
1193180 -> 1234DDh ->frecuencia base del PIT en Hz.
frecuencia= -------
contador
Sólo hay que sustituir valores y utilizar la calculadora de las magdalenas.
Los puertos del PIT que nos interesan para generar sonidos son:
43h Puerto de control. Se encarga de identificar el tímer y el modo de funcionamiento, que deseamos. Para programar el tímer 2 con el modo 3 tendremos que poner en este puerto el valor B6h. Por ahora creeros todo lo que os digamos, en la próxima lección vendrán las demostraciones. ;)
42h Se encarga de recibir el valor inicial para el contador, como este valor es de tipo word y el puerto maneja bytes, necesitamos hacer la carga en dos pasos, primero indicaremos el byte bajo (menos significativo) y después el byte alto. Una vez introducido el segundo byte comenzará la cuenta automáticamente. En este instante tendremos al tímer decrementándose y generando la onda cuadrada solicitada, pero su salida no excita a ningún dispositivo, necesitamos programar el speaker para que se conecte al tímer 2. Para esta función utilizaremos el ya mencionado Bit 1 del puerto 60h que indicaba quién maneja el speaker (0:nosotros 1:Timer 2), además en el caso de que este bit este encendido, el bit 1 indicará por su parte si el speaker esta conectado o no.
En el siguiente programa de demostración os muestro cómo se lleva acabo la programación del tímer 2 en la práctica, podéis ejecutarlo para ver los resultados.
Pulsa F2 para ejecutar el programa SONIDO. - Posibilidad no disponible en la versión HTML-
Acabo este artículo exponiendo la prometida tabla de frecuencias musicales, quién entienda solfeo que la utilice, yo, seguiré haciendo mis pitidos de siempre.
Octava 0 Octava 1 Octava 2 Octava 3 Octava 4 Octava 5 Octava 6 Octava 7Pulsa F2 para ejecutar el programa MARCADOR. - Posibilidad no disponible en la versión HTML-.
Todo se basa en la utilización de una función de la BIOS suministrada por la interrupción de video 10h. Este servicio es la función 11h, subfunción 30h. Al llamar a esta función, la BIOS nos devuelve un puntero (una dirección) a una zona de memoria donde se encuentra, un mapa del juego de caracteres de la VGA. Podemos solicitar siete juegos de caracteres distintos, según el valor que pongamos en el registro bh. El formato completo para esta función es:
int 10h Función 11h Subfunción 30h
Entrada:
ah
= 11h
(*número de función*)
al =
30h
(*número de subfunción*)
bh =
3
(*caracteres 8x8*)
(*bh=2 caracteres 8x14*)
(*bh=6 caracteres 8x16*)
Salida : es:bp=
puntero FAR (segmento:desplazamiento) donde reside
el juego de caracteres pedido.
Si nosotros situamos en esa dirección un array como el
siguiente:
Tabla:Array[ 1..256 , 1..8 ] of byte;
------ ----
| | | |
| | | |
Número de Altura del
caracteres carácter
obtendremos en cada posición de esa tabla una máscara
(byte) de la fila indicada por la segunda dimensión de la tabla,
del carácter indicado por la primera posición de la
tabla.
Os pondré un ejemplo para que 'pilléis' la idea:
Imaginemos que queremos obtener las máscaras (filas) que forman
la letra 'A' mayúscula. El mapa del carácter
sería:
12345678
12345678
1
__
1 00011000 <--
Tabla[65,1]
= 24
2
____
2 00111100 <--
Tabla[65,2]
= 60
3 __
__
3 01100110 <--
Tabla[65,3]
= 102
4 __
__
4 01100110 <--
Tabla[65,4]
= 102
5
______
5 01111110 <--
Tabla[65,5]
= 126
6 __
__
6 01100110 <--
Tabla[65,6]
= 102
7 __
__
7 01100110 <--
Tabla[65,7]
= 102
8
8 00000000 <--
Tabla[65,8]
= 0
(BINARIO)
(DECIMAL)
Esto se aplica para todos los caracteres y para todos los tamaños, con los cambios necesarios.
Os presento a continuación un programa para modo texto más sencillito que el anterior, donde podréis ver cómo utilizar el mapa de caracteres. :)
Pulse F2 para ejecutar el programa BANNER. - Posibilidad no disponible en la versión HTML-.
Bueno me imagino que si estáis aquí, ya habréis leído todos los artículos y tanto Benjamín como yo estamos impacientes por conocer vuestra opinión. Os recuerdo que este curso y todo lo que conlleva tiene poco más de un mes de vida y en ese tiempo la perfección es imposible, luego pienso que tendréis bastantes cosas que decir.
Me he permitido facilitaros la siguiente encuesta para destacar los puntos de los que más nos interesa saber vuestra opinión, pero podéis añadir todas las cuestiones que queráis:
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
Hasta que no decidamos la forma de distribución, no podemos
comprometernos a nada, pero mencionaros que una de las posibilidades
que
estamos contemplando es 'abrir' nuestro CPV a todo el que quiera
participar
con programas, artículos propios, gráficos, composiciones
musicales, o ¿Por qué no?, lecciones magistrales.
Así
que podéis ir pensando en ello.
Otro asunto, aunque os pueda resultar una tontería, hemos intentado cuidar al máximo nuestro lenguaje en los artículos del CPV y como os habréis dado cuenta, evitamos el uso de anglicismos, pero este empeño nuestro, no es tampoco una obsesión, luego rogamos a los puristas nos perdonen toda 'vestihalidad' cometida. :)
Sobra decir que si encontráis cualquier bug, error grave o definición equivocada, nos lo comuniquéis sin ningún prejuicio, ya que después de ver el DOOM, nuestro orgullo como programadores de videojuegos aficionadillos, está lo bastante mitigado para aceptar de buen gusto correcciones y cachetitos.
Quiero dar las GRACIAS a todas las personas que han
participado
en el lanzamiento del CPV:
Gracias a Benjamín Moreno Palacios por embarcarse
conmigo
en una nave de enormes dimensiones, que solo cuenta con dos
tripulantes,
novatos y sin salvavidas. :)
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 Cristobal Saraiba Bello por hacer de cartero para mi en Sakery BBS. :)
Gracias a Juán Antonio Ubeda Torres por permitirnos utilizar sus gráficos para el CPV.
Gracias a Jesús Angel Sánchez Mena, porque tan
loco hay que estar para darse las gracias a sí mismo como para
afrontar
un proyecto de esta envergadura . ;)
En el número que viene trataremos los siguientes temas.
- El ratón. Estudio de las diferentes herramientas y
métodos
de control.
- La paleta gráfica. Vamos a descubrir el color en las imágenes y ver que podemos hacer con él.
- Modo 13h 2/3. En esta entrega estudiaremos los sprites, el ABC de los videojuegos y realizaremos animaciones con ellos.
- Formato gráfico PCX. Iniciaremos nuestro estudio de los formatos gráficos con el archiconocido PCX.
- El juego del mes. Para el siguiente capítulo ya tenemos preparado el que será el juego del mes, como será norma durante el curso, éste, se basará en el uso de los conceptos tratados en la lección y os servirá de ejemplo super-práctico. Además de ilustraros espero que os sirva para, entre artículo y artículo, echar una partidilla. Para que vayáis practicando y afinando vuestra puntería aquí tenéis el ejecutable del juego.
Pulse F2 para ejecutar el programa DARDOS. - Posibilidad no disponible en la versión HTML-.
Bueno pues que os divirtáis y cuidad vuestro pulso. El próximo número saldrá para principios de febrero-95, por cierto feliz año, espero tener algo que contestar para esa fecha. Gracias a todos por llegar hasta aquí, un saludo de JASM y BMP y .... :)
Hasta
luego