// some experimental code on a noname STM32F103C8T6 board
// 
#include "mbed.h"
#include "SWO.h"

#define TEXT_RAM    0x1000
#define GRAPH_RAM   0x0000

// graphic controller software commands
#define SCREEN_OFF                  0x00
#define SCREEN1_ON                  0x01
#define SCREEN2_ON                  0x02
#define SCREEN1AND2_ON              0x03

#define AUTO_INCREMENT_CURSOR_ON    0x04
#define AUTO_INCREMENT_CURSOR_OFF   0x05

#define CHARACTER_SCREEN2           0x06
#define GRAPHIC_SCREEN2             0x07

#define DATA_WRITE                  0x08
#define DATA_READ                   0x09

#define ADDRESS_SCREEN1_LOW         0x0a
#define ADDRESS_SCREEN1_HIGH        0x0b
#define ADDRESS_SCREEN2_LOW         0x0c
#define ADDRESS_SCREEN2_HIGH        0x0d

#define CURSOR_ADDRESS_LOW          0x0e
#define CURSOR_ADDRESS_HIGH         0x0f

#define SCREEN_MERGE_OR             0x10
#define SCREEN_MERGE_AND            0x11
#define SCREEN_MERGE_XOR            0x12

#define SCREEN_LUMINANCE_100        0x18
#define SCREEN_LUMINANCE_85         0x19
#define SCREEN_LUMINANCE_75         0x1a
#define SCREEN_LUMINANCE_62         0x1b

DigitalOut CnD(PB_13);
DigitalOut Wr(PB_12);
DigitalOut Cs(PB_14);

DigitalOut myLED(PC_13);

// setup an 8 bit bus for reading and writing the latch
// BusOut mybus (p30,p29,p8,p7,p6,p5,p28,p27);
BusOut mybus (PA_0,PA_1,PA_2,PA_3,PA_4,PA_5,PA_6,PA_7);

SWO_Channel SWO;

// framebuffer copy
volatile unsigned char screen2[8192];

static char mysprite[8]= {
    0x7e,
    0xff,
    0xeb,
    0xfd,
    0xfd,
    0xeb,
    0xff,
    0x7e
};

static char empty[8]= {
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00
};

void VFD_write_command(unsigned char b)
{
    CnD=1;
    wait_us(1);
    Cs=0;
    Wr=0;
    mybus=b;
    wait_us(2);
    Wr=1;
    //wait_us(1);
    Cs=1;
    wait_us(1);
}

void VFD_write_data(unsigned char b)
{
    CnD=0;
    wait_us(1);
    Cs=0;
    Wr=0;
    mybus=b;
    wait_us(2);
    Wr=1;
//    wait_us(1);
    Cs=1;
    wait_us(1);
}

void VFD_cls()
{
    int t;
    VFD_write_command(CURSOR_ADDRESS_LOW);      // set lower address of cursor #1
    VFD_write_data(0x00);                       // reset to 0
    VFD_write_command(CURSOR_ADDRESS_HIGH);     // set high address of cursor #1
    VFD_write_data(0x00);                       // reset to 0

    VFD_write_command(DATA_WRITE);    // data write mode

    for (t=0; t<8192; t++) {
        VFD_write_data(0x00);       // reset to 0
        screen2[t]=0x00;
    }
}


void VFD_plot(int x,int y)
{
    int ad;
    ad=GRAPH_RAM+(x<<3)+(y>>3);                 // calculate address ad = GRAPH_RAM+(x*8)+(y/8)
    screen2[ad]|=128>>(y & 7);                  // write pixel in shadow screen

    VFD_write_command(CURSOR_ADDRESS_LOW);      // set lower address of cursor #1
    VFD_write_data(ad & 0xff);
    VFD_write_command(CURSOR_ADDRESS_HIGH);     // set high address of cursor #1
    VFD_write_data(ad >> 8);
    VFD_write_command(DATA_WRITE);              // data write mode
    VFD_write_data(screen2[ad]);
}

void VFD_unplot(int x,int y)
{
    int ad;
    ad=GRAPH_RAM+(x<<3)+(y>>3);                 // calculate address ad = GRAPH_RAM+(x*8)+(y/8)
    screen2[ad]&=~(128>>(y & 7));               // clear pixel in shadow screen

    VFD_write_command(CURSOR_ADDRESS_LOW);      // set lower address of cursor #1
    VFD_write_data(ad & 0xff);
    VFD_write_command(CURSOR_ADDRESS_HIGH);     // set high address of cursor #1
    VFD_write_data(ad >> 8);
    VFD_write_command(DATA_WRITE);              // data write mode
    VFD_write_data(screen2[ad]);
}

