///////////////////////////////////////////////////////////////////////////////
//
// M68681.hxx 
//
// The Motorola 68681 DUART
//
// Sim68000 "Motorola 68000 Simulator"
// Copyright (c) 1993
// By: Bradford W. Mott
// Novermber 24,1993
//
///////////////////////////////////////////////////////////////////////////////
// $Id: M68681.cxx,v 1.1 1996/08/02 15:01:10 bwmott Exp $
///////////////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <strstream.h>
#include <string>

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

#include "BasicCPU.hxx"
#include "M68681.hxx"

// Callback types
#define READ_A_CALLBACK   1
#define WRITE_A_CALLBACK  2
#define READ_B_CALLBACK   3
#define WRITE_B_CALLBACK  4

// The duration of the default read events
#define DEFAULT_READ_CALLBACK_DURATION  50000

// Channel/port states 
#define ACTIVE   1
#define INACTIVE 0

// Bit fields in status registers
#define RxRDY    1
#define FFULL    2
#define TxRDY    4
#define TxEMT    8

///////////////////////////////////////////////////////////////////////////////
// Baudrate table (contains event duration times in micro-seconds)
///////////////////////////////////////////////////////////////////////////////
long M68681::baudrate_table[32] = {
    160000,    // 50     baudrate (i.e. (8*1000000)/50) uS)
    72727,     // 110
    59479,     // 134.5
    40000,     // 200
    26666,     // 300
    13333,     // 600
    6666,      // 1200
    7619,      // 1050
    3333,      // 2400
    1666,      // 4800
    1111,      // 7200
    833,       // 9600
    208,       // 38400
    1666,      // 4800   (Should be timer but that's not implemented)
    1666,      // 4800   (Should be IP4-16X but that's not implemented)
    1666,      // 4800   (Should be IP4-1X but that's not implemented)
    106666,    // 75
    72727,     // 110
    59479,     // 134.5
    53333,     // 150
    26666,     // 300
    13333,     // 600
    6666,      // 1200
    4000,      // 2000
    3333,      // 2400
    1666,      // 4800
    4444,      // 1800
    833,       // 9600
    416,       // 19200
    1666,      // 4800   (Should be timer but that's not implemented)
    1666,      // 4800   (Should be IP4-16X but that's not implemented)
    1666       // 4800   (Should be IP4-1X but that's not implemented)
  };

///////////////////////////////////////////////////////////////////////////////
// The Constructor
///////////////////////////////////////////////////////////////////////////////
M68681::M68681(const string& args, BasicCPU& cpu)
    : BasicDevice("M68681", args, cpu)
{
  istrstream in(args.data(), args.size());
  string keyword, equals;
  int portAStdIOFlag;
  int portBStdIOFlag;

  // Scan "BaseAddress = nnnn"
  in >> keyword >> equals >> hex >> base_address;
  if((!in) || (keyword != "BaseAddress") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }
  base_address *= cpu.Granularity();

  // Scan "OffsetToFirstRegister = [0-8]"
  in >> keyword >> equals >> hex >> offset_to_first_register;
  if((!in) || (keyword != "OffsetToFirstRegister") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }
  offset_to_first_register *= cpu.Granularity();

  // Scan "OffsetBetweenRegisters = [1-8]"
  in >> keyword >> equals >> hex >> offset_between_registers;
  if((!in) || (keyword != "OffsetBetweenRegisters") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }
  offset_between_registers *= cpu.Granularity();

  // Scan "InterruptLevel = [1-7]"
  in >> keyword >> equals >> hex >> interrupt_level;
  if((!in) || (keyword != "InterruptLevel") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }

  // Scan "PortAStandardInputOutputFlag = 0|1" 
  in >> keyword >> equals >> portAStdIOFlag;
  if((!in) || (keyword != "PortAStandardInputOutputFlag") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }

  // Scan "PortBStandardInputOutputFlag = 0|1" 
  in >> keyword >> equals >> portBStdIOFlag;
  if((!in) || (keyword != "PortBStandardInputOutputFlag") || (equals != "="))
  {
    ErrorMessage("Invalid initialization arguments!");
    return;
  }

  int loc;
  loc = args.find("PortACommand = ") + 15;
  string portACommand(args, loc, args.find(" PortBCommand = ") - loc);

  loc = args.find("PortBCommand = ") + 15;
  string portBCommand(args, loc, args.size() - loc);


  // Default Invalid values for everything
  coma_pid = coma_read_id = coma_write_id = -1;
  comb_pid = comb_read_id = comb_write_id = -1;
 
  // Startup the process for port a
  if(portACommand != "")
  {
    if(!StartPortCommand(portACommand, portAStdIOFlag,
        coma_read_id, coma_write_id, coma_pid))
    {
      ErrorMessage("Problem starting port a's process!");
      return;
    }
  }

  // Startup the process for port b
  if(portBCommand != "")
  {
    if(!StartPortCommand(portBCommand, portBStdIOFlag,
        comb_read_id, comb_write_id, comb_pid))
    {
      ErrorMessage("Problem starting port b's process!");
      return;
    } 
  }

  // Reset the DUART to its startup state
  Reset();

  if(coma_read_id != -1)
  {
    // Schedule an event to start checking the A pipe
    (cpu.eventHandler()).Add(this, READ_A_CALLBACK, 0,
        DEFAULT_READ_CALLBACK_DURATION);
  }

  if(comb_read_id != -1)
  {
    // Schedule an event to start checking the B pipe
    (cpu.eventHandler()).Add(this, READ_B_CALLBACK, 0,
        DEFAULT_READ_CALLBACK_DURATION);
  }
}

