////////////////////////////////////////////////////////////////////////
//
//  bios modules for Pico-2
//      version 2.5 (May 6, 2010)
//
//  (c) Hideki Kozima (xkozima@myu.ac.jp), subject to GPLv2
//
//  modules
//      SWT: address switches (SW1-5 = PE21-17)
//          void   SWT_init (void);
//          ushort SWT_get (void);
//      LED: indicators (LED1-5 = PE16-12)
//          void   LED_init (void);
//          void   LED_set (ushort dataW5);
//          void   LED_op  (ushort locW5, ushort dataW5);
//      PWM: PWM/DIR output (TIOC0A-D; PE11,9,7,5)
//          void   PWM_init (void);
//          void   PWM_set (uchar numB2, short valW);
//      RCS: RC servo output to CN6=RCS (PE10,8,6,4)
//          void   RCS_init (void);
//          void   RCS_set (uchar numB2, ushort valW);
//      ENC: encoder interface (ZEN2044F)
//          void   ENC_init (void);
//          int    ENC_get (uchar numB2);
//          void   ENC_set (uchar numB2, int count);
//      SEN: current sensor input (AN19-16)
//          void   SEN_init (void);
//          ushort SEN_get  (uchar numB2);
//          void   SEN_gets (ushort *sen0W, *sen1W, *sen2W, *sen3W);
//      LIO: logic I/O on CN7=LIO (PB5-2)
//          void   LIO_init (uchar dirB4);
//          void   LIO_set (uchar valB4);
//          ushort LIO_get (void);
//      LIN: logic input of CN8=ALINX (PF11-8) and CN9=ALINY (PF15-12)
//          void   LIN_init (void);
//          ushort LIN_get (void);
//      AIX: analog input of CN8=ALINX (PF11-8) 
//          void   AIX_init (void);
//          void   AIX_gets (ushort *val0W, ushort *val1W, 
//                           ushort *val2W, ushort *val3W );
//      AIY: analog input of CN8=ALINY (PF15-12) 
//          void   AIY_init (void);
//          void   AIY_gets (ushort *val0W, ushort *val1W, 
//                           ushort *val2W, ushort *val3W );
//      CMT: compare match timer (CMT0-1)
//          void  CMT0_init (uchar cselB2, ushort periodW, void (*action)());
//          void  CMT1_init (uchar cselB2, ushort periodW, void (*action)());
//          void  CMT0_quit (void);
//          void  CMT1_quit (void);
//      SCI: serial port (RS485)
//          void  SCI_init (uchar addr_mask, uchar addr, 
//                          uchar pack_len, void *interpreter());
//          void  SCI_transmit (uchar *packet);
//          void  SCI_quit  (void);
//      WDT: watch dog timer (WDT)
//          void  WDT_init (uchar cselB3);
//          void  WDT_reset ();
//          int   WDT_check ();

#include "sh7046f.h"                    //  header for sh7046f

//
//  bitwise operation

#define  bit_op(var,mask,data)  (var=((var)&(~(mask)))|(data))

////////////////////////////////////////////////////////////////////////
//
//  SWT: address switches (SW1-5 = PE17-21)
//      void  SWT_init (void);
//          initialize SWT (PE21-17: in).
//      ushort  SWT_get (void);
//          read 5bits (0x00-0x1f) status of the switches.

void  SWT_init (void)
{
    //  PE21-17: I/O
    bit_op(PFC.PECRH.WORD,  0x0ffc, 0x0000);

    //  PE21-17: input
    bit_op(PFC.PEIORH.WORD, 0x003e, 0x0000);
}

ushort  SWT_get (void)
{
    //  get PE21-17 (= DRH[5:1])
    return (~(PE.DRH.WORD >> 1)) & 0x1f;
}

////////////////////////////////////////////////////////////////////////
//
//  LED: indicators (LED1-5 = PE16-12)
//      void   LED_init (void);
//          initialize LED (PE16-12: out).
//      void   LED_set (ushort  dataW5);
//          set 5bits data to LED port.
//      ushort LED_get (void);
//          get 5bits data from LED port
//      void   LED_op  (ushort locW5, ushort dataW5);
//          manipulate specific bit(s) of LED port.
//          (ex. LED_op(0x05, 0x05) puts on LED3 and LED0.)

void  LED_init (void)
{
    //  PE16-12: I/O
    bit_op(PFC.PECRH.WORD,  0x0003, 0x0000);
    bit_op(PFC.PECRL1.WORD, 0xff00, 0x0000);

    //  PE16-12: output
    bit_op(PFC.PEIORH.WORD, 0x0001, 0x0001);
    bit_op(PFC.PEIORL.WORD, 0xf000, 0xf000);
}

void  LED_set (ushort dataW5)
{
    ushort  dataH, dataL;

    //  set hi/lo
    dataH = (~dataW5 & 0x001f) >> 4;
    dataL = (~dataW5 & 0x001f) << 12;

    //  set PE16-12 (= DRH[0],DRL[15-12])
    bit_op(PE.DRH.WORD, 0x0001, dataH);
    bit_op(PE.DRL.WORD, 0xf000, dataL);
}

