/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id: bootloader.c,v 1.0 2003/07/04 08:20:30 hohensoh Exp $
 *
 * Copyright (C) 2003 by Jörg Hohensohn 
 * 
 * Second-level bootloader, with dual-boot feature by holding F1/Menu
 * This is the image being descrambled and executed by the boot ROM.
 * It's task is to copy Rockbox from Flash to DRAM.
 * The image(s) in flash may optionally be compressed with UCL 2e
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "sh7034.h"

#ifndef NULL
#define NULL ((void*)0)
#endif

// scalar types
typedef unsigned char  UINT8;
typedef unsigned short UINT16;
typedef unsigned long  UINT32;

typedef void(*tpFunc)(void); // type for execute
typedef int(*tpMain)(void); // type for start vector to main()


// resolve platform dependency of F1 button check
#if defined PLATFORM_PLAYER
#define CHANNEL 1
#define LOWER 0
#define UPPER 384
#elif defined PLATFORM_RECORDER
#define CHANNEL 4
#define LOWER 250
#define UPPER 500
#elif defined PLATFORM_FM
#define CHANNEL 4
#define LOWER 150
#define UPPER 385
#else
#error ("No platform given!")
#endif

#define FLASH_BASE 0x02000000 // start of the flash memory

// structure of an image in the flash
typedef struct 
{
	UINT32* pDestination; // address to copy it to
	UINT32 size;          // how many bytes of payload (to the next header)
	tpFunc pExecute;      // entry point
	UINT32 flags;         // uncompressed or compressed
	// end of header, now comes the payload
	UINT32 image[];       // the binary image starts here
	// after the payload, the next header may follow, all 0xFF if none
} tImage;

// flags valid for image header
#define IF_NONE   0x00000000
#define IF_UCL_2E 0x00000001 // image is compressed with UCL, algorithm 2e


// prototypes
int main(void);

// our binary has to start with a vector to the entry point
tpMain start_vector[] __attribute__ ((section (".startvector"))) = {main};


// Thinned out version of the UCL 2e decompression sourcecode
// Original (C) Markus F.X.J Oberhumer under GNU GPL license
#define GETBIT(bb, src, ilen) \
    (((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1) 

int ucl_nrv2e_decompress_8(
	const UINT8 *src, UINT8 *dst, UINT32* dst_len)
{ 
    UINT32 bb = 0;
    unsigned ilen = 0, olen = 0, last_m_off = 1;

    for (;;)
    {
        unsigned m_off, m_len;

        while (GETBIT(bb,src,ilen))
        {
            dst[olen++] = src[ilen++];
        }
        m_off = 1;
        for (;;)
        {
            m_off = m_off*2 + GETBIT(bb,src,ilen);
            if (GETBIT(bb,src,ilen)) break;
            m_off = (m_off-1)*2 + GETBIT(bb,src,ilen);
        }
        if (m_off == 2)
        {
            m_off = last_m_off;
            m_len = GETBIT(bb,src,ilen);
        }
        else
        {
            m_off = (m_off-3)*256 + src[ilen++];
            if (m_off == 0xffffffff)
                break;
            m_len = (m_off ^ 0xffffffff) & 1;
            m_off >>= 1;
            last_m_off = ++m_off;
        }
        if (m_len)
            m_len = 1 + GETBIT(bb,src,ilen);
        else if (GETBIT(bb,src,ilen))
            m_len = 3 + GETBIT(bb,src,ilen);
        else
        {
            m_len++;
            do {
                m_len = m_len*2 + GETBIT(bb,src,ilen);
            } while (!GETBIT(bb,src,ilen));
            m_len += 3;
        }
        m_len += (m_off > 0x500);
        {
            const UINT8 *m_pos;
            m_pos = dst + olen - m_off;
            dst[olen++] = *m_pos++;
            do dst[olen++] = *m_pos++; while (--m_len > 0);
        }
    }
    *dst_len = olen;
    return ilen;
}


// move the image into place and start it
void DecompressStart(tImage* pImage)
{
	UINT32* pSrc;
	UINT32* pDest;

	pSrc = pImage->image;
	pDest = pImage->pDestination;

	if (pSrc != pDest) // if not linked to that flash address
	{
		if (pImage->flags & IF_UCL_2E)
		{	// UCL compressed, algorithm 2e
			UINT32 dst_len; // dummy
			ucl_nrv2e_decompress_8((UINT8*)pSrc, (UINT8*)pDest, &dst_len);
		}
		else
		{	// uncompressed, copy it
			UINT32 size = pImage->size;
			size = (size + 3) / 4; // round up to 32bit-words

			while (size--)
			{
				*pDest++ = *pSrc++;
			}
		}
	}

	pImage->pExecute();
}

int ReadADC(int channel)
{
	// after channel 3, the ports wrap and get re-used
	volatile UINT16* pResult = (UINT16*)(ADDRAH_ADDR + 2 * (channel & 0x03)); 
	int timeout = 266; // conversion takes 266 clock cycles

	ADCSR = 0x20 | channel; // start single conversion
	while (((ADCSR & 0x80) == 0) && (--timeout)); // 6 instructions per round

	return (timeout == 0) ? -1 : *pResult>>6;
}


// This function is platform-dependent, 
//  until I figure out how to distinguish at runtime.
int IsButtonPressed(void) // return if F1/Menu is pressed
{
	int value = ReadADC(CHANNEL);

	if (value >= LOWER && value <= UPPER) // in range
	{
		return 1;
	}
	
	return 0;
}


// Determine the image to be started
tImage* GetStartImage(void)
{
	tImage* pImage1;
	tImage* pImage2 = NULL; // default to not present
	UINT32 pos;
	UINT32* pFlash = (UINT32*)FLASH_BASE;

	// determine the first image position
	pos = pFlash[2] + pFlash[3]; // position + size of the bootloader = after it
	pos = (pos + 3) & ~3; // be shure it's 32 bit aligned

	pImage1 = (tImage*)pos;

	if (pImage1->size != 0)
	{	// check for second image
		pos = (UINT32)(&pImage1->image) + pImage1->size;
		pImage2 = (tImage*)pos;

		// does it make sense? (not in FF or 00 erazed space)
		if (pImage2->pDestination == (void*)0xFFFFFFFF
		 || pImage2->size == 0xFFFFFFFF		
		 || pImage2->pExecute == (void*)0xFFFFFFFF		
		 || pImage2->flags == 0xFFFFFFFF
		 || pImage2->pDestination == NULL) // size, execute and flags can legally be 0		
		{
			pImage2 = NULL; // invalidate
		}
	}

	if (pImage2 == NULL || IsButtonPressed())
	{	// no second image or overridden: return the first
		return pImage1;
	}

	return pImage2; // return second image
}



int main(void)
{
	tImage* pImage;
	
    DCR |= 0x1000; // enable burst mode on DRAM
    BCR |= 0x2000; // activate Warp mode (simultaneous internal and external mem access)
	
	pImage = GetStartImage(); // which image

	DecompressStart(pImage); // move into place and start it

	return 0; // I guess we won't return  ;-)
}
