domingo, 9 de diciembre de 2012

Representación interna de un flotante

En este post vamos a hacer un pequeño experimento para desplegar la representación de un flotante, ya que a final de cuentas debe ser guardado como un conjunto de bits.

Primero, repasemos rápidamente uniones en C. En una unión todos sus miembros comparten la misma ubicación de memoria. Por ejemplo, en el siguiente programa, la unión contiene dos elementos, una estructura con cuatro enteros y un arreglo de cuatro enteros. Observen como inicializamos los valores de la estructura y posteriormente desplegamos esos valores usando los miembros del arreglo.

#include <stdio.h>

typedef struct {
  int byte1;
  int byte2;
  int byte3;
  int byte4;
} Mi_estructura;

typedef union {
  Mi_estructura estructura;
  int arreglo[4];
} Mi_union;

void main(void) {
  Mi_union u;
  int i;
  
  u.estructura.byte1 = 11;
  u.estructura.byte2 = 22;
  u.estructura.byte3 = 33;
  u.estructura.byte4 = 44;
  
  for(i = 0; i < 4; i++) {
    printf("%d\n",u.arreglo[i]);
  }
}

Este caso en específico puede ser útil cuando algunas partes del programa son más legibles usando los miembros de la estructura, por ejemplo al momento de asignar valores. Y otras veces es más eficiente usar un arreglo, como al querer copiar o desplegar los elementos.

Regresando al caso de un flotante. Usaremos una unión que contiene un float y un int, en la máquina que estoy usando ambos usan 4 bytes. Lo que haremos es modificar la unión usando el float y desplegar el contenido usando el int.

#include <stdio.h>

typedef union {
  int i;
  float f;
} Mi_union;

void main(void) {
  Mi_union u;
  printf("# bytes int: %zu\n",sizeof(int));
  printf("# bytes float: %zu\n",sizeof(float));
  
  u.f = 1234.5678f;
  printf("%.10f\n",u.f);
  printf("0x%x\n",u.i);
}

Podemos ver en la imagen anterior que el valor 1234.5678f es almacenado con el siguiente patrón de bits 0x449a522b. Este patrón sigue el estándar IEEE754, la siguiente página contiene una muy buena explicación.

Lo que haremos es extraer los bits de acuerdo a este formato y verificaremos que tengamos el valor 1234.5678f.

#include <stdio.h>

typedef union {
  unsigned int i;
  float f;
} Mi_union;

unsigned int extraer_bits(unsigned int valor, short inicio, 
                          short nbits);
void representacion_float(unsigned int valor);

void main(void) {
  Mi_union u;
  printf("# bytes int: %zu\n",sizeof(int));
  printf("# bytes float: %zu\n",sizeof(float));
  
  u.f = 1234.5678f;
  printf("%.10f\n",u.f);
  printf("0x%x\n",u.i);
  representacion_float(u.i);
}


void representacion_float(unsigned int valor){
  unsigned int signo = 0;
  unsigned int exponente_bias = 0;
  unsigned int exponente_unbias = 0;
  unsigned int mantisa = 0;
  unsigned int parte_entera = 0;
  unsigned int parte_decimal = 0;
  unsigned int parte_decimal_max = 0;
  
  signo = extraer_bits(valor, 31, 1);
  exponente_bias = extraer_bits(valor, 23, 8);
  exponente_unbias = exponente_bias - 127;
  mantisa = extraer_bits(valor, 0, 23);
  parte_decimal = extraer_bits(valor,0,23-exponente_unbias);
  parte_decimal_max = 1<<23-exponente_unbias;
  parte_entera = extraer_bits(valor,23-exponente_unbias,
                              exponente_unbias);
  // la parte entera tiene un bit 1 por default al inicio.
  parte_entera |= 1<<exponente_unbias;
  
  printf("Signo: %d\n", signo);
  printf("Exponente (bias): 0x%x (%d)\n", exponente_bias, 
                                          exponente_bias);
  printf("Exponente (unbias): %d\n", exponente_unbias);
  printf("Mantisa: 0x%x\n", mantisa);
  printf("Parte entera: 0x%x (%d)\n", parte_entera, 
                                      parte_entera);
  printf("Parte fraccional: 0x%x (%d) de 0x%x (%d)\n", 
    parte_decimal, parte_decimal, 
    parte_decimal_max, parte_decimal_max);
}


unsigned int extraer_bits(unsigned int valor, short inicio, 
                 short nbits){
  unsigned int mascara = 0;
  short i = 0;
  // primero recorremos el bit inicial al bit 0
  valor >>= inicio;
  // obtener mascara de los bits que deseamos
  for(i = 0; i < nbits; i++){
    mascara <<= 1;
    mascara |= 1;
  }
  // enmascaramos los bits que deseamos
  return valor & mascara;
}

Noten que el código no es muy robusto, ya que para números muy grandes (es decir, exponentes muy grandes) no funciona. Pero ayuda a ilustrar la representación de un flotante.

El programa obtiene los bits correspondientes al signo, el exponente y la mantisa. Además usa el exponente para extraer de la mantisa la parte entera y fraccionaria. La parte fraccionaria la podemos interpretar ya sea viendo los bit y calculando el valor. Por ejemplo, 0x122b en binario es 1 0010 0010 1011, lo cual lo podemos evaluar como 2^−1+2^−4+2^−8+2^−10+2^−12+2^−13 = 0.567749023. Otra forma de verlo, es que el máximo valor de la parte fraccionaria más uno es 0x2000 ó 8192 decimal (es decir esto equivale al valor 1). Por lo que nuestra parte fraccionaria es 4651/8192=0.567749023. Noten, que el valor que obtuvimos es ligeramente menor al valor inicial, esto es a lo que llamamos errores de precisión.

Espero que les haya resultado interesante ver de una forma práctica como guardamos número flotantes.

domingo, 2 de diciembre de 2012

Reiniciar peticiones de tweets

Tutorial de Grafos
Anterior        Índice        Siguiente

En este post volveremos a visitar el programa que obtiene la información de los hash tags. Agregué dos nuevas funcionalidades.

