Organización de pantalla alternativa.... ¿mejor?

Todo sobre la creación, diseño y programación de nuevo software para
nuestro Spectrum

Moderador: Sir Cilve Sinclair

Responder
Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Organización de pantalla alternativa.... ¿mejor?

Mensaje por mcleod_ideafix » Dom Sep 07, 2014 4:44 pm

Bueno, resulta que estoy escribiendo una memoria sobre el soft-core que va dentro del ZX-Uno, y que como sabeis, implementa un Spectrum 128K con un montón de cositas.

Entonces, a la hora de explicar la extraña organización de la memoria de video del Spectrum, me asaltó una duda.... ¿era ésta de verdad la mejor opción que tenía Altwasser?

La comparé con una supuesta organización lineal, y ahí creo que hay consenso: la organización original del Spectrum permite dibujar caracteres de forma muy rápida: de un scan al siguiente, cuando se pinta un carácter, sólo hay que incrementar el byte más significativo del registro que lleva la dirección de pantalla. Si la organización fuera lineal, habría que sumar una cantidad, no un incremento, y eso implica tener que usar sí o sí el acumulador, en cambio para incrementar un registro de 8 bits, el que sea, no hay que pasar por el acumulador.

Código: Seleccionar todo

;HL = address of first byte of character pattern
;DE = address of first scan of character in video memory
    ld b,8
BucScan:
    ld a,(hl)
    ld (de),a
    inc l
    inc d   ;move to next scan
    djnz BucScan

Por otra parte, la rutina de la ROM que calcula la dirección de memoria que contiene a un determinado pixel de coordenadas C,B es tal que así:

Código: Seleccionar todo

;Taken from The Incomplete Spectrum ROM Assembly
;http://www.wearmouth.demon.co.uk/
;INPUT: C=coordinate X, B=coordinate Y.
;OUTPUT: HL=address of byte containing pixel
LD      B,A
AND     A
RRA
SCF
RRA
AND     A
RRA
XOR     B
AND     $F8
XOR     B
LD      H,A
LD      A,C
RLCA
RLCA
RLCA
XOR     B
AND     $C7
XOR     B
RLCA
RLCA
LD      L,A

Pero entonces me acordé de un experimento que hice con el Commodore 64:
El C64, para quien no lo sepa, le diré que tiene un modo de pantalla denominado "HiRes" y que es el que te da los 320x200 puntos, peeeeeeero con una limitación de 2 colores por cada bloque de 8x8 píxeles. ¿Os suena?

No se usa mucho en juegos, porque no permite el pintado rápido de tiles, ni en pixel art, por la limitación antes comentada, pero algunos artistas sí que usan este modo para sus creaciones, bien "a palo seco", o bien ayudándose dando más color en algunas zonas mediante el uso de sprites superpuestos al dibujo.

Lo curioso de este modo de pantalla es la organización de la zona de bitmaps (que como en el Spectrum, está separada de la zona de atributos). Me explico: si en el Spectrum hacemos el típico programa...

Código: Seleccionar todo

10 FOR n=16384 TO 22527: POKE n,255: NEXT n

Obtenemos el también típico patrón de rellenado de pantalla, que es el mismo que se ve cuando se carga una pantalla desde el cassette:
Imagen

Si el Spectrum tuviera la organización de la memoria de bitmaps como la del C64, el programa anterior se vería así:
Imagen

Curioso, ¿no? Parece, intuitivamente, que esta organización ayuda aún más que la anterior a pintar caracteres de forma rápida:
- Para pasar de un scan al siguiente, basta incrementar el registro (o su byte menos significativo)

Código: Seleccionar todo

;HL = address of first byte of character pattern
;DE = address of first scan of character in video memory
    ld b,8
BucScan:
    ld a,(hl)
    ld (de),a
    inc l
    inc e  ;Increment lo byte instead of hi byte
    djnz BucScan

- Para pasar de un caracter al siguiente, incluso de una línea a la siguiente, no hay que hacer nada especial, ya que una vez pintado un carácter, el registro que apunta a la dirección de pantalla ya está apuntando al principio del siguiente carácter. Esto significa que para pintar cadenas de texto largas no hay que recalcular la dirección de pantalla, acelerándose por tanto esta operación.

Si la ROM se hubiera escrito atendiendo a esta disposición de la pantalla, incluso la rutina que hemos puesto antes, para calcular la dirección de memoria del byte que contiene un pixel de coordenadas C,B se hace más corta y rápida:

Código: Seleccionar todo

;INPUT: C=coordinate X, B=coordinate Y.
;OUTPUT: HL=address of byte containing pixel
ld a,b
and a
rra
scf
rra
and a
rra
ld h,a
ld a,c
and $F8
ld l,a
ld a,b
and $07
or l
ld l,a

