]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/ide/test.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / ide / test.c
1 /* THINGS TO DO:
2  *
3  *      - Modularize copy-pasta'd code such as:
4  *         * The "if you value your data" warning
5  *         * ATAPI packet commands
6  *         * EVERYTHING---This code basically works on test hardware, now it's time to clean it up
7  *           modularize and refactor.
8  *      - Add menu item where the user can ask this code to test whether or not the IDE controller
9  *        supports 32-bit PIO properly.
10  *      - Start using this code for reference and implementation of IDE emulation within DOSBox-X
11  *      - Add menu items to allow the user to play with SET FEATURES command
12  *      - Add submenu where the user can play with the S.M.A.R.T. ATA commands
13  *      - **TEST THIS CODE ON AS MANY MACHINES AS POSSIBLE** The IDE interface is one of those
14  *        hardware standards that is conceptually simple yet so manu manufacturers managed to fuck
15  *        up their implementation in some way or another.
16  *      - Cleanup this code, move library into idelib.c+idelib.h
17  *      - Fix corner cases where we're IDE busy waiting and user commands to break free (ESC or spacebar
18  *        do not break out of read/write loop, forcing the user to CTRL+ALT+DEL or press reset.
19  *
20  * Also don't forget:
21  *   - Test programs for specific IDE chipsets (like Intel PIIX3) to demonstrate IDE DMA READ/WRITE commands
22  *
23  * Interesting notes:
24  *   - Toshiba Satellite Pro 465CDX/2.1
25  *      - Putting the hard drive to sleep seems to put the IDE controller to sleep too. IDE controller
26  *        "busy" bit is stuck on when hard drive asleep. Only way out seems to be doing a "host reset"
27  *        on that IDE controller. Note that NORMAL behavior is that the IDE controller remains not-busy
28  *        but the device is not ready.
29  *
30  *      - The CD-ROM drive (or secondary IDE?) appears to ignore 32-bit I/O to port 0x170 (base_io+0).
31  *        If you attempt to read a sector or identify command results using 32-bit PIO, you get only
32  *        0xFFFFFFFF. Reading data only works when PIO is done as 16-bit. NORMAL behavior suggests
33  *        either 32-bit PIO gets 32 bits at a time (PCI based IDE) or 32-bit PIO gets 16 bits of IDE
34  *        data and 16 bits of the adjacent 16-bit I/O register due to 386/486-era ISA subdivision of
35  *        32-bit I/O into two 16-bit I/O reads. Supposedly, on pre-PCI controllers, 32-bit PIO could
36  *        be made to work if you tell the card in advance using the "VLB keying sequence", but I have
37  *        yet to find such a card.
38  *
39  * Known hardware this code has trouble with (so far):
40  * 
41  *   - Ancient (1999-2002-ish) DVD-ROM drives. They generally work with this code but there seem to be a lot
42  *     of edge cases. One DVD-ROM drive I own will not raise "drive ready" after receipt of a command it doesn't
43  *     recognize. */
44
45 #include <stdio.h>
46 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <malloc.h>
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <dos.h>
54
55 #include <hw/vga/vga.h>
56 #include <hw/pci/pci.h>
57 #include <hw/dos/dos.h>
58 #include <hw/8254/8254.h>               /* 8254 timer */
59 #include <hw/8259/8259.h>               /* 8259 PIC interrupts */
60 #include <hw/vga/vgagui.h>
61 #include <hw/vga/vgatty.h>
62 #include <hw/ide/idelib.h>
63
64 #include "testutil.h"
65 #include "testmbox.h"
66 #include "testcmui.h"
67 #include "testbusy.h"
68 #include "testpiot.h"
69 #include "testrvfy.h"
70 #include "testrdwr.h"
71 #include "testidnt.h"
72 #include "testcdej.h"
73 #include "testpiom.h"
74 #include "testtadj.h"
75 #include "testcdrm.h"
76 #include "testmumo.h"
77 #include "testmisc.h"
78 #include "test.h"
79
80 #include "testnop.h"
81 #include "testpwr.h"
82
83 #ifdef ISAPNP
84 #include <hw/isapnp/isapnp.h>
85 #include <hw/sndsb/sndsbpnp.h>
86 #endif
87
88 unsigned char                   opt_ignore_smartdrv = 0;
89 unsigned char                   opt_no_irq = 0;
90 unsigned char                   opt_no_pci = 0;
91 unsigned char                   opt_no_isapnp = 0;
92 unsigned char                   opt_no_isa_probe = 0;
93 unsigned char                   opt_irq_chain = 1;
94
95 unsigned char                   cdrom_read_mode = 12;
96 unsigned char                   pio_width_warning = 1;
97 unsigned char                   big_scary_write_test_warning = 1;
98
99 char                            tmp[1024];
100 uint16_t                        ide_info[256];
101 #ifdef ISAPNP
102 static unsigned char far        devnode_raw[4096];
103 #endif
104
105 #if TARGET_MSDOS == 32
106 unsigned char                   cdrom_sector[512U*256U];/* ~128KB, enough for 64 CD-ROM sector or 256 512-byte sectors */
107 #else
108 # if defined(__LARGE__) || defined(__COMPACT__)
109 unsigned char                   cdrom_sector[512U*16U]; /* ~8KB, enough for 4 CD-ROM sector or 16 512-byte sectors */
110 # else
111 unsigned char                   cdrom_sector[512U*8U];  /* ~4KB, enough for 2 CD-ROM sector or 8 512-byte sectors */
112 # endif
113 #endif
114
115 /*-----------------------------------------------------------------*/
116
117 static const char *drive_main_menustrings[] = {
118         "Show IDE register taskfile",           /* 0 */
119         "Identify (ATA)",
120         "Identify packet (ATAPI)",
121         "Power states >>",
122         "PIO mode >>",                          /* 4 */ /* rewritten */
123         "No-op",                                /* 5 */
124         "Tweaks and adjustments >>",
125         "CD-ROM eject/load >>",
126         "CD-ROM reading >>",
127         "Multiple mode >>",
128         "Read/Write tests >>",                  /* 10 */
129         "Miscellaneous >>"
130 };
131
132 void do_ide_controller_drive(struct ide_controller *ide,unsigned char which) {
133         struct menuboxbounds mbox;
134         char backredraw=1;
135         VGA_ALPHA_PTR vga;
136         unsigned int x,y;
137         int select=-1;
138         char redraw=1;
139         int c;
140
141         /* UI element vars */
142         menuboxbounds_set_def_list(&mbox,/*ofsx=*/4,/*ofsy=*/7,/*cols=*/1);
143         menuboxbounds_set_item_strings_arraylen(&mbox,drive_main_menustrings);
144
145         /* most of the commands assume a ready controller. if it's stuck,
146          * we'd rather the user have a visual indication that it's stuck that way */
147         c = do_ide_controller_user_wait_busy_controller(ide);
148         if (c != 0) return;
149
150         /* select the drive we want */
151         idelib_controller_drive_select(ide,which,/*head*/0,IDELIB_DRIVE_SELECT_MODE_CHS);
152
153         /* in case the IDE controller is busy for that time */
154         c = do_ide_controller_user_wait_busy_controller(ide);
155         if (c != 0) return;
156
157         /* read back: did the drive select take effect? if not, it might not be there. another common sign is the head/drive select reads back 0xFF */
158         c = do_ide_controller_drive_check_select(ide,which);
159         if (c < 0) return;
160
161         /* it might be a CD-ROM drive, which in some cases might not raise the Drive Ready bit */
162         do_ide_controller_atapi_device_check_post_host_reset(ide);
163
164         /* wait for the drive to indicate readiness */
165         /* NTS: If the drive never becomes ready even despite our reset hacks, there's a strong
166          *      possibility that the device doesn't exist. This can happen for example if there
167          *      is a master attached but no slave. */
168         c = do_ide_controller_user_wait_drive_ready(ide);
169         if (c < 0) return;
170
171         /* for completeness, clear pending IRQ */
172         idelib_controller_ack_irq(ide);
173
174         while (1) {
175                 if (backredraw) {
176                         vga = vga_alpha_ram;
177                         backredraw = 0;
178                         redraw = 1;
179
180                         for (y=0;y < vga_height;y++) {
181                                 for (x=0;x < vga_width;x++) {
182                                         *vga++ = 0x1E00 + 177;
183                                 }
184                         }
185
186                         vga_moveto(0,0);
187
188                         vga_write_color(0x1F);
189                         vga_write("        IDE controller ");
190                         sprintf(tmp,"@%X",ide->base_io);
191                         vga_write(tmp);
192                         if (ide->alt_io != 0) {
193                                 sprintf(tmp," alt %X",ide->alt_io);
194                                 vga_write(tmp);
195                         }
196                         if (ide->irq >= 0) {
197                                 sprintf(tmp," IRQ %d",ide->irq);
198                                 vga_write(tmp);
199                         }
200                         vga_write(which ? " Slave" : " Master");
201                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
202
203                         vga_write_color(0xC);
204                         vga_write("WARNING: This code talks directly to your hard disk controller.");
205                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
206                         vga_write_color(0xC);
207                         vga_write("         If you value the data on your hard drive do not run this program.");
208                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
209                 }
210
211                 if (redraw) {
212                         redraw = 0;
213
214                         /* update a string or two: PIO mode */
215                         if (ide->pio_width == 33)
216                                 drive_main_menustrings[4] = "PIO mode (currently: 32-bit VLB) >>";
217                         else if (ide->pio_width == 32)
218                                 drive_main_menustrings[4] = "PIO mode (currently: 32-bit) >>";
219                         else
220                                 drive_main_menustrings[4] = "PIO mode (currently: 16-bit) >>";
221
222                         vga_moveto(mbox.ofsx,mbox.ofsy - 2);
223                         vga_write_color((select == -1) ? 0x70 : 0x0F);
224                         vga_write("Back to IDE controller main menu");
225                         while (vga_pos_x < (mbox.width+mbox.ofsx) && vga_pos_x != 0) vga_writec(' ');
226
227                         menuboxbound_redraw(&mbox,select);
228                 }
229
230                 c = getch();
231                 if (c == 0) c = getch() << 8;
232
233                 if (c == 27) {
234                         break;
235                 }
236                 else if (c == 13) {
237                         if (select == -1)
238                                 break;
239
240                         switch (select) {
241                                 case 0: /* show IDE register taskfile */
242                                         do_common_show_ide_taskfile(ide,which);
243                                         redraw = backredraw = 1;
244                                         break;
245                                 case 1: /*Identify*/
246                                 case 2: /*Identify packet*/
247                                         do_drive_identify_device_test(ide,which,select == 2 ? 0xA1/*Identify packet*/ : 0xEC/*Identify*/);
248                                         redraw = backredraw = 1;
249                                         break;
250                                 case 3: /* power states */
251 #ifdef POWER_MENU
252                                         do_drive_power_states_test(ide,which);
253                                         redraw = backredraw = 1;
254 #endif
255                                         break;
256                                 case 4: /* PIO mode */
257 #ifdef PIO_MODE_MENU
258                                         do_drive_pio_mode(ide,which);
259                                         redraw = backredraw = 1;
260 #endif
261                                         break;
262                                 case 5: /* NOP */
263 #ifdef NOP_TEST
264                                         do_ide_controller_drive_nop_test(ide,which);
265 #endif
266                                         break;
267                                 case 6: /* Tweaks and adjustments */
268 #ifdef TWEAK_MENU
269                                         do_drive_tweaks_and_adjustments(ide,which);
270                                         redraw = backredraw = 1;
271 #endif
272                                         break;
273                                 case 7: /* CD-ROM start/stop/eject/load */
274                                         do_drive_cdrom_startstop_test(ide,which);
275                                         redraw = backredraw = 1;
276                                         break;
277                                 case 8: /* CD-ROM reading */
278                                         do_drive_cdrom_reading(ide,which);
279                                         redraw = backredraw = 1;
280                                         break;
281                                 case 9: /* multiple mode */
282 #ifdef MULTIPLE_MODE_MENU
283                                         do_drive_multiple_mode(ide,which);
284                                         redraw = backredraw = 1;
285 #endif
286                                         break;
287                                 case 10: /* read/write tests */
288                                         do_drive_readwrite_tests(ide,which);
289                                         redraw = backredraw = 1;
290                                         break;
291                                 case 11: /* misc */
292 #ifdef MISC_TEST
293                                         do_drive_misc_tests(ide,which);
294                                         redraw = backredraw = 1;
295 #endif
296                                         break;
297                         };
298                 }
299                 else if (c == 0x4800) {
300                         if (--select < -1)
301                                 select = mbox.item_max;
302
303                         redraw = 1;
304                 }
305                 else if (c == 0x4B00) { /* left */
306                         redraw = 1;
307                 }
308                 else if (c == 0x4D00) { /* right */
309                         redraw = 1;
310                 }
311                 else if (c == 0x5000) {
312                         if (++select > mbox.item_max)
313                                 select = -1;
314
315                         redraw = 1;
316                 }
317         }
318 }
319
320 static void (interrupt *my_ide_old_irq)() = NULL;
321 static struct ide_controller *my_ide_irq_ide = NULL;
322 static unsigned long ide_irq_counter = 0;
323 static int my_ide_irq_number = -1;
324
325 static void interrupt my_ide_irq() {
326         int i;
327
328         _cli();
329
330         /* we CANNOT use sprintf() here. sprintf() doesn't work to well from within an interrupt handler,
331          * and can cause crashes in 16-bit realmode builds. */
332         i = vga_width*(vga_height-1);
333         vga_alpha_ram[i++] = 0x1F00 | 'I';
334         vga_alpha_ram[i++] = 0x1F00 | 'R';
335         vga_alpha_ram[i++] = 0x1F00 | 'Q';
336         vga_alpha_ram[i++] = 0x1F00 | ':';
337         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter / 100000UL) % 10UL));
338         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter /  10000UL) % 10UL));
339         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter /   1000UL) % 10UL));
340         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter /    100UL) % 10UL));
341         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter /     10UL) % 10UL));
342         vga_alpha_ram[i++] = 0x1F00 | ('0' + ((ide_irq_counter /       1L) % 10UL));
343         vga_alpha_ram[i++] = 0x1F00 | ' ';
344         ide_irq_counter++;
345
346         if (my_ide_irq_ide != NULL) {
347                 my_ide_irq_ide->irq_fired++;
348
349                 /* NTS: This code requires some explanation: On an Intel Core i3 mini-itx motherboard I recently
350                  * bought, the SATA controller (in IDE mode) has a problem with IDE interrupts where for reasons
351                  * beyond my understanding, once the IRQ fires, the IRQ continues to fire and will not stop no
352                  * matter what registers we read or ports we poke. It fires rapidly enough that our busy wait
353                  * code cannot proceed and the program "hangs" while the IRQ counter on the screen counts upward
354                  * very fast. It is only when exiting back to DOS that the BIOS somehow makes it stop.
355                  *
356                  * That motherboard is the reason this code was implemented. should any other SATA/IDE controller
357                  * have this problem, this code will eventually stop the IRQ flood by masking off the IRQ and
358                  * switching the IDE controller struct into polling mode so that the user can continue to use
359                  * this program without having to hit the reset button!
360                  *
361                  * Another Intel Core i3 system (2010) has the same problem, with SATA ports and one IDE port.
362                  * This happens even when talking to the IDE port and not the SATA-IDE emulation.
363                  *
364                  * It seems to be a problem with Intel-based motherboards, 2010 or later.
365                  *
366                  * Apparently the fix is to chain to the BIOS IRQ handler, which knows how to cleanup the IRQ signal. */
367
368                 /* ack IRQ on IDE controller */
369                 idelib_controller_ack_irq(my_ide_irq_ide);
370         }
371
372         if (!opt_irq_chain || my_ide_old_irq == NULL) {
373                 /* ack PIC */
374                 if (my_ide_irq_ide->irq >= 8) p8259_OCW2(8,P8259_OCW2_NON_SPECIFIC_EOI);
375                 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
376         }
377         else {
378                 /* chain to previous */
379                 my_ide_old_irq();
380         }
381
382         /* If too many IRQs fired, then stop the IRQ and use polling from now on. */
383         if (my_ide_irq_ide != NULL) {
384                 if (my_ide_irq_ide->irq_fired >= 0xFFFEU) {
385                         do_ide_controller_emergency_halt_irq(my_ide_irq_ide);
386                         vga_alpha_ram[i+12] = 0x1C00 | '!';
387                         my_ide_irq_ide->irq_fired = ~0; /* make sure the IRQ counter is as large as possible */
388                 }
389         }
390 }
391
392 void do_ide_controller_hook_irq(struct ide_controller *ide) {
393         if (my_ide_irq_number >= 0 || ide->irq < 0)
394                 return;
395
396         /* let the IRQ know what IDE controller */
397         my_ide_irq_ide = ide;
398
399         /* enable on IDE controller */
400         p8259_mask(ide->irq);
401         idelib_otr_enable_interrupt(ide,1);
402         idelib_controller_ack_irq(ide);
403
404         /* hook IRQ */
405         my_ide_old_irq = _dos_getvect(irq2int(ide->irq));
406         _dos_setvect(irq2int(ide->irq),my_ide_irq);
407         my_ide_irq_number = ide->irq;
408
409         /* enable at PIC */
410         p8259_unmask(ide->irq);
411 }
412
413 void do_ide_controller_unhook_irq(struct ide_controller *ide) {
414         if (my_ide_irq_number < 0 || ide->irq < 0)
415                 return;
416
417         /* disable on IDE controller, then mask at PIC */
418         p8259_mask(ide->irq);
419         idelib_controller_ack_irq(ide);
420         idelib_otr_enable_interrupt(ide,0);
421
422         /* restore the original vector */
423         _dos_setvect(irq2int(ide->irq),my_ide_old_irq);
424         my_ide_irq_number = -1;
425         my_ide_old_irq = NULL;
426 }
427
428 void do_ide_controller_emergency_halt_irq(struct ide_controller *ide) {
429         /* disable on IDE controller, then mask at PIC */
430         if (ide->irq >= 0) p8259_mask(ide->irq);
431         idelib_controller_ack_irq(ide);
432         idelib_otr_enable_interrupt(ide,0);
433 }
434
435 void do_ide_controller_enable_irq(struct ide_controller *ide,unsigned char en) {
436         if (!en || ide->irq < 0 || ide->irq != my_ide_irq_number)
437                 do_ide_controller_unhook_irq(ide);
438         if (en && ide->irq >= 0)
439                 do_ide_controller_hook_irq(ide);
440 }
441
442 void do_ide_controller(struct ide_controller *ide) {
443         struct vga_msg_box vgabox;
444         char backredraw=1;
445         VGA_ALPHA_PTR vga;
446         unsigned int x,y;
447         char redraw=1;
448         int select=-1;
449         int c;
450
451         /* we're taking a drive, possibly out from MS-DOS.
452          * make sure SMARTDRV flushes the cache so that it does not attempt to
453          * write to the disk while we're controlling the IDE controller */
454         if (smartdrv_version != 0) {
455                 for (c=0;c < 4;c++) smartdrv_flush();
456         }
457
458         /* most of the commands assume a ready controller. if it's stuck,
459          * we'd rather the user have a visual indication that it's stuck that way */
460         c = do_ide_controller_user_wait_busy_controller(ide);
461         if (c < 0) return;
462
463         /* if the IDE struct says to use interrupts, then do it */
464         do_ide_controller_enable_irq(ide,ide->flags.io_irq_enable);
465
466         while (1) {
467                 if (backredraw) {
468                         vga = vga_alpha_ram;
469                         backredraw = 0;
470                         redraw = 1;
471
472                         for (y=0;y < vga_height;y++) {
473                                 for (x=0;x < vga_width;x++) {
474                                         *vga++ = 0x1E00 + 177;
475                                 }
476                         }
477
478                         vga_moveto(0,0);
479
480                         vga_write_color(0x1F);
481                         vga_write("        IDE controller ");
482                         sprintf(tmp,"@%X",ide->base_io);
483                         vga_write(tmp);
484                         if (ide->alt_io != 0) {
485                                 sprintf(tmp," alt %X",ide->alt_io);
486                                 vga_write(tmp);
487                         }
488                         if (ide->irq >= 0) {
489                                 sprintf(tmp," IRQ %d",ide->irq);
490                                 vga_write(tmp);
491                         }
492                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
493
494                         vga_write_color(0xC);
495                         vga_write("WARNING: This code talks directly to your hard disk controller.");
496                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
497                         vga_write_color(0xC);
498                         vga_write("         If you value the data on your hard drive do not run this program.");
499                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
500                 }
501
502                 if (redraw) {
503                         redraw = 0;
504
505                         y = 5;
506                         vga_moveto(8,y++);
507                         vga_write_color((select == -1) ? 0x70 : 0x0F);
508                         vga_write("Main menu");
509                         while (vga_pos_x < (vga_width-8) && vga_pos_x != 0) vga_writec(' ');
510                         y++;
511
512                         vga_moveto(8,y++);
513                         vga_write_color((select == 0) ? 0x70 : 0x0F);
514                         vga_write("Host Reset");
515                         while (vga_pos_x < (vga_width-8) && vga_pos_x != 0) vga_writec(' ');
516
517                         vga_moveto(8,y++);
518                         vga_write_color((select == 1) ? 0x70 : 0x0F);
519                         vga_write("Tinker with Master device");
520                         while (vga_pos_x < (vga_width-8) && vga_pos_x != 0) vga_writec(' ');
521
522                         vga_moveto(8,y++);
523                         vga_write_color((select == 2) ? 0x70 : 0x0F);
524                         vga_write("Tinker with Slave device");
525                         while (vga_pos_x < (vga_width-8) && vga_pos_x != 0) vga_writec(' ');
526
527                         vga_moveto(8,y++);
528                         vga_write_color((select == 3) ? 0x70 : 0x0F);
529                         vga_write("Currently using ");
530                         vga_write(ide->flags.io_irq_enable ? "IRQ" : "polling");
531                         vga_write(", switch to ");
532                         vga_write((!ide->flags.io_irq_enable) ? "IRQ" : "polling");
533                         while (vga_pos_x < (vga_width-8) && vga_pos_x != 0) vga_writec(' ');
534                 }
535
536                 c = getch();
537                 if (c == 0) c = getch() << 8;
538
539                 if (c == 27) {
540                         break;
541                 }
542                 else if (c == 13) {
543                         if (select == -1) {
544                                 break;
545                         }
546                         else if (select == 0) { /* host reset */
547                                 if (ide->alt_io != 0) {
548                                         vga_msg_box_create(&vgabox,"Host reset in progress",0,0);
549
550                                         idelib_device_control_set_reset(ide,1);
551                                         t8254_wait(t8254_us2ticks(1000000));
552                                         idelib_device_control_set_reset(ide,0);
553
554                                         vga_msg_box_destroy(&vgabox);
555
556                                         /* now wait for not busy */
557                                         do_ide_controller_user_wait_busy_controller(ide);
558                                 }
559                         }
560                         else if (select == 1) {
561                                 do_ide_controller_drive(ide,0/*master*/);
562                                 redraw = backredraw = 1;
563                         }
564                         else if (select == 2) {
565                                 do_ide_controller_drive(ide,1/*slave*/);
566                                 redraw = backredraw = 1;
567                         }
568                         else if (select == 3) {
569                                 if (ide->irq >= 0)
570                                         ide->flags.io_irq_enable = !ide->flags.io_irq_enable;
571                                 else
572                                         ide->flags.io_irq_enable = 0;
573
574                                 do_ide_controller_enable_irq(ide,ide->flags.io_irq_enable);
575                                 redraw = backredraw = 1;
576                         }
577                 }
578                 else if (c == 0x4800) {
579                         if (--select < -1)
580                                 select = 3;
581
582                         redraw = 1;
583                 }
584                 else if (c == 0x5000) {
585                         if (++select > 3)
586                                 select = -1;
587
588                         redraw = 1;
589                 }
590         }
591
592         do_ide_controller_enable_irq(ide,0);
593         idelib_otr_enable_interrupt(ide,1); /* NTS: Most BIOSes know to unmask the IRQ at the PIC, but there might be some
594                                                 idiot BIOSes who don't clear the nIEN bit in the device control when
595                                                 executing INT 13h, so it's probably best to do it for them. */
596 }
597
598 void do_main_menu() {
599         char redraw=1;
600         char backredraw=1;
601         VGA_ALPHA_PTR vga;
602         struct ide_controller *ide;
603         unsigned int x,y,i;
604         int select=-1;
605         int c;
606
607         while (1) {
608                 if (backredraw) {
609                         vga = vga_alpha_ram;
610                         backredraw = 0;
611                         redraw = 1;
612
613                         for (y=0;y < vga_height;y++) {
614                                 for (x=0;x < vga_width;x++) {
615                                         *vga++ = 0x1E00 + 177;
616                                 }
617                         }
618
619                         vga_moveto(0,0);
620
621                         vga_write_color(0x1F);
622                         vga_write("        IDE controller test program");
623                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
624
625                         vga_write_color(0xC);
626                         vga_write("WARNING: This code talks directly to your hard disk controller.");
627                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
628                         vga_write_color(0xC);
629                         vga_write("         If you value the data on your hard drive do not run this program.");
630                         while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
631                 }
632
633                 if (redraw) {
634                         redraw = 0;
635
636                         y = 5;
637                         vga_moveto(8,y++);
638                         vga_write_color((select == -1) ? 0x70 : 0x0F);
639                         vga_write("Exit program");
640                         y++;
641
642                         for (i=0;i < MAX_IDE_CONTROLLER;i++) {
643                                 ide = idelib_get_controller(i);
644                                 if (ide != NULL) {
645                                         vga_moveto(8,y++);
646                                         vga_write_color((select == (int)i) ? 0x70 : 0x0F);
647
648                                         sprintf(tmp,"Controller @ %04X",ide->base_io);
649                                         vga_write(tmp);
650
651                                         if (ide->alt_io != 0) {
652                                                 sprintf(tmp," alt %04X",ide->alt_io);
653                                                 vga_write(tmp);
654                                         }
655
656                                         if (ide->irq >= 0) {
657                                                 sprintf(tmp," IRQ %2d",ide->irq);
658                                                 vga_write(tmp);
659                                         }
660                                 }
661                         }
662                 }
663
664                 c = getch();
665                 if (c == 0) c = getch() << 8;
666
667                 if (c == 27) {
668                         break;
669                 }
670                 else if (c == 13) {
671                         if (select == -1) {
672                                 break;
673                         }
674                         else if (select >= 0 && select < MAX_IDE_CONTROLLER) {
675                                 ide = idelib_get_controller(select);
676                                 if (ide != NULL) do_ide_controller(ide);
677                                 backredraw = redraw = 1;
678                         }
679                 }
680                 else if (c == 0x4800) {
681                         if (select <= -1)
682                                 select = MAX_IDE_CONTROLLER - 1;
683                         else
684                                 select--;
685
686                         while (select >= 0 && idelib_get_controller(select) == NULL)
687                                 select--;
688
689                         redraw = 1;
690                 }
691                 else if (c == 0x5000) {
692                         select++;
693                         while (select >= 0 && select < MAX_IDE_CONTROLLER && idelib_get_controller(select) == NULL)
694                                 select++;
695                         if (select >= MAX_IDE_CONTROLLER)
696                                 select = -1;
697
698                         redraw = 1;
699                 }
700         }
701 }
702
703 static void help() {
704         printf("test [options]\n");
705         printf("\n");
706         printf("IDE ATA/ATAPI test program\n");
707         printf("(C) 2012-2015 Jonathan Campbell, Hackipedia.org\n");
708         printf("\n");
709         printf("  /NS             Don't check if SMARTDRV is resident\n");
710         printf("  /NOIRQ          Don't use IRQ by default\n");
711 #ifdef PCI_SCAN
712         printf("  /NOPCI          Don't scan PCI bus\n");
713 #endif
714 #ifdef ISAPNP
715         printf("  /NOISAPNP       Don't scan ISA Plug & Play BIOS\n");
716 #endif
717         printf("  /NOPROBE        Don't probe ISA legacy ports\n");
718         printf("  /IRQCHAIN       IRQ should chain to previous handler (default)\n");
719         printf("  /IRQNOCHAIN     IRQ should NOT chain to previous handler\n");
720 }
721
722 int parse_argv(int argc,char **argv) {
723         char *a;
724         int i;
725
726         for (i=1;i < argc;) {
727                 a = argv[i++];
728
729                 if (*a == '/') {
730                         do { a++; } while (*a == '/');
731
732                         if (!strcasecmp(a,"?") || !strcasecmp(a,"h") || !strcasecmp(a,"help")) {
733                                 help();
734                                 return 1;
735                         }
736                         else if (!strcasecmp(a,"ns")) {
737                                 opt_ignore_smartdrv = 1;
738                         }
739                         else if (!strcasecmp(a,"irqnochain")) {
740                                 opt_irq_chain = 0;
741                         }
742                         else if (!strcasecmp(a,"irqchain")) {
743                                 opt_irq_chain = 1;
744                         }
745                         else if (!strcasecmp(a,"noirq")) {
746                                 opt_no_irq = 1;
747                         }
748                         else if (!strcasecmp(a,"nopci")) {
749                                 opt_no_pci = 1;
750                         }
751                         else if (!strcasecmp(a,"noisapnp")) {
752                                 opt_no_isapnp = 1;
753                         }
754                         else if (!strcasecmp(a,"noprobe")) {
755                                 opt_no_isa_probe = 1;
756                         }
757                         else {
758                                 printf("Unknown switch %s\n",a);
759                                 return 1;
760                         }
761                 }
762                 else {
763                         help();
764                         return 1;
765                 }
766         }
767
768         return 0;
769 }
770
771 int main(int argc,char **argv) {
772         struct ide_controller *idectrl;
773         struct ide_controller *newide;
774         int i;
775
776         if (parse_argv(argc,argv))
777                 return 1;
778
779         if (!opt_ignore_smartdrv) {
780                 if (smartdrv_detect()) {
781                         printf("WARNING: SMARTDRV %u.%02u or equivalent disk cache detected!\n",smartdrv_version>>8,smartdrv_version&0xFF);
782 #ifdef MORE_TEXT
783                         printf("         Running this program with SMARTDRV enabled is NOT RECOMMENDED,\n");
784                         printf("         especially when using the snapshot functions!\n");
785                         printf("         If you choose to test anyway, this program will attempt to flush\n");
786                         printf("         the disk cache as much as possible to avoid conflict.\n");
787 #endif
788                 }
789         }
790
791         /* we take a GUI-based approach (kind of) */
792         if (!probe_vga()) {
793                 printf("Cannot init VGA\n");
794                 return 1;
795         }
796         /* the IDE code has some timing requirements and we'll use the 8254 to do it */
797         /* I bet that by the time motherboard manufacturers stop implementing the 8254 the legacy DOS support this
798          * program requires to run will be long gone too. */
799         if (!probe_8254()) {
800                 printf("8254 chip not detected\n");
801                 return 1;
802         }
803         /* interrupt controller */
804         if (!probe_8259()) {
805                 printf("8259 chip not detected\n");
806                 return 1;
807         }
808         if (!init_idelib()) {
809                 printf("Cannot init IDE lib\n");
810                 return 1;
811         }
812 #ifdef PCI_SCAN
813         if (!opt_no_pci) {
814                 if (pci_probe(-1/*default preference*/) != PCI_CFG_NONE) {
815                         uint8_t bus,dev,func,iport;
816
817                         printf("PCI bus detected.\n");
818                         if (pci_bios_last_bus == -1) {
819                                 printf("  Autodetecting PCI bus count...\n");
820                                 pci_probe_for_last_bus();
821                         }
822                         printf("  Last bus:                 %d\n",pci_bios_last_bus);
823                         printf("  Bus decode bits:          %d\n",pci_bus_decode_bits);
824                         for (bus=0;bus <= pci_bios_last_bus;bus++) {
825                                 for (dev=0;dev < 32;dev++) {
826                                         uint8_t functions = pci_probe_device_functions(bus,dev);
827                                         for (func=0;func < functions;func++) {
828                                                 /* make sure something is there before announcing it */
829                                                 uint16_t vendor,device,subsystem,subvendor_id;
830                                                 struct ide_controller ide={0};
831                                                 uint32_t class_code;
832                                                 uint8_t revision_id;
833                                                 int IRQ_pin,IRQ_n;
834                                                 uint32_t reg;
835
836                                                 vendor = pci_read_cfgw(bus,dev,func,0x00); if (vendor == 0xFFFF) continue;
837                                                 device = pci_read_cfgw(bus,dev,func,0x02); if (device == 0xFFFF) continue;
838                                                 subvendor_id = pci_read_cfgw(bus,dev,func,0x2C);
839                                                 subsystem = pci_read_cfgw(bus,dev,func,0x2E);
840                                                 class_code = pci_read_cfgl(bus,dev,func,0x08);
841                                                 revision_id = class_code & 0xFF;
842                                                 class_code >>= 8UL;
843
844                                                 /* must be: class 0x01 (mass storage) 0x01 (IDE controller) */
845                                                 if ((class_code&0xFFFF00UL) != 0x010100UL)
846                                                         continue;
847
848                                                 /* read the command register. is the device enabled? */
849                                                 reg = pci_read_cfgw(bus,dev,func,0x04); /* read Command register */
850                                                 if (!(reg&1)) continue; /* if the I/O space bit is cleared, then no */
851
852                                                 /* tell the user! */
853                                                 printf("    Found PCI IDE controller %02x:%02x:%02x class=0x%06x\n",bus,dev,func,class_code&0xFFFFFFUL);
854
855                                                 /* enumerate from THAT the primary and secondary IDE */
856                                                 for (iport=0;iport < 2;iport++) {
857                                                         if (class_code&(0x01 << (iport*2))) { /* bit 0 is set if primary in native, bit 2 if secondary in native */
858                                                                 /* "native mode" */
859
860                                                                 /* read it from the BARs */
861                                                                 reg = pci_read_cfgl(bus,dev,func,0x10+(iport*8)); /* command block */
862                                                                 if ((reg&1) && (reg&0xFFFF0000UL) == 0UL) /* copy down IF an I/O resource */
863                                                                         ide.base_io = reg & 0xFFFC;
864
865                                                                 reg = pci_read_cfgl(bus,dev,func,0x14+(iport*8)); /* control block */
866                                                                 if ((reg&1) && (reg&0xFFFF0000UL) == 0UL) { /* copy down IF an I/O resource */
867                                                                         /* NTS: This requires some explanation: The PCI I/O resource encoding cannot
868                                                                          * represent I/O port ranges smaller than 4 ports, nor can it represent
869                                                                          * a 4-port resource unless the base port is a multiple of the I/O port
870                                                                          * range length.
871                                                                          *
872                                                                          * The alt I/O port on legacy systems is 0x3F6/0x376. For a PCI device to
873                                                                          * declare the same range, it must effectively declare 0x3F4/0x374 to
874                                                                          * 0x3F7/377 and then map the legacy ports from 2 ports in from the base.
875                                                                          *
876                                                                          * When a newer chipset uses a different base port, the same rule applies:
877                                                                          * the I/O resource is 4 ports large, and the last 2 ports (base+2) are
878                                                                          * the legacy IDE I/O ports that would be 0x3F6/0x376. */
879                                                                         ide.alt_io = (reg & 0xFFFC) + 2;
880                                                                 }
881
882                                                                 /* get IRQ number and PCI interrupt (A-D) */
883                                                                 IRQ_n = pci_read_cfgb(bus,dev,func,0x3C);
884                                                                 IRQ_pin = pci_read_cfgb(bus,dev,func,0x3D);
885                                                                 if (IRQ_n != 0 && IRQ_n < 16 && IRQ_pin != 0 && IRQ_pin <= 4)
886                                                                         ide.irq = IRQ_n;
887                                                                 else
888                                                                         ide.irq = -1;
889
890                                                                 if (ide.base_io != 0 && (ide.base_io&7) == 0) {
891                                                                         printf("      PCI IDE%u in native mode, IRQ=%d base=0x%3x alt=0x%3x\n",
892                                                                                         iport,ide.irq,ide.base_io,ide.alt_io);
893
894                                                                         if ((newide = idelib_probe(&ide)) == NULL)
895                                                                                 printf("    Warning: probe failed\n");
896
897                                                                         /* HACK: An ASUS Intel Core i3 motherboard I own has a SATA controller
898                                                                          * that has problems with IDE interrupts (when set to IDE mode).
899                                                                          * Once an IDE interrupt fires there's no way to shut it off and
900                                                                          * the controller crapfloods the PIC causing our program to "hang"
901                                                                          * running through the IRQ handler. */
902                                                                         if (vendor == 0x8086 && device == 0x8C80) { /* Intel Haswell-based motherboard (2014) */
903                                                                                 idelib_enable_interrupt(newide,0); /* don't bother with interrupts */
904                                                                         }
905                                                                 }
906                                                         }
907                                                         else {
908                                                                 /* "compatability mode".
909                                                                  * this is retarded, why didn't the PCI standards people just come out and
910                                                                  * say: guys, if you're a PCI device then frickin' show up as a proper PCI
911                                                                  * device and announce what resources you're using in the BARs and IRQ
912                                                                  * registers so OSes are not required to guess like this! */
913                                                                 ide.base_io = iport ? 0x170 : 0x1F0;
914                                                                 ide.alt_io = iport ? 0x376 : 0x3F6;
915                                                                 ide.irq = iport ? 15 : 14;
916
917                                                                 printf("      PCI IDE%u in compat mode, IRQ=%d base=0x%3x alt=0x%3x\n",
918                                                                                 iport,ide.irq,ide.base_io,ide.alt_io);
919
920                                                                 if ((newide = idelib_probe(&ide)) == NULL)
921                                                                         printf("    Warning: probe failed\n");
922                                                         }
923                                                 }
924                                         }
925                                 }
926                         }
927                 }
928         }
929 #endif
930 #ifdef ISAPNP
931         if (!opt_no_isapnp) {
932                 if (!init_isa_pnp_bios()) {
933                         printf("Cannot init ISA PnP\n");
934                 }
935                 if (find_isa_pnp_bios()) {
936                         unsigned int nodesize=0;
937                         unsigned char node=0,numnodes=0xFF,data[192];
938
939                         memset(data,0,sizeof(data));
940                         printf("ISA PnP BIOS detected\n");
941                         if (isa_pnp_bios_get_pnp_isa_cfg(data) == 0) {
942                                 struct isapnp_pnp_isa_cfg *nfo = (struct isapnp_pnp_isa_cfg*)data;
943                                 isapnp_probe_next_csn = nfo->total_csn;
944                                 isapnp_read_data = nfo->isa_pnp_port;
945                         }
946                         else {
947                                 printf("  ISA PnP BIOS failed to return configuration info\n");
948                         }
949
950                         /* enumerate device nodes reported by the BIOS */
951                         if (isa_pnp_bios_number_of_sysdev_nodes(&numnodes,&nodesize) == 0 && numnodes != 0xFF && nodesize <= sizeof(devnode_raw)) {
952                                 printf("Scanning ISA PnP BIOS devices...\n");
953                                 for (node=0;node != 0xFF;) {
954                                         struct isa_pnp_device_node far *devn;
955                                         unsigned char far *rsc, far *rf;
956                                         unsigned char this_node;
957                                         struct isapnp_tag tag;
958                                         unsigned int ioport1=0;
959                                         unsigned int ioport2=0;
960                                         unsigned int i;
961                                         int irq = -1;
962
963                                         /* apparently, start with 0. call updates node to
964                                          * next node number, or 0xFF to signify end */
965                                         this_node = node;
966                                         if (isa_pnp_bios_get_sysdev_node(&node,devnode_raw,ISA_PNP_BIOS_GET_SYSDEV_NODE_CTRL_NOW) != 0) break;
967
968                                         devn = (struct isa_pnp_device_node far*)devnode_raw;
969                                         if (devn->type_code[0] == 0x01/*system device, hard disk controller*/ &&
970                                                         devn->type_code[1] == 0x01/*Generic ESDI/IDE/ATA controller*/ &&
971                                                         devn->type_code[2] == 0x00/*Generic IDE*/) {
972                                                 rsc = (unsigned char far*)devn + sizeof(*devn);
973                                                 rf = (unsigned char far*)devn + sizeof(devnode_raw);
974
975                                                 do {
976                                                         if (!isapnp_read_tag(&rsc,rf,&tag))
977                                                                 break;
978                                                         if (tag.tag == ISAPNP_TAG_END)
979                                                                 break;
980
981                                                         /* NTS: A Toshiba Satellite 465CDX I own lists the primary IDE controller's alt port range (0x3F6)
982                                                          *      as having length == 1 for some reason. Probably because of the floppy controller. */
983
984                                                         switch (tag.tag) {
985                                                                 case ISAPNP_TAG_IO_PORT: {
986                                                                         struct isapnp_tag_io_port far *x = (struct isapnp_tag_io_port far*)tag.data;
987                                                                         if (ioport1 == 0 && x->length == 8)
988                                                                                 ioport1 = x->min_range;
989                                                                         else if (ioport2 == 0 && (x->length == 1 || x->length == 2 || x->length == 4))
990                                                                                 ioport2 = x->min_range;
991                                                                 } break;
992                                                         case ISAPNP_TAG_FIXED_IO_PORT: {
993                                                                 struct isapnp_tag_fixed_io_port far *x = (struct isapnp_tag_fixed_io_port far*)tag.data;
994                                                                 if (ioport1 == 0 && x->length == 8)
995                                                                         ioport1 = x->base;
996                                                                 else if (ioport2 == 0 && (x->length == 1 || x->length == 2 || x->length == 4))
997                                                                         ioport2 = x->base;
998                                                                 } break;
999                                                         case ISAPNP_TAG_IRQ_FORMAT: {
1000                                                                 struct isapnp_tag_irq_format far *x = (struct isapnp_tag_irq_format far*)tag.data;
1001                                                                 for (i=0;i < 16;i++) {
1002                                                                         if (x->irq_mask & (1U << (unsigned int)i)) { /* NTS: PnP devices usually support odd IRQs like IRQ 9 */
1003                                                                                 if (irq < 0) irq = i;
1004                                                                         }
1005                                                                 }
1006                                                                 } break;
1007                                                         }
1008                                                 } while (1);
1009
1010                                                 if (ioport1 != 0) {
1011                                                         struct ide_controller n;
1012
1013                                                         printf("  Found PnP IDE controller: base=0x%03x alt=0x%03x IRQ=%d\n",
1014                                                                 ioport1,ioport2,irq);
1015
1016                                                         memset(&n,0,sizeof(n));
1017                                                         n.base_io = ioport1;
1018                                                         n.alt_io = ioport2;
1019                                                         n.irq = (int8_t)irq; /* -1 is no IRQ */
1020                                                         if ((newide = idelib_probe(&n)) == NULL) {
1021                                                                 printf("    Warning: probe failed\n");
1022                                                                 /* not filling it in leaves it open for allocation again */
1023                                                         }
1024                                                 }
1025                                         }
1026                                 }
1027                         }
1028                 }
1029         }
1030 #endif
1031
1032         if (!opt_no_isa_probe) {
1033                 printf("Probing standard IDE ports...\n");
1034                 for (i=0;(idectrl = (struct ide_controller*)idelib_get_standard_isa_port(i)) != NULL;i++) {
1035                         printf("   %3X/%3X IRQ %d: ",idectrl->base_io,idectrl->alt_io,idectrl->irq); fflush(stdout);
1036
1037                         if ((newide = idelib_probe(idectrl)) != NULL) {
1038                                 printf("FOUND: alt=%X irq=%d\n",newide->alt_io,newide->irq);
1039                         }
1040                         else {
1041                                 printf("\x0D                             \x0D"); fflush(stdout);
1042                         }
1043                 }
1044         }
1045
1046         if (opt_no_irq) {
1047                 unsigned int i;
1048
1049                 for (i=0;i < MAX_IDE_CONTROLLER;i++) {
1050                         struct ide_controller *ide = &ide_controller[i];
1051                         if (idelib_controller_allocated(ide)) idelib_enable_interrupt(ide,0); /* don't bother with interrupts */
1052                 }
1053         }
1054
1055         printf("Hit ENTER to continue, ESC to cancel\n");
1056         i = wait_for_enter_or_escape();
1057         if (i == 27) {
1058                 smartdrv_close();
1059                 free_idelib();
1060                 return 0;
1061         }
1062
1063         if (int10_getmode() != 3) {
1064                 int10_setmode(3);
1065                 update_state_from_vga();
1066         }
1067
1068         do_main_menu();
1069         smartdrv_close();
1070         free_idelib();
1071         return 0;
1072 }
1073