1 // license:BSD-3-Clause
2 // copyright-holders:Sergey Svishchev
3 /*********************************************************************
4 
5     agat840k_hle.cpp
6 
7     High-level simulation of the Agat 840K floppy controller card
8 
9     http://agatcomp.ru/Reading/docs/es5323.txt
10     https://github.com/sintech/AGAT/blob/master/docs/agat-840k-format.txt
11     http://www.torlus.com/floppy/forum/viewtopic.php?f=19&t=1385
12 
13 *********************************************************************/
14 
15 #include "emu.h"
16 #include "agat840k_hle.h"
17 
18 #include "formats/agat840k_hle_dsk.h"
19 
20 //#define VERBOSE 1
21 #include "logmacro.h"
22 
23 
24 /***************************************************************************
25     PARAMETERS
26 ***************************************************************************/
27 
28 //**************************************************************************
29 //  GLOBAL VARIABLES
30 //**************************************************************************
31 
32 DEFINE_DEVICE_TYPE(A2BUS_AGAT840K_HLE, a2bus_agat840k_hle_device, "agat840k_hle", "Agat 840K floppy card")
33 
34 #define AGAT840K_ROM_REGION  "agat840k_hle_rom"
35 
36 
37 ROM_START( agat840k_hle )
38 	ROM_REGION(0x100, AGAT840K_ROM_REGION, 0)
39 	// "Zagorsk" variant
40 	ROM_LOAD( "teac.rom", 0x0000, 0x0100, CRC(94266928) SHA1(5d369bad6cdd6a70b0bb16480eba69640de87a2e) )
41 ROM_END
42 
43 //-------------------------------------------------
44 //  device_add_mconfig - add device configuration
45 //-------------------------------------------------
46 
47 static const floppy_interface agat840k_hle_floppy_interface =
48 {
49 	FLOPPY_STANDARD_5_25_DSHD,
50 	LEGACY_FLOPPY_OPTIONS_NAME(agat840k_hle),
51 	"floppy_5_25"
52 };
53 
device_add_mconfig(machine_config & config)54 void a2bus_agat840k_hle_device::device_add_mconfig(machine_config &config)
55 {
56 	legacy_floppy_image_device &floppy0(LEGACY_FLOPPY(config, m_floppy_image[0], 0, &agat840k_hle_floppy_interface));
57 	floppy0.out_idx_cb().set(FUNC(a2bus_agat840k_hle_device::index_0_w));
58 	legacy_floppy_image_device &floppy1(LEGACY_FLOPPY(config, m_floppy_image[1], 0, &agat840k_hle_floppy_interface));
59 	floppy1.out_idx_cb().set(FUNC(a2bus_agat840k_hle_device::index_1_w));
60 
61 	I8255(config, m_d14);
62 	// PA not connected
63 	m_d14->in_pb_callback().set(FUNC(a2bus_agat840k_hle_device::d14_i_b)); // status signals from drive
64 	m_d14->out_pc_callback().set(FUNC(a2bus_agat840k_hle_device::d14_o_c)); // control
65 
66 	I8255(config, m_d15);
67 	m_d15->in_pa_callback().set(FUNC(a2bus_agat840k_hle_device::d15_i_a)); // read data
68 //  m_d15->out_pb_callback().set(FUNC(a2bus_agat840k_hle_device::d15_o_b)); // write data
69 	m_d15->in_pc_callback().set(FUNC(a2bus_agat840k_hle_device::d15_i_c));
70 	m_d15->out_pc_callback().set(FUNC(a2bus_agat840k_hle_device::d15_o_c));
71 }
72 
73 //-------------------------------------------------
74 //  rom_region - device-specific ROM region
75 //-------------------------------------------------
76 
device_rom_region() const77 const tiny_rom_entry *a2bus_agat840k_hle_device::device_rom_region() const
78 {
79 	return ROM_NAME(agat840k_hle);
80 }
81 
82 //**************************************************************************
83 //  LIVE DEVICE
84 //**************************************************************************
85 
a2bus_agat840k_hle_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)86 a2bus_agat840k_hle_device::a2bus_agat840k_hle_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
87 	: device_t(mconfig, type, tag, owner, clock)
88 	, device_a2bus_card_interface(mconfig, *this)
89 	, m_floppy_image(*this, "floppy%u", 0U)
90 	, m_d14(*this, "d14")
91 	, m_d15(*this, "d15")
92 	, m_rom(nullptr)
93 {
94 }
95 
a2bus_agat840k_hle_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)96 a2bus_agat840k_hle_device::a2bus_agat840k_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
97 	a2bus_agat840k_hle_device(mconfig, A2BUS_AGAT840K_HLE, tag, owner, clock)
98 {
99 }
100 
WRITE_LINE_MEMBER(a2bus_agat840k_hle_device::index_0_w)101 WRITE_LINE_MEMBER(a2bus_agat840k_hle_device::index_0_w)
102 {
103 	index_callback(0, state);
104 }
105 
WRITE_LINE_MEMBER(a2bus_agat840k_hle_device::index_1_w)106 WRITE_LINE_MEMBER(a2bus_agat840k_hle_device::index_1_w)
107 {
108 	index_callback(1, state);
109 }
110 
index_callback(int unit,int state)111 void a2bus_agat840k_hle_device::index_callback(int unit, int state)
112 {
113 	if (unit != m_unit) return;
114 
115 	LOG("index: unit %d state %d (%s)\n", unit, state, m_seen_magic ? "MAGIC" : "magic");
116 
117 #if 0
118 	if (!state && !m_seen_magic)
119 	{
120 		m_seen_magic = true;
121 		m_count_read = 0;
122 		m_count_write = 0;
123 		m_d15->pc4_w(0); // latch data into port A
124 		m_d15->pc4_w(1);
125 		m_timer_wait->adjust(attotime::from_usec(m_waittime), 0, attotime::from_usec(m_waittime));
126 	}
127 #endif
128 }
129 
130 //-------------------------------------------------
131 //  device_start - device-specific startup
132 //-------------------------------------------------
133 
device_start()134 void a2bus_agat840k_hle_device::device_start()
135 {
136 	m_rom = device().machine().root_device().memregion(this->subtag(AGAT840K_ROM_REGION).c_str())->base();
137 
138 	m_mxcs = MXCSR_SYNC;
139 
140 	m_timer_wait = timer_alloc(TIMER_ID_WAIT);
141 	m_timer_seek = timer_alloc(TIMER_ID_SEEK);
142 
143 	m_seektime = 6000; // 6 ms, per es5323.txt
144 	m_waittime = 32;   // 16 bits x 2 us
145 }
146 
device_reset()147 void a2bus_agat840k_hle_device::device_reset()
148 {
149 	u8 buf[256];
150 
151 	for (auto &img : m_floppy_image)
152 	{
153 		if (img.found())
154 		{
155 			img->floppy_drive_set_ready_state(FLOPPY_DRIVE_READY, 0);
156 			img->floppy_drive_set_rpm(300.);
157 			img->floppy_drive_seek(-img->floppy_drive_get_current_track());
158 		}
159 	}
160 	m_floppy = m_floppy_image[0].target();
161 
162 	// generate track images in memory, using default volume ID and gap padding bytes
163 	int t = 0;
164 	for (auto &elem : m_tracks)
165 	{
166 		elem = std::make_unique<uint16_t[]>(6250);
167 
168 		for (int i = 0; i < 13; i++)
169 		{
170 			elem[i] = 0xaa;
171 		}
172 		for (int j = 0; j < 21; j++)
173 		{
174 			const int s = (j * 1) % 21;
175 			int cksum = 0;
176 
177 			m_floppy->floppy_drive_read_sector_data(t & 1, s, buf, 256);
178 
179 			enum
180 			{
181 				BAUX = 22,
182 				BLOB = 256 + 19 + BAUX
183 			};
184 
185 			for (int k = 0; k < 256; k++)
186 			{
187 				if (cksum > 255) { cksum++; cksum &= 255; }
188 				cksum += buf[k];
189 				elem[13 + (BLOB * j) + 17 + k] = buf[k];
190 			}
191 			cksum &= 255;
192 
193 			elem[13 + (BLOB * j) +  0] = 0xa4;
194 			elem[13 + (BLOB * j) +  1] = 0x80ff; // desync
195 			elem[13 + (BLOB * j) +  2] = 0x95;
196 			elem[13 + (BLOB * j) +  3] = 0x6a;
197 			elem[13 + (BLOB * j) +  4] = 0x40fe; // volume id
198 			elem[13 + (BLOB * j) +  5] = t;
199 			elem[13 + (BLOB * j) +  6] = s;
200 			elem[13 + (BLOB * j) +  7] = 0x5a;
201 			elem[13 + (BLOB * j) +  8] = 0xaa;
202 			elem[13 + (BLOB * j) +  9] = 0xaa;
203 			elem[13 + (BLOB * j) + 10] = 0xaa;
204 			elem[13 + (BLOB * j) + 11] = 0xaa;
205 			elem[13 + (BLOB * j) + 12] = 0xaa;
206 			elem[13 + (BLOB * j) + 13] = 0xa4;
207 			elem[13 + (BLOB * j) + 14] = 0x80ff; // desync
208 			elem[13 + (BLOB * j) + 15] = 0x6a;
209 			elem[13 + (BLOB * j) + 16] = 0x95;
210 			elem[13 + (BLOB * j) + 17 + 256] = cksum + 0x2000;
211 			elem[13 + (BLOB * j) + 17 + 257] = 0x5a;
212 
213 			// gap3
214 			for (int k = 0; k < BAUX; k++)
215 			{
216 				elem[13 + (BLOB * j) + 17 + 258 + k] = 0xaa;
217 			}
218 		}
219 
220 		t++;
221 		if ((t & 1) == 0)
222 		{
223 			m_floppy->floppy_drive_seek(1);
224 		}
225 	}
226 	m_floppy->floppy_drive_seek(-m_floppy->floppy_drive_get_current_track());
227 
228 	m_mxcs |= MXCSR_SYNC;
229 	m_mxcs &= ~MXCSR_TR;
230 }
231 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)232 void a2bus_agat840k_hle_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
233 {
234 	switch (id)
235 	{
236 	case TIMER_ID_WAIT:
237 	{
238 		m_count_read++;
239 		m_count_read %= 6250;
240 		m_d15->pc4_w(0);
241 		m_d15->pc4_w(1);
242 		if (BIT(m_tracks[(2 * m_floppy->floppy_drive_get_current_track()) + m_side][m_count_read], 15))
243 			m_mxcs &= ~MXCSR_SYNC;
244 	}
245 	break;
246 
247 	case TIMER_ID_SEEK:
248 		m_floppy->floppy_stp_w(1);
249 		m_floppy->floppy_stp_w(0);
250 		break;
251 	}
252 }
253 
254 
255 /*-------------------------------------------------
256     read_c0nx - called for reads from this card's c0nx space
257 -------------------------------------------------*/
258 
read_c0nx(uint8_t offset)259 uint8_t a2bus_agat840k_hle_device::read_c0nx(uint8_t offset)
260 {
261 	u8 data;
262 
263 	switch (offset)
264 	{
265 	case 0: case 1: case 2: case 3:
266 		data = m_d14->read(offset);
267 		break;
268 
269 	case 4: case 5: case 6: case 7:
270 		data = m_d15->read(offset - 4);
271 		break;
272 
273 	default:
274 		data = 0xff;
275 		break;
276 	}
277 
278 	return data;
279 }
280 
281 
282 /*-------------------------------------------------
283     write_c0nx - called for writes to this card's c0nx space
284 -------------------------------------------------*/
285 
write_c0nx(uint8_t offset,uint8_t data)286 void a2bus_agat840k_hle_device::write_c0nx(uint8_t offset, uint8_t data)
287 {
288 	switch (offset)
289 	{
290 	case 0: case 1: case 2: case 3:
291 		m_d14->write(offset, data);
292 		break;
293 
294 	case 4: case 5: case 6: case 7:
295 		m_d15->write(offset - 4, data);
296 		break;
297 
298 	case 8: // write desync
299 		break;
300 
301 	case 9: // step
302 		LOG("step at %11.6f\n", machine().time().as_double());
303 		m_seen_magic = false;
304 		m_timer_wait->adjust(attotime::from_usec(m_seektime), 0, attotime::from_usec(m_waittime));
305 		m_floppy->floppy_stp_w(1);
306 		m_floppy->floppy_stp_w(0);
307 		break;
308 
309 	case 10: // reset desync flipflop
310 		m_mxcs |= MXCSR_SYNC;
311 		break;
312 
313 	default:
314 		break;
315 	}
316 }
317 
318 /*-------------------------------------------------
319     read_cnxx - called for reads from this card's c0nx space
320 -------------------------------------------------*/
321 
read_cnxx(uint8_t offset)322 uint8_t a2bus_agat840k_hle_device::read_cnxx(uint8_t offset)
323 {
324 	return m_rom[offset];
325 }
326 
327 /*
328  * all signals active low.  write support not implemented; WPT is always active.
329  *
330  * b0-b1    type of drive 2: 00 - ES 5323.01 "1000 KB", 01 - "500 KB", 10 - "250 KB", 11 - not present
331  * b2-b3    type of drive 1: -""-
332  * b4       INDEX/SECTOR
333  * b5       WRITE PROTECT
334  * b6       TRACK 0
335  * b7       READY
336  *
337  * C0x1
338  */
d14_i_b()339 uint8_t a2bus_agat840k_hle_device::d14_i_b()
340 {
341 	u8 data = 0x3;
342 
343 	m_floppy->floppy_drive_set_ready_state(FLOPPY_DRIVE_READY, 1);
344 
345 	data |= (m_floppy->floppy_index_r() << 4) ^ 0x10;
346 //  data |= m_floppy->floppy_wpt_r() << 5;
347 	data |= m_floppy->floppy_tk00_r() << 6;
348 	data |= m_floppy->floppy_ready_r() << 7;
349 
350 	LOG("status A: %s %s (t %d) %s %s\n", BIT(data, 7) ? "ready" : "READY", BIT(data, 6) ? "tk00" : "TK00",
351 		m_floppy->floppy_drive_get_current_track(),
352 		BIT(data, 5) ? "wpt" : "WPT", BIT(data, 4) ? "index" : "INDEX");
353 
354 	return data;
355 }
356 
357 /*
358  * b0   AH  strong write precomp
359  * b1   --  NC
360  * b2   --  step direction (1 - inward, 0 - outward)
361  * b3   --  drive select (0 - drive 1, 1 - drive 2)
362  * b4   --  head select (0 - bottom, 1 - top)
363  * b5   AH  write precomp off
364  * b6   AH  write enable
365  * b7   AH  motor on
366  *
367  * C0x2
368  */
d14_o_c(uint8_t data)369 void a2bus_agat840k_hle_device::d14_o_c(uint8_t data)
370 {
371 	m_unit = BIT(data, 3);
372 	m_floppy = m_floppy_image[m_unit].target();
373 	if (m_unit)
374 		m_floppy->floppy_ds_w(m_unit != 1);
375 	else
376 		m_floppy->floppy_ds_w(m_unit != 0);
377 
378 	m_floppy->floppy_drtn_w(!BIT(data, 2));
379 	m_side = BIT(data, 4);
380 	m_floppy->floppy_wtg_w(!BIT(data, 6));
381 	m_floppy->floppy_mon_w(!BIT(data, 7)); // tied to 'drive select', 'motor on' and 'head load'
382 
383 	if (!BIT(data, 7))
384 	{
385 		m_seen_magic = false;
386 		m_timer_wait->adjust(attotime::never);
387 	}
388 	else
389 	{
390 		m_d15->pc4_w(0);
391 		m_d15->pc4_w(1);
392 		m_timer_wait->adjust(attotime::from_usec(m_waittime), 0, attotime::from_usec(m_waittime));
393 	}
394 
395 	LOG("D14 C <- %02X (unit %d side %d drtn %d wtg %d mon %d)\n",
396 		data, m_unit, m_side, !BIT(data, 2), !BIT(data, 6), !BIT(data, 7));
397 }
398 
399 // C0x4
400 //
401 // data are latched in by write to PC4
d15_i_a()402 uint8_t a2bus_agat840k_hle_device::d15_i_a()
403 {
404 	const u16 data = m_tracks[(2 * m_floppy->floppy_drive_get_current_track()) + m_side][m_count_read];
405 	LOG("sector data: %02x @ %4d (head %d track %2d)%s\n", data & 0xff, m_count_read,
406 		m_side, m_floppy->floppy_drive_get_current_track(),
407 		BIT(data, 14) ? " volume" : (BIT(data, 13) ? " cksum" : ""));
408 
409 	return data & 0xff;
410 }
411 
412 // C0x6
413 //
414 // b6   AL  desync detected
415 // b7   AH  read or write data ready
d15_i_c()416 uint8_t a2bus_agat840k_hle_device::d15_i_c()
417 {
418 	LOG("status B:       @ %4d %s %s (%s)\n", m_count_read,
419 		BIT(m_mxcs, 7) ? "ready" : "READY", BIT(m_mxcs, 6) ? "SYNC" : "sync",
420 		m_seen_magic ? "MAGIC" : "magic");
421 
422 	return m_mxcs;
423 }
424 
425 // C0x7
426 //
427 // b0   --  connected to b7, set if m_intr[PORT_B]
428 // b2   AH  b7 = ready for write data
429 // b3   --  connected to b7, set if m_intr[PORT_A]
430 // b4   AH  b7 = read data ready
d15_o_c(uint8_t data)431 void a2bus_agat840k_hle_device::d15_o_c(uint8_t data)
432 {
433 	if (BIT(data, 0) || BIT(data, 3))
434 	{
435 		m_mxcs |= MXCSR_TR;
436 	}
437 	else
438 	{
439 		m_mxcs &= ~MXCSR_TR;
440 	}
441 }
442