// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//  midi.c - main program for Tiny Synth open project
//  Copyright (C) 2006 miguel angel labolida
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//  implemented with AT89C2051 with 24 MHz and 2k(800h) flash memory
//  MIDI input is controlled by the serial interface associated to
//  timer 1 in mode 2 (8 bits auto reload).
//  The digital oscillator (DCO) frequency is controlled by the 
//  timer 0 in mode 1 (16 bits without auto reload).
//  compiled with sdcc --no-pack-iram 
//__code char versn[] = {"_Midi109_"};    // adapt to Tiny 4 ATOM - Leds
//__code char versn[] = {"_Midi110_"};    // sample buffer
//__code char versn[] = {"_Midi111_"};  // ASR envelope signal
__code char versn[] = {"__Midi112__"};  // AR envelope signal

#include <8051.h>                       // standard 8051 header
#include <string.h>
#define INT_DISABLE (EA = 0)            // disable interrupts
#define INT_ENABLE  (EA = 1)            // enable interrupts
#define UCHAR unsigned char

#define NOTE_ON     0x90                // midi command note on
#define NOTE_OFF    0x80                // midi command note off
                                        // pin 6 P3_2 INT0 portamento
                                        // pin 16 to 19 P1_4 to 7 vave out
#define BUTTON_WAVE P3_1                // pin 3 wave select button
#define LED_1       P3_3                // pin 7 INT1 indicator LED 1
#define LED_2       P3_4                // pin 8 T0 indicator LED 2
#define LED_3       P3_5                // pin 9 T1 indicator LED 3
#define LED_4       P3_7                // pin 11 indicator LED 4
#define AR_REF      P1_0                // pin 12 AR envelope reference(+)
#define AR_LEV      P1_1                // pin 13 AR envelope comparator(-)

static void dco_ini(void);
static void dco_int(void) interrupt 1 using 2;
static void midi_ini(void);
static void midi_int(void) interrupt 4 using 1;
static __data UCHAR input_byte;         // input byte MIDI interface
static __data UCHAR midi_state;         // parser automata state
static __data UCHAR note_curr;          // current note number
static __data UCHAR note_next;          // future note number
static __data UCHAR note_count;         // note on counter
static __data UCHAR frqdiv_lo;          // freq.divider timer 0 low
static __data UCHAR frqdiv_hi;          // freq.divider timer 0 high
static __data UCHAR wave_current;       // index current wave 0 to 3
static __data UCHAR wave_sample[16];    // waveform sample table
static __data UCHAR wave_buffer;        // wave sample and envelope buffer
                                        // bits 7 - 4 wave high to low
                                        // bit 3 ASR - bit 2 AR
                                        // bit 1 input comp - bit 0 refer.
