
#include <Arduino.h>
#include "YMZ294_Serial.h"

// For SSG YMZ294 / 74HC164 shift register
#define SSG_DATA_PIN           (5) // 164 B(2)
#define SSG_CLOCK_PIN          (6) // 164 CLK(8)
#define SSG_ADDRESS_SELECT_PIN (7) // 294 A0

////////////////////////////////////////////////////////////////
// YMZ294 SSG interface
////////////////////////////////////////////////////////////////
void setupSSG() {
  pinMode(SSG_DATA_PIN,OUTPUT);
  pinMode(SSG_CLOCK_PIN,OUTPUT);
  pinMode(SSG_ADDRESS_SELECT_PIN,OUTPUT);
}

#define SSG_ENVELOPE_SHAPE 0x0D
void SSG_CPU_Interface::write(unsigned int data) {
  shiftOut(SSG_DATA_PIN, SSG_CLOCK_PIN, MSBFIRST, data);
  digitalWrite(writePin, LOW);
  digitalWrite(writePin, HIGH);
}

SSG_CPU_Interface::SSG_CPU_Interface(byte writePin) {
  this->writePin = writePin;
  memset( (void *)registers, 0, sizeof(registers) );
}
void SSG_CPU_Interface::begin() {
  digitalWrite(writePin,HIGH);
  pinMode(writePin,OUTPUT);
}

void SSG_CPU_Interface::setByte(byte reg, byte data) {
  // Check carbon-copy before change
  byte *r = NULL;
  if( reg < SSG_ENVELOPE_SHAPE && *(r = registers + reg) == data )
    return; // Data not changed or 0x0D(Envelope Shape) not specified - skip
  digitalWrite( SSG_ADDRESS_SELECT_PIN, LOW );
  write( (unsigned int)reg );
  digitalWrite( SSG_ADDRESS_SELECT_PIN, HIGH );
  write( (unsigned int)data );
  if( r != NULL ) *r = data; // Make carbon-copy
}

SSG::SSG(byte writePin) : SSG_CPU_Interface(writePin) {
}
void SSG::begin() {
  SSG_CPU_Interface::begin();
  allMixerOff();
}
// Tone Period (12bit) : 0x00 - 0x05 : f = 4MHz / (16 * TP)
void SSG::setTonePeriod(byte channel, unsigned int tp12) {
  if( channel >= SSG_CHANNELS_PER_CHIP ) return;
  setByte(channel *= 2, lowByte(tp12));
  setByte(++channel, highByte(tp12));
}
// Noise Period (5bit) : 0x06 : f = 4MHz / (16 * NP)
void SSG::setNoisePeriod(byte data) { setByte(0x06, data); }
//
// Mixer (6bit) : 0x07
//   --NNNTTT ... Noise/Tone
//   --CBACBA ... channel : on:0 / off:1
void SSG::setMixer(byte channel, boolean isOn, boolean isNoise) {
  byte data = registers[0x07];
  byte mask = (1<<( channel + (isNoise?3:0) ));
  if( isOn ) data &= ~mask; else data |= mask;
  setByte(0x07,data);
}
void SSG::allMixerOff() { setByte(0x07,0xFF); }
// Volume (5bit) : 0x08 - 0x0A : xxxMLLLL
//   M: Envelope on:1 / off:0
//   L: Volume( min:0 - max:15 )
void SSG::setVolume(byte channel, byte volume) {
  if( channel >= SSG_CHANNELS_PER_CHIP ) return;
  setByte(0x08+channel, volume);
}
void SSG::setVolume(byte volume) {
  byte reg, i;
  for( reg = 0x08, i = SSG_CHANNELS_PER_CHIP; i>0; reg++, i-- )
    setByte(reg,volume);
}
// Envelope Period (16bit) : 0x0B - 0x0C : f = 4MHz / (256 * EP)
void SSG::setEnvelopePeriod(unsigned int data) {
  setByte(0x0B, lowByte(data));
  setByte(0x0C, highByte(data));
}
// Envelope Shape (4bit) : 0x0D : -- -- -- -- CONT ATT ALT HOLD
//    This function re-starts counter of envelope generator
void SSG::startEnvelope(byte data) {
  setByte(SSG_ENVELOPE_SHAPE, data);
}
void SSG::startEnvelope() {
  startEnvelope(registers[SSG_ENVELOPE_SHAPE]);
}