///////////////////////////////////////////////////////////////////////////////
// Startup a process 
///////////////////////////////////////////////////////////////////////////////
bool M68681::StartPortCommand(const string& command, bool std_flag,
    int &read_pipe_id, int &write_pipe_id, pid_t &pid)
{
  int read_pipe_ids[2];
  int write_pipe_ids[2];

  // Create the write pipe 
  if(pipe(write_pipe_ids))
    return false;

  // Create the read pipe 
  if(pipe(read_pipe_ids))
    return false;

  // Set the NONBLOCK flag for the reading pipe
  if(fcntl(read_pipe_ids[0], F_SETFL, O_NONBLOCK) == -1)
    return false;

  // Set the close on exec bits
  if(fcntl(write_pipe_ids[1], F_SETFD, 1) == -1)
    return false;
  if(fcntl(read_pipe_ids[0], F_SETFD, 1) == -1)
    return false;

  if((pid = fork()) == 0)
  {
    // See if the pipes should be connected to STDIN and STDOUT
    if(std_flag)
    {
      // Close STDIN and put the pipe in its place
      if(dup2(write_pipe_ids[0],0) == -1)
      close(write_pipe_ids[0]);

      // Close STDOUT and put the pipe in its place
      if(dup2(read_pipe_ids[1],1) == -1)
      close(read_pipe_ids[1]);

      // Dup the STDOUT to STDERR
      dup2(1, 2);
    } 
    else  
    {
      // If pipe isn't connected to STDIN and STDOUT then we will
      // close STDIN, STDOUT, and STDERR and put the read pipe
      // at descriptor 3 and the write pipe at descriptor 4.

      // First dup down to stdin and stdout
      dup2(write_pipe_ids[0], 0);
      close(write_pipe_ids[0]);
      dup2(read_pipe_ids[1], 1);
      close(read_pipe_ids[1]);

      // Now dup read pipe to 3 and write pipe to 4
      dup2(0, 3);
      dup2(1, 4);

      // Close STDIN, STDOUT, STDERR
      close(0);
      close(1);
      close(2);
    }

    char execCommand[1024];
    ostrstream out(execCommand, 1024, ios::out);
    out << "exec " << command << flush;
    execCommand[out.pcount()] = 0;

    if(execl("/bin/sh","sh","-c", execCommand, NULL))
      exit(0);

    return false;
  }
  else
  {
    close(write_pipe_ids[0]);
    close(read_pipe_ids[1]);
    read_pipe_id=read_pipe_ids[0];
    write_pipe_id=write_pipe_ids[1];

    return true;
  }
}


