Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

optimizando z80

13 views
Skip to first unread message

Kak

unread,
Nov 8, 2006, 3:49:46 PM11/8/06
to
Hola gente! :)

merodeando por paginas web de desarrollo he encontrado a alguien
haciendo preguntas de Z80, y
se me ha ocurrido que seguro que entre todos podemos ayudarle y
desenpolvar los apuntes de
z80 como haciamos hace tiempo :)

La idea es que tiene un byte "abcdefgh" , y quiere rotar los bits 4
a 4, o sea, que quede
"dcbahgfe", por ejemplo, 00101101 pasaria a 01001011.

Al principio se propuso hacer la rotacion normal usando rotaciones
(obtener hgfedcba), y al final rotar 4 veces hacia cualquier lado
(dcba-hgfe)


(a contiene el byte a girar)

ld b,8 ; 4t,1b (4 t-estados, 1 byte)

lab:
rra ; 4t,1b
rl c ; 8t,2b
djnz lab ; 13/8t, 2b

ld a,c ; 4t,1b

rrca ; 4t,1b
rrca
rrca
rrca

que hacen 11 bytes y 4+(4+8+13)*7+(4+8+8)+4+4*4 = 219 t-estados si no
me he equivocado.
Haciendo loop unroll, serian 4+(4+8)*8+4+4*4 = 120 t-estados, y 19
bytes mas


despues pensamos que podriamos usar una tabla de 16 bytes con los
nibbles rotados, o sea:

tabla:
db 0000b ; el nibble 0000 rotado
db 1000b ; el nibble 0001 rotado
db 0100b ; el nibble 0010 rotado
db 1100b ; el nibble 0011 rotado
db 0010b ; el nibble 0100 rotado

etc...


y hacerlo de esta forma:
ld h,tabla/256 ; 4t, 2b

; resguardamos el valor a rotar
ld c,a ; 4t,1b
and $f ; 7t,2b

ld l,a ; 4t,1b
ld b,(hl) ; 7t,1b

; tenemos en b el nibble bajo

; recuperamos valor inicial
ld a,c ; 4t,1b

; obtenemos nibble alto
rra ; 4t,1b
rra
rra
rra

ld l,a ;4t,1b
ld a,(hl) ;7t,1b

rla ; 4t,1b
rla
rla
rla

or b ;4t,1b

; diria que ya ta :)

total, 35 bytes (16 de tabla) y 4+4+7+4+7+4+4*4+4+7+4*4+4= 81 t-estados


por supuesto, tabla deberia empezar en una direccion multiple de 256
bytes, sino se puede se anyadirian un par de sumas y una resta

se os ocurre algun sistema mas rapido?


Segun ha dicho lo quiere porque esta intentando hacer un juego para
CPC, y quiere una rutina
para pintar los sprites en "modo espejo"... supongo que los timings
siguen siendo los mismos, no?
Con esta premisa lo mas practico quizas sea tener una tabla de 256
bytes y acceder a ella, pues
seria una rutina critica en el tiempo, pero claro, 256 bytes... buffff
:)

Saludos! :)
Kak

Julián Albo

unread,
Nov 8, 2006, 4:38:39 PM11/8/06
to
Kak wrote:

> se os ocurre algun sistema mas rapido?
>
> Segun ha dicho lo quiere porque esta intentando hacer un juego para
> CPC, y quiere una rutina para pintar los sprites en "modo espejo"...

Que guarde copias ya espejadas de los sprites. Ya sea metiéndolas
directamente en el código como datos, ya sea preparándolas en la
inicialización del programa, que así no tiene que preocuparse tanto de la
velocidad.

--
Salu2

Jaime Tejedor Gómez, aka Metalbrain

unread,
Nov 8, 2006, 5:03:47 PM11/8/06
to
Hola

>[...]


>que hacen 11 bytes y 4+(4+8+13)*7+(4+8+8)+4+4*4 = 219 t-estados si no
>me he equivocado.
>Haciendo loop unroll, serian 4+(4+8)*8+4+4*4 = 120 t-estados, y 19
>bytes mas

