1 #define _LARGEFILE_SOURCE
2 #define _LARGEFILE64_SOURCE
3 #define _GNU_SOURCE
4 
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <stdlib.h>
9 
10 #include <sys/types.h>
11 
12 #include "ibm.h"
13 #include "device.h"
14 #include "hdd_file.h"
15 #include "io.h"
16 #include "mem.h"
17 #include "pic.h"
18 #include "rom.h"
19 #include "timer.h"
20 
21 #include "esdi_at.h"
22 
23 
24 #define IDE_TIME (TIMER_USEC*10)//(5 * 100 * (1 << TIMER_SHIFT))
25 
26 #define STAT_ERR		0x01
27 #define STAT_INDEX		0x02
28 #define STAT_CORRECTED_DATA	0x04
29 #define STAT_DRQ		0x08 /* Data request */
30 #define STAT_DSC                0x10
31 #define STAT_SEEK_COMPLETE      0x20
32 #define STAT_READY		0x40
33 #define STAT_BUSY		0x80
34 
35 #define ERR_DAM_NOT_FOUND       0x01 /*Data Address Mark not found*/
36 #define ERR_TR000               0x02 /*Track 0 not found*/
37 #define ERR_ABRT		0x04 /*Command aborted*/
38 #define ERR_ID_NOT_FOUND	0x10 /*ID not found*/
39 #define ERR_DATA_CRC	        0x40 /*Data CRC error*/
40 #define ERR_BAD_BLOCK	        0x80 /*Bad Block detected*/
41 
42 #define CMD_NOP                         0x00
43 #define CMD_RESTORE			0x10
44 #define CMD_READ			0x20
45 #define CMD_WRITE			0x30
46 #define CMD_VERIFY			0x40
47 #define CMD_FORMAT			0x50
48 #define CMD_SEEK   			0x70
49 #define CMD_DIAGNOSE                    0x90
50 #define CMD_SET_PARAMETERS              0x91
51 #define CMD_READ_PARAMETERS             0xec
52 
53 extern char ide_fn[4][512];
54 
55 typedef struct esdi_drive_t
56 {
57         int cfg_spt;
58         int cfg_hpc;
59         int current_cylinder;
60         hdd_file_t hdd_file;
61 } esdi_drive_t;
62 
63 typedef struct esdi_t
64 {
65         uint8_t status;
66         uint8_t error;
67         int secount,sector,cylinder,head,cylprecomp;
68         uint8_t command;
69         uint8_t fdisk;
70         int pos;
71 
72         int drive_sel;
73         int reset;
74         uint16_t buffer[256];
75         int irqstat;
76 
77         int callback;
78 
79         esdi_drive_t drives[2];
80 
81         rom_t bios_rom;
82 } esdi_t;
83 
84 uint16_t esdi_readw(uint16_t port, void *p);
85 void esdi_writew(uint16_t port, uint16_t val, void *p);
86 
esdi_irq_raise(esdi_t * esdi)87 static inline void esdi_irq_raise(esdi_t *esdi)
88 {
89 //        pclog("IDE_IRQ_RAISE\n");
90 	if (!(esdi->fdisk&2))
91                 picint(1 << 14);
92 
93 	esdi->irqstat=1;
94 }
95 
esdi_irq_lower(esdi_t * esdi)96 static inline void esdi_irq_lower(esdi_t *esdi)
97 {
98         picintc(1 << 14);
99 }
100 
esdi_irq_update(esdi_t * esdi)101 void esdi_irq_update(esdi_t *esdi)
102 {
103 	if (esdi->irqstat && !((pic2.pend|pic2.ins)&0x40) && !(esdi->fdisk & 2))
104                 picint(1 << 14);
105 }
106 
107 /*
108  * Return the sector offset for the current register values
109  */
esdi_get_sector(esdi_t * esdi,off64_t * addr)110 int esdi_get_sector(esdi_t *esdi, off64_t *addr)
111 {
112         esdi_drive_t *drive = &esdi->drives[esdi->drive_sel];
113         int heads = drive->cfg_hpc;
114         int sectors = drive->cfg_spt;
115 
116         if (esdi->head > heads)
117         {
118                 pclog("esdi_get_sector: past end of configured heads\n");
119                 return 1;
120         }
121         if (esdi->sector >= sectors+1)
122         {
123                 pclog("esdi_get_sector: past end of configured sectors\n");
124                 return 1;
125         }
126 
127         if (drive->cfg_spt == drive->hdd_file.spt && drive->cfg_hpc == drive->hdd_file.hpc)
128         {
129                 *addr = ((((off64_t) esdi->cylinder * heads) + esdi->head) *
130                 	          sectors) + (esdi->sector - 1);
131         }
132         else
133         {
134                 /*When performing translation, the firmware seems to leave 1
135                   sector per track inaccessible (spare sector)*/
136                 int c, h, s;
137                 *addr = ((((off64_t) esdi->cylinder * heads) + esdi->head) *
138                 	          sectors) + (esdi->sector - 1);
139 
140                 s = *addr % (drive->hdd_file.spt - 1);
141                 h = (*addr / (drive->hdd_file.spt - 1)) % drive->hdd_file.hpc;
142                 c = (*addr / (drive->hdd_file.spt - 1)) / drive->hdd_file.hpc;
143 
144                 *addr = ((((off64_t) c * drive->hdd_file.hpc) + h) *
145                 	          drive->hdd_file.spt) + s;
146         }
147 
148         return 0;
149 }
150 
151 /**
152  * Move to the next sector using CHS addressing
153  */
esdi_next_sector(esdi_t * esdi)154 void esdi_next_sector(esdi_t *esdi)
155 {
156         esdi_drive_t *drive = &esdi->drives[esdi->drive_sel];
157 
158         esdi->sector++;
159         if (esdi->sector == (drive->cfg_spt + 1))
160         {
161         	esdi->sector = 1;
162         	esdi->head++;
163         	if (esdi->head == drive->cfg_hpc)
164                 {
165         		esdi->head = 0;
166         		esdi->cylinder++;
167         		if (drive->current_cylinder < drive->hdd_file.tracks)
168                 		drive->current_cylinder++;
169 		}
170 	}
171 }
172 
esdi_write(uint16_t port,uint8_t val,void * p)173 void esdi_write(uint16_t port, uint8_t val, void *p)
174 {
175         esdi_t *esdi = (esdi_t *)p;
176 
177 //        pclog("esdi_write: addr=%04x val=%02x\n", port, val);
178         switch (port)
179         {
180                 case 0x1F0: /* Data */
181                 esdi_writew(port, val | (val << 8), p);
182                 return;
183 
184                 case 0x1F1: /* Write precompenstation */
185                 esdi->cylprecomp = val;
186                 return;
187 
188                 case 0x1F2: /* Sector count */
189                 esdi->secount = val;
190                 return;
191 
192                 case 0x1F3: /* Sector */
193                 esdi->sector = val;
194                 return;
195 
196                 case 0x1F4: /* Cylinder low */
197                 esdi->cylinder = (esdi->cylinder & 0xFF00) | val;
198                 return;
199 
200                 case 0x1F5: /* Cylinder high */
201                 esdi->cylinder = (esdi->cylinder & 0xFF) | (val << 8);
202                 return;
203 
204                 case 0x1F6: /* Drive/Head */
205                 esdi->head = val & 0xF;
206                 esdi->drive_sel = (val & 0x10) ? 1 : 0;
207                 if (esdi->drives[esdi->drive_sel].hdd_file.f == NULL)
208                         esdi->status = 0;
209                 else
210                         esdi->status = STAT_READY | STAT_DSC;
211                 return;
212 
213                 case 0x1F7: /* Command register */
214 //                if (esdi->drives[esdi->drive_sel].hdd_file.f == NULL && val != CMD_SET_PARAMETERS && val != CMD_NOP && val != 0xe0 && (val & ~3) != 0x20 && (val & ~3) != 0x30 && (val & ~3) != 0x40 && val != 0x90 && val != CMD_READ_PARAMETERS)
215 //                        fatal("Command %02x on non-present drive\n", val);
216 
217                 esdi_irq_lower(esdi);
218                 esdi->command = val;
219                 esdi->error = 0;
220 
221                 switch (val & 0xf0)
222                 {
223                         case CMD_RESTORE:
224 //                        pclog("Restore\n");
225                         esdi->command &= ~0x0f; /*Mask off step rate*/
226                         esdi->status = STAT_BUSY;
227                         timer_clock();
228                         esdi->callback = 200*IDE_TIME;
229                         timer_update_outstanding();
230                         break;
231 
232                         case CMD_SEEK:
233 //                        pclog("Seek to cylinder %i\n", esdi->cylinder);
234                         esdi->command &= ~0x0f; /*Mask off step rate*/
235                         esdi->status = STAT_BUSY;
236                         timer_clock();
237                         esdi->callback = 200*IDE_TIME;
238                         timer_update_outstanding();
239                         break;
240 
241                         default:
242                         switch (val)
243                         {
244                                 case CMD_NOP:
245                                 esdi->status = STAT_BUSY;
246                                 timer_clock();
247                                 esdi->callback = 200*IDE_TIME;
248                                 timer_update_outstanding();
249                                 break;
250 
251                                 case CMD_READ: case CMD_READ+1:
252                                 case CMD_READ+2: case CMD_READ+3:
253 //                                pclog("Read %i sectors from sector %i cylinder %i head %i  %i\n",esdi->secount,esdi->sector,esdi->cylinder,esdi->head,ins);
254                                 esdi->command &= ~3;
255                                 if (val & 2)
256                                         fatal("Read with ECC\n");
257                                 case 0xa0:
258                                 esdi->status = STAT_BUSY;
259                                 timer_clock();
260                                 esdi->callback = 200*IDE_TIME;
261                                 timer_update_outstanding();
262                                 break;
263 
264                                 case CMD_WRITE: case CMD_WRITE+1:
265                                 case CMD_WRITE+2: case CMD_WRITE+3:
266 //                                pclog("Write %i sectors to sector %i cylinder %i head %i\n",esdi->secount,esdi->sector,esdi->cylinder,esdi->head);
267                                 esdi->command &= ~3;
268                                 if (val & 2)
269                                         fatal("Write with ECC\n");
270                                 esdi->status = STAT_DRQ | STAT_DSC;// | STAT_BUSY;
271                                 esdi->pos=0;
272                                 break;
273 
274                                 case CMD_VERIFY: case CMD_VERIFY+1:
275 //                                pclog("Read verify %i sectors from sector %i cylinder %i head %i\n",esdi->secount,esdi->sector,esdi->cylinder,esdi->head);
276                                 esdi->command &= ~1;
277                                 esdi->status = STAT_BUSY;
278                                 timer_clock();
279                                 esdi->callback = 200 * IDE_TIME;
280                                 timer_update_outstanding();
281                                 break;
282 
283                                 case CMD_FORMAT:
284 //                                pclog("Format track %i head %i\n", esdi->cylinder, esdi->head);
285                                 esdi->status = STAT_DRQ;// | STAT_BUSY;
286                                 esdi->pos=0;
287                                 break;
288 
289                                 case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
290                                 esdi->status = STAT_BUSY;
291                                 timer_clock();
292                                 esdi->callback = 30*IDE_TIME;
293                                 timer_update_outstanding();
294                                 break;
295 
296                                 case CMD_DIAGNOSE: /* Execute Drive Diagnostics */
297                                 esdi->status = STAT_BUSY;
298                                 timer_clock();
299                                 esdi->callback = 200*IDE_TIME;
300                                 timer_update_outstanding();
301                                 break;
302 
303                                 case 0xe0: /*???*/
304                                 case CMD_READ_PARAMETERS:
305                                 esdi->status = STAT_BUSY;
306                                 timer_clock();
307                                 esdi->callback = 200*IDE_TIME;
308                                 timer_update_outstanding();
309                                 break;
310 
311                                 default:
312                                 pclog("Bad esdi command %02X\n", val);
313                                 case 0xe8: /*???*/
314                                 esdi->status = STAT_BUSY;
315                                 timer_clock();
316                                 esdi->callback = 200*IDE_TIME;
317                                 timer_update_outstanding();
318                                 break;
319                         }
320                 }
321                 break;
322 
323                 case 0x3F6: /* Device control */
324                 if ((esdi->fdisk & 4) && !(val & 4))
325                 {
326                         timer_clock();
327                         esdi->callback = 500*IDE_TIME;
328                         timer_update_outstanding();
329                         esdi->reset = 1;
330                         esdi->status = STAT_BUSY;
331 //                        pclog("esdi Reset\n");
332                 }
333                 if (val & 4)
334                 {
335                         /*Drive held in reset*/
336                         timer_clock();
337                         esdi->callback = 0;
338                         timer_update_outstanding();
339                         esdi->status = STAT_BUSY;
340                 }
341                 esdi->fdisk = val;
342                 esdi_irq_update(esdi);
343                 return;
344         }
345 //        fatal("Bad ESDI write %04X %02X\n", port, val);
346 }
347 
esdi_writew(uint16_t port,uint16_t val,void * p)348 void esdi_writew(uint16_t port, uint16_t val, void *p)
349 {
350         esdi_t *esdi = (esdi_t *)p;
351 
352 //        pclog("Write ESDIw %04X\n",val);
353         esdi->buffer[esdi->pos >> 1] = val;
354         esdi->pos += 2;
355 
356         if (esdi->pos >= 512)
357         {
358                 esdi->pos = 0;
359                 esdi->status = STAT_BUSY;
360                 timer_clock();
361               	esdi->callback = 6*IDE_TIME;
362                 timer_update_outstanding();
363         }
364 }
365 
esdi_read(uint16_t port,void * p)366 uint8_t esdi_read(uint16_t port, void *p)
367 {
368         esdi_t *esdi = (esdi_t *)p;
369         uint8_t temp = 0xff;
370 
371         switch (port)
372         {
373                 case 0x1F0: /* Data */
374                 temp = esdi_readw(port, esdi) & 0xff;
375                 break;
376 
377                 case 0x1F1: /* Error */
378                 temp = esdi->error;
379                 break;
380 
381                 case 0x1F2: /* Sector count */
382                 temp = (uint8_t)esdi->secount;
383                 break;
384 
385                 case 0x1F3: /* Sector */
386                 temp = (uint8_t)esdi->sector;
387                 break;
388 
389                 case 0x1F4: /* Cylinder low */
390                 temp = (uint8_t)(esdi->cylinder&0xFF);
391                 break;
392 
393                 case 0x1F5: /* Cylinder high */
394                 temp = (uint8_t)(esdi->cylinder>>8);
395                 break;
396 
397                 case 0x1F6: /* Drive/Head */
398                 temp = (uint8_t)(esdi->head | (esdi->drive_sel ? 0x10 : 0) | 0xa0);
399                 break;
400 
401                 case 0x1F7: /* Status */
402                 esdi_irq_lower(esdi);
403                 temp = esdi->status;
404                 break;
405         }
406 
407 //        if (port != 0x1f7) pclog("esdi_read: addr=%04x val=%02x %04X:%04x\n", port, temp, CS, cpu_state.pc);
408         return temp;
409 }
410 
esdi_readw(uint16_t port,void * p)411 uint16_t esdi_readw(uint16_t port, void *p)
412 {
413         esdi_t *esdi = (esdi_t *)p;
414         uint16_t temp;
415 
416         temp = esdi->buffer[esdi->pos >> 1];
417         esdi->pos += 2;
418 
419         if (esdi->pos >= 512)
420         {
421 //                pclog("Over! packlen %i %i\n",ide->packlen,ide->pos);
422                 esdi->pos=0;
423                 esdi->status = STAT_READY | STAT_DSC;
424                 if (esdi->command == CMD_READ || esdi->command == 0xa0)
425                 {
426                         esdi->secount = (esdi->secount - 1) & 0xff;
427                         if (esdi->secount)
428                         {
429                                 esdi_next_sector(esdi);
430                                 esdi->status = STAT_BUSY;
431                                 timer_clock();
432                                 esdi->callback = 6*IDE_TIME;
433                                 timer_update_outstanding();
434                         }
435                 }
436         }
437 
438 //        pclog("esdi_readw: temp=%04x %i\n", temp, esdi->pos);
439         return temp;
440 }
441 
esdi_callback(void * p)442 void esdi_callback(void *p)
443 {
444         esdi_t *esdi = (esdi_t *)p;
445         esdi_drive_t *drive = &esdi->drives[esdi->drive_sel];
446         off64_t addr;
447 
448 //        pclog("esdi_callback: command=%02x reset=%i\n", esdi->command, esdi->reset);
449         esdi->callback = 0;
450         if (esdi->reset)
451         {
452                 esdi->status = STAT_READY | STAT_DSC;
453                 esdi->error = 1;
454                 esdi->secount = 1;
455                 esdi->sector = 1;
456                 esdi->head = 0;
457                 esdi->cylinder = 0;
458                 esdi->reset = 0;
459 //                pclog("Reset callback\n");
460                 return;
461         }
462         switch (esdi->command)
463         {
464                 case CMD_RESTORE:
465                 if (!drive->hdd_file.f)
466                 {
467                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
468                 	esdi->error = ERR_ABRT;
469                 	esdi_irq_raise(esdi);
470                 }
471                 else
472                 {
473                         drive->current_cylinder = 0;
474                         esdi->status = STAT_READY | STAT_DSC;
475                         esdi_irq_raise(esdi);
476                 }
477                 break;
478 
479                 case CMD_SEEK:
480                 if (!drive->hdd_file.f)
481                 {
482                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
483                 	esdi->error = ERR_ABRT;
484                 	esdi_irq_raise(esdi);
485                 }
486                 else
487                 {
488                         esdi->status = STAT_READY | STAT_DSC;
489                         esdi_irq_raise(esdi);
490                 }
491                 break;
492 
493                 case CMD_READ:
494                 if (!drive->hdd_file.f)
495                 {
496                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
497                 	esdi->error = ERR_ABRT;
498                 	esdi_irq_raise(esdi);
499                 }
500                 else
501                 {
502                         if (esdi_get_sector(esdi, &addr))
503                         {
504                                 esdi->error = ERR_ID_NOT_FOUND;
505                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
506                                 esdi_irq_raise(esdi);
507                                 break;
508                         }
509                         if (hdd_read_sectors(&drive->hdd_file, addr, 1, esdi->buffer))
510                         {
511                                 esdi->error = ERR_ID_NOT_FOUND;
512                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
513                                 esdi_irq_raise(esdi);
514                                 break;
515                         }
516                         esdi->pos = 0;
517                         esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
518 //                pclog("Read sector callback %i %i %i offset %08X %i left %i %02X\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount,ide.spt,ide.atastat[ide.board]);
519 //                if (addr) output=3;
520                         esdi_irq_raise(esdi);
521                         readflash_set(READFLASH_HDC, esdi->drive_sel);
522                 }
523                 break;
524 
525                 case CMD_WRITE:
526                 if (!drive->hdd_file.f)
527                 {
528                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
529                 	esdi->error = ERR_ABRT;
530                 	esdi_irq_raise(esdi);
531                 }
532                 else
533                 {
534                         if (esdi_get_sector(esdi, &addr))
535                         {
536                                 esdi->error = ERR_ID_NOT_FOUND;
537                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
538                                 esdi_irq_raise(esdi);
539                                 break;
540                         }
541                         if (hdd_write_sectors(&drive->hdd_file, addr, 1, esdi->buffer))
542                         {
543                                 esdi->error = ERR_ID_NOT_FOUND;
544                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
545                                 esdi_irq_raise(esdi);
546                                 break;
547                         }
548                         esdi_irq_raise(esdi);
549                         esdi->secount = (esdi->secount - 1) & 0xff;
550                         if (esdi->secount)
551                         {
552                                 esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
553                                 esdi->pos = 0;
554                                 esdi_next_sector(esdi);
555                         }
556                         else
557                                 esdi->status = STAT_READY | STAT_DSC;
558                         readflash_set(READFLASH_HDC, esdi->drive_sel);
559                 }
560                 break;
561 
562                 case CMD_VERIFY:
563                 if (!drive->hdd_file.f)
564                 {
565                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
566                 	esdi->error = ERR_ABRT;
567                 	esdi_irq_raise(esdi);
568                 }
569                 else
570                 {
571                         if (esdi_get_sector(esdi, &addr))
572                         {
573                                 esdi->error = ERR_ID_NOT_FOUND;
574                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
575                                 esdi_irq_raise(esdi);
576                                 break;
577                         }
578                         if (hdd_read_sectors(&drive->hdd_file, addr, 1, esdi->buffer))
579                         {
580                                 esdi->error = ERR_ID_NOT_FOUND;
581                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
582                                 esdi_irq_raise(esdi);
583                                 break;
584                         }
585                         readflash_set(READFLASH_HDC, esdi->drive_sel);
586                         esdi_next_sector(esdi);
587                         esdi->secount = (esdi->secount - 1) & 0xff;
588                         if (esdi->secount)
589                                 esdi->callback = 6*IDE_TIME;
590                         else
591                         {
592                                 esdi->pos = 0;
593                                 esdi->status = STAT_READY | STAT_DSC;
594 //                pclog("Read verify callback %i %i %i offset %08X %i left\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount);
595                                 esdi_irq_raise(esdi);
596                         }
597                 }
598                 break;
599 
600                 case CMD_FORMAT:
601                 if (!drive->hdd_file.f)
602                 {
603                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
604                 	esdi->error = ERR_ABRT;
605                 	esdi_irq_raise(esdi);
606                 }
607                 else
608                 {
609                         if (esdi_get_sector(esdi, &addr))
610                         {
611                                 esdi->error = ERR_ID_NOT_FOUND;
612                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
613                                 esdi_irq_raise(esdi);
614                                 break;
615                         }
616                         if (hdd_format_sectors(&drive->hdd_file, addr, esdi->secount))
617                         {
618                                 esdi->error = ERR_ID_NOT_FOUND;
619                                 esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
620                                 esdi_irq_raise(esdi);
621                                 break;
622                         }
623                         esdi->status = STAT_READY | STAT_DSC;
624                         esdi_irq_raise(esdi);
625                         readflash_set(READFLASH_HDC, esdi->drive_sel);
626                 }
627                 break;
628 
629                 case CMD_DIAGNOSE:
630                 if (!drive->hdd_file.f)
631                 {
632                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
633                 	esdi->error = ERR_ABRT;
634                 	esdi_irq_raise(esdi);
635                 }
636                 else
637                 {
638                         esdi->error = 1; /*No error detected*/
639         		esdi->status = STAT_READY | STAT_DSC;
640         		esdi_irq_raise(esdi);
641                 }
642                 break;
643 
644                 case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
645                 if (!drive->hdd_file.f)
646                 {
647                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
648                 	esdi->error = ERR_ABRT;
649                 	esdi_irq_raise(esdi);
650                 }
651                 else
652                 {
653                         drive->cfg_spt = esdi->secount;
654                         drive->cfg_hpc = esdi->head+1;
655                         pclog("Parameters: spt=%i hpc=%i\n", drive->cfg_spt,drive->cfg_hpc);
656                         if (!esdi->secount)
657                                 fatal("secount=0\n");
658                         esdi->status = STAT_READY | STAT_DSC;
659                         esdi_irq_raise(esdi);
660                 }
661                 break;
662 
663                 case CMD_NOP:
664                 esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
665                 esdi->error = ERR_ABRT;
666                 esdi_irq_raise(esdi);
667                 break;
668 
669                 case 0xe0:
670                 if (!drive->hdd_file.f)
671                 {
672                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
673                 	esdi->error = ERR_ABRT;
674                 	esdi_irq_raise(esdi);
675                 }
676                 else
677                 {
678                         switch (esdi->cylinder >> 8)
679                         {
680                                 case 0x31:
681                                 esdi->cylinder = drive->hdd_file.tracks;
682                                 break;
683                                 case 0x33:
684                                 esdi->cylinder = drive->hdd_file.hpc;
685                                 break;
686                                 case 0x35:
687                                 esdi->cylinder = 0x200;
688                                 break;
689                                 case 0x36:
690                                 esdi->cylinder = drive->hdd_file.spt;
691                                 break;
692                                 default:
693                                 pclog("EDSI Bad read config %02x\n", esdi->cylinder >> 8);
694                         }
695         		esdi->status = STAT_READY | STAT_DSC;
696         		esdi_irq_raise(esdi);
697                 }
698                 break;
699 
700                 case 0xa0:
701                 if (!drive->hdd_file.f)
702                 {
703                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
704                 	esdi->error = ERR_ABRT;
705                 	esdi_irq_raise(esdi);
706                 }
707                 else
708                 {
709                         memset(esdi->buffer, 0, 512);
710                         memset(&esdi->buffer[3], 0xff, 512-6);
711                         esdi->pos = 0;
712                         esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
713 //                pclog("Read sector callback %i %i %i offset %08X %i left %i %02X\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount,ide.spt,ide.atastat[ide.board]);
714 //                if (addr) output=3;
715                         esdi_irq_raise(esdi);
716                 }
717                 break;
718 
719                 case CMD_READ_PARAMETERS:
720                 if (!drive->hdd_file.f)
721                 {
722                 	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
723                 	esdi->error = ERR_ABRT;
724                 	esdi_irq_raise(esdi);
725                 }
726                 else
727                 {
728                         memset(esdi->buffer, 0, 512);
729 
730                         esdi->buffer[0] = 0x44;                      /* general configuration */
731                         esdi->buffer[1] = drive->hdd_file.tracks; /* number of non-removable cylinders */
732                         esdi->buffer[2] = 0;                      /* number of removable cylinders */
733                         esdi->buffer[3] = drive->hdd_file.hpc;    /* number of heads */
734 	                esdi->buffer[5] = esdi->buffer[4] * drive->hdd_file.spt; /* number of unformatted bytes/sector */
735                         esdi->buffer[4] = 600; /* number of unformatted bytes/track */
736                 	esdi->buffer[6] = drive->hdd_file.spt; /* number of sectors */
737                         esdi->buffer[7] = 0; /*minimum bytes in inter-sector gap*/
738                         esdi->buffer[8] = 0; /* minimum bytes in postamble */
739                         esdi->buffer[9] = 0; /* number of words of vendor status */
740 	/* controller info */
741 //	char	wdp_cnsn[20];		/* controller serial number */
742                         esdi->buffer[20] = 2; 	/* controller type */
743                 	esdi->buffer[21] = 1; /* sector buffer size, in sectors */
744                 	esdi->buffer[22] = 0; /* ecc bytes appended */
745 //	char	wdp_rev[8];		/* firmware revision */
746 	//27
747 	                esdi->buffer[27] = 'W' | ('D' << 8);
748 	                esdi->buffer[28] = '1' | ('0' << 8);
749 	                esdi->buffer[29] = '0' | ('7' << 8);
750 	                esdi->buffer[30] = 'V' | ('-' << 8);
751 	                esdi->buffer[31] = 'S' | ('E' << 8);
752 	                esdi->buffer[32] = '1';
753                 	esdi->buffer[47] = 0; /* sectors per interrupt */
754                 	esdi->buffer[48] = 0;/* can use double word read/write? */
755                         esdi->pos = 0;
756                         esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
757                         esdi_irq_raise(esdi);
758                 }
759                 break;
760 
761                 default:
762                 pclog("ESDI Callback on unknown command %02x\n", esdi->command);
763                 case 0xe8:
764         	esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
765         	esdi->error = ERR_ABRT;
766         	esdi_irq_raise(esdi);
767         	break;
768         }
769 }
770 
esdi_rom_write(uint32_t addr,uint8_t val,void * p)771 static void esdi_rom_write(uint32_t addr, uint8_t val, void *p)
772 {
773         rom_t *rom = (rom_t *)p;
774 
775         addr &= rom->mask;
776 
777         if (addr >= 0x1f00 && addr < 0x2000)
778                 rom->rom[addr] = val;
779 }
780 
wd1007vse1_init()781 void *wd1007vse1_init()
782 {
783         esdi_t *esdi = malloc(sizeof(esdi_t));
784         memset(esdi, 0, sizeof(esdi_t));
785 
786         hdd_load(&esdi->drives[0].hdd_file, 0, ide_fn[0]);
787         hdd_load(&esdi->drives[1].hdd_file, 1, ide_fn[1]);
788 
789         esdi->drives[0].cfg_spt = esdi->drives[0].hdd_file.spt;
790         esdi->drives[0].cfg_hpc = esdi->drives[0].hdd_file.hpc;
791         esdi->drives[1].cfg_spt = esdi->drives[1].hdd_file.spt;
792         esdi->drives[1].cfg_hpc = esdi->drives[1].hdd_file.hpc;
793 
794         esdi->status = STAT_READY | STAT_DSC;
795         esdi->error = 1; /*No errors*/
796 
797         rom_init(&esdi->bios_rom, "62-000279-061.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
798 
799         mem_mapping_set_handler(&esdi->bios_rom.mapping,
800                     rom_read, rom_readw, rom_readl,
801                     esdi_rom_write, NULL, NULL);
802 
803         io_sethandler(0x01f0, 0x0001, esdi_read, esdi_readw, NULL, esdi_write, esdi_writew, NULL, esdi);
804         io_sethandler(0x01f1, 0x0007, esdi_read, NULL,      NULL, esdi_write, NULL,       NULL, esdi);
805         io_sethandler(0x03f6, 0x0001, NULL,     NULL,      NULL, esdi_write, NULL,       NULL, esdi);
806 
807         timer_add(esdi_callback, &esdi->callback, &esdi->callback, esdi);
808 
809 	return esdi;
810 }
811 
wd1007vse1_close(void * p)812 void wd1007vse1_close(void *p)
813 {
814         esdi_t *esdi = (esdi_t *)p;
815 
816         hdd_close(&esdi->drives[0].hdd_file);
817         hdd_close(&esdi->drives[1].hdd_file);
818 
819         free(esdi);
820 }
821 
wd1007vse1_available()822 static int wd1007vse1_available()
823 {
824         return rom_present("62-000279-061.bin");
825 }
826 
827 device_t wd1007vse1_device =
828 {
829         "Western Digital WD1007V-SE1 (ESDI)",
830         DEVICE_AT,
831         wd1007vse1_init,
832         wd1007vse1_close,
833         wd1007vse1_available,
834         NULL,
835         NULL,
836         NULL,
837         NULL
838 };
839