OSDN Git Service

[VM][PC8801] Re-Fix slowdown (eat too many CPUs).
[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 "../pc80s31k.h"
27 #include "../upd765a.h"
28
29 #ifdef USE_DEBUGGER
30 #include "../debugger.h"
31 #endif
32
33 #ifdef SUPPORT_PC88_PCG8100
34 #include "../i8253.h"
35 #endif
36
37 #include "pc88.h"
38
39 // ----------------------------------------------------------------------------
40 // initialize
41 // ----------------------------------------------------------------------------
42
43 VM::VM(EMU* parent_emu) : emu(parent_emu)
44 {
45         // check configs
46         boot_mode = config.boot_mode;
47         
48         // create devices
49         first_device = last_device = NULL;
50         dummy = new DEVICE(this, emu);  // must be 1st device
51         
52         pc88event = new EVENT(this, emu);
53 //      pc88event->set_frames_per_sec(60);
54 //      pc88event->set_lines_per_frame(260);
55         
56         pc88 = new PC88(this, emu);
57 //      pc88->set_context_event_manager(pc88event);
58         pc88sio = new I8251(this, emu);
59 //      pc88sio->set_context_event_manager(pc88event);
60         pc88pio = new I8255(this, emu);
61 //      pc88pio->set_context_event_manager(pc88event);
62         pc88pcm = new PCM1BIT(this, emu);
63 //      pc88pcm->set_context_event_manager(pc88event);
64         pc88rtc = new UPD1990A(this, emu);
65 //      pc88rtc->set_context_event_manager(pc88event);
66         // config.sound_device_type
67         //      0: 44h:OPNA A4h:None            PC-8801FH/MH or later
68         //      1: 44h:OPN  A4h:None            PC-8801mkIISR/TR/MR/FR
69         //      2: 44h:OPN  A4h:OPNA            PC-8801mkIISR/TR/MR/FR + PC-8801-23
70         pc88opn = new YM2203(this, emu);
71 //      pc88opn->set_context_event_manager(pc88event);
72 #ifdef USE_SOUND_DEVICE_TYPE
73 #ifdef SUPPORT_PC88_OPNA
74         pc88opn->is_ym2608 = (config.sound_device_type == 0);
75 #endif
76 #ifdef SUPPORT_PC88_SB2
77         if(config.sound_device_type == 2) {
78                 pc88sb2 = new YM2203(this, emu);
79 #ifdef SUPPORT_PC88_OPNA
80                 pc88sb2->is_ym2608 = true;
81 #endif
82 //              pc88sb2->set_context_event_manager(pc88event);
83         } else {
84                 pc88sb2 = NULL;
85         }
86 #endif
87 #endif
88         if(config.printer_device_type == 0) {
89                 pc88prn = new PRNFILE(this, emu);
90 //              pc88prn->set_context_event_manager(pc88event);
91 //      } else if(config.printer_device_type == 1) {
92 //              pc88prn = new PCPR201(this, emu);
93 //              pc88prn->set_context_event_manager(pc88event);
94         } else {
95                 pc88prn = dummy;
96         }
97
98         dummycpu = new DEVICE(this, emu);
99         pc88cpu = new Z80(this, emu);
100 //      pc88cpu->set_context_event_manager(pc88event);
101         
102         pc88sub = new PC80S31K(this, emu);
103 //      pc88sub->set_context_event_manager(pc88event);
104         pc88pio_sub = new I8255(this, emu);
105 //      pc88pio_sub->set_context_event_manager(pc88event);
106         pc88fdc_sub = new UPD765A(this, emu);
107 //      pc88fdc_sub->set_context_event_manager(pc88event);
108         pc88cpu_sub = new Z80(this, emu);
109 //      pc88cpu_sub->set_context_event_manager(pc88event);
110         
111 #ifdef SUPPORT_PC88_PCG8100
112         pc88pit = new I8253(this, emu);
113 //      pc88pit->set_context_event_manager(pc88event);
114         pc88pcm0 = new PCM1BIT(this, emu);
115 //      pc88pcm->set_context_event_manager(pc88event);
116         pc88pcm1 = new PCM1BIT(this, emu);
117 //      pc88pcm->set_context_event_manager(pc88event);
118         pc88pcm2 = new PCM1BIT(this, emu);
119 //      pc88pcm->set_context_event_manager(pc88event);
120 #endif
121         
122         pc88event->set_context_cpu(dummycpu, 3993624 / 4);
123 #ifdef SUPPORT_PC88_HIGH_CLOCK
124         pc88event->set_context_cpu(pc88cpu, (config.cpu_type != 0) ? 3993624 : 7987248);
125 #else
126         pc88event->set_context_cpu(pc88cpu, 3993624);
127 #endif
128         pc88event->set_context_cpu(pc88cpu_sub, 3993624);
129         pc88event->set_context_sound(pc88opn);
130 #ifdef SUPPORT_PC88_SB2
131         if(pc88sb2 != NULL) {
132                 pc88event->set_context_sound(pc88sb2);
133         }
134 #endif
135         pc88event->set_context_sound(pc88pcm);
136 #ifdef SUPPORT_PC88_PCG8100
137         pc88event->set_context_sound(pc88pcm0);
138         pc88event->set_context_sound(pc88pcm1);
139         pc88event->set_context_sound(pc88pcm2);
140 #endif
141         
142         pc88->set_context_cpu(pc88cpu);
143         pc88->set_context_opn(pc88opn);
144 #ifdef SUPPORT_PC88_SB2
145         pc88->set_context_sb2(pc88sb2);
146 #endif
147         pc88->set_context_pcm(pc88pcm);
148         pc88->set_context_pio(pc88pio);
149         pc88->set_context_prn(pc88prn);
150         pc88->set_context_rtc(pc88rtc);
151         pc88->set_context_sio(pc88sio);
152 #ifdef SUPPORT_PC88_PCG8100
153         pc88->set_context_pcg_pit(pc88pit);
154         pc88->set_context_pcg_pcm0(pc88pcm0);
155         pc88->set_context_pcg_pcm1(pc88pcm1);
156         pc88->set_context_pcg_pcm2(pc88pcm2);
157 #endif
158         pc88cpu->set_context_mem(pc88);
159         pc88cpu->set_context_io(pc88);
160         pc88cpu->set_context_intr(pc88);
161 #ifdef USE_DEBUGGER
162         pc88cpu->set_context_debugger(new DEBUGGER(this, emu));
163 #endif
164         pc88opn->set_context_irq(pc88, SIG_PC88_SOUND_IRQ, 1);
165 #ifdef SUPPORT_PC88_SB2
166         if(pc88sb2 != NULL) {
167                 pc88sb2->set_context_irq(pc88, SIG_PC88_SB2_IRQ, 1);
168         }
169 #endif
170         pc88sio->set_context_rxrdy(pc88, SIG_PC88_USART_IRQ, 1);
171         pc88sio->set_context_out(pc88, SIG_PC88_USART_OUT);
172         
173         pc88sub->set_context_cpu(pc88cpu_sub);
174         pc88sub->set_context_fdc(pc88fdc_sub);
175         pc88sub->set_context_pio(pc88pio_sub);
176         pc88pio->set_context_port_a(pc88pio_sub, SIG_I8255_PORT_B, 0xff, 0);
177         pc88pio->set_context_port_b(pc88pio_sub, SIG_I8255_PORT_A, 0xff, 0);
178         pc88pio->set_context_port_c(pc88pio_sub, SIG_I8255_PORT_C, 0x0f, 4);
179         pc88pio->set_context_port_c(pc88pio_sub, SIG_I8255_PORT_C, 0xf0, -4);
180         pc88pio->clear_ports_by_cmdreg = true;
181         pc88pio_sub->set_context_port_a(pc88pio, SIG_I8255_PORT_B, 0xff, 0);
182         pc88pio_sub->set_context_port_b(pc88pio, SIG_I8255_PORT_A, 0xff, 0);
183         pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0x0f, 4);
184         pc88pio_sub->set_context_port_c(pc88pio, SIG_I8255_PORT_C, 0xf0, -4);
185         pc88pio_sub->clear_ports_by_cmdreg = true;
186         pc88fdc_sub->set_context_irq(pc88cpu_sub, SIG_CPU_IRQ, 1);
187         pc88cpu_sub->set_context_mem(pc88sub);
188         pc88cpu_sub->set_context_io(pc88sub);
189         pc88cpu_sub->set_context_intr(pc88sub);
190 #ifdef USE_DEBUGGER
191         pc88cpu_sub->set_context_debugger(new DEBUGGER(this, emu));
192 #endif
193         
194 #ifdef SUPPORT_PC88_PCG8100
195         pc88pit->set_context_ch0(pc88pcm0, SIG_PCM1BIT_SIGNAL, 1);
196         pc88pit->set_context_ch1(pc88pcm1, SIG_PCM1BIT_SIGNAL, 1);
197         pc88pit->set_context_ch2(pc88pcm2, SIG_PCM1BIT_SIGNAL, 1);
198         pc88pit->set_constant_clock(0, 3993624);
199         pc88pit->set_constant_clock(1, 3993624);
200         pc88pit->set_constant_clock(2, 3993624);
201 #endif
202         
203         // initialize all devices
204         for(DEVICE* device = first_device; device; device = device->next_device) {
205                 device->initialize();
206         }
207 }
208
209 VM::~VM()
210 {
211         // delete all devices
212         for(DEVICE* device = first_device; device;) {
213                 DEVICE *next_device = device->next_device;
214                 device->release();
215                 delete device;
216                 device = next_device;
217         }
218 }
219
220 DEVICE* VM::get_device(int id)
221 {
222         for(DEVICE* device = first_device; device; device = device->next_device) {
223                 if(device->this_device_id == id) {
224                         return device;
225                 }
226         }
227         return NULL;
228 }
229
230 // ----------------------------------------------------------------------------
231 // drive virtual machine
232 // ----------------------------------------------------------------------------
233
234 void VM::reset()
235 {
236         // reset all devices
237         for(DEVICE* device = first_device; device; device = device->next_device) {
238                 device->reset();
239         }
240         for(DEVICE* device = first_device; device; device = device->next_device) {
241                 device->reset();
242         }
243         
244         // initial device settings
245         pc88opn->set_reg(0x29, 3); // for Misty Blue
246         pc88pio->write_signal(SIG_I8255_PORT_C, 0, 0xff);
247         pc88pio_sub->write_signal(SIG_I8255_PORT_C, 0, 0xff);
248 }
249
250 void VM::run()
251 {
252         pc88event->drive();
253 }
254
255 double VM::get_frame_rate()
256 {
257         return pc88event->get_frame_rate();
258 }
259
260 // ----------------------------------------------------------------------------
261 // debugger
262 // ----------------------------------------------------------------------------
263
264 #ifdef USE_DEBUGGER
265 DEVICE *VM::get_cpu(int index)
266 {
267         if(index == 0) {
268                 return pc88cpu;
269         } else if(index == 1) {
270                 return pc88cpu_sub;
271         }
272         return NULL;
273 }
274 #endif
275
276 // ----------------------------------------------------------------------------
277 // draw screen
278 // ----------------------------------------------------------------------------
279
280 void VM::draw_screen()
281 {
282         pc88->draw_screen();
283 }
284
285 uint32_t VM::get_access_lamp_status()
286 {
287         return pc88fdc_sub->read_signal(0);
288 }
289
290 // ----------------------------------------------------------------------------
291 // soud manager
292 // ----------------------------------------------------------------------------
293
294 void VM::initialize_sound(int rate, int samples)
295 {
296         // init sound manager
297         pc88event->initialize_sound(rate, samples);
298         
299         // init sound gen
300 #ifdef SUPPORT_PC88_OPNA
301         if(pc88opn->is_ym2608) {
302                 pc88opn->initialize_sound(rate, 7987248, samples, 0, 0);
303         } else
304 #endif
305         pc88opn->initialize_sound(rate, 3993624, samples, 0, 0);
306 #ifdef SUPPORT_PC88_SB2
307         if(pc88sb2 != NULL) {
308 #ifdef SUPPORT_PC88_OPNA
309                 if(pc88sb2->is_ym2608) {
310                         pc88sb2->initialize_sound(rate, 7987248, samples, 0, 0);
311                 } else
312 #endif
313                 pc88sb2->initialize_sound(rate, 3993624, samples, 0, 0);
314         }
315 #endif
316         pc88pcm->initialize_sound(rate, 8000);
317 #ifdef SUPPORT_PC88_PCG8100
318         pc88pcm0->initialize_sound(rate, 8000);
319         pc88pcm1->initialize_sound(rate, 8000);
320         pc88pcm2->initialize_sound(rate, 8000);
321 #endif
322 }
323
324 uint16_t* VM::create_sound(int* extra_frames)
325 {
326         return pc88event->create_sound(extra_frames);
327 }
328
329 int VM::get_sound_buffer_ptr()
330 {
331         return pc88event->get_sound_buffer_ptr();
332 }
333
334 #ifdef USE_SOUND_VOLUME
335 void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
336 {
337         if(ch-- == 0) {
338                 pc88opn->set_volume(0, decibel_l, decibel_r);
339         } else if(ch-- == 0) {
340                 pc88opn->set_volume(1, decibel_l, decibel_r);
341 #ifdef SUPPORT_PC88_OPNA
342         } else if(ch-- == 0) {
343                 pc88opn->set_volume(2, decibel_l, decibel_r);
344         } else if(ch-- == 0) {
345                 pc88opn->set_volume(3, decibel_l, decibel_r);
346 #endif
347 #ifdef SUPPORT_PC88_SB2
348         } else if(ch-- == 0) {
349                 if(pc88sb2 != NULL) {
350                         pc88sb2->set_volume(0, decibel_l, decibel_r);
351                 }
352         } else if(ch-- == 0) {
353                 if(pc88sb2 != NULL) {
354                         pc88sb2->set_volume(1, decibel_l, decibel_r);
355                 }
356         } else if(ch-- == 0) {
357                 if(pc88sb2 != NULL) {
358                         pc88sb2->set_volume(2, decibel_l, decibel_r);
359                 }
360         } else if(ch-- == 0) {
361                 if(pc88sb2 != NULL) {
362                         pc88sb2->set_volume(3, decibel_l, decibel_r);
363                 }
364 #endif
365 #ifdef SUPPORT_PC88_PCG8100
366         } else if(ch-- == 0) {
367                 pc88pcm0->set_volume(0, decibel_l, decibel_r);
368                 pc88pcm1->set_volume(0, decibel_l, decibel_r);
369                 pc88pcm2->set_volume(0, decibel_l, decibel_r);
370 #endif
371         } else if(ch-- == 0) {
372                 pc88pcm->set_volume(0, decibel_l, decibel_r);
373         }
374 }
375 #endif
376
377 // ----------------------------------------------------------------------------
378 // notify key
379 // ----------------------------------------------------------------------------
380
381 void VM::key_down(int code, bool repeat)
382 {
383         pc88->key_down(code, repeat);
384 }
385
386 void VM::key_up(int code)
387 {
388 }
389
390 // ----------------------------------------------------------------------------
391 // user interface
392 // ----------------------------------------------------------------------------
393
394 void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
395 {
396         pc88fdc_sub->open_disk(drv, file_path, bank);
397 }
398
399 void VM::close_floppy_disk(int drv)
400 {
401         pc88fdc_sub->close_disk(drv);
402 }
403
404 bool VM::is_floppy_disk_inserted(int drv)
405 {
406         return pc88fdc_sub->is_disk_inserted(drv);
407 }
408
409 void VM::is_floppy_disk_protected(int drv, bool value)
410 {
411         pc88fdc_sub->is_disk_protected(drv, value);
412 }
413
414 bool VM::is_floppy_disk_protected(int drv)
415 {
416         return pc88fdc_sub->is_disk_protected(drv);
417 }
418
419 void VM::play_tape(const _TCHAR* file_path)
420 {
421         pc88->play_tape(file_path);
422 }
423
424 void VM::rec_tape(const _TCHAR* file_path)
425 {
426         pc88->rec_tape(file_path);
427 }
428
429 void VM::close_tape()
430 {
431         pc88->close_tape();
432 }
433
434 bool VM::is_tape_inserted()
435 {
436         return pc88->is_tape_inserted();
437 }
438
439 bool VM::is_frame_skippable()
440 {
441 //      return event->is_frame_skippable();
442         return pc88->is_frame_skippable();
443 }
444
445 void VM::update_config()
446 {
447         if(boot_mode != config.boot_mode) {
448                 // boot mode is changed !!!
449                 boot_mode = config.boot_mode;
450                 reset();
451         } else {
452                 for(DEVICE* device = first_device; device; device = device->next_device) {
453                         device->update_config();
454                 }
455         }
456 }
457
458 #define STATE_VERSION   6
459
460 void VM::save_state(FILEIO* state_fio)
461 {
462         state_fio->FputUint32(STATE_VERSION);
463         
464         for(DEVICE* device = first_device; device; device = device->next_device) {
465                 device->save_state(state_fio);
466         }
467         state_fio->FputInt32(boot_mode);
468 }
469
470 bool VM::load_state(FILEIO* state_fio)
471 {
472         if(state_fio->FgetUint32() != STATE_VERSION) {
473                 return false;
474         }
475         for(DEVICE* device = first_device; device; device = device->next_device) {
476                 if(!device->load_state(state_fio)) {
477                         return false;
478                 }
479         }
480         boot_mode = state_fio->FgetInt32();
481         return true;
482 }
483