# Vuelve a cantarla Sam Ya sabe crear subrutinas, pero no sabe que hacer para que se repitan una y otra vez o que se ejecuten como reacción a algo. De igual forma le gustaría crear un juego que hiciera algo, no sólo mostrar cosas bonitas en pantalla y volver al BASIC... necesita crear bucles (no, no son espacio-temporales ni estamos en el día de la marmota) y también realizar acciones en base a una respuesta (es decir, un salto condicional). Los saltos son la espina dorsal de nuestros programas, pero también nuestras peor pesadilla. Hay varias formas de realizar saltos, le vamos a ir enseñando una a una empezando por la más sencilla. # Salto incondicional La forma más sencilla de realizar un bucle es mediante un salto incondicional, es decir mediante la instrucción "JP" (jump). Veamos un ejemplo: ; ------------------------------------------- ; Prueba de salto incondicional ; ------------------------------------------- ORG 32768 ; ------------------------------------------- ; Rutina principal ; ------------------------------------------- inic1 jp inic1 ret Si tecleamos, ensamblamos, y ejecutamos el ejemplo el ordenador se quedará visualmente "colgado" (que lo haga de una soga depende enteramente de usted). Lo que estamos haciendo es saltar con la línea "jp inic1" a sí misma, ya que usamos la etiqueta inic1 como sitio a donde saltar y la hemos situado en la misma línea (a esto le llamamos un programa egocéntrico, sólo se presta atención a si mismo y pasa olímpicamente del usuario). Es decir, si ponemos un salto incondicional antes del ret que apunte al inicio de la rutina principal, o a la parte que nos interesa repetir de forma indefinida, conseguiremos que nunca se retorne a BASIC. OJO, esto significa que nunca se ejecuta el RET y que si hemos realizado algún CALL y no hemos retornado la vamos a liar bien gorda... porque en cada CALL estamos usando la pila de saltos de la CPU, y si no retornamos la pila se llena indefinidamente ocupando toda la memoria y borrando el propio programa. ¿Pila? ¿no sabe lo que es la pila de la CPU? ¿no ha visto el anuncio de los conejitos de Duracell? ¿no tiene una "pila" para lavar en casa? ¿nunca ha realizado una pila de ropa? mejor le ponemos las pìlas... # La pila de la CPU La pila de la CPU es un espacio de la memoria RAM que reserva la CPU para su uso. En este espacio se guardan las posiciones de memoria a donde se debe retornar tras cada CALL. Es decir, cada llamada a subrutina nos consume 2 bytes de memoria, y cuando retornamos con RET liberamos esos 2 bytes. Si realizamos CALL dentro de más CALL (subrutinas llamando a otras subrutinas) estaremos llenando más espacio de la pila. Una imagen vale más que mil palabras: --------> Tamaño de la pila en bytes 2 4 6 8 CALL CALL CALL CALL RET RET RET RET Evidentemente no es exactamente así, ya que para usar nuestra rutina en ensamblador el BASIC ya ha usado algo de la pila, pero nos sirve para saber como funciona. La pila se situa en la parte alta de la memoria, es decir parte de 65535 y crece hacia abajo. Ocupará tanta memoria como necesite, por lo que si vemos que nuestro juego empieza a hacer cosas raras o se jode por completo es que nos falta un RET en el bucle y la pila empieza a machacar toda la memoria. No olvide dejar algo de espacio para la pila. De igual forma un RET no ejecutado significa que los retornos no se corresponden... y que nuestro juego no funcionará. Ojito. # Salto relativo Anteriormente hemos usado JP, pero también podemos usar JR de la misma forma: ; ------------------------------------------- ; Prueba de salto incondicional ; ------------------------------------------- ORG 32768 ; ------------------------------------------- ; Rutina principal ; ------------------------------------------- inic1 jr inic1 ret Como puede observar usar JP o JR parece lo mismo, de hecho realizan el mismo efecto... pero no son iguales. JP es un salto absoluto a cualquier posición de memoria, mientras que JR es un salto relativo a una posición cercana (127 bytes de diferencia). JR es más rápida que JP, eso es lo importante y a tener en cuenta, usaremos JR mientras podamos y JP cuando no nos quede más remedio o no tengamos problemas de rapidez. # Salto condicional Un salto condicional significa que sólo se realiza el salto si se da una condición, y no hay mejor forma de comprenderlo que con un ejemplo práctico. Vamos a coger un fragmento de la rutina de impresión de sprites: draw ld a,(hl) ; hl indica la posición del sprite en memoria ld (de),a ; de indica la posición de pantalla inc hl inc e ld a,(hl) ; esta parte imprime el segundo byte ld (de),a inc hl dec e inc d djnz draw ; decrementa B y si es cero deja de saltar a draw ret En este fragmento hemos tomado como base que antes de esta subrutina el registro “b” vale 8. La instrucción djnz lo que hace es decrementar “b”, compararlo con 0 y si dicha comparación es afirmativa NO salta a la etiqueta indicada. En inglés se leería como: d-j-nz, es decir decrementa (d) y salta (j) si no es cero (nz) a la etiqueta. En este ejemplo, como b vale 8 antes de comenzar, se realizan 8 ciclos desde la etiqueta draw a la instrucción ret. Es decir, imprimimos los 8 bytes que componen un carácter (en este caso la subrutina imprime dos caracteres a la vez para mayor rapidez). DJNZ es muy similar en BASIC a un bucle FOR b=x to 0... next b. Ahora veamos un salto condicional más sencillo: ; ------------------------------------------- ; Prueba de salto condicional sencillo ; ------------------------------------------- ORG 32768 ; ------------------------------------------- ; Rutina principal ; ------------------------------------------- ld a, 5 inic1 dec a jr nz, inic1 ret Esto es más sencillo de comprender. Si tecleamos y ejecutamos el programa retornaremos al BASIC sin aparentemente haber realizado nada, pero la verdad es que habremos realizado 5 bucles con la instrucción jr. Hemos decrementado el registro “a” hasta que su valor fuese 0 y jr dejara de saltar, ya que jr ha estado comparando el valor de “a” con un número distinto de cero (nz). # Un registro con bandera Estos no son los únicos tipos de salto condicional, hay más, todos afectados por las denominadas “banderas” del acumulador (el acumulador es el registro “a”). El registro “a” es el más importante de todos los de la CPU, ya que es con el que se opera y obtiene el resultado en la mayoría de los casos (por eso se le llama también acumulador). Por ser tan importante está asociado al registro F... si, no se ha vuelto loco, existe un registro F que es el que almacena las banderas (flags en inglés) del registro A. Las banderas son: signo (negativo/positivo) cero (que es el usado en djnz) acarreo (ej. cuando una suma excede el valor del registro) semi acarreo (mejor no pregunteis por el momento) paridad (cuando realizamos comparaciones entre registros) adición/substracción (no pregunteis igualmente) Se puede aprovechar de las banderas del acumulador para poder realizar saltos realmente complejos. # Comparando que es gerundio Las comparaciones se realizan con la instrucción CP, que hace uso de los flag de adición/substracción y cero: cp b ; compara a con b jr z, salt1 ; si a es igual que b salta a salt1 cp c ; compara a con c jr z, salt2 ; si a es igual que c salta a salt2 Para los que sepan BASIC les es conocida esta forma de programar, pueden usar CP como usan IF...THEN en BASIC. De igual forma que se compara “a” con otro registro en igualdad se puede usar en comparación de tamaño: cp b ; compara a con b jp p, salt1 ; si a es mayor que b salta a salt1 cp c ; compara a con c jp m, salt2 ; si a es menor que c salta a salt2 Como puede ver no es necesaria una explicación más detallada, es bien sencillo. Y hasta aquí podemos resumir por encima lo que son los saltos, porque hay para todos los gustos: JP y JR se pueden combinar con Z (cero), NZ (no cero), etc... y a su vez tenemos la eficaz DJNZ que usa el registro “b” para realizar bucles.