
/*********************************************************************
 *	compiler.c - functions for the various dsp registers. 
 *      Copyright (C) 2000 Rui Sousa 
 ********************************************************************* 
 *     This program is free software; you can redistribute it and/or 
 *     modify it under the terms of the GNU General Public License as 
 *     published by the Free Software Foundation; either version 2 of 
 *     the License, or (at your option) any later version. 
 * 
 *     This program is distributed in the hope that it will be useful, 
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 *     GNU General Public License for more details. 
 * 
 *     You should have received a copy of the GNU General Public 
 *     License along with this program; if not, write to the Free 
 *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 
 *     USA. 
 ********************************************************************* 
*/

#include<stdio.h>
#include "compiler.h"

void illegal(const char *message)
{
	fprintf(stderr, "%s ip:%d\n", message, ip);
	exit(1);
	return;
}

int is_input(int arg)
{
	if (arg >= 0x0 && arg < 0x20)
                return 1;

        return 0;
}

int is_output(int arg)
{
        if (arg >= 0x20 && arg < 0x40)
                return 1;

        return 0;
}

int is_constant(int arg)
{
        if (arg >= 0x40 && arg < 0x60)
                return 1;

        return 0;
}

int is_gpr(int arg)
{
        if (arg >= 0x100 && arg < 0x200)
                return 1;

        return 0;
}

int is_tram_line_address(int arg)
{
        if (arg >= 0x300 && arg < 0x3a0)
                return 1;

        return 0;
}

int is_tram_line_data(int arg)
{
	if (arg >= 0x200 && arg < 0x2a0)
		return 1;

	return 0;
}

int is_tram_line(int arg)
{
	if(is_tram_line_address(arg) || is_tram_line_data(arg))
		return 1;

	return 0;
}

void valid_range(int arg)
{
	if (is_gpr(arg)) {
                if (gpr_table[arg - 0x100] == NULL)
                        illegal("GPR not declared");
        } else if (is_tram_line_data(arg)) {
                if (tram_line_table[arg-0x200] == NULL)
                        illegal("TRAM LINE not declared");
        } else if (is_tram_line_address(arg)) {
		if (tram_line_table[arg-0x300] == NULL)
                        illegal("TRAM LINE not declared");
	} else if(!is_input(arg) && !is_output(arg) && !is_constant(arg))
		illegal("register out of range");

	return;
}

void read_ok(int arg)
{
	valid_range(arg);

	if (is_output(arg))
		illegal("can't read from register");

	if (is_tram_line_data(arg))
		if (tram_line_table[arg - 0x200]->usage == TRAM_LINE_USAGE_WRITE)
			illegal("can't read from TRAM DATA register");

	return;
}

void write_ok(int arg)
{
	valid_range(arg);

	if (is_input(arg))
		illegal("can't write to register");

	if (is_tram_line_data(arg))
                if (tram_line_table[arg - 0x200]->usage == TRAM_LINE_USAGE_READ)
                        illegal("can't write to TRAM DATA register");

	return;
}

void op(int op, int z, int w, int x, int y)
{
	int t, w0, w1;

	if (ip > 0x200)
		illegal("to many instructions");

	if (op >= 0x10 || op < 0x00)
		illegal("illegal op code");

	write_ok(z);
	read_ok(w);
	read_ok(x);
	read_ok(y);

	w0 = (x << 10) | y;
	w1 = (op << 20) | (z << 10) | w;
	dsp_code[ip * 2] = w0;
	dsp_code[ip * 2 + 1] = w1;
	ip++;

	return;
}

/* FX send bus (16 inputs) */

int fx(int x)
{
	if (x >= 0x010 || x < 0x00)
		illegal("invalid FX BUS");

	return (0x00 + x);
}

/* inputs */

int in(int x)
{
	if (x >= 0x20 || x < 0x00)
		illegal("invalid INPUT");

	return (0x00 + x);
}

/* outputs */

int out(int x)
{
	if (x >= 0x20 || x < 0x00)
		illegal("invalid OUTPUT");

	return (0x20 + x);
}

/* constants and hardware registers */

int cnt(int x)
{
	if (x >= 0x20 || x < 0x00)
		illegal("invalid CONSTANT");

	return (0x40 + x);
}

/* general purpose registers */

int gpr(int x)
{
	if (x >= 0x100 || x < 0x000)
		illegal("GPR out of range");

	return (0x100 + x);
}

