cursos:ensamblador:aritmetica

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
cursos:ensamblador:aritmetica [24-01-2024 08:01] – [Enlaces] sromerocursos: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 ''ADD'', ''ADC'', ''SUB'', ''SBC'', ''NEG'', ''CPL'' y ''DAA'', así como instrucciones para rotación/desplazamiento de bits como ''RLC'', ''RRC', ''RL'', ''RR'', ''RLA'', ''RRA'', ''RLCA', ''RRCA'', ''RLD'', 'RRD', ''SRA'', ''SLA'' y ''SRL''.+El Z80 nos provee de funciones aritméticas de 8 bits básicas, como son ''ADD'', ''ADC'', ''SUB'', ''SBC'', ''NEG'', ''CPL'' y ''DAA'', así como instrucciones para rotación/desplazamiento de bits como ''RLC'', ''RRC'', ''RL'', ''RR'', ''RLA'', ''RRA'', ''RLCA'', ''RRCA'', ''RLD'', ''RRD'', ''SRA'', ''SLA'' y ''SRL''.
  
 En muchas de estas operaciones toma especial relevancia ''CF'' (el **Carry Flag**), que podemos modificar mediante ''SCF'' y ''CCF'' (o realizando atajos como ''AND A'' o ''OR A'' para sustituir a ''CCF''). En muchas de estas operaciones toma especial relevancia ''CF'' (el **Carry Flag**), que podemos modificar mediante ''SCF'' y ''CCF'' (o realizando atajos como ''AND A'' o ''OR A'' para sustituir a ''CCF'').
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), de forma que las podamos incorporar directamente en nuestros programas. 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), de forma que las podamos incorporar directamente en nuestros programas.
  
-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 ''PUSH''es y ''POP''para preservar registros, y otras no lo hagan. Debido a esto, cuando vayamos a incorporarlas en nuestros programas o librerías se recomienda que se revise la rutina para añadir o quitar la preservación de registros según la necesitemos o no.+Las rutinas se reproducen tal cual aparecen en la web, por lo que es posible que alguna de ellas incluyan varios ''PUSH'' y ''POP'' para preservar registros, y otras no lo hagan. Debido a esto, cuando vayamos a incorporarlas en nuestros programas o librerías se recomienda que se revise la rutina para añadir o quitar la preservación de registros según la necesitemos o no.
  
 Se recomienda al lector que se desarrolle su propia librería ''aritmetica.asm'' con una selección de rutinas de esta página, eligiendo aquellas rutinas que pueda necesitar en sus programas, y las aproveche en lugar de intentar "reinventar la rueda" y escribir rutinas de aritmética que puedan ser más lentas que las presentadas. Se recomienda al lector que se desarrolle su propia librería ''aritmetica.asm'' con una selección de rutinas de esta página, eligiendo aquellas rutinas que pueda necesitar en sus programas, y las aproveche en lugar de intentar "reinventar la rueda" y escribir rutinas de aritmética que puedan ser más lentas que las presentadas.
Línea 47: Línea 47:
     ld b, 0     ld b, 0
     ld c, a     ld c, a
-    adc hl, bc            ; HL = HL+A +    add hl, bc            ; HL = HL+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:
 </code> </code>
  
-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 restado 2 veces al resultado, etc.
  
  Pero volvamos a ''SLA'' para multiplicar por potencias de 2. Hemos dicho que cada vez que multiplicamos con desplazamientos (o con sumas de A), el bit más significativo (bit 7) pasa al Carry Flag. Si hacemos múltiples operaciones sin utilizar ese bit lo "perdemos", ya que el máximo valor que podemos almacenar en A es 255, así que no podemos multiplicar números muy grandes. Para valores grandes necesitaremos usar una pareja de registros de 16 bits, e ir pasando el bit saliente (el Carry Flag) al registro de la parte alta del valor:  Pero volvamos a ''SLA'' para multiplicar por potencias de 2. Hemos dicho que cada vez que multiplicamos con desplazamientos (o con sumas de A), el bit más significativo (bit 7) pasa al Carry Flag. Si hacemos múltiples operaciones sin utilizar ese bit lo "perdemos", ya que el máximo valor que podemos almacenar en A es 255, así que no podemos multiplicar números muy grandes. Para valores grandes necesitaremos usar una pareja de registros de 16 bits, e ir pasando el bit saliente (el Carry Flag) al registro de la parte alta del valor:
Línea 172: Línea 172:
 </code> </code>
  
- Al igual que pasaba con ''add aa'', las sumas de 16 bits con ''add hlhl'' pueden llegar a ser más rápidas (1 byte, y 11 ciclos de reloj) que la combinación de ''sla'' + ''rl'' (4 bytes y 16 ciclos).+ Al igual que pasaba con ''ADD AA'', las sumas de 16 bits con ''ADD HLHL'' pueden llegar a ser más rápidas (1 byte, y 11 ciclos de reloj) que la combinación de ''SLA'' + ''RL'' (4 bytes y 16 ciclos).
  
 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, entonces **los desplazamientos a la derecha equivalen a dividir por potencias de 2 (2, 4, 8...)**, mediante la instrucción ''srl'':+ Del mismo modo que desplazar a la izquierda es multiplicar, entonces **los desplazamientos a la derecha equivalen a dividir por potencias de 2 (2, 4, 8...)**, mediante la instrucción ''SRL'':
  
 <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, lo idea es utilizar algoritmos que no tengan un tiempo de ejecución que dependa linealmente de los valores a dividir.+Sin embargo, como ocurría con la multiplicación, lo ideal es utilizar algoritmos que no tengan un tiempo de ejecución que dependa linealmente de los valores a dividir.
  