M68681::~M68681()
{
  // We need to destory the Command Process
  if(coma_pid != -1)
  {
    close(coma_read_id);
    close(coma_write_id);
    kill(coma_pid,SIGKILL);
  }

  // We need to destory the Command Process
  if(comb_pid != -1)
  {
    close(comb_read_id);
    close(comb_write_id);
    kill(comb_pid,SIGKILL);
  }
}

///////////////////////////////////////////////////////////////////////////////
// Perform a reset on the DUART 
///////////////////////////////////////////////////////////////////////////////
void M68681::Reset()
{
  BasicDevice::Reset();              // Do the BasicDevice reset

  IVR = 0x0f;                        // 68000's Uninitialized interrupt vector
  ISR = 0;                           // Clear the interrupt status register
  IMR = 0;                           // Clear the interrupt mask register
  SRA = 0;                           // Clear status register A
  SRB = 0;                           // Clear status register B
  receiver_a_state = INACTIVE;       // Set the channels to inactive state
  transmitter_a_state = INACTIVE;
  receiver_b_state = INACTIVE;
  transmitter_b_state = INACTIVE;

  mr1a_pointer=1;                    // The mr?a address points to MR1A
  mr1b_pointer=1;                    // The mr?b address points to MR1B
}

///////////////////////////////////////////////////////////////////////////////
// Check to see if the address maps into this device
///////////////////////////////////////////////////////////////////////////////
bool M68681::CheckMapped(unsigned long address) const
{
 return (address >= base_address) && (address <= base_address +
     offset_to_first_register + 15 * offset_between_registers);
}

///////////////////////////////////////////////////////////////////////////////
// Read one of the registers in the DUART
///////////////////////////////////////////////////////////////////////////////
unsigned char M68681::Peek(unsigned long addr)
{
  addr -= (base_address+offset_to_first_register);

  if(addr==0)         // Mode Register A
  {
     if(mr1a_pointer)
     {
       mr1a_pointer=0;
       return(MR1A);
     }
     else
     {
       return(MR2A);
     }
  }
  else if(addr==1*offset_between_registers)    // Status Register A
  {
    return(SRA);
  }
  else if(addr==3*offset_between_registers)    // Receive Buffer A
  {
    SRA &= ~RxRDY;
    SRA &= ~FFULL;
    SetInterruptStatusRegister();
    return(RBA);
  }
  else if(addr==5*offset_between_registers)    // Interrupt Status Register
  {
    return(ISR);
  }
  else if(addr==8*offset_between_registers)    // Mode Register B
  {
     if(mr1b_pointer)
     {
       mr1b_pointer=0;
       return(MR1B);
     }
     else
     {
       return(MR2B);
     }
  }
  else if(addr==9*offset_between_registers)    // Status Register B
  {
    return(SRB);
  }
  else if(addr==11*offset_between_registers)   // Receive Buffer B
  {
    SRB &= ~RxRDY;
    SRB &= ~FFULL;
    SetInterruptStatusRegister();
    return(RBB);
  }
  else if(addr==12*offset_between_registers)   // Interrupt-vector register
  {
    return(IVR);
  }
  else
    return(0);
}

