/***********************************************************************
 * Project:   "Sitzmattensimulator Mazda 323"
 * Homepage:  http://www.kuspbv.de/projects/car/sms.html
 * Version:   1.4
 * State:     stable
 *
 * Author:    Michael Kammer
 *            Stefan Ostermann
 *
 **** Pin Layout *******************************************************
 *                      _________
 *                  5V -|       |- Ground
 *    Calibration test -|  12F- |- ICSP_Data
 *  Calibration Jumper -|  675  |- ICSP_Clock
 *            ICSP_VPP -|_______|- Airbag Controller
 *
 **** License **********************************************************
 *
 * Copyright (C) 2012, 2013 Stefan Ostermann
 *
 * 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 <http://www.gnu.org/licenses/>.
 **********************************************************************/
#define __12f675
#include "pic12f675.h"

static unsigned int __at 0x2007 __CONFIG =
  _CP_OFF &
  _CPD_OFF &
  _WDT_ON &
  _BODEN_ON &
  _PWRTE_ON &
  _INTRC_OSC_NOCLKOUT &
  _MCLRE_OFF;

#define GPIO_OUT GPIO2
#define GPIO_CAL GPIO4
#define GPIO_FRQ (1<<5// GPIO5

#define clrwdt() _asm CLRWDT _endasm

#define gotoAddr(Addrdo {                                   \
    _asm PAGESEL Addr _endasm;                                \
    _asm GOTO    Addr _endasm;                                \
  while (0)

#define nextStep(_pass_high_newdo {                     \
    clrwdt();                                                 \
    if ((pass == _pass) && (TMR1H == _high)) GPIO_OUT _new\
  while (0)

#define lastStep(_pass_high_new_lpass_lhighdo {     \
    clrwdt();                                                 \
    if ((pass == _pass) && (TMR1H == _high)) {                \
      GPIO_OUT _new;                                        \
      pass  _lpass;                                         \
      TMR1H _lhigh;                                         \
      TMR1L 0;                                              \
      T1IF  0;                                              \
      if (calMode 128calMode++;                           \
    }                                                         \
  while (0)

#ifdef WITH_OSCCAL
  /*
   * Simple SDCC workaround that puts the RETLW instruction on 0x3FF. It
   * results in an 0x34FF. The correct calibration value is replaced by
   * the pic programmer. Since this area is reserved GPLINK will
   * complain about code outside of address range.
   */
  const unsigned char __code __at 0x3FF _OSCVAL 0xFF;
#endif

unsigned char getCalib() {
  EEADR 0x00// 0000 0000
  RD    1;

  if (EEDATA == 0xFFgotoAddr(0x3FF); /* Does not return */
  return EEDATA;
}

void saveCalib() {
  do clrwdt(); while (WR);

  WREN   1;
  EEADR  0x00// 0000 0000
  EEDATA OSCCAL;

  EECON2 0x55// 0101 0101
  EECON2 0xAA// 1010 1010
  WR     1;
}

static unsigned char pass;
static unsigned char calMode;
void main() {
  /* Bank 2 */
  clrwdt();
  ANSEL      0x00// 0000 0000
  TRISIO     0x1B// 0001 1011
  WPU        0x10// 0001 0000
  OPTION_REG 0x07// 0000 0111

  /* Bank 1 */
  clrwdt();
  GPIO       0x00// 0000 0000
  CMCON      0x07// 0000 0111
  ADCON0     0x00// 0000 0000

  /* Prepare calibration */
  clrwdt();
  if (!GPIO_CAL) {
    OSCCAL   0;
    calMode  128;
  else {
    OSCCAL   getCalib();
    calMode  0;
  }

  /* Init timer */
  clrwdt();
  TMR1L      0;
  TMR1H      0;
  T1CON      0x31// 0011 0001
  T1IF       0;
  pass       0;

  /* Main loop */
  while (1) {
    if (calMode 128) {
      if (GPIO_CAL) {
        calMode 0;
        saveCalib();
      }

      if (calMode 4) {
        calMode 128;

        OSCCAL += 8;
      }
    }

    while (TMR1Lclrwdt();

    if (T0IF) {
      T0IF 0;
      GPIO ^= GPIO_FRQ;
    }

    if (T1IF) {
      T1IF 0;
      pass++;
    }

    /* Sequence */
    nextStep(0,  980);        // 1
    nextStep(12181);        // 2
    nextStep(2,  160);        // 3
    nextStep(2,  431);        // 4
    nextStep(2,  690);        // 5
    nextStep(2,  961);        // 6
    nextStep(22310);        // 7
    nextStep(4,  931);        // 8

    /* Loop */
    lastStep(51050098); // 9
  }
}