//----------------------------------------------------------------------------
// Talk/2 ISD2560/8xC750 based talker                          OZ9AAR 19940103
//----------------------------------------------------------------------------
#include <reg750.h>

// ISD2560 definitions:
sbit EOM    = P3^2;    // End Of Message
sbit OVF_INT= P1^6;    // OVerFlow, Generates INT1 interrupt.
sbit PD     = P3^1;    // Power Down
sbit CE     = P3^0;    // Chip Enable
sbit PR     = P3^3;    // Play/Record
sbit A0     = P1^5;    // LSB Address
sbit A1     = P3^5;    // MSB Address

// Auxiliary circuit definitions:
sbit PTTIn  = P3^6;    // PTT switch has been activated
sbit PTTOut = P3^7;    // Activate PTT on radio
sbit AuxIn  = P0^1;    // Aux In on ISD2560 (generating Roger beep etc.)
sbit Source = P0^0;    // Mic/Sp to radio or ISD2560

// Pushbuttons/LED definitions:
sbit Msg1   = P1^0;    // Message number 1
sbit Msg2   = P1^1;    // Message number 2
sbit Msg3   = P1^2;    // Message number 3
sbit Msg4   = P1^3;    // Message number 4
sbit Rec    = P1^4;    // Start recording
sbit FKEY   = P1^7;    // Function Key
sbit LEDOut = P3^4;    // LED for various functions

#define Active   0     // Input state for pushbutton pressed down
#define Deactive 1     // Input state for pushbutton released

#define TRUE     1
#define FALSE    0

// Sounds:
#define dah    12      // Duration of dah sound
#define dit     4      // Duration of Dit sound
#define Roger  12      // Duration of Roger beep

//----------------------------------------------------------------------------
// Globals
//----------------------------------------------------------------------------
// What sound we terminate TX with...
#define TerminateNone          0       // No sound at end of TX
#define TerminateRoger         1       // Make a Roger beep at the end of TX
#define TerminateK             2       // Make a 'K' sound at the end of TX
unsigned char Terminate;               // Sound to terminate with

bit    GenerateTone;                   // Make a tone on the 'AuxIn' input on the ISD2560
bit    OVF;                            // Overflow detected from ISD2560
bit    Looping;                        // Message looping activated

unsigned char PresetLoopTime;          // Time in seconds before repeat of a message
unsigned char SecTick;                 // Counts seconds

unsigned char LED;                     // Control of LED
#define OFF     0                      // LED off
#define ON      1                      // LED On
#define FLASH   2                      // Flash LED

//----------------------------------------------------------------------------
// wait for a specific time in 10 mSec
// Based on a 10 MHz crystal
//----------------------------------------------------------------------------
void Wait(unsigned char t) {
  unsigned int i;
  while (t--) for(i=0;i<700; i++);
}

//----------------------------------------------------------------------------
// Check if any of the Msg1..4 is being pressed...
//----------------------------------------------------------------------------
bit CheckMsg(void) {
  bit rc=FALSE;
  if (Msg1==Active || Msg2==Active || Msg3==Active || Msg4==Active)
    rc=TRUE;
  Wait(3); // debounce
  return rc;
}

//----------------------------------------------------------------------------
// Wait for and determine which of the 4 Msg buttons is being pressed
//----------------------------------------------------------------------------
unsigned char WaitAndGetMsgPressed(void) {
  unsigned char rc=0;
//  Wait(1); // debounce
  // Wait for pressed Msg key...
  while (CheckMsg()==FALSE);
//  Wait(1); // debounce
  if (Msg1==Active) rc=1;
  if (Msg2==Active) rc=2;
  if (Msg3==Active) rc=3;
  if (Msg4==Active) rc=4;
  return rc;
}

//----------------------------------------------------------------------------
// Make a tone on the Aux In on the ISD2560 for a specific time in 10 mSec.
//----------------------------------------------------------------------------
void Beep(unsigned char t) {
  GenerateTone=1;
  Wait(t);
  GenerateTone=0;
}

//----------------------------------------------------------------------------
// Say "R" for Roger
//----------------------------------------------------------------------------
void SayRoger(void) {
  PR=1;        // Play mode
  PD=0;        // Power up ISD2560
  Wait(3);     // Powerup delay
  Source=1;    // Put ISD2560 to Radio
  Wait(1);     // Let relay settle
  Beep(dit);
  Wait(dit);
  Beep(dah);
  Wait(dit);
  Beep(dit);
  Wait(dit);
  Source=0;    // SP+Mic back to radio
  Wait(1);     // Let relay settle
  PD=1;        // Power down ISD2560
}

