1 #define _LARGEFILE_SOURCE
2 #define _LARGEFILE64_SOURCE
3 #define _GNU_SOURCE
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <stdlib.h>
9
10 #include <sys/types.h>
11 #include "ibm.h"
12
13 #include "device.h"
14 #include "dma.h"
15 #include "hdd_file.h"
16 #include "io.h"
17 #include "mca.h"
18 #include "mem.h"
19 #include "pic.h"
20 #include "rom.h"
21 #include "timer.h"
22
23 #include "hdd_esdi.h"
24
25 #define ESDI_TIME (500 * TIMER_USEC)
26
27 #define CMD_ADAPTER 0
28
29 extern char ide_fn[4][512];
30
31 typedef struct esdi_t
32 {
33 rom_t bios_rom;
34
35 uint8_t basic_ctrl;
36 uint8_t status;
37 uint8_t irq_status;
38
39 int irq_in_progress;
40 int cmd_req_in_progress;
41
42 int cmd_pos;
43 uint16_t cmd_data[4];
44 int cmd_dev;
45
46 int status_pos, status_len;
47 uint16_t status_data[256];
48
49 int data_pos;
50 uint16_t data[256];
51
52 uint16_t sector_buffer[256][256];
53
54 int sector_pos;
55 int sector_count;
56
57 int command;
58 int cmd_state;
59
60 int in_reset;
61 int callback;
62
63 uint32_t rba;
64
65 struct
66 {
67 int req_in_progress;
68 } cmds[3];
69
70 hdd_file_t hdd_file[2];
71
72 uint8_t pos_regs[8];
73 } esdi_t;
74
75 #define STATUS_DMA_ENA (1 << 7)
76 #define STATUS_IRQ_PENDING (1 << 6)
77 #define STATUS_CMD_IN_PROGRESS (1 << 5)
78 #define STATUS_BUSY (1 << 4)
79 #define STATUS_STATUS_OUT_FULL (1 << 3)
80 #define STATUS_CMD_IR_FULL (1 << 2)
81 #define STATUS_TRANSFER_REQ (1 << 1)
82 #define STATUS_IRQ (1 << 0)
83
84 #define CTRL_RESET (1 << 7)
85 #define CTRL_DMA_ENA (1 << 1)
86 #define CTRL_IRQ_ENA (1 << 0)
87
88 #define IRQ_HOST_ADAPTER (7 << 5)
89 #define IRQ_DEVICE_0 (0 << 5)
90 #define IRQ_CMD_COMPLETE_SUCCESS 0x1
91 #define IRQ_RESET_COMPLETE 0xa
92 #define IRQ_DATA_TRANSFER_READY 0xb
93 #define IRQ_CMD_COMPLETE_FAILURE 0xc
94
95 #define ATTN_DEVICE_SEL (7 << 5)
96 #define ATTN_HOST_ADAPTER (7 << 5)
97 #define ATTN_DEVICE_0 (0 << 5)
98 #define ATTN_DEVICE_1 (1 << 5)
99 #define ATTN_REQ_MASK 0xf
100 #define ATTN_CMD_REQ 1
101 #define ATTN_EOI 2
102 #define ATTN_RESET 4
103
104 #define CMD_SIZE_4 (1 << 14)
105
106 #define CMD_DEVICE_SEL (7 << 5)
107 #define CMD_MASK 0x1f
108 #define CMD_READ 0x01
109 #define CMD_WRITE 0x02
110 #define CMD_READ_VERIFY 0x03
111 #define CMD_WRITE_VERIFY 0x04
112 #define CMD_SEEK 0x05
113 #define CMD_GET_DEV_STATUS 0x08
114 #define CMD_GET_DEV_CONFIG 0x09
115 #define CMD_GET_POS_INFO 0x0a
116
117 #define STATUS_LEN(x) ((x) << 8)
118 #define STATUS_DEVICE(x) ((x) << 5)
119 #define STATUS_DEVICE_HOST_ADAPTER (7 << 5)
120
esdi_set_irq(esdi_t * esdi)121 static inline void esdi_set_irq(esdi_t *esdi)
122 {
123 if (esdi->basic_ctrl & CTRL_IRQ_ENA)
124 picint(1 << 14);
125 }
esdi_clear_irq()126 static inline void esdi_clear_irq()
127 {
128 picintc(1 << 14);
129 }
130
esdi_read(uint16_t port,void * p)131 static uint8_t esdi_read(uint16_t port, void *p)
132 {
133 esdi_t *esdi = (esdi_t *)p;
134 uint8_t temp = 0xff;
135
136 switch (port)
137 {
138 case 0x3512: /*Basic status register*/
139 temp = esdi->status;
140 break;
141 case 0x3513: /*IRQ status*/
142 esdi->status &= ~STATUS_IRQ;
143 temp = esdi->irq_status;
144 break;
145
146 default:
147 fatal("esdi_read port=%04x\n", port);
148 }
149
150 // pclog("esdi_read: port=%04x val=%02x %04x(%05x):%04x %02x %02x\n", port, temp,CS,cs,cpu_state.pc, BL, BH);
151 return temp;
152 }
153
esdi_write(uint16_t port,uint8_t val,void * p)154 static void esdi_write(uint16_t port, uint8_t val, void *p)
155 {
156 esdi_t *esdi = (esdi_t *)p;
157
158 // pclog("esdi_write: port=%04x val=%02x %04x:%04x %02x %i\n", port, val,CS,cpu_state.pc, esdi->status, esdi->irq_in_progress);
159 switch (port)
160 {
161 case 0x3512: /*Basic control register*/
162 if ((esdi->basic_ctrl & CTRL_RESET) && !(val & CTRL_RESET))
163 {
164 // pclog("ESDI reset\n");
165 esdi->in_reset = 1;
166 esdi->callback = ESDI_TIME * 50;
167 esdi->status = STATUS_BUSY;
168 }
169 esdi->basic_ctrl = val;
170 if (!(esdi->basic_ctrl & CTRL_IRQ_ENA))
171 picintc(1 << 14);
172 break;
173 case 0x3513: /*Attention register*/
174 switch (val & ATTN_DEVICE_SEL)
175 {
176 case ATTN_HOST_ADAPTER:
177 switch (val & ATTN_REQ_MASK)
178 {
179 case ATTN_CMD_REQ:
180 if (esdi->cmd_req_in_progress)
181 fatal("Try to start command on in_progress adapter\n");
182 esdi->cmd_req_in_progress = 1;
183 esdi->cmd_dev = ATTN_HOST_ADAPTER;
184 esdi->status |= STATUS_BUSY;
185 esdi->cmd_pos = 0;
186 esdi->status_pos = 0;
187 break;
188
189 case ATTN_EOI:
190 esdi->irq_in_progress = 0;
191 esdi->status &= ~STATUS_IRQ;
192 esdi_clear_irq();
193 break;
194
195 case ATTN_RESET:
196 // pclog("ESDI reset\n");
197 esdi->in_reset = 1;
198 esdi->callback = ESDI_TIME * 50;
199 esdi->status = STATUS_BUSY;
200 break;
201
202 default:
203 fatal("Bad attention request %02x\n", val);
204 }
205 break;
206
207 case ATTN_DEVICE_0:
208 switch (val & ATTN_REQ_MASK)
209 {
210 case ATTN_CMD_REQ:
211 if (esdi->cmd_req_in_progress)
212 fatal("Try to start command on in_progress device0\n");
213 esdi->cmd_req_in_progress = 1;
214 esdi->cmd_dev = ATTN_DEVICE_0;
215 esdi->status |= STATUS_BUSY;
216 esdi->cmd_pos = 0;
217 esdi->status_pos = 0;
218 break;
219
220 case ATTN_EOI:
221 esdi->irq_in_progress = 0;
222 esdi->status &= ~STATUS_IRQ;
223 esdi_clear_irq();
224 break;
225
226 default:
227 fatal("Bad attention request %02x\n", val);
228 }
229 break;
230
231 case ATTN_DEVICE_1:
232 switch (val & ATTN_REQ_MASK)
233 {
234 case ATTN_CMD_REQ:
235 if (esdi->cmd_req_in_progress)
236 fatal("Try to start command on in_progress device0\n");
237 esdi->cmd_req_in_progress = 1;
238 esdi->cmd_dev = ATTN_DEVICE_1;
239 esdi->status |= STATUS_BUSY;
240 esdi->cmd_pos = 0;
241 esdi->status_pos = 0;
242 break;
243
244 case ATTN_EOI:
245 esdi->irq_in_progress = 0;
246 esdi->status &= ~STATUS_IRQ;
247 esdi_clear_irq();
248 break;
249
250 default:
251 fatal("Bad attention request %02x\n", val);
252 }
253 break;
254
255 default:
256 fatal("Attention to unknown device %02x\n", val);
257 }
258 break;
259
260 default:
261 fatal("esdi_write port=%04x val=%02x\n", port, val);
262 }
263 }
264
esdi_readw(uint16_t port,void * p)265 static uint16_t esdi_readw(uint16_t port, void *p)
266 {
267 esdi_t *esdi = (esdi_t *)p;
268 uint16_t temp = 0xffff;
269
270 switch (port)
271 {
272 case 0x3510: /*Status Interface Register*/
273 if (esdi->status_pos >= esdi->status_len)
274 return 0;
275 temp = esdi->status_data[esdi->status_pos++];
276 if (esdi->status_pos >= esdi->status_len)
277 {
278 esdi->status &= ~STATUS_STATUS_OUT_FULL;
279 esdi->status_pos = esdi->status_len = 0;
280 }
281 break;
282
283 default:
284 fatal("esdi_readw port=%04x\n", port);
285 }
286
287 // pclog("esdi_readw: port=%04x val=%04x\n", port, temp);
288 return temp;
289 }
290
esdi_writew(uint16_t port,uint16_t val,void * p)291 static void esdi_writew(uint16_t port, uint16_t val, void *p)
292 {
293 esdi_t *esdi = (esdi_t *)p;
294
295 // pclog("esdi_writew: port=%04x val=%04x\n", port, val);
296 switch (port)
297 {
298 case 0x3510: /*Command Interface Register*/
299 if (esdi->cmd_pos >= 4)
300 fatal("CIR pos 4\n");
301 esdi->cmd_data[esdi->cmd_pos++] = val;
302 if ( ((esdi->cmd_data[0] & CMD_SIZE_4) && esdi->cmd_pos == 4) ||
303 (!(esdi->cmd_data[0] & CMD_SIZE_4) && esdi->cmd_pos == 2))
304 {
305 // pclog("Received command - %04x %04x %04x %04x\n", esdi->cmd_data[0], esdi->cmd_data[1], esdi->cmd_data[2], esdi->cmd_data[3]);
306
307 esdi->cmd_pos = 0;
308 esdi->cmd_req_in_progress = 0;
309 esdi->cmd_state = 0;
310
311 if ((esdi->cmd_data[0] & CMD_DEVICE_SEL) != esdi->cmd_dev)
312 fatal("Command device mismatch with attn\n");
313 esdi->command = esdi->cmd_data[0] & CMD_MASK;
314 esdi->callback = ESDI_TIME;
315 esdi->status = STATUS_BUSY;
316 esdi->data_pos = 0;
317 }
318 break;
319
320 default:
321 fatal("esdi_writew port=%04x val=%04x\n", port, val);
322 }
323 }
324
cmd_unsupported(esdi_t * esdi)325 static void cmd_unsupported(esdi_t *esdi)
326 {
327 esdi->status_len = 9;
328 esdi->status_data[0] = esdi->command | STATUS_LEN(9) | esdi->cmd_dev;
329 esdi->status_data[1] = 0x0f03; /*Attention error, command not supported*/
330 esdi->status_data[2] = 0x0002; /*Interface fault*/
331 esdi->status_data[3] = 0;
332 esdi->status_data[4] = 0;
333 esdi->status_data[5] = 0;
334 esdi->status_data[6] = 0;
335 esdi->status_data[7] = 0;
336 esdi->status_data[8] = 0;
337
338 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
339 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_FAILURE;
340 esdi->irq_in_progress = 1;
341 esdi_set_irq(esdi);
342 }
343
device_not_present(esdi_t * esdi)344 static void device_not_present(esdi_t *esdi)
345 {
346 esdi->status_len = 9;
347 esdi->status_data[0] = esdi->command | STATUS_LEN(9) | esdi->cmd_dev;
348 esdi->status_data[1] = 0x0c11; /*Command failed, internal hardware error*/
349 esdi->status_data[2] = 0x000b; /*Selection error*/
350 esdi->status_data[3] = 0;
351 esdi->status_data[4] = 0;
352 esdi->status_data[5] = 0;
353 esdi->status_data[6] = 0;
354 esdi->status_data[7] = 0;
355 esdi->status_data[8] = 0;
356
357 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
358 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_FAILURE;
359 esdi->irq_in_progress = 1;
360 esdi_set_irq(esdi);
361 }
362
rba_out_of_range(esdi_t * esdi)363 static void rba_out_of_range(esdi_t *esdi)
364 {
365 esdi->status_len = 9;
366 esdi->status_data[0] = esdi->command | STATUS_LEN(9) | esdi->cmd_dev;
367 esdi->status_data[1] = 0x0e01; /*Command block error, invalid parameter*/
368 esdi->status_data[2] = 0x0007; /*RBA out of range*/
369 esdi->status_data[3] = 0;
370 esdi->status_data[4] = 0;
371 esdi->status_data[5] = 0;
372 esdi->status_data[6] = 0;
373 esdi->status_data[7] = 0;
374 esdi->status_data[8] = 0;
375
376 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
377 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_FAILURE;
378 esdi->irq_in_progress = 1;
379 esdi_set_irq(esdi);
380 }
381
complete_command_status(esdi_t * esdi)382 static void complete_command_status(esdi_t *esdi)
383 {
384 esdi->status_len = 7;
385 if (esdi->cmd_dev == ATTN_DEVICE_0)
386 esdi->status_data[0] = CMD_READ | STATUS_LEN(7) | STATUS_DEVICE(0);
387 else
388 esdi->status_data[0] = CMD_READ | STATUS_LEN(7) | STATUS_DEVICE(1);
389 esdi->status_data[1] = 0x0000; /*Error bits*/
390 esdi->status_data[2] = 0x1900; /*Device status*/
391 esdi->status_data[3] = 0; /*Number of blocks left to do*/
392 esdi->status_data[4] = (esdi->rba-1) & 0xffff; /*Last RBA processed*/
393 esdi->status_data[5] = (esdi->rba-1) >> 8;
394 esdi->status_data[6] = 0; /*Number of blocks requiring error recovery*/
395 }
396
397 #define ESDI_ADAPTER_ONLY() do \
398 { \
399 if (esdi->cmd_dev != ATTN_HOST_ADAPTER) \
400 { \
401 cmd_unsupported(esdi); \
402 return; \
403 } \
404 } while (0)
405
406 #define ESDI_DRIVE_ONLY() do \
407 { \
408 if (esdi->cmd_dev != ATTN_DEVICE_0 && esdi->cmd_dev != ATTN_DEVICE_1) \
409 { \
410 cmd_unsupported(esdi); \
411 return; \
412 } \
413 if (esdi->cmd_dev == ATTN_DEVICE_0) \
414 hdd_file = &esdi->hdd_file[0]; \
415 else \
416 hdd_file = &esdi->hdd_file[1]; \
417 } while (0)
418
esdi_callback(void * p)419 static void esdi_callback(void *p)
420 {
421 esdi_t *esdi = (esdi_t *)p;
422 hdd_file_t *hdd_file;
423
424 esdi->callback = 0;
425
426 // pclog("esdi_callback: in_reset=%i command=%x\n", esdi->in_reset, esdi->command);
427 if (esdi->in_reset)
428 {
429 esdi->in_reset = 0;
430 esdi->status = STATUS_IRQ;
431 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_RESET_COMPLETE;
432
433 // pclog("esdi reset complete\n");
434 return;
435 }
436 switch (esdi->command)
437 {
438 case CMD_READ:
439 ESDI_DRIVE_ONLY();
440
441 if (!hdd_file->f)
442 {
443 device_not_present(esdi);
444 return;
445 }
446
447 switch (esdi->cmd_state)
448 {
449 case 0:
450 esdi->rba = (esdi->cmd_data[2] | (esdi->cmd_data[3] << 16)) & 0x0fffffff;
451
452 esdi->sector_pos = 0;
453 esdi->sector_count = esdi->cmd_data[1];
454
455 if ((esdi->rba + esdi->sector_count) >= hdd_file->sectors)
456 {
457 rba_out_of_range(esdi);
458 return;
459 }
460
461 esdi->status = STATUS_IRQ | STATUS_CMD_IN_PROGRESS | STATUS_TRANSFER_REQ;
462 esdi->irq_status = esdi->cmd_dev | IRQ_DATA_TRANSFER_READY;
463 esdi->irq_in_progress = 1;
464 esdi_set_irq(esdi);
465
466 esdi->cmd_state = 1;
467 esdi->callback = ESDI_TIME;
468 esdi->data_pos = 0;
469 break;
470
471 case 1:
472 if (!(esdi->basic_ctrl & CTRL_DMA_ENA))
473 {
474 // pclog("DMA not enabled\n");
475 esdi->callback = ESDI_TIME;
476 return;
477 }
478 while (esdi->sector_pos < esdi->sector_count)
479 {
480 if (!esdi->data_pos)
481 {
482 if (esdi->rba >= hdd_file->sectors)
483 fatal("Read past end of drive\n");
484 hdd_read_sectors(hdd_file, esdi->rba, 1, esdi->data);
485 readflash_set(READFLASH_HDC, esdi->cmd_dev == ATTN_DEVICE_0 ? 0 : 1);
486 }
487 // pclog("Read sector %i %i %08x\n", esdi->sector_pos, esdi->data_pos, esdi->rba*512);
488 while (esdi->data_pos < 256)
489 {
490 int val = dma_channel_write(5, esdi->data[esdi->data_pos]);
491
492 if (val == DMA_NODATA)
493 {
494 // pclog("DMA out of data\n");
495 esdi->callback = ESDI_TIME;
496 return;
497 }
498
499 esdi->data_pos++;
500 }
501
502 esdi->data_pos = 0;
503 esdi->sector_pos++;
504 esdi->rba++;
505 }
506
507 esdi->status = STATUS_CMD_IN_PROGRESS;
508 esdi->cmd_state = 2;
509 esdi->callback = ESDI_TIME;
510 break;
511
512 case 2:
513 // pclog("Read sector complete\n");
514 complete_command_status(esdi);
515
516 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
517 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
518 esdi->irq_in_progress = 1;
519 esdi_set_irq(esdi);
520 break;
521 }
522 break;
523
524 case CMD_WRITE:
525 case CMD_WRITE_VERIFY:
526 ESDI_DRIVE_ONLY();
527
528 if (!hdd_file->f)
529 {
530 device_not_present(esdi);
531 return;
532 }
533
534 switch (esdi->cmd_state)
535 {
536 case 0:
537 esdi->rba = (esdi->cmd_data[2] | (esdi->cmd_data[3] << 16)) & 0x0fffffff;
538
539 esdi->sector_pos = 0;
540 esdi->sector_count = esdi->cmd_data[1];
541
542 if ((esdi->rba + esdi->sector_count) >= hdd_file->sectors)
543 {
544 rba_out_of_range(esdi);
545 return;
546 }
547
548 esdi->status = STATUS_IRQ | STATUS_CMD_IN_PROGRESS | STATUS_TRANSFER_REQ;
549 esdi->irq_status = esdi->cmd_dev | IRQ_DATA_TRANSFER_READY;
550 esdi->irq_in_progress = 1;
551 esdi_set_irq(esdi);
552
553 esdi->cmd_state = 1;
554 esdi->callback = ESDI_TIME;
555 esdi->data_pos = 0;
556 break;
557
558 case 1:
559 if (!(esdi->basic_ctrl & CTRL_DMA_ENA))
560 {
561 // pclog("DMA not enabled\n");
562 esdi->callback = ESDI_TIME;
563 return;
564 }
565 while (esdi->sector_pos < esdi->sector_count)
566 {
567 // pclog("Write sector %i %08x\n", esdi->sector_pos, esdi->rba*512);
568 while (esdi->data_pos < 256)
569 {
570 int val = dma_channel_read(5);
571
572 if (val == DMA_NODATA)
573 {
574 // pclog("DMA out of data\n");
575 esdi->callback = ESDI_TIME;
576 return;
577 }
578
579 esdi->data[esdi->data_pos++] = val & 0xffff;
580 }
581
582 if (esdi->rba >= hdd_file->sectors)
583 fatal("Write past end of drive\n");
584 hdd_write_sectors(hdd_file, esdi->rba, 1, esdi->data);
585 esdi->rba++;
586 esdi->sector_pos++;
587 readflash_set(READFLASH_HDC, esdi->cmd_dev == ATTN_DEVICE_0 ? 0 : 1);
588
589 esdi->data_pos = 0;
590 }
591
592 esdi->status = STATUS_CMD_IN_PROGRESS;
593 esdi->cmd_state = 2;
594 esdi->callback = ESDI_TIME;
595 break;
596
597 case 2:
598 // pclog("Write sector complete\n");
599 complete_command_status(esdi);
600
601 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
602 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
603 esdi->irq_in_progress = 1;
604 esdi_set_irq(esdi);
605 break;
606 }
607 break;
608
609 case CMD_READ_VERIFY:
610 ESDI_DRIVE_ONLY();
611
612 if (!hdd_file->f)
613 {
614 device_not_present(esdi);
615 return;
616 }
617
618 if ((esdi->rba + esdi->sector_count) >= hdd_file->sectors)
619 {
620 rba_out_of_range(esdi);
621 return;
622 }
623 esdi->rba += esdi->sector_count;
624 complete_command_status(esdi);
625 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
626 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
627 esdi->irq_in_progress = 1;
628 esdi_set_irq(esdi);
629 break;
630
631 case CMD_SEEK:
632 ESDI_DRIVE_ONLY();
633
634 if (!hdd_file->f)
635 {
636 device_not_present(esdi);
637 return;
638 }
639
640 complete_command_status(esdi);
641 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
642 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
643 esdi->irq_in_progress = 1;
644 esdi_set_irq(esdi);
645 break;
646
647 case CMD_GET_DEV_STATUS:
648 ESDI_DRIVE_ONLY();
649
650 if (!hdd_file->f)
651 {
652 device_not_present(esdi);
653 return;
654 }
655
656 if ((esdi->status & STATUS_IRQ) || esdi->irq_in_progress)
657 fatal("IRQ in progress %02x %i\n", esdi->status, esdi->irq_in_progress);
658
659 esdi->status_len = 9;
660 esdi->status_data[0] = CMD_GET_DEV_STATUS | STATUS_LEN(9) | STATUS_DEVICE_HOST_ADAPTER;
661 esdi->status_data[1] = 0x0000; /*Error bits*/
662 esdi->status_data[2] = 0x1900; /*Device status*/
663 esdi->status_data[3] = 0; /*ESDI Standard Status*/
664 esdi->status_data[4] = 0; /*ESDI Vendor Unique Status*/
665 esdi->status_data[5] = 0;
666 esdi->status_data[6] = 0;
667 esdi->status_data[7] = 0;
668 esdi->status_data[8] = 0;
669
670 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
671 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
672 esdi->irq_in_progress = 1;
673 esdi_set_irq(esdi);
674 break;
675
676 case CMD_GET_DEV_CONFIG:
677 ESDI_DRIVE_ONLY();
678
679 if (!hdd_file->f)
680 {
681 device_not_present(esdi);
682 return;
683 }
684
685 if ((esdi->status & STATUS_IRQ) || esdi->irq_in_progress)
686 fatal("IRQ in progress %02x %i\n", esdi->status, esdi->irq_in_progress);
687
688 esdi->status_len = 6;
689 esdi->status_data[0] = CMD_GET_DEV_CONFIG | STATUS_LEN(6) | STATUS_DEVICE_HOST_ADAPTER;
690 esdi->status_data[1] = 0x10; /*Zero defect*/
691 esdi->status_data[2] = hdd_file->sectors & 0xffff;
692 esdi->status_data[3] = hdd_file->sectors >> 16;
693 esdi->status_data[4] = hdd_file->tracks;
694 esdi->status_data[5] = hdd_file->hpc | (hdd_file->spt << 16);
695
696 /* pclog("CMD_GET_DEV_CONFIG %i %04x %04x %04x %04x %04x %04x\n", drive->sectors,
697 esdi->status_data[0], esdi->status_data[1],
698 esdi->status_data[2], esdi->status_data[3],
699 esdi->status_data[4], esdi->status_data[5]);*/
700
701 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
702 esdi->irq_status = esdi->cmd_dev | IRQ_CMD_COMPLETE_SUCCESS;
703 esdi->irq_in_progress = 1;
704 esdi_set_irq(esdi);
705 break;
706
707 case CMD_GET_POS_INFO:
708 ESDI_ADAPTER_ONLY();
709
710 if ((esdi->status & STATUS_IRQ) || esdi->irq_in_progress)
711 fatal("IRQ in progress %02x %i\n", esdi->status, esdi->irq_in_progress);
712
713 esdi->status_len = 5;
714 esdi->status_data[0] = CMD_GET_POS_INFO | STATUS_LEN(5) | STATUS_DEVICE_HOST_ADAPTER;
715 esdi->status_data[1] = 0xffdd; /*MCA ID*/
716 esdi->status_data[2] = esdi->pos_regs[3] | (esdi->pos_regs[2] << 8);
717 esdi->status_data[3] = 0xff;
718 esdi->status_data[4] = 0xff;
719
720 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
721 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_CMD_COMPLETE_SUCCESS;
722 esdi->irq_in_progress = 1;
723 esdi_set_irq(esdi);
724 break;
725
726 case 0x11:
727 ESDI_ADAPTER_ONLY();
728 switch (esdi->cmd_state)
729 {
730 case 0:
731 esdi->sector_pos = 0;
732 esdi->sector_count = esdi->cmd_data[1];
733 if (esdi->sector_count > 256)
734 fatal("Read sector buffer count %04x\n", esdi->cmd_data[1]);
735
736 esdi->status = STATUS_IRQ | STATUS_CMD_IN_PROGRESS | STATUS_TRANSFER_REQ;
737 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_DATA_TRANSFER_READY;
738 esdi->irq_in_progress = 1;
739 esdi_set_irq(esdi);
740
741 esdi->cmd_state = 1;
742 esdi->callback = ESDI_TIME;
743 esdi->data_pos = 0;
744 break;
745
746 case 1:
747 if (!(esdi->basic_ctrl & CTRL_DMA_ENA))
748 {
749 // pclog("DMA not enabled\n");
750 esdi->callback = ESDI_TIME;
751 return;
752 }
753 while (esdi->sector_pos < esdi->sector_count)
754 {
755 if (!esdi->data_pos)
756 memcpy(esdi->data, esdi->sector_buffer[esdi->sector_pos++], 512);
757 // pclog("Transfer sector %i\n", esdi->sector_pos);
758 while (esdi->data_pos < 256)
759 {
760 int val = dma_channel_write(5, esdi->data[esdi->data_pos]);
761
762 if (val == DMA_NODATA)
763 {
764 // pclog("DMA out of data\n");
765 esdi->callback = ESDI_TIME;
766 return;
767 }
768
769 esdi->data_pos++;
770 }
771
772 esdi->data_pos = 0;
773 }
774
775 esdi->status = STATUS_CMD_IN_PROGRESS;
776 esdi->cmd_state = 2;
777 esdi->callback = ESDI_TIME;
778 break;
779
780 case 2:
781 // pclog("Read sector buffer complete\n");
782 esdi->status = STATUS_IRQ;
783 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_CMD_COMPLETE_SUCCESS;
784 esdi->irq_in_progress = 1;
785 esdi_set_irq(esdi);
786 break;
787 }
788 break;
789
790 case 0x10:
791 ESDI_ADAPTER_ONLY();
792 switch (esdi->cmd_state)
793 {
794 case 0:
795 esdi->sector_pos = 0;
796 esdi->sector_count = esdi->cmd_data[1];
797 if (esdi->sector_count > 256)
798 fatal("Write sector buffer count %04x\n", esdi->cmd_data[1]);
799
800 esdi->status = STATUS_IRQ | STATUS_CMD_IN_PROGRESS | STATUS_TRANSFER_REQ;
801 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_DATA_TRANSFER_READY;
802 esdi->irq_in_progress = 1;
803 esdi_set_irq(esdi);
804
805 esdi->cmd_state = 1;
806 esdi->callback = ESDI_TIME;
807 esdi->data_pos = 0;
808 break;
809
810 case 1:
811 if (!(esdi->basic_ctrl & CTRL_DMA_ENA))
812 {
813 // pclog("DMA not enabled\n");
814 esdi->callback = ESDI_TIME;
815 return;
816 }
817 while (esdi->sector_pos < esdi->sector_count)
818 {
819 // pclog("Transfer sector %i\n", esdi->sector_pos);
820 while (esdi->data_pos < 256)
821 {
822 int val = dma_channel_read(5);
823
824 if (val == DMA_NODATA)
825 {
826 // pclog("DMA out of data\n");
827 esdi->callback = ESDI_TIME;
828 return;
829 }
830
831 esdi->data[esdi->data_pos++] = val & 0xffff;;
832 }
833
834 memcpy(esdi->sector_buffer[esdi->sector_pos++], esdi->data, 512);
835 esdi->data_pos = 0;
836 }
837
838 esdi->status = STATUS_CMD_IN_PROGRESS;
839 esdi->cmd_state = 2;
840 esdi->callback = ESDI_TIME;
841 break;
842
843 case 2:
844 // pclog("Write sector buffer complete\n");
845 esdi->status = STATUS_IRQ;
846 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_CMD_COMPLETE_SUCCESS;
847 esdi->irq_in_progress = 1;
848 esdi_set_irq(esdi);
849 break;
850 }
851 break;
852
853 case 0x12:
854 ESDI_ADAPTER_ONLY();
855
856 if ((esdi->status & STATUS_IRQ) || esdi->irq_in_progress)
857 fatal("IRQ in progress %02x %i\n", esdi->status, esdi->irq_in_progress);
858
859 esdi->status_len = 2;
860 esdi->status_data[0] = 0x12 | STATUS_LEN(5) | STATUS_DEVICE_HOST_ADAPTER;
861 esdi->status_data[1] = 0;
862
863 esdi->status = STATUS_IRQ | STATUS_STATUS_OUT_FULL;
864 esdi->irq_status = IRQ_HOST_ADAPTER | IRQ_CMD_COMPLETE_SUCCESS;
865 esdi->irq_in_progress = 1;
866 esdi_set_irq(esdi);
867 break;
868
869 default:
870 fatal("Bad command %02x %i\n", esdi->command, esdi->cmd_dev);
871
872 }
873 }
874
esdi_mca_read(int port,void * p)875 static uint8_t esdi_mca_read(int port, void *p)
876 {
877 esdi_t *esdi = (esdi_t *)p;
878
879 // pclog("esdi_mca_read: port=%04x\n", port);
880
881 return esdi->pos_regs[port & 7];
882 }
883
esdi_mca_write(int port,uint8_t val,void * p)884 static void esdi_mca_write(int port, uint8_t val, void *p)
885 {
886 esdi_t *esdi = (esdi_t *)p;
887
888 if (port < 0x102)
889 return;
890
891 // pclog("esdi_mca_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc);
892
893 esdi->pos_regs[port & 7] = val;
894
895 io_removehandler(0x3510, 0x0008, esdi_read, esdi_readw, NULL, esdi_write, esdi_writew, NULL, esdi);
896 mem_mapping_disable(&esdi->bios_rom.mapping);
897 if (esdi->pos_regs[2] & 1)
898 {
899 io_sethandler(0x3510, 0x0008, esdi_read, esdi_readw, NULL, esdi_write, esdi_writew, NULL, esdi);
900 if (!(esdi->pos_regs[3] & 8))
901 {
902 mem_mapping_enable(&esdi->bios_rom.mapping);
903 mem_mapping_set_addr(&esdi->bios_rom.mapping, ((esdi->pos_regs[3] & 7) * 0x4000) + 0xc0000, 0x4000);
904 // pclog("ESDI BIOS ROM at %05x %02x\n", ((esdi->pos_regs[3] & 7) * 0x4000) + 0xc0000, esdi->pos_regs[3]);
905 }
906 }
907 }
908
esdi_init()909 static void *esdi_init()
910 {
911 esdi_t *esdi = malloc(sizeof(esdi_t));
912 memset(esdi, 0, sizeof(esdi_t));
913
914 rom_init_interleaved(&esdi->bios_rom, "90x8970.bin", "90x8969.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
915 mem_mapping_disable(&esdi->bios_rom.mapping);
916
917 hdd_load(&esdi->hdd_file[0], 0, ide_fn[0]);
918 hdd_load(&esdi->hdd_file[1], 1, ide_fn[1]);
919
920 timer_add(esdi_callback, &esdi->callback, &esdi->callback, esdi);
921
922 mca_add(esdi_mca_read, esdi_mca_write, esdi);
923
924 esdi->pos_regs[0] = 0xff;
925 esdi->pos_regs[1] = 0xdd;
926
927 esdi->in_reset = 1;
928 esdi->callback = ESDI_TIME * 50;
929 esdi->status = STATUS_BUSY;
930
931 return esdi;
932 }
933
esdi_close(void * p)934 static void esdi_close(void *p)
935 {
936 esdi_t *esdi = (esdi_t *)p;
937
938 hdd_close(&esdi->hdd_file[0]);
939 hdd_close(&esdi->hdd_file[1]);
940
941 free(esdi);
942 }
943
esdi_available()944 static int esdi_available()
945 {
946 return rom_present("90x8969.bin") && rom_present("90x8970.bin");
947 }
948
949 device_t hdd_esdi_device =
950 {
951 "IBM ESDI Fixed Disk Adapter (MCA)",
952 DEVICE_MCA,
953 esdi_init,
954 esdi_close,
955 esdi_available,
956 NULL,
957 NULL,
958 NULL,
959 NULL
960 };
961