///////////////////////////////////////////////////////////////////////////////
// Write to one of the registers in the DUART
///////////////////////////////////////////////////////////////////////////////
void M68681::Poke(unsigned long addr, unsigned char c)
{
  addr -= (base_address+offset_to_first_register);

  if(addr==0)         // Mode Register A
  {
     if(mr1a_pointer)
     {
       mr1a_pointer=0;
       MR1A=c;
       SetInterruptStatusRegister();
     }
     else
     {
       MR2A=c;
     }
  }
  else if(addr==offset_between_registers)    // Clock-select Register A
  {
    CSRA=c;
  }
  else if(addr==2*offset_between_registers)    // Command Register A
  {
    switch ((c & 0x70) >> 4)
    {
      case 1:    // Reset mode register pointer
        mr1a_pointer=1;
        break;
      case 2:    // Reset receiver
        receiver_a_state=INACTIVE;
        SRA &= ~RxRDY;
        SRA &= ~FFULL;
        SetInterruptStatusRegister();
        break;
      case 3:    // Reset transmitter
        transmitter_a_state=INACTIVE;
        SRA &= ~TxRDY;
        SRA &= ~TxEMT;
        SetInterruptStatusRegister();
        break;
      case 4:    // Clear error status
        SRA &= 0x0f;
        break;
    }
    switch ((c & 0x0c) >> 2)
    {
      case 1:    // Enable transmitter
        transmitter_a_state=ACTIVE;
        SRA |= TxRDY;
        SRA |= TxEMT;
        SetInterruptStatusRegister();
        break;
      case 2:    // Disable transmitter
        transmitter_a_state=INACTIVE;
        SRA &= ~TxRDY;
        SRA &= ~TxEMT;
        SetInterruptStatusRegister();
        break;
    }
    switch (c & 0x3)
    {
      case 1:    // Enable receiver
        receiver_a_state=ACTIVE;
        SRA &= ~RxRDY;
        SRA &= ~FFULL;
        SetInterruptStatusRegister();
        break;
      case 2:
        receiver_a_state=INACTIVE;
        break;
    }
  }
  else if (addr==3*offset_between_registers)   // Transmitter Buffer A
  {
    TBA=c;           // Store the transmit data
    SRA &= ~TxRDY;   // Mark register as full
    SRA &= ~TxEMT;
    SetInterruptStatusRegister();

    if(ACR & 128)
      (myCPU.eventHandler()).Add(this, WRITE_A_CALLBACK, 0,
          baudrate_table[(CSRA & 0xf)+16]);
    else
      (myCPU.eventHandler()).Add(this, WRITE_A_CALLBACK, 0,
          baudrate_table[(CSRA & 0xf)]);
  }
  else if(addr==5*offset_between_registers)    // Interrupt Mask Register
  {
    IMR=c;
  }
  else if(addr==8*offset_between_registers)    // Mode Register B
  {
     if(mr1b_pointer)
     {
       mr1b_pointer=0;
       MR1B=c;
       SetInterruptStatusRegister();
     }
     else
     {
       MR2B=c;
     }
  }
  else if(addr==9*offset_between_registers)    // Clock-select Register B
  {
    CSRB=c;
  }
  else if(addr==10*offset_between_registers)   // Command Register B
  {
    switch ((c & 0x70) >> 4)
    {
      case 1:    // Reset mode register pointer
        mr1b_pointer=1;
        break;
      case 2:    // Reset receiver
        receiver_b_state=INACTIVE;
        SRB &= ~RxRDY;
        SRB &= ~FFULL;
        SetInterruptStatusRegister();
        break;
      case 3:    // Reset transmitter
        transmitter_b_state=INACTIVE;
        SRB &= ~TxRDY;
        SRB &= ~TxEMT;
        SetInterruptStatusRegister();
        break;
      case 4:    // Clear error status
        SRB &= 0x0f;
        break;
    }
    switch ((c & 0x0c) >> 2)
    {
      case 1:    // Enable transmitter
        transmitter_b_state=ACTIVE;
        SRB |= TxRDY;
        SRB |= TxEMT;
        SetInterruptStatusRegister();
        break;
      case 2:    // Disable transmitter
        transmitter_b_state=INACTIVE;
        SRB &= ~TxRDY;
        SRB &= ~TxEMT;
        SetInterruptStatusRegister();
        break;
    }
    switch (c & 0x3)
    {
      case 1:    // Enable receiver
        receiver_b_state=ACTIVE;
        SRB &= ~RxRDY;
        SRB &= ~FFULL;
        SetInterruptStatusRegister();
        break;
      case 2:
        receiver_b_state=INACTIVE;
        break;
    }
  }
  else if(addr==11*offset_between_registers)   // Transmitter Buffer B
  {
    TBB=c;           // Store the transmit data
    SRB &= ~TxRDY;   // Mark register as full
    SRB &= ~TxEMT;
    SetInterruptStatusRegister();

    if(ACR & 128)
      (myCPU.eventHandler()).Add(this, WRITE_B_CALLBACK, 0,
          baudrate_table[(CSRB & 0xf)+16]);
    else
      (myCPU.eventHandler()).Add(this, WRITE_B_CALLBACK, 0,
          baudrate_table[(CSRB & 0xf)]);
  }
  else if(addr==12*offset_between_registers)   // Interrupt-vector register
  {
    IVR=c;
  }
}