void VFD_putchar(int x,int y, unsigned char c)
{
    int ad=TEXT_RAM+(y<<7)+x;                   // calculate address as TEXT_RAM+(y*128)+x
    VFD_write_command(CURSOR_ADDRESS_LOW);      // select lower address of cursor #1
    VFD_write_data(ad & 0xff);
    VFD_write_command(CURSOR_ADDRESS_HIGH);     // select high address of cursor #1
    VFD_write_data(ad >> 8);
    VFD_write_command(DATA_WRITE);              // data write mode
    screen2[ad]=c;
    VFD_write_data(c);
}

void VFD_putstring(int x, int y, char *string)
{
    int ad=TEXT_RAM+(y<<7)+x;                   // calculate address as TEXT_RAM+(y*128)+x
    VFD_write_command(CURSOR_ADDRESS_LOW);      // select lower address of cursor #1
    VFD_write_data(ad & 0xff);
    VFD_write_command(CURSOR_ADDRESS_HIGH);     // select high address of cursor #1
    VFD_write_data(ad >> 8);
    VFD_write_command(DATA_WRITE);              // data write mode

    while (*string) VFD_write_data(*string++);  // write string to display until NULL found
}

void VFD_putsprite(int x, int y, char *sprite)
{
    int ad;
    int d;
    int m;
    int n;

    ad=GRAPH_RAM+(x<<3)+(y>>3);                     // calculate address ad = GRAPH_RAM+(x*8)+(y/8)

    for (n=0; n<8; n++) {                           // 8 columns to go
        m=0xff;                                     // preset mask
        d=*(sprite+n)<<(7-(y&7));                   // get sprite data and shift it
        m<<=(7-(y&7));                              // shift mask

        VFD_write_command(CURSOR_ADDRESS_LOW);      // set lower address of cursor #1
        VFD_write_data(ad & 0xff);
        VFD_write_command(CURSOR_ADDRESS_HIGH);     // set high address of cursor #1
        VFD_write_data(ad >> 8);
        VFD_write_command(DATA_WRITE);              // data write mode
        screen2[ad]&=~(m >> 8);                     // mask off sprite
        screen2[ad]|=(d >> 8);                      // OR in sprite data

        VFD_write_data(screen2[ad]);                // write this byte

        ad++;

        screen2[ad]&=~(m & 0xff);                   // mask off sprite
        screen2[ad]|=(d & 0xff);                    // OR in sprite data
        VFD_write_data(screen2[ad]);                // write this byte
        ad+=7;                                      // advance to next column
    }

}

int main()
{
    int t,n;
    int x,y;
    int dx,dy;

    x=1;
    y=1;
    dx=1;
    dy=1;

    SWO.printf("\r\nHello World from SWO\r\n");
    SWO.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock);
  
    CnD=1;                                          // set Command/Data to command mode
    Wr=1;                                           // release write line
    mybus=0;                                        // clear bus bits

    wait_us(10);                                    // wait a bit

    VFD_write_command(ADDRESS_SCREEN1_LOW);         // select lower address of display #1
    VFD_write_data(GRAPH_RAM & 0xff);               // set to low byte of graphic ram space
    VFD_write_command(ADDRESS_SCREEN1_HIGH);        // select high address of display #1
    VFD_write_data(GRAPH_RAM >> 8);                 // set to high byte of graphic ram space

    VFD_write_command(ADDRESS_SCREEN2_LOW);         // select lower address of display #2
    VFD_write_data(TEXT_RAM & 0xff);                // set to low  byte of text ram space
    VFD_write_command(ADDRESS_SCREEN2_HIGH);        // select high address of display #2
    VFD_write_data(TEXT_RAM >> 8);                  // set to high byte of text ram space

    VFD_write_command(SCREEN_MERGE_OR);             // OR screen #1 with screen #2
    VFD_write_command(CHARACTER_SCREEN2);           // set display #2 to text mode

    VFD_cls();                                      // clear the screen


    for (n=1; n<7; n++) VFD_putstring(n,n,"Hello world STM32F103C8T6 (c)KGE2015!");



    // plot some points on screen #1
    for (t=0; t<64; t++) {
        VFD_plot(0,t);
        VFD_plot(255,t);
    }

    for (t=0; t<255; t++) {
        VFD_plot(t,0);
        VFD_plot(t,63);
    }

//    for (t=0; t<256; t++) VFD_plot(t,sin((float)t/40.75)*-31+32);
//    for (t=0; t<256; t++) VFD_plot(t,cos((float)t/12)*-20+32);

  

    VFD_write_command(SCREEN1AND2_ON);    // show screen #1 + #2

//    for (n=0; n<16; n++)
//    VFD_putsprite(128+n*10,n,mysprite);

    // bounce something on the graphic screen
    while (1) {

        VFD_putsprite(x,y,mysprite);
        wait_ms(10);
        VFD_putsprite(x,y,empty);
        x+=dx;
        y+=dy;
        if ((x>246) || (x<2))
        {
            dx=-dx;
            myLED=!myLED;
            SWO.printf("Bounce X\n");
        }
        if ((y>53) || (y<1))
        {
            dy=-dy;
            myLED=!myLED;
            SWO.printf("Bounce Y\n");
        }


    }
}
