Home  +  Forums  +  C++ and Sockets  +  C++ and SQL: MySQL, sqlite, ODBC  +  Miscellaneous Projects
Logo
~Sockets~
~Projects~
~Contact~

Rowo code samples

Rowo language

sample.r
Main
{
	if (!robot.scanning && !robot.driving && !gun.tilting && !turret.turning)
	{
		radar.scan( 0 )
	}
}

OnScan
{
	o = loop radar.objects
	{
		if (o.type == Robot)
		{
			if (o.friend)
			{
			}
			if (o.foe)
			{
				turret.turn(o.relative_angle)
				gun.tilt(o.distance)
//				end loop
			}
			if (o.neutral)
			{
			}
		}
		if (o.type == Powerup)
		{
			robot.turn(o.relative_angle)
			robot.distance = o.distance
		}
	}
}

OnTurretTurnComplete
{
	if (!gun.tilting)
	{
		gun.fire( 0 )
	}
}

OnTiltComplete
{
	if (!turret.turning)
	{
		gun.fire( 0 )
	}
}

OnRobotTurnComplete
{
	robot.drive()
}

OnArrival
{
	if (robot.at_powerup)
	{
		robot.get()
	}
}


Pseudo assembler output

sample.xml
<?xml version='1.0'?>
<robot>
<name value='sample' />
<owner value='3e1a4392-58f2-429c-b172-a92722d9fa8e' />
<code value="AQMAEgIBBAASAgEBABICAQEAEiQ0JDQkNAoACQMAAAAAAg4CAISCAgAAn4MAAAoAmQMAAAABAhAAAAR0eXBlJCkKAEsQAAAGZnJpZW5kCgAAEAAAA2ZvZQoAJhAAAA5yZWxhdGl2ZV9hbmdsZQIOAwAQAAAIZGlzdGFuY2UCDgQAEAAAB25ldXRyYWwKAAADAAAABAIQAAAEdHlwZSQpCgAlEAAADnJlbGF0aXZlX2FuZ2xlAg4BABAAAAhkaXN0YW5jZQ8BAIGAnIQBBAASCgAJAwAAAAACDgQAhAEDABIKAAkDAAAAAAIOBACEDgEAhAEBAAoAAw4BAIQ=" />
<methods>
<method name='Main' entry='0' />
<method name='OnArrival' entry='241' />
<method name='OnRobotTurnComplete' entry='237' />
<method name='OnScan' entry='38' />
<method name='OnTiltComplete' entry='220' />
<method name='OnTurretTurnComplete' entry='203' />
</methods>
<variables>
<variable name='o' type='2' index='0' />
</variables>
</robot>

Yacc parser

rowo.y
%{
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <map>
#include "robotworld.h"

#define C the_str[the_ptr]
#define C1 the_str[the_ptr+1]

// local
static	char the_str[1000];
static	int the_ptr;
static	int line;
static	FILE *fil;
static	int uniq;
static	std::vector<VARIABLE *> variables;
static	std::string codestr; // total bytecode string
static	std::map<std::string, int> codemap; // method to address map
static	bool failed;
static	int x_ptr;

const std::string& GetCodestr() { return codestr; }
std::map<std::string, int>& GetCodemap() { return codemap; }
std::vector<VARIABLE *>& GetVariables() { return variables; }
bool GetFailed() { return failed; }

// forward declarations
int yyparse();
short reg_variable(const std::string& ,int type = 0);
void check_property(const std::string& , const std::string& );
void check_method(const std::string& , const std::string& );
void add_bytecode(char *, int opcode);
void add_bytecode(char *, int opcode, short val);
void add_bytecode(char *, int opcode, long val);
void add_bytecode(char *, int opcode, const std::string& object, const std::string& property_or_method);
%}

%union {
	long l;
	char str[1000];
	struct codestruct {
		char code[10000];
		char bytecode[10000];
	} code;
}

%token <l> INT
%token <str> IDENTIFIER
%token <str> COMMENT
%token <str> Object
%token <l> EnumObjectType

%token Cos Sin
%token If Loop
%token EQ NEQ AND OR

%left ':'
%left AND OR
%left EQ NEQ
%left '?'
%left '-' '+'
%left '*' '/'
%left '!'

%type <code> expr
%type <code> parameters parameter
%type <code> statements statement
%type <code> if_statement assign_statement loop_statement
%type <code> object_statement
%type <code> program methods method scope
%%

program : methods

methods : method
| methods method

