Habi Hablóg
Declaro:
XML válidoXHTML válido800x600 +
RSS válidoCSS válidoNavegador digno
  Blog   Archivo   Contacto   Administración  

Acerca de

Matemático, informático, aficionado a la electrónica, friki... y otras cosas que no vienen a cuento ni pasan los filtros de palabras.

¿Queríais un blog? Ahí va.

Red antisocial

¡Me van a volver loca! 2.0
La Fragata Portuguesa

Z
¡Me van a volver loca!

Últimos posts

El expediente X que nadie pidió
eNigma
La cuadratura del píxel
Portando desde Spectrum
Inexorable

Últimos comentarios

Habi
NoSupoResolverLaFuncion
Edu
Habi
EnriqueGG

Calendario

No hay fechas.

Categorías

Chorradas
Paranoias
Posts lúcidos
Tecnoesoterismo
Yuyus

Cenas de Abj

Abj debe 7 cenas.

Frase célebre

Zarith: ya está, se acabó 
 
todo tiene un límite, habi 
 
HabiSoft: incluso 1/x cuando x->0 ?

Aclarando cosas

Habi - 22/08/2013 23:07:39 - Tecnoesoterismo

Si alguien se pone a intentar emular un Z80 lo más fielmente posible, al final se encontrará emulando el registro interno MemPTR (también llamado WZ).

Es uno de esos registros internos de los procesadores para efectuar sus operaciones intermedias; el problema es que con cierta instrucción pueden leerse dos bits del mismo en dos flags indocumentados, de ahí que sea "necesaria" su implementación.

Se recomienda una lectura del tema en: [link].

Y este es mi punto de partida, ese documento y su ambigüedad en ciertas cosas. Por ejemplo:

1) En la lista aparecen Ret y RetI, pero no aparece RetN. ¿Acaso su microcodificación es tan diferente (la asignación del Iff correspondiente bien puede hacerse en paralelo, el resto es idéntico)? Resulta extraño creer que RetN no modifique MemPTR.

2) Aunque el documento habla de interrupciones no aclara si es sólo para las mascarables. Internamente las NMIs son bastante bizarras, pues hay un ciclo pseudo fetch (con lectura incorporada) antes de su tratamiento. Sin embargo un salto es un salto, e internamente sólo se pueden saltar a registros, luego debiera estar incluída. Otra cosa a verificar.

3) El documento menciona las instrucciones In A, (C) y Out (C), A. Pero hay un bloque entero de instrucciones In r, (C) y Out (C), r para r en [A, B, C, D, E, H, L, x]. El valor que he marcado con x no está documentado y correspondería como es lógico a (HL), pero dada la estructura del procesador no se puede acceder a memoria. El resultado es que en lectura no se asigna a ningún registro lo leído, y en escritura se pone en el bus externo el contenido del bus interno del procesador ($00 en NMOS, $FF en CMOS para un Z80 estándar).

Desgraciadamente, ningún programa de test mira otras instrucciones que no sean las que aparecen en esa lista (y el caso de la NMI es un tanto rebuscado, lo admito).

La lógica nos dice que debiera haber modificación de MemPTR en los 3 casos aunque las instrucciones no aparezcan en la lista. Y como veremos, es lo correcto.

Hoy que he tenido un rato he decidido despejar todas las dudas; me basta con construir un contraejemplo en cada caso, así que vayamos por partes:

 

Caso 1

Consideremos el siguiente programa rápido (para Spectrum):

	Org	$8000
	Di
	Ld	Hl, Siguiente
	Push	Hl		;Retorno para RetN
	Ld	A, ($FFFE)	;MemPtr=$FFFF
	RetN			;MemPTR = $80xx; bits 13 y 11 a 0
				;HIPÓTESIS
	Siguiente:
	Bit	0, (Hl)	;MemPtr a flags
	Push	Af
	Pop	Bc		;En C
	Ei
	Ld	A, %00101000
	And	C
	Ld	C, A
	Ld	B, 0
	Ret

Haciendo PRINT USR 32768 debiera escribirnos un valor: 40 si MemPTR no se modifica o 0 si lo hace. Como era de esperar, el resultado es 0, luego la lista tiene una errata en ese punto (debieran haber añadido RetN junto a Ret y RetI).

 

Caso 2

Comprobar la NMI es un tema peliagudo: tenemos que tener nuestro código en $66 (ROM por defecto en la mayoría de los ordenadores basados en Z80) y ser capaces de generar una NMI cuando queramos; a ser posible sin hardware externo.

Afortunadamente, hay un ordenador en el que eso es posible: ¡el PCW!

