CAAD

Comunidad de Aventuras Conversacionales y Relatos Interactivos
Fecha actual 12 Dic 2017 20:58

Todos los horarios son UTC + 1 hora




Nuevo tema Responder al tema  [ 7 mensajes ] 
Autor Mensaje
NotaPublicado: 19 Jul 2013 18:47 
Desconectado
Momio
Momio
Avatar de Usuario

Registrado: 09 Mar 2004 16:14
Mensajes: 4614
Hola,

Aviso que esto es una paja mental, de caracter grave y por supuesto muy friki en variante retro. Encima es un tocho-mensaje, la mitad del mismo pensado según lo escribía...

Resulta que andaba yo mirando unpaws por cosas de la vida, y me encontré con que PAW solo tiene 107 condactos, pero usa 1 byte para codificar el opcode.

Eso significa que una aventura de PAW está desperdiciando nada menos que 149 posibilidades de distintos condactos, o mirándolo de otro modo, que desperdicia 21, y nos sobra un bit entero para otra cosa.

¿Para que otra cosa? ... Indirección. Esto es algo que ya tenía el DAAD, y por supuesto tiene Superglús, pero meterselo al PAW es un reto.

Digamos que si el opcode de LET es el 12 (es un ejemplo)

[12 100 5] ==> LET 100 5
[130 100 5] ==> LET @100 5 (140 = 12+128, es decir, el mismo opcode pero con el bit que sobra a 1)

Pero esto solo nos da para hacer indirección en el primer parámetro, así que quizá sea mejor algo así:

[130 1 100 5] ==> LET @100 5
[130 2 100 5] ==> LET 100 @5
[130 3 100 5] ==> LET @100 @5

Sí, gastamos un byte entero para decir que parámetros de los que siguen llevan indirección. Es un desperdicio ciertamente, porque sobran 6 bits, pero aunque la indirección es muy útil donde se usa, tampoco aparece continuamente, así que puede ser aceptable.

Bueno, sea uno u otro modelo, el caso es que como poco podría llevar indirección en un parámetro (ya veríamos si es más util en general el primero o el segundo, o si hay alguna manera de hacerlo variable segun condacto).

Y que hay que hacer para dar soporte a esto:

1) Debug paso a paso de PAW hasta encontrar donde se ejecuta el interprete que lee los opcodes. Supongo que en PAW habrá algún sitio que haga esto:

- Leer el opcode del condacto
- Mirar cuantos parámetros tiene (de una tabla lookup, supongo) , leerlos y meterlos en la pila.
- Mirar donde está la rutina que ejecuta ese condacto (en otra tabla lookup, supongo), y hacer un CALL.
- Al retornar del CALL, incrementar el puntero que apunte al siguiente condacto en el número de parámetros del condacto, para apuntar al siguiente opcode.

Y habría que cambiarlo a esto:

- Leer el opcode del condacto, y detectar si tiene indirección, quedándonos con el opcode real.
- Si no había indirección mirar cuantos parámetros tiene , leerlos y meterlos en la pila.
- Si había indirección, mirar cuantos parámetros tiene y los que tengan indirección, leer el valor del código, hacer la indirección, y meter los valores resultantes en la pila.
- Mirar donde está la rutina que ejecuta ese condacto y hacer un CALL.
- Al retornar del CALL, incrementar el puntero que apunte al siguiente condacto (si hay indirección y al final se mete el byte de más, numero de parámetros +1).

Para hacer la rutina que implementa la indirección hace falta hueco, y dudo que quepa en el mismo sitio que esté la actual sin indirección, así que probablemente tras leer el opcode habría que hacer un JMP a algun sitio donde hayamos implementado esta lógica y que a su vez, tras hacer el cambio de valores, vuelva a saltar a donde llegaríamos normalmente tras leer los parámetros.

¡Pero si no hay sitio para meter esa rutina!

Pues no, probablemente esa rutina no cabe, o quizás sí, pero da igual. ¿Por qué?

Por que con indirección nos sobran condactos.. Ver equivalencias:

Código:
copyfo   place ojno @flagno
prompt   let 42 value
score      print 30
add      plus flagno2 @flagno1
clear      let flagno 0
copyff   let flagno2 @flagno1
set      let flagno 255
sub      minus flagno2 @flagno1
create   place objno 255
destroy   place objno 252
ability      let 37 v1;let 52 v2
carried   isat objno 254
notcarr   isnotat objno 254
notworn   isnotat objno 253
worn      isat objno 253
notzero   noteq flagno 0
same      eq flagno1 @flagno2
notsame   noteq flagno1 @flagno2
zero      eq flagno 0


No estoy 100% seguro de todos, especialmente de los de objetos, ya se afinaría.

Estoy seguro de que si podemos quitar el código que ejecuta todos esos condactos, queda hueco para meter la indirección, aunque haya que desplazar algunos otros para que el hueco esté todo junto. En cualquier caso, si se encuentra hueco sin quitarlos, pues no hace falta quitarlos.

En definitiva, deberíamos conseguir una versión hackeada del interprete PAW que contenga estos parches.

Ya, ¿pero como hacemos que el editor de PAW soporte esa sintaxis y genere aventuras para ese intérprete?

No lo hacemos, usamos inPAWS. Creo que sería fácil meter el soporte de esa sintaxis en inPAWs, y hacer que genere una base de datos PAW que el interprete hackeado pueda ejecutar. Incluso puede que con inPAWs pudieramos darle soporte a esos condactos que hemos quitado, mediante el preprocesador (vamos, que el preprocesador nos cambia automaticamente cada ability X Y por let 37 X;let 52 Y).

Lo que sería importante es que este inPAWs modificado no permitiera meter los opcodes de los condactos borrados.

¿Y qué más podemos hacer?

Pues no se, si al final se quitan los condactos, quizá quede hueco para otros condactos.
Superglús tiene un montón de condactos más que PAW, algunos de los cuales son razonables para una maquina de 8bits (los de reproducir ficheros OGG no). Se podría mirar si cabe implementar alguno intersante (y darle soporte en inPAWs).

Incluso... paja mental #2... si conseguimos que todo el "hueco" que dejan los condactos esté junto, sería posible cargar condactos plugin con inPAWs. Algo como tener un ficherito con el binario de un condacto, y decir a que opcode lo aplicas. Por ejemplo, podríamos poner en inPAWs un

Código:
loadPlugin mul.plu 97


Lo cual haría que inPAWs, que debe de alguna manera poder saber donde tiene un .TAP del interprete hackeado, pudiera colocar el binario mul.plu en el primer hueco de la zona vaciada, y en la "tabla lookup" de condactos de PAW apuntar a el 97 a esa direccion. Para ello el mul.plu debería contener el nombre del condacto (para la sintaxis de inPaws), el numero de parámetros (para rellenar también la tabla lookup de parámetros y la propia sintaxis de inPaws) y el codigo binario propiamente dicho (Z80).

Si ponemos dos plugin:
Código:
loadPlugin mul.plu 97
loadPlugin div.plu 98


Pues inPAWS coloca el binario de uno detrás del otro y actualiza las tablas lookup. Como inPAWs sabe cuanto hueco hay, podrá dar una alarma si usas demasiados condactos plugin. Como el autor elige qué plugins usa, puede meter los que le hagan falta para esa aventura en concreto, y en otra aventura meter otros. Incluso hace más fácil el asunto de los EXTERN, si quieres hacer algo concreto en lugar de un EXTERN te haces un condacto plugin. Eso podría liberar el hueco del propio condacto EXTERN de hecho.

¿Se puede hacer para Amstrad CPC o PAW PC?

Pues supongo que sí, habría que hackear su interprete y mirar el output que hace inPAWs para CPC.

¿Y para Superglús?

Claro, pero es una tontería, Superglús ya soporta todo eso y más. Ahora bien, se podría generar una aventura con inPAWs que generara output de Spectrum, CPC, PAW-PC y Superglús.

¿Posibles problemas?

Pues todo esto es una conjetura, así que lo mismo PAW no funciona así, no hay las tablas lookup que creo que debe haber, y no hay manera humana de hacer esto (lo de los plugins, lo de la indirección creo que sí).