method : IDENTIFIER scope {
		// if (*scope) ...
		add_bytecode($2.bytecode, 0x84); // ret
		// create codestr / codemap
		codemap[$1] = codestr.size() / 2; // entry point
		codestr += $2.bytecode;
	}

scope : '{' '}' {
		*$$.code = 0;
		*$$.bytecode = 0;
	}
| '{' statements '}' {
		strcpy($$.code, $2.code);
		// bytecode
		strcpy($$.bytecode, $2.bytecode);
	}

statements : statement {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| statements statement {
		strcpy($$.code, $1.code);
		strcat($$.code, $2.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
		strcat($$.bytecode, $2.bytecode);
	}

statement : if_statement {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| assign_statement {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| loop_statement {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| object_statement {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| COMMENT {	*$$.code = 0;
		*$$.bytecode = 0;
	}

if_statement : If '(' expr ')' scope {
		// if (*scope) ...
		int x = uniq++;
		*$$.code = 0;
		strcat($$.code, "	// if_statement\n");
		strcat($$.code, $3.code);
		sprintf($$.code + strlen($$.code), "	jz	label_%d\n", x);
		strcat($$.code, $5.code);
		sprintf($$.code + strlen($$.code), "label_%d:\n", x);
		// bytecode
		strcpy($$.bytecode, $3.bytecode); // get expression
		short len = strlen($5.bytecode) / 2; // length in bytes
		add_bytecode($$.bytecode, 0x0a, len); // jz
		strcat($$.bytecode, $5.bytecode); // scope
	}

assign_statement : IDENTIFIER '=' expr {
		*$$.code = 0;
		strcat($$.code, "	// assign_statement\n");
		strcat($$.code, $3.code);
		sprintf($$.code + strlen($$.code), "	ld	$%s, a\n", $1);
		//
		short var = reg_variable($1, 1);
		// bytecode
		strcpy($$.bytecode, $3.bytecode); // get expression
		add_bytecode($$.bytecode, 0x0d, var); // variable[] = register1
	}
| Object '.' IDENTIFIER '=' expr {
		*$$.code = 0;
		strcat($$.code, "	// assign_statement\n");
		strcat($$.code, $5.code);
		sprintf($$.code + strlen($$.code), "	ld	$%s.%s, a\n", $1, $3);
		//
		check_property($1, $3);
		// bytecode
		strcpy($$.bytecode, $5.bytecode); // get expression
		add_bytecode($$.bytecode, 0x0f, $1, $3); // assign object.property
	}

loop_statement : IDENTIFIER '=' Loop Object '.' IDENTIFIER scope {
		short var = reg_variable($1, 2);
		check_property($4, $6);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x82, $4, $6); 	// loop init object.property
		strcat($$.bytecode, "0000");			// loop body length
		size_t x = strlen($$.bytecode);
		add_bytecode($$.bytecode, 0x83, var); 		// assign object variable: 3 bytes
		short len = strlen($7.bytecode) / 2 + 3; 	// length in bytes
		add_bytecode($$.bytecode, 0x0a, len); 		// jz end of loop: 3 bytes
		strcat($$.bytecode, $7.bytecode); 		// scope
		len = strlen($7.bytecode) / 2 + 6;
		len = -len;
		add_bytecode($$.bytecode, 0x81, len);		// jmp loop beginning
		//
		size_t body = (strlen($$.bytecode) - x) / 2;
		char slask[100];
		sprintf(slask, "%04x", body);
		memcpy($$.bytecode + x - 4, slask, 4);
	}
| IDENTIFIER '=' Loop IDENTIFIER '.' IDENTIFIER scope {
		short var1 = reg_variable($1, 2);
		short var2 = reg_variable($4);
		short len = strlen($6);
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x11, var2);		// register1 = variable[zz zz].property
		sprintf($$.bytecode + strlen($$.bytecode), "%02x", len);
		for (int i = 0; i < len; i++)
		{
			sprintf($$.bytecode + strlen($$.bytecode), "%02x", $6[i]);
		}
		strcat($$.bytecode, "0000");			// loop body length
		size_t x = strlen($$.bytecode);
		add_bytecode($$.bytecode, 0x83, var1); 		// assign object variable: 3 bytes
		len = strlen($7.bytecode) / 2 + 3; 		// length in bytes
		add_bytecode($$.bytecode, 0x0a, len); 		// jz end of loop: 3 bytes
		strcat($$.bytecode, $7.bytecode); 		// scope
		len = strlen($7.bytecode) / 2 + 6;
		len = -len;
		add_bytecode($$.bytecode, 0x81, len);		// jmp loop beginning
		//
		size_t body = (strlen($$.bytecode) - x) / 2;
		char slask[100];
		sprintf(slask, "%04x", body);
		memcpy($$.bytecode + x - 4, slask, 4);
	}

object_statement : Object '.' IDENTIFIER '(' parameters ')' {
		*$$.code = 0;
		strcat($$.code, "	// object_statement\n");
		strcat($$.code, $5.code);
		sprintf($$.code + strlen($$.code), "	call $%s.%s\n", $1, $3);
		strcat($$.code, "	pop	a // ? - call returns with value in a, not on stack perhaps?\n");
		//
		check_method($1, $3);
		// bytecode
		*$$.bytecode = 0;
		strcpy($$.bytecode, $5.bytecode); 		// parameters
		add_bytecode($$.bytecode, 0x0e, $1, $3); 	// call object.method result in register1
	}

parameters : {
		*$$.code = 0;
		*$$.bytecode = 0;
	}
| parameter {
		strcpy($$.code, $1.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
	}
| parameters ', ' parameter {
		strcpy($$.code, $1.code);
		strcat($$.code, $3.code);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
		strcat($$.bytecode, $3.bytecode);
	}

parameter : expr {
		strcpy($$.code, $1.code);
		strcat($$.code, "	push	a\n");
		// bytecode
		strcpy($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
	}

expr : INT { 	sprintf($$.code, "	ld	a, %ld\n", $1);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x03, $1);
	}
| EnumObjectType {
		sprintf($$.code, "	ld	a, %ld\n", $1);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x03, $1);
	}
| Object '.' IDENTIFIER {
		sprintf($$.code, "	ld	a, $%s.%s\n", $1, $3);
		//
		check_property($1, $3);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x01, $1, $3); 	// register1 = object.property
	}
| IDENTIFIER { 	sprintf($$.code, "	ld	a, $%s\n", $1);
		//
		short var = reg_variable($1);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x0c, var);		// register1 = variable
	}
| IDENTIFIER '.' IDENTIFIER {
		sprintf($$.code, "	ld	a, $%s.%s\n", $1, $3);
		//
		short var = reg_variable($1);
		short len = strlen($3);
		check_property($1, $3);
		// bytecode
		*$$.bytecode = 0;
		add_bytecode($$.bytecode, 0x10, var);		// register1 = variable[zz zz].property
		sprintf($$.bytecode + strlen($$.bytecode), "%02x", len);
		for (int i = 0; i < len; i++)
		{
			sprintf($$.bytecode + strlen($$.bytecode), "%02x", $3[i]);
		}
	}
| expr '+' expr {
		strcpy($$.code, $3.code);
		strcat($$.code, "	push	a\n");
		strcat($$.code, $1.code);
		strcat($$.code, "	pop	b\n");
		strcat($$.code, "	add	b\n");
		// bytecode
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x25);		// add register2
	}
| expr '-' expr {
		strcpy($$.code, $3.code);
		strcat($$.code, "	push	a\n");
		strcat($$.code, $1.code);
		strcat($$.code, "	pop	b\n");
		strcat($$.code, "	sub	b\n");
		// bytecode
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x26);		// add register2
	}
| expr '*' expr {
		strcpy($$.code, $3.code);
		strcat($$.code, "	push	a\n");
		strcat($$.code, $1.code);
		strcat($$.code, "	pop	b\n");
		strcat($$.code, "	mul	b\n");
		// bytecode
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x27);		// add register2
	}
| expr '/' expr {
		strcpy($$.code, $3.code);
		strcat($$.code, "	push	a\n");
		strcat($$.code, $1.code);
		strcat($$.code, "	pop	b\n");
		strcat($$.code, "	div	b\n");
		// bytecode
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x28);		// add register2
	}
| expr EQ expr {
		strcpy($$.code, $1.code);
		strcat($$.code, "	push	a\n");
		strcat($$.code, $3.code);
		strcat($$.code, "	pop	b\n");
		strcat($$.code, "	eq	b // a=0 if a!=b, a=1 if a==b\n");
		// bytecode
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x29);		// add register2
	}
| expr '?' expr ':' expr {
		int x = uniq++;
		strcpy($$.code, $1.code);
		sprintf($$.code + strlen($$.code), "	jz	label_second_%d\n", x);
		strcat($$.code, $3.code);
		sprintf($$.code + strlen($$.code), "	jmp	label_%d\n", x);
		sprintf($$.code + strlen($$.code), "label_second_%d:\n", x);
		strcat($$.code, $5.code);
		sprintf($$.code + strlen($$.code), "label_%d:\n", x);
		// bytecode
		strcpy($$.bytecode, $1.bytecode);		// get expression
		short len = strlen($3.bytecode) / 2 + 3;	// first block + jmp
		add_bytecode($$.bytecode, 0x0a, len);		// jz
		strcat($$.bytecode, $3.bytecode);
		len = strlen($5.bytecode) / 2;
		add_bytecode($$.bytecode, 0x81, len);		// jmp
		strcat($$.bytecode, $5.bytecode);
	}
