1 // license:BSD-3-Clause
2 // copyright-holders:hap
3 // thanks-to:Sean Riddle
4 /******************************************************************************
5 
6 SciSys Executive Chess, handheld chesscomputer.
7 Also known as Senator Chess in Germany.
8 
9 Hardware notes:
10 - Fairchild 3870 MCU (variant with 4KB internal ROM)
11 - 1KB RAM (2*TC5514P)
12 - HLCD0538, HLCD0539, LCD screen
13 
14 ******************************************************************************/
15 
16 #include "emu.h"
17 
18 #include "cpu/f8/f8.h"
19 #include "machine/f3853.h"
20 #include "video/hlcd0538.h"
21 #include "video/pwm.h"
22 
23 #include "screen.h"
24 
25 // internal artwork
26 #include "saitek_exchess.lh" // clickable
27 
28 
29 namespace {
30 
31 class exchess_state : public driver_device
32 {
33 public:
exchess_state(const machine_config & mconfig,device_type type,const char * tag)34 	exchess_state(const machine_config &mconfig, device_type type, const char *tag) :
35 		driver_device(mconfig, type, tag),
36 		m_maincpu(*this, "maincpu"),
37 		m_lcd1(*this, "lcd1"),
38 		m_lcd2(*this, "lcd2"),
39 		m_display(*this, "display"),
40 		m_battery(*this, "battery"),
41 		m_inputs(*this, "IN.%u", 0)
42 	{ }
43 
44 	void exchess(machine_config &config);
45 
46 	// battery status indicator is not software controlled
DECLARE_INPUT_CHANGED_MEMBER(battery)47 	DECLARE_INPUT_CHANGED_MEMBER(battery) { m_battery = newval; }
48 
49 protected:
50 	virtual void machine_start() override;
51 
52 private:
53 	// devices/pointers
54 	required_device<cpu_device> m_maincpu;
55 	required_device<hlcd0538_device> m_lcd1;
56 	required_device<hlcd0539_device> m_lcd2;
57 	required_device<pwm_display_device> m_display;
58 	output_finder<> m_battery;
59 	required_ioport_array<4> m_inputs;
60 
61 	// address maps
62 	void main_map(address_map &map);
63 	void main_io(address_map &map);
64 
65 	// I/O handlers
66 	template<int N> void lcd_output_w(u64 data);
67 	void lcd_data_w(u8 data);
68 
ram_address()69 	u16 ram_address() { return (m_ram_address[1] << 8 | m_ram_address[0]) & 0x3ff; }
70 	template<int N> u8 ram_address_r();
71 	template<int N> void ram_address_w(u8 data);
72 	u8 ram_data_r();
73 	void ram_data_w(u8 data);
74 
75 	std::unique_ptr<u8[]> m_ram;
76 	u8 m_ram_address[2] = { 0, 0 };
77 	u64 m_lcd_data[2] = { 0, 0 };
78 };
79 
machine_start()80 void exchess_state::machine_start()
81 {
82 	m_battery.resolve();
83 	m_ram = make_unique_clear<u8[]>(0x400);
84 
85 	// register for savestates
86 	save_pointer(NAME(m_ram), 0x400);
87 	save_item(NAME(m_ram_address));
88 	save_item(NAME(m_lcd_data));
89 }
90 
91 
92 
93 /******************************************************************************
94     I/O
95 ******************************************************************************/
96 
97 // LCD
98 
99 template<int N>
lcd_output_w(u64 data)100 void exchess_state::lcd_output_w(u64 data)
101 {
102 	m_lcd_data[N] = data;
103 	m_display->matrix(m_lcd_data[0] & 0xff, m_lcd_data[1] << 26 | m_lcd_data[0] >> 8);
104 }
105 
lcd_data_w(u8 data)106 void exchess_state::lcd_data_w(u8 data)
107 {
108 	// P40: HLCD0539 data
109 	// P44: HLCD0538 data
110 	m_lcd1->data_w(BIT(data, 4));
111 	m_lcd2->data_w(BIT(data, 0));
112 
113 	if (~m_ram_address[1] & 4)
114 	{
115 		m_lcd1->clk_w(1); m_lcd1->clk_w(0);
116 		m_lcd2->clk_w(1); m_lcd2->clk_w(0);
117 	}
118 }
119 
120 
121 // 1KB RAM (port-mapped)
122 
123 template<int N>
ram_address_w(u8 data)124 void exchess_state::ram_address_w(u8 data)
125 {
126 	// P00-P07: RAM A0-A7
127 	// P10-P11: RAM A8-A9
128 	// P12: RAM CE
129 	m_ram_address[N] = data;
130 }
131 
132 template<int N>
ram_address_r()133 u8 exchess_state::ram_address_r()
134 {
135 	u8 data = m_ram_address[N];
136 
137 	// P13: Enter button
138 	return (N) ? data | (m_inputs[0]->read() & 8) : data;
139 }
140 
ram_data_w(u8 data)141 void exchess_state::ram_data_w(u8 data)
142 {
143 	if (m_ram_address[1] & 4)
144 		m_ram[ram_address()] = data;
145 }
146 
ram_data_r()147 u8 exchess_state::ram_data_r()
148 {
149 	return (m_ram_address[1] & 4) ? m_ram[ram_address()] : 0;
150 }
151 
152 
153 
154 /******************************************************************************
155     Address Maps
156 ******************************************************************************/
157 
main_map(address_map & map)158 void exchess_state::main_map(address_map &map)
159 {
160 	map.global_mask(0xfff);
161 	map(0x0000, 0x0fff).rom();
162 }
163 
main_io(address_map & map)164 void exchess_state::main_io(address_map &map)
165 {
166 	map(0x00, 0x00).rw(FUNC(exchess_state::ram_address_r<0>), FUNC(exchess_state::ram_address_w<0>));
167 	map(0x01, 0x01).rw(FUNC(exchess_state::ram_address_r<1>), FUNC(exchess_state::ram_address_w<1>));
168 	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
169 }
170 
171 
172 
173 /******************************************************************************
174     Input Ports
175 ******************************************************************************/
176 
177 static INPUT_PORTS_START( exchess )
178 	PORT_START("IN.0")
PORT_CODE(KEYCODE_ENTER)179 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
180 	PORT_BIT(0xf7, IP_ACTIVE_HIGH, IPT_UNUSED)
181 
182 	PORT_START("IN.1")
183 	PORT_BIT(0x01, 0x01, IPT_CUSTOM) PORT_CONDITION("IN.2", 0x51, NOTEQUALS, 0x00)
184 	PORT_BIT(0x02, 0x02, IPT_CUSTOM) PORT_CONDITION("IN.2", 0x32, NOTEQUALS, 0x00)
185 	PORT_BIT(0x04, 0x04, IPT_CUSTOM) PORT_CONDITION("IN.2", 0xa4, NOTEQUALS, 0x00)
186 	PORT_BIT(0x08, 0x08, IPT_CUSTOM) PORT_CONDITION("IN.2", 0xc8, NOTEQUALS, 0x00)
187 	PORT_BIT(0x30, IP_ACTIVE_HIGH, IPT_UNUSED)
188 	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
189 	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("2nd F")
190 
191 	PORT_START("IN.2") // square 'd-pad' (8-way, so define joystick)
192 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Cursor Left")
193 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_CODE(KEYCODE_UP) PORT_NAME("Cursor Up")
194 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right")
195 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Cursor Down")
196 	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) // ul
197 	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) // ur
198 	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) // dl
199 	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) // dr
200 
201 	PORT_START("IN.3")
202 	PORT_CONFNAME( 0x01, 0x00, "Battery Status" ) PORT_CHANGED_MEMBER(DEVICE_SELF, exchess_state, battery, 0)
203 	PORT_CONFSETTING(    0x01, "Low" )
204 	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
205 INPUT_PORTS_END
206 
207 
208 
209 /******************************************************************************
210     Machine Configs
211 ******************************************************************************/
212 
213 void exchess_state::exchess(machine_config &config)
214 {
215 	/* basic machine hardware */
216 	F8(config, m_maincpu, 4500000/2); // measured
217 	m_maincpu->set_addrmap(AS_PROGRAM, &exchess_state::main_map);
218 	m_maincpu->set_addrmap(AS_IO, &exchess_state::main_io);
219 	m_maincpu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));
220 
221 	f38t56_device &psu(F38T56(config, "psu", 4500000/2));
222 	psu.set_int_vector(0x0020);
223 	psu.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
224 	psu.read_a().set(FUNC(exchess_state::ram_data_r));
225 	psu.write_a().set(FUNC(exchess_state::ram_data_w));
226 	psu.write_a().append(FUNC(exchess_state::lcd_data_w));
227 	psu.read_b().set_ioport("IN.1");
228 
229 	/* video hardware */
230 	HLCD0538(config, m_lcd1, 310); // measured
231 	m_lcd1->write_cols().set(FUNC(exchess_state::lcd_output_w<0>));
232 	m_lcd1->write_interrupt().set(m_lcd2, FUNC(hlcd0539_device::lcd_w));
233 
234 	HLCD0539(config, m_lcd2, 0);
235 	m_lcd2->write_cols().set(FUNC(exchess_state::lcd_output_w<1>));
236 	m_lcd2->write_interrupt().set("psu", FUNC(f38t56_device::ext_int_w)).invert();
237 
238 	PWM_DISPLAY(config, m_display).set_size(8, 26+34);
239 	m_display->set_interpolation(0.2);
240 	config.set_default_layout(layout_saitek_exchess);
241 
242 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
243 	screen.set_refresh_hz(60);
244 	screen.set_size(1020/1.5, 1080/1.5);
245 	screen.set_visarea_full();
246 }
247 
248 
249 
250 /******************************************************************************
251     ROM Definitions
252 ******************************************************************************/
253 
254 ROM_START( exchess )
255 	ROM_REGION( 0x1000, "maincpu", 0 )
256 	ROM_LOAD("sl90553", 0x0000, 0x1000, CRC(a61b0c7e) SHA1(a13b11a93f78236223c5c0b9879a93284b7f7525) )
257 
258 	ROM_REGION( 852610, "screen", 0 )
259 	ROM_LOAD("exchess.svg", 0, 852610, CRC(cb36f9d3) SHA1(83be9b5d906d185b7cf6895f50992e7eea390c7a) )
260 ROM_END
261 
262 } // anonymous namespace
263 
264 
265 
266 /******************************************************************************
267     Drivers
268 ******************************************************************************/
269 
270 //    YEAR  NAME     PARENT CMP MACHINE  INPUT    STATE          INIT        COMPANY, FULLNAME, FLAGS
271 CONS( 1981, exchess, 0,      0, exchess, exchess, exchess_state, empty_init, "SciSys", "Executive Chess", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
272