Sinónimos de números, consumo de memoria, calculador

Todo sobre la creación, diseño y programación de nuevo software para
nuestro Spectrum

Moderador: Sir Cilve Sinclair

Responder
zup
Freddy Hardest
Mensajes: 666
Registrado: Vie Ago 15, 2008 2:43 pm

Sinónimos de números, consumo de memoria, calculador

Mensaje por zup » Mié Nov 27, 2013 10:31 am

De vez en cuando tengo que hacer cargadores para juegos con el CLEAR muy bajo, y cada byte de memoria cuenta. Como sabéis, en el BASIC del ZX Spectrum el número se almacena de dos maneras: como cadena primero y como número flotante (5 bytes) después (que es el que finalmente utiliza el intérprete). Entre ambos números se usa el byte 0x0E como separador. Esto lleva a varias consecuencias:

- El número 0 ocupa en realidad 7 bytes (1 del 0, 1 del separador y 5 de su representación en coma flotante).
- El número 32767 ocupa en realidad 11 bytes (5 de la cadena, 1 del separador y otros 5 de la coma flotante).
- Si hay una discrepancia de números, se utiliza el de la coma flotante. Es decir, si ves un USR 32767 y en la representación de coma flotante pone 30000, el programa saltará a la dirección 30000. Esto lo he visto en algún cargador para despistar a hackers.

Si se usan expresiones en lugar de números, el número nos va a ocupar en memoria el mismo espacio que la expresión (salvo que la expresión contenga números). Así que esta es una tabla de sustituciones que pueden servir para ahorrar memoria y espacio:

Código: Seleccionar todo

   -1 = COS PI    --> 2 bytes, se ahorran 5
    0 = NOT PI    --> 2 bytes, se ahorran 5
    1 = SGN PI    --> 2 bytes, se ahorran 5
    3 = INT PI    --> 2 bytes, se ahorran 5

    x = VAL "x"   --> siempre se ahorran 3 bytes
40000 = VAL "4e4" --> 6 bytes, se ahorran 5


En general, usar VAL ahorra tres bytes, salvo en el último caso en que utilizamos notación científica para "acortar" el número y nos ahorramos algún byte más.

Todo esto no es gratis y cada expresión debe ser evaluada (excepción debajo), por lo que lo que ahorramos en memoria lo pagamos en tiempo de proceso. En un cargador tenemos montones de tiempo, pero en el bucle principal de un juego en BASIC no. Hay que estudiar hasta que punto nos beneficia o nos perjudica usar esto.

Otro truco es que si vas a utilizar muchas veces un número, lo asignes a una variable de una sola letra. A partir de cierto límite, empiezas a ahorrar memoria. Además, si pones algo del estilo LET Z=NOT PI, la expresión sólo será evaluada una vez (cuando metes el valor a la variable) por lo que no penaliza demasiado la velocidad.

Ahora las preguntas...
- En esta tabla hay un montón de "huecos". Estaría bien disponer de los valores del 0 al 9 codificados en uno o dos bytes. Me interesarían sobre todo los valores del 0 al 7.
- El calculador. Esta parte que convierte las expresiones de BASIC a valores, a veces tiene un comportamiento no demasiado intuitivo. El calculador usa la pila, y si detecta que la pila va a colisionar con las variables, nos suelta un "Out of Memory". Esto hace que cuando vas muy pillado de memoria, a veces sea mejor dejar un USR 32767 funcione y un USR VAL "32767" no lo haga (aunque en teoría el último ocupe menos memoria). ¿Existe alguna regla para preveer el uso de memoria del calculador?

EDITO: Dos pifias (es lo que tiene escribir de memoria). Por una parte, me había olvidado por completo del byte que se usa como separador.

Por otra parte, usar BIN en vez de 0 no ahorra ningún byte (he corregido la tabla para arreglarlo). La clave es que BIN se trata como si fuera BIN 0, y se codifica como un BIN con un "cero numérico" detrás... lo que no ahorra bytes. La representación de estos números sería así:

Código: Seleccionar todo

0       - 0x30 0x0E 0x00 0x00 0x00 0x00 0x00 - 7 bytes
BIN     - 0xC4 0x0E 0x00 0x00 0x00 0x00 0x00 - 7 bytes
VAL "0" - 0xB0 0x22 0x30 0x22                - 4 bytes
NOT PI  - 0xC3 0xA7                          - 2 bytes


Y nos quedamos con que la codificación más eficiente del cero parece ser NOT PI (la tradicional, vamos).
Última edición por zup el Mié Nov 27, 2013 4:43 pm, editado 5 veces en total.
I have traveled across the universe and through the years to find Her. Sometimes going all the way is just a start...

