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Última revisiónAmbos lados, revisión siguiente | ||
cursos:ensamblador:lenguaje_4 [16-01-2024 15:21] – [La pila y las llamadas a subrutinas] sromero | cursos:ensamblador:lenguaje_4 [19-01-2024 07:31] – sromero | ||
---|---|---|---|
Línea 25: | Línea 25: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
(...) ; Operamos con BC | (...) ; Operamos con BC | ||
- | | + | |
- | | + | |
- | ; (recordemos que no existe "LD HL, BC", de modo que | + | ; (recordemos que no existe "ld hl, bc", de modo que |
; lo almacenamos como HL = 0+BC | ; lo almacenamos como HL = 0+BC | ||
- | | + | |
; recuperamos el valor que tenia BC (1000). | ; recuperamos el valor que tenia BC (1000). | ||
</ | </ | ||
- | La instrucción '' | + | La instrucción '' |
La realidad es que //el Spectrum no tiene una zona de memoria especial o aislada de la RAM dedicada a la pila. En su lugar se utiliza la misma RAM// del Spectrum (0-65535). | La realidad es que //el Spectrum no tiene una zona de memoria especial o aislada de la RAM dedicada a la pila. En su lugar se utiliza la misma RAM// del Spectrum (0-65535). | ||
Línea 57: | Línea 57: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
+ | (Nota: como veremos más adelante, para poner la pila al final de la memoria en realidad hay que ponerla en $0000, pero por motivos didácticos y para simplificar la explicación, | ||
+ | |||
Si ahora hacemos: | Si ahora hacemos: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 72: | Línea 74: | ||
< | < | ||
SP = SP - 2 = 65533 | SP = SP - 2 = 65533 | ||
- | (SP) = BC = $00FF | + | (SP) = BC = $00ff |
</ | </ | ||
Línea 80: | Línea 82: | ||
Celdilla | Celdilla | ||
| | ||
- | | + | |
SP -> 65533 $00 | SP -> 65533 $00 | ||
</ | </ | ||
Línea 87: | Línea 89: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 94: | Línea 96: | ||
< | < | ||
SP = SP - 2 = 65531 | SP = SP - 2 = 65531 | ||
- | (SP) = DE = $AABB | + | (SP) = DE = $aabb |
</ | </ | ||
Línea 102: | Línea 104: | ||
Celdilla | Celdilla | ||
| | ||
- | | + | |
| | ||
- | | + | |
- | SP -> 65531 $BB | + | SP -> 65531 $bb |
</ | </ | ||
Línea 111: | Línea 113: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 117: | Línea 119: | ||
< | < | ||
- | DE = (SP) = $AABB | + | DE = (SP) = $aabb |
SP = SP + 2 = 65533 | SP = SP + 2 = 65533 | ||
</ | </ | ||
Línea 126: | Línea 128: | ||
Celdilla | Celdilla | ||
| | ||
- | | + | |
SP -> 65533 $00 | SP -> 65533 $00 | ||
</ | </ | ||
- | Como podemos ver, **PUSH apila valores**, haciendo decrecer el valor de SP, mientras que **POP recupera valores**, haciendo crecer (en 2 bytes, 16 bits) el valor de SP. | + | Como podemos ver, **PUSH apila valores**, haciendo decrecer el valor de SP, mientras que **pop recupera valores**, haciendo crecer (en 2 bytes, 16 bits) el valor de SP. |
Con el objetivo de que el ejemplo fuera más comprensible, | Con el objetivo de que el ejemplo fuera más comprensible, | ||
Línea 141: | Línea 143: | ||
Así pues, podemos hacer **PUSH** y **POP** de los siguientes registros: | Así pues, podemos hacer **PUSH** y **POP** de los siguientes registros: | ||
- | * PUSH: AF, BC, DE, HL, IX, IY | + | * '' |
- | * POP : AF, BC, DE, HL, IX, IY | + | * '' |
Lo que hacen PUSH y POP, tal y como funciona la pila, es: | Lo que hacen PUSH y POP, tal y como funciona la pila, es: | ||
< | < | ||
- | PUSH xx : | + | push xx : |
| | ||
(SP) = xx | (SP) = xx | ||
- | POP xx : | + | pop xx : |
xx = (SP) | xx = (SP) | ||
SP = SP+2 | SP = SP+2 | ||
Línea 162: | Línea 164: | ||
| | ||
| | ||
- | POP xx |- - - - - -| | + | pop xx |- - - - - -| |
- | PUSH xx |- - - - - -| | + | push xx |- - - - - -| |
</ | </ | ||
- | | + | |
\\ | \\ | ||
Línea 178: | Línea 180: | ||
<code z80> | <code z80> | ||
- | | + | |
- | (código) | + | (código) |
- | | + | |
</ | </ | ||
Línea 188: | Línea 190: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
bucle: | bucle: | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 202: | Línea 204: | ||
<code basic> | <code basic> | ||
- | FOR I=0 TO 20: | + | For i=0 TO 20: |
FOR J=0 TO 100: | FOR J=0 TO 100: | ||
CODIGO | CODIGO | ||
Línea 212: | Línea 214: | ||
<code z80> | <code z80> | ||
- | | + | |
bucle_externo: | bucle_externo: | ||
- | | + | |
- | | + | |
bucle_interno: | bucle_interno: | ||
(... código ...) | (... código ...) | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 229: | Línea 231: | ||
<code z80> | <code z80> | ||
- | | + | |
bucle_externo: | bucle_externo: | ||
- | | + | |
- | | + | |
bucle_interno: | bucle_interno: | ||
- | (... código ...) ; En este codigo no podemos usar D | + | (... código ...) |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 253: | Línea 255: | ||
\\ | \\ | ||
- | * Para manipular el registro F: La instrucción '' | + | * Para manipular el registro F: La instrucción '' |
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 269: | Línea 271: | ||
; Simulando "EX DE, BC" | ; Simulando "EX DE, BC" | ||
| | ||
- | | + | |
- | | + | |
- | | + | |
- | ; ahora BC=(valor apilado en PUSH DE) | + | |
- | | + | |
- | ; ahora DE=(valor apilado en PUSH BC) | + | |
</ | </ | ||
Línea 280: | Línea 282: | ||
<code z80> | <code z80> | ||
- | ; Simulando "EX DE, BC" | + | ; Simulando "ex de, bc" |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 290: | Línea 292: | ||
<code z80> | <code z80> | ||
- | EX (SP), HL | + | ex (sp), hl |
- | EX (SP), IX | + | ex (sp), ix |
- | EX (SP), IY | + | ex (sp), iy |
</ | </ | ||
Línea 300: | Línea 302: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 309: | Línea 311: | ||
|< 50% 50% 50% >| | |< 50% 50% 50% >| | ||
^ Instrucción incorrecta ^ Alternativa ^ | ^ Instrucción incorrecta ^ Alternativa ^ | ||
- | | EX BC, HL | PUSH BC\\ EX (SP), HL\\ POP BC | | + | | ex bc, hl | push bc\\ ex (sp), hl\\ pop bc | |
- | | EX BC, IX | PUSH BC\\ EX (SP), IX\\ POP BC | | + | | ex bc, ix | push bc\\ ex (sp), ix\\ pop bc | |
- | | EX BC, IY | PUSH BC\\ EX (SP), IY\\ POP BC | | + | | ex bc, iy | push bc\\ ex (sp), iy\\ pop bc | |
- | | EX AF, HL | PUSH AF\\ EX (SP), HL\\ POP AF | | + | | ex af, hl | push af\\ ex (sp), hl\\ pop af | |
- | | EX AF, IX | PUSH AF\\ EX (SP), IX\\ POP AF | | + | | ex af, ix | push af\\ ex (sp), ix\\ pop af | |
- | | EX AF, IY | PUSH AF\\ EX (SP), IY\\ POP AF | | + | | ex af, iy | push af\\ ex (sp), iy\\ pop af | |
- | | EX DE, IX | PUSH DE\\ EX (SP), IX\\ POP DE | | + | | ex de, ix | push de\\ ex (sp), ix\\ pop de | |
- | | EX DE, IY | PUSH DE\\ EX (SP), IY\\ POP DE | | + | | ex de, iy | push de\\ ex (sp), iy\\ pop de | |
\\ | \\ | ||
Línea 328: | Línea 330: | ||
* Dado que la pila decrece en memoria, tenemos que tener cuidado con el valor de SP y la posición más alta de memoria donde hayamos almacenado datos o rutinas. Si ponemos un gráfico o una rutina cerca del valor inicial de SP, y realizamos muchas operaciones de PUSH, podemos sobreescribir nuestros datos con los valores que estamos apilando. | * Dado que la pila decrece en memoria, tenemos que tener cuidado con el valor de SP y la posición más alta de memoria donde hayamos almacenado datos o rutinas. Si ponemos un gráfico o una rutina cerca del valor inicial de SP, y realizamos muchas operaciones de PUSH, podemos sobreescribir nuestros datos con los valores que estamos apilando. | ||
- | * Hacer más '' | + | * Hacer más '' |
* Ampliando la regla anterior, hay que tener cuidado con los bucles a la hora de hacer '' | * Ampliando la regla anterior, hay que tener cuidado con los bucles a la hora de hacer '' | ||
- | * Finalmente, no hay que asumir que SP tiene un valor correcto para nosotros. Tal vez tenemos planeado usar una zona de la memoria para guardar datos o subrutinas y el uso de '' | + | * Finalmente, no hay que asumir que SP tiene un valor correcto para nosotros. Tal vez tenemos planeado usar una zona de la memoria para guardar datos o subrutinas y el uso de '' |
| | ||
Línea 339: | Línea 341: | ||
; Este programa se colgará (probablemente, | ; Este programa se colgará (probablemente, | ||
; pero en cualquier caso, no seguirá su ejecución normal. | ; pero en cualquier caso, no seguirá su ejecución normal. | ||
- | | + | |
- | | + | |
(código) | (código) | ||
- | | + | |
- | | + | |
; a la que teníamos que volver, volveremos a | ; a la que teníamos que volver, volveremos a | ||
; la dirección apuntada por el valor de BC, que | ; la dirección apuntada por el valor de BC, que | ||
Línea 355: | Línea 357: | ||
<code z80> | <code z80> | ||
bucle: | bucle: | ||
- | | + | |
(código que usa B) | (código que usa B) | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 367: | Línea 369: | ||
<code z80> | <code z80> | ||
bucle: | bucle: | ||
- | | + | |
(código) | (código) | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 377: | Línea 379: | ||
<code z80> | <code z80> | ||
- | | + | |
bucle: | bucle: | ||
(código) | (código) | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 403: | Línea 405: | ||
Otra opción segura es acotar en nuestro programa un espacio con DB / DS donde alojar la pila, en cualquier punto del mismo (al principio, al final, o en medio, no importa, siempre que no nos salgamos con PUSHes del espacio que le hemos dejado). En ese caso el problema es que ese " | Otra opción segura es acotar en nuestro programa un espacio con DB / DS donde alojar la pila, en cualquier punto del mismo (al principio, al final, o en medio, no importa, siempre que no nos salgamos con PUSHes del espacio que le hemos dejado). En ese caso el problema es que ese " | ||
- | La última opción es poner SP a 0, con lo que decrecerá desde el final de la RAM. Recuerda que cuando hacemos PUSH, primero se decrementa SP y luego se guardan los valores en memoria, por lo que SP = 0 usará $FFFE para el byte menos significativo y $FFFF para el más significativo (recordemos que el Z80 es Low-Endian). | + | La última opción es poner SP a 0, con lo que decrecerá desde el final de la RAM. Recuerda que cuando hacemos PUSH, primero se decrementa SP y luego se guardan los valores en memoria, por lo que SP = 0 usará $fffe para el byte menos significativo y $ffff para el más significativo (recordemos que el Z80 es Low-Endian). |
- | Pero si el target de nuestro programa es un modelo 128K y vamos a paginar, entonces el stack tiene que estar por debajo de $C000 ya que si no, al cambiar de banco lo perderíamos hasta volver al mismo (a menos que tengamos controlado que nuestro código no va a hacer ningún PUSH/POP hasta volver a poner el banco que tenía la pila, y además tengamos deshabilitadas las interrupciones). | + | Pero si el target de nuestro programa es un modelo 128K y vamos a paginar, entonces el stack tiene que estar por debajo de $c000 ya que si no, al cambiar de banco lo perderíamos hasta volver al mismo (a menos que tengamos controlado que nuestro código no va a hacer ningún PUSH/pop hasta volver a poner el banco que tenía la pila, y además tengamos deshabilitadas las interrupciones). |
Por lo tanto, mantenemos la recomendación que hicimos en los primeros capítulos del curso de dejar la pila por debajo de nuestro programa, con un '' | Por lo tanto, mantenemos la recomendación que hicimos en los primeros capítulos del curso de dejar la pila por debajo de nuestro programa, con un '' | ||
Línea 431: | Línea 433: | ||
Esto no tiene por qué ser un problema (de hecho, puede ser inapreciable) salvo que tengamos que hacer rutinas muy precisas o que estemos desarrollando un juego y necesitemos arañar hasta el último ciclo de reloj. Además, el problema no es que un PUSH/POP sea unos ciclos de reloj más lento en " | Esto no tiene por qué ser un problema (de hecho, puede ser inapreciable) salvo que tengamos que hacer rutinas muy precisas o que estemos desarrollando un juego y necesitemos arañar hasta el último ciclo de reloj. Además, el problema no es que un PUSH/POP sea unos ciclos de reloj más lento en " | ||
- | Otro punto que puede afectar a esto es si tenemos la pila en la contended memory y estamos haciendo un programa en C puro con Z88DK o en C con funciones en ensamblador, | + | Otro punto que puede afectar a esto es si tenemos la pila en la contended memory y estamos haciendo un programa en C puro con Z88DK o en C con funciones en ensamblador, |
No debemos obsesionarnos con el hecho de que la pila esté en la memoria en contienda, pero está bien saberlo y tenerlo en cuenta. | No debemos obsesionarnos con el hecho de que la pila esté en la memoria en contienda, pero está bien saberlo y tenerlo en cuenta. | ||
Línea 449: | Línea 451: | ||
El lector podría preguntar, ¿por qué no utilizar las instrucciones de salto '' | El lector podría preguntar, ¿por qué no utilizar las instrucciones de salto '' | ||
- | | + | |
<code z80> | <code z80> | ||
Línea 459: | Línea 461: | ||
SUMA_A_10: | SUMA_A_10: | ||
- | ADD A, 10 ; A = A + 10 | + | add a, 10 ; A = A + 10 |
- | LD B, A ; B = A | + | ld b, a ; B = A |
</ | </ | ||
Línea 468: | Línea 470: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
volver1: | volver1: | ||
Línea 478: | Línea 480: | ||
; Nota: Modifica el valor de A | ; Nota: Modifica el valor de A | ||
SUMA_A_10: | SUMA_A_10: | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
En este caso, cargaríamos A con el valor 35, saltaríamos a la subrutina, sumaríamos 10 a A (pasando a valer 45), haríamos B = 45, y volveríamos al lugar posterior al punto de llamada. | En este caso, cargaríamos A con el valor 35, saltaríamos a la subrutina, sumaríamos 10 a A (pasando a valer 45), haríamos B = 45, y volveríamos al lugar posterior al punto de llamada. | ||
- | Pero ... ¿qué pasaría si quisieramos volver a llamar a la subrutina desde otro punto de nuestro programa? Que sería inviable, porque nuestra subrutina acaba con un '' | + | Pero ... ¿qué pasaría si quisieramos volver a llamar a la subrutina desde otro punto de nuestro programa? Que sería inviable, porque nuestra subrutina acaba con un '' |
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
volver1: | volver1: | ||
- | | + | |
- | | + | |
; Nunca llegariamos a volver aqui | ; Nunca llegariamos a volver aqui | ||
(...) | (...) | ||
SUMA_A_10: | SUMA_A_10: | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | Para evitar ese enorme problema es para lo que se usa **CALL** y **RET**. | + | Para evitar ese enorme problema es para lo que se usa **call** y **ret**. |
Línea 508: | Línea 510: | ||
===== Uso de CALL y RET ===== | ===== Uso de CALL y RET ===== | ||
- | '' | + | '' |
- | ¿Y para qué sirve eso? Para que lo aprovechemos dentro de nuestra subrutina con '' | + | ¿Y para qué sirve eso? Para que lo aprovechemos dentro de nuestra subrutina con '' |
Son, por tanto, el equivalente ensamblador de '' | Son, por tanto, el equivalente ensamblador de '' | ||
< | < | ||
- | CALL NN equivale a: | + | call NN equivale a: |
PUSH PC | PUSH PC | ||
- | | + | |
- | RET equivale a: | + | ret equivale a: |
POP PC | POP PC | ||
</ | </ | ||
Línea 526: | Línea 528: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
(...) | (...) | ||
SUMA_A_10: | SUMA_A_10: | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | En esta ocasión, cuando ejecutamos el primer '' | + | En esta ocasión, cuando ejecutamos el primer '' |
- | Al acabar la subrutina encontramos el '' | + | Al acabar la subrutina encontramos el '' |
Al hablar de la pila os contamos lo importante que era mantener la misma cantidad de PUSH que de POPs en nuestro código. Ahora entenderéis por qué: si dentro de una subrutina hacéis un '' | Al hablar de la pila os contamos lo importante que era mantener la misma cantidad de PUSH que de POPs en nuestro código. Ahora entenderéis por qué: si dentro de una subrutina hacéis un '' | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
SUMA_A_10: | SUMA_A_10: | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
; sino " | ; sino " | ||
</ | </ | ||
Línea 569: | Línea 571: | ||
| | ||
| | ||
- | CALL NN |- - - - - -| | + | call NN |- - - - - -| |
- | RET |- - - - - -| | + | ret |- - - - - -| |
</ | </ | ||
Línea 578: | Línea 580: | ||
La instrucción **RST** es un resquicio de la compatibilidad que el Z80 tiene con el procesador 8080. | La instrucción **RST** es un resquicio de la compatibilidad que el Z80 tiene con el procesador 8080. | ||
- | '' | + | '' |
Las siguientes instrucciones son equivalentes en cuanto a resultado de la ejecución: | Las siguientes instrucciones son equivalentes en cuanto a resultado de la ejecución: | ||
Línea 584: | Línea 586: | ||
|< 40% 50% 50% >| | |< 40% 50% 50% >| | ||
^ CALL ^ RST ^ | ^ CALL ^ RST ^ | ||
- | | CALL $0000 | RST $00 | | + | | call $0000 | rst $00 | |
- | | CALL $0008 | RST $08 | | + | | call $0008 | rst $08 | |
- | | CALL $0010 | RST $10 | | + | | call $0010 | rst $10 | |
- | | CALL $0018 | RST $18 | | + | | call $0018 | rst $18 | |
- | | CALL $0020 | RST $20 | | + | | call $0020 | rst $20 | |
- | | CALL $0028 | RST $28 | | + | | call $0028 | rst $28 | |
- | | CALL $0030 | RST $30 | | + | | call $0030 | rst $30 | |
- | | CALL $0038 | RST $38 | | + | | call $0038 | rst $38 | |
La principal ventaja de '' | La principal ventaja de '' | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | Por contra, | + | Por contra, |
La ventaja de tener estas instrucciones de salto de 1 sólo byte es que un programador puede colocar en estas direcciones rutinas que sean muy comunes de usar ($0008, $0010, etc), ahorrando 2 bytes en cada llamada que después se hagan a ellas. | La ventaja de tener estas instrucciones de salto de 1 sólo byte es que un programador puede colocar en estas direcciones rutinas que sean muy comunes de usar ($0008, $0010, etc), ahorrando 2 bytes en cada llamada que después se hagan a ellas. | ||
- | En el caso del Spectrum, estas direcciones de memoria caen en la ROM (no así en otros ordenadores que tienen la ROM al final, por ejemplo), por lo que no las podemos aprovechar en nuestros programas, aunque ya lo hicieron por nosotros los diseñadores de la ROM del Spectrum al colocar en esas direcciones de salto puntos de entrada a rutinas tan comunes como RST 16 (RST $10) que sirve, como ya hemos visto, para imprimir un carácter. | + | En el caso del Spectrum, estas direcciones de memoria caen en la ROM (no así en otros ordenadores que tienen la ROM al final, por ejemplo), por lo que no las podemos aprovechar en nuestros programas, aunque ya lo hicieron por nosotros los diseñadores de la ROM del Spectrum al colocar en esas direcciones de salto puntos de entrada a rutinas tan comunes como rst 16 (rst $10) que sirve, como ya hemos visto, para imprimir un carácter. |
\\ | \\ | ||
===== Saltos y retornos condicionales ===== | ===== Saltos y retornos condicionales ===== | ||
- | Una de las peculiaridades de CALL y RET es que tienen instrucciones condicionales con respecto al estado de los flags, igual que '' | + | Una de las peculiaridades de call y ret es que tienen instrucciones condicionales con respecto al estado de los flags, igual que '' |
Para eso, utilizamos las siguientes instrucciones: | Para eso, utilizamos las siguientes instrucciones: | ||
\\ | \\ | ||
- | * **CALL flag, NN** : Salta sólo si FLAG está activo. | + | * **call flag, NN** : Salta sólo si FLAG está activo. |
- | * **RET flag** : Vuelve sólo si FLAG está activo. | + | * **ret flag** : Vuelve sólo si FLAG está activo. |
\\ | \\ | ||
Línea 637: | Línea 639: | ||
; lo primero, comprobamos que BC no sea cero: | ; lo primero, comprobamos que BC no sea cero: | ||
- | | + | |
- | | + | |
; Si BC es cero, activará el flag Z | ; Si BC es cero, activará el flag Z | ||
- | | + | |
(más código) | (más código) | ||
- | ; Aquí seguiremos si BC no es cero, el RET no se habrá ejecutado. | + | ; Aquí seguiremos si BC no es cero, el ret no se habrá ejecutado. |
</ | </ | ||
Línea 654: | Línea 656: | ||
| | ||
| | ||
- | CALL cc, NN |- - - - - -| IF cc CALL NN | + | call cc, NN |- - - - - -| IF cc call NN |
- | RET cc |- - - - - -| IF cc RET | + | ret cc |- - - - - -| IF cc ret |
</ | </ | ||
Línea 675: | Línea 677: | ||
<code z80> | <code z80> | ||
; | ; | ||
- | ; MULTIPLI: Multiplica DE*BC | + | ; Mult_HL_DE: Multiplica DE*BC |
- | ; | + | ; |
- | ; | + | ; Entrada: |
- | ; | + | ; |
- | ; | + | ; Salida: |
+ | ; Modifica: | ||
; | ; | ||
- | MULTIPLICA: | + | Mult_HL_DE: |
- | | + | |
- | | + | |
- | | + | |
- | MULTI01: | + | multiloop_01: |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | Antes de hacer la llamada a MULTIPLICA, tendremos que cargar en DE y en BC los valores que queremos multiplicar, | + | Antes de hacer la llamada a '' |
| | ||
Línea 715: | Línea 718: | ||
<code z80> | <code z80> | ||
MiFuncion: | MiFuncion: | ||
- | | + | |
- | | + | |
(...) | (...) | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 729: | Línea 732: | ||
Habrá casos en que no será necesario ponerlos. Si por ejemplo tenemos una función que ejecutamos al inicio del programa para, por ejemplo, precalcular algunos datos, no necesitaremos que preserve registros ya que sus valores al llamarlas no son importantes. También, en funciones muy críticas y que necesitan ser rápidas, en ocasiones no preservaremos los registros en ellas y lo que haremos será cerciorarnos en el código que hace la llamada que estas no modifican ningún registro que sea importante para nosotros en esa parte del código. | Habrá casos en que no será necesario ponerlos. Si por ejemplo tenemos una función que ejecutamos al inicio del programa para, por ejemplo, precalcular algunos datos, no necesitaremos que preserve registros ya que sus valores al llamarlas no son importantes. También, en funciones muy críticas y que necesitan ser rápidas, en ocasiones no preservaremos los registros en ellas y lo que haremos será cerciorarnos en el código que hace la llamada que estas no modifican ningún registro que sea importante para nosotros en esa parte del código. | ||
- | No nos olvidemos de que en algunos casos (muy pocos normalmente) podemos usar el juego de registros alternativos ('' | + | No nos olvidemos de que en algunos casos (muy pocos normalmente) podemos usar el juego de registros alternativos ('' |
\\ | \\ | ||
Línea 739: | Línea 742: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
(...) | (...) | ||
MiFuncion: | MiFuncion: | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | (Codigo) | + | (... código ...) |
- | | + | |
- | | + | |
- | | + | |
x DB 0 | x DB 0 | ||
Línea 781: | Línea 784: | ||
En C (y en otros lenguajes de programación) los parámetros se insertan en la pila en el orden en que son leídos. La subrutina después lee los valores sin desapilarlos, | En C (y en otros lenguajes de programación) los parámetros se insertan en la pila en el orden en que son leídos. La subrutina después lee los valores sin desapilarlos, | ||
- | En ese caso, simplemente apilamos los parámetros con PUSH y dentro de la rutina los vamos recogiendo con POP: | + | En ese caso, simplemente apilamos los parámetros con '' |
| | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
(...) | (...) | ||
Rutina: | Rutina: | ||
- | | + | |
; (trabajamos con HL) | ; (trabajamos con HL) | ||
Línea 799: | Línea 802: | ||
; Ahora recogemos el parametro 2, | ; Ahora recogemos el parametro 2, | ||
; en HL o en cualquier otro registro | ; en HL o en cualquier otro registro | ||
- | | + | |
; (hacemos calculos con HL) | ; (hacemos calculos con HL) | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 813: | Línea 816: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
Rutina: | Rutina: | ||
- | | + | |
- | | + | |
; en la pila por el compilador (valor de Y) | ; en la pila por el compilador (valor de Y) | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
;;; (ahora hacemos lo que queramos en asm) | ;;; (ahora hacemos lo que queramos en asm) | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 842: | Línea 845: | ||
<code z80> | <code z80> | ||
Rutina: | Rutina: | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | ; Aqui no necesitamos mas INC HL, no hay mas parametros | + | ; Aqui no necesitamos mas inc hl, no hay mas parametros |
; Nuestra rutina empieza a trabajar aquí | ; Nuestra rutina empieza a trabajar aquí | ||
Línea 879: | Línea 882: | ||
<code z80> | <code z80> | ||
BuclePrincipal: | BuclePrincipal: | ||
- | | + | |
- | | + | |
- | | + | |
- | jp Bucle_Principal | + | jp BuclePrincipal |
- | Leer_Teclado: | + | LeerTeclado: |
- | | + | |
- | Logica_Juego: | + | LogicaJuego: |
- | | + | |
- | Comprobar_Estado: | + | ComprobarEstado: |
- | | + | |
</ | </ | ||
Línea 904: | Línea 907: | ||
; Probamos de diferentes formas nuestra rutina | ; Probamos de diferentes formas nuestra rutina | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
; Rutina DrawSprite | ; Rutina DrawSprite | ||
Línea 914: | Línea 917: | ||
DrawSprite: | DrawSprite: | ||
(aquí el código) | (aquí el código) | ||
- | | + | |
sprite DB 0, | sprite DB 0, | ||
Línea 921: | Línea 924: | ||
</ | </ | ||
- | | + | |
Así, vamos creando diferentes rutinas en un entorno controlado y testeable, y las vamos incorporando a nuestro programa. Si hay algún bug en una rutina y tenemos que reproducirlo, | Así, vamos creando diferentes rutinas en un entorno controlado y testeable, y las vamos incorporando a nuestro programa. Si hay algún bug en una rutina y tenemos que reproducirlo, | ||
Línea 929: | Línea 932: | ||
En ocasiones una excesiva desgranación del programa en módulos más pequeños puede dar lugar a una penalización en el rendimiento, | En ocasiones una excesiva desgranación del programa en módulos más pequeños puede dar lugar a una penalización en el rendimiento, | ||
- | Hay gente que, en lugar de esto, preferirá realizar una función específica que dibuje los 10x10 bloques dentro de una misma función. Esto es así porque de este modo te evitas 100 CALLs (10x10) y sus correspondientes RETs, lo cual puede ser importante en una rutina gráfica que se ejecute X veces por segundo. Por supuesto, en muchos casos tendrán razón, en ciertas ocasiones hay que hacer rutinas concretas para tareas concretas, aún cuando puedan repetir parte de otro código que hayamos escrito anteriormente, | + | Hay gente que, en lugar de esto, preferirá realizar una función específica que dibuje los 10x10 bloques dentro de una misma función. Esto es así porque de este modo te evitas 100 calls (10x10) y sus correspondientes RETs, lo cual puede ser importante en una rutina gráfica que se ejecute X veces por segundo. Por supuesto, en muchos casos tendrán razón, en ciertas ocasiones hay que hacer rutinas concretas para tareas concretas, aún cuando puedan repetir parte de otro código que hayamos escrito anteriormente, |
Pero si, por ejemplo, nosotros sólo dibujamos la pantalla una vez cuando nuestro personaje sale por el borde, y no volvemos a dibujar otra hasta que sale por otro borde (típico caso de juegos sin scroll que muestran pantallas completas de una sóla vez), vale la pena el usar funciones modulares dado que unos milisegundos más de ejecución en el trazado de la pantalla no afectarán al desarrollo del juego. | Pero si, por ejemplo, nosotros sólo dibujamos la pantalla una vez cuando nuestro personaje sale por el borde, y no volvemos a dibujar otra hasta que sale por otro borde (típico caso de juegos sin scroll que muestran pantallas completas de una sóla vez), vale la pena el usar funciones modulares dado que unos milisegundos más de ejecución en el trazado de la pantalla no afectarán al desarrollo del juego. | ||
Línea 967: | Línea 970: | ||
* {{cursos: | * {{cursos: | ||
* {{cursos: | * {{cursos: | ||
- | * {{cursos: | + | * {{cursos: |
* {{cursos: | * {{cursos: | ||
* {{cursos: | * {{cursos: |