static __data unsigned char toggle;     // DEBUG
#include "freq.c"                       // frequency divider table
#include "wave.c"                       // waveform table
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void main()
{
    static __data int wave_debounce;    // debounce counter
    __data unsigned char i;             // table notes index

    midi_ini();
    dco_ini();
    IT0 = 1;                            // portamento 
    wave_current = 0;                   // default waveform
    LED_1 = 0;                          // initialize default led
    LED_2 = LED_3 = LED_4 = 1;          // initialize leds
    memcpy(wave_sample, wave_table[0],  // load default sample wave
        sizeof(wave_sample));
    wave_buffer = 0x03;                 // initialize AR ASR A0 A1 high z
    while(1)
    {
        if (IE0)                        // INT0 portamento
        {   IE0 = 0;
            if (note_next > note_curr)
            {   note_curr ++;
                i = (note_curr - 29)*2; // index of note table
                frqdiv_lo = note29[i+1];// freq.divider timer 0 low
                frqdiv_hi = note29[i];  // freq.divider timer 0 high
            }
            if (note_next < note_curr)
            {   note_curr --;
                i = (note_curr - 29)*2; // index of note table
                frqdiv_lo = note29[i+1];// freq.divider timer 0 low
                frqdiv_hi = note29[i];  // freq.divider timer 0 high
            }
        }

        if ((! BUTTON_WAVE)             // wave selection button on
        &&  (! wave_debounce))          // debounce complete
        {   wave_debounce = 500;        // load counter
            wave_current ++;            // change wave index
            wave_current &= 0x03;       // truncate
            memcpy(wave_sample,         // store waveform
                wave_table[wave_current], 
                sizeof(wave_sample));
            LED_1 = LED_2 = LED_3 = LED_4 = 1; // initialize leds
            switch(wave_current)        // preparing
            {
            case 0: LED_1 = 0; break;   // wave leds indicators
            case 1: LED_2 = 0; break;
            case 2: LED_3 = 0; break;
            case 3: LED_4 = 0; break;
            }
        }
        if (BUTTON_WAVE                 // wave selection button off
        &&  wave_debounce)              // if counter
            wave_debounce --;           // debounce

        if (! P3_6)                     // analog comparator test
            wave_buffer &= ~0x04;       // AR envelope off

        for (i = 0; i < 10 ;i ++)       // delay
        {   i = i;
        }

    }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void dco_ini(void) 
{
    INT_DISABLE;                        // disable interrupts
    TMOD &= 0xF0;                       // clear timer 0 mode bits
    TMOD |= 0x01;                       // timer 0 in mode 1
    ET0 = 1;                            // enable timer 0 interrupt
    TR0 = 1;                            // enable timer 0 counter
    INT_ENABLE;                         // enable interrupts
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void dco_int(void) interrupt 1 using 2
{
static __data UCHAR w_i = 0;            // sample index
    INT_DISABLE;                        // disable interrupts
    TR0 = 0;                            // stop timer 0
    TH0 = frqdiv_hi;                    // load timer value high
    TL0 = frqdiv_lo;                    // load timer value low
    wave_buffer &= 0x0F;                // clear wave bits
    wave_buffer |= wave_sample[w_i ++]; // set wave sample
    P1 = wave_buffer;                   // output wave sample
    w_i &= 0x0F;                        // normalize interval 0 - 15
    TR0 = 1;                            // start timer 0
    INT_ENABLE;                         // enable interrupts
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void midi_ini(void) 
{
    INT_DISABLE;
    TR1 = 0;                            // stop timer
    ET1 = 0;                            // disable timer 1 interrupt
    TI = 0;                             // clear transmit - not used
    RI = 0;                             // clear receiver interrupt
    SCON = 0x50;                        // mode 1 and enable receipt
    PCON = 0x80;                        // smod 1: baudrate doubler
    TMOD &= ~0xF0;                      // clear timer 1 mode bits
    TMOD |= 0x20;                       // timer 1 in mode 2
                                        // baud rate counter = 0xfc
    TH1 = (unsigned char)(256 - (24000000L / (16L * 12L * 31250L)));
    ES = 1;                             // enable serial interrupt
    TR1 = 1;                            // enable counter timer 1
    INT_ENABLE;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void midi_int(void) interrupt 4 using 1
{
    static __data UCHAR note_temp;      // table notes index
    __data UCHAR aux_command;           // midi command


    RI = 0;                             // reset int. MUST BE 1rst!
    input_byte = SBUF;                  // store input byte
    aux_command = input_byte & 0xF0;    // clear channel
    if (aux_command == NOTE_ON)         // midi command parsing
    {   midi_state = 1;
        return;
    }
    if (aux_command == NOTE_OFF)        // midi command parsing
    {   midi_state = 4;
        return;
    }
    if ((midi_state == 1)               // midi note number parsing
    ||  (midi_state == 4))              // for both note on and off
    {   midi_state ++;                  // appoint to next state
        note_temp = input_byte;         // store future note number
        return;
    }
    if (midi_state == 2)                // velocity value parsing
    {   midi_state ++;                  // appoint to next state
        if (input_byte)                 // if greater 0 is note on
        {   wave_buffer |= 0x04;        // AR envelope on
            note_count ++;              // increment note on counter
            note_next = note_temp;      // store future note number
        }
        else                            // if velocity 0 is note off
            note_count --;              // decrease note on counter
    }
    if (midi_state == 5)                // velocity value parsing
    {   midi_state ++;                  // for note off command
        note_count --;                  // decrease note on counter
    }
    wave_buffer =                       // set ASR envelope
    (note_count) ? wave_buffer | 0x08   // ASR 1
                 : wave_buffer & ~0x08; // ASR 0 when notes off
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -