A modo de ejercicio he hecho ULOOP compiler con el m.c. incrustado en la zona Run-time.
Para poder incrustar el código de modo que no se altere al cargar el diccionario o redefinir otra palabra con él, he hecho una palabra compilante
de uso general(*). Al usar esta palabra ya no necesitamos PC.
La he llamado RESERVE y lo que hace es reservar tantos bytes a continuación de ella como el número que se le suministra al compilarla. Además de hacer innecesario el uso de PC tiene otra ventaja, a saber, que la palabra ULOOP se puede listar. También se puede editar, pero no es conveniente por lo que luego explicaré.
Armados con RESERVE ya podemos definir ULOOP
Código: Seleccionar todo
2 COMPILER ULOOP
( 52)
HERE - ,
RUNS>
[ 52 ] RESERVE 2+ CALL
;
He incluido un comentario con el número que precisa suministrársele a RESERVE cuando se define ULOOP. El número se ha de dar en el momento de la compilación, de ahí el uso de los corchetes. No se puede usar ningún número que estuviera en la pila antes de empezar la definición de una palabra, pues el JA usa un sistema de comprobación que no lo permite (el JA comprueba que la pila queda igual al final de la compilación que antes, si no es así, da error #5). Es el número de bytes que tiene la rutina en m.c. (es la misma que ULOOPMC).
Si ahora listamos ULOOP veríamos esto:
El 52 en el comentario nos recuerda que hemos reservado 52 bytes de espacio entre el RESERVE y el +2 (sin contar los 2 primeros bytes que se reserva el propio RESERVE para guardar el número)
RESERVE al ejecutarse deja en la pila la dirección dónde se guarda el tamaño de la reserva. Este número está almacenado en los dos bytes que siguen a RESERVE. Luego de ellos vienen los bytes reservados para el m.c. Así, para ejecutar la rutina, usamos CALL con el número que devuelve RESERVER +2.
Ahora hay que cargar el programa en m.c. en ULOOP. Lo hago artesanalmente. Aunque es pesado, solo se hace una vez. Luego ya tenemos ULOOP para siempre.
NOTA: Si se editara ULOOP lo mejor es borrar el ULOOP nuevo, pues tendrá el campo de 52 bytes SIN el m.c. Habría que volver a cargarlo!!
Proceso de carga del m.c. (código máquina) a la zona reservada dentro de ULOOP:
Primero convierto ULOOPMC en tipo CREATE así:
DECIMAL
4076 FIND ULOOPMC !
Luego, con mi volcador hexadecimal a 3 columnas SHOWV (o con cualquier otro medio, incluyento el Debugger del EigthyOne) y sabiendo la estructura general de una palabra tipo COMPILER y la particular de ULOOP:
FIND ULOOP 2+ SHOWV
localizo dónde comienza la zona de 52 bytes reservadas en la zona Run-time de ULOOP. En mi caso era en la posición 20103 (este valor varía si se mueve la palabra). Me hago una palabra para realizar la copia:
y la ejecuto.
Con esto queda la palabra lista para usarse.
Timings: FAST/SLOW en segundos para 1000 vueltas
Método 1 (ULOOPMC): 0.30/0.38
Método 2 (RESERVE) : 0.59/0.77
Hay más overhead (sobre todo el debido a RESERVE)
NOTA: El método 3 (100% m.c.) lo dejo aparcado porque el assembler/Disassmbler de Boldfield (el que uso) se queda corto para lo que hay que hacer. Tendría que usar el TASM o algo por el estilo, con más apoyo, si no sería un lío hacerlo con el Boldfield. Así que queda aparcado.
(*) Al ser de uso general significa que, en el caso hipotético y muy poco probable, que definiéramos varias palabras compilantes (u otro tipo) que incrusten el código máquina de modo parecido, SOLO necesitamos un RESERVE mientras que por el método otro, necesitamos tantas "ULOOPMC"s como palabras definamos.