From 1bd8bb35d6c7373771c3c05ecb3878d32187ca54 Mon Sep 17 00:00:00 2001 From: AJR Date: Sat, 27 Jan 2024 12:17:40 -0500 Subject: [PATCH] i8275: Moderate change to API and major expansion of configuration possibilities - Change the "draw character" callback to pack all attribute flags in a single parameter. A bit enum is provided to facilitate unpacking the bits. - Allow for configurations in which up to four CRTCs are attached to the same character clock and screen. The secondary CRTCs are not fully emulated with regard to timing and some output callbacks, but their character and attribute outputs may be merged with those of the primary screen through the same display callback. * wy100: Add half-intensity display for protected characters. * ms6102: Support attribute that enables Cyrillic character set. * t7000: Show cursor and several character attributes. Machines promoted to WORKING ---------------------------- WY-100 [AJR] --- src/devices/video/i8275.cpp | 483 ++++++++++++++++-------------- src/devices/video/i8275.h | 59 +++- src/mame/beehive/microb.cpp | 8 +- src/mame/hp/hp64k.cpp | 9 +- src/mame/intel/imds2ioc.cpp | 7 +- src/mame/intel/ipds.cpp | 9 +- src/mame/misc/dwarfd.cpp | 27 +- src/mame/nokia/mikromik_v.cpp | 17 +- src/mame/regnecentralen/rc702.cpp | 8 +- src/mame/robotron/rt1715.cpp | 11 +- src/mame/sfrj/tim100.cpp | 8 +- src/mame/skeleton/grfd2301.cpp | 6 +- src/mame/skeleton/scopus.cpp | 7 +- src/mame/skeleton/systel1.cpp | 5 +- src/mame/skeleton/unistar.cpp | 9 +- src/mame/skeleton/zorba.cpp | 10 +- src/mame/trs/trs80dt1.cpp | 9 +- src/mame/ussr/apogee.cpp | 10 +- src/mame/ussr/argo.cpp | 9 +- src/mame/ussr/mikrosha.cpp | 9 +- src/mame/ussr/ms6102.cpp | 25 +- src/mame/ussr/partner_m.cpp | 9 +- src/mame/ussr/radio86_m.cpp | 9 +- src/mame/ussr/sm1800.cpp | 9 +- src/mame/ussr/unior.cpp | 9 +- src/mame/wicat/t7000.cpp | 22 +- src/mame/wicat/wicat.cpp | 5 +- src/mame/wyse/wy100.cpp | 23 +- 28 files changed, 485 insertions(+), 346 deletions(-) diff --git a/src/devices/video/i8275.cpp b/src/devices/video/i8275.cpp index b11db64d8df..3da93ce26e6 100644 --- a/src/devices/video/i8275.cpp +++ b/src/devices/video/i8275.cpp @@ -1,10 +1,25 @@ // license:BSD-3-Clause -// copyright-holders:Curt Coder +// copyright-holders:Curt Coder, AJR /********************************************************************** Intel 8275 Programmable CRT Controller Intel 8276 Small Systems CRT Controller + This emulation allows up to four 8275 or 8276 CRTCs connected in + parallel and reading out their row buffers simultaneously. The + secondary CRTCs are assumed to use identical timings to the + primary CRTC and run in perfect sync with each other. + + The features provided by 8276 are practically a subset of the + 8275. Light pen input, line attributes, invisible character + attributes and DMA bursts are not supported on the 8276. Also, + the DRQ and _DACK pins are renamed BRDY (Buffer Ready) and _BS + (Buffer Select), and the latter must be asserted coincident + with _WR (but not _CS). (Even in systems without a DMAC, the + processor usually does not write to the row buffer directly, + but enables some pseudo-DMA mode which causes memory read + operations to transmit data to the CRTC in the same cycle.) + **********************************************************************/ /* @@ -31,46 +46,6 @@ //************************************************************************** -static const int DMA_BURST_SPACING[] = { 0, 7, 15, 23, 31, 39, 47, 55 }; - - -#define DOUBLE_SPACED_ROWS \ - BIT(m_param[REG_SCN1], 7) - -#define CHARACTERS_PER_ROW \ - ((m_param[REG_SCN1] & 0x7f) + 1) - -#define VRTC_ROW_COUNT \ - ((m_param[REG_SCN2] >> 6) + 1) - -#define CHARACTER_ROWS_PER_FRAME \ - ((m_param[REG_SCN2] & 0x3f) + 1) - -#define UNDERLINE \ - (m_param[REG_SCN3] >> 4) - -#define SCANLINES_PER_ROW \ - ((m_param[REG_SCN3] & 0x0f) + 1) - -#define OFFSET_LINE_COUNTER \ - BIT(m_param[REG_SCN4], 7) - -#define VISIBLE_FIELD_ATTRIBUTE \ - BIT(m_param[REG_SCN4], 6) - -#define CURSOR_FORMAT \ - ((m_param[REG_SCN4] >> 4) & 0x03) - -#define HRTC_COUNT \ - (((m_param[REG_SCN4] & 0x0f) + 1) * 2) - -#define DMA_BURST_COUNT \ - (1 << (m_param[REG_DMA] & 0x03)) - -#define DMA_BURST_SPACE \ - DMA_BURST_SPACING[(m_param[REG_DMA] >> 2) & 0x07] - - const int i8275_device::character_attribute[3][16] = { { 2, 2, 4, 4, 2, 4, 4, 4, 2, 4, 4, 0, 2, 0, 0, 0 }, @@ -108,11 +83,14 @@ i8275_device::i8275_device(const machine_config &mconfig, device_type type, cons m_write_lc(*this), m_display_cb(*this), m_refresh_hack(false), + m_next_crtc(*this, finder_base::DUMMY_TAG), + m_is_crtc0(true), m_status(0), m_param_idx(0), m_param_end(0), m_buffer_idx(0), m_fifo_idx(0), + m_fifo_idx_out(0), m_dma_idx(0), m_dma_last_char(0), m_buffer_dma(0), @@ -140,22 +118,32 @@ i8276_device::i8276_device(const machine_config &mconfig, const char *tag, devic } +void i8275_device::device_resolve_objects() +{ + if (m_next_crtc.found()) + m_next_crtc->m_is_crtc0 = false; +} + + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void i8275_device::device_start() { - // get the screen device - screen().register_screen_bitmap(m_bitmap); + if (m_is_crtc0) + { + // get the screen device + screen().register_screen_bitmap(m_bitmap); - // resolve delegates - m_display_cb.resolve_safe(); + // resolve delegates + m_display_cb.resolve_safe(); - // allocate timers - m_hrtc_on_timer = timer_alloc(FUNC(i8275_device::hrtc_on), this); - m_drq_on_timer = timer_alloc(FUNC(i8275_device::drq_on), this); - m_scanline_timer = timer_alloc(FUNC(i8275_device::scanline_tick), this); + // allocate timers + m_hrtc_on_timer = timer_alloc(FUNC(i8275_device::hrtc_on), this); + m_drq_on_timer = timer_alloc(FUNC(i8275_device::drq_on), this); + m_scanline_timer = timer_alloc(FUNC(i8275_device::scanline_tick), this); + } // state saving save_item(NAME(m_status)); @@ -164,6 +152,8 @@ void i8275_device::device_start() save_item(NAME(m_param_end)); save_item(NAME(m_buffer[0])); save_item(NAME(m_buffer[1])); + save_item(NAME(m_fifo[0])); + save_item(NAME(m_fifo[1])); save_item(NAME(m_buffer_idx)); save_item(NAME(m_fifo_idx)); save_item(NAME(m_dma_idx)); @@ -205,16 +195,13 @@ void i8275_device::device_reset() void i8275_device::vrtc_start() { - //LOG("I8275 y %u x %u VRTC 1\n", y, x); - m_write_vrtc(1); - // reset field attributes m_field_attr = 0; // Intel datasheets imply DMA requests begin only after a "Start Display" command is issued. // WY-100, however, expects a BRDY cycle from the 8276 after the program first configures and stops the display. // This suggests that DMA bursts proceed as normal in this case, but any characters sent will not be displayed. - m_buffer_idx = CHARACTERS_PER_ROW; + m_buffer_idx = characters_per_row(); m_dma_stop = false; m_end_of_screen = !(m_status & ST_VE); @@ -250,7 +237,8 @@ void i8275_device::dma_start() m_dma_idx = 0; m_dma_last_char = 0; - m_drq_on_timer->adjust(clocks_to_attotime(DMA_BURST_SPACE)); + if (m_is_crtc0) + m_drq_on_timer->adjust(clocks_to_attotime(dma_burst_space())); } @@ -270,10 +258,10 @@ TIMER_CALLBACK_MEMBER(i8275_device::drq_on) TIMER_CALLBACK_MEMBER(i8275_device::scanline_tick) { - int rc = m_scanline / SCANLINES_PER_ROW; - int lc = m_scanline % SCANLINES_PER_ROW; + int rc = m_scanline / scanlines_per_row(); + int lc = m_scanline % scanlines_per_row(); - int line_counter = OFFSET_LINE_COUNTER ? ((lc - 1) % SCANLINES_PER_ROW) : lc; + int line_counter = offset_line_counter() ? ((lc - 1) % scanlines_per_row()) : lc; m_write_lc(line_counter); m_write_hrtc(0); @@ -282,170 +270,214 @@ TIMER_CALLBACK_MEMBER(i8275_device::scanline_tick) if (lc == 0 && m_scanline < m_vrtc_scanline) { - if (!m_dma_stop && m_buffer_idx < CHARACTERS_PER_ROW) + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc) { - m_status |= ST_DU; - m_dma_stop = true; + if (!crtc->m_dma_stop && crtc->m_buffer_idx < characters_per_row()) + { + crtc->m_status |= ST_DU; + crtc->m_dma_stop = true; - // blank screen until after VRTC - m_end_of_screen = true; + // blank screen until after VRTC + crtc->m_end_of_screen = true; - m_write_drq(0); - } + crtc->m_write_drq(0); + } - if (!m_dma_stop) - { - // swap line buffers - m_buffer_dma = !m_buffer_dma; + if (!crtc->m_dma_stop) + { + // swap line buffers + crtc->m_buffer_dma = !crtc->m_buffer_dma; - if (m_scanline < (m_vrtc_scanline - SCANLINES_PER_ROW)) - dma_start(); + if (m_scanline < (m_vrtc_scanline - scanlines_per_row())) + crtc->dma_start(); + } } } - if ((m_status & ST_IE) && !(m_status & ST_IR) && m_scanline == m_irq_scanline) + if (m_scanline == m_irq_scanline) { - m_status |= ST_IR; - m_write_irq(ASSERT_LINE); + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc) + { + if ((crtc->m_status & ST_IE) && !(crtc->m_status & ST_IR)) + { + crtc->m_status |= ST_IR; + crtc->m_write_irq(ASSERT_LINE); + } + } } if (m_scanline == m_vrtc_scanline) - vrtc_start(); - - if (!m_dma_stop && m_scanline == m_vrtc_drq_scanline) { - // swap line buffers - m_buffer_dma = !m_buffer_dma; + //LOG("I8275 y %u x %u VRTC 1\n", y, x); + m_write_vrtc(1); - // start DMA burst - dma_start(); + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc) + crtc->vrtc_start(); } - if ((m_status & ST_VE) && m_scanline < m_vrtc_scanline) + if (m_scanline == m_vrtc_drq_scanline) { - bool end_of_row = false; - bool blank_row = (UNDERLINE >= 8) && ((lc == 0) || (lc == SCANLINES_PER_ROW - 1)); - int fifo_idx = 0; - m_field_attr = m_stored_attr; - - for (int sx = 0; sx < CHARACTERS_PER_ROW; sx++) + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc) { - int lineattr = 0; - - uint8_t data = (end_of_row || m_end_of_screen) ? 0 : m_buffer[!m_buffer_dma][sx]; - uint8_t attr = m_field_attr; - - if ((data & 0xc0) == 0x80) + if (!crtc->m_dma_stop) { - // field attribute code - m_field_attr = data & (FAC_H | FAC_B | FAC_GG | FAC_R | FAC_U); + // swap line buffers + crtc->m_buffer_dma = !crtc->m_buffer_dma; - if (!VISIBLE_FIELD_ATTRIBUTE) - { - attr = m_field_attr; - data = m_fifo[!m_buffer_dma][fifo_idx]; - - fifo_idx++; - fifo_idx &= 0xf; - - if (blank_row) - attr |= FAC_B; - else if (!(m_char_blink < 32)) - attr &= ~FAC_B; - if (lc != UNDERLINE) - attr &= ~FAC_U; - } - else - { - // simply blank the attribute character itself - attr = FAC_B; - } + // start DMA burst + crtc->dma_start(); } - else if (data >= 0xf0 || end_of_row || m_end_of_screen) - { - // special control character - switch (data) - { - case SCC_END_OF_ROW: - case SCC_END_OF_ROW_DMA: - end_of_row = true; - break; + } + } - case SCC_END_OF_SCREEN: - case SCC_END_OF_SCREEN_DMA: - m_end_of_screen = true; - break; - } - attr = FAC_B; + if (m_scanline < m_vrtc_scanline) + { + int end_of_row = 0; + int blank_row = 0; + { + int n = 0; + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc, n++) + { + if (crtc->m_end_of_screen || !(m_status & ST_VE)) + end_of_row |= 1 << n; + if ((crtc->underline() >= 8) && ((lc == 0) || (lc == scanlines_per_row() - 1))) + blank_row |= 1 << n; + crtc->m_fifo_idx_out = 0; + crtc->m_field_attr = crtc->m_stored_attr; } - else if (data >= 0xc0) + } + + for (int sx = 0; sx < characters_per_row(); sx++) + { + int n = 0; + uint32_t charcode = 0; + uint32_t attrcode = 0; + + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc, n++) { - // character attribute code - attr = data & (m_char_blink < 32 ? (CA_H | CA_B) : CA_H); - - uint8_t ca; - int cccc = (data >> 2) & 0x0f; - - if (lc < UNDERLINE) - { - ca = character_attribute[0][cccc]; - } - else if (lc == UNDERLINE) - { - ca = character_attribute[1][cccc]; - } - else - { - ca = character_attribute[2][cccc]; - } - - if (ca & CA_LTEN) - attr |= FAC_U; - if (ca & CA_VSP) - attr |= FAC_B; - lineattr = ca >> 2; - } - else - { - if (blank_row) - attr |= FAC_B; - else if (!(m_char_blink < 32)) - attr &= ~FAC_B; - if (lc != UNDERLINE) - attr &= ~FAC_U; - } - - if ((rc == m_param[REG_CUR_ROW]) && (sx == m_param[REG_CUR_COL])) - { - if ((CURSOR_FORMAT & 0x02) || (m_cursor_blink < 16)) - { - if (CURSOR_FORMAT & 0x01) - attr |= (lc == UNDERLINE) ? FAC_U : 0; - else - attr ^= FAC_R; - } + auto [data, attr] = crtc->char_from_buffer(n, sx, rc, lc, end_of_row, blank_row); + charcode |= uint32_t(data) << (n * 8); + attrcode |= uint32_t(attr) << (n * 8); } m_display_cb(m_bitmap, sx * m_hpixels_per_column, // x position on screen of starting point m_scanline, // y position on screen line_counter, // current line of char - (data & 0x7f), // char code to be displayed - lineattr, // line attribute code - (attr & FAC_U) ? 1 : 0, // light enable signal - (attr & FAC_R) ? 1 : 0, // reverse video signal - (attr & FAC_B) ? 1 : 0, // video suppression - (attr & FAC_GG) >> 2, // general purpose attribute code - (attr & FAC_H) ? 1 : 0 // highlight - ); + charcode, // char code to be displayed + attrcode); } - if ((SCANLINES_PER_ROW - lc) == 1) - m_stored_attr = m_field_attr; + if ((scanlines_per_row() - lc) == 1) + { + for (i8275_device *crtc = this; crtc != nullptr; crtc = crtc->m_next_crtc) + crtc->m_stored_attr = crtc->m_field_attr; + } } m_scanline++; - m_scanline %= ((CHARACTER_ROWS_PER_FRAME + VRTC_ROW_COUNT) * SCANLINES_PER_ROW); + m_scanline %= ((character_rows_per_frame() + vrtc_row_count()) * scanlines_per_row()); +} + + +std::pair i8275_device::char_from_buffer(int n, int sx, int rc, int lc, int &end_of_row, int blank_row) +{ + uint8_t data = BIT(end_of_row, n) ? 0 : m_buffer[!m_buffer_dma][sx]; + uint8_t attr = m_field_attr; + int lineattr = 0; + + if ((data & 0xc0) == 0x80) + { + // field attribute code + m_field_attr = data & (FAC_H | FAC_B | FAC_GG | FAC_R | FAC_U); + + if (!visible_field_attribute()) + { + attr = m_field_attr; + data = m_fifo[!m_buffer_dma][m_fifo_idx_out]; + + m_fifo_idx_out++; + m_fifo_idx_out &= 0xf; + + if (BIT(blank_row, n)) + attr |= FAC_B; + else if (!(m_char_blink < 32)) + attr &= ~FAC_B; + if (lc != underline()) + attr &= ~FAC_U; + } + else + { + // simply blank the attribute character itself + attr = FAC_B; + } + } + else if (data >= 0xf0 || BIT(end_of_row, n)) + { + // special control character + switch (data) + { + case SCC_END_OF_ROW: + case SCC_END_OF_ROW_DMA: + end_of_row |= 1 << n; + break; + + case SCC_END_OF_SCREEN: + case SCC_END_OF_SCREEN_DMA: + m_end_of_screen = true; + break; + } + attr = FAC_B; + } + else if (data >= 0xc0) + { + // character attribute code + attr = data & (m_char_blink < 32 ? (CA_H | CA_B) : CA_H); + + uint8_t ca; + int cccc = (data >> 2) & 0x0f; + + if (lc < underline()) + { + ca = character_attribute[0][cccc]; + } + else if (lc == underline()) + { + ca = character_attribute[1][cccc]; + } + else + { + ca = character_attribute[2][cccc]; + } + + if (ca & CA_LTEN) + attr |= FAC_U; + if (ca & CA_VSP) + attr |= FAC_B; + lineattr = ca >> 2; + } + else + { + if (BIT(blank_row, n)) + attr |= FAC_B; + else if (!(m_char_blink < 32)) + attr &= ~FAC_B; + if (lc != underline()) + attr &= ~FAC_U; + } + + if ((rc == m_param[REG_CUR_ROW]) && (sx == m_param[REG_CUR_COL])) + { + if ((cursor_format() & 0x02) || (m_cursor_blink < 16)) + { + if (cursor_format() & 0x01) + attr |= (lc == underline()) ? FAC_U : 0; + else + attr ^= FAC_R; + } + } + + return std::make_pair(data & 0x7f, attr | lineattr << 6); } @@ -508,12 +540,15 @@ void i8275_device::write(offs_t offset, uint8_t data) */ if (m_preset) { - int hrtc_on_pos = CHARACTERS_PER_ROW * m_hpixels_per_column; + int hrtc_on_pos = characters_per_row() * m_hpixels_per_column; m_preset = false; - m_hrtc_on_timer->adjust(screen().time_until_pos(screen().vpos(), hrtc_on_pos), 0, screen().scan_period()); m_scanline = m_vrtc_drq_scanline; - m_scanline_timer->adjust(screen().time_until_pos(m_vrtc_drq_scanline, 0), 0, screen().scan_period()); + if (m_is_crtc0) + { + m_hrtc_on_timer->adjust(screen().time_until_pos(screen().vpos(), hrtc_on_pos), 0, screen().scan_period()); + m_scanline_timer->adjust(screen().time_until_pos(m_vrtc_drq_scanline, 0), 0, screen().scan_period()); + } } switch (data >> 5) @@ -532,7 +567,8 @@ void i8275_device::write(offs_t offset, uint8_t data) LOG("I8275 IRQ 0\n"); m_write_irq(CLEAR_LINE); m_write_drq(0); - m_drq_on_timer->adjust(attotime::never); + if (m_is_crtc0) + m_drq_on_timer->adjust(attotime::never); m_dma_stop = true; m_param_idx = REG_SCN1; @@ -546,7 +582,7 @@ void i8275_device::write(offs_t offset, uint8_t data) */ case CMD_START_DISPLAY: m_param[REG_DMA] = data; - LOG("I8275 Start Display %u %u\n", DMA_BURST_COUNT, DMA_BURST_SPACE); + LOG("I8275 Start Display %u %u\n", dma_burst_count(), dma_burst_space()); m_dma_stop = false; m_status |= (ST_IE | ST_VE); break; @@ -585,8 +621,11 @@ void i8275_device::write(offs_t offset, uint8_t data) case CMD_PRESET_COUNTERS: LOG("I8275 Preset Counters\n"); m_preset = true; - m_scanline_timer->adjust(attotime::never); - m_hrtc_on_timer->adjust(attotime::never); + if (m_is_crtc0) + { + m_scanline_timer->adjust(attotime::never); + m_hrtc_on_timer->adjust(attotime::never); + } break; } } @@ -614,7 +653,7 @@ void i8275_device::dack_w(uint8_t data) m_write_drq(0); - if (!VISIBLE_FIELD_ATTRIBUTE && ((m_dma_last_char & 0xc0) == 0x80)) + if (!visible_field_attribute() && ((m_dma_last_char & 0xc0) == 0x80)) { if (m_fifo_idx == 16) { @@ -627,7 +666,7 @@ void i8275_device::dack_w(uint8_t data) data = 0; } - else if (m_buffer_idx < CHARACTERS_PER_ROW) + else if (m_buffer_idx < characters_per_row()) { m_buffer[m_buffer_dma][m_buffer_idx++] = data; } @@ -638,27 +677,30 @@ void i8275_device::dack_w(uint8_t data) { case SCC_END_OF_ROW_DMA: // stop DMA - m_buffer_idx = CHARACTERS_PER_ROW; + m_buffer_idx = characters_per_row(); break; case SCC_END_OF_SCREEN_DMA: m_dma_stop = true; - m_buffer_idx = CHARACTERS_PER_ROW; + m_buffer_idx = characters_per_row(); break; default: - if (m_buffer_idx == CHARACTERS_PER_ROW) + if (m_is_crtc0) { - // stop DMA - m_drq_on_timer->adjust(attotime::never); - } - else if (!(m_dma_idx % DMA_BURST_COUNT)) - { - m_drq_on_timer->adjust(clocks_to_attotime(DMA_BURST_SPACE)); - } - else - { - m_drq_on_timer->adjust(attotime::zero); + if (m_buffer_idx == characters_per_row()) + { + // stop DMA + m_drq_on_timer->adjust(attotime::never); + } + else if (!(m_dma_idx % dma_burst_count())) + { + m_drq_on_timer->adjust(clocks_to_attotime(dma_burst_space())); + } + else + { + m_drq_on_timer->adjust(attotime::zero); + } } } @@ -675,7 +717,7 @@ void i8275_device::lpen_w(int state) if (!m_lpen && state) { m_param[REG_LPEN_COL] = screen().hpos() / m_hpixels_per_column; - m_param[REG_LPEN_ROW] = screen().vpos() / SCANLINES_PER_ROW; + m_param[REG_LPEN_ROW] = screen().vpos() / scanlines_per_row(); m_status |= ST_LP; } @@ -707,30 +749,33 @@ uint32_t i8275_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap void i8275_device::recompute_parameters() { + if (!m_is_crtc0) + return; + int y = screen().vpos(); - int horiz_pix_total = (CHARACTERS_PER_ROW + HRTC_COUNT) * m_hpixels_per_column; - int vert_pix_total = (CHARACTER_ROWS_PER_FRAME + VRTC_ROW_COUNT) * SCANLINES_PER_ROW; - attotime refresh = clocks_to_attotime((CHARACTERS_PER_ROW + HRTC_COUNT) * vert_pix_total); - int max_visible_x = (CHARACTERS_PER_ROW * m_hpixels_per_column) - 1; - int max_visible_y = (CHARACTER_ROWS_PER_FRAME * SCANLINES_PER_ROW) - 1; + int horiz_pix_total = (characters_per_row() + hrtc_count()) * m_hpixels_per_column; + int vert_pix_total = (character_rows_per_frame() + vrtc_row_count()) * scanlines_per_row(); + attotime refresh = clocks_to_attotime((characters_per_row() + hrtc_count()) * vert_pix_total); + int max_visible_x = (characters_per_row() * m_hpixels_per_column) - 1; + int max_visible_y = (character_rows_per_frame() * scanlines_per_row()) - 1; LOG("width %u height %u max_x %u max_y %u refresh %f\n", horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, refresh.as_hz()); rectangle visarea(0, max_visible_x, 0, max_visible_y); screen().configure(horiz_pix_total, vert_pix_total, visarea, (m_refresh_hack ? screen().frame_period() : refresh).as_attoseconds()); - int hrtc_on_pos = CHARACTERS_PER_ROW * m_hpixels_per_column; + int hrtc_on_pos = characters_per_row() * m_hpixels_per_column; m_hrtc_on_timer->adjust(screen().time_until_pos(y, hrtc_on_pos), 0, screen().scan_period()); - m_irq_scanline = (CHARACTER_ROWS_PER_FRAME - 1) * SCANLINES_PER_ROW; - m_vrtc_scanline = CHARACTER_ROWS_PER_FRAME * SCANLINES_PER_ROW; - m_vrtc_drq_scanline = vert_pix_total - SCANLINES_PER_ROW; + m_irq_scanline = (character_rows_per_frame() - 1) * scanlines_per_row(); + m_vrtc_scanline = character_rows_per_frame() * scanlines_per_row(); + m_vrtc_drq_scanline = vert_pix_total - scanlines_per_row(); LOG("irq_y %u vrtc_y %u drq_y %u\n", m_irq_scanline, m_vrtc_scanline, m_vrtc_drq_scanline); m_scanline = y; m_scanline_timer->adjust(screen().time_until_pos((y + 1) % vert_pix_total, 0), 0, screen().scan_period()); - if (DOUBLE_SPACED_ROWS) fatalerror("Double spaced rows not supported!"); + if (double_spaced_rows()) fatalerror("Double spaced rows not supported!"); } diff --git a/src/devices/video/i8275.h b/src/devices/video/i8275.h index 6853d92a678..8948ac4d44a 100644 --- a/src/devices/video/i8275.h +++ b/src/devices/video/i8275.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Curt Coder +// copyright-holders:Curt Coder, AJR /********************************************************************** Intel 8275/8276 CRT Controller emulation @@ -41,7 +41,29 @@ // INTERFACE CONFIGURATION MACROS //************************************************************************** -#define I8275_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int x, int y, uint8_t linecount, uint8_t charcode, uint8_t lineattr, uint8_t lten, uint8_t rvv, uint8_t vsp, uint8_t gpa, uint8_t hlgt) +#define I8275_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int x, int y, uint8_t linecount, uint32_t charcode, uint32_t attrcode) + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +namespace i8275_attributes +{ + // Each 8275 outputs eight attribute signals in parallel for each character. + // Except for LA1 and LA0, the bit assignments provided here match the encoding of character attributes. + enum + { + LA1 = 7, // line attribute code 1 + LA0 = 6, // line attribute code 0 + LTEN = 5, // light enable signal + RVV = 4, // reverse video signal + GPA1 = 3, // general purpose attribute code 1 + GPA0 = 2, // general purpose attribute code 0 + VSP = 1, // video suppression + HLGT = 0 // highlight + }; +}; //************************************************************************** @@ -55,7 +77,7 @@ class i8275_device : public device_t, public device_video_interface { public: - typedef device_delegate draw_character_delegate; + typedef device_delegate draw_character_delegate; // construction/destruction i8275_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); @@ -63,6 +85,7 @@ public: void set_character_width(int value) { m_hpixels_per_column = value; } void set_refresh_hack(bool hack) { m_refresh_hack = hack; } template void set_display_callback(T &&... args) { m_display_cb.set(std::forward(args)...); } + template void set_next_crtc(T &&tag) { m_next_crtc.set_tag(std::forward(tag)); } auto drq_wr_callback() { return m_write_drq.bind(); } auto irq_wr_callback() { return m_write_irq.bind(); } @@ -83,6 +106,7 @@ protected: i8275_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); // device-level overrides + virtual void device_resolve_objects() override; virtual void device_start() override; virtual void device_reset() override; @@ -94,9 +118,24 @@ protected: void vrtc_end(); void dma_start(); + std::pair char_from_buffer(int n, int sx, int rc, int lc, int &end_of_row, int blank_row); + void recompute_parameters(); - enum + bool double_spaced_rows() const { return BIT(m_param[REG_SCN1], 7); } + uint8_t characters_per_row() const { return (m_param[REG_SCN1] & 0x7f) + 1; } + uint8_t vrtc_row_count() const { return (m_param[REG_SCN2] >> 6) + 1; } + uint8_t character_rows_per_frame() const { return (m_param[REG_SCN2] & 0x3f) + 1; } + uint8_t underline() const { return m_param[REG_SCN3] >> 4; } + uint8_t scanlines_per_row() const { return (m_param[REG_SCN3] & 0x0f) + 1; } + bool offset_line_counter() const { return BIT(m_param[REG_SCN4], 7); } + bool visible_field_attribute() const { return BIT(m_param[REG_SCN4], 6); } + uint8_t cursor_format() const { return (m_param[REG_SCN4] >> 4) & 0x03; } + uint8_t hrtc_count() const { return ((m_param[REG_SCN4] & 0x0f) + 1) * 2; } + uint8_t dma_burst_count() const { return 1 << (m_param[REG_DMA] & 0x03); } + uint8_t dma_burst_space() const { uint8_t sp = (m_param[REG_DMA] >> 2) & 0x07; return sp ? sp * 8 - 1 : 0; } + + enum : uint8_t { ST_IE = 0x40, ST_IR = 0x20, @@ -132,7 +171,7 @@ protected: REG_DMA }; - enum + enum : uint8_t { CA_H = 0x01, CA_B = 0x02, @@ -143,7 +182,7 @@ protected: CA_LA1 = 0x08 }; - enum + enum : uint8_t { SCC_END_OF_ROW = 0xf0, SCC_END_OF_ROW_DMA = 0xf1, @@ -151,7 +190,7 @@ protected: SCC_END_OF_SCREEN_DMA = 0xf3 }; - enum + enum : uint8_t { FAC_H = 0x01, FAC_B = 0x02, @@ -172,6 +211,9 @@ protected: int m_hpixels_per_column; bool m_refresh_hack; + optional_device m_next_crtc; + bool m_is_crtc0; + bitmap_rgb32 m_bitmap; uint8_t m_status; @@ -182,7 +224,8 @@ protected: uint8_t m_buffer[2][80]; uint8_t m_fifo[2][16]; int m_buffer_idx; - int m_fifo_idx; + uint8_t m_fifo_idx; + uint8_t m_fifo_idx_out; int m_dma_idx; uint8_t m_dma_last_char; int m_buffer_dma; diff --git a/src/mame/beehive/microb.cpp b/src/mame/beehive/microb.cpp index e21c807c87e..b01e0ae1c3a 100644 --- a/src/mame/beehive/microb.cpp +++ b/src/mame/beehive/microb.cpp @@ -280,12 +280,14 @@ void microb_state::machine_start() I8275_DRAW_CHARACTER_MEMBER(microb_state::draw_character) { - u8 dots = lten ? 0xff : (vsp || linecount == 9) ? 0 : m_p_chargen[(charcode << 4) | linecount]; - if (rvv) + using namespace i8275_attributes; + + u8 dots = BIT(attrcode, LTEN) ? 0xff : (BIT(attrcode, VSP) || linecount == 9) ? 0 : m_p_chargen[(charcode << 4) | linecount]; + if (BIT(attrcode, RVV)) dots ^= 0xff; // HLGT is active on status line - rgb_t const fg = hlgt ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white(); + rgb_t const fg = BIT(attrcode, HLGT) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white(); u32 *pix = &bitmap.pix(y, x); for (int i = 0; i < 8; i++) diff --git a/src/mame/hp/hp64k.cpp b/src/mame/hp/hp64k.cpp index afcc96e3e77..591e8cfbe38 100644 --- a/src/mame/hp/hp64k.cpp +++ b/src/mame/hp/hp64k.cpp @@ -477,16 +477,17 @@ I8275_DRAW_CHARACTER_MEMBER(hp64k_state::crtc_display_pixels) uint8_t chargen_byte = m_chargen[ linecount | ((unsigned)charcode << 4) ]; uint16_t pixels_lvid , pixels_livid; - if (vsp) { + using namespace i8275_attributes; + if (BIT(attrcode , VSP)) { pixels_lvid = pixels_livid = ~0; - } else if (lten) { + } else if (BIT(attrcode , LTEN)) { pixels_livid = ~0; - if (rvv) { + if (BIT(attrcode , RVV)) { pixels_lvid = ~0; } else { pixels_lvid = 0; } - } else if (rvv) { + } else if (BIT(attrcode , RVV)) { pixels_lvid = ~0; pixels_livid = (uint16_t)chargen_byte << 1; } else { diff --git a/src/mame/intel/imds2ioc.cpp b/src/mame/intel/imds2ioc.cpp index 44d6f2acedb..e7d52b646fd 100644 --- a/src/mame/intel/imds2ioc.cpp +++ b/src/mame/intel/imds2ioc.cpp @@ -337,9 +337,10 @@ I8275_DRAW_CHARACTER_MEMBER(imds2ioc_device::crtc_display_pixels) uint8_t const chargen_byte = m_chargen[ (linecount & 7) | ((unsigned)charcode << 3) ]; uint16_t pixels; - if (lten) { + using namespace i8275_attributes; + if (BIT(attrcode, LTEN)) { pixels = ~0; - } else if (vsp != 0 || (linecount & 8) != 0) { + } else if (BIT(attrcode, VSP) || (linecount & 8) != 0) { pixels = 0; // VSP is gated with LC3 } else { // See hardware ref. manual, pg 58 for the very peculiar way of generating character images @@ -373,7 +374,7 @@ I8275_DRAW_CHARACTER_MEMBER(imds2ioc_device::crtc_display_pixels) pixels = exp_pix_l | exp_pix_r; } - if (rvv) { + if (BIT(attrcode, RVV)) { pixels = ~pixels; } diff --git a/src/mame/intel/ipds.cpp b/src/mame/intel/ipds.cpp index c0b08c1d196..0bbb4295784 100644 --- a/src/mame/intel/ipds.cpp +++ b/src/mame/intel/ipds.cpp @@ -98,15 +98,18 @@ I8275_DRAW_CHARACTER_MEMBER( ipds_state::crtc_display_pixels ) uint8_t *charmap = memregion("chargen")->base(); uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + using namespace i8275_attributes; + + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(int i=0;i<6;i++) bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/misc/dwarfd.cpp b/src/mame/misc/dwarfd.cpp index febdc71ad92..a8a399f8c0d 100644 --- a/src/mame/misc/dwarfd.cpp +++ b/src/mame/misc/dwarfd.cpp @@ -609,19 +609,18 @@ INPUT_PORTS_END I8275_DRAW_CHARACTER_MEMBER(dwarfd_state::pesp_display_pixels) { - int bank = ((gpa & 2) ? 0 : 2) + (gpa & 1); + using namespace i8275_attributes; + int bank = (BIT(attrcode, GPA1) ? 0 : 2) + (BIT(attrcode, GPA0) ? 1 : 0); + int palbank = (BIT(attrcode, RVV) ? 2 : 0) + (BIT(attrcode, VSP) ? 1 : 0); const rgb_t *palette = m_palette->palette()->entry_list_raw(); uint16_t pixels = m_charmap[(linecount & 7) + ((charcode + (bank * 128)) << 3)]; if(!x) m_back_color = false; - //if(!linecount) - // logerror("%d %d %02x %02x %02x %02x %02x %02x %02x\n", x/8, y/8, charcode, lineattr, lten, rvv, vsp, gpa, hlgt); - for (int i = 0; i < 8; i += 2) { uint8_t pixel = (pixels >> (i * 2)) & 0xf; - uint8_t value = (pixel >> 1) | (rvv << 4) | (vsp << 3); + uint8_t value = (pixel >> 1) | (palbank << 3); bitmap.pix(y, x + i) = palette[value]; bitmap.pix(y, x + i + 1) = palette[(pixel & 1) ? 0 : value]; if(m_back_color) @@ -632,19 +631,18 @@ I8275_DRAW_CHARACTER_MEMBER(dwarfd_state::pesp_display_pixels) I8275_DRAW_CHARACTER_MEMBER(dwarfd_state::display_pixels) { - int bank = ((gpa & 2) ? 0 : 4) + (gpa & 1) + (m_dsw2->read() & 2); + using namespace i8275_attributes; + int bank = (BIT(attrcode, GPA1) ? 0 : 4) + (BIT(attrcode, GPA0) ? 1 : 0) + (m_dsw2->read() & 2); + int palbank = (BIT(attrcode, RVV) ? 2 : 0) + (BIT(attrcode, VSP) ? 1 : 0); const rgb_t *palette = m_palette->palette()->entry_list_raw(); uint16_t pixels = m_charmap[(linecount & 7) + ((charcode + (bank * 128)) << 3)]; if(!x) m_back_color = false; - //if(!linecount) - // logerror("%d %d %02x %02x %02x %02x %02x %02x %02x\n", x/8, y/8, charcode, lineattr, lten, rvv, vsp, gpa, hlgt); - for (int i = 0; i < 8; i += 2) { uint8_t pixel = (pixels >> (i * 2)) & 0xf; - uint8_t value = (pixel >> 1) | (rvv << 4) | (vsp << 3); + uint8_t value = (pixel >> 1) | (palbank << 3); bitmap.pix(y, x + i) = palette[value]; bitmap.pix(y, x + i + 1) = palette[(pixel & 1) ? 0 : value]; if(m_back_color) @@ -655,19 +653,18 @@ I8275_DRAW_CHARACTER_MEMBER(dwarfd_state::display_pixels) I8275_DRAW_CHARACTER_MEMBER(dwarfd_state::qc_display_pixels) { - int bank = gpa; + using namespace i8275_attributes; + int bank = BIT(attrcode, GPA0, 2); + int palbank = (BIT(attrcode, RVV) ? 2 : 0) + (BIT(attrcode, VSP) ? 1 : 0); const rgb_t *palette = m_palette->palette()->entry_list_raw(); uint16_t pixels = m_charmap[(linecount & 7) + ((charcode + (bank * 128)) << 3)]; if(!x) m_back_color = false; - //if(!linecount) - // logerror("%d %d %02x %02x %02x %02x %02x %02x %02x\n", x/8, y/8, charcode, lineattr, lten, rvv, vsp, gpa, hlgt); - for (int i = 0; i < 8; i += 2) { uint8_t pixel = (pixels >> (i * 2)) & 0xf; - uint8_t value = (pixel >> 1) | (rvv << 4) | (vsp << 3); + uint8_t value = (pixel >> 1) | (palbank << 3); bitmap.pix(y, x + i) = palette[value]; bitmap.pix(y, x + i + 1) = palette[(pixel & 1) ? 0 : value]; if(m_back_color) diff --git a/src/mame/nokia/mikromik_v.cpp b/src/mame/nokia/mikromik_v.cpp index 40a2a270c59..2ab2ae802ad 100644 --- a/src/mame/nokia/mikromik_v.cpp +++ b/src/mame/nokia/mikromik_v.cpp @@ -14,14 +14,17 @@ I8275_DRAW_CHARACTER_MEMBER( mm1_state::crtc_display_pixels ) { uint8_t romdata = m_char_rom->base()[(charcode << 4) | linecount]; - int gpa0 = BIT(gpa, 0); // general purpose attribute 0 - int llen = m_llen; // light enable - int compl_in = rvv; // reverse video - int hlt_in = hlgt; // highlight; - int color; // 0 = black, 1 = dk green, 2 = lt green; on MikroMikko 1, "highlight" is actually the darker shade of green + using namespace i8275_attributes; + bool vsp = BIT(attrcode, VSP); + bool lten = BIT(attrcode, LTEN); + bool gpa0 = BIT(attrcode, GPA0); // general purpose attribute 0 + int llen = m_llen; // light enable + bool compl_in = BIT(attrcode, RVV); // reverse video + bool hlt_in = BIT(attrcode, HLGT); // highlight + int color; // 0 = black, 1 = dk green, 2 = lt green; on MikroMikko 1, "highlight" is actually the darker shade of green - int d7 = BIT(romdata, 7); // save MSB (1 indicates that this is a Visual Attribute or Special Code instead of a normal display character) - int d6 = BIT(romdata, 6); // save also first and last char bitmap bits before shifting out the MSB + int d7 = BIT(romdata, 7); // save MSB (1 indicates that this is a Visual Attribute or Special Code instead of a normal display character) + int d6 = BIT(romdata, 6); // save also first and last char bitmap bits before shifting out the MSB int d0 = BIT(romdata, 0); uint8_t data = (romdata << 1) | (d7 & d0); // get rid of MSB, duplicate LSB for special characters diff --git a/src/mame/regnecentralen/rc702.cpp b/src/mame/regnecentralen/rc702.cpp index f2a9a189c34..d78e0015911 100644 --- a/src/mame/regnecentralen/rc702.cpp +++ b/src/mame/regnecentralen/rc702.cpp @@ -273,13 +273,15 @@ I8275_DRAW_CHARACTER_MEMBER( rc702_state::display_pixels ) const rgb_t *palette = m_palette->palette()->entry_list_raw(); uint8_t gfx = 0; - if (!vsp) + using namespace i8275_attributes; + + if (!BIT(attrcode, VSP)) gfx = m_p_chargen[(linecount & 15) | (charcode << 4)]; - if (lten) + if (BIT(attrcode, LTEN)) gfx = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; // Highlight not used diff --git a/src/mame/robotron/rt1715.cpp b/src/mame/robotron/rt1715.cpp index fe9bbf908ef..b9a31dc28d0 100644 --- a/src/mame/robotron/rt1715.cpp +++ b/src/mame/robotron/rt1715.cpp @@ -423,15 +423,18 @@ void rt1715_state::crtc_drq_w(int state) I8275_DRAW_CHARACTER_MEMBER(rt1715_state::crtc_display_pixels) { + using namespace i8275_attributes; + rgb_t const *const palette = m_palette->palette()->entry_list_raw(); - u8 gfx = (lten) ? 0xff : 0; + u8 gfx = BIT(attrcode, LTEN) ? 0xff : 0; - if (!vsp) - gfx = m_p_chargen[((gpa & 1) << 11) | (linecount << 7) | charcode]; + if (!BIT(attrcode, VSP)) + gfx = m_p_chargen[(BIT(attrcode, GPA0) ? 0x800 : 0) | (linecount << 7) | charcode]; - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for (u8 i=0; i<8; i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/sfrj/tim100.cpp b/src/mame/sfrj/tim100.cpp index e24640d08d9..667c87b1834 100644 --- a/src/mame/sfrj/tim100.cpp +++ b/src/mame/sfrj/tim100.cpp @@ -142,16 +142,18 @@ I8275_DRAW_CHARACTER_MEMBER( tim100_state::crtc_display_pixels ) rgb_t const *const palette = m_palette->palette()->entry_list_raw(); for (uint8_t i = 0; i < 2; i++) { + using namespace i8275_attributes; uint8_t pixels = m_charmap[(i * 0x1000) | (linecount & 15) | (charcode << 4)]; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); bitmap.pix(y, x++) = palette[BIT(pixels, 7) ? (hlgt ? 2 : 1) : 0]; bitmap.pix(y, x++) = palette[BIT(pixels, 6) ? (hlgt ? 2 : 1) : 0]; bitmap.pix(y, x++) = palette[BIT(pixels, 5) ? (hlgt ? 2 : 1) : 0]; diff --git a/src/mame/skeleton/grfd2301.cpp b/src/mame/skeleton/grfd2301.cpp index fcb3cd29de2..fda3182ac8e 100644 --- a/src/mame/skeleton/grfd2301.cpp +++ b/src/mame/skeleton/grfd2301.cpp @@ -92,10 +92,12 @@ void grfd2301_state::vrtc_w(int state) I8275_DRAW_CHARACTER_MEMBER(grfd2301_state::draw_character) { + using namespace i8275_attributes; + // HACK: adjust for incorrect character generator u8 lc = (linecount - 1) & 0x0f; - u8 gfx = lten ? 0xff : (vsp || lc > 8) ? 0 : m_p_chargen[(charcode << 4) | lc]; - if (rvv) + u8 gfx = BIT(attrcode, LTEN) ? 0xff : (BIT(attrcode, VSP) || lc > 8) ? 0 : m_p_chargen[(charcode << 4) | lc]; + if (BIT(attrcode, RVV)) gfx ^= 0xff; for (int i = 8; --i >= 0; ) bitmap.pix(y, x++) = BIT(gfx, i) ? rgb_t::white() : rgb_t::black(); diff --git a/src/mame/skeleton/scopus.cpp b/src/mame/skeleton/scopus.cpp index ebf5e369d8c..37a085d8596 100644 --- a/src/mame/skeleton/scopus.cpp +++ b/src/mame/skeleton/scopus.cpp @@ -101,15 +101,16 @@ I8275_DRAW_CHARACTER_MEMBER(sagitta180_state::crtc_display_pixels) uint8_t const chargen_byte = m_chargen[ (linecount & 7) | ((unsigned)charcode << 3) ]; uint8_t pixels; - if (lten) { + using namespace i8275_attributes; + if (BIT(attrcode, LTEN)) { pixels = ~0; - } else if (vsp != 0 || (linecount & 8) != 0) { + } else if (BIT(attrcode, VSP) || (linecount & 8) != 0) { pixels = 0; } else { pixels = chargen_byte; } - if (rvv) { + if (BIT(attrcode, RVV)) { pixels = ~pixels; } diff --git a/src/mame/skeleton/systel1.cpp b/src/mame/skeleton/systel1.cpp index 7ead948869a..626c97e8fdd 100644 --- a/src/mame/skeleton/systel1.cpp +++ b/src/mame/skeleton/systel1.cpp @@ -105,8 +105,9 @@ void systel1_state::memory_w(offs_t offset, u8 data) I8275_DRAW_CHARACTER_MEMBER(systel1_state::draw_character) { - u8 dots = lten ? 0xff : vsp ? 0 : m_chargen[(charcode << 4) | linecount]; - if (rvv) + using namespace i8275_attributes; + u8 dots = BIT(attrcode, LTEN) ? 0xff : BIT(attrcode, VSP) ? 0 : m_chargen[(charcode << 4) | linecount]; + if (BIT(attrcode, RVV)) dots ^= 0xff; for (int i = 0; i < 7; i++) diff --git a/src/mame/skeleton/unistar.cpp b/src/mame/skeleton/unistar.cpp index a2963397427..d018ee9b8c2 100644 --- a/src/mame/skeleton/unistar.cpp +++ b/src/mame/skeleton/unistar.cpp @@ -141,15 +141,18 @@ I8275_DRAW_CHARACTER_MEMBER(unistar_state::draw_character) rgb_t const *const palette = m_palette->palette()->entry_list_raw(); u8 gfx = m_chargen[(linecount & 15) | (charcode << 4)]; - if (vsp) + using namespace i8275_attributes; + + if (BIT(attrcode, VSP)) gfx = 0; - if (lten) + if (BIT(attrcode, LTEN)) gfx = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(u8 i=0;i<8;i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/skeleton/zorba.cpp b/src/mame/skeleton/zorba.cpp index 10d52b68100..dbeb8de55b7 100644 --- a/src/mame/skeleton/zorba.cpp +++ b/src/mame/skeleton/zorba.cpp @@ -612,18 +612,20 @@ I8275_DRAW_CHARACTER_MEMBER( zorba_state::zorba_update_chr ) { rgb_t const *const palette = m_palette->palette()->entry_list_raw(); - uint8_t gfx = m_p_chargen[(linecount & 15) + (charcode << 4) + ((gpa & 1) << 11)]; + using namespace i8275_attributes; + uint8_t gfx = m_p_chargen[(linecount & 15) + (charcode << 4) + (BIT(attrcode, GPA0) ? 0x800 : 0)]; - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; // VSP actually overrides reverse video here - if (vsp) + if (BIT(attrcode, VSP)) gfx = 0; - if (lten) + if (BIT(attrcode, LTEN)) gfx = 0xff; + bool hlgt = BIT(attrcode, HLGT); for (int i = 0; i < 8; i++) bitmap.pix(y, x + 7 - i) = palette[BIT(gfx, i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/trs/trs80dt1.cpp b/src/mame/trs/trs80dt1.cpp index 0910018a5c5..dbb26aa5f69 100644 --- a/src/mame/trs/trs80dt1.cpp +++ b/src/mame/trs/trs80dt1.cpp @@ -335,18 +335,19 @@ I8275_DRAW_CHARACTER_MEMBER( trs80dt1_state::crtc_update_row ) rgb_t const *const palette = m_palette->palette()->entry_list_raw(); u8 gfx = 0; - if (lten) // underline attr + using namespace i8275_attributes; + if (BIT(attrcode, LTEN)) // underline attr gfx = 0xff; - else - if ((gpa | vsp)==0) // blinking and invisible attributes + else if (BIT(attrcode, GPA0, 2) == 0 && !BIT(attrcode, VSP)) // blinking and invisible attributes gfx = m_p_chargen[linecount | (charcode << 4)]; - if (rvv) // reverse video attr + if (BIT(attrcode, RVV)) // reverse video attr gfx ^= 0xff; if (m_bow) // black-on-white gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(u8 i=0; i<8; i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/apogee.cpp b/src/mame/ussr/apogee.cpp index fa78910aa31..4006177f300 100644 --- a/src/mame/ussr/apogee.cpp +++ b/src/mame/ussr/apogee.cpp @@ -208,18 +208,20 @@ void apogee_state::machine_start() I8275_DRAW_CHARACTER_MEMBER(apogee_state::display_pixels) { + using namespace i8275_attributes; rgb_t const *const palette = m_palette->palette()->entry_list_raw(); - uint8_t const *const charmap = &m_chargen[(gpa & 1) * 0x400]; + uint8_t const *const charmap = &m_chargen[BIT(attrcode, GPA0) ? 0x400 : 0]; uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(int i=0;i<6;i++) bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/argo.cpp b/src/mame/ussr/argo.cpp index 64d157f963d..d41d6211e8f 100644 --- a/src/mame/ussr/argo.cpp +++ b/src/mame/ussr/argo.cpp @@ -257,19 +257,22 @@ I8275_DRAW_CHARACTER_MEMBER(argo_state::display_pixels) const rgb_t *palette = m_palette->palette()->entry_list_raw(); u8 gfx = m_p_chargen[(linecount & 15) | (charcode << 4)]; - if (vsp) + using namespace i8275_attributes; + + if (BIT(attrcode, VSP)) gfx = 0; - if (lten) + if (BIT(attrcode, LTEN)) { gfx = 0xff; if (x > 6) x-=6; // hack to fix cursor position } - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(u8 i=0;i<7;i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 6-i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/mikrosha.cpp b/src/mame/ussr/mikrosha.cpp index 71c16a914f5..c24bd76138b 100644 --- a/src/mame/ussr/mikrosha.cpp +++ b/src/mame/ussr/mikrosha.cpp @@ -181,18 +181,21 @@ void mikrosha_state::machine_start() I8275_DRAW_CHARACTER_MEMBER(mikrosha_state::display_pixels) { + using namespace i8275_attributes; + rgb_t const *const palette = m_palette->palette()->entry_list_raw(); uint8_t const *const charmap = &m_chargen[(m_mikrosha_font_page & 1) * 0x400]; uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(int i=0;i<6;i++) bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/ms6102.cpp b/src/mame/ussr/ms6102.cpp index 8a3451d55d8..9e1c9aab734 100644 --- a/src/mame/ussr/ms6102.cpp +++ b/src/mame/ussr/ms6102.cpp @@ -93,7 +93,6 @@ private: template void irq(int state) { m_pic->r_w(N, state ? 0 : 1); } I8275_DRAW_CHARACTER_MEMBER(display_pixels); - I8275_DRAW_CHARACTER_MEMBER(display_attr); void hrq_w(int state); void irq_w(int state); @@ -188,22 +187,21 @@ u8 ms6102_state::memory_read_byte(offs_t offset) I8275_DRAW_CHARACTER_MEMBER(ms6102_state::display_pixels) { - rgb_t const *const palette = m_palette->palette()->entry_list_raw(); - u8 gfx = (lten) ? 0xff : 0; - if (!vsp) - gfx = m_p_chargen[linecount | (charcode << 4)]; + using namespace i8275_attributes; - if (rvv) + rgb_t const *const palette = m_palette->palette()->entry_list_raw(); + u8 gfx = BIT(attrcode, LTEN) ? 0xff : 0; + if (!BIT(attrcode, VSP) && !BIT(attrcode, LTEN)) + gfx = m_p_chargen[linecount | (charcode & 0x7f) << 4 | (BIT(attrcode, GPA0 + 8) ? 0x800 : 0)]; + + if (BIT(attrcode, RVV)) gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(u8 i=0; i<8; i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0]; } -I8275_DRAW_CHARACTER_MEMBER(ms6102_state::display_attr) // TODO: attributes -{ -} - u8 ms6102_state::crtc_r(offs_t offset) { m_crtc2->read(offset); @@ -245,8 +243,7 @@ void ms6102_state::vdack_w(u8 data) { if(m_dmaaddr & 1) m_crtc1->dack_w(data); - else - m_crtc2->dack_w(data | 0x80); + m_crtc2->dack_w(data | 0x80); } IRQ_CALLBACK_MEMBER(ms6102_state::ms6102_int_ack) @@ -279,6 +276,8 @@ void ms6102_state::machine_start() { const uint8_t *srcp = &m_p_chargen[0x1000 | bitswap<3>(m_p_charmap[i], 0, 1, 2) << 8 | (m_p_charmap[i] & 8) >> 1 | (i & 0x01) << 1]; uint8_t *dstp = &m_p_chargen[(i & 0x38) << 6 | (i & 0x07) << 1]; + + // copy a 2-line slice of a 32-character block for (int j = 0; j < 0x20; j++) std::copy_n(srcp + j * 8, 2, dstp + j * 16); } @@ -316,10 +315,10 @@ void ms6102_state::ms6102(machine_config &config) m_crtc1->set_display_callback(FUNC(ms6102_state::display_pixels)); m_crtc1->drq_wr_callback().set("dma8257", FUNC(i8257_device::dreq2_w)); m_crtc1->set_screen(m_screen); + m_crtc1->set_next_crtc(m_crtc2); I8275(config, m_crtc2, XTAL(16'400'000) / 8); m_crtc2->set_character_width(8); - m_crtc2->set_display_callback(FUNC(ms6102_state::display_attr)); m_crtc2->irq_wr_callback().set(FUNC(ms6102_state::irq<5>)); m_crtc2->set_screen(m_screen); diff --git a/src/mame/ussr/partner_m.cpp b/src/mame/ussr/partner_m.cpp index 2d5bb055813..9809c54c68d 100644 --- a/src/mame/ussr/partner_m.cpp +++ b/src/mame/ussr/partner_m.cpp @@ -343,16 +343,17 @@ void partner_state::mem_page_w(u8 data) I8275_DRAW_CHARACTER_MEMBER(partner_state::display_pixels) { + using namespace i8275_attributes; rgb_t const *const palette = m_palette->palette()->entry_list_raw(); - u8 const *const charmap = &m_chargen[0x400 * (gpa * 2 + hlgt)]; + u8 const *const charmap = &m_chargen[0x400 * bitswap<3>(attrcode, GPA1, GPA0, HLGT)]; u8 pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; for (int i=0;i<6;i++) diff --git a/src/mame/ussr/radio86_m.cpp b/src/mame/ussr/radio86_m.cpp index 0807ec01a89..4b52dc081f0 100644 --- a/src/mame/ussr/radio86_m.cpp +++ b/src/mame/ussr/radio86_m.cpp @@ -206,17 +206,20 @@ void radio86_state::radio86_romdisk_portc_w(u8 data) I8275_DRAW_CHARACTER_MEMBER(radio86_state::display_pixels) { + using namespace i8275_attributes; + rgb_t const *const palette = m_palette->palette()->entry_list_raw(); u8 pixels = m_chargen[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for (u8 i = 0; i < 6; i++) bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/sm1800.cpp b/src/mame/ussr/sm1800.cpp index 66fa689104c..e461ddda5fd 100644 --- a/src/mame/ussr/sm1800.cpp +++ b/src/mame/ussr/sm1800.cpp @@ -101,18 +101,21 @@ INTERRUPT_GEN_MEMBER(sm1800_state::vblank_interrupt) I8275_DRAW_CHARACTER_MEMBER( sm1800_state::crtc_display_pixels ) { + using namespace i8275_attributes; + rgb_t const *const palette = m_palette->palette()->entry_list_raw(); uint8_t const *const charmap = memregion("chargen")->base(); uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff; - if (vsp) + if (BIT(attrcode, VSP)) pixels = 0; - if (lten) + if (BIT(attrcode, LTEN)) pixels = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) pixels ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(int i=0;i<8;i++) bitmap.pix(y, x + i) = palette[(pixels >> (7-i)) & 1 ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/ussr/unior.cpp b/src/mame/ussr/unior.cpp index c6be1848c16..1bbcd4e5f00 100644 --- a/src/mame/ussr/unior.cpp +++ b/src/mame/ussr/unior.cpp @@ -288,15 +288,18 @@ I8275_DRAW_CHARACTER_MEMBER(unior_state::display_pixels) rgb_t const *const palette = m_palette->palette()->entry_list_raw(); u8 gfx = m_p_chargen[(linecount & 7) | (charcode << 3)]; - if (vsp) + using namespace i8275_attributes; + + if (BIT(attrcode, VSP)) gfx = 0; - if (lten) + if (BIT(attrcode, LTEN)) gfx = 0xff; - if (rvv) + if (BIT(attrcode, RVV)) gfx ^= 0xff; + bool hlgt = BIT(attrcode, HLGT); for(u8 i=0;i<6;i++) bitmap.pix(y, x + i) = palette[BIT(gfx, 5-i) ? (hlgt ? 2 : 1) : 0]; } diff --git a/src/mame/wicat/t7000.cpp b/src/mame/wicat/t7000.cpp index a223a7edf0d..f3d17692a0a 100644 --- a/src/mame/wicat/t7000.cpp +++ b/src/mame/wicat/t7000.cpp @@ -10,9 +10,7 @@ sets. Keytronic Model L2207 is the specified keyboard. Settings can be saved to nonvolatile memory by typing "PERM" (all caps) in Set-Up mode. - Currently the cursor display and attributes are not provided due to - 8276 emulation not supporting the dual configuration used here. The - optional touch panel is also not supported. + Currently the optional touch panel is not supported. ****************************************************************************/ @@ -93,7 +91,18 @@ void t7000_state::machine_start() I8275_DRAW_CHARACTER_MEMBER(t7000_state::display_character) { - u16 dots = vsp ? 0 : m_chargen[(charcode << 4) | linecount]; + // TODO: blinking and blanked characters + using namespace i8275_attributes; + u16 dots = 0; + if (!BIT(attrcode, VSP)) + { + if (BIT(attrcode, LTEN + 8) || (BIT(charcode, 12) && linecount == 10)) + dots = 0x3ff; // underscore + else + dots = m_chargen[(charcode & 0x7f) << 4 | linecount] << 1; + if (BIT(attrcode, RVV + 8) || BIT(charcode, 11)) + dots ^= 0x3ff; // reverse video + } rgb_t fg = rgb_t::white(); rgb_t bg = rgb_t::black(); @@ -102,10 +111,12 @@ I8275_DRAW_CHARACTER_MEMBER(t7000_state::display_character) using std::swap; swap(fg, bg); } + if (BIT(charcode, 8)) + fg = rgb_t(0xc0, 0xc0, 0xc0); // reduced intensity for (int i = 0; i < 10; i++) { - bitmap.pix(y, x + i) = ((dots & 0x300) != 0) ? fg : bg; + bitmap.pix(y, x + i) = BIT(dots, 9) ? fg : bg; dots <<= 1; } } @@ -239,6 +250,7 @@ void t7000_state::t7000(machine_config &config) m_crtc[0]->drq_wr_callback().set("mainnmi", FUNC(input_merger_device::in_w<1>)); m_crtc[0]->vrtc_wr_callback().set(FUNC(t7000_state::vblint_w)); m_crtc[0]->set_screen("screen"); + m_crtc[0]->set_next_crtc(m_crtc[1]); I8276(config, m_crtc[1], 19.6608_MHz_XTAL / 10); m_crtc[1]->set_character_width(10); diff --git a/src/mame/wicat/wicat.cpp b/src/mame/wicat/wicat.cpp index 38135ea0639..450f616497b 100644 --- a/src/mame/wicat/wicat.cpp +++ b/src/mame/wicat/wicat.cpp @@ -456,12 +456,13 @@ void wicat_state::crtc_irq_clear_w(int state) I8275_DRAW_CHARACTER_MEMBER(wicat_state::wicat_display_pixels) { - uint16_t romdata = lten ? 0x3ff : vsp ? 0 : m_chargen->base()[(charcode << 4) | linecount]; + using namespace i8275_attributes; + uint16_t romdata = BIT(attrcode, LTEN) ? 0x3ff : BIT(attrcode, VSP) ? 0 : m_chargen->base()[(charcode << 4) | linecount]; pen_t const *const pen = m_palette->pens(); for (int i = 0; i < 10; i++) { - int color = ((romdata & 0x300) != 0) ^ rvv; + int color = ((romdata & 0x300) != 0) ^ BIT(attrcode, RVV); bitmap.pix(y, x + i) = pen[color]; romdata <<= 1; diff --git a/src/mame/wyse/wy100.cpp b/src/mame/wyse/wy100.cpp index b270895bdc0..41f25a6b4d7 100644 --- a/src/mame/wyse/wy100.cpp +++ b/src/mame/wyse/wy100.cpp @@ -2,17 +2,13 @@ // copyright-holders:AJR /******************************************************************************* - Skeleton driver for Wyse WY-100 video terminal. + Driver for Wyse WY-100 video terminal. The WY-100 was Wyse Technology's first product. Of the two 8276 CRTCs, one is used solely to keep track of which characters are protected, which is the only transparent attribute supported. - Known emulation bugs: - - Frequent screen glitches when writing to the display - - No dimming of protected characters - *******************************************************************************/ #include "emu.h" @@ -101,19 +97,19 @@ void wy100_state::brdy_w(int state) I8275_DRAW_CHARACTER_MEMBER(wy100_state::draw_character) { // LTEN attribute output is not used (GPA1 generates underline instead) + using namespace i8275_attributes; u8 dots = 0; - if (!vsp) + if (!BIT(attrcode, VSP)) { - if (BIT(gpa, 1) && (linecount & 0xb) == 0xa) + if (BIT(attrcode, GPA1) && (linecount & 0xb) == 0xa) dots = 0xff; - else if (!BIT(gpa, 0)) - dots = m_chargen[(charcode << 4) | linecount]; + else if (!BIT(attrcode, GPA0)) + dots = m_chargen[((charcode & 0x7f) << 4) | linecount]; } - if (rvv) + if (BIT(attrcode, RVV)) dots ^= 0xff; - // TODO: dim protected characters - const rgb_t fg = rgb_t::white(); + const rgb_t fg = BIT(charcode, 8) && BIT(attrcode, HLGT) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0); const rgb_t bg = rgb_t::black(); for (int i = 0; i < 10; i++) bitmap.pix(y, x + i) = BIT(dots, i < 1 || i > 8 ? 7 : 8 - i) ? fg : bg; @@ -277,6 +273,7 @@ void wy100_state::wy100(machine_config &config) m_crtc[0]->drq_wr_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ); m_crtc[0]->drq_wr_callback().append(FUNC(wy100_state::brdy_w)); m_crtc[0]->lc_wr_callback().set("spkrgate", FUNC(input_merger_device::in_w<1>)).bit(3); + m_crtc[0]->set_next_crtc(m_crtc[1]); SPEAKER(config, "mono").front_center(); SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5); @@ -304,4 +301,4 @@ ROM_END } // anonymous namespace -COMP(1981, wy100, 0, 0, wy100, wy100, wy100_state, empty_init, "Wyse Technology", "WY-100", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS) +COMP(1981, wy100, 0, 0, wy100, wy100, wy100_state, empty_init, "Wyse Technology", "WY-100", MACHINE_SUPPORTS_SAVE)