Una pequeña mejora al sistema sin tablas:

ld c,a ;1/4
ld b,0 ;2/7
sla a ;2/8
rr b ;2/8
rla ;1/4
rr b ;2/8
rla ;1/4
rr b ;2/8
rla ;1/4
rr b ;2/8
rr c ;2/8
rla ;1/4
rr c ;2/8
rla ;1/4
rr c ;2/8
rla ;1/4
rr c ;2/8
rla ;1/4
or b ;1/4

115 estados, 29 bytes.

>despues pensamos que podriamos usar una tabla de 16 bytes con los
>nibbles rotados, o sea:
>
>tabla:
>db 0000b ; el nibble 0000 rotado
>db 1000b ; el nibble 0001 rotado
>db 0100b ; el nibble 0010 rotado
>db 1100b ; el nibble 0011 rotado
>db 0010b ; el nibble 0100 rotado
>
>etc...
>
>
>y hacerlo de esta forma:
> ld h,tabla/256 ; 4t, 2b

esto es 7t


>
> ; resguardamos el valor a rotar
> ld c,a ; 4t,1b
> and $f ; 7t,2b
>
> ld l,a ; 4t,1b
> ld b,(hl) ; 7t,1b
>
> ; tenemos en b el nibble bajo
>
> ; recuperamos valor inicial
> ld a,c ; 4t,1b
>
> ; obtenemos nibble alto
> rra ; 4t,1b
> rra
> rra
> rra

yo diría que justo aquí va otro "and $f" para evitar que las rotaciones dejen
bits a 1 en la parte alta

> ld l,a ;4t,1b
> ld a,(hl) ;7t,1b
>
> rla ; 4t,1b
> rla
> rla
> rla
>
> or b ;4t,1b
>
> ; diria que ya ta :)
>
>total, 35 bytes (16 de tabla) y 4+4+7+4+7+4+4*4+4+7+4*4+4= 81 t-estados

y corregido queda en 37 bytes/87 t-estados (tu suma inicial daría 77, no 81)

>se os ocurre algun sistema mas rapido? [...]


>Con esta premisa lo mas practico quizas sea tener una tabla de 256
>bytes y acceder a ella, pues
>seria una rutina critica en el tiempo, pero claro, 256 bytes... buffff

Pues yo diría que es la mejor manera, si se puede permitir el lujo:
ld h,tabla/256 ;2/7
ld l,a ;1/4
la a,(hl) ;1/7

4+256 bytes / 18 estados

METALBRAIN
(C) 1977 Tejedor & Gómez Research Ltd.

McLeod / IdeaFix

unread,
Nov 8, 2006, 9:40:43 PM11/8/06
to
Kak escribió:
> Hola gente! :)

>
> La idea es que tiene un byte "abcdefgh" , y quiere rotar los bits 4
> a 4, o sea, que quede
> "dcbahgfe", por ejemplo, 00101101 pasaria a 01001011.

Vale. No quiere rotarlo, quiere tener en espejo cada nibble por separado. Es que
lo de "rotar" me ha liado.

> se os ocurre algun sistema mas rapido?

La tabla de traducción de toda-la-vida, con la base de la tabla en una posición
múltiplo de 256: rápido y corto (en bytes de código). En A está el byte a invertir.

ld h,Tabla/256 7
ld l,a 4
ld a,(hl) 7
TOTAL 18 ciclos

Versión que no usa HL, con código automodificable (más lento eso sí)
ld ($+1),a 13
ld a,(Tabla) 13
TOTAL 26 ciclos


Si no se puede hacer múltiplo de 256, hay dos opciones:

Sumando offsets:
ld hl, Tabla 10
ld e,a 4
ld d,0 7
add hl,de 11
ld a,(hl) 7
TOTAL 39 ciclos

Código automodificable: (en IX tendríamos como constante la dirección base de la
Tabla de traducción). Es algo más rápido que el método de los offsets, y más
corto (6 bytes) y tiene la ventaja de no necesitar a HL o DE.
ld ($+2),a 13
ld a,(ix+0) 19
TOTAL 32 ciclos

