La máquina virtual haxbox dispone de tres cadenas de registros de uso general, llamados reg0, reg1 y reg2. El hecho de que sean tres y no dos o diez se explica fácilmente, dos de ellos están destinados a contener operandos y el tercero el resultado de cada operación básica, ya sea con números o con cadenas. Así pues si queremos diseñar un código que suma 2 + 3 el procedimiento sería el siguiente:

// 1- declarar el modo de los registros reg0 y reg1 
//    para operar deben de ser iguales:
mode0 int
mode1 int


// 2- introducir los operandos a los registros respectivos:
set0 2
set1 3


// 3- operar
suma

// 4- ahora tenemos el resultado contenido en reg2 y podemos enviarlo a la consola de salida
//    core_reg2 es la palabra reservada para recuperar el valor de reg2
busout 3,core_reg2


El código anterior funciona bien, pero no está en absoluto optimizado. Haxbox es muy permisivo en ciertos sentidos, en el punto 2 (set0 2 y set0 3) queremos guardar los operandos en los registros generales, pero el intérprete va a tratarlos como si fueran valores dinámicos, así que comprobará si pueden ser interpretados como variables, direcciones de memoria, fracciones de array o cualquier otro registro de la estructura. Puede que apenas notemos diferencia, pero esto llevará una fracción de tiempo que podemos ahorrar avisando a la máquina de que se trata de valores literales con el prefijo #, lo usaremos también en la instrucción modeX para indicar el modo y busout para indicar el satelite con el cual comunicar, no asi en el caso del segundo parámetro, ya que core_reg2 forma parte de la estructura dinámica:


// 1
mode0 #int
mode1 #int


// 2
set0 #2
set1 #3


// 3
suma

// 4
busout #3,core_reg2

Al usar el prefijo literal (#) conseguimos saltarnos una serie de búsquedas de valor dinámico que no darán ningún resultado y por lo tanto devolverá el mismo valor de entrada, por lo que se trata de trabajo inútil y debemos evitarlo. Por otro lado existe una órden para definir los modos de los tres registros en un mismo valor, en lugar de mode0 y mode1 podemos usar mode [tipo] lo cual nos ahorra una línea, así tendremos:


mode #int
set0 #2
set1 #3
suma
busout #3,core_reg2

Ahora si podemos decir que tenemos el código optimizado, la ejecución de este código dará como resultado una línea de texto en la consola en la que vendrá escrito el resultado de 2+3, es decir 5. Cada operación debe de ser efectuada de una en una y, como hemos dicho el resultado lo encontraremos siempre en core_reg2. Hemos dicho antes que haxbox dispone de tres cadenas de registros, en este caso sólo hemos usado el primer registro de cada cadena. Para cambiar entre unos registros y otros de cada cadena hay que modificar el valor del puntero de cada registro. Para ello existe la órden pX, donde x indica un identificador numérico (0, 1 o 2) y a la que le añadimos un parámetro que será la nueva posición del puntero. Imaginemos que queremos realizar una operación similar a la siguiente:

(2+3) + (3-2)

o lo que es lo mismo en notación infija postasignación:

2+3[a]3-2[b]a+b[res]

Como hemos explicado deberemos efectuar las operaciones de una en una y siempre con dos operandos, en este caso debemos efectuar tres operaciones, dos de ellas con valores literales y otra última con los resultados de las mismas. Tres registros se nos quedan cortos si queremos efectuar esta operación sin desperdiciar la memoria por eso haxbox dispone de cadenas de registros. Debemos usar la instrucción p0, p1 y p2 para guardar temporalmente los valores que nos interesan. El problema se podría resolver así:


// 1- el código comienza en p0=0, p1=0 y p2=0
//    efectuamos la primera operación:
mode #int
set0 #2
set1 #3
suma

// 2- tenemos el resultado de la suma contenido en reg2 p2 0
//    seguimos con la siguiente operación tras cambiar de posición p2:
p2 #1
set0 #3
set1 #2
resta


// 3- ahora deberemos asignar los dos resultados a los dos registros principales para operar 
//    los resultados intermedios están contenidos den core_reg2 p2 0 y core_reg2 p2 1:

set1 core_reg2
p2 #0
set0 core_reg2
suma


// 4- listo, ahora tenemos el resultado final en core_reg2 p2 0, podemos escribirlo o usarlo:
busout #3,core_reg2

// 5- hemos modificado varios registros de la cadena reg2, a veces necesitamos estar seguros de 
//  que  una cadena de registros está vacía para no generar errores, para eso usamos flashX o flash
flash


Esta vez hemos terminado usando flash (también existe flash0, flash1 y flash2), ésta órden símplemente limpia toda la cadena de registros, así no tendremos sorpresas inesperadas al continuar con el código puesto que nos aseguramos de que tenemos la memoria limpia. Existen 6 instrucciones importantes para manejar los registros genearles, éstos son: (X significa un identificador de cadena de registros, 0 para reg0, 1 para reg1 y 2 para reg2)


flash       para vaciar todos los registros de reg0, reg1 y reg2 a la vez
flashX     vacía todos los registros de la cadena de registros regX
setX        asigna un valor al registro señalado por regX
setX+      asigna valores múltiples separados por coma, uno en cada pX de regX
pX           cambia la dirección a la que apunta el puntero (pX) de regX
mode      cambia el modo en el que se operan los datos de todos los registros
modeX    cambia el modo en el que se operan los datos del registro regX
 
También existen varias palabras reservadas para leer datos y otra información de la estructura de registros generales, estas son las más importantes:

core_regX    devuelve el valor contenido en regX en el registro apuntado por pX
core_pX        devuelve un número que indica la posición actual del puntero de regX (pX)
core_numX   devuelve un número que representa la cantidad de objetos no nulos dentro de regX
core_joinX    devuelve una cadena de texto con todos los registros de regX concatenados
 

Haxbox es una máquina virtual capaz de interpretar código hax (o jenoassembler-hax), el cual está basado en la máquina virtual jénova, diseñada para correr sobre mundos de realidad virtual (opensim). Sus instrucciones básicas son compatibles con jénova, pero dispone de una estructura más completa así como de ciertas instrucciones especiales que iremos viendo.

Es costumbre comenzar con un simple código de escritura siempre que se presenta un lenguaje de programación, el clásico "hola mundo". Así que sin entrar en detalles sobre su funcionamiento aquí está un simple hola mundo en lenguaje hax.

// -hola mundo jahax
busout #3,#Hola mundo!
return

Como se puede apreciar, a pesar de tratarse de un lenguaje ensamblador simbólico dispone de una codificación bastante reducida, no es necesario añadir metadatos ni tampoco cabeceras especiales, lo cual lo transforma en un ensamblador muy peculiar y versátil. En este ejemplo no hemos tenido la necesidad de usar ningún registro ni bloque de memoria, tan sólo dos parámetros estáticos, los cuales son creados, usados y destruidos. Busout es la instrucción que usa los parámetros, sirve para enviar mensajes a los módulos periféricos de haxbox (llamados en ocasiones satelites), cada satelite tiene un protocolo especial y realiza diferentes funciones, en este caso nos comunicamos con el satelite número 3, hablaremos de ello más adelante. Tan sólo saber que la manera de acceder a la estructura de salida es la instrucción busout, que puede tener dos o tres parámetros, dependiendo del caso.
 
reg0, reg1 y reg2 son listas de registros, los resultados de operaciones se guardan en reg2
busout comunica con los satelites
flags es una lista de registros para leer información opcional
memscript contiene el código que se va a ejecutar
mempar contiene las variables en parejas nombre-valor
 
Haxbox es un intérprete de máquina virtual abstracta, una herramienta de programación con un lenguaje especial, que toma parte de los ensambladores mnemónicos y parte de los lenguajes de nivel alto. Tiene muchas peculiaridades que lo distinguen de otros sistemas de máquina virtual existentes, como por ejemplo java, tampoco emula la arquitectura x86 ni ninguna otra, se trata de una arquitectura propia diseñada especialmente para facilitar la flexibilidad, abstracción y legibilidad al máximo, así como optimizar la capacidad de compartir y editar los códigos. 

Un procesador común tiene un tipo de datos básico en el que funciona, el byte, es decir tanto las instrucciones como los parámetros de éstas son transformadas a largas cadenas de bytes a la hora de trabajar con los datos. Haxbox utiliza el tipo de cadena de texto como unidad mínima, éstas cadenas pueden ser transformadas a números o a interrupciones boleanas en tiempo de ejecución. Ésto permite la lectura del código en cualquier momento, así como la edición de la memoria. Haxbox no es multihilo, ésto no es un punto a favor, pero facilita la comprensión de los códigos así como su diseño. 

Haxbox está escrito en lenguaje .net, se podría entender como un interface que permite manejar una serie de objetos .net de un modo alternativo. Así pues un script en lenguaje hax lee las instrucciones almacenadas en su memoria y las transforma en interacciones con varios objetos separados en módulos. Un objeto es la consola de texto plano, otro para escribir o leer archivos, otra reproduce el sonido indicado, otra es una superficie donde reproducir gráficos gdi, otra ejecuta comandos de la consola de windows (cmd.exe) y devuelve opcionalmente la salida, otra hace peticiones tcp y otra hace peticiones web usando el componente iexplorer y captura la respuesta. Diseñar un programa manejando tantos métodos y objetos usando .net es tedioso, pero con haxbox en unas pocas líneas de texto plano podremos conseguir un resultado decente.

El lenguaje hax no tiene demasiadas instrucciones y su sintaxis es sencilla está basada en ensambladores mnemónicos. A pesar de asemejarse a un código ensamblador se trata de un lenguaje muy diferente con instrucciones que disparan procesos muy complejos y partes muy abstractas. El parecido proviene del hecho de que las instrucciones actuan diréctamente sobre la máquina, en este caso virtual, que dispone de registros y memorias al igual que un cpu físico. Eso si, haxbox mantiene un nivel de abstracción mucho más alto en el propio diseño de dichos componentes, haciendo que no debamos manejar complejas cadenas de bytes. Haxbox usa tipos de datos, pero éstos sólamente afectan en el momento de realizar operaciones y comparaciones aritméticas, el resto del tiempo se considera un objeto variable y su tipo sólo dependerá de dónde lo usemos. Las memorias están compuestas de registros que contienen cada uno una cadena de texto de longitud variable, tanto las memorias de programa como las de acceso aleatorio. 

Haxbox dispone de una arquitectura interna y otra externa. Llamamos interna a todo lo que incluye a los registros, la memoria del programa, las de acceso aleatorio, el contador, la caché etc y externa a los dispositivos virtuales de entrada-salida, la consola de texto plano, la superficie gráfica, el sonido, el sistema de archivos, el protocolo tcp etc... Para comunicarnos con toda esta estructura usamos sólamente una instrucción, "busout" que tiene 3 parámetros, el primero indica a qué componente enviar los datos, el segundo y el tercero son los datos que enviaremos. Cada dispositivo dispone de un protocolo propio, en ocasiones muy simple, como la consola de texto plano (que tan sólo escribe todo lo que llega en el primer dato) y otras algo más complejo (como la superficie gráfica). El resto de instrucciones interactuan con la arquitectura interna de la máquina y se usarán tanto para efectuar operaciones como para controlar el flujo del programa. 

 
Conjunto de instrucciones:

// comentario
@ etiqueta label
nop no ejecuta ninguna operación, a excepción de las operaciones laterales de los parámetros (como por ejemplo core_c+)

flash vaciar todos los casilleros de los tres registros generales
flash0 vaciar todos los casilleros de reg0
flash1 vaciar todos los casilleros de reg1
flash2 vaciar todos los casilleros de reg2
set0 asignar valor al casillero 0
set1 asignar valor al casillero 1
set2 asignar valor al casillero 2
set0+ asignar valores múltiples desde el casillero 0 actual ocupando los siguientes bloques
set1+ asignar valores múltiples desde el casillero 1 actual ocupando los siguientes bloques
set2+ asignar valores múltiples desde el casillero 2 actual ocupando los siguientes bloques
p0 cambiar el número de casillero 0
p1 cambiar el número de casillero 1
p2 cambiar el número de casillero 2
mode0 cambiar el tipo de dato en casillero 0
mode1 cambiar el tipo de dato en casillero 1
mode2 cambiar el tipo de dato en casillero 2
mode cambiar el tipo de datos de los tres registros

cachesave0 guardar todos los casilleros del registro 0 en el bloque caché indicado
cachesave1 guardar todos los casilleros del registro 1 en el bloque caché indicado
cachesave2 guardar todos los casilleros del registro 2 en el bloque caché indicado
cacheload0 recuperar el bloque de caché indicado en el registro 0
cacheload1 recuperar el bloque de caché indicado en el registro 1
cacheload2 recuperar el bloque de caché indicado en el registro 2

parse0 dividir el valor del casillero 0 usando el caracter indicado como separador
parse1 dividir el valor del casillero 1 usando el caracter indicado como separador
parse2 dividir el valor del casillero 2 usando el caracter indicado como separador
explode0 separar cada carácter de texto del registro 0 en un casillero de dicho registro
explode1 separar cada carácter de texto del registro 1 en un casillero de dicho registro
explode2 separar cada carácter de texto del registro 2 en un casillero de dicho registro

tobyte0 transforma todos los casilleros de texto de reg0 en su equivalente en bytes
tobyte1 transforma todos los casilleros de texto de reg1 en su equivalente en bytes
tobyte2 transforma todos los casilleros de texto de reg2 en su equivalente en bytes
todata0 transforma todos los casilleros de bytes de reg0 en su equivalente en texto
todata1 transforma todos los casilleros de bytes de reg1 en su equivalente en texto
todata2 transforma todos los casilleros de bytes de reg2 en su equivalente en texto

input para la ejecución hasta que el usuario introduzca datos
busout envía la pareja de datos indicada a través del bus requerido
set asigna valor a una palabra clave en la memoria de pares o crea una nueva
get sitúa en reg2 el valor de la palabra clave requerida
set+ guarda como lista todos los valores indicados en los parámetros
get+ recupera todos los valores de la lista indicada en reg2 
unset elimina la palabra clave indicada y su valor de la memoria de pares
debug escribe el texto indicado en la consola de debug

jump salta hasta la etiqueta indicada
jif_match salta hasta la etiqueta indicada si reg0 es igual a reg1
jif_max salta hasta la etiqueta indicada si reg0 es mayor que reg1
jif_less salta hasta la etiqueta indicada si reg0 es menor que reg1
jif salta hasta la etiqueta indicada si reg0 es igual que el valor true o 1
call llama al procedimiento de usuario indicado
return termina el procedimiento de usuario actual y vuelve al lugar desde el que fue llamado
hardstop para definitivamente la ejecución de la máquina
pause para la ejecución de la máquina hasta recibir la señal de control run

while inicia el modo bucle mientras la variable introducida sea true booleano
loop indica el final de un bucle while, saltará hasta el inicio solo si se cumple la condición
foreach inicia el modo bucle para cada uno de los casilleros de reg0
next pasa al siguiente casillero de reg0 para continuar con el bucle

suma suma reg1 a reg0 y lo guarda en reg2, si uno de los dos registros es str concatenará en lugar de sumar
resta resta reg1 a reg0 y lo guarda en reg2
mult multiplica reg0 y reg1 y lo guarda en reg2
div divide reg0 entre reg1 y lo guarda en reg2
rand genera un número aleatorio comprendido entre reg0 y reg1

and efectua la operación boleana and sobre reg0 y reg1 y guarda el resultado en reg2
or efectua la operación boleana or sobre reg0 y reg1 y guarda el resultado en reg2
not guarda en reg2 el valor boleano contrario a reg1

// :: obsoleto :: //
setc definir el valor numérico del contador
csum sumar el valor al contador
csub restar el valor del contador
minc definir valor mínimo del contador en modo loop
maxc definir valor máximo del contador en modo loop
loopc activar (1) o desactivar (0) el contador en modo loop
// :: obsoleto :: //
 
Los llamados satelites son periféricos virtuales de haxbox con los que se puede comunicar usando la instrucción busout. Cada uno de ellos tiene una función y un protocolo determinados, así como un número identificador:
 
Estos son unos simples ejemplos de uso del lenguaje jenoassembler-hax que pueden disipar alguna duda sobre el mismo:


suma de valores introducidos por el usuario

// sumar dos números introducidos
call frame.prompt,#introduce número 1
set #num1,core_reg2
call frame.prompt,#introduce número 2
set #num2,core_reg2
mode #int
set0 num1
set1 num2
suma
call frame.print,core_reg2
return


capturar teclas pulsadas y escribirlas en pantalla

// test de captura de teclas
busout 8,start
@ rst
call frame.printinln,surface_key
jump @ rst
// fin

bucles foreach y while

// test bucles
busout 8,start
flash0
set0+ hola,mundo,entero,
p0 0
foreach
call frame.print,core_reg0
next

flash0
set0+ adios,cabeza,melon
p0 0
foreach
call frame.print,core_reg0
next

setc 5
while core_c-
call frame.printinln,#instrucciones iterativas!-
call frame.print,core_c
loop

return

sufijo post-suma
// sumar 1 automáticamente a test al consultarlo y lo escribe en consola
mode int
set test,10

while 1
call frame.print,test++
loop

cambiar color de fondo de surface

// cambia constantemente el color del fondo
busout 8,start
@ start
busout #8,#background,#green
call frame.print,#green
sleep #1
busout #8,#background,#blue
call frame.print,#blue
sleep #1
busout #8,#background,#red
call frame.print,#red
sleep #1
jump @ start
 
Frame.hax es un archivo pensado para ser cargado a la memoria de haxbox justo al iniciar. Debería incluir los procedimientos más usados escritos en jenoassembler-hax para que cualquiera de nuestros programas pueda usarlos. Se aconseja mucho el uso de éstas funciones con su mismo nombre si se quiere mantener compatibilidad con los códigos de este sitio. Éste es el frame.hax hoy día.

// frame.hax para haxbox 0.05
busout 8,start
call frame.print,#*********************************** /n
call frame.print,#****  maquina virtual haxbox.  **** /n
call frame.print,#*********************************** /n /n
call frame.print,#2012 abdlab software/n/n/n/n
set #_%ilasmexe%_,#C:\Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe 
jump @ haxbox_frame_fin

// :: haxboxframework V.1.0 ::

proc frame.print
busout #3,f!1
busout #3,/n
return

proc frame.printinln
busout #3,f!1
return

proc frame.printlst
get+ f!1
set0+ core_reg2
foreach
call frame.print,core_reg0
next
return

proc frame.prompt
busout #3,f!1
busout #3,/n
input
busout #3,core_reg2
busout #3,/n
return core_reg2

proc frame.rand
set #_nums0,f!1
set #_nums1,f!2
mode int
set0 _nums0
set1 _nums1
rand
set #?return,core_reg2
mode str
return core_reg2

proc frame.il.make
set #_tmp,f!1
call frame.concat,#_%ilasmexe%_,_tmp
call frame.shell,core_reg2
return f!0

proc frame.shell
busout #10,f!1
return f!0

proc frame.web
busout #12,f!1
return f!0

proc frame.telnet
busout #11,f!1,f!2
return f!0

proc frame.nav.open
set _tmp,f!1
call frame.concat,#start ,_tmp
busout #10,core_reg2
return

proc frame.savefile
busout #5,f!1,f!2
return

proc frame.readfile
busout #6,f!1
return f!0

proc frame.concat
mode str
set0 f!1
set1 f!2
suma
return core_reg2

proc frame.file.del
mode str
set0 #del 
set1 f!1
suma
busout #10,core_reg2
return

proc frame.bytecrypt
set0 f!1
explode0
tobyte0
join0 #x
return core_reg0

proc frame.bytedecrypt
set0 f!1
parse0 #x
todata0
set0 core_dump0
return core_reg0

proc frame.crypt
set #_pass,f!2
set0 f!1
explode0
tobyte0
set #_nums0,core_num0
set #_nums1,_nums0
join0 #x
return core_reg0

proc frame.decrypt
set0 f!1
parse0 #x
todata0
set0 core_dump0
return core_reg0

@ haxbox_frame_fin
call frame.print,#$cls

Abdab software