OSDN Git Service

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