ushort LED_get (void)
{
    ushort  data;

    //  get PE16-12 (= DRH[0],DRL[15-12])
    data = (PE.DRH.WORD << 4) | (PE.DRL.WORD >> 12);

    //  return complement (lower 5bits)
    return (~data & 0x1f);
}

void  LED_op  (ushort locW5, ushort dataW5)
{
    ushort  data;

    //  get current LED status
    data = LED_get();

    //  manipulate specific bits
    bit_op(data, locW5, dataW5);

    //  output to LED port
    LED_set(data);
}

////////////////////////////////////////////////////////////////////////
//
//  PWM: PWM output (TIOC0A-D)
//      void  PWM_init (void);
//          initialize PWM (TIOC0A-D = PE0-3: PWM out).
//          (TGRA-D_0, PWM2 mode, clear with TGRA_1, 24KHz=0x0400*P.)
//      void  PWM_set (uchar numB2, short valW);
//          set duty ratio of PWM[numB2] to valW16,
//              where numB2: PWM axis number [0..3],
//                    valW : 16bit value -> abs -> shift to [0..0x03ff].

#define  PWM_CYCLE    0x0400                    //  41.666us (24KHz)
#define  PWM_SHIFT    6                         //  0xffff -> 0x03ff
#define  PWM_DEFAULT  0x0400                    //  never match (= 0%)

void  PWM_init (void)
{
    //  init DIR (PE11,9,7,5) as I/O out
    bit_op(PFC.PECRL1.WORD, 0x00cc, 0x0000);
    bit_op(PFC.PECRL2.WORD, 0xcc00, 0x0000);
    bit_op(PFC.PEIORL.WORD, 0x0aa0, 0x0aa0);

    //  wake up MTU from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x2000, 0x0000);

    //  stop counting (for ch0,4)
    bit_op(MTU.TSTR.BYTE, 0x81, 0x00);

    //  select clear/edge/presc (for ch0)
    bit_op(MTU0.TCR.BYTE, 0xe0, 0x60);          //  sync clear
    bit_op(MTU0.TCR.BYTE, 0x18, 0x00);          //  rise edge
    bit_op(MTU0.TCR.BYTE, 0x07, 0x00);          //  at P (P=24.576MHz)

    //  TGRA-D as output cmp reg (0->1)
    MTU0.TIOR.WORD = 0x2222;

    //  set initial duty ratio
    MTU0.TCNT = 0x0000;
    MTU0.TGRA = PWM_DEFAULT;
    MTU0.TGRB = PWM_DEFAULT;
    MTU0.TGRC = PWM_DEFAULT;
    MTU0.TGRD = PWM_DEFAULT;

    //  set ch0 to PWM2 mode
    bit_op(MTU0.TMDR.BYTE, 0xcf, 0xc3);

    //  pin assignment PE0-3 -> TIOC0A-D
    bit_op(PFC.PECRL2.WORD, 0x00ff, 0x0055);
    bit_op(PFC.PEIORL.WORD, 0x000f, 0x000f);

    //  select clear/edge/presc (for ch4)
    bit_op(MTU34.TCR_4.BYTE, 0xe0, 0x20);       //  clear with TGRA_4
    bit_op(MTU34.TCR_4.BYTE, 0x18, 0x00);       //  rise edge
    bit_op(MTU34.TCR_4.BYTE, 0x07, 0x00);       //  P (P=24.576MHz)

    //  set PWM period (to clear all)
    MTU34.TCNT_4 = 0x0000;
    MTU34.TGRA_4 = PWM_CYCLE - 1;

    //  set ch4 to normal mode
    bit_op(MTU34.TMDR_4.BYTE, 0xcf, 0xc0);

    //  sync ch0,4
    bit_op(MTU.TSYR.BYTE, 0x81, 0x81);

    //  start ch0,4
    bit_op(MTU.TSTR.BYTE, 0x81, 0x81);
}

#define  PWM_dir(n)  (0x0020<<((n)*2))

void  PWM_set (uchar numB2, short valW)
{
    int     valLabs;
    ushort  dir, valWc;

    //  get absolute value & output direction
    dir = PWM_dir(numB2 & 0x03);
    if (valW == -32768) {
        //  avoid out-of-range of 15bits: abs(-32768)=0x8000
        valLabs = 0x00007fff;                   //  trim to 15bits
        bit_op(PE.DRL.WORD, dir, dir);          //  negative direction
    }
    else if (valW < 0) {
        valLabs = valW;
        valLabs = -valLabs;                     //  [0,0x00007fff]
        bit_op(PE.DRL.WORD, dir, dir);          //  negative direction
    }
    else {
        valLabs = valW;                         //  [0,0x00007fff]
        bit_op(PE.DRL.WORD, dir, 0);            //  positive direction
    }

    //  make complement and trim
    valWc = (0x00007fff - valLabs) >> (PWM_SHIFT - 1);

    //  when trying 0%
    if (valWc == PWM_CYCLE - 1)
        valWc = PWM_CYCLE;                      //  force 0% (never match)

    //  update duty-ratio
    switch (numB2 & 0x03) {
        case 0: MTU0.TGRA = valWc; break;
        case 1: MTU0.TGRB = valWc; break;
        case 2: MTU0.TGRC = valWc; break;
        case 3: MTU0.TGRD = valWc; break;
    }
}