¿Y para qué vale?

Probablemente para nada. Es difícil que nadie use este hack para hacer una aventura, de hecho creo que salvo Mastodon nadie ha usado inPAWs para hacer una aventura (y eso que está genial). ¿Pero a que mola?

En fin, yo solo no creo que lo vaya a hacer, mas que nada porque no me da tiempo o quizá no le doy la prioridad que tendria que tener, pero no se, lo mismo si alguien se une y vamos viendo posibilidad, me animo.

¿Dije que era una paja mental o no lo dije? :lol: :lol: :lol:

_________________
Sígueme en twitter: @uto_dev
http://www.ngpaws.com


Arriba
 Perfil  
 
NotaPublicado: 19 Jul 2013 23:58 
Desconectado
Momio
Momio
Avatar de Usuario

Registrado: 09 Mar 2004 16:14
Mensajes: 4614
IMPORTANTE: En el post anterior, donde pone 130 en los ejemplos del principio debe poner en todos 140.

Perdonad que use esto como un blog, pero es que me he picado...

Solo para muy retros que sepan ensamblador del Z80...

Estoy debuggeando esto:

https://copy.com/8CzIPy3SaAKoUUcd

Dentro hay dos ficheros de cinta de Spectrum, uno con el PAW de Aventuras AD, que he usado para crear una aventura muy simple, y otro con la aventura como tal (grabada con interprete y todo).

La aventura tiene esto al principio del proceso 1:

Código:
* _
AT 0
ANYKEY
INK 6
LET 100 9
GOTO 1
DESC


El objetivo de crear esa entrada es poder comparar lo que veo que ocurre con lo que he programado en PAW, y he incluido condactos con pausa, con uno, dos y ningún parámetro, para ver como va.

En este PAW (ojo que con otra versión puede ser diferente), hay un bucle donde se lee el opcode que toca ejecutar en la dirección 30374. En ella se lee el opcode y si no es 255 se sigue. Si es 255 significa que es fin de "entrada".

Utilizo los spoilers para comentar cosas que me resultan extrañas, aunque como obviamente no puedo leer la mente de Tim Gilberts ahora mismo, ni por supuesto lo que pensaba entonces, lo mismo mis juicios son los del tipico listillo al que deberían haberle dicho entonces "haberlo hecho tú" :lol:

Spoiler: Mostrar
La verdad es que PAW se podría haber ahorrado bastante espacio simplemente usando ese bit que nos sobra para marcar fin de entrada en lugar de un byte 255 completo, pero bueno, al fin y al cabo no hay tantísimas entradas en una aventura PAW, aunque si las suficientes para haberlo pensado.


Lo siguiente que hace es guardar ese opcode en la dirección 34221, por razones que ignoro (no he visto cuando lo usa, quizá algún condacto lo haga, o haya condactos que compartan código y sea dependiente de ese valor).


Después lee el siguiente byte (el que va detrás del opcode). Esto es curioso porque lo lee independientemente del número de parámetros que tenga el condacto (incluso si no tiene ninguno), aunque no aumenta el puntero que lleva la cuenta de por donde vamos en la ejecución (que por cierto es el registro BC). Esto da al traste con mi esperanza de que hubiera una tabla lookup donde pudiéramos ver cuantos parámetros tiene un condacto. No es así, sino que PAW siempre lee un byte mas por si acaso y luego llama a la rutina de cada condacto, donde si hace falta otro parametro más incrementa BC otra vez y lee de nuevo. Esto visto así, es un gran palo a las posibilidades de poner indirección al segundo parámetro, porque cada segundo parámetro se trata en cada condacto aparte, por lo que no podemos "hacer trampas" y pasarle a la rutina del condacto el contenido del flag x en lugar de x, al menos para el segundo condacto, para el primero sí.

