// digi8b.c

#include "digi8b.h"

// configure the array number location with the port numbers indicated
// and to the bit numbers dictated by the port numbers
// type:
// 0 = unidirctional, no brake
// 1 = unidirectional with brake
// 2 = pwm line, directional line, no brake
// 3 = pwm line, directional line, with brake
// 4 = dual pwm lines -- both high = brake
// 5 = pwm line and two direction lines as for L298
// 255 = end of points = tells isr not to look anymore & saves time
int ConfigureOutput(int arraynumber, 
                    int type, 
                    int ForwardPortNumber, 
                    int ReversePortNumber,
                    int DirectionPortNumber,
                    int BrakePortNumber)
{
  int x;

  if(arraynumber < 0 || arraynumber > 23)
    return 0; // illegal number

  if(ForwardPortNumber < 0 || ForwardPortNumber > 23)
    return 0; // illegal number

  if(OutputControl[arraynumber] == NULL)
  {
    if((OutputControl[arraynumber] = malloc(sizeof(struct OC))) == NULL)
    {
      printf("Not enough memory for output control.\n");
      return 0;
    }
  }

  // zero out members
  memset(OutputControl[arraynumber], 0, sizeof(struct OC));

  if(type == 255)
  {
    OutputControl[arraynumber]-type = 255;
    return 1;
  }

	// set up the forward masks	
  if(!SetPort(arraynumber, ForwardPortNumber,
              &OutputControl[arraynumber]->ForwardPortAddress,
              &OutputControl[arraynumber]->ForwardPortData,
              &OutputControl[arraynumber]->ForwardOnMask,
              &OutputControl[arraynumber]->ForwardOffMask))
    return 0;

  if(!type) // unidirectional no brake has forward pwm only
    return 1;

  if(type == 1 || type == 3 || type == 5) // 1,3,5 use brake line
                                          // (5 uses it for logic lines)
  {
    if(!SetPort(arraynumber, BrakePortNumber,
                &OutputControl[arraynumber]->BrakePortAddress,
                &OutputControl[arraynumber]->BrakePortData,
                &OutputControl[arraynumber]->BrakeOnMask,
                &OutputControl[arraynumber]->BrakeOffMask))
      return 0;
  }

  if(type == 1 || type == 3 || type == 5) // 1,3,5 use direction line
                                          // (5 uses it for logic lines)
  {
    if(!SetPort(arraynumber, DirectionPortNumber,
                &OutputControl[arraynumber]->DirectionPortAddress,
                &OutputControl[arraynumber]->DirectionPortData,
                &OutputControl[arraynumber]->DirectionOnMask,
                &OutputControl[arraynumber]->DirectionOffMask))
      return 0;

    OutputControl[arraynumber]->ReversePortAddress = // reverse is same as forward
      OutputControl[arraynumber]->ForwardPortAddress; // with a direction line

    OutputControl[arraynumber]->ReversePortData = 
      OutputControl[arraynumber]->ForwardPortData;

    OutputControl[arraynumber]->ReverseOnMask = 
      OutputControl[arraynumber]->ForwardOnMask;

    OutputControl[arraynumber]->ReverseOffMask = 
      OutputControl[arraynumber]->ForwardOffMask;
  }

  if(type == 4) // 4 is a dual pwm so has a separate reverse line
  {
    if(!SetPort(arraynumber, ReversePortNumber,
                &OutputControl[arraynumber]->ReversePortAddress,
                &OutputControl[arraynumber]->ReversePortData,
                &OutputControl[arraynumber]->ReverseOnMask,
                &OutputControl[arraynumber]->ReverseOffMask))
      return 0;
  }

  return 1;
}

// portflag settings:
// A CU B CL
// x  x x  x
// low = output, hi = input
// example:
// 0101
// A = out, CU = in, B = out, CL = in

int portflag = 0x0f;  // set by set_up_ppi() -- init to all inputs

