/*  Filename     MKIT4.C v3.0 ???
    Description  MKIT-4 Monitor Program
    Hardware     MKIT-4
    MCU			 89S52 
    Clock        11.0592 Mhz
    Compiler     Keil PK51 v7.10
    Engineer     Kriangsak B.
    Company      Sila Research Co.,Ltd. 
*/

#include <reg52.h>
#include <absacc.h>
#include <assert.h>
#include <ctype.h>
#include <intrins.h>
#include <math.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define  MAXBUF    35        // maximun for combuf (not include ':' & 0xd)
#define  CORRECT   5         // correct msec
#define  INPUT     P1        // input (switch on board = 4)
#define  ADDRB     P0        // sound chip address A0-A5 (2590=A0-A7)

/********** I/O PORT **********/

sbit     KREC      = P1^7;   // record key
sbit     KEND      = P1^0;   // end key
sbit     LED       = P2^0;   // led monitor

                             // **** ISD25120 ****
sbit     PR        = P2^1;   // P/R
sbit     EOM       = P2^2;   // EOM
sbit     PD        = P2^3;   // PD
sbit     CE        = P2^4;   // CE
sbit     OVF       = P2^5;   // VOF
sbit     A9        = P2^6;   // A9 
sbit     A8        = P2^7;   // A8

sbit     IPSCL     = P3^2;   // 24LC04
sbit     IPSDA     = P3^3;

sfr      WDTRST    = 0xa6;   // 89C55WD,89C51RC,89S5x,89C51RD2 wd sfr

/********** INT-RAM WORKING AREA **********/

unsigned int       PAGEAD[8];               // page 1-8 start address table
unsigned char      INPUTM;                  // input switch memory
unsigned char      COMBUF[MAXBUF+1];        // communication buffer
unsigned char      COMINX;                  // COMBUF index

         bit       TXOK;                    // tx flag
         bit       RXOK;                    // rx command ready flag
         bit       SCOMF;                   // start comand flag

/********** BASIC FUNCTION **********/

void wdtrig (void) {         // 89C55WD,89C51RC,89S5x,89C51RD2 start & trig watch dog
    WDTRST = 0x1e;           // trig every 18 ms
    WDTRST = 0xe1;            
}

void dmsec (unsigned int count) {           // mSec Delay
    unsigned int i;                         // for Keil PK51 (Speed x 1)
    while (count) {
        wdtrig ();
        i = 115; while (i>0) i--;
        count--;
    }
}

void flash (void) {                         // flash LED
    LED = 0;
    dmsec (200);
    LED = 1;
    dmsec (300);                
}            

unsigned char atohr (unsigned char a) {          // change ascii to hex (R)
    if ((a>='a' && a<='f') || (a>='A' && a<='F'))
        return ((a + 9) & 0xf);
    else return (a & 0xf);
}

void ipdel (void) {                    // I2C delay 
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
}

void ipchigh (void) {                  // I2C clock high 
    IPSCL = 1;
    ipdel ();
}

void ipclow (void) {                   // I2C clock low 
    IPSCL = 0;
    ipdel ();
}

void ipstart (void) {                  // start condition 
    IPSDA = 1;
    IPSCL = 1;
    IPSDA = 0;
    ipdel ();
    IPSCL = 0;
    IPSDA = 1;
}

void ipstop (void) {                   // stop condition 
    IPSDA = 0;
    IPSCL = 1;
    ipdel ();
    IPSDA = 1;
}

bit ipwrbyte (unsigned char dat) {     // write one byte 
    unsigned char i;                   // return 0 = ok 
    bit outbit;                        // return 1 = error 
    for (i=1;i<=8;i++) {
        outbit = dat & 0x80;
        IPSDA = outbit;
        dat = dat << 1;
        ipchigh ();
        ipclow ();
    }
    IPSDA = 1;
    ipchigh ();         
    outbit = IPSDA;
    ipclow ();
    return (outbit);
}

unsigned char iprdbyte (void) {        // read one byte 
    unsigned char i,dat;               
    bit inbit;
    dat = 0;
    for (i=1;i<=8;i++) {
        ipchigh ();
        inbit = IPSDA;
        dat = dat << 1;
        dat = dat | inbit;
        ipclow ();
    }
    IPSDA = 1;
    ipchigh ();
    ipclow ();
    return (dat);
}

unsigned char iprdbytex () {           // read one byte (last byte)
    unsigned char i,dat;               
    bit inbit;
    dat = 0;
    for (i=1;i<=8;i++) {
        ipchigh ();
        inbit = IPSDA;
        dat = dat << 1;
        dat = dat | inbit;
        ipclow ();
    }
    IPSDA = 0;
    ipchigh ();
    ipclow ();
    IPSDA = 1;
    return (dat);
}

void eepwr (void) {                    // eeprom write (16 byte)
    unsigned char i;
    ipstart ();
    ipwrbyte (0xa0);                   // control byte 
    ipwrbyte (0);                      // address
    for (i=0;i<=7;i++) {     	       // data
        ipwrbyte (PAGEAD[i] >> 8);
        ipwrbyte (PAGEAD[i]);  
    }
    ipstop ();
}

void eeprd (void) {                    // eeprom read (16 byte)
    unsigned char i,dat;
    ipstart ();
    ipwrbyte (0xa0);                   // control byte
    ipwrbyte (0);                      // address
    ipstop ();
    ipdel ();
    ipstart ();
    ipwrbyte (0xa1);                   // control byte
    for (i=0;i<=7;i++) {
        dat = iprdbytex ();
        PAGEAD[i] = dat << 8;
        if (i==7) dat = iprdbyte ();   // last byte
        else dat = iprdbytex ();
        PAGEAD[i] = PAGEAD[i] | dat;
    }
    ipstop ();
}

