cursos:ensamblador:lenguaje_4

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:lenguaje_4 [19-01-2024 07:14] sromerocursos:ensamblador:lenguaje_4 [22-01-2024 07:54] (actual) – [PUSH y POP] sromero
Línea 62: Línea 62:
 </code> </code>
  
 +(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, vamos a hacer este ejemplo con ese valor).
 + 
  Si ahora hacemos:  Si ahora hacemos:
  
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 +  * ''PUSH'':  AF, BC, DE, HL, IX, IY 
-  * POP :  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:
  
 <code> <code>
-PUSH xx :+push xx :
      SP  = SP-2      SP  = SP-2
     (SP) = xx     (SP) = xx
  
-POP xx :+pop xx :
     xx   = (SP)     xx   = (SP)
     SP   = SP+2     SP   = SP+2
 +</code>
 +
 + Visto en "pseucódigo BASIC", ambos comandos serían:
 +
 +<code z80>
 +push hl  =   LET SP = SP-2
 +             POKE (SP+1), H
 +             POKE SP, L
 +
 +pop hl     LET L = PEEK SP
 +             LET H = PEEK (SP+1)
 +             LET SP = SP+2
 </code> </code>
  
Línea 162: Línea 176:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
- POP xx              |- - - - - -| + pop xx              |- - - - - -| 
- PUSH xx             |- - - - - -|+ push xx             |- - - - - -|
 </code> </code>
  
Línea 178: Línea 192:
  
 <code z80> <code z80>
-    push bc       ; Guardamos el valor de BC+    push bc          ; Guardamos el valor de BC
  
-    (código)      ; Hacemos operaciones que necesitan usar BC+    (código)         ; Hacemos operaciones que necesitan usar BC
  
-    pop bc        ; Recuperamos el valor que teníamos en BC+    pop bc           ; Recuperamos el valor que teníamos en BC
 </code> </code>
  
Línea 192: Línea 206:
  
 bucle: bucle:
-    push bc         ; Guardamos BC+    push bc          ; Guardamos BC
     ld b, 1     ld b, 1
     add a, b     add a, b
-    pop bc          ; Recuperamos BC+    pop bc           ; Recuperamos BC
     djnz bucle     djnz bucle
 </code> </code>
Línea 212: Línea 226:
  
 <code z80> <code z80>
-    ld b, 20                ; repetimos bucle externo 20 veces+    ld b, 20                 ; repetimos bucle externo 20 veces
  
 bucle_externo: bucle_externo:
-    push bc                 ; Nos guardamos el valor de BC +    push bc                  ; Nos guardamos el valor de BC 
-    ld b, 100               ; Iteraciones del bucle interno+    ld b, 100                ; Iteraciones del bucle interno
  
 bucle_interno: bucle_interno:
     (... código ...)     (... código ...)
-    djnz bucle_interno      ; FOR J=0 TO 100 +    djnz bucle_interno       ; FOR J=0 TO 100 
-    pop bc                  ; Recuperamos el valor de B+    pop bc                   ; Recuperamos el valor de B
  
-    djnz bucle_externo      ; For i=0 TO 20+    djnz bucle_externo       ; For i=0 TO 20
 </code> </code>
  
Línea 229: Línea 243:
  
 <code z80> <code z80>
-    ld b, 20                ; repetimos bucle externo 20 veces+    ld b, 20                 ; repetimos bucle externo 20 veces
  
 bucle_externo: bucle_externo:
-    ld d, b                 ; Nos guardamos el valor de B+    ld d, b                  ; Nos guardamos el valor de B
  
-    ld b, 100               ; Iteraciones del bucle interno+    ld b, 100                ; Iteraciones del bucle interno
  
 bucle_interno: bucle_interno:
-    (... código ...)        ; En este codigo no podemos usar D +    (... código ...)         ; En este codigo no podemos usar D 
-    djnz bucle_interno      ; FOR J=0 TO 100+    djnz bucle_interno       ; FOR J=0 TO 100
  
-    ld b, d                 ; Recuperamos el valor de B +    ld b, d                  ; Recuperamos el valor de B 
-    djnz bucle_externo      ; For i=0 TO 20+    djnz bucle_externo       ; For i=0 TO 20
 </code> </code>
  
