/* sleep.c - go into low power mode for a period of time.
 *  Copyright (C) 2022 by Jed Marti
 *   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 3 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, see https://www.gnu.org/licenses/.
 */


#include <C8051F380.h>
#include <stdint.h>

#include "config.h"
#include "lfxmit.h"
#include "serial.h"
#include "storage.h"


extern __bit              errorThrown;
extern __bit              doit;
extern __xdata floatOrInt IACC;
extern uint16_t           DREG;


static __xdata uint32_t icount;
volatile __bit          t4Expired;


void ISS_TIMER4(void) __interrupt 19
// Timer 4 only for sleep running off the 80 KHz clock. Note the problem with SFRPAGE which
// means all ISS's need to set their page. The default page is 0 but during an interrupt it
// might be set somewhere else.
{
  uint8_t osfr;
  osfr = SFRPAGE;
  SFRPAGE = 15;
  --icount;
  if (icount == 0) t4Expired = 1;     // Time expired.
  TMR4CN &= ~0x80;                    // Clear interrupt.
  SFRPAGE = osfr;

}



void runSleep(void)
// The result of a SLEEP command.
//          SLEEP <expression>;
// where expression is the number of seconds (must be an integer). Go into LF low power mode
// until the time expires. Turn off extra stuff during this period. Note that the DS1085 goes
// back on when you transmit or reset the frequency.
{

  incPC();
  parseExpression();
  if (errorThrown || !doit) return;

  DREG = popST();
  if (tag() != DESC_INT)
  {
    putstring("SLEEP needs Int\n");
    errorThrown = 1;
    return;
  }
  getIACC();
  icount = (uint32_t) IACC.i;

  // Power down the LEDs and DS1085.
  powerDownDS1085();
  LED0 = 0;
  LED1 = 0;
  LED2 = 0;
  LED3 = 0;

    // Set up the low frequency clock, timer 4 and switch to low frequency.
  OSCLCN = 0x83;                  // Enable oscillator, divide by 1 (about 80 KHz).
  while ((OSCLCN & 0x40) == 0);   // Wait for it to stabilize.
  CLKSEL = 0x04;                  // Select as SYSCLK / 1 (80 khtz).
  OSCICN = 0x00;                  // Turn off HF clock.
  
  SFRPAGE = 15;                   // Timer 4 on page 1.
  // Note: the preprocessor screws up the top byte.
  //  TMR4L = TMR4RLL = (65536 - 80000 / 12) & 0xFF;
  //  TMR4H = TMR4RLH = (65536 - 80000 / 12) >> 8;
  TMR4L = TMR4RLL = 0xF6;
  TMR4H = TMR4RLH = 0xE5;
  TMR4CN = 0x04;                  // Enable on SYSCLK / 12
  SFRPAGE = 0;
  t4Expired = 0;
  EIE2 |= 0x10;                   // Enable timer 4 interrupts.
  
  while (!t4Expired);             // Wait for timer to expire.

  // Switch back to HF clock.
  EIE2 &= ~0x10;                  // Turn off interrupts.
  SFRPAGE = 15;
  TMR4CN = 0x00;
  SFRPAGE = 0;
  OSCICN = 0x83;                  // Turn HF clock back on.
  while ((OSCICN & 0x40) == 0);   // Wait for it.
  CLKSEL = 0x03;                  // Switches back to HF clock.
}

   

// EOF
