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)