// brs525e2.c
//
// This is meant to be compiled with Quick C for Windows version 1.0
// (the makefile will not work with other make utilities)
//
// Dynalink for driving the JVC BR-S525E video tape recorder.
// See section 1.7 of your service manual ("PROTOCOL OF 9-PIN REMOTE CONNECTOR").
// Hardware requirements: RS-422 board or a full-duplex RS-232 <-> RS-422 Converter.
// If you use a converter, a model which transmits only data lines TD and RD is sufficient.
// Version 2 vom Juni ´97. Version ist ist durch Diskettenschaden teilweise vernichtet.
// Version 2 ist gegenüber Version 1 in einigen Punkten verbessert.

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "brs525e2.h"

// Declaration of local help functions:
void write_err(void);
void isReadError(void);
void getACK(void);
void getDeviceResponse(BYTE *byteBuf,BYTE expRet1,BYTE expRet2,int expCmdLen);
void forwardVar1Byte(BYTE selector);
void forwardVar2Byte(BYTE selector1,BYTE selector2);
BYTE asPackedBCD(int n);

// Globals:
static int wR;                                        // Return value of WriteComm
static int rR;                                        // Return value of ReadComm
static szCMD[18];                                     // Storage for a command block
static BYTE inBufLevel2[64];                          // Second level input buffer
static const BYTE rfdAck[] = {0x10,0x01};             // Standard return from device acknowledge
static int nCom;                                      // Communication device handle
static BYTE cmd1FWDVAR[]= {0x21,0x12,0x00,0x00};      // 1-byte forward var command block
static BYTE cmd2FWDVAR[]= {0x22,0x12,0x00,0x00,0x00}; // 2-bytes forward var command block
static char gloComName[5];                            // Name der Schnittstelle (COM1 - COM4)

// Using a WEP speeds up dll termination
int FAR PASCAL _WEP(int idExit)
{
  if(CloseComm(nCom))
    MessageBox(NULL,"Cannot close serial communication","BR-S525E2.DLL error",MB_ICONEXCLAMATION);

  return 1;
}

