OSDN Git Service

[VM][General] Merge Upstream 2017-12-15.
[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) : emu(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         
253         // initialize all devices
254         for(DEVICE* device = first_device; device; device = device->next_device) {
255                 device->initialize();
256         }
257 }
258
259 VM::~VM()
260 {
261         // delete all devices
262         for(DEVICE* device = first_device; device;) {
263                 DEVICE *next_device = device->next_device;
264                 device->release();
265                 delete device;
266                 device = next_device;
267         }
268 }
269
270 DEVICE* VM::get_device(int id)
271 {
272         for(DEVICE* device = first_device; device; device = device->next_device) {
273                 if(device->this_device_id == id) {
274                         return device;
275                 }
276         }
277         return NULL;
278 }
279
280 // ----------------------------------------------------------------------------
281 // drive virtual machine
282 // ----------------------------------------------------------------------------
283
284 void VM::reset()
285 {
286         // reset all devices
287         for(DEVICE* device = first_device; device; device = device->next_device) {
288                 device->reset();
289         }
290         for(DEVICE* device = first_device; device; device = device->next_device) {
291                 device->reset();
292         }
293         
294         // initial device settings
295         pc88opn->set_reg(0x29, 3); // for Misty Blue
296         pc88pio->write_signal(SIG_I8255_PORT_C, 0, 0xff);
297         pc88pio_sub->write_signal(SIG_I8255_PORT_C, 0, 0xff);
298 }
299
300 void VM::run()
301 {
302         pc88event->drive();
303 }
304
305 double VM::get_frame_rate()
306 {
307         return pc88event->get_frame_rate();
308 }
309
310 // ----------------------------------------------------------------------------
311 // debugger
312 // ----------------------------------------------------------------------------
313
314 #ifdef USE_DEBUGGER
315 DEVICE *VM::get_cpu(int index)
316 {
317         if(index == 0) {
318                 return pc88cpu;
319         } else if(index == 1) {
320                 return pc88cpu_sub;
321         }
322         return NULL;
323 }
324 #endif
325
326 // ----------------------------------------------------------------------------
327 // draw screen
328 // ----------------------------------------------------------------------------
329
330 void VM::draw_screen()
331 {
332         pc88->draw_screen();
333 }
334
335 // ----------------------------------------------------------------------------
336 // soud manager
337 // ----------------------------------------------------------------------------
338
339 void VM::initialize_sound(int rate, int samples)
340 {
341         // init sound manager
342         pc88event->initialize_sound(rate, samples);
343         
344         // init sound gen
345 #ifdef SUPPORT_PC88_OPNA
346         if(pc88opn->is_ym2608) {
347                 pc88opn->initialize_sound(rate, 7987248, samples, 0, 0);
348         } else
349 #endif
350         pc88opn->initialize_sound(rate, 3993624, samples, 0, 0);
351 #ifdef SUPPORT_PC88_SB2
352         if(pc88sb2 != NULL) {
353 #ifdef SUPPORT_PC88_OPNA
354                 if(pc88sb2->is_ym2608) {
355                         pc88sb2->initialize_sound(rate, 7987248, samples, 0, 0);
356                 } else
357 #endif
358                 pc88sb2->initialize_sound(rate, 3993624, samples, 0, 0);
359         }
360 #endif
361         pc88pcm->initialize_sound(rate, 8000);
362 #ifdef SUPPORT_PC88_PCG8100
363         pc88pcm0->initialize_sound(rate, 8000);
364         pc88pcm1->initialize_sound(rate, 8000);
365         pc88pcm2->initialize_sound(rate, 8000);
366 #endif
367 }
368
369 uint16_t* VM::create_sound(int* extra_frames)
370 {
371         return pc88event->create_sound(extra_frames);
372 }
373
374 int VM::get_sound_buffer_ptr()
375 {
376         return pc88event->get_sound_buffer_ptr();
377 }
378
379 #ifdef USE_SOUND_VOLUME
380 void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
381 {
382         if(ch-- == 0) {
383                 pc88opn->set_volume(0, decibel_l, decibel_r);
384         } else if(ch-- == 0) {
385                 pc88opn->set_volume(1, decibel_l, decibel_r);
386 #ifdef SUPPORT_PC88_OPNA
387         } else if(ch-- == 0) {
388                 pc88opn->set_volume(2, decibel_l, decibel_r);
389         } else if(ch-- == 0) {
390                 pc88opn->set_volume(3, decibel_l, decibel_r);
391 #endif
392 #ifdef SUPPORT_PC88_SB2
393         } else if(ch-- == 0) {
394                 if(pc88sb2 != NULL) {
395                         pc88sb2->set_volume(0, decibel_l, decibel_r);
396                 }
397         } else if(ch-- == 0) {
398                 if(pc88sb2 != NULL) {
399                         pc88sb2->set_volume(1, decibel_l, decibel_r);
400                 }
401 #ifdef SUPPORT_PC88_OPNA
402         } else if(ch-- == 0) {
403                 if(pc88sb2 != NULL) {
404                         pc88sb2->set_volume(2, decibel_l, decibel_r);
405                 }
406         } else if(ch-- == 0) {
407                 if(pc88sb2 != NULL) {
408                         pc88sb2->set_volume(3, decibel_l, decibel_r);
409                 }
410 #endif
411 #endif
412 #ifdef SUPPORT_PC88_PCG8100
413         } else if(ch-- == 0) {
414                 pc88pcm0->set_volume(0, decibel_l, decibel_r);
415                 pc88pcm1->set_volume(0, decibel_l, decibel_r);
416                 pc88pcm2->set_volume(0, decibel_l, decibel_r);
417 #endif
418         } else if(ch-- == 0) {
419                 pc88pcm->set_volume(0, decibel_l, decibel_r);
420         } else if(ch-- == 0) {
421                 pc88noise_seek->set_volume(0, decibel_l, decibel_r);
422                 pc88noise_head_down->set_volume(0, decibel_l, decibel_r);
423                 pc88noise_head_up->set_volume(0, decibel_l, decibel_r);
424         }
425 }
426 #endif
427
428 // ----------------------------------------------------------------------------
429 // notify key
430 // ----------------------------------------------------------------------------
431
432 void VM::key_down(int code, bool repeat)
433 {
434         pc88->key_down(code, repeat);
435 }
436
437 void VM::key_up(int code)
438 {
439 }
440
441 bool VM::get_caps_locked()
442 {
443         return pc88->get_caps_locked();
444 }
445
446 bool VM::get_kana_locked()
447 {
448         return pc88->get_kana_locked();
449 }
450
451 // ----------------------------------------------------------------------------
452 // user interface
453 // ----------------------------------------------------------------------------
454
455 void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
456 {
457         pc88fdc_sub->open_disk(drv, file_path, bank);
458 }
459
460 void VM::close_floppy_disk(int drv)
461 {
462         pc88fdc_sub->close_disk(drv);
463 }
464
465 bool VM::is_floppy_disk_inserted(int drv)
466 {
467         return pc88fdc_sub->is_disk_inserted(drv);
468 }
469
470 void VM::is_floppy_disk_protected(int drv, bool value)
471 {
472         pc88fdc_sub->is_disk_protected(drv, value);
473 }
474
475 bool VM::is_floppy_disk_protected(int drv)
476 {
477         return pc88fdc_sub->is_disk_protected(drv);
478 }
479
480 uint32_t VM::is_floppy_disk_accessed()
481 {
482         return pc88fdc_sub->read_signal(0);
483 }
484
485 void VM::play_tape(int drv, const _TCHAR* file_path)
486 {
487         pc88->play_tape(file_path);
488 }
489
490 void VM::rec_tape(int drv, const _TCHAR* file_path)
491 {
492         pc88->rec_tape(file_path);
493 }
494
495 void VM::close_tape(int drv)
496 {
497         pc88->close_tape();
498 }
499
500 bool VM::is_tape_inserted(int drv)
501 {
502         return pc88->is_tape_inserted();
503 }
504
505 bool VM::is_frame_skippable()
506 {
507 //      return event->is_frame_skippable();
508         return pc88->is_frame_skippable();
509 }
510
511 void VM::update_config()
512 {
513         if(boot_mode != config.boot_mode) {
514                 // boot mode is changed !!!
515                 boot_mode = config.boot_mode;
516                 reset();
517         } else {
518                 for(DEVICE* device = first_device; device; device = device->next_device) {
519                         device->update_config();
520                 }
521         }
522 }
523
524 #define STATE_VERSION   8
525
526 void VM::save_state(FILEIO* state_fio)
527 {
528         state_fio->FputUint32(STATE_VERSION);
529         
530         for(DEVICE* device = first_device; device; device = device->next_device) {
531                 const char *name = typeid(*device).name() + 6; // skip "class "
532                 
533                 state_fio->FputInt32(strlen(name));
534                 state_fio->Fwrite(name, strlen(name), 1);
535                 device->save_state(state_fio);
536         }
537         state_fio->FputInt32(boot_mode);
538 }
539
540 bool VM::load_state(FILEIO* state_fio)
541 {
542         if(state_fio->FgetUint32() != STATE_VERSION) {
543                 return false;
544         }
545         for(DEVICE* device = first_device; device; device = device->next_device) {
546                 const char *name = typeid(*device).name() + 6; // skip "class "
547                 
548                 if(!(state_fio->FgetInt32() == strlen(name) && state_fio->Fcompare(name, strlen(name)))) {
549                         return false;
550                 }
551                 if(!device->load_state(state_fio)) {
552                         return false;
553                 }
554         }
555         boot_mode = state_fio->FgetInt32();
556         return true;
557 }
558