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:interrupciones [19-01-2024 08:07] – sromero | cursos:ensamblador:interrupciones [21-01-2024 11:23] (actual) – [Instrucción HALT] sromero | ||
---|---|---|---|
Línea 105: | Línea 105: | ||
==== Instrucción HALT ==== | ==== Instrucción HALT ==== | ||
- | La instrucción '' | + | La instrucción '' |
<code z80> | <code z80> | ||
Línea 111: | Línea 111: | ||
</ | </ | ||
- | Como veremos más adelante, la instrucción | + | Básicamente, la ejecución de nuestro programa se detiene en el '' |
- | Como veremos con detalle a continuación, | + | Como veremos con detalle a continuación, |
+ | |||
+ | Como veremos más adelante, la instrucción '' | ||
\\ | \\ | ||
Línea 292: | Línea 294: | ||
Las ISR deben optimizarse lo máximo posible, tratando de que sean lo más rápidas y óptimas posibles, ya que nuestro programa se ha visto interrumpido y no se continuará su ejecución hasta la salida de la ISR. Si tenemos en cuenta que normalmente nuestras ISR se ejecutarán 50 veces por segundo, es importante no ralentizar la ejecución del programa principal llenando de código innecesario la ISR. | Las ISR deben optimizarse lo máximo posible, tratando de que sean lo más rápidas y óptimas posibles, ya que nuestro programa se ha visto interrumpido y no se continuará su ejecución hasta la salida de la ISR. Si tenemos en cuenta que normalmente nuestras ISR se ejecutarán 50 veces por segundo, es importante no ralentizar la ejecución del programa principal llenando de código innecesario la ISR. | ||
- | Es crítico también que en la salida de la ISR no hayamos modificado los valores de los registros con respecto a su entrada. Para eso, podemos utilizar la pila y hacer '' | + | Es crítico también que en la salida de la ISR no hayamos modificado los valores de los registros con respecto a su entrada. Para eso, podemos utilizar la pila y hacer '' |
Al principio de nuestra ISR no es necesario desactivar las interrupciones con di, ya que el Z80 las deshabilita al aceptar la interrupción. Debido a este '' | Al principio de nuestra ISR no es necesario desactivar las interrupciones con di, ya que el Z80 las deshabilita al aceptar la interrupción. Debido a este '' | ||
Línea 359: | Línea 361: | ||
Nuestra_ISR: | Nuestra_ISR: | ||
; Hay que preservar todos los registros que se modifiquen, incluido AF | ; Hay que preservar todos los registros que se modifiquen, incluido AF | ||
- | | + | |
(código de la ISR) | (código de la ISR) | ||
; Y recuperar su valor antes de finalizar la ISR | ; Y recuperar su valor antes de finalizar la ISR | ||
- | | + | |
ei | ei | ||
reti | reti | ||
Línea 400: | Línea 402: | ||
or l ; only incremented when the value | or l ; only incremented when the value | ||
jr nz, | jr nz, | ||
- | | + | |
0048 KEY-INT: | 0048 KEY-INT: | ||
push bc ; Save the current values held | push bc ; Save the current values held | ||
Línea 459: | Línea 461: | ||
; Instalamos la ISR: | ; Instalamos la ISR: | ||
- | ld hl, iSR_ASM_ROUTINE | + | ld hl, ISR_ASM_ROUTINE |
di ; Deshabilitamos las interrupciones | di ; Deshabilitamos las interrupciones | ||
ld ($feff), hl ; Guardamos en (65279 = $feff) la direccion | ld ($feff), hl ; Guardamos en (65279 = $feff) la direccion | ||
Línea 473: | Línea 475: | ||
push af | push af | ||
push hl | push hl | ||
- | | + | |
(código de la ISR) | (código de la ISR) | ||
- | | + | |
pop hl | pop hl | ||
pop af | pop af | ||
Línea 620: | Línea 622: | ||
inc a | inc a | ||
ld (ticks), a | ld (ticks), a | ||
- | cp iNTS_PER_SECOND | + | cp INTS_PER_SECOND |
- | jr c, FRAMES_menor_que_50 | + | jr c, frames_menor_que_50 |
- | sub iNTS_PER_SECOND | + | sub INTS_PER_SECOND |
; por si alguien incremento su valor externamente) | ; por si alguien incremento su valor externamente) | ||
Línea 629: | Línea 631: | ||
inc (hl) ; Incrementamos segundos | inc (hl) ; Incrementamos segundos | ||
- | FRAMES_menor_que_50: | + | frames_menor_que_50: |
ld (ticks), a | ld (ticks), a | ||
pop hl | pop hl | ||
Línea 1050: | Línea 1052: | ||
ld (timer), hl ; seteamos " | ld (timer), hl ; seteamos " | ||
- | Waitnticks_loop: ; bucle de espera, la ISR lo ira decrementando | + | waitnticks_loop: ; bucle de espera, la ISR lo ira decrementando |
ld hl, (timer) | ld hl, (timer) | ||
ld a, h ; cuando (timer) valga 0 y lo decrementen, | ld a, h ; cuando (timer) valga 0 y lo decrementen, | ||
cp $ff ; byte alto pasara a valer FFh, lo que quiere | cp $ff ; byte alto pasara a valer FFh, lo que quiere | ||
; decir que ha pasado el tiempo a esperar. | ; decir que ha pasado el tiempo a esperar. | ||
- | jr nz, Waitnticks_loop | + | jr nz, waitnticks_loop |
ret | ret | ||
Línea 1102: | Línea 1104: | ||
ld (timer), hl ; seteamos " | ld (timer), hl ; seteamos " | ||
- | Waitkeyticks_loop: ; bucle de espera, la ISR lo ira decrementando | + | waitkeyticks_loop: ; bucle de espera, la ISR lo ira decrementando |
- | xor a | + | xor a ; A = 0 => leer todas las semifilas del teclado |
- | in a,(254) | + | in a, ($fe) ; Leer teclado |
- | or 224 | + | or %11100000 |
- | inc a ; Comprobamos el estado del teclado | + | inc a ; |
- | ret nz ; Si hay tecla pulsada, salimos | + | ret nz ; Si A=0 => ZF = 1 => no hay tecla pulsada |
+ | ; Si A!=0 => ZF = 0 => hay alguna tecla => salimos | ||
ld hl, (timer) | ld hl, (timer) | ||
Línea 1113: | Línea 1116: | ||
cp $ff ; byte alto pasara a valer FFh, lo que quiere | cp $ff ; byte alto pasara a valer FFh, lo que quiere | ||
; decir que ha pasado el tiempo a esperar. | ; decir que ha pasado el tiempo a esperar. | ||
- | jr nz, Waitkeyticks_loop | + | jr nz, waitkeyticks_loop |
ret | ret | ||
</ | </ | ||
+ | |||
+ | |||
+ | \\ | ||
+ | ===== Utilizar un JP como ISR ===== | ||
+ | |||
+ | En muchos juegos comerciales y homebrew, la rutina de ISR no es una rutina en sí misma sino un '' | ||
+ | |||
+ | |||
+ | <code z80> | ||
+ | ; Rutina de ISR ubicada junto a nuestro programa, y no en dirección tipo $XYXY | ||
+ | ORG 40000 | ||
+ | |||
+ | ; Generamos una tabla de 257 valores " | ||
+ | ld hl, $a000 | ||
+ | ld a, $a2 ; A = $a2 | ||
+ | ld (hl), a ; Cargamos $a2 en $fe00 | ||
+ | ld de, $fe01 ; Apuntamos DE a $fe01 | ||
+ | ld bc, 256 ; Realizamos 256 ldi para copiar $a2 | ||
+ | ldir ; en toda la tabla de vectores de int. | ||
+ | |||
+ | ; Activamos im2 con nuestra ISR | ||
+ | di | ||
+ | ld a, $fe ; Definimos la tabla a partir de $fe00. | ||
+ | ld i, a | ||
+ | im 2 ; Saltamos a im2 | ||
+ | ei | ||
+ | |||
+ | ; Nuestro programa | ||
+ | ; (...) | ||
+ | |||
+ | Rutina_ISR: | ||
+ | ; La rutina ISR | ||
+ | (...) | ||
+ | ei | ||
+ | reti | ||
+ | |||
+ | ; Guardamos en una variable de preprocesador la posicion | ||
+ | ; de este punto en el proceso de ensamblado ($) | ||
+ | PUNTO_ENSAMBLADO EQU $ | ||
+ | |||
+ | ; | ||
+ | ; Nuestra rutina de ISR ensamblada en $A2A2: JP a rutina real | ||
+ | ; | ||
+ | ORG $A2A2 | ||
+ | |||
+ | PUNTO_ENTRADA_ISR: | ||
+ | jp Rutina_ISR | ||
+ | </ | ||
+ | |||
+ | De esta forma, la tabla de vectores de interrupción y la rutina ISR (un simple '' | ||
Línea 1167: | Línea 1220: | ||
Necesitaremos buscar un hueco en nuestro programa, para colocar un ORG adecuado delante de la ISR, como por ejemplo $a1a1 (41377), o cualquier otra dirección donde el byte alto y el bajo coincidan, y que nos venga bien según el mapa de memoria de nuestro juego. | Necesitaremos buscar un hueco en nuestro programa, para colocar un ORG adecuado delante de la ISR, como por ejemplo $a1a1 (41377), o cualquier otra dirección donde el byte alto y el bajo coincidan, y que nos venga bien según el mapa de memoria de nuestro juego. | ||
+ | |||
+ | Por citar ejemplos de direcciones, | ||
+ | |||
+ | \\ | ||
+ | |< 90% >| | ||
+ | ^ Juego ^ Ubicación tabla vectores ^ Ubicación ISR ^ Tipo de ISR (rutina o\\ salto a rutina real) ^ Notas ^ | ||
+ | | La Familia Addams (The Addams Family) | $b900-$ba00 | $5b5b | ISR es '' | ||
+ | | Where Time Stood Still | $8400-$8500 | $bebe | Rutina ISR | - | | ||
+ | | Demo 7th Reality | $6300-$6400 | $6464 | ISR es '' | ||
+ | | La Abadia Del Crimen | $be00-$bf00 | $bfbf | Rutina ISR | - | | ||
+ | | Chase HQ 2 | $9b00-$9c00 | $fdfd | ISR es '' | ||
+ | | Grand Prix Circuit | $8200-$8300 | $6363 | ISR es '' | ||
+ | | Robocop 2 | $7700-$7800 | $5b5b | ISR es '' | ||
+ | | Robocop 3 | $7700-$7800 | $7676 | ISR es '' | ||
+ | | Spacegun | $be00-$bf00 | $bfbf | ISR es '' | ||
+ | | Desafio Total (Total Recall) | $9100-$9200 | $5d5d | ISR es '' | ||
+ | | Carrier Command | $8300-$8400 | $8585 | Rutina ISR | - | | ||
+ | | El Gran Halcón (Hudson Hawk) | $9000-$8100 | $8181 | Rutina ISR | - | | ||
+ | | NARC | $be00-$bf00 | $bfbf | ISR es '' | ||
+ | | Navy Seals | $9100-$9200 | $5d5d | ISR es '' | ||
+ | | Pang | $8000-$8101 | $8181 | ISR es '' | ||
+ | \\ | ||
+ | |||
+ | Varias curiosidades: | ||
+ | |||
+ | * Como se puede ver, no hay un estándar sobre dónde ubicar la tabla de vectores de interrupción, | ||
+ | |||
+ | * Sólo 2 juegos de la lista (**Grand Prix Circuit** y **Pang**) tienen una tabla de 257 bytes, teniendo el resto de juegos una tabla de 1 página (256 bytes), con lo que técnicamente, | ||
+ | |||
+ | * Sólo 4 juegos de la lista implementan la rutina directamente en la dirección apuntada por el vector de interrupciones, | ||
+ | |||
\\ | \\ |