BOOL FAR PASCAL SetupCommunicationJVC(char *comName)
{
  static WORD FAR *comEvent;    // Address of event change flag word
  static DCB dcb;               // A device control block
  static BYTE bCmd[4];
  static BYTE bResponse;

  if (lstrlen(comName) != 4)
  {
    MessageBox(NULL,"Argument must consist of 4, e.g. COM1","SetupCommunicationJVC error",MB_ICONHAND);
    return FALSE;
  }
  else
  {
    lstrcpy(gloComName,comName);
  }

  // Open serial port COM2 with receive and transmit queues of size 64 byte.
  // The queues are used by interrupt-driven transmit/receive software, which is
  // a part of the windows system.
  nCom=OpenComm(gloComName,64,64);

  if (nCom < 0)
  {
    switch (nCom)
    {
      case IE_BADID:
           MessageBox(NULL,"Invalid or unsupported ID","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_BAUDRATE:
           MessageBox(NULL,"Unsupported baud rate","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_BYTESIZE:
           MessageBox(NULL,"Invalid byte size","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_DEFAULT:
           MessageBox(NULL,"Error in default parameters","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_HARDWARE:
           MessageBox(NULL,"Hardware not present","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_MEMORY:
           MessageBox(NULL,"Unable allocate queues","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_NOPEN:
           MessageBox(NULL,"Device not open","OpenComm error",MB_ICONHAND);
           return FALSE;

      case IE_OPEN:
           MessageBox(NULL,"Device already open","OpenComm error",MB_ICONHAND);
           return FALSE;
    }
  }
  // Set com event mask and get event mask address.
  comEvent=SetCommEventMask(nCom,EV_RXCHAR);

  // Configure COMx
  if (GetCommState(nCom,&dcb) < 0)
  {
    MessageBox(NULL,"Unable to read settings","GetCommState error",MB_ICONHAND);
    return FALSE;
  }

  dcb.BaudRate=38400;         // No
  dcb.ByteSize=8;             // other
  dcb.Parity=ODDPARITY;       // settings
  dcb.StopBits=ONESTOPBIT;    // necessary

  if (SetCommState(&dcb) < 0)
  {
    MessageBox(NULL,"Unable to set communication device","SetCommState error",MB_ICONHAND);
    return FALSE;
  }

  // Alles ok, gleich nachsehen, ob das Gerät auch bereit ist:
  bCmd[0] = 0x61; // STATUS..
  bCmd[1] = 0x20; // SENSE
  bCmd[2] = 0x01; // I want 1 byte back, the DATA-0
  bCmd[3] = 0x82; // Checksum

TESTAGAIN:
  bResponse = 0x00;
  wR=WriteComm(nCom,(LPSTR)bCmd,4);

  if (wR < 0)
    write_err();

  getDeviceResponse(&bResponse,0x71,0x20,4);

  // CASSETTE OUT ?
  if (bResponse & 0x20)
  {
    MessageBeep(MB_ICONHAND);
    MessageBox(NULL,
               "Legen Sie bitte eine Videokassette ein",
               "Keine Kassette im Player",
               MB_OK|MB_TASKMODAL|MB_ICONHAND);
    goto TESTAGAIN;
  }

  // LOCAL ?
  if (bResponse & 0x01)
  {
    MessageBeep(MB_ICONHAND);
    MessageBox(NULL,
               "Stellen Sie bitte den Schieberegler \"REMOTE\" auf \"9PIN\"",
               "Player ist im Local-Modus",
               MB_OK|MB_TASKMODAL|MB_ICONHAND);
    goto TESTAGAIN;
  }

  return TRUE;
}

// Disable operational functions of the device
void FAR PASCAL LocalDisableJVC(void)
{
  const BYTE cmLocalDisable[] = {0x00,0x0c,0x0c};

  wR=WriteComm(nCom,(LPSTR)cmLocalDisable,3);

  if (wR < 0)
    write_err();

  getACK(); // Read Acknowledge (standard ACK without data)
}

// Set front panel operation in accordance with the settings of the memory switch
void FAR PASCAL LocalEnableJVC(void)
{
  const BYTE cmLocalEnable[] = {0x00,0x1d,0x1d};

  wR=WriteComm(nCom,(LPSTR)cmLocalEnable,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL StopJVC(void)
{
  const BYTE cmStop[] = {0x20,0x00,0x20};

  wR=WriteComm(nCom,(LPSTR)cmStop,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL PlayJVC(void)
{
  const BYTE cmPlay[] = {0x20,0x01,0x21};

  wR=WriteComm(nCom,(LPSTR)cmPlay,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL StandByOffJVC(void)
{
  const BYTE cmStandByOff[] = {0x20,0x04,0x24};

  wR=WriteComm(nCom,(LPSTR)cmStandByOff,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL StandByOnJVC(void)
{
  const BYTE cmStandByOn[] = {0x20,0x05,0x25};

  wR=WriteComm(nCom,(LPSTR)cmStandByOn,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL EjectJVC(void)
{
  const BYTE cmEject[] = {0x20,0x0f,0x2f};

  wR=WriteComm(nCom,(LPSTR)cmEject,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL FastFwdJVC(void)
{
  const BYTE cmFastFwd[] = {0x20,0x10,0x30};

  wR=WriteComm(nCom,(LPSTR)cmFastFwd,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL RewindJVC(void)
{
  const BYTE cmRewind[] = {0x20,0x20,0x40};

  wR=WriteComm(nCom,(LPSTR)cmRewind,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL PrerollJVC(void)
{
  const BYTE cmPreroll[] = {0x20,0x30,0x50};

  wR=WriteComm(nCom,(LPSTR)cmPreroll,3);

  if (wR < 0)
    write_err();

  getACK();
}


void FAR PASCAL SyncPlayJVC(void)
{
  const BYTE cmSyncPlay[] = {0x20,0x34,0x54};

  wR=WriteComm(nCom,(LPSTR)cmSyncPlay,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL TensionReleaseJVC(void)
{
  const BYTE cmTensionRelease[] = {0x20,0x52,0x72};

  wR=WriteComm(nCom,(LPSTR)cmTensionRelease,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL AntiClogTimerDisableJVC(void)
{
  const BYTE cmAntiClogTimerDisable[] = {0x20,0x54,0x74};

  wR=WriteComm(nCom,(LPSTR)cmAntiClogTimerDisable,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL AntiClogTimerEnableJVC(void)
{
  const BYTE cmAntiClogTimerEnable[] = {0x20,0x55,0x75};

  wR=WriteComm(nCom,(LPSTR)cmAntiClogTimerEnable,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL Timer1ResetJVC(void)
{
  const BYTE cmTimer1Reset[] = {0x40,0x08,0x48};

  wR=WriteComm(nCom,(LPSTR)cmTimer1Reset,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL InEntryJVC(void)
{
  const BYTE cmInEntry[] = {0x40,0x10,0x50};

  wR=WriteComm(nCom,(LPSTR)cmInEntry,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL OutEntryJVC(void)
{
  const BYTE cmOutEntry[] = {0x40,0x11,0x51};

  wR=WriteComm(nCom,(LPSTR)cmOutEntry,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL InShiftPosJVC(void)
{
  const BYTE cmInShiftPos[] = {0x40,0x18,0x58};

  wR=WriteComm(nCom,(LPSTR)cmInShiftPos,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL InShiftNegJVC(void)
{
  const BYTE cmInShiftNeg[] = {0x40,0x19,0x59};

  wR=WriteComm(nCom,(LPSTR)cmInShiftNeg,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL OutShiftPosJVC(void)
{
  const BYTE cmOutShiftPos[] = {0x40,0x1a,0x5a};

  wR=WriteComm(nCom,(LPSTR)cmOutShiftPos,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL OutShiftNegJVC(void)
{
  const BYTE cmOutShiftNeg[] = {0x40,0x1b,0x5b};

  wR=WriteComm(nCom,(LPSTR)cmOutShiftNeg,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL InResetJVC(void)
{
  const BYTE cmInReset[] = {0x40,0x20,0x60};

  wR=WriteComm(nCom,(LPSTR)cmInReset,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL OutResetJVC(void)
{
  const BYTE cmOutReset[] = {0x40,0x21,0x61};

  wR=WriteComm(nCom,(LPSTR)cmOutReset,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL InRecallJVC(void)
{
  const BYTE cmInRecall[] = {0x40,0x24,0x64};

  wR=WriteComm(nCom,(LPSTR)cmInRecall,3);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL OutRecallJVC(void)
{
  const BYTE cmOutRecall[] = {0x40,0x25,0x65};

  wR=WriteComm(nCom,(LPSTR)cmOutRecall,3);

  if (wR < 0)
    write_err();

  getACK();
}

// Return an LPSTR pointing to a 2-byte device type ID, not Null-terminated
void FAR PASCAL DeviceTypeRequestJVC(BYTE FAR *b2Bytes)
{
  const BYTE cmDeviceTypeRequest[] = {0x00,0x11,0x11};
  static BYTE deviceType[3]; // getDeviceResponse will add a string terminator

  wR=WriteComm(nCom,(LPSTR)cmDeviceTypeRequest,3);

  if (wR < 0)
    write_err();

  getDeviceResponse(deviceType,0x12,0x11,5);
  _fstrncpy(b2Bytes,(BYTE FAR *)deviceType,2);
}

void FAR PASCAL SloppyMoveJVC(MOVETYPE how,DIRECTION direc,BYTE speedData)
{
  static BYTE cmdBlock[4];

  cmdBlock[0] = 0x21; // Always only one data byte in sloppy move

  switch (how)        // Nibbles of byte 2 depend on movement type
  {
    case JOG:     cmdBlock[1] = 0xf1;
                  break;
    case VAR:     cmdBlock[1] = 0xf2;
                  break;
    case SHUTTLE: cmdBlock[1] = 0xf3;
                  break;
  }

  switch (direc)      // ... and direction of movement
  {
    case FORWARD: cmdBlock[1] &= 0x1f;
                  break;
    case REVERSE: cmdBlock[1] &= 0x2f;
                  break;
  }

  cmdBlock[2] = speedData; // (DECIMAL)
  cmdBlock[3] = cmdBlock[0] + cmdBlock[1] + cmdBlock[2];
  wR=WriteComm(nCom,(LPSTR)cmdBlock,4);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL ExactMoveJVC(MOVETYPE how,DIRECTION direc,BYTE data1,BYTE
data2)
{
  static BYTE cmdBlock[5];

  cmdBlock[0] = 0x22; // Always two data bytes in exact move

  switch (how)        // Nibbles of byte 2 depend on movement type
  {
    case JOG:     cmdBlock[1] = 0xf1;
                  break;
    case VAR:     cmdBlock[1] = 0xf2;
                  break;
    case SHUTTLE: cmdBlock[1] = 0xf3;
                  break;
  }

  switch (direc)      // ... and direction of movement
  {
    case FORWARD: cmdBlock[1] &= 0x1f;
                  break;
    case REVERSE: cmdBlock[1] &= 0x2f;
                  break;
  }

  cmdBlock[2] = data1; // (DECIMAL)
  cmdBlock[3] = data2; // (DECIMAL)
  cmdBlock[4] = cmdBlock[0] + cmdBlock[1] + cmdBlock[2] + cmdBlock[3];
  wR=WriteComm(nCom,(LPSTR)cmdBlock,5);

  if (wR < 0)
    write_err();

  getACK();
}

void write_err(void)
{
  MessageBox(NULL,"WriteComm returns error condition","BR-S525E2.DLL fatal error",MB_ICONHAND);
}

// Read Acknowledge (standard ACK without data)
void getACK(void)
{
  // Just make empty the motherfucking receive queue;
  // Can't help if device does not acknowledge a command.
  //  while (EV_RXCHAR == GetCommEventMask(nCom,EV_RXCHAR))
  //  {
  //    ReadComm(nCom,(LPSTR)inBufLevel2,64);
  //    memset(inBufLevel2,0,64); // Forget what device said
  //  }

  static BYTE ackBuf[4];

  memset(ackBuf,0,4);

  while (3 != lstrlen(ackBuf)) // Make receive queue empty, wait for acknowledge
  {
    if (EV_RXCHAR == GetCommEventMask(nCom,EV_RXCHAR))
    {
      memset(inBufLevel2,0,64); // Preset buffer with string terminators (ASCII 0)
      rR=ReadComm(nCom,(LPSTR)inBufLevel2,64);
      strncat(ackBuf,inBufLevel2,(size_t)rR); // Possible, cause ACK or NAK do not contain 0x00
      //isReadError(); kann man darauf verzichten ? Man muß
    }
  }

  // Test if the recorder has sent acknowledge; mal ohne probieren
  if (strncmp(ackBuf,rfdAck,2))
    if (EV_RXCHAR == GetCommEventMask(nCom,EV_RXCHAR))
      ReadComm(nCom,(LPSTR)inBufLevel2,64);  // Read the rest (if any)
  //  MessageBox(NULL,"Device does not acknowledge command","BR-S525E.DLL error",MB_ICONEXCLAMATION);
}

void isReadError(void)
{
  // Test if reading operation was successful
  if (rR <= 0) // This indicates an error
  {
    COMSTAT comstat;
    int ec;

    ec=GetCommError(nCom,&comstat);

    if (ec & CE_BREAK)
      MessageBox(NULL,"CE_BREAK","Communications Error",MB_ICONHAND);

    if (ec & CE_CTSTO)
      MessageBox(NULL,"CE_CTSTO","Communications Error",MB_ICONHAND);

    if (ec & CE_DNS)
      MessageBox(NULL,"CE_DNS","Communications Error",MB_ICONHAND);

    if (ec & CE_DSRTO)
      MessageBox(NULL,"CE_DSRTO","Communications Error",MB_ICONHAND);

    if (ec & CE_FRAME)
      MessageBox(NULL,"CE_FRAME","Communications Error",MB_ICONHAND);

    if (ec & CE_IOE)
      MessageBox(NULL,"CE_IOE","Communications Error",MB_ICONHAND);

    if (ec & CE_MODE)
      MessageBox(NULL,"CE_MODE","Communications Error",MB_ICONHAND);

    if (ec & CE_OOP)
      MessageBox(NULL,"CE_OOP","Communications Error",MB_ICONHAND);

    if (ec & CE_OVERRUN)
      MessageBox(NULL,"CE_OVERRUN","Communications Error",MB_ICONHAND);

    if (ec & CE_PTO)
      MessageBox(NULL,"CE_PTO","Communications Error",MB_ICONHAND);

    if (ec & CE_RLSDTO)
      MessageBox(NULL,"CE_RLSDTO","Communications Error",MB_ICONHAND);

    if (ec & CE_RXOVER)
      MessageBox(NULL,"CE_RXOVER","Communications Error",MB_ICONHAND);

    if (ec & CE_RXPARITY)
      MessageBox(NULL,"CE_RXPARITY","Communications Error",MB_ICONHAND);

    if (ec & CE_TXFULL)
      MessageBox(NULL,"CE_TXFULL","Communications Error",MB_ICONHAND);

    if (comstat.fCtsHold)
      MessageBox(NULL,"comstat.fCtsHold is set",NULL,MB_ICONHAND);

    if (comstat.fDsrHold)
      MessageBox(NULL,"comstat.fDsrHold is set",NULL,MB_ICONHAND);

    if (comstat.fRlsdHold)
      MessageBox(NULL,"comstat.fRlsdHold is set",NULL,MB_ICONHAND);

    if (comstat.fXoffHold)
      MessageBox(NULL,"comstat.fXoffHold is set",NULL,MB_ICONHAND);

    if (comstat.fXoffSent)
      MessageBox(NULL,"comstat.fXoffSent is set",NULL,MB_ICONHAND);

    if (comstat.fEof)
      MessageBox(NULL,"comstat.fEof is set",NULL,MB_ICONHAND);

    if (comstat.fTxim)
      MessageBox(NULL,"comstat.fTxim is set",NULL,MB_ICONHAND);

    {
      char strBuf[50];

      wsprintf(strBuf,"%u chars in receive queue",comstat.cbInQue);
      MessageBox(NULL,strBuf,"comstat.cbInQue",MB_ICONHAND);

      wsprintf(strBuf,"%u chars in transmit queue",comstat.cbOutQue);
      MessageBox(NULL,strBuf,"comstat.cbOutQue",MB_ICONHAND);
    }
  }
}

// Get a response from the device and copy it to byteBuf. The function tests if
// the expected answer-bytes expRet1 and expRet2 are identical with the actually
// sent "CMD1" and "CMD2". CMD1,CMD2 and the checksum byte are removed from
// the answer, only the DATA parts of the answer are copied to byteBuf.
// byteBuf MUST BE BIG ENOUGH TO CONTAIN THE ANSWER BYTES, that means it must be
// at least as big as the DATA COUNT nibble of CMD1 specifies !
// For performance reasons, the checksum is not tested

void getDeviceResponse(BYTE *byteBuf,BYTE expRet1,BYTE expRet2,int expCmdLen)
{
  static BYTE rawReceive[19]; // No receive command block is longer than 18 bytes
  static BYTE dataCount;      // How many data bytes between CMD1 and CMD2 ?
  static int nLevel3;         // How many incoming bytes already saved ?

  // Wait until the serial port driver has filled the receive queue
  memset(rawReceive,0,19);
  nLevel3=0;

  while (expCmdLen != nLevel3) // While stuff waiting to be received
  {
    if (EV_RXCHAR == GetCommEventMask(nCom,EV_RXCHAR))  // If stuff waiting in receive queue
    {
      rR=ReadComm(nCom,(LPSTR)inBufLevel2,18);           // Read to interim buffer
      memcpy(rawReceive+nLevel3,inBufLevel2,(size_t)rR); // Append
      nLevel3+=rR;
      // You cannot do it with strncat, gets you in trouble in case of 0x00 sent from device
    }
  }

  dataCount=rawReceive[0]; // Copy CMD1
  dataCount &= 0x0f;       // Mask out non - data count bits

  if (rawReceive[0] != expRet1 || rawReceive[1] != expRet2)
  {
    static int i;
    static char szAHexVal[3];
    static char szStr[128];

    wsprintf((LPSTR)szStr,(LPSTR)"Device did not answer correctly: ");

    for(i=0;i < lstrlen(rawReceive);i++)
    {
      wsprintf((LPSTR)szAHexVal,"%02x",rawReceive[i]);
      strcat(szStr,szAHexVal); // The cmd1 and cmd2 return bytes are never 0x00
    }

    MessageBox(NULL,szStr,"BR-S525E2.DLL fatal error",MB_ICONHAND);
  }

  // Copy the DATA part of the answer
  memcpy(byteBuf,(rawReceive+2),(size_t)dataCount);
}

// Variable Tracking in Simulation. Divide et impera - mit 6 Vergleichen zum Ziel
void FAR PASCAL SetVarJVC(float toKmH,float filmedSpeed)
{
  static float var;

  var = toKmH / filmedSpeed;

  if (var < 2.10F)
  {
    if (var < 0.50F)
    {
      if (var < 0.24F)
      {
        if (var < 0.12F)
        {
          if (var < 0.06F)
          {
            if (var < 0.03F)
              forwardVar1Byte(0x00);// 0.00 VarStep0
            else
              forwardVar1Byte(0x10);// 0.03 VarStep1
          }
          else // >= 0.06
          {
            if (var < 0.09F)
              forwardVar1Byte(0x18);// 0.06 VarStep2
            else
              forwardVar1Byte(0x1e);// 0.09 VarStep3
          }
        }
        else // >= 0.12
        {
          if (var < 0.18F)
          {
            if (var < 0.15F)
              forwardVar1Byte(0x22);// 0.12 VarStep4
            else
              forwardVar1Byte(0x26);// 0.15 VarStep5
          }
          else // >= 0.18
          {
            if (var < 0.21F)
              forwardVar1Byte(0x28);// 0.18 VarStep6
            else
              forwardVar2Byte(0x2a,0x05);// 0.21 VarStep7
          }
        }
      }
      else // >= 0.24
      {
        if (var < 0.36F)
        {
          if (var < 0.30F)
          {
            if (var < 0.27F)
              forwardVar1Byte(0x2c);// 0.24 VarStep8
            else
              forwardVar1Byte(0x2e);// 0.27 VarStep9
          }
          else // var >= 0.30
          {
            if (var < 0.33F)
              forwardVar1Byte(0x2f);// 0.30 VarStep10
            else
              forwardVar2Byte(0x30,0x80);// 0.33 VarStep11
          }
        }
        else // var >= 0.36
        {
          if (var < 0.42F)
          {
            if (var < 0.39F)
              forwardVar2Byte(0x31,0xa0);// 0.36 VarStep12
            else
              forwardVar2Byte(0x32,0xc0);// 0.39 VarStep13
          }
          else // var >= 0.42
          {
            if (var < 0.46F)
              forwardVar2Byte(0x34,0x05);// 0.42 VarStep14
            else
              forwardVar2Byte(0x35,0x40);// 0.46 VarStep15
          }
        }
      }
    }
    else // >= 0.50
    {
      if (var < 1.30F)
      {
        if (var < 0.90F)
        {
          if (var < 0.70F)
          {
            if (var < 0.60F)
              forwardVar1Byte(0x36);// 0.50 VarStep16
            else
              forwardVar1Byte(0x39);// 0.60 VarStep17
          }
          else // >= 0.70
          {
            if (var < 0.80F)
              forwardVar1Byte(0x3b);// 0.70 VarStep18
            else
              forwardVar1Byte(0x3d);// 0.80 VarStep19
          }
        }
        else // >= 0.90
        {
          if (var < 1.10F)
          {
            if (var < 1.00F)
              forwardVar2Byte(0x3e,0x78);// 0.90 VarStep20
            else
              forwardVar1Byte(0x40);// 1.00 VarStep21
          }
          else // >= 1.10
          {
            if (var < 1.20F)
              forwardVar2Byte(0x41,0x50);// 1.10 VarStep22
            else
              forwardVar2Byte(0x42,0x80);// 1.20 VarStep23
          }
        }
      }
      else // >= 1.30
      {
        if (var < 1.70F)
        {
          if (var < 1.50F)
          {
            if (var < 1.40F)
              forwardVar2Byte(0x43,0xa0);// 1.30 VarStep24
            else
              forwardVar2Byte(0x44,0xad);// 1.40 VarStep25
          }
          else // >= 1.50
          {
            if (var < 1.60F)
              forwardVar2Byte(0x45,0xaa);// 1.50 VarStep26
            else
              forwardVar2Byte(0x46,0x90);// 1.60 VarStep27
          }
        }
        else // >= 1.70
        {
          if (var < 1.90F)
          {
            if (var < 1.80F)
              forwardVar2Byte(0x47,0x56);// 1.70 VarStep28
            else
              forwardVar2Byte(0x48,0x28);// 1.80 VarStep29
          }
          else // >= 1.90
          {
            if (var < 2.00F)
              forwardVar2Byte(0x48,0xf0);// 1.90 VarStep30
            else
              forwardVar1Byte(0x49);// 2.00 VarStep31
          }
        }
      }
    }
  }
  else // >= 2.10
  {
    if (var < 2.90F)
    {
      if (var < 2.50F)
      {
        if (var < 2.30F)
        {
          if (var < 2.20F)
            forwardVar2Byte(0x4a,0x46);// 2.10 VarStep32
          else
            forwardVar2Byte(0x4a,0xfa);// 2.20 VarStep33
        }
        else // >= 2.30
        {
          if (var < 2.40F)
            forwardVar2Byte(0x4b,0x96);// 2.30 VarStep34
          else
            forwardVar2Byte(0x4c,0x28);// 2.40 VarStep35
        }
      }
      else // >= 2.50
      {
        if (var < 2.70F)
        {
          if (var < 2.60F)
            forwardVar1Byte(0x4d);// 2.50 VarStep36
          else
            forwardVar2Byte(0x4d,0x46);// 2.60 VarStep37
        }
        else // >= 2.70
        {
          if (var < 2.80F)
            forwardVar2Byte(0x4d,0xc8);// 2.70 VarStep38
          else
            forwardVar2Byte(0x4e,0x50);// 2.80 VarStep39
        }
      }
    }
    else // >= 2.90
    {
      if (var < 32.00F)
      {
        if (var < 6.00F)
        {
          if (var < 3.00F)
            forwardVar2Byte(0x4e,0xc8);// 2.90 VarStep40
          else
            forwardVar1Byte(0x4f);// 3.00 VarStep41 // Letzte echte Variable-Tracking-Stufe
        }
        else // >= 6.00
        {
          if (var < 10.00F)
            ;// 6.00 ShuttleStep42Erste Shuttle-Stufe
          else
            ;// 10.00 ShuttleStep43
        }
      }
      else // >= 32
      {
        ;// 32.00 ShuttleStep44
      }
    }
  }


//  if (var < 0.1F)                       // Step 0
//    forwardVar1Byte(0x00);
//  else if (var >= 0.1F && var < 0.2F)   // Step 1
//    forwardVar1Byte(0x20);
//  else if (var >= 0.2F && var < 0.3F)   // Step 2
//    forwardVar1Byte(0x29);
//  else if (var >= 0.3F && var < 0.4F)   // Step 3
//    forwardVar1Byte(0x2f);
//  else if (var >= 0.4F && var < 0.5F)   // Step 4
//    forwardVar1Byte(0x33);
//  else if (var >= 0.5F && var < 0.6F)   // Step 5
//    forwardVar1Byte(0x36);
//  else if (var >= 0.6F && var < 0.7F)   // Step 6
//    forwardVar1Byte(0x39);
//  else if (var >= 0.7F && var < 0.8F)   // Step 7
//    forwardVar1Byte(0x3b);
//  else if (var >= 0.8F && var < 0.9F)   // Step 8
//    forwardVar1Byte(0x3d);
//  else if (var >= 0.9F && var < 1.0F)   // Step 9
//    forwardVar2Byte(0x3e,0x78);
//  else if (var >= 1.0F && var < 1.1F)   // Step 10
//    forwardVar1Byte(0x40);
//  else if (var >= 1.1F && var < 1.2F)   // Step 11
//    forwardVar2Byte(0x41,0x50);
//  else if (var >= 1.2F && var < 1.3F)   // Step 12
//    forwardVar2Byte(0x42,0x80);
//  else if (var >= 1.3F && var < 1.4F)   // Step 13
//    forwardVar2Byte(0x43,0xa0);
// else if (var >= 1.4F && var < 1.5F)  // Step 14
//
// else if (var >= 1.5F && var < 1.6F)  // Step 15
//
// else if (var >= 1.6F && var < 1.7F)  // Step 16
//
// else if (var >= 1.7F && var < 1.8F)  // Step 17
//
// else if (var >= 1.8F && var < 1.9F)  // Step 18
//
// else if (var >= 1.9F && var < 2.0F)  // Step 19
//
//  else if (var >= 2.0F && var < 2.1F)   // Step 20
//    forwardVar1Byte(0x49);
//  else if (var >= 2.1F && var < 2.2F)   // Step 21
//    forwardVar2Byte(0x4a,0x46);
//  else if (var >= 2.2F && var < 2.3F)   // Step 22
//    forwardVar2Byte(0x4a,0xfa);
//  else if (var >= 2.3F && var < 2.4F)   // Step 23
//    forwardVar2Byte(0x4b,0x96);
//  else if (var >= 2.4F && var < 2.5F)   // Step 24
//    forwardVar2Byte(0x4c,0x28);
//  else if (var >= 2.5F && var < 2.6F)   // Step 25
//    forwardVar1Byte(0x4d);
//  else if (var >= 2.6F && var < 2.7F)   // Step 26
//    forwardVar2Byte(0x4d,0x46);
//  else if (var >= 2.7F && var < 2.8F)   // Step 27
//    forwardVar2Byte(0x4d,0xc8);
//  else if (var >= 2.8F && var < 2.9F)   // Step 28
//    forwardVar2Byte(0x4e,0x50);
//  else if (var >= 2.9F && var < 3.0F)   // Step 29
//    forwardVar2Byte(0x4e,0xc8);
//  else if (var >= 3.0F && var < 3.1F)   // Step 30
//    forwardVar1Byte(0x4f);
//  else if (var >= 3.1F && var < 3.2F)   // Step 31
//    forwardVar2Byte(0x4f,0xb4);
//  else if (var >= 3.2F && var < 3.3F)   // Step 32
//    forwardVar2Byte(0x50,0x28);
//  else if (var >= 3.3F && var < 3.4F)   // Step 33
//    forwardVar2Byte(0x50,0x96);
//  else if (var >= 3.4F && var < 3.5F)   // Step 34
//    forwardVar1Byte(0x51);
//  else if (var >= 3.5F && var < 3.6F)   // Step 35
//    forwardVar2Byte(0x51,0x3c);
//  else if (var >= 3.6F && var < 3.7F)   // Step 36
//    forwardVar2Byte(0x51,0xa0);
//  else if (var >= 3.7F && var < 3.8F)   // Step 37
//    forwardVar1Byte(0x52);
//  else if (var >= 3.8F && var < 3.9F)   // Step 38
//    forwardVar2Byte(0x52,0x64);
//  else if (var >= 3.9F && var < 4.0F)   // Step 39
//    forwardVar2Byte(0x52,0xbe);
//  else if (var >= 4.0F && var < 4.1F)   // Step 40
//    forwardVar1Byte(0x53);
//  else if (var >= 4.1F && var < 4.2F)   // Step 41
//    forwardVar2Byte(0x53,0x6e);
//  else if (var >= 4.2F && var < 4.3F)   // Step 42
//    forwardVar2Byte(0x53,0xc8);
//  else if (var >= 4.3F && var < 4.4F)   // Step 43
//    forwardVar2Byte(0x54,0x1e);
//  else if (var >= 4.4F && var < 4.5F)   // Step 44
//    forwardVar2Byte(0x54,0x6e);
//  else if (var >= 4.5F && var < 4.6F)   // Step 45
//    forwardVar1Byte(0x55);
//  else if (var >= 4.6F && var < 4.7F)   // Step 46
//    forwardVar2Byte(0x55,0x14);
//  else if (var >= 4.7F && var < 4.8F)   // Step 47
//    forwardVar2Byte(0x55,0x5a);
//  else if (var >= 4.8F && var < 4.9F)   // Step 48
//    forwardVar2Byte(0x55,0xaa);
//  else if (var >= 4.9F && var < 5.0F)   // Step 49
//    forwardVar2Byte(0x55,0xfa);
//  else if (var >= 5.0F && var < 5.6F)   // Step 50
//    forwardVar1Byte(0x56);
//  else if (var >= 5.6F && var < 6.0F)   // Step 51 Very Bad Display Quality, useless to support
//    forwardVar1Byte(0x58);              // smaller steps...
//  else if (var >= 6.0F && var < 6.5F)   // Step 52
//    forwardVar1Byte(0x59);
//  else if (var >= 6.5F && var < 7.0F)   // Step 53
//    forwardVar1Byte(0x5a);
//  else if (var >= 7.0F && var < 7.5F)   // Step 54
//    forwardVar1Byte(0x5b);
//  else if (var >= 7.5F && var < 8.0F)   // Step 55
//    forwardVar1Byte(0x5c);
//  else if (var >= 8.0F && var < 8.7F)   // Step 56
//    forwardVar1Byte(0x5d);
//  else if (var >= 8.7F && var < 9.3F)   // Step 57
//    forwardVar1Byte(0x5e);
//  else if (var >= 9.3F && var < 10.0F)  // Step 58
//    forwardVar1Byte(0x5f);
//  else                                  // Step 59
//    forwardVar1Byte(0x60);

}

// Many speed selections can be done with only one byte
void forwardVar1Byte(BYTE selector)
{
  const BYTE b1b2=0x33;                    // 0x21 + 0x12

  *(cmd1FWDVAR+2)=selector;                // cmd1FWDVAR is a global variable
  *(cmd1FWDVAR+3)= b1b2 + *(cmd1FWDVAR+2); // Checksum
  wR=WriteComm(nCom,(LPSTR)cmd1FWDVAR,4);

  if (wR < 0)
    write_err();

  getACK();
}

// Some speeds can only be set with a two byte command
void forwardVar2Byte(BYTE selector1,BYTE selector2)
{
  const BYTE b1b2=0x34;     // 0x22 + 0x12

  *(cmd2FWDVAR+2)=selector1;               // cmd2FWDVAR is a global variable
  *(cmd2FWDVAR+3)=selector2;
  *(cmd2FWDVAR+4)= b1b2 + *(cmd2FWDVAR+2) + *(cmd2FWDVAR+3);
  wR=WriteComm(nCom,(LPSTR)cmd2FWDVAR,5);

  if (wR < 0)
    write_err();

  getACK();
}

void FAR PASCAL ForwardVar1Byte(BYTE selector)
{
  forwardVar1Byte(selector);
}

void FAR PASCAL ForwardVar2Byte(BYTE selector1,BYTE selector2)
{
  forwardVar2Byte(selector1,selector2);
}

void FAR PASCAL CueUpWithDataJVC(int nSt,int nMi,int nSe,int nFr)
{
  BYTE bCueUp[7];

  // Timecode is in packed binary coded decimal format, see service manual p. 1-25
  if (nFr > 30 || nSe > 59 || nMi > 59 || nSt > 39)
  {
    MessageBox(NULL,"Invalid or nonsense timecode","BR-S525E2.CueUpWithDataJVC",MB_ICONHAND);
  }
  else
  {
    bCueUp[0]=0x24;
    bCueUp[1]=0x31;
    bCueUp[2]=asPackedBCD(nFr);
    bCueUp[3]=asPackedBCD(nSe);
    bCueUp[4]=asPackedBCD(nMi);
    bCueUp[5]=asPackedBCD(nSt);
    bCueUp[6]=bCueUp[0]+bCueUp[1]+bCueUp[2]+bCueUp[3]+bCueUp[4]+bCueUp[5];
    wR=WriteComm(nCom,(LPSTR)bCueUp,7);

    if (wR < 0)
      write_err();

    getACK();
  }
}


BYTE asPackedBCD(int n)
{
  static BYTE bRes;
  static char chStr[3];
  static char chRepair;

  if (n > 99)
  {
    MessageBox(NULL,"Argument > 99","BRS525E2.asPackedBCD",MB_ICONHAND);
    return 0x00;
  }

  memset(chStr,0,3);

  itoa(n,chStr,10);

  if (2 > lstrlen(chStr))  // It was a single digit integer..
  {
    chRepair=chStr[0];    // .. and must be preceded with a 0
    chStr[0]='0';         // for the following stuff:
    chStr[1]=chRepair;
  }


  switch (chStr[0]) // bits 0 to 3
  {
    case '0': bRes=0x00;   break;  // 00000000
    case '1': bRes=0x10;   break;  // 00010000
    case '2': bRes=0x20;   break;  // 00100000
    case '3': bRes=0x30;   break;  // 00110000
    case '4': bRes=0x40;   break;  // 01000000
    case '5': bRes=0x50;   break;  // 01010000
    case '6': bRes=0x60;   break;  // 01100000
    case '7': bRes=0x70;   break;  // 01110000
    case '8': bRes=0x80;   break;  // 10000000
    case '9': bRes=0x90;   break;  // 10010000
  }

  switch (chStr[1]) // bits 5 to 7
  {
    case '0': bRes|=0x00;  break;  // 00000000
    case '1': bRes|=0x01;  break;  // 00000001
    case '2': bRes|=0x02;  break;  // 00000010
    case '3': bRes|=0x03;  break;  // 00000011
    case '4': bRes|=0x04;  break;  // 00000100
    case '5': bRes|=0x05;  break;  // 00000101
    case '6': bRes|=0x06;  break;  // 00000110
    case '7': bRes|=0x07;  break;  // 00000111
    case '8': bRes|=0x08;  break;  // 00001000
    case '9': bRes|=0x09;  break;  // 00001001
  }

  return bRes;
}

ISCUEUPCOMPLETERETURN FAR PASCAL IsCueUpComplete(void)
{
  static BOOL success;
  static BOOL atEndOfTape;
  static BYTE bCmdGetData2[] = {0x61,  // STATUS..
                                0x20,  // SENSE
                                0x21,  // I want 1 byte back, the DATA-2
                                0xa2}; // Checksum

  static BYTE bCmdGetData8[] = {0x61,  // STATUS..
                                0x20,  // SENSE
                                0x81,  // I want 1 byte back, the DATA-8
                                0x02}; // Checksum (its least significant 8 bits)
  static BYTE bResponse;
  static BYTE bResponse2;

  // Initialize
  success = atEndOfTape = FALSE;

  wR=WriteComm(nCom,(LPSTR)bCmdGetData2,4);

  if (wR < 0)
    write_err();

  getDeviceResponse(&bResponse,0x71,0x20,4);

  // See table on protocol page 1-22
  if (bResponse & 0x01) // CUE UP COMPLETE
  {
    success = TRUE;
  }

  wR=WriteComm(nCom,(LPSTR)bCmdGetData8,4);

  if (wR < 0)
    write_err();

  getDeviceResponse(&bResponse2,0x71,0x20,4);

  if (bResponse2 & 0x10) // END OF TAPE
  {
    atEndOfTape = TRUE;
  }

  if (atEndOfTape)
    return CUEUPATENDOFTAPE;
  else if (success)
    return CUEUPTCFOUND;
  else
    return CUEUPSTILLRUNNING;
}

void FAR PASCAL ShutDownCommunicationJVC(void)
{
  if(CloseComm(nCom))
  {
    MessageBox(NULL,"Cannot close serial communication","BR-S525E2.DLL error",MB_ICONEXCLAMATION);
  }
}

BOOL FAR PASCAL DoesPlayerRespondJVC(char *comName)
{
  static int run;
  static WORD FAR *comEvent;    // Address of event change flag word
  static DCB dcb;               // A device control block
  static BYTE bCmd[4];
  static BYTE unusedResponse;   // Wird nicht ausgewertet

  if (lstrlen(comName) != 4)
  {
    MessageBox(NULL,"Argument must consist of 4, e.g. COM1","SetupCommunicationJVC error",MB_ICONHAND);
    return FALSE;
  }
  else
  {
    lstrcpy(gloComName,comName);
  }

  // Open serial port COM2 with receive and transmit queues of size 64 byte.
  // The queues are used by interrupt-driven transmit/receive software, which is
  // a part of the windows system.
  nCom=OpenComm(gloComName,64,64);

  if (nCom < 0)
  {
    CloseComm(nCom);
    return TRUE; // Diese Fehlerklasse wird später behandelt
  }

  // Set com event mask and get event mask address.
  comEvent=SetCommEventMask(nCom,EV_RXCHAR);

  // Configure COMx
  if (GetCommState(nCom,&dcb) < 0)
  {
    MessageBox(NULL,"Unable to read settings","GetCommState error",MB_ICONHAND);
    return FALSE;
  }

  dcb.BaudRate=38400;         // No
  dcb.ByteSize=8;             // other
  dcb.Parity=ODDPARITY;       // settings
  dcb.StopBits=ONESTOPBIT;    // necessary

  if (SetCommState(&dcb) < 0)
  {
    MessageBox(NULL,"Unable to set communication device","SetCommState error",MB_ICONHAND);
    return FALSE;
  }

  // Alles ok, gleich nachsehen, ob das Gerät auch bereit ist:
  // Hier ist der Befehl willkürlich, es geht nur darum, ob irgendeine Antwort kommt:
  bCmd[0] = 0x61; // STATUS..
  bCmd[1] = 0x20; // SENSE
  bCmd[2] = 0x01; // I want 1 byte back, the DATA-0
  bCmd[3] = 0x82; // Checksum

  wR=WriteComm(nCom,(LPSTR)bCmd,4); // Muß Antwort auslösen

  if (wR < 0)
    write_err();

  for(run=0;run < 1000;run++)
  {
    if (EV_RXCHAR == GetCommEventMask(nCom,EV_RXCHAR))  // irgenwas empfangen ?
    {
      getDeviceResponse(&unusedResponse,0x71,0x20,4); // Antwort zur obigen Anfrage
      CloseComm(nCom);
      return TRUE;  // Wenn was kommt, reicht das hier als Information
    }
  }

  CloseComm(nCom);
  return FALSE; // Passiert sehr oft, meistens ist einfach nicht eingeschaltet
}





