La primera mejora tiene que ver con qué pasa si queremos volver a ejecutar búsquedas y guárdalas en la misma carpeta de tweets de una ejecución anterior. Sin ninguna modificación al programa, lo que pasaría es que los nuevos archivos de salida se agregan sin problema aún cuando volvamos a visitar un mismo hash tag, ya que el nombre del archivo esta formado por un “timestamp” y el hash tag (por ejemplo: 1351987950#felicidad). Y tal vez ese sea un comportamiento deseado.

Sin embargo, decidí modificar el programa para evitar visitar hash tags que hayan sido visitados en ejecuciones anteriores. Esto es muy sencillo, ya que el nombre del archivo contiene el hash tag, por lo que sólo tuve que extraerlos y agregarlos al conjunto de hash tags visitados.

Un efecto secundario de este cambio es cuando nuestro hash tag inicial ya lo habíamos visitado anteriormente, el resultado es que el programa termina inmediatamente. Esto nos lleva a el segundo cambio, inicializar automáticamente la búsqueda cuando pasemos “##” como hash tag inicial en la línea de comandos.

En caso de que hayamos ejecutado el programa previamente, entonces tendremos un conjunto de archivos de los hash tags visitados. Los hash tags dentro de los archivos no necesariamente han sido visitados, por lo que inicializaré los hash tags a visitar con todos aquellos hash tags que no se visitaron en ejecuciones anteriores. El siguiente comando visitará otros 2,000 hash tags, tomando como base la información en "../tweets" de los hash tags visitados anteriormente.

python twitter_search.py "##" 100 5 2000 ../tweets

El código lo pueden encontrar en la siguiente liga.

Creo que por el momento el programa de extracción de datos está bastante completo, en el siguiente post veremos cómo construir nuestro grafo de hash tags con esta información.

Tutorial de Grafos
Anterior        Índice        Siguiente

viernes, 23 de noviembre de 2012

Grafo de hash tags (parte 3)

Tutorial de Grafos
Anterior        Índice        Siguiente

Hasta el momento podemos obtener un número de tweets deseados para un hash tag. El objetivo es obtener la relación entre hash tags. El programa de este post, analiza cada tweet y extrae los hash tags para poder construir más búsquedas. De esta forma podemos obtener más tweets de otros tópicos.

Al mismo tiempo de obtener tweets y extraer hash tags, iremos guardando la información. Usaremos la siguiente convención para guardar los datos. Por cada hash tag que consultemos crearemos un archivo, cuyo nombre es el resultado de concatenar un “timestamp” con el hash tag. El archivo contiene un hash tag por línea, estos son los hash tags encontrados en los tweets que regreso la búsqueda. Por ejemplo, después de consultar #Mexico, obtuvimos el siguiente archivo:

Nombre del archivo: 1351385380#mexico

Contenido del archivo:

#musica 
#cancun 
#guadalajara 
...
#instagram 
#instagramhub 
...
#saltillo 
#torreon 
...
#saltillo 
#argentina 
#colombia 
#bolivia 
#brasil 
...
#reforma 
#instagram
...

Como pueden ver, algunos hash tags aparecen más de una vez; esto se debe a que el mismo hash tag está en varios tweets. Decidí dejar los duplicados ya que considero que son un muy buen indicador de qué tan relacionados están los tópicos.

Esta versión del programa recibe cinco parámetros, por ejemplo:

python twitter_search.py "#Mexico" 50 5 500 ./tweets
  1. El hash tag inicial, es decir, con el que iniciaremos las búsquedas.
  2. El número de resultados por petición. En este ejemplo cada petición a Twitter regresará a lo más 50 tweets.
  3. El número de peticiones por hash tag. En el ejemplo, para cada hash tag que busquemos realizaremos a los más 5 peticiones. Este parámetro junto con el anterior nos da el máximo número de tweets por hash tag. En este caso a lo más 250 tweets por hash tag. Puede ser menor, ya que algunos tópicos son poco populares.
  4. El número de hash tags a visitar. Por ejemplo, buscaremos 500 hash tags.
  5. El directorio donde queremos guardar los resultados. Después de que se termine de ejecutar el comando anterior, tendremos 500 archivos en el directorio ./tweets

La lógica del programa es la siguiente:

Mientras no hayamos visitado el número de hash tags

  1. Obtener los tweets de un hash tag que aún no hayamos visitado.
  2. Extraer los hash tags de los tweets.
  3. Guardar los hash tags asociados al hash tag consultado.
  4. Actualizar la información de los hash tags a visitar con los hash tags obtenidos en el paso 2. En este paso sólo agregamos hash tags que no hayamos visitado y que no estén ya en la lista por visitar.

El código lo pueden encontrar en la siguiente liga.

Tutorial de Grafos
Anterior        Índice        Siguiente

domingo, 18 de noviembre de 2012

Grafo de hash tags (parte 2)

Tutorial de Grafos
Anterior        Índice        Siguiente

Ahora modificaremos el programa del post anterior para obtener más de 15 tweets; que es lo que regresa una petición sin parámetros. El programa recibe dos parámetros, además del hash tag inicial, también espera el número de tweets que se desean obtener. Por ejemplo:

python twitter_search.py "#Toluca" 35

Obtiene 35 tweets que contienen el hash tag #Toluca. El programa realiza peticiones hasta que obtiene los tweets deseados.

Sin embargo hay un punto a considerar, la documentación de Twitter menciona que para la API de búsqueda existe un límite en la frecuencia con la que se pueden realizar peticiones, pero no publican dicho límite. En caso de que excedamos este límite simplemente obtendremos algo como:

{"error":"..."}

Por lo que antes de iniciar una nueva petición, espero 5 segundos, si la petición resulta en un error, espero el doble. Quizá tengamos que modificar estos valores.

El siguiente es un ejemplo de la salida del programa.

El código lo pueden encontrar aquí. En el siguiente post comenzaremos a navegar entre hash tags.

Tutorial de Grafos
Anterior        Índice        Siguiente

sábado, 3 de noviembre de 2012

Grafo de hash tags (parte 1)

Tutorial de Grafos
Índice        Siguiente

Realizar una búsqueda usando la API de twitter es tan sencillo como construir la siguiente URL:

http://search.twitter.com/search.json?q=%23Toluca

Esta consulta regresa tweets recientes que contienen el hash tag #Toluca. De hecho si pegan esta URL en su navegado pueden observar el formato de la respuesta. La respuesta viene en formato JSON. Los signos de llaves indican un objeto y los corchetes indican un arreglo.

{
 "completed_in":0.089,
 "max_id":259679677768151040,
 "max_id_str":"259679677768151040",
 "next_page":"?page=2&max_id=259679677768151040&q=%23Toluca",
 "page":1,
 "query":"%23Toluca",
 "refresh_url":"?since_id=259679677768151040&q=%23Toluca",
 "results":
  [
   {
    "created_at":"Sat, 20 Oct 2012 15:18:54 +0000",
    "from_user":"tlcweather",
    "from_user_id":461072765,
    "from_user_id_str":"461072765",
    "from_user_name":"Toluca Weather",
    "geo":null,
    "id":259675063110995968,
    "id_str":"259675063110995968",
    "iso_language_code":"es",
    "metadata":{"result_type":"recent"},
    "profile_image_url":"http:\/\/a0.twimg.com\/profile_...",
    "profile_image_url_https":"https:\/\/si0.twimg.com\/...",
    "source":"<a href="http:\/\/www.google.com\/">Google<\/a>",
    "text":"Toluca, MEXICO Weather :: 13C Mist ... #Toluca #Mexico",
    "to_user":null,
    "to_user_id":0,
    "to_user_id_str":"0",
    "to_user_name":null
   },
   {...}
  ],
 "results_per_page":15,
 "since_id":0,
 "since_id_str":"0"
}

Analicemos algunas variables de la respuesta JSON que utilizaremos. Como podemos ver en la variable results_per_page, cada consulta regresa sólo 15 tweets. Para obtener los siguientes 15 tweets utilizaremos la variable next_page.

La variable results es un arreglo de objetos. Cada objeto de results contiene el usuario que creó el tweet, si está dirigido a alguien, cuándo fue creado y por supuesto el texto del tweet.

El programa en python recibe el hash tag que deseamos buscar:

python twitter_search.py "#Toluca"

El programa construye y realiza la petición de búsqueda. Después de parsear el resultado, obtenemos un mapa de llaves y valores. Por ejemplo, para acceder a la variable next_page usamos algo como search_result["next_page"]. El programa despliega todos los elementos del resultado de búsqueda, todos los elementos de un tweet y finalmente sólo los textos de los 15 tweets.

En el siguiente post extenderemos este ejemplo para obtener más de 15 tweets.

Tutorial de Grafos
Índice        Siguiente

Tutorial de Grafos

Una de las estructuras de datos que más ha llamado mi atención son los grafos. Y no sólo por la parte teórica, sino por su presencia en diversas situaciones. Uno de los ejemplos más claros son las redes sociales. Este es el primer post de una serie en la que hablaremos de grafos y algunos algoritmos.

El primer paso será construir un grafo. Resulta un tanto aburrido el estudiar grafos donde los nodos son simplemente letras y las conexiones entre ellos no tienen un significado real. Algunos libros cuando hablan de encontrar rutas usan ejemplos donde los nodos son ciudades y las conexiones son caminos entre ellas. Intentaré usar algo más atractivo para esta serie.

Después de investigar un poco las APIs de Facebook y Twitter, decidí que utilizaremos el servicio de búsqueda de Twitter, ya que no requiere autenticación, así que es muy fácil de empezar a usar. Como lenguaje de programación usaremos Python y el código estará en mercurial.

En el siguiente post comenzaremos a investigar cómo construir nuestro grafo usando la API de Twitter.

Todos los post de esta serie se irán listando a continuación:

sábado, 20 de octubre de 2012

Grafo de Lenguajes de Programación

La siguiente página muestra una red de lenguajes de programación, donde las conexiones significan que un lenguaje influenció a otro. Entre más grande sea un nodo significa que ese lenguaje ha influenciado a más lenguajes de programación.

Cuando posicionas el cursor sobre un lenguaje muestra en un color los lenguajes que lo han influenciado y en otro color los que ha influenciado. Dando clic en el nodo despliega más información acerca del lenguaje.

Esta visualización resulta muy interesante. Además, si en algún momento deseamos aprender un nuevo lenguaje, puede ser una buena guía. Por ejemplo, tal vez resulte de más utilidad aprender un lenguaje que tiene mucha influencia, porque nos puede ayudar a aprender fácilmente más lenguajes.

En la página también explican como fue generado el grafo y tiene enlaces al código.

Programming Languages Influence Network

domingo, 2 de septiembre de 2012

Recomendaciones de video clases

El hecho de que universidades como el MIT, Berkeley, Stanford, University of Washington publiquen diferentes video clases de forma gratuita, nos brinda la oportunidad de estudiar por nuestra cuenta o reforzar conocimientos sobre alguna clase que cursemos. Además de poder seguir la clase a nuestro propio ritmo o ver sólo un tópico que nos resulte interesante.

Recientemente, me topé con un par de pláticas acerca de nuevos sitios que están impulsando la educación gratuita en línea y están promoviendo técnicas más interactivas. Además ambos sitios se enfocan en proveer tutoriales cortos (entre 5 y 10 minutos) para poder mantener lo mejor posible el interés del estudiante.

Udacity

Este sitio ofrece clases como inteligencia artificial, algoritmos, desarrollo web y criptografía. Las video clases son divididas en segmentos de alrededor de cinco minutos y entre videos usualmente se presenta un pequeño examen para reforzar lo aprendido.

Un punto que puede resultar atractivo es que el material es presentado por profesores de reconocidas universidades. Por ejemplo, el curso de inteligencia artificial fue preparado por Sebastian Thrun, quien además de ser uno de los fundadores de Udacity, es un reconocido profesor de inteligencia artificial de Stanford y es el líder del proyecto del automóvil autónomo de Google.

Khan Academy

Contiene videos acerca de una gran variedad de tópicos (economía, álgebra, cálculo, física, química, entre muchos otros) presentados de una forma muy amigable, explicando de la manera más simple los conceptos. Los videos son cortos y se obtiene más provecho cuando se selecciona una serie que cubre cierto tema.

Pero este sitio no sólo se limita a publicar videos. Han estado probando estrategias para hacer más interactiva y personalizada la educación. El siguiente video es una plática del fundador del sitio donde habla desde cómo nació la idea hasta cómo han estado colaborando con escuelas para mejorar la educación.

lunes, 27 de agosto de 2012

Recursos Ensamblador 8086

Durante los últimos meses estuvimos publicando diversos posts acerca de lenguaje ensamblador. En este post nos gustaría compartir las mejores fuentes de información que estuvimos usando:

  • Esta presentación de modos de direccionamiento resultó muy útil para estar constantemente revisando los diferentes detalles al direccionar localidades de memoria.
  • La siguiente página muestra una lista muy buena de interrupciones y posibles valores para los parámetros de dichas interrupciones.
  • Una breve lista de instrucciones.
  • Definitivamente esta página contiene bastante detalle. En ella consultamos cómo exportar variables y funciones entre módulos y cómo ligar varios módulos en un ejecutable.

martes, 24 de julio de 2012

Ensamblador 8086 Buffer Doble

En este último post de la serie, explicaremos la técnica de buffer doble para pintar en la memoria de gráficos y comentaremos algunas posibles mejoras al código. Como platicábamos en el post anterior, si escribimos continuamente al área de memoria, puede suceder que versiones incompletas de nuestra imagen sean desplegadas. La técnica de buffer doble consiste en reservar un área de memoria del mismo tamaño de nuestra memoria de gráficos, en nuestro caso de 64,000 bytes (320x200); y pintar nuestros pixeles en este buffer. Una vez que tengamos nuestra imagen final lista, entonces copiamos nuestro buffer al área de memoria de gráficos. Esto minimiza el tiempo en el que estamos escribiendo directamente al área de gráficos y también el parpadeo. El siguiente video muestra el resultado final.

En el siguiente proyecto de mercurial podrán encontrar los archivos para construir el ejemplo. El archivo ensambla.bat contiene la instrucción usada para crear el ejecutable.

Reservar Memoria

Usamos la interrupción 21h con el servicio 48h para reservar memoria dinámicamente para el buffer doble, el registro bx contiene la cantidad de memoria deseada en bloques de 16 bytes (función iniciar_video en libgraf.asm). Puedes encontrar más información acerca de memoria dinámica en las siguientes referencias: referencia 1 y referencia 2. La variable vram guarda la dirección inicial del buffer doble. Ahora en el código, en lugar de escribir a la dirección A000h (memoria de gráficos), escribimos al buffer doble (vram).

Es importante notar que como reservamos memoria dinámicamente al momento de ligar los archivos (link) es necesario indicar cuanta memoria extra utilizará nuestro programa en tiempo de ejecución, es por ello que usamos la opción /link /cp:4000 en el comando ml del archivo ensambla.bat. El parámetro 4000 corresponde a la cantidad que solicitamos con la interrupción 21h, servicio 48h (4000 x 16 = 64,000 = 320 x 200).

Transparencia

Pareciera que la imagen de la pelota tiene transparencia, pero si la abrimos en un editor, el exterior es de color negro. Para lograr este efecto, en la función LoadBMP de libgraf.asm ignoramos los pixeles cuyo valor sea cero, que corresponde en este caso al color negro de nuestra paleta de colores.

Posibles Mejoras

Es notorio que la pelota se mueve lentamente, esto puede deberse a que estamos haciendo mucho procesamiento innecesario. En cada ciclo del programa (1) abrimos y leemos dos archivos (uno para la imagen de fondo y otro para la imagen de la pelota), (2) escribimos dos veces a la paleta de colores, (3) escribimos las dos imágenes al buffer y (4) copiamos el buffer doble a la memoria de gráficos. Los puntos 1 y 2 sólo son necesarios al inicio del programa.

Para el punto 1, podemos modificar el método mostrar_bmp (en libgraf.asm) para que abra el archivo, reserve memoria dinámica y guarde la imagen en esta memoria. El método mostrar_bmp arrojará como resultados el ancho, el alto y el apuntador a los pixeles de la imagen. De esta forma, en nuestro ciclo principal del programa trabajaremos con información en RAM y no tendremos que abrir continuamente archivos, lo cual es un proceso que consume mucho tiempo.

Con respecto al punto 2, estamos asumiendo que todas las imágenes usadas en el programa utilizan la misma paleta de colores. En caso de que no sea así, algunas imágenes podrían lucir extrañas. Hasta el momento, las imágenes que hemos guardado con Paint parecen tener una paleta común (usamos el programa pixelformer para observar la paleta de colores de las imágenes). Habría que modificar la función mostrar_bmp para que reciba un parámetro que indique si deseamos actualizar la paleta de colores con la paleta de colores de la imagen.

Aún así, en cada ciclo del programa estamos copiando al menos 64,000 bytes del buffer doble al área de memoria de gráficos (función copiar_buffer en libgraf.asm). Creo que resultaría interesante explorar otros modos de video, como el que menciona la siguiente página, que ya tiene soporte directo para un buffer doble y en donde es posible cambiar los buffers con pocas instrucciones.

lunes, 23 de julio de 2012

DOSBox autoexec

En la serie de posts referentes al lenguaje Ensamblador hicimos uso de DOSBox para compilar, ligar y ejecutar los programas que utilizamos como ejemplos.

DOSBox demostró ser una herramienta muy útil, sin embargo, cada vez que lo utilizamos, era necesario llevar a cabo los siguientes pasos:

  1. Montar una unidad de disco nueva que en nuestro caso, apunta al fólder donde tenemos instalado el compilador y el linker de ensamblador.
  2. Agregar al PATH de DOSBox la ruta del fólder donde se localizan nuestros códigos, ya que éste es distinto del fólder donde se encuentran el compilador y el linker.
  3. Abrir el directorio de la carpeta donde se encuentran los programas.

Después de realizar estos pasos, ya estábamos listos para comenzar a trabajar.

Ahora bien, teclear un par de veces estas instrucciones parece muy poco trabajo, pero al cabo de un rato, se vuelve tedioso, por lo que nos dimos a la tarea de automatizarlas.

Para automatizar la ejecución de un conjunto de instrucciones al abrir DOSBox en Windows, debes llevar a cabo estos sencillos pasos:

  1. Desde el botón de inicio de Windows, abre la opción de Todos los programas.
  2. Dirígete al fólder de DOSBox y abre el subfólder llamado Options (Opciones). En este fólder observarás que hay tres archivos. El que nos interesa es el archivo que termina con la palabra Options, que es un archivo de configuración para las opciones que puedes utilizar con DOSBox.
  3. Abre el archivo de opciones simplemente haciendo click sobre él.
  4. Este archivo está dividido en secciones. Reconocerás el título de una sección porque éste se encuentra escrito entre corchetes, por ejemplo: [seccion]. Debajo de cada sección, observarás que las opciones a editar se presentan escritas en forma de variable=valor y los comentarios inician con el símbolo "#".
  5. Dirígete a la última sección, llamada [autoexec]. Debajo de esta sección puedes colocar la serie de instrucciones que deseas que se ejecuten automáticamente al abrir DOSBox.

En nuestro caso, el resultado de editar la sección [autoexec] luce de la siguiente manera:

Al abrir DOSBox, observamos cómo se ejecutaron automáticamente las instrucciones que editamos en el archivo de configuración:

miércoles, 18 de julio de 2012

Ensamblador 8086 Moviendo imagen BMP

En el post anterior de esta serie mostramos cómo desplegar una imagen bmp. En este post expandiremos el código para mover la imagen alrededor de la pantalla. El siguiente video muestra el objetivo final del programa.

Lo primero que realizamos fue separar las rutinas relacionadas con desplegar la imagen, éstas fueron colocadas en el archivo libgraf.asm con su respectivo archivo libgraf.inc para poder exportar las funciones más importantes a otros módulos (el siguiente post muestra cómo exportar funciones entre módulos). La función principal para desplegar una imagen bmp (mostrar_bmp) ahora recibe los siguientes parámetros: la ruta del archivo y la posición inicial de la esquina superior izquierda de la imagen en la pantalla. De esta forma ya podemos colocar una imagen en cualquier parte de la pantalla y no sólo en la esquina superior izquierda.

La lógica para mover la imagen en la pantalla es muy sencilla (bmp01.asm):

1 Inicializar el modo de video
2 Ciclo hasta que presionemos una tecla:
   2.1 Limpiar pantalla
   2.2 Actualizar la coordenada x en la dirección actual
   2.3 Si hubo una colisión contra el límite izquierdo o derecho
      2.3.1 Ajustar coordenada x
      2.3.2 Invertir dirección en x
   2.4 Realizar pasos 2.2 y 2.3 para y
   2.5 Desplegar imagen

El código que implementa esta lógica se muestra a continuación (bmp01.asm). En el siguiente proyecto de mercurial podrás descargar los 3 archivos necesarios para construir el ejecutable. El proyecto también incluye la imagen bmp usada. Para ensamblar y ligar estos archivos basta con invocar:

ml bmp01.asm libgraf.asm

Como pueden observar en el video, la imagen parpadea notablemente, esto se debe a que estamos escribiendo al área de memoria de gráficos al mismo tiempo que esta área está siendo desplegada en la pantalla. Esto causa que frecuentemente se desplieguen en la pantalla versiones incompletas de la imagen final. En el siguiente post exploraremos una opción para mejorar esto.

.model  small
.stack 128
INCLUDE libgraf.inc
.data
; El nombre del archivo debe terminar en 0
pelotaf         db "pelota.bmp",0
pelotax         dw 100
pelotay         dw 100
pelotad         dw 40
maxx            dw 320
maxy            dw 200
deltax          dw 2
deltay          dw 2

.code
.startup
  mov ax,@data
  mov ds,ax
main proc
  ; Configurar modo grafico VGA con resolucion de 320x200
  call    InitVid
  
  ciclo1:
  call limpiar_pantalla
  ; -------------------------------------------------------
  ; Controlar posicion x de la pelota
  ; -------------------------------------------------------
  testX:
  mov ax, pelotax
  ; avanzar la pelota en la direccion actual
  add ax, deltax
  
  ; validar colision contra limite derecho
  testmaxX:
  mov cx, maxx
  sub cx, pelotad
  cmp cx, ax
  jg testminX
  ; si la pelota esta en el limite derecho
  ; alinearla al limite e invertir la direccion
  mov ax, cx
  mov deltax, -2
  
  ; validar colision contra limite izquierdo
  testminX:
  cmp ax,0
  jg testY
  ; si la pelota esta en el limite izquierdo
  ; alinearla al limite e invertir la direccion
  mov ax,0
  mov deltax, 2
  
  ; -------------------------------------------------------
  ; Controlar posicion y de la pelota
  ; -------------------------------------------------------
  testY:
  mov bx, pelotay
  ; avanzar la pelota en la direccion actual
  add bx, deltay
  
  ; validar colision contra limite inferior
  testmaxY:
  mov cx, maxy
  sub cx, pelotad
  cmp cx, bx
  jg testminY
  ; si la pelota esta en el limite inferior
  ; alinearla al limite e invertir la direccion
  mov bx, cx
  mov deltay, -2
  
  ; validar colision contra limite superior
  testminY:
  cmp bx,0
  jg dibpelota
  ; si la pelota esta en el limite superior
  ; alinearla al limite e invertir la direccion
  mov bx,0
  mov deltay, 2
  
  ; -------------------------------------------------------
  ; Actualizar posicion (x,y) y pintar pelota
  ; -------------------------------------------------------
  dibpelota:
  mov pelotax, ax
  mov pelotay, bx
  mov dx, offset pelotaf
  call mostrar_bmp

  ; Si una tecla es presionada, salimos del ciclo.
  ; Noten que esta interrupcion no espera por una tecla,
  ; solo verifica si fue presionada y si no, continua.
  mov ah,01h
  int 16h
  jnz fin

  jmp ciclo1

  fin:
  ; Regresar a modo texto
  mov ax,0003h
  int 10h
  
  ; Finalizar el programa
  mov ax,4c00h
  int 21h
  ret
main endp
end

domingo, 8 de julio de 2012

Ensamblador 8086 Creando módulos

Conforme desarrollamos ejemplos más elaborados, el tamaño de nuestros archivos aumenta y muchas veces copiamos el mismo código en varios programas (por ejemplo, el código que despliega una imagen bmp). En este post, veremos una forma de cómo reusar funciones que están en un módulo (archivo) independiente.

Tomemos como ejemplo el programa de este post. El programa convierte dos cadenas a enteros usando la función atoi (ascii to integer), los suma y convierte el resultado a cadena usando itoa (integer to ascii) para poder desplegarlo. Las funciones itoa/atoi son funciones que podemos reusar en varios programas, por lo que es buena idea ponerlas en un archivo independiente.

Comencemos por colocar estas funciones en un archivo llamado libcad.asm. El segundo paso es crear un archivo que contenga los prototipos de las funciones que queremos usar en otros archivos. Llamemos a este archivo libcad.inc. El contenido del archivo libcad.inc es el siguiente:

.CODE
EXTERNDEF atoi:NEAR
EXTERNDEF itoa:NEAR
@CurSeg ENDS

Noten que debemos especificar el segmento en donde se encuentra lo que queremos exportar. En este caso nuestras funciones se encuentran en el segmento .CODE; además, es recomendable cerrar el segmento ya que incluiremos este archivo en otros archivos. La palabra clave EXTERNDEF declara lo que queremos exportar, en este caso es seguida por el nombre de la función y el tipo de la función (NEAR, porque estamos usando .MODEL SMALL en nuestros programas).

El archivo libcad.inc se debe incluir tanto en el archivo que define las funciones como en los archivos que las usan. El archivo libcad.asm queda de la siguiente forma:

.model SMALL
.STACK 128
INCLUDE libcad.inc
.CODE
; ========= Convertir cadena a numero =====================
; Parametros
; si: offset inicial de la cadena con respecto a DS
; Retorna
; bx: valor
atoi proc NEAR
  xor bx,bx   ;BX = 0

  atoi_1:
  lodsb       ;carga byte apuntado por SI en AL
              ;e incrementa si
  cmp al,'0'  ;es numero ascii? [0-9]
  jb noascii  ;no, salir
  cmp al,'9'
  ja noascii  ;no, salir

  sub al,30h  ;ascii '0'=30h, ascii '1'=31h...etc.
  cbw         ;byte a word
  push ax
  mov ax,bx   ;BX tendra el valor final
  mov cx,10
  mul cx      ;AX=AX*10
  mov bx,ax
  pop ax
  add bx,ax
  jmp atoi_1  ;seguir mientras SI apunte a un numero ascii
  noascii:
  ret         ;BX tiene el valor final
atoi endp

; =============== Convertir numero a cadena ===============
; Parametros
; ax: valor
; bx: donde guardar la cadena final
; Retorna
; cadena
itoa proc NEAR
  xor cx,cx  ;CX = 0

  itoa_1:
  cmp ax,0   ; El ciclo itoa_1 extrae los digitos del
  je itoa_2  ; menos al mas significativo de AX y los
             ; guarda en el stack. Al finalizar el 
  xor dx,dx  ; ciclo el digito mas significativo esta
  push bx    ; arriba del stack.
  mov bx,10  ; CX contiene el numero de digitos
  div bx
  pop bx
  push dx
  inc cx
  jmp itoa_1

  itoa_2:
  cmp cx,0    ; Esta seccion maneja el caso cuando
  ja itoa_3   ; el numero a convertir (AX) es 0.
  mov ax,'0'  ; En este caso, el ciclo anterior
  mov [bx],ax ; no guarda valores en el stack y
  inc bx      ; CX tiene el valor 0
  jmp itoa_4

  itoa_3:
  pop ax      ; Extraemos los numero del stack
  add ax,30h  ; lo pasamos a su valor ascii
  mov [bx],ax ; lo guardamos en la cadena final
  inc bx
  loop itoa_3

  itoa_4:
  mov ax,'$'  ; terminar cadena con '$' para 
  mov [bx],ax ; imprimirla con la INT21h/AH=9
  ret
itoa endp

end

Y el archivo de ejemplo que usa las funciones es el siguiente (ejem01.asm):

.model SMALL
.STACK 128
INCLUDE libcad.inc
.DATA
  cadena1 db '123$'
  cadena2 db '444$'
  cadena3 db 6 DUP(?)
  op1 dw ?
  op2 dw ?

.CODE
  .STARTUP
  mov ax,@data
  mov ds,ax

main proc
  ; SI parametro
  mov si, offset cadena1
  call atoi
  mov op1,bx
   
  ; SI parametro
  mov si, offset cadena2
  call atoi
  mov op2,bx
   
  ; sumar
  mov ax, op1
  add ax, op2
  mov bx, offset cadena3
  call itoa
   
  mov dx, offset cadena3
  call desplegar
   
  ; INT 21h / AH=4Ch retorna el control al sistema operativo
  ;                  termina el programa
  salir:
  mov ax,4c00h
  int 21h
  ret
main endp

; ============ Proc: Desplegar mensaje ====================
; Parametros
; dx: offset de cadena terminada por $ con respecto a DS
desplegar proc
   ; INT 21h / AH=9 - despliega cadena apuntada por DS:DX. 
   ; la cadena debe estar terminada por '$'.
   mov ah,09h
   ;mov dx, offset cad
   int 21h
   ret
desplegar endp

end

Para ensamblar y ligar estos archivos basta con invocar

ml EJEM01.ASM LIBCAD.ASM

sábado, 12 de mayo de 2012

Ensamblador 8086 Desplegar una imagen BMP

En el post anterior hablamos acerca del formato BMP que contiene una paleta de colores. Ahora veremos cómo podemos desplegar en pantalla una imagen de este tipo.

A continuación mostraremos el código que es responsable de desplegar la imagen BMP en pantalla utilizando el modo gráfico 13h, la memoria de gráficos y la paleta de colores, pero primero haremos una descripción general de cómo está estructurado este código.

Lo primero que encontramos son las definiciones de las variables que contienen información acerca de la imagen, los mensajes de error y la ruta donde se encuentra guardada la imagen. En el código encontramos varios procedimientos y el primero de ellos es InitVid que inicializa el modo de video y se asegura de que el segmento ES apunte a la memoria de video para que más adelante se pueda desplegar la imagen en la pantalla.

El procedimiento principal se llama mostrar_bmp. La llamada a este procedimiento es lo único que debemos realizar para desplegar la imagen. Dentro de mostrar_bmp encontramos las llamadas correspondientes a procedimientos auxiliares que desempeñan distintas tareas, como por ejemplo: abrir el archivo, leer el encabezado de la imagen, validar el formato BMP, leer la paleta, cargar y desplegar el BMP.

El código correspondiente a cada uno de estos procedimientos auxiliares así como sus descripciones se encuentra a continuación de la definición del procedimiento mostrar_bmp.

Además, en el siguiente link podrás descargar la imagen BMP que usamos para las pruebas.

; This program is free software: you can redistribute it 
; and/or modify it under the terms of the LGNU Lesser 
; General Public License as published by the Free Software 
; Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be 
; useful, but WITHOUT ANY WARRANTY; without even the 
; implied warranty of MERCHANTABILITY or FITNESS FOR A 
; PARTICULAR PURPOSE.  See the LGNU Lesser General Public 
; License for more details.
;
; You should have received a copy of the LGNU Lesser 
; General Public License along with this program.  If not, 
; see .
 
; Programa para desplegar una imagen tipo bitmap (mibmp.asm)

; El procedimiento mostrar_bmp carga un archivo tipo BMP 
; de Windows y la despliega en la pantalla. Recibe los 
; siguientes parametros: DS:DX que apunta a una cadena tipo 
; ASCIIZ la cual contiene la ruta donde esta guardado el 
; archivo BMP. La resolucion maxima en modo grafico es de 
; 320x200, con 256 colores disponibles. Este codigo esta 
; adaptado del codigo de Diego Escala, Miami, Florida
; y puede encontrarse en: 
; http://www.ece.msstate.edu/~reese/EE3724/labs/lab9/bitmap.asm

.model  small
.stack 128

.data
Header          label word
HeadBuff        db 54 dup('H')
palBuff         db 1024 dup('P')
ScrLine         db 320 dup(0)

BMPStart        db 'BM'

PalSize         dw ?
BMPHeight       dw ?
BMPWidth        dw ?

msgInvBMP       db "Archivo BMP invalido.",7,0Dh,0Ah,24h
msgFileErr      db "Error al abrir archivo.",7,0Dh,0Ah,24h
; El nombre del archivo debe terminar en 0
ruta            db "imagen.bmp",0

.code
;.startup
  mov ax,@data
  mov ds,ax
main proc
  ; Configurar modo grafico VGA con resolucion de 320x200
  call    InitVid
  mov dx, offset ruta
  call mostrar_bmp

  ; Esperar por cualquier tecla
  mov ah,10h
  int 16h

  ; Regresar a modo texto
  mov ax,0003h
  int 10h
  
  ; Finalizar el programa
  mov ax,4c00h
  int 21h
  ret
main endp


mostrar_bmp proc
  ; Guardar los registros en la pila
  PUSH AX
  PUSH CX
  PUSH DX
  PUSH BX
  PUSH SP
  PUSH BP
  PUSH SI
  PUSH DI
  
  ; Abrir el archivo que se encuentra en la direccion 
  ; a la que esta apuntando DS:DX
  call    abrir_lectura           
  ; Error? Desplegar mensaje de error y salir
  jc      FileErr
  ; Colocar el manejador del archivo en BX
  mov     bx,ax
  ; Leer el encabezado de 54 bytes que contiene la 
  ; informacion del archivo BMP
  call    ReadHeader
  ; No es un archivo BMP valido? Desplegar mensaje de error
  ; y salir
  jc      InvalidBMP
  ; Leer la paleta del BMP y colocarla en el buffer
  call    ReadPal
  push    es
  ; Cargar la imagen y desplegarla
  call    LoadBMP
  pop     es

  ; Cerrar el archivo
  call    cerrar_archivo
  jmp     ProcDone

  FileErr:
  mov     ah,9
  mov     dx,offset msgFileErr
  int     21h
  jmp     ProcDone

  InvalidBMP:
  mov     ah,9
  mov     dx,offset msgInvBMP
  int     21h

  ProcDone:
  ; Restaurar los registros
  POP DI
  POP SI
  POP BP
  POP SP
  POP BX
  POP DX
  POP CX
  POP AX
  
  ret
mostrar_bmp endp

; =========================================================
InitVid proc
  ; Este procedimiento inicializa el modo de video y hace
  ; que el registro ES apunte a la memoria de video.
  mov     ax,13h
  ; Configurar el modo de video para: 320x200x256.
  int     10h
  mov ax, 0A000h
  ; ES = A000h (apunta al segmento de memoria de video)
  mov es, ax
  ret
InitVid endp


; ============= Abrir archivo en modo de lectura ==========
; Parametros
; DS:DX: direccion del nombre del archivo
; Retorna
; AX: si puede abrirlo contiene el manejador del archivo
abrir_lectura proc
  ; INT 21h / AH= 3Dh - abrir un archivo existente.
  ;   AL = Servicios:
  ;     mov al, 0   ; leer
  ;     mov al, 1   ; escribir
  ;     mov al, 2   ; leer/escribir 
  ;   DS:DX -> Nombre del archivo con formato ASCIZ.
  ;   Retorna:
  ;     CF Carry Flag en cero (operacion exitosa)
  ;     con AX = manejador de archivo.
  ;     CF Carry Flag en 1 (operacion fallida)
  ;     con AX = codigo de error.
  ;  nota 1: el apuntador apunta al inicio del archivo.
  ;  nota 2: el archivo debe existir.
  MOV ax,3D00h
  INT 21h
  ret
abrir_lectura endp

cerrar_archivo proc
  MOV ah,3eh     ;Peticion para salir
  INT 21h
  ret
cerrar_archivo endp


; =============== Leer Encabezado del BMP =================
; Este procedimiento revisa el encabezado del archivo para 
; asegurarse de que es un archivo tipo BMP valido
; y obtiene la informacion necesaria del mismo.
ReadHeader proc
  mov     ah,3fh
  mov     cx,54
  mov     dx,offset Header
  ; Lee encabezado y lo guarda en el buffer.
  int     21h
  ; Si no se leyeron los 54 bytes del encabezado, terminar.
  jc      RHdone

  ; Este procedimiento obtiene informacion importante del 
  ; encabezado del BMP y la coloca en las variables
  ; adecuadas
  ; AX = Desplazamiento de la direccion donde comienza
  ;      la imagen
  mov     ax,header[0Ah]
  ; Restar la longitud del encabezado de la imagen
  sub     ax,54
  ; Dividir el resultado entre 4
  shr ax,1
  shr ax,1
  ; Obtener el numero de colores del BMP
  ; (cada elemento de la paleta tiene 4 bytes de longitud)
  mov     PalSize,ax
  ; Guardar el ancho del BMP en BMPWidth
  mov     ax,header[12h]
  mov     BMPWidth,ax             
  ; Guardar la altura del BMP en BMPHeight
  mov     ax,header[16h]    
  mov     BMPHeight,ax 

  RHdone:
  ret
ReadHeader endp


; ================ Leer la paleta de video ================
ReadPal proc
  mov     ah,3fh
  ; Numero de colores de la paleta en CX
  mov     cx,PalSize
  ; Multiplicar este numero por 4 para obtener tamanio de 
  ; la paleta en bytes
  shl     cx,1
  shl     cx,1
  mov     dx,offset palBuff
  ; Poner la paleta en el buffer
  int     21h

  ; Este procedimiento recorre el buffer de la paleta y 
  ; envia informacion de la paleta a los registros de 
  ; video. Se envia un byte a traves del puerto 3C8h que 
  ; contiene el primer indice a modificar de la paleta  
  ; de colores. Despues, se envia informacion acerca de 
  ; los colores (RGB) a traves del puerto 3C9h
  
  ; SI apunta al buffer que contiene a la paleta.
  mov     si,offset palBuff
  ; Numero de colores a enviar en CX
  mov     cx,PalSize
  mov     dx,3c8h
  ; Comenzar en el color 0
  mov     al,0
  out     dx,al
  ; DX = 3C9h
  inc     dx
  sndLoop:
  ; Nota: los colores en un archivo BMP se guardan como
  ; BGR y no como RGB

  ; Obtener el valor para el rojo
  mov     al,[si+2]
  ; El maximo es 255, pero el modo de video solamente
  ; permite valores hasta 63, por lo tanto dividimos 
  ; entre 4 para obtener un valor valido
  shr     al,1
  shr     al,1
  ; Mandar el valor del rojo por el puerto 3C9h
  out     dx,al
  ; Obtener el valor para el verde
  mov     al,[si+1]
  shr     al,1
  shr     al,1
  ; Mandar el valor del verde por el puerto
  out     dx,al
  ; Obtener el valor para el azul
  mov     al,[si]
  shr     al,1
  shr     al,1
  ; Enviarlo por el puerto
  out     dx,al

  ; Apuntar al siguiente color
  ; (Hay un caracter nulo al final de cada color)
  add     si,4
  loop    sndLoop
ret
ReadPal endp


; ====================== Cargar BMP =======================
LoadBMP proc
  ; Las imagenes BMP se guardan de cabeza. Este 
  ; procedimiento lee la imagen linea por linea, y la
  ; despliega comenzando con la linea inferior hasta la 
  ; superior. La esquina superior izquierda de la imagen 
  ; se pinta en la esquina superior de la pantalla.

  ; La memoria de video es un arreglo bidimensional de 
  ; bytes que se pueden manipular individualmente. Cada 
  ; byte representa un pixel en la pantalla y contiene 
  ; el color del pixel que se va a pintar en esa posicion

  ; Numero de lineas a desplegar
  mov     cx,BMPHeight
  ShowLoop:
  push    cx
  ; Hacer una copia de CX en DI
  mov     di,cx
  ; Multiplicar CX por 64
  shl     cx,1
  shl     cx,1
  shl     cx,1
  shl     cx,1
  shl     cx,1
  shl     cx,1
  ; Multiplicar DI por 256
  shl     di,1
  shl     di,1
  shl     di,1
  shl     di,1
  shl     di,1
  shl     di,1
  shl     di,1
  shl     di,1
  ; Todo esto es equivalente a DI = CX * 320
  ; DI apunta al primer pixel de la linea deseada en 
  ; la pantalla
  add     di,cx
  
  ; Copia del archivo bmp una linea de la imagen en un
  ; buffer temporal (SrcLine).
  mov     ah,3fh
  mov     cx,BMPWidth
  mov     dx,offset ScrLine
  ; Colocar una linea de la imagen en el buffer
  int     21h

  ; Limpiar la bandera de direccion (direction flag) para
  ; usar movsb
  cld
  mov     cx,BMPWidth
  mov     si,offset ScrLine
  ; Instruccion movsb: 
  ; Mover el byte en la direccion DS:SI a la direccion 
  ; ES:DI. Despues de esto, los registros SI y DI se 
  ; incrementan o decrementan automaticamente de acuerdo
  ; con el valor que contiene la bandera de direccion
  ; (DF). Si la bandera DF esta puesta en 0, los 
  ; registros SI y DI se incrementan en 1. Si la bandera 
  ; DF esta puesta en 1, los registros SI y DI se
  ; decrementan en 1.
  
  ; Instruccion rep: repite cx times una operacion
  ; Copiar en la pantalla la linea que esta en el buffer
  rep     movsb

  pop     cx
  loop    ShowLoop
  ret
LoadBMP endp
end

martes, 10 de abril de 2012

Ensamblador 8086 Formato BMP

Hasta el momento hemos hablado del modo 13h, la memoria de gráficos y la paleta de colores. En este post veremos cómo está compuesto el formato BMP. En particular hablaremos del formato que contiene una paleta de colores, lo cual lo hace ideal para poder desplegarlo en el modo 13h.

El formato BMP con paleta de colores tiene 3 secciones principales:

  1. Encabezado fijo de 54 bytes: donde podemos encontrar información como el ancho y el alto de la imagen, y la dirección donde empieza la información de cada pixel de la imagen (sección 3).
  2. Paleta de colores: la cual puede variar en tamaño. Cada entrada tiene 4 bytes en el siguiente orden: azul (B), verde (G), rojo (R), byte reservado. Para calcular el número de elementos de la paleta tomamos la dirección donde inicia la sección 3 (información de cada pixel) y restamos los 54 bytes del encabezado fijo. Esto nos da el tamaño en bytes de la paleta de colores, simplemente dividimos este número entre cuatro y obtenemos el número de entradas o colores de la paleta.
  3. Información de cada pixel de la imagen.

Usaremos la siguiente imagen y la abriremos con un visualizador de datos en hexadecimal (GHex) para analizar algunos puntos importantes de su contenido.

Encabezado Fijo

Los valores numéricos están guardados en formato little endian, es decir el byte menos significativo está en la dirección menos significativa. Si tenemos el número 36 9A, lo leeremos 9A36.

  • 42 4D: son los valores ASCII de BM, los archivos en formato BMP empiezan con estos 2 bytes.
  • 00 00 9A 36: la imagen mide 39,478 bytes.
  • 00 00 04 36: donde empiezan los datos de cada pixel, es decir en el byte 1078 del archivo.
  • 00 00 00 28: hasta este punto van 14 bytes del encabezado y faltan 40 bytes más (28h), que dan los 54 bytes del encabezado fijo.
  • 00 00 00 F0: ancho de la imagen (240 pixeles).
  • 00 00 00 A0: alto de la imagen (160 pixeles).
  • 00 00 96 00: número de bytes de la imagen (38,400 = 240 * 160)

Si los datos de cada pixel inician en el byte 1078 y el encabezado fijo mide 54 bytes, entonces la paleta de colores mide 1024 bytes; es decir tiene 256 entradas.

Pixeles y Paleta de Colores

Ahora realizaremos un ejercicio para mostrar que leer los colores de la imagen es prácticamente equivalente a la explicación que vimos para el modo 13h.

  1. Tomamos la información de un pixel: el primer pixel inicia en 1078 (436h) y contiene 37h. Esta es la entrada de la paleta de donde tomaremos el color.



  2. La paleta de colores inicia en 36h, por lo que la entrada 37h esta en: 36h + (37h * 4) = 112h.



  3. Si ponemos el código de color en un editor de imágenes podemos ver que corresponde al color del fondo de la imagen.

El valor 37h aparece repetidamente al inicio del archivo ya que es el valor del fondo de la imagen. Ahora, busquemos el primer valor diferente y veamos su color.

  1. El primer valor encontrado es 71h.



  2. La entrada de la paleta que buscamos esta en: 36h + (71h * 4) = 1FAh.



  3. Nuestra intuición diría que el color debe ser rojo; sin embargo, podemos ver que el color es verde, y corresponde al cuadro de la parte inferior. Esto se debe a que en el formato BMP, las líneas se guardan de abajo hacia arriba con respecto a como se deben mostrar en la pantalla. Es decir, la primer línea de datos del archivo es la línea inferior de la imagen.

Muy bien, ya tenemos todos los elementos para poder desplegar una imagen BMP en ensamblador 8086 modo 13h, en el siguiente post veremos el código.

La siguiente página tiene información adicional acerca del modo 13h y del formato BMP.

domingo, 8 de abril de 2012

Ensamblador 8086 Modo 13h Paleta de Colores

En post anterior explicamos que para desplegar pixeles en la pantalla usando el modo 13h tenemos que escribir a memoria y configurar la paleta de colores. Sin embargo, el ejemplo sólo mostró como escribir a la memoria de gráficos y usamos la paleta default de colores. En este post realizaremos un pequeño experimento para mostrar cómo configurar la paleta de colores.

El siguiente programa pinta 10 franjas horizontales de 20 pixeles de ancho cada una. Recordemos que hay 64,000 pixeles en la pantalla (320x200), por lo que cada franja de 20 pixeles de ancho ocupa un 10% de la pantalla es decir 6,400 pixeles. La primera parte del programa llena el área de memoria de gráficos (que inicia en A000:0000) con 6,400 ceros, seguidos de 6,400 unos, seguidos de 6,400 dos, y así sucesivamente hasta nueve.

Los colores que vemos en la pantalla son los colores de las primeras diez entradas de la paleta default de colores.

Después de presionar una tecla, el programa configura en tonos de azules las primeras diez entradas de la paleta de colores. Podemos observar los resultados en la siguiente imagen. Es importante recalcar que NO modificamos el área de memoria de gráficos, la cual sigue teniendo diez conjuntos de 6,400 pixeles numerados del cero al nueve.

Por último, volvemos a modificar las primeras diez entradas de la paleta de colores pero esta vez con tonos de verde.

.model small
.stack 128
.data

.code
.startup
main proc
  ; INT 10h / AH = 0 - configurar modo de video.
  ; AL = modo de video deseado.
  ;     00h - modo texto. 40x25. 16 colores. 8 paginas.
  ;     03h - modo texto. 80x25. 16 colores. 8 paginas.
  ;     13h - modo grafico. 40x25. 256 colores. 
  ;           320x200 pixeles. 1 pagina. 
  mov ax,0013h
  int 10h
  mov ax, 0A000h
  mov ds, ax  ; DS = A000h (memoria de graficos).

  ; ============== Franjas horizontales ===================
  ; Pintar 10 franjas horizontales de 20 pixeles de ancho
  ; cada una. La primer franja usara el indice 0 de la 
  ; paleta de colores, y asi sucesivamente. 
  ; Es decir, tenemos que pintar toda la pantalla 64,000
  ; pixeles, pero cada 6,400 pixeles cambiamos el color.
  mov cx,0FA00h ; todos los pixeles de la pantalla
  xor dx,dx ; color para cada franja
  xor bx,bx ; contador de pixeles por franja
  xor di,di

  ciclo_1:
  mov [di], dx ; poner color en A000:DI
  inc di
  inc bx
  cmp bx,6400
  jne sig_pix1
            ; nueva franja
  xor bx,bx ; resetear contador de pixeles por franja
  inc dx    ; cambiar color
  sig_pix1:
  loop ciclo_1

  ; esperar por tecla
  mov ah,10h
  int 16h
  
  ; ======= Cambiar paleta de colores a tonos de azul =====
  mov dx,3c8h ; Empezamos a modificar la paleta de colores
  mov al,0    ; desde la entrada 0
  out dx,al
  inc dx      ; DX = 3C9h.
  mov cx,10

  ciclo_2:   ; Modificar 10 entradas de la paleta
  mov al,0   ; Rojo.
  out dx,al
  mov al,0   ; Verde
  out dx,al
  mov al,6   ; Azul
  mul cl
  out dx,al
  loop ciclo_2

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; ======= Cambiar paleta de colores a tonos de verde =====
  mov dx,3c8h ; Empezamos a modificar la paleta de colores
  mov al,0    ; desde la entrada 0
  out dx,al
  inc dx      ; DX = 3C9h.
  mov cx,10

  ciclo_3:   ; Modificar 10 entradas de la paleta
  mov al,0   ; Rojo.
  out dx,al
  mov al,6   ; Verde
  mul cl
  out dx,al
  mov al,0   ; Azul
  out dx,al
  loop ciclo_3

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; regresar a modo texto
  mov ax,0003h
  int 10h
  
  ; finalizar el programa
  mov ax,4c00h
  int 21h
  ret
main endp
end

jueves, 5 de abril de 2012

Ensamblador 8086 Modo 13h Memoria para Gráficos

El programa del post anterior pinta pixeles usando la interrupción 10h servicio 0Ch; podríamos pensar que estamos mandando la instrucción de pintar un pixel directamente a la tarjeta de video. Sin embargo, lo que realmente estamos haciendo es escribir al área de memoria de gráficos que inicia en A000:0000.

Esta área consta de 320x200 bytes (64,000 bytes) y cada byte corresponde a un pixel. Para cada byte del área de memoria de gráficos, la tarjeta de video lee el byte y usa ese byte como índice en la paleta de colores para obtener el color del pixel.

En la pantalla, el pixel superior-izquierdo (fila 0, columna 0) corresponde al byte en la dirección A000:0000, el pixel superior-derecho (fila 0, columna 319) corresponde a A000:013F, el primer pixel de la segunda fila (fila 1, columna 0) está mapeado a la dirección A000:0140. En general, el pixel en la fila i, columna j corresponde a A000:(140h*i+j).

La siguiente imagen muestra cómo interactuamos con el área de memoria y la paleta de colores; la tarjeta de video usa esta información para desplegar el contenido de la pantalla.

El siguiente programa muestra la misma funcionalidad del programa del post anterior, pero esta vez escribiendo directamente al área de memoria de gráficos.

.model small
.stack 128
.data

.code
.startup
main proc
  ; INT 10h / AH = 0 - configurar modo de video.
  ; AL = modo de video deseado.
  ;     00h - modo texto. 40x25. 16 colores. 8 paginas.
  ;     03h - modo texto. 80x25. 16 colores. 8 paginas.
  ;     13h - modo grafico. 40x25. 256 colores. 
  ;           320x200 pixeles. 1 pagina. 
  mov ax,0013h
  int 10h
  mov ax, 0A000h
  mov ds, ax  ; DS = A000h (memoria de graficos).

  ; ============== Lineas verticales ======================
  ; Queremos pintar 256 colummas, cada una con un alto de 
  ; 200 pixeles. Podemos ejecutar 51,200 ciclos.
  ; Como la memoria de graficos es lineal, es mejor pintar
  ; una fila a la vez, cada fila tiene 320 columnas, pero
  ; solo pintamos 256. Al llegar a la columna 256 saltamos
  ; a la siguiente fila sumando 320-256 = 64
  ; Cada pixel cambia de color para dar el efecto de lineas
  ; verticales
  mov cx,0C800h ; # de pixeles
  xor dx,dx     ; contador de columnas y color
  xor di,di

  ciclo_1:
  mov [di], dx ; poner color en A000:DI
  inc di
  inc dx
  cmp dx,256
  jne sig_pix1

  add di,0040h ; saltar al inicio de siguiente fila
  xor dx,dx    ; reiniciar columnas y color
  sig_pix1:
  loop ciclo_1

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; ============== Lineas horizontales ======================
  ; Queremos pintar 200 filas, cada una con un largo de 
  ; 320 pixeles. Podemos ejecutar 64,000 ciclos.
  ; Despues de pintar los 320 pixeles correspondientes a
  ; una fila, cambiamos el color para pintar la siguiente
  ; fila y dar el efecto de lineas horizontales.
  mov cx,0FA00h ; todos los pixeles de la pantalla
  xor dx,dx ; color para cada fila
  xor bx,bx ; contador de columnas
  xor di,di

  ciclo_2:
  mov [di], dx ; poner color en A000:DI
  inc di
  inc bx
  cmp bx,320
  jne sig_pix2
            ; nueva fila
  xor bx,bx ; resetear contador de columnas
  inc dx    ; cambiar color
  sig_pix2:
  loop ciclo_2

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; regresar a modo texto
  mov ax,0003h
  int 10h
  
  ; finalizar el programa
  mov ax,4c00h
  int 21h
  ret
main endp
end

martes, 3 de abril de 2012

Ensamblador 8086 Modo Gráfico 13h

El modo gráfico 13h nos permite manejar la pantalla como una matriz de 320 píxeles de ancho por 200 píxeles de alto. Cada píxel puede tomar uno de 256 colores, estos colores están definidos en una paleta de colores la cual podemos configurar.

El siguiente programa despliega primero líneas verticales, espera por una tecla, despliega líneas horizontales y espera por otra tecla. Los colores mostrados son los de la paleta default de colores. Usamos la interrupción 10h, servicio 0Ch para modificar los píxeles; el parámetro AL más que especificar directamente el color, indica la entrada de la paleta de colores que se debe usar para el píxel ubicado en la fila DX y la columna CX.

En el siguiente post veremos con más detalle la paleta de colores y cómo podemos pintar en pantalla mediante escrituras a memoria en lugar de usar la interrupción 10h.

La siguiente entrada en Wikipedia muestra la paleta default de colores que corresponde a los colores mostrados por el programa.

.model small
.stack 128
.data

.code
.startup
main proc
  ; INT 10h / AH = 0 - configurar modo de video.
  ; AL = modo de video deseado.
  ;     00h - modo texto. 40x25. 16 colores. 8 paginas.
  ;     03h - modo texto. 80x25. 16 colores. 8 paginas.
  ;     13h - modo grafico. 40x25. 256 colores. 
  ;           320x200 pixeles. 1 pagina. 
  mov ax,0013h
  int 10h

  ; ============== Lineas verticales ======================
  ; INT 10h / AH = 0Ch - cambiar color de un pixel.
  ;   AL = color
  ;   CX = columna
  ;   DX = fila

  ; for CX=0 to 256
  ;   for DX=0 to 200
  ;     Poner pixel (DX,CX) con color CX
  mov cx,0000h
  mov dx,0000h

  ciclo_1:
  mov ah,0ch
  mov al,cl
  int 10h
  inc dx
  cmp dx,200
  jne ciclo_1

  mov dx,0000h
  inc cx
  cmp cx,256
  jne ciclo_1

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; ============== Lineas horizontales ======================
  ; INT 10h / AH = 0Ch - cambiar color de un pixel.
  ;   AL = color
  ;   CX = columna
  ;   DX = fila

  ; for DX=0 to 200
  ;   for CX=0 to 320
  ;     Poner pixel (DX,CX) con color DX
  mov cx,0000h
  mov dx,0000h

  ciclo_2:
  mov ah,0ch
  mov al,dl
  int 10h
  inc cx
  cmp cx,320
  jne ciclo_2
  
  mov cx,0000h
  inc dx
  cmp dx,200
  jne ciclo_2

  ; esperar por tecla
  mov ah,10h
  int 16h

  ; regresar a modo texto
  mov ax,0003h
  int 10h
  
  ; finalizar el programa
  mov ax,4c00h
  int 21h
  ret
main endp
end

domingo, 1 de abril de 2012

Ensamblador 8086 itoa

Una vez entendiendo la función atoi del post anterior, decidí implementar la función que realiza la operación inversa. Ahora la entrada es un valor entero y lo debemos transformar en cadena. El pseudo-código se muestra a continuación:

1 temp = 0
2 indice = 0
3 while entero > 0
4   residuo = entero modulo 10
5   entero = entero div 10
6   cadena[indice] = residuo + 30h
7   indice = indice + 1
8 end while

La idea es ir extrayendo los dígitos (empezando por el menos significativo) e ir guardándolos en la cadena, noten que sumamos 30h para guardar el valor ascii que representa el dígito. Sin embargo, esta implementación genera una cadena al revés, es decir, si entero inicia con valor de 1234, la cadena final contendrá “4321”. Por lo que es necesario invertir esta cadena.

Pero es posible evitar invertir la cadena al final, para ello usaremos el stack (una estructura lifo – last in first out).

01 temp = 0
02 indice = 0
03 ndigitos = 0
04 while entero > 0
05   temp = entero modulo 10
06   entero = entero div 10
07   push temp
08   ndigitos = ndigitos + 1
09 end while
10 while ndigitos > 0
11   pop temp
12   cadena[indice] = temp + 30h
13   ndigitos = ndigitos – 1
14   indice = indice + 1
15 end while

El primer ciclo (línea 04-09) extrae los dígitos y los guarda en el stack, al final del ciclo el dígito más significativo se encuentran arriba del stack. El segundo ciclo (línea 10-15) extrae los dígitos del stack y los guarda en la cadena final.

El siguiente programa convierte dos cadenas a enteros usando atoi, los suma y convierte el resultado a cadena usando itoa para poder desplegarlo.

.model SMALL
.STACK 128
.DATA
  cadena1 db '411$'
  cadena2 db '144$'
  cadena3 db 6 DUP(?)
  op1 dw ?
  op2 dw ?

.CODE
  .STARTUP
  mov ax,@data
  mov ds,ax

main proc
  ; SI parametro
  mov si, offset cadena1
  call atoi
  mov op1,bx
   
  ; SI parametro
  mov si, offset cadena2
  call atoi
  mov op2,bx
   
  ; sumar
  mov ax, op1
  add ax, op2
  mov bx, offset cadena3
  call itoa
   
  mov dx, offset cadena3
  call desplegar
   
  ; INT 21h / AH=4Ch retorna el control al sistema operativo
  ;                  termina el programa
  salir:
  mov ax,4c00h
  int 21h
  ret
main endp

; ============ Proc: Desplegar mensaje ====================
; Parametros
; dx: offset de cadena terminada por $ con respecto a DS
desplegar proc
   ; INT 21h / AH=9 - despliega cadena apuntada por DS:DX. 
   ; la cadena debe estar terminada por '$'.
   mov ah,09h
   ;mov dx, offset cad
   int 21h
   ret
desplegar endp

; ========= Convertir cadena a numero =====================
; Parametros
; si: offset inicial de la cadena con respecto a DS
; Retorna
; bx: valor
atoi proc
  xor bx,bx   ;BX = 0

  atoi_1:
  lodsb       ;carga byte apuntado por SI en AL
              ;e incrementa si
  cmp al,'0'  ;es numero ascii? [0-9]
  jb noascii  ;no, salir
  cmp al,'9'
  ja noascii  ;no, salir

  sub al,30h  ;ascii '0'=30h, ascii '1'=31h...etc.
  cbw         ;byte a word
  push ax
  mov ax,bx   ;BX tendra el valor final
  mov cx,10
  mul cx      ;AX=AX*10
  mov bx,ax
  pop ax
  add bx,ax
  jmp atoi_1  ;seguir mientras SI apunte a un numero ascii
  noascii:
  ret         ;BX tiene el valor final
atoi endp

; =============== Convertir numero a cadena ===============
; Parametros
; ax: valor
; bx: donde guardar la cadena final
; Retorna
; cadena
itoa proc
  xor cx,cx  ;CX = 0

  itoa_1:
  cmp ax,0   ; El ciclo itoa_1 extrae los digitos del
  je itoa_2  ; menos al mas significativo de AX y los
             ; guarda en el stack. Al finalizar el 
  xor dx,dx  ; ciclo el digito mas significativo esta
  push bx    ; arriba del stack.
  mov bx,10  ; CX contiene el numero de digitos
  div bx
  pop bx
  push dx
  inc cx
  jmp itoa_1

  itoa_2:
  cmp cx,0    ; Esta seccion maneja el caso cuando
  ja itoa_3   ; el numero a convertir (AX) es 0.
  mov ax,'0'  ; En este caso, el ciclo anterior
  mov [bx],ax ; no guarda valores en el stack y
  inc bx      ; CX tiene el valor 0
  jmp itoa_4

  itoa_3:
  pop ax      ; Extraemos los numero del stack
  add ax,30h  ; lo pasamos a su valor ascii
  mov [bx],ax ; lo guardamos en la cadena final
  inc bx
  loop itoa_3

  itoa_4:
  mov ax,'$'  ; terminar cadena con '$' para 
  mov [bx],ax ; imprimirla con la INT21h/AH=9
  ret
itoa endp

end

lunes, 26 de marzo de 2012

Ensamblador 8086 atoi

Para implementar la tarea donde tuvimos que leer del usuario dos números enteros, realizar operaciones con ellos y desplegar el resultado, fue necesario implementar dos rutinas muy interesantes: itoa (para convertir un entero a cadena) y atoi (para convertir una cadena a entero). Los nombres de estas dos rutinas los tomé de las funciones en C que ayudan a realizar estás tareas, entero a cadena (integer to ascii por sus siglas en inglés) y cadena a entero (ascii to integer). En este post analizaremos la implementación de atoi.

El objetivo de esta función es tomar una cadena y convertirla a su valor entero. Por ejemplo la cadena “120” debe generar el entero 120. El pseudo-código para esta función es el siguiente:

1 entero = 0
2 temp = 0
3 indice = 0
4 while cadena[indice] es un digito
5   temp = convertir cadena[indice] a digito
6   entero = entero * 10
7   entero = entero + temp
8   indice = indice + 1
9 end while

Recuerden que en ascii los caracteres '0' al '9' se encuentran de manera consecutiva, de hecho el valor númerico de '0' es 30h y el de '9' es 39h. Por lo que el paso 4 del código anterior se puede implementar fácilmente verificando si cadena[indice] se encuentra entre '0' y '9'. De la misma forma, en el paso 5, sólo basta con restar el valor de '0' a cadena[indice] para obtener el valor del dígito en esa posición. El siguiente post muestra el código en ensamblador para esta función (sólo tuve que cambiar los registros de 32 a 16 bits, por ejemplo de eax a ax).

La siguiente función muestra la versión de 16 bits de atoi, la cadena a convertir debe iniciar en DS:SI y el valor final lo regresa en BX.

; ========= Convertir cadena a numero ===========
; Parametros
; si: offset inicial de la cadena con respecto a DS
; Retorna
; bx: valor
atoi proc
  xor bx,bx   ;BX = 0

atoi_1:
  lodsb       ;carga byte apuntado por SI en AL
              ;e incrementa si
  cmp al,'0'  ;es numero ascii? [0-9]
  jb noascii  ;no, salir
  cmp al,'9'
  ja noascii  ;no, salir

  sub al,30h  ;ascii '0'=30h, ascii '1'=31h...etc.
  cbw         ;byte a word
  push ax
  mov ax,bx   ;BX tendra el valor final
  mov cx,10
  mul cx      ;AX=AX*10
  mov bx,ax
  pop ax
  add bx,ax
  jmp atoi_1  ;seguir mientras SI apunte a un numero ascii
  noascii:
  ret         ;BX tiene el valor final
atoi endp

lunes, 19 de marzo de 2012

Ensamblador 8086 en Ubuntu

En la clase de lenguaje ensamblador hemos estado usando un usb para iniciar en modo DOS y poder ejecutar nuestros programas de 16 bits. En este post, mostraré cómo realizar pruebas de nuestros programas en Ubuntu. Sin embargo, es importante probar nuestros programas al final en el modo DOS nativo en caso de que haya instrucciones no compatibles o que se comporten diferente.

  1. Instalar DOSBox que emula el ambiente DOS.
    sudo apt-get update
    sudo apt-get install dosbox
  2. Copiar los archivos de nuestro usb masm/bin en una carpeta, por ejemplo: /media/common/prgrmmng/8086. Para su conveniencia el siguiente zip contiene los archivos.
  3. Abrir DOSBox (Applications > Games > DOSBox Emulator), montar la carpeta del paso anterior en la unidad c, y cambiar a la unidad c.
  4. Ya estamos listos para empezar a programar. La siguiente imagen muestra la primer tarea corriendo en DOSBox.

El siguiente post también tiene información para bajar DOSBox para windows.

Si alguien usa DOSBox, por favor compartan si encuentran alguna incompatibilidad o problema con los programas.

martes, 24 de enero de 2012

SQLite

¿Qué es SQLite?

SQLite es una base de datos SQL relacional, ligera, fácil de utilizar, muy confiable y libre. Es muy sencilla de utilizar porque basta con descargar las librerías de su sitio web, desenziparlas y listo, ya es posible construir una base de datos desde una terminal de Windows y haciendo uso de los comandos de SQL apropiados para ello.

Esta es una de las ventajas más grandes de esta base de datos, pues es posible comenzar a crear una aplicación sin perder tiempo en la configuración de un servidor de bases de datos en nuestra computadora. Trabajar con SQLite es como instalar una aplicación y poder utilizarla inmediatamente.

Otra de las características distintivas de SQLite es su portabilidad. SQLite trabaja de manera similar a Java: utiliza una máquina virtual que procesa bytecode generado durante el proceso de compilación. Esta modalidad, perimite que SQLite sea independiente del sistema operativo dentro del que está trabajando.

¿Quién utiliza SQLite?

SQLite es usado en varios productos importantes. Entre ellos podemos mencionar el sistema operativo Mac OS X, el navegador Safari, la base de datos para los contactos en el iPhone, Firefox, Thunderbird, el sistema operativo Android para dispositivos móviles, así como en algunas aplicaciones para dichos dispositivos.

¿Cómo utilizar SQLite?

Primero deberás descargar los archivos necesarios de la página de SQLite: http://sqlite.org/. En este sitio web, podrás encontrar tanto las versiones precompiladas como el código fuente si es que tú mismo quieres compilarlo. En este post, explicaremos brevemente cómo obtener los archivos precompilados para usuarios de Windows (aunque también hay instrucciones para los usuarios de otros sistemas operativos). Primero hay que dirigirse al sitio de SQLite y dar clic en el enlace "Download" (descargar). Esto te llevará a la página de descargas.

Ubica la sección donde aparecen los archivos precompilados para Windows (Precompiled Binaries for Windows) y descarga el archivo con extensión .zip. Puedes agregar el ejecutable al Path de Windows para poder utilizarlo mediante la línea de comandos desde cualquier directorio o puedes dejar dicho ejecutable en el fólder de tu preferencia, abrir una consola o línea de comandos y ubicarte en la dirección de ese fólder.

Una vez que te encuentras en la dirección correcta en la línea de comandos, teclea "sqlite3" seguido del nombre que quieres dar a la base de datos con la que deseas trabajar utilizando la extensión .db, como se muestra a continuación:

Si ya existe una base de datos con ese nombre, SQLite abrirá esta base de datos para que continúes trabajando con ella. Si no es así, se creará una base de datos nueva con este nombre.

Ahora ya puedes comenzar a construir tus tablas. Siguiendo con el ejemplo, supongamos que queremos crear una tabla con los IDs, nombres y capitales de varios países. Para hacer esto, ocuparemos las sentencias de SQL "CREATE TABLE" e "INSERT INTO... VALUES(...)":

No olvides terminar cada enunciado de SQL con un punto y coma (";").

Hay un par de detalles en esta imagen que me gustaría comentar: primero, observa que la columna IDPais es la llave primaria, es de tipo entero y además le coloqué un atributo que incrementa automáticamente el valor de este campo. De esta manera, al insertar los valores correspondientes dentro de la tabla, puedo olvidarme de insertar el valor de la llave primaria y dejar que SQLite los genere automáticamente. El segundo detalle es el comando ".table", con él puedes obtener los nombres de las tablas que has creado en esta base de datos.

Podemos verificar toda la información introducida hasta el momento realizando la siguiente consulta:

Observa que el formato por defecto en el que SQLite despliega la información de la tabla es un tanto ilegible. Puedes cambiar este formato de la siguiente manera:

Como se muestra en la imagen, es posible establecer el ancho para cada columna. Esto es muy útil cuando se tienen valores muy grandes o muy pequeños en alguna columna.

En este post introducimos el concepto de SQLite, sus ventajas, sus usuarios más destacados y la manera en la que puedes comenzar a trabajar con él. Existen muchas otras herramientas que puedes utilizar y muchos otros comandos de configuración que también pueden serte útiles dependiendo de la aplicación que estés desarrollando.

Un muy buen tutorial de SQLite lo puedes encontrar en la siguiente liga:

http://zetcode.com/databases/sqlitetutorial/

Por supuesto, también debes tomar en cuenta la documentación de SQLite:

http://sqlite.org/docs.html

Finalmente, si quieres trabajar con SQLite y Java, puedes encontrar el código y los pasos necesarios para conectarte a SQLite mediante Netbeans en la siguiente dirección:

http://stackoverflow.com/questions/41233/java-and-sqlite