El controlador de disco NEC 765 genera interrupciones; normalmente no se tienen en cuenta en la arquitectura (p.ej. Spectrum +3, Amstrad CPC) pero sí en el caso del PCW. Y aún es más: pueden ser dirigidas a ningún lado (deshabilitadas), a la interrupción enmascarable y a la no enmascarable.

El programa aquí es un tanto más complicado, pues rápidamente deshabilita interrupciones, relocaliza un trozo en la dirección $66 para atender a la NMI, reprograma el Gate Array, y apaga el motor del disco (no lo hará la BIOS pues tenemos deshabilitadas las interrupciones). Al deshabilitarse la rotación del motor un momento después (y por variación de la línea /READY) tenemos nuestra bonita NMI. Simplemente la esperamos con un Halt (y las interrupciones deshabilitadas).

Al no poder imprimir fácilmente números, también hay que incorporar esas rutinas de soporte, restaurar las cosas como estaban, etc. Por todo lo anterior el programa es bastante más largo, así que por razones de longitud no lo listaré aquí.

La conclusión: se modifica MemPTR también. No lo considero una errata, si bien hubiese estado bien añadir una aclaración.

 

Caso 3

De nuevo un programa rapidito en Spectrum:

	Org	$8000
	Di
	Ld	Bc, $FFFE	;No pasa nada por escribir a ULA
	Ld	A, (0)		;MemPtr=1
	Out	(C), C		;MemPtr = $FFFF HIPÓTESIS
	Bit	0, (Hl)	;MemPtr a flags
	Push	Af
	Pop	Bc		;En C
	Ei
	Ld	A, %00101000
	And	C
	Ld	C, A
	Ld	B, 0
	Ret

En este caso haciendo de nuevo PRINT USR 32768 obtenemos un resultado, si bien aquí es al revés: 0 indica que MemPTR no se modifica y 40 que sí lo hizo. Y así sucede, y de nuevo una errata: se debiera cambiar en esas instrucciones la "A" por "r", donde r es un registro de 8 bits. Se cumple para todos los casos (los cambios en el programa son triviales).

 

Moraleja: verificar siempre antes las cosas uno mismo; me toca hacer un par de cambios por haberme fiado. sad

2


Nueva alfombrilla

Habi - 31/05/2013 11:38:07 - Posts lúcidos

1


Tarde no tan aburrida

Habi - 02/05/2013 22:21:37 - Yuyus

Secuencia:

 

2


Adios, Messenger

Habi - 10/04/2013 12:09:15 - Yuyus

En memoria del cerdo bailón.

(Botón derecho + Reproducir)

D.E.P.

1


Compresión noir

Habi - 27/01/2013 15:47:12 - Tecnoesoterismo

Supongamos que queremos reducir el tamaño de varias imágenes. Es un problema ampliamente tratado, así que vamos a particularizar:

  1. Esas imágenes tendrían a lo sumo 4 colores y transparencia. Podemos comprimir sobre los datos tal cual, o separar en 4 planos de transparencia y color correspondiente.
  2. El tamaño de las mismas sería "pequeño", 320x200 a lo sumo. Podría no compensar usar ciertas técnicas estándar.
  3. Queremos un sistema simple y relativamente rápido de descompresión en ensamblador y para un Z80. Eso elimina cosas como compresión aritmética.
  4. Tenemos restricciones de memoria, lo que limita el uso de métodos de diccionario.

Por todo ello, haremos pruebas combinadas con diferentes formas de compresión y empaquetado (Run-Length, Huffman (siempre canónico), LZ77, Deflate, diccionario rotatorio) y ciertas transformaciones (Haar, tiles, subdivisión, algunos predictores). Dejamos fuera LZ78, LZW, y diccionarios varios cuando su tamaño se dispare.

Hagamos otra hipótesis:

  1. Las imágenes tendrán estética tipo comic, sólo tendrán blanco y negro (y muy ocasionalmente algo de gris o rojo). Además, cualquier fondo parte de un lienzo en blanco.

Por ello es que nos compensa tratar los planos por separado, y comprimir cada uno con una técnica especializada para imágenes monocromas.

Así pues, podemos empezar a hacer pruebas para un fondo, es decir, imágenes transparente + negro. Además, eso nos da una cota superior: si el archivo comprimido ocupa Ancho*Alto/8 o más es obvio que hemos fracasado, pues es el tamaño que ocuparía empaquetando cada pixel en un bit (equivalente al caso trivial de Huffman con 2 valores, óptimo por tanto trabajando con bits completos).

Tras todas las pruebas mis conclusiones son:

Así que mis recomendaciones son:

Tipo fax (RLE y Huffman alternos).

Una forma muy usada es la compresión de los faxes: básicamente consisten en contar cuántos unos o ceros hay (run-length) y comprimir esas longitudes con códigos de Huffman (modificados). No es necesario indicar cuál es el valor que se repite pues al ser sólo dos se van alternando. Se usan árboles de Huffman distintos para las longitudes de cada valor.

En mi caso usaré árboles creados aposta para la imagen en vez de unos genéricos y no usaré códigos modificados pues no hay longitudes grandes al ser una imagen pequeña.

Debo decir que no funciona nada mal el algoritmo, es el segundo mejor de todas mis pruebas; como ejemplo:

Se comprime en 2987 bytes + diccionario aparte (las longitudes en bits de los códigos Huffman canónicos, para reconstruir los árboles / tablas). Los diccionarios van aparte por si se quieren unificar códigos en toda la aplicación, compartir, o lo que sea. Pongamos unos 40-60 bytes extras.

En cualquier caso, estamos hablando de unas 3K en una imagen cuyo límite estaba en 8K. Es decir, un 37,5% del original, que no está nada mal. Este algoritmo funciona de vicio en cualquier tipo de imágenes.

 

Subdivisión recursiva + Huffman.

Sin embargo, dadas mis restricciones de espacio necesito más compresión que el método anterior. Y dada la estética de mis imágenes haré otra hipótesis:

  1. Las imágenes serán claroscuros, es decir, con masas sólidas. Pero por joder, serán más pequeñas ya que irán en viñetas tipo cómic.

Cogemos una imagen, le ajustamos la exposición y gamma y empezamos a comprimir:

  ->  

Tenemos el límite en 128x96/8=1546 bytes. El método anterior lo deja en 499 + diccionarios. Pongamos 550 bytes en total.

Empezamos extendiendo las dimensiones para que la imagen entre en un cuadrado cuyo lado sea potencia de 2. Ahora construimos un árbol cuaternario donde las hojas sean los cuadrados de color a dibujar. Es decir, sólo codificamos el color, y si un nodo no es hoja entonces no se dibuja (es de soporte). Por tanto, cada nodo se puede codificar con 4 bits indicando si tiene o no hijos en cualquiera de las 4 direcciones.

Se generan 1700 nodos (1025 hojas), empaquetando tal cual son 850 bytes. Pero no vamos a empaquetar sino aplicar huffman.

Nos quedan 534 bytes, todo incluido. Pero se puede hacer mejor: reordenemos el árbol, que se emita en orden de recorrido por anchura en vez de profundidad; de esta forma todas las hojas del último nivel (a 0) estarán juntas, y por tanto se pueden quitar asumiendo que a partir de que terminen los datos siempre se da ese valor (quedando 1065 de 1700 nodos).

Y así se queda en 433 bytes, incluyendo diccionario, flags y dimensiones (sólo necesita el Log2 del lado del cuadrado). En total un 28,19% del total, nada mal para una imagen no especialmente buena, y dejando abierta la posibilidad de comprimirse aún más (hay muchas rachas de 0's y 15's antes de pasar por Huffman).

¿Me dará para lo que quiero? No lo sé, pero ha sido entretenido.

2


Sacando los colores

Habi - 19/12/2012 21:59:51 - Tecnoesoterismo

Desde que me leí la documentación del ASIC del PCW16 (Anne) y pude ver que en efecto saca colores y tiene sincronismos muy cercanos al estándar VGA quise hacerme con una de estas estupendas máquinas para hacer pruebas.

Esta es la placa de un PCW16 sin modificar:

Y esta es la mía ahora:

Hay que poner el conector, rehacer los pequeños DAC del rojo y azul (el del verde viene hecho), invertir sincronismo horizontal, ... Pero merece la pena.

En el monitor plano se puede apreciar una ligera interferencia; teniendo en cuenta que para probar he pasado de construirle el filtro de frecuencias a la salida (están puenteadas), que los sincronismos no son del todo iguales a los de la VGA y que justo encima tenemos el flyback del monitor a la derecha y la fuente de alimentación a la izquierda casi que esto es algo más que normal:

El monitor plano se ve realmente BLANCO y no amarillo, cosas del flash y el TFT.

Un detalle curioso: si el PCW16 tiene un monitor externo baja mucho la señal interna y apenas se ve la pantalla, y viceversa. Por supuesto, teniendo un control por software del mismo (Out &hF8, &h2F / 3F) no hay problema alguno:

Por último una prueba de color: arranco el CP/M, un Mallard BASIC, y escribo: “ ? Chr$(27);"30" ” para cambiar a "modo 0" (16 colores); ésta es la salida (no se aprecian mucho los colores en la foto pero ahí están, rojos, verdes y azules:

Y eso es todo por hoy. Para la próxima entrega ese puerto IDE... :9


Edito: ¡nuevas fotos de regalo!

2


I.D.E.A.

Habi - 22/11/2012 23:39:24 - Chorradas

( Imágenes Del Emulador Autista )


A los pocos milisegundos:

5 segundos después:

10 minutos más tarde:

Y ya; algún día emulará teclado y ratón.

2


Tríptico críptico (II)

Habi - 18/11/2012 22:22:22 - Yuyus

8


Hard

Habi - 21/09/2012 12:37:36 - Tecnoesoterismo

Sonda lógica. ^__^

Mis primeras placas encargadas. ^_______^


Moraleja:

 

4


Portando abadías

Habi - 05/09/2012 13:28:46 - Tecnoesoterismo

Como ya he contado por otro lado, mi idea era partir de la versión original, la de CPC 6128, e intentar adaptarla a PCW haciendo la menor cantidad posible de cambios.

Teniendo en cuenta que un PCW tiene un mejor sistema de paginación de memoria, que puedo derivar el sonido a la interfaz Dk’tronics (mismo integrado) y que tengo formas de hacer que la memoria de vídeo me entre en menos de 16KBs, me surgió la pregunta: ¿y si uso el propio binario y parcheo encima?

Así pues comienzo extrayendo los datos de las pistas del disco original, dividiéndolas en páginas y reordenando los bytes (pues carga de arriba abajo, curiosamente). Hacemos un archivo para cada página y podemos continuar.

Análisis:

Aunque existe un desensamblado (Vigasoco) no me resulta útil pues no es re-ensamblable y es un tanto incompleto; así que me hago el mío propio con el IDA.

Puede verse claramente que el juego es un prototipo, no una versión pulida. Por ejemplo, la página 4 entera es un debugger (decido eliminarla y aprovecharla para mi cargador y las rutinas "externas" que sean necesarias), hay un montón de rutinas de depuración anuladas, código redundante y algunas rutinas sin optimización alguna.

Supongo que le meterían prisa al autor, o bien no le dejaron terminarlo. En cualquier caso, juega a mi favor pues eso me da más espacio para meter mis parches.

Infraestructura:

Lo primero es montarnos un entorno de desarrollo adecuado. Utilizo UltraEdit con un resaltado de sintaxis para ensamblador de Z80 para la edición, y archivos de proceso por lotes para automatizar todo.

Para ello, creo una imagen de disco cruda (.img) con los datos introducidos tal cual de forma secuencial; corto en 3 partes (inicio, datos, fin) de tal forma que con un Copy /b podemos montar todo con los datos correspondientes y su .bat correspondiente. Y otro .bat para lanzar el emulador arrancado y con el disco metido.

El código se ensambla con Pasmo, empezamos con un Org en su dirección correspondiente, un IncBin para cargar el código original y a partir de ahí tranquilamente con Org podemos poner el parche correspondiente donde queramos, comprobar espacio libre con If $>..., etc.

Cargador:

Me decido a hacer un .ems como Ópera, además de ser más estándar en un PCW. Éste agrupa las páginas 3 (vídeo, en este caso pantalla de carga) y 4 (en vez del debugger tengo mi código de carga y rutinas de acceso al disco a nivel de ficheros que serán además aprovechadas para grabar partidas).

El cargador inicializa cosas como el vídeo, mueve la pantalla de presentación a la memoria de vídeo, carga cada página en su sitio y salta al código del juego. No tiene mucho más que contar.

Memoria:

El juego utiliza normalmente la configuración 0 (0-1-2-3) y ocasionalmente el resto de las páginas en el marco 1, los modos 4-7 (0-x-2-3) lo cual nos simplifica el tema. Basta localizar los:

Ld     Bc, $7FCx

Out    (C), C

Y cambiarlos por una secuencia que haga lo propio y ocupe lo mismo; es decir:

Ld     Bc, $8yF1

Out    (C), B

Donde y=1 para x=0, y x=y para el resto.

Además habilito en el cargador el modo "original" de paginado del PCW, pues me resulta útil usarlo en las rutinas del pergamino del final.

Interrupciones:

El siguiente problema son las interrupciones; el PCW tiene varias fuentes, y deben ser reconocidas cada una por separado, a su manera; no ocurre automáticamente. Afortunadamente, las de vídeo ocurren a la misma frecuencia.

En nuestro caso tenemos anuladas las de disco (como el CPC) y se nos reduce a intercalar un In A, ($F4) en el lugar apropiado. Y de nuevo en nuestro caso, en la primera llamada desde la rutina principal (tema sonido en $1060) vemos un Di redundante y un Reti que puede ser Ret (además ya hay un Reti en la rutina principal). Ahí ganamos esos dos bytes.

Sonido:

Otra cosa sencillita; basta con cambiar la rutina en $134E por:

Out    ($AA), A

Ld     A, C

Out    ($AB), A

Ret

Vídeo:

Lo primero es eliminar lo relacionado con la paleta; al estar centralizado, basta un Ret en $3F4C. Tenemos que usar dos píxeles por cada uno del modo 1 del CPC (misma memoria de vídeo y mismos gráficos), y los colores deben ser remapeados, pues las paletas usadas no están en orden por luminosidad:

CPC PCW
0 1
1 3 (blanco)
2 2
3 0 (negro)

El juego utiliza 256x192, pensando quizás en ser portado a Spectrum y MSX; de nuevo, algo que nos viene muy bien.

Como seguramente sabréis, el PCW es un tanto particular con el tema vídeo. Tiene un modo fijo 720x256 (PAL, en NTSC son 200) monocromo, y eso no se puede cambiar.

Sin embargo, podemos elegir dónde se encuentra cada fila en memoria gracias a una estructura, llamada roller-ram. Y además, podemos ubicar ésta donde queramos. Sólo hay una pega, y es que cuando se recorre la memoria de vídeo para construir la imagen el puntero de la misma se incrementa en 8 y no en 1, haciendo que pixeles adyacentes no tengan direcciones adyacentes (ahora, para modos de texto o mejor dicho tiles 8x8 alineadas, es genial).

Así que la ubicamos en el espacio del cargador, y hacemos que las 192 primeras líneas apunten a direcciones de la página 3 (igual que el CPC), el resto a una línea en el propio cargador entera a 0. Al arrancar detectamos PAL / NTSC y centramos verticalmente utilizando el puerto $F6.

Para ahorrar memoria de vídeo y que nos entre en una página utilizamos el truco de solapar las líneas:

Vamos a considerar filas de caracteres (8 píxeles) por el incremento astuto del que hablamos antes. Cada una de ellas tiene por tanto el mismo ancho que su tamaño en bytes (720).

Por tanto nos sobran 720-512=208 bytes; sin embargo, no podemos poner 104 por cada lado pues no es divisible entre 16; así que los divido como 96+112 (es importante que por la derecha quede igual o más que por la izquierda, porque vamos a solapar y el lado izquierdo siguiente será el derecho anterior).

De esta forma nos ocupa todo menos de 15KB, importante pues utilizo un par de trozos por el final para meter un par de tablas para optimizar y algo más importante: al final del bloque 3, el PCW mapea el teclado.

Por supuesto, dada la no-adyacencia de la memoria de vídeo tenemos que rehacer TODAS las rutinas gráficas del juego, las que generan tablas para ser usadas por éstas y algunas de las que les pasan parámetros a las anteriores. Por cierto, yo me pregunto: ¿qué se fumaron los ingenieros de Amstrad para decidir que el CPC tuviese entrelazados los bits de los píxeles en modos empaquetados?

Teclado:

Leer el teclado en un CPC es un infierno, hay que darse garbeos por el PPI, el PSG (a través del PPI, de nuevo), etc. En el PCW sin embargo tenemos siempre su estado en una dirección de memoria, un auténtico lujo. Sólo tengo que cambiar la rutina en $32BC por una que emule el trabajo del CPC y luego cambiar las comprobaciones de códigos de teclas donde correspondan; afortunadamente son teclados bastante parecidos.

De paso, le añado los cursores además de A-Z-K-L por comodidad.

Fin:

Por supuesto quedan parches variados: me salto el poner el modo de vídeo, hago mi propia copia de las rutinas del pergamino y su recuperación al final (para que ocupen menos, como siempre que se parchea vamos pillados de espacio), se monta una tercera tabla de transparencia (sprites) y otra con las direcciones de las filas de vídeo para acelerar el juego al final del bloque 3, etc.

¿Fin?:

Escribiendo este artículo, me he dado cuenta de que... ¡no hay versión para 9512 / 9512+! Modifico el cargador para hacerlo universal, separo el sector de arranque y añado las instrucciones para generar las dos imágenes. Arreglado (y subido a la web).

8


Reglas del 10:
10 últimos   10 después   10 antes   10 primeros