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