~ubuntu-branches/debian/squeeze/stella/squeeze

« back to all changes in this revision

Viewing changes to src/emucore/M6502.cxx

  • Committer: Bazaar Package Importer
  • Author(s): Stephen Kitt
  • Date: 2010-07-12 23:49:36 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20100712234936-juawrr3etzhr2qpv
Tags: 3.1.2-1
* New maintainer (closes: #532039).
* New upstream version (closes: #461121):
  - includes launcher (closes: #396058).
* Fix the reference to the X Window System in the description (closes:
  #411815).
* Move to main, DFSG-free ROMs are available (see README.Debian).
* Enhance the package description.
* Drop the libslang2-dev dependency (closes: #560274).
* Remove the Encoding entry from stella.desktop.
* Avoid ignoring errors when cleaning.
* Add ${misc:Depends} to the package dependencies.
* Provide a doc-base file to install the documentation using doc-base.
* Switch to debhelper 7 with a simplified rules file.
* Use autotools-dev to provide updated configuration files.
* Update to Standards-Version 3.9.0:
  - Move to menu section Applications/Emulators.
  - Move the homepage declaration.
* Re-write the manpage.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//============================================================================
 
2
//
 
3
// MM     MM  6666  555555  0000   2222
 
4
// MMMM MMMM 66  66 55     00  00 22  22
 
5
// MM MMM MM 66     55     00  00     22
 
6
// MM  M  MM 66666  55555  00  00  22222  --  "A 6502 Microprocessor Emulator"
 
7
// MM     MM 66  66     55 00  00 22
 
8
// MM     MM 66  66 55  55 00  00 22
 
9
// MM     MM  6666   5555   0000  222222
 
10
//
 
11
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
 
12
// and the Stella Team
 
13
//
 
14
// See the file "License.txt" for information on usage and redistribution of
 
15
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
16
//
 
17
// $Id: M6502.cxx 2001 2010-04-10 21:37:23Z stephena $
 
18
//============================================================================
 
19
 
 
20
//#define DEBUG_OUTPUT
 
21
#define debugStream cout
 
22
 
 
23
#ifdef DEBUGGER_SUPPORT
 
24
  #include "Debugger.hxx"
 
25
  #include "Expression.hxx"
 
26
#endif
 
27
 
 
28
#include "M6502.hxx"
 
29
 
 
30
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
31
M6502::M6502(uInt32 systemCyclesPerProcessorCycle)
 
32
  : myExecutionStatus(0),
 
33
    mySystem(0),
 
34
    mySystemCyclesPerProcessorCycle(systemCyclesPerProcessorCycle),
 
35
    myLastAccessWasRead(true),
 
36
    myTotalInstructionCount(0),
 
37
    myNumberOfDistinctAccesses(0),
 
38
    myLastAddress(0),
 
39
    myLastPeekAddress(0),
 
40
    myLastPokeAddress(0)
 
41
{
 
42
#ifdef DEBUGGER_SUPPORT
 
43
  myDebugger    = NULL;
 
44
  myBreakPoints = NULL;
 
45
  myReadTraps   = NULL;
 
46
  myWriteTraps  = NULL;
 
47
 
 
48
  myJustHitTrapFlag = false;
 
49
#endif
 
50
 
 
51
  // Compute the System Cycle table
 
52
  for(uInt32 t = 0; t < 256; ++t)
 
53
  {
 
54
    myInstructionSystemCycleTable[t] = ourInstructionCycleTable[t] *
 
55
        mySystemCyclesPerProcessorCycle;
 
56
  }
 
57
 
 
58
#ifdef DEBUG_OUTPUT
 
59
debugStream << "( Fm  Ln Cyc Clk) ( P0  P1  M0  M1  BL)  "
 
60
            << "flags   A  X  Y SP  Code           Disasm" << endl
 
61
            << endl;
 
62
#endif
 
63
}
 
64
 
 
65
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
66
M6502::~M6502()
 
67
{
 
68
#ifdef DEBUGGER_SUPPORT
 
69
  myBreakConds.clear();
 
70
  myBreakCondNames.clear();
 
71
#endif
 
72
}
 
73
 
 
74
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
75
void M6502::install(System& system)
 
76
{
 
77
  // Remember which system I'm installed in
 
78
  mySystem = &system;
 
79
}
 
80
 
 
81
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
82
void M6502::reset()
 
83
{
 
84
  // Clear the execution status flags
 
85
  myExecutionStatus = 0;
 
86
 
 
87
  // Set registers to default values
 
88
  A = X = Y = 0;
 
89
  SP = 0xff;
 
90
  PS(0x20);
 
91
 
 
92
  // Reset access flag
 
93
  myLastAccessWasRead = true;
 
94
 
 
95
  // Load PC from the reset vector
 
96
  PC = (uInt16)mySystem->peek(0xfffc) | ((uInt16)mySystem->peek(0xfffd) << 8);
 
97
 
 
98
  myTotalInstructionCount = 0;
 
99
}
 
100
 
 
101
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
102
void M6502::irq()
 
103
{
 
104
  myExecutionStatus |= MaskableInterruptBit;
 
105
}
 
106
 
 
107
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
108
void M6502::nmi()
 
109
{
 
110
  myExecutionStatus |= NonmaskableInterruptBit;
 
111
}
 
112
 
 
113
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
114
void M6502::stop()
 
115
{
 
116
  myExecutionStatus |= StopExecutionBit;
 
117
}
 
118
 
 
119
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
120
uInt8 M6502::PS() const
 
121
{
 
122
  uInt8 ps = 0x20;
 
123
 
 
124
  if(N) 
 
125
    ps |= 0x80;
 
126
  if(V) 
 
127
    ps |= 0x40;
 
128
  if(B) 
 
129
    ps |= 0x10;
 
130
  if(D) 
 
131
    ps |= 0x08;
 
132
  if(I) 
 
133
    ps |= 0x04;
 
134
  if(!notZ) 
 
135
    ps |= 0x02;
 
136
  if(C) 
 
137
    ps |= 0x01;
 
138
 
 
139
  return ps;
 
140
}
 
141
 
 
142
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
143
void M6502::PS(uInt8 ps)
 
144
{
 
145
  N = ps & 0x80;
 
146
  V = ps & 0x40;
 
147
  B = true;        // B = ps & 0x10;  The 6507's B flag always true
 
148
  D = ps & 0x08;
 
149
  I = ps & 0x04;
 
150
  notZ = !(ps & 0x02);
 
151
  C = ps & 0x01;
 
152
}
 
153
 
 
154
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
155
inline uInt8 M6502::peek(uInt16 address)
 
156
{
 
157
  if(address != myLastAddress)
 
158
  {
 
159
    myNumberOfDistinctAccesses++;
 
160
    myLastAddress = address;
 
161
  }
 
162
  mySystem->incrementCycles(mySystemCyclesPerProcessorCycle);
 
163
 
 
164
#ifdef DEBUGGER_SUPPORT
 
165
  if(myReadTraps != NULL && myReadTraps->isSet(address))
 
166
  {
 
167
    myJustHitTrapFlag = true;
 
168
    myHitTrapInfo.message = "RTrap: ";
 
169
    myHitTrapInfo.address = address;
 
170
  }
 
171
#endif
 
172
 
 
173
  uInt8 result = mySystem->peek(address);
 
174
  myLastAccessWasRead = true;
 
175
  myLastPeekAddress = address;
 
176
  return result;
 
177
}
 
178
 
 
179
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
180
inline void M6502::poke(uInt16 address, uInt8 value)
 
181
{
 
182
  if(address != myLastAddress)
 
183
  {
 
184
    myNumberOfDistinctAccesses++;
 
185
    myLastAddress = address;
 
186
  }
 
187
  mySystem->incrementCycles(mySystemCyclesPerProcessorCycle);
 
188
 
 
189
#ifdef DEBUGGER_SUPPORT
 
190
  if(myWriteTraps != NULL && myWriteTraps->isSet(address))
 
191
  {
 
192
    myJustHitTrapFlag = true;
 
193
    myHitTrapInfo.message = "WTrap: ";
 
194
    myHitTrapInfo.address = address;
 
195
  }
 
196
#endif
 
197
 
 
198
  mySystem->poke(address, value);
 
199
  myLastAccessWasRead = false;
 
200
  myLastPokeAddress = address;
 
201
}
 
202
 
 
203
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
204
bool M6502::execute(uInt32 number)
 
205
{
 
206
  // Clear all of the execution status bits except for the fatal error bit
 
207
  myExecutionStatus &= FatalErrorBit;
 
208
 
 
209
  // Loop until execution is stopped or a fatal error occurs
 
210
  for(;;)
 
211
  {
 
212
    for(; !myExecutionStatus && (number != 0); --number)
 
213
    {
 
214
#ifdef DEBUGGER_SUPPORT
 
215
      if(myJustHitTrapFlag)
 
216
      {
 
217
        if(myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address))
 
218
        {
 
219
          myJustHitTrapFlag = false;
 
220
          return true;
 
221
        }
 
222
      }
 
223
 
 
224
      if(myBreakPoints != NULL)
 
225
      {
 
226
        if(myBreakPoints->isSet(PC))
 
227
        {
 
228
          if(myDebugger->start("BP: ", PC))
 
229
            return true;
 
230
        }
 
231
      }
 
232
 
 
233
      int cond = evalCondBreaks();
 
234
      if(cond > -1)
 
235
      {
 
236
        string buf = "CBP: " + myBreakCondNames[cond];
 
237
        if(myDebugger->start(buf))
 
238
          return true;
 
239
      }
 
240
#endif
 
241
      uInt16 operandAddress = 0;
 
242
      uInt8 operand = 0;
 
243
 
 
244
      // Reset the peek/poke address pointers
 
245
      myLastPeekAddress = myLastPokeAddress = 0;
 
246
 
 
247
      // Fetch instruction at the program counter
 
248
      IR = peek(PC++);
 
249
 
 
250
#ifdef DEBUG_OUTPUT
 
251
      debugStream << ::hex << setw(2) << (int)A << " "
 
252
                  << ::hex << setw(2) << (int)X << " "
 
253
                  << ::hex << setw(2) << (int)Y << " "
 
254
                  << ::hex << setw(2) << (int)SP << "  "
 
255
                  << setw(4) << (PC-1) << ": "
 
256
                  << setw(2) << (int)IR << "       "
 
257
//      << "<" << ourAddressingModeTable[IR] << " ";
 
258
//      debugStream << hex << setw(4) << operandAddress << " ";
 
259
                  << setw(3) << ourInstructionMnemonicTable[IR]
 
260
 
 
261
//      debugStream << "PS=" << ::hex << setw(2) << (int)PS() << " ";
 
262
 
 
263
//      debugStream << "Cyc=" << dec << mySystem->cycles();
 
264
                  << endl;
 
265
#endif
 
266
 
 
267
      // Call code to execute the instruction
 
268
      switch(IR)
 
269
      {
 
270
        // 6502 instruction emulation is generated by an M4 macro file
 
271
        #include "M6502.ins"
 
272
 
 
273
        default:
 
274
          // Oops, illegal instruction executed so set fatal error flag
 
275
          myExecutionStatus |= FatalErrorBit;
 
276
      }
 
277
 
 
278
      myTotalInstructionCount++;
 
279
    }
 
280
 
 
281
    // See if we need to handle an interrupt
 
282
    if((myExecutionStatus & MaskableInterruptBit) || 
 
283
        (myExecutionStatus & NonmaskableInterruptBit))
 
284
    {
 
285
      // Yes, so handle the interrupt
 
286
      interruptHandler();
 
287
    }
 
288
 
 
289
    // See if execution has been stopped
 
290
    if(myExecutionStatus & StopExecutionBit)
 
291
    {
 
292
      // Yes, so answer that everything finished fine
 
293
      return true;
 
294
    }
 
295
 
 
296
    // See if a fatal error has occured
 
297
    if(myExecutionStatus & FatalErrorBit)
 
298
    {
 
299
      // Yes, so answer that something when wrong
 
300
      return false;
 
301
    }
 
302
 
 
303
    // See if we've executed the specified number of instructions
 
304
    if(number == 0)
 
305
    {
 
306
      // Yes, so answer that everything finished fine
 
307
      return true;
 
308
    }
 
309
  }
 
310
}
 
311
 
 
312
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
313
void M6502::interruptHandler()
 
314
{
 
315
  // Handle the interrupt
 
316
  if((myExecutionStatus & MaskableInterruptBit) && !I)
 
317
  {
 
318
    mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle);
 
319
    mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
 
320
    mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
 
321
    mySystem->poke(0x0100 + SP--, PS() & (~0x10));
 
322
    D = false;
 
323
    I = true;
 
324
    PC = (uInt16)mySystem->peek(0xFFFE) | ((uInt16)mySystem->peek(0xFFFF) << 8);
 
325
  }
 
326
  else if(myExecutionStatus & NonmaskableInterruptBit)
 
327
  {
 
328
    mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle);
 
329
    mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
 
330
    mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
 
331
    mySystem->poke(0x0100 + SP--, PS() & (~0x10));
 
332
    D = false;
 
333
    PC = (uInt16)mySystem->peek(0xFFFA) | ((uInt16)mySystem->peek(0xFFFB) << 8);
 
334
  }
 
335
 
 
336
  // Clear the interrupt bits in myExecutionStatus
 
337
  myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit);
 
338
}
 
339
 
 
340
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
341
bool M6502::save(Serializer& out) const
 
342
{
 
343
  const string& CPU = name();
 
344
 
 
345
  try
 
346
  {
 
347
    out.putString(CPU);
 
348
 
 
349
    out.putByte((char)A);   // Accumulator
 
350
    out.putByte((char)X);   // X index register
 
351
    out.putByte((char)Y);   // Y index register
 
352
    out.putByte((char)SP);  // Stack Pointer
 
353
    out.putByte((char)IR);  // Instruction register
 
354
    out.putInt(PC);         // Program Counter
 
355
 
 
356
    out.putBool(N);     // N flag for processor status register
 
357
    out.putBool(V);     // V flag for processor status register
 
358
    out.putBool(B);     // B flag for processor status register
 
359
    out.putBool(D);     // D flag for processor status register
 
360
    out.putBool(I);     // I flag for processor status register
 
361
    out.putBool(notZ);  // Z flag complement for processor status register
 
362
    out.putBool(C);     // C flag for processor status register
 
363
 
 
364
    out.putByte((char)myExecutionStatus);
 
365
 
 
366
    // Indicates the number of distinct memory accesses
 
367
    out.putInt(myNumberOfDistinctAccesses);
 
368
    // Indicates the last address which was accessed
 
369
    out.putInt(myLastAddress);
 
370
 
 
371
  }
 
372
  catch(const char* msg)
 
373
  {
 
374
    cerr << "ERROR: M6502::save" << endl << "  " << msg << endl;
 
375
    return false;
 
376
  }
 
377
 
 
378
  return true;
 
379
}
 
380
 
 
381
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
382
bool M6502::load(Serializer& in)
 
383
{
 
384
  const string& CPU = name();
 
385
 
 
386
  try
 
387
  {
 
388
    if(in.getString() != CPU)
 
389
      return false;
 
390
 
 
391
    A = (uInt8) in.getByte();    // Accumulator
 
392
    X = (uInt8) in.getByte();    // X index register
 
393
    Y = (uInt8) in.getByte();    // Y index register
 
394
    SP = (uInt8) in.getByte();   // Stack Pointer
 
395
    IR = (uInt8) in.getByte();   // Instruction register
 
396
    PC = (uInt16) in.getInt();  // Program Counter
 
397
 
 
398
    N = in.getBool();     // N flag for processor status register
 
399
    V = in.getBool();     // V flag for processor status register
 
400
    B = in.getBool();     // B flag for processor status register
 
401
    D = in.getBool();     // D flag for processor status register
 
402
    I = in.getBool();     // I flag for processor status register
 
403
    notZ = in.getBool();  // Z flag complement for processor status register
 
404
    C = in.getBool();     // C flag for processor status register
 
405
 
 
406
    myExecutionStatus = (uInt8) in.getByte();
 
407
 
 
408
    // Indicates the number of distinct memory accesses
 
409
    myNumberOfDistinctAccesses = (uInt32) in.getInt();
 
410
    // Indicates the last address which was accessed
 
411
    myLastAddress = (uInt16) in.getInt();
 
412
  }
 
413
  catch(const char* msg)
 
414
  {
 
415
    cerr << "ERROR: M6502::laod" << endl << "  " << msg << endl;
 
416
    return false;
 
417
  }
 
418
 
 
419
  return true;
 
420
}
 
421
 
 
422
#if 0
 
423
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
424
ostream& operator<<(ostream& out, const M6502::AddressingMode& mode)
 
425
{
 
426
  switch(mode)
 
427
  {
 
428
    case M6502::Absolute:
 
429
      out << "$nnnn  ";
 
430
      break;
 
431
    case M6502::AbsoluteX:
 
432
      out << "$nnnn,X";
 
433
      break;
 
434
    case M6502::AbsoluteY:
 
435
      out << "$nnnn,Y";
 
436
      break;
 
437
    case M6502::Implied:
 
438
      out << "implied";
 
439
      break;
 
440
    case M6502::Immediate:
 
441
      out << "#$nn   ";
 
442
      break;
 
443
    case M6502::Indirect:
 
444
      out << "($nnnn)";
 
445
      break;
 
446
    case M6502::IndirectX:
 
447
      out << "($nn,X)";
 
448
      break;
 
449
    case M6502::IndirectY:
 
450
      out << "($nn),Y";
 
451
      break;
 
452
    case M6502::Invalid:
 
453
      out << "invalid";
 
454
      break;
 
455
    case M6502::Relative:
 
456
      out << "$nn    ";
 
457
      break;
 
458
    case M6502::Zero:
 
459
      out << "$nn    ";
 
460
      break;
 
461
    case M6502::ZeroX:
 
462
      out << "$nn,X  ";
 
463
      break;
 
464
    case M6502::ZeroY:
 
465
      out << "$nn,Y  ";
 
466
      break;
 
467
  }
 
468
  return out;
 
469
}
 
470
#endif
 
471
 
 
472
#ifdef DEBUGGER_SUPPORT
 
473
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
474
void M6502::attach(Debugger& debugger)
 
475
{
 
476
  // Remember the debugger for this microprocessor
 
477
  myDebugger = &debugger;
 
478
}
 
479
 
 
480
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
481
unsigned int M6502::addCondBreak(Expression *e, const string& name)
 
482
{
 
483
  myBreakConds.push_back(e);
 
484
  myBreakCondNames.push_back(name);
 
485
  return myBreakConds.size() - 1;
 
486
}
 
487
 
 
488
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
489
void M6502::delCondBreak(unsigned int brk)
 
490
{
 
491
  if(brk < myBreakConds.size())
 
492
  {
 
493
    delete myBreakConds[brk];
 
494
    myBreakConds.remove_at(brk);
 
495
    myBreakCondNames.remove_at(brk);
 
496
  }
 
497
}
 
498
 
 
499
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
500
void M6502::clearCondBreaks()
 
501
{
 
502
  for(uInt32 i = 0; i < myBreakConds.size(); i++)
 
503
    delete myBreakConds[i];
 
504
 
 
505
  myBreakConds.clear();
 
506
  myBreakCondNames.clear();
 
507
}
 
508
 
 
509
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
510
const StringList& M6502::getCondBreakNames() const
 
511
{
 
512
  return myBreakCondNames;
 
513
}
 
514
 
 
515
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
516
int M6502::evalCondBreaks()
 
517
{
 
518
  for(uInt32 i = 0; i < myBreakConds.size(); i++)
 
519
    if(myBreakConds[i]->evaluate())
 
520
      return i;
 
521
 
 
522
  return -1; // no break hit
 
523
}
 
524
 
 
525
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
526
void M6502::setBreakPoints(PackedBitArray *bp)
 
527
{
 
528
  myBreakPoints = bp;
 
529
}
 
530
 
 
531
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
532
void M6502::setTraps(PackedBitArray *read, PackedBitArray *write)
 
533
{
 
534
  myReadTraps = read;
 
535
  myWriteTraps = write;
 
536
}
 
537
 
 
538
#endif
 
539
 
 
540
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
541
uInt32 M6502::ourInstructionCycleTable[256] = {
 
542
//  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
 
543
    7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,  // 0
 
544
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,  // 1
 
545
    6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6,  // 2
 
546
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,  // 3
 
547
    6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,  // 4
 
548
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,  // 5
 
549
    6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6,  // 6
 
550
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,  // 7
 
551
    2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,  // 8
 
552
    2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5,  // 9
 
553
    2, 6, 2, 6, 3, 3, 3, 4, 2, 2, 2, 2, 4, 4, 4, 4,  // a
 
554
    2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4,  // b
 
555
    2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,  // c
 
556
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,  // d
 
557
    2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,  // e
 
558
    2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7   // f
 
559
};