OSDN Git Service

[General] Merge Upstream 2018-12-27.
[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                                 }
1052                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1053                                 break;
1054                         case 2:
1055                         case 3:
1056                                 if(cdda_register_id != -1) {
1057                                         cancel_event(this, cdda_register_id);
1058                                 }
1059                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1060                                 break;
1061                         case 4:
1062                                 if(cdda_register_id != -1) {
1063                                         cancel_event(this, cdda_register_id);
1064                                 }
1065                                 register_event(this, EVENT_FADE_IN, 100, true, &cdda_register_id); // 100ms
1066                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1067                                 break;
1068                         case 5:
1069                                 if(cdda_register_id != -1) {
1070                                         cancel_event(this, cdda_register_id);
1071                                 }
1072                                 register_event(this, EVENT_FADE_IN, 1500, true, &cdda_register_id); // 1500ms
1073                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
1074                                 break;
1075                         case 6:
1076                                 if(cdda_register_id != -1) {
1077                                         cancel_event(this, cdda_register_id);
1078                                 }
1079                                 register_event(this, EVENT_FADE_OUT, 100, true, &cdda_register_id); // 100ms
1080                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1081                                 break;
1082                         case 7:
1083                                 if(cdda_register_id != -1) {
1084                                         cancel_event(this, cdda_register_id);
1085                                 }
1086                                 register_event(this, EVENT_FADE_OUT, 5000, true, &cdda_register_id); // 5000ms
1087                                 d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
1088                                 break;
1089                         }
1090                 }
1091                 break;
1092         case 0x99:
1093                 if(cdbios_loaded && (mod & 0x10)) {
1094                         update_low_memmap();
1095                 }
1096                 break;
1097 #endif
1098 #ifdef SUPPORT_PC88_SB2
1099         case 0xa8:
1100         case 0xa9:
1101                 if(d_sb2 != NULL) {
1102                         d_sb2->write_io8(addr, data);
1103                 }
1104                 break;
1105         case 0xaa:
1106                 if(mod & 0x80) {
1107                         if(intr_req_sb2 && !PortAA_S2INTM) {
1108                                 request_intr(IRQ_SOUND, true);
1109                         }
1110                 }
1111                 break;
1112 #ifdef SUPPORT_PC88_OPNA
1113         case 0xac:
1114         case 0xad:
1115                 if(d_sb2 != NULL && d_sb2->is_ym2608) {
1116                         d_sb2->write_io8(addr | 2, data);
1117                 }
1118                 break;
1119 #endif
1120 #endif
1121         case 0xe2:
1122                 if(mod & 0x11) {
1123                         update_low_memmap();
1124                 }
1125                 break;
1126         case 0xe3:
1127 #ifdef PC88_IODATA_EXRAM
1128                 if(mod) {
1129 #else
1130                 if(mod & 0x0f) {
1131 #endif
1132                         if(PortE2_RDEN || PortE2_WREN) {
1133                                 update_low_memmap();
1134                         }
1135                 }
1136                 break;
1137 #endif
1138         case 0xe4:
1139                 intr_mask1 = ~(0xff << (data < 8 ? data : 8));
1140                 update_intr();
1141                 break;
1142         case 0xe6:
1143                 // for Romancia (XM8 version 1.00)
1144                 if(intr_mask2_table[data & 7] != intr_mask2) {
1145                         intr_req &= (intr_mask2_table[data & 7] & intr_mask2);
1146                 }
1147                 intr_mask2 = intr_mask2_table[data & 7];
1148                 intr_req &= intr_mask2;
1149                 update_intr();
1150                 break;
1151 #ifdef SUPPORT_PC88_DICTIONARY
1152         case 0xf0:
1153                 // XM8 version 1.20
1154                 if(port[0xf0] >= 0x20) {
1155                         // no effect if data >= 0x20
1156                         port[0xf0] ^= mod;
1157                 }
1158                 break;
1159         case 0xf1:
1160                 // XM8 version 1.20
1161                 if(port[0xf1] != 0x00 && port[0xf1] != 0x01) {
1162                         // effect only 0x00 or 0x01
1163                         port[0xf1] ^= mod;
1164                 }
1165                 break;
1166 #endif
1167         case 0xfc:
1168         case 0xfd:
1169         case 0xfe:
1170         case 0xff:
1171                 d_pio->write_io8(addr, data);
1172                 break;
1173         }
1174 }
1175
1176 uint32_t PC88::read_io8(uint32_t addr)
1177 #ifdef _IO_DEBUG_LOG
1178 {
1179         uint32_t val = read_io8_debug(addr);
1180         this->out_debug_log(_T("%06x\tIN8\t%02x = %02x\n"), d_cpu->get_pc(), addr & 0xff, val);
1181         return val;
1182 }
1183
1184 uint32_t PC88::read_io8_debug(uint32_t addr)
1185 #endif
1186 {
1187         uint32_t val = 0xff;
1188         
1189         addr &= 0xff;
1190         switch(addr) {
1191         case 0x00:
1192         case 0x01:
1193         case 0x02:
1194         case 0x03:
1195         case 0x04:
1196         case 0x05:
1197         case 0x06:
1198         case 0x07:
1199         case 0x08:
1200         case 0x09:
1201         case 0x0a:
1202         case 0x0b:
1203         case 0x0c:
1204         case 0x0d:
1205         case 0x0e:
1206                 for(int i = 0; i < 8; i++) {
1207                         if(key_status[key_table[addr & 0x0f][i]]) {
1208                                 val &= ~(1 << i);
1209                         }
1210                 }
1211                 if(addr == 0x0e) {
1212                         val &= ~0x80; // http://www.maroon.dti.ne.jp/youkan/pc88/iomap.html
1213                 }
1214                 return val;
1215         case 0x20:
1216         case 0x21:
1217                 return d_sio->read_io8(addr);
1218 #if defined(_PC8001SR)
1219         case 0x30:
1220                 return (config.boot_mode == MODE_PC80_N ? 0 : 1) | (config.boot_mode == MODE_PC80_V2 ? 0 : 2) | 0xfc;
1221         case 0x31:
1222                 return (config.boot_mode == MODE_PC80_V2 ? 0 : 0x80) | 0x39;
1223         case 0x33:
1224                 return port[0x33];
1225 #else
1226         case 0x30:
1227 //              return (config.boot_mode == MODE_PC88_N ? 0 : 1) | 0xca; // 80x20 (XM8 version 1.00)
1228                 return (config.boot_mode == MODE_PC88_N ? 0 : 1) | 0xc2; // 80x25
1229         case 0x31:
1230                 // XM8 version 1.10
1231                 return (config.boot_mode == MODE_PC88_V2 ? 0 : 0x80) | (config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N ? 0 : 0x40) | 0x39;
1232 //              return (config.boot_mode == MODE_PC88_V2 ? 0 : 0x80) | (config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N ? 0 : 0x40);
1233         case 0x32:
1234                 return port[0x32];
1235 #endif
1236         case 0x40:
1237                 // XM8 version 1.10
1238 //              return (crtc.vblank ? 0x20 : 0) | (d_rtc->read_signal(0) ? 0x10 : 0) | (usart_dcd ? 4 : 0) | (hireso ? 0 : 2) | 0xc1;
1239                 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;
1240         case 0x44:
1241                 val = d_opn->read_io8(addr);
1242                 if(opn_busy) {
1243                         // show busy flag for first access (for ALPHA)
1244                         if(d_cpu->get_pc() == 0xe615) {
1245                                 val |= 0x80;
1246                         }
1247                         opn_busy = false;
1248                 }
1249                 return val;
1250         case 0x45:
1251                 if(Port44_OPNCH == 14) {
1252 #ifdef SUPPORT_PC88_JOYSTICK
1253                         if(config.joystick_type == DEVICE_JOYSTICK) {
1254                                 return (~(joystick_status[0] >> 0) & 0x0f) | 0xf0;
1255                         } else if(config.joystick_type == DEVICE_MOUSE) {
1256                                 switch(mouse_phase) {
1257                                 case 0:
1258                                         return ((mouse_lx >> 4) & 0x0f) | 0xf0;
1259                                 case 1:
1260                                         return ((mouse_lx >> 0) & 0x0f) | 0xf0;
1261                                 case 2:
1262                                         return ((mouse_ly >> 4) & 0x0f) | 0xf0;
1263                                 case 3:
1264                                         return ((mouse_ly >> 0) & 0x0f) | 0xf0;
1265                                 }
1266                                 return 0xf0; // ???
1267                         }
1268 #endif
1269                         return 0xff;
1270                 } else if(Port44_OPNCH == 15) {
1271 #ifdef SUPPORT_PC88_JOYSTICK
1272                         if(config.joystick_type == DEVICE_JOYSTICK) {
1273                                 return (~(joystick_status[0] >> 4) & 0x03) | 0xfc;
1274                         } else if(config.joystick_type == DEVICE_MOUSE) {
1275                                 return (~mouse_status[2] & 0x03) | 0xfc;
1276                         }
1277 #endif
1278                         return 0xff;
1279                 }
1280                 return d_opn->read_io8(addr);
1281 #ifdef SUPPORT_PC88_OPNA
1282         case 0x46:
1283         case 0x47:
1284                 if(d_opn->is_ym2608) {
1285                         return d_opn->read_io8(addr);
1286                 }
1287                 break;
1288 #endif
1289         case 0x50:
1290                 return crtc.read_param();
1291         case 0x51:
1292                 return crtc.read_status();
1293         case 0x5c:
1294                 return gvram_plane | 0xf8;
1295         case 0x60:
1296         case 0x61:
1297         case 0x62:
1298         case 0x63:
1299         case 0x64:
1300         case 0x65:
1301         case 0x66:
1302         case 0x67:
1303         case 0x68:
1304                 return dmac.read_io8(addr);
1305         case 0x6e:
1306                 // XM8 version 1.20
1307                 return (cpu_clock_low ? 0x80 : 0) | (is_sr_mr() ? 0x7f : 0x10);
1308         case 0x6f:
1309                 // XM8 version 1.20
1310                 return is_sr_mr() ? 0xff : (port[0x6f] | 0xf0);
1311 #if !defined(_PC8001SR)
1312         case 0x70:
1313                 // PC-8001mkIISR returns the constant value
1314                 // this port is used to detect PC-8001mkIISR or 8801mkIISR
1315                 return port[0x70];
1316 #endif
1317         case 0x71:
1318                 return port[0x71];
1319 #ifdef SUPPORT_PC88_HMB20
1320 //      case 0x88:
1321         case 0x89:
1322                 return d_opm->read_io8(addr);
1323 #endif
1324 #ifdef SUPPORT_PC88_CDROM
1325         // M88 cdif
1326         case 0x90:
1327                 if(cdbios_loaded) {
1328                         val  = d_scsi_host->read_signal(SIG_SCSI_BSY) ? 0x80 : 0;
1329                         val |= d_scsi_host->read_signal(SIG_SCSI_REQ) ? 0x40 : 0;
1330                         val |= d_scsi_host->read_signal(SIG_SCSI_MSG) ? 0x20 : 0;
1331                         val |= d_scsi_host->read_signal(SIG_SCSI_CD ) ? 0x10 : 0;
1332                         val |= d_scsi_host->read_signal(SIG_SCSI_IO ) ? 0x08 : 0;
1333                         // do not show BSY,MSG,CxD,IxD when SEL=1 (\90M\92·\82Ì\96ì\96\95\90\8f«\95\97\89_\98^)
1334                         if(port[0x90] & 0x01) {
1335                                 val &= ~(0x80 | 0x20 | 0x10 | 0x08);
1336                                 val |= (port[0x9f] & 0x01); // correct ???
1337                         }
1338                         #ifdef _SCSI_DEBUG_LOG
1339                                 this->out_debug_log(_T("[SCSI_PC88] Status = %02X\n"), val);
1340                         #endif
1341                         return val;
1342                 }
1343                 break;
1344         case 0x91:
1345                 if(cdbios_loaded) {
1346                         return d_scsi_host->read_dma_io8(0);
1347                 }
1348                 break;
1349         case 0x92:
1350         case 0x93:
1351         case 0x96:
1352                 if(cdbios_loaded) {
1353                         return 0x00;
1354                 }
1355                 break;
1356         case 0x99:
1357                 if(cdbios_loaded) {
1358 //                      return 0xcd; // PC-8801MC
1359                         return 0x00;
1360                 }
1361                 break;
1362         case 0x98:
1363                 if(cdbios_loaded) {
1364                         port[0x98] ^= 0x80; // clock ???
1365                         return port[0x98];
1366                 }
1367                 break;
1368         case 0x9b:
1369         case 0x9d:
1370                 if(cdbios_loaded) {
1371                         return 60;
1372                 }
1373                 break;
1374 #endif
1375 #ifdef SUPPORT_PC88_SB2
1376         case 0xa8:
1377                 if(d_sb2 != NULL) {
1378                         return d_sb2->read_io8(addr);
1379                 }
1380                 break;
1381         case 0xa9:
1382                 if(d_sb2 != NULL) {
1383                         if(PortA8_OPNCH == 14) {
1384 #ifdef SUPPORT_PC88_JOYSTICK
1385                                 if(config.joystick_type == DEVICE_JOYSTICK) {
1386                                         return (~(joystick_status[0] >> 0) & 0x0f) | 0xf0;
1387                                 } else if(config.joystick_type == DEVICE_MOUSE) {
1388                                         switch(mouse_phase) {
1389                                         case 0:
1390                                                 return ((mouse_lx >> 4) & 0x0f) | 0xf0;
1391                                         case 1:
1392                                                 return ((mouse_lx >> 0) & 0x0f) | 0xf0;
1393                                         case 2:
1394                                                 return ((mouse_ly >> 4) & 0x0f) | 0xf0;
1395                                         case 3:
1396                                                 return ((mouse_ly >> 0) & 0x0f) | 0xf0;
1397                                         }
1398                                         return 0xf0; // ???
1399                                 }
1400 #endif
1401                                 return 0xff;
1402                         } else if(PortA8_OPNCH == 15) {
1403 #ifdef SUPPORT_PC88_JOYSTICK
1404                                 if(config.joystick_type == DEVICE_JOYSTICK) {
1405                                         return (~(joystick_status[0] >> 4) & 0x03) | 0xfc;
1406                                 } else if(config.joystick_type == DEVICE_MOUSE) {
1407                                         return (~mouse_status[2] & 0x03) | 0xfc;
1408                                 }
1409 #endif
1410                                 return 0xff;
1411                         }
1412                         return d_sb2->read_io8(addr);
1413                 }
1414                 break;
1415         case 0xaa:
1416                 return (PortAA_S2INTM) | 0x7f;
1417 #ifdef SUPPORT_PC88_OPNA
1418         case 0xac:
1419         case 0xad:
1420                 if(d_sb2 != NULL && d_sb2->is_ym2608) {
1421                         d_sb2->read_io8(addr | 2);
1422                 }
1423                 break;
1424 #endif
1425 #endif
1426 #if defined(SUPPORT_PC88_VAB)
1427         // X88000
1428         case 0xb4:
1429         case 0xb5:
1430                 if(PortE3_VAB_SEL) {
1431                         return port[addr];
1432                 }
1433                 break;
1434 #endif
1435         case 0xe2:
1436                 return (~port[0xe2]) | 0xee;
1437         case 0xe3:
1438 #ifdef PC88_IODATA_EXRAM
1439                 return port[0xe3];
1440 #else
1441                 return port[0xe3] | 0xf0;
1442 #endif
1443         case 0xe8:
1444                 return kanji1[PortE8E9_KANJI1 * 2 + 1];
1445         case 0xe9:
1446                 return kanji1[PortE8E9_KANJI1 * 2];
1447         case 0xec:
1448                 return kanji2[PortECED_KANJI2 * 2 + 1];
1449         case 0xed:
1450                 return kanji2[PortECED_KANJI2 * 2];
1451         case 0xfc:
1452         case 0xfd:
1453         case 0xfe:
1454                 return d_pio->read_io8(addr);
1455         }
1456         return 0xff;
1457 }
1458
1459 uint32_t PC88::read_dma_data8(uint32_t addr)
1460 {
1461         // from ram
1462 #if defined(_PC8001SR)
1463         return ram[addr & 0xffff];
1464 #else
1465         if((addr & 0xf000) == 0xf000 && (config.boot_mode == MODE_PC88_V1H || config.boot_mode == MODE_PC88_V2)) {
1466                 return tvram[addr & 0xfff];
1467         } else {
1468                 return ram[addr & 0xffff];
1469         }
1470 #endif
1471 }
1472
1473 void PC88::write_dma_data8(uint32_t addr, uint32_t data)
1474 {
1475         // to ram
1476         ram[addr & 0xffff] = data;
1477 }
1478
1479 void PC88::write_dma_io8(uint32_t addr, uint32_t data)
1480 {
1481         // to crtc
1482         crtc.write_buffer(data);
1483 }
1484
1485 void PC88::update_timing()
1486 {
1487         int lines_per_frame = (crtc.height + crtc.vretrace) * crtc.char_height;
1488         // 56.4229Hz (25line) on PC-8801MA2 (XM8 version 1.00)
1489         double frames_per_sec = (hireso ? 24860.0 * 56.423 / 56.5 : 15980.0) / (double)lines_per_frame;
1490 //      double frames_per_sec = (hireso ? 24860.0 * 56.424 / 56.5 : 15980.0) / (double)lines_per_frame;
1491         
1492         set_frames_per_sec(frames_per_sec);
1493         set_lines_per_frame(lines_per_frame);
1494 }
1495
1496 int PC88::get_m1_wait(bool addr_f000)
1497 {
1498         // XM8 version 1.20
1499         int wait = 0;
1500         
1501 #if defined(_PC8001SR)
1502         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1503 #else
1504         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1505 #endif
1506                 // V1S or N
1507                 if(!mem_wait_on) {
1508                         // memory wait = off
1509                         if(cpu_clock_low) {
1510                                 // 4MHz
1511                                 wait += 1;
1512                         }
1513                 }
1514         } else {
1515                 // V1H or V2
1516                 if(!mem_wait_on) {
1517                         // no memory wait
1518                         if(addr_f000) {
1519                                 // TVRAM
1520                                 if(!gvram_sel && !Port32_TMODE) {
1521                                         if(cpu_clock_low) {
1522                                                 // TVRAM, 4MHz only
1523                                                 wait += 1;
1524                                         }
1525                                 }
1526                         }
1527                 }
1528         }
1529         return wait;
1530 }
1531
1532 int  PC88::get_main_wait(bool read)
1533 {
1534         // XM8 version 1.20
1535         int wait = 0;
1536         
1537         if(cpu_clock_low) {
1538                 // 4MHz
1539                 if(mem_wait_on) {
1540                         if(read) {
1541                                 // memory wait + read
1542                                 wait += 1;
1543                         }
1544                 }
1545         } else {
1546                 // 8MHz
1547 #if !defined(_PC8001SR)
1548                 if(!cpu_clock_high_fe2) {
1549                         // not 8MHzH
1550                         wait += 1;
1551                 }
1552 #endif
1553                 if(mem_wait_on) {
1554                         // memory wait (read and write)
1555                         wait += 1;
1556                 }
1557         }
1558         return wait;
1559 }
1560
1561 int PC88::get_tvram_wait(bool read)
1562 {
1563         int wait = 0;
1564         
1565         if(cpu_clock_low) {
1566                 // 4MHz
1567                 if(read) {
1568                         if(mem_wait_on) {
1569                                 // memory wait + read
1570                                 wait += 1;
1571                         }
1572                 }
1573         } else {
1574                 // 8MHz -> memory wait do not effect
1575                 if(read) {
1576                         // read
1577                         wait += 2;
1578                 } else {
1579                         // write
1580                         wait += 1;
1581                 }
1582         }
1583         return wait;
1584 }
1585
1586 int  PC88::get_gvram_wait(bool read)
1587 {
1588         // XM8 version 1.20
1589         int wait = 0;
1590         
1591         if(Port31_GRAPH) {
1592                 // graphic on
1593                 if(cpu_clock_low) {
1594                         // 4MHz
1595 #if defined(_PC8001SR)
1596                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1597 #else
1598                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1599 #endif
1600                                 // V1S
1601                                 if(!Port40_GHSM && !crtc.vblank) {
1602                                         // V1S + not GHSM, V-DISP
1603                                         if(hireso) {
1604                                                 wait += 114;
1605                                         } else {
1606                                                 wait += 68;
1607                                         }
1608                                 } else {
1609                                         if(crtc.vblank) {
1610                                                 wait += 0;
1611                                         } else {
1612                                                 wait += 2;
1613                                         }
1614                                 }
1615                         } else {
1616                                 // V1H or V2
1617                                 if(crtc.vblank) {
1618                                         wait += 0;
1619                                 } else {
1620                                         wait += 2;
1621                                 }
1622                         }
1623                 }
1624                 else {
1625                         // 8MHz
1626 #if defined(_PC8001SR)
1627                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1628 #else
1629                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1630 #endif
1631                                 // V1S
1632                                 if(!Port40_GHSM && !crtc.vblank) {
1633                                         // V1S + not GHSM, V-DISP
1634                                         if(hireso) {
1635                                                 wait += 141;
1636                                         } else {
1637                                                 wait += 90;
1638                                         }
1639                                 } else {
1640                                         if(crtc.vblank) {
1641                                                 wait += 3;
1642                                         } else {
1643                                                 wait += 5;
1644                                         }
1645                                 }
1646                         } else {
1647                                 // V1H or V2
1648                                 if(crtc.vblank) {
1649                                         wait += 3;
1650                                 } else {
1651                                         wait += 5;
1652                                 }
1653                         }
1654                 }
1655         } else {
1656                 // graphic off
1657                 if(cpu_clock_low) {
1658                         // 4MHz
1659                         if(mem_wait_on) {
1660                                 if(read) {
1661                                         // memory wait + read
1662                                         wait += 1;
1663                                 }
1664                         }
1665                 } else {
1666                         // 8MHz -> memory wait do not effect
1667                         wait += 3;
1668                 }
1669         }
1670         return wait;
1671 }
1672
1673 void PC88::update_gvram_wait()
1674 {
1675         gvram_wait_clocks_r = get_gvram_wait(true);
1676         gvram_wait_clocks_w = get_gvram_wait(false);
1677 }
1678
1679 void PC88::update_gvram_sel()
1680 {
1681 #if defined(_PC8001SR)
1682         if(Port33_GVAM) {
1683 #else
1684         if(Port32_GVAM) {
1685 #endif
1686                 if(Port35_GAM) {
1687                         gvram_sel = 8;
1688                 } else {
1689                         gvram_sel = 0;
1690                 }
1691                 gvram_plane = 0; // from M88
1692         } else {
1693                 gvram_sel = gvram_plane;
1694         }
1695         f000_m1_wait_clocks = get_m1_wait(true);
1696 }
1697
1698 #if defined(_PC8001SR)
1699 void PC88::update_n80_write()
1700 {
1701         if(PortE2_WREN || Port31_MMODE) {
1702                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1703                         SET_BANK_W(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1704                 } else {
1705                         SET_BANK_W(0x0000, 0x7fff, exram);
1706                 }
1707         } else {
1708                 SET_BANK_W(0x0000, 0x7fff, wdmy);
1709         }
1710 }
1711
1712 void PC88::update_n80_read()
1713 {
1714         if(PortE2_RDEN || Port31_MMODE) {
1715                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1716                         SET_BANK_R(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1717                 } else {
1718                         SET_BANK_R(0x0000, 0x7fff, exram);
1719                 }
1720         } else if(Port33_N80SR) {
1721                 if(port[0x71] & 1) {
1722                         SET_BANK_R(0x0000, 0x7fff, n80mk2srrom);
1723                 } else {
1724                         SET_BANK_R(0x0000, 0x5fff, n80mk2srrom);
1725                         SET_BANK_R(0x6000, 0x7fff, n80mk2srrom + 0x8000);
1726                 }
1727         } else {
1728                 if(port[0x31] & 1) {
1729                         SET_BANK_R(0x0000, 0x7fff, n80mk2rom);
1730                 } else {
1731                         SET_BANK_R(0x0000, 0x5fff, n80mk2rom);
1732                         SET_BANK_R(0x6000, 0x7fff, rdmy);
1733                 }
1734         }
1735 }
1736 #else
1737 void PC88::update_low_memmap()
1738 {
1739         // read
1740         if(PortE2_RDEN) {
1741 #ifdef PC88_EXRAM_BANKS
1742                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1743                         SET_BANK_R(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1744                 } else {
1745 #endif
1746 //                      SET_BANK_R(0x0000, 0x7fff, rdmy);
1747 #ifdef PC88_EXRAM_BANKS
1748                 }
1749 #endif
1750         } else if(Port31_MMODE) {
1751                 // 64K RAM
1752                 SET_BANK_R(0x0000, 0x7fff, ram);
1753 #ifdef SUPPORT_PC88_CDROM
1754         } else if(cdbios_loaded && Port99_CDREN) {
1755                 if(Port31_RMODE) {
1756                         SET_BANK_R(0x0000, 0x7fff, cdbios + 0x8000);
1757                 } else {
1758                         SET_BANK_R(0x0000, 0x7fff, cdbios + 0x0000);
1759                 }
1760 #endif
1761         } else if(Port31_RMODE) {
1762                 // N-BASIC
1763                 SET_BANK_R(0x0000, 0x7fff, n80rom);
1764         } else {
1765                 // N-88BASIC
1766                 SET_BANK_R(0x0000, 0x5fff, n88rom);
1767                 if(Port71_EROM & 1) {
1768                         SET_BANK_R(0x6000, 0x7fff, n88rom + 0x6000);
1769                 } else {
1770                         SET_BANK_R(0x6000, 0x7fff, n88exrom + 0x2000 * Port32_EROMSL);
1771                 }
1772         }
1773         
1774         // write
1775         if(PortE2_WREN) {
1776 #ifdef PC88_EXRAM_BANKS
1777                 if(PortE3_ERAMSL < PC88_EXRAM_BANKS) {
1778                         SET_BANK_W(0x0000, 0x7fff, exram + 0x8000 * PortE3_ERAMSL);
1779                 } else {
1780 #endif
1781 //                      SET_BANK_W(0x0000, 0x7fff, wdmy);
1782                         SET_BANK_W(0x0000, 0x7fff, ram);
1783 #ifdef PC88_EXRAM_BANKS
1784                 }
1785 #endif
1786         } else {
1787                 SET_BANK_W(0x0000, 0x7fff, ram);
1788         }
1789 }
1790
1791 void PC88::update_tvram_memmap()
1792 {
1793         // XM8 version 1.10
1794         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N || Port32_TMODE) {
1795                 SET_BANK(0xf000, 0xffff, ram + 0xf000, ram + 0xf000);
1796         } else {
1797                 SET_BANK(0xf000, 0xffff, tvram, tvram);
1798         }
1799 }
1800 #endif
1801
1802 void PC88::write_signal(int id, uint32_t data, uint32_t mask)
1803 {
1804         if(id == SIG_PC88_USART_IRQ) {
1805                 request_intr(IRQ_USART, ((data & mask) != 0));
1806         } else if(id == SIG_PC88_SOUND_IRQ) {
1807                 intr_req_sound = ((data & mask) != 0);
1808 #if defined(_PC8001SR)
1809                 if(intr_req_sound && !Port33_SINTM) {
1810 #else
1811                 if(intr_req_sound && !Port32_SINTM) {
1812 #endif
1813                         request_intr(IRQ_SOUND, true);
1814                 }
1815 #ifdef SUPPORT_PC88_SB2
1816         } else if(id == SIG_PC88_SB2_IRQ) {
1817                 intr_req_sb2 = ((data & mask) != 0);
1818                 if(intr_req_sb2 && !PortAA_S2INTM) {
1819                         request_intr(IRQ_SOUND, true);
1820                 }
1821 #endif
1822 #ifdef SUPPORT_PC88_CDROM
1823         } else if(id == SIG_PC88_SCSI_DRQ) {
1824                 if((data & mask) && cdbios_loaded) {
1825                         if(!dmac.ch[1].running) {
1826                                 dmac.start(1);
1827                         }
1828                         if(dmac.ch[1].running) {
1829                                 dmac.run(1, 1);
1830                         }
1831                 }
1832 #endif
1833         } else if(id == SIG_PC88_USART_OUT) {
1834                 // recv from sio
1835                 if(Port30_CMT) {
1836                         // send to cmt
1837                         if(cmt_rec && Port30_MTON) {
1838                                 cmt_buffer[cmt_bufptr++] = data & mask;
1839                                 if(cmt_bufptr >= CMT_BUFFER_SIZE) {
1840                                         cmt_fio->Fwrite(cmt_buffer, cmt_bufptr, 1);
1841                                         cmt_bufptr = 0;
1842                                 }
1843                         }
1844                 } else {
1845                         // send to rs-232c
1846                 }
1847         }
1848 }
1849
1850 void PC88::event_callback(int event_id, int err)
1851 {
1852         switch(event_id) {
1853         case EVENT_TIMER:
1854                 request_intr(IRQ_TIMER, true);
1855                 break;
1856         case EVENT_BUSREQ:
1857                 d_cpu->write_signal(SIG_CPU_BUSREQ, 0, 0);
1858                 break;
1859         case EVENT_CMT_SEND:
1860                 // check data carrier
1861                 if(cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON) {
1862                         // detect the data carrier at the top of next block
1863                         if(check_data_carrier()) {
1864                                 register_event(this, EVENT_CMT_DCD, 1000000, false, &cmt_register_id);
1865                                 usart_dcd = true;
1866                                 break;
1867                         }
1868                 }
1869         case EVENT_CMT_DCD:
1870                 // send data to sio
1871                 usart_dcd = false;
1872                 if(cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON) {
1873                         d_sio->write_signal(SIG_I8251_RECV, cmt_buffer[cmt_bufptr++], 0xff);
1874                         if(cmt_bufptr < cmt_bufcnt) {
1875                                 register_event(this, EVENT_CMT_SEND, 5000, false, &cmt_register_id);
1876                                 break;
1877                         }
1878                 }
1879                 usart_dcd = true; // Jackie Chan no Spartan X
1880                 cmt_register_id = -1;
1881                 break;
1882         case EVENT_BEEP:
1883                 beep_signal = !beep_signal;
1884                 d_pcm->write_signal(SIG_PCM1BIT_SIGNAL, ((beep_on && beep_signal) || sing_signal) ? 1 : 0, 1);
1885                 break;
1886 #ifdef SUPPORT_PC88_CDROM
1887         case EVENT_FADE_IN:
1888                 if((cdda_volume += 0.1) >= 100.0) {
1889                         cancel_event(this, cdda_register_id);
1890                         cdda_register_id = -1;
1891                         cdda_volume = 100.0;
1892                 }
1893                 d_scsi_cdrom->set_volume((int)cdda_volume);
1894                 break;
1895         case EVENT_FADE_OUT:
1896                 if((cdda_volume -= 0.1) <= 0) {
1897                         cancel_event(this, cdda_register_id);
1898                         cdda_register_id = -1;
1899                         cdda_volume = 0.0;
1900                 }
1901                 d_scsi_cdrom->set_volume((int)cdda_volume);
1902                 break;
1903 #endif
1904         }
1905 }
1906
1907 void PC88::event_frame()
1908 {
1909         // update key status
1910         memcpy(key_status, emu->get_key_buffer(), sizeof(key_status));
1911         
1912         for(int i = 0; i < array_length(key_conv_table); i++) {
1913                 // INS or F6-F10 -> SHIFT + DEL or F1-F5
1914                 if(key_status[key_conv_table[i][0]]) {
1915                         key_status[key_conv_table[i][1]] = 1;
1916                         key_status[0x10] |= key_conv_table[i][2];
1917                 }
1918         }
1919         if(key_status[0x11] && (key_status[0xbc] || key_status[0xbe])) {
1920                 // CTRL + "," or "." -> NumPad "," or "."
1921                 key_status[0x6c] = key_status[0xbc];
1922                 key_status[0x6e] = key_status[0xbe];
1923                 key_status[0x11] = key_status[0xbc] = key_status[0xbe] = 0;
1924         }
1925         key_status[0x14] = key_caps;
1926         key_status[0x15] = key_kana;
1927         
1928         crtc.update_blink();
1929         
1930 #ifdef SUPPORT_PC88_JOYSTICK
1931         mouse_dx += mouse_status[0];
1932         mouse_dy += mouse_status[1];
1933 #endif
1934 }
1935
1936 void PC88::event_vline(int v, int clock)
1937 {
1938         int disp_line = crtc.height * crtc.char_height;
1939         
1940         if(v == 0) {
1941                 if(crtc.status & 0x10) {
1942                         // start dma transfer to crtc
1943                         dmac.start(2);
1944                         if(!dmac.ch[2].running) {
1945                                 // dma underrun occurs !!!
1946                                 crtc.status |= 8;
1947 //                              crtc.status &= ~0x10;
1948                         } else {
1949                                 crtc.status &= ~8;
1950                         }
1951                         // dma wait cycles
1952                         // from memory access test on PC-8801MA2 (XM8 version 1.20)
1953                         busreq_clocks = (int)((double)(dmac.ch[2].count.sd + 1) * (cpu_clock_low ? 5.95 : 10.58) / (double)disp_line + 0.5);
1954 //                      busreq_clocks = (int)((double)(dmac.ch[2].count.sd + 1) * (cpu_clock_low ? 7.0 : 16.0) / (double)disp_line + 0.5);
1955                 }
1956                 crtc.start();
1957                 // for Nobunaga Fuunroku Opening (XM8 version 1.00)
1958 //              request_intr(IRQ_VRTC, false);
1959                 update_gvram_wait();
1960         }
1961         if(v < disp_line) {
1962                 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
1963                         // bus request
1964 #if defined(_PC8001SR)
1965                         if(config.boot_mode == MODE_PC80_V1 || config.boot_mode == MODE_PC80_N) {
1966 #else
1967                         if(config.boot_mode == MODE_PC88_V1S || config.boot_mode == MODE_PC88_N) {
1968 #endif
1969                                 d_cpu->write_signal(SIG_CPU_BUSREQ, 1, 1);
1970                                 register_event_by_clock(this, EVENT_BUSREQ, busreq_clocks, false, NULL);
1971                         }
1972                         // run dma transfer to crtc
1973                         if((v % crtc.char_height) == 0) {
1974                                 dmac.run(2, 80 + crtc.attrib.num * 2);
1975                         }
1976                 }
1977         } else if(v == disp_line) {
1978                 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
1979                         dmac.finish(2);
1980                         // for Romancia (XM8 version 1.00)
1981 //                      crtc.expand_buffer(hireso, Port31_400LINE);
1982                 }
1983                 // for Romancia (XM8 version 1.00)
1984                 crtc.expand_buffer(hireso, Port31_400LINE);
1985                 
1986                 crtc.finish();
1987                 request_intr(IRQ_VRTC, true);
1988                 update_gvram_wait();
1989         }
1990         // update palette
1991 #if !defined(_PC8001SR)
1992         if(v < (disp_line <= 200 ? 200 : 400)) {
1993 #else
1994         if(v < 200) {
1995 #endif
1996                 if(update_palette) {
1997                         static bool initialized = false;
1998                         static palette_t initial[9] = {0};
1999                         
2000                         if(!initialized) {
2001                                 for(int i = 1; i < 8; i++) {
2002                                         initial[i].b = (i & 1) ? 7 : 0;
2003                                         initial[i].r = (i & 2) ? 7 : 0;
2004                                         initial[i].g = (i & 4) ? 7 : 0;
2005                                 }
2006                                 initialized = true;
2007                         }
2008                         for(int i = 0; i < 9; i++) {
2009                                 palette_digital[i] = palette_analog[i] = initial[i];
2010                         }
2011 #if defined(_PC8001SR)
2012                         if(config.boot_mode != MODE_PC80_V2) {
2013                                 if(Port31_V1_320x200) {
2014                                         for(int i = 0; i < 3; i++) {
2015                                                 palette_analog[i].b = (port[0x31] & 4) ? 7 : 0;
2016                                                 palette_analog[i].r = (i          & 1) ? 7 : 0;
2017                                                 palette_analog[i].g = (i          & 2) ? 7 : 0;
2018                                         }
2019                                         palette_analog[3] = palette[8];
2020                                 } else if(Port31_V1_MONO) {
2021 //                                      palette_analog[0] = {0, 0, 0};
2022                                         palette_analog[1] = palette[8];
2023                                 } else {
2024 //                                      for(int i = 1; i < 8; i++) {
2025 //                                              palette_analog[i].b = (i & 1) ? 7 : 0;
2026 //                                              palette_analog[i].r = (i & 2) ? 7 : 0;
2027 //                                              palette_analog[i].g = (i & 4) ? 7 : 0;
2028 //                                      }
2029                                         palette_analog[0] = palette[8];
2030                                 }
2031                                 if(Port31_V1_320x200) {
2032 //                                      palette_digital[0] = {0, 0, 0};
2033                                 } else {
2034                                         palette_digital[0] = palette_analog[0];
2035                                 }
2036                         } else {
2037                                 for(int i = 0; i < 8; i++) {
2038                                         palette_analog[i].b = (port[0x54 + i] & 1) ? 7 : 0;
2039                                         palette_analog[i].r = (port[0x54 + i] & 2) ? 7 : 0;
2040                                         palette_analog[i].g = (port[0x54 + i] & 4) ? 7 : 0;
2041                                 }
2042                                 if(!Port31_HCOLOR) {
2043                                         palette_analog[0] = palette[8];
2044                                 }
2045                                 palette_digital[0] = palette_analog[0];
2046                         }
2047 #else
2048                         for(int i = 0; i < 8; i++) {
2049                                 palette_analog[i] = palette[i];
2050                         }
2051                         if(!Port31_HCOLOR) {
2052                                 if(!Port32_PMODE) {
2053                                         palette_analog[0] = palette[8];
2054                                 } else {
2055                                         palette_analog[0] = palette[9];
2056                                 }
2057                         }
2058                         palette_digital[0] = palette_analog[0];
2059 #endif
2060                 }
2061                 if(v == 0) {
2062                         memset(palette_line_changed, 0, sizeof(palette_line_changed));
2063                 }
2064                 if((palette_line_changed[v] = (update_palette || v == 0)) == true) {
2065                         for(int i = 0; i < 9; i++) {
2066                                 palette_line_digital[v][i] = palette_digital[i];
2067                                 palette_line_analog [v][i] = palette_analog [i];
2068                         }
2069                         update_palette = false;
2070                 }
2071         }
2072 }
2073
2074 void PC88::key_down(int code, bool repeat)
2075 {
2076         if(!repeat) {
2077                 if(code == 0x14) {
2078                         key_caps ^= 1;
2079                 } else if(code == 0x15) {
2080                         key_kana ^= 1;
2081                 }
2082         }
2083 }
2084
2085 void PC88::play_tape(const _TCHAR* file_path)
2086 {
2087         close_tape();
2088         
2089         if(cmt_fio->Fopen(file_path, FILEIO_READ_BINARY)) {
2090                 if(check_file_extension(file_path, _T(".n80"))) {
2091                         cmt_fio->Fread(ram + 0x8000, 0x7f40, 1);
2092                         cmt_fio->Fclose();
2093                         d_cpu->set_sp(ram[0xff3e] | (ram[0xff3f] << 8));
2094                         d_cpu->set_pc(0xff3d);
2095                         return;
2096                 }
2097                 
2098                 cmt_fio->Fseek(0, FILEIO_SEEK_END);
2099                 cmt_bufcnt = cmt_fio->Ftell();
2100                 cmt_bufptr = 0;
2101                 cmt_data_carrier_cnt = 0;
2102                 cmt_fio->Fseek(0, FILEIO_SEEK_SET);
2103                 memset(cmt_buffer, 0, sizeof(cmt_buffer));
2104                 cmt_fio->Fread(cmt_buffer, sizeof(cmt_buffer), 1);
2105                 cmt_fio->Fclose();
2106                 
2107                 if(strncmp((char *)cmt_buffer, "PC-8801 Tape Image(T88)", 23) == 0) {
2108                         // this is t88 format
2109                         int ptr = 24, tag = -1, len = 0, prev_bufptr = 0;
2110                         while(!(tag == 0 && len == 0)) {
2111                                 tag = cmt_buffer[ptr + 0] | (cmt_buffer[ptr + 1] << 8);
2112                                 len = cmt_buffer[ptr + 2] | (cmt_buffer[ptr + 3] << 8);
2113                                 ptr += 4;
2114                                 
2115                                 if(tag == 0x0101) {
2116                                         // data tag
2117                                         for(int i = 12; i < len; i++) {
2118                                                 cmt_buffer[cmt_bufptr++] = cmt_buffer[ptr + i];
2119                                         }
2120                                 } else if(tag == 0x0102 || tag == 0x0103) {
2121                                         // data carrier
2122                                         if(prev_bufptr != cmt_bufptr) {
2123                                                 cmt_data_carrier[cmt_data_carrier_cnt++] = prev_bufptr = cmt_bufptr;
2124                                         }
2125                                 }
2126                                 ptr += len;
2127                         }
2128                         cmt_bufcnt = cmt_bufptr;
2129                         cmt_bufptr = 0;
2130                 }
2131                 cmt_play = (cmt_bufcnt != 0);
2132                 
2133                 if(cmt_play && Port30_MTON) {
2134                         // start motor and detect the data carrier at the top of tape
2135                         if(cmt_register_id != -1) {
2136                                 cancel_event(this, cmt_register_id);
2137                         }
2138                         register_event(this, EVENT_CMT_SEND, 5000, false, &cmt_register_id);
2139                 }
2140         }
2141 }
2142
2143 void PC88::rec_tape(const _TCHAR* file_path)
2144 {
2145         close_tape();
2146         
2147         if(cmt_fio->Fopen(file_path, FILEIO_READ_WRITE_NEW_BINARY)) {
2148                 my_tcscpy_s(rec_file_path, _MAX_PATH, file_path);
2149                 cmt_bufptr = 0;
2150                 cmt_rec = true;
2151         }
2152 }
2153
2154 void PC88::close_tape()
2155 {
2156         // close file
2157         release_tape();
2158         
2159         // clear sio buffer
2160         d_sio->write_signal(SIG_I8251_CLEAR, 0, 0);
2161 }
2162
2163 void PC88::release_tape()
2164 {
2165         // close file
2166         if(cmt_fio->IsOpened()) {
2167                 if(cmt_rec && cmt_bufptr) {
2168                         cmt_fio->Fwrite(cmt_buffer, cmt_bufptr, 1);
2169                 }
2170                 cmt_fio->Fclose();
2171         }
2172         cmt_play = cmt_rec = false;
2173 }
2174
2175 bool PC88::is_frame_skippable()
2176 {
2177         return (cmt_play && cmt_bufptr < cmt_bufcnt && Port30_MTON);
2178 }
2179
2180 bool PC88::check_data_carrier()
2181 {
2182         if(cmt_bufptr == 0) {
2183                 return true;
2184         } else if(cmt_data_carrier_cnt) {
2185                 for(int i = 0; i < cmt_data_carrier_cnt; i++) {
2186                         if(cmt_data_carrier[i] == cmt_bufptr) {
2187                                 return true;
2188                         }
2189                 }
2190         } else if(cmt_buffer[cmt_bufptr] == 0xd3) {
2191                 for(int i = 1; i < 10; i++) {
2192                         if(cmt_buffer[cmt_bufptr + i] != cmt_buffer[cmt_bufptr]) {
2193                                 return false;
2194                         }
2195                 }
2196                 return true;
2197         } else if(cmt_buffer[cmt_bufptr] == 0x9c) {
2198                 for(int i = 1; i < 6; i++) {
2199                         if(cmt_buffer[cmt_bufptr + i] != cmt_buffer[cmt_bufptr]) {
2200                                 return false;
2201                         }
2202                 }
2203                 return true;
2204         }
2205         return false;
2206 }
2207
2208 void PC88::draw_screen()
2209 {
2210         // render text screen
2211         draw_text();
2212         
2213         // render graph screen
2214         bool disp_color_graph = true;
2215         bool draw_scanline_black = config.scan_line;
2216 #if defined(_PC8001SR)
2217         if(config.boot_mode != MODE_PC80_V2) {
2218                 if(Port31_V1_320x200) {
2219                         disp_color_graph = draw_320x200_4color_graph();
2220                 } else if(Port31_V1_MONO) {
2221                         draw_640x200_mono_graph();
2222                 } else {
2223                         if(hireso) {
2224                                 draw_scanline_black = false;
2225                         }
2226                         draw_640x200_attrib_graph();
2227                 }
2228                 emu->set_vm_screen_lines(200);
2229         } else {
2230                 if(Port31_HCOLOR) {
2231                         if(Port31_320x200) {
2232                                 disp_color_graph = draw_320x200_color_graph();
2233                         } else {
2234                                 disp_color_graph = draw_640x200_color_graph();
2235                         }
2236                         emu->set_vm_screen_lines(200);
2237                 } else {
2238                         if(Port31_320x200) {
2239                                 draw_320x200_attrib_graph();
2240                         } else {
2241                                 draw_640x200_attrib_graph();
2242                         }
2243                         if(hireso) {
2244                                 draw_scanline_black = false;
2245                         }
2246                         emu->set_vm_screen_lines(200);
2247                 }
2248         }
2249 #else
2250         if(Port31_HCOLOR) {
2251                 disp_color_graph = draw_640x200_color_graph();
2252                 emu->set_vm_screen_lines(200);
2253         } else if(!Port31_400LINE) {
2254                 if(hireso) {
2255                         draw_scanline_black = false;
2256                 }
2257                 draw_640x200_attrib_graph();
2258 //              draw_640x200_mono_graph();
2259                 emu->set_vm_screen_lines(200);
2260         } else {
2261                 if(hireso) {
2262                         draw_scanline_black = false;
2263                 }
2264                 draw_640x400_attrib_graph();
2265 //              draw_640x400_mono_graph();
2266                 emu->set_vm_screen_lines(400);
2267         }
2268 #endif
2269         
2270         // create palette for each scanline
2271 #if !defined(_PC8001SR)
2272         int disp_line = crtc.height * crtc.char_height;
2273         int ymax = (disp_line <= 200) ? 200 : 400;
2274 #else
2275         int ymax = 200;
2276 #endif
2277         static const uint32_t pex[8] = {
2278                 0,  36,  73, 109, 146, 182, 219, 255 // from m88
2279         };
2280         scrntype_t palette_digital_text_pc [9];
2281         scrntype_t palette_analog_text_pc  [9];
2282         scrntype_t palette_digital_graph_pc[9];
2283         scrntype_t palette_analog_graph_pc [9];
2284         
2285         scrntype_t palette_line_digital_text_pc [400][9];
2286         scrntype_t palette_line_analog_graph_pc [400][9];
2287 #if !defined(_PC8001SR)
2288         scrntype_t palette_line_analog_text_pc  [400][9];
2289         scrntype_t palette_line_digital_graph_pc[400][9];
2290 #endif
2291         
2292         for(int y = 0; y < ymax; y++) {
2293                 if(palette_line_changed[y]) {
2294                         for(int i = 0; i < 9; i++) {
2295                                 // A is a flag for crt filter
2296                                 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);
2297                                 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);
2298                                 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);
2299                                 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);
2300                         }
2301                         // set back color to black if cg screen is off in color mode
2302                         if(!disp_color_graph) {
2303                                 palette_digital_text_pc [0] = 
2304                                 palette_analog_text_pc  [0] = 
2305                                 palette_digital_graph_pc[0] = 
2306                                 palette_analog_graph_pc [0] = 0;
2307                         }
2308                         palette_analog_text_pc [8] = palette_digital_text_pc [0];
2309                         palette_analog_graph_pc[8] = palette_digital_graph_pc[0];
2310                 }
2311                 if(ymax == 200) {
2312                         for(int i = 0; i < 9; i++) {
2313                                 palette_line_digital_text_pc [2 * y    ][i] = 
2314                                 palette_line_digital_text_pc [2 * y + 1][i] = palette_digital_text_pc [i];
2315                                 palette_line_analog_graph_pc [2 * y    ][i] = 
2316                                 palette_line_analog_graph_pc [2 * y + 1][i] = palette_analog_graph_pc [i];
2317 #if !defined(_PC8001SR)
2318                                 palette_line_analog_text_pc  [2 * y    ][i] = 
2319                                 palette_line_analog_text_pc  [2 * y + 1][i] = palette_analog_text_pc  [i];
2320                                 palette_line_digital_graph_pc[2 * y    ][i] = 
2321                                 palette_line_digital_graph_pc[2 * y + 1][i] = palette_digital_graph_pc[i];
2322 #endif
2323                         }
2324                 } else {
2325                         for(int i = 0; i < 9; i++) {
2326                                 palette_line_digital_text_pc [y][i] = palette_digital_text_pc [i];
2327                                 palette_line_analog_graph_pc [y][i] = palette_analog_graph_pc [i];
2328 #if !defined(_PC8001SR)
2329                                 palette_line_analog_text_pc  [y][i] = palette_analog_text_pc  [i];
2330                                 palette_line_digital_graph_pc[y][i] = palette_digital_graph_pc[i];
2331 #endif
2332                         }
2333                 }
2334         }
2335         
2336         // copy to screen buffer
2337 #if !defined(_PC8001SR)
2338 #if defined(SUPPORT_PC88_VAB)
2339         // X88000
2340         if(PortB4_VAB_DISP) {
2341                 uint8_t *src = &exram[(0x8000 * 4) * PC88_VAB_PAGE];
2342                 
2343                 for(int y = 0; y < 400; y += 2) {
2344                         scrntype_t* dest0 = emu->get_screen_buffer(y);
2345                         scrntype_t* dest1 = emu->get_screen_buffer(y + 1);
2346                         
2347                         for(int x = 0; x < 640; x += 2) {
2348                                 pair16_t c;
2349                                 c.b.l = *src++;
2350                                 c.b.h = *src++;
2351                                 dest0[x] = dest0[x + 1] = palette_vab_pc[c.w];
2352                         }
2353                         if(config.scan_line) {
2354                                 memset(dest1, 0, sizeof(scrntype_t) * 640);
2355                         } else {
2356                                 memcpy(dest1, dest0, sizeof(scrntype_t) * 640);
2357                         }
2358                 }
2359                 emu->screen_skip_line(true);
2360         } else
2361 #endif
2362         if(!Port31_HCOLOR && Port31_400LINE) {
2363                 for(int y = 0; y < 400; y++) {
2364                         scrntype_t* dest = emu->get_screen_buffer(y);
2365                         uint8_t* src_t = text[y >> 1];
2366                         uint8_t* src_g = graph[y];
2367                         scrntype_t* pal_t;
2368                         scrntype_t* pal_g;
2369                         
2370 //                      if(Port31_HCOLOR) {
2371 //                              pal_t = palette_line_digital_text_pc [y];
2372 //                              pal_g = palette_line_analog_graph_pc [y];
2373 //                      } else
2374                         if(Port32_PMODE) {
2375                                 pal_t = palette_line_analog_text_pc  [y];
2376                                 pal_g = palette_line_analog_graph_pc [y];
2377                         } else {
2378                                 pal_t = palette_line_digital_text_pc [y];
2379                                 pal_g = palette_line_digital_graph_pc[y];
2380                         }
2381                         for(int x = 0; x < 640; x++) {
2382                                 uint32_t t = src_t[x];
2383                                 dest[x] = t ? pal_t[t] : pal_g[src_g[x]];
2384                         }
2385                 }
2386                 emu->screen_skip_line(false);
2387         } else
2388 #endif
2389         {
2390                 for(int y = 0; y < 400; y++) {
2391                         scrntype_t* dest = emu->get_screen_buffer(y);
2392                         uint8_t* src_t = text[y >> 1];
2393                         uint8_t* src_g = graph[y];
2394                         scrntype_t* pal_t;
2395                         scrntype_t* pal_g;
2396 #if defined(_PC8001SR)
2397                         pal_t = palette_line_digital_text_pc[y];
2398                         pal_g = palette_line_analog_graph_pc[y];
2399                         
2400                         if(port[0x33] & 8) {
2401                                 for(int x = 0; x < 640; x++) {
2402                                         uint32_t t = src_t[x];
2403                                         uint32_t g = src_g[x];
2404                                         dest[x] = (!g && t) ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[g];
2405                                 }
2406                         } else {
2407                                 for(int x = 0; x < 640; x++) {
2408                                         uint32_t t = src_t[x];
2409                                         dest[x] = t ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[src_g[x]];
2410                                 }
2411                         }
2412 #else
2413                         if(Port31_HCOLOR) {
2414                                 pal_t = palette_line_digital_text_pc [y];
2415                                 pal_g = palette_line_analog_graph_pc [y];
2416                         } else if(Port32_PMODE) {
2417                                 pal_t = palette_line_analog_text_pc  [y];
2418                                 pal_g = palette_line_analog_graph_pc [y];
2419                         } else {
2420                                 pal_t = palette_line_digital_text_pc [y];
2421                                 pal_g = palette_line_digital_graph_pc[y];
2422                         }
2423                         for(int x = 0; x < 640; x++) {
2424                                 uint32_t t = src_t[x];
2425                                 dest[x] = t ? pal_t[t] : ((y & 1) && draw_scanline_black) ? 0 : pal_g[src_g[x]];
2426                         }
2427 #endif
2428                 }
2429                 emu->screen_skip_line(true);
2430         }
2431 }
2432
2433 /*
2434         attributes:
2435         
2436         bit7: green
2437         bit6: red
2438         bit5: blue
2439         bit4: graph=1/character=0
2440         bit3: under line
2441         bit2: upper line
2442         bit1: secret
2443         bit0: reverse
2444 */
2445
2446 void PC88::draw_text()
2447 {
2448         if(crtc.status & 0x88) {
2449                 // dma underrun
2450                 crtc.status &= ~0x80;
2451                 memset(crtc.text.expand, 0, 200 * 80);
2452                 memset(crtc.attrib.expand, crtc.reverse ? 3 : 2, 200 * 80);
2453         }
2454         // for Advanced Fantasian Opening (20line) (XM8 version 1.00)
2455         if(!(crtc.status & 0x10) || Port53_TEXTDS) {
2456 //      if(!(crtc.status & 0x10) || (crtc.status & 8) || Port53_TEXTDS) {
2457                 memset(crtc.text.expand, 0, 200 * 80);
2458                 for(int y = 0; y < 200; y++) {
2459                         for(int x = 0; x < 80; x++) {
2460                                 crtc.attrib.expand[y][x] &= 0xe0;
2461                                 crtc.attrib.expand[y][x] |= 0x02;
2462                         }
2463                 }
2464 //              memset(crtc.attrib.expand, 2, 200 * 80);
2465         }
2466         
2467         // for Xak2 opening
2468         memset(text, 8, sizeof(text));
2469         memset(text_color, 7, sizeof(text_color));
2470         memset(text_reverse, 0, sizeof(text_reverse));
2471         
2472         int char_height = crtc.char_height;
2473         uint8_t color_mask = Port30_COLOR ? 0 : 7;
2474         uint8_t code_expand, attr_expand;
2475         
2476         if(!hireso) {
2477                 char_height <<= 1;
2478         }
2479 //      if(Port31_400LINE || !crtc.skip_line) {
2480 //              char_height >>= 1;
2481 //      }
2482         if(crtc.skip_line) {
2483                 char_height <<= 1;
2484         }
2485 //      for(int cy = 0, ytop = 0; cy < 64 && ytop < 400; cy++, ytop += char_height) {
2486         for(int cy = 0, ytop = 0; cy < crtc.height && ytop < 400; cy++, ytop += char_height) {
2487                 for(int x = 0, cx = 0; cx < crtc.width; x += 8, cx++) {
2488                         if(Port30_40 && (cx & 1)) {
2489                                 // don't update code/attrib
2490                         } else {
2491                                 code_expand = crtc.text.expand[cy][cx];
2492                                 attr_expand = crtc.attrib.expand[cy][cx];
2493                         }
2494                         uint8_t attrib = attr_expand;//crtc.attrib.expand[cy][cx];
2495 //                      uint8_t color = !(Port30_COLOR && (attrib & 8)) ? 7 : (attrib & 0xe0) ? (attrib >> 5) : 8;
2496                         uint8_t color = (attrib & 0xe0) ? ((attrib >> 5) | color_mask) : 8;
2497                         bool under_line = ((attrib & 8) != 0);
2498                         bool upper_line = ((attrib & 4) != 0);
2499                         bool secret = ((attrib & 2) != 0);
2500                         bool reverse = ((attrib & 1) != 0);
2501                         
2502                         uint8_t color_tmp = color;
2503                         bool reverse_tmp = reverse;
2504                         
2505                         // from ePC-8801MA\89ü
2506                         if(Port31_GRAPH && !Port31_HCOLOR) {
2507                                 if(reverse) {
2508                                         reverse = false;
2509                                         color = 8;
2510                                 }
2511                         }
2512                         uint8_t code = secret ? 0 : code_expand;//crtc.text.expand[cy][cx];
2513 #ifdef SUPPORT_PC88_PCG8100
2514                         uint8_t *pattern = ((attrib & 0x10) ? sg_pattern : pcg_pattern) + code * 8;
2515 #else
2516                         uint8_t *pattern = ((attrib & 0x10) ? sg_pattern : kanji1 + 0x1000) + code * 8;
2517 #endif
2518                         
2519                         for(int l = 0, y = ytop; l < char_height / 2 && y < 400; l++, y += 2) {
2520                                 uint8_t pat = (l < 8) ? pattern[l] : 0;
2521                                 
2522                                 if(Port30_40) {
2523                                         // from ePC-8801MA\89ü
2524                                         static const uint8_t wct[16] = {
2525                                                 0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff
2526                                         };
2527                                         pat = wct[(cx & 1) ? (pat & 0x0f) : (pat >> 4)];
2528                                 }
2529                                 if((upper_line && l == 0) || (under_line && l >= 7)) {
2530                                         pat = 0xff;
2531                                 }
2532                                 if(reverse) {
2533                                         pat ^= 0xff;
2534                                 }
2535                                 uint8_t *dest = &text[y >> 1][x];
2536                                 dest[0] = (pat & 0x80) ? color : 0;
2537                                 dest[1] = (pat & 0x40) ? color : 0;
2538                                 dest[2] = (pat & 0x20) ? color : 0;
2539                                 dest[3] = (pat & 0x10) ? color : 0;
2540                                 dest[4] = (pat & 0x08) ? color : 0;
2541                                 dest[5] = (pat & 0x04) ? color : 0;
2542                                 dest[6] = (pat & 0x02) ? color : 0;
2543                                 dest[7] = (pat & 0x01) ? color : 0;
2544                                 
2545                                 // store text attributes for monocolor graph screen
2546                                 text_color[y >> 1][cx] = color_tmp;
2547                                 text_reverse[y >> 1][cx] = reverse_tmp;
2548                         }
2549                 }
2550         }
2551 }
2552
2553 #if defined(_PC8001SR)
2554 bool PC88::draw_320x200_color_graph()
2555 {
2556         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2557                 memset(graph, 0, sizeof(graph));
2558                 return false;
2559         }
2560         uint8_t *gvram_b0 = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2561         uint8_t *gvram_r0 = Port53_G0DS ? gvram_null : (gvram + 0x4000);
2562         uint8_t *gvram_g0 = Port53_G0DS ? gvram_null : (gvram + 0x8000);
2563         uint8_t *gvram_b1 = Port53_G1DS ? gvram_null : (gvram + 0x2000);
2564         uint8_t *gvram_r1 = Port53_G1DS ? gvram_null : (gvram + 0x6000);
2565         uint8_t *gvram_g1 = Port53_G1DS ? gvram_null : (gvram + 0xa000);
2566         
2567         if(port[0x33] & 4) {
2568                 // G1>G0
2569                 uint8_t *tmp;
2570                 tmp = gvram_b0; gvram_b0 = gvram_b1; gvram_b1 = tmp;
2571                 tmp = gvram_r0; gvram_r0 = gvram_r1; gvram_r1 = tmp;
2572                 tmp = gvram_g0; gvram_g0 = gvram_g1; gvram_g1 = tmp;
2573         }
2574         
2575         for(int y = 0, addr = 0; y < 400; y += 2) {
2576                 for(int x = 0; x < 640; x += 16) {
2577                         uint8_t b0 = gvram_b0[addr];
2578                         uint8_t r0 = gvram_r0[addr];
2579                         uint8_t g0 = gvram_g0[addr];
2580                         uint8_t b1 = gvram_b1[addr];
2581                         uint8_t r1 = gvram_r1[addr];
2582                         uint8_t g1 = gvram_g1[addr];
2583                         addr++;
2584                         uint8_t *dest = &graph[y][x];
2585                         uint8_t brg0, brg1;
2586                         brg0 = ((b0 & 0x80) >> 7) | ((r0 & 0x80) >> 6) | ((g0 & 0x80) >> 5);
2587                         brg1 = ((b1 & 0x80) >> 7) | ((r1 & 0x80) >> 6) | ((g1 & 0x80) >> 5);
2588                         dest[ 0] = dest[ 1] = brg0 ? brg0 : brg1;
2589                         brg0 = ((b0 & 0x40) >> 6) | ((r0 & 0x40) >> 5) | ((g0 & 0x40) >> 4);
2590                         brg1 = ((b1 & 0x40) >> 6) | ((r1 & 0x40) >> 5) | ((g1 & 0x40) >> 4);
2591                         dest[ 2] = dest[ 3] = brg0 ? brg0 : brg1;
2592                         brg0 = ((b0 & 0x20) >> 5) | ((r0 & 0x20) >> 4) | ((g0 & 0x20) >> 3);
2593                         brg1 = ((b1 & 0x20) >> 5) | ((r1 & 0x20) >> 4) | ((g1 & 0x20) >> 3);
2594                         dest[ 4] = dest[ 5] = brg0 ? brg0 : brg1;
2595                         brg0 = ((b0 & 0x10) >> 4) | ((r0 & 0x10) >> 3) | ((g0 & 0x10) >> 2);
2596                         brg1 = ((b1 & 0x10) >> 4) | ((r1 & 0x10) >> 3) | ((g1 & 0x10) >> 2);
2597                         dest[ 6] = dest[ 7] = brg0 ? brg0 : brg1;
2598                         brg0 = ((b0 & 0x08) >> 3) | ((r0 & 0x08) >> 2) | ((g0 & 0x08) >> 1);
2599                         brg1 = ((b1 & 0x08) >> 3) | ((r1 & 0x08) >> 2) | ((g1 & 0x08) >> 1);
2600                         dest[ 8] = dest[ 9] = brg0 ? brg0 : brg1;
2601                         brg0 = ((b0 & 0x04) >> 2) | ((r0 & 0x04) >> 1) | ((g0 & 0x04)     );
2602                         brg1 = ((b1 & 0x04) >> 2) | ((r1 & 0x04) >> 1) | ((g1 & 0x04)     );
2603                         dest[10] = dest[11] = brg0 ? brg0 : brg1;
2604                         brg0 = ((b0 & 0x02) >> 1) | ((r0 & 0x02)     ) | ((g0 & 0x02) << 1);
2605                         brg1 = ((b1 & 0x02) >> 1) | ((r1 & 0x02)     ) | ((g1 & 0x02) << 1);
2606                         dest[12] = dest[13] = brg0 ? brg0 : brg1;
2607                         brg0 = ((b0 & 0x01)     ) | ((r0 & 0x01) << 1) | ((g0 & 0x01) << 2);
2608                         brg1 = ((b1 & 0x01)     ) | ((r1 & 0x01) << 1) | ((g1 & 0x01) << 2);
2609                         dest[14] = dest[15] = brg0 ? brg0 : brg1;
2610                 }
2611                 if(config.scan_line) {
2612                         memset(graph[y + 1], 0, 640);
2613                 } else {
2614                         memcpy(graph[y + 1], graph[y], 640);
2615                 }
2616         }
2617         return true;
2618 }
2619
2620 bool PC88::draw_320x200_4color_graph()
2621 {
2622         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2623                 memset(graph, 0, sizeof(graph));
2624                 return false;
2625         }
2626         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2627         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2628         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2629         
2630         for(int y = 0, addr = 0; y < 400; y += 2) {
2631                 for(int x = 0; x < 640; x += 8) {
2632                         uint8_t brg = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2633                         addr++;
2634                         uint8_t *dest = &graph[y][x];
2635                         dest[0] = dest[1] = (brg >> 6) & 3;
2636                         dest[2] = dest[3] = (brg >> 4) & 3;
2637                         dest[4] = dest[5] = (brg >> 2) & 3;
2638                         dest[6] = dest[7] = (brg     ) & 3;
2639                 }
2640                 if(config.scan_line) {
2641                         memset(graph[y + 1], 0, 640);
2642                 } else {
2643                         memcpy(graph[y + 1], graph[y], 640);
2644                 }
2645         }
2646         return true;
2647 }
2648
2649 void PC88::draw_320x200_attrib_graph()
2650 {
2651         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS && Port53_G3DS && Port53_G4DS && Port53_G5DS)) {
2652                 memset(graph, 0, sizeof(graph));
2653                 return;
2654         }
2655         uint8_t *gvram_b0 = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2656         uint8_t *gvram_r0 = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2657         uint8_t *gvram_g0 = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2658         uint8_t *gvram_b1 = Port53_G3DS ? gvram_null : (gvram + 0x2000);
2659         uint8_t *gvram_r1 = Port53_G4DS ? gvram_null : (gvram + 0x6000);
2660         uint8_t *gvram_g1 = Port53_G5DS ? gvram_null : (gvram + 0xa000);
2661         
2662         if(Port30_40) {
2663                 for(int y = 0, addr = 0; y < 400; y += 2) {
2664                         for(int x = 0, cx = 0; x < 640; x += 16, cx += 2) {
2665                                 uint8_t color = text_color[y >> 1][cx];
2666                                 uint8_t brg0 = gvram_b0[addr] | gvram_r0[addr] | gvram_g0[addr] |
2667                                                gvram_b1[addr] | gvram_r1[addr] | gvram_g1[addr];
2668                                 uint8_t brg1 = 0;
2669                                 if(text_reverse[y >> 1][cx]) {
2670                                         brg0 ^= 0xff;
2671                                         brg1 ^= 0xff;
2672                                 }
2673                                 addr++;
2674                                 uint8_t *dest0 = &graph[y    ][x];
2675                                 uint8_t *dest1 = &graph[y + 1][x];
2676                                 dest0[ 0] = dest0[ 1] = (brg0 & 0x80) ? color : 0;
2677                                 dest0[ 2] = dest0[ 3] = (brg0 & 0x40) ? color : 0;
2678                                 dest0[ 4] = dest0[ 5] = (brg0 & 0x20) ? color : 0;
2679                                 dest0[ 6] = dest0[ 7] = (brg0 & 0x10) ? color : 0;
2680                                 dest0[ 8] = dest0[ 9] = (brg0 & 0x08) ? color : 0;
2681                                 dest0[10] = dest0[11] = (brg0 & 0x04) ? color : 0;
2682                                 dest0[12] = dest0[13] = (brg0 & 0x02) ? color : 0;
2683                                 dest0[14] = dest0[15] = (brg0 & 0x01) ? color : 0;
2684                                 if(!hireso) continue;
2685                                 dest1[ 0] = dest1[ 1] = (brg1 & 0x80) ? color : 0;
2686                                 dest1[ 2] = dest1[ 3] = (brg1 & 0x40) ? color : 0;
2687                                 dest1[ 4] = dest1[ 5] = (brg1 & 0x20) ? color : 0;
2688                                 dest1[ 6] = dest1[ 7] = (brg1 & 0x10) ? color : 0;
2689                                 dest1[ 8] = dest1[ 9] = (brg1 & 0x08) ? color : 0;
2690                                 dest1[10] = dest1[11] = (brg1 & 0x04) ? color : 0;
2691                                 dest1[12] = dest1[13] = (brg1 & 0x02) ? color : 0;
2692                                 dest1[14] = dest1[15] = (brg1 & 0x01) ? color : 0;
2693                         }
2694                         if(!hireso) {
2695                                 if(config.scan_line) {
2696                                         memset(graph[y + 1], 0, 640);
2697                                 } else {
2698                                         memcpy(graph[y + 1], graph[y], 640);
2699                                 }
2700                         }
2701                 }
2702         } else {
2703                 for(int y = 0, addr = 0; y < 400; y += 2) {
2704                         for(int x = 0, cx = 0; x < 640; x += 16, cx += 2) {
2705                                 uint8_t color_l = text_color[y >> 1][cx + 0];
2706                                 uint8_t color_r = text_color[y >> 1][cx + 1];
2707                                 uint8_t brg0 = gvram_b0[addr] | gvram_r0[addr] | gvram_g0[addr] |
2708                                                gvram_b1[addr] | gvram_r1[addr] | gvram_g1[addr];
2709                                 uint8_t brg1 = 0;
2710                                 if(text_reverse[y >> 1][cx + 0]) {
2711                                         brg0 ^= 0xf0;
2712                                         brg0 ^= 0xf0;
2713                                 }
2714                                 if(text_reverse[y >> 1][cx + 1]) {
2715                                         brg1 ^= 0x0f;
2716                                         brg1 ^= 0x0f;
2717                                 }
2718                                 addr++;
2719                                 uint8_t *dest0 = &graph[y    ][x];
2720                                 uint8_t *dest1 = &graph[y + 1][x];
2721                                 dest0[ 0] = dest0[ 1] = (brg0 & 0x80) ? color_l : 0;
2722                                 dest0[ 2] = dest0[ 3] = (brg0 & 0x40) ? color_l : 0;
2723                                 dest0[ 4] = dest0[ 5] = (brg0 & 0x20) ? color_l : 0;
2724                                 dest0[ 6] = dest0[ 7] = (brg0 & 0x10) ? color_l : 0;
2725                                 dest0[ 8] = dest0[ 9] = (brg0 & 0x08) ? color_r : 0;
2726                                 dest0[10] = dest0[11] = (brg0 & 0x04) ? color_r : 0;
2727                                 dest0[12] = dest0[13] = (brg0 & 0x02) ? color_r : 0;
2728                                 dest0[14] = dest0[15] = (brg0 & 0x01) ? color_r : 0;
2729                                 if(!hireso) continue;
2730                                 dest1[ 0] = dest1[ 1] = (brg1 & 0x80) ? color_l : 0;
2731                                 dest1[ 2] = dest1[ 3] = (brg1 & 0x40) ? color_l : 0;
2732                                 dest1[ 4] = dest1[ 5] = (brg1 & 0x20) ? color_l : 0;
2733                                 dest1[ 6] = dest1[ 7] = (brg1 & 0x10) ? color_l : 0;
2734                                 dest1[ 8] = dest1[ 9] = (brg1 & 0x08) ? color_r : 0;
2735                                 dest1[10] = dest1[11] = (brg1 & 0x04) ? color_r : 0;
2736                                 dest1[12] = dest1[13] = (brg1 & 0x02) ? color_r : 0;
2737                                 dest1[14] = dest1[15] = (brg1 & 0x01) ? color_r : 0;
2738                         }
2739                         if(!hireso) {
2740                                 if(config.scan_line) {
2741                                         memset(graph[y + 1], 0, 640);
2742                                 } else {
2743                                         memcpy(graph[y + 1], graph[y], 640);
2744                                 }
2745                         }
2746                 }
2747         }
2748 }
2749 #endif
2750
2751 bool PC88::draw_640x200_color_graph()
2752 {
2753 #if defined(_PC8001SR)
2754         if(!Port31_GRAPH || Port53_G0DS) {
2755 #else
2756         if(!Port31_GRAPH/* || (Port53_G0DS && Port53_G1DS && Port53_G2DS)*/) {
2757 #endif
2758                 memset(graph, 0, sizeof(graph));
2759                 return false;
2760         }
2761         uint8_t *gvram_b = /*Port53_G0DS ? gvram_null : */(gvram + 0x0000);
2762         uint8_t *gvram_r = /*Port53_G1DS ? gvram_null : */(gvram + 0x4000);
2763         uint8_t *gvram_g = /*Port53_G2DS ? gvram_null : */(gvram + 0x8000);
2764         
2765         for(int y = 0, addr = 0; y < 400; y += 2) {
2766                 for(int x = 0; x < 640; x += 8) {
2767                         uint8_t b = gvram_b[addr];
2768                         uint8_t r = gvram_r[addr];
2769                         uint8_t g = gvram_g[addr];
2770                         addr++;
2771                         uint8_t *dest = &graph[y][x];
2772                         dest[0] = ((b & 0x80) >> 7) | ((r & 0x80) >> 6) | ((g & 0x80) >> 5);
2773                         dest[1] = ((b & 0x40) >> 6) | ((r & 0x40) >> 5) | ((g & 0x40) >> 4);
2774                         dest[2] = ((b & 0x20) >> 5) | ((r & 0x20) >> 4) | ((g & 0x20) >> 3);
2775                         dest[3] = ((b & 0x10) >> 4) | ((r & 0x10) >> 3) | ((g & 0x10) >> 2);
2776                         dest[4] = ((b & 0x08) >> 3) | ((r & 0x08) >> 2) | ((g & 0x08) >> 1);
2777                         dest[5] = ((b & 0x04) >> 2) | ((r & 0x04) >> 1) | ((g & 0x04)     );
2778                         dest[6] = ((b & 0x02) >> 1) | ((r & 0x02)     ) | ((g & 0x02) << 1);
2779                         dest[7] = ((b & 0x01)     ) | ((r & 0x01) << 1) | ((g & 0x01) << 2);
2780                 }
2781                 if(config.scan_line) {
2782                         memset(graph[y + 1], 0, 640);
2783                 } else {
2784                         memcpy(graph[y + 1], graph[y], 640);
2785                 }
2786         }
2787         return true;
2788 }
2789
2790 void PC88::draw_640x200_mono_graph()
2791 {
2792         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2793                 memset(graph, 0, sizeof(graph));
2794                 return;
2795         }
2796         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2797         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2798         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2799         
2800         for(int y = 0, addr = 0; y < 400; y += 2) {
2801                 for(int x = 0; x < 640; x += 8) {
2802                         uint8_t brg = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2803                         addr++;
2804                         uint8_t *dest = &graph[y][x];
2805                         dest[0] = (brg & 0x80) >> 7;
2806                         dest[1] = (brg & 0x40) >> 6;
2807                         dest[2] = (brg & 0x20) >> 5;
2808                         dest[3] = (brg & 0x10) >> 4;
2809                         dest[4] = (brg & 0x08) >> 3;
2810                         dest[5] = (brg & 0x04) >> 2;
2811                         dest[6] = (brg & 0x02) >> 1;
2812                         dest[7] = (brg & 0x01)     ;
2813                 }
2814                 if(config.scan_line) {
2815                         memset(graph[y + 1], 0, 640);
2816                 } else {
2817                         memcpy(graph[y + 1], graph[y], 640);
2818                 }
2819         }
2820 }
2821
2822 void PC88::draw_640x200_attrib_graph()
2823 {
2824         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS && Port53_G2DS)) {
2825                 memset(graph, 0, sizeof(graph));
2826                 return;
2827         }
2828         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2829         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2830         uint8_t *gvram_g = Port53_G2DS ? gvram_null : (gvram + 0x8000);
2831         
2832         for(int y = 0, addr = 0; y < 400; y += 2) {
2833                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2834                         uint8_t color = text_color[y >> 1][cx];
2835                         uint8_t brg0 = gvram_b[addr] | gvram_r[addr] | gvram_g[addr];
2836                         uint8_t brg1 = 0;
2837                         if(text_reverse[y >> 1][cx]) {
2838                                 brg0 ^= 0xff;
2839                                 brg1 ^= 0xff;
2840                         }
2841                         addr++;
2842                         uint8_t *dest0 = &graph[y    ][x];
2843                         uint8_t *dest1 = &graph[y + 1][x];
2844                         dest0[0] = (brg0 & 0x80) ? color : 0;
2845                         dest0[1] = (brg0 & 0x40) ? color : 0;
2846                         dest0[2] = (brg0 & 0x20) ? color : 0;
2847                         dest0[3] = (brg0 & 0x10) ? color : 0;
2848                         dest0[4] = (brg0 & 0x08) ? color : 0;
2849                         dest0[5] = (brg0 & 0x04) ? color : 0;
2850                         dest0[6] = (brg0 & 0x02) ? color : 0;
2851                         dest0[7] = (brg0 & 0x01) ? color : 0;
2852                         if(!hireso) continue;
2853                         dest1[0] = (brg1 & 0x80) ? color : 0;
2854                         dest1[1] = (brg1 & 0x40) ? color : 0;
2855                         dest1[2] = (brg1 & 0x20) ? color : 0;
2856                         dest1[3] = (brg1 & 0x10) ? color : 0;
2857                         dest1[4] = (brg1 & 0x08) ? color : 0;
2858                         dest1[5] = (brg1 & 0x04) ? color : 0;
2859                         dest1[6] = (brg1 & 0x02) ? color : 0;
2860                         dest1[7] = (brg1 & 0x01) ? color : 0;
2861                 }
2862                 if(!hireso) {
2863                         if(config.scan_line) {
2864                                 memset(graph[y + 1], 0, 640);
2865                         } else {
2866                                 memcpy(graph[y + 1], graph[y], 640);
2867                         }
2868                 }
2869         }
2870 }
2871
2872 #if !defined(_PC8001SR)
2873 void PC88::draw_640x400_mono_graph()
2874 {
2875         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2876                 memset(graph, 0, sizeof(graph));
2877                 return;
2878         }
2879         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2880         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2881         
2882         for(int y = 0, addr = 0; y < 200; y++) {
2883                 for(int x = 0; x < 640; x += 8) {
2884                         uint8_t b = gvram_b[addr];
2885                         addr++;
2886                         uint8_t *dest = &graph[y][x];
2887                         dest[0] = (b & 0x80) >> 7;
2888                         dest[1] = (b & 0x40) >> 6;
2889                         dest[2] = (b & 0x20) >> 5;
2890                         dest[3] = (b & 0x10) >> 4;
2891                         dest[4] = (b & 0x08) >> 3;
2892                         dest[5] = (b & 0x04) >> 2;
2893                         dest[6] = (b & 0x02) >> 1;
2894                         dest[7] = (b & 0x01)     ;
2895                 }
2896         }
2897         for(int y = 200, addr = 0; y < 400; y++) {
2898                 for(int x = 0; x < 640; x += 8) {
2899                         uint8_t r = gvram_r[addr];
2900                         addr++;
2901                         uint8_t *dest = &graph[y][x];
2902                         dest[0] = (r & 0x80) >> 7;
2903                         dest[1] = (r & 0x40) >> 6;
2904                         dest[2] = (r & 0x20) >> 5;
2905                         dest[3] = (r & 0x10) >> 4;
2906                         dest[4] = (r & 0x08) >> 3;
2907                         dest[5] = (r & 0x04) >> 2;
2908                         dest[6] = (r & 0x02) >> 1;
2909                         dest[7] = (r & 0x01)     ;
2910                 }
2911         }
2912 }
2913
2914 void PC88::draw_640x400_attrib_graph()
2915 {
2916         if(!Port31_GRAPH || (Port53_G0DS && Port53_G1DS)) {
2917                 memset(graph, 0, sizeof(graph));
2918                 return;
2919         }
2920         uint8_t *gvram_b = Port53_G0DS ? gvram_null : (gvram + 0x0000);
2921         uint8_t *gvram_r = Port53_G1DS ? gvram_null : (gvram + 0x4000);
2922         
2923         for(int y = 0, addr = 0; y < 200; y++) {
2924                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2925                         uint8_t color = text_color[y >> 1][cx];
2926                         uint8_t b = gvram_b[addr];
2927                         if(text_reverse[y >> 1][cx]) {
2928                                 b ^= 0xff;
2929                         }
2930                         addr++;
2931                         uint8_t *dest = &graph[y][x];
2932                         dest[0] = (b & 0x80) ? color : 0;
2933                         dest[1] = (b & 0x40) ? color : 0;
2934                         dest[2] = (b & 0x20) ? color : 0;
2935                         dest[3] = (b & 0x10) ? color : 0;
2936                         dest[4] = (b & 0x08) ? color : 0;
2937                         dest[5] = (b & 0x04) ? color : 0;
2938                         dest[6] = (b & 0x02) ? color : 0;
2939                         dest[7] = (b & 0x01) ? color : 0;
2940                 }
2941         }
2942         for(int y = 200, addr = 0; y < 400; y++) {
2943                 for(int x = 0, cx = 0; x < 640; x += 8, cx++) {
2944                         uint8_t color = text_color[y >> 1][cx];
2945                         uint8_t r = gvram_r[addr];
2946                         if(text_reverse[y >> 1][cx]) {
2947                                 r ^= 0xff;
2948                         }
2949                         addr++;
2950                         uint8_t *dest = &graph[y][x];
2951                         dest[0] = (r & 0x80) ? color : 0;
2952                         dest[1] = (r & 0x40) ? color : 0;
2953                         dest[2] = (r & 0x20) ? color : 0;
2954                         dest[3] = (r & 0x10) ? color : 0;
2955                         dest[4] = (r & 0x08) ? color : 0;
2956                         dest[5] = (r & 0x04) ? color : 0;
2957                         dest[6] = (r & 0x02) ? color : 0;
2958                         dest[7] = (r & 0x01) ? color : 0;
2959                 }
2960         }
2961 }
2962 #endif
2963
2964 void PC88::request_intr(int level, bool status)
2965 {
2966         uint8_t bit = 1 << level;
2967         
2968         if(status) {
2969                 // for Nobunaga Fuunroku Opening & MID-GARTS Opening (XM8 version 1.00)
2970 //              bit &= intr_mask2;
2971                 if(!(intr_req & bit)) {
2972                         intr_req |= bit;
2973                         update_intr();
2974                 }
2975         } else {
2976                 if(intr_req & bit) {
2977                         intr_req &= ~bit;
2978                         update_intr();
2979                 }
2980         }
2981 }
2982
2983 void PC88::update_intr()
2984 {
2985         d_cpu->set_intr_line(((intr_req & intr_mask1 & intr_mask2) != 0), true, 0);
2986 }
2987
2988 uint32_t PC88::get_intr_ack()
2989 {
2990         uint8_t ai = intr_req & intr_mask1 & intr_mask2;
2991         
2992         for(int i = 0; i < 8; i++, ai >>= 1) {
2993                 if(ai & 1) {
2994                         intr_req &= ~(1 << i);
2995                         intr_mask1 = 0;
2996                         return i * 2;
2997                 }
2998         }
2999         return 0;
3000 }
3001
3002 void PC88::notify_intr_ei()
3003 {
3004         update_intr();
3005 }
3006
3007 /* ----------------------------------------------------------------------------
3008         CRTC (uPD3301)
3009 ---------------------------------------------------------------------------- */
3010
3011 void pc88_crtc_t::reset(bool hireso)
3012 {
3013         blink.rate = 24;
3014         cursor.type = cursor.mode = -1;
3015         cursor.x = cursor.y = -1;
3016         attrib.data = 0xe0;
3017         attrib.num = 20;
3018         width = 80;
3019         height = 25;
3020         char_height = hireso ? 16 : 8;
3021         skip_line = false;
3022         vretrace = hireso ? 3 : 7;
3023         timing_changed = false;
3024         reverse = 0;
3025         intr_mask = 3;
3026 }
3027
3028 void pc88_crtc_t::write_cmd(uint8_t data)
3029 {
3030         cmd = (data >> 5) & 7;
3031         cmd_ptr = 0;
3032         switch(cmd) {
3033         case 0: // reset
3034                 status &= ~0x16;
3035                 status |= 0x80; // fix
3036                 cursor.x = cursor.y = -1;
3037                 break;
3038         case 1: // start display
3039                 reverse = data & 1;
3040 //              status |= 0x10;
3041                 status |= 0x90; // fix
3042                 status &= ~8;
3043                 break;
3044         case 2: // set interrupt mask
3045                 if(!(data & 1)) {
3046 //                      status = 0; // from M88
3047                         status = 0x80; // fix
3048                 }
3049                 intr_mask = data & 3;
3050                 break;
3051         case 3: // read light pen
3052                 status &= ~1;
3053                 break;
3054         case 4: // load cursor position ON/OFF
3055                 cursor.type = (data & 1) ? cursor.mode : -1;
3056                 break;
3057         case 5: // reset interrupt
3058                 status &= ~6;
3059                 break;
3060         case 6: // reset counters
3061                 status &= ~6;
3062                 break;
3063         }
3064 }
3065
3066 void pc88_crtc_t::write_param(uint8_t data)
3067 {
3068         switch(cmd) {
3069         case 0:
3070                 switch(cmd_ptr) {
3071                 case 0:
3072                         width = min((data & 0x7f) + 2, 80);
3073                         break;
3074                 case 1:
3075                         if(height != (data & 0x3f) + 1) {
3076                                 height = (data & 0x3f) + 1;
3077                                 timing_changed = true;
3078                         }
3079                         blink.rate = 32 * ((data >> 6) + 1);
3080                         break;
3081                 case 2:
3082                         if(char_height != (data & 0x1f) + 1) {
3083                                 char_height = (data & 0x1f) + 1;
3084                                 timing_changed = true;
3085                         }
3086                         cursor.mode = (data >> 5) & 3;
3087                         skip_line = ((data & 0x80) != 0);
3088                         break;
3089                 case 3:
3090                         if(vretrace != ((data >> 5) & 7) + 1) {
3091                                 vretrace = ((data >> 5) & 7) + 1;
3092                                 timing_changed = true;
3093                         }
3094                         break;
3095                 case 4:
3096                         mode = (data >> 5) & 7;
3097                         attrib.num = (mode & 1) ? 0 : min((data & 0x1f) + 1, 20);
3098                         break;
3099                 }
3100                 break;
3101         case 4:
3102                 switch(cmd_ptr) {
3103                 case 0:
3104                         cursor.x = data;
3105                         break;
3106                 case 1:
3107                         cursor.y = data;
3108                         break;
3109                 }
3110                 break;
3111         case 6:
3112                 status = 0;
3113                 break;
3114         }
3115         cmd_ptr++;
3116 }
3117
3118 uint32_t pc88_crtc_t::read_param()
3119 {
3120         uint32_t val = 0xff;
3121         
3122         switch(cmd) {
3123         case 3: // read light pen
3124                 switch(cmd_ptr) {
3125                 case 0:
3126                         val = 0; // fix me
3127                         break;
3128                 case 1:
3129                         val = 0; // fix me
3130                         break;
3131                 }
3132                 break;
3133         default:
3134                 // XM8 version 1.10
3135                 val = read_status();
3136                 break;
3137         }
3138         cmd_ptr++;
3139         return val;
3140 }
3141
3142 uint32_t pc88_crtc_t::read_status()
3143 {
3144         if(status & 8) {
3145                 return status & ~0x10;
3146         } else {
3147                 return status;
3148         }
3149 }
3150
3151 void pc88_crtc_t::start()
3152 {
3153         memset(buffer, 0, sizeof(buffer));
3154         buffer_ptr = 0;
3155         vblank = false;
3156 }
3157
3158 void pc88_crtc_t::finish()
3159 {
3160         if((status & 0x10) && !(intr_mask & 1)) {
3161                 status |= 2;
3162         }
3163         vblank = true;
3164 }
3165
3166 void pc88_crtc_t::write_buffer(uint8_t data)
3167 {
3168         buffer[(buffer_ptr++) & 0x3fff] = data;
3169 }
3170
3171 uint8_t pc88_crtc_t::read_buffer(int ofs)
3172 {
3173         if(ofs < buffer_ptr) {
3174                 return buffer[ofs];
3175         }
3176         // dma underrun occurs !!!
3177         status |= 8;
3178 //      status &= ~0x10;
3179         return 0;
3180 }
3181
3182 void pc88_crtc_t::update_blink()
3183 {
3184         // from m88
3185         if(++blink.counter > blink.rate) {
3186                 blink.counter = 0;
3187         }
3188         blink.attrib = (blink.counter < blink.rate / 4) ? 2 : 0;
3189         blink.cursor = (blink.counter <= blink.rate / 4) || (blink.rate / 2 <= blink.counter && blink.counter <= 3 * blink.rate / 4);
3190 }
3191
3192 void pc88_crtc_t::expand_buffer(bool hireso, bool line400)
3193 {
3194         int char_height_tmp = char_height;
3195         int exitline = -1;
3196         
3197         if(!hireso) {
3198                 char_height_tmp <<= 1;
3199         }
3200         if(line400 || !skip_line) {
3201                 char_height_tmp >>= 1;
3202         }
3203         if(!(status & 0x10)) {
3204                 exitline = 0;
3205                 goto underrun;
3206         }
3207         for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3208                 for(int cx = 0; cx < width; cx++) {
3209                         text.expand[cy][cx] = read_buffer(ofs + cx);
3210                 }
3211                 if((status & 8) && exitline == -1) {
3212                         exitline = cy;
3213 //                      goto underrun;
3214                 }
3215         }
3216         if(mode & 4) {
3217                 // non transparent
3218                 for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3219                         for(int cx = 0; cx < width; cx += 2) {
3220                                 set_attrib(read_buffer(ofs + cx + 1));
3221                                 attrib.expand[cy][cx] = attrib.expand[cy][cx + 1] = attrib.data;
3222                         }
3223                         if((status & 8) && exitline == -1) {
3224                                 exitline = cy;
3225 //                              goto underrun;
3226                         }
3227                 }
3228         } else {
3229                 // transparent
3230                 if(mode & 1) {
3231                         memset(attrib.expand, 0xe0, sizeof(attrib.expand));
3232                 } else {
3233                         for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
3234                                 uint8_t flags[128];
3235                                 memset(flags, 0, sizeof(flags));
3236                                 for(int i = 2 * (attrib.num - 1); i >= 0; i -= 2) {
3237                                         flags[read_buffer(ofs + i + 80) & 0x7f] = 1;
3238                                 }
3239                                 attrib.data &= 0xf3; // for PC-8801mkIIFR \95t\91®\83f\83\82
3240                                 
3241                                 for(int cx = 0, pos = 0; cx < width; cx++) {
3242                                         if(flags[cx]) {
3243                                                 set_attrib(read_buffer(ofs + pos + 81));
3244                                                 pos += 2;
3245                                         }
3246                                         attrib.expand[cy][cx] = attrib.data;
3247                                 }
3248                                 if((status & 8) && exitline == -1) {
3249                                         exitline = cy;
3250 //                                      goto underrun;
3251                                 }
3252                         }
3253                 }
3254         }
3255         if(cursor.x < 80 && cursor.y < 200) {
3256                 if((cursor.type & 1) && blink.cursor) {
3257                         // no cursor
3258                 } else {
3259                         static const uint8_t ctype[5] = {0, 8, 8, 1, 1};
3260                         attrib.expand[cursor.y][cursor.x] ^= ctype[cursor.type + 1];
3261                 }
3262         }
3263         // only burst mode
3264 underrun:
3265         if(exitline != -1) {
3266                 for(int cy = exitline; cy < 200; cy++) {
3267                         memset(&text.expand[cy][0], 0, width);
3268 #if 1
3269                         // SORCERIAN Music Library ver-2.1
3270                         memset(&attrib.expand[cy][0], 0xe0, width); // color=7
3271 #else
3272                         // from ePC-8801MA\89ü
3273                         memset(&attrib.expand[cy][0], 0x00, width);
3274 #endif
3275                 }
3276         }
3277 }
3278
3279 void pc88_crtc_t::set_attrib(uint8_t code)
3280 {
3281         if(mode & 2) {
3282                 // color
3283                 if(code & 8) {
3284                         attrib.data = (attrib.data & 0x0f) | (code & 0xf0);
3285                 } else {
3286                         attrib.data = (attrib.data & 0xf0) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
3287                         attrib.data ^= reverse;
3288                         attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
3289                 }
3290         } else {
3291                 attrib.data = 0xe0 | ((code >> 3) & 0x10) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
3292                 attrib.data ^= reverse;
3293                 attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
3294         }
3295 }
3296
3297 /* ----------------------------------------------------------------------------
3298         DMAC (uPD8257)
3299 ---------------------------------------------------------------------------- */
3300
3301 void pc88_dmac_t::write_io8(uint32_t addr, uint32_t data)
3302 {
3303         int c = (addr >> 1) & 3;
3304         
3305         switch(addr & 0x0f) {
3306         case 0:
3307         case 2:
3308         case 4:
3309         case 6:
3310                 if(!high_low) {
3311                         if((mode & 0x80) && c == 2) {
3312                                 ch[3].addr.b.l = data;
3313                         }
3314                         ch[c].addr.b.l = data;
3315                 } else {
3316                         if((mode & 0x80) && c == 2) {
3317                                 ch[3].addr.b.h = data;
3318                                 ch[3].addr.b.h2 = ch[3].addr.b.h3 = 0;
3319                         }
3320                         ch[c].addr.b.h = data;
3321                         ch[c].addr.b.h2 = ch[c].addr.b.h3 = 0;
3322                 }
3323                 high_low = !high_low;
3324                 break;
3325         case 1:
3326         case 3:
3327         case 5:
3328         case 7:
3329                 if(!high_low) {
3330                         if((mode & 0x80) && c == 2) {
3331                                 ch[3].count.b.l = data;
3332                         }
3333                         ch[c].count.b.l = data;
3334                 } else {
3335                         if((mode & 0x80) && c == 2) {
3336                                 ch[3].count.b.h = data & 0x3f;
3337                                 ch[3].count.b.h2 = ch[3].count.b.h3 = 0;
3338                                 ch[3].mode = data & 0xc0;
3339                         }
3340                         ch[c].count.b.h = data & 0x3f;
3341                         ch[c].count.b.h2 = ch[c].count.b.h3 = 0;
3342                         ch[c].mode = data & 0xc0;
3343                 }
3344                 high_low = !high_low;
3345                 break;
3346         case 8:
3347                 mode = data;
3348                 high_low = false;
3349                 break;
3350         }
3351 }
3352
3353 uint32_t pc88_dmac_t::read_io8(uint32_t addr)
3354 {
3355         uint32_t val = 0xff;
3356         int c = (addr >> 1) & 3;
3357         
3358         switch(addr & 0x0f) {
3359         case 0:
3360         case 2:
3361         case 4:
3362         case 6:
3363                 if(!high_low) {
3364                         val = ch[c].addr.b.l;
3365                 } else {
3366                         val = ch[c].addr.b.h;
3367                 }
3368                 high_low = !high_low;
3369                 break;
3370         case 1:
3371         case 3:
3372         case 5:
3373         case 7:
3374                 if(!high_low) {
3375                         val = ch[c].count.b.l;
3376                 } else {
3377                         val = (ch[c].count.b.h & 0x3f) | ch[c].mode;
3378                 }
3379                 high_low = !high_low;
3380                 break;
3381         case 8:
3382                 val = status;
3383                 status &= 0xf0;
3384 //              high_low = false;
3385                 break;
3386         }
3387         return val;
3388 }
3389
3390 void pc88_dmac_t::start(int c)
3391 {
3392         if(mode & (1 << c)) {
3393                 status &= ~(1 << c);
3394                 ch[c].running = true;
3395         } else {
3396                 ch[c].running = false;
3397         }
3398 }
3399
3400 void pc88_dmac_t::run(int c, int nbytes)
3401 {
3402         if(ch[c].running) {
3403                 while(nbytes > 0 && ch[c].count.sd >= 0) {
3404                         if(ch[c].mode == 0x80) {
3405                                 ch[c].io->write_dma_io8(0, mem->read_dma_data8(ch[c].addr.w.l));
3406                         } else if(ch[c].mode == 0x40) {
3407                                 mem->write_dma_data8(ch[c].addr.w.l, ch[c].io->read_dma_io8(0));
3408                         } else if(ch[c].mode == 0x00) {
3409                                 ch[c].io->read_dma_io8(0); // verify
3410                         }
3411                         ch[c].addr.sd++;
3412                         ch[c].count.sd--;
3413                         nbytes--;
3414                 }
3415                 if(ch[c].count.sd < 0) {
3416                         finish(c);
3417                 }
3418         }
3419 }
3420
3421 void pc88_dmac_t::finish(int c)
3422 {
3423         if(ch[c].running) {
3424                 while(ch[c].count.sd >= 0) {
3425                         if(ch[c].mode == 0x80) {
3426                                 ch[c].io->write_dma_io8(0, mem->read_dma_data8(ch[c].addr.w.l));
3427                         } else if(ch[c].mode == 0x40) {
3428                                 mem->write_dma_data8(ch[c].addr.w.l, ch[c].io->read_dma_io8(0));
3429                         } else if(ch[c].mode == 0x00) {
3430                                 ch[c].io->read_dma_io8(0); // verify
3431                         }
3432                         ch[c].addr.sd++;
3433                         ch[c].count.sd--;
3434                 }
3435                 if((mode & 0x80) && c == 2) {
3436                         ch[2].addr.sd = ch[3].addr.sd;
3437                         ch[2].count.sd = ch[3].count.sd;
3438                         ch[2].mode = ch[3].mode;
3439                 } else if(mode & 0x40) {
3440                         mode &= ~(1 << c);
3441                 }
3442                 status |= (1 << c);
3443                 ch[c].running = false;
3444         }
3445 }
3446
3447 #define STATE_VERSION   10
3448
3449 bool PC88::process_state(FILEIO* state_fio, bool loading)
3450 {
3451         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
3452                 return false;
3453         }
3454         if(!state_fio->StateCheckInt32(this_device_id)) {
3455                 return false;
3456         }
3457         state_fio->StateArray(ram, sizeof(ram), 1);
3458 #if defined(PC88_EXRAM_BANKS)
3459         state_fio->StateArray(exram, sizeof(exram), 1);
3460 #endif
3461         state_fio->StateArray(gvram, sizeof(gvram), 1);
3462         state_fio->StateArray(tvram, sizeof(tvram), 1);
3463         state_fio->StateArray(port, sizeof(port), 1);
3464         state_fio->StateValue(crtc.blink.rate);
3465         state_fio->StateValue(crtc.blink.counter);
3466         state_fio->StateValue(crtc.blink.cursor);
3467         state_fio->StateValue(crtc.blink.attrib);
3468         state_fio->StateValue(crtc.cursor.type);
3469         state_fio->StateValue(crtc.cursor.mode);
3470         state_fio->StateValue(crtc.cursor.x);
3471         state_fio->StateValue(crtc.cursor.y);
3472         state_fio->StateValue(crtc.attrib.data);
3473         state_fio->StateValue(crtc.attrib.num);
3474         state_fio->StateArray(&crtc.attrib.expand[0][0], sizeof(crtc.attrib.expand), 1);
3475         state_fio->StateArray(&crtc.text.expand[0][0], sizeof(crtc.text.expand), 1);
3476         state_fio->StateValue(crtc.width);
3477         state_fio->StateValue(crtc.height);
3478         state_fio->StateValue(crtc.char_height);
3479         state_fio->StateValue(crtc.skip_line);
3480         state_fio->StateValue(crtc.vretrace);
3481         state_fio->StateValue(crtc.timing_changed);
3482         state_fio->StateArray(crtc.buffer, sizeof(crtc.buffer), 1);
3483         state_fio->StateValue(crtc.buffer_ptr);
3484         state_fio->StateValue(crtc.cmd);
3485         state_fio->StateValue(crtc.cmd_ptr);
3486         state_fio->StateValue(crtc.mode);
3487         state_fio->StateValue(crtc.reverse);
3488         state_fio->StateValue(crtc.intr_mask);
3489         state_fio->StateValue(crtc.status);
3490         state_fio->StateValue(crtc.vblank);
3491         for(int i = 0; i < array_length(dmac.ch); i++) {
3492                 state_fio->StateValue(dmac.ch[i].addr);
3493                 state_fio->StateValue(dmac.ch[i].count);
3494                 state_fio->StateValue(dmac.ch[i].mode);
3495                 state_fio->StateValue(dmac.ch[i].nbytes);
3496                 state_fio->StateValue(dmac.ch[i].running);
3497         }
3498         state_fio->StateValue(dmac.mode);
3499         state_fio->StateValue(dmac.status);
3500         state_fio->StateValue(dmac.high_low);
3501         state_fio->StateArray(alu_reg, sizeof(alu_reg), 1);
3502         state_fio->StateValue(gvram_plane);
3503         state_fio->StateValue(gvram_sel);
3504         state_fio->StateValue(cpu_clock_low);
3505 #if defined(SUPPORT_PC88_HIGH_CLOCK)
3506         state_fio->StateValue(cpu_clock_high_fe2);
3507 #endif
3508         state_fio->StateValue(mem_wait_on);
3509         state_fio->StateValue(m1_wait_clocks);
3510         state_fio->StateValue(f000_m1_wait_clocks);
3511         state_fio->StateValue(mem_wait_clocks_r);
3512         state_fio->StateValue(mem_wait_clocks_w);
3513         state_fio->StateValue(tvram_wait_clocks_r);
3514         state_fio->StateValue(tvram_wait_clocks_w);
3515         state_fio->StateValue(gvram_wait_clocks_r);
3516         state_fio->StateValue(gvram_wait_clocks_w);
3517         state_fio->StateValue(busreq_clocks);
3518         for(int i = 0; i < array_length(palette); i++) {
3519                 state_fio->StateValue(palette[i].r);
3520                 state_fio->StateValue(palette[i].g);
3521                 state_fio->StateValue(palette[i].b);
3522         }
3523 //      state_fio->StateValue(update_palette);
3524         state_fio->StateValue(hireso);
3525         state_fio->StateArray(&text[0][0], sizeof(text), 1);
3526         state_fio->StateArray(&graph[0][0], sizeof(graph), 1);
3527 /*
3528         for(int i = 0; i < 9; i++) {
3529                 state_fio->StateValue(palette_digital[i].b);
3530                 state_fio->StateValue(palette_digital[i].r);
3531                 state_fio->StateValue(palette_digital[i].g);
3532                 state_fio->StateValue(palette_analog [i].b);
3533                 state_fio->StateValue(palette_analog [i].r);
3534                 state_fio->StateValue(palette_analog [i].g);
3535         }
3536 */
3537         state_fio->StateValue(usart_dcd);
3538         state_fio->StateValue(opn_busy);
3539         state_fio->StateValue(key_caps);
3540         state_fio->StateValue(key_kana);
3541 #ifdef SUPPORT_PC88_JOYSTICK
3542         state_fio->StateValue(mouse_strobe_clock);
3543         state_fio->StateValue(mouse_strobe_clock_lim);
3544         state_fio->StateValue(mouse_phase);
3545         state_fio->StateValue(mouse_dx);
3546         state_fio->StateValue(mouse_dy);
3547         state_fio->StateValue(mouse_lx);
3548         state_fio->StateValue(mouse_ly);
3549 #endif
3550         state_fio->StateValue(intr_req);
3551         state_fio->StateValue(intr_req_sound);
3552 #ifdef SUPPORT_PC88_SB2
3553         state_fio->StateValue(intr_req_sb2);
3554 #endif
3555         state_fio->StateValue(intr_mask1);
3556         state_fio->StateValue(intr_mask2);
3557         state_fio->StateValue(cmt_play);
3558         state_fio->StateValue(cmt_rec);
3559         state_fio->StateArray(rec_file_path, sizeof(rec_file_path), 1);
3560         if(loading) {
3561                 int length_tmp = state_fio->FgetInt32_LE();
3562                 if(cmt_rec) {
3563                         cmt_fio->Fopen(rec_file_path, FILEIO_READ_WRITE_NEW_BINARY);
3564                         while(length_tmp != 0) {
3565                                 uint8_t buffer[1024];
3566                                 int length_rw = min(length_tmp, (int)sizeof(buffer));
3567                                 state_fio->Fread(buffer, length_rw, 1);
3568                                 if(cmt_fio->IsOpened()) {
3569                                         cmt_fio->Fwrite(buffer, length_rw, 1);
3570                                 }
3571                                 length_tmp -= length_rw;
3572                         }
3573                 }
3574         } else {
3575                 if(cmt_rec && cmt_fio->IsOpened()) {
3576                         int length_tmp = (int)cmt_fio->Ftell();
3577                         cmt_fio->Fseek(0, FILEIO_SEEK_SET);
3578                         state_fio->FputInt32_LE(length_tmp);
3579                         while(length_tmp != 0) {
3580                                 uint8_t buffer[1024];
3581                                 int length_rw = min(length_tmp, (int)sizeof(buffer));
3582                                 cmt_fio->Fread(buffer, length_rw, 1);
3583                                 state_fio->Fwrite(buffer, length_rw, 1);
3584                                 length_tmp -= length_rw;
3585                         }
3586                 } else {
3587                         state_fio->FputInt32_LE(0);
3588                 }
3589         }
3590         state_fio->StateValue(cmt_bufptr);
3591         state_fio->StateValue(cmt_bufcnt);
3592         state_fio->StateArray(cmt_buffer, sizeof(cmt_buffer), 1);
3593         state_fio->StateArray(cmt_data_carrier, sizeof(cmt_data_carrier), 1);
3594         state_fio->StateValue(cmt_data_carrier_cnt);
3595         state_fio->StateValue(cmt_register_id);
3596         state_fio->StateValue(beep_on);
3597         state_fio->StateValue(beep_signal);
3598         state_fio->StateValue(sing_signal);
3599 #ifdef SUPPORT_PC88_PCG8100
3600         state_fio->StateValue(pcg_addr);
3601         state_fio->StateValue(pcg_data);
3602         state_fio->StateValue(pcg_ctrl);
3603         state_fio->StateArray(pcg_pattern, sizeof(pcg_pattern), 1);
3604 #endif
3605 #ifdef SUPPORT_PC88_CDROM
3606         state_fio->StateValue(cdda_register_id);
3607         state_fio->StateValue(cdda_volume);
3608 #endif
3609 #ifdef NIPPY_PATCH
3610         state_fio->StateValue(nippy_patch);
3611 #endif
3612         
3613         // post process
3614         if(loading) {
3615 #if defined(_PC8001SR)
3616                 update_n80_write();
3617                 update_n80_read();
3618 #else
3619                 update_low_memmap();
3620                 update_tvram_memmap();
3621 #endif
3622                 // force update palette when state file is loaded
3623                 update_palette = true;
3624         }
3625         return true;
3626 }
3627 }
3628