src/gb/gb.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "gb.h"
7
8#include "gb/io.h"
9#include "gb/mbc.h"
10
11#include "core/core.h"
12#include "core/cheats.h"
13#include "util/crc32.h"
14#include "util/memory.h"
15#include "util/math.h"
16#include "util/patch.h"
17#include "util/vfs.h"
18
19#define CLEANUP_THRESHOLD 15
20
21const uint32_t CGB_LR35902_FREQUENCY = 0x800000;
22const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E;
23
24const uint32_t GB_COMPONENT_MAGIC = 0x400000;
25
26mLOG_DEFINE_CATEGORY(GB, "GB");
27
28static void GBInit(void* cpu, struct mCPUComponent* component);
29static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
30static void GBProcessEvents(struct LR35902Core* cpu);
31static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
32static void GBIllegal(struct LR35902Core* cpu);
33static void GBStop(struct LR35902Core* cpu);
34
35#ifdef _3DS
36extern uint32_t* romBuffer;
37extern size_t romBufferSize;
38#endif
39
40void GBCreate(struct GB* gb) {
41 gb->d.id = GB_COMPONENT_MAGIC;
42 gb->d.init = GBInit;
43 gb->d.deinit = 0;
44}
45
46static void GBInit(void* cpu, struct mCPUComponent* component) {
47 struct GB* gb = (struct GB*) component;
48 gb->cpu = cpu;
49 gb->sync = NULL;
50
51 GBInterruptHandlerInit(&gb->cpu->irqh);
52 GBMemoryInit(gb);
53
54 gb->video.p = gb;
55 GBVideoInit(&gb->video);
56
57 gb->audio.p = gb;
58 GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant
59
60 gb->sio.p = gb;
61 GBSIOInit(&gb->sio);
62
63 gb->timer.p = gb;
64
65 gb->model = GB_MODEL_AUTODETECT;
66
67 gb->biosVf = 0;
68 gb->romVf = 0;
69 gb->sramVf = 0;
70
71 gb->pristineRom = 0;
72 gb->pristineRomSize = 0;
73 gb->yankedRomSize = 0;
74
75 gb->stream = NULL;
76}
77
78bool GBLoadROM(struct GB* gb, struct VFile* vf) {
79 GBUnloadROM(gb);
80 gb->romVf = vf;
81 gb->pristineRomSize = vf->size(vf);
82 vf->seek(vf, 0, SEEK_SET);
83#ifdef _3DS
84 gb->pristineRom = 0;
85 if (gb->pristineRomSize <= romBufferSize) {
86 gb->pristineRom = romBuffer;
87 vf->read(vf, romBuffer, gb->pristineRomSize);
88 }
89#else
90 gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
91#endif
92 if (!gb->pristineRom) {
93 return false;
94 }
95 gb->yankedRomSize = 0;
96 gb->memory.rom = gb->pristineRom;
97 gb->memory.romBase = gb->memory.rom;
98 gb->memory.romSize = gb->pristineRomSize;
99 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
100
101 // TODO: error check
102 return true;
103}
104
105bool GBLoadSave(struct GB* gb, struct VFile* vf) {
106 gb->sramVf = vf;
107 gb->sramRealVf = vf;
108 return vf;
109}
110
111static void GBSramDeinit(struct GB* gb) {
112 if (gb->sramVf) {
113 gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
114 if (gb->memory.mbcType == GB_MBC3_RTC) {
115 GBMBCRTCWrite(gb);
116 }
117 gb->sramVf->close(gb->sramVf);
118 gb->sramVf = 0;
119 } else if (gb->memory.sram) {
120 mappedMemoryFree(gb->memory.sram, gb->sramSize);
121 }
122 gb->memory.sram = 0;
123}
124
125void GBResizeSram(struct GB* gb, size_t size) {
126 if (gb->memory.sram && size <= gb->sramSize) {
127 return;
128 }
129 mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
130 struct VFile* vf = gb->sramVf;
131 if (vf) {
132 if (vf == gb->sramRealVf) {
133 ssize_t vfSize = vf->size(vf);
134 if (vfSize >= 0 && (size_t) vfSize < size) {
135 uint8_t extdataBuffer[0x100];
136 if (vfSize & 0xFF) {
137 vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
138 vf->read(vf, extdataBuffer, vfSize & 0xFF);
139 }
140 if (gb->memory.sram) {
141 vf->unmap(vf, gb->memory.sram, gb->sramSize);
142 }
143 vf->truncate(vf, size + (vfSize & 0xFF));
144 if (vfSize & 0xFF) {
145 vf->seek(vf, size, SEEK_SET);
146 vf->write(vf, extdataBuffer, vfSize & 0xFF);
147 }
148 gb->memory.sram = vf->map(vf, size, MAP_WRITE);
149 memset(&gb->memory.sram[gb->sramSize], 0xFF, size - gb->sramSize);
150 } else if (size > gb->sramSize || !gb->memory.sram) {
151 if (gb->memory.sram) {
152 vf->unmap(vf, gb->memory.sram, gb->sramSize);
153 }
154 gb->memory.sram = vf->map(vf, size, MAP_WRITE);
155 }
156 } else {
157 if (gb->memory.sram) {
158 vf->unmap(vf, gb->memory.sram, gb->sramSize);
159 }
160 gb->memory.sram = vf->map(vf, size, MAP_READ);
161 }
162 if (gb->memory.sram == (void*) -1) {
163 gb->memory.sram = NULL;
164 }
165 } else {
166 uint8_t* newSram = anonymousMemoryMap(size);
167 if (gb->memory.sram) {
168 if (size > gb->sramSize) {
169 memcpy(newSram, gb->memory.sram, gb->sramSize);
170 memset(&newSram[gb->sramSize], 0xFF, size - gb->sramSize);
171 } else {
172 memcpy(newSram, gb->memory.sram, size);
173 }
174 mappedMemoryFree(gb->memory.sram, gb->sramSize);
175 } else {
176 memset(newSram, 0xFF, size);
177 }
178 gb->memory.sram = newSram;
179 }
180 if (gb->sramSize < size) {
181 gb->sramSize = size;
182 }
183}
184
185void GBSramClean(struct GB* gb, uint32_t frameCount) {
186 // TODO: Share with GBASavedataClean
187 if (!gb->sramVf) {
188 return;
189 }
190 if (gb->sramDirty & GB_SRAM_DIRT_NEW) {
191 gb->sramDirtAge = frameCount;
192 gb->sramDirty &= ~GB_SRAM_DIRT_NEW;
193 if (!(gb->sramDirty & GB_SRAM_DIRT_SEEN)) {
194 gb->sramDirty |= GB_SRAM_DIRT_SEEN;
195 }
196 } else if ((gb->sramDirty & GB_SRAM_DIRT_SEEN) && frameCount - gb->sramDirtAge > CLEANUP_THRESHOLD) {
197 if (gb->memory.mbcType == GB_MBC3_RTC) {
198 GBMBCRTCWrite(gb);
199 }
200 gb->sramDirty = 0;
201 if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
202 mLOG(GB_MEM, INFO, "Savedata synced");
203 } else {
204 mLOG(GB_MEM, INFO, "Savedata failed to sync!");
205 }
206 }
207}
208
209void GBSavedataMask(struct GB* gb, struct VFile* vf) {
210 GBSramDeinit(gb);
211 gb->sramVf = vf;
212 gb->memory.sram = vf->map(vf, gb->sramSize, MAP_READ);
213}
214
215void GBSavedataUnmask(struct GB* gb) {
216 if (gb->sramVf == gb->sramRealVf) {
217 return;
218 }
219 GBSramDeinit(gb);
220 gb->sramVf = gb->sramRealVf;
221 gb->memory.sram = gb->sramVf->map(gb->sramVf, gb->sramSize, MAP_WRITE);
222}
223
224void GBUnloadROM(struct GB* gb) {
225 // TODO: Share with GBAUnloadROM
226 if (gb->memory.rom && gb->memory.romBase != gb->memory.rom) {
227 free(gb->memory.romBase);
228 }
229 if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
230 if (gb->yankedRomSize) {
231 gb->yankedRomSize = 0;
232 }
233 mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
234 }
235 gb->memory.rom = 0;
236
237 if (gb->romVf) {
238#ifndef _3DS
239 gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
240#endif
241 gb->romVf->close(gb->romVf);
242 gb->pristineRom = 0;
243 gb->romVf = 0;
244 }
245
246 GBSramDeinit(gb);
247}
248
249void GBLoadBIOS(struct GB* gb, struct VFile* vf) {
250 gb->biosVf = vf;
251}
252
253void GBApplyPatch(struct GB* gb, struct Patch* patch) {
254 size_t patchedSize = patch->outputSize(patch, gb->memory.romSize);
255 if (!patchedSize) {
256 return;
257 }
258 if (patchedSize > GB_SIZE_CART_MAX) {
259 patchedSize = GB_SIZE_CART_MAX;
260 }
261 gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
262 if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) {
263 mappedMemoryFree(gb->memory.rom, patchedSize);
264 gb->memory.rom = gb->pristineRom;
265 return;
266 }
267 gb->memory.romSize = patchedSize;
268 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
269}
270
271void GBDestroy(struct GB* gb) {
272 GBUnloadROM(gb);
273
274 if (gb->biosVf) {
275 gb->biosVf->close(gb->biosVf);
276 gb->biosVf = 0;
277 }
278
279 GBMemoryDeinit(gb);
280 GBVideoDeinit(&gb->video);
281 GBSIODeinit(&gb->sio);
282}
283
284void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
285 irqh->reset = GBReset;
286 irqh->processEvents = GBProcessEvents;
287 irqh->setInterrupts = GBSetInterrupts;
288 irqh->hitIllegal = GBIllegal;
289 irqh->stop = GBStop;
290 irqh->halt = GBHalt;
291}
292
293void GBReset(struct LR35902Core* cpu) {
294 struct GB* gb = (struct GB*) cpu->master;
295 GBDetectModel(gb);
296 if (gb->biosVf) {
297 gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
298 gb->memory.romBase = malloc(GB_SIZE_CART_BANK0);
299 ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0);
300 uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
301 switch (biosCrc) {
302 case 0x59C8598E:
303 break;
304 case 0x41884E46:
305 break;
306 default:
307 gb->biosVf->close(gb->biosVf);
308 gb->biosVf = NULL;
309 free(gb->memory.romBase);
310 gb->memory.romBase = gb->memory.rom;
311 break;
312 }
313
314 if (gb->biosVf) {
315 memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size);
316 if (size > 0x100) {
317 memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], sizeof(struct GBCartridge));
318 }
319
320 cpu->a = 0;
321 cpu->f.packed = 0;
322 cpu->c = 0;
323 cpu->e = 0;
324 cpu->h = 0;
325 cpu->l = 0;
326 cpu->sp = 0;
327 cpu->pc = 0;
328 }
329 }
330
331 cpu->b = 0;
332 cpu->d = 0;
333
334 if (!gb->biosVf) {
335 switch (gb->model) {
336 case GB_MODEL_DMG:
337 // TODO: SGB
338 case GB_MODEL_SGB:
339 case GB_MODEL_AUTODETECT: // Silence warnings
340 gb->model = GB_MODEL_DMG;
341 cpu->a = 1;
342 cpu->f.packed = 0xB0;
343 cpu->c = 0x13;
344 cpu->e = 0xD8;
345 cpu->h = 1;
346 cpu->l = 0x4D;
347 break;
348 case GB_MODEL_AGB:
349 cpu->b = 1;
350 // Fall through
351 case GB_MODEL_CGB:
352 cpu->a = 0x11;
353 cpu->f.packed = 0x80;
354 cpu->c = 0;
355 cpu->e = 0x08;
356 cpu->h = 0;
357 cpu->l = 0x7C;
358 break;
359 }
360
361 cpu->sp = 0xFFFE;
362 cpu->pc = 0x100;
363 }
364
365 gb->eiPending = INT_MAX;
366 gb->doubleSpeed = 0;
367
368 cpu->memory.setActiveRegion(cpu, cpu->pc);
369
370 if (gb->yankedRomSize) {
371 gb->memory.romSize = gb->yankedRomSize;
372 gb->yankedRomSize = 0;
373 }
374 GBMemoryReset(gb);
375 GBVideoReset(&gb->video);
376 GBTimerReset(&gb->timer);
377 GBIOReset(gb);
378 GBAudioReset(&gb->audio);
379 GBSIOReset(&gb->sio);
380
381 GBSavedataUnmask(gb);
382}
383
384void GBDetectModel(struct GB* gb) {
385 if (gb->model != GB_MODEL_AUTODETECT) {
386 return;
387 }
388 if (gb->biosVf) {
389 gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
390 void* bios = malloc(GB_SIZE_CART_BANK0);
391 ssize_t size = gb->biosVf->read(gb->biosVf, bios, GB_SIZE_CART_BANK0);
392 uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
393 switch (biosCrc) {
394 case 0x59C8598E:
395 gb->model = GB_MODEL_DMG;
396 break;
397 case 0x41884E46:
398 gb->model = GB_MODEL_CGB;
399 break;
400 default:
401 gb->biosVf->close(gb->biosVf);
402 gb->biosVf = NULL;
403 }
404 free(bios);
405 }
406 if (gb->model == GB_MODEL_AUTODETECT && gb->memory.rom) {
407 const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
408 if (cart->cgb & 0x80) {
409 gb->model = GB_MODEL_CGB;
410 } else {
411 gb->model = GB_MODEL_DMG;
412 }
413 }
414
415 switch (gb->model) {
416 case GB_MODEL_DMG:
417 case GB_MODEL_SGB:
418 case GB_MODEL_AUTODETECT: //Silence warnings
419 gb->audio.style = GB_AUDIO_DMG;
420 break;
421 case GB_MODEL_AGB:
422 case GB_MODEL_CGB:
423 gb->audio.style = GB_AUDIO_CGB;
424 break;
425 }
426}
427
428void GBUpdateIRQs(struct GB* gb) {
429 int irqs = gb->memory.ie & gb->memory.io[REG_IF];
430 if (!irqs) {
431 return;
432 }
433 gb->cpu->halted = false;
434
435 if (!gb->memory.ime || gb->cpu->irqPending) {
436 return;
437 }
438
439 if (irqs & (1 << GB_IRQ_VBLANK)) {
440 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
441 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
442 return;
443 }
444 if (irqs & (1 << GB_IRQ_LCDSTAT)) {
445 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
446 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
447 return;
448 }
449 if (irqs & (1 << GB_IRQ_TIMER)) {
450 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
451 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
452 return;
453 }
454 if (irqs & (1 << GB_IRQ_SIO)) {
455 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
456 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
457 return;
458 }
459 if (irqs & (1 << GB_IRQ_KEYPAD)) {
460 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
461 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
462 }
463}
464
465void GBProcessEvents(struct LR35902Core* cpu) {
466 struct GB* gb = (struct GB*) cpu->master;
467 do {
468 int32_t cycles = cpu->nextEvent;
469 int32_t nextEvent = INT_MAX;
470 int32_t testEvent;
471
472 if (gb->eiPending != INT_MAX) {
473 gb->eiPending -= cycles;
474 if (gb->eiPending <= 0) {
475 gb->memory.ime = true;
476 GBUpdateIRQs(gb);
477 gb->eiPending = INT_MAX;
478 } else {
479 nextEvent = gb->eiPending;
480 }
481 }
482
483 testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
484 if (testEvent != INT_MAX) {
485 testEvent <<= gb->doubleSpeed;
486 if (testEvent < nextEvent) {
487 nextEvent = testEvent;
488 }
489 }
490
491 testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
492 if (testEvent != INT_MAX) {
493 testEvent <<= gb->doubleSpeed;
494 if (testEvent < nextEvent) {
495 nextEvent = testEvent;
496 }
497 }
498
499 testEvent = GBTimerProcessEvents(&gb->timer, cycles);
500 if (testEvent < nextEvent) {
501 nextEvent = testEvent;
502 }
503
504 testEvent = GBSIOProcessEvents(&gb->sio, cycles);
505 if (testEvent < nextEvent) {
506 nextEvent = testEvent;
507 }
508
509 testEvent = GBMemoryProcessEvents(gb, cycles);
510 if (testEvent < nextEvent) {
511 nextEvent = testEvent;
512 }
513
514 cpu->cycles -= cycles;
515 cpu->nextEvent = nextEvent;
516
517 if (cpu->halted) {
518 cpu->cycles = cpu->nextEvent;
519 }
520 } while (cpu->cycles >= cpu->nextEvent);
521}
522
523void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
524 struct GB* gb = (struct GB*) cpu->master;
525 if (!enable) {
526 gb->memory.ime = enable;
527 gb->eiPending = INT_MAX;
528 GBUpdateIRQs(gb);
529 } else {
530 if (cpu->nextEvent > cpu->cycles + 4) {
531 cpu->nextEvent = cpu->cycles + 4;
532 }
533 gb->eiPending = cpu->cycles + 4;
534 }
535}
536
537void GBHalt(struct LR35902Core* cpu) {
538 if (!cpu->irqPending) {
539 cpu->cycles = cpu->nextEvent;
540 cpu->halted = true;
541 }
542}
543
544void GBStop(struct LR35902Core* cpu) {
545 struct GB* gb = (struct GB*) cpu->master;
546 if (cpu->bus) {
547 mLOG(GB, GAME_ERROR, "Hit illegal stop at address %04X:%02X\n", cpu->pc, cpu->bus);
548 if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
549 struct mDebuggerEntryInfo info = {
550 .address = cpu->pc - 1,
551 .opcode = 0x1000 | cpu->bus
552 };
553 mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
554 }
555 // Hang forever
556 gb->memory.ime = 0;
557 cpu->pc -= 2;
558 } else if (gb->memory.io[REG_KEY1] & 1) {
559 gb->doubleSpeed ^= 1;
560 gb->memory.io[REG_KEY1] &= 1;
561 gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
562 }
563 // TODO: Actually stop
564}
565
566void GBIllegal(struct LR35902Core* cpu) {
567 struct GB* gb = (struct GB*) cpu->master;
568 mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
569 if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
570 struct mDebuggerEntryInfo info = {
571 .address = cpu->pc,
572 .opcode = cpu->bus
573 };
574 mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
575 }
576 // Hang forever
577 gb->memory.ime = 0;
578 --cpu->pc;
579}
580
581bool GBIsROM(struct VFile* vf) {
582 vf->seek(vf, 0x104, SEEK_SET);
583 uint8_t header[4];
584 static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
585
586 if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
587 return false;
588 }
589 if (memcmp(header, knownHeader, sizeof(header))) {
590 return false;
591 }
592 return true;
593}
594
595void GBGetGameTitle(struct GB* gb, char* out) {
596 const struct GBCartridge* cart = NULL;
597 if (gb->memory.rom) {
598 cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
599 }
600 if (gb->pristineRom) {
601 cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
602 }
603 if (!cart) {
604 return;
605 }
606 if (cart->oldLicensee != 0x33) {
607 memcpy(out, cart->titleLong, 16);
608 } else {
609 memcpy(out, cart->titleShort, 11);
610 }
611}
612
613void GBGetGameCode(struct GB* gb, char* out) {
614 memset(out, 0, 8);
615 const struct GBCartridge* cart = NULL;
616 if (gb->memory.rom) {
617 cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
618 }
619 if (gb->pristineRom) {
620 cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
621 }
622 if (!cart) {
623 return;
624 }
625 if (cart->cgb == 0xC0) {
626 memcpy(out, "CGB-????", 8);
627 } else {
628 memcpy(out, "DMG-????", 8);
629 }
630 if (cart->oldLicensee == 0x33) {
631 memcpy(&out[4], cart->maker, 4);
632 }
633}
634
635void GBFrameEnded(struct GB* gb) {
636 GBSramClean(gb, gb->video.frameCounter);
637
638 if (gb->cpu->components && gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
639 struct mCheatDevice* device = (struct mCheatDevice*) gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
640 size_t i;
641 for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
642 struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
643 mCheatRefresh(device, cheats);
644 }
645 }
646}