////////////////////////////////////////////////////////////////////////
//
//  RCS: RC servo output to CN6=RCS (PE10,8,6,4)
//      void  RCS_init (void);
//          initialize PWM1 at T=10.667ms, 93.75Hz.
//          (PE10=TIOC3C, PE08=TIOC3A, PE06=TIOC2A, PE04=TIOC1A)
//      void  RCS_set (uchar numB2, ushort valW);
//          set duty ratio of RSC[numB2] to valW16,
//              where numB2 : RCS axis number [0..3],
//                    valW16: 16bit value [0..0xffff].
//                            (0x2400 gives 1.5ms = center)

#define  RCS_CYCLE    0x1000			//  10.667ms (4096)
#define  RCS_SHIFT    4                         //  16bit-log2(RCS_CYCLE)
#define  RCS_DEFAULT  0x0240                    //  1.500ms (576)

void  RCS_init (void)
{
    //  wake up MTU from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x2000, 0x0000);

    //  stop counting (for ch1,2,3)
    bit_op(MTU.TSTR.BYTE, 0x46, 0x00);

    //  pin assignment PE10=TIOC3C,PE8=TIOC3A,PE6=TIOC2A,PE4=TIOC1A
    bit_op(PFC.PECRL1.WORD, 0x0033, 0x0011);
    bit_op(PFC.PECRL2.WORD, 0x3300, 0x1100);
    bit_op(PFC.PEIORL.WORD, 0x0550, 0x0550);

    //  select clear/edge/presc (for ch1)
    bit_op(MTU1.TCR.BYTE, 0xe0, 0x20);          //  TGRA clear
    bit_op(MTU1.TCR.BYTE, 0x18, 0x00);          //  rise edge
    bit_op(MTU1.TCR.BYTE, 0x07, 0x03);          //  P/64 (P=24.576MHz)

    //  select clear/edge/presc (for ch2)
    bit_op(MTU2.TCR.BYTE, 0xe0, 0x20);          //  TGRA clear
    bit_op(MTU2.TCR.BYTE, 0x18, 0x00);          //  rise edge
    bit_op(MTU2.TCR.BYTE, 0x07, 0x03);          //  P/64 (P=24.576MHz)

    //  select clear/edge/presc (for ch3)
    bit_op(MTU34.TCR_3.BYTE, 0xe0, 0x20);       //  TGRA clear
    bit_op(MTU34.TCR_3.BYTE, 0x18, 0x00);       //  rise edge
    bit_op(MTU34.TCR_3.BYTE, 0x07, 0x03);       //  P/64 (P=24.576MHz)

    //  ch1,2: TGRA=0/0, TGRB=0/1
    //  ch3  : TGRA=0/0, TGRB=0/1, TGRC=0/0, TGRD=0/1, 
    MTU1.TIOR.BYTE.H  = 0x21;
    MTU2.TIOR.BYTE.H  = 0x21;
    MTU34.TIOR_3.WORD = 0x2121;

    //  set PWM period and duty-ratio
    //      period: P/64/65536 -> 10.67ms (93.75Hz)
    //      duty 3520 -> 1.50ms (default)
    MTU1.TCNT    = 0x0000;
    MTU1.TGRA    = RCS_CYCLE -1;
    MTU1.TGRB    = RCS_CYCLE - RCS_DEFAULT;
    MTU2.TCNT    = 0x0000;
    MTU2.TGRA    = RCS_CYCLE -1;
    MTU2.TGRB    = RCS_CYCLE - RCS_DEFAULT;
    MTU34.TCNT_3 = 0x0000;
    MTU34.TGRA_3 = RCS_CYCLE -1;
    MTU34.TGRB_3 = RCS_CYCLE - RCS_DEFAULT;
    MTU34.TGRC_3 = RCS_CYCLE -1;
    MTU34.TGRD_3 = RCS_CYCLE - RCS_DEFAULT;

    //  set ch1,2,3 to PWM1 mode
    bit_op(MTU1.TMDR.BYTE, 0xcf, 0xc2);
    bit_op(MTU2.TMDR.BYTE, 0xcf, 0xc2);
    bit_op(MTU34.TMDR_3.BYTE, 0xcf, 0xc2);

    //  start ch1,2,3
    bit_op(MTU.TSTR.BYTE, 0x46, 0x46);
}

void  RCS_set (uchar numB2, ushort valW)
{
    ushort valWc;

    //  make complement and trim
    valWc = (0xffff - valW) >> RCS_SHIFT;

    //  when trying 0%
    if (valWc == RCS_CYCLE - 1)
        valWc = RCS_CYCLE;                      //  force 0% (never match)

    //  update duty-ratio
    switch (numB2 & 0x03) {
        case 0: MTU1.TGRB    = valWc; break;
        case 1: MTU2.TGRB    = valWc; break;
        case 2: MTU34.TGRB_3 = valWc; break;
        case 3: MTU34.TGRD_3 = valWc; break;
    }
}

