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