Página 1 de 1

¿Diferencias RETN/RET?

NotaPublicado: Dom Mar 11, 2018 12:52 pm
por zup
La historia es que para quitar óxido a mis habilidades, quiero hacer mi propia versión de un programa que convierta snapshots de Spectrum en ficheros TAP o DSK.

De momento, voy a centrarme en la conversión SNA a TAP. Para hacerlo más "auténtico", quiero que haga una cinta que parezca haber sido grabada con uno de los transfers de la época (Transtape, Phoenix, Pokeador automático). Por ejemplo, en el caso del Transtape, eso significaría un bloque BASIC, seguido de uno de 75 bytes y otro sin cabecera de 49052 bytes.

Después de esta introducción, el problema:

Leyendo la documentación del formato SNA, me llama la atención la frase "When the registers have been loaded, a RETN command is required to start the program". Tiene lógica, ya que "supuestamente" hemos grabado usando una NMI. El tema es que, mirando los cargadores que usan los interfaces, todos ejecutan el programa usando RET.

Supongo que eso es porque (al cargar el programa) realmente no estamos volviendo de una interrupción. La pregunta es... ¿debería usar RET o RETN al volver? ¿Da lo mismo en este caso o tengo que parchear el cargador?

Gracias

DATO INTERESANTE: Buscando en la ROM del 48k, he encontrado en la dirección 7805 estas instrucciones:

Código: Seleccionar todo
out (c),a
ret


Visto así, parece una tontería, pero permite paginar y retornar sin consumir demasiados bytes. En el caso del transfer Phoenix (que corrompe muy poquito la pantalla), solo deja en la pantalla las instrucciones para cargar los registros y retornar. Las instrucciones para cargar el último bloque son las siguientes:

Código: Seleccionar todo
ld ix,64000
ld de,1536
ld hl,16384
push hl
jp xxx ; que en realidad luego salta a 1378


Suponiendo que quisiera hacer un cargador para cargar desde disco +3, podría ejecutar el código desde la página 7 y (después de cargar todos los bloques en su sitio) acabar usando algo así:

Código: Seleccionar todo
di
ld hl,16384
push hl
ld a,48 ; el phoenix solo hace snapshots de 48k...
ld bc,32765
jp 7805


De manera que el código que pone la página 0 y desactiva la paginación se ejecuta realmente la ROM y me evito problemas raros o corromper más pantalla.

Re: ¿Diferencias RETN/RET?

NotaPublicado: Dom Mar 11, 2018 1:34 pm
por zx81
Digo yo que lo suyo sería volver con RETN porque, además de volver, restaura el estado anterior de las interrupciones, cosa que no hace RET por sí solo. Pero como en la cabecera del SNA está guardado el estado del IFF2 que es la copia de lo que tenía el IFF1 en el momento de la NMI, puedes habilitar las interrupciones con EI si es preciso y luego volver con RET.

En una máquina real no puedes volver con RETN porque no está dentro de una NMI, así que no puedes contar con que el IFF2 tenga un valor concreto (podrías averiguarlo con LD A,I o LD A,R, pero al final da igual, porque no puedes volver con RETN.

Lo que complica el formato SNA y que soluciona el formato de SNA para 128k, es que el registro PC no se guarda implícitamente en la cabecera, de modo que en JSpeccy lo que hago es cargar IFFx con lo que diga la cabecera y luego saltar al RETN de la ROM del 48k (el RETN que pertenece a la rutina en 0x0066). Pero ese truco no vale para el real.

Pensándolo un poco, el posible EI debería estar justo antes del RET para que sea imposible que salte una INT antes de que se ejecute RET (las interrupciones no se comprueban hasta el final de la siguiente instrucción que sigue al EI).

Re: ¿Diferencias RETN/RET?

NotaPublicado: Dom Mar 11, 2018 1:57 pm
por zup
Entonces, deduzco que el RET es correcto.

En el caso de los cargadores de los transfers, suelen restaurar todos los valores con las interrupciones deshabilitadas. Una vez hecho, esto hay dos variantes:
- En algunos, la instrucción EI o DI (según corresponda) va insertada tal cual antes del RET.
- En otros, se comprueba un bit que contiene el estado de las interrupciones. Si están habilitadas, va a una rutina que acaba en EI / RET; si no va a otra que acaba en DI / RET.

Con IM pasa lo mismo... algunos ponen la instrucción IM x hardcoded y otros tienen dos rutinas (una para IM1 y otra para IM2) y ejecutan una u otra según el valor de I o un bit guardado en alguna parte.