Línea 247: Línea 261:
  
 \\  \\ 
-  * Como veremos en el próximo apartado, la pila es la clave de las subrutinas (''call''/''RET'') en el Spectrum (equivalente al ''GOSUB''/''RETURN'' de BASIC).+  * Como veremos en el próximo apartado, la pila es la clave de las subrutinas (''CALL''/''RET'') en el Spectrum (equivalente al ''GOSUB''/''RETURN'' de BASIC).
  
 \\  \\ 
Línea 269: Línea 283:
     ; Simulando "EX DE, BC"     ; Simulando "EX DE, BC"
          
-    push bc       ; Apilamos BC +    push bc          ; Apilamos BC 
-    push de       ; Apilamos DE +    push de          ; Apilamos DE 
-    pop bc        ; Desapilamos BC +    pop bc           ; Desapilamos BC 
-                  ; ahora BC=(valor apilado en push de) +                     ; ahora BC=(valor apilado en push de) 
-    pop de        ; Desapilamos DE +    pop de           ; Desapilamos DE 
-                  ; ahora DE=(valor apilado en push bc)+                     ; ahora DE=(valor apilado en push bc)
 </code> </code>
  
Línea 280: Línea 294:
  
 <code z80> <code z80>
-    ; Simulando "EX DEBC"+    ; Simulando "ex debc"
     push hl     push hl
     ld l, c     ld l, c
Línea 292: Línea 306:
 ex (sp), hl ex (sp), hl
 ex (sp), ix ex (sp), ix
-EX (SP), IY+ex (sp), iy
 </code> </code>
  
Línea 309: Línea 323:
 |< 50% 50% 50% >| |< 50% 50% 50% >|
 ^ Instrucción incorrecta ^ Alternativa ^ ^ Instrucción incorrecta ^ Alternativa ^
-EX BCHL | push bc\\ ex (sp), hl\\ pop bc | +ex bchl | push bc\\ ex (sp), hl\\ pop bc | 
-EX BCIX | push bc\\ ex (sp), ix\\ pop bc | +ex bcix | push bc\\ ex (sp), ix\\ pop bc | 
-EX BCIY | push bc\\ EX (SP), IY\\ pop bc | +ex bciy | push bc\\ ex (sp), iy\\ pop bc | 
-EX AFHL | push af\\ ex (sp), hl\\ pop af | +ex afhl | push af\\ ex (sp), hl\\ pop af | 
-EX AFIX | push af\\ ex (sp), ix\\ pop af | +ex afix | push af\\ ex (sp), ix\\ pop af | 
-EX AFIY | push af\\ EX (SP), IY\\ pop af |+ex afiy | 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 DEIY | push de\\ EX (SP), IY\\ pop de |+ex deiy | push de\\ ex (sp), iy\\ pop de |
  
 \\  \\ 
Línea 425: Línea 439:
  Esto es lo que se conoce como "contented memory" o "memoria en contienda".  Esto es lo que se conoce como "contented memory" o "memoria en contienda".
  
- Esto implica que las lecturas y escrituras de nuestro programa (ejecutado por el Z80) en la página de memoria de 16KB que va desde 16384 a 32767 se ven interrumpidas de forma constante por la ULA (aunque de forma transparente para nuestro programa), por lo que ubicar la pila en esta zona puede suponer una ralentización con respecto a ubicarla más arriba de la dirección 32768. Recuerda que cada operación ''PUSH'' y ''POP'' es, físicamente, un acceso de escritura y lectura a memoria, y las rutinas de nuestro programa harán, seguro, gran uso de ellas, además de los ''call''s y ''RET''s (''PUSH PC'' + ''jp DIR'' / ''POP PC'').+ Esto implica que las lecturas y escrituras de nuestro programa (ejecutado por el Z80) en la página de memoria de 16KB que va desde 16384 a 32767 se ven interrumpidas de forma constante por la ULA (aunque de forma transparente para nuestro programa), por lo que ubicar la pila en esta zona puede suponer una ralentización con respecto a ubicarla más arriba de la dirección 32768. Recuerda que cada operación ''PUSH'' y ''POP'' es, físicamente, un acceso de escritura y lectura a memoria, y las rutinas de nuestro programa harán, seguro, gran uso de ellas, además de los ''CALL''s y ''RET''s (''PUSH PC'' + ''JP DIR'' / ''POP PC'').
  
  Por lo tanto, si establecemos la pila por debajo de nuestro programa, y tenemos nuestro programa en 32768, tendremos la pila en contended memory, lo cual implica que funcionará un poco más lenta en general que tener la pila por encima.  Por lo tanto, si establecemos la pila por debajo de nuestro programa, y tenemos nuestro programa en 32768, tendremos la pila en contended memory, lo cual implica que funcionará un poco más lenta en general que tener la pila por encima.
