// File: ke6jjj[P]r-dat/AudioFrameReceiver.cc
// Function: AudioFrameReceiver::HandleDateTime(unsigned char const*, unsigned int)
void
AudioFrameReceiver::HandleDateTime(const uint8_t *item, uint32_t absoluteFrame)
{
  uint8_t dow, year, mon, day, hour, min, sec;
  int likelyYear;
  bool timeIsValid = false;
  bool droppedSync = false;
  uint64_t abs_seconds;

  if (item != NULL) {
    //
    // Date and time.
    //
    dow = item[0] & 0xf;
    year = BCDDecode(item[1]);
    mon = BCDDecode(item[2]);
    day = BCDDecode(item[3]);
    hour = BCDDecode(item[4]);
    min = BCDDecode(item[5]);
    sec = BCDDecode(item[6]);

    if (year < 80)
      likelyYear = year + 2000;
    else
      likelyYear = year + 1900;
    
    //
    // Check if anything seems funny (bad BCD decodes)
    //
    if (mon != 100 && day != 100 && hour != 100 && min != 100 && sec != 100) {
      //
      // Determine the absolute date time in seconds since 1900.
      //
      abs_seconds = SecondsSince1900(likelyYear, mon, day, hour, min, sec);
      timeIsValid = true;
    }
  } else {
    dow = year = mon = day = hour = min = sec = 0;
  }

  if (mHaveDateTimeSync) {
    //
    // Syncronization is in effect. Make sure that everything is still ok.
    //
    if (timeIsValid && absoluteFrame == mLastAbsoluteFrameNumber + 1) {
      //
      // Everything seems ok so far. We've got a new timestamp and there
      // hasn't been a discontiuity in the frame stream.
      //
      // Advance our current clock by 30ms.
      //
      mCurrentDateTimeMilliseconds += 30;
      if (mCurrentDateTimeMilliseconds >= 1000) {
        mCurrentDateTimeMilliseconds %= 1000;
        mCurrentDateTimeSeconds += 1;
      }

      //
      // Does the second hand of our clock still match the transmitted
      // clock?
      //
      if (mCurrentDateTimeSeconds != abs_seconds) {
        //
        // No. We're no longer in sync.
        //
        mHaveDateTimeSync = false;
        droppedSync = true;
      }
    } else {
      //
      // We lost date and time sub-code or there was a jump in absolute
      // time.
      //
      mHaveDateTimeSync = false;
      droppedSync = true;
    }
  }

  //
  // Regardless of the current sync state, keep the closest-to-the-second
  // engine running, possibly establishing sync when it detects a long
  // (34-frame) second.
  //
  if (mHaveLastDateTime && timeIsValid) {
    //
    // We have a previous date and time stamp. Has the time changed
    // since then?
    //
    if (mLastDateTimeSeconds != abs_seconds) {
      //
      // The time has changed. Is it a discontinuity or is it a simple
      // advancement?
      //
      if (abs_seconds == mLastDateTimeSeconds + 1) {
        //
        // Simple advancement. This is a chance to achive a frame
        // lock and/or confirm that the lock is still in place.
        // In order to do so, however, we have to observe two successive
        // increments. Do we have a previous increment observed?
        //           
        if (mHaveLastChangeFrame) {
          //
          // DAT provides 33 1/3 frames per second. Since timestamps
          // can only be placed at the begining of frames, timestamps
          // should advance in the following pattern:
          //
          // 1. 34 frames (No millisecond offset)
          // 2. 33 frames (20 millisecond offset)
          // 3. 33 frames (10 millisecond offset)
          //
          // The timestamp that started with the 34-frame sequence is the
          // one that is closest to the real start of the second.
          //
          uint32_t frames_per_second = absoluteFrame - mLastChangeFrame;
          if (frames_per_second == 34) {
            //
            // We've just received received the frame that ends pattern
            // 1 and starts pattern 2. This means this frame marks the
            // start of pattern 2.
            //
            // If we aren't time synchronized then we can be now.
            //
            if (!mHaveDateTimeSync) {
              //
              // We don't currently have a frame lock. Let's mark
              // that we do and that the last second change occured as
              // pattern 1.
              //
              mHaveDateTimeSync = true;
              mCurrentDateTimeMilliseconds = 20;
              mCurrentDateTimeSeconds = abs_seconds;
            }
          }
        }

        //
        // Mark that this is the new last change frame.
        //
        mHaveLastChangeFrame = true;
        mLastChangeFrame = absoluteFrame;
      } else {
        //
        // There's been a bad discontinuity. Mark that we no longer
        // have a good roll-over frame.
        //
        mHaveLastChangeFrame = false;
      }
    }
  }
    
  //
  // Record that we now have a date and time.
  //
  mHaveLastDateTime = timeIsValid;
  mLastDateTimeSeconds = abs_seconds;

  if (droppedSync && mHaveDateTimeSync)
    printf("Date     time: ------- SYNC DROPPED AND REESTABLISHED --------\n");
  else if (droppedSync)
    printf("Date     time: ------- SYNC DROPPED --------------------------\n");

  if (mHaveDateTimeSync) {
    printf("Date     time: %02d %04d-%02d-%02d %02d:%02d:%02d.%03d (SYNCED)\n",
      dow,
      likelyYear,
      mon,
      day,
      hour,
      min,
      sec,
      mCurrentDateTimeMilliseconds
    );
  } else if (timeIsValid) {
    printf("Date     time: %02d %04d-%02d-%02d %02d:%02d:%02d\n",
      dow,
      likelyYear,
      mon,
      day,
      hour,
      min,
      sec
    );
  }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x60, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movl %edx, -0x14(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x40(%rbp)
movb $0x0, -0x21(%rbp)
movb $0x0, -0x22(%rbp)
cmpq $0x0, -0x10(%rbp)
je 0x6345
movq -0x10(%rbp), %rax
movzbl (%rax), %eax
andl $0xf, %eax
movb %al, -0x15(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x1(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x16(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x2(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x17(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x3(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x18(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x4(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x19(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x5(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x1a(%rbp)
movq -0x10(%rbp), %rax
movzbl 0x6(%rax), %eax
movzwl %ax, %edi
callq 0xce20
movb %al, -0x1b(%rbp)
movzbl -0x16(%rbp), %eax
cmpl $0x50, %eax
jge 0x62e4
movzbl -0x16(%rbp), %eax
addl $0x7d0, %eax            # imm = 0x7D0
movl %eax, -0x20(%rbp)
jmp 0x62f0
movzbl -0x16(%rbp), %eax
addl $0x76c, %eax            # imm = 0x76C
movl %eax, -0x20(%rbp)
movzbl -0x17(%rbp), %eax
cmpl $0x64, %eax
je 0x6343
movzbl -0x18(%rbp), %eax
cmpl $0x64, %eax
je 0x6343
movzbl -0x19(%rbp), %eax
cmpl $0x64, %eax
je 0x6343
movzbl -0x1a(%rbp), %eax
cmpl $0x64, %eax
je 0x6343
movzbl -0x1b(%rbp), %eax
cmpl $0x64, %eax
je 0x6343
movl -0x20(%rbp), %edi
movzbl -0x17(%rbp), %esi
movzbl -0x18(%rbp), %edx
movzbl -0x19(%rbp), %ecx
movzbl -0x1a(%rbp), %r8d
movzbl -0x1b(%rbp), %r9d
callq 0x65c0
movq %rax, -0x30(%rbp)
movb $0x1, -0x21(%rbp)
jmp 0x6361
movb $0x0, -0x1b(%rbp)
movb $0x0, -0x1a(%rbp)
movb $0x0, -0x19(%rbp)
movb $0x0, -0x18(%rbp)
movb $0x0, -0x17(%rbp)
movb $0x0, -0x16(%rbp)
movb $0x0, -0x15(%rbp)
movq -0x40(%rbp), %rax
testb $0x1, 0x2dd0(%rax)
je 0x6410
testb $0x1, -0x21(%rbp)
je 0x63ff
movq -0x40(%rbp), %rcx
movl -0x14(%rbp), %eax
movl 0x2db4(%rcx), %ecx
addl $0x1, %ecx
cmpl %ecx, %eax
jne 0x63ff
movq -0x40(%rbp), %rax
movl 0x2dd4(%rax), %ecx
addl $0x1e, %ecx
movl %ecx, 0x2dd4(%rax)
cmpl $0x3e8, 0x2dd4(%rax)    # imm = 0x3E8
jl 0x63dd
movq -0x40(%rbp), %rax
movl 0x2dd4(%rax), %eax
movl $0x3e8, %ecx            # imm = 0x3E8
cltd
idivl %ecx
movq -0x40(%rbp), %rax
movl %edx, 0x2dd4(%rax)
movq 0x2dd8(%rax), %rcx
addq $0x1, %rcx
movq %rcx, 0x2dd8(%rax)
movq -0x40(%rbp), %rax
movq 0x2dd8(%rax), %rax
cmpq -0x30(%rbp), %rax
je 0x63fd
movq -0x40(%rbp), %rax
movb $0x0, 0x2dd0(%rax)
movb $0x1, -0x22(%rbp)
jmp 0x640e
movq -0x40(%rbp), %rax
movb $0x0, 0x2dd0(%rax)
movb $0x1, -0x22(%rbp)
jmp 0x6410
movq -0x40(%rbp), %rax
testb $0x1, 0x2dbc(%rax)
je 0x64d1
testb $0x1, -0x21(%rbp)
je 0x64d1
movq -0x40(%rbp), %rax
movq 0x2dc8(%rax), %rax
cmpq -0x30(%rbp), %rax
je 0x64cf
movq -0x40(%rbp), %rcx
movq -0x30(%rbp), %rax
movq 0x2dc8(%rcx), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jne 0x64c2
movq -0x40(%rbp), %rax
testb $0x1, 0x2dbd(%rax)
je 0x64ac
movq -0x40(%rbp), %rcx
movl -0x14(%rbp), %eax
subl 0x2dc0(%rcx), %eax
movl %eax, -0x34(%rbp)
cmpl $0x22, -0x34(%rbp)
jne 0x64aa
movq -0x40(%rbp), %rax
testb $0x1, 0x2dd0(%rax)
jne 0x64a8
movq -0x40(%rbp), %rax
movb $0x1, 0x2dd0(%rax)
movl $0x14, 0x2dd4(%rax)
movq -0x30(%rbp), %rcx
movq %rcx, 0x2dd8(%rax)
jmp 0x64aa
jmp 0x64ac
movq -0x40(%rbp), %rax
movb $0x1, 0x2dbd(%rax)
movl -0x14(%rbp), %ecx
movl %ecx, 0x2dc0(%rax)
jmp 0x64cd
movq -0x40(%rbp), %rax
movb $0x0, 0x2dbd(%rax)
jmp 0x64cf
jmp 0x64d1
movq -0x40(%rbp), %rax
movb -0x21(%rbp), %cl
andb $0x1, %cl
movb %cl, 0x2dbc(%rax)
movq -0x30(%rbp), %rcx
movq %rcx, 0x2dc8(%rax)
testb $0x1, -0x22(%rbp)
je 0x650f
movq -0x40(%rbp), %rax
testb $0x1, 0x2dd0(%rax)
je 0x650f
leaq 0x8b27(%rip), %rdi      # 0xf02d
movb $0x0, %al
callq 0x2040
jmp 0x6525
testb $0x1, -0x22(%rbp)
je 0x6523
leaq 0x8b51(%rip), %rdi      # 0xf06d
movb $0x0, %al
callq 0x2040
jmp 0x6525
movq -0x40(%rbp), %rax
testb $0x1, 0x2dd0(%rax)
je 0x6578
movq -0x40(%rbp), %rax
movzbl -0x15(%rbp), %esi
movl -0x20(%rbp), %edx
movzbl -0x17(%rbp), %ecx
movzbl -0x18(%rbp), %r8d
movzbl -0x19(%rbp), %r9d
movzbl -0x1a(%rbp), %r11d
movzbl -0x1b(%rbp), %r10d
movl 0x2dd4(%rax), %eax
leaq 0x8b4b(%rip), %rdi      # 0xf0ad
movl %r11d, (%rsp)
movl %r10d, 0x8(%rsp)
movl %eax, 0x10(%rsp)
movb $0x0, %al
callq 0x2040
jmp 0x65b4
testb $0x1, -0x21(%rbp)
je 0x65b2
movzbl -0x15(%rbp), %esi
movl -0x20(%rbp), %edx
movzbl -0x17(%rbp), %ecx
movzbl -0x18(%rbp), %r8d
movzbl -0x19(%rbp), %r9d
movzbl -0x1a(%rbp), %r10d
movzbl -0x1b(%rbp), %eax
leaq 0x8b4b(%rip), %rdi      # 0xf0ee
movl %r10d, (%rsp)
movl %eax, 0x8(%rsp)
movb $0x0, %al
callq 0x2040
jmp 0x65b4
addq $0x60, %rsp
popq %rbp
retq
nopw (%rax,%rax)