OSDN Git Service

3664eb342076e18edccd1929305681c2846a4c69
[csp-qt/common_source_project-fm7.git] / source / src / vm / x1 / x1.cpp
1 /*
2         SHARP X1 Emulator 'eX1'
3         SHARP X1twin Emulator 'eX1twin'
4         SHARP X1turbo Emulator 'eX1turbo'
5
6         Author : Takeda.Toshiya
7         Date   : 2009.03.11-
8
9         [ virtual machine ]
10 */
11
12 #include "x1.h"
13 #include "../../emu.h"
14 #include "../device.h"
15 #include "../event.h"
16
17 #include "../datarec.h"
18 #include "../disk.h"
19 #include "../hd46505.h"
20 #include "../i8255.h"
21 #include "../io.h"
22 #include "../mb8877.h"
23 #include "../ym2151.h"
24 #include "../ym2203.h"
25 #include "../z80.h"
26 #include "../z80ctc.h"
27 #include "../z80sio.h"
28 #ifdef _X1TURBO_FEATURE
29 #include "../z80dma.h"
30 #endif
31
32 #ifdef USE_DEBUGGER
33 #include "../debugger.h"
34 #endif
35
36 #include "display.h"
37 #include "emm.h"
38 #include "floppy.h"
39 #include "iobus.h"
40 #include "joystick.h"
41 #include "memory.h"
42 #include "mouse.h"
43 #include "printer.h"
44 #include "psub.h"
45
46 #include "../mcs48.h"
47 #include "../upd1990a.h"
48 #include "sub.h"
49 #include "keyboard.h"
50
51 #ifdef _X1TWIN
52 #include "../huc6280.h"
53 #include "../pcengine/pce.h"
54 #endif
55
56 // ----------------------------------------------------------------------------
57 // initialize
58 // ----------------------------------------------------------------------------
59
60 VM::VM(EMU* parent_emu) : emu(parent_emu)
61 {
62         pseudo_sub_cpu = !(FILEIO::IsFileExists(emu->bios_path(SUB_ROM_FILE_NAME)) && FILEIO::IsFileExists(emu->bios_path(KBD_ROM_FILE_NAME)));
63         
64         sound_device_type = config.sound_device_type;
65         
66         // create devices
67         first_device = last_device = NULL;
68         dummy = new DEVICE(this, emu);  // must be 1st device
69         event = new EVENT(this, emu);   // must be 2nd device
70         
71         drec = new DATAREC(this, emu);
72         crtc = new HD46505(this, emu);
73         pio = new I8255(this, emu);
74         io = new IO(this, emu);
75         fdc = new MB8877(this, emu);
76         psg = new YM2203(this, emu);
77         cpu = new Z80(this, emu);
78         ctc = new Z80CTC(this, emu);
79         sio = new Z80SIO(this, emu);
80         if(sound_device_type >= 1) {
81                 opm1 = new YM2151(this, emu);
82                 ctc1 = new Z80CTC(this, emu);
83         }
84         if(sound_device_type == 2) {
85                 opm2 = new YM2151(this, emu);
86                 ctc2 = new Z80CTC(this, emu);
87         }
88 #ifdef _X1TURBO_FEATURE
89         dma = new Z80DMA(this, emu);
90 #endif
91         
92         display = new DISPLAY(this, emu);
93         emm = new EMM(this, emu);
94         floppy = new FLOPPY(this, emu);
95         iobus = new IOBUS(this, emu);
96         joy = new JOYSTICK(this, emu);
97         memory = new MEMORY(this, emu);
98         mouse = new MOUSE(this, emu);
99         printer = new PRINTER(this, emu);
100         
101         if(pseudo_sub_cpu) {
102                 psub = new PSUB(this, emu);
103                 cpu_sub = NULL;
104                 cpu_kbd = NULL;
105         } else {
106                 // sub cpu
107                 cpu_sub = new MCS48(this, emu);
108                 pio_sub = new I8255(this, emu);
109                 rtc_sub = new UPD1990A(this, emu);
110                 sub = new SUB(this, emu);
111                 
112                 // keyboard
113                 cpu_kbd = new MCS48(this, emu);
114                 kbd = new KEYBOARD(this, emu);
115         }
116         
117         // set contexts
118         event->set_context_cpu(cpu);
119         if(!pseudo_sub_cpu) {
120                 event->set_context_cpu(cpu_sub, 6000000);
121                 event->set_context_cpu(cpu_kbd, 6000000);
122         }
123         if(sound_device_type >= 1) {
124                 event->set_context_sound(opm1);
125         }
126         if(sound_device_type == 2) {
127                 event->set_context_sound(opm2);
128         }
129         event->set_context_sound(psg);
130         event->set_context_sound(drec);
131    
132         drec->set_context_out(pio, SIG_I8255_PORT_B, 0x02);
133         crtc->set_context_vblank(display, SIG_DISPLAY_VBLANK, 1);
134         crtc->set_context_vblank(pio, SIG_I8255_PORT_B, 0x80);
135         crtc->set_context_vsync(pio, SIG_I8255_PORT_B, 0x04);
136         pio->set_context_port_a(printer, SIG_PRINTER_OUT, 0xff, 0);
137         pio->set_context_port_c(drec, SIG_DATAREC_OUT, 0x01, 0);
138         pio->set_context_port_c(display, SIG_DISPLAY_COLUMN40, 0x40, 0);
139         pio->set_context_port_c(iobus, SIG_IOBUS_MODE, 0x60, 0);
140         pio->set_context_port_c(printer, SIG_PRINTER_STB, 0x80, 0);
141 #ifdef _X1TURBO_FEATURE
142         fdc->set_context_drq(dma, SIG_Z80DMA_READY, 1);
143 #endif
144         ctc->set_context_zc0(ctc, SIG_Z80CTC_TRIG_3, 1);
145         ctc->set_context_zc1(sio, SIG_Z80SIO_TX_CLK_CH0, 1);
146         ctc->set_context_zc1(sio, SIG_Z80SIO_RX_CLK_CH0, 1);
147         ctc->set_context_zc2(sio, SIG_Z80SIO_TX_CLK_CH1, 1);
148         ctc->set_context_zc2(sio, SIG_Z80SIO_RX_CLK_CH1, 1);
149         ctc->set_constant_clock(1, CPU_CLOCKS >> 1);
150         ctc->set_constant_clock(2, CPU_CLOCKS >> 1);
151         sio->set_context_rts(1, mouse, SIG_MOUSE_RTS, 1);
152 //      sio->set_tx_clock(0, 9600 * 16);        // 9600 baud for RS-232C
153 //      sio->set_rx_clock(0, 9600 * 16);        // clock is from Z-80CTC ch1 (2MHz/13)
154 //      sio->set_tx_clock(1, 4800 * 16);        // 4800 baud for mouse
155 //      sio->set_rx_clock(1, 4800 * 16);        // clock is from Z-80CTC ch2 (2MHz/26)
156
157         if(sound_device_type >= 1) {
158                 ctc1->set_context_zc0(ctc1, SIG_Z80CTC_TRIG_3, 1);
159 //              ctc1->set_constant_clock(1, CPU_CLOCKS >> 1);
160 //              ctc1->set_constant_clock(2, CPU_CLOCKS >> 1);
161         }
162         if(sound_device_type == 2) {
163                 ctc2->set_context_zc0(ctc2, SIG_Z80CTC_TRIG_3, 1);
164 //              ctc2->set_constant_clock(1, CPU_CLOCKS >> 1);
165 //              ctc2->set_constant_clock(2, CPU_CLOCKS >> 1);
166         }
167 #ifdef _X1TURBO_FEATURE
168         dma->set_context_memory(memory);
169         dma->set_context_io(iobus);
170 #endif
171         
172 #ifdef _X1TURBO_FEATURE
173         display->set_context_cpu(cpu);
174         display->set_context_crtc(crtc);
175 #endif
176         display->set_vram_ptr(iobus->get_vram());
177         display->set_regs_ptr(crtc->get_regs());
178         floppy->set_context_fdc(fdc);
179         iobus->set_context_cpu(cpu);
180         iobus->set_context_display(display);
181         iobus->set_context_io(io);
182         joy->set_context_psg(psg);
183 #ifdef _X1TURBO_FEATURE
184         memory->set_context_pio(pio);
185 #endif
186         mouse->set_context_sio(sio);
187         
188         if(pseudo_sub_cpu) {
189                 drec->set_context_remote(psub, SIG_PSUB_TAPE_REMOTE, 1);
190                 drec->set_context_end(psub, SIG_PSUB_TAPE_END, 1);
191                 psub->set_context_pio(pio);
192                 psub->set_context_drec(drec);
193         } else {
194                 // sub cpu
195                 cpu_sub->set_context_mem(new MCS48MEM(this, emu));
196                 cpu_sub->set_context_io(sub);
197 #ifdef USE_DEBUGGER
198                 cpu_sub->set_context_debugger(new DEBUGGER(this, emu));
199 #endif
200                 drec->set_context_end(sub, SIG_SUB_TAPE_END, 1);
201                 drec->set_context_apss(sub, SIG_SUB_TAPE_APSS, 1);
202                 pio_sub->set_context_port_c(sub, SIG_SUB_PIO_PORT_C, 0x80, 0);
203                 // pc1:break -> pb0 of 8255(main)
204                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x02, -1);
205                 // pc5:ibf -> pb6 of 8255(main)
206                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x20, 1);
207                 // pc7:obf -> pb5 of 8255(main)
208                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x80, -2);
209                 // pc7:obf -> pb7 of 8255(sub)
210                 pio_sub->set_context_port_c(pio_sub, SIG_I8255_PORT_B, 0x80, 0);
211                 
212                 sub->set_context_pio(pio_sub);
213                 sub->set_context_rtc(rtc_sub);
214                 sub->set_context_drec(drec);
215                 
216                 // keyboard
217                 cpu_kbd->set_context_mem(new MCS48MEM(this, emu));
218                 cpu_kbd->set_context_io(kbd);
219 #ifdef USE_DEBUGGER
220                 cpu_kbd->set_context_debugger(new DEBUGGER(this, emu));
221 #endif
222                 kbd->set_context_cpu(cpu_sub);
223         }
224         
225         // cpu bus
226         cpu->set_context_mem(memory);
227         cpu->set_context_io(iobus);
228 #if defined(_X1TURBO_FEATURE) && defined(SINGLE_MODE_DMA)
229         cpu->set_context_dma(dma);
230 #endif
231 #ifdef USE_DEBUGGER
232         cpu->set_context_debugger(new DEBUGGER(this, emu));
233 #endif
234         
235         // z80 family daisy chain
236         DEVICE* parent_dev = NULL;
237         int level = 0;
238         
239         #define Z80_DAISY_CHAIN(dev) { \
240                 if(parent_dev == NULL) { \
241                         cpu->set_context_intr(dev); \
242                 } else { \
243                         parent_dev->set_context_child(dev); \
244                 } \
245                 dev->set_context_intr(cpu, level++); \
246                 parent_dev = dev; \
247         }
248 #ifndef _X1TURBO_FEATURE
249         Z80_DAISY_CHAIN(sio);   // CZ-8BM2
250         Z80_DAISY_CHAIN(ctc);
251 #endif
252         if(sound_device_type >= 1) {
253                 Z80_DAISY_CHAIN(ctc1);
254         }
255         if(sound_device_type == 2) {
256                 Z80_DAISY_CHAIN(ctc2);
257         }
258 #ifdef _X1TURBO_FEATURE
259         Z80_DAISY_CHAIN(sio);
260         Z80_DAISY_CHAIN(dma);
261         Z80_DAISY_CHAIN(ctc);
262 #endif
263         if(pseudo_sub_cpu) {
264                 Z80_DAISY_CHAIN(psub);
265         } else {
266                 Z80_DAISY_CHAIN(sub);
267         }
268         
269         // i/o bus
270         if(sound_device_type >= 1) {
271                 io->set_iomap_single_w(0x700, opm1);
272                 io->set_iovalue_single_r(0x700, 0x00);
273                 io->set_iomap_single_rw(0x701, opm1);
274 #ifdef _X1TURBOZ
275                 io->set_flipflop_single_rw(0x704, 0x00);
276 #else
277                 io->set_iomap_range_rw(0x704, 0x707, ctc1);
278 #endif
279         }
280         if(sound_device_type == 2) {
281                 io->set_iomap_single_w(0x708, opm2);
282                 io->set_iovalue_single_r(0x708, 0x00);
283                 io->set_iomap_single_rw(0x709, opm2);
284                 io->set_iomap_range_rw(0x70c, 0x70f, ctc2);
285         }
286 #ifdef _X1TURBO_FEATURE
287         io->set_iomap_single_rw(0xb00, memory);
288 #endif
289         io->set_iomap_range_rw(0xd00, 0xd03, emm);
290         io->set_iomap_range_r(0xe80, 0xe81, display);
291         io->set_iomap_range_w(0xe80, 0xe82, display);
292         io->set_iomap_range_rw(0xff8, 0xffb, fdc);
293         io->set_iomap_single_w(0xffc, floppy);
294 #ifdef _X1TURBO_FEATURE
295         io->set_iomap_range_r(0xffc, 0xfff, floppy);
296 #endif
297         io->set_iomap_range_rw(0x1000, 0x17ff, display);
298         for(int i = 0x1800; i <= 0x18ff; i += 0x10) {
299                 io->set_iomap_range_rw(i, i + 1, crtc);
300         }
301         if(pseudo_sub_cpu) {
302                 io->set_iomap_range_rw(0x1900, 0x19ff, psub);
303         } else {
304                 io->set_iomap_range_rw(0x1900, 0x19ff, sub);
305         }
306         for(int i = 0x1a00; i <= 0x1aff; i += 4) {
307                 io->set_iomap_range_rw(i, i + 3, pio);
308         }
309         for(int i = 0x1b00; i <= 0x1bff; i++) {
310                 io->set_iomap_alias_rw(i, psg, 1);
311         }
312         for(int i = 0x1c00; i <= 0x1cff; i++) {
313                 io->set_iomap_alias_w(i, psg, 0);
314         }
315         io->set_iomap_range_w(0x1d00, 0x1eff, memory);
316 #ifndef _X1TURBO_FEATURE
317         io->set_iomap_range_rw(0x1f98, 0x1f9b, sio);    // CZ-8BM2
318         io->set_iomap_range_rw(0x1fa8, 0x1fab, ctc);
319 #else
320         io->set_iomap_range_rw(0x1f80, 0x1f8f, dma);
321         io->set_iomap_range_rw(0x1f90, 0x1f93, sio);
322         io->set_iomap_range_rw(0x1fa0, 0x1fa3, ctc);
323 #ifdef _X1TURBOZ
324         io->set_iomap_single_rw(0x1fd0, display);
325         io->set_iomap_single_rw(0x1fe0, display);
326 #else
327         io->set_iomap_single_w(0x1fd0, display);
328         io->set_iomap_single_w(0x1fe0, display);
329 #endif
330         // 0x1ff0: dipswitch
331 //      io->set_iovalue_single_r(0x1ff0, 0x00);
332         update_dipswitch();
333 #endif
334         io->set_iomap_range_rw(0x2000, 0x3fff, display);        // tvram
335         
336 #ifdef _X1TWIN
337         // init PC Engine
338         pceevent = new EVENT(this, emu);
339         pceevent->set_frames_per_sec(PCE_FRAMES_PER_SEC);
340         pceevent->set_lines_per_frame(PCE_LINES_PER_FRAME);
341         
342         pcecpu = new HUC6280(this, emu);
343         pcecpu->set_context_event_manager(pceevent);
344         pce = new PCE(this, emu);
345         pce->set_context_event_manager(pceevent);
346         
347         pceevent->set_context_cpu(pcecpu, PCE_CPU_CLOCKS);
348         pceevent->set_context_sound(pce);
349         
350         pcecpu->set_context_mem(pce);
351         pcecpu->set_context_io(pce);
352 #ifdef USE_DEBUGGER
353         pcecpu->set_context_debugger(new DEBUGGER(this, emu));
354 #endif
355         pce->set_context_cpu(pcecpu);
356 #endif
357         
358         // initialize all devices
359         for(DEVICE* device = first_device; device; device = device->next_device) {
360                 device->initialize();
361         }
362         if(!pseudo_sub_cpu) {
363                 // load rom images after cpustate is allocated
364                 cpu_sub->load_rom_image(emu->bios_path(SUB_ROM_FILE_NAME));
365                 cpu_kbd->load_rom_image(emu->bios_path(KBD_ROM_FILE_NAME));
366                 
367                 // patch to set the current year
368                 uint8 *rom = cpu_sub->get_rom_ptr();
369                 sub->rom_crc32 = getcrc32(rom, 0x800);  // 2KB
370                 if(rom[0x23] == 0xb9 && rom[0x24] == 0x35 && rom[0x25] == 0xb1) {
371                         cur_time_t cur_time;
372                         emu->get_host_time(&cur_time);
373                         rom[0x26] = TO_BCD(cur_time.year);
374                 }
375         }
376         for(int i = 0; i < MAX_DRIVE; i++) {
377 #ifdef _X1TURBO_FEATURE
378                 fdc->set_drive_type(i, DRIVE_TYPE_2DD);
379 #else
380                 fdc->set_drive_type(i, DRIVE_TYPE_2D);
381 #endif
382 //              fdc->set_drive_rpm(i, 300);
383 //              fdc->set_drive_mfm(i, true);
384         }
385 }
386
387 VM::~VM()
388 {
389         // delete all devices
390         for(DEVICE* device = first_device; device;) {
391                 DEVICE *next_device = device->next_device;
392                 device->release();
393                 delete device;
394                 device = next_device;
395         }
396 }
397
398 DEVICE* VM::get_device(int id)
399 {
400         for(DEVICE* device = first_device; device; device = device->next_device) {
401                 if(device->this_device_id == id) {
402                         return device;
403                 }
404         }
405         return NULL;
406 }
407
408 // ----------------------------------------------------------------------------
409 // drive virtual machine
410 // ----------------------------------------------------------------------------
411
412 void VM::reset()
413 {
414         // reset all devices
415         for(DEVICE* device = first_device; device; device = device->next_device) {
416                 device->reset();
417         }
418         psg->SetReg(0x2e, 0);   // set prescaler
419 }
420
421 void VM::special_reset()
422 {
423         // nmi reset
424         cpu->write_signal(SIG_CPU_NMI, 1, 1);
425 }
426
427 void VM::run()
428 {
429         event->drive();
430 #ifdef _X1TWIN
431         if(pce->cart_inserted()) {
432                 pceevent->drive();
433         }
434 #endif
435 }
436
437 double VM::frame_rate()
438 {
439 #ifdef _X1TWIN
440         if(pce->cart_inserted()) {
441                 return pceevent->frame_rate();
442         }
443 #endif
444         return event->frame_rate();
445 }
446
447 // ----------------------------------------------------------------------------
448 // debugger
449 // ----------------------------------------------------------------------------
450
451 #ifdef USE_DEBUGGER
452 DEVICE *VM::get_cpu(int index)
453 {
454         if(index == 0) {
455                 return cpu;
456         } else if(index == 1) {
457                 return cpu_sub;
458         } else if(index == 2) {
459                 return cpu_kbd;
460 #ifdef _X1TWIN
461         } else if(index == 3 && pce->cart_inserted()) {
462                 return pcecpu;
463 #endif
464         }
465         return NULL;
466 }
467 #endif
468
469 // ----------------------------------------------------------------------------
470 // draw screen
471 // ----------------------------------------------------------------------------
472
473 void VM::draw_screen()
474 {
475         display->draw_screen();
476 #ifdef _X1TWIN
477         if(pce->cart_inserted()) {
478                 pce->draw_screen();
479         }
480 #endif
481 }
482
483 int VM::access_lamp()
484 {
485         uint32 status = fdc->read_signal(0);
486         return (status & (1 | 4)) ? 1 : (status & (2 | 8)) ? 2 : 0;
487 }
488
489 // ----------------------------------------------------------------------------
490 // soud manager
491 // ----------------------------------------------------------------------------
492
493 void VM::initialize_sound(int rate, int samples)
494 {
495         // init sound manager
496         event->initialize_sound(rate, samples);
497 #ifdef _X1TWIN
498         pceevent->initialize_sound(rate, samples);
499 #endif
500         
501         // init sound gen
502         if(sound_device_type >= 1) {
503                 opm1->init(rate, 4000000, samples, 0);
504         }
505         if(sound_device_type == 2) {
506                 opm2->init(rate, 4000000, samples, 0);
507         }
508         psg->init(rate, 2000000, samples, 0, 0);
509 #ifdef _X1TWIN
510         pce->initialize_sound(rate);
511 #endif
512 }
513
514 uint16* VM::create_sound(int* extra_frames)
515 {
516 #ifdef _X1TWIN
517         if(pce->cart_inserted()) {
518                 uint16* buffer = pceevent->create_sound(extra_frames);
519                 for(int i = 0; i < *extra_frames; i++) {
520                         event->drive();
521                 }
522                 return buffer;
523         }
524 #endif
525         return event->create_sound(extra_frames);
526 }
527
528 int VM::sound_buffer_ptr()
529 {
530 #ifdef _X1TWIN
531         if(pce->cart_inserted()) {
532                 return pceevent->sound_buffer_ptr();
533         }
534 #endif
535         return event->sound_buffer_ptr();
536 }
537
538 // ----------------------------------------------------------------------------
539 // notify key
540 // ----------------------------------------------------------------------------
541
542 void VM::key_down(int code, bool repeat)
543 {
544 #ifdef _X1TWIN
545         if(!repeat && !pce->cart_inserted()) {
546 #else
547         if(!repeat) {
548 #endif
549                 if(pseudo_sub_cpu) {
550                         psub->key_down(code, false);
551                 } else {
552                         kbd->key_down(code, false);
553                 }
554         }
555 }
556
557 void VM::key_up(int code)
558 {
559 #ifdef _X1TWIN
560         if(!pce->cart_inserted()) {
561 #endif
562                 if(pseudo_sub_cpu) {
563                         psub->key_up(code);
564                 } else {
565                         //kbd->key_up(code);
566                 }
567 #ifdef _X1TWIN
568         }
569 #endif
570 }
571
572 // ----------------------------------------------------------------------------
573 // user interface
574 // ----------------------------------------------------------------------------
575
576 void VM::open_disk(int drv, const _TCHAR* file_path, int bank)
577 {
578         fdc->open_disk(drv, file_path, bank);
579 }
580
581 void VM::close_disk(int drv)
582 {
583         fdc->close_disk(drv);
584 }
585
586 bool VM::disk_inserted(int drv)
587 {
588         return fdc->disk_inserted(drv);
589 }
590  
591 void VM::set_disk_protected(int drv, bool value)
592 {
593         fdc->set_disk_protected(drv, value);
594 }
595
596 bool VM::get_disk_protected(int drv)
597 {
598         return fdc->get_disk_protected(drv);
599 }
600  
601 void VM::play_tape(const _TCHAR* file_path)
602 {
603         bool value = drec->play_tape(file_path);
604         if(pseudo_sub_cpu) {
605                 psub->close_tape();
606                 psub->play_tape(value);
607         } else {
608                 sub->close_tape();
609                 sub->play_tape(value);
610         }
611 }
612
613 void VM::rec_tape(const _TCHAR* file_path)
614 {
615         bool value = drec->rec_tape(file_path);
616         if(pseudo_sub_cpu) {
617                 psub->close_tape();
618                 psub->rec_tape(value);
619         } else {
620                 sub->close_tape();
621                 sub->rec_tape(value);
622         }
623 }
624
625 void VM::close_tape()
626 {
627         drec->close_tape();
628         if(pseudo_sub_cpu) {
629                 psub->close_tape();
630         } else {
631                 sub->close_tape();
632         }
633 }
634
635 bool VM::tape_inserted()
636 {
637         return drec->tape_inserted();
638 }
639
640 #if defined(USE_TAPE_PTR)
641 int VM::get_tape_ptr(void)
642 {
643         return drec->get_tape_ptr();
644 }
645 #endif
646  
647 void VM::push_play()
648 {
649         drec->set_ff_rew(0);
650         drec->set_remote(true);
651 }
652
653 bool VM::get_tape_play(void)
654 {
655         return drec->get_tape_play();
656 }
657
658 void VM::push_stop()
659 {
660         drec->set_remote(false);
661 }
662
663 void VM::push_fast_forward()
664 {
665         drec->set_ff_rew(1);
666         drec->set_remote(true);
667 }
668
669 void VM::push_fast_rewind()
670 {
671         drec->set_ff_rew(-1);
672         drec->set_remote(true);
673 }
674
675 void VM::push_apss_forward()
676 {
677         drec->do_apss(1);
678 }
679
680 void VM::push_apss_rewind()
681 {
682         drec->do_apss(-1);
683 }
684
685 bool VM::now_skip()
686 {
687 #ifdef _X1TWIN
688         if(pce->cart_inserted()) {
689                 return pceevent->now_skip();
690         }
691 #endif
692         return event->now_skip();
693 }
694
695 #ifdef _X1TWIN
696 void VM::open_cart(int drv, const _TCHAR* file_path)
697 {
698         if(drv == 0) {
699                 pce->open_cart(file_path);
700                 pce->reset();
701                 pcecpu->reset();
702         }
703 }
704
705 void VM::close_cart(int drv)
706 {
707         if(drv == 0) {
708                 pce->close_cart();
709                 pce->reset();
710                 pcecpu->reset();
711         }
712 }
713
714 bool VM::cart_inserted(int drv)
715 {
716         if(drv == 0) {
717                 return pce->cart_inserted();
718         } else {
719                 return false;
720         }
721 }
722 #endif
723
724 void VM::update_config()
725 {
726         for(DEVICE* device = first_device; device; device = device->next_device) {
727                 device->update_config();
728         }
729 #ifdef _X1TURBO_FEATURE
730         update_dipswitch();
731 #endif
732 }
733
734 #ifdef _X1TURBO_FEATURE
735 void VM::update_dipswitch()
736 {
737         // bit0         0=High 1=Standard
738         // bit2         0=5"2D 1=5"2HD
739         io->set_iovalue_single_r(0x1ff0, (config.monitor_type & 1) | ((config.drive_type & 1) << 2));
740 }
741 #endif
742
743 #define STATE_VERSION   4
744
745 void VM::save_state(FILEIO* state_fio)
746 {
747         state_fio->FputUint32(STATE_VERSION);
748         
749         for(DEVICE* device = first_device; device; device = device->next_device) {
750                 device->save_state(state_fio);
751         }
752         state_fio->FputBool(pseudo_sub_cpu);
753         state_fio->FputInt32(sound_device_type);
754 }
755
756 bool VM::load_state(FILEIO* state_fio)
757 {
758         if(state_fio->FgetUint32() != STATE_VERSION) {
759                 return false;
760         }
761         for(DEVICE* device = first_device; device; device = device->next_device) {
762                 if(!device->load_state(state_fio)) {
763                         return false;
764                 }
765         }
766         pseudo_sub_cpu = state_fio->FgetBool();
767         sound_device_type = state_fio->FgetInt32();
768         
769 #ifdef _X1TURBO_FEATURE
770         // post process
771         update_dipswitch();
772 #endif
773         return true;
774 }
775