/* dispaly-hc08qy.c
 *
 * Ring: 20-40Hz - 40-150V
 * Linea: 48Vcc a vuoto
 *        12V "of-hook" su 600 Ohm
 * Segnale Caller ID:
 *        0,4 Vrms su circuito aperto.
 *
 * TcycE = 500ns -> Da fronte di salita a fronte di salita di EN
 * Pweh  = 230nS -> Da salita a discesa di EN
 * Tas   = 40nS  -> Da cambiamento di RS a Salita di EN
 * Tah   = 10 nS -> Da discesa di EN a cambio di RS
 * Tdsw  = 80 nS -> Da cambiamento dato a Discesa di EN
 * Th    = 10 nS -> Da discesa di E a cambio dati
 *
 *  DISPLAY:
 *  DB4       15          PTB0
 *  DB5       14          PTB1
 *  DB6       11          PTB2
 *  DB7       10          PTB3
 *
 *  EN_LCD    3           PTB6                LED Verde
 *  RS_LCD    5           PTA4/OSC2/AD2/KBI4  LED Giallo
 *
 *  IN-CIRCUIT
 *  RESET       8     7   PTA3/RST/KBI3 - (pull-up) S4
 *  IRQ1/VTEST  9     5   PTA2/IRQ/KBI2/TCLK (5V)
 *  MOD2        5     3   PTA4/OSC2/AD2/KBI4 (Pull-Down) - RS_LCD
 *  COMMSERIE   13    2   PTA0/AD0/TCH0/KIB0 (Pull-Up) -> In linea
 *  MOD1        12    4   PTA1/AD1/TCH1/KIB1 (Pull-Up) -> Out audio
 *
 *  S5          2         PTB7
 *  RXD-SCI     6         PTB5
 *  TXD-SCI     7         PTB4
 *
 *  MAPPA PIEDINI:
 *  PTA0        COMMSERIE (IC PU)
 *  PTA1        MOD1 (IC - PU) - S5 R18
 *  PTA2        IRQ1/VTEST (IC 5V)
 *  PTA3        RESET (IC PU)
 *  PTA4        RS_LCD (IC/LCD PD)  0=Instruction Register 1=Data Register
 * (PTA5        Oscillatore)
 *
 *  PRB0        DB4 (LCD)
 *  PTB1        DB5 (LCD)
 *  PTB2        DB6 (LCD)
 *  PTB3        DB7 (LCD)
 *  PTB4        TXD-SCI
 *  PTB5        RXD-SCI
 *  PTB6        EN_LCD (LCD) Sul fronte di discesa legge il dato (prima Alto poi Basso)
 *  PTB7        S5
 *  _____
 *  |
 *  | S3
 *  |
 *  | S4 <-- Reset
 *  |
 *  | S5 <--
 *  |
 *  | S1
 *  |
 *  | S2
 *  |
 *  | [R18  ] <--
 *  |
 *
 * Display:
 * Comandi:
 *
 * Clear      0   0   0   0   0   0   0   1   Clears entire display and
 * display                                    sets DDRAM address 0 in
 *                                            address counter.
 *
 * Return     0   0   0   0   0   0   1   —   Sets DDRAM address 0 in
 * home                                       address counter. Also
 *                                            returns display from being
 *                                            shifted to original position.
 *                                            DDRAM contents remain
 *                                            unchanged. (1,5 mS)
 *
 * Entry      0   0   0   0   0   1   I/D S   Sets cursor move direction
 * mode set                                   and specifies display shift.
 *                                            These operations are
 *                                            performed during data write
 *                                            and read.
 *
 * Display    0   0   0   0   1   D   C   B   Sets entire display (D) on/off,
 * on/off                                     cursor on/off (C), and
 * control                                    blinking of cursor position
 *                                            character (B).
 *
 * Cursor or  0   0   0   1   S/C R/L —   —   Moves cursor and shifts
 * display                                    display without changing
 * shift                                      DDRAM contents.
 *
 * Function   0   0   1   DL  N   F   —   —   Sets interface data length
 * set                                        (DL), number of display lines
 *                                            (N), and character font (F).
 *
 * Set        0   1   ACG ACG ACG ACG ACG ACG Sets CGRAM address.
 * CGRAM                                      CGRAM data is sent and
 * address                                    received after this setting.
 *
 * Set        1   ADD ADD ADD ADD ADD ADD ADD Sets DDRAM address.
 * DDRAM                                      DDRAM data is sent and
 * address                                    received after this setting.
 *
 *            I/D = 1: Increment                      DDRAM: Display data RAM *
 *
 *            I/D = 0: Decrement                      CGRAM: Character generator
 *            S   = 1: Accompanies display shift                RAM
 *            S/C = 1: Display shift                  ACG:      CGRAM address
 *            S/C = 0: Cursor move                    ADD:      DDRAM address
 *            R/L = 1: Shift to the right                  (corresponds to cursor
 *            R/L = 0: Shift to the left                   address)
 *            DL  = 1: 8 bits, DL = 0: 4 bits         AC: Address counter used for
 *            N   = 1: 2 lines, N = 0: 1 line              both DD and CGRAM
 *                     5 × 10 dots, F = 0: 5 × 8 dots      addresses
 *            F   = 1:
 *            BF  = 1: Internally operating
 *            BF  = 0: Instructions acceptable
 *
 */