> Segun ha dicho lo quiere porque esta intentando hacer un juego para
> CPC, y quiere una rutina
> para pintar los sprites en "modo espejo"... supongo que los timings
> siguen siendo los mismos, no?

Si mal no recuerdo, el CPC va a 4MHz. Los timmings son los mismos que en el
Spectrum, si usas como unidad de medida el T-estado.

> Con esta premisa lo mas practico quizas sea tener una tabla de 256
> bytes y acceder a ella, pues
> seria una rutina critica en el tiempo, pero claro, 256 bytes... buffff

256 bytes no es mucho. Desde luego, es lo más rápido. Aunque si sus sprites son
unos determinados, igual le sale más a cuenta tener guardados esos mismos
sprites en espejo y usar unos u otros en función de.... digo yo, el sentido en
el que camine el personaje.

Jaime Tejedor Gómez, aka Metalbrain

unread,
Nov 9, 2006, 9:15:57 AM11/9/06
to
Otra pequeña optimización, esta vez a la versión pequeña:

> ld b,8 ; 4t,1b (4 t-estados, 1 byte)
>
>lab:
> rra ; 4t,1b
> rl c ; 8t,2b
> djnz lab ; 13/8t, 2b
>
> ld a,c ; 4t,1b
>
> rrca ; 4t,1b
> rrca
> rrca
> rrca
>
>que hacen 11 bytes y 4+(4+8+13)*7+(4+8+8)+4+4*4 = 219 t-estados si no
>me he equivocado.

ld c,1 ; 4t,1b (4 t-estados, 1 byte)


lab:
rra ; 4t,1b
rl c ; 8t,2b

jr nc,lab ; 12/7t, 2b

ld a,c ; 4t,1b

rrca ; 4t,1b
rrca
rrca
rrca

que hacen 11 bytes y 4+(4+8+12)*7+(4+8+7)+4+4*4 = 211 t-estados, y
además deja libre el registro B.

sejuan

unread,
Nov 10, 2006, 10:07:19 AM11/10/06
to

McLeod / IdeaFix ha escrito:

> Kak escribió:
> > Hola gente! :)
> >
> > La idea es que tiene un byte "abcdefgh" , y quiere rotar los bits 4
> > a 4, o sea, que quede
> > "dcbahgfe", por ejemplo, 00101101 pasaria a 01001011.

...


> La tabla de traducción de toda-la-vida, con la base de la tabla en una posición
> múltiplo de 256: rápido y corto (en bytes de código). En A está el byte a invertir.
>
> ld h,Tabla/256 7
> ld l,a 4
> ld a,(hl) 7
> TOTAL 18 ciclos
>
> Versión que no usa HL, con código automodificable (más lento eso sí)
> ld ($+1),a 13
> ld a,(Tabla) 13
> TOTAL 26 ciclos
>
>
> Si no se puede hacer múltiplo de 256, hay dos opciones:
>

Hola. Una duda solo por y para los que andamos toqueteando el z80 que
motivo tiene la tabla para que sea de 256 bytes? Más aún porque debe
estar en una posición múltiplo de 256? Por cierto, que quiere decir
el $ que incluyes sumandole 1 al que vas a meterle a? Vamos preguntas
de andar por casa.... se nota que no tengo mucha idea?

Gracias


sejuan

Jaime Tejedor Gómez, aka Metalbrain

unread,
Nov 10, 2006, 10:26:28 AM11/10/06
to
>Hola. Una duda solo por y para los que andamos toqueteando el z80 que
>motivo tiene la tabla para que sea de 256 bytes?

Porque hay 256 posibles entradas con sus 256 posibles soluciones.

>Más aún porque debe
>estar en una posición múltiplo de 256?

Para que no varíe el valor de byte alto de la dirección, facilitando la tarea.

>Por cierto, que quiere decir
>el $ que incluyes sumandole 1 al que vas a meterle a?

