2 Systems Formulate BUBCOM80 Emulator 'eBUBCOM80'
4 Author : Takeda.Toshiya
12 #include "../pcm1bit.h"
16 #define EVENT_BUSREQ 0
18 void DISPLAY::initialize()
20 for(int i = 0; i < 256; i++) {
21 uint8_t *dest = sg_pattern + 8 * i;
22 dest[0] = dest[1] = ((i & 0x01) ? 0xf0 : 0) | ((i & 0x02) ? 0x0f : 0);
23 dest[2] = dest[3] = ((i & 0x04) ? 0xf0 : 0) | ((i & 0x08) ? 0x0f : 0);
24 dest[4] = dest[5] = ((i & 0x10) ? 0xf0 : 0) | ((i & 0x20) ? 0x0f : 0);
25 dest[6] = dest[7] = ((i & 0x40) ? 0xf0 : 0) | ((i & 0x80) ? 0x0f : 0);
27 memset(font, 0x00, sizeof(font));
28 memset(vram, 0x00, sizeof(vram));
30 for(int i = 0; i < 8; i++) {
31 palette_text_pc [i] = RGBA_COLOR((i & 2) ? 255 : 0, (i & 4) ? 255 : 0, (i & 1) ? 255 : 0, 255); // A is a flag for crt filter
32 palette_graph_pc[i] = RGBA_COLOR((i & 2) ? 255 : 0, (i & 4) ? 255 : 0, (i & 1) ? 255 : 0, 0);
38 register_frame_event(this);
39 register_vline_event(this);
42 #define STORE_DMAC_CONTEXTS() \
43 DEVICE *d_mem = dmac.mem; \
44 DEVICE *d_ch0 = dmac.ch[0].io; \
45 DEVICE *d_ch1 = dmac.ch[1].io; \
46 DEVICE *d_ch2 = dmac.ch[2].io; \
47 DEVICE *d_ch3 = dmac.ch[3].io
49 #define RESTORE_DMAC_CONTEXTS() \
51 dmac.ch[0].io = d_ch0; \
52 dmac.ch[1].io = d_ch1; \
53 dmac.ch[2].io = d_ch2; \
54 dmac.ch[3].io = d_ch3;
58 memset(&crtc, 0, sizeof(crtc));
62 STORE_DMAC_CONTEXTS();
63 memset(&dmac, 0, sizeof(dmac));
64 RESTORE_DMAC_CONTEXTS();
67 void DISPLAY::write_io8(uint32_t addr, uint32_t data)
71 crtc.write_param(data);
72 if(crtc.timing_changed) {
74 crtc.timing_changed = false;
81 d_prn->write_signal(SIG_PRINTER_DATA, data, 0xff);
84 d_cmt->write_signal(SIG_CMT_REMOTE, data, 0x01);
85 color = ((data & 0x04) != 0);
86 width40 = ((data & 0x08) != 0);
87 d_pcm->write_signal(SIG_PCM1BIT_ON, data, 0x10);
88 d_prn->write_signal(SIG_PRINTER_STROBE, data, 0x20);
99 dmac.write_io8(addr, data);
105 if(addr >= 0x800 && addr < 0x1000) {
106 font[addr & 0x7ff] = data;
107 } else if(addr >= 0x4000 && addr < 0x10000) {
114 uint32_t DISPLAY::read_io8(uint32_t addr)
118 return crtc.read_param();
120 return crtc.read_status();
122 return d_prn->read_signal(SIG_PRINTER_BUSY) & 0x40;
124 // bit1: 1=WIDTH80, 0=WIDTH40
125 return 0xff; // dipswitch???
135 return dmac.read_io8(addr);
137 return (crtc.vblank ? 0x40 : 0);
139 if(addr >= 0x800 && addr < 0x1000) {
140 return font[addr & 0x7ff];
141 } else if(addr >= 0x4000 && addr < 0x10000) {
149 void DISPLAY::write_dma_io8(uint32_t addr, uint32_t data)
152 crtc.write_buffer(data);
155 void DISPLAY::write_signal(int id, uint32_t data, uint32_t mask)
158 case SIG_DISPLAY_DMAC_CH0:
159 case SIG_DISPLAY_DMAC_CH1:
160 case SIG_DISPLAY_DMAC_CH2:
161 case SIG_DISPLAY_DMAC_CH3:
163 if(!dmac.ch[id].running) {
172 void DISPLAY::event_callback(int event_id, int err)
176 d_cpu->write_signal(SIG_CPU_BUSREQ, 0, 0);
181 void DISPLAY::event_frame()
186 void DISPLAY::event_vline(int v, int clock)
188 int disp_line = crtc.height * crtc.char_height;
191 if(crtc.status & 0x10) {
192 // start dma transfer to crtc
194 if(!dmac.ch[2].running) {
195 // dma underrun occurs !!!
197 // crtc.status &= ~0x10;
202 // from memory access test on PC-8801MA2 (XM8 version 1.20)
203 busreq_clocks = (int)((double)(dmac.ch[2].count.sd + 1) * 5.95 / (double)disp_line + 0.5);
208 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
210 d_cpu->write_signal(SIG_CPU_BUSREQ, 1, 1);
211 register_event_by_clock(this, EVENT_BUSREQ, busreq_clocks, false, NULL);
212 // run dma transfer to crtc
213 if((v % crtc.char_height) == 0) {
214 for(int i = 0; i < 80 + crtc.attrib.num * 2; i++) {
219 } else if(v == disp_line) {
220 if(/*(crtc.status & 0x10) && */dmac.ch[2].running) {
223 crtc.expand_buffer();
228 void DISPLAY::update_timing()
230 int lines_per_frame = (crtc.height + crtc.vretrace) * crtc.char_height;
231 double frames_per_sec = 15980.0 / (double)lines_per_frame;
233 set_frames_per_sec(frames_per_sec);
234 set_lines_per_frame(lines_per_frame);
237 void DISPLAY::draw_screen()
242 for(int y = 0; y < 200; y++) {
243 scrntype_t* dest0 = emu->get_screen_buffer(y * 2);
244 scrntype_t* dest1 = emu->get_screen_buffer(y * 2 + 1);
245 uint8_t* src_t = text[y];
246 uint8_t* src_g = graph[y];
248 for(int x = 0; x < 640; x++) {
249 uint32_t t = src_t[x];
250 uint32_t g = src_g[x];
251 dest0[x] = t ? palette_text_pc[t & 7] : palette_graph_pc[g ? g : (mode & 7)];
253 if(config.scan_line) {
254 memset(dest1, 0, 640 * sizeof(scrntype_t));
256 for(int x = 0; x < 640; x++) {
261 emu->screen_skip_line(true);
267 bit7: graph=1/character=0
277 void DISPLAY::draw_text()
279 if(crtc.status & 0x88) {
281 crtc.status &= ~0x80;
282 memset(crtc.text.expand, 0, 200 * 80);
283 memset(crtc.attrib.expand, crtc.reverse ? 3 : 2, 200 * 80);
285 // for Advanced Fantasian Opening (20line) (XM8 version 1.00)
286 if(!(crtc.status & 0x10)) {
287 // if(!(crtc.status & 0x10) || (crtc.status & 8)) {
288 memset(crtc.text.expand, 0, 200 * 80);
289 for(int y = 0; y < 200; y++) {
290 for(int x = 0; x < 80; x++) {
291 crtc.attrib.expand[y][x] &= 0x70;
292 crtc.attrib.expand[y][x] |= 0x02;
295 // memset(crtc.attrib.expand, 2, 200 * 80);
299 memset(text, 8, sizeof(text));
301 int char_height = crtc.char_height;
306 for(int cy = 0, ytop = 0; cy < crtc.height && ytop < 200; cy++, ytop += char_height) {
307 for(int x = 0, cx = 0; cx < crtc.width; x += 8, cx++) {
308 if(width40 && (cx & 1)) {
311 uint8_t attrib = crtc.attrib.expand[cy][cx];
312 uint8_t color = (attrib & 0x70) ? (attrib >> 4) : 8;
313 bool under_line = ((attrib & 8) != 0);
314 bool upper_line = ((attrib & 4) != 0);
315 bool secret = ((attrib & 2) != 0);
316 bool reverse = ((attrib & 1) != 0);
318 uint8_t code = secret ? 0 : crtc.text.expand[cy][cx];
319 uint8_t *pattern = ((attrib & 0x80) ? sg_pattern : font) + code * 8;
321 for(int l = 0, y = ytop; l < char_height && y < 200; l++, y++) {
322 uint8_t pat = (l < 8) ? pattern[l] : 0;
323 if((upper_line && l == 0) || (under_line && l >= 7)) {
330 uint8_t *dest = &text[y][x];
332 dest[ 0] = dest[ 1] = (pat & 0x80) ? color : 0;
333 dest[ 2] = dest[ 3] = (pat & 0x40) ? color : 0;
334 dest[ 4] = dest[ 5] = (pat & 0x20) ? color : 0;
335 dest[ 6] = dest[ 7] = (pat & 0x10) ? color : 0;
336 dest[ 8] = dest[ 9] = (pat & 0x08) ? color : 0;
337 dest[10] = dest[11] = (pat & 0x04) ? color : 0;
338 dest[12] = dest[13] = (pat & 0x02) ? color : 0;
339 dest[14] = dest[15] = (pat & 0x01) ? color : 0;
341 dest[0] = (pat & 0x80) ? color : 0;
342 dest[1] = (pat & 0x40) ? color : 0;
343 dest[2] = (pat & 0x20) ? color : 0;
344 dest[3] = (pat & 0x10) ? color : 0;
345 dest[4] = (pat & 0x08) ? color : 0;
346 dest[5] = (pat & 0x04) ? color : 0;
347 dest[6] = (pat & 0x02) ? color : 0;
348 dest[7] = (pat & 0x01) ? color : 0;
355 void DISPLAY::draw_graph()
357 uint8_t *vram_b, *vram_r, *vram_g;
359 switch(mode & 0xe0) {
361 vram_b = vram_r = vram_g = vram + 0x0000; // null
364 vram_b = vram_r = vram_g = vram + 0x8000; // blue
367 vram_b = vram_r = vram_g = vram + 0xc000; // red
370 vram_b = vram_r = vram_g = vram + 0x4000; // green
373 vram_b = vram + 0x8000; // blue
374 vram_r = vram + 0xc000; // red
375 vram_g = vram + 0x4000; // green
378 for(int y = 0, src = 0; y < 200; y ++) {
379 for(int x = 0; x < 80; x++) {
380 uint8_t b = vram_b[src];
381 uint8_t r = vram_r[src];
382 uint8_t g = vram_g[src];
383 uint8_t* d = &graph[y][x << 3];
385 d[0] = ((b & 0x80) >> 7) | ((r & 0x80) >> 6) | ((g & 0x80) >> 5);
386 d[1] = ((b & 0x40) >> 6) | ((r & 0x40) >> 5) | ((g & 0x40) >> 4);
387 d[2] = ((b & 0x20) >> 5) | ((r & 0x20) >> 4) | ((g & 0x20) >> 3);
388 d[3] = ((b & 0x10) >> 4) | ((r & 0x10) >> 3) | ((g & 0x10) >> 2);
389 d[4] = ((b & 0x08) >> 3) | ((r & 0x08) >> 2) | ((g & 0x08) >> 1);
390 d[5] = ((b & 0x04) >> 2) | ((r & 0x04) >> 1) | ((g & 0x04) >> 0);
391 d[6] = ((b & 0x02) >> 1) | ((r & 0x02) >> 0) | ((g & 0x02) << 1);
392 d[7] = ((b & 0x01) >> 0) | ((r & 0x01) << 1) | ((g & 0x01) << 2);
394 src = (src + 1) & 0x3fff;
399 /* ----------------------------------------------------------------------------
401 ---------------------------------------------------------------------------- */
406 cursor.type = cursor.mode = -1;
407 cursor.x = cursor.y = -1;
415 timing_changed = false;
420 void crtc_t::write_cmd(uint8_t data)
422 cmd = (data >> 5) & 7;
427 status |= 0x80; // fix
428 cursor.x = cursor.y = -1;
430 case 1: // start display
433 status |= 0x90; // fix
436 case 2: // set interrupt mask
438 // status = 0; // from M88
439 status = 0x80; // fix
441 intr_mask = data & 3;
443 case 3: // read light pen
446 case 4: // load cursor position ON/OFF
447 cursor.type = (data & 1) ? cursor.mode : -1;
449 case 5: // reset interrupt
452 case 6: // reset counters
458 void crtc_t::write_param(uint8_t data)
464 width = min((data & 0x7f) + 2, 80);
467 if(height != (data & 0x3f) + 1) {
468 height = (data & 0x3f) + 1;
469 timing_changed = true;
471 blink.rate = 32 * ((data >> 6) + 1);
474 if(char_height != (data & 0x1f) + 1) {
475 char_height = (data & 0x1f) + 1;
476 timing_changed = true;
478 cursor.mode = (data >> 5) & 3;
479 skip_line = ((data & 0x80) != 0);
482 if(vretrace != ((data >> 5) & 7) + 1) {
483 vretrace = ((data >> 5) & 7) + 1;
484 timing_changed = true;
488 mode = (data >> 5) & 7;
489 attrib.num = (mode & 1) ? 0 : min((data & 0x1f) + 1, 20);
510 uint32_t crtc_t::read_param()
515 case 3: // read light pen
534 uint32_t crtc_t::read_status()
537 return status & ~0x10;
545 memset(buffer, 0, sizeof(buffer));
550 void crtc_t::finish()
552 if((status & 0x10) && !(intr_mask & 1)) {
558 void crtc_t::write_buffer(uint8_t data)
560 buffer[(buffer_ptr++) & 0x3fff] = data;
563 uint8_t crtc_t::read_buffer(int ofs)
565 if(ofs < buffer_ptr) {
568 // dma underrun occurs !!!
574 void crtc_t::update_blink()
577 if(++blink.counter > blink.rate) {
580 blink.attrib = (blink.counter < blink.rate / 4) ? 2 : 0;
581 blink.cursor = (blink.counter <= blink.rate / 4) || (blink.rate / 2 <= blink.counter && blink.counter <= 3 * blink.rate / 4);
584 void crtc_t::expand_buffer()
586 int char_height_tmp = char_height;
590 char_height_tmp <<= 1;
592 if(!(status & 0x10)) {
596 for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
597 for(int cx = 0; cx < width; cx++) {
598 text.expand[cy][cx] = read_buffer(ofs + cx);
600 if((status & 8) && exitline == -1) {
607 for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
608 for(int cx = 0; cx < width; cx += 2) {
609 set_attrib(read_buffer(ofs + cx + 1));
610 attrib.expand[cy][cx] = attrib.expand[cy][cx + 1] = attrib.data;
612 if((status & 8) && exitline == -1) {
620 memset(attrib.expand, 0x70, sizeof(attrib.expand));
622 for(int cy = 0, ytop = 0, ofs = 0; cy < height && ytop < 200; cy++, ytop += char_height_tmp, ofs += 80 + attrib.num * 2) {
624 memset(flags, 0, sizeof(flags));
625 for(int i = 2 * (attrib.num - 1); i >= 0; i -= 2) {
626 flags[read_buffer(ofs + i + 80) & 0x7f] = 1;
628 attrib.data &= 0xf3; // for PC-8801mkIIFR
\95t
\91®
\83f
\83\82
630 for(int cx = 0, pos = 0; cx < width; cx++) {
632 set_attrib(read_buffer(ofs + pos + 81));
635 attrib.expand[cy][cx] = attrib.data;
637 if((status & 8) && exitline == -1) {
644 if(cursor.x < 80 && cursor.y < 200) {
645 if((cursor.type & 1) && blink.cursor) {
648 static const uint8_t ctype[5] = {0, 8, 8, 1, 1};
649 attrib.expand[cursor.y][cursor.x] ^= ctype[cursor.type + 1];
655 for(int cy = exitline; cy < 200; cy++) {
656 memset(&text.expand[cy][0], 0, width);
657 memset(&attrib.expand[cy][0], 0x70, width); // color=7
662 void crtc_t::set_attrib(uint8_t code)
667 attrib.data = (attrib.data & 0x0f) | (code & 0xf0);
669 attrib.data = (attrib.data & 0xf0) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
670 attrib.data ^= reverse;
671 attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
674 attrib.data = 0x70 | (code & 0x80) | ((code >> 2) & 0x0d) | ((code << 1) & 2);
675 attrib.data ^= reverse;
676 attrib.data ^= ((code & 2) && !(code & 1)) ? blink.attrib : 0;
680 /* ----------------------------------------------------------------------------
682 ---------------------------------------------------------------------------- */
684 void dmac_t::write_io8(uint32_t addr, uint32_t data)
686 int c = (addr >> 1) & 3;
688 switch(addr & 0x0f) {
694 if((mode & 0x80) && c == 2) {
695 ch[3].addr.b.l = data;
697 ch[c].addr.b.l = data;
699 if((mode & 0x80) && c == 2) {
700 ch[3].addr.b.h = data;
702 ch[c].addr.b.h = data;
704 high_low = !high_low;
711 if((mode & 0x80) && c == 2) {
712 ch[3].count.b.l = data;
714 ch[c].count.b.l = data;
716 if((mode & 0x80) && c == 2) {
717 ch[3].count.b.h = data & 0x3f;
718 ch[3].mode = data & 0xc0;
720 ch[c].count.b.h = data & 0x3f;
721 ch[c].mode = data & 0xc0;
723 high_low = !high_low;
732 uint32_t dmac_t::read_io8(uint32_t addr)
735 int c = (addr >> 1) & 3;
737 switch(addr & 0x0f) {
743 val = ch[c].addr.b.l;
745 val = ch[c].addr.b.h;
747 high_low = !high_low;
754 val = ch[c].count.b.l;
756 val = (ch[c].count.b.h & 0x3f) | ch[c].mode;
758 high_low = !high_low;
769 void dmac_t::start(int c)
771 if(mode & (1 << c)) {
773 ch[c].running = true;
775 ch[c].running = false;
779 void dmac_t::finish(int c)
782 while(ch[c].count.sd >= 0) {
788 void dmac_t::run(int c)
791 if(ch[c].count.sd >= 0) {
792 if(ch[c].mode == 0x80) {
793 ch[c].io->write_dma_io8(0, mem->read_dma_data8(ch[c].addr.w.l));
794 } else if(ch[c].mode == 0x40) {
795 mem->write_dma_data8(ch[c].addr.w.l, ch[c].io->read_dma_io8(0));
800 if(ch[c].count.sd < 0) {
801 if((mode & 0x80) && c == 2) {
802 ch[2].addr.sd = ch[3].addr.sd;
803 ch[2].count.sd = ch[3].count.sd;
804 ch[2].mode = ch[3].mode;
805 } else if(mode & 0x40) {
809 ch[c].running = false;
814 #define STATE_VERSION 2
816 bool DISPLAY::process_state(FILEIO* state_fio, bool loading)
818 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
821 if(!state_fio->StateCheckInt32(this_device_id)) {
824 state_fio->StateBuffer(font, sizeof(font), 1);
825 state_fio->StateBuffer(vram, sizeof(vram), 1);
826 state_fio->StateInt32(busreq_clocks);
827 state_fio->StateBool(color);
828 state_fio->StateBool(width40);
829 state_fio->StateUint8(mode);
830 //state_fio->StateBuffer(&crtc, sizeof(crtc), 1);
834 state_fio->StateInt32(crtc.blink.rate);
835 state_fio->StateInt32(crtc.blink.counter);
836 state_fio->StateUint8(crtc.blink.cursor);
837 state_fio->StateUint8(crtc.blink.attrib);
840 state_fio->StateInt32(crtc.cursor.type);
841 state_fio->StateInt32(crtc.cursor.mode);
842 state_fio->StateInt32(crtc.cursor.x);
843 state_fio->StateInt32(crtc.cursor.y);
846 state_fio->StateUint8(crtc.attrib.data);
847 state_fio->StateInt32(crtc.attrib.num);
848 state_fio->StateBuffer(crtc.attrib.expand, sizeof(crtc.attrib.expand), 1);
851 state_fio->StateBuffer(crtc.text.expand, sizeof(crtc.text.expand), 1);
853 state_fio->StateInt32(crtc.width);
854 state_fio->StateInt32(crtc.height);
855 state_fio->StateInt32(crtc.char_height);
856 state_fio->StateBool(crtc.skip_line);
857 state_fio->StateInt32(crtc.vretrace);
858 state_fio->StateBool(crtc.timing_changed);
859 state_fio->StateBuffer(crtc.buffer, sizeof(crtc.buffer), 1);
860 state_fio->StateInt32(crtc.buffer_ptr);
861 state_fio->StateUint8(crtc.cmd);
862 state_fio->StateInt32(crtc.cmd_ptr);
863 state_fio->StateUint8(crtc.mode);
864 state_fio->StateUint8(crtc.reverse);
865 state_fio->StateUint8(crtc.intr_mask);
866 state_fio->StateUint8(crtc.status);
867 state_fio->StateBool(crtc.vblank);
869 STORE_DMAC_CONTEXTS();
870 //state_fio->StateBuffer(&dmac, sizeof(dmac), 1);
873 for(int i = 0; i < 4; i++) {
874 state_fio->StateUint32(dmac.ch[i].addr.d);
875 state_fio->StateUint32(dmac.ch[i].count.d);
876 state_fio->StateUint8(dmac.ch[i].mode);
877 state_fio->StateInt32(dmac.ch[i].nbytes);
878 state_fio->StateBool(dmac.ch[i].running);
880 state_fio->StateUint8(dmac.mode);
881 state_fio->StateUint8(dmac.status);
882 state_fio->StateBool(dmac.high_low);
884 RESTORE_DMAC_CONTEXTS();