1// Copyright 2013, ARM Limited
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7//   * Redistributions of source code must retain the above copyright notice,
8//     this list of conditions and the following disclaimer.
9//   * Redistributions in binary form must reproduce the above copyright notice,
10//     this list of conditions and the following disclaimer in the documentation
11//     and/or other materials provided with the distribution.
12//   * Neither the name of ARM Limited nor the names of its contributors may be
13//     used to endorse or promote products derived from this software without
14//     specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#ifndef VIXL_A64_SIMULATOR_A64_H_
28#define VIXL_A64_SIMULATOR_A64_H_
29
30#include "globals-vixl.h"
31#include "utils-vixl.h"
32#include "a64/instructions-a64.h"
33#include "a64/assembler-a64.h"
34#include "a64/disasm-a64.h"
35#include "a64/instrument-a64.h"
36
37namespace vixl {
38
39enum ReverseByteMode {
40  Reverse16 = 0,
41  Reverse32 = 1,
42  Reverse64 = 2
43};
44
45// Printf. See debugger-a64.h for more information on pseudo instructions.
46//  - arg_count: The number of arguments.
47//  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
48//
49// Simulate a call to printf.
50//
51// Floating-point and integer arguments are passed in separate sets of registers
52// in AAPCS64 (even for varargs functions), so it is not possible to determine
53// the type of each argument without some information about the values that were
54// passed in. This information could be retrieved from the printf format string,
55// but the format string is not trivial to parse so we encode the relevant
56// information with the HLT instruction.
57//
58// The interface is as follows:
59//    x0: The format string
60// x1-x7: Optional arguments, if type == CPURegister::kRegister
61// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
62const Instr kPrintfOpcode = 0xdeb1;
63const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
64const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
65const unsigned kPrintfLength = 3 * kInstructionSize;
66
67const unsigned kPrintfMaxArgCount = 4;
68
69// The argument pattern is a set of two-bit-fields, each with one of the
70// following values:
71enum PrintfArgPattern {
72  kPrintfArgW = 1,
73  kPrintfArgX = 2,
74  // There is no kPrintfArgS because floats are always converted to doubles in C
75  // varargs calls.
76  kPrintfArgD = 3
77};
78static const unsigned kPrintfArgPatternBits = 2;
79
80
81// The proper way to initialize a simulated system register (such as NZCV) is as
82// follows:
83//  SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV);
84class SimSystemRegister {
85 public:
86  // The default constructor represents a register which has no writable bits.
87  // It is not possible to set its value to anything other than 0.
88  SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }
89
90  inline uint32_t RawValue() const {
91    return value_;
92  }
93
94  inline void SetRawValue(uint32_t new_value) {
95    value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
96  }
97
98  inline uint32_t Bits(int msb, int lsb) const {
99    return unsigned_bitextract_32(msb, lsb, value_);
100  }
101
102  inline int32_t SignedBits(int msb, int lsb) const {
103    return signed_bitextract_32(msb, lsb, value_);
104  }
105
106  void SetBits(int msb, int lsb, uint32_t bits);
107
108  // Default system register values.
109  static SimSystemRegister DefaultValueFor(SystemRegister id);
110
111#define DEFINE_GETTER(Name, HighBit, LowBit, Func)                            \
112  inline uint32_t Name() const { return Func(HighBit, LowBit); }              \
113  inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); }
114#define DEFINE_WRITE_IGNORE_MASK(Name, Mask)                                  \
115  static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask);
116
117  SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK)
118
119#undef DEFINE_ZERO_BITS
120#undef DEFINE_GETTER
121
122 protected:
123  // Most system registers only implement a few of the bits in the word. Other
124  // bits are "read-as-zero, write-ignored". The write_ignore_mask argument
125  // describes the bits which are not modifiable.
126  SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
127      : value_(value), write_ignore_mask_(write_ignore_mask) { }
128
129  uint32_t value_;
130  uint32_t write_ignore_mask_;
131};
132
133
134// Represent a register (r0-r31, v0-v31).
135template<int kSizeInBytes>
136class SimRegisterBase {
137 public:
138  template<typename T>
139  void Set(T new_value, unsigned size = sizeof(T)) {
140    VIXL_ASSERT(size <= kSizeInBytes);
141    VIXL_ASSERT(size <= sizeof(new_value));
142    // All AArch64 registers are zero-extending; Writing a W register clears the
143    // top bits of the corresponding X register.
144    memset(value_, 0, kSizeInBytes);
145    memcpy(value_, &new_value, size);
146  }
147
148  // Copy 'size' bytes of the register to the result, and zero-extend to fill
149  // the result.
150  template<typename T>
151  T Get(unsigned size = sizeof(T)) const {
152    VIXL_ASSERT(size <= kSizeInBytes);
153    T result;
154    memset(&result, 0, sizeof(result));
155    memcpy(&result, value_, size);
156    return result;
157  }
158
159 protected:
160  uint8_t value_[kSizeInBytes];
161};
162typedef SimRegisterBase<kXRegSizeInBytes> SimRegister;      // r0-r31
163typedef SimRegisterBase<kDRegSizeInBytes> SimFPRegister;    // v0-v31
164
165
166class Simulator : public DecoderVisitor {
167 public:
168  explicit Simulator(Decoder* decoder, FILE* stream = stdout);
169  ~Simulator();
170
171  void ResetState();
172
173  // Run the simulator.
174  virtual void Run();
175  void RunFrom(Instruction* first);
176
177  // Simulation helpers.
178  inline Instruction* pc() { return pc_; }
179  inline void set_pc(Instruction* new_pc) {
180    pc_ = new_pc;
181    pc_modified_ = true;
182  }
183
184  inline void increment_pc() {
185    if (!pc_modified_) {
186      pc_ = pc_->NextInstruction();
187    }
188
189    pc_modified_ = false;
190  }
191
192  inline void ExecuteInstruction() {
193    // The program counter should always be aligned.
194    VIXL_ASSERT(IsWordAligned(pc_));
195    decoder_->Decode(pc_);
196    increment_pc();
197  }
198
199  // Declare all Visitor functions.
200  #define DECLARE(A)  void Visit##A(Instruction* instr);
201  VISITOR_LIST(DECLARE)
202  #undef DECLARE
203
204  // Register accessors.
205
206  // Return 'size' bits of the value of an integer register, as the specified
207  // type. The value is zero-extended to fill the result.
208  //
209  // The only supported values of 'size' are kXRegSize and kWRegSize.
210  template<typename T>
211  inline T reg(unsigned size, unsigned code,
212               Reg31Mode r31mode = Reg31IsZeroRegister) const {
213    unsigned size_in_bytes = size / 8;
214    VIXL_ASSERT(size_in_bytes <= sizeof(T));
215    VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize));
216    VIXL_ASSERT(code < kNumberOfRegisters);
217
218    if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
219      T result;
220      memset(&result, 0, sizeof(result));
221      return result;
222    }
223    return registers_[code].Get<T>(size_in_bytes);
224  }
225
226  // Like reg(), but infer the access size from the template type.
227  template<typename T>
228  inline T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
229    return reg<T>(sizeof(T) * 8, code, r31mode);
230  }
231
232  // Common specialized accessors for the reg() template.
233  inline int32_t wreg(unsigned code,
234                      Reg31Mode r31mode = Reg31IsZeroRegister) const {
235    return reg<int32_t>(code, r31mode);
236  }
237
238  inline int64_t xreg(unsigned code,
239                      Reg31Mode r31mode = Reg31IsZeroRegister) const {
240    return reg<int64_t>(code, r31mode);
241  }
242
243  inline int64_t reg(unsigned size, unsigned code,
244                     Reg31Mode r31mode = Reg31IsZeroRegister) const {
245    return reg<int64_t>(size, code, r31mode);
246  }
247
248  // Write 'size' bits of 'value' into an integer register. The value is
249  // zero-extended. This behaviour matches AArch64 register writes.
250  //
251  // The only supported values of 'size' are kXRegSize and kWRegSize.
252  template<typename T>
253  inline void set_reg(unsigned size, unsigned code, T value,
254                      Reg31Mode r31mode = Reg31IsZeroRegister) {
255    unsigned size_in_bytes = size / 8;
256    VIXL_ASSERT(size_in_bytes <= sizeof(T));
257    VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize));
258    VIXL_ASSERT(code < kNumberOfRegisters);
259
260    if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
261      return;
262    }
263    return registers_[code].Set(value, size_in_bytes);
264  }
265
266  // Like set_reg(), but infer the access size from the template type.
267  template<typename T>
268  inline void set_reg(unsigned code, T value,
269                      Reg31Mode r31mode = Reg31IsZeroRegister) {
270    set_reg(sizeof(value) * 8, code, value, r31mode);
271  }
272
273  // Common specialized accessors for the set_reg() template.
274  inline void set_wreg(unsigned code, int32_t value,
275                       Reg31Mode r31mode = Reg31IsZeroRegister) {
276    set_reg(kWRegSize, code, value, r31mode);
277  }
278
279  inline void set_xreg(unsigned code, int64_t value,
280                       Reg31Mode r31mode = Reg31IsZeroRegister) {
281    set_reg(kXRegSize, code, value, r31mode);
282  }
283
284  // Commonly-used special cases.
285  template<typename T>
286  inline void set_lr(T value) {
287    set_reg(kLinkRegCode, value);
288  }
289
290  template<typename T>
291  inline void set_sp(T value) {
292    set_reg(31, value, Reg31IsStackPointer);
293  }
294
295  // Return 'size' bits of the value of a floating-point register, as the
296  // specified type. The value is zero-extended to fill the result.
297  //
298  // The only supported values of 'size' are kDRegSize and kSRegSize.
299  template<typename T>
300  inline T fpreg(unsigned size, unsigned code) const {
301    unsigned size_in_bytes = size / 8;
302    VIXL_ASSERT(size_in_bytes <= sizeof(T));
303    VIXL_ASSERT((size == kDRegSize) || (size == kSRegSize));
304    VIXL_ASSERT(code < kNumberOfFPRegisters);
305    return fpregisters_[code].Get<T>(size_in_bytes);
306  }
307
308  // Like fpreg(), but infer the access size from the template type.
309  template<typename T>
310  inline T fpreg(unsigned code) const {
311    return fpreg<T>(sizeof(T) * 8, code);
312  }
313
314  // Common specialized accessors for the fpreg() template.
315  inline float sreg(unsigned code) const {
316    return fpreg<float>(code);
317  }
318
319  inline uint32_t sreg_bits(unsigned code) const {
320    return fpreg<uint32_t>(code);
321  }
322
323  inline double dreg(unsigned code) const {
324    return fpreg<double>(code);
325  }
326
327  inline uint64_t dreg_bits(unsigned code) const {
328    return fpreg<uint64_t>(code);
329  }
330
331  inline double fpreg(unsigned size, unsigned code) const {
332    switch (size) {
333      case kSRegSize: return sreg(code);
334      case kDRegSize: return dreg(code);
335      default:
336        VIXL_UNREACHABLE();
337        return 0.0;
338    }
339  }
340
341  // Write 'value' into a floating-point register. The value is zero-extended.
342  // This behaviour matches AArch64 register writes.
343  template<typename T>
344  inline void set_fpreg(unsigned code, T value) {
345    VIXL_ASSERT((sizeof(value) == kDRegSizeInBytes) ||
346           (sizeof(value) == kSRegSizeInBytes));
347    VIXL_ASSERT(code < kNumberOfFPRegisters);
348    fpregisters_[code].Set(value, sizeof(value));
349  }
350
351  // Common specialized accessors for the set_fpreg() template.
352  inline void set_sreg(unsigned code, float value) {
353    set_fpreg(code, value);
354  }
355
356  inline void set_sreg_bits(unsigned code, uint32_t value) {
357    set_fpreg(code, value);
358  }
359
360  inline void set_dreg(unsigned code, double value) {
361    set_fpreg(code, value);
362  }
363
364  inline void set_dreg_bits(unsigned code, uint64_t value) {
365    set_fpreg(code, value);
366  }
367
368  bool N() { return nzcv_.N() != 0; }
369  bool Z() { return nzcv_.Z() != 0; }
370  bool C() { return nzcv_.C() != 0; }
371  bool V() { return nzcv_.V() != 0; }
372  SimSystemRegister& nzcv() { return nzcv_; }
373
374  // TODO(jbramley): Find a way to make the fpcr_ members return the proper
375  // types, so these accessors are not necessary.
376  FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
377  bool DN() { return fpcr_.DN() != 0; }
378  SimSystemRegister& fpcr() { return fpcr_; }
379
380  // Debug helpers
381  void PrintSystemRegisters(bool print_all = false);
382  void PrintRegisters(bool print_all_regs = false);
383  void PrintFPRegisters(bool print_all_regs = false);
384  void PrintProcessorState();
385
386  static const char* WRegNameForCode(unsigned code,
387                                     Reg31Mode mode = Reg31IsZeroRegister);
388  static const char* XRegNameForCode(unsigned code,
389                                     Reg31Mode mode = Reg31IsZeroRegister);
390  static const char* SRegNameForCode(unsigned code);
391  static const char* DRegNameForCode(unsigned code);
392  static const char* VRegNameForCode(unsigned code);
393
394  inline bool coloured_trace() { return coloured_trace_; }
395  void set_coloured_trace(bool value);
396
397  inline bool disasm_trace() { return disasm_trace_; }
398  inline void set_disasm_trace(bool value) {
399    if (value != disasm_trace_) {
400      if (value) {
401        decoder_->InsertVisitorBefore(print_disasm_, this);
402      } else {
403        decoder_->RemoveVisitor(print_disasm_);
404      }
405      disasm_trace_ = value;
406    }
407  }
408  inline void set_instruction_stats(bool value) {
409    if (value != instruction_stats_) {
410      if (value) {
411        decoder_->AppendVisitor(instrumentation_);
412      } else {
413        decoder_->RemoveVisitor(instrumentation_);
414      }
415      instruction_stats_ = value;
416    }
417  }
418
419 protected:
420  const char* clr_normal;
421  const char* clr_flag_name;
422  const char* clr_flag_value;
423  const char* clr_reg_name;
424  const char* clr_reg_value;
425  const char* clr_fpreg_name;
426  const char* clr_fpreg_value;
427  const char* clr_memory_value;
428  const char* clr_memory_address;
429  const char* clr_debug_number;
430  const char* clr_debug_message;
431  const char* clr_printf;
432
433  // Simulation helpers ------------------------------------
434  bool ConditionPassed(Condition cond) {
435    switch (cond) {
436      case eq:
437        return Z();
438      case ne:
439        return !Z();
440      case hs:
441        return C();
442      case lo:
443        return !C();
444      case mi:
445        return N();
446      case pl:
447        return !N();
448      case vs:
449        return V();
450      case vc:
451        return !V();
452      case hi:
453        return C() && !Z();
454      case ls:
455        return !(C() && !Z());
456      case ge:
457        return N() == V();
458      case lt:
459        return N() != V();
460      case gt:
461        return !Z() && (N() == V());
462      case le:
463        return !(!Z() && (N() == V()));
464      case nv:  // Fall through.
465      case al:
466        return true;
467      default:
468        VIXL_UNREACHABLE();
469        return false;
470    }
471  }
472
473  bool ConditionFailed(Condition cond) {
474    return !ConditionPassed(cond);
475  }
476
477  void AddSubHelper(Instruction* instr, int64_t op2);
478  int64_t AddWithCarry(unsigned reg_size,
479                       bool set_flags,
480                       int64_t src1,
481                       int64_t src2,
482                       int64_t carry_in = 0);
483  void LogicalHelper(Instruction* instr, int64_t op2);
484  void ConditionalCompareHelper(Instruction* instr, int64_t op2);
485  void LoadStoreHelper(Instruction* instr,
486                       int64_t offset,
487                       AddrMode addrmode);
488  void LoadStorePairHelper(Instruction* instr, AddrMode addrmode);
489  uint8_t* AddressModeHelper(unsigned addr_reg,
490                             int64_t offset,
491                             AddrMode addrmode);
492
493  uint64_t MemoryRead(const uint8_t* address, unsigned num_bytes);
494  uint8_t MemoryRead8(uint8_t* address);
495  uint16_t MemoryRead16(uint8_t* address);
496  uint32_t MemoryRead32(uint8_t* address);
497  float MemoryReadFP32(uint8_t* address);
498  uint64_t MemoryRead64(uint8_t* address);
499  double MemoryReadFP64(uint8_t* address);
500
501  void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes);
502  void MemoryWrite32(uint8_t* address, uint32_t value);
503  void MemoryWriteFP32(uint8_t* address, float value);
504  void MemoryWrite64(uint8_t* address, uint64_t value);
505  void MemoryWriteFP64(uint8_t* address, double value);
506
507  int64_t ShiftOperand(unsigned reg_size,
508                       int64_t value,
509                       Shift shift_type,
510                       unsigned amount);
511  int64_t Rotate(unsigned reg_width,
512                 int64_t value,
513                 Shift shift_type,
514                 unsigned amount);
515  int64_t ExtendValue(unsigned reg_width,
516                      int64_t value,
517                      Extend extend_type,
518                      unsigned left_shift = 0);
519
520  uint64_t ReverseBits(uint64_t value, unsigned num_bits);
521  uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode);
522
523  template <typename T>
524  T FPDefaultNaN() const;
525
526  void FPCompare(double val0, double val1);
527  double FPRoundInt(double value, FPRounding round_mode);
528  double FPToDouble(float value);
529  float FPToFloat(double value, FPRounding round_mode);
530  double FixedToDouble(int64_t src, int fbits, FPRounding round_mode);
531  double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode);
532  float FixedToFloat(int64_t src, int fbits, FPRounding round_mode);
533  float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode);
534  int32_t FPToInt32(double value, FPRounding rmode);
535  int64_t FPToInt64(double value, FPRounding rmode);
536  uint32_t FPToUInt32(double value, FPRounding rmode);
537  uint64_t FPToUInt64(double value, FPRounding rmode);
538
539  template <typename T>
540  T FPAdd(T op1, T op2);
541
542  template <typename T>
543  T FPDiv(T op1, T op2);
544
545  template <typename T>
546  T FPMax(T a, T b);
547
548  template <typename T>
549  T FPMaxNM(T a, T b);
550
551  template <typename T>
552  T FPMin(T a, T b);
553
554  template <typename T>
555  T FPMinNM(T a, T b);
556
557  template <typename T>
558  T FPMul(T op1, T op2);
559
560  template <typename T>
561  T FPMulAdd(T a, T op1, T op2);
562
563  template <typename T>
564  T FPSqrt(T op);
565
566  template <typename T>
567  T FPSub(T op1, T op2);
568
569  // This doesn't do anything at the moment. We'll need it if we want support
570  // for cumulative exception bits or floating-point exceptions.
571  void FPProcessException() { }
572
573  // Standard NaN processing.
574  template <typename T>
575  T FPProcessNaN(T op);
576
577  bool FPProcessNaNs(Instruction* instr);
578
579  template <typename T>
580  T FPProcessNaNs(T op1, T op2);
581
582  template <typename T>
583  T FPProcessNaNs3(T op1, T op2, T op3);
584
585  // Pseudo Printf instruction
586  void DoPrintf(Instruction* instr);
587
588  // Processor state ---------------------------------------
589
590  // Output stream.
591  FILE* stream_;
592  PrintDisassembler* print_disasm_;
593
594  // Instruction statistics instrumentation.
595  Instrument* instrumentation_;
596
597  // General purpose registers. Register 31 is the stack pointer.
598  SimRegister registers_[kNumberOfRegisters];
599
600  // Floating point registers
601  SimFPRegister fpregisters_[kNumberOfFPRegisters];
602
603  // Program Status Register.
604  // bits[31, 27]: Condition flags N, Z, C, and V.
605  //               (Negative, Zero, Carry, Overflow)
606  SimSystemRegister nzcv_;
607
608  // Floating-Point Control Register
609  SimSystemRegister fpcr_;
610
611  // Only a subset of FPCR features are supported by the simulator. This helper
612  // checks that the FPCR settings are supported.
613  //
614  // This is checked when floating-point instructions are executed, not when
615  // FPCR is set. This allows generated code to modify FPCR for external
616  // functions, or to save and restore it when entering and leaving generated
617  // code.
618  void AssertSupportedFPCR() {
619    VIXL_ASSERT(fpcr().FZ() == 0);             // No flush-to-zero support.
620    VIXL_ASSERT(fpcr().RMode() == FPTieEven);  // Ties-to-even rounding only.
621
622    // The simulator does not support half-precision operations so fpcr().AHP()
623    // is irrelevant, and is not checked here.
624  }
625
626  static inline int CalcNFlag(uint64_t result, unsigned reg_size) {
627    return (result >> (reg_size - 1)) & 1;
628  }
629
630  static inline int CalcZFlag(uint64_t result) {
631    return result == 0;
632  }
633
634  static const uint32_t kConditionFlagsMask = 0xf0000000;
635
636  // Stack
637  byte* stack_;
638  static const int stack_protection_size_ = 256;
639  // 2 KB stack.
640  static const int stack_size_ = 2 * 1024 + 2 * stack_protection_size_;
641  byte* stack_limit_;
642
643  Decoder* decoder_;
644  // Indicates if the pc has been modified by the instruction and should not be
645  // automatically incremented.
646  bool pc_modified_;
647  Instruction* pc_;
648
649  static const char* xreg_names[];
650  static const char* wreg_names[];
651  static const char* sreg_names[];
652  static const char* dreg_names[];
653  static const char* vreg_names[];
654
655  static const Instruction* kEndOfSimAddress;
656
657 private:
658  bool coloured_trace_;
659
660  // Indicates whether the disassembly trace is active.
661  bool disasm_trace_;
662
663  // Indicates whether the instruction instrumentation is active.
664  bool instruction_stats_;
665};
666}  // namespace vixl
667
668#endif  // VIXL_A64_SIMULATOR_A64_H_
669