OSDN Git Service

29689b9b72b959c50f2727aeb30818fa964ee684
[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 "../mz1p17.h"
24 #include "../noise.h"
25 //#include "../pcpr201.h"
26 #include "../prnfile.h"
27 #include "../ym2151.h"
28 //#include "../ym2203.h"
29 #include "../ay_3_891x.h"
30 #include "../z80.h"
31 #include "../z80ctc.h"
32 #include "../z80sio.h"
33 #ifdef _X1TURBO_FEATURE
34 #include "../z80dma.h"
35 #endif
36
37 #ifdef USE_DEBUGGER
38 #include "../debugger.h"
39 #endif
40
41 #include "display.h"
42 #include "emm.h"
43 #include "floppy.h"
44 #include "iobus.h"
45 #include "joystick.h"
46 #include "memory.h"
47 #include "mouse.h"
48 #include "psub.h"
49
50 #include "../mcs48.h"
51 #include "../upd1990a.h"
52 #include "sub.h"
53 #include "keyboard.h"
54
55 #ifdef _X1TWIN
56 #include "../huc6280.h"
57 #include "../pcengine/pce.h"
58 #endif
59 #if defined(Q_OS_WIN)
60 DLL_PREFIX_I struct cur_time_s cur_time;
61 #endif
62
63 // ----------------------------------------------------------------------------
64 // initialize
65 // ----------------------------------------------------------------------------
66
67 VM::VM(EMU* parent_emu) : emu(parent_emu)
68 {
69         pseudo_sub_cpu = !(FILEIO::IsFileExisting(create_local_path(SUB_ROM_FILE_NAME)) && FILEIO::IsFileExisting(create_local_path(KBD_ROM_FILE_NAME)));
70         
71         sound_type = config.sound_type;
72         
73         // create devices
74         first_device = last_device = NULL;
75         dummy = new DEVICE(this, emu);  // must be 1st device
76         event = new EVENT(this, emu);   // must be 2nd device
77         dummy->set_device_name(_T("1st Dummy"));
78         
79         drec = new DATAREC(this, emu);
80         drec->set_context_noise_play(new NOISE(this, emu));
81         drec->set_context_noise_stop(new NOISE(this, emu));
82         drec->set_context_noise_fast(new NOISE(this, emu));
83         crtc = new HD46505(this, emu);
84         pio = new I8255(this, emu);
85         io = new IO(this, emu);
86         fdc = new MB8877(this, emu);
87         fdc->set_context_noise_seek(new NOISE(this, emu));
88         fdc->set_context_noise_head_down(new NOISE(this, emu));
89         fdc->set_context_noise_head_up(new NOISE(this, emu));
90 //      psg = new YM2203(this, emu);
91         psg = new AY_3_891X(this, emu);
92         cpu = new Z80(this, emu);
93         ctc = new Z80CTC(this, emu);
94         sio = new Z80SIO(this, emu);
95         if(sound_type >= 1) {
96                 opm1 = new YM2151(this, emu);
97                 opm1->set_device_name(_T("YM2151 OPM (CZ-8BS1 #1)"));
98                 ctc1 = new Z80CTC(this, emu);
99                 ctc1->set_device_name(_T("Z80 CTC (CZ-8BS1 #1)"));
100         }
101         if(sound_type == 2) {
102                 opm2 = new YM2151(this, emu);
103                 opm2->set_device_name(_T("YM2151 OPM (CZ-8BS1 #2)"));
104                 ctc2 = new Z80CTC(this, emu);
105                 ctc2->set_device_name(_T("Z80 CTC (CZ-8BS1 #2)"));
106         }
107         if(config.printer_type == 0) {
108                 printer = new PRNFILE(this, emu);
109         } else if(config.printer_type == 1) {
110                 printer = new MZ1P17(this, emu);
111 //      } else if(config.printer_type == 2) {
112 //              printer = new PCPR201(this, emu);
113         } else {
114                 printer = dummy;
115         }
116 #ifdef _X1TURBO_FEATURE
117         dma = new Z80DMA(this, emu);
118 #endif
119         
120         display = new DISPLAY(this, emu);
121         emm = new EMM(this, emu);
122         floppy = new FLOPPY(this, emu);
123         iobus = new IOBUS(this, emu);
124         joy = new JOYSTICK(this, emu);
125         memory = new MEMORY(this, emu);
126         mouse = new MOUSE(this, emu);
127         
128         if(pseudo_sub_cpu) {
129                 psub = new PSUB(this, emu);
130                 cpu_sub = NULL;
131                 cpu_kbd = NULL;
132         } else {
133                 // sub cpu
134                 cpu_sub = new MCS48(this, emu);
135                 cpu_sub->set_device_name(_T("MCS48 MCU (Sub)"));
136                 pio_sub = new I8255(this, emu);
137                 pio_sub->set_device_name(_T("i8255 PIO (Sub)"));
138                 rtc_sub = new UPD1990A(this, emu);
139                 rtc_sub->set_device_name(_T("uPD1990A RTC (Sub)"));
140                 sub = new SUB(this, emu);
141
142                 // keyboard
143                 cpu_kbd = new MCS48(this, emu);
144                 cpu_kbd->set_device_name(_T("MCS48 MCU (Keyboard)"));
145                 kbd = new KEYBOARD(this, emu);
146         }
147         
148         // set contexts
149         event->set_context_cpu(cpu);
150         if(!pseudo_sub_cpu) {
151                 event->set_context_cpu(cpu_sub, 6000000);
152                 event->set_context_cpu(cpu_kbd, 6000000);
153         }
154         if(sound_type >= 1) {
155                 event->set_context_sound(opm1);
156         }
157         if(sound_type == 2) {
158                 event->set_context_sound(opm2);
159         }
160         event->set_context_sound(psg);
161         event->set_context_sound(drec);
162         event->set_context_sound(fdc->get_context_noise_seek());
163         event->set_context_sound(fdc->get_context_noise_head_down());
164         event->set_context_sound(fdc->get_context_noise_head_up());
165         event->set_context_sound(drec->get_context_noise_play());
166         event->set_context_sound(drec->get_context_noise_stop());
167         event->set_context_sound(drec->get_context_noise_fast());
168         
169         drec->set_context_ear(pio, SIG_I8255_PORT_B, 0x02);
170         crtc->set_context_vblank(display, SIG_DISPLAY_VBLANK, 1);
171         crtc->set_context_disp(display, SIG_DISPLAY_DISP, 1);
172         crtc->set_context_vblank(pio, SIG_I8255_PORT_B, 0x80);
173         crtc->set_context_vsync(pio, SIG_I8255_PORT_B, 0x04);
174         pio->set_context_port_a(printer, SIG_PRINTER_DATA, 0xff, 0);
175         pio->set_context_port_c(drec, SIG_DATAREC_MIC, 0x01, 0);
176         pio->set_context_port_c(display, SIG_DISPLAY_COLUMN40, 0x40, 0);
177         pio->set_context_port_c(iobus, SIG_IOBUS_MODE, 0x60, 0);
178         pio->set_context_port_c(printer, SIG_PRINTER_STROBE, 0x80, 0);
179 #ifdef _X1TURBO_FEATURE
180         fdc->set_context_drq(dma, SIG_Z80DMA_READY, 1);
181 #endif
182         ctc->set_context_zc0(ctc, SIG_Z80CTC_TRIG_3, 1);
183         ctc->set_context_zc1(sio, SIG_Z80SIO_TX_CLK_CH0, 1);
184         ctc->set_context_zc1(sio, SIG_Z80SIO_RX_CLK_CH0, 1);
185         ctc->set_context_zc2(sio, SIG_Z80SIO_TX_CLK_CH1, 1);
186         ctc->set_context_zc2(sio, SIG_Z80SIO_RX_CLK_CH1, 1);
187         ctc->set_constant_clock(1, CPU_CLOCKS >> 1);
188         ctc->set_constant_clock(2, CPU_CLOCKS >> 1);
189         sio->set_context_rts(1, mouse, SIG_MOUSE_RTS, 1);
190 //      sio->set_tx_clock(0, 9600 * 16);        // 9600 baud for RS-232C
191 //      sio->set_rx_clock(0, 9600 * 16);        // clock is from Z-80CTC ch1 (2MHz/13)
192 //      sio->set_tx_clock(1, 4800 * 16);        // 4800 baud for mouse
193 //      sio->set_rx_clock(1, 4800 * 16);        // clock is from Z-80CTC ch2 (2MHz/26)
194         
195         if(sound_type >= 1) {
196                 ctc1->set_context_zc0(ctc1, SIG_Z80CTC_TRIG_3, 1);
197 //              ctc1->set_constant_clock(1, CPU_CLOCKS >> 1);
198 //              ctc1->set_constant_clock(2, CPU_CLOCKS >> 1);
199         }
200         if(sound_type == 2) {
201                 ctc2->set_context_zc0(ctc2, SIG_Z80CTC_TRIG_3, 1);
202 //              ctc2->set_constant_clock(1, CPU_CLOCKS >> 1);
203 //              ctc2->set_constant_clock(2, CPU_CLOCKS >> 1);
204         }
205         if(config.printer_type == 0) {
206                 PRNFILE *prnfile = (PRNFILE *)printer;
207                 prnfile->set_context_busy(pio, SIG_I8255_PORT_B, 0x08);
208         } else if(config.printer_type == 1) {
209                 MZ1P17 *mz1p17 = (MZ1P17 *)printer;
210                 mz1p17->mode = MZ1P17_MODE_X1;
211                 mz1p17->set_context_busy(pio, SIG_I8255_PORT_B, 0x08);
212 //      } else if(config.printer_type == 2) {
213 //              PCPR201 *pcpr201 = (PCPR201 *)printer;
214 //              pcpr201->set_context_busy(pio, SIG_I8255_PORT_B, 0x08);
215         }
216 #ifdef _X1TURBO_FEATURE
217         dma->set_context_memory(memory);
218         dma->set_context_io(iobus);
219 #endif
220         
221 #ifdef _X1TURBO_FEATURE
222         display->set_context_cpu(cpu);
223 #endif
224         display->set_context_crtc(crtc);
225         display->set_vram_ptr(iobus->get_vram());
226         display->set_regs_ptr(crtc->get_regs());
227         floppy->set_context_fdc(fdc);
228         iobus->set_context_cpu(cpu);
229         iobus->set_context_display(display);
230         iobus->set_context_io(io);
231         joy->set_context_psg(psg);
232 #ifdef _X1TURBO_FEATURE
233         memory->set_context_pio(pio);
234 #endif
235         mouse->set_context_sio(sio);
236         
237         if(pseudo_sub_cpu) {
238                 drec->set_context_remote(psub, SIG_PSUB_TAPE_REMOTE, 1);
239                 drec->set_context_end(psub, SIG_PSUB_TAPE_END, 1);
240                 psub->set_context_pio(pio);
241                 psub->set_context_drec(drec);
242         } else {
243                 // sub cpu
244                 cpu_sub->set_context_mem(new MCS48MEM(this, emu));
245                 cpu_sub->set_context_io(sub);
246 #ifdef USE_DEBUGGER
247                 cpu_sub->set_context_debugger(new DEBUGGER(this, emu));
248 #endif
249                 drec->set_context_end(sub, SIG_SUB_TAPE_END, 1);
250                 drec->set_context_apss(sub, SIG_SUB_TAPE_APSS, 1);
251                 pio_sub->set_context_port_c(sub, SIG_SUB_PIO_PORT_C, 0x80, 0);
252                 // pc1:break -> pb0 of 8255(main)
253                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x02, -1);
254                 // pc5:ibf -> pb6 of 8255(main)
255                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x20, 1);
256                 // pc7:obf -> pb5 of 8255(main)
257                 pio_sub->set_context_port_c(pio, SIG_I8255_PORT_B, 0x80, -2);
258                 // pc7:obf -> pb7 of 8255(sub)
259                 pio_sub->set_context_port_c(pio_sub, SIG_I8255_PORT_B, 0x80, 0);
260                 
261                 sub->set_context_pio(pio_sub);
262                 sub->set_context_rtc(rtc_sub);
263                 sub->set_context_drec(drec);
264                 
265                 // keyboard
266                 cpu_kbd->set_context_mem(new MCS48MEM(this, emu));
267                 cpu_kbd->set_context_io(kbd);
268 #ifdef USE_DEBUGGER
269                 cpu_kbd->set_context_debugger(new DEBUGGER(this, emu));
270 #endif
271                 kbd->set_context_cpu(cpu_sub);
272         }
273         
274         // cpu bus
275         cpu->set_context_mem(memory);
276         cpu->set_context_io(iobus);
277 #if defined(_X1TURBO_FEATURE) && defined(SINGLE_MODE_DMA)
278         cpu->set_context_dma(dma);
279 #endif
280 #ifdef USE_DEBUGGER
281         cpu->set_context_debugger(new DEBUGGER(this, emu));
282 #endif
283         
284         // z80 family daisy chain
285         DEVICE* parent_dev = NULL;
286         int level = 0;
287         
288         #define Z80_DAISY_CHAIN(dev) { \
289                 if(parent_dev == NULL) { \
290                         cpu->set_context_intr(dev); \
291                 } else { \
292                         parent_dev->set_context_child(dev); \
293                 } \
294                 dev->set_context_intr(cpu, level++); \
295                 parent_dev = dev; \
296         }
297 #ifndef _X1TURBO_FEATURE
298         Z80_DAISY_CHAIN(sio);   // CZ-8BM2
299         Z80_DAISY_CHAIN(ctc);
300 #endif
301         if(sound_type >= 1) {
302                 Z80_DAISY_CHAIN(ctc1);
303         }
304         if(sound_type == 2) {
305                 Z80_DAISY_CHAIN(ctc2);
306         }
307 #ifdef _X1TURBO_FEATURE
308         Z80_DAISY_CHAIN(sio);
309         Z80_DAISY_CHAIN(dma);
310         Z80_DAISY_CHAIN(ctc);
311 #endif
312         if(pseudo_sub_cpu) {
313                 Z80_DAISY_CHAIN(psub);
314         } else {
315                 Z80_DAISY_CHAIN(sub);
316         }
317         
318         // i/o bus
319         if(sound_type >= 1) {
320                 io->set_iomap_single_w(0x700, opm1);
321                 io->set_iovalue_single_r(0x700, 0x00);
322                 io->set_iomap_single_rw(0x701, opm1);
323 #ifdef _X1TURBOZ
324                 io->set_flipflop_single_rw(0x704, 0x00);
325 #else
326                 io->set_iomap_range_rw(0x704, 0x707, ctc1);
327 #endif
328         }
329         if(sound_type == 2) {
330                 io->set_iomap_single_w(0x708, opm2);
331                 io->set_iovalue_single_r(0x708, 0x00);
332                 io->set_iomap_single_rw(0x709, opm2);
333                 io->set_iomap_range_rw(0x70c, 0x70f, ctc2);
334         }
335 #ifdef _X1TURBO_FEATURE
336         io->set_iomap_single_rw(0xb00, memory);
337 #endif
338         io->set_iomap_range_rw(0xd00, 0xd03, emm);
339         io->set_iomap_range_r(0xe80, 0xe81, display);
340         io->set_iomap_range_w(0xe80, 0xe82, display);
341         io->set_iomap_range_rw(0xff8, 0xffb, fdc);
342         io->set_iomap_single_w(0xffc, floppy);
343 #ifdef _X1TURBO_FEATURE
344         io->set_iomap_range_r(0xffc, 0xfff, floppy);
345 #endif
346         io->set_iomap_range_rw(0x1000, 0x17ff, display);
347         for(int i = 0x1800; i <= 0x18ff; i += 0x10) {
348                 io->set_iomap_range_rw(i, i + 1, crtc);
349         }
350         if(pseudo_sub_cpu) {
351                 io->set_iomap_range_rw(0x1900, 0x19ff, psub);
352         } else {
353                 io->set_iomap_range_rw(0x1900, 0x19ff, sub);
354         }
355         for(int i = 0x1a00; i <= 0x1aff; i += 4) {
356                 io->set_iomap_range_rw(i, i + 3, pio);
357         }
358         for(int i = 0x1b00; i <= 0x1bff; i++) {
359                 io->set_iomap_alias_rw(i, psg, 1);
360         }
361         for(int i = 0x1c00; i <= 0x1cff; i++) {
362                 io->set_iomap_alias_w(i, psg, 0);
363         }
364         io->set_iomap_range_r(0x1e00, 0x1eff, memory);
365         io->set_iomap_range_w(0x1d00, 0x1eff, memory);
366 #ifdef _X1TURBO_FEATURE
367         io->set_iomap_range_rw(0x1f80, 0x1f8f, dma);
368         io->set_iomap_range_rw(0x1f90, 0x1f93, sio);
369         io->set_iomap_range_rw(0x1fa0, 0x1fa3, ctc);
370 #ifdef _X1TURBOZ
371         io->set_iomap_single_rw(0x1fb0, display);
372         io->set_iomap_range_rw(0x1fb9, 0x1fc5, display);
373         io->set_iomap_single_rw(0x1fd0, display);
374         io->set_iomap_single_rw(0x1fe0, display);
375 #else
376         io->set_iomap_single_w(0x1fd0, display);
377         io->set_iomap_single_w(0x1fe0, display);
378 #endif
379         // 0x1ff0: dipswitch
380 //      io->set_iovalue_single_r(0x1ff0, 0x00);
381         update_dipswitch();
382 #else
383         io->set_iomap_range_rw(0x1f98, 0x1f9b, sio);    // CZ-8BM2
384         io->set_iomap_range_rw(0x1fa8, 0x1fab, ctc);
385 #endif
386         io->set_iomap_range_rw(0x2000, 0x3fff, display);        // tvram
387         
388 #ifdef _X1TWIN
389         // init PC Engine
390         pceevent = new EVENT(this, emu);
391         pceevent->set_frames_per_sec(PCE_FRAMES_PER_SEC);
392         pceevent->set_lines_per_frame(PCE_LINES_PER_FRAME);
393         pceevent->set_device_name(_T("EVENT (PC-ENGINE)"));
394         pcecpu = new HUC6280(this, emu);
395         pcecpu->set_device_name(_T("HuC6820 CPU (PC-ENGINE)"));
396         pcecpu->set_context_event_manager(pceevent);
397         pce = new PCE(this, emu);
398         pce->set_device_name(_T("SUB SYSTEM (PC-ENGINE)"));
399         pce->set_context_event_manager(pceevent);
400
401         pceevent->set_context_cpu(pcecpu, PCE_CPU_CLOCKS);
402         pceevent->set_context_sound(pce);
403         
404         pcecpu->set_context_mem(pce);
405         pcecpu->set_context_io(pce);
406 #ifdef USE_DEBUGGER
407         pcecpu->set_context_debugger(new DEBUGGER(this, emu));
408 #endif
409         pce->set_context_cpu(pcecpu);
410 #endif
411         
412         // initialize all devices
413         for(DEVICE* device = first_device; device; device = device->next_device) {
414                 device->initialize();
415         }
416         if(!pseudo_sub_cpu) {
417                 // load rom images after cpustate is allocated
418                 cpu_sub->load_rom_image(create_local_path(SUB_ROM_FILE_NAME));
419                 cpu_kbd->load_rom_image(create_local_path(KBD_ROM_FILE_NAME));
420                 
421                 // patch to set the current year
422                 uint8_t *rom = cpu_sub->get_rom_ptr();
423                 sub->rom_crc32 = get_crc32(rom, 0x800); // 2KB
424                 if(rom[0x23] == 0xb9 && rom[0x24] == 0x35 && rom[0x25] == 0xb1) {
425                         //dll_cur_time_t cur_time;
426                         cur_time_t cur_time;
427                         get_host_time(&cur_time);
428                         rom[0x26] = TO_BCD(cur_time.year);
429                 }
430         }
431         for(int i = 0; i < MAX_DRIVE; i++) {
432 //#ifdef _X1TURBO_FEATURE
433 //              fdc->set_drive_type(i, DRIVE_TYPE_2DD);
434 //#else
435                 fdc->set_drive_type(i, DRIVE_TYPE_2D);
436 //#endif
437 //              fdc->set_drive_rpm(i, 300);
438 //              fdc->set_drive_mfm(i, true);
439         }
440 }
441
442 VM::~VM()
443 {
444         // delete all devices
445         for(DEVICE* device = first_device; device;) {
446                 DEVICE *next_device = device->next_device;
447                 device->release();
448                 delete device;
449                 device = next_device;
450         }
451 }
452
453 DEVICE* VM::get_device(int id)
454 {
455         for(DEVICE* device = first_device; device; device = device->next_device) {
456                 if(device->this_device_id == id) {
457                         return device;
458                 }
459         }
460         return NULL;
461 }
462
463 // ----------------------------------------------------------------------------
464 // drive virtual machine
465 // ----------------------------------------------------------------------------
466
467 void VM::reset()
468 {
469         // reset all devices
470         for(DEVICE* device = first_device; device; device = device->next_device) {
471                 device->reset();
472         }
473         pio->write_signal(SIG_I8255_PORT_B, 0x00, 0x08);        // busy = low
474         psg->set_reg(0x2e, 0);  // set prescaler
475 }
476
477 void VM::special_reset()
478 {
479         // nmi reset
480         cpu->write_signal(SIG_CPU_NMI, 1, 1);
481 }
482
483 void VM::run()
484 {
485         event->drive();
486 #ifdef _X1TWIN
487         if(pce->is_cart_inserted()) {
488                 pceevent->drive();
489         }
490 #endif
491 }
492
493 double VM::get_frame_rate()
494 {
495 #ifdef _X1TWIN
496         if(pce->is_cart_inserted()) {
497                 return pceevent->get_frame_rate();
498         }
499 #endif
500         return event->get_frame_rate();
501 }
502
503 // ----------------------------------------------------------------------------
504 // debugger
505 // ----------------------------------------------------------------------------
506
507 #ifdef USE_DEBUGGER
508 DEVICE *VM::get_cpu(int index)
509 {
510         if(index == 0) {
511                 return cpu;
512         } else if(index == 1) {
513                 return cpu_sub;
514         } else if(index == 2) {
515                 return cpu_kbd;
516 #ifdef _X1TWIN
517         } else if(index == 3 && pce->is_cart_inserted()) {
518                 return pcecpu;
519 #endif
520         }
521         return NULL;
522 }
523 #endif
524
525 // ----------------------------------------------------------------------------
526 // draw screen
527 // ----------------------------------------------------------------------------
528
529 void VM::draw_screen()
530 {
531         display->draw_screen();
532 #ifdef _X1TWIN
533         if(pce->is_cart_inserted()) {
534                 pce->draw_screen();
535         }
536 #endif
537 }
538
539 // ----------------------------------------------------------------------------
540 // soud manager
541 // ----------------------------------------------------------------------------
542
543 void VM::initialize_sound(int rate, int samples)
544 {
545         // init sound manager
546         event->initialize_sound(rate, samples);
547 #ifdef _X1TWIN
548         pceevent->initialize_sound(rate, samples);
549 #endif
550         
551         // init sound gen
552         if(sound_type >= 1) {
553                 opm1->initialize_sound(rate, 4000000, samples, 0);
554         }
555         if(sound_type == 2) {
556                 opm2->initialize_sound(rate, 4000000, samples, 0);
557         }
558         psg->initialize_sound(rate, 2000000, samples, 0, 0);
559 #ifdef _X1TWIN
560         pce->initialize_sound(rate);
561 #endif
562 }
563
564 uint16_t* VM::create_sound(int* extra_frames)
565 {
566 #ifdef _X1TWIN
567         if(pce->is_cart_inserted()) {
568                 uint16_t* buffer = pceevent->create_sound(extra_frames);
569                 for(int i = 0; i < *extra_frames; i++) {
570                         event->drive();
571                 }
572                 return buffer;
573         }
574 #endif
575         return event->create_sound(extra_frames);
576 }
577
578 int VM::get_sound_buffer_ptr()
579 {
580 #ifdef _X1TWIN
581         if(pce->is_cart_inserted()) {
582                 return pceevent->get_sound_buffer_ptr();
583         }
584 #endif
585         return event->get_sound_buffer_ptr();
586 }
587
588 #ifdef USE_SOUND_VOLUME
589 void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
590 {
591         if(ch == 0) {
592                 psg->set_volume(1, decibel_l, decibel_r);
593         } else if(ch == 1) {
594                 if(sound_type >= 1) {
595                         opm1->set_volume(0, decibel_l, decibel_r);
596                 }
597         } else if(ch == 2) {
598                 if(sound_type >= 2) {
599                         opm2->set_volume(0, decibel_l, decibel_r);
600                 }
601         } else if(ch == 3) {
602                 drec->set_volume(0, decibel_l, decibel_r);
603         } else if(ch == 4) {
604                 fdc->get_context_noise_seek()->set_volume(0, decibel_l, decibel_r);
605                 fdc->get_context_noise_head_down()->set_volume(0, decibel_l, decibel_r);
606                 fdc->get_context_noise_head_up()->set_volume(0, decibel_l, decibel_r);
607         } else if(ch == 5) {
608                 drec->get_context_noise_play()->set_volume(0, decibel_l, decibel_r);
609                 drec->get_context_noise_stop()->set_volume(0, decibel_l, decibel_r);
610                 drec->get_context_noise_fast()->set_volume(0, decibel_l, decibel_r);
611 #if defined(_X1TWIN)
612         } else if(ch == 6) {
613                 pce->set_volume(0, decibel_l, decibel_r);
614 #endif
615         }
616 }
617 #endif
618
619 // ----------------------------------------------------------------------------
620 // notify key
621 // ----------------------------------------------------------------------------
622
623 void VM::key_down(int code, bool repeat)
624 {
625 #ifdef _X1TWIN
626         if(!repeat && !pce->is_cart_inserted()) {
627 #else
628         if(!repeat) {
629 #endif
630                 if(pseudo_sub_cpu) {
631                         psub->key_down(code, false);
632                 } else {
633                         kbd->key_down(code, false);
634                 }
635         }
636 }
637
638 void VM::key_up(int code)
639 {
640 #ifdef _X1TWIN
641         if(!pce->is_cart_inserted()) {
642 #endif
643                 if(pseudo_sub_cpu) {
644                         psub->key_up(code);
645                 } else {
646                         //kbd->key_up(code);
647                 }
648 #ifdef _X1TWIN
649         }
650 #endif
651 }
652
653 bool VM::get_caps_locked()
654 {
655 #ifdef _X1TWIN
656         if(!pce->is_cart_inserted()) {
657 #endif
658                 if(pseudo_sub_cpu) {
659                         return psub->get_caps_locked();
660                 } else {
661                         return kbd->get_caps_locked();
662                 }
663 #ifdef _X1TWIN
664         }
665         return false;
666 #endif
667 }
668
669 bool VM::get_kana_locked()
670 {
671 #ifdef _X1TWIN
672         if(!pce->is_cart_inserted()) {
673 #endif
674                 if(pseudo_sub_cpu) {
675                         return psub->get_kana_locked();
676                 } else {
677                         return kbd->get_kana_locked();
678                 }
679 #ifdef _X1TWIN
680         }
681         return false;
682 #endif
683 }
684
685 // ----------------------------------------------------------------------------
686 // user interface
687 // ----------------------------------------------------------------------------
688
689 void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
690 {
691         fdc->open_disk(drv, file_path, bank);
692 }
693
694 void VM::close_floppy_disk(int drv)
695 {
696         fdc->close_disk(drv);
697 }
698
699 bool VM::is_floppy_disk_inserted(int drv)
700 {
701         return fdc->is_disk_inserted(drv);
702 }
703
704 void VM::is_floppy_disk_protected(int drv, bool value)
705 {
706         fdc->is_disk_protected(drv, value);
707 }
708
709 bool VM::is_floppy_disk_protected(int drv)
710 {
711         return fdc->is_disk_protected(drv);
712 }
713
714 uint32_t VM::is_floppy_disk_accessed()
715 {
716         return fdc->read_signal(0);
717 }
718
719 void VM::play_tape(int drv, const _TCHAR* file_path)
720 {
721         bool value = drec->play_tape(file_path);
722         if(pseudo_sub_cpu) {
723                 psub->close_tape();
724                 psub->play_tape(value);
725         } else {
726                 sub->close_tape();
727                 sub->play_tape(value);
728         }
729 }
730
731 void VM::rec_tape(int drv, const _TCHAR* file_path)
732 {
733         bool value = drec->rec_tape(file_path);
734         if(pseudo_sub_cpu) {
735                 psub->close_tape();
736                 psub->rec_tape(value);
737         } else {
738                 sub->close_tape();
739                 sub->rec_tape(value);
740         }
741 }
742
743 void VM::close_tape(int drv)
744 {
745         emu->lock_vm();
746         drec->close_tape();
747         emu->unlock_vm();
748         if(pseudo_sub_cpu) {
749                 psub->close_tape();
750         } else {
751                 sub->close_tape();
752         }
753 }
754
755 bool VM::is_tape_inserted(int drv)
756 {
757         return drec->is_tape_inserted();
758 }
759
760 bool VM::is_tape_playing(int drv)
761 {
762         return drec->is_tape_playing();
763 }
764
765 bool VM::is_tape_recording(int drv)
766 {
767         return drec->is_tape_recording();
768 }
769
770 int VM::get_tape_position(int drv)
771 {
772         return drec->get_tape_position();
773 }
774
775 const _TCHAR* VM::get_tape_message(int drv)
776 {
777         return drec->get_message();
778 }
779
780 void VM::push_play(int drv)
781 {
782         drec->set_ff_rew(0);
783         drec->set_remote(true);
784 }
785
786 void VM::push_stop(int drv)
787 {
788         drec->set_remote(false);
789 }
790
791 void VM::push_fast_forward(int drv)
792 {
793         drec->set_ff_rew(1);
794         drec->set_remote(true);
795 }
796
797 void VM::push_fast_rewind(int drv)
798 {
799         drec->set_ff_rew(-1);
800         drec->set_remote(true);
801 }
802
803 void VM::push_apss_forward(int drv)
804 {
805         drec->do_apss(1);
806 }
807
808 void VM::push_apss_rewind(int drv)
809 {
810         drec->do_apss(-1);
811 }
812
813 bool VM::is_frame_skippable()
814 {
815 #ifdef _X1TWIN
816         if(pce->is_cart_inserted()) {
817                 return pceevent->is_frame_skippable();
818         }
819 #endif
820         return event->is_frame_skippable();
821 }
822
823 #ifdef _X1TWIN
824 void VM::open_cart(int drv, const _TCHAR* file_path)
825 {
826         if(drv == 0) {
827                 pce->open_cart(file_path);
828                 pce->reset();
829                 pcecpu->reset();
830         }
831 }
832
833 void VM::close_cart(int drv)
834 {
835         if(drv == 0) {
836                 pce->close_cart();
837                 pce->reset();
838                 pcecpu->reset();
839         }
840 }
841
842 bool VM::is_cart_inserted(int drv)
843 {
844         if(drv == 0) {
845                 return pce->is_cart_inserted();
846         } else {
847                 return false;
848         }
849 }
850 #endif
851
852 void VM::update_config()
853 {
854         for(DEVICE* device = first_device; device; device = device->next_device) {
855                 device->update_config();
856         }
857 #ifdef _X1TURBO_FEATURE
858         update_dipswitch();
859 #endif
860 }
861
862 #ifdef _X1TURBO_FEATURE
863 void VM::update_dipswitch()
864 {
865         // bit0         0=High 1=Standard
866         // bit1-3       000=5"2D 001=5"2DD 010=5"2HD
867         io->set_iovalue_single_r(0x1ff0, (config.monitor_type & 1) | ((config.drive_type & 7) << 1));
868 }
869 #endif
870
871 #define STATE_VERSION   9
872
873 void VM::save_state(FILEIO* state_fio)
874 {
875         state_fio->FputUint32(STATE_VERSION);
876         
877         for(DEVICE* device = first_device; device; device = device->next_device) {
878                 const char *name = typeid(*device).name() + 6; // skip "class "
879                 
880                 state_fio->FputInt32(strlen(name));
881                 state_fio->Fwrite(name, strlen(name), 1);
882                 device->save_state(state_fio);
883         }
884         state_fio->FputBool(pseudo_sub_cpu);
885         state_fio->FputInt32(sound_type);
886 }
887
888 bool VM::load_state(FILEIO* state_fio)
889 {
890         if(state_fio->FgetUint32() != STATE_VERSION) {
891                 return false;
892         }
893         for(DEVICE* device = first_device; device; device = device->next_device) {
894                 const char *name = typeid(*device).name() + 6; // skip "class "
895                 
896                 if(!(state_fio->FgetInt32() == strlen(name) && state_fio->Fcompare(name, strlen(name)))) {
897                         return false;
898                 }
899                 if(!device->load_state(state_fio)) {
900                         return false;
901                 }
902         }
903         pseudo_sub_cpu = state_fio->FgetBool();
904         sound_type = state_fio->FgetInt32();
905         
906 #ifdef _X1TURBO_FEATURE
907         // post process
908         update_dipswitch();
909 #endif
910         return true;
911 }
912