OSDN Git Service

[VM] Fix crash when end of emulation at various(!) VMs.
[csp-qt/common_source_project-fm7.git] / source / src / vm / msx / msx.cpp
1 /*
2         ASCII MSX1 Emulator 'yaMSX1'
3         ASCII MSX2 Emulator 'yaMSX2'
4         Pioneer PX-7 Emulator 'ePX-7'
5
6         Author : tanam
7         Date   : 2013.06.29-
8
9         modified by Takeda.Toshiya
10         modified by umaiboux
11
12         [ virtual machine ]
13 */
14
15 #include "msx.h"
16 #include "../../emu.h"
17 #include "../device.h"
18 #include "../event.h"
19
20 #include "../datarec.h"
21 #include "../i8255.h"
22 #include "../io.h"
23 #if defined(_PX7)
24 #include "../ld700.h"
25 #endif
26 #include "../noise.h"
27 #include "../not.h"
28 //#include "../ym2203.h"
29 #include "../ay_3_891x.h"
30 #include "../pcm1bit.h"
31 #if defined(_MSX2)
32 #include "../rp5c01.h"
33 #include "../v99x8.h"
34 #else
35 #include "../tms9918a.h"
36 #endif
37 #include "../z80.h"
38
39 #ifdef USE_DEBUGGER
40 #include "../debugger.h"
41 #endif
42
43 #include "joystick.h"
44 #include "keyboard.h"
45 #include "./memory.h"
46 #if defined(_MSX2)
47 #include "rtcif.h"
48 #endif
49
50 using MSX::JOYSTICK;
51 using MSX::KEYBOARD;
52 using MSX::MEMORY;
53 #ifdef _MSX2
54 using MSX::RTCIF;
55 #endif
56 using MSX::SLOT0;
57 using MSX::SLOT1;
58 using MSX::SLOT2;
59 using MSX::SLOT3;
60
61 // ----------------------------------------------------------------------------
62 // initialize
63 // ----------------------------------------------------------------------------
64
65 VM::VM(EMU* parent_emu) : VM_TEMPLATE(parent_emu)
66 {
67         // create devices
68         first_device = last_device = NULL;
69         dummy = new DEVICE(this, emu);  // must be 1st device
70         event = new EVENT(this, emu);   // must be 2nd device
71         dummy->set_device_name(_T("1st Dummy"));
72         event->set_device_name(_T("EVENT"));
73         
74         drec = new DATAREC(this, emu);
75         drec->set_context_noise_play(new NOISE(this, emu));
76         drec->set_context_noise_stop(new NOISE(this, emu));
77         drec->set_context_noise_fast(new NOISE(this, emu));
78         pio = new I8255(this, emu);
79         io = new IO(this, emu);
80 #if defined(_PX7)
81         ldp = new LD700(this, emu);
82 #endif
83         not_remote = new NOT(this, emu);
84         psg = new AY_3_891X(this, emu);
85 #ifdef USE_DEBUGGER
86         psg->set_context_debugger(new DEBUGGER(this, emu));
87 #endif
88         pcm = new PCM1BIT(this, emu);
89
90         not_remote->set_device_name(_T("NOT Gate (REMOTE)"));
91 #if defined(_MSX2)
92         rtc = new RP5C01(this, emu);
93         vdp = new V99X8(this, emu);
94 #else
95         vdp = new TMS9918A(this, emu);
96 #endif
97         cpu = new Z80(this, emu);
98         joystick = new JOYSTICK(this, emu);
99         keyboard = new KEYBOARD(this, emu);
100         memory = new MSX_MEMORY(this, emu);
101 #if defined(_MSX2)
102         rtcif = new RTCIF(this, emu);
103 #endif
104         slot0 = new SLOT0(this, emu);   // #0: main memory
105         slot1 = new SLOT1(this, emu);   // #1: rom-cartridge or msx-dos
106         slot2 = new SLOT2(this, emu);   // #2: fdd-cartridge or p-basic
107         slot3 = new SLOT3(this, emu);   // #3: rom-cartridge or ram-cartridge
108
109         slot0->set_device_name(_T("SLOT#0 MAIN MEMORY"));
110         slot1->set_device_name(_T("SLOT#1 ROM CARTRIDGE"));
111         slot2->set_device_name(_T("SLOT#2 FDD CARTRIDGE"));
112         slot3->set_device_name(_T("SLOT#3 ROM CARTRIDGE"));
113
114         // set contexts
115         event->set_context_cpu(cpu);
116         event->set_context_sound(psg);
117         event->set_context_sound(pcm);
118         event->set_context_sound(drec);
119 #if defined(_PX7)
120         event->set_context_sound(ldp);
121 #endif
122         event->set_context_sound(drec->get_context_noise_play());
123         event->set_context_sound(drec->get_context_noise_stop());
124         event->set_context_sound(drec->get_context_noise_fast());
125         
126         drec->set_context_ear(psg, SIG_AY_3_891X_PORT_A, 0x80);
127         pio->set_context_port_a(memory, SIG_MEMORY_SEL, 0xff, 0);
128         pio->set_context_port_c(keyboard, SIG_KEYBOARD_COLUMN, 0x0f, 0);
129         pio->set_context_port_c(not_remote, SIG_NOT_INPUT, 0x10, 0);
130         not_remote->set_context_out(drec, SIG_DATAREC_REMOTE, 1);
131         pio->set_context_port_c(drec, SIG_DATAREC_MIC, 0x20, 0);
132         pio->set_context_port_c(pcm, SIG_PCM1BIT_SIGNAL, 0x80, 0);
133         psg->set_context_port_b(joystick, SIG_JOYSTICK_SEL, 0x40, 0);
134         vdp->set_context_irq(cpu, SIG_CPU_IRQ, 1);
135
136 #if defined(_PX7)
137         pio->set_context_port_c(slot2, SIG_SLOT2_MUTE, 0x10, 0);
138         ldp->set_context_exv(slot2, SIG_SLOT2_EXV, 1);
139         ldp->set_context_ack(slot2, SIG_SLOT2_ACK, 1);
140         ldp->set_context_sound(psg, SIG_AY_3_891X_PORT_A, 0x80);
141 #endif
142         joystick->set_context_psg(psg);
143 //      keyboard->set_context_cpu(cpu);
144         keyboard->set_context_pio(pio);
145         memory->set_context_slot(0, slot0);
146         memory->set_context_slot(1, slot1);
147         memory->set_context_slot(2, slot2);
148         memory->set_context_slot(3, slot3);
149 #if defined(_MSX2)
150         rtcif->set_context_rtc(rtc);
151 #endif
152 #if defined(_PX7)
153         slot2->set_context_cpu(cpu);
154         slot2->set_context_ldp(ldp);
155         slot2->set_context_vdp(vdp);
156 #endif
157         
158         // cpu bus
159         cpu->set_context_mem(memory);
160         cpu->set_context_io(io);
161         cpu->set_context_intr(dummy);
162 #if !defined(_PX7)
163         cpu->set_context_bios(memory);
164 #endif
165 #ifdef USE_DEBUGGER
166         cpu->set_context_debugger(new DEBUGGER(this, emu));
167 #endif
168         
169         // i/o bus
170 #ifdef _MSX2
171         io->set_iomap_range_rw(0xb4, 0xb5, rtcif);
172         io->set_iomap_range_rw(0x98, 0x9b, vdp);
173 #else
174         io->set_iomap_range_rw(0x98, 0x99, vdp);
175 #endif
176         io->set_iomap_range_rw(0xa8, 0xab, pio);
177         io->set_iomap_alias_w(0xa0, psg, 0);    // PSG ch
178         io->set_iomap_alias_w(0xa1, psg, 1);    // PSG data
179         io->set_iomap_alias_r(0xa2, psg, 1);    // PSG data
180         io->set_iomap_range_rw(0xfc, 0xff, memory);
181         
182         // initialize all devices
183 #if defined(__GIT_REPO_VERSION)
184         strncpy(_git_revision, __GIT_REPO_VERSION, sizeof(_git_revision) - 1);
185 #endif
186         for(DEVICE* device = first_device; device; device = device->next_device) {
187                 device->initialize();
188         }
189 }
190
191 VM::~VM()
192 {
193         // delete all devices
194         for(DEVICE* device = first_device; device;) {
195                 DEVICE *next_device = device->next_device;
196                 device->release();
197                 delete device;
198                 device = next_device;
199         }
200 }
201
202 DEVICE* VM::get_device(int id)
203 {
204         for(DEVICE* device = first_device; device; device = device->next_device) {
205                 if(device->this_device_id == id) {
206                         return device;
207                 }
208         }
209         return NULL;
210 }
211
212 // ----------------------------------------------------------------------------
213 // drive virtual machine
214 // ----------------------------------------------------------------------------
215
216 void VM::reset()
217 {
218         // reset all devices
219         for(DEVICE* device = first_device; device; device = device->next_device) {
220                 device->reset();
221         }
222 }
223
224 void VM::run()
225 {
226         event->drive();
227 }
228
229 // ----------------------------------------------------------------------------
230 // debugger
231 // ----------------------------------------------------------------------------
232
233 #ifdef USE_DEBUGGER
234 DEVICE *VM::get_cpu(int index)
235 {
236         if(index == 0) {
237                 return cpu;
238         }
239         return NULL;
240 }
241 #endif
242
243 // ----------------------------------------------------------------------------
244 // draw screen
245 // ----------------------------------------------------------------------------
246
247 void VM::draw_screen()
248 {
249         vdp->draw_screen();
250 }
251
252 // ----------------------------------------------------------------------------
253 // soud manager
254 // ----------------------------------------------------------------------------
255
256 void VM::initialize_sound(int rate, int samples)
257 {
258         // init sound manager
259         event->initialize_sound(rate, samples);
260         
261         // init sound gen
262         psg->initialize_sound(rate, 3579545, samples, 0, 0);
263         pcm->initialize_sound(rate, 8000);
264 #if defined(_PX7)
265         ldp->initialize_sound(rate, samples);
266 #endif
267 }
268
269 uint16_t* VM::create_sound(int* extra_frames)
270 {
271         return event->create_sound(extra_frames);
272 }
273
274 int VM::get_sound_buffer_ptr()
275 {
276         return event->get_sound_buffer_ptr();
277 }
278
279 #if defined(_PX7)
280 void VM::movie_sound_callback(uint8_t *buffer, long size)
281 {
282         ldp->movie_sound_callback(buffer, size);
283 }
284 #endif
285
286 #ifdef USE_SOUND_VOLUME
287 void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
288 {
289         if(ch-- == 0) {
290                 psg->set_volume(1, decibel_l, decibel_r);
291         } else if(ch-- == 0) {
292                 pcm->set_volume(0, decibel_l, decibel_r);
293         } else if(ch-- == 0) {
294                 drec->set_volume(0, decibel_l, decibel_r);
295 #if defined(_PX7)
296         } else if(ch-- == 0) {
297                 ldp->set_volume(0, decibel_l, decibel_r);
298 #endif
299         } else if(ch-- == 0) {
300                 drec->get_context_noise_play()->set_volume(0, decibel_l, decibel_r);
301                 drec->get_context_noise_stop()->set_volume(0, decibel_l, decibel_r);
302                 drec->get_context_noise_fast()->set_volume(0, decibel_l, decibel_r);
303         }
304 }
305 #endif
306
307 // ----------------------------------------------------------------------------
308 // user interface
309 // ----------------------------------------------------------------------------
310
311 void VM::open_cart(int drv, const _TCHAR* file_path)
312 {
313         if(drv == 0) {
314                 slot1->open_cart(file_path);
315         } else {
316                 slot3->open_cart(file_path);
317         }
318         reset();
319 }
320
321 void VM::close_cart(int drv)
322 {
323         if(drv == 0) {
324                 slot1->close_cart();
325         } else {
326                 slot3->close_cart();
327         }
328         reset();
329 }
330
331 bool VM::is_cart_inserted(int drv)
332 {
333         if(drv == 0) {
334                 return slot1->is_cart_inserted();
335         } else {
336                 return slot3->is_cart_inserted();
337         }
338 }
339
340 void VM::play_tape(int drv, const _TCHAR* file_path)
341 {
342         bool remote = drec->get_remote();
343         
344         if(drec->play_tape(file_path) && remote) {
345                 // if machine already sets remote on, start playing now
346                 push_play(drv);
347         }
348 }
349
350 void VM::rec_tape(int drv, const _TCHAR* file_path)
351 {
352         bool remote = drec->get_remote();
353         
354         if(drec->rec_tape(file_path) && remote) {
355                 // if machine already sets remote on, start recording now
356                 push_play(drv);
357         }
358 }
359
360 void VM::close_tape(int drv)
361 {
362         emu->lock_vm();
363         drec->close_tape();
364         emu->unlock_vm();
365         drec->set_remote(false);
366 }
367
368 bool VM::is_tape_inserted(int drv)
369 {
370         return drec->is_tape_inserted();
371 }
372
373 bool VM::is_tape_playing(int drv)
374 {
375         return drec->is_tape_playing();
376 }
377
378 bool VM::is_tape_recording(int drv)
379 {
380         return drec->is_tape_recording();
381 }
382
383 int VM::get_tape_position(int drv)
384 {
385         return drec->get_tape_position();
386 }
387
388 const _TCHAR* VM::get_tape_message(int drv)
389 {
390         return drec->get_message();
391 }
392
393 void VM::push_play(int drv)
394 {
395         drec->set_remote(false);
396         drec->set_ff_rew(0);
397         drec->set_remote(true);
398 }
399
400 void VM::push_stop(int drv)
401 {
402         drec->set_remote(false);
403 }
404
405 void VM::push_fast_forward(int drv)
406 {
407         drec->set_remote(false);
408         drec->set_ff_rew(1);
409         drec->set_remote(true);
410 }
411
412 void VM::push_fast_rewind(int drv)
413 {
414         drec->set_remote(false);
415         drec->set_ff_rew(-1);
416         drec->set_remote(true);
417 }
418
419 void VM::load_binary(int drv, const _TCHAR* file_path)
420 {
421         if(drv == 0) {
422                 pac2->open_rampac2(file_path);
423         }
424 }
425
426 #if defined(_PX7)
427 void VM::open_laser_disc(int drv, const _TCHAR* file_path)
428 {
429         ldp->open_disc(file_path);
430 }
431
432 void VM::close_laser_disc(int drv)
433 {
434         ldp->close_disc();
435 }
436
437 bool VM::is_laser_disc_inserted(int drv)
438 {
439         return ldp->is_disc_inserted();
440 }
441
442 uint32_t VM::is_laser_disc_accessed()
443 {
444         return lpd->read_signal(0);
445 }
446 #else
447 void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
448 {
449         memory->open_disk(drv, file_path, bank);
450 }
451
452 void VM::close_floppy_disk(int drv)
453 {
454         memory->close_disk(drv);
455 }
456
457 bool VM::is_floppy_disk_inserted(int drv)
458 {
459         return memory->is_disk_inserted(drv);
460 }
461
462 void VM::is_floppy_disk_protected(int drv, bool value)
463 {
464         memory->is_disk_protected(drv, value);
465 }
466
467 bool VM::is_floppy_disk_protected(int drv)
468 {
469         return memory->is_disk_protected(drv);
470 }
471
472 uint32_t VM::is_floppy_disk_accessed()
473 {
474         return memory->read_signal(0);
475 }
476 #endif
477
478 bool VM::is_frame_skippable()
479 {
480         return event->is_frame_skippable();
481 }
482
483 void VM::update_config()
484 {
485         for(DEVICE* device = first_device; device; device = device->next_device) {
486                 device->update_config();
487         }
488 }
489
490 #define STATE_VERSION   5
491
492 void VM::save_state(FILEIO* state_fio)
493 {
494         state_fio->FputUint32(STATE_VERSION);
495         
496         for(DEVICE* device = first_device; device; device = device->next_device) {
497                 device->save_state(state_fio);
498         }
499 }
500
501 bool VM::load_state(FILEIO* state_fio)
502 {
503         if(state_fio->FgetUint32() != STATE_VERSION) {
504                 return false;
505         }
506         for(DEVICE* device = first_device; device; device = device->next_device) {
507                 if(!device->load_state(state_fio)) {
508                         return false;
509                 }
510         }
511         return true;
512 }
513
514 bool VM::process_state(FILEIO* state_fio, bool loading)
515 {
516         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
517                 return false;
518         }
519         for(DEVICE* device = first_device; device; device = device->next_device) {
520                 // Note: typeid(foo).name is fixed by recent ABI.Not dec 6.
521                 // const char *name = typeid(*device).name();
522                 //       But, using get_device_name() instead of typeid(foo).name() 20181008 K.O
523                 const char *name = device->get_device_name();
524                 int len = strlen(name);
525                 
526                 if(!state_fio->StateCheckInt32(len)) {
527                         if(loading) {
528                                 printf("Class name len Error: DEVID=%d EXPECT=%s\n", device->this_device_id, name);
529                         }
530                         return false;
531                 }
532                 if(!state_fio->StateCheckBuffer(name, len, 1)) {
533                         if(loading) {
534                                 printf("Class name Error: DEVID=%d EXPECT=%s\n", device->this_device_id, name);
535                         }
536                         return false;
537                 }
538                 if(!device->process_state(state_fio, loading)) {
539                         if(loading) {
540                                 printf("Data loading Error: DEVID=%d\n", device->this_device_id);
541                         }
542                         return false;
543                 }
544         }
545         // Machine specified.
546         return true;
547 }