Línea 445: Línea 459:
  Las subrutinas son bloques de código máquina a las cuales saltamos, hacen su tarea asignada, y devuelven el control al punto en que fueron llamadas. A veces, esperan recibir los registros con una serie de valores y devuelven registros con los valores resultantes.  Las subrutinas son bloques de código máquina a las cuales saltamos, hacen su tarea asignada, y devuelven el control al punto en que fueron llamadas. A veces, esperan recibir los registros con una serie de valores y devuelven registros con los valores resultantes.
  
- Para saltar a subrutinas utilizamos la instrucción ''call'', y estas deben de terminar en un ''RET''.+ Para saltar a subrutinas utilizamos la instrucción ''CALL'', y estas deben de terminar en un ''RET''.
  
- El lector podría preguntar, ¿por qué no utilizar las instrucciones de salto ''jp'' y ''jr'' vistas hasta ahora? La respuesta es: debido a la necesidad de una dirección de retorno.+ El lector podría preguntar, ¿por qué no utilizar las instrucciones de salto ''JP'' y ''JR'' vistas hasta ahora? La respuesta es: debido a la necesidad de una dirección de retorno.
  
- Veamos un ejemplo ilustrativo de la importancia de call/ret realizando una subrutina que se utilice ''jp'' para su llamada. Supongamos la siguiente "subrutina" sin ''RET'':+ Veamos un ejemplo ilustrativo de la importancia de call/ret realizando una subrutina que se utilice ''JP'' para su llamada. Supongamos la siguiente "subrutina" sin ''RET'':
  
 <code z80> <code z80>
Línea 465: Línea 479:
  Nuestra función/subrutina de ejemplo espera obtener en A un valor, y devuelve el resultado de su ejecución en B. Antes de llamar a esta rutina, nosotros deberemos poner en A el valor sobre el que actuar, y posteriormente interpretar el resultado (sabiendo que lo tenemos en B).  Nuestra función/subrutina de ejemplo espera obtener en A un valor, y devuelve el resultado de su ejecución en B. Antes de llamar a esta rutina, nosotros deberemos poner en A el valor sobre el que actuar, y posteriormente interpretar el resultado (sabiendo que lo tenemos en B).
  
- Pero, ¿cómo llamamos a las subrutinas y volvemos de ellas? Comencemos probando con ''jp'':+ Pero, ¿cómo llamamos a las subrutinas y volvemos de ellas? Comencemos probando con ''JP'':
  
 <code z80> <code z80>
Línea 508: Línea 522:
 ===== Uso de CALL y RET ===== ===== Uso de CALL y RET =====
  
- ''call'' es, en esencia, similar a jp, salvo porque antes de realizar el salto, introduce en la pila (''PUSH'') el valor del registro **PC** (Program Counter, o contador de programa), el cual (una vez leída y decodificada la instrucción ''call'') apunta a la instrucción que sigue al ''call''.+ ''CALL'' es, en esencia, similar a jp, salvo porque antes de realizar el salto, introduce en la pila (''PUSH'') el valor del registro **PC** (Program Counter, o contador de programa), el cual (una vez leída y decodificada la instrucción ''CALL'') apunta a la instrucción que sigue al ''CALL''.
  
  ¿Y para qué sirve eso? Para que lo aprovechemos dentro de nuestra subrutina con ''RET''. ret lee de la pila la dirección que introdujo call y salta a ella. Así, cuando acaba nuestra función, el ret devuelve la ejecución a la instrucción siguiente al call que hizo la llamada.  ¿Y para qué sirve eso? Para que lo aprovechemos dentro de nuestra subrutina con ''RET''. ret lee de la pila la dirección que introdujo call y salta a ella. Así, cuando acaba nuestra función, el ret devuelve la ejecución a la instrucción siguiente al call que hizo la llamada.
