Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
cursos:ensamblador:avanzadas2 [14-01-2024 07:54] – borrado sromero | cursos:ensamblador:avanzadas2 [24-01-2024 17:25] (actual) – sromero | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
+ | ====== Consideraciones avanzadas (II) ====== | ||
+ | En este capítulo vamos a ver algunas consideraciones relativas a optimizaciones y mejoras que no hemos querido incluír dentro de sus respectivos capítulos para evitar confundir al lector con código o ideas complejas en un momento en que está aprendiendo los fundamenos.. | ||
+ | |||
+ | \\ | ||
+ | ===== Optimizaciones generales ===== | ||
+ | |||
+ | Veamos una serie de " | ||
+ | |||
+ | \\ | ||
+ | **Copiar el flag Zero al Carry** | ||
+ | |||
+ | <code z80> | ||
+ | scf ; CF = 1 | ||
+ | jr z, $+3 ; Si ZF=1, saltamos y dejamos CF=1 | ||
+ | ccf ; Si ZF=0, al no saltar ejecutamos ccf | ||
+ | ; y hacemos CF=0 | ||
+ | ; $+3 es la instrucción posterior al ccf | ||
+ | </ | ||
+ | |||
+ | **Copiar el flag de Carry al Zero flag** | ||
+ | |||
+ | <code z80> | ||
+ | ccf | ||
+ | sbc a, a | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | **Realizar un '' | ||
+ | |||
+ | <code z80> | ||
+ | xor a | ||
+ | sub Parte_baja_Registro | ||
+ | ld Parte_baja_Registro, | ||
+ | sbc a, a | ||
+ | sub Parte_alta_Registro | ||
+ | ld Parte_alta_Registro, | ||
+ | </ | ||
+ | |||
+ | Por ejemplo, para simular '' | ||
+ | |||
+ | <code z80> | ||
+ | xor a | ||
+ | sub l | ||
+ | ld l, a | ||
+ | sbc a, a | ||
+ | sub h | ||
+ | ld h, a | ||
+ | </ | ||
+ | |||
+ | |||
+ | \\ | ||
+ | ===== Optimizaciones de bucles ===== | ||
+ | |||
+ | \\ | ||
+ | ==== Bucles de 16 bits optimizados ==== | ||
+ | |||
+ | |||
+ | Es posible hacer loops de 16 bits que son tan rápidos como los de 8, tal y como se explica en la siguiente URL | ||
+ | |||
+ | * https:// | ||
+ | |||
+ | La forma normal de realizar loops de 16 bits sería algo como: | ||
+ | |||
+ | <code z80> | ||
+ | ld de, NNNN ; veces a iterar | ||
+ | |||
+ | loop: | ||
+ | ; ... código de nuestro bucle ... | ||
+ | dec de | ||
+ | ld a, d | ||
+ | or e | ||
+ | jp nz, loop | ||
+ | </ | ||
+ | |||
+ | El problema es que tenemos que hacer operaciones costosas como el '' | ||
+ | |||
+ | Para eso, esta página nos propone aprovechar '' | ||
+ | |||
+ | Poniendo B a 0, tras ejecutar el código 1 vez, se decrementa B, con lo que pasa a valer $ff (255), así que nos aseguramos 255 ejecuciones más la inicial, total 256. | ||
+ | |||
+ | Debido a esto, podemos hacer algo como lo siguiente: partimos el número de 16 bits a iterar en 2 registros e iteramos el primero un total de veces almacenado en el segundo. Cada vez que el primer registro llegue a cero, volveremos a iterar 256 veces, salvo la primera vez que iteramos el " | ||
+ | |||
+ | Ejemplo: | ||
+ | |||
+ | <code z80> | ||
+ | ld b, 10 ; loop LSB | ||
+ | ld d, 3 ; loop MSB => $050a => 522 iteraciones | ||
+ | |||
+ | loop: | ||
+ | ; ... código de nuestro bucle ... | ||
+ | djnz loop | ||
+ | dec d | ||
+ | jp nz, loop | ||
+ | </ | ||
+ | |||
+ | Este bucle iterará un total de 522 veces ($050a). | ||
+ | |||
+ | Ahora la pregunta es ¿cómo calculamos los valores que tenemos que poner en D y en B? | ||
+ | |||
+ | Con el siguiente código: | ||
+ | |||
+ | <code z80> | ||
+ | ld de, NNNN ; veces a iterar | ||
+ | ld b, e | ||
+ | dec de | ||
+ | inc d ; D y B listos para usar en nuestro bucle | ||
+ | </ | ||
+ | |||
+ | Es decir, el bucle completo sería: | ||
+ | |||
+ | <code z80> | ||
+ | ld de, NNNN ; veces a iterar | ||
+ | ld b, e ; | ||
+ | dec de ; Calculate DB value (destroys B, D and E) | ||
+ | inc d | ||
+ | loop: | ||
+ | ; ... código de nuestro bucle ... | ||
+ | djnz LOOP | ||
+ | dec d | ||
+ | jp nz, LOOP | ||
+ | </ | ||
+ | |||
+ | Si queremos usar B y C en lugar de D y B: | ||
+ | |||
+ | <code z80> | ||
+ | ld de, NNNN ; veces a iterar | ||
+ | ld b, e ; | ||
+ | dec de ; Calculate DB value (destroys B, D and E) | ||
+ | inc d | ||
+ | ld c, d | ||
+ | |||
+ | loop: | ||
+ | ; ... código de nuestro bucle ... | ||
+ | djnz LOOP | ||
+ | dec c | ||
+ | jp nz, LOOP | ||
+ | </ | ||
+ | |||
+ | Al respecto de los tiempos: | ||
+ | |||
+ | El bucle de 16 bits estándar que hemos visto en el primer ejemplo utiliza 4 instrucciones para hacer el bucle, lo que suma un total de 28 ciclos de reloj o T-states por iteración. | ||
+ | |||
+ | En cambio, el bucle con '' | ||
+ | |||
+ | El bucle exterior se ejecuta de forma poco frecuente (1 vez cada 256 iteraciones), | ||
+ | |||
+ | Hemos necesitado 25 ciclos de reloj para precalcular inicialmente los valores del bucle, pero como vemos, se han recuperado a partir de la tercera iteración del bucle si lo comparamos con un bucle de 16 bits normal. | ||
+ | |||
+ | |||
+ | \\ | ||
+ | ===== Uso de los Shadow Registers ===== | ||
+ | |||
+ | Cuando explicamos los //Shadow Registers//, | ||
+ | |||
+ | Un ejemplo de uso de los registros alternativos es multiplicar vectores 16 bits por escalares, aritmética de punto flotante, como se puede ver en la siguiente URL: | ||
+ | |||
+ | * https:// | ||
+ | |||
+ | Finalmente, un caso muy interesante del uso de '' | ||
+ | |||
+ | Por ejemplo, supongamos que hacemos un scroll de un bitmap así: | ||
+ | |||
+ | <code z80> | ||
+ | rl (hl) | ||
+ | dec l ; este bloque RL + DEC se repite 16 veces | ||
+ | </ | ||
+ | |||
+ | En este caso, rotamos el byte apuntado por HL a la izquierda, de forma que el Carry Flag entra por la derecha con el nuevo pixel entrante en la pantalla. | ||
+ | |||
+ | ¿Qué ocurre si queremos scrollear 2 pixeles, y necesitamos no perder el Carry Flag del primer RL cuando hacemos el segundo? Pues que podemos preservarlo dejándolo en AF' y recuperándolo después: | ||
+ | |||
+ | <code z80> | ||
+ | rl (hl) | ||
+ | ex af, af' | ||
+ | rl (hl) | ||
+ | ex af, af' | ||
+ | dec l ; este bloque se repite 16 veces | ||
+ | </ | ||
+ | |||
+ | |||
+ | \\ | ||
+ | **[ [[.: |