Es un símbolo que utiliza el ensamblador para indicar la posición actual
(o tal vez la posición siguiente, que sería lo lógico en este caso. Yo la
verdad es que no la suelo usar, prefiero etiquetas). En este caso lo que
hace es que la primera instrucción modifica el byte bajo de la segunda,
y es equivalente a:

ld (IQLEV+1),a ; Instrucción que ajusta la dirección
IQLEV: ld a,(Tabla) ; Instrucción Que Lee El Valor

El +1 es porque la IQLEV se codifica con 3 bytes: el primero
significa ld a,(NN) y los dos siguientes especifican la dirección NN, de
forma que el valor pequeño aparece antes (y es el que tenemos
que modificar según valga el registro a) y luego el mayor (que tiene
que quedarse fijo).

sejuan

unread,
Nov 10, 2006, 1:15:27 PM11/10/06
to

Jaime Tejedor Gómez, aka Metalbrain ha escrito:

> >Hola. Una duda solo por y para los que andamos toqueteando el z80 que
> >motivo tiene la tabla para que sea de 256 bytes?
>
> Porque hay 256 posibles entradas con sus 256 posibles soluciones.

> .

Supongo coordenada X en pantalla, ¿no?

> ld (IQLEV+1),a ; Instrucción que ajusta la dirección
> IQLEV: ld a,(Tabla) ; Instrucción Que Lee El Valor
>
> El +1 es porque la IQLEV se codifica con 3 bytes: el primero
> significa ld a,(NN) y los dos siguientes especifican la dirección NN, de
> forma que el valor pequeño aparece antes (y es el que tenemos
> que modificar según valga el registro a) y luego el mayor (que tiene
> que quedarse fijo).

Esta claro.
Gracias, como siempre un placer.

Sejuan

McLeod / IdeaFix

unread,
Nov 11, 2006, 9:23:25 AM11/11/06
to
> Hola. Una duda solo por y para los que andamos toqueteando el z80 que

Bueno... ya te han respondido :D

Solo añadir quizás, que en mis propuestas, el valor de $ que estoy considerando
que es la dirección de memoria donde comienza la siguiente instrucción a aquella
que se está ensamblando. En algunos ensambladores $ es la dirección de memoria
de la instrucción que se está ensamblando en ese momentofr5.

Las 256 entradas no corresponden a la pantalla en este caso. Es una forma de
tabular los posibles valores de una función.

Dicho de otra forma: si tú quieres implementar un algoritmo que calcule f(x) con
x valiendo 0 a 255 tienes dos alternativas:

- Haces un algoritmo (programa) que de forma procedural genere el valor de la
función f(x) que le corresponde a x
- Haces una tabla con todos los posibles valores de f(x) ordenados según el
valor de x

La tabla tiene una gran ventaja sobre el algoritmo, y es su velocidad: calcular
f(x) se limita a leer cierta posición de memoria que varía en función de x. En
esa posición de memoria está f(x). Sin embargo, para poder usar este método debe
cumplirse:

- x debe estar acotado de tal forma que el número de posiciones diferentes de
memoria no sea excesivo para la máquina en la que se va a implementar. Para una
máquina con 48K de RAM, 256 valores posibles no es problemático. Para un PC
trabajando en 32 bits, 65536 valores posibles tampoco es problemático. Si x
originalmente no es un número entero, o bien no se puede usar este método, o
bien hay que limitar x sólo a ciertos valores de su dominio e interpolar para el
resto.
- el cálculo de f(x) debe ser más costoso en tiempo de proceso que el hecho de
calcular qué posición de memoria ha de leerse y leerla.

Un ejemplo muy típico, en las demos y juegos, es tener tabulados los valores de
sen y cos para los valores de x desde 0 a 359 grados. Si se necesita un valor
intermedio, pongamos el coseno de 45.7 grados, se usan los valores de cos 45 y
cos 46 y se interpola entre ellos para averiguar el dado. La interpolación es
mucho menos costosa que el cálculo directo del coseno usando series de potencias.

0 new messages