Línea 523: Línea 537:
 </code> </code>
  
- Veamos la aplicación de ''call'' y ''RET'' con nuestro ejemplo anterior:+ Veamos la aplicación de ''CALL'' y ''RET'' con nuestro ejemplo anterior:
  
 <code z80> <code z80>
Línea 542: Línea 556:
 </code> </code>
  
- En esta ocasión, cuando ejecutamos el primer ''call'', se introduce en la pila el valor de PC, que se corresponde exáctamente con la dirección de memoria donde estaría ensamblada la siguiente instrucción (''ld a, 50''). El ''call'' cambia el valor de PC al de la dirección de ''SUMA_A_10'', y se continúa la ejecución dentro de la subrutina.+ En esta ocasión, cuando ejecutamos el primer ''CALL'', se introduce en la pila el valor de PC, que se corresponde exáctamente con la dirección de memoria donde estaría ensamblada la siguiente instrucción (''ld a, 50''). El ''CALL'' cambia el valor de PC al de la dirección de ''SUMA_A_10'', y se continúa la ejecución dentro de la subrutina.
  
- Al acabar la subrutina encontramos el ''RET'', quien extrae de la pila el valor de PC anteriormente introducido, con lo que en el siguiente ciclo de instrucción del microprocesador, el Z80 leerá, decodificará y ejecutará la instrucción ''ld a, 50'', siguiendo el flujo del programa linealmente desde ahí. Con la segunda llamada a ''call'' ocurriría lo mismo, pero esta vez lo que se introduce en la pila es la dirección de memoria en la que está ensamblada la instrucción ''ld c, b''. Esto asegura el retorno de nuestra subrutina al punto adecuado.+ Al acabar la subrutina encontramos el ''RET'', quien extrae de la pila el valor de PC anteriormente introducido, con lo que en el siguiente ciclo de instrucción del microprocesador, el Z80 leerá, decodificará y ejecutará la instrucción ''ld a, 50'', siguiendo el flujo del programa linealmente desde ahí. Con la segunda llamada a ''CALL'' ocurriría lo mismo, pero esta vez lo que se introduce en la pila es la dirección de memoria en la que está ensamblada la instrucción ''ld c, b''. Esto asegura el retorno de nuestra subrutina al punto adecuado.
  
- 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 ''PUSH'' que no elimináis después con un ''POP'', cuando lleguéis al ''RET'' éste obtendrá de la pila un valor que no será el introducido por ''call'' (sino el introducido por el ''PUSH''), y saltará a esa dirección incorrecta. Por ejemplo:+ 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 ''PUSH'' que no elimináis después con un ''POP'', cuando lleguéis al ''RET'' éste obtendrá de la pila un valor que no será el introducido por ''CALL'' (sino el introducido por el ''PUSH''), y saltará a esa dirección incorrecta. Por ejemplo:
  
 <code z80> <code z80>
Línea 561: Línea 575:
 </code> </code>
  
- Aquí ''RET'' sacará de la pila 0000h, en lugar de la dirección que introdujo ''call'', y saltará al inicio del a ROM, produciendo un bonito reset.+ Aquí ''RET'' sacará de la pila 0000h, en lugar de la dirección que introdujo ''CALL'', y saltará al inicio del a ROM, produciendo un bonito reset.
  
- Ni ''call'' ni ''RET'' afectan a la tabla de flags del registro F.+ Ni ''CALL'' ni ''RET'' afectan a la tabla de flags del registro F.
  
 <code> <code>
Línea 578: Línea 592:
 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.
  
