/*
 *	avarice - The "avarice" program.
 *	Copyright (C) 2001 Scott Finneran
 *      Copyright (C) 2002 Intel Corporation
 *	Copyright (C) 2005 Joerg Wunsch
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License Version 2
 *      as published by the Free Software Foundation.
 *
 *	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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * This file contains functions for interfacing with the JTAG box.
 *
 * $Id: jtagrun.cc,v 1.5 2005/06/20 20:35:24 joerg_wunsch Exp $
 */


#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>

#include "avarice.h"
#include "jtag.h"
#include "jtag1.h"
#include "remote.h"

unsigned long jtag1::getProgramCounter(void)
{
    uchar *response = NULL;
    uchar command[] = {'2', JTAG_EOM };
    unsigned long result = 0;

    response = doJtagCommand(command, sizeof(command), 4);

    if (response[3] != JTAG_R_OK)
	result = PC_INVALID;
    else
    {
	result = decodeAddress(response);

	result--; // returned value is PC + 1 as far as GDB is concerned

	// The JTAG box sees program memory as 16-bit wide locations. GDB
	// sees bytes. As such, double the PC value.
	result *= 2;
    }

    delete [] response;
    return result;
}

bool jtag1::setProgramCounter(unsigned long pc)
{
    uchar *response = NULL;
    uchar command[] = {'3', 0, 0, 0, JTAG_EOM };
    bool result;

    // See decoding in getProgramCounter
    encodeAddress(&command[1], pc / 2 + 1);

    response = doJtagCommand(command, sizeof(command), 1);

    result = response[0] == JTAG_R_OK;

    delete [] response;

    return result;
}

bool jtag1::resetProgram(void)
{
    return doSimpleJtagCommand('x', 1);
}

bool jtag1::interruptProgram(void)
{
    // Just ignore the returned PC. It appears to be wrong if the most
    // recent instruction was a branch.
    return doSimpleJtagCommand('F', 4);
}

bool jtag1::resumeProgram(void)
{
    return doSimpleJtagCommand('G', 0);
}

bool jtag1::jtagSingleStep(bool useHLL)
{
    return doSimpleJtagCommand('1', 1);
}

bool jtag1::jtagContinue(void)
{
    updateBreakpoints();        // download new bp configuration

    if (!doSimpleJtagCommand('G', 0))
    {
	gdbOut("Failed to continue\n");
	return true;
    }

    for (;;)
    {
	int maxfd;
	fd_set readfds;
	bool breakpoint = false, gdbInterrupt = false;

	// Now that we are "going", wait for either a response from the JTAG
	// box or a nudge from GDB.
	debugOut("Waiting for input.\n");

	// Check for input from JTAG ICE (breakpoint, sleep, info, power)
	// or gdb (user break)
	FD_ZERO (&readfds);
	FD_SET (gdbFileDescriptor, &readfds);
	FD_SET (jtagBox, &readfds);
	maxfd = jtagBox > gdbFileDescriptor ? jtagBox : gdbFileDescriptor;

	int numfds = select(maxfd + 1, &readfds, 0, 0, 0);
	unixCheck(numfds, "GDB/JTAG ICE communications failure");

	if (FD_ISSET(gdbFileDescriptor, &readfds))
	{
	    int c = getDebugChar();
	    if (c == 3) // interrupt
	    {
		debugOut("interrupted by GDB\n");
		gdbInterrupt = true;
	    }
	    else
		debugOut("Unexpected GDB input `%02x'\n", c);
	}

	// Read all extant responses (there's a small chance there could
	// be more than one)

	// Note: In case of a gdb interrupt, it's possible that we will
	// receive one of these responses before interruptProgram is 
	// called. This should not cause a problem as the normal retry
	// mechanism should eat up this response.
	// It might be cleaner to handle JTAG_R_xxx in sendJtagCommand

	// Check the JTAG ICE's message. It can indicate a breakpoint
	// which causes us to return, a sleep status change (ignored),
	// power "event" -- whatever that is (ignored), or a byte of
	// info sent by the program (currently ignored, could be used
	// for something...)
	uchar response;

        // This read shouldn't need to be a timeout_read(), but some cygwin
        // systems don't seem to honor the O_NONBLOCK flag on file
        // descriptors.
	while (timeout_read(&response, 1, 1) == 1)
	{
	    uchar buf[2];
	    int count;

	    debugOut("JTAG box sent %c", response);
	    switch (response)
	    {
	    case JTAG_R_BREAK:
		count = timeout_read(buf, 2, JTAG_RESPONSE_TIMEOUT);
		jtagCheck(count);
		check(count == 2, JTAG_CAUSE);
		breakpoint = true;
                debugOut(": Break Status Register = 0x%02x%02x\n",
                         buf[0], buf[1]);
		break;
	    case JTAG_R_INFO: case JTAG_R_SLEEP:
		// we could do something here, esp. for info
		count = timeout_read(buf, 2, JTAG_RESPONSE_TIMEOUT);
		jtagCheck(count);
		check(count == 2, JTAG_CAUSE);
                debugOut(": 0x%02, 0x%02\n", buf[0], buf[1]);
		break;
	    case JTAG_R_POWER:
		// apparently no args?
                debugOut("\n");
		break;
	    default:
		debugOut(": Unknown response\n");
		break; 
	    }
	}

	// We give priority to user interrupts
	if (gdbInterrupt)
	    return false;
	if (breakpoint)
	    return true;
    }
}

