1 // license:BSD-3-Clause
2 // copyright-holders:hap
3 // thanks-to:bataais
4 /******************************************************************************
5 
6 SciSys Chess Partner 2000, also sold by Novag with the same name.
7 It's probably the last SciSys / Novag collaboration.
8 
9 Hardware notes:
10 - 3850PK CPU at ~2.77MHz(averaged), 3853PK memory interface
11 - 4KB ROM, 256 bytes RAM(2*2111N)
12 - 4-digit 7seg panel, sensory chessboard
13 
14 3850 is officially rated 2MHz, and even the CP2000 manual says it runs at 2MHz,
15 but tests show that the chesscomputer runs at a much higher speed. Three individual
16 CP2000 were measured, by timing move calculation, and one recording to verify
17 beeper pitch and display blinking rate. Real CP2000 CPU frequency is in the
18 2.63MHz to 2.91MHz range.
19 
20 Entering moves is not as friendly as newer sensory games. The player is expected
21 to press ENTER after their own move, but if they (accidentally) press it after
22 doing the computer's move, the computer takes your turn.
23 
24 Capturing pieces is also unintuitive, having to press the destination square twice.
25 
26 ******************************************************************************/
27 
28 #include "emu.h"
29 #include "cpu/f8/f8.h"
30 #include "machine/f3853.h"
31 #include "video/pwm.h"
32 #include "machine/sensorboard.h"
33 #include "sound/dac.h"
34 #include "speaker.h"
35 
36 // internal artwork
37 #include "saitek_cp2000.lh" // clickable
38 
39 
40 namespace {
41 
42 class cp2000_state : public driver_device
43 {
44 public:
cp2000_state(const machine_config & mconfig,device_type type,const char * tag)45 	cp2000_state(const machine_config &mconfig, device_type type, const char *tag) :
46 		driver_device(mconfig, type, tag),
47 		m_maincpu(*this, "maincpu"),
48 		m_display(*this, "display"),
49 		m_board(*this, "board"),
50 		m_dac(*this, "dac"),
51 		m_inputs(*this, "IN.%u", 0)
52 	{ }
53 
54 	// machine configs
55 	void cp2000(machine_config &config);
56 
57 protected:
58 	virtual void machine_start() override;
59 
60 private:
61 	// devices/pointers
62 	required_device<cpu_device> m_maincpu;
63 	required_device<pwm_display_device> m_display;
64 	required_device<sensorboard_device> m_board;
65 	required_device<dac_bit_interface> m_dac;
66 	required_ioport_array<4> m_inputs;
67 
68 	// address maps
69 	void main_map(address_map &map);
70 	void main_io(address_map &map);
71 
72 	// I/O handlers
73 	void update_display();
74 	void control_w(u8 data);
75 	void digit_w(u8 data);
76 	u8 input_r();
77 
78 	u16 m_inp_mux = 0;
79 	u8 m_select = 0;
80 	u8 m_7seg_data = 0;
81 };
82 
machine_start()83 void cp2000_state::machine_start()
84 {
85 	// register for savestates
86 	save_item(NAME(m_select));
87 	save_item(NAME(m_inp_mux));
88 	save_item(NAME(m_7seg_data));
89 }
90 
91 
92 
93 /******************************************************************************
94     I/O
95 ******************************************************************************/
96 
97 // 3850 ports
98 
update_display()99 void cp2000_state::update_display()
100 {
101 	m_display->matrix(m_select, m_7seg_data);
102 }
103 
control_w(u8 data)104 void cp2000_state::control_w(u8 data)
105 {
106 	// d0-d3: digit select
107 	m_select = ~data;
108 	update_display();
109 
110 	// d4: keypad/chessboard select
111 
112 	// d5: speaker out
113 	m_dac->write(BIT(~data, 5));
114 }
115 
input_r()116 u8 cp2000_state::input_r()
117 {
118 	u8 data = m_inp_mux;
119 
120 	// read chessboard buttons
121 	if (m_select & 0x10)
122 	{
123 		u8 cb = m_board->read_rank((m_inp_mux >> 1 & 7) ^ 3);
124 		if (m_inp_mux & 1)
125 			cb <<= 4;
126 		data |= cb & 0xf0;
127 	}
128 
129 	// read keypad buttons
130 	else
131 	{
132 		// d0-d3: multiplexed inputs from d4-d7
133 		for (int i = 0; i < 4; i++)
134 			if (BIT(m_inp_mux, i+4))
135 				data |= m_inputs[i]->read();
136 
137 		// d4-d7: multiplexed inputs from d0-d3
138 		for (int i = 0; i < 4; i++)
139 			if (m_inp_mux & m_inputs[i]->read())
140 				data |= 1 << (i+4);
141 	}
142 
143 	return data;
144 }
145 
digit_w(u8 data)146 void cp2000_state::digit_w(u8 data)
147 {
148 	// d0-d3: chessboard input mux (demux)
149 	// d0-d7: keypad input mux (direct)
150 	m_inp_mux = data;
151 
152 	// also digit segment data
153 	m_7seg_data = bitswap<8>(data,0,2,1,3,4,5,6,7);
154 	update_display();
155 }
156 
157 
158 
159 /******************************************************************************
160     Address Maps
161 ******************************************************************************/
162 
main_map(address_map & map)163 void cp2000_state::main_map(address_map &map)
164 {
165 	map(0x0000, 0x0fff).rom();
166 	map(0x1000, 0x10ff).ram();
167 }
168 
main_io(address_map & map)169 void cp2000_state::main_io(address_map &map)
170 {
171 	map(0x00, 0x00).rw(FUNC(cp2000_state::input_r), FUNC(cp2000_state::digit_w));
172 	map(0x01, 0x01).w(FUNC(cp2000_state::control_w));
173 	map(0x0c, 0x0f).rw("f3853", FUNC(f3853_device::read), FUNC(f3853_device::write));
174 }
175 
176 
177 
178 /******************************************************************************
179     Input Ports
180 ******************************************************************************/
181 
182 static INPUT_PORTS_START( cp2000 )
183 	PORT_START("IN.0")
PORT_CODE(KEYCODE_4)184 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Rook")
185 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / Black")
186 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
187 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Find Position")
188 
189 	PORT_START("IN.1")
190 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Bishop")
191 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / White")
192 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
193 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
194 
195 	PORT_START("IN.2")
196 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Knight")
197 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / King")
198 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Cancel EP")
199 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / Clear Square / Level")
200 
201 	PORT_START("IN.3")
202 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Pawn")
203 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Queen")
204 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
205 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Clear Board / New Game")
206 INPUT_PORTS_END
207 
208 
209 
210 /******************************************************************************
211     Machine Configs
212 ******************************************************************************/
213 
214 void cp2000_state::cp2000(machine_config &config)
215 {
216 	/* basic machine hardware */
217 	F8(config, m_maincpu, 2750000); // see driver notes
218 	m_maincpu->set_addrmap(AS_PROGRAM, &cp2000_state::main_map);
219 	m_maincpu->set_addrmap(AS_IO, &cp2000_state::main_io);
220 	m_maincpu->set_irq_acknowledge_callback("f3853", FUNC(f3853_device::int_acknowledge));
221 
222 	f3853_device &f3853(F3853(config, "f3853", 2750000));
223 	f3853.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
224 
225 	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
226 	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
227 	m_board->set_delay(attotime::from_msec(100));
228 
229 	/* video hardware */
230 	PWM_DISPLAY(config, m_display).set_size(4, 7);
231 	m_display->set_segmask(0xf, 0x7f);
232 	config.set_default_layout(layout_saitek_cp2000);
233 
234 	/* sound hardware */
235 	SPEAKER(config, "speaker").front_center();
236 	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
237 }
238 
239 
240 
241 /******************************************************************************
242     ROM Definitions
243 ******************************************************************************/
244 
245 ROM_START( cp2000 )
246 	ROM_REGION( 0x10000, "maincpu", 0 )
247 	ROM_LOAD("c55126_y01-rom.u3", 0x0000, 0x1000, CRC(aa7b8536) SHA1(62fb2c5631541e9058e51eb6bdc6e69569baeef3) )
248 ROM_END
249 
250 } // anonymous namespace
251 
252 
253 
254 /******************************************************************************
255     Drivers
256 ******************************************************************************/
257 
258 //    YEAR  NAME    PARENT CMP MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
259 CONS( 1980, cp2000, 0,      0, cp2000,  cp2000, cp2000_state, empty_init, "SciSys / Novag", "Chess Partner 2000", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
260