-''RST'' ejecuta un ''call'' como el que ya hemos visto, pero permitiendo un salto sólo a una serie de direcciones en el bloque desde la dirección 0 a la dirección 255. **RST $NN** es, literalmente, un **call $00NN**.+''RST'' ejecuta un ''CALL'' como el que ya hemos visto, pero permitiendo un salto sólo a una serie de direcciones en el bloque desde la dirección 0 a la dirección 255. **RST $NN** es, literalmente, un **call $00NN**.
  
 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 646: Línea 660:
 </code> </code>
  
- Del mismo modo, el uso de ''call'' condicionado al estado de flags (''call Z'', ''call NZ'', ''call M'', ''call P'', etc) nos permitirá llamar o no a funciones según el estado de un flag.+ Del mismo modo, el uso de ''CALL'' condicionado al estado de flags (''CALL Z'', ''CALL NZ'', ''CALL M'', ''CALL P'', etc) nos permitirá llamar o no a funciones según el estado de un flag.
  
- Al igual que ''call'' y ''RET'', sus versiones condicionales no afectan al estado de los flags.+ Al igual que ''CALL'' y ''RET'', sus versiones condicionales no afectan al estado de los flags.
  
 <code> <code>
Línea 667: Línea 681:
 ==== Método 1: Uso de registros ==== ==== Método 1: Uso de registros ====
  
- Este método consiste en modificar unos registros concretos antes de hacer el ''call'' a nuestra subrutina, sabiendo que dicha subrutina espera esos registros con los valores sobre los que actuar. Asímismo, nuestra rutina puede modificar alguno de los registros con el objetivo de devolvernos un valor. Si modifica algún registro en el transcurso de la rutina, lo normal es preservarlo con un PUSH+ Este método consiste en modificar unos registros concretos antes de hacer el ''CALL'' a nuestra subrutina, sabiendo que dicha subrutina espera esos registros con los valores sobre los que actuar. Asímismo, nuestra rutina puede modificar alguno de los registros con el objetivo de devolvernos un valor. Si modifica algún registro en el transcurso de la rutina, lo normal es preservarlo con un PUSH
  
  Este método es el más habitual en los programas en ensamblador siempre y cuando no tengamos más parámetros de entrada a la rutina que registros existentes en el Z80.  Este método es el más habitual en los programas en ensamblador siempre y cuando no tengamos más parámetros de entrada a la rutina que registros existentes en el Z80.
Línea 675: Línea 689:
 <code z80> <code z80>
 ;-------------------------------------------------------------- ;--------------------------------------------------------------
-MULTIPLI: Multiplica DE*BC +Mult_HL_DE: Multiplica DE*BC 
-      Entrada:        DE: Multiplicando, +; 
-                      BC: Multiplicador +Entrada:        DE: Multiplicando, 
-      Salida:         HL: Resultado. +                BC: Multiplicador 
-      Modifica:       Ningun registro aparte de HL+; Salida:         HL: Resultado. 
 +; Modifica:       Ningun registro aparte de HL
 ;-------------------------------------------------------------- ;--------------------------------------------------------------
-MULTIPLICA:+Mult_HL_DE:
     push af             ; Preservamos AF porque F se va a modificar     push af             ; Preservamos AF porque F se va a modificar
     push bc             ; Preservamos BC porque su valor se pierde     push bc             ; Preservamos BC porque su valor se pierde
     ld hl, 0     ld hl, 0
  
-MULTI01:+multiloop_01:
     add hl, de     add hl, de
     dec bc     dec bc
     ld a, b     ld a, b
     or c     or c
-    jr nz, MULTI01+    jr nz, multiloop_01
  
     pop bc              ; Rescatamos el valor de BC     pop bc              ; Rescatamos el valor de BC
Línea 698: Línea 713:
 </code> </code>
  
- Antes de hacer la llamada a MULTIPLICA, tendremos que cargar en DE y en BC los valores que queremos multiplicar, de modo que si estos valores están en otros registros o en memoria, tendremos que moverlos a DE y BC.+ Antes de hacer la llamada a ''Mult_HL_DE'', tendremos que cargar en DE y en BC los valores que queremos multiplicar, de modo que si estos valores están en otros registros o en memoria, tendremos que moverlos a DE y BC.
  
  Además, sabemos que la salida nos será devuelta en HL, con lo que si dicho registro contenía algún valor importante y que no debemos perder en el código que llama a la rutina, deberemos preservarlo previamente.  Además, sabemos que la salida nos será devuelta en HL, con lo que si dicho registro contenía algún valor importante y que no debemos perder en el código que llama a la rutina, deberemos preservarlo previamente.