-Mostraremos a continuación una rutinas finales de división que podemos incluír en nuestros programas.+Mostraremos a continuación unas rutinas finales de división que podemos incluír en nuestros programas.
  
 \\  \\ 
Línea 591: Línea 591:
 Si hay una funcionalidad que necesitaremos casi con total seguridad para desarrollar un juego es la posibilidad de generar números aleatorios. Si queremos que cada partida sea diferente y que ciertas acciones varíen (como por ejemplo, que en un juego de puzzle no aparezcan siempre las mismas piezas), necesitaremos una fuente de aleatoriedad, una rutina que nos devuelva un número diferente cada vez. Si hay una funcionalidad que necesitaremos casi con total seguridad para desarrollar un juego es la posibilidad de generar números aleatorios. Si queremos que cada partida sea diferente y que ciertas acciones varíen (como por ejemplo, que en un juego de puzzle no aparezcan siempre las mismas piezas), necesitaremos una fuente de aleatoriedad, una rutina que nos devuelva un número diferente cada vez.
  
-Hay varias formas de generar números aleatorios. La primera de ella es utilizar funciones matemáticas que nos devuelve un número diferente de una sucesión cada vez que la llamamos. El problema de las rutinas aleatorias basadas en fórmulas es que la sucesión de números aleatorios suele ser cíclico, es decir, que siempre proporcionan los mismos números y en el mismo orden, a partir de una determinada semilla.+Hay varias formas de generar números aleatorios. La primera de ella es utilizar funciones matemáticas que nos devuelven un número diferente de una sucesión cada vez que la llamamos. El problema de las rutinas aleatorias basadas en fórmulas es que la sucesión de números aleatorios suele ser cíclico, es decir, que siempre proporcionan los mismos números y en el mismo orden, a partir de una determinada semilla.
  
 Veamos una rutina muy simple (extraída de la página //Z80 bits//, de //Milos Bazelides//), que utiliza la fórmula ''x[i + 1] = (5 * x[i] + 1) mod 256''. Es muy simple y de reducido tamaño, pero la serie resultante tiene poca aleatoriedad: Veamos una rutina muy simple (extraída de la página //Z80 bits//, de //Milos Bazelides//), que utiliza la fórmula ''x[i + 1] = (5 * x[i] + 1) mod 256''. Es muy simple y de reducido tamaño, pero la serie resultante tiene poca aleatoriedad:
Línea 615: Línea 615:
 </code> </code>
  
-la rutina utiliza una semilla (RAND_SEED) que inserta en el registro A para realizar una serie de operaciones y generar el siguiente valor de la serie en A.+La rutina utiliza una semilla (RAND_SEED) que inserta en el registro A para realizar una serie de operaciones y generar el siguiente valor de la serie en A.
  
 Al final de la rutina podemos ver cómo hay una instrucción ''ld (Rand8+1),a'' la cual **modifica el propio código** para meter el valor resultante obtenido en el ''ld a, X'' inicial. De este modo, la siguiente vez que llamemos a la rutina tendremos en su primer instrucción el último valor generado, que usaremos como origen de los cálculos para obtener el siguiente. Al final de la rutina podemos ver cómo hay una instrucción ''ld (Rand8+1),a'' la cual **modifica el propio código** para meter el valor resultante obtenido en el ''ld a, X'' inicial. De este modo, la siguiente vez que llamemos a la rutina tendremos en su primer instrucción el último valor generado, que usaremos como origen de los cálculos para obtener el siguiente.
Línea 638: Línea 638:
 </code> </code>
  
-De la misma forma, la siguiente rutina proporciona un número aleatorio de 16 bits, lo cual hace que la serie de +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.
  
 \\  \\ 
Línea 677: Línea 677:
 Aparte de estos 2 ejemplos, hay gran cantidad de rutinas que proporcionan números aleatorios por diferentes métodos. Aparte de estos 2 ejemplos, hay gran cantidad de rutinas que proporcionan números aleatorios por diferentes métodos.
  
-Una rutina bastante rápidaes la basada en el algoritmo ''Xorshift'', de //George Marsaglia//, sería la siguiente:+Una rutina bastante rápida es la basada en el algoritmo ''Xorshift'', de //George Marsaglia//
 + 
 +Este algoritmo es el usado por, entre otros, la rutina ''srand()'' del compilador de C Z88DK, y es una muy buena rutina generadora de números aleatorios
  
 <code z80> <code z80>
Línea 694: 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  EQU        ; La semilla inicial **NO** debe de ser 0 
 + 
 +srand
 +    ld hl, SRAND_SEED
     ld a, h     ld a, h
     rra     rra
Línea 710: 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
 +    
 </code> </code>
 \\  \\ 
Línea 839: Línea 853:
 \\  \\ 
  
-En realidad, podríamos llegar 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, ya que alguna de ellas ha sido utilizada en juegos comerciales reales de la época.+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, ya que alguna de ellas ha sido utilizada en juegos comerciales reales de la época.
  
 \\  \\ 
Línea 922: Línea 936:
  
 \\  \\ 
-**[ [[.:indice|⬉]] | [[.:lenguaje_5|⬅]] | [[.:ensambladores|➡]] ]**+**[ [[.:indice|⬉]] | [[.:habituales|⬅]] | [[.:ensambladores|➡]] ]**
  
  • cursos/ensamblador/aritmetica.1706083275.txt.gz
  • Última modificación: 24-01-2024 08:01
  • por sromero