OSDN Git Service

fe5446637e53dc4acd83a07a31e5dcdccc2a484c
[unagi/old-svn-converted.git] / client / trunk / anago / script_dump.c
1 #include <assert.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <squirrel.h>
5 #include <sqstdio.h>
6 #include <sqstdaux.h>
7 #include "type.h"
8 #include "file.h"
9 #include "widget.h"
10 #include "romimage.h"
11 #include "memory_manage.h"
12 #include "reader_master.h"
13 #include "squirrel_wrap.h"
14 #include "script_common.h"
15 #include "script_dump.h"
16
17 #define USERPOINTER_GET(Pointer, Result) \
18  struct dump_config *Pointer; \
19  SQRESULT Result =  qr_userpointer_get(v, (SQUserPointer) &Pointer); \
20  if(SQ_FAILED(Result)){ \
21         return Result; \
22  }
23
24
25 static SQInteger cpu_write(HSQUIRRELVM v)
26 {
27         USERPOINTER_GET(d, r)
28         cpu_write_execute(v, d->handle, d->cpu.access);
29         return 0;
30 }
31
32 //ここの printf は debug 用に残しておく
33 static void buffer_show(struct memory *t, long length)
34 {
35         int i;
36         const uint8_t *buf = t->data + t->offset;
37 #ifdef _UNICODE
38         wprintf(L"%s 0x%06x:", t->name, t->offset);
39 #else
40         printf("%s 0x%06x:", t->name, t->offset);
41 #endif
42         for(i = 0; i < 0x10; i++){
43                 wgChar dump[3+1];
44 #ifdef _UNICODE
45                 //wsprintf(dump, L"%02x", buf[i]);
46 #else
47                 sprintf(dump, "%02x", buf[i]);
48 #endif
49                 switch(i){
50                 case 7:
51                         dump[2] = wgT('-');
52                         break;
53                 case 0x0f:
54                         dump[2] = wgT('\0');
55                         break;
56                 default:
57                         dump[2] = wgT(' ');
58                         break;
59                 }
60                 dump[3] = wgT('\0');
61 #ifdef _UNICODE
62                 wprintf(L"%s", dump);
63 #else
64                 printf("%s", dump);
65 #endif
66         }
67         int sum = 0;
68         while(length != 0){
69                 sum += (int) *buf;
70                 buf++;
71                 length--;
72         }
73 #ifdef _UNICODE
74         wprintf(L":0x%06x\n", sum);
75 #else
76         printf(":0x%06x\n", sum);
77 #endif
78         fflush(stdout);
79 }
80
81 static SQInteger read_memory(HSQUIRRELVM v, const struct reader_handle *h, struct dump_memory_driver *t, bool progress)
82 {
83         long address, length;
84         SQRESULT r = qr_argument_get(v, 2, &address, &length);
85         if(SQ_FAILED(r)){
86                 return r;
87         }
88         assert(t->memory.attribute == MEMORY_ATTR_WRITE);
89         t->access->memory_read(h, &t->gauge, address, length, t->memory.data + t->memory.offset);
90
91         if((length != 0) && (progress == false)){
92                 buffer_show(&t->memory, length);
93         }
94         t->memory.offset += length;
95         return 0;
96 }
97
98 static SQInteger cpu_read(HSQUIRRELVM v)
99 {
100         USERPOINTER_GET(d, r)
101         r = read_memory(v, d->handle, &d->cpu, d->progress);
102         return r;
103 }
104
105 static SQInteger ppu_read(HSQUIRRELVM v)
106 {
107         USERPOINTER_GET(d, r)
108         r = read_memory(v, d->handle, &d->ppu, d->progress);
109         return r;
110 }
111
112 static SQInteger ppu_ramfind(HSQUIRRELVM v)
113 {
114         struct dump_config *d;
115         enum{
116                 testsize = 8,
117                 testaddress = 1234
118         };
119         static const uint8_t test_val[testsize] = {0xaa, 0x55, 0, 0xff, 0x46, 0x49, 0x07, 0x21};
120         static const uint8_t test_str[testsize] = "pputest";
121         uint8_t test_result[testsize];
122         SQRESULT r =  qr_userpointer_get(v, (SQUserPointer) &d);
123         struct dump_memory_driver *p = &d->ppu;
124
125         if(SQ_FAILED(r)){
126                 return r;
127         }
128         p->access->memory_write(d->handle, testaddress, testsize, test_val);
129         p->access->memory_read(d->handle, &GAUGE_DUMMY, testaddress, testsize, test_result);
130         if(memcmp(test_val, test_result, testsize) != 0){
131                 sq_pushbool(v, SQFalse);
132                 return 1;
133         }
134         p->access->memory_write(d->handle, testaddress, testsize, test_str);
135         p->access->memory_read(d->handle, &GAUGE_DUMMY, testaddress, testsize, test_result);
136         if(memcmp(test_str, test_result, testsize) != 0){
137                 sq_pushbool(v, SQFalse);
138                 return 1;
139         }
140         p->memory.offset = 0;
141         p->memory.size = 0;
142         sq_pushbool(v, SQTrue);
143         return 1;
144 }
145
146 static SQInteger return_true(HSQUIRRELVM v)
147 {
148         sq_pushbool(v, SQFalse);
149         return 1;
150 }
151
152 static void memory_new_init(struct dump_memory_driver *d)
153 {
154         d->memory.offset = 0;
155         d->memory.data = Malloc(d->memory.size);
156         d->gauge.range_set(d->gauge.bar, d->memory.size);
157         d->gauge.value_set(d->gauge.bar, d->gauge.label, 0);
158 }
159
160 //test 時/1度目の call で使用
161 static SQInteger memory_new(HSQUIRRELVM v)
162 {
163         USERPOINTER_GET(d, r)
164
165         r = qr_argument_get(v, 2, &d->cpu.memory.size, &d->ppu.memory.size);
166         if(SQ_FAILED(r)){
167                 return r;
168         }
169
170         memory_new_init(&d->cpu);
171         if(d->mode == MODE_ROM_DUMP){
172                 memory_new_init(&d->ppu);
173         }
174         return 0;
175 }
176
177 //dump 時/2度目の call で nesfile_save として使用
178 static SQInteger nesfile_save(HSQUIRRELVM v)
179 {
180         USERPOINTER_GET(d, r)
181
182         struct romimage image;
183         long mirrorfind;
184         r = qr_argument_get(v, 2, &image.mappernum, &mirrorfind);
185         if(SQ_FAILED(r)){
186                 return r;
187         }
188         image.cpu_rom = d->cpu.memory;
189         image.cpu_ram.data = NULL;
190         image.ppu_rom = d->ppu.memory;
191         image.mirror = MIRROR_PROGRAMABLE;
192         if(mirrorfind == 1){
193                 uint8_t c = d->control->vram_connection(d->handle);
194                 if(DEBUG == 1){
195                         d->log.append(d->log.object, wgT("vram connection %x\n"), c);
196                 }
197 /*
198 kazzo 1.0 return value H:9 V:5
199 kazzo 2.x return value H:C V:A
200 */
201                 if(c == 0x05 || c == 0x0a){
202                         image.mirror = MIRROR_VERTICAL;
203                 }else{
204                         image.mirror = MIRROR_HORIZONAL;
205                 }
206         }
207         image.backupram = 0;
208         if(d->battery == true){
209                 image.backupram = 1;
210         }
211         d->crc = nesfile_create(&d->log, &image, d->target);
212         nesbuffer_free(&image, 0); //0 is MODE_xxx_xxxx
213         
214         d->cpu.memory.data = NULL;
215         d->ppu.memory.data = NULL;
216         return 0;
217 }
218
219 static bool length_check_core(struct dump_config *d, struct dump_memory_driver *m, const wgChar * str)
220 {
221         bool ret = true;
222         if((m->read_count_bit & 0x7) != 0){
223                 ret = false;
224         }
225         m->read_count_byte += m->read_count_bit >> 3;
226         if(m->memory.size != m->read_count_byte){
227                 ret = false;
228         }
229         if(ret == false){
230                 d->log.append(d->log.object, wgT("%s is not connected 0x%06x.%d/0x%06x\n"), str, (int) m->read_count_byte, (int) m->read_count_bit & 7, (int) m->memory.size);
231         }
232         
233         return ret;
234 }
235
236 //dump 時/1度目の call で nesfile_save として使用
237 static SQInteger length_check(HSQUIRRELVM v)
238 {
239         USERPOINTER_GET(d, r)
240
241         bool cpu = true, ppu = true;
242         r = 0;
243         cpu = length_check_core(d, &d->cpu, d->mode == MODE_ROM_DUMP ? wgT("board.cpu_rom.size") : wgT("board.cpu_ram.size"));
244         ppu = length_check_core(d, &d->ppu, wgT("board.ppu_rom.size"));
245         if(cpu == false || ppu == false){
246                 r = sq_throwerror(v, wgT("script logical error"));
247         }
248         return r;
249 }
250
251 static SQInteger read_count(HSQUIRRELVM v, const struct textcontrol *l, struct dump_memory_driver *t, const struct range *range_address, const struct range *range_length)
252 {
253         long address, length;
254         SQRESULT r = qr_argument_get(v, 2, &address, &length);
255         if(SQ_FAILED(r)){
256                 return r;
257         }
258         r = range_check(v, wgT("length"), length, range_length);
259         if(SQ_FAILED(r)){
260                 return r;
261         }
262         if((address < range_address->start) || ((address + length) > range_address->end)){
263                 l->append(l->object, wgT("address range must be 0x%06x to 0x%06x"), (int) range_address->start, (int) range_address->end);
264                 return sq_throwerror(v, wgT("script logical error"));;
265         }
266         t->read_count_byte += length;
267         return 0;
268 }
269 static SQInteger cpu_read_count(HSQUIRRELVM v)
270 {
271 #ifdef DEBUG
272         static const struct range range_address = {0x6000, 0x10000}; //for Sunsoft-5 series test
273 #else
274         static const struct range range_address = {0x8000, 0x10000};
275 #endif
276         static const struct range range_length = {0x0001, 0x4000};
277         USERPOINTER_GET(d, r)
278
279         return read_count(v, &d->log, &d->cpu, &range_address, &range_length);
280 }
281
282 static SQInteger ppu_read_count(HSQUIRRELVM v)
283 {
284 #ifdef DEBUG
285         static const struct range range_address = {0x0000, 0x2800};
286 #else
287         static const struct range range_address = {0x0000, 0x2000};
288 #endif
289         //length == 0 は 対象アドレスを呼んで、バッファにいれない。mmc2, mmc4 で使用する。
290         static const struct range range_length = {0x0000, 0x2000};
291         USERPOINTER_GET(d, r)
292
293         return read_count(v, &d->log, &d->ppu, &range_address, &range_length);
294 }
295
296 static SQInteger cpu_read_register_check(HSQUIRRELVM v)
297 {
298         static const struct range range_address = {0x4800, 0x8000};
299         static const struct range range_byte = {0, 0xff};
300         USERPOINTER_GET(d, r)
301
302         long address, byte;
303         r = qr_argument_get(v, 2, &address, &byte);
304         if(SQ_FAILED(r)){
305                 return r;
306         }
307         r = range_check(v, wgT("address"), address, &range_address);
308         if(SQ_FAILED(r)){
309                 return r;
310         }
311         r = range_check(v, wgT("byte"), byte, &range_byte);
312         if(SQ_FAILED(r)){
313                 return r;
314         }
315
316         sq_pushinteger(v, byte);
317         return 1;
318 }
319
320 static SQInteger cpu_read_register(HSQUIRRELVM v)
321 {
322         USERPOINTER_GET(d, r)
323         long address, dummy;
324         r = qr_argument_get(v, 2, &address, &dummy);
325         if(SQ_FAILED(r)){
326                 return r;
327         }
328
329         uint8_t readdata;
330         d->cpu.access->memory_read(d->handle, &GAUGE_DUMMY, address, 1, &readdata);
331
332         sq_pushinteger(v, readdata);
333         return 1;
334 }
335
336 static SQInteger memory_size_set(HSQUIRRELVM v)
337 {
338         USERPOINTER_GET(d, r)
339         r = qr_argument_get(v, 2, &d->cpu.memory.size, &d->ppu.memory.size);
340         return r;
341 }
342
343 static bool script_execute(HSQUIRRELVM v, struct dump_config *d)
344 {
345         bool ret = true;
346         if(SQ_FAILED(sqstd_dofile(v, wgT("dumpcore.nut"), SQFalse, SQTrue))){
347                 d->log.append(d->log.object, wgT("dump core script error\n"));
348                 ret = false;
349         }else{
350                 SQRESULT r = qr_call(
351                         v, wgT("dump"), (SQUserPointer) d, d->script, 
352                         3, d->mappernum, d->cpu.increase, d->ppu.increase
353                 );
354                 if(SQ_FAILED(r)){
355                         ret = false;
356                         Free(d->cpu.memory.data);
357                         Free(d->ppu.memory.data);
358                         d->cpu.memory.data = NULL;
359                         d->ppu.memory.data = NULL;
360                 }
361         }
362         return ret;
363 }
364
365 static void dump_memory_driver_init(struct dump_memory_driver *dd, enum memory_attribute at)
366 {
367         dd->memory.size = 0;
368         dd->memory.offset = 0;
369         dd->memory.attribute = at;
370         dd->memory.transtype = TRANSTYPE_FULL;
371         dd->memory.data = NULL;
372         dd->read_count_byte = 0;
373         dd->read_count_bit = 0;
374 }
375
376 bool script_dump_execute(struct dump_config *d)
377 {
378         dump_memory_driver_init(&d->cpu, MEMORY_ATTR_WRITE);
379         d->cpu.memory.name = wgT("Program");
380         
381         dump_memory_driver_init(&d->ppu, MEMORY_ATTR_WRITE);
382         d->ppu.memory.name = wgT("Charcter");
383         
384         {
385                 HSQUIRRELVM v = qr_open(&d->log);
386                 qr_function_register_global(v, wgT("cpu_write"), cpu_write_check);
387                 qr_function_register_global(v, wgT("memory_new"), memory_size_set);
388                 qr_function_register_global(v, wgT("nesfile_save"), length_check);
389                 qr_function_register_global(v, wgT("cpu_read"), cpu_read_count);
390                 qr_function_register_global(v, wgT("ppu_read"), ppu_read_count);
391                 qr_function_register_global(v, wgT("ppu_ramfind"), return_true);
392                 if(script_execute(v, d) == false){
393                         qr_close(v);
394                         return false;
395                 }
396                 qr_close(v);
397         }
398
399         d->handle = d->control->open(d->except, &d->log);
400         if(d->handle == NULL){
401                 d->log.append(d->log.object, wgT("reader open error\n"));
402                 return false;
403         }
404 /*      d->control->init(d->handle);
405         if(connection_check(d->handle, &d->log, d->cpu.access, d->ppu.access) == false){
406                 d->control->close(d->handle);
407                 return false;
408         }*/
409         {
410                 HSQUIRRELVM v = qr_open(&d->log); 
411                 qr_function_register_global(v, wgT("memory_new"), memory_new);
412                 qr_function_register_global(v, wgT("nesfile_save"), nesfile_save);
413                 qr_function_register_global(v, wgT("cpu_write"), cpu_write);
414                 qr_function_register_global(v, wgT("cpu_read"), cpu_read);
415                 qr_function_register_global(v, wgT("ppu_read"), ppu_read);
416                 qr_function_register_global(v, wgT("ppu_ramfind"), ppu_ramfind);
417                 script_execute(v, d);
418                 qr_close(v);
419         }
420         d->control->close(d->handle);
421         d->handle = NULL;
422         return true;
423 }
424
425 static bool workram_execute(HSQUIRRELVM v, struct dump_config *d)
426 {
427         bool ret = true;
428         if(SQ_FAILED(sqstd_dofile(v, wgT("dumpcore.nut"), SQFalse, SQTrue))){
429                 d->log.append(d->log.object, wgT("dump core script error\n"));
430                 ret = false;
431         }else{
432                 SQRESULT r = qr_call(
433                         v, wgT("workram_rw"), (SQUserPointer) d, d->script, 
434                         1, d->cpu.increase
435                 );
436                 if(SQ_FAILED(r)){
437                         ret = false;
438                         Free(d->cpu.memory.data);
439                         d->cpu.memory.data = NULL;
440 //                      Free(d->ppu.memory.data);
441 //                      d->ppu.memory.data = NULL;
442                 }
443         }
444         return ret;
445 }
446
447 static SQInteger cpu_ramrw_check(HSQUIRRELVM v)
448 {
449         static const struct range range_address = {0x4800, 0xdfff};
450         static const struct range range_length = {1, 0x2000};
451         USERPOINTER_GET(d, r)
452
453         return read_count(v, &d->log, &d->cpu, &range_address, &range_length);
454 }
455
456 static SQInteger ramimage_open(HSQUIRRELVM v)
457 {
458         USERPOINTER_GET(d, r)
459         memory_new(v);
460         if(buf_load(d->cpu.memory.data, d->target, d->cpu.memory.size) == NG){
461                 return r = sq_throwerror(v, wgT("RAM image open error"));
462         }
463         return 0;
464 }
465
466 static SQInteger memory_finalize(HSQUIRRELVM v)
467 {
468         USERPOINTER_GET(d, r)
469
470         if(d->mode == MODE_RAM_READ){
471                 buf_save(d->cpu.memory.data, d->target, d->cpu.memory.size);
472         }
473         Free(d->cpu.memory.data);
474         d->cpu.memory.data = NULL;
475 //      Free(d->ppu.memory.data);
476 //      d->ppu.memory.data = NULL;
477         
478         return 0;
479 }
480
481 static SQInteger cpu_write_ramimage(HSQUIRRELVM v)
482 {
483         USERPOINTER_GET(d, r)
484
485         long address, length;
486         uint8_t *cmp;
487         const uint8_t *writedata = d->cpu.memory.data;
488         writedata += d->cpu.memory.offset;
489         
490         r = qr_argument_get(v, 2, &address, &length);
491         if(SQ_FAILED(r)){
492                 return r;
493         }
494         cmp = Malloc(length);
495         assert(d->cpu.memory.attribute == MEMORY_ATTR_READ);
496
497         d->cpu.access->memory_write(
498                 d->handle, address, length, writedata
499         );
500         d->cpu.access->memory_read(
501                 d->handle, &d->cpu.gauge, address, length, cmp
502         );
503         d->cpu.memory.offset += length;
504
505         r = memcmp(cmp, writedata, length);
506         Free(cmp);
507         if(r != 0){
508                 r = sq_throwerror(v, wgT("memory write failed"));
509         }
510         return 0;
511 }
512
513 static SQInteger mode_is_read(HSQUIRRELVM v)
514 {
515         USERPOINTER_GET(d, r)
516         sq_pushbool(v, d->mode == MODE_RAM_READ ? SQTrue : SQFalse);
517         return 1;
518 }
519
520 static SQInteger cpu_read_bit_check(HSQUIRRELVM v)
521 {
522         static const struct range range_address = {0x4800, 0xdfff};
523         static const struct range range_bit = {0, 7};
524         USERPOINTER_GET(d, r)
525
526         long address, bit;
527         r = qr_argument_get(v, 2, &address, &bit);
528         if(SQ_FAILED(r)){
529                 return r;
530         }
531         r = range_check(v, wgT("address"), address, &range_address);
532         if(SQ_FAILED(r)){
533                 return r;
534         }
535         r = range_check(v, wgT("bit"), bit, &range_bit);
536         if(SQ_FAILED(r)){
537                 return r;
538         }
539         d->cpu.read_count_bit += 1;
540         return 0;
541 }
542
543 static inline void gauge_increment(const struct gauge *g)
544 {
545         g->value_add(g->bar, g->label, 1);
546 }
547
548 static SQInteger cpu_read_bit_msb(HSQUIRRELVM v)
549 {
550         USERPOINTER_GET(d, r)
551         long address, bit;
552         r = qr_argument_get(v, 2, &address, &bit);
553         if(SQ_FAILED(r)){
554                 return r;
555         }
556
557         assert(d->cpu.memory.attribute == MEMORY_ATTR_WRITE);
558         uint8_t readdata;
559
560         d->cpu.access->memory_read(d->handle, &GAUGE_DUMMY, address, 1, &readdata);
561         readdata >>= bit;
562         readdata &= 1;
563
564         if(d->cpu.read_count_bit == 0){
565                 d->cpu.bitbuffer = 0;
566         }
567         d->cpu.bitbuffer |= readdata;
568
569         d->cpu.read_count_bit += 1;
570         
571         if(d->cpu.read_count_bit == 8){
572                 d->cpu.read_count_bit = 0;
573                 d->cpu.memory.data[d->cpu.memory.offset] = d->cpu.bitbuffer;
574                 d->cpu.memory.offset += 1;
575                 gauge_increment(&d->cpu.gauge);
576         }else{
577                 d->cpu.bitbuffer <<= 1;
578         }
579         return 0;
580 }
581
582 static SQInteger cpu_fetch_bit_check(HSQUIRRELVM v)
583 {
584         USERPOINTER_GET(d, r)
585         d->cpu.read_count_bit += 1;
586         return 0;
587 }
588
589 static SQInteger cpu_fetch_bit_msb(HSQUIRRELVM v)
590 {
591         USERPOINTER_GET(d, r)
592
593         if(d->cpu.read_count_bit == 0){
594                 d->cpu.bitbuffer = d->cpu.memory.data[d->cpu.memory.offset];
595         }
596         sq_pushinteger(v, d->cpu.bitbuffer & 0x80);
597         
598         d->cpu.read_count_bit += 1;
599         if(d->cpu.read_count_bit == 8){
600                 d->cpu.read_count_bit = 0;
601                 d->cpu.memory.offset += 1;
602                 gauge_increment(&d->cpu.gauge);
603         }else{
604                 d->cpu.bitbuffer <<= 1;
605         }
606         return 1;
607 }
608
609 bool script_workram_execute(struct dump_config *d)
610 {
611         dump_memory_driver_init(&d->cpu, d->mode == MODE_RAM_READ ? MEMORY_ATTR_WRITE : MEMORY_ATTR_READ);
612         dump_memory_driver_init(&d->ppu, MEMORY_ATTR_NOTUSE);
613         d->cpu.memory.name = wgT("Workram");
614         d->ppu.memory.name = wgT("N/A");
615
616         {
617                 HSQUIRRELVM v = qr_open(&d->log);
618                 qr_function_register_global(v, wgT("memory_new"), memory_size_set);
619                 qr_function_register_global(v, wgT("cpu_write"), cpu_write_check);
620                 qr_function_register_global(v, wgT("cpu_ramrw"), cpu_ramrw_check);
621                 qr_function_register_global(v, wgT("memory_finalize"), length_check);
622                 qr_function_register_global(v, wgT("mode_is_read"), mode_is_read);
623                 qr_function_register_global(v, wgT("cpu_read_register"), cpu_read_register_check);
624                 switch(d->mode){
625                 case MODE_RAM_READ:
626                         qr_function_register_global(v, wgT("cpu_read_bit_msb"), cpu_read_bit_check);
627                         break;
628                 case MODE_RAM_WRITE:
629                         qr_function_register_global(v, wgT("cpu_fetch_bit_msb"), cpu_fetch_bit_check);
630                         break;
631                 default:
632                         break;
633                 }
634                 if(workram_execute(v, d) == false){
635                         qr_close(v);
636                         return false;
637                 }
638                 qr_close(v);
639         }
640         
641         d->handle = d->control->open(d->except, &d->log);
642         if(d->handle == NULL){
643                 d->log.append(d->log.object, wgT("reader open error\n"));
644                 return false;
645         }
646         assert((d->cpu.read_count_bit & 7) == 0);
647         d->cpu.read_count_bit = 0;
648         d->control->init(d->handle);
649         if(connection_check(d->handle, &d->log, d->cpu.access, d->ppu.access) == false){
650                 d->control->close(d->handle);
651                 return false;
652         }
653         {
654                 HSQUIRRELVM v = qr_open(&d->log); 
655                 qr_function_register_global(v, wgT("cpu_write"), cpu_write);
656                 qr_function_register_global(v, wgT("cpu_read_register"), cpu_read_register);
657                 qr_function_register_global(v, wgT("mode_is_read"), mode_is_read);
658                 switch(d->mode){
659                 case MODE_RAM_READ:
660                         qr_function_register_global(v, wgT("memory_new"), memory_new);
661                         qr_function_register_global(v, wgT("cpu_ramrw"), cpu_read);
662                         qr_function_register_global(v, wgT("cpu_read_bit_msb"), cpu_read_bit_msb);
663                         break;
664                 case MODE_RAM_WRITE:
665                         qr_function_register_global(v, wgT("memory_new"), ramimage_open);
666                         qr_function_register_global(v, wgT("cpu_ramrw"), cpu_write_ramimage);
667                         qr_function_register_global(v, wgT("cpu_fetch_bit_msb"), cpu_fetch_bit_msb);
668                         break;
669                 default:
670                         assert(0);
671                         break;
672                 }
673                 qr_function_register_global(v, wgT("memory_finalize"), memory_finalize);
674                 workram_execute(v, d);
675                 qr_close(v);
676         }
677         d->control->close(d->handle);
678         d->handle = NULL;
679         return true;
680 }