Tutorial para pasar de dsk a cdt por CECPC

Cómo pasar juegos a diferentes archivos, herramientas, software.
CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Tutorial para pasar de dsk a cdt por CECPC

Mensajepor CECPC » Sab 22 Ago , 2015 3:26 pm

Digamos que queremos pasar de disco a cinta. ¿Cómo lo hacemos?

Primero debemos entender cómo se carga un juego o programa.
Normalmente sabemos que hacemos para empezar la carga aunque desconocemos que hay detrás.

Si estamos en modo cinta (si tenemos disco, ponemos |TAPE para pasar a la cinta) normalmente hacemos

Código: Seleccionar todo

RUN"
Mientras que en los discos, normalmente hay dos métodos

Algunos discos se cargan usando el comando

Código: Seleccionar todo

|CPM
Mientras que otros hay que hacer "RUN" también, pero sobre el archivo concreto para iniciar el juego.
Así que...

Código: Seleccionar todo

cat
Buscamos el archivo más evidente que inicie el juego (si existe un BAS con el nombre del juego suele ser ese. Si no, un .BIN con el nombre del juego o similar)

Código: Seleccionar todo

RUN"ARCHIVO
Vale... Hasta aquí lo que sabemos todos. ¿Y qué hay detrás?

El comando RUN varía su comportamiento en función de qué está cargando.
Los archivos de BASIC se cargan en la posición determinada para ello y se inicia el intérprete de este lenguaje incorporado en la ROM. Si es un binario, sin embargo, el código se carga en una posición que viene determinada en el mismo archivo de cinta, e igualmente se pasa el control al código máquina incluida en la posición que se determina también en el archivo.

Normalmente los juegos no cargan directamente. Es decir, que meterías todo el juego de golpe en la memoria y le darías el control.
Lo normal es que lo que ejecutas es un pequeño programa que a su vez carga el resto del juego. Un "cargador".

Así que, si queremos pasar un juego de disco a cinta o viceversa, lamentablemente tenemos que reprogramar el cargador para adaptarlo al nuevo medio.
Debemos tener en cuenta que son medios muy diferentes.

Primero, si tenemos suerte que el cargador está basado en Basic o en código máquina que usa llamadas al firmware AMSDOS en funciones que trabajan con archivos (equivalentes al RUN o LOAD), son casi idénticas, así que solo debemos hacer mínimos cambios. Aún así, hay diferencias obvias que hay que tener en cuenta.
Por ejemplo, entre otras, que:

- La cinta es un medio secuencial. Eso permite las cargas anónimas (RUN" o LOAD" sin indicar nombre de archivo)
- Los archivos deben estar colocados en el orden de carga.
- Las cargas son visuales (indican mensajes de Loading ... salvo que lo modifiquemos para indicar una admiración (!) al nombre para desactivar esa visualización.
- La carga del primer bloque se va a un buffer de memoria inexistente en disco.


Por todo eso, es probable que debamos modificar o reprogramar el cargador. Pero si tenemos la suerte de que el original está basado en funciones de Firmware es probable que esa reprogramación sea sencilla.
Si tenemos la mala suerte de que el juego o programa está basado en un acceso directo al medio, con un complejo cargador hecho en ensamblador, nos veremos obligados a comprenderlo y crear un substituto alternativo. Una tarea considerablemente más compleja.

El ejemplo que mostraré a continuación contará con tres fases. Primero, pasar un disco "fácil" basado en cargador basic, de disco a cinta estandar, basado en carga estandar. El nivel de este ejemplo es básico.

En el segundo ejemplo, pasaré ese cargador estandar a un cargador en ensamblador turbo.
Primero en carga en memoria directa y luego comprimida.

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 4:38 pm

Ejemplo 1.
Paso de Disco a Cinta. Cargador BASIC muy sencillo.
Nivel de conocimiento Amstrad - Básico.

Herramientas usadas.

Emulador CPCE (link)
CDTMaster (link)

El disco elegido es "Chichen Itza" porque usa un cargador basic desprotegido muy sencillo muy adecuado para este ejemplo.
Link a imagen de disco

Hay que descomprimir el zip del juego en una carpeta. Aunque CPCE no lo necesita, CDTMaster sí.

Arrancamos el CPCE e "introducimos" el disco en la unidad A del emulador ( File -> Open Drive A)
Seleccionamos el DSK que hemos descomprimido y hacemos

Código: Seleccionar todo

cat
Este es el listado:

Código: Seleccionar todo

Drive A: user 0

CHICHEN1.BIN 37K DISC .BAS 1K
CHICHEN1.BIN 40K PANTALLA.BIN 17K

83K free
Este juego se inicia con el comando RUN"DISC
Así que en este caso, tenemos un cargador en basic.

Vamos a cargarlo sin ejecutar con el comando LOAD"DISC

Código: Seleccionar todo

LOAD"DISC

Código: Seleccionar todo

10 CLS:MODE 1:INK 0,0:INK 1,26:INK 2,13:INK 3,9:BORDER 0
20 LOCATE 10,10:PRINT"1.- CHICHEN ITZA PARTE 1":LOCATE 10,12:PRINT"2.- CHICHEN ITZA PARTE 2"
30 a$=INKEY$
40 IF a$="1" OR a$="2" THEN 60
50 GOTO 30
60 LOAD "pantalla
70 IF a$="1" THEN RUN "CHICHEN1.BIN"
80 IF a$="2" THEN RUN "CHICHEN2.BIN"
El código es muy sencillo y fácil de comprender, incluso para quien no está acostumbrado a la programación.
CLS -> limpiar pantalla
Mode 1 -> Modo cuatro colores del Amstrad CPC (también limpia la pantalla. El CLS sería innecesario).
INK ... -> Establece el color concreto de los cuatro colores disponibles del modo 1
BORDER 0 -> Establece el color del borde de la pantalla del CPC a un color (0 = negro)
LOCATE -> Establece la fila y columna donde escribir despues.
PRINT -> Imprime la cadena
a$=INKEY$ -> INKEY$ es el buffer de lo escrito. Como no se habrá escrito nada quedará a la espera. Cuando se pulse una tecla, pasa a la variable "a$"
40 IF a$="1" OR a$="2" THEN 60 -> Cuando a$ sea un "1" o un "2" valos a la línea 60 (o sea, continua la carga)
50 GOTO 30 -> Salta a la línea 30... Es decir, vamos a seguir esperando hasta que pulsen "1" o "2"
60 LOAD "pantalla -> Carga la pantalla en la posición indicada por el propio archivo.
70 IF a$="1" THEN RUN "CHICHEN1.BIN" -> Si pulsaste "1" carga "CHICHEN1.BIN"
80 IF a$="2" THEN RUN "CHICHEN2.BIN" -> Si pulsaste "2" carga "CHICHEN2.BIN"

Eso es todo el cargador. Como podemos ver, sí que podemos hacer directamente RUN"CHICHEN1
pero si lo hacemos, no vemos la pantalla de carga. Directamente cargaríamos el juego.

Vale... Lo primero que podemos ver es que si tenemos disco, tiene sentido un menú, porque podemos cargar indistintamente la primera y la segunda parte. Si pasamos a cinta, deberemos hacer dos cargadores independientes. Uno para la primera parte y otro para la segunda.

Así que vamos a reprogramar el cargador, y salvarlo al mismo disco.

Cargador de la primera parte

Código: Seleccionar todo

10 MODE 1:INK 0,0:INK 1,26:INK 2,13:INK 3,9:BORDER 0
20 LOCATE 12,11:PRINT"CHICHEN ITZA PARTE 1"
30 LOAD "!PANTALLA.SCR"
40 RUN "!
Lo he simplificado. Como podemos ver, he eliminado la comprobación del 1 o 2. El LOAD incluye ahora una admiración para hacer carga silenciosa, y he optado por hacer la carga final de forma anónima para que el segundo cargador solo necesite cambiar el print.
De momento guardamos este cargador en la imagen de disco.

Código: Seleccionar todo

SAVE"CARGA1"

Cargador de la segunda parte

Código: Seleccionar todo

10 MODE 1:INK 0,0:INK 1,26:INK 2,13:INK 3,9:BORDER 0
20 LOCATE 12,11:PRINT"CHICHEN ITZA PARTE 2"
30 LOAD "!PANTALLA.SCR"
40 RUN "!
(Podeis editar el de antes. Solo teneis que modificar la línea 20 con edit 20).
Y guardamos

Código: Seleccionar todo

SAVE"CARGA2"
Estos cargadores NO funcionarán el disco. La carga anónima RUN"! no funciona en disco. Además, el nombre "PANTALLA.SCR" es una elección arbitraria mía. Optaré por renombrar el archivo al pasar a cinta.
Pero al tenerlo en la imagen de disco, ahora con CDTMaster nos bastará con pasar estos archivos de disco a cinta.

Ahora cargamos el CDTMaster. Recuerda cerrar el CPCE para asegurarse de que la imagen del DSK esté cerrada y además ya hemos acabado con él por el momento.

Seleccionamos la pestaña "AMSDOS"
selec_tab_AMSDOS.png
(5.45 KiB) Descargado 170 veces
Seleccionamos la imagen de disco.
select_import_from_DSK.png
(7.59 KiB) Descargado 170 veces
Seleccionamos el archivo "CARGA1.BAS"
CARGA1.png
(11.06 KiB) Descargado 170 veces
Aunque esto sería opcional, optaremos por renombrarlo. Los nombres de archivo de las cintas son más flexibles. He elegido el nombre "CHICHEN1"

Una vez cambiado el nombre, seleccionamos convertirlo en bloques TZX.
...
Por limitaciones del foro no me permite más adjuntos (supongo que por post) así que seguiré en otro comentario

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 4:52 pm

convert_to_tzx.png
(11.29 KiB) Descargado 157 veces

Seleccionamos la velocidad. 2000 baudios.
2000_baud.png
Y aceptamos. Nos dirá que ha generado 1 bloque.
Nota: Esto se refiere a bloques AMSDOS. Cada bloque AMSDOS son dos bloques TZX. Estos bloques se han guardado secuencialmente en el panel derecho de la pestaña "CDT/TZX Editor". Todavía no iremos allí. Nos queda convertir la pantalla y el código.

Seleccionamos la "PANTALLA.BIN" y lo renombramos como "PANTALLA.SCR".
Nuevamente seleccionamos "Convert to TZX Blocks"
Aparte de seleccionar la velocidad otra vez, también vamos a cambiar el campo "Maximum length for other blocks" a 65535. De esta forma, el archivo resultante será un primer bloque normal y otro muy largo. El primer bloque no puede ser más grande de dos kilobytes porque es el tamaño del buffer donde se guarda. Por eso no se muestra directamente en pantalla cuando es un archivo. Pero el segundo bloque se carga directamente en el destino y no hay límite a su tamaño, aunque el estandar es un máximo de 2 kilobytes. Cambiando eso logramos una carga mucho más rápida.
segundo_bloque_largo.png

Por eso nos dirá que solo ha generado 2 bloques.

Y por último, convertiremos "CHICHEN1.BIN"

Ahora pasamos a la pestaña "CDT/TZX Editor"
Veremos que hay una lista en el panel derecho.
Para hacer una selección múltiple, pinchamos con el ratón en el primer bloque, y luego, manteniendo mayúsculas pulsada, pinchamos en el último (NO en ***End of file)
Pulsamos el atajo de teclado "Control+C"
Pulsamos en el panel izquierdo en la posición "***End of file" y luego "Control+V"
Los bloques deberían copiarse.

Luego solo nos queda pulsar en el menú "File" -> "Save CDT/TZX" y guardamos el archivo CDT donde queramos.
Este archivo será la primera parte. Repetimos la operación con "Carga2.bas" y "Chichen2.bin" y tendremos un CDT de la segunda parte.

Caso 1 terminado. :D
Última edición por CECPC el Sab 22 Ago , 2015 6:04 pm, editado 1 vez en total.

Avatar de Usuario
MiguelSky
Lord of Short Time
Lord of Short Time
Mensajes: 6969
Registrado: Sab 08 Oct , 2005 2:02 am
Contactar:

Re: Juegos comprimidos

Mensajepor MiguelSky » Sab 22 Ago , 2015 5:16 pm

=D> =D> =D> =D> =D> =D> =D> =D> =D> =D> =D> =D> =D> =D>

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 8:29 pm

Ejemplo 2.
Crear una cinta con carga turbo.
Nivel de conocimiento Amstrad: Avanzado.

Ahora vamos a ser más ambiciosos. Queremos un cargador "profesional". Queremos carga turbo total y con bloques de colores.

Para ello, tenemos que realizar nuestro cargador en ensamblador.

Afortunadamente contamos con códigos prehechos que simplifican considerablemente la tarea.

Para este caso 2, vamos a usar WinAPE, por su cómodo ensamblador en línea y debugger integrado con Windows que comprobaremos, nos permite una vía muy sencilla de pasar los datos entre el emulador y CDTMaster.

El cargador que vamos a usar es una versión documentada y modificada que obtuve hace tiempo del juego "Karnov".

La ventaja de este cargador es que lee bloques codificados de forma idéntica al CPC en sus funciones de firmware BC9E y BCA1. Esto del firmware lo explico despues con más detalle.

Gracias a eso podemos, usar la función de CDTMaster para convertir un archivo AMSDOS en bloques TZX (un solo bloque en este caso) y quedándonos con el bloque TZX que representa los datos.

Este tipo de bloques además incluye comprobación de errores con un código de redundancia cíclica CRC-16 cada 256 bytes, por lo que obtenemos cierta fiabilidad de carga (en caso de errores, normalmente obtendremos confirmación y podemos optar por resetear o bloquearnos intencionalmente).

Antes de comenzar esa parte, debemos saber un poco sobre las llamadas firmware del Amstrad.

El CPC, además de sus famosos 64/128 KBytes de Ram, 32 o más KBytes de Rom. Sin entrar demasiado en detalles, este código dispone de un "puente" en unas posiciones de memoria RAM específicas, para poder acceder a funciones específicas similares a lo que logramos mediante el Basic normal.

La lista de estas funciones la podeis consultar desde este enlace.
http://www.cpcwiki.eu/index.php/BIOS_Functions

Aquí un extracto rápido de llamadas, acciones, registros de entrada y de salida.
http://www.cpcwiki.eu/imgs/e/e1/The_Amstrad_CPC_Firmware_Guide.txt

Y eso es por lo que vamos a empezar...
Vamos a programar, paso a paso, nuestro primer cargador turbo.

Espero que sepais ensamblador. A pesar que no necesitamos conocerlo al dedillo, necesitamos un poco de conocimientos mínimos.
Pero como con los años nos oxidamos, un pequeño paseo para refrescarnos la memoria.

Abrid vuestro WinAPE.
Antes de ponernos a usarlo, unas recomendaciones. A mí me gusta usarlo en modo CPC6128 clásico.
Para configurarlo, usad estas opciones.
Settings -> General
En las pestañas
-> Memory

Desactivad Multiface - Cartidge y 256K Silicon Disk
Escoger 128 K
Luego en las ROMs, tengo...
Lower - OS6128
Upper 0 - Basic 1-1
Upper 8 - AMSDOS

Estas opciones podeis salvarlas en "Settings" para ahorraros cambiar de nuevo las configuraciones

Luego en la pestaña "General" desactivad "Enable Plus Features"

Y le haceis un Reset a la emulación.

De esta forma, WinAPE emula más parecido a un CPC6128 original.

Bien... Pulsad Debug - Pause (O por atajo de teclado, F7)

La emulación se para, y sale la ventaja del debugger integrado.
Os recomiendo que la estireis bien.
Winape_debugger.png
La pantalla que podemos observar está dividido en varios bloques.

En el de arriba a la derecha, podemos ver los registros del Z80.
Debajo de los registros, el contenido de la memoria asociado a la pila (como podemos ver, la posición que nos muestra "BFEC" coincide con el valor del registro "SP" que es el que sirve para decir donde está situado el puntero de pila.

La ventana principal, arriba a la izquierda, es una traducción al vuelo de código máquina a ensamblador.
En concreto, primero la posición de memoria, luego la instrucción ensamblador traducida de dicha posición, en tercer lugar el código máquina (los valores de la memoria) y en último término la visualización de la misma información como "ascii". Normalmente no se trata de información ASCII, así que lo que aparecen son símbolos sin significado.

Como podemos ver, salvo que lo desplacemos manualmente, el debugger se abre automáticamente en la posición donde está trabajando el procesador Z80 emulado. Posición que está indicada por el registro "PC" (1BBF en el screenshot).

En la ventaja bajo esta, tenemos un editor hexadecimal tradicional de la memoria.
Debajo, unas pocas opciones. Una de las más interesantes corresponde a la función "Memory".
Dado que el Z80 solo dispone de un espacio de memoria de 16 Bits, que corresponde a 64KBytes, para poder acceder a más memoria o a la ROM, lo hace el Z80 es "paginar" la memoria. Lo que el procesador ve no corresponde a la memoria RAM lineal, sino que en función de la configuración, los bancos de memoria, ROM y RAM, segmentados en 4 bloques de 16 KBytes, pueden mapearse de diversas formas en la memoria que el procesador ve, lee, escribe y/o ejecuta.

Por ejemplo, en el momento que lo he parado para el pantallazo, el procesador estaba trabajando en ROM. Por eso, tanto la ventana de debugger como de datos ve un montón de cosas que ahora mismo están en ROM.

Si pulsamos la opción "Memory - Any", veremos que lo que muestra cambia mostrándonos por defecto lo que hay en RAM, que lo más probable (en mi caso así es, es la RAM vacía tras un reset)

Bien... Ya nos hemos familiarizado un poco con el debugger. Al cerrarlo (NO minimizarlo), el emulador comenzará a funcionar con normalidad.
NOTA: Si minimizamos el debugger, la emulación sigue parada. La ventana de debugger no se muestra en la barra de tareas de Windows, pero no es una ventana modal.
Si alguna vez parece que WinAPE se ha quedado "colgado" fijaros bien, cuando tengais como ventana con foco el emulador, si no sale el debugger abajo a la izquierda totalmente minimizado. Es muy discreto...


Ahora vamos a probar el ensamblador en línea.
Assembler -> Show Assembler
File -> New

Y ahora vamos a crear nuestro primer código, para ponernos en situación.
Nuestro primer código va a ser un simple "Mode 1" usando el firmware y volveremos a Basic.

Código: Seleccionar todo

ORG #4000 ; <- Hace que lo que vayamos a ensamblar se compile en la posición 0x4000 de memoria

; La llamada que firmware buscamos es "SCR SET MODE"
; Corresponde a &BC0E. Recuerda consultar
; http //www.cpcwiki.eu/imgs/e/e1/The_Amstrad_CPC_Firmware_Guide.txt

; Una buena costumbre es salvar a la pila los registros que se van a "corromper" si tenemos la intención
; de volver a BASIC
; Si llegamos a este código desde BASIC, con un CALL y no corrompemos los registros, podemos regresar con un
; RET

; Según la documentación, se corrompen AF, BC, DE, HL

PUSH AF
PUSH BC
PUSH DE
PUSH HL

LD A,1 ; Cargamos el modo 1
CALL &BC0E

POP HL ; Recuerda que es una pila, y hay que recuperar los valores en orden inverso
POP DE
POP BC
POP AF

RET
Le damos a Assemble -> Run

Y en BASIC hacemos

Código: Seleccionar todo

CALL &4000
Si todo va correctamente, veremos a nuestro Basic hacer una limpieza de pantalla como si hubieramos hecho un "Mode 1"

Por cierto, que siendo estrictos, deberíamos hacer un "Memory &3fff" antes de ensamblar ya que estamos compartiendo la memoria de nuestro BASIC. Pero como no estamos escribiendo BASIC en este momento, y por ahorrarnos código "inútil" (por ahora) trabajaremos directamente.

Hemos comprobado que nuestro código ha hecho un MODE 1 con éxito.
Ahora vamos a hacer el equivalente de nuestro BASIC, de cambiar las tintas y escribir "CHICHEN ITZA" como en el cargador de BASIC.

Código: Seleccionar todo

ORG #4000

PUSH AF
PUSH BC
PUSH DE
PUSH HL

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,12
CALL &BB6F ; TXT SET COLUMN
LD A,11
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter


POP HL
POP DE
POP BC
POP AF

RET

cadena:

DB "CHICHEN ITZA - PARTE I"
DB 0 ; Fin de cadena
Si hacemos "CALL &4000" ahora ya veremos algo muy parecido al cargador BASIC.
Falta la carga de la pantalla y el juego.

De hecho, si introdujeramos la imagen anterior de disco, y cargáramos la pantalla con "LOAD" comprobaríamos que los colores son correctos.

Pero eso no vamos a hacerlo con firmware, sino con un cargador especial de ensamblador ya construido. Poco más que copiar y pegar.

Seguimos en otro post

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 9:49 pm

A riesgo de romper un poco el hilo del desarrollo del cargador, voy a hacer unos pequeños "incisos" y desvíos para que sepamos un poco como funcionan las cintas.
Simplificando, hay dos lineas de conexión entre la cinta y los chips del Amstrad. Una es para el motor. El Amstrad le dice a la cinta cuando puede encenderse (suponiendo que el "PLAY" está pulsado) y cuando pararse.

La otra linea es de entrada/salida. Si la cinta está en modo "PLAY", le envía la información del sonido al Amstrad. El Amstrad puede enviarle igualmente la información a la cinta, que solo guardará si tenemos pulsado el REC.

La información de la cinta, que es principalmente una onda de sonido, podríamos decir que es el valor de la amplitud de la onda en el tiempo, es simplificada a un solo bit. La onda puede ser positiva o negativa.
Sería algo así como una "onda cuadrada", o simplificada a ello. En la realidad tal onda no puede representarse en un medio como una cinta, así que sufre algunas deformaciones, pero nada importante.

A todos nuestros efectos, no nos importa tanto la forma de la onda, como el tiempo en que el valor de la onda es positivo o negativo (bit 1 o 0).

Para representar información, lo que se hace es medir el tiempo en que dicha onda tiende a cambiar de signo.

Esto es, como si se tratara de un morse, "punto" y "raya". Un punto es una duración de bits 1 y 0s. Una raya es otra duración diferente de 1 y 0s. Esto es porque por temas de las ondas de sonido, no puede representarse un estado de bit 1 demasiado largo, ya que requiere una frecuencia más allá de las capacidades de la cinta.
Así que jugamos con intervalos válidos. Lo que medimos es un ciclo de 1s y 0S.

Digamos que si un ciclo de 1 y luego 0 son 600 ciclos de reloj, y a dichos ciclos le asignamos un valor de BIT 0, al bit 1 consideramos que sería la mitad de tiempo. Digamos que 300. Usaríamos el punto frontera (450) para determinar si un ciclo es un 1 o un 0. Y si fuera alarmántemente grande (Ej. más de 2000) consideraríamos que el sonido "se ha cortado" y que ese bloque de datos ha finalizado.

La rutina estandar carga del AMSDOS funciona aproximadamente así.

Espera un tiempo a "escuchar" ciclos siempre de la misma frecuencia (dentro de un intervalo). Las cintas no son precisas.
Podría obtener 400, 389, 412, 399, 403, 405, 397... Va calculando una "media" para hacerse una idea de la frecuencia (los baudios) en la que se está transmitiendo un bloque.
Para el AMSDOS, durante esa fase que se llama "sincronización", se considera que este valor es el que representa al bit 1, mientras que el bit 0 (más probable cuando hay datos vacios) se usa un pulso de la mitad de duración que el bit 1.
Esta forma de condificar tiene una gran ventaja, y es que permite usar la misma rutina independientemente de la velocidad de referencia. Puedes cargar igual a 1000, 1500 2000 baudios, etc. etc.
Hace más tolerante a la cinta, ya que no todas son exactas. Una cinta más lenta podría leer una cinta de 1000 baudios como si fuera de 950, solo porque el motor es un poco más lento de lo que debería o la cinta es más tensa de lo esperado. Pero no es problema, ya que ese cambio de velocidad afecta por igual a los bits 1 y 0.
Otros cargadores implementados en otras computadoras o en ensamblador, usan velocidades de refenrecia fijas, y eso los hace más intolerantes a cambios en la velocidad.

Una vez que ha obtenido un mínimo de pulsos y estos se mantienen en un patrón regular, él espera la llegada de un bit 0, que representa el fin de la sincronización.

Despues de eso, le sigue un byte de identificador de bloque. Un número arbitrario que se usa para distinguir entre los bloques que se usan de cabecera (nombre de archivo, número de bloque de archivo, tipo de archivo, etc... Los que se leen y se usan para poner mensajes en "Loading...") y los de datos.

A estos "bloques", para que no nos confundamos, los vamos a llamar "Bloques Turbo". Estos "Bloques Turbo" en los archivos TZX/CDT se representan con una entrada del tipo "Turbo Speed Data Block".

Los archivos AMSDOS estandar se dividen en trozos de datos de 2KBytes máximo.
Cada trozo lleva dos "Bloques Turbo".
Uno representa la cabecera. Contiene el nombre del archivo, tipo, posición de carga por defecto (se puede sobreescribir en el comando para binarios), posición de arranque que se usará si se carga con RUN", longitud a efectos de archivo (independientemente de como va en la cinta), número de bloque, si es el primer bloque, si es el último bloque y no debe cargar más, etc. etc.

En la definición de TZX, no existe ese byte de sincronización que existe en AMSDOS, así que para el CDT, es el primer byte de datos que contiene dicho bloque. Si con CDTMaster observais dicho Byte, vereis que los bloques para cabecera, tienen un byte presincronización (el primer byte de datos) = 44 (0x2C), mientras que los bloques turbo de datos se indican con 22 (0x16).

Luego, otra cosa que podreis observar, es que los datos se guardar siempre en subloques de datos de 256 bytes.
Cada subbloque va seguido de dos bytes que representan un CRC-16 de los datos de dicho bloque.

Así, aunque el archivo tenga una longitud de 50 bytes, lo que salvará el AMSDOS es.
Dos bloques TZX de 259 bytes (más alguno extra que carece de importancia, solo como garantía de que el último byte es interpretado bien).
Esto es, un byte de identificación (0x2C o 0x16)+ subbloque de 256 + 2 CRC.
El primero 0x2C y contiene los datos de la cabecera
El segundo 0x16 con los datos del archivo. A pesar de que el archivo tiene 50 bytes, se rellenará con un grupo de bytes (normalmente 0) para completar un bloque de 256 bytes y luego se hace el CRC, contando incluido con dichos bytes de relleno.
Eso sí, a efectos de carga, en memoria solo trasladará esos 50 bytes. El resto solo se procesarán para calcular el CRC final y saber si los 50 bytes + relleno coinciden con el código de comprobación CRC.

Así pues, a nivel de cinta, ocupa lo mismo un archivo de 1 byte que uno de 256, porque siempre se rellena a bloques de 256 bytes. Eso sí, si ocupara 257, entonces incluiría dos bloques, el primero para los primeros 256 y el segundo el byte que falta más 255 más de relleno. Más CRCs y tal...

La función CAS READ (&BCA1) del firmware nos permite leer "Bloques turbo" acordes a esta forma de codificación. Eso sí, sin colores ni nada.

¿Por qué todo este rollo? Porque vamos a usar esta función CAS READ para leer nuestros bloques como ejemplo, ya que el cargador en ensamblador por el que luego daremos el cambiazo lee bloques en el mismo formato, así que es tan simple cambiar la llamada a CAS READ, por el fragmento de nuestro cargador "Karnov" modificado. Copiar y pegar.

Pero antes de nada, necesitamos una cinta que cargar. Así que volvemos a CDTMaster a nuestro disco, para convertirlo en bloques para la función "CAS READ". (Recuerda asegurarte de que los emuladores no estén usando la misma imagen de disco o CDTMaster no podrá abrirlo).
Si teníais CDTMaster iniciado, mejor cerrarlo y abridlo de nuevo para partir de los paneles limpios.

Abrimos igual que antes la imagen de disco, y nos dirigimos al archivo PANTALLA.BIN.
Antes de convertirlo, por curiosidad, podeis fijaros en la información del panel.
Ved que la posición de carga "Load Position" es de 0xC000 (indicado en decimal en la zona editable, en hexadecimal al lado). Justo la posición de memoria de la pantalla en situación estandar.

Le damos a convertir a TZX como antes. Pero esta vez, en las opciones de conversión, vamos a indicar algo diferente.
En vez de la opción de antes, vamos a poner en la opción "Maximum length for first block" 65535.
Esto está fuera del rango indicado (hasta 2048) pero nos permite obtener el bloque de datos.

Dado que no lo vamos a cargar con LOAD sino con la función "CAS READ" que puede cargar bloques turbo directamente, podemos... debemos convertirlo en un solo bloque.
Y también, porque vamos a forzar un poco la carga, vamos a poner como velocidad 3000 baudios.

Echemos un vistazo a la pestaña de CDT/TZX Editor.

Esta vez, en el panel de la derecha veremos solo dos bloques TZX, tal y como esperábamos.
Uno de la cabecera, pequeño y otro de 16515 bytes.
Este es el que nos interesa.

Si hacemos doble click sobre dicho bloque, nos desplega una nueva pantalla.
Edit_block.png
Si pulsamos "Edit Data", nos pasa a un editor hexadecimal.

Tal y como esperábamos, el primer byte a nivel CDT, que es, a nivel AMSDOS, el byte de Identificación de bloque turbo, es "16" (hex) que representa un bloque de datos.

CAS IN READ nos permite especificar el tipo de bloque, y puede ser cualquier byte. Vamos a cambiar este, para que no sea coincidente con los estándares para archivos. Vamos a poner, por ejemplo, 54 para la pantalla, y 55 para el juego (CHICHEN1.BIN).
Se edita directamente en el hexadecimal, y pulsamos (botón abajo) "Save" y luego sobre la pantalla anterior "Accept".

Este bloque lo copiamos a la parte izquierda como la vez anterior, con Control+C en el panel derecho y Control+V en el izquierdo.

Podemos borrar, si queremos no liarnos, los bloques del panel derecho, seleccionándolos y pulsando "delete" (sí, por ahora solo hay atajos de teclado que hay que saberse ;)

Volvemos a la pantalla "AMSDOS Files" y escogemos CHICHEN1.BIN

Esta vez es importante fijarnos en la posición de carga y arranque, porque son totalmente dependientes del juego.
En CHICHEN, la posición de carga y la de ejecución han coincidido, pero no tendría por que ser así.

Es 0x0840

Repetimos operación.

Convertimos, un solo bloque, 3000 baudios, y en la pestaña CDT/TZX Editor, editamos bloque y ponemos el primer byte a 55 tal y como hemos planteado.
Copiamos y pegamos al panel derecho.

Y salvamos a "Chi1test.cdt". La cinta NO está completa, pero nos servirá para las pruebas.

Seguimos en otro post

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 10:47 pm

Vamos a modificar algo más el cargador en proceso de antes, para hacer ya un cargador funcional, eso sí, basado en firmware con la función "CAS READ" - &BCA1.

Como el cargador va a ser funcional, debemos cambiar la posición de ejecución. No puede ser #4000, dado que el juego va desde 0x0840 a 0x0840+0x9300 = 0x9C40

Optaremos por 0x9D00.

Otra cosa es que para reducir los "clicks" de la cinta, vamos a intentar encender el motor tan pronto como sea posible.
Existe una función en el firmware para eso, pero tiene un defecto y es que genera una espera si estaba el motor parado.
En su lugar, activaremos manualmente la línea mediante instrucciones de entrada/salida.
En concreto, mediante las instrucciones:

LD BC,#F709
OUT (C),C

La función "CAS READ" enciende el motor y lo apaga al terminar. Dado que la pantalla de carga la voy a cargar en otra posición de memoria para copiarla "de golpe" como hacían los cargadores clásicos de Dinamic, tras la primera carga vuelvo a encender el motor, para que esa copia de bloque no genere una nueva pausa.

Por lo demás, el nuevo código es sencillo.

Código: Seleccionar todo

ORG #9D00

PUSH AF
PUSH BC
PUSH DE
PUSH HL

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,12
CALL &BB6F ; TXT SET COLUMN
LD A,11
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter:

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter

LD A,#54
LD HL,#4000
LD DE,#4000
CALL &BCA1 ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Al terminar la carga, nos aseguramos de encender otra vez el motor
; de la cinta, para que no se pare tras cargar el bloque
LD BC,#F709
OUT (C),C

; Ahora copiamos la pantalla de carga, desde la posicion 4000 a la memoria
; de la pantalla.
LD HL,#4000 ; Posicion de origen
LD DE,#C000 ; Posicion de destino
LD BC,#4000 ; Longitud del blqoue
LDIR

LD A,#55
LD HL,#0840
LD DE,#9300
CALL &BCA1 ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Hemos terminado de cargar el juego.

POP HL
POP DE
POP BC
POP AF
JP #0840 ; Iniciamos el juego

bad_loading:
CALL #0 ; Reset

cadena:

DB "CHICHEN ITZA - PARTE I"
DB 0 ; Fin de cadena
Podemos compilarlo y probarlo con "CALL &9D00".
Usando el CDT de antes, debería funcionar.

Pero ahora vamos a usar una función del CDTMASTER para crear directamente el archivo de carga.
Cuando ensamblamos, vemos lo que ha ocupado en destino.

Vamos al debugger del WinAPE. Desde allí, vamos a transferir los datos directamente al CDTMaster.
Tras compilar, pulsa F7 para abrir el debugger.
En "Memory" selecciona Any para trabajar con la memoria, que es donde está el cargador que hemos creado.
En la parte de datos, damos al botón derecho y seleccionamos "Goto".
Nos pregunta "Enter Goto Address" y ponemos #9D00
Seleccionamos el código que hemos creado (recuerda copiar el fin de cadena, un byte 0 también), y pulsamos "Control+C"

Ahora vamos al CDTMaster, a la pestaña de "AMSDOS Files". Luego "File" -> "New File"
Eso agregará a la lista un nuevo archivo llamado "Nuevo".
Le pulsamos.
En FileName, ponemos "CHICHEN1"
Pulsamos "Edit Data"
Nos sale un editor hexadecimal que está totalmente vacío.
En el menú de arriba, escoge "Paste HEX" (NO USES CONTROL+V dado que lo pegaría como ASCII)
Pulsamos "Save"
En "Load Position" ponemos "0x9D00"
Y en "Execution Position" también.
Le das a "Convert to TZX Blocks".
Esta vez todo estandar, a 2000 baudios.

Nos vamos a la pestaña "CDT/TZX Editor"
Luego "File" -> "Open CDT/TZX"
Cogemos el archivo de antes "chi1test.cdt"
Cogemos los bloques de la derecha, los seleccionamos a la vez y pulsamos "Control+C"
Pulsamos en el primer bloque de la izquierda y pulsamos "Control+V"
Debería estar todo en el orden correcto.

"Save CDT/TZX" y esta vez lo guardamos como "chi1test2.cdt"

Esta cinta debería ser funcional. Podemos probarla con el CPCE.
Ya solo nos falta cambiar la llamada "CAS READ" por el código alternativo del karnov.

En el siguiente post

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Sab 22 Ago , 2015 11:27 pm

El código que suplanta CAS READ tiene algunas consideraciones diferentes.
Primero, no enciende el motor por sí mismo.
Segundo, para funcionar debe desactivar las interrupciones. Eso significa que si se ejecuta demasiado rápido desde el cambio de las tintas (INK) es posible que eso no se refleje, dado que esa función no provoca un cambion inmediato, sino que espera a unas interrupciones temporales.
Como "hack", realizo un "MODE 1" redundante, para dar ese tiempo.

Tercero, la posición de carga, en vez de HL se usa el registro IX.

El código de carga lo proporciono integrado al completo.
No entraré al detalle sobre el código "carga_bloque". De hecho, tengo una versión mejor documentada pero en otro equipo y ahora no puedo postearlo.

El cargador quedaría tal que así.

Código: Seleccionar todo

ORG #9D00

PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IX

CALL motor_on

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1 . Redundante para ganar tiempo.

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,12
CALL &BB6F ; TXT SET COLUMN
LD A,11
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter:

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter

DI ; Desactivamos interrupciones.

LD A,#54
LD IX,#4000
LD DE,#4000
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Ahora copiamos la pantalla de carga, desde la posicion 4000 a la memoria
; de la pantalla.
LD HL,#4000 ; Posicion de origen
LD DE,#C000 ; Posicion de destino
LD BC,#4000 ; Longitud del blqoue
LDIR

LD A,#55
LD IX,#0840
LD DE,#9300
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Hemos terminado de cargar el juego.
POP IX
POP HL
POP DE
POP BC
POP AF
CALL motor_off
EI
JP #0840 ; Iniciamos el juego

bad_loading:
CALL #0 ; Reset

cadena:

DB "CHICHEN ITZA - PARTE I"
DB 0 ; Fin de cadena


carga_bloque: EXX
PUSH BC
PUSH DE
PUSH HL
CALL start_bloque
POP HL
POP DE
POP BC
EXX
RET
start_bloque:
LD BC,#7F10
OUT (C),C
LD C,A
LD DE,#1000
LD HL,#544a
; LD HL,#534C
; #534C ->Colores en modo sincronización 53 ->cyan brillante 4c-> Rojo brillante
; Prueba 54-> negro 4A -> Amarillo brillante
EXX
L3061: PUSH DE
CALL L30AF
POP DE
JR NC,L3061
EXX
LD HL,#5457
; LD HL,#4A5f
; Colores en modo carga 4a44 -> 4a -> Amarillo brillante 44-> Azul
; Cambio a 54 -> negro 57 -> Azul Celeste
EXX
LD BC,#FFFF
LD (L3092),BC
INC B
L3075: CALL L30EF
RET NC
LD (IX+0),A
INC IX
DEC B
CALL Z,L3091
RET NC
DEC DE
LD A,D
OR E
JR NZ,L3075
CP B
SCF
RET Z
L308B: CALL L30EF
RET NC
DJNZ L308B
L3091: DEFB #01
L3092: DEFB #00
L3093: DEFB #00
CALL L30EF
RET NC
OR B
INC A
JR NZ,L30AB
CALL L30EF
RET NC
OR C
LD (L3092),A
LD (L3093),A
INC A
LD B,A
SCF
RET Z
L30AB: LD A,#02
OR A
RET
L30AF: LD L,#55
CALL L3120
RET NC
LD DE,#0000
LD H,D
L30B9: CALL L3120
RET NC
EX DE,HL
LD B,#00
ADD HL,BC
EX DE,HL
DEC H
JR NZ,L30B9
L30C5: LD H,C
LD A,C
SUB D
LD C,A
SBC A,A
LD B,A
EX DE,HL
ADD HL,BC
EX DE,HL
CALL L3120
RET NC
LD A,D
SRL A
SRL A
ADC A,D
SUB H
JR C,L30C5
SUB C
JR C,L30C5
LD A,D
RRA
ADC A,D
LD H,A
LD (L30F4),HL
CALL L30EF
RET NC
EXX
XOR C
EXX
RET NZ
SCF
RET
L30EF: PUSH DE
PUSH BC
LD E,#08
L30F3: DEFB "!"
L30F4: DEFB #00,#00
CALL L3120
CALL C,L3128
JR NC,L311D
LD A,H
SUB C
SBC A,A
RL D
LD HL,(L3092)
XOR H
JP P,L3113
LD A,H
XOR #08
LD H,A
LD A,L
XOR #10
LD L,A
SCF
L3113: ADC HL,HL
LD (L3092),HL
DEC E
JR NZ,L30F3
LD A,D
SCF
L311D: POP BC
POP DE
RET
L3120: LD A,R
RRCA
RRCA
INC A
AND #1F
LD C,A
L3128: LD B,#F5
L312A: LD A,C
ADD A,#02
LD C,A
JR C,L3147
IN A,(C)
XOR L
AND #80
JR NZ,L312A
LD R,A
RRC L
EXX
JR C,L3143
OUT (C),L
EXX
SCF
RET
L3143: OUT (C),H
EXX
RET
L3147: XOR A
INC A
RET


motor_off: LD BC,#F708
OUT (C),C
RET
motor_on: LD BC,#F709
OUT (C),C
RET
Si repetimos los pasos. Compilación, pegue al CDTMaster, cambio en el CDT y demás, obtendremos ya, por fin, una cinta con carga turbo, bordes y colores y demás.

Ya solo nos falta agregar compresión.

En otro post

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Dom 23 Ago , 2015 12:22 am

Para comprimir y descomprimir, creé mi propio compresor que ya publiqué en un foro (el de mforos) en su momento.
El código original fue en C, y lo he portado a c# en CDTMaster (es GPL, el código está en sourceforge).

Hice un descompresor en ensamblador para Amstrad. El algoritmo no requiere buffer. Solo hay que saber que descomprime en orden "ascendente". La rutina de compresión recibe la posición de origen, la de destino, y el tamaño de los datos comprimidos.

Vamos a poner un ejemplo al vuelo para comprenderlo, sin crear cinta ni nada.

En el WinAPE, cargamos el disco del Chichen.

Vamos a hacer un código de carga y bloqueo, para que la pantalla esté en memoria.

Código: Seleccionar todo

10 LOAD"PANTALLA.BIN"
20 GOTO 20
RUN
La pantalla (aunque con los colores equivocados, solo es para demostración) se carga y la vemos.
Esta pantalla ocupa &4000 bytes (16 Kbytes).
Vamos al debugger. Ponemos la memoria en modo "any" y copiamos el bloque (Paste HEX) al editor del CDTMaster y pegamos como hicimos en el caso anterior para hacer un archivo.
Sin embargo, esta vez usamos la opción del CDTMaster, Tools, "compress data".
Una vez comprimido, copiamos todo y usamos la opción "Edit-> Copy HEX".

Volvemos al WinApe, nos vamos a la posición de memoria #5000 y pegamos el resultado comprimido.

Ahora vamos a ver un ejemplo de ensamblador de descompresión.
La rutina propiamente dicha es de "decod" para abajo.
El llamador es bastante evidente. La mejor manera de ver el tamaño del código comprimido es que, tras editar los datos comprimidos en CDTMaster, aceptes, y el tamaño del archivo será justamente ese.

Código: Seleccionar todo

ORG #4000

PUSH HL
PUSH DE
PUSH BC
LD HL,#5000 ; Dirección de código comprimido
LD DE,#C000 ; Dirección de descompresión
LD BC,#08AC ; Tamaño de los datos comprimidos
CALL decod
POP BC
POP DE
POP HL
RET



decod:
PUSH HL
PUSH BC
POP HL
POP BC
ADD HL,BC
PUSH HL
PUSH BC
POP HL
POP BC
;; Ahora BC Contiene la última posición de los datos comprimidos
paso:
LD A,B
CP H
RET C ;; Salir si nos hemos pasado
JR NZ,falta
LD A,C
CP L
RET C ;; Salir si nos hemos pasado
RET Z ;; Salir si hemos llegado
falta:
LD A,(HL)
INC HL
CP #80
JR NC,LRE
;; Si menor que #80, solo hay que copiar los datos
PUSH BC
LD C,A
XOR A
LD B,A
LDIR
POP BC
JR paso
lRE:
CP #FF
JR NZ,lz77
PUSH BC
LD A,(HL)
INC HL
PUSH AF
LD A,(HL)
INC HL
CP #80
JP p,lrelargo
JR lrecorto
lrelargo:
CP A
RL A
CP A
RR A
LD B,A
LD A,(HL)
INC HL
LD C,A
JR go_lre
lrecorto:
LD C,A
XOR A
LD B,A
go_lre:
POP AF
PUSH HL
LD (DE),A
PUSH DE
POP HL
INC DE
DEC BC
LDIR
POP HL
POP BC
JR paso
lz77:
PUSH BC
LD C,(HL)
INC HL
BIT 6,A
JR Z,noextra
LD B,C
LD C,(HL)
INC HL
JR cpbloq
noextra:
LD B,0
cpbloq:
AND #3F
PUSH HL
PUSH AF
PUSH DE
POP HL ;; HL = final de buffer de salida. Restamos BC
LD A,L
SUB C
LD L,A
LD A,H
SBC B
LD H,A
POP AF
LD B,0
LD C,A
LDIR
POP HL
POP BC
JR paso
Cuando hacemos "CALL &4000" nos aparece la pantalla de demostración de nuevo.

Pues bien... Ahora solo hay que mezclar esto con el código de carga anterior.

Para ello, vamos a guardar en el CDT la pantalla comprimida y los datos comprimidos también.
PANTALLA.BIN: Tamaño antes de comprimir, #4000. Tamaño tras comprimir, #08AC
CHICHEN1.BIN: Tamaño antes de comprimir, #9300. Tamaño tras comprimir, #7957

Una cosa que tenemos que tener en cuenta, es que los datos comprimidos los carguemos en una posición que no se superponga durante la compresión.
Dado que la descompresión es ascendente, colocaremos el código de forma que la posición de carga sea algo superior a la original de forma que no lleguen a pisarse.
Nuestro código de carga lo colocamos en #9d00. Pues vamos a hacer que nuestro código comprimido acabe en #9cff

#9d00 - #7957 = #2349

Así pues, vamos a cargar los datos cargados en esa posición. Desde ahí descomprimimos a la posición de carga original

Los archivos comprimidos los pasamos a TZX a 3000 baudios en un solo bloque, igual que en el caso anterior y cambiamos el ID por 54 y 55.

Ya solo falta agregar el código de compresión, ajustar las posiciones de carga, juntar todo, y ¡Hecho!

El código del cargador final.

Código: Seleccionar todo

ORG #9D00

PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IX

CALL motor_on

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1 . Redundante para ganar tiempo.

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,12
CALL &BB6F ; TXT SET COLUMN
LD A,11
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter:

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter

DI ; Desactivamos interrupciones.

LD A,#54
LD IX,#4000
LD DE,#08AC
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

LD HL,#4000
LD DE,#C000
LD BC,#08AC
CALL decod

LD A,#55
LD IX,#2349
LD DE,#7957
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

LD HL,#2349
LD DE,#0840
LD BC,#7957
CALL decod

; Hemos terminado de cargar el juego.
POP IX
POP HL
POP DE
POP BC
POP AF
CALL motor_off
EI
JP #0840 ; Iniciamos el juego

bad_loading:
CALL #0 ; Reset

cadena:

DB "CHICHEN ITZA - PARTE I"
DB 0 ; Fin de cadena


carga_bloque: EXX
PUSH BC
PUSH DE
PUSH HL
CALL start_bloque
POP HL
POP DE
POP BC
EXX
RET
start_bloque:
LD BC,#7F10
OUT (C),C
LD C,A
LD DE,#1000
LD HL,#544a
; LD HL,#534C
; #534C ->Colores en modo sincronización 53 ->cyan brillante 4c-> Rojo brillante
; Prueba 54-> negro 4A -> Amarillo brillante
EXX
L3061: PUSH DE
CALL L30AF
POP DE
JR NC,L3061
EXX
LD HL,#5457
; LD HL,#4A5f
; Colores en modo carga 4a44 -> 4a -> Amarillo brillante 44-> Azul
; Cambio a 54 -> negro 57 -> Azul Celeste
EXX
LD BC,#FFFF
LD (L3092),BC
INC B
L3075: CALL L30EF
RET NC
LD (IX+0),A
INC IX
DEC B
CALL Z,L3091
RET NC
DEC DE
LD A,D
OR E
JR NZ,L3075
CP B
SCF
RET Z
L308B: CALL L30EF
RET NC
DJNZ L308B
L3091: DEFB #01
L3092: DEFB #00
L3093: DEFB #00
CALL L30EF
RET NC
OR B
INC A
JR NZ,L30AB
CALL L30EF
RET NC
OR C
LD (L3092),A
LD (L3093),A
INC A
LD B,A
SCF
RET Z
L30AB: LD A,#02
OR A
RET
L30AF: LD L,#55
CALL L3120
RET NC
LD DE,#0000
LD H,D
L30B9: CALL L3120
RET NC
EX DE,HL
LD B,#00
ADD HL,BC
EX DE,HL
DEC H
JR NZ,L30B9
L30C5: LD H,C
LD A,C
SUB D
LD C,A
SBC A,A
LD B,A
EX DE,HL
ADD HL,BC
EX DE,HL
CALL L3120
RET NC
LD A,D
SRL A
SRL A
ADC A,D
SUB H
JR C,L30C5
SUB C
JR C,L30C5
LD A,D
RRA
ADC A,D
LD H,A
LD (L30F4),HL
CALL L30EF
RET NC
EXX
XOR C
EXX
RET NZ
SCF
RET
L30EF: PUSH DE
PUSH BC
LD E,#08
L30F3: DEFB "!"
L30F4: DEFB #00,#00
CALL L3120
CALL C,L3128
JR NC,L311D
LD A,H
SUB C
SBC A,A
RL D
LD HL,(L3092)
XOR H
JP P,L3113
LD A,H
XOR #08
LD H,A
LD A,L
XOR #10
LD L,A
SCF
L3113: ADC HL,HL
LD (L3092),HL
DEC E
JR NZ,L30F3
LD A,D
SCF
L311D: POP BC
POP DE
RET
L3120: LD A,R
RRCA
RRCA
INC A
AND #1F
LD C,A
L3128: LD B,#F5
L312A: LD A,C
ADD A,#02
LD C,A
JR C,L3147
IN A,(C)
XOR L
AND #80
JR NZ,L312A
LD R,A
RRC L
EXX
JR C,L3143
OUT (C),L
EXX
SCF
RET
L3143: OUT (C),H
EXX
RET
L3147: XOR A
INC A
RET


motor_off: LD BC,#F708
OUT (C),C
RET
motor_on: LD BC,#F709
OUT (C),C
RET



decod:
PUSH HL
PUSH BC
POP HL
POP BC
ADD HL,BC
PUSH HL
PUSH BC
POP HL
POP BC
;; Ahora BC Contiene la última posición de los datos comprimidos
paso:
LD A,B
CP H
RET C ;; Salir si nos hemos pasado
JR NZ,falta
LD A,C
CP L
RET C ;; Salir si nos hemos pasado
RET Z ;; Salir si hemos llegado
falta:
LD A,(HL)
INC HL
CP #80
JR NC,LRE
;; Si menor que #80, solo hay que copiar los datos
PUSH BC
LD C,A
XOR A
LD B,A
LDIR
POP BC
JR paso
lRE:
CP #FF
JR NZ,lz77
PUSH BC
LD A,(HL)
INC HL
PUSH AF
LD A,(HL)
INC HL
CP #80
JP p,lrelargo
JR lrecorto
lrelargo:
CP A
RL A
CP A
RR A
LD B,A
LD A,(HL)
INC HL
LD C,A
JR go_lre
lrecorto:
LD C,A
XOR A
LD B,A
go_lre:
POP AF
PUSH HL
LD (DE),A
PUSH DE
POP HL
INC DE
DEC BC
LDIR
POP HL
POP BC
JR paso
lz77:
PUSH BC
LD C,(HL)
INC HL
BIT 6,A
JR Z,noextra
LD B,C
LD C,(HL)
INC HL
JR cpbloq
noextra:
LD B,0
cpbloq:
AND #3F
PUSH HL
PUSH AF
PUSH DE
POP HL ;; HL = final de buffer de salida. Restamos BC
LD A,L
SUB C
LD L,A
LD A,H
SBC B
LD H,A
POP AF
LD B,0
LD C,A
LDIR
POP HL
POP BC
JR paso
Y la cinta final...
chichen1.cdt
(34.11 KiB) Descargado 96 veces
Última edición por CECPC el Mié 02 Sep , 2015 8:43 pm, editado 1 vez en total.

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Dom 23 Ago , 2015 10:35 am

Últimas consideraciones...
Si repetís las operaciones con la segunda parte, vemos que algo no coincide.

La segunda parte es más larga. El tamaño es 0x9d80.
9d80 + 0840 = A5C0

Esto está demasiado cerca de los límites de las zonas que usa el firmware.
Si bien, usando un cargador propio podemos sobreescribir estas zonas, es recomendable no hacerlo.
http://www.cpcalive.com/docs/amstrad_cp ... y_map.html

Vamos a poner un límite de A600. Hay sitio para desplazar el comprimido, pero no el cargador.
Así que podemos poner el cargador antes del juego. En concreto a #600

El cargador resultante sería este...

Código: Seleccionar todo

ORG #0600

PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IX

CALL motor_on

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1 . Redundante para ganar tiempo.

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,10
CALL &BB6F ; TXT SET COLUMN
LD A,12
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter:

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter

DI ; Desactivamos interrupciones.

LD A,#54
LD IX,#4000
LD DE,#08AC
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

LD HL,#4000
LD DE,#C000
LD BC,#08AC
CALL decod

LD A,#55
LD IX,#2791
LD DE,#7e6f
CALL carga_bloque ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

LD HL,#2791
LD DE,#0840
LD BC,#7e6f
CALL decod

; Hemos terminado de cargar el juego.
POP IX
POP HL
POP DE
POP BC
POP AF
CALL motor_off
EI
JP #0840 ; Iniciamos el juego

bad_loading:
CALL #0 ; Reset

cadena:

DB "CHICHEN ITZA - PARTE II"
DB 0 ; Fin de cadena


carga_bloque: EXX
PUSH BC
PUSH DE
PUSH HL
CALL start_bloque
POP HL
POP DE
POP BC
EXX
RET
start_bloque:
LD BC,#7F10
OUT (C),C
LD C,A
LD DE,#1000
LD HL,#544a
; LD HL,#534C
; #534C ->Colores en modo sincronización 53 ->cyan brillante 4c-> Rojo brillante
; Prueba 54-> negro 4A -> Amarillo brillante
EXX
L3061: PUSH DE
CALL L30AF
POP DE
JR NC,L3061
EXX
LD HL,#5457
; LD HL,#4A5f
; Colores en modo carga 4a44 -> 4a -> Amarillo brillante 44-> Azul
; Cambio a 54 -> negro 57 -> Azul Celeste
EXX
LD BC,#FFFF
LD (L3092),BC
INC B
L3075: CALL L30EF
RET NC
LD (IX+0),A
INC IX
DEC B
CALL Z,L3091
RET NC
DEC DE
LD A,D
OR E
JR NZ,L3075
CP B
SCF
RET Z
L308B: CALL L30EF
RET NC
DJNZ L308B
L3091: DEFB #01
L3092: DEFB #00
L3093: DEFB #00
CALL L30EF
RET NC
OR B
INC A
JR NZ,L30AB
CALL L30EF
RET NC
OR C
LD (L3092),A
LD (L3093),A
INC A
LD B,A
SCF
RET Z
L30AB: LD A,#02
OR A
RET
L30AF: LD L,#55
CALL L3120
RET NC
LD DE,#0000
LD H,D
L30B9: CALL L3120
RET NC
EX DE,HL
LD B,#00
ADD HL,BC
EX DE,HL
DEC H
JR NZ,L30B9
L30C5: LD H,C
LD A,C
SUB D
LD C,A
SBC A,A
LD B,A
EX DE,HL
ADD HL,BC
EX DE,HL
CALL L3120
RET NC
LD A,D
SRL A
SRL A
ADC A,D
SUB H
JR C,L30C5
SUB C
JR C,L30C5
LD A,D
RRA
ADC A,D
LD H,A
LD (L30F4),HL
CALL L30EF
RET NC
EXX
XOR C
EXX
RET NZ
SCF
RET
L30EF: PUSH DE
PUSH BC
LD E,#08
L30F3: DEFB "!"
L30F4: DEFB #00,#00
CALL L3120
CALL C,L3128
JR NC,L311D
LD A,H
SUB C
SBC A,A
RL D
LD HL,(L3092)
XOR H
JP P,L3113
LD A,H
XOR #08
LD H,A
LD A,L
XOR #10
LD L,A
SCF
L3113: ADC HL,HL
LD (L3092),HL
DEC E
JR NZ,L30F3
LD A,D
SCF
L311D: POP BC
POP DE
RET
L3120: LD A,R
RRCA
RRCA
INC A
AND #1F
LD C,A
L3128: LD B,#F5
L312A: LD A,C
ADD A,#02
LD C,A
JR C,L3147
IN A,(C)
XOR L
AND #80
JR NZ,L312A
LD R,A
RRC L
EXX
JR C,L3143
OUT (C),L
EXX
SCF
RET
L3143: OUT (C),H
EXX
RET
L3147: XOR A
INC A
RET


motor_off: LD BC,#F708
OUT (C),C
RET
motor_on: LD BC,#F709
OUT (C),C
RET



decod:
PUSH HL
PUSH BC
POP HL
POP BC
ADD HL,BC
PUSH HL
PUSH BC
POP HL
POP BC
;; Ahora BC Contiene la última posición de los datos comprimidos
paso:
LD A,B
CP H
RET C ;; Salir si nos hemos pasado
JR NZ,falta
LD A,C
CP L
RET C ;; Salir si nos hemos pasado
RET Z ;; Salir si hemos llegado
falta:
LD A,(HL)
INC HL
CP #80
JR NC,LRE
;; Si menor que #80, solo hay que copiar los datos
PUSH BC
LD C,A
XOR A
LD B,A
LDIR
POP BC
JR paso
lRE:
CP #FF
JR NZ,lz77
PUSH BC
LD A,(HL)
INC HL
PUSH AF
LD A,(HL)
INC HL
CP #80
JP p,lrelargo
JR lrecorto
lrelargo:
CP A
RL A
CP A
RR A
LD B,A
LD A,(HL)
INC HL
LD C,A
JR go_lre
lrecorto:
LD C,A
XOR A
LD B,A
go_lre:
POP AF
PUSH HL
LD (DE),A
PUSH DE
POP HL
INC DE
DEC BC
LDIR
POP HL
POP BC
JR paso
lz77:
PUSH BC
LD C,(HL)
INC HL
BIT 6,A
JR Z,noextra
LD B,C
LD C,(HL)
INC HL
JR cpbloq
noextra:
LD B,0
cpbloq:
AND #3F
PUSH HL
PUSH AF
PUSH DE
POP HL ;; HL = final de buffer de salida. Restamos BC
LD A,L
SUB C
LD L,A
LD A,H
SBC B
LD H,A
POP AF
LD B,0
LD C,A
LDIR
POP HL
POP BC
JR paso
Y la cinta...
chichen2.cdt
(35.38 KiB) Descargado 76 veces
Caso 2 terminado :)

dragon131
Forum Addict
Forum Addict
Mensajes: 486
Registrado: Mar 08 Jun , 2010 12:36 pm

Re: Juegos comprimidos

Mensajepor dragon131 » Lun 24 Ago , 2015 2:49 pm

Anda que no hay leer :D.

De todas formas veo mas interesante pasar de cinta a disco que de disco a cinta.

¿Te apetecería hacer un tutorial a la inversa?.(si puedo sugerir un juego para el ejemplo que sea el ninja spirit por ejemplo).

CECPC
Me voy lanzando
Me voy lanzando
Mensajes: 77
Registrado: Jue 29 Dic , 2005 4:07 pm

Re: Juegos comprimidos

Mensajepor CECPC » Lun 24 Ago , 2015 3:33 pm

Anda que no hay leer :D.

De todas formas veo mas interesante pasar de cinta a disco que de disco a cinta.

¿Te apetecería hacer un tutorial a la inversa?.(si puedo sugerir un juego para el ejemplo que sea el ninja spirit por ejemplo).
Le podría echar un vistazo a ver que dificultad tiene. Por lo que he visto en el hilo del foro
http://amstrad.es/forum/viewtopic.php?n ... 04&p=38898
se trata de un juego multicarga, que requieren meterse a hacer cargadores de ensamblador.

¿Sabes donde está el CDT?

En todo caso, si encuentro tiempo, sería en otro hilo diferente.

KaosOverride
Keeper of The Forum
Keeper of The Forum
Mensajes: 712
Registrado: Vie 27 Feb , 2009 12:21 pm

Re: Juegos comprimidos

Mensajepor KaosOverride » Lun 24 Ago , 2015 5:53 pm

CECPC magnifico e interesante tutorial. La verdad es que se merece un hilo propio y chinchetearlo 8)

