OSDN Git Service

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