// set up flags, etc. for an output port
int SetPort(int arraynumber,
            int PortNumber,
            int *PortAddress,
            int **PortData,
            int *OnMask,
            int *OffMask)
{
  int x;

  if(arraynumber < 0 || arraynumber > 23)
    return 0;

  if(PortNumber < 0 || PortNumber > 23)
    return ClearPort(arraynumber); // error - free port & return 0

  *OnMask = 1 << (PortNumber & 7); // shift by bits[2:0]
  *OffMask = ~*OnMask;

  x = PortNumber >> 3; // the port number is in bits 3 and 4

  switch(x) // 0 = Port A, 1 = Port B, 2 = Port C
  {
    case 0:
    if(portflag & 8) // bit 3 hi = A in, not out
      return ClearPort(arraynumber);
    *PortAddress = ppi_porta; // address for Port A
    *PortData = &porta_val;   // point to Port A data value
    break;

    case 1:
    if(portflag & 2) // bit 1 hi = B in, not out
      return ClearPort(arraynumber);
    *PortAddress = ppi_portb; // address for Port B
    *PortData = &portb_val;   // point to Port B data value
    break;

    case 2:
    if(portflag & 1 && *OnMask &0x0f) // bit 0 hi = CL in, not out
      return ClearPort(arraynumber);
		if(portflag & 4 && *OnMask &0xf0) // bit 2 hi = CU in, not out
      return ClearPort(arraynumber);
    *PortAddress = ppi_portc; // address for Port C
    *PortData = &portc_val;   // point to Port C data value
    break;
  }

  return 1;
}            

// set the last array location so isr won't waste time looking
int SetLast(int arraynumber)
{
  if(arraynumber < 0 || arraynumber > 23)
    return 0;

  if(OutputControl[arraynumber] == NULL)
  {
    if((OutputControl[arraynumber] = malloc(sizeof(struct OC))) == NULL)
    {
      printf("Not enough memory for output control.\n");
      return 0;
    }
  }

  // zero out members
  memset(OutputControl[arraynumber], 0, sizeof(struct OC));

  OutputControl[arraynumber]->type = 255; // show as last
}

// free and  null out error location
int ClearPort(int arraynumber)
{
  if(OutputControl[arraynumber] != NULL)
  {
    free(OutputControl[arraynumber]);
    OutputControl[arraynumber] = NULL;
  }
  return 0; // always 0 -- illegal number
}


// ================================================================
//                    set_up_ppi()
// set up the ppi according to the dictates of the mode argument
// The new modes are as follows, along with the value after shifting:
//                                 bits 3210
// Aout_CUout_Bout_CLout = 0x000 >> 6 = 0000
// Aout_CUout_Bout_CLin =  0x040 >> 6 = 0001
// Aout_CUout_Bin_CLout =  0x080 >> 6 = 0010
// Aout_CUout_Bin_CLin =   0x0C0 >> 6 = 0011
// Aout_CUin_Bout_CLout =  0x100 >> 6 = 0100
// Aout_CUin_Bout_CLin =   0x140 >> 6 = 0101
// Aout_CUin_Bin_CLout =   0x180 >> 6 = 0110
// Aout_CUin_Bin_CLin =    0x1C0 >> 6 = 0111
// Ain_CUout_Bout_CLout =  0x200 >> 6 = 1000
// Ain_CUout_Bout_CLin =   0x240 >> 6 = 1001
// Ain_CUout_Bin_CLout =   0x280 >> 6 = 1010
// Ain_CUout_Bin_CLin =    0x2C0 >> 6 = 1011
// Ain_CUin_Bout_CLout =   0x300 >> 6 = 1100
// Ain_CUin_Bout_CLin =    0x340 >> 6 = 1101
// Ain_CUin_Bin_CLout =    0x380 >> 6 = 1110
// Ain_CUin_Bin_CLin =     0x3C0 >> 6 = 1111
//
// PPI setup bits: 4  3  2 1 0
//                 A  CU   B CL 
//
// CU = C Upper and CL = C Lower
// A 0 at a bit location makes the port an output,
// and a 1 makes it an input
// Consider Aout_CUin_Bout_CLin = 0101 after right shift
// 1. put bits 2 and 3 into positions 3 and 4: 00001000
// 2. add in bits 0 and 1:                     00001001
// 3. OR in 0x80 which says program ports:     10001001
// bit 4 = 0, so A is an output
// bit 3 = 1, so CU is an input
// bit 1 = 0, so B is an output
// bit 0 = 1, so CL is an input
// ================================================================
void set_up_ppi(int mode)
{
  unsigned control = base + 0x23;
  int command;

  // make certain control locations start at NULL
  for(command=0; command<24; command++)
    OutputControl[command] = NULL;

  if(mode >= 15)
    mode >>= 6; // new mode numbers get shifted first

  portflag = mode; // see above -- used to check port setup

  command = (mode & 0x0c) << 1; // shift bits 2 and 3 into positions 3 and 4
  command += (mode & 3); // add in bits 0 and 1
  command |= 0x80; // OR in bit 7 for PPI set up

  outp(control, command); // set according to mode command

} // end set_up_ppi()