/* 296 nS/Ciclo
 * Utilizzo del timer:
 * Prescaler -> /64 (296*64 ns -> 18.944 uS
 * Timer 52787 -> 1S
 *
 * Prescaler: 1
 * Divisore: 256 (pwm)
 * freq: 13.196,7 Hz
 * 13197
 */
#include <mc68hc908qy.h>

#define CLI __asm  cli  __endasm;

#define SEI __asm  sei  __endasm;

// To avoid Undefined Global '__sdcc_external_startup' referenced by module problem
unsigned char _sdcc_external_startup () { return 0; }

volatile unsigned char g;
volatile unsigned char count;

const char msg[] = "Display Test";
const char msg1[] = "MC68HC08QY";

void delay (unsigned char n) {
#ifndef EMULATOR
 unsigned char i = 0, j = 0, k = 0;
  // Loop annidati di ritardo
  for (i = 0; i < n; i++) {
    for (j = 0; j < 25; j++) {
      for (k = 0; k < 255; k++) {
        __asm nop __endasm;
      }
    }
  }
#endif
}

// Il primo (e solo) parametro è già in A
// TAS (prima)
// TAH (dopo)
// PWeh (impulso)
unsigned char sendbyte_tmp_1_1;
void sendbyte (char ch) {
  __asm
  psha          ; Salva il valore da spedire
  nsa           ; Scambia i nibble
  and   #0xf    ; azzera il nibble alto
  sta   _sendbyte_tmp_1_1
  lda   _PTB
  and   #0xD0   ; Abbassa EN_LCD ed il dato
  ora   _sendbyte_tmp_1_1
  sta   _PTB    ; mette in output il dato
;
  bset  #6,*_PTB
  bclr  #6,*_PTB ; Impulso su EN_LCD
;
  pula          ; recupera il dato
  and   #0xf    ; tiene il nibble basso
  sta   _sendbyte_tmp_1_1
  lda   _PTB
  and   #0xD0   ; Abbassa EN_LCD ed il dato
  ora   _sendbyte_tmp_1_1
  sta   _PTB    ; mette in output il dato
;
  bset  #6,*_PTB
  bclr  #6,*_PTB ; Impulso su EN_LCD
;
  lda   #12
sendbyte_lp6:
  deca
  bne   sendbyte_lp6
;
  __endasm;
}

void sendcmd (char ch) {
  PTA4 = 0;
  sendbyte (ch);
}