void SET(int reg, u32 value, ...)
{
	if (is_gpr(reg)) {
		if (gpr_table[reg - 0x100] == NULL)
			illegal("GPR not declared");
		else
			gpr_table[reg - 0x100]->value = value;
	} else if (is_tram_line_data(reg)) {
		if (tram_line_table[reg - 0x200] == NULL)
			illegal("TRAM LINE not declared");
		else
			tram_line_table[reg - 0x200]->data_value = value;
	} else if (is_tram_line_address(reg)) {
		if (tram_line_table[reg - 0x300] == NULL)
			illegal("TRAM not declared");
		else
			tram_line_table[reg - 0x300]->address_value = value;
	} else
		illegal("cannot set register");

	return;
}

void DECLARE_GPR(int gpr, int usage)
{
	gpr -= 0x100;

        if (gpr >= 0x100 || gpr < 0x000)
                illegal("illegal GPR");

        if (gpr_table[gpr - 0x100] == NULL) {
                gpr_table[gpr] = (struct gpr *) malloc(sizeof(struct gpr));
                gpr_table[gpr]->usage = usage;
                gpr_table[gpr]->value = 0;
		gpr_table[gpr]->num = gpr - 0x100;

                list_add(&gpr_list, &gpr_table[gpr]->list);
        }
        else
                illegal("GPR already in use");

	return;
}

void DECLARE_GPR_DYNAMIC(int gpr, ...)
{
	DECLARE_GPR(gpr, GPR_USAGE_DYNAMIC);

	return;
}

void DECLARE_GPR_STATIC(int gpr, ...)
{
	DECLARE_GPR(gpr, GPR_USAGE_STATIC);

	return;
}

void DECLARE_GPR_INPUT(int gpr, ...)
{
	DECLARE_GPR(gpr, GPR_USAGE_INPUT);

	return;
}

void DECLARE_GPR_OUTPUT(int gpr, ...)
{
	DECLARE_GPR(gpr, GPR_USAGE_OUTPUT);

	return;
}

void DECLARE_GPR_CONTROL(int gpr, const char *name, ...)
{
	DECLARE_GPR(gpr, GPR_USAGE_CONTROL);

//	gpr_table[gpr]->name = name;

	return;
}

/* tank memory data registers */

int tank_line_data(int x)
{
	if (x >= 0xa0 || x < 0x00)
		illegal("TANK data register out of range");

	return (0x200 + x);
}

/* tank memory address registers */

int tank_line_addr(int x)
{
	if (x >= 0xa0 || x < 0x00)
		illegal("TANK address register out of range");

	return (0x300 + x);
}

int tank_line(int x)
{
	if (x >= 0xa0 || x < 0x00)
		illegal("TANK LINE out of range");

	return x;
}

void DECLARE_TRAM_BLOCK(int block, int size, int type, ...)
{
	if (size > 0x100000)
                illegal("TRAM BLOCK too big");

        if (tram_block[block].type == TRAM_BLOCK_TYPE_EMPTY)
                tram_block[block].type = type;
        else
                illegal("TRAM BLOCK already in use");

	return;
}

void DECLARE_TRAM_BLOCK_DELAYLINE(int block, int size, ...)
{
	DECLARE_TRAM_BLOCK(block, size, TRAM_BLOCK_TYPE_DELAYLINE);

	return;
}

void DECLARE_TRAM_BLOCK_LOOKUPTABLE(int block, int size, ...)
{
	DECLARE_TRAM_BLOCK(block, size, TRAM_BLOCK_TYPE_LOOKUPTABLE);

	return;
}

void DECLARE_TRAM_LINE(int line, int block, int usage, ...)
{
	if (line >= 0xa0 || line < 0x00)
                illegal("illegal TANK register");

        if (tram_line_table[line] == NULL) {
		tram_line_table[line] = (struct tram_line *) malloc(sizeof(struct tram_line));
                tram_line_table[line]->usage = usage;
		tram_line_table[line]->num = line;
		list_add(&tram_line_list, &tram_line_table[line]->list);
	}
        else
                illegal("TRAM register already in use");

        if (tram_block[block].type == TRAM_BLOCK_TYPE_EMPTY)
                illegal("illegal TRAM BLOCK");

        tram_block[block].tram_line[tram_block[block].tram_line_num++] = line;

	return;
}

void DECLARE_TRAM_LINE_WRITE(int line, int block, ...)
{
	DECLARE_TRAM_LINE(line, block, TRAM_LINE_USAGE_WRITE);

	return;
}

void DECLARE_TRAM_LINE_READ(int line, int block, ...)
{
	DECLARE_TRAM_LINE(line, block, TRAM_LINE_USAGE_READ);

	return;
}

void INC_IP(x)
{
	if ((x >= 0x000) && (ip + x < 0x200))
		ip += x;
	else
		illegal("cannot increase ip");

	return;
}
