// license:BSD-3-Clause // copyright-holders:Wilbert Pol /*************************************************************************** ef9340_1.h Thomson EF9340 + EF9341 teletext graphics chips with 1KB external character ram. ***************************************************************************/ #include "emu.h" #include "ef9340_1.h" #include "screen.h" // device type definition DEFINE_DEVICE_TYPE(EF9340_1, ef9340_1_device, "ef9340_1", "Thomson EF9340+EF9341") static constexpr uint8_t bgr2rgb[8] = { 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07 }; ef9340_1_device::ef9340_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, EF9340_1, tag, owner, clock) , device_video_interface(mconfig, *this) , m_line_timer(nullptr) , m_charset(*this, "ef9340_1") //, m_start_vpos(START_Y) //, m_start_vblank(START_Y + SCREEN_HEIGHT) //, m_screen_lines(LINES) { } ROM_START( ef9340_1 ) ROM_REGION( 0xA00, "ef9340_1", 0 ) ROM_LOAD( "charset_ef9340_1.rom", 0x0000, 0x0A00, CRC(d557a7bf) SHA1(d100b0f6a0d5a2d540844bf362788659ed9a6eb4) ) ROM_END const tiny_rom_entry *ef9340_1_device::device_rom_region() const { return ROM_NAME( ef9340_1 ); } void ef9340_1_device::device_start() { // Let the screen create our temporary bitmap with the screen's dimensions screen().register_screen_bitmap(m_tmp_bitmap); m_line_timer = timer_alloc(TIMER_LINE); m_line_timer->adjust( screen().time_until_pos(0, 0), 0, screen().scan_period() ); // register our state save_item(NAME(m_ef9341.TA)); save_item(NAME(m_ef9341.TB)); save_item(NAME(m_ef9341.busy)); save_item(NAME(m_ef9340.X)); save_item(NAME(m_ef9340.Y)); save_item(NAME(m_ef9340.Y0)); save_item(NAME(m_ef9340.R)); save_item(NAME(m_ef9340.M)); save_pointer(NAME(m_ef934x_ram_a), 1024); save_pointer(NAME(m_ef934x_ram_b), 1024); save_pointer(NAME(m_ef934x_ext_char_ram), 1024); } void ef9340_1_device::device_reset() { memset(m_ef934x_ram_a, 0, sizeof(m_ef934x_ram_a)); memset(m_ef934x_ram_b, 0, sizeof(m_ef934x_ram_b)); m_ef9340.X = 0; m_ef9340.Y = 0; m_ef9340.Y0 = 0; m_ef9340.R = 0; m_ef9340.M = 0; m_ef9340.max_vpos = 210; m_ef9341.TA = 0; m_ef9341.TB = 0; m_ef9341.busy = 0; } void ef9340_1_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch ( id ) { case TIMER_LINE: ef9340_scanline(screen().vpos()); break; } } uint16_t ef9340_1_device::ef9340_get_c_addr(uint8_t x, uint8_t y) { if ( ( y & 0x18 ) == 0x18 ) { return 0x318 | ( ( x & 0x38 ) << 2 ) | ( x & 0x07 ); } if ( x & 0x20 ) { return 0x300 | ( ( y & 0x07 ) << 5 ) | ( y & 0x18 ) | ( x & 0x07 ); } return y << 5 | x; } void ef9340_1_device::ef9340_inc_c() { m_ef9340.X++; if ( m_ef9340.X == 40 || m_ef9340.X == 48 || m_ef9340.X == 56 || m_ef9340.X == 64 ) { m_ef9340.Y = ( m_ef9340.Y + 1 ) & 0x1f; if ( m_ef9340.Y == 24 ) { m_ef9340.Y = 0; } m_ef9340.X = 0; } } uint16_t ef9340_1_device::external_chargen_address(uint8_t b, uint8_t slice) { uint8_t cc = b & 0x7f; if ( slice & 8 ) { // 0 0 CCE4 CCE3 CCE2 CCE1 CCE0 CCE6 CCE5 ADR0 return ( ( cc << 3 ) & 0xf8 ) | ( ( cc >> 4 ) & 0x06) | ( slice & 0x01 ); } // CCE6 CCE5 CCE4 CCE3 CCE2 CCE1 CCE0 ADR2 ADR1 ADR0 return ( cc << 3 ) | ( slice & 0x07 ); } void ef9340_1_device::ef9341_write( uint8_t command, uint8_t b, uint8_t data ) { logerror("ef9341 %s write, t%s, data %02X\n", command ? "command" : "data", b ? "B" : "A", data ); if ( command ) { if ( b ) { m_ef9341.TB = data; m_ef9341.busy = 0x80; switch( m_ef9341.TB & 0xE0 ) { case 0x00: /* Begin row */ m_ef9340.X = 0; m_ef9340.Y = m_ef9341.TA & 0x1F; break; case 0x20: /* Load Y */ m_ef9340.Y = m_ef9341.TA & 0x1F; break; case 0x40: /* Load X */ m_ef9340.X = m_ef9341.TA & 0x3F; break; case 0x60: /* INC C */ ef9340_inc_c(); break; case 0x80: /* Load M */ m_ef9340.M = m_ef9341.TA; break; case 0xA0: /* Load R */ m_ef9340.R = m_ef9341.TA; m_ef9340.max_vpos = ( m_ef9340.R & 0x40 ) ? 250 : 210; break; case 0xC0: /* Load Y0 */ m_ef9340.Y0 = m_ef9341.TA & 0x3F; break; } m_ef9341.busy = 0; } else { m_ef9341.TA = data; } } else { if ( b ) { uint16_t addr = ef9340_get_c_addr( m_ef9340.X, m_ef9340.Y ) & 0x3ff; m_ef9341.TB = data; m_ef9341.busy = 0x80; switch ( m_ef9340.M & 0xE0 ) { case 0x00: /* Write */ m_ef934x_ram_a[addr] = m_ef9341.TA; m_ef934x_ram_b[addr] = m_ef9341.TB; ef9340_inc_c(); break; case 0x20: /* Read */ m_ef9341.TA = m_ef934x_ram_a[addr]; m_ef9341.TB = m_ef934x_ram_b[addr]; ef9340_inc_c(); break; case 0x40: /* Write without increment */ m_ef934x_ram_a[addr] = m_ef9341.TA; m_ef934x_ram_b[addr] = m_ef9341.TB; break; case 0x60: /* Read without increment */ m_ef9341.TA = m_ef934x_ram_a[addr]; m_ef9341.TB = m_ef934x_ram_b[addr]; break; case 0x80: /* Write slice */ { uint8_t a = m_ef934x_ram_a[addr]; uint8_t b = m_ef934x_ram_b[addr]; uint8_t slice = ( m_ef9340.M & 0x0f ) % 10; if ( b >= 0xa0 ) { m_ef934x_ext_char_ram[ ( ( a & 0x80 ) << 3 ) | external_chargen_address( b, slice ) ] = bitswap<8>(m_ef9341.TA,0,1,2,3,4,5,6,7); } // Increment slice number m_ef9340.M = ( m_ef9340.M & 0xf0) | ( ( slice + 1 ) % 10 ); } break; case 0xA0: /* Read slice */ default: fatalerror/*logerror*/("ef9341 unimplemented data action %02X\n", m_ef9340.M & 0xE0 ); } m_ef9341.busy = 0; } else { m_ef9341.TA = data; } } } uint8_t ef9340_1_device::ef9341_read( uint8_t command, uint8_t b ) { uint8_t data; logerror("ef9341 %s read, t%s\n", command ? "command" : "data", b ? "B" : "A" ); if ( command ) { if ( b ) { data = 0; } else { data = m_ef9341.busy; } } else { if ( b ) { data = m_ef9341.TB; } else { data = m_ef9341.TA; } } return data; } void ef9340_1_device::ef9340_scanline(int vpos) { if ( vpos < m_ef9340.max_vpos ) { int y = vpos - 0; int y_row, slice; if ( y < 10 ) { // Service row if ( m_ef9340.R & 0x08 ) { // Service row is enabled y_row = 31; slice = y; } else { // Service row is disabled for ( int i = 0; i < 40 * 8; i++ ) { m_tmp_bitmap.pix16(vpos, 0 + i ) = 24; } return; } } else { // Displaying regular row y_row = (y - 10) / 10; slice = (y - 10) % 10; } for ( int x = 0; x < 40; x++ ) { uint16_t addr = ef9340_get_c_addr( x, y_row ); uint8_t a = m_ef934x_ram_a[addr]; uint8_t b = m_ef934x_ram_b[addr]; uint8_t fg = 0; uint8_t bg = 0; uint8_t char_data = 0x00; if ( a & 0x80 ) { // Graphics if ( b & 0x80 ) { if ( b & 0x60 ) { // Extension char_data = m_ef934x_ext_char_ram[ 0x400 | external_chargen_address( b & 0x7f, slice ) ]; fg = bgr2rgb[ a & 0x07 ]; bg = bgr2rgb[ ( a >> 4 ) & 0x07 ]; } } else { // Normal char_data = m_charset[((b | 0x80) * 10) + slice]; fg = bgr2rgb[ a & 0x07 ]; bg = bgr2rgb[ ( a >> 4 ) & 0x07 ]; } } else { // Alphannumeric if ( b & 0x80 ) { if ( b & 0x60 ) { // Extension char_data = m_ef934x_ext_char_ram[ external_chargen_address( b & 0x7f, slice ) ]; if ( a & 0x40 ) { fg = bg; bg = bgr2rgb[ a & 0x07 ]; } else { fg = bgr2rgb[ a & 0x07 ]; } } else { // DEL char_data = 0xff; fg = bgr2rgb[ a & 0x07 ]; } } else { // Normal char_data = m_charset[((b & 0x7f) * 10) + slice]; if ( a & 0x40 ) { fg = bg; bg = bgr2rgb[ a & 0x07 ]; } else { fg = bgr2rgb[ a & 0x07 ]; } } } for ( int i = 0; i < 8; i++ ) { m_tmp_bitmap.pix16(vpos, 0 + x*8 + i ) = (char_data & 0x80) ? fg : bg; char_data <<= 1; } } } }