Avatar de Usuario
Zeit
Herbert
Mensajes: 87
Registrado: Jue Oct 22, 2009 1:49 am
Ubicación: Montevideo, Uruguay
Contactar:

Re: Sinónimos de números, consumo de memoria, calculador

Mensaje por Zeit » Mié Nov 27, 2013 12:53 pm

Recuerdo usar algunos de esos "trucos" cuando programaba en el BASIC de la ZX81 no expandida, con 1K de RAM. Usaba también SGN y NOT en conjunto con PI. Una buena pregunta la que planteas, que alguna vez me he hecho...
8 bits forever!

Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Re: Sinónimos de números, consumo de memoria, calculador

Mensaje por mcleod_ideafix » Mié Dic 04, 2013 1:34 am

¿Y... no daría menos dolores de cabeza escribir el cargador en código máquina y alojarlo donde te dé la gana?
Web: ZX Projects | Twitter: @zxprojects

zup
Freddy Hardest
Mensajes: 666
Registrado: Vie Ago 15, 2008 2:43 pm

Re: Sinónimos de números, consumo de memoria, calculador

Mensaje por zup » Mié Dic 04, 2013 8:26 am

mcleod_ideafix escribió:¿Y... no daría menos dolores de cabeza escribir el cargador en código máquina y alojarlo donde te dé la gana?


Pueees... es una opción que no he puesto en marcha por algunas razones.

Mi objetivo es ir creando cargadores para que algunos juegos carguen desde disco duro/tarjeta flash en un +3e (como consecuencia indirecta, también pueden cargar de disquete en un +3 y funcionar en +2A/+3 en modo 128k). En la mayoría de juegos cargo un montón de bloques en BASIC (pantalla, código principal, páginas de RAM) con algún "apaño" para cargar la página 7 o la zona entre 23296 y 24575. Esto permite ver de un vistazo qué se carga y dónde se carga y POKEar fácilmente el juego.

De momento, al cargar todo desde BASIC, los juegos se pueden readaptar de disco a cinta con muy pocos cambios (quitar todos los nombres de fichero y un USR especialito), salvo los juegos que van en ROM (todos los cartuchos de IF2 y el "Shadow of the Unicorn) que necesitan los modos de paginación especiales del +2A y +3. Solamente he usado c/m hasta ahora en un juego: "Viaje al centro de la tierra", en forma de un montón de parches aquí y allí para que cargue las diferentes fases desde disco.

Si uso un cargador en c/m tengo que tomar unas cuantas decisiones:
- ¿Retorno al BASIC y permito POKEar o lo dejo estar y lanzo el juego después de cargar? Si retorno al BASIC, tendré que usar otro USR para cargar los restos de la página 7; tanto el c/m como el BASIC serán más largos.
- ¿Un solo fichero o varios? Usar un solo fichero hace el cargador más compacto; usar varios permite ver mejor lo que se carga.
- ¿Comprimo los datos o no los comprimo? Comprimirlos me va a llevar unos 20 bytes más de cargador en el mejor de los casos; unos 100 en el peor.

Al final, calculo que un cargador en c/m para +3 se podría llevar entre 100 y 250 bytes (sin optimizar). No lo he implementado porque no sé si es suficiente ganancia en memoria como para justificar toda la complejidad extra.

(En otro orden de cosas, por las mismas razones no he hecho adaptaciones para que los juegos carguen/salven a disco... salvo los que lo hacen en BASIC ninguno de los que he publicado lo hacen).
I have traveled across the universe and through the years to find Her. Sometimes going all the way is just a start...

zup
Freddy Hardest
Mensajes: 666
Registrado: Vie Ago 15, 2008 2:43 pm

Re: Sinónimos de números, consumo de memoria, calculador

Mensaje por zup » Jue Dic 05, 2013 8:50 am

Después de pasarme un buen rato peleándome con ZXSpin (yo no soy tan bueno con estas cosas), he puesto un cargador en c/m en este otro hilo.

Investigando un poco cuánto se gana:
- El cargador original del Gokumal (modificado para cargar desde disco, incluyendo variables) ocupa hasta la direccón 23982.
- El cargador en c/m del Gokumal ocupa hasta la dirección 23976 (aunque pueden arañarse varios bytes de aquí y allí).
- Mi cargador estándar BASIC ocupa hasta la dirección 24120.

El cargador estándar que uso lo he pillado del "Renegade" y carga una página extra, ya que no puedo hacer una versión de Gokumal que cargue con este cargador. De todas formas, cargar una página extra en el cargador de c/m son 5 bytes de más (lo que me ocupa en la tabla).

En el caso de carga desde cinta, las cosas deberían ser más espectaculares, pero...
I have traveled across the universe and through the years to find Her. Sometimes going all the way is just a start...

Responder

¿Quién está conectado?

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