Un poco de ensamblador...
Publicado: Sab, 28 Feb 2015, 01:27
Voy a ver si vuelvo poquito a poco a la carga,
Estoy intentando hacer mejoras al sistema de cargas de pantallas que tenía hecho (por motivos de rendimiento y blablabla). Para ello estoy rehaciendo una serie rutinas en c que implicaba estar continuamente paginando aún estando la mayor parte del tiempo usando la misma página de memoria (no entraré en detalles para no volveros locos con datos innecesarios).
Para evitar esto estoy 'trasladando' (no realmente porque cambian cosas) a ensamblador algunas rutinas. Una de ellas requiere la copia de bloques de bytes desde una dirección a otra; pero me estoy volviendo loco
- Una función que copie bloques de 32 bytes N veces. N es constante.
- La dirección de memoria destino se conoce y es fija. Los N bloques de 32 bytes se colocan sucesivamente
- La dirección de origen se conoce pero no es fija, hay que añadirle un offset. Dicho offset es diferente para cada bloque.
En principio los offsets vendrian representados por un array declarado en c (pero estoy abierto a otras posibilidades).
$this->bbcode_second_pass_code('', 'unsigned char offsets [N] = {4,10,5,6,7}')
Pero a la hora de interpretar los valores contenidos en el array habría que multiplicar por 32 cada valor (si es más rápido siempre podría hacer el array de enteros y guardar el valor real necesitado desde un principio)
$this->bbcode_second_pass_code('', '
offset[0]=128
offset[1]=320
offset[2]=160
... etc
')
Ok, hasta aquí la teoría, en la práctica entiendo que puedo copiar bloques facilmente con ldir (que no se si será lo más rápido); Así, en el ejemplo siguiente se copiarian 32 bytes consecutivos desde 56000-56031 a 20000-20031:
$this->bbcode_second_pass_code('', '
ld hl, 56000
ld de, 20000
ld bc,31
ldir
')
Ok, repetir esto N veces (perdonad mi ignorancia), pero podria usar el registro a y un salto condicional. Así:
$this->bbcode_second_pass_code('', '
ld a, 5
ld de, 20000
.bucle
ld hl, 56000
ld bc,31
ldir
dec a
jr nz, bucle
')
En este caso, y al sacar ld de,20000 del bucle y aprovechar que ldir autoincrementa el registro de, conseguiria copiar 5 veces el mismo bloque de 32 bytes (56000-56031) al 20000-20159 (160 bytes)
A partir de aquí he hecho multitud de pruebas para añadir el offset a hl y obtener asi la dirección origen correcta, pero no hay manera. En pseudocódigo tendría que hacer lo siguiente:
$this->bbcode_second_pass_code('', '
DESTINO = 20000
PARA N VECES
ORIGEN = 56000 + offset[N]*32
COPIAR 32 BYTES DESDE ORIGEN A DESTINO
DESTINO = DESTINO + 32
SIGUIENTE N
')
Alguna ayudita please
PD: edito para corregir alguna cosa y aprovecho para lanzar otra pregunta ya que lo que pretendo únicamente en NO paginar continuamente tal como ahora hago:
$this->bbcode_second_pass_code('', '
void CopiaBloque(unsigned int num) {
ram_page [0] = 3
ram_address [0] = 56000+(offset[num]<<5);
ram_destination [0] = (unsigned int) (destino+(num<<5));
#asm
di
ld a, (_ram_page)
ld b, a
call SetRAMBank
ld hl, (_ram_address)
ld de, (_ram_destination)
ld bc,31
ldir
ld b, 0
call SetRAMBank
ei
#endasm
};
')
donde destino se declaró como (por ejemplo):
$this->bbcode_second_pass_code('', 'unsigned char destino[16] = {0,1,2,3,15,38,44,2,0,12,11,90,100,101,102,103} ')
y llamar a dicha función en un bucle:
$this->bbcode_second_pass_code('', '
for(i=0;i<16;i++) {
CopiaBloque(i);
}
')
¿Realmente ganaría (en ciclos de reloj, pero algo humanamente perceptible) haciendolo todo en asm (en una hipotética función CopiarBloqueS(void) {}; ) evitando paginar con SetRAMBank cada vez que llamo a CopiaBloque? Hay que tener en cuenta, para mi caso, que finalmente se copiarian entre pantalla y pantalla (de un mismo nivel) en torno a 60 bloques de 32 bytes.
Estoy intentando hacer mejoras al sistema de cargas de pantallas que tenía hecho (por motivos de rendimiento y blablabla). Para ello estoy rehaciendo una serie rutinas en c que implicaba estar continuamente paginando aún estando la mayor parte del tiempo usando la misma página de memoria (no entraré en detalles para no volveros locos con datos innecesarios).
Para evitar esto estoy 'trasladando' (no realmente porque cambian cosas) a ensamblador algunas rutinas. Una de ellas requiere la copia de bloques de bytes desde una dirección a otra; pero me estoy volviendo loco
- Una función que copie bloques de 32 bytes N veces. N es constante.
- La dirección de memoria destino se conoce y es fija. Los N bloques de 32 bytes se colocan sucesivamente
- La dirección de origen se conoce pero no es fija, hay que añadirle un offset. Dicho offset es diferente para cada bloque.
En principio los offsets vendrian representados por un array declarado en c (pero estoy abierto a otras posibilidades).
$this->bbcode_second_pass_code('', 'unsigned char offsets [N] = {4,10,5,6,7}')
Pero a la hora de interpretar los valores contenidos en el array habría que multiplicar por 32 cada valor (si es más rápido siempre podría hacer el array de enteros y guardar el valor real necesitado desde un principio)
$this->bbcode_second_pass_code('', '
offset[0]=128
offset[1]=320
offset[2]=160
... etc
')
Ok, hasta aquí la teoría, en la práctica entiendo que puedo copiar bloques facilmente con ldir (que no se si será lo más rápido); Así, en el ejemplo siguiente se copiarian 32 bytes consecutivos desde 56000-56031 a 20000-20031:
$this->bbcode_second_pass_code('', '
ld hl, 56000
ld de, 20000
ld bc,31
ldir
')
Ok, repetir esto N veces (perdonad mi ignorancia), pero podria usar el registro a y un salto condicional. Así:
$this->bbcode_second_pass_code('', '
ld a, 5
ld de, 20000
.bucle
ld hl, 56000
ld bc,31
ldir
dec a
jr nz, bucle
')
En este caso, y al sacar ld de,20000 del bucle y aprovechar que ldir autoincrementa el registro de, conseguiria copiar 5 veces el mismo bloque de 32 bytes (56000-56031) al 20000-20159 (160 bytes)
A partir de aquí he hecho multitud de pruebas para añadir el offset a hl y obtener asi la dirección origen correcta, pero no hay manera. En pseudocódigo tendría que hacer lo siguiente:
$this->bbcode_second_pass_code('', '
DESTINO = 20000
PARA N VECES
ORIGEN = 56000 + offset[N]*32
COPIAR 32 BYTES DESDE ORIGEN A DESTINO
DESTINO = DESTINO + 32
SIGUIENTE N
')
Alguna ayudita please
PD: edito para corregir alguna cosa y aprovecho para lanzar otra pregunta ya que lo que pretendo únicamente en NO paginar continuamente tal como ahora hago:
$this->bbcode_second_pass_code('', '
void CopiaBloque(unsigned int num) {
ram_page [0] = 3
ram_address [0] = 56000+(offset[num]<<5);
ram_destination [0] = (unsigned int) (destino+(num<<5));
#asm
di
ld a, (_ram_page)
ld b, a
call SetRAMBank
ld hl, (_ram_address)
ld de, (_ram_destination)
ld bc,31
ldir
ld b, 0
call SetRAMBank
ei
#endasm
};
')
donde destino se declaró como (por ejemplo):
$this->bbcode_second_pass_code('', 'unsigned char destino[16] = {0,1,2,3,15,38,44,2,0,12,11,90,100,101,102,103} ')
y llamar a dicha función en un bucle:
$this->bbcode_second_pass_code('', '
for(i=0;i<16;i++) {
CopiaBloque(i);
}
')
¿Realmente ganaría (en ciclos de reloj, pero algo humanamente perceptible) haciendolo todo en asm (en una hipotética función CopiarBloqueS(void) {}; ) evitando paginar con SetRAMBank cada vez que llamo a CopiaBloque? Hay que tener en cuenta, para mi caso, que finalmente se copiarian entre pantalla y pantalla (de un mismo nivel) en torno a 60 bloques de 32 bytes.