Spoiler: Mostrar
Yo no lo habría hecho así, tener una tabla con el número de condactos solo habría ocupado 107 bytes, incluso menos porque como los condactos solo pueden tener 0, 1 o 2 parametros en realidad nos bastaba con 2 bits para cada uno, 31 bytes. El código para coger cuantos parametros tiene y cargarlos podría haber ocupado apenas 10-15 bytes mas, así que en total eran 40-46 bytes. A cambio de eso tenemos 24 condactos de dos parámetros en PAW, y cada [INC BC ; LOAD A,(BC)], que es lo que hace que carguemos un segundo parámetro en cada condacto que usa mas de uno, ocupa dos bytes, por lo que en total son 48 bytes usados en todo PAW.

Lo cierto es que entra dentro de lo posible que algunos condactos compartan código, y que no sean 48 sino menos, habría que mirar las funciones de cada uno de los condactos de 2 parametros para saberlo. En cualquier caso, en un entorno tan ajustado, hacerlo estándar para todo habría sido mejor, y ahora podríamos hacer indirección del segundo parámetro.


Bueno, después de coger ese primer parámetro, pero sin mover BC (con lo cual sigue apuntando a ese parámetro) hace dos cosas que aun no entiendo. Mete en HL el valor 34240 + el valor del parámetro, y en DE 34496 + el valor del parámetro. Aún no he descubierto para que es esto, pero cuando llama a la funcion que maneja el condacto, en A va el valor del primer parámetro (o el siguiente opcode si tiene 0 parametros) y en HL y DE van esos valores extraños.

Antes de hacer esa cosas rara carga de una tabla lookup de valores de 16 bits que hay en 32302 el valor que corresponde al opcode. Esta tabla lookup es la que intuía que existiría, a partir de ahí, de dos en dos bytes, están las direcciones de inicio de las rutinas de los 107 condactos.

Para llamar a cada condacto lo hace raro, pero lo hace por lo tipico de ahorrar bytes. Hace un PUSH HL, y luego un RET (lo cual provoca un salto a la direccion de la tabla lookup).

Por último decir que también son las propias rutinas de cada condacto las que, en caso de tener un parámetro ,se encargan de hacer un INC BC (para que apunten al siguiente opcode), o dos INC BC si son dos parámetros. Cuando no hay parámetros al terminar hacen un JP a 30374 (donde se leía el opcode siguiente), las que tienen un parámetro saltan a otra direccion donde se hace el INC BC y un par de cosas mas que no se para que son, y luego saltan a 30374, y las que tienen 2 hacen el INC BC a pelo, y luego llaman a la zona que hace el otro INC BC.

Spoiler: Mostrar
Es decir, un caos :roll:


Bueno, estas investigaciones casi cierran la puerta a la indirección en dos parámetros, pero hacer la de un parámetro resulta fácil, siempre que sea el primero. Pero mira por donde si os fijáis en mi tabla de equivalencias de antes de condactos con otros condactos usando indirección, los que usan indireccion para sustituir a otro, lo hacen en el segundo parámetro, así que no podemos quitar esos.

Podríamos quitar los que de por sí son redundantes, pero en realidad si quito "ZERO flagno" y lo sustituyo por "EQ flagno 0" estoy salvando espacio para hacer la indirección a costa de que las aventuras ocuparan más (cada ZERO que ocupa 2 bytes pasaría a ocupar 3). La única opción que veo es buscar un hueco libre (¿quiza zona del basic, buffer de impresora, o donde leches se metieran los EXTERN que no me acuerdo?) o intentar quitar condactos de poco uso, como ability o prompt.

Veamos:

Código:
Ability:[11 bytes]

LD (34277),A  ; --> Mete en esa direccion el parametro 1 de ABILITY
INC BC
LD A, (BC) ; --> Carga el segundo parámetro
LD (34292),A  ; --> Mete en esa direccion el parametro 2 de ABILITY
JP 31357  ; --> La parte que incrementa BC otra vez, que son dos parámetros, para que apunte al siguiente opcode



Código:
Prompt [6 bytes]:

LD (34282),A  ; --> Mete en esa direccion el parametro 1 de PROMPT
JP 35357


Pues bueno, pues en 11 bytes de ability lo mismo cabe el truco, pero quizá convendría mirar si hay otro hueco, antes de cargarnos un condacto.