////////////////////////////////////////////////////////////////////////
//
//  ENC: encoder interface (ZEN2044F)
//      void  ENC_init (void);
//          initialize the interface to ZEN2044F.
//              PA15 : C/D* (Commnand or Data)
//              PA14 : RD*  (Read strobe)
//              PA13 : WR*  (Write strobe)
//              PA12 : AD1  (address high-bit)
//              PA11 : AD0  (address low-bit)
//              PA7-0: command or data
//      int   ENC_get (uchar numB2);
//              get encoder count of axis numB2.
//      void  ENC_set (uchar numB2, int count);
//              set encoder count of axis numB2.

void  ENC_init (void)
{
    void  ENC_set(uchar, int);

    //  PA7-0: in
    bit_op(PFC.PACRL3.WORD, 0x00ff, 0x0000);
    bit_op(PFC.PACRL2.WORD, 0xffff, 0x0000);
    bit_op(PFC.PAIORL.WORD, 0x00ff, 0x0000);    //  PA7-0: in

    //  PA15-11: out
    //      PA15:C/D*=0, PA14:RD*=1, PA13:WR*=1, PA12-11:AD=00
    bit_op(PFC.PACRL3.WORD, 0xf800, 0x0000);
    bit_op(PFC.PACRL1.WORD, 0xffc0, 0x0000);
    bit_op(PA.DR.WORD, 0xf800, 0x6000);
    bit_op(PFC.PAIORL.WORD, 0xf800, 0xf800);    //  PA15-11: out

    //  set zero to encoders
    ENC_set(0, 0);
    ENC_set(1, 0);
    ENC_set(2, 0);
    ENC_set(3, 0);
}

int  ENC_get (uchar numB2)
{
    ushort  axis;
    uint    valLB, valMB, valHB;                //  24bits={H,M,L}
    int     result;

    //  latch data
    axis = (numB2 << 11);
    bit_op(PA.DR.WORD, 0xf8ff, 0xe010 | axis);  //  mark C + "ud-cnt/latch"
    PFC.PAIORL.BYTE.L = 0xff;                   //  set PA0-7 out
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0xe000, 0x6000);         //  unmark WR* + C
    PFC.PAIORL.BYTE.L = 0x00;                   //  set PA0-7 in

    //  read data
    bit_op(PA.DR.WORD, 0x6000, 0x2000);         //  mark RD*
    valLB = PA.DR.BYTE.L;                       //  read low-byte
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark RD*
    bit_op(PA.DR.WORD, 0x6000, 0x2000);         //  mark RD*
    valMB = PA.DR.BYTE.L;                       //  read mid-byte
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark RD*
    bit_op(PA.DR.WORD, 0x6000, 0x2000);         //  mark RD*
    valHB = PA.DR.BYTE.L;                       //  read high-byte
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark RD*

    //  make up the result
    result = (valHB << 16) | (valMB << 8) | valLB;
    if (valHB & 0x80)
        result |= 0xff000000;

    return  result;
}

void  ENC_set (uchar numB2, int count)
{
    ushort  axis;

    //  set access pointer
    axis = (numB2 << 11);
    bit_op(PA.DR.WORD, 0xf8ff, 0xe008 | axis);  //  mark C + "preload"
    PFC.PAIORL.BYTE.L = 0xff;                   //  set PA0-7 out
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0xe000, 0x6000);         //  unmark WR* + C

    //  write data
    PA.DR.BYTE.L = count & 0x000000ff;          //  set lower data
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark WR*
    PA.DR.BYTE.L = (count & 0x0000ff00) >> 8;   //  set mid data
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark WR*
    PA.DR.BYTE.L = (count & 0x00ff0000) >> 16;  //  set higher data
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0x6000, 0x6000);         //  unmark WR*

    //  load (preload -> ud-cnt)
    bit_op(PA.DR.WORD, 0xf8ff, 0xe088 | axis);  //  mark C + "LD+preload"
    bit_op(PA.DR.WORD, 0x6000, 0x4000);         //  mark WR*
    bit_op(PA.DR.WORD, 0xe000, 0x6000);         //  unmark WR* + C

    //  calm down
    PFC.PAIORL.BYTE.L = 0x00;                   //  set PA0-7 in
}

////////////////////////////////////////////////////////////////////////
//
//  SEN: current sensor input (AN19-16)
//      void   SEN_init (void);
//          init AD2 for AN[19-16]=PG03-00 (SEN3-0), 
//          and start the first A/D conversion.
//      ushort  SEN_get (uchar num);
//          read analog value from SENnum, 
//              where return=0x0000 for 0V (GND level),
//                           0xffff for 5V (Vcc level).
//              (usually, a steady offset (approx. 0x0300)
//               will be observed for 0V input.)
//      void   SEN_gets (ushort *sen0W, ushort *sen1W, 
//                       ushort *sen2W, ushort *sen3W );
//          read analog value from SEN3-0.
//              where sen?W=0x0000 for 0V (GND level),
//                          0xffff for 5V (Vcc level).
//              (usually, a steady offset (approx. 0x0300)
//               will be observed for 0V input.)

void  SEN_init (void)
{
    //  wake up AD2 from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x0040, 0x0000);

    //  stop conversion (while setup)
    bit_op(AD.ADCR_2, 0x10, 0x00);

    //  setup control registers
    //      no-int, 4ch-scan, AN19-16
    //      P/8 (266/256T=24KHz), continuous-scan
    bit_op(AD.ADCSR_2, 0x77, 0x13);
    bit_op(AD.ADCR_2,  0xf8, 0x48);

    //  start first conversion (ADST<-1)
    bit_op(AD.ADCR_2,  0x10, 0x10);
}

ushort  SEN_get (uchar numB2)
{
    return  AD.ADDR[16 + (numB2 & 0x03)];
}

void  SEN_gets (ushort *sen0W, ushort *sen1W, 
                ushort *sen2W, ushort *sen3W )
{
    //  read data
    *sen0W = AD.ADDR[16];
    *sen1W = AD.ADDR[17];
    *sen2W = AD.ADDR[18];
    *sen3W = AD.ADDR[19];
}

////////////////////////////////////////////////////////////////////////
//
//  LIO: logic I/O on CN7=LIO (PB5-2)
//      void  LIO_init (uchar dirB4);
//          init PB[5-2] as logic I/O, and 
//          set direction as valB4[3-0] (0/1 for in/out).
//      void  LIO_set (uchar valB4);
//          for output pins, output the bit-pattern.
//      ushort  LIO_get (void);
//          for input pins, read the bit-pattern at the pins.
//          (for output pins, output levels appear in the result.)

void  LIO_init (uchar dirB4)
{
    //  PB5-2 as logic I/O
    bit_op(PFC.PBCR1.WORD, 0x3c00, 0x0000);
    bit_op(PFC.PBCR2.WORD, 0x0ff0, 0x0000);

    //  direction of PB5-2 (0/1 for in/out)
    bit_op(PFC.PBIOR.WORD, 0x003c, (dirB4 & 0x0f) << 2);
}

void  LIO_set (uchar valB4)
{
    PB.DR.WORD = (valB4 & 0x0f) << 2;
}

ushort  LIO_get (void)
{
    return PB.DR.WORD >> 2;
}

////////////////////////////////////////////////////////////////////////
//
//  LIN: logic input of CN8=ALINX (PF11-8) and CN9=ALINY (PF15-12)
//      void  LIN_init (void);
//          init CN8/9 as logic input.
//      ushort  LIN_get (void);
//          read the 8-bit-pattern of the pins.

void  LIN_init (void)
{
    //  nothing to do with LIN (PF8-15)
}

ushort  LIN_get (void)
{
    return PF.DR.BYTE.H;
}

////////////////////////////////////////////////////////////////////////
//
//  AIX: analog input of CN8(ALINX) = AN11-8
//      void  AIX_init (void);
//          init AD0 for AN[11-8] (ALINX4-0), 
//          and start the first A/D conversion.
//      void  AIX_gets (ushort *val0W, ushort *val1W, 
//                      ushort *val2W, ushort *val3W );
//          read analog value from AN[11-8] (ALINX4-0), 
//          and start the next A/D conversion.
//              where val?W=0x0000 for 0V (GND level),
//                          0xffff for 5V (Vcc level).

void  AIX_init (void)
{
    //  wake up AD2 from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x0010, 0x0000);

    //  stop conversion (while setup)
    bit_op(AD.ADCR_0, 0x10, 0x00);

    //  setup control registers
    //      no-int, 4ch-scan, AN11-8
    //      P/8 (266/256T=24KHz), 1cycle-scan
    bit_op(AD.ADCSR_0, 0x77, 0x17);
    bit_op(AD.ADCR_0,  0xf8, 0x40);

    //  start first conversion
    bit_op(AD.ADCR_0,  0x10, 0x10);
}

void  AIX_gets (ushort *val0W, ushort *val1W, 
                ushort *val2W, ushort *val3W )
{
    //  wait for ADF (end flag)
    while ((AD.ADCSR_0 & 0x80) == 0);
    bit_op(AD.ADCSR_0, 0x80, 0x00);             //  clear ADF (end flag)

    //  read data from AN[11-8]
    *val0W = AD.ADDR[8];
    *val1W = AD.ADDR[9];
    *val2W = AD.ADDR[10];
    *val3W = AD.ADDR[11];

    //  start next conversion
    bit_op(AD.ADCR_0,  0x10, 0x10);
}

////////////////////////////////////////////////////////////////////////
//
//  AIY: analog input of CN9(ALINY) = AN15-12
//      void  AIY_init (void);
//          init AD1 for AN[15-12] (ALINY4-0), 
//          and start the first A/D conversion.
//      void  AIY_gets (ushort *val0W, ushort *val1W, 
//                      ushort *val2W, ushort *val3W );
//          read analog value from AN[11-8] (ALINX4-0), 
//          and start the next A/D conversion.
//              where val?W=0x0000 for 0V (GND level),
//                          0xffff for 5V (Vcc level).

void  AIY_init (void)
{
    //  wake up AD1 from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x0020, 0x0000);

    //  stop conversion (while setup)
    bit_op(AD.ADCR_1, 0x10, 0x00);

    //  setup control registers
    //      no-int, 4ch-scan, AN15-12
    //      P/8 (266/256T=24KHz), 1cycle-scan
    bit_op(AD.ADCSR_1, 0x77, 0x17);
    bit_op(AD.ADCR_1,  0xf8, 0x40);

    //  start first conversion
    bit_op(AD.ADCR_1,  0x10, 0x10);
}

void  AIY_gets (ushort *val0W, ushort *val1W, 
                ushort *val2W, ushort *val3W )
{
    //  wait for ADF (end flag)
    while ((AD.ADCSR_1 & 0x80) == 0);
    bit_op(AD.ADCSR_1, 0x80, 0x00);             //  clear ADF (end flag)

    //  read data from AN[11-8]
    *val0W = AD.ADDR[12];
    *val1W = AD.ADDR[13];
    *val2W = AD.ADDR[14];
    *val3W = AD.ADDR[15];

    //  start next conversion
    bit_op(AD.ADCR_1,  0x10, 0x10);
}

////////////////////////////////////////////////////////////////////////
//
//  CMT: compare match timer
//      void  CMT0_init (uchar cselB2, ushort periodW, void (*action)());
//      void  CMT1_init (uchar cselB2, ushort periodW, void (*action)());
//          initialize CMT0 or CMT1, where
//              cselB2 : clock prescaler
//                       00 for C=P/8,    01 for C=P/32
//                       10 for C=P/128,  11 for C=P/512
//              periodW: period (C /(period+1) Hz)
//              action : a function called by CMT0/1 interrupt
//                       ex. void  f_tick (void) {
//                               do_something();
//                           }
//          ex. CMT0_init(0x03, 47999, f_tick);
//              C=P/512, C/48000 -> 1Hz
//      void  CMT0_quit (void);
//      void  CMT1_quit (void);
//          disable CMT interruption

void  CMT0_interrupt (void);
void  CMT1_interrupt (void);

void   (*CMT0_action)();                        //  user's action
void   (*CMT1_action)();                        //  user's action

void  CMT0_init (uchar cselB2, ushort periodW, void (*action)())
{
    ushort  start_bit, vec_num;

    //  wakeu up CMT from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x1000, 0x0000);

    //  stop counting
    bit_op(CMT.CMSTR.WORD,  0x0001, 0x0000);

    //  set period
    bit_op(CMT0.CMCSR.WORD, 0x0003, cselB2);
    CMT0.CMCOR = periodW;

    //  setup interrupt CMI0
    definterrupt(144, (void *) CMT0_interrupt);
    CMT0_action = action;
    bit_op(CMT0.CMCSR.WORD, 0x0040, 0x0040);
    bit_op(INTC.IPRG.WORD,  0x00f0, 0x0070);

    //  start counting
    bit_op(CMT.CMSTR.WORD,  0x0001, 0x0001);
}

void  CMT1_init (uchar cselB2, ushort periodW, void (*action)())
{
    ushort  start_bit, vec_num;

    //  wake up CMT from stand-by mode
    bit_op(MSTCR.MSTCR2.WORD, 0x1000, 0x0000);

    //  stop counting
    bit_op(CMT.CMSTR.WORD,  0x0002, 0x0000);

    //  set period
    bit_op(CMT1.CMCSR.WORD, 0x0003, cselB2);
    CMT1.CMCOR = periodW;

    //  setup interrupt CMI1
    definterrupt(148, (void *) CMT1_interrupt);
    CMT1_action = action;
    bit_op(CMT1.CMCSR.WORD, 0x0040, 0x0040);
    bit_op(INTC.IPRG.WORD,  0x000f, 0x0007);

    //  start counting
    bit_op(CMT.CMSTR.WORD,  0x0002, 0x0002);
}

#pragma interrupt
void  CMT0_interrupt (void)
{
    //  clear interrupt flag for CMT0
    bit_op(CMT0.CMCSR.WORD, 0x0080, 0x0000);

    //  call the action function
    (*CMT0_action)();
}

#pragma interrupt
void  CMT1_interrupt (void)
{
    //  clear interrupt flag for CMT0
    bit_op(CMT1.CMCSR.WORD, 0x0080, 0x0000);

    //  call the action function
    (*CMT1_action)();
}

void  CMT0_quit (void)
{
    //  stop counting
    bit_op(CMT.CMSTR.WORD,  0x0001, 0x0000);
}

void  CMT1_quit (void)
{
    //  stop counting
    bit_op(CMT.CMSTR.WORD,  0x0002, 0x0000);
}

