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