Commits

spencercw committed 24a7eda

#49 Save the real-time clock state in the emulator save file.

  • Participants
  • Parent commits 54fde75

Comments (0)

Files changed (6)

File gb_emulator/gb.pb.cc

 const ::google::protobuf::Descriptor* GbVideoData_descriptor_ = NULL;
 const ::google::protobuf::internal::GeneratedMessageReflection*
   GbVideoData_reflection_ = NULL;
+const ::google::protobuf::Descriptor* GbTimerData_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+  GbTimerData_reflection_ = NULL;
 const ::google::protobuf::Descriptor* GbData_descriptor_ = NULL;
 const ::google::protobuf::internal::GeneratedMessageReflection*
   GbData_reflection_ = NULL;
       ::google::protobuf::DescriptorPool::generated_pool(),
       ::google::protobuf::MessageFactory::generated_factory(),
       sizeof(GbVideoData));
-  GbData_descriptor_ = file->message_type(4);
-  static const int GbData_offsets_[8] = {
+  GbTimerData_descriptor_ = file->message_type(4);
+  static const int GbTimerData_offsets_[5] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, real_time_clock_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, wall_clock_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, divider_countdown_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, timer_countdown_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, rtc_countdown_),
+  };
+  GbTimerData_reflection_ =
+    new ::google::protobuf::internal::GeneratedMessageReflection(
+      GbTimerData_descriptor_,
+      GbTimerData::default_instance_,
+      GbTimerData_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbTimerData, _unknown_fields_),
+      -1,
+      ::google::protobuf::DescriptorPool::generated_pool(),
+      ::google::protobuf::MessageFactory::generated_factory(),
+      sizeof(GbTimerData));
+  GbData_descriptor_ = file->message_type(5);
+  static const int GbData_offsets_[9] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, rom_),
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, rom_size_),
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, rom_checksum_),
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, memory_),
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, sound_),
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, video_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GbData, timers_),
   };
   GbData_reflection_ =
     new ::google::protobuf::internal::GeneratedMessageReflection(
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
     GbVideoData_descriptor_, &GbVideoData::default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+    GbTimerData_descriptor_, &GbTimerData::default_instance());
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
     GbData_descriptor_, &GbData::default_instance());
 }
 
   delete GbSoundData_Sound4_reflection_;
   delete GbVideoData::default_instance_;
   delete GbVideoData_reflection_;
+  delete GbTimerData::default_instance_;
+  delete GbTimerData_reflection_;
   delete GbData::default_instance_;
   delete GbData_reflection_;
 }
     "tdown\030\007 \002(\001\022\025\n\rcounter_steps\030\010 \002(\010\022\025\n\rco"
     "unter_index\030\t \002(\r\"W\n\013GbVideoData\022\024\n\014prio"
     "rity_map\030\001 \002(\014\022\026\n\016gbc_bg_palette\030\002 \002(\014\022\032"
-    "\n\022gbc_sprite_palette\030\003 \002(\014\"\274\001\n\006GbData\022\013\n"
-    "\003rom\030\001 \002(\t\022\020\n\010rom_size\030\002 \002(\r\022\024\n\014rom_chec"
-    "ksum\030\003 \002(\r\022\013\n\003gbc\030\004 \002(\010\022\027\n\003cpu\030\005 \002(\0132\n.G"
-    "bCpuData\022\035\n\006memory\030\006 \002(\0132\r.GbMemoryData\022"
-    "\033\n\005sound\030\007 \002(\0132\014.GbSoundData\022\033\n\005video\030\010 "
-    "\002(\0132\014.GbVideoData", 1737);
+    "\n\022gbc_sprite_palette\030\003 \002(\014\"\205\001\n\013GbTimerDa"
+    "ta\022\027\n\017real_time_clock\030\001 \002(\004\022\022\n\nwall_cloc"
+    "k\030\002 \002(\003\022\031\n\021divider_countdown\030\003 \002(\005\022\027\n\017ti"
+    "mer_countdown\030\004 \002(\005\022\025\n\rrtc_countdown\030\005 \002"
+    "(\005\"\332\001\n\006GbData\022\013\n\003rom\030\001 \002(\t\022\020\n\010rom_size\030\002"
+    " \002(\r\022\024\n\014rom_checksum\030\003 \002(\r\022\013\n\003gbc\030\004 \002(\010\022"
+    "\027\n\003cpu\030\005 \002(\0132\n.GbCpuData\022\035\n\006memory\030\006 \002(\013"
+    "2\r.GbMemoryData\022\033\n\005sound\030\007 \002(\0132\014.GbSound"
+    "Data\022\033\n\005video\030\010 \002(\0132\014.GbVideoData\022\034\n\006tim"
+    "ers\030\t \002(\0132\014.GbTimerData", 1903);
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
     "gb.proto", &protobuf_RegisterTypes);
   GbCpuData::default_instance_ = new GbCpuData();
   GbSoundData_Sound3::default_instance_ = new GbSoundData_Sound3();
   GbSoundData_Sound4::default_instance_ = new GbSoundData_Sound4();
   GbVideoData::default_instance_ = new GbVideoData();
+  GbTimerData::default_instance_ = new GbTimerData();
   GbData::default_instance_ = new GbData();
   GbCpuData::default_instance_->InitAsDefaultInstance();
   GbMemoryData::default_instance_->InitAsDefaultInstance();
   GbSoundData_Sound3::default_instance_->InitAsDefaultInstance();
   GbSoundData_Sound4::default_instance_->InitAsDefaultInstance();
   GbVideoData::default_instance_->InitAsDefaultInstance();
+  GbTimerData::default_instance_->InitAsDefaultInstance();
   GbData::default_instance_->InitAsDefaultInstance();
   ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_gb_2eproto);
 }
 // ===================================================================
 
 #ifndef _MSC_VER
+const int GbTimerData::kRealTimeClockFieldNumber;
+const int GbTimerData::kWallClockFieldNumber;
+const int GbTimerData::kDividerCountdownFieldNumber;
+const int GbTimerData::kTimerCountdownFieldNumber;
+const int GbTimerData::kRtcCountdownFieldNumber;
+#endif  // !_MSC_VER
+
+GbTimerData::GbTimerData()
+  : ::google::protobuf::Message() {
+  SharedCtor();
+}
+
+void GbTimerData::InitAsDefaultInstance() {
+}
+
+GbTimerData::GbTimerData(const GbTimerData& from)
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  MergeFrom(from);
+}
+
+void GbTimerData::SharedCtor() {
+  _cached_size_ = 0;
+  real_time_clock_ = GOOGLE_ULONGLONG(0);
+  wall_clock_ = GOOGLE_LONGLONG(0);
+  divider_countdown_ = 0;
+  timer_countdown_ = 0;
+  rtc_countdown_ = 0;
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+GbTimerData::~GbTimerData() {
+  SharedDtor();
+}
+
+void GbTimerData::SharedDtor() {
+  if (this != default_instance_) {
+  }
+}
+
+void GbTimerData::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* GbTimerData::descriptor() {
+  protobuf_AssignDescriptorsOnce();
+  return GbTimerData_descriptor_;
+}
+
+const GbTimerData& GbTimerData::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_gb_2eproto();  return *default_instance_;
+}
+
+GbTimerData* GbTimerData::default_instance_ = NULL;
+
+GbTimerData* GbTimerData::New() const {
+  return new GbTimerData;
+}
+
+void GbTimerData::Clear() {
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    real_time_clock_ = GOOGLE_ULONGLONG(0);
+    wall_clock_ = GOOGLE_LONGLONG(0);
+    divider_countdown_ = 0;
+    timer_countdown_ = 0;
+    rtc_countdown_ = 0;
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+  mutable_unknown_fields()->Clear();
+}
+
+bool GbTimerData::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+  ::google::protobuf::uint32 tag;
+  while ((tag = input->ReadTag()) != 0) {
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required uint64 real_time_clock = 1;
+      case 1: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+                 input, &real_time_clock_)));
+          set_has_real_time_clock();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(16)) goto parse_wall_clock;
+        break;
+      }
+      
+      // required int64 wall_clock = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_wall_clock:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+                 input, &wall_clock_)));
+          set_has_wall_clock();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(24)) goto parse_divider_countdown;
+        break;
+      }
+      
+      // required int32 divider_countdown = 3;
+      case 3: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_divider_countdown:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &divider_countdown_)));
+          set_has_divider_countdown();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(32)) goto parse_timer_countdown;
+        break;
+      }
+      
+      // required int32 timer_countdown = 4;
+      case 4: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_timer_countdown:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &timer_countdown_)));
+          set_has_timer_countdown();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(40)) goto parse_rtc_countdown;
+        break;
+      }
+      
+      // required int32 rtc_countdown = 5;
+      case 5: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_rtc_countdown:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &rtc_countdown_)));
+          set_has_rtc_countdown();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectAtEnd()) return true;
+        break;
+      }
+      
+      default: {
+      handle_uninterpreted:
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          return true;
+        }
+        DO_(::google::protobuf::internal::WireFormat::SkipField(
+              input, tag, mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+  return true;
+#undef DO_
+}
+
+void GbTimerData::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // required uint64 real_time_clock = 1;
+  if (has_real_time_clock()) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->real_time_clock(), output);
+  }
+  
+  // required int64 wall_clock = 2;
+  if (has_wall_clock()) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt64(2, this->wall_clock(), output);
+  }
+  
+  // required int32 divider_countdown = 3;
+  if (has_divider_countdown()) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->divider_countdown(), output);
+  }
+  
+  // required int32 timer_countdown = 4;
+  if (has_timer_countdown()) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->timer_countdown(), output);
+  }
+  
+  // required int32 rtc_countdown = 5;
+  if (has_rtc_countdown()) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(5, this->rtc_countdown(), output);
+  }
+  
+  if (!unknown_fields().empty()) {
+    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+        unknown_fields(), output);
+  }
+}
+
+::google::protobuf::uint8* GbTimerData::SerializeWithCachedSizesToArray(
+    ::google::protobuf::uint8* target) const {
+  // required uint64 real_time_clock = 1;
+  if (has_real_time_clock()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->real_time_clock(), target);
+  }
+  
+  // required int64 wall_clock = 2;
+  if (has_wall_clock()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(2, this->wall_clock(), target);
+  }
+  
+  // required int32 divider_countdown = 3;
+  if (has_divider_countdown()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->divider_countdown(), target);
+  }
+  
+  // required int32 timer_countdown = 4;
+  if (has_timer_countdown()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(4, this->timer_countdown(), target);
+  }
+  
+  // required int32 rtc_countdown = 5;
+  if (has_rtc_countdown()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(5, this->rtc_countdown(), target);
+  }
+  
+  if (!unknown_fields().empty()) {
+    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+        unknown_fields(), target);
+  }
+  return target;
+}
+
+int GbTimerData::ByteSize() const {
+  int total_size = 0;
+  
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required uint64 real_time_clock = 1;
+    if (has_real_time_clock()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::UInt64Size(
+          this->real_time_clock());
+    }
+    
+    // required int64 wall_clock = 2;
+    if (has_wall_clock()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int64Size(
+          this->wall_clock());
+    }
+    
+    // required int32 divider_countdown = 3;
+    if (has_divider_countdown()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->divider_countdown());
+    }
+    
+    // required int32 timer_countdown = 4;
+    if (has_timer_countdown()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->timer_countdown());
+    }
+    
+    // required int32 rtc_countdown = 5;
+    if (has_rtc_countdown()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->rtc_countdown());
+    }
+    
+  }
+  if (!unknown_fields().empty()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+        unknown_fields());
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void GbTimerData::MergeFrom(const ::google::protobuf::Message& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  const GbTimerData* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const GbTimerData*>(
+      &from);
+  if (source == NULL) {
+    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+  } else {
+    MergeFrom(*source);
+  }
+}
+
+void GbTimerData::MergeFrom(const GbTimerData& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from.has_real_time_clock()) {
+      set_real_time_clock(from.real_time_clock());
+    }
+    if (from.has_wall_clock()) {
+      set_wall_clock(from.wall_clock());
+    }
+    if (from.has_divider_countdown()) {
+      set_divider_countdown(from.divider_countdown());
+    }
+    if (from.has_timer_countdown()) {
+      set_timer_countdown(from.timer_countdown());
+    }
+    if (from.has_rtc_countdown()) {
+      set_rtc_countdown(from.rtc_countdown());
+    }
+  }
+  mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+}
+
+void GbTimerData::CopyFrom(const ::google::protobuf::Message& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void GbTimerData::CopyFrom(const GbTimerData& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool GbTimerData::IsInitialized() const {
+  if ((_has_bits_[0] & 0x0000001f) != 0x0000001f) return false;
+  
+  return true;
+}
+
+void GbTimerData::Swap(GbTimerData* other) {
+  if (other != this) {
+    std::swap(real_time_clock_, other->real_time_clock_);
+    std::swap(wall_clock_, other->wall_clock_);
+    std::swap(divider_countdown_, other->divider_countdown_);
+    std::swap(timer_countdown_, other->timer_countdown_);
+    std::swap(rtc_countdown_, other->rtc_countdown_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::google::protobuf::Metadata GbTimerData::GetMetadata() const {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::Metadata metadata;
+  metadata.descriptor = GbTimerData_descriptor_;
+  metadata.reflection = GbTimerData_reflection_;
+  return metadata;
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
 const int GbData::kRomFieldNumber;
 const int GbData::kRomSizeFieldNumber;
 const int GbData::kRomChecksumFieldNumber;
 const int GbData::kMemoryFieldNumber;
 const int GbData::kSoundFieldNumber;
 const int GbData::kVideoFieldNumber;
+const int GbData::kTimersFieldNumber;
 #endif  // !_MSC_VER
 
 GbData::GbData()
   memory_ = const_cast< ::GbMemoryData*>(&::GbMemoryData::default_instance());
   sound_ = const_cast< ::GbSoundData*>(&::GbSoundData::default_instance());
   video_ = const_cast< ::GbVideoData*>(&::GbVideoData::default_instance());
+  timers_ = const_cast< ::GbTimerData*>(&::GbTimerData::default_instance());
 }
 
 GbData::GbData(const GbData& from)
   memory_ = NULL;
   sound_ = NULL;
   video_ = NULL;
+  timers_ = NULL;
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
     delete memory_;
     delete sound_;
     delete video_;
+    delete timers_;
   }
 }
 
       if (video_ != NULL) video_->::GbVideoData::Clear();
     }
   }
+  if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    if (has_timers()) {
+      if (timers_ != NULL) timers_->::GbTimerData::Clear();
+    }
+  }
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
   mutable_unknown_fields()->Clear();
 }
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(74)) goto parse_timers;
+        break;
+      }
+      
+      // required .GbTimerData timers = 9;
+      case 9: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+         parse_timers:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+               input, mutable_timers()));
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
       8, this->video(), output);
   }
   