| '(' expr ')' {
		strcpy($$.code, $2.code);
		// bytecode
		strcpy($$.bytecode, $2.bytecode);
	}
| '!' expr {
		*$$.bytecode = 0;
		strcpy($$.bytecode, $2.bytecode);
		add_bytecode($$.bytecode, 0x12);		// not
	}
| expr NEQ expr {
		*$$.bytecode = 0;
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x33);		// neq register2
	}
| expr AND expr {
		*$$.bytecode = 0;
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x34);		// and register2
	}
| expr OR expr {
		*$$.bytecode = 0;
		strcpy($$.bytecode, $3.bytecode);
		add_bytecode($$.bytecode, 0x02);		// push register1
		strcat($$.bytecode, $1.bytecode);
		add_bytecode($$.bytecode, 0x24);		// pop register2
		add_bytecode($$.bytecode, 0x35);		// or register2
	}
%%


int yyerror(char *s)
{
	int i;

	printf("%d>%s\n", line, the_str);
	printf("%d ", line);
	for (i = 1; i < x_ptr; i++)
		printf(" ");
	printf("^ yyerror '%s'\n", s);
	failed = true;
	return 0;
}


int yylex()
{
	size_t x;

	while (C == ' ' || C == 9)
	{
		the_ptr++;
	}
	while (!C && !feof(fil))
	{
		fgets(the_str, 1000, fil);
		the_str[strlen(the_str) - 1] = 0;
		for (size_t i = 0; i < strlen(the_str); i++)
			if (the_str[i] == 9)
				the_str[i] = ' ';
		the_ptr = 0;
		line++;
		while (C == ' ' || C == 9)
		{
			the_ptr++;
		}
	}
	if (feof(fil))
	{
		return 0;
	}
	x_ptr = the_ptr;
	if (C == '=' && C1 == '=')
	{
		the_ptr += 2;
		return EQ;
	}
	if (C == '!' && C1 == '=')
	{
		the_ptr += 2;
		return NEQ;
	}
	if (C == '&' && C1 == '&')
	{
		the_ptr += 2;
		return AND;
	}
	if (C == '|' && C1 == '|')
	{
		the_ptr += 2;
		return OR;
	}
	if (C == '/' && C1 == '/')
	{
		strcpy(yylval.str, the_str + the_ptr);
		C = 0;
		return COMMENT;
	}
	if (isalpha(C))
	{
		x = the_ptr++;
		while (isalnum(C) || C == '_')
		{
			the_ptr++;
		}
		std::string ord = static_cast<std::string>(the_str).substr(x, the_ptr - x);
		strcpy(yylval.str, ord.c_str());
		if (ord == "if")
			return If;
		if (ord == "radar" || ord == "turret" || ord == "gun" || ord == "robot" || ord == "shields" )
		{
			return Object;
		}
		if (ord == "Robot") // Robot, Portal, Powerup, Base, Nothing, Item
		{
			yylval.l = MAPOBJECT_ROBOT;
			return EnumObjectType;
		}
		if (ord == "Portal")
		{
			yylval.l = MAPOBJECT_PORTAL;
			return EnumObjectType;
		}
		if (ord == "Powerup")
		{
			yylval.l = MAPOBJECT_POWERUP_UNKNOWN;
			return EnumObjectType;
		}
		if (ord == "Base")
		{
			yylval.l = MAPOBJECT_BASE;
			return EnumObjectType;
		}
		if (ord == "Item")
		{
			yylval.l = MAPOBJECT_ITEM;
			return EnumObjectType;
		}
		if (ord == "Nothing")
		{
			yylval.l = MAPOBJECT_NOTHING;
			return EnumObjectType;
		}
		if (ord == "loop")
			return Loop;
		return IDENTIFIER;
	}
	if (isdigit(C))
	{
		bool f = false;
		x = the_ptr;
		while (isdigit(C)) // || C == '.' || C == 'e' || C == 'E')
		{
			if (!isdigit(C))
			f = true;
			the_ptr++;
		}
		std::string ord = static_cast<std::string>(the_str).substr(x, the_ptr - x);
/*
		if (f)
		{
			yylval.d = atof(ord.c_str());
			return FLOAT;
		}
*/
		yylval.l = atol(ord.c_str());
		return INT;
	}
	return the_str[the_ptr++];
}


