// license:BSD-3-Clause
// copyright-holders: Jean-Francois DEL NERO
/***************************************************************************
Minitel 2
The Minitel is a small, on-line computer/Videotex terminal with multi-services that
can be connected to any French telephone line. This terminal was widely used in France
during the 80's and 90's.
There are several modeles and version. Most of them are based on a mcu from the 8051 family
and a EF9345 like semi graphic video chip.
The current implementation is an Minitel 2 from "La RADIOTECHNIQUE PORTENSEIGNE" / RPIC (Philips)
You can found more informations about this hardware there :
http://hxc2001.free.fr/minitel
What is implemented and working :
- Main MCU
- Video output
- Keyboard
What is not yet implemented :
- Modem and sound output.
- The rear serial port.
- Parameters I2C 24C02 EEPROM.
- Screen should go blank when switched off
The original firmware and the experimental demo rom are currently both working.
Please note the current special function keys assignation :
F1 -> Suite
F2 -> Retour
F3 -> Envoi
F4 -> Repetition
F5 -> TEL
F6 -> Guide
F7 -> Sommaire
F8 -> Connexion/Fin
F9 -> Fonction
F10-> ON / OFF
With the official ROM you need to press F10 to switch on the CRT.
****************************************************************************/
#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/timer.h"
#include "video/ef9345.h"
#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "logmacro.h"
// IO expander latch usage definitions
enum
{
CTRL_REG_DTMF = 0x02,
CTRL_REG_MCBC = 0x04,
CTRL_REG_OPTO = 0x08,
CTRL_REG_RELAY = 0x10,
CTRL_REG_CRTON = 0x20
};
// 80C32 Port IO usage definitions
enum
{
PORT_1_KBSERIN = 0x01,
PORT_1_MDM_DCD = 0x02,
PORT_1_MDM_PRD = 0x04,
PORT_1_MDM_TXD = 0x08,
PORT_1_MDM_RTS = 0x10,
PORT_1_KBLOAD = 0x20,
PORT_1_SCL = 0x40,
PORT_1_SDA = 0x80
};
class minitel_state : public driver_device
{
public:
minitel_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_ts9347(*this, "ts9347")
, m_palette(*this, "palette")
, m_io_kbd(*this, "Y%u", 0)
{
}
void minitel2(machine_config &config);
private:
required_device<cpu_device> m_maincpu;
required_device<ts9347_device> m_ts9347;
required_device<palette_device> m_palette;
TIMER_DEVICE_CALLBACK_MEMBER(minitel_scanline);
DECLARE_WRITE8_MEMBER(port1_w);
DECLARE_WRITE8_MEMBER(port3_w);
DECLARE_READ8_MEMBER(port1_r);
DECLARE_READ8_MEMBER(port3_r);
DECLARE_WRITE8_MEMBER ( dev_crtl_reg_w );
DECLARE_READ8_MEMBER ( dev_keyb_ser_r );
DECLARE_READ8_MEMBER ( ts9347_io_r );
DECLARE_WRITE8_MEMBER ( ts9347_io_w );
void mem_io(address_map &map);
void mem_prg(address_map &map);
required_ioport_array<16> m_io_kbd;
virtual void machine_start() override;
uint8_t port1, port3;
int keyboard_para_ser;
uint8_t keyboard_x_row_reg;
uint8_t last_ctrl_reg;
};
void minitel_state::machine_start()
{
m_palette->set_pen_color( 0, 0, 0, 0);
m_palette->set_pen_color( 1, 86, 86, 86);
m_palette->set_pen_color( 2, 172, 172, 172);
m_palette->set_pen_color( 3, 255, 255, 255);
m_palette->set_pen_color( 4, 44, 44, 44);
m_palette->set_pen_color( 5, 86, 86, 86);
m_palette->set_pen_color( 6, 172, 172, 172);
m_palette->set_pen_color( 7, 255, 255, 255);
}
WRITE8_MEMBER(minitel_state::port1_w)
{
LOG("port_w: write %02X to PORT1\n", data);
if( (port1 ^ data) & PORT_1_KBSERIN )
{
LOG("PORT_1_KBSERIN : %d \n", data & PORT_1_KBSERIN );
}
if( (port1 ^ data) & PORT_1_MDM_DCD )
{
LOG("PORT_1_MDM_DCD : %d \n", data & PORT_1_MDM_DCD );
}
if( (port1 ^ data) & PORT_1_MDM_PRD )
{
LOG("PORT_1_MDM_PRD : %d \n", data & PORT_1_MDM_PRD );
}
if( (port1 ^ data) & PORT_1_MDM_TXD )
{
LOG("PORT_1_MDM_TXD : %d \n", data & PORT_1_MDM_TXD );
}
if( (port1 ^ data) & PORT_1_MDM_RTS )
{
LOG("PORT_1_MDM_RTS : %d \n", data & PORT_1_MDM_RTS );
}
if( (port1 ^ data) & PORT_1_KBLOAD )
{
LOG("PORT_1_KBLOAD : %d PC:0x%x\n", data & PORT_1_KBLOAD,m_maincpu->pc() );
if(data & PORT_1_KBLOAD)
keyboard_para_ser = 1;
else
keyboard_para_ser = 0;
}
if( (port1 ^ data) & PORT_1_SCL )
{
LOG("PORT_1_SCL : %d \n", data & PORT_1_SCL );
}
if( (port1 ^ data) & PORT_1_SDA )
{
LOG("PORT_1_SDA : %d \n", data & PORT_1_SDA );
}
port1 = data;
}
WRITE8_MEMBER(minitel_state::port3_w)
{
LOG("port_w: write %02X to PORT3\n", data);
port3 = data;
}
READ8_MEMBER(minitel_state::port1_r)
{
LOG("port_r: read %02X from PORT1 - Keyboard -> %x\n", port1,((keyboard_x_row_reg>>7)&1));
return ( (port1&0xFE) | ((keyboard_x_row_reg>>7)&1) ) ;
}
READ8_MEMBER(minitel_state::port3_r)
{
LOG("port_r: read %02X from PORT3\n", port3);
return port3;
}
WRITE8_MEMBER(minitel_state::dev_crtl_reg_w)
{
if( last_ctrl_reg != data)
{
LOG("minitel_state::hw_ctrl_reg : %x %x\n",offset, data);
if( (last_ctrl_reg ^ data) & CTRL_REG_DTMF )
{
LOG("CTRL_REG_DTMF : %d \n", data & CTRL_REG_DTMF );
}
if( (last_ctrl_reg ^ data) & CTRL_REG_MCBC )
{
LOG("CTRL_REG_MCBC : %d \n", data & CTRL_REG_MCBC );
}
if( (last_ctrl_reg ^ data) & CTRL_REG_OPTO )
{
LOG("CTRL_REG_OPTO : %d \n", data & CTRL_REG_OPTO );
}
if( (last_ctrl_reg ^ data) & CTRL_REG_RELAY )
{
LOG("CTRL_REG_RELAY : %d \n", data & CTRL_REG_RELAY );
}
if( (last_ctrl_reg ^ data) & CTRL_REG_CRTON )
{
LOG("CTRL_REG_CRTON : %d \n", data & CTRL_REG_CRTON );
}
}
last_ctrl_reg = data;
}
READ8_MEMBER(minitel_state::dev_keyb_ser_r)
{
LOG("minitel_state::keyb read : %x\n",offset);
if ( keyboard_para_ser )
{
// load the 4014 with the keyboard row state
keyboard_x_row_reg = m_io_kbd[(offset>>8)&0xF]->read();
LOG("4014 Load : 0x%.2X 0x%.2X\n",(offset>>8)&0xF,keyboard_x_row_reg);
}
else
{
//shift the keyboard register...
keyboard_x_row_reg = keyboard_x_row_reg << 1;
}
return 0xFF;
}
READ8_MEMBER ( minitel_state::ts9347_io_r )
{
return m_ts9347->data_r(space, offset, 0xff);
}
WRITE8_MEMBER ( minitel_state::ts9347_io_w )
{
LOG("minitel_state::ts9347_io_w : %x %x\n",offset, data);
m_ts9347->data_w(space, offset, data, 0xff);
}
TIMER_DEVICE_CALLBACK_MEMBER(minitel_state::minitel_scanline)
{
m_ts9347->update_scanline((uint16_t)param);
}
void minitel_state::mem_prg(address_map &map)
{
map(0x0000, 0x7fff).rom();
}
void minitel_state::mem_io(address_map &map)
{
map(0x2000, 0x3fff).rw(FUNC(minitel_state::dev_keyb_ser_r), FUNC(minitel_state::dev_crtl_reg_w));
/* ts9347 */
map(0x4000, 0x5ffF).rw(FUNC(minitel_state::ts9347_io_r), FUNC(minitel_state::ts9347_io_w));
}
/* Input ports */
static INPUT_PORTS_START( minitel2 )
PORT_START("Y0")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('-')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Suite") PORT_CODE(KEYCODE_F1)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Retour") PORT_CODE(KEYCODE_F2)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
PORT_START("Y1")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Connexion/Fin") PORT_CODE(KEYCODE_F8)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Fonction") PORT_CODE(KEYCODE_F9)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) // Right maj
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) // Left maj
PORT_START("Y2")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
PORT_START("Y3")
PORT_START("Y4")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Envoi") PORT_CODE(KEYCODE_F3)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repetition") PORT_CODE(KEYCODE_F4)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
PORT_START("Y5")
PORT_START("Y6")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('?')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tel") PORT_CODE(KEYCODE_F5)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
PORT_START("Y7")
PORT_START("Y8")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(']')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BackS Delete") PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL))
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
PORT_START("Y9")
PORT_START("Y10")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('*')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('#')
PORT_START("Y11")
PORT_START("Y12")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("On/Off") PORT_CODE(KEYCODE_F10)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
PORT_START("Y13")
PORT_START("Y14")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('[')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Guide") PORT_CODE(KEYCODE_F6)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sommaire") PORT_CODE(KEYCODE_F7)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
PORT_START("Y15")
INPUT_PORTS_END
MACHINE_CONFIG_START(minitel_state::minitel2)
/* basic machine hardware */
MCFG_DEVICE_ADD("maincpu", I80C32, XTAL(14'318'181)) //verified on pcb
MCFG_DEVICE_PROGRAM_MAP(mem_prg)
MCFG_DEVICE_IO_MAP(mem_io)
MCFG_MCS51_PORT_P1_IN_CB(READ8(*this, minitel_state, port1_r))
MCFG_MCS51_PORT_P1_OUT_CB(WRITE8(*this, minitel_state, port1_w))
MCFG_MCS51_PORT_P3_IN_CB(READ8(*this, minitel_state, port3_r))
MCFG_MCS51_PORT_P3_OUT_CB(WRITE8(*this, minitel_state, port3_w))
MCFG_DEVICE_ADD("ts9347", TS9347, 0)
MCFG_EF9345_PALETTE("palette")
MCFG_TIMER_DRIVER_ADD_SCANLINE("minitel_sl", minitel_state, minitel_scanline, "screen", 0, 10)
/* video hardware */
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_UPDATE_DEVICE("ts9347", ts9347_device, screen_update)
MCFG_SCREEN_SIZE(512, 312)
MCFG_SCREEN_VISIBLE_AREA(2, 512-10, 0, 278-1)
MCFG_PALETTE_ADD("palette", 8+1)
MACHINE_CONFIG_END
ROM_START( minitel2 )
ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
ROM_DEFAULT_BIOS("ft_bv4")
ROM_SYSTEM_BIOS(0, "ft_bv4", "Minitel 2 ROM BV4")
ROMX_LOAD( "minitel2_bv4.bin", 0x0000, 0x8000, CRC(8844a0a7) SHA1(d3e9079b080dbcee27ad870ec6c39ac42e7deacf), ROM_BIOS(0) )
ROM_SYSTEM_BIOS(1, "demov1", "Minitel 2 Demo")
ROMX_LOAD( "demo_minitel.bin", 0x0000, 0x8000, CRC(607f2482) SHA1(7965edbef68e45d09dc67a4684da56003eff6328), ROM_BIOS(1) )
ROM_SYSTEM_BIOS(2, "ft_bv9", "Minitel 2 ROM Bv9")
ROMX_LOAD( "bv9.1402", 0x0000, 0x8000, CRC(ace5d65e) SHA1(c8d589f8af6bd7d339964fdece937a76db972115), ROM_BIOS(2) )
ROM_REGION( 0x4000, "ts9347", 0 )
ROM_LOAD( "charset.rom", 0x0000, 0x2000, BAD_DUMP CRC(b2f49eb3) SHA1(d0ef530be33bfc296314e7152302d95fdf9520fc) ) // from dcvg5k
ROM_END
COMP( 1989, minitel2, 0, 0, minitel2, minitel2, minitel_state, empty_init, "Philips", "Minitel 2", MACHINE_NO_SOUND )