De todas formas creo que la pregunta inicial trataba de conseguir meter el juego en el minimo espacio, sea disquete, cinta o futuros almacenamientos masivos, un uso mas general por asi decirlo.

Eso le mete la dificultas de que el cargador ha de poder maniobrar con el descompresor, pero que combinado al metodo de carga turbo que nos has dejado mas asequible aun, podemos currarnos unos CDT o MP3 para darle de comer a los CPC de una manera mas rapida y eficiente. Enhorabuena!! \:D/
--------
Carpeta publica [url=https://mega.nz/#F!W5IyhbLa!51JpgZqvyx6j__v12Pr9QA]MEGA Amstrad[/url]
Carpeta proyecto [url=https://github.com/KaosOverride]GitHub[/url]

Avatar de Usuario
LexSparrow
Master of The Forum
Master of The Forum
Mensajes: 1009
Registrado: Dom 18 Dic , 2005 3:17 am
Contactar:

Re: Tutorial para pasar de dsk a cdt por CECPC

Mensajepor LexSparrow » Jue 24 Sep , 2015 9:11 am

Hola, ojo a los adjuntos que tras el cambio de servidor han dejado de verse.
END OF LINE

Neli
Lechoncillo
Lechoncillo
Mensajes: 8
Registrado: Lun 29 Ago , 2016 8:19 pm

Re: Juegos comprimidos

Mensajepor Neli » Lun 29 Ago , 2016 8:29 pm

Vamos a modificar algo más el cargador en proceso de antes, para hacer ya un cargador funcional, eso sí, basado en firmware con la función "CAS READ" - &BCA1.

Como el cargador va a ser funcional, debemos cambiar la posición de ejecución. No puede ser #4000, dado que el juego va desde 0x0840 a 0x0840+0x9300 = 0x9C40

Optaremos por 0x9D00.

Otra cosa es que para reducir los "clicks" de la cinta, vamos a intentar encender el motor tan pronto como sea posible.
Existe una función en el firmware para eso, pero tiene un defecto y es que genera una espera si estaba el motor parado.
En su lugar, activaremos manualmente la línea mediante instrucciones de entrada/salida.
En concreto, mediante las instrucciones:

LD BC,#F709
OUT (C),C

La función "CAS READ" enciende el motor y lo apaga al terminar. Dado que la pantalla de carga la voy a cargar en otra posición de memoria para copiarla "de golpe" como hacían los cargadores clásicos de Dinamic, tras la primera carga vuelvo a encender el motor, para que esa copia de bloque no genere una nueva pausa.

Por lo demás, el nuevo código es sencillo.

Código: Seleccionar todo

ORG #9D00

PUSH AF
PUSH BC
PUSH DE
PUSH HL

LD A,1
CALL &BC0E ; SCR SET MODE - Mode 1

LD BC,&0000
CALL &BC38 ; SCR SET BORDER - BORDER 0

XOR A ; Es equivalente a hacer LD A,0, pero un byte menos ;)
LD B,A
LD C,A
CALL &BC32 ; SCR SET INK - INK 0,0

LD A,1
LD B,26
LD C,B
CALL &BC32 ; SCR SET INK - INK 1,26

LD A,2
LD B,13
LD C,B
CALL &BC32 ; SCR SET INK - INK 2,13

LD A,3
LD B,9
LD C,B
CALL &BC32 ; SCR SET INK - INK 3,9

; Nos vamos a asegurar de usar la "pluma" 1

LD A,1
CALL &BB90 ; TXT SET PEN

; En llamadas a firmware, el Locate son dos llamadas.
; Uno para establecer la fila y otro la columna

LD A,12
CALL &BB6F ; TXT SET COLUMN
LD A,11
CALL &BB72 ; TXT SET ROW

; Para imprimir una cadena, usaremos la función para imprimir un caracter
; Usamos una etiqueta de referencia para la posición de esta
LD HL,cadena

JR primer_caracter:

siguiente_caracter:

CALL &BB5A ; TXT OUTPUT. Imprimimos el caracter

primer_caracter:
LD A,(HL)
INC HL
OR A ; Al comparar A con A, en flag Z se activará cuando A sea 0, que usaremos como final de cadena.
JR NZ,siguiente_caracter

LD A,#54
LD HL,#4000
LD DE,#4000
CALL &BCA1 ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Al terminar la carga, nos aseguramos de encender otra vez el motor
; de la cinta, para que no se pare tras cargar el bloque
LD BC,#F709
OUT (C),C

; Ahora copiamos la pantalla de carga, desde la posicion 4000 a la memoria
; de la pantalla.
LD HL,#4000 ; Posicion de origen
LD DE,#C000 ; Posicion de destino
LD BC,#4000 ; Longitud del blqoue
LDIR

LD A,#55
LD HL,#0840
LD DE,#9300
CALL &BCA1 ; Cargamos un bloque 54, en &4000 con longitud &4000
JR NC,bad_loading ; Segun firmware. Carry= carga bien

; Hemos terminado de cargar el juego.

POP HL
POP DE
POP BC
POP AF
JP #0840 ; Iniciamos el juego

bad_loading:
CALL #0 ; Reset

cadena:

DB "CHICHEN ITZA - PARTE I"
DB 0 ; Fin de cadena
Podemos compilarlo y probarlo con "CALL &9D00".
Usando el CDT de antes, debería funcionar.

Pero ahora vamos a usar una función del CDTMASTER para crear directamente el archivo de carga.
Cuando ensamblamos, vemos lo que ha ocupado en destino.

Vamos al debugger del WinAPE. Desde allí, vamos a transferir los datos directamente al CDTMaster.
Tras compilar, pulsa F7 para abrir el debugger.
En "Memory" selecciona Any para trabajar con la memoria, que es donde está el cargador que hemos creado.
En la parte de datos, damos al botón derecho y seleccionamos "Goto".
Nos pregunta "Enter Goto Address" y ponemos #9D00
Seleccionamos el código que hemos creado (recuerda copiar el fin de cadena, un byte 0 también), y pulsamos "Control+C"

Ahora vamos al CDTMaster, a la pestaña de "AMSDOS Files". Luego "File" -> "New File"
Eso agregará a la lista un nuevo archivo llamado "Nuevo".
Le pulsamos.
En FileName, ponemos "CHICHEN1"
Pulsamos "Edit Data"
Nos sale un editor hexadecimal que está totalmente vacío.
En el menú de arriba, escoge "Paste HEX" (NO USES CONTROL+V dado que lo pegaría como ASCII)
Pulsamos "Save"
En "Load Position" ponemos "0x9D00"
Y en "Execution Position" también.
Le das a "Convert to TZX Blocks".
Esta vez todo estandar, a 2000 baudios.

Nos vamos a la pestaña "CDT/TZX Editor"
Luego "File" -> "Open CDT/TZX"
Cogemos el archivo de antes "chi1test.cdt"
Cogemos los bloques de la derecha, los seleccionamos a la vez y pulsamos "Control+C"
Pulsamos en el primer bloque de la izquierda y pulsamos "Control+V"
Debería estar todo en el orden correcto.

"Save CDT/TZX" y esta vez lo guardamos como "chi1test2.cdt"

Esta cinta debería ser funcional. Podemos probarla con el CPCE.
Ya solo nos falta cambiar la llamada "CAS READ" por el código alternativo del karnov.

En el siguiente post

Disculpa pero me he perdido en este punto. No se a que te refieres.

----> " En la parte de datos, damos al botón derecho y seleccionamos "Goto". "
Que parte de datos?

Enhorabuena por el tutorial. Estoy intentando seguirte pero se me está complicando bastante a partir de este punto.
Lo siento. :(
Si pudieras explicarlo de una manera un poco más sencilla (para los que saben muy poquito de Ensamblador...)


¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro


La Comunidad Española
ESP Soft, juegos para tu CPC Foro de Amstrad CPC Todos los juegos para CPC en un CD Web dedicada al Amstrad CPC (utilidades) Información útil para el CPC (talleres) Selección de juegos de Amstrad CPC Mundo CPC Pree Play then any Key CPC Basic