diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 5b1ee9aa1da..798545f4811 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -2417,6 +2417,7 @@ files { MAME_DIR .. "src/mame/drivers/pc100.cpp", MAME_DIR .. "src/mame/drivers/pc9801.cpp", MAME_DIR .. "src/mame/includes/pc9801.h", + MAME_DIR .. "src/mame/video/pc9801.cpp", MAME_DIR .. "src/mame/machine/pc9801_kbd.cpp", MAME_DIR .. "src/mame/machine/pc9801_kbd.h", MAME_DIR .. "src/mame/machine/pc9801_cd.cpp", diff --git a/src/mame/drivers/pc9801.cpp b/src/mame/drivers/pc9801.cpp index 6ae9c53b245..a99cc7a041a 100644 --- a/src/mame/drivers/pc9801.cpp +++ b/src/mame/drivers/pc9801.cpp @@ -391,220 +391,10 @@ void pc9801_state::device_timer(emu_timer &timer, device_timer_id id, int param, { case TIMER_VBIRQ: m_pic1->ir2_w(0); + break; } } -void pc9801_state::video_start() -{ - m_tvram = std::make_unique(0x2000); - - // find memory regions - m_char_rom = memregion("chargen")->base(); - m_kanji_rom = memregion("kanji")->base(); -} - -uint32_t pc9801_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - bitmap.fill(m_palette->black_pen(), cliprect); - - /* graphics */ - m_hgdc2->screen_update(screen, bitmap, cliprect); - m_hgdc1->screen_update(screen, bitmap, cliprect); - return 0; -} - -UPD7220_DISPLAY_PIXELS_MEMBER( pc9801_state::hgdc_display_pixels ) -{ - const rgb_t *palette = m_palette->palette()->entry_list_raw(); - int xi; - int res_x,res_y; - uint8_t pen; - uint8_t colors16_mode; - - if(m_video_ff[DISPLAY_REG] == 0) //screen is off - return; - - colors16_mode = (m_ex_video_ff[ANALOG_16_MODE]) ? 16 : 8; - - if(m_ex_video_ff[ANALOG_256_MODE]) - { - uint8_t *ext_gvram = (uint8_t *)m_ext_gvram.target(); - for(xi=0;xi<16;xi++) - { - res_x = x + xi; - res_y = y; - - pen = ext_gvram[(address >> 1)*16+xi+(m_vram_disp*0x20000)]; - - bitmap.pix32(res_y, res_x) = palette[pen + 0x20]; - } - } - else - { - for(xi=0;xi<16;xi++) - { - res_x = x + xi; - res_y = y; - - pen = ((m_video_ram_2[((address & 0x7fff) + (0x08000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 1 : 0; - pen|= ((m_video_ram_2[((address & 0x7fff) + (0x10000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 2 : 0; - pen|= ((m_video_ram_2[((address & 0x7fff) + (0x18000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 4 : 0; - if(m_ex_video_ff[ANALOG_16_MODE]) - pen|= ((m_video_ram_2[((address & 0x7fff) + (0) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 8 : 0; - bitmap.pix32(res_y, res_x) = palette[pen + colors16_mode]; - } - } -} - -UPD7220_DRAW_TEXT_LINE_MEMBER( pc9801_state::hgdc_draw_text ) -{ - const rgb_t *palette = m_palette->palette()->entry_list_raw(); - int xi,yi; - int x; - uint8_t char_size; -// uint8_t interlace_on; - uint16_t tile; - uint8_t kanji_lr; - uint8_t kanji_sel; - uint8_t x_step; - - if(m_video_ff[DISPLAY_REG] == 0) //screen is off - return; - -// interlace_on = m_video_ff[INTERLACE_REG]; - char_size = m_video_ff[FONTSEL_REG] ? 16 : 8; - tile = 0; - - for(x=0;x> 8; - if(knj_tile) - { - /* Note: bit 7 doesn't really count, if a kanji is enabled then the successive tile is always the second part of it. - Trusted with Alice no Yakata, Animahjong V3, Aki no Tsukasa no Fushigi no Kabe, Apros ... - */ - //kanji_lr = (knj_tile & 0x80) >> 7; - //kanji_lr |= (tile & 0x80) >> 7; // Tokimeki Sports Gal 3 - tile &= 0x7f; - tile <<= 8; - tile |= (knj_tile & 0x7f); - kanji_sel = 1; - if((tile & 0x7c00) == 0x0800) // 8x16 charset selector - x_step = 1; - else - x_step = 2; -// kanji_lr = 0; - } - else - x_step = 1; - - - - for(kanji_lr=0;kanji_lr> 5; - - for(yi=0;yivisible_area().contains(res_x, res_y)) - continue; - - tile_data = 0; - - if(!secret) - { - /* TODO: priority */ - if(gfx_mode) - { - int gfx_bit; - tile_data = 0; - - /* - gfx strip mode: - - number refers to the bit number in the tile data. - This mode is identical to the one seen in PC-8801 - 00004444 - 11115555 - 22226666 - 33337777 - */ - - gfx_bit = (xi & 4); - gfx_bit+= (yi & (2 << (char_size == 16 ? 0x01 : 0x00)))>>(1+(char_size == 16)); - gfx_bit+= (yi & (4 << (char_size == 16 ? 0x01 : 0x00)))>>(1+(char_size == 16)); - - tile_data = ((tile >> gfx_bit) & 1) ? 0xff : 0x00; - } - else if(kanji_sel) - tile_data = (m_kanji_rom[tile*0x20+yi*2+kanji_lr]); - else - tile_data = (m_char_rom[tile*char_size+m_video_ff[FONTSEL_REG]*0x800+yi]); - } - - if(reverse) { tile_data^=0xff; } - if(u_line && yi == lr-1) { tile_data = 0xff; } - if(v_line) { tile_data|=8; } - - /* TODO: proper blink rate for these two */ - if(cursor_on && cursor_addr == tile_addr && m_screen->frame_number() & 0x10) - tile_data^=0xff; - - if(blink && m_screen->frame_number() & 0x10) - tile_data^=0xff; - - if(yi >= char_size) - pen = -1; - else - pen = (tile_data >> (7-xi) & 1) ? color : -1; - - if(pen != -1) - bitmap.pix32(res_y, res_x) = palette[pen]; - - if(m_video_ff[WIDTH40_REG]) - { - if(!m_screen->visible_area().contains(res_x+1, res_y)) - continue; - - if(pen != -1) - bitmap.pix32(res_y, res_x+1) = palette[pen]; - } - } - } - } - } -} WRITE8_MEMBER(pc9801_state::rtc_w) @@ -643,190 +433,6 @@ WRITE8_MEMBER(pc9801_state::vrtc_clear_w) m_pic1->ir2_w(0); } -WRITE8_MEMBER(pc9801_state::pc9801_video_ff_w) -{ - /* - TODO: this is my best bet so far. Register 4 is annoying, the pattern seems to be: - Write to video FF register Graphic -> 00 - Write to video FF register 200 lines -> 0x - Write to video FF register 200 lines -> 00 - - where x is the current mode. - */ - switch((data & 0x0e) >> 1) - { - case 1: - m_gfx_ff = 1; - if(data & 1) - logerror("Graphic f/f actually enabled!\n"); - break; - case 4: - if(m_gfx_ff) - { - m_video_ff[(data & 0x0e) >> 1] = data &1; - m_gfx_ff = 0; - } - break; - default: m_video_ff[(data & 0x0e) >> 1] = data & 1; break; - } - - if(0) - { - static const char *const video_ff_regnames[] = - { - "Attribute Select", // 0 - "Graphic", // 1 - "Column", // 2 - "Font Select", // 3 - "200 lines", // 4 - "KAC?", // 5 - "Memory Switch", // 6 - "Display ON" // 7 - }; - - logerror("Write to video FF register %s -> %02x\n",video_ff_regnames[(data & 0x0e) >> 1],data & 1); - } -} - - -READ8_MEMBER(pc9801_state::txt_scrl_r) -{ - //logerror("Read to display register [%02x]\n",offset+0x70); - /* TODO: ok? */ - if(offset <= 5) - return m_txt_scroll_reg[offset]; - return 0xff; -} - -WRITE8_MEMBER(pc9801_state::txt_scrl_w) -{ - //logerror("Write to display register [%02x] %02x\n",offset+0x70,data); - if(offset <= 5) - m_txt_scroll_reg[offset] = data; - - //popmessage("%02x %02x %02x %02x",m_txt_scroll_reg[0],m_txt_scroll_reg[1],m_txt_scroll_reg[2],m_txt_scroll_reg[3]); -} - -READ8_MEMBER(pc9801_state::pc9801_a0_r) -{ - if((offset & 1) == 0) - { - switch(offset & 0xe) - { - case 0x00: - case 0x02: - return m_hgdc2->read(space, (offset & 2) >> 1); - /* TODO: double check these two */ - case 0x04: - return m_vram_disp & 1; - case 0x06: - return m_vram_bank & 1; - /* bitmap palette clut read */ - case 0x08: - case 0x0a: - case 0x0c: - case 0x0e: - return m_pal_clut[(offset & 0x6) >> 1]; - } - - return 0xff; //code unreachable - } - else // odd - { - switch((offset & 0xe) + 1) - { - case 0x09://cg window font read - { - uint32_t pcg_offset; - - pcg_offset = (m_font_addr & 0x7f7f) << 5; - pcg_offset|= m_font_line; - pcg_offset|= m_font_lr; - - return m_kanji_rom[pcg_offset]; - } - } - - logerror("Read to undefined port [%02x]\n",offset+0xa0); - return 0xff; - } -} - -WRITE8_MEMBER(pc9801_state::pc9801_a0_w) -{ - if((offset & 1) == 0) - { - switch(offset & 0xe) - { - case 0x00: - case 0x02: - m_hgdc2->write(space, (offset & 2) >> 1,data); - return; - case 0x04: - m_vram_disp = data & 1; - return; - case 0x06: - m_vram_bank = data & 1; - return; - /* bitmap palette clut write */ - case 0x08: - case 0x0a: - case 0x0c: - case 0x0e: - { - uint8_t pal_entry; - - m_pal_clut[(offset & 0x6) >> 1] = data; - - /* can't be more twisted I presume ... :-/ */ - pal_entry = (((offset & 4) >> 1) | ((offset & 2) << 1)) >> 1; - pal_entry ^= 3; - - m_palette->set_pen_color((pal_entry)|4|8, pal1bit((data & 0x2) >> 1), pal1bit((data & 4) >> 2), pal1bit((data & 1) >> 0)); - m_palette->set_pen_color((pal_entry)|8, pal1bit((data & 0x20) >> 5), pal1bit((data & 0x40) >> 6), pal1bit((data & 0x10) >> 4)); - return; - } - default: - logerror("Write to undefined port [%02x] <- %02x\n",offset+0xa0,data); - return; - } - } - else // odd - { - switch((offset & 0xe) + 1) - { - case 0x01: - m_font_addr = (data & 0xff) | (m_font_addr & 0x7f00); - return; - case 0x03: - m_font_addr = ((data & 0x7f) << 8) | (m_font_addr & 0xff); - return; - case 0x05: - //logerror("%02x\n",data); - m_font_line = ((data & 0x0f) << 1); - m_font_lr = ((data & 0x20) >> 5) ^ 1; - return; - case 0x09: //cg window font write - { - uint32_t pcg_offset; - - pcg_offset = m_font_addr << 5; - pcg_offset|= m_font_line; - pcg_offset|= m_font_lr; - //logerror("%04x %02x %02x %08x\n",m_font_addr,m_font_line,m_font_lr,pcg_offset); - if((m_font_addr & 0xff00) == 0x5600 || (m_font_addr & 0xff00) == 0x5700) - { - m_kanji_rom[pcg_offset] = data; - m_gfxdecode->gfx(2)->mark_dirty(pcg_offset >> 5); - } - return; - } - } - - //logerror("Write to undefined port [%02x) <- %02x\n",offset+0xa0,data); - } -} - DECLARE_WRITE_LINE_MEMBER(pc9801_state::write_uart_clock) { m_sio->write_txc(state); @@ -885,290 +491,6 @@ WRITE8_MEMBER(pc9801_state::fdc_2dd_ctrl_w) m_fdc_2dd->subdevice("1")->get_device()->mon_w(data & 8 ? CLEAR_LINE : ASSERT_LINE); } - -/* TODO: banking? */ -READ16_MEMBER(pc9801_state::tvram_r) -{ - uint16_t res; - - if((offset & 0x1000) && (mem_mask == 0xff00)) - return 0xffff; - - res = m_tvram[offset]; - - return res; -} - -WRITE16_MEMBER(pc9801_state::tvram_w) -{ - if(offset < (0x3fe2>>1) || m_video_ff[MEMSW_REG]) - COMBINE_DATA(&m_tvram[offset]); - - COMBINE_DATA(&m_video_ram_1[offset]); //TODO: check me -} - -/* +0x8000 is trusted (bank 0 is actually used by 16 colors mode) */ -READ8_MEMBER(pc9801_state::gvram_r) -{ - return BITSWAP8(m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000] >> ((offset & 1) << 3),0,1,2,3,4,5,6,7); -} - -WRITE8_MEMBER(pc9801_state::gvram_w) -{ - uint16_t ram = m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000]; - int mask = (offset & 1) << 3; - data = BITSWAP8(data,0,1,2,3,4,5,6,7); - m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000] = (ram & (0xff00 >> mask)) | (data << mask); -} - -uint16_t pc9801_state::egc_shift(int plane, uint16_t val) -{ - int src_off = m_egc.regs[6] & 0xf, dst_off = (m_egc.regs[6] >> 4) & 0xf; - int left = src_off - dst_off, right = dst_off - src_off; - uint16_t out; - if(m_egc.regs[6] & 0x1000) - { - if(right >= 0) - { - out = (val >> right) | m_egc.leftover[plane]; - m_egc.leftover[plane] = val << (16 - right); - } - else - { - out = (val >> (16 - left)) | m_egc.leftover[plane]; - m_egc.leftover[plane] = val << left; - } - } - else - { - if(right >= 0) - { - out = (val << right) | m_egc.leftover[plane]; - m_egc.leftover[plane] = val >> (16 - right); - } - else - { - out = (val << (16 - left)) | m_egc.leftover[plane]; - m_egc.leftover[plane] = val >> left; - } - } - return out; -} - -uint16_t pc9801_state::egc_do_partial_op(int plane, uint16_t src, uint16_t pat, uint16_t dst) const -{ - uint16_t out = 0; - - for(int i = 7; i >= 0; i--) - { - if(BIT(m_egc.regs[2], i)) - out |= src & pat & dst; - pat = ~pat; - dst = (!(i & 1)) ? ~dst : dst; - src = (i == 4) ? ~src : src; - } - return out; -} - -void pc9801_state::egc_blit_w(uint32_t offset, uint16_t data, uint16_t mem_mask) -{ - uint16_t mask = m_egc.regs[4] & mem_mask, out = 0; - bool dir = !(m_egc.regs[6] & 0x1000); - int dst_off = (m_egc.regs[6] >> 4) & 0xf, src_off = m_egc.regs[6] & 0xf; - offset &= 0x13fff; - - if(!m_egc.init && (src_off > dst_off)) - { - if(BIT(m_egc.regs[2], 10)) - { - m_egc.leftover[0] = 0; - egc_shift(0, data); - // leftover[0] is inited above, set others to same - m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = m_egc.leftover[0]; - } - m_egc.init = true; - return; - } - - // mask off the bits before the start - if(m_egc.first) - { - mask &= dir ? ~((1 << dst_off) - 1) : ((1 << (16 - dst_off)) - 1); - if(BIT(m_egc.regs[2], 10) && !m_egc.init) - m_egc.leftover[0] = m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = 0; - } - - // mask off the bits past the end of the blit - if(((m_egc.count < 8) && (mem_mask != 0xffff)) || ((m_egc.count < 16) && (mem_mask == 0xffff))) - { - uint16_t end_mask = dir ? ((1 << m_egc.count) - 1) : ~((1 << (16 - m_egc.count)) - 1); - // if the blit is less than the write size, adjust the masks - if(m_egc.first) - { - if(dir) - end_mask <<= dst_off; - else - end_mask >>= dst_off; - } - mask &= end_mask; - } - - for(int i = 0; i < 4; i++) - { - if(!BIT(m_egc.regs[0], i)) - { - uint16_t src = m_egc.src[i], pat = m_egc.pat[i]; - if(BIT(m_egc.regs[2], 10)) - src = egc_shift(i, data); - - if((m_egc.regs[2] & 0x300) == 0x200) - pat = m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)]; - - switch((m_egc.regs[2] >> 11) & 3) - { - case 0: - out = data; - break; - case 1: - out = egc_do_partial_op(i, src, pat, m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)]); - break; - case 2: - out = pat; - break; - case 3: - logerror("Invalid EGC blit operation\n"); - return; - } - - m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)] &= ~mask; - m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)] |= out & mask; - } - } - if(mem_mask != 0xffff) - { - if(m_egc.first) - m_egc.count -= 8 - (dst_off & 7); - else - m_egc.count -= 8; - } - else - { - if(m_egc.first) - m_egc.count -= 16 - dst_off; - else - m_egc.count -= 16; - } - - m_egc.first = false; - - if(m_egc.count <= 0) - { - m_egc.first = true; - m_egc.init = false; - m_egc.count = (m_egc.regs[7] & 0xfff) + 1; - } -} - -uint16_t pc9801_state::egc_blit_r(uint32_t offset, uint16_t mem_mask) -{ - uint32_t plane_off = offset & 0x13fff; - if((m_egc.regs[2] & 0x300) == 0x100) - { - m_egc.pat[0] = m_video_ram_2[plane_off + 0x4000]; - m_egc.pat[1] = m_video_ram_2[plane_off + (0x4000 * 2)]; - m_egc.pat[2] = m_video_ram_2[plane_off + (0x4000 * 3)]; - m_egc.pat[3] = m_video_ram_2[plane_off]; - } - if(m_egc.first && !m_egc.init) - { - m_egc.leftover[0] = m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = 0; - if(((m_egc.regs[6] >> 4) & 0xf) >= (m_egc.regs[6] & 0xf)) // check if we have enough bits - m_egc.init = true; - } - for(int i = 0; i < 4; i++) - m_egc.src[i] = egc_shift(i, m_video_ram_2[plane_off + (((i + 1) & 3) * 0x4000)]); - - if(BIT(m_egc.regs[2], 13)) - return m_video_ram_2[offset]; - else - return m_egc.src[(m_egc.regs[1] >> 8) & 3]; -} - -READ16_MEMBER(pc9801_state::upd7220_grcg_r) -{ - uint16_t res = 0; - - if(!(m_grcg.mode & 0x80) || machine().side_effect_disabled()) - res = m_video_ram_2[offset]; - else if(m_ex_video_ff[2]) - res = egc_blit_r(offset, mem_mask); - else if(!(m_grcg.mode & 0x40)) - { - int i; - - offset &= 0x13fff; - res = 0; - for(i=0;i<4;i++) - { - if((m_grcg.mode & (1 << i)) == 0) - { - res |= m_video_ram_2[offset | (((i + 1) & 3) * 0x4000)] ^ (m_grcg.tile[i] | m_grcg.tile[i] << 8); - } - } - - res ^= 0xffff; - } - - return res; -} - -WRITE16_MEMBER(pc9801_state::upd7220_grcg_w) -{ - if(!(m_grcg.mode & 0x80)) - COMBINE_DATA(&m_video_ram_2[offset]); - else if(m_ex_video_ff[2]) - egc_blit_w(offset, data, mem_mask); - else - { - int i; - uint8_t *vram = (uint8_t *)m_video_ram_2.target(); - offset = (offset << 1) & 0x27fff; - - if(m_grcg.mode & 0x40) // RMW - { - for(i=0;i<4;i++) - { - if((m_grcg.mode & (1 << i)) == 0) - { - if(mem_mask & 0xff) - { - vram[offset | (((i + 1) & 3) * 0x8000)] &= ~(data >> 0); - vram[offset | (((i + 1) & 3) * 0x8000)] |= m_grcg.tile[i] & (data >> 0); - } - if(mem_mask & 0xff00) - { - vram[offset | (((i + 1) & 3) * 0x8000) | 1] &= ~(data >> 8); - vram[offset | (((i + 1) & 3) * 0x8000) | 1] |= m_grcg.tile[i] & (data >> 8); - } - } - } - } - else // TDW - { - for(i=0;i<4;i++) - { - if((m_grcg.mode & (1 << i)) == 0) - { - if(mem_mask & 0xff) - vram[offset | (((i + 1) & 3) * 0x8000)] = m_grcg.tile[i]; - if(mem_mask & 0xff00) - vram[offset | (((i + 1) & 3) * 0x8000) | 1] = m_grcg.tile[i]; - } - } - } - } -} - READ8_MEMBER(pc9801_state::ide_ctrl_r) { address_space &ram = m_maincpu->space(AS_PROGRAM); diff --git a/src/mame/video/pc9801.cpp b/src/mame/video/pc9801.cpp new file mode 100644 index 00000000000..3e73878e6b6 --- /dev/null +++ b/src/mame/video/pc9801.cpp @@ -0,0 +1,744 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese,Carl +/******************************************************************* + * + * PC98xx video related functions + * + * TODO: + * - further break-down into specific sub-parts/devices for GRCG / EGC; + * + ******************************************************************/ + +#include "emu.h" +#include "includes/pc9801.h" + + +void pc9801_state::video_start() +{ + m_tvram = std::make_unique(0x2000); + + // find memory regions + m_char_rom = memregion("chargen")->base(); + m_kanji_rom = memregion("kanji")->base(); +} + +uint32_t pc9801_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + bitmap.fill(m_palette->black_pen(), cliprect); + + /* graphics */ + m_hgdc2->screen_update(screen, bitmap, cliprect); + m_hgdc1->screen_update(screen, bitmap, cliprect); + return 0; +} + +/************************************************* + * + * UPD7220 (GDC2) bitmap layer + * + ************************************************/ + + +UPD7220_DISPLAY_PIXELS_MEMBER( pc9801_state::hgdc_display_pixels ) +{ + const rgb_t *palette = m_palette->palette()->entry_list_raw(); + int xi; + int res_x,res_y; + uint8_t pen; + uint8_t colors16_mode; + + if(m_video_ff[DISPLAY_REG] == 0) //screen is off + return; + + colors16_mode = (m_ex_video_ff[ANALOG_16_MODE]) ? 16 : 8; + + if(m_ex_video_ff[ANALOG_256_MODE]) + { + uint8_t *ext_gvram = (uint8_t *)m_ext_gvram.target(); + for(xi=0;xi<16;xi++) + { + res_x = x + xi; + res_y = y; + + pen = ext_gvram[(address >> 1)*16+xi+(m_vram_disp*0x20000)]; + + bitmap.pix32(res_y, res_x) = palette[pen + 0x20]; + } + } + else + { + for(xi=0;xi<16;xi++) + { + res_x = x + xi; + res_y = y; + + pen = ((m_video_ram_2[((address & 0x7fff) + (0x08000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 1 : 0; + pen|= ((m_video_ram_2[((address & 0x7fff) + (0x10000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 2 : 0; + pen|= ((m_video_ram_2[((address & 0x7fff) + (0x18000) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 4 : 0; + if(m_ex_video_ff[ANALOG_16_MODE]) + pen|= ((m_video_ram_2[((address & 0x7fff) + (0) + (m_vram_disp*0x20000)) >> 1] >> xi) & 1) ? 8 : 0; + bitmap.pix32(res_y, res_x) = palette[pen + colors16_mode]; + } + } +} + +/************************************************* + * + * UPD7220 (GDC1) text layer + * + ************************************************/ + +UPD7220_DRAW_TEXT_LINE_MEMBER( pc9801_state::hgdc_draw_text ) +{ + const rgb_t *palette = m_palette->palette()->entry_list_raw(); + int xi,yi; + int x; + uint8_t char_size; +// uint8_t interlace_on; + uint16_t tile; + uint8_t kanji_lr; + uint8_t kanji_sel; + uint8_t x_step; + + if(m_video_ff[DISPLAY_REG] == 0) //screen is off + return; + +// interlace_on = m_video_ff[INTERLACE_REG]; + char_size = m_video_ff[FONTSEL_REG] ? 16 : 8; + tile = 0; + + for(x=0;x> 8; + if(knj_tile) + { + /* Note: bit 7 doesn't really count, if a kanji is enabled then the successive tile is always the second part of it. + Trusted with Alice no Yakata, Animahjong V3, Aki no Tsukasa no Fushigi no Kabe, Apros ... + */ + //kanji_lr = (knj_tile & 0x80) >> 7; + //kanji_lr |= (tile & 0x80) >> 7; // Tokimeki Sports Gal 3 + tile &= 0x7f; + tile <<= 8; + tile |= (knj_tile & 0x7f); + kanji_sel = 1; + if((tile & 0x7c00) == 0x0800) // 8x16 charset selector + x_step = 1; + else + x_step = 2; +// kanji_lr = 0; + } + else + x_step = 1; + + + + for(kanji_lr=0;kanji_lr> 5; + + for(yi=0;yivisible_area().contains(res_x, res_y)) + continue; + + tile_data = 0; + + if(!secret) + { + /* TODO: priority */ + if(gfx_mode) + { + int gfx_bit; + tile_data = 0; + + /* + gfx strip mode: + + number refers to the bit number in the tile data. + This mode is identical to the one seen in PC-8801 + 00004444 + 11115555 + 22226666 + 33337777 + */ + + gfx_bit = (xi & 4); + gfx_bit+= (yi & (2 << (char_size == 16 ? 0x01 : 0x00)))>>(1+(char_size == 16)); + gfx_bit+= (yi & (4 << (char_size == 16 ? 0x01 : 0x00)))>>(1+(char_size == 16)); + + tile_data = ((tile >> gfx_bit) & 1) ? 0xff : 0x00; + } + else if(kanji_sel) + tile_data = (m_kanji_rom[tile*0x20+yi*2+kanji_lr]); + else + tile_data = (m_char_rom[tile*char_size+m_video_ff[FONTSEL_REG]*0x800+yi]); + } + + if(reverse) { tile_data^=0xff; } + if(u_line && yi == lr-1) { tile_data = 0xff; } + if(v_line) { tile_data|=8; } + + /* TODO: proper blink rate for these two */ + if(cursor_on && cursor_addr == tile_addr && m_screen->frame_number() & 0x10) + tile_data^=0xff; + + if(blink && m_screen->frame_number() & 0x10) + tile_data^=0xff; + + if(yi >= char_size) + pen = -1; + else + pen = (tile_data >> (7-xi) & 1) ? color : -1; + + if(pen != -1) + bitmap.pix32(res_y, res_x) = palette[pen]; + + if(m_video_ff[WIDTH40_REG]) + { + if(!m_screen->visible_area().contains(res_x+1, res_y)) + continue; + + if(pen != -1) + bitmap.pix32(res_y, res_x+1) = palette[pen]; + } + } + } + } + } +} + +/************************************************* + * + * Flip Flop registers + * + ************************************************/ + +WRITE8_MEMBER(pc9801_state::pc9801_video_ff_w) +{ + /* + TODO: this is my best bet so far. Register 4 is annoying, the pattern seems to be: + Write to video FF register Graphic -> 00 + Write to video FF register 200 lines -> 0x + Write to video FF register 200 lines -> 00 + + where x is the current mode. + */ + switch((data & 0x0e) >> 1) + { + case 1: + m_gfx_ff = 1; + if(data & 1) + logerror("Graphic f/f actually enabled!\n"); + break; + case 4: + if(m_gfx_ff) + { + m_video_ff[(data & 0x0e) >> 1] = data &1; + m_gfx_ff = 0; + } + break; + default: m_video_ff[(data & 0x0e) >> 1] = data & 1; break; + } + + if(0) + { + static const char *const video_ff_regnames[] = + { + "Attribute Select", // 0 + "Graphic", // 1 + "Column", // 2 + "Font Select", // 3 + "200 lines", // 4 + "KAC?", // 5 + "Memory Switch", // 6 + "Display ON" // 7 + }; + + logerror("Write to video FF register %s -> %02x\n",video_ff_regnames[(data & 0x0e) >> 1],data & 1); + } +} + +READ8_MEMBER(pc9801_state::txt_scrl_r) +{ + //logerror("Read to display register [%02x]\n",offset+0x70); + /* TODO: ok? */ + if(offset <= 5) + return m_txt_scroll_reg[offset]; + return 0xff; +} + +WRITE8_MEMBER(pc9801_state::txt_scrl_w) +{ + //logerror("Write to display register [%02x] %02x\n",offset+0x70,data); + if(offset <= 5) + m_txt_scroll_reg[offset] = data; + + //popmessage("%02x %02x %02x %02x",m_txt_scroll_reg[0],m_txt_scroll_reg[1],m_txt_scroll_reg[2],m_txt_scroll_reg[3]); +} + +/************************************************* + * + * Video accessors + * + ************************************************/ + +READ8_MEMBER(pc9801_state::pc9801_a0_r) +{ + if((offset & 1) == 0) + { + switch(offset & 0xe) + { + case 0x00: + case 0x02: + return m_hgdc2->read(space, (offset & 2) >> 1); + /* TODO: double check these two */ + case 0x04: + return m_vram_disp & 1; + case 0x06: + return m_vram_bank & 1; + /* bitmap palette clut read */ + case 0x08: + case 0x0a: + case 0x0c: + case 0x0e: + return m_pal_clut[(offset & 0x6) >> 1]; + } + + return 0xff; //code unreachable + } + else // odd + { + switch((offset & 0xe) + 1) + { + case 0x09://cg window font read + { + uint32_t pcg_offset; + + pcg_offset = (m_font_addr & 0x7f7f) << 5; + pcg_offset|= m_font_line; + pcg_offset|= m_font_lr; + + return m_kanji_rom[pcg_offset]; + } + } + + logerror("Read to undefined port [%02x]\n",offset+0xa0); + return 0xff; + } +} + +WRITE8_MEMBER(pc9801_state::pc9801_a0_w) +{ + if((offset & 1) == 0) + { + switch(offset & 0xe) + { + case 0x00: + case 0x02: + m_hgdc2->write(space, (offset & 2) >> 1,data); + return; + case 0x04: + m_vram_disp = data & 1; + return; + case 0x06: + m_vram_bank = data & 1; + return; + /* bitmap palette clut write */ + case 0x08: + case 0x0a: + case 0x0c: + case 0x0e: + { + uint8_t pal_entry; + + m_pal_clut[(offset & 0x6) >> 1] = data; + + /* can't be more twisted I presume ... :-/ */ + pal_entry = (((offset & 4) >> 1) | ((offset & 2) << 1)) >> 1; + pal_entry ^= 3; + + m_palette->set_pen_color((pal_entry)|4|8, pal1bit((data & 0x2) >> 1), pal1bit((data & 4) >> 2), pal1bit((data & 1) >> 0)); + m_palette->set_pen_color((pal_entry)|8, pal1bit((data & 0x20) >> 5), pal1bit((data & 0x40) >> 6), pal1bit((data & 0x10) >> 4)); + return; + } + default: + logerror("Write to undefined port [%02x] <- %02x\n",offset+0xa0,data); + return; + } + } + else // odd + { + switch((offset & 0xe) + 1) + { + case 0x01: + m_font_addr = (data & 0xff) | (m_font_addr & 0x7f00); + return; + case 0x03: + m_font_addr = ((data & 0x7f) << 8) | (m_font_addr & 0xff); + return; + case 0x05: + //logerror("%02x\n",data); + m_font_line = ((data & 0x0f) << 1); + m_font_lr = ((data & 0x20) >> 5) ^ 1; + return; + case 0x09: //cg window font write + { + uint32_t pcg_offset; + + pcg_offset = m_font_addr << 5; + pcg_offset|= m_font_line; + pcg_offset|= m_font_lr; + //logerror("%04x %02x %02x %08x\n",m_font_addr,m_font_line,m_font_lr,pcg_offset); + if((m_font_addr & 0xff00) == 0x5600 || (m_font_addr & 0xff00) == 0x5700) + { + m_kanji_rom[pcg_offset] = data; + m_gfxdecode->gfx(2)->mark_dirty(pcg_offset >> 5); + } + return; + } + } + + //logerror("Write to undefined port [%02x) <- %02x\n",offset+0xa0,data); + } +} + + +/************************************************* + * + * Text layer accessors + * + ************************************************/ + +/* TODO: banking? */ +READ16_MEMBER(pc9801_state::tvram_r) +{ + uint16_t res; + + if((offset & 0x1000) && (mem_mask == 0xff00)) + return 0xffff; + + res = m_tvram[offset]; + + return res; +} + +WRITE16_MEMBER(pc9801_state::tvram_w) +{ + if(offset < (0x3fe2>>1) || m_video_ff[MEMSW_REG]) + COMBINE_DATA(&m_tvram[offset]); + + COMBINE_DATA(&m_video_ram_1[offset]); //TODO: check me +} + +/************************************************* + * + * Graphic layer accessors + * + ************************************************/ + +/* +0x8000 is trusted (bank 0 is actually used by 16 colors mode) */ +READ8_MEMBER(pc9801_state::gvram_r) +{ + return BITSWAP8(m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000] >> ((offset & 1) << 3),0,1,2,3,4,5,6,7); +} + +WRITE8_MEMBER(pc9801_state::gvram_w) +{ + uint16_t ram = m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000]; + int mask = (offset & 1) << 3; + data = BITSWAP8(data,0,1,2,3,4,5,6,7); + m_video_ram_2[(offset>>1)+0x04000+m_vram_bank*0x10000] = (ram & (0xff00 >> mask)) | (data << mask); +} + +/************************************************* + * + * GRCG (GRaphic CharGer) + * + ************************************************/ + +READ16_MEMBER(pc9801_state::upd7220_grcg_r) +{ + uint16_t res = 0; + + if(!(m_grcg.mode & 0x80) || machine().side_effect_disabled()) + res = m_video_ram_2[offset]; + else if(m_ex_video_ff[2]) + res = egc_blit_r(offset, mem_mask); + else if(!(m_grcg.mode & 0x40)) + { + int i; + + offset &= 0x13fff; + res = 0; + for(i=0;i<4;i++) + { + if((m_grcg.mode & (1 << i)) == 0) + { + res |= m_video_ram_2[offset | (((i + 1) & 3) * 0x4000)] ^ (m_grcg.tile[i] | m_grcg.tile[i] << 8); + } + } + + res ^= 0xffff; + } + + return res; +} + +WRITE16_MEMBER(pc9801_state::upd7220_grcg_w) +{ + if(!(m_grcg.mode & 0x80)) + COMBINE_DATA(&m_video_ram_2[offset]); + else if(m_ex_video_ff[2]) + egc_blit_w(offset, data, mem_mask); + else + { + int i; + uint8_t *vram = (uint8_t *)m_video_ram_2.target(); + offset = (offset << 1) & 0x27fff; + + if(m_grcg.mode & 0x40) // RMW + { + for(i=0;i<4;i++) + { + if((m_grcg.mode & (1 << i)) == 0) + { + if(mem_mask & 0xff) + { + vram[offset | (((i + 1) & 3) * 0x8000)] &= ~(data >> 0); + vram[offset | (((i + 1) & 3) * 0x8000)] |= m_grcg.tile[i] & (data >> 0); + } + if(mem_mask & 0xff00) + { + vram[offset | (((i + 1) & 3) * 0x8000) | 1] &= ~(data >> 8); + vram[offset | (((i + 1) & 3) * 0x8000) | 1] |= m_grcg.tile[i] & (data >> 8); + } + } + } + } + else // TDW + { + for(i=0;i<4;i++) + { + if((m_grcg.mode & (1 << i)) == 0) + { + if(mem_mask & 0xff) + vram[offset | (((i + 1) & 3) * 0x8000)] = m_grcg.tile[i]; + if(mem_mask & 0xff00) + vram[offset | (((i + 1) & 3) * 0x8000) | 1] = m_grcg.tile[i]; + } + } + } + } +} + + +/************************************************* + * + * EGC (Enhanced Graphics Charger) + * + ************************************************/ + +uint16_t pc9801_state::egc_shift(int plane, uint16_t val) +{ + int src_off = m_egc.regs[6] & 0xf, dst_off = (m_egc.regs[6] >> 4) & 0xf; + int left = src_off - dst_off, right = dst_off - src_off; + uint16_t out; + if(m_egc.regs[6] & 0x1000) + { + if(right >= 0) + { + out = (val >> right) | m_egc.leftover[plane]; + m_egc.leftover[plane] = val << (16 - right); + } + else + { + out = (val >> (16 - left)) | m_egc.leftover[plane]; + m_egc.leftover[plane] = val << left; + } + } + else + { + if(right >= 0) + { + out = (val << right) | m_egc.leftover[plane]; + m_egc.leftover[plane] = val >> (16 - right); + } + else + { + out = (val << (16 - left)) | m_egc.leftover[plane]; + m_egc.leftover[plane] = val >> left; + } + } + return out; +} + +uint16_t pc9801_state::egc_do_partial_op(int plane, uint16_t src, uint16_t pat, uint16_t dst) const +{ + uint16_t out = 0; + + for(int i = 7; i >= 0; i--) + { + if(BIT(m_egc.regs[2], i)) + out |= src & pat & dst; + pat = ~pat; + dst = (!(i & 1)) ? ~dst : dst; + src = (i == 4) ? ~src : src; + } + return out; +} + +void pc9801_state::egc_blit_w(uint32_t offset, uint16_t data, uint16_t mem_mask) +{ + uint16_t mask = m_egc.regs[4] & mem_mask, out = 0; + bool dir = !(m_egc.regs[6] & 0x1000); + int dst_off = (m_egc.regs[6] >> 4) & 0xf, src_off = m_egc.regs[6] & 0xf; + offset &= 0x13fff; + + if(!m_egc.init && (src_off > dst_off)) + { + if(BIT(m_egc.regs[2], 10)) + { + m_egc.leftover[0] = 0; + egc_shift(0, data); + // leftover[0] is inited above, set others to same + m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = m_egc.leftover[0]; + } + m_egc.init = true; + return; + } + + // mask off the bits before the start + if(m_egc.first) + { + mask &= dir ? ~((1 << dst_off) - 1) : ((1 << (16 - dst_off)) - 1); + if(BIT(m_egc.regs[2], 10) && !m_egc.init) + m_egc.leftover[0] = m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = 0; + } + + // mask off the bits past the end of the blit + if(((m_egc.count < 8) && (mem_mask != 0xffff)) || ((m_egc.count < 16) && (mem_mask == 0xffff))) + { + uint16_t end_mask = dir ? ((1 << m_egc.count) - 1) : ~((1 << (16 - m_egc.count)) - 1); + // if the blit is less than the write size, adjust the masks + if(m_egc.first) + { + if(dir) + end_mask <<= dst_off; + else + end_mask >>= dst_off; + } + mask &= end_mask; + } + + for(int i = 0; i < 4; i++) + { + if(!BIT(m_egc.regs[0], i)) + { + uint16_t src = m_egc.src[i], pat = m_egc.pat[i]; + if(BIT(m_egc.regs[2], 10)) + src = egc_shift(i, data); + + if((m_egc.regs[2] & 0x300) == 0x200) + pat = m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)]; + + switch((m_egc.regs[2] >> 11) & 3) + { + case 0: + out = data; + break; + case 1: + out = egc_do_partial_op(i, src, pat, m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)]); + break; + case 2: + out = pat; + break; + case 3: + logerror("Invalid EGC blit operation\n"); + return; + } + + m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)] &= ~mask; + m_video_ram_2[offset + (((i + 1) & 3) * 0x4000)] |= out & mask; + } + } + if(mem_mask != 0xffff) + { + if(m_egc.first) + m_egc.count -= 8 - (dst_off & 7); + else + m_egc.count -= 8; + } + else + { + if(m_egc.first) + m_egc.count -= 16 - dst_off; + else + m_egc.count -= 16; + } + + m_egc.first = false; + + if(m_egc.count <= 0) + { + m_egc.first = true; + m_egc.init = false; + m_egc.count = (m_egc.regs[7] & 0xfff) + 1; + } +} + +uint16_t pc9801_state::egc_blit_r(uint32_t offset, uint16_t mem_mask) +{ + uint32_t plane_off = offset & 0x13fff; + if((m_egc.regs[2] & 0x300) == 0x100) + { + m_egc.pat[0] = m_video_ram_2[plane_off + 0x4000]; + m_egc.pat[1] = m_video_ram_2[plane_off + (0x4000 * 2)]; + m_egc.pat[2] = m_video_ram_2[plane_off + (0x4000 * 3)]; + m_egc.pat[3] = m_video_ram_2[plane_off]; + } + if(m_egc.first && !m_egc.init) + { + m_egc.leftover[0] = m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = 0; + if(((m_egc.regs[6] >> 4) & 0xf) >= (m_egc.regs[6] & 0xf)) // check if we have enough bits + m_egc.init = true; + } + for(int i = 0; i < 4; i++) + m_egc.src[i] = egc_shift(i, m_video_ram_2[plane_off + (((i + 1) & 3) * 0x4000)]); + + if(BIT(m_egc.regs[2], 13)) + return m_video_ram_2[offset]; + else + return m_egc.src[(m_egc.regs[1] >> 8) & 3]; +} +