void parse_file(const std::string& filename)
{
	*the_str = 0;
	the_ptr = 0;
	line = 0;
	uniq = 1;
	codestr = "";
	failed = false;
	while (codemap.size())
	{
		codemap.erase(codemap.begin());
	}
	while (variables.size())
	{
		variables.erase(variables.begin());
	}
	if ((fil = fopen(filename.c_str(), "rt")) != NULL)
	{
		yyparse();
		fclose(fil);
	}
	else
	{
		fprintf(stderr, "Couldn't open '%s'...\n", filename.c_str());
		failed = true;
	}
}


short reg_variable(const std::string& name,int type)
{
	for (std::vector<VARIABLE *>::iterator it = variables.begin(); it != variables.end(); it++)
	{
		VARIABLE *p = *it;
		if (name == p -> name)
		{
			if (type && !p -> type)
				p -> type = type;
			if (type && p -> type && type != p -> type)
				fprintf(stderr,"Variable type mismatch: '%s'\n",name.c_str());
			return p -> nr;
		}
	}
	VARIABLE *p = new VARIABLE(name, type, variables.size());
	variables.push_back(p);
	return p -> nr;
}


void check_property(const std::string& object, const std::string& property)
{
}


void check_method(const std::string& object, const std::string& method)
{
}


void add_bytecode(char *s, int opcode)
{
	sprintf(s + strlen(s), "%02x", opcode);
}


void add_bytecode(char *s, int opcode, short valx)
{
	unsigned short val;
	if (valx < 0)
	{
		val = -valx;
		val |= 0x8000;
	}
	else
	{
		val = valx;
	}
	sprintf(s + strlen(s), "%02x%04x", opcode, val);
}


void add_bytecode(char *s, int opcode, long val)
{
	sprintf(s + strlen(s), "%02x%08lx", opcode, val);
}


void add_bytecode(char *s, int opcode, const std::string& object, const std::string& property_or_method)
{
	int objectcode = 0;
	int val = 0;
	{
		// robot, radar, turret, gun, shields
		if (!strcmp(object.c_str(), "robot"))
		{
			objectcode = ROBOTOBJECT_ROBOT;
			if (property_or_method == "ok")
				val = 1;
			if (property_or_method == "damage")
				val = 2;
			if (property_or_method == "repair_rate")
				val = 3;
			if (property_or_method == "inventory")
				val = 4;
			if (property_or_method == "power")
				val = 5;
			if (property_or_method == "charging")
				val = 6;
		}
		else
		if (!strcmp(object.c_str(), "radar"))
		{
			objectcode = ROBOTOBJECT_RADAR;
			if (property_or_method == "ok")
				val = 1;
			if (property_or_method == "damage")
				val = 2;
			if (property_or_method == "repair_rate")
				val = 3;
		}
		else
		if (!strcmp(object.c_str(), "turret"))
		{
			objectcode = ROBOTOBJECT_TURRET;
			if (property_or_method == "ok")
				val = 1;
			if (property_or_method == "damage")
				val = 2;
			if (property_or_method == "repair_rate")
				val = 3;
		}
		else
		if (!strcmp(object.c_str(), "gun"))
		{
			objectcode = ROBOTOBJECT_GUN;
			if (property_or_method == "ok")
				val = 1;
			if (property_or_method == "damage")
				val = 2;
			if (property_or_method == "repair_rate")
				val = 3;
		}
		else
		if (!strcmp(object.c_str(), "shields"))
		{
			objectcode = ROBOTOBJECT_SHIELDS;
			if (property_or_method == "ok")
				val = 1;
			if (property_or_method == "damage")
				val = 2;
			if (property_or_method == "repair_rate")
				val = 3;
		}
		else
		{
			printf("Unknown object: %s\n", object.c_str() );
			failed = true;
		}
	}
	sprintf(s + strlen(s), "%02x%02x%02x", opcode, objectcode, val);
}



Page, code, and content Copyright (C) 2021 by Anders Hedström