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:avanzadas1 [13-01-2024 11:14] – [Cómo aprovechar el área entre 25000 y 32768] sromero | cursos:ensamblador:avanzadas1 [21-01-2024 10:31] (actual) – [Obtener el valor de PC (Contador de Programa)] sromero | ||
---|---|---|---|
Línea 54: | Línea 54: | ||
Entonces ¿por qué no ubicar nuestro programa en '' | Entonces ¿por qué no ubicar nuestro programa en '' | ||
- | Con '' | + | Con '' |
- | Al igual que pasaba con el código de nuestro programa, en este caso cualquier operación '' | + | Al igual que pasaba con el código de nuestro programa, en este caso cualquier operación '' |
Para que la pila quede fuera de la contended memory, dado que BASIC la situa por debajo de ORG, hay que subir un poco ORG, para darle espacio dentro de estos primeros bytes del bloque 32K-48K. Si establecemos ORG en diferentes valores, y con el siguiente programa miramos el valor de SP al inicio de nuestro programa después de cargarlo, veremos lo siguiente: | Para que la pila quede fuera de la contended memory, dado que BASIC la situa por debajo de ORG, hay que subir un poco ORG, para darle espacio dentro de estos primeros bytes del bloque 32K-48K. Si establecemos ORG en diferentes valores, y con el siguiente programa miramos el valor de SP al inicio de nuestro programa después de cargarlo, veremos lo siguiente: | ||
Línea 64: | Línea 64: | ||
ORG 33500 | ORG 33500 | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | ROM_CLS | + | ROM_CLS |
- | ROM_STACK_BC | + | ROM_STACK_BC |
- | ROM_PRINT_FP | + | ROM_PRINT_FP |
END 33500 | END 33500 | ||
Línea 101: | Línea 101: | ||
Cabe destacar que **no estamos hablando de que sólo tengamos 708 bytes de pila, y que si hacemos PUSHes adicionales se vaya a colgar el programa**. No, simplemente si hacemos muchas apilaciones de datos, sin sacarlos de la pila, los nuevos valores después de algo más de 350 PUSH' | Cabe destacar que **no estamos hablando de que sólo tengamos 708 bytes de pila, y que si hacemos PUSHes adicionales se vaya a colgar el programa**. No, simplemente si hacemos muchas apilaciones de datos, sin sacarlos de la pila, los nuevos valores después de algo más de 350 PUSH' | ||
- | Y recordemos que la pila crece hacia abajo con '' | + | Y recordemos que la pila crece hacia abajo con '' |
Así pues, nuestra recomendación de valor de inicio **ORG** mínimo para cualquier programa sería de __**cualquiera de esos 4 valores**__, | Así pues, nuestra recomendación de valor de inicio **ORG** mínimo para cualquier programa sería de __**cualquiera de esos 4 valores**__, | ||
Va a ser complicado que lleguemos a ocupar los 30-31KB de memoria que tenemos por delante, salvo que estemos realizando un juego de calidad comercial o con muchos datos. En ese caso, en este mismo capítulo veremos ideas para, entre otras cosas, darle un uso a esos 7KB de RAM " | Va a ser complicado que lleguemos a ocupar los 30-31KB de memoria que tenemos por delante, salvo que estemos realizando un juego de calidad comercial o con muchos datos. En ese caso, en este mismo capítulo veremos ideas para, entre otras cosas, darle un uso a esos 7KB de RAM " | ||
- | |||
- | |||
\\ | \\ | ||
Línea 137: | Línea 135: | ||
\\ | \\ | ||
- | Para poner uno de los bloques disponibles en la " | + | Para poner uno de los bloques disponibles en la " |
Eso quiere decir que tenemos que organizar y diseñar nuestro código alrededor de esto ya que: | Eso quiere decir que tenemos que organizar y diseñar nuestro código alrededor de esto ya que: | ||
- | * El código de nuestro programa deberá estar en la ventana que no se mapea (entre 32768 -$8000- y 49152 -$BFFF-). Si vamos a cambiar de banco, la ejecución del programa no puede estar en el banco que vamos a " | + | * El código de nuestro programa deberá estar en la ventana que no se mapea (entre 32768 -$8000- y 49152 -$bfff-). Si vamos a cambiar de banco, la ejecución del programa no puede estar en el banco que vamos a " |
* La pila tampoco puede estar situada en el bloque que paginamos, porque la perderíamos y el próximo '' | * La pila tampoco puede estar situada en el bloque que paginamos, porque la perderíamos y el próximo '' | ||
Línea 173: | Línea 171: | ||
Para hacer esto, lo idea sería tener 2 ORG en nuestro programa en sjasmplus, o dos ficheros ASM en pasmo, para generar 2 ficheros BIN de código máquina separados. En el primero, tendríamos el punto de ejecución del inicio de nuestro programa. | Para hacer esto, lo idea sería tener 2 ORG en nuestro programa en sjasmplus, o dos ficheros ASM en pasmo, para generar 2 ficheros BIN de código máquina separados. En el primero, tendríamos el punto de ejecución del inicio de nuestro programa. | ||
- | Lo primero que hacemos es cambiar la pila para sacarla de la contended memory, asignándola a la misma dirección donde empieza el código del juego en sí (33500). Debido a este cambio, ya no podremos volver al BASIC si finalizamos el menú con un RET. Lo normal es que no se vuelva más al BASIC, pero si quisíeramos hacerlo, podemos guardarnos el valor de SP en una variable en memoria ('' | + | Lo primero que hacemos es cambiar la pila para sacarla de la contended memory, asignándola a la misma dirección donde empieza el código del juego en sí (33500). Debido a este cambio, ya no podremos volver al BASIC si finalizamos el menú con un ret. Lo normal es que no se vuelva más al BASIC, pero si quisíeramos hacerlo, podemos guardarnos el valor de SP en una variable en memoria ('' |
Lo siguiente que hacemos es llamar a todas las rutinas de inicialización de uso único que tenga el programa, alojadas en este " | Lo siguiente que hacemos es llamar a todas las rutinas de inicialización de uso único que tenga el programa, alojadas en este " | ||
Línea 185: | Línea 183: | ||
ORG 26000 | ORG 26000 | ||
- | ; Ponemos la pila colgando del programa principal, | + | ; Ponemos la pila colgando del programa principal, |
- | ; y fuera de la contended memory. | + | ; de la contended memory. |
- | | + | |
+ | ; recuperarla antes del RET final del programa. | ||
+ | ld sp, 33500 | ||
- | | + | |
- | | + | |
- | menu: | + | Menu: |
; codigo del menu aqui. | ; codigo del menu aqui. | ||
; ... en algun punto.. | ; ... en algun punto.. | ||
- | | + | |
- | | + | |
graficos_menu DEFB ..... | graficos_menu DEFB ..... | ||
Línea 212: | Línea 212: | ||
ORG 33500 | ORG 33500 | ||
- | iniciar_partida: | + | Iniciar_Partida: |
; codigo del juego aqui | ; codigo del juego aqui | ||
- | ; para volver al menu, un RET | + | ; para volver al menu, un ret |
- | | + | |
END 33500 | END 33500 | ||
Línea 266: | Línea 266: | ||
El segundo mecanismo de ahorro de tiempo de carga si usamos gráficos con máscaras, es " | El segundo mecanismo de ahorro de tiempo de carga si usamos gráficos con máscaras, es " | ||
+ | \\ | ||
+ | ==== Utilizar el Buffer de Impresión para la pila o alojar variables ==== | ||
+ | |||
+ | Existe una zona de memoria en el Spectrum de 48K que BASIC utiliza como " | ||
+ | |||
+ | Este área son 256 bytes, desde 23296 (5B00h) a 23551 (5BFFh). | ||
+ | |||
+ | Hay gente que pone la pila en la posición de memoria del buffer de impresión, que puede ser suficiente si no vamos a hacer más de 128 anidaciones (cada '' | ||
+ | |||
+ | Aún así, podemos utilizar esa zona como " | ||
+ | |||
+ | 1.- Está en la Contended Memory, aunque no es un problema grande para lo que es el acceso puntual a variables. | ||
+ | |||
+ | 2.- Los modelos de 128 tienen ahí rutinas de paginación de las ROM de sintaxis 128 y también partes del intérpretes de 48. Si pretendemos volver al BASIC después de ejecutar nuestro programa, o usar rutinas de la ROM en un 128K, no podemos tocar ese área. | ||
+ | |||
+ | 3.- Tampoco podremos usarlo en modelos 128K si tenemos activas las interrupciones en im1 (Modo de Interrupciones 1). Si usamos nuestra propia rutina ISR en im2 y desde ella no llamamos a la im1 (para que actualice FRAMES u otras variables del sistema), no debería haber problemas en utilizar ese área si vemos que nuestro programa crece y necesitamos arañar unos bytes para las variables del mismo. | ||
+ | |||
+ | \\ | ||
\\ | \\ | ||
Línea 357: | Línea 375: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
</ | </ | ||
- | El valor de **23760** (**$5C76**) es el actual valor de '' | + | El valor de **23760** (**$5c76**) es el actual valor de '' |
\\ | \\ | ||
Línea 373: | Línea 391: | ||
Otra restricción importante sobre nuestro código máquina si lo vamos a llamar desde BASIC es que no debemos de utilizar los registros del Z80 llamados **IX** e **IY** en funciones que vayamos a llamar desde BASIC. | Otra restricción importante sobre nuestro código máquina si lo vamos a llamar desde BASIC es que no debemos de utilizar los registros del Z80 llamados **IX** e **IY** en funciones que vayamos a llamar desde BASIC. | ||
- | De hecho, no deberíamos utilizar **IY** ni siquiera desde ensamblador puro si estamos usando el modo de interrupción | + | De hecho, no deberíamos utilizar **IY** ni siquiera desde ensamblador puro si estamos usando el modo de interrupción |
- | Como ya comentamos, la rutina de la ROM **$0038**, llamada en IM1, utiliza el registro IY para actualizar el tercer byte de la variable '' | + | Como ya comentamos, la rutina de la ROM **$0038**, llamada en im1, utiliza el registro IY para actualizar el tercer byte de la variable '' |
\\ | \\ | ||
==== Uso del registro I ==== | ==== Uso del registro I ==== | ||
- | Tampoco se debe cargar el registro " | + | Tampoco se debe cargar el registro " |
Línea 405: | Línea 423: | ||
1.- Antes de mostrar el menú del juego 48K, si estamos en modo 128K (consultando la variable que estableció la rutina detectora de modelo) podemos utilizar los gráficos extra cargados en otro banco para mostrar la introducción (que sólo se verá al arrancar el juego). Conmutamos al banco y pintamos las pantallas, textos, animaciones, | 1.- Antes de mostrar el menú del juego 48K, si estamos en modo 128K (consultando la variable que estableció la rutina detectora de modelo) podemos utilizar los gráficos extra cargados en otro banco para mostrar la introducción (que sólo se verá al arrancar el juego). Conmutamos al banco y pintamos las pantallas, textos, animaciones, | ||
- | 2.- En la rutina ISR IM2 del juego, si es un modo 128K, conmutamos al banco de la música y llamamos al player. A este player le pediremos lo que sea necesario (parar, arrancar música, cambiar de melodía, etc) según lo que indique alguna variable de control que tengamos en memoria para que el IM2 sepa qué tiene que hacer con la música en cada momento. Esta variable la controlaremos desde el juego en sí como veremos en el paso 5. | + | 2.- En la rutina ISR im2 del juego, si es un modo 128K, conmutamos al banco de la música y llamamos al player. A este player le pediremos lo que sea necesario (parar, arrancar música, cambiar de melodía, etc) según lo que indique alguna variable de control que tengamos en memoria para que el im2 sepa qué tiene que hacer con la música en cada momento. Esta variable la controlaremos desde el juego en sí como veremos en el paso 5. |
3.- Una vez se ha llamado a la rutina del player y hemos vuelto de ella (todavía en la ISR), si estamos en modo 128K ya se puede volver a conmutar al banco de memoria original, para que la ISR siga ejecutándose como en los modelos de 48K. | 3.- Una vez se ha llamado a la rutina del player y hemos vuelto de ella (todavía en la ISR), si estamos en modo 128K ya se puede volver a conmutar al banco de memoria original, para que la ISR siga ejecutándose como en los modelos de 48K. | ||
Línea 414: | Línea 432: | ||
Con esto el mismo juego 48K podrá tener música en modo 128K con unos pequeños añadidos muy sencillos, y ser compatible con ambos modelos. | Con esto el mismo juego 48K podrá tener música en modo 128K con unos pequeños añadidos muy sencillos, y ser compatible con ambos modelos. | ||
+ | |||
+ | |||
+ | \\ | ||
+ | \\ | ||
+ | ===== Código Automodificable ===== | ||
+ | |||
+ | Ya vimos en un capítulo anterior un ejemplo de **código automodificable**. Consiste en sobreescribir en memoria un opcode o un dato en función de una condición, para que cuando lleguemos a ese punto del programa, se ejecute el opcode modificado. | ||
+ | |||
+ | En el ejemplo que ya vimos, utilizamos código automodificable para sobreescribir un instrucción de carga " | ||
+ | |||
+ | <code z80> | ||
+ | ld (save_bc+1), | ||
+ | ; opcode "ld bc, NN NN" en memoria | ||
+ | |||
+ | ... hacer cosas con BC, perdiendo su valor ... | ||
+ | |||
+ | save_bc: | ||
+ | ld bc, $0000 ; En el LD anterior cambiamos $000 | ||
+ | ; por el valor de BC, así que cuando | ||
+ | ; el z80 llegue aquí no es ya ld bc, 0 | ||
+ | ; sino ld bc, valor_que_tenia_BC | ||
+ | ; así que recuperaremos BC aquí. | ||
+ | </ | ||
+ | |||
+ | Cuando se ejecuta la instrucción '' | ||
+ | |||
+ | Cuando se ejecuta el '' | ||
+ | |||
+ | La escritura en memoria ('' | ||
+ | |||
+ | Y no sólo podemos modificar datos, también podríamos modificar opcodes, cambiando '' | ||
+ | |||
+ | \\ | ||
+ | ===== Obtener el valor de PC (Contador de Programa) ===== | ||
+ | |||
+ | Utilizando '' | ||
+ | |||
+ | <code z80> | ||
+ | call NextInstr | ||
+ | NextInstr: | ||
+ | pop hl ; HL contiene ahora PC | ||
+ | </ | ||
+ | |||
+ | Al ejecutarse '' | ||
+ | |||
+ | ¿Para qué podría valer algo así? Nuestro compañero **< | ||
+ | |||
+ | <code z80> | ||
+ | Imprimir_Mensajes: | ||
+ | call MyPrintString | ||
+ | DB " | ||
+ | call MyPrintString | ||
+ | DB " | ||
+ | call MyPrintString | ||
+ | DB " | ||
+ | (...) | ||
+ | |||
+ | MyPrintString: | ||
+ | pop hl ; obtenemos la direccion de la cadena (PC) | ||
+ | mpr_loop: | ||
+ | ld a, (hl) | ||
+ | cp $ff | ||
+ | Jr z, mprexit | ||
+ | rst $10 | ||
+ | inc hl | ||
+ | jr mpr_loop | ||
+ | mpr_exit: | ||
+ | push hl ; introducimos direccion de retorno | ||
+ | ret ; PC continuará después de la cadena impresa | ||
+ | </ | ||
+ | |||
+ | La llamada a '' | ||
+ | |||
+ | La rutina extrae ese valor de la pila y lo usa como " | ||
+ | |||
+ | Esto nos evitaría lo siguiente, que suponen 3 bytes por cada instrucción '' | ||
+ | |||
+ | <code z80> | ||
+ | ld hl, cadena1 | ||
+ | call MyPrintString | ||
+ | ld hl, cadena2 | ||
+ | call MyPrintString | ||
+ | ld hl, cadena3 | ||
+ | call MyPrintString | ||
+ | </ | ||
\\ | \\ | ||
- | **[ [[.: | + | **[ [[.: |