void initDisplay () {
  delay (2);
                  // Se sono a 4 bit, leggo un solo "vai a 8 bit"
  sendcmd (0x33); // Se sono a 8 bit, lego due volte "vai a 8 bit"
  delay (2);
  // Ora sono sicuramente a 8 bit, quindi ogni metà viene letta come un intero comando
  sendcmd (0x32); // 8 bit (quindi non fa niente) - 4 bit (quindi commuta a 4 bit)
  delay (2);
  sendcmd (0x28); // 4 bit, 2 linee, matrice 5X10
  delay (1);
  sendcmd (0x8);  // Display spento
  delay (1);
  sendcmd (1);    // Clear display
  delay (1);
  sendcmd (0xe);  // Display acceso, cursore lampeggiante
  delay (1);
}

void clear () {
  sendcmd (1);    // Clear display
  sendcmd (2);    // Return Home
  delay(1);
}

// First line starts at address 0
// Second line starts at address 40
void gotoXY (char x, char y) {
  char ad = (x%40) + ((y & 1) * 40);
  ad |= 0x80;
  sendcmd (ad);
}

void senddata (char ch) {
  PTA4 = 1;
  sendbyte (ch);
}

#define MODULO  13197

int prescaler;
unsigned char value;

/*
void timer_isr (void) __interrupt (6)
{
  if (prescaler > 10000) {
    value += 8;
  }
  CH1F=0;
  TCH1L = value;
  if (--prescaler == 0)
  {
  char i;
    prescaler = MODULO;
    PTA ^= 0x10;  // Inverte il bit del LED (PTA4)
    if (--g == 0) {
      g = 10;

      gotoXY (0,1);
      senddata ('0' + (count/10)%10);
      senddata ('0' + count%10);
      count++;

      PTB &= 0xf0;
      PTB |= (count & 0xf);
    }
  }
  TOF=0;
}
 */

void printString(const char *str) {
  unsigned char i;
  for (i=0; str[i]; i++) {
    senddata (str[i]);
  }
}

void main ()
{
  // Inizializzazione processore e porte
  PTA = 0xff;   // PTA ad 1 meno PTA4 (LED)
  DDRA = 0x10;  // PTA4 in output
  PTAPUE = 0;
  PTB = 0x0;   // PTA ad 1 meno PTA4 (LED)
  DDRB = 0x5f; // PTA4 in output
  PTBPUE = 0xc0;
  // LVIRSTD disables the reset signal from the LVI module, LVIPWRD disables the LVI module, COPD — COP Disable Bit
  CONFIG1 = 0x31;
  // CLK inerno, no reset
  CONFIG2 = 0;

  initDisplay ();
  clear ();


  gotoXY (2,0);
  printString(msg);
  gotoXY (3,1);
  printString(msg1);


  // Prove sul posizionamento delle scritte
  // sendcmd(0x18);  // Shift display left (0 1 2 -> 1 2 3)
  // gotoXY (0,0);    // 00    Ok
  // gotoXY (0,1);    // 40 40 Ok
  // gotoXY (39,0);   // 39    Ok
  // gotoXY (39,1);   // 55 55
  // gotoXY (24,1);   // 40 40
  // gotoXY (20,1);   // 40 40
  // gotoXY (16,1);   // 56 40 (?)
  // gotoXY (18,1);   // 40 40
  // gotoXY (34,1);   // 50 50
  // for (i=0; i<80; i+=2) {
  //   senddata (i/10 + '0');
  //   senddata (i%10 + '0');
  // }

  g = 1;
  count = 9;
  prescaler = 1;

  // Gestione del timer interrupt
  /*
  TSC = 0x30;  // TOIE=0 TSTOP=1 TRST=1 PS=0-> /1
  TMODH=1;
  TMODL=0;     // Divisore = 256
  TSC1 = 0x16;     //
  TCH1H = 0;
  value = 128;
  TSC = 0x40;  // TOIE=1 TSTOP=0 TRST=0 PS=6-> /1
  CLI
  */

  while (1) { // Fine programma (se c'è l'interrupt, allora funzionerà quello.
  }
}