//----------------------------------------------------------------------------
// Outputs a Roger beep
//----------------------------------------------------------------------------
void MakeRogerBeep() {
  PR=1;        // Play mode
  Wait(5);     //
  Source=1;    // Put ISD2560 to Radio
  PTTOut=1;    // Key transmitter
  Wait(1);     // Let relay settle
  Beep(Roger);
  Wait(dit);
  Source=0;    // SP+Mic back to radio
  PTTOut=0;    // Kill transmitter
}

//----------------------------------------------------------------------------
// Outputs a K beep
//----------------------------------------------------------------------------
void MakeKBeep() {
  PR=1;        // Play mode
  Wait(5);     //
  Source=1;    // Put ISD2560 to Radio
  PTTOut=1;    // Key transmitter
  Wait(1);     // Let relay settle
  Beep(dah);
  Wait(dit);
  Beep(dit);
  Wait(dit);
  Beep(dah);
  Wait(dit);
  Source=0;    // SP+Mic back to radio
  PTTOut=0;    // Kill transmitter
}

//----------------------------------------------------------------------------
// Set Address on ISD2560
//----------------------------------------------------------------------------
void SetAddress(unsigned char MsgNum) {
  switch (MsgNum) {
    case 1: A0=0; A1=0; break;
    case 2: A0=1; A1=0; break;
    case 3: A0=0; A1=1; break;
    case 4: A0=1; A1=1; break;
  }
}

//----------------------------------------------------------------------------
// Play a message.
//----------------------------------------------------------------------------
void Play(unsigned int LoopTime) {
  unsigned int i=0;
  // Check if we have to loop playing the message...
  bit Looping = LoopTime ? TRUE : FALSE;
  bit First=TRUE;
  LoopTime *=5; // in 0.2 sec units...

  LED=FLASH;    // Ignite LED
  // Get and set message number...
  while (i==0)
    i=WaitAndGetMsgPressed();
  SetAddress(i);
  // Wait for release...
  while (CheckMsg());

  while (Looping || First) {
    First=FALSE;
    PR=1;      // Play mode
    PD=0;      // Power up ISD2560
    PTTOut=1;  // Key transmitter
    Source=1;  // Put ISD2560 to Radio
    Wait(5);   // Powerup delay
    CE=0;      // Pulse CE low
    CE=1;      // and back high
    LED=ON;    // Ignite LED
    // Now playing...

    // Wait for and stop playing if OVF, EOM, PTT or FKey is pressed
    while (EOM==1 && PTTIn==Deactive && OVF==FALSE && FKEY==Deactive);
    OVF=FALSE;                                        // œœœ
    // If it was PTT or FKey that canceled the message, then
    // cancel the looping...
    if (PTTIn==Active || FKEY==Active)
      Looping=FALSE;     // œœœ
    else {
      // Make termination sound
      if (Terminate==TerminateRoger) MakeRogerBeep();
      if (Terminate==TerminateK) MakeKBeep();
    }

    // Stop Playing...
    Source=0;  // SP+Mic back to radio
    PTTOut=0;  // Kill transmitter
    Wait(5);   // Avoid "BLOP" from SP amplifier
    PD=1;      // Power down ISD2560
    LED=0;     // Kill LED
    // If we are looping, wait looptime...
    if (Looping) {
    LED=FLASH;    // Ignite LED
      // Wait the preset time...
      for (i=0; i<LoopTime; i++) {
        // if abort during the waiting time, stop looping
        if (PTTIn==Active) {
          Looping=FALSE;
          Wait(10);
          while (PTTIn==Active);
          Wait(10);
        }
        if (Looping==FALSE) break;
        Wait(20); //1/5 second
      }
    } // if looping

  } // While (Looping || First)
  LED=OFF;    // Kill LED
  while (CheckMsg());
  while (PTTIn==Active);
  while (FKEY==Active); // œœœ
}

//----------------------------------------------------------------------------
// Record a message.
//----------------------------------------------------------------------------
void Record(void) {
  unsigned int i=0;
  PD=0;      // Power up ISD2560
  Wait(3);   // Powerup delay
  PR=0;      // Record mode
  LED=FLASH; // Ignite LED
  // Get and set message number...
  while (i==0) {
    if (CheckMsg()==TRUE)
      i=WaitAndGetMsgPressed();
    if (Rec==Active) {
      // Wait for release...
      Wait(3);
      while (Rec==Active);
      SayRoger();
      LED=OFF;
      return;
    }
  }


  //while (i==0)
  //  i=WaitAndGetMsgPressed();


  SetAddress(i);
  CE=0;      // Set CE low to begin recording...

  LED=ON;    // Ignite LED
  // Record as long as Message button pressed or we get OVerFlow condition...
  while (CheckMsg() && OVF==FALSE);
  // if overflow, tell it
  if (OVF) {
    OVF=FALSE;
    SayRoger();
  }
  LED=OFF;   // Kill LED
  // Stop recording
  CE=1;      // CE back to high, to end recording
  PR=1;      // Play mode
  PD=1;      // Power down ISD2560
  while (CheckMsg());
  while (PTTIn==Active);
}


