// Based on: http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/
// and: http://wiki.osdev.org/ARM_Integrator-CP_PL110_Dirty

typedef int(*PFN)(void);

#define VEXPRESS

#define PL110_CR_EN             0x001
#define PL110_CR_16BPP          0x008
#define PL111_CR_24BPP          0x009
#define PL110_CR_MONO           0x010
#define PL110_CR_TFT            0x020
#define PL110_CR_MONO_8B        0x040
#define PL110_CR_DUAL_LCD       0x080
#define PL110_CR_BGR            0x100
#define PL110_CR_BEBO           0x200
#define PL110_CR_BEPO           0x400
#define PL110_CR_PWR            0x800

#ifdef VEXPRESS
#define PL110_IOBASE            0x10020000
#define FB_BASE                 0x60020000
#else
#define PL110_IOBASE            0xc0000000
#define FB_BASE                 0x00200000
#endif

#define PL110_PALBASE           (PL110_IOBASE + 0x200)


volatile unsigned int * const UART0_DR = (unsigned int *)0x10009000;

typedef unsigned int            uint32;
typedef unsigned char           uint8;
typedef unsigned short          uint16;

typedef struct _PL110MMIO
{
        uint32          volatile tim0;          //0
        uint32          volatile tim1;          //4
        uint32          volatile tim2;          //8
        uint32          volatile d;             //c
        uint32          volatile upbase;        //10
        uint32          volatile f;             //14
        uint32          volatile g;             //18
        uint32          volatile control;       //1c
} PL110MMIO;

typedef struct _PL111MMIO
{
        uint32          volatile tim0;          //0
        uint32          volatile tim1;          //4
        uint32          volatile tim2;          //8
        uint32          volatile tim3;          //c
        uint32          volatile upbase;        //10
        uint32          volatile lpbase;        //14
        uint32          volatile control;       //18
} PL111MMIO;

#ifdef VEXPRESS
#define PL11xMMIO PL111MMIO
#else
#define PL11xMMIO PL110MMIO
#endif

void print_uart0(const char *s) {
#ifdef VEXPRESS
 while(*s != '\0') { /* Loop until end of string */
 *UART0_DR = (unsigned int)(*s); /* Transmit char */
 s++; /* Next char */
 }
#endif
}

//void __attribute__((naked)) start(void)
void start(void)
{
        PFN             fn;
        PL11xMMIO       *plio;
        int             x;
        uint32          plid;
        uint16          volatile *fb;
        uint16          pixel;

        plio = (PL11xMMIO*)PL110_IOBASE;

        plid = *(uint32*)(PL110_IOBASE + 0xff0);

        print_uart0("Setting screen configuration\n");

        /* 640x480 pixels */
        plio->tim0 = 0x3f1f3f9c;
        plio->tim1 = 0x080b61df;
        plio->upbase = FB_BASE;

        /* 16-bit color */
        plio->control = PL110_CR_EN | PL110_CR_16BPP | PL110_CR_TFT | PL110_CR_PWR;

        fb = (uint16*)FB_BASE;
        print_uart0("Filling framebuffer\n");
        pixel = 0;
        for (x = 0; x < (640 * 480) - 10; ++x)
        {
                //fb[x] = 0x1f << (5 + 6) | 0xf << 5;
                fb[x] = pixel;
                pixel++;
        }

        print_uart0("Done\n");

        for(;;);
}

/* Also used to get it loading:

startup.s:
.global _Reset
_Reset:
 LDR sp, =stack_top
 BL start
 B .

test.ld:
ENTRY(_Reset)
SECTIONS
{
 . = 0x10000;
 .startup . : { startup.o(.text) }
 .text : { *(.text) }
 .data : { *(.data) }
 .bss : { *(.bss) }
 . = . + 0x1000; 
 stack_top = .;
}

build.sh:
TARGET=arm-linux-gnueabi
MCPU=arm926ej-s
$TARGET-as -mcpu=$MCPU -g startup.s -o startup.o
$TARGET-gcc -c -mcpu=$MCPU -marm test.c -nostdlib -o test.o
$TARGET-ld -T test.ld test.o startup.o -o test.elf
$TARGET-objcopy -O binary test.elf test.bin

run_arm.sh:
qemu-system-arm -M vexpress-a9 -m 128M -serial $(tty) -kernel $@
echo
echo qemu exitied, fixing terminal
sleep 1
reset

debug_arm.sh:
qemu-system-arm -M vexpress-a9 -m 128M -serial $(tty) -s -S -kernel $@
echo qemu exited, fixing terminal...
sleep 1
reset
*/