Spoiler: Mostrar
Vaya, mirando otra cosa acabo de descubrir para que se calcula el valor ese 34240+P1, es el flag P1. Así en cualquier función, si necesita acceder al valor del flag cuyo valor va en el primer parámetro del condacto, basta con acceder a (HL). Imagino que lo que pasa en 34496+P1 es algo parecido, quizá la palabra de vocabulario, el objeto... supongo que bastará con encontrar un condacto que lo use para saber qué es. Es curioso pero probablemente es efectivo en términos de ahorro de espacio (no en términos de velocidad de ejecución, claro, porque hace muchas cosas para todos los condactos que luego no hacen falta para la mayoría).


Bueno, en cualquier caso, y antes de seguir si me animo, voy a valorar la utilidad de la indirección en el primer parámetro, que sin duda será menor que la indirección en los dos.

Fin del segundo (y probablemente último) capítulo.

¿Habría dado esto para un artículo de SPAC?
Probablemente sí, aunque con final triste o medio triste.

Spoiler: Mostrar
Obviamente todo esto tiene una solución: reprogramar completamente el interprete de PAW. Ahora si que se me va la olla :lol: :lol:

_________________
Sígueme en twitter: @uto_dev
http://www.ngpaws.com


Arriba
 Perfil  
 
NotaPublicado: 20 Jul 2013 10:15 
Desconectado
Momio
Momio
Avatar de Usuario

Registrado: 09 Mar 2004 16:14
Mensajes: 4614
Esto ya son conjeturas, porque PAW no es así, pero podría haberlo sido:

Siguiendo con el tema del número de parámetros la tabla lookup que no hay que indica el numero de parámetros de cada condacto podría haberse hecho todavía más fácil, ordenando los condactos por el número de parámetros de modo que podemos saber el número de parámetros por el opcode (por ejemplo hasta el 50 son 0, del 50 al 82 son 1, y del 82 para arriba son 2). Eso sí, eso crea incompatibilidad con versiones anteriores de PAW.

Por otro lado, dandole vueltas al asunto, caben los dos bits en el opcode que indicarían si lleva indirección en uno o en dos parámetros,la cuestión es crear una especie de opcode paralelo para cada opción, así :

- Para los condactos de PAW que no tienen parámetros (ANYKEY, QUIT, END, etc.) vale con un opcode.
- Para los condactos de PAW que tienen un parámetro (CHANCE, ZERO, ABSENT, etc.) hacen falta dos opcodes, el original (CHANCE x) y otro que sea por ejemplo CHANCE @x.
- Para los condactos de PAW que tienen dos parámetros (LET, ISAT, PLACE, etc.) hacen falta 4 opcodes: LET X Y, LET @X Y, LET X @Y y LET @X @Y.

- Hay 28 condactos sin parámetro (28 opcodes)
- Hay 53 condactos con un parámetro (106 opcodes)
- Hay 27 condactos con dos parámetros (108 opcodes)

El total de opcodes necesarios es 242, si bien sería necesario recolocarlos, para que fueran, de nuevo, todos los del mismo numero de parámetros juntos y así poder saber, cuando se lee el opcode, que hacer con la indirección.

Por ejemplo supongamos que colocamos el sistema así:

0-107: opcodes para los de dos parámetros
108-213: opcodes para los de un parámetro
214-241: opcodes para los sin parámetro

El código que decodifica, en pseudocódigo, sería:

Código:
cargar_opcode();
si (opcode <= 107)
{
  indireccion1 = valorbit(0, opcode);
  indireccion2 = valorbit(1, opcode);
  realopcode = SHR(opcode, 2);
}

si ((opcode > 107) Y (opcode<=213))
{
  indireccion1 = valorbit(0, opcode);
  indireccion2 = falso;
  realopcode = SHR(opcode, 1);
}

si (opcode > 213)
{
  indireccion1 = falso;
  indireccion2 = falso;
  realopcode = opcode
}



Los opcodes se almacenarían así

Imagen

_________________
Sígueme en twitter: @uto_dev
http://www.ngpaws.com


Arriba
 Perfil  
 
NotaPublicado: 20 Jul 2013 16:03 
Desconectado
Implementador
Implementador

