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:aritmetica [24-01-2024 07:17] – [Raíz cuadrada] sromero | cursos:ensamblador:aritmetica [02-02-2024 10:12] (actual) – [Números aleatorios] sromero | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
====== Aritmética con el Z80 ====== | ====== Aritmética con el Z80 ====== | ||
- | El Z80 nos provee de funciones aritméticas de 8 bits básicas, como son '' | + | El Z80 nos provee de funciones aritméticas de 8 bits básicas, como son '' |
En muchas de estas operaciones toma especial relevancia '' | En muchas de estas operaciones toma especial relevancia '' | ||
Línea 11: | Línea 11: | ||
En este capítulo vamos a ver rutinas para realizar este tipo de operaciones extraídas de diferentes fuentes (libros, webs dedicadas al Z80, foros de desarrollo, canales de discusión sobre programación en ensamblador), | En este capítulo vamos a ver rutinas para realizar este tipo de operaciones extraídas de diferentes fuentes (libros, webs dedicadas al Z80, foros de desarrollo, canales de discusión sobre programación en ensamblador), | ||
- | En algunas ocasiones se incluirán comentarios sobre cómo opera esta rutina, y en otras simplemente citaremos la rutina con sus parámetros de entrada y salida para poder usarla en nuestros programas. Para toda rutina, se citará la fuente de la que se ha extraído si es conocida, y su autor (si es posible) para darle sus merecido crédito por ella. | + | En algunas ocasiones se incluirán comentarios sobre cómo opera esta rutina, y en otras simplemente citaremos la rutina con sus parámetros de entrada y salida para poder usarla en nuestros programas. Para toda rutina, se citará la fuente de la que se ha extraído si es conocida, y su autor (si es posible) para darle su merecido crédito por ella. |
- | Las rutinas se reproducen tal cual aparecen en la web, por lo que es posible que alguna de ellas incluyan '' | + | Las rutinas se reproducen tal cual aparecen en la web, por lo que es posible que alguna de ellas incluyan |
Se recomienda al lector que se desarrolle su propia librería '' | Se recomienda al lector que se desarrolle su propia librería '' | ||
Línea 47: | Línea 47: | ||
ld b, 0 | ld b, 0 | ||
ld c, a | ld c, a | ||
- | | + | |
| | ||
; Sumas de registros de 8 bits a registros de 16 bits: OPCION 2 | ; Sumas de registros de 8 bits a registros de 16 bits: OPCION 2 | ||
Línea 163: | Línea 163: | ||
</ | </ | ||
- | Si hubiéramos necesitado multiplicar por 21 o por 19 (por ejemplo), podríamos haber guardado el valor de A antes de este proceso (por ejemplo en el registro C) y haber después sumado o restado C a la multiplicación por 20 para encontrar el valor deseado. Para multiplicar por 22 o por 18 podríamos haberlo sumado restado 2 veces al resultado, etc. | + | Si hubiéramos necesitado multiplicar por 21 o por 19 (por ejemplo), podríamos haber guardado el valor de A antes de este proceso (por ejemplo en el registro C) y haber después sumado o restado C a la multiplicación por 20 para encontrar el valor deseado. Para multiplicar por 22 o por 18 podríamos haberlo sumado |
Pero volvamos a '' | Pero volvamos a '' | ||
Línea 172: | Línea 172: | ||
</ | </ | ||
- | Al igual que pasaba con '' | + | Al igual que pasaba con '' |
Por ejemplo: | Por ejemplo: | ||
Línea 203: | Línea 203: | ||
\\ **Dividiendo por potencias de dos**\\ | \\ **Dividiendo por potencias de dos**\\ | ||
- | Del mismo modo que desplazar a la izquierda es multiplicar, | + | Del mismo modo que desplazar a la izquierda es multiplicar, |
<code z80> | <code z80> | ||
Línea 449: | Línea 449: | ||
De la misma forma que se puede multiplicar con múltiples sumas, también podemos dividir de forma poco eficiente con múltiples restas. | De la misma forma que se puede multiplicar con múltiples sumas, también podemos dividir de forma poco eficiente con múltiples restas. | ||
- | Sin embargo, como ocurría con la multiplicación, | + | Sin embargo, como ocurría con la multiplicación, |
- | Mostraremos a continuación | + | Mostraremos a continuación |
\\ | \\ | ||
Línea 589: | Línea 589: | ||
===== Números aleatorios ===== | ===== Números aleatorios ===== | ||
- | Las siguientes rutinas proporcionan | + | Si hay una funcionalidad que necesitaremos casi con total seguridad para desarrollar un juego es la posibilidad de generar |
- | La rutina que veremos a continuación, | + | Hay varias formas de generar números aleatorios. |
+ | Veamos una rutina muy simple (extraída de la página //Z80 bits//, de //Milos Bazelides// | ||
+ | |||
+ | \\ | ||
<code z80> | <code z80> | ||
- | ;------------------------------------------------------- | + | ; Rand8 |
- | ; random.asm - part of the ZX Spectrum libzx | + | ; Input: none |
- | ; library by Sebastian Mihai, 2016 | + | ; Output: A = pseudo-random number, period 256 |
- | ;------------------------------------------------------- | + | ; From: https:// |
- | ; our random numbers are, in part, based on reading bytes | + | RAND8_SEED EQU 0 |
- | ; from the 16kb ROM, whose contents are " | + | |
- | lastRandomNumber db 33 | + | |
- | romPointer | + | |
- | ; Gets an 8-bit random number. | + | Rand8: |
- | ; It is computed using a combination of: | + | ld a, RAND8_SEED |
- | ; - the last returned random number | + | ld b, a |
- | ; | + | add a, a |
- | ; - current values of various registers | + | |
- | ; | + | |
- | ; | + | inc a |
- | ; Output: | + | ld (Rand8+1), a |
- | ; A - next random number | + | ret |
- | get_next_random: | + | </ |
- | push af | + | |
- | push hl | + | La rutina utiliza una semilla |
- | ; advance ROM pointer | + | |
- | ld hl, romPointer | + | |
- | ld c, (hl) | + | |
- | inc hl | + | |
- | ld b, (hl) ; BC := word (romPointer) | + | |
- | ld hl, 3 | + | |
- | add hl, bc ; HL := ROM pointer advanced by 3 | + | |
- | ld a, h | + | |
- | and %00111111 | + | |
- | ld h, a ; H := H mod %00111111 | + | |
- | ; HL := HL mod 16384, to make sure | + | |
- | ; HL points at a ROM location | + | |
- | ld (romPointer), | + | |
- | pop hl | + | |
- | ; now compute the random number | + | Al final de la rutina podemos ver cómo hay una instrucción '' |
- | pop bc ; BC := AF | + | Como la semilla inicial estará definida en el código, esta rutina siempre devolverá la misma secuencia de valores, haciendo que nuestro juego obtenga números aleatorios, pero los mismos en cada partida. Para evitar esto, podemos cambiar el valor de la semilla (alojada en la instrucción '' |
- | rlc c | + | |
- | rlc b | + | |
- | ld a, (lastRandomNumber) | + | |
- | add a, b ; current reg values are " | + | |
- | add a, c ; so add them in the mix | + | |
- | add a, 47 | + | |
- | add a, d | + | |
- | add a, e | + | |
- | add a, h | + | |
- | add a, l | + | |
- | ld hl, romPointer | + | \\ |
- | add a, (hl) ; the contents of the ROM are " | + | <code z80> |
- | | + | ; A = semilla a utilizar para Rand8 |
- | ld hl, lastRandomNumber | + | SetRand8Seed: |
- | ld (hl), a | + | ld (Rand8+1), a ; Establecemos semilla A |
+ | ret | ||
+ | </ | ||
+ | \\ | ||
+ | Y ahora, en nuestro programa, establecer el valor de esa semilla por ejemplo cuando el usuario pulse la tecla para iniciar el juego, y también cambiarla en medio de la partida con valores como por ejemplo: el valor de un determinado registro que no sea el mismo en cada partida, el valor del registro '' | ||
+ | |||
+ | \\ | ||
+ | <code z80> | ||
+ | ld a, r ; El registro R cambia con cada opcode | ||
+ | call SetRand8Seed | ||
+ | </ | ||
+ | |||
+ | De la misma forma, la siguiente rutina proporciona un número aleatorio de 16 bits, lo cual hace que la serie de posibles resultados sea mucho mayor (65535 valores) antes de volver a repetir valores de la secuencia. | ||
+ | |||
+ | \\ | ||
+ | <code z80> | ||
+ | ; Rand16 | ||
+ | ; Input: none | ||
+ | ; Output: HL = pseudo-random number, period 65536 | ||
+ | ; From: https:// | ||
+ | |||
+ | RAND16_SEED | ||
+ | |||
+ | Rand16: | ||
+ | ld de, RAND16_SEED | ||
+ | ld a, d | ||
+ | ld h, e | ||
+ | ld l, 253 | ||
+ | or a | ||
+ | sbc hl, de | ||
+ | sbc a, 0 | ||
+ | sbc hl, de | ||
+ | ld d, 0 | ||
+ | sbc a, d | ||
+ | ld e, a | ||
+ | sbc hl, de | ||
+ | jr nc, rand16end | ||
+ | inc hl | ||
+ | rand16end: | ||
+ | ld (Rand16+1), hl ; Código automodificable | ||
+ | ret | ||
+ | | ||
+ | ; HL = semilla a utilizar para Rand16 | ||
+ | SetRand16Seed: | ||
+ | ld (Rand16+1), hl ; Establecemos semilla HL | ||
ret | ret | ||
</ | </ | ||
+ | \\ | ||
+ | |||
+ | Aparte de estos 2 ejemplos, hay gran cantidad de rutinas que proporcionan números aleatorios por diferentes métodos. | ||
+ | |||
+ | Una rutina bastante rápida es la basada en el algoritmo '' | ||
- | Una rutina | + | Este algoritmo es el usado por, entre otros, la rutina |
<code z80> | <code z80> | ||
Línea 672: | Línea 696: | ||
; some alternative shift triplets which also perform well are: | ; some alternative shift triplets which also perform well are: | ||
; 6, 7, 13; 7, 9, 13; 9, 7, 13. | ; 6, 7, 13; 7, 9, 13; 9, 7, 13. | ||
- | xrnd: | + | |
- | ld hl, 1 ; initial seed must not be 0 | + | SRAND_SEED |
+ | |||
+ | srand: | ||
+ | ld hl, SRAND_SEED | ||
ld a, h | ld a, h | ||
rra | rra | ||
Línea 688: | Línea 715: | ||
xor h | xor h | ||
ld h, a | ld h, a | ||
- | ld (xrnd+1), hl ; Self-modifying code (alter seed) | + | ld (srand+1), hl ; Código automodificable: |
+ | ; cambiamos la semilla de la primera línea | ||
+ | ; de la subrutina, por el último valor | ||
+ | ; obtenido. | ||
ret | ret | ||
+ | | ||
+ | ; HL = semilla a utilizar para srand | ||
+ | SetSrandSeed: | ||
+ | ld (srand+1), hl ; Establecemos semilla HL | ||
+ | ret | ||
+ | | ||
</ | </ | ||
+ | \\ | ||
- | O su variación de Patrik Rak: | + | O la variación de Xorshift implementada por Patrik Rak: |
+ | \\ | ||
<code z80> | <code z80> | ||
; A = Random Number | ; A = Random Number | ||
Línea 719: | Línea 757: | ||
</ | </ | ||
+ | En el caso del Spectrum algunas rutinas se basan en que la ROM por su naturaleza es " | ||
+ | |||
+ | Podríamos haber realizado una rutina básica similar en el Spectrum simplemente usando HL para recoger un dato de la ROM usando el valor actual del registro R (que se incrementa con cada instrucción): | ||
+ | |||
+ | <code z80> | ||
+ | ; A = Numero aleatorio 8 bits | ||
+ | ; Modifica HL | ||
+ | Rand8_ROM: | ||
+ | ld a, r ; Algo de aleatoriedad | ||
+ | ld l, a ; L = R | ||
+ | and %00111111 | ||
+ | ld h, a ; H = R MOD 16384 | ||
+ | ld a, (hl) ; Nuestro número " | ||
+ | ret | ||
+ | | ||
+ | ; DE = Numero aleatorio 16 bits | ||
+ | ; Modifica A, HL | ||
+ | Rand16_ROM: | ||
+ | ld a, r ; Algo de aleatoriedad | ||
+ | ld l, a ; L = R | ||
+ | and %00111111 | ||
+ | ld h, a ; H = R MOD 16384 | ||
+ | ld d, (hl) | ||
+ | inc l | ||
+ | ld e, (hl) ; Nuestro número " | ||
+ | ret | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | Estas rutinas tienen la desventaja de que dependen de la existencia de la ROM (no valen para desarrollar juegos en cartucho de IF2, donde no hay ROM) y que hay algunas zonas de la rom " | ||
+ | |||
+ | La rutina que veremos a continuación, | ||
+ | |||
+ | <code z80> | ||
+ | ; | ||
+ | ; random.asm - part of the ZX Spectrum libzx | ||
+ | ; library by Sebastian Mihai, 2016 | ||
+ | ; | ||
+ | |||
+ | ; our random numbers are, in part, based on reading bytes | ||
+ | ; from the 16kb ROM, whose contents are " | ||
+ | lastRandomNumber | ||
+ | romPointer | ||
+ | |||
+ | ; Gets an 8-bit random number. | ||
+ | ; It is computed using a combination of: | ||
+ | ; - the last returned random number | ||
+ | ; - a byte from ROM, in increasing order | ||
+ | ; - current values of various registers | ||
+ | ; - a flat incremented value | ||
+ | ; | ||
+ | ; Output: | ||
+ | ; A - next random number | ||
+ | get_next_random: | ||
+ | push af | ||
+ | |||
+ | push hl | ||
+ | ; advance ROM pointer | ||
+ | ld hl, romPointer | ||
+ | ld c, (hl) | ||
+ | inc hl | ||
+ | ld b, (hl) ; BC := word (romPointer) | ||
+ | ld hl, 3 | ||
+ | add hl, bc ; HL := ROM pointer advanced by 3 | ||
+ | ld a, h | ||
+ | and %00111111 | ||
+ | ld h, a ; H := H mod %00111111 | ||
+ | ; HL := HL mod 16384, to make sure | ||
+ | ; HL points at a ROM location | ||
+ | ld (romPointer), | ||
+ | pop hl | ||
+ | |||
+ | ; now compute the random number | ||
+ | |||
+ | pop bc ; BC := AF | ||
+ | rlc c | ||
+ | rlc b | ||
+ | ld a, (lastRandomNumber) | ||
+ | add a, b ; current reg values are " | ||
+ | add a, c ; so add them in the mix | ||
+ | add a, 47 | ||
+ | add a, d | ||
+ | add a, e | ||
+ | add a, h | ||
+ | add a, l | ||
+ | |||
+ | ld hl, romPointer | ||
+ | add a, (hl) ; the contents of the ROM are " | ||
+ | ; so add it in the mix | ||
+ | ld hl, lastRandomNumber | ||
+ | ld (hl), a ; save this number | ||
+ | |||
+ | ret | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | En realidad, podríamos llenar un capítulo entero de rutinas diferentes para generar números aleatorios, ya que hay decenas en la red usando diferentes técnicas. Por ahora, podemos utilizar cualquiera de las vistas anteriormente en nuestros programas y obtener una más que decente aleatoriedad, | ||
\\ | \\ | ||
Línea 725: | Línea 860: | ||
Finalmente, veamos 2 rutinas de WikiTi para " | Finalmente, veamos 2 rutinas de WikiTi para " | ||
- | Primero, veamos la rutina | + | Primero veamos la rutina |
+ | \\ | ||
<code z80> | <code z80> | ||
; input: b | ; input: b | ||
Línea 750: | Línea 886: | ||
ret | ret | ||
</ | </ | ||
+ | \\ | ||
- | A continuación, podemos ver la versión optimizada por **calmaniac84** que tarda sólo 66 ciclos de reloj y ocupa 17 bytes: | + | Sin embargo, como hemos visto en otras rutinas, siempre se pueden abordar los problemas con algoritmos más óptimos con la idea de ahorrar espacio, ahorrar tiempo de ejecución, o ambos. |
+ | \\ | ||
<code z80> | <code z80> | ||
;- Reverse a | ;- Reverse a | ||
Línea 781: | Línea 919: | ||
===== Enlaces ===== | ===== Enlaces ===== | ||
+ | * [[https:// | ||
* [[http:// | * [[http:// | ||
* [[https:// | * [[https:// | ||
Línea 791: | Línea 930: | ||
* [[https:// | * [[https:// | ||
* [[https:// | * [[https:// | ||
+ | * [[https:// | ||
* [[http:// | * [[http:// | ||
* [[http:// | * [[http:// | ||
Línea 796: | Línea 936: | ||
\\ | \\ | ||
- | **[ [[.: | + | **[ [[.: |