OSDN Git Service

30c5ca47ad4cfbe64a465f069d0663e900f05055
[csp-qt/common_source_project-fm7.git] / source / src / vm / pc8801 / pc88.cpp
1 /*
2         NEC PC-98DO Emulator 'ePC-98DO'
3         NEC PC-8801MA Emulator 'ePC-8801MA'
4         NEC PC-8001mkIISR Emulator 'ePC-8001mkIISR'
5
6         Author : Takeda.Toshiya
7         Date   : 2011.12.29-
8
9         [ PC-8801 ]
10 */
11
12 #include "pc88.h"
13 #include "../event.h"
14 #include "../i8251.h"
15 #include "../pcm1bit.h"
16 #include "../upd1990a.h"
17 #include "../ym2203.h"
18 #include "../z80.h"
19
20 #ifdef SUPPORT_PC88_CDROM
21 #include "../scsi_cdrom.h"
22 #include "../scsi_host.h"
23 #endif
24
25 #define DEVICE_JOYSTICK 0
26 #define DEVICE_MOUSE    1
27 #define DEVICE_JOYMOUSE 2       // not supported yet
28
29 #define EVENT_TIMER     0
30 #define EVENT_BUSREQ    1
31 #define EVENT_CMT_SEND  2
32 #define EVENT_CMT_DCD   3
33 #define EVENT_BEEP      4
34 #ifdef SUPPORT_PC88_CDROM
35 #define EVENT_FADE_IN   5
36 #define EVENT_FADE_OUT  6
37 #endif
38
39 #define IRQ_USART       0
40 #define IRQ_VRTC        1
41 #define IRQ_TIMER       2
42 #define IRQ_SOUND       4
43
44 namespace PC88DEV 
45 {
46    
47 #define Port30_40       !(port[0x30] & 0x01)
48 #define Port30_COLOR    !(port[0x30] & 0x02)
49 #define Port30_MTON     (port[0x30] & 0x08)
50 #define Port30_CMT      !(port[0x30] & 0x20)
51 #define Port30_RS232C   (port[0x30] & 0x20)
52
53 #define Port31_MMODE    (port[0x31] & 0x02)
54 #define Port31_RMODE    (port[0x31] & 0x04)
55 #define Port31_GRAPH    (port[0x31] & 0x08)
56 #define Port31_HCOLOR   (port[0x31] & 0x10)
57 #if !defined(_PC8001SR)
58 #define Port31_400LINE  !(port[0x31] & 0x11)
59 #else
60 #define Port31_400LINE  false
61 #endif
62
63 #define Port31_V1_320x200       (port[0x31] & 0x10)     // PC-8001 (V1)
64 #define Port31_V1_MONO          (port[0x31] & 0x04)     // PC-8001 (V1)
65
66 #define Port31_320x200  (port[0x31] & 0x04)     // PC-8001
67
68 #define Port32_EROMSL   (port[0x32] & 0x03)
69 #define Port32_TMODE    (port[0x32] & 0x10)
70 #if !defined(_PC8001SR)
71 #define Port32_PMODE    (port[0x32] & 0x20)
72 #else
73 #define Port32_PMODE    false
74 #endif
75 #define Port32_GVAM     (port[0x32] & 0x40)
76 #define Port32_SINTM    (port[0x32] & 0x80)
77
78 #define Port33_SINTM    (port[0x33] & 0x02)     // PC-8001
79 #define Port33_GVAM     (port[0x33] & 0x40)     // PC-8001
80 #define Port33_N80SR    (port[0x33] & 0x80)     // PC-8001
81
82 #define Port34_ALU      port[0x34]
83
84 #define Port35_PLN0     (port[0x35] & 0x01)
85 #define Port35_PLN1     (port[0x35] & 0x02)
86 #define Port35_PLN2     (port[0x35] & 0x04)
87 #define Port35_GDM      (port[0x35] & 0x30)
88 #define Port35_GAM      (port[0x35] & 0x80)
89
90 #define Port40_GHSM     (port[0x40] & 0x10)
91 #define Port40_JOP1     (port[0x40] & 0x40)
92
93 #define Port44_OPNCH    port[0x44]
94
95 #define Port53_TEXTDS   (port[0x53] & 0x01)
96 #define Port53_G0DS     (port[0x53] & 0x02)
97 #define Port53_G1DS     (port[0x53] & 0x04)
98 #define Port53_G2DS     (port[0x53] & 0x08)
99 #define Port53_G3DS     (port[0x53] & 0x10)     // PC-8001
100 #define Port53_G4DS     (port[0x53] & 0x20)     // PC-8001
101 #define Port53_G5DS     (port[0x53] & 0x40)     // PC-8001
102
103 #define Port70_TEXTWND  port[0x70]
104
105 #define Port71_EROM     port[0x71]
106
107 #ifdef SUPPORT_PC88_CDROM
108 #define Port99_CDREN    (port[0x99] & 0x10)
109 #endif
110
111 // XM8 version 1.20
112 #define PortA8_OPNCH    port[0xa8]
113 #define PortAA_S2INTM   (port[0xaa] & 0x80)
114
115 #define PortE2_RDEN     (port[0xe2] & 0x01)
116 #define PortE2_WREN     (port[0xe2] & 0x10)
117
118 #ifdef PC88_IODATA_EXRAM
119 #define PortE3_ERAMSL   port[0xe3]
120 #else
121 #define PortE3_ERAMSL   (port[0xe3] & 0x0f)
122 #endif
123
124 #define PortE8E9_KANJI1 (port[0xe8] | (port[0xe9] << 8))
125 #define PortECED_KANJI2 (port[0xec] | (port[0xed] << 8))
126
127 #define PortF0_DICROMSL (port[0xf0] & 0x1f)
128 #define PortF1_DICROM   !(port[0xf1] & 0x01)
129
130 #if defined(SUPPORT_PC88_VAB)
131 // X88000
132 #define PortB4_VAB_DISP ((port[0xb4] & 0x41) == 0x41)
133 #define PortE3_VAB_SEL  (((port[0xe3] >> 2) & 3) == PC88_VAB_PAGE)
134 #endif
135
136 #define SET_BANK(s, e, w, r) { \
137         int sb = (s) >> 12, eb = (e) >> 12; \
138         for(int i = sb; i <= eb; i++) { \
139                 if((w) == wdmy) { \
140                         wbank[i] = wdmy; \
141                 } else { \
142                         wbank[i] = (w) + 0x1000 * (i - sb); \
143                 } \
144                 if((r) == rdmy) { \
145                         rbank[i] = rdmy; \
146                 } else { \
147                         rbank[i] = (r) + 0x1000 * (i - sb); \
148                 } \
149         } \
150 }
151
152 #define SET_BANK_W(s, e, w) { \
153         int sb = (s) >> 12, eb = (e) >> 12; \
154         for(int i = sb; i <= eb; i++) { \
155                 if((w) == wdmy) { \
156                         wbank[i] = wdmy; \
157                 } else { \
158                         wbank[i] = (w) + 0x1000 * (i - sb); \
159                 } \
160         } \
161 }
162
163 #define SET_BANK_R(s, e, r) { \
164         int sb = (s) >> 12, eb = (e) >> 12; \
165         for(int i = sb; i <= eb; i++) { \
166                 if((r) == rdmy) { \
167                         rbank[i] = rdmy; \
168                 } else { \
169                         rbank[i] = (r) + 0x1000 * (i - sb); \
170                 } \
171         } \
172 }
173
174 static const int key_table[15][8] = {
175         { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67 },
176         { 0x68, 0x69, 0x6a, 0x6b, 0x92, 0x6c, 0x6e, 0x0d },     // 0x92 = VK_OEM_NEC_EQUAL
177         { 0xc0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 },
178         { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f },
179         { 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57 },
180         { 0x58, 0x59, 0x5a, 0xdb, 0xdc, 0xdd, 0xde, 0xbd },
181         { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
182         { 0x38, 0x39, 0xba, 0xbb, 0xbc, 0xbe, 0xbf, 0xe2 },
183 //      { 0x24, 0x26, 0x27, 0x2e, 0x12, 0x15, 0x10, 0x11 },
184         { 0x24, 0x26, 0x27, 0x08, 0x12, 0x15, 0x10, 0x11 },
185         { 0x13, 0x70, 0x71, 0x72, 0x73, 0x74, 0x20, 0x1b },
186         { 0x09, 0x28, 0x25, 0x23, 0x7b, 0x6d, 0x6f, 0x14 },
187         { 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
188         { 0x75, 0x76, 0x77, 0x78, 0x79, 0x08, 0x2d, 0x2e },
189         { 0x1c, 0x1d, 0x7a, 0x19, 0x00, 0x00, 0x00, 0x00 },
190         { 0x0d, 0x00, 0xa0, 0xa1, 0x00, 0x00, 0x00, 0x00 }
191 };
192
193 static const int key_conv_table[][3] = {
194         {0x2d, 0x2e, 1}, // INS -> SHIFT + DEL
195         {0x75, 0x70, 1}, // F6  -> SHIFT + F1
196         {0x76, 0x71, 1}, // F7  -> SHIFT + F2
197         {0x77, 0x72, 1}, // F8  -> SHIFT + F3
198         {0x78, 0x73, 1}, // F9  -> SHIFT + F4
199         {0x79, 0x74, 1}, // F10 -> SHIFT + F5
200 //      {0x08, 0x2e, 0}, // BS  -> DEL
201         {0x2e, 0x08, 0}, // DEL -> BS
202         {0x1c, 0x20, 0}, // \95Ï\8a·-> SPACE
203         {0x1d, 0x20, 0}, // \8c\88\92è-> SPACE
204 };
205
206 static const uint8_t intr_mask2_table[8] = {
207         (uint8_t)~7, (uint8_t)~3, (uint8_t)~5, (uint8_t)~1, (uint8_t)~6, (uint8_t)~2, (uint8_t)~4, (uint8_t)~0
208 };
209
210 void PC88::initialize()
211 {
212         memset(rdmy, 0xff, sizeof(rdmy));
213 //      memset(ram, 0, sizeof(ram));
214 #ifdef PC88_EXRAM_BANKS
215         memset(exram, 0, sizeof(exram));
216 #endif
217         memset(gvram, 0, sizeof(gvram));
218         memset(gvram_null, 0, sizeof(gvram_null));
219         memset(tvram, 0, sizeof(tvram));
220 #if defined(_PC8001SR)
221         memset(n80mk2rom, 0xff, sizeof(n80mk2rom));
222         memset(n80mk2srrom, 0xff, sizeof(n80mk2srrom));
223 #else
224         memset(n88rom, 0xff, sizeof(n88rom));
225         memset(n88exrom, 0xff, sizeof(n88exrom));
226         memset(n80rom, 0xff, sizeof(n80rom));
227 #endif
228         memset(kanji1, 0xff, sizeof(kanji1));
229         memset(kanji2, 0xff, sizeof(kanji2));
230 #ifdef SUPPORT_PC88_DICTIONARY
231         memset(dicrom, 0xff, sizeof(dicrom));
232 #endif
233 #ifdef SUPPORT_PC88_CDROM
234         memset(cdbios, 0xff, sizeof(cdbios));
235         cdbios_loaded = false;
236 #endif
237         
238         // load rom images
239         FILEIO* fio = new FILEIO();
240 #if defined(_PC8001SR)
241         if(fio->Fopen(create_local_path(_T("N80_2.ROM")), FILEIO_READ_BINARY)) {
242                 fio->Fread(n80mk2rom, 0x8000, 1);
243                 fio->Fclose();
244         }
245         if(fio->Fopen(create_local_path(_T("N80_3.ROM")), FILEIO_READ_BINARY)) {
246                 fio->Fread(n80mk2srrom, 0xa000, 1);
247                 fio->Fclose();
248         }
249 #else
250         if(fio->Fopen(create_local_path(_T("PC88.ROM")), FILEIO_READ_BINARY)) {
251                 fio->Fread(n88rom, 0x8000, 1);
252                 fio->Fread(n80rom + 0x6000, 0x2000, 1);
253                 fio->Fseek(0x2000, FILEIO_SEEK_CUR);
254                 fio->Fread(n88exrom, 0x8000, 1);
255                 fio->Fseek(0x2000, FILEIO_SEEK_CUR);
256                 fio->Fread(n80rom, 0x6000, 1);
257                 fio->Fclose();
258         }
259         if(fio->Fopen(create_local_path(_T("N88.ROM")), FILEIO_READ_BINARY)) {
260                 fio->Fread(n88rom, 0x8000, 1);
261                 fio->Fclose();
262         }
263         if(fio->Fopen(create_local_path(_T("N88_0.ROM")), FILEIO_READ_BINARY)) {
264                 fio->Fread(n88exrom + 0x0000, 0x2000, 1);
265                 fio->Fclose();
266         }
267         if(fio->Fopen(create_local_path(_T("N88_1.ROM")), FILEIO_READ_BINARY)) {
268                 fio->Fread(n88exrom + 0x2000, 0x2000, 1);
269                 fio->Fclose();
270         }
271         if(fio->Fopen(create_local_path(_T("N88_2.ROM")), FILEIO_READ_BINARY)) {
272                 fio->Fread(n88exrom + 0x4000, 0x2000, 1);
273                 fio->Fclose();
274         }
275         if(fio->Fopen(create_local_path(_T("N88_3.ROM")), FILEIO_READ_BINARY)) {
276                 fio->Fread(n88exrom + 0x6000, 0x2000, 1);
277                 fio->Fclose();
278         }
279         if(fio->Fopen(create_local_path(_T("N80.ROM")), FILEIO_READ_BINARY)) {
280                 fio->Fread(n80rom, 0x8000, 1);
281                 fio->Fclose();
282         }
283 #endif
284         if(fio->Fopen(create_local_path(_T("KANJI1.ROM")), FILEIO_READ_BINARY)) {
285                 fio->Fread(kanji1, 0x20000, 1);
286                 fio->Fclose();
287         }
288         if(fio->Fopen(create_local_path(_T("KANJI2.ROM")), FILEIO_READ_BINARY)) {
289                 fio->Fread(kanji2, 0x20000, 1);
290                 fio->Fclose();
291         }
292 #ifdef SUPPORT_PC88_DICTIONARY
293         if(fio->Fopen(create_local_path(_T("JISYO.ROM")), FILEIO_READ_BINARY)) {
294                 fio->Fread(dicrom, 0x80000, 1);
295                 fio->Fclose();
296         }
297 #endif
298 #ifdef SUPPORT_PC88_CDROM
299         if(config.boot_mode == MODE_PC88_V2) {
300                 if(fio->Fopen(create_local_path(_T("CDBIOS.ROM")), FILEIO_READ_BINARY)) {
301                         fio->Fread(cdbios, 0x10000, 1);
302                         fio->Fclose();
303                         cdbios_loaded = true;
304                 }
305         }
306 #endif
307         delete fio;
308         
309         // memory pattern
310         for(int i = 0, ofs = 0; i < 256; i++) {
311                 for(int j = 0; j < 16; j++) {
312                         static const uint8_t p0[256] = {
313                                 0,1,0,1,0,1,0,0,0,0,0,0,1,0,1,0, // 0000
314                                 0,1,0,1,0,1,0,0,0,0,1,0,1,0,1,0, // 1000
315                                 0,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0, // 2000
316                                 0,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0, // 3000
317                                 1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1, // 4000
318                                 1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,1, // 5000
319                                 1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1, // 6000
320                                 1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1, // 7000
321                                 1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1, // 8000
322                                 1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1, // 9000
323                                 1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,1, // a000
324                                 1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1, // b000
325                                 0,1,0,1,0,1,0,0,0,0,0,0,1,0,1,0, // c000
326                                 0,1,0,1,0,1,0,0,0,0,0,0,1,0,1,0, // d000
327                                 0,1,0,1,0,1,0,0,0,0,1,0,1,0,1,0, // e000
328                                 0,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0, // f000
329                         };
330                         static const uint8_t p1[16] = {
331                                 0x00,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0x00,
332                         };
333                         memset(ram + ofs, (p0[i] == 0) ? p1[j] : ~p1[j], 16);
334                         ofs += 16;
335                 }
336         }
337         
338         // create semi graphics pattern
339         for(int i = 0; i < 256; i++) {
340                 uint8_t *dest = sg_pattern + 8 * i;
341                 dest[0] = dest[1] = ((i & 1) ? 0xf0 : 0) | ((i & 0x10) ? 0x0f : 0);
342                 dest[2] = dest[3] = ((i & 2) ? 0xf0 : 0) | ((i & 0x20) ? 0x0f : 0);
343                 dest[4] = dest[5] = ((i & 4) ? 0xf0 : 0) | ((i & 0x40) ? 0x0f : 0);
344                 dest[6] = dest[7] = ((i & 8) ? 0xf0 : 0) | ((i & 0x80) ? 0x0f : 0);
345         }
346         
347 #ifdef SUPPORT_PC88_VAB
348         // X88000
349         for(uint32_t g = 0; g < 64; g++) {
350                 uint32_t gg = (255 * g) / 63;
351                 for(uint32_t r = 0; r < 32; r++) {
352                         uint32_t rr = (255 * r) / 31;
353                         for(uint32_t b = 0; b < 32; b++) {
354                                 uint32_t bb = (255 * b) / 31;
355                                 palette_vab_pc[b | (r << 5) | (g << 10)] = RGB_COLOR(rr, gg, bb);
356                         }
357                 }
358         }
359 #endif
360         
361 #ifdef SUPPORT_PC88_HIGH_CLOCK
362         cpu_clock_low = (config.cpu_type == 1);         // 4MHz
363         cpu_clock_high_fe2 = (config.cpu_type == 2);    // 8MHz (FE2/MC)
364 #else
365         cpu_clock_low = true;
366 #endif
367         
368 #ifdef SUPPORT_PC88_JOYSTICK
369         joystick_status = emu->get_joy_buffer();
370         mouse_status = emu->get_mouse_buffer();
371         mouse_strobe_clock_lim = (int)((cpu_clock_low ? 720 : 1440) * 1.25);
372 #endif
373         
374         // initialize cmt
375         cmt_fio = new FILEIO();
376         cmt_play = cmt_rec = false;
377         
378         register_frame_event(this);
379         register_vline_event(this);
380         register_event(this, EVENT_TIMER, 1000000.0 / 600.0, true, NULL);
381         register_event(this, EVENT_BEEP, 1000000.0 / 4800.0, true, NULL);
382         
383 #if !defined(_PC8001SR)
384         // hack to update config.scan_line at first
385         hireso = !(config.monitor_type == 0);
386 #endif
387 #ifdef SUPPORT_PC88_CDROM
388         cdda_register_id = -1;
389 #endif
390 }
391
392 void PC88::release()
393 {
394         release_tape();
395         delete cmt_fio;
396 }
397
398 void PC88::reset()
399 {
400 #if defined(_PC8001SR)
401         hireso = false;
402 #else
403         bool value = (config.monitor_type == 0);
404         if(hireso != value) {
405                 // update config.scan_line when config.monitor_type is changed
406                 //hireso = config.scan_line = value;
407                 hireso = value; // Revert 20181217 K.O
408         }
409 #endif
410         
411         // memory
412         memset(port, 0, sizeof(port));
413         port[0x31] = 0x01;
414         port[0x32] = 0x98;
415         for(int i = 0; i < 8; i++) {
416                 port[0x54 + i] = i;
417         }
418 //      port[0x70] = 0x80;      // XM8 version 1.10
419         port[0x71] = port[0xf1] = 0xff;
420 #if defined(SUPPORT_PC88_CDROM)
421         if (cdbios_loaded) {
422                 port[0x99]  = 0x10;
423         }
424 #endif
425         memset(alu_reg, 0, sizeof(alu_reg));
426         gvram_plane = gvram_sel = 0;
427         
428 #if defined(_PC8001SR)
429         if(config.boot_mode == MODE_PC80_V2) {
430                 SET_BANK(0x0000, 0x7fff, wdmy, n80mk2srrom);
431                 port[0x33] = 0x80;
432         } else {
433                 SET_BANK(0x0000, 0x7fff, wdmy, n80mk2rom);
434         }
435         SET_BANK(0x8000, 0xffff, ram + 0x8000, ram + 0x8000);
436 #else
437 //      SET_BANK(0x0000, 0x7fff, ram, n88rom);
438         SET_BANK(0x8000, 0xefff, ram + 0x8000, ram + 0x8000);
439         update_low_memmap();
440         update_tvram_memmap();  // XM8 version 1.10
441 #endif
442         
443         // misc
444         usart_dcd = false;
445         opn_busy = true;
446         
447         // memory wait
448         mem_wait_on = ((config.dipswitch & 1) != 0);
449         
450         m1_wait_clocks = get_m1_wait(false);
451         f000_m1_wait_clocks = get_m1_wait(true);
452         mem_wait_clocks_r = get_main_wait(true);
453         mem_wait_clocks_w = get_main_wait(false);
454         tvram_wait_clocks_r = get_tvram_wait(true);
455         tvram_wait_clocks_w = get_tvram_wait(false);
456         
457         // crtc
458         memset(&crtc, 0, sizeof(crtc));
459         crtc.reset(hireso);
460         update_timing();
461         
462         memset(palette, 0, sizeof(palette));
463         for(int i = 1; i < 8; i++) {
464                 palette[i].b = (i & 1) ? 7 : 0;
465                 palette[i].r = (i & 2) ? 7 : 0;
466                 palette[i].g = (i & 4) ? 7 : 0;
467         }
468         update_palette = true;
469         
470         // dma
471         memset(&dmac, 0, sizeof(dmac));
472         dmac.ch[0].io = dmac.ch[3].io = vm->dummy;
473 #ifdef SUPPORT_PC88_CDROM
474         if(cdbios_loaded) {
475                 dmac.ch[1].io = d_scsi_host;
476         } else
477 #endif
478         dmac.ch[1].io = vm->dummy;;
479         dmac.ch[2].io = dmac.mem = this;
480         dmac.ch[0].addr.b.l = 0x56;     // XM8 version 1.10
481         dmac.ch[0].addr.b.h = 0x56;
482         dmac.ch[1].addr.b.l = 0x7a;
483         dmac.ch[1].addr.b.h = 0x7a;
484         
485         // keyboard
486         key_kana = key_caps = 0;
487         
488         // mouse
489 #ifdef SUPPORT_PC88_JOYSTICK
490         mouse_strobe_clock = get_current_clock();
491         mouse_phase = -1;
492         mouse_dx = mouse_dy = mouse_lx = mouse_ly = 0;
493 #endif
494         
495         // interrupt
496         intr_req = intr_mask1 = intr_mask2 = 0;
497         intr_req_sound = false;
498 #ifdef SUPPORT_PC88_SB2
499         intr_req_sb2 = false;
500 #endif
501         
502         // fdd i/f
503         d_pio->write_io8(1, 0);
504         d_pio->write_io8(2, 0);
505         
506         // data recorder
507         close_tape();
508         cmt_play = cmt_rec = false;
509         cmt_register_id = -1;
510         
511         // beep/sing
512         beep_on = beep_signal = sing_signal = false;
513         
514 #ifdef SUPPORT_PC88_PCG8100
515         // pcg
516         memcpy(pcg_pattern, kanji1 + 0x1000, sizeof(pcg_pattern));
517         write_io8(1, 0);
518         write_io8(2, 0);
519         write_io8(3, 0);
520 #endif
521 #ifdef SUPPORT_PC88_CDROM
522         if(cdda_register_id != -1) {
523                 cancel_event(this, cdda_register_id);
524                 cdda_register_id = -1;
525         }
526         cdda_volume = 100.0;
527         d_scsi_cdrom->set_volume((int)cdda_volume);
528 #endif
529 #ifdef NIPPY_PATCH
530         // dirty patch for NIPPY
531         nippy_patch = false;
532 #endif
533 }
534
535 void PC88::write_data8w(uint32_t addr, uint32_t data, int* wait)
536 {
537         addr &= 0xffff;
538         *wait = mem_wait_clocks_w;
539         
540 #if !defined(_PC8001SR)
541         if((addr & 0xfc00) == 0x8000) {
542                 // text window
543                 if(!Port31_MMODE && !Port31_RMODE) {
544                         addr = (Port70_TEXTWND << 8) + (addr & 0x3ff);
545                 }
546                 ram[addr & 0xffff] = data;
547                 return;
548         } else if((addr & 0xc000) == 0xc000) {
549 #else
550         if((addr & 0xc000) == 0x8000) {
551 #endif
552                 switch(gvram_sel) {
553                 case 1:
554                         *wait = gvram_wait_clocks_w;
555                         gvram[(addr & 0x3fff) | 0x0000] = data;
556                         return;
557                 case 2:
558                         *wait = gvram_wait_clocks_w;
559                         gvram[(addr & 0x3fff) | 0x4000] = data;
560                         return;
561                 case 4:
562                         *wait = gvram_wait_clocks_w;
563                         gvram[(addr & 0x3fff) | 0x8000] = data;
564                         return;
565                 case 8:
566                         *wait = gvram_wait_clocks_w;
567                         addr &= 0x3fff;
568                         switch(Port35_GDM) {
569                         case 0x00:
570                                 for(int i = 0; i < 3; i++) {
571                                         switch((Port34_ALU >> i) & 0x11) {
572                                         case 0x00:      // reset
573                                                 gvram[addr | (0x4000 * i)] &= ~data;
574                                                 break;
575                                         case 0x01:      // set
576                                                 gvram[addr | (0x4000 * i)] |= data;
577                                                 break;
578                                         case 0x10:      // reverse
579                                                 gvram[addr | (0x4000 * i)] ^= data;
580                                                 break;
581                                         }
582                                 }
583                                 break;
584                         case 0x10:
585                                 gvram[addr | 0x0000] = alu_reg[0];
586                                 gvram[addr | 0x4000] = alu_reg[1];
587                                 gvram[addr | 0x8000] = alu_reg[2];
588                                 break;
589                         case 0x20:
590                                 gvram[addr | 0x0000] = alu_reg[1];
591                                 break;
592                         case 0x30:
593                                 gvram[addr | 0x4000] = alu_reg[0];
594                                 break;
595                         }
596                         return;
597                 }
598         }
599 #if !defined(_PC8001SR)
600         if((addr & 0xf000) == 0xf000) {
601                 // high speed ram
602                 *wait += tvram_wait_clocks_w;
603         }
604 #endif
605         wbank[addr >> 12][addr & 0xfff] = data;
606 }
607
608 uint32_t PC88::read_data8w(uint32_t addr, int* wait)
609 {
610         addr &= 0xffff;
611         *wait = mem_wait_clocks_r;
612         
613 #if !defined(_PC8001SR)
614         if((addr & 0xfc00) == 0x8000) {
615                 // text window
616                 if(!Port31_MMODE && !Port31_RMODE) {
617                         addr = (Port70_TEXTWND << 8) + (addr & 0x3ff);
618                 }
619                 return ram[addr & 0xffff];
620         } else if((addr & 0xc000) == 0xc000) {
621 #else
622         if((addr & 0xc000) == 0x8000) {
623 #endif
624                 uint8_t b, r, g;
625                 switch(gvram_sel) {
626                 case 1:
627                         *wait = gvram_wait_clocks_r;
628                         return gvram[(addr & 0x3fff) | 0x0000];
629                 case 2:
630                         *wait = gvram_wait_clocks_r;
631                         return gvram[(addr & 0x3fff) | 0x4000];
632                 case 4:
633                         *wait = gvram_wait_clocks_r;
634                         return gvram[(addr & 0x3fff) | 0x8000];
635                 case 8:
636                         *wait = gvram_wait_clocks_r;
637                         addr &= 0x3fff;
638                         alu_reg[0] = gvram[addr | 0x0000];
639                         alu_reg[1] = gvram[addr | 0x4000];
640                         alu_reg[2] = gvram[addr | 0x8000];
641                         b = alu_reg[0]; if(!Port35_PLN0) b ^= 0xff;
642                         r = alu_reg[1]; if(!Port35_PLN1) r ^= 0xff;
643                         g = alu_reg[2]; if(!Port35_PLN2) g ^= 0xff;
644                         return b & r & g;
645                 }
646 #ifdef SUPPORT_PC88_DICTIONARY
647                 if(PortF1_DICROM) {
648                         return dicrom[(addr & 0x3fff) | (0x4000 * PortF0_DICROMSL)];
649                 }
650 #endif
651         }
652 #if !defined(_PC8001SR)
653         if((addr & 0xf000) == 0xf000) {
654                 // high speed ram
655                 *wait += tvram_wait_clocks_r;
656         }
657 #endif
658         return rbank[addr >> 12][addr & 0xfff];
659 }
660
661 uint32_t PC88::fetch_op(uint32_t addr, int *wait)
662 {
663         uint32_t data = read_data8w(addr, wait);
664         if((addr & 0xf000) == 0xf000) {
665                 *wait += f000_m1_wait_clocks;
666         } else {
667                 *wait += m1_wait_clocks;
668         }
669         return data;
670 }
671
672 void PC88::write_io8(uint32_t addr, uint32_t data)
673 {
674         addr &= 0xff;
675 #ifdef _IO_DEBUG_LOG
676         this->out_debug_log(_T("%06x\tOUT8\t%02x,%02x\n"), d_cpu->get_pc(), addr, data);
677 #endif
678 #ifdef NIPPY_PATCH
679         // dirty patch for NIPPY
680         if(addr == 0x31 && data == 0x3f && d_cpu->get_pc() == 0xaa4f && nippy_patch) {
681                 data = 0x39; // select n88rom
682         }
683         // poke &haa4e, &h39
684 #endif
685         uint8_t mod = port[addr] ^ data;
686         port[addr] = data;
687         
688         switch(addr) {
689         case 0x00:
690 #ifdef SUPPORT_PC88_PCG8100
691                 pcg_data = data;
692 #endif
693                 // load tape image ??? (from QUASI88)
694                 if(cmt_play) {
695                         while(cmt_buffer[cmt_bufptr++] != 0x3a) {
696                                 if(!(cmt_bufptr <= cmt_bufcnt)) return;
697                         }
698                         int val, sum, ptr, len, wait;
699                         sum = (val = cmt_buffer[cmt_bufptr++]);
700                         ptr = val << 8;
701                         sum += (val = cmt_buffer[cmt_bufptr++]);
702                         ptr |= val;
703                         sum += (val = cmt_buffer[cmt_bufptr++]);
704                         if((sum & 0xff) != 0) return;
705                         
706                         while(1) {
707                                 while(cmt_buffer[cmt_bufptr++] != 0x3a) {
708                                         if(!(cmt_bufptr <= cmt_bufcnt)) return;
709                                 }
710                                 sum = (len = cmt_buffer[cmt_bufptr++]);
711                                 if(len == 0) break;
712                                 for(; len; len--) {
713                                         sum += (val = cmt_buffer[cmt_bufptr++]);
714                                         write_data8w(ptr++, val, &wait);
715                                 }
716                                 sum += cmt_buffer[cmt_bufptr++];
717                                 if((sum & 0xff) != 0) return;
718                         }
719                 }
720                 break;
721 #ifdef SUPPORT_PC88_PCG8100
722         case 0x01:
723                 pcg_addr = (pcg_addr & 0x300) | data;
724                 break;
725         case 0x02:
726                 if((pcg_ctrl & 0x10) && !(data & 0x10)) {
727                         if(pcg_ctrl & 0x20) {
728                                 pcg_pattern[0x400 | pcg_addr] = kanji1[0x1400 | pcg_addr];
729                         } else {
730                                 pcg_pattern[0x400 | pcg_addr] = pcg_data;
731                         }
732                 }
733                 pcg_addr = (pcg_addr & 0x0ff) | ((data & 3) << 8);
734                 pcg_ctrl = data;
735                 d_pcg_pcm0->write_signal(SIG_PCM1BIT_ON, data, 0x08);
736                 d_pcg_pcm1->write_signal(SIG_PCM1BIT_ON, data, 0x40);
737                 d_pcg_pcm2->write_signal(SIG_PCM1BIT_ON, data, 0x80);
738                 break;
739         case 0x0c:
740         case 0x0d:
741         case 0x0e:
742         case 0x0f:
743                 d_pcg_pit->write_io8(addr & 3, data);
744 #endif
745                 break;
746         case 0x10:
747                 d_prn->write_signal(SIG_PRINTER_DATA, data, 0xff);
748                 d_rtc->write_signal(SIG_UPD1990A_C0, data, 1);
749                 d_rtc->write_signal(SIG_UPD1990A_C1, data, 2);
750                 d_rtc->write_signal(SIG_UPD1990A_C2, data, 4);
751                 d_rtc->write_signal(SIG_UPD1990A_DIN, data, 8);
752                 break;
753         case 0x20:
754         case 0x21:
755                 d_sio->write_io8(addr, data);
756                 break;
757         case 0x30:
758                 if(mod & 0x08) {
759                         if(Port30_MTON) {
760                                 // start motor
761                                 if(cmt_play && cmt_bufptr < cmt_bufcnt) {
762 #if 0
763                                         // skip to the top of next block
764                                         int tmp = cmt_bufptr;
765                                         while(cmt_bufptr < cmt_bufcnt) {
766                                                 if(check_data_carrier()) {
767                                                         break;
768                                                 }
769                                                 cmt_bufptr++;
770                                         }
771                                         if(cmt_bufptr == cmt_bufcnt) {
772                                                 cmt_bufptr = tmp;
773                                         }
774 #endif
775                                         if(cmt_register_id != -1) {
776                                                 cancel_event(this, cmt_register_id);
777                                         }
778                                         register_event(this, EVENT_CMT_SEND, 5000, false, &cmt_register_id);
779                                 }
780                         } else {
781                                 // stop motor
782                                 if(cmt_register_id != -1) {
783                                         cancel_event(this, cmt_register_id);
784                                         cmt_register_id = -1;
785                                 }
786                                 usart_dcd = true; // for Jackie Chan no Spartan X
787                         }
788                 }
789                 break;
790 #if defined(_PC8001SR)
791         case 0x31:
792                 if(mod & 0x03) {
793                         update_n80_write();
794                         update_n80_read();
795                 }
796                 if(mod & 0xfc) {
797                         palette[8].b = (data & 0x20) ? 7 : 0;
798                         palette[8].r = (data & 0x40) ? 7 : 0;
799                         palette[8].g = (data & 0x80) ? 7 : 0;
800                         update_palette = true;
801                 }
802                 break;
803         case 0x33:
804                 if(mod & 0x80) {
805                         update_n80_read();
806                         update_gvram_wait();
807                         update_palette = true;
808                 }
809                 if(mod & 0xc0) {
810                         update_gvram_sel();
811                 }
812                 if(mod & 0x02) {
813                         if(intr_req_sound && !Port33_SINTM) {
814                                 request_intr(IRQ_SOUND, true);
815                         }
816                 }
817                 break;
818 #else
819         case 0x31:
820                 if(mod & 0x06) {
821                         update_low_memmap();
822                 }
823                 if(mod & 0x08) {
824                         update_gvram_wait();
825                         update_palette = true;
826                 }
827                 if(mod & 0x11) {
828                         update_timing();
829                         update_palette = true;
830                 }
831 #ifdef NIPPY_PATCH
832                 // dirty patch for NIPPY
833                 nippy_patch = (data == 0x37 && d_cpu->get_pc() == 0xaa32);
834 #endif
835                 break;
836         case 0x32:
837                 if(mod & 0x03) {
838                         if(!(Port71_EROM & 1)) {
839                                 update_low_memmap();
840                         }
841                 }
842                 if(mod & 0x10) {
843                         // XM8 version 1.10
844 //                      if(config.boot_mode == MODE_PC88_V1H || config.boot_mode == MODE_PC88_V2) {
845                                 update_tvram_memmap();
846                                 f000_m1_wait_clocks = get_m1_wait(true);
847 //                      }
848                 }
849                 if(mod & 0x40) {
850                         update_gvram_sel();
851                 }
852                 if(mod & 0x80) {
853                         if(intr_req_sound && !Port32_SINTM) {
854                                 request_intr(IRQ_SOUND, true);
855                         }
856                 }
857                 break;
858 #endif
859         case 0x35:
860                 if(mod & 0x80) {
861                         update_gvram_sel();
862                 }
863                 break;
864         case 0x40:
865                 d_prn->write_signal(SIG_PRINTER_STROBE, data, 1);
866                 d_rtc->write_signal(SIG_UPD1990A_STB, ~data, 2);
867                 d_rtc->write_signal(SIG_UPD1990A_CLK, data, 4);
868                 // bit3: crtc i/f sync mode
869                 if(mod & 0x10) {
870                         update_gvram_wait();
871                 }
872                 beep_on = ((data & 0x20) != 0);
873 #ifdef SUPPORT_PC88_JOYSTICK
874                 if(mod & 0x40) {
875                         if(Port40_JOP1 && (mouse_phase == -1 || get_passed_clock(mouse_strobe_clock) > mouse_strobe_clock_lim)) {
876                                 mouse_phase = 0;//mouse_dx = mouse_dy = 0;
877                         } else {
878                                 mouse_phase = (mouse_phase + 1) & 3;
879                         }
880                         if(mouse_phase == 0) {
881                                 // latch position
882                                 mouse_lx = -((mouse_dx > 127) ? 127 : (mouse_dx < -127) ? -127 : mouse_dx);
883                                 mouse_ly = -((mouse_dy > 127) ? 127 : (mouse_dy < -127) ? -127 : mouse_dy);
884                                 mouse_dx = mouse_dy = 0;
885                         }
886                         mouse_strobe_clock = get_current_clock();
887                 }
888 #endif
889                 sing_signal = ((data & 0x80) != 0);
890                 d_pcm->write_signal(SIG_PCM1BIT_SIGNAL, ((beep_on && beep_signal) || sing_signal) ? 1 : 0, 1);
891                 break;
892         case 0x44:
893         case 0x45:
894                 d_opn->write_io8(addr, data);
895                 break;
896 #ifdef SUPPORT_PC88_OPNA
897         case 0x46:
898         case 0x47:
899                 if(d_opn->is_ym2608) {
900                         d_opn->write_io8(addr, data);
901                 }
902                 break;
903 #endif
904         case 0x50:
905                 crtc.write_param(data);
906                 if(crtc.timing_changed) {
907                         update_timing();
908                         crtc.timing_changed = false;
909                 }
910                 break;
911         case 0x51:
912                 crtc.write_cmd(data);
913                 break;
914 #if !defined(_PC8001SR)
915         case 0x52:
916                 palette[8].b = (data & 0x10) ? 7 : 0;
917                 palette[8].r = (data & 0x20) ? 7 : 0;
918                 palette[8].g = (data & 0x40) ? 7 : 0;
919                 update_palette = true;
920                 break;
921 #endif
922         case 0x54:
923         case 0x55:
924         case 0x56:
925         case 0x57:
926         case 0x58:
927         case 0x59:
928         case 0x5a:
929         case 0x5b:
930 #if !defined(_PC8001SR)
931                 if(Port32_PMODE) {
932                         int n = (data & 0x80) ? 9 : (addr - 0x54);
933                         if(data & 0x40) {
934                                 palette[n].g = data & 7;
935                         } else {
936                                 palette[n].b = data & 7;
937                                 palette[n].r = (data >> 3) & 7;
938                         }
939                 } else {
940                         int n = addr - 0x54;
941                         palette[n].b = (data & 1) ? 7 : 0;
942                         palette[n].r = (data & 2) ? 7 : 0;
943                         palette[n].g = (data & 4) ? 7 : 0;
944                 }
945 #endif
946                 update_palette = true;
947                 break;
948         case 0x5c:
949                 if(gvram_plane != 1) {
950                         gvram_plane = 1;
951                         update_gvram_sel();
952                 }
953                 break;
954         case 0x5d:
955                 if(gvram_plane != 2) {
956                         gvram_plane = 2;
957                         update_gvram_sel();
958                 }
959                 break;
960         case 0x5e:
961                 if(gvram_plane != 4) {
962                         gvram_plane = 4;
963                         update_gvram_sel();
964                 }
965                 break;
966         case 0x5f:
967                 if(gvram_plane != 0) {
968                         gvram_plane = 0;
969                         update_gvram_sel();
970                 }
971                 break;
972         case 0x60:
973         case 0x61:
974         case 0x62:
975         case 0x63:
976         case 0x64:
977         case 0x65:
978         case 0x66:
979         case 0x67:
980         case 0x68:
981                 dmac.write_io8(addr, data);
982                 break;
983 #if defined(_PC8001SR)
984         case 0x71:
985                 if((mod & 1) && Port33_N80SR) {
986                         update_n80_read();
987                 }
988                 break;
989         case 0xe2:
990                 if(mod & 0x01) {
991                         update_n80_read();
992                 }
993                 if(mod & 0x10) {
994                         update_n80_write();
995                 }
996                 break;
997         case 0xe3:
998                 if(mod) {
999                         update_n80_write();
1000                         update_n80_read();
1001                 }
1002                 break;
1003 #else
1004         case 0x71:
1005                 if(mod & 0x01) {
1006                         update_low_memmap();
1007                 }
1008                 break;
1009         case 0x78:
1010                 Port70_TEXTWND++;
1011                 break;
1012 #ifdef SUPPORT_PC88_HMB20
1013         case 0x88:
1014         case 0x89:
1015                 d_opm->write_io8(addr, data);
1016                 break;
1017 #endif
1018 #ifdef SUPPORT_PC88_CDROM
1019         // M88 cdif
1020         case 0x90:
1021                 if(cdbios_loaded && (mod & 0x01)) {
1022                         if(data & 0x01) {
1023                                 if(port[0x9f] & 0x01) {
1024                                         d_scsi_host->write_signal(SIG_SCSI_SEL, 0, 1);
1025                                         d_scsi_host->write_signal(SIG_SCSI_SEL, 1, 1);
1026                                         d_scsi_host->write_signal(SIG_SCSI_SEL, 0, 1);
1027                                 }
1028                         } else {
1029                                 d_scsi_host->write_signal(SIG_SCSI_SEL, 0, 1);
1030                         }
1031 //                      d_scsi_host->write_signal(SIG_SCSI_SEL, data, 1);
1032                 }
1033                 break;
1034         case 0x91:
1035                 if(cdbios_loaded) {
1036                         d_scsi_host->write_dma_io8(0, data);
1037                 }
1038                 break;
1039         case 0x94:
1040                 if(cdbios_loaded) {
1041                         d_scsi_host->write_signal(SIG_SCSI_RST, data, 0x80);
1042                 }
1043                 break;
1044         case 0x98:
1045                 if(cdbios_loaded) {
1046                         switch(data & 7) {
1047                         case 0:
1048                         case 1:
1049                                 if(cdda_register_id != -1) {
1050                                         cancel_event(this, cdda_register_id);
1051                                         cdda_register_id = -1;
1052                                 }
1053                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1054                                 break;
1055                         case 2:
1056                         case 3:
1057                                 if(cdda_register_id != -1) {
1058                                         cancel_event(this, cdda_register_id);
1059                                         cdda_register_id = -1;
1060                                 }
1061                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1062                                 break;
1063                         case 4:
1064                                 if(cdda_register_id != -1) {
1065                                         cancel_event(this, cdda_register_id);
1066                                 }
1067                                 register_event(this, EVENT_FADE_IN, 100, true, &cdda_register_id); // 100ms
1068                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1069                                 break;
1070                         case 5:
1071                                 if(cdda_register_id != -1) {
1072                                         cancel_event(this, cdda_register_id);
1073                                 }
1074                                 register_event(this, EVENT_FADE_IN, 1500, true, &cdda_register_id); // 1500ms
1075                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1076                                 break;
1077                         case 6:
1078                                 if(cdda_register_id != -1) {
1079                                         cancel_event(this, cdda_register_id);
1080                                 }
1081                                 register_event(this, EVENT_FADE_OUT, 100, true, &cdda_register_id); // 100ms
1082                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1083                                 break;
1084                         case 7:
1085                                 if(cdda_register_id != -1) {
1086                                         cancel_event(this, cdda_register_id);
1087                                 }
1088                                 register_event(this, EVENT_FADE_OUT, 5000, true, &cdda_register_id); // 5000ms
1089                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1090                                 break;
1091                         }
1092                 }
1093                 break;
1094         case 0x99:
1095                 if(cdbios_loaded && (mod & 0x10)) {
1096                         update_low_memmap();
1097                 }
1098                 break;
1099 #endif
1100 #ifdef SUPPORT_PC88_SB2
1101         case 0xa8:
1102         case 0xa9:
1103                 if(d_sb2 != NULL) {
1104                         d_sb2->write_io8(addr, data);
1105                 }
1106                 break;
1107         case 0xaa:
1108                 if(mod & 0x80) {
1109                         if(intr_req_sb2 && !PortAA_S2INTM) {
1110                                 request_intr(IRQ_SOUND, true);
1111                         }
1112                 }
1113                 break;
1114 #ifdef SUPPORT_PC88_OPNA
1115         case 0xac:
1116         case 0xad:
1117                 if(d_sb2 != NULL && d_sb2->is_ym2608) {
1118                         d_sb2->write_io8(addr | 2, data);
1119                 }
1120                 break;
1121 #endif
1122 #endif
1123         case 0xe2:
1124                 if(mod & 0x11) {
1125                         update_low_memmap();
1126                 }
1127                 break;
1128         case 0xe3:
1129 #ifdef PC88_IODATA_EXRAM
1130                 if(mod) {
1131 #else
1132                 if(mod & 0x0f) {
1133 #endif
1134                         if(PortE2_RDEN || PortE2_WREN) {
1135                                 update_low_memmap();
1136                         }
1137                 }
1138                 break;
1139 #endif
1140         case 0xe4:
1141                 intr_mask1 = ~(0xff << (data < 8 ? data : 8));
1142                 update_intr();
1143                 break;
1144         case 0xe6:
1145                 // for Romancia (XM8 version 1.00)
1146                 if(intr_mask2_table[data & 7] != intr_mask2) {
1147                         intr_req &= (intr_mask2_table[data & 7] & intr_mask2);
1148                 }
1149                 intr_mask2 = intr_mask2_table[data & 7];
1150                 intr_req &= intr_mask2;
1151                 update_intr();
1152                 break;
1153 #ifdef SUPPORT_PC88_DICTIONARY
1154         case 0xf0:
1155                 // XM8 version 1.20
1156                 if(port[0xf0] >= 0x20) {
1157                         // no effect if data >= 0x20
1158                         port[0xf0] ^= mod;
1159                 }
1160                 break;
1161         case 0xf1:
1162                 // XM8 version 1.20
1163                 if(port[0xf1] != 0x00 && port[0xf1] != 0x01) {
1164                         // effect only 0x00 or 0x01
1165                         port[0xf1] ^= mod;
1166                 }
1167                 break;
1168 #endif
1169         case 0xfc:
1170         case 0xfd:
1171         case 0xfe:
1172         case 0xff:
1173                 d_pio->write_io8(addr, data);
1174                 break;
1175         }
1176 }
1177
1178 uint32_t PC88::read_io8(uint32_t addr)
1179 #ifdef _IO_DEBUG_LOG
1180 {
1181         uint32_t val = read_io8_debug(addr);
1182         this->out_debug_log(_T("%06x\tIN8\t%02x = %02x\n"), d_cpu->get_pc(), addr & 0xff, val);
1183         return val;
1184 }
1185
1186 uint32_t PC88::read_io8_debug(uint32_t addr)
1187 #endif
1188 {
1189         uint32_t val = 0xff;
1190         
1191         addr &= 0xff;
1192         switch(addr) {
1193         case 0x00:
1194         case 0x01:
1195         case 0x02:
1196         case 0x03:
1197         case 0x04:
1198         case 0x05:
1199         case 0x06:
1200         case 0x07:
1201         case 0x08:
1202         case 0x09:
1203         case 0x0a:
1204         case 0x0b:
1205         case 0x0c:
1206         case 0x0d:
1207         case 0x0e:
1208                 for(int i = 0; i < 8; i++) {
1209                         if(key_status[key_table[addr & 0x0f][i]]) {
1210                                 val &= ~(1 << i);
1211                         }
1212                 }
1213                 if(addr == 0x0e) {
1214                         val &= ~0x80; // http://www.maroon.dti.ne.jp/youkan/pc88/iomap.html
1215                 }
1216                 return val;
1217         case 0x20:
1218         case 0x21:
1219                 return d_sio->read_io8(addr);
1220 #if defined(_PC8001SR)
1221         case 0x30:
1222                 return (config.boot_mode == MODE_PC80_N ? 0 : 1) | (config.boot_mode == MODE_PC80_V2 ? 0 : 2) | 0xfc;
1223         case 0x31:
1224                 return (config.boot_mode == MODE_PC80_V2 ? 0 : 0x80) | 0x39;
1225         case 0x33:
1226                 return port[0x33];
1227 #else
1228         case 0x30:
1229 //              return (config.boot_mode == MODE_PC88_N ? 0 : 1) | 0xca; // 80x20 (XM8 version 1.00)
1230                 return (config.boot_mode == MODE_PC88_N ? 0 : 1) | 0xc2; // 80x25
1231         case 0x31:
1232                 // XM8 version 1.10
1233                 return (config.boot_mode == MODE_PC88_V2 ? 0 : 0x80) | (config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N ? 0 : 0x40) | 0x39;
1234 //              return (config.boot_mode == MODE_PC88_V2 ? 0 : 0x80) | (config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N ? 0 : 0x40);
1235         case 0x32:
1236                 return port[0x32];
1237 #endif
1238         case 0x40:
1239                 // XM8 version 1.10
1240 //              return (crtc.vblank ? 0x20 : 0) | (d_rtc->read_signal(0) ? 0x10 : 0) | (usart_dcd ? 4 : 0) | (hireso ? 0 : 2) | 0xc1;
1241                 return (crtc.vblank ? 0x20 : 0) | (d_rtc->read_signal(0) ? 0x10 : 0) | (usart_dcd ? 4 : 0) | (hireso ? 0 : 2) | (d_prn->read_signal(SIG_PRINTER_BUSY) ? 1 : 0) | 0xc0;
1242         case 0x44:
1243                 val = d_opn->read_io8(addr);
1244                 if(opn_busy) {
1245                         // show busy flag for first access (for ALPHA)
1246                         if(d_cpu->get_pc() == 0xe615) {
1247                                 val |= 0x80;
1248                         }
1249                         opn_busy = false;
1250                 }
1251                 return val;
1252         case 0x45:
1253                 if(Port44_OPNCH == 14) {
1254 #ifdef SUPPORT_PC88_JOYSTICK
1255                         if(config.joystick_type == DEVICE_JOYSTICK) {
1256                                 return (~(joystick_status[0] >> 0) & 0x0f) | 0xf0;
1257                         } else if(config.joystick_type == DEVICE_MOUSE) {
1258                                 switch(mouse_phase) {
1259                                 case 0:
1260                                         return ((mouse_lx >> 4) & 0x0f) | 0xf0;
1261                                 case 1:
1262                                         return ((mouse_lx >> 0) & 0x0f) | 0xf0;
1263                                 case 2:
1264                                         return ((mouse_ly >> 4) & 0x0f) | 0xf0;
1265                                 case 3:
1266                                         return ((mouse_ly >> 0) & 0x0f) | 0xf0;
1267                                 }
1268                                 return 0xf0; // ???
1269                         }
1270 #endif
1271                         return 0xff;
1272                 } else if(Port44_OPNCH == 15) {
1273 #ifdef SUPPORT_PC88_JOYSTICK
1274                         if(config.joystick_type == DEVICE_JOYSTICK) {
1275                                 return (~(joystick_status[0] >> 4) & 0x03) | 0xfc;
1276                         } else if(config.joystick_type == DEVICE_MOUSE) {
1277                                 return (~mouse_status[2] & 0x03) | 0xfc;
1278                         }
1279 #endif
1280                         return 0xff;
1281                 }
1282                 return d_opn->read_io8(addr);
1283 #ifdef SUPPORT_PC88_OPNA
1284         case 0x46:
1285         case 0x47:
1286                 if(d_opn->is_ym2608) {
1287                         return d_opn->read_io8(addr);
1288                 }
1289                 break;
1290 #endif
1291         case 0x50:
1292                 return crtc.read_param();
1293         case 0x51:
1294                 return crtc.read_status();
1295         case 0x5c:
1296                 return gvram_plane | 0xf8;
1297         case 0x60:
1298         case 0x61:
1299         case 0x62:
1300         case 0x63:
1301         case 0x64:
1302         case 0x65:
1303         case 0x66:
1304         case 0x67:
1305         case 0x68:
1306                 return dmac.read_io8(addr);
1307         case 0x6e:
1308                 // XM8 version 1.20
1309                 return (cpu_clock_low ? 0x80 : 0) | (is_sr_mr() ? 0x7f : 0x10);
1310         case 0x6f:
1311                 // XM8 version 1.20
1312                 return is_sr_mr() ? 0xff : (port[0x6f] | 0xf0);
1313 #if !defined(_PC8001SR)
1314         case 0x70:
1315                 // PC-8001mkIISR returns the constant value
1316                 // this port is used to detect PC-8001mkIISR or 8801mkIISR
1317                 return port[0x70];
1318 #endif
1319         case 0x71:
1320                 return port[0x71];
1321 #ifdef SUPPORT_PC88_HMB20
1322 //      case 0x88:
1323         case 0x89:
1324                 return d_opm->read_io8(addr);
1325 #endif
1326 #ifdef SUPPORT_PC88_CDROM
1327         // M88 cdif
1328         case 0x90:
1329                 if(cdbios_loaded) {
1330                         val  = d_scsi_host->read_signal(SIG_SCSI_BSY) ? 0x80 : 0;
1331                         val |= d_scsi_host->read_signal(SIG_SCSI_REQ) ? 0x40 : 0;
1332                         val |= d_scsi_host->read_signal(SIG_SCSI_MSG) ? 0x20 : 0;
1333                         val |= d_scsi_host->read_signal(SIG_SCSI_CD ) ? 0x10 : 0;
1334                         val |= d_scsi_host->read_signal(SIG_SCSI_IO ) ? 0x08 : 0;
1335                         // do not show BSY,MSG,CxD,IxD when SEL=1 (\90M\92·\82Ì\96ì\96\95\90\8f«\95\97\89_\98^)
1336                         if(port[0x90] & 0x01) {
1337                                 val &= ~(0x80 | 0x20 | 0x10 | 0x08);
1338                                 val |= (port[0x9f] & 0x01); // correct ???
1339                         }
1340                         #ifdef _SCSI_DEBUG_LOG
1341                                 this->out_debug_log(_T("[SCSI_PC88] Status = %02X\n"), val);
1342                         #endif
1343                         return val;
1344                 }
1345                 break;
1346         case 0x91:
1347                 if(cdbios_loaded) {
1348                         return d_scsi_host->read_dma_io8(0);
1349                 }
1350                 break;
1351         case 0x92:
1352         case 0x93:
1353         case 0x96:
1354                 if(cdbios_loaded) {
1355                         return 0x00;
1356                 }
1357                 break;
1358         case 0x99:
1359                 if(cdbios_loaded) {
1360 //                      return 0xcd; // PC-8801MC
1361                         return 0x00;
1362                 }
1363                 break;
1364         case 0x98:
1365                 if(cdbios_loaded) {
1366                         port[0x98] ^= 0x80; // clock ???
1367                         return port[0x98];
1368                 }
1369                 break;
1370         case 0x9b:
1371         case 0x9d:
1372                 if(cdbios_loaded) {
1373                         return 60;
1374                 }
1375                 break;
1376 #endif
1377 #ifdef SUPPORT_PC88_SB2
1378         case 0xa8:
1379                 if(d_sb2 != NULL) {
1380                         return d_sb2->read_io8(addr);
1381                 }
1382                 break;
1383         case 0xa9:
1384                 if(d_sb2 != NULL) {
1385                         if(PortA8_OPNCH == 14) {
1386 #ifdef SUPPORT_PC88_JOYSTICK
1387                                 if(config.joystick_type == DEVICE_JOYSTICK) {
1388                                         return (~(joystick_status[0] >> 0) & 0x0f) | 0xf0;
1389                                 } else if(config.joystick_type == DEVICE_MOUSE) {
1390                                         switch(mouse_phase) {
1391                                         case 0:
1392                                                 return ((mouse_lx >> 4) & 0x0f) | 0xf0;
1393                                         case 1:
1394                                                 return ((mouse_lx >> 0) & 0x0f) | 0xf0;
1395                                         case 2:
1396                                                 return ((mouse_ly >> 4) & 0x0f) | 0xf0;
1397                                         case 3:
1398                                                 return ((mouse_ly >> 0) & 0x0f) | 0xf0;
1399                                         }
1400                                         return 0xf0; // ???
1401                                 }
1402 #endif
1403                                 return 0xff;
1404                         } else if(PortA8_OPNCH == 15) {
1405 #ifdef SUPPORT_PC88_JOYSTICK
1406                                 if(config.joystick_type == DEVICE_JOYSTICK) {
1407                                         return (~(joystick_status[0] >> 4) & 0x03) | 0xfc;
1408                                 } else if(config.joystick_type == DEVICE_MOUSE) {
1409                                         return (~mouse_status[2] & 0x03) | 0xfc;
1410                                 }
1411 #endif
1412                                 return 0xff;
1413                         }
1414                         return d_sb2->read_io8(addr);
1415                 }
1416                 break;
1417         case 0xaa:
1418                 return (PortAA_S2INTM) | 0x7f;
1419 #ifdef SUPPORT_PC88_OPNA
1420         case 0xac:
1421         case 0xad:
1422                 if(d_sb2 != NULL && d_sb2->is_ym2608) {
1423                         d_sb2->read_io8(addr | 2);
1424                 }
1425                 break;
1426 #endif
1427 #endif
1428 #if defined(SUPPORT_PC88_VAB)
1429         // X88000
1430         case 0xb4:
1431         case 0xb5:
1432                 if(PortE3_VAB_SEL) {
1433                         return port[addr];
1434                 }
1435                 break;
1436 #endif
1437         case 0xe2:
1438                 return (~port[0xe2]) | 0xee;
1439         case 0xe3:
1440 #ifdef PC88_IODATA_EXRAM
1441                 return port[0xe3];
1442 #else
1443                 return port[0xe3] | 0xf0;
1444 #endif
1445         case 0xe8:
1446                 return kanji1[PortE8E9_KANJI1 * 2 + 1];
1447         case 0xe9:
1448                 return kanji1[PortE8E9_KANJI1 * 2];
1449         case 0xec:
1450                 return kanji2[PortECED_KANJI2 * 2 + 1];
1451         case 0xed:
1452                 return kanji2[PortECED_KANJI2 * 2];
1453         case 0xfc:
1454         case 0xfd:
1455         case 0xfe:
1456                 return d_pio->read_io8(addr);
1457         }
1458         return 0xff;
1459 }
1460
1461 uint32_t PC88::read_dma_data8(uint32_t addr)
1462 {
1463         // from ram
1464 #if defined(_PC8001SR)
1465         return ram[addr & 0xffff];
1466 #else
1467         if((addr & 0xf000) == 0xf000 && (config.boot_mode == MODE_PC88_V1H || config.boot_mode == MODE_PC88_V2)) {
1468                 return tvram[addr & 0xfff];
1469         } else {
1470                 return ram[addr & 0xffff];
1471         }
1472 #endif
1473 }
1474
1475 void PC88::write_dma_data8(uint32_t addr, uint32_t data)
1476 {
1477         // to ram
1478         ram[addr & 0xffff] = data;
1479 }
1480
1481 void PC88::write_dma_io8(uint32_t addr, uint32_t data)
1482 {
1483         // to crtc
1484         crtc.write_buffer(data);
1485 }
1486
1487 void PC88::update_timing()
1488 {
1489         int lines_per_frame = (crtc.height + crtc.vretrace) * crtc.char_height;
1490         // 56.4229Hz (25line) on PC-8801MA2 (XM8 version 1.00)
1491         double frames_per_sec = (hireso ? 24860.0 * 56.423 / 56.5 : 15980.0) / (double)lines_per_frame;
1492 //      double frames_per_sec = (hireso ? 24860.0 * 56.424 / 56.5 : 15980.0) / (double)lines_per_frame;
1493         
1494         set_frames_per_sec(frames_per_sec);
1495         set_lines_per_frame(lines_per_frame);
1496 }
1497
1498 int PC88::get_m1_wait(bool addr_f000)
1499 {
1500         // XM8 version 1.20
1501         int wait = 0;
1502         
1503 #if defined(_PC8001SR)
1504         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1505 #else
1506         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1507 #endif
1508                 // V1S or N
1509                 if(!mem_wait_on) {
1510                         // memory wait = off
1511                         if(cpu_clock_low) {
1512                                 // 4MHz
1513                                 wait += 1;
1514                         }
1515                 }
1516         } else {
1517                 // V1H or V2
1518                 if(!mem_wait_on) {
1519                         // no memory wait
1520                         if(addr_f000) {
1521                                 // TVRAM
1522                                 if(!gvram_sel && !Port32_TMODE) {
1523                                         if(cpu_clock_low) {
1524                                                 // TVRAM, 4MHz only
1525                                                 wait += 1;
1526                                         }
1527                                 }
1528                         }
1529                 }
1530         }
1531         return wait;
1532 }
1533
1534 int  PC88::get_main_wait(bool read)
1535 {
1536         // XM8 version 1.20
1537         int wait = 0;
1538         
1539         if(cpu_clock_low) {
1540                 // 4MHz
1541                 if(mem_wait_on) {
1542                         if(read) {
1543                                 // memory wait + read
1544                                 wait += 1;
1545                         }
1546                 }
1547         } else {
1548                 // 8MHz
1549 #if !defined(_PC8001SR)
1550                 if(!cpu_clock_high_fe2) {
1551                         // not 8MHzH
1552                         wait += 1;
1553                 }
1554 #endif
1555                 if(mem_wait_on) {
1556                         // memory wait (read and write)
1557                         wait += 1;
1558                 }
1559         }
1560         return wait;
1561 }
1562
1563 int PC88::get_tvram_wait(bool read)
1564 {
1565         int wait = 0;
1566         
1567         if(cpu_clock_low) {
1568                 // 4MHz
1569                 if(read) {
1570                         if(mem_wait_on) {
1571                                 // memory wait + read
1572                                 wait += 1;
1573                         }
1574                 }
1575         } else {
1576                 // 8MHz -> memory wait do not effect
1577                 if(read) {
1578                         // read
1579                         wait += 2;
1580                 } else {
1581                         // write
1582                         wait += 1;
1583                 }
1584         }
1585         return wait;
1586 }
1587
1588 int  PC88::get_gvram_wait(bool read)
1589 {
1590         // XM8 version 1.20
1591         int wait = 0;
1592         
1593         if(Port31_GRAPH) {
1594                 // graphic on
1595                 if(cpu_clock_low) {
1596                         // 4MHz
1597 #if defined(_PC8001SR)
1598                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1599 #else
1600                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1601 #endif
1602                                 // V1S
1603                                 if(!Port40_GHSM && !crtc.vblank) {
1604                                         // V1S + not GHSM, V-DISP
1605                                         if(hireso) {
1606                                                 wait += 114;
1607                                         } else {
1608                                                 wait += 68;
1609                                         }
1610                                 } else {
1611                                         if(crtc.vblank) {
1612                                                 wait += 0;
1613                                         } else {
1614                                                 wait += 2;
1615                                         }
1616                                 }
1617                         } else {
1618                                 // V1H or V2
1619                                 if(crtc.vblank) {
1620                                         wait += 0;
1621                                 } else {
1622                                         wait += 2;
1623                                 }
1624                         }
1625                 }
1626                 else {
1627                         // 8MHz
1628 #if defined(_PC8001SR)
1629                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1630 #else
1631                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1632 #endif
1633                                 // V1S
1634                                 if(!Port40_GHSM && !crtc.vblank) {
1635                                         // V1S + not GHSM, V-DISP
1636                                         if(hireso) {
1637                                                 wait += 141;
1638                                         } else {
1639                                                 wait += 90;
1640                                         }
1641                                 } else {
1642                                         if(crtc.vblank) {
1643                                                 wait += 3;
1644                                         } else {
1645                                                 wait += 5;
1646                                         }
1647                                 }
1648                         } else {
1649                                 // V1H or V2
1650                                 if(crtc.vblank) {
1651                                         wait += 3;
1652                                 } else {
1653                                         wait += 5;
1654                                 }
1655                         }
1656                 }
1657         } else {
1658                 // graphic off
1659                 if(cpu_clock_low) {
1660                         // 4MHz
1661                         if(mem_wait_on) {
1662                                 if(read) {
1663                                         // memory wait + read
1664                                         wait += 1;
1665                                 }
1666                         }
1667                 } else {
1668                         // 8MHz -> memory wait do not effect
1669                         wait += 3;
1670                 }
1671         }
1672         return wait;
1673 }
1674
1675 void PC88::update_gvram_wait()
1676 {
1677         gvram_wait_clocks_r = get_gvram_wait(true);
1678         gvram_wait_clocks_w = get_gvram_wait(false);
1679 }
1680
1681 void PC88::update_gvram_sel()
1682 {
1683 #if defined(_PC8001SR)
1684         if(Port33_GVAM) {
1685 #else
1686         if(Port32_GVAM) {
1687 #endif
1688                 if(Port35_GAM) {
1689                         gvram_sel = 8;
1690                 } else {
1691                         gvram_sel = 0;
1692                 }
1693                 gvram_plane = 0; // from M88
1694         } else {
1695                 gvram_sel = gvram_plane;
1696         }
1697         f000_m1_wait_clocks = get_m1_wait(true);
1698 }
1699
1700 #if defined(_PC8001SR)
1701 void PC88::update_n80_write()
1702 {
1703         if(PortE2_WREN || Port31_MMODE) {
1704                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1705                         SET_BANK_W(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1706                 } else {
1707                         SET_BANK_W(0x0000, 0x7fff, exram);
1708                 }
1709         } else {
1710                 SET_BANK_W(0x0000, 0x7fff, wdmy);
1711         }
1712 }
1713
1714 void PC88::update_n80_read()
1715 {
1716         if(PortE2_RDEN || Port31_MMODE) {
1717                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1718                         SET_BANK_R(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1719                 } else {
1720                         SET_BANK_R(0x0000, 0x7fff, exram);
1721                 }
1722         } else if(Port33_N80SR) {
1723                 if(port[0x71] & 1) {
1724                         SET_BANK_R(0x0000, 0x7fff, n80mk2srrom);
1725                 } else {
1726                         SET_BANK_R(0x0000, 0x5fff, n80mk2srrom);
1727                         SET_BANK_R(0x6000, 0x7fff, n80mk2srrom + 0x8000);
1728                 }
1729         } else {
1730                 if(port[0x31] & 1) {
1731                         SET_BANK_R(0x0000, 0x7fff, n80mk2rom);
1732                 } else {
1733                         SET_BANK_R(0x0000, 0x5fff, n80mk2rom);
1734                         SET_BANK_R(0x6000, 0x7fff, rdmy);
1735                 }
1736         }
1737 }
1738 #else
1739 void PC88::update_low_memmap()
1740 {
1741         // read
1742         if(PortE2_RDEN) {
1743 #ifdef PC88_EXRAM_BANKS
1744                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1745                         SET_BANK_R(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1746                 } else {
1747 #endif
1748 //                      SET_BANK_R(0x0000, 0x7fff, rdmy);
1749 #ifdef PC88_EXRAM_BANKS
1750                 }
1751 #endif
1752         } else if(Port31_MMODE) {
1753                 // 64K RAM
1754                 SET_BANK_R(0x0000, 0x7fff, ram);
1755 #ifdef SUPPORT_PC88_CDROM
1756         } else if(cdbios_loaded && Port99_CDREN) {
1757                 if(Port31_RMODE) {
1758                         SET_BANK_R(0x0000, 0x7fff, cdbios + 0x8000);
1759                 } else {
1760                         SET_BANK_R(0x0000, 0x7fff, cdbios + 0x0000);
1761                 }
1762 #endif
1763         } else if(Port31_RMODE) {
1764                 // N-BASIC
1765                 SET_BANK_R(0x0000, 0x7fff, n80rom);
1766         } else {
1767                 // N-88BASIC
1768                 SET_BANK_R(0x0000, 0x5fff, n88rom);
1769                 if(Port71_EROM & 1) {
1770                         SET_BANK_R(0x6000, 0x7fff, n88rom + 0x6000);
1771                 } else {
1772                         SET_BANK_R(0x6000, 0x7fff, n88exrom + 0x2000 * Port32_EROMSL);
1773                 }
1774         }
1775         
1776         // write
1777         if(PortE2_WREN) {
1778 #ifdef PC88_EXRAM_BANKS
1779                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1780                         SET_BANK_W(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1781                 } else {
1782 #endif
1783 //                      SET_BANK_W(0x0000, 0x7fff, wdmy);
1784                         SET_BANK_W(0x0000, 0x7fff, ram);
1785 #ifdef PC88_EXRAM_BANKS
1786                 }
1787 #endif
1788         } else {
1789                 SET_BANK_W(0x0000, 0x7fff, ram);
1790         }
1791 }
1792
1793 void PC88::update_tvram_memmap()
1794 {
1795         // XM8 version 1.10
1796         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N || Port32_TMODE) {
1797                 SET_BANK(0xf000, 0xffff, ram + 0xf000, ram + 0xf000);
1798         } else {
1799                 SET_BANK(0xf000, 0xffff, tvram, tvram);
1800         }
1801 }
1802 #endif
1803
1804 void PC88::write_signal(int id, uint32_t data, uint32_t mask)
1805 {
1806         if(id == SIG_PC88_USART_IRQ) {
1807                 request_intr(IRQ_USART, ((data & mask) != 0));
1808         } else if(id == SIG_PC88_SOUND_IRQ) {
1809                 intr_req_sound = ((data & mask) != 0);
1810 #if defined(_PC8001SR)
1811                 if(intr_req_sound && !Port33_SINTM) {
1812 #else
1813                 if(intr_req_sound && !Port32_SINTM) {
1814 #endif
1815                         request_intr(IRQ_SOUND, true);
1816                 }
1817 #ifdef SUPPORT_PC88_SB2
1818         } else if(id == SIG_PC88_SB2_IRQ) {
1819                 intr_req_sb2 = ((data & mask) != 0);
1820                 if(intr_req_sb2 && !PortAA_S2INTM) {
1821                         request_intr(IRQ_SOUND, true);
1822                 }
1823 #endif
1824 #ifdef SUPPORT_PC88_CDROM
1825         } else if(id == SIG_PC88_SCSI_DRQ) {
1826                 if((data & mask) && cdbios_loaded) {
1827                         if(!dmac.ch[1].running) {
1828                                 dmac.start(1);
1829                         }
1830                         if(dmac.ch[1].running) {
1831                                 dmac.run(1, 1);
1832                         }
1833                 }
1834 #endif
1835         } else if(id == SIG_PC88_USART_OUT) {
1836                 // recv from sio
1837                 if(Port30_CMT) {
1838                         // send to cmt
1839                         if(cmt_rec && Port30_MTON) {
1840                                 cmt_buffer[cmt_bufptr++] = data & mask;
1841                                 if(cmt_bufptr >= CMT_BUFFER_SIZE) {
1842                                         cmt_fio->Fwrite(cmt_buffer, cmt_bufptr, 1);
1843                                         cmt_bufptr = 0;
1844                                 }
1845                         }
1846                 } else {
1847                         // send to rs-232c
1848                 }
1849         }
1850 }
1851
1852 void PC88::event_callback(int event_id, int err)
1853 {
1854         switch(event_id) {
1855         case EVENT_TIMER:
1856                 request_intr(IRQ_TIMER, true);
1857                 break;
1858         case EVENT_BUSREQ:
1859                 d_cpu->write_signal(SIG_CPU_BUSREQ, 0, 0);
1860                 break;
1861         case EVENT_CMT_SEND:
1862                 // check data carrier
1863                 if(cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON) {
1864                         // detect the data carrier at the top of next block
1865                         if(check_data_carrier()) {
1866                                 register_event(this, EVENT_CMT_DCD, 1000000, false, &cmt_register_id);
1867                                 usart_dcd = true;
1868                                 break;
1869                         }
1870                 }
1871         case EVENT_CMT_DCD:
1872                 // send data to sio
1873                 usart_dcd = false;
1874                 if(cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON) {
1875                         d_sio->write_signal(SIG_I8251_RECV, cmt_buffer[cmt_bufptr++], 0xff);
1876                         if(cmt_bufptr < cmt_bufcnt) {
1877                                 register_event(this, EVENT_CMT_SEND, 5000, false, &cmt_register_id);
1878                                 break;
1879                         }
1880                 }
1881                 usart_dcd = true; // Jackie Chan no Spartan X
1882                 cmt_register_id = -1;
1883                 break;
1884         case EVENT_BEEP:
1885                 beep_signal = !beep_signal;
1886                 d_pcm->write_signal(SIG_PCM1BIT_SIGNAL, ((beep_on && beep_signal) || sing_signal) ? 1 : 0, 1);
1887                 break;
1888 #ifdef SUPPORT_PC88_CDROM
1889         case EVENT_FADE_IN:
1890                 if((cdda_volume += 0.1) >= 100.0) {
1891                         cancel_event(this, cdda_register_id);
1892                         cdda_register_id = -1;
1893                         cdda_volume = 100.0;
1894                 }
1895                 d_scsi_cdrom->set_volume((int)cdda_volume);
1896                 break;
1897         case EVENT_FADE_OUT:
1898                 if((cdda_volume -= 0.1) <= 0) {
1899                         cancel_event(this, cdda_register_id);
1900                         cdda_register_id = -1;
1901                         cdda_volume = 0.0;
1902                 }
1903                 d_scsi_cdrom->set_volume((int)cdda_volume);
1904                 break;
1905 #endif
1906         }
1907 }
1908
1909 void PC88::event_frame()
1910 {
1911         // update key status
1912         memcpy(key_status, emu->get_key_buffer(), sizeof(key_status));
1913         
1914         for(int i = 0; i < array_length(key_conv_table); i++) {
1915                 // INS or F6-F10 -> SHIFT + DEL or F1-F5
1916                 if(key_status[key_conv_table[i][0]]) {
1917                         key_status[key_conv_table[i][1]] = 1;
1918                         key_status[0x10] |= key_conv_table[i][2];
1919                 }
1920         }
1921         if(key_status[0x11] && (key_status[0xbc] || key_status[0xbe])) {
1922                 // CTRL + "," or "." -> NumPad "," or "."
1923                 key_status[0x6c] = key_status[0xbc];
1924                 key_status[0x6e] = key_status[0xbe];
1925                 key_status[0x11] = key_status[0xbc] = key_status[0xbe] = 0;
1926         }
1927         key_status[0x14] = key_caps;
1928         key_status[0x15] = key_kana;
1929         
1930         crtc.update_blink();
1931         
1932 #ifdef SUPPORT_PC88_JOYSTICK
1933         mouse_dx += mouse_status[0];
1934         mouse_dy += mouse_status[1];
1935 #endif
1936 }
1937
1938 void PC88::event_vline(int v, int clock)
1939 {
1940         int disp_line = crtc.height * crtc.char_height;
1941         
1942         if(v == 0) {
1943                 if(crtc.status & 0x10) {
1944                         // start dma transfer to crtc
1945                         dmac.start(2);
1946                         if(!dmac.ch[2].running) {
1947                                 // dma underrun occurs !!!
1948                                 crtc.status |= 8;
1949 //                              crtc.status &= ~0x10;
1950                         } else {
1951                                 crtc.status &= ~8;
1952                         }
1953                         // dma wait cycles
1954                         // from memory access test on PC-8801MA2 (XM8 version 1.20)
1955                         busreq_clocks = (int)((double)(dmac.ch[2].count.sd + 1) * (cpu_clock_low ? 5.95 : 10.58) / (double)disp_line + 0.5);
1956 //                      busreq_clocks = (int)((double)(dmac.ch[2].count.sd + 1) * (cpu_clock_low ? 7.0 : 16.0) / (double)disp_line + 0.5);
1957                 }
1958                 crtc.start();
1959                 // for Nobunaga Fuunroku Opening (XM8 version 1.00)
1960 //              request_intr(IRQ_VRTC, false);
1961                 update_gvram_wait();
1962         }
1963         if(v < disp_line) {
1964                 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
1965                         // bus request
1966 #if defined(_PC8001SR)
1967                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1968 #else
1969                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1970 #endif
1971                                 d_cpu->write_signal(SIG_CPU_BUSREQ, 1, 1);
1972                                 register_event_by_clock(this, EVENT_BUSREQ, busreq_clocks, false, NULL);
1973                         }
1974                         // run dma transfer to crtc
1975                         if((v % crtc.char_height) == 0) {
1976                                 dmac.run(2, 80 + crtc.attrib.num * 2);
1977                         }
1978                 }
1979         } else if(v == disp_line) {
1980                 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
1981                         dmac.finish(2);
1982                         // for Romancia (XM8 version 1.00)
1983 //                      crtc.expand_buffer(hireso, Port31_400LINE);
1984                 }
1985                 // for Romancia (XM8 version 1.00)
1986                 crtc.expand_buffer(hireso, Port31_400LINE);
1987                 
1988                 crtc.finish();
1989                 request_intr(IRQ_VRTC, true);
1990                 update_gvram_wait();
1991         }
1992         // update palette
1993 #if !defined(_PC8001SR)
1994         if(v < (disp_line <= 200 ? 200 : 400)) {
1995 #else
1996         if(v < 200) {
1997 #endif
1998                 if(update_palette) {
1999                         static bool initialized = false;
2000                         static palette_t initial[9] = {0};
2001                         
2002                         if(!initialized) {
2003                                 for(int i = 1; i < 8; i++) {
2004                                         initial[i].b = (i & 1) ? 7 : 0;
2005                                         initial[i].r = (i & 2) ? 7 : 0;
2006                                         initial[i].g = (i & 4) ? 7 : 0;
2007                                 }
2008                                 initialized = true;
2009                         }
2010                         for(int i = 0; i < 9; i++) {
2011                                 palette_digital[i] = palette_analog[i] = initial[i];
2012                         }
2013 #if defined(_PC8001SR)
2014                         if(config.boot_mode != MODE_PC80_V2) {
2015                                 if(Port31_V1_320x200) {
2016                                         for(int i = 0; i < 3; i++) {
2017                                                 palette_analog[i].b = (port[0x31] & 4) ? 7 : 0;
2018                                                 palette_analog[i].r = (i          & 1) ? 7 : 0;
2019                                                 palette_analog[i].g = (i          & 2) ? 7 : 0;
2020                                         }
2021                                         palette_analog[3] = palette[8];
2022                                 } else if(Port31_V1_MONO) {
2023 //                                      palette_analog[0] = {0, 0, 0};
2024                                         palette_analog[1] = palette[8];
2025                                 } else {
2026 //                                      for(int i = 1; i < 8; i++) {
2027 //                                              palette_analog[i].b = (i & 1) ? 7 : 0;
2028 //                                              palette_analog[i].r = (i & 2) ? 7 : 0;
2029 //                                              palette_analog[i].g = (i & 4) ? 7 : 0;
2030 //                                      }
2031                                         palette_analog[0] = palette[8];
2032                                 }
2033                                 if(Port31_V1_320x200) {
2034 //                                      palette_digital[0] = {0, 0, 0};
2035                                 } else {
2036                                         palette_digital[0] = palette_analog[0];
2037                                 }
2038                         } else {
2039                                 for(int i = 0; i < 8; i++) {
2040                                         palette_analog[i].b = (port[0x54 + i] & 1) ? 7 : 0;
2041                                         palette_analog[i].r = (port[0x54 + i] & 2) ? 7 : 0;
2042                                         palette_analog[i].g = (port[0x54 + i] & 4) ? 7 : 0;
2043                                 }
2044                                 if(!Port31_HCOLOR) {
2045                                         palette_analog[0] = palette[8];
2046                                 }
2047                                 palette_digital[0] = palette_analog[0];
2048                         }
2049 #else
2050                         for(int i = 0; i < 8; i++) {
2051                                 palette_analog[i] = palette[i];
2052                         }
2053                         if(!Port31_HCOLOR) {
2054                                 if(!Port32_PMODE) {
2055                                         palette_analog[0] = palette[8];
2056                                 } else {
2057                                         palette_analog[0] = palette[9];
2058                                 }
2059                         }
2060                         palette_digital[0] = palette_analog[0];
2061 #endif
2062                 }
2063                 if(v == 0) {
2064                         memset(palette_line_changed, 0, sizeof(palette_line_changed));
2065                 }
2066                 if((palette_line_changed[v] = (update_palette || v == 0)) == true) {
2067                         for(int i = 0; i < 9; i++) {
2068                                 palette_line_digital[v][i] = palette_digital[i];
2069                                 palette_line_analog [v][i] = palette_analog [i];
2070                         }
2071                         update_palette = false;
2072                 }
2073         }
2074 }
2075
2076 void PC88::key_down(int code, bool repeat)
2077 {
2078         if(!repeat) {
2079                 if(code == 0x14) {
2080                         key_caps ^= 1;
2081                 } else if(code == 0x15) {
2082                         key_kana ^= 1;
2083                 }
2084         }
2085 }
2086
2087 void PC88::play_tape(const _TCHAR* file_path)
2088 {
2089         close_tape();
2090         
2091         if(cmt_fio->Fopen(file_path, FILEIO_READ_BINARY)) {
2092                 if(check_file_extension(file_path, _T(".n80"))) {
2093                         cmt_fio->Fread(ram + 0x8000, 0x7f40, 1);
2094                         cmt_fio->Fclose();
2095                         d_cpu->set_sp(ram[0xff3e] | (ram[0xff3f] << 8));
2096                         d_cpu->set_pc(0xff3d);
2097                         return;
2098                 }
2099                 
2100                 cmt_fio->Fseek(0, FILEIO_SEEK_END);
2101                 cmt_bufcnt = cmt_fio->Ftell();
2102                 cmt_bufptr = 0;
2103                 cmt_data_carrier_cnt = 0;
2104                 cmt_fio->Fseek(0, FILEIO_SEEK_SET);
2105                 memset(cmt_buffer, 0, sizeof(cmt_buffer));
2106                 cmt_fio->Fread(cmt_buffer, sizeof(cmt_buffer), 1);
2107                 cmt_fio->Fclose();
2108                 
2109                 if(strncmp((char *)cmt_buffer, "PC-8801 Tape Image(T88)", 23) == 0) {
2110                         // this is t88 format
2111                         int ptr = 24, tag = -1, len = 0, prev_bufptr = 0;
2112                         while(!(tag == 0 && len == 0)) {
2113                                 tag = cmt_buffer[ptr + 0] | (cmt_buffer[ptr + 1] << 8);
2114                                 len = cmt_buffer[ptr + 2] | (cmt_buffer[ptr + 3] << 8);
2115                                 ptr += 4;
2116                                 
2117                                 if(tag == 0x0101) {
2118                                         // data tag
2119                                         for(int i = 12; i < len; i++) {
2120                                                 cmt_buffer[cmt_bufptr++] = cmt_buffer[ptr + i];
2121                                         }
2122                                 } else if(tag == 0x0102 || tag == 0x0103) {
2123                                         // data carrier
2124                                         if(prev_bufptr != cmt_bufptr) {
2125                                                 cmt_data_carrier[cmt_data_carrier_cnt++] = prev_bufptr = cmt_bufptr;
2126                                         }
2127                                 }
2128                                 ptr += len;
2129                         }
2130                         cmt_bufcnt = cmt_bufptr;
2131                         cmt_bufptr = 0;
2132                 }
2133                 cmt_play = (cmt_bufcnt != 0);
2134                 
2135                 if(cmt_play && Port30_MTON) {
2136                         // start motor and detect the data carrier at the top of tape
2137                         if(cmt_register_id != -1) {
2138                                 cancel_event(this, cmt_register_id);
2139                         }
2140                         register_event(this, EVENT_CMT_SEND, 5000, false, &cmt_register_id);
2141                 }
2142         }
2143 }
2144
2145 void PC88::rec_tape(const _TCHAR* file_path)
2146 {
2147         close_tape();
2148         
2149         if(cmt_fio->Fopen(file_path, FILEIO_READ_WRITE_NEW_BINARY)) {
2150                 my_tcscpy_s(rec_file_path, _MAX_PATH, file_path);
2151                 cmt_bufptr = 0;
2152                 cmt_rec = true;
2153         }
2154 }
2155
2156 void PC88::close_tape()
2157 {
2158         // close file
2159         release_tape();
2160         
2161         // clear sio buffer
2162         d_sio->write_signal(SIG_I8251_CLEAR, 0, 0);
2163 }
2164
2165 void PC88::release_tape()
2166 {
2167         // close file
2168         if(cmt_fio->IsOpened()) {
2169                 if(cmt_rec && cmt_bufptr) {
2170                         cmt_fio->Fwrite(cmt_buffer, cmt_bufptr, 1);
2171                 }
2172                 cmt_fio->Fclose();
2173         }
2174         cmt_play = cmt_rec = false;
2175 }
2176
2177 bool PC88::is_frame_skippable()
2178 {
2179         return (cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON);
2180 }
2181
2182 bool PC88::check_data_carrier()
2183 {
2184         if(cmt_bufptr == 0) {
2185                 return true;
2186         } else if(cmt_data_carrier_cnt) {
2187                 for(int i = 0; i < cmt_data_carrier_cnt; i++) {
2188                         if(cmt_data_carrier[i] == cmt_bufptr) {
2189                                 return true;
2190                         }
2191                 }
2192         } else if(cmt_buffer[cmt_bufptr] == 0xd3) {
2193                 for(int i = 1; i < 10; i++) {
2194                         if(cmt_buffer[cmt_bufptr + i] != cmt_buffer[cmt_bufptr]) {
2195                                 return false;
2196                         }
2197                 }
2198                 return true;
2199         } else if(cmt_buffer[cmt_bufptr] == 0x9c) {
2200                 for(int i = 1; i < 6; i++) {
2201                         if(cmt_buffer[cmt_bufptr + i] != cmt_buffer[cmt_bufptr]) {
2202                                 return false;
2203                         }
2204                 }
2205                 return true;
2206         }
2207         return false;
2208 }
2209
2210 void PC88::draw_screen()
2211 {
2212         // render text screen
2213         draw_text();
2214         
2215         // render graph screen
2216         bool disp_color_graph = true;
2217         bool draw_scanline_black = config.scan_line;
2218 #if defined(_PC8001SR)
2219         if(config.boot_mode != MODE_PC80_V2) {
2220                 if(Port31_V1_320x200) {
2221                         disp_color_graph = draw_320x200_4color_graph();
2222                 } else if(Port31_V1_MONO) {
2223                         draw_640x200_mono_graph();
2224                 } else {
2225                         if(hireso) {
2226                                 draw_scanline_black = false;
2227                         }
2228                         draw_640x200_attrib_graph();
2229                 }
2230                 emu->set_vm_screen_lines(200);
2231         } else {
2232                 if(Port31_HCOLOR) {
2233                         if(Port31_320x200) {
2234                                 disp_color_graph = draw_320x200_color_graph();
2235                         } else {
2236                                 disp_color_graph = draw_640x200_color_graph();
2237                         }
2238                         emu->set_vm_screen_lines(200);
2239                 } else {
2240                         if(Port31_320x200) {
2241                                 draw_320x200_attrib_graph();
2242                         } else {
2243                                 draw_640x200_attrib_graph();
2244                         }
2245                         if(hireso) {
2246                                 draw_scanline_black = false;
2247                         }
2248                         emu->set_vm_screen_lines(200);
2249                 }
2250         }
2251 #else
2252         if(Port31_HCOLOR) {
2253                 disp_color_graph = draw_640x200_color_graph();
2254                 emu->set_vm_screen_lines(200);
2255         } else if(!Port31_400LINE) {
2256                 if(hireso) {
2257                         draw_scanline_black = false;
2258                 }
2259                 draw_640x200_attrib_graph();
2260 //              draw_640x200_mono_graph();
2261                 emu->set_vm_screen_lines(200);
2262         } else {
2263                 if(hireso) {
2264                         draw_scanline_black = false;
2265                 }
2266                 draw_640x400_attrib_graph();
2267 //              draw_640x400_mono_graph();
2268                 emu->set_vm_screen_lines(400);
2269         }
2270 #endif
2271         
2272         // create palette for each scanline
2273 #if !defined(_PC8001SR)
2274         int disp_line = crtc.height * crtc.char_height;
2275         int ymax = (disp_line <= 200) ? 200 : 400;
2276 #else
2277         int ymax = 200;
2278 #endif
2279         static const uint32_t pex[8] = {
2280                 0,  36,  73, 109, 146, 182, 219, 255 // from m88
2281         };
2282         scrntype_t palette_digital_text_pc [9];
2283         scrntype_t palette_analog_text_pc  [9];
2284         scrntype_t palette_digital_graph_pc[9];
2285         scrntype_t palette_analog_graph_pc [9];
2286         
2287         scrntype_t palette_line_digital_text_pc [400][9];
2288         scrntype_t palette_line_analog_graph_pc [400][9];
2289 #if !defined(_PC8001SR)
2290         scrntype_t palette_line_analog_text_pc  [400][9];
2291         scrntype_t palette_line_digital_graph_pc[400][9];
2292 #endif
2293         
2294         for(int y = 0; y < ymax; y++) {
2295                 if(palette_line_changed[y]) {
2296                         for(int i = 0; i < 9; i++) {
2297                                 // A is a flag for crt filter
2298                                 palette_digital_text_pc [i] = RGBA_COLOR(pex[palette_line_digital[y][i].r], pex[palette_line_digital[y][i].g], pex[palette_line_digital[y][i].b], 255);
2299                                 palette_analog_text_pc  [i] = RGBA_COLOR(pex[palette_line_analog [y][i].r], pex[palette_line_analog [y][i].g], pex[palette_line_analog [y][i].b], 255);
2300                                 palette_digital_graph_pc[i] = RGBA_COLOR(pex[palette_line_digital[y][i].r], pex[palette_line_digital[y][i].g], pex[palette_line_digital[y][i].b],   0);
2301                                 palette_analog_graph_pc [i] = RGBA_COLOR(pex[palette_line_analog [y][i].r], pex[palette_line_analog [y][i].g], pex[palette_line_analog [y][i].b],   0);
2302                         }
2303                         // set back color to black if cg screen is off in color mode
2304                         if(!disp_color_graph) {
2305                                 palette_digital_text_pc [0] = 
2306                                 palette_analog_text_pc  [0] = 
2307                                 palette_digital_graph_pc[0] = 
2308                                 palette_analog_graph_pc [0] = 0;
2309                         }
2310                         palette_analog_text_pc [8] = palette_digital_text_pc [0];
2311                         palette_analog_graph_pc[8] = palette_digital_graph_pc[0];
2312                 }
2313                 if(ymax == 200) {
2314                         for(int i = 0; i < 9; i++) {
2315                                 palette_line_digital_text_pc [2 * y    ][i] = 
2316                                 palette_line_digital_text_pc [2 * y + 1][i] = palette_digital_text_pc [i];
2317                                 palette_line_analog_graph_pc [2 * y    ][i] = 
2318                                 palette_line_analog_graph_pc [2 * y + 1][i] = palette_analog_graph_pc [i];
2319 #if !defined(_PC8001SR)
2320                                 palette_line_analog_text_pc  [2 * y    ][i] = 
2321                                 palette_line_analog_text_pc  [2 * y + 1][i] = palette_analog_text_pc  [i];
2322                                 palette_line_digital_graph_pc[2 * y    ][i] = 
2323                                 palette_line_digital_graph_pc[2 * y + 1][i] = palette_digital_graph_pc[i];
2324 #endif
2325                         }
2326                 } else {
2327                         for(int i = 0; i < 9; i++) {
2328                                 palette_line_digital_text_pc [y][i] = palette_digital_text_pc [i];
2329                                 palette_line_analog_graph_pc [y][i] = palette_analog_graph_pc [i];
2330 #if !defined(_PC8001SR)
2331                                 palette_line_analog_text_pc  [y][i] = palette_analog_text_pc  [i];
2332                                 palette_line_digital_graph_pc[y][i] = palette_digital_graph_pc[i];
2333 #endif
2334                         }
2335                 }
2336         }
2337         
2338         // copy to screen buffer
2339 #if !defined(_PC8001SR)
2340 #if defined(SUPPORT_PC88_VAB)
2341         // X88000
2342         if(PortB4_VAB_DISP) {
2343                 uint8_t *src = &exram[(0x8000 * 4) * PC88_VAB_PAGE];
2344                 
2345                 for(int y = 0; y < 400; y += 2) {
2346                         scrntype_t* dest0 = emu->get_screen_buffer(y);
2347                         scrntype_t* dest1 = emu->get_screen_buffer(y + 1);
2348                         
2349                         for(int x = 0; x < 640; x += 2) {
2350                                 pair16_t c;
2351                                 c.b.l = *src++;
2352                                 c.b.h = *src++;
2353                                 dest0[x] = dest0[x + 1] = palette_vab_pc[c.w];
2354                         }
2355                         if(config.scan_line) {
2356                                 memset(dest1, 0, sizeof(scrntype_t) * 640);
2357                         } else {
2358                                 memcpy(dest1, dest0, sizeof(scrntype_t) * 640);
2359                         }
2360                 }
2361                 emu->screen_skip_line(true);
2362         } else
2363 #endif
2364         if(!Port31_HCOLOR && Port31_400LINE) {
2365                 for(int y = 0; y < 400; y++) {
2366                         scrntype_t* dest = emu->get_screen_buffer(y);
2367                         uint8_t* src_t = text[y >> 1];
2368                         uint8_t* src_g = graph[y];
2369                         scrntype_t* pal_t;
2370                         scrntype_t* pal_g;
2371                         
2372 //                      if(Port31_HCOLOR) {
2373 //                              pal_t = palette_line_digital_text_pc [y];
2374 //                              pal_g = palette_line_analog_graph_pc [y];
2375 //                      } else
2376                         if(Port32_PMODE) {
2377                                 pal_t = palette_line_analog_text_pc  [y];
2378                                 pal_g = palette_line_analog_graph_pc [y];
2379                         } else {
2380                                 pal_t = palette_line_digital_text_pc [y];
2381                                 pal_g = palette_line_digital_graph_pc[y];
2382                         }
2383                         for(int x = 0; x < 640; x++) {
2384                                 uint32_t t = src_t[x];
2385                                 dest[x] = t ? pal_t[t] : pal_g[src_g[x]];
2386                         }
2387                 }
2388                 emu->screen_skip_line(false);
2389         } else
2390 #endif
2391         {
2392                 for(int y = 0; y < 400; y++) {
2393                         scrntype_t* dest = emu->get_screen_buffer(y);
2394                         uint8_t* src_t = text[y >> 1];
2395                         uint8_t* src_g = graph[y];
2396                         scrntype_t* pal_t;
2397                         scrntype_t* pal_g;
2398 #if defined(_PC8001SR)
2399                         pal_t = palette_line_digital_text_pc[y];
2400                         pal_g = palette_line_analog_graph_pc[y];
2401                         
2402                         if(port[0x33] & 8) {
2403                                 for(int x = 0; x < 640; x++) {
2404                                         uint32_t t = src_t[x];
2405                                         uint32_t g = src_g[x];
2406                                         dest[x] = (!g && t) ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[g];
2407                                 }
2408                         } else {
2409                                 for(int x = 0; x < 640; x++) {
2410                                         uint32_t t = src_t[x];
2411                                         dest[x] = t ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[src_g[x]];
2412                                 }
2413                         }
2414 #else
2415                         if(Port31_HCOLOR) {
2416                                 pal_t = palette_line_digital_text_pc [y];
2417                                 pal_g = palette_line_analog_graph_pc [y];
2418                         } else if(Port32_PMODE) {
2419                                 pal_t = palette_line_analog_text_pc  [y];
2420                                 pal_g = palette_line_analog_graph_pc [y];
2421                         } else {
2422                                 pal_t = palette_line_digital_text_pc [y];
2423                                 pal_g = palette_line_digital_graph_pc[y];
2424                         }
2425                         for(int x = 0; x < 640; x++) {
2426                                 uint32_t t = src_t[x];
2427                                 dest[x] = t ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[src_g[x]];
2428                         }
2429 #endif
2430                 }
2431                 emu->screen_skip_line(true);
2432         }
2433 }
2434
2435 /*
2436         attributes:
2437         
2438         bit7: green
2439         bit6: red
2440         bit5: blue
2441         bit4: graph=1/character=0
2442         bit3: under line
2443         bit2: upper line
2444         bit1: secret
2445         bit0: reverse
2446 */
2447
2448 void PC88::draw_text()
2449 {
2450         if(crtc.status & 0x88) {
2451                 // dma underrun
2452                 crtc.status &= ~0x80;
2453                 memset(crtc.text.expand, 0, 200 * 80);
2454                 memset(crtc.attrib.expand, crtc.reverse ? 3 : 2, 200 * 80);
2455         }
2456         // for Advanced Fantasian Opening (20line) (XM8 version 1.00)
2457         if(!(crtc.status & 0x10) || Port53_TEXTDS) {
2458 //      if(!(crtc.status & 0x10) || (crtc.status & 8) || Port53_TEXTDS) {
2459                 memset(crtc.text.expand, 0, 200 * 80);
2460                 for(int y = 0; y < 200; y++) {
2461                         for(int x = 0; x < 80; x++) {
2462                                 crtc.attrib.expand[y][x] &= 0xe0;
2463                                 crtc.attrib.expand[y][x] |= 0x02;
2464                         }
2465                 }
2466 //              memset(crtc.attrib.expand, 2, 200 * 80);
2467         }
2468         
2469         // for Xak2 opening
2470         memset(text, 8, sizeof(text));
2471         memset(text_color, 7, sizeof(text_color));
2472         memset(text_reverse, 0, sizeof(text_reverse));
2473         
2474         int char_height = crtc.char_height;
2475         uint8_t color_mask = Port30_COLOR ? 0 : 7;
2476         uint8_t code_expand, attr_expand;
2477         
2478         if(!hireso) {
2479                 char_height <<= 1;
2480         }
2481 //      if(Port31_400LINE || !crtc.skip_line) {
2482 //              char_height >>= 1;
2483 //      }
2484         if(crtc.skip_line) {
2485                 char_height <<= 1;
2486         }
2487 //      for(int cy = 0, ytop = 0; cy < 64 && ytop < 400; cy++, ytop += char_height) {
2488         for(int cy = 0, ytop = 0; cy < crtc.height && ytop < 400; cy++, ytop += char_height) {
2489                 for(int x = 0, cx = 0; cx < crtc.width; x += 8, cx++) {
2490                         if(Port30_40 && (cx & 1)) {
2491                                 // don't update code/attrib
2492                         } else {
2493                                 code_expand = crtc.text.expand[cy][cx];
2494                                 attr_expand = crtc.attrib.expand[cy][cx];
2495                         }
2496                         uint8_t attrib = attr_expand;//crtc.attrib.expand[cy][cx];
2497 //                      uint8_t color = !(Port30_COLOR && (attrib & 8)) ? 7 : (attrib & 0xe0) ? (attrib >> 5) : 8;
2498                         uint8_t color = (attrib & 0xe0) ? ((attrib >> 5) | color_mask) : 8;
2499                         bool under_line = ((attrib & 8) != 0);
2500                         bool upper_line = ((attrib & 4) != 0);
2501                         bool secret = ((attrib & 2) != 0);
2502                         bool reverse = ((attrib & 1) != 0);
2503                         
2504                         uint8_t color_tmp = color;
2505                         bool reverse_tmp = reverse;
2506                         
2507                         // from ePC-8801MA\89ü
2508                         if(Port31_GRAPH && !Port31_HCOLOR) {
2509                                 if(reverse) {
2510                                         reverse = false;
2511                                         color = 8;
2512                                 }
2513                         }
2514                         uint8_t code = secret ? 0 : code_expand;//crtc.text.expand[cy][cx];
2515 #ifdef SUPPORT_PC88_PCG8100
2516                         uint8_t *pattern = ((attrib & 0x10) ? sg_pattern : pcg_pattern) + code * 8;
2517 #else
2518                         uint8_t *pattern = ((attrib & 0x10) ? sg_pattern : kanji1 + 0x1000) + code * 8;
2519 #endif
2520                         
2521                         for(int l = 0, y = ytop; l < char_height / 2 && y < 400; l++, y += 2) {
2522                                 uint8_t pat = (l < 8) ? pattern[l] : 0;
2523                                 
2524                                 if(Port30_40) {
2525                                         // from ePC-8801MA\89ü
2526                                         static const uint8_t wct[16] = {
2527                                                 0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff
2528                                         };
2529                                         pat = wct[(cx & 1) ? (pat & 0x0f) : (pat >> 4)];
2530                                 }
2531                                 if((upper_line && l == 0) || (under_line && l >= 7)) {
2532                                         pat = 0xff;
2533                                 }
2534                                 if(reverse) {
2535                                         pat ^= 0xff;
2536                                 }
2537                                 uint8_t *dest = &text[y >> 1][x];
2538                                 dest[0] = (pat & 0x80) ? color : 0;
2539                                 dest[1] = (pat & 0x40) ? color : 0;
2540                                 dest[2] = (pat & 0x20) ? color : 0;
2541                                 dest[3] = (pat & 0x10) ? color : 0;
2542                                 dest[4] = (pat & 0x08) ? color : 0;
2543                                 dest[5] = (pat & 0x04) ? color : 0;
2544                                 dest[6] = (pat & 0x02) ? color : 0;
2545                                 dest[7] = (pat & 0x01) ? color : 0;
2546                                 
2547                                 // store text attributes for monocolor graph screen
2548                                 text_color[y >> 1][cx] = color_tmp;
2549                                 text_reverse[y >> 1][cx] = reverse_tmp;
2550                         }
2551                 }
2552         }
2553 }
2554
2555 #if defined(_PC8001SR)
2556 bool PC88::draw_320x200_color_graph()
2557 {
2558         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2559                 memset(graph, 0, sizeof(graph));
2560                 return false;
2561         }
2562         uint8_t *gvram_b0 = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2563         uint8_t *gvram_r0 = Port53_G0DS ? gvram_null : (gvram + 0x4000);
2564         uint8_t *gvram_g0 = Port53_G0DS ? gvram_null : (gvram + 0x8000);
2565         uint8_t *gvram_b1 = Port53_G1DS ? gvram_null : (gvram + 0x2000);
2566         uint8_t *gvram_r1 = Port53_G1DS ? gvram_null : (gvram + 0x6000);
2567         uint8_t *gvram_g1 = Port53_G1DS ? gvram_null : (gvram + 0xa000);
2568         
2569         if(port[0x33] & 4) {
2570                 // G1>G0
2571                 uint8_t *tmp;
2572                 tmp = gvram_b0; gvram_b0 = gvram_b1; gvram_b1 = tmp;
2573                 tmp = gvram_r0; gvram_r0 = gvram_r1; gvram_r1 = tmp;
2574                 tmp = gvram_g0; gvram_g0 = gvram_g1; gvram_g1 = tmp;
2575         }
2576         
2577         for(int y = 0, addr = 0; y < 400; y += 2) {
2578                 for(int x = 0; x < 640; x += 16) {
2579                         uint8_t b0 = gvram_b0[addr];
2580                         uint8_t r0 = gvram_r0[addr];
2581                         uint8_t g0 = gvram_g0[addr];
2582                         uint8_t b1 = gvram_b1[addr];
2583                         uint8_t r1 = gvram_r1[addr];
2584                         uint8_t g1 = gvram_g1[addr];
2585                         addr++;
2586                         uint8_t *dest = &graph[y][x];
2587                         uint8_t brg0, brg1;
2588                         brg0 = ((b0 & 0x80) >> 7) | ((r0 & 0x80) >> 6) | ((g0 & 0x80) >> 5);
2589                         brg1 = ((b1 & 0x80) >> 7) | ((r1 & 0x80) >> 6) | ((g1 & 0x80) >> 5);
2590                         dest[ 0] = dest[ 1] = brg0 ? brg0 : brg1;
2591                         brg0 = ((b0 & 0x40) >> 6) | ((r0 & 0x40) >> 5) | ((g0 & 0x40) >> 4);
2592                         brg1 = ((b1 & 0x40) >> 6) | ((r1 & 0x40) >> 5) | ((g1 & 0x40) >> 4);
2593                         dest[ 2] = dest[ 3] = brg0 ? brg0 : brg1;
2594                         brg0 = ((b0 & 0x20) >> 5) | ((r0 & 0x20) >> 4) | ((g0 & 0x20) >> 3);
2595                         brg1 = ((b1 & 0x20) >> 5) | ((r1 & 0x20) >> 4) | ((g1 & 0x20) >> 3);
2596                         dest[ 4] = dest[ 5] = brg0 ? brg0 : brg1;
2597                         brg0 = ((b0 & 0x10) >> 4) | ((r0 & 0x10) >> 3) | ((g0 & 0x10) >> 2);
2598                         brg1 = ((b1 & 0x10) >> 4) | ((r1 & 0x10) >> 3) | ((g1 & 0x10) >> 2);
2599                         dest[ 6] = dest[ 7] = brg0 ? brg0 : brg1;
2600                         brg0 = ((b0 & 0x08) >> 3) | ((r0 & 0x08) >> 2) | ((g0 & 0x08) >> 1);
2601                         brg1 = ((b1 & 0x08) >> 3) | ((r1 & 0x08) >> 2) | ((g1 & 0x08) >> 1);
2602                         dest[ 8] = dest[ 9] = brg0 ? brg0 : brg1;
2603                         brg0 = ((b0 & 0x04) >> 2) | ((r0 & 0x04) >> 1) | ((g0 & 0x04)     );
2604                         brg1 = ((b1 & 0x04) >> 2) | ((r1 & 0x04) >> 1) | ((g1 & 0x04)     );
2605                         dest[10] = dest[11] = brg0 ? brg0 : brg1;
2606                         brg0 = ((b0 & 0x02) >> 1) | ((r0 & 0x02)     ) | ((g0 & 0x02) << 1);
2607                         brg1 = ((b1 & 0x02) >> 1) | ((r1 & 0x02)     ) | ((g1 & 0x02) << 1);
2608                         dest[12] = dest[13] = brg0 ? brg0 : brg1;
2609                         brg0 = ((b0 & 0x01)     ) | ((r0 & 0x01) << 1) | ((g0 & 0x01) << 2);
2610                         brg1 = ((b1 & 0x01)     ) | ((r1 & 0x01) << 1) | ((g1 & 0x01) << 2);
2611                         dest[14] = dest[15] = brg0 ? brg0 : brg1;
2612                 }
2613                 if(config.scan_line) {
2614                         memset(graph[y + 1], 0, 640);
2615                 } else {
2616                         memcpy(graph[y + 1], graph[y], 640);
2617                 }
2618         }
2619         return true;
2620 }
2621
2622 bool PC88::draw_320x200_4color_graph()
2623 {
2624         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2625                 memset(graph, 0, sizeof(graph));
2626                 return false;
2627         }
2628         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2629         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2630         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2631         
2632         for(int y = 0, addr = 0; y < 400; y += 2) {
2633                 for(int x = 0; x < 640; x += 8) {
2634                         uint8_t brg = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2635                         addr++;
2636                         uint8_t *dest = &graph[y][x];
2637                         dest[0] = dest[1] = (brg >> 6) & 3;
2638                         dest[2] = dest[3] = (brg >> 4) & 3;
2639                         dest[4] = dest[5] = (brg >> 2) & 3;
2640                         dest[6] = dest[7] = (brg     ) & 3;
2641                 }
2642                 if(config.scan_line) {
2643                         memset(graph[y + 1], 0, 640);
2644                 } else {
2645                         memcpy(graph[y + 1], graph[y], 640);
2646                 }
2647         }
2648         return true;
2649 }
2650
2651 void PC88::draw_320x200_attrib_graph()
2652 {
2653         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS && Port53_G3DS && Port53_G4DS && Port53_G5DS)) {
2654                 memset(graph, 0, sizeof(graph));
2655                 return;
2656         }
2657         uint8_t *gvram_b0 = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2658         uint8_t *gvram_r0 = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2659         uint8_t *gvram_g0 = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2660         uint8_t *gvram_b1 = Port53_G3DS ? gvram_null : (gvram + 0x2000);
2661         uint8_t *gvram_r1 = Port53_G4DS ? gvram_null : (gvram + 0x6000);
2662         uint8_t *gvram_g1 = Port53_G5DS ? gvram_null : (gvram + 0xa000);
2663         
2664         if(Port30_40) {
2665                 for(int y = 0, addr = 0; y < 400; y += 2) {
2666                         for(int x = 0, cx = 0; x < 640; x += 16, cx += 2) {
2667                                 uint8_t color = text_color[y >> 1][cx];
2668                                 uint8_t brg0 = gvram_b0[addr] | gvram_r0[addr] | gvram_g0[addr] |
2669                                                gvram_b1[addr] | gvram_r1[addr] | gvram_g1[addr];
2670                                 uint8_t brg1 = 0;
2671                                 if(text_reverse[y >> 1][cx]) {
2672                                         brg0 ^= 0xff;
2673                                         brg1 ^= 0xff;
2674                                 }
2675                                 addr++;
2676                                 uint8_t *dest0 = &graph[y    ][x];
2677                                 uint8_t *dest1 = &graph[y + 1][x];
2678                                 dest0[ 0] = dest0[ 1] = (brg0 & 0x80) ? color : 0;
2679                                 dest0[ 2] = dest0[ 3] = (brg0 & 0x40) ? color : 0;
2680                                 dest0[ 4] = dest0[ 5] = (brg0 & 0x20) ? color : 0;
2681                                 dest0[ 6] = dest0[ 7] = (brg0 & 0x10) ? color : 0;
2682                                 dest0[ 8] = dest0[ 9] = (brg0 & 0x08) ? color : 0;
2683                                 dest0[10] = dest0[11] = (brg0 & 0x04) ? color : 0;
2684                                 dest0[12] = dest0[13] = (brg0 & 0x02) ? color : 0;
2685                                 dest0[14] = dest0[15] = (brg0 & 0x01) ? color : 0;
2686                                 if(!hireso) continue;
2687                                 dest1[ 0] = dest1[ 1] = (brg1 & 0x80) ? color : 0;
2688                                 dest1[ 2] = dest1[ 3] = (brg1 & 0x40) ? color : 0;
2689                                 dest1[ 4] = dest1[ 5] = (brg1 & 0x20) ? color : 0;
2690                                 dest1[ 6] = dest1[ 7] = (brg1 & 0x10) ? color : 0;
2691                                 dest1[ 8] = dest1[ 9] = (brg1 & 0x08) ? color : 0;
2692                                 dest1[10] = dest1[11] = (brg1 & 0x04) ? color : 0;
2693                                 dest1[12] = dest1[13] = (brg1 & 0x02) ? color : 0;
2694                                 dest1[14] = dest1[15] = (brg1 & 0x01) ? color : 0;
2695                         }
2696                         if(!hireso) {
2697                                 if(config.scan_line) {
2698                                         memset(graph[y + 1], 0, 640);
2699                                 } else {
2700                                         memcpy(graph[y + 1], graph[y], 640);
2701                                 }
2702                         }
2703                 }
2704         } else {
2705                 for(int y = 0, addr = 0; y < 400; y += 2) {
2706                         for(int x = 0, cx = 0; x < 640; x += 16, cx += 2) {
2707                                 uint8_t color_l = text_color[y >> 1][cx + 0];
2708                                 uint8_t color_r = text_color[y >> 1][cx + 1];
2709                                 uint8_t brg0 = gvram_b0[addr] | gvram_r0[addr] | gvram_g0[addr] |
2710                                                gvram_b1[addr] | gvram_r1[addr] | gvram_g1[addr];
2711                                 uint8_t brg1 = 0;
2712                                 if(text_reverse[y >> 1][cx + 0]) {
2713                                         brg0 ^= 0xf0;
2714                                         brg0 ^= 0xf0;
2715                                 }
2716                                 if(text_reverse[y >> 1][cx + 1]) {
2717                                         brg1 ^= 0x0f;
2718                                         brg1 ^= 0x0f;
2719                                 }
2720                                 addr++;
2721                                 uint8_t *dest0 = &graph[y    ][x];
2722                                 uint8_t *dest1 = &graph[y + 1][x];
2723                                 dest0[ 0] = dest0[ 1] = (brg0 & 0x80) ? color_l : 0;
2724                                 dest0[ 2] = dest0[ 3] = (brg0 & 0x40) ? color_l : 0;
2725                                 dest0[ 4] = dest0[ 5] = (brg0 & 0x20) ? color_l : 0;
2726                                 dest0[ 6] = dest0[ 7] = (brg0 & 0x10) ? color_l : 0;
2727                                 dest0[ 8] = dest0[ 9] = (brg0 & 0x08) ? color_r : 0;
2728                                 dest0[10] = dest0[11] = (brg0 & 0x04) ? color_r : 0;
2729                                 dest0[12] = dest0[13] = (brg0 & 0x02) ? color_r : 0;
2730                                 dest0[14] = dest0[15] = (brg0 & 0x01) ? color_r : 0;
2731                                 if(!hireso) continue;
2732                                 dest1[ 0] = dest1[ 1] = (brg1 & 0x80) ? color_l : 0;
2733                                 dest1[ 2] = dest1[ 3] = (brg1 & 0x40) ? color_l : 0;
2734                                 dest1[ 4] = dest1[ 5] = (brg1 & 0x20) ? color_l : 0;
2735                                 dest1[ 6] = dest1[ 7] = (brg1 & 0x10) ? color_l : 0;
2736                                 dest1[ 8] = dest1[ 9] = (brg1 & 0x08) ? color_r : 0;
2737                                 dest1[10] = dest1[11] = (brg1 & 0x04) ? color_r : 0;
2738                                 dest1[12] = dest1[13] = (brg1 & 0x02) ? color_r : 0;
2739                                 dest1[14] = dest1[15] = (brg1 & 0x01) ? color_r : 0;
2740                         }
2741                         if(!hireso) {
2742                                 if(config.scan_line) {
2743                                         memset(graph[y + 1], 0, 640);
2744                                 } else {
2745                                         memcpy(graph[y + 1], graph[y], 640);
2746                                 }
2747                         }
2748                 }
2749         }
2750 }
2751 #endif
2752
2753 bool PC88::draw_640x200_color_graph()
2754 {
2755 #if defined(_PC8001SR)
2756         if(!Port31_GRAPH || Port53_G0DS) {
2757 #else
2758         if(!Port31_GRAPH/* || (Port53_G0DS && Port53_G1DS && Port53_G2DS)*/) {
2759 #endif
2760                 memset(graph, 0, sizeof(graph));
2761                 return false;
2762         }
2763         uint8_t *gvram_b = /*Port53_G0DS ? gvram_null : */(gvram + 0x0000);
2764         uint8_t *gvram_r = /*Port53_G1DS ? gvram_null : */(gvram + 0x4000);
2765         uint8_t *gvram_g = /*Port53_G2DS ? gvram_null : */(gvram + 0x8000);
2766         
2767         for(int y = 0, addr = 0; y < 400; y += 2) {
2768                 for(int x = 0; x < 640; x += 8) {
2769                         uint8_t b = gvram_b[addr];
2770                         uint8_t r = gvram_r[addr];
2771                         uint8_t g = gvram_g[addr];
2772                         addr++;
2773                         uint8_t *dest = &graph[y][x];
2774                         dest[0] = ((b & 0x80) >> 7) | ((r & 0x80) >> 6) | ((g & 0x80) >> 5);
2775                         dest[1] = ((b & 0x40) >> 6) | ((r & 0x40) >> 5) | ((g & 0x40) >> 4);
2776                         dest[2] = ((b & 0x20) >> 5) | ((r & 0x20) >> 4) | ((g & 0x20) >> 3);
2777                         dest[3] = ((b & 0x10) >> 4) | ((r & 0x10) >> 3) | ((g & 0x10) >> 2);
2778                         dest[4] = ((b & 0x08) >> 3) | ((r & 0x08) >> 2) | ((g & 0x08) >> 1);
2779                         dest[5] = ((b & 0x04) >> 2) | ((r & 0x04) >> 1) | ((g & 0x04)     );
2780                         dest[6] = ((b & 0x02) >> 1) | ((r & 0x02)     ) | ((g & 0x02) << 1);
2781                         dest[7] = ((b & 0x01)     ) | ((r & 0x01) << 1) | ((g & 0x01) << 2);
2782                 }
2783                 if(config.scan_line) {
2784                         memset(graph[y + 1], 0, 640);
2785                 } else {
2786                         memcpy(graph[y + 1], graph[y], 640);
2787                 }
2788         }
2789         return true;
2790 }
2791
2792 void PC88::draw_640x200_mono_graph()
2793 {
2794         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2795                 memset(graph, 0, sizeof(graph));
2796                 return;
2797         }
2798         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2799         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2800         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2801         
2802         for(int y = 0, addr = 0; y < 400; y += 2) {
2803                 for(int x = 0; x < 640; x += 8) {
2804                         uint8_t brg = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2805                         addr++;
2806                         uint8_t *dest = &graph[y][x];
2807                         dest[0] = (brg & 0x80) >> 7;
2808                         dest[1] = (brg & 0x40) >> 6;
2809                         dest[2] = (brg & 0x20) >> 5;
2810                         dest[3] = (brg & 0x10) >> 4;
2811                         dest[4] = (brg & 0x08) >> 3;
2812                         dest[5] = (brg & 0x04) >> 2;
2813                         dest[6] = (brg & 0x02) >> 1;
2814                         dest[7] = (brg & 0x01)     ;
2815                 }
2816                 if(config.scan_line) {
2817                         memset(graph[y + 1], 0, 640);
2818                 } else {
2819                         memcpy(graph[y + 1], graph[y], 640);
2820                 }
2821         }
2822 }
2823
2824 void PC88::draw_640x200_attrib_graph()
2825 {
2826         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2827                 memset(graph, 0, sizeof(graph));
2828                 return;
2829         }
2830         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2831         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2832         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2833         
2834         for(int y = 0, addr = 0; y < 400; y += 2) {
2835                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2836                         uint8_t color = text_color[y >> 1][cx];
2837                         uint8_t brg0 = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2838                         uint8_t brg1 = 0;
2839                         if(text_reverse[y >> 1][cx]) {
2840                                 brg0 ^= 0xff;
2841                                 brg1 ^= 0xff;
2842                         }
2843                         addr++;
2844                         uint8_t *dest0 = &graph[y    ][x];
2845                         uint8_t *dest1 = &graph[y + 1][x];
2846                         dest0[0] = (brg0 & 0x80) ? color : 0;
2847                         dest0[1] = (brg0 & 0x40) ? color : 0;
2848                         dest0[2] = (brg0 & 0x20) ? color : 0;
2849                         dest0[3] = (brg0 & 0x10) ? color : 0;
2850                         dest0[4] = (brg0 & 0x08) ? color : 0;
2851                         dest0[5] = (brg0 & 0x04) ? color : 0;
2852                         dest0[6] = (brg0 & 0x02) ? color : 0;
2853                         dest0[7] = (brg0 & 0x01) ? color : 0;
2854                         if(!hireso) continue;
2855                         dest1[0] = (brg1 & 0x80) ? color : 0;
2856                         dest1[1] = (brg1 & 0x40) ? color : 0;
2857                         dest1[2] = (brg1 & 0x20) ? color : 0;
2858                         dest1[3] = (brg1 & 0x10) ? color : 0;
2859                         dest1[4] = (brg1 & 0x08) ? color : 0;
2860                         dest1[5] = (brg1 & 0x04) ? color : 0;
2861                         dest1[6] = (brg1 & 0x02) ? color : 0;
2862                         dest1[7] = (brg1 & 0x01) ? color : 0;
2863                 }
2864                 if(!hireso) {
2865                         if(config.scan_line) {
2866                                 memset(graph[y + 1], 0, 640);
2867                         } else {
2868                                 memcpy(graph[y + 1], graph[y], 640);
2869                         }
2870                 }
2871         }
2872 }
2873
2874 #if !defined(_PC8001SR)
2875 void PC88::draw_640x400_mono_graph()
2876 {
2877         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2878                 memset(graph, 0, sizeof(graph));
2879                 return;
2880         }
2881         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2882         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2883         
2884         for(int y = 0, addr = 0; y < 200; y++) {
2885                 for(int x = 0; x < 640; x += 8) {
2886                         uint8_t b = gvram_b[addr];
2887                         addr++;
2888                         uint8_t *dest = &graph[y][x];
2889                         dest[0] = (b & 0x80) >> 7;
2890                         dest[1] = (b & 0x40) >> 6;
2891                         dest[2] = (b & 0x20) >> 5;
2892                         dest[3] = (b & 0x10) >> 4;
2893                         dest[4] = (b & 0x08) >> 3;
2894                         dest[5] = (b & 0x04) >> 2;
2895                         dest[6] = (b & 0x02) >> 1;
2896                         dest[7] = (b & 0x01)     ;
2897                 }
2898         }
2899         for(int y = 200, addr = 0; y < 400; y++) {
2900                 for(int x = 0; x < 640; x += 8) {
2901                         uint8_t r = gvram_r[addr];
2902                         addr++;
2903                         uint8_t *dest = &graph[y][x];
2904                         dest[0] = (r & 0x80) >> 7;
2905                         dest[1] = (r & 0x40) >> 6;
2906                         dest[2] = (r & 0x20) >> 5;
2907                         dest[3] = (r & 0x10) >> 4;
2908                         dest[4] = (r & 0x08) >> 3;
2909                         dest[5] = (r & 0x04) >> 2;
2910                         dest[6] = (r & 0x02) >> 1;
2911                         dest[7] = (r & 0x01)     ;
2912                 }
2913         }
2914 }
2915
2916 void PC88::draw_640x400_attrib_graph()
2917 {
2918         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2919                 memset(graph, 0, sizeof(graph));
2920                 return;
2921         }
2922         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2923         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2924         
2925         for(int y = 0, addr = 0; y < 200; y++) {
2926                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2927                         uint8_t color = text_color[y >> 1][cx];
2928                         uint8_t b = gvram_b[addr];
2929                         if(text_reverse[y >> 1][cx]) {
2930                                 b ^= 0xff;
2931                         }
2932                         addr++;
2933                         uint8_t *dest = &graph[y][x];
2934                         dest[0] = (b & 0x80) ? color : 0;
2935                         dest[1] = (b & 0x40) ? color : 0;
2936                         dest[2] = (b & 0x20) ? color : 0;
2937                         dest[3] = (b & 0x10) ? color : 0;
2938                         dest[4] = (b & 0x08) ? color : 0;
2939                         dest[5] = (b & 0x04) ? color : 0;
2940                         dest[6] = (b & 0x02) ? color : 0;
2941                         dest[7] = (b & 0x01) ? color : 0;
2942                 }
2943         }
2944         for(int y = 200, addr = 0; y < 400; y++) {
2945                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2946                         uint8_t color = text_color[y >> 1][cx];
2947                         uint8_t r = gvram_r[addr];
2948                         if(text_reverse[y >> 1][cx]) {
2949                                 r ^= 0xff;
2950                         }
2951                         addr++;
2952                         uint8_t *dest = &graph[y][x];
2953                         dest[0] = (r & 0x80) ? color : 0;
2954                         dest[1] = (r & 0x40) ? color : 0;
2955                         dest[2] = (r & 0x20) ? color : 0;
2956                         dest[3] = (r & 0x10) ? color : 0;
2957                         dest[4] = (r & 0x08) ? color : 0;
2958                         dest[5] = (r & 0x04) ? color : 0;
2959                         dest[6] = (r & 0x02) ? color : 0;
2960                         dest[7] = (r & 0x01) ? color : 0;
2961                 }
2962         }
2963 }
2964 #endif
2965
2966 void PC88::request_intr(int level, bool status)
2967 {
2968         uint8_t bit = 1 << level;
2969         
2970         if(status) {
2971                 // for Nobunaga Fuunroku Opening & MID-GARTS Opening (XM8 version 1.00)
2972 //              bit &= intr_mask2;
2973                 if(!(intr_req & bit)) {
2974                         intr_req |= bit;
2975                         update_intr();
2976                 }
2977         } else {
2978                 if(intr_req & bit) {
2979                         intr_req &= ~bit;
2980                         update_intr();
2981                 }
2982         }
2983 }
2984
2985 void PC88::update_intr()
2986 {
2987         d_cpu->set_intr_line(((intr_req & intr_mask1 & intr_mask2) != 0), true, 0);
2988 }
2989
2990 uint32_t PC88::get_intr_ack()
2991 {
2992         uint8_t ai = intr_req & intr_mask1 & intr_mask2;
2993         
2994         for(int i = 0; i < 8; i++, ai >>= 1) {
2995                 if(ai & 1) {
2996                         intr_req &= ~(1 << i);
2997                         intr_mask1 = 0;
2998                         return i * 2;
2999                 }
3000         }
3001         return 0;
3002 }
3003
3004 void PC88::notify_intr_ei()
3005 {
3006         update_intr();
3007 }
3008
3009 /* ----------------------------------------------------------------------------
3010         CRTC (uPD3301)
3011 ---------------------------------------------------------------------------- */
3012
3013 void pc88_crtc_t::reset(bool hireso)
3014 {
3015         blink.rate = 24;
3016         cursor.type = cursor.mode = -1;
3017         cursor.x = cursor.y = -1;
3018         attrib.data = 0xe0;
3019         attrib.num = 20;
3020         width = 80;
3021         height = 25;
3022         char_height = hireso ? 16 : 8;
3023         skip_line = false;
3024         vretrace = hireso ? 3 : 7;
3025         timing_changed = false;
3026         reverse = 0;
3027         intr_mask = 3;
3028 }
3029
3030 void pc88_crtc_t::write_cmd(uint8_t data)
3031 {
3032         cmd = (data >> 5) & 7;
3033         cmd_ptr = 0;
3034         switch(cmd) {
3035         case 0: // reset
3036                 status &= ~0x16;
3037                 status |= 0x80; // fix
3038                 cursor.x = cursor.y = -1;
3039                 break;
3040         case 1: // start display
3041                 reverse = data & 1;
3042 //              status |= 0x10;
3043                 status |= 0x90; // fix
3044                 status &= ~8;
3045                 break;
3046         case 2: // set interrupt mask
3047                 if(!(data & 1)) {
3048 //                      status = 0; // from M88
3049                         status = 0x80; // fix
3050                 }
3051                 intr_mask = data & 3;
3052                 break;
3053         case 3: // read light pen
3054                 status &= ~1;
3055                 break;
3056         case 4: // load cursor position ON/OFF
3057                 cursor.type = (data & 1) ? cursor.mode : -1;
3058                 break;
3059         case 5: // reset interrupt
3060                 status &= ~6;
3061                 break;
3062         case 6: // reset counters
3063                 status &= ~6;
3064                 break;
3065         }
3066 }
3067
3068 void pc88_crtc_t::write_param(uint8_t data)
3069 {
3070         switch(cmd) {
3071         case 0:
3072                 switch(cmd_ptr) {
3073                 case 0:
3074                         width = min((data & 0x7f) + 2, 80);
3075                         break;
3076                 case 1:
3077                         if(height != (data & 0x3f) + 1) {
3078                                 height = (data & 0x3f) + 1;
3079                                 timing_changed = true;
3080                         }
3081                         blink.rate = 32 * ((data >> 6) + 1);
3082                         break;
3083                 case 2:
3084                         if(char_height != (data & 0x1f) + 1) {
3085                                 char_height = (data & 0x1f) + 1;
3086                                 timing_changed = true;
3087                         }
3088                         cursor.mode = (data >> 5) & 3;
3089                         skip_line = ((data & 0x80) != 0);
3090                         break;
3091                 case 3:
3092                         if(vretrace != ((data >> 5) & 7) + 1) {
3093                                 vretrace = ((data >> 5) & 7) + 1;
3094                                 timing_changed = true;
3095                         }
3096                         break;
3097                 case 4:
3098                         mode = (data >> 5) & 7;
3099                         attrib.num = (mode & 1) ? 0 : min((data & 0x1f) + 1, 20);
3100                         break;
3101                 }
3102                 break;
3103         case 4:
3104                 switch(cmd_ptr) {
3105                 case 0:
3106                         cursor.x = data;
3107                         break;
3108                 case 1:
3109                         cursor.y = data;
3110                         break;
3111                 }
3112                 break;
3113         case 6:
3114                 status = 0;
3115                 break;
3116         }
3117         cmd_ptr++;
3118 }
3119
3120 uint32_t pc88_crtc_t::read_param()
3121 {
3122         uint32_t val = 0xff;
3123         
3124         switch(cmd) {
3125         case 3: // read light pen
3126                 switch(cmd_ptr) {
3127                 case 0:
3128                         val = 0; // fix me
3129                         break;
3130                 case 1:
3131                         val = 0; // fix me
3132                         break;
3133                 }
3134                 break;
3135         default:
3136                 // XM8 version 1.10
3137                 val = read_status();
3138                 break;
3139         }
3140         cmd_ptr++;
3141         return val;
3142 }
3143
3144 uint32_t pc88_crtc_t::read_status()
3145 {
3146         if(status & 8) {
3147                 return status & ~0x10;
3148         } else {
3149                 return status;
3150         }
3151 }
3152
3153 void pc88_crtc_t::start()
3154 {
3155         memset(buffer, 0, sizeof(buffer));
3156         buffer_ptr = 0;
3157         vblank = false;
3158 }
3159
3160 void pc88_crtc_t::finish()
3161 {
3162         if((status & 0x10) && !(intr_mask & 1)) {
3163                 status |= 2;
3164         }
3165         vblank = true;
3166 }
3167
3168 void pc88_crtc_t::write_buffer(uint8_t data)
3169 {
3170         buffer[(buffer_ptr++) & 0x3fff] = data;
3171 }
3172
3173 uint8_t pc88_crtc_t::read_buffer(int ofs)
3174 {
3175         if(ofs < buffer_ptr) {
3176                 return buffer[ofs];
3177         }
3178         // dma underrun occurs !!!
3179         status |= 8;
3180 //      status &= ~0x10;
3181         return 0;
3182 }
3183
3184 void pc88_crtc_t::update_blink()
3185 {
3186         // from m88
3187         if(++blink.counter > blink.rate) {
3188                 blink.counter = 0;
3189         }
3190         blink.attrib = (blink.counter < blink.rate / 4) ? 2 : 0;
3191         blink.cursor = (blink.counter <= blink.rate / 4) || (blink.rate / 2 <= blink.counter && blink.counter <= 3 * blink.rate / 4);
3192 }
3193
3194 void pc88_crtc_t::expand_buffer(bool hireso, bool line400)
3195 {
3196         int char_height_tmp = char_height;
3197         int exitline = -1;
3198         
3199         if(!hireso) {
3200                 char_height_tmp <<= 1;
3201         }
3202         if(line400 || !skip_line) {
3203                 char_height_tmp >>= 1;
3204         }
3205         if(!(status & 0x10)) {
3206                 exitline = 0;
3207                 goto underrun;
3208         }
3209         for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3210                 for(int cx = 0; cx < width; cx++) {
3211                         text.expand[cy][cx] = read_buffer(ofs + cx);
3212                 }
3213                 if((status & 8) && exitline == -1) {
3214                         exitline = cy;
3215 //                      goto underrun;
3216                 }
3217         }
3218         if(mode & 4) {
3219                 // non transparent
3220                 for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3221                         for(int cx = 0; cx < width; cx += 2) {
3222                                 set_attrib(read_buffer(ofs + cx + 1));
3223                                 attrib.expand[cy][cx] = attrib.expand[cy][cx + 1] = attrib.data;
3224                         }
3225                         if((status & 8) && exitline == -1) {
3226                                 exitline = cy;
3227 //                              goto underrun;
3228                         }
3229                 }
3230         } else {
3231                 // transparent
3232                 if(mode & 1) {
3233                         memset(attrib.expand, 0xe0, sizeof(attrib.expand));
3234                 } else {
3235                         for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3236                                 uint8_t flags[128];
3237                                 memset(flags, 0, sizeof(flags));
3238                                 for(int i = 2 * (attrib.num - 1); i >= 0; i -= 2) {
3239                                         flags[read_buffer(ofs + i + 80) & 0x7f] = 1;
3240                                 }
3241                                 attrib.data &= 0xf3; // for PC-8801mkIIFR \95t\91®\83f\83\82
3242                                 
3243                                 for(int cx = 0, pos = 0; cx < width; cx++) {
3244                                         if(flags[cx]) {
3245                                                 set_attrib(read_buffer(ofs + pos + 81));
3246                                                 pos += 2;
3247                                         }
3248                                         attrib.expand[cy][cx] = attrib.data;
3249                                 }
3250                                 if((status & 8) && exitline == -1) {
3251                                         exitline = cy;
3252 //                                      goto underrun;
3253                                 }
3254                         }
3255                 }
3256         }
3257         if(cursor.x < 80 && cursor.y < 200) {
3258                 if((cursor.type & 1) && blink.cursor) {
3259                         // no cursor
3260                 } else {
3261                         static const uint8_t ctype[5] = {0, 8, 8, 1, 1};
3262                         attrib.expand[cursor.y][cursor.x] ^= ctype[cursor.type + 1];
3263                 }
3264         }
3265         // only burst mode
3266 underrun:
3267         if(exitline != -1) {
3268                 for(int cy = exitline; cy < 200; cy++) {
3269                         memset(&text.expand[cy][0], 0, width);
3270 #if 1
3271                         // SORCERIAN Music Library ver-2.1
3272                         memset(&attrib.expand[cy][0], 0xe0, width); // color=7
3273 #else
3274                         // from ePC-8801MA\89ü
3275                         memset(&attrib.expand[cy][0], 0x00, width);
3276 #endif
3277                 }
3278         }
3279 }
3280
3281 void pc88_crtc_t::set_attrib(uint8_t code)
3282 {
3283         if(mode & 2) {
3284                 // color
3285                 if(code & 8) {
3286                         attrib.data = (attrib.data & 0x0f) | (code & 0xf0);
3287                 } else {
3288                         attrib.data = (attrib.data & 0xf0) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
3289                         attrib.data ^= reverse;
3290                         attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
3291                 }
3292         } else {
3293                 attrib.data = 0xe0 | ((code >> 3) & 0x10) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
3294                 attrib.data ^= reverse;
3295                 attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
3296         }
3297 }
3298
3299 /* ----------------------------------------------------------------------------
3300         DMAC (uPD8257)
3301 ---------------------------------------------------------------------------- */
3302
3303 void pc88_dmac_t::write_io8(uint32_t addr, uint32_t data)
3304 {
3305         int c = (addr >> 1) & 3;
3306         
3307         switch(addr & 0x0f) {
3308         case 0:
3309         case 2:
3310         case 4:
3311         case 6:
3312                 if(!high_low) {
3313                         if((mode & 0x80) && c == 2) {
3314                                 ch[3].addr.b.l = data;
3315                         }
3316                         ch[c].addr.b.l = data;
3317                 } else {
3318                         if((mode & 0x80) && c == 2) {
3319                                 ch[3].addr.b.h = data;
3320                                 ch[3].addr.b.h2 = ch[3].addr.b.h3 = 0;
3321                         }
3322                         ch[c].addr.b.h = data;
3323                         ch[c].addr.b.h2 = ch[c].addr.b.h3 = 0;
3324                 }
3325                 high_low = !high_low;
3326                 break;
3327         case 1:
3328         case 3:
3329         case 5:
3330         case 7:
3331                 if(!high_low) {
3332                         if((mode & 0x80) && c == 2) {
3333                                 ch[3].count.b.l = data;
3334                         }
3335                         ch[c].count.b.l = data;
3336                 } else {
3337                         if((mode & 0x80) && c == 2) {
3338                                 ch[3].count.b.h = data & 0x3f;
3339                                 ch[3].count.b.h2 = ch[3].count.b.h3 = 0;
3340                                 ch[3].mode = data & 0xc0;
3341                         }
3342                         ch[c].count.b.h = data & 0x3f;
3343                         ch[c].count.b.h2 = ch[c].count.b.h3 = 0;
3344                         ch[c].mode = data & 0xc0;
3345                 }
3346                 high_low = !high_low;
3347                 break;
3348         case 8:
3349                 mode = data;
3350                 high_low = false;
3351                 break;
3352         }
3353 }
3354
3355 uint32_t pc88_dmac_t::read_io8(uint32_t addr)
3356 {
3357         uint32_t val = 0xff;
3358         int c = (addr >> 1) & 3;
3359         
3360         switch(addr & 0x0f) {
3361         case 0:
3362         case 2:
3363         case 4:
3364         case 6:
3365                 if(!high_low) {
3366                         val = ch[c].addr.b.l;
3367                 } else {
3368                         val = ch[c].addr.b.h;
3369                 }
3370                 high_low = !high_low;
3371                 break;
3372         case 1:
3373         case 3:
3374         case 5:
3375         case 7:
3376                 if(!high_low) {
3377                         val = ch[c].count.b.l;
3378                 } else {
3379                         val = (ch[c].count.b.h & 0x3f) | ch[c].mode;
3380                 }
3381                 high_low = !high_low;
3382                 break;
3383         case 8:
3384                 val = status;
3385                 status &= 0xf0;
3386 //              high_low = false;
3387                 break;
3388         }
3389         return val;
3390 }
3391
3392 void pc88_dmac_t::start(int c)
3393 {
3394         if(mode & (1 << c)) {
3395                 status &= ~(1 << c);
3396                 ch[c].running = true;
3397         } else {
3398                 ch[c].running = false;
3399         }
3400 }
3401
3402 void pc88_dmac_t::run(int c, int nbytes)
3403 {
3404         if(ch[c].running) {
3405                 while(nbytes > 0 && ch[c].count.sd >= 0) {
3406                         if(ch[c].mode == 0x80) {
3407                                 ch[c].io->write_dma_io8(0, mem->read_dma_data8(ch[c].addr.w.l));
3408                         } else if(ch[c].mode == 0x40) {
3409                                 mem->write_dma_data8(ch[c].addr.w.l, ch[c].io->read_dma_io8(0));
3410                         } else if(ch[c].mode == 0x00) {
3411                                 ch[c].io->read_dma_io8(0); // verify
3412                         }
3413                         ch[c].addr.sd++;
3414                         ch[c].count.sd--;
3415                         nbytes--;
3416                 }
3417                 if(ch[c].count.sd < 0) {
3418                         finish(c);
3419                 }
3420         }
3421 }
3422
3423 void pc88_dmac_t::finish(int c)
3424 {
3425         if(ch[c].running) {
3426                 while(ch[c].count.sd >= 0) {
3427                         if(ch[c].mode == 0x80) {
3428                                 ch[c].io->write_dma_io8(0, mem->read_dma_data8(ch[c].addr.w.l));
3429                         } else if(ch[c].mode == 0x40) {
3430                                 mem->write_dma_data8(ch[c].addr.w.l, ch[c].io->read_dma_io8(0));
3431                         } else if(ch[c].mode == 0x00) {
3432                                 ch[c].io->read_dma_io8(0); // verify
3433                         }
3434                         ch[c].addr.sd++;
3435                         ch[c].count.sd--;
3436                 }
3437                 if((mode & 0x80) && c == 2) {
3438                         ch[2].addr.sd = ch[3].addr.sd;
3439                         ch[2].count.sd = ch[3].count.sd;
3440                         ch[2].mode = ch[3].mode;
3441                 } else if(mode & 0x40) {
3442                         mode &= ~(1 << c);
3443                 }
3444                 status |= (1 << c);
3445                 ch[c].running = false;
3446         }
3447 }
3448
3449 #define STATE_VERSION   10
3450
3451 bool PC88::process_state(FILEIO* state_fio, bool loading)
3452 {
3453         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
3454                 return false;
3455         }
3456         if(!state_fio->StateCheckInt32(this_device_id)) {
3457                 return false;
3458         }
3459         state_fio->StateArray(ram, sizeof(ram), 1);
3460 #if defined(PC88_EXRAM_BANKS)
3461         state_fio->StateArray(exram, sizeof(exram), 1);
3462 #endif
3463         state_fio->StateArray(gvram, sizeof(gvram), 1);
3464         state_fio->StateArray(tvram, sizeof(tvram), 1);
3465         state_fio->StateArray(port, sizeof(port), 1);
3466         state_fio->StateValue(crtc.blink.rate);
3467         state_fio->StateValue(crtc.blink.counter);
3468         state_fio->StateValue(crtc.blink.cursor);
3469         state_fio->StateValue(crtc.blink.attrib);
3470         state_fio->StateValue(crtc.cursor.type);
3471         state_fio->StateValue(crtc.cursor.mode);
3472         state_fio->StateValue(crtc.cursor.x);
3473         state_fio->StateValue(crtc.cursor.y);
3474         state_fio->StateValue(crtc.attrib.data);
3475         state_fio->StateValue(crtc.attrib.num);
3476         state_fio->StateArray(&crtc.attrib.expand[0][0], sizeof(crtc.attrib.expand), 1);
3477         state_fio->StateArray(&crtc.text.expand[0][0], sizeof(crtc.text.expand), 1);
3478         state_fio->StateValue(crtc.width);
3479         state_fio->StateValue(crtc.height);
3480         state_fio->StateValue(crtc.char_height);
3481         state_fio->StateValue(crtc.skip_line);
3482         state_fio->StateValue(crtc.vretrace);
3483         state_fio->StateValue(crtc.timing_changed);
3484         state_fio->StateArray(crtc.buffer, sizeof(crtc.buffer), 1);
3485         state_fio->StateValue(crtc.buffer_ptr);
3486         state_fio->StateValue(crtc.cmd);
3487         state_fio->StateValue(crtc.cmd_ptr);
3488         state_fio->StateValue(crtc.mode);
3489         state_fio->StateValue(crtc.reverse);
3490         state_fio->StateValue(crtc.intr_mask);
3491         state_fio->StateValue(crtc.status);
3492         state_fio->StateValue(crtc.vblank);
3493         for(int i = 0; i < array_length(dmac.ch); i++) {
3494                 state_fio->StateValue(dmac.ch[i].addr);
3495                 state_fio->StateValue(dmac.ch[i].count);
3496                 state_fio->StateValue(dmac.ch[i].mode);
3497                 state_fio->StateValue(dmac.ch[i].nbytes);
3498                 state_fio->StateValue(dmac.ch[i].running);
3499         }
3500         state_fio->StateValue(dmac.mode);
3501         state_fio->StateValue(dmac.status);
3502         state_fio->StateValue(dmac.high_low);
3503         state_fio->StateArray(alu_reg, sizeof(alu_reg), 1);
3504         state_fio->StateValue(gvram_plane);
3505         state_fio->StateValue(gvram_sel);
3506         state_fio->StateValue(cpu_clock_low);
3507 #if defined(SUPPORT_PC88_HIGH_CLOCK)
3508         state_fio->StateValue(cpu_clock_high_fe2);
3509 #endif
3510         state_fio->StateValue(mem_wait_on);
3511         state_fio->StateValue(m1_wait_clocks);
3512         state_fio->StateValue(f000_m1_wait_clocks);
3513         state_fio->StateValue(mem_wait_clocks_r);
3514         state_fio->StateValue(mem_wait_clocks_w);
3515         state_fio->StateValue(tvram_wait_clocks_r);
3516         state_fio->StateValue(tvram_wait_clocks_w);
3517         state_fio->StateValue(gvram_wait_clocks_r);
3518         state_fio->StateValue(gvram_wait_clocks_w);
3519         state_fio->StateValue(busreq_clocks);
3520         for(int i = 0; i < array_length(palette); i++) {
3521                 state_fio->StateValue(palette[i].r);
3522                 state_fio->StateValue(palette[i].g);
3523                 state_fio->StateValue(palette[i].b);
3524         }
3525 //      state_fio->StateValue(update_palette);
3526         state_fio->StateValue(hireso);
3527         state_fio->StateArray(&text[0][0], sizeof(text), 1);
3528         state_fio->StateArray(&graph[0][0], sizeof(graph), 1);
3529 /*
3530         for(int i = 0; i < 9; i++) {
3531                 state_fio->StateValue(palette_digital[i].b);
3532                 state_fio->StateValue(palette_digital[i].r);
3533                 state_fio->StateValue(palette_digital[i].g);
3534                 state_fio->StateValue(palette_analog [i].b);
3535                 state_fio->StateValue(palette_analog [i].r);
3536                 state_fio->StateValue(palette_analog [i].g);
3537         }
3538 */
3539         state_fio->StateValue(usart_dcd);
3540         state_fio->StateValue(opn_busy);
3541         state_fio->StateValue(key_caps);
3542         state_fio->StateValue(key_kana);
3543 #ifdef SUPPORT_PC88_JOYSTICK
3544         state_fio->StateValue(mouse_strobe_clock);
3545         state_fio->StateValue(mouse_strobe_clock_lim);
3546         state_fio->StateValue(mouse_phase);
3547         state_fio->StateValue(mouse_dx);
3548         state_fio->StateValue(mouse_dy);
3549         state_fio->StateValue(mouse_lx);
3550         state_fio->StateValue(mouse_ly);
3551 #endif
3552         state_fio->StateValue(intr_req);
3553         state_fio->StateValue(intr_req_sound);
3554 #ifdef SUPPORT_PC88_SB2
3555         state_fio->StateValue(intr_req_sb2);
3556 #endif
3557         state_fio->StateValue(intr_mask1);
3558         state_fio->StateValue(intr_mask2);
3559         state_fio->StateValue(cmt_play);
3560         state_fio->StateValue(cmt_rec);
3561         state_fio->StateArray(rec_file_path, sizeof(rec_file_path), 1);
3562         if(loading) {
3563                 int length_tmp = state_fio->FgetInt32_LE();
3564                 if(cmt_rec) {
3565                         cmt_fio->Fopen(rec_file_path, FILEIO_READ_WRITE_NEW_BINARY);
3566                         while(length_tmp != 0) {
3567                                 uint8_t buffer[1024];
3568                                 int length_rw = min(length_tmp, (int)sizeof(buffer));
3569                                 state_fio->Fread(buffer, length_rw, 1);
3570                                 if(cmt_fio->IsOpened()) {
3571                                         cmt_fio->Fwrite(buffer, length_rw, 1);
3572                                 }
3573                                 length_tmp -= length_rw;
3574                         }
3575                 }
3576         } else {
3577                 if(cmt_rec && cmt_fio->IsOpened()) {
3578                         int length_tmp = (int)cmt_fio->Ftell();
3579                         cmt_fio->Fseek(0, FILEIO_SEEK_SET);
3580                         state_fio->FputInt32_LE(length_tmp);
3581                         while(length_tmp != 0) {
3582                                 uint8_t buffer[1024];
3583                                 int length_rw = min(length_tmp, (int)sizeof(buffer));
3584                                 cmt_fio->Fread(buffer, length_rw, 1);
3585                                 state_fio->Fwrite(buffer, length_rw, 1);
3586                                 length_tmp -= length_rw;
3587                         }
3588                 } else {
3589                         state_fio->FputInt32_LE(0);
3590                 }
3591         }
3592         state_fio->StateValue(cmt_bufptr);
3593         state_fio->StateValue(cmt_bufcnt);
3594         state_fio->StateArray(cmt_buffer, sizeof(cmt_buffer), 1);
3595         state_fio->StateArray(cmt_data_carrier, sizeof(cmt_data_carrier), 1);
3596         state_fio->StateValue(cmt_data_carrier_cnt);
3597         state_fio->StateValue(cmt_register_id);
3598         state_fio->StateValue(beep_on);
3599         state_fio->StateValue(beep_signal);
3600         state_fio->StateValue(sing_signal);
3601 #ifdef SUPPORT_PC88_PCG8100
3602         state_fio->StateValue(pcg_addr);
3603         state_fio->StateValue(pcg_data);
3604         state_fio->StateValue(pcg_ctrl);
3605         state_fio->StateArray(pcg_pattern, sizeof(pcg_pattern), 1);
3606 #endif
3607 #ifdef SUPPORT_PC88_CDROM
3608         state_fio->StateValue(cdda_register_id);
3609         state_fio->StateValue(cdda_volume);
3610 #endif
3611 #ifdef NIPPY_PATCH
3612         state_fio->StateValue(nippy_patch);
3613 #endif
3614         
3615         // post process
3616         if(loading) {
3617 #if defined(_PC8001SR)
3618                 update_n80_write();
3619                 update_n80_read();
3620 #else
3621                 update_low_memmap();
3622                 update_tvram_memmap();
3623 #endif
3624                 // force update palette when state file is loaded
3625                 update_palette = true;
3626         }
3627         return true;
3628 }
3629 }
3630