1 // license:BSD-3-Clause
2 // copyright-holders:Robbbert
3 /***************************************************************************
4 
5 DG680
6 
7 2013-01-14 Driver created
8 
9 All input must be in uppercase.
10 
11 DG680 (ETI-680), using the DGOS-Z80 operating system.
12 
13 This is a S100 card.
14 
15 In some ways, this system is the ancestor of the original Microbee.
16 
17 No schematic available, most of this is guesswork.
18 
19 Port 0 is the input from an ascii keyboard.
20 
21 Port 2 is the cassette interface.
22 
23 Port 8 controls some kind of memory protection scheme.
24 The code indicates that B is the page to protect, and
25 A is the code (0x08 = inhibit; 0x0B = unprotect;
26 0x0C = enable; 0x0E = protect). There are 256 pages so
27 each page is 256 bytes.
28 
29 The clock is controlled by the byte in D80D.
30 
31 Monitor Commands:
32 C (compare)*
33 E (edit)*
34 F (fill)*
35 G - Go to address
36 I - Inhibit CTC
37 M (move)*
38 P (clear screen)*
39 R (read tape)*
40 S (search)*
41 T hhmm [ss] - Set the time
42 W (write tape)*
43 X - protection status
44 XC - clear ram
45 XD - same as X
46 XE - enable facilities
47 XF - disable facilities
48 XP - protect block
49 XU - unprotect block
50 Z - go to 0000.
51 
52 * These commands are identical to the Microbee ones.
53 
54 ToDo:
55 - dips
56 - leds
57 - need schematic to find out what else is missing
58 
59 ****************************************************************************/
60 
61 #include "emu.h"
62 #include "machine/keyboard.h"
63 #include "machine/z80daisy.h"
64 #include "cpu/z80/z80.h"
65 #include "imagedev/cassette.h"
66 #include "machine/clock.h"
67 #include "machine/timer.h"
68 #include "machine/z80ctc.h"
69 #include "machine/z80pio.h"
70 #include "bus/s100/s100.h"
71 #include "bus/s100/dg640.h"
72 #include "speaker.h"
73 
74 
75 class dg680_state : public driver_device
76 {
77 public:
dg680_state(const machine_config & mconfig,device_type type,const char * tag)78 	dg680_state(const machine_config &mconfig, device_type type, const char *tag)
79 		: driver_device(mconfig, type, tag)
80 		, m_maincpu(*this, "maincpu")
81 		, m_cass(*this, "cassette")
82 		, m_pio(*this, "pio")
83 		, m_ctc(*this, "ctc")
84 		, m_clock(*this, "cass_clock")
85 		, m_s100(*this, "s100")
86 	{ }
87 
88 	void dg680(machine_config &config);
89 
90 private:
91 	u8 porta_r();
92 	u8 portb_r();
93 	void portb_w(u8 data);
94 	u8 port08_r();
95 	void port08_w(u8 data);
96 	u8 mem_r(offs_t offset);
97 	void mem_w(offs_t offset, u8 data);
98 	DECLARE_WRITE_LINE_MEMBER(kansas_w);
99 	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
100 	void kbd_put(u8 data);
101 
102 	void io_map(address_map &map);
103 	void mem_map(address_map &map);
104 	void machine_reset() override;
105 	void machine_start() override;
106 
107 	u8 m_pio_b;
108 	u8 m_term_data;
109 	u8 m_protection[0x100];
110 	u8 m_cass_data[4];
111 	bool m_cassold, m_cassinbit, m_cassoutbit;
112 
113 	required_device<cpu_device> m_maincpu;
114 	required_device<cassette_image_device> m_cass;
115 	required_device<z80pio_device> m_pio;
116 	required_device<z80ctc_device> m_ctc;
117 	required_device<clock_device> m_clock;
118 	required_device<s100_bus_device> m_s100;
119 };
120 
WRITE_LINE_MEMBER(dg680_state::kansas_w)121 WRITE_LINE_MEMBER( dg680_state::kansas_w )
122 {
123 	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_RECORD)
124 		return;
125 
126 	u8 twobit = m_cass_data[3] & 15;
127 
128 	if (state)
129 	{
130 		if (twobit == 0)
131 			m_cassold = m_cassoutbit;
132 
133 		if (m_cassold)
134 			m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
135 		else
136 			m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
137 
138 		m_cass_data[3]++;
139 	}
140 }
141 
TIMER_DEVICE_CALLBACK_MEMBER(dg680_state::kansas_r)142 TIMER_DEVICE_CALLBACK_MEMBER( dg680_state::kansas_r )
143 {
144 	// no tape - set to idle
145 	m_cass_data[1]++;
146 	if (m_cass_data[1] > 32)
147 	{
148 		m_cass_data[1] = 32;
149 		m_cassinbit = 1;
150 	}
151 
152 	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
153 		return;
154 
155 	/* cassette - turn 1200/2400Hz to a bit */
156 	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;
157 
158 	if (cass_ws != m_cass_data[0])
159 	{
160 		m_cass_data[0] = cass_ws;
161 		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
162 		m_cass_data[1] = 0;
163 		m_pio->pb0_w(m_cassinbit);
164 	}
165 }
166 
mem_r(offs_t offset)167 u8 dg680_state::mem_r(offs_t offset)
168 {
169 	return m_s100->smemr_r(offset + 0xf000);
170 }
171 
mem_w(offs_t offset,u8 data)172 void dg680_state::mem_w(offs_t offset, u8 data)
173 {
174 	m_s100->mwrt_w(offset + 0xf000, data);
175 }
176 
177 
mem_map(address_map & map)178 void dg680_state::mem_map(address_map &map)
179 {
180 	map.unmap_value_high();
181 	map(0x0000, 0xcfff).ram();
182 	map(0xd000, 0xd7ff).rom().region("maincpu", 0);
183 	map(0xd800, 0xefff).ram();
184 	map(0xf000, 0xffff).rw(FUNC(dg680_state::mem_r),FUNC(dg680_state::mem_w));
185 }
186 
io_map(address_map & map)187 void dg680_state::io_map(address_map &map)
188 {
189 	map.unmap_value_high();
190 	map.global_mask(0xff);
191 	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
192 	map(0x04, 0x07).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
193 	map(0x08, 0x08).rw(FUNC(dg680_state::port08_r), FUNC(dg680_state::port08_w)); //SWP Control and Status
194 	//map(0x09,0x09) parallel input port
195 	// Optional AM9519 Programmable Interrupt Controller (port c = data, port d = control)
196 	//map(0x0c,0x0d).rw("am9519", FUNC(am9519_device::read), FUNC(am9519_device::write));
197 }
198 
machine_start()199 void dg680_state::machine_start()
200 {
201 	save_item(NAME(m_pio_b));
202 	save_item(NAME(m_term_data));
203 	save_item(NAME(m_protection));
204 	save_item(NAME(m_cass_data));
205 	save_item(NAME(m_cassold));
206 	save_item(NAME(m_cassinbit));
207 	save_item(NAME(m_cassoutbit));
208 }
209 
machine_reset()210 void dg680_state::machine_reset()
211 {
212 	m_maincpu->set_pc(0xd000);
213 	m_pio_b = 0xFF;
214 }
215 
216 // this is a guess there is no information available
217 static const z80_daisy_config dg680_daisy_chain[] =
218 {
219 	{ "ctc" },
220 	{ "pio" },
221 	{ 0x00 }
222 };
223 
224 
225 /* Input ports */
INPUT_PORTS_START(dg680)226 static INPUT_PORTS_START( dg680 )
227 INPUT_PORTS_END
228 
229 void dg680_state::kbd_put(u8 data)
230 {
231 	if (data == 8)
232 		data = 127;   // fix backspace
233 	m_term_data = data;
234 	/* strobe in keyboard data */
235 	m_pio->strobe_a(0);
236 	m_pio->strobe_a(1);
237 }
238 
porta_r()239 u8 dg680_state::porta_r()
240 {
241 	u8 data = m_term_data;
242 	m_term_data = 0;
243 	return data;
244 }
245 
portb_r()246 u8 dg680_state::portb_r()
247 {
248 	return m_pio_b | m_cassinbit;
249 }
250 
251 // bit 1 = cassout; bit 2 = motor on
portb_w(u8 data)252 void dg680_state::portb_w(u8 data)
253 {
254 	if (BIT(m_pio_b ^ data, 2))
255 		m_cass->change_state(BIT(data, 2) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
256 	m_pio_b = data & 0xfe;
257 	m_cassoutbit = BIT(data, 1);
258 }
259 
port08_r()260 u8 dg680_state::port08_r()
261 {
262 	u8 breg = m_maincpu->state_int(Z80_B);
263 	return m_protection[breg];
264 }
265 
port08_w(u8 data)266 void dg680_state::port08_w(u8 data)
267 {
268 	u8 breg = m_maincpu->state_int(Z80_B);
269 	m_protection[breg] = data;
270 }
271 
272 
dg680_s100_devices(device_slot_interface & device)273 static void dg680_s100_devices(device_slot_interface &device)
274 {
275 	device.option_add("dg640", S100_DG640);
276 }
277 
278 DEVICE_INPUT_DEFAULTS_START(dg680_dg640_f000)
279 	DEVICE_INPUT_DEFAULTS("DSW", 0x1f, 0x1e) // F000-F7FF
280 DEVICE_INPUT_DEFAULTS_END
281 
dg680(machine_config & config)282 void dg680_state::dg680(machine_config &config)
283 {
284 	SPEAKER(config, "mono").front_center();
285 
286 	/* Cassette */
287 	CASSETTE(config, m_cass);
288 	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
289 	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
290 	TIMER(config, "kansas_r").configure_periodic(FUNC(dg680_state::kansas_r), attotime::from_hz(40000));
291 
292 	CLOCK(config, m_clock, 4'800); // 300 baud x 16(divider) = 4800
293 	m_clock->signal_handler().set(FUNC(dg680_state::kansas_w));
294 	m_clock->signal_handler().append(m_ctc, FUNC(z80ctc_device::trg2));
295 	m_clock->signal_handler().append(m_ctc, FUNC(z80ctc_device::trg3));
296 
297 	/* basic machine hardware */
298 	z80_device& maincpu(Z80(config, m_maincpu, XTAL(8'000'000) / 4));
299 	maincpu.set_addrmap(AS_PROGRAM, &dg680_state::mem_map);
300 	maincpu.set_addrmap(AS_IO, &dg680_state::io_map);
301 	maincpu.set_daisy_config(dg680_daisy_chain);
302 
303 	/* Keyboard */
304 	generic_keyboard_device &keyb(GENERIC_KEYBOARD(config, "keyb", 0));
305 	keyb.set_keyboard_callback(FUNC(dg680_state::kbd_put));
306 
307 	/* Devices */
308 	Z80CTC(config, m_ctc, XTAL(8'000'000) / 4);
309 	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
310 	m_ctc->set_clk<0>(200);
311 	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
312 
313 	Z80PIO(config, m_pio, XTAL(8'000'000) / 4);
314 	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
315 	m_pio->in_pa_callback().set(FUNC(dg680_state::porta_r));
316 	// OUT_ARDY - this activates to ask for kbd data but not known if actually used
317 	m_pio->in_pb_callback().set(FUNC(dg680_state::portb_r));
318 	m_pio->out_pb_callback().set(FUNC(dg680_state::portb_w));
319 
320 	S100_BUS(config, m_s100, 1_MHz_XTAL);
321 	S100_SLOT(config, "s100:1", dg680_s100_devices, "dg640")
322 		.set_option_device_input_defaults("dg640", DEVICE_INPUT_DEFAULTS_NAME(dg680_dg640_f000));
323 }
324 
325 /* ROM definition */
326 ROM_START( dg680 )
327 	ROM_REGION( 0x0800, "maincpu", 0 )
328 	ROM_LOAD( "dg680.rom", 0x0000, 0x0800, BAD_DUMP CRC(c1aaef6a) SHA1(1508ca8315452edfb984718e795ccbe79a0c0b58) )
329 
330 	ROM_REGION( 0x0020, "proms", 0 )
331 	ROM_LOAD( "82s123.bin", 0x0000, 0x0020, NO_DUMP )
332 ROM_END
333 
334 /* Driver */
335 
336 //    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY            FULLNAME                   FLAGS
337 COMP( 1980, dg680, 0,      0,      dg680,   dg680, dg680_state, empty_init, "David Griffiths", "DG680 with DGOS-Z80 1.4", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
338