/********** INTERRUPT FUNCTION **********/

char putchar (unsigned char x) {            // change putchar for printf  
    while (!TXOK);
    TXOK = 0;
    return (SBUF = x);
}

void input (unsigned char a) {              // input to COMBUF
    unsigned char i;                   
    if (a==':') {                           // start 
        COMINX = 0;  
        SCOMF = 1;
        for (i=0;i<=MAXBUF;i++) COMBUF[i] = 0;
    }

    else {
        if (SCOMF) {
            if (a==0x0d || a==0xff) {       // end
                COMBUF[COMINX] = a;
                SCOMF = 0;  
                RXOK = 1;
            }    
            else if (a<0x20) return;        // nothing
            else if (COMINX<MAXBUF) {
                COMBUF[COMINX] = a;         // command
                COMINX++;
            }
        }
    }
}

void serial (void) interrupt 4 {            // serial interrupt 
    unsigned char a;                        // receive binary command
    if (TI) {                               // TXD
        TI = 0;
        TXOK = 1;
        return;
    }

    a = SBUF;                               // RXD
    RI = 0;
    input (a);
}

/********** ISD25120 FUNCTION **********/

void setaddr (unsigned int addr) {          // set address to sound chip 
    unsigned char x;
    ADDRB = addr;
    addr = addr >> 8; 
    x = addr;
    if (x & 0x2) A9 = 1; else A9 = 0;
    if (x & 0x1) A8 = 1; else A8 = 0;
}

bit checkmax (unsigned int addr) {          // check max address
    if (addr>600) return (0);               // max
    else return (1);                        // <max  
}

void delay (void) {                         // durations delay & flash LED
    LED = 0;                                // 2590 (200 mS)
    dmsec (100);
    LED = 1;
    dmsec (100-CORRECT);
}

void startrec (void) {                      // start record
    PR = 0;
    PD = 0;
    CE = 0;
}

void stoprec (void) {                       // stop record
    CE = 1;
    PD = 1;
}

void play (void) {                          // playback 
    PR = 1;
    PD = 0;
    CE = 0;
    LED = 0;
    dmsec (100);
    while (1) {
        wdtrig ();
        if (!EOM) break;
        if (!OVF) break;
        if (INPUT!=INPUTM && INPUT!=0xff) break;
    }
    LED = 1;
    CE = 1;
    PD = 1;
    dmsec (100);
}

/********** START FUNCTION **********/

void start (void) {               // speed x 1
    SCON = 0x52;             	  // set RS232 parameter
    TMOD = 0x20;
//    TH1 = 0xfd;                   // 9600 
    TH1 = 0xfd; PCON |= 0x80;     // 19200
    TR1  = 1;
    ES   = 1;                     // serial interrupt enable
    EA   = 1;
}

/********** SUB FUNCTION **********/

void playrx (void) {                   // check play from rs232  
    unsigned char page;
    unsigned int addr;
    wdtrig ();
    if (RXOK) {
        RXOK = 0;
        if (COMBUF[0]>='1' && COMBUF[0]<='8') page = atohr (COMBUF[0]) - 1; 
        else {
            printf ("MKIT-4 v3.0\r");  // ???
			return; 
		}
        if (page>7) addr = 0xffff;
        else addr = PAGEAD[page];
        if (addr==0xffff || (addr==0 && page>0));
        else {
            setaddr (addr);
            play ();
			printf ("OK\r");
        }
    }
}

void playdirect (void) {               // play (direct,instant)   
    unsigned char page;
    unsigned int addr;
    while (1) {
        while ((INPUTM = INPUT)==0xff) playrx ();
        switch (INPUTM) {
            case 0x7f: page = 7; break;
            case 0xbf: page = 6; break;
            case 0xdf: page = 5; break;
            case 0xef: page = 4; break;
            case 0xf7: page = 3; break;
            case 0xfb: page = 2; break;
            case 0xfd: page = 1; break;
            case 0xfe: page = 0; break;
            default: page = 0xff;
        }    
        if (page==0xff) addr = 0xffff; 
		else addr = PAGEAD[page];
        if (addr==0xffff || (addr==0 && page>0));
        else {
            setaddr (addr);
            play ();
			dmsec (200);
        }
        if (INPUT==INPUTM) dmsec (2000); 
    }
}

void record (void) {                   // record mode
    unsigned char page;
    unsigned int addr;

    while (KREC==0) wdtrig ();         // release
    flash ();
    flash ();
    flash ();
    flash ();

    for (page=0;page<=7;page++) {      // clear table
        PAGEAD[page] = 0xffff;
    }
    addr = 0;
    page = 0;
    while (1) {
        while (1) {
            wdtrig ();
            if (KREC==0) {             // key press
                dmsec (200);
                break;       
            } 
            if (KEND==0) return;       // key end (to playback)
        }  
        PAGEAD[page] = addr;                     // start record
        setaddr (addr);            
        startrec ();
        while (1) {
            delay ();
            addr++;
            if (!checkmax (addr)) break;
            if (KREC) {dmsec (200); break;}
        }
        stoprec ();                              // stop   
        addr = addr + 4;
        page++;
        if (page>7 || !checkmax (addr)) {        // full 
            LED = 0;
            while (KEND) wdtrig ();                       
            return;
        }
    } 
}

void write (void) {                    // write ro eeprom
    LED = 1;
    eepwr ();
    dmsec (500);
}

/********** MAIN **********/

void main (void) {
    dmsec (800);                  // start up & read eeprom
    eeprd ();
    start ();
    dmsec (200);
    if (KREC==0) {                // record 
	    record ();
		write ();
	}
    flash ();                     // play back
    flash ();
    playdirect ();
}



