funciones sp_GetCharAddr

Soporte técnico sobre los lanzamientos de MojonTwins y comentarios sobre los güegos. Ofrecemos soporte técnico con Fourspriter, te ayudamos con ZX Basic o Z88DK, te damos pistas some cómo saltarse un bicho y cosas así.

Moderador: na_th_an

luckpro
Mensajes: 21
Registrado: Mar, 11 Mar 2014, 09:11

funciones sp_GetCharAddr

Mensajepor luckpro » Jue, 10 Mar 2016, 12:26

Buenas, hacia tiempo que no entraba por aquí, pero también hacia tiempo que no hago nada para zx. Estoy intentando hacer un jueguecillo y me ha surgido una duda que buscando en google me ha dado 0 resultados jejejeje. A ver si vosotros me podéis dar luz sobre este tema.

Quiero algo muy simple, saber que carácter esta en una dirección de la pantalla concreta, por ejemplo, si yo pongo esto para pintar una frase en la pantalla

print_str (1, 23, COLOR_BLANCO_FOREGROUND, "RIGHT");

quiero una función como esta:

getCaracter(1,23);

y que me devuelva un char con 'R'

simple verdad? pues no tengo cojones de hacerlo.

He visto vuestro pdf tutorial:
http://www.mojontwins.com/warehouse/splib2-tutorial.pdf

y veo que existe la función sp_GetCharAddr pero no me da lo que esperaba o no se usarla bien. Lo mismo esta función no es lo que yo creo que es.

Alguien la ha usado o sabe como funciona?

Gracias de antemano :adore:
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: funciones sp_GetCharAddr

Mensajepor na_th_an » Lun, 14 Mar 2016, 08:16

Buenas.

Esa función te devuelve la dirección de memoria de pantalla donde empieza la celda de caracter que le especifiques en los parámetros. Como la organización de la pantalla del Spectrum no es lineal, obtener la dirección de memoria de una posición de caracter determinada precisa un cálculo que se encapsula en esa función.

Para hacer lo que quieres, supongo que, dado que splib2 mantiene un buffer con los tiles de fondo de cada celda de carácter para hacer sus cosas, lo suyo sería consultar en dicho buffer.

Sin mirar, supongo que dicho estará añadido de forma estática dentro del binario del juego cuando se compila con splib2. Para acceder a él supongo que lo suyo sería tocar el código fuente de splib2 para que exporte ese símbolo, y luego consultar la memoria a partir de ahí.

Mirando un poco el código, veo que hay una rutina ".SPCompDListAddr" que recibe las coordenadas en A y C (fila y columna, respectivamente) y devuelve la posición de memoria donde se almacena la información relativa al tile de fondo que hay en esa posición. Según veo, atributos y nºs de tile están intercalados, por lo que habría que sumar 1 a la dirección devuelta y leer el byte almacenado en esa dirección para obtener el carácter.

Sin embargo, esta rutina se utiliza de forma interna y no extá exportada "al exterior" para se empleada desde tu programa. Exportarla implica escribir un wrapper en C. Al estar todo encapsulado dentro de una biblioteca no sé si esto será posible de hacer desde "el exterior" o habrá que crear el wrapper y recompilar splib2.

Ahora mismo no tengo tiempo para hacer pruebas, pero habría que hacer una función que sacase los parámetros, lo metiese en los registros, y obtuviese el resultado para devolverlo. Como no tengo mucha idea de como hacer estas cosas, me he buscado una función similar (que tome dos unsigned char y devuelva un puntero). No he tenido que buscar mucho: la que tú nombrabas es exactamente así. Viendo su código dentro de splib2 y extrapolando, esto debería funcionar:

Código: Seleccionar todo

void *sp_CompDListAddr(uchar row, uchar col)
{
#asm
   LIB SPCompDListAddr

   ld hl,2
   add hl,sp
   ld c,(hl)
   inc hl
   inc hl
   ld a,(hl)
   call SPCompDListAddr
#endasm
}


Pero no sé si se puede usar desde "el exterior" o habrá que meterla en splib2. Habría que hacer pruebas.

Usar esto para obtener el nº del tile en una posición podría hacerse así:

Código: Seleccionar todo

tile = *(sp_CompDListAddr (x, y) + 1);
Como diría Rorshach: "Urm..."
luckpro
Mensajes: 21
Registrado: Mar, 11 Mar 2014, 09:11

Re: funciones sp_GetCharAddr

Mensajepor luckpro » Lun, 14 Mar 2016, 09:02

Estupenda respuesta, luego pruebo a ver si funciona desde fuera, porque meterla dentro tiene pinta de ser un lío considerable.

Aunque ya me ha picado la curiosidad, cuando me comentas que la función esa devuelve la dirección a la "celda de caracter", por simple curiosidad, ¿que hay en esa dirección de memoria? ¿que información contienen esos bytes? Te lo pregunto porque yo me imaginaba (iluso de mi jejeje) que spectrum guardaba 2 bytes por cada caracter de la pantalla, 1 byte para decir que letra/simbolo debe imprimirse y otra para el color/atributo, pero veo que no es así de simple no?