// ================================================================
//                         is_closure
// 1. Return -1 error indicator if the input
//    is less than 1 or greater than 67.
//
// 2. If there is a fall-through from the above and the input
//    is less than 4, return the status based on switch_port.
//
// 3. If there is a fall-through from both of the above, then
//    return the status based on the matrix:

//             |-----------------------|
// Port A bit 0| 4| 5| 6| 7| 8| 9|10|11|
// Port A bit 1|12|13|14|15|16|17|18|19|
// Port A bit 2|20|21|22|23|24|25|26|27|
// Port A bit 3|28|29|30|31|32|33|34|35|
// Port A bit 4|36|37|38|39|40|41|42|43|
// Port A bit 5|44|45|46|47|48|49|50|51|
// Port A bit 6|52|53|54|55|56|57|58|59|
// Port A bit 7|60|61|62|63|64|65|66|67|
//             |-----------------------|
//  Port B bits  0  1  2  3  4  5  6  7

// ================================================================
int is_closure(int input)
{

  if(input < 1 || input > 67) // if the input is less than 1 or greater
    return -1;                // than 67, then return -1 showing an error

  // we fell through the above so see if input is less than 4
  if(input < 4)
    return ((inp(switch_port) >> (input + 3)) & 1) ^ 1; // yes, return using switch_port

  // input is >= 4 so look at the matrix
  // by first setting up Port A to take the appropriate row bit low
  porta_val = (~(1 << ( (input - 4) / 8) )) & 0xff;

  // clear the appropriate Port A bit 
  outp(ppi_porta, porta_val);

  // determine what column bit to look at for this input
  portb_mask = (1 << ((input - 4) % 8)) & 0xff;

  // a closure will cause a low, so invert the return 
  if(!(inp(ppi_portb) & portb_mask))
    return 1;

  return 0;

}// end is_closure()

// get the port -- this will grow into an auto-detect function in the future 
unsigned oldget_port(void)
{
  base = 0x200; // all switches on -- change if desired
  switch_port = base + 0x18;
  ppi_porta = base + 0x20;
  ppi_portb = base + 0x21;
  ppi_portc = base + 0x22;
  return base;
  
} // end get_port()

// find hardware port if one exists
unsigned get_port(void)
{
  int x;
  static unsigned local_port;
  unsigned int not_ready_count,ready_count;

  if(local_port == 32767)
    return 0;

  if(local_port > 0)
    return local_port;

  for(x=0x200; x<0x3c0; x+=0x40)
  {
    not_ready_count = 32767;
    ready_count = 32767;

    outp(x,0); /* start conversion */

    while((inp(x+0x18) & 0x80) && --not_ready_count); /* wait for not ready */

    while(!(inp(x+0x18) & 0x80) && --ready_count); /* wait for ready */

//printf("ready_count = %d\n",ready_count);

    if(ready_count < 32767 && ready_count > 0)
    {
      local_port = base = x;
      switch_port = base + 0x18;
      ppi_porta = base + 0x20;
      ppi_portb = base + 0x21;
      ppi_portc = base + 0x22;
      return base;
    }
  }

  local_port = 32767;
  return 0;
}


void blinker(long on, long off)
{
  int x;
  long y,z;

  for(x=0x80; x>0; x>>=1)
  {
    outp(ppi_porta, x);
    for(y=0L; y<on; y++);

    outp(ppi_porta, 0);
    for(z=0L; z<off; z++);
  }

  for(x=2; x<0x80; x<<=1)
  {
    outp(ppi_porta, x);
    for(y=0L; y<on; y++);

    outp(ppi_porta, 0);
    for(z=0L; z<off; z++);
  }

}

void blinker2(long on)
{
  int x;
  long z;

  porta_val = 0;

  for(x=0x80; x>0; x>>=1)
  {
    porta_val |= x;
    outp(ppi_porta, porta_val);
    printf("%3X",porta_val);
    for(z=0L; z<on; z++);
  }

  for(x=1; x<=0x80; x<<=1)
  {
    porta_val ^= x;
    outp(ppi_porta, porta_val);
    printf("%3X",porta_val);
    for(z=0L; z<on; z++);
  }
  puts("");
}

void btoa(void)
{
    outp(ppi_porta, ~inp(ppi_portb));
}

void motor(long on, long off)
{
  int x;
  long y,z;

  printf("ON  ");
  outp(ppi_porta, 0xff);
  for(y=0L; y<on; y++);

  printf("OFF ");
  outp(ppi_porta, 0);
  for(z=0L; z<off; z++);
}

