OSDN Git Service

55e651cbe380cdc1bf04233dad5956b71536377e
[csp-qt/common_source_project-fm7.git] / source / src / vm / pc8801 / pc8801.cpp
1 /*
2         NEC PC-8801MA Emulator 'ePC-8801MA'
3         NEC PC-8001mkIISR Emulator 'ePC-8001mkIISR'
4
5         Author : Takeda.Toshiya
6         Date   : 2012.02.16-
7
8         [ virtual machine ]
9 */
10
11 #include "pc8801.h"
12 #include "../../emu.h"
13 #include "../device.h"
14 #include "../event.h"
15
16 #include "../i8251.h"
17 #include "../i8255.h"
18 #include "../pcm1bit.h"
19 //#include "../pcpr201.h"
20 #include "../prnfile.h"
21 #include "../upd1990a.h"
22 #include "../ym2203.h"
23 #include "../z80.h"
24
25 #include "../disk.h"
26 #include "../noise.h"
27 #include "../pc80s31k.h"
28 #include "../upd765a.h"
29
30 #ifdef USE_DEBUGGER
31 #include "../debugger.h"
32 #endif
33
34 #ifdef SUPPORT_PC88_PCG8100
35 #include "../i8253.h"
36 #endif
37
38 #include "pc88.h"
39
40 // ----------------------------------------------------------------------------
41 // initialize
42 // ----------------------------------------------------------------------------
43
44 VM::VM(EMU* parent_emu) : VM_TEMPLATE(parent_emu)
45 {
46         // check configs
47         boot_mode = config.boot_mode;
48         
49         // create devices
50         first_device = last_device = NULL;
51         dummy = new DEVICE(this, emu);  // must be 1st device
52         
53         pc88event = new EVENT(this, emu);
54         pc88event->set_device_name(_T("Event Manager (PC-8801)"));
55 //      pc88event->set_frames_per_sec(60);
56 //      pc88event->set_lines_per_frame(260);
57         dummy->set_device_name(_T("1st Dummy"));
58         pc88 = new PC88(this, emu);
59
60   #if defined(_PC8001) || defined(_PC8001MK2) || defined(_PC8001SR)
61         pc88->set_device_name(_T("PC8001 MAIN"));
62   #else
63         pc88->set_device_name(_T("PC8801 MAIN"));
64   #endif
65 //      pc88->set_context_event_manager(pc88event);
66         pc88sio = new I8251(this, emu);
67 //      pc88sio->set_device_name(_T("8251 SIO (PC-8801)"));
68 //      pc88sio->set_context_event_manager(pc88event);
69         pc88pio = new I8255(this, emu);
70 //      pc88pio->set_device_name(_T("8255 PIO (PC-8801)"));
71 //      pc88pio->set_context_event_manager(pc88event);
72         pc88pcm = new PCM1BIT(this, emu);
73 //      pc88pcm->set_device_name(_T("1-Bit PCM Sound (PC-8801)"));
74 //      pc88pcm->set_context_event_manager(pc88event);
75         pc88rtc = new UPD1990A(this, emu);
76 //      pc88rtc->set_device_name(_T("uPD1990A RTC (PC-8801)"));
77 //      pc88rtc->set_context_event_manager(pc88event);
78         // config.sound_type
79         //      0: 44h:OPNA A4h:None            PC-8801FH/MH or later
80         //      1: 44h:OPN  A4h:None            PC-8801mkIISR/TR/FR/MR
81         //      2: 44h:OPN  A4h:OPNA            PC-8801mkIISR/TR/FR/MR + PC-8801-23
82         //      3: 44h:OPN  A4h:OPN             PC-8801mkIISR/TR/FR/MR + PC-8801-11
83         //      4: 44h:OPNA A4h:OPNA            PC-8801FH/MH or later  + PC-8801-24
84         //      5: 44h:OPNA A4h:OPN             PC-8801FH/MH or later  + PC-8801-11
85         pc88opn = new YM2203(this, emu);
86 //      pc88opn->set_context_event_manager(pc88event);
87 #ifdef USE_SOUND_TYPE
88 #ifdef SUPPORT_PC88_OPNA
89         if(config.sound_type == 0 || config.sound_type == 4 || config.sound_type == 5) {
90                 pc88opn->is_ym2608 = true;
91                 pc88opn->set_device_name(_T("YM2608 OPNA"));
92         } else {
93                 pc88opn->is_ym2608 = false;
94                 pc88opn->set_device_name(_T("YM2203 OPN"));
95         }
96 #endif
97 #ifdef SUPPORT_PC88_SB2
98         if(config.sound_type >= 2) {
99                 pc88sb2 = new YM2203(this, emu);
100 //              pc88sb2->set_context_event_manager(pc88event);
101 #ifdef SUPPORT_PC88_OPNA
102                 if(config.sound_type == 2 || config.sound_type == 4) {
103                         pc88sb2->is_ym2608 = true;
104                         pc88sb2->set_device_name(_T("YM2608 OPNA (SB2)"));
105                 } else {
106                         pc88sb2->is_ym2608 = false;
107 #endif
108                         pc88sb2->set_device_name(_T("YM2203 OPN (SB2)"));
109 #ifdef SUPPORT_PC88_OPNA
110                 }
111 #endif
112         } else {
113                 pc88sb2 = NULL;
114         }
115 #endif
116 #endif
117         if(config.printer_type == 0) {
118                 pc88prn = new PRNFILE(this, emu);
119 //              pc88prn->set_context_event_manager(pc88event);
120 //      } else if(config.printer_type == 1) {
121 //              pc88prn = new PCPR201(this, emu);
122 //              pc88prn->set_context_event_manager(pc88event);
123         } else {
124                 pc88prn = dummy;
125         }
126
127         dummycpu = new DEVICE(this, emu);
128         pc88cpu = new Z80(this, emu);
129 //      pc88cpu->set_context_event_manager(pc88event);
130         dummycpu->set_device_name(_T("DUMMY CPU"));
131         pc88cpu->set_device_name(_T("MAIN CPU(Z80)"));
132         
133         pc88sub = new PC80S31K(this, emu);
134         pc88sub->set_device_name(_T("PC-80S31K (Sub)"));
135 //      pc88sub->set_context_event_manager(pc88event);
136         pc88pio_sub = new I8255(this, emu);
137         pc88pio_sub->set_device_name(_T("8255 PIO (Sub)"));
138 //      pc88pio_sub->set_context_event_manager(pc88event);
139         pc88fdc_sub = new UPD765A(this, emu);
140         pc88fdc_sub->set_device_name(_T("uPD765A FDC (Sub)"));
141 //      pc88fdc_sub->set_context_event_manager(pc88event);
142         pc88noise_seek = new NOISE(this, emu);
143 //      pc88noise_seek->set_context_event_manager(pc88event);
144         pc88noise_head_down = new NOISE(this, emu);
145 //      pc88noise_head_down->set_context_event_manager(pc88event);
146         pc88noise_head_up = new NOISE(this, emu);
147 //      pc88noise_head_up->set_context_event_manager(pc88event);
148         pc88cpu_sub = new Z80(this, emu);
149         pc88cpu_sub->set_device_name(_T("Z80 CPU (Sub)"));
150 //      pc88cpu_sub->set_context_event_manager(pc88event);
151
152 #ifdef SUPPORT_PC88_PCG8100
153         pc88pit = new I8253(this, emu);
154 //      pc88pit->set_context_event_manager(pc88event);
155         pc88pcm0 = new PCM1BIT(this, emu);
156 //      pc88pcm0->set_context_event_manager(pc88event);
157         pc88pcm1 = new PCM1BIT(this, emu);
158 //      pc88pcm1->set_context_event_manager(pc88event);
159         pc88pcm2 = new PCM1BIT(this, emu);
160 //      pc88pcm2->set_context_event_manager(pc88event);
161         pc88pit->set_device_name(_T("i8253 PIT (PCG8100)"));
162         pc88pcm0->set_device_name(_T("SOUND #1 (PCG8100)"));
163         pc88pcm1->set_device_name(_T("SOUND #2 (PCG8100)"));
164         pc88pcm2->set_device_name(_T("SOUND #3 (PCG8100)"));
165 #endif
166         
167         pc88event->set_context_cpu(dummycpu, 3993624 / 4);
168 #ifdef SUPPORT_PC88_HIGH_CLOCK
169         pc88event->set_context_cpu(pc88cpu, (config.cpu_type == 1) ? 3993624 : 7987248);
170 #else
171         pc88event->set_context_cpu(pc88cpu, 3993624);
172 #endif
173         pc88event->set_context_cpu(pc88cpu_sub, 3993624);
174         pc88event->set_context_sound(pc88opn);
175 #ifdef SUPPORT_PC88_SB2
176         if(pc88sb2 != NULL) {
177                 pc88event->set_context_sound(pc88sb2);
178         }
179 #endif
180         pc88event->set_context_sound(pc88pcm);
181 #ifdef SUPPORT_PC88_PCG8100
182         pc88event->set_context_sound(pc88pcm0);
183         pc88event->set_context_sound(pc88pcm1);
184         pc88event->set_context_sound(pc88pcm2);
185 #endif
186         pc88event->set_context_sound(pc88noise_seek);
187         pc88event->set_context_sound(pc88noise_head_down);
188         pc88event->set_context_sound(pc88noise_head_up);
189         pc88->set_context_cpu(pc88cpu);
190         pc88->set_context_opn(pc88opn);
191 #ifdef SUPPORT_PC88_SB2
192         pc88->set_context_sb2(pc88sb2);
193 #endif
194         pc88->set_context_pcm(pc88pcm);
195         pc88->set_context_pio(pc88pio);
196         pc88->set_context_prn(pc88prn);
197         pc88->set_context_rtc(pc88rtc);
198         pc88->set_context_sio(pc88sio);
199 #ifdef SUPPORT_PC88_PCG8100
200         pc88->set_context_pcg_pit(pc88pit);
201         pc88->set_context_pcg_pcm0(pc88pcm0);
202         pc88->set_context_pcg_pcm1(pc88pcm1);
203         pc88->set_context_pcg_pcm2(pc88pcm2);
204 #endif
205         pc88cpu->set_context_mem(pc88);
206         pc88cpu->set_context_io(pc88);
207         pc88cpu->set_context_intr(pc88);
208 #ifdef USE_DEBUGGER
209         pc88cpu->set_context_debugger(new DEBUGGER(this, emu));
210 #endif
211         pc88opn->set_context_irq(pc88, SIG_PC88_SOUND_IRQ, 1);
212 #ifdef SUPPORT_PC88_SB2
213         if(pc88sb2 != NULL) {
214                 pc88sb2->set_context_irq(pc88, SIG_PC88_SB2_IRQ, 1);
215         }
216 #endif
217         pc88sio->set_context_rxrdy(pc88, SIG_PC88_USART_IRQ, 1);
218         pc88sio->set_context_out(pc88, SIG_PC88_USART_OUT);
219         
220         pc88sub->set_context_cpu(pc88cpu_sub);
221         pc88sub->set_context_fdc(pc88fdc_sub);
222         pc88sub->set_context_pio(pc88pio_sub);
223         pc88pio->set_context_port_a(pc88pio_sub, SIG_I8255_PORT_B, 0xff, 0);
224         pc88pio->set_context_port_b(pc88pio_sub, SIG_I8255_PORT_A, 0xff, 0);
225         pc88pio->set_context_port_c(pc88pio_sub, SIG_I8255_PORT_C, 0x0f, 4);
226         pc88pio->set_context_port_c(pc88pio_sub, SIG_I8255_PORT_C, 0xf0, -4);
227         pc88pio->clear_ports_by_cmdreg = true;
228         pc88pio_sub->set_context_port_a(pc88pio, SIG_I8255_PORT_B, 0xff, 0);
229         pc88pio_sub->set_context_port_b(pc88pio, SIG_I8255_PORT_A, 0xff, 0);
230         pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0x0f, 4);
231         pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0xf0, -4);
232         pc88pio_sub->clear_ports_by_cmdreg = true;
233         pc88fdc_sub->set_context_irq(pc88cpu_sub, SIG_CPU_IRQ, 1);
234         pc88fdc_sub->set_context_noise_seek(pc88noise_seek);
235         pc88fdc_sub->set_context_noise_head_down(pc88noise_head_down);
236         pc88fdc_sub->set_context_noise_head_up(pc88noise_head_up);
237         pc88cpu_sub->set_context_mem(pc88sub);
238         pc88cpu_sub->set_context_io(pc88sub);
239         pc88cpu_sub->set_context_intr(pc88sub);
240 #ifdef USE_DEBUGGER
241         pc88cpu_sub->set_context_debugger(new DEBUGGER(this, emu));
242 #endif
243         
244 #ifdef SUPPORT_PC88_PCG8100
245         pc88pit->set_context_ch0(pc88pcm0, SIG_PCM1BIT_SIGNAL, 1);
246         pc88pit->set_context_ch1(pc88pcm1, SIG_PCM1BIT_SIGNAL, 1);
247         pc88pit->set_context_ch2(pc88pcm2, SIG_PCM1BIT_SIGNAL, 1);
248         pc88pit->set_constant_clock(0, 3993624);
249         pc88pit->set_constant_clock(1, 3993624);
250         pc88pit->set_constant_clock(2, 3993624);
251 #endif
252         // initialize all devices
253 #if defined(__GIT_REPO_VERSION)
254         strncpy(_git_revision, __GIT_REPO_VERSION, sizeof(_git_revision) - 1);
255 #endif
256         for(DEVICE* device = first_device; device; device = device->next_device) {
257                 device->initialize();
258         }
259         decl_state();
260 }
261
262 VM::~VM()
263 {
264         // delete all devices
265         for(DEVICE* device = first_device; device;) {
266                 DEVICE *next_device = device->next_device;
267                 device->release();
268                 delete device;
269                 device = next_device;
270         }
271 }
272
273 DEVICE* VM::get_device(int id)
274 {
275         for(DEVICE* device = first_device; device; device = device->next_device) {
276                 if(device->this_device_id == id) {
277                         return device;
278                 }
279         }
280         return NULL;
281 }
282
283 // ----------------------------------------------------------------------------
284 // drive virtual machine
285 // ----------------------------------------------------------------------------
286
287 void VM::reset()
288 {
289         // reset all devices
290         for(DEVICE* device = first_device; device; device = device->next_device) {
291                 device->reset();
292         }
293         for(DEVICE* device = first_device; device; device = device->next_device) {
294                 device->reset();
295         }
296         
297         // initial device settings
298         pc88opn->set_reg(0x29, 3); // for Misty Blue
299         pc88pio->write_signal(SIG_I8255_PORT_C, 0, 0xff);
300         pc88pio_sub->write_signal(SIG_I8255_PORT_C, 0, 0xff);
301 }
302
303 void VM::run()
304 {
305         pc88event->drive();
306 }
307
308 double VM::get_frame_rate()
309 {
310         return pc88event->get_frame_rate();
311 }
312
313 // ----------------------------------------------------------------------------
314 // debugger
315 // ----------------------------------------------------------------------------
316
317 #ifdef USE_DEBUGGER
318 DEVICE *VM::get_cpu(int index)
319 {
320         if(index == 0) {
321                 return pc88cpu;
322         } else if(index == 1) {
323                 return pc88cpu_sub;
324         }
325         return NULL;
326 }
327 #endif
328
329 // ----------------------------------------------------------------------------
330 // draw screen
331 // ----------------------------------------------------------------------------
332
333 void VM::draw_screen()
334 {
335         pc88->draw_screen();
336 }
337
338 // ----------------------------------------------------------------------------
339 // soud manager
340 // ----------------------------------------------------------------------------
341
342 void VM::initialize_sound(int rate, int samples)
343 {
344         // init sound manager
345         pc88event->initialize_sound(rate, samples);
346         
347         // init sound gen
348 #ifdef SUPPORT_PC88_OPNA
349         if(pc88opn->is_ym2608) {
350                 pc88opn->initialize_sound(rate, 7987248, samples, 0, 0);
351         } else
352 #endif
353         pc88opn->initialize_sound(rate, 3993624, samples, 0, 0);
354 #ifdef SUPPORT_PC88_SB2
355         if(pc88sb2 != NULL) {
356 #ifdef SUPPORT_PC88_OPNA
357                 if(pc88sb2->is_ym2608) {
358                         pc88sb2->initialize_sound(rate, 7987248, samples, 0, 0);
359                 } else
360 #endif
361                 pc88sb2->initialize_sound(rate, 3993624, samples, 0, 0);
362         }
363 #endif
364         pc88pcm->initialize_sound(rate, 8000);
365 #ifdef SUPPORT_PC88_PCG8100
366         pc88pcm0->initialize_sound(rate, 8000);
367         pc88pcm1->initialize_sound(rate, 8000);
368         pc88pcm2->initialize_sound(rate, 8000);
369 #endif
370 }
371
372 uint16_t* VM::create_sound(int* extra_frames)
373 {
374         return pc88event->create_sound(extra_frames);
375 }
376
377 int VM::get_sound_buffer_ptr()
378 {
379         return pc88event->get_sound_buffer_ptr();
380 }
381
382 #ifdef USE_SOUND_VOLUME
383 void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
384 {
385         if(ch-- == 0) {
386                 pc88opn->set_volume(0, decibel_l, decibel_r);
387         } else if(ch-- == 0) {
388                 pc88opn->set_volume(1, decibel_l, decibel_r);
389 #ifdef SUPPORT_PC88_OPNA
390         } else if(ch-- == 0) {
391                 pc88opn->set_volume(2, decibel_l, decibel_r);
392         } else if(ch-- == 0) {
393                 pc88opn->set_volume(3, decibel_l, decibel_r);
394 #endif
395 #ifdef SUPPORT_PC88_SB2
396         } else if(ch-- == 0) {
397                 if(pc88sb2 != NULL) {
398                         pc88sb2->set_volume(0, decibel_l, decibel_r);
399                 }
400         } else if(ch-- == 0) {
401                 if(pc88sb2 != NULL) {
402                         pc88sb2->set_volume(1, decibel_l, decibel_r);
403                 }
404 #ifdef SUPPORT_PC88_OPNA
405         } else if(ch-- == 0) {
406                 if(pc88sb2 != NULL) {
407                         pc88sb2->set_volume(2, decibel_l, decibel_r);
408                 }
409         } else if(ch-- == 0) {
410                 if(pc88sb2 != NULL) {
411                         pc88sb2->set_volume(3, decibel_l, decibel_r);
412                 }
413 #endif
414 #endif
415 #ifdef SUPPORT_PC88_PCG8100
416         } else if(ch-- == 0) {
417                 pc88pcm0->set_volume(0, decibel_l, decibel_r);
418                 pc88pcm1->set_volume(0, decibel_l, decibel_r);
419                 pc88pcm2->set_volume(0, decibel_l, decibel_r);
420 #endif
421         } else if(ch-- == 0) {
422                 pc88pcm->set_volume(0, decibel_l, decibel_r);
423         } else if(ch-- == 0) {
424                 pc88noise_seek->set_volume(0, decibel_l, decibel_r);
425                 pc88noise_head_down->set_volume(0, decibel_l, decibel_r);
426                 pc88noise_head_up->set_volume(0, decibel_l, decibel_r);
427         }
428 }
429 #endif
430
431 // ----------------------------------------------------------------------------
432 // notify key
433 // ----------------------------------------------------------------------------
434
435 void VM::key_down(int code, bool repeat)
436 {
437         pc88->key_down(code, repeat);
438 }
439
440 void VM::key_up(int code)
441 {
442 }
443
444 bool VM::get_caps_locked()
445 {
446         return pc88->get_caps_locked();
447 }
448
449 bool VM::get_kana_locked()
450 {
451         return pc88->get_kana_locked();
452 }
453
454 // ----------------------------------------------------------------------------
455 // user interface
456 // ----------------------------------------------------------------------------
457
458 void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
459 {
460         pc88fdc_sub->open_disk(drv, file_path, bank);
461 }
462
463 void VM::close_floppy_disk(int drv)
464 {
465         pc88fdc_sub->close_disk(drv);
466 }
467
468 bool VM::is_floppy_disk_inserted(int drv)
469 {
470         return pc88fdc_sub->is_disk_inserted(drv);
471 }
472
473 void VM::is_floppy_disk_protected(int drv, bool value)
474 {
475         pc88fdc_sub->is_disk_protected(drv, value);
476 }
477
478 bool VM::is_floppy_disk_protected(int drv)
479 {
480         return pc88fdc_sub->is_disk_protected(drv);
481 }
482
483 uint32_t VM::is_floppy_disk_accessed()
484 {
485         return pc88fdc_sub->read_signal(0);
486 }
487
488 void VM::play_tape(int drv, const _TCHAR* file_path)
489 {
490         pc88->play_tape(file_path);
491 }
492
493 void VM::rec_tape(int drv, const _TCHAR* file_path)
494 {
495         pc88->rec_tape(file_path);
496 }
497
498 void VM::close_tape(int drv)
499 {
500         pc88->close_tape();
501 }
502
503 bool VM::is_tape_inserted(int drv)
504 {
505         return pc88->is_tape_inserted();
506 }
507
508 bool VM::is_frame_skippable()
509 {
510 //      return event->is_frame_skippable();
511         return pc88->is_frame_skippable();
512 }
513
514 void VM::update_config()
515 {
516         if(boot_mode != config.boot_mode) {
517                 // boot mode is changed !!!
518                 boot_mode = config.boot_mode;
519                 reset();
520         } else {
521                 for(DEVICE* device = first_device; device; device = device->next_device) {
522                         device->update_config();
523                 }
524         }
525 }
526
527 #define STATE_VERSION   8
528
529 #include "../../statesub.h"
530 #include "../../qt/gui/csp_logger.h"
531 extern CSP_Logger DLL_PREFIX_I *csp_logger;
532
533 void VM::decl_state(void)
534 {
535         state_entry = new csp_state_utils(STATE_VERSION, 0, (_TCHAR *)(_T("CSP::PC88_SERIES_HEAD")), csp_logger);
536         DECL_STATE_ENTRY_BOOL(boot_mode);
537         for(DEVICE* device = first_device; device; device = device->next_device) {
538                 device->decl_state();
539         }
540 }
541
542 void VM::save_state(FILEIO* state_fio)
543 {
544         if(state_entry != NULL) {
545                 state_entry->save_state(state_fio);
546         }
547         for(DEVICE* device = first_device; device; device = device->next_device) {
548                 device->save_state(state_fio);
549         }
550 }
551
552 bool VM::load_state(FILEIO* state_fio)
553 {
554         bool mb = false;
555         if(state_entry != NULL) {
556                 mb = state_entry->load_state(state_fio);
557         }
558         if(!mb) {
559                 emu->out_debug_log("INFO: HEADER DATA ERROR");
560                 return false;
561         }
562         for(DEVICE* device = first_device; device; device = device->next_device) {
563                 if(!device->load_state(state_fio)) {
564                         return false;
565                 }
566         }
567         return true;
568 }
569