Muchas gracias, me va a ser de gran ayuda.
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: funciones sp_GetCharAddr

Mensajepor na_th_an » Mar, 15 Mar 2016, 08:03

El Spectrum no tiene memoria de tile, sino usa un framebuffer que describe los pixeles y sus colores (en dos "archivos" diferentes). Es lo que se llama el "Display File". Splib simula esa "memoria de tile" por software, imitando cómo funcionan los procesadores gráficos de muchas consolas y ordenadores como la NES, la Master System o el MSX.

La función sp_GetCharAddr te calcula la dirección en RAM donde empieza la celda de caracter que le pases en los parámetros. Leyendo de esa dirección obtendrías el byte que representan los 8 pixels (en monocromo) de la primera fila de la celda de 8x8. Para obtener la siguiente, habría que sumar 256 a esa dirección, y así hasta tener las 8. Esto es debido a como se organiza el "Display File". Aparte, habría que leer el byte que describe los colores que toman esos pixeles, pero ese cálculo es más trivial: 22528 + x + y * 32.

Splib emplea una capa de abstracción y mantiene un buffer aparte en el que almacena qué caracter de los 256 que maneja hay en cada celda. Luego, cuando un trozo de la pantalla cambia (porque pongas nuevos tiles o porque un sprite se mueva por ahí), "construye" la imagen que ves sobre el Display File obteniendo información de su buffer, de los gráficos de los tiles, y de los gráficos de los sprites.

Lo que intentamos hacer es leer directamente de este buffer. La otra opción (que es la que, por ejemplo, tomó el diseñador de la ROM de BASIC del Spectrum) sería leer 8 bytes del display file y buscar a qué caracter corresponden de la tabla de caracteres, lo cual sería infinitamente más lento (¡por eso la función SCREEN$ de BASIC es tan lenta!).

Sobre hacer funcionar esto, en realidad incluirla en splib2 no sería tan complicado. El problema es que los fuentes de splib2 no se llevan bien con las últimas versiones de z88dk y hay que sudar un poquito. Creo que la versión de splib2 que usamos todos la compilé con la versión 1.7 o 1.8 de z88dk, ¡sí que ha llovido! A unas malas, sólo tengo que buscar en los cajones y sacar esa versión vieja. Si no lo puedes hacer funcionar, busco un hueco.
Como diría Rorshach: "Urm..."
luckpro
Mensajes: 21
Registrado: Mar, 11 Mar 2014, 09:11

Re: funciones sp_GetCharAddr

Mensajepor luckpro » Mar, 15 Mar 2016, 08:51

Joder, explicado a la perfección, no tenia ni idea de que funcionase así.

Vamos todo esto viene por intentar ahorrarme tener dos arrays de 32x24 con todos los caracteres y colores, pensé que si ya estaba en la RAM en algún sitio pues me lo ahorraba. En cualquier caso podría usar el array igualmente para los caracteres y sacar los colores con esa formula que me has puesto (así solo gastaría 768 bytes).

Muchas gracias por ofrecerte para modificar la lib, pero no te quiero hacer perder tiempo en esto. Ya me las apañare con lo que pueda.

Infinitas gracias. Me ha quedado todo mucho más claro. A ver si consigo hacer algo chulo...
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: funciones sp_GetCharAddr

Mensajepor na_th_an » Mar, 15 Mar 2016, 15:57

No necesitas ese buffer de caracteres, puedes leer directamente del displayFile si lo que quieres es ver los colores.

Código: Seleccionar todo

atributo = *((unsigned char *) (22528 + x + (y << 5)));


Ahora no puedo probar, quizá el casting a "unsigned char *" no sea necesario... Tengo ya un lío de compiladores bestiales, en cada uno parece ser diferente :P
Como diría Rorshach: "Urm..."
luckpro
Mensajes: 21
Registrado: Mar, 11 Mar 2014, 09:11

Re: funciones sp_GetCharAddr

Mensajepor luckpro » Lun, 28 Mar 2016, 14:32

Genial, ahora tengo más dudas, parece que te estoy haciendo un examen o algo así, si quieres puedes mandarme a la mierda, no problem, lo entenderé jejejeje.

La duda es ¿como se puede sacar el tiempo del sistema? en C se suele sacar añadiendo el time.h y llamando al clock() que te devuelve los milisegundos, pero lo he intentado hacer aquí y creo que clock() me da siempre el mismo valor, o eso creo. Tengo este trozo de código y se queda colgado ahí dentro.


Código: Seleccionar todo

deltaNew=clock(); //pillamos el tiempo actual
while(deltaNew<(deltaOld+30))
{
   deltaNew=clock();  //no ha pasado los 30 milisecs así que volvemos a actualizar el tiempo actual
}
deltaOld=deltaNew; //ya han pasado los 30 milisecs seguimos haciendo cositas (nunca llega aquí)