///////////////////////////////////////////////////////////////////////////////
// Handle event callbacks
///////////////////////////////////////////////////////////////////////////////
void M68681::EventCallback(long type, void*)
{
  SetInterruptStatusRegister();

  if(type==WRITE_A_CALLBACK)
  {
    unsigned char c;

    // If pipe is not availiable then just pretend we sent it somewhere
    if(coma_write_id == -1)
    {
      SRA |= TxRDY;      // Ready for more data
      SRA |= TxEMT;      // Transmitter is empty
      SetInterruptStatusRegister();
      return;
    }
    else if(transmitter_a_state==INACTIVE)
    {
      return;
    }

    // Mask off bits that shouldn't be transmitted
    switch (MR1A & 3)
    {
      case 0:
        c=TBA & 0x1f;
        break;
      case 1:
        c=TBA & 0x3f;
        break;
      case 2:
        c=TBA & 0x7f;
        break;

      default:
        c=TBA;
    }

    // Do the data transfer
    switch ((MR2A & 0xc0) >> 6)
    {
      case 1:    // Automatic-echo mode (transmitter link disabled)
        SRA |= TxRDY;
        SRA |= TxEMT;
        break;

      default:   // Normal mode
        write(coma_write_id, &c ,1);
        SRA |= TxRDY;      // Ready for more data
        SRA |= TxEMT;      // Transmitter is empty
        SetInterruptStatusRegister();
    }        
  }
  else if(type==READ_A_CALLBACK)
  {
    unsigned char c;

    // If receiver is disabled or full then just add event and return
    if((receiver_a_state==INACTIVE) || (SRA & FFULL))
    {
      // Reschedule another read callback to check for more characters
      (myCPU.eventHandler()).Add(this, READ_A_CALLBACK, 0,
          DEFAULT_READ_CALLBACK_DURATION);
      return;
    }

    // Try to read a byte from the pipe
    if(read(coma_read_id, &c, 1)==1)
    {
      // Mask off bits that shouldn't be received
      switch (MR1A & 3)
      {
        case 0:
          RBA=c & 0x1f;
          break;
        case 1:
          RBA=c & 0x3f;
          break;
        case 2:
          RBA=c & 0x7f;
          break;

        default:
          RBA=c;
      }

      // Handle any special stuff for the funky modes
      switch ((MR2A & 0xc0) >> 6)
      {
        case 1:    // Automatic-echo mode
          write(coma_write_id, &RBA, 1);
          break;
      }        

      // If we're already full then set the overrun error bit
//      if(SRA & FFULL)
//        SRA |= 16;

      SRA |= RxRDY;
      SRA |= FFULL;
      SetInterruptStatusRegister();

      // Reschedule another read callback to check for more characters
      if(ACR & 128)
        (myCPU.eventHandler()).Add(this, READ_A_CALLBACK, 0,
            baudrate_table[(CSRA >> 4)+16]);
      else
        (myCPU.eventHandler()).Add(this, READ_A_CALLBACK, 0,
            baudrate_table[(CSRA >> 4)]);
    }
    else
    {
      // Reschedule another read callback to check for more characters
      (myCPU.eventHandler()).Add(this, READ_A_CALLBACK, 0,
          DEFAULT_READ_CALLBACK_DURATION);
    }
  }
  else if(type==WRITE_B_CALLBACK)
  {
    unsigned char c;

    // If pipe is not availiable then just pertend we sent it somewhere
    if(comb_write_id == -1)
    {
      SRB |= TxRDY;
      SRB |= TxEMT;
      SetInterruptStatusRegister();
      return;
    }
    else if(transmitter_b_state==INACTIVE)
    {
      return;
    }

    // Mask off bits that shouldn't be transmitted
    switch (MR1B & 3)
    {
      case 0:
        c=TBB & 0x1f;
        break;
      case 1:
        c=TBB & 0x3f;
        break;
      case 2:
        c=TBB & 0x7f;
        break;

      default:
        c=TBB;
    }

    // Do the data transfer
    switch ((MR2B & 0xc0) >> 6)
    {
      case 1:    // Automatic-echo mode (transmitter link disabled)
        SRB |= TxRDY;
        SRB |= TxEMT;
        break;
      case 2:    // Local-loopback mode (not implemented) 
      case 3:    // Multidrop mode (not implemented)

      default:   // Normal mode
        write(comb_write_id, &c ,1);
        SRB |= TxRDY;
        SRB |= TxEMT;
        SetInterruptStatusRegister();
    }        
  }
  else if(type==READ_B_CALLBACK)
  {
    unsigned char c;

    // If receiver is disabled or full then just add event and return 
    if((receiver_b_state==INACTIVE) || (SRB & FFULL))
    {
      if(SRB & FFULL)
      {
        if(ACR & 128)
          (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
              baudrate_table[(CSRB >> 4)+16]);
        else
          (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
              baudrate_table[(CSRB >> 4)]);
      }
      else
      {
        // Reschedule another read callback to check for more characters
        (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
            DEFAULT_READ_CALLBACK_DURATION);
      }
      return;
    }

    // Try to read a byte from the pipe
    if(read(comb_read_id, &c, 1)==1)
    {
      // Mask off bits that shouldn't be received
      switch (MR1B & 3)
      {
        case 0:
          RBB=c & 0x1f;
          break;
        case 1:
          RBB=c & 0x3f;
          break;
        case 2:
          RBB=c & 0x7f;
          break;

        default:
          RBB=c;
      }

      // Handle any special stuff for the funky modes
      switch ((MR2B & 0xc0) >> 6)
      {
        case 1:    // Automatic-echo mode (transmitter link disabled)
          write(comb_write_id, &RBB, 1);
          break;
      }        

      // If we're already full then set the overrun error bit
//      if(SRB & FFULL)
//        SRB |= 16;

      SRB |= RxRDY;
      SRB |= FFULL;
      SetInterruptStatusRegister();

      // Reschedule another read callback to check for more characters
      if(ACR & 128)
        (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
            baudrate_table[(CSRB >> 4)+16]);
      else
        (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
            baudrate_table[(CSRB >> 4)]);
    }
    else
    {
      // Reschedule another read callback to check for more characters
      (myCPU.eventHandler()).Add(this, READ_B_CALLBACK, 0,
          DEFAULT_READ_CALLBACK_DURATION);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// Set the Interrupt Status Register (ISR) and request an interrupt if needed
///////////////////////////////////////////////////////////////////////////////
void M68681::SetInterruptStatusRegister()
{
  ISR = 0;

  // See if transmitter A is ready for data
  if(SRA & TxRDY)
    ISR |= 1;

  // See if receiver A has data or if the FIFO is full
  if(MR1A & 64)
  {
    if(SRA & FFULL)
      ISR |= 2;
  }
  else
  {
    if(SRA & RxRDY)
      ISR |= 2;
  }

  // See if transmitter B is ready for data
  if(SRB & TxRDY)
    ISR |= 16;

  // See if receiver B has data or if the FIFO is full
  if(MR1B & 64)
  {
    if(SRB & FFULL)
      ISR |= 32;
  }
  else
  {
    if(SRB & RxRDY)
      ISR |= 32;
  }

  // Request an interrupt if one is needed
  if(ISR & IMR)
    InterruptRequest(interrupt_level);
}

///////////////////////////////////////////////////////////////////////////////
// Acknowledege Interrupt - Called by CPU when it processes the interrupt
///////////////////////////////////////////////////////////////////////////////
long M68681::InterruptAcknowledge(int)
{
  if(myInterruptPending)
  {
    myInterruptPending = false;    // Clear the pending interrupt flag
    return(IVR);                   // Return the programmed Interrupt Vector 
  }
  else
  {
    return(SPURIOUS_INTERRUPT);
  }
}