+  // required .GbTimerData timers = 9;
+  if (has_timers()) {
+    ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
+      9, this->timers(), output);
+  }
+  
   if (!unknown_fields().empty()) {
     ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
         unknown_fields(), output);
         8, this->video(), target);
   }
   
+  // required .GbTimerData timers = 9;
+  if (has_timers()) {
+    target = ::google::protobuf::internal::WireFormatLite::
+      WriteMessageNoVirtualToArray(
+        9, this->timers(), target);
+  }
+  
   if (!unknown_fields().empty()) {
     target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
         unknown_fields(), target);
     }
     
   }
+  if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    // required .GbTimerData timers = 9;
+    if (has_timers()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+          this->timers());
+    }
+    
+  }
   if (!unknown_fields().empty()) {
     total_size +=
       ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
       mutable_video()->::GbVideoData::MergeFrom(from.video());
     }
   }
+  if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+    if (from.has_timers()) {
+      mutable_timers()->::GbTimerData::MergeFrom(from.timers());
+    }
+  }
   mutable_unknown_fields()->MergeFrom(from.unknown_fields());
 }
 
 }
 
 bool GbData::IsInitialized() const {
-  if ((_has_bits_[0] & 0x000000ff) != 0x000000ff) return false;
+  if ((_has_bits_[0] & 0x000001ff) != 0x000001ff) return false;
   
   if (has_cpu()) {
     if (!this->cpu().IsInitialized()) return false;
   if (has_video()) {
     if (!this->video().IsInitialized()) return false;
   }
+  if (has_timers()) {
+    if (!this->timers().IsInitialized()) return false;
+  }
   return true;
 }
 
     std::swap(memory_, other->memory_);
     std::swap(sound_, other->sound_);
     std::swap(video_, other->video_);
+    std::swap(timers_, other->timers_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     _unknown_fields_.Swap(&other->_unknown_fields_);
     std::swap(_cached_size_, other->_cached_size_);

File gb_emulator/gb.pb.h

 class GbSoundData_Sound3;
 class GbSoundData_Sound4;
 class GbVideoData;
+class GbTimerData;
 class GbData;
 
 // ===================================================================
 };
 // -------------------------------------------------------------------
 
-class GbData : public ::google::protobuf::Message {
+class GbTimerData : public ::google::protobuf::Message {
  public:
-  GbData();
-  virtual ~GbData();
-  
-  GbData(const GbData& from);
-  
-  inline GbData& operator=(const GbData& from) {
+  GbTimerData();
+  virtual ~GbTimerData();
+  
+  GbTimerData(const GbTimerData& from);
+  
+  inline GbTimerData& operator=(const GbTimerData& from) {
     CopyFrom(from);
     return *this;
   }
   }
   
   static const ::google::protobuf::Descriptor* descriptor();
-  static const GbData& default_instance();
-  
-  void Swap(GbData* other);
+  static const GbTimerData& default_instance();
+  
+  void Swap(GbTimerData* other);
   
   // implements Message ----------------------------------------------
   
-  GbData* New() const;
+  GbTimerData* New() const;
   void CopyFrom(const ::google::protobuf::Message& from);
   void MergeFrom(const ::google::protobuf::Message& from);
-  void CopyFrom(const GbData& from);
-  void MergeFrom(const GbData& from);
+  void CopyFrom(const GbTimerData& from);
+  void MergeFrom(const GbTimerData& from);
   void Clear();
   bool IsInitialized() const;
   
   
   // accessors -------------------------------------------------------
   
+  // required uint64 real_time_clock = 1;
+  inline bool has_real_time_clock() const;
+  inline void clear_real_time_clock();
+  static const int kRealTimeClockFieldNumber = 1;
+  inline ::google::protobuf::uint64 real_time_clock() const;
+  inline void set_real_time_clock(::google::protobuf::uint64 value);
+  
+  // required int64 wall_clock = 2;
+  inline bool has_wall_clock() const;
+  inline void clear_wall_clock();
+  static const int kWallClockFieldNumber = 2;
+  inline ::google::protobuf::int64 wall_clock() const;
+  inline void set_wall_clock(::google::protobuf::int64 value);
+  
+  // required int32 divider_countdown = 3;
+  inline bool has_divider_countdown() const;
+  inline void clear_divider_countdown();
+  static const int kDividerCountdownFieldNumber = 3;
+  inline ::google::protobuf::int32 divider_countdown() const;
+  inline void set_divider_countdown(::google::protobuf::int32 value);
+  
+  // required int32 timer_countdown = 4;
+  inline bool has_timer_countdown() const;
+  inline void clear_timer_countdown();
+  static const int kTimerCountdownFieldNumber = 4;
+  inline ::google::protobuf::int32 timer_countdown() const;
+  inline void set_timer_countdown(::google::protobuf::int32 value);
+  
+  // required int32 rtc_countdown = 5;
+  inline bool has_rtc_countdown() const;
+  inline void clear_rtc_countdown();
+  static const int kRtcCountdownFieldNumber = 5;
+  inline ::google::protobuf::int32 rtc_countdown() const;
+  inline void set_rtc_countdown(::google::protobuf::int32 value);
+  
+  // @@protoc_insertion_point(class_scope:GbTimerData)
+ private:
+  inline void set_has_real_time_clock();
+  inline void clear_has_real_time_clock();
+  inline void set_has_wall_clock();
+  inline void clear_has_wall_clock();
+  inline void set_has_divider_countdown();
+  inline void clear_has_divider_countdown();
+  inline void set_has_timer_countdown();
+  inline void clear_has_timer_countdown();
+  inline void set_has_rtc_countdown();
+  inline void clear_has_rtc_countdown();
+  
+  ::google::protobuf::UnknownFieldSet _unknown_fields_;
+  
+  ::google::protobuf::uint64 real_time_clock_;
+  ::google::protobuf::int64 wall_clock_;
+  ::google::protobuf::int32 divider_countdown_;
+  ::google::protobuf::int32 timer_countdown_;
+  ::google::protobuf::int32 rtc_countdown_;
+  
+  mutable int _cached_size_;
+  ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32];
+  
+  friend void  protobuf_AddDesc_gb_2eproto();
+  friend void protobuf_AssignDesc_gb_2eproto();
+  friend void protobuf_ShutdownFile_gb_2eproto();
+  
+  void InitAsDefaultInstance();
+  static GbTimerData* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class GbData : public ::google::protobuf::Message {
+ public:
+  GbData();
+  virtual ~GbData();
+  
+  GbData(const GbData& from);
+  
+  inline GbData& operator=(const GbData& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _unknown_fields_;
+  }
+  
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return &_unknown_fields_;
+  }
+  
+  static const ::google::protobuf::Descriptor* descriptor();
+  static const GbData& default_instance();
+  
+  void Swap(GbData* other);
+  
+  // implements Message ----------------------------------------------
+  
+  GbData* New() const;
+  void CopyFrom(const ::google::protobuf::Message& from);
+  void MergeFrom(const ::google::protobuf::Message& from);
+  void CopyFrom(const GbData& from);
+  void MergeFrom(const GbData& from);
+  void Clear();
+  bool IsInitialized() const;
+  
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  public:
+  
+  ::google::protobuf::Metadata GetMetadata() const;
+  
+  // nested types ----------------------------------------------------
+  
+  // accessors -------------------------------------------------------
+  
   // required string rom = 1;
   inline bool has_rom() const;
   inline void clear_rom();
   inline ::GbVideoData* mutable_video();
   inline ::GbVideoData* release_video();
   
+  // required .GbTimerData timers = 9;
+  inline bool has_timers() const;
+  inline void clear_timers();
+  static const int kTimersFieldNumber = 9;
+  inline const ::GbTimerData& timers() const;
+  inline ::GbTimerData* mutable_timers();
+  inline ::GbTimerData* release_timers();
+  
   // @@protoc_insertion_point(class_scope:GbData)
  private:
   inline void set_has_rom();
   inline void clear_has_sound();
   inline void set_has_video();
   inline void clear_has_video();
+  inline void set_has_timers();
+  inline void clear_has_timers();
   
   ::google::protobuf::UnknownFieldSet _unknown_fields_;
   
   ::GbMemoryData* memory_;
   ::GbSoundData* sound_;
   ::GbVideoData* video_;
+  ::GbTimerData* timers_;
   bool gbc_;
   
   mutable int _cached_size_;
-  ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(9 + 31) / 32];
   
   friend void  protobuf_AddDesc_gb_2eproto();
   friend void protobuf_AssignDesc_gb_2eproto();
 
 // -------------------------------------------------------------------
 
+// GbTimerData
+
+// required uint64 real_time_clock = 1;
+inline bool GbTimerData::has_real_time_clock() const {
+  return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void GbTimerData::set_has_real_time_clock() {
+  _has_bits_[0] |= 0x00000001u;
+}
+inline void GbTimerData::clear_has_real_time_clock() {
+  _has_bits_[0] &= ~0x00000001u;
+}
+inline void GbTimerData::clear_real_time_clock() {
+  real_time_clock_ = GOOGLE_ULONGLONG(0);
+  clear_has_real_time_clock();
+}
+inline ::google::protobuf::uint64 GbTimerData::real_time_clock() const {
+  return real_time_clock_;
+}
+inline void GbTimerData::set_real_time_clock(::google::protobuf::uint64 value) {
+  set_has_real_time_clock();
+  real_time_clock_ = value;
+}
+
+// required int64 wall_clock = 2;
+inline bool GbTimerData::has_wall_clock() const {
+  return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void GbTimerData::set_has_wall_clock() {
+  _has_bits_[0] |= 0x00000002u;
+}
+inline void GbTimerData::clear_has_wall_clock() {
+  _has_bits_[0] &= ~0x00000002u;
+}
+inline void GbTimerData::clear_wall_clock() {
+  wall_clock_ = GOOGLE_LONGLONG(0);
+  clear_has_wall_clock();
+}
+inline ::google::protobuf::int64 GbTimerData::wall_clock() const {
+  return wall_clock_;
+}
+inline void GbTimerData::set_wall_clock(::google::protobuf::int64 value) {
+  set_has_wall_clock();
+  wall_clock_ = value;
+}
+
+// required int32 divider_countdown = 3;
+inline bool GbTimerData::has_divider_countdown() const {
+  return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void GbTimerData::set_has_divider_countdown() {
+  _has_bits_[0] |= 0x00000004u;
+}
+inline void GbTimerData::clear_has_divider_countdown() {
+  _has_bits_[0] &= ~0x00000004u;
+}
+inline void GbTimerData::clear_divider_countdown() {
+  divider_countdown_ = 0;
+  clear_has_divider_countdown();
+}
+inline ::google::protobuf::int32 GbTimerData::divider_countdown() const {
+  return divider_countdown_;
+}
+inline void GbTimerData::set_divider_countdown(::google::protobuf::int32 value) {
+  set_has_divider_countdown();
+  divider_countdown_ = value;
+}
+
+// required int32 timer_countdown = 4;
+inline bool GbTimerData::has_timer_countdown() const {
+  return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void GbTimerData::set_has_timer_countdown() {
+  _has_bits_[0] |= 0x00000008u;
+}
+inline void GbTimerData::clear_has_timer_countdown() {
+  _has_bits_[0] &= ~0x00000008u;
+}
+inline void GbTimerData::clear_timer_countdown() {
+  timer_countdown_ = 0;
+  clear_has_timer_countdown();
+}
+inline ::google::protobuf::int32 GbTimerData::timer_countdown() const {
+  return timer_countdown_;
+}
+inline void GbTimerData::set_timer_countdown(::google::protobuf::int32 value) {
+  set_has_timer_countdown();
+  timer_countdown_ = value;
+}
+
+// required int32 rtc_countdown = 5;
+inline bool GbTimerData::has_rtc_countdown() const {
+  return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void GbTimerData::set_has_rtc_countdown() {
+  _has_bits_[0] |= 0x00000010u;
+}
+inline void GbTimerData::clear_has_rtc_countdown() {
+  _has_bits_[0] &= ~0x00000010u;
+}
+inline void GbTimerData::clear_rtc_countdown() {
+  rtc_countdown_ = 0;
+  clear_has_rtc_countdown();
+}
+inline ::google::protobuf::int32 GbTimerData::rtc_countdown() const {
+  return rtc_countdown_;
+}
+inline void GbTimerData::set_rtc_countdown(::google::protobuf::int32 value) {
+  set_has_rtc_countdown();
+  rtc_countdown_ = value;
+}
+
+// -------------------------------------------------------------------
+
 // GbData
 
 // required string rom = 1;
   return temp;
 }
 
+// required .GbTimerData timers = 9;
+inline bool GbData::has_timers() const {
+  return (_has_bits_[0] & 0x00000100u) != 0;
+}
+inline void GbData::set_has_timers() {
+  _has_bits_[0] |= 0x00000100u;
+}
+inline void GbData::clear_has_timers() {
+  _has_bits_[0] &= ~0x00000100u;
+}
+inline void GbData::clear_timers() {
+  if (timers_ != NULL) timers_->::GbTimerData::Clear();
+  clear_has_timers();
+}
+inline const ::GbTimerData& GbData::timers() const {
+  return timers_ != NULL ? *timers_ : *default_instance_->timers_;
+}
+inline ::GbTimerData* GbData::mutable_timers() {
+  set_has_timers();
+  if (timers_ == NULL) timers_ = new ::GbTimerData;
+  return timers_;
+}
+inline ::GbTimerData* GbData::release_timers() {
+  clear_has_timers();
+  ::GbTimerData* temp = timers_;
+  timers_ = NULL;
+  return temp;
+}
+
 
 // @@protoc_insertion_point(namespace_scope)
 

File gb_emulator/gb.proto

 	required bytes gbc_sprite_palette     = 3;
 }
 
+message GbTimerData
+{
+	required uint64 real_time_clock    = 1;
+	required int64  wall_clock         = 2;
+
+	required int32  divider_countdown  = 3;
+	required int32  timer_countdown    = 4;
+	required int32  rtc_countdown      = 5;
+}
+
 message GbData
 {
 	required string rom           = 1;
 	required GbMemoryData memory  = 6;
 	required GbSoundData sound    = 7;
 	required GbVideoData video    = 8;
+	required GbTimerData timers   = 9;
 }

File gb_emulator/include/gb_emulator/gb_timers.hpp

 #include <stdint.h>
 
 class Gb;
+class GbTimerData;
 
 //! GameBoy timers emulator.
 class GbTimers
 	//! Writes the given RTC register values back into the RTC clock.
 	void rtcWrite(const uint8_t registers[5]);
 
+	//! Saves the current state of the timers into the given message.
+	void save(GbTimerData &data) const;
+
+	//! Loads the timers state from the given message.
+	void load(const GbTimerData &data);
+
 private:
 	Gb &gb_;
 

File gb_emulator/src/gb.cpp

 	mem_->load(data.memory());
 	sound_->load(data.sound());
 	video_->load(data.video());
+	timers_.load(data.timers());
 }
 
 void Gb::save() const
 	mem_->save(*data.mutable_memory());
 	sound_->save(*data.mutable_sound());
 	video_->save(*data.mutable_video());
+	timers_.save(*data.mutable_timers());
 
 	//data.PrintDebugString();
 

File gb_emulator/src/gb_timers.cpp

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#include <time.h>
+
 #include <algorithm>
 
+#ifdef _MSC_VER
+#pragma warning(push, 0)
+#endif
+
+#include "gb.pb.h"
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 #include <gb_emulator/gb.hpp>
 #include <gb_emulator/gb_memory.hpp>
 #include <gb_emulator/gb_timers.hpp>
 		registers[RTC_M] * 60 +
 		registers[RTC_S];
 }
+
+void GbTimers::save(GbTimerData &data) const
+{
+	data.set_real_time_clock(rtc_);
+	data.set_wall_clock(time(NULL));
+	data.set_divider_countdown(dividerCountdown_);
+	data.set_timer_countdown(timerCountdown_);
+	data.set_rtc_countdown(rtcCountdown_);
+}
+
+void GbTimers::load(const GbTimerData &data)
+{
+	rtc_ = data.real_time_clock();
+
+	// Update the real-time clock with the time that has elapsed since the data was saved
+	int64_t oldWallClock = data.wall_clock();
+	int64_t wallClock = time(NULL);
+	if (wallClock > oldWallClock)
+	{
+		rtc_ += wallClock - oldWallClock;
+	}
+
+	dividerCountdown_ = data.divider_countdown();
+	timerCountdown_ = data.timer_countdown();
+	rtcCountdown_ = data.rtc_countdown();
+}