////////////////////////////////////////////////////////////////////////
//
//  SCI: serial port (RS485)
//      void  SCI_init (uchar addr, uchar paclen, void *interpreter());
//          initialize SCI3 (PA8:RxD, PA9:TxD) as 38400bps 1-7-M-1
//          *interpreter is a pointer to packet interpreting function
//              ex. void  packet_interpreter (uchar *packet);
//          where packet is an array of paclen uchars.
//                addr is bit[6:2] (5bits) of packet[0]
//      void  SCI_transmit (uchar *packet);
//          transmit the packet, where packet is an array of paclen
//          uchars.
//
//  Serial data format:
//      packet[0]       packet[1]  ...  packet[paclen-2]  packet[paclen-1]
//        bit[7]=1        bit[7]=0        bit[7]=0          bit[7]=0   
//        bit[6:2]=Addr   bit[6:0]=data   bit[6:0]=data     bit[6:0]=data
//        bit[1:0]=data

#define  ADDR_ALL  0x1f
uchar  Addr;                                    //  my address (5bits)

uchar  Rnum,                                    //  num bytes received
       Tnum;                                    //  num bytes transmitted

#define  PACLEN_MAX  8                          //  max packet length
uchar  Rdata[PACLEN_MAX],                       //  packet received 
       Tdata[PACLEN_MAX];                       //  packet to transmit
uchar  Paclen;

void   SCI_int_ERI (void);
void   SCI_int_RXI (void);
void   SCI_int_TXI (void);
void   SCI_int_TEI (void);

void   (*SCI_interpreter)();                    //  receiver function

void  SCI_init (uchar addr, uchar paclen, void *interpreter())
{
    int  i;

    //  assign my address (7bits)
    Addr = addr;

    //  store Paclen (packet length)
    Paclen = paclen;

    //  clear Rnum/Tnum
    Rnum = 0;                                   //  no data received
    Tnum = 0;                                   //  no data transmitted

    //  wake up SCI3 (from stand-by mode)
    bit_op(MSTCR.MSTCR1.WORD, 0x0008, 0x0000);

    //  disable SCI3 while setup
    bit_op(SCI3.SCR.BYTE,  0xfc, 0x00);         //  TE/RE/... are all off

    //  select clock source
    bit_op(SCI3.SCR.BYTE,  0x03, 0x00);         //  use internal clock

    //  setup data format (1-7-M-1)
    bit_op(SCI3.SMR.BYTE,  0xff, 0x44);         //  async, 1-7-M-1, P/1
    bit_op(SCI3.SDCR.BYTE, 0x08, 0x00);         //  LSB-first (as usual)

    //  set baud 38400 (n:N=0:19)
    //      P=24576000, B=P/(32*(N+1)) -> B=38400, N=19
    SCI3.BRR = 19;

    //  wait for the 1bit period
    //      49152000/38400 = 1280T
    //          fastest code:  Lable  DT  Rn      (1)
    //                                BT  Lable   (3)
    //      1280/4 = 320
    for (i = 0; i < 320; i++);

    //  setup interrupt hander
    //      ERI3 172 (error = ORER|FER|PER)
    //      RXI3 173 (received data = RDRF)
    //      TXI3 174 (transmitter ready = TDRE)
    //      TEI3 175 (transmission completed)
    //      SCI_interpreter (packet interpreter)
    definterrupt(172, (void *) SCI_int_ERI);
    definterrupt(173, (void *) SCI_int_RXI);
    definterrupt(174, (void *) SCI_int_TXI);
    definterrupt(175, (void *) SCI_int_TEI);
    SCI_interpreter = (void *) interpreter;

    //  enable interrupt (TIE/RIE) and multi-processor mode
    bit_op(INTC.IPRI.WORD,  0x0f00, 0x0700);
    bit_op(SCI3.SCR.BYTE, 0xcc, 0xc8);

    //  pin re-assignment
    //      PA08: RxD
    //      PA09: TxD
    //      PA10: TE (transmitter enable for the RS485 driver)
    bit_op(PFC.PACRL1.WORD, 0x003f, 0x0005);
    bit_op(PFC.PACRL3.WORD, 0x0700, 0x0300);
    bit_op(PFC.PAIORL.WORD, 0x0400, 0x0400);    //  PA10 out for TE
    bit_op(PA.DR.WORD,      0x0400, 0x0000);    //  driver disable

    //  enable transmitter and receiver
    bit_op(SCI3.SCR.BYTE, 0x30, 0x30);
}

#pragma interrupt
void  SCI_int_ERI (void)
{
    //  clear error flags (RDRF, ORER, FER, PER)
    bit_op(SCI3.SSR.BYTE, 0x78, 0x00);

    //  on error, ignore so-far-received data
    //  sleep until next address-mark
    Rnum = 0;
    bit_op(SCI3.SCR.BYTE, 0x08, 0x08);
}

#pragma interrupt
void  SCI_int_RXI (void)
{
    uchar  data, addr5;

    //  read data from RDR and clear RDRF
    data = SCI3.RDR;
    bit_op(SCI3.SSR.BYTE, 0x40, 0x00);

    //  address or body
    if (SCI3.SSR.BYTE & 0x02) {
        //  the data has address-mark (MPB==1)

        //  check the address
        addr5 = data >> 2;
        if (addr5 == Addr || addr5 == ADDR_ALL)  {
            //  HIT (to me or boardcast)
            //  entering listening mode
            Rdata[0] = data | 0x80;
            Rnum = 1;
        }
        else {
            //  IGNORE (not to me)
            //  sleep until next address-mark (MPIE<-1)
            Rnum = 0;
            bit_op(SCI3.SCR.BYTE, 0x08, 0x08);
        }
    }
    else {
        //  the data is packet-body (MPB==0)

        //  store the packet-body
        Rdata[Rnum++] = data;

        //  check packet length
        if (Rnum == Paclen) {
            //  reached packet end

            //  interpret the packet
            (*SCI_interpreter)(Rdata);
            //  sleep until next address-mark (MPIE<-1)
            Rnum = 0;
            bit_op(SCI3.SCR.BYTE, 0x08, 0x08);
        }
    }
}

#pragma interrupt
void  SCI_int_TXI (void)
{
    //  if not in sending, then return.
    if (Tnum == 0) return;

    //  send one byte and clear TDRE and MPBT
    SCI3.TDR = Tdata[Tnum];
    bit_op(SCI3.SSR.BYTE, 0x81, 0x00);

    //  update Tnum
    Tnum++;

    //  if the final byte is being sent, clear Tnum 
    //  and disable TXI and enable TEI
    if (Tnum == Paclen) {
        Tnum = 0;
        bit_op(SCI3.SCR.BYTE, 0x84, 0x04);
    }
}

#pragma interrupt
void  SCI_int_TEI (void)
{
    //  disable TEI
    bit_op(SCI3.SCR.BYTE, 0x04, 0x00);

    //  disable the RS485 driver (TE10<-0)
    bit_op(PA.DR.WORD, 0x0400, 0x0000);         //  driver disable
}

void  SCI_transmit (uchar *packet)
{
    int  i;

    //  wait for Tnum==0
    while (Tnum);

    //  double check TDRE==1
    while ((SCI3.SSR.BYTE & 0x80) == 0);

    //  store tdata to Tdata
    for (i = 0; i < Paclen; i++)
        Tdata[i] = packet[i] & 0x7f;

    //  enable TXI
    bit_op(SCI3.SCR.BYTE, 0x80, 0x80);

    //  enable the RS485 driver (TE10<-1)
    bit_op(PA.DR.WORD, 0x0400, 0x0400);         //  driver enable

    //  send the first byte with MPB
    SCI3.TDR = Tdata[0];
    bit_op(SCI3.SSR.BYTE, 0x81, 0x01);
    Tnum = 1;
}

void  SCI_quit (void)
{
    //  disable SCI3
    bit_op(SCI3.SCR.BYTE,  0xfc, 0x00);         //  TE/RE/... are all off

    //  sleep down SCI3 (into stand-by mode)
    bit_op(MSTCR.MSTCR1.WORD, 0x0008, 0x0008);
}

////////////////////////////////////////////////////////////////////////
//
//  WDT: watch dog timer (WDT)
//      void  WDT_init (uchar cselB3);
//          initializes WDT (watch dog timer) that could reset the CPU
//          when the counter TCNT overflows.  Not to get the CPU reset,
//          the user program has to call WDT_clear() before TCNT exceeds
//          0xff.  cselB3 selects the clock for counting up TCNT --- see
//          the table below for details.
//      void  WDT_reset ();
//          clears TCNT of WDT (watch dog timer).  The user program has
//          to call WDT_clear before TCNT overflows.
//      int   WDT_check ();
//          checks if the system has just rebooted from WDT reset.  If
//          this is the case, WOVF of WDT.RSTCSR has been set after
//          reboot, so WDT_check clears WOVF and returns non-zero value;
//          otherwise, it returns zero.
//
//  Time to overflow for pico2 (clock 49.152MHz):
//      csel = 0 :  0.0104msec (96000.0Hz)
//             1 :  0.3333msec (3000.00Hz)
//             2 :  0.6667msec (1500.00Hz)
//             3 :  1.3333msec (750.000Hz)
//             4 :  2.6667msec (375.000Hz)
//             5 :  5.3333msec (187.500Hz)
//             6 : 21.3333msec (46.8750Hz)
//             7 : 42.6667msec (23.4375Hz)

void  WDT_init (uchar cselB3)
{
    ushort  tcsr;

    tcsr = 0xa558 | (cselB3 & 0x07);

    //  set WT=1,TME=0,CKS2-0 to TCSR
    WDT.TCSR.W.WORD = tcsr;

    //  set RSTE=1, RSTS=0 to RSTCSR
    WDT.RSTCSR.W.WORD = 0x5a5f;

    //  set TME=1 (start counting)
    WDT.TCSR.W.WORD = tcsr | 0x20;
}

void  WDT_reset ()
{
    //  clear TCNT
    WDT.TCNT.W.WORD = 0x5a00;
}

int  WDT_check ()
{
    int  wovf;

    //  check WOVF
    wovf = WDT.RSTCSR.R.BYTE & 0x80;

    //  clear WOVF
    if (wovf)
        WDT.RSTCSR.W.WORD = 0xa500;

    //  return wovf
    return wovf;
}

////////////////////////////////////////////////////////////////////////