Y mi segunda duda y no por ello menos importante, ¿hay alguna forma de debuguear las variables? me encantaría saber que tienen deltaNew y deltaOld, alguna vez he tirado del print_number2 pero no sé si hay alguna forma más simple de hacer algún log o algo. ¿Pido mucho? Gracias.
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: funciones sp_GetCharAddr

Mensajepor na_th_an » Mar, 29 Mar 2016, 08:47

El Spectrum no tiene reloj del sistema.

La ISR que hay activa en el intérprete de BASIC lleva una cuenta de las veces que se ejecuta, lo que resulta en una cuenta efectiva de los frames que lleva el Spectrum encendido (50 frames por segundo). El problema es que esa ISR está desactivada cuando se está ejecutando el juego.

No sé si estás haciendo algo por tu cuenta o estás usando MK1 o MK2. En el caso de nuestros motores, si estás en modo 128K tienes una ISR disponible que es la que llama al player que hace sonar el AY. Puedes poner ahí un incremento de una variable global y usarla como temporizador, se incrementará 50 veces por segundo. Si estás en modo 48K, no hay ISR y tendrás que añadirlo. El problema es que el mapa de memora del modo 48K de MK1 y en especial de MK2 está bastante ajustado y el espacio reservado en el modo 128K para el vector de saltos necesario está ocupado con otras cosas.

Si estás por tu cuenta, creo que si no haces nada el programa se ejecuta en modo IM1 y con las interrupciones habilitadas, con lo que estaría funcionando el ISR del BASIC y tendrías esa cuenta de frames disponible. Es un valor de 24 bits que puedes consultar leyendo de la variable del sistema BASIC llamada "FRAMES". Mira aquí: http://www.worldofspectrum.org/ZXBasicM ... hap25.html y aquí: http://www.worldofspectrum.org/ZXBasicM ... hap18.html

Personalmente, y aunque no es una medida de tiempo demasiado robusta, yo siempre cuento cuadros de juego. El timer de MK1 y MK2 funciona así, por ejemplo. Obviamente, cuanto más faps tenga tu juego, más rápido contarás el "tiempo", pero uno se apaña.

Para ver el valor de las variables tienes que usar un emulador con debugger (como Spectaculator) y encontrar donde están en la memoria, no hay otra. Supongo que z88dk, como otros compiladores, tiene la opción de sacarte un mapa de donde coloca las cosas. Ojete, que si las variables son locales o parámetros estarán en la pila y tendrás que mirar ahí. Otra forma es tener una dirección RAM de memoria libre siempre y usarla para "ver" las variables simplemente escribiendo su valor ahí y mirando con el debugger o con un visor de memoria qué valor va tomando.

Código: Seleccionar todo

*((unsigned char *) (22528)) = valor; // por ejemplo
Como diría Rorshach: "Urm..."
luckpro
Mensajes: 21
Registrado: Mar, 11 Mar 2014, 09:11

Re: funciones sp_GetCharAddr

Mensajepor luckpro » Mar, 29 Mar 2016, 11:01

DOH!, no tiene reloj, pues entonces normal que se quedase pillado ahí jejejeje :-) . En este caso concreto no me valdría contar los frames, porque precisamente lo que quiero controlar es el tiempo entre frame y frame para que no se ponga el juego demasiado rápido.

Dependiendo del input del jugador y de varios factores más, el código hace más o menos cosas y claro, en el mejor de los casos, el juego puede embalarse un poco, de ahí que quisiera hacer un pequeño delay en esos momentos para que la velocidad fuese estable todo el tiempo y no marear al jugador con diferentes velocidades.

Creo que lo mejor será que el código ejecute siempre las mismas cosas (más o menos) para asegurarme que va a la misma velocidad.

Estoy usando la churrera, pero como no me hace falta el control de niveles, ni los 4 sprites, ni los scripts, esos trozos de código los he comentado y he agregado mis cosillas. Así aprendo cositas nuevas. La idea es hacer un shoot em up alineado a carácter, no creo que me salga muy bien, pero bueno lo voy a intentar por lo menos :-)

Ya te iré contando como va. Te tendré que poner en los créditos porque la ayuda que me estas dando no tiene precio jejeje.
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: funciones sp_GetCharAddr

Mensajepor na_th_an » Mar, 29 Mar 2016, 11:12

Si quieres meter delays en los cuadros de juego más cortos, lo suyo es que montes un ISR que cuente interrupciones, así te garantizas 50 incrementos por segundo y puedes usarlo para temporizar. Al principio de cada cuadro de juego pones el contador a cero, y al final de tu bucle de juego haces un while que espere hasta que tome el número fijo que decidas.

Montar un ISR no es difícil, si sigues los pasos que salen en la documentación de splib2 lo sacas seguro. Si no, puedes mirar como está hecho en la versión 128K de MK1 o MK2.
Como diría Rorshach: "Urm..."

Volver a “Ayuda”

¿Quién está conectado?

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