void portaon(void)
{
  outp(ppi_porta, 0xff);
}

void portaoff(void)
{
  outp(ppi_porta, 0);
}

void portboff(void)
{
  outp(ppi_portb, 0);
}

void portcoff(void)
{
  outp(ppi_portc, 0);
}

// free the output control structures
void FreeOutputControl(void)
{
  int x;

  for(x=0; x<24; x++)
  {
    if(OutputControl[x] != NULL)
      free(OutputControl[x]);
  }
}


// turn on an output node
int TurnOn(int arraynumber)
{
  if(OutputControl[arraynumber] == NULL)
  {
    printf("can't turn on -- location %d not set up\n",arraynumber);
    return 0; // node not set up
  }

  // signal service routine that this is not pwm
  OutputControl[arraynumber]->ForwardSetOn = -1L;

  // keep existing bits and OR this one in
  *OutputControl[arraynumber]->ForwardPortData 
  |= OutputControl[arraynumber]->ForwardOnMask;

  // put the result in this node's port register
  outp(OutputControl[arraynumber]->ForwardPortAddress, 
  *OutputControl[arraynumber]->ForwardPortData);

  return 1;
}


// turn off an output node
int TurnOff(int arraynumber)
{
  if(OutputControl[arraynumber] == NULL)
  {
    printf("can't turn off -- location %d not set up\n",arraynumber);
    return 0; // node not set up
  }

  // signal service routine that this is not pwm
  OutputControl[arraynumber]->ForwardSetOn = -1L;

  // keep existing bits but remove this one
  *OutputControl[arraynumber]->ForwardPortData 
  &= OutputControl[arraynumber]->ForwardOffMask;

  // put the result in this node's port register
  outp(OutputControl[arraynumber]->ForwardPortAddress, 
  *OutputControl[arraynumber]->ForwardPortData);

  return 1;
}


// ======================== old config routine ============================

// configure the array number location to a port
// and bit number dictated by portselect
int oldConfigureOutput(int arraynumber, int portselect)
{
  int x;

  if(arraynumber < 0 || arraynumber > 23)
  {
    printf("arraynumber error --- %d\n",arraynumber);
    return 0; // illegal number
  }

  if(portselect < 0 || portselect > 23)
  {
    printf("portselect error --- %d\n",portselect);
    return 0; // illegal number
  }

  if(OutputControl[arraynumber] == NULL)
  {
    if((OutputControl[arraynumber] = malloc(sizeof(struct OC))) == NULL)
    {
      printf("Not enough memory\n");
      return 0;
    }
  }

  // zero out members
  memset(OutputControl[arraynumber], 0, sizeof(struct OC));

  x = portselect >> 3; // the port number is in bits 3 and 4

  switch(x) // 0 = Port A, 1 = Port B, 2 = Port C
  {
    case 0:
    OutputControl[arraynumber]->ForwardPortAddress = ppi_porta; // address for Port A
    OutputControl[arraynumber]->ForwardPortData = &porta_val;   // point to Port A data value
    break;

    case 1:
    OutputControl[arraynumber]->ForwardPortAddress = ppi_portb; // address for Port B
    OutputControl[arraynumber]->ForwardPortData = &portb_val;   // point to Port B data value
    break;

    case 2:
    OutputControl[arraynumber]->ForwardPortAddress = ppi_portc; // address for Port C
    OutputControl[arraynumber]->ForwardPortData = &portc_val;   // point to Port C data value
    break;
  }

  // shift by bits[2:0]
  OutputControl[arraynumber]->ForwardOnMask = 1 << (portselect & 7);
  OutputControl[arraynumber]->ForwardOffMask = ~OutputControl[arraynumber]->ForwardOnMask;

 /* add double slash rem to print debug statements

  printf("arraynum=%02d port select=%02d ",arraynumber,portselect);

  printf("Addr=%#X "
  ,OutputControl[arraynumber]->ForwardPortAddress);

  printf("*Data=%X "
  ,*OutputControl[arraynumber]->ForwardPortData);

  printf("onmsk=");
  for(x=128; x>0; x/=2)
  {
    if(x & OutputControl[arraynumber]->ForwardOnMask)
      printf("1");
    else printf("0");
  }

  printf(" offmsk=");
  for(x=128; x>0; x/=2)
  {
    if(x & OutputControl[arraynumber]->ForwardOffMask)
      printf("1");
    else printf("0");
  }
  printf("\n");

 add double slash rem to print debug statements */ 

  return 1;
}

// end digi8b.c
 



