#include <piggybacker/ieee1275.h>
word_t call_addr = 0;
__unused prom_handle_t prom_stdout = INVALID_PROM_HANDLE;
__unused prom_handle_t prom_stdin = INVALID_PROM_HANDLE;
__unused prom_handle_t prom_chosen = INVALID_PROM_HANDLE;
__unused prom_handle_t prom_options = INVALID_PROM_HANDLE;
__unused prom_handle_t prom_memory = INVALID_PROM_HANDLE;
#define ToPointer32(x)	(unsigned int)((unsigned long)x & 0xffffffff)
int prom_get_path( prom_handle_t phandle, const char *service, char *path, int pathlen )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int phandle;
		unsigned int path;
		int pathlen;
		int size;
	} args;
	args.service = ToPointer32(service);
	args.nargs = 3;
	args.nret = 1;
	args.phandle = ToPointer32(phandle);
	args.path = ToPointer32(path);
	args.pathlen = pathlen;
	args.size = -1;
	prom_entry( &args );
	if( args.size > -1 )
		path[args.size] = '\0';
	return args.size;
}
int prom_package_to_path( prom_handle_t phandle, char *path, int pathlen )
{
	return prom_get_path( phandle, "package-to-path", path, pathlen );
}
int prom_instance_to_path( prom_handle_t ihandle, char *path, int pathlen )
{
	return prom_get_path( ihandle, "instance-to-path", path, pathlen );
}
prom_handle_t prom_instance_to_package( prom_handle_t ihandle )
{
    struct {
	unsigned int service;
	int nargs;
	int nret;
	unsigned int ihandle;
	int phandle;
    } args;
    args.service = ToPointer32("instance-to-package");
    args.nargs = 1;
    args.nret = 1;
    args.ihandle = ToPointer32(ihandle);
    args.phandle = ToPointer32(INVALID_PROM_HANDLE);
    prom_entry( &args );
    return (prom_handle_t)(word_t)args.phandle;
}
int prom_next_prop( prom_handle_t node, const char *prev_name, char *name )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int node;
		unsigned int prev_name;
		unsigned int name;
		int flag;
	} args;
	args.service = ToPointer32("nextprop");
	args.nargs = 3;
	args.nret = 1;
	args.node = ToPointer32(node);
	args.prev_name = ToPointer32(prev_name);
	args.name = ToPointer32(name);
	args.flag = -1;
	prom_entry( &args );
	return args.flag;
}
int prom_read( prom_handle_t phandle, void *buf, int len )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int phandle;
		unsigned int buf;
		int len;
		int actual;
	} args;
	args.service = ToPointer32("read");
	args.nargs = 3;
	args.nret = 1;
	args.phandle = ToPointer32(phandle);
	args.buf = ToPointer32(buf);
	args.len = len;
	args.actual = -1;
	prom_entry( &args );
	return args.actual;
}
int prom_write( prom_handle_t phandle, const void *buf, int len )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int phandle;
		unsigned int buf;
		int len;
		int actual;
	} args;
	args.service = ToPointer32("write");
	args.nargs = 3;
	args.nret = 1;
	args.phandle = ToPointer32(phandle);
	args.buf = ToPointer32(buf);
	args.len = len;
	args.actual = -1;
	prom_entry( &args );
	return args.actual;
}
void prom_puts( const char *msg )
{
        unsigned start, current, len;
	char nl[] = "\r\n";
	current = 0;
	do {
		start = current;
		
		while( msg[current] && (msg[current] != '\n') )
			current++;
		
		len = current-start;
		if( len > 0 )
			prom_write( prom_stdout, msg, len );
		
		prom_write( prom_stdout, nl, sizeof(nl) );
		
		if( msg[current] == '\n' )
			current++;
	} while( msg[current] );
}
prom_handle_t prom_find_device( const char *name )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int name;
		int phandle;
	} args;
	args.service = ToPointer32("finddevice");
	args.nargs = 1;
	args.nret = 1;
	args.name = ToPointer32(name);
	args.phandle = ToPointer32(INVALID_PROM_HANDLE);
	prom_entry( &args );
	return (prom_handle_t)(word_t)args.phandle;
}
int prom_get_prop( prom_handle_t phandle, const char *name, void *buf, int buflen )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int phandle;
		unsigned int name;
		unsigned int buf;
		int buflen;
		int size;
	} args;
	args.service = ToPointer32("getprop");
	args.nargs = 4;
	args.nret = 1;
	args.phandle = ToPointer32(phandle);
	args.name = ToPointer32(name);
	for (int i=0; i<buflen; i++)
	    ((char*)buf)[i] = 0;
	args.buf = ToPointer32(buf);
	args.buflen = buflen;
	args.size = -1;
	prom_entry( &args );
	return args.size;
}
int prom_get_prop_len( prom_handle_t phandle, const char *name )
{
    struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int phandle;
		unsigned int name;
		int size;
	} args;
	args.service = ToPointer32("getproplen");
	args.nargs = 2;
	args.nret = 1;
	args.phandle = ToPointer32(phandle);
	args.name = ToPointer32(name);
	args.size = -1;
	prom_entry( &args );
	return args.size;
}
prom_handle_t prom_nav_tree( prom_handle_t node, const char *which )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int node;
		int out;
	} args;
	args.service = ToPointer32(which);
	args.nargs = 1;
	args.nret = 1;
	args.node = ToPointer32(node);
	args.out = ToPointer32(INVALID_PROM_HANDLE);
	prom_entry( &args );
	if( args.out == 0 )
		args.out = ToPointer32(INVALID_PROM_HANDLE);
	return (prom_handle_t)(word_t)args.out;
}
__noreturn void prom_exit( void )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
	} args;
	args.service = ToPointer32("exit");
	args.nargs = 0;
	args.nret = 0;
	prom_entry( &args );
	
	while( 1 ) ;
}
__noreturn void prom_fatal( const char *msg )
{
	char caption[] = "\r\nfatal error: ";
	if( prom_stdout != INVALID_PROM_HANDLE ) {
		prom_write( prom_stdout, caption, sizeof(caption) );
		prom_puts( msg );
	}
	prom_exit();
}
void prom_enter( void )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
	} args;
	args.service = ToPointer32("enter");
	args.nargs = 0;
	args.nret = 0;
	prom_entry( &args );
}
int prom_interpret( const char *forth )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int forth;
		int result;
	} args;
	args.service = ToPointer32("interpret");
	args.nargs = 1;
	args.nret = 1;
	args.forth = ToPointer32(forth);
	args.result = -1;
	prom_entry( &args );
	return args.result;
}
prom_handle_t prom_open( char *device )
{
    struct {
	unsigned int service;
	int nargs;
	int nret;
	unsigned int name;
	unsigned int handle;
    } args;
    args.service = ToPointer32("open");
    args.nargs = 1;
    args.nret = 1;
    args.name = ToPointer32(device);
    args.handle = ToPointer32(INVALID_PROM_HANDLE);
    prom_entry( &args );
    return (prom_handle_t)(word_t)args.handle;
}
word_t prom_instantiate_rtas( word_t rtas_base_address )
{
    prom_handle_t prom_rtas;
    prom_rtas = prom_open( "/rtas" );
    if( prom_rtas == INVALID_PROM_HANDLE )
	return 0;
    struct {
	unsigned int service;
	int nargs;
	int nret;
	unsigned int method;
	unsigned int handle;
	unsigned int rtas_base_address;
	int result;
	unsigned int rtas_call;
    } args;
    args.service = ToPointer32("call-method");
    args.nargs = 3;
    args.nret = 2;
    args.method = ToPointer32("instantiate-rtas");
    args.handle = ToPointer32(prom_rtas);
    args.rtas_base_address = ToPointer32(rtas_base_address);
    args.result = -1;
    args.rtas_call = 0;
    prom_entry( &args );
    return args.rtas_call;
}
void prom_get_rom_range( int ranges[], unsigned len, int *cnt )
{
	prom_handle_t rom;
	rom = prom_find_device( "/rom" );
	if( rom == INVALID_PROM_HANDLE )
		*cnt = 0;
	else {
		*cnt = prom_get_prop( rom, "ranges", ranges, len );
		*cnt /= sizeof(int);	
	}
}
void prom_quiesce( void )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
	} args;
	args.service = ToPointer32("quiesce");
	args.nargs = args.nret = 0;
	prom_entry( &args );
}
prom_callback_t prom_set_callback( prom_callback_t new_cb )
{
	struct {
		unsigned int service;
		int nargs;
		int nret;
		unsigned int new_cb;
		int old_cb;
	} args;
	args.service = ToPointer32("set-callback");
	args.nargs = 1;
	args.nret = 1;
	args.new_cb = ToPointer32(new_cb);
	args.old_cb = 0;
	prom_entry( &args );
	return (prom_callback_t)(word_t)args.old_cb;
}
void prom_init( word_t r5 )
{
	char path[256];
	int len;
	int temp;
	call_addr = (L4_Word32_t)r5;
	prom_chosen = prom_find_device( "/chosen" );
	if( prom_chosen == INVALID_PROM_HANDLE )
		prom_exit();
	if( prom_get_prop(prom_chosen, "stdout", &temp, 4) != 4 )
		prom_exit();
	prom_stdout = (word_t*)(word_t)temp;
	if( prom_get_prop(prom_chosen, "stdin", &temp, 4) != 4 )
		prom_fatal( "unable to open /chosen/stdin" );
	prom_stdin = (word_t*)(word_t)temp;
	if( prom_get_prop(prom_chosen, "memory", &temp, 4) != 4 )
		prom_fatal( "unable to open /chosen/memory" );
	prom_memory = (word_t*)(word_t)temp;
	prom_options = prom_find_device( "/options" );
	if( prom_options == INVALID_PROM_HANDLE )
		prom_fatal( "unable to find /options" );
	len = prom_instance_to_path( prom_memory, path, sizeof(path) );
	if( len != -1 )
		prom_memory = prom_find_device( path );
	if( (len == -1) || (prom_memory == INVALID_PROM_HANDLE) )
		prom_fatal( "unable to open the memory node" );
}
void get_prom_range( word_t *start, word_t *size )
{
	prom_handle_t rom;
	int ranges[2];
	rom = prom_find_device( "/rom" );
	if( rom == INVALID_PROM_HANDLE )
		prom_fatal( "unable to open /rom" );
	if( prom_get_prop(rom, "ranges", ranges, sizeof(ranges)) !=
			sizeof(ranges) )
		prom_fatal( "unable to read /rom/ranges" );
	*start = ranges[0];
	*size = ranges[1];
}