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