Línea 756: Línea 771:
     ld bc, (size)      ; Leemos los parametros     ld bc, (size)      ; Leemos los parametros
  
-    (Codigo)+    (... código ...)
  
     ld (salida), a     ; Devolvemos un valor     ld (salida), a     ; Devolvemos un valor
Línea 781: Línea 796:
  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, usando el valor de SP para acceder a ellos. En ensamblador no es normal utilizar este método a menos que tengamos muchos parámetros, no nos quepan en registros y que queramos ir rescatándolos de la pila en el punto de la función que nos interese (sea con el valor de SP o con POP).  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, usando el valor de SP para acceder a ellos. En ensamblador no es normal utilizar este método a menos que tengamos muchos parámetros, no nos quepan en registros y que queramos ir rescatándolos de la pila en el punto de la función que nos interese (sea con el valor de SP o con POP).
  
-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 ''PUSH'' y dentro de la rutina los vamos recogiendo con ''POP'':
  
  Veamos unos ejemplos:  Veamos unos ejemplos:
Línea 810: Línea 825:
  Nótese que hacemos ''PUSH'' de los 3 parámetros con 3 registros concretos pero que luego no hacemos ''POP'' de esos mismos registros. Hacer eso sería lo mismo que pasarse los parámetros en esos registros sin usar la pila. Si estamos usando la pila, es porque tenemos más parámetros que registros, o porque necesitamos extraer cada parámetro en el punto del programa donde nos interese y en un registro concreto. Lo importante es que antes del ''RET'' hayamos sacado de la pila todo lo que se introdujo, para que lo siguiente que esté presente en la pila sea la dirección de retorno para ''RET''.  Nótese que hacemos ''PUSH'' de los 3 parámetros con 3 registros concretos pero que luego no hacemos ''POP'' de esos mismos registros. Hacer eso sería lo mismo que pasarse los parámetros en esos registros sin usar la pila. Si estamos usando la pila, es porque tenemos más parámetros que registros, o porque necesitamos extraer cada parámetro en el punto del programa donde nos interese y en un registro concreto. Lo importante es que antes del ''RET'' hayamos sacado de la pila todo lo que se introdujo, para que lo siguiente que esté presente en la pila sea la dirección de retorno para ''RET''.
  
-También podemos usar acceso directo a memoria mediante el valor de SP saltándonos los 2 bytes de la dirección de retorno introducida en la pila por el ''call'', como hace el compilador de C Z88DK, pero en ese caso necesitaremos antes de salir de la rutina hacer un POP de todos los parámetros introducidos:+También podemos usar acceso directo a memoria mediante el valor de SP saltándonos los 2 bytes de la dirección de retorno introducida en la pila por el ''CALL'', como hace el compilador de C Z88DK, pero en ese caso necesitaremos antes de salir de la rutina hacer un POP de todos los parámetros introducidos:
  
 <code z80> <code z80>
Línea 879: Línea 894:
 <code z80> <code z80>
 BuclePrincipal: BuclePrincipal:
-    call Leer_Teclado +    call LeerTeclado             ; o Leer_Teclado 
-    call Logica_Juego +    call LogicaJuego             ; o Logica_Juego 
-    call Comprobar_Estado +    call ComprobarEstado         ; o Comprobar_Estado 
-    jp Bucle_Principal+    jp BuclePrincipal            ; o Bucle_Principal
  
-Leer_Teclado:+LeerTeclado:
     ret     ret
  
-Logica_Juego:+LogicaJuego:
     ret     ret
  
-Comprobar_Estado:+ComprobarEstado:
     ret     ret
 </code> </code>
  • cursos/ensamblador/lenguaje_4.1705648488.txt.gz
  • Última modificación: 19-01-2024 07:14
  • por sromero