OSDN Git Service

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