#include "mbed.h"

#define BUF_SIZE 16384

DigitalOut led1(LED1);

BusInOut data_bus(p13, p14, p15, p16, p17, p18, p19, p20);
DigitalOut n_bus_en(p12);
DigitalOut fd_sel(p21);
DigitalOut n_reset(p23);
DigitalOut n_cs(p24);
DigitalOut rw_en(p25);
DigitalOut rw(p26);
BusOut address_bus(p27, p28);
DigitalIn drq(p29);
DigitalIn intrq(p30);

Serial pc(USBTX, USBRX); 

uint8_t data_buf[BUF_SIZE] __attribute__((section("AHBSRAM0"))); 
volatile uint16_t buf_fill = 0;
volatile uint8_t buf_dir; //0: increment (write into buffer), 1: decrement (read from buffer)
volatile uint8_t buf_stat = 0;


uint8_t read_data(uint8_t address)
{    
    uint8_t data;

    //RW = 1 --> read
    //also sets transceiver direction
    rw = 1;
    
    //enable transceiver
    data_bus.input();
    n_bus_en = 0;
    
    //put address on bus
    address_bus = address;
    wait_us(1);
    
    //apply read pulse
    rw_en = 1;
    wait_us(1);
    
    //read data
    data = ~data_bus;
    
    //remove read pulse
    rw_en = 0;
    
    //disable transceiver
    data_bus.input();
    n_bus_en = 1;
    
    return data;
}

void write_data(uint8_t address, uint8_t data)
{
    //RW = 0 --> write
    //also sets transceiver direction
    rw = 0;
    
    //enable transceiver
    data_bus.output();
    n_bus_en = 0;
    
    //put address on bus
    address_bus = address;
    //wait_us(1);

    //put data on bus
    data_bus = ~data;
    wait_us(1);
    
    //apply write pulse
    rw_en = 1;
    wait_us(1);
    
    //remove write pulse
    rw_en = 0;
    
    //disable transceiver
    data_bus.input();
    n_bus_en = 1;
}

int main() 
{
    
    pc.baud(115200);
    rw_en = 0;
    n_bus_en = 1;
    n_reset = 0;
    n_cs = 1;
    rw = 1;
    fd_sel = 0;
    data_bus.input();
    
   
    uint8_t op;
    uint8_t buf[4];
    
    
    
    while(1) 
    {
            /*
            pc.printf("Select mode operation: \r\n");
            pc.printf("1 ... n_reset = 1 \r\n");
            pc.printf("2 ... n_reset = 0 \r\n");
            pc.printf("3 ... n_cs = 1 \r\n");
            pc.printf("4 ... n_cs = 0 \r\n");
            pc.printf("5 ... fd_sel = 1 \r\n");
            pc.printf("6 ... fd_sel = 0 \r\n");
            pc.printf("w ... write data \r\n");
            pc.printf("r ... read data \r\n");
            pc.printf("i ... add byte to buffer (and set to read from buffer)\r\n");
            pc.printf("o ... read buffer \r\n");
            pc.printf("c ... clear buffer (and set to write to buffer)\r\n\r\n");
            
            
            pc.printf("n_reset = %X\r\n", n_reset.read());
            pc.printf("n_cs = %X\r\n", n_cs.read());
            pc.printf("fd_sel = %X\r\n", fd_sel.read());
            pc.printf("buf_fill = %d\r\n", buf_fill);
            pc.printf("buf_dir = %X\r\n", buf_dir);
            pc.printf("buf_stat = %X\r\n\r\n", buf_stat);
            */
            
            op = pc.getc();   
            
            if(op == '1')
            {
                n_reset = 1;
            }
            
            if(op == '2')
            {
                n_reset = 0;
            }
            
            if(op == '3')
            {
                n_cs = 1;
            }
            
            if(op == '4')
            {
                n_cs = 0;
            }
            
            if(op == '5')
            {
                fd_sel = 1;
            }
            
            if(op == '6')
            {
                fd_sel = 0;
            }

            if(op == 'w')
            {
                //pc.printf("Enter AA DD: "); 
                pc.scanf("%x %x", &buf[0], &buf[1]);
                //pc.printf("\r\nwriting 0x%02X to 0x%02X\r\n", buf[1], buf[0]);   
                write_data(buf[0], buf[1]);
            }
            
            if(op == 'r')
            {
                //pc.printf("Enter AA: "); 
                pc.scanf("%x", &buf[0]);
                buf[1] = read_data(buf[0]);
                //pc.printf("\r\nread 0x%02X from 0x%02X\r\n", buf[1], buf[0]);   
                pc.printf("0x%02X\r\n", buf[1]);   
                
            }
            
            if(op == 'i')
            {
                //pc.printf("Enter DD: "); 
                pc.scanf("%x", &data_buf[buf_fill]);
                
                buf_dir = 1;
                
                if(buf_fill == (BUF_SIZE - 1))
                {
                    buf_stat |= 0x01;   
                }
                else
                {
                    buf_fill++;   
                }
            }
            
            if(op == 'o')
            {
                for(uint16_t i = 0; i < buf_fill; i++)
                {
                    pc.printf("%02X ", data_buf[i]);    
                }    
                pc.printf("\r\n");
            }
            
            if(op == 'c')
            {
                buf_fill = 0;
                buf_stat = 0;
                buf_dir = 0;   
            }
            
            if(op == 'R')
            {
                led1 = 1;
                write_data(0x00, 0xE4);
                
                //wait for intrq
                while(!intrq)
                {
                    if(drq)
                    {
                        data_buf[buf_fill] = read_data(0x03);
                        buf_fill++;  
                    }
                }

                led1 = 0;
                pc.printf("%d\r\n", buf_fill);
            }
            
            if(op == 'Q')
            {
                led1 = 1;
                write_data(0x00, 0x84);
                
                //wait for intrq
                while(!intrq)
                {
                    if(drq)
                    {
                        data_buf[buf_fill] = read_data(0x03);
                        buf_fill++;  
                    }
                }

                led1 = 0;
                pc.printf("%d\r\n", buf_fill);
            }
            
            if(op == 'W')
            {
                uint16_t i = 0;
                led1 = 1;
                
                write_data(0x00, 0xF4);
                
                //wait for intrq
                while(!intrq)
                {
                    if(drq)
                    {
                        write_data(0x03, data_buf[i]);    
                        i++;
                    }
                }
                
                led1 = 0;
                pc.printf("%d\r\n", i);
            }
            
            if(op == 'P')
            {
                uint16_t i = 0;
                led1 = 1;
                
                write_data(0x00, 0xA4);
                
                //wait for intrq
                while(!intrq)
                {
                    if(drq)
                    {
                        write_data(0x03, data_buf[i]);
                        i++;
                    }
                }
                
                led1 = 0;
                pc.printf("%d\r\n", i);
            }
            

    }
}
