/*
 * Copyright (C) 2001-2004 by egnite Software GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 *
 */

/*
 * $Log$
 * Revision 1.6  2008/08/11 06:59:07  haraldkipp
 * BSD types replaced by stdint types (feature request #1282721).
 *
 * Revision 1.5  2006/10/08 16:48:07  haraldkipp
 * Documentation fixed
 *
 * Revision 1.4  2006/04/07 12:08:18  haraldkipp
 * Compiler warning fixed.
 *
 * Revision 1.3  2005/10/24 17:59:19  haraldkipp
 * Use correct header file, arm, not gba.
 *
 * Revision 1.2  2005/08/02 17:46:45  haraldkipp
 * Major API documentation update.
 *
 * Revision 1.1  2005/07/26 18:02:26  haraldkipp
 * Moved from dev.
 *
 * Revision 1.3  2005/04/05 17:50:46  haraldkipp
 * Use register names in gba.h.
 *
 * Revision 1.2  2004/11/08 18:11:49  haraldkipp
 * Support for carriage return added.
 *
 * Revision 1.1  2004/10/03 18:39:12  haraldkipp
 * GBA debug output on screen
 *
 */

#include <dev/debug.h>
#include <arch/arm.h>

#include <sys/device.h>
#include <sys/file.h>

/*!
 * \addtogroup xgDevDebugGba
 */
/*@{*/

#define PALRAM_BASE     0x05000000
#define VIDRAM_BASE     0x06000000

#define LCD_COLS    30  /* Visible display columns */
#define LCD_ROWS    20  /* Visible display rows */

#define LCD_MCOLS   32  /* Buffer memory columns */
#define LCD_MROWS   64  /* Buffer memory rows */

static uint16_t pos_x;       /* Current column */
static uint16_t pos_y;       /* Current row */
static uint16_t pos_vofs;    /* Vertical offset */

static NUTFILE dbgfile;

/* 8x8 character font, ASCII 32..127. */
static uint8_t font8x8[]= {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00,
    0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x28, 0x28, 0xFE, 0x28, 0xFE, 0x28, 0x28, 0x00,
    0x10, 0x7C, 0x80, 0x7C, 0x02, 0x7C, 0x10, 0x00,
    0xC2, 0xC4, 0x08, 0x10, 0x20, 0x46, 0x86, 0x00,
    0x60, 0x90, 0x60, 0x90, 0x88, 0x84, 0x7A, 0x00,
    0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00,
    0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00,
    0x44, 0x28, 0x10, 0xFE, 0x10, 0x28, 0x44, 0x00,
    0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0x10, 0x00,
    0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
    0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00,
    0x7C, 0x86, 0x8A, 0x92, 0xA2, 0xC2, 0x7C, 0x00,
    0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00,
    0x7C, 0x82, 0x04, 0x08, 0x10, 0x20, 0xFE, 0x00,
    0x7C, 0x82, 0x02, 0x3C, 0x02, 0x82, 0x7C, 0x00,
    0x40, 0x40, 0x84, 0x84, 0xFE, 0x04, 0x04, 0x00,
    0xFE, 0x80, 0x80, 0xFC, 0x02, 0x82, 0x7C, 0x00,
    0x10, 0x20, 0x40, 0xFC, 0x82, 0x82, 0x7C, 0x00,
    0xFE, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00,
    0x7C, 0x82, 0x82, 0x7C, 0x82, 0x82, 0x7C, 0x00,
    0x7C, 0x82, 0x82, 0x7E, 0x04, 0x08, 0x10, 0x00,
    0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00,
    0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00,
    0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00,
    0x40, 0x20, 0x10, 0x08, 0x10, 0x20, 0x40, 0x00,
    0x7C, 0x82, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00,
    0x7C, 0x82, 0xBE, 0xA2, 0xBE, 0x80, 0x7E, 0x00,
    0x10, 0x28, 0x44, 0x82, 0xFE, 0x82, 0x82, 0x00,
    0xFC, 0x82, 0x82, 0xFC, 0x82, 0x82, 0xFC, 0x00,
    0x7C, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7C, 0x00,
    0xFC, 0x82, 0x82, 0x82, 0x82, 0x82, 0xFC, 0x00,
    0xFE, 0x80, 0x80, 0xFC, 0x80, 0x80, 0xFE, 0x00,
    0xFE, 0x80, 0x80, 0xFC, 0x80, 0x80, 0x80, 0x00,
    0x7C, 0x82, 0x80, 0x8E, 0x82, 0x82, 0x7E, 0x00,
    0x82, 0x82, 0x82, 0xFE, 0x82, 0x82, 0x82, 0x00,
    0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x82, 0x7C, 0x00,
    0x82, 0x8C, 0xB0, 0xC0, 0xB0, 0x8C, 0x82, 0x00,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFE, 0x00,
    0x82, 0xC6, 0xAA, 0x92, 0x82, 0x82, 0x82, 0x00,
    0x82, 0xC2, 0xA2, 0x92, 0x8A, 0x86, 0x82, 0x00,
    0x7C, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7C, 0x00,
    0xFC, 0x82, 0x82, 0xFC, 0x80, 0x80, 0x80, 0x00,
    0x7C, 0x82, 0x82, 0x82, 0x82, 0x84, 0x7A, 0x00,
    0xFC, 0x82, 0x82, 0xFC, 0x88, 0x84, 0x82, 0x00,
    0x7C, 0x82, 0x80, 0x7C, 0x02, 0x82, 0x7C, 0x00,
    0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00,
    0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7C, 0x00,
    0x82, 0x82, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00,
    0x82, 0x82, 0x82, 0x92, 0x92, 0x54, 0x28, 0x00,
    0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x00,
    0x82, 0x82, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00,
    0xFE, 0x04, 0x08, 0x10, 0x20, 0x40, 0xFE, 0x00,
    0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00,
    0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00,
    0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00,
    0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x7E, 0x82, 0x82, 0x86, 0x7A, 0x00,
    0x80, 0x80, 0xFC, 0x82, 0x82, 0x82, 0xFC, 0x00,
    0x00, 0x00, 0x7E, 0x80, 0x80, 0x80, 0x7E, 0x00,
    0x02, 0x02, 0x7E, 0x82, 0x82, 0x82, 0x7E, 0x00,
    0x00, 0x00, 0x7C, 0x82, 0xFE, 0x80, 0x7C, 0x00,
    0x0E, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x00,
    0x00, 0x00, 0x7E, 0x82, 0x7E, 0x02, 0x7C, 0x00,
    0x80, 0x80, 0xFC, 0x82, 0x82, 0x82, 0x82, 0x00,
    0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00,
    0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x70, 0x00,
    0x80, 0x80, 0x86, 0x98, 0xE0, 0x98, 0x86, 0x00,
    0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00,
    0x00, 0x00, 0xEC, 0x92, 0x92, 0x92, 0x82, 0x00,
    0x00, 0x00, 0xBC, 0xC2, 0x82, 0x82, 0x82, 0x00,
    0x00, 0x00, 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00,
    0x00, 0x00, 0xFC, 0x82, 0xFC, 0x80, 0x80, 0x00,
    0x00, 0x00, 0x7E, 0x82, 0x7E, 0x02, 0x02, 0x00,
    0x00, 0x00, 0xBE, 0xC0, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x00, 0x7E, 0x80, 0x7C, 0x02, 0xFC, 0x00,
    0x10, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x0C, 0x00,
    0x00, 0x00, 0x82, 0x82, 0x82, 0x86, 0x7A, 0x00,
    0x00, 0x00, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00,
    0x00, 0x00, 0x92, 0x92, 0x92, 0x92, 0x6C, 0x00,
    0x00, 0x00, 0x82, 0x44, 0x38, 0x44, 0x82, 0x00,
    0x00, 0x00, 0x82, 0x82, 0x7E, 0x02, 0x7C, 0x00,
    0x00, 0x00, 0xFE, 0x0C, 0x30, 0xC0, 0xFE, 0x00,
    0x0C, 0x10, 0x10, 0x20, 0x10, 0x10, 0x0C, 0x00,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00,
    0x60, 0x10, 0x10, 0x08, 0x10, 0x10, 0x60, 0x00,
    0x34, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/*!
 * \brief Handle I/O controls for debug device 0.
 *
 * The debug device doesn't support any.
 *
 * \return Always -1.
 */
int DebugIOCtl(NUTDEVICE * dev, int req, void *conf)
{
    return -1;
}

/*!
 * \brief Initialize debug device 0.
 *
 * \return Always 0.
 */
int DebugInit(NUTDEVICE * dev)
{
    register uint8_t ch;
    uint_fast16_t i;
    uint_fast16_t j;
    uint_fast16_t k;
    uint16_t *pal;
    uint16_t *vid;

    outw(REG_DISPCNT, 0x0400);
    outw(REG_BG2CNT, 0xA880);

    pal = (uint16_t *)PALRAM_BASE;
    for (i = 1; i < 256; i++) {
        pal[i] = 0x7FFF;
    }
    pal[0] = 0;

    vid = (uint16_t *)VIDRAM_BASE + 1024;
    for(i = 0; i < 96; i++) {
        for (j = 0; j < 8; j++) {
            ch = font8x8[i * 8 + j];
            for(k = 0; k < 4; k++) {
                *vid++ = ((ch & 0x80) ? 1 : 0) | ((ch & 0x40) ? 0x0100 : 0);
                ch *= 4;
            }
        }
    }

    return 0;
}

/*!
 * \brief Send a single character to debug device 0.
 *
 */
static void DebugPut(char ch)
{
    uint8_t i;
    uint16_t *vid = (uint16_t *)(VIDRAM_BASE + 0x4000);
    uint16_t pos = 0;

    if (ch == '\r') {
        pos_x = 0;
    }
    else if (pos_x == LCD_COLS || ch == '\n') {
        pos_x = 0;
        pos_y++;
        if ((pos_y - pos_vofs) == LCD_ROWS) {
            pos_vofs++;
            outw(REG_BG2VOFS, (pos_vofs & (LCD_MROWS - 1)) * 8);
            if (pos_vofs >= LCD_MROWS && pos_y >= LCD_MROWS) {
                pos_vofs -= LCD_MROWS;
                pos_y -= LCD_MROWS;
            }
        }
        pos = (pos_y & (LCD_MROWS - 1)) * LCD_MCOLS;
        for(i = 0; i < LCD_COLS; i++) {
            vid[pos + i] = 32;
        }
    }
    else {
        pos = (pos_y & (LCD_MROWS - 1)) * LCD_MCOLS;
    }
    if (ch >= 32 && ch <= 127) {
        vid[pos + pos_x++] = ch;
    }
}

/*!
 * \brief Send characters to debug device 0.
 *
 * A carriage return character will be automatically appended
 * to any linefeed.
 *
 * \return Number of characters sent.
 */
int DebugWrite(NUTFILE * fp, const void *buffer, int len)
{
    int c = len;
    const char *cp = buffer;

    while(c--) {
        DebugPut(*cp++);
    }
    return len;
}

/*!
 * \brief Open debug device 0.
 *
 * \return Pointer to a static NUTFILE structure.
 */
NUTFILE *DebugOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
{
    dbgfile.nf_dev = dev;
    dbgfile.nf_fcb = NULL;

    return &dbgfile;
}

/*!
 * \brief Close debug device 0.
 *
 * \return Always 0.
 */
int DebugClose(NUTFILE * fp)
{
    return 0;
}

/*!
 * \brief Debug device 0 information structure.
 */
NUTDEVICE devDebug0 = {
    0,                          /*!< Pointer to next device, dev_next. */
    {'c', 'o', 'n', 0, 0, 0, 0, 0, 0},      /*!< Unique device name, dev_name. */
    0,                          /*!< Type of device, dev_type. */
    0,                          /*!< Base address, dev_base. */
    0,                          /*!< First interrupt number, dev_irq. */
    0,                          /*!< Interface control block, dev_icb. */
    0,                          /*!< Driver control block, dev_dcb. */
    DebugInit,                  /*!< Driver initialization routine, dev_init. */
    DebugIOCtl,                 /*!< Driver specific control function, dev_ioctl. */
    0,                          /*!< dev_read. */
    DebugWrite,                 /*!< dev_write. */
    DebugOpen,                  /*!< dev_opem. */
    DebugClose,                 /*!< dev_close. */
    0,                          /*!< dev_size. */
    0,                          /*!< Select function, optional, not yet implemented */
};

/*@}*/