Registrado: 13 Feb 2005 18:57
Mensajes: 1855
Sobre la carga en cada condacto del segundo parámetro,
Uto escribió:
Lo cierto es que entra dentro de lo posible que algunos condactos compartan código, y que no sean 48 sino menos, habría que mirar las funciones de cada uno de los condactos de 2 parametros para saberlo.


Creo que sería interesante analizar esto porque... ¿se podría utilizar ese supuesto código compartido para hacer ahí la indirección del segundo parámetro?


Arriba
 Perfil  
 
NotaPublicado: 20 Jul 2013 17:11 
Desconectado
Momio
Momio
Avatar de Usuario

Registrado: 09 Mar 2004 16:14
Mensajes: 4614
dddddd escribió:
Sobre la carga en cada condacto del segundo parámetro,
Uto escribió:
Lo cierto es que entra dentro de lo posible que algunos condactos compartan código, y que no sean 48 sino menos, habría que mirar las funciones de cada uno de los condactos de 2 parametros para saberlo.


Creo que sería interesante analizar esto porque... ¿se podría utilizar ese supuesto código compartido para hacer ahí la indirección del segundo parámetro?


Me temo que no. Al menos tres o cuatro condactos que mire lo pillan a pelo. Tiene su lógica: pillarlo a pelo son 2 bytes, hacer un CALL a un sitio común son tres.

Spoiler: Mostrar
si bien es cierto que igual que hace lo de coger el primer parámetro "por si acaso" y la dirección donde esta el flag apuntado por el primer parámetro, también por si acaso, y el otro valor que aún no se que es, bien podría haber leído el segundo parámetro por si acaso (3 bytes) y pasar de cargarlo en cada condacto.

_________________
Sígueme en twitter: @uto_dev
http://www.ngpaws.com


Arriba
 Perfil  
 
NotaPublicado: 21 Jul 2013 15:48 
Desconectado
Implementador
Implementador

Registrado: 13 Feb 2005 18:57
Mensajes: 1855
Uto escribió:
Lo siguiente que hace es guardar ese opcode en la dirección 34221, por razones que ignoro (no he visto cuando lo usa, quizá algún condacto lo haga, o haya condactos que compartan código y sea dependiente de ese valor).

Una búsqueda trivial sólo revela un uso de esa dirección:
Código:
29720 LD A,(34221)


Arriba
 Perfil  
 
NotaPublicado: 26 Jul 2013 15:35 
Desconectado
Momio
Momio
Avatar de Usuario

Registrado: 09 Mar 2004 16:14
Mensajes: 4614
dddddd escribió:
Uto escribió:
Lo siguiente que hace es guardar ese opcode en la dirección 34221, por razones que ignoro (no he visto cuando lo usa, quizá algún condacto lo haga, o haya condactos que compartan código y sea dependiente de ese valor).

Una búsqueda trivial sólo revela un uso de esa dirección:
Código:
29720 LD A,(34221)



Bien ,eso significa que quizá se pueda retocar el código para que no lo precargue, y poner en su lugar la precarga del parametro 2. Luego habría que cambiar el código de todos los condactos con dos parámetros para que no lean el valor sino que cojan el que les llega, y revisar el condacto que usa el (34221).

Complicado pero no imposible. Lo peor sería que alguno de los cambios "no cupiera", en cuyo caso tenemos un problema, porque mover de sitio código con tantísimo salto absoluto es bastante complicado.

_________________
Sígueme en twitter: @uto_dev
http://www.ngpaws.com


Arriba
 Perfil  
 
Mostrar mensajes previos:  Ordenar por  
Nuevo tema Responder al tema  [ 7 mensajes ] 

Todos los horarios son UTC + 1 hora


¿Quién está conectado?

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


No puede abrir nuevos temas en este Foro
No puede responder a temas en este Foro
No puede editar sus mensajes en este Foro
No puede borrar sus mensajes en este Foro

Buscar:
Saltar a:  
Desarrollado por phpBB® Forum Software © phpBB Group
Traducción al español por Huan Manwë para phpBB-Es.COM