//----------------------------------------------------------------------------
// Check state of PTT input
// Returns: 0 Not pressed
//          1 Activating
//          2 Active
//          3 Deactivating
//----------------------------------------------------------------------------
unsigned char CheckPTT(void) {
  static bit Shadow=Deactive;
  bit PTT;
  unsigned char rc=0;
  PTT=PTTIn;   // To avoid bouncing effect
  if (Shadow != PTT) {
    Wait(3);
    if (Shadow==Deactive && PTT==Active)   rc=1;
    if (Shadow==Active   && PTT==Deactive) rc=3;
    Shadow=PTT;
  }
  return rc;
}


//----------------------------------------------------------------------------
// Timer 0 interrupt
//----------------------------------------------------------------------------
void Timer0(void) interrupt 1 using 1 {
static unsigned int Second;
static unsigned int Count;
  // If we are generating a tone, flip AuxIn to make one
  if (GenerateTone==1) AuxIn=!AuxIn;
  // every second:
  if (Second++ > 3000) {
    SecTick++;
    Second=0;
  }
  // Control the LED, On, Off, or flash at 1/6 Hz
  if (Count++ > 500) {
    switch (LED) {
      case OFF:    LEDOut=0; break;
      case ON:     LEDOut=1; break;
      case FLASH:  LEDOut=!LEDOut; break;
    }
    Count=0;
  }
}


//----------------------------------------------------------------------------
// External INT1 interrupt
//----------------------------------------------------------------------------
void ExternalInt1(void) interrupt 2 {
  OVF=TRUE; // Signal OVerFlow condition in ISD2560
}


//----------------------------------------------------------------------------
// Check Press on function key
//----------------------------------------------------------------------------
void CheckFKey(void) {
  if (FKEY==Active) {
    LED=FLASH; // Ignite LED
    Wait(5); // debounce
    while (FKEY==Active);
    // wait for Msg1..Msg4,
    while (Msg1==Deactive && Msg2==Deactive && Msg3==Deactive && Msg4==Deactive && FKEY==Deactive);
    if (Msg1==Active) Terminate=TerminateRoger;                                    // œœœ
    if (Msg2==Active) Terminate=TerminateK;
    if (Msg3==Active) Terminate=TerminateNone;

    if (Msg4==Active) {
      SecTick=0;
      Wait(5);
      while (Msg4==Active);
      Wait(5);
      PresetLoopTime=SecTick;
    }

    // Wait for release...
    while (CheckMsg());
    while (FKEY==Active);   // œœœ
    SayRoger();
    Wait(5); // debounce
    LED=OFF; // Ignite LED
  }
}


//----------------------------------------------------------------------------
// The main
//----------------------------------------------------------------------------
void main(void) {

 //----------------------------------------------------------------------------
 // Initialization of ports and sfr's...
 //----------------------------------------------------------------------------
  // Set outputs
  A0=A1=AuxIn=LED=PTTOut=Source=0;
  // Configure timer 0
  ET0=1;    // Enable timer interrupt
  RTL=0xEB; // Reload timer low byte (0-(Fosc / (12 * Freq)). ex: 0-(10.000.000/(12*3000))=-277
  RTH=0xFE; //   and high byte.                                                           = 0xFEEB
  TR=1;     // Start timer
  // Enable INT1 (OVF from ISD2560)
  IT1=0;    // Edge triggered
  EX1=1;    // Enable INT1 interrupts
  // Enable interrupts
  EA=1;
 //----------------------------------------------------------------------------

  SayRoger();
 //----------------------------------------------------------------------------
 // Main loop
 //----------------------------------------------------------------------------
  while (1) {

    //***********************************
    // Check if PTT is being released...
    //***********************************
    if (CheckPTT()==3) {
      if (Terminate>TerminateNone) {
        PTTOut=1; // Keep Transmitter on Air
        PD=0;  // Power up
        Wait(3);
        // Make termination sound
        if (Terminate==TerminateRoger) MakeRogerBeep();
        if (Terminate==TerminateK) MakeKBeep();
        Wait(5);   // Avoid "BLOP" from SP amplifier
        PD=1;      // Power down ISD2560
      }
    }
    //***********************************
    // Check record...
    //***********************************
    if (Rec==Active) {
      // Wait for release...
      Wait(3);
      while (Rec==Active);
      // Record the message...
      Record();
    }

    //***********************************
    // Check start of play...
    //***********************************
    if (CheckMsg())
      // Play (evt. looping) message...
      Play(PresetLoopTime);

    //***********************************
    // Check functionkey...
    //***********************************
    CheckFKey();
  }
}