Y dado que la ROM usa esta rutina no sólamente para dibujar un punto (PLOT) sino para DRAW (que llama a PLOT) y CIRCLE (que llama a DRAW), resulta que, desde BASIC, esta disposición daría lugar a rutinas algo más rápidas que las que tenemos.

Incluso la rutina que calcula la dirección de inicio de un carácter dada su fila y columna es más sencilla con esta organización:

Código: Seleccionar todo

;INPUT: H : valid character Y coordinate. L : valid character X coordinate
;OUTPUT: HL = address of character in screen

ld a,l
add a,a
add a,a
add a,a
ld l,a
ld a,h
or $40h
ld h,a
ret

Con todo esto, pensé que si Altwasser no usó esta disposición (que además parece más intuitiva que la que después se usó realmente) era porque en hardware resultaba costoso reordenar la pantalla así. Pues no (al menos, no para la zona de bitmaps). Si tenemos X,Y las coordenadas de un pixel (8 bits cada una):

Código: Seleccionar todo

Coordenada X: X7 X6 X5 X4 X3 X2 X1 X0
Coordenada Y: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0

Dirección de memoria (organización original del ZX Spectrum)
Y7 Y6 Y2 Y1 Y0 Y5 Y4 Y3 X7 X6 X5 X4 X3

Dirección de memoria (organización estilo C64)
Y7 Y6 Y5 Y4 Y3 X7 X6 X5 X4 X3 Y2 Y1 Y0


Pero es que no acaba ahí la cosa: al usar esta disposición como dirección de fila/columna para direccionar la DRAM, resulta que las 128 filas las recorre en sólo 512us, cuando usando la organización del Spectrum, las recorre en 2048us (=2,048ms). Lo peor es que el tiempo máximo de refresco para las 128 filas es precisamente de 2ms, con lo que tenemos que la organización original del Spectrum está usando la DRAM al límite de su velocidad de refresco (llega a tardar un pelín más y habría serios problemas con la integridad de los datos en memoria)

O sea: resulta que la organización del C64 resulta mejor si tenemos en cuenta los objetivos que se marcaron los diseñadores de Sinclair, respecto de una pantalla orientada a mostrar caracteres de forma rápida (ese, si mal no recuerdo, era su objetivo), ya que:
- Las rutinas que manejan esta disposición requieren menos cálculos, o en el peor de los casos, el mismo número de cálculos que con la organización del ZX.
- En hardware resulta igual de sencillo implementar una u otra.

Al menos esto era lo que yo pensaba. Incluso imaginé que sería algún problema de patentes, o vaya usted a saber..... hasta que me dio por intentar incluir los atributos en esta ecuación.

Y ahí vi mi error: realmente esto no se puede implementar (con las restricciones del Z80, la DRAM, etc) en la ULA, al menos no de forma evidente. Y si al final se pudiera, seguramente sería a costa de usar más puertas lógicas que las que da la ULA original. Veamos:

La organización de bitmap + atributos es tal que a partir de una coordenada de un pixel (en alta resolución), la dirección de memoria que contiene dicho pixel, y la dirección del atributo que contiene su color se calcula así:

Código: Seleccionar todo

Coordenada X: X7 X6 X5 X4 X3 X2 X1 X0
Coordenada Y: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0

Dirección de memoria de pixel (tal como la genera la ULA, 14 bits)
0 Y7 Y6 Y2 Y1 Y0 Y5 || Y4 Y3 X7 X6 X5 X4 X3

Dirección de memoria de atributo (tal como la genera la ULA, 14 bits)
1 1 0 Y7 Y6 Y5 || Y4 Y3 X7 X6 X5 X4 X3

La ULA no genera estas direcciones de golpe, sino que como tiene que comandar memorias dinámicas, y éstas usan la dirección multiplexada en fila y columna, primero la ULA manda la fila (7 bits) y luego la columna (los otros 7 bits)

La fila se corresponde con los 7 bits menos significativos de la dirección, y la columna, los 7 más significativos. En el diagrama anterior, he separado fila y columna para que se vea lo que quiero decir:

Y lo que quiero decir es que tanto para la dirección de bitmap como para la de atributo, el valor de la fila es el mismo. Esto no es ninguna coincidencia: es un requisito necesario para poder hacer en la DRAM una lectura en modo página: se deja la fila fijada, y se cambia la columna tantas veces como uno quiera. Así se pueden leer múltiples posiciones de memoria, dentro de la misma página de DRAM, a más velocidad que si fuera una lectura separada.

Y esto es lo que posibilita que la contienda en la memoria de video no sea tan brutal como hubiera sido si no llega a ser por esta posibilidad de la lectura en modo página.

Nótese, para lo que veremos después, que la dirección de fila tiene el valor Y4 Y3 X7 X6 X5 X4 X3 . Cada bit que se usa en la construcción de esta dirección mantiene su peso relativo a su peso original cuando se toman las coordenadas X,Y por separado: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 X7 X6 X5 X4 X3 X2 X1 X0.
Esto significa que si en la segunda secuencia, Y3 está por encima de cualquier Xk, en la primera también debe complirse. Esta propiedad es la que permite que se pueda construir una dirección de atributo que sea correlativa una de la siguiente. Si esto no se cumpliera, las direcciones de atributo no podrían ser correlativas, y tendrían que seguir algún tipo de patrón más o menos extraño.

Pues bien, esto es lo que ocurre si tomáramos el modelo de organización estilo C64: las direcciones de bitmap y atributo tendrían que ser tal que así:

Código: Seleccionar todo

Coordenada X: X7 X6 X5 X4 X3 X2 X1 X0
Coordenada Y: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0

Dirección de memoria de pixel (tal como la generaría la ULA, 14 bits)
0 Y7 Y6 Y5 Y4 Y3 X7 || X6 X5 X4 X3 Y2 Y1 Y0

Dirección de memoria de atributo (tal como la generaría la ULA, 14 bits)
? ? ? ? ? ? ? || X6 X5 X4 X3 Y2 Y1 Y0

La pregunta es: ¿qué combinación de bits pongo en los 7 bits superiores para que la dirección total siga siendo correlativa? Pues resulta que con la combinación que hay en los 7 bits inferiores es imposible encontrar una, ya que hay bits dentro de esos 7 bits inferiores que no conservan su peso (orden) original: Y2,Y1 e Y0 por ejemplo van después que los bits X6 X5 X4 X3.

Bueno, pues parece que después de todo Altwasser dio con la organización de pantalla que mejor venía a sus intereses:
- Fácil de implementar en hardware
- Posibilita la impresión rápida de caracteres
- Posibilita la lectura en modo página de la DRAM

Así que ahí dejé mi pequeña investigación, no sin antes acordarme de que había visto en algún sitio, creo que la pantalla de carga del Fairlight, el 1 o el 2, que se cargaba la pantalla con un patrón de relleno similar a éste que he puesto, así que se me ha ocurrido, como tontería adicional, escribir un hack para la rutina de carga estándar del Spectrum, para que cargue una pantalla usando esta disposición. Creo que queda resultona, ¿no?

Podeis probarla (en un emulador desactivar el "flash load") reproduciendo este TAP (aunque sea un TAP no funcionará con DivIDE, sólo con entrada de audio "de verdad"):
http://www.zxprojects.com/atc/demo_c64_ ... _color.tap

(la pantalla es una de las pedazo de obras de arte de MAC)
Última edición por mcleod_ideafix el Dom Oct 15, 2017 11:33 pm, editado 2 veces en total.
Web: ZX Projects | Twitter: @zxprojects

Avatar de Usuario
antoniovillena
Nonamed
Mensajes: 1164
Registrado: Dom Ene 09, 2011 8:55 am

Re: Organización de pantalla alternativa.... ¿mejor?

Mensaje por antoniovillena » Dom Sep 07, 2014 7:25 pm

En el libro de Chris habla sobre este tema. Él proponía varias distribuciones (te hablo de memoria) en las que se mantenía la máxima de poder acceder en fast page mode (que byte y atributo compartan la misma fila para poder acceder a ambos bytes con tres direcciones: fila, columna de atributo y columna de byte). De todas las posibilidades que había Chris conjeturó que Altwasser eligió la del spectrum porque las líneas contiguas quedan separadas 256 bytes, y ésto le viene muy bien al software de pintado de sprites/caracteres. Sólamente hay que incrementar la parte alta de la dirección (INC H) para acceder a la siguiente línea, por lo que además de sencillo es más rápido a la hora de pintar sprites/caracteres.

Pero que sí, que el diseño del Spectrum no es perfecto. A parte de la distribución del C64 más sencilla se podría haber escogido el esquema de compartición de memoria del Inves, en el que no existe la contención. Teniendo en cuenta los medios de los que disponía (lo tuvo que hacer todo en un prototipo con componentes discretos) bastante bien lo hizo. A los que les guste el tema les recomiendo también que le echen un vistazo a otra obra de Richard Altwasser, el Jupiter Ace.
Imagen

Avatar de Usuario
Kyp
Sabreman
Mensajes: 444
Registrado: Lun Dic 16, 2013 6:16 pm

Re: Organización de pantalla alternativa.... ¿mejor?

Mensaje por Kyp » Lun Sep 08, 2014 11:50 am

Muy interesante, como siempre.
Y muy chulo el cargador :)

Por cierto, ya que lo comentas... siempre me ha llamado mucho la atención el cargador del Fairlight. Recuerdo que cuando cargué por primera vez el juego me quedé impresionado.

La versión de 48K es parecido al tuyo, pero por cada 8 filas carga primero los atributos y luego el bitmap, no por caracteres.

La versión de 128K es aún más espectacular. Este si que carga por caracteres, pero primero las columnas de los lados, luego el título del juego arriba, y finalmente el interior de la imagen rellenando de fuera a dentro.

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 8 invitados