#include <ncurses/ncurses.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <math.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#define MAP_ADDRESS (31*0x100000)  /* 1 mb less than total ram */
#define BYTE unsigned char
#define WORD unsigned short
#define LONG unsigned long
#define DR  18
#define BC  3
#define FC  6
#define UPDATE 10
#define DREGS  512
#define FREGS  512

int  mfd;

unsigned short value;

WINDOW *w1,*w2;
char *statp,tdatetime[20];

/* vars for time routines */
long t;
struct tm *tp, *localtime();

/* Demo map, 512 packed 16 bit I/0 regs, 512 32 bit float regs, 32 bit flags */
/* This would be a lot of points, but allows for counters and such.*/
/* 512 floats would be a lot of analog I/O, allows for fp math regs. */

typedef struct
{
    unsigned short     reg[512];
    float     alg[512];
    int       flags;
} LMAP;

/* This map struct would be mmap()ed onto shared memory */
/* Like this for Fred Proctor's method */


int hep = 0;
int key;
int ccols = 3;
int nregs = 512;
int mode = 0;
char rtype = 'r';


void showbitz(unsigned short s,char *t); /* forward dec */
void quit(void);
void set_outs(void);
void setup_io(void);
void close_io(void);
void read_io(void);
void write_io(void);

LMAP *mapp,*zapp;
LMAP zap;
main(int argc,char *argv[])
{
    int i,count;

    /* Let's do the nasty stuff first */
    /* We are using 1mb of ram excluded from the linux mm as shared memory */
    /* By typedef'ing our struct and mmaping it at MAP_ADDRESS we in effect */
    /* allocate it in the shared memory translated into user space. If this */
    /* seems like FM to you, don't feel bad, I had to read the kernel sources */
    /* to figure out where we actually allocate anything. hint, the compiler  */
    /* did it */

    /* Note, I should check if there is a way to get and drop perms for */
    /* /dev/mem without running as root or chmod'ing /dev/mem. I don't  */
    /* think ioperm() covers this. (security) */

    if(( mfd = open("/dev/mem",O_RDWR)) < 0)
    {
        perror("dev/mem open failed");
        exit(1);
    }
    mapp = (LMAP *) mmap( 0,sizeof(LMAP),PROT_READ | PROT_WRITE,MAP_SHARED,mfd,MAP_ADDRESS);
    if(MAP_FAILED == mmap)
    {
        perror("mmap failed");
        exit(1);
    }
    close(mfd);

    /* We should now have a shared memory map with no fuss. The code above */
    /* should be copied into all the map users. The declarations will go   */
    /* into a common header file for abstraction.                          */
    zapp = &zap ;


    initscr();
    start_color();
    init_pair(1,COLOR_WHITE,COLOR_BLUE);
    init_pair(2,COLOR_WHITE,COLOR_RED);
    init_pair(3,COLOR_BLUE,COLOR_GREEN);
    init_pair(4,COLOR_RED,COLOR_BLACK);
    init_pair(5,COLOR_BLACK,COLOR_RED);
    bkgdset(COLOR_PAIR(1) |  ' ');
    bkgd(COLOR_PAIR(1) |  ' ');
    attron(COLOR_PAIR(1));
    keypad(stdscr,TRUE);

    w1 = newwin(18,7,4,1);
    wbkgdset(w1 , COLOR_PAIR(1) | ' ');
    wbkgd(w1 , COLOR_PAIR(1) | ' ');
    w2 = newwin(18,70,4,9);
    wbkgdset(w2 , COLOR_PAIR(1) | ' ');
    wbkgd(w2 , COLOR_PAIR(1) | ' ');

    while( TRUE )
    {
        form();
        halfdelay(UPDATE);
        key = getch();
        switch(key)
        {
        case 'q':  quit();
            break;
        case 's': set_outs();
            break;
        case 'b':
            {
                rtype = 'b';
                ccols = BC;
                mode = 0 ;
                nregs = DREGS;
            }
            break;
        case 'f':
            {
                rtype = 'f';
                ccols = FC;
                mode = 1;
                nregs = FREGS;
            }
            break;
        case 'd':
            {
                rtype = 'd';
                ccols = FC;
                mode = 2;
                nregs = DREGS;
            }
            break;
        case 'h':
            {
                rtype = 'h';
                ccols = FC;
                mode = 3;
                nregs = DREGS;
            }
            break;
        case KEY_DOWN:
            {
                hep += DR * ccols;
                if(hep > nregs - DR*ccols) hep = nregs - DR*ccols;
            }
            break;
        case KEY_UP:
            {
                hep -= DR * ccols;
                if(hep < 0) hep = 0;
            }
            break;
        default :
        }
    }
    endwin();

}
form()
{
    int i,c;
    char str[17];
    char lab[7];
    time(&t);
    tp = localtime(&t);
    sprintf(tdatetime,"%02d/%02d %02d:%02d:%02d",tp->tm_mon +1 ,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
    clear();
    wclear(w1);
    wclear(w2);
    curs_set(0);
    noecho();
    box(stdscr,ACS_VLINE,ACS_HLINE);
    mvaddstr(1,2,"LPLC I/O Map Display");
    mvaddstr(1,60,tdatetime);
    mvaddch(2,0,ACS_LTEE);
    mvaddch(2,79,ACS_RTEE);
    move(2,1);
    hline(ACS_HLINE,78);
    if(mode == 0) mvaddstr(3,1," REG       7654321076543210       7654321076543210       7654321076543210");
    if(mode == 0) mvaddstr(1,23,", Binary Bus Order MSB--LSB 2 <-0 ");
    if(mode == 1) mvaddstr(1,23,", Float Registers                 ");
    if(mode == 2) mvaddstr(1,23,", Digital Registers in Decimal    ");
    if(mode == 3) mvaddstr(1,23,", Digital Registers in Hexidecimal");
    mvaddch(22,0,ACS_LTEE);
    mvaddch(22,79,ACS_RTEE);
    move(22,1);
    hline(ACS_HLINE,78);
    for(i = 0 ; i <= DR  ; i++)
    {
        sprintf(lab,"%c%04d",rtype,hep + i * ccols);
        mvwaddstr(w1,i,1,lab);
    }
    while (mapp->flags != 0);  /* wait for IO to finish */
    mapp->flags = 1;           /* set in use flag       */
    *zapp = *mapp ;            /* copy struct (snapshot)*/
    mapp->flags = 0;           /* reset in use flag     */

    for(i = 0 ; i <= DR ; i++)
        for(c = 0 ; c < ccols  ; c++)
        {
            if(mode == 0) showbitz(zapp->reg[hep + i * ccols + c],str);
            if(mode == 1) sprintf(str,"%+g",zapp->alg[hep + i * ccols + c]);
            if(mode == 2) sprintf(str,"%d",zapp->reg[hep + i * ccols + c]);
            if(mode == 3) sprintf(str,"%04x",zapp->reg[hep + i * ccols + c]);
            if(mode == 0) mvwaddstr(w2,i,(ccols -  c - 1) *(70/ccols) + 3,str);
            else mvwaddstr(w2,i, c *(70/ccols) + 3,str);
        }
    refresh();
    wrefresh(w1);
    wrefresh(w2);

}
void set_outs()
{
    char ans[20];
    int type = 0;
    int reg = 0;
    unsigned int val = 0;
    curs_set(1);
    echo();
    mvaddstr(23,2,"Digital or Analog ? : (d/a) :");
    refresh();
    mvgetstr(23,33,ans);
    if(ans[0] == 'a') type = 1;
    refresh();
    mvaddstr(23,2,"Register?:                        ");
    refresh();
    mvgetstr(23,12,ans);
    reg = atoi(ans);
    if(type) mvaddstr(23,2,"Value? (float):                     ");
    else mvaddstr(23,2,"Value? (hex):                     ");
    mvgetstr(23,16,ans);
    sscanf(ans,"%x",&val);
    while(mapp->flags != 0); /* wait til io is done  */
    mapp->flags = 1;         /* set in use flag      */
    *zapp = *mapp;           /* copy shared to local */
    if(type) zapp->alg[reg] = atof(ans);
    else zapp->reg[reg] = (unsigned short) val;
    *mapp = *zapp;           /* copy local to shared */
    mapp->flags = 0;         /* reset in use flag    */


}

void showbitz(unsigned short s,char *t)
{
    int i;
    for( i = 0 ; i < 16 ; i++)
    {
        if(s & (0X01 << i)) t[15 - i] = '1' ;
        else t[15 - i] = '0';
    }
    t[16] = '\0';
}
